diff --git a/.gitignore b/.gitignore index 2cf0d709d..3a65f4c93 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,19 @@ -/bin -/src/Util/obj -/test/Util.Tests/obj -/.vs -obj/ -bin/ -BenchmarkDotNet.Artifacts/ -/output -/template/CodeSmith/CodeGenerator/02-Result -/sample/Util.Samples.Webs/wwwroot/lib -/sample/Util.Samples.Webs/node_modules -/sample/Util.Samples.Webs/wwwroot/dist -/sample/Util.Samples.Webs/wwwroot/test +*.suo +*.user +.vs +*.log +[Dd]ebug/ +[Rr]elease/ +x64/ +[Bb]in/ +[Oo]bj/ +output/ +node_modules/ +BenchmarkDotNet.Artifacts +**/ClientApp/**/html/** +/sample/Util.Ui.NgZorro.Sample/ClientApp/*.lock +/sample/Util.Ui.NgZorro.Sample/ClientApp/src/app/app.component.html +/document/demo/*.pdb +/document/Test/PowerDesigner/*.pdb +/test/Util.Data.Dapper.SqlServer.Tests/Document/PowerDesigner/*.pdb +/documents/PowerDesigner/*.pdb diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 000000000..f3c4d6364 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/NuGet.config b/NuGet.config deleted file mode 100644 index 3f0e00340..000000000 --- a/NuGet.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Util.sln b/Util.sln index 381709d99..9475258d6 100644 --- a/Util.sln +++ b/Util.sln @@ -1,180 +1,1055 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2027 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Samples.Webs", "sample\Util.Samples.Webs\Util.Samples.Webs.csproj", "{323B64B7-5CE0-4222-B9ED-9F418730897C}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01-src", "01-src", "{ECBD28DE-AFFB-4C9A-8988-6297FA5ADDD5}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02-test", "02-test", "{DE1DF0B9-0403-4888-8B24-8208348E9AFA}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01-Core", "01-Core", "{3746073A-1C5C-40A7-9D50-CB2A94CA888A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Tests", "test\Util.Tests\01-Util.Tests.csproj", "{4C93DBB6-7840-4277-BAF1-56148B62FB3D}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01-Core", "01-Core", "{6FA93B2E-A5E5-44CA-9851-52FAA2BF5C57}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util", "src\Util\01-Util.csproj", "{5981ADBE-58A4-4F84-9529-E7D7F7E1610C}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "05-Util.Datas", "src\Util.Datas\05-Util.Datas.csproj", "{CD83426E-2BA1-4DB0-94D9-1185C076276F}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Datas.Tests.Integration", "test\Util.Datas.Tests.Integration\02-Util.Datas.Tests.Integration.csproj", "{411B666F-EDA0-47E0-BB34-3EAF047549A0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03-Util.Logs", "src\Util.Logs\03-Util.Logs.csproj", "{0330EB27-814D-4CF6-B377-3BFA884D5FD2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "03-sample", "03-sample", "{F1B4AAEF-9162-4DFA-8B62-4069FC914839}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02-Ui", "02-Ui", "{78CC88DA-0B25-45BC-86A8-FFE9628B34A6}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "03-Biz", "03-Biz", "{62644B42-76EF-4FC2-8736-3E9B96C1D832}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Biz", "src\Util.Biz\01-Util.Biz.csproj", "{BAB54FFD-DF77-40B6-86A5-A1A3E0985468}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Security", "src\Util.Security\02-Util.Security.csproj", "{83E78951-5FBE-43D5-A6BE-7735721E4C1D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "06-Util.Applications", "src\Util.Applications\06-Util.Applications.csproj", "{7A5E9C1B-CE91-463D-B0D1-8117C9E29D30}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "04-Util.Events", "src\Util.Events\04-Util.Events.csproj", "{A93DFEEC-0DC7-4628-96F7-914A8053732B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Ui.Abstractions", "src\Util.Ui.Abstractions\01-Util.Ui.Abstractions.csproj", "{F93F72BE-F3F4-4CB6-A3A2-54CDBF3F9B18}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Ui.Core", "src\Util.Ui.Core\02-Util.Ui.Core.csproj", "{EB80F45C-FCC8-436C-900A-754A5E91A8E0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02-Ui", "02-Ui", "{0629A068-957D-4920-9290-0B9288341E0D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Ui.Tests", "test\Util.Ui.Tests\Util.Ui.Tests.csproj", "{91F99541-D37F-46D5-8026-AD57C3F75B52}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "04-Tools", "04-Tools", "{1907A873-8368-4CFF-B5B3-98F04CD2E3A7}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03-Util.Ui.Angular", "src\Util.Ui.Angular\03-Util.Ui.Angular.csproj", "{AD7CD285-D899-443D-A02C-D62C7C0A351F}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "07-Util.Webs", "src\Util.Webs\07-Util.Webs.csproj", "{0A32F32B-49DF-49AB-8F3C-AFDD28517A89}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Tools.QrCode", "src\Util.Tools.QrCode\01-Util.Tools.QrCode.csproj", "{ED44ED49-3F9E-4D14-A600-51D7792A3F64}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Tools.Sms", "src\Util.Tools.Sms\02-Util.Tools.Sms.csproj", "{883EF7C5-4234-4A1B-82BC-36597D280B57}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Biz.Payments", "src\Util.Biz.Payments\02-Util.Biz.Payments.csproj", "{69B79A60-3719-43E5-BE12-78E401C7BC0C}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "03-Biz", "03-Biz", "{D1083FEB-81A7-4BBF-ADDB-C03D143BC4AB}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Biz.Tests.Integration", "test\Util.Biz.Tests.Integration\Util.Biz.Tests.Integration.csproj", "{F680DA14-282E-471E-8E1C-F4D2067B718E}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {323B64B7-5CE0-4222-B9ED-9F418730897C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {323B64B7-5CE0-4222-B9ED-9F418730897C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {323B64B7-5CE0-4222-B9ED-9F418730897C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {323B64B7-5CE0-4222-B9ED-9F418730897C}.Release|Any CPU.Build.0 = Release|Any CPU - {4C93DBB6-7840-4277-BAF1-56148B62FB3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4C93DBB6-7840-4277-BAF1-56148B62FB3D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4C93DBB6-7840-4277-BAF1-56148B62FB3D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4C93DBB6-7840-4277-BAF1-56148B62FB3D}.Release|Any CPU.Build.0 = Release|Any CPU - {5981ADBE-58A4-4F84-9529-E7D7F7E1610C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5981ADBE-58A4-4F84-9529-E7D7F7E1610C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5981ADBE-58A4-4F84-9529-E7D7F7E1610C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5981ADBE-58A4-4F84-9529-E7D7F7E1610C}.Release|Any CPU.Build.0 = Release|Any CPU - {CD83426E-2BA1-4DB0-94D9-1185C076276F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CD83426E-2BA1-4DB0-94D9-1185C076276F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CD83426E-2BA1-4DB0-94D9-1185C076276F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CD83426E-2BA1-4DB0-94D9-1185C076276F}.Release|Any CPU.Build.0 = Release|Any CPU - {411B666F-EDA0-47E0-BB34-3EAF047549A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {411B666F-EDA0-47E0-BB34-3EAF047549A0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {411B666F-EDA0-47E0-BB34-3EAF047549A0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {411B666F-EDA0-47E0-BB34-3EAF047549A0}.Release|Any CPU.Build.0 = Release|Any CPU - {0330EB27-814D-4CF6-B377-3BFA884D5FD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0330EB27-814D-4CF6-B377-3BFA884D5FD2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0330EB27-814D-4CF6-B377-3BFA884D5FD2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0330EB27-814D-4CF6-B377-3BFA884D5FD2}.Release|Any CPU.Build.0 = Release|Any CPU - {BAB54FFD-DF77-40B6-86A5-A1A3E0985468}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BAB54FFD-DF77-40B6-86A5-A1A3E0985468}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BAB54FFD-DF77-40B6-86A5-A1A3E0985468}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BAB54FFD-DF77-40B6-86A5-A1A3E0985468}.Release|Any CPU.Build.0 = Release|Any CPU - {83E78951-5FBE-43D5-A6BE-7735721E4C1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {83E78951-5FBE-43D5-A6BE-7735721E4C1D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {83E78951-5FBE-43D5-A6BE-7735721E4C1D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {83E78951-5FBE-43D5-A6BE-7735721E4C1D}.Release|Any CPU.Build.0 = Release|Any CPU - {7A5E9C1B-CE91-463D-B0D1-8117C9E29D30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7A5E9C1B-CE91-463D-B0D1-8117C9E29D30}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7A5E9C1B-CE91-463D-B0D1-8117C9E29D30}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7A5E9C1B-CE91-463D-B0D1-8117C9E29D30}.Release|Any CPU.Build.0 = Release|Any CPU - {A93DFEEC-0DC7-4628-96F7-914A8053732B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A93DFEEC-0DC7-4628-96F7-914A8053732B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A93DFEEC-0DC7-4628-96F7-914A8053732B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A93DFEEC-0DC7-4628-96F7-914A8053732B}.Release|Any CPU.Build.0 = Release|Any CPU - {F93F72BE-F3F4-4CB6-A3A2-54CDBF3F9B18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F93F72BE-F3F4-4CB6-A3A2-54CDBF3F9B18}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F93F72BE-F3F4-4CB6-A3A2-54CDBF3F9B18}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F93F72BE-F3F4-4CB6-A3A2-54CDBF3F9B18}.Release|Any CPU.Build.0 = Release|Any CPU - {EB80F45C-FCC8-436C-900A-754A5E91A8E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EB80F45C-FCC8-436C-900A-754A5E91A8E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EB80F45C-FCC8-436C-900A-754A5E91A8E0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EB80F45C-FCC8-436C-900A-754A5E91A8E0}.Release|Any CPU.Build.0 = Release|Any CPU - {91F99541-D37F-46D5-8026-AD57C3F75B52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {91F99541-D37F-46D5-8026-AD57C3F75B52}.Debug|Any CPU.Build.0 = Debug|Any CPU - {91F99541-D37F-46D5-8026-AD57C3F75B52}.Release|Any CPU.ActiveCfg = Release|Any CPU - {91F99541-D37F-46D5-8026-AD57C3F75B52}.Release|Any CPU.Build.0 = Release|Any CPU - {AD7CD285-D899-443D-A02C-D62C7C0A351F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AD7CD285-D899-443D-A02C-D62C7C0A351F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AD7CD285-D899-443D-A02C-D62C7C0A351F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AD7CD285-D899-443D-A02C-D62C7C0A351F}.Release|Any CPU.Build.0 = Release|Any CPU - {0A32F32B-49DF-49AB-8F3C-AFDD28517A89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0A32F32B-49DF-49AB-8F3C-AFDD28517A89}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0A32F32B-49DF-49AB-8F3C-AFDD28517A89}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0A32F32B-49DF-49AB-8F3C-AFDD28517A89}.Release|Any CPU.Build.0 = Release|Any CPU - {ED44ED49-3F9E-4D14-A600-51D7792A3F64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ED44ED49-3F9E-4D14-A600-51D7792A3F64}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ED44ED49-3F9E-4D14-A600-51D7792A3F64}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ED44ED49-3F9E-4D14-A600-51D7792A3F64}.Release|Any CPU.Build.0 = Release|Any CPU - {883EF7C5-4234-4A1B-82BC-36597D280B57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {883EF7C5-4234-4A1B-82BC-36597D280B57}.Debug|Any CPU.Build.0 = Debug|Any CPU - {883EF7C5-4234-4A1B-82BC-36597D280B57}.Release|Any CPU.ActiveCfg = Release|Any CPU - {883EF7C5-4234-4A1B-82BC-36597D280B57}.Release|Any CPU.Build.0 = Release|Any CPU - {69B79A60-3719-43E5-BE12-78E401C7BC0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {69B79A60-3719-43E5-BE12-78E401C7BC0C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {69B79A60-3719-43E5-BE12-78E401C7BC0C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {69B79A60-3719-43E5-BE12-78E401C7BC0C}.Release|Any CPU.Build.0 = Release|Any CPU - {F680DA14-282E-471E-8E1C-F4D2067B718E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F680DA14-282E-471E-8E1C-F4D2067B718E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F680DA14-282E-471E-8E1C-F4D2067B718E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F680DA14-282E-471E-8E1C-F4D2067B718E}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {323B64B7-5CE0-4222-B9ED-9F418730897C} = {F1B4AAEF-9162-4DFA-8B62-4069FC914839} - {3746073A-1C5C-40A7-9D50-CB2A94CA888A} = {ECBD28DE-AFFB-4C9A-8988-6297FA5ADDD5} - {4C93DBB6-7840-4277-BAF1-56148B62FB3D} = {6FA93B2E-A5E5-44CA-9851-52FAA2BF5C57} - {6FA93B2E-A5E5-44CA-9851-52FAA2BF5C57} = {DE1DF0B9-0403-4888-8B24-8208348E9AFA} - {5981ADBE-58A4-4F84-9529-E7D7F7E1610C} = {3746073A-1C5C-40A7-9D50-CB2A94CA888A} - {CD83426E-2BA1-4DB0-94D9-1185C076276F} = {3746073A-1C5C-40A7-9D50-CB2A94CA888A} - {411B666F-EDA0-47E0-BB34-3EAF047549A0} = {6FA93B2E-A5E5-44CA-9851-52FAA2BF5C57} - {0330EB27-814D-4CF6-B377-3BFA884D5FD2} = {3746073A-1C5C-40A7-9D50-CB2A94CA888A} - {78CC88DA-0B25-45BC-86A8-FFE9628B34A6} = {ECBD28DE-AFFB-4C9A-8988-6297FA5ADDD5} - {62644B42-76EF-4FC2-8736-3E9B96C1D832} = {ECBD28DE-AFFB-4C9A-8988-6297FA5ADDD5} - {BAB54FFD-DF77-40B6-86A5-A1A3E0985468} = {62644B42-76EF-4FC2-8736-3E9B96C1D832} - {83E78951-5FBE-43D5-A6BE-7735721E4C1D} = {3746073A-1C5C-40A7-9D50-CB2A94CA888A} - {7A5E9C1B-CE91-463D-B0D1-8117C9E29D30} = {3746073A-1C5C-40A7-9D50-CB2A94CA888A} - {A93DFEEC-0DC7-4628-96F7-914A8053732B} = {3746073A-1C5C-40A7-9D50-CB2A94CA888A} - {F93F72BE-F3F4-4CB6-A3A2-54CDBF3F9B18} = {78CC88DA-0B25-45BC-86A8-FFE9628B34A6} - {EB80F45C-FCC8-436C-900A-754A5E91A8E0} = {78CC88DA-0B25-45BC-86A8-FFE9628B34A6} - {0629A068-957D-4920-9290-0B9288341E0D} = {DE1DF0B9-0403-4888-8B24-8208348E9AFA} - {91F99541-D37F-46D5-8026-AD57C3F75B52} = {0629A068-957D-4920-9290-0B9288341E0D} - {1907A873-8368-4CFF-B5B3-98F04CD2E3A7} = {ECBD28DE-AFFB-4C9A-8988-6297FA5ADDD5} - {AD7CD285-D899-443D-A02C-D62C7C0A351F} = {78CC88DA-0B25-45BC-86A8-FFE9628B34A6} - {0A32F32B-49DF-49AB-8F3C-AFDD28517A89} = {3746073A-1C5C-40A7-9D50-CB2A94CA888A} - {ED44ED49-3F9E-4D14-A600-51D7792A3F64} = {1907A873-8368-4CFF-B5B3-98F04CD2E3A7} - {883EF7C5-4234-4A1B-82BC-36597D280B57} = {1907A873-8368-4CFF-B5B3-98F04CD2E3A7} - {69B79A60-3719-43E5-BE12-78E401C7BC0C} = {62644B42-76EF-4FC2-8736-3E9B96C1D832} - {D1083FEB-81A7-4BBF-ADDB-C03D143BC4AB} = {DE1DF0B9-0403-4888-8B24-8208348E9AFA} - {F680DA14-282E-471E-8E1C-F4D2067B718E} = {D1083FEB-81A7-4BBF-ADDB-C03D143BC4AB} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {AA6E8E23-8AB6-4AB6-9B31-494CB7CE5E63} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01-Core", "01-Core", "{345739E2-E20C-4B1F-B1B2-725006FC9F72}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{0D42DE24-8D3C-4BE1-BAAE-7543281C3113}" + ProjectSection(SolutionItems) = preProject + build\common.props = build\common.props + build\version.props = build\version.props + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C01B9930-D67F-41C5-90C9-C87DC53F39CB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Build", "build\Build.csproj", "{12539757-7371-455D-B86B-DAF22F2DEE4E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Core", "src\Util.Core\01-Util.Core.csproj", "{5F1A7144-C954-4B47-B1FF-820FEA9B6B61}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Core.Tests", "test\Util.Core.Tests\Util.Core.Tests.csproj", "{1E50F81A-FBFF-4873-9731-FA8835A51080}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "14-Ui", "14-Ui", "{354B3720-D050-487D-A46A-22BCFEE56D43}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Ui", "src\Util.Ui\01-Util.Ui.csproj", "{5F7ACBE4-A1D8-4680-AA86-B87F8494BAC2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03-Util.Ui.NgZorro", "src\Util.Ui.NgZorro\03-Util.Ui.NgZorro.csproj", "{BA79977F-6DCE-4787-9A0D-AF5E61EBC177}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Ui.NgZorro.Tests", "test\Util.Ui.NgZorro.Tests\Util.Ui.NgZorro.Tests.csproj", "{2B56E8AA-4DAB-4A70-9439-ED16470C0BFE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01-Core", "01-Core", "{9BFB5DBE-8F64-45A9-BDB2-BAD91D44A4C5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "16-Ui", "16-Ui", "{DE39BB20-AE82-47C3-9141-8CFD4C673217}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Ui.Tests", "test\Util.Ui.Tests\Util.Ui.Tests.csproj", "{766CAB38-F064-419F-837B-D6DA8B61AF66}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Ui.Angular", "src\Util.Ui.Angular\02-Util.Ui.Angular.csproj", "{E8481A4C-0C24-4418-A2C1-742877BE4F0D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Core.Tests.Integration", "test\Util.Core.Tests.Integration\Util.Core.Tests.Integration.csproj", "{21064598-1418-422E-BA37-8EC09541DFC1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "08-Util.AspNetCore", "src\Util.AspNetCore\08-Util.AspNetCore.csproj", "{08EF3E38-E0C3-4E18-984C-3216CB6E8897}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.ObjectMapping.AutoMapper", "src\Util.ObjectMapping.AutoMapper\02-Util.ObjectMapping.AutoMapper.csproj", "{12C94395-09F1-42E4-8CE1-6E54D3FEBAA6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.ObjectMapping.AutoMapper.Tests", "test\Util.ObjectMapping.AutoMapper.Tests\Util.ObjectMapping.AutoMapper.Tests.csproj", "{955D94B8-747D-4CA4-832A-6377C9FF5B8F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03-Util.Aop.AspectCore", "src\Util.Aop.AspectCore\03-Util.Aop.AspectCore.csproj", "{FB337B27-C6C6-43A1-BFA5-58B3CF381BA8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Aop.AspectCore.Tests", "test\Util.Aop.AspectCore.Tests\Util.Aop.AspectCore.Tests.csproj", "{B8825802-81F7-4671-82E8-3A6E3A21F570}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "10-Domain", "10-Domain", "{7AB71D86-D351-4009-A789-24097897E74A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "04-Util.Validation", "src\Util.Validation\04-Util.Validation.csproj", "{1DE277C2-EE56-4F4D-BB89-71107DE0E4C6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Validation.Tests", "test\Util.Validation.Tests\Util.Validation.Tests.csproj", "{166852FC-2F86-4A5B-9029-E16958798DD6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Domain", "src\Util.Domain\01-Util.Domain.csproj", "{61982C8D-5376-45EE-BCC3-D4F9E90AF3B0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "10-Domain", "10-Domain", "{FC5E00D1-ABA5-4168-8B8F-FA2A26D84068}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Domain.Tests", "test\Util.Domain.Tests\Util.Domain.Tests.csproj", "{7CA38535-297B-4DA9-AFF5-3199268EEAF6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Events", "src\Util.Events\02-Util.Events.csproj", "{EE426441-DE33-4F3B-B66A-E258AC725B78}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "11-Data", "11-Data", "{66A85767-2877-4B99-84F1-EEABC6DC5247}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "13-Data", "13-Data", "{CC492CA2-5D86-4DB8-AB15-B118DF27C344}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Data.Tests", "test\Util.Data.Tests\Util.Data.Tests.csproj", "{4D40321F-2C3F-4ACB-8AE0-7A228DF0551B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Data.EntityFrameworkCore", "src\Util.Data.EntityFrameworkCore\01-Util.Data.EntityFrameworkCore.csproj", "{7F7D9EE0-7263-4621-BA65-FC470B0F3C6B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01-Core", "01-Core", "{45E2ADF5-F386-44CD-A43D-A3AB47E9C39A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02-EntityFrameworkCore", "02-EntityFrameworkCore", "{4E99CBC5-1C74-44E7-9DEF-3FA44CADCCF0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Data.EntityFrameworkCore.SqlServer", "src\Util.Data.EntityFrameworkCore.SqlServer\02-Util.Data.EntityFrameworkCore.SqlServer.csproj", "{04E5EDDB-8440-4BAD-9531-F87A3AC826FB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Templates.Razor", "src\Util.Templates.Razor\02-Util.Templates.Razor.csproj", "{4656C609-DBBA-4F6B-B16F-5E1A8C9D9858}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Templates", "src\Util.Templates\01-Util.Templates.csproj", "{F7925EED-65E6-4CA2-9B8D-B7036F6DB3E8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03-Util.Templates.Handlebars", "src\Util.Templates.Handlebars\03-Util.Templates.Handlebars.csproj", "{D22C3F6D-434A-4F09-9EB2-B0433CC43642}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03-Util.Data.Metadata", "src\Util.Data.Metadata\03-Util.Data.Metadata.csproj", "{257A0A17-5D13-4DA4-8E91-8A3BB2711F49}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "03-Dapper", "03-Dapper", "{4A955BB3-C1AF-45DB-9590-82C00996B608}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Data.Dapper.SqlServer", "src\Util.Data.Dapper.SqlServer\02-Util.Data.Dapper.SqlServer.csproj", "{9A2C7C42-086B-4482-828D-7B02D85372ED}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Data.Sql", "src\Util.Data.Sql\02-Util.Data.Sql.csproj", "{661B88AE-6F83-437C-9A7E-C5C1A565E650}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Data.Sql.Tests", "test\Util.Data.Sql.Tests\Util.Data.Sql.Tests.csproj", "{C99E1554-5039-42BD-B1FD-93814FBEBE44}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Data.Dapper.Core", "src\Util.Data.Dapper.Core\01-Util.Data.Dapper.Core.csproj", "{60424AB5-356A-4623-B1FB-7560334C0BE0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "06-Util.Data.Dapper.All", "src\Util.Data.Dapper.All\06-Util.Data.Dapper.All.csproj", "{C2A4E6AB-16B9-4457-9445-FFBF672DFBCF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Domain.Biz", "src\Util.Domain.Biz\02-Util.Domain.Biz.csproj", "{AFB629C5-00C2-492C-8787-C66D810A24DB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "12-Application", "12-Application", "{648CE3E3-CEC4-4F91-8696-6A3FB2F91220}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Application", "src\Util.Application\01-Util.Application.csproj", "{9404B5FE-3431-4EC0-BCC6-42525FC5D1B8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "14-Application", "14-Application", "{BB24822F-F104-4D8E-9CB5-0A94DEF04BAB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Application.Tests", "test\Util.Application.Tests\Util.Application.Tests.csproj", "{EF7BDA1B-22E7-4F3D-97E0-16C9C10AA1B0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Data.Core", "src\Util.Data.Core\01-Util.Data.Core.csproj", "{B5305F08-3856-42B5-92D5-6FD446B39931}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "06-Util.Data.Abstractions", "src\Util.Data.Abstractions\06-Util.Data.Abstractions.csproj", "{88FE7642-27D9-4693-A130-706FCE46194A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration", "test\Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration\Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration.csproj", "{CA43B145-9DF2-49D3-9371-B88BFB0E846A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Application.EntityFrameworkCore", "src\Util.Application.EntityFrameworkCore\02-Util.Application.EntityFrameworkCore.csproj", "{95842BB6-559E-4317-B1E9-FF19D39ADA24}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03-Util.Application.WebApi", "src\Util.Application.WebApi\03-Util.Application.WebApi.csproj", "{3DE7DD7A-8500-46C9-91A1-A65DB60E0235}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03-Util.Data.EntityFrameworkCore.PostgreSql", "src\Util.Data.EntityFrameworkCore.PostgreSql\03-Util.Data.EntityFrameworkCore.PostgreSql.csproj", "{8618EE3F-F120-4AEA-8A77-F7BD5DCCB888}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02-Logging", "02-Logging", "{D5CF25B2-1A91-4065-A647-06CDF55F4B96}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "04-Templates", "04-Templates", "{2697EEFB-06BE-4786-89C6-A566FD94A41B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02-Logging", "02-Logging", "{A6EB05D5-EF38-45B6-960A-0EB759373FDF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "04-Templates", "04-Templates", "{445850B7-1A9D-4E9D-8AC1-765F3792DC19}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "03-Events", "03-Events", "{59F82491-51E3-4A12-B427-9815AEA23D10}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "03-Events", "03-Events", "{3ABD7B4B-37F9-4D49-92D9-384190177286}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Logging", "src\Util.Logging\01-Util.Logging.csproj", "{577A7F6F-4836-4811-B715-32573127BDB3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Logging.Tests", "test\Util.Logging.Tests\Util.Logging.Tests.csproj", "{04EA7A93-CDC7-4C68-815D-699180D99DCE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Logging.Serilog", "src\Util.Logging.Serilog\02-Util.Logging.Serilog.csproj", "{A1186FB9-FB77-427C-9AEC-55E48A8D046A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03-Util.Logging.Serilog.Exceptionless", "src\Util.Logging.Serilog.Exceptionless\03-Util.Logging.Serilog.Exceptionless.csproj", "{9960FA8A-98C1-45FB-8A09-33CA183AA140}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "11-Share", "11-Share", "{E3414144-C19E-40C0-A73A-2FD41AE514D1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.TestShare", "test\Util.TestShare\Util.TestShare.csproj", "{1855CAB7-6379-47A4-A0A4-F890C6C46403}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "04-Util.Data.EntityFrameworkCore.MySql", "src\Util.Data.EntityFrameworkCore.MySql\04-Util.Data.EntityFrameworkCore.MySql.csproj", "{C43539A5-8343-4381-B2FD-5323323F1DB6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Logging.Serilog.Tests.Integration", "test\Util.Logging.Serilog.Tests.Integration\Util.Logging.Serilog.Tests.Integration.csproj", "{D3C326CD-F1F1-4E06-962D-47F6C8090DEE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Logging.Serilog.Exceptionless.Tests.Integration", "test\Util.Logging.Serilog.Exceptionless.Tests.Integration\Util.Logging.Serilog.Exceptionless.Tests.Integration.csproj", "{13690AB8-9AA4-4936-A25F-7AF1AE3D4ED0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration", "test\Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration\Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration.csproj", "{244A930D-5B84-4CBC-A3A7-7E8DE1E603D6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration", "test\Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration\Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration.csproj", "{0EDF10D7-D6D6-48E2-AC0C-BA6D814CB633}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.TestShare.SqlServer", "test\Util.TestShare.SqlServer\Util.TestShare.SqlServer.csproj", "{05D8A63F-B3E1-4A5E-AA66-337BCF8840C1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.TestShare.PostgreSql", "test\Util.TestShare.PostgreSql\Util.TestShare.PostgreSql.csproj", "{3993A838-71C9-49C5-80B3-16CC48A5F5C9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Data.EntityFrameworkCore.MySql.Tests.Integration", "test\Util.Data.EntityFrameworkCore.MySql.Tests.Integration\Util.Data.EntityFrameworkCore.MySql.Tests.Integration.csproj", "{D785A240-1115-41C8-943E-5058161D6478}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.TestShare.MySql", "test\Util.TestShare.MySql\Util.TestShare.MySql.csproj", "{B1952639-773B-4C82-9D95-7CB4ED8AC59E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Data.Dapper.SqlServer.Tests.Integration", "test\Util.Data.Dapper.SqlServer.Tests.Integration\Util.Data.Dapper.SqlServer.Tests.Integration.csproj", "{29792C72-565A-46DF-B887-494AC5352818}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration", "test\Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration\Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration.csproj", "{C57D22E0-075B-4F27-87F1-E6CF5F203975}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Application.EntityFrameworkCore.MySql.Tests.Integration", "test\Util.Application.EntityFrameworkCore.MySql.Tests.Integration\Util.Application.EntityFrameworkCore.MySql.Tests.Integration.csproj", "{B9B66159-4FC7-4F94-8C21-ADC8CCED84F4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Application.WebApi.SqlServer.Tests.Integration", "test\Util.Application.WebApi.SqlServer.Tests.Integration\Util.Application.WebApi.SqlServer.Tests.Integration.csproj", "{24FFA5F1-37CD-408B-80A9-006B5A6E11E8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Application.WebApi.PostgreSql.Tests.Integration", "test\Util.Application.WebApi.PostgreSql.Tests.Integration\Util.Application.WebApi.PostgreSql.Tests.Integration.csproj", "{6FE28297-B958-4BC5-A320-A531874D7F58}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Application.WebApi.MySql.Tests.Integration", "test\Util.Application.WebApi.MySql.Tests.Integration\Util.Application.WebApi.MySql.Tests.Integration.csproj", "{838A27C6-F125-4C61-923E-7C855B598FBD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Data.Dapper.PostgreSql.Tests.Integration", "test\Util.Data.Dapper.PostgreSql.Tests.Integration\Util.Data.Dapper.PostgreSql.Tests.Integration.csproj", "{FDE43241-3C82-4005-87B3-FD1349FEF4B6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03-Util.Data.Dapper.PostgreSql", "src\Util.Data.Dapper.PostgreSql\03-Util.Data.Dapper.PostgreSql.csproj", "{878A2744-5122-48A7-90E6-7D8CFABC29FD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "04-Util.Data.Dapper.MySql", "src\Util.Data.Dapper.MySql\04-Util.Data.Dapper.MySql.csproj", "{0C464725-DEDC-4894-84A3-7A155E43818B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Data.Dapper.MySql.Tests.Integration", "test\Util.Data.Dapper.MySql.Tests.Integration\Util.Data.Dapper.MySql.Tests.Integration.csproj", "{AC831843-CDE2-4B67-8BC5-8F2A0D09B273}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "05-Util.Data.EntityFrameworkCore.Sqlite", "src\Util.Data.EntityFrameworkCore.Sqlite\05-Util.Data.EntityFrameworkCore.Sqlite.csproj", "{2BE691A3-19C0-4FCF-85D2-D2371C9462C6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration", "test\Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration\Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration.csproj", "{B140F739-EC51-4265-B4B8-59467606E6EE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.TestShare.Sqlite", "test\Util.TestShare.Sqlite\Util.TestShare.Sqlite.csproj", "{00E8EE51-A263-4971-9431-C4A2DEA4993A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "06-Util.Data.EntityFrameworkCore.Oracle", "src\Util.Data.EntityFrameworkCore.Oracle\06-Util.Data.EntityFrameworkCore.Oracle.csproj", "{6B2BA282-5C97-4A71-885F-E5259274F714}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.TestShare.Oracle", "test\Util.TestShare.Oracle\Util.TestShare.Oracle.csproj", "{05CF624D-F42E-461A-A21D-41BEE0CCEB5C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Data.EntityFrameworkCore.Oracle.Tests.Integration", "test\Util.Data.EntityFrameworkCore.Oracle.Tests.Integration\Util.Data.EntityFrameworkCore.Oracle.Tests.Integration.csproj", "{C4A782C1-AA25-476B-B936-90433422D0A2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "09-Util.Localization", "src\Util.Localization\09-Util.Localization.csproj", "{D9E411DD-58B1-4160-8025-068D43ADE72C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Localization.Tests.Integration", "test\Util.Localization.Tests.Integration\Util.Localization.Tests.Integration.csproj", "{B2AADB36-8433-4EDB-B247-49A3EA82FE06}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Localization.Resources", "test\Util.Localization.Resources\Util.Localization.Resources.csproj", "{40F2A962-6209-42D0-9F21-CEACB411007B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "05-Caching", "05-Caching", "{22E49D65-F51D-4A45-A35E-B27FF7DE6787}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "05-Caching", "05-Caching", "{B85E8FC7-748E-4990-9279-1F06E095B6E6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Caching", "src\Util.Caching\01-Util.Caching.csproj", "{4170A699-C110-4136-92EA-D2BFEBF68230}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Caching.EasyCaching", "src\Util.Caching.EasyCaching\02-Util.Caching.EasyCaching.csproj", "{78B9EC26-ADC5-4934-9F31-000D4D678121}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Caching.EasyCaching.Tests.Integration", "test\Util.Caching.EasyCaching.Tests.Integration\Util.Caching.EasyCaching.Tests.Integration.csproj", "{027B0175-2D22-4168-A0B4-1E0DC5BE2A61}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Application.WebApi.Tests.Integration", "test\Util.Application.WebApi.Tests.Integration\Util.Application.WebApi.Tests.Integration.csproj", "{EA4C219B-D00A-4BDB-A787-A79FA9611303}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "04-Util.Ui.NgAlain", "src\Util.Ui.NgAlain\04-Util.Ui.NgAlain.csproj", "{B40CD480-D4A0-4A43-B855-0CD84EC537CC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Ui.NgAlain.Tests", "test\Util.Ui.NgAlain.Tests\Util.Ui.NgAlain.Tests.csproj", "{C3C7C037-B5B6-4CE0-BC02-820667D74F60}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.AspNetCore.Tests.Integration", "test\Util.AspNetCore.Tests.Integration\Util.AspNetCore.Tests.Integration.csproj", "{48F77D8F-60AE-4173-BA74-FC2E0B3578BB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "06-Scheduling", "06-Scheduling", "{76AA6040-AA3A-43DD-BD4B-7F931271BFD6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "12-Scheduling", "12-Scheduling", "{6493D592-7930-4979-A07D-B7CAA751C7BE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Scheduling.Quartz", "src\Util.Scheduling.Quartz\02-Util.Scheduling.Quartz.csproj", "{4E12B03E-D607-492E-BB28-96235993A39F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Scheduling.Quartz.Tests.Integration", "test\Util.Scheduling.Quartz.Tests.Integration\Util.Scheduling.Quartz.Tests.Integration.csproj", "{D2F38ACB-7158-4DE0-BDEA-B13E3D52A655}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Scheduling", "src\Util.Scheduling\01-Util.Scheduling.csproj", "{1272E006-8D8B-46B5-AD18-2E31CA9C6CBD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03-Util.Scheduling.Hangfire", "src\Util.Scheduling.Hangfire\03-Util.Scheduling.Hangfire.csproj", "{E0D77981-E412-477D-B866-E43F055CAAFC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Scheduling.Hangfire.Tests.Integration", "test\Util.Scheduling.Hangfire.Tests.Integration\Util.Scheduling.Hangfire.Tests.Integration.csproj", "{66B96C01-6A08-4C7A-B5F5-A0489D8BBB47}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "05-Util.Security", "src\Util.Security\05-Util.Security.csproj", "{8BC69296-3C9A-45B3-B6C3-3DD575027CCD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Security.Tests", "test\Util.Security.Tests\Util.Security.Tests.csproj", "{B25D3B7A-AEAA-4EFB-9839-7C6E3F7C24BB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "07-Tools", "07-Tools", "{1F5EE147-BD88-4B6D-BA84-35B4F3BE4A4E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Images.ImageSharp", "src\Util.Images.ImageSharp\01-Util.Images.ImageSharp.csproj", "{22305349-51C7-4D80-A538-C7C0F4495895}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "06-Localization", "06-Localization", "{54800E45-FF91-4091-A146-EFA5432A6443}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02-Dapper", "02-Dapper", "{664F0DBE-CB31-4D63-858A-8C7E6411C76C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "03-EntityFrameworkCore", "03-EntityFrameworkCore", "{7D087635-9E77-46B2-B647-962F7E2F8688}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01-Core", "01-Core", "{2864196E-3044-4FEC-B92A-D1D3C521C567}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "07-Tools", "07-Tools", "{96DFADC5-FAE8-4860-A57A-93F0534B1B7A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Images.ImageSharp.Tests.Integration", "test\Util.Images.ImageSharp.Tests.Integration\Util.Images.ImageSharp.Tests.Integration.csproj", "{5921A7FA-8C70-49A9-82E1-BE3FA5B50C33}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Images.Avatar", "src\Util.Images.Avatar\02-Util.Images.Avatar.csproj", "{599BAD8E-DB56-4967-8DDE-74BD21DA2B49}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Images.Avatar.Tests.Integration", "test\Util.Images.Avatar.Tests.Integration\Util.Images.Avatar.Tests.Integration.csproj", "{75A31AAB-B7E6-4F13-9B7A-B74216B2EDDE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "15-Generators", "15-Generators", "{411247A4-4665-4BE6-8E5C-08F6D0BA2213}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Generators", "src\Util.Generators\01-Util.Generators.csproj", "{36990D32-6F41-44A6-BB88-A3F15E12C35B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Generators.Razor", "src\Util.Generators.Razor\02-Util.Generators.Razor.csproj", "{21831A4F-B3C9-40E7-855B-75F9D679FF55}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "17-Generators", "17-Generators", "{F7D4E7CB-468D-4570-8D82-D065F08BE40D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Generators.Tests", "test\Util.Generators.Tests\Util.Generators.Tests.csproj", "{5996F536-14B9-4F95-A18B-716E5F7EE478}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "08-FileStorage", "08-FileStorage", "{E069CEB0-8092-4907-BC9D-C6B8BDE20AD2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.FileStorage", "src\Util.FileStorage\02-Util.FileStorage.csproj", "{6FC29D15-7581-4E29-9897-76DC5AEF2042}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03-Util.FileStorage.Minio", "src\Util.FileStorage.Minio\03-Util.FileStorage.Minio.csproj", "{3E81F8D0-06C7-4FD4-A5B0-E3D96E909646}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "04-Util.FileStorage.Aliyun", "src\Util.FileStorage.Aliyun\04-Util.FileStorage.Aliyun.csproj", "{C233FF83-E15D-4A17-8FF1-BBFF9E9DE210}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "08-FileStorage", "08-FileStorage", "{16175228-03CA-414A-8786-E5BA844110C4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.FileStorage.Minio.Tests.Integration", "test\Util.FileStorage.Minio.Tests.Integration\Util.FileStorage.Minio.Tests.Integration.csproj", "{AB2D6B9A-0EC8-402B-849A-95E826B09AB3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Templates.Handlebars.Tests.Integration", "test\Util.Templates.Handlebars.Tests.Integration\Util.Templates.Handlebars.Tests.Integration.csproj", "{87B6EF51-9F1B-4A81-9764-F359138B774D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Templates.Razor.Tests.Integration", "test\Util.Templates.Razor.Tests.Integration\Util.Templates.Razor.Tests.Integration.csproj", "{4FFD1073-9358-418D-BB4D-DB63FE0A11DF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Generators.Razor.Tests.Integration", "test\Util.Generators.Razor.Tests.Integration\Util.Generators.Razor.Tests.Integration.csproj", "{5BFF8BA0-EA27-4493-88A5-5BC57286D536}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Events.Tests.Integration", "test\Util.Events.Tests.Integration\Util.Events.Tests.Integration.csproj", "{FB11256C-06C0-4B24-863A-C87D78F8EE2A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "15-Microservices", "15-Microservices", "{547FD391-A56B-47A6-B20C-5FC04FA15C56}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Events.Abstractions", "src\Util.Events.Abstractions\01-Util.Events.Abstractions.csproj", "{6233BF36-9636-43BD-9462-2D31A05EE4ED}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "13-Microservices", "13-Microservices", "{1DEC52C8-6692-4DA7-AE34-B76D728F895A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Microservices", "src\Util.Microservices\01-Util.Microservices.csproj", "{F8417623-6936-43C4-A967-9D6BD002AD16}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03-Util.Microservices.Dapr", "src\Util.Microservices.Dapr\03-Util.Microservices.Dapr.csproj", "{CAA32331-4A8E-4C16-BC7D-7CA646B393F4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Microservices.Dapr.Tests.Integration", "test\Util.Microservices.Dapr.Tests.Integration\Util.Microservices.Dapr.Tests.Integration.csproj", "{08B1A026-4F8C-4336-B5B8-3189023DFF2B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Microservices.Polly", "src\Util.Microservices.Polly\02-Util.Microservices.Polly.csproj", "{0BCB6FF1-E374-4AED-9888-83C7CFED0E98}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Microservices.Polly.Tests.Integration", "test\Util.Microservices.Polly.Tests.Integration\Util.Microservices.Polly.Tests.Integration.csproj", "{DF1E55A5-7EA8-41B2-BEBA-72544D256069}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "04-Util.Microservices.HealthChecks", "src\Util.Microservices.HealthChecks\04-Util.Microservices.HealthChecks.csproj", "{9C58E50F-0589-4A4E-B94D-2EC0BF02F61F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Microservices.Dapr.WebApiSample", "test\Util.Microservices.Dapr.WebApiSample\Util.Microservices.Dapr.WebApiSample.csproj", "{AE19ED93-AFC4-4F91-93BD-B00D343D3BA0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Microservices.Dapr.PubsubSample", "test\Util.Microservices.Dapr.PubsubSample\Util.Microservices.Dapr.PubsubSample.csproj", "{5D74BD1D-3142-4478-9062-4543FADA3E91}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Microservices.Dapr.Tests", "test\Util.Microservices.Dapr.Tests\Util.Microservices.Dapr.Tests.csproj", "{B737883C-9450-46CB-B81C-1A3405071D04}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02-Util.Tenants", "src\Util.Tenants\02-Util.Tenants.csproj", "{156447ED-8515-4FB0-8B5A-D5A3A1E43AC6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Tenants.Tests", "test\Util.Tenants.Tests\Util.Tenants.Tests.csproj", "{DE51B662-C843-4E99-87F8-F367504D5640}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "09-Tenant", "09-Tenant", "{98F1FEAD-AFD5-4744-8A55-C57920461EB9}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "09-Tenant", "09-Tenant", "{835A8155-411B-4DF2-AE58-D3FDEFB61E63}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.Tenants.Abstractions", "src\Util.Tenants.Abstractions\01-Util.Tenants.Abstractions.csproj", "{0C062400-58D1-4EE3-8EBB-C5B3AAEECF87}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03-Util.Events.MediatR", "src\Util.Events.MediatR\03-Util.Events.MediatR.csproj", "{FA6AF0F1-B4EE-4E46-B933-9573861A10FC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Events.MediatR.Tests.Integration", "test\Util.Events.MediatR.Tests.Integration\Util.Events.MediatR.Tests.Integration.csproj", "{4FECE655-7B06-4DED-BED0-B67731A43E0B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Tenants.Tests.Integration", "test\Util.Tenants.Tests.Integration\Util.Tenants.Tests.Integration.csproj", "{6A84E4B0-3A9C-4B40-9B64-AD3A26324DAC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Localization.Tests", "test\Util.Localization.Tests\Util.Localization.Tests.csproj", "{9B941AF9-34A0-4DFF-80F8-0418E212659A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Data.Dapper.Oracle.Tests.Integration", "test\Util.Data.Dapper.Oracle.Tests.Integration\Util.Data.Dapper.Oracle.Tests.Integration.csproj", "{A44B570E-0676-4003-A827-E910A9332DEA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "05-Util.Data.Dapper.Oracle", "src\Util.Data.Dapper.Oracle\05-Util.Data.Dapper.Oracle.csproj", "{00C518D5-EFAC-43CC-AD1E-9837BDBD93BB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "10-Util.IdGenerator", "src\Util.IdGenerator\10-Util.IdGenerator.csproj", "{698AE3A0-65E3-433C-B56C-7D47A7A44263}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.IdGenerator.Tests", "test\Util.IdGenerator.Tests\Util.IdGenerator.Tests.csproj", "{BA78E270-92A7-46C2-932B-3109E9DC5824}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "07-Util.Http", "src\Util.Http\07-Util.Http.csproj", "{EDB3405A-58BD-444C-82FF-05FAB7CE72B4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.FileStorage.Tests.Integration", "test\Util.FileStorage.Tests.Integration\Util.FileStorage.Tests.Integration.csproj", "{A23BA1A3-8EC7-4937-B0AA-69CCEE40453C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "05-Util.FileStorage.All", "src\Util.FileStorage.All\05-Util.FileStorage.All.csproj", "{4938D59D-CF50-4090-B936-36DB29EBE344}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.FileStorage.Aliyun.Tests.Integration", "test\Util.FileStorage.Aliyun.Tests.Integration\Util.FileStorage.Aliyun.Tests.Integration.csproj", "{58C01936-5D8F-4077-8663-EE0E35776D65}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "01-Util.FileStorage.Abstractions", "src\Util.FileStorage.Abstractions\01-Util.FileStorage.Abstractions.csproj", "{814E42E9-508E-487F-BCD3-0F07AE7D1492}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "03-Util.QrCode.ZXing", "src\Util.QrCode.ZXing\03-Util.QrCode.ZXing.csproj", "{3F5120A6-9CA4-4B25-9507-86E9E5571DB6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.QrCode.ZXing.Tests.Integration", "test\Util.QrCode.ZXing.Tests.Integration\Util.QrCode.ZXing.Tests.Integration.csproj", "{79A40AC0-A182-49D0-AED9-DF8B6835432A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {12539757-7371-455D-B86B-DAF22F2DEE4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {12539757-7371-455D-B86B-DAF22F2DEE4E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {12539757-7371-455D-B86B-DAF22F2DEE4E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {12539757-7371-455D-B86B-DAF22F2DEE4E}.Release|Any CPU.Build.0 = Release|Any CPU + {5F1A7144-C954-4B47-B1FF-820FEA9B6B61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F1A7144-C954-4B47-B1FF-820FEA9B6B61}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F1A7144-C954-4B47-B1FF-820FEA9B6B61}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F1A7144-C954-4B47-B1FF-820FEA9B6B61}.Release|Any CPU.Build.0 = Release|Any CPU + {1E50F81A-FBFF-4873-9731-FA8835A51080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1E50F81A-FBFF-4873-9731-FA8835A51080}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E50F81A-FBFF-4873-9731-FA8835A51080}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1E50F81A-FBFF-4873-9731-FA8835A51080}.Release|Any CPU.Build.0 = Release|Any CPU + {5F7ACBE4-A1D8-4680-AA86-B87F8494BAC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F7ACBE4-A1D8-4680-AA86-B87F8494BAC2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F7ACBE4-A1D8-4680-AA86-B87F8494BAC2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F7ACBE4-A1D8-4680-AA86-B87F8494BAC2}.Release|Any CPU.Build.0 = Release|Any CPU + {BA79977F-6DCE-4787-9A0D-AF5E61EBC177}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BA79977F-6DCE-4787-9A0D-AF5E61EBC177}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BA79977F-6DCE-4787-9A0D-AF5E61EBC177}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BA79977F-6DCE-4787-9A0D-AF5E61EBC177}.Release|Any CPU.Build.0 = Release|Any CPU + {2B56E8AA-4DAB-4A70-9439-ED16470C0BFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2B56E8AA-4DAB-4A70-9439-ED16470C0BFE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2B56E8AA-4DAB-4A70-9439-ED16470C0BFE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2B56E8AA-4DAB-4A70-9439-ED16470C0BFE}.Release|Any CPU.Build.0 = Release|Any CPU + {766CAB38-F064-419F-837B-D6DA8B61AF66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {766CAB38-F064-419F-837B-D6DA8B61AF66}.Debug|Any CPU.Build.0 = Debug|Any CPU + {766CAB38-F064-419F-837B-D6DA8B61AF66}.Release|Any CPU.ActiveCfg = Release|Any CPU + {766CAB38-F064-419F-837B-D6DA8B61AF66}.Release|Any CPU.Build.0 = Release|Any CPU + {E8481A4C-0C24-4418-A2C1-742877BE4F0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E8481A4C-0C24-4418-A2C1-742877BE4F0D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E8481A4C-0C24-4418-A2C1-742877BE4F0D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E8481A4C-0C24-4418-A2C1-742877BE4F0D}.Release|Any CPU.Build.0 = Release|Any CPU + {21064598-1418-422E-BA37-8EC09541DFC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {21064598-1418-422E-BA37-8EC09541DFC1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {21064598-1418-422E-BA37-8EC09541DFC1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {21064598-1418-422E-BA37-8EC09541DFC1}.Release|Any CPU.Build.0 = Release|Any CPU + {08EF3E38-E0C3-4E18-984C-3216CB6E8897}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {08EF3E38-E0C3-4E18-984C-3216CB6E8897}.Debug|Any CPU.Build.0 = Debug|Any CPU + {08EF3E38-E0C3-4E18-984C-3216CB6E8897}.Release|Any CPU.ActiveCfg = Release|Any CPU + {08EF3E38-E0C3-4E18-984C-3216CB6E8897}.Release|Any CPU.Build.0 = Release|Any CPU + {12C94395-09F1-42E4-8CE1-6E54D3FEBAA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {12C94395-09F1-42E4-8CE1-6E54D3FEBAA6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {12C94395-09F1-42E4-8CE1-6E54D3FEBAA6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {12C94395-09F1-42E4-8CE1-6E54D3FEBAA6}.Release|Any CPU.Build.0 = Release|Any CPU + {955D94B8-747D-4CA4-832A-6377C9FF5B8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {955D94B8-747D-4CA4-832A-6377C9FF5B8F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {955D94B8-747D-4CA4-832A-6377C9FF5B8F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {955D94B8-747D-4CA4-832A-6377C9FF5B8F}.Release|Any CPU.Build.0 = Release|Any CPU + {FB337B27-C6C6-43A1-BFA5-58B3CF381BA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB337B27-C6C6-43A1-BFA5-58B3CF381BA8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB337B27-C6C6-43A1-BFA5-58B3CF381BA8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB337B27-C6C6-43A1-BFA5-58B3CF381BA8}.Release|Any CPU.Build.0 = Release|Any CPU + {B8825802-81F7-4671-82E8-3A6E3A21F570}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B8825802-81F7-4671-82E8-3A6E3A21F570}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B8825802-81F7-4671-82E8-3A6E3A21F570}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B8825802-81F7-4671-82E8-3A6E3A21F570}.Release|Any CPU.Build.0 = Release|Any CPU + {1DE277C2-EE56-4F4D-BB89-71107DE0E4C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1DE277C2-EE56-4F4D-BB89-71107DE0E4C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1DE277C2-EE56-4F4D-BB89-71107DE0E4C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1DE277C2-EE56-4F4D-BB89-71107DE0E4C6}.Release|Any CPU.Build.0 = Release|Any CPU + {166852FC-2F86-4A5B-9029-E16958798DD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {166852FC-2F86-4A5B-9029-E16958798DD6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {166852FC-2F86-4A5B-9029-E16958798DD6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {166852FC-2F86-4A5B-9029-E16958798DD6}.Release|Any CPU.Build.0 = Release|Any CPU + {61982C8D-5376-45EE-BCC3-D4F9E90AF3B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {61982C8D-5376-45EE-BCC3-D4F9E90AF3B0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {61982C8D-5376-45EE-BCC3-D4F9E90AF3B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {61982C8D-5376-45EE-BCC3-D4F9E90AF3B0}.Release|Any CPU.Build.0 = Release|Any CPU + {7CA38535-297B-4DA9-AFF5-3199268EEAF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7CA38535-297B-4DA9-AFF5-3199268EEAF6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7CA38535-297B-4DA9-AFF5-3199268EEAF6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7CA38535-297B-4DA9-AFF5-3199268EEAF6}.Release|Any CPU.Build.0 = Release|Any CPU + {EE426441-DE33-4F3B-B66A-E258AC725B78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EE426441-DE33-4F3B-B66A-E258AC725B78}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EE426441-DE33-4F3B-B66A-E258AC725B78}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EE426441-DE33-4F3B-B66A-E258AC725B78}.Release|Any CPU.Build.0 = Release|Any CPU + {4D40321F-2C3F-4ACB-8AE0-7A228DF0551B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D40321F-2C3F-4ACB-8AE0-7A228DF0551B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D40321F-2C3F-4ACB-8AE0-7A228DF0551B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D40321F-2C3F-4ACB-8AE0-7A228DF0551B}.Release|Any CPU.Build.0 = Release|Any CPU + {7F7D9EE0-7263-4621-BA65-FC470B0F3C6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F7D9EE0-7263-4621-BA65-FC470B0F3C6B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F7D9EE0-7263-4621-BA65-FC470B0F3C6B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F7D9EE0-7263-4621-BA65-FC470B0F3C6B}.Release|Any CPU.Build.0 = Release|Any CPU + {04E5EDDB-8440-4BAD-9531-F87A3AC826FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04E5EDDB-8440-4BAD-9531-F87A3AC826FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04E5EDDB-8440-4BAD-9531-F87A3AC826FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04E5EDDB-8440-4BAD-9531-F87A3AC826FB}.Release|Any CPU.Build.0 = Release|Any CPU + {4656C609-DBBA-4F6B-B16F-5E1A8C9D9858}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4656C609-DBBA-4F6B-B16F-5E1A8C9D9858}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4656C609-DBBA-4F6B-B16F-5E1A8C9D9858}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4656C609-DBBA-4F6B-B16F-5E1A8C9D9858}.Release|Any CPU.Build.0 = Release|Any CPU + {F7925EED-65E6-4CA2-9B8D-B7036F6DB3E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F7925EED-65E6-4CA2-9B8D-B7036F6DB3E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F7925EED-65E6-4CA2-9B8D-B7036F6DB3E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F7925EED-65E6-4CA2-9B8D-B7036F6DB3E8}.Release|Any CPU.Build.0 = Release|Any CPU + {D22C3F6D-434A-4F09-9EB2-B0433CC43642}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D22C3F6D-434A-4F09-9EB2-B0433CC43642}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D22C3F6D-434A-4F09-9EB2-B0433CC43642}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D22C3F6D-434A-4F09-9EB2-B0433CC43642}.Release|Any CPU.Build.0 = Release|Any CPU + {257A0A17-5D13-4DA4-8E91-8A3BB2711F49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {257A0A17-5D13-4DA4-8E91-8A3BB2711F49}.Debug|Any CPU.Build.0 = Debug|Any CPU + {257A0A17-5D13-4DA4-8E91-8A3BB2711F49}.Release|Any CPU.ActiveCfg = Release|Any CPU + {257A0A17-5D13-4DA4-8E91-8A3BB2711F49}.Release|Any CPU.Build.0 = Release|Any CPU + {9A2C7C42-086B-4482-828D-7B02D85372ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9A2C7C42-086B-4482-828D-7B02D85372ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9A2C7C42-086B-4482-828D-7B02D85372ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9A2C7C42-086B-4482-828D-7B02D85372ED}.Release|Any CPU.Build.0 = Release|Any CPU + {661B88AE-6F83-437C-9A7E-C5C1A565E650}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {661B88AE-6F83-437C-9A7E-C5C1A565E650}.Debug|Any CPU.Build.0 = Debug|Any CPU + {661B88AE-6F83-437C-9A7E-C5C1A565E650}.Release|Any CPU.ActiveCfg = Release|Any CPU + {661B88AE-6F83-437C-9A7E-C5C1A565E650}.Release|Any CPU.Build.0 = Release|Any CPU + {C99E1554-5039-42BD-B1FD-93814FBEBE44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C99E1554-5039-42BD-B1FD-93814FBEBE44}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C99E1554-5039-42BD-B1FD-93814FBEBE44}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C99E1554-5039-42BD-B1FD-93814FBEBE44}.Release|Any CPU.Build.0 = Release|Any CPU + {60424AB5-356A-4623-B1FB-7560334C0BE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {60424AB5-356A-4623-B1FB-7560334C0BE0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {60424AB5-356A-4623-B1FB-7560334C0BE0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {60424AB5-356A-4623-B1FB-7560334C0BE0}.Release|Any CPU.Build.0 = Release|Any CPU + {C2A4E6AB-16B9-4457-9445-FFBF672DFBCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2A4E6AB-16B9-4457-9445-FFBF672DFBCF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2A4E6AB-16B9-4457-9445-FFBF672DFBCF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2A4E6AB-16B9-4457-9445-FFBF672DFBCF}.Release|Any CPU.Build.0 = Release|Any CPU + {AFB629C5-00C2-492C-8787-C66D810A24DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AFB629C5-00C2-492C-8787-C66D810A24DB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AFB629C5-00C2-492C-8787-C66D810A24DB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AFB629C5-00C2-492C-8787-C66D810A24DB}.Release|Any CPU.Build.0 = Release|Any CPU + {9404B5FE-3431-4EC0-BCC6-42525FC5D1B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9404B5FE-3431-4EC0-BCC6-42525FC5D1B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9404B5FE-3431-4EC0-BCC6-42525FC5D1B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9404B5FE-3431-4EC0-BCC6-42525FC5D1B8}.Release|Any CPU.Build.0 = Release|Any CPU + {EF7BDA1B-22E7-4F3D-97E0-16C9C10AA1B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF7BDA1B-22E7-4F3D-97E0-16C9C10AA1B0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF7BDA1B-22E7-4F3D-97E0-16C9C10AA1B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF7BDA1B-22E7-4F3D-97E0-16C9C10AA1B0}.Release|Any CPU.Build.0 = Release|Any CPU + {B5305F08-3856-42B5-92D5-6FD446B39931}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B5305F08-3856-42B5-92D5-6FD446B39931}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B5305F08-3856-42B5-92D5-6FD446B39931}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B5305F08-3856-42B5-92D5-6FD446B39931}.Release|Any CPU.Build.0 = Release|Any CPU + {88FE7642-27D9-4693-A130-706FCE46194A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {88FE7642-27D9-4693-A130-706FCE46194A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {88FE7642-27D9-4693-A130-706FCE46194A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {88FE7642-27D9-4693-A130-706FCE46194A}.Release|Any CPU.Build.0 = Release|Any CPU + {CA43B145-9DF2-49D3-9371-B88BFB0E846A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CA43B145-9DF2-49D3-9371-B88BFB0E846A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CA43B145-9DF2-49D3-9371-B88BFB0E846A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CA43B145-9DF2-49D3-9371-B88BFB0E846A}.Release|Any CPU.Build.0 = Release|Any CPU + {95842BB6-559E-4317-B1E9-FF19D39ADA24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {95842BB6-559E-4317-B1E9-FF19D39ADA24}.Debug|Any CPU.Build.0 = Debug|Any CPU + {95842BB6-559E-4317-B1E9-FF19D39ADA24}.Release|Any CPU.ActiveCfg = Release|Any CPU + {95842BB6-559E-4317-B1E9-FF19D39ADA24}.Release|Any CPU.Build.0 = Release|Any CPU + {3DE7DD7A-8500-46C9-91A1-A65DB60E0235}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3DE7DD7A-8500-46C9-91A1-A65DB60E0235}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3DE7DD7A-8500-46C9-91A1-A65DB60E0235}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3DE7DD7A-8500-46C9-91A1-A65DB60E0235}.Release|Any CPU.Build.0 = Release|Any CPU + {8618EE3F-F120-4AEA-8A77-F7BD5DCCB888}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8618EE3F-F120-4AEA-8A77-F7BD5DCCB888}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8618EE3F-F120-4AEA-8A77-F7BD5DCCB888}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8618EE3F-F120-4AEA-8A77-F7BD5DCCB888}.Release|Any CPU.Build.0 = Release|Any CPU + {577A7F6F-4836-4811-B715-32573127BDB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {577A7F6F-4836-4811-B715-32573127BDB3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {577A7F6F-4836-4811-B715-32573127BDB3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {577A7F6F-4836-4811-B715-32573127BDB3}.Release|Any CPU.Build.0 = Release|Any CPU + {04EA7A93-CDC7-4C68-815D-699180D99DCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04EA7A93-CDC7-4C68-815D-699180D99DCE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04EA7A93-CDC7-4C68-815D-699180D99DCE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04EA7A93-CDC7-4C68-815D-699180D99DCE}.Release|Any CPU.Build.0 = Release|Any CPU + {A1186FB9-FB77-427C-9AEC-55E48A8D046A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1186FB9-FB77-427C-9AEC-55E48A8D046A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1186FB9-FB77-427C-9AEC-55E48A8D046A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1186FB9-FB77-427C-9AEC-55E48A8D046A}.Release|Any CPU.Build.0 = Release|Any CPU + {9960FA8A-98C1-45FB-8A09-33CA183AA140}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9960FA8A-98C1-45FB-8A09-33CA183AA140}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9960FA8A-98C1-45FB-8A09-33CA183AA140}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9960FA8A-98C1-45FB-8A09-33CA183AA140}.Release|Any CPU.Build.0 = Release|Any CPU + {1855CAB7-6379-47A4-A0A4-F890C6C46403}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1855CAB7-6379-47A4-A0A4-F890C6C46403}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1855CAB7-6379-47A4-A0A4-F890C6C46403}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1855CAB7-6379-47A4-A0A4-F890C6C46403}.Release|Any CPU.Build.0 = Release|Any CPU + {C43539A5-8343-4381-B2FD-5323323F1DB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C43539A5-8343-4381-B2FD-5323323F1DB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C43539A5-8343-4381-B2FD-5323323F1DB6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C43539A5-8343-4381-B2FD-5323323F1DB6}.Release|Any CPU.Build.0 = Release|Any CPU + {D3C326CD-F1F1-4E06-962D-47F6C8090DEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D3C326CD-F1F1-4E06-962D-47F6C8090DEE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D3C326CD-F1F1-4E06-962D-47F6C8090DEE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D3C326CD-F1F1-4E06-962D-47F6C8090DEE}.Release|Any CPU.Build.0 = Release|Any CPU + {13690AB8-9AA4-4936-A25F-7AF1AE3D4ED0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {13690AB8-9AA4-4936-A25F-7AF1AE3D4ED0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {13690AB8-9AA4-4936-A25F-7AF1AE3D4ED0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {13690AB8-9AA4-4936-A25F-7AF1AE3D4ED0}.Release|Any CPU.Build.0 = Release|Any CPU + {244A930D-5B84-4CBC-A3A7-7E8DE1E603D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {244A930D-5B84-4CBC-A3A7-7E8DE1E603D6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {244A930D-5B84-4CBC-A3A7-7E8DE1E603D6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {244A930D-5B84-4CBC-A3A7-7E8DE1E603D6}.Release|Any CPU.Build.0 = Release|Any CPU + {0EDF10D7-D6D6-48E2-AC0C-BA6D814CB633}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0EDF10D7-D6D6-48E2-AC0C-BA6D814CB633}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0EDF10D7-D6D6-48E2-AC0C-BA6D814CB633}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0EDF10D7-D6D6-48E2-AC0C-BA6D814CB633}.Release|Any CPU.Build.0 = Release|Any CPU + {05D8A63F-B3E1-4A5E-AA66-337BCF8840C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {05D8A63F-B3E1-4A5E-AA66-337BCF8840C1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {05D8A63F-B3E1-4A5E-AA66-337BCF8840C1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {05D8A63F-B3E1-4A5E-AA66-337BCF8840C1}.Release|Any CPU.Build.0 = Release|Any CPU + {3993A838-71C9-49C5-80B3-16CC48A5F5C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3993A838-71C9-49C5-80B3-16CC48A5F5C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3993A838-71C9-49C5-80B3-16CC48A5F5C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3993A838-71C9-49C5-80B3-16CC48A5F5C9}.Release|Any CPU.Build.0 = Release|Any CPU + {D785A240-1115-41C8-943E-5058161D6478}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D785A240-1115-41C8-943E-5058161D6478}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D785A240-1115-41C8-943E-5058161D6478}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D785A240-1115-41C8-943E-5058161D6478}.Release|Any CPU.Build.0 = Release|Any CPU + {B1952639-773B-4C82-9D95-7CB4ED8AC59E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1952639-773B-4C82-9D95-7CB4ED8AC59E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1952639-773B-4C82-9D95-7CB4ED8AC59E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1952639-773B-4C82-9D95-7CB4ED8AC59E}.Release|Any CPU.Build.0 = Release|Any CPU + {29792C72-565A-46DF-B887-494AC5352818}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {29792C72-565A-46DF-B887-494AC5352818}.Debug|Any CPU.Build.0 = Debug|Any CPU + {29792C72-565A-46DF-B887-494AC5352818}.Release|Any CPU.ActiveCfg = Release|Any CPU + {29792C72-565A-46DF-B887-494AC5352818}.Release|Any CPU.Build.0 = Release|Any CPU + {C57D22E0-075B-4F27-87F1-E6CF5F203975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C57D22E0-075B-4F27-87F1-E6CF5F203975}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C57D22E0-075B-4F27-87F1-E6CF5F203975}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C57D22E0-075B-4F27-87F1-E6CF5F203975}.Release|Any CPU.Build.0 = Release|Any CPU + {B9B66159-4FC7-4F94-8C21-ADC8CCED84F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B9B66159-4FC7-4F94-8C21-ADC8CCED84F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9B66159-4FC7-4F94-8C21-ADC8CCED84F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B9B66159-4FC7-4F94-8C21-ADC8CCED84F4}.Release|Any CPU.Build.0 = Release|Any CPU + {24FFA5F1-37CD-408B-80A9-006B5A6E11E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24FFA5F1-37CD-408B-80A9-006B5A6E11E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24FFA5F1-37CD-408B-80A9-006B5A6E11E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24FFA5F1-37CD-408B-80A9-006B5A6E11E8}.Release|Any CPU.Build.0 = Release|Any CPU + {6FE28297-B958-4BC5-A320-A531874D7F58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6FE28297-B958-4BC5-A320-A531874D7F58}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6FE28297-B958-4BC5-A320-A531874D7F58}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6FE28297-B958-4BC5-A320-A531874D7F58}.Release|Any CPU.Build.0 = Release|Any CPU + {838A27C6-F125-4C61-923E-7C855B598FBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {838A27C6-F125-4C61-923E-7C855B598FBD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {838A27C6-F125-4C61-923E-7C855B598FBD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {838A27C6-F125-4C61-923E-7C855B598FBD}.Release|Any CPU.Build.0 = Release|Any CPU + {FDE43241-3C82-4005-87B3-FD1349FEF4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDE43241-3C82-4005-87B3-FD1349FEF4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDE43241-3C82-4005-87B3-FD1349FEF4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDE43241-3C82-4005-87B3-FD1349FEF4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {878A2744-5122-48A7-90E6-7D8CFABC29FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {878A2744-5122-48A7-90E6-7D8CFABC29FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {878A2744-5122-48A7-90E6-7D8CFABC29FD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {878A2744-5122-48A7-90E6-7D8CFABC29FD}.Release|Any CPU.Build.0 = Release|Any CPU + {0C464725-DEDC-4894-84A3-7A155E43818B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0C464725-DEDC-4894-84A3-7A155E43818B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0C464725-DEDC-4894-84A3-7A155E43818B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0C464725-DEDC-4894-84A3-7A155E43818B}.Release|Any CPU.Build.0 = Release|Any CPU + {AC831843-CDE2-4B67-8BC5-8F2A0D09B273}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC831843-CDE2-4B67-8BC5-8F2A0D09B273}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC831843-CDE2-4B67-8BC5-8F2A0D09B273}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AC831843-CDE2-4B67-8BC5-8F2A0D09B273}.Release|Any CPU.Build.0 = Release|Any CPU + {2BE691A3-19C0-4FCF-85D2-D2371C9462C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2BE691A3-19C0-4FCF-85D2-D2371C9462C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2BE691A3-19C0-4FCF-85D2-D2371C9462C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2BE691A3-19C0-4FCF-85D2-D2371C9462C6}.Release|Any CPU.Build.0 = Release|Any CPU + {B140F739-EC51-4265-B4B8-59467606E6EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B140F739-EC51-4265-B4B8-59467606E6EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B140F739-EC51-4265-B4B8-59467606E6EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B140F739-EC51-4265-B4B8-59467606E6EE}.Release|Any CPU.Build.0 = Release|Any CPU + {00E8EE51-A263-4971-9431-C4A2DEA4993A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {00E8EE51-A263-4971-9431-C4A2DEA4993A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {00E8EE51-A263-4971-9431-C4A2DEA4993A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {00E8EE51-A263-4971-9431-C4A2DEA4993A}.Release|Any CPU.Build.0 = Release|Any CPU + {6B2BA282-5C97-4A71-885F-E5259274F714}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B2BA282-5C97-4A71-885F-E5259274F714}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B2BA282-5C97-4A71-885F-E5259274F714}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B2BA282-5C97-4A71-885F-E5259274F714}.Release|Any CPU.Build.0 = Release|Any CPU + {05CF624D-F42E-461A-A21D-41BEE0CCEB5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {05CF624D-F42E-461A-A21D-41BEE0CCEB5C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {05CF624D-F42E-461A-A21D-41BEE0CCEB5C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {05CF624D-F42E-461A-A21D-41BEE0CCEB5C}.Release|Any CPU.Build.0 = Release|Any CPU + {C4A782C1-AA25-476B-B936-90433422D0A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C4A782C1-AA25-476B-B936-90433422D0A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C4A782C1-AA25-476B-B936-90433422D0A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C4A782C1-AA25-476B-B936-90433422D0A2}.Release|Any CPU.Build.0 = Release|Any CPU + {D9E411DD-58B1-4160-8025-068D43ADE72C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D9E411DD-58B1-4160-8025-068D43ADE72C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D9E411DD-58B1-4160-8025-068D43ADE72C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D9E411DD-58B1-4160-8025-068D43ADE72C}.Release|Any CPU.Build.0 = Release|Any CPU + {B2AADB36-8433-4EDB-B247-49A3EA82FE06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B2AADB36-8433-4EDB-B247-49A3EA82FE06}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B2AADB36-8433-4EDB-B247-49A3EA82FE06}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B2AADB36-8433-4EDB-B247-49A3EA82FE06}.Release|Any CPU.Build.0 = Release|Any CPU + {40F2A962-6209-42D0-9F21-CEACB411007B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {40F2A962-6209-42D0-9F21-CEACB411007B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {40F2A962-6209-42D0-9F21-CEACB411007B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {40F2A962-6209-42D0-9F21-CEACB411007B}.Release|Any CPU.Build.0 = Release|Any CPU + {4170A699-C110-4136-92EA-D2BFEBF68230}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4170A699-C110-4136-92EA-D2BFEBF68230}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4170A699-C110-4136-92EA-D2BFEBF68230}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4170A699-C110-4136-92EA-D2BFEBF68230}.Release|Any CPU.Build.0 = Release|Any CPU + {78B9EC26-ADC5-4934-9F31-000D4D678121}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {78B9EC26-ADC5-4934-9F31-000D4D678121}.Debug|Any CPU.Build.0 = Debug|Any CPU + {78B9EC26-ADC5-4934-9F31-000D4D678121}.Release|Any CPU.ActiveCfg = Release|Any CPU + {78B9EC26-ADC5-4934-9F31-000D4D678121}.Release|Any CPU.Build.0 = Release|Any CPU + {027B0175-2D22-4168-A0B4-1E0DC5BE2A61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {027B0175-2D22-4168-A0B4-1E0DC5BE2A61}.Debug|Any CPU.Build.0 = Debug|Any CPU + {027B0175-2D22-4168-A0B4-1E0DC5BE2A61}.Release|Any CPU.ActiveCfg = Release|Any CPU + {027B0175-2D22-4168-A0B4-1E0DC5BE2A61}.Release|Any CPU.Build.0 = Release|Any CPU + {EA4C219B-D00A-4BDB-A787-A79FA9611303}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA4C219B-D00A-4BDB-A787-A79FA9611303}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA4C219B-D00A-4BDB-A787-A79FA9611303}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA4C219B-D00A-4BDB-A787-A79FA9611303}.Release|Any CPU.Build.0 = Release|Any CPU + {B40CD480-D4A0-4A43-B855-0CD84EC537CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B40CD480-D4A0-4A43-B855-0CD84EC537CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B40CD480-D4A0-4A43-B855-0CD84EC537CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B40CD480-D4A0-4A43-B855-0CD84EC537CC}.Release|Any CPU.Build.0 = Release|Any CPU + {C3C7C037-B5B6-4CE0-BC02-820667D74F60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C3C7C037-B5B6-4CE0-BC02-820667D74F60}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3C7C037-B5B6-4CE0-BC02-820667D74F60}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C3C7C037-B5B6-4CE0-BC02-820667D74F60}.Release|Any CPU.Build.0 = Release|Any CPU + {48F77D8F-60AE-4173-BA74-FC2E0B3578BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {48F77D8F-60AE-4173-BA74-FC2E0B3578BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48F77D8F-60AE-4173-BA74-FC2E0B3578BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {48F77D8F-60AE-4173-BA74-FC2E0B3578BB}.Release|Any CPU.Build.0 = Release|Any CPU + {4E12B03E-D607-492E-BB28-96235993A39F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4E12B03E-D607-492E-BB28-96235993A39F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4E12B03E-D607-492E-BB28-96235993A39F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4E12B03E-D607-492E-BB28-96235993A39F}.Release|Any CPU.Build.0 = Release|Any CPU + {D2F38ACB-7158-4DE0-BDEA-B13E3D52A655}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D2F38ACB-7158-4DE0-BDEA-B13E3D52A655}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D2F38ACB-7158-4DE0-BDEA-B13E3D52A655}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D2F38ACB-7158-4DE0-BDEA-B13E3D52A655}.Release|Any CPU.Build.0 = Release|Any CPU + {1272E006-8D8B-46B5-AD18-2E31CA9C6CBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1272E006-8D8B-46B5-AD18-2E31CA9C6CBD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1272E006-8D8B-46B5-AD18-2E31CA9C6CBD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1272E006-8D8B-46B5-AD18-2E31CA9C6CBD}.Release|Any CPU.Build.0 = Release|Any CPU + {E0D77981-E412-477D-B866-E43F055CAAFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E0D77981-E412-477D-B866-E43F055CAAFC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E0D77981-E412-477D-B866-E43F055CAAFC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E0D77981-E412-477D-B866-E43F055CAAFC}.Release|Any CPU.Build.0 = Release|Any CPU + {66B96C01-6A08-4C7A-B5F5-A0489D8BBB47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66B96C01-6A08-4C7A-B5F5-A0489D8BBB47}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66B96C01-6A08-4C7A-B5F5-A0489D8BBB47}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66B96C01-6A08-4C7A-B5F5-A0489D8BBB47}.Release|Any CPU.Build.0 = Release|Any CPU + {8BC69296-3C9A-45B3-B6C3-3DD575027CCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8BC69296-3C9A-45B3-B6C3-3DD575027CCD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8BC69296-3C9A-45B3-B6C3-3DD575027CCD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8BC69296-3C9A-45B3-B6C3-3DD575027CCD}.Release|Any CPU.Build.0 = Release|Any CPU + {B25D3B7A-AEAA-4EFB-9839-7C6E3F7C24BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B25D3B7A-AEAA-4EFB-9839-7C6E3F7C24BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B25D3B7A-AEAA-4EFB-9839-7C6E3F7C24BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B25D3B7A-AEAA-4EFB-9839-7C6E3F7C24BB}.Release|Any CPU.Build.0 = Release|Any CPU + {22305349-51C7-4D80-A538-C7C0F4495895}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {22305349-51C7-4D80-A538-C7C0F4495895}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22305349-51C7-4D80-A538-C7C0F4495895}.Release|Any CPU.ActiveCfg = Release|Any CPU + {22305349-51C7-4D80-A538-C7C0F4495895}.Release|Any CPU.Build.0 = Release|Any CPU + {5921A7FA-8C70-49A9-82E1-BE3FA5B50C33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5921A7FA-8C70-49A9-82E1-BE3FA5B50C33}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5921A7FA-8C70-49A9-82E1-BE3FA5B50C33}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5921A7FA-8C70-49A9-82E1-BE3FA5B50C33}.Release|Any CPU.Build.0 = Release|Any CPU + {599BAD8E-DB56-4967-8DDE-74BD21DA2B49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {599BAD8E-DB56-4967-8DDE-74BD21DA2B49}.Debug|Any CPU.Build.0 = Debug|Any CPU + {599BAD8E-DB56-4967-8DDE-74BD21DA2B49}.Release|Any CPU.ActiveCfg = Release|Any CPU + {599BAD8E-DB56-4967-8DDE-74BD21DA2B49}.Release|Any CPU.Build.0 = Release|Any CPU + {75A31AAB-B7E6-4F13-9B7A-B74216B2EDDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75A31AAB-B7E6-4F13-9B7A-B74216B2EDDE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75A31AAB-B7E6-4F13-9B7A-B74216B2EDDE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75A31AAB-B7E6-4F13-9B7A-B74216B2EDDE}.Release|Any CPU.Build.0 = Release|Any CPU + {36990D32-6F41-44A6-BB88-A3F15E12C35B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {36990D32-6F41-44A6-BB88-A3F15E12C35B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36990D32-6F41-44A6-BB88-A3F15E12C35B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {36990D32-6F41-44A6-BB88-A3F15E12C35B}.Release|Any CPU.Build.0 = Release|Any CPU + {21831A4F-B3C9-40E7-855B-75F9D679FF55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {21831A4F-B3C9-40E7-855B-75F9D679FF55}.Debug|Any CPU.Build.0 = Debug|Any CPU + {21831A4F-B3C9-40E7-855B-75F9D679FF55}.Release|Any CPU.ActiveCfg = Release|Any CPU + {21831A4F-B3C9-40E7-855B-75F9D679FF55}.Release|Any CPU.Build.0 = Release|Any CPU + {5996F536-14B9-4F95-A18B-716E5F7EE478}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5996F536-14B9-4F95-A18B-716E5F7EE478}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5996F536-14B9-4F95-A18B-716E5F7EE478}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5996F536-14B9-4F95-A18B-716E5F7EE478}.Release|Any CPU.Build.0 = Release|Any CPU + {6FC29D15-7581-4E29-9897-76DC5AEF2042}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6FC29D15-7581-4E29-9897-76DC5AEF2042}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6FC29D15-7581-4E29-9897-76DC5AEF2042}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6FC29D15-7581-4E29-9897-76DC5AEF2042}.Release|Any CPU.Build.0 = Release|Any CPU + {3E81F8D0-06C7-4FD4-A5B0-E3D96E909646}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E81F8D0-06C7-4FD4-A5B0-E3D96E909646}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E81F8D0-06C7-4FD4-A5B0-E3D96E909646}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E81F8D0-06C7-4FD4-A5B0-E3D96E909646}.Release|Any CPU.Build.0 = Release|Any CPU + {C233FF83-E15D-4A17-8FF1-BBFF9E9DE210}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C233FF83-E15D-4A17-8FF1-BBFF9E9DE210}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C233FF83-E15D-4A17-8FF1-BBFF9E9DE210}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C233FF83-E15D-4A17-8FF1-BBFF9E9DE210}.Release|Any CPU.Build.0 = Release|Any CPU + {AB2D6B9A-0EC8-402B-849A-95E826B09AB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AB2D6B9A-0EC8-402B-849A-95E826B09AB3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB2D6B9A-0EC8-402B-849A-95E826B09AB3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AB2D6B9A-0EC8-402B-849A-95E826B09AB3}.Release|Any CPU.Build.0 = Release|Any CPU + {87B6EF51-9F1B-4A81-9764-F359138B774D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {87B6EF51-9F1B-4A81-9764-F359138B774D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {87B6EF51-9F1B-4A81-9764-F359138B774D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {87B6EF51-9F1B-4A81-9764-F359138B774D}.Release|Any CPU.Build.0 = Release|Any CPU + {4FFD1073-9358-418D-BB4D-DB63FE0A11DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4FFD1073-9358-418D-BB4D-DB63FE0A11DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4FFD1073-9358-418D-BB4D-DB63FE0A11DF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4FFD1073-9358-418D-BB4D-DB63FE0A11DF}.Release|Any CPU.Build.0 = Release|Any CPU + {5BFF8BA0-EA27-4493-88A5-5BC57286D536}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5BFF8BA0-EA27-4493-88A5-5BC57286D536}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5BFF8BA0-EA27-4493-88A5-5BC57286D536}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5BFF8BA0-EA27-4493-88A5-5BC57286D536}.Release|Any CPU.Build.0 = Release|Any CPU + {FB11256C-06C0-4B24-863A-C87D78F8EE2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB11256C-06C0-4B24-863A-C87D78F8EE2A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB11256C-06C0-4B24-863A-C87D78F8EE2A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB11256C-06C0-4B24-863A-C87D78F8EE2A}.Release|Any CPU.Build.0 = Release|Any CPU + {6233BF36-9636-43BD-9462-2D31A05EE4ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6233BF36-9636-43BD-9462-2D31A05EE4ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6233BF36-9636-43BD-9462-2D31A05EE4ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6233BF36-9636-43BD-9462-2D31A05EE4ED}.Release|Any CPU.Build.0 = Release|Any CPU + {F8417623-6936-43C4-A967-9D6BD002AD16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8417623-6936-43C4-A967-9D6BD002AD16}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8417623-6936-43C4-A967-9D6BD002AD16}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8417623-6936-43C4-A967-9D6BD002AD16}.Release|Any CPU.Build.0 = Release|Any CPU + {CAA32331-4A8E-4C16-BC7D-7CA646B393F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CAA32331-4A8E-4C16-BC7D-7CA646B393F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CAA32331-4A8E-4C16-BC7D-7CA646B393F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CAA32331-4A8E-4C16-BC7D-7CA646B393F4}.Release|Any CPU.Build.0 = Release|Any CPU + {08B1A026-4F8C-4336-B5B8-3189023DFF2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {08B1A026-4F8C-4336-B5B8-3189023DFF2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {08B1A026-4F8C-4336-B5B8-3189023DFF2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {08B1A026-4F8C-4336-B5B8-3189023DFF2B}.Release|Any CPU.Build.0 = Release|Any CPU + {0BCB6FF1-E374-4AED-9888-83C7CFED0E98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BCB6FF1-E374-4AED-9888-83C7CFED0E98}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BCB6FF1-E374-4AED-9888-83C7CFED0E98}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BCB6FF1-E374-4AED-9888-83C7CFED0E98}.Release|Any CPU.Build.0 = Release|Any CPU + {DF1E55A5-7EA8-41B2-BEBA-72544D256069}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF1E55A5-7EA8-41B2-BEBA-72544D256069}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF1E55A5-7EA8-41B2-BEBA-72544D256069}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DF1E55A5-7EA8-41B2-BEBA-72544D256069}.Release|Any CPU.Build.0 = Release|Any CPU + {9C58E50F-0589-4A4E-B94D-2EC0BF02F61F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9C58E50F-0589-4A4E-B94D-2EC0BF02F61F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C58E50F-0589-4A4E-B94D-2EC0BF02F61F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9C58E50F-0589-4A4E-B94D-2EC0BF02F61F}.Release|Any CPU.Build.0 = Release|Any CPU + {AE19ED93-AFC4-4F91-93BD-B00D343D3BA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE19ED93-AFC4-4F91-93BD-B00D343D3BA0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE19ED93-AFC4-4F91-93BD-B00D343D3BA0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE19ED93-AFC4-4F91-93BD-B00D343D3BA0}.Release|Any CPU.Build.0 = Release|Any CPU + {5D74BD1D-3142-4478-9062-4543FADA3E91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5D74BD1D-3142-4478-9062-4543FADA3E91}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D74BD1D-3142-4478-9062-4543FADA3E91}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5D74BD1D-3142-4478-9062-4543FADA3E91}.Release|Any CPU.Build.0 = Release|Any CPU + {B737883C-9450-46CB-B81C-1A3405071D04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B737883C-9450-46CB-B81C-1A3405071D04}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B737883C-9450-46CB-B81C-1A3405071D04}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B737883C-9450-46CB-B81C-1A3405071D04}.Release|Any CPU.Build.0 = Release|Any CPU + {156447ED-8515-4FB0-8B5A-D5A3A1E43AC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {156447ED-8515-4FB0-8B5A-D5A3A1E43AC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {156447ED-8515-4FB0-8B5A-D5A3A1E43AC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {156447ED-8515-4FB0-8B5A-D5A3A1E43AC6}.Release|Any CPU.Build.0 = Release|Any CPU + {DE51B662-C843-4E99-87F8-F367504D5640}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DE51B662-C843-4E99-87F8-F367504D5640}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DE51B662-C843-4E99-87F8-F367504D5640}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DE51B662-C843-4E99-87F8-F367504D5640}.Release|Any CPU.Build.0 = Release|Any CPU + {0C062400-58D1-4EE3-8EBB-C5B3AAEECF87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0C062400-58D1-4EE3-8EBB-C5B3AAEECF87}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0C062400-58D1-4EE3-8EBB-C5B3AAEECF87}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0C062400-58D1-4EE3-8EBB-C5B3AAEECF87}.Release|Any CPU.Build.0 = Release|Any CPU + {FA6AF0F1-B4EE-4E46-B933-9573861A10FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FA6AF0F1-B4EE-4E46-B933-9573861A10FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FA6AF0F1-B4EE-4E46-B933-9573861A10FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FA6AF0F1-B4EE-4E46-B933-9573861A10FC}.Release|Any CPU.Build.0 = Release|Any CPU + {4FECE655-7B06-4DED-BED0-B67731A43E0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4FECE655-7B06-4DED-BED0-B67731A43E0B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4FECE655-7B06-4DED-BED0-B67731A43E0B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4FECE655-7B06-4DED-BED0-B67731A43E0B}.Release|Any CPU.Build.0 = Release|Any CPU + {6A84E4B0-3A9C-4B40-9B64-AD3A26324DAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6A84E4B0-3A9C-4B40-9B64-AD3A26324DAC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A84E4B0-3A9C-4B40-9B64-AD3A26324DAC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6A84E4B0-3A9C-4B40-9B64-AD3A26324DAC}.Release|Any CPU.Build.0 = Release|Any CPU + {9B941AF9-34A0-4DFF-80F8-0418E212659A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9B941AF9-34A0-4DFF-80F8-0418E212659A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B941AF9-34A0-4DFF-80F8-0418E212659A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9B941AF9-34A0-4DFF-80F8-0418E212659A}.Release|Any CPU.Build.0 = Release|Any CPU + {A44B570E-0676-4003-A827-E910A9332DEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A44B570E-0676-4003-A827-E910A9332DEA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A44B570E-0676-4003-A827-E910A9332DEA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A44B570E-0676-4003-A827-E910A9332DEA}.Release|Any CPU.Build.0 = Release|Any CPU + {00C518D5-EFAC-43CC-AD1E-9837BDBD93BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {00C518D5-EFAC-43CC-AD1E-9837BDBD93BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {00C518D5-EFAC-43CC-AD1E-9837BDBD93BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {00C518D5-EFAC-43CC-AD1E-9837BDBD93BB}.Release|Any CPU.Build.0 = Release|Any CPU + {698AE3A0-65E3-433C-B56C-7D47A7A44263}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {698AE3A0-65E3-433C-B56C-7D47A7A44263}.Debug|Any CPU.Build.0 = Debug|Any CPU + {698AE3A0-65E3-433C-B56C-7D47A7A44263}.Release|Any CPU.ActiveCfg = Release|Any CPU + {698AE3A0-65E3-433C-B56C-7D47A7A44263}.Release|Any CPU.Build.0 = Release|Any CPU + {BA78E270-92A7-46C2-932B-3109E9DC5824}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BA78E270-92A7-46C2-932B-3109E9DC5824}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BA78E270-92A7-46C2-932B-3109E9DC5824}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BA78E270-92A7-46C2-932B-3109E9DC5824}.Release|Any CPU.Build.0 = Release|Any CPU + {EDB3405A-58BD-444C-82FF-05FAB7CE72B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EDB3405A-58BD-444C-82FF-05FAB7CE72B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EDB3405A-58BD-444C-82FF-05FAB7CE72B4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EDB3405A-58BD-444C-82FF-05FAB7CE72B4}.Release|Any CPU.Build.0 = Release|Any CPU + {A23BA1A3-8EC7-4937-B0AA-69CCEE40453C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A23BA1A3-8EC7-4937-B0AA-69CCEE40453C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A23BA1A3-8EC7-4937-B0AA-69CCEE40453C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A23BA1A3-8EC7-4937-B0AA-69CCEE40453C}.Release|Any CPU.Build.0 = Release|Any CPU + {4938D59D-CF50-4090-B936-36DB29EBE344}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4938D59D-CF50-4090-B936-36DB29EBE344}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4938D59D-CF50-4090-B936-36DB29EBE344}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4938D59D-CF50-4090-B936-36DB29EBE344}.Release|Any CPU.Build.0 = Release|Any CPU + {58C01936-5D8F-4077-8663-EE0E35776D65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {58C01936-5D8F-4077-8663-EE0E35776D65}.Debug|Any CPU.Build.0 = Debug|Any CPU + {58C01936-5D8F-4077-8663-EE0E35776D65}.Release|Any CPU.ActiveCfg = Release|Any CPU + {58C01936-5D8F-4077-8663-EE0E35776D65}.Release|Any CPU.Build.0 = Release|Any CPU + {814E42E9-508E-487F-BCD3-0F07AE7D1492}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {814E42E9-508E-487F-BCD3-0F07AE7D1492}.Debug|Any CPU.Build.0 = Debug|Any CPU + {814E42E9-508E-487F-BCD3-0F07AE7D1492}.Release|Any CPU.ActiveCfg = Release|Any CPU + {814E42E9-508E-487F-BCD3-0F07AE7D1492}.Release|Any CPU.Build.0 = Release|Any CPU + {3F5120A6-9CA4-4B25-9507-86E9E5571DB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3F5120A6-9CA4-4B25-9507-86E9E5571DB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3F5120A6-9CA4-4B25-9507-86E9E5571DB6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3F5120A6-9CA4-4B25-9507-86E9E5571DB6}.Release|Any CPU.Build.0 = Release|Any CPU + {79A40AC0-A182-49D0-AED9-DF8B6835432A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {79A40AC0-A182-49D0-AED9-DF8B6835432A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {79A40AC0-A182-49D0-AED9-DF8B6835432A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {79A40AC0-A182-49D0-AED9-DF8B6835432A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {345739E2-E20C-4B1F-B1B2-725006FC9F72} = {C01B9930-D67F-41C5-90C9-C87DC53F39CB} + {12539757-7371-455D-B86B-DAF22F2DEE4E} = {0D42DE24-8D3C-4BE1-BAAE-7543281C3113} + {5F1A7144-C954-4B47-B1FF-820FEA9B6B61} = {345739E2-E20C-4B1F-B1B2-725006FC9F72} + {1E50F81A-FBFF-4873-9731-FA8835A51080} = {9BFB5DBE-8F64-45A9-BDB2-BAD91D44A4C5} + {354B3720-D050-487D-A46A-22BCFEE56D43} = {C01B9930-D67F-41C5-90C9-C87DC53F39CB} + {5F7ACBE4-A1D8-4680-AA86-B87F8494BAC2} = {354B3720-D050-487D-A46A-22BCFEE56D43} + {BA79977F-6DCE-4787-9A0D-AF5E61EBC177} = {354B3720-D050-487D-A46A-22BCFEE56D43} + {2B56E8AA-4DAB-4A70-9439-ED16470C0BFE} = {DE39BB20-AE82-47C3-9141-8CFD4C673217} + {9BFB5DBE-8F64-45A9-BDB2-BAD91D44A4C5} = {E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF} + {DE39BB20-AE82-47C3-9141-8CFD4C673217} = {E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF} + {766CAB38-F064-419F-837B-D6DA8B61AF66} = {DE39BB20-AE82-47C3-9141-8CFD4C673217} + {E8481A4C-0C24-4418-A2C1-742877BE4F0D} = {354B3720-D050-487D-A46A-22BCFEE56D43} + {21064598-1418-422E-BA37-8EC09541DFC1} = {9BFB5DBE-8F64-45A9-BDB2-BAD91D44A4C5} + {08EF3E38-E0C3-4E18-984C-3216CB6E8897} = {345739E2-E20C-4B1F-B1B2-725006FC9F72} + {12C94395-09F1-42E4-8CE1-6E54D3FEBAA6} = {345739E2-E20C-4B1F-B1B2-725006FC9F72} + {955D94B8-747D-4CA4-832A-6377C9FF5B8F} = {9BFB5DBE-8F64-45A9-BDB2-BAD91D44A4C5} + {FB337B27-C6C6-43A1-BFA5-58B3CF381BA8} = {345739E2-E20C-4B1F-B1B2-725006FC9F72} + {B8825802-81F7-4671-82E8-3A6E3A21F570} = {9BFB5DBE-8F64-45A9-BDB2-BAD91D44A4C5} + {7AB71D86-D351-4009-A789-24097897E74A} = {C01B9930-D67F-41C5-90C9-C87DC53F39CB} + {1DE277C2-EE56-4F4D-BB89-71107DE0E4C6} = {345739E2-E20C-4B1F-B1B2-725006FC9F72} + {166852FC-2F86-4A5B-9029-E16958798DD6} = {9BFB5DBE-8F64-45A9-BDB2-BAD91D44A4C5} + {61982C8D-5376-45EE-BCC3-D4F9E90AF3B0} = {7AB71D86-D351-4009-A789-24097897E74A} + {FC5E00D1-ABA5-4168-8B8F-FA2A26D84068} = {E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF} + {7CA38535-297B-4DA9-AFF5-3199268EEAF6} = {FC5E00D1-ABA5-4168-8B8F-FA2A26D84068} + {EE426441-DE33-4F3B-B66A-E258AC725B78} = {59F82491-51E3-4A12-B427-9815AEA23D10} + {66A85767-2877-4B99-84F1-EEABC6DC5247} = {C01B9930-D67F-41C5-90C9-C87DC53F39CB} + {CC492CA2-5D86-4DB8-AB15-B118DF27C344} = {E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF} + {4D40321F-2C3F-4ACB-8AE0-7A228DF0551B} = {2864196E-3044-4FEC-B92A-D1D3C521C567} + {7F7D9EE0-7263-4621-BA65-FC470B0F3C6B} = {4E99CBC5-1C74-44E7-9DEF-3FA44CADCCF0} + {45E2ADF5-F386-44CD-A43D-A3AB47E9C39A} = {66A85767-2877-4B99-84F1-EEABC6DC5247} + {4E99CBC5-1C74-44E7-9DEF-3FA44CADCCF0} = {66A85767-2877-4B99-84F1-EEABC6DC5247} + {04E5EDDB-8440-4BAD-9531-F87A3AC826FB} = {4E99CBC5-1C74-44E7-9DEF-3FA44CADCCF0} + {4656C609-DBBA-4F6B-B16F-5E1A8C9D9858} = {2697EEFB-06BE-4786-89C6-A566FD94A41B} + {F7925EED-65E6-4CA2-9B8D-B7036F6DB3E8} = {2697EEFB-06BE-4786-89C6-A566FD94A41B} + {D22C3F6D-434A-4F09-9EB2-B0433CC43642} = {2697EEFB-06BE-4786-89C6-A566FD94A41B} + {257A0A17-5D13-4DA4-8E91-8A3BB2711F49} = {45E2ADF5-F386-44CD-A43D-A3AB47E9C39A} + {4A955BB3-C1AF-45DB-9590-82C00996B608} = {66A85767-2877-4B99-84F1-EEABC6DC5247} + {9A2C7C42-086B-4482-828D-7B02D85372ED} = {4A955BB3-C1AF-45DB-9590-82C00996B608} + {661B88AE-6F83-437C-9A7E-C5C1A565E650} = {45E2ADF5-F386-44CD-A43D-A3AB47E9C39A} + {C99E1554-5039-42BD-B1FD-93814FBEBE44} = {2864196E-3044-4FEC-B92A-D1D3C521C567} + {60424AB5-356A-4623-B1FB-7560334C0BE0} = {4A955BB3-C1AF-45DB-9590-82C00996B608} + {C2A4E6AB-16B9-4457-9445-FFBF672DFBCF} = {4A955BB3-C1AF-45DB-9590-82C00996B608} + {AFB629C5-00C2-492C-8787-C66D810A24DB} = {7AB71D86-D351-4009-A789-24097897E74A} + {648CE3E3-CEC4-4F91-8696-6A3FB2F91220} = {C01B9930-D67F-41C5-90C9-C87DC53F39CB} + {9404B5FE-3431-4EC0-BCC6-42525FC5D1B8} = {648CE3E3-CEC4-4F91-8696-6A3FB2F91220} + {BB24822F-F104-4D8E-9CB5-0A94DEF04BAB} = {E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF} + {EF7BDA1B-22E7-4F3D-97E0-16C9C10AA1B0} = {BB24822F-F104-4D8E-9CB5-0A94DEF04BAB} + {B5305F08-3856-42B5-92D5-6FD446B39931} = {45E2ADF5-F386-44CD-A43D-A3AB47E9C39A} + {88FE7642-27D9-4693-A130-706FCE46194A} = {345739E2-E20C-4B1F-B1B2-725006FC9F72} + {CA43B145-9DF2-49D3-9371-B88BFB0E846A} = {BB24822F-F104-4D8E-9CB5-0A94DEF04BAB} + {95842BB6-559E-4317-B1E9-FF19D39ADA24} = {648CE3E3-CEC4-4F91-8696-6A3FB2F91220} + {3DE7DD7A-8500-46C9-91A1-A65DB60E0235} = {648CE3E3-CEC4-4F91-8696-6A3FB2F91220} + {8618EE3F-F120-4AEA-8A77-F7BD5DCCB888} = {4E99CBC5-1C74-44E7-9DEF-3FA44CADCCF0} + {D5CF25B2-1A91-4065-A647-06CDF55F4B96} = {C01B9930-D67F-41C5-90C9-C87DC53F39CB} + {2697EEFB-06BE-4786-89C6-A566FD94A41B} = {C01B9930-D67F-41C5-90C9-C87DC53F39CB} + {A6EB05D5-EF38-45B6-960A-0EB759373FDF} = {E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF} + {445850B7-1A9D-4E9D-8AC1-765F3792DC19} = {E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF} + {59F82491-51E3-4A12-B427-9815AEA23D10} = {C01B9930-D67F-41C5-90C9-C87DC53F39CB} + {3ABD7B4B-37F9-4D49-92D9-384190177286} = {E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF} + {577A7F6F-4836-4811-B715-32573127BDB3} = {D5CF25B2-1A91-4065-A647-06CDF55F4B96} + {04EA7A93-CDC7-4C68-815D-699180D99DCE} = {A6EB05D5-EF38-45B6-960A-0EB759373FDF} + {A1186FB9-FB77-427C-9AEC-55E48A8D046A} = {D5CF25B2-1A91-4065-A647-06CDF55F4B96} + {9960FA8A-98C1-45FB-8A09-33CA183AA140} = {D5CF25B2-1A91-4065-A647-06CDF55F4B96} + {E3414144-C19E-40C0-A73A-2FD41AE514D1} = {E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF} + {1855CAB7-6379-47A4-A0A4-F890C6C46403} = {E3414144-C19E-40C0-A73A-2FD41AE514D1} + {C43539A5-8343-4381-B2FD-5323323F1DB6} = {4E99CBC5-1C74-44E7-9DEF-3FA44CADCCF0} + {D3C326CD-F1F1-4E06-962D-47F6C8090DEE} = {A6EB05D5-EF38-45B6-960A-0EB759373FDF} + {13690AB8-9AA4-4936-A25F-7AF1AE3D4ED0} = {A6EB05D5-EF38-45B6-960A-0EB759373FDF} + {244A930D-5B84-4CBC-A3A7-7E8DE1E603D6} = {7D087635-9E77-46B2-B647-962F7E2F8688} + {0EDF10D7-D6D6-48E2-AC0C-BA6D814CB633} = {7D087635-9E77-46B2-B647-962F7E2F8688} + {05D8A63F-B3E1-4A5E-AA66-337BCF8840C1} = {E3414144-C19E-40C0-A73A-2FD41AE514D1} + {3993A838-71C9-49C5-80B3-16CC48A5F5C9} = {E3414144-C19E-40C0-A73A-2FD41AE514D1} + {D785A240-1115-41C8-943E-5058161D6478} = {7D087635-9E77-46B2-B647-962F7E2F8688} + {B1952639-773B-4C82-9D95-7CB4ED8AC59E} = {E3414144-C19E-40C0-A73A-2FD41AE514D1} + {29792C72-565A-46DF-B887-494AC5352818} = {664F0DBE-CB31-4D63-858A-8C7E6411C76C} + {C57D22E0-075B-4F27-87F1-E6CF5F203975} = {BB24822F-F104-4D8E-9CB5-0A94DEF04BAB} + {B9B66159-4FC7-4F94-8C21-ADC8CCED84F4} = {BB24822F-F104-4D8E-9CB5-0A94DEF04BAB} + {24FFA5F1-37CD-408B-80A9-006B5A6E11E8} = {BB24822F-F104-4D8E-9CB5-0A94DEF04BAB} + {6FE28297-B958-4BC5-A320-A531874D7F58} = {BB24822F-F104-4D8E-9CB5-0A94DEF04BAB} + {838A27C6-F125-4C61-923E-7C855B598FBD} = {BB24822F-F104-4D8E-9CB5-0A94DEF04BAB} + {FDE43241-3C82-4005-87B3-FD1349FEF4B6} = {664F0DBE-CB31-4D63-858A-8C7E6411C76C} + {878A2744-5122-48A7-90E6-7D8CFABC29FD} = {4A955BB3-C1AF-45DB-9590-82C00996B608} + {0C464725-DEDC-4894-84A3-7A155E43818B} = {4A955BB3-C1AF-45DB-9590-82C00996B608} + {AC831843-CDE2-4B67-8BC5-8F2A0D09B273} = {664F0DBE-CB31-4D63-858A-8C7E6411C76C} + {2BE691A3-19C0-4FCF-85D2-D2371C9462C6} = {4E99CBC5-1C74-44E7-9DEF-3FA44CADCCF0} + {B140F739-EC51-4265-B4B8-59467606E6EE} = {7D087635-9E77-46B2-B647-962F7E2F8688} + {00E8EE51-A263-4971-9431-C4A2DEA4993A} = {E3414144-C19E-40C0-A73A-2FD41AE514D1} + {6B2BA282-5C97-4A71-885F-E5259274F714} = {4E99CBC5-1C74-44E7-9DEF-3FA44CADCCF0} + {05CF624D-F42E-461A-A21D-41BEE0CCEB5C} = {E3414144-C19E-40C0-A73A-2FD41AE514D1} + {C4A782C1-AA25-476B-B936-90433422D0A2} = {7D087635-9E77-46B2-B647-962F7E2F8688} + {D9E411DD-58B1-4160-8025-068D43ADE72C} = {345739E2-E20C-4B1F-B1B2-725006FC9F72} + {B2AADB36-8433-4EDB-B247-49A3EA82FE06} = {54800E45-FF91-4091-A146-EFA5432A6443} + {40F2A962-6209-42D0-9F21-CEACB411007B} = {54800E45-FF91-4091-A146-EFA5432A6443} + {22E49D65-F51D-4A45-A35E-B27FF7DE6787} = {C01B9930-D67F-41C5-90C9-C87DC53F39CB} + {B85E8FC7-748E-4990-9279-1F06E095B6E6} = {E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF} + {4170A699-C110-4136-92EA-D2BFEBF68230} = {22E49D65-F51D-4A45-A35E-B27FF7DE6787} + {78B9EC26-ADC5-4934-9F31-000D4D678121} = {22E49D65-F51D-4A45-A35E-B27FF7DE6787} + {027B0175-2D22-4168-A0B4-1E0DC5BE2A61} = {B85E8FC7-748E-4990-9279-1F06E095B6E6} + {EA4C219B-D00A-4BDB-A787-A79FA9611303} = {BB24822F-F104-4D8E-9CB5-0A94DEF04BAB} + {B40CD480-D4A0-4A43-B855-0CD84EC537CC} = {354B3720-D050-487D-A46A-22BCFEE56D43} + {C3C7C037-B5B6-4CE0-BC02-820667D74F60} = {DE39BB20-AE82-47C3-9141-8CFD4C673217} + {48F77D8F-60AE-4173-BA74-FC2E0B3578BB} = {9BFB5DBE-8F64-45A9-BDB2-BAD91D44A4C5} + {76AA6040-AA3A-43DD-BD4B-7F931271BFD6} = {C01B9930-D67F-41C5-90C9-C87DC53F39CB} + {6493D592-7930-4979-A07D-B7CAA751C7BE} = {E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF} + {4E12B03E-D607-492E-BB28-96235993A39F} = {76AA6040-AA3A-43DD-BD4B-7F931271BFD6} + {D2F38ACB-7158-4DE0-BDEA-B13E3D52A655} = {6493D592-7930-4979-A07D-B7CAA751C7BE} + {1272E006-8D8B-46B5-AD18-2E31CA9C6CBD} = {76AA6040-AA3A-43DD-BD4B-7F931271BFD6} + {E0D77981-E412-477D-B866-E43F055CAAFC} = {76AA6040-AA3A-43DD-BD4B-7F931271BFD6} + {66B96C01-6A08-4C7A-B5F5-A0489D8BBB47} = {6493D592-7930-4979-A07D-B7CAA751C7BE} + {8BC69296-3C9A-45B3-B6C3-3DD575027CCD} = {345739E2-E20C-4B1F-B1B2-725006FC9F72} + {B25D3B7A-AEAA-4EFB-9839-7C6E3F7C24BB} = {9BFB5DBE-8F64-45A9-BDB2-BAD91D44A4C5} + {1F5EE147-BD88-4B6D-BA84-35B4F3BE4A4E} = {C01B9930-D67F-41C5-90C9-C87DC53F39CB} + {22305349-51C7-4D80-A538-C7C0F4495895} = {1F5EE147-BD88-4B6D-BA84-35B4F3BE4A4E} + {54800E45-FF91-4091-A146-EFA5432A6443} = {E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF} + {664F0DBE-CB31-4D63-858A-8C7E6411C76C} = {CC492CA2-5D86-4DB8-AB15-B118DF27C344} + {7D087635-9E77-46B2-B647-962F7E2F8688} = {CC492CA2-5D86-4DB8-AB15-B118DF27C344} + {2864196E-3044-4FEC-B92A-D1D3C521C567} = {CC492CA2-5D86-4DB8-AB15-B118DF27C344} + {96DFADC5-FAE8-4860-A57A-93F0534B1B7A} = {E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF} + {5921A7FA-8C70-49A9-82E1-BE3FA5B50C33} = {96DFADC5-FAE8-4860-A57A-93F0534B1B7A} + {599BAD8E-DB56-4967-8DDE-74BD21DA2B49} = {1F5EE147-BD88-4B6D-BA84-35B4F3BE4A4E} + {75A31AAB-B7E6-4F13-9B7A-B74216B2EDDE} = {96DFADC5-FAE8-4860-A57A-93F0534B1B7A} + {411247A4-4665-4BE6-8E5C-08F6D0BA2213} = {C01B9930-D67F-41C5-90C9-C87DC53F39CB} + {36990D32-6F41-44A6-BB88-A3F15E12C35B} = {411247A4-4665-4BE6-8E5C-08F6D0BA2213} + {21831A4F-B3C9-40E7-855B-75F9D679FF55} = {411247A4-4665-4BE6-8E5C-08F6D0BA2213} + {F7D4E7CB-468D-4570-8D82-D065F08BE40D} = {E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF} + {5996F536-14B9-4F95-A18B-716E5F7EE478} = {F7D4E7CB-468D-4570-8D82-D065F08BE40D} + {E069CEB0-8092-4907-BC9D-C6B8BDE20AD2} = {C01B9930-D67F-41C5-90C9-C87DC53F39CB} + {6FC29D15-7581-4E29-9897-76DC5AEF2042} = {E069CEB0-8092-4907-BC9D-C6B8BDE20AD2} + {3E81F8D0-06C7-4FD4-A5B0-E3D96E909646} = {E069CEB0-8092-4907-BC9D-C6B8BDE20AD2} + {C233FF83-E15D-4A17-8FF1-BBFF9E9DE210} = {E069CEB0-8092-4907-BC9D-C6B8BDE20AD2} + {16175228-03CA-414A-8786-E5BA844110C4} = {E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF} + {AB2D6B9A-0EC8-402B-849A-95E826B09AB3} = {16175228-03CA-414A-8786-E5BA844110C4} + {87B6EF51-9F1B-4A81-9764-F359138B774D} = {445850B7-1A9D-4E9D-8AC1-765F3792DC19} + {4FFD1073-9358-418D-BB4D-DB63FE0A11DF} = {445850B7-1A9D-4E9D-8AC1-765F3792DC19} + {5BFF8BA0-EA27-4493-88A5-5BC57286D536} = {F7D4E7CB-468D-4570-8D82-D065F08BE40D} + {FB11256C-06C0-4B24-863A-C87D78F8EE2A} = {3ABD7B4B-37F9-4D49-92D9-384190177286} + {547FD391-A56B-47A6-B20C-5FC04FA15C56} = {E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF} + {6233BF36-9636-43BD-9462-2D31A05EE4ED} = {59F82491-51E3-4A12-B427-9815AEA23D10} + {1DEC52C8-6692-4DA7-AE34-B76D728F895A} = {C01B9930-D67F-41C5-90C9-C87DC53F39CB} + {F8417623-6936-43C4-A967-9D6BD002AD16} = {1DEC52C8-6692-4DA7-AE34-B76D728F895A} + {CAA32331-4A8E-4C16-BC7D-7CA646B393F4} = {1DEC52C8-6692-4DA7-AE34-B76D728F895A} + {08B1A026-4F8C-4336-B5B8-3189023DFF2B} = {547FD391-A56B-47A6-B20C-5FC04FA15C56} + {0BCB6FF1-E374-4AED-9888-83C7CFED0E98} = {1DEC52C8-6692-4DA7-AE34-B76D728F895A} + {DF1E55A5-7EA8-41B2-BEBA-72544D256069} = {547FD391-A56B-47A6-B20C-5FC04FA15C56} + {9C58E50F-0589-4A4E-B94D-2EC0BF02F61F} = {1DEC52C8-6692-4DA7-AE34-B76D728F895A} + {AE19ED93-AFC4-4F91-93BD-B00D343D3BA0} = {547FD391-A56B-47A6-B20C-5FC04FA15C56} + {5D74BD1D-3142-4478-9062-4543FADA3E91} = {547FD391-A56B-47A6-B20C-5FC04FA15C56} + {B737883C-9450-46CB-B81C-1A3405071D04} = {547FD391-A56B-47A6-B20C-5FC04FA15C56} + {156447ED-8515-4FB0-8B5A-D5A3A1E43AC6} = {98F1FEAD-AFD5-4744-8A55-C57920461EB9} + {DE51B662-C843-4E99-87F8-F367504D5640} = {835A8155-411B-4DF2-AE58-D3FDEFB61E63} + {98F1FEAD-AFD5-4744-8A55-C57920461EB9} = {C01B9930-D67F-41C5-90C9-C87DC53F39CB} + {835A8155-411B-4DF2-AE58-D3FDEFB61E63} = {E6328DC1-C8F0-4DC6-941F-C6CAFFF783DF} + {0C062400-58D1-4EE3-8EBB-C5B3AAEECF87} = {98F1FEAD-AFD5-4744-8A55-C57920461EB9} + {FA6AF0F1-B4EE-4E46-B933-9573861A10FC} = {59F82491-51E3-4A12-B427-9815AEA23D10} + {4FECE655-7B06-4DED-BED0-B67731A43E0B} = {3ABD7B4B-37F9-4D49-92D9-384190177286} + {6A84E4B0-3A9C-4B40-9B64-AD3A26324DAC} = {835A8155-411B-4DF2-AE58-D3FDEFB61E63} + {9B941AF9-34A0-4DFF-80F8-0418E212659A} = {54800E45-FF91-4091-A146-EFA5432A6443} + {A44B570E-0676-4003-A827-E910A9332DEA} = {664F0DBE-CB31-4D63-858A-8C7E6411C76C} + {00C518D5-EFAC-43CC-AD1E-9837BDBD93BB} = {4A955BB3-C1AF-45DB-9590-82C00996B608} + {698AE3A0-65E3-433C-B56C-7D47A7A44263} = {345739E2-E20C-4B1F-B1B2-725006FC9F72} + {BA78E270-92A7-46C2-932B-3109E9DC5824} = {9BFB5DBE-8F64-45A9-BDB2-BAD91D44A4C5} + {EDB3405A-58BD-444C-82FF-05FAB7CE72B4} = {345739E2-E20C-4B1F-B1B2-725006FC9F72} + {A23BA1A3-8EC7-4937-B0AA-69CCEE40453C} = {16175228-03CA-414A-8786-E5BA844110C4} + {4938D59D-CF50-4090-B936-36DB29EBE344} = {E069CEB0-8092-4907-BC9D-C6B8BDE20AD2} + {58C01936-5D8F-4077-8663-EE0E35776D65} = {16175228-03CA-414A-8786-E5BA844110C4} + {814E42E9-508E-487F-BCD3-0F07AE7D1492} = {E069CEB0-8092-4907-BC9D-C6B8BDE20AD2} + {3F5120A6-9CA4-4B25-9507-86E9E5571DB6} = {1F5EE147-BD88-4B6D-BA84-35B4F3BE4A4E} + {79A40AC0-A182-49D0-AED9-DF8B6835432A} = {96DFADC5-FAE8-4860-A57A-93F0534B1B7A} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {94347832-A36D-4C42-9C4D-B848BD4F5DA9} + EndGlobalSection +EndGlobal diff --git a/build/Build.csproj b/build/Build.csproj new file mode 100644 index 000000000..154b7d157 --- /dev/null +++ b/build/Build.csproj @@ -0,0 +1,17 @@ + + + + $(NetTargetFramework) + + + + + + + + + + + + + diff --git a/build/BuildScript.cs b/build/BuildScript.cs new file mode 100644 index 000000000..b995f676f --- /dev/null +++ b/build/BuildScript.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using FlubuCore.Context; +using FlubuCore.Context.Attributes.BuildProperties; +using FlubuCore.Context.FluentInterface.Interfaces; +using FlubuCore.IO; +using FlubuCore.Scripting; + +namespace Build { + /// + /// 构建脚本 + /// + public class BuildScript : DefaultBuildScript { + /// + /// 解决方案文件名 + /// + [SolutionFileName] + public string SolutionFileName { get; set; } = "../Util.sln"; + /// + /// 构建配置 + /// + [FromArg( "c|configuration" )] + [BuildConfiguration] + public string BuildConfiguration { get; set; } = "Release"; + /// + /// Nuget推送地址 + /// + [FromArg( "nugetUrl" )] + public string NugetUrl { get; set; } = "https://api.nuget.org/v3/index.json"; + /// + /// Nuget密钥 + /// + [FromArg( "nugetKey", "Nuget api key for publishing nuget packages." )] + public string NugetApiKey { get; set; } + /// + /// 源代码目录 + /// + public FullPath SourceDir => RootDirectory.CombineWith( "../src" ); + /// + /// 测试目录 + /// + public FullPath TestDir => RootDirectory.CombineWith( "../test" ); + /// + /// 输出目录 + /// + public FullPath OutputDir => RootDirectory.CombineWith( "../output" ); + /// + /// 项目文件列表 + /// + public List Projects { get; set; } + /// + /// 单元测试项目文件列表 + /// + public List UnitTestProjecs { get; set; } + /// + /// 集成测试项目文件列表 + /// + public List IntegrationTestProjecs { get; set; } + /// + /// 忽略测试项目文件列表 + /// + public List IgnoreTestProjecs { get; set; } + /// + /// 忽略打包项目文件列表 + /// + public List IgnorePackProjecs { get; set; } + + /// + /// 获取集成测试项目文件列表 + /// + protected List GetIntegrationTestProjecs() { + return IntegrationTestProjecs.Where( t => IgnoreTestProjecs.Exists( p => p.FileName == t.FileName ) == false ).ToList(); + } + + /// + /// 获取打包项目文件列表 + /// + protected List GetPackProjecs() { + return Projects.Where( t => IgnorePackProjecs.Exists( p => p.FileName == t.FileName ) == false ).ToList(); + } + + /// + /// 构建前操作 + /// + /// 构建任务上下文 + protected override void BeforeBuildExecution( ITaskContext context ) { + Projects = context.GetFiles( SourceDir, "*/*.csproj" ); + UnitTestProjecs = context.GetFiles( TestDir, "*/*.Tests.csproj" ); + IntegrationTestProjecs = context.GetFiles( TestDir, "*/*.Tests.Integration.csproj" ); + IgnoreTestProjecs = new List(); + IgnorePackProjecs = new List(); + AddIgnoreTestProjecs( context ); + AddIgnorePackProjecs( context ); + } + + /// + /// 添加忽略测试项目文件列表 + /// + private void AddIgnoreTestProjecs( ITaskContext context ) { + IgnoreTestProjecs.AddRange( context.GetFiles( TestDir, "*/Util.Images.*.Tests.Integration.csproj" ) ); + IgnoreTestProjecs.AddRange( context.GetFiles( TestDir, "*/*.Oracle.Tests.Integration.csproj" ) ); + IgnoreTestProjecs.AddRange( context.GetFiles( TestDir, "*/Util.Data.Dapper.PostgreSql.Tests.Integration.csproj" ) ); + IgnoreTestProjecs.AddRange( context.GetFiles( TestDir, "*/*.Dapr.Tests.Integration.csproj" ) ); + } + + /// + /// 添加忽略打包项目文件列表 + /// + private void AddIgnorePackProjecs( ITaskContext context ) { + IgnorePackProjecs.AddRange( context.GetFiles( SourceDir, "*/*.WebApiClient.csproj" ) ); + } + + /// + /// 配置构建目标 + /// + /// 构建任务上下文 + protected override void ConfigureTargets( ITaskContext context ) { + var clean = Clean( context ); + var restore = Restore( context, clean ); + var build = Build( context, restore ); + var test = Test( context ); + var pack = Pack( context, clean ); + PublishNuGetPackage( context, pack ); + } + + /// + /// 清理解决方案 + /// + private ITarget Clean( ITaskContext context ) { + return context.CreateTarget( "clean" ) + .SetDescription( "Clean the solution." ) + .AddCoreTask( t => t.Clean().AddDirectoryToClean( OutputDir, false ) ); + } + + /// + /// 还原包 + /// + private ITarget Restore( ITaskContext context, params ITarget[] dependTargets ) { + return context.CreateTarget( "restore" ) + .SetDescription( "Restore the solution." ) + .DependsOn( dependTargets ) + .AddCoreTask( t => t.Restore() ); + } + + /// + /// 编译解决方案 + /// + private ITarget Build( ITaskContext context, params ITarget[] dependTargets ) { + return context.CreateTarget( "compile" ) + .SetDescription( "Compiles the solution." ) + .DependsOn( dependTargets ) + .AddCoreTask( t => t.Build() ); + } + + /// + /// 运行测试 + /// + private ITarget Test( ITaskContext context, params ITarget[] dependTargets ) { + var unitTest = UnitTest( context, dependTargets ); + var integrationTest = IntegrationTest( context, dependTargets ); + return context.CreateTarget( "test" ) + .SetDescription( "Run all tests." ) + .DependsOn( unitTest, integrationTest ); + } + + /// + /// 运行单元测试 + /// + private ITarget UnitTest( ITaskContext context, params ITarget[] dependTargets ) { + return context.CreateTarget( "unit.test" ) + .SetDescription( "Run unit tests." ) + .DependsOn( dependTargets ) + .ForEach( UnitTestProjecs, ( project, target ) => { + target.AddCoreTask( t => t.Test().Project( project ) ); + } ); + } + + /// + /// 运行集成测试 + /// + private ITarget IntegrationTest( ITaskContext context, params ITarget[] dependTargets ) { + return context.CreateTarget( "integration.test" ) + .SetDescription( "Run integration tests." ) + .DependsOn( dependTargets ) + .ForEach( GetIntegrationTestProjecs(), ( project, target ) => { + target.AddCoreTask( t => t.Test().Project( project ) ); + } ); + } + + /// + /// 创建nuget包 + /// + private ITarget Pack( ITaskContext context, params ITarget[] dependTargets ) { + return context.CreateTarget( "pack" ) + .SetDescription( "Create nuget packages." ) + .DependsOn( dependTargets ) + .ForEach( GetPackProjecs(), ( project, target ) => { + target.AddCoreTask( t => t.Pack() + .Project( project ) + .IncludeSymbols() + .OutputDirectory( OutputDir ) ); + } ); + } + + /// + /// 发布nuget包 + /// + private void PublishNuGetPackage( ITaskContext context, params ITarget[] dependTargets ) { + context.CreateTarget( "nuget.publish" ) + .SetDescription( "Publishes nuget packages" ) + .DependsOn( dependTargets ) + .Do( t => { + var packages = Directory.GetFiles( OutputDir, "*.nupkg" ); + foreach ( var package in packages ) { + if( package.EndsWith( "symbols.nupkg", StringComparison.OrdinalIgnoreCase ) ) + continue; + context.CoreTasks().NugetPush( package ) + .DoNotFailOnError( ex => { Console.WriteLine( $"Failed to publish {package}.exception: {ex.Message}" ); } ) + .ServerUrl( NugetUrl ) + .ApiKey( NugetApiKey ) + .SkipDuplicate() + .Execute( context ); + } + } ); + } + } +} \ No newline at end of file diff --git a/build/common.props b/build/common.props new file mode 100644 index 000000000..08ea70dcb --- /dev/null +++ b/build/common.props @@ -0,0 +1,19 @@ + + + + net8.0 + + + Util Application Framework + ncc;何镇汐 + ncc;何镇汐 + xiadao521@qq.com + https://github.com/dotnetcore/Util + https://github.com/dotnetcore/Util + git + Util应用框架以MIT开源发布,可随意使用 + MIT + https://avatars2.githubusercontent.com/u/19421630 + util;applicationframework;dotnetcore;aspnetcore; + + \ No newline at end of file diff --git a/build/icon.jpg b/build/icon.jpg new file mode 100644 index 000000000..b591214cc Binary files /dev/null and b/build/icon.jpg differ diff --git a/build/version.props b/build/version.props new file mode 100644 index 000000000..e526b26c3 --- /dev/null +++ b/build/version.props @@ -0,0 +1,9 @@ + + + 8 + 0 + 19 + $(VersionMajor).$(VersionMinor).$(VersionPatch) + + + \ No newline at end of file diff --git a/clear-bin.ps1 b/clear-bin.ps1 new file mode 100644 index 000000000..719b86c28 --- /dev/null +++ b/clear-bin.ps1 @@ -0,0 +1,3 @@ +Write-Host "remove bin,obj..." +Get-ChildItem .\ -include bin,obj -Recurse | foreach ($_) { remove-item $_.fullname -Force -Recurse } +Write-Host "remove completed." \ No newline at end of file diff --git a/readme.md b/readme.md index 37eca4273..000441d5f 100644 --- a/readme.md +++ b/readme.md @@ -1,470 +1,714 @@ # Util应用框架介绍 -[![Member project of .NET China Foundation](https://github.com/dotnetcore/Home/blob/master/icons/member-project-of-netchina.png)](https://github.com/dotnetcore) +[![Member project of .NET Core Community](https://img.shields.io/badge/member%20project%20of-NCC-9e20c9.svg)](https://github.com/dotnetcore) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://mit-license.org/) -Util是一个.net core平台下的应用框架,旨在提升小型团队的开发输出能力,由常用公共操作类(工具类)、分层架构基类、Ui组件,第三方组件封装,第三方业务接口封装,配套代码生成模板,权限等组成。 + + + -## 引子 -几年前我在博客园写了一个 [应用程序框架实战](https://www.cnblogs.com/xiadao521) 的系列文章,受到不少.net同学的青睐,不过由于种种原因(想写的范围过大,技术更新过快,工作忙,人越来越懒,好吧,我承认主要是最后一点)而烂尾。 +## 什么是Util应用框架? -我在博客中放出过一些示例代码,由于未正式开源,修复过的bug也未能更新,所以将博客中提供的示例项目都删除了。 +Util是一个.Net平台下的应用框架,旨在提升中小团队的开发能力,由工具类、分层架构基类、Ui组件,配套代码生成模板,权限等组成。 -一些同学将这些代码应用在项目上,当碰到问题时,由于没有技术支持,也没有相关教程,苦不堪言。而另外一些同学没有下载到示例项目。 +## Util应用框架的作者: **何镇汐** -虽然博客断更2年,但时常有同学通过加QQ或发EMAIL的方式联系我,希望能够得到最新代码,并对用法和设计思路进行讲解。 +Util应用框架的主要作者为何镇汐。 -.net core发展迅猛,它为.net提供了更广阔的竞争力,同时也意味着新一轮的学习与代码移植工作开始,我也在这个大潮中跌跌撞撞的摸索着。 +## Util应用框架的开源协议: **MIT** -几位好友([AlexLEWIS](https://github.com/alexinea "刘怡") [Kiler](https://github.com/kiler398 "谢炀") [Lemon](https://github.com/liuhaoyang "刘浩杨") )发起了.net core学习小组,并打算为国内.net core开源大业作出贡献,我果断加入了队伍进行学习,并决定对Util应用框架进行开源。 +这是最宽松的开源协议,意味着可以以任何方式使用该代码。 -经讨论,大家决定将各自的开源项目统一发布到 [.NET China Foundation](https://github.com/dotnetcore) 之下,以方便国内.net core开源项目的推广,同时也欢迎国内.net core开源项目作者的加入。 +## Util应用框架的主要贡献者 -## 技术框架与应用框架 +[汪总](https://github.com/jianxuanbing "简楚恩") [胡兴业](https://github.com/huxingye) -框架是一种可复用的基础代码库,如果它只解决纯技术问题,可以认为是技术框架,如果它与你的业务相关,则可认为是应用框架(框架,应用框架均没有标准定义,我在此以个人理解描述术语含义,以方便后续讨论,并非权威定义)。 +这里只列出了主要贡献者,还有很多开发人员以不同形式对Util做出了宝贵贡献。 -.net提供的基础类库是最基础的技术框架,它提供了进行.net开发所需要的基础API,这些API都比较原始。完全使用原始API进行项目开发,会导致高昂的学习成本,更多的Bug,难以维护的重复代码,低效的开发进度。 +## Util应用框架的目标: **让开发更简单** -为了完成某项特定的技术需求,比如写日志,你可以使用原始API进行简单实现,并在多个地方复制粘贴进行调用。这很快会让你厌烦,除了冗余代码以外,你发现还需要经常扩展日志操作的功能,于是你想把日志操作封装成库来调用。 +Util应用框架让你的团队迅速进入业务开发状态,并在开发过程中持续提供帮助。 -等等,不要再重复造轮子了(为了学习目的造轮子,这绝对是值得肯定的,但确实准备拿来使用的,如果不是真有几把刷子,比如 [Lemon](https://github.com/liuhaoyang "刘浩杨") 同学,建议还是省省吧),一些前辈已经做了这些基础工作,并且他们的工作已经在全球大范围的使用,非常成熟,并且持续维护。 +## Util应用框架的特点: **简单易用** -这些能够解决特定技术问题的类库暂时统称为第三方技术框架,在项目上使用第三方技术框架是项目成功的必要保障。从此,你不再为如何实现某项技术问题而烦恼,也不需要为持续扩展的技术需求而疲于奔命,更多的精力被用在业务上,这才是你应该干的事。 +Util应用框架的设计理念是追求Api简单化,尽量少的配置,不用精确记忆Api,有个模糊印象即可使用。 -对于你的项目,每当碰到某种技术障碍时,你会四处打听有没有某个第三方技术框架解决了你的问题,找到以后,你通过调试一些Demo代码,简单的用起来了。每当需要这个功能的时候,你会从以前的代码复制粘贴,由于第三方技术框架已经高度封装,所以冗余代码不是特别显著,你没有理会它。 +Util应用框架的学习成本相对较低,对于有.Net基础的开发人员,进行常规业务开发,通常在3天内上手。 -如果你的团队并不经常沟通,处理同一问题的过程将在团队中反复发生,这将导致对于某个问题的技术实现不一致,并且付出高昂的学习成本。当调用代码被大量的复制粘贴后,如果发现调用代码有Bug,或需要增加一些健壮性,你才意识到虽然第三方技术框架已经比较完善,但对它的调用依然有封装的必要。 +## Util应用框架开发流程 -你开始封装一些经常需要操作的代码,这些代码可能是用.net原始Api封装的,也可能是封装了第三方技术框架的调用,这些封装好的代码被称为公共操作类,也叫工具类。 +> *搜集* - *整理* - *集成* - *封装* -对于最基础的Crud和业务代码的结构,你发现也可以抽取出来,形成分层架构基类。 +每当发现无法满足项目需求时,扩展框架的时刻来临。 -你的项目可能需要在网页上进行开发,采用原始的html,css,js技术,将面临界面美观,浏览器兼容性,开发效率低下等问题,有第三方技术框架提供了相关的解决方案,比如EasyUi,Bootstrap,Angularjs,ReactJs等。将第三方UI框架集成并封装起来,能够显著提升开发效率。 +从头造轮子是下策,所以总是先四处**搜集**相关资料。对于简单的需求,可能只需要找到一些代码片断即可。对于更专业的问题,需要寻求第三方技术框架的帮助。 -如果在项目中,你需要实现支付等业务功能,你可以对这些第三方业务接口进行封装。 +找到解决方案并调试通过后,需要对代码进行**整理**,以符合我们的编程风格。 -对于常规的项目,总是需要一些通用模块,比如权限,将它作为后续项目的起点是一个好主意。 +通常以Nuget包或Npm包的方式进行**集成**,为了降低学习成本,需要仔细考虑如何**封装**才能让调用者最省力。 -当这些基础代码被高度封装后,常规的Crud代码变得非常简洁,但你还是需要复制这些基础结构,通过反复整理,可以得到一个代码模板,通过代码生成工具将基础的Crud代码生成出来,可以大幅提升生产力。 +> 任何有技术含量的工作,均由第三方技术框架完成,Util应用框架仅对技术框架选型并集成封装。 +> +> Util应用框架只是很薄的一层外观,为复杂的技术框架提供一个简易视图。 +> +> 这导致更健壮的实现和更易用的API。 -这个持续封装过程演变为一套项目开发模式,支持这套模式的所有基础代码库,我将它称之为应用框架。 +## Util应用框架的使用方式 -应用框架为你项目的所有方面提供支持,你需要不断的完善它。由于应用框架需要解决你项目上的所有问题,所以没有绝对通用和包罗万象的应用框架,你必须在这些应用框架之上进行扩展。 +使用Util应用框架有几种不同的方式。 -## Util的由来 +最灵活的方式是将Util应用框架的代码复制到你自己的应用框架中,然后可以随意修改它们来满足项目需求。 -Util是我在.net开发生涯中不断积累而成的一套应用框架,最初的名字叫CommonUtilities,这是一个基本由公共操作类构成的类库。 +不过这种方式的代价也很大,很难合并Util的任何更新,你需要自行维护相当大的代码库。 -随着经验和水平的上升,我根据自己的习惯,逐步实现了经典图书中某些概念(比如规约),并吸收了一些流行应用框架( 比如 [ABP](https://github.com/aspnetboilerplate/aspnetboilerplate) [Nop](https://www.nopcommerce.com) )的部分代码。 +由于Util应用框架的体积在不断增长,维护这些代码库可能给你带来不小的负担。 -Util并不是一个完善的应用框架,我总是根据自己项目上的需求来扩展它,所以它基本只是为了满足我个人习惯和项目的产物。 +另一种可行的方式是将Util应用框架作为你的基础库,使用Nuget引入它们,基础的工作让Util应用框架帮你完成,你只需要扩展自己业务领域相关的功能。 -由于技术更新异常迅速,多年来大量的代码被持续重构,还有更多的代码被抛弃,这也是Util不完善的一个重要原因。 +这种方式的限制也很明显,Util提供的功能可能无法满足你的要求。 -发布Util的目的,是希望帮助技术还比较落后的.net小型团队能够搭建出自己的应用框架,提供一个示范,同时也希望它成为跟我有相似开发习惯和偏好的.net同学的重要工具。 +解决这个问题的关键是与Util开发团队保持沟通,将你的需求告诉我们。 -## 愿景 +对于通用功能,我们会尽量满足,这样会让更多的开发者受益,对于定制需求,我们也会为你提供扩展点,方便你能自行扩展。 -让开发更简单 +## 框架核心 -## 承诺 +- ### 启动器 -不必担心再次断更,代码会持续更新,使用文档将由Util核心开发团队成员编写,我会补充一些设计方面的文档上来。 +Util应用框架在项目启动时,会扫描全部程序集,并自动执行服务注册器,执行相关依赖配置. -## 特点 +启动器核心代码参考自 [NopCommerce](https://www.nopcommerce.com) . -**简单易用**的傻瓜式API设计,让你的团队能够快速上手进行开发。对于简单操作,通常采用静态方法或扩展方法进行调用,对于更加复杂的操作,尽量封装为链式API。 +- ### 服务注册器 -**强类型(*静态类型*)** 化风格,将UI组件封装为asp.net core razor组件,充分发挥VS开发工具的优势,比如智能提示,编译时检查。 +项目启动扫描时,会加载所有的服务注册器,并按顺序执行. -**代码整洁**,Util开发小组成员都有着代码洁癖,每行代码都经过反复斟酌。 +> 服务注册器类似其它应用框架的模块概念,但功能有限. +> +> 只能用于配置依赖注入关系,无法获取服务提供程序,所以不能执行复杂的操作. -地道的**中国味**,Util开发小组成员的英文水平比较菜,命名充满国风,并以中文注释进行补充。 +- ### 依赖注册器 -## 面向群体 +扫描 ISingletonDependency, IScopeDependency, ITransientDependency 接口,并自动配置依赖. -Util应用框架面向的是*架构初学者*,但不是.net初学者。 +## 框架基础 -如果你是.Net初学者,基本的Crud(增删改查)尚且完成不了,请缓一年再来。 +- ### 对象到对象映射 -你可能已经有了一些项目经验,甚至已经开发多年,但你的项目依然进度缓慢,很难按时交付,代码质量糟糕,难以继续维护,你迫切希望找到一种方法来改善这个过程,Util就是为你准备的。 +技术选型: [AutoMapper](http://automapper.org/). -如果你手里没有任何积累,Util将是你搭建自己框架的一个很好的起点。 +对象到对象映射主要用于分层对象之间的转换,比如从实体映射到DTO. -如果你已经身经百战,自己的框架也比较成熟,Util对你的帮助不会太大。 +Util 在所有对象上添加 **MapTo** 扩展方法,自动完成基础配置,如果映射的两端对象属性名相同,则无需配置,直接使用即可. -如果你对API的英文语法要求很高,Util可能无法达到你的要求,请自行修改命名或远离它。 +- ### AOP( 面向切面编程 ) -## 使用方式 +技术选型: [AspectCore](https://github.com/dotnetcore/AspectCore-Framework). -我推荐的方式是搭建你自己的应用框架,逐步吸收Util和其它应用框架的代码,但这需要你投入大量的学习时间。 +AOP用于在调用方法时进行拦截,在执行前后添加自定义逻辑. -我会逐步更新框架代码,当时机成熟时,将提供一个示例应用程序供大家参考。 +Util 主要使用AOP拦截器进行验证. -在把框架完全应用到你的项目之前,你的团队至少要有一名成员能够根据项目需求进行扩展。对于懒汉,通常希望拿到一套干货,直接在项目上当成黑盒来使用,除非你的项目异常简单,否则这种方式不可取。 +- ### 对象验证 -对于简单的操作,你可以运行和阅读相应单元测试代码,即可了解它们如何使用。对于复杂操作,一方面看单元测试,另外我们会编写相关文档以描述调用方式和工作机制。 +Util 使用 DataAnnotation 注解作为基础验证方式,并提供 **验证规则** 和 **验证处理器** 等构造块进行自定义验证和处理. -## 开发环境及类库依赖 +Util 在实体,DTO等对象上添加了 **Validate** 方法, 以标准化的方式进行对象验证. -以下是我们在项目开发和部署时使用的工具和组件,这个列表会经常更新。 +- ### 异常处理 -> 如果没有标注版本号,则采用最新版本。 +Util 提供了 **Warning** 类型,表示业务异常,并封装相关异常操作. -1. 开发工具: - - Visual Studio 2017 - - Resharper +- ### 身份认证和授权 -2. 数据库 - - Sql Server - - Mysql - - PostgreSQL +技术选型: [Identity](https://learn.microsoft.com/zh-cn/aspnet/core/security/authentication/identity), [Identity Server 4](https://github.com/IdentityServer/IdentityServer4) -3. 设计工具 - - PowerDesigner 16.5 - - XMind +Identity 是Asp.Net Core自带的身份认证框架,提供用户管理,登录认证等功能. -4. 版本控制 - - Git +> Identity Server 4 是一个身份认证服务器,用于颁发令牌和验证令牌等操作. +> +> Identity Server 4 已经停止更新,它的新版本是收费的,Util 仅使用 Identity Server 4 的基础功能,由于它的基础功能已经十分稳定,暂时不需要迁移到其它框架. -5. 部署环境 - - Ubuntu Server - - Docker +Util 除了集成 Identity 和 Identity Server 4,还扩展了Asp.Net Core自定义授权策略,提供简单易行的Api访问控制方法. -6. 开发平台 - - .Net Core 2.0 +Util Platform提供了基于角色和资源的权限管理功能,可以轻松设置前端UI菜单和按钮等权限,同时对后端API进行访问控制. -7. 单元测试及模拟框架 - - XUnit - - NSubstitute +- ### 本地化 -8. ORM - - EntityFrameworkCore 2.0 - - Microsoft.EntityFrameworkCore.SqlServer 2.0.0 - - Npgsql.EntityFrameworkCore.PostgreSQL 2.0.0 - - Pomelo.EntityFrameworkCore.MySql 2.0.x +技术选型: [My.Extensions.Localization.Json](https://github.com/hishamco/My.Extensions.Localization.Json) -9. Ioc框架 - - Autofac +Util 本地化支持Json文件或自定义数据存储方式. -10. Aop框架 - - [AspectCore](https://github.com/dotnetcore/AspectCore-Framework) +My.Extensions.Localization.Json 提供了Json本地化功能. -11. Json框架 - - Newtonsoft.Json( 即Json.Net ) +由于无法满足项目需求, 以源码方式引入, 对健壮性和性能进行了提升. -12. 映射框架 - - AutoMapper +- ### 日志 -13. 日志框架 - - [NLog](http://nlog-project.org/) - - [Exceptionless](https://github.com/exceptionless) +技术选型: [Serilog](https://serilog.net/), [Seq](https://datalust.co/), [Exceptionless](https://exceptionless.com/) -14. Id生成器 - - [ECommon.Utilities.ObjectId](https://github.com/tangxuehua/ecommon/blob/master/src/ECommon/Utilities/ObjectId.cs) +Serilog是.Net目前最流行的日志框架,支持结构化日志. -15. RSA加密算法 - - [DotnetCore.RSA](https://github.com/stulzq/DotnetCore.RSA/blob/master/DotnetCore.RSA/RSAHelper.cs) +Seq 和 Exceptionless 是日志管理系统,提供了操作日志的界面,可以使用Serilog写入. -16. 二维码操作 - - [QRCoder](https://github.com/codebude/QRCoder/) +Util 对它们进行了轻度封装,集成了常用配置,并提供日志上下文等扩展. -17. 短信操作 - - [Luosimao](https://luosimao.com/) +- ### 缓存 -18. Queryable动态扩展 - - [System.Linq.Dynamic.Core](https://github.com/StefH/System.Linq.Dynamic.Core) +技术选型: [EasyCaching](https://github.com/dotnetcore/EasyCaching) -19. Web框架 - - [Asp.Net Core](https://docs.microsoft.com/zh-cn/aspnet/core/) +Util 集成了内存缓存和Redis缓存两种提供程序. -20. UI - - 脚本语言 - - [TypeScript](https://www.tslang.cn) - - 脚本框架 - - [Angular 6](https://angular.cn/) - - Css预处理器 - - [Sass](http://www.sass-lang.com) - - 组件库 - - [Angular Material](https://material.angular.io/) - - [PrimeNg](https://www.primefaces.org/primeng) - - [Angular-Material-App](https://github.com/stbui/angular-material-app) - - 脚本库 - - [Moment](http://momentjs.cn/) - - 图标 - - [Material Design Icon](https://material.io/icons/) - - [Font Awesome Icon](http://fontawesome.io/) - - 打包 - - Webpack - - 测试 - - Karma - - Jasmine +- ### 事件总线 -21. 代码生成器 - - CodeSmith 6.5 +技术选型: [MediatR](https://github.com/jbogard/MediatR), [Dapr](https://dapr.io/) -22. 参考应用框架 - - [ABP](https://github.com/aspnetboilerplate/aspnetboilerplate) - - [Nop](https://www.nopcommerce.com) - - Nop是一个开源商城,封装了大量实用的基础代码。 +> 事件总线分为本地事件总线和集成事件总线. -## 框架开发流程 +Util 本地事件总线提供两种实现,默认使用Ioc方式实现,无需外部依赖. 另外引入了 MediatR 框架,它是流行的进程内事件处理框架. - > *搜集* - *整理* - *集成* - *封装* +集成事件总线用于微服务之间消息通信,Util 集成了 Dapr 微服务框架,基于 Dapr 的发布订阅实现集成事件总线. -每当发现框架无法满足项目中的需求时,扩展的时刻来临。 +- ### 模板引擎 -从头造轮子是下策,所以总是先四处**搜集**相关资料。对于简单的需求,可能只需要找到一些代码片断即可。对于更专业的问题,比如将对象转换为json字符串,需要寻求第三方技术框架的帮助。 +技术选型: [RazorEngineCore](https://github.com/adoconnection/razorenginecore) , [Handlebars.Net](https://github.com/Handlebars-Net/Handlebars.Net) -找到解决方案并调试通过后,需要对代码进行**整理**,以符合我们的编程风格。 +Util 以源码方式引入 RazorEngineCore ,并进行大量修改. + +Razor 模板引擎目前主要用在 Util 代码生成器. + +- ### 图像处理 + +技术选型: [ImageSharp](https://sixlabors.com/) + +ImageSharp 是一个图像处理库. + +Util 使用 ImageSharp 实现动态文本头像生成. + +- ### 文件存储 + +技术选型: [Minio](http://minio.org.cn/) + +Minio 是流行的对象存储系统,适合部署到内部服务器. + +Util 集成封装了 Minio SDK, 提供 存储桶名称处理策略, 文件名处理策略等构造块进行扩展处理. + +- ### 后台任务 + +技术选型: [Quartz](https://www.quartz-scheduler.net/) , [HangFire](https://www.hangfire.io/) + +Util 封装了 Quartz 和 HangFire, 用于处理后台任务. + +- ### 数据访问 + +技术选型: [EntityFrameworkCore](https://docs.microsoft.com/zh-cn/ef/core/) , [Dapper](https://github.com/DapperLib/Dapper) + +Util数据访问主要使用 EntityFrameworkCore ,它是.Net官方提供的ORM框架,属于重量级数据访问框架. + +EntityFrameworkCore 支持工作单元模式,对复杂的业务处理非常有效,但对复杂的查询操作无能为力. + +可以直接使用 EntityFrameworkCore 执行Sql语句, 但此操作非常原始,效率低下. + +Dapper是轻量级数据访问框架, Util引入它并进行封装,用于解决查询问题. + +目前 Util Dapper 封装尚不可用,它还缺乏分页,Lambda表达式支持等高级功能,现在仅用于代码生成读取元数据,后续会进行扩展. + +由于使用国产数据库的需求逐渐增多,后续将考虑引入 [FreeSql](https://github.com/dotnetcore/FreeSql) 数据访问框架. + +- ### 工具类 + +Util包含大量工具类,用于处理常用操作. + +- 类型转换操作 +- Lambda操作 +- 命令行操作 +- 配置操作 +- 枚举操作 +- Ioc操作 +- Json操作 +- 字符串操作 +- 时间操作 +- IP操作 +- Web操作 +- 加密操作 +- ... + +## 架构支持 + +### 多租户架构支持 + +Saas系统支持多家客户使用同一系统,客户数据彼此隔离. + +Util 参考了 [ABP](https://abp.io/) 应用框架的多租户架构设计的租户解析和数据过滤部分. + +Util 多租户架构在独立部署租户数据库方面基于 EntityFrameworkCore 的官方建议,有一些限制. + +### DDD分层架构支持 + +Util 分层架构基于领域驱动设计(DDD) 和 企业应用架构模式(POEAA) 的基础构造块. + +.Net 早期流行的分层架构是三层架构, 对于不熟悉 DDD 的开发人员,可以把它当成三层架构使用,不过术语的变化而已. + +但是对于有一定领域模型使用经验的开发人员,该架构在处理更复杂的业务时可以有效管控复杂性. + +对于复杂模块的开发,该架构与三层架构的主要区别在于,实体是作为简单数据容器,还是作为业务处理的场所. + +对于很多业务领域,将实体对应的业务操作封装到实体本身,可以提升业务表达能力和封装性. + +Util 分层架构分为四层. + +#### 领域层 + +领域层是Util分层架构的核心. + +领域层非常纯净, 没有对数据访问和UI的依赖,很容易对该层进行单元测试. + + - ##### 聚合根 + + 作为并发单元的最外层实体. + + - ##### 实体 + + 由唯一标识决定的业务对象. + + - ##### 值对象 + + 表示实体属性值的对象. + + - ##### 仓储 + + 表示聚合根的集合,用于进行数据访问. + + - ##### 领域服务 + + 用于封装多个聚合根的操作. + + - ##### 领域事件 + + 表示实体数据发生变化. + + 使用Util本地事件总线发布领域事件. + + 有几个标准领域事件: + + - EntityChangedEvent - 实体变更事件 + - EntityCreatedEvent - 实体创建事件 + - EntityUpdatedEvent - 实体修改事件 + - EntityDeletedEvent - 实体删除事件 + +#### 基础设施层 + +对于大多项目,基础设施层主要包含数据访问的实现. + +#### 应用层 + +集成了领域层和基础设施层,为UI提供服务. + + - ##### 应用服务 + + 应用服务接收UI请求,并将调用委托给实体,领域服务等构造块完成业务功能. + + 对于普通项目,业务逻辑直接在应用服务中实现即可,这与三层架构相似. + + - ##### 数据传输对象(DTO) + + 数据传输对象是一种用来传递参数的对象. + + - ##### 查询参数对象 + + 查询参数对象是一种专门用来传递查询参数的对象. + +#### 表现层 + +### 微服务架构支持 + +Util 集成了 [Dapr](https://dapr.io/) ,它是微软主推的微服务框架. + +Util 封装了 Dapr Http同步调用,异步事件发布订阅,状态存储等功能. + +并对身份认证,事件日志,事件重发等重要功能进行扩展. + +### 常见业务功能支持 + +#### 树形结构支持 + +有些数据包含层次结构. + +Util 分层架构包含对树形结构的支持. + +如果使用 Util UI,无论加载树形表格还是树形,继承基类即可实现功能. + +#### 审计支持 + +大多操作需要记录创建人,创建时间,修改人,修改时间等审计信息. + +Util支持保存时自动设置审计信息. + +Util 审计设计参考自 [ABP](https://abp.io/) 应用框架. + +#### 逻辑删除支持 + +Util 支持逻辑删除,删除时更新为已删除状态,查询时自动过滤已删除记录. + +Util 逻辑删除设计参考自 [ABP](https://abp.io/) 应用框架. + +#### 对象变更值支持 + +修改时可能需要获取对象哪些数据发生了变化. + +Util 提供多种方式获取对象变更值记录. + + +## 代码生成 + +由于 Util 采用 DDD分层架构,导致简单需求也需要创建大量的类型. + +手工创建这些类型效率低下,通过代码生成迅速创建它们,可以大幅提升开发效率和质量. + +Util 配套代码生成器, 简单易用, 可解决大部分机械工作. + +在生成的代码基础上进行修改,比从头开始工作要好得多. + +## UI 技术选型 + +- ### Js语言 + - #### [TypeScript](https://www.typescriptlang.org/zh/) + TypeScript 是 微软开发的脚本语言, 扩展了弱类型的 Javascript,提供增强的语法和强类型支持. + + 为编辑器代码提示和语法错误检测奠定坚实基础. + +- ### Js框架 + - #### [Angular](https://angular.io) + + Angular 是 Google开发的 Js框架. + + Angular使用 TypeScript 脚本语言开发, 并采用 [RxJs](https://github.com/ReactiveX/RxJS) 响应式编程框架. + + Angular 是前端Js三大框架之一,另外两个是 Vue 和 React. + + Util UI 用于开发管理后台,选择 Angular 是因为它的语法最优雅, 也最符合后端开发人员的习惯. + +- ### Angular 组件库 + + - #### [Ng Zorro](https://ng.ant.design/) + + Ng Zorro 是阿里 Ant Design 的 Angular 版本,提供 80+ 常用组件,覆盖大部分业务开发场景. + + - #### [Ng Alain](https://ng-alain.com/) + + 虽然 Ng Zorro 提供了大量常用组件,但项目开发需要一个集成度更高的环境. + + Ng Alain 是一个基架项目, 集成了 Ng Zorro 组件,提供业务开发的项目模板,除了菜单导航等框架元素,还有很多开箱即用的业务处理页面模板. + +- ### Angular 微前端框架 + + - #### [Angular Module Federation](https://github.com/angular-architects/module-federation-plugin) + + 如果你的项目包含大量 Angular 模块,所有文件在同一个项目中,会导致开发环境卡顿和缓慢. + + 发布项目也可能需要很长时间. + + 另外,如果某个模块需要进行修改,哪怕只修改一行代码,也需要对所有模块重新发布. + + 与后端的微服务类似,微前端是前端的项目拆分方法. + + 微前端将不同的 Angular 模块拆分到不同项目中,可以独立开发,独立测试和独立部署. + + > 无论你是否使用微服务架构,均可使用微前端拆分方式. + + Angular Module Federation 是基于Webpack模块联合的Angular微前端解决方案. + + +## Util Angular UI 特点 + +- ### 组件扩展支持 + + 除了支持 Ng Zorro 原生功能外,Util UI还对常用组件进行了扩展. + + 最重要的扩展是支持常用组件直接发出 Api 请求,而不用定义额外的服务. + +- ### Razor TagHelper 支持 + + Util Angular UI不仅可以使用 html 页面,还能使用 .Net Razor 页面. + + Razor 页面可以使用 TagHelper 服务端标签. + + Util 已将大部分 Ng Zorro 组件封装为 TagHelper 标签. + + 除了获得强类型提示外,TagHelper 作为抽象层,提供更简洁的标签语法. + + 另一个强大之处在于Lambda表达式支持, 可以将DTO直接绑定到 TagHelper 标签上. + + 能够从Lambda表达式提取元数据,并自动设置大量常用属性,比如name,验证,模型绑定等. -对于像autofac这样的类库,只需要nuget引用一下就完事,但对于UI层面的框架,需要进行更复杂的**集成**才能工作,我们可能还会修改一些源码以方便后续扩展。 +- ### 前后分离 -最后,为了达到复用的目的,需要仔细考虑如何**封装**才能让调用者最省力。 + 一些开发人员看到 Util Angular UI 使用 .Net Razor 页面,可能认为 UI 与 .Net 高度耦合,但现在的趋势是前后分离. -> 前几年,我喜欢把第三方技术框架的源码放到我的类库中,那是因为nuget经常连接失败,不过现在nuget比较稳定了,并且对包的嵌套引用支持得很好,所以不再采用山寨方式。 + 所谓前后分离,是前端UI和后端API没有依赖,更换某一端对另一端没有影响. -> 任何有技术含量的工作,均由第三方技术框架完成,Util应用框架仅对技术框架选型并集成封装。Util只是很薄的一层外观,为复杂的技术框架提供一个简易视图。这导致更健壮的实现和更易用的API。 + 另外,前后分离后,前端UI和后端API可以由不同的开发人员完成. -## 项目开发流程 + .Net Razor页面仅在开发阶段提供帮助,在发布时, Razor 页面会转换为 html ,后续发布流程与纯前端开发方式相同. -一套得心应手的应用框架,能让你的团队如虎添翼,开发效率和开发质量将同时提升N倍,这在拥有应用框架的公司已经是不争的事实。 + 一旦发布成功,将完全脱离.Net 环境,可以使用 Nginx 容器承载它. -那是不是意味着,只要下载到一套应用框架,你的团队马上就能脱胎换骨呢? 非也,梅花香自苦寒来,宝剑锋从磨砺出,路漫漫其修远兮,你将上下而求索。 + 发布后的产物,与你使用纯前端方式开发打包没有区别. -任何应用框架都属于辅助设施,能否高效完成项目,还是靠你团队本身的水平。要高效完成项目,需要对技术、架构、过程等方面达到一定认识,这是一个漫长的学习过程。我会为你分享一些项目开发流程上的经验,并提供一份经典图书的清单。 + 如果你喜欢,可以把后端API换成JAVA,也能正常运行. -对于普通的项目,大体由简单的基础管理模块和较复杂的业务模块构成。 +- ### 配套Api支持 -基础管理模块表现为Crud操作加上一些简单业务逻辑,它们的特点是功能简单但数量众多,解决它们的有效手段是代码生成器,由生成器生成全套代码(包括管理后台的UI代码),然后在这个基础上手工修改。 + 前端UI和后端API的开发是两个完全不同的领域. -对于复杂的业务模块,它们往往由权限(操作权限与数据权限)、流程控制、复杂的计算、绕脑的策略等问题交织在一起,解决它们的有效手段是DDD(领域驱动设计),TDD(测试驱动开发),设计模式。 + 但开发一个功能,又需要前端和后端的配合,他们需要沟通,作出一些约定. -> 遗憾的是,听过这些名词的部分开发人员,由于没有真正实践,还在道听途说的质疑这些方法的有效性,或是用这些方法来开发Crud操作,用大炮打蚊子,没用对地方反而质疑方法的作用。 + 对于配合不到位的团队,前后端的沟通成本可能很高,另外提供的API可能无法满足UI的需求,从而让前端代码变得畸形. -对于权限,目前发现最通用,最简单,最易理解的方法是基于*资源*和*角色*的权限设计。操作权限是通用的,但数据权限太灵活,目前我们配合规约模式来解决。 + 通常.Net开发人员的Js编程功底高于常规前端人员,前端人员更擅长样式布局. -由于项目开发流程是一个很大的主题,我会用专门的文章来进行介绍,以抛砖引玉。 + Util Angular UI 不仅提供对前端组件的封装,同时也为常见功能提供 Api 支持. -## 作者 + 对于使用 Util Angular UI 的团队, 将 UI 和 API 交给同一个.Net开发人员就是最好的选择. -何镇汐 + 前端人员仅调整界面样式即可. -## 核心开发团队 + 不仅减少了沟通成本, API和前端组件的高度集成封装,让常规功能的开发效率得到大幅提升. -[何镇汐](https://github.com/utilcore) [程序喵](https://github.com/program-meow "胡雲鹏") [Richfiter](https://github.com/xingwen1987 "邢文") + 当然,这对 .Net 开发人员的水平有一定要求. -应用框架的开发工作量很大,个人显得力不从心,我也迫切希望找到一些志同道合的同学共同完成,同时也欢迎你的加入。 +- ### 本地化支持 -对于Util核心开发团队成员,需要至少独立完成Util中的一个模块(比如微信公众号接口封装),并持续维护它。 + 得益于 Ng Alain 本地化的良好设计, 可以使用 i18n 管道进行文本的本地化转换. -> 为了保持代码风格的统一,Util核心开发团队必须以统一的编码规范提交代码,在必要的时候,我会对代码进行重构。 + ``` + '文本' | i18n + ``` -## 技术顾问团队 + 不过对于需要支持本地化的项目,这依然是一个负担,每个表单项,每个表格项,每个文本,可能都需要添加 i18n 管道. -[AlexLEWIS](https://github.com/alexinea "刘怡") [Kiler](https://github.com/kiler398 "谢炀") [Lemon](https://github.com/liuhaoyang "刘浩杨") [Savorboard](https://github.com/yuleyule66 "杨晓东") [Lyrics](https://github.com/lyricsyo "娄宇") + Util Angular UI 让本地化开发更进一步,对大部分组件提供了本地化支持,只有极少数文本需要手工添加 i18n 管道. -Util应用框架技术顾问团队负责对API易用性,代码健壮性,设计缺陷等内容进行审查,在发现问题时提出改善意见。 +- ### 授权访问支持 -> 如果您是一名资深.net开发专家,并希望为Util应用框架出谋划策,欢迎加入Util应用框架技术顾问团队。 + Ng Alain提供了授权访问的支持. -## 贡献与反馈 + Util Platform权限模块基于资源和角色的设计,可以很好的与 Ng Alain授权进行集成. -> 如果你在阅读或使用Util中任意一个代码片断时发现Bug,或有更佳实现方式,请通知我们。 + 你可以控制菜单和任意区域根据权限显示和隐藏. -> 为了保持代码简单,目前很多功能只建立了基本结构,细节特性未进行迁移,在后续需要时进行添加,如果你发现某个类无法满足你的需求,请通知我们。 +- ### 微前端支持 -> 你可以通过github的Issue或Pull Request向我们提交问题和代码,如果你更喜欢使用QQ进行交流,请加入我们的交流QQ群。 + Util Angular UI 引入了 [Angular Module Federation](https://github.com/angular-architects/module-federation-plugin) , 能够将 Angular 模块拆分到不同项目中,可以独立开发,独立测试和独立部署. -> 对于你提交的代码,如果我们决定采纳,可能会进行相应重构,以统一代码风格。 + 对于大中型项目,这是非常有必要的. -> 对于热心的同学,将会把你的名字放到**贡献者**名单中。 -## 贡献者 +## Util Angular UI 功能列表 -## 交流方式与技术支持 +Util Angular UI 主要由 util-angular 和 Util.Ui.NgZorro 两个库提供支持. - 当你在使用Util进行开发时,倘若碰到无法解决的问题,可以加群讨论,Util团队会尽力帮助你度过难关。 + - ### util-angular 功能列表 - - Util应用框架交流QQ群(一群):24791014 + [util-angular](https://github.com/util-core/util-angular) 是一个 Js 库, 由Curd组件基类, Ng Zorro常用组件扩展指令和一组工具类组成. - > 不论是Util应用框架的代码,还是交流活动都是免费的,所以你不能指望我们必须在短时间内响应你,同时也希望大家都保持开放的心态,为初学者提供一些便利。 + - #### 基础类型 - > 对于一些伸手党,本来百度3分钟就可以解决的问题,却四处找人问,对于这些家伙,我建议你们转JAVA。 + - ViewModel - 视图模型基类 + - TreeViewModel - 树形视图模型基类 + - TreeNode - 树形节点基类 + - PageList - 分页列表 + - QueryParameter - 查询参数基类 + - TreeQueryParameter - 树形查询参数基类 + - Result - 服务端返回结果 + - StateCode - 服务端状态码约定 + - SelectItem - 列表项 + - SelectList - 列表 + - SelectOptionGroup - 列表配置组 + - SelectOption - 列表配置项 -## 免责申明 + - #### 工具类 -- 虽然我们对代码已经进行高度审查,并用于自己的项目中,但依然可能存在某些未知的BUG,如果你的生产系统蒙受损失,Util团队不会对此负责。 + - 浏览器本地存储操作 + - Cookie操作 + - 事件总线操作 + - 本地化操作 + - Ioc操作 + - 加载操作 + - 路由操作 + - 弹出层操作 + - 抽屉操作 + - 表单操作 + - Http操作 + - Web Api操作 + - 消息操作 + - ... -- 出于成本的考虑,我们不会对已发布的API保持兼容,每当更新代码时,请注意该问题。 + - #### Crud组件基类 -## 开源地址 + - 编辑组件基类 + - 表格编辑组件基类 + - 树形编辑组件基类 + - 查询组件基类 + - 表格查询组件基类 + - 树形表格查询组件基类 -https://github.com/dotnetcore/util/ + - #### Ng Zorro指令扩展 -## License + - 必填项验证扩展指令 + - 验证消息扩展指令 + - Ng Zorro 按钮扩展指令 + - Ng Zorro 选择框扩展指令 + - Ng Zorro 表格扩展指令 + - Ng Zorro 表格编辑扩展指令 + - Ng Zorro 树形表格扩展指令 + - Ng Zorro 树形扩展指令 + - Ng Zorro 上传扩展指令 -**MIT** + - ### Util.Ui.NgZorro 库介绍 + + Util.Ui.NgZorro 是一个 C# 类库,包含 TagHelper标签和树形控制器等类型. + + 绝大部分 Ng Zorro 组件已经封装. + + 由于组件很多,就不一一列出. -> 这意味着你可以在任意场景下使用Util应用框架而不会有人找你要钱。 +## Util Angular UI 已知缺陷 -> Util会尽量引入开源免费的第三方技术框架,如有意外,还请自行了解。 +Util Angular UI 所有已知缺陷均已解决. -## 书单 +## Util Angular UI 适合你吗? -> 本清单会持续更新 +Util Angular UI 是为 .Net 全栈工程师准备的,如果你喜欢更简洁的语法,希望开发的成本更低,它就适合你. -- 面向对象 - - 《UML和模式应用》 作者:Craig Larman - - 《面向对象分析与设计》 作者:Grady Booch +## 参考应用框架 -- 架构模式 - - 《企业应用架构模式》 作者:Martin Fowler - - 《领域驱动设计》 作者:Eric Evans - - 《敏捷软件开发-原则、模式与实战》 作者:Robert C.Martin - - 《实现领域驱动设计》 作者:Vaughn Vernon +很明显,闭门造车不可取。 -- 设计模式 - - 《设计模式》 作者:Erich Gamma - - 《Head First设计模式》 作者:Eric Freeman +Util应用框架大部分依赖以Nuget方式引入,极少部分需要修改源码,则以复制源码方式使用。 -- TDD - - 《测试驱动开发》 作者:Kent Beck - - 《测试驱动开发的艺术》 作者:Lasse Koskela - - 《单元测试的艺术》 作者:Roy Osherove - - 《重构-改善既有代码的设计》 作者:Martin Fowler - - 《实现模式》 作者:Kent Beck - - 《代码整洁之道-Clean Code》 作者:Robert C.Martin - - 《重构与模式》 作者:Joshua Kerievsky +另外,.Net应用框架还有很多优秀的开源项目,从中学习和吸取养分是Util成长的关键。 -## 重大升级 +Util应用框架主要参考了以下开源项目,从中吸收架构和代码,并以Util风格进行整理。 -> 包括.net core版本的跳跃式升级等,比如从.net core1.x升级到2.0。 - -- 2017年7月21日,将.net core升级到2.0 Preview 2。 - -## 更新计划 - -> 不打算提供精确的更新计划,以免无法兑现。 - -> 对于已发布的代码,将标记为[已发布]。对于已迁移到.net core的代码,将标记为[已完成],并即将发布。对于正在开发或迁移的代码,标记为[开发中]。对于尚未启动的部分,标记为[待启动]。这个列表会经常更新。 - -- 公共操作类(工具类)及扩展 - - 类型转换操作 [已发布] - - Json操作 - 基于Newtonsoft.Json [已发布] - - 映射操作 - 基于AutoMapper [已发布] - - Ioc操作 - 基于Autofac [已发布] - - 应用程序异常操作 [已发布] - - 验证操作 [已发布] - - 验证操作拦截器 [已发布] - - 枚举操作 [已发布] - - 字符串操作 [已发布] - - Lambda表达式操作 [已发布] - - 日志操作 - 基于NLog和Exceptionless [已发布] - - 日志操作拦截器 [已发布] - - IQueryable查询扩展 [已发布] - - 时间操作 [已发布] - - 上下文操作 [已发布] - - 类型查找器 [已发布] - - 二维码操作 - 基于QRCoder [已发布] - - 短信操作 -基于LuoSiMao [已发布] - - 加密操作 [已发布] - - Url参数生成器 [已发布] -- 分层架构基类及组件 - - 实体基类 [已发布] - - 聚合根基类 [已发布] - - 值对象基类 [已发布] - - 树型实体基类 [已发布] - - 操作审计 [已发布] - - EF Core实体映射配置基类 [已发布] - - EF Core工作单元基类 [已发布] - - EF Core调试日志 [已发布] - - 仓储基类 [已发布] - - 查询对象 [已发布] - - 范围查询条件 [已发布] - - 分页参数 [已发布] - - 分页集合 [已发布] - - 查询参数 [已发布] - - 工作单元服务 [已发布] - - 工作单元拦截器 [已发布] - - Crud服务 [已发布] - - 树型Crud服务 [待启动] - - 持久化对象Po基类 [已发布] - - 持久化对象存储基类 [已发布] - - 事件总线 [已发布] -- UI组件 - 支持HtmlHelper和TagHelper两种方式 - - 组件基类 [已发布] - - TagHelper基类 [已发布] - - 图标 - 集成了Material Design和Font Awesome图标集 [已发布] - - 下拉列表 - 基于Material Select组件 [已发布] - - 文本框 - 基于Material 文本框和日期选择框组件 [已发布] - - 表单 [已发布] - - 按钮 - 基于Material 按钮组件 [已发布] - - 链接 - 基于Material 链接组件 [已发布] - - 复选框 - 基于Material 复选框组件 [已发布] - - 滑动开关 - 基于Material slide-toggle组件 [已发布] - - 单选框 - 基于Material 单选框组件 [已发布] - - 菜单 - 基于Material Menu组件 [已发布] - - 选项卡 - 基于Material Tabs组件 [已发布] - - 导航侧边栏 - 基于Material SideNav组件 [已发布] - - 工具栏 - 基于Material Toolbar组件 [已发布] - - 卡片 - 基于Material Card组件 [已发布] - - 面板 - 基于Material Panel组件 [已发布] - - 文件上传 - 基于PrimeNg 文件上传组件 [待启动] - - 表格 [待启动] -- 权限 [待启动] -- 公共业务基类 - - 地址 [已发布] -- 支付操作 - - 支付宝条码支付 [已发布] - -## 更新列表 - -- 2017年7月6日,更新了类型转换操作类,类型转换扩展方法,类型转换单元测试。 -- 2017年7月14日,更新了Json操作类,Json操作单元测试。更新了对象映射操作类,对象映射操作单元测试。 -- 2017年7月18日,更新了Ioc操作类,Ioc扩展方法,Ioc操作单元测试。 -- 2017年7月21日,将Util升级到.net core 2.0 prew。 -- 2017年7月26日,更新了应用程序异常操作类Warning及单元测试。 -- 2017年7月28日,更新了验证操作(Util/Validations目录)及单元测试。 -- 2017年8月1日,更新了枚举操作及单元测试。 -- 2017年8月2日,更新了实体、聚合根基类及单元测试。 -- 2017年8月3日,更新了值对象基类及单元测试。 -- 2017年8月4日,更新了字符串操作及扩展,Lambda表达式操作及扩展,以及相关单元测试。 -- 2017年8月5日,更新了映射操作的集合映射。更新了范围查询条件(Util/Datas/Queries/Criterias)及相关单元测试。 -- 2017年8月6日,更新了分页参数、分页集合、查询参数及相关单元测试。 -- 2017年8月8日,更新了查询对象及相关单元测试。 -- 2017年8月9日,更新了EF实体映射配置基类。更新了EF工作单元基类。 -- 2017年8月10日,更新了数据操作集成测试项目。 -- 2017年8月11日,更新了CodeSmith代码生成模板。 -- 2017年8月12日,更新了树型实体基类及单元测试。 -- 2017年8月14日,更新了持久化对象Po基类,仓储部分实现及相关单元测试。 -- 2017年8月19日,更新了操作审计(Util/Domains/Auditing)。 -- 2017年8月21日,更新了仓储基类,持久化对象存储基类,IQueryable查询扩展。 -- 2017年9月3日,更新了公共业务类型-地址(Address),修改了代码生成模板。 -- 2017年9月10日,更新了时间操作类,修改了代码生成模板。 -- 2017年9月15日,更新了上下文操作(Util/Contexts),更新了日期格式化扩展(Util/Extensions.DateTime)。 -- 2017年9月19日,更新了日志操作,日志提供程序支持NLog与Exceptionless。 -- 2017年9月20日,更新了日志操作拦截器(Util.Logs/Aspects)。 -- 2017年9月21日,更新了验证操作拦截器(Util/Validations/Aspects)。 -- 2017年9月27日,更新了工作单元服务(Util/Datas.UnitOfWorks),工作单元拦截器(Util.Applications/Aspects),Crud服务(Util.Applications)。 -- 2017年9月30日,更新了事件总线(Util/Events),类型查找器(Util/Reflections),支持了IOC扫描注册功能。 -- 2017年10月21日,更新了Ui组件基类及TagHelper基类,图标组件(Util.Ui.Angular/Material/Icons)及单元测试。 -- 2017年10月25日,更新了二维码操作(Util.Tools.QrCode,基于QRCoder)和短信操作(Util.Tools.Sms,封装了LuoSiMao接口)。 -- 2017年10月29日,更新了加密操作(Util.Helpers.Encrypt,集成了MD5,DES,AES,RSA加密操作)及单元测试。 -- 2017年10月30日,更新了Url参数生成器(Util.Parameters)及单元测试。 -- 2017年10月31日,更新了支付宝条码支付操作(Util.Biz.Payments/Alipay)及单元测试。 -- 2017年11月15日,更新了EF Core调试日志(Util.Datas/Logs)及单元测试。 -- 2018年1月2日,更新了下拉列表组件(Util.Ui.Angular/Material/Forms)及单元测试。 -- 2018年1月10日,更新了文本框组件(Util.Ui.Angular/Material/Forms)及单元测试。 -- 2018年1月12日,更新了表单组件(Util.Ui.Angular/Material/Forms)及单元测试。 -- 2018年1月14日,更新了按钮和链接组件(Util.Ui.Angular/Material/Buttons)及单元测试。 -- 2018年1月16日,更新了复选框组件(Util.Ui.Angular/Material/Forms)及单元测试。 -- 2018年1月17日,更新了滑动开关组件(Util.Ui.Angular/Material/Forms)及单元测试。 -- 2018年1月18日,更新了单选框组件(Util.Ui.Angular/Material/Forms)及单元测试。 -- 2018年2月18日,更新了菜单组件(Util.Ui.Angular/Material/Menus)及单元测试。 -- 2018年2月22日,更新了选项卡组件(Util.Ui.Angular/Material/Tabs)及单元测试。 -- 2018年2月25日,更新了导航侧边栏组件(Util.Ui.Angular/Material/SideNavs)及工具栏组件(Util.Ui.Angular/Material/Toolbars)。 -- 2018年2月26日,更新了卡片组件(Util.Ui.Angular/Material/Cards)。 -- 2018年2月27日,更新了面板组件(Util.Ui.Angular/Material/Panels)。 \ No newline at end of file +- [ABP](https://abp.io/) + +- [NopCommerce](https://www.nopcommerce.com) + +- [Csla](https://cslanet.com/) + +## Util应用框架相关资源 + +### Util教程目录 + +- 知乎: + +- 掘金: + +### Github项目地址 + +- **Util** + + 该项目包含Util应用框架全部源码。 +- **Util.Generator** + + 该项目提供Util代码生成模板,帮助你迅速创建业务项目基架。 +- **util-angular** + + 该项目是对angular,ng zorro,ng alain前端框架的二次封装Js库,与Util.Ui.NgZorro类库配合使用。 +- **Util.Platform.Single** + + 使用新的应用框架通常具有高昂的成本,为帮助你减轻初始负担,该项目提供一些常用业务功能,你可以将它作为项目起点。 + + 目前提供了权限管理模块,可以控制到菜单和按钮,并能基于角色资源对API进行访问控制,后续将不断完善其它常用功能。 + + 该项目提供单体和微服务两个版本,Util.Platform.Single是单体版本。 +- **Util.Platform.Dapr** + + 它是Util Platform的微服务版本,采用Dapr微服务框架,项目结构参考自eShopOnDapr。 +- **Util.Platform.Share** + + 包含Util.Platform.Single和Util.Platform.Dapr的共享代码,并发布到Nuget,供两个版本使用。 + +### Gitee项目地址 + +**由于国内访问Github非常缓慢,现在Util所有项目发布时会在Gitee进行同步更新。** + +- **Util** +- **Util.Generator** +- **util-angular** +- **Util.Platform.Single** +- **Util.Platform.Dapr** +- **Util.Platform.Share** + +### Nuget包 + +| 包名 | 版本 | 下载量 +|--------------| ------- | ---- +| Util.Core | ![](https://img.shields.io/nuget/v/Util.Core.svg) | ![](https://img.shields.io/nuget/dt/Util.Core.svg) +| Util.ObjectMapping.AutoMapper | ![](https://img.shields.io/nuget/v/Util.ObjectMapping.AutoMapper.svg) | ![](https://img.shields.io/nuget/dt/Util.ObjectMapping.AutoMapper.svg) +| Util.Aop.AspectCore | ![](https://img.shields.io/nuget/v/Util.Aop.AspectCore.svg) | ![](https://img.shields.io/nuget/dt/Util.Aop.AspectCore.svg) +| Util.Validation | ![](https://img.shields.io/nuget/v/Util.Validation.svg) | ![](https://img.shields.io/nuget/dt/Util.Validation.svg) +| Util.Security | ![](https://img.shields.io/nuget/v/Util.Security.svg) | ![](https://img.shields.io/nuget/dt/Util.Security.svg) +| Util.Data.Abstractions | ![](https://img.shields.io/nuget/v/Util.Data.Abstractions.svg) | ![](https://img.shields.io/nuget/dt/Util.Data.Abstractions.svg) +| Util.AspNetCore | ![](https://img.shields.io/nuget/v/Util.AspNetCore.svg) | ![](https://img.shields.io/nuget/dt/Util.AspNetCore.svg) +| Util.Localization | ![](https://img.shields.io/nuget/v/Util.Localization.svg) | ![](https://img.shields.io/nuget/dt/Util.Localization.svg) +| Util.Logging | ![](https://img.shields.io/nuget/v/Util.Logging.svg) | ![](https://img.shields.io/nuget/dt/Util.Logging.svg) +| Util.Logging.Serilog | ![](https://img.shields.io/nuget/v/Util.Logging.Serilog.svg) | ![](https://img.shields.io/nuget/dt/Util.Logging.Serilog.svg) +| Util.Logging.Serilog.Exceptionless | ![](https://img.shields.io/nuget/v/Util.Logging.Serilog.Exceptionless.svg) | ![](https://img.shields.io/nuget/dt/Util.Logging.Serilog.Exceptionless.svg) +| Util.Events.Abstractions | ![](https://img.shields.io/nuget/v/Util.Events.Abstractions.svg) | ![](https://img.shields.io/nuget/dt/Util.Events.Abstractions.svg) +| Util.Events | ![](https://img.shields.io/nuget/v/Util.Events.svg) | ![](https://img.shields.io/nuget/dt/Util.Events.svg) +| Util.Events.MediatR | ![](https://img.shields.io/nuget/v/Util.Events.MediatR.svg) | ![](https://img.shields.io/nuget/dt/Util.Events.MediatR.svg) +| Util.Templates | ![](https://img.shields.io/nuget/v/Util.Templates.svg) | ![](https://img.shields.io/nuget/dt/Util.Templates.svg) +| Util.Templates.Razor | ![](https://img.shields.io/nuget/v/Util.Templates.Razor.svg) | ![](https://img.shields.io/nuget/dt/Util.Templates.Razor.svg) +| Util.Templates.Handlebars | ![](https://img.shields.io/nuget/v/Util.Templates.Handlebars.svg) | ![](https://img.shields.io/nuget/dt/Util.Templates.Handlebars.svg) +| Util.Caching | ![](https://img.shields.io/nuget/v/Util.Caching.svg) | ![](https://img.shields.io/nuget/dt/Util.Caching.svg) +| Util.Caching.EasyCaching | ![](https://img.shields.io/nuget/v/Util.Caching.EasyCaching.svg) | ![](https://img.shields.io/nuget/dt/Util.Caching.EasyCaching.svg) +| Util.Scheduling | ![](https://img.shields.io/nuget/v/Util.Scheduling.svg) | ![](https://img.shields.io/nuget/dt/Util.Scheduling.svg) +| Util.Scheduling.Quartz | ![](https://img.shields.io/nuget/v/Util.Scheduling.Quartz.svg) | ![](https://img.shields.io/nuget/dt/Util.Scheduling.Quartz.svg) +| Util.Scheduling.Hangfire | ![](https://img.shields.io/nuget/v/Util.Scheduling.Hangfire.svg) | ![](https://img.shields.io/nuget/dt/Util.Scheduling.Hangfire.svg) +| Util.Images.ImageSharp | ![](https://img.shields.io/nuget/v/Util.Images.ImageSharp.svg) | ![](https://img.shields.io/nuget/dt/Util.Images.ImageSharp.svg) +| Util.Images.Avatar | ![](https://img.shields.io/nuget/v/Util.Images.Avatar.svg) | ![](https://img.shields.io/nuget/dt/Util.Images.Avatar.svg) +| Util.FileStorage.Abstractions | ![](https://img.shields.io/nuget/v/Util.FileStorage.Abstractions.svg) | ![](https://img.shields.io/nuget/dt/Util.FileStorage.Abstractions.svg) +| Util.FileStorage | ![](https://img.shields.io/nuget/v/Util.FileStorage.svg) | ![](https://img.shields.io/nuget/dt/Util.FileStorage.svg) +| Util.FileStorage.Minio | ![](https://img.shields.io/nuget/v/Util.FileStorage.Minio.svg) | ![](https://img.shields.io/nuget/dt/Util.FileStorage.Minio.svg) +| Util.FileStorage.Aliyun | ![](https://img.shields.io/nuget/v/Util.FileStorage.Aliyun.svg) | ![](https://img.shields.io/nuget/dt/Util.FileStorage.Aliyun.svg) +| Util.FileStorage.All | ![](https://img.shields.io/nuget/v/Util.FileStorage.All.svg) | ![](https://img.shields.io/nuget/dt/Util.FileStorage.All.svg) +| Util.Tenants.Abstractions | ![](https://img.shields.io/nuget/v/Util.Tenants.Abstractions.svg) | ![](https://img.shields.io/nuget/dt/Util.Tenants.Abstractions.svg) +| Util.Tenants | ![](https://img.shields.io/nuget/v/Util.Tenants.svg) | ![](https://img.shields.io/nuget/dt/Util.Tenants.svg) +| Util.Domain | ![](https://img.shields.io/nuget/v/Util.Domain.svg) | ![](https://img.shields.io/nuget/dt/Util.Domain.svg) +| Util.Domain.Biz | ![](https://img.shields.io/nuget/v/Util.Domain.Biz.svg) | ![](https://img.shields.io/nuget/dt/Util.Domain.Biz.svg) +| Util.Data.Core | ![](https://img.shields.io/nuget/v/Util.Data.Core.svg) | ![](https://img.shields.io/nuget/dt/Util.Data.Core.svg) +| Util.Data.Sql | ![](https://img.shields.io/nuget/v/Util.Data.Sql.svg) | ![](https://img.shields.io/nuget/dt/Util.Data.Sql.svg) +| Util.Data.Metadata | ![](https://img.shields.io/nuget/v/Util.Data.Metadata.svg) | ![](https://img.shields.io/nuget/dt/Util.Data.Metadata.svg) +| Util.Data.EntityFrameworkCore | ![](https://img.shields.io/nuget/v/Util.Data.EntityFrameworkCore.svg) | ![](https://img.shields.io/nuget/dt/Util.Data.EntityFrameworkCore.svg) +| Util.Data.EntityFrameworkCore.SqlServer | ![](https://img.shields.io/nuget/v/Util.Data.EntityFrameworkCore.SqlServer.svg) | ![](https://img.shields.io/nuget/dt/Util.Data.EntityFrameworkCore.SqlServer.svg) +| Util.Data.EntityFrameworkCore.PostgreSql | ![](https://img.shields.io/nuget/v/Util.Data.EntityFrameworkCore.PostgreSql.svg) | ![](https://img.shields.io/nuget/dt/Util.Data.EntityFrameworkCore.PostgreSql.svg) +| Util.Data.EntityFrameworkCore.MySql | ![](https://img.shields.io/nuget/v/Util.Data.EntityFrameworkCore.MySql.svg) | ![](https://img.shields.io/nuget/dt/Util.Data.EntityFrameworkCore.MySql.svg) +| Util.Data.EntityFrameworkCore.Sqlite | ![](https://img.shields.io/nuget/v/Util.Data.EntityFrameworkCore.Sqlite.svg) | ![](https://img.shields.io/nuget/dt/Util.Data.EntityFrameworkCore.Sqlite.svg) +| Util.Data.EntityFrameworkCore.Oracle | ![](https://img.shields.io/nuget/v/Util.Data.EntityFrameworkCore.Oracle.svg) | ![](https://img.shields.io/nuget/dt/Util.Data.EntityFrameworkCore.Oracle.svg) +| Util.Data.Dapper.Core | ![](https://img.shields.io/nuget/v/Util.Data.Dapper.Core.svg) | ![](https://img.shields.io/nuget/dt/Util.Data.Dapper.Core.svg) +| Util.Data.Dapper.SqlServer | ![](https://img.shields.io/nuget/v/Util.Data.Dapper.SqlServer.svg) | ![](https://img.shields.io/nuget/dt/Util.Data.Dapper.SqlServer.svg) +| Util.Data.Dapper.PostgreSql | ![](https://img.shields.io/nuget/v/Util.Data.Dapper.PostgreSql.svg) | ![](https://img.shields.io/nuget/dt/Util.Data.Dapper.PostgreSql.svg) +| Util.Data.Dapper.MySql | ![](https://img.shields.io/nuget/v/Util.Data.Dapper.MySql.svg) | ![](https://img.shields.io/nuget/dt/Util.Data.Dapper.MySql.svg) +| Util.Data.Dapper.All | ![](https://img.shields.io/nuget/v/Util.Data.Dapper.All.svg) | ![](https://img.shields.io/nuget/dt/Util.Data.Dapper.All.svg) +| Util.Application | ![](https://img.shields.io/nuget/v/Util.Application.svg) | ![](https://img.shields.io/nuget/dt/Util.Application.svg) +| Util.Application.EntityFrameworkCore | ![](https://img.shields.io/nuget/v/Util.Application.EntityFrameworkCore.svg) | ![](https://img.shields.io/nuget/dt/Util.Application.EntityFrameworkCore.svg) +| Util.Application.WebApi | ![](https://img.shields.io/nuget/v/Util.Application.WebApi.svg) | ![](https://img.shields.io/nuget/dt/Util.Application.WebApi.svg) +| Util.Microservices | ![](https://img.shields.io/nuget/v/Util.Microservices.svg) | ![](https://img.shields.io/nuget/dt/Util.Microservices.svg) +| Util.Microservices.Polly | ![](https://img.shields.io/nuget/v/Util.Microservices.Polly.svg) | ![](https://img.shields.io/nuget/dt/Util.Microservices.Polly.svg) +| Util.Microservices.Dapr | ![](https://img.shields.io/nuget/v/Util.Microservices.Dapr.svg) | ![](https://img.shields.io/nuget/dt/Util.Microservices.Dapr.svg) +| Util.Microservices.HealthChecks | ![](https://img.shields.io/nuget/v/Util.Microservices.HealthChecks.svg) | ![](https://img.shields.io/nuget/dt/Util.Microservices.HealthChecks.svg) +| Util.Ui | ![](https://img.shields.io/nuget/v/Util.Ui.svg) | ![](https://img.shields.io/nuget/dt/Util.Ui.svg) +| Util.Ui.Angular | ![](https://img.shields.io/nuget/v/Util.Ui.Angular.svg) | ![](https://img.shields.io/nuget/dt/Util.Ui.Angular.svg) +| Util.Ui.NgZorro | ![](https://img.shields.io/nuget/v/Util.Ui.NgZorro.svg) | ![](https://img.shields.io/nuget/dt/Util.Ui.NgZorro.svg) +| Util.Ui.NgAlain | ![](https://img.shields.io/nuget/v/Util.Ui.NgAlain.svg) | ![](https://img.shields.io/nuget/dt/Util.Ui.NgAlain.svg) +| Util.Generators | ![](https://img.shields.io/nuget/v/Util.Generators.svg) | ![](https://img.shields.io/nuget/dt/Util.Generators.svg) +| Util.Generators.Razor | ![](https://img.shields.io/nuget/v/Util.Generators.Razor.svg) | ![](https://img.shields.io/nuget/dt/Util.Generators.Razor.svg) + +### Npm包 + +**util-angular** +[![npm package](https://img.shields.io/npm/v/util-angular.svg?style=flat-square)](https://www.npmjs.org/package/util-angular) +[![NPM downloads](http://img.shields.io/npm/dm/util-angular.svg?style=flat-square)](https://npmjs.org/package/util-angular) \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Api/Customers/CustomersController.cs b/sample/Util.Samples.Webs/Api/Customers/CustomersController.cs deleted file mode 100644 index 0ca2c68a6..000000000 --- a/sample/Util.Samples.Webs/Api/Customers/CustomersController.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Generic; -using Donau.Services.Abstractions.Customers; -using Donau.Services.Dtos.Customers; -using Donau.Services.Queries.Customers; -using Microsoft.AspNetCore.Mvc; -using Util.Webs.Controllers; - -namespace Util.Samples.Webs.Api.Customers { - public class CustomersController : CrudControllerBase { - public CustomersController(ICustomersService customersService) : base(customersService) { - } - - [HttpGet("test")] - public IActionResult Test() { - List items = new List { - new Item( "B","1",2,"Group1" ), - new Item( "A","2",1,"Group2" ), - new Item( "D","3",4,"Group1" ), - new Item( "C","4",3,"Group2" ), - }; - return Success( items ); - } - } -} diff --git a/sample/Util.Samples.Webs/Areas/Logs/Controllers/LogControllerBase.cs b/sample/Util.Samples.Webs/Areas/Logs/Controllers/LogControllerBase.cs deleted file mode 100644 index 90a989b8e..000000000 --- a/sample/Util.Samples.Webs/Areas/Logs/Controllers/LogControllerBase.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace Util.Samples.Webs.Areas.Logs.Controllers { - /// - /// 日志控制器 - /// - [Area( "Logs" )] - public abstract class LogControllerBase : Controller { - } -} diff --git a/sample/Util.Samples.Webs/Areas/Logs/Controllers/NLog1Controller.cs b/sample/Util.Samples.Webs/Areas/Logs/Controllers/NLog1Controller.cs deleted file mode 100644 index 99baddfd9..000000000 --- a/sample/Util.Samples.Webs/Areas/Logs/Controllers/NLog1Controller.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Util.Logs; -using Util.Logs.Extensions; - -namespace Util.Samples.Webs.Areas.Logs.Controllers { - /// - /// NLog日志控制器 - /// - public class NLog1Controller : LogControllerBase { - /// - /// 初始化NLog日志控制器 - /// - /// 日志操作 - public NLog1Controller( ILog log ) { - Log = log; - } - - /// - /// 日志操作 - /// - public ILog Log { get; set; } - - /// - /// 首页 - /// - public IActionResult Index() { - return View(); - } - - /// - /// 写异步日志 - /// - public async Task LogAsync() { - await WriteLog(); - await WriteLog(); - await WriteLog(); - await WriteLog(); - return Content( "写入成功,请查看c:/log/application/debug目录" ); - } - - /// - /// 写日志 - /// - private Task WriteLog() { - return Task.Run( () => Log.Debug( "测试异步写入日志" ) ); - } - - /// - /// 写入业务日志 - /// - public IActionResult Info() { - Log.BusinessId( Guid.NewGuid().ToString() ) - .Module( "订单" ) - .Method( "PlaceOrder" ) - .Caption( "有人下单了" ) - .Params( "int","a","1" ) - .Params( "string", "b", "c" ) - .Content( $"购买商品数量: {100}" ) - .Content( $"购买商品总额:{200}" ) - .Sql( "select * from system.users" ) - .Sql( "select * from system.roles" ) - .SqlParams( $"@a={1},@b={2}" ) - .SqlParams( $"@userId={Guid.NewGuid().ToString()}" ) - .Info(); - return Content( "写入成功,请查看c:/log/application/info目录" ); - } - - /// - /// 写入错误日志 - /// - public IActionResult Error() { - try { - Throw(); - } - catch ( Exception ex ) { - Log.BusinessId( Guid.NewGuid().ToString() ) - .Exception( ex, "100" ) - .Error(); - } - return Content( "写入成功,请查看c:/log/application/error目录" ); - } - - /// - /// 抛出异常 - /// - private void Throw() { - throw new NotImplementedException( "我还没有实现" ); - } - } -} diff --git a/sample/Util.Samples.Webs/Areas/Logs/Views/NLog1/Index.cshtml b/sample/Util.Samples.Webs/Areas/Logs/Views/NLog1/Index.cshtml deleted file mode 100644 index 0d0b4271d..000000000 --- a/sample/Util.Samples.Webs/Areas/Logs/Views/NLog1/Index.cshtml +++ /dev/null @@ -1,7 +0,0 @@ -@{ - Layout = "_Layout"; -} - -测试异步方法 -写业务日志 -写错误日志 diff --git a/sample/Util.Samples.Webs/Configs/ServiceRegister.cs b/sample/Util.Samples.Webs/Configs/ServiceRegister.cs deleted file mode 100644 index d662ecffd..000000000 --- a/sample/Util.Samples.Webs/Configs/ServiceRegister.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Util.Datas.Tests.Samples.Datas.SqlServer.Repositories; -using Util.Datas.Tests.Samples.Domains.Repositories; -using Util.Dependency; -using Util.Domains.Sessions; -using Util.Files; -using Util.Files.Paths; -using Util.Randoms; - -namespace Util.Samples.Webs.Configs { - /// - /// 服务注册 - /// - public class ServiceRegister : IDependencyRegistrar { - /// - /// 注册依赖 - /// - public void Regist( IServiceCollection services ) { - services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - } - } -} diff --git a/sample/Util.Samples.Webs/Configs/UploadBasePath.cs b/sample/Util.Samples.Webs/Configs/UploadBasePath.cs deleted file mode 100644 index a00f16690..000000000 --- a/sample/Util.Samples.Webs/Configs/UploadBasePath.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Util.Files.Paths; - -namespace Util.Samples.Webs.Configs { - /// - /// 上传基路径 - /// - public class UploadBasePath : IBasePath { - /// - /// 获取基路径 - /// - public string GetPath() { - return "upload"; - } - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Controllers/HomeController.cs b/sample/Util.Samples.Webs/Controllers/HomeController.cs deleted file mode 100644 index a57ee5eaa..000000000 --- a/sample/Util.Samples.Webs/Controllers/HomeController.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Donau.Services.Dtos.Customers; -using Microsoft.AspNetCore.Mvc; - -namespace Util.Samples.Webs.Controllers { - - public class HomeController : Controller { - - public IActionResult Index() { - return View(); - } - - public IActionResult Main() { - return View(); - } - - public IActionResult Demo() { - return View(new CustomersDto()); - } - } -} diff --git a/sample/Util.Samples.Webs/Program.cs b/sample/Util.Samples.Webs/Program.cs deleted file mode 100644 index 4dc3f17d1..000000000 --- a/sample/Util.Samples.Webs/Program.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Util.Logs; -using Util.Logs.Extensions; - -namespace Util.Samples.Webs { - /// - /// 应用程序 - /// - public class Program { - /// - /// 应用程序入口点 - /// - /// 入口点参数 - public static void Main( string[] args ) { - try { - WebHost.CreateDefaultBuilder( args ) - .UseStartup() - .Build() - .Run(); - } - catch ( Exception ex ) { - WriteLog( ex ); - } - } - - /// - /// 记录错误日志 - /// - private static void WriteLog( Exception ex ) { - ex.Log( Log.GetLog().Caption( "应用程序启动失败" ) ); - } - } -} diff --git a/sample/Util.Samples.Webs/Properties/PublishProfiles/FolderProfile.pubxml b/sample/Util.Samples.Webs/Properties/PublishProfiles/FolderProfile.pubxml deleted file mode 100644 index 8b30094e1..000000000 --- a/sample/Util.Samples.Webs/Properties/PublishProfiles/FolderProfile.pubxml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - FileSystem - FileSystem - Release - Any CPU - - True - False - 323b64b7-5ce0-4222-b9ed-9f418730897c - D:\Github\Util\output\publish - True - - \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Properties/PublishProfiles/FolderProfile.pubxml.user b/sample/Util.Samples.Webs/Properties/PublishProfiles/FolderProfile.pubxml.user deleted file mode 100644 index 44823a679..000000000 --- a/sample/Util.Samples.Webs/Properties/PublishProfiles/FolderProfile.pubxml.user +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - <_PublishTargetUrl>D:\Github\Util\output\publish - - \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Properties/launchSettings.json b/sample/Util.Samples.Webs/Properties/launchSettings.json deleted file mode 100644 index 5fe7fa320..000000000 --- a/sample/Util.Samples.Webs/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:5200/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Util.Samples.Webs": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:5201/" - } - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Services/Abstractions/Customers/ICustomersService.cs b/sample/Util.Samples.Webs/Services/Abstractions/Customers/ICustomersService.cs deleted file mode 100644 index 36dcaaaa3..000000000 --- a/sample/Util.Samples.Webs/Services/Abstractions/Customers/ICustomersService.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Util.Applications; -using Donau.Services.Queries.Customers; -using Donau.Services.Dtos.Customers; - -namespace Donau.Services.Abstractions.Customers { - /// - /// 客户服务 - /// - public interface ICustomersService : ICrudService { - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Services/Core/Customers/CustomersService.cs b/sample/Util.Samples.Webs/Services/Core/Customers/CustomersService.cs deleted file mode 100644 index 8c4d1f264..000000000 --- a/sample/Util.Samples.Webs/Services/Core/Customers/CustomersService.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Util; -using Util.Maps; -using Util.Domains.Repositories; -using Util.Datas.Queries; -using Util.Applications; -using Donau.Services.Dtos.Customers; -using Donau.Services.Queries.Customers; -using Donau.Services.Abstractions.Customers; -using Util.Datas.Tests.Samples.Datas.SqlServer.UnitOfWorks; -using Util.Datas.Tests.Samples.Domains.Models; -using Util.Datas.Tests.Samples.Domains.Repositories; - -namespace Donau.Services.Core.Customers { - /// - /// 客户服务 - /// - public class CustomersService : CrudServiceBase, ICustomersService { - /// - /// 初始化客户服务 - /// - /// 工作单元 - /// 客户仓储 - public CustomersService( ISqlServerUnitOfWork unitOfWork, ICustomerRepository customersRepository ) - : base( unitOfWork, customersRepository ) { - CustomersRepository = customersRepository; - } - - /// - /// 客户仓储 - /// - public ICustomerRepository CustomersRepository { get; set; } - - /// - /// 转换为数据传输对象 - /// - /// 实体 - protected override CustomersDto ToDto( Customer entity ) { - return entity.MapTo(); - } - - /// - /// 转换为实体 - /// - /// 数据传输对象 - protected override Customer ToEntity( CustomersDto dto ) { - return dto.MapTo( new Customer( dto.Id ) ); - } - - protected override bool IsTracking => true; - - - /// - /// 创建查询对象 - /// - /// 客户查询实体 - protected override IQueryBase CreateQuery( CustomersQuery param ) { - return new Query( param ) - .WhereIfNotEmpty(t => t.Name.Contains(param.Name)) - .WhereIfNotEmpty( t => t.Nickname.Contains(param.Nickname)) - .WhereIfNotEmpty( t => t.Mobile.Contains(param.Mobile)); - } - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Services/Dtos/Customers/CustomersDto.cs b/sample/Util.Samples.Webs/Services/Dtos/Customers/CustomersDto.cs deleted file mode 100644 index 551c50fa9..000000000 --- a/sample/Util.Samples.Webs/Services/Dtos/Customers/CustomersDto.cs +++ /dev/null @@ -1,157 +0,0 @@ -using System; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.Runtime.Serialization; -using Util.Applications.Dtos; -using Util.Biz.Enums; -using Util.Ui.Attributes; -using Util.Ui.Enums; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Menus.Datas; - -namespace Donau.Services.Dtos.Customers { - /// - /// 客户数据传输对象 - /// - [DataContract] - [Model("model")] - public class CustomersDto : DtoBase { - /// - /// 客户名称 - /// - [Required(ErrorMessage = "客户名称不能为空")] - [MaxLength(20)] - [Display( Name = "客户名称" )] - [DataMember] - public string Name { get; set; } - - /// - /// 昵称 - /// - [StringLength( 30, ErrorMessage = "昵称输入过长,不能超过30位",MinimumLength = 3)] - [Display( Name = "昵称" )] - [DataMember] - public string Nickname { get; set; } - - /// - /// 余额 - /// - [DataMember] - [StringLength( 6, ErrorMessage = "昵称输入过长,不能超过30位" )] - [Display( Name = "余额" )] - public double Balance { get; set; } - - /// - /// 性别 - /// - [Display( Name = "性别" )] - [DataMember] - public Gender Gender { get; set; } - - - /// - /// 性别 - /// - [Display( Name = "性别" )] - [DataMember] - public bool IsGender { get; set; } - - /// - /// 年龄 - /// - [Display( Name = "年龄" )] - [Required(ErrorMessage = "年龄不能为空")] - [DataMember] - public double Age { get; set; } - - /// - /// 民族 - /// - [Display( Name = "民族" )] - [Required(ErrorMessage = "必须选择一个民族" )] - [DataMember] - public Nation Nation { get; set; } - - /// - /// 联系电话 - /// - [StringLength( 20, ErrorMessage = "联系电话输入过长,不能超过20位" )] - [Display( Name = "联系电话" )] - [DataMember] - public string Tel { get; set; } - - /// - /// 手机号 - /// - [StringLength( 20, ErrorMessage = "手机号输入过长,不能超过20位" )] - [Display( Name = "手机号" )] - [DataMember] - public string Mobile { get; set; } - - /// - /// 电子邮件 - /// - [StringLength( 100, ErrorMessage = "电子邮件输入过长,不能超过100位" )] - [Display( Name = "电子邮件" )] - [DataMember] - [EmailAddress(ErrorMessage = "你的电子邮件是错的")] - public string Email { get; set; } - - /// - /// 创建时间 - /// - [Display( Name = "创建时间" )] - [DataMember] - public DateTime? CreationTime { get; set; } - - /// - /// 创建人 - /// - [Display( Name = "创建人" )] - [DataMember] - public Guid? CreatorId { get; set; } - - /// - /// 最后修改时间 - /// - [Display( Name = "最后修改时间" )] - [DataMember] - public DateTime? LastModificationTime { get; set; } - - /// - /// 最后修改人 - /// - [Display( Name = "最后修改人" )] - [DataMember] - public Guid? LastModifierId { get; set; } - - /// - /// 版本号 - /// - [Display( Name = "版本号" )] - [DataMember] - public Byte[] Version { get; set; } - - public MenuNodeCollection GetMenus() { - var result = new MenuNodeCollection(); - result.Overlap = false; - result.Nodes.Add( new MenuNode{Label = "A", MaterialIcon = MaterialIcon.Block,OnClick = "onChange()"} ); - result.Nodes.Add( new MenuNode { Id = "2", Label = "B", MaterialIcon = MaterialIcon.Android,Link = "/searchmenu" } ); - result.Nodes.Add( new MenuNode { Id = "3", Label = "C", MaterialIcon = MaterialIcon.Bluetooth, OnClick = "onChange()" } ); - result.Nodes.Add( new MenuNode { ParentId = "2", Label = "D", FontAwesomeIcon = FontAwesomeIcon.Apple, OnClick = "onChange()", Disabled="true" } ); - result.Nodes.Add( new MenuNode { ParentId = "2", Label = "E", FontAwesomeIcon = FontAwesomeIcon.Book, Link = "/searchmenu" } ); - result.Nodes.Add( new MenuNode { Id = "4", ParentId = "2", Label = "F", FontAwesomeIcon = FontAwesomeIcon.Anchor, Link = "/searchmenu" } ); - result.Nodes.Add( new MenuNode { ParentId = "3", Label = "H", FontAwesomeIcon = FontAwesomeIcon.Apple, OnClick = "onChange()", Disabled = "true" } ); - result.Nodes.Add( new MenuNode { ParentId = "3", Label = "I", FontAwesomeIcon = FontAwesomeIcon.Book, Link = "/searchmenu" } ); - result.Nodes.Add( new MenuNode { ParentId = "2", Label = "G", FontAwesomeIcon = FontAwesomeIcon.Anchor, Link = "/searchmenu" } ); - - result.Nodes.Add( new MenuNode { ParentId = "4", Label = "D", FontAwesomeIcon = FontAwesomeIcon.Apple, OnClick = "onChange()", Disabled = "true" } ); - result.Nodes.Add( new MenuNode { ParentId = "4", Label = "E", FontAwesomeIcon = FontAwesomeIcon.Book, Link = "/searchmenu" } ); - result.Nodes.Add( new MenuNode { ParentId = "4", Label = "F", FontAwesomeIcon = FontAwesomeIcon.Anchor, Link = "/searchmenu" } ); - result.Nodes.Add( new MenuNode { ParentId = "4", Label = "H", FontAwesomeIcon = FontAwesomeIcon.Apple, OnClick = "onChange()", Disabled = "true" } ); - result.Nodes.Add( new MenuNode { ParentId = "4", Label = "I", MaterialIcon = MaterialIcon.Android, Link = "/searchmenu" } ); - result.Nodes.Add( new MenuNode { ParentId = "4", Label = "G", FontAwesomeIcon = FontAwesomeIcon.Anchor, Link = "/searchmenu" } ); - return result; - } - } -} diff --git a/sample/Util.Samples.Webs/Services/Extensions/Customers/Extensions.CustomersDto.cs b/sample/Util.Samples.Webs/Services/Extensions/Customers/Extensions.CustomersDto.cs deleted file mode 100644 index d6389c2ea..000000000 --- a/sample/Util.Samples.Webs/Services/Extensions/Customers/Extensions.CustomersDto.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Util; -using Util.Maps; -using Donau.Services.Dtos.Customers; -using Util.Datas.Tests.Samples.Domains.Models; - -namespace Donau.Services.Extensions.Customers { - /// - /// 客户数据传输对象扩展 - /// - public static class CustomersDtoExtension { - /// - /// 转换为客户实体 - /// - /// 客户数据传输对象 - public static Customer ToEntity( this CustomersDto dto ) { - if ( dto == null ) - return new Customer(); - return dto.MapTo( new Customer( dto.Id ) ); - } - - /// - /// 转换为客户数据传输对象 - /// - /// 客户实体 - public static CustomersDto ToDto(this Customer entity) { - if( entity == null ) - return new CustomersDto(); - return entity.MapTo(); - } - - } -} diff --git a/sample/Util.Samples.Webs/Services/Queries/Customers/CustomersQuery.cs b/sample/Util.Samples.Webs/Services/Queries/Customers/CustomersQuery.cs deleted file mode 100644 index f0e7292a6..000000000 --- a/sample/Util.Samples.Webs/Services/Queries/Customers/CustomersQuery.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using Util; -using Util.Datas.Queries; - -namespace Donau.Services.Queries.Customers { - /// - /// 客户查询实体 - /// - public class CustomersQuery : QueryParameter { - - private string _customerId = string.Empty; - /// - /// 客户编号 - /// - [Display(Name="客户编号")] - public string CustomerId { - get => _customerId == null ? string.Empty : _customerId.Trim(); - set => _customerId=value; - } - - private string _name = string.Empty; - /// - /// 客户名称 - /// - [Display(Name="客户名称")] - public string Name { - get => _name == null ? string.Empty : _name.Trim(); - set => _name=value; - } - - private string _nickname = string.Empty; - /// - /// 昵称 - /// - [Display(Name="昵称")] - public string Nickname { - get => _nickname == null ? string.Empty : _nickname.Trim(); - set => _nickname=value; - } - /// - /// 余额 - /// - [Display(Name="余额")] - public decimal? Balance { get; set; } - /// - /// 性别 - /// - [Display(Name="性别")] - public int? Gender { get; set; } - - private string _tel = string.Empty; - /// - /// 联系电话 - /// - [Display(Name="联系电话")] - public string Tel { - get => _tel == null ? string.Empty : _tel.Trim(); - set => _tel=value; - } - - private string _mobile = string.Empty; - /// - /// 手机号 - /// - [Display(Name="手机号")] - public string Mobile { - get => _mobile == null ? string.Empty : _mobile.Trim(); - set => _mobile=value; - } - - private string _email = string.Empty; - /// - /// 电子邮件 - /// - [Display(Name="电子邮件")] - public string Email { - get => _email == null ? string.Empty : _email.Trim(); - set => _email=value; - } - /// - /// 起始创建时间 - /// - [Display( Name = "起始创建时间" )] - public DateTime? BeginCreationTime { get; set; } - /// - /// 结束创建时间 - /// - [Display( Name = "结束创建时间" )] - public DateTime? EndCreationTime { get; set; } - /// - /// 创建人 - /// - [Display(Name="创建人")] - public Guid? CreatorId { get; set; } - /// - /// 起始最后修改时间 - /// - [Display( Name = "起始最后修改时间" )] - public DateTime? BeginLastModificationTime { get; set; } - /// - /// 结束最后修改时间 - /// - [Display( Name = "结束最后修改时间" )] - public DateTime? EndLastModificationTime { get; set; } - /// - /// 最后修改人 - /// - [Display(Name="最后修改人")] - public Guid? LastModifierId { get; set; } - - /// - /// 添加描述 - /// - protected override void AddDescriptions() { - base.AddDescriptions(); - AddDescription( "客户编号", CustomerId ); - AddDescription( "客户名称", Name ); - AddDescription( "昵称", Nickname ); - AddDescription( "余额", Balance ); - AddDescription( "性别", Gender ); - AddDescription( "联系电话", Tel ); - AddDescription( "手机号", Mobile ); - AddDescription( "电子邮件", Email ); - AddDescription( "起始创建时间", BeginCreationTime ); - AddDescription( "结束创建时间", EndCreationTime ); - AddDescription( "创建人", CreatorId ); - AddDescription( "起始最后修改时间", BeginLastModificationTime ); - AddDescription( "结束最后修改时间", EndLastModificationTime ); - AddDescription( "最后修改人", LastModifierId ); - } - } -} diff --git a/sample/Util.Samples.Webs/Startup.cs b/sample/Util.Samples.Webs/Startup.cs deleted file mode 100644 index 1cb8dea30..000000000 --- a/sample/Util.Samples.Webs/Startup.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.SpaServices.Webpack; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Util.Datas.Ef; -using Util.Datas.Tests.Samples.Datas.SqlServer.UnitOfWorks; -using Util.Events.Default; -using Util.Logs.Extensions; -using Util.Webs.Extensions; - -namespace Util.Samples.Webs { - /// - /// 启动配置 - /// - public class Startup { - /// - /// 初始化启动配置 - /// - /// 配置 - public Startup( IConfiguration configuration ) { - Configuration = configuration; - } - - /// - /// 配置 - /// - public IConfiguration Configuration { get; } - - /// - /// 配置服务 - /// - public IServiceProvider ConfigureServices( IServiceCollection services ) { - //添加Mvc服务 - services.AddMvc().AddControllersAsServices(); - - //添加NLog日志操作 - services.AddNLog(); - - //添加事件总线服务 - services.AddEventBus(); - - services.AddUnitOfWork( Configuration.GetConnectionString( "DefaultConnection" ) ); - - //添加Util基础设施服务 - return services.AddUtil(); - } - - /// - /// 配置请求管道 - /// - public void Configure( IApplicationBuilder app, IHostingEnvironment env ) { - if( env.IsDevelopment() == false ) { - ProductionConfig( app ); - return; - } - DevelopmentConfig( app ); - } - - /// - /// 生产环境配置 - /// - private void ProductionConfig( IApplicationBuilder app ) { - app.UseExceptionHandler( "/Home/Error" ); - app.UseAuthentication(); - CommonConfig( app ); - } - - /// - /// 公共配置 - /// - private void CommonConfig( IApplicationBuilder app ) { - app.UseErrorLog(); - app.UseStaticFiles(); - ConfigRoute( app ); - } - - /// - /// 路由配置,支持区域 - /// - private void ConfigRoute( IApplicationBuilder app ) { - app.UseMvc( routes => { - routes.MapRoute( "areaRoute", "{area:exists}/{controller}/{action=Index}/{id?}" ); - routes.MapRoute( "default", "{controller=Home}/{action=Index}/{id?}" ); - routes.MapSpaFallbackRoute( "spa-fallback", new { controller = "Home", action = "Index" } ); - } ); - } - - /// - /// 开发环境配置 - /// - private void DevelopmentConfig( IApplicationBuilder app ) { - app.UseBrowserLink(); - app.UseDeveloperExceptionPage(); - app.UseWebpackDevMiddleware( new WebpackDevMiddlewareOptions { - HotModuleReplacement = true - } ); - CommonConfig( app ); - } - } -} diff --git a/sample/Util.Samples.Webs/Typings/app/app.component.ts b/sample/Util.Samples.Webs/Typings/app/app.component.ts deleted file mode 100644 index 7cf122a53..000000000 --- a/sample/Util.Samples.Webs/Typings/app/app.component.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Component } from "@angular/core" -import {util} from "../util"; - -/** - * 根组件 - */ -@Component({ - selector: 'app', - templateUrl: '/home/main' -}) -export class AppComponent { -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/app/app.module.ts b/sample/Util.Samples.Webs/Typings/app/app.module.ts deleted file mode 100644 index fe9c142a8..000000000 --- a/sample/Util.Samples.Webs/Typings/app/app.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -//Angular模块 -import { NgModule, Injector } from '@angular/core'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; - -//框架模块 -import { FrameworkModule } from './framework.module'; -import { util } from '../util'; - -//根组件 -import { AppComponent } from './app.component'; - -//路由模块 -import { AppRoutingModule } from './app.routing.module'; - -/** - * 应用根模块 - */ -@NgModule({ - declarations: [ - AppComponent - ], - imports: [ - BrowserAnimationsModule,FrameworkModule, AppRoutingModule - ], - bootstrap: [AppComponent] -}) -export class AppModule { - /** - * 初始化应用根模块 - * @param injector 注入器 - */ - constructor(injector: Injector) { - util.ioc.injector = injector; - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/app/app.routing.module.ts b/sample/Util.Samples.Webs/Typings/app/app.routing.module.ts deleted file mode 100644 index ad5e03644..000000000 --- a/sample/Util.Samples.Webs/Typings/app/app.routing.module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { NgModule } from '@angular/core' -import { RouterModule, Routes } from '@angular/router' - -//路由配置 -const routes: Routes = [ - { path: '', redirectTo: 'demo', pathMatch: 'full' }, - { path: 'demo', loadChildren: "./demo/demo.module#DemoModule" }, - { path: '**', redirectTo: '/' } -]; - -/** - * 路由配置模块 - */ -@NgModule({ - imports: [RouterModule.forRoot(routes)] -}) -export class AppRoutingModule { } \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/app/demo/demo.component.ts b/sample/Util.Samples.Webs/Typings/app/demo/demo.component.ts deleted file mode 100644 index 4e5bac5e8..000000000 --- a/sample/Util.Samples.Webs/Typings/app/demo/demo.component.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Component, ViewChild, OnInit, Injector } from "@angular/core" -import { NgForm } from "@angular/forms" -import { ComponentBase, ViewModel, QueryParameter, TableWrapperComponent, util } from "../../util"; -@Component({ - selector: 'demo', - templateUrl: '/Home/Demo' -}) -export class DemoComponent extends ComponentBase implements OnInit { - queryParam: CustomerQueryModel; - - @ViewChild('grid') grid: TableWrapperComponent; - - model: CustomerViewModel; - id:string; - - delete() { - this.grid.delete(); - } - - constructor(injector: Injector) { - super(injector); - this.queryParam = new CustomerQueryModel(); - this.model = new CustomerViewModel(); - } - - query() { - this.grid.query(); - } - - onChange(form: NgForm, event) { - this.util.message.error("a"); - this.util.form.submit({ - form: form, - url: '/api/customers', - data: this.model, - }); - } - - ngOnInit() { - this.model.isGender = true; - } - - show() { - util.message.error("aa"); - } -} - - - - -class CustomerQueryModel extends QueryParameter { - public name: string; -} - -class CustomerViewModel extends ViewModel { - name: string; - value: string; - nation; - hide: boolean; - num: number; - date: Date; - creationTime:Date; - isGender:boolean; -} - - -@Component({ - selector: 'ao', - template: 'This is the routed body of the sunny tab.1111111111', -}) -export class A1 { } - -@Component({ - selector: 'bo', - template: 'This is the routed body of the sunny tab.2222222222', -}) -export class B1 { } -@Component({ - selector: 'co', - template: 'This is the routed body of the sunny tab.333333333333', -}) -export class C1 { } - -@Component({ - selector: 'do', - template: 'This is the routed body of the sunny tab.444444444444', -}) -export class D1 { } \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/app/demo/demo.module.ts b/sample/Util.Samples.Webs/Typings/app/demo/demo.module.ts deleted file mode 100644 index 1fcb94624..000000000 --- a/sample/Util.Samples.Webs/Typings/app/demo/demo.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { NgModule } from '@angular/core'; -import { FrameworkModule } from "../framework.module"; -import { DemoRoutingModule } from "./demo.routing.module"; -import { DemoComponent, A1, B1, C1, D1 } from "./demo.component"; - -@NgModule({ - declarations: [ - DemoComponent, A1, B1, C1, D1 - ], - imports: [ - FrameworkModule,DemoRoutingModule - ], - exports: [ - DemoComponent, A1, B1, C1, D1 - ] -}) -export class DemoModule { -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/app/demo/demo.routing.module.ts b/sample/Util.Samples.Webs/Typings/app/demo/demo.routing.module.ts deleted file mode 100644 index 0750a0939..000000000 --- a/sample/Util.Samples.Webs/Typings/app/demo/demo.routing.module.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { NgModule } from '@angular/core' -import { RouterModule, Routes } from '@angular/router' -import { DemoComponent, A1, B1,C1,D1 } from "./demo.component"; - -//路由配置 -const routes: Routes = [ - { path: '', component: DemoComponent, children: [{ path: 'a', component: A1 }, { path: 'b', component: B1 }, { path: 'c', component: C1 }, { path: 'd', component: D1 }] } -]; - -/** - * 路由配置模块 - */ -@NgModule({ - imports: [RouterModule.forChild(routes)] -}) -export class DemoRoutingModule { } \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/app/framework.module.ts b/sample/Util.Samples.Webs/Typings/app/framework.module.ts deleted file mode 100644 index c90ab4e5a..000000000 --- a/sample/Util.Samples.Webs/Typings/app/framework.module.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { NgModule } from '@angular/core'; -//Util模块 -import { UtilModule } from '../util'; -//Material模块 -import { - MatButtonToggleModule, - MatChipsModule, - MatDialogModule, - MatListModule, - MatSliderModule, - MatStepperModule -} from '@angular/material'; -@NgModule({ - exports: [ - UtilModule, - MatButtonToggleModule, - MatChipsModule, - MatDialogModule, - MatListModule, - MatSliderModule, - MatStepperModule - ] -}) -export class FrameworkModule { -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/main.ts b/sample/Util.Samples.Webs/Typings/main.ts deleted file mode 100644 index 05ea8fb35..000000000 --- a/sample/Util.Samples.Webs/Typings/main.ts +++ /dev/null @@ -1,14 +0,0 @@ -import 'reflect-metadata'; -import 'zone.js'; -import 'es6-shim'; -import 'hammerjs'; -import { enableProdMode } from '@angular/core'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { AppModule } from "./app/app.module"; - -if (module.hot) { - module.hot.accept(); -} else { - enableProdMode(); -} -platformBrowserDynamic().bootstrapModule(AppModule).catch(ex => console.log(ex)); \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/test/karma-main.ts b/sample/Util.Samples.Webs/Typings/test/karma-main.ts deleted file mode 100644 index f5e37a4c0..000000000 --- a/sample/Util.Samples.Webs/Typings/test/karma-main.ts +++ /dev/null @@ -1,25 +0,0 @@ -import 'babel-polyfill'; -import 'reflect-metadata'; -import 'zone.js'; -import 'zone.js/dist/long-stack-trace-zone'; -import 'zone.js/dist/proxy.js'; -import 'zone.js/dist/sync-test'; -import 'zone.js/dist/jasmine-patch'; -import 'zone.js/dist/async-test'; -import 'zone.js/dist/fake-async-test'; -import * as testing from '@angular/core/testing'; -import * as testingBrowser from '@angular/platform-browser-dynamic/testing'; - -declare var __karma__: any; -declare var require: any; - -__karma__.loaded = function () {}; - -testing.getTestBed().initTestEnvironment( - testingBrowser.BrowserDynamicTestingModule, - testingBrowser.platformBrowserDynamicTesting() -); - -const context = require.context('../', true, /\.test\.ts$/); -context.keys().map(context); -__karma__.start(); \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/test/karma.conf.js b/sample/Util.Samples.Webs/Typings/test/karma.conf.js deleted file mode 100644 index d463f06e9..000000000 --- a/sample/Util.Samples.Webs/Typings/test/karma.conf.js +++ /dev/null @@ -1,47 +0,0 @@ -const webpack = require('webpack'); - -module.exports = function (config) { - config.set({ - basePath: '.', - frameworks: ['jasmine'], - files: [ - '../../wwwroot/dist/polyfills.js', - '../../wwwroot/dist/vendor.js', - '../../wwwroot/dist/util.js', - './karma-main.ts' - ], - preprocessors: { - './karma-main.ts':['webpack'] - }, - reporters: ['progress'], - port: 9876, - colors: true, - logLevel: config.LOG_INFO, - autoWatch: true, - browsers: ['PhantomJS'], - mime: { 'application/javascript': ['ts'] }, - singleRun: false, - webpack: { - resolve: { - extensions: ['.js', '.ts'] - }, - module: { - rules: [ - { test: /\.ts$/, include: /Typings/, use: ['awesome-typescript-loader?silent=true'] } - ] - }, - plugins: [ - new webpack.DllReferencePlugin({ - manifest: require('../../wwwroot/dist/polyfills-manifest.json') - }), - new webpack.DllReferencePlugin({ - manifest: require('../../wwwroot/dist/vendor-manifest.json') - }), - new webpack.DllReferencePlugin({ - manifest: require('../../wwwroot/dist/util-manifest.json') - }) - ] - }, - webpackMiddleware: { stats: 'errors-only' } - }); -} diff --git a/sample/Util.Samples.Webs/Typings/test/util/common/helper.test.ts b/sample/Util.Samples.Webs/Typings/test/util/common/helper.test.ts deleted file mode 100644 index 4d9ab7d91..000000000 --- a/sample/Util.Samples.Webs/Typings/test/util/common/helper.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -//============== Util公共操作测试================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { util } from "../../../util" - -describe("util.helper", () => { - it("isEmpty", () => { - expect(util.helper.isEmpty(undefined)).toBeTruthy("undefined"); - expect(util.helper.isEmpty(null)).toBeTruthy("null"); - expect(util.helper.isEmpty("")).toBeTruthy("''"); - expect(util.helper.isEmpty(" ")).toBeTruthy("' '"); - expect(util.helper.isEmpty(0)).toBeFalsy("0"); - expect(util.helper.isEmpty("0")).toBeFalsy("'0'"); - expect(util.helper.isEmpty("00000000-0000-0000-0000-000000000000")).toBeTruthy("00000000-0000-0000-0000-000000000000"); - expect(util.helper.isEmpty("4ABCA27E-EAFC-4DEE-B809-8DD2ABDFDA1C")).toBeFalsy("4ABCA27E-EAFC-4DEE-B809-8DD2ABDFDA1C"); - expect(util.helper.isEmpty("6")).toBeFalsy("'6'"); - expect(util.helper.isEmpty(6)).toBeFalsy("6"); - expect(util.helper.isEmpty("a6")).toBeFalsy("a6"); - }); - it("isNumber", () => { - expect(util.helper.isNumber(1)).toBeTruthy("1"); - expect(util.helper.isNumber("1")).toBeTruthy("'1'"); - expect(util.helper.isNumber("")).toBeFalsy("''"); - expect(util.helper.isNumber("a")).toBeFalsy("'a'"); - }); - it("toNumber", () => { - expect(util.helper.toNumber("a")).toEqual(0, "a"); - expect(util.helper.toNumber("0")).toEqual(0, "0"); - expect(util.helper.toNumber("1")).toEqual(1, "1"); - expect(util.helper.toNumber("1.5")).toEqual(1.5, "1.5"); - expect(util.helper.toNumber("1.5", 0)).toEqual(2, "1.5,0"); - expect(util.helper.toNumber("1.5", 0, true)).toEqual(1, "1.5,0,true"); - expect(util.helper.toNumber("8.99999999999999999", 0)).toEqual(9); - expect(util.helper.isNumber(util.helper.toNumber("8.99999999999999999", 0))).toBeTruthy(); - expect(util.helper.toNumber("8.99999999999999999", 2, true)).toEqual(8.99); - expect(util.helper.toNumber(1.567, 1, true)).toEqual(1.5); - expect(util.helper.isNumber(util.helper.toNumber("8.99999999999999999", 2, true))).toBeTruthy(); - }); - it("isEmptyArray", () => { - expect(util.helper.isEmptyArray(undefined)).toBeFalsy(); - expect(util.helper.isEmptyArray(null)).toBeFalsy(); - expect(util.helper.isEmptyArray([])).toBeTruthy(); - expect(util.helper.isEmptyArray([1])).toBeFalsy(); - }); - it("isValidDate", () => { - expect(util.helper.isValidDate("1999-09-09")).toBeTruthy(); - expect(util.helper.isValidDate("1999-9-9")).toBeTruthy(); - }); - it("toDate", () => { - expect(util.helper.isValidDate(util.helper.toDate("1999-09-09"))).toBeTruthy(); - expect(util.helper.toDate("1999-09-09").getFullYear()).toBe(1999); - expect(util.helper.toDate("1999-09-09").getMonth()).toBe(8); - expect(util.helper.toDate("1999-09-09").getDate()).toBe(9); - expect(util.helper.isValidDate(util.helper.toDate("1999-9-9"))).toBeTruthy(); - expect(util.helper.toDate("1999-9-9 1:2:3").getFullYear()).toBe(1999); - expect(util.helper.toDate("1999-9-9 1:2:3").getMonth()).toBe(8); - expect(util.helper.toDate("1999-9-9 1:2:3").getDate()).toBe(9); - expect(util.helper.toDate("1999-9-9 1:2:3").getHours()).toBe(1); - expect(util.helper.toDate("1999-9-9 1:2:3").getMinutes()).toBe(2); - expect(util.helper.toDate("1999-9-9 1:2:3").getSeconds()).toBe(3); - }); - it("formatDate", () => { - var date = new Date(2014, 11, 30, 1, 20, 15, 285); - expect(util.helper.formatDate(date, "YYYY-MM-DD HH:mm:ss:SSS")).toBe("2014-12-30 01:20:15:285"); - expect(util.helper.formatDate(date, "YYYY年MM月DD日 HH:mm:ss")).toBe("2014年12月30日 01:20:15"); - - expect(util.helper.formatDate("1999-09-09", "YYYY年MM月DD日")).toBe("1999年09月09日"); - expect(util.helper.formatDate("1999-9-9", "YYYY年MM月DD日")).toBe("1999年09月09日"); - }); - it("getValidDate", () => { - expect(util.helper.getValidDate("1999-09-09")).toBe("1999-09-09"); - expect(util.helper.getValidDate("1999-9-9")).toBe("1999-09-09"); - expect(util.helper.getValidDate("1999-9-9 1:2")).toBe("1999-09-09 01:02"); - expect(util.helper.getValidDate("1999-9-9 1:2:3")).toBe("1999-09-09 01:02:03"); - }); -}); diff --git a/sample/Util.Samples.Webs/Typings/test/util/core/select.test.ts b/sample/Util.Samples.Webs/Typings/test/util/core/select.test.ts deleted file mode 100644 index 701b9bb0d..000000000 --- a/sample/Util.Samples.Webs/Typings/test/util/core/select.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -//============== Util列表操作测试================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import {Select,SelectItem} from '../../../util/core/select'; - -describe("select", () => { - it("toOptions_1", () => { - let data: SelectItem[] = [{text:"a",value:"1"}]; - let select = new Select(data); - let result = select.toOptions(); - expect(result.length).toBe(1); - expect(result[0].text).toBe("a"); - expect(result[0].value).toBe("1"); - }); - it("toOptions_2", () => { - let data: SelectItem[] = [{ text: "a", value: "1" }, { text: "b", value: "2",disabled:true }]; - let select = new Select(data); - let result = select.toOptions(); - expect(result.length).toBe(2); - expect(result[0].text).toBe("a"); - expect(result[0].value).toBe("1"); - expect(result[0].disabled).toBeFalsy(); - expect(result[1].text).toBe("b"); - expect(result[1].value).toBe("2"); - expect(result[1].disabled).toBeTruthy(); - }); - it("toGroups_1", () => { - let data: SelectItem[] = [{ text: "a", value: "1", group: "group" }, { text: "b", value: "2", disabled: true, group: "group"}]; - let select = new Select(data); - let result = select.toGroups(); - expect(result.length).toBe(1); - expect(result[0].text).toBe("group"); - let value = result[0].value; - expect(value && value[0].text).toBe("a"); - expect(value && value[0].value).toBe("1"); - expect(value && value[1].text).toBe("b"); - expect(value && value[1].value).toBe("2"); - }); - it("toGroups_2", () => { - let data: SelectItem[] = [{ text: "a", value: "1", group: "group", sortId: 3 }, - { text: "b", value: "2", group: "group", sortId: 1 }, - { text: "c", value: "3", group: "group", sortId: 2 }]; - let select = new Select(data); - let result = select.toGroups(); - expect(result.length).toBe(1); - expect(result[0].text).toBe("group"); - let value = result[0].value; - expect(value && value[0].text).toBe("b"); - expect(value && value[0].value).toBe("2"); - expect(value && value[1].text).toBe("c"); - expect(value && value[1].value).toBe("3"); - expect(value && value[2].text).toBe("a"); - expect(value && value[2].value).toBe("1"); - }); - it("toGroups_3", () => { - let data: SelectItem[] = [{ text: "a", value: "1", group: "group", sortId: 3 }, - { text: "b", value: "2", group: "group", sortId: 2 }, - { text: "c", value: "3", group: "group2", sortId: 4 }, - { text: "d", value: "4", group: "group2", sortId: 1 }]; - let select = new Select(data); - let result = select.toGroups(); - expect(result.length).toBe(2); - expect(result[0].text).toBe("group2"); - expect(result[1].text).toBe("group"); - let group1 = result[0].value; - expect(group1 && group1[0].text).toBe("d"); - expect(group1 && group1[0].value).toBe("4"); - expect(group1 && group1[1].text).toBe("c"); - expect(group1 && group1[1].value).toBe("3"); - let group2 = result[1].value; - expect(group2 && group2[0].text).toBe("b"); - expect(group2 && group2[0].value).toBe("2"); - expect(group2 && group2[1].text).toBe("a"); - expect(group2 && group2[1].value).toBe("1"); - }); - it("isGroup_1", () => { - let data: SelectItem[] = [{ text: "a", value: "1" }, { text: "b", value: "2", disabled: true }]; - let select = new Select(data); - expect(select.isGroup()).toBeFalsy(); - }); - it("isGroup_2", () => { - let data: SelectItem[] = [{ text: "a", value: "1",group:"a" }, { text: "b", value: "2", disabled: true }]; - let select = new Select(data); - expect(select.isGroup()).toBeFalsy(); - }); - it("isGroup_3", () => { - let data: SelectItem[] = [{ text: "a", value: "1", group: "a" }, { text: "b", value: "2", disabled: true, group: "a" }]; - let select = new Select(data); - expect(select.isGroup()).toBeTruthy(); - }); -}); \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/angular/event-helper.ts b/sample/Util.Samples.Webs/Typings/util/angular/event-helper.ts deleted file mode 100644 index 14bb6432f..000000000 --- a/sample/Util.Samples.Webs/Typings/util/angular/event-helper.ts +++ /dev/null @@ -1,19 +0,0 @@ -//============== 事件操作========================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ - -/** - * 事件操作 - */ -export class EventHelper { - /** - * 获取事件传递的值 - * @param event 事件 - */ - static getValue(event): TResult { - if (event && event.target ) - return event.target.value; - return event; - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/angular/http-helper.ts b/sample/Util.Samples.Webs/Typings/util/angular/http-helper.ts deleted file mode 100644 index a24ed5ef6..000000000 --- a/sample/Util.Samples.Webs/Typings/util/angular/http-helper.ts +++ /dev/null @@ -1,231 +0,0 @@ -//============== Http操作========================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { HttpClient, HttpHeaders, HttpParams, HttpErrorResponse } from '@angular/common/http'; -import { IocHelper as ioc } from './ioc-helper'; -import { Observable } from 'rxjs/Observable'; - -/** - * Http操作 - */ -export class HttpHelper { - /** - * 发送请求 - * @param url 请求地址 - * @param httpMethod http方法 - * @param data 数据 - */ - static send(url: string, httpMethod: HttpMethod, data?): HttpRequest { - switch (httpMethod) { - case HttpMethod.Get: - return this.get(url).param(data); - case HttpMethod.Post: - return this.post(url, data); - case HttpMethod.Put: - return this.put(url, data); - case HttpMethod.Delete: - return this.delete(url).param(data); - default: - return this.get(url).param(data); - } - } - - /** - * get请求 - * @param url 请求地址 - */ - static get(url: string): HttpRequest { - return new HttpRequest(HttpMethod.Get, url); - } - - /** - * post请求 - * @param url 请求地址 - * @param body Http主体 - */ - static post(url: string, body?): HttpRequest { - return new HttpRequest(HttpMethod.Post, url, body); - } - - /** - * put请求 - * @param url 请求地址 - * @param body Http主体 - */ - static put(url: string, body?): HttpRequest { - return new HttpRequest(HttpMethod.Put, url, body); - } - - /** - * delete请求 - * @param url 请求地址 - */ - static delete(url: string): HttpRequest { - return new HttpRequest(HttpMethod.Delete, url); - } -} - -/** - * Http请求操作 - */ -export class HttpRequest { - /** - * Http头集合 - */ - private headers: HttpHeaders; - /** - * 内容类型 - */ - private httpContentType: HttpContentType; - /** - * Http参数集合 - */ - private parameters: HttpParams; - - /** - * 初始化Http请求操作 - * @param httpMethod Http方法 - * @param url 请求地址 - * @param body Http主体 - */ - constructor(private httpMethod: HttpMethod, private url: string, private body?) { - this.headers = new HttpHeaders(); - this.parameters = new HttpParams(); - } - - /** - * 添加Http头 - * @param name 名称 - * @param value 值 - */ - header(name: string, value): HttpRequest { - let stringValue = ""; - if (value !== undefined && value !== null) - stringValue = String(value); - this.headers = this.headers.append(name, stringValue ); - return this; - } - - /** - * 设置内容类型 - * @param contentType 内容类型 - */ - contentType(contentType: HttpContentType): HttpRequest { - this.httpContentType = contentType; - return this; - } - - /** - * 添加Http参数,添加到url查询字符串 - * @param data 参数对象 - */ - param(data): HttpRequest; - /** - * 添加Http参数,添加到url查询字符串 - * @param name 名称 - * @param value 值 - */ - param(name: string, value: string): HttpRequest; - param(data, value?: string): HttpRequest { - if (typeof data === "object") { - for (let key in data) { - if (data.hasOwnProperty(key)) - this.parameters = this.parameters.append(key, data[key]); - } - return this; - } - if (typeof data === "string" && value) - this.parameters = this.parameters.append(data, value); - return this; - } - - /** - * 处理响应 - * @param handler 响应处理函数 - * @param errorHandler 错误处理函数 - * @param beforeHandler 发送前处理函数,返回false则取消发送 - * @param completeHandler 请求完成处理函数 - */ - handle(handler: (value: T) => void, errorHandler?: (error: HttpErrorResponse) => void, beforeHandler?: () => boolean, completeHandler?: () => void) { - if (beforeHandler && beforeHandler() === false) - return; - this.request().subscribe(handler, errorHandler, completeHandler); - } - - /** - * 发送请求 - */ - private request(): Observable { - this.setContentType(); - let httpClient = ioc.get(HttpClient); - let options = { headers: this.headers, params: this.parameters }; - switch (this.httpMethod) { - case HttpMethod.Get: - return httpClient.get(this.url, options); - case HttpMethod.Post: - return httpClient.post(this.url, this.getBody(), options); - case HttpMethod.Put: - return httpClient.put(this.url, this.getBody(), options); - case HttpMethod.Delete: - return httpClient.delete(this.url, options); - default: - return httpClient.get(this.url, options); - } - } - - /** - * 获取body - */ - private getBody() { - if (typeof this.body === "string") - return JSON.stringify(this.body); - return this.body; - } - - /** - * 设置内容类型 - */ - private setContentType() { - return this.header("Content-Type", this.getContentType(this.httpContentType)); - } - - /** - * 获取内容类型 - * @param contentType - */ - private getContentType(contentType: HttpContentType) { - switch (contentType) { - case HttpContentType.FormUrlEncoded: - return "application/x-www-form-urlencoded"; - case HttpContentType.Json: - return "application/json"; - default: - return "application/json"; - } - } -} - -/** - * Http方法 - */ -export enum HttpMethod { - Get, - Post, - Put, - Delete -} - -/** - * Http内容类型 - */ -export enum HttpContentType { - /** - * application/x-www-form-urlencoded - */ - FormUrlEncoded, - /** - * application/json - */ - Json -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/angular/ioc-helper.ts b/sample/Util.Samples.Webs/Typings/util/angular/ioc-helper.ts deleted file mode 100644 index 294e62181..000000000 --- a/sample/Util.Samples.Webs/Typings/util/angular/ioc-helper.ts +++ /dev/null @@ -1,35 +0,0 @@ -//============== Ioc操作========================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { Injector, Type, InjectionToken } from '@angular/core'; - -/** - * Ioc操作 - */ -export class IocHelper { - /** - * 全局注入器 - */ - static injector: Injector; - /** - * 当前组件注入器 - */ - static componentInjector: Injector; - - /** - * 获取实例,从全局注入器中获取 - * @param token 实例标记,一般为类或接口名称,范例:util.ioc.get(Http) - */ - static get(token: Type | InjectionToken): T { - return IocHelper.injector.get(token); - } - - /** - * 获取实例,从当前组件注入器中获取 - * @param token 实例标记,一般为类或接口名称,范例:util.ioc.getByComponent(Http) - */ - static getByComponent(token: Type | InjectionToken): T { - return IocHelper.componentInjector.get(token); - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/angular/router-helper.ts b/sample/Util.Samples.Webs/Typings/util/angular/router-helper.ts deleted file mode 100644 index fe520e80c..000000000 --- a/sample/Util.Samples.Webs/Typings/util/angular/router-helper.ts +++ /dev/null @@ -1,48 +0,0 @@ -//============== 路由操作========================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { Router, ActivatedRoute, NavigationExtras } from '@angular/router'; -import { Location } from '@angular/common'; -import { IocHelper as ioc } from './ioc-helper'; - -/** - * 路由操作 - */ -export class RouterHelper { - /** - * 返回上一次视图 - */ - static back(): void { - let location: Location = ioc.get(Location); - location.back(); - } - - /** - * 导航 - * @param commands 导航参数,范例: ['team', 33, 'user', 11],表示 /team/33/user/11 - * @param extras 附加参数,范例: {queryParams: {id:'1'}},表示 ?id=1 - */ - static navigate(commands: any[], extras?: NavigationExtras): Promise { - let router = ioc.get(Router); - return router.navigate(commands, extras); - } - - /** - * 获取当前路由参数值,从路由快照中获取参数 - * @param paramName 参数名 - */ - static getParam(paramName: string): string | null { - let route = ioc.getByComponent(ActivatedRoute); - return route.snapshot.paramMap.get(paramName); - } - - /** - * 获取当前路由参数值,从路由快照中的查询字符串获取参数 - * @param paramName 参数名 - */ - static getQueryParam(paramName: string): string | null { - let route = ioc.getByComponent(ActivatedRoute); - return route.snapshot.queryParamMap.get(paramName); - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/common/form.ts b/sample/Util.Samples.Webs/Typings/util/common/form.ts deleted file mode 100644 index 1f74a7f5d..000000000 --- a/sample/Util.Samples.Webs/Typings/util/common/form.ts +++ /dev/null @@ -1,183 +0,0 @@ -//============== 表单操作 ======================== -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { NgForm } from '@angular/forms'; -import { FailResult } from '../core/result'; -import { ViewModel } from '../core/view-model'; -import { HttpMethod } from '../angular/http-helper'; -import { WebApi } from './webapi'; -import { RouterHelper } from '../angular/router-helper'; -import { Message } from './message'; -import { MessageConfig } from '../config/message-config'; - -/** - * 表单操作 - */ -export class Form { - /** - * 提交表单 - * @param options 表单提交参数 - */ - submit(options: IFormSubmitOptions): void { - this.initSubmit(options); - if (!this.validateSubmit(options)) { - options["fnComplete"](); - return; - } - if (!this.submitBefore(options)) { - options["fnComplete"](); - return; - } - if (!options.confirm) { - this.submitForm(options); - return; - } - Message.confirm({ - title: options.confirmTitle, - message: options.confirm, - ok: () => this.submitForm(options), - cancel: options["fnComplete"] - }); - } - - /** - * 提交表单初始化 - */ - private initSubmit(options: IFormSubmitOptions) { - if (!options) - return; - options["fnComplete"] = () => { - if (options.form) - (options.form as { submitted: boolean }).submitted = false; - options.completeHandler && options.completeHandler(); - }; - } - - /** - * 提交表单验证 - */ - private validateSubmit(options: IFormSubmitOptions) { - if (!options) { - Message.error("表单参数[options: FormSubmitOptions]未设置"); - return false; - } - if (!options.url) { - Message.error("表单url未设置"); - return false; - } - if (!options.form) { - Message.error("表单ngForm未设置"); - return false; - } - if (!options.data) { - Message.error("表单数据未设置"); - return false; - } - return true; - } - - /** - * 提交前操作 - */ - private submitBefore(options: IFormSubmitOptions) { - if (!options.beforeHandler) - return true; - return options.beforeHandler(); - } - - /** - * 提交表单 - */ - private submitForm(options: IFormSubmitOptions) { - if (!options.httpMethod) { - options.httpMethod = options.data.id ? HttpMethod.Put : HttpMethod.Post; - } - WebApi.send(options.url, options.httpMethod, options.data).header(options.header).handle({ - handler: result => { - this.submitHandler(options, result); - }, - failHandler: options.failHandler, - completeHandler: options["fnComplete"] - }); - } - - /** - * 提交表单成功处理函数 - */ - private submitHandler(options: IFormSubmitOptions, result) { - options.handler && options.handler(result); - if (options.showMessage !== false) - Message.snack(MessageConfig.successed); - if (options.reset) - options.form.resetForm(); - if (options.back) - RouterHelper.back(); - } -} - -/** - * 表单提交参数 - */ -export interface IFormSubmitOptions { - /** - * 表单 - */ - form: NgForm; - /** - * 请求地址 - */ - url: string; - /** - * 提交数据 - */ - data: ViewModel; - /** - * Http头 - */ - header?: { name: string, value }[]; - /** - * Http方法 - */ - httpMethod?: HttpMethod; - /** - * 确认消息,设置该项则提交前需要确认 - */ - confirm?: string; - /** - * 确认标题 - */ - confirmTitle?: string; - /** - * 提交成功后是否显示成功提示,默认为true - */ - showMessage?: boolean; - /** - * 提交成功后是否返回上一个视图,默认为false - */ - back?: boolean; - /** - * 提交成功后是否关闭弹出层,当在弹出层中编辑时使用,默认为false - */ - closeDialog?: boolean; - /** - * 提交成功后是否重置表单,默认为false - */ - reset?: boolean; - /** - * 提交前处理函数,返回false则取消提交 - */ - beforeHandler?: () => boolean; - /** - * 提交成功处理函数 - */ - handler?: (result) => void; - /** - * 提交失败处理函数 - */ - failHandler?: (result: FailResult) => void; - /** - * 操作完成处理函数,注意:该函数在任意情况下都会执行 - */ - completeHandler?: () => void; -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/common/helper.ts b/sample/Util.Samples.Webs/Typings/util/common/helper.ts deleted file mode 100644 index 57852f2e0..000000000 --- a/sample/Util.Samples.Webs/Typings/util/common/helper.ts +++ /dev/null @@ -1,172 +0,0 @@ -//============== 公共操作========================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { UUID } from './internal/uuid'; -import * as moment from 'moment'; - -/** - * 是否未定义 - * @param value 值 - */ -export let isUndefined = (value): boolean => { - return typeof value === 'undefined'; -} - -/** - * 是否字符串 - * @param value 值 - */ -export let isString = (value): boolean => { - return typeof value === 'string'; -} - -/** - * 是否空值,当值为undefined、null、空字符串、空Guid时返回true,其余返回false - * @param value 值 - */ -export let isEmpty = (value): boolean => { - if (isUndefined(value)) - return true; - if (value === null) - return true; - if (!isString(value)) - return false; - if (value && value.trim) - value = value.trim(); - if (!value) - return true; - if (value === "00000000-0000-0000-0000-000000000000") - return true; - return false; -} - -/** - * 是否数字,字符串"1"返回true - * @param value 值 - */ -export let isNumber = (value): boolean => { - return !isNaN(parseFloat(value)) && isFinite(value); -} - -/** - * 转换为数值 - * @param value 输入值 - * @param precision 数值精度,即小数位数,可选值为0-20 - * @param isTruncate 是否截断,传入true,则按精度截断,否则四舍五入 - */ -export let toNumber = (value, precision?, isTruncate?: boolean) => { - if (!isNumber(value)) - return 0; - value = value.toString(); - if (isEmpty(precision)) - return parseFloat(value); - if (isTruncate) - return parseFloat(value.substring(0, value.indexOf(".") + parseInt(precision) + 1)); - return parseFloat(parseFloat(value).toFixed(precision)); -} - -/** - * 是否数组 - * @param value 值 - */ -export let isArray = (value): boolean => { - return Array.isArray(value); -} - -/** - * 是否空数组,undefined和null返回false,[]返回true - * @param value 值 - */ -export let isEmptyArray = (value): boolean => { - return isArray(value) && value.length === 0; -} - -/** - * 转换为json字符串 - * @param value 值 - */ -export let toJson = (value): string => { - return JSON.stringify(value); -} - -/** - * 创建唯一标识 - */ -export let uuid = (): string => { - return UUID.UUID(); -} - -/** - * 是否有效日期 - * @param date 日期 - */ -export let isValidDate = (date): boolean => { - return moment(getValidDate(date)).isValid(); -} - -/** - * *获取有效的日期字符串,对无效日期补全前导0,不支持毫秒, - * 范例:2000-1-1 1:2:3,返回2000-01-01 01:02:03 - * @param date 日期 - */ -export let getValidDate = (date) => { - if (!date) - return date; - if (typeof date !== "string") - return date; - if (date.indexOf("-") <= 0) - return date; - let regex = /(\d{4})-(\d{1,2})-(\d{1,2})(?:(?:\s+)(\d{1,2}):(\d{1,2}):?(\d{1,2})?)?/; - if (!regex.test(date)) - return date; - let dateSegment = date.match(regex); - if (!dateSegment) - return date; - let year = dateSegment[1]; - let month = dateSegment[2]; - let day = dateSegment[3]; - let hour = dateSegment[4]; - let minute = dateSegment[5]; - let second = dateSegment[6]; - month = month.length === 1 ? `0${month}` : month; - day = day.length === 1 ? `0${day}` : day; - let result = `${year}-${month}-${day}`; - if (hour && minute) { - hour = hour.length === 1 ? `0${hour}` : hour; - minute = minute.length === 1 ? `0${minute}` : minute; - result += ` ${hour}:${minute}`; - } - if (second) { - second = second.length === 1 ? `0${second}` : second; - result += `:${second}`; - } - return result; -} - -/** - * 转换为日期 - * @param date 日期,字符串日期范例:2001-01-01 - */ -export let toDate = (date): Date => { - return moment(getValidDate(date)).toDate(); -} - -/** - * 格式化日期 - * @param datetime 日期 - * @param format 格式化字符串,范例:YYYY-MM-DD,可选值:(注意:区分大小写) - * (1) 年: YYYY - * (2) 月: MM - * (3) 日: DD - * (4) 时: HH - * (5) 分: mm - * (6) 秒: ss - * (7) 毫秒: SSS - */ -export let formatDate = (datetime, format: string): string => { - let date = moment(getValidDate(datetime)); - if (!date.isValid()) - return ""; - return date.format(format); -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/common/internal/uuid.ts b/sample/Util.Samples.Webs/Typings/util/common/internal/uuid.ts deleted file mode 100644 index 3b8fa218e..000000000 --- a/sample/Util.Samples.Webs/Typings/util/common/internal/uuid.ts +++ /dev/null @@ -1,36 +0,0 @@ -//Դhttps://github.com/wulfsolter/angular2-uuid/blob/master/src/uuid.ts -"use strict"; - -export class UUID { - - public static UUID(): string { - if (typeof (window) !== "undefined" && typeof (window.crypto) !== "undefined" && typeof (window.crypto.getRandomValues) !== "undefined") { - // If we have a cryptographically secure PRNG, use that - // http://stackoverflow.com/questions/6906916/collisions-when-generating-uuids-in-javascript - let buf: Uint16Array = new Uint16Array(8); - window.crypto.getRandomValues(buf); - return (this.pad4(buf[0]) + this.pad4(buf[1]) + "-" + this.pad4(buf[2]) + "-" + this.pad4(buf[3]) + "-" + this.pad4(buf[4]) + "-" + this.pad4(buf[5]) + this.pad4(buf[6]) + this.pad4(buf[7])); - } else { - // Otherwise, just use Math.random - // https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript - // https://stackoverflow.com/questions/11605068/why-does-jshint-argue-against-bitwise-operators-how-should-i-express-this-code - return this.random4() + this.random4() + "-" + this.random4() + "-" + this.random4() + "-" + - this.random4() + "-" + this.random4() + this.random4() + this.random4(); - } - } - - private static pad4(num: number): string { - let ret: string = num.toString(16); - while (ret.length < 4) { - ret = "0" + ret; - } - return ret; - } - - private static random4(): string { - return Math.floor((1 + Math.random()) * 0x10000) - .toString(16) - .substring(1); - } - -} diff --git a/sample/Util.Samples.Webs/Typings/util/common/message.ts b/sample/Util.Samples.Webs/Typings/util/common/message.ts deleted file mode 100644 index 1e08e68e9..000000000 --- a/sample/Util.Samples.Webs/Typings/util/common/message.ts +++ /dev/null @@ -1,152 +0,0 @@ -//============== 消息操作========================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { MatSnackBar } from '@angular/material'; -import { MessageService } from 'primeng/components/common/messageservice'; -import { ConfirmationService } from 'primeng/primeng'; -import { IocHelper as ioc } from '../angular/ioc-helper'; - -/** - * 消息操作 - */ -export class Message { - /** - * 成功消息 - * @param message 消息 - * @param title 标题 - */ - static success(message: string, title?: string): void { - var service = ioc.get(MessageService); - service.add({ severity: 'success', summary: title || "成功", detail: message }); - } - - /** - * 信息消息 - * @param message 消息 - * @param title 标题 - */ - static info(message: string, title?: string): void { - var service = ioc.get(MessageService); - service.add({ severity: 'info', summary: title || "信息", detail: message }); - } - - /** - * 警告消息 - * @param message 消息 - * @param title 标题 - */ - static warn(message: string, title?: string): void { - var service = ioc.get(MessageService); - service.add({ severity: 'warn', summary: title || "警告", detail: message }); - } - - /** - * 错误消息 - * @param message 消息 - * @param title 标题 - */ - static error(message: string, title?: string): void { - var service = ioc.get(MessageService); - service.add({ severity: 'error', summary: title || "错误", detail: message }); - } - - /** - * 确认 - * @param options 配置 - */ - static confirm(options: { - /** - * 消息 - */ - message: string, - /** - * 确认回调函数 - */ - ok: () => void, - /** - * 取消回调函数 - */ - cancel?: () => void, - /** - * 标题 - */ - title?: string; - }): void; - /** - * 确认 - * @param message 消息 - * @param ok 确认回调函数 - */ - static confirm(message: string, ok?: () => void): void; - static confirm(options, ok?): void { - let service = ioc.get(ConfirmationService); - let message = ""; - let title = ""; - let cancel = () => { }; - if (typeof options === "object") { - message = options["message"]; - title = options["title"]; - ok = options["ok"]; - cancel = options["cancel"]; - } - else if (typeof options === "string") { - message = options; - } - service.confirm({ - message: message, - accept: ok, - reject: cancel, - header: title || "确认" - }); - } - - /** - * 小提示 - * @param options - */ - static snack(options: { - /** - * 消息 - */ - message: string; - /** - * 持续时间,单位:毫秒,默认值:2000 - */ - duration?: number; - /** - * 操作文本 - */ - actionLabel?: string; - /** - * 操作 - */ - action?: () => void; - }); - /** - * 小提示 - * @param message 消息 - */ - static snack(message: string); - static snack(options) { - let bar = ioc.get(MatSnackBar); - let message = ""; - let actionLabel = ""; - let action; - let duration; - if (typeof options === "object") { - message = options["message"]; - actionLabel = options["actionLabel"]; - action = options["action"]; - duration = options["duration"]; - } - else if (typeof options === "string") { - message = options; - } - let ref = bar.open(message, actionLabel || "关闭", { - duration: duration || 2000 - }); - if (action) - ref.onAction().subscribe(action); - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/common/webapi.ts b/sample/Util.Samples.Webs/Typings/util/common/webapi.ts deleted file mode 100644 index 39c224368..000000000 --- a/sample/Util.Samples.Webs/Typings/util/common/webapi.ts +++ /dev/null @@ -1,239 +0,0 @@ -//============== WebApi操作========================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { HttpErrorResponse } from '@angular/common/http'; -import { Result, FailResult, StateCode } from '../core/result'; -import { HttpHelper, HttpRequest, HttpContentType,HttpMethod } from '../angular/http-helper'; -import { Message } from './message'; - -/** - * WebApi操作,与服务端返回的标准result对象交互 - */ -export class WebApi { - /** - * 发送请求 - * @param url 请求地址 - * @param httpMethod http方法 - * @param data 数据 - */ - static send(url: string, httpMethod: HttpMethod, data?): WebApiRequest { - switch (httpMethod) { - case HttpMethod.Get: - return this.get(url).param(data); - case HttpMethod.Post: - return this.post(url, data); - case HttpMethod.Put: - return this.put(url, data); - case HttpMethod.Delete: - return this.delete(url).param(data); - default: - return this.get(url).param(data); - } - } - - /** - * get请求 - * @param url 请求地址 - */ - static get(url: string): WebApiRequest { - return new WebApiRequest(HttpHelper.get>(url)); - } - - /** - * post请求 - * @param url 请求地址 - * @param body Http主体 - */ - static post(url: string, body?): WebApiRequest { - return new WebApiRequest(HttpHelper.post>(url, body)); - } - - /** - * put请求 - * @param url 请求地址 - * @param body Http主体 - */ - static put(url: string, body?): WebApiRequest { - return new WebApiRequest(HttpHelper.put>(url, body)); - } - - /** - * delete请求 - * @param url 请求地址 - */ - static delete(url: string): WebApiRequest { - return new WebApiRequest(HttpHelper.delete>(url)); - } -} - -/** - * WebApi请求操作 - */ -export class WebApiRequest { - /** - * 初始化WebApi请求操作 - * @param request Http请求操作 - */ - constructor(private request: HttpRequest>) { - } - - /** - * 添加Http头 - * @param headers Http头集合 - */ - header(headers?: { name: string, value }[]): WebApiRequest; - /** - * 添加Http头 - * @param name 名称 - * @param value 值 - */ - header(name: string, value): WebApiRequest; - header(name, value?): WebApiRequest { - if (typeof name === "object") - this.addHeaders(name); - else if (typeof name === "string") - this.request.header(name, value); - return this; - } - - /** - * 添加Http头 - */ - private addHeaders(param) { - let headers = param as { name: string, value }[]; - if (!headers) - return; - headers.forEach(item => { - this.request.header(item.name, item.value); - }); - } - - /** - * 设置内容类型 - * @param contentType 内容类型 - */ - contentType(contentType: HttpContentType): WebApiRequest { - this.request.contentType(contentType); - return this; - } - - /** - * 添加Http参数,添加到url查询字符串 - * @param data 参数对象 - */ - param(data): WebApiRequest; - /** - * 添加Http参数,添加到url查询字符串 - * @param name 名称 - * @param value 值 - */ - param(name: string, value: string): WebApiRequest; - param(data, value?: string): WebApiRequest { - if (typeof data === "object") { - this.request.param(data); - return this; - } - if (typeof data === "string" && value) - this.request.param(data, value); - return this; - } - - /** - * 处理响应 - * @param options 响应处理器配置 - */ - handle(options: WebApiHandleOptions) { - if (!options) - return; - this.request.handle( - (result: Result) => this.handleOk(options, result), - (error: HttpErrorResponse) => this.handleFail(options, undefined, error), - options.beforeHandler, - options.completeHandler - ); - } - - /** - * 处理成功响应 - */ - private handleOk(options: WebApiHandleOptions, result: Result) { - if (!result) - return; - if (result.code === StateCode.Ok) { - options.handler && options.handler(result.data); - return; - } - this.handleFail(options, result); - } - - /** - * 处理失败响应 - */ - private handleFail(options: WebApiHandleOptions, result?: Result, errorResponse?: HttpErrorResponse) { - let failResult = new FailResult(result, errorResponse); - if (options.failHandler) { - options.failHandler(failResult); - return; - } - if (result) { - this.handleBusinessException(result); - return; - } - this.handleHttpException(options, failResult); - } - - /** - * 处理业务异常 - */ - private handleBusinessException(result: Result) { - if (result.code === StateCode.Fail) { - Message.error(result.message); - console.log(`业务异常:\n${result.message}` ); - } - } - - /** - * 处理Http异常 - */ - private handleHttpException(options: WebApiHandleOptions, failResult: FailResult) { - if (failResult.errorResponse) - console.log(this.getHttpExceptionMessage(failResult)); - if (options.completeHandler) - options.completeHandler(); - } - - /** - * 获取Http异常消息 - */ - private getHttpExceptionMessage(failResult: FailResult) { - if (!failResult.errorResponse) - return ""; - let error = failResult.errorResponse; - return `Http请求异常:\nUrl:${error.url}\n状态码:${error.status},${error.statusText}\n` - + `错误消息:${error.message}\n错误响应:\n ${error.error.text}\n`; - } -} - -/** - * WebApi响应处理器 - */ -export class WebApiHandleOptions { - /** - * 发送前处理函数,返回false则取消发送 - */ - beforeHandler?: () => boolean; - /** - * 成功处理函数 - */ - handler: (value: T) => void; - /** - * 失败处理函数 - */ - failHandler?: (result: FailResult) => void; - /** - * 请求完成处理函数 - */ - completeHandler?: () => void; -} - diff --git a/sample/Util.Samples.Webs/Typings/util/config/message-config.ts b/sample/Util.Samples.Webs/Typings/util/config/message-config.ts deleted file mode 100644 index 9e947d541..000000000 --- a/sample/Util.Samples.Webs/Typings/util/config/message-config.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * 提示消息配置 - */ -export class MessageConfig { - /** - * 清除 - */ - static clear = "清除"; - /** - * 取消 - */ - static cancel = "取消"; - /** - * 上传 - */ - static upload = "上传"; - /** - * 选择文件 - */ - static chooseFile = "选择文件"; - /** - * 重 置 - */ - static reset = "重 置"; - /** - * 提 交 - */ - static submit = "提 交"; - /** - * 重置项默认文本 - */ - static resetOptionText = "-- 请选择 --"; - /** - * 必填项默认验证消息 - */ - static requiredMessage = "该项必须填写"; - /** - * 最小长度默认验证消息 - */ - static minLengthMessage = "输入内容的长度必须大于{0}位"; - /** - * 电子邮件默认验证消息 - */ - static emailMessage = "请输入有效的电子邮件"; - /** - * 请选择待删除的记录 - */ - static deleteNotSelected = "请选择待删除的记录"; - /** - * 操作成功 - */ - static successed = "操作成功"; - /** - * 删除成功 - */ - static deleteSuccessed = "删除成功"; - /** - * 您确定删除选中的记录吗? - */ - static deleteConfirm = "您确定删除选中的记录吗?"; -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/core/component-base.ts b/sample/Util.Samples.Webs/Typings/util/core/component-base.ts deleted file mode 100644 index e1263ffb7..000000000 --- a/sample/Util.Samples.Webs/Typings/util/core/component-base.ts +++ /dev/null @@ -1,33 +0,0 @@ -//============== 组件基类========================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { Injector } from '@angular/core'; -import { IocHelper as ioc } from '../angular/ioc-helper'; -import { RouterHelper as router } from "../angular/router-helper"; -import { Util } from '../util'; - -/** - * 组件 - */ -export class ComponentBase { - /** - * 初始化组件 - * @param injector 注入器 - */ - constructor(injector: Injector) { - ioc.componentInjector = injector; - } - - /** - * 操作库 - */ - protected util = Util; - - /** - * 返回上一次视图 - */ - back(): void { - router.back(); - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/core/file-info.ts b/sample/Util.Samples.Webs/Typings/util/core/file-info.ts deleted file mode 100644 index 84ed85612..000000000 --- a/sample/Util.Samples.Webs/Typings/util/core/file-info.ts +++ /dev/null @@ -1,43 +0,0 @@ -//============== 文件信息========================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -/** - * 文件信息 - */ -export class FileInfo { - /** - * 标识 - */ - id: string; - /** - * 文件地址 - */ - url: string; - /** - * 文件名 - */ - fileName: string; - /** - * 扩展名 - */ - extension: string; - /** - * 文件类型 - */ - type: FileType; -} - -/** - * 文件类型 - */ -export enum FileType { - /** - * 图片 - */ - Image = 1, - /** - * 其它 - */ - Other = 2 -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/core/pager-list.ts b/sample/Util.Samples.Webs/Typings/util/core/pager-list.ts deleted file mode 100644 index 732056a1c..000000000 --- a/sample/Util.Samples.Webs/Typings/util/core/pager-list.ts +++ /dev/null @@ -1,58 +0,0 @@ -//============== 分页集合========================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -/** - * 分页集合 - */ -export class PagerList { - /** - * 初始化分页集合 - * @param list 分页集合 - */ - constructor(list?: PagerList) { - if (!list) - return; - this.page = list.page; - this.pageSize = list.pageSize; - this.totalCount = list.totalCount; - this.pageCount = list.pageCount; - this.order = list.order; - this.data = list.data; - } - - /** - * 页索引,即第几页 - */ - page: number; - /** - * 每页显示行数 - */ - pageSize: number; - /** - * 总行数 - */ - totalCount: number; - /** - * 总页数 - */ - pageCount: number; - /** - * 排序条件 - */ - order: string; - /** - * 数据 - */ - data: T[]; - - /** - * 初始化行号 - */ - initLineNumbers(): void { - for (let i = 0; i < this.data.length; i++) { - let line = (this.page - 1) * this.pageSize + i + 1; - this.data[i]["lineNumber"] = line; - } - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/core/query-parameter.ts b/sample/Util.Samples.Webs/Typings/util/core/query-parameter.ts deleted file mode 100644 index 2957ade92..000000000 --- a/sample/Util.Samples.Webs/Typings/util/core/query-parameter.ts +++ /dev/null @@ -1,25 +0,0 @@ -//============== 查询参数========================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -/** - * 查询参数 - */ -export class QueryParameter { - /** - * 页索引,即第几页 - */ - page: number; - /** - * 每页显示行数 - */ - pageSize: number; - /** - * 排序条件 - */ - order: string; - /** - * 搜索关键字 - */ - keyword:string; -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/core/result.ts b/sample/Util.Samples.Webs/Typings/util/core/result.ts deleted file mode 100644 index cd7578b9e..000000000 --- a/sample/Util.Samples.Webs/Typings/util/core/result.ts +++ /dev/null @@ -1,62 +0,0 @@ -//============== 服务端结果约定格式=============== -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { HttpErrorResponse } from '@angular/common/http'; - -/** - * 服务端返回的标准结果 - */ -export class Result { - /** - * 状态码 - */ - code: StateCode; - /** - * 消息 - */ - message: string; - /** - * 数据 - */ - data: T; -} - -/** - * 错误结果 - */ -export class FailResult { - /** - * - * @param result 结果 - * @param errorResponse Http错误响应 - */ - constructor(result?: Result, public errorResponse?: HttpErrorResponse) { - if (result) { - this.code = result.code; - this.message = result.message; - } - } - /** - * 状态码 - */ - code: StateCode; - /** - * 消息 - */ - message: string; -} - -/** - * 状态码 - */ -export enum StateCode { - /** - * 成功 - */ - Ok = 1, - /** - * 失败 - */ - Fail = 2 -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/core/select.ts b/sample/Util.Samples.Webs/Typings/util/core/select.ts deleted file mode 100644 index 6f8027d8e..000000000 --- a/sample/Util.Samples.Webs/Typings/util/core/select.ts +++ /dev/null @@ -1,124 +0,0 @@ -//============== 列表============================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { ISort, sort } from '../core/sort'; - -/** - * 列表 - */ -export class Select { - /** - * 初始化列表 - * @param items 列表项集合 - */ - constructor(private items: SelectItem[]) { - } - - /** - * 转换为下拉列表项集合 - */ - toOptions(): SelectOption[] { - return this.getSortedItems().map(value => new SelectOption(value)); - } - - /** - * 获取已排序的列表项集合 - */ - private getSortedItems() { - return sort(this.items); - } - - /** - * 转换为下拉列表组集合 - */ - toGroups(): SelectOptionGroup[] { - let result: SelectOptionGroup[] = new Array(); - this.getSortedItems().forEach(item => { - if (!item.group) - return; - let group = result.find(group => group.text === item.group); - if (group) { - group.value.push(new SelectOption(item)); - return; - } - result.push(new SelectOptionGroup(item.group, [new SelectOption(item)], false)); - }); - return result; - } - - /** - * 是否列表组 - */ - isGroup(): boolean { - return this.items.every(value => !!value.group); - } -} - -/** - * 列表项 - */ -export class SelectItem implements ISort { - /** - * 文本 - */ - text: string; - /** - * 值 - */ - value; - /** - * 禁用 - */ - disabled?: boolean; - /** - * 排序号 - */ - sortId?: number; - /** - * 组 - */ - group?: string; -} - -/** - * 下拉列表项 - */ -export class SelectOption { - /** - * 文本 - */ - text: string; - /** - * 值 - */ - value; - /** - * 禁用 - */ - disabled?: boolean; - - /** - * 初始化下拉列表项 - * @param item 列表项 - */ - constructor(item: SelectItem) { - this.text = item.text; - this.value = item.value; - this.disabled = item.disabled; - } -} - -/** - * 下拉列表组 - */ -export class SelectOptionGroup { - /** - * 初始化下拉列表组 - * @param text 文本 - * @param value 值 - * @param disabled 禁用 - */ - constructor(public text: string, public value: SelectOption[], public disabled?: boolean) { - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/core/sort.ts b/sample/Util.Samples.Webs/Typings/util/core/sort.ts deleted file mode 100644 index 4ef763bfa..000000000 --- a/sample/Util.Samples.Webs/Typings/util/core/sort.ts +++ /dev/null @@ -1,22 +0,0 @@ -//============== 排序 ============================ -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ - -/** - * 排序 - */ -export interface ISort { - /** - * 排序号 - */ - sortId?: number; -} - -/** - * 排序 - * @param array 数组 - */ -export function sort(array: T[]): T[] { - return array.sort((a, b) => (a.sortId === undefined || b.sortId === undefined) ? 0 : a.sortId - b.sortId); -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/core/tree-node.ts b/sample/Util.Samples.Webs/Typings/util/core/tree-node.ts deleted file mode 100644 index 2c0760bbf..000000000 --- a/sample/Util.Samples.Webs/Typings/util/core/tree-node.ts +++ /dev/null @@ -1,27 +0,0 @@ -//============== 树节点 ========================== -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { ISort } from '../core/sort'; - -/** - * 树节点 - */ -export class TreeNode implements ISort{ - /** - * 标识 - */ - id?: string; - /** - * 父标识 - */ - parentId?: string; - /** - * 文本 - */ - text: string; - /** - * 排序号 - */ - sortId?: number; -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/core/view-model.ts b/sample/Util.Samples.Webs/Typings/util/core/view-model.ts deleted file mode 100644 index 8d5e68af1..000000000 --- a/sample/Util.Samples.Webs/Typings/util/core/view-model.ts +++ /dev/null @@ -1,13 +0,0 @@ -//============== 视图模型========================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -/** - * 视图模型 - */ -export class ViewModel { - /** - * 标识 - */ - id: string; -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/index.ts b/sample/Util.Samples.Webs/Typings/util/index.ts deleted file mode 100644 index aeca9bc06..000000000 --- a/sample/Util.Samples.Webs/Typings/util/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -//============== util操作========================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -export { Util as util } from './util'; -export { UtilModule } from './util.module'; -export { HttpContentType } from "./angular/http-helper"; -export { ComponentBase } from './core/component-base'; -export { ViewModel } from './core/view-model'; -export { PagerList } from './core/pager-list'; -export { QueryParameter } from './core/query-parameter'; -export { Result, FailResult, StateCode } from './core/result'; -export { SelectItem } from './core/select'; -export { TableWrapperComponent } from './material/table-wrapper.component'; -export { SelectWrapperComponent } from './material/select-wrapper.component'; -export { TextBoxWrapperComponent } from './material/textbox-wrapper.component'; -export { TextareaWrapperComponent } from './material/textarea-wrapper.component'; -export { DatePickerWrapperComponent } from './material/datepicker-wrapper.component'; -export { ButtonWrapperComponent } from './material/button-wrapper.component'; -export { RadioWrapperComponent } from './material/radio-wrapper.component'; \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/material/base/form-control-wrapper-base.ts b/sample/Util.Samples.Webs/Typings/util/material/base/form-control-wrapper-base.ts deleted file mode 100644 index 046647a4c..000000000 --- a/sample/Util.Samples.Webs/Typings/util/material/base/form-control-wrapper-base.ts +++ /dev/null @@ -1,163 +0,0 @@ -//============== 表单控件包装器=========================== -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { Input, Output, EventEmitter, ViewChild, Optional, Host, AfterViewInit } from '@angular/core'; -import { NgModel, NgForm } from '@angular/forms'; -import { MessageConfig } from '../../config/message-config'; - -/** - * 表单控件包装器 - */ -export class FormControlWrapperBase implements AfterViewInit{ - /** - * 名称 - */ - @Input() name: string; - /** - * 禁用 - */ - @Input() disabled: boolean; - /** - * 占位提示符 - */ - @Input() placeholder: string; - /** - * 占位提示符浮动位置,可选值:auto,never,always - */ - @Input() floatPlaceholder: string; - /** - * 起始提示,显示在控件左下方 - */ - @Input() startHint: string; - /** - * 结束提示,显示在控件右下方 - */ - @Input() endHint: string; - /** - * 前缀文本 - */ - @Input() prefixText: string; - /** - * 后缀文本 - */ - @Input() suffixText: string; - /** - * 后缀FontAwesome图标 - */ - @Input() suffixFontAwesomeIcon: string; - /** - * 后缀Material图标 - */ - @Input() suffixMaterialIcon: string; - /** - * 必填项 - */ - @Input() required: boolean; - /** - * 必填项验证消息 - */ - @Input() requiredMessage: string; - /** - * 后缀图标单击事件 - */ - @Output() onSuffixIconClick = new EventEmitter(); - /** - * 模型,用于双向绑定 - */ - @Input() model; - /** - * 模型变更事件,用于双向绑定 - */ - @Output() modelChange = new EventEmitter(); - /** - * 变更事件 - */ - @Output() onChange = new EventEmitter(); - /** - * 获得焦点事件 - */ - @Output() onFocus = new EventEmitter(); - /** - * 失去焦点事件 - */ - @Output() onBlur = new EventEmitter(); - /** - * 键盘按键事件 - */ - @Output() onKeyup = new EventEmitter(); - /** - * 键盘按下事件 - */ - @Output() onKeydown = new EventEmitter(); - /** - * 控件模型 - */ - @ViewChild('controlModel') controlModel: NgModel; - - /** - * 表单控件包装器 - * @param form 表单 - */ - constructor( @Optional() @Host() private form: NgForm) { - this.floatPlaceholder = "auto"; - this.requiredMessage = MessageConfig.requiredMessage; - } - - /** - * 视图加载完成 - */ - ngAfterViewInit(): void { - this.form && this.form.addControl(this.controlModel); - } - - /** - * 获取值 - */ - get value() { - return this.controlModel.value; - } - - /** - * 模型变更事件处理 - */ - protected onModelChange(value) { - this.modelChange.emit(value); - this.onChange.emit(value); - } - - /** - * 后缀图标单击事件 - */ - protected suffixIconClick() { - this.onSuffixIconClick.emit(); - } - - /** - * 获得焦点事件 - */ - protected focus(event: FocusEvent) { - this.onFocus.emit(event); - } - - /** - * 失去焦点事件 - */ - protected blur(event: FocusEvent) { - this.onBlur.emit(event); - } - - /** - * 键盘按键事件 - */ - protected keyup(event: KeyboardEvent) { - this.onKeyup.emit(event); - } - - /** - * 键盘按下事件 - */ - protected keydown(event: KeyboardEvent) { - this.onKeydown.emit(event); - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/material/button-wrapper.component.ts b/sample/Util.Samples.Webs/Typings/util/material/button-wrapper.component.ts deleted file mode 100644 index 798d63919..000000000 --- a/sample/Util.Samples.Webs/Typings/util/material/button-wrapper.component.ts +++ /dev/null @@ -1,116 +0,0 @@ -//============== 按钮包装器===================== -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { Component, Input, Output, EventEmitter, Host, Optional, ContentChild, TemplateRef} from '@angular/core'; -import { NgForm } from '@angular/forms'; -import { MessageConfig } from '../config/message-config'; - -/** - * Mat按钮包装器 - */ -@Component({ - selector: 'mat-button-wrapper', - template:` - - - - - - - - ` -}) -export class ButtonWrapperComponent { - /** - * 文本 - */ - @Input() text: string; - /** - * 类型,可选值:button,reset,submit - */ - @Input() type: string; - /** - * Mat按钮样式,可选值:mat-button,mat-raised-button,mat-icon-button,mat-fab,mat-mini-fab - */ - @Input() style: string; - /** - * 颜色,可选值:primary,accent,warn - */ - @Input() color: string; - /** - * 禁用 - */ - @Input() disabled: boolean; - /** - * 提示 - */ - @Input() tooltip: string; - /** - * 单击事件 - */ - @Output() onClick = new EventEmitter(); - /** - * 模板,用来支持图标 - */ - @ContentChild(TemplateRef) template: TemplateRef; - - /** - * 初始化Mat按钮包装器 - * @param form 表单 - */ - constructor(@Optional() @Host() private form: NgForm) { - this.type = "button"; - } - - /** - * 是否禁用 - */ - private isDisabled() { - if (this.disabled !== undefined) - return this.disabled; - return this.type === 'submit' && this.form && (!this.form.valid || this.form.submitted); - } - - /** - * 获取文本 - */ - private getText() { - if (this.text !== undefined) - return this.text; - if (this.template) - return null; - if (this.type === 'reset') - return MessageConfig.reset; - if (this.type === 'submit') - return MessageConfig.submit; - return null; - } - - /** - * 单击事件 - */ - private click(event) { - this.onClick.emit(event); - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/material/datepicker-wrapper.component.ts b/sample/Util.Samples.Webs/Typings/util/material/datepicker-wrapper.component.ts deleted file mode 100644 index 86099e53c..000000000 --- a/sample/Util.Samples.Webs/Typings/util/material/datepicker-wrapper.component.ts +++ /dev/null @@ -1,107 +0,0 @@ -//============== 日期选择包装器===================== -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { Component, Input, Host, Optional } from '@angular/core'; -import { NgForm } from '@angular/forms'; -import { FormControlWrapperBase } from './base/form-control-wrapper-base'; -import { MessageConfig } from '../config/message-config'; -import { toDate } from '../common/helper'; - -/** - * Mat日期选择包装器 - */ -@Component({ - selector: 'mat-datepicker-wrapper', - template: ` - - - - {{startHint}} - {{endHint}} - {{prefixText}}  - - - {{suffixMaterialIcon}} - - {{suffixText}} - {{requiredMessage}} - - `, - host: { - 'class': 'util-form-field', - } -}) -export class DatePickerWrapperComponent extends FormControlWrapperBase { - /** - * 清除按钮提示 - */ - private clearButtonTooltip: string; - /** - * 是否显示清除按钮 - */ - @Input() showClearButton: boolean; - /** - * 只读 - */ - @Input() readonly: boolean; - /** - * 起始视图,可选值:year,month - */ - @Input() startView: string; - /** - * 触摸模式 - */ - @Input() touchUi: boolean; - /** - * 宽度 - */ - @Input() width: number|undefined; - /** - * 最小日期 - */ - private min: string | Date; - /** - * 最小日期 - */ - @Input() - get minDate(): string | Date { - return this.min; - } - set minDate(value: string | Date) { - this.min = toDate(value); - } - /** - * 最大日期 - */ - private max: string | Date; - /** - * 最大日期 - */ - @Input() - get maxDate(): string | Date { - return this.max; - } - set maxDate(value: string | Date) { - this.max = toDate(value); - } - - /** - * 初始化Mat日期选择包装器 - * @param form 表单 - */ - constructor( @Optional() @Host() form: NgForm) { - super(form); - this.startView = 'month'; - this.readonly = true; - this.clearButtonTooltip = MessageConfig.clear; - this.showClearButton = true; - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/material/local/date-adapter.ts b/sample/Util.Samples.Webs/Typings/util/material/local/date-adapter.ts deleted file mode 100644 index f89ef23d4..000000000 --- a/sample/Util.Samples.Webs/Typings/util/material/local/date-adapter.ts +++ /dev/null @@ -1,252 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * 汉化:何镇汐 - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import { Inject, Injectable, Optional } from '@angular/core'; -import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material'; -import { formatDate, toDate } from "../../common/helper" - -/** The default month names to use if Intl API is not available. */ -const DEFAULT_MONTH_NAMES = { - 'long': [ - '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', - '十月', '十一月', '十二月' - ], - 'short': ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], - 'narrow': ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'] -}; - - -/** The default date names to use if Intl API is not available. */ -const DEFAULT_DATE_NAMES = range(31, i => String(i + 1)); - - -/** The default day of the week names to use if Intl API is not available. */ -const DEFAULT_DAY_OF_WEEK_NAMES = { - 'long': ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], - 'short': ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], - 'narrow': ['日', '一', '二', '三', '四', '五', '六'] -}; - - -/** - * Matches strings that have the form of a valid RFC 3339 string - * (https://tools.ietf.org/html/rfc3339). Note that the string may not actually be a valid date - * because the regex will match strings an with out of bounds month, date, etc. - */ -const ISO_8601_REGEX = - /^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|(?:(?:\+|-)\d{2}:\d{2}))?)?$/; - - -/** Creates an array and fills it with values. */ -function range(length: number, valueFunction: (index: number) => T): T[] { - const valuesArray = Array(length); - for (let i = 0; i < length; i++) { - valuesArray[i] = valueFunction(i); - } - return valuesArray; -} - -/** Adapts the native JS Date for use with cdk-based components that work with dates. */ -@Injectable() -export class UtilDateAdapter extends DateAdapter { - /** - * Whether to use `timeZone: 'utc'` with `Intl.DateTimeFormat` when formatting dates. - * Without this `Intl.DateTimeFormat` sometimes chooses the wrong timeZone, which can throw off - * the result. (e.g. in the en-US locale `new Date(1800, 7, 14).toLocaleDateString()` - * will produce `'8/13/1800'`. - */ - useUtcForDisplay: boolean; - - constructor( @Optional() @Inject(MAT_DATE_LOCALE) matDateLocale: string) { - super(); - super.setLocale(matDateLocale); - - // IE does its own time zone correction, so we disable this on IE. - // TODO(mmalerba): replace with !platform.TRIDENT, logic currently duplicated to avoid breaking - // change from injecting the Platform. - this.useUtcForDisplay = !(typeof document === 'object' && !!document && - /(msie|trident)/i.test(navigator.userAgent)); - } - - getYear(date: Date): number { - return date.getFullYear(); - } - - getMonth(date: Date): number { - return date.getMonth(); - } - - getDate(date: Date): number { - return date.getDate(); - } - - getDayOfWeek(date: Date): number { - return date.getDay(); - } - - getMonthNames(style: 'long' | 'short' | 'narrow'): string[] { - return DEFAULT_MONTH_NAMES[style]; - } - - getDateNames(): string[] { - return DEFAULT_DATE_NAMES; - } - - getDayOfWeekNames(style: 'long' | 'short' | 'narrow'): string[] { - return DEFAULT_DAY_OF_WEEK_NAMES[style]; - } - - getYearName(date: Date): string { - return `${this.getYear(date)}年`; - } - - getFirstDayOfWeek(): number { - // We can't tell using native JS Date what the first day of the week is, we default to Sunday. - return 0; - } - - getNumDaysInMonth(date: Date): number { - return this.getDate(this._createDateWithOverflow( - this.getYear(date), this.getMonth(date) + 1, 0)); - } - - clone(date: Date): Date { - return this.createDate(this.getYear(date), this.getMonth(date), this.getDate(date)); - } - - createDate(year: number, month: number, date: number): Date { - // Check for invalid month and date (except upper bound on date which we have to check after - // creating the Date). - if (month < 0 || month > 11) { - throw Error(`Invalid month index "${month}". Month index has to be between 0 and 11.`); - } - - if (date < 1) { - throw Error(`Invalid date "${date}". Date has to be greater than 0.`); - } - - let result = this._createDateWithOverflow(year, month, date); - - // Check that the date wasn't above the upper bound for the month, causing the month to overflow - if (result.getMonth() != month) { - throw Error(`Invalid date "${date}" for month with index "${month}".`); - } - - return result; - } - - today(): Date { - return new Date(); - } - - parse(value: any): Date | null { - return toDate(value); - } - - format(date: Date, displayFormat: Object): string { - return this._stripDirectionalityCharacters(formatDate(date, "YYYY-MM-DD")); - } - - addCalendarYears(date: Date, years: number): Date { - return this.addCalendarMonths(date, years * 12); - } - - addCalendarMonths(date: Date, months: number): Date { - let newDate = this._createDateWithOverflow( - this.getYear(date), this.getMonth(date) + months, this.getDate(date)); - - // It's possible to wind up in the wrong month if the original month has more days than the new - // month. In this case we want to go to the last day of the desired month. - // Note: the additional + 12 % 12 ensures we end up with a positive number, since JS % doesn't - // guarantee this. - if (this.getMonth(newDate) != ((this.getMonth(date) + months) % 12 + 12) % 12) { - newDate = this._createDateWithOverflow(this.getYear(newDate), this.getMonth(newDate), 0); - } - - return newDate; - } - - addCalendarDays(date: Date, days: number): Date { - return this._createDateWithOverflow( - this.getYear(date), this.getMonth(date), this.getDate(date) + days); - } - - toIso8601(date: Date): string { - return [ - date.getUTCFullYear(), - this._2digit(date.getUTCMonth() + 1), - this._2digit(date.getUTCDate()) - ].join('-'); - } - - /** - * Returns the given value if given a valid Date or null. Deserializes valid ISO 8601 strings - * (https://www.ietf.org/rfc/rfc3339.txt) into valid Dates and empty string into null. Returns an - * invalid date for all other values. - */ - deserialize(value: any): Date | null { - if (typeof value === 'string') { - if (!value) { - return null; - } - // The `Date` constructor accepts formats other than ISO 8601, so we need to make sure the - // string is the right format first. - if (ISO_8601_REGEX.test(value)) { - let date = new Date(value); - if (this.isValid(date)) { - return date; - } - } - } - return super.deserialize(value); - } - - isDateInstance(obj: any) { - return obj instanceof Date; - } - - isValid(date: Date) { - return !isNaN(date.getTime()); - } - - invalid(): Date { - return new Date(NaN); - } - - /** Creates a date but allows the month and date to overflow. */ - private _createDateWithOverflow(year: number, month: number, date: number) { - let result = new Date(year, month, date); - - // We need to correct for the fact that JS native Date treats years in range [0, 99] as - // abbreviations for 19xx. - if (year >= 0 && year < 100) { - result.setFullYear(this.getYear(result) - 1900); - } - return result; - } - - /** - * Pads a number to make it two digits. - * @param n The number to pad. - * @returns The padded number. - */ - private _2digit(n: number) { - return ('00' + n).slice(-2); - } - - /** - * Strip out unicode LTR and RTL characters. Edge and IE insert these into formatted dates while - * other browsers do not. We remove them to make output consistent and because they interfere with - * date parsing. - * @param str The string to strip direction characters from. - * @returns The stripped string. - */ - private _stripDirectionalityCharacters(str: string) { - return str.replace(/[\u200e\u200f]/g, ''); - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/material/radio-wrapper.component.ts b/sample/Util.Samples.Webs/Typings/util/material/radio-wrapper.component.ts deleted file mode 100644 index b2d338ed1..000000000 --- a/sample/Util.Samples.Webs/Typings/util/material/radio-wrapper.component.ts +++ /dev/null @@ -1,174 +0,0 @@ -//============== 单选按钮包装器=================== -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { Component, Input, Output, EventEmitter, OnInit, Host, Optional, ViewChild, AfterViewInit, ViewEncapsulation } from '@angular/core'; -import { NgModel,NgForm } from '@angular/forms'; -import { Select, SelectItem, SelectOption } from '../core/select'; -import { WebApi as webapi } from '../common/webapi'; - -/** - * Mat单选按钮包装器 - */ -@Component({ - selector: 'mat-radio-wrapper', - template:` -
- - - - {{ item.text }} - - -
- `, - host: { - 'class': 'util-form-field', - }, - styles: [` - .radio-layout-vertical { - display: inline-flex; - flex-direction: column; - } - .radio-label{ - margin-bottom: 8px; - color:rgba(0,0,0,.54); - } - .radio-required{ - color: red; - } - .radio-button-horizontal { - margin-right: 12px; - } - .radio-button-vertical { - margin-bottom: 5px; - } - `] -}) -export class RadioWrapperComponent implements OnInit, AfterViewInit { - /** - * 名称 - */ - @Input() name: string; - /** - * 是否垂直布局 - */ - @Input() vertical: boolean; - /** - * 是否显示标签 - */ - @Input() showLabel: boolean; - /** - * 标签文本 - */ - @Input() label: string; - /** - * 标签文本位置,可选值:'before', 'after' - */ - @Input() labelPosition: string; - /** - * 禁用 - */ - @Input() disabled: boolean; - /** - * 必填项 - */ - @Input() required: boolean; - /** - * 数据源 - */ - @Input() dataSource: SelectOption[]; - /** - * 请求地址 - */ - @Input() url: string; - /** - * 模型,用于双向绑定 - */ - @Input() model; - /** - * 模型变更事件,用于双向绑定 - */ - @Output() modelChange = new EventEmitter(); - /** - * 变更事件 - */ - @Output() onChange = new EventEmitter(); - /** - * 控件模型 - */ - @ViewChild('controlModel') controlModel: NgModel; - - /** - * 初始化Mat单选按钮包装器 - */ - constructor( @Optional() @Host() private form: NgForm) { - this.showLabel = true; - } - - /** - * 组件初始化 - */ - ngOnInit() { - if (this.dataSource) - return; - this.loadUrl(); - } - - /** - * 加载数据 - * @param data 列表项集合 - */ - loadData(data?: SelectItem[]) { - if (!data) - return; - let select = new Select(data); - this.dataSource = select.toOptions(); - } - - /** - * 从服务器加载 - * @param url 请求地址 - */ - loadUrl(url?: string) { - url = url || this.url; - if (!url) { - console.log("请设置单选按钮Url"); - return; - } - webapi.get(url).handle({ - handler: result => { - this.loadData(result); - } - }); - } - - /** - * 视图加载完成 - */ - ngAfterViewInit(): void { - this.form && this.form.addControl(this.controlModel); - } - - /** - * 获取值 - */ - get value() { - return this.controlModel.value; - } - - /** - * 模型变更事件处理 - */ - protected onModelChange(value) { - this.modelChange.emit(value); - this.onChange.emit(value); - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/material/select-wrapper.component.ts b/sample/Util.Samples.Webs/Typings/util/material/select-wrapper.component.ts deleted file mode 100644 index 416929202..000000000 --- a/sample/Util.Samples.Webs/Typings/util/material/select-wrapper.component.ts +++ /dev/null @@ -1,147 +0,0 @@ -//============== 下拉列表包装器=================== -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { Component, Input, OnInit, Host, Optional } from '@angular/core'; -import { NgForm } from '@angular/forms'; -import { FormControlWrapperBase } from './base/form-control-wrapper-base'; -import { Select, SelectItem, SelectOption, SelectOptionGroup } from '../core/select'; -import { WebApi as webapi } from '../common/webapi'; -import { MessageConfig } from '../config/message-config'; - -/** - * Mat下拉列表包装器 - */ -@Component({ - selector: 'mat-select-wrapper', - template: ` - - - {{getTemplate(control.triggerValue)}} - {{resetOptionText }} - - - {{ item.text }} - - - - - - {{ item.text }} - - - - - {{startHint}} - {{endHint}} - {{prefixText}}  - {{suffixMaterialIcon}} - - {{suffixText}} - {{requiredMessage}} - - `, - host: { - 'class': 'util-form-field', - } -}) -export class SelectWrapperComponent extends FormControlWrapperBase implements OnInit { - /** - * 按组显示 - */ - private isGroup: boolean; - /** - * 列表项集合 - */ - private options: SelectOption[]; - /** - * 列表组集合 - */ - private optionGroups: SelectOptionGroup[]; - /** - * 数据源 - */ - @Input() dataSource: SelectItem[]; - /** - * 请求地址 - */ - @Input() url: string; - /** - * 多选 - */ - @Input() multiple: boolean; - /** - * 启用重置项 - */ - @Input() enableResetOption: boolean; - /** - * 重置项文本 - */ - @Input() resetOptionText: string; - /** - * 显示模板,值用{0}表示,范例:当前选中:{0} ,显示为 当前选中:1,2,3 - */ - @Input() template: string; - - /** - * 初始化Mat下拉列表包装器 - */ - constructor( @Optional() @Host() form: NgForm ) { - super(form); - this.enableResetOption = true; - this.resetOptionText = MessageConfig.resetOptionText; - } - - /** - * 组件初始化 - */ - ngOnInit() { - this.loadData(); - if (this.dataSource) - return; - this.loadUrl(); - } - - /** - * 加载数据 - * @param data 列表项集合 - */ - loadData(data?: SelectItem[]) { - data = data || this.dataSource; - if (!data) - return; - let select = new Select(data); - if (select.isGroup()) { - this.isGroup = true; - this.optionGroups = select.toGroups(); - return; - } - this.options = select.toOptions(); - } - - /** - * 从服务器加载 - * @param url 请求地址 - */ - loadUrl(url?: string) { - url = url || this.url; - if (!url) { - console.log("请设置下拉列表Url"); - return; - } - webapi.get(url).handle({ - handler: result => { - this.loadData(result); - } - }); - } - - /** - * 获取模板 - */ - private getTemplate(value) { - return this.template.replace(/\{0\}/g, value); - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/material/table-wrapper.component.ts b/sample/Util.Samples.Webs/Typings/util/material/table-wrapper.component.ts deleted file mode 100644 index 49feb769b..000000000 --- a/sample/Util.Samples.Webs/Typings/util/material/table-wrapper.component.ts +++ /dev/null @@ -1,317 +0,0 @@ -//============== 表格包装器======================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { Component, Input, ViewChild, OnInit, ContentChild } from '@angular/core'; -import { MatTableDataSource, MatPaginator, MatPaginatorIntl, MatSort } from '@angular/material'; -import { SelectionModel } from '@angular/cdk/collections'; -import { WebApi as webapi } from '../common/webapi'; -import { Message as message } from '../common/message'; -import { PagerList } from '../core/pager-list'; -import { ViewModel } from '../core/view-model'; -import { QueryParameter } from '../core/query-parameter'; -import { MessageConfig as config } from '../config/message-config'; - -/** - * 创建分页本地化提示 - */ -function createMatPaginatorIntl() { - let result = new MatPaginatorIntl(); - result.itemsPerPageLabel = "每页"; - result.nextPageLabel = "下一页"; - result.previousPageLabel = "上一页"; - result.getRangeLabel = (page: number, pageSize: number, length: number) => { - if (length == 0 || pageSize == 0) { return `0`; } - length = Math.max(length, 0); - const startIndex = page * pageSize; - const endIndex = startIndex < length ? Math.min(startIndex + pageSize, length) : startIndex + pageSize; - return `当前:${startIndex + 1} - ${endIndex},共: ${length}`; - }; - return result; -} - -/** - * Mat表格包装器 - */ -@Component({ - selector: 'table-wrapper', - providers: [{ provide: MatPaginatorIntl, useFactory: createMatPaginatorIntl }], - template: ` -
-
- -
- - -
- `, - styles: [` - .table-container { - display: flex; - flex-direction: column; - position: relative; - } - .table-loading-shade { - position: absolute; - top: 0; - left: 0; - bottom: 56px; - right: 0; - background: rgba(0, 0, 0, 0.15); - z-index: 1; - display: flex; - align-items: center; - justify-content: center; - } - ::ng-deep .mat-table { - overflow: auto; - } - ::ng-deep .table-header { - min-height: 64px; - padding: 8px 24px 0; - z-index: 100; - } - ::ng-deep .mat-form-field { - width: 100%; - } - `] -}) -export class TableWrapperComponent implements OnInit { - /** - * 显示进度条 - */ - private loading: boolean; - /** - * 总行数 - */ - private totalCount = 0; - /** - * 数据源 - */ - dataSource: MatTableDataSource; - /** - * 是否自动加载,默认在初始化时自动加载数据,设置成false则手工加载 - */ - @Input() autoLoad: boolean; - /** - * 最大高度 - */ - @Input() maxHeight: number; - /** - * 最小高度 - */ - @Input() minHeight: number; - /** - * 最小宽度 - */ - @Input() minWidth: number; - /** - * 宽度 - */ - @Input() width: number; - /** - * 分页长度列表 - */ - @Input() pageSizeItems: number[]; - /** - * 基地址,基于该地址构建请求和删除地址,范例:传入test,则请求地址为/api/test,删除地址为/api/test/delete - */ - @Input() baseUrl: string; - /** - * 请求地址,如果设置了基地址baseUrl,则可以省略该参数 - */ - @Input() url: string; - /** - * 查询参数 - */ - @Input() queryParam: QueryParameter; - /** - * 排序组件 - */ - @ContentChild(MatSort) sort: MatSort; - /** - * 分页组件 - */ - @ViewChild(MatPaginator) paginator: MatPaginator; - /** - * 选中列表 - */ - selection = new SelectionModel(true, []); - - /** - * 初始化Mat表格包装器 - */ - constructor() { - this.maxHeight = 500; - this.minHeight = 300; - this.minWidth = 300; - this.pageSizeItems = [20, 50, 100, 200]; - this.dataSource = new MatTableDataSource(); - this.loading = false; - this.autoLoad = true; - } - - /** - * 组件初始化 - */ - ngOnInit() { - this.initPaginator(); - this.initSort(); - if (this.autoLoad) - this.init(); - } - - /** - * 初始化分页组件 - */ - private initPaginator() { - this.paginator.page.subscribe(() => { - this.queryParam.page = this.paginator.pageIndex + 1; - this.queryParam.pageSize = this.paginator.pageSize; - this.query(); - }); - } - - /** - * 初始化排序组件 - */ - private initSort() { - this.sort.sortChange.subscribe(() => { - this.queryParam.order = `${this.sort.active} ${this.sort.direction}`; - this.query(); - }); - } - - /** - * 初始化 - */ - init() { - this.queryParam.page = 1; - if (this.pageSizeItems && this.pageSizeItems.length > 0) - this.queryParam.pageSize = this.pageSizeItems[0]; - this.query(); - } - - /** - * 发送查询请求 - */ - query() { - let url = this.url || (this.baseUrl && `/api/${this.baseUrl}`); - if (!url) { - console.log("表格url未设置"); - return; - } - webapi.get>(url).param(this.queryParam).handle({ - beforeHandler: () => { this.loading = true; return true; }, - handler: result => { - result = new PagerList(result); - result.initLineNumbers(); - this.dataSource.data = result.data; - this.paginator.pageIndex = result.page - 1; - this.totalCount = result.totalCount; - this.selection.clear(); - }, - completeHandler: () => this.loading = false - }); - } - - /** - * 刷新表格 - */ - refresh() { - this.query(); - } - - /** - * 获取样式 - */ - private getStyle() { - return { - 'max-height': `${this.maxHeight}px`, - 'min-width': `${this.minWidth}px`, - 'width': this.width ? `${this.width}px` : null - }; - } - - /** - * 表头主复选框切换选中状态 - */ - masterToggle() { - if (this.isMasterChecked()) { - this.selection.clear(); - return; - } - this.dataSource.data.forEach(data => this.selection.select(data)); - } - - /** - * 表头主复选框的选中状态 - */ - isMasterChecked() { - return this.selection.hasValue() && - this.isAllSelected() && - this.selection.selected.length >= this.dataSource.data.length; - } - - /** - * 是否所有行复选框被选中 - */ - private isAllSelected() { - return this.dataSource.data.every(data => this.selection.isSelected(data)); - } - - /** - * 表头主复选框的确定状态 - */ - isMasterIndeterminate() { - return this.selection.hasValue() && (!this.isAllSelected() || !this.dataSource.data.length); - } - - /** - * 获取被选中实体列表 - */ - getSelected(): T[] { - return this.dataSource.data.filter(data => this.selection.isSelected(data)); - } - - /** - * 获取被选中实体Id列表 - */ - getSelectedIds(): string { - return this.getSelected().map((value) => value.id).join(","); - } - - /** - * 批量删除被选中实体 - * @param ids 待删除的Id列表,多个Id用逗号分隔,范例:1,2,3 - * @param handler 删除成功回调函数 - * @param deleteUrl 服务端删除Api地址,如果设置了基地址baseUrl,则可以省略该参数 - */ - delete(ids?: string, handler?: () => {}, deleteUrl?: string) { - ids = ids || this.getSelectedIds(); - if (!ids) { - message.warn(config.deleteNotSelected); - return; - } - message.confirm(config.deleteConfirm, () => { - this.deleteRequest(ids, handler, deleteUrl); - }); - } - - /** - * 发送删除请求 - */ - private deleteRequest(ids?: string, handler?: () => {}, deleteUrl?: string) { - deleteUrl = deleteUrl || `/api/${this.baseUrl}/delete`; - webapi.post(deleteUrl, ids).handle({ - handler: () => { - if (handler) { - handler(); - return; - } - message.success(config.deleteSuccessed); - this.refresh(); - } - }); - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/material/textarea-wrapper.component.ts b/sample/Util.Samples.Webs/Typings/util/material/textarea-wrapper.component.ts deleted file mode 100644 index 5dbc56e2d..000000000 --- a/sample/Util.Samples.Webs/Typings/util/material/textarea-wrapper.component.ts +++ /dev/null @@ -1,91 +0,0 @@ -//============== 多行文本框包装器================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { Component, Input, Host, Optional } from '@angular/core'; -import { NgForm } from '@angular/forms'; -import { FormControlWrapperBase } from './base/form-control-wrapper-base'; -import { MessageConfig } from '../config/message-config'; - -/** - * Mat多行文本框包装器 - */ -@Component({ - selector: 'mat-textarea-wrapper', - template:` - - - {{startHint}} - {{endHint}} - {{prefixText}}  - - {{suffixMaterialIcon}} - - {{suffixText}} - {{getErrorMessage()}} - - `, - host: { - 'class': 'util-form-field', - } -}) -export class TextareaWrapperComponent extends FormControlWrapperBase { - /** - * 是否显示清除按钮 - */ - @Input() showClearButton: boolean; - /** - * 最小行数 - */ - @Input() minRows: number; - /** - * 最大行数 - */ - @Input() maxRows: number; - /** - * 只读 - */ - @Input() readonly: boolean; - /** - * 最小长度 - */ - @Input() minLength: number; - /** - * 最小长度验证消息 - */ - @Input() minLengthMessage: string; - /** - * 最大长度 - */ - @Input() maxLength: number; - - /** - * 初始化Mat多行文本框包装器 - * @param form 表单 - */ - constructor( @Optional() @Host() form: NgForm ) { - super(form); - this.showClearButton = true; - this.minRows = 1; - this.maxRows = 8; - } - - /** - * 获取错误消息 - */ - private getErrorMessage(): string { - if (!this.controlModel) - return ""; - if (this.controlModel.hasError('required')) - return this.requiredMessage; - if (this.controlModel.hasError('minlength')) - return this.minLengthMessage || MessageConfig.minLengthMessage.replace(/\{0\}/, String(this.minLength)); - return ""; - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/material/textbox-wrapper.component.ts b/sample/Util.Samples.Webs/Typings/util/material/textbox-wrapper.component.ts deleted file mode 100644 index 4197df2b5..000000000 --- a/sample/Util.Samples.Webs/Typings/util/material/textbox-wrapper.component.ts +++ /dev/null @@ -1,141 +0,0 @@ -//============== 文本框包装器===================== -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { Component, Input, OnInit, Host, Optional } from '@angular/core'; -import { NgForm } from '@angular/forms'; -import { FormControlWrapperBase } from './base/form-control-wrapper-base'; -import { MessageConfig } from '../config/message-config'; - -/** - * Mat文本框包装器 - */ -@Component({ - selector: 'mat-textbox-wrapper', - template: ` - - - {{startHint}} - {{endHint}} - {{prefixText}}  - - {{suffixMaterialIcon}} - - {{suffixText}} - {{getErrorMessage()}} - - `, - host: { - 'class': 'util-form-field', - } -}) -export class TextBoxWrapperComponent extends FormControlWrapperBase implements OnInit { - /** - * 是否密码框 - */ - private isPassword: boolean; - /** - * 密码显示隐藏开关 - */ - private hide: boolean; - /** - * 清除按钮提示 - */ - private clearButtonTooltip: string; - /** - * 是否显示清除按钮 - */ - @Input() showClearButton: boolean; - /** - * 文本框类型,可选值:text,password,number,email,date - */ - @Input() type: string; - /** - * 只读 - */ - @Input() readonly: boolean; - /** - * 最小长度 - */ - @Input() minLength: number; - /** - * 最小长度验证消息 - */ - @Input() minLengthMessage: string; - /** - * 最大长度 - */ - @Input() maxLength: number; - /** - * 电子邮件验证消息 - */ - @Input() emailMessage: string; - - /** - * 初始化Mat文本框包装器 - * @param form 表单 - */ - constructor(@Optional() @Host() form: NgForm) { - super(form); - this.clearButtonTooltip = MessageConfig.clear; - this.showClearButton = true; - } - - /** - * 组件初始化 - */ - ngOnInit() { - this.initPassword(); - } - - /** - * 初始化密码框 - */ - private initPassword() { - if (this.type !== "password") - return; - this.isPassword = true; - this.hide = true; - this.togglePassword(); - } - - /** - * 切换密码显示状态 - */ - private togglePassword() { - this.suffixMaterialIcon = this.hide ? 'visibility' : 'visibility_off'; - this.type = this.hide ? 'password' : 'text'; - this.hide = !this.hide; - } - - /** - * 后缀图标单击事件 - */ - protected suffixIconClick() { - super.suffixIconClick(); - if (this.isPassword) - this.togglePassword(); - } - - /** - * 获取错误消息 - */ - private getErrorMessage(): string { - if (!this.controlModel) - return ""; - if (this.controlModel.hasError('required')) - return this.requiredMessage; - if (this.controlModel.hasError('minlength')) - return this.minLengthMessage || MessageConfig.minLengthMessage.replace(/\{0\}/, String(this.minLength)); - if (this.controlModel.hasError('email')) - return this.emailMessage || MessageConfig.emailMessage; - return ""; - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/prime/file-upload.component.ts b/sample/Util.Samples.Webs/Typings/util/prime/file-upload.component.ts deleted file mode 100644 index 0783f68d1..000000000 --- a/sample/Util.Samples.Webs/Typings/util/prime/file-upload.component.ts +++ /dev/null @@ -1,92 +0,0 @@ -//============== 文件上传========================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { Component, NgZone, ElementRef } from '@angular/core'; -import { DomSanitizer } from '@angular/platform-browser'; -import { FileUpload, DomHandler } from 'primeng/primeng'; -import { MessageConfig } from '../config/message-config'; - -/** - * 文件上传,基于Prime文件上传组件 - */ -@Component({ - selector: 'prime-file-upload', - template:` -
-
- - - - - - -
-
- - - - -
-
-
-
-
{{file.name}}
-
{{formatSize(file.size)}}
-
- -
-
-
-
- -
-
- -
-
- - `, - styles: [` - .mat-progress-bar{ - height:2px; - } - `], - providers: [DomHandler] -}) -export class FileUploadComponent extends FileUpload { - /** - * 初始化文件上传 - */ - constructor(el: ElementRef, domHandler: DomHandler, sanitizer: DomSanitizer, zone: NgZone) { - super(el, domHandler, sanitizer, zone); - this.chooseLabel = MessageConfig.chooseFile; - this.uploadLabel = MessageConfig.upload; - this.cancelLabel = MessageConfig.cancel; - } - - /** - * 获取进度条样式 - */ - private getProgressStyles() { - return { - 'position': 'absolute', - 'top': '0px', - 'left': '0px' - }; - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/prime/single-file-upload.component.ts b/sample/Util.Samples.Webs/Typings/util/prime/single-file-upload.component.ts deleted file mode 100644 index 166b8c559..000000000 --- a/sample/Util.Samples.Webs/Typings/util/prime/single-file-upload.component.ts +++ /dev/null @@ -1,43 +0,0 @@ -//============== 单文件上传====================== -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { Component, EventEmitter, Input } from '@angular/core'; -import { MessageConfig } from '../config/message-config'; -import { FileInfo,FileType } from '../core/file-info'; - -/** - * 单文件上传 - */ -@Component({ - selector: 'single-file-upload', - template:` - - - - 下载 - - - ` -}) -export class SingleFileUploadComponent { - /** - * 上传地址 - */ - @Input() url: string; - - private images: any[]; - - private file:FileInfo; - - constructor() { - this.images = new Array(); - } - - upload(result) { - this.file = JSON.parse(result.xhr.responseText); - this.file.type = FileType.Image; - this.images = new Array(); - this.images.push({ source: `${this.file.url}`, thumbnail: `${this.file.url}`, title: this.file.fileName }); - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/util.module.ts b/sample/Util.Samples.Webs/Typings/util/util.module.ts deleted file mode 100644 index 4f20a9920..000000000 --- a/sample/Util.Samples.Webs/Typings/util/util.module.ts +++ /dev/null @@ -1,78 +0,0 @@ -//============== util模块========================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import { NgModule } from '@angular/core'; -//Angular模块 -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; -import { RouterModule } from '@angular/router'; -import { HttpClientModule } from "@angular/common/http"; -//Material模块 -import { - MatCommonModule, MatRippleModule, MatSortModule, - MatFormFieldModule, MatSelectModule, MatInputModule, MatDatepickerModule, MatAutocompleteModule, - MatCheckboxModule, MatSlideToggleModule, MatRadioModule, - MatSnackBarModule, MatProgressBarModule, MatMenuModule, MatTableModule, MatTabsModule, - MatSidenavModule, MatToolbarModule, MatCardModule, MatExpansionModule, MatGridListModule, - MatProgressSpinnerModule, MatPaginatorModule, MatIconModule, MatButtonModule, MatTooltipModule, - DateAdapter, MAT_DATE_LOCALE_PROVIDER, MAT_DATE_FORMATS, MAT_NATIVE_DATE_FORMATS, MAT_DATE_LOCALE -} from '@angular/material'; -//PrimeNg模块 -import { - ButtonModule, GrowlModule, MessageModule, MessagesModule, ConfirmDialogModule, ConfirmationService, - FileUploadModule, LightboxModule -} from 'primeng/primeng'; -//Util组件 -import { UtilDateAdapter } from './material/local/date-adapter'; -import { MessageService } from 'primeng/components/common/messageservice'; -import { TableWrapperComponent } from './material/table-wrapper.component'; -import { SelectWrapperComponent } from './material/select-wrapper.component'; -import { TextBoxWrapperComponent } from './material/textbox-wrapper.component'; -import { TextareaWrapperComponent } from './material/textarea-wrapper.component'; -import { DatePickerWrapperComponent } from './material/datepicker-wrapper.component'; -import { ButtonWrapperComponent } from './material/button-wrapper.component'; -import { RadioWrapperComponent } from './material/radio-wrapper.component'; -import { FileUploadComponent } from './prime/file-upload.component'; -import { SingleFileUploadComponent } from "./prime/single-file-upload.component"; -/** - * Util模块 - */ -@NgModule({ - imports: [ - CommonModule, FormsModule, RouterModule, HttpClientModule, - MatCommonModule, MatRippleModule, MatSortModule, - MatFormFieldModule, MatSelectModule, MatInputModule, MatDatepickerModule, MatAutocompleteModule, - MatCheckboxModule, MatSlideToggleModule, MatRadioModule, - MatSnackBarModule, MatProgressBarModule, MatMenuModule, MatTableModule, MatTabsModule, - MatSidenavModule, MatToolbarModule, MatCardModule, MatExpansionModule, MatGridListModule, - MatProgressSpinnerModule, MatPaginatorModule, MatIconModule, MatButtonModule, MatTooltipModule, - ButtonModule, GrowlModule, MessageModule, MessagesModule, ConfirmDialogModule, FileUploadModule, LightboxModule - ], - declarations: [ - TableWrapperComponent, SelectWrapperComponent, TextBoxWrapperComponent, TextareaWrapperComponent, - DatePickerWrapperComponent, ButtonWrapperComponent, RadioWrapperComponent, - FileUploadComponent, SingleFileUploadComponent - ], - exports: [ - CommonModule, FormsModule, RouterModule, HttpClientModule, - MatCommonModule, MatRippleModule, MatSortModule, - MatFormFieldModule, MatSelectModule, MatInputModule, MatDatepickerModule, MatAutocompleteModule, - MatCheckboxModule, MatSlideToggleModule, MatRadioModule, - MatSnackBarModule, MatProgressBarModule, MatMenuModule, MatTableModule, MatTabsModule, - MatSidenavModule, MatToolbarModule, MatCardModule, MatExpansionModule, MatGridListModule, - MatProgressSpinnerModule, MatPaginatorModule, MatIconModule, MatButtonModule, MatTooltipModule, - ButtonModule, GrowlModule, MessageModule, MessagesModule, ConfirmDialogModule, FileUploadModule, LightboxModule, - TableWrapperComponent, SelectWrapperComponent, TextBoxWrapperComponent, TextareaWrapperComponent, - DatePickerWrapperComponent, ButtonWrapperComponent, RadioWrapperComponent, - FileUploadComponent, SingleFileUploadComponent - ], - providers: [ - MessageService, ConfirmationService, MAT_DATE_LOCALE_PROVIDER, - { provide: MAT_DATE_LOCALE, useValue: 'zh-cn' }, - { provide: DateAdapter, useClass: UtilDateAdapter }, - { provide: MAT_DATE_FORMATS, useValue: MAT_NATIVE_DATE_FORMATS } - ] -}) -export class UtilModule { -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Typings/util/util.ts b/sample/Util.Samples.Webs/Typings/util/util.ts deleted file mode 100644 index 30c5024af..000000000 --- a/sample/Util.Samples.Webs/Typings/util/util.ts +++ /dev/null @@ -1,50 +0,0 @@ -//============== util操作库======================= -//Copyright 2018 何镇汐 -//Licensed under the MIT license -//================================================ -import * as helper from './common/helper'; -import { IocHelper } from './angular/ioc-helper'; -import { HttpHelper } from './angular/http-helper'; -import { RouterHelper } from './angular/router-helper'; -import { EventHelper } from "./angular/event-helper"; -import { WebApi } from './common/webapi'; -import { Message } from './common/message'; -import { Form } from './common/form'; - -/** - * 操作库 - */ -export class Util { - /** - * 公共操作 - */ - static helper = helper; - /** - * Ioc操作 - */ - static ioc = IocHelper; - /** - * Http操作 - */ - static http = HttpHelper; - /** - * 事件操作 - */ - static event = EventHelper; - /** - * WebApi操作,与服务端返回的标准result对象交互 - */ - static webapi = WebApi; - /** - * 路由操作 - */ - static router = RouterHelper; - /** - * 消息操作 - */ - static message = Message; - /** - * 表单操作 - */ - static form = new Form(); -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Util.Samples.Webs.csproj b/sample/Util.Samples.Webs/Util.Samples.Webs.csproj deleted file mode 100644 index f5cf315e0..000000000 --- a/sample/Util.Samples.Webs/Util.Samples.Webs.csproj +++ /dev/null @@ -1,82 +0,0 @@ - - - - netcoreapp2.0 - aspnet-Util.Samples.Webs-D0551EBC-0589-40AD-ADC2-A88CA4609632 - 何镇汐 - 何镇汐 - - - Util应用框架Web示例项目 - - true - Latest - - - - portable - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Always - - - - - - - - - - - - - - - - - diff --git a/sample/Util.Samples.Webs/Util.Samples.Webs.csproj.user b/sample/Util.Samples.Webs/Util.Samples.Webs.csproj.user deleted file mode 100644 index 3bdb03739..000000000 --- a/sample/Util.Samples.Webs/Util.Samples.Webs.csproj.user +++ /dev/null @@ -1,16 +0,0 @@ - - - - false - 0.0 - IIS Express - FolderProfile - 600 - - - ProjectDebugger - - - ProjectDebugger - - \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Views/Home/Demo.cshtml b/sample/Util.Samples.Webs/Views/Home/Demo.cshtml deleted file mode 100644 index 94c165ef3..000000000 --- a/sample/Util.Samples.Webs/Views/Home/Demo.cshtml +++ /dev/null @@ -1,127 +0,0 @@ -@model Donau.Services.Dtos.Customers.CustomersDto -@{ - var x = Html.Ui(); -} - - - - 工具栏 - - - - - - 标题 - 子标题 - - - - - - - @x.TextBox( t => t.Name ) - - - - - - - - - - - - - - - - @x.Select( t => t.Nation ) - - - - - @*表头*@ - - - - - - - - - - - ID - - - - 姓名 - - - - 昵称 - - - - 电话 - - - - @*数据*@ - - - - - - - - - - - - - - - ID - {{ row.lineNumber }} - - - - 姓名 - {{ row.name }} - - - - 昵称 - {{ row.nickname }} - - - - 电话 - {{ row.mobile }} - - - - - - - - - - - @x.Button().Reset() - @x.Button().Submit() - - - - - diff --git a/sample/Util.Samples.Webs/Views/Home/Index.cshtml b/sample/Util.Samples.Webs/Views/Home/Index.cshtml deleted file mode 100644 index 4b902d70a..000000000 --- a/sample/Util.Samples.Webs/Views/Home/Index.cshtml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Views/Home/Main.cshtml b/sample/Util.Samples.Webs/Views/Home/Main.cshtml deleted file mode 100644 index 53320b733..000000000 --- a/sample/Util.Samples.Webs/Views/Home/Main.cshtml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - 哈哈 - - - - 哈哈 - - - - - - - - 哈哈 - - - 哈哈 - - - - - - - - - - - - - - - - - -@*消息提示*@ - \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Views/Shared/_Layout.cshtml b/sample/Util.Samples.Webs/Views/Shared/_Layout.cshtml deleted file mode 100644 index b542f5d1a..000000000 --- a/sample/Util.Samples.Webs/Views/Shared/_Layout.cshtml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - @ViewBag.Title - - - - - - - @RenderBody() - - - - - @RenderSection( "Scripts", required: false ) - - diff --git a/sample/Util.Samples.Webs/Views/_ViewImports.cshtml b/sample/Util.Samples.Webs/Views/_ViewImports.cshtml deleted file mode 100644 index 7f2816392..000000000 --- a/sample/Util.Samples.Webs/Views/_ViewImports.cshtml +++ /dev/null @@ -1,7 +0,0 @@ -@using Util.Ui.Services -@using Util.Ui.Extensions -@using Util.Ui.Material.Extensions -@using Util.Ui.Material.Enums -@using Util.Ui.Enums -@addTagHelper *,Microsoft.AspNetCore.Mvc.TagHelpers -@addTagHelper *,Util.Ui.Angular \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Views/_ViewStart.cshtml b/sample/Util.Samples.Webs/Views/_ViewStart.cshtml deleted file mode 100644 index a5f10045d..000000000 --- a/sample/Util.Samples.Webs/Views/_ViewStart.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -@{ - Layout = "_Layout"; -} diff --git a/sample/Util.Samples.Webs/appsettings.Development.json b/sample/Util.Samples.Webs/appsettings.Development.json deleted file mode 100644 index fa8ce71a9..000000000 --- a/sample/Util.Samples.Webs/appsettings.Development.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information" - } - } -} diff --git a/sample/Util.Samples.Webs/appsettings.json b/sample/Util.Samples.Webs/appsettings.json deleted file mode 100644 index 26057306c..000000000 --- a/sample/Util.Samples.Webs/appsettings.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "ConnectionStrings": { - "DefaultConnection": "Server=.\\sql2014;Database=UtilTest;uid=sa;pwd=sa;Trusted_Connection=True;MultipleActiveResultSets=true" - }, - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Warning" - } - } -} diff --git a/sample/Util.Samples.Webs/nlog.config b/sample/Util.Samples.Webs/nlog.config deleted file mode 100644 index 9047de043..000000000 --- a/sample/Util.Samples.Webs/nlog.config +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/sample/Util.Samples.Webs/package.json b/sample/Util.Samples.Webs/package.json deleted file mode 100644 index e528dca3a..000000000 --- a/sample/Util.Samples.Webs/package.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "version": "1.0.0", - "name": "util", - "private": true, - "scripts": { - "vendor": "webpack --config webpack.config.vendor.js", - "app": "webpack --config webpack.config.util.js && webpack", - "dev": "npm run vendor && npm run app", - "prod": "webpack --config webpack.config.vendor.js --env.prod && webpack --config webpack.config.util.js --env.prod && webpack --env.prod", - "test": "karma start Typings/test/karma.conf.js" - }, - "dependencies": { - "@angular/animations": "6.0.0-beta.5", - "@angular/common": "6.0.0-beta.5", - "@angular/compiler": "6.0.0-beta.5", - "@angular/core": "6.0.0-beta.5", - "@angular/forms": "6.0.0-beta.5", - "@angular/platform-browser": "6.0.0-beta.5", - "@angular/platform-browser-dynamic": "6.0.0-beta.5", - "@angular/router": "6.0.0-beta.5", - "@angular/cdk": "6.0.0-beta.3", - "@angular/material": "6.0.0-beta.3", - "font-awesome": "4.7.0", - "hammerjs": "2.0.8", - "rxjs": "5.5.6", - "zone.js": "0.8.20", - "es6-shim": "0.35.3", - "es6-promise": "4.2.4", - "event-source-polyfill": "0.0.12", - "babel-polyfill": "6.26.0", - "material-design-icons": "3.0.1", - "primeng": "5.2.0", - "moment": "2.20.1" - }, - "devDependencies": { - "@angular/compiler-cli": "6.0.0-beta.5", - "@types/jasmine": "2.8.6", - "@types/webpack-env": "1.13.5", - "aspnet-webpack": "2.0.3", - "autoprefixer": "8.0.0", - "awesome-typescript-loader": "3.5.0", - "css-loader": "0.28.10", - "extract-text-webpack-plugin": "3.0.2", - "file-loader": "1.1.10", - "jasmine-core": "3.1.0", - "karma": "2.0.0", - "karma-phantomjs-launcher": "1.0.4", - "karma-jasmine": "1.1.1", - "karma-webpack": "2.0.13", - "node-sass": "4.7.2", - "postcss-loader": "2.1.1", - "sass-loader": "6.0.6", - "typescript": "2.7.2", - "url-loader": "0.6.2", - "webpack": "3.11.0", - "webpack-cli": "2.0.9", - "webpack-hot-middleware": "2.21.0", - "angular-router-loader": "0.8.2" - } -} diff --git a/sample/Util.Samples.Webs/tsconfig.json b/sample/Util.Samples.Webs/tsconfig.json deleted file mode 100644 index 28cc34f21..000000000 --- a/sample/Util.Samples.Webs/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compileOnSave": false, - "compilerOptions": { - "target": "es5", - "module": "es2015", - "moduleResolution": "node", - "noImplicitAny": false, - "noEmitOnError": true, - "removeComments": false, - "sourceMap": true, - "experimentalDecorators": true, - "emitDecoratorMetadata": true, - "skipDefaultLibCheck": true, - "skipLibCheck": true, - "strictPropertyInitialization": false, - "strict": true, - "lib": [ "es6", "dom" ], - "types": [ "webpack-env", "jasmine" ] - }, - "exclude": [ "bin", "node_modules" ] -} diff --git a/sample/Util.Samples.Webs/webpack.config.js b/sample/Util.Samples.Webs/webpack.config.js deleted file mode 100644 index 12b2962e1..000000000 --- a/sample/Util.Samples.Webs/webpack.config.js +++ /dev/null @@ -1,87 +0,0 @@ -const pathPlugin = require('path'); -const webpack = require('webpack'); -var Extract = require("extract-text-webpack-plugin"); - -module.exports = (env) => { - //是否开发环境 - const isDev = !(env && env.prod); - - //将css提取到单独文件中 - const extractCss = new Extract("app.css"); - - //获取路径 - function getPath(path) { - return pathPlugin.join(__dirname, path); - } - - //打包js - let jsConfig = { - entry: { app: getPath("Typings/main.ts") }, - output: { - publicPath: 'dist/', - path: getPath("wwwroot/dist"), - filename: "[name].js" - }, - resolve: { - extensions: ['.js', '.ts'] - }, - devtool: "source-map", - module: { - rules: [ - { test: /\.ts$/, use: ['awesome-typescript-loader?silent=true', 'angular-router-loader'] } - ] - }, - plugins: [ - new webpack.DllReferencePlugin({ - manifest: require('./wwwroot/dist/polyfills-manifest.json') - }), - new webpack.DllReferencePlugin({ - manifest: require('./wwwroot/dist/vendor-manifest.json') - }), - new webpack.DllReferencePlugin({ - manifest: require('./wwwroot/dist/util-manifest.json') - }), - new webpack.optimize.ModuleConcatenationPlugin() - ].concat(isDev ? [] : [ - new webpack.optimize.UglifyJsPlugin() - ]) - } - - //打包css - let cssConfig = { - entry: { app: getPath("wwwroot/css/main.scss") }, - output: { - publicPath: './', - path: getPath("wwwroot/dist"), - filename: "[name].css" - }, - resolve: { - modules: ['wwwroot'] - }, - devtool: "source-map", - module: { - rules: [ - { - test: /\.scss$/, use: extractCss.extract({ - use: isDev ? ['css-loader', { loader: 'postcss-loader', options: { plugins: [require('autoprefixer')] } }, 'sass-loader'] - : ['css-loader?minimize', { loader: 'postcss-loader', options: { plugins: [require('autoprefixer')] } }, 'sass-loader'] - }) - }, - { - test: /\.(png|jpg|gif|woff|woff2|eot|ttf|svg)(\?|$)/, use: { - loader: 'url-loader', - options: { - limit: 20000, - name: "[name].[ext]", - outputPath: "images/" - } - } - } - ] - }, - plugins: [ - extractCss - ] - } - return [jsConfig, cssConfig]; -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/webpack.config.util.js b/sample/Util.Samples.Webs/webpack.config.util.js deleted file mode 100644 index 0bea06d0c..000000000 --- a/sample/Util.Samples.Webs/webpack.config.util.js +++ /dev/null @@ -1,48 +0,0 @@ -const pathPlugin = require('path'); -const webpack = require('webpack'); - -//env代表环境变量,如果传入env.production表示正式生产环境 -module.exports = (env) => { - //是否开发环境 - const isDev = !(env && env.prod); - - //获取路径 - function getPath(path) { - return pathPlugin.join(__dirname, path); - } - - //打包util脚本库 - return { - entry: { util: [getPath("Typings/util/index.ts")] }, - output: { - publicPath: 'dist/', - path: getPath("wwwroot/dist"), - filename: "[name].js", - library: '[name]' - }, - resolve: { - extensions: ['.js', '.ts'] - }, - devtool: "source-map", - module: { - rules: [ - { test: /\.ts$/, use: ['awesome-typescript-loader?silent=true'] } - ] - }, - plugins: [ - new webpack.DllReferencePlugin({ - manifest: require('./wwwroot/dist/polyfills-manifest.json') - }), - new webpack.DllReferencePlugin({ - manifest: require('./wwwroot/dist/vendor-manifest.json') - }), - new webpack.DllPlugin({ - path: getPath("wwwroot/dist/[name]-manifest.json"), - name: "[name]" - }), - new webpack.optimize.ModuleConcatenationPlugin() - ].concat(isDev ? [] : [ - new webpack.optimize.UglifyJsPlugin() - ]) - } -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/webpack.config.vendor.js b/sample/Util.Samples.Webs/webpack.config.vendor.js deleted file mode 100644 index 67d4daff6..000000000 --- a/sample/Util.Samples.Webs/webpack.config.vendor.js +++ /dev/null @@ -1,130 +0,0 @@ -const pathPlugin = require('path'); -const webpack = require('webpack'); -var Extract = require("extract-text-webpack-plugin"); - -//支持老浏览器的补丁 -const polyfillModules = [ - 'es6-shim', - 'es6-promise', - 'event-source-polyfill' -]; - -//第三方Js库 -const jsModules = [ - 'moment', - 'reflect-metadata', - 'zone.js', - '@angular/animations', - '@angular/common', - '@angular/common/http', - '@angular/compiler', - '@angular/core', - '@angular/forms', - '@angular/platform-browser', - '@angular/platform-browser/animations', - '@angular/platform-browser-dynamic', - '@angular/router', - 'hammerjs', - '@angular/cdk/esm5/collections.es5', - '@angular/material', - 'primeng/primeng' -]; - -//第三方Css库 -const cssModules = [ - '@angular/material/prebuilt-themes/indigo-pink.css', - 'material-design-icons/iconfont/material-icons.css', - 'font-awesome/css/font-awesome.css', - 'primeng/resources/primeng.css' -]; - -//env代表环境变量,如果传入env.production表示正式生产环境 -module.exports = (env) => { - //是否开发环境 - const isDev = !(env && env.prod); - - //将css提取到单独文件中 - const extractCss = new Extract("vendor.css"); - - //获取路径 - function getPath(path) { - return pathPlugin.join(__dirname, path); - } - - //打包补丁 - let polyfills = { - entry: { polyfills: polyfillModules }, - output: { - publicPath: 'dist/', - path: getPath("wwwroot/dist"), - filename: "[name].js", - library: '[name]' - }, - resolve: { - extensions: ['.js'] - }, - devtool: "source-map", - plugins: [ - new webpack.DllPlugin({ - path: getPath("wwwroot/dist/[name]-manifest.json"), - name: "[name]" - }), - new webpack.optimize.ModuleConcatenationPlugin() - ].concat(isDev ? [] : [new webpack.optimize.UglifyJsPlugin()]) - } - - //打包第三方Js库 - let vendorJs = { - entry: { vendor: jsModules }, - output: { - publicPath: 'dist/', - path: getPath("wwwroot/dist"), - filename: "[name].js", - library: '[name]' - }, - resolve: { - extensions: ['.js'] - }, - devtool: "source-map", - plugins: [ - new webpack.DllPlugin({ - path: getPath("wwwroot/dist/[name]-manifest.json"), - name: "[name]" - }), - new webpack.optimize.ModuleConcatenationPlugin(), - new webpack.ContextReplacementPlugin(/\@angular\b.*\b(bundles|linker)/, getPath('./Typings')), - new webpack.ContextReplacementPlugin(/angular(\\|\/)core(\\|\/)@angular/, getPath('./Typings')), - new webpack.IgnorePlugin(/^vertx$/) - ].concat(isDev ? [] : [new webpack.optimize.UglifyJsPlugin()]) - } - - //打包css - let vendorCss = { - entry: { vendor: cssModules }, - output: { - publicPath: './', - path: getPath("wwwroot/dist"), - filename: "[name].css" - }, - devtool: "source-map", - module: { - rules: [ - { test: /\.css$/, use: extractCss.extract({ use: isDev ? 'css-loader' : 'css-loader?minimize' }) }, - { - test: /\.(png|jpg|gif|woff|woff2|eot|ttf|svg)(\?|$)/, use: { - loader: 'url-loader', - options: { - limit: 20000, - name: "[name].[ext]", - outputPath: "images/" - } - } - } - ] - }, - plugins: [ - extractCss - ] - } - return [polyfills, vendorJs, vendorCss]; -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/wwwroot/css/app/_main.scss b/sample/Util.Samples.Webs/wwwroot/css/app/_main.scss deleted file mode 100644 index 5f282702b..000000000 --- a/sample/Util.Samples.Webs/wwwroot/css/app/_main.scss +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/sample/Util.Samples.Webs/wwwroot/css/common/_main.scss b/sample/Util.Samples.Webs/wwwroot/css/common/_main.scss deleted file mode 100644 index ed5c5c6a2..000000000 --- a/sample/Util.Samples.Webs/wwwroot/css/common/_main.scss +++ /dev/null @@ -1,16 +0,0 @@ -//导入变量 -@import "base/variables"; - -//导入基础样式 -@import "base/base"; - -//导入组件样式 -@import "base/component"; - -//导入primeNg组件样式 -@import "prime/theme"; - -//导入主界面布局样式 -@import 'layout/header'; -@import 'layout/navigation'; -@import 'layout/footer'; \ No newline at end of file diff --git a/sample/Util.Samples.Webs/wwwroot/css/common/base/_base.scss b/sample/Util.Samples.Webs/wwwroot/css/common/base/_base.scss deleted file mode 100644 index 5f282702b..000000000 --- a/sample/Util.Samples.Webs/wwwroot/css/common/base/_base.scss +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/sample/Util.Samples.Webs/wwwroot/css/common/base/_component.scss b/sample/Util.Samples.Webs/wwwroot/css/common/base/_component.scss deleted file mode 100644 index cc9603374..000000000 --- a/sample/Util.Samples.Webs/wwwroot/css/common/base/_component.scss +++ /dev/null @@ -1,49 +0,0 @@ -//mat表格复选框 -.mat-column-selectCheckbox { - max-width: 48px; - overflow: initial !important; -} -.mat-cell, .mat-header-cell { - overflow: initial !important; -} -//mat表格序号 -.mat-column-lineNumber { - max-width: 48px; - justify-content: flex-end; -} -//mat表格行滑过样式 -.mat-row-hover { - position: relative; - &:hover { - background: #f5f5f5; - } - &:active { - background: #eaeaea; - } -} -//mat菜单font-awesome图标 -.mat-menu-item .fa { - vertical-align: middle; - margin-right: 16px; - font-size: 24px; -} -//mat选项卡font-awesome图标 -.mat-tab-label .fa, .mat-tab-link .fa { - font-size: 24px; - margin-right: 3px; -} -//mat工具栏 -.mat-toolbar .mat-icon { - padding: 0 14px; -} -.toolbar-fill-remain { - flex: 1 1 auto; -} -//修复自定义表单组件宽度 -.util-form-field { - width: 100% -} -//重置栅格内容左对齐 -.mat-grid-tile .mat-figure { - justify-content:flex-start; -} \ No newline at end of file diff --git a/sample/Util.Samples.Webs/wwwroot/css/common/base/_variables.scss b/sample/Util.Samples.Webs/wwwroot/css/common/base/_variables.scss deleted file mode 100644 index 5f282702b..000000000 --- a/sample/Util.Samples.Webs/wwwroot/css/common/base/_variables.scss +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/sample/Util.Samples.Webs/wwwroot/css/common/layout/_footer.scss b/sample/Util.Samples.Webs/wwwroot/css/common/layout/_footer.scss deleted file mode 100644 index 5f282702b..000000000 --- a/sample/Util.Samples.Webs/wwwroot/css/common/layout/_footer.scss +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/sample/Util.Samples.Webs/wwwroot/css/common/layout/_header.scss b/sample/Util.Samples.Webs/wwwroot/css/common/layout/_header.scss deleted file mode 100644 index 5f282702b..000000000 --- a/sample/Util.Samples.Webs/wwwroot/css/common/layout/_header.scss +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/sample/Util.Samples.Webs/wwwroot/css/common/layout/_navigation.scss b/sample/Util.Samples.Webs/wwwroot/css/common/layout/_navigation.scss deleted file mode 100644 index 5f282702b..000000000 --- a/sample/Util.Samples.Webs/wwwroot/css/common/layout/_navigation.scss +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/sample/Util.Samples.Webs/wwwroot/css/common/prime/_base.scss b/sample/Util.Samples.Webs/wwwroot/css/common/prime/_base.scss deleted file mode 100644 index fd715d6aa..000000000 --- a/sample/Util.Samples.Webs/wwwroot/css/common/prime/_base.scss +++ /dev/null @@ -1,645 +0,0 @@ -@mixin hover-element() { - border-color: $stateHoverBorderColor; - background: $stateHoverBgColor; - color: $stateHoverTextColor; - - a { - color: $stateHoverTextColor; - } -} - -@mixin icon-override($icon) { - background: none !important; - display: inline-block; - font: normal normal normal 14px/1 FontAwesome; - font-size: inherit; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - text-indent: 0px !important; - text-align: center; - - &:before { - content: $icon; - } -} - -.ui-widget { - font-family: $fontFamily; - font-size: $fontSize; - - input, select, textarea, button { - font-family: $fontFamily; - font-size: $fontSize; - } - - :active { - outline: none; - } -} - -.ui-widget-content { - border: $contentBorderWidth solid $contentBorderColor; - background: $contentBgColor; - color: $contentTextColor; - - a { - color: $contentTextColor; - } -} - -.ui-widget-header { - border: $headerBorderWidth solid $headerBorderColor; - background: $headerBgColor; - color: $headerTextColor; - font-weight: $headerFontWeight; - - a { - color: $headerTextColor; - } -} - -.ui-widget-overlay { - background: #666666; - opacity: .50; - filter:Alpha(Opacity=50); -} - -.ui-state-default { - border: $stateDefaultBorderWidth solid $stateDefaultBorderColor; - background: $stateDefaultBgColor; - color: $stateDefaultTextColor; - - a { - color: $stateDefaultTextColor ; - } -} - -.ui-state-active { - border-color: $stateActiveBorderColor; - background: $stateActiveBgColor; - color: $stateActiveTextColor; - - a { - color: $stateActiveTextColor; - } -} - -.ui-state-highlight { - border-color: $stateHighlightBorderColor; - background: $stateHighlightBgColor; - color: $stateHighlightTextColor; - - a { - color: $stateHighlightTextColor; - } -} - -.ui-state-focus { - border-color: $stateFocusBorderColor; - background: $stateFocusBgColor; - color: $stateFocusTextColor; - - a { - color: $stateFocusTextColor; - } -} - -.ui-state-error { - border-color: $stateErrorBorderColor; - background: $stateErrorBgColor; - color: $stateErrorTextColor; - - a { - color: $stateErrorTextColor; - } -} - -.ui-state-disabled, -.ui-widget:disabled { - opacity: $disabledOpacity; - filter: Alpha(Opacity= $disabledOpacity * 100); - background-image: none; - cursor: default !important; - - * { - cursor: default !important; - } -} - -/* Forms */ -.ui-inputtext { - background: $inputBgColor; - color: $inputTextColor; -} - -.ui-inputtext:enabled:hover { - border-color: $stateHoverBorderColor; -} - -.ui-inputtext.ui-state-focus, -.ui-inputtext:focus { - outline: 0 none; - border-color: $stateFocusBorderColor; - -moz-box-shadow: 0px 0px 5px $stateFocusBorderColor; - -webkit-box-shadow: 0px 0px 5px $stateFocusBorderColor; - box-shadow: 0px 0px 5px $stateFocusBorderColor; -} - -.ui-inputgroup { - .ui-inputgroup-addon { - border-color: $stateDefaultBorderColor; - background-color: lighten($stateDefaultBorderColor, 10%); - color: $inputGroupTextColor; - - &:first-child { - border-top-left-radius: $borderRadius; - border-bottom-left-radius: $borderRadius; - } - - &:last-child { - border-top-right-radius: $borderRadius; - border-bottom-right-radius: $borderRadius; - } - } - - .ui-button { - &:first-child { - border-top-left-radius: $borderRadius; - border-bottom-left-radius: $borderRadius; - } - - &:last-child { - border-top-right-radius: $borderRadius; - border-bottom-right-radius: $borderRadius; - } - } -} - -.ui-float-label input.ng-dirty.ng-invalid ~ label { - color: $stateErrorTextColor; -} - -.ui-autocomplete { - .ui-autocomplete-multiple-container:not(.ui-state-disabled) { - &:hover { - border-color: $stateHoverBorderColor; - } - - &.ui-state-focus { - border-color: $stateFocusBorderColor; - - } - } -} - -.ui-chips { - > ul:not(.ui-state-disabled) { - &:hover { - border-color: $stateHoverBorderColor; - } - - &.ui-state-focus { - border-color: $stateFocusBorderColor; - } - } -} - -.ui-fileupload-buttonbar { - padding: 0; -} - -.ui-widget-header { - background: #fafafa !important; -} -.ui-widget-content{ - border-top:0; -} - -.ui-button:focus, -.ui-button:enabled:hover { - outline: 0 none; - @include hover-element(); -} - -.ui-button:enabled:active { - border-color: $stateActiveBorderColor; - background: $stateActiveBgColor; - color: $stateActiveTextColor; -} - -.ui-chkbox-box:not(.ui-state-disabled):not(.ui-state-active):hover { - @include hover-element(); -} - -.ui-radiobutton-box:not(.ui-state-disabled):not(.ui-state-active):hover { - @include hover-element(); -} - -.ui-dropdown:not(.ui-state-disabled):hover { - @include hover-element(); -} - -.ui-dropdown-panel .ui-dropdown-item:not(.ui-state-highlight):hover { - @include hover-element(); -} - -.ui-listbox { - .ui-listbox-header { - .ui-listbox-filter-container { - .fa { - color: $inputTextColor; - } - } - } - - &:not(.ui-state-disabled) { - .ui-listbox-item:not(.ui-state-highlight):hover { - @include hover-element(); - } - } - - &.ui-state-disabled { - .ui-chkbox-box:not(.ui-state-active):hover { - border-color: $stateDefaultBorderColor; - background: $stateDefaultBgColor; - color: $stateDefaultTextColor; - } - } -} - -.ui-multiselect:not(.ui-state-disabled):hover { - @include hover-element(); -} - -.ui-multiselect-panel .ui-multiselect-item:not(.ui-state-highlight):hover { - @include hover-element(); -} - -.ui-multiselect-panel .ui-multiselect-close { - color: $headerIconTextColor; -} - -.ui-multiselect-panel .ui-multiselect-filter-container .fa { - color: $inputTextColor; -} - -.ui-spinner:not(.ui-state-disabled) .ui-spinner-button:enabled:hover { - @include hover-element(); -} - -.ui-spinner:not(.ui-state-disabled) .ui-spinner-button:enabled:active { - border-color: $stateActiveBorderColor; - background: $stateActiveBgColor; - color: $stateActiveTextColor; -} - -.ui-selectbutton .ui-button:not(.ui-state-disabled):not(.ui-state-active):hover { - @include hover-element(); -} - -.ui-togglebutton:not(.ui-state-disabled):not(.ui-state-active):hover { - @include hover-element(); -} - -.ui-paginator a:not(.ui-state-disabled):not(.ui-state-active):hover { - @include hover-element(); -} - -.ui-paginator a { - color: $stateDefaultTextColor; -} - -.ui-datatable { - .ui-rowgroup-header a { - color: $headerTextColor; - } - - .ui-sortable-column:not(.ui-state-active):hover { - background: $stateHoverBgColor; - color: $stateHoverTextColor; - } - - .ui-row-toggler { - color: $contentTextColor; - } - - tbody.ui-datatable-hoverable-rows { - > tr.ui-widget-content:not(.ui-state-highlight):hover { - cursor: pointer; - background: $stateHoverBgColor; - color: $stateHoverTextColor; - } - } -} - -.ui-orderlist { - .ui-orderlist-item:not(.ui-state-highlight):hover { - @include hover-element(); - } -} - -.ui-picklist { - .ui-picklist-item:not(.ui-state-disabled):not(.ui-state-highlight):hover { - @include hover-element(); - } - - .ui-picklist-droppoint-highlight { - border-color: $stateHighlightBorderColor; - background: $stateHighlightBgColor; - color: darken($contentTextColor,1%); - - a { - color: darken($contentTextColor,1%); - } - } - - .ui-picklist-highlight { - border-color: $stateHighlightBorderColor; - color: darken($contentTextColor,1%); - - a { - color: darken($contentTextColor,1%); - } - } -} - -.ui-tree { - &.ui-treenode-dragover { - border-color: $stateHighlightBorderColor; - } - - .ui-treenode-content { - &.ui-treenode-selectable { - .ui-treenode-label:not(.ui-state-highlight):hover { - @include hover-element(); - } - } - - &.ui-treenode-dragover { - background: $stateActiveBgColor; - color: $stateActiveTextColor; - } - } - - &.ui-tree-horizontal { - .ui-treenode-content.ui-treenode-selectable { - .ui-treenode-label:not(.ui-state-highlight):hover { - background-color: inherit; - color: inherit; - } - - &:not(.ui-state-highlight):hover { - @include hover-element(); - } - } - } -} - -.ui-treetable { - .ui-treetable-row.ui-treetable-row-selectable:not(.ui-state-highlight):hover { - background: $stateHoverBgColor; - color: $stateHoverTextColor; - } -} - -.ui-organizationchart { - .ui-organizationchart-node-content { - &.ui-organizationchart-selectable-node:not(.ui-state-highlight):hover { - @include hover-element(); - } - } -} - -.ui-accordion { - .ui-accordion-header:not(.ui-state-active):not(.ui-state-disabled):hover { - @include hover-element(); - } -} - -.ui-fieldset { - &.ui-fieldset-toggleable { - .ui-fieldset-legend:hover { - @include hover-element(); - } - } -} - -.ui-panel { - .ui-panel-titlebar { - .ui-panel-titlebar-icon:hover { - @include hover-element(); - } - } -} - -.ui-tabview { - .ui-tabview-nav { - li { - &:not(.ui-state-active):not(.ui-state-disabled):hover { - @include hover-element(); - } - } - } -} - -.ui-dialog { - .ui-dialog-titlebar-icon { - color: $headerTextColor; - - &:hover { - @include hover-element(); - } - } -} - -.ui-sidebar { - .ui-sidebar-close { - color: $headerTextColor; - - &:hover { - @include hover-element(); - } - } -} - -.ui-overlaypanel { - .ui-overlaypanel-close:hover { - @include hover-element(); - } -} - -.ui-inplace { - .ui-inplace-display:hover { - @include hover-element(); - } -} - -.ui-breadcrumb { - a { - color: $headerTextColor; - } -} - -.ui-menu { - .ui-menuitem { - .ui-menuitem-link { - color: $contentTextColor; - - &:hover { - @include hover-element(); - border-color: transparent; - } - } - - &.ui-menuitem-active { - > .ui-menuitem-link { - @include hover-element(); - border-color: transparent; - } - } - } -} - -.ui-tabmenu { - .ui-tabmenu-nav { - li:not(.ui-state-active):hover { - @include hover-element(); - } - } -} - -.ui-steps { - .ui-steps-item:not(.ui-state-highlight):not(.ui-state-disabled):hover { - @include hover-element(); - } -} - -.ui-panelmenu { - .ui-panelmenu-header { - &:not(.ui-state-active):hover { - @include hover-element(); - border-color: $stateDefaultBorderColor; - - a { - color: $stateHoverTextColor; - } - } - - &.ui-state-active { - a { - color: $stateActiveTextColor; - } - } - } - - .ui-panelmenu-content { - .ui-menuitem-link { - color: $contentTextColor; - - &:hover { - @include hover-element(); - border-color: transparent; - } - } - } -} - -.ui-datepicker { - .ui-datepicker-header { - a { - color: $headerTextColor; - - &:hover { - @include hover-element(); - } - } - } - - .ui-datepicker-calendar { - td:not(.ui-state-disabled) { - a:hover { - @include hover-element(); - } - } - } -} - -.fc { - .fc-toolbar { - .fc-prev-button { - .ui-icon-circle-triangle-w { - margin-top: .3em; - @include icon_override("\f053"); - } - } - - .fc-next-button { - .ui-icon-circle-triangle-e { - margin-top: .3em; - @include icon_override("\f054"); - } - } - } -} - -.ui-rating { - a { - color: $inputTextColor; - } -} - -.ui-organizationchart { - .ui-organizationchart-line-down { - background-color: darken($contentBorderColor, 10%); - } - - .ui-organizationchart-line-left { - border-right: 1px solid darken($contentBorderColor, 10%); - } - - .ui-organizationchart-line-right { - - } - - .ui-organizationchart-line-top { - border-top: 1px solid darken($contentBorderColor, 10%); - } - - .ui-organizationchart-node-content { - border-color: darken($contentBorderColor, 10%); - } - - .ui-organizationchart-node-content .ui-node-toggler { - color: darken($contentBorderColor, 10%); - } -} - -/* Validation */ -.ui-inputtext.ng-dirty.ng-invalid, -p-dropdown.ng-dirty.ng-invalid > .ui-dropdown, -p-autocomplete.ng-dirty.ng-invalid > .ui-autocomplete > .ui-inputtext, -p-calendar.ng-dirty.ng-invalid > .ui-calendar > .ui-inputtext, -p-chips.ng-dirty.ng-invalid > .ui-inputtext, -p-inputmask.ng-dirty.ng-invalid > .ui-inputtext, -p-checkbox.ng-dirty.ng-invalid .ui-chkbox-box, -p-radiobutton.ng-dirty.ng-invalid .ui-radiobutton-box, -p-inputswitch.ng-dirty.ng-invalid .ui-inputswitch, -p-listbox.ng-dirty.ng-invalid .ui-inputtext, -p-multiselect.ng-dirty.ng-invalid > .ui-multiselect, -p-spinner.ng-dirty.ng-invalid > .ui-inputtext, -p-selectbutton.ng-dirty.ng-invalid .ui-button, -p-togglebutton.ng-dirty.ng-invalid .ui-button { - border-bottom-color: $invalidInputBorderColor; -} - -/* Cornering */ -.ui-corner-tl { -moz-border-radius-topleft: $borderRadius; -webkit-border-top-left-radius: $borderRadius; border-top-left-radius: $borderRadius; } -.ui-corner-tr { -moz-border-radius-topright: $borderRadius; -webkit-border-top-right-radius: $borderRadius; border-top-right-radius: $borderRadius; } -.ui-corner-bl { -moz-border-radius-bottomleft: $borderRadius; -webkit-border-bottom-left-radius: $borderRadius; border-bottom-left-radius: $borderRadius; } -.ui-corner-br { -moz-border-radius-bottomright: $borderRadius; -webkit-border-bottom-right-radius: $borderRadius; border-bottom-right-radius: $borderRadius; } -.ui-corner-top { -moz-border-radius-topleft: $borderRadius; -webkit-border-top-left-radius: $borderRadius; border-top-left-radius: $borderRadius; -moz-border-radius-topright: $borderRadius; -webkit-border-top-right-radius: $borderRadius; border-top-right-radius: $borderRadius; } -.ui-corner-bottom { -moz-border-radius-bottomleft: $borderRadius; -webkit-border-bottom-left-radius: $borderRadius; border-bottom-left-radius: $borderRadius; -moz-border-radius-bottomright: $borderRadius; -webkit-border-bottom-right-radius: $borderRadius; border-bottom-right-radius: $borderRadius; } -.ui-corner-right { -moz-border-radius-topright: $borderRadius; -webkit-border-top-right-radius: $borderRadius; border-top-right-radius: $borderRadius; -moz-border-radius-bottomright: $borderRadius; -webkit-border-bottom-right-radius: $borderRadius; border-bottom-right-radius: $borderRadius; } -.ui-corner-left { -moz-border-radius-topleft: $borderRadius; -webkit-border-top-left-radius: $borderRadius; border-top-left-radius: $borderRadius; -moz-border-radius-bottomleft: $borderRadius; -webkit-border-bottom-left-radius: $borderRadius; border-bottom-left-radius: $borderRadius; } -.ui-corner-all { -moz-border-radius: $borderRadius; -webkit-border-radius: $borderRadius; border-radius: $borderRadius; } diff --git a/sample/Util.Samples.Webs/wwwroot/css/common/prime/_theme.scss b/sample/Util.Samples.Webs/wwwroot/css/common/prime/_theme.scss deleted file mode 100644 index 9d2d374e7..000000000 --- a/sample/Util.Samples.Webs/wwwroot/css/common/prime/_theme.scss +++ /dev/null @@ -1,572 +0,0 @@ -$fontFamily: "Roboto", "Trebuchet MS", Arial, Helvetica, sans-serif; -$fontSize: 1em; -$borderRadius: 3px; -$disabledOpacity: 0.35; - -//Header -$headerBorderWidth: 1px; -$headerBorderColor: #d9d9d9; -$headerBgColor: #f6f7f9; -$headerTextColor: #1b1d1f; -$headerFontWeight: normal; -$headerIconTextColor: #1b1d1f; - -//Content -$contentBorderWidth: 1px; -$contentBorderColor: #D5D5D5; -$contentBgColor: #ffffff; -$contentTextColor: #222222; - -//Default State -$stateDefaultBorderWidth: 1px; -$stateDefaultBorderColor: #d6d6d6; -$stateDefaultBgColor: #ffffff; -$stateDefaultTextColor: #555555; - -//Active State -$stateActiveBorderColor: #bebebe; -$stateActiveBgColor: #d6d6d6; -$stateActiveTextColor: #212121; - -//Highlight State -$stateHighlightBorderColor: #156090; -$stateHighlightBgColor: #186ba0; -$stateHighlightTextColor: #FFFFFF; - -//Focus State -$stateFocusBorderColor: #c0c0c0; -$stateFocusBgColor: #eeeeee; -$stateFocusTextColor: #212121; - -//Error State -$stateErrorBorderColor: #f44336; -$stateErrorBgColor: #f5554a; -$stateErrorTextColor: #cd0a0a; - -//Hover State -$stateHoverBorderColor: #c0c0c0; -$stateHoverBgColor: #eeeeee; -$stateHoverTextColor: #212121; - -//Forms -$inputBgColor: #ffffff; -$inputTextColor: #222222; -$invalidInputBorderColor: #f44336; -$inputGroupTextColor: #222222; - -@import './base'; - -/* roboto-regular - latin */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - src: url('fonts/roboto-v15-latin-regular.eot'); /* IE9 Compat Modes */ - src: local('Roboto'), local('Roboto-Regular'), - url('fonts/roboto-v15-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ - url('fonts/roboto-v15-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */ - url('fonts/roboto-v15-latin-regular.woff') format('woff'), /* Modern Browsers */ - url('fonts/roboto-v15-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */ - url('fonts/roboto-v15-latin-regular.svg#Roboto') format('svg'); /* Legacy iOS */ -} - -.ui-widget-header { - background: #f6f7f9 0 0 repeat-x; /* Old browsers */ - background: -moz-linear-gradient(top, #f6f7f9 0%, #ebedf0 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f6f7f9), color-stop(100%,#ebedf0)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #f6f7f9 0%,#ebedf0 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #f6f7f9 0%,#ebedf0 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #f6f7f9 0%,#ebedf0 100%); /* IE10+ */ - background: linear-gradient(to bottom, #f6f7f9 0%,#ebedf0 100%); /* W3C */ -} - -.ui-accordion { - .ui-accordion-header { - background: $headerBgColor; - border-top: 1px solid $headerBorderColor; - - a { - color: $headerTextColor; - } - - &:not(.ui-state-active):not(.ui-state-disabled):hover { - background: #ededf0; - } - - &.ui-state-active { - background: #ffffff; - border-left-color: transparent; - border-right-color: transparent; - border-bottom-color: transparent; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; - } - } - - .ui-accordion-content { - border: 0 none; - } -} - -/* Tabview */ -.ui-tabview { - &.ui-widget-content { - border: 0 none; - } - - .ui-tabview-nav { - background: transparent; - - > li { - &.ui-state-default { - background: #f6f7f9; - } - - &.ui-state-active { - background: #ffffff; - font-weight: normal; - color: #555555; - } - - &:not(.ui-state-active):not(.ui-state-disabled):hover { - background: #ededf0; - } - } - } -} - -/* Spinner */ -.ui-spinner:not(.ui-state-disabled) .ui-spinner-button:enabled:hover { - border: 1px solid #1f89ce; - background: #1f89ce; - outline: 0 none; - color: #ffffff; -} - -.ui-spinner:not(.ui-state-disabled) .ui-spinner-button:enabled:active { - border: 1px solid #156090; - background: #186ba0; - color: #ffffff; -} - -.ui-slider { position: relative; text-align: left; background: #838688; border: none; -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.6) inset; -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.6) inset; box-shadow: 0 1px 3px rgba(0,0,0,0.6) inset;} -.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 17px !important; height: 21px !important; cursor: default; background: url("images/slider_handles.png") 0 0 no-repeat; outline: none; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; border: none; } -.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background: #14a4ff; -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.6) inset; -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.6) inset; box-shadow: 0 1px 3px rgba(0,0,0,0.6) inset; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; } -.ui-slider .ui-slider-handle.ui-state-active { background-position: -17px 0; } - -.ui-slider-horizontal { height: 6px; } -.ui-slider-horizontal .ui-slider-handle { top: -2px !important; margin-left: -.6em; } -.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } -.ui-slider-horizontal .ui-slider-range-min { left: 0; } -.ui-slider-horizontal .ui-slider-range-max { right: 0; } - -.ui-slider-vertical { width: .8em; height: 100px; } -.ui-slider-vertical .ui-slider-handle { left: -.1em !important; margin-left: 0; margin-bottom: -.6em; } -.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } -.ui-slider-vertical .ui-slider-range-min { bottom: 0; } -.ui-slider-vertical .ui-slider-range-max { top: 0; } - -.ui-progressbar { - &.ui-progressbar-determinate { - .ui-progressbar-value { - border: 0 none; - background: #8ec5fc; - } - - .ui-progressbar .ui-progressbar-label { - color: #222222; - } - } - - &.ui-progressbar-indeterminate { - background: #8ec5fc; - - .ui-progressbar-value { - border: 0 none; - background-color: $stateHighlightBgColor; - } - } -} - - - -.ui-widget-header .ui-button, -.ui-widget-content .ui-button, -.ui-widget.ui-button, -.ui-button { - border: 1px solid #2399e5; - color: #FFFFFF; - background: #2399e5; - -webkit-transition: background-color .2s; - -moz-transition: background-color .2s; - transition: background-color .2s; - - &:enabled:hover, - &:focus { - border: 1px solid #1f89ce; - background: #1f89ce; - outline: 0 none; - color: #ffffff; - } - - &:enabled:active { - border: 1px solid #156090; - background: #186ba0; - color: #ffffff; - } -} - - -/* Checkbox and Radio */ -.ui-chkbox-box.ui-state-active, -.ui-radiobutton-box.ui-state-active { - border: 1px solid #156090; - background: #186ba0; - color: #FFFFFF; -} - -.ui-chkbox-box.ui-state-focus, -.ui-radiobutton-box.ui-state-focus { - -moz-box-shadow: 0px 0px 5px #1f89ce; - -webkit-box-shadow: 0px 0px 5px #1f89ce; - box-shadow: 0px 0px 5px #1f89ce; -} - -.ui-chkbox-box.ui-state-focus.ui-state-active { - background: #186ba0; -} - -/* Inputs */ -.ui-inputtext { - background: #ffffff; - color: #222222; - -webkit-transition: .2s; - -moz-transition: .2s; - transition: .2s; -} - -.ui-inputtext.ui-state-focus, -.ui-inputtext:focus { - -moz-box-shadow: 0px 0px 5px #1f89ce; - -webkit-box-shadow: 0px 0px 5px #1f89ce; - box-shadow: 0px 0px 5px #1f89ce; -} - -/* InputSwitch */ -.ui-inputswitch-on { - background: #186ba0 !important; - color: #ffffff !important; -} - -.ui-paginator .ui-paginator-page.ui-state-active { - background: #186ba0; - color: #ffffff; - border-color: #156090; -} - -/* DataTable */ -.ui-datatable { - th { - &.ui-state-default { - background: #ebedf0; - border-color: #d9d9d9; - } - - &.ui-sortable-column:not(.ui-state-active):hover { - background: #d3d5d8; - border-color: #d9d9d9; - } - - &.ui-state-active { - background: #186ba0; - color: #ffffff; - } - } - - tbody { - > tr.ui-widget-content { - border-color: #d9d9d9; - - &.ui-datatable-odd { - background-color: #fafafb; - } - - &.ui-state-highlight { - background-color: $stateHighlightBgColor; - color: $stateHighlightTextColor; - } - } - } - - tfoot { - td { - &.ui-state-default { - background: #ebedf0; - border-color: #d9d9d9; - } - } - } -} - -/* Panel */ -.ui-panel.ui-widget { - padding: 0; - - .ui-panel-titlebar.ui-corner-all { - -moz-border-radius-bottom-left: 0; - -webkit-border-bottom-left-radius: 0; - border-bottom-left-radius: 0; - -moz-border-radius-bottom-right: 0; - -webkit-border-bottom-right-radius: 0; - border-bottom-right-radius: 0; - } - - .ui-panel-titlebar { - border-width: 0 0 1px 0; - } - - .ui-panel-titlebar-icon span { - position: relative; - top: 1px; - } - -} - -/* TreeTable */ -.ui-treetable { - th.ui-state-default { - background: #ebedf0; - border-color: #d9d9d9; - } -} - -/* ButtonSet */ -.ui-togglebutton.ui-button.ui-state-default, -.ui-selectbutton .ui-button.ui-state-default { - border: 1px solid #d6d6d6; - background: #ffffff; - font-weight: normal; - color: #555555; -} - -.ui-togglebutton.ui-button.ui-state-hover,.ui-togglebutton.ui-button.ui-state-focus, -.ui-selectbutton .ui-button.ui-state-hover,.ui-selectbutton .ui-button.ui-state-focus { - border: 1px solid #c0c0c0; - background: #eeeeee; - font-weight: normal; - color: #212121; -} - -.ui-togglebutton.ui-button.ui-state-focus, -.ui-selectbutton .ui-button.ui-state-focus.ui-state-active { - -moz-box-shadow: 0px 0px 5px #1f89ce; - -webkit-box-shadow: 0px 0px 5px #1f89ce; - box-shadow: 0px 0px 5px #1f89ce; -} - -.ui-togglebutton.ui-button.ui-state-active, -.ui-selectbutton .ui-button.ui-state-active { - border: 1px solid #156090; - background: #186ba0; - color: #FFFFFF; -} - -.ui-multiselect { - .ui-multiselect-label { - background-color: #ffffff; - } -} - -.ui-dropdown.ui-state-focus, .ui-multiselect.ui-state-focus { - -moz-box-shadow: 0px 0px 5px #1f89ce; - -webkit-box-shadow: 0px 0px 5px #1f89ce; - box-shadow: 0px 0px 5px #1f89ce; -} - -/* Growl */ -.ui-growl-item-container.ui-state-highlight { - &.ui-growl-message-info { - background-color: #2196f3; - border-color :#2196f3; - } - - &.ui-growl-message-error { - background-color: #f44336; - border-color :#f44336; - } - - &.ui-growl-message-warn { - background-color: #FFB300; - border-color :#FFB300; - } - - &.ui-growl-message-success { - background-color: #4CAF50; - border-color :#4CAF50; - } -} - -/* TabMenu */ -.ui-tabmenu { - border: 0 none; - - .ui-tabmenu-nav { - background: none; - - > li { - &.ui-state-default { - background: #f6f7f9; - } - - &.ui-state-active { - background: #ffffff; - font-weight: normal; - color: #555555; - } - - &:not(.ui-state-active):not(.ui-state-disabled):hover { - background: #ededf0; - } - } - } -} - -/* Menus */ -.ui-menu, -.ui-menu .ui-menu-child { - border: 1px solid #d9d9d9; - color: #1b1d1f; - background: #f6f7f9 0 0 repeat-x; /* Old browsers */ - background: -moz-linear-gradient(top, #f6f7f9 0%, #ebedf0 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f6f7f9), color-stop(100%,#ebedf0)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #f6f7f9 0%,#ebedf0 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #f6f7f9 0%,#ebedf0 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #f6f7f9 0%,#ebedf0 100%); /* IE10+ */ - background: linear-gradient(to bottom, #f6f7f9 0%,#ebedf0 100%); /* W3C */ -} - -.ui-menu { - .ui-menuitem { - .ui-menuitem-link { - &:hover { - background-color: #a6a6a6; - color: #ffffff; - } - } - - &.ui-menuitem-active { - > .ui-menuitem-link { - background-color: #a6a6a6; - color: #ffffff; - } - } - } -} - -/* PanelMenu */ -.ui-panelmenu .ui-panelmenu-header.ui-state-active, -.ui-panelmenu .ui-panelmenu-header.ui-state-active a { - border-color: #156090; - background: #186ba0; - color: #FFFFFF; -} - -/* DatePicker */ -.ui-datepicker.ui-widget { - padding: 0; - - .ui-datepicker-header { - -webkit-border-radius: 0px; - -moz-border-radius: 0px; - border-radius: 0px; - border-top: 0 none; - border-left: 0 none; - border-right: 0 none; - - a { - &:hover { - border-width: 1px; - } - } - } - - .ui-datepicker-calendar { - margin: 0; - - thead th { - background-color: #f6f8fa; - padding: 8px; - } - - td { - border-bottom: 1px solid rgba(213, 213, 213, 0.5); - padding: 0; - - a { - border: 0 none; - text-align: center; - padding: 8px; - - &.ui-state-highlight { - background-color: #d6d6d6; - color: #212121; - } - - &.ui-state-active { - background-color: #186ba0; - color: #ffffff; - } - } - } - - tr:last-child td { - border-bottom: 0 none; - } - } - - .ui-timepicker { - border-bottom: 0 none; - border-left: 0 none; - border-right: 0 none; - -moz-border-radius: 0; - -webkit-border-radius: 0; - border-radius: 0; - } - - &.ui-datepicker-timeonly { - .ui-timepicker { - border-top: 0 none; - } - } -} - -/* Steps */ -.ui-steps .ui-steps-item.ui-state-highlight .ui-menuitem-link { - color: #ffffff; -} - -/* Dialog */ -.ui-dialog.ui-widget .ui-dialog-titlebar { - padding: 1em 1.5em; -} - -.ui-dialog.ui-widget .ui-dialog-titlebar .ui-dialog-title { - font-size: 1.25em; -} - -.ui-dialog.ui-widget .ui-dialog-content { - padding: 1em 1.5em; -} - -/* Schedule */ -.fc { - .fc-button-group { - .ui-state-active { - border: 1px solid #156090; - background: #186ba0; - color: #ffffff; - } - } -} - diff --git a/sample/Util.Samples.Webs/wwwroot/css/fonts/roboto-v15-latin-regular.eot b/sample/Util.Samples.Webs/wwwroot/css/fonts/roboto-v15-latin-regular.eot deleted file mode 100644 index d26bc8f51..000000000 Binary files a/sample/Util.Samples.Webs/wwwroot/css/fonts/roboto-v15-latin-regular.eot and /dev/null differ diff --git a/sample/Util.Samples.Webs/wwwroot/css/fonts/roboto-v15-latin-regular.svg b/sample/Util.Samples.Webs/wwwroot/css/fonts/roboto-v15-latin-regular.svg deleted file mode 100644 index ed55c105d..000000000 --- a/sample/Util.Samples.Webs/wwwroot/css/fonts/roboto-v15-latin-regular.svg +++ /dev/null @@ -1,308 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sample/Util.Samples.Webs/wwwroot/css/fonts/roboto-v15-latin-regular.ttf b/sample/Util.Samples.Webs/wwwroot/css/fonts/roboto-v15-latin-regular.ttf deleted file mode 100644 index 7b25f3ce9..000000000 Binary files a/sample/Util.Samples.Webs/wwwroot/css/fonts/roboto-v15-latin-regular.ttf and /dev/null differ diff --git a/sample/Util.Samples.Webs/wwwroot/css/fonts/roboto-v15-latin-regular.woff b/sample/Util.Samples.Webs/wwwroot/css/fonts/roboto-v15-latin-regular.woff deleted file mode 100644 index 941dfa4ba..000000000 Binary files a/sample/Util.Samples.Webs/wwwroot/css/fonts/roboto-v15-latin-regular.woff and /dev/null differ diff --git a/sample/Util.Samples.Webs/wwwroot/css/fonts/roboto-v15-latin-regular.woff2 b/sample/Util.Samples.Webs/wwwroot/css/fonts/roboto-v15-latin-regular.woff2 deleted file mode 100644 index 120796bb7..000000000 Binary files a/sample/Util.Samples.Webs/wwwroot/css/fonts/roboto-v15-latin-regular.woff2 and /dev/null differ diff --git a/sample/Util.Samples.Webs/wwwroot/css/images/slider_handles.png b/sample/Util.Samples.Webs/wwwroot/css/images/slider_handles.png deleted file mode 100644 index 0fddde5fe..000000000 Binary files a/sample/Util.Samples.Webs/wwwroot/css/images/slider_handles.png and /dev/null differ diff --git a/sample/Util.Samples.Webs/wwwroot/css/images/slider_handles@2x.png b/sample/Util.Samples.Webs/wwwroot/css/images/slider_handles@2x.png deleted file mode 100644 index d8c901ec8..000000000 Binary files a/sample/Util.Samples.Webs/wwwroot/css/images/slider_handles@2x.png and /dev/null differ diff --git a/sample/Util.Samples.Webs/wwwroot/css/main.scss b/sample/Util.Samples.Webs/wwwroot/css/main.scss deleted file mode 100644 index fd2e3476e..000000000 --- a/sample/Util.Samples.Webs/wwwroot/css/main.scss +++ /dev/null @@ -1,8 +0,0 @@ -//字符编码 -@charset 'utf-8'; - -//导入公共样式 -@import "common/main"; - -//导入项目样式 -@import "app/main"; \ No newline at end of file diff --git a/sample/Util.Samples.Webs/wwwroot/favicon.ico b/sample/Util.Samples.Webs/wwwroot/favicon.ico deleted file mode 100644 index a3a799985..000000000 Binary files a/sample/Util.Samples.Webs/wwwroot/favicon.ico and /dev/null differ diff --git a/src/Util.Aop.AspectCore/03-Util.Aop.AspectCore.csproj b/src/Util.Aop.AspectCore/03-Util.Aop.AspectCore.csproj new file mode 100644 index 000000000..1d8d5c920 --- /dev/null +++ b/src/Util.Aop.AspectCore/03-Util.Aop.AspectCore.csproj @@ -0,0 +1,39 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Aop.AspectCore + Util.Aop + Util.Aop.AspectCore是Util应用框架基于 AspectCore 实现AOP拦截功能的库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Aop.AspectCore.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Aop.AspectCore.xml + + + + + True + False + + + + + + + + + + + + + + + diff --git a/src/Util.Aop.AspectCore/AopOptions.cs b/src/Util.Aop.AspectCore/AopOptions.cs new file mode 100644 index 000000000..6e56f1701 --- /dev/null +++ b/src/Util.Aop.AspectCore/AopOptions.cs @@ -0,0 +1,15 @@ +namespace Util.Aop; + +/// +/// Aop配置 +/// +public class AopOptions { + /// + /// 是否启用IAopProxy接口标记 + /// + public bool IsEnableIAopProxy { get; set; } + /// + /// 是否启用参数拦截器,默认值: true + /// + public bool IsEnableParameterAspect { get; set; } = true; +} \ No newline at end of file diff --git a/src/Util.Aop.AspectCore/AppBuilderExtensions.cs b/src/Util.Aop.AspectCore/AppBuilderExtensions.cs new file mode 100644 index 000000000..6c52982c2 --- /dev/null +++ b/src/Util.Aop.AspectCore/AppBuilderExtensions.cs @@ -0,0 +1,105 @@ +using Util.Configs; + +namespace Util.Aop; + +/// +/// Aop配置扩展 +/// +public static class AppBuilderExtensions { + /// + /// 启用AspectCore拦截器 + /// + /// 应用生成器 + public static IAppBuilder AddAop( this IAppBuilder builder ) { + return builder.AddAop( false ); + } + + /// + /// 启用AspectCore拦截器 + /// + /// 应用生成器 + /// 是否启用IAopProxy接口标记 + public static IAppBuilder AddAop( this IAppBuilder builder,bool isEnableIAopProxy ) { + return builder.AddAop( null, isEnableIAopProxy ); + } + + /// + /// 启用AspectCore拦截器 + /// + /// 应用生成器 + /// AspectCore拦截器配置操作 + public static IAppBuilder AddAop( this IAppBuilder builder, Action setupAction ) { + return builder.AddAop( setupAction, false ); + } + + /// + /// 启用AspectCore拦截器 + /// + /// 应用生成器 + /// AspectCore拦截器配置操作 + /// 是否启用IAopProxy接口标记 + private static IAppBuilder AddAop( this IAppBuilder builder, Action setupAction, bool isEnableIAopProxy ) { + return builder.AddAop( setupAction, t => t.IsEnableIAopProxy = isEnableIAopProxy ); + } + + /// + /// 启用AspectCore拦截器 + /// + /// 应用生成器 + /// AspectCore拦截器配置操作 + /// 配置操作 + private static IAppBuilder AddAop( this IAppBuilder builder, Action setupAction, Action action ) { + builder.CheckNull( nameof( builder ) ); + builder.Host.UseServiceProviderFactory( new DynamicProxyServiceProviderFactory() ); + builder.Host.ConfigureServices( ( context, services ) => { + var options = new AopOptions(); + action?.Invoke( options ); + ConfigureDynamicProxy( services, setupAction, options.IsEnableParameterAspect,options.IsEnableIAopProxy ); + RegisterAspectScoped( services ); + } ); + return builder; + } + + /// + /// 配置拦截器 + /// + private static void ConfigureDynamicProxy( IServiceCollection services, Action setupAction,bool isEnableParameterAspect, bool isEnableIAopProxy ) { + services.ConfigureDynamicProxy( config => { + if ( setupAction == null ) { + config.NonAspectPredicates.Add( t => !IsProxy( t.DeclaringType, isEnableIAopProxy ) ); + if( isEnableParameterAspect ) + config.EnableParameterAspect(); + return; + } + setupAction.Invoke( config ); + } ); + } + + /// + /// 是否创建代理 + /// + private static bool IsProxy( Type type, bool isEnableIAopProxy ) { + if ( type == null ) + return false; + if ( isEnableIAopProxy == false ) { + return type.SafeString().Contains( "Xunit.DependencyInjection.ITestOutputHelperAccessor" ) == false; + } + var interfaces = type.GetInterfaces(); + if ( interfaces == null || interfaces.Length == 0 ) + return false; + foreach ( var item in interfaces ) { + if ( item == typeof( IAopProxy ) ) + return true; + } + return false; + } + + /// + /// 注册拦截器服务 + /// + private static void RegisterAspectScoped( IServiceCollection services ) { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + } +} \ No newline at end of file diff --git a/src/Util.Aop.AspectCore/ExceptionExtensions.cs b/src/Util.Aop.AspectCore/ExceptionExtensions.cs new file mode 100644 index 000000000..4b6b14016 --- /dev/null +++ b/src/Util.Aop.AspectCore/ExceptionExtensions.cs @@ -0,0 +1,21 @@ +namespace Util.Aop; + +/// +/// 异常扩展 +/// +public static class ExceptionExtensions { + /// + /// 获取原始异常 + /// + /// 异常 + public static Exception GetRawException( this Exception exception ) { + if( exception == null ) + return null; + if( exception is AspectCore.DynamicProxy.AspectInvocationException aspectInvocationException ) { + if( aspectInvocationException.InnerException == null ) + return aspectInvocationException; + return GetRawException( aspectInvocationException.InnerException ); + } + return exception; + } +} \ No newline at end of file diff --git a/src/Util.Aop.AspectCore/IAopProxy.cs b/src/Util.Aop.AspectCore/IAopProxy.cs new file mode 100644 index 000000000..7a2bd1a04 --- /dev/null +++ b/src/Util.Aop.AspectCore/IAopProxy.cs @@ -0,0 +1,7 @@ +namespace Util.Aop; + +/// +/// Aop代理标记 +/// +public interface IAopProxy { +} \ No newline at end of file diff --git a/src/Util.Aop.AspectCore/IgnoreAttribute.cs b/src/Util.Aop.AspectCore/IgnoreAttribute.cs new file mode 100644 index 000000000..0032d80e7 --- /dev/null +++ b/src/Util.Aop.AspectCore/IgnoreAttribute.cs @@ -0,0 +1,7 @@ +namespace Util.Aop; + +/// +/// 忽略拦截 +/// +public class IgnoreAttribute : NonAspectAttribute { +} \ No newline at end of file diff --git a/src/Util.Aop.AspectCore/InterceptorBase.cs b/src/Util.Aop.AspectCore/InterceptorBase.cs new file mode 100644 index 000000000..e246baf72 --- /dev/null +++ b/src/Util.Aop.AspectCore/InterceptorBase.cs @@ -0,0 +1,7 @@ +namespace Util.Aop; + +/// +/// 拦截器基类 +/// +public abstract class InterceptorBase : AbstractInterceptorAttribute { +} \ No newline at end of file diff --git a/src/Util.Aop.AspectCore/NotEmptyAttribute.cs b/src/Util.Aop.AspectCore/NotEmptyAttribute.cs new file mode 100644 index 000000000..5e5bfc10b --- /dev/null +++ b/src/Util.Aop.AspectCore/NotEmptyAttribute.cs @@ -0,0 +1,15 @@ +namespace Util.Aop; + +/// +/// 验证参数不能为空 +/// +public class NotEmptyAttribute : ParameterInterceptorBase { + /// + /// 执行 + /// + public override Task Invoke( ParameterAspectContext context, ParameterAspectDelegate next ) { + if( string.IsNullOrWhiteSpace( context.Parameter.Value.SafeString() ) ) + throw new ArgumentNullException( context.Parameter.Name ); + return next( context ); + } +} \ No newline at end of file diff --git a/src/Util.Aop.AspectCore/NotNullAttribute.cs b/src/Util.Aop.AspectCore/NotNullAttribute.cs new file mode 100644 index 000000000..f20dd221e --- /dev/null +++ b/src/Util.Aop.AspectCore/NotNullAttribute.cs @@ -0,0 +1,15 @@ +namespace Util.Aop; + +/// +/// 验证参数不能为null +/// +public class NotNullAttribute : ParameterInterceptorBase { + /// + /// 执行 + /// + public override Task Invoke( ParameterAspectContext context, ParameterAspectDelegate next ) { + if( context.Parameter.Value == null ) + throw new ArgumentNullException( context.Parameter.Name ); + return next( context ); + } +} \ No newline at end of file diff --git a/src/Util.Aop.AspectCore/ParameterInterceptorBase.cs b/src/Util.Aop.AspectCore/ParameterInterceptorBase.cs new file mode 100644 index 000000000..802cb6fd5 --- /dev/null +++ b/src/Util.Aop.AspectCore/ParameterInterceptorBase.cs @@ -0,0 +1,7 @@ +namespace Util.Aop; + +/// +/// 参数拦截器基类 +/// +public abstract class ParameterInterceptorBase : ParameterInterceptorAttribute { +} \ No newline at end of file diff --git a/src/Util.Aop.AspectCore/Usings.cs b/src/Util.Aop.AspectCore/Usings.cs new file mode 100644 index 000000000..059e6cc71 --- /dev/null +++ b/src/Util.Aop.AspectCore/Usings.cs @@ -0,0 +1,8 @@ +global using System; +global using System.Threading.Tasks; +global using Microsoft.Extensions.DependencyInjection; +global using AspectCore.Configuration; +global using AspectCore.DynamicProxy; +global using AspectCore.DynamicProxy.Parameters; +global using AspectCore.Extensions.AspectScope; +global using AspectCore.Extensions.DependencyInjection; \ No newline at end of file diff --git a/src/Util.Application.EntityFrameworkCore/02-Util.Application.EntityFrameworkCore.csproj b/src/Util.Application.EntityFrameworkCore/02-Util.Application.EntityFrameworkCore.csproj new file mode 100644 index 000000000..1a9c8eb95 --- /dev/null +++ b/src/Util.Application.EntityFrameworkCore/02-Util.Application.EntityFrameworkCore.csproj @@ -0,0 +1,34 @@ + + + + $(NetTargetFramework) + Util.Applications + icon.jpg + Util.Application.EntityFrameworkCore是Util应用框架基于EntityFrameworkCore数据访问的应用层类库 + Util.Application.EntityFrameworkCore + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Application.EntityFrameworkCore.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Application.EntityFrameworkCore.xml + + + + + True + False + + + + + + + + + + diff --git a/src/Util.Application.EntityFrameworkCore/CrudServiceBase.cs b/src/Util.Application.EntityFrameworkCore/CrudServiceBase.cs new file mode 100644 index 000000000..ce7bf7ecf --- /dev/null +++ b/src/Util.Application.EntityFrameworkCore/CrudServiceBase.cs @@ -0,0 +1,466 @@ +using Util.Applications.Dtos; +using Util.Data; +using Util.Data.Queries; +using Util.Domain.Compare; +using Util.Domain.Entities; +using Util.Domain.Repositories; +using Util.Helpers; +using Util.Properties; + +namespace Util.Applications; + +/// +/// 增删改查服务 +/// +/// 实体类型 +/// 数据传输对象类型 +/// 查询参数类型 +public abstract class CrudServiceBase : CrudServiceBase + where TEntity : class, IAggregateRoot, new() + where TDto : class, IDto, new() + where TQuery : IPage { + /// + /// 初始化增删改查服务 + /// + /// 服务提供器 + /// 工作单元 + /// 仓储 + protected CrudServiceBase( IServiceProvider serviceProvider, IUnitOfWork unitOfWork, IRepository repository ) : base( serviceProvider, unitOfWork, repository ) { + } +} + +/// +/// 增删改查服务 +/// +/// 实体类型 +/// 数据传输对象类型 +/// 查询参数类型 +/// 实体标识类型 +public abstract class CrudServiceBase + : CrudServiceBase, ICrudService + where TEntity : class, IAggregateRoot, new() + where TDto : class, IDto, new() + where TQuery : IPage { + /// + /// 初始化增删改查服务 + /// + /// 服务提供器 + /// 工作单元 + /// 仓储 + protected CrudServiceBase( IServiceProvider serviceProvider, IUnitOfWork unitOfWork, IRepository repository ) : base( serviceProvider, unitOfWork, repository ) { + } +} + +/// +/// 增删改查服务 +/// +/// 实体类型 +/// 数据传输对象类型 +/// 创建参数类型 +/// 修改参数类型 +/// 查询参数类型 +public abstract class CrudServiceBase + : CrudServiceBase + where TEntity : class, IAggregateRoot, new() + where TDto : class, IDto, new() + where TCreateRequest : class, IRequest, new() + where TUpdateRequest : class, IDto, new() + where TQuery : IPage { + /// + /// 初始化增删改查服务 + /// + /// 服务提供器 + /// 工作单元 + /// 仓储 + protected CrudServiceBase( IServiceProvider serviceProvider, IUnitOfWork unitOfWork, IRepository repository ) : base( serviceProvider, unitOfWork, repository ) { + } +} + +/// +/// 增删改查服务 +/// +/// 实体类型 +/// 数据传输对象类型 +/// 创建参数类型 +/// 修改参数类型 +/// 查询参数类型 +/// 实体标识类型 +public abstract class CrudServiceBase + : QueryServiceBase, ICrudService + where TEntity : class, IAggregateRoot, new() + where TDto : class,IDto, new() + where TCreateRequest : class, IRequest, new() + where TUpdateRequest : class, IDto, new() + where TQuery : IPage { + + #region 字段 + + /// + /// 仓储 + /// + private readonly IRepository _repository; + + #endregion + + #region 构造方法 + + /// + /// 初始化增删改查服务 + /// + /// 服务提供器 + /// 工作单元 + /// 仓储 + protected CrudServiceBase( IServiceProvider serviceProvider, IUnitOfWork unitOfWork, IRepository repository ) : base( serviceProvider, repository ) { + UnitOfWork = unitOfWork ?? throw new ArgumentNullException( nameof( unitOfWork ) ); + _repository = repository ?? throw new ArgumentNullException( nameof( repository ) ); + EntityDescription = Reflection.GetDisplayNameOrDescription(); + } + + #endregion + + #region 属性 + + /// + /// 工作单元 + /// + protected IUnitOfWork UnitOfWork { get; } + + /// + /// 实体描述 + /// + protected string EntityDescription { get; } + + #endregion + + #region CreateAsync(创建) + + /// + public virtual async Task CreateAsync( TCreateRequest request ) { + request.CheckNull( nameof(request) ); + request.Validate(); + var entity = ToEntity( request ); + entity.CheckNull( nameof( entity ) ); + await CreateAsync( entity ); + await CommitAsync(); + await CreateCommitAfterAsync( entity ); + return entity.Id.ToString(); + } + + /// + /// 转换为实体 + /// + /// 创建参数 + protected virtual TEntity ToEntity( TCreateRequest request ) { + return request.MapTo(); + } + + /// + /// 创建实体 + /// + private async Task CreateAsync( TEntity entity ) { + await CreateBeforeAsync( entity ); + entity.Init(); + await _repository.AddAsync( entity ); + await CreateAfterAsync( entity ); + } + + /// + /// 创建前操作 + /// + /// 实体 + protected virtual Task CreateBeforeAsync( TEntity entity ) { + return Task.CompletedTask; + } + + /// + /// 创建后操作 + /// + /// 实体 + protected virtual Task CreateAfterAsync( TEntity entity ) { + return Task.CompletedTask; + } + + /// + /// 提交工作单元 + /// + protected virtual async Task CommitAsync() { + await UnitOfWork.CommitAsync(); + } + + /// + /// 创建提交后操作 + /// + /// 实体 + protected virtual Task CreateCommitAfterAsync( TEntity entity ) { + return Task.CompletedTask; + } + + #endregion + + #region UpdateAsync(修改) + + /// + public virtual async Task UpdateAsync( TUpdateRequest request ) { + request.CheckNull( nameof(request) ); + request.Validate(); + if ( request.Id.IsEmpty() ) + throw new InvalidOperationException( R.IdIsEmpty ); + var oldEntity = await FindOldEntityAsync( request.Id ); + oldEntity.CheckNull( nameof( oldEntity ) ); + var entity = ToEntity( oldEntity.Clone(), request ); + entity.CheckNull( nameof( entity ) ); + var changes = oldEntity.GetChanges( entity ); + await UpdateAsync( entity ); + await CommitAsync(); + await UpdateCommitAfterAsync( entity, changes ); + } + + /// + /// 查找旧实体 + /// + /// 标识 + private async Task FindOldEntityAsync( object id ) { + return await _repository.FindByIdAsync( id ); + } + + /// + /// 转换为实体 + /// + /// 旧实体 + /// 修改参数 + protected virtual TEntity ToEntity( TEntity oldEntity, TUpdateRequest request ) { + return request.MapTo( oldEntity ); + } + + /// + /// 修改实体 + /// + /// 实体 + private async Task UpdateAsync( TEntity entity ) { + await UpdateBeforeAsync( entity ); + await _repository.UpdateAsync( entity ); + await UpdateAfterAsync( entity ); + } + + /// + /// 修改前操作 + /// + /// 实体 + protected virtual Task UpdateBeforeAsync( TEntity entity ) { + return Task.CompletedTask; + } + + /// + /// 修改后操作 + /// + /// 实体 + protected virtual Task UpdateAfterAsync( TEntity entity ) { + return Task.CompletedTask; + } + + /// + /// 修改提交后操作 + /// + /// 实体 + /// 变更值集合 + protected virtual Task UpdateCommitAfterAsync( TEntity entity,ChangeValueCollection changeValues ) { + return Task.CompletedTask; + } + + #endregion + + #region DeleteAsync(删除) + + /// + public virtual async Task DeleteAsync( string ids ) { + if ( ids.IsEmpty() ) + return; + var entities = await _repository.FindByIdsAsync( ids ); + if ( entities?.Count == 0 ) + return; + await DeleteBeforeAsync( entities ); + await _repository.RemoveAsync( entities ); + await DeleteAfterAsync( entities ); + await CommitAsync(); + await DeleteCommitAfterAsync( entities ); + } + + /// + /// 删除前操作 + /// + /// 实体列表 + protected virtual Task DeleteBeforeAsync( List entities ) { + return Task.CompletedTask; + } + + /// + /// 删除后操作 + /// + /// 实体集合 + protected virtual Task DeleteAfterAsync( List entities ) { + return Task.CompletedTask; + } + + /// + /// 删除提交后操作 + /// + /// 实体集合 + protected virtual Task DeleteCommitAfterAsync( List entities ) { + return Task.CompletedTask; + } + + #endregion + + #region SaveAsync(批量保存) + + /// + /// 批量保存 + /// + /// 新增列表 + /// 修改列表 + /// 删除列表 + public virtual async Task> SaveAsync( List creationList, List updateList, List deleteList ) { + if ( creationList == null && updateList == null && deleteList == null ) + return new List(); + creationList ??= new List(); + updateList ??= new List(); + deleteList ??= new List(); + FilterList( creationList, updateList, deleteList ); + var addEntities = ToEntities( creationList ); + var updateEntities = ToEntities( updateList ); + var deleteEntities = ToEntities( deleteList ); + await SaveBeforeAsync( addEntities, updateEntities, deleteEntities ); + await AddListAsync( addEntities ); + var changeValues = await UpdateListAsync( updateEntities ); + await DeleteListAsync( deleteEntities ); + await SaveAfterAsync( addEntities, updateEntities, deleteEntities ); + await CommitAsync(); + await SaveCommitAfterAsync( addEntities, updateEntities, deleteEntities, changeValues ); + return GetResult( addEntities, updateEntities ); + } + + /// + /// 过滤列表 + /// + private void FilterList( List creationList, List updateList, List deleteList ) { + FilterByDeleteList( creationList, deleteList ); + FilterByDeleteList( updateList, deleteList ); + } + + /// + /// 过滤需要删除的项 + /// + private void FilterByDeleteList( List list, List deleteList ) { + for ( int i = 0; i < list.Count; i++ ) { + var item = list[i]; + if ( deleteList.Any( d => d.Id == item.Id ) ) + list.Remove( item ); + } + } + + /// + /// 转换为实体集合 + /// + private List ToEntities( List dtos ) { + return dtos.Select( ToEntity ).Distinct().ToList(); + } + + /// + /// 转换为实体 + /// + /// 参数 + protected virtual TEntity ToEntity( TDto request ) { + return request.MapTo(); + } + + /// + /// 保存前操作 + /// + /// 新增列表 + /// 修改列表 + /// 删除列表 + protected virtual Task SaveBeforeAsync( List creationList, List updateList, List deleteList ) { + return Task.CompletedTask; + } + + /// + /// 添加实体列表 + /// + private async Task AddListAsync( List list ) { + if ( list.Count == 0 ) + return; + foreach ( var entity in list ) + await CreateAsync( entity ); + } + + /// + /// 更新实体列表 + /// + private async Task>> UpdateListAsync( List list ) { + var result = new List>(); + if ( list.Count == 0 ) + return result; + var oldEntities = await FindOldEntities( list ); + foreach ( var entity in list ) { + var oldEntity = oldEntities.Find( t => t.Id.Equals( entity.Id ) ); + if ( oldEntity != null ) + result.Add( new Tuple( entity, oldEntity.GetChanges( entity ) ) ); + await UpdateAsync( entity ); + } + return result; + } + + /// + /// 查找旧实体集合 + /// + private async Task> FindOldEntities( List list ) { + return await _repository.FindAllAsync( item => list.Select( t => t.Id ).Contains( item.Id ) ); + } + + /// + /// 删除实体列表 + /// + private async Task DeleteListAsync( List list ) { + if ( list.Count == 0 ) + return; + foreach ( var entity in list ) + await DeleteChildrenAsync( entity ); + } + + /// + /// 删除子节点集合 + /// + protected virtual async Task DeleteChildrenAsync( TEntity parent ) { + await _repository.RemoveAsync( parent.Id ); + } + + /// + /// 保存后操作 + /// + /// 新增列表 + /// 修改列表 + /// 删除列表 + protected virtual Task SaveAfterAsync( List creationList, List updateList, List deleteList ) { + return Task.CompletedTask; + } + + /// + /// 保存提交后操作 + /// + /// 新增列表 + /// 修改列表 + /// 删除列表 + /// 变更值集合 + protected virtual Task SaveCommitAfterAsync( List creationList, List updateList, List deleteList, List> changeValues ) { + return Task.CompletedTask; + } + + /// + /// 获取结果 + /// + protected virtual List GetResult( List creationList, List updateList ) { + return creationList.Concat( updateList ).Select( ToDto ).ToList(); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Application.EntityFrameworkCore/QueryServiceBase.cs b/src/Util.Application.EntityFrameworkCore/QueryServiceBase.cs new file mode 100644 index 000000000..4c125140c --- /dev/null +++ b/src/Util.Application.EntityFrameworkCore/QueryServiceBase.cs @@ -0,0 +1,178 @@ +using Microsoft.EntityFrameworkCore; +using Util.Data; +using Util.Data.EntityFrameworkCore; +using Util.Data.Queries; +using Util.Data.Stores; +using Util.Domain; + +namespace Util.Applications; + +/// +/// 查询服务 +/// +/// 实体类型 +/// 数据传输对象类型 +/// 查询参数类型 +public abstract class QueryServiceBase : QueryServiceBase + where TEntity : class, IKey + where TDto : new() + where TQuery : IPage { + /// + /// 初始化查询服务 + /// + /// 服务提供器 + /// 查询存储器 + protected QueryServiceBase( IServiceProvider serviceProvider, IQueryStore store ) : base( serviceProvider,store ) { + } +} + +/// +/// 查询服务 +/// +/// 实体类型 +/// 数据传输对象类型 +/// 查询参数类型 +/// 实体标识类型 +public abstract class QueryServiceBase : ServiceBase, IQueryService + where TEntity : class, IKey + where TDto : new() + where TQuery : IPage { + + #region 字段 + + /// + /// 查询存储器 + /// + private readonly IQueryStore _store; + + #endregion + + #region 构造方法 + + /// + /// 初始化查询服务 + /// + /// 服务提供器 + /// 查询存储器 + protected QueryServiceBase( IServiceProvider serviceProvider,IQueryStore store ) : base( serviceProvider ) { + _store = store ?? throw new ArgumentNullException( nameof( store ) ); + } + + #endregion + + #region 属性 + + /// + /// 查询时是否跟踪实体 + /// + protected virtual bool IsTracking => false; + + #endregion + + #region ToDto(转换) + + /// + /// 转换为数据传输对象 + /// + /// 实体 + protected virtual TDto ToDto( TEntity entity ) { + return entity.MapTo(); + } + + #endregion + + #region GetAllAsync(获取全部实体) + + /// + public virtual async Task> GetAllAsync() { + var entities = await GetStore().FindAllAsync(); + return entities.Select( ToDto ).ToList(); + } + + /// + /// 获取查询存储器 + /// + private IQueryStore GetStore() { + if( IsTracking ) + return _store; + return _store.NoTracking(); + } + + #endregion + + #region GetByIdAsync(通过标识获取实体) + + /// + public virtual async Task GetByIdAsync( object id ) { + var key = Util.Helpers.Convert.To( id ); + return ToDto( await GetStore().FindByIdAsync( key ) ); + } + + #endregion + + #region GetByIdsAsync(通过标识列表获取实体列表) + + /// + public virtual async Task> GetByIdsAsync( string ids ) { + var entities = await GetStore().FindByIdsAsync( ids ); + return entities.Select( ToDto ).ToList(); + } + + #endregion + + #region QueryAsync(查询) + + /// + public virtual async Task> QueryAsync( TQuery param ) { + if( param == null ) + return new List(); + var queryable = GetStore().Find(); + queryable = AddConditions( queryable, param ); + queryable = Filter( queryable, param ); + var result = await queryable.OrderBy( param ).ToListAsync(); + return result.Select( ToDto ).ToList(); + } + + /// + /// 添加条件列表 + /// + private IQueryable AddConditions( IQueryable queryable, TQuery param ) { + var conditions = GetConditions( param ); + if ( conditions == null ) + return queryable; + foreach ( var condition in conditions ) + queryable = queryable.Where( condition ); + return queryable; + } + + /// + /// 获取条件列表 + /// + protected virtual IEnumerable> GetConditions( TQuery param ) { + return null; + } + + /// + /// 过滤 + /// + protected virtual IQueryable Filter( IQueryable queryable, TQuery param ) { + return queryable; + } + + #endregion + + #region PageQueryAsync(分页查询) + + /// + public virtual async Task> PageQueryAsync( TQuery param ) { + if( param == null ) + return new PageList(); + var queryable = GetStore().Find(); + queryable = AddConditions( queryable, param ); + queryable = Filter( queryable, param ); + var result = await queryable.ToPageListAsync( param ); + return result.Convert( ToDto ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Application.EntityFrameworkCore/Trees/TreeQueryServiceBase.cs b/src/Util.Application.EntityFrameworkCore/Trees/TreeQueryServiceBase.cs new file mode 100644 index 000000000..e1f784951 --- /dev/null +++ b/src/Util.Application.EntityFrameworkCore/Trees/TreeQueryServiceBase.cs @@ -0,0 +1,70 @@ +using Util.Data; +using Util.Data.Trees; +using Util.Domain.Trees; + +namespace Util.Applications.Trees; + +/// +/// 树形查询服务 +/// +/// 实体类型 +/// 数据传输对象类型 +/// 查询参数类型 +public abstract class TreeQueryServiceBase + : TreeQueryServiceBase + where TEntity : class, ITreeEntity, new() + where TDto : class, ITreeNode, new() + where TQuery : class, ITreeQueryParameter { + /// + /// 初始化树形查询服务 + /// + /// 服务提供器 + /// 树形仓储 + protected TreeQueryServiceBase( IServiceProvider serviceProvider, ITreeRepository repository ) : base( serviceProvider, repository ) { + } +} + +/// +/// 树形查询服务 +/// +/// 实体类型 +/// 数据传输对象类型 +/// 查询参数类型 +/// 实体标识类型 +/// 父标识类型 +public abstract class TreeQueryServiceBase + : QueryServiceBase, ITreeQueryService + where TEntity : class, ITreeEntity, new() + where TDto : class, ITreeNode, new() + where TQuery : class, ITreeQueryParameter { + /// + /// 树形仓储 + /// + private readonly ITreeRepository _repository; + + /// + /// 初始化树形查询服务 + /// + /// 服务提供器 + /// 树形仓储 + protected TreeQueryServiceBase( IServiceProvider serviceProvider, ITreeRepository repository ) : base( serviceProvider, repository ) { + _repository = repository; + } + + /// + /// 获取条件列表 + /// + protected override IEnumerable> GetConditions( TQuery parameter ) { + return new[] { new TreeCondition( parameter ) }; + } + + /// + /// 通过父标识列表获取节点集合 + /// + /// 父标识列表,以逗号分隔标识 + public virtual async Task> GetByParentIds( string parentIds ) { + var keys = Util.Helpers.Convert.ToList( parentIds ); + var entities = await _repository.FindAllAsync( t => keys.Contains( t.ParentId ) ); + return entities.Select( ToDto ).ToList(); + } +} \ No newline at end of file diff --git a/src/Util.Application.EntityFrameworkCore/Trees/TreeServiceBase.cs b/src/Util.Application.EntityFrameworkCore/Trees/TreeServiceBase.cs new file mode 100644 index 000000000..1b5bb6839 --- /dev/null +++ b/src/Util.Application.EntityFrameworkCore/Trees/TreeServiceBase.cs @@ -0,0 +1,373 @@ +using Util.Applications.Dtos; +using Util.Data; +using Util.Data.Trees; +using Util.Domain; +using Util.Domain.Compare; +using Util.Domain.Trees; +using Util.Properties; + +namespace Util.Applications.Trees; + +/// +/// 树形服务 +/// +/// 实体类型 +/// 数据传输对象类型 +/// 查询参数类型 +public abstract class TreeServiceBase + : TreeServiceBase + where TEntity : class, ITreeEntity, new() + where TDto : class, ITreeNode, new() + where TQuery : class, ITreeQueryParameter { + /// + /// 初始化树形服务 + /// + /// 服务提供器 + /// 工作单元 + /// 树形仓储 + protected TreeServiceBase( IServiceProvider serviceProvider, IUnitOfWork unitOfWork, ITreeRepository repository ) : base( serviceProvider, unitOfWork, repository ) { + } +} + +/// +/// 树形服务 +/// +/// 实体类型 +/// 数据传输对象类型 +/// 创建参数类型 +/// 修改参数类型 +/// 查询参数类型 +public abstract class TreeServiceBase + : TreeServiceBase + where TEntity : class, ITreeEntity, new() + where TDto : class, ITreeNode, new() + where TCreateRequest : class, IRequest, new() + where TUpdateRequest : class, IDto, new() + where TQuery : class, ITreeQueryParameter { + /// + /// 初始化树形服务 + /// + /// 服务提供器 + /// 工作单元 + /// 树形仓储 + protected TreeServiceBase( IServiceProvider serviceProvider, IUnitOfWork unitOfWork, ITreeRepository repository ) : base( serviceProvider, unitOfWork, repository ) { + } +} + +/// +/// 树形服务 +/// +/// 实体类型 +/// 数据传输对象类型 +/// 创建参数类型 +/// 修改参数类型 +/// 查询参数类型 +/// 实体标识类型 +/// 父标识类型 +public abstract class TreeServiceBase + : TreeQueryServiceBase, ITreeService + where TEntity : class, ITreeEntity, new() + where TDto : class, ITreeNode, new() + where TCreateRequest : class,IRequest, new() + where TUpdateRequest : class, IDto, new() + where TQuery : class, ITreeQueryParameter { + + #region 字段 + + /// + /// 树形仓储 + /// + private readonly ITreeRepository _repository; + + #endregion + + #region 构造方法 + + /// + /// 初始化树形服务 + /// + /// 服务提供器 + /// 工作单元 + /// 树形仓储 + protected TreeServiceBase( IServiceProvider serviceProvider, IUnitOfWork unitOfWork, ITreeRepository repository ) : base( serviceProvider, repository ) { + UnitOfWork = unitOfWork; + _repository = repository; + } + + #endregion + + #region 属性 + + /// + /// 工作单元 + /// + protected IUnitOfWork UnitOfWork { get; } + + #endregion + + #region CreateAsync(创建) + + /// + /// 创建 + /// + /// 创建参数 + public virtual async Task CreateAsync( TCreateRequest request ) { + request.CheckNull( nameof( request ) ); + request.Validate(); + var entity = ToEntity( request ); + entity.CheckNull( nameof( entity ) ); + await CreateAsync( entity ); + await CommitAsync(); + await CreateCommitAfterAsync( entity ); + return entity.Id.ToString(); + } + + /// + /// 转换为实体 + /// + /// 创建参数 + protected virtual TEntity ToEntity( TCreateRequest request ) { + return request.MapTo(); + } + + /// + /// 创建实体 + /// + private async Task CreateAsync( TEntity entity ) { + await CreateBeforeAsync( entity ); + entity.Init(); + var parent = await _repository.FindByIdAsync( entity.ParentId ); + entity.InitPath( parent ); + await _repository.AddAsync( entity ); + await CreateAfterAsync( entity ); + } + + /// + /// 创建前操作 + /// + /// 实体 + protected virtual Task CreateBeforeAsync( TEntity entity ) { + return Task.CompletedTask; + } + + /// + /// 创建后操作 + /// + /// 实体 + protected virtual Task CreateAfterAsync( TEntity entity ) { + return Task.CompletedTask; + } + + /// + /// 提交工作单元 + /// + protected virtual async Task CommitAsync() { + await UnitOfWork.CommitAsync(); + } + + /// + /// 创建提交后操作 + /// + /// 实体 + protected virtual Task CreateCommitAfterAsync( TEntity entity ) { + return Task.CompletedTask; + } + + #endregion + + #region UpdateAsync(修改) + + /// + public virtual async Task UpdateAsync( TUpdateRequest request ) { + request.CheckNull( nameof( request ) ); + request.Validate(); + if ( request.Id.IsEmpty() ) + throw new InvalidOperationException( R.IdIsEmpty ); + var oldEntity = await FindOldEntityAsync( request.Id ); + oldEntity.CheckNull( nameof( oldEntity ) ); + var entity = ToEntity( oldEntity.Clone(), request ); + entity.CheckNull( nameof( entity ) ); + var changes = oldEntity.GetChanges( entity ); + await UpdateAsync( entity ); + await CommitAsync(); + await UpdateCommitAfterAsync( entity, changes ); + } + + /// + /// 查找旧实体 + /// + /// 标识 + private async Task FindOldEntityAsync( object id ) { + return await _repository.FindByIdAsync( id ); + } + + /// + /// 转换为实体 + /// + /// 旧实体 + /// 修改参数 + protected virtual TEntity ToEntity( TEntity oldEntity, TUpdateRequest request ) { + return request.MapTo( oldEntity ); + } + + /// + /// 修改实体 + /// + /// 实体 + private async Task UpdateAsync( TEntity entity ) { + await UpdateBeforeAsync( entity ); + await _repository.UpdatePathAsync( entity ); + await _repository.UpdateAsync( entity ); + await UpdateAfterAsync( entity ); + } + + /// + /// 修改前操作 + /// + /// 实体 + protected virtual Task UpdateBeforeAsync( TEntity entity ) { + return Task.CompletedTask; + } + + /// + /// 修改后操作 + /// + /// 实体 + protected virtual Task UpdateAfterAsync( TEntity entity ) { + return Task.CompletedTask; + } + + /// + /// 修改提交后操作 + /// + /// 实体 + /// 变更值集合 + protected virtual Task UpdateCommitAfterAsync( TEntity entity, ChangeValueCollection changeValues ) { + return Task.CompletedTask; + } + + #endregion + + #region DeleteAsync(删除) + + /// + public virtual async Task DeleteAsync( string ids ) { + if ( ids.IsEmpty() ) + return; + var entities = await _repository.FindByIdsAsync( ids ); + if ( entities?.Count == 0 ) + return; + await DeleteBeforeAsync( entities ); + await _repository.RemoveAsync( entities ); + await DeleteAfterAsync( entities ); + await CommitAsync(); + await DeleteCommitAfterAsync( entities ); + } + + /// + /// 删除前操作 + /// + /// 实体列表 + protected virtual Task DeleteBeforeAsync( List entities ) { + return Task.CompletedTask; + } + + /// + /// 删除后操作 + /// + /// 实体集合 + protected virtual Task DeleteAfterAsync( List entities ) { + return Task.CompletedTask; + } + + /// + /// 删除提交后操作 + /// + /// 实体集合 + protected virtual Task DeleteCommitAfterAsync( List entities ) { + return Task.CompletedTask; + } + + #endregion + + #region EnableAsync(启用) + + /// + /// 启用 + /// + /// 标识列表 + public virtual async Task EnableAsync( string ids ) { + await Enable( ids, true ); + } + + /// + /// 启用 + /// + private async Task Enable( string ids, bool enabled ) { + if ( ids.IsEmpty() ) + return; + var entities = await _repository.FindByIdsAsync( ids ); + if ( entities == null || entities.Count == 0 ) + return; + foreach ( var entity in entities ) { + if ( enabled && await AllowEnable( entity ) == false ) + return; + if ( enabled == false && await AllowDisable( entity ) == false ) + return; + entity.Enabled = enabled; + await _repository.UpdateAsync( entity ); + } + await CommitAsync(); + if ( enabled ) { + await EnableCommitAfterAsync( entities ); + return; + } + await DisableCommitAfterAsync( entities ); + } + + /// + /// 允许启用 + /// + /// 实体 + protected virtual Task AllowEnable( TEntity entity ) { + return Task.FromResult( true ); + } + + /// + /// 允许禁用 + /// + /// 实体 + protected virtual Task AllowDisable( TEntity entity ) { + return Task.FromResult( true ); + } + + /// + /// 启用提交后操作 + /// + /// 实体集合 + protected virtual Task EnableCommitAfterAsync( List entities ) { + return Task.CompletedTask; + } + + /// + /// 禁用提交后操作 + /// + /// 实体集合 + protected virtual Task DisableCommitAfterAsync( List entities ) { + return Task.CompletedTask; + } + + #endregion + + #region DisableAsync(禁用) + + /// + /// 禁用 + /// + /// 标识列表 + public virtual Task DisableAsync( string ids ) { + return Enable( ids, false ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Application.EntityFrameworkCore/Usings.cs b/src/Util.Application.EntityFrameworkCore/Usings.cs new file mode 100644 index 000000000..7bf07ab32 --- /dev/null +++ b/src/Util.Application.EntityFrameworkCore/Usings.cs @@ -0,0 +1,4 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; diff --git a/src/Util.Application.WebApi/03-Util.Application.WebApi.csproj b/src/Util.Application.WebApi/03-Util.Application.WebApi.csproj new file mode 100644 index 000000000..b472c1813 --- /dev/null +++ b/src/Util.Application.WebApi/03-Util.Application.WebApi.csproj @@ -0,0 +1,49 @@ + + + + $(NetTargetFramework) + Util.Applications + icon.jpg + Util.Application.WebApi是Util应用框架基于Asp.Net Core Web Api的应用层类库 + Util.Application.WebApi + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Application.WebApi.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Application.WebApi.xml + + + + + True + False + + + + + + + + + + + + True + True + WebApiResource.resx + + + + + + PublicResXFileCodeGenerator + WebApiResource.Designer.cs + + + + diff --git a/src/Util.Application.WebApi/Controllers/CrudControllerBase.cs b/src/Util.Application.WebApi/Controllers/CrudControllerBase.cs new file mode 100644 index 000000000..dc3859bc7 --- /dev/null +++ b/src/Util.Application.WebApi/Controllers/CrudControllerBase.cs @@ -0,0 +1,118 @@ +using Util.Applications.Dtos; +using Util.Applications.Models; +using Util.Applications.Properties; +using Util.Data.Queries; + +namespace Util.Applications.Controllers; + +/// +/// Crud控制器 +/// +/// 数据传输对象类型 +/// 查询参数类型 +public abstract class CrudControllerBase : CrudControllerBase + where TDto : IDto, new() + where TQuery : IPage { + /// + /// 初始化Crud控制器 + /// + /// Crud服务 + protected CrudControllerBase( ICrudService service ) + : base( service ) { + } +} + +/// +/// Crud控制器 +/// +/// 数据传输对象类型 +/// 创建参数类型 +/// 修改参数类型 +/// 查询参数类型 +public abstract class CrudControllerBase : QueryControllerBase + where TDto : IDto, new() + where TCreateRequest : IRequest, new() + where TUpdateRequest : IDto, new() + where TQuery : IPage { + /// + /// Crud服务 + /// + private readonly ICrudService _service; + + /// + /// 初始化Crud控制器 + /// + /// Crud服务 + protected CrudControllerBase( ICrudService service ) + : base( service ) { + _service = service; + } + + /// + /// 创建 + /// + /// 创建参数 + protected async Task CreateAsync( TCreateRequest request ) { + if( request == null ) + return Fail( ApplicationResource.CreateRequestIsEmpty ); + CreateBefore( request ); + var id = await _service.CreateAsync( request ); + var result = await _service.GetByIdAsync( id ); + return Success( result ); + } + + /// + /// 创建前操作 + /// + /// 创建参数 + protected virtual void CreateBefore( TCreateRequest request ) { + } + + /// + /// 修改 + /// + /// 标识 + /// 修改参数 + protected async Task UpdateAsync( string id, TUpdateRequest request ) { + if( request == null ) + return Fail( ApplicationResource.UpdateRequestIsEmpty ); + if( id.IsEmpty() && request.Id.IsEmpty() ) + return Fail( ApplicationResource.IdIsEmpty ); + if( request.Id.IsEmpty() ) + request.Id = id; + UpdateBefore( request ); + await _service.UpdateAsync( request ); + var result = await _service.GetByIdAsync( request.Id ); + return Success( result ); + } + + /// + /// 修改前操作 + /// + /// 修改参数 + protected virtual void UpdateBefore( TUpdateRequest dto ) { + } + + /// + /// 删除 + /// + /// 标识列表 + protected async Task DeleteAsync( string ids ) { + await _service.DeleteAsync( ids ); + return Success(); + } + + /// + /// 批量保存 + /// + /// 保存参数 + protected async Task SaveAsync( SaveModel request ) { + if ( request == null ) + return Fail( ApplicationResource.RequestIsEmpty ); + var creationList = Util.Helpers.Json.ToObject>( request.CreationList ); + var updateList = Util.Helpers.Json.ToObject>( request.UpdateList ); + var deleteList = Util.Helpers.Json.ToObject>( request.DeleteList ); + await _service.SaveAsync( creationList, updateList, deleteList ); + return Success(); + } +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/Controllers/QueryControllerBase.cs b/src/Util.Application.WebApi/Controllers/QueryControllerBase.cs new file mode 100644 index 000000000..502d5b63e --- /dev/null +++ b/src/Util.Application.WebApi/Controllers/QueryControllerBase.cs @@ -0,0 +1,183 @@ +using Util.Applications.Dtos; +using Util.Applications.Properties; +using Util.Data; +using Util.Data.Queries; + +namespace Util.Applications.Controllers; + +/// +/// 查询控制器 +/// +/// 数据传输对象类型 +/// 查询参数类型 +public abstract class QueryControllerBase : WebApiControllerBase + where TQuery : IPage + where TDto : IDto, new() { + + #region 字段 + + /// + /// 查询服务 + /// + private readonly IQueryService _service; + + #endregion + + #region 构造方法 + + /// + /// 初始化查询控制器 + /// + /// 查询服务 + protected QueryControllerBase( IQueryService service ) { + _service = service; + } + + #endregion + + #region GetAsync(获取单个实体) + + /// + /// 获取单个实体 + /// + /// 标识 + protected async Task GetAsync( string id ) { + var result = await _service.GetByIdAsync( id ); + return Success( result ); + } + + #endregion + + #region QueryAsync(查询) + + /// + /// 查询 + /// + /// 查询参数 + protected async Task QueryAsync( TQuery query ) { + if ( query == null ) + return Fail( ApplicationResource.QueryIsEmpty ); + QueryBefore( query ); + var list = await _service.QueryAsync( query ); + var result = QueryAfter( list ); + return Success( result ); + } + + /// + /// 查询前操作 + /// + /// 查询参数 + protected virtual void QueryBefore( TQuery query ) { + } + + /// + /// 查询后操作,用于转换查询结果 + /// + /// 查询结果 + protected virtual dynamic QueryAfter( List list ) { + return list; + } + + #endregion + + #region PageQueryAsync(分页查询) + + /// + /// 分页查询 + /// + /// 查询参数 + protected async Task PageQueryAsync( TQuery query ) { + if ( query == null ) + return Fail( ApplicationResource.QueryIsEmpty ); + PageQueryBefore( query ); + var pageList = await _service.PageQueryAsync( query ); + var result = PageQueryAfter( pageList ); + return Success( result ); + } + + /// + /// 分页查询前操作 + /// + /// 查询参数 + protected virtual void PageQueryBefore( TQuery query ) { + } + + /// + /// 分页查询后操作,用于转换查询结果 + /// + /// 分页查询结果 + protected virtual dynamic PageQueryAfter( PageList pageList ) { + return pageList; + } + + #endregion + + #region GetItemsAsync(获取项列表) + + /// + /// 获取项列表 + /// + /// 查询参数 + protected async Task GetItemsAsync( TQuery query ) { + if ( query == null ) + return Fail( ApplicationResource.QueryIsEmpty ); + var result = new List(); + await LoadSelectedItems( result ); + query = FilterGetItemsQuery( query ); + await LoadItemsByQuery( result, query ); + return Success( result.Select( dto => ToItem( dto, query ) ) ); + } + + /// + /// 加载选中项 + /// + protected virtual async Task LoadSelectedItems( List items ) { + var keys = GetLoadKeys(); + if ( keys.IsEmpty() ) + return; + var selectedItems = await _service.GetByIdsAsync( keys ); + items.AddRange( selectedItems ); + } + + /// + /// 获取选中项标识列表 + /// + protected string GetLoadKeys() { + return Request.Query["load_keys"].SafeString(); + } + + /// + /// 过滤 GetItemsAsync 方法的查询参数 + /// + /// 查询参数 + protected virtual TQuery FilterGetItemsQuery( TQuery query ) { + return query; + } + + /// + /// 查询加载项 + /// + protected virtual async Task LoadItemsByQuery( List items, TQuery query ) { + var pageList = await _service.PageQueryAsync( query ); + items.AddRange( pageList.Data ); + } + + /// + /// 将Dto转换为列表项 + /// + /// 数据传输对象 + /// 查询参数 + protected virtual Item ToItem( TDto dto, TQuery query ) { + return ToItem( dto ); + } + + /// + /// 将Dto转换为列表项 + /// + /// 数据传输对象 + protected virtual Item ToItem( TDto dto ) { + throw new NotImplementedException( nameof( ToItem ) ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/Controllers/TreeControllerBase.cs b/src/Util.Application.WebApi/Controllers/TreeControllerBase.cs new file mode 100644 index 000000000..7f5cb83e1 --- /dev/null +++ b/src/Util.Application.WebApi/Controllers/TreeControllerBase.cs @@ -0,0 +1,181 @@ +using Util.Applications.Trees; +using Util.Data.Trees; +using Util.Applications.Properties; +using Util.Applications.Dtos; + +namespace Util.Applications.Controllers; + +/// +/// 树形控制器 +/// +/// 参数类型 +/// 查询参数类型 +public abstract class TreeControllerBase : TreeControllerBase + where TDto : class, ITreeNode, new() + where TQuery : class, ITreeQueryParameter { + /// + /// 初始化树形控制器 + /// + /// 树形服务 + protected TreeControllerBase( ITreeService service ) : base( service ) { + } +} + +/// +/// 树形控制器 +/// +/// 参数类型 +/// 创建参数类型 +/// 修改参数类型 +/// 查询参数类型 +public abstract class TreeControllerBase : TreeQueryControllerBase + where TDto : class, ITreeNode, new() + where TCreateRequest : IRequest, new() + where TUpdateRequest : IDto, new() + where TQuery : class, ITreeQueryParameter { + + #region 字段 + + /// + /// 树形服务 + /// + private readonly ITreeService _service; + + #endregion + + #region 构造方法 + + /// + /// 初始化树形控制器 + /// + /// 树形服务 + protected TreeControllerBase( ITreeService service ) : base( service ) { + _service = service ?? throw new ArgumentNullException( nameof( service ) ); + } + + #endregion + + #region CreateAsync(创建) + + /// + /// 创建 + /// + /// 创建参数 + protected async Task CreateAsync( TCreateRequest request ) { + if( request == null ) + return Fail( ApplicationResource.CreateRequestIsEmpty ); + CreateBefore( request ); + var id = await _service.CreateAsync( request ); + var result = await _service.GetByIdAsync( id ); + return Success( CreateAfter( result ) ); + } + + /// + /// 创建前操作 + /// + /// 创建参数 + protected virtual void CreateBefore( TCreateRequest request ) { + } + + /// + /// 创建后操作 + /// + /// 结果 + protected virtual object CreateAfter( TDto result ) { + return result; + } + + #endregion + + #region UpdateAsync(修改) + + /// + /// 修改 + /// + /// 标识 + /// 修改参数 + protected async Task UpdateAsync( string id, TUpdateRequest request ) { + if( request == null ) + return Fail( ApplicationResource.UpdateRequestIsEmpty ); + if( id.IsEmpty() && request.Id.IsEmpty() ) + return Fail( ApplicationResource.IdIsEmpty ); + if( request.Id.IsEmpty() ) + request.Id = id; + UpdateBefore( request ); + await _service.UpdateAsync( request ); + var result = await _service.GetByIdAsync( request.Id ); + return Success( UpdateAfter( result ) ); + } + + /// + /// 修改前操作 + /// + /// 修改参数 + protected virtual void UpdateBefore( TUpdateRequest dto ) { + } + + /// + /// 修改后操作 + /// + /// 结果 + protected virtual object UpdateAfter( TDto result ) { + return result; + } + + #endregion + + #region DeleteAsync(删除) + + /// + /// 删除 + /// + /// 标识列表 + protected async Task DeleteAsync( string ids ) { + await _service.DeleteAsync( ids ); + return Success(); + } + + #endregion + + #region EnableAsync(启用) + + /// + /// 启用 + /// + /// 标识列表 + protected async Task EnableAsync( string ids ) { + EnableBefore( ids ); + await _service.EnableAsync( ids ); + return Success(); + } + + /// + /// 启用前操作 + /// + /// 标识列表 + protected virtual void EnableBefore( string ids ) { + } + + #endregion + + #region DisableAsync(禁用) + + /// + /// 禁用 + /// + /// 标识列表 + protected async Task DisableAsync( string ids ) { + DisableBefore( ids ); + await _service.DisableAsync( ids ); + return Success(); + } + + /// + /// 禁用前操作 + /// + /// 标识列表 + protected virtual void DisableBefore( string ids ) { + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/Controllers/TreeQueryControllerBase.cs b/src/Util.Application.WebApi/Controllers/TreeQueryControllerBase.cs new file mode 100644 index 000000000..e0d783e83 --- /dev/null +++ b/src/Util.Application.WebApi/Controllers/TreeQueryControllerBase.cs @@ -0,0 +1,168 @@ +using Util.Applications.Trees; +using Util.Data.Trees; +using Util.Data; + +namespace Util.Applications.Controllers; + +/// +/// 树形查询控制器 +/// +/// 参数类型 +/// 查询参数类型 +public abstract class TreeQueryControllerBase : WebApiControllerBase + where TDto : class, ITreeNode, new() + where TQuery : class, ITreeQueryParameter { + + #region 字段 + + /// + /// 树形服务 + /// + private readonly ITreeQueryService _service; + + #endregion + + #region 构造方法 + + /// + /// 初始化树形查询控制器 + /// + /// 树形服务 + protected TreeQueryControllerBase( ITreeQueryService service ) { + _service = service ?? throw new ArgumentNullException( nameof( service ) ); + } + + #endregion + + #region GetMaxPageSize(获取最大分页大小 ) + + /// + /// 获取最大分页大小,默认值: 999 + /// + protected virtual int GetMaxPageSize() { + return 999; + } + + #endregion + + #region GetLoadMode(获取加载模式) + + /// + /// 获取加载模式,默认值: 同步加载模式 + /// + protected virtual LoadMode GetLoadMode() { + var loadMode = Request.Query["loadMode"].SafeString(); + var result = Util.Helpers.Enum.Parse( loadMode ); + if ( result != null ) + return result.Value; + return LoadMode.Sync; + } + + #endregion + + #region IsFirstLoad(是否首次加载) + + /// + /// 是否首次加载 + /// + protected virtual bool IsFirstLoad() { + var isSearch = Request.Query["is_search"].SafeString(); + if( isSearch == "false" ) + return true; + return false; + } + + #endregion + + #region IsExpandAll(是否展开所有节点) + + /// + /// 是否展开所有节点,仅对同步加载和同步查询有效 + /// + protected virtual bool IsExpandAll() { + var isExpandAll = Request.Query["is_expand_all"].SafeString(); + if ( isExpandAll == "true" ) + return true; + return false; + } + + #endregion + + #region IsExpandForRootAsync(根节点异步加载模式是否展开子节点) + + /// + /// 根节点异步加载模式是否展开子节点,默认值: true + /// + protected virtual bool IsExpandForRootAsync() { + var isExpand = Request.Query["is_expand_for_root_async"].SafeString(); + if ( isExpand == "false" ) + return false; + return true; + } + + #endregion + + #region GetLoadKeys(获取需要加载的标识列表) + + /// + /// 获取需要加载的标识列表,当异步模式首次加载时,将选中节点的相关父节点加载回来,标识列表以逗号分隔 + /// + /// 查询参数 + protected virtual string GetLoadKeys( TQuery query ) { + return Request.Query["load_keys"].SafeString(); + } + + #endregion + + #region GetAsync(获取单个实体) + + /// + /// 获取单个实体 + /// + /// 标识 + protected async Task GetAsync( string id ) { + var result = await _service.GetByIdAsync( id ); + return Success( result ); + } + + #endregion + + #region QueryAsync(树形表格查询) + + /// + /// 树形表格查询 + /// + /// 查询参数 + protected async Task QueryAsync( TQuery query ) { + var action = new TreeTableQueryAction( _service, GetLoadMode(), GetOperation( query ), + GetMaxPageSize(), IsFirstLoad(), IsExpandAll(), IsExpandForRootAsync(), QueryBefore, QueryAfter ); + var result = await action.QueryAsync( query ); + return Success( result ); + } + + /// + /// 获取加载操作 + /// + protected virtual LoadOperation GetOperation( TQuery query ) { + if ( query.ParentId.IsEmpty() ) + return LoadOperation.Query; + return LoadOperation.LoadChildren; + } + + /// + /// 查询前操作 + /// + /// 查询参数 + protected virtual void QueryBefore( TQuery query ) { + } + + /// + /// 查询后操作 + /// + /// 数据列表 + /// 查询参数 + protected virtual void QueryAfter( PageList data, TQuery query ) { + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/Controllers/WebApiControllerBase.cs b/src/Util.Application.WebApi/Controllers/WebApiControllerBase.cs new file mode 100644 index 000000000..d7afb402e --- /dev/null +++ b/src/Util.Application.WebApi/Controllers/WebApiControllerBase.cs @@ -0,0 +1,103 @@ +using Util.Applications.Filters; +using Util.AspNetCore; +using Util.Helpers; +using Util.Logging; +using Util.Properties; +using Util.Sessions; +using Util.SystemTextJson; + +namespace Util.Applications.Controllers; + +/// +/// WebApi控制器基类 +/// +[ApiController] +[Route( "api/[controller]" )] +[ExceptionHandler( Order = 1 )] +[ErrorLogFilter( Order = 2 )] +public abstract class WebApiControllerBase : ControllerBase { + /// + /// 会话 + /// + protected virtual ISession Session => UserSession.Instance; + + /// + /// 获取日志操作 + /// + protected virtual ILog GetLog() { + try { + var logFactory = Ioc.Create(); + return logFactory.CreateLog( GetType() ); + } + catch { + return NullLog.Instance; + } + } + + /// + /// 返回成功消息 + /// + /// 数据 + /// 消息 + /// Http状态码 + protected virtual IActionResult Success( dynamic data = null, string message = null, int? statusCode = 200 ) { + message ??= R.Success; + return GetResult( StateCode.Ok, message, data, statusCode ); + } + + /// + /// 获取结果 + /// + private IActionResult GetResult( string code, string message, dynamic data, int? httpStatusCode ) { + var options = GetJsonSerializerOptions(); + var resultFactory = HttpContext.RequestServices.GetService(); + if ( resultFactory == null ) + return new Result( code, message, data, httpStatusCode, options ); + return resultFactory.CreateResult( code, message, data, httpStatusCode, options ); + } + + /// + /// 获取Json序列化配置 + /// + private JsonSerializerOptions GetJsonSerializerOptions() { + var factory = HttpContext.RequestServices.GetService(); + if( factory != null ) + return factory.CreateOptions(); + return new JsonSerializerOptions { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ), + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = { + new DateTimeJsonConverter(), + new NullableDateTimeJsonConverter(), + new LongJsonConverter(), + new NullableLongJsonConverter() + } + }; + } + + /// + /// 返回失败消息 + /// + /// 消息 + /// Http状态码 + protected virtual IActionResult Fail( string message, int? statusCode = 200 ) { + return GetResult( StateCode.Fail, message, null, statusCode ); + } + + /// + /// 获取文件流结果,内容类型设置为 application/octet-stream + /// + /// 文件流 + protected IActionResult GetStreamResult( Stream stream ) { + return new FileStreamResult( stream, "application/octet-stream" ); + } + + /// + /// 获取文件流结果,内容类型设置为 application/octet-stream + /// + /// 文件流 + protected IActionResult GetStreamResult( byte[] stream ) { + return GetStreamResult( new MemoryStream( stream ) ); + } +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/Filters/ErrorLogFilterAttribute.cs b/src/Util.Application.WebApi/Filters/ErrorLogFilterAttribute.cs new file mode 100644 index 000000000..5b7a0f2d8 --- /dev/null +++ b/src/Util.Application.WebApi/Filters/ErrorLogFilterAttribute.cs @@ -0,0 +1,24 @@ +using Util.Aop; +using Util.Exceptions; + +namespace Util.Applications.Filters; + +/// +/// 错误日志过滤器 +/// +public class ErrorLogFilterAttribute : ExceptionFilterAttribute { + /// + /// 异常处理 + /// + public override void OnException( ExceptionContext context ) { + if( context == null ) + return; + var log = context.HttpContext.RequestServices.GetService>(); + var exception = context.Exception.GetRawException(); + if( exception is Warning warning ) { + log.LogWarning( warning, exception.Message ); + return; + } + log.LogError( exception, exception.Message ); + } +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/Filters/ExceptionHandlerAttribute.cs b/src/Util.Application.WebApi/Filters/ExceptionHandlerAttribute.cs new file mode 100644 index 000000000..4ae19508d --- /dev/null +++ b/src/Util.Application.WebApi/Filters/ExceptionHandlerAttribute.cs @@ -0,0 +1,82 @@ +using Util.Aop; +using Util.Helpers; +using Util.Exceptions; +using Util.AspNetCore; +using Util.SystemTextJson; + +namespace Util.Applications.Filters; + +/// +/// 异常处理过滤器 +/// +public class ExceptionHandlerAttribute : ExceptionFilterAttribute { + /// + /// 异常处理 + /// + public override void OnException( ExceptionContext context ) { + context.ExceptionHandled = true; + var message = context.Exception.GetPrompt( Web.Environment.IsProduction() ); + message = GetLocalizedMessages( context, message ); + var errorCode = context.Exception.GetErrorCode() ?? StateCode.Fail; + var httpStatusCode = context.Exception.GetHttpStatusCode() ?? 200; + context.Result = GetResult( context, errorCode, message, httpStatusCode ); + } + + /// + /// 获取本地化异常消息 + /// + protected virtual string GetLocalizedMessages( ExceptionContext context, string message ) { + var exception = context.Exception.GetRawException(); + if ( exception is not Warning warning ) + return message; + if( warning.IsLocalization == false ) + return message; + if ( warning.IsLocalization == null ) { + var localizationOptions = context.HttpContext.RequestServices.GetService>(); + if ( localizationOptions.Value.IsLocalizeWarning == false ) + return message; + } + var stringLocalizerFactory = context.HttpContext.RequestServices.GetService(); + if ( stringLocalizerFactory == null ) + return message; + var stringLocalizer = stringLocalizerFactory.Create( "Warning",null ); + var localizedString = stringLocalizer[message]; + if ( localizedString.ResourceNotFound == false ) + return localizedString.Value; + stringLocalizer = context.HttpContext.RequestServices.GetService(); + if ( stringLocalizer == null ) + return message; + return stringLocalizer[message]; + } + + /// + /// 获取结果 + /// + protected virtual IActionResult GetResult( ExceptionContext context, string code, string message, int? httpStatusCode ) { + var options = GetJsonSerializerOptions( context ); + var resultFactory = context.HttpContext.RequestServices.GetService(); + if ( resultFactory == null ) + return new Result( code, message, null, httpStatusCode, options ); + return resultFactory.CreateResult( code, message, null, httpStatusCode, options ); + } + + /// + /// 获取Json序列化配置 + /// + private JsonSerializerOptions GetJsonSerializerOptions( ExceptionContext context ) { + var factory = context.HttpContext.RequestServices.GetService(); + if( factory != null ) + return factory.CreateOptions(); + return new JsonSerializerOptions { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ), + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = { + new DateTimeJsonConverter(), + new NullableDateTimeJsonConverter(), + new LongJsonConverter(), + new NullableLongJsonConverter() + } + }; + } +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/Filters/LockAttribute.cs b/src/Util.Application.WebApi/Filters/LockAttribute.cs new file mode 100644 index 000000000..307ceaa4a --- /dev/null +++ b/src/Util.Application.WebApi/Filters/LockAttribute.cs @@ -0,0 +1,139 @@ +using Util.Applications.Locks; +using Util.AspNetCore; +using Util.Helpers; +using Util.Properties; +using Util.Sessions; +using Util.SystemTextJson; + +namespace Util.Applications.Filters; + +/// +/// 请求锁过滤器,用于防止重复提交 +/// +[AttributeUsage( AttributeTargets.Method )] +public class LockAttribute : ActionFilterAttribute { + /// + /// 业务标识 + /// + public string Key { get; set; } + /// + /// 业务锁类型 + /// + public LockType Type { get; set; } = LockType.User; + /// + /// 再次提交时间间隔,单位:秒 + /// + public int Interval { get; set; } + + /// + /// 执行 + /// + public override async Task OnActionExecutionAsync( ActionExecutingContext context, ActionExecutionDelegate next ) { + context.CheckNull( nameof( context ) ); + next.CheckNull( nameof( next ) ); + var @lock = CreateLock( context ); + var key = GetKey( context ); + var isSuccess = false; + try { + isSuccess = await @lock.LockAsync( key, GetExpiration() ); + if ( isSuccess == false ) { + context.Result = GetResult( context,StateCode.Fail, GetFailMessage( context ) ); + return; + } + OnActionExecuting( context ); + if ( context.Result != null ) + return; + var executedContext = await next(); + OnActionExecuted( executedContext ); + } + finally { + if ( isSuccess ) { + await @lock.UnLockAsync(); + } + } + } + + /// + /// 创建业务锁 + /// + protected virtual ILock CreateLock( ActionExecutingContext context ) { + return context.HttpContext.RequestServices.GetService() ?? NullLock.Instance; + } + + /// + /// 获取锁定标识 + /// + protected virtual string GetKey( ActionExecutingContext context ) { + var userId = string.Empty; + if ( Type == LockType.User ) + userId = $"{GetUserId( context )}_"; + return string.IsNullOrWhiteSpace( Key ) ? $"{userId}{Web.Request.Path}" : $"{userId}{Key}"; + } + + /// + /// 获取用户标识 + /// + protected string GetUserId( ActionExecutingContext context ) { + var session = context.HttpContext.RequestServices.GetService(); + return session?.UserId; + } + + /// + /// 获取到期时间间隔 + /// + private TimeSpan? GetExpiration() { + if ( Interval == 0 ) + return null; + return TimeSpan.FromSeconds( Interval ); + } + + /// + /// 获取结果 + /// + private IActionResult GetResult( ActionExecutingContext context,string code, string message ) { + var options = GetJsonSerializerOptions( context ); + var resultFactory = context.HttpContext.RequestServices.GetService(); + if ( resultFactory == null ) + return new Result( code, message,options: options ); + return resultFactory.CreateResult( code, message, null, null, options ); + } + + /// + /// 获取Json序列化配置 + /// + private JsonSerializerOptions GetJsonSerializerOptions( ActionExecutingContext context ) { + var factory = context.HttpContext.RequestServices.GetService(); + if( factory != null ) + return factory.CreateOptions(); + return new JsonSerializerOptions { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ), + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = { + new DateTimeJsonConverter(), + new NullableDateTimeJsonConverter(), + new LongJsonConverter(), + new NullableLongJsonConverter() + } + }; + } + + /// + /// 获取失败消息 + /// + protected virtual string GetFailMessage( ActionExecutingContext context ) { + if ( Type == LockType.User ) + return GetLocalizedMessage( context, R.UserDuplicateRequest ); + return GetLocalizedMessage( context, R.GlobalDuplicateRequest ); + } + + /// + /// 获取本地化消息 + /// + protected virtual string GetLocalizedMessage( ActionExecutingContext context, string message ) { + var stringLocalizer = context.HttpContext.RequestServices.GetService(); + if ( stringLocalizer == null ) + return message; + return stringLocalizer[message]; + } +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/IResultFactory.cs b/src/Util.Application.WebApi/IResultFactory.cs new file mode 100644 index 000000000..072e5ecab --- /dev/null +++ b/src/Util.Application.WebApi/IResultFactory.cs @@ -0,0 +1,18 @@ +using Util.Dependency; + +namespace Util.Applications; + +/// +/// 返回结果工厂 +/// +public interface IResultFactory : ISingletonDependency { + /// + /// 创建返回结果 + /// + /// 业务状态码 + /// 消息 + /// 数据 + /// Http状态码 + /// Json序列化配置 + IActionResult CreateResult( string code, string message, dynamic data, int? httpStatusCode, JsonSerializerOptions options ); +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/Infrastructure/ServiceRegistrarConfigExtensions.cs b/src/Util.Application.WebApi/Infrastructure/ServiceRegistrarConfigExtensions.cs new file mode 100644 index 000000000..0b0977cfc --- /dev/null +++ b/src/Util.Application.WebApi/Infrastructure/ServiceRegistrarConfigExtensions.cs @@ -0,0 +1,26 @@ +using Util.Infrastructure; + +namespace Util.Applications.Infrastructure; + +/// +/// Web Api服务注册器配置扩展 +/// +public static class ServiceRegistrarConfigExtensions { + /// + /// 启用Web Api服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig EnableWebApiServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Enable( WebApiServiceRegistrar.ServiceName ); + return config; + } + + /// + ///禁用Web Api服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig DisableWebApiServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Disable( WebApiServiceRegistrar.ServiceName ); + return config; + } +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/Infrastructure/WebApiServiceRegistrar.cs b/src/Util.Application.WebApi/Infrastructure/WebApiServiceRegistrar.cs new file mode 100644 index 000000000..9c9f4da0a --- /dev/null +++ b/src/Util.Application.WebApi/Infrastructure/WebApiServiceRegistrar.cs @@ -0,0 +1,120 @@ +using Microsoft.AspNetCore.Http; +using Util.Applications.Logging; +using Util.AspNetCore; +using Util.Infrastructure; +using Util.Logging; +using Util.SystemTextJson; + +namespace Util.Applications.Infrastructure; + +/// +/// Web Api服务注册器 +/// +public class WebApiServiceRegistrar : IServiceRegistrar { + /// + /// 获取服务名 + /// + public static string ServiceName => "Util.Applications.Infrastructure.WebApiServiceRegistrar"; + + /// + /// 排序号 + /// + public int OrderId => 1210; + + /// + /// 是否启用 + /// + public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName ); + + /// + /// 注册服务 + /// + /// 服务上下文 + public Action Register( ServiceContext serviceContext ) { + serviceContext.HostBuilder.ConfigureServices( ( context, services ) => { + services.AddSingleton(); + services.AddSingleton(); + ConfigApiBehaviorOptions( services ); + } ); + return null; + } + + /// + /// 配置ApiBehavior + /// + private void ConfigApiBehaviorOptions( IServiceCollection services ) { + services.Configure( options => { + options.SuppressModelStateInvalidFilter = false; + var builtInFactory = options.InvalidModelStateResponseFactory; + options.InvalidModelStateResponseFactory = actionContext => { + var log = actionContext.HttpContext.RequestServices.GetRequiredService>(); + if ( actionContext.ModelState.IsValid ) + return builtInFactory( actionContext ); + var error = GetModelError( actionContext ); + log.LogError( error.Exception, "Model Binding Failed,ErrorMessage: {ErrorMessage}", error.ErrorMessage ); + var message = GetLocalizedMessages( actionContext.HttpContext, error.ErrorMessage ); + return GetResult( actionContext.HttpContext, StateCode.Fail, message, 200 ); + }; + } ); + } + + /// + /// 获取模型错误 + /// + private ModelError GetModelError( ActionContext actionContext ) { + foreach ( var state in actionContext.ModelState ) { + foreach ( var error in state.Value.Errors ) + return error; + } + return null; + } + + /// + /// 获取本地化异常消息 + /// + protected virtual string GetLocalizedMessages( HttpContext context, string message ) { + var stringLocalizerFactory = context.RequestServices.GetService(); + if ( stringLocalizerFactory == null ) + return message; + var assemblyName = new AssemblyName( GetType().Assembly.FullName ); + var stringLocalizer = stringLocalizerFactory.Create( "Warning", assemblyName.Name ); + var localizedString = stringLocalizer[message]; + if ( localizedString.ResourceNotFound == false ) + return localizedString.Value; + stringLocalizer = context.RequestServices.GetService(); + if ( stringLocalizer == null ) + return message; + return stringLocalizer[message]; + } + + /// + /// 获取结果 + /// + protected virtual IActionResult GetResult( HttpContext context, string code, string message, int? httpStatusCode ) { + var options = GetJsonSerializerOptions( context ); + var resultFactory = context.RequestServices.GetService(); + if ( resultFactory == null ) + return new Result( code, message, null, httpStatusCode, options ); + return resultFactory.CreateResult( code, message, null, httpStatusCode, options ); + } + + /// + /// 获取Json序列化配置 + /// + private JsonSerializerOptions GetJsonSerializerOptions( HttpContext context ) { + var factory = context.RequestServices.GetService(); + if( factory != null ) + return factory.CreateOptions(); + return new JsonSerializerOptions { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ), + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = { + new DateTimeJsonConverter(), + new NullableDateTimeJsonConverter(), + new LongJsonConverter(), + new NullableLongJsonConverter() + } + }; + } +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/JsonSerializerOptionsFactory.cs b/src/Util.Application.WebApi/JsonSerializerOptionsFactory.cs new file mode 100644 index 000000000..15c715fd9 --- /dev/null +++ b/src/Util.Application.WebApi/JsonSerializerOptionsFactory.cs @@ -0,0 +1,26 @@ +using Util.AspNetCore; +using Util.SystemTextJson; + +namespace Util.Applications; + +/// +/// Json序列化配置工厂 +/// +public class JsonSerializerOptionsFactory : IJsonSerializerOptionsFactory { + /// + /// 创建Json序列化配置 + /// + public JsonSerializerOptions CreateOptions() { + return new JsonSerializerOptions { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ), + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = { + new DateTimeJsonConverter(), + new NullableDateTimeJsonConverter(), + new LongJsonConverter(), + new NullableLongJsonConverter() + } + }; + } +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/Logging/LogContextAccessor.cs b/src/Util.Application.WebApi/Logging/LogContextAccessor.cs new file mode 100644 index 000000000..6b0a054fd --- /dev/null +++ b/src/Util.Application.WebApi/Logging/LogContextAccessor.cs @@ -0,0 +1,22 @@ +using Util.Helpers; +using Util.Logging; + +namespace Util.Applications.Logging; + +/// +/// 日志上下文访问器 +/// +public class LogContextAccessor : ILogContextAccessor { + /// + /// 日志上下文键名 + /// + public const string LogContextKey = "Util.Logging.LogContext"; + + /// + /// 日志上下文 + /// + public LogContext Context { + get => Util.Helpers.Convert.To( Web.HttpContext?.Items[LogContextKey] ); + set => Web.HttpContext.Items[LogContextKey] = value; + } +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/Logging/LogContextMiddleware.cs b/src/Util.Application.WebApi/Logging/LogContextMiddleware.cs new file mode 100644 index 000000000..cd1cba14d --- /dev/null +++ b/src/Util.Application.WebApi/Logging/LogContextMiddleware.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Http; +using Util.Logging; + +namespace Util.Applications.Logging; + +/// +/// 日志上下文中间件 +/// +public class LogContextMiddleware { + /// + /// 下个中间件 + /// + private readonly RequestDelegate _next; + + /// + /// 初始化日志上下文中间件 + /// + /// 下个中间件 + public LogContextMiddleware( RequestDelegate next ) { + _next = next; + } + + /// + /// 执行中间件 + /// + /// Http上下文 + public async Task Invoke( HttpContext context ) { + var traceId = context.Request.Headers["x-correlation-id"].SafeString(); + if ( traceId.IsEmpty() ) + traceId = context.TraceIdentifier; + var session = context.RequestServices.GetService(); + var environment = context.RequestServices.GetService(); + var logContext = new LogContext { + Stopwatch = Stopwatch.StartNew(), + TraceId = traceId, + UserId = session?.UserId, + Application = environment?.ApplicationName, + Environment = environment?.EnvironmentName + }; + context.Items[LogContextAccessor.LogContextKey] = logContext; + await _next( context ); + } +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/Logging/LogContextMiddlewareExtensions.cs b/src/Util.Application.WebApi/Logging/LogContextMiddlewareExtensions.cs new file mode 100644 index 000000000..9a8ac0ef8 --- /dev/null +++ b/src/Util.Application.WebApi/Logging/LogContextMiddlewareExtensions.cs @@ -0,0 +1,14 @@ +namespace Util.Applications.Logging; + +/// +/// 日志上下文中间件扩展 +/// +public static class LogContextMiddlewareExtensions { + /// + /// 注册日志上下文中间件 + /// + /// 应用程序生成器 + public static IApplicationBuilder UseLogContext( this IApplicationBuilder builder ) { + return builder.UseMiddleware(); + } +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/Logging/LogContextStartupFilter.cs b/src/Util.Application.WebApi/Logging/LogContextStartupFilter.cs new file mode 100644 index 000000000..a617faa41 --- /dev/null +++ b/src/Util.Application.WebApi/Logging/LogContextStartupFilter.cs @@ -0,0 +1,17 @@ +namespace Util.Applications.Logging; + +/// +/// 日志上下文启动过滤器 +/// +public class LogContextStartupFilter : IStartupFilter { + /// + /// 配置日志上下文中间件 + /// + /// 下个中间件操作 + public Action Configure( Action next ) { + return app => { + app.UseLogContext(); + next( app ); + }; + } +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/Models/SaveModel.cs b/src/Util.Application.WebApi/Models/SaveModel.cs new file mode 100644 index 000000000..956db768a --- /dev/null +++ b/src/Util.Application.WebApi/Models/SaveModel.cs @@ -0,0 +1,19 @@ +namespace Util.Applications.Models; + +/// +/// 保存参数 +/// +public class SaveModel { + /// + /// 创建列表 + /// + public string CreationList { get; set; } + /// + /// 更新列表 + /// + public string UpdateList { get; set; } + /// + /// 删除列表 + /// + public string DeleteList { get; set; } +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/Properties/WebApiResource.Designer.cs b/src/Util.Application.WebApi/Properties/WebApiResource.Designer.cs new file mode 100644 index 000000000..290fd994f --- /dev/null +++ b/src/Util.Application.WebApi/Properties/WebApiResource.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace Util.Applications.Properties { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class WebApiResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal WebApiResource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Util.Applications.Properties.WebApiResource", typeof(WebApiResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/src/Util.Application.WebApi/Properties/WebApiResource.resx b/src/Util.Application.WebApi/Properties/WebApiResource.resx new file mode 100644 index 000000000..29dcb1b3a --- /dev/null +++ b/src/Util.Application.WebApi/Properties/WebApiResource.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Util.Application.WebApi/Result.cs b/src/Util.Application.WebApi/Result.cs new file mode 100644 index 000000000..cb14d47f5 --- /dev/null +++ b/src/Util.Application.WebApi/Result.cs @@ -0,0 +1,66 @@ +using Util.SystemTextJson; + +namespace Util.Applications; + +/// +/// 返回结果 +/// +public class Result : JsonResult { + /// + /// 业务状态码 + /// + public string Code { get; } + /// + /// 消息 + /// + public string Message { get; } + /// + /// 数据 + /// + public dynamic Data { get; } + + /// + /// 初始化返回结果 + /// + /// 业务状态码 + /// 消息 + /// 数据 + /// Http状态码 + /// Json序列化配置 + public Result( string code, string message, dynamic data = null, int? httpStatusCode = null, JsonSerializerOptions options = null ) : base( null ) { + Code = code; + Message = message; + Data = data; + SerializerSettings = GetOptions( options ); + StatusCode = httpStatusCode; + } + + /// + /// 获取Json序列化配置 + /// + private JsonSerializerOptions GetOptions( JsonSerializerOptions options ) { + if ( options != null ) + return options; + return new JsonSerializerOptions { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ), + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = { + new DateTimeJsonConverter(), + new NullableDateTimeJsonConverter() + } + }; + } + + /// + /// 执行结果 + /// + public override Task ExecuteResultAsync( ActionContext context ) { + Value = new { + Code, + Message, + Data + }; + return base.ExecuteResultAsync( context ); + } +} \ No newline at end of file diff --git a/src/Util.Application.WebApi/Usings.cs b/src/Util.Application.WebApi/Usings.cs new file mode 100644 index 000000000..378c1b1b6 --- /dev/null +++ b/src/Util.Application.WebApi/Usings.cs @@ -0,0 +1,21 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Diagnostics; +global using System.IO; +global using System.Reflection; +global using System.Text.Json; +global using System.Text.Encodings.Web; +global using System.Text.Unicode; +global using System.Text.Json.Serialization; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.Logging; +global using Microsoft.AspNetCore.Mvc; +global using Microsoft.AspNetCore.Mvc.Filters; +global using Microsoft.Extensions.Localization; +global using Microsoft.AspNetCore.Hosting; +global using Microsoft.AspNetCore.Mvc.ModelBinding; +global using Microsoft.AspNetCore.Builder; +global using Microsoft.Extensions.Options; \ No newline at end of file diff --git a/src/Util.Application/01-Util.Application.csproj b/src/Util.Application/01-Util.Application.csproj new file mode 100644 index 000000000..e1d6b9e34 --- /dev/null +++ b/src/Util.Application/01-Util.Application.csproj @@ -0,0 +1,51 @@ + + + + $(NetTargetFramework) + Util.Applications + icon.jpg + Util.Application是Util应用框架的应用层基础类库 + Util.Application + + + + .\obj\Debug\$(NetTargetFramework)\Util.Application.xml + + + + + + .\obj\Release\$(NetTargetFramework)\Util.Application.xml + + + + + True + False + + + + + + + + + + + + + + True + True + ApplicationResource.resx + + + + + + PublicResXFileCodeGenerator + ApplicationResource.Designer.cs + + + + diff --git a/src/Util.Application/AppBuilderExtensions.cs b/src/Util.Application/AppBuilderExtensions.cs new file mode 100644 index 000000000..8324bc74c --- /dev/null +++ b/src/Util.Application/AppBuilderExtensions.cs @@ -0,0 +1,21 @@ +using Util.Applications.Locks; +using Util.Configs; + +namespace Util.Applications; + +/// +/// 业务锁操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置业务锁 + /// + /// 应用生成器 + public static IAppBuilder AddLock( this IAppBuilder builder ) { + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + services.TryAddTransient(); + } ); + return builder; + } +} \ No newline at end of file diff --git a/src/Util.Application/Dtos/DtoBase.cs b/src/Util.Application/Dtos/DtoBase.cs new file mode 100644 index 000000000..0e3f2c165 --- /dev/null +++ b/src/Util.Application/Dtos/DtoBase.cs @@ -0,0 +1,11 @@ +namespace Util.Applications.Dtos; + +/// +/// 数据传输对象 +/// +public abstract class DtoBase : RequestBase, IDto { + /// + /// 标识 + /// + public string Id { get; set; } +} \ No newline at end of file diff --git a/src/Util.Application/Dtos/IDto.cs b/src/Util.Application/Dtos/IDto.cs new file mode 100644 index 000000000..dc6878696 --- /dev/null +++ b/src/Util.Application/Dtos/IDto.cs @@ -0,0 +1,9 @@ +using Util.Data; + +namespace Util.Applications.Dtos; + +/// +/// 数据传输对象 +/// +public interface IDto : IRequest, IDataKey { +} \ No newline at end of file diff --git a/src/Util.Application/Dtos/IRequest.cs b/src/Util.Application/Dtos/IRequest.cs new file mode 100644 index 000000000..8b7687568 --- /dev/null +++ b/src/Util.Application/Dtos/IRequest.cs @@ -0,0 +1,9 @@ +using Util.Validation; + +namespace Util.Applications.Dtos; + +/// +/// 请求参数 +/// +public interface IRequest : IValidation { +} \ No newline at end of file diff --git a/src/Util.Application/Dtos/RequestBase.cs b/src/Util.Application/Dtos/RequestBase.cs new file mode 100644 index 000000000..7b647d0f4 --- /dev/null +++ b/src/Util.Application/Dtos/RequestBase.cs @@ -0,0 +1,28 @@ +using Util.Exceptions; +using Util.Helpers; +using Util.Validation; + +namespace Util.Applications.Dtos; + +/// +/// 请求参数 +/// +public abstract class RequestBase : IRequest { + /// + /// 验证 + /// + public virtual ValidationResultCollection Validate() { + var validator = Ioc.Create(); + if ( validator == null ) { + var result = DataAnnotationValidation.Validate( this ); + if ( result.IsValid ) + return ValidationResultCollection.Success; + throw new Warning( result.First().ErrorMessage ); + } + var actionContext = new ActionContext(); + validator.Validate( actionContext, null, string.Empty, this ); + if ( actionContext.ModelState.IsValid ) + return ValidationResultCollection.Success; + throw new Warning( actionContext.ModelState.Values.First().Errors.First().ErrorMessage ); + } +} \ No newline at end of file diff --git a/src/Util.Application/ExceptionExtensions.cs b/src/Util.Application/ExceptionExtensions.cs new file mode 100644 index 000000000..40a395f59 --- /dev/null +++ b/src/Util.Application/ExceptionExtensions.cs @@ -0,0 +1,56 @@ +using Util.Aop; +using Util.Exceptions; +using Util.Properties; + +namespace Util.Applications; + +/// +/// 异常扩展 +/// +public static class ExceptionExtensions { + /// + /// 获取异常提示 + /// + /// 异常 + /// 是否生产环境 + public static string GetPrompt( this Exception exception, bool isProduction = false ) { + if( exception == null ) + return null; + exception = exception.GetRawException(); + if( exception == null ) + return null; + if( exception is Warning warning ) + return warning.GetMessage( isProduction ); + return isProduction ? R.SystemError : exception.Message; + } + + /// + /// 获取Http状态码 + /// + /// 异常 + public static int? GetHttpStatusCode( this Exception exception ) { + if ( exception == null ) + return null; + exception = exception.GetRawException(); + if ( exception == null ) + return null; + if ( exception is Warning warning ) + return warning.HttpStatusCode; + return null; + } + + /// + /// 获取错误码 + /// + /// 异常 + public static string GetErrorCode( this Exception exception ) { + if ( exception == null ) + return null; + exception = exception.GetRawException(); + if ( exception == null ) + return null; + if ( exception is Warning warning ) + return warning.Code; + return null; + } +} \ No newline at end of file diff --git a/src/Util.Application/ICrudService.cs b/src/Util.Application/ICrudService.cs new file mode 100644 index 000000000..4946ab5c9 --- /dev/null +++ b/src/Util.Application/ICrudService.cs @@ -0,0 +1,50 @@ +using Util.Applications.Dtos; +using Util.Data.Queries; + +namespace Util.Applications; + +/// +/// 增删改查服务 +/// +/// 数据传输对象类型 +/// 查询参数类型 +public interface ICrudService : ICrudService + where TDto : IDto, new() + where TQuery : IPage { +} + +/// +/// 增删改查服务 +/// +/// 数据传输对象类型 +/// 创建参数类型 +/// 修改参数类型 +/// 查询参数类型 +public interface ICrudService : IQueryService + where TDto : IDto, new() + where TCreateRequest : IRequest, new() + where TUpdateRequest : IDto, new() + where TQuery : IPage { + /// + /// 创建 + /// + /// 创建参数 + Task CreateAsync( TCreateRequest request ); + /// + /// 修改 + /// + /// 修改参数 + Task UpdateAsync( TUpdateRequest request ); + /// + /// 删除 + /// + /// 用逗号分隔的标识列表,范例:"1,2" + Task DeleteAsync( string ids ); + /// + /// 批量保存 + /// + /// 新增列表 + /// 修改列表 + /// 删除列表 + Task> SaveAsync( List creationList, List updateList, List deleteList ); +} \ No newline at end of file diff --git a/src/Util.Application/IQueryService.cs b/src/Util.Application/IQueryService.cs new file mode 100644 index 000000000..a9bf58afa --- /dev/null +++ b/src/Util.Application/IQueryService.cs @@ -0,0 +1,38 @@ +using Util.Data; +using Util.Data.Queries; + +namespace Util.Applications; + +/// +/// 查询服务 +/// +/// 数据传输对象类型 +/// 查询参数类型 +public interface IQueryService : IService + where TDto : new() + where TQuery : IPage { + /// + /// 获取全部实体 + /// + Task> GetAllAsync(); + /// + /// 通过标识获取实体 + /// + /// 实体标识 + Task GetByIdAsync( object id ); + /// + /// 通过标识列表获取实体列表 + /// + /// 用逗号分隔的标识列表,范例:"1,2" + Task> GetByIdsAsync( string ids ); + /// + /// 查询 + /// + /// 查询参数 + Task> QueryAsync( TQuery param ); + /// + /// 分页查询 + /// + /// 查询参数 + Task> PageQueryAsync( TQuery param ); +} \ No newline at end of file diff --git a/src/Util.Application/IService.cs b/src/Util.Application/IService.cs new file mode 100644 index 000000000..54566933b --- /dev/null +++ b/src/Util.Application/IService.cs @@ -0,0 +1,10 @@ +using Util.Aop; +using Util.Dependency; + +namespace Util.Applications; + +/// +/// 应用服务 +/// +public interface IService : IScopeDependency, IAopProxy { +} \ No newline at end of file diff --git a/src/Util.Application/Locks/DefaultLock.cs b/src/Util.Application/Locks/DefaultLock.cs new file mode 100644 index 000000000..bdfb981b7 --- /dev/null +++ b/src/Util.Application/Locks/DefaultLock.cs @@ -0,0 +1,47 @@ +using Util.Caching; + +namespace Util.Applications.Locks; + +/// +/// 业务锁 +/// +public class DefaultLock : ILock { + /// + /// 缓存 + /// + private readonly ICache _cache; + /// + /// 锁定标识 + /// + private string _key; + /// + /// 锁定时间间隔 + /// + private TimeSpan? _expiration; + + /// + /// 初始化业务锁 + /// + /// 缓存 + public DefaultLock( ICache cache ) { + _cache = cache; + } + + /// + public async Task LockAsync( string key, TimeSpan? expiration = null ) { + _key = key; + _expiration = expiration; + if ( await _cache.ExistsAsync( key ) ) + return false; + return await _cache.TrySetAsync( key, 1, new CacheOptions { Expiration = expiration } ); + } + + /// + public async Task UnLockAsync() { + if ( _expiration != null ) + return; + if ( await _cache.ExistsAsync( _key ) == false ) + return; + await _cache.RemoveAsync( _key ); + } +} \ No newline at end of file diff --git a/src/Util.Application/Locks/ILock.cs b/src/Util.Application/Locks/ILock.cs new file mode 100644 index 000000000..e27fccf47 --- /dev/null +++ b/src/Util.Application/Locks/ILock.cs @@ -0,0 +1,17 @@ +namespace Util.Applications.Locks; + +/// +/// 业务锁 +/// +public interface ILock { + /// + /// 锁定,true表示本次操作成功锁定资源,false表示资源已被之前的操作锁定 + /// + /// 锁定标识 + /// 锁定时间间隔 + Task LockAsync( string key, TimeSpan? expiration = null ); + /// + /// 解除锁定 + /// + Task UnLockAsync(); +} \ No newline at end of file diff --git a/src/Util.Application/Locks/LockType.cs b/src/Util.Application/Locks/LockType.cs new file mode 100644 index 000000000..7b8ff8c6e --- /dev/null +++ b/src/Util.Application/Locks/LockType.cs @@ -0,0 +1,15 @@ +namespace Util.Applications.Locks; + +/// +/// 业务锁类型 +/// +public enum LockType { + /// + /// 用户锁,当用户发出多个执行该操作的请求,只有第一个请求被执行,其它请求被抛弃,其它用户不受影响 + /// + User = 0, + /// + /// 全局锁,该操作同时只有一个用户的请求被执行 + /// + Global = 1 +} \ No newline at end of file diff --git a/src/Util.Application/Locks/NullLock.cs b/src/Util.Application/Locks/NullLock.cs new file mode 100644 index 000000000..f9f747ecc --- /dev/null +++ b/src/Util.Application/Locks/NullLock.cs @@ -0,0 +1,21 @@ +namespace Util.Applications.Locks; + +/// +/// 空业务锁 +/// +public class NullLock : ILock { + /// + /// 空业务锁 + /// + public static readonly ILock Instance = new NullLock(); + + /// + public Task LockAsync( string key, TimeSpan? expiration = null ) { + return Task.FromResult( true ); + } + + /// + public Task UnLockAsync() { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/Util.Application/Properties/ApplicationResource.Designer.cs b/src/Util.Application/Properties/ApplicationResource.Designer.cs new file mode 100644 index 000000000..1ed1fa2d8 --- /dev/null +++ b/src/Util.Application/Properties/ApplicationResource.Designer.cs @@ -0,0 +1,117 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace Util.Applications.Properties { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class ApplicationResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ApplicationResource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Util.Applications.Properties.ApplicationResource", typeof(ApplicationResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 创建参数不能为空 的本地化字符串。 + /// + public static string CreateRequestIsEmpty { + get { + return ResourceManager.GetString("CreateRequestIsEmpty", resourceCulture); + } + } + + /// + /// 查找类似 标识不能为空 的本地化字符串。 + /// + public static string IdIsEmpty { + get { + return ResourceManager.GetString("IdIsEmpty", resourceCulture); + } + } + + /// + /// 查找类似 父标识不能为空 的本地化字符串。 + /// + public static string ParentIdIsEmpty { + get { + return ResourceManager.GetString("ParentIdIsEmpty", resourceCulture); + } + } + + /// + /// 查找类似 查询参数不能为空 的本地化字符串。 + /// + public static string QueryIsEmpty { + get { + return ResourceManager.GetString("QueryIsEmpty", resourceCulture); + } + } + + /// + /// 查找类似 参数不能为空 的本地化字符串。 + /// + public static string RequestIsEmpty { + get { + return ResourceManager.GetString("RequestIsEmpty", resourceCulture); + } + } + + /// + /// 查找类似 修改参数不能为空 的本地化字符串。 + /// + public static string UpdateRequestIsEmpty { + get { + return ResourceManager.GetString("UpdateRequestIsEmpty", resourceCulture); + } + } + } +} diff --git a/src/Util.Application/Properties/ApplicationResource.resx b/src/Util.Application/Properties/ApplicationResource.resx new file mode 100644 index 000000000..2cf877015 --- /dev/null +++ b/src/Util.Application/Properties/ApplicationResource.resx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 创建参数不能为空 + + + 标识不能为空 + + + 父标识不能为空 + + + 查询参数不能为空 + + + 参数不能为空 + + + 修改参数不能为空 + + \ No newline at end of file diff --git a/src/Util.Application/ServiceBase.cs b/src/Util.Application/ServiceBase.cs new file mode 100644 index 000000000..3dc936891 --- /dev/null +++ b/src/Util.Application/ServiceBase.cs @@ -0,0 +1,44 @@ +namespace Util.Applications; + +/// +/// 应用服务 +/// +public abstract class ServiceBase : IService { + /// + /// 初始化应用服务 + /// + /// 服务提供器 + protected ServiceBase( IServiceProvider serviceProvider ) { + ServiceProvider = serviceProvider ?? throw new ArgumentNullException( nameof( serviceProvider ) ); + Session = serviceProvider.GetService() ?? NullSession.Instance; + IntegrationEventBus = serviceProvider.GetService() ?? NullIntegrationEventBus.Instance; + var logFactory = serviceProvider.GetService(); + Log = logFactory?.CreateLog( GetType() ) ?? NullLog.Instance; + L = serviceProvider.GetService() ?? NullStringLocalizer.Instance; + } + + /// + /// 服务提供器 + /// + protected IServiceProvider ServiceProvider { get; } + + /// + /// 用户会话 + /// + protected ISession Session { get; set; } + + /// + /// 集成事件总线 + /// + protected IIntegrationEventBus IntegrationEventBus { get; } + + /// + /// 日志操作 + /// + protected ILog Log { get; } + + /// + /// 本地化字符串 + /// + protected IStringLocalizer L { get; set; } +} \ No newline at end of file diff --git a/src/Util.Application/TreeNodeExtensions.cs b/src/Util.Application/TreeNodeExtensions.cs new file mode 100644 index 000000000..9262a475c --- /dev/null +++ b/src/Util.Application/TreeNodeExtensions.cs @@ -0,0 +1,41 @@ +using Util.Applications.Trees; + +namespace Util.Applications; + +/// +/// 树形节点扩展 +/// +public static class TreeNodeExtensions { + /// + /// 从路径中获取所有上级节点标识 + /// + /// 树节点 + /// 是否排除当前节点,默认排除自身 + public static List GetParentIdsFromPath( this ITreeNode node, bool excludeSelf = true ) { + if ( node == null || node.Path.IsEmpty() ) + return new List(); + var result = node.Path.Split( ',' ).Where( id => !string.IsNullOrWhiteSpace( id ) && id != "," ).ToList(); + if ( excludeSelf ) + result = result.Where( id => id.SafeString().ToLower() != node.Id.SafeString().ToLower() ).ToList(); + return result; + } + + /// + /// 获取缺失的父标识列表 + /// + /// 树节点类型 + /// 树节点列表 + public static List GetMissingParentIds( this IEnumerable nodes ) where TNode : class, ITreeNode { + var result = new List(); + if ( nodes == null ) + return result; + var list = nodes.ToList(); + list.ForEach( entity => { + if ( entity == null ) + return; + result.AddRange( entity.GetParentIdsFromPath().Select( t => t.SafeString() ) ); + } ); + var ids = list.DistinctBy( t => t.Id ).Select( t => t?.Id.SafeString() ); + return result.Except( ids ).ToList(); + } +} \ No newline at end of file diff --git a/src/Util.Application/Trees/ITreeNode.cs b/src/Util.Application/Trees/ITreeNode.cs new file mode 100644 index 000000000..104c1bfbd --- /dev/null +++ b/src/Util.Application/Trees/ITreeNode.cs @@ -0,0 +1,51 @@ +using Util.Applications.Dtos; + +namespace Util.Applications.Trees; + +/// +/// 树节点 +/// +public interface ITreeNode : ITreeNode where TNode : ITreeNode { + /// + /// 子节点列表 + /// + List Children { get; set; } +} + +/// +/// 树节点 +/// +public interface ITreeNode : IDto { + /// + /// 父标识 + /// + string ParentId { get; set; } + /// + /// 路径 + /// + string Path { get; set; } + /// + /// 层级 + /// + int? Level { get; set; } + /// + /// 是否展开 + /// + bool? Expanded { get; set; } + /// + /// 复选框是否被勾选 + /// + bool? Checked { get; set; } + /// + /// 排序号 + /// + int? SortId { get; set; } + /// + /// 是否叶节点 + /// + bool? Leaf { get; set; } + /// + /// 是否隐藏 + /// + bool? Hide { get; set; } +} \ No newline at end of file diff --git a/src/Util.Application/Trees/ITreeQueryService.cs b/src/Util.Application/Trees/ITreeQueryService.cs new file mode 100644 index 000000000..d29c0e1ec --- /dev/null +++ b/src/Util.Application/Trees/ITreeQueryService.cs @@ -0,0 +1,18 @@ +using Util.Data.Queries; + +namespace Util.Applications.Trees; + +/// +/// 查询服务 +/// +/// 数据传输对象类型 +/// 查询参数类型 +public interface ITreeQueryService : IQueryService + where TDto : new() + where TQuery : IPage { + /// + /// 通过父标识列表获取节点集合 + /// + /// 父标识列表,以逗号分隔标识 + Task> GetByParentIds( string parentIds ); +} \ No newline at end of file diff --git a/src/Util.Application/Trees/ITreeService.cs b/src/Util.Application/Trees/ITreeService.cs new file mode 100644 index 000000000..4e04a888c --- /dev/null +++ b/src/Util.Application/Trees/ITreeService.cs @@ -0,0 +1,53 @@ +using Util.Applications.Dtos; +using Util.Data.Trees; + +namespace Util.Applications.Trees; + +/// +/// 树形服务 +/// +/// 数据传输对象类型 +/// 查询参数类型 +public interface ITreeService : ITreeService + where TDto : ITreeNode, new() + where TQuery : ITreeQueryParameter { +} + +/// +/// 树形服务 +/// +/// 数据传输对象类型 +/// 创建参数类型 +/// 修改参数类型 +/// 查询参数类型 +public interface ITreeService : ITreeQueryService + where TDto : ITreeNode, new() + where TCreateRequest : IRequest, new() + where TUpdateRequest : IDto,new() + where TQuery : ITreeQueryParameter { + /// + /// 创建 + /// + /// 创建参数 + Task CreateAsync( TCreateRequest request ); + /// + /// 修改 + /// + /// 修改参数 + Task UpdateAsync( TUpdateRequest request ); + /// + /// 删除 + /// + /// 用逗号分隔的标识列表,范例:"1,2" + Task DeleteAsync( string ids ); + /// + /// 启用 + /// + /// 标识列表 + Task EnableAsync( string ids ); + /// + /// 禁用 + /// + /// 标识列表 + Task DisableAsync( string ids ); +} \ No newline at end of file diff --git a/src/Util.Application/Trees/LoadMode.cs b/src/Util.Application/Trees/LoadMode.cs new file mode 100644 index 000000000..f42e367f7 --- /dev/null +++ b/src/Util.Application/Trees/LoadMode.cs @@ -0,0 +1,19 @@ +namespace Util.Applications.Trees; + +/// +/// 加载模式 +/// +public enum LoadMode { + /// + /// 同步加载,一次性加载全部节点 + /// + Sync = 0, + /// + /// 异步加载,首次加载根节点,点击仅加载直接下级节点 + /// + Async = 1, + /// + /// 根节点异步加载,首次加载根节点,点击加载全部下级节点 + /// + RootAsync = 2 +} \ No newline at end of file diff --git a/src/Util.Application/Trees/LoadOperation.cs b/src/Util.Application/Trees/LoadOperation.cs new file mode 100644 index 000000000..e618a8b8a --- /dev/null +++ b/src/Util.Application/Trees/LoadOperation.cs @@ -0,0 +1,15 @@ +namespace Util.Applications.Trees; + +/// +/// 加载操作 +/// +public enum LoadOperation { + /// + /// 查询 + /// + Query, + /// + /// 加载子节点列表 + /// + LoadChildren +} \ No newline at end of file diff --git a/src/Util.Application/Trees/TreeDtoBase.cs b/src/Util.Application/Trees/TreeDtoBase.cs new file mode 100644 index 000000000..f43f8d7e0 --- /dev/null +++ b/src/Util.Application/Trees/TreeDtoBase.cs @@ -0,0 +1,91 @@ +using Util.Applications.Dtos; + +namespace Util.Applications.Trees; + +/// +/// 树形参数 +/// +public abstract class TreeDtoBase : TreeDtoBase,ITreeNode where TNode : ITreeNode { + /// + /// 初始化树形参数 + /// + protected TreeDtoBase() { + Children = new List(); + } + + /// + /// 子节点列表 + /// + public List Children { get; set; } +} + +/// +/// 树形参数 +/// +public abstract class TreeDtoBase : DtoBase, ITreeNode { + /// + /// 父标识 + /// + public string ParentId { get; set; } + /// + /// 父名称 + /// + [Display( Name = "util.parentName" )] + public string ParentName { get; set; } + /// + /// 路径 + /// + public string Path { get; set; } + /// + /// 层级 + /// + public int? Level { get; set; } + /// + /// 启用 + /// + [Display( Name = "util.enabled" )] + public bool? Enabled { get; set; } = true; + /// + /// 排序号 + /// + [Display( Name = "util.sortId" )] + public int? SortId { get; set; } + /// + /// 是否展开 + /// + [Display( Name = "util.expanded" )] + public bool? Expanded { get; set; } + /// + /// 图标 + /// + [Display( Name = "util.icon" )] + public string Icon { get; set; } + /// + /// 是否禁用复选框 + /// + public bool? DisableCheckbox { get; set; } + /// + /// 是否可选中 + /// + public bool? Selectable { get; set; } = true; + /// + /// 复选框是否被勾选 + /// + public bool? Checked { get; set; } + /// + /// 节点是否被选中 + /// + public bool? Selected { get; set; } + /// + /// 是否叶节点 + /// + public bool? Leaf { get; set; } + /// + /// 是否隐藏 + /// + public bool? Hide { get; set; } + /// + /// 获取树节点显示文本 + /// + public abstract string GetText(); +} \ No newline at end of file diff --git a/src/Util.Application/Trees/TreeQueryActionBase.cs b/src/Util.Application/Trees/TreeQueryActionBase.cs new file mode 100644 index 000000000..71a6722ab --- /dev/null +++ b/src/Util.Application/Trees/TreeQueryActionBase.cs @@ -0,0 +1,313 @@ +using Util.Applications.Properties; +using Util.Data; +using Util.Data.Trees; + +namespace Util.Applications.Trees; + +/// +/// 树形查询操作 +/// +/// 参数类型 +/// 查询参数类型 +public abstract class TreeQueryActionBase + where TDto : class, ITreeNode, new() + where TQuery : class, ITreeQueryParameter { + + #region 字段 + + /// + /// 查询服务 + /// + private readonly ITreeQueryService _service; + /// + /// 加载模式 + /// + private readonly LoadMode _loadMode; + /// + /// 加载操作 + /// + private readonly LoadOperation _loadOperation; + /// + /// 最大分页大小 + /// + private readonly int _maxPageSize; + /// + /// 是否首次加载 + /// + private readonly bool _isFirstLoad; + /// + /// 是否展开所有节点 + /// + private readonly bool _isExpandAll; + /// + /// 根节点异步加载模式是否展开子节点列表 + /// + private readonly bool _isExpandForRootAsync; + /// + /// 需要加载的标识列表 + /// + private readonly string _loadKeys; + /// + /// 查询前操作 + /// + private readonly Action _queryBefore; + /// + /// 数据处理操作 + /// + private readonly Action, TQuery> _queryAfter; + + #endregion + + #region 构造方法 + + /// + /// 初始化树形查询操作 + /// + /// 查询服务 + /// 加载模式 + /// 加载操作 + /// 最大分页大小 + /// 是否首次加载 + /// 是否展开所有节点 + /// 根节点异步加载模式是否展开子节点列表 + /// 需要加载的标识列表 + /// 查询前操作 + /// 查询后操作 + protected TreeQueryActionBase( ITreeQueryService service, LoadMode loadMode, LoadOperation loadOperation, + int maxPageSize, bool isFirstLoad, bool isExpandAll, bool isExpandForRootAsync,string loadKeys, + Action queryBefore, Action, TQuery> queryAfter ) { + _service = service ?? throw new ArgumentNullException( nameof( service ) ); + _loadMode = loadMode; + _loadOperation = loadOperation; + _maxPageSize = maxPageSize; + _isFirstLoad = isFirstLoad; + _isExpandAll = isExpandAll; + _isExpandForRootAsync = isExpandForRootAsync; + _loadKeys = loadKeys; + _queryBefore = queryBefore; + _queryAfter = queryAfter; + } + + #endregion + + #region QueryAsync + + /// + /// 查询 + /// + /// 查询参数 + public async Task QueryAsync( TQuery query ) { + query.CheckNull( nameof( query ) ); + _queryBefore?.Invoke( query ); + InitQueryParam( query ); + return await GetQueryResult( query ); + } + + /// + /// 初始化查询参数 + /// + /// 查询参数 + protected virtual void InitQueryParam( TQuery query ) { + if ( query.Order.IsEmpty() ) + query.Order = "SortId"; + query.Path = null; + if ( _loadOperation == LoadOperation.LoadChildren ) + return; + query.ParentId = null; + } + + /// + /// 获取查询结果 + /// + protected virtual async Task GetQueryResult( TQuery query ) { + if ( _loadOperation == LoadOperation.LoadChildren ) + return await LoadChildren( query ); + return await LoadQuery( query ); + } + + #endregion + + #region SyncQuery + + /// + /// 同步查询 + /// + protected virtual async Task> SyncQuery( TQuery query ) { + query.PageSize = _maxPageSize; + return await _service.PageQueryAsync( query ); + } + + #endregion + + #region AsyncQuery + + /// + /// 异步查询 + /// + protected virtual async Task> AsyncQuery( TQuery query ) { + return await _service.PageQueryAsync( query ); + } + + #endregion + + #region ToResult + + /// + /// 转换为树形结果 + /// + /// 数据列表 + /// 是否异步加载 + /// 所有节点是否全部展开 + protected abstract dynamic ToResult( PageList data, bool async = false, bool allExpand = false ); + + #endregion + + #region LoadChildren + + /// + /// 加载子节点列表 + /// + protected virtual async Task LoadChildren( TQuery query ) { + if ( query.ParentId.IsEmpty() ) + throw new InvalidOperationException( ApplicationResource.ParentIdIsEmpty ); + query.Page = 1; + if ( _loadMode == LoadMode.RootAsync ) + return await SyncLoadChildren( query ); + return await AsyncLoadChildren( query ); + } + + /// + /// 同步加载子节点列表 - 用于根节点异步加载模式 + /// + protected virtual async Task SyncLoadChildren( TQuery query ) { + var parentId = query.ParentId.SafeString(); + var queryParam = await InitSyncLoadChildrenQuery( query ); + var data = await SyncQuery( queryParam ); + data.Total -= 1; + data.Data.RemoveAll( t => t.Id == parentId ); + _queryAfter?.Invoke( data, query ); + return ToResult( data, false, _isExpandForRootAsync ); + } + + /// + /// 初始化同步加载子节点查询参数 + /// + /// 查询参数 + protected virtual async Task InitSyncLoadChildrenQuery( TQuery query ) { + var parent = await _service.GetByIdAsync( query.ParentId ); + query.Path = parent.Path; + query.Level = null; + query.ParentId = null; + return query; + } + + /// + /// 异步加载子节点列表 + /// + protected virtual async Task AsyncLoadChildren( TQuery query ) { + var queryParam = await InitAsyncLoadChildrenQuery( query ); + var data = await SyncQuery( queryParam ); + _queryAfter?.Invoke( data, query ); + return ToResult( data, true, _isExpandAll ); + } + + /// + /// 初始化异步加载子节点查询参数 + /// + /// 查询参数 + protected virtual Task InitAsyncLoadChildrenQuery( TQuery query ) { + query.Level = null; + query.Path = null; + return Task.FromResult( query ); + } + + #endregion + + #region LoadQuery + + /// + /// 加载查询结果 + /// + protected virtual async Task LoadQuery( TQuery query ) { + if ( _loadMode == LoadMode.Sync ) + return await SyncLoadQuery( query ); + return await AsyncLoadQuery( query ); + } + + /// + /// 同步查询 + /// + protected virtual async Task SyncLoadQuery( TQuery query ) { + var data = await SyncQuery( query ); + await AddMissingParents( data ); + _queryAfter?.Invoke( data, query ); + return ToResult( data, false, _isExpandAll ); + } + + /// + /// 添加缺失的父节点 + /// + protected virtual async Task AddMissingParents( PageList data ) { + if ( data.Data.Any( t => t.Level > 1 ) == false ) + return; + var ids = data.Data.GetMissingParentIds(); + var list = await _service.GetByIdsAsync( ids.Join() ); + data.Data.AddRange( list.DistinctBy( t => t.Id ) ); + } + + /// + /// 异步查询 + /// + protected virtual async Task AsyncLoadQuery( TQuery query ) { + if ( _isFirstLoad ) + return await AsyncFirstLoadQuery( query ); + query.PageSize = _maxPageSize; + var data = await AsyncQuery( query ); + await AddMissingParents( data ); + _queryAfter?.Invoke( data, query ); + return ToResult( data, true, _isExpandAll ); + } + + /// + /// 异步首次加载 + /// + protected virtual async Task AsyncFirstLoadQuery( TQuery query ) { + query.Level = 1; + var data = await AsyncQuery( query ); + if ( _loadKeys.IsEmpty() == false ) { + var parentIds = await GetLoadParentIds( _loadKeys ); + var nodes = await GetLoadNodes( parentIds.Join() ); + data.Data.AddRange( nodes ); + ExpandParentNodes( data, parentIds ); + } + _queryAfter?.Invoke( data, query ); + return ToResult( data, true, _isExpandAll ); + } + + /// + /// 获取需要加载的父标识列表 + /// + protected virtual async Task> GetLoadParentIds( string keys ) { + var result = new List(); + var selectedNodes = await _service.GetByIdsAsync( keys ); + selectedNodes.ForEach( t => result.AddRange( t.GetParentIdsFromPath( false ) ) ); + return result; + } + + /// + /// 根据父标识列表加载节点 + /// + protected virtual async Task> GetLoadNodes( string parentIds ) { + return await _service.GetByParentIds( parentIds ); + } + + /// + /// 展开父节点 + /// + protected virtual void ExpandParentNodes( PageList data, List parentIds ) { + var nodes = data.Data.FindAll( node => parentIds.Any( id => node.Id == id ) ); + nodes.ForEach( t => t.Expanded = true ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Application/Trees/TreeResultBase.cs b/src/Util.Application/Trees/TreeResultBase.cs new file mode 100644 index 000000000..dae656527 --- /dev/null +++ b/src/Util.Application/Trees/TreeResultBase.cs @@ -0,0 +1,47 @@ +namespace Util.Applications.Trees; + +/// +/// 树形结果 +/// +/// 源节点类型 +/// 目标节点类型 +/// 树形结果 +public abstract class TreeResultBase : TreeTableResultBase + where TSourceNode : ITreeNode + where TDestinationNode : class + where TResult : class { + /// + /// 初始化树形结果 + /// + /// 树形参数列表 + /// 是否异步加载 + /// 所有节点是否全部展开 + protected TreeResultBase( IEnumerable data, bool async = false, bool allExpand = false ) : base( data, async, allExpand ) { + } + + /// + /// 添加节点 + /// + /// 根节点 + protected override void AddNode( TSourceNode root ) { + if ( root == null ) + return; + InitNode( root ); + AddChildren( root ); + var destinationNode = ToDestinationNode( root ); + AddDestinationNode( destinationNode ); + } + + /// + /// 添加子节点 + /// + protected virtual void AddChildren( TSourceNode node ) { + if ( node == null ) + return; + var children = GetChildren( node ); + children.ForEach( InitNode ); + node.Children = children; + foreach ( var child in node.Children ) + AddChildren( child ); + } +} \ No newline at end of file diff --git a/src/Util.Application/Trees/TreeTableQueryAction.cs b/src/Util.Application/Trees/TreeTableQueryAction.cs new file mode 100644 index 000000000..05419d04e --- /dev/null +++ b/src/Util.Application/Trees/TreeTableQueryAction.cs @@ -0,0 +1,41 @@ +using Util.Data; +using Util.Data.Trees; + +namespace Util.Applications.Trees; + +/// +/// 树形表格查询操作 +/// +/// 参数类型 +/// 查询参数类型 +public class TreeTableQueryAction : TreeQueryActionBase + where TDto : class, ITreeNode, new() + where TQuery : class, ITreeQueryParameter { + /// + /// 初始化树形表格查询操作 + /// + /// 查询服务 + /// 加载模式 + /// 加载操作 + /// 最大分页大小 + /// 是否首次加载 + /// 是否展开所有节点 + /// 根节点异步加载模式是否展开子节点列表 + /// 查询前操作 + /// 数据处理操作 + public TreeTableQueryAction( ITreeQueryService service, LoadMode loadMode, LoadOperation loadOperation, + int maxPageSize, bool isFirstLoad, bool isExpandAll, bool isExpandForRoot, + Action queryBefore, Action, TQuery> processData ) + : base( service, loadMode, loadOperation, maxPageSize, isFirstLoad, isExpandAll,isExpandForRoot,null, queryBefore, processData ) { + } + + /// + /// 转换为树形结果 + /// + /// 数据列表 + /// 是否异步加载 + /// 所有节点是否全部展开 + protected override dynamic ToResult( PageList data, bool async = false, bool allExpand = false ) { + return data.Convert( new TreeTableResult( data.Data, async, allExpand ).GetResult() ); + } +} \ No newline at end of file diff --git a/src/Util.Application/Trees/TreeTableResult.cs b/src/Util.Application/Trees/TreeTableResult.cs new file mode 100644 index 000000000..4c41e1cd2 --- /dev/null +++ b/src/Util.Application/Trees/TreeTableResult.cs @@ -0,0 +1,23 @@ +namespace Util.Applications.Trees; + +/// +/// 树形表格结果 +/// +/// 节点类型 +public class TreeTableResult : TreeTableResultBase> where TNode : class,ITreeNode { + /// + /// 初始化树形表格结果 + /// + /// 树形参数列表 + /// 是否异步加载 + /// 所有节点是否全部展开 + public TreeTableResult( IEnumerable data, bool async = false, bool allExpand = false ) : base(data,async,allExpand){ + } + + /// + /// 转换结果 + /// + protected override List ToResult( List nodes ) { + return nodes; + } +} \ No newline at end of file diff --git a/src/Util.Application/Trees/TreeTableResultBase.cs b/src/Util.Application/Trees/TreeTableResultBase.cs new file mode 100644 index 000000000..0b36e2097 --- /dev/null +++ b/src/Util.Application/Trees/TreeTableResultBase.cs @@ -0,0 +1,167 @@ +namespace Util.Applications.Trees; + +/// +/// 树形表格结果 +/// +/// 源节点类型 +/// 目标节点类型 +/// 树形结果 +public abstract class TreeTableResultBase + where TSourceNode : ITreeNode + where TDestinationNode : class + where TResult : class { + /// + /// 源节点列表 + /// + private readonly List _data; + /// + /// 目标节点列表 + /// + private readonly List _result; + /// + /// 是否异步加载 + /// + private readonly bool _async; + /// + /// 所有节点是否全部展开 + /// + private readonly bool _allExpand; + + /// + /// 初始化树形表格结果 + /// + /// 树形参数列表 + /// 是否异步加载 + /// 所有节点是否全部展开 + protected TreeTableResultBase( IEnumerable data, bool async = false, bool allExpand = false ) { + data.CheckNull( nameof( data ) ); + _data = data.ToList(); + _async = async; + _allExpand = allExpand; + _result = new List(); + } + + /// + /// 获取源数据 + /// + protected List GetData() { + return _data; + } + + /// + /// 获取树形结果 + /// + public TResult GetResult() { + if ( _data == null ) + return ToResult( null ); + foreach ( var root in GetRootNodes() ) + AddNode( root ); + return ToResult( _result ); + } + + /// + /// 获取根节点列表 + /// + protected virtual List GetRootNodes() { + return _data.Where( IsRoot ).OrderBy( t => t.SortId ).ToList(); + } + + /// + /// 是否根节点 + /// + protected virtual bool IsRoot( TSourceNode dto ) { + if ( _data.Any( t => t.ParentId.IsEmpty() ) ) + return dto.ParentId.IsEmpty(); + return dto.Level == _data.Min( t => t.Level ); + } + + /// + /// 添加节点 + /// + protected virtual void AddNode( TSourceNode node ) { + if ( node == null ) + return; + InitNode( node ); + var destinationNode = ToDestinationNode( node ); + AddDestinationNode( destinationNode ); + var children = GetChildren( node ); + foreach ( var child in children ) + AddNode( child ); + } + + /// + /// 初始化节点 + /// + protected void InitNode( TSourceNode node ) { + InitLeaf( node ); + InitExpanded( node ); + } + + /// + /// 初始化叶节点状态 + /// + protected virtual void InitLeaf( TSourceNode node ) { + node.Leaf = false; + if ( _async ) + return; + if ( IsLeaf( node ) ) + node.Leaf = true; + } + + /// + /// 是否叶节点 + /// + protected virtual bool IsLeaf( TSourceNode node ) { + if ( _data.All( t => t.ParentId != node.Id ) ) + return true; + var children = _data.FindAll( t => t.ParentId == node.Id ); + if ( children.All( t => t.Hide == true ) ) + return true; + return false; + } + + /// + /// 初始化节点展开状态 + /// + protected virtual void InitExpanded( TSourceNode node ) { + if ( _allExpand == false ) + return; + if ( _async == false ) { + node.Expanded = true; + return; + } + if ( _data.All( t => t.Level == 1 ) ) + return; + if( node.Leaf == false ) + node.Expanded = true; + } + + /// + /// 添加目标节点 + /// + /// 目标节点 + protected void AddDestinationNode( TDestinationNode node ) { + _result.Add( node ); + } + + /// + /// 获取节点直接下级 + /// + protected List GetChildren( TSourceNode node ) { + return _data.Where( t => t.ParentId == node.Id ).OrderBy( t => t.SortId ).ToList(); + } + + /// + /// 将源节点转换为目标节点 + /// + /// 源节点 + protected virtual TDestinationNode ToDestinationNode( TSourceNode node ) { + return node.MapTo(); + } + + /// + /// 转换为结果 + /// + /// 目标节点列表 + protected abstract TResult ToResult( List nodes ); +} \ No newline at end of file diff --git a/src/Util.Application/Usings.cs b/src/Util.Application/Usings.cs new file mode 100644 index 000000000..ecacfd67c --- /dev/null +++ b/src/Util.Application/Usings.cs @@ -0,0 +1,14 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.ComponentModel.DataAnnotations; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Microsoft.AspNetCore.Mvc; +global using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +global using Microsoft.Extensions.Localization; +global using Util.Events; +global using Util.Localization; +global using Util.Logging; +global using Util.Sessions; \ No newline at end of file diff --git a/src/Util.Applications/06-Util.Applications.csproj b/src/Util.Applications/06-Util.Applications.csproj deleted file mode 100644 index cfb0fb3fe..000000000 --- a/src/Util.Applications/06-Util.Applications.csproj +++ /dev/null @@ -1,47 +0,0 @@ - - - - netstandard2.0 - Util.Applications - Util.Applications - 何镇汐 - 何镇汐 - 何镇汐 - Util.Applications是Util应用框架的应用层基类库。 -Util是一个.net core平台下的应用框架,旨在提升小型团队的开发输出能力,由常用公共操作类(工具类)、分层架构基类、Ui组件,第三方组件封装,第三方业务接口封装,配套代码生成模板,权限等组成。 - https://github.com/dotnetcore/Util/blob/master/LICENSE.txt - https://github.com/dotnetcore/Util - https://avatars2.githubusercontent.com/u/19421630 - https://github.com/dotnetcore/Util - git - util;applicationframework;dotnetcore;aspnetcore; - Util应用框架以MIT开源发布,可随意使用 - 0.2.2-preview - 0.2.2.0 - 0.2.2.0 - false - - - - ..\..\output\release - ..\..\output\release\netstandard2.0\Util.Applications.xml - - - - ..\..\output\release - ..\..\output\release\netstandard2.0\Util.Applications.xml - - - - - - - - - - - - - - - diff --git a/src/Util.Applications/Aspects/ICommitAfter.cs b/src/Util.Applications/Aspects/ICommitAfter.cs deleted file mode 100644 index 597f5ff0e..000000000 --- a/src/Util.Applications/Aspects/ICommitAfter.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Util.Applications.Aspects { - /// - /// 提交工作单元后操作 - /// - public interface ICommitAfter { - /// - /// 提交后操作 - /// - void CommitAfter(); - } -} diff --git a/src/Util.Applications/Aspects/UnitOfWorkAttribute.cs b/src/Util.Applications/Aspects/UnitOfWorkAttribute.cs deleted file mode 100644 index 3f5e0d363..000000000 --- a/src/Util.Applications/Aspects/UnitOfWorkAttribute.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Threading.Tasks; -using AspectCore.DynamicProxy; -using AspectCore.Extensions.AspectScope; -using Microsoft.Extensions.DependencyInjection; -using Util.Aspects.Base; -using Util.Datas.UnitOfWorks; - -namespace Util.Applications.Aspects { - /// - /// 工作单元拦截器 - /// - public class UnitOfWorkAttribute : InterceptorBase,IScopeInterceptor { - /// - /// 作用域,当嵌套使用工作单元拦截器时,设置为Scope.Aspect,只有最外层工作单元拦截器生效 - /// - public Scope Scope { get; set; } = Scope.Aspect; - - /// - /// 执行 - /// - public override async Task Invoke( AspectContext context, AspectDelegate next ) { - await next( context ); - var manager = context.ServiceProvider.GetService(); - if ( manager == null ) - return; - await manager.CommitAsync(); - if( context.Implementation is ICommitAfter service ) - service.CommitAfter(); - } - } -} diff --git a/src/Util.Applications/CrudServiceBase.BatchSave.cs b/src/Util.Applications/CrudServiceBase.BatchSave.cs deleted file mode 100644 index ed195df15..000000000 --- a/src/Util.Applications/CrudServiceBase.BatchSave.cs +++ /dev/null @@ -1,217 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Util.Logs.Extensions; - -namespace Util.Applications { - /// - /// 增删改查服务 - 批量Save - /// - public abstract partial class CrudServiceBase { - /// - /// 批量保存 - /// - /// 新增列表 - /// 修改列表 - /// 删除列表 - public List Save( List addList, List updateList, List deleteList ) { - if( addList == null && updateList == null && deleteList == null ) - return new List(); - addList = addList ?? new List(); - updateList = updateList ?? new List(); - deleteList = deleteList ?? new List(); - FilterList( addList, updateList, deleteList ); - var addEntities = ToEntities( addList ); - var updateEntities = ToEntities( updateList ); - var deleteEntities = ToEntities( deleteList ); - SaveBefore( addEntities, updateEntities, deleteEntities ); - AddList( addEntities ); - UpdateList( updateEntities ); - DeleteList( deleteEntities ); - Commit(); - SaveAfter( addEntities, updateEntities, deleteEntities ); - return GetResult( addEntities, updateEntities ); - } - - /// - /// 转换为实体集合 - /// - private List ToEntities( List dtos ) { - return dtos.Select( ToEntity ).Distinct().ToList(); - } - - /// - /// 过滤列表 - /// - private void FilterList( List addList, List updateList, List deleteList ) { - FilterByDeleteList( addList, deleteList ); - FilterByDeleteList( updateList, deleteList ); - } - - /// - /// 过滤需要删除的项 - /// - private void FilterByDeleteList( List list, List deleteList ) { - for( int i = 0; i < list.Count; i++ ) { - var item = list[i]; - if( deleteList.Any( d => d.Id == item.Id ) ) - list.Remove( item ); - } - } - - /// - /// 保存前操作 - /// - /// 新增列表 - /// 修改列表 - /// 删除列表 - protected virtual void SaveBefore( List addList, List updateList, List deleteList ) { - } - - /// - /// 添加列表 - /// - private void AddList( List list ) { - if( list.Count == 0 ) - return; - Log.Content( "创建实体:" ); - list.ForEach( Create ); - } - - /// - /// 更新列表 - /// - private void UpdateList( List list ) { - if( list.Count == 0 ) - return; - Log.Content( "修改实体:" ); - list.ForEach( Update ); - } - - /// - /// 删除列表 - /// - private void DeleteList( List list ) { - if( list.Count == 0 ) - return; - Log.Content( "删除实体:" ); - list.ForEach( DeleteChilds ); - } - - /// - /// 删除子节点集合 - /// - protected virtual void DeleteChilds( TEntity parent ) { - DeleteEntity( parent ); - } - - /// - /// 删除实体 - /// - protected void DeleteEntity( TEntity entity ) { - _repository.Remove( entity.Id ); - AddLog( entity ); - } - - /// - /// 提交 - /// - private void Commit() { - _unitOfWork.Commit(); - } - - /// - /// 保存后操作 - /// - protected virtual void SaveAfter( List addList, List updateList, List deleteList ) { - WriteLog( $"保存{EntityDescription}成功" ); - } - - /// - /// 获取结果 - /// - protected virtual List GetResult( List addList, List updateList ) { - return addList.Concat( updateList ).Select( ToDto ).ToList(); - } - - /// - /// 批量保存 - /// - /// 新增列表 - /// 修改列表 - /// 删除列表 - public async Task> SaveAsync( List addList, List updateList,List deleteList ) { - if( addList == null && updateList == null && deleteList == null ) - return new List(); - addList = addList ?? new List(); - updateList = updateList ?? new List(); - deleteList = deleteList ?? new List(); - FilterList( addList, updateList, deleteList ); - var addEntities = ToEntities( addList ); - var updateEntities = ToEntities( updateList ); - var deleteEntities = ToEntities( deleteList ); - SaveBefore( addEntities, updateEntities, deleteEntities ); - await AddListAsync( addEntities ); - await UpdateListAsync( updateEntities ); - await DeleteListAsync( deleteEntities ); - await CommitAsync(); - SaveAfter( addEntities, updateEntities, deleteEntities ); - return GetResult( addEntities, updateEntities ); - } - - /// - /// 添加列表 - /// - private async Task AddListAsync( List list ) { - if( list.Count == 0 ) - return; - Log.Content( "创建实体:" ); - foreach ( var entity in list ) - await CreateAsync( entity ); - } - - /// - /// 更新列表 - /// - private async Task UpdateListAsync( List list ) { - if( list.Count == 0 ) - return; - Log.Content( "修改实体:" ); - foreach( var entity in list ) - await UpdateAsync( entity ); - } - - /// - /// 删除列表 - /// - private async Task DeleteListAsync( List list ) { - if( list.Count == 0 ) - return; - Log.Content( "删除实体:" ); - foreach( var entity in list ) - await DeleteChildsAsync( entity ); - } - - /// - /// 删除子节点集合 - /// - protected virtual async Task DeleteChildsAsync( TEntity parent ) { - await DeleteEntityAsync( parent ); - } - - /// - /// 删除实体 - /// - protected async Task DeleteEntityAsync( TEntity entity ) { - await _repository.RemoveAsync( entity.Id ); - AddLog( entity ); - } - - /// - /// 提交 - /// - private async Task CommitAsync() { - await _unitOfWork.CommitAsync(); - } - } -} diff --git a/src/Util.Applications/CrudServiceBase.Save.cs b/src/Util.Applications/CrudServiceBase.Save.cs deleted file mode 100644 index 2433f5d12..000000000 --- a/src/Util.Applications/CrudServiceBase.Save.cs +++ /dev/null @@ -1,197 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace Util.Applications { - /// - /// 增删改查服务 - Save - /// - public abstract partial class CrudServiceBase { - /// - /// 创建 - /// - /// 请求参数 - public void Create( TRequest request ) { - if( request == null ) - throw new ArgumentNullException( nameof( request ) ); - var entity = ToEntity( request ); - if( entity == null ) - throw new ArgumentNullException( nameof( entity ) ); - Create( entity ); - request.Id = entity.Id.ToString(); - } - - /// - /// 创建实体 - /// - protected void Create( TEntity entity ) { - CreateBefore( entity ); - entity.Init(); - _repository.Add( entity ); - CreateAfter( entity ); - } - - /// - /// 创建前操作 - /// - protected virtual void CreateBefore( TEntity entity ) { - } - - /// - /// 创建后操作 - /// - protected virtual void CreateAfter( TEntity entity ) { - AddLog( entity ); - } - - /// - /// 修改 - /// - /// 请求参数 - public void Update( TRequest request ) { - if( request == null ) - throw new ArgumentNullException( nameof( request ) ); - var entity = ToEntity( request ); - if( entity == null ) - throw new ArgumentNullException( nameof( entity ) ); - Update( entity ); - } - - /// - /// 修改实体 - /// - protected void Update( TEntity entity ) { - UpdateBefore( entity ); - _repository.Update( entity ); - UpdateAfter( entity ); - } - - /// - /// 修改前操作 - /// - /// 实体 - protected virtual void UpdateBefore( TEntity entity ) { - } - - /// - /// 修改后操作 - /// - /// 实体 - protected virtual void UpdateAfter( TEntity entity ) { - AddLog( entity ); - } - - /// - /// 保存 - /// - /// 请求参数 - public void Save( TRequest request ) { - if( request == null ) - throw new ArgumentNullException( nameof( request ) ); - SaveBefore( request ); - var entity = ToEntity( request ); - if( entity == null ) - throw new ArgumentNullException( nameof( entity ) ); - if ( IsNew( request, entity ) ) { - Create( entity ); - request.Id = entity.Id.ToString(); - } - else - Update( entity ); - } - - /// - /// 保存前操作 - /// - /// 请求参数 - protected virtual void SaveBefore( TRequest request ) { - } - - /// - /// 是否创建 - /// - /// 请求参数 - /// 实体 - protected virtual bool IsNew( TRequest request, TEntity entity ) { - return string.IsNullOrWhiteSpace( request.Id ) || entity.Id.Equals( default( TKey ) ); - } - - /// - /// 保存后操作 - /// - protected virtual void SaveAfter() { - WriteLog( $"保存{EntityDescription}成功" ); - } - - /// - /// 提交后操作 - 该方法由工作单元拦截器调用 - /// - public void CommitAfter() { - SaveAfter(); - } - - /// - /// 创建 - /// - /// 请求参数 - public async Task CreateAsync( TRequest request ) { - if( request == null ) - throw new ArgumentNullException( nameof( request ) ); - var entity = ToEntity( request ); - if( entity == null ) - throw new ArgumentNullException( nameof( entity ) ); - await CreateAsync( entity ); - request.Id = entity.Id.ToString(); - } - - /// - /// 创建实体 - /// - protected async Task CreateAsync( TEntity entity ) { - CreateBefore( entity ); - entity.Init(); - await _repository.AddAsync( entity ); - CreateAfter( entity ); - } - - /// - /// 修改 - /// - /// 请求参数 - public async Task UpdateAsync( TRequest request ) { - if( request == null ) - throw new ArgumentNullException( nameof( request ) ); - var entity = ToEntity( request ); - if( entity == null ) - throw new ArgumentNullException( nameof( entity ) ); - await UpdateAsync( entity ); - } - - /// - /// 修改实体 - /// - protected async Task UpdateAsync( TEntity entity ) { - UpdateBefore( entity ); - await _repository.UpdateAsync( entity ); - UpdateAfter( entity ); - } - - /// - /// 保存 - /// - /// 请求参数 - public async Task SaveAsync( TRequest request ) { - if( request == null ) - throw new ArgumentNullException( nameof( request ) ); - SaveBefore( request ); - var entity = ToEntity( request ); - if( entity == null ) - throw new ArgumentNullException( nameof( entity ) ); - if ( IsNew( request, entity ) ) { - await CreateAsync( entity ); - request.Id = entity.Id.ToString(); - } - else - await UpdateAsync( entity ); - } - } -} diff --git a/src/Util.Applications/CrudServiceBase.cs b/src/Util.Applications/CrudServiceBase.cs deleted file mode 100644 index 0af19e7e6..000000000 --- a/src/Util.Applications/CrudServiceBase.cs +++ /dev/null @@ -1,196 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Util.Applications.Aspects; -using Util.Applications.Dtos; -using Util.Datas.Queries; -using Util.Datas.UnitOfWorks; -using Util.Domains; -using Util.Domains.Repositories; -using Util.Helpers; -using Util.Logs.Extensions; - -namespace Util.Applications { - /// - /// 增删改查服务 - /// - /// 实体类型 - /// 数据传输对象类型 - /// 查询参数类型 - public abstract class CrudServiceBase : CrudServiceBase, ICrudService - where TEntity : class, IAggregateRoot - where TDto : IDto, new() - where TQueryParameter : IQueryParameter { - /// - /// 初始化增删改查服务 - /// - /// 工作单元 - /// 仓储 - protected CrudServiceBase( IUnitOfWork unitOfWork, IRepository repository ) : base( unitOfWork, repository ) { - } - } - - /// - /// 增删改查服务 - /// - /// 实体类型 - /// 数据传输对象类型 - /// 查询参数类型 - /// 实体标识类型 - public abstract class CrudServiceBase : CrudServiceBase, ICrudService - where TEntity : class, IAggregateRoot - where TDto : IDto, new() - where TQueryParameter : IQueryParameter { - /// - /// 初始化增删改查服务 - /// - /// 工作单元 - /// 仓储 - protected CrudServiceBase( IUnitOfWork unitOfWork, IRepository repository ) : base( unitOfWork, repository ) { - } - } - - /// - /// 增删改查服务 - /// - /// 实体类型 - /// 数据传输对象类型 - /// 请求参数类型 - /// 查询参数类型 - /// 实体标识类型 - public abstract partial class CrudServiceBase - : QueryServiceBase, ICrudService, ICommitAfter - where TEntity : class, IAggregateRoot - where TDto : IDto, new() - where TRequest : IRequest, IKey, new() - where TQueryParameter : IQueryParameter { - /// - /// 工作单元 - /// - private readonly IUnitOfWork _unitOfWork; - /// - /// 仓储 - /// - private readonly IRepository _repository; - - /// - /// 初始化增删改查服务 - /// - /// 工作单元 - /// 仓储 - protected CrudServiceBase( IUnitOfWork unitOfWork, IRepository repository ) : base( repository ) { - _unitOfWork = unitOfWork; - _repository = repository; - EntityDescription = Reflection.GetDisplayNameOrDescription(); - } - - /// - /// 实体描述 - /// - protected string EntityDescription { get; } - - /// - /// 转换为实体 - /// - /// 请求参数 - protected abstract TEntity ToEntity( TRequest request ); - - /// - /// 写日志 - /// - /// 标题 - protected void WriteLog( string caption ) { - Log.Class( typeof( TEntity ).FullName ) - .Caption( caption ) - .Info(); - } - - /// - /// 写日志 - /// - /// 标题 - /// 实体 - protected void WriteLog( string caption, TEntity entity ) { - AddLog( entity ); - WriteLog( caption ); - } - - /// - /// 添加日志 - /// - /// 实体 - protected void AddLog( TEntity entity ) { - Log.BusinessId( entity.Id.SafeString() ); - Log.Content( entity.ToString() ); - } - - /// - /// 写日志 - /// - /// 标题 - /// 实体集合 - protected void WriteLog( string caption, IList entities ) { - AddLog( entities ); - WriteLog( caption ); - } - - /// - /// 添加日志 - /// - /// 实体集合 - protected void AddLog( IList entities ) { - Log.BusinessId( entities.Select( t => t.Id ).Join() ); - foreach( var entity in entities ) - Log.Content( entity.ToString() ); - } - - /// - /// 删除 - /// - /// 用逗号分隔的Id列表,范例:"1,2" - public void Delete( string ids ) { - if ( string.IsNullOrWhiteSpace( ids ) ) - return; - var entities = _repository.FindByIds( ids ); - if( entities?.Count == 0 ) - return; - DeleteBefore( entities ); - _repository.Remove( entities ); - _unitOfWork.Commit(); - DeleteAfter( entities ); - } - - /// - /// 删除前操作 - /// - /// 实体集合 - protected virtual void DeleteBefore( List entities ) { - } - - /// - /// 删除后操作 - /// - /// 实体集合 - protected virtual void DeleteAfter( List entities ) { - AddLog( entities ); - WriteLog( $"删除{EntityDescription}成功" ); - } - - /// - /// 删除 - /// - /// 用逗号分隔的Id列表,范例:"1,2" - public async Task DeleteAsync( string ids ) { - if( string.IsNullOrWhiteSpace( ids ) ) - return; - var entities = await _repository.FindByIdsAsync( ids ); - if( entities?.Count == 0 ) - return; - DeleteBefore( entities ); - await _repository.RemoveAsync( entities ); - await _unitOfWork.CommitAsync(); - DeleteAfter( entities ); - } - } -} diff --git a/src/Util.Applications/ICrudService.cs b/src/Util.Applications/ICrudService.cs deleted file mode 100644 index a2dba1ba4..000000000 --- a/src/Util.Applications/ICrudService.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Util.Applications.Aspects; -using Util.Applications.Dtos; -using Util.Datas.Queries; -using Util.Validations.Aspects; - -namespace Util.Applications { - /// - /// 增删改查服务 - /// - /// 数据传输对象类型 - /// 查询参数类型 - public interface ICrudService : ICrudService - where TDto : IDto,new() - where TQueryParameter : IQueryParameter { - } - - /// - /// 增删改查服务 - /// - /// 数据传输对象类型 - /// 请求参数类型 - /// 查询参数类型 - public interface ICrudService : IQueryService - where TDto : IDto, new() - where TRequest : IRequest, new() - where TQueryParameter : IQueryParameter { - /// - /// 创建 - /// - /// 请求参数 - [UnitOfWork] - void Create( [Valid] TRequest request ); - /// - /// 创建 - /// - /// 请求参数 - [UnitOfWork] - Task CreateAsync( [Valid] TRequest request ); - /// - /// 修改 - /// - /// 请求参数 - [UnitOfWork] - void Update( [Valid] TRequest request ); - /// - /// 修改 - /// - /// 请求参数 - [UnitOfWork] - Task UpdateAsync( [Valid] TRequest request ); - /// - /// 保存 - /// - /// 请求参数 - [UnitOfWork] - void Save( [Valid] TRequest request ); - /// - /// 保存 - /// - /// 请求参数 - [UnitOfWork] - Task SaveAsync( [Valid] TRequest request ); - /// - /// 批量保存 - /// - /// 新增列表 - /// 修改列表 - /// 删除列表 - List Save( List addList, List updateList, List deleteList ); - /// - /// 批量保存 - /// - /// 新增列表 - /// 修改列表 - /// 删除列表 - Task> SaveAsync( List addList, List updateList, List deleteList ); - /// - /// 删除 - /// - /// 用逗号分隔的Id列表,范例:"1,2" - void Delete( string ids ); - /// - /// 删除 - /// - /// 用逗号分隔的Id列表,范例:"1,2" - Task DeleteAsync( string ids ); - } -} diff --git a/src/Util.Applications/QueryServiceBase.cs b/src/Util.Applications/QueryServiceBase.cs deleted file mode 100644 index 2c53b0321..000000000 --- a/src/Util.Applications/QueryServiceBase.cs +++ /dev/null @@ -1,227 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Util.Datas.Queries; -using Util.Domains; -using Util.Domains.Repositories; -using System.Linq.Dynamic.Core; -using Microsoft.EntityFrameworkCore; -using Util.Applications.Dtos; -using Util.Datas.Ef; -using Util.Domains.Sessions; -using Util.Logs; -using Util.Maps; - -namespace Util.Applications { - /// - /// 查询服务 - /// - /// 实体类型 - /// 数据传输对象类型 - /// 查询参数类型 - public abstract class QueryServiceBase : QueryServiceBase - where TEntity : class, IAggregateRoot - where TDto : IDto, new() - where TQueryParameter : IQueryParameter { - /// - /// 初始化查询服务 - /// - /// 仓储 - protected QueryServiceBase( IRepository repository ) - : base( repository ) { - } - } - - /// - /// 查询服务 - /// - /// 实体类型 - /// 数据传输对象类型 - /// 查询参数类型 - /// 实体标识类型 - public abstract class QueryServiceBase : IQueryService - where TEntity : class, IAggregateRoot - where TDto : IDto, new() - where TQueryParameter : IQueryParameter { - /// - /// 仓储 - /// - private readonly IRepository _repository; - - /// - /// 初始化查询服务 - /// - /// 仓储 - protected QueryServiceBase( IRepository repository ) { - _repository = repository ?? throw new ArgumentNullException( nameof( repository ) ); - Log = Util.Logs.Log.Null; - Session = Util.Domains.Sessions.Session.Null; - } - - /// - /// 日志组件 - /// - public ILog Log { get; set; } - - /// - /// 用户会话 - /// - public ISession Session { get; set; } - - /// - /// 转换为数据传输对象 - /// - /// 实体 - protected virtual TDto ToDto( TEntity entity ) { - return entity.MapTo(); - } - - /// - /// 获取全部 - /// - public List GetAll() { - return _repository.FindAll().Select( ToDto ).ToList(); - } - - /// - /// 获取全部 - /// - public async Task> GetAllAsync() { - var entities = await _repository.FindAllAsync(); - return entities.Select( ToDto ).ToList(); - } - - /// - /// 通过编号获取 - /// - /// 实体编号 - public TDto GetById( object id ) { - var key = Util.Helpers.Convert.To( id ); - return ToDto( _repository.Find( key ) ); - } - - /// - /// 通过编号获取 - /// - /// 实体编号 - public async Task GetByIdAsync( object id ) { - var key = Util.Helpers.Convert.To( id ); - return ToDto( await _repository.FindAsync( key ) ); - } - - /// - /// 通过编号列表获取 - /// - /// 用逗号分隔的Id列表,范例:"1,2" - public List GetByIds( string ids ) { - return _repository.FindByIds( ids ).Select( ToDto ).ToList(); - } - - /// - /// 通过编号列表获取 - /// - /// 用逗号分隔的Id列表,范例:"1,2" - public async Task> GetByIdsAsync( string ids ) { - var entities = await _repository.FindByIdsAsync( ids ); - return entities.Select( ToDto ).ToList(); - } - - /// - /// 查询 - /// - /// 查询参数 - public List Query( TQueryParameter parameter ) { - if( parameter == null ) - return new List(); - var queryable = ExecuteQuery( parameter ); - return queryable.ToList().Select( ToDto ).ToList(); - } - - /// - /// 执行查询 - /// - private IQueryable ExecuteQuery( TQueryParameter parameter ) { - var query = CreateQuery( parameter ); - var queryable = Filter( query ); - queryable = Filter( queryable ); - var order = query.GetOrder(); - return string.IsNullOrWhiteSpace( order ) ? queryable : queryable.OrderBy( order ); - } - - /// - /// 创建查询对象 - /// - /// 查询参数 - protected abstract IQueryBase CreateQuery( TQueryParameter parameter ); - - /// - /// 过滤 - /// - private IQueryable Filter( IQueryBase query ) { - return IsTracking ? _repository.Find().Where( query ) : _repository.FindAsNoTracking().Where( query ); - } - - /// - /// 查询时是否跟踪对象 - /// - protected virtual bool IsTracking => false; - - /// - /// 过滤 - /// - protected virtual IQueryable Filter( IQueryable queryable ) { - return queryable; - } - - /// - /// 查询 - /// - /// 查询参数 - public async Task> QueryAsync( TQueryParameter parameter ) { - if( parameter == null ) - return new List(); - var queryable = ExecuteQuery( parameter ); - var entities = await queryable.ToListAsync(); - return entities.Select( ToDto ).ToList(); - } - - /// - /// 分页查询 - /// - /// 查询参数 - public PagerList PagerQuery( TQueryParameter parameter ) { - if( parameter == null ) - return new PagerList(); - var query = CreateQuery( parameter ); - var pager = query.GetPager(); - return ExecutePagerQuery( query, pager ).ToPagerList( pager ).Convert( ToDto ); - } - - /// - /// 执行分页查询 - /// - private IQueryable ExecutePagerQuery( IQueryBase query, IPager pager ) { - var queryable = Filter( query ); - queryable = Filter( queryable ); - var order = query.GetOrder(); - if( string.IsNullOrWhiteSpace( order ) ) - order = "Id"; - return queryable.OrderBy( order ).Pager( pager ); - } - - /// - /// 分页查询 - /// - /// 查询参数 - public async Task> PagerQueryAsync( TQueryParameter parameter ) { - if( parameter == null ) - return new PagerList(); - var query = CreateQuery( parameter ); - var pager = query.GetPager(); - var queryable = ExecutePagerQuery( query, pager ); - var result = await queryable.ToPagerListAsync( pager ); - return result.Convert( ToDto ); - } - } -} diff --git a/src/Util.AspNetCore/08-Util.AspNetCore.csproj b/src/Util.AspNetCore/08-Util.AspNetCore.csproj new file mode 100644 index 000000000..3303c3e62 --- /dev/null +++ b/src/Util.AspNetCore/08-Util.AspNetCore.csproj @@ -0,0 +1,44 @@ + + + + $(NetTargetFramework) + Util + icon.jpg + Util.AspNetCore是Util应用框架的AspNetCore操作类库 + Util.AspNetCore + + + + + .\obj\Debug\$(NetTargetFramework)\Util.AspNetCore.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.AspNetCore.xml + + + + + True + False + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Util.AspNetCore/AppBuilderExtensions.cs b/src/Util.AspNetCore/AppBuilderExtensions.cs new file mode 100644 index 000000000..b3e49d361 --- /dev/null +++ b/src/Util.AspNetCore/AppBuilderExtensions.cs @@ -0,0 +1,76 @@ +namespace Util; + +/// +/// AspNetCore操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置授权访问控制 + /// + /// 应用生成器 + public static IAppBuilder AddAcl( this IAppBuilder builder ) { + return builder.AddAcl(); + } + + /// + /// 配置授权访问控制 + /// + /// 应用生成器 + /// 访问控制配置操作 + public static IAppBuilder AddAcl( this IAppBuilder builder, Action setupAction ) { + return builder.AddAcl( setupAction ); + } + + /// + /// 配置授权访问控制 + /// + /// 权限管理器类型 + /// 应用生成器 + public static IAppBuilder AddAcl( this IAppBuilder builder ) where TPermissionManager : class, IPermissionManager { + return builder.AddAcl(); + } + + /// + /// 配置授权访问控制 + /// + /// 权限管理器类型 + /// 应用生成器 + /// 访问控制配置操作 + public static IAppBuilder AddAcl( this IAppBuilder builder, Action setupAction ) where TPermissionManager : class, IPermissionManager { + return builder.AddAcl( setupAction ); + } + + /// + /// 配置授权访问控制 + /// + /// 权限管理器类型 + /// 授权中间件结果处理器类型 + /// 应用生成器 + public static IAppBuilder AddAcl( this IAppBuilder builder ) + where TPermissionManager : class, IPermissionManager + where TAuthorizationMiddlewareResultHandler : class, IAuthorizationMiddlewareResultHandler { + return builder.AddAcl( null ); + } + + /// + /// 配置授权访问控制 + /// + /// 权限管理器类型 + /// 授权中间件结果处理器类型 + /// 应用生成器 + /// 访问控制配置操作 + public static IAppBuilder AddAcl( this IAppBuilder builder, Action setupAction ) + where TPermissionManager : class, IPermissionManager + where TAuthorizationMiddlewareResultHandler : class, IAuthorizationMiddlewareResultHandler { + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + if ( setupAction != null ) + services.Configure( setupAction ); + } ); + return builder; + } +} \ No newline at end of file diff --git a/src/Util.AspNetCore/AspNetCore/IJsonSerializerOptionsFactory.cs b/src/Util.AspNetCore/AspNetCore/IJsonSerializerOptionsFactory.cs new file mode 100644 index 000000000..933761332 --- /dev/null +++ b/src/Util.AspNetCore/AspNetCore/IJsonSerializerOptionsFactory.cs @@ -0,0 +1,11 @@ +namespace Util.AspNetCore; + +/// +/// Json序列化配置工厂 +/// +public interface IJsonSerializerOptionsFactory : ISingletonDependency { + /// + /// 创建Json序列化配置 + /// + JsonSerializerOptions CreateOptions(); +} \ No newline at end of file diff --git a/src/Util.AspNetCore/Helpers/Ip.cs b/src/Util.AspNetCore/Helpers/Ip.cs new file mode 100644 index 000000000..46a759154 --- /dev/null +++ b/src/Util.AspNetCore/Helpers/Ip.cs @@ -0,0 +1,74 @@ +namespace Util.Helpers; + +/// +/// Ip地址操作 +/// +public static class Ip { + /// + /// Ip地址 + /// + private static readonly AsyncLocal _ip = new(); + + /// + /// 设置Ip地址 + /// + /// Ip地址 + public static void SetIp( string ip ) { + _ip.Value = ip; + } + + /// + /// 重置Ip地址 + /// + public static void Reset() { + _ip.Value = null; + } + + /// + /// 获取客户端Ip地址 + /// + public static string GetIp() { + if ( string.IsNullOrWhiteSpace( _ip.Value ) == false ) + return _ip.Value; + var list = new[] { "127.0.0.1", "::1" }; + var result = Web.HttpContext?.Connection.RemoteIpAddress.SafeString(); + if ( string.IsNullOrWhiteSpace( result ) || list.Contains( result ) ) + result = Common.IsWindows ? GetLanIp() : GetLanIp( NetworkInterfaceType.Ethernet ); + return result; + } + + /// + /// 获取局域网IP + /// + private static string GetLanIp() { + foreach ( var hostAddress in Dns.GetHostAddresses( Dns.GetHostName() ) ) { + if ( hostAddress.AddressFamily == AddressFamily.InterNetwork ) + return hostAddress.ToString(); + } + return string.Empty; + } + + /// + /// 获取局域网IP + /// + /// 网络接口类型 + private static string GetLanIp( NetworkInterfaceType type ) { + try { + foreach ( var item in NetworkInterface.GetAllNetworkInterfaces() ) { + if ( item.NetworkInterfaceType != type || item.OperationalStatus != OperationalStatus.Up ) + continue; + var ipProperties = item.GetIPProperties(); + if ( ipProperties.GatewayAddresses.FirstOrDefault() == null ) + continue; + foreach ( var ip in ipProperties.UnicastAddresses ) { + if ( ip.Address.AddressFamily == AddressFamily.InterNetwork ) + return ip.Address.ToString(); + } + } + } + catch { + return string.Empty; + } + return string.Empty; + } +} \ No newline at end of file diff --git a/src/Util.AspNetCore/Helpers/Web.cs b/src/Util.AspNetCore/Helpers/Web.cs new file mode 100644 index 000000000..f28ef9e83 --- /dev/null +++ b/src/Util.AspNetCore/Helpers/Web.cs @@ -0,0 +1,433 @@ +using System.Collections.Specialized; +using System.Security.Claims; +using Util.Http; +using Util.Security.Authentication; + +namespace Util.Helpers; + +/// +/// Web操作 +/// +public static class Web { + + #region HttpContextAccessor(Http上下文访问器) + + /// + /// Http上下文访问器 + /// + public static IHttpContextAccessor HttpContextAccessor { get; set; } + + #endregion + + #region HttpContext(Http上下文) + + /// + /// 当前Http上下文 + /// + public static HttpContext HttpContext => HttpContextAccessor?.HttpContext; + + #endregion + + #region ServiceProvider(服务提供器) + + /// + /// 当前Http请求服务提供器 + /// + public static IServiceProvider ServiceProvider => HttpContext?.RequestServices; + + #endregion + + #region Request(Http请求) + + /// + /// 当前Http请求 + /// + public static HttpRequest Request => HttpContext?.Request; + + #endregion + + #region Response(Http响应) + + /// + /// 当前Http响应 + /// + public static HttpResponse Response => HttpContext?.Response; + + #endregion + + #region Body(请求正文) + + /// + /// 请求正文 + /// + public static byte[] Body { + get { + Request.EnableBuffering(); + return File.ReadToBytes( Request.Body ); + } + } + + #endregion + + #region GetBodyAsync(获取请求正文) + + /// + /// 获取请求正文 + /// + public static async Task GetBodyAsync() { + Request.EnableBuffering(); + return await File.ReadToBytesAsync( Request.Body ); + } + + #endregion + + #region Environment(主机环境) + + /// + /// 主机环境 + /// + public static IWebHostEnvironment Environment => ServiceProvider?.GetService(); + + #endregion + + #region User(当前用户安全主体) + + /// + /// 当前用户安全主体 + /// + public static ClaimsPrincipal User { + get { + if ( HttpContext?.User is { } principal ) + return principal; + return UnauthenticatedPrincipal.Instance; + } + } + + #endregion + + #region Identity(当前用户身份标识) + + /// + /// 当前用户身份标识 + /// + public static ClaimsIdentity Identity { + get { + if ( User.Identity is ClaimsIdentity identity ) + return identity; + return UnauthenticatedIdentity.Instance; + } + } + + #endregion + + #region Client(Http客户端) + + /// + /// Http客户端 + /// + public static IHttpClient Client => Ioc.Create(); + + #endregion + + #region GetPhysicalPath(获取物理路径) + + /// + /// 获取物理路径,基路径为IWebHostEnvironment.ContentRootPath + /// + /// 相对路径,范例:"test/a.txt" 或 "/test/a.txt" + public static string GetPhysicalPath( string relativePath ) { + return Common.GetPhysicalPath( relativePath, Environment.ContentRootPath ); + } + + #endregion + + #region GetFiles(获取客户端文件集合) + + /// + /// 获取客户端文件集合 + /// + public static List GetFiles() { + var result = new List(); + var files = Request.Form.Files; + if ( files.Count == 0 ) + return result; + result.AddRange( files.Where( file => file?.Length > 0 ) ); + return result; + } + + #endregion + + #region GetFile(获取客户端文件) + + /// + /// 获取客户端文件 + /// + public static IFormFile GetFile() { + var files = GetFiles(); + return files.Count == 0 ? null : files[0]; + } + + #endregion + + #region GetParam(获取请求参数) + + /// + /// 获取请求参数,搜索路径:查询参数->表单参数->请求头 + /// + /// 参数名 + public static string GetParam( string name ) { + if ( name.IsEmpty() ) + return string.Empty; + if ( Request == null ) + return string.Empty; + string result = Request.Query[name]; + if ( result.IsEmpty() == false ) + return result; + result = Request.Form[name]; + if ( result.IsEmpty() == false ) + return result; + return Request.Headers[name]; + } + + #endregion + + #region Host(获取主机) + + /// + /// 获取主机,范例: https://test.com:666 + /// + public static string Host => $"{Request?.Scheme}://{Request?.Host.Value}"; + + #endregion + + #region Url(请求地址) + + /// + /// 请求地址 + /// + public static string Url => Request?.GetDisplayUrl(); + + #endregion + + #region UrlEncode(Url编码) + + /// + /// Url编码 + /// + /// url + /// 编码字符是否转成大写,范例,"http://"转成"http%3A%2F%2F" + public static string UrlEncode( string url, bool isUpper = false ) { + return UrlEncode( url, Encoding.UTF8, isUpper ); + } + + /// + /// Url编码 + /// + /// url + /// 字符编码 + /// 编码字符是否转成大写,范例,"http://"转成"http%3A%2F%2F" + public static string UrlEncode( string url, string encoding, bool isUpper = false ) { + encoding = string.IsNullOrWhiteSpace( encoding ) ? "UTF-8" : encoding; + return UrlEncode( url, Encoding.GetEncoding( encoding ), isUpper ); + } + + /// + /// Url编码 + /// + /// url + /// 字符编码 + /// 编码字符是否转成大写,范例,"http://"转成"http%3A%2F%2F" + public static string UrlEncode( string url, Encoding encoding, bool isUpper = false ) { + var result = HttpUtility.UrlEncode( url, encoding ); + if ( isUpper == false ) + return result; + return GetUpperEncode( result ); + } + + /// + /// 获取大写编码字符串 + /// + private static string GetUpperEncode( string encode ) { + var result = new StringBuilder(); + int index = int.MinValue; + for ( int i = 0; i < encode.Length; i++ ) { + string character = encode[i].ToString(); + if ( character == "%" ) + index = i; + if ( i - index == 1 || i - index == 2 ) + character = character.ToUpper(); + result.Append( character ); + } + return result.ToString(); + } + + #endregion + + #region UrlDecode(Url解码) + + /// + /// Url解码 + /// + /// url + public static string UrlDecode( string url ) { + return HttpUtility.UrlDecode( url ); + } + + /// + /// Url解码 + /// + /// url + /// 字符编码 + public static string UrlDecode( string url, Encoding encoding ) { + return HttpUtility.UrlDecode( url, encoding ); + } + + #endregion + + #region ParseQueryString(将查询字符串转换为名值对集合) + + /// + /// 将查询字符串转换为名值对集合 + /// + /// 查询字符串 + public static NameValueCollection ParseQueryString( string query ) { + return HttpUtility.ParseQueryString( query ); + } + + /// + /// 将查询字符串转换为名值对集合 + /// + /// 查询字符串 + /// 字符编码 + public static NameValueCollection ParseQueryString( string query, Encoding encoding ) { + return HttpUtility.ParseQueryString( query, encoding ); + } + + #endregion + + #region DownloadAsync(下载) + + /// + /// 下载文件 + /// + /// 文件绝对路径 + /// 文件名,包含扩展名 + public static async Task DownloadFileAsync( string filePath, string fileName ) { + await DownloadFileAsync( filePath, fileName, Encoding.UTF8 ); + } + + /// + /// 下载文件 + /// + /// 文件绝对路径 + /// 文件名,包含扩展名 + /// 字符编码 + public static async Task DownloadFileAsync( string filePath, string fileName, Encoding encoding ) { + var bytes = File.ReadToBytes( filePath ); + await DownloadAsync( bytes, fileName, encoding ); + } + + /// + /// 下载 + /// + /// 流 + /// 文件名,包含扩展名 + public static async Task DownloadAsync( Stream stream, string fileName ) { + await DownloadAsync( stream, fileName, Encoding.UTF8 ); + } + + /// + /// 下载 + /// + /// 流 + /// 文件名,包含扩展名 + /// 字符编码 + public static async Task DownloadAsync( Stream stream, string fileName, Encoding encoding ) { + var bytes = await File.ToBytesAsync( stream ); + await DownloadAsync( bytes, fileName, encoding ); + } + + /// + /// 下载 + /// + /// 字节流 + /// 文件名,包含扩展名 + public static async Task DownloadAsync( byte[] bytes, string fileName ) { + await DownloadAsync( bytes, fileName, Encoding.UTF8 ); + } + + /// + /// 下载 + /// + /// 字节流 + /// 文件名,包含扩展名 + /// 字符编码 + public static async Task DownloadAsync( byte[] bytes, string fileName, Encoding encoding ) { + if ( bytes == null || bytes.Length == 0 ) + return; + fileName = fileName.Replace( " ", "" ); + fileName = UrlEncode( fileName, encoding ); + Response.ContentType = "application/octet-stream"; + Response.Headers.Append( "Content-Disposition", $"attachment; filename={fileName}" ); + Response.Headers.Append( "Content-Length", bytes.Length.ToString() ); + await Response.Body.WriteAsync( bytes, 0, bytes.Length ); + } + + #endregion + + #region GetCookie(获取Cookie) + + /// + /// 获取Cookie + /// + /// cookie键名 + public static string GetCookie( string key ) { + return Request?.Cookies[key]; + } + + #endregion + + #region SetCookie(设置Cookie) + + /// + /// 设置Cookie + /// + /// cookie键名 + /// 值 + public static void SetCookie( string key, string value ) { + Response?.Cookies.Append( key, value ); + } + + /// + /// 设置Cookie + /// + /// cookie键名 + /// 值 + /// Cookie配置 + public static void SetCookie( string key,string value,CookieOptions options ) { + Response?.Cookies.Append( key, value, options ); + } + + #endregion + + #region RemoveCookie(移除Cookie) + + /// + /// 移除Cookie + /// + /// cookie键名 + public static void RemoveCookie( string key ) { + Response?.Cookies.Delete( key ); + } + + /// + /// 移除Cookie + /// + /// cookie键名 + /// Cookie配置 + public static void RemoveCookie( string key, CookieOptions options ) { + Response?.Cookies.Delete( key, options ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.AspNetCore/IdentityExtensions.cs b/src/Util.AspNetCore/IdentityExtensions.cs new file mode 100644 index 000000000..f86842a3d --- /dev/null +++ b/src/Util.AspNetCore/IdentityExtensions.cs @@ -0,0 +1,33 @@ +using System.Security.Claims; +using Util.Security.Authentication; + +namespace Util; + +/// +/// 身份标识扩展 +/// +public static class IdentityExtensions { + /// + /// 获取用户标识声明值 + /// + /// 用户标识 + /// 声明类型 + public static string GetValue( this ClaimsIdentity identity, string type ) { + var claim = identity.FindFirst( type ); + if( claim == null ) + return string.Empty; + return claim.Value; + } + + /// + /// 获取身份标识 + /// + /// Http上下文 + public static ClaimsIdentity GetIdentity( this HttpContext context ) { + if( context?.User is not { } principal ) + return UnauthenticatedIdentity.Instance; + if( principal.Identity is ClaimsIdentity identity ) + return identity; + return UnauthenticatedIdentity.Instance; + } +} \ No newline at end of file diff --git a/src/Util.AspNetCore/Infrastructure/AspNetCoreServiceRegistrar.cs b/src/Util.AspNetCore/Infrastructure/AspNetCoreServiceRegistrar.cs new file mode 100644 index 000000000..d30dc9146 --- /dev/null +++ b/src/Util.AspNetCore/Infrastructure/AspNetCoreServiceRegistrar.cs @@ -0,0 +1,73 @@ +using Util.Helpers; +using Util.Http; +using Util.SystemTextJson; + +namespace Util.Infrastructure; + +/// +/// AspNetCore服务注册器 +/// +public class AspNetCoreServiceRegistrar : IServiceRegistrar { + /// + /// 获取服务名 + /// + public static string ServiceName => "Util.Infrastructure.AspNetCoreServiceRegistrar"; + + /// + /// 排序号 + /// + public int OrderId => 200; + + /// + /// 是否启用 + /// + public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName ); + + /// + /// 注册服务 + /// + /// 服务上下文 + public Action Register( ServiceContext serviceContext ) { + serviceContext.HostBuilder.ConfigureServices( ( context, services ) => { + RegisterHttpContextAccessor( services ); + services.AddHttpClient(); + RegisterServiceLocator(); + RegisterHttpClient( services ); + ConfigJsonOptions( services ); + } ); + return null; + } + + /// + /// 注册Http上下文访问器 + /// + private void RegisterHttpContextAccessor( IServiceCollection services ) { + var httpContextAccessor = new HttpContextAccessor(); + services.TryAddSingleton( httpContextAccessor ); + Web.HttpContextAccessor = httpContextAccessor; + } + + /// + /// 注册服务定位器 + /// + private void RegisterServiceLocator() { + Ioc.SetServiceProviderAction( () => Web.ServiceProvider ); + } + + /// + /// 注册Http客户端 + /// + private void RegisterHttpClient( IServiceCollection services ) { + services.TryAddSingleton(); + } + + /// + /// 配置Json + /// + private void ConfigJsonOptions( IServiceCollection services ) { + services.Configure( ( Microsoft.AspNetCore.Mvc.JsonOptions options ) => { + options.JsonSerializerOptions.Converters.Add( new DateTimeJsonConverter() ); + options.JsonSerializerOptions.Converters.Add( new NullableDateTimeJsonConverter() ); + } ); + } +} \ No newline at end of file diff --git a/src/Util.AspNetCore/Security/AclAttribute.cs b/src/Util.AspNetCore/Security/AclAttribute.cs new file mode 100644 index 000000000..1afe2c4c2 --- /dev/null +++ b/src/Util.AspNetCore/Security/AclAttribute.cs @@ -0,0 +1,58 @@ +namespace Util.Security; + +/// +/// 访问控制 +/// +public class AclAttribute : AuthorizeAttribute { + /// + /// 是否忽略 + /// + private bool _ignore; + /// + /// 资源标识 + /// + private string _resourceUri; + + /// + /// 初始化访问控制 + /// + public AclAttribute() { + Policy = AclPolicyHelper.GetPolicy( null, false ); + } + + /// + /// 初始化访问控制 + /// + /// 资源标识 + public AclAttribute( string resourceUri ) { + ResourceUri = resourceUri; + } + + /// + /// 是否忽略授权检查,设置为true则不验证权限,但仍然需要登录,需要匿名访问请使用AllowAnonymous特性 + /// + public bool Ignore { + get { + var requirement = AclPolicyHelper.GetRequirement(Policy); + return requirement.Ignore; + } + set { + _ignore = value; + Policy = AclPolicyHelper.GetPolicy( _resourceUri , _ignore ); + } + } + + /// + /// 资源标识 + /// + public string ResourceUri { + get { + var requirement = AclPolicyHelper.GetRequirement( Policy ); + return requirement.Uri; + } + set { + _resourceUri = value; + Policy = AclPolicyHelper.GetPolicy( _resourceUri, _ignore ); + } + } +} \ No newline at end of file diff --git a/src/Util.AspNetCore/Security/AclOptions.cs b/src/Util.AspNetCore/Security/AclOptions.cs new file mode 100644 index 000000000..7da30615a --- /dev/null +++ b/src/Util.AspNetCore/Security/AclOptions.cs @@ -0,0 +1,19 @@ +namespace Util.Security; + +/// +/// 访问控制配置 +/// +public class AclOptions { + /// + /// 是否允许匿名访问,无需授权,无需登录 + /// + public bool AllowAnonymous { get; set; } + /// + /// 是否忽略授权检查,无需授权,但仍需要登录 + /// + public bool IgnoreAcl { get; set; } + /// + /// 全局资源标识,未指定资源标识的Api使用该值 + /// + public string ResourceUri { get; set; } +} \ No newline at end of file diff --git a/src/Util.AspNetCore/Security/Authorization/AclFilter.cs b/src/Util.AspNetCore/Security/Authorization/AclFilter.cs new file mode 100644 index 000000000..8192eea50 --- /dev/null +++ b/src/Util.AspNetCore/Security/Authorization/AclFilter.cs @@ -0,0 +1,96 @@ +using Util.Applications; + +namespace Util.Security.Authorization; + +/// +/// 访问控制过滤器 +/// +public class AclFilter : AuthorizeFilter { + /// + /// 初始化访问控制过滤器 + /// + public AclFilter() + : base( new AclPolicyProvider(),new[] { new AclAttribute() } ) { + } + + /// + /// 授权 + /// + /// 授权过滤器上下文 + public override async Task OnAuthorizationAsync( AuthorizationFilterContext context ) { + context.CheckNull( nameof( context ) ); + if ( context.IsEffectivePolicy( this ) == false ) { + return; + } + var effectivePolicy = await GetEffectivePolicyAsync( context ); + if ( effectivePolicy == null ) + return; + var policyEvaluator = context.HttpContext.RequestServices.GetRequiredService(); + var authenticateResult = await policyEvaluator.AuthenticateAsync( effectivePolicy, context.HttpContext ); + if ( HasAllowAnonymous( context ) ) { + return; + } + var authorizationResult = await policyEvaluator.AuthorizeAsync( effectivePolicy, authenticateResult, context.HttpContext, context ); + SetContextResult( context, authorizationResult ); + } + + /// + /// 获取有效授权策略 + /// + protected async Task GetEffectivePolicyAsync( AuthorizationFilterContext context ) { + var builder = new AuthorizationPolicyBuilder( await ComputePolicyAsync() ); + for ( var i = 0; i < context.Filters.Count; i++ ) { + if ( ReferenceEquals( this, context.Filters[i] ) ) + continue; + if ( context.Filters[i] is AclFilter authorizeFilter ) { + builder.Combine( await authorizeFilter.ComputePolicyAsync() ); + } + } + var endpoint = context.HttpContext.GetEndpoint(); + if ( endpoint != null ) { + var policyProvider = PolicyProvider ?? context.HttpContext.RequestServices.GetRequiredService(); + var endpointAuthorizeData = endpoint.Metadata.GetOrderedMetadata() ?? Array.Empty(); + var endpointPolicy = await AuthorizationPolicy.CombineAsync( policyProvider, endpointAuthorizeData ); + if ( endpointPolicy != null && endpointPolicy.Requirements.Any( t => t is AclRequirement ) ) { + builder.Requirements.Remove( builder.Requirements.FirstOrDefault( t => t is AclRequirement ) ); + builder.Combine( endpointPolicy ); + } + } + return builder.Build(); + } + + /// + /// 合并授权策略 + /// + protected async ValueTask ComputePolicyAsync() { + return await AuthorizationPolicy.CombineAsync( PolicyProvider, AuthorizeData ); + } + + /// + /// 是否允许匿名访问 + /// + protected bool HasAllowAnonymous( AuthorizationFilterContext context ) { + var filters = context.Filters; + for ( var i = 0; i < filters.Count; i++ ) { + if ( filters[i] is IAllowAnonymousFilter ) { + return true; + } + } + var endpoint = context.HttpContext.GetEndpoint(); + if ( endpoint?.Metadata?.GetMetadata() != null ) { + return true; + } + return false; + } + + /// + /// 设置授权过滤器上下文结果 + /// + /// 授权过滤器上下文 + /// 授权结果 + protected virtual void SetContextResult( AuthorizationFilterContext context,PolicyAuthorizationResult authorizationResult ) { + if ( authorizationResult.Succeeded ) + return; + context.Result = new JsonResult( new { Code = StateCode.Unauthorized } ) { StatusCode = 200 }; + } +} \ No newline at end of file diff --git a/src/Util.AspNetCore/Security/Authorization/AclHandler.cs b/src/Util.AspNetCore/Security/Authorization/AclHandler.cs new file mode 100644 index 000000000..e006e97cc --- /dev/null +++ b/src/Util.AspNetCore/Security/Authorization/AclHandler.cs @@ -0,0 +1,87 @@ +namespace Util.Security.Authorization; + +/// +/// 授权处理器 +/// +public class AclHandler : AuthorizationHandler { + /// + /// 处理授权要求 + /// + /// 授权处理器上下文 + /// 授权要求 + protected override async Task HandleRequirementAsync( AuthorizationHandlerContext context, AclRequirement requirement ) { + if ( context == null ) + return; + if ( requirement == null ) + return; + var httpContext = GetHttpContext( context.Resource ); + if ( httpContext == null ) + return; + var options = GetAclOptions( httpContext ); + if ( options.AllowAnonymous ) { + context.Succeed( requirement ); + return; + } + if ( httpContext.GetIdentity().IsAuthenticated == false ) + return; + if ( options.IgnoreAcl ) { + context.Succeed( requirement ); + return; + } + if ( requirement.Ignore ) { + context.Succeed( requirement ); + return; + } + var permissionManager = httpContext.RequestServices.GetService(); + if ( permissionManager == null ) { + context.Fail( new AuthorizationFailureReason( this, "未实现IPermissionManager" ) ); + return; + } + var uri = GetResourceUri( requirement, httpContext, options ); + var result = await permissionManager.HasPermissionAsync( uri ); + if ( result ) { + context.Succeed( requirement ); + return; + } + context.Fail( new AuthorizationFailureReason( this, $"没有访问资源 {uri} 的权限" ) ); + } + + /// + /// 获取Http上下文 + /// + /// 资源 + protected virtual HttpContext GetHttpContext( dynamic resource ) { + if ( resource is DefaultHttpContext httpContext ) + return httpContext; + return resource.HttpContext; + } + + /// + /// 获取访问控制配置 + /// + protected virtual AclOptions GetAclOptions( HttpContext httpContext ) { + var result = httpContext.RequestServices.GetService>(); + return result == null ? new AclOptions() : result.Value; + } + + /// + /// 获取资源标识 + /// + protected virtual string GetResourceUri( AclRequirement requirement,HttpContext httpContext, AclOptions options ) { + if ( requirement.Uri.IsEmpty() == false ) + return requirement.Uri; + if ( options.ResourceUri.IsEmpty() == false ) + return options.ResourceUri; + return GetResourceUri( httpContext.Request.Path, httpContext.Request.Method ); + } + + /// + /// 获取资源标识 + /// + protected virtual string GetResourceUri( string path, string httpMethod ) { + if ( path.IsEmpty() ) + return null; + var result = $"{path}#{httpMethod}"; + return result.ToLower( CultureInfo.InvariantCulture ); + } +} \ No newline at end of file diff --git a/src/Util.AspNetCore/Security/Authorization/AclMiddlewareResultHandler.cs b/src/Util.AspNetCore/Security/Authorization/AclMiddlewareResultHandler.cs new file mode 100644 index 000000000..2d0c46061 --- /dev/null +++ b/src/Util.AspNetCore/Security/Authorization/AclMiddlewareResultHandler.cs @@ -0,0 +1,24 @@ +namespace Util.Security.Authorization; + +/// +/// 授权中间件结果处理器 +/// +public class AclMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler { + /// + /// 默认处理器 + /// + private readonly AuthorizationMiddlewareResultHandler defaultHandler = new(); + + /// + /// 处理授权结果 + /// + public async Task HandleAsync( RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult ) { + if ( authorizeResult.Succeeded == false ) { + var factory = context.RequestServices.GetRequiredService(); + context.Response.StatusCode = factory.HttpStatusCode; + await context.Response.WriteAsJsonAsync( factory.CreateResult( context ) ); + return; + } + await defaultHandler.HandleAsync( next, context, policy, authorizeResult ); + } +} \ No newline at end of file diff --git a/src/Util.AspNetCore/Security/Authorization/AclPolicyHelper.cs b/src/Util.AspNetCore/Security/Authorization/AclPolicyHelper.cs new file mode 100644 index 000000000..e76eac9cc --- /dev/null +++ b/src/Util.AspNetCore/Security/Authorization/AclPolicyHelper.cs @@ -0,0 +1,32 @@ +namespace Util.Security.Authorization; + +/// +/// 授权辅助操作 +/// +public static class AclPolicyHelper { + /// + /// 策略前缀 + /// + public const string Prefix = "acl_"; + + /// + /// 获取授权要求 + /// + public static AclRequirement GetRequirement( string policy ) { + var json = policy.RemoveStart( Prefix ); + return Util.Helpers.Json.ToObject( json ); + } + + /// + /// 获取授权策略 + /// + /// 资源标识 + /// 是否忽略 + public static string GetPolicy( string uri,bool ignore ) { + var requirement = new AclRequirement { + Uri = uri, + Ignore = ignore + }; + return $"{Prefix}{Util.Helpers.Json.ToJson( requirement )}"; + } +} \ No newline at end of file diff --git a/src/Util.AspNetCore/Security/Authorization/AclPolicyProvider.cs b/src/Util.AspNetCore/Security/Authorization/AclPolicyProvider.cs new file mode 100644 index 000000000..8c5669158 --- /dev/null +++ b/src/Util.AspNetCore/Security/Authorization/AclPolicyProvider.cs @@ -0,0 +1,31 @@ +namespace Util.Security.Authorization; + +/// +/// 授权策略提供器 +/// +public class AclPolicyProvider : IAuthorizationPolicyProvider { + /// + /// 获取授权策略 + /// + /// 授权策略名称 + public Task GetPolicyAsync( string policyName ) { + var builder = new AuthorizationPolicyBuilder(); + var requirement = AclPolicyHelper.GetRequirement( policyName ); + builder.AddRequirements( requirement ); + return Task.FromResult( builder.Build() ); + } + + /// + /// 获取默认授权策略 + /// + public Task GetDefaultPolicyAsync() { + return Task.FromResult( new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build() ); + } + + /// + /// 获取回退授权策略 + /// + public Task GetFallbackPolicyAsync() { + return Task.FromResult( null ); + } +} \ No newline at end of file diff --git a/src/Util.AspNetCore/Security/Authorization/AclRequirement.cs b/src/Util.AspNetCore/Security/Authorization/AclRequirement.cs new file mode 100644 index 000000000..54ad76e45 --- /dev/null +++ b/src/Util.AspNetCore/Security/Authorization/AclRequirement.cs @@ -0,0 +1,15 @@ +namespace Util.Security.Authorization; + +/// +/// 授权要求 +/// +public class AclRequirement : IAuthorizationRequirement { + /// + /// 是否忽略 + /// + public bool Ignore { get; set; } + /// + /// 资源标识 + /// + public string Uri { get; set; } +} \ No newline at end of file diff --git a/src/Util.AspNetCore/Security/Authorization/IUnauthorizedResultFactory.cs b/src/Util.AspNetCore/Security/Authorization/IUnauthorizedResultFactory.cs new file mode 100644 index 000000000..bf86399f4 --- /dev/null +++ b/src/Util.AspNetCore/Security/Authorization/IUnauthorizedResultFactory.cs @@ -0,0 +1,16 @@ +namespace Util.Security.Authorization; + +/// +/// 未授权返回结果工厂 +/// +public interface IUnauthorizedResultFactory : ISingletonDependency { + /// + /// 设置Http状态码 + /// + int HttpStatusCode { get; } + /// + /// 创建未授权返回结果 + /// + /// Http上下文 + object CreateResult( HttpContext context ); +} \ No newline at end of file diff --git a/src/Util.AspNetCore/Security/Authorization/UnauthorizedResultFactory.cs b/src/Util.AspNetCore/Security/Authorization/UnauthorizedResultFactory.cs new file mode 100644 index 000000000..d31388853 --- /dev/null +++ b/src/Util.AspNetCore/Security/Authorization/UnauthorizedResultFactory.cs @@ -0,0 +1,16 @@ +using Util.Applications; + +namespace Util.Security.Authorization; + +/// +/// 未授权返回结果工厂 +/// +public class UnauthorizedResultFactory : IUnauthorizedResultFactory { + /// + public virtual int HttpStatusCode => 200; + + /// + public virtual object CreateResult( HttpContext context ) { + return new { Code = StateCode.Unauthorized }; + } +} \ No newline at end of file diff --git a/src/Util.AspNetCore/Security/Encryptors/DataProtectionEncryptor.cs b/src/Util.AspNetCore/Security/Encryptors/DataProtectionEncryptor.cs new file mode 100644 index 000000000..9c89b731d --- /dev/null +++ b/src/Util.AspNetCore/Security/Encryptors/DataProtectionEncryptor.cs @@ -0,0 +1,33 @@ +namespace Util.Security.Encryptors; + +/// +/// 基于数据保护提供程序的加密器 +/// +public class DataProtectionEncryptor : IEncryptor { + /// + /// 数据保护器 + /// + private readonly IDataProtector _dataProtector; + + /// + /// 初始化加密器 + /// + /// 数据保护器 + public DataProtectionEncryptor( IDataProtector dataProtector ) { + _dataProtector = dataProtector ?? throw new ArgumentNullException( nameof( dataProtector ) ); + } + + /// + public string Encrypt( string data ) { + if ( data.IsEmpty() ) + return null; + return _dataProtector.Protect( data ); + } + + /// + public string Decrypt( string data ) { + if ( data.IsEmpty() ) + return null; + return _dataProtector.Unprotect( data ); + } +} \ No newline at end of file diff --git a/src/Util.AspNetCore/Sessions/SessionExtensions.cs b/src/Util.AspNetCore/Sessions/SessionExtensions.cs new file mode 100644 index 000000000..12b529c48 --- /dev/null +++ b/src/Util.AspNetCore/Sessions/SessionExtensions.cs @@ -0,0 +1,22 @@ +namespace Util.Sessions; + +/// +/// 用户会话扩展 +/// +public static class SessionExtensions { + /// + /// 获取当前操作人标识 + /// + /// 用户会话 + public static Guid GetUserId( this ISession session ) { + return session.UserId.ToGuid(); + } + + /// + /// 获取当前操作人标识 + /// + /// 用户会话 + public static T GetUserId( this ISession session ) { + return Util.Helpers.Convert.To( session.UserId ); + } +} \ No newline at end of file diff --git a/src/Util.AspNetCore/Sessions/UserSession.cs b/src/Util.AspNetCore/Sessions/UserSession.cs new file mode 100644 index 000000000..c5bacbbd4 --- /dev/null +++ b/src/Util.AspNetCore/Sessions/UserSession.cs @@ -0,0 +1,35 @@ +using Util.Helpers; + +namespace Util.Sessions; + +/// +/// 用户会话 +/// +public class UserSession : ISession { + /// + /// 空用户会话 + /// + public static readonly ISession Null = NullSession.Instance; + + /// + /// 用户会话实例 + /// + public static readonly ISession Instance = new UserSession(); + + /// + public virtual IServiceProvider ServiceProvider => Web.ServiceProvider; + + /// + public virtual bool IsAuthenticated => Web.Identity.IsAuthenticated; + + /// + public virtual string UserId { + get { + var result = Web.Identity.GetValue( ClaimTypes.UserId ); + return result.IsEmpty() ? Web.Identity.GetValue( System.Security.Claims.ClaimTypes.NameIdentifier ) : result; + } + } + + /// + public virtual string TenantId => Web.Identity.GetValue( ClaimTypes.TenantId ); +} \ No newline at end of file diff --git a/src/Util.AspNetCore/Usings.cs b/src/Util.AspNetCore/Usings.cs new file mode 100644 index 000000000..800b8a0aa --- /dev/null +++ b/src/Util.AspNetCore/Usings.cs @@ -0,0 +1,35 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using System.Text; +global using System.IO; +global using System.Globalization; +global using System.Text.Json; +global using System.Text.Json.Serialization; +global using System.Net.NetworkInformation; +global using System.Net.Sockets; +global using System.Net; +global using System.Web; +global using System.Net.Http; +global using System.ComponentModel; +global using System.Security.Cryptography.X509Certificates; +global using Microsoft.Extensions.Options; +global using Microsoft.AspNetCore.Hosting; +global using Microsoft.AspNetCore.Http; +global using Microsoft.AspNetCore.Http.Extensions; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Microsoft.AspNetCore.WebUtilities; +global using Microsoft.AspNetCore.Authorization; +global using Microsoft.AspNetCore.Authorization.Policy; +global using Microsoft.AspNetCore.Mvc; +global using Microsoft.AspNetCore.Mvc.Authorization; +global using Microsoft.AspNetCore.Mvc.Filters; +global using Microsoft.AspNetCore.DataProtection; +global using Microsoft.AspNetCore.Builder; +global using Util.Dependency; +global using Util.Security; +global using Util.Security.Authorization; +global using Util.Configs; \ No newline at end of file diff --git a/src/Util.AspNetCore/WebApplicationBuilderExtensions.cs b/src/Util.AspNetCore/WebApplicationBuilderExtensions.cs new file mode 100644 index 000000000..872dfda24 --- /dev/null +++ b/src/Util.AspNetCore/WebApplicationBuilderExtensions.cs @@ -0,0 +1,15 @@ +namespace Util; + +/// +/// Web应用生成器扩展 +/// +public static class WebApplicationBuilderExtensions { + /// + /// 转换为Util应用生成器 + /// + /// Web应用生成器 + public static IAppBuilder AsBuild( this WebApplicationBuilder builder ) { + builder.CheckNull( nameof( builder ) ); + return new AppBuilder( builder.Host ); + } +} \ No newline at end of file diff --git a/src/Util.Biz.Payments/02-Util.Biz.Payments.csproj b/src/Util.Biz.Payments/02-Util.Biz.Payments.csproj deleted file mode 100644 index ef7b07fd9..000000000 --- a/src/Util.Biz.Payments/02-Util.Biz.Payments.csproj +++ /dev/null @@ -1,67 +0,0 @@ - - - - netstandard2.0 - Util.Biz.Payments - Util.Biz.Payments - 0.2.2-preview - 0.2.2.0 - 0.2.2.0 - 何镇汐 - 何镇汐 - 何镇汐 - Util.Biz.Payments是Util应用框架的支付操作类库。 -Util是一个.net core平台下的应用框架,旨在提升小型团队的开发输出能力,由常用公共操作类(工具类)、分层架构基类、Ui组件,第三方组件封装,第三方业务接口封装,配套代码生成模板,权限等组成。 - - https://github.com/dotnetcore/Util/blob/master/LICENSE.txt - https://github.com/dotnetcore/Util - https://avatars2.githubusercontent.com/u/19421630 - https://github.com/dotnetcore/Util - git - util;applicationframework;dotnetcore;aspnetcore; - Util应用框架以MIT开源发布,可随意使用 - false - - - - ..\..\output\release\ - ..\..\output\release\netstandard2.0\Util.Biz.Payments.xml - - - - ..\..\output\release\ - ..\..\output\release\netstandard2.0\Util.Biz.Payments.xml - - - - - - - - - - - - - - - C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.aspnetcore.http\2.0.0\lib\netstandard2.0\Microsoft.AspNetCore.Http.dll - - - - - - True - True - PayResource.resx - - - - - - PublicResXFileCodeGenerator - PayResource.Designer.cs - - - - diff --git a/src/Util.Biz.Payments/02-Util.Biz.Payments.csproj.DotSettings.user b/src/Util.Biz.Payments/02-Util.Biz.Payments.csproj.DotSettings.user deleted file mode 100644 index 5df25627a..000000000 --- a/src/Util.Biz.Payments/02-Util.Biz.Payments.csproj.DotSettings.user +++ /dev/null @@ -1,2 +0,0 @@ - - 69B79A60-3719-43E5-BE12-78E401C7BC0C/f:PayResource.resx \ No newline at end of file diff --git a/src/Util.Biz.Payments/Alipay/Configs/AlipayConfig.cs b/src/Util.Biz.Payments/Alipay/Configs/AlipayConfig.cs deleted file mode 100644 index 6d6ff292e..000000000 --- a/src/Util.Biz.Payments/Alipay/Configs/AlipayConfig.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using Util.Validations; - -namespace Util.Biz.Payments.Alipay.Configs { - /// - /// 支付宝配置 - /// - public class AlipayConfig { - /// - /// 支付网关地址,默认为正式地址: https://openapi.alipay.com/gateway.do ,如果进行测试,则设置为 https://openapi.alipaydev.com/gateway.do - /// - [Required(ErrorMessage = "支付网关地址[GatewayUrl]不能为空" )] - public string GatewayUrl { get; set; } = "https://openapi.alipay.com/gateway.do"; - - /// - /// 应用Id - /// - [Required(ErrorMessage = "应用Id[AppId]不能为空" )] - public string AppId { get; set; } - - /// - /// 商户应用私钥 - /// - [Required( ErrorMessage = "商户应用私钥[PrivateKey]不能为空" )] - public string AppPrivateKey { get; set; } - - /// - /// 支付宝公钥 - /// - [Required( ErrorMessage = "支付宝公钥[PublicKey]不能为空" )] - public string AlipayPublicKey { get; set; } - - /// - /// 回调通知地址 - /// - [Required( ErrorMessage = "回调通知地址[NotifyUrl]不能为空" )] - public string NotifyUrl { get; set; } - - /// - /// 返回地址 - /// - public string ReturnUrl { get; set; } - - /// - /// 验证 - /// - public void Validate() { - var result = DataAnnotationValidation.Validate( this ); - if( result.IsValid == false ) - throw new ArgumentException( result.First().ErrorMessage ); - } - } -} diff --git a/src/Util.Biz.Payments/Alipay/Configs/AlipayConst.cs b/src/Util.Biz.Payments/Alipay/Configs/AlipayConst.cs deleted file mode 100644 index 422060967..000000000 --- a/src/Util.Biz.Payments/Alipay/Configs/AlipayConst.cs +++ /dev/null @@ -1,79 +0,0 @@ -namespace Util.Biz.Payments.Alipay.Configs { - /// - /// 支付宝常量 - /// - public static class AlipayConst { - /// - /// 编码 - /// - public const string Charset = "charset"; - /// - /// 应用Id - /// - public const string AppId = "app_id"; - /// - /// 请求方法 - /// - public const string Method = "method"; - /// - /// 时间戳 - /// - public const string Timestamp = "timestamp"; - /// - /// 版本 - /// - public const string Version = "version"; - /// - /// 格式 - /// - public const string Format = "format"; - /// - /// 签名 - /// - public const string Sign = "sign"; - /// - /// 签名类型 - /// - public const string SignType = "sign_type"; - /// - /// 回调通知Url - /// - public const string NotifyUrl = "notify_url"; - /// - /// 返回Url - /// - public const string ReturnUrl = "return_url"; - /// - /// 内容 - /// - public const string BizContent = "biz_content"; - /// - /// 商户订单号 - /// - public const string OutTradeNo = "out_trade_no"; - /// - /// 订单金额 - /// - public const string TotalAmount = "total_amount"; - /// - /// 订单标题 - /// - public const string Subject = "subject"; - /// - /// 用户付款授权码 - /// - public const string AuthCode = "auth_code"; - /// - /// 交易超时时间 - /// - public const string TimeoutExpress = "timeout_express"; - /// - /// 场景 - /// - public const string Scene = "scene"; - /// - /// 商户门店编号 - /// - public const string StoreId = "store_id"; - } -} diff --git a/src/Util.Biz.Payments/Alipay/Configs/IAlipayConfigProvider.cs b/src/Util.Biz.Payments/Alipay/Configs/IAlipayConfigProvider.cs deleted file mode 100644 index 97ed1c281..000000000 --- a/src/Util.Biz.Payments/Alipay/Configs/IAlipayConfigProvider.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Util.Biz.Payments.Alipay.Configs { - /// - /// 支付宝配置提供器 - /// - public interface IAlipayConfigProvider { - /// - /// 获取配置 - /// - AlipayConfig GetConfig(); - } -} diff --git a/src/Util.Biz.Payments/Alipay/Parameters/AlipayContentBuilder.cs b/src/Util.Biz.Payments/Alipay/Parameters/AlipayContentBuilder.cs deleted file mode 100644 index 92dceb0ed..000000000 --- a/src/Util.Biz.Payments/Alipay/Parameters/AlipayContentBuilder.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Util.Biz.Payments.Alipay.Configs; -using Util.Biz.Payments.Core; -using Util.Parameters; -using Util.Parameters.Formats; - -namespace Util.Biz.Payments.Alipay.Parameters { - /// - /// 支付宝内容参数生成器 - /// - public class AlipayContentBuilder : ParameterBuilder { - /// - /// 加载支付参数 - /// - /// 支付参数 - public AlipayContentBuilder Load( PayParam param ) { - return OutTradeNo( param.OrderId ) - .Subject( param.Subject ) - .TotalAmount( param.Money ) - .TimeoutExpress( param.Timeout ); - } - - /// - /// 设置商户订单号 - /// - /// 商户订单号 - public AlipayContentBuilder OutTradeNo( string orderId ) { - Add( AlipayConst.OutTradeNo, orderId ); - return this; - } - - /// - /// 设置场景 - /// - /// 场景 - public AlipayContentBuilder Scene( string scene ) { - Add( AlipayConst.Scene, scene ); - return this; - } - - /// - /// 设置用户付款授权码 - /// - /// 用户付款授权码 - public AlipayContentBuilder AuthCode( string code ) { - Add( AlipayConst.AuthCode, code ); - return this; - } - - /// - /// 设置订单标题 - /// - /// 订单标题 - public AlipayContentBuilder Subject( string subject ) { - Add( AlipayConst.Subject, subject ); - return this; - } - - /// - /// 设置支付超时 - /// - /// 支付超时间隔,单位:分钟 - public AlipayContentBuilder TimeoutExpress( int timeout ) { - Add( AlipayConst.TimeoutExpress, $"{timeout}m" ); - return this; - } - - /// - /// 设置金额 - /// - /// 支付金额 - public AlipayContentBuilder TotalAmount( decimal amount ) { - Add( AlipayConst.TotalAmount, amount ); - return this; - } - - /// - /// 输出结果 - /// - public override string ToString() { - return Result( UrlParameterFormat.Instance ); - } - } -} diff --git a/src/Util.Biz.Payments/Alipay/Parameters/AlipayParameterBuilder.cs b/src/Util.Biz.Payments/Alipay/Parameters/AlipayParameterBuilder.cs deleted file mode 100644 index 4ce04252b..000000000 --- a/src/Util.Biz.Payments/Alipay/Parameters/AlipayParameterBuilder.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System.Collections.Generic; -using Util.Biz.Payments.Alipay.Configs; -using Util.Helpers; -using Util.Parameters; -using Util.Signatures; - -namespace Util.Biz.Payments.Alipay.Parameters { - /// - /// 支付宝参数生成器 - /// - public class AlipayParameterBuilder { - /// - /// 配置 - /// - private readonly AlipayConfig _config; - /// - /// 参数生成器 - /// - private readonly UrlParameterBuilder _builder; - - /// - /// 初始化支付宝参数生成器 - /// - /// 配置 - public AlipayParameterBuilder( AlipayConfig config ) { - config.CheckNull( nameof( config ) ); - _config = config; - _builder = new UrlParameterBuilder(); - Config(); - } - - /// - /// 设置配置 - /// - private void Config() { - Format( "json" ).Charset( "utf-8" ).SignType( "RSA2" ).Timestamp().Version( "1.0" ).AppId( _config.AppId ); - } - - /// - /// 设置格式 - /// - /// 格式 - private AlipayParameterBuilder Format( string format ) { - _builder.Add( AlipayConst.Format, format ); - return this; - } - - /// - /// 设置编码 - /// - /// 字符集 - private AlipayParameterBuilder Charset( string charset ) { - _builder.Add( AlipayConst.Charset, charset ); - return this; - } - - /// - /// 设置签名类型 - /// - /// 签名类型 - private AlipayParameterBuilder SignType( string type ) { - _builder.Add( AlipayConst.SignType, type ); - return this; - } - - /// - /// 设置时间戳 - /// - private AlipayParameterBuilder Timestamp() { - _builder.Add( AlipayConst.Timestamp, Time.GetDateTime() ); - return this; - } - - /// - /// 设置版本 - /// - /// 版本 - private AlipayParameterBuilder Version( string version ) { - _builder.Add( AlipayConst.Version, version ); - return this; - } - - /// - /// 设置应用Id - /// - /// 应用Id - public AlipayParameterBuilder AppId( string appId ) { - _builder.Add( AlipayConst.AppId, appId ); - return this; - } - - /// - /// 设置请求方法 - /// - /// 请求方法 - public AlipayParameterBuilder Method( string method ) { - _builder.Add( AlipayConst.Method, method ); - return this; - } - - /// - /// 设置内容 - /// - /// 内容 - public AlipayParameterBuilder Content( string content ) { - _builder.Add( AlipayConst.BizContent, content ); - return this; - } - - /// - /// 设置内容 - /// - /// 内容参数生成器 - public AlipayParameterBuilder Content( AlipayContentBuilder builder ) { - return Content( builder.ToJson() ); - } - - /// - /// 获取参数字典 - /// - public IDictionary GetDictionary() { - return GetSignBuilder().GetDictionary(); - } - - /// - /// 获取签名的参数生成器 - /// - private UrlParameterBuilder GetSignBuilder() { - var builder = new UrlParameterBuilder( _builder ); - builder.Add( AlipayConst.Sign, GetSign() ); - return builder; - } - - /// - /// 获取签名 - /// - private string GetSign() { - var signManager = new SignManager( new SignKey( _config.AppPrivateKey ), _builder ); - return signManager.Sign(); - } - - /// - /// 输出结果 - /// - public override string ToString() { - return GetSignBuilder().Result( true ); - } - } -} diff --git a/src/Util.Biz.Payments/Alipay/Results/AlipayResult.cs b/src/Util.Biz.Payments/Alipay/Results/AlipayResult.cs deleted file mode 100644 index c19492e3f..000000000 --- a/src/Util.Biz.Payments/Alipay/Results/AlipayResult.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System.Collections.Generic; -using Newtonsoft.Json.Linq; - -namespace Util.Biz.Payments.Alipay.Results { - /// - /// 支付宝结果 - /// - public class AlipayResult { - /// - /// 结果 - /// - private readonly IDictionary _result; - - /// - /// 初始化支付宝结果 - /// - /// json响应消息 - public AlipayResult( string json ) { - Raw = json; - _result = new Dictionary(); - LoadJson( json ); - } - - /// - /// 支付宝原始响应 - /// - public string Raw { get; } - - /// - /// 加载json - /// - private void LoadJson( string json ) { - if( json.IsEmpty() ) - return; - var jObject = JObject.Parse( json ); - foreach( var token in jObject.Children() ) - AddNodes( token ); - } - - /// - /// 添加节点 - /// - private void AddNodes( JToken token ) { - if( !( token is JProperty item ) ) - return; - foreach( var value in item.Value ) - AddNodes( value ); - if( GetIgnoreItems().Contains( item.Name ) ) - return; - _result.Add( item.Name, item.Value.SafeString() ); - } - - /// - /// 获取忽略项 - /// - private List GetIgnoreItems() { - return new List { - "alipay_trade_pay_response" - }; - } - - /// - /// 获取字典 - /// - public IDictionary GetDictionary() { - return _result; - } - - /// - /// 获取值 - /// - /// 键 - public string GetValue( string key ) { - if( key.IsEmpty() ) - return string.Empty; - return _result.ContainsKey( key ) ? _result[key] : string.Empty; - } - - /// - /// 是否包含指定键 - /// - /// 键 - public bool HasKey( string key ) { - if( key.IsEmpty() ) - return false; - return _result.ContainsKey( key ); - } - - /// - /// 获取状态码 - /// - public string GetCode() { - return GetValue( "code" ); - } - - /// - /// 获取消息 - /// - public string GetMessage() { - return GetValue( "msg" ); - } - - /// - /// 获取支付订单号 - /// - public string GetTradeNo() { - return GetValue( "trade_no" ); - } - - /// - /// 是否成功 - /// - public bool Success => GetCode() == "10000"; - } -} diff --git a/src/Util.Biz.Payments/Alipay/Services/AlipayBarcodePayService.cs b/src/Util.Biz.Payments/Alipay/Services/AlipayBarcodePayService.cs deleted file mode 100644 index dacf28adb..000000000 --- a/src/Util.Biz.Payments/Alipay/Services/AlipayBarcodePayService.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Util.Biz.Payments.Alipay.Configs; -using Util.Biz.Payments.Alipay.Parameters; -using Util.Biz.Payments.Core; -using Util.Exceptions; - -namespace Util.Biz.Payments.Alipay.Services { - /// - /// 支付宝条码支付服务 - /// - public class AlipayBarcodePayService : AlipayServiceBase { - /// - /// 初始化支付宝条码支付服务 - /// - /// 支付宝配置提供器 - public AlipayBarcodePayService( IAlipayConfigProvider provider ) : base( provider ){ - } - - /// - /// 获取场景 - /// - protected override string GetScene() { - return "bar_code"; - } - - /// - /// 获取请求方法 - /// - protected override string GetMethod() { - return "alipay.trade.pay"; - } - - /// - /// 获取支付方式 - /// - protected override PayWay GetPayWay() { - return PayWay.AlipayBarcodePay; - } - - /// - /// 验证参数 - /// - /// 支付参数 - protected override void ValidateParam( PayParam param ) { - if ( param.AuthCode.IsEmpty() ) - throw new Warning( PayResource.AuthCodeIsEmpty ); - } - - /// - /// 获取内容参数生成器 - /// - /// 支付参数 - protected override AlipayContentBuilder GetContentBuilder( PayParam param ) { - return base.GetContentBuilder( param ) - .AuthCode( param.AuthCode ); - } - } -} diff --git a/src/Util.Biz.Payments/Alipay/Services/AlipayServiceBase.cs b/src/Util.Biz.Payments/Alipay/Services/AlipayServiceBase.cs deleted file mode 100644 index 83177089f..000000000 --- a/src/Util.Biz.Payments/Alipay/Services/AlipayServiceBase.cs +++ /dev/null @@ -1,193 +0,0 @@ -using System.Threading.Tasks; -using Util.Biz.Payments.Alipay.Configs; -using Util.Biz.Payments.Alipay.Parameters; -using Util.Biz.Payments.Alipay.Results; -using Util.Biz.Payments.Core; -using Util.Helpers; -using Util.Logs; -using Util.Logs.Extensions; - -namespace Util.Biz.Payments.Alipay.Services { - /// - /// 支付宝支付服务 - /// - public abstract class AlipayServiceBase : IPayService { - /// - /// 支付宝跟踪日志名 - /// - public const string TraceLogName = "AlipayTraceLog"; - /// - /// 支付宝配置 - /// - private readonly AlipayConfig _config; - /// - /// 支付宝参数生成器 - /// - private readonly AlipayParameterBuilder _builder; - /// - /// 支付参数 - /// - private PayParam _payParam; - - /// - /// 初始化支付宝支付服务 - /// - /// 支付宝配置提供器 - protected AlipayServiceBase( IAlipayConfigProvider provider ) { - provider.CheckNull( nameof( provider ) ); - _config = provider.GetConfig(); - _builder = new AlipayParameterBuilder( _config ); - } - - /// - /// 支付 - /// - /// 支付参数 - public PayResult Pay( PayParam param ) { - return Async.Run( async () => await PayAsync( param ) ); - } - - /// - /// 支付 - /// - /// 支付参数 - public async Task PayAsync( PayParam param ) { - Validate( param ); - _payParam = param; - Config(); - var result = new AlipayResult( await RequestAsync() ); - WriteLog( result ); - return CreateResult( result ); - } - - /// - /// 验证 - /// - private void Validate( PayParam param ) { - param.CheckNull( nameof( param ) ); - param.Validate(); - ValidateParam( param ); - _config.Validate(); - } - - /// - /// 验证参数 - /// - /// 支付参数 - protected virtual void ValidateParam( PayParam param ) { - } - - /// - /// 参数配置 - /// - private void Config() { - var contentBuilder = GetContentBuilder( _payParam ).Scene( GetScene() ); - _builder.Content( contentBuilder ).Method( GetMethod() ); - } - - /// - /// 获取内容参数生成器 - /// - /// 支付参数 - protected virtual AlipayContentBuilder GetContentBuilder( PayParam param ) { - var builder = new AlipayContentBuilder(); - builder.Load( param ); - return builder; - } - - /// - /// 获取场景 - /// - protected virtual string GetScene() { - return string.Empty; - } - - /// - /// 获取请求方法 - /// - protected abstract string GetMethod(); - - /// - /// 发送请求 - /// - private async Task RequestAsync() { - if ( IsSendRequest == false ) - return string.Empty; - return await Web.Client() - .Post( GetGatewayUrl() ) - .Data( _builder.GetDictionary() ) - .ResultAsync(); - } - - /// - /// 是否发送请求 - /// - public bool IsSendRequest { get; set; } = true; - - /// - /// 获取支付宝网关Url - /// - private string GetGatewayUrl() { - return _config.GatewayUrl; - } - - /// - /// 写日志 - /// - private void WriteLog( AlipayResult result ) { - var log = GetLog(); - if( log.IsTraceEnabled == false ) - return; - log.Class( GetType().FullName ) - .Caption( "请求支付宝支付" ) - .Content( $"支付方式 : {GetPayWay().Description()}" ) - .Content( $"支付网关 : {GetGatewayUrl()}" ) - .Content() - .Content( "请求参数:" ) - .Content( _builder.GetDictionary() ) - .Content() - .Content( "返回结果:" ) - .Content( result.GetDictionary() ) - .Content() - .Content( "原始请求:" ) - .Content( _builder.ToString() ) - .Content() - .Content( "原始响应: " ) - .Content( result.Raw ) - .Trace(); - } - - /// - /// 获取日志操作 - /// - private ILog GetLog() { - try { - return Log.GetLog( TraceLogName ); - } - catch { - return Log.Null; - } - } - - /// - /// 获取支付方式 - /// - protected abstract PayWay GetPayWay(); - - /// - /// 创建结果 - /// - private PayResult CreateResult( AlipayResult result ) { - return new PayResult( result.Success,result.GetTradeNo(), result.Raw ) { - Message = result.GetMessage() - }; - } - - /// - /// 输出请求参数 - /// - public override string ToString() { - return _builder.ToString(); - } - } -} diff --git a/src/Util.Biz.Payments/Core/IPayService.cs b/src/Util.Biz.Payments/Core/IPayService.cs deleted file mode 100644 index 21179f4b3..000000000 --- a/src/Util.Biz.Payments/Core/IPayService.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Threading.Tasks; - -namespace Util.Biz.Payments.Core { - /// - /// 支付服务 - /// - public interface IPayService { - /// - /// 支付 - /// - /// 支付参数 - PayResult Pay( PayParam param ); - /// - /// 支付 - /// - /// 支付参数 - Task PayAsync( PayParam param ); - } -} diff --git a/src/Util.Biz.Payments/Core/PayParam.cs b/src/Util.Biz.Payments/Core/PayParam.cs deleted file mode 100644 index 87795bd49..000000000 --- a/src/Util.Biz.Payments/Core/PayParam.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.Linq; -using Util.Exceptions; -using Util.Validations; - -namespace Util.Biz.Payments.Core { - /// - /// 支付参数 - /// - public class PayParam { - /// - /// 商户订单号 - /// - [Required( ErrorMessageResourceType = typeof( PayResource ), ErrorMessageResourceName = "OrderIdIsEmpty" )] - public string OrderId { get; set; } - /// - /// 支付金额 - /// - public decimal Money { get; set; } - /// - /// 用户付款授权码 - /// - public string AuthCode { get; set; } - /// - /// 订单标题 - /// - public string Subject { get; set; } - /// - /// 支付订单付款超时时间,单位:分钟,默认为90分钟 - /// - public int Timeout { get; set; } = 90; - - /// - /// 验证 - /// - public void Validate() { - InitSubject(); - ValidateDataAnnotation(); - CustomValidate(); - } - - /// - /// 初始化订单标题 - /// - private void InitSubject() { - if ( Subject.IsEmpty() ) - Subject = OrderId; - } - - /// - /// 验证数据注解 - /// - private void ValidateDataAnnotation() { - var result = DataAnnotationValidation.Validate( this ); - if( result.IsValid == false ) - throw new Warning( result.First().ErrorMessage ); - } - - /// - /// 自定义验证 - /// - private void CustomValidate() { - if( Money <= 0 ) - throw new Warning( PayResource.InvalidMoney ); - } - } -} diff --git a/src/Util.Biz.Payments/Core/PayResult.cs b/src/Util.Biz.Payments/Core/PayResult.cs deleted file mode 100644 index 8dfad8a86..000000000 --- a/src/Util.Biz.Payments/Core/PayResult.cs +++ /dev/null @@ -1,43 +0,0 @@ -namespace Util.Biz.Payments.Core { - /// - /// 支付结果 - /// - public class PayResult { - /// - /// 初始化支付结果 - /// - /// 是否成功 - /// 交易编号 - /// 支付接口返回的原始消息 - public PayResult( bool success,string tradeId, string raw ) { - Success = success; - TradeId = tradeId; - Raw = raw; - } - - /// - /// 是否成功 - /// - public bool Success { get; } - - /// - /// 交易编号,外部支付系统的交易流水号 - /// - public string TradeId { get; } - - /// - /// 支付接口返回的原始消息 - /// - public string Raw { get; } - - /// - /// 结果 - /// - public string Result { get; set; } - - /// - /// 消息 - /// - public string Message { get; set; } - } -} diff --git a/src/Util.Biz.Payments/Core/PayWay.cs b/src/Util.Biz.Payments/Core/PayWay.cs deleted file mode 100644 index 8dec1e3af..000000000 --- a/src/Util.Biz.Payments/Core/PayWay.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.ComponentModel; - -namespace Util.Biz.Payments.Core { - /// - /// 支付方式 - /// - public enum PayWay { - /// - /// 支付宝条码支付 - /// - [Description( "支付宝条码支付" )] - AlipayBarcodePay - } -} diff --git a/src/Util.Biz.Payments/Factories/PayFactory.cs b/src/Util.Biz.Payments/Factories/PayFactory.cs deleted file mode 100644 index 18f7e252b..000000000 --- a/src/Util.Biz.Payments/Factories/PayFactory.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using Util.Biz.Payments.Alipay; -using Util.Biz.Payments.Alipay.Configs; -using Util.Biz.Payments.Alipay.Services; -using Util.Biz.Payments.Core; - -namespace Util.Biz.Payments.Factories { - /// - /// 支付工厂 - /// - public class PayFactory : IPayFactory { - /// - /// 支付宝配置提供器 - /// - private readonly IAlipayConfigProvider _alipayConfigProvider; - - /// - /// 初始化支付工厂 - /// - /// 支付宝配置提供器 - public PayFactory( IAlipayConfigProvider alipayConfigProvider ) { - _alipayConfigProvider = alipayConfigProvider; - } - - /// - /// 创建支付服务 - /// - /// 支付方式 - public IPayService CreatePayService( PayWay way ) { - switch( way ) { - case PayWay.AlipayBarcodePay: - return new AlipayBarcodePayService( _alipayConfigProvider ); - } - throw new NotImplementedException(); - } - } -} - diff --git a/src/Util.Biz.Payments/IPayFactory.cs b/src/Util.Biz.Payments/IPayFactory.cs deleted file mode 100644 index fa464b40e..000000000 --- a/src/Util.Biz.Payments/IPayFactory.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Biz.Payments.Core; - -namespace Util.Biz.Payments { - /// - /// 支付工厂 - /// - public interface IPayFactory { - /// - /// 创建支付服务 - /// - /// 支付方式 - IPayService CreatePayService( PayWay way ); - } -} diff --git a/src/Util.Biz.Payments/PayResource.Designer.cs b/src/Util.Biz.Payments/PayResource.Designer.cs deleted file mode 100644 index 357dc268a..000000000 --- a/src/Util.Biz.Payments/PayResource.Designer.cs +++ /dev/null @@ -1,99 +0,0 @@ -//------------------------------------------------------------------------------ -// -// 此代码由工具生成。 -// 运行时版本:4.0.30319.42000 -// -// 对此文件的更改可能会导致不正确的行为,并且如果 -// 重新生成代码,这些更改将会丢失。 -// -//------------------------------------------------------------------------------ - -namespace Util.Biz.Payments { - using System; - - - /// - /// 一个强类型的资源类,用于查找本地化的字符串等。 - /// - // 此类是由 StronglyTypedResourceBuilder - // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 - // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen - // (以 /str 作为命令选项),或重新生成 VS 项目。 - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class PayResource { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal PayResource() { - } - - /// - /// 返回此类使用的缓存的 ResourceManager 实例。 - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Util.Biz.Payments.PayResource", typeof(PayResource).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// 使用此强类型资源类,为所有资源查找 - /// 重写当前线程的 CurrentUICulture 属性。 - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// 查找类似 用户付款授权码 的本地化字符串。 - /// - public static string AuthCode { - get { - return ResourceManager.GetString("AuthCode", resourceCulture); - } - } - - /// - /// 查找类似 用户付款授权码不能为空 的本地化字符串。 - /// - public static string AuthCodeIsEmpty { - get { - return ResourceManager.GetString("AuthCodeIsEmpty", resourceCulture); - } - } - - /// - /// 查找类似 支付金额必须大于0 的本地化字符串。 - /// - public static string InvalidMoney { - get { - return ResourceManager.GetString("InvalidMoney", resourceCulture); - } - } - - /// - /// 查找类似 商户订单号不能为空 的本地化字符串。 - /// - public static string OrderIdIsEmpty { - get { - return ResourceManager.GetString("OrderIdIsEmpty", resourceCulture); - } - } - } -} diff --git a/src/Util.Biz.Payments/PayResource.resx b/src/Util.Biz.Payments/PayResource.resx deleted file mode 100644 index f8d238a76..000000000 --- a/src/Util.Biz.Payments/PayResource.resx +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 用户付款授权码 - - - 用户付款授权码不能为空 - - - 支付金额必须大于0 - - - 商户订单号不能为空 - - \ No newline at end of file diff --git a/src/Util.Biz/01-Util.Biz.csproj b/src/Util.Biz/01-Util.Biz.csproj deleted file mode 100644 index bb9b374a6..000000000 --- a/src/Util.Biz/01-Util.Biz.csproj +++ /dev/null @@ -1,41 +0,0 @@ - - - - netstandard2.0 - Util.Biz是Util应用框架的公共业务基础类库。 -Util是一个.net core平台下的应用框架,旨在提升小型团队的开发输出能力,由常用公共操作类(工具类)、分层架构基类、Ui组件,第三方组件封装,第三方业务接口封装,配套代码生成模板,权限等组成。 - - Util应用框架以MIT开源发布,可随意使用 - 何镇汐 - 何镇汐 - 何镇汐 - 0.2.2-preview - 0.2.2.0 - 0.2.2.0 - https://github.com/dotnetcore/Util/blob/master/LICENSE.txt - https://github.com/dotnetcore/Util - https://avatars2.githubusercontent.com/u/19421630 - https://github.com/dotnetcore/Util - git - util;applicationframework;dotnetcore;aspnetcore; - false - Util.Biz - Util.Biz - Util.Biz - - - - ..\..\output\release - ..\..\output\release\netstandard2.0\Util.Biz.xml - - - - ..\..\output\release - ..\..\output\release\netstandard2.0\Util.Biz.xml - - - - - - - diff --git a/src/Util.Biz/Addresses/Address.cs b/src/Util.Biz/Addresses/Address.cs deleted file mode 100644 index 4ad775d5b..000000000 --- a/src/Util.Biz/Addresses/Address.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using Util.Domains; - -namespace Util.Biz.Addresses { - /// - /// 地址 - 不可变 - /// - public class Address : ValueObjectBase
{ - /// - /// 初始化地址 - /// - protected Address() { - } - - /// - /// 初始化地址 - /// - /// 省份编号 - /// 城市编号 - /// 区县编号 - /// 省份 - /// 城市 - /// 区县 - /// 街道 - /// 邮政编码 - public Address( Guid? provinceId, Guid? cityId, Guid? countyId, string province, string city, string county, string street, string zip = "" ) { - ProvinceId = provinceId; - CityId = cityId; - CountyId = countyId; - Province = province; - City = city; - County = county; - Street = street; - Zip = zip; - } - - /// - /// 省份编号 - /// - [Column( "ProvinceId" )] - public Guid? ProvinceId { get; } - /// - /// 城市编号 - /// - [Column( "CityId" )] - public Guid? CityId { get; } - /// - /// 区县编号 - /// - [Column( "CountyId" )] - public Guid? CountyId { get; } - /// - /// 省份 - /// - [Column( "Province" )] - [StringLength( 100, ErrorMessage = "省份输入过长,不能超过100位" )] - public string Province { get; } - /// - /// 城市 - /// - [Column( "City" )] - [StringLength( 100, ErrorMessage = "城市输入过长,不能超过100位" )] - public string City { get; } - /// - /// 区县 - /// - [Column( "County" )] - [StringLength( 100, ErrorMessage = "区县输入过长,不能超过100位" )] - public string County { get; } - /// - /// 街道 - /// - [Column( "Street" )] - [StringLength( 200, ErrorMessage = "街道输入过长,不能超过200位" )] - public string Street { get; } - /// - /// 邮政编码 - /// - [Column( "Zip" )] - [StringLength( 20, ErrorMessage = "邮政编码输入过长,不能超过20位" )] - public string Zip { get; } - - /// - /// 地址描述 - /// - public string Description => $"{Province}{City}{County}{Street}"; - - /// - /// 添加描述 - /// - protected override void AddDescriptions() { - AddDescription( "省份编号", ProvinceId ); - AddDescription( "城市编号", CityId ); - AddDescription( "区县编号", CountyId ); - AddDescription( "省份", Province ); - AddDescription( "城市", City ); - AddDescription( "区县", County ); - AddDescription( "街道", Street ); - AddDescription( "邮政编码", Zip ); - } - - /// - /// 空地址 - /// - public static readonly Address Null = new NullAddress(); - } -} diff --git a/src/Util.Biz/Addresses/AddressInfo.cs b/src/Util.Biz/Addresses/AddressInfo.cs deleted file mode 100644 index 10013a834..000000000 --- a/src/Util.Biz/Addresses/AddressInfo.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.Runtime.Serialization; -using Util.Domains; - -namespace Util.Biz.Addresses { - /// - /// 地址信息 - 可变 - /// - [DataContract] - public class AddressInfo : ValueObjectBase { - /// - /// 省份编号 - /// - [DataMember] - public string ProvinceId { get; set; } - - /// - /// 城市编号 - /// - [DataMember] - public string CityId { get; set; } - - /// - /// 区县编号 - /// - [DataMember] - public string CountyId { get; set; } - - /// - /// 省份 - /// - [StringLength( 100, ErrorMessage = "省份输入过长,不能超过100位" )] - [Display( Name = "省份" )] - [DataMember] - public string Province { get; set; } - - /// - /// 城市 - /// - [StringLength( 100, ErrorMessage = "城市输入过长,不能超过100位" )] - [Display( Name = "城市" )] - [DataMember] - public string City { get; set; } - - /// - /// 区县 - /// - [StringLength( 100, ErrorMessage = "区县输入过长,不能超过100位" )] - [Display( Name = "区县" )] - [DataMember] - public string County { get; set; } - - /// - /// 街道 - /// - [StringLength( 200, ErrorMessage = "街道输入过长,不能超过200位" )] - [Display( Name = "街道" )] - [DataMember] - public string Street { get; set; } - - /// - /// 邮政编码 - /// - [StringLength( 20, ErrorMessage = "邮政编码输入过长,不能超过20位" )] - [Display( Name = "邮政编码" )] - [DataMember] - public string Zip { get; set; } - - /// - /// 地址描述 - /// - [Display( Name = "地址描述" )] - [DataMember] - public string Description { get; set; } - } -} diff --git a/src/Util.Biz/Addresses/Extension.AddressInfo.cs b/src/Util.Biz/Addresses/Extension.AddressInfo.cs deleted file mode 100644 index ac9c06c6a..000000000 --- a/src/Util.Biz/Addresses/Extension.AddressInfo.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Util.Biz.Addresses { - /// - /// 地址信息扩展 - /// - public static class Extension { - /// - /// 转换为地址 - /// - public static Address ToAddress( this AddressInfo info ) { - return new Address( info.ProvinceId.ToGuidOrNull(), info.CityId.ToGuidOrNull(), info.CountyId.ToGuidOrNull(), info.Province, info.City, info.County, info.Street, info.Zip ); - } - - /// - /// 转换为地址信息 - /// - /// 地址 - public static AddressInfo ToInfo( this Address address ) { - return new AddressInfo { - ProvinceId = address.ProvinceId.SafeString(), - CityId = address.CityId.SafeString(), - CountyId = address.CountyId.SafeString(), - Province = address.Province, - City = address.City, - County = address.County, - Street = address.Street, - Zip = address.Zip, - Description = address.Description - }; - } - } -} diff --git a/src/Util.Biz/Addresses/NullAddress.cs b/src/Util.Biz/Addresses/NullAddress.cs deleted file mode 100644 index f17a92e4f..000000000 --- a/src/Util.Biz/Addresses/NullAddress.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.ComponentModel.DataAnnotations.Schema; - -namespace Util.Biz.Addresses { - /// - /// 空地址 - /// - [NotMapped] - public class NullAddress : Address{ - } -} diff --git a/src/Util.Biz/Enums/Gender.cs b/src/Util.Biz/Enums/Gender.cs deleted file mode 100644 index f7a95aa29..000000000 --- a/src/Util.Biz/Enums/Gender.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.ComponentModel; - -namespace Util.Biz.Enums { - /// - /// 性别 - /// - public enum Gender { - /// - /// 女 - /// - [Description( "女士" )] - Female = 1, - /// - /// 男 - /// - [Description( "先生" )] - Male = 2 - } - - /// - /// 性别枚举扩展 - /// - public static class GenderExtensions { - /// - /// 获取描述 - /// - public static string Description( this Gender? gender ) { - return gender == null ? string.Empty : gender.Value.Description(); - } - - /// - /// 获取值 - /// - public static int? Value( this Gender? gender ) { - return gender?.Value(); - } - } -} diff --git a/src/Util.Biz/Enums/Nation.cs b/src/Util.Biz/Enums/Nation.cs deleted file mode 100644 index 1a990a1dd..000000000 --- a/src/Util.Biz/Enums/Nation.cs +++ /dev/null @@ -1,308 +0,0 @@ -using System.ComponentModel; - -namespace Util.Biz.Enums { - /// - /// 民族 - /// - public enum Nation { - /// - /// 汉族 - /// - [Description( "汉族" )] - Hz = 0, - /// - /// 蒙古族 - /// - [Description( "蒙古族" )] - Mgz = 1, - /// - /// 回族 - /// - [Description( "回族" )] - HuiZ = 2, - /// - /// 藏族 - /// - [Description( "藏族" )] - Zz = 3, - /// - /// 维吾尔族 - /// - [Description( "维吾尔族" )] - Wwez = 4, - /// - /// 苗族 - /// - [Description( "苗族" )] - Mz = 5, - /// - /// 彝族 - /// - [Description( "彝族" )] - Yz = 6, - /// - /// 壮族 - /// - [Description( "壮族" )] - ZhuangZ = 7, - /// - /// 布依族 - /// - [Description( "布依族" )] - Byz = 8, - /// - /// 朝鲜族 - /// - [Description( "朝鲜族" )] - Cxz = 9, - /// - /// 满族 - /// - [Description( "满族" )] - ManZ = 10, - /// - /// 侗族 - /// - [Description( "侗族" )] - Tz = 11, - /// - /// 瑶族 - /// - [Description( "瑶族" )] - YaoZ = 12, - /// - /// 白族 - /// - [Description( "白族" )] - Bz = 13,//baizu - /// - /// 土家族 - /// - [Description( "土家族" )] - Tjz = 14, - /// - /// 哈尼族 - /// - [Description( "哈尼族" )] - Hnz = 15, - /// - /// 哈萨克族 - /// - [Description( "哈萨克族" )] - Hskz = 16, - /// - /// 傣族 - /// - [Description( "傣族" )] - Dz = 17, - /// - /// 黎族 - /// - [Description( "黎族" )] - Lz = 18, - /// - /// 傈僳族 - /// - [Description( "傈僳族" )] - Lsz = 19, - /// - /// 佤族 - /// - [Description( "佤族" )] - Wz = 20, - /// - /// 畲族 - /// - [Description( "畲族" )] - Sz = 21, - /// - /// 高山族 - /// - [Description( "高山族" )] - Gsz = 22, - /// - /// 拉祜族 - /// - [Description( "拉祜族" )] - Lhz = 23, - /// - /// 水族 - /// - [Description( "水族" )] - ShuiZ = 24, - /// - /// 东乡族 - /// - [Description( "东乡族" )] - Dxz = 25, - /// - /// 纳西族 - /// - [Description( "纳西族" )] - Nxz = 26, - /// - /// 景颇族 - /// - [Description( "景颇族" )] - Jpz = 27, - /// - /// 柯尔克孜族 - /// - [Description( "柯尔克孜族" )] - Kekzz = 28, - /// - /// 土族 - /// - [Description( "土族" )] - TuZ = 29, - /// - /// 达斡尔族 - /// - [Description( "达斡尔族" )] - Dwez = 30, - /// - /// 仫佬族 - /// - [Description( "仫佬族" )] - Mlz = 31, - /// - /// 羌族 - /// - [Description( "羌族" )] - Qz = 32, - /// - /// 布朗族 - /// - [Description( "布朗族" )] - Blz = 33, - /// - /// 撒拉族 - /// - [Description( "撒拉族" )] - Slz = 34, - /// - /// 毛南族 - /// - [Description( "毛南族" )] - Mnz = 35, - /// - /// 仡佬族 - /// - [Description( "仡佬族" )] - Ylz = 36, - /// - /// 锡伯族 - /// - [Description( "锡伯族" )] - Xbz = 37, - /// - /// 阿昌族 - /// - [Description( "阿昌族" )] - Acz = 38, - /// - /// 普米族 - /// - [Description( "普米族" )] - Pmz = 39, - /// - /// 塔吉克族 - /// - [Description( "塔吉克族" )] - Tjkz = 40, - /// - /// 怒族 - /// - [Description( "怒族" )] - Nz = 41, - /// - /// 乌孜别克族 - /// - [Description( "乌孜别克族" )] - Wzbkz = 42, - /// - /// 俄罗斯族 - /// - [Description( "俄罗斯族" )] - Elsz = 43, - /// - /// 鄂温克族 - /// - [Description( "鄂温克族" )] - Ewkz = 44, - /// - /// 德昂族 - /// - [Description( "德昂族" )] - Daz = 45, - /// - /// 保安族 - /// - [Description( "保安族" )] - Baz = 46, - /// - /// 裕固族 - /// - [Description( "裕固族" )] - Ygz = 47, - /// - /// 京族 - /// - [Description( "京族" )] - Jz = 48, - /// - /// 塔塔尔族 - /// - [Description( "塔塔尔族" )] - Ttrz = 49, - /// - /// 独龙族 - /// - [Description( "独龙族" )] - Dlz = 50, - /// - /// 鄂伦春族 - /// - [Description( "鄂伦春族" )] - Elcz = 51, - /// - /// 赫哲族 - /// - [Description( "赫哲族" )] - Hzz = 52, - /// - /// 门巴族 - /// - [Description( "门巴族" )] - Mbz = 53, - /// - /// 珞巴族 - /// - [Description( "珞巴族" )] - Lbz = 54, - /// - /// 基诺族 - /// - [Description( "基诺族" )] - Jnz = 55 - } - - /// - /// 民族枚举扩展 - /// - public static class NationExtensions { - /// - /// 获取描述 - /// - public static string Description( this Nation? nation ) { - return nation == null ? string.Empty : nation.Value.Description(); - } - - /// - /// 获取值 - /// - public static int? Value( this Nation? nation ) { - return nation?.Value(); - } - } -} diff --git a/src/Util.Caching.EasyCaching/02-Util.Caching.EasyCaching.csproj b/src/Util.Caching.EasyCaching/02-Util.Caching.EasyCaching.csproj new file mode 100644 index 000000000..f495d267a --- /dev/null +++ b/src/Util.Caching.EasyCaching/02-Util.Caching.EasyCaching.csproj @@ -0,0 +1,41 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Caching.EasyCaching + Util.Caching.EasyCaching + Util.Caching.EasyCaching是Util应用框架基于EasyCaching的缓存操作类库,默认引入内存缓存和Redis缓存实现 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Caching.EasyCaching.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Caching.EasyCaching.xml + + + + + True + False + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Util.Caching.EasyCaching/AppBuilderExtensions.cs b/src/Util.Caching.EasyCaching/AppBuilderExtensions.cs new file mode 100644 index 000000000..f61977659 --- /dev/null +++ b/src/Util.Caching.EasyCaching/AppBuilderExtensions.cs @@ -0,0 +1,203 @@ +namespace Util.Caching.EasyCaching; + +/// +/// EasyCaching缓存操作扩展 +/// +public static class AppBuilderExtensions { + + #region 字段 + + /// + /// 最大随机秒数 + /// + public static int MaxRdSecond = 1200; + + #endregion + + #region AddMemoryCache + + /// + /// 配置EasyCaching内存缓存操作 + /// + /// 应用生成器 + public static IAppBuilder AddMemoryCache( this IAppBuilder builder ) { + return builder.AddMemoryCache( options => { + options.MaxRdSecond = MaxRdSecond; + options.CacheNulls = true; + } ); + } + + /// + /// 配置EasyCaching内存缓存操作 + /// + /// 应用生成器 + /// 配置 + /// 配置节名称,默认值: "EasyCaching:Memory" + public static IAppBuilder AddMemoryCache( this IAppBuilder builder, IConfiguration configuration, string section = "EasyCaching:Memory" ) { + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + ConfigCommonService( services ); + services.TryAddSingleton(); + services.AddEasyCaching( options => options.UseInMemory( configuration, CacheProviderKey.MemoryCache, section ) ); + } ); + return builder; + } + + /// + /// 配置公共服务 + /// + private static void ConfigCommonService( IServiceCollection services ) { + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + } + + /// + /// 配置EasyCaching内存缓存操作 + /// + /// 应用生成器 + /// 内存缓存配置操作 + public static IAppBuilder AddMemoryCache( this IAppBuilder builder, Action setupAction ) { + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + ConfigCommonService( services ); + services.TryAddSingleton(); + services.AddEasyCaching( options => options.UseInMemory( setupAction ) ); + } ); + return builder; + } + + #endregion + + #region AddRedisCache + + /// + /// 配置EasyCaching Redis缓存操作 + /// + /// 应用生成器 + /// Redis服务地址,范例: 127.0.0.1 + /// Redis服务端口,默认值: 6379 + /// 缓存键前缀,默认值: null + public static IAppBuilder AddRedisCache( this IAppBuilder builder, string host,int port = 6379, string keyPrefix = null ) { + return builder.AddRedisCache( options => { + options.MaxRdSecond = MaxRdSecond; + options.CacheNulls = true; + options.DBConfig.AllowAdmin = true; + options.DBConfig.KeyPrefix = keyPrefix; + options.DBConfig.Endpoints.Add( new ServerEndPoint( host, port ) ); + } ); + } + + /// + /// 配置EasyCaching Redis缓存操作 + /// + /// 应用生成器 + /// 配置 + /// 配置节名称,默认值: "EasyCaching:Redis" + public static IAppBuilder AddRedisCache( this IAppBuilder builder, IConfiguration configuration, string section = "EasyCaching:Redis" ) { + builder.CheckNull( nameof( builder ) ); + CachingOptions.AddRedisEndPoints( configuration,section ); + builder.Host.ConfigureServices( ( context, services ) => { + ConfigCommonService( services ); + services.TryAddSingleton(); + services.AddEasyCaching( options => { + var serializerNameKey = $"{section}:SerializerName"; + var serializerName = configuration[serializerNameKey]; + if ( serializerName.IsEmpty() ) + configuration[serializerNameKey] = CacheProviderKey.SystemTextJson; + options.UseRedis( configuration, CacheProviderKey.RedisCache, section ); + ConfigRedisSerializer( options ); + } ); + } ); + return builder; + } + + /// + /// 配置Redis序列化 + /// + private static void ConfigRedisSerializer( EasyCachingOptions options ) { + options.WithSystemTextJson( t => t.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, CacheProviderKey.SystemTextJson ); + } + + /// + /// 配置EasyCaching Redis缓存操作 + /// + /// 应用生成器 + /// Redis缓存配置操作 + public static IAppBuilder AddRedisCache( this IAppBuilder builder, Action setupAction ) { + builder.CheckNull( nameof( builder ) ); + CachingOptions.AddRedisEndPoints( setupAction ); + builder.Host.ConfigureServices( ( context, services ) => { + ConfigCommonService( services ); + services.TryAddSingleton(); + services.AddEasyCaching( options => { + ConfigRedisSerializer( options ); + options.UseRedis( redisOptions => { + redisOptions.SerializerName = CacheProviderKey.SystemTextJson; + setupAction( redisOptions ); + } ); + } ); + } ); + return builder; + } + + #endregion + + #region AddHybridCache + + /// + /// 配置EasyCaching 2级缓存操作 + /// + /// 应用生成器 + /// 订阅主题,默认值: "EasyCachingHybridCache" + public static IAppBuilder AddHybridCache( this IAppBuilder builder,string topicName= "EasyCachingHybridCache" ) { + return builder.AddHybridCache( hybridOptions => { + hybridOptions.LocalCacheProviderName = CacheProviderKey.MemoryCache; + hybridOptions.DistributedCacheProviderName = CacheProviderKey.RedisCache; + hybridOptions.TopicName = topicName; + }, redisBusOptions => { + CachingOptions.GetRedisEndPoints().ForEach( endpoint => { + redisBusOptions.Endpoints.Add( endpoint ); + } ); + redisBusOptions.SerializerName = CacheProviderKey.SystemTextJson; + } ); + } + + /// + /// 配置EasyCaching 2级缓存操作 + /// + /// 应用生成器 + /// 配置 + /// 2级缓存配置节名称,默认值: "EasyCaching:Hybrid" + /// Redis总线配置节名称,默认值: "EasyCaching:RedisBus" + public static IAppBuilder AddHybridCache( this IAppBuilder builder, IConfiguration configuration, + string hybridSection = "EasyCaching:Hybrid", string redisBusSection = "EasyCaching:RedisBus" ) { + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + services.AddEasyCaching( options => { + options.UseHybrid( configuration, CacheProviderKey.HybridCache, hybridSection ); + options.WithRedisBus( configuration, CacheProviderKey.RedisBus, redisBusSection ); + } ); + } ); + return builder; + } + + /// + /// 配置EasyCaching 2级缓存操作 + /// + /// 应用生成器 + /// 2缓存配置操作 + /// Redis总线配置操作 + public static IAppBuilder AddHybridCache( this IAppBuilder builder, Action hybridConfigAction, Action redisBusConfigAction ) { + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + services.AddEasyCaching( options => { + options.UseHybrid( hybridConfigAction ); + options.WithRedisBus( redisBusConfigAction ); + } ); + } ); + return builder; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Caching.EasyCaching/CacheKeyGenerator.cs b/src/Util.Caching.EasyCaching/CacheKeyGenerator.cs new file mode 100644 index 000000000..ee5d811b4 --- /dev/null +++ b/src/Util.Caching.EasyCaching/CacheKeyGenerator.cs @@ -0,0 +1,24 @@ +namespace Util.Caching.EasyCaching; + +/// +/// 缓存键生成器 +/// +public class CacheKeyGenerator : ICacheKeyGenerator { + /// + /// EasyCaching缓存键生成器 + /// + private readonly IEasyCachingKeyGenerator _keyGenerator; + + /// + /// 初始化缓存键生成器 + /// + /// EasyCaching缓存键生成器 + public CacheKeyGenerator( IEasyCachingKeyGenerator keyGenerator ) { + _keyGenerator = keyGenerator ?? throw new ArgumentNullException( nameof(keyGenerator) ); + } + + /// + public string CreateCacheKey( MethodInfo methodInfo, object[] args, string prefix ) { + return _keyGenerator.GetCacheKey( methodInfo, args, prefix ); + } +} \ No newline at end of file diff --git a/src/Util.Caching.EasyCaching/CacheManager.cs b/src/Util.Caching.EasyCaching/CacheManager.cs new file mode 100644 index 000000000..9480ebd36 --- /dev/null +++ b/src/Util.Caching.EasyCaching/CacheManager.cs @@ -0,0 +1,415 @@ +namespace Util.Caching.EasyCaching; + +/// +/// EasyCaching缓存服务 +/// +public class CacheManager : ICache { + + #region 字段 + + /// + /// 缓存提供器 + /// + private readonly IEasyCachingProviderBase _provider; + /// + /// 缓存提供器 + /// + private readonly IEasyCachingProvider _cachingProvider; + + #endregion + + #region 构造方法 + + /// + /// 初始化EasyCaching缓存服务 + /// + /// EasyCaching缓存提供器 + /// EasyCaching 2级缓存提供器 + public CacheManager( IEasyCachingProvider provider, IHybridCachingProvider hybridProvider = null ) { + CachingOptions.Clear(); + if ( provider != null ) { + _provider = provider; + _cachingProvider = provider; + } + if( hybridProvider != null ) + _provider = hybridProvider; + _provider.CheckNull( nameof( provider ) ); + } + + #endregion + + #region Exists + + /// + public bool Exists( CacheKey key ) { + key.Validate(); + return Exists( key.Key ); + } + + /// + public bool Exists( string key ) { + return _provider.Exists( key ); + } + + #endregion + + #region ExistsAsync + + /// + public async Task ExistsAsync( CacheKey key, CancellationToken cancellationToken = default ) { + key.Validate(); + return await ExistsAsync( key.Key, cancellationToken ); + } + + /// + public async Task ExistsAsync( string key, CancellationToken cancellationToken = default ) { + return await _provider.ExistsAsync( key, cancellationToken ); + } + + #endregion + + #region Get + + /// + public T Get( CacheKey key ) { + key.Validate(); + return Get( key.Key ); + } + + /// + public T Get( string key ) { + var result = _provider.Get( key ); + return result.Value; + } + + /// + public List Get( IEnumerable keys ) { + return Get( ToKeys( keys ) ); + } + + /// + /// 转换为缓存键字符串集合 + /// + private IEnumerable ToKeys( IEnumerable keys ) { + keys.CheckNull( nameof( keys ) ); + var cacheKeys = keys.ToList(); + cacheKeys.ForEach( t => t.Validate() ); + return cacheKeys.Select( t => t.Key ); + } + + /// + public List Get( IEnumerable keys ) { + Validate(); + var result = _cachingProvider.GetAll( keys ); + return result.Values.Select( t => t.Value ).ToList(); + } + + /// + /// 验证 + /// + private void Validate() { + if ( _cachingProvider == null ) + throw new NotSupportedException( "2级缓存不支持该操作" ); + } + + /// + public T Get( CacheKey key, Func action, CacheOptions options = null ) { + key.Validate(); + return Get( key.Key, action, options ); + } + + /// + public T Get( string key, Func action, CacheOptions options = null ) { + var result = _provider.Get( key, action, GetExpiration( options ) ); + return result.Value; + } + + /// + /// 获取过期时间间隔 + /// + private TimeSpan GetExpiration( CacheOptions options ) { + var result = options?.Expiration; + result ??= TimeSpan.FromHours( 8 ); + return result.SafeValue(); + } + + #endregion + + #region GetAsync + + /// + public async Task GetAsync( string key, Type type, CancellationToken cancellationToken = default ) { + return await _provider.GetAsync( key, type, cancellationToken ); + } + + /// + public async Task GetAsync( CacheKey key, CancellationToken cancellationToken = default ) { + key.Validate(); + return await GetAsync( key.Key, cancellationToken ); + } + + /// + public async Task GetAsync( string key, CancellationToken cancellationToken = default ) { + var result = await _provider.GetAsync( key, cancellationToken ); + return result.Value; + } + + /// + public async Task> GetAsync( IEnumerable keys, CancellationToken cancellationToken = default ) { + return await GetAsync( ToKeys( keys ), cancellationToken ); + } + + /// + public async Task> GetAsync( IEnumerable keys, CancellationToken cancellationToken = default ) { + Validate(); + var result = await _cachingProvider.GetAllAsync( keys, cancellationToken ); + return result.Values.Select( t => t.Value ).ToList(); + } + + /// + public async Task GetAsync( CacheKey key, Func> action, CacheOptions options = null, CancellationToken cancellationToken = default ) { + key.Validate(); + return await GetAsync( key.Key, action, options, cancellationToken ); + } + + /// + public async Task GetAsync( string key, Func> action, CacheOptions options = null, CancellationToken cancellationToken = default ) { + var result = await _provider.GetAsync( key, action, GetExpiration( options ), cancellationToken ); + return result.Value; + } + + #endregion + + #region GetByPrefix + + /// + public List GetByPrefix( string prefix ) { + if( prefix.IsEmpty() ) + return new List(); + Validate(); + return _cachingProvider.GetByPrefix( prefix ).Where( t => t.Value.HasValue ).Select( t => t.Value.Value ).ToList(); + } + + #endregion + + #region GetByPrefixAsync + + /// + public async Task> GetByPrefixAsync( string prefix, CancellationToken cancellationToken = default ) { + if( prefix.IsEmpty() ) + return new List(); + Validate(); + var result = await _cachingProvider.GetByPrefixAsync( prefix, cancellationToken ); + return result.Where( t => t.Value.HasValue ).Select( t => t.Value.Value ).ToList(); + } + + #endregion + + #region TrySet + + /// + public bool TrySet( CacheKey key, T value, CacheOptions options = null ) { + key.Validate(); + return TrySet( key.Key, value, options ); + } + + /// + public bool TrySet( string key, T value, CacheOptions options = null ) { + return _provider.TrySet( key, value, GetExpiration( options ) ); + } + + #endregion + + #region TrySetAsync + + /// + public async Task TrySetAsync( CacheKey key, T value, CacheOptions options = null, CancellationToken cancellationToken = default ) { + key.Validate(); + return await TrySetAsync( key.Key, value, options, cancellationToken ); + } + + /// + public async Task TrySetAsync( string key, T value, CacheOptions options = null, CancellationToken cancellationToken = default ) { + return await _provider.TrySetAsync( key, value, GetExpiration( options ), cancellationToken ); + } + + #endregion + + #region Set + + /// + public void Set( CacheKey key, T value, CacheOptions options = null ) { + key.Validate(); + Set( key.Key, value, options ); + } + + /// + public void Set( string key, T value, CacheOptions options = null ) { + _provider.Set( key, value, GetExpiration( options ) ); + } + + /// + public void Set( IDictionary items, CacheOptions options = null ) { + Set( ToItems( items ), options ); + } + + /// + /// 转换为缓存项集合 + /// + private IDictionary ToItems( IDictionary items ) { + items.CheckNull( nameof( items ) ); + return items.Select( item => { + item.Key.Validate(); + return new KeyValuePair( item.Key.Key, item.Value ); + } ).ToDictionary( t => t.Key, t => t.Value ); + } + + /// + public void Set( IDictionary items, CacheOptions options = null ) { + _provider.SetAll( items, GetExpiration( options ) ); + } + + #endregion + + #region SetAsync + + /// + public async Task SetAsync( CacheKey key, T value, CacheOptions options = null, CancellationToken cancellationToken = default ) { + key.Validate(); + await SetAsync( key.Key, value, options, cancellationToken ); + } + + /// + public async Task SetAsync( string key, T value, CacheOptions options = null, CancellationToken cancellationToken = default ) { + await _provider.SetAsync( key, value, GetExpiration( options ), cancellationToken ); + } + + /// + public async Task SetAsync( IDictionary items, CacheOptions options = null, CancellationToken cancellationToken = default ) { + await SetAsync( ToItems( items ), options, cancellationToken ); + } + + /// + public async Task SetAsync( IDictionary items, CacheOptions options = null, CancellationToken cancellationToken = default ) { + await _provider.SetAllAsync( items, GetExpiration( options ), cancellationToken ); + } + + #endregion + + #region Remove + + /// + public void Remove( CacheKey key ) { + key.Validate(); + Remove( key.Key ); + } + + /// + public void Remove( string key ) { + _provider.Remove( key ); + } + + /// + public void Remove( IEnumerable keys ) { + Remove( ToKeys( keys ) ); + } + + /// + public void Remove( IEnumerable keys ) { + _provider.RemoveAll( keys ); + } + + #endregion + + #region RemoveAsync + + /// + public async Task RemoveAsync( CacheKey key, CancellationToken cancellationToken = default ) { + key.Validate(); + await RemoveAsync( key.Key, cancellationToken ); + } + + /// + public async Task RemoveAsync( string key, CancellationToken cancellationToken = default ) { + await _provider.RemoveAsync( key, cancellationToken ); + } + + /// + public async Task RemoveAsync( IEnumerable keys, CancellationToken cancellationToken = default ) { + await RemoveAsync( ToKeys( keys ), cancellationToken ); + } + + /// + public async Task RemoveAsync( IEnumerable keys, CancellationToken cancellationToken = default ) { + await _provider.RemoveAllAsync( keys, cancellationToken ); + } + + #endregion + + #region RemoveByPrefix + + /// + /// 通过缓存键前缀移除缓存 + /// + /// 缓存键前缀 + public void RemoveByPrefix( string prefix ) { + if( prefix.IsEmpty() ) + return; + _provider.RemoveByPrefix( prefix ); + } + + #endregion + + #region RemoveByPrefixAsync + + /// + public async Task RemoveByPrefixAsync( string prefix, CancellationToken cancellationToken = default ) { + if( prefix.IsEmpty() ) + return; + await _provider.RemoveByPrefixAsync( prefix, cancellationToken ); + } + + #endregion + + #region RemoveByPattern + + /// + public void RemoveByPattern( string pattern ) { + if( pattern.IsEmpty() ) + return; + _provider.RemoveByPattern( pattern ); + } + + #endregion + + #region RemoveByPatternAsync + + /// + public async Task RemoveByPatternAsync( string pattern, CancellationToken cancellationToken = default ) { + if( pattern.IsEmpty() ) + return; + await _provider.RemoveByPatternAsync( pattern, cancellationToken ); + } + + #endregion + + #region Clear + + /// + public void Clear() { + Validate(); + _cachingProvider.Flush(); + } + + #endregion + + #region ClearAsync + + /// + public async Task ClearAsync( CancellationToken cancellationToken = default ) { + Validate(); + await _cachingProvider.FlushAsync( cancellationToken ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Caching.EasyCaching/CacheProviderKey.cs b/src/Util.Caching.EasyCaching/CacheProviderKey.cs new file mode 100644 index 000000000..918b8256c --- /dev/null +++ b/src/Util.Caching.EasyCaching/CacheProviderKey.cs @@ -0,0 +1,27 @@ +namespace Util.Caching.EasyCaching; + +/// +/// 缓存提供器标识 +/// +public static class CacheProviderKey { + /// + /// 内存缓存提供器标识 + /// + public const string MemoryCache = "DefaultInMemory"; + /// + /// Redis缓存提供器标识 + /// + public const string RedisCache = "DefaultRedis"; + /// + /// Hybrid缓存提供器标识 + /// + public const string HybridCache = "DefaultHybrid"; + /// + /// Redis总线提供器标识 + /// + public const string RedisBus = "DefaultRedis"; + /// + /// SystemTextJson序列化提供程序标识 + /// + public const string SystemTextJson = "SystemTextJson"; +} \ No newline at end of file diff --git a/src/Util.Caching.EasyCaching/CachingOptions.cs b/src/Util.Caching.EasyCaching/CachingOptions.cs new file mode 100644 index 000000000..d5384cc2a --- /dev/null +++ b/src/Util.Caching.EasyCaching/CachingOptions.cs @@ -0,0 +1,51 @@ +namespace Util.Caching.EasyCaching; + +/// +/// EasyCaching配置 +/// +internal class CachingOptions { + /// + /// Redis服务端点列表 + /// + private static List _redisEndPoints = new(); + + /// + /// 添加Redis服务端点 + /// + /// 配置 + /// 配置节名称 + public static void AddRedisEndPoints( IConfiguration configuration, string section ) { + var config = configuration.GetSection( $"{section}:DbConfig:Endpoints" ); + var endpoints = config.GetChildren(); + foreach( var endpoint in endpoints ) { + var host = endpoint["Host"]; + var port = endpoint["Port"].ToIntOrNull() ?? 6379; + _redisEndPoints.Add( new ServerEndPoint( host, port ) ); + } + } + + /// + /// 添加Redis服务端点 + /// + /// 配置操作 + public static void AddRedisEndPoints( Action setupAction ) { + setupAction.CheckNull( nameof( setupAction ) ); + var options = new RedisOptions(); + setupAction( options ); + _redisEndPoints.AddRange( options.DBConfig.Endpoints ); + } + + /// + /// 获取Redis服务端点列表 + /// + public static List GetRedisEndPoints() { + return _redisEndPoints; + } + + /// + /// 清理 + /// + public static void Clear() { + _redisEndPoints = null; + } +} \ No newline at end of file diff --git a/src/Util.Caching.EasyCaching/MemoryCacheManager.cs b/src/Util.Caching.EasyCaching/MemoryCacheManager.cs new file mode 100644 index 000000000..bc0f20152 --- /dev/null +++ b/src/Util.Caching.EasyCaching/MemoryCacheManager.cs @@ -0,0 +1,13 @@ +namespace Util.Caching.EasyCaching; + +/// +/// EasyCaching内存缓存服务 +/// +public class MemoryCacheManager : CacheManager, ILocalCache { + /// + /// 初始化EasyCaching内存缓存服务 + /// + /// EasyCaching缓存提供器工厂 + public MemoryCacheManager( IEasyCachingProviderFactory factory ) : base( factory?.GetCachingProvider( CacheProviderKey.MemoryCache ) ) { + } +} \ No newline at end of file diff --git a/src/Util.Caching.EasyCaching/RedisCacheManager.cs b/src/Util.Caching.EasyCaching/RedisCacheManager.cs new file mode 100644 index 000000000..9ad9354a5 --- /dev/null +++ b/src/Util.Caching.EasyCaching/RedisCacheManager.cs @@ -0,0 +1,21 @@ +namespace Util.Caching.EasyCaching; + +/// +/// EasyCaching Redis缓存服务 +/// +public class RedisCacheManager : CacheManager, IRedisCache { + /// + /// Redis缓存提供器 + /// + private readonly IRedisCachingProvider _provider; + + /// + /// 初始化EasyCaching Redis缓存服务 + /// + /// EasyCaching缓存提供器工厂 + public RedisCacheManager( IEasyCachingProviderFactory factory ) : base( factory?.GetCachingProvider( CacheProviderKey.RedisCache ) ) { + if ( factory == null ) + throw new ArgumentNullException( nameof(factory) ); + _provider = factory.GetRedisProvider( CacheProviderKey.RedisCache ); + } +} \ No newline at end of file diff --git a/src/Util.Caching.EasyCaching/Usings.cs b/src/Util.Caching.EasyCaching/Usings.cs new file mode 100644 index 000000000..bc5949e4c --- /dev/null +++ b/src/Util.Caching.EasyCaching/Usings.cs @@ -0,0 +1,19 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using System.Reflection; +global using System.Text.Json.Serialization; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using EasyCaching.Core; +global using EasyCaching.Core.Interceptor; +global using EasyCaching.InMemory; +global using EasyCaching.Redis; +global using EasyCaching.HybridCache; +global using EasyCaching.Bus.Redis; +global using EasyCaching.Serialization.SystemTextJson.Configurations; +global using EasyCaching.Core.Configurations; +global using Util.Configs; \ No newline at end of file diff --git a/src/Util.Caching/01-Util.Caching.csproj b/src/Util.Caching/01-Util.Caching.csproj new file mode 100644 index 000000000..3774f25ab --- /dev/null +++ b/src/Util.Caching/01-Util.Caching.csproj @@ -0,0 +1,37 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Caching + Util.Caching + Util.Caching是Util应用框架缓存操作基础类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Caching.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Caching.xml + + + + + True + False + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Util.Caching/CacheAttribute.cs b/src/Util.Caching/CacheAttribute.cs new file mode 100644 index 000000000..a3b9a0389 --- /dev/null +++ b/src/Util.Caching/CacheAttribute.cs @@ -0,0 +1,96 @@ +using Util.Aop; + +namespace Util.Caching; + +/// +/// 缓存拦截器 +/// +public class CacheAttribute : InterceptorBase { + /// + /// 缓存键前缀,可使用占位符, {0} 表示第一个参数值,范例: User-{0} + /// + public string Prefix { get; set; } + /// + /// 缓存过期间隔,单位:秒,默认值:36000 + /// + public int Expiration { get; set; } = 36000; + + /// + /// 执行 + /// + public override async Task Invoke( AspectContext context, AspectDelegate next ) { + var cache = GetCache( context ); + var returnType = GetReturnType( context ); + var key = CreateCacheKey( context ); + var value = await GetCacheValue( cache, returnType, key ); + if( value != null ) { + SetReturnValue( context, returnType, value ); + return; + } + await next( context ); + await SetCache( context, cache, key ); + } + + /// + /// 获取缓存服务 + /// + protected virtual ICache GetCache( AspectContext context ) { + return context.ServiceProvider.GetService(); + } + + /// + /// 获取返回类型 + /// + private Type GetReturnType( AspectContext context ) { + return context.IsAsync() ? context.ServiceMethod.ReturnType.GetGenericArguments().First() : context.ServiceMethod.ReturnType; + } + + /// + /// 创建缓存键 + /// + private string CreateCacheKey( AspectContext context ) { + var keyGenerator = context.ServiceProvider.GetService(); + return keyGenerator.CreateCacheKey( context.ServiceMethod, context.Parameters, GetPrefix( context ) ); + } + + /// + /// 获取缓存键前缀 + /// + private string GetPrefix( AspectContext context ) { + try { + return string.Format( Prefix, context.Parameters.ToArray() ); + } + catch { + return Prefix; + } + } + + /// + /// 获取缓存值 + /// + private async Task GetCacheValue( ICache cache, Type returnType, string key ) { + return await cache.GetAsync( key, returnType ); + } + + /// + /// 设置返回值 + /// + private void SetReturnValue( AspectContext context, Type returnType, object value ) { + if( context.IsAsync() ) { + context.ReturnValue = typeof( Task ).GetMethods() + .First( p => p.Name == "FromResult" && p.ContainsGenericParameters ) + .MakeGenericMethod( returnType ).Invoke( null, new[] { value } ); + return; + } + context.ReturnValue = value; + } + + /// + /// 设置缓存 + /// + private async Task SetCache( AspectContext context, ICache cache, string key ) { + var options = new CacheOptions { Expiration = TimeSpan.FromSeconds( Expiration ) }; + var returnValue = context.IsAsync() ? await context.UnwrapAsyncReturnValue() : context.ReturnValue; + await cache.SetAsync( key, returnValue, options ); + } +} \ No newline at end of file diff --git a/src/Util.Caching/CacheKey.cs b/src/Util.Caching/CacheKey.cs new file mode 100644 index 000000000..301af5f00 --- /dev/null +++ b/src/Util.Caching/CacheKey.cs @@ -0,0 +1,46 @@ +namespace Util.Caching; + +/// +/// 缓存键 +/// +public class CacheKey { + /// + /// 缓存键 + /// + private string _key; + + /// + /// 初始化缓存键 + /// + public CacheKey() { + } + + /// + /// 初始化缓存键 + /// + /// 缓存键 + /// 缓存键参数 + public CacheKey( string key,params object[] parameters) { + _key = string.Format( key, parameters ); + } + + /// + /// 缓存键 + /// + public string Key { + get => ToString(); + set => _key = value; + } + + /// + /// 缓存键前缀 + /// + public string Prefix { get; set; } + + /// + /// 获取缓存键 + /// + public override string ToString() { + return $"{Prefix}{_key}"; + } +} \ No newline at end of file diff --git a/src/Util.Caching/CacheKeyExtensions.cs b/src/Util.Caching/CacheKeyExtensions.cs new file mode 100644 index 000000000..e5ccf7f2e --- /dev/null +++ b/src/Util.Caching/CacheKeyExtensions.cs @@ -0,0 +1,15 @@ +namespace Util.Caching; + +/// +/// 缓存键扩展 +/// +public static class CacheKeyExtensions { + /// + /// 验证缓存键 + /// + /// 缓存键 + public static void Validate( this CacheKey cacheKey ) { + cacheKey.CheckNull( nameof( cacheKey ) ); + cacheKey.Key.CheckNull( nameof( cacheKey.Key ) ); + } +} \ No newline at end of file diff --git a/src/Util.Caching/CacheOptions.cs b/src/Util.Caching/CacheOptions.cs new file mode 100644 index 000000000..05910c742 --- /dev/null +++ b/src/Util.Caching/CacheOptions.cs @@ -0,0 +1,11 @@ +namespace Util.Caching; + +/// +/// 缓存配置 +/// +public class CacheOptions { + /// + /// 过期时间间隔,默认值: 8小时 + /// + public TimeSpan? Expiration { get; set; } +} \ No newline at end of file diff --git a/src/Util.Caching/ICache.cs b/src/Util.Caching/ICache.cs new file mode 100644 index 000000000..f1151d57a --- /dev/null +++ b/src/Util.Caching/ICache.cs @@ -0,0 +1,308 @@ +namespace Util.Caching; + +/// +/// 缓存 +/// +public interface ICache { + /// + /// 缓存是否已存在 + /// + /// 缓存键 + bool Exists( CacheKey key ); + /// + /// 缓存是否已存在 + /// + /// 缓存键 + bool Exists( string key ); + /// + /// 缓存是否已存在 + /// + /// 缓存键 + /// 取消令牌 + Task ExistsAsync( CacheKey key, CancellationToken cancellationToken = default ); + /// + /// 缓存是否已存在 + /// + /// 缓存键 + /// 取消令牌 + Task ExistsAsync( string key, CancellationToken cancellationToken = default ); + /// + /// 从缓存中获取数据 + /// + /// 缓存数据类型 + /// 缓存键 + T Get( CacheKey key ); + /// + /// 从缓存中获取数据 + /// + /// 缓存数据类型 + /// 缓存键 + T Get( string key ); + /// + /// 从缓存中获取数据 + /// + /// 缓存数据类型 + /// 缓存键集合 + List Get( IEnumerable keys ); + /// + /// 从缓存中获取数据 + /// + /// 缓存数据类型 + /// 缓存键集合 + List Get( IEnumerable keys ); + /// + /// 从缓存中获取数据,如果不存在,则执行获取数据操作并添加到缓存中 + /// + /// 缓存数据类型 + /// 缓存键 + /// 获取数据操作 + /// 缓存配置 + T Get( CacheKey key, Func action, CacheOptions options = null ); + /// + /// 从缓存中获取数据,如果不存在,则执行获取数据操作并添加到缓存中 + /// + /// 缓存数据类型 + /// 缓存键 + /// 获取数据操作 + /// 缓存配置 + T Get( string key, Func action, CacheOptions options = null ); + /// + /// 从缓存中获取数据 + /// + /// 缓存键 + /// 缓存数据类型 + /// 取消令牌 + Task GetAsync( string key, Type type, CancellationToken cancellationToken = default ); + /// + /// 从缓存中获取数据 + /// + /// 缓存数据类型 + /// 缓存键 + /// 取消令牌 + Task GetAsync( CacheKey key, CancellationToken cancellationToken = default ); + /// + /// 从缓存中获取数据 + /// + /// 缓存数据类型 + /// 缓存键 + /// 取消令牌 + Task GetAsync( string key, CancellationToken cancellationToken = default ); + /// + /// 从缓存中获取数据 + /// + /// 缓存数据类型 + /// 缓存键集合 + /// 取消令牌 + Task> GetAsync( IEnumerable keys, CancellationToken cancellationToken = default ); + /// + /// 从缓存中获取数据 + /// + /// 缓存数据类型 + /// 缓存键集合 + /// 取消令牌 + Task> GetAsync( IEnumerable keys, CancellationToken cancellationToken = default ); + /// + /// 从缓存中获取数据,如果不存在,则执行获取数据操作并添加到缓存中 + /// + /// 缓存数据类型 + /// 缓存键 + /// 获取数据操作 + /// 缓存配置 + /// 取消令牌 + Task GetAsync( CacheKey key, Func> action, CacheOptions options = null, CancellationToken cancellationToken = default ); + /// + /// 从缓存中获取数据,如果不存在,则执行获取数据操作并添加到缓存中 + /// + /// 缓存数据类型 + /// 缓存键 + /// 获取数据操作 + /// 缓存配置 + /// 取消令牌 + Task GetAsync( string key, Func> action, CacheOptions options = null, CancellationToken cancellationToken = default ); + /// + /// 通过缓存键前缀获取数据 + /// + /// 缓存数据类型 + /// 缓存键前缀 + List GetByPrefix( string prefix ); + /// + /// 通过缓存键前缀获取数据 + /// + /// 缓存数据类型 + /// 缓存键前缀 + /// 取消令牌 + Task> GetByPrefixAsync( string prefix, CancellationToken cancellationToken = default ); + /// + /// 设置缓存,当缓存已存在则忽略,设置成功返回true + /// + /// 缓存数据类型 + /// 缓存键 + /// 值 + /// 缓存配置 + bool TrySet( CacheKey key, T value, CacheOptions options = null ); + /// + /// 设置缓存,当缓存已存在则忽略,设置成功返回true + /// + /// 缓存数据类型 + /// 缓存键 + /// 值 + /// 缓存配置 + bool TrySet( string key, T value, CacheOptions options = null ); + /// + /// 设置缓存,当缓存已存在则忽略,设置成功返回true + /// + /// 缓存数据类型 + /// 缓存键 + /// 值 + /// 缓存配置 + /// 取消令牌 + Task TrySetAsync( CacheKey key, T value, CacheOptions options = null, CancellationToken cancellationToken = default ); + /// + /// 设置缓存,当缓存已存在则忽略,设置成功返回true + /// + /// 缓存数据类型 + /// 缓存键 + /// 值 + /// 缓存配置 + /// 取消令牌 + Task TrySetAsync( string key, T value, CacheOptions options = null, CancellationToken cancellationToken = default ); + /// + /// 设置缓存,当缓存已存在则覆盖 + /// + /// 缓存数据类型 + /// 缓存键 + /// 值 + /// 缓存配置 + void Set( CacheKey key, T value, CacheOptions options = null ); + /// + /// 设置缓存,当缓存已存在则覆盖 + /// + /// 缓存数据类型 + /// 缓存键 + /// 值 + /// 缓存配置 + void Set( string key, T value, CacheOptions options = null ); + /// + /// 设置缓存集合 + /// + /// 缓存数据类型 + /// 缓存项集合 + /// 缓存配置 + void Set( IDictionary items, CacheOptions options = null ); + /// + /// 设置缓存集合 + /// + /// 缓存数据类型 + /// 缓存项集合 + /// 缓存配置 + void Set( IDictionary items, CacheOptions options = null ); + /// + /// 设置缓存,当缓存已存在则覆盖 + /// + /// 缓存数据类型 + /// 缓存键 + /// 值 + /// 缓存配置 + /// 取消令牌 + Task SetAsync( CacheKey key, T value, CacheOptions options = null, CancellationToken cancellationToken = default ); + /// + /// 设置缓存,当缓存已存在则覆盖 + /// + /// 缓存数据类型 + /// 缓存键 + /// 值 + /// 缓存配置 + /// 取消令牌 + Task SetAsync( string key, T value, CacheOptions options = null, CancellationToken cancellationToken = default ); + /// + /// 设置缓存集合 + /// + /// 缓存数据类型 + /// 缓存项集合 + /// 缓存配置 + /// 取消令牌 + Task SetAsync( IDictionary items, CacheOptions options = null, CancellationToken cancellationToken = default ); + /// + /// 设置缓存集合 + /// + /// 缓存数据类型 + /// 缓存项集合 + /// 缓存配置 + /// 取消令牌 + Task SetAsync( IDictionary items, CacheOptions options = null, CancellationToken cancellationToken = default ); + /// + /// 移除缓存 + /// + /// 缓存键 + void Remove( CacheKey key ); + /// + /// 移除缓存 + /// + /// 缓存键 + void Remove( string key ); + /// + /// 移除缓存集合 + /// + /// 缓存键集合 + void Remove( IEnumerable keys ); + /// + /// 移除缓存集合 + /// + /// 缓存键集合 + void Remove( IEnumerable keys ); + /// + /// 移除缓存 + /// + /// 缓存键 + /// 取消令牌 + Task RemoveAsync( CacheKey key, CancellationToken cancellationToken = default ); + /// + /// 移除缓存 + /// + /// 缓存键 + /// 取消令牌 + Task RemoveAsync( string key, CancellationToken cancellationToken = default ); + /// + /// 移除缓存集合 + /// + /// 缓存键集合 + /// 取消令牌 + Task RemoveAsync( IEnumerable keys, CancellationToken cancellationToken = default ); + /// + /// 移除缓存集合 + /// + /// 缓存键集合 + /// 取消令牌 + Task RemoveAsync( IEnumerable keys, CancellationToken cancellationToken = default ); + /// + /// 通过缓存键前缀移除缓存 + /// + /// 缓存键前缀 + void RemoveByPrefix( string prefix ); + /// + /// 通过缓存键前缀移除缓存 + /// + /// 缓存键前缀 + /// 取消令牌 + Task RemoveByPrefixAsync( string prefix, CancellationToken cancellationToken = default ); + /// + /// 通过缓存键模式移除缓存 + /// + /// 缓存键模式,范例: test* + void RemoveByPattern( string pattern ); + /// + /// 通过缓存键模式移除缓存 + /// + /// 缓存键模式,范例: test* + /// 取消令牌 + Task RemoveByPatternAsync( string pattern, CancellationToken cancellationToken = default ); + /// + /// 清空缓存 + /// + void Clear(); + /// + /// 清空缓存 + /// + /// 取消令牌 + Task ClearAsync( CancellationToken cancellationToken = default ); +} \ No newline at end of file diff --git a/src/Util.Caching/ICacheKeyGenerator.cs b/src/Util.Caching/ICacheKeyGenerator.cs new file mode 100644 index 000000000..f1f4559d5 --- /dev/null +++ b/src/Util.Caching/ICacheKeyGenerator.cs @@ -0,0 +1,14 @@ +namespace Util.Caching; + +/// +/// 缓存键生成器 +/// +public interface ICacheKeyGenerator { + /// + /// 创建缓存键 + /// + /// 方法 + /// 参数 + /// 缓存键前缀 + string CreateCacheKey( MethodInfo methodInfo, object[] args, string prefix ); +} \ No newline at end of file diff --git a/src/Util.Caching/ILocalCache.cs b/src/Util.Caching/ILocalCache.cs new file mode 100644 index 000000000..5237b6c88 --- /dev/null +++ b/src/Util.Caching/ILocalCache.cs @@ -0,0 +1,7 @@ +namespace Util.Caching; + +/// +/// 本地缓存 +/// +public interface ILocalCache : ICache { +} \ No newline at end of file diff --git a/src/Util.Caching/IRedisCache.cs b/src/Util.Caching/IRedisCache.cs new file mode 100644 index 000000000..fcda8a523 --- /dev/null +++ b/src/Util.Caching/IRedisCache.cs @@ -0,0 +1,7 @@ +namespace Util.Caching; + +/// +/// Redis缓存 +/// +public interface IRedisCache : ICache { +} \ No newline at end of file diff --git a/src/Util.Caching/LocalCacheAttribute.cs b/src/Util.Caching/LocalCacheAttribute.cs new file mode 100644 index 000000000..9c049df3c --- /dev/null +++ b/src/Util.Caching/LocalCacheAttribute.cs @@ -0,0 +1,13 @@ +namespace Util.Caching; + +/// +/// 本地缓存拦截器 +/// +public class LocalCacheAttribute : CacheAttribute { + /// + /// 获取缓存服务 + /// + protected override ICache GetCache( AspectContext context ) { + return context.ServiceProvider.GetService(); + } +} \ No newline at end of file diff --git a/src/Util.Caching/NullCache.cs b/src/Util.Caching/NullCache.cs new file mode 100644 index 000000000..0d65a2f44 --- /dev/null +++ b/src/Util.Caching/NullCache.cs @@ -0,0 +1,230 @@ +namespace Util.Caching; + +/// +/// 空缓存 +/// +public class NullCache : ILocalCache { + /// + /// 缓存空实例 + /// + public static readonly ILocalCache Instance = new NullCache(); + + /// + public bool Exists( CacheKey key ) { + return false; + } + + /// + public bool Exists( string key ) { + return false; + } + + /// + public Task ExistsAsync( CacheKey key, CancellationToken cancellationToken = default ) { + return Task.FromResult( false ); + } + + /// + public Task ExistsAsync( string key, CancellationToken cancellationToken = default ) { + return Task.FromResult( false ); + } + + /// + public T Get( CacheKey key ) { + return default; + } + + /// + public T Get( string key ) { + return default; + } + + /// + public List Get( IEnumerable keys ) { + return new List(); + } + + /// + public List Get( IEnumerable keys ) { + return new List(); + } + + /// + public T Get( CacheKey key, Func action, CacheOptions options = null ) { + return action == null ? default : action(); + } + + /// + public T Get( string key, Func action, CacheOptions options = null ) { + return action == null ? default : action(); + } + + /// + public Task GetAsync( string key, Type type, CancellationToken cancellationToken = default ) { + return null; + } + + /// + public async Task GetAsync( CacheKey key, CancellationToken cancellationToken = default ) { + await Task.CompletedTask; + return default; + } + + /// + public async Task GetAsync( string key, CancellationToken cancellationToken = default ) { + await Task.CompletedTask; + return default; + } + + /// + public async Task> GetAsync( IEnumerable keys, CancellationToken cancellationToken = default ) { + await Task.CompletedTask; + return new List(); + } + + /// + public async Task> GetAsync( IEnumerable keys, CancellationToken cancellationToken = default ) { + await Task.CompletedTask; + return new List(); + } + + /// + public async Task GetAsync( CacheKey key, Func> action, CacheOptions options = null, CancellationToken cancellationToken = default ) { + return action == null ? default : await action(); + } + + /// + public async Task GetAsync( string key, Func> action, CacheOptions options = null, CancellationToken cancellationToken = default ) { + return action == null ? default : await action(); + } + + /// + public List GetByPrefix( string prefix ) { + return new List(); + } + + /// + public async Task> GetByPrefixAsync( string prefix, CancellationToken cancellationToken = default ) { + await Task.CompletedTask; + return new List(); + } + + /// + public bool TrySet( CacheKey key, T value, CacheOptions options = null ) { + return false; + } + + /// + public bool TrySet( string key, T value, CacheOptions options = null ) { + return false; + } + + /// + public Task TrySetAsync( CacheKey key, T value, CacheOptions options = null, CancellationToken cancellationToken = default ) { + return Task.FromResult( false ); + } + + /// + public Task TrySetAsync( string key, T value, CacheOptions options = null, CancellationToken cancellationToken = default ) { + return Task.FromResult( false ); + } + + /// + public void Set( CacheKey key, T value, CacheOptions options = null ) { + } + + /// + public void Set( string key, T value, CacheOptions options = null ) { + } + + /// + public void Set( IDictionary items, CacheOptions options = null ) { + } + + /// + public void Set( IDictionary items, CacheOptions options = null ) { + } + + /// + public Task SetAsync( CacheKey key, T value, CacheOptions options = null, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } + + /// + public Task SetAsync( string key, T value, CacheOptions options = null, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } + + /// + public Task SetAsync( IDictionary items, CacheOptions options = null, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } + + /// + public Task SetAsync( IDictionary items, CacheOptions options = null, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } + + /// + public void Remove( CacheKey key ) { + } + + /// + public void Remove( string key ) { + } + + /// + public void Remove( IEnumerable keys ) { + } + + /// + public void Remove( IEnumerable keys ) { + } + + /// + public Task RemoveAsync( CacheKey key, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } + + /// + public Task RemoveAsync( string key, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } + + /// + public Task RemoveAsync( IEnumerable keys, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } + + /// + public Task RemoveAsync( IEnumerable keys, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } + + /// + public void RemoveByPrefix( string prefix ) { + } + + /// + public Task RemoveByPrefixAsync( string prefix, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } + + /// + public void RemoveByPattern( string pattern ) { + } + + /// + public Task RemoveByPatternAsync( string pattern, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } + + /// + public void Clear() { + } + + /// + public Task ClearAsync( CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/Util.Caching/RedisCacheAttribute.cs b/src/Util.Caching/RedisCacheAttribute.cs new file mode 100644 index 000000000..639158090 --- /dev/null +++ b/src/Util.Caching/RedisCacheAttribute.cs @@ -0,0 +1,13 @@ +namespace Util.Caching; + +/// +/// Redis缓存拦截器 +/// +public class RedisCacheAttribute : CacheAttribute { + /// + /// 获取缓存服务 + /// + protected override ICache GetCache( AspectContext context ) { + return context.ServiceProvider.GetService(); + } +} \ No newline at end of file diff --git a/src/Util.Caching/Usings.cs b/src/Util.Caching/Usings.cs new file mode 100644 index 000000000..4c6502951 --- /dev/null +++ b/src/Util.Caching/Usings.cs @@ -0,0 +1,8 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using System.Reflection; +global using Microsoft.Extensions.DependencyInjection; +global using AspectCore.DynamicProxy; \ No newline at end of file diff --git a/src/Util.Core/01-Util.Core.csproj b/src/Util.Core/01-Util.Core.csproj new file mode 100644 index 000000000..53355a58e --- /dev/null +++ b/src/Util.Core/01-Util.Core.csproj @@ -0,0 +1,56 @@ + + + + $(NetTargetFramework) + Util + icon.jpg + Util.Core是Util应用框架的基础类库 + Util.Core + + + + .\obj\Debug\$(NetTargetFramework)\Util.Core.xml + + + + + + .\obj\Release\$(NetTargetFramework)\Util.Core.xml + + + + + True + False + + + + + + + + + + + + + + + + + + + True + True + R.resx + + + + + + PublicResXFileCodeGenerator + R.Designer.cs + + + + diff --git a/src/Util.Core/Applications/StateCode.cs b/src/Util.Core/Applications/StateCode.cs new file mode 100644 index 000000000..f12ec569b --- /dev/null +++ b/src/Util.Core/Applications/StateCode.cs @@ -0,0 +1,19 @@ +namespace Util.Applications; + +/// +/// 业务状态码 +/// +public static class StateCode { + /// + /// 失败 + /// + public const string Fail = "0"; + /// + /// 成功 + /// + public const string Ok = "1"; + /// + /// 未授权 + /// + public const string Unauthorized = "2"; +} \ No newline at end of file diff --git a/src/Util.Core/CommonExtensions.cs b/src/Util.Core/CommonExtensions.cs new file mode 100644 index 000000000..4f023fdb9 --- /dev/null +++ b/src/Util.Core/CommonExtensions.cs @@ -0,0 +1,56 @@ +namespace Util; + +/// +/// 公共扩展 +/// +public static class CommonExtensions { + /// + /// 安全获取值,当值为null时,不会抛出异常 + /// + /// 可空值 + public static T SafeValue( this T? value ) where T : struct { + return value ?? default; + } + + /// + /// 获取枚举值 + /// + /// 枚举实例 + public static int? Value( this System.Enum instance ) { + if( instance == null ) + return null; + return Util.Helpers.Enum.GetValue( instance.GetType(), instance ); + } + + /// + /// 获取枚举值 + /// + /// 返回值类型 + /// 枚举实例 + public static TResult Value( this System.Enum instance ) { + if( instance == null ) + return default; + return Util.Helpers.Convert.To( Value( instance ) ); + } + + /// + /// 获取枚举描述,使用System.ComponentModel.Description特性设置描述 + /// + /// 枚举实例 + public static string Description( this System.Enum instance ) { + if ( instance == null ) + return string.Empty; + return Util.Helpers.Enum.GetDescription( instance.GetType(), instance ); + } + + /// + /// 转换为用分隔符连接的字符串 + /// + /// 集合元素类型 + /// 集合 + /// 引号,默认不带引号,范例:单引号 "'" + /// 分隔符,默认使用逗号分隔 + public static string Join( this IEnumerable list, string quotes = "", string separator = "," ) { + return Util.Helpers.String.Join( list, quotes, separator ); + } +} \ No newline at end of file diff --git a/src/Util.Core/Configs/AppBuilder.cs b/src/Util.Core/Configs/AppBuilder.cs new file mode 100644 index 000000000..1de3d692c --- /dev/null +++ b/src/Util.Core/Configs/AppBuilder.cs @@ -0,0 +1,28 @@ +using Util.Helpers; + +namespace Util.Configs; + +/// +/// 应用生成器 +/// +public class AppBuilder : IAppBuilder { + /// + /// 初始化应用生成器 + /// + /// 主机生成器 + public AppBuilder( IHostBuilder host ) { + Host = host ?? throw new ArgumentNullException( nameof( host ) ); + } + + /// + public IHostBuilder Host { get; } + + /// + /// 构建 + /// + public IHost Build() { + var result = Host.Build(); + Ioc.SetServiceProviderAction( () => result.Services ); + return result; + } +} \ No newline at end of file diff --git a/src/Util.Core/Configs/IAppBuilder.cs b/src/Util.Core/Configs/IAppBuilder.cs new file mode 100644 index 000000000..b4d774389 --- /dev/null +++ b/src/Util.Core/Configs/IAppBuilder.cs @@ -0,0 +1,15 @@ +namespace Util.Configs; + +/// +/// 应用生成器 +/// +public interface IAppBuilder { + /// + /// 主机生成器 + /// + public IHostBuilder Host { get; } + /// + /// 构建 + /// + public IHost Build(); +} \ No newline at end of file diff --git a/src/Util.Core/ConvertExtensions.cs b/src/Util.Core/ConvertExtensions.cs new file mode 100644 index 000000000..11f9d783f --- /dev/null +++ b/src/Util.Core/ConvertExtensions.cs @@ -0,0 +1,144 @@ +namespace Util; + +/// +/// 类型转换扩展 +/// +public static class ConvertExtensions { + /// + /// 安全转换为字符串,去除两端空格,当值为null时返回"" + /// + /// 输入值 + public static string SafeString( this object input ) { + return input?.ToString()?.Trim() ?? string.Empty; + } + + /// + /// 转换为bool + /// + /// 数据 + public static bool ToBool( this string obj ) { + return Util.Helpers.Convert.ToBool( obj ); + } + + /// + /// 转换为可空bool + /// + /// 数据 + public static bool? ToBoolOrNull( this string obj ) { + return Util.Helpers.Convert.ToBoolOrNull( obj ); + } + + /// + /// 转换为int + /// + /// 数据 + public static int ToInt( this string obj ) { + return Util.Helpers.Convert.ToInt( obj ); + } + + /// + /// 转换为可空int + /// + /// 数据 + public static int? ToIntOrNull( this string obj ) { + return Util.Helpers.Convert.ToIntOrNull( obj ); + } + + /// + /// 转换为long + /// + /// 数据 + public static long ToLong( this string obj ) { + return Util.Helpers.Convert.ToLong( obj ); + } + + /// + /// 转换为可空long + /// + /// 数据 + public static long? ToLongOrNull( this string obj ) { + return Util.Helpers.Convert.ToLongOrNull( obj ); + } + + /// + /// 转换为double + /// + /// 数据 + public static double ToDouble( this string obj ) { + return Util.Helpers.Convert.ToDouble( obj ); + } + + /// + /// 转换为可空double + /// + /// 数据 + public static double? ToDoubleOrNull( this string obj ) { + return Util.Helpers.Convert.ToDoubleOrNull( obj ); + } + + /// + /// 转换为decimal + /// + /// 数据 + public static decimal ToDecimal( this string obj ) { + return Util.Helpers.Convert.ToDecimal( obj ); + } + + /// + /// 转换为可空decimal + /// + /// 数据 + public static decimal? ToDecimalOrNull( this string obj ) { + return Util.Helpers.Convert.ToDecimalOrNull( obj ); + } + + /// + /// 转换为日期 + /// + /// 数据 + public static DateTime ToDateTime( this string obj ) { + return Util.Helpers.Convert.ToDateTime( obj ); + } + + /// + /// 转换为可空日期 + /// + /// 数据 + public static DateTime? ToDateTimeOrNull( this string obj ) { + return Util.Helpers.Convert.ToDateTimeOrNull( obj ); + } + + /// + /// 转换为Guid + /// + /// 数据 + public static Guid ToGuid( this string obj ) { + return Util.Helpers.Convert.ToGuid( obj ); + } + + /// + /// 转换为可空Guid + /// + /// 数据 + public static Guid? ToGuidOrNull( this string obj ) { + return Util.Helpers.Convert.ToGuidOrNull( obj ); + } + + /// + /// 转换为Guid集合 + /// + /// 数据,范例: "83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A" + public static List ToGuidList( this string obj ) { + return Util.Helpers.Convert.ToGuidList( obj ); + } + + /// + /// 转换为Guid集合 + /// + /// 字符串集合 + public static List ToGuidList( this IList obj ) { + if( obj == null ) + return new List(); + return obj.Select( t => t.ToGuid() ).ToList(); + } +} \ No newline at end of file diff --git a/src/Util.Core/Data/IDataKey.cs b/src/Util.Core/Data/IDataKey.cs new file mode 100644 index 000000000..b7fecc73d --- /dev/null +++ b/src/Util.Core/Data/IDataKey.cs @@ -0,0 +1,11 @@ +namespace Util.Data; + +/// +/// 数据标识 +/// +public interface IDataKey { + /// + /// 标识 + /// + string Id { get; set; } +} \ No newline at end of file diff --git a/src/Util.Core/Data/Operator.cs b/src/Util.Core/Data/Operator.cs new file mode 100644 index 000000000..9c7bdb1a1 --- /dev/null +++ b/src/Util.Core/Data/Operator.cs @@ -0,0 +1,62 @@ +namespace Util.Data; + +/// +/// 查询操作符 +/// +public enum Operator { + /// + /// 等于 + /// + [Description( "等于" )] + Equal, + /// + /// 不等于 + /// + [Description( "不等于" )] + NotEqual, + /// + /// 大于 + /// + [Description( "大于" )] + Greater, + /// + /// 大于等于 + /// + [Description( "大于等于" )] + GreaterEqual, + /// + /// 小于 + /// + [Description( "小于" )] + Less, + /// + /// 小于等于 + /// + [Description( "小于等于" )] + LessEqual, + /// + /// 头匹配 + /// + [Description( "头匹配" )] + Starts, + /// + /// 尾匹配 + /// + [Description( "尾匹配" )] + Ends, + /// + /// 模糊匹配 + /// + [Description( "模糊匹配" )] + Contains, + /// + /// In + /// + [Description( "In" )] + In, + /// + /// Not In + /// + [Description( "Not In" )] + NotIn, +} \ No newline at end of file diff --git a/src/Util.Core/DateTimeExtensions.cs b/src/Util.Core/DateTimeExtensions.cs new file mode 100644 index 000000000..c55471d25 --- /dev/null +++ b/src/Util.Core/DateTimeExtensions.cs @@ -0,0 +1,172 @@ +using Util.Helpers; +using Util.Properties; + +namespace Util; + +/// +/// 日期扩展 +/// +public static class DateTimeExtensions { + /// + /// 获取格式化字符串,带时分秒,格式:"yyyy-MM-dd HH:mm:ss" + /// + /// 日期 + /// 是否移除秒 + public static string ToDateTimeString( this DateTime dateTime, bool removeSecond = false ) { + dateTime = GetLocalDateTime( dateTime ); + if ( removeSecond ) + return dateTime.ToString( "yyyy-MM-dd HH:mm" ); + return dateTime.ToString( "yyyy-MM-dd HH:mm:ss" ); + } + + /// + /// 获取本地日期 + /// + private static DateTime GetLocalDateTime( DateTime dateTime ) { + return Time.ToLocalTime( dateTime ); + } + + /// + /// 获取格式化字符串,带时分秒,格式:"yyyy-MM-dd HH:mm:ss" + /// + /// 日期 + /// 是否移除秒 + public static string ToDateTimeString( this DateTime? dateTime, bool removeSecond = false ) { + if( dateTime == null ) + return string.Empty; + return ToDateTimeString( dateTime.Value, removeSecond ); + } + + /// + /// 获取格式化字符串,不带时分秒,格式:"yyyy-MM-dd" + /// + /// 日期 + public static string ToDateString( this DateTime dateTime ) { + dateTime = GetLocalDateTime( dateTime ); + return dateTime.ToString( "yyyy-MM-dd" ); + } + + /// + /// 获取格式化字符串,不带时分秒,格式:"yyyy-MM-dd" + /// + /// 日期 + public static string ToDateString( this DateTime? dateTime ) { + if( dateTime == null ) + return string.Empty; + return ToDateString( dateTime.Value ); + } + + /// + /// 获取格式化字符串,不带年月日,格式:"HH:mm:ss" + /// + /// 日期 + public static string ToTimeString( this DateTime dateTime ) { + dateTime = GetLocalDateTime( dateTime ); + return dateTime.ToString( "HH:mm:ss" ); + } + + /// + /// 获取格式化字符串,不带年月日,格式:"HH:mm:ss" + /// + /// 日期 + public static string ToTimeString( this DateTime? dateTime ) { + if( dateTime == null ) + return string.Empty; + return ToTimeString( dateTime.Value ); + } + + /// + /// 获取格式化字符串,带毫秒,格式:"yyyy-MM-dd HH:mm:ss.fff" + /// + /// 日期 + public static string ToMillisecondString( this DateTime dateTime ) { + dateTime = GetLocalDateTime( dateTime ); + return dateTime.ToString( "yyyy-MM-dd HH:mm:ss.fff" ); + } + + /// + /// 获取格式化字符串,带毫秒,格式:"yyyy-MM-dd HH:mm:ss.fff" + /// + /// 日期 + public static string ToMillisecondString( this DateTime? dateTime ) { + if( dateTime == null ) + return string.Empty; + return ToMillisecondString( dateTime.Value ); + } + + /// + /// 获取中文格式化字符串,不带时分秒,格式:"yyyy年MM月dd日" + /// + /// 日期 + public static string ToChineseDateString( this DateTime dateTime ) { + dateTime = GetLocalDateTime( dateTime ); + return $"{dateTime.Year}年{dateTime.Month}月{dateTime.Day}日"; + } + + /// + /// 获取中文格式化字符串,不带时分秒,格式:"yyyy年MM月dd日" + /// + /// 日期 + public static string ToChineseDateString( this DateTime? dateTime ) { + if( dateTime == null ) + return string.Empty; + return ToChineseDateString( dateTime.SafeValue() ); + } + + /// + /// 获取中文格式化字符串,带时分秒,格式:"yyyy年MM月dd日 HH时mm分" + /// + /// 日期 + /// 是否移除秒 + public static string ToChineseDateTimeString( this DateTime dateTime, bool removeSecond = false ) { + dateTime = GetLocalDateTime( dateTime ); + StringBuilder result = new StringBuilder(); + result.AppendFormat( "{0}年{1}月{2}日", dateTime.Year, dateTime.Month, dateTime.Day ); + result.AppendFormat( " {0}时{1}分", dateTime.Hour, dateTime.Minute ); + if( removeSecond == false ) + result.AppendFormat( "{0}秒", dateTime.Second ); + return result.ToString(); + } + + /// + /// 获取中文格式化字符串,带时分秒,格式:"yyyy年MM月dd日 HH时mm分" + /// + /// 日期 + /// 是否移除秒 + public static string ToChineseDateTimeString( this DateTime? dateTime, bool removeSecond = false ) { + if( dateTime == null ) + return string.Empty; + return ToChineseDateTimeString( dateTime.Value, removeSecond ); + } + + /// + /// 获取描述 + /// + /// 时间间隔 + public static string Description( this TimeSpan timeSpan ) { + StringBuilder result = new StringBuilder(); + if ( timeSpan.Days > 0 ) { + result.Append( timeSpan.Days ); + result.Append( R.Days ); + } + if ( timeSpan.Hours > 0 ) { + result.Append( timeSpan.Hours ); + result.Append( R.Hours ); + } + if ( timeSpan.Minutes > 0 ) { + result.Append( timeSpan.Minutes ); + result.Append( R.Minutes ); + } + if ( timeSpan.Seconds > 0 ) { + result.Append( timeSpan.Seconds ); + result.Append( R.Seconds ); + } + if ( timeSpan.Milliseconds > 0 ) { + result.Append( timeSpan.Milliseconds ); + result.Append( R.Milliseconds ); + } + if ( result.Length > 0 ) + return result.ToString(); + return $"{timeSpan.TotalMilliseconds}{R.Milliseconds}"; + } +} \ No newline at end of file diff --git a/src/Util.Core/Dates/AppBuilderExtensions.cs b/src/Util.Core/Dates/AppBuilderExtensions.cs new file mode 100644 index 000000000..07de6cbd9 --- /dev/null +++ b/src/Util.Core/Dates/AppBuilderExtensions.cs @@ -0,0 +1,27 @@ +using Util.Configs; + +namespace Util.Dates; + +/// +/// 日期操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置使用Utc日期 + /// + /// 应用生成器 + public static IAppBuilder AddUtc( this IAppBuilder builder ) { + return builder.AddUtc( true ); + } + + /// + /// 配置使用Utc日期 + /// + /// 应用生成器 + /// 是否使用Utc日期 + public static IAppBuilder AddUtc( this IAppBuilder builder, bool isUseUtc ) { + builder.CheckNull( nameof( builder ) ); + TimeOptions.IsUseUtc = isUseUtc; + return builder; + } +} \ No newline at end of file diff --git a/src/Util.Core/Dates/TimeOptions.cs b/src/Util.Core/Dates/TimeOptions.cs new file mode 100644 index 000000000..c4b7394bc --- /dev/null +++ b/src/Util.Core/Dates/TimeOptions.cs @@ -0,0 +1,11 @@ +namespace Util.Dates; + +/// +/// 时间配置 +/// +public static class TimeOptions { + /// + /// 是否使用Utc日期 + /// + public static bool IsUseUtc { get; set; } = false; +} \ No newline at end of file diff --git a/src/Util.Core/Dependency/Container.cs b/src/Util.Core/Dependency/Container.cs new file mode 100644 index 000000000..7929ec116 --- /dev/null +++ b/src/Util.Core/Dependency/Container.cs @@ -0,0 +1,78 @@ +namespace Util.Dependency; + +/// +/// 容器 +/// +public class Container { + /// + /// 服务集合 + /// + private readonly ServiceCollection _services; + /// + /// 服务提供器 + /// + private IServiceProvider _provider; + + /// + /// 容器实例 + /// + public static readonly Container Instance = new(); + + /// + /// 初始化容器 + /// + public Container() { + _services = new ServiceCollection(); + } + + /// + /// 获取服务集合 + /// + public ServiceCollection GetServices() { + return _services; + } + + /// + /// 获取服务提供器 + /// + public IServiceProvider GetServiceProvider() { + return _provider ??= _services.BuildServiceProvider(); + } + + /// + /// 获取服务 + /// + /// 对象类型 + public T GetService() { + return GetService( typeof(T) ); + } + + /// + /// 获取服务 + /// + /// 返回对象类型 + /// 对象类型 + public T GetService( Type type ) { + var service = GetService( type ); + if ( service == null ) + return default; + return (T)service; + } + + /// + /// 获取服务 + /// + /// 对象类型 + public object GetService( Type type ) { + var provider = GetServiceProvider(); + return provider.GetService( type ); + } + + /// + /// 清理 + /// + public void Clear() { + _services.Clear(); + _provider = null; + } +} \ No newline at end of file diff --git a/src/Util.Core/Dependency/IScopeDependency.cs b/src/Util.Core/Dependency/IScopeDependency.cs new file mode 100644 index 000000000..a46baf041 --- /dev/null +++ b/src/Util.Core/Dependency/IScopeDependency.cs @@ -0,0 +1,7 @@ +namespace Util.Dependency; + +/// +/// 实现该接口将自动注册到Ioc容器,生命周期为每次请求创建一个实例 +/// +public interface IScopeDependency { +} \ No newline at end of file diff --git a/src/Util.Core/Dependency/ISingletonDependency.cs b/src/Util.Core/Dependency/ISingletonDependency.cs new file mode 100644 index 000000000..b6d705cad --- /dev/null +++ b/src/Util.Core/Dependency/ISingletonDependency.cs @@ -0,0 +1,7 @@ +namespace Util.Dependency; + +/// +/// 实现该接口将自动注册到Ioc容器,生命周期为单例 +/// +public interface ISingletonDependency { +} \ No newline at end of file diff --git a/src/Util.Core/Dependency/ITransientDependency.cs b/src/Util.Core/Dependency/ITransientDependency.cs new file mode 100644 index 000000000..5e6ad231e --- /dev/null +++ b/src/Util.Core/Dependency/ITransientDependency.cs @@ -0,0 +1,7 @@ +namespace Util.Dependency; + +/// +/// 实现该接口将自动注册到Ioc容器,生命周期为每次创建一个新实例 +/// +public interface ITransientDependency { +} \ No newline at end of file diff --git a/src/Util.Core/Dependency/IocAttribute.cs b/src/Util.Core/Dependency/IocAttribute.cs new file mode 100644 index 000000000..496bbd273 --- /dev/null +++ b/src/Util.Core/Dependency/IocAttribute.cs @@ -0,0 +1,20 @@ +namespace Util.Dependency; + +/// +/// 依赖关系配置,用于覆盖实现类 +/// +[AttributeUsage(AttributeTargets.Class)] +public class IocAttribute : Attribute { + /// + /// 初始化依赖关系配置 + /// + /// 优先级,值越大优先级越高 + public IocAttribute( int priority ) { + Priority = priority; + } + + /// + /// 优先级,值越大优先级越高 + /// + public int Priority { get; set; } +} \ No newline at end of file diff --git a/src/Util.Core/DictionaryExtensions.cs b/src/Util.Core/DictionaryExtensions.cs new file mode 100644 index 000000000..482449d41 --- /dev/null +++ b/src/Util.Core/DictionaryExtensions.cs @@ -0,0 +1,17 @@ +namespace Util; + +/// +/// 字典操作扩展 +/// +public static class DictionaryExtensions { + /// + /// 获取值 + /// + /// 字典数据 + /// 键 + public static TValue GetValue( this IDictionary source, TKey key ) { + if ( source == null ) + return default; + return source.TryGetValue( key, out var obj ) ? obj : default; + } +} \ No newline at end of file diff --git a/src/Util.Core/DisposeAction.cs b/src/Util.Core/DisposeAction.cs new file mode 100644 index 000000000..227b70f63 --- /dev/null +++ b/src/Util.Core/DisposeAction.cs @@ -0,0 +1,32 @@ +namespace Util; + +/// +/// 释放操作 +/// +public class DisposeAction : IDisposable { + /// + /// 操作 + /// + private readonly Action _action; + + /// + /// 初始化释放操作 + /// + /// 操作 + public DisposeAction( Action action ) { + action.CheckNull( nameof( action ) ); + _action = action; + } + + /// + /// 空释放操作 + /// + public static readonly IDisposable Null = new DisposeAction( null ); + + /// + /// 释放 + /// + public void Dispose() { + _action?.Invoke(); + } +} \ No newline at end of file diff --git a/src/Util.Core/Domain/IDelete.cs b/src/Util.Core/Domain/IDelete.cs new file mode 100644 index 000000000..35163c3e8 --- /dev/null +++ b/src/Util.Core/Domain/IDelete.cs @@ -0,0 +1,11 @@ +namespace Util.Domain; + +/// +/// 逻辑删除 +/// +public interface IDelete { + /// + /// 是否删除 + /// + bool IsDeleted { get; set; } +} \ No newline at end of file diff --git a/src/Util.Core/Domain/IKey.cs b/src/Util.Core/Domain/IKey.cs new file mode 100644 index 000000000..e00c2b57c --- /dev/null +++ b/src/Util.Core/Domain/IKey.cs @@ -0,0 +1,12 @@ +namespace Util.Domain; + +/// +/// 标识 +/// +/// 标识类型 +public interface IKey { + /// + /// 标识 + /// + TKey Id { get; } +} \ No newline at end of file diff --git a/src/Util.Core/Domain/IVersion.cs b/src/Util.Core/Domain/IVersion.cs new file mode 100644 index 000000000..56d0e69ce --- /dev/null +++ b/src/Util.Core/Domain/IVersion.cs @@ -0,0 +1,11 @@ +namespace Util.Domain; + +/// +/// 乐观锁 +/// +public interface IVersion { + /// + /// 版本号 + /// + byte[] Version { get; set; } +} \ No newline at end of file diff --git a/src/Util.Core/Exceptions/ConcurrencyException.cs b/src/Util.Core/Exceptions/ConcurrencyException.cs new file mode 100644 index 000000000..c32d35eb1 --- /dev/null +++ b/src/Util.Core/Exceptions/ConcurrencyException.cs @@ -0,0 +1,50 @@ +using Util.Properties; + +namespace Util.Exceptions; + +/// +/// 并发异常 +/// +public class ConcurrencyException : Warning { + /// + /// 消息 + /// + private readonly string _message; + + /// + /// 初始化并发异常 + /// + public ConcurrencyException() + : this( "" ) { + } + + /// + /// 初始化并发异常 + /// + /// 异常 + public ConcurrencyException( Exception exception ) + : this( "", exception ) { + } + + /// + /// 初始化并发异常 + /// + /// 错误消息 + /// 异常 + /// 错误码 + /// Http状态码 + public ConcurrencyException( string message, Exception exception = null, string code = null, int? httpStatusCode = null ) + : base( message, exception, code, httpStatusCode ) { + _message = message; + } + + /// + public override string Message => $"{R.ConcurrencyExceptionMessage}.{_message}"; + + /// + public override string GetMessage( bool isProduction = false ) { + if( isProduction ) + return R.ConcurrencyExceptionMessage; + return GetMessage(this); + } +} \ No newline at end of file diff --git a/src/Util.Core/Exceptions/Warning.cs b/src/Util.Core/Exceptions/Warning.cs new file mode 100644 index 000000000..258626c4b --- /dev/null +++ b/src/Util.Core/Exceptions/Warning.cs @@ -0,0 +1,97 @@ +namespace Util.Exceptions; + +/// +/// 应用程序异常 +/// +public class Warning : Exception { + /// + /// 初始化应用程序异常 + /// + /// 异常 + public Warning( Exception exception ) + : this( null, exception ) { + } + + /// + /// 初始化应用程序异常 + /// + /// 错误消息 + /// 异常 + /// 错误码 + /// Http状态码 + public Warning( string message, Exception exception = null, string code = null, int? httpStatusCode = null ) + : base( message ?? "", exception ) { + Code = code; + HttpStatusCode = httpStatusCode; + } + + /// + /// 错误码 + /// + public string Code { get; set; } + + /// + /// Http状态码 + /// + public int? HttpStatusCode { get; set; } + + /// + /// 是否本地化异常消息 + /// + public bool? IsLocalization { get; set; } + + /// + /// 获取错误消息 + /// + /// 是否生产环境 + public virtual string GetMessage( bool isProduction = false ) { + return GetMessage( this ); + } + + /// + /// 获取错误消息 + /// + public static string GetMessage( Exception ex ) { + var result = new StringBuilder(); + var list = GetExceptions( ex ); + foreach( var exception in list ) + AppendMessage( result, exception ); + return result.ToString().Trim( Environment.NewLine.ToCharArray() ); + } + + /// + /// 添加异常消息 + /// + private static void AppendMessage( StringBuilder result, Exception exception ) { + if( exception == null ) + return; + result.AppendLine( exception.Message ); + } + + /// + /// 获取异常列表 + /// + public IList GetExceptions() { + return GetExceptions( this ); + } + + /// + /// 获取异常列表 + /// + /// 异常 + public static IList GetExceptions( Exception ex ) { + var result = new List(); + AddException( result, ex ); + return result; + } + + /// + /// 添加内部异常 + /// + private static void AddException( List result, Exception exception ) { + if( exception == null ) + return; + result.Add( exception ); + AddException( result, exception.InnerException ); + } +} \ No newline at end of file diff --git a/src/Util.Core/ExpressionExtensions.cs b/src/Util.Core/ExpressionExtensions.cs new file mode 100644 index 000000000..20d86396f --- /dev/null +++ b/src/Util.Core/ExpressionExtensions.cs @@ -0,0 +1,448 @@ +using Util.Data; +using Util.Expressions; +using Util.Helpers; + +namespace Util; + +/// +/// 表达式扩展 +/// +public static class ExpressionExtensions { + + #region Property(属性表达式) + + /// + /// 创建属性表达式 + /// + /// 表达式 + /// 属性名,支持多级属性名,用句点分隔,范例:Customer.Name + public static Expression Property( this Expression expression, string propertyName ) { + if( propertyName.All( t => t != '.' ) ) + return Expression.Property( expression, propertyName ); + var propertyNameList = propertyName.Split( '.' ); + Expression result = null; + for( int i = 0; i < propertyNameList.Length; i++ ) { + if( i == 0 ) { + result = Expression.Property( expression, propertyNameList[0] ); + continue; + } + result = result.Property( propertyNameList[i] ); + } + return result; + } + + /// + /// 创建属性表达式 + /// + /// 表达式 + /// 属性 + public static Expression Property( this Expression expression, MemberInfo member ) { + return Expression.MakeMemberAccess( expression, member ); + } + + #endregion + + #region And(与表达式) + + /// + /// 与操作表达式 + /// + /// 左操作数 + /// 右操作数 + public static Expression And( this Expression left, Expression right ) { + if( left == null ) + return right; + if( right == null ) + return left; + return Expression.AndAlso( left, right ); + } + + /// + /// 与操作表达式 + /// + /// 对象类型 + /// 左操作数 + /// 右操作数 + public static Expression> And( this Expression> left, Expression> right ) { + if( left == null ) + return right; + if( right == null ) + return left; + return left.Compose( right, Expression.AndAlso ); + } + + #endregion + + #region Or(或表达式) + + /// + /// 或操作表达式 + /// + /// 左操作数 + /// 右操作数 + public static Expression Or( this Expression left, Expression right ) { + if( left == null ) + return right; + if( right == null ) + return left; + return Expression.OrElse( left, right ); + } + + /// + /// 或操作表达式 + /// + /// 对象类型 + /// 左操作数 + /// 右操作数 + public static Expression> Or( this Expression> left, Expression> right ) { + if( left == null ) + return right; + if( right == null ) + return left; + return left.Compose( right, Expression.OrElse ); + } + + #endregion + + #region Value(获取lambda表达式的值) + + /// + /// 获取lambda表达式的值 + /// + /// 对象类型 + public static object Value( this Expression> expression ) { + return Lambda.GetValue( expression ); + } + + #endregion + + #region Equal(等于表达式) + + /// + /// 创建等于运算表达式 + /// + /// 左操作数 + /// 右操作数 + public static Expression Equal( this Expression left, Expression right ) { + return Expression.Equal( left, right ); + } + + /// + /// 创建等于运算表达式 + /// + /// 左操作数 + /// 值 + public static Expression Equal( this Expression left, object value ) { + return left.Equal( Lambda.Constant( value, left ) ); + } + + #endregion + + #region NotEqual(不等于表达式) + + /// + /// 创建不等于运算表达式 + /// + /// 左操作数 + /// 右操作数 + public static Expression NotEqual( this Expression left, Expression right ) { + return Expression.NotEqual( left, right ); + } + + /// + /// 创建不等于运算表达式 + /// + /// 左操作数 + /// 值 + public static Expression NotEqual( this Expression left, object value ) { + return left.NotEqual( Lambda.Constant( value, left ) ); + } + + #endregion + + #region Greater(大于表达式) + + /// + /// 创建大于运算表达式 + /// + /// 左操作数 + /// 右操作数 + public static Expression Greater( this Expression left, Expression right ) { + return Expression.GreaterThan( left, right ); + } + + /// + /// 创建大于运算表达式 + /// + /// 左操作数 + /// 值 + public static Expression Greater( this Expression left, object value ) { + return left.Greater( Lambda.Constant( value, left ) ); + } + + #endregion + + #region GreaterEqual(大于等于表达式) + + /// + /// 创建大于等于运算表达式 + /// + /// 左操作数 + /// 右操作数 + public static Expression GreaterEqual( this Expression left, Expression right ) { + return Expression.GreaterThanOrEqual( left, right ); + } + + /// + /// 创建大于等于运算表达式 + /// + /// 左操作数 + /// 值 + public static Expression GreaterEqual( this Expression left, object value ) { + return left.GreaterEqual( Lambda.Constant( value, left ) ); + } + + #endregion + + #region Less(小于表达式) + + /// + /// 创建小于运算表达式 + /// + /// 左操作数 + /// 右操作数 + public static Expression Less( this Expression left, Expression right ) { + return Expression.LessThan( left, right ); + } + + /// + /// 创建小于运算表达式 + /// + /// 左操作数 + /// 值 + public static Expression Less( this Expression left, object value ) { + return left.Less( Lambda.Constant( value, left ) ); + } + + #endregion + + #region LessEqual(小于等于表达式) + + /// + /// 创建小于等于运算表达式 + /// + /// 左操作数 + /// 右操作数 + public static Expression LessEqual( this Expression left, Expression right ) { + return Expression.LessThanOrEqual( left, right ); + } + + /// + /// 创建小于等于运算表达式 + /// + /// 左操作数 + /// 值 + public static Expression LessEqual( this Expression left, object value ) { + return left.LessEqual( Lambda.Constant( value, left ) ); + } + + #endregion + + #region StartsWith(头匹配) + + /// + /// 头匹配 + /// + /// 左操作数 + /// 值 + public static Expression StartsWith( this Expression left, object value ) { + return left.Call( "StartsWith", new[] { typeof( string ) }, value ); + } + + #endregion + + #region EndsWith(尾匹配) + + /// + /// 尾匹配 + /// + /// 左操作数 + /// 值 + public static Expression EndsWith( this Expression left, object value ) { + return left.Call( "EndsWith", new[] { typeof( string ) }, value ); + } + + #endregion + + #region Contains(模糊匹配) + + /// + /// 模糊匹配 + /// + /// 左操作数 + /// 值 + public static Expression Contains( this Expression left, object value ) { + return left.Call( "Contains", new[] { typeof( string ) }, value ); + } + + #endregion + + #region Operation(操作) + + /// + /// 操作 + /// + /// 左操作数 + /// 运算符 + /// 值 + public static Expression Operation( this Expression left, Operator @operator, object value ) { + switch( @operator ) { + case Operator.Equal: + return left.Equal( value ); + case Operator.NotEqual: + return left.NotEqual( value ); + case Operator.Greater: + return left.Greater( value ); + case Operator.GreaterEqual: + return left.GreaterEqual( value ); + case Operator.Less: + return left.Less( value ); + case Operator.LessEqual: + return left.LessEqual( value ); + case Operator.Starts: + return left.StartsWith( value ); + case Operator.Ends: + return left.EndsWith( value ); + case Operator.Contains: + return left.Contains( value ); + } + throw new NotImplementedException(); + } + + /// + /// 操作 + /// + /// 左操作数 + /// 运算符 + /// 值 + public static Expression Operation( this Expression left, Operator @operator, Expression value ) { + switch( @operator ) { + case Operator.Equal: + return left.Equal( value ); + case Operator.NotEqual: + return left.NotEqual( value ); + case Operator.Greater: + return left.Greater( value ); + case Operator.GreaterEqual: + return left.GreaterEqual( value ); + case Operator.Less: + return left.Less( value ); + case Operator.LessEqual: + return left.LessEqual( value ); + } + throw new NotImplementedException(); + } + + #endregion + + #region Call(调用方法表达式) + + /// + /// 创建调用方法表达式 + /// + /// 调用的实例 + /// 方法名 + /// 参数值列表 + public static Expression Call( this Expression instance, string methodName, params Expression[] values ) { + if( instance == null ) + throw new ArgumentNullException( nameof( instance ) ); + var methodInfo = instance.Type.GetMethod( methodName ); + if( methodInfo == null ) + return null; + return Expression.Call( instance, methodInfo, values ); + } + + /// + /// 创建调用方法表达式 + /// + /// 调用的实例 + /// 方法名 + /// 参数值列表 + public static Expression Call( this Expression instance, string methodName, params object[] values ) { + if( instance == null ) + throw new ArgumentNullException( nameof( instance ) ); + var methodInfo = instance.Type.GetMethod( methodName ); + if( methodInfo == null ) + return null; + if( values == null || values.Length == 0 ) + return Expression.Call( instance, methodInfo ); + return Expression.Call( instance, methodInfo, values.Select( Expression.Constant ) ); + } + + /// + /// 创建调用方法表达式 + /// + /// 调用的实例 + /// 方法名 + /// 参数类型列表 + /// 参数值列表 + public static Expression Call( this Expression instance, string methodName, Type[] paramTypes, params object[] values ) { + if( instance == null ) + throw new ArgumentNullException( nameof( instance ) ); + var methodInfo = instance.Type.GetMethod( methodName, paramTypes ); + if( methodInfo == null ) + return null; + if( values == null || values.Length == 0 ) + return Expression.Call( instance, methodInfo ); + return Expression.Call( instance, methodInfo, values.Select( Expression.Constant ) ); + } + + #endregion + + #region Compose(组合表达式) + + /// + /// 组合表达式 + /// + /// 对象类型 + /// 左操作数 + /// 右操作数 + /// 合并操作 + internal static Expression Compose( this Expression first, Expression second, Func merge ) { + var map = first.Parameters.Select( ( f, i ) => new { f, s = second.Parameters[i] } ).ToDictionary( p => p.s, p => p.f ); + var secondBody = ParameterRebinder.ReplaceParameters( map, second.Body ); + return Expression.Lambda( merge( first.Body, secondBody ), first.Parameters ); + } + + #endregion + + #region ToLambda(创建Lambda表达式) + + /// + /// 创建Lambda表达式 + /// + /// 委托类型 + /// 表达式 + /// 参数列表 + public static Expression ToLambda( this Expression body, params ParameterExpression[] parameters ) { + if( body == null ) + return null; + return Expression.Lambda( body, parameters ); + } + + #endregion + + #region ToPredicate(创建谓词表达式) + + /// + /// 创建谓词表达式 + /// + /// 委托类型 + /// 表达式 + /// 参数列表 + public static Expression> ToPredicate( this Expression body, params ParameterExpression[] parameters ) { + return ToLambda>( body, parameters ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Core/Expressions/ParameterRebinder.cs b/src/Util.Core/Expressions/ParameterRebinder.cs new file mode 100644 index 000000000..41cb0f0de --- /dev/null +++ b/src/Util.Core/Expressions/ParameterRebinder.cs @@ -0,0 +1,38 @@ +namespace Util.Expressions; + +/// +/// 参数重绑定操作 +/// +public class ParameterRebinder : ExpressionVisitor { + /// + /// 参数字典 + /// + private readonly Dictionary _map; + + /// + /// 初始化参数重绑定操作 + /// + /// 参数字典 + public ParameterRebinder( Dictionary map ) { + _map = map ?? new Dictionary(); + } + + /// + /// 替换参数 + /// + /// 参数字典 + /// 表达式 + public static Expression ReplaceParameters( Dictionary map, Expression exp ) { + return new ParameterRebinder( map ).Visit( exp ); + } + + /// + /// 访问参数 + /// + /// 参数 + protected override Expression VisitParameter( ParameterExpression parameterExpression ) { + if( _map.TryGetValue( parameterExpression, out var replacement ) ) + parameterExpression = replacement; + return base.VisitParameter( parameterExpression ); + } +} \ No newline at end of file diff --git a/src/Util.Core/Expressions/PredicateExpressionBuilder.cs b/src/Util.Core/Expressions/PredicateExpressionBuilder.cs new file mode 100644 index 000000000..5215ee2b4 --- /dev/null +++ b/src/Util.Core/Expressions/PredicateExpressionBuilder.cs @@ -0,0 +1,86 @@ +using Util.Data; +using Util.Helpers; + +namespace Util.Expressions; + +/// +/// 谓词表达式生成器 +/// +public class PredicateExpressionBuilder { + /// + /// 参数 + /// + private readonly ParameterExpression _parameter; + /// + /// 结果表达式 + /// + private Expression _result; + + /// + /// 初始化谓词表达式生成器 + /// + public PredicateExpressionBuilder() { + _parameter = Lambda.CreateParameter(); + } + + /// + /// 获取参数 + /// + public ParameterExpression GetParameter() { + return _parameter; + } + + /// + /// 添加表达式 + /// + /// 属性表达式 + /// 运算符 + /// 值 + public void Append( Expression> property, Operator @operator, object value ) { + _result = _result.And( _parameter.Property( Lambda.GetMember( property ) ).Operation( @operator, value ) ); + } + + /// + /// 添加表达式 + /// + /// 属性表达式 + /// 运算符 + /// 值 + public void Append( Expression> property, Operator @operator, Expression value ) { + _result = _result.And( _parameter.Property( Lambda.GetMember( property ) ).Operation( @operator, value ) ); + } + + /// + /// 添加表达式 + /// + /// 属性名 + /// 运算符 + /// 值 + public void Append( string property, Operator @operator, object value ) { + _result = _result.And( _parameter.Property( property ).Operation( @operator, value ) ); + } + + /// + /// 添加表达式 + /// + /// 属性名 + /// 运算符 + /// 值 + public void Append( string property, Operator @operator, Expression value ) { + _result = _result.And( _parameter.Property( property ).Operation( @operator, value ) ); + } + + /// + /// 清空 + /// + public void Clear() { + _result = null; + } + + /// + /// 转换为Lambda表达式 + /// + public Expression> ToLambda() { + return _result.ToLambda>( _parameter ); + } +} \ No newline at end of file diff --git a/src/Util.Core/FormatExtensions.cs b/src/Util.Core/FormatExtensions.cs new file mode 100644 index 000000000..dbc378417 --- /dev/null +++ b/src/Util.Core/FormatExtensions.cs @@ -0,0 +1,24 @@ +using Util.Properties; + +namespace Util; + +/// +/// 格式化扩展 +/// +public static class FormatExtensions { + /// + /// 获取描述 + /// + /// 布尔值 + public static string Description( this bool value ) { + return value ? R.Yes : R.No; + } + + /// + /// 获取描述 + /// + /// 布尔值 + public static string Description( this bool? value ) { + return value == null ? "" : Description( value.Value ); + } +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/CommandLine.cs b/src/Util.Core/Helpers/CommandLine.cs new file mode 100644 index 000000000..dc1305657 --- /dev/null +++ b/src/Util.Core/Helpers/CommandLine.cs @@ -0,0 +1,339 @@ +namespace Util.Helpers; + +/// +/// 命令行操作 +/// +public class CommandLine { + /// + /// 日志操作 + /// + private ILogger _logger; + /// + /// 命令,范例: dotnet + /// + private string _command; + /// + /// 命令参数,范例: --info + /// + private string _arguments; + /// + /// 环境变量 + /// + private readonly IDictionary _environmentVariables; + /// + /// 是否重定向标准输出流 + /// + private bool _redirectStandardOutput; + /// + /// 是否重定向标准错误流 + /// + private bool _redirectStandardError; + /// + /// 输出流字符编码,默认值: Encoding.UTF8 + /// + private Encoding _outputEncoding; + /// + /// 是否使用操作系统shell启动,默认值: false + /// + private bool _useShellExecute; + /// + /// 工作目录 + /// + private string _workingDirectory; + /// + /// 启动进程是否不要创建新窗口 + /// + private bool _createNoWindow; + /// + /// 命令执行超时间隔 + /// + private TimeSpan _timeout; + /// + /// 事件等待句柄 + /// + private readonly EventWaitHandle _outputReceived; + /// + /// 预期完成输出消息 + /// + private readonly List _outputToMatch; + + /// + /// 初始化命令行操作 + /// + public CommandLine() { + _logger = NullLogger.Instance; + _redirectStandardOutput = true; + _redirectStandardError = true; + _outputEncoding = Encoding.UTF8; + _useShellExecute = false; + _createNoWindow = true; + _environmentVariables = new Dictionary(); + _timeout = TimeSpan.FromSeconds( 30 ); + _outputReceived = new EventWaitHandle( false, EventResetMode.ManualReset ); + _outputToMatch = new List(); + } + + /// + /// 创建命令行操作 + /// + /// 命令,范例: dotnet + /// 命令参数,范例: --info + public static CommandLine Create( string command, string arguments = null ) { + return new CommandLine().Command( command ).Arguments( arguments ); + } + + /// + /// 设置日志操作 + /// + public CommandLine Log( ILogger logger ) { + _logger = logger ?? NullLogger.Instance; + return this; + } + + /// + /// 设置命令 + /// + /// 命令,范例: dotnet + public CommandLine Command( string command ) { + _command = command; + return this; + } + + /// + /// 根据条件设置命令参数 + /// + /// 条件,如果为false则不设置命令参数 + /// 命令参数,范例: --info + public CommandLine ArgumentsIf( bool condition, params string[] arguments ) { + if ( condition == false ) + return this; + return Arguments( arguments ); + } + + /// + /// 设置命令参数 + /// + /// 命令参数,范例: --info + public CommandLine Arguments( params string[] arguments ) { + if ( arguments == null ) + return this; + return Arguments( (IEnumerable)arguments ); + } + + /// + /// 根据条件设置命令参数 + /// + /// 条件,如果为false则不设置命令参数 + /// 命令参数 + public CommandLine ArgumentsIf( bool condition, IEnumerable arguments ) { + if ( condition == false ) + return this; + return Arguments( arguments ); + } + + /// + /// 设置命令参数 + /// + /// 命令参数 + public CommandLine Arguments( IEnumerable arguments ) { + if ( arguments == null ) + return this; + if ( _arguments.IsEmpty() == false ) + _arguments += " "; + _arguments += string.Join( " ", arguments ); + return this; + } + + /// + /// 设置环境变量 + /// + /// 键 + /// 值 + public CommandLine Env( string key, string value ) { + if ( key.IsEmpty() ) + return this; + if ( _environmentVariables.ContainsKey( key ) ) + _environmentVariables.Remove( key ); + _environmentVariables.Add( key, value ); + return this; + } + + /// + /// 设置环境变量 + /// + /// 环境变量 + public CommandLine Env( IDictionary env ) { + if ( env == null ) + return this; + foreach ( var item in env ) + Env( item.Key, item.Value ); + return this; + } + + /// + /// 设置重定向标准输出流 + /// + /// 是否重定向标准输出流,默认值: true,注意: 如果要设置UseShellExecute为 false, + /// 则必须将RedirectStandardOutput设置为true + public CommandLine RedirectStandardOutput( bool value = true ) { + _redirectStandardOutput = value; + if ( value ) { + _useShellExecute = false; + return this; + } + _useShellExecute = true; + _outputEncoding = null; + return this; + } + + /// + /// 设置重定向标准错误流 + /// + /// 是否重定向标准错误流 + public CommandLine RedirectStandardError( bool value = true ) { + _redirectStandardError = value; + return this; + } + + /// + /// 设置输出流字符编码 + /// + /// 输出流字符编码,默认值: Encoding.UTF8 + public CommandLine OutputEncoding( Encoding encoding ) { + _outputEncoding = encoding; + return this; + } + + /// + /// 设置是否使用操作系统shell启动 + /// + /// 是否使用操作系统shell启动,默认值: false + public CommandLine UseShellExecute( bool value = true ) { + _useShellExecute = value; + if ( value ) { + _redirectStandardOutput = false; + _outputEncoding = null; + return this; + } + _redirectStandardOutput = true; + return this; + } + + /// + /// 设置工作目录 + /// + /// 工作目录 + public CommandLine WorkingDirectory( string value ) { + _workingDirectory = value; + return this; + } + + /// + /// 启动进程是否不要创建新窗口 + /// + /// 启动进程是否不要创建新窗口,默认为true + public CommandLine CreateNoWindow( bool value ) { + _createNoWindow = value; + return this; + } + + /// + /// 设置命令执行超时间隔,默认值: 60秒 + /// + /// 命令执行超时间隔 + public CommandLine Timeout( TimeSpan timeout ) { + _timeout = timeout; + return this; + } + + /// + /// 设置命令执行完成的预期输出消息 + /// + public CommandLine OutputToMatch( params string[] outputs ) { + _outputToMatch.AddRange( outputs ); + return this; + } + + /// + /// 执行命令行 + /// + public Process Execute() { + if ( _command.IsEmpty() ) + throw new ArgumentException( "命令未设置" ); + _logger.LogDebug( $"Running command: {GetDebugText()}" ); + var process = new Process { + StartInfo = CreateProcessStartInfo() + }; + process.OutputDataReceived += OnOutput; + process.ErrorDataReceived += OnOutput; + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + var done = _outputReceived.WaitOne( _timeout ); + if ( !done ) + throw new Exception( $"命令 \"{GetDebugText()}\" 超时" ); + return process; + } + + /// + /// 创建进程启动信息 + /// + private ProcessStartInfo CreateProcessStartInfo() { + var escapedArgs = _arguments.Replace( "\"", "\\\"" ); + var result = new ProcessStartInfo( _command, escapedArgs ) { + RedirectStandardOutput = _redirectStandardOutput, + RedirectStandardError = _redirectStandardError, + StandardOutputEncoding = _outputEncoding, + StandardErrorEncoding = _outputEncoding, + UseShellExecute = _useShellExecute, + WorkingDirectory = _workingDirectory, + CreateNoWindow = _createNoWindow, + }; + foreach ( var item in _environmentVariables ) + result.EnvironmentVariables.Add( item.Key, item.Value ); + return result; + } + + /// + /// 接收输出消息事件处理 + /// + private void OnOutput( object sendingProcess, DataReceivedEventArgs e ) { + if ( e.Data.IsEmpty() ) + return; + try { + _logger.LogDebug( e.Data ); + } + catch ( InvalidOperationException ) { + } + if ( _outputToMatch.Any( e.Data.Contains ) ) + _outputReceived.Set(); + } + + /// + /// 获取命令调试文本 + /// + public string GetDebugText() { + return $"{_command} {_arguments}"; + } + + /// + /// 执行PowerShell命令 + /// + /// PowerShell命令,范例: Set-ExecutionPolicy RemoteSigned + /// 工作目录 + public static void ExecutePowerShell( string command, string workingDirectory = null ) { + using Process process = new Process(); + process.StartInfo.FileName = "cmd.exe"; + process.StartInfo.CreateNoWindow = false; + process.StartInfo.RedirectStandardError = true; + process.StartInfo.RedirectStandardInput = true; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.UseShellExecute = false; + process.StartInfo.WorkingDirectory = workingDirectory; + process.Start(); + process.StandardInput.WriteLine( $"powershell {command}" ); + process.StandardInput.WriteLine( "exit" ); + process.StandardInput.AutoFlush = true; + process.WaitForExit(); + } +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Common.cs b/src/Util.Core/Helpers/Common.cs new file mode 100644 index 000000000..9f52cdb9a --- /dev/null +++ b/src/Util.Core/Helpers/Common.cs @@ -0,0 +1,84 @@ +namespace Util.Helpers; + +/// +/// 公共操作 +/// +public static class Common { + /// + /// 获取当前应用程序基路径 + /// + public static string ApplicationBaseDirectory => AppContext.BaseDirectory; + /// + /// 换行符 + /// + public static string Line => System.Environment.NewLine; + /// + /// 是否Linux操作系统 + /// + public static bool IsLinux => RuntimeInformation.IsOSPlatform( OSPlatform.Linux ); + /// + /// 是否Windows操作系统 + /// + public static bool IsWindows => RuntimeInformation.IsOSPlatform( OSPlatform.Windows ); + + /// + /// 获取类型 + /// + /// 类型 + public static Type GetType() { + return GetType( typeof( T ) ); + } + + /// + /// 获取类型 + /// + /// 类型 + public static Type GetType( Type type ) { + return Nullable.GetUnderlyingType( type ) ?? type; + } + + /// + /// 获取物理路径 + /// + /// 相对路径,范例:"test/a.txt" 或 "/test/a.txt" + /// 基路径,默认为AppContext.BaseDirectory + public static string GetPhysicalPath( string relativePath, string basePath = null ) { + if ( relativePath.StartsWith( "~" ) ) + relativePath = relativePath.TrimStart( '~' ); + if ( relativePath.StartsWith( "/" ) ) + relativePath = relativePath.TrimStart( '/' ); + if ( relativePath.StartsWith( "\\" ) ) + relativePath = relativePath.TrimStart( '\\' ); + basePath ??= ApplicationBaseDirectory; + return Path.Combine( basePath, relativePath ); + } + + /// + /// 连接路径 + /// + /// 路径列表 + public static string JoinPath( params string[] paths ) { + return Url.JoinPath( paths ); + } + + /// + /// 获取当前目录路径 + /// + public static string GetCurrentDirectory() { + return Directory.GetCurrentDirectory(); + } + + /// + /// 获取当前目录的上级路径 + /// + /// 向上钻取的深度 + public static string GetParentDirectory( int depth = 1 ) { + var path = Directory.GetCurrentDirectory(); + for ( int i = 0; i < depth; i++ ) { + var parent = Directory.GetParent( path ); + if ( parent is { Exists: true } ) + path = parent.FullName; + } + return path; + } +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Config.cs b/src/Util.Core/Helpers/Config.cs new file mode 100644 index 000000000..68cea7882 --- /dev/null +++ b/src/Util.Core/Helpers/Config.cs @@ -0,0 +1,88 @@ +namespace Util.Helpers; + +/// +/// 配置操作 +/// +public static class Config { + /// + /// 配置 + /// + private static IConfiguration _configuration; + + /// + /// 设置配置 + /// + /// 配置 + public static void SetConfiguration( IConfiguration configuration ) { + _configuration = configuration; + } + + /// + /// 获取配置值 + /// + /// 配置键 + public static string GetValue( string key ) { + return GetValue( key ); + } + + /// + /// 获取配置值 + /// + /// 配置键 + public static T GetValue( string key ) { + return GetConfiguration().GetValue( key ); + } + + /// + /// 获取配置选项 + /// + /// 配置选项类型 + /// 配置节 + public static TOptions Get( string section ) { + return GetSection( section ).Get(); + } + + /// + /// 获取配置节 + /// + /// 配置节 + public static IConfigurationSection GetSection( string section ) { + return GetConfiguration().GetSection( section ); + } + + /// + /// 获取配置 + /// + private static IConfiguration GetConfiguration() { + return _configuration ??= CreateConfiguration(); + } + + /// + /// 创建配置 + /// + /// 配置文件目录绝对路径 + /// 配置文件列表,默认已包含appsettings.json + public static IConfiguration CreateConfiguration( string basePath = null,params string[] jsonFiles ) { + basePath ??= Common.ApplicationBaseDirectory; + var builder = new ConfigurationBuilder() + .SetBasePath( basePath ) + .AddJsonFile( "appsettings.json", true, false ); + var environment = Environment.GetEnvironmentName(); + if ( environment.IsEmpty() == false ) + builder.AddJsonFile( $"appsettings.{environment}.json", true, false ); + builder.AddEnvironmentVariables(); + if ( jsonFiles == null ) + return builder.Build(); + foreach ( var file in jsonFiles ) + builder.AddJsonFile( file, true, false ); + return builder.Build(); + } + + /// + /// 获取数据库连接字符串 + /// + /// 数据库连接字符串键名 + public static string GetConnectionString( string name ) { + return GetConfiguration().GetConnectionString( name ); + } +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Const.cs b/src/Util.Core/Helpers/Const.cs new file mode 100644 index 000000000..edcc65ec6 --- /dev/null +++ b/src/Util.Core/Helpers/Const.cs @@ -0,0 +1,15 @@ +namespace Util.Helpers; + +/// +/// 常量 +/// +public static class Const { + /// + /// 汉字及拼音首字母 + /// + public const string ChinesePinYin = "丂k丄s丅x丆m丏m丒c丗s丢d丠q両l丣y并b丩j丮j丯j丱g丳c丵z丷b丼j乀f乁y讈l乆j乑y乕h乗c乚h乛w乢g乣j乤h乧d乨s乪n乫g乬g乭d乮m乯o乲c乴x乵y乶p乷s乸n乹q乺s乻e乼z乽z乿z亀g亁q乱l亃l亄y亅j亇m亊s亐y亖s亗s亘g亜y亝q亚y亣d亪y亯x亰j亱y亴y亶d亷l亸d亹m亼j亽j亾w仈b仌b仏f仐j仒e仚x仛t仜h仢b仦c仧c仩c仭r仮f仯c仱q仴w仸y仹f仺c仼w仾d伀z伂p伃y伄d伅d伆w伇y伈x汲j伌a伒j伓p伔d伖t伜c伝y伡c伣q伨x伩x伬c伭x伮n伱n伳x伵x伷z伹q伻b伾p伿z佀s佁a佄h佅m伫z布b佉q佊b佋z佌c佒y占z佖b佡x佢q佦s佨b徊h佫h佭x佮g佱f佲m佷h佸h佺q佽c侁s侂t侅g来l侇y侊g侌y侎m侐x侒a侓l侕e仑l侘c侙c侚x侜z侞r侟c価s侢d侤t侫n侭j侰j侱c侲z侳z侴h侣l局j侸s侹t侺s侻t侾x俀t俣y系j俆x俇g俈k俉w俋y俌f俍l俒h俓j俔q俕s俖p俙x俛f侠x俢x俤d俥c俧z俫l俬s俰h俲x俴j俵b俶c俷f俹y俻b俼y俽x俿h伥c倁z倂b倃j倅c俩l倇w倈l仓c倊z个g倎t倐s们m倓t倕c幸x倗p倛q倝g倞j倠s倢j仿f値z倧z伦l倯s倰l倱h倲d倳z倴b倵w倶j倷n倸c倹j倻y倽s倿n偀y偁c偂q偄r偅z偆c伟w偊y偋b偍t偐y偑f偒t偓w偔e偖c偗s偘k偙d偛c偝b偞x偠y偡z偢c偣a偦x偧z偨c偩f偪b偫z偭m偮j偯y偰x偱x偲c偳d侧c侦z偸t偹b咱z偼j伪w傁s傂z傃s傄x傆y傇r傉n傊y傋j傌m傎d傏t傐h杰j傒x傓s傔q傕j伧c傗c伞s备b傚x傛r傜y傝t傞s傟y傠f傡b家j傤z傦g傪c傫l佣y傮z偬z傰p傱s传c伛y债z伤s傹j傼h傽z倾q傿y僀d僁x偻l僃b僄b仅j僆l戮l僈m佥q僊x僋t僌y働d僎z僐s侨q僒j僓t僔z仆p僗l僘c僙g僛q僜d僝c僞w僟j僠b僡h僢c僣t僤d侥j偾f僩x僪y僫e僯l僰b雇g僲x僴x僶m僷y僸j价j僺q僼f僽z僾a僿s仪y儁j侬n儃c亿y当d侩k俭j儊c儌j儍s儎z儏c傧b儑a儓t俦c侪c儖l儗y尽j儙q儚m儛w儜n儝q儞n偿c儠l儢l儣k儤b儥y儦b儧z儨z儩s优y儫h儬q儭c儮l儯t儰w儱l储c儳c儴x儵s儶h俪l罗l儹z傩n傥t俨y儽l儾n兂z凶x兊d兑d兎t兏c児e儿e兖y兘s兟s兡b兤h兦w内n两l兪y兯h兲t兾j兿y冃m冄r円y冇m册c冋j冎g冏j冐m胄z冓g冔x冘y冚k冝y冞s冟s冡m冣z冦k冧l冨f冩x幂m冭t冮g冴y冸p冹f冺m冾q冿j凁s凂m凃t凅g净j凊q冻d凎g凐y凒a凓l凔c凕m凖z凗c凘s凙d凚j凛l凞x凟d凢f凣f凥j処c凧y凨f凩k凪n凬f凮f凯k凲g凴p凷k凾h刄r刅c刉j刋q刌c刏j刐d刓w刔j刕l刜f刞q刟d刡m刢l刣z别b刦j刧j删s刬c刯g刱c刲k刴d刵e刼j刾c刭j剅d剆l则z剈y剉c克k刹c剒c剓l剕f剗c剘q剙c剚z刚g剥b剟d剠q剢d剣j剤j剦y剨h剫d剬z剭w剐g剰s剱j剳d剀k创c剶c铲c剸t剹l剺l剻p剼s剾k劀g划h劄z劅z劆l剧j刘l刽g劋j刿g剑j劎j劏t剂j劒j劔j劕z劖c劗z劘m劙l劚z劜y劤j劥k劦x劧z劮y劯z劰m労l劵j劶k劷y劸w効x劺m匡k劼j劽l勀k劲j勂g勄m勅c勆l勈y勊k勌j勍q勎l勏b勑c勓k勔m动d勖x务w勚y勋x勜w胜s劳l勠l勡p势s积j勥j剿c勧q勨x勪q勫f勬j勭t勮j勯d劢m勲x勳x勴l励l勶c勷x劝q匀y勼j勽b匁m匂x匃g匄g匇y匉p匊j匋t匌g匎e匑g匒d匓j匔g匘n匛j匜y匞j匟k匢h匤q匥f匧q匨z匩k匫h匬y匦g汇h匰d匮k匲l匳l匴s匵d匶j匷j匸x匼k匽y区o卂x卄n卆z卋s卌x卍w卐w协x単d卙j卛s卝g卥x卨x卪j卬a卭q卲s卶c恤x却q卼w卽j卾e厀x厁s厃w厇z厈h厊y厎d厏z厐p厑y厒q厓y厔z厖m厗t厍s厛t厜z厞f厡y厤l厧d厪j厫a厬g厌y厯l厰c厱q厉l厳y厣y厵y厷g厸l厹r厺q厼k厽l厾d叀z参c叄c叅c叆a叇d収s叏g叐b叒r叓l叕z叚j叜s叝j叞w叡r丛c叧g叴q叺c叾d叿h吀m吂m吅x吇z寸c吔y吘o吙h吚y吜c吢q吤j吥b吪e吰h吴w呐n吷x吺d吽h吿g呁j吕l呄g呅w呇q呉w呌j呍h尺c呏s呑t呚h呝e呞s呟j呠p呡w呣m呥r呧d呪z呫t呬x呭y呮q呯p呰z呴g呹y呺x呾d呿q咁x咃t咅p咇b咈f咉y咊h咍h咑d咓w咗z咘b咜t咞x咟h咠q咡e咢e咥d咮z咰s咲x咵k咶g咷t咹e咺x呙g咾l哃t哅x哊y哋d哖n哘x哛p哠h员y哢l哣p哤m哫z哬h哯x哰l哱b哴l哵b哶m哸s哹f哻h哾c唀y唂g唃g呗b含h唈y唊j唋t唌d唍w唎l唒q唓c唕z唖y唗d唘q唙d唚q唜m唝g唞d唟g唡l唥l唦s唨z唩w唫j唭q唲e唴q唵a唶j念n唹y唺t唻l唽x啀a啂g啇d啈h啋c啌q啍z啎w问w啑s啒g啓q啔q啗d啘w啚b啝h哑y启q啠z啢l衔x啨q啩g啫z啯g啰l啱y啲d啳q啴c啹j啺t啽a啿d喅y喆z喌z喍c喎w喐h喒z喓y喕m喖h喗y唤h喛h喞j喠z喡w喢s喣x喤h喥d喦y喨l喩y丧s吃c乔q喭y单c喯p喰c哟y喴w営y喸p喺x喼j喿z嗀h嗁t嗂y嗃h呛q啬s嗈y嗊g嗋x吗m嗏c嗐h嗕r嗗w嗙p呜w嗛q嗞z嗠l嗢w嗧j唢s嗭z嗮s嗰g嗱n嗴q哔b嗸a嗹l嗺z嗻z嗼m嗿t嘂j嘃c嘄j嘅k叹t嘇s嘊a嘋x喽l嘐x嘑h嘒h呕o嘕x啧z尝c嘙p嘚d唛m嘝h嘠g嘡t嘢y嘥s嘦j嘨x哗h嘪m嘫r唠l啸x叽j嘳k哓x嘷h呒m嘺q嘼c嘽t嘾d噀x恶e噂z噃f噄c噅h噆z噇c噈c噉d噊j噋t噏x噐q噑h噒l嘘s噕h噖y噚x噛n噝s噞y噟y哒d噡z噣z哝n哕y噧x噭j噮y嗳a噰y哙k噳y喷p噷x吨d噺h噽p噾y噿z咛n嚁d嚂l嚃t嚄h吓h嚈y嚉d嚊x嚋z哜j嚑x嚔t噜l嚖h嚗b嚘y啮n嚚y嚛h嚜m嚝h嚞z嚟l嚠l嚡x嚢n嚤m咽y呖l嚧l咙l嚩p嚪d嚫c嚬p嚭p向x嚰m嚱x嚲d喾k严y嚵c嘤y嚸d嚹l嚺t嚻x嚽c嚾h嚿h啭z嗫n嚣a囃z冁c囆c囇l呓y囋z苏s囍x囎z囏j囐y嘱z囒l囓n囕r囖l囘h囙y囜n団t囥k囦y囧j囨p囩y囱c囬h囮e囯g囲w図t囶g囷q囸r囼t圀g圁y圂h圅h囵l国g圌c围w圎y圏q圐k圑p园y圆y圔y圕t图t圗t团t圙l圚h圛y圝l圞l圠y圡t圢t圤p圥l圦k圧y圫y圱q圲q圴z圵d圶q圷x圸s圼n圽m圿j坁z坃x坄y坅q坆m坈r坉t坋b坒b坓j坔d坕j坖j坘d坙j坢b坣t坥q坧z坬g坮t垧s坱y坲f坴l坵q坸g坹x坽l坾z坿f垁z垇a垈d垉p垊m垍j垎h垏l垐c垑c垔y垕h垖d垗z垘f垙g垚y垜d垝g垞c垟y垥x垨s垪b垬h垯d垰k垱d垳h垵a垶x垷x垹b垺p垻b垼y垽y垾h垿x埀c埁c埄b埅d埆q埇y埈j埉x埊d埌l埍j埐q埑z埓l埖h埗b埛j埜y埞d垭y埢q埣s埥q埦w埧j埨l埩z埪k埫c埬d埮t埰c埲b埳x埵d执z埻z埼q埾j埿n堁k堃k堄n坚j堈g堉y垩e堌g堎l堏f堐y堒k堓a堔s堖n堗t堘c堚h堛b堜l埚g堟z堢b堣y堥m堦j堧r堨a堩g堫z堬y堭h堮e尧y报b堲j堳m场c堶t堷y堸f堹z堺j堻j堼f堽g堾c碱j塀p塁l塂j塃h塅d塆w塇x塉j块k茔y塎y垲k塐s埘s塓m塕w塖c涂t塙q塚z塛l塜p塝b塟z塠d塡t坞w塣z埙x塦z塧a塨g塩y塪x塭w塮x塯l塰h塱l塲c塳p塴b尘c塶l塷l塸o堑q塺m塻m塼z塽s塿l墂b墄q墆z墇z墈k垫d墋c墌z墍x墎g墏q墐j墒s墔c墕y墖t増z墘q墛w坠z墝q墠s墡s墢f墣p墤k墥t墦f墧q墪d墫c墬d墭s堕d墯d墰t墱d墲w墴h墵t墶d墷y墸z墹j墺a墙q垦k墿y壀p壂d壃j壄y壆x坛t壈l壉j壊h壋d壌r壍q壏x壐x壒a压y壖r壗j垒l圹k垆l壛y壜t壝w坏h垄l壠l壡r坜l壣l壥c壦x壧y壨l坝b壪w壭s壮z壱y売m壴z壵z壷h壸k壶h壻x壼k寿s壾m壿d夀s夁y夃g夅j夆f夈z変b夊s夋q夌l夎c夐x夑x夒n夓x夗y夘w夛d夝q夞y够g夡q梦m夣m夦c夨c夬g夰g夲b夳t夵y夶b夻h夽y夹g夿b奀e奃d奅p奆j奊x奌d奍q奂h奒k奓z奙b奛h奜f奝d奞x奟b奡a奣w奤h奦w奥a奨j奁l夺d奫y奬j奭s奋f奯h奰b奱l奲d奵d奷q奺j奻n奼c奾x奿f妀j妅h妉d妋f妎h妏w妐z妑p妔k妕z妘y妚f妜y妆z妟y妠n妡x妦f妧w妬d妰z妱z你n妴y妵t妶x妷z妸e妺m妼b妽s妿e姀h姁x姂f姄m姅b姇f姈l姉z姌r姗s姎y姏g姕z姖j姙r姛d姞j姟g姠x姡h姢j姤g奸j姧j姩n侄z姫z姭x姮h姯g姰j姱h姲y姳m姴l姵p姶y姷y姸y姺x姼s姽g姾q娀s娂h娋s娍c娎x娏m娐f娒m娔k娕c娖c娗t娙x娚n娱y娝b娞n娡z娢h娤z娦p娧t娨m娪w娫y娬w娭a娮y娯y娰s娳l娵j娷s娸q娹x娺z娻d娽l娾a娿e娄l婃c婄p婅j婇c婈l婋x婌s婍q婎h婏f婐w婑w婒t婓f婔f婖t婗n婘j婙j婛j婜q婝d婞x婟h婠w婡l婣y婤z婥c妇f婨l婩a婫h婬y娅y婮j婯l婰d婱x婳h婸d婹y婻n婼c婽j婾t媀y媁w媂d媃r媄m媅d媆r媇q媈h媉w媊q媋c媌m媍f媎j媏d媐y媓h媔m媕a媖y媗x媘j媙w媜z媞t媟x媠d媢m媣r媤s媥p媦w娲w媨c媩h媫j媬b媭x媮t妫g媰c媱y媴y媶r媷r媹l媺m媻p媪a妈m媿k嫀q嫃z嫄y嫅j嫆r嫇m嫈y嫊s嫋n嫍t嫎p嫏l嫐n嫑b嫓p嫕y妪y嫙x嫚m嫛y嫝k嫞y嫟n嫢g嫤j嫥z嫧z嫨h嫪l嫬z嫭h嫮h嫯a嫰n嫲m嫳p嫴g妩w嫶q嫷t嫸z嫹m嫺x娴x嫼m嫽l嫾l嬀g嬁d嬂z嬄y嬅h嬆x嬇k娆r嬊y婵c娇j嬍m嬎f嬏f嬐x嬑y嬒h嬔f嬕s嬘s嫱q嬚l嬛h袅n嬞d嬠c嫒a嬢n嬣n嬷m嬥t嬦c嬧j嬨c嬩y嫔p嬫r嬬r嬭n嬮y嬯t婴y嬱q嬳y嬵m嬶k婶s嬹x嬺n嬻d嬼l嬽y嬾l嬿y孁l孂j娘n孄l孅x孆y孇s孈x孉h孊m孋l娈l孍y孎z孏l孖m孞x孠s孡t孧y孙s孭m孮c孯q孲y孴n孷l学x孹b孻n孼n孾y孪l宂r宆q宊t宍r宎y宐y宑j宒z宔z宖h実s宧y宨t宩s宬c宭q宫g宯x宱z宲b宷c宺h宻m宼k寀c寁z寃y寈q寉h寊z寋j寍n寎b寏h寑q寔s寕n寖j寗n寘z寙y寚b寛k寜n寠j寝q寣h实s宁n审s寪w写x宽k寭h寯j寱y寲y寳b寴q宠c宝b寷f寽l対d尀p専z尃f将j专z寻x尌s对d导d尐j尒e尓e尗s尙s尛m尞l尟x尠x尡h尣w尦l尨m尩w尪w尫w尭y尮d尯k尰z尲g尳g尵t尶g尴g屃x届j屇t屌d屍s屒z屓x屔n屖x屘m屚l屛p屉t扉f屟x屡l层c屧x屦j屩j屪l屫j属s屭x屰n屲w屳x屴l屵a屶h屷h屸h屻r屼w屽h屾s岀c岃y岄y岆y岇a岉w岊j岋e岏w岒q岓q岕j岝z岞z岠j冈g岤x岥p岦l迢t岨j岮t岯p岰a岲k岴q岶p岹t岺l岻d岼p峀x峂t峃x峅b峆h峇b峈l峉e峌d峍l峎e峏e峐g峑q峓y峔m峕s峖a峗w峘h峚m峛l峜f峝t峞w峟y峠q峢l峧j峩e峫x峬b峯f峱n峲l峳y岘x峵r岛d峷s峸c峹t峺g峼g峡x峿w崀l崁k崄x崅q崈c崉t崊l崋h崌j崃l崏m崐k崑k崒z崓g崕y岗g崘l崚l崜d崝z崟y岽d崡h峥z崣w崥p崨j崪z崫j崬d崯y崰z崱z崲h嵛y崵y崶f崷q崸y崹t崺y崻z崼s崿e嵀z嵁k嵂l嵃y嵄m嵅h嵆j嵈h嵉t嵍w嵎y嵏z岚l嵑k岩y嵓y嵔w嵕z嵖c嵗s嵙k嵚q嵜q嵞t嵟c嵠x嵡w嵢c嵣d嵤r嵥j嵦k嵧l嵨w嵪k嵭b嵮d嵰q嵱y嵲n嵳c嵵s嵶r嵷s嵸z嵹j嵺l嵻k嵼y嵾c嵿d嶀t嵝l嶃z崭z嶅a嶆c岖q嶉w嶊z嶋d嶌d嶍x嶎y嶏p嶐l嶑x嶒c嶓b嶔q嶕j嶖y崂l嶘z嶚l嶛l嶜q嶞t嶟z峤j嶡j嶢y嶣j嶤y嶥j嶦z峄y嶨x嶩n嶫y嶬y嶭n嶮x嶯j嶰x嶱k嶲g嶳d嶴a嶵z嶶w嵘r嶹d岭l嶻j屿y岳y嶾y嶿r巀j巁l巂x巃l巄l巇x巈j巉c巊y岿k巌y巎n巐c漓l峦l巓d巅d巕n巗y巘y巙k巚y巜k巟h巠j巣c巤l巪g巬p巭b巯q巵z巶z巸y卺j巺x巻j巼p巿f帀z帄d帇n帊p帋z帍h帎d帒d帓m帗f帞m帟y帠y帡p帢q帣j帤r帅s帨s帩q帪z师s帬q帯d帰g帲p帐z帴j帵w带d帹s帺q帾z帿h帧z幁z帏w幆y幇b幈p幉d幊g幋p幍t幎m幏j幐t幑h幒z幓s幖b帼g帻z幙m幚b幜j幝c帜z幠h币b幤b幦m幧q幨c蒙m帮b帱c幭m幮c幰x幱l幵j幷b干g几j庁t仄z広g庈q庉d庌y庍b庎j庒z庘y庛c庝t庡y庢z庣t庤z庨x庩t庪g库k庬m庮y庯b庰b庱c庲l庴j庻s庼q庽y庿m廀s厕c厢x廃f厩j廅e廆h廇l厦s廋s廌z廍b廎q廏j廔l廕y廗d廘l廙y厨c廜t厮s廞x庙m厂a庑w废f广a廤k廥k廦b廧q廪l廫l庐l廭j廮y廯x廰t痈y廲l厅t廵x廸d廹p廻h廼n廽h弆j弇y弉z弌y弍e弎s弑s吊d弖h弙w弚t弜j弝b弡j弢t弣f弤d弨c弫z弬y弮j弰s弲x弪j弴d张z弶j强j弸p弻b弽s弾d弿j彁g彂f彃b彄k彅j彇x弹d彉g彋h弥m彍g弯w彏j彑j彔l彚h彛y彜y彞y彟h彠h彣w彦y彧y彨c雕d彮y彯p彲c彴z彵t彶j彸z彺w彽d彾l佛f徃w徆x徍w徎c徏z径j従c徔c徖c徝z从c徟z徕l徢x徣j徤j徥s徧b复f旁p徯x徰z徱p徲t徳d徶b彻c徺j徻h徾m徿l忀x忁b忂q忈r忊d忋g忎r忓h忔y忕s忚x忛f応y忞m忟m忢w忣j忥x忦j忨w忩c忬y忯q忰c忲t忳d忴q忶h忷x忹w忺x忼k怇j怈y怉b怋m怌p怐j怑b怗t怘h怚c怞y怟d怢t怣y怤f怬x怮y怰x怲b怳h怴x怶b怷s怸x怹t怺y怽m恀s恄x恅l恒h恇k恈m恉z恊x恌y恏h恑g恓x恔x恖s恗h恘q恛h恞y恟x恠g恡l耻c恦s恮z恱y恲p恴d恵h恷q恾m悀y悁j悂p悦y悆y悇t悈j悊z悋l悎h悏q悐t悑b悓q悕x悗m悘y悙h悜c悞w悡l悢l悤c悥y悧l悩n悪e悮w悰c悳d怅c闷m悷l悹g悺g凄q悾k悿t惀l惁x惂k惃g惄n惇d惈g惉z惌y惍j惎j惏l惐y惒h惓q惔t惖t惗n啜c惛h惞x惢s惣z惤j惥y惪d恼n恽y惵d惷c惸q恻c惼b惽m惾z惿t愃x愄w愅g愇w愊b愋x愌h愐m愑y愒q愓d愔y愖c愗m愘k愙k爱a惬q愝y愞n愡c愢s愥y悫q愩g愬s愭q愮y愯s愰h愱j愳j怆c愵n愶x恺k愸z愹y愺c愻x愼s愽b忾k慀x慁h慂y栗l慅s慆t殷y慉x态t愠y慏m慐g慒c慓p慔m慖g慗c惨c惭c慛c慜m慞z恸t慠a慡s惯g慤q慥z慦j慩l怄o怂s慬q慭y虑l慯s慱t慲m悭q慴s庆q慸d慹z慺l慻j戚q慽q欲y慿p憀l憁c忧y憃c憄z憅t憆c憇q憈q惫b憌q憍j憏c怜l凭p愦k憓h憕c憖y憗y憘x憙x惮d憛t憞d憟s憠j憢x憣f愤f憥l憦l憪x悯m憭l怃w憯c憰j憱c宪x憳t憴s憵p忆y憸x憹n憺d憻t憼j憽s懀w懁x懃q懄q懅j懆s恳k应y怿y檩l懎s懐h懓a懕y懖g懗x懘c懙y懚y懛d懜m懝a怼d懠q懢l懑m懤c懥z懧n恹y懩y懪b懫z懬k懭k懮y懯f懰l懱m惩c懳h懴c懒l怀h悬x懹r忏c懻j惧j懽h慑s恋l戁n戂m戃t戄j戅g戆g钺y戓g戋j戙d戜d戝z戞j戠z戣k戦z戗q戨g戬j戫y戭y戯x战z戱x戏h户h戸h戹e戺s戻t戼m扂d扄s扅y扆y扊y扏q仂l払f扖h扗z扙z扚d扜y扝y扞h扟s叉c扡t扦q扤w扥d扨s扱c扲q扴j扵y扷b扸x抵d扻z扽d抁y抂k拚p抅j擦c抇h抈y抋q抌d抍z殒y抏w抐n抔p抙p抜b抝a択z抣y抦b抧z抩n抪p抭y抮z抰y抲h抳n曳y抶c抷p抸j抺m抾q拀z拁j拃z抛p拏n拑q拕t拝b拞d拠j拡k拤q拪q拫h拰n拵c拸y拹x拺c拻h挀b挃z挄k挅d挆d挊n挋z挍j挏d挐n挒l挓z挔l挕d挗j挘l挙j挜y挦x挧y挩t挬b挭g挮t挰c挱s挳k挴m挵n挷b挸j挻s挼r挟x挿c捀p捁j捄j捇h捈t捊p捑z捒s捓y捔j捖w捗b捘z捙y捚z捛l捜s捝t捠b捤w捥w捦q舍s捪m扪m捬f捯d捰w卷j捳y捴z捵c捸t捹b捼r捽z捾w捿q掁c扫s抡l掅q掆g掋d掍h掑q掓s掔q掕l掗y挣z掚l挂g掜y掝h掞y掟z采c掦t掫z掯k掱p掲j掵m掶j掹m掻s掽p掿n拣j揁z揂j揃j揅y揇n揈h揊p揋w揌z揑n揓s揔z揕z揗x揘h揙b扬y换h揜y揝z揟x揢k揤j揥d揦l揧l揨c揫j揬t挥h揯g揰c揱x揳x揵q揷c背b揺y揻w揼b揾w搃z搄g构g搇q搈r搉q搊c损s搎s搑r搒b搕k摇y捣d搘z搙n搚l搝q擀g搢j搣m搤e搥c搧s搨t搩j搫p搯t搰h搱z搲w搳h搵w抢q搷t搸z搹e搻n搼q搾z摂s摃g摉s摋s摌c摍s摎n摏c摐c掴g摓f摕d摗s摙l摚c摛c掼g摝l搂l摠z摡g摢h摣z摤q摥t摦h摨z摪j摫g摬y摮a挚z摰n摱m摲c抠k摴c摵s抟t摷j掺c摼k摽b摿y撀g撁q撃j撆p捞l撉d撊x撋r撌g撍z撎y撏x撑c挠n撔h撗h搭d捻n撛l撜z撝h挢j撠j撡c掸d拨b撦c撨x撪b抚f撯z撱w扑p揿q撴d撶h撹j挞t撽j挝w捡j拥y擃n掳l擆z择z擈p擉c击j挡d擌s擏q擑j擓k担d擕x擖y擙a据j擛y擜e擝m挤j擡t擥l擧j擨y擩r擪y擫y拟n擭h擮j摈b拧n搁g掷z擳z扩k擵m擶j撷x擸l擹t摆b擞s撸l擽l扰r攁y攃c摅s攅z撵n攇x攈j攊l攋l攌h攍y攎l隆l攐q攑q攓q拦l攕x撄y攗m搀c撺c携x摄s攞l攟j攠m攡c攒c挛l摊t攦l攧d攨w攩d搅j揽l攭l攰g攱g攲q攳x考k攼g攽b敀p敁d敂k敃m敄w敆h敇c敊c敋g敍x敎j敐c敒s敓d敔y败b叙x敚d敜n敟d敠d敡y敤k敥y敧q敨t敩x敪d敭y敮x敯m敱a敳a敌d敶c数s敹l敺q敻x敼y敽j敾s敿j斀z斁d敛l毙b斄l斅x斆x斈x斉q斊q斍j斎z斏l斒b斔y斓l斖w斘s斚j斝j斞y斠j斢t斣d斦y斨q斪q斩z斱z斲z斴l斵z斶c断d斸z斺c斻h斾p斿l旀m旗q旇p旈l旉f旊f旍j旐z旑y旓s旔j旕e旘z旙f旚p旛f旜z旝k旞s旟y旡j旣j旪x旫t旲t旳d旴x旵c旸y旹s旻m旼m旽t旾c旿w昁b昄b昅j升s昈h昉f昋g昍x昑q昒h昖y昗z昘f昚s昛j昜y昞b昡x昢p昣z昤l昦h昩m昪b昫x昬h昮z昰s昲f昳d昷w昸d昹a昺b昻a昽l昿k晀t时s晄h晅x晆k晈j晋j晊z晍t晎h晐g晑x晘h晙j晛x晜k昼z曦x晠s晢z晣z晥w晧h晩w晪t晫z晭z晱s晳x晵q晸z晹y晻a晼w晽l晿c暀w暁x暃f暅g暆y晕y晖h暊x暋m暍y暏s暐w暒q暓m暕j阳y暙c暚y暛s暜p暞j暟k暠g暡w畅c暣q暤h暥y暦l暩j暪m暂z暬x暯m暰c昵n暲z暳h暵h暶x暷c了l暺t暻j暼p暽l暿x曀y曁j曂h曃d晔y曅y历l昙t曈t晓x曊f曋s曍h曎y曏x曐x曑s曒j曓b曔j曕y暧a曘r曚m曞l曟c旷k曡d曣y曤h曥l曧r曨l曪l曫l晒s曭t曮y曯z曱y曵y曶h书s曺c曽z朁c朂x会g朄y朅q朇b朎l朏k朒g朓t朖l朘j朙m朚h朜t朞j朠y朡z朢w朣t朤l朥l胧l术s朰t朲r朳b朶d朷d朸l朹q朻j朼b朾c朿c杁r杄q杅y圬w杊x杋f杍z杒r杔t杕d杗m杘c杙y杚g杛g杝t杢j杣m杤w杦j杧m杫s杬y杮f东d杴x杶c杸s杹h杺x杻c杽c枀s枂w枃j枅j枆m枊a枍y枎f枏n枑h枒y枓d枔x枖y栀z枛z枟y枠h枡s枤d枦l枩s枬z枮x枱s枲x拐g枹b枺m枻y枼y枽y枾s枿n柀b柂d柅n柆l柇h柈b柉f柊z柋d柌c柍y柎f柕m柖s柗s柟n柡y柣z柤z柦d柧g柪a柫f柭b柮w柲b栅s柶s柷z柹s柺g査c柼y柾j栁l栂m栃l栄r栆z栍s栐y栒x栔q栕c栘y栙x栚z栛l栜s栞k栟b栠r栢b栣r栤b栥z栦c栧y栨c栫j栬z栮e栯y栰f栱g栴z栵l栶y栺z栻s栿f桇r桋y桍k桏q桒s桖x桘c桙y桚z桛k桝j桞l桟z桪x桬s桭c桮b桯t桰g桱j桲b桳b桵r桸x桹l桺l桻f桼q桽w桾j杆g梀s梂q梄y梇l梈p梉z梊d梋x梌t梎a梐b梑d梒h梕r梖b梘j梙h梚w梛n梜j条t梞j枭x梠l梡k梣c梥s里l梪d梬y梮j梱k梲z梴c梶w梷j梸l梹b梺x梻x梼d梽z梾l梿l棁z弃q棅b棆l棈q棊q棌c棎c棏d棐f棑p棓b棔h枨c枣z棙l棛y棜y棝g棞j栋d棡g棢w棤q棥f棦c栈z棨q棩y棪y棫y棬j棭y棯s栖q棳z棴f棶l棷z棸z棻f棽c棾q棿n椀w椂l椄j椆c椇j椈j椉c椊z椌k桠y椑b椓z椔z椕b椖p椗d椙c椚m椛h検j椝g椞x椡d椢g椣d椥z椦q椧m椨f椩g椪p椫s椬y椮s椯d椱f椲h椳w椵j椶z椷j椸y椺x椻y椼y椾j楀y楁h楃w楄p楅b楆y楇g楈x楉r杨y楋l楌y楍b楎h楏k楐j楑k楒s枫f楘m楙m楛h楜h楟t楢y楤c楥x楧y桢z楩p楪d楬j业y楯d楰y楲w楳m楴d极j楶j楺r楻h楽l楾h楿k榁m榃t榅w榊s榋c榌b榎j榏y榑b榒n榓m榖g榗j榚y榝s榞y榟z榠m榡s榢j榣y榤j榥h榩q杩m榬y荣r榯s榰z榲w榳t榵r榶t榸z榹s榺s榼k榽x榾g桤q槂s盘p槄t槅g槆c槇d槈n槉j枪q槏q槑m槒x杠g槕z槖t様y槙d槚j槜z槝d槞l槡s槢x槣j槤l槥h槦r椠q椁g槩g槪g槫t槬h槮s槯c槰p槱y桨j槴h槵h槶g槷n槹g槺k槻g槼g槾m樀d桩z樃l樄c枞c樆l樇x樈q樉s樋t樌g樍z樎s樏l梁l樒m楼l樔c樕s樖k标b樚l樛l樜z樝z枢s樠m樢n样y樤t樥p樦z樧s権q樫j樬c樭j樮y樰x樲e樳x樴z樶z樷c朴p树s桦h樻g橀x橁c橂d橃f橅m橆w桡r橉l橊l桥q橌x橍r橎f橏j橑l橒y橓s橔t橕c橖t橗m橚s橜j橝t橞h机j橠n椭t橣n橤r橦t橧z橪y横h橬q橭g橮l橯l橰g橲x橳s橴z橵z橶j橷d橸j橺x橻c橽t橾s橿j檃y檅h檆s檇z檈x柽c檊g檋j檌z檍y檏p檒f檓h档d檕j檖s檘p檙c檚c檛z桧g檝j檞j檟j檡z检j樯q檤d檥y檦b檧s檨s檭y檮c台t檰m檱q檲t槟b檴h檵j檶q檷n柠n檹y檺g槛j檼y檽n檿y櫀q櫁m櫂z柜g櫄c櫅j櫆k櫇p櫉c櫊g櫋m櫌y櫍z櫎h櫏q櫑l櫒s橹l櫔l櫕c櫖l櫗m櫘h櫙o榈l栉z櫜g椟d橼y栎l櫠f櫡z櫣l櫤j橱c櫦q槠z栌l櫩y枥l櫫z榇c櫭j櫮e櫯s櫰h櫱n櫲y栊l櫵q櫶x櫷g榉j櫹x棂l樱y櫼j櫽y櫾y欀x欁n栏l欅j欆s欇s欈w欉c权q欋q欌z欍j欎y椤l欐l欑c栾l欓d欔q欕e榄l欗l欘z欙l欚l欛b欜n欝y欥y欨x欩c欪c欫q欬k欭y欮j欯x欰x欱h欳k欴l欵k欶s欸a欻c欼c钦q欿k歀k歁k歂c歄g歅y歈y歊x歋y歍w歏j欧o歑h歒t歓h歔x歕p歖x歗x歘x歚s歛l歜c歝y欤y歠c欢h歨b歩b歫j歭z歮s歯c歰s歱z岁s歳s歴l歵z歶y归g歺d歽z歾m殁m殀y殅s殈x殌j殎q殐s殑j殔y殕f殗y残c殙h殜d殝z殟w殠c殢t殣j殇s殥y殦c殧j殨k殩c殚d殬d殓l殡b殰d殱j歼j殶z殸q殹y杀s殻k壳k殽x殾x毁h毃q毄j殴o毇h毈d毉y毊x毋w毎m毐a毑j毘p毚c毞b毟m毠j毢s毣m毤t毦e毧r毨x毩j球q毭d毰p毱j毲d毴b毶s毷m毸s毺s毻t毼h毾t毵s氀l氁m氂m氃t氄r毡z氉s氊z氋m氇l氎d氒j気q氜y氝n氞n氠s气q氥x氢q氩y氭d氱y氲y氶z氷b氹d氺s氻l氼n泛f氿g汃p汄z汅m汈d汋z汌c丸w汏d汑t汒m汓q汖p汘q污w汚w汢t汣j汥z汧q汫j汬j汭r汮j汯h汱t汳b汵g汷z汸f决j汼n汿x沀x沄y沇y沊d沋y沍h沎h沑n没m冲c沗h沘b沚z沜p沝z沞z沠p沢z沨f沬m沯z沰t沴l沵m沶y沷f沺t泀s况k泂j泃j泆y泇j泋h泍b泎z泏c泑y泒g泙p泚c泜z泝s泟c泤s泦j泧y泩s泬j泭f泲j泴g泹d洀p洃h洅z洆c洈w洉h洊j洍s洏e洐x洑f洓s洔z洕y洖w洘k洝a洟y洠s洡l洢y洣m洤q洦p洨x泄x洬s洭k洯q洰j洴p汹x洷z洸g洺m洿w浀q浂y浄j浉s浌p浐c浕j浖l浗q浘w浛h浝m浟y浡b浢d浤h浥y浧y浨l浫h浭g浱c浳y浵t浶l浃j浺c浻j浽s浾c浿p涀x涁s涃k涄p涆h泾j涊n涋t涍x涏t涐e涒t涗s涘s涙l涚s涢y涥h涬x涭s涰c涱z涳n涴w涷d涹w涺j涻s凉l涽h涾t淁q淂d淈g淉g淊y淍z淎p淏h淐c淓f淔z淕l淗j泪l淛z淜p淟t淢y淣n渌l淧m淩l沦l淭q淯y淰n淲b淴h渊y涞l淸q浅j淽z淾y淿b渀b渁y渂w渃r渄f渆y渇k済j渉s渋s渏y渒p渓x渕y渘r涣h减j渞q渟t渢f涡g渧t渨w渪r测c渮h渰y渱h渳m渵m渶y渷y渹q渻s渼m渽z浑h渿n湀g湁c湂e湅l湆q湇q湈m湉t凑c湋w湌c湏h湐p湑x湒j湗f湙y湚y湜s湝j浈z湠t湡y湢b湣m湤s湥t湦s涌c湨j湩d湪t湬q湭q汤s湰l湱h湳n湴b湵y湶q湷z湸l湹c湺x湻c湼n湽z満m溁y溂l溄f溇l沩w溊b溋y溌h溍j溎y溑s溒y溓l溔y溕m准z溗c溙t溚t溛w沟g溞s溠z溡s溣l溤m溦w溨z溩w温w溬q溭z溮s溰a溵y溸s溹s湿s溾a溿p滀c滃w沧c灭m滆g滈h滉h滊q涤d滍z荥x滐j滒g滘j滙h滛y滝l滣c滧y滪y滫x沪h滭b滮b滞z滰j滱k渗s滳s滶a卤l浒h滺y滻c滼f滽y滚g满m漀q渔y漃j漄y漅c漇x漈j漊l漋l漌j漍g漎c漑g漒q漖j漗c漘c漙t沤o漛t漜y漝x漞m漟t漡s汉h涟l漥w漦c漧g漨f渍z漮k漰p涨z漴c漵x漷h渐j漺s漻l漼c漽t浆j潀c潂h潃x潄s潅g潈z潉k潊x潌z潏y潐j泼p潒d潓h洁j潕w潖p潗j潙w潚s潜q潝x泻x潠s潡d润r潥s潧z潨c潩y潪z潫w潬t浔x溃k潱y潳t潵s潶h滗b潹c潻s潽p潾l涠w涩s澁s澂c澃j澅h浇a涝l澊c澋j澏h澐y澑l澒h澓f澔h澕h澖x涧j澘s澙x澛l澝n澞y澟l渑m澢d澣h泽d澥x澦y澨s泶x澪l澫w澬z澭y浍k澯c澰l淀d澲y澴h澷m澸g澺y澻s澼p澽j澾t澿q浊z浓n濄g濅j濆p濇s濈j濊h濋c濌t濍s濎d濏s濐z濓l濔m濖s濗m泞n濙y濚y濜j濝q济j濢c濣w涛t濥y濦y濧d濨c濩h濪q滥l濬j濭a潍w滨b濲g濳q濴y濶k濷f濹b溅j濻w泺p濽z滤l濿l瀁y瀂l瀃s瀄z滢y渎d瀈h瀊p渖s瀌b瀍c瀎m浏l瀒s瀓c瀔g濒b瀖h瀗x泸l瀙q瀜r沥l瀞j潇x潆y瀡s瀢w瀤h瀥x瀦z泷l濑l瀩d瀪f瀫h瀬l瀭s瀮l瀯y瀱j潋l瀳j瀴y瀶l瀷y瀸j瀺c瀻d瀼r瀽j澜l瀿f灀s灁y灂z沣f滠s灅l灆l灇c灈q灉y灊q灋f灍j灎y灐y洒s灒z灓l灔y灖m灗s滩t灙d灚j灛c灏h灟z灠l灡l灢n湾w滦l灥x灦x灧y灨g灪y灮g灱x灲x灳h灴h灷z灹z灺x灻c灾z炁q炂z炄n炆w炇p炈y炋p炌k炐p炑m炘x炚g炛g炞b炟d炠x炡z炢z炣k炤z炥f炦b炧x炨x炩l炪z炰p炴y炵t炶s为w炿z烄j烅x烆h烇q烉h烋x烌x烍x烎y乌w烐z烑y烒s烓w烔t烕m烖z烗k烚x烜x烝z烠h烡g烢c烣h烥c烰f烱j烲x烳p烃t烵z烶t烸h烺l烻y烼x烾c烿r焀h焁x焂s焃h焄x焅k焆j焇x焈x焋z焌j焍d焎x焏j焒l焔y焗j焛l焜k焝h焞t焟x焠c无m焢h焣j焤f焥w焧z焨f焩p焪q焫r焬x焮x焳j焴y焵g焸x焹g焻c焼s焽x焾n焿g煀q煁c煂h煃k煄z煆x煇h煈f炼l煋x煍j煏b煐y煑z炜w煓t煔s煕x煖n煗n煘c烟y煚j煛j煝m煟w煠y煡j茕q煣r焕h烦f煪q煫s炀y煭l煯j煰z煱g煴y煵n煶s煷h煹g煻t煼c煾e煿b熀h熁x熂x熃w熅y熆h熇h熈x熉y熋x熌s熍q熎y熐m熑l荧y熓w熕g熖y炝q熚b熛b熜c熝l熞j熡l熢p熣s熤y熥t熦j熧z熩h熪y熫z熭w熮l熯h熰o热r熲j熴k熶c熷z熸j熺x熻x熼y熽x炽c熿h燀c烨y燂t焰y燅x燆q燇j灯d炖d燊s燋q燍s燏y磷l燑t烧s燖x燗l燘m烫t燚y燛j焖m燝z营y燡y燣l燤t灿c燨x燩q燪z燫l烛z燯l燱y燲x燳z烩h燵t燶n燷l燸x燺h燻x烬j燽c焘d燿y爀h爂b爃r爄l爅m爇r爈l爉l爊a爋x爌k烁s爎l爏l炉l爑j爒l爓y爔x爕x爖l爘c爚y烂l爜c爞c爟g爡c爢m爣t爤l爦l爧l爩y爫z争z爮p爯c爲w爳h爴j爷y爼z尔e牀c牁k牂z牃d牄q牅y牉p牊c牋j牎c牏y牐z牑b牓b牕c牗y牍d牚c牜n牞j它t牣r牤m牥f牨g瘪b牫g牬b牭s牰y牱k牳m牶q牷q牸z牻m牼k牵q犂l犃p犅g犆z犇b犈q犉c犌j犎f犐k犑j犓c犔x犕b荦l犗j犘m犙s犚w犁l犜d犝t犞q犠x犡l犊d犣l犤b犥p犦b牺x犨c犩w犪k犫c犮q犱j犲c犳z犵g犺k犻p犼h犽y犾y犿h状z狅k狆z狇m狉p狊j狋y狌s狏t狑l狓p狕y狖y狘x狛b狜k狝x狟h狢h狣z狤j狥x狦s狧t狪t狫l狵m狶x狭x狈b狾z狿y猀s猂h猄j猅p猆f猇x猈b猉q猋b猌y猐q猑k猒y猔z猘z狰z猚y猟l猠c猣z猤g猦f猧w猨y猭c猯t猰y猲h猳j猵p犹y猺y狲s猼b獀s獁m獂h獃d狱y狮s獆h獇q獈y獊c獋h獌m奖j獏m獑c獓a獔h獕s獘b獙b獚h獛p獜l獝x獞t獟y獡s獢x獣s獤d獥j獦g獧j独d獩h狯k猃x猕m狞n獱b获h獳r獴m猎l獶n犷g兽s獭t献x獽r獿n猡l玁x玂q玃j玅m兹c玈l玊s玌q玍g玏l玐b玒h玓d玔c玕g玗y玘q玙y玚c玜h玝w玞f玠j玡y玣b玤b玥y玦j玧m珏j玪j玬d玭p玱q玴y玵a玶p玸f玹x玼c玽g玾j玿s珁c珃r珄s珅s珆y珇z珋l珌b珎z珒j珓j珔j珕l珖g珗x珘z珚y珛x珜y珝x珟s珡q珢y珣x珤b珦x珨x珪g珫c珬x佩p珯l珰d珱y珳w珴e珵c珶d珷w珸w珹c珺j珻m珼b珽t现x珿c琀h琁x琂y琄x琇x琈f琋x琌l琍l琎j琑s琒f琓w琔d琕p琖z琗c琘m琙y琜l琝m琞s琟w琠t琡s琣b琤c琧e琩c琫b琭l琯g琱d琲b琷q琸z琹q珐f琻j琽d琾j珲h瑀y瑂m瑃c瑄x瑅t瑆x瑇d瑈r瑉m玮w瑌r瑍h瑎x瑏c瑐j瑑z瑒y瑓l瑔q瑖d瑝h瑠l瑡s瑢r琐s瑶y瑥w瑧z瑨j莹y玛m瑫t瑬l瑮l琅l瑱t瑲q瑳c瑴j瑵z瑹t瑺c瑻k瑼z瑽c瑿y璂q璄j璅s璆q璈a琏l璌y璍y璏z璑w璒d璓x璔z璕x璖q璗d璘l璙l璚j璛s璝g璟j璡j璢l玑j璤h璥j瑷a璪z璫d璬j璭g璮t璯h环h璱s璲s璳t璴c璵y璶j璷l璸b璹s璻z璼l玺x璾j璿x瓀r瓁w瓂g瓃l瓄d瓅l瓆z瓇r瓈l瓉z琼q瓋t瓌g瓍s瓎l珑l瓐l瓑l瓓l璎y瓕m镶x瓗q瓘g瓙d瓒z瓛h瓝b瓟b瓡z瓥l瓨x瓩q瓪b瓫p瓬f瓭d瓳h瓵y瓸b瓹j瓺c瓻c瓽d瓾w甀z甁p甂b甃z甅l甆c甈q甉x甊l甋d瓯o甎z甐l甒w甔d瓮w甖y甗y甛t甝h甞c甠q甡s产c産c甤r甧s甪l甮f甴y甶f甹p甼t甽z甿m畁b畂m畃x畆m畇y畉f畊g畍j畐f畑t畒m畓d畕j畖w畗d畘n亩m畟j畠t畡g毕b畣d畤z畨f畩y画h畲s畭s畮m畯j异y畱l畳d畵h畷z畺j畻c畽t畾l疀c疁l疂d疄l疅j畴c疈p疉d叠d疌j疍d疎s疐z疓n疕b疘g疛z疞x疢c疦j疧q疨y疩c疭z疶x疷z疺f疻z痱f痀g痁d痆n痋t痌t痎j痏w痐h痑t痓z痗m痉j痚x痜t痝m酸s痡f痥d痩s痬y痮z痯g麻m痵j痶t痷a痸c痹b痻m痽d痾k瘂y瘄c瘆s瘇z瘈z瘉y疯f疡y瘎c瘏t瘑g瘒w痪h瘔k瘖y瘚j瘜x瘝g瘗y疮c瘣h疟n瘨d瘬z瘮s瘯c瘱y瘲z瘶s瘷s瘘l瘻l瘽q疗l癄q癅l痨l痫x癈f瘅d癋h癎x癏g癐g癑n癒y癓w癕y癗l疠l癙s癚d癛l癝l癠j痴c痒y疖j症z癧l癨h癞l癪j癣x瘿y瘾y瘫t癫d癳l癴l癵l癶b癹b発f发f癿q皀b皁z皃m皅p皉c皊l皌m皍j皏p皐g皒e皔h皕b皗c皘q皑a皛x皜h皝h皞h皟z皠c皡h皢x皣y皥h皦j皧a皨x皩h皪l皫p皬h皭j皯g疱p皳q皵q皶z皷g皲j皹j皱z隳h皼g皽z皾d盀q盁y盃b啊a阿a埃a挨a哎a哀a癌a蔼a矮a艾a碍a隘a鞍a氨a安a俺a按a暗a岸a胺a案a肮a昂a盎a凹a敖a熬a翱a袄a傲a懊a澳a芭b捌b扒b叭b吧b笆b八b疤b巴b拔b跋b靶b把b耙b霸b罢b爸b白b柏b百b佰b拜b稗b斑b班b搬b扳b般b颁b板b版b扮b拌b伴b瓣b半b办b绊b邦b梆b榜b膀b绑b棒b磅b蚌b镑b傍b谤b苞b胞b包b褒b盄d盇h盉h盋b盌w盓y盕f盙f盚q盗d盝l盏z盠l盢x监j盦a卢l盨x盩z荡d盫a盬g盭l盰g盳m盵q盷x盺x盻x盽f盿m眀m眂s眃y眅p眆f眊m県x眎s眏y眐z眑y眒s眓h眔d眕z眖k眗j眘s眛m眜m眝z眞z眡s眤n眦z眧c眪b眫p眬l眮t眰d眱d眲n眳m眴s眹z眻y眽m众z眿m睂m睄s睅h睆h睈c睉c睊j睋e睌m睍x睎x困k睒s睓t睔g睕w睖l睗s睘q睙l睁z薄b雹b保b堡b饱b抱b暴b豹b鲍b爆b杯b碑b悲b卑b北b辈b贝b钡b倍b焙b被b奔b本b笨b崩b绷b甭b泵b蹦b迸b逼b鼻b比b鄙b笔b彼b碧b蓖b蔽b毖b庇b闭b敝b弊b必b辟b壁b臂b避b陛b鞭b边b编b贬b扁b便b变b卞b辨b辩b辫b遍b彪b膘b表b鳖b憋b彬b斌b宾b兵b冰b柄b丙b秉b饼b炳b睝l睐l睟s睠j睤b睧h睩l睾g睭z睮y睯h睰m睱x睲x睳h睴h睵z睶c睷j睸m睺h睻x睼t瞁x瞂f瞃w瞆g眯m瞉k瞊d瞋c瞐m瞕z瞖y瞗d瞘k瞙m瞚s瞛c瞜l瞝c瞒m瞡g瞣w瞤s瞦x瞨p瞫s瞮c瞯x瞱y瞲x瞴w瞶g瞷j瞸y瞹a瞺h睑j瞾z矀w矁c矂s矃n矄x矅y矆h矈m矉p矋l矌k矎x矏m矐h矑l矒m矔g矕m矖x矘t矙k瞩z矝j矞y矟s矠z矡j矤s病b玻b菠b播b钵b波b博b勃b搏b铂b箔b伯b帛b舶b脖b膊b渤b泊b驳b捕b卜b哺b补b埠b不b步b簿b部b怖b猜c裁c材c才c财c睬c踩c彩c菜c蔡c餐c蚕c苍c舱c藏c操c糙c槽c曹c草c策c蹭c插c茬c茶c查c碴c搽c察c岔c差c诧c拆c柴c豺c蝉c馋c谗c缠c阐c颤c昌c猖c矦h矨y矪z矫j矰z矱y矴d矵q矷z矹w矺z矻k矼g砃d砄j砅l砆f砇m砈e砊k砋z砎j砏p砐e砓z砕s砙w砛j砞m砠j砡y砢l砤t砨e砪m砫z砮n砯p砱l炮b砳l砵b砶p砽y砿k硁k硂q朱z硄k硆e硈q硉l硊w硋a硍k硏y硑p硓l硔h硘q硙w硚q硛c硜k硞q硟c硠l硣x硖x硥m硦l硧y砗c硩c砚y硰s硱k硲y硳z硴h硵l硶c硹s硺z硻k硽y硾z硿k碀c碁q碂z碃q常c长c肠c敞c唱c倡c超c抄c钞c朝c嘲c潮c巢c吵c炒c车c扯c撤c掣c澈c郴c臣c辰c晨c忱c沉c陈c趁c衬c称c城c橙c成c呈c乘c程c澄c诚c承c逞c骋c秤c持c匙c池c迟c弛c驰c齿c侈c赤c翅c斥c充c虫c崇c抽c酬c踌c稠c愁c筹c仇c绸c瞅c丑c臭c初c出c躇c锄c雏c滁c除c楚c碄l碅j碆b碈m碊j碋h碏x碒y碔w碕q碖l碙n碝r碞y碠d碢t碤y碦k碨w硕s碪z碫d碬x砀d碮t碯n碵t碶q碷d碸f确q码m碿s磀e磃s磄t磆h磇p磈k磌t磍x磎x磏l磑w磒y磓d磖l磗z磘y砖z磛c磜q磝a磞p磟l磠l磡k磢c碜c磤y磥l磦b碛q磩z磪c磫z磮l矶j磱l磳z磵j磶x磸d磹d磻b磼j硗q磾d磿l礀j礂x礄q礆j礇y礈z礉h礊k礋z礌l础c矗c搐c触c处c揣c川c穿c椽c船c喘c串c窗c幢c床c闯c吹c炊c捶c锤c垂c春c椿c醇c唇c淳c纯c蠢c戳c绰c疵c茨c磁c雌c辞c慈c瓷c词c此c刺c赐c次c聪c葱c匆c粗c醋c簇c促c蹿c篡c窜c摧c崔c催c脆c瘁c粹c淬c翠c村c存c磋c撮c搓c措c挫c错c达d答d瘩d打d大d呆a歹d傣d戴d殆d代d贷d袋d待d逮d礍j礏y礐q礑d礒y礔p礕p礖y礗p礘e礚k礛j礜y礝r礟p礠c礡b礢y礣m礥x矿k礧l礨l礩z砺l砾l矾f礭q礮p礯y礰l砻l礲l礳m礵s礷j礸c礹y礽r礿y祂t祃m祄x祆x只z祊b祋d祌z祎y祏s佑y祑z祒t祔f秘b祙m祡c祣l祤y祦w祩z祪g祫x祬z祮g祰g祲j祳s祴g祵k祹t祻g祼g祽z祾l禄l禂d禃z禆b禇c禉y禋y禌z祸h禐y禑x禒x怠d耽d丹d郸d胆d旦d氮d但d淡d诞d蛋d党d刀d蹈d倒d祷d到d稻d悼d道d德d得d的d蹬d登d等d瞪d凳d邓d堤d低d滴d迪d笛d狄d翟d嫡d底d地d蒂d第d帝d弟d递d缔d颠d掂d滇d碘d点d典d靛d电d佃d甸d店d惦d奠d殿d碉d叼d凋d刁d掉d钓d调d跌d爹d碟d蝶d迭d谍d禓s禔z禕y禖m禗s禘d禙b禛z禜y禝j禞g禟t禠s禡m禢t禣f禤x禥q御y禨j禩s禅c禫d禬g禭s礼l禯n祢m禲l禴y禵t禶z禷l禸r禼x秃t秂r秄z秅c秇y籼x秊n秌q秏h秐y秓z秔j秖z秗y秙k秚b秛p秜n秝l秞y秠p秡b秥n秨z秪d秬j秮h秱t秲s秳h秴h秵y秶z秷z秹r秺d秼z秾n秿f稁g稄x税s稇k秆g稉j稊t稌s稏y稐l稑l稒g稓z稕z稖b稘j稙z稛k棱l丁d盯d叮d钉d顶d鼎d锭d定d订d冬d董d懂d侗d恫d洞d兜d抖d陡d豆d逗d痘d都d督d毒d读d堵d睹d赌d杜d镀d肚d度d渡d妒d端d短d锻d段d缎d堆d队d墩d蹲c敦d顿d囤d钝d盾d遁d掇d哆d多d垛d躲d朵d跺d舵d剁d惰d蛾e峨e鹅e俄e额e讹e娥e厄e扼e遏e鄂e饿e恩e而e耳e饵e洱e二e稝p禀b稡z稢y稤l稦y稧x稨b稩j稪f稫p稭j种c稯z稰x稲d稴x稵z稶y稺z稾g谷g穁r穂s穃r穄j穅k穇c穈m穊j穋l稣s颖y穏w穐q穒k穓y穔h穕q穖j穘x穚j穛z穜t穝z穞l穟s穑s秽h穣r穤n穥y穦p穧j穨t稳w穪c穬k穭l穮b穯s穱z穲l穳c穵w穻y穼s穽j穾y窂l窅y窇b窉b窊w窋z窌j窎d窏w窐w窓c窔y窙x窚c窛k窞d窡z窢h贰e罚f筏f伐f乏f阀f法f藩f帆f番f翻f樊f钒f繁f凡f反f返f范f贩f犯f饭f芳f方f肪f房f防f妨f访f纺f放f菲f非f啡f飞f肥f匪f诽f吠f肺f沸f费f芬f酚f吩f氛f分f纷f焚f汾f粉f份f忿f粪f丰f封f蜂f峰f锋f风f烽f逢f冯f缝f讽f奉f凤f否f敷f肤f孵f扶f拂b辐f幅f氟f符f伏f俘f服f窣s窤g窝w洼w窫y穷q窑y窰y窱t窲c窴t窵d窭j窷l窸x窹w窥k窻c窼c窾k竀c竁c竂l竃z窍q竆q窦d竈z竉l窃q竍s竎f竏q竐c竑h竒q竓h竔s竗m竘q竚z竛l竜l竝b竡b竢s竤h竧j竩y竫j竬q竮p竰l竱z竲c竳d竴c竵w竞j竷k竻l竼p竾c笀m笁z笂w笅j笇s笉q笌y笍z笎y笐h笒c笓b笖y笗d笘s笚d笜z笝n笟g笡q笢m笣b笧c笩f笭l浮f涪f福f袱f弗f甫f辅f俯f釜f斧f脯f腑f府f腐f赴f副f覆f赋f傅f付f阜f父f腹f负f富f讣f附f缚f咐f噶g嘎g该g改g概g钙g盖g溉g甘g柑g竿g肝g赶g感g敢g赣g钢g缸g肛g纲g港g篙g皋g高g膏g羔g糕g搞g镐g稿g哥g歌g戈g鸽g胳g疙g割g革g葛g格g蛤g阁g隔g铬g各g给g根g跟g耕g更g庚g羹g笯n笰f笲f笴g笵f笶s笷m笹t筇q笽m笿l筀g筁q筂c筃y筄y筈k筊j笋s筎r筓j筕h筗z筙l筜d筞c筟f筡t筣l筤l筥j筦g笕j筨h筩t筪x筫z筬c筭s筯z筰z筶g筸g筺k筼y筽o筿x箁p箂l箃z箅b箆b箉g箊y笺j箌z箎c筝z箑s箒z箓l箖l箘q箙f箚z箛g箞q箟j箠c箣c箤z箥p箮x箯b箰s箲x箳p箵x箶h箷s箹y箺c箻l箼w箽d箾s箿j节j篂x篃m埂g耿g梗g工g攻g功g恭g龚g供g躬g公g弓g巩g汞g拱g贡g共g钩g勾g苟g狗g垢g购g辜g菇g咕g箍g估g沽g孤g姑g鼓g古g蛊g骨g股g故g顾g固g刮g瓜g寡g褂g乖g怪g棺g关g官g冠g观g管g馆g罐g灌g贯g光g逛g瑰g规g圭g硅g闺g轨g鬼g诡g癸g桂g跪g贵g辊g棍g锅g郭g果g裹g过g哈h篅c篈f筑z篊h箧q篍q篎m篏q篐g篔y篕h篖t篗y篘c篛r篜z篞n篟q筱x篢l篣p笃d篧z篨c筛s篫z篬q篭l篯j篰b篲h筚b篴z篵c篶y篸c篹s篺p篽y篿t箦z簁s簂g簃y簄h簅c簆k簈p簉z簊j篓l簎c簐n蓑s簒c簓s簔s簕l簗z簘x簙b簚m簛s簜d簝l箪d簠f简j簢m篑k簤d簥j簨z簩l箫x簬l簭s簮z簯q簰p簱q簲p簳g簴j簵l簶l檐y簹d簺s簻z簼g签q帘l籂s骸h孩h海h氦h亥h害h骇h酣h憨h邯h韩h涵h寒h函h喊h罕h翰h撼h捍h旱h憾h悍h焊h汗h夯b杭h航h壕h嚎h豪h毫b郝h好h耗h号h浩h呵h喝h荷h菏h核h禾h和h何h合g盒h貉h阂h河h涸h赫h褐h鹤h贺h嘿h黑h痕h很h狠h恨h亨h衡h轰h哄h烘h虹h鸿h洪h宏h弘h红g喉h侯h猴h吼h厚h候h后h呼h乎h忽h瑚h葫h胡h蝴h狐h糊h湖h篮l籄k籅y籆y籇h籈z籉t籊t籎y籏q藤t籑z籒z籓f籔s籕z籖q籗z籘t籙l籚l籛j箨t籝y籞y籁l笼l籡s籢l籣l龠y仠g籦z籧j籨l笾b簖d籫z篱l籭s箩l籯y籰y籱z吁x籵f籶s籷z籸s籹n籺h籾n籿c粀z粁q粂z粃b粄b粅w粆s粇k粈r粊b粋c粌y粍z佂z粏t粐h佹g侀x粔j粖y粙z粚c粛s粠h侼b侽n粣c粦l粨b粩l粫e粬q粭h粯x粰f粴l粤y粶l粷j粸q粺b粻z弧h虎h唬h护h互h花h华h猾h滑h化h话h槐h淮h桓h还h缓h患h豢h宦h幻h荒h慌h黄h磺h蝗h簧h皇h凰h惶h煌h晃h幌h恍h谎h灰h辉h徽h恢h蛔h回h悔h慧h卉h惠h晦h贿h讳h诲h绘h荤h昏h婚h魂h混h豁h活h伙h火h或h惑h霍h货h圾j基j畸j稽j箕j粿g糀h倄y糃t糄b糆m糉z糋j糎l糏x糐f糑n糒b糓g糔x糘j糚z糁s糡j馍m糣s糤s糥n糦x粮l糩k糪b糫h糬s糭z糮x糱n粝l糳z籴d糵n粜t糷l糹s糺j糼g糽z纠j纪j紁c纣z紃x约y纡y纥g纨w纫r纹w紌q纳n紎z紏t纽n紑f紒j纾s纰p紖z纱s紘h纸z级j纭y紝r紞d紟j紣c紥z紦h紨f紩z紪q紬c紭h扎z细x绂f绁x绅s紴b紵z紶q偟h肌j饥j迹j激j讥j鸡j姬j绩j缉j吉j棘j辑j籍j集j及j急j疾j即j嫉j脊j己j蓟j技j偤y冀j季j伎j祭j悸j寄j寂j计j记j既j忌j际j妓j继j嘉j枷j佳j加j荚j颊j贾g甲j钾j假j稼j架j驾j嫁j尖j间j煎j兼j肩j艰j缄j茧j柬j硷j剪j荐j鉴j践j贱j见j键j箭j件j紷l紸z绍s绀g紻y绋f紽t紾z绐d绌c絁s终z弦x组z絅j絇q絈m絉s絊z絋k経j絍r绗h絏x傶q结j絑z絒c傸c絓g絔b绝j絖k絗h絘c絙h絚g绦t絜j絝k绞j絟q絠g络l绚x絣p絤x絥f絧t绒r絩t絪y絫l絬x絭j絯g絰d统t丝s絴x絵h絶j絸j绢j絺c絻w絼z絽l絿q綀s綂t绡x綄h綅q绠g綇x绨t綉x綊x綋h綌x綍f綎t绥s綐d捆k綒f经j綔h綕z綖y綗j綘f健j舰j饯j建j僵j姜j江j疆j蒋j讲j匠j酱j降j蕉j椒j礁j焦j胶j交j郊j骄j嚼j铰j脚j狡j角j饺j缴j教j酵j轿j较j叫j窖j揭j接j皆j秸j街j阶j截j劫j桔j捷j睫j竭j姐j戒j藉j芥g界j借j介j疥j诫j巾j筋j斤j金j兠d今j津j襟j紧j锦j谨j进j靳j禁j近j浸j継j続x综z綝c缍d綟l绿l綡j绻q綤s綧z綨j綩w綪q綫x绶s维w綯t绾w网w綳b缀z綶g綷c纶g绺l绮q绽z綼b绫l绵m緀q緁j緂t緃z绲g緅z緆x缁z緈x緉l绯f緌r緍m緎y総z緐f緑l绪x緓y緔s緕z緖x缃x缂k线x緜m緟c缗m緢m缘y緤x緥b缌s緧q緪g緫z缅m緭w緮f纬w緰x缑g缈m緳x练l緵z缏b緷g緸y缇t緺g荆j兢j茎j睛j晶j鲸j京j惊j精j粳j井j警j景j颈g静j境j敬j镜j靖j竟j炯j窘j揪j究j玖j韭j久j灸j九j酒j救j旧j臼j舅j咎j就j疚j鞠j拘j狙j疽j居j驹j菊j咀j矩j举j沮j聚j拒j巨j具j距j剏c踞j锯j俱j句g炬j捐j鹃j娟j倦j眷j撅j攫j抉j掘j倔j爵j觉j诀j均j菌j钧j军j君j峻j致z緼w緽c緾c緿d縀x縁y縂z縃x縄s縅o縆g縇s萦y缙j缢y缒z縌n縍b縎g縏p绉z缣j縒c縓q縔s縕y縖x縗c縘x縙r縜y缜z缟g缛r縠h縡z縢t县x縤s縥z縦z縧t縨h縩c縪b縬c缡l缩s演y縰x纵z缧l纤q缦m絷z缕l縸m缥p縺l縼x总z縿s繀s繂l繄y缫s缪m襁q繉s繊x繋j繌z繍x繎r繐s缯z繓z织z缮s繖s繗l繘j繙f缭l繛c繜z勩y俊j竣j浚j郡j骏j喀k咖g卡k咯g开k揩k楷j慨k刊k堪k勘k坎k砍k看k康k慷k糠k扛g抗k亢g炕k拷k烤k靠k坷k苛k柯k棵k磕k颗k科k咳h可k渴k刻k客k课k肯k啃k坑k吭h空k恐k孔k控k口k扣k寇k枯k哭k窟k苦k酷k裤k夸k垮k挎k跨k胯k筷k快k款k筐k狂k框k眶k亏k盔k葵k奎k魁k傀g绕r繟c繠r绣x缋h繣h繤z繥x繦q繧w繨d绳s繬s繮j缳h缲q繱c繲x繴b繵d繶y繷n繸s绎y繺s繻x缤b缱q繿l纀p纁x纃z纅y纆m纇l缬x纉z纩k续x累l纎x纐k纑l纒c缨y厠c纗z缵z纙l缆l纝l纞l纮h纴r纻z纼z绖d绤x绬y绹t缊w缐x缞c缷x缹f缻f缼q缾p罀z罁g罃y罆g罇z罉c罊q罋w罂y罍l罎t罏l罒w罓w馈k愧k坤k昆k括g廓k阔k垃l拉l喇l蜡l腊l辣l啦l莱l赖l蓝l婪l阑l兰l谰l览l榔l狼l廊l郎l朗l浪l牢l老l佬l姥l酪l烙l勒l雷l镭l蕾l磊l儡l肋l类l楞l冷l厘l梨l黎l狸l离l理l李l鲤l莉l荔l吏l丽l利l傈l例l俐l罙s罛g罜z罝j罞m罠m罣g罤t罥j罦f罧s罫g罬z罭y罯a罳s骂m罶l罸f罺c呩s罻w罼b罽j罿c羀l羂j羃m羄z罴p羇j羁j羉l羍d羏y羐l羑y羓b羕y羖g羗q羙m羠y羢r羟q羡x义y羬x羱y羴s羺n羾h翀c翃h翆c翇f翈x翉b翋l翍p翏l翐z翑q习x翓x翖x翗k翙h翚h翛x翜s翝h翞j翢d翣s哷l痢l立l粒l隶l力l璃l哩l联l莲l连l镰l廉l脸l链i啅z粱l良l辆l量l晾l亮l谅l撩l聊l僚l燎l寥l辽l潦l撂l镣l廖l料l列l烈l劣l琳l林l霖l临l邻l鳞l淋l赁l啙z啛c吝l拎l玲l菱l零l龄l铃l伶l羚l凌l灵l陵l领l另l令l溜l琉l榴l硫l馏l留l瘤l流l柳l六l龙l聋l窿l翤c翧x翨c翪z翫w翬h翯h翲p翴l翵h翶a翷l翸p翘q翽h翾x翿d耂l耇g耈g耉g耊d耎n耏n耓t耚p嗘j耝q耞j耟j耡c耣l耤j耫z耧l耭j耮l耯h耰y耲h耴y耹q耺y耼d耾h聀z聄z聅c聇z聈y聉w聏e聐y聑t聓x聕h圣s聗l聙j聛b聜d聝g闻w聟x聠p聡c聢x聣n聤t聥j聦c聧k聨l聫l聬w聭k聮l声s耸s聴t聩k聂n职z聸d聍n聺q聻n嘓g聼t听t拢l陇l漏l陋l芦l颅l虏l鲁l麓l碌l露l路l赂l鹿l潞l录l陆l驴l铝l旅l履l氯l律l率l卵l掠l略l轮l噵d论l萝l螺l逻l锣l骡l裸g落l洛l嚍j骆l嚒m蚂m马m嘛m埋m买m麦m卖m迈m脉m馒m蛮m蔓m曼m慢m漫m肁z肂s肃s肈z肊y肍q肎k肏c肐g肑d囄l肔c肕r肗r肙y肞c肣q肦b肧p肨p肬y肰r肳w肵q肶p肹x肻k胅d胇b胈b胉b胊q坺f垀h胏z胐f胒n胓p胔z胕f胘x胟m胠q胢q胣c胦y胮p胵c胷x胹e埱c埶y胾z胿g脀z脁t脃c脄m胁x脇x脋x脌n脕w脗w脙x胫j脜r脝h脟l脠s脡t脢m脤s脥j塈x睃s脨c修x脪x脱t脭c脮n脰d脳n脴p脵g脷l胀z脼l脽s脿b谩m芒m墽q茫m盲m氓m壔d忙m莽m猫m茅m锚m毛m矛m铆m卯m茂m冒m帽m貌m贸m么m玫m枚m梅m酶m霉m煤m眉m媒m镁m每m美m昧m寐m妹m媚m门m萌m檬m盟m锰m猛m孟m醚m靡m糜m迷m谜m米m觅m泌b蜜m密m棉m眠m冕m免m勉m娩m面m苗m妌j描m瞄m藐m秒m妛c妭b渺m妙m蔑m民m抿m皿m敏m姃z闽m明m螟m鸣m铭m名m命m谬m摸m腀l腁p腂g腃j腄c腅d腇n腉n腍r肾s腏z腒j腖d腗p腘g腛w腜m腝r腞z娊x脶l腢o腣d腤a脑n腨s腪y肿z腬r腯t腲w腵j婂m腶d腷b膁q腽w膄s膅t膆s膇z膉y膋l膌j膍p膎x膐l膒o膓c膔l膕g膖p膗c膙j膞z膟l膡y膢l膤x膥c膧t腻n膫l膬c膭k膮x媑z膯t膰f膱z膲j膴h媝q媡l膵c膶r膷x膸s膼z脍k脓n臄j臅c臇j臈l臋t脐q臎c膑b臐x臑n嫿h嬃x嬓j臓z摹m蘑m模m嬜x膜m磨m摩m嬟y魔m抹m末m莫m墨m默m沫m漠m寞m陌m谋m牟m某m拇m牡m姆m母m墓m暮m幕m募m慕m木m目m孨z睦m牧m穆m拿n哪n孶z钠n那n娜n氖n乃n奶n耐n奈n南n男n难n囊n闹n淖n呢n馁n嫩n能n妮n霓n倪n泥n尼n匿n逆n溺n蔫n拈n年n碾n酿n鸟d尿n捏n孽n镊n镍n涅n您n凝n臔x臕b臖x臗k臙y胪l臛h臜z臝l臞q脏z脔l臡n臢z卧w臦g臩g臫j臮j臯g臰c岅b臲n臵g臶j臷d臸z臹x岟y臽x臿c舃x与y兴x岪f岾z峊f舎s舏j舑t峣y舓s舕t铺p舗p峮q舘g舙h舚t舝x舠d舤f舥p舦t舧f舩c舮l舲l舼q峾y舽p艀f艁z艂f艃l艅y艆l艊b艌n艍j艎h艐k艑b艒m艓d艔d嵽d嶈q艖c艗y艛l艜d艝s艞y艠d艡d艢q舣y艥j艧h艩q牛m扭n钮n嶪y农n弄l奴n努n怒n女n暖n虐n挪n懦n糯n诺n哦e巆y鸥o藕o偶o巏q啪p趴p爬p帕p怕p琶p拍p排p牌p徘p湃p派p攀p潘f磐p盼p畔p判p叛p乓p庞p耪p胖p咆p刨b袍p跑p泡p呸p胚p培p裴p赔p陪p配p沛b盆p砰p抨p烹p澎p彭p蓬p棚p硼p篷p膨p朋p鹏p捧p碰p坯p砒p霹p批p披p劈p琵p毗p艪l舻l艬c艭s艵p艶y艳y艹a艻l艼d幯j庅m庺s芃p芅y芆c芇m廐j芉g芌y苄b芓z芔h芕s弐e芚t芛w芞q芠w芢r芣f芧x芲l弞s芵j芶g芺a刍c芼m芿r苀h苂c苃y苅y苆s苉p苐d苖d苙l苝b苢y苎z苨n苩p苪b苬x苭y苮x苰h徛j苲z徦j苳d苵d苶n苸h苺m苼s苽g苾b苿w徴z茀f徸z茊z茋z茐c茒y茓x茖g茘l忇l茙r茝c茞c怓n茠h茡z茢l茣w茤j茥g茦c茩h茪g茮j怱c茷f茻m茽z啤p脾p疲p皮p匹p恎d痞p僻p屁p譬p篇p偏p片p骗p恜c飘p瓢p票p撇p瞥p拼p频p贫p品p聘p乒p坪p苹p萍p平p瓶p评p屏b坡p颇p婆p破p魄b迫p粕p剖p莆p葡p菩p蒲p埔b圃b普p浦p谱p曝b瀑b期j欺q妻q七q漆q柒q沏q其j棋q奇j歧q畦q崎q愂b齐j祈q祁q起q岂q乞q企q契q器q迄q汽q泣q愪y讫q掐q茾q茿z荁h荂f荄g荅d愲g荈c荋e荌a荍q荎c荓p荕j荖l荗s荘z荙d荝c荢z慃y荰d荱w荲l荳d荴f荵r荶y荹b荺y荾s荿c莀c莁w莂b莃x莄g莇z莈m庄z莋z莌t莏s莐c莑p莔m莕x憉p莗c莙j莚y莝c莟h莡c莣w莤s莥n莦s憜d苋x莬w莭j莮n憡c莯m莵t莻n莾m莿c菂d菃q菄d菆c菈l菉l菋w菍n菎k菐p菑z菒g菓g菕l菗c憿j菙c菚z菛m菞l菢b菣q菤j菦q菧d菨j菫j懏j菬q菭t恰q洽q钎q铅q千q懡m迁q仟q谦q乾q黔q钱q钳q前q遣q谴q嵌k欠q歉q腔q羌q蔷q橇q锹q敲q悄q瞧q巧q鞘q撬q峭q俏q切q茄j且j怯q戵q侵q亲q秦q琴q勤q芹q擒q禽q沁q青q轻q卿q清q擎q晴q氰q情q顷q请q秋q丘q邱q求q囚q抯z酋q泅q趋q蛆q曲q躯q屈q驱q渠q菮g菳q庵a菵w拲g菶b菷z菺j挌g菼t菾t菿d萀h萅c苌c萈h萉f萐s萒y萓y萔t萕q萖w萗c萙k萚t萛j萞b萟y萠p萡b萢p萣d萩q萪k萫x万m萭y萮y萯f萰l萲x萳n萴c掤b莴w萶c萷s萹b萺m萻a萾y葀k葁j葂m葃z葄z葅z葇r葈x叶x葊a葋q葌j葍f葎l葏j葐p荭h葓h搮l摀w葕x葖t葘z葝q葞m葟h葠s葢g葤z摖q葥q苇w葧b葨w葪j葮d药y葰j葲q葴z摾j葹s葻l葼z取q娶q龋q趣q去q圈j颧q醛q泉q全q痊q拳q犬q券q撧j缺q炔g瘸q鹊q榷q裙q群q然r燃r冉r染r瓤r壤r攘r嚷r让r饶r惹r壬r擟m仁r人r忍r韧r任r认r刃r妊r扔r仍r日r戎r茸r擿t攂l蓉r融r熔r溶r攚w容r冗r揉r柔r肉r茹r蠕r儒r孺r如r辱r乳r汝r攺y入r褥r软r阮r蕊r瑞r锐r闰r若r弱r撒s萨s腮s鳃s塞s赛s三s叁s葽y葾y葿m蒀y蒁s蒃z蒄g蒅s蒆x蒍w蒏y蒐s蒑y蒒s蒓c莳s蒕y蒖z蒘r蒚l蒛q蒝y莅l蒟j蒠x蒢c蒣x蒤t蒥l蒦h蒧d蒨q蒩z蒪p蒫c蒬y蒭c蒮y蒰p蒱p蒳n斮z蒵x斳q蒷y蒻r蒾m荪s蓂m蓃s蓅l蓆x蓇g蓈l蓌c蓎t蓏l蓒x蓔y蓕g蓗z蓘g蓙g蓚t蓛c蓜p蓞d蓡s旤h蓢l蓤l蓧d蓨t蓩m蓪t蓫z蓭a苁c蓱p蓲q蓳j蓴c蓵j蓶w蓷t蓸c蓹y蓺y蓻z荜b蓾l蔀b蔁z蔂l散s桑s嗓s搔s骚s嫂s瑟s色s森s僧s莎s砂s沙s傻s啥s晇x煞s珊s苫s杉s山s煽s衫s闪s陕s擅s赡s膳s善s汕s晬z扇s商s赏s晌s上s尚s裳c梢s捎s稍s芍s勺s韶s少s哨s邵s奢s赊s蛇s舌s赦s射s涉s社s设s砷s申s呻s伸s身s深s娠s神s沈s暎y暔n甚s慎s生s甥s牲s蔃q暭h蔄m蔅y蔇j蔈b蔉g蔊h蔋d蔍l蔎s蔏s蔐d蔒h蔕d蔖c蔘s蔙x蔛h蔜a曌z曗y曻s蒌l蔠z蔢p蔤m茑n蔧h蔩y蔪j荫y蔮g蔯c蔰h蔱s蔲k蔳q枈p蔵z蔶z蔾l蔿w蕀j荨q蕂s蕄m蕅o蒇c蕇d蕋r蕌l蕍y荞q蕏z蕐h蕑j芸y蕔b莸y蕗l柛s荛r蕚e蕛t蕜f蕝j蕟f蕠r柨b蒉k蕣s蕥y蕦x蕧f柸b芜w蕫d蕬s萧x栭e蕯s蕰y蕱s蕳j蕵s蕶l蓣y蕸x桗d蕼s蕽n蕿x薀y薁y桜y省s盛c剩s失s施s诗s尸s虱s十s石d拾s什s梍z食s蚀s识s史s矢s使s屎s驶s始s式s示s士s世s梫q柿s事s拭s誓s逝s嗜s噬s适s棃l仕s侍s释s棇c饰s氏s市f恃s室s视s试s收s手s首s守s授s售s受s椃h瘦s蔬s梳s殊s抒s输s叔s舒s淑s椘c疏s赎s孰s熟s薯s暑s曙s署s蜀s黍s鼠s述s束s戍s竖s墅s庶s漱s薂x薃h楕t楖j楡y荟h薉h薋c芗x薍w薎m薐l薒c薓s薕l薖k榐z薗y薘d薙t薚t薝z薞s槀g薠f薡d薢x薣g薥s槁g薫x薬y薭b薱d槗q薲p薳y薴n薵c薶m薸p荠j薻z薼c薽z薾e薿n藀y藂c藃x藄q藅f藆j藇x藈k藊b藋d藌m荩j槸y藑q藒q藔l藖x藗s樐l藘l藙y藚x藛x艺y藞l藟l藠j藡d藢z藣b藦m藧h藨b薮s藫t藬t藭q藮q藯w藰l樼z樿s藲o藳g藴y藵b苈l藸z橩q恕s刷s耍s摔s衰c甩s栓s拴s霜s双s爽s谁s水s睡s吮s瞬s顺s舜s说s朔s斯s撕s嘶s思s私s司s死s肆s寺s嗣s四s似s饲s巳s松s颂s送s宋s讼s诵s搜s艘s嗽s酥s俗s素s檪l速s粟s僳s塑s溯s宿s诉s蒜s算s檾q櫈d櫐l虽s隋s随s髓s碎s穗s遂s隧s祟s櫢s梭s櫴l唆s索s锁s所s櫿y塌t他t她t欃c塔d蔺l欟g欦q藽q藾l歞e蘁w蘂r歬q殏q蕲q蘈t蘉m蕴y蘌y蘍x蘎j蘏j蘐x殭j毥x蘔j蘕p蘖n蘘r蘙y藓x蘛y蘜j蘝l蔹l蘟y蘠q蘡y茏l蘣t蘤h蘥y蘦l蘨y蘪m蘫l蘬g蘮j蘯d蘰k蘱l蘲l蘳h蘴f蘵z蘶w蘷k汦z汻h沕m蓠l蘻j蘽l蘾h虀j虁k泈z泘h虃j泿y虄s虅t洜l虇q虈x虉y虊l虋m虌b虒s虓x虖h虗x虘c虙f虚x虝h虠j浰l虡j虣b浲f虤y虥z虦z涖l涶t虩x虪s蹋t踏t胎t淃j抬t泰t酞t太t汰t坍t贪t檀t痰t潭t谭t谈t坦t毯t袒t碳t探t炭t渜n塘t搪t堂t棠t膛t唐t糖t倘c躺t淌t趟t掏t滔t萄t桃t逃t淘t陶t讨t套t特t腾t疼t誊t梯t湕j剔t踢t锑t提d题t蹄t啼t体t替t嚏t惕t涕t剃t湲y天t添t填t田t甜t恬t舔t腆t挑t眺t跳t贴t铁f帖t虭d虯q虰d虳j虴z虵s溳y滖s滜z滵m虸z蚃x蚄f漐z蚆b蚇c蚈q蚉w蚎y蚏y蚐j蚑q蚒t漹y潎p蚖y潣m蚗j蚘h蚙q蚚q蚛z蚞m蚟w蚡f蚢h蚥f蚦r蚫b澚a蚮d蚲p蚳c蚷j蚸l蚹f蚻z蚼g蚽p蚾p蛁d蛂b蛃b蛈t蛌t蛍y蛒g蛓c蛕h蛖m蛗f蛚l蛜y蛝x蛠l濵b蛡y蛢p蛣j濸c蛥s蛦y蛧w蛨m蛪q蛫g蛬q瀀y瀇w瀐j蛵x蛶j蛷q蛱j蜕t蛼c蛽b蛿h蜁x蜄s蜅f蚬x蜋l蜌b蜎y蜏y蜐j蜑d蜔d蜖h汀t廷t停t亭t庭t挺t艇t通t桐t酮t瞳t同t铜t彤t童t桶t灜y捅t筒t痛t偷t投t头t灩y透t凸t突t徒t途t屠t土t吐t兔t湍t推t颓t腿t褪t退t吞t屯t臀t拖t托t炏y鸵t陀t驮d炓l炗g驼t妥t拓t唾t挖w哇w蛙w娃w瓦w袜w炲t歪w外w豌w玩w顽w烷w完w碗w挽w晚w皖w惋w宛w烞p婉w腕w汪w王w烮l亡w枉w往w旺w望w忘w妄w威w蜙z蜛j蜝q蜟y蜠j蜤s蜦l蜧l蜨d蜪t蜫k蜬h蜭h蜯b蜰f蜲w蜳d蜵y蜶s蜸q蜹r蜺n蜼w蜽l蝀d蝁e蝂b蝃d蝄w蝅c焭q蝆y焲y蝋l蝍j蝏t焷p蝐m蝑x蝒m蝔j蝘y蝚r蝛w蝜f蝝y蝞m蝟w蝡r蝢x虾h蝧y蝨s蝩c蝪t蝫z蝬z蝭d蝯y蝱m蝲l蝳d蝵q蝷l蜗w蝹y蝺q蝿y螀j螁b蛳s螆c螇x螉w螊l螌b螎r螏j螑x螒h螔y螕b螖h螘y螙d螛h螜h螝g螠y螡w萤y螣t螤z巍w微w危w韦w违w桅w唯w惟w萎w委w尾w未w蔚w味w畏w胃w喂w魏w位w渭w谓w尉w慰w卫w瘟w蚊w文w燰w吻w紊w嗡w翁w爁l我w斡w握w沃w巫w钨w诬w屋w爗y爙r梧w吾w武w爠q五w捂w午w爥z舞w伍w侮w戊w雾w晤w物w勿w悟w误w昔x熙x析x西x硒x矽x晰x嘻x吸x牔b锡x螥c螦s螧q螩d螪s螮d螰l螱w螲d螴c螶q螷p螸y螹c蝼l螼q螾y螿j蟁w蟂x蟃w蛰z蟅z蝈g蟉l蟌c蟍l蟎m蟏x蟐c蟔m蟕z蟖s蟗q蟘t蟙z蟚p蟜j蟝q蟞b蟟l蟡g蟢x虮j蟤z蟦f蟧l蟨j蟩j蟫y狔n狚d蛲n猍l蟰x猏j蟱w蟳x蟴s蟵c蛏c蟷d蟸l蟺s蚁y蟼j蟽d蟿q蠀c蠁x蠂s蠄q蝇y虿c蠇l蠈z蠉x蠋z猽m蠌z蠍x蠎m蠏x蛴q蝾r蠒j蚝h獉z蠗z蠘j蠙b蠚h蠜f蠝l蠞j蠠m蛎l稀x息x希x悉x膝x夕x惜x熄x烯x溪x汐x犀x檄x袭x席x媳x喜x铣x洗x獹l隙x瞎x匣x霞x辖x暇x下x夏x掀x锨x先x仙x鲜x咸x贤x舷x闲x涎x嫌x显x险x腺x馅x陷x限x相x香x箱x襄x湘x乡x翔x祥x详x想x响x享x项x巷h橡x像x象x硝x霄x削x哮x销x消x宵x淆x蠤q蠥n蠦l蠧d蠨x蠩z蠪l蠫l蠬l蠭f蠮y蠯p蠰n蠳y蠴s蠵x瑊j瑘y蠸q瑦w瑸b璊m蠽j蠾z蠿z璠f衃p衆z衉k衋x衎k衏y衐q衑l衒x衕t衘x衦g衧y衪y甇y衱j衳z衴d衵r衶z衸j衺x衻r衼z袀j袃c袆h袇r畄l袉t袊l袌p袎y袏z袐b袑s袓j袔h袕x袗z袘y袙p畞m袚f畧l袝f袟z袠z袡r袣y袥t袦n袧g袨x袩z袪q小x孝x校j肖x疜x笑x效x楔x些x歇x疪b蝎x鞋x邪x斜x谐x械x卸x蟹x懈x痟x谢x屑x薪x痭b芯x锌x欣x辛x新x忻x心x信x衅x星x腥x猩x惺x刑x型x形x邢x行h醒x杏x性x姓x兄x胸x瘹d匈x雄x癁f熊x休x羞x朽x嗅x癊y锈x秀x袖x墟x戌q需x须x徐x许x蓄x酗x癦m旭x序x畜c絮n婿x轩x喧x宣x旋x玄x袬y袮m袯b袲c袳c袵r袶p袸j袹b袺j袻e袽r袿g裀y裃k裄h裇x裈k裋s裌j裍k裐j裑s裓g裖z裗l裚j裛y装z裞s裠q裦b裧c裩k裪t裫y裬l裭c裮c裯c裲l裵p裶f裷y裺y裻d制z裿y褀q褁g褃k褄q褅t褆t褈c褉x褋d褌k褍d褎x褏x褑y褔f褕y褖t褗y褘h褜p褝d褞y褟t褠g褢h褣r褤y褦n褧j褨s褩b褬s褭n褮y褯j褱h裢l褵l褷s选x眩x靴x薛x穴x雪x血x熏x循x旬x询x驯x巡x殉x汛x训x讯x逊x迅x押y鸦y鸭y丫y芽y牙y蚜y崖y衙y涯y雅y盶y讶y焉y阉y淹y盐y研y蜒y延y言y颜y阎y炎y沿y奄y掩y眼y衍y堰y燕y雁y唁y宴y谚y验y殃y央y鸯y秧y佯y羊y洋y氧y仰y养y漾y邀y腰y妖y褛l褹y褺d亵x褼x褽w褾b褿c襀j襃b襅q襆f襇j襈z襉j襊c襋j襌d襍z襎f襏b襑x襒b襓r襔m襕l襗z襘g襙c襚s襛n襜c裣l裆d襡s襢z襣b褴l襥f襧z襨t襩s襫s襭x襮b襰l襱l襳x襴l襵z襶d襷t襸z襹s襺j襼y襽l襾y覀y覂f覄f覅f覇b覉j瞈w覊j瞏q覌g覍b覎y瞔z覐j覑p覒m覔m覕p覗s觇c覙z覚j覛m覜t覝l覞y覟z覠j觋x遥y谣y姚y咬y舀y要y矊m耀y椰y噎y耶y野y冶y也y页y掖y腋y夜y液y一y壹y医y揖y铱y依y伊y衣y颐y夷y遗w移y胰y疑y沂y宜y姨y彝y椅y倚y已y乙y矣y以y抑y易y邑y屹g役y臆y逸y肄y疫y亦y裔y意y毅y益y溢y诣y议y谊y译y翼y翌y茵y因y音y阴y姻y吟y银y淫y寅y饮y尹y引y隐y覢s覣w覤x覥t觎y覧l矲b覨e覩d觊j覭m覮y觏g覰q覱z觐j観g覴d覵j覶l觑q覸j硡h硢y覻q覼l覾s觌d觃y觍t觕c觗z觘c觙j觛d觝d觟h觠q觡g觢s觤g觧j觨h觩q觪x觬n觭j觮j觰z觱b觲x觞s觵g觯z觷x觹x觺y觻l觼j觽x觾y觿x訁y訄q訅q訆j訉f訋d讧h訍c訏x讦j訑y訒r訔y讪s訙x訚y印y英y鹰y迎y赢y盈y影y硬y映y臃y庸y雍y踊y蛹y咏y碐l泳y永y恿y勇y用y幽y悠y尤y由y邮y铀y油y游y酉y碻q有y碽g友y磂l右y釉y诱y又y幼y迂y淤y于y盂y榆y虞y愚y舆y余y俞y逾y鱼y愉y渝y磭c隅y予y磰s雨y禹y宇y语y羽y玉y域y礃z芋y郁y遇y喻y峪y愈y育y誉y訞y訠s訡y讷n訦c訧y訨z訩x訫x訬c訮y訯s訰z訲y訳y訵c诃h訷s訸h訹x诊z注z证z訽g訿z詀z诂g詂f詃j詄d詅l诋d詇y詊p詋z詌g詍y讵j詏y诈z詑t诒y詓q诏z詖b礶g祍r祘s诎q詙b詚d诅z詜t詝z詟z诩x祱s詤h詥h祶d詧c詨x詪h诟g禈h诠q詯h诘j祯z詴w诜s詶c詷t詸m詻l诙h詽y詾x诖g誀e浴y寓y裕y预y豫y驭y鸳y冤y元y垣y袁y原y援y辕y猿y源y远y苑y愿y怨y院y曰y越y跃y钥y月y阅y耘y云y郧y陨y允y运y酝y韵y孕y匝z砸z杂z栽z哉z宰z载z再z在z赞z赃z葬z遭z糟z凿z藻z早z澡z蚤z躁z噪z造z皂z灶z燥z责z贼z怎z增z憎z曾c赠z喳c秢l渣z札z轧g誁b誂t誃y诔l诛z诓k誈w誋j志z誏l誐e诳k诶a誔t誖b誗c誙k诮q誛q誜s誝a誟x誢x诰g誧b誩j誫z説s読d誮h誯c誱j誳q誴c誵x谇s誷w誸x誺c誻t誾y諀p諁z谄c諃c谆z諅j諆j諈z诿w諊j諌d诤z諎z诹z諐q诼z諓j諔c諕x谂s諘b諙h諚b谀y諝x稬n谝p諟s諠x諡s诨h諣h稸x穉z铡z闸z眨z榨z咋z穙p乍z炸z摘z斋z穠n宅z窄z寨z瞻z詹z粘n沾z辗z展z蘸z站z湛z樟z章z彰z漳z掌z杖z丈z账z仗z瘴z障z招z昭z找z沼z赵z照z罩z兆z肇z召s遮z折s哲z辙z者z锗z蔗z这z浙z珍z斟z真z甄z砧z臻z贞z针z枕z疹z震z振z镇z阵z蒸z征z怔z整z拯z正z政z谔e諥z谛d諨f諩p諪t谏j諬q谕y谘z諯z諰x諲y谙a諴x諵n谌c诸z諹y諻h谖x窧z窽k谒y竌c竨d竪s诌z謆s謈b謉k謋h謌g謍y謏x竸j谧m謑x謒q謓c谑x謕t谡s謘c谥s謜y謞h謡y謢l謣y謤b謥c謧l谟m謩m謪s谪z謭j謮z謯j謰l謱l讴o謴g謵x謶z謷a謸a謺z謻y謼h謽j謿c譀h譂c譃x譄z譅s郑z芝z枝z支z吱z蜘z知z肢z脂z汁z之z直z植z殖s值z址z指z止z趾z旨z至z置z峙s智z秩z稚z质z炙z痔z治z窒z中z盅z筳t筴c忠z钟z衷z重c仲z舟z箈t周z州z洲z粥y轴z肘z帚z咒z宙z骤z珠z株z蛛z猪z逐z竹z煮z拄z主z著z柱z助z蛀z贮z铸z譆x譇z譈d譊n譋l譌e谲j譐z譑j譒b譓h譔z譕w谮z譗z谯q譛j譝s譞x譠t譡d譢s譣x譤j譥j譧z譨n譩y谵z譭h譮h譱s譲r譳r篒y譶t譸z譹h譺a譻y譼j譾j譿h讁z讂j讃z讄l讅s讆w讇c讉y讋z篻p讌y讍e雠c讏w讐c讑y讔y谶c讗x讘n讙h讛y谠d讝z谳y讟d讬t讱r讻x诇x诐b诪z谉s谞x住z祝z驻z抓z爪z拽z转z撰z赚z篆z撞z椎c锥z追z赘z捉z拙z卓z桌z琢z茁z酌z啄z着z灼z咨z资z姿z滋z淄z孜z紫z仔z籽z滓z自z字z鬃z棕z踪z宗z邹z走z奏z揍z租z足z卒c族z祖z阻z钻z纂z嘴z醉z最z罪z尊z遵z昨z左z佐z柞z做z作z坐z座z谸q谹h谺x谻j谼h谽h谾h谿x豀x豂l豃h豄d豅l豋d豍b豏x豑z豒z豓y籋n豖c豗h豘t豙y豛y豜j豝b豞h豟e豠c豣j豤k豥g豦j豧f豨x豩b豭j豮f豯x豰h豱w豲h豴d豵z豷y豻a豼p豽n豾p豿g貀n貁y貃m貄s貆h貇k貈h貋a貎n貏b貐y貑j貒t貕x貖y貗j貙c貚t貛h貜j粎m粓g貟y粡t貣d貤y貦w貭z亍c丌q兀w丐g廿n卅s丕p丞c鬲g孬n噩e丨g禺o丿p匕b乇z夭a爻y卮z氐d囟x胤y馗k毓y鼗t丶z亟j鼐n乜m亓q孛b嘏g厝c厥j靥y赝y匚f叵p糂s匾b赜z卦g卣y刂d刈y刎w刳k剌l糛t剞j剡s剜w蒯k剽p劂j劁q劐h劓y冂j罔w亻r仃d仉z仨s糿g仫m仞r仳p伢y佤w仵w伉k紤j佞n佧k攸y佚y佝g貮e贳s貱b赀z貵p貹s贶k絾c綛k綥q緛r繏x繑q繝j纄p纋y纕x纚l缿x翄c翭h翺a耛s聎t肒h肸x胋t胑z胻h脦t脺c脻j腟c臒w臤q臱m舋x舿k艕b芀t芁q芖z苚y茟y茰y莍q菻l萂h萿k葔h蔝m蔨j蔴m蕮x薆a莶x藱h藼x蘀t蘃r蘒q蘓s蘹h虂l虆l虨b虶y虷h蚅e蚔q蚭n蚿x蛯e蝖x螐w螚n蟭j蠷q蠺c衂n衭f袛d袾z襂s襐x覫p覹w觓q訤x詉n詗x詺m誎c誽n諽g諿x謃x謟t謲c譍y譪a譵d貥h乩j仡g斛h颢h銮l乊h乥h夫f兓j兙s兛q兝g兞h兣g兺b囻g坟f妢f婲h孒j尅k屗u岎f帉f幥z幩f弅f徚u怭b怾g慙c昐f晲n曢u朆f朌f朑u朩p枌f梤f乐l橨f檂n欂b毜c毮s洂y涜d颍y澵z炃f炍p炾h烪u焑y焺s燌f燓f燞u燢x獖f瓧s瓰f瓱m瓲t瓼l榛z彳c夔k璞p丟d並b丬p乂y乄s亙g亞y亠t亳b仝t伋j伕f伲n伽g佇z佈b佔z佗t佘s佟t佪h佴e併b佶j佻t佼j佾y侃k來l侉k侏z侑y侔m侖l侶l侷j俅q俎z俑y俚l俜p俟q俠x俳p俸f俾b倆l倉c個g倌g倏s們m倖x倜t倣f倥k倨j倩q倫l倬z倭w倮l偃y偈j偉w偌r偎w偕x側c偵z偺z偽w傖c傘s備b傢j傭y傳c傴y債z傷s傺c傾q僂l僅j僇l僉q僑q僕p僖x僥j僦j僬j僭j僮t僱g價j儀y儂n億y儅d儆j儇x儉j儋d儐b儔c儕c儘j優y儲c儷l儸l儺n儻t儼y兌d兒e兕s兗y內n兩l兮x冊c冑z冖m冢z冥m冪m冫b冱h冼x冽l凇s凈j凍d凫f凱k凵k凼d刖y別b刪s剄j則z剋k剎c剛g剝b剮g剴k創c剷c劃h劇j劉l劊g劌g劍j劑j劬q劭s劻k劾h勁j勐m動d勗x務w勛x勝s勞l勢s勣j勦j勰x勱m勵l勹b勻y匍p匏p匐f匭g匯h匱k區q協x卟b卩j卹x卻q厙s厭y厲l厴y厶s參c叟s叢c叨d叩k叱c叻l吆y吋c吒z吖y吡b吣q吲y吳w吶n呂l呃e呋f呎c呔d呤l呦y呱g呲c呶n呷g咂z咄d咔k咚d咝s咣g咤c咦y咧l咩m咪m咫z咭j咻x咼w咿y哂s哌p哏g哐k哚d哞m員y哧c哳z哽g哿g唄b唅h唏x唑z唔m唣z唧j唪f唰s唳l唷y唸n唼s唿h啁z啉l問w啐c啕t啖d啞y啟q啣x啵b啶d啷l啻c啾j喁y喃n喈j喋z喏n喑y喔o喙h喚h喟k喪s喫c喬q單d喱l喵m嗄a嗆q嗇s嗉s嗌y嗍s嗎m嗑k嗒d嗔c嗖s嗚w嗝g嗟j嗤c嗥h嗦s嗨h嗪q嗬h嗲d嗵t嗶b嗷a嗾s嘀d嘁q嘈c嘌p嘍l嘔o嘖z嘜m嘞l嘟d嘣b嘧m嘩h嘬c嘭p嘮l嘯x嘰j嘵x嘸f嘹l噁e噌c噍j噓x噔d噗p噘j噙q噠d噢o噤j噥n噦y噫a噯a噱j噲k噴p噸d噹d噻s噼p嚀n嚅r嚆h嚇h嚌j嚐c嚓c嚙n嚥y嚦l嚨l嚮x嚯h嚳k嚴y囀z囁n囂x囅c囈y囉l囌s囑z囔n囗w囝j囡n囪c囫h囹l囿y圄y圇l圉y圊q國g圍w園y圓y圖t圜h圩w圪g圮p圯y圳z圻q坂b坌b坨t坩g坫d坭n坰j坳a坶m坻c坼c垅l垌d垓g垠y垡f垤d垴n埏s埒l埕c埝n埡y埤p埭d埯a埴z執z埸y埽s堀k堅j堇j堊e堋b堍t堙y堞d堠h報b場c堿j塄l塊k塋y塍c塒s塗t塢w塤x塥g塬y塵c塹q塾s墀c墁m墉y墊d墑d墚l墜z墮d墻q壅y壇t壑h壓y壙k壚l壞h壟l壢l壩b壯z壺h壽s夂z夙s夠g夢m夤y夥h夼k夾j奐h奕y奘z奚x奧a奪d妁s妃f妍y妗j妝z妞n妣b妤y妯z妲d妳n妾q姊z姍s姒s姘p姝s姣j姦j姪z姹c娉p娌l娑s娓w娛y娣d娼c婀e婊b婕j婢b婦f婧j婭y婷t婺w媛y媧w媯g媲p媸c媼a媽m媾g嫖p嫗y嫘l嫜z嫠l嫣y嫦c嫫m嫵w嫻x嬈r嬉x嬌j嬖b嬗s嬙q嬝n嬡a嬤m嬰y嬲n嬴y嬸s孀s孃n孑j孓j孚f孢b孥n孫s孱c孳z學x孿l宀m宄g宓m宕d宥y宮g宸c寢q寤w實s寧n審s寫x寬k寮l寰h寵c寶b將j專z尋x對d尕g尜g尢w尥l尬g尷g尻k屄b屆j屐j屙e屜t屣x層c屬s屮c屺q岈y岌j岍q岐q岑c岙a岜b岡g岢k岣g岧t岫x岬j岱d岵h岷m峁m峋x峒d峴x島d峽x崆k崍l崗g崙l崛j崞g崠d崢z崤x崦y崧s崮g崴w崽z崾y嵇j嵊s嵋m嵒y嵩s嵫z嵬w嵯c嵴j嶁l嶂z嶄z嶇q嶗l嶙l嶷n嶸r嶺l嶼y嶽y巋k巑c巔d巖y巛c巰q巹j巽x帑t帔p帙z帥s帳z帶d帷w幃w幄w幔m幘z幛z幞f幟z幡f幣b幪m幫b幬c幹g幺y幾j庀p庂z庋g庖p庠x庥x庫k庳b庹t庾y廁c廂x廄j廈s廑q廒a廚c廛c廝s廡w廢f廣g廨x廩l廬l廱y廳t廴y廾g弁b弈y弋y弒s弔d弩n弭m弳j張z強q弼b彀g彆b彈d彊q彌m彎w彐x彖t彗h彘z彙h彡s彥y彫d彷f彿f徂c徇x徉y後h徑j徙x徜c從c徠l徨h復f徫w徬p徭y徵z徹c徼j忉d忐t忑t忒t忖c忝t忡c忤w忪s忭b忮z忸n怊c怍z怏y怙h怛d怡y怦p怩n怫f怵c恁n恂x恆h恙y恚h恝j恣z恥c恧n恪k悃k悅y悌t悒y悖b悚s悛q悝k悱f悴c悵c悶m悻x悽q惆c惘w惙c惝t惡e惱n惲y惴z惻c愀q愆q愍m愎b愕e愛a愜q愣l愨q愫s愴c愾k慄l慇y慊q態t慍y慘c慚c慝t慟t慣g慫s慮l慵y慶q慼q慾y憂y憊b憐l憑p憔q憚d憝d憧c憩q憫m憬j憲x憶y憷c懇k應y懋m懌y懍l懔l懞m懟d懣m懨y懲c懵m懶l懷h懺c懼j懾s懿y戀l戇g戉y戔j戕q戛j戟j戡k戢j戤g戥d戧q戩j戰z戲x戶h戽h戾l扃j扈h扌s扐c扠c扢g扺z抃b抆w抎y抴y抻c拊f拋p拗a拮j拶z挈q挲s挶j挹y挾x捃j捋l捨s捩l捫m捭b捱a捺n掃s掄l掊p掎j掙z掛g採c掬j掭t掮q掰b掾y揀j揄y揆k揎x揚y換h揞a揠y揲d揶y揸z揹b搆g搋c搌z損s搖y搗d搛j搟x搠s搡s搦n搴q搶q搿g摁e摑g摒b摜g摞l摟l摭z摯z摳k摶t摺z摻c撈l撓n撙z撚n撟j撣d撥b撫f撲p撻t撾z撿j擁y擄l擇z擊j擐h擔d擗p擘b據j擠j擢z擣d擤x擬n擯b擰n擱g擲z擴k擷x擺b擻s擼l擾r攄s攆n攉h攏l攔l攖y攙c攛c攜x攝s攢z攣l攤t攥z攬l攮n攴p攷k敉m敕c敗b敘x敫j敵d斂l斃b斐f斕l斫z斬z斷d於w旂q旃z旄m旆p旌j旎n旒l旖y旮g旯l旰g昀y昃z昇s昊h昕x昝z昱y昴m昶c晁c時s晉j晏y晗h晝z晞x晟c晡b晷g暄x暈y暉h暌k暘y暝m暨j暫z暱n暸l暹x暾t曄y曆l曇t曉x曖a曛x曜y曠k曩n曬s曷h書s會h朊r朐q朕z朦m朧l朮s杇w杈c杌w杓b杞q杪m東d杲g杳y杵c杷b杼z枇p枋f枘r枙e枰p枳z枵x枸g柁d柃l柘z柚y柝t柢d柩j柰n柵s栝g栩x栲k栳l桀j桁h桄g桉a桊j桎z桕j桫s桴f桷j桿g梃t梏g梓z梔z條t梟x梩q梵f棄q棖c棗z棟d棣d棧z棰c棲q棹z棼f椋l椏y椐j椴d椹s楂c楊y楓f楗j楝l楠n楣m楦x楨z楫j業y楮c楱c極j楸q楹y榀p榍x榕r榘j榙t榧f榪m榫s榭x榮r榱c榻t榿q槃p槊s槌c槍q槎c槓g槧q槨g槭q槲h槳j槿j樁z樂l樅c樑l樓l樗c樘t標b樞s樨x樵q樸p樹s樺h樽z樾y橄g橈r橋q橐t橘j橛j機j橢t橫h檁l檉c檎q檔d檗b檜g檠q檢j檫c檯t檳b檸n檻j櫓l櫚l櫛z櫝d櫟l櫥c櫧z櫨l櫪l櫬c櫳l櫸j櫺l櫻y欄l權q欏l欒l欖l欞l欷x欹q歃s歆x歎t歐o歙s歟y歡h歲s歷l歿m殂c殄t殍p殘c殞y殤s殪y殫d殮l殯b殲j殳s殺s殼k毀h毂g毆o毌g毪m毬q毳c毹s毽j毿s氅c氆p氈z氌l氍q氕p氘d氙x氚c氡d氣q氤y氪k氫q氬y氳y氵s氽c氾f汆c汊c汍w汎f汔q汜s汨m汩g汴b汶w決j沅y沆h沌d沐m沒m沓d沔m沖c沭s沱t沲t泐l泓h泔g泖m泗s泠l泫x泮p泯m泱y洄h洇y洌l洎j洙z洚j洧w洩x洫x洮t洳r洵x洶x洹h浜b浞z浠x浣h浬h浯w浹j浼m涇j涑s涓j涔c涫g涮s涼l涿z淅x淇q淒q淙c淚l淝f淞s淠p淥l淦g淨j淪l淵y淶l淼m渙h渚z減j渥w渦w渫x測c渲x渾h湄m湊c湎m湓p湔j湞z湟h湧y湫j湮y湯t溆x溏t準z溘k溝g溟m溥p溧l溫w溱q溲s溴x溷h溻t溼s溽r滂p滄c滅m滎x滏f滕t滟y滬h滯z滲s滷l滸h滹h滾g滿m漉l漕c漚o漢h漣l漤l漩x漪y漬z漭m漯l漲z漶h漸j漿j潁y潑p潔j潛q潟x潢h潤r潯x潰k潲s潴z潷b潸s潺c潼t澀s澆j澇l澉g澌s澍s澗j澠m澤d澧l澩x澮k澱d澶c澹d濂l濃n濉s濕s濘n濛m濞b濟j濠h濡r濤t濫l濮p濯z濰w濱b濺j濼l瀅y瀆d瀉x瀋s瀏l瀕b瀘l瀚h瀛y瀝l瀟x瀠y瀣x瀧l瀨l瀰m瀲l瀵f瀹y灃f灄s灑s灕l灘t灝h灞b灣w灤l灬h災z炅g炫x炱t為w炻s烀h烊y烏w焐w焓h無w焯c焱y煅d煉l煊x煒w煙y煜y煢q煦x煨w煩f煬y煲b煳h煸b煺t熒y熗q熘l熠y熨y熱r熳m熵s熹x熾c燁y燄y燉d燐l燒s燔f燙t營y燠y燦c燧s燬h燭z燮x燴h燹x燼j燾d爐l爛l爝j爨c爭z爰y爺y爾e爿p牆q牒d牖y牘d牝p牠t牦m牪y牮j牯g牴d牽q牿g犄j犍j犏p犒k犛l犟j犧x犭q犰q犴a犸m狀z狁y狃n狍p狎x狒f狨r狩s狳y狴b狷j狹x狺y狻s狽b猁l猊n猓g猗y猙z猝c猞s猢h猥w猬w猱n猶y猷y猸m猹c猻s獄y獅s獍j獐z獒a獗j獠l獨d獪k獫x獬x獯x獰n獲h獵l獷g獸s獺t獻x獼m獾h玀l玆z玎d玟m玢b玨j玳d玷d珀p珂k珈j珉m珙g珞l珥e珧y珩h珮p現x琊y琚j琛c琥h琦q琨k琪q琬w琮c琰y琺f琿h瑁m瑕x瑗y瑙n瑛y瑜y瑣s瑤y瑩y瑪m瑭t瑯l瑾j璀c璇x璉l璋z璐l璜h璣j璦a璧b璨c璩q璺w璽x瓊q瓏l瓖x瓚z瓞d瓠h瓴l瓿b甍m甏b甑z甓p甙d產c甦s甬y町d甾z畀b畈f畋t畎q畚b畛z畝m畫h畬s異y畹w畼c畿j疃t疊d疋y疒n疔d疝s疣y疬l疰z疳g疴k疸d疿f痂j痃x痄z痍y痖y痙j痣z痤c痦w痧s痲m痳m痼g痿w瘀y瘃z瘊h瘌l瘍y瘐y瘓h瘕j瘙s瘛c瘞y瘠j瘡c瘢b瘥c瘭b瘰l瘳c瘺l瘼m癀h療l癃l癆l癇x癍b癔y癖p癜d癟b癡c癢y癥z癩l癬x癭y癮y癯q癰y癱t癲d發f皈b皎j皓h皙x皚a皤f皰p皴c皸j皺z皻z盍h盜d盞z盡j監j盤p盥g盧l盪d盱x盹d眄m眇m眈d眍k眚s眢y眣d眥z眭s眵c眸m睇d睏k睚y睜z睞l睢s睥b睨n睪y睽k睿r瞀m瞌k瞍s瞑m瞞m瞟p瞠c瞢m瞭l瞰k瞵l瞼j瞽g瞿j矇m矍j矓l矚z矜j矧s矬c矯j矸g砉h砑y砘d砜f砝f砟z砣t砥d砩f砬l砭b砲p砹a砼t硃z硇n硌g硎x硐d硤x硨c硪w硭m硯y碇d碓d碚b碣j碥b碩s碲d碹x碼m磉s磔z磙g磚z磣c磧q磬q磯j磲q磴d礅d礎c礓j礙a礞m礤c礦k礪l礫l礱l礴b礻s祀s祅y祇q祉z祐y祓f祕m祗q祚z祛q祜h祠c祧t祺q祿l禊x禍h禎z禚z禦y禧x禮l禰m禱d禳r禿t秈x秕b秣m秫s秭z稂l稃f稅s稆l稈g稔r稜l稞k稟b種z稱c稷j稹z穀g穌s積j穢h穩w穫h穰r穸x穹q窀z窆b窈y窕t窠k窨x窩w窪w窬y窮q窯y窳y窶j窺k竄c竅q竊q竦s競j竺d竽y笄j笈j笊z笏h笙s笞c笠l笤t笥s笪d笫z笮z笱g笳j笸p笻q筆b筌q筍s筘k筠j筢p筮s筲s筵y筻g箄b箇g箋j箏z箐q箜k箝q箬r箴z箸z節j篁h範f築z篋q篌h篚f篝g篠x篤d篥l篦b篩s篪c篳b篼d篾m簀z簋g簌s簍l簏l簑s簟d簡j簣k簦d簪z簫x簷y簸b簽q簾l籀z籃l籌c籐t籜t籟l籠l籤q籥y籩b籪d籬l籮l籲y粑b粞x粢z粧z粲c粵y粼l粽z糅r糇h糈x糌z糍c糗q糝s糢m糧l糨j糰t糲l糴d糶t糸m糾j紀j紂z約y紅h紇h紈w紉r紋w納n紐n紓s紕p紗s紙z級j紜y紡f紮z細x紱f紲x紳s紹s紺g紼f紿d絀c終z絃x組z絆b絎h結j絛t絞j絡l絢x給g絨r統t絲s絳j絹j綁b綃x綆g綈t綏s綑k經j綜z綞d綠l綢c綣q綦q綬s維w綮q綰w綱g網w綴z綵c綸l綹l綺q綻z綾l綿m緇z緊j緋f緒x緗x緘j緙k線x緝j緞d締d緡m緣y緦s編b緩h緬m緯w緱g緲m練l緶b緹t緻z縈y縉j縊y縐z縑j縚t縛f縝z縞g縟r縣x縫f縭l縮s縯y縱z縳z縴q縵m縶z縷l縹p縻m總z績j繅s繆m繇y繈q繒z織z繕s繚l繞r繡x繢h繩s繪h繫x繭j繯h繰q繳j繹y繼j繾q纈x纊k續x纍l纏c纓y纔c纖x纘z纛d纜l纟s绔k绛j绱s缰j缶f缽b罄q罅x罈t罌y罘f罟g罡g罨y罰f罱l罵m罹l罾z羅l羆p羈j羋m羝d羧s義y羯j羰t羲x羸l羼c羿y翊y翎l習x翕x翡f翥z翦j翩p翮h翳y翹q耄m耆q耋d耑d耒l耔z耖c耜s耠h耢l耥t耦o耨n耩j耬l耱m耵d耷d聃d聆l聒g聞w聯l聰c聱a聲s聳s聵k聶n職z聹n聽t聾l聿y肅s肓h肜c肟w肫z肭n肱g肴y肷q肼j肽t胂s胍g胗z胛j胝z胥x胨d胩k胬n胭y胱g胲h胴d胼p脅x脈m脎s脒m脘w脛j脞c脣c脧j脩x脫t脬p脲n脹z腈j腌a腎s腓f腙z腚d腠c腡l腦n腧s腩n腫z腭e腱j腴y腸c腼m膂l膃w膈g膚f膠j膦l膪c膺y膻s膽d膾k膿n臁l臉l臊s臌g臍q臏b臚l臟z臠l臧z臨l臬n臾y舁y舂c舄x與y興x舉j舊j舐s舖p舛c舡c舢s舨b舫f舭b舯z舳z舴z舸g舺x舾x艄s艉w艋m艏s艚c艟c艣l艦j艨m艫l艮g艱j艴f艷y艸c艽j艿n芄w芊q芎x芏d芑q芘b芙f芟s芡q芤k芨j芩q芪q芫y芮r芰j芴w芷z芻c芾f苊e苒r苓l苕s苘q苜m苠m苡y苣j苤p苧n苴j苷g苻f茆m茇b茈c茉m茌c茍j茗m茚y茛g茜q茭j茯f茱z茲z茳j茴h茺c茼t荀x荃q荇x荊j荏r荑t荬m荸b荻d荼a荽s莊z莒j莓m莘s莛t莞g莠y莢j莧x莨l莩f莪e莰k莺y莼c菀w菁j菅j菔f菖c菘s菝b菟t菡h菥x華h菰g菸y菹z菽s萁q萃c萆b萊l萋q萏d萑h萘n萜t萬w萱x萵w萸y萼e葆b葉y葑f葙x葚r葜q葦w葩p葭j葯y葳w葶t葷h葸x葺q蒈k蒎p蒔s蒗l蒞l蒡b蒴s蒺j蒼c蒽e蒿h蓁z蓊w蓋g蓍s蓐r蓓b蓥y蓦m蓯c蓰x蓼l蓽b蔌s蔔b蔟c蔣j蔥c蔦n蔭y蔸d蔻k蕁q蕃f蕈x蕎q蕓y蕕y蕖q蕙h蕞z蕢k蕤r蕨j蕩d蕪w蕭x蕷y蕹w蕺j蕻h薅h薇w薈h薊j薌x薏y薑j薔q薜b薟x薤x薦j薧h薨h薩s薰x薷r薹t薺j藁g藍l藎j藜l藝y藥y藪s藶l藷s藹a藺l藿h蘄q蘅h蘆l蘇s蘊y蘋p蘗b蘚x蘢l蘧q蘩f蘭l蘺l蘼m蘿l虍h虔q處c虛x虜l號h虢g虧k虬q虺h虻m蚋r蚍p蚓y蚣g蚧j蚨f蚩c蚪d蚯q蚰y蚱z蚴y蚵k蚶h蚺r蛄g蛅z蛉l蛐q蛑m蛘y蛞k蛟j蛩q蛭z蛸s蛺j蛻t蜃s蜆x蜇z蜈w蜉f蜊l蜍c蜓t蜚f蜞q蜢m蜣q蜥x蜩t蜮y蜱p蜷q蜻q蜾g蜿w蝌k蝓y蝕s蝙b蝠f蝣y蝤q蝥m蝦x蝮f蝰k蝸w蝻n蝽c螂l螃p螅x螋s螓q螗t螞m螢y螨m螫s螬c螭c螯a螳t螵p螻l螽z蟀s蟄z蟆m蟈g蟊m蟋x蟑z蟒m蟓x蟠p蟥h蟪h蟬c蟮s蟯n蟲c蟶c蟻y蟾c蠃l蠅y蠆c蠊l蠐q蠑r蠓m蠔h蠖h蠛m蠟l蠡l蠣l蠱g蠲j蠹d蠼q衄n術s衛w衝c衢q衤y衩c衲n衹z衽r衾q衿j袂m袈j袞g袢p袤m袷q裊n裎c裏l裒p裘q補b裝z裟s裡l裥j裨b裰d裱b裼t製z裾j複f褊b褓b褙b褚c褡d褫c褰q褲k褳l褶z褻x襝l襞b襠d襤l襦r襪w襬b襯c襻p覃q見j規g覓m視s覘c覡x覦y親q覬j覯g覲j覷q覺j覽l覿d觖j觚g觜z觥g觫s觳h觴s觶z觸c訂d訃f訇h訊x訌h討t訐j訓x訕s訖q託t記j訛e訝y訢x訣j訪f設s許x訴s診z註z証z訾z詁g詆d詈l詎j詐z詒y詔z評p詘q詛z詞c詠y詡x詢x詣y詩s詫c詭g詮q話h該g詵s詼h誅z誆k誇k誌z認r誑k誕d誘y誚q語y誠c誡j誣w誤w誥g誨h誰s課k誶s誼y調d諂c諄z談t諉w請q諍z諏z諑z諒l論l諗s諛y諜d諞p諢h諤e諧x諫j諮z諱h諳a諶c諷f諸z諺y諾n謀m謁y謂w謅z謇j謊h謎m謐m謔x謗b謙q謚s講j謝x謠y謦q謨m謫z謬m謳o謹j譁h證z譎j譖z識s譙q譚t譜p譟z譫z譯y議y譴q護h譽y讀d讎c讒c讓r讕l讖c讚z谫j豇j豈q豉c豎s豐f豔y豕s豚t豬z豳b豸z貂d貅x貊m貍l貓m貔p貘m貝b負f財c貢g貧p貨h販f貪t貫g責z貯z貰s貲z貳e貴g貶b貸d貺k貼t貾c貿m賁b賂l賃l賄h賅g賆p資z賈j賉x賊z賋j賌g賎j賏y賐j賑z賒s賓b賔b賕q賖s賗c賘z賙z賛z賜c賝c賞s賟t賠p賡g賢x賣m賤j賥s賧d賨c賩c質z賬z賭d賮j賯x賰c賳z賴l賵f賶c賷j賸s賹y賺z賻f購g賽s賾z賿l贀y贂c贃w贄z贅z贆b贇y贈z贉d贊z贋y贌p贍s贎w贏y贐j贑g贒x贓z贔b贕d贗y贘s贙x贚l贛g贜z贠y贲b贻y贽z赅g赆j赇q赈z赉l赍j赑b赒z赓g赕d赗f赙f赟y赥x赧n赨t赩x赪c赬c赭z赮x赯t赱z赲l赳j赸s赹q赺y赻x赼z赽j赿c趂c趃d趄j趆d趇x趈z趉j趌j趍q趎c趏g趐x趑z趔l趖s趗c趘x趚s趜j趝j趞q趠c趡c趤d趥q趧t趨q趩c趪h趫q趭j趮z趯t趰e趱z趲z趵b趷k趹j趺f趻c趼j趽f趿t跁b跂q跄q跅t跆t跇y跈j跉l跍k跎t跏j跐c跒q跓z跔j跕d跖z跗f跘p跙j跚s跛b跜n跞l跠y跡j跢d跣x跤j跦z跧q跩z跫q跬k跭x跰p跱z跲j跴c跶d跷q跸b跹x跻j跼j跽j跾s跿t踀c踁j踂n踃x踄b踅x踆c踈s踉l踍q踎m踑j踒w踓w踔c踕j踗n踘j踙n踚l踛l踝h踟c踡j踣b踥q踦q踧c踫p踬z踭z踮d踯z踰y踱d踲d踳c踴y踵z踶d踷z踸c踹c踺j踻g踽j踾f踿c蹀d蹁p蹂r蹃n蹇j蹉c蹊q蹍n蹐j蹑n蹒p蹓l蹔z蹕b蹖c蹗l蹘l蹙c蹚t蹛d蹜s蹝x蹞k蹟j蹠z蹡q蹢d蹣p蹤z蹥l蹧z蹨n蹩b蹪t蹫j蹮x蹯f蹰c蹱z蹳b蹴c蹵c蹶g蹷j蹸l蹹t蹺q蹻q蹼p蹽l蹾d躀g躂d躃b躄b躅z躆j躈q躉d躊c躋j躍y躎n躏l躐l躑z躒l躓z躔c躕c躖d躗w躘l躙l躚x躛w躜z躝l躞x躟r躠s躡n躢t躣q躥c躦z躧x躨k躩j躭d躮s躰t躱d躵h躶l躷a躸j躹j躻w躽y躾x躿k軀q軁l軃d軄z軆t軇d軉y車c軋y軌g軎w軏y軐x軑d軒x軓f軔r軕s軖k軗s軘t軙c軚d軜n軝q軟r軠k軡q転z軣h軤h軥q軦k軧d軨l軩d軪a軫z軬f軮y軰b軱g軲g軴z軵r軶e軷b軹z軺y軻k軼y軽q軾s軿p輀e輁g輂j較j輄g輅l輆k輈z載z輋s輌l輍y輎s輏y輐w輓w輔f輕q輖z輗n輘l輙z輚z輛l輝h輞w輟c輡k輢y輣p輤q輥g輦n輨g輩b輫p輬l輭r輮r輰y輱x輲c輴c輵g輶y輷h輸s輹f輺z輻f輽f輾z輿y轀w轁t轂g轃z轄x轅y轆l轇j轈c轉z轊w轍z轎j轏z轐b轑l轔l轖s轗k轘h俁y係x轛d轝y轞j轟h轠l轡p轢l倀c轤l轥l轪d轫r轭e轱g轲k轳l轵z轶y轷h轸z轹l轺y轼s轾z辁q辂l辄z辇n辋w辌l辍c辏c辒w辔p辘l傑j辚l辝c辠z辡b辢l辥x辦b辪x辬b辮b辯b農n辳n辵c辶c辷y辸r辻s込y辿c迃y迆y迉q迊b迋w迌t迍z迏d儈k迕w迖d迗e迚d迤y迥j迨d迩e迬z迮z迯t迱y迲k迳j迴h迵d迺n迻y凜l迾l逄p逅h逇d逈j逋b逍x逎q逑q逓d逕j逖t這z逜w逡q連l逥h逦l逧g逨l逪c逬b逭h逯l週z進j逳y逴c逵k逶w逷t逹d逺y勸q遀s遄c遅c遈s遊y運y遌e過g遐x遑h遒q遖n遘g遙y遛l遜x遝t遞d遟c遠y遡s遢t遤h遦g遨a遪c遫c遯d遲c咴h遴l遶r遷q選x遹y遺y遻e遼l遽j遾s邁m邂x邃s還h邅z邆t邇e邈m邉b邊b邋l邌l邏l邒t邔q邕y邖s邘y邙m邛q邜x邝k邟k邠b邡f邥s邧y邬w邭j邰t邲b邳p邴b邶b邷w邸d邺y邼k邾z邿s喹k郀k郂g郃h郄q郅z郆j郇x郈h郉x郍n嗩s郏j郐k郒l郓y郔y郕c郖d郗x郘l郙f嘆t郚w郛f郜g郟j郠g嘗c郢y郤x郥b郦l郪q郫p郮z郯t郱p郳n郵y郶b郷x郹j郻q郾y郿m鄀r鄁b鄃s鄄j鄆y鄇h鄈k鄉x嚕l鄊x鄌t鄎x鄏r鄐c鄑z鄓y嚶y鄔w鄕x鄗h鄚m鄛c鄜f鄝l鄞y鄟z鄠h鄡q鄢y鄣z鄥q鄦x鄨b鄫z鄬w鄭z鄮m鄯s鄰l鄱p鄲d鄴y鄵c鄶k鄷f鄸m團t鄺k鄻l鄼z鄽c鄾y酀y酁c酂z酃l酄h酆f酇z酈l酊d酎z酏y酐g酑y酓y酔z酕m酖d酘d酙z酛k酜f酟t酠q酡t酢c酤g酦f垸y酧c酩m酫c酭y酯z酰x酲c酴t酹l酺p酻z堝g酾s醀w醁l醂l醃y醄t醅p醊z醌k醍t醎x醏d醐h醑x醕c醖y醗p醘k醙s醜c醝c醞y醟y醠a醡z醢h醣t醤j醥p醧y醨l醩z醪l醫y醬j醭b醮j醯x醰t醱p醲n醳y醴l醵j醶y醷y醸n醹r醺x醻c醼y醽l醾m醿m墼j釁x釃s釄m釅y壎x釆b壘l釐l釓g釖d奩l釘d釚q釛h針z釞z釟b釡f釣d釦k釧c釨z釩f釬h釭g釮q釰r釱d釲s釳x釴y釵c釶s釷t釸x釹n釺q釻q釼r釽p釾y鈀b鈁f鈂c鈅y鈆q嬋c鈉n鈊x鈋e鈌j鈍d嬪p孌l鈐q鈒s鈓r鈔c鈕n鈗y鈘y鈛g鈜h鈞j導d鈡z鈢x鈣g鈤r鈥h鈦t鈧k屝f鈫q鈬d鈭z鈮n鈯t鈰s鈲g鈳k鈴l屨j鈶s鈷g鈸b鈹p鈺y鈻s鈼z鈽b鉁z鉂s鉃s鉄t鉆z鉈t鉉x鉊z鉋b鉌h鉍b鉎s鉏c鉐s鉑b鉓c鉕p鉖t鉘f鉙z鉚m鉛q鉜f鉝l鉟p鉠y鉣j鉤g鉦z崳y鉨x鉩x鉪d鉫j鉭t鉮s嵐l鉯y鉰s鉱k鉶x鉷h鉸j鉹c鉺e鉻g鉼b嶝d鉿h嶧y銀y銂z巒l銄x銆m銇l銈j銉y銊x銋r銌z師s銎q銏s銒x銓q銔p銖z銙k銚d銛x銜x銝x銞j銟c銢p銥y銦y銧g銩d銪y銭q銯k銲h銳r銴s廟m銵k廠c銶q銷x銸z銺z銻t鋀t鋁l鋂m鋄w鋅x鋆y鋇b鋈w鋉s鋋c鋌t鋎h鋑j鋕z鋖s鋗x鋘h鋙w鋚t鋛k忄x鋞x鋟q鋠s鋡h鋢l鋣y鋤c鋥z鋦j鋧x鋨e鋩m鋪p鋬p鋭r鋮c鋯g鋰l鋳z鋴z鋵t惚h鋷z鋸j鋹c鋺y愷k鋼g鋽d鋿s錀l慪o錁k錂l錃p慳q憒k錅l憮w錇p錉m錊z錋p錌a錍p錎x錏y懸x錑l錒a錓k錕k錗n錘c錙z錚z錜n錞c錟t錠d錡q錢q錣z錤j錥y錦j錧g錨m錩c錪t捲j錬l錭d錮g錯c錱z録l錳m錴l錵h錷g錸l錹k錺k錻b揮h錼n錽w錾z錿h鍀d鍁x鍄l鍆m鍇k鍈y鍉c鍊l鍋g鍌x鍍d鍏w鍐z鍑f鍒r鍓j鍔e鍕j鍖c鍗t撐c鍘z鍙h鍚y撖h鍛d撘d撢d撳q擋d鍞k鍟s鍠h鍡w鍢f鍣z鍤c鍥q鍦s鍧h鍨k鍩n鍪m鍫q鍬q鍭h鍯c鍱y鍲m鍳j鍵j鍶s鍷k鍸h鍹x攪j鍺z鍻j鍼z鍽b鍾z鍿z鎀x鎁y鎂m鎃p鎄a鎅j鎆q鎇m鎈c鎉d鎊b攵p鎋x鎌l鎍s數s鎏l鎐y鎑y鎒n鎓w鎔r鎕t鎖s鎘g鎙s鎛b鎜p鎝d鎞b鎟s鎠g鎡z鎤h暢c鎦l鎧k鎨s鎩s鎪s鎫w鎬g鎭z鎮z鎯l鎰y鎱y鎲t鎳n鎴x鎵j鎶g鎷m鎸j鎹k鎺h鎻s鎼x鎽f鎾w鎿n鏀l鏁s鏂o鏃z鏄t鏅x鏆g鏇x鏈l鏉s鏊a鏋m鏌m鏍l鏎b鏏w鏐l鏑d鏒s鏔y鏕l鏖a鏗k鏘q鏙c鏚q鏛s鏝m鏞y鏟c鏠f鏡j鏢b鏣s鏤l鏥x枧j枴g柙x鏧l鏨z鏩j鏪c鏫l鏬x鏭x鏮k鏯s鏰b鏱z鏲q鏳z鏴l鏵h鏶j鏸h鏹q鏺p鏻l鏼s鏽x鏾s鏿c鐀g鐁s鐂l鐃n鐄h鐅p鐆s鐇f鐈q鐉q鐊x鐋t鐌x鐎j鐏z鐐l鐑q鐒l鐓d鐔x鐕z鐖j鐗j鐘z鐙d鐚y鐛y鐜d鐝j鐞n鐟z鐠p鐡t鐣c鐤d鐥s鐦k鐧j鐨f鐩s鐪l鐫j鐬h鐭y鐮l鐯z鐰q鐱j鐲z鐴b鐵t榦g鐷y鐸d鐹g鐻j鐽d鐾b鐿y鑀a構g鑁z鑂x槔g鑄z鑅h鑆z鑇j鑈n鑉h鑊h鑋q鑌b樣y鑎g鑏n鑐x鑑j鑒j鑓y鑔c鑕z鑖m鑗l鑘l檑l鑜s鑝p檣q鑞l鑟d鑠s鑡c鑢l鑣b鑤b鑥l鑦x鑧k鑨l鑩e櫃g鑪l鑫x鑬j櫞y鑮b鑯j鑰y鑱c鑲x鑳j鑴x鑵g鑷n鑸l鑹c鑺q鑻p鑼l鑽z鑾l鑿z钀n钁j欽q钃z钄l钅j钆g钇y钊z钋p钌l钍t钏c钐s钑s钔m钕n钖y钗c钚b钛t钜j钣b钤q钪k钫f钬h钭t钯b钰y钲z钴g钶k钷p钸b钹b钼m钽t铄s铈s铉x铊s铋b铌n铍p铎d铏x铐k铑l铒e铓m铔y铕y铖c铗j铘y铙n铚z铛c铞d铟y铠k铢z汙w铤d铥d铦x铧h铨q铩s铪h铫d铮z铯s铳c铴t铵a铷r铹l铻w铼l铽t铿k锂l锃z锆g锇e锉c锊l锍l锎k锏j況k锒l锓q锔j锕a锖q锘n锛b锜q锝d锞k锟k锠c锢g锧z锩j锪h锫p锬t锱z锲q锴k锵q锶s锷e锸c锺z锼s锽h锾h锿a镂l镃z镄f镅m镆m镈b镉g镋t镌j镎n镏l镒y镓j镔b镕r镖b镗t镘m镙l镚b镛y镝d镞z镟x镠l镡t镢j镤p淺q镥l镦d镧l镨p镩c镫d镬h镯z镱y镲c镳b镴l镵c長c镸c镹j溈w镻d镼j镽l門m閂s閃s閄h滌d閆y閇b閈h閉b閊c開k閌k閍b閎h閏r閐s閑x閒x漁y閔m閕x閖l閗d閘z閙n閚z閛p閜x閝l閞b閠r閡h関g閣g閤g閦c閧h閨g閩m閪s閫k閬l閭l閮t閰j閱y閲y潿w閳c閴q閶c閷s閸k閹y閺w閻y閼e閽h閾y閿w闀h闁b濁z闄y闅w闆b闈w闉y闊k闋q闌l闍d闎q闏p闐t濾l闓k瀾l闕q闖c闗g闘d闙q闚k闛t關g闝p闞k闟x闠h闡c闤h闥t闦w闩s闫y闬h闱w闳h闵m闶k闼t闾l闿k阃k阄j阆l阇d阈y阊c阋x阌w阍h阏e阒q阓h阕q阖h阗t阘t阙j阚k阛h阝f阞l阠x烴t阡q阢w阣y阤t阥y阦y阧d阨a阩s阪b阫p阬k阭y阯z阰p阱j阷c阸e阹q阺d阼z阽d阾l陁t陂b陃b陉x陊d煥h陎s陏d陑e陒g陓y陔g陖j陗q陘x陙c陚w陜x陝s陞s陟z陠p陣z陦d陧n陫f燈d燜m陭q陮d爍s陰y陱j陲c陳c陴p陸l陹s険x陻y陼z陽y陾r陿x隀c隁y隂y隃y隄d隇w隈w隉n隊d隌a牾w隍h階j隑g隒y隓h犋j隕y隖w隗k隚t際j犖l隝d隞a犢d隟x隠y隡s隢r隣l隤t隥d隦p隨s隩a險x隬n隭e隮j隯d隰x隱y隲z隴l隵x隷l隸l隹z隺h隻z隼s隽j隿y雂q雃j雈h雉z雊g雋j雎j雐h雑z雒l雓y雔c雖s雗h雘h雙s雚g雛c雜z雝y雞j雟g雡l離l難n雤y雥z雦c雧j雩y雫n雬f獎j雭s雮m雯w雰f雱p雲y雳l雴c雵y雸a獮x電d雼d雽h雿d霁j霂m霃c霅z霆t霈p霊l霋q霌z霎s霏f霐h霑z霒y霔s霕t霗l霘d霙y霚w霛l霝l霟h霠y霡m霢m霤l霥m霦b霧w霨w霩k霪y霫x霬y霭a霮d霯t霰s霱y霳l霴d霵j霶p霷y霺w霼x瑋w霽j霾m霿m靀m靁l靂l靃h靄a靅f靆d靇l靈l靉a靊f靋l靌b靍h靎h靏h靐b璁c靑q靓j靔t靕z靗c靘q靚j靜j靝t靟f靣m靤p靦m靧h靨y靪d靫c靬j靭r靮d靯d靰w靱r靲q靵n靷y環h靸s靹n靺m靻z靼d靽b靾x靿y鞀t鞁b瓔y鞂j鞃h鞄p鞅y鞆b鞇y鞈g鞉t鞊j鞌a鞎h鞏g鞐k鞑d鞒q鞓t甌o鞔m鞕b鞖s鞗t鞙x鞚k甕w鞛b鞜t鞝s鞞b鞟k鞡l鞢x鞣r鞤b鞥e鞦q鞧q鞨h鞩q鞪m鞫j鞬j鞮d鞯j鞰w鞱t鞲g鞳t鞴b鞵x畢b鞶p鞷g鞸b鞹k鞺t鞻l鞼g當d鞽q鞾x鞿j韀j疇c韂c韃d韅x韆q韇d韈w韉j韊l痠s韋w韌r韍f韎m韏q韐g韑w韒q韓h韔c韕k痺b韖r韗y韘s韙w韚g韛b韜t韝g瘋f韞y韟g韠b韡w韢s韣d韤w韥d韨f韪w韫y韬t韮j韯x韰x韱x韲j韴z韷l韸p韹h韺y韻y韼p韽a韾y響x頀h頁y頂d頃q項x癉d頇h須x頉y癘l頊x頋e頌s頍k頎q頏h癤j預y頑w頒b頓d頔d頕d頖p頗p領l頙c頚j頛l頜h頝q頞e頟e頠w頡j頢k頣s頤y頥y頦k頧d頨y頩p頪l頫f頬j頭t頮h頯k頰j頱l頲t頳c頴y頵j頶h頷h頸j頹t頺t頻b頼l頽t頾z頿z顀c顁d顂l顃t顄h顅q顆k顇c顈j顉q顊y顋s題t額e顎e顏y眙y顐w顒y顓z顔y顕x顖x顗y願y顙s顚d顛d顜j眾z顝k類l顟l顠p顡w顢m顣c顤y顥h顦q顧g顨x顩y顪h顫c顬r顭m顮b顯x顰p瞇m顱l顲l顳n顴q顸h顼x颀q颉j颋t颌g颎j颏k颒h颔h颕y颙y颚e颛z颞n颟m颡s颣l颥r颦p風f颩d颪g颫f颬x颭z颮b颯s颰b颱t颲l颳g颴x颵x颶j颷b颸s颹w颺y颻y颼s颽k颾s颿f飀l飁x飂l飃p飄p飅l飆b飇b飈b飉l飊b飋s飌f飍x飏y飐z飑b飒s飓j飔s飕s飖y砦z飗l飙b飚b飛f飜f飝f飡c飢j飣d飤s飥t飦z飧s飨x飩t飪r飫y飬y飭c飮y飯f飰f飱s碭d飲y飳z飴y飵z飶b飷j確q飸t飹b飺c飻t飼s飽b飾s飿d餀h餁r餂t餃j餄h餅b餆y餇t餈c餉x養y餋j餌e磽q餍y餎l餏x餑b餒n餓e餔b餕j餖d餗s餘y餙s餚y餛h餜g餝s餞j餟c餠b餡x餢b餣y餤d餥f餦z餧w館g餩e餪n餫y餬h餭h餮t餯h餰j餱h餲a餳x礬f餵w餶g餷c餸s餹t餺b餻g餼x餽k餾l餿s饀t饂w饃m饄t饅m饆b饇y饈x饉j饊s饋k饌z饍s饎x饏d饐y饑j饒r饔y饕t饖w饗x饘z饚h饛m饜y饝m饞c饟x饠l饡z饢n饣s饤d饦t饧x饨t禪s饩x饪r饫y饬c饳d饴y饷x饸h饹g饻x饽b饾d馀y馂j馃g馄h馇c馉g馊s馌y馎b馐x馑j馓s馔z馕n馘g馛b馜n馝b馞b馟t馠h馡f馢j馣a馤a馥f馦x馧y馨x穎y穡s馪p馫x馬m馭y馮f馯h馰d馱t馲t馳c馴x馵z馶z馷p馸x馹r馺s馻y馼w馽z馾d馿l駀y竇d駂b駃j筅x駅y駆q駇w駈q駉j駊p駋z駌y筧j駍p駎z駏j駐z駑n駒j駓p駔z駕j駖l駗z駘t駙f駚y駛s駜b駝t駞t駟s箢y駠l駡m駢p駣t駤z駥r駦t駧d駨x駩q駪s駫j駬e駭h駮b簞d駰y駱l駲z駳d駴h駵l駶j駷s駸q駹m駺l駻h駼t駽x駾t駿j騀e騁c騂x騃a騄l騅z騆z騇s騈p騉k騊t騋l騌z騍k騎q騏q騐y騑f騒s験y騔g騕y騖w騗p騙p騚q騛f紆y騜h騝q騞h騟y騠t騡q騢x騣z純c騥r騦s騧g騨t絕j騩g騪s騬c騭z騮l騯p騰t騱x騲c騳d騴y綽c緄g騶z騷s騸s騹q騺z騻s騼l騽x騾l騿z縋z驁a驂c驃p驄c驅q縲l驆b驇z驈y驉x驊h驋b驌s驍x驎l驏z驐d繃b驑l驒t驓c驔d驕j驖t驗y驘l驙z驚j驛y驜y繽b驝t驞p驟z驠y驡l驢l驣t驤x驥j驦s驧j驨x驩h驪l驫b驲r驵z驷s驸f驺z驽n驿y骀d骁x骃y骅h骈p骉b骊l骍x骎q骐q骒k骓z骔z骕s骖c骘z骙k罷b骛w骜a骝l骞q骟s骠b骢c骣c骥j骦s骧x骩w骪w骫w骬y骭g骮y骯a骰t骱j骲b骳b骴c骵t羨x骶d骷k骹q骺h骻k骼g骽t骾g骿p羻q髀b髁k髂q髃y髄s髅l髆b髇x髈b髉b髋k髌b髎l髏l聖s髐x髑d髒z體t髕b髖k髗l髙g髚q髛k髜q髝l髞s髟b髠k髡k髢d肀y髣f髤x髥r髦m髧d髩b髪f髫t髬p髭z髮f髯r髰t胙z髱b髲b髳m髴f髷q髸g髹x髺k髻j髽z髾s髿s鬀t鬁l膣z鬄t鬅p鬆s鬇z鬈q鬉z鬊s鬋j鬌d鬍h鬎l鬏j鬐q鬑l鬒z鬓b鬔p鬕m鬖s鬗m鬘m鬙s鬚x鬛l鬜q鬝q鬞n鬟h鬠k臘l鬡n鬢b鬣l鬤r鬥d臥w鬧n鬨h鬩x鬪d鬬d鬭d鬮j鬯c鬰y艙c鬳y艤y鬴f鬵z鬶g鬷z鬸l鬹g鬺s鬻y鬽m鬾j鬿q魀g魃b魅m魆x魇y芐h魉l魊y魋t魍w魎l魐g魑c魒p魕j魖x魗c魘y魙z魚y荮z莖j魜r魝j魞a魟h魠t魡d魢j魣x魤e魥j魦s魧h菪d菴a魩m萇c魪j魫s魬b魭y魮p魯l魰w葒h魲l魳z魴f魶n魷y魸n魹m魺h魻x魼q魽h魾p魿l鮀t蒹j蓀s鮂q鮃p鮄f鮅b鮆c鮇w鮈j鮉d蓮l鮊b鮋y鮌g鮍p鮎n鮏x鮐t鮑b蔆l鮒f鮓z鮔j鮕g鮖s蔞l蕆c蕘r鮘c鮙t鮚j鮛s鮜h鮝x鮞e鮟a鮠w鮡z鮢z鮣y鮤l鮥l鮦t鮧y鮨y鮩b鮪w鮫j鮬k鮭g鮮x鮯g鮰h鮱l鮲m鮳k鮴x鮵t鮶j鮷t鮸m鮹s鮺z鮻s鮼q鮽y鮾n鯀g鯁g鯂s鯃w鯄q鯅s鯆p鯇h鯈t蘞l鯊s鯋s鯌k鯍m鯎c鯏l虼g鯐s鯑k鯒y鯓s鯔z鯕q鯖q鯗x鯘n鯚j鯛d鯜q鯝g鯞z鯟d鯠l鯡f鯢n鯣y鯤k蜴y鯦j鯧c鯨j鯩l鯪l螄s螈y鯬l鯭m鯮z鯯z鯰n鯱x鯲d鯳s鯴s鯵s鯶h鯷t鯹x鯺z鯻l鯼z鯽j鯾b鯿b鰀h蟛p鰁q鰂z鰃w蟣j鰄w鰅y鰆c鰇r鰈d鰉h鰊l鰋y鰌q鰍q鰎j鰏b蠶c鰑y蠻m衊m鰓s鰔j鰕x鰖t鰗h鰘s鰙r鰚h鰛w鰜j鰝h鰞w鰟p鰠s鰡l鰢m鰣s鰤s鰥g鰦z鰧t鰨t鰩y鰪e鰫y鰬q鰭q鰮w鰯r鰰h鰱l鰲a袼g裉k鰴h鰵m鰶j鰷t鰸q鰹j鰺s鰻m鰼x鰽q鰿j鱀j鱁z鱂j鱃x鱄z鱅y鱆z鱇k鱈x襖a襲x鱊y鱋q鱌x鱍b鱎j鱏x鱐s鱑h鱒z覈h觀g觔j鱔s鱕f鱖g鱗l鱘x鱙y鱚x鱛a鱜k鱞g鱟h鱠k鱡z鱢s鱣z鱤g鱥g計j鱦y鱧l鱨c鱩h鱪s鱫a訟s訥n訶h鱭j鱮x鱯h鱰s鱱l鱲l鱳l鱴m鱵z鱶x鱷e鱸l鱹g鱺l鱻x鱽d鱾j試s鱿y鲀t鲂f詬g鲃b鲄h鲅b詰j鲆p鲇n詳x鲈l鲉y鲊z鲋f詿g鲌b鲎h誄l鲏p鲐t鲑g鲒j鲓k鲔w鲕e鲖t鲗z鲘h鲙k鲚j鲛j鲝z鲞x鲟x誦s鲠g鲡l說s鲢l鲣j鲥s鲦t誹f鲧g鲨s鲩h鲪j鲫j鲬y鲭q鲮l鲯q鲰z鲱f鲲k鲳c鲴g鲵n鲶n鲷d鲹s鲺s諦d鲻z鲼f諭y鲽d鲾b鲿c鳀t諼x謄t鳁w鳂w鳄e謖s鳅q鳆f謾m譏j鳇h鳈q鳉j鳊b變b鳌a鳍q鳎t鳏g鳐y鳑p讞y鳓l鳔b鳕x鳗m鳘m鳙y鳚w鳜g鳝s鳟z鳠h鳡g鳢l鳣z鳤g鳥n鳦y鳧f豊l鳨l鳪b鳫y鳬f鳭d鳮j鳯f鳰n鳱g鳲s鳳f鳴m鳵b鳶y貞z買m費f貽y鳸h賀h鳹q鳺f鳻b鳼w鳽j鳾s鳿y鴀f鴁y鴂j鴃j鴄p賍z鴅h鴆z鴇b鴈y鴉y鴊z鴋f鴌f賚l賦f賫l鴎o賱y鴏d賲b鴐j鴑r鴒l鴓m鴔f鴕t鴖m鴗l鴘b鴙z鴚g鴛y鴜c鴝q鴞x鴟c鴠d鴡j鴣g鴤z鴥y鴦y鴧y鴨y鴩d鴪y贖s鴬y鴭d赾q鴰g鴱a鴲z鴳y鴴h鴵x鴶j鴷l鴸z鴹y鴺y鴻h鴼l鴽r趒t鴾m鴿g趕g鵁j鵂x鵃z鵄c鵅l鵆q鵇t鵈e趛y鵊j鵋j趢l鵌t鵍h鵎t趦z趬q趶k鵐w鵒y鵓b鵔j鵕j跊m鵖b鵗x鵘j鵙j鵚t鵛j鵜t鵝e鵞e鵟k鵠h鵢s鵣l鵤z跮d鵦l鵧p鵨s鵩f鵪a鵫z鵬p踇m鵭q鵮q鵯b踋j鵰d鵱l鵲q踐j鵳j鵴j鵵t鵶y鵷y踖j鵸q鵹l鵺y鵻z鵼k鵽d踜l鵾k鵿s鶀q踠w鶁j鶂y踤z踨z踼t鶄j鶅z鶆l鶇d蹅c蹆t蹌q蹎d蹏t鶉c鶊g鶋j鶌q鶍y鶎k鶏j鶐s鶑y鶒c鶓m躌w躤j鶖q躪l鶗t鶘h鶙t鶚e躳g躴l躼l軂l軅y軈y軍j鶜m鶝f鶞c鶟t鶠y鶡h鶢y鶣p鶤k鶥m鶦h軛e鶧y軞m軭k軯p軳p鶩w鶪j軸z輇q鶬c鶭f鶮h鶯y鶰y鶱x鶲w輑y鶴h鶵c輜z鶶t鶷x鶸r輠g鶹l鶺j鶻g鶼j輧p輪l輯j輳c輼w鶾h鶿c鷀c鷁y鷂y轋h轌x轓f鷄j鷅l鷆t鷇k鷈t鷉t鷊y鷋t鷌m鷍x鷎g鷏t鷐c鷑j鷒t鷓z轙y鷔a轚j鷕y鷖y轜e鷗o鷘c鷙z鷚l鷛y轣l鷝b鷞s鷟z鷠y鷡w鷢j鷣y鷤t鷥s鷦j鷨h鷩b鷪y鷫s鷬h辀z鷮j辎z鷯l辤c鷱g辧b鷲j鷳x鷴x鷵t鷶m鷷z鷸y鷹y鷺l鷻t鷼x鷽x辭c鷿p鸀z鸁l鸂x鸃y鸄j鸅z鸆y鸇z辴z鸈y鸊p鸋n鸌h辺d鸍m鸎y鸏m鸐d鸑y迀g鸒y鸓l鸔b鸕l鸖h鸗l鸘s鸙y鸚y鸛g鸜q鸝l鸞l鸠j鸢y鸤s鸧c鸨b鸩z鸪g鸫d鸬l鸮x鸰l鸱c鸲q鸴x鸶s鸷z鸸e鸹g鸺x鸻h鸼z鸾l鹀w鹁b鹂l鹄g鹆y鹇x迒h鹈t迓y鹉w鹋m鹌a鹍k迠c鹎b迡c鹐q鹑c迣z迦j迧c鹓y鹔s迶y鹖h鹗e迼j鹘g迿x鹙q鹚c鹛m逌y逘y鹜w鹝y鹞y鹟w鹠l鹡j鹢y鹣j鹥y鹦y鹨l鹩l鹪j逤s鹫j逩b逫z逰y鹬y鹭l鹮h鹯z鹱h鹲m鹳g鹵l鹷l鹸j鹹x鹻j鹼j鹽y鹾c逽n逿d遃y遆t遉z麁c麂j麃b麄c麅p麆z麇j麈z麉j麊m麋b麌y麍l麎c麏j達d麐l違w麑n麒q麔j遚c麕j麖j麗l麘x麙x麚j麛m麜l麝s麞z麟l麠j麢l麣y麤c麥m麧h麨c麩f麪m麫m麬f麭p麮q麯q麰m麱f麲x麳l麴q麵m麶c麷f麸f麹q麺m適s麼m麽m遬s麾h麿m遰d黀z遱l黁n遳c黃h黅j黆g黇t黈t黉h黊h黋k黌h黏n黐c黒h黓y黕d黖x黗t黙m黚q黛d黜c黝y點d黟y黠x黡y黢q黣m黤y黥q黦y黧l黨d黩d黪c黫y邍y黬y邎y黭y黮d邐l黯a黰z黱d黲c黳y黴m黵d黶y黷d黸l黹z黻f黼f黽m邗h黾m黿y鼀c鼁q鼂c鼃w鼄z鼅z鼆m鼇a邞f鼊b鼋y邨c鼌c邩h鼍t邫b邽g鼑d鼒z鼔g鼕d鼘y鼙p鼚c鼛g鼜c鼝y鼞t鼟t鼡n鼢f鼣f鼤w鼥b鼦d鼧t郋x鼨z郌g鼩q鼪s鼫s鼬y鼭s鼮t鼯w鼰j鼱j鼲h鼳j鼴y鼵t鼶s鼷x鼸x郞l鼹y鼺l鼼y鼽q鼾h鼿w齀w齁h齂x齃e齄z齅x郣b郩x齆w齇z齈n齉n齊q郰z郲l齌j齍z齎j齏j齑j齒c齓c齔c齕h齖y齗y齘x郼y齚z齛x齜z齝c齞y齟j齠t齡l齢l齣c齤q齥x鄅y鄋s齧n齨j鄍m齪c齫y齬y齭c鄒z齯n鄖y鄘y鄤m齱z齲q齳y齴y鄧d鄩x鄪b齶e齷w齸y齹c齺z齻d齼c齾y龀c龁h龂y鄳m鄹z龃j龅b龆t龇z龈y龉y龊c龌w龍l龏g鄿q酅x龑y龒l龓l龔g龕k龗l龘d龛k龜g龝q酨z龞b龢h龣j龤x龥y鳩j鳷z鴍w鴢y鴫t鴮w鴯e鵀r鵉l鵏b鵑j鵡w鵥k鶃y鶛j鶨c鶫d鶳s鶽s鷃y鷜l鷧y鷭f鷰y鷾y鸉y鹒g鹕h鹶j鹺c麀y麡q鼈b鼉t鼏m齋z齙b齦y齮y齰z齵y齽j龎p龖d龡c癷b唉a苯b瞓f硸n秎f斗d稥x坊f竕f告g龟g哼h解j擂l罖r芈m羒f羘z羛y羜z羣q羦h羪y羫q羭y羮g羳f羵f羶s羷l翂f裂l聁u膹f艈u漂p骑q砌q雀q蒊h蒶f蕒m蕡f是s伺c毝c苔t虲u蚠f蝊u蟇m衁h衇m衈e衖x衚h衜d衞w衟d衠z衯f衮g袰b袴k呀y訜f子z豶f亂l傯z僨f償c兇x喲Y嗯E堯Y塏K墳F墾K鶕A鶔r鶈q鱬r鱝f魵f髍m馩f馚f饙f餴f颃h霻f霣y雺w隫f闧u闇a閥f閟b間j镪q钿d鐼f鐢f鏦c鏜t鍂p奮f婁l媵y屢l嶠j幀z幗g憤f錖d錆q銽x橥z歸g殛j炷z甯n瘧n瘵z碡z糞f紛f羥q腳j膩n臺t莜y蓿x褸l誒e讜d讠y贁b趀c趓d趙z趸d跀y跥d輊z輒z轒f轕g遧z邚r邤x郬q郺y酳y酼h酽y醆z醈t醓t醔q醦c釀n釂j釈s釋s釒j釔y釕l釗z釙p釠l釢n釤s釥q鉀j釪h釫h釯m釿j鈃x鈄d鈇f鈈b鈎g鈏y鈑b鈖f鈙q鈚p鈝y鈟d鈠y鈨y鈩h鈱m鈵b鈾y鈿d鉅j鉇s鉒z鉔z鉗q鉞y鉡b鉢b鉥s鉧m鉬m鉲k鉳b鉵t鉽s鉾m銁j銃c銅t銍z銐c銑x銕t銗h銘m銠l銡j銣r銤m銨a銫s銬k銰a銱d銹x銼c銾h銿z鋃l鋊y鋍b鋏j鋐h鋒f鋓c鋔w鋜z鋝l鋫l鋱t鋲b鋶l鋻j鋾t錄l錈j錐z錔t錛b錝c錫x錰s錶b鍃h鍅f鍎t鍜x鍝y鍮t鍰h鍴d鎎k鎗q鎚c鎢w鎣y鎥t鏓c鏷p鐍j鐳l鐶h鐺d鑃d鑍y鑙j鑚z鑛k鑭l鑶c钂t钘x铇b锳y镮h镺a镾m閁m閅m閯s閵l闂h闃g闑n闒t闔h闢p闣d陥x陬z陯l韁j韄h頄q順s顑k飠s饁y饓c駁b駄t駯z騘c騤k騫q騵y鰐e鰒f鰳l鰾p鱉b鱓s鳋s鳒j鳛x鹧z鹴s黂f黺f鼖f齩y驀m髊c髨k髵e髶e髼p鬂b鬦d鬫k鬱y魈x魌q魓b魛d魨t魱h鮁b鮗d鮿z鯙c鯉l鯥l鯫z鯸h龐p"; + /// + /// 常用简体汉字 + /// + public const string SimplifiedChinese = "一乙二十丁厂七卜人入八九几儿了力乃刀又三于干亏士工土才寸下大丈与万上小口巾山千乞川亿个勺久凡及夕丸么广亡门义之尸弓己已子卫也女飞刃习叉马乡丰王井开夫天无元专云扎艺木五支厅不太犬区历尤友匹车巨牙屯比互切瓦止少日中冈贝内水见午牛手毛气升长仁什片仆化仇币仍仅斤爪反介父从今凶分乏公仓月氏勿欠风丹匀乌凤勾文六方火为斗忆订计户认心尺引丑巴孔队办以允予劝双书幻玉刊示末未击打巧正扑扒功扔去甘世古节本术可丙左厉右石布龙平灭轧东卡北占业旧帅归且旦目叶甲申叮电号田由史只央兄叼叫另叨叹四生失禾丘付仗代仙们仪白仔他斥瓜乎丛令用甩印乐句匆册犯外处冬鸟务包饥主市立闪兰半汁汇头汉宁穴它讨写让礼训必议讯记永司尼民出辽奶奴加召皮边发孕圣对台矛纠母幼丝式刑动扛寺吉扣考托老执巩圾扩扫地扬场耳共芒亚芝朽朴机权过臣再协西压厌在有百存而页匠夸夺灰达列死成夹轨邪划迈毕至此贞师尘尖劣光当早吐吓虫曲团同吊吃因吸吗屿帆岁回岂刚则肉网年朱先丢舌竹迁乔伟传乒乓休伍伏优伐延件任伤价份华仰仿伙伪自血向似后行舟全会杀合兆企众爷伞创肌朵杂危旬旨负各名多争色壮冲冰庄庆亦刘齐交次衣产决充妄闭问闯羊并关米灯州汗污江池汤忙兴宇守宅字安讲军许论农讽设访寻那迅尽导异孙阵阳收阶阴防奸如妇好她妈戏羽观欢买红纤级约纪驰巡寿弄麦形进戒吞远违运扶抚坛技坏扰拒找批扯址走抄坝贡攻赤折抓扮抢孝均抛投坟抗坑坊抖护壳志扭块声把报却劫芽花芹芬苍芳严芦劳克苏杆杠杜材村杏极李杨求更束豆两丽医辰励否还歼来连步坚旱盯呈时吴助县里呆园旷围呀吨足邮男困吵串员听吩吹呜吧吼别岗帐财针钉告我乱利秃秀私每兵估体何但伸作伯伶佣低你住位伴身皂佛近彻役返余希坐谷妥含邻岔肝肚肠龟免狂犹角删条卵岛迎饭饮系言冻状亩况床库疗应冷这序辛弃冶忘闲间闷判灶灿弟汪沙汽沃泛沟没沈沉怀忧快完宋宏牢究穷灾良证启评补初社识诉诊词译君灵即层尿尾迟局改张忌际陆阿陈阻附妙妖妨努忍劲鸡驱纯纱纳纲驳纵纷纸纹纺驴纽奉玩环武青责现表规抹拢拔拣担坦押抽拐拖拍者顶拆拥抵拘势抱垃拉拦拌幸招坡披拨择抬其取苦若茂苹苗英范直茄茎茅林枝杯柜析板松枪构杰述枕丧或画卧事刺枣雨卖矿码厕奔奇奋态欧垄妻顷转斩轮软到非叔肯齿些虎虏肾贤尚旺具果味昆国昌畅明易昂典固忠咐呼鸣咏呢岸岩帖罗帜岭凯败贩购图钓制知垂牧物乖刮秆和季委佳侍供使例版侄侦侧凭侨佩货依的迫质欣征往爬彼径所舍金命斧爸采受乳贪念贫肤肺肢肿胀朋股肥服胁周昏鱼兔狐忽狗备饰饱饲变京享店夜庙府底剂郊废净盲放刻育闸闹郑券卷单炒炊炕炎炉沫浅法泄河沾泪油泊沿泡注泻泳泥沸波泼泽治怖性怕怜怪学宝宗定宜审宙官空帘实试郎诗肩房诚衬衫视话诞询该详建肃录隶居届刷屈弦承孟孤陕降限妹姑姐姓始驾参艰线练组细驶织终驻驼绍经贯奏春帮珍玻毒型挂封持项垮挎城挠政赴赵挡挺括拴拾挑指垫挣挤拼挖按挥挪某甚革荐巷带草茧茶荒茫荡荣故胡南药标枯柄栋相查柏柳柱柿栏树要咸威歪研砖厘厚砌砍面耐耍牵残殃轻鸦皆背战点临览竖省削尝是盼眨哄显哑冒映星昨畏趴胃贵界虹虾蚁思蚂虽品咽骂哗咱响哈咬咳哪炭峡罚贱贴骨钞钟钢钥钩卸缸拜看矩怎牲选适秒香种秋科重复竿段便俩贷顺修保促侮俭俗俘信皇泉鬼侵追俊盾待律很须叙剑逃食盆胆胜胞胖脉勉狭狮独狡狱狠贸怨急饶蚀饺饼弯将奖哀亭亮度迹庭疮疯疫疤姿亲音帝施闻阀阁差养美姜叛送类迷前首逆总炼炸炮烂剃洁洪洒浇浊洞测洗活派洽染济洋洲浑浓津恒恢恰恼恨举觉宣室宫宪突穿窃客冠语扁袄祖神祝误诱说诵垦退既屋昼费陡眉孩除险院娃姥姨姻娇怒架贺盈勇怠柔垒绑绒结绕骄绘给络骆绝绞统耕耗艳泰珠班素蚕顽盏匪捞栽捕振载赶起盐捎捏埋捉捆捐损都哲逝捡换挽热恐壶挨耻耽恭莲莫荷获晋恶真框桂档桐株桥桃格校核样根索哥速逗栗配翅辱唇夏础破原套逐烈殊顾轿较顿毙致柴桌虑监紧党晒眠晓鸭晃晌晕蚊哨哭恩唤啊唉罢峰圆贼贿钱钳钻铁铃铅缺氧特牺造乘敌秤租积秧秩称秘透笔笑笋债借值倚倾倒倘俱倡候俯倍倦健臭射躬息徒徐舰舱般航途拿爹爱颂翁脆脂胸胳脏胶脑狸狼逢留皱饿恋桨浆衰高席准座脊症病疾疼疲效离唐资凉站剖竞部旁旅畜阅羞瓶拳粉料益兼烤烘烦烧烛烟递涛浙涝酒涉消浩海涂浴浮流润浪浸涨烫涌悟悄悔悦害宽家宵宴宾窄容宰案请朗诸读扇袜袖袍被祥课谁调冤谅谈谊剥恳展剧屑弱陵陶陷陪娱娘通能难预桑绢绣验继球理捧堵描域掩捷排掉堆推掀授教掏掠培接控探据掘职基著勒黄萌萝菌菜萄菊萍菠营械梦梢梅检梳梯桶救副票戚爽聋袭盛雪辅辆虚雀堂常匙晨睁眯眼悬野啦晚啄距跃略蛇累唱患唯崖崭崇圈铜铲银甜梨犁移笨笼笛符第敏做袋悠偿偶偷您售停偏假得衔盘船斜盒鸽悉欲彩领脚脖脸脱象够猜猪猎猫猛馅馆凑减毫麻痒痕廊康庸鹿盗章竟商族旋望率着盖粘粗粒断剪兽清添淋淹渠渐混渔淘液淡深婆梁渗情惜惭悼惧惕惊惨惯寇寄宿窑密谋谎祸谜逮敢屠弹随蛋隆隐婚婶颈绩绪续骑绳维绵绸绿琴斑替款堪搭塔越趁趋超提堤博揭喜插揪搜煮援裁搁搂搅握揉斯期欺联散惹葬葛董葡敬葱落朝辜葵棒棋植森椅椒棵棍棉棚棕惠惑逼厨厦硬确雁殖裂雄暂雅辈悲紫辉敞赏掌晴暑最量喷晶喇遇喊景践跌跑遗蛙蛛蜓喝喂喘喉幅帽赌赔黑铸铺链销锁锄锅锈锋锐短智毯鹅剩稍程稀税筐等筑策筛筒答筋筝傲傅牌堡集焦傍储奥街惩御循艇舒番释禽腊脾腔鲁猾猴然馋装蛮就痛童阔善羡普粪尊道曾焰港湖渣湿温渴滑湾渡游滋溉愤慌惰愧愉慨割寒富窜窝窗遍裕裤裙谢谣谦属屡强粥疏隔隙絮嫂登缎缓编骗缘瑞魂肆摄摸填搏塌鼓摆携搬摇搞塘摊蒜勤鹊蓝墓幕蓬蓄蒙蒸献禁楚想槐榆楼概赖酬感碍碑碎碰碗碌雷零雾雹输督龄鉴睛睡睬鄙愚暖盟歇暗照跨跳跪路跟遣蛾蜂嗓置罪罩错锡锣锤锦键锯矮辞稠愁筹签简毁舅鼠催傻像躲微愈遥腰腥腹腾腿触解酱痰廉新韵意粮数煎塑慈煤煌满漠源滤滥滔溪溜滚滨粱滩慎誉塞谨福群殿辟障嫌嫁叠缝缠静碧璃墙撇嘉摧截誓境摘摔聚蔽慕暮蔑模榴榜榨歌遭酷酿酸磁愿需弊裳颗嗽蜻蜡蝇蜘赚锹锻舞稳算箩管僚鼻魄貌膜膊膀鲜疑馒裹敲豪膏遮腐瘦辣竭端旗精歉熄熔漆漂漫滴演漏慢寨赛察蜜谱嫩翠熊凳骡缩慧撕撒趣趟撑播撞撤增聪鞋蕉蔬横槽樱橡飘醋醉震霉瞒题暴瞎影踢踏踩踪蝶蝴嘱墨镇靠稻黎稿稼箱箭篇僵躺僻德艘膝膛熟摩颜毅糊遵潜潮懂额慰劈操燕薯薪薄颠橘整融醒餐嘴蹄器赠默镜赞篮邀衡膨雕磨凝辨辩糖糕燃澡激懒壁避缴戴擦鞠藏霜霞瞧蹈螺穗繁辫赢糟糠燥臂翼骤鞭覆蹦镰翻鹰警攀蹲颤瓣爆疆壤耀躁嚼嚷籍魔灌蠢霸露囊罐"; +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Convert.cs b/src/Util.Core/Helpers/Convert.cs new file mode 100644 index 000000000..626fc441d --- /dev/null +++ b/src/Util.Core/Helpers/Convert.cs @@ -0,0 +1,397 @@ +namespace Util.Helpers; + +/// +/// 类型转换 +/// +public static class Convert { + + #region ToInt(转换为32位整型) + + /// + /// 转换为32位整型 + /// + /// 输入值 + public static int ToInt( object input ) { + return ToIntOrNull( input ) ?? 0; + } + + #endregion + + #region ToIntOrNull(转换为32位可空整型) + + /// + /// 转换为32位可空整型 + /// + /// 输入值 + public static int? ToIntOrNull( object input ) { + var success = int.TryParse( input.SafeString(), out var result ); + if( success ) + return result; + try { + var temp = ToDoubleOrNull( input, 0 ); + if( temp == null ) + return null; + return System.Convert.ToInt32( temp ); + } + catch { + return null; + } + } + + #endregion + + #region ToLong(转换为64位整型) + + /// + /// 转换为64位整型 + /// + /// 输入值 + public static long ToLong( object input ) { + return ToLongOrNull( input ) ?? 0; + } + + #endregion + + #region ToLongOrNull(转换为64位可空整型) + + /// + /// 转换为64位可空整型 + /// + /// 输入值 + public static long? ToLongOrNull( object input ) { + var success = long.TryParse( input.SafeString(), out var result ); + if( success ) + return result; + try { + var temp = ToDecimalOrNull( input, 0 ); + if( temp == null ) + return null; + return System.Convert.ToInt64( temp ); + } + catch { + return null; + } + } + + #endregion + + #region ToFloat(转换为32位浮点型) + + /// + /// 转换为32位浮点型,并按指定小数位舍入 + /// + /// 输入值 + /// 小数位数 + public static float ToFloat( object input, int? digits = null ) { + return ToFloatOrNull( input, digits ) ?? 0; + } + + #endregion + + #region ToFloatOrNull(转换为32位可空浮点型) + + /// + /// 转换为32位可空浮点型,并按指定小数位舍入 + /// + /// 输入值 + /// 小数位数 + public static float? ToFloatOrNull( object input, int? digits = null ) { + var success = float.TryParse( input.SafeString(), out var result ); + if( !success ) + return null; + if( digits == null ) + return result; + return (float)Math.Round( result, digits.Value ); + } + + #endregion + + #region ToDouble(转换为64位浮点型) + + /// + /// 转换为64位浮点型,并按指定小数位舍入 + /// + /// 输入值 + /// 小数位数 + public static double ToDouble( object input, int? digits = null ) { + return ToDoubleOrNull( input, digits ) ?? 0; + } + + #endregion + + #region ToDoubleOrNull(转换为64位可空浮点型) + + /// + /// 转换为64位可空浮点型,并按指定小数位舍入 + /// + /// 输入值 + /// 小数位数 + public static double? ToDoubleOrNull( object input, int? digits = null ) { + var success = double.TryParse( input.SafeString(), out var result ); + if( !success ) + return null; + if( digits == null ) + return result; + return Math.Round( result, digits.Value ); + } + + #endregion + + #region ToDecimal(转换为128位浮点型) + + /// + /// 转换为128位浮点型,并按指定小数位舍入 + /// + /// 输入值 + /// 小数位数 + public static decimal ToDecimal( object input, int? digits = null ) { + return ToDecimalOrNull( input, digits ) ?? 0; + } + + #endregion + + #region ToDecimalOrNull(转换为128位可空浮点型) + + /// + /// 转换为128位可空浮点型,并按指定小数位舍入 + /// + /// 输入值 + /// 小数位数 + public static decimal? ToDecimalOrNull( object input, int? digits = null ) { + var success = decimal.TryParse( input.SafeString(), out var result ); + if( !success ) + return null; + if( digits == null ) + return result; + return Math.Round( result, digits.Value ); + } + + #endregion + + #region ToBool(转换为布尔值) + + /// + /// 转换为布尔值 + /// + /// 输入值 + public static bool ToBool( object input ) { + return ToBoolOrNull( input ) ?? false; + } + + #endregion + + #region ToBoolOrNull(转换为可空布尔值) + + /// + /// 转换为可空布尔值 + /// + /// 输入值 + public static bool? ToBoolOrNull( object input ) { + var value = input.SafeString(); + switch( value ) { + case "1": + return true; + case "0": + return false; + } + return bool.TryParse( value, out var result ) ? result : null; + } + + #endregion + + #region ToDateTime(转换为日期) + + /// + /// 转换为日期 + /// + /// 输入值 + public static DateTime ToDateTime( object input ) { + return ToDateTimeOrNull( input ) ?? DateTime.MinValue; + } + + #endregion + + #region ToDateTimeOrNull(转换为可空日期) + + /// + /// 转换为可空日期 + /// + /// 输入值 + public static DateTime? ToDateTimeOrNull( object input ) { + var success = DateTime.TryParse( input.SafeString(), out var result ); + if( success == false ) + return null; + return result; + } + + #endregion + + #region ToGuid(转换为Guid) + + /// + /// 转换为Guid + /// + /// 输入值 + public static Guid ToGuid( object input ) { + return ToGuidOrNull( input ) ?? Guid.Empty; + } + + #endregion + + #region ToGuidOrNull(转换为可空Guid) + + /// + /// 转换为可空Guid + /// + /// 输入值 + public static Guid? ToGuidOrNull( object input ) { + if( input == null ) + return null; + if( input.GetType() == typeof( byte[] ) ) + return new Guid( (byte[])input ); + return Guid.TryParse( input.SafeString(), out var result ) ? result : null; + } + + #endregion + + #region ToGuidList(转换为Guid集合) + + /// + /// 转换为Guid集合 + /// + /// 以逗号分隔的Guid集合字符串,范例:83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A + public static List ToGuidList( string input ) { + return ToList( input ); + } + + #endregion + + #region ToBytes(转换为字节数组) + + /// + /// 转换为字节数组 + /// + /// 输入值 + public static byte[] ToBytes( string input ) { + return ToBytes( input, Encoding.UTF8 ); + } + + /// + /// 转换为字节数组 + /// + /// 输入值 + /// 字符编码 + public static byte[] ToBytes( string input, Encoding encoding ) { + return string.IsNullOrWhiteSpace( input ) ? new byte[] { } : encoding.GetBytes( input ); + } + + #endregion + + #region ToBase64(转换为base64字符串) + + /// + /// 转换为base64字符串 + /// + /// 输入值 + public static string ToBase64( string input ) { + return input.IsEmpty() ? null : System.Convert.ToBase64String( Encoding.UTF8.GetBytes( input ) ); + } + + #endregion + + #region ToList(泛型集合转换) + + /// + /// 泛型集合转换 + /// + /// 目标元素类型 + /// 以逗号分隔的元素集合字符串,范例:83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A + public static List ToList( string input ) { + var result = new List(); + if( string.IsNullOrWhiteSpace( input ) ) + return result; + var array = input.Split( ',' ); + result.AddRange( from each in array where !string.IsNullOrWhiteSpace( each ) select To( each ) ); + return result; + } + + #endregion + + #region To(通用泛型转换) + + /// + /// 通用泛型转换 + /// + /// 目标类型 + /// 输入值 + public static T To( object input ) { + if( input == null ) + return default; + if( input is string && string.IsNullOrWhiteSpace( input.ToString() ) ) + return default; + var type = Common.GetType(); + var typeName = type.Name.ToUpperInvariant(); + try { + if( typeName == "STRING" || typeName == "GUID" ) + return (T)TypeDescriptor.GetConverter( typeof( T ) ).ConvertFromInvariantString( input.ToString() ); + if( type.IsEnum ) + return Enum.Parse( input ); + if( input is IConvertible ) + return (T)System.Convert.ChangeType( input, type, CultureInfo.InvariantCulture ); + if( input is JsonElement element ) { + var value = element.GetRawText(); + var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + return Json.ToObject( value, options ); + } + return (T)input; + } + catch { + return default; + } + } + + #endregion + + #region ToDictionary(对象转换为属性名值对) + + /// + /// 对象转换为属性名值对 + /// + /// 对象 + public static IDictionary ToDictionary( object data ) { + return ToDictionary( data, false ); + } + + /// + /// 对象转换为属性名值对 + /// + /// 对象 + /// 是否使用显示名称,可使用[Description] 或 [DisplayName]特性设置 + public static IDictionary ToDictionary( object data,bool useDisplayName ) { + var result = new Dictionary(); + if( data == null ) + return result; + if( data is IEnumerable> dic ) + return new Dictionary( dic ); + foreach( PropertyDescriptor property in TypeDescriptor.GetProperties( data ) ) { + var value = property.GetValue( data ); + result.Add( GetPropertyDescriptorName( property, useDisplayName ), value ); + } + return result; + } + + /// + /// 获取属性名 + /// + private static string GetPropertyDescriptorName( PropertyDescriptor property, bool useDisplayName ) { + if ( useDisplayName == false ) + return property.Name; + if( string.IsNullOrEmpty( property.Description ) == false ) + return property.Description; + if( string.IsNullOrEmpty( property.DisplayName ) == false ) + return property.DisplayName; + return property.Name; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Culture.cs b/src/Util.Core/Helpers/Culture.cs new file mode 100644 index 000000000..daf0e810a --- /dev/null +++ b/src/Util.Core/Helpers/Culture.cs @@ -0,0 +1,63 @@ +namespace Util.Helpers; + +/// +/// 区域文化 +/// +public static class Culture { + /// + /// 获取当前区域文化 + /// + public static CultureInfo GetCurrentCulture() { + return CultureInfo.CurrentCulture; + } + + /// + /// 获取当前UI区域文化 + /// + public static CultureInfo GetCurrentUICulture() { + return CultureInfo.CurrentUICulture; + } + + /// + /// 获取当前区域文化名称 + /// + public static string GetCurrentCultureName() { + return CultureInfo.CurrentCulture.Name; + } + + /// + /// 获取当前UI区域文化名称 + /// + public static string GetCurrentUICultureName() { + return CultureInfo.CurrentUICulture.Name; + } + + /// + /// 获取当前区域文化信息列表,包含所有父区域文化 + /// + public static List GetCurrentCultures() { + return GetCultures( GetCurrentCulture() ); + } + + /// + /// 获取当前UI区域文化信息列表,包含所有父区域文化 + /// + public static List GetCurrentUICultures() { + return GetCultures( GetCurrentUICulture() ); + } + + /// + /// 获取区域文化信息列表,包含所有父区域文化 + /// + /// 区域文化信息 + public static List GetCultures( CultureInfo culture ) { + var result = new List(); + if ( culture == null ) + return result; + while ( culture.Equals( culture.Parent ) == false ) { + result.Add( culture ); + culture = culture.Parent; + } + return result; + } +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Enum.cs b/src/Util.Core/Helpers/Enum.cs new file mode 100644 index 000000000..b913f1bd6 --- /dev/null +++ b/src/Util.Core/Helpers/Enum.cs @@ -0,0 +1,147 @@ +using Util.Properties; + +namespace Util.Helpers; + +/// +/// 枚举操作 +/// +public static class Enum { + /// + /// 获取实例 + /// + /// 枚举类型 + /// 成员名或值,范例:Enum1枚举有成员A=0,则传入"A"或"0"获取 Enum1.A + public static TEnum Parse( object member ) { + string value = member.SafeString(); + if( string.IsNullOrWhiteSpace( value ) ) { + if( typeof( TEnum ).IsGenericType ) + return default( TEnum ); + throw new ArgumentNullException( nameof( member ) ); + } + return (TEnum)System.Enum.Parse( Common.GetType(), value, true ); + } + + /// + /// 获取成员名 + /// + /// 枚举类型 + /// 成员名、值、实例均可,范例:Enum1枚举有成员A=0,则传入Enum1.A或0,获取成员名"A" + public static string GetName( object member ) { + return GetName( Common.GetType(), member ); + } + + /// + /// 获取成员名 + /// + /// 枚举类型 + /// 成员名、值、实例均可 + public static string GetName( Type type, object member ) { + if( type == null ) + return string.Empty; + if( member == null ) + return string.Empty; + if( member is string ) + return member.ToString(); + if( type.GetTypeInfo().IsEnum == false ) + return string.Empty; + return System.Enum.GetName( type, member ); + } + + /// + /// 获取成员值 + /// + /// 枚举类型 + /// 成员名、值、实例均可,范例:Enum1枚举有成员A=0,可传入"A"、0、Enum1.A,获取值0 + public static int? GetValue( object member ) { + return GetValue( Common.GetType(), member ); + } + + /// + /// 获取成员值 + /// + /// 枚举类型 + /// 成员名、值、实例均可 + public static int? GetValue( Type type, object member ) { + string value = member.SafeString(); + if ( value.IsEmpty() ) + return null; + var result = System.Enum.Parse( type, value, true ); + return Convert.To( result ); + } + + /// + /// 获取描述,使用System.ComponentModel.Description特性设置描述 + /// + /// 枚举类型 + /// 成员名、值、实例均可 + public static string GetDescription( object member ) { + return Reflection.GetDescription( GetName( member ) ); + } + + /// + /// 获取描述,使用System.ComponentModel.Description特性设置描述 + /// + /// 枚举类型 + /// 成员名、值、实例均可 + public static string GetDescription( Type type, object member ) { + return Reflection.GetDescription( type, GetName( type, member ) ); + } + + /// + /// 获取项集合,文本设置为Description,值为Value + /// + /// 枚举类型 + public static List GetItems() { + return GetItems( typeof( TEnum ) ); + } + + /// + /// 获取项集合,文本设置为Description,值为Value + /// + /// 枚举类型 + public static List GetItems( Type type ) { + type = Common.GetType( type ); + if( type.IsEnum == false ) + throw new InvalidOperationException( string.Format( R.TypeNotEnum,type ) ); + var result = new List(); + foreach( var field in type.GetFields() ) + AddItem( type, result, field ); + return result.OrderBy( t => t.SortId ).ToList(); + } + + /// + /// 添加描述项 + /// + private static void AddItem( Type type, ICollection result, FieldInfo field ) { + if( !field.FieldType.IsEnum ) + return; + var value = GetValue( type, field.Name ); + var description = Reflection.GetDescription( field ); + result.Add( new Item( description, value, value ) ); + } + + /// + /// 获取名称集合 + /// + /// 枚举类型 + public static List GetNames() { + return GetNames( typeof( TEnum ) ); + } + + /// + /// 获取名称集合 + /// + /// 枚举类型 + public static List GetNames( Type type ) { + type = Common.GetType( type ); + if( type.IsEnum == false ) + throw new InvalidOperationException( string.Format( R.TypeNotEnum, type ) ); + var result = new List(); + foreach ( var field in type.GetFields() ) { + if( !field.FieldType.IsEnum ) + continue; + result.Add( field.Name ); + } + return result; + } +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Environment.cs b/src/Util.Core/Helpers/Environment.cs new file mode 100644 index 000000000..d81cbd608 --- /dev/null +++ b/src/Util.Core/Helpers/Environment.cs @@ -0,0 +1,89 @@ +namespace Util.Helpers; + +/// +/// 环境操作 +/// +public static class Environment { + /// + /// DOTNET_ENVIRONMENT + /// + private const string DOTNET_ENVIRONMENT = "DOTNET_ENVIRONMENT"; + /// + /// ASPNETCORE_ENVIRONMENT + /// + private const string ASPNETCORE_ENVIRONMENT = "ASPNETCORE_ENVIRONMENT"; + /// + /// Development + /// + private const string Development = "Development"; + /// + /// 换行符 + /// + public static string NewLine => System.Environment.NewLine; + /// + /// 是否测试环境 + /// + public static bool IsTest { get; set; } + + /// + /// 设置环境变量 + /// + /// 环境变量名 + /// 值 + public static void SetEnvironmentVariable( string name, object value ) { + System.Environment.SetEnvironmentVariable( name, value.SafeString() ); + } + + /// + /// 获取环境变量 + /// + /// 环境变量名 + public static string GetEnvironmentVariable( string name ) { + return System.Environment.GetEnvironmentVariable( name ); + } + + /// + /// 获取环境变量 + /// + /// 环境变量名 + public static T GetEnvironmentVariable( string name ) { + return Convert.To( GetEnvironmentVariable( name ) ); + } + + /// + /// 获取环境名称 + /// + public static string GetEnvironmentName() { + var environment = GetEnvironmentVariable( ASPNETCORE_ENVIRONMENT ); + if ( environment.IsEmpty() == false ) + return environment; + return GetEnvironmentVariable( DOTNET_ENVIRONMENT ); + } + + /// + /// 设置开发环境变量,如果环境变量已设置则忽略 + /// + public static void SetDevelopment() { + var environment = GetEnvironmentVariable( DOTNET_ENVIRONMENT ); + if ( environment.IsEmpty() == false ) + return; + environment = GetEnvironmentVariable( ASPNETCORE_ENVIRONMENT ); + if ( environment.IsEmpty() == false ) + return; + SetEnvironmentVariable( DOTNET_ENVIRONMENT, Development ); + SetEnvironmentVariable( ASPNETCORE_ENVIRONMENT, Development ); + } + + /// + /// 是否开发环境 + /// + public static bool IsDevelopment() { + var environment = GetEnvironmentVariable( DOTNET_ENVIRONMENT ); + if ( environment == Development ) + return true; + environment = GetEnvironmentVariable( ASPNETCORE_ENVIRONMENT ); + if ( environment == Development ) + return true; + return false; + } +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/File.cs b/src/Util.Core/Helpers/File.cs new file mode 100644 index 000000000..704501804 --- /dev/null +++ b/src/Util.Core/Helpers/File.cs @@ -0,0 +1,454 @@ +namespace Util.Helpers; + +/// +/// 文件流操作 +/// +public static class File { + + #region ToBytes + + /// + /// 流转换为字节数组 + /// + /// 流 + public static byte[] ToBytes( Stream stream ) { + stream.Seek( 0, SeekOrigin.Begin ); + var buffer = new byte[stream.Length]; + stream.Read( buffer, 0, buffer.Length ); + return buffer; + } + + /// + /// 字符串转换成字节数组 + /// + /// 数据,默认字符编码utf-8 + public static byte[] ToBytes( string data ) { + return ToBytes( data, Encoding.UTF8 ); + } + + /// + /// 字符串转换成字节数组 + /// + /// 数据 + /// 字符编码 + public static byte[] ToBytes( string data, Encoding encoding ) { + if ( string.IsNullOrWhiteSpace( data ) ) + return Array.Empty(); + return encoding.GetBytes( data ); + } + + #endregion + + #region ToBytesAsync + + /// + /// 流转换为字节数组 + /// + /// 流 + /// 取消令牌 + public static async Task ToBytesAsync( Stream stream, CancellationToken cancellationToken = default ) { + stream.Seek( 0, SeekOrigin.Begin ); + var buffer = new byte[stream.Length]; + await stream.ReadAsync( buffer, 0, buffer.Length, cancellationToken ); + return buffer; + } + + #endregion + + #region ToStream + + /// + /// 字符串转换成流 + /// + /// 数据 + public static Stream ToStream( string data ) { + return ToStream( data, Encoding.UTF8 ); + } + + /// + /// 字符串转换成流 + /// + /// 数据 + /// 字符编码 + public static Stream ToStream( string data, Encoding encoding ) { + if ( data.IsEmpty() ) + return Stream.Null; + return new MemoryStream( ToBytes( data, encoding ) ); + } + + #endregion + + #region FileExists + + /// + /// 判断文件是否存在 + /// + /// 文件绝对路径 + public static bool FileExists( string path ) { + return System.IO.File.Exists( path ); + } + + #endregion + + #region DirectoryExists + + /// + /// 判断目录是否存在 + /// + /// 目录绝对路径 + public static bool DirectoryExists( string path ) { + return Directory.Exists( path ); + } + + #endregion + + #region CreateDirectory + + /// + /// 创建目录 + /// + /// 文件或目录绝对路径 + public static void CreateDirectory( string path ) { + if ( path.IsEmpty() ) + return; + var file = new FileInfo( path ); + var directoryPath = file.Directory?.FullName; + if ( Directory.Exists( directoryPath ) ) + return; + Directory.CreateDirectory( directoryPath ); + } + + #endregion + + #region ReadToString + + /// + /// 读取文件到字符串 + /// + /// 文件绝对路径 + public static string ReadToString( string filePath ) { + return ReadToString( filePath, Encoding.UTF8 ); + } + + /// + /// 读取文件到字符串 + /// + /// 文件绝对路径 + /// 字符编码 + public static string ReadToString( string filePath, Encoding encoding ) { + if ( System.IO.File.Exists( filePath ) == false ) + return string.Empty; + using var reader = new StreamReader( filePath, encoding ); + return reader.ReadToEnd(); + } + + /// + /// 读取流转换成字符串 + /// + /// 流 + /// 字符编码 + /// 缓冲区大小 + /// 读取完成是否释放流,默认为true + public static string ReadToString( Stream stream, Encoding encoding = null, int bufferSize = 1024 * 2, bool isCloseStream = true ) { + if ( stream == null ) + return string.Empty; + encoding ??= Encoding.UTF8; + if ( stream.CanRead == false ) + return string.Empty; + using var reader = new StreamReader( stream, encoding, true, bufferSize, !isCloseStream ); + if ( stream.CanSeek ) + stream.Seek( 0, SeekOrigin.Begin ); + var result = reader.ReadToEnd(); + if ( stream.CanSeek ) + stream.Seek( 0, SeekOrigin.Begin ); + return result; + } + + #endregion + + #region ReadToStringAsync + + /// + /// 读取文件到字符串 + /// + /// 文件绝对路径 + public static async Task ReadToStringAsync( string filePath ) { + return await ReadToStringAsync( filePath, Encoding.UTF8 ); + } + + /// + /// 读取文件到字符串 + /// + /// 文件绝对路径 + /// 字符编码 + public static async Task ReadToStringAsync( string filePath, Encoding encoding ) { + if ( System.IO.File.Exists( filePath ) == false ) + return string.Empty; + using var reader = new StreamReader( filePath, encoding ); + return await reader.ReadToEndAsync(); + } + + #endregion + + #region ReadToStream + + /// + /// 读取文件流 + /// + /// 文件绝对路径 + public static Stream ReadToStream( string filePath ) { + try { + return new FileStream( filePath, FileMode.Open ); + } + catch { + return null; + } + } + + #endregion + + #region ReadToBytes + + /// + /// 将文件读取到字节流中 + /// + /// 文件的绝对路径 + public static byte[] ReadToBytes( string filePath ) { + if ( !System.IO.File.Exists( filePath ) ) + return null; + var fileInfo = new FileInfo( filePath ); + using var reader = new BinaryReader( fileInfo.Open( FileMode.Open ) ); + return reader.ReadBytes( (int)fileInfo.Length ); + } + + /// + /// 读取流转换成字节数组 + /// + /// 流 + public static byte[] ReadToBytes( Stream stream ) { + if ( stream == null ) + return null; + if ( stream.CanRead == false ) + return null; + if ( stream.CanSeek ) + stream.Seek( 0, SeekOrigin.Begin ); + var buffer = new byte[stream.Length]; + stream.Read( buffer, 0, buffer.Length ); + if ( stream.CanSeek ) + stream.Seek( 0, SeekOrigin.Begin ); + return buffer; + } + + #endregion + + #region ReadToBytesAsync + + /// + /// 读取流转换成字节数组 + /// + /// 流 + /// 取消令牌 + public static async Task ReadToBytesAsync( Stream stream, CancellationToken cancellationToken = default ) { + if ( stream == null ) + return null; + if ( stream.CanRead == false ) + return null; + if ( stream.CanSeek ) + stream.Seek( 0, SeekOrigin.Begin ); + var buffer = new byte[stream.Length]; + await stream.ReadAsync( buffer, 0, buffer.Length, cancellationToken ); + if ( stream.CanSeek ) + stream.Seek( 0, SeekOrigin.Begin ); + return buffer; + } + + #endregion + + #region ReadToMemoryStreamAsync + + /// + /// 读取文件到内存流 + /// + /// 文件绝对路径 + /// 取消令牌 + public static async Task ReadToMemoryStreamAsync( string filePath, CancellationToken cancellationToken = default ) { + try { + if ( FileExists( filePath ) == false ) + return null; + var memoryStream = new MemoryStream(); + await using var stream = new FileStream( filePath, FileMode.Open ); + await stream.CopyToAsync( memoryStream, cancellationToken ).ConfigureAwait( false ); + return memoryStream; + } + catch { + return null; + } + } + + #endregion + + #region Write + + /// + /// 将字符串写入文件 + /// + /// 文件绝对路径 + /// 内容 + public static void Write( string filePath, string content ) { + if ( content.IsEmpty() ) + return; + Write( filePath, Convert.ToBytes( content ) ); + } + + /// + /// 将流写入文件 + /// + /// 文件绝对路径 + /// 内容 + public static void Write( string filePath, Stream content ) { + if ( content == null ) + return; + using ( content ) { + var bytes = ToBytes( content ); + Write( filePath, bytes ); + } + } + + /// + /// 将字节流写入文件 + /// + /// 文件绝对路径 + /// 内容 + public static void Write( string filePath, byte[] content ) { + if ( string.IsNullOrWhiteSpace( filePath ) ) + return; + if ( content == null ) + return; + CreateDirectory( filePath ); + System.IO.File.WriteAllBytes( filePath, content ); + } + + #endregion + + #region WriteAsync + + /// + /// 将字符串写入文件 + /// + /// 文件绝对路径 + /// 内容 + /// 取消令牌 + public static async Task WriteAsync( string filePath, string content, CancellationToken cancellationToken = default ) { + if ( content.IsEmpty() ) + return; + await WriteAsync( filePath, Convert.ToBytes( content ), cancellationToken ); + } + + /// + /// 将流写入文件 + /// + /// 文件绝对路径 + /// 内容 + /// 取消令牌 + public static async Task WriteAsync( string filePath, Stream content, CancellationToken cancellationToken = default ) { + if ( content == null ) + return; + await using ( content ) { + var bytes = await ToBytesAsync( content, cancellationToken ); + await WriteAsync( filePath, bytes, cancellationToken ); + } + } + + /// + /// 将字节流写入文件 + /// + /// 文件绝对路径 + /// 内容 + /// 取消令牌 + public static async Task WriteAsync( string filePath, byte[] content, CancellationToken cancellationToken = default ) { + if ( string.IsNullOrWhiteSpace( filePath ) ) + return; + if ( content == null ) + return; + CreateDirectory( filePath ); + await System.IO.File.WriteAllBytesAsync( filePath, content, cancellationToken ); + } + + #endregion + + #region Delete + + /// + /// 删除文件 + /// + /// 文件绝对路径集合 + public static void Delete( IEnumerable filePaths ) { + foreach ( var filePath in filePaths ) + Delete( filePath ); + } + + /// + /// 删除文件 + /// + /// 文件绝对路径 + public static void Delete( string filePath ) { + if ( string.IsNullOrWhiteSpace( filePath ) ) + return; + if ( System.IO.File.Exists( filePath ) ) + System.IO.File.Delete( filePath ); + } + + #endregion + + #region GetAllFiles + + /// + /// 获取全部文件,包括所有子目录 + /// + /// 目录路径 + /// 搜索模式 + public static List GetAllFiles( string path, string searchPattern ) { + return Directory.GetFiles( path, searchPattern, SearchOption.AllDirectories ) + .Select( filePath => new FileInfo( filePath ) ).ToList(); + } + + #endregion + + #region Copy + + /// + /// 复制文件 + /// + /// 源文件绝对路径 + /// 目标文件绝对路径 + /// 目标文件存在时是否覆盖,默认值: false + public static void Copy( string sourceFilePath, string destinationFilePath, bool overwrite = false ) { + if ( sourceFilePath.IsEmpty() || destinationFilePath.IsEmpty() ) + return; + if ( FileExists( sourceFilePath ) == false ) + return; + CreateDirectory( destinationFilePath ); + System.IO.File.Copy( sourceFilePath, destinationFilePath, overwrite ); + } + + #endregion + + #region Move + + /// + /// 移动文件 + /// + /// 源文件绝对路径 + /// 目标文件绝对路径 + /// 目标文件存在时是否覆盖,默认值: false + public static void Move( string sourceFilePath, string destinationFilePath, bool overwrite = false ) { + if ( sourceFilePath.IsEmpty() || destinationFilePath.IsEmpty() ) + return; + if ( FileExists( sourceFilePath ) == false ) + return; + CreateDirectory( destinationFilePath ); + System.IO.File.Move( sourceFilePath, destinationFilePath, overwrite ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/FileWatcher.cs b/src/Util.Core/Helpers/FileWatcher.cs new file mode 100644 index 000000000..7db14331f --- /dev/null +++ b/src/Util.Core/Helpers/FileWatcher.cs @@ -0,0 +1,280 @@ +namespace Util.Helpers; + +/// +/// 文件监视器 +/// +public class FileWatcher : IDisposable { + /// + /// 文件系统监视器 + /// + private readonly FileSystemWatcher _watcher; + /// + /// 防抖间隔 + /// + private int _debounceInterval; + /// + /// 文件监视事件过滤器 + /// + private readonly FileWatcherEventFilter _fileWatcherEventFilter; + + /// + /// 初始化文件监视器 + /// + public FileWatcher() { + _watcher = new FileSystemWatcher(); + _watcher.NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.LastWrite + | NotifyFilters.CreationTime | NotifyFilters.LastAccess + | NotifyFilters.Attributes | NotifyFilters.Size | NotifyFilters.Security; + _debounceInterval = 200; + _fileWatcherEventFilter = new FileWatcherEventFilter(); + } + + /// + /// 设置防抖间隔,默认值: 200 毫秒 + /// + /// 防抖间隔, 单位: 毫秒 + public FileWatcher Debounce( int interval ) { + _debounceInterval = interval; + return this; + } + + /// + /// 设置监听目录路径 + /// + /// 目录绝对路径 + /// 是否监听子目录,默认值: true + public FileWatcher Path( string path, bool includeSubdirectories = true ) { + _watcher.Path = path; + _watcher.IncludeSubdirectories = includeSubdirectories; + return this; + } + + /// + /// 设置监听通知过滤 + /// + /// 监听通知过滤 + public FileWatcher NotifyFilter( NotifyFilters notifyFilters ) { + _watcher.NotifyFilter = notifyFilters; + return this; + } + + /// + /// 设置过滤模式 + /// + /// 过滤模式,默认值: *.* ,范例: *.cshtml 可过滤cshtml文件 + public FileWatcher Filter( string filter ) { + _watcher.Filter = filter; + return this; + } + + /// + /// 处理文件创建监听事件 + /// + /// 文件创建监听事件处理器 + public FileWatcher OnCreated( Action action ) { + _watcher.Created += ( source, e ) => { + if ( _fileWatcherEventFilter.IsValid( e.FullPath, _debounceInterval ) ) + action( source, e ); + }; + return this; + } + + /// + /// 处理文件创建监听事件 + /// + /// 文件创建监听事件处理器 + public FileWatcher OnCreatedAsync( Func action ) { + _watcher.Created += async ( source, e ) => { + if ( _fileWatcherEventFilter.IsValid( e.FullPath, _debounceInterval ) ) + await action( source, e ); + }; + return this; + } + + /// + /// 处理文件变更监听事件 + /// + /// 文件变更监听事件处理器 + public FileWatcher OnChanged( Action action ) { + _watcher.Changed += ( source, e ) => { + if ( _fileWatcherEventFilter.IsValid( e.FullPath, _debounceInterval ) ) + action( source, e ); + }; + return this; + } + + /// + /// 处理文件变更监听事件 + /// + /// 文件变更监听事件处理器 + public FileWatcher OnChangedAsync( Func action ) { + _watcher.Changed += async ( source, e ) => { + if ( _fileWatcherEventFilter.IsValid( e.FullPath, _debounceInterval ) ) + await action( source, e ); + }; + return this; + } + + /// + /// 处理文件删除监听事件 + /// + /// 文件删除监听事件处理器 + public FileWatcher OnDeleted( Action action ) { + _watcher.Deleted += ( source, e ) => { + if ( _fileWatcherEventFilter.IsValid( e.FullPath, _debounceInterval ) ) + action( source, e ); + }; + return this; + } + + /// + /// 处理文件删除监听事件 + /// + /// 文件删除监听事件处理器 + public FileWatcher OnDeletedAsync( Func action ) { + _watcher.Deleted += async ( source, e ) => { + if ( _fileWatcherEventFilter.IsValid( e.FullPath, _debounceInterval ) ) + await action( source, e ); + }; + return this; + } + + /// + /// 处理文件重命名监听事件 + /// + /// 文件重命名监听事件处理器 + public FileWatcher OnRenamed( Action action ) { + _watcher.Renamed += ( source, e ) => { + if ( _fileWatcherEventFilter.IsValid( e.FullPath, _debounceInterval ) ) + action( source, e ); + }; + return this; + } + + /// + /// 处理文件重命名监听事件 + /// + /// 文件重命名监听事件处理器 + public FileWatcher OnRenamedAsync( Func action ) { + _watcher.Renamed += async ( source, e ) => { + if ( _fileWatcherEventFilter.IsValid( e.FullPath, _debounceInterval ) ) + await action( source, e ); + }; + return this; + } + + /// + /// 处理文件错误监听事件 + /// + /// 文件错误监听事件处理器 + public FileWatcher OnError( Action action ) { + _watcher.Error += ( source, e ) => { + action( source, e ); + }; + return this; + } + + /// + /// 处理文件错误监听事件 + /// + /// 文件错误监听事件处理器 + public FileWatcher OnErrorAsync( Func action ) { + _watcher.Error += async ( source, e ) => { + await action( source, e ); + }; + return this; + } + + /// + /// 处理文件监听器释放事件 + /// + /// 文件监听器释放事件处理器 + public FileWatcher OnDisposed( Action action ) { + _watcher.Disposed += ( source, e ) => { + action( source, e ); + }; + return this; + } + + /// + /// 处理文件监听器释放事件 + /// + /// 文件监听器释放事件处理器 + public FileWatcher OnDisposedAsync( Func action ) { + _watcher.Disposed += async ( source, e ) => { + await action( source, e ); + }; + return this; + } + + /// + /// 启动监听 + /// + public FileWatcher Start() { + _watcher.EnableRaisingEvents = true; + return this; + } + + /// + /// 停止监听 + /// + public FileWatcher Stop() { + _watcher.EnableRaisingEvents = false; + return this; + } + + /// + /// 释放 + /// + public void Dispose() { + _watcher?.Dispose(); + } +} + +/// +/// 文件监视事件过滤器 +/// +internal class FileWatcherEventFilter { + /// + /// 最后一个文件 + /// + private WatchFile _file; + + /// + /// 监视事件是否有效 + /// + /// 文件路径 + /// 防抖间隔 + internal bool IsValid( string path, int debounceInterval ) { + if ( _file != null && path == _file.Path && DateTime.Now - _file.Time < TimeSpan.FromMilliseconds( debounceInterval ) ) { + _file = new WatchFile( path ); + return false; + } + _file = new WatchFile( path ); + return true; + } +} + +/// +/// 监视文件 +/// +internal class WatchFile { + /// + /// 初始化监视文件 + /// + /// 文件路径 + public WatchFile( string path ) { + Path = path; + Time = DateTime.Now; + } + + /// + /// 文件路径 + /// + public string Path { get; } + + /// + /// 处理时间 + /// + public DateTime Time { get; } +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Ioc.cs b/src/Util.Core/Helpers/Ioc.cs new file mode 100644 index 000000000..e093eb109 --- /dev/null +++ b/src/Util.Core/Helpers/Ioc.cs @@ -0,0 +1,119 @@ +namespace Util.Helpers; + +/// +/// 容器操作 +/// +public static class Ioc { + /// + /// 容器 + /// + private static readonly Util.Dependency.Container _container = Util.Dependency.Container.Instance; + /// + /// 获取服务提供器操作 + /// + private static Func _getServiceProviderAction; + + /// + /// 服务范围工厂 + /// + public static IServiceScopeFactory ServiceScopeFactory { get; set; } + + /// + /// 创建新容器 + /// + public static Util.Dependency.Container CreateContainer() { + return new Util.Dependency.Container(); + } + + /// + /// 获取服务集合 + /// + public static IServiceCollection GetServices() { + return _container.GetServices(); + } + + /// + /// 设置获取服务提供器操作 + /// + /// 获取服务提供器操作 + public static void SetServiceProviderAction( Func action ) { + _getServiceProviderAction = action; + } + + /// + /// 获取 + /// + public static IServiceProvider GetServiceProvider() { + var provider = _getServiceProviderAction?.Invoke(); + if ( provider != null ) + return provider; + return _container.GetServiceProvider(); + } + + /// + /// 创建对象 + /// + /// 对象类型 + public static T Create() { + return Create( typeof( T ) ); + } + + /// + /// 创建对象 + /// + /// 返回对象类型 + /// 对象类型 + public static T Create( Type type ) { + var service = Create( type ); + if( service == null ) + return default; + return (T)service; + } + + /// + /// 创建对象 + /// + /// 对象类型 + public static object Create( Type type ) { + if( type == null ) + return null; + var provider = GetServiceProvider(); + return provider.GetService( type ); + } + + /// + /// 创建对象集合 + /// + /// 返回类型 + public static List CreateList() { + return CreateList( typeof( T ) ); + } + + /// + /// 创建对象集合 + /// + /// 返回类型 + /// 对象类型 + public static List CreateList( Type type ) { + Type serviceType = typeof( IEnumerable<> ).MakeGenericType( type ); + var result = Create( serviceType ); + if( result == null ) + return new List(); + return ( (IEnumerable)result ).ToList(); + } + + /// + /// 创建服务范围 + /// + public static IServiceScope CreateScope() { + var provider = GetServiceProvider(); + return provider.CreateScope(); + } + + /// + /// 清理 + /// + public static void Clear() { + _container.Clear(); + } +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Json.cs b/src/Util.Core/Helpers/Json.cs new file mode 100644 index 000000000..645764ab8 --- /dev/null +++ b/src/Util.Core/Helpers/Json.cs @@ -0,0 +1,280 @@ +using Util.SystemTextJson; + +namespace Util.Helpers; + +/// +/// Json操作 +/// +public static class Json { + /// + /// 将对象转换为Json字符串 + /// + /// 目标对象 + /// Json配置 + public static string ToJson( T value, JsonOptions options ) { + if ( options == null ) + return ToJson( value ); + var jsonSerializerOptions = ToJsonSerializerOptions( options ); + return ToJson( value, jsonSerializerOptions, options.RemoveQuotationMarks, options.ToSingleQuotes, options.IgnoreInterface ); + } + + /// + /// 转换序列化配置 + /// + private static JsonSerializerOptions ToJsonSerializerOptions( JsonOptions options ) { + var jsonSerializerOptions = new JsonSerializerOptions(); + if ( options.IgnoreEmptyString ) { + jsonSerializerOptions.TypeInfoResolver = new DefaultJsonTypeInfoResolver { + Modifiers = { IgnoreEmptyString } + }; + } + if ( options.IgnoreNullValues ) + jsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; + if ( options.IgnoreCase ) + jsonSerializerOptions.PropertyNameCaseInsensitive = true; + if ( options.ToCamelCase ) + jsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; + jsonSerializerOptions.Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ); + jsonSerializerOptions.Converters.Add( new DateTimeJsonConverter() ); + jsonSerializerOptions.Converters.Add( new NullableDateTimeJsonConverter() ); + jsonSerializerOptions.Converters.Add( new LongJsonConverter() ); + jsonSerializerOptions.Converters.Add( new NullableLongJsonConverter() ); + return jsonSerializerOptions; + } + + /// + /// 忽略空字符串 + /// + private static void IgnoreEmptyString( JsonTypeInfo jsonTypeInfo ) { + if ( jsonTypeInfo.Kind != JsonTypeInfoKind.Object ) + return; + foreach ( JsonPropertyInfo jsonPropertyInfo in jsonTypeInfo.Properties ) { + if ( jsonPropertyInfo.PropertyType == typeof( string ) ) { + jsonPropertyInfo.ShouldSerialize = static ( _, value ) => value.SafeString().IsEmpty() == false; + } + } + } + + /// + /// 将对象转换为Json字符串 + /// + /// 目标对象 + /// 序列化配置 + /// 是否移除双引号 + /// 是否将双引号转成单引号 + public static string ToJson( T value, JsonSerializerOptions options = null, bool removeQuotationMarks = false, bool toSingleQuotes = false ) { + return ToJson( value, options, removeQuotationMarks, toSingleQuotes, true ); + } + + /// + /// 将对象转换为Json字符串 + /// + private static string ToJson( T value, JsonSerializerOptions options, bool removeQuotationMarks, bool toSingleQuotes, bool ignoreInterface ) { + if ( value == null ) + return string.Empty; + options = GetToJsonOptions( options ); + var result = Serialize( value, options, ignoreInterface ); + if ( removeQuotationMarks ) + result = result.Replace( "\"", "" ); + if ( toSingleQuotes ) + result = result.Replace( "\"", "'" ); + return result; + } + + /// + /// 获取对象转换为Json字符串的序列化配置 + /// + private static JsonSerializerOptions GetToJsonOptions( JsonSerializerOptions options ) { + if ( options != null ) + return options; + return new JsonSerializerOptions { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ), + Converters = { + new DateTimeJsonConverter(), + new NullableDateTimeJsonConverter(), + new LongJsonConverter(), + new NullableLongJsonConverter() + } + }; + } + + /// + /// Json序列化 + /// + private static string Serialize( T value, JsonSerializerOptions options, bool ignoreInterface ) { + if ( ignoreInterface ) { + object instance = value; + if ( instance != null ) + return JsonSerializer.Serialize( instance, options ); + } + return JsonSerializer.Serialize( value, options ); + } + + /// + /// 将对象转换为Json字符串 + /// + /// 目标对象 + /// 序列化配置 + /// 取消令牌 + public static async Task ToJsonAsync( T value, JsonSerializerOptions options = null, CancellationToken cancellationToken = default ) { + if ( value == null ) + return string.Empty; + options = GetToJsonOptions( options ); + await using var stream = new MemoryStream(); + await JsonSerializer.SerializeAsync( stream, value, typeof( T ), options, cancellationToken ); + stream.Position = 0; + using var reader = new StreamReader( stream ); + return await reader.ReadToEndAsync( cancellationToken ); + } + + /// + /// 将Json字符串转换为对象 + /// + /// Json字符串 + /// 序列化配置 + public static T ToObject( string json, JsonOptions options ) { + if ( string.IsNullOrWhiteSpace( json ) ) + return default; + if ( options == null ) + return ToObject( json ); + return ToObject( json, ToJsonSerializerOptions( options ) ); + } + + /// + /// 将Json字符串转换为对象 + /// + /// Json字符串 + /// 序列化配置 + public static T ToObject( string json, JsonSerializerOptions options = null ) { + if ( string.IsNullOrWhiteSpace( json ) ) + return default; + options = GetToObjectOptions( options ); + return JsonSerializer.Deserialize( json, options ); + } + + /// + /// 将Json字符串转换为对象 + /// + /// Json字符串 + /// 返回类型 + /// 序列化配置 + public static object ToObject( string json, Type returnType, JsonSerializerOptions options = null ) { + if ( string.IsNullOrWhiteSpace( json ) ) + return default; + options = GetToObjectOptions( options ); + return JsonSerializer.Deserialize( json, returnType, options ); + } + + /// + /// 获取Json字符串转换为对象的序列化配置 + /// + private static JsonSerializerOptions GetToObjectOptions( JsonSerializerOptions options ) { + if ( options != null ) + return options; + return new JsonSerializerOptions { + PropertyNameCaseInsensitive = true, + NumberHandling = JsonNumberHandling.AllowReadingFromString, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ), + Converters = { + new DateTimeJsonConverter(), + new NullableDateTimeJsonConverter(), + new LongJsonConverter(), + new NullableLongJsonConverter() + } + }; + } + + /// + /// 将Json字节数组转换为对象 + /// + /// Json字节数组 + /// 序列化配置 + public static T ToObject( byte[] json, JsonSerializerOptions options = null ) { + if ( json == null ) + return default; + options = GetToObjectOptions( options ); + return JsonSerializer.Deserialize( json, options ); + } + + /// + /// 将Json字节流转换为对象 + /// + /// Json字节流 + /// 序列化配置 + public static T ToObject( Stream json, JsonSerializerOptions options = null ) { + if ( json == null ) + return default; + options = GetToObjectOptions( options ); + return JsonSerializer.Deserialize( json, options ); + } + + /// + /// 将Json字符串转换为对象 + /// + /// Json字符串 + /// 序列化配置 + /// Json字符编码,默认UTF8 + /// 取消令牌 + public static async Task ToObjectAsync( string json, JsonSerializerOptions options = null, Encoding encoding = null, CancellationToken cancellationToken = default ) { + if ( string.IsNullOrWhiteSpace( json ) ) + return default; + encoding ??= Encoding.UTF8; + byte[] bytes = encoding.GetBytes( json ); + await using var stream = new MemoryStream( bytes ); + return await ToObjectAsync( stream, options, cancellationToken ); + } + + /// + /// 将Json流转换为对象 + /// + /// Json流 + /// 序列化配置 + /// 取消令牌 + public static async Task ToObjectAsync( Stream json, JsonSerializerOptions options = null, CancellationToken cancellationToken = default ) { + if ( json == null ) + return default; + options = GetToObjectOptions( options ); + return await JsonSerializer.DeserializeAsync( json, options, cancellationToken ); + } + + /// + /// 将Json流转换为对象 + /// + /// Json流 + /// 序列化配置 + /// 取消令牌 + public static async Task ToObjectAsync( byte[] json, JsonSerializerOptions options = null, CancellationToken cancellationToken = default ) { + if ( json == null ) + return default; + await using var stream = new MemoryStream( json ); + return await ToObjectAsync( stream, options, cancellationToken ); + } + + /// + /// 将对象转换为字节数组 + /// + /// 目标对象 + /// Json配置 + public static byte[] ToBytes( T value, JsonSerializerOptions options = null ) { + options = GetToBytesOptions( options ); + return JsonSerializer.SerializeToUtf8Bytes( value, options ); + } + + /// + /// 获取转换为字节数组的序列化配置 + /// + private static JsonSerializerOptions GetToBytesOptions( JsonSerializerOptions options ) { + if ( options != null ) + return options; + return new JsonSerializerOptions { + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ), + Converters = { + new DateTimeJsonConverter(), + new NullableDateTimeJsonConverter(), + new LongJsonConverter(), + new NullableLongJsonConverter() + } + }; + } +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Lambda.cs b/src/Util.Core/Helpers/Lambda.cs new file mode 100644 index 000000000..e4ab3e61b --- /dev/null +++ b/src/Util.Core/Helpers/Lambda.cs @@ -0,0 +1,667 @@ +using Util.Data; + +namespace Util.Helpers; + +/// +/// Lambda表达式操作 +/// +public static class Lambda { + + #region GetType(获取类型) + + /// + /// 获取类型 + /// + /// 表达式,范例:t => t.Name + public static Type GetType( Expression expression ) { + var memberExpression = GetMemberExpression( expression ); + return memberExpression?.Type; + } + + #endregion + + #region GetMember(获取成员) + + /// + /// 获取成员 + /// + /// 表达式,范例:t => t.Name + public static MemberInfo GetMember( Expression expression ) { + var memberExpression = GetMemberExpression( expression ); + return memberExpression?.Member; + } + + /// + /// 获取成员表达式 + /// + /// 表达式 + /// 取表达式右侧,(l,r) => l.id == r.id,设置为true,返回r.id表达式 + public static MemberExpression GetMemberExpression( Expression expression, bool right = false ) { + if( expression == null ) + return null; + switch( expression.NodeType ) { + case ExpressionType.Lambda: + return GetMemberExpression( ( (LambdaExpression)expression ).Body, right ); + case ExpressionType.Convert: + case ExpressionType.Not: + return GetMemberExpression( ( (UnaryExpression)expression ).Operand, right ); + case ExpressionType.MemberAccess: + return (MemberExpression)expression; + case ExpressionType.Equal: + case ExpressionType.NotEqual: + case ExpressionType.GreaterThan: + case ExpressionType.LessThan: + case ExpressionType.GreaterThanOrEqual: + case ExpressionType.LessThanOrEqual: + return GetMemberExpression( right ? ( (BinaryExpression)expression ).Right : ( (BinaryExpression)expression ).Left, right ); + case ExpressionType.Call: + return GetMethodCallExpressionName( expression ); + } + return null; + } + + /// + /// 获取方法调用表达式的成员名称 + /// + private static MemberExpression GetMethodCallExpressionName( Expression expression ) { + var methodCallExpression = (MethodCallExpression)expression; + var left = (MemberExpression)methodCallExpression.Object; + if( Reflection.IsGenericCollection( left?.Type ) ) { + var argumentExpression = methodCallExpression.Arguments.FirstOrDefault(); + if( argumentExpression != null && argumentExpression.NodeType == ExpressionType.MemberAccess ) + return (MemberExpression)argumentExpression; + } + return left; + } + + #endregion + + #region GetName(获取成员名称) + + /// + /// 获取成员名称,范例:t => t.A.Name,返回 A.Name + /// + /// 表达式,范例:t => t.Name + public static string GetName( Expression expression ) { + var memberExpression = GetMemberExpression( expression ); + return GetMemberName( memberExpression ); + } + + /// + /// 获取成员名称 + /// + public static string GetMemberName( MemberExpression memberExpression ) { + if( memberExpression == null ) + return string.Empty; + string result = memberExpression.ToString(); + return result.Substring( result.IndexOf( ".", StringComparison.Ordinal ) + 1 ); + } + + #endregion + + #region GetNames(获取名称列表) + + /// + /// 获取名称列表,范例:t => new object[] { t.A.B, t.C },返回A.B,C + /// + /// 实体类型 + /// 属性集合表达式,范例:t => new object[]{t.A,t.B} + public static List GetNames( Expression> expression ) { + var result = new List(); + if( expression == null ) + return result; + if( !( expression.Body is NewArrayExpression arrayExpression ) ) + return result; + foreach( var each in arrayExpression.Expressions ) { + var name = GetName( each ); + if( string.IsNullOrWhiteSpace( name ) == false ) + result.Add( name ); + } + return result; + } + + #endregion + + #region GetLastName(获取最后一级成员名称) + + /// + /// 获取最后一级成员名称,范例:t => t.A.Name,返回 Name + /// + /// 表达式,范例:t => t.Name + /// 取表达式右侧,(l,r) => l.LId == r.RId,设置为true,返回RId + public static string GetLastName( Expression expression, bool right = false ) { + var memberExpression = GetMemberExpression( expression, right ); + if( memberExpression == null ) + return string.Empty; + if( IsValueExpression( memberExpression ) ) + return string.Empty; + string result = memberExpression.ToString(); + return result.Substring( result.LastIndexOf( ".", StringComparison.Ordinal ) + 1 ); + } + + /// + /// 是否值表达式 + /// + /// 表达式 + private static bool IsValueExpression( Expression expression ) { + if( expression == null ) + return false; + switch( expression.NodeType ) { + case ExpressionType.MemberAccess: + return IsValueExpression( ( (MemberExpression)expression ).Expression ); + case ExpressionType.Constant: + return true; + } + return false; + } + + #endregion + + #region GetLastNames(获取最后一级成员名称列表) + + /// + /// 获取最后一级成员名称列表,范例:t => new object[] { t.A.B, t.C },返回B,C + /// + /// 实体类型 + /// 属性集合表达式,范例:t => new object[]{t.A,t.B} + public static List GetLastNames( Expression> expression ) { + var result = new List(); + if( expression == null ) + return result; + if( !( expression.Body is NewArrayExpression arrayExpression ) ) + return result; + foreach( var each in arrayExpression.Expressions ) { + var name = GetLastName( each ); + if( string.IsNullOrWhiteSpace( name ) == false ) + result.Add( name ); + } + return result; + } + + #endregion + + #region GetValue(获取值) + + /// + /// 获取值,范例:t => t.Name == "A",返回 A + /// + /// 表达式,范例:t => t.Name == "A" + public static object GetValue( Expression expression ) { + if( expression == null ) + return null; + switch( expression.NodeType ) { + case ExpressionType.Lambda: + return GetValue( ( (LambdaExpression)expression ).Body ); + case ExpressionType.Convert: + return GetValue( ( (UnaryExpression)expression ).Operand ); + case ExpressionType.Equal: + case ExpressionType.NotEqual: + case ExpressionType.GreaterThan: + case ExpressionType.LessThan: + case ExpressionType.GreaterThanOrEqual: + case ExpressionType.LessThanOrEqual: + var hasParameter = HasParameter( ( (BinaryExpression)expression ).Left ); + if( hasParameter ) + return GetValue( ( (BinaryExpression)expression ).Right ); + return GetValue( ( (BinaryExpression)expression ).Left ); + case ExpressionType.Call: + return GetMethodCallExpressionValue( expression ); + case ExpressionType.MemberAccess: + return GetMemberValue( (MemberExpression)expression ); + case ExpressionType.Constant: + return GetConstantExpressionValue( expression ); + case ExpressionType.Not: + if( expression.Type == typeof( bool ) ) + return false; + return null; + } + return null; + } + + /// + /// 是否包含参数,用于检测是属性,而不是值 + /// + private static bool HasParameter( Expression expression ) { + if( expression == null ) + return false; + switch( expression.NodeType ) { + case ExpressionType.Convert: + return HasParameter( ( (UnaryExpression)expression ).Operand ); + case ExpressionType.MemberAccess: + return HasParameter( ( (MemberExpression)expression ).Expression ); + case ExpressionType.Parameter: + return true; + } + return false; + } + + /// + /// 获取方法调用表达式的值 + /// + private static object GetMethodCallExpressionValue( Expression expression ) { + var methodCallExpression = (MethodCallExpression)expression; + var value = GetValue( methodCallExpression.Arguments.FirstOrDefault() ); + if( value != null ) + return value; + if( methodCallExpression.Arguments.Count > 1 ) + return GetValue( methodCallExpression.Arguments[1] ); + if ( methodCallExpression.Object == null ) + return methodCallExpression.Type.InvokeMember( methodCallExpression.Method.Name, BindingFlags.InvokeMethod, null, null, null ); + return GetValue( methodCallExpression.Object ); + } + + /// + /// 获取属性表达式的值 + /// + private static object GetMemberValue( MemberExpression expression ) { + if( expression == null ) + return null; + var field = expression.Member as FieldInfo; + if( field != null ) { + var constValue = GetConstantExpressionValue( expression.Expression ); + return field.GetValue( constValue ); + } + var property = expression.Member as PropertyInfo; + if( property == null ) + return null; + if( expression.Expression == null ) + return property.GetValue( null ); + if ( expression.Expression is ConstantExpression ) { + var constValue = GetConstantExpressionValue( expression.Expression ); + return property.GetValue( constValue ); + } + var value = GetMemberValue( expression.Expression as MemberExpression ); + if( value == null ) { + if( property.PropertyType == typeof( bool ) ) + return true; + return null; + } + return property.GetValue( value ); + } + + /// + /// 获取常量表达式的值 + /// + private static object GetConstantExpressionValue( Expression expression ) { + var constantExpression = (ConstantExpression)expression; + return constantExpression.Value; + } + + #endregion + + #region GetOperator(获取查询操作符) + + /// + /// 获取查询操作符,范例:t => t.Name == "A",返回 Operator.Equal + /// + /// 表达式,范例:t => t.Name == "A" + public static Operator? GetOperator( Expression expression ) { + if( expression == null ) + return null; + switch( expression.NodeType ) { + case ExpressionType.Lambda: + return GetOperator( ( (LambdaExpression)expression ).Body ); + case ExpressionType.Convert: + return GetOperator( ( (UnaryExpression)expression ).Operand ); + case ExpressionType.Equal: + return Operator.Equal; + case ExpressionType.NotEqual: + return Operator.NotEqual; + case ExpressionType.GreaterThan: + return Operator.Greater; + case ExpressionType.LessThan: + return Operator.Less; + case ExpressionType.GreaterThanOrEqual: + return Operator.GreaterEqual; + case ExpressionType.LessThanOrEqual: + return Operator.LessEqual; + case ExpressionType.Call: + return GetMethodCallExpressionOperator( expression ); + } + return null; + } + + /// + /// 获取方法调用表达式的值 + /// + private static Operator? GetMethodCallExpressionOperator( Expression expression ) { + var methodCallExpression = (MethodCallExpression)expression; + switch( methodCallExpression?.Method?.Name?.ToLower() ) { + case "contains": + return Operator.Contains; + case "endswith": + return Operator.Ends; + case "startswith": + return Operator.Starts; + } + return null; + } + + #endregion + + #region GetParameter(获取参数) + + /// + /// 获取参数,范例:t.Name,返回 t + /// + /// 表达式,范例:t.Name + public static ParameterExpression GetParameter( Expression expression ) { + if( expression == null ) + return null; + switch( expression.NodeType ) { + case ExpressionType.Lambda: + return GetParameter( ( (LambdaExpression)expression ).Body ); + case ExpressionType.Convert: + return GetParameter( ( (UnaryExpression)expression ).Operand ); + case ExpressionType.Equal: + case ExpressionType.NotEqual: + case ExpressionType.GreaterThan: + case ExpressionType.LessThan: + case ExpressionType.GreaterThanOrEqual: + case ExpressionType.LessThanOrEqual: + return GetParameter( ( (BinaryExpression)expression ).Left ); + case ExpressionType.MemberAccess: + return GetParameter( ( (MemberExpression)expression ).Expression ); + case ExpressionType.Call: + return GetParameter( ( (MethodCallExpression)expression ).Object ); + case ExpressionType.Parameter: + return (ParameterExpression)expression; + } + return null; + } + + #endregion + + #region GetGroupPredicates(获取分组的谓词表达式) + + /// + /// 获取分组的谓词表达式,通过Or进行分组 + /// + /// 谓词表达式 + public static List> GetGroupPredicates( Expression expression ) { + var result = new List>(); + if( expression == null ) + return result; + AddPredicates( expression, result, CreateGroup( result ) ); + return result; + } + + /// + /// 创建分组 + /// + private static List CreateGroup( List> result ) { + var gourp = new List(); + result.Add( gourp ); + return gourp; + } + + /// + /// 添加通过Or分割的谓词表达式 + /// + private static void AddPredicates( Expression expression, List> result, List group ) { + switch( expression.NodeType ) { + case ExpressionType.Lambda: + AddPredicates( ( (LambdaExpression)expression ).Body, result, group ); + break; + case ExpressionType.OrElse: + AddPredicates( ( (BinaryExpression)expression ).Left, result, group ); + AddPredicates( ( (BinaryExpression)expression ).Right, result, CreateGroup( result ) ); + break; + case ExpressionType.AndAlso: + AddPredicates( ( (BinaryExpression)expression ).Left, result, group ); + AddPredicates( ( (BinaryExpression)expression ).Right, result, group ); + break; + default: + group.Add( expression ); + break; + } + } + + #endregion + + #region GetConditionCount(获取查询条件个数) + + /// + /// 获取查询条件个数 + /// + /// 谓词表达式,范例1:t => t.Name == "A" ,结果1。 + /// 范例2:t => t.Name == "A" && t.Age =1 ,结果2。 + public static int GetConditionCount( LambdaExpression expression ) { + if( expression == null ) + return 0; + var result = expression.ToString().Replace( "AndAlso", "|" ).Replace( "OrElse", "|" ); + return result.Split( '|' ).Count(); + } + + #endregion + + #region GetAttribute(获取特性) + + /// + /// 获取特性 + /// + /// 特性类型 + /// 属性表达式 + public static TAttribute GetAttribute( Expression expression ) where TAttribute : Attribute { + var memberInfo = GetMember( expression ); + return memberInfo.GetCustomAttribute(); + } + + /// + /// 获取特性 + /// + /// 实体类型 + /// 属性类型 + /// 特性类型 + /// 属性表达式 + public static TAttribute GetAttribute( Expression> propertyExpression ) where TAttribute : Attribute { + return GetAttribute( propertyExpression ); + } + + /// + /// 获取特性 + /// + /// 属性类型 + /// 特性类型 + /// 属性表达式 + public static TAttribute GetAttribute( Expression> propertyExpression ) where TAttribute : Attribute { + return GetAttribute( propertyExpression ); + } + + #endregion + + #region GetAttributes(获取特性列表) + + /// + /// 获取特性列表 + /// + /// 实体类型 + /// 属性类型 + /// 特性类型 + /// 属性表达式 + public static IEnumerable GetAttributes( Expression> propertyExpression ) where TAttribute : Attribute { + var memberInfo = GetMember( propertyExpression ); + return memberInfo.GetCustomAttributes(); + } + + #endregion + + #region Constant(获取常量) + + /// + /// 获取常量表达式 + /// + /// 值 + /// 表达式 + public static ConstantExpression Constant( object value, Expression expression = null ) { + var type = GetType( expression ); + if( type == null ) + return Expression.Constant( value ); + return Expression.Constant( value, type ); + } + + #endregion + + #region CreateParameter(创建参数表达式) + + /// + /// 创建参数表达式 + /// + /// 参数类型 + public static ParameterExpression CreateParameter() { + return Expression.Parameter( typeof( T ), "t" ); + } + + #endregion + + #region Equal(等于表达式) + + /// + /// 创建等于运算lambda表达式 + /// + /// 对象类型 + /// 属性名 + /// 值 + public static Expression> Equal( string propertyName, object value ) { + var parameter = CreateParameter(); + return parameter.Property( propertyName ).Equal( value ).ToPredicate( parameter ); + } + + #endregion + + #region NotEqual(不等于表达式) + + /// + /// 创建不等于运算lambda表达式 + /// + /// 对象类型 + /// 属性名 + /// 值 + public static Expression> NotEqual( string propertyName, object value ) { + var parameter = CreateParameter(); + return parameter.Property( propertyName ).NotEqual( value ).ToPredicate( parameter ); + } + + #endregion + + #region Greater(大于表达式) + + /// + /// 创建大于运算lambda表达式 + /// + /// 对象类型 + /// 属性名 + /// 值 + public static Expression> Greater( string propertyName, object value ) { + var parameter = CreateParameter(); + return parameter.Property( propertyName ).Greater( value ).ToPredicate( parameter ); + } + + #endregion + + #region GreaterEqual(大于等于表达式) + + /// + /// 创建大于等于运算lambda表达式 + /// + /// 对象类型 + /// 属性名 + /// 值 + public static Expression> GreaterEqual( string propertyName, object value ) { + var parameter = CreateParameter(); + return parameter.Property( propertyName ).GreaterEqual( value ).ToPredicate( parameter ); + } + + #endregion + + #region Less(小于表达式) + + /// + /// 创建小于运算lambda表达式 + /// + /// 对象类型 + /// 属性名 + /// 值 + public static Expression> Less( string propertyName, object value ) { + var parameter = CreateParameter(); + return parameter.Property( propertyName ).Less( value ).ToPredicate( parameter ); + } + + #endregion + + #region LessEqual(小于等于表达式) + + /// + /// 创建小于等于运算lambda表达式 + /// + /// 对象类型 + /// 属性名 + /// 值 + public static Expression> LessEqual( string propertyName, object value ) { + var parameter = CreateParameter(); + return parameter.Property( propertyName ).LessEqual( value ).ToPredicate( parameter ); + } + + #endregion + + #region Starts(调用StartsWith方法) + + /// + /// 调用StartsWith方法 + /// + /// 对象类型 + /// 属性名 + /// 值 + public static Expression> Starts( string propertyName, object value ) { + var parameter = CreateParameter(); + return parameter.Property( propertyName ).StartsWith( value ).ToPredicate( parameter ); + } + + #endregion + + #region Ends(调用EndsWith方法) + + /// + /// 调用EndsWith方法 + /// + /// 对象类型 + /// 属性名 + /// 值 + public static Expression> Ends( string propertyName, object value ) { + var parameter = CreateParameter(); + return parameter.Property( propertyName ).EndsWith( value ).ToPredicate( parameter ); + } + + #endregion + + #region Contains(调用Contains方法) + + /// + /// 调用Contains方法 + /// + /// 对象类型 + /// 属性名 + /// 值 + public static Expression> Contains( string propertyName, object value ) { + var parameter = CreateParameter(); + return parameter.Property( propertyName ).Contains( value ).ToPredicate( parameter ); + } + + #endregion + + #region ParsePredicate(解析为谓词表达式) + + /// + /// 解析为谓词表达式 + /// + /// 实体类型 + /// 属性名 + /// 值 + /// 运算符 + public static Expression> ParsePredicate( string propertyName, object value, Operator @operator ) { + var parameter = CreateParameter(); + return parameter.Property( propertyName ).Operation( @operator, value ).ToPredicate( parameter ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Random.cs b/src/Util.Core/Helpers/Random.cs new file mode 100644 index 000000000..0a96a0394 --- /dev/null +++ b/src/Util.Core/Helpers/Random.cs @@ -0,0 +1,83 @@ +namespace Util.Helpers; + +/// +/// 随机数操作 +/// +public class Random { + /// + /// 随机数 + /// + private readonly System.Random _random; + + /// + /// 初始化随机数 + /// + public Random() { + _random = new System.Random(); + } + + /// + /// 获取指定范围的随机整数 + /// + /// 最大值 + public int Next( int max ) { + return _random.Next( max ); + } + + /// + /// 获取指定范围的随机整数,该范围包括最小值,但不包括最大值 + /// + /// 最小值 + /// 最大值 + public int Next( int min, int max ) { + return _random.Next( min, max ); + } + + /// + /// 从集合中随机获取值 + /// + /// 集合 + public static T GetValue( IEnumerable array ) { + if ( array == null ) + return default; + var list = array.ToList(); + var index = System.Random.Shared.Next( 0, list.Count ); + return list[index]; + } + + /// + /// 从集合中随机获取值列表 + /// + /// 集合 + /// 获取数量 + public static List GetValues( IEnumerable array, int count ) { + var result = new List(); + if ( array == null ) + return result; + var list = array.ToList(); + while ( list.Count > 0 && result.Count < count ) { + var index = System.Random.Shared.Next( 0, list.Count ); + var item = list[index]; + result.Add( item ); + list.Remove( item ); + } + return result; + } + + /// + /// 对集合随机排序 + /// + /// 集合元素类型 + /// 集合 + public static List Sort( IEnumerable array ) { + if ( array == null ) + return null; + var list = array.ToList(); + for ( int i = 0; i < list.Count; i++ ) { + int index1 = System.Random.Shared.Next( 0, list.Count ); + int index2 = System.Random.Shared.Next( 0, list.Count ); + (list[index1], list[index2]) = (list[index2], list[index1]); + } + return list; + } +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Reflection.cs b/src/Util.Core/Helpers/Reflection.cs new file mode 100644 index 000000000..11e1a3512 --- /dev/null +++ b/src/Util.Core/Helpers/Reflection.cs @@ -0,0 +1,513 @@ +namespace Util.Helpers; + +/// +/// 反射操作 +/// +public static class Reflection { + + #region GetDescription(获取描述) + + /// + /// 获取类型描述,使用DescriptionAttribute设置描述 + /// + /// 类型 + public static string GetDescription() { + return GetDescription( Common.GetType() ); + } + + /// + /// 获取类型成员描述,使用DescriptionAttribute设置描述 + /// + /// 类型 + /// 成员名称 + public static string GetDescription( string memberName ) { + return GetDescription( Common.GetType(), memberName ); + } + + /// + /// 获取类型成员描述,使用DescriptionAttribute设置描述 + /// + /// 类型 + /// 成员名称 + public static string GetDescription( Type type, string memberName ) { + if ( type == null ) + return string.Empty; + if ( string.IsNullOrWhiteSpace( memberName ) ) + return string.Empty; + return GetDescription( type.GetTypeInfo().GetMember( memberName ).FirstOrDefault() ); + } + + /// + /// 获取类型成员描述,使用DescriptionAttribute设置描述 + /// + /// 成员 + public static string GetDescription( MemberInfo member ) { + if ( member == null ) + return string.Empty; + return member.GetCustomAttribute() is { } attribute ? attribute.Description : member.Name; + } + + #endregion + + #region GetDisplayName(获取显示名称) + + /// + /// 获取显示名称,使用DisplayNameAttribute设置显示名称 + /// + /// 类型 + public static string GetDisplayName() { + return GetDisplayName( Common.GetType() ); + } + + /// + /// 获取显示名称,使用DisplayAttribute或DisplayNameAttribute设置显示名称 + /// + public static string GetDisplayName( MemberInfo member ) { + if ( member == null ) + return string.Empty; + if ( member.GetCustomAttribute() is { } displayAttribute ) + return displayAttribute.Name; + if ( member.GetCustomAttribute() is { } displayNameAttribute ) + return displayNameAttribute.DisplayName; + return string.Empty; + } + + #endregion + + #region GetDisplayNameOrDescription(获取显示名称或描述) + + /// + /// 获取显示名称或描述,使用DisplayNameAttribute设置显示名称,使用DescriptionAttribute设置描述 + /// + /// 类型 + public static string GetDisplayNameOrDescription() { + return GetDisplayNameOrDescription( Common.GetType() ); + } + + /// + /// 获取属性显示名称或描述,使用DisplayAttribute或DisplayNameAttribute设置显示名称,使用DescriptionAttribute设置描述 + /// + public static string GetDisplayNameOrDescription( MemberInfo member ) { + var result = GetDisplayName( member ); + return string.IsNullOrWhiteSpace( result ) ? GetDescription( member ) : result; + } + + #endregion + + #region CreateInstance(动态创建实例) + + /// + /// 动态创建实例 + /// + /// 目标类型 + /// 类型 + /// 传递给构造函数的参数 + public static T CreateInstance( Type type, params object[] parameters ) { + return Convert.To( Activator.CreateInstance( type, parameters ) ); + } + + #endregion + + #region FindImplementTypes(查找实现类型列表) + + /// + /// 在指定的程序集中查找实现类型列表 + /// + /// 查找类型 + /// 待查找的程序集列表 + public static List FindImplementTypes( params Assembly[] assemblies ) { + return FindImplementTypes( typeof( TFind ), assemblies ); + } + + /// + /// 在指定的程序集中查找实现类型列表 + /// + /// 查找类型 + /// 待查找的程序集列表 + public static List FindImplementTypes( Type findType, params Assembly[] assemblies ) { + var result = new List(); + foreach ( var assembly in assemblies ) + result.AddRange( GetTypes( findType, assembly ) ); + return result.Distinct().ToList(); + } + + /// + /// 获取类型列表 + /// + private static List GetTypes( Type findType, Assembly assembly ) { + var result = new List(); + if ( assembly == null ) + return result; + Type[] types; + try { + types = assembly.GetTypes(); + } + catch ( ReflectionTypeLoadException ) { + return result; + } + foreach ( var type in types ) + AddType( result, findType, type ); + return result; + } + + /// + /// 添加类型 + /// + private static void AddType( List result, Type findType, Type type ) { + if ( type.IsInterface || type.IsAbstract ) + return; + if ( findType.IsAssignableFrom( type ) == false && MatchGeneric( findType, type ) == false ) + return; + result.Add( type ); + } + + /// + /// 泛型匹配 + /// + private static bool MatchGeneric( Type findType, Type type ) { + if ( findType.IsGenericTypeDefinition == false ) + return false; + var definition = findType.GetGenericTypeDefinition(); + foreach ( var implementedInterface in type.FindInterfaces( ( filter, criteria ) => true, null ) ) { + if ( implementedInterface.IsGenericType == false ) + continue; + return definition.IsAssignableFrom( implementedInterface.GetGenericTypeDefinition() ); + } + return false; + } + + #endregion + + #region GetDirectInterfaceTypes(获取直接接口类型列表) + + /// + /// 获取直接接口类型列表,排除基接口类型 + /// + /// 在该类型上查找接口 + /// 基接口类型列表,只返回继承了基接口的直接接口 + public static List GetDirectInterfaceTypes( params Type[] baseInterfaceTypes ) { + return GetDirectInterfaceTypes( typeof( T ), baseInterfaceTypes ); + } + + /// + /// 获取直接接口类型列表,排除基接口类型 + /// + /// 在该类型上查找接口 + /// 基接口类型列表,只返回继承了基接口的直接接口 + public static List GetDirectInterfaceTypes( Type type, params Type[] baseInterfaceTypes ) { + var interfaceTypes = type.GetInterfaces(); + var directInterfaceTypes = interfaceTypes.Except( interfaceTypes.SelectMany( t => t.GetInterfaces() ) ).ToList(); + if ( baseInterfaceTypes == null || baseInterfaceTypes.Length == 0 ) + return directInterfaceTypes; + return GetInterfaceTypes( directInterfaceTypes, baseInterfaceTypes ); + } + + /// + /// 获取接口类型 + /// + private static List GetInterfaceTypes( IEnumerable interfaceTypes, Type[] baseInterfaceTypes ) { + var result = new List(); + foreach ( var interfaceType in interfaceTypes ) { + if ( interfaceType.GetInterfaces().Any( baseInterfaceTypes.Contains ) == false ) + continue; + if ( interfaceType.IsGenericType && !interfaceType.IsGenericTypeDefinition && interfaceType.FullName == null ) { + result.Add( interfaceType.GetGenericTypeDefinition() ); + continue; + } + result.Add( interfaceType ); + } + return result; + } + + #endregion + + #region GetInterfaceTypes(获取接口类型列表) + + /// + /// 获取接口类型列表,排除基接口类型 + /// + /// 在该类型上查找接口 + /// 基接口类型列表 + public static List GetInterfaceTypes( params Type[] baseInterfaceTypes ) { + return GetInterfaceTypes( typeof( T ), baseInterfaceTypes ); + } + + /// + /// 获取接口类型列表,排除基接口类型 + /// + /// 在该类型上查找接口 + /// 基接口类型列表 + public static List GetInterfaceTypes( Type type, params Type[] baseInterfaceTypes ) { + var interfaceTypes = type.GetInterfaces(); + if ( baseInterfaceTypes == null || baseInterfaceTypes.Length == 0 ) + return interfaceTypes.ToList(); + return GetInterfaceTypes( interfaceTypes, baseInterfaceTypes ); + } + + #endregion + + #region IsCollection(是否集合) + + /// + /// 是否集合 + /// + /// 类型 + public static bool IsCollection( Type type ) { + if ( type.IsArray ) + return true; + return IsGenericCollection( type ); + } + + #endregion + + #region IsGenericCollection(是否泛型集合) + + /// + /// 是否泛型集合 + /// + /// 类型 + public static bool IsGenericCollection( Type type ) { + if ( !type.IsGenericType ) + return false; + var typeDefinition = type.GetGenericTypeDefinition(); + return typeDefinition == typeof( IEnumerable<> ) + || typeDefinition == typeof( IReadOnlyCollection<> ) + || typeDefinition == typeof( IReadOnlyList<> ) + || typeDefinition == typeof( ICollection<> ) + || typeDefinition == typeof( IList<> ) + || typeDefinition == typeof( List<> ); + } + + #endregion + + #region IsBool(是否布尔类型) + + /// + /// 是否布尔类型 + /// + /// 成员 + public static bool IsBool( MemberInfo member ) { + if ( member == null ) + return false; + switch ( member.MemberType ) { + case MemberTypes.TypeInfo: + return member.ToString() == "System.Boolean"; + case MemberTypes.Property: + return IsBool( (PropertyInfo)member ); + } + return false; + } + + /// + /// 是否布尔类型 + /// + private static bool IsBool( PropertyInfo property ) { + return property.PropertyType == typeof( bool ) || property.PropertyType == typeof( bool? ); + } + + #endregion + + #region IsEnum(是否枚举类型) + + /// + /// 是否枚举类型 + /// + /// 成员 + public static bool IsEnum( MemberInfo member ) { + if ( member == null ) + return false; + switch ( member.MemberType ) { + case MemberTypes.TypeInfo: + return ( (TypeInfo)member ).IsEnum; + case MemberTypes.Property: + return IsEnum( (PropertyInfo)member ); + } + return false; + } + + /// + /// 是否枚举类型 + /// + private static bool IsEnum( PropertyInfo property ) { + if ( property.PropertyType.GetTypeInfo().IsEnum ) + return true; + var value = Nullable.GetUnderlyingType( property.PropertyType ); + if ( value == null ) + return false; + return value.GetTypeInfo().IsEnum; + } + + #endregion + + #region IsDate(是否日期类型) + + /// + /// 是否日期类型 + /// + /// 成员 + public static bool IsDate( MemberInfo member ) { + if ( member == null ) + return false; + switch ( member.MemberType ) { + case MemberTypes.TypeInfo: + return member.ToString() == "System.DateTime"; + case MemberTypes.Property: + return IsDate( (PropertyInfo)member ); + } + return false; + } + + /// + /// 是否日期类型 + /// + private static bool IsDate( PropertyInfo property ) { + if ( property.PropertyType == typeof( DateTime ) ) + return true; + if ( property.PropertyType == typeof( DateTime? ) ) + return true; + return false; + } + + #endregion + + #region IsInt(是否整型) + + /// + /// 是否整型 + /// + /// 成员 + public static bool IsInt( MemberInfo member ) { + if ( member == null ) + return false; + switch ( member.MemberType ) { + case MemberTypes.TypeInfo: + return member.ToString() == "System.Int32" || member.ToString() == "System.Int16" || member.ToString() == "System.Int64"; + case MemberTypes.Property: + return IsInt( (PropertyInfo)member ); + } + return false; + } + + /// + /// 是否整型 + /// + private static bool IsInt( PropertyInfo property ) { + if ( property.PropertyType == typeof( int ) ) + return true; + if ( property.PropertyType == typeof( int? ) ) + return true; + if ( property.PropertyType == typeof( short ) ) + return true; + if ( property.PropertyType == typeof( short? ) ) + return true; + if ( property.PropertyType == typeof( long ) ) + return true; + if ( property.PropertyType == typeof( long? ) ) + return true; + return false; + } + + #endregion + + #region IsNumber(是否数值类型) + + /// + /// 是否浮点型 + /// + /// 成员 + public static bool IsNumber( MemberInfo member ) { + if ( member == null ) + return false; + if ( IsInt( member ) ) + return true; + switch ( member.MemberType ) { + case MemberTypes.TypeInfo: + return member.ToString() == "System.Double" || member.ToString() == "System.Decimal" || member.ToString() == "System.Single"; + case MemberTypes.Property: + return IsNumber( (PropertyInfo)member ); + } + return false; + } + + /// + /// 是否数值类型 + /// + private static bool IsNumber( PropertyInfo property ) { + if ( property.PropertyType == typeof( double ) ) + return true; + if ( property.PropertyType == typeof( double? ) ) + return true; + if ( property.PropertyType == typeof( decimal ) ) + return true; + if ( property.PropertyType == typeof( decimal? ) ) + return true; + if ( property.PropertyType == typeof( float ) ) + return true; + if ( property.PropertyType == typeof( float? ) ) + return true; + return false; + } + + #endregion + + #region GetElementType(获取元素类型) + + /// + /// 获取元素类型,如果是集合,返回集合的元素类型 + /// + /// 类型 + public static Type GetElementType( Type type ) { + if ( IsCollection( type ) == false ) + return type; + if ( type.IsArray ) + return type.GetElementType(); + var genericArgumentsTypes = type.GetTypeInfo().GetGenericArguments(); + if ( genericArgumentsTypes == null || genericArgumentsTypes.Length == 0 ) + throw new ArgumentException( nameof( genericArgumentsTypes ) ); + return genericArgumentsTypes[0]; + } + + #endregion + + #region GetTopBaseType(获取顶级基类) + + /// + /// 获取顶级基类 + /// + /// 类型 + public static Type GetTopBaseType() { + return GetTopBaseType( typeof( T ) ); + } + + /// + /// 获取顶级基类 + /// + /// 类型 + public static Type GetTopBaseType( Type type ) { + if ( type == null ) + return null; + if ( type.IsInterface ) + return type; + if ( type.BaseType == typeof( object ) ) + return type; + return GetTopBaseType( type.BaseType ); + } + + #endregion + + #region GetPropertyValue(获取属性值) + + /// + /// 获取属性值 + /// + /// 实例 + /// 属性名 + public static object GetPropertyValue( object instance, string propertyName ) { + if ( instance == null ) + return null; + var property = instance.GetType().GetProperty( propertyName ); + return property == null ? null : property.GetValue( instance ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Regex.cs b/src/Util.Core/Helpers/Regex.cs new file mode 100644 index 000000000..2770114b5 --- /dev/null +++ b/src/Util.Core/Helpers/Regex.cs @@ -0,0 +1,145 @@ +namespace Util.Helpers; + +/// +/// 正则操作 +/// +public static class Regex { + /// + /// 获取匹配值集合 + /// + /// 输入字符串 + /// 模式字符串 + /// 结果模式字符串数组,范例:new[]{"$1","$2"} + /// 选项 + public static Dictionary GetValues( string input, string pattern, string[] resultPatterns, RegexOptions options = RegexOptions.IgnoreCase ) { + var result = new Dictionary(); + if( string.IsNullOrWhiteSpace( input ) ) + return result; + var match = System.Text.RegularExpressions.Regex.Match( input, pattern, options ); + if( match.Success == false ) + return result; + AddResults( result, match, resultPatterns ); + return result; + } + + /// + /// 添加匹配结果 + /// + private static void AddResults( Dictionary result, Match match, string[] resultPatterns ) { + if( resultPatterns == null ) { + result.Add( string.Empty, match.Value ); + return; + } + foreach( var resultPattern in resultPatterns ) + result.Add( resultPattern, match.Result( resultPattern ) ); + } + + /// + /// 获取匹配值 + /// + /// 输入字符串 + /// 模式字符串 + /// 结果模式字符串,范例:"$1"用来获取第一个()内的值 + /// 选项 + public static string GetValue( string input, string pattern, string resultPattern = "", RegexOptions options = RegexOptions.IgnoreCase ) { + if( string.IsNullOrWhiteSpace( input ) ) + return string.Empty; + var match = System.Text.RegularExpressions.Regex.Match( input, pattern, options ); + if( match.Success == false ) + return string.Empty; + return string.IsNullOrWhiteSpace( resultPattern ) ? match.Value : match.Result( resultPattern ); + } + + /// + /// 分割成字符串数组 + /// + /// 输入字符串 + /// 模式字符串 + /// 选项 + public static string[] Split( string input, string pattern, RegexOptions options = RegexOptions.IgnoreCase ) { + if( string.IsNullOrWhiteSpace( input ) ) + return new string[]{}; + return System.Text.RegularExpressions.Regex.Split( input, pattern, options ); + } + + /// + /// 替换 + /// + /// 输入字符串 + /// 模式字符串 + /// 替换字符串 + /// 选项 + public static string Replace( string input, string pattern,string replacement, RegexOptions options = RegexOptions.IgnoreCase ) { + if( string.IsNullOrWhiteSpace( input ) ) + return string.Empty; + return System.Text.RegularExpressions.Regex.Replace( input, pattern, replacement, options ); + } + + /// + /// 验证输入与模式是否匹配 + /// + /// 输入字符串 + /// 模式字符串 + public static bool IsMatch( string input, string pattern ) { + return System.Text.RegularExpressions.Regex.IsMatch( input, pattern ); + } + + /// + /// 验证输入与模式是否匹配 + /// + /// 输入的字符串 + /// 模式字符串 + /// 选项 + public static bool IsMatch( string input, string pattern, RegexOptions options ) { + return System.Text.RegularExpressions.Regex.IsMatch( input, pattern, options ); + } + + /// + ///获取匹配结果 + /// + /// 输入字符串 + /// 模式字符串 + public static Match Match( string input, string pattern ) { + return System.Text.RegularExpressions.Regex.Match( input, pattern ); + } + + /// + ///获取匹配结果 + /// + /// 输入字符串 + /// 模式字符串 + /// 选项 + public static Match Match( string input, string pattern, RegexOptions options ) { + return System.Text.RegularExpressions.Regex.Match( input, pattern, options ); + } + + /// + ///获取匹配结果集合 + /// + /// 输入字符串 + /// 模式字符串 + public static MatchCollection Matches( string input, string pattern ) { + return System.Text.RegularExpressions.Regex.Matches( input, pattern ); + } + + /// + ///获取匹配结果集合 + /// + /// 输入字符串 + /// 模式字符串 + /// 选项 + public static MatchCollection Matches( string input, string pattern, RegexOptions options ) { + return System.Text.RegularExpressions.Regex.Matches( input, pattern, options ); + } + + /// + ///获取匹配结果集合 + /// + /// 输入字符串 + /// 模式字符串 + /// 选项 + /// 匹配超时间隔 + public static MatchCollection Matches( string input, string pattern, RegexOptions options, TimeSpan matchTimeout ) { + return System.Text.RegularExpressions.Regex.Matches( input, pattern, options, matchTimeout ); + } +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/String.cs b/src/Util.Core/Helpers/String.cs new file mode 100644 index 000000000..4d26bd6b6 --- /dev/null +++ b/src/Util.Core/Helpers/String.cs @@ -0,0 +1,335 @@ +namespace Util.Helpers; + +/// +/// 字符串操作 +/// +public static class String { + + #region Join(将集合连接为带分隔符的字符串) + + /// + /// 将集合连接为带分隔符的字符串 + /// + /// 集合元素类型 + /// 值 + /// 引号,默认不带引号,范例:单引号 "'" + /// 分隔符,默认使用逗号分隔 + public static string Join( IEnumerable values, string quotes = "", string separator = "," ) { + if ( values == null ) + return string.Empty; + var result = new StringBuilder(); + foreach ( var each in values ) + result.AppendFormat( "{0}{1}{0}{2}", quotes, each, separator ); + return result.ToString().RemoveEnd( separator ); + } + + #endregion + + #region FirstLowerCase(首字母小写) + + /// + /// 首字母小写 + /// + /// 值 + public static string FirstLowerCase( string value ) { + if ( string.IsNullOrWhiteSpace( value ) ) + return string.Empty; + var result = Rune.DecodeFromUtf16( value, out var rune, out var charsConsumed ); + if ( result != OperationStatus.Done || Rune.IsLower( rune ) ) + return value; + return Rune.ToLowerInvariant( rune ) + value[charsConsumed..]; + } + + #endregion + + #region FirstUpperCase(首字母大写) + + /// + /// 首字母大写 + /// + /// 值 + public static string FirstUpperCase( string value ) { + if ( string.IsNullOrWhiteSpace( value ) ) + return string.Empty; + var result = Rune.DecodeFromUtf16( value, out var rune, out var charsConsumed ); + if ( result != OperationStatus.Done || Rune.IsUpper( rune ) ) + return value; + return Rune.ToUpperInvariant( rune ) + value[charsConsumed..]; + } + + #endregion + + #region RemoveStart(移除起始字符串) + + /// + /// 移除起始字符串 + /// + /// 值 + /// 要移除的值 + /// 是否忽略大小写,默认值: true + public static string RemoveStart( string value, string start, bool ignoreCase = true ) { + if ( string.IsNullOrWhiteSpace( value ) ) + return string.Empty; + if ( string.IsNullOrEmpty( start ) ) + return value; + var options = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + if ( value.StartsWith( start, options ) == false ) + return value; + return value.Substring( start.Length, value.Length - start.Length ); + } + + /// + /// 移除起始字符串 + /// + /// 值 + /// 要移除的值 + public static StringBuilder RemoveStart( StringBuilder value, string start ) { + if ( value == null || value.Length == 0 ) + return null; + if ( string.IsNullOrEmpty( start ) ) + return value; + if ( start.Length > value.Length ) + return value; + var chars = start.ToCharArray(); + for ( int i = 0; i < chars.Length; i++ ) { + if ( value[i] != chars[i] ) + return value; + } + return value.Remove( 0, start.Length ); + } + + #endregion + + #region RemoveEnd(移除末尾字符串) + + /// + /// 移除末尾字符串 + /// + /// 值 + /// 要移除的值 + /// 是否忽略大小写,默认值: true + public static string RemoveEnd( string value, string end,bool ignoreCase = true ) { + if ( string.IsNullOrWhiteSpace( value ) ) + return string.Empty; + if ( string.IsNullOrEmpty( end ) ) + return value; + var options = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + if ( value.EndsWith( end, options ) == false ) + return value; + return value.Substring( 0, value.LastIndexOf( end, options ) ); + } + + /// + /// 移除末尾字符串 + /// + /// 值 + /// 要移除的值 + public static StringBuilder RemoveEnd( StringBuilder value, string end ) { + if ( value == null || value.Length == 0 ) + return null; + if ( string.IsNullOrEmpty( end ) ) + return value; + if ( end.Length > value.Length ) + return value; + var chars = end.ToCharArray(); + for ( int i = chars.Length - 1; i >= 0; i-- ) { + var j = value.Length - ( chars.Length - i ); + if ( value[j] != chars[i] ) + return value; + } + return value.Remove( value.Length - end.Length, end.Length ); + } + + #endregion + + #region PinYin(获取汉字的拼音简码) + + /// + /// 获取汉字的拼音简码,即首字母缩写,范例:中国,返回zg + /// + /// 汉字文本,范例: 中国 + public static string PinYin( string chineseText ) { + if ( chineseText.IsEmpty() ) + return string.Empty; + var result = new StringBuilder(); + foreach ( char text in chineseText ) + result.Append( ResolvePinYin( text ) ); + return result.ToString().ToLower(); + } + + /// + /// 解析单个汉字的拼音简码 + /// + private static string ResolvePinYin( char text ) { + byte[] charBytes = Encoding.UTF8.GetBytes( text.ToString() ); + if ( charBytes[0] <= 127 ) + return text.ToString(); + var unicode = (ushort)( charBytes[0] * 256 + charBytes[1] ); + string pinYin = ResolveByCode( unicode ); + if ( pinYin.IsEmpty() == false ) + return pinYin; + return ResolveByConst( text.ToString() ); + } + + /// + /// 使用字符编码方式获取拼音简码 + /// + private static string ResolveByCode( ushort unicode ) { + if ( unicode >= '\uB0A1' && unicode <= '\uB0C4' ) + return "A"; + if ( unicode >= '\uB0C5' && unicode <= '\uB2C0' && unicode != 45464 ) + return "B"; + if ( unicode >= '\uB2C1' && unicode <= '\uB4ED' ) + return "C"; + if ( unicode >= '\uB4EE' && unicode <= '\uB6E9' ) + return "D"; + if ( unicode >= '\uB6EA' && unicode <= '\uB7A1' ) + return "E"; + if ( unicode >= '\uB7A2' && unicode <= '\uB8C0' ) + return "F"; + if ( unicode >= '\uB8C1' && unicode <= '\uB9FD' ) + return "G"; + if ( unicode >= '\uB9FE' && unicode <= '\uBBF6' ) + return "H"; + if ( unicode >= '\uBBF7' && unicode <= '\uBFA5' ) + return "J"; + if ( unicode >= '\uBFA6' && unicode <= '\uC0AB' ) + return "K"; + if ( unicode >= '\uC0AC' && unicode <= '\uC2E7' ) + return "L"; + if ( unicode >= '\uC2E8' && unicode <= '\uC4C2' ) + return "M"; + if ( unicode >= '\uC4C3' && unicode <= '\uC5B5' ) + return "N"; + if ( unicode >= '\uC5B6' && unicode <= '\uC5BD' ) + return "O"; + if ( unicode >= '\uC5BE' && unicode <= '\uC6D9' ) + return "P"; + if ( unicode >= '\uC6DA' && unicode <= '\uC8BA' ) + return "Q"; + if ( unicode >= '\uC8BB' && unicode <= '\uC8F5' ) + return "R"; + if ( unicode >= '\uC8F6' && unicode <= '\uCBF9' ) + return "S"; + if ( unicode >= '\uCBFA' && unicode <= '\uCDD9' ) + return "T"; + if ( unicode >= '\uCDDA' && unicode <= '\uCEF3' ) + return "W"; + if ( unicode >= '\uCEF4' && unicode <= '\uD188' ) + return "X"; + if ( unicode >= '\uD1B9' && unicode <= '\uD4D0' ) + return "Y"; + if ( unicode >= '\uD4D1' && unicode <= '\uD7F9' ) + return "Z"; + return string.Empty; + } + + /// + /// 通过拼音简码常量获取 + /// + private static string ResolveByConst( string text ) { + int index = Const.ChinesePinYin.IndexOf( text, StringComparison.Ordinal ); + if ( index < 0 ) + return string.Empty; + return Const.ChinesePinYin.Substring( index + 1, 1 ); + } + + #endregion + + #region Extract(提取字符串中的变量值) + + /// + /// 提取字符串中的变量值 + /// + /// 原始值,范例: Hello,World + /// 字符串格式,范例: 原始值为Hello,World,格式为Hello,{value} ,则value变量的值为World + public static IDictionary Extract( string value, string format ) { + var result = new Dictionary(); + if ( value.IsEmpty() ) + return result; + if ( format.IsEmpty() ) + return result; + if ( format.Contains( "{", StringComparison.Ordinal ) == false ) + return result; + if ( format.Contains( "}", StringComparison.Ordinal ) == false ) + return result; + var formatItems = SplitFormat( format.SafeString() ); + return ExtractValue( value.SafeString(), formatItems ); + } + + /// + /// 拆分格式字符串 + /// + private static List SplitFormat( string format ) { + var result = new List(); + var item = new StringBuilder(); + for ( int i = 0; i < format.Length; i++ ) { + var temp = format[i]; + if ( temp == '{' ) { + item.RemoveEnd( "{" ); + if ( i == 0 ) { + result.Add( string.Empty ); + } + if ( item.Length > 0 ) { + result.Add( item.ToString() ); + item.Clear(); + } + item.Append( temp ); + continue; + } + if ( temp == '}' ) { + if( item.ToString().IsEmpty() ) + continue; + item.RemoveEnd( "}" ); + item.Append( temp ); + result.Add( item.ToString() ); + item.Clear(); + if ( i == format.Length - 1 ) + result.Add( string.Empty ); + continue; + } + item.Append( temp ); + if ( i == format.Length - 1 ) { + result.Add( item.ToString() ); + item.Clear(); + } + } + return result; + } + + /// + /// 提取字符串中变量值 + /// + private static IDictionary ExtractValue( string value, List formatItems ) { + var result = new Dictionary(); + var leftIndex = 0; + var length = 0; + for ( int i = 0; i < formatItems.Count; i++ ) { + var item = formatItems[i]; + if ( item == string.Empty ) + continue; + if ( item.StartsWith( "{", StringComparison.Ordinal ) == false ) { + leftIndex += item.Length; + continue; + } + if ( i + 1 < formatItems.Count ) { + var rightItem = formatItems[i + 1]; + if ( rightItem == string.Empty ) + length = value.Length - leftIndex; + else + length = value.IndexOf( rightItem, leftIndex + 1, StringComparison.OrdinalIgnoreCase ) - leftIndex; + } + var varName = item.Replace( "{", "" ).Replace( "}", "" ); + if ( length <= 0 ) { + result.Add( varName, string.Empty ); + continue; + } + var variableValue = value.Substring( leftIndex, length ); + result.Add( varName, variableValue ); + leftIndex += length; + } + return result; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Thread.cs b/src/Util.Core/Helpers/Thread.cs new file mode 100644 index 000000000..477c7da1f --- /dev/null +++ b/src/Util.Core/Helpers/Thread.cs @@ -0,0 +1,69 @@ +namespace Util.Helpers; + +/// +/// 线程操作 +/// +public static class Thread { + /// + /// 执行多个操作,等待所有操作完成 + /// + /// 操作集合 + public static void WaitAll( params Action[] actions ) { + if ( actions == null ) + return; + var tasks = new List(); + foreach ( var action in actions ) + tasks.Add( Task.Factory.StartNew( action, TaskCreationOptions.None ) ); + Task.WaitAll( tasks.ToArray() ); + } + + /// + /// 并发执行多个操作 + /// + /// 操作集合 + public static void ParallelInvoke( params Action[] actions ) { + Parallel.Invoke( actions ); + } + + /// + /// 循环并发执行操作 + /// + /// 操作 + /// 执行次数 + /// 并发执行配置 + public static void ParallelFor( Action action, int count = 1,ParallelOptions options = null ) { + if ( options == null ) { + Parallel.For( 0, count, i => action() ); + return; + } + Parallel.For( 0, count, options, i => action() ); + } + + /// + /// 循环并发执行操作 + /// + /// 操作 + /// 执行次数 + /// 并发执行配置 + public static async Task ParallelForAsync( Func action, int count = 1, ParallelOptions options = null ) { + if ( options == null ) { + await Parallel.ForEachAsync( Enumerable.Range( 1, count ), async ( i, token ) => await action() ); + return; + } + await Parallel.ForEachAsync( Enumerable.Range( 1,count ), options, async (i,token)=> await action() ); + } + + /// + /// 循环并发执行操作 + /// + /// 操作 + /// 执行次数 + /// 并发执行配置 + public static async Task ParallelForAsync( Func action, int count = 1, ParallelOptions options = null ) { + if ( options == null ) { + await Parallel.ForEachAsync( Enumerable.Range( 1, count ), async ( i, token ) => await action(i) ); + return; + } + await Parallel.ForEachAsync( Enumerable.Range( 1, count ), options, async ( i, token ) => await action(i) ); + } +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Time.cs b/src/Util.Core/Helpers/Time.cs new file mode 100644 index 000000000..ecf6dfcf6 --- /dev/null +++ b/src/Util.Core/Helpers/Time.cs @@ -0,0 +1,162 @@ +using Util.Dates; + +namespace Util.Helpers; + +/// +/// 时间操作 +/// +public static class Time { + /// + /// 日期 + /// + private static readonly AsyncLocal _dateTime = new(); + /// + /// 是否使用Utc日期 + /// + private static readonly AsyncLocal _isUseUtc = new(); + /// + /// 是否使用Utc日期 + /// + private static bool IsUseUtc => _isUseUtc.Value != null ? _isUseUtc.Value.SafeValue() : TimeOptions.IsUseUtc; + + /// + /// 设置时间 + /// + /// 时间 + public static void SetTime( DateTime? dateTime ) { + _dateTime.Value = dateTime; + } + + /// + /// 设置时间 + /// + /// 时间 + public static void SetTime( string dateTime ) { + SetTime( Convert.ToDateTimeOrNull( dateTime ) ); + } + + /// + /// 设置使用Utc日期 + /// + /// 是否使用Utc日期,默认值: true + public static void UseUtc( bool? isUseUtc = true ) { + _isUseUtc.Value = isUseUtc; + } + + /// + /// 重置时间和Utc标志 + /// + public static void Reset() { + _dateTime.Value = null; + _isUseUtc.Value = null; + } + + /// + /// 获取当前日期时间 + /// + public static DateTime Now { + get { + if ( _dateTime.Value != null ) + return _dateTime.Value.Value; + return IsUseUtc ? DateTime.UtcNow : DateTime.Now; + } + } + + + /// + /// 转换为标准化日期 + /// + /// 日期 + public static DateTime? Normalize( DateTime? date ) { + if( date == null ) + return null; + return Normalize( date.Value ); + } + + /// + /// 转换为标准化日期 + /// + /// 日期 + public static DateTime Normalize( DateTime date ) { + if ( IsUseUtc ) + return ToUniversalTime( date ); + return ToLocalTime( date ); + } + + /// + /// 转换为UTC日期 + /// + /// 日期 + public static DateTime ToUniversalTime( DateTime date ) { + if ( date == DateTime.MinValue ) + return DateTime.MinValue; + switch ( date.Kind ) { + case DateTimeKind.Local: + return date.ToUniversalTime(); + case DateTimeKind.Unspecified: + return DateTime.SpecifyKind( date, DateTimeKind.Local ).ToUniversalTime(); + default: + return date; + } + } + + /// + /// 转换为本地化日期 + /// + /// 日期 + public static DateTime ToLocalTime( DateTime date ) { + if ( date == DateTime.MinValue ) + return DateTime.MinValue; + switch ( date.Kind ) { + case DateTimeKind.Utc: + return date.ToLocalTime(); + case DateTimeKind.Unspecified: + return DateTime.SpecifyKind( date, DateTimeKind.Local ); + default: + return date; + } + } + + /// + /// Utc日期转换为本地化日期 + /// + /// 日期 + public static DateTime UtcToLocalTime( DateTime date ) { + if ( date == DateTime.MinValue ) + return DateTime.MinValue; + if( date.Kind == DateTimeKind.Utc ) + return date.ToLocalTime(); + if ( date.Kind == DateTimeKind.Local ) + return date; + if ( IsUseUtc ) + return DateTime.SpecifyKind( date, DateTimeKind.Utc ).ToLocalTime(); + return date; + } + + /// + /// 获取Unix时间戳 + /// + public static long GetUnixTimestamp() { + return GetUnixTimestamp( DateTime.Now ); + } + + /// + /// 获取Unix时间戳 + /// + /// 时间 + public static long GetUnixTimestamp( DateTime time ) { + var start = TimeZoneInfo.ConvertTime( new DateTime( 1970, 1, 1 ), TimeZoneInfo.Local ); + long ticks = ( time - start.Add( new TimeSpan( 8, 0, 0 ) ) ).Ticks; + return Util.Helpers.Convert.ToLong( ticks / TimeSpan.TicksPerSecond ); + } + + /// + /// 从Unix时间戳获取时间 + /// + /// Unix时间戳 + public static DateTime GetTimeFromUnixTimestamp( long timestamp ) { + var start = TimeZoneInfo.ConvertTime( new DateTime( 1970, 1, 1 ), TimeZoneInfo.Local ); + TimeSpan span = new TimeSpan( long.Parse( timestamp + "0000000" ) ); + return start.Add( span ).Add( new TimeSpan( 8, 0, 0 ) ); + } +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Url.cs b/src/Util.Core/Helpers/Url.cs new file mode 100644 index 000000000..ee714a4a3 --- /dev/null +++ b/src/Util.Core/Helpers/Url.cs @@ -0,0 +1,39 @@ +namespace Util.Helpers; + +/// +/// Url操作 +/// +public static class Url { + /// + /// 连接路径 + /// + /// 路径列表 + public static string JoinPath( params string[] paths ) { + if ( paths == null ) + return string.Empty; + paths = paths.Where( path => string.IsNullOrWhiteSpace( path ) == false ).Select( t => t.Replace( @"\", "/" ) ).ToArray(); + if ( paths.Length == 0 ) + return string.Empty; + var firstPath = paths.First(); + var lastPath = paths.Last(); + string schema = string.Empty; + if ( firstPath.StartsWith( "http:", StringComparison.OrdinalIgnoreCase ) ) + schema = "http://"; + if ( firstPath.StartsWith( "https:", StringComparison.OrdinalIgnoreCase ) ) + schema = "https://"; + paths = paths.Select( t => t.Trim( '/' ) ).ToArray(); + var result = Path.Combine( paths ).Replace( @"\", "/" ); + if ( paths.Any( path => path.StartsWith( "." ) ) ) { + result = Path.GetFullPath( Path.Combine( paths ) ); + result = result.RemoveStart( AppContext.BaseDirectory ).Replace( @"\", "/" ); + } + if ( firstPath.StartsWith( '/' ) ) + result = $"/{result}"; + if ( lastPath.EndsWith( '/' ) ) + result = $"{result}/"; + result = result.RemoveStart( "http:/" ).RemoveStart( "https:/" ); + if (schema.IsEmpty()) + return result; + return schema + result.RemoveStart( "/" ); + } +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Validation.cs b/src/Util.Core/Helpers/Validation.cs new file mode 100644 index 000000000..e3a8fcc14 --- /dev/null +++ b/src/Util.Core/Helpers/Validation.cs @@ -0,0 +1,17 @@ +namespace Util.Helpers; + +/// +/// 验证操作 +/// +public static class Validation { + /// + /// 是否数字 + /// + /// 输入值 + public static bool IsNumber( string input ) { + if( input.IsEmpty() ) + return false; + const string pattern = @"^(-?\d*)(\.\d+)?$"; + return Regex.IsMatch( input, pattern ); + } +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Xml.Builder.cs b/src/Util.Core/Helpers/Xml.Builder.cs new file mode 100644 index 000000000..69e7ccadf --- /dev/null +++ b/src/Util.Core/Helpers/Xml.Builder.cs @@ -0,0 +1,102 @@ +namespace Util.Helpers; + +/// +/// Xml操作 - 生成器 +/// +public partial class Xml { + /// + /// 初始化Xml操作 + /// + /// Xml字符串 + public Xml( string xml = null ) { + Document = new XmlDocument(); + Document.LoadXml( GetXml( xml ) ); + Root = Document.DocumentElement; + if( Root == null ) + throw new ArgumentException( nameof( xml ) ); + } + + /// + /// 获取Xml字符串 + /// + private string GetXml( string xml ) { + return string.IsNullOrWhiteSpace( xml ) ? "" : xml; + } + + /// + /// Xml文档 + /// + public XmlDocument Document { get; } + + /// + /// Xml根节点 + /// + public XmlElement Root { get; } + + /// + /// 添加节点 + /// + /// 节点名称 + /// 值 + /// 父节点 + public XmlNode AddNode( string name, object value = null, XmlNode parent = null ) { + var node = CreateNode( name, value, XmlNodeType.Element ); + GetParent( parent ).AppendChild( node ); + return node; + } + + /// + /// 创建节点 + /// + private XmlNode CreateNode( string name, object value, XmlNodeType type ) { + var node = Document.CreateNode( type, name, string.Empty ); + if( string.IsNullOrWhiteSpace( value.SafeString() ) == false ) + node.InnerText = value.SafeString(); + return node; + } + + /// + /// 获取父节点 + /// + private XmlNode GetParent( XmlNode parent ) { + if( parent == null ) + return Root; + return parent; + } + + /// + /// 添加CDATA节点 + /// + /// 值 + /// 父节点 + public XmlNode AddCDataNode( object value, XmlNode parent = null ) { + var node = CreateNode( CreateId(), value, XmlNodeType.CDATA ); + GetParent( parent ).AppendChild( node ); + return node; + } + + /// + /// 创建标识 + /// + private string CreateId() { + return System.Guid.NewGuid().ToString( "N" ); + } + + /// + /// 添加CDATA节点 + /// + /// 值 + /// 父节点名称 + public XmlNode AddCDataNode( object value, string parentName ) { + var parent = CreateNode( parentName, null, XmlNodeType.Element ); + Root.AppendChild( parent ); + return AddCDataNode( value, parent ); + } + + /// + /// 输出Xml + /// + public override string ToString() { + return Document.OuterXml; + } +} \ No newline at end of file diff --git a/src/Util.Core/Helpers/Xml.Tools.cs b/src/Util.Core/Helpers/Xml.Tools.cs new file mode 100644 index 000000000..7bc9e9247 --- /dev/null +++ b/src/Util.Core/Helpers/Xml.Tools.cs @@ -0,0 +1,61 @@ +namespace Util.Helpers; + +/// +/// Xml操作 - 工具 +/// +public partial class Xml { + /// + /// 将Xml字符串转换为XDocument + /// + /// Xml字符串 + public static XDocument ToDocument( string xml ) { + return XDocument.Parse( xml ); + } + + /// + /// 将Xml字符串转换为XElement列表 + /// + /// Xml字符串 + public static List ToElements( string xml ) { + var document = ToDocument( xml ); + if( document?.Root == null ) + return new List(); + return document.Root.Elements().ToList(); + } + + /// + /// 加载Xml文件到XDocument + /// + /// Xml文件绝对路径 + public static async Task LoadFileToDocumentAsync( string filePath ) { + return await LoadFileToDocumentAsync( filePath, Encoding.UTF8 ); + } + + /// + /// 加载Xml文件到XDocument + /// + /// Xml文件绝对路径 + /// 字符编码 + public static async Task LoadFileToDocumentAsync( string filePath,Encoding encoding ) { + var xml = await Util.Helpers.File.ReadToStringAsync( filePath, encoding ); + return ToDocument( xml ); + } + + /// + /// 加载Xml文件到XElement列表 + /// + /// Xml文件绝对路径 + public static async Task> LoadFileToElementsAsync( string filePath ) { + return await LoadFileToElementsAsync( filePath, Encoding.UTF8 ); + } + + /// + /// 加载Xml文件到XElement列表 + /// + /// Xml文件绝对路径 + /// 字符编码 + public static async Task> LoadFileToElementsAsync( string filePath, Encoding encoding ) { + var xml = await Util.Helpers.File.ReadToStringAsync( filePath, encoding ); + return ToElements( xml ); + } +} \ No newline at end of file diff --git a/src/Util.Core/IHostBuilderExtensions.cs b/src/Util.Core/IHostBuilderExtensions.cs new file mode 100644 index 000000000..b90bc2b6e --- /dev/null +++ b/src/Util.Core/IHostBuilderExtensions.cs @@ -0,0 +1,40 @@ +using Util.Configs; +using Util.Infrastructure; + +namespace Util; + +/// +/// 主机生成器服务扩展 +/// +public static class IHostBuilderExtensions { + /// + /// 启动Util服务 + /// + /// 主机生成器 + public static IHostBuilder AddUtil( this IHostBuilder hostBuilder ) { + hostBuilder.CheckNull( nameof( hostBuilder ) ); + var bootstrapper = new Bootstrapper( hostBuilder ); + bootstrapper.Start(); + return hostBuilder; + } + + /// + /// 转换为Util应用生成器 + /// + /// 主机生成器 + public static IAppBuilder AsBuild( this IHostBuilder hostBuilder ) { + hostBuilder.CheckNull( nameof( hostBuilder ) ); + return new AppBuilder( hostBuilder ); + } + + /// + /// 启动Util服务 + /// + /// 应用生成器 + public static IAppBuilder AddUtil( this IAppBuilder appBuilder ) { + appBuilder.CheckNull( nameof( appBuilder ) ); + var bootstrapper = new Bootstrapper( appBuilder.Host ); + bootstrapper.Start(); + return appBuilder; + } +} \ No newline at end of file diff --git a/src/Util.Core/Images/ImageType.cs b/src/Util.Core/Images/ImageType.cs new file mode 100644 index 000000000..9af75b864 --- /dev/null +++ b/src/Util.Core/Images/ImageType.cs @@ -0,0 +1,42 @@ +namespace Util.Images; + +/// +/// 图片类型 +/// +public enum ImageType { + /// + /// .jpg + /// + [Description( ".jpg" )] + Jpg, + /// + /// .jpeg + /// + [Description( ".jpeg" )] + Jpeg, + /// + /// .png + /// + [Description( ".png" )] + Png, + /// + /// .gif + /// + [Description( ".gif" )] + Gif, + /// + /// .svg + /// + [Description( ".svg" )] + Svg, + /// + /// .bmp + /// + [Description( ".bmp" )] + Bmp, + /// + /// .webp + /// + [Description( ".webp" )] + Webp +} \ No newline at end of file diff --git a/src/Util.Core/Infrastructure/Bootstrapper.cs b/src/Util.Core/Infrastructure/Bootstrapper.cs new file mode 100644 index 000000000..28cde8719 --- /dev/null +++ b/src/Util.Core/Infrastructure/Bootstrapper.cs @@ -0,0 +1,74 @@ +using Util.Helpers; +using Util.Reflections; + +namespace Util.Infrastructure; + +/// +/// 启动器 +/// +public class Bootstrapper { + /// + /// 主机生成器 + /// + private readonly IHostBuilder _hostBuilder; + /// + /// 程序集查找器 + /// + private readonly IAssemblyFinder _assemblyFinder; + /// + /// 类型查找器 + /// + private readonly ITypeFinder _typeFinder; + /// + /// 服务配置操作列表 + /// + private readonly List _serviceActions; + + /// + /// 初始化启动器 + /// + /// 主机生成器 + public Bootstrapper( IHostBuilder hostBuilder ) { + _hostBuilder = hostBuilder ?? throw new ArgumentNullException( nameof( hostBuilder ) ); + _assemblyFinder = new AppDomainAssemblyFinder { AssemblySkipPattern = BootstrapperConfig.AssemblySkipPattern }; + _typeFinder = new AppDomainTypeFinder( _assemblyFinder ); + _serviceActions = new List(); + } + + /// + /// 启动 + /// + public virtual void Start() { + ConfigureServices(); + ResolveServiceRegistrar(); + ExecuteServiceActions(); + } + + /// + /// 配置服务 + /// + protected virtual void ConfigureServices() { + _hostBuilder.ConfigureServices( ( context, services ) => { + Util.Helpers.Config.SetConfiguration( context.Configuration ); + services.TryAddSingleton( _assemblyFinder ); + services.TryAddSingleton( _typeFinder ); + } ); + } + + /// + /// 解析服务注册器 + /// + protected virtual void ResolveServiceRegistrar() { + var types = _typeFinder.Find(); + var instances = types.Select( type => Reflection.CreateInstance( type ) ).Where( t => t.Enabled ).OrderBy( t => t.OrderId ).ToList(); + var context = new ServiceContext( _hostBuilder, _assemblyFinder, _typeFinder ); + instances.ForEach( t => _serviceActions.Add( t.Register( context ) ) ); + } + + /// + /// 执行延迟服务注册操作 + /// + protected virtual void ExecuteServiceActions() { + _serviceActions.ForEach( action => action?.Invoke() ); + } +} \ No newline at end of file diff --git a/src/Util.Core/Infrastructure/BootstrapperConfig.cs b/src/Util.Core/Infrastructure/BootstrapperConfig.cs new file mode 100644 index 000000000..421aef351 --- /dev/null +++ b/src/Util.Core/Infrastructure/BootstrapperConfig.cs @@ -0,0 +1,11 @@ +namespace Util.Infrastructure; + +/// +/// 启动器配置 +/// +public class BootstrapperConfig { + /// + /// 启动时扫描程序集的过滤模式 + /// + public static string AssemblySkipPattern { get; set; } = "^System|^Mscorlib|^msvcr120|^Netstandard|^Microsoft|^Autofac|^AutoMapper|^EntityFramework|^Newtonsoft|^Castle|^NLog|^Pomelo|^AspectCore|^Xunit|^Nito|^Npgsql|^Exceptionless|^MySqlConnector|^Anonymously Hosted|^libuv|^api-ms|^clrcompression|^clretwrc|^clrjit|^coreclr|^dbgshim|^e_sqlite3|^hostfxr|^hostpolicy|^MessagePack|^mscordaccore|^mscordbi|^mscorrc|sni|sos|SOS.NETCore|^sos_amd64|^SQLitePCLRaw|^StackExchange|^Swashbuckle|WindowsBase|ucrtbase|^DotNetCore.CAP|^MongoDB|^Confluent.Kafka|^librdkafka|^EasyCaching|^RabbitMQ|^Consul|^Dapper|^EnyimMemcachedCore|^Pipelines|^DnsClient|^IdentityModel|^zlib|^NSwag|^Humanizer|^NJsonSchema|^Namotion|^ReSharper|^JetBrains|^NuGet|^ProxyGenerator|^testhost|^MediatR|^Polly|^AspNetCore|^Minio|^SixLabors|^Quartz|^Hangfire|^Handlebars|^Serilog|^WebApiClientCore|^BouncyCastle|^RSAExtensions|^MartinCostello|^Dapr.|^Bogus|^Azure.|^Grpc.|^HealthChecks|^Google|^CommunityToolkit|^Elasticsearch|^ICSharpCode|Enums.NET|^IdentityServer4|JWT|^MathNet|^MK.Hangfire|Mono.TextTemplating|Nest|^NPOI|^Oracle|Spire.Pdf|^FileSignatures"; +} \ No newline at end of file diff --git a/src/Util.Core/Infrastructure/DependencyServiceRegistrar.cs b/src/Util.Core/Infrastructure/DependencyServiceRegistrar.cs new file mode 100644 index 000000000..e6a07272b --- /dev/null +++ b/src/Util.Core/Infrastructure/DependencyServiceRegistrar.cs @@ -0,0 +1,109 @@ +using Util.Dependency; +using Util.Reflections; + +namespace Util.Infrastructure; + +/// +/// 依赖服务注册器 - 用于扫描注册ISingletonDependency,IScopeDependency,ITransientDependency +/// +public class DependencyServiceRegistrar : IServiceRegistrar { + /// + /// 获取服务名 + /// + public static string ServiceName => "Util.Infrastructure.DependencyServiceRegistrar"; + + /// + /// 排序号 + /// + public int OrderId => 100; + + /// + /// 是否启用 + /// + public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName ); + + /// + /// 注册服务 + /// + /// 服务上下文 + public Action Register( ServiceContext serviceContext ) { + return () => { + serviceContext.HostBuilder.ConfigureServices( ( context, services ) => { + RegisterDependency( services, serviceContext.TypeFinder, ServiceLifetime.Singleton ); + RegisterDependency( services, serviceContext.TypeFinder, ServiceLifetime.Scoped ); + RegisterDependency( services, serviceContext.TypeFinder, ServiceLifetime.Transient ); + } ); + }; + } + + /// + /// 注册依赖 + /// + private void RegisterDependency( IServiceCollection services, ITypeFinder finder, ServiceLifetime lifetime ) { + var types = GetTypes( finder ); + var result = FilterTypes( types ); + foreach ( var item in result ) + RegisterType( services, item.Item1, item.Item2, lifetime ); + } + + /// + /// 获取接口类型和实现类型列表 + /// + private List<(Type, Type)> GetTypes( ITypeFinder finder ) { + var result = new List<(Type, Type)>(); + var classTypes = finder.Find(); + foreach ( var classType in classTypes ) { + var interfaceTypes = Util.Helpers.Reflection.GetInterfaceTypes( classType, typeof( TDependencyInterface ) ); + interfaceTypes.ForEach( interfaceType => result.Add( (interfaceType, classType) ) ); + } + return result; + } + + /// + /// 过滤类型 + /// + private List<(Type, Type)> FilterTypes( List<(Type, Type)> types ) { + var result = new List<(Type, Type)>(); + foreach ( var group in types.GroupBy( t => t.Item1 ) ) { + if ( group.Count() == 1 ) { + result.Add( group.First() ); + continue; + } + result.Add( GetTypesByPriority( group ) ); + } + return result; + } + + /// + /// 获取优先级类型 + /// + private (Type, Type) GetTypesByPriority( IGrouping group ) { + int? currentPriority = null; + Type classType = null; + foreach ( var item in group ) { + var priority = GetPriority( item.Item2 ); + if ( currentPriority == null || priority > currentPriority ) { + currentPriority = priority; + classType = item.Item2; + } + } + return ( group.Key, classType ); + } + + /// + /// 获取优先级 + /// + private int GetPriority( Type type ) { + var attribute = type.GetCustomAttribute(); + if ( attribute == null ) + return 0; + return attribute.Priority; + } + + /// + /// 注册类型 + /// + private void RegisterType( IServiceCollection services, Type interfaceType, Type classType, ServiceLifetime lifetime ) { + services.TryAdd( new ServiceDescriptor( interfaceType, classType, lifetime ) ); + } +} \ No newline at end of file diff --git a/src/Util.Core/Infrastructure/IServiceRegistrar.cs b/src/Util.Core/Infrastructure/IServiceRegistrar.cs new file mode 100644 index 000000000..2e1a14e4d --- /dev/null +++ b/src/Util.Core/Infrastructure/IServiceRegistrar.cs @@ -0,0 +1,24 @@ +namespace Util.Infrastructure; + +/// +/// 服务注册器 +/// +public interface IServiceRegistrar { + /// + /// 排序号 + /// + int OrderId { get; } + + /// + /// 是否启用 + /// + bool Enabled { + get; + } + + /// + /// 注册服务,该操作在启动开始时执行,如果需要延迟执行某些操作,可在返回的Action中执行,它将在启动最后执行 + /// + /// 服务上下文 + Action Register( ServiceContext context ); +} \ No newline at end of file diff --git a/src/Util.Core/Infrastructure/ServiceContext.cs b/src/Util.Core/Infrastructure/ServiceContext.cs new file mode 100644 index 000000000..5a922b4aa --- /dev/null +++ b/src/Util.Core/Infrastructure/ServiceContext.cs @@ -0,0 +1,35 @@ +using Util.Reflections; + +namespace Util.Infrastructure; + +/// +/// 服务上下文 +/// +public class ServiceContext { + /// + /// 初始化服务上下文 + /// + /// 主机生成器 + /// 程序集查找器 + /// 类型查找器 + public ServiceContext( IHostBuilder hostBuilder, IAssemblyFinder assemblyFinder, ITypeFinder typeFinder ) { + HostBuilder = hostBuilder ?? throw new ArgumentNullException( nameof( hostBuilder ) ); + AssemblyFinder = assemblyFinder ?? throw new ArgumentNullException( nameof( assemblyFinder ) ); + TypeFinder = typeFinder ?? throw new ArgumentNullException( nameof( typeFinder ) ); + } + + /// + /// 主机生成器 + /// + public IHostBuilder HostBuilder { get; } + + /// + /// 程序集查找器 + /// + public IAssemblyFinder AssemblyFinder { get; } + + /// + /// 类型查找器 + /// + public ITypeFinder TypeFinder { get; } +} \ No newline at end of file diff --git a/src/Util.Core/Infrastructure/ServiceRegistrarConfig.cs b/src/Util.Core/Infrastructure/ServiceRegistrarConfig.cs new file mode 100644 index 000000000..e31e5033a --- /dev/null +++ b/src/Util.Core/Infrastructure/ServiceRegistrarConfig.cs @@ -0,0 +1,38 @@ +namespace Util.Infrastructure; + +/// +/// 服务注册器配置 +/// +public class ServiceRegistrarConfig { + /// + /// 服务注册器配置实例 + /// + public static readonly ServiceRegistrarConfig Instance = new (); + + /// + /// 禁用服务注册器 + /// + /// 服务注册器名称 + public static void Disable( string serviceName ) { + AppContext.SetSwitch( serviceName, false ); + } + + /// + /// 启用服务注册器 + /// + /// 服务注册器名称 + public static void Enable( string serviceName ) { + AppContext.SetSwitch( serviceName, true ); + } + + /// + /// 是否启用 + /// + /// 服务注册器名称 + public static bool IsEnabled( string serviceName ) { + var result = AppContext.TryGetSwitch( serviceName, out bool isEnabled ); + if ( result && isEnabled == false ) + return false; + return true; + } +} \ No newline at end of file diff --git a/src/Util.Core/Infrastructure/ServiceRegistrarConfigExtensions.cs b/src/Util.Core/Infrastructure/ServiceRegistrarConfigExtensions.cs new file mode 100644 index 000000000..68c33fb68 --- /dev/null +++ b/src/Util.Core/Infrastructure/ServiceRegistrarConfigExtensions.cs @@ -0,0 +1,24 @@ +namespace Util.Infrastructure; + +/// +/// 依赖服务注册器配置扩展 +/// +public static class ServiceRegistrarConfigExtensions { + /// + /// 启用依赖服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig EnableDependencyServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Enable( DependencyServiceRegistrar.ServiceName ); + return config; + } + + /// + ///禁用依赖服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig DisableDependencyServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Disable( DependencyServiceRegistrar.ServiceName ); + return config; + } +} \ No newline at end of file diff --git a/src/Util.Core/Item.cs b/src/Util.Core/Item.cs new file mode 100644 index 000000000..46986e8f7 --- /dev/null +++ b/src/Util.Core/Item.cs @@ -0,0 +1,76 @@ +namespace Util; + +/// +/// 列表项 +/// +public class Item : IComparable { + /// + /// 初始化 + /// + /// 文本 + /// 值 + /// 排序号 + /// 组 + /// 禁用 + /// 选中 + /// 图标 + public Item( string text, object value, int? sortId = null, string group = null, bool? disabled = null, bool? selected = null,string icon = null ) { + Text = text; + Value = value; + SortId = sortId; + Group = group; + Disabled = disabled; + Selected = selected; + Icon = icon; + } + + /// + /// 文本 + /// + [JsonPropertyName("text")] + public string Text { get; } + + /// + /// 值 + /// + [JsonPropertyName("value")] + public object Value { get; } + + /// + /// 排序号 + /// + [JsonPropertyName("sortId")] + public int? SortId { get; } + + /// + /// 组 + /// + [JsonPropertyName("group")] + public string Group { get; } + + /// + /// 禁用 + /// + [JsonPropertyName("disabled")] + public bool? Disabled { get; } + + /// + /// 选中 + /// + [JsonPropertyName( "selected" )] + public bool? Selected { get; } + + /// + /// 图标 + /// + [JsonPropertyName( "icon" )] + public string Icon { get; } + + /// + /// 比较 + /// + /// 其它列表项 + public int CompareTo( Item other ) { + return string.Compare( Text, other.Text, StringComparison.CurrentCulture ); + } +} \ No newline at end of file diff --git a/src/Util.Core/JsonOptions.cs b/src/Util.Core/JsonOptions.cs new file mode 100644 index 000000000..df495358f --- /dev/null +++ b/src/Util.Core/JsonOptions.cs @@ -0,0 +1,45 @@ +namespace Util; + +/// +/// Json配置 +/// +public class JsonOptions { + /// + /// 初始化Json配置 + /// + public JsonOptions() { + IgnoreNullValues = true; + IgnoreEmptyString = true; + IgnoreCase = true; + IgnoreInterface = true; + } + + /// + /// 是否移除双引号,默认值: false + /// + public bool RemoveQuotationMarks { get; set; } + /// + /// 是否将双引号转成单引号,默认值: false + /// + public bool ToSingleQuotes { get; set; } + /// + /// 是否忽略null值,默认值: true + /// + public bool IgnoreNullValues { get; set; } + /// + /// 是否忽略空字符串,默认值: true + /// + public bool IgnoreEmptyString { get; set; } + /// + /// 转换为Json时,属性名是否使用驼峰形式 + /// + public bool ToCamelCase { get; set; } + /// + /// 反序列化时是否忽略大小写,默认值: true + /// + public bool IgnoreCase { get; set; } + /// + /// 反序列化接口实例时是否忽略接口,按实现类进行序列化,默认值: true + /// + public bool IgnoreInterface { get; set; } +} \ No newline at end of file diff --git a/src/Util.Core/ObjectMapperExtensions.cs b/src/Util.Core/ObjectMapperExtensions.cs new file mode 100644 index 000000000..8b6dc3f22 --- /dev/null +++ b/src/Util.Core/ObjectMapperExtensions.cs @@ -0,0 +1,54 @@ +using Util.ObjectMapping; + +namespace Util; + +/// +/// 对象映射扩展 +/// +public static class ObjectMapperExtensions { + /// + /// 对象映射器 + /// + private static IObjectMapper _mapper; + + /// + /// 设置对象映射器 + /// + /// 对象映射器 + public static void SetMapper( IObjectMapper mapper ) { + _mapper = mapper ?? throw new ArgumentNullException( nameof( mapper ) ); + } + + /// + /// 将源对象映射到目标对象 + /// + /// 目标类型 + /// 源对象 + public static TDestination MapTo( this object source ) { + if ( _mapper == null ) + throw new ArgumentNullException( nameof(_mapper) ); + return _mapper.Map( source ); + } + + /// + /// 将源对象映射到目标对象 + /// + /// 源类型 + /// 目标类型 + /// 源对象 + /// 目标对象 + public static TDestination MapTo( this TSource source, TDestination destination ) { + if( _mapper == null ) + throw new ArgumentNullException( nameof( _mapper ) ); + return _mapper.Map( source, destination ); + } + + /// + /// 将源集合映射到目标集合 + /// + /// 目标元素类型,范例:Sample,不要加List + /// 源集合 + public static List MapToList( this System.Collections.IEnumerable source ) { + return MapTo>( source ); + } +} \ No newline at end of file diff --git a/src/Util.Core/ObjectMapping/IObjectMapper.cs b/src/Util.Core/ObjectMapping/IObjectMapper.cs new file mode 100644 index 000000000..40df0c493 --- /dev/null +++ b/src/Util.Core/ObjectMapping/IObjectMapper.cs @@ -0,0 +1,22 @@ +namespace Util.ObjectMapping; + +/// +/// 对象映射器 +/// +public interface IObjectMapper { + /// + /// 将源对象映射到目标对象 + /// + /// 源类型 + /// 目标类型 + /// 源对象 + TDestination Map( TSource source ); + /// + /// 将源对象映射到目标对象 + /// + /// 源类型 + /// 目标类型 + /// 源对象 + /// 目标对象 + TDestination Map( TSource source, TDestination destination ); +} \ No newline at end of file diff --git a/src/Util.Core/Properties/R.Designer.cs b/src/Util.Core/Properties/R.Designer.cs new file mode 100644 index 000000000..2effeff96 --- /dev/null +++ b/src/Util.Core/Properties/R.Designer.cs @@ -0,0 +1,288 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace Util.Properties { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class R { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal R() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Util.Properties.R", typeof(R).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 仅允许添加一个条件,条件:{0} 的本地化字符串。 + /// + public static string CanOnlyOneCondition { + get { + return ResourceManager.GetString("CanOnlyOneCondition", resourceCulture); + } + } + + /// + /// 查找类似 当前操作的数据已被其他人修改,请刷新后重试 的本地化字符串。 + /// + public static string ConcurrencyExceptionMessage { + get { + return ResourceManager.GetString("ConcurrencyExceptionMessage", resourceCulture); + } + } + + /// + /// 查找类似 天 的本地化字符串。 + /// + public static string Days { + get { + return ResourceManager.GetString("Days", resourceCulture); + } + } + + /// + /// 查找类似 删除成功 的本地化字符串。 + /// + public static string DeleteSuccess { + get { + return ResourceManager.GetString("DeleteSuccess", resourceCulture); + } + } + + /// + /// 查找类似 扩展属性 的本地化字符串。 + /// + public static string ExtraProperties { + get { + return ResourceManager.GetString("ExtraProperties", resourceCulture); + } + } + + /// + /// 查找类似 其他用户正在执行该操作,请稍后再试 的本地化字符串。 + /// + public static string GlobalDuplicateRequest { + get { + return ResourceManager.GetString("GlobalDuplicateRequest", resourceCulture); + } + } + + /// + /// 查找类似 小时 的本地化字符串。 + /// + public static string Hours { + get { + return ResourceManager.GetString("Hours", resourceCulture); + } + } + + /// + /// 查找类似 标识 的本地化字符串。 + /// + public static string Id { + get { + return ResourceManager.GetString("Id", resourceCulture); + } + } + + /// + /// 查找类似 标识不能为空 的本地化字符串。 + /// + public static string IdIsEmpty { + get { + return ResourceManager.GetString("IdIsEmpty", resourceCulture); + } + } + + /// + /// 查找类似 无效的身份证 的本地化字符串。 + /// + public static string InvalidIdCard { + get { + return ResourceManager.GetString("InvalidIdCard", resourceCulture); + } + } + + /// + /// 查找类似 是否删除 的本地化字符串。 + /// + public static string IsDeleted { + get { + return ResourceManager.GetString("IsDeleted", resourceCulture); + } + } + + /// + /// 查找类似 序号 的本地化字符串。 + /// + public static string LineNumber { + get { + return ResourceManager.GetString("LineNumber", resourceCulture); + } + } + + /// + /// 查找类似 毫秒 的本地化字符串。 + /// + public static string Milliseconds { + get { + return ResourceManager.GetString("Milliseconds", resourceCulture); + } + } + + /// + /// 查找类似 分 的本地化字符串。 + /// + public static string Minutes { + get { + return ResourceManager.GetString("Minutes", resourceCulture); + } + } + + /// + /// 查找类似 否 的本地化字符串。 + /// + public static string No { + get { + return ResourceManager.GetString("No", resourceCulture); + } + } + + /// + /// 查找类似 秒 的本地化字符串。 + /// + public static string Seconds { + get { + return ResourceManager.GetString("Seconds", resourceCulture); + } + } + + /// + /// 查找类似 操作成功 的本地化字符串。 + /// + public static string Success { + get { + return ResourceManager.GetString("Success", resourceCulture); + } + } + + /// + /// 查找类似 系统忙,请稍后再试 的本地化字符串。 + /// + public static string SystemError { + get { + return ResourceManager.GetString("SystemError", resourceCulture); + } + } + + /// + /// 查找类似 租户标识 的本地化字符串。 + /// + public static string TenantId { + get { + return ResourceManager.GetString("TenantId", resourceCulture); + } + } + + /// + /// 查找类似 类型 {0} 不是枚举 的本地化字符串。 + /// + public static string TypeNotEnum { + get { + return ResourceManager.GetString("TypeNotEnum", resourceCulture); + } + } + + /// + /// 查找类似 您没有该操作的权限 的本地化字符串。 + /// + public static string UnauthorizedMessage { + get { + return ResourceManager.GetString("UnauthorizedMessage", resourceCulture); + } + } + + /// + /// 查找类似 上传 的本地化字符串。 + /// + public static string Upload { + get { + return ResourceManager.GetString("Upload", resourceCulture); + } + } + + /// + /// 查找类似 请不要重复提交 的本地化字符串。 + /// + public static string UserDuplicateRequest { + get { + return ResourceManager.GetString("UserDuplicateRequest", resourceCulture); + } + } + + /// + /// 查找类似 版本号 的本地化字符串。 + /// + public static string Version { + get { + return ResourceManager.GetString("Version", resourceCulture); + } + } + + /// + /// 查找类似 是 的本地化字符串。 + /// + public static string Yes { + get { + return ResourceManager.GetString("Yes", resourceCulture); + } + } + } +} diff --git a/src/Util.Core/Properties/R.resx b/src/Util.Core/Properties/R.resx new file mode 100644 index 000000000..89c75681a --- /dev/null +++ b/src/Util.Core/Properties/R.resx @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 仅允许添加一个条件,条件:{0} + + + 当前操作的数据已被其他人修改,请刷新后重试 + + + + + + 删除成功 + + + 扩展属性 + + + 其他用户正在执行该操作,请稍后再试 + + + 小时 + + + 标识 + + + 标识不能为空 + + + 无效的身份证 + + + 是否删除 + + + 序号 + + + 毫秒 + + + + + + + + + + + + 操作成功 + + + 系统忙,请稍后再试 + 抛出系统异常时显示该消息 + + + 租户标识 + + + 类型 {0} 不是枚举 + + + 您没有该操作的权限 + + + 上传 + + + 请不要重复提交 + + + 版本号 + + + + + \ No newline at end of file diff --git a/src/Util.Core/ReflectionExtensions.cs b/src/Util.Core/ReflectionExtensions.cs new file mode 100644 index 000000000..39816fa3d --- /dev/null +++ b/src/Util.Core/ReflectionExtensions.cs @@ -0,0 +1,19 @@ +namespace Util; + +/// +/// 反射扩展 +/// +public static class ReflectionExtensions { + /// + /// 获取实例上的属性值 + /// + /// 成员信息 + /// 成员所在的类实例 + public static object GetPropertyValue( this MemberInfo member, object instance ) { + if( member == null ) + throw new ArgumentNullException( nameof( member ) ); + if( instance == null ) + throw new ArgumentNullException( nameof( instance ) ); + return instance.GetType().GetProperty( member.Name )?.GetValue( instance ); + } +} \ No newline at end of file diff --git a/src/Util.Core/Reflections/AppDomainAssemblyFinder.cs b/src/Util.Core/Reflections/AppDomainAssemblyFinder.cs new file mode 100644 index 000000000..a99b79368 --- /dev/null +++ b/src/Util.Core/Reflections/AppDomainAssemblyFinder.cs @@ -0,0 +1,84 @@ +namespace Util.Reflections; + +/// +/// 应用程序域程序集查找器 +/// +public class AppDomainAssemblyFinder : IAssemblyFinder { + /// + /// 程序集过滤模式 + /// + public string AssemblySkipPattern { get; set; } + /// + /// 程序集列表 + /// + private List _assemblies; + + /// + /// 获取程序集列表 + /// + public List Find() { + if ( _assemblies != null ) + return _assemblies; + _assemblies = new List(); + LoadAssemblies(); + foreach( var assembly in AppDomain.CurrentDomain.GetAssemblies() ) { + if( IsSkip( assembly ) ) + continue; + _assemblies.Add( assembly ); + } + return _assemblies; + } + + /// + /// 加载引用但尚未调用的程序集列表到当前应用程序域 + /// + protected virtual void LoadAssemblies() { + var currentDomainAssemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach( string file in GetLoadAssemblyFiles() ) + LoadAssembly( file, currentDomainAssemblies ); + } + + /// + /// 获取需要加载的程序集文件列表 + /// + protected virtual string[] GetLoadAssemblyFiles() { + return Directory.GetFiles( AppContext.BaseDirectory, "*.dll" ); + } + + /// + /// 加载程序集到当前应用程序域 + /// + protected void LoadAssembly( string file, Assembly[] currentDomainAssemblies ) { + try { + var assemblyName = AssemblyName.GetAssemblyName( file ); + if( IsSkip( assemblyName.Name ) ) + return; + if( currentDomainAssemblies.Any( t => t.FullName == assemblyName.FullName ) ) + return; + AppDomain.CurrentDomain.Load( assemblyName ); + } + catch( BadImageFormatException ) { + } + } + + /// + /// 是否过滤程序集 + /// + protected bool IsSkip( string assemblyName ) { + var applicationName = Assembly.GetEntryAssembly()?.GetName().Name; + if ( assemblyName.StartsWith( $"{applicationName}.Views" ) ) + return true; + if( assemblyName.StartsWith( $"{applicationName}.PrecompiledViews" ) ) + return true; + if ( string.IsNullOrWhiteSpace( AssemblySkipPattern ) ) + return false; + return Regex.IsMatch( assemblyName, AssemblySkipPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled ); + } + + /// + /// 是否过滤程序集 + /// + private bool IsSkip( Assembly assembly ) { + return IsSkip( assembly.FullName ); + } +} \ No newline at end of file diff --git a/src/Util.Core/Reflections/AppDomainTypeFinder.cs b/src/Util.Core/Reflections/AppDomainTypeFinder.cs new file mode 100644 index 000000000..f85c843b0 --- /dev/null +++ b/src/Util.Core/Reflections/AppDomainTypeFinder.cs @@ -0,0 +1,44 @@ +using Util.Helpers; + +namespace Util.Reflections; + +/// +/// 应用程序域类型查找器 +/// +public class AppDomainTypeFinder : ITypeFinder { + /// + /// 程序集查找器 + /// + private readonly IAssemblyFinder _assemblyFinder; + + /// + /// 初始化应用程序域类型查找器 + /// + /// 程序集查找器 + public AppDomainTypeFinder( IAssemblyFinder assemblyFinder ) { + _assemblyFinder = assemblyFinder ?? throw new ArgumentNullException( nameof( assemblyFinder ) ); + } + + /// + /// 查找类型列表 + /// + /// 查找类型 + public List Find() { + return Find( typeof( T ) ); + } + + /// + /// 获取程序集列表 + /// + public List GetAssemblies() { + return _assemblyFinder.Find(); + } + + /// + /// 查找类型列表 + /// + /// 查找类型 + public List Find( Type findType ) { + return Reflection.FindImplementTypes( findType, GetAssemblies()?.ToArray() ); + } +} \ No newline at end of file diff --git a/src/Util.Core/Reflections/IAssemblyFinder.cs b/src/Util.Core/Reflections/IAssemblyFinder.cs new file mode 100644 index 000000000..43a4e635e --- /dev/null +++ b/src/Util.Core/Reflections/IAssemblyFinder.cs @@ -0,0 +1,15 @@ +namespace Util.Reflections; + +/// +/// 程序集查找器 +/// +public interface IAssemblyFinder { + /// + /// 程序集过滤模式 + /// + public string AssemblySkipPattern { get; set; } + /// + /// 查找程序集列表 + /// + List Find(); +} \ No newline at end of file diff --git a/src/Util.Core/Reflections/ITypeFinder.cs b/src/Util.Core/Reflections/ITypeFinder.cs new file mode 100644 index 000000000..130006272 --- /dev/null +++ b/src/Util.Core/Reflections/ITypeFinder.cs @@ -0,0 +1,17 @@ +namespace Util.Reflections; + +/// +/// 类型查找器 +/// +public interface ITypeFinder { + /// + /// 查找类型列表 + /// + /// 查找类型 + List Find(); + /// + /// 查找类型列表 + /// + /// 查找类型 + List Find( Type findType ); +} \ No newline at end of file diff --git a/src/Util.Core/ServiceCollectionExtensions.cs b/src/Util.Core/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..8411e3b8c --- /dev/null +++ b/src/Util.Core/ServiceCollectionExtensions.cs @@ -0,0 +1,29 @@ +namespace Util; + +/// +/// 服务扩展 +/// +public static class ServiceCollectionExtensions { + /// + /// 获取配置实例 + /// + /// 服务集合 + public static IConfiguration GetConfiguration( this IServiceCollection services ) { + return services.GetSingletonInstance(); + } + + /// + /// 获取单例实例 + /// + /// 服务集合 + public static T GetSingletonInstance( this IServiceCollection services ) { + var descriptor = services.FirstOrDefault( t => t.ServiceType == typeof( T ) && t.Lifetime == ServiceLifetime.Singleton ); + if( descriptor == null ) + return default; + if( descriptor.ImplementationInstance != null ) + return (T)descriptor.ImplementationInstance; + if( descriptor.ImplementationFactory != null ) + return (T)descriptor.ImplementationFactory.Invoke( null ); + return default; + } +} \ No newline at end of file diff --git a/src/Util.Core/Sessions/ISession.cs b/src/Util.Core/Sessions/ISession.cs new file mode 100644 index 000000000..f4770acc8 --- /dev/null +++ b/src/Util.Core/Sessions/ISession.cs @@ -0,0 +1,25 @@ +using Util.Dependency; + +namespace Util.Sessions; + +/// +/// 用户会话 +/// +public interface ISession : ISingletonDependency { + /// + /// 服务提供器 + /// + IServiceProvider ServiceProvider { get; } + /// + /// 是否认证 + /// + bool IsAuthenticated { get; } + /// + /// 用户标识 + /// + string UserId { get; } + /// + /// 租户标识 + /// + string TenantId { get; } +} \ No newline at end of file diff --git a/src/Util.Core/Sessions/NullSession.cs b/src/Util.Core/Sessions/NullSession.cs new file mode 100644 index 000000000..e46ad3c12 --- /dev/null +++ b/src/Util.Core/Sessions/NullSession.cs @@ -0,0 +1,30 @@ +using Util.Dependency; + +namespace Util.Sessions; + +/// +/// 空用户会话 +/// +[Ioc(-9)] +public class NullSession : ISession { + /// + /// 用户会话空实例 + /// + public static readonly ISession Instance = new NullSession(); + + /// + /// 服务提供器 + /// + public IServiceProvider ServiceProvider => null; + + /// + /// 是否认证 + /// + public bool IsAuthenticated => false; + + /// + public string UserId => string.Empty; + + /// + public string TenantId => string.Empty; +} \ No newline at end of file diff --git a/src/Util.Core/StringExtensions.cs b/src/Util.Core/StringExtensions.cs new file mode 100644 index 000000000..1e5496215 --- /dev/null +++ b/src/Util.Core/StringExtensions.cs @@ -0,0 +1,72 @@ +using String = Util.Helpers.String; + +namespace Util; + +/// +/// 字符串扩展 +/// +public static class StringExtensions { + /// + /// 移除起始字符串 + /// + /// 值 + /// 要移除的值 + /// 是否忽略大小写,默认值: true + public static string RemoveStart( this string value, string start, bool ignoreCase = true ) { + return String.RemoveStart( value, start, ignoreCase ); + } + + /// + /// 移除末尾字符串 + /// + /// 值 + /// 要移除的值 + /// 是否忽略大小写,默认值: true + public static string RemoveEnd( this string value, string end, bool ignoreCase = true ) { + return String.RemoveEnd( value, end, ignoreCase ); + } + + /// + /// 移除起始字符串 + /// + /// 值 + /// 要移除的值 + public static StringBuilder RemoveStart( this StringBuilder value, string start ) { + return String.RemoveStart( value, start ); + } + + /// + /// 移除末尾字符串 + /// + /// 值 + /// 要移除的值 + public static StringBuilder RemoveEnd( this StringBuilder value, string end ) { + return String.RemoveEnd( value, end ); + } + + /// + /// 移除起始字符串 + /// + /// 字符串写入器 + /// 要移除的值 + public static StringWriter RemoveStart( this StringWriter writer, string start ) { + if ( writer == null ) + return null; + var builder = writer.GetStringBuilder(); + builder.RemoveStart( start ); + return writer; + } + + /// + /// 移除末尾字符串 + /// + /// 字符串写入器 + /// 要移除的值 + public static StringWriter RemoveEnd( this StringWriter writer, string end ) { + if ( writer == null ) + return null; + var builder = writer.GetStringBuilder(); + builder.RemoveEnd( end ); + return writer; + } +} \ No newline at end of file diff --git a/src/Util.Core/SystemTextJson/DateTimeJsonConverter.cs b/src/Util.Core/SystemTextJson/DateTimeJsonConverter.cs new file mode 100644 index 000000000..bd4c23e0d --- /dev/null +++ b/src/Util.Core/SystemTextJson/DateTimeJsonConverter.cs @@ -0,0 +1,46 @@ +using Util.Helpers; + +namespace Util.SystemTextJson; + +/// +/// 日期格式Json转换器 +/// +public class DateTimeJsonConverter : JsonConverter { + /// + /// 日期格式 + /// + private readonly string _format; + + /// + /// 初始化日期格式Json转换器 + /// + public DateTimeJsonConverter() : this( "yyyy-MM-dd HH:mm:ss" ) { + } + + /// + /// 初始化日期格式Json转换器 + /// + /// 日期格式,默认值: yyyy-MM-dd HH:mm:ss + public DateTimeJsonConverter( string format ) { + _format = format; + } + + /// + /// 读取数据 + /// + public override DateTime Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options ) { + if ( reader.TokenType == JsonTokenType.String ) + return Time.ToLocalTime( Util.Helpers.Convert.ToDateTime( reader.GetString() ) ); + if ( reader.TryGetDateTime( out var date ) ) + return Time.ToLocalTime( date ); + return DateTime.MinValue; + } + + /// + /// 写入数据 + /// + public override void Write( Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options ) { + var date = Time.ToLocalTime( value ).ToString( _format ); + writer.WriteStringValue( date ); + } +} \ No newline at end of file diff --git a/src/Util.Core/SystemTextJson/EnumJsonConverter.cs b/src/Util.Core/SystemTextJson/EnumJsonConverter.cs new file mode 100644 index 000000000..1ed80e975 --- /dev/null +++ b/src/Util.Core/SystemTextJson/EnumJsonConverter.cs @@ -0,0 +1,37 @@ +using System.Runtime.CompilerServices; + +namespace Util.SystemTextJson; + +/// +/// 枚举Json转换器 +/// +public class EnumJsonConverter : JsonConverter where T : struct, Enum { + /// + /// 读取数据 + /// + public override T Read( ref Utf8JsonReader reader, System.Type type, JsonSerializerOptions options ) { + var isSuccess = reader.TryGetSingle( out var floatValue ); + if ( isSuccess ) + return (T)System.Enum.Parse( type, floatValue.ToString( CultureInfo.InvariantCulture ), true ); + isSuccess = reader.TryGetInt32( out var intValue ); + if ( isSuccess ) + return (T)System.Enum.Parse( type, intValue.ToString( CultureInfo.InvariantCulture ), true ); + return default; + } + + + /// + /// 写入数据 + /// + public override void Write( Utf8JsonWriter writer, T value, JsonSerializerOptions options ) { + var code = Type.GetTypeCode( typeof(T) ); + switch ( code ) { + case TypeCode.Byte: + writer.WriteNumberValue( Unsafe.As( ref value ) ); + break; + case TypeCode.Int32: + writer.WriteNumberValue( Unsafe.As( ref value ) ); + break; + } + } +} \ No newline at end of file diff --git a/src/Util.Core/SystemTextJson/EnumJsonConverterFactory.cs b/src/Util.Core/SystemTextJson/EnumJsonConverterFactory.cs new file mode 100644 index 000000000..89ca8b8c6 --- /dev/null +++ b/src/Util.Core/SystemTextJson/EnumJsonConverterFactory.cs @@ -0,0 +1,30 @@ +namespace Util.SystemTextJson; + +/// +/// 枚举Json转换器工厂 +/// +public class EnumJsonConverterFactory : JsonConverterFactory { + /// + /// 是否允许转换为枚举 + /// + /// 转换类型 + public override bool CanConvert( Type type ) { + return type.IsEnum; + } + + /// + /// 创建枚举Json转换器 + /// + /// 转换类型 + /// Json序列化配置 + public override JsonConverter CreateConverter( Type type, JsonSerializerOptions options ) { + return (JsonConverter)Activator.CreateInstance( GetEnumConverterType( type ) ); + } + + /// + /// 获取枚举Json转换器类型 + /// + /// 枚举类型 + private static Type GetEnumConverterType( Type enumType ) => typeof( EnumJsonConverter<> ).MakeGenericType( enumType ); +} + diff --git a/src/Util.Core/SystemTextJson/LongJsonConverter.cs b/src/Util.Core/SystemTextJson/LongJsonConverter.cs new file mode 100644 index 000000000..1fa115206 --- /dev/null +++ b/src/Util.Core/SystemTextJson/LongJsonConverter.cs @@ -0,0 +1,22 @@ +namespace Util.SystemTextJson; + +/// +/// long格式Json转换器 +/// +public class LongJsonConverter : JsonConverter { + /// + /// 读取数据 + /// + public override long Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options ) { + if ( reader.TokenType == JsonTokenType.String ) + return Util.Helpers.Convert.ToLong( reader.GetString() ); + return reader.TryGetInt64( out var value ) ? value : 0; + } + + /// + /// 写入数据 + /// + public override void Write( Utf8JsonWriter writer, long value, JsonSerializerOptions options ) { + writer.WriteStringValue( value.SafeString() ); + } +} \ No newline at end of file diff --git a/src/Util.Core/SystemTextJson/NullableDateTimeJsonConverter.cs b/src/Util.Core/SystemTextJson/NullableDateTimeJsonConverter.cs new file mode 100644 index 000000000..0e87283f4 --- /dev/null +++ b/src/Util.Core/SystemTextJson/NullableDateTimeJsonConverter.cs @@ -0,0 +1,52 @@ +using Util.Helpers; + +namespace Util.SystemTextJson; + +/// +/// 可空日期格式Json转换器 +/// +public class NullableDateTimeJsonConverter : JsonConverter { + /// + /// 日期格式 + /// + private readonly string _format; + + /// + /// 初始化可空日期格式Json转换器 + /// + public NullableDateTimeJsonConverter() : this( "yyyy-MM-dd HH:mm:ss" ) { + } + + /// + /// 初始化可空日期格式Json转换器 + /// + /// 日期格式,默认值: yyyy-MM-dd HH:mm:ss + public NullableDateTimeJsonConverter( string format ) { + _format = format; + } + + /// + /// 读取数据 + /// + public override DateTime? Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options ) { + if ( reader.TokenType == JsonTokenType.String ) { + return Time.ToLocalTime( Util.Helpers.Convert.ToDateTime( reader.GetString() ) ); + } + if ( reader.TryGetDateTime( out var date ) ) { + return Time.ToLocalTime( date ); + } + return DateTime.MinValue; + } + + /// + /// 写入数据 + /// + public override void Write( Utf8JsonWriter writer, DateTime? value, JsonSerializerOptions options ) { + if ( value == null ) { + writer.WriteNullValue(); + return; + } + var date = Time.ToLocalTime( value.Value ).ToString( _format ); + writer.WriteStringValue( date ); + } +} \ No newline at end of file diff --git a/src/Util.Core/SystemTextJson/NullableLongJsonConverter.cs b/src/Util.Core/SystemTextJson/NullableLongJsonConverter.cs new file mode 100644 index 000000000..196bcdd9a --- /dev/null +++ b/src/Util.Core/SystemTextJson/NullableLongJsonConverter.cs @@ -0,0 +1,22 @@ +namespace Util.SystemTextJson; + +/// +/// 可空long格式Json转换器 +/// +public class NullableLongJsonConverter : JsonConverter { + /// + /// 读取数据 + /// + public override long? Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options ) { + if( reader.TokenType == JsonTokenType.String ) + return Util.Helpers.Convert.ToLongOrNull( reader.GetString() ); + return reader.TryGetInt64( out var value ) ? value : null; + } + + /// + /// 写入数据 + /// + public override void Write( Utf8JsonWriter writer, long? value, JsonSerializerOptions options ) { + writer.WriteStringValue( value.SafeString() ); + } +} \ No newline at end of file diff --git a/src/Util.Core/SystemTextJson/UtcDateTimeJsonConverter.cs b/src/Util.Core/SystemTextJson/UtcDateTimeJsonConverter.cs new file mode 100644 index 000000000..25b76daa5 --- /dev/null +++ b/src/Util.Core/SystemTextJson/UtcDateTimeJsonConverter.cs @@ -0,0 +1,48 @@ +using Util.Helpers; + +namespace Util.SystemTextJson; + +/// +/// Utc日期格式Json转换器 +/// +public class UtcDateTimeJsonConverter : JsonConverter { + /// + /// 日期格式 + /// + private readonly string _format; + + /// + /// 初始化Utc日期格式Json转换器 + /// + public UtcDateTimeJsonConverter() : this( "yyyy-MM-ddTHH:mm:sszzzz" ) { + } + + /// + /// 初始化Utc日期格式Json转换器 + /// + /// 日期格式,默认值: yyyy-MM-ddTHH:mm:sszzzz + public UtcDateTimeJsonConverter( string format ) { + _format = format; + } + + /// + /// 读取数据 + /// + public override DateTime Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options ) { + if ( reader.TokenType == JsonTokenType.String ) { + return Time.UtcToLocalTime( Util.Helpers.Convert.ToDateTime( reader.GetString() ) ); + } + if ( reader.TryGetDateTime( out var date ) ) { + return Time.UtcToLocalTime( date ); + } + return DateTime.MinValue; + } + + /// + /// 写入数据 + /// + public override void Write( Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options ) { + var date = Time.Normalize( value ).ToString( _format ); + writer.WriteStringValue( date ); + } +} \ No newline at end of file diff --git a/src/Util.Core/SystemTextJson/UtcNullableDateTimeJsonConverter.cs b/src/Util.Core/SystemTextJson/UtcNullableDateTimeJsonConverter.cs new file mode 100644 index 000000000..8d2f74540 --- /dev/null +++ b/src/Util.Core/SystemTextJson/UtcNullableDateTimeJsonConverter.cs @@ -0,0 +1,52 @@ +using Util.Helpers; + +namespace Util.SystemTextJson; + +/// +/// 可空日期格式Json转换器 +/// +public class UtcNullableDateTimeJsonConverter : JsonConverter { + /// + /// 日期格式 + /// + private readonly string _format; + + /// + /// 初始化可空日期格式Json转换器 + /// + public UtcNullableDateTimeJsonConverter() : this( "yyyy-MM-ddTHH:mm:sszzzz" ) { + } + + /// + /// 初始化可空日期格式Json转换器 + /// + /// 日期格式,默认值: yyyy-MM-ddTHH:mm:sszzzz + public UtcNullableDateTimeJsonConverter( string format ) { + _format = format; + } + + /// + /// 读取数据 + /// + public override DateTime? Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options ) { + if ( reader.TokenType == JsonTokenType.String ) { + return Time.UtcToLocalTime( Util.Helpers.Convert.ToDateTime( reader.GetString() ) ); + } + if ( reader.TryGetDateTime( out var date ) ) { + return Time.UtcToLocalTime( date ); + } + return DateTime.MinValue; + } + + /// + /// 写入数据 + /// + public override void Write( Utf8JsonWriter writer, DateTime? value, JsonSerializerOptions options ) { + if ( value == null ) { + writer.WriteNullValue(); + return; + } + var date = Time.Normalize( value.Value ).ToString( _format ); + writer.WriteStringValue( date ); + } +} \ No newline at end of file diff --git a/src/Util.Core/Ui/HtmlAttribute.cs b/src/Util.Core/Ui/HtmlAttribute.cs new file mode 100644 index 000000000..e43986c0b --- /dev/null +++ b/src/Util.Core/Ui/HtmlAttribute.cs @@ -0,0 +1,23 @@ +namespace Util.Ui; + +/// +/// 用于控制Razor Page生成Html的路径 +/// +public class HtmlAttribute : Attribute { + /// + /// 生成路径,相对根路径,范例:/ClientApp/src/app/app.component.html + /// + public string Path { get; set; } + /// + /// 不生成该页面的html + /// + public bool Ignore { get; set; } + + /// + /// 初始化 + /// + /// 生成路径,相对根路径,范例:/ClientApp/src/app/app.component.html + public HtmlAttribute( string path = null ) { + Path = path; + } +} \ No newline at end of file diff --git a/src/Util.Core/Ui/ModelAttribute.cs b/src/Util.Core/Ui/ModelAttribute.cs new file mode 100644 index 000000000..a00bb72c7 --- /dev/null +++ b/src/Util.Core/Ui/ModelAttribute.cs @@ -0,0 +1,20 @@ +namespace Util.Ui; + +/// +/// 模型绑定 +/// +[AttributeUsage( AttributeTargets.Class )] +public class ModelAttribute : Attribute { + /// + /// 初始化模型绑定 + /// + /// 模型 + public ModelAttribute( string model ) { + Model = model; + } + + /// + /// 模型 + /// + public string Model { get; set; } +} \ No newline at end of file diff --git a/src/Util.Core/Usings.cs b/src/Util.Core/Usings.cs new file mode 100644 index 000000000..d0e96760f --- /dev/null +++ b/src/Util.Core/Usings.cs @@ -0,0 +1,31 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using System.Diagnostics; +global using System.Text; +global using System.IO; +global using System.Reflection; +global using System.ComponentModel; +global using System.Globalization; +global using System.Text.Json; +global using System.Linq.Expressions; +global using System.Text.RegularExpressions; +global using System.ComponentModel.DataAnnotations; +global using System.Runtime.InteropServices; +global using System.Text.Encodings.Web; +global using System.Text.Json.Serialization; +global using System.Text.Unicode; +global using System.Buffers; +global using System.Diagnostics.CodeAnalysis; +global using System.Net.Http; +global using System.Xml; +global using System.Xml.Linq; +global using System.Text.Json.Serialization.Metadata; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Logging.Abstractions; +global using Microsoft.Extensions.DependencyInjection.Extensions; \ No newline at end of file diff --git a/src/Util.Core/ValidationExtensions.cs b/src/Util.Core/ValidationExtensions.cs new file mode 100644 index 000000000..c0081faad --- /dev/null +++ b/src/Util.Core/ValidationExtensions.cs @@ -0,0 +1,62 @@ +#nullable enable +namespace Util; + +/// +/// 验证扩展 +/// +public static class ValidationExtensions { + /// + /// 检测对象是否为null,为null则抛出异常 + /// + /// 对象 + /// 参数名 + public static void CheckNull( this object obj, string parameterName ) { + if( obj == null ) + throw new ArgumentNullException( parameterName ); + } + + /// + /// 是否为空 + /// + /// 值 + public static bool IsEmpty( [NotNullWhen( false )] this string? value ) { + return string.IsNullOrWhiteSpace( value ); + } + + /// + /// 是否为空 + /// + /// 值 + public static bool IsEmpty( this Guid value ) { + return value == Guid.Empty; + } + + /// + /// 是否为空 + /// + /// 值 + public static bool IsEmpty( [NotNullWhen( false )] this Guid? value ) { + if( value == null ) + return true; + return value == Guid.Empty; + } + + /// + /// 是否为空 + /// + /// 值 + public static bool IsEmpty( this IEnumerable? value ) { + if( value == null ) + return true; + return !value.Any(); + } + + /// + /// 是否默认值 + /// + /// 值类型 + /// 值 + public static bool IsDefault( this T value ) { + return EqualityComparer.Default.Equals( value, default ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Abstractions/06-Util.Data.Abstractions.csproj b/src/Util.Data.Abstractions/06-Util.Data.Abstractions.csproj new file mode 100644 index 000000000..5656afc5d --- /dev/null +++ b/src/Util.Data.Abstractions/06-Util.Data.Abstractions.csproj @@ -0,0 +1,34 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Data.Abstractions + Util.Data + Util.Data.Abstractions是Util应用框架数据访问基础契约库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Data.Abstractions.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Data.Abstractions.xml + + + + + True + False + + + + + + + + + + diff --git a/src/Util.Data.Abstractions/ConnectionStringCollection.cs b/src/Util.Data.Abstractions/ConnectionStringCollection.cs new file mode 100644 index 000000000..acaf30fce --- /dev/null +++ b/src/Util.Data.Abstractions/ConnectionStringCollection.cs @@ -0,0 +1,27 @@ +namespace Util.Data; + +/// +/// 连接字符串集合 +/// +public class ConnectionStringCollection : Dictionary { + /// + /// 默认连接字符串名称,值: Default + /// + public const string DefaultName = "Default"; + + /// + /// 默认连接字符串 + /// + public string Default { + get => this.GetValue( DefaultName ); + set => this[DefaultName] = value; + } + + /// + /// 获取连接字符串 + /// + /// 连接字符串名称 + public string GetConnectionString( string name ) { + return ContainsKey( name ) ? this.GetValue( name ) : Default; + } +} \ No newline at end of file diff --git a/src/Util.Data.Abstractions/ConnectionStringNameAttribute.cs b/src/Util.Data.Abstractions/ConnectionStringNameAttribute.cs new file mode 100644 index 000000000..89408942b --- /dev/null +++ b/src/Util.Data.Abstractions/ConnectionStringNameAttribute.cs @@ -0,0 +1,38 @@ +namespace Util.Data; + +/// +/// 连接字符串名称 +/// +public class ConnectionStringNameAttribute : Attribute { + /// + /// 连接字符串名称 + /// + public string Name { get; } + + /// + /// 初始化连接字符串名称 + /// + /// 连接字符串名称 + public ConnectionStringNameAttribute( string name ) { + Name = name; + } + + /// + /// 获取连接字符串名称,如果未设置名称则返回类型名 + /// + /// 类型 + public static string GetName() { + return GetName( typeof( T ) ); + } + + /// + /// 获取连接字符串名称,如果未设置名称则返回类型名 + /// + /// 类型 + public static string GetName( Type type ) { + if ( type == null ) + return null; + var attribute = type.GetCustomAttribute(); + return attribute == null || attribute.Name.IsEmpty() ? type.Name : attribute.Name; + } +} \ No newline at end of file diff --git a/src/Util.Data.Abstractions/Filters/IFilterOperation.cs b/src/Util.Data.Abstractions/Filters/IFilterOperation.cs new file mode 100644 index 000000000..fcf200c48 --- /dev/null +++ b/src/Util.Data.Abstractions/Filters/IFilterOperation.cs @@ -0,0 +1,7 @@ +namespace Util.Data.Filters; + +/// +/// 数据过滤操作 +/// +public interface IFilterOperation { +} \ No newline at end of file diff --git a/src/Util.Data.Abstractions/ICondition.cs b/src/Util.Data.Abstractions/ICondition.cs new file mode 100644 index 000000000..1c6c567ac --- /dev/null +++ b/src/Util.Data.Abstractions/ICondition.cs @@ -0,0 +1,12 @@ +namespace Util.Data; + +/// +/// 查询条件 +/// +/// 实体类型 +public interface ICondition { + /// + /// 获取查询条件 + /// + Expression> GetCondition(); +} \ No newline at end of file diff --git a/src/Util.Data.Abstractions/IUnitOfWork.cs b/src/Util.Data.Abstractions/IUnitOfWork.cs new file mode 100644 index 000000000..da2e0e12c --- /dev/null +++ b/src/Util.Data.Abstractions/IUnitOfWork.cs @@ -0,0 +1,14 @@ +using Util.Data.Filters; + +namespace Util.Data; + +/// +/// 工作单元 +/// +[Util.Aop.Ignore] +public interface IUnitOfWork : IDisposable, IFilterOperation { + /// + /// 提交,返回影响的行数 + /// + Task CommitAsync(); +} \ No newline at end of file diff --git a/src/Util.Data.Abstractions/IUnitOfWorkActionManager.cs b/src/Util.Data.Abstractions/IUnitOfWorkActionManager.cs new file mode 100644 index 000000000..e3883ea19 --- /dev/null +++ b/src/Util.Data.Abstractions/IUnitOfWorkActionManager.cs @@ -0,0 +1,18 @@ +using Util.Dependency; + +namespace Util.Data; + +/// +/// 工作单元操作管理器,用于将操作延迟到工作单元提交后执行 +/// +public interface IUnitOfWorkActionManager : IScopeDependency { + /// + /// 注册操作 + /// + /// 操作 + void Register( Func action ); + /// + /// 执行操作 + /// + Task ExecuteAsync(); +} \ No newline at end of file diff --git a/src/Util.Data.Abstractions/NullUnitOfWorkActionManager.cs b/src/Util.Data.Abstractions/NullUnitOfWorkActionManager.cs new file mode 100644 index 000000000..b92961317 --- /dev/null +++ b/src/Util.Data.Abstractions/NullUnitOfWorkActionManager.cs @@ -0,0 +1,26 @@ +namespace Util.Data; + +/// +/// 空工作单元操作管理器 +/// +public class NullUnitOfWorkActionManager : IUnitOfWorkActionManager { + /// + /// 初始化空工作单元操作管理器 + /// + public NullUnitOfWorkActionManager() { + } + + /// + /// 空工作单元操作管理器实例 + /// + public static readonly IUnitOfWorkActionManager Instance = new NullUnitOfWorkActionManager(); + + /// + public void Register( Func action ) { + } + + /// + public Task ExecuteAsync() { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/Util.Data.Abstractions/PageList.cs b/src/Util.Data.Abstractions/PageList.cs new file mode 100644 index 000000000..8a099f9a2 --- /dev/null +++ b/src/Util.Data.Abstractions/PageList.cs @@ -0,0 +1,149 @@ +using Util.Data.Queries; + +namespace Util.Data; + +/// +/// 分页集合 +/// +/// 元素类型 +public class PageList { + /// + /// 初始化分页集合 + /// + public PageList() : this( 0 ) { + } + + /// + /// 初始化分页集合 + /// + /// 内容 + public PageList( IEnumerable data = null ) + : this( 0, data ) { + } + + /// + /// 初始化分页集合 + /// + /// 总行数 + /// 内容 + public PageList( int totalCount, IEnumerable data = null ) + : this( 1, 20, totalCount, data ) { + } + + /// + /// 初始化分页集合 + /// + /// 页索引 + /// 每页显示行数 + /// 总行数 + /// 内容 + public PageList( int page, int pageSize, int totalCount, IEnumerable data = null ) + : this( page, pageSize, totalCount, "", data ) { + } + + /// + /// 初始化分页集合 + /// + /// 页索引 + /// 每页显示行数 + /// 总行数 + /// 排序条件 + /// 内容 + public PageList( int page, int pageSize, int totalCount, string order, IEnumerable data = null ) { + Data = data?.ToList() ?? new List(); + var pager = new Pager( page, pageSize, totalCount ); + Total = pager.Total; + PageCount = pager.GetPageCount(); + Page = pager.Page; + PageSize = pager.PageSize; + Order = order; + } + + /// + /// 初始化分页集合 + /// + /// 查询对象 + /// 内容 + public PageList( IPage pager, IEnumerable data = null ) + : this( pager.Page, pager.PageSize, pager.Total, pager.Order, data ) { + } + + /// + /// 页索引,即第几页,从1开始 + /// + public int Page { get; set; } + + /// + /// 每页显示行数 + /// + public int PageSize { get; set; } + + /// + /// 总行数 + /// + public int Total { get; set; } + + /// + /// 总页数 + /// + public int PageCount { get; set; } + + /// + /// 排序条件 + /// + public string Order { get; set; } + + /// + /// 内容 + /// + public List Data { get; init; } + + /// + /// 索引器 + /// + /// 索引 + public T this[int index] { + get => Data[index]; + set => Data[index] = value; + } + + /// + /// 添加元素 + /// + /// 元素 + public void Add( T item ) { + Data.Add( item ); + } + + /// + /// 添加元素集合 + /// + /// 元素集合 + public void AddRange( IEnumerable collection ) { + Data.AddRange( collection ); + } + + /// + /// 清空 + /// + public void Clear() { + Data.Clear(); + } + + /// + /// 转换分页集合 + /// + /// 目标元素类型 + /// 转换方法 + public PageList Convert( Func converter ) { + return Convert( Data.Select( converter ) ); + } + + /// + /// 转换分页集合 + /// + /// 内容 + public PageList Convert( IEnumerable data ) { + return new( Page, PageSize, Total, Order, data ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Abstractions/Queries/IPage.cs b/src/Util.Data.Abstractions/Queries/IPage.cs new file mode 100644 index 000000000..ad710d5e5 --- /dev/null +++ b/src/Util.Data.Abstractions/Queries/IPage.cs @@ -0,0 +1,39 @@ +namespace Util.Data.Queries; + +/// +/// 分页 +/// +public interface IPage { + /// + /// 页数,即第几页,从1开始 + /// + int Page { get; set; } + /// + /// 每页显示行数 + /// + int PageSize { get; set; } + /// + /// 总行数 + /// + int Total { get; set; } + /// + /// 获取总页数 + /// + int GetPageCount(); + /// + /// 获取跳过的行数 + /// + int GetSkipCount(); + /// + /// 排序条件 + /// + string Order { get; set; } + /// + /// 获取起始行数 + /// + int GetStartNumber(); + /// + /// 获取结束行数 + /// + int GetEndNumber(); +} \ No newline at end of file diff --git a/src/Util.Data.Abstractions/Queries/ITrack.cs b/src/Util.Data.Abstractions/Queries/ITrack.cs new file mode 100644 index 000000000..cf4363a57 --- /dev/null +++ b/src/Util.Data.Abstractions/Queries/ITrack.cs @@ -0,0 +1,11 @@ +namespace Util.Data.Queries; + +/// +/// 跟踪 +/// +public interface ITrack { + /// + /// 设置为不跟踪实体 + /// + void NoTracking(); +} \ No newline at end of file diff --git a/src/Util.Data.Abstractions/Queries/Pager.cs b/src/Util.Data.Abstractions/Queries/Pager.cs new file mode 100644 index 000000000..33822174e --- /dev/null +++ b/src/Util.Data.Abstractions/Queries/Pager.cs @@ -0,0 +1,94 @@ +namespace Util.Data.Queries; + +/// +/// 分页参数 +/// +public class Pager : IPage { + /// + /// 初始化分页参数 + /// + public Pager() + : this( 1 ) { + } + + /// + /// 初始化分页参数 + /// + /// 页索引 + /// 每页显示行数,默认20 + /// 排序条件 + public Pager( int page, int pageSize, string order ) + : this( page, pageSize, 0, order ) { + } + + /// + /// 初始化分页参数 + /// + /// 页索引 + /// 每页显示行数,默认20 + /// 总行数 + /// 排序条件 + public Pager( int page, int pageSize = 20, int total = 0, string order = "" ) { + Page = page; + PageSize = pageSize; + Total = total; + Order = order; + } + + private int _pageIndex; + /// + /// 页索引,即第几页,从1开始 + /// + public int Page { + get { + if( _pageIndex <= 0 ) + _pageIndex = 1; + return _pageIndex; + } + set => _pageIndex = value; + } + + /// + /// 每页显示行数 + /// + public int PageSize { get; set; } + + /// + /// 总行数 + /// + public int Total { get; set; } + + /// + /// 获取总页数 + /// + public int GetPageCount() { + if ( ( Total % PageSize ) == 0 ) + return Total / PageSize; + return ( Total / PageSize ) + 1; + } + + /// + /// 获取跳过的行数 + /// + public int GetSkipCount() { + return PageSize * ( Page - 1 ); + } + + /// + /// 排序条件 + /// + public string Order { get; set; } + + /// + /// 起始行数 + /// + public int GetStartNumber() { + return ( Page - 1 ) * PageSize + 1; + } + /// + /// 结束行数 + /// + public int GetEndNumber() { + return Page * PageSize; + } +} \ No newline at end of file diff --git a/src/Util.Data.Abstractions/Queries/QueryParameter.cs b/src/Util.Data.Abstractions/Queries/QueryParameter.cs new file mode 100644 index 000000000..582189dee --- /dev/null +++ b/src/Util.Data.Abstractions/Queries/QueryParameter.cs @@ -0,0 +1,15 @@ +using Util.Ui; + +namespace Util.Data.Queries; + +/// +/// 查询参数 +/// +[Model( "queryParam" )] +public class QueryParameter : Pager { + /// + /// 搜索关键字 + /// + [Display( Name = "util.keyword" )] + public string Keyword { get; set; } +} \ No newline at end of file diff --git a/src/Util.Data.Abstractions/StoreExtensions.cs b/src/Util.Data.Abstractions/StoreExtensions.cs new file mode 100644 index 000000000..30e135c4c --- /dev/null +++ b/src/Util.Data.Abstractions/StoreExtensions.cs @@ -0,0 +1,23 @@ +using Util.Data.Queries; +using Util.Data.Stores; +using Util.Domain; + +namespace Util.Data; + +/// +/// 存储器扩展 +/// +public static class StoreExtensions { + /// + /// 设置为不跟踪实体 + /// + /// 实体类型 + /// 实体标识类型 + /// 源 + public static IQueryStore NoTracking( this IQueryStore source ) where TEntity : class, IKey { + source.CheckNull( nameof( source ) ); + if( source is ITrack store ) + store.NoTracking(); + return source; + } +} \ No newline at end of file diff --git a/src/Util.Data.Abstractions/Stores/IQueryStore.cs b/src/Util.Data.Abstractions/Stores/IQueryStore.cs new file mode 100644 index 000000000..f06105f4a --- /dev/null +++ b/src/Util.Data.Abstractions/Stores/IQueryStore.cs @@ -0,0 +1,144 @@ +using Util.Data.Filters; +using Util.Dependency; +using Util.Domain; + +namespace Util.Data.Stores; + +/// +/// 查询存储器 +/// +/// 实体类型 +public interface IQueryStore : IQueryStore where TEntity : class, IKey { +} + +/// +/// 查询存储器 +/// +/// 实体类型 +/// 实体标识类型 +public interface IQueryStore : IFilterOperation,IScopeDependency,IDisposable where TEntity : class, IKey { + /// + /// 获取查询对象 + /// + IQueryable Find(); + /// + /// 查询 + /// + /// 查询条件 + IQueryable Find( ICondition condition ); + /// + /// 查询 + /// + /// 查询条件 + IQueryable Find( Expression> condition ); + /// + /// 通过标识查找实体 + /// + /// 标识 + TEntity FindById( object id ); + /// + /// 通过标识查找实体 + /// + /// 标识 + /// 取消令牌 + Task FindByIdAsync( object id, CancellationToken cancellationToken = default ); + /// + /// 通过标识列表查找实体列表 + /// + /// 标识列表 + List FindByIds( params TKey[] ids ); + /// + /// 通过标识列表查找实体列表 + /// + /// 标识列表 + List FindByIds( IEnumerable ids ); + /// + /// 通过标识列表查找实体列表 + /// + /// 逗号分隔的标识列表,范例:"1,2" + List FindByIds( string ids ); + /// + /// 通过标识列表查找实体列表 + /// + /// 标识列表 + Task> FindByIdsAsync( params TKey[] ids ); + /// + /// 通过标识列表查找实体列表 + /// + /// 标识列表 + /// 取消令牌 + Task> FindByIdsAsync( IEnumerable ids, CancellationToken cancellationToken = default ); + /// + /// 通过标识列表查找实体列表 + /// + /// 逗号分隔的标识列表,范例:"1,2" + /// 取消令牌 + Task> FindByIdsAsync( string ids, CancellationToken cancellationToken = default ); + /// + /// 查找单个实体 + /// + /// 查询条件 + TEntity Single( Expression> condition ); + /// + /// 查找单个实体 + /// + /// 查询条件 + /// 访问IQueryable的回调函数,用于执行Include等操作 + TEntity Single( Expression> condition, Func, IQueryable> action ); + /// + /// 查找单个实体 + /// + /// 查询条件 + /// 取消令牌 + Task SingleAsync( Expression> condition, CancellationToken cancellationToken = default ); + /// + /// 查找单个实体 + /// + /// 查询条件 + /// 访问IQueryable的回调函数,用于执行Include等操作 + /// 取消令牌 + Task SingleAsync( Expression> condition, Func, IQueryable> action, CancellationToken cancellationToken = default ); + /// + /// 查找实体列表 + /// + /// 查询条件 + List FindAll( Expression> condition = null ); + /// + /// 查找实体列表 + /// + /// 查询条件 + /// 取消令牌 + Task> FindAllAsync( Expression> condition = null, CancellationToken cancellationToken = default ); + /// + /// 判断是否存在 + /// + /// 标识列表 + bool Exists( params TKey[] ids ); + /// + /// 判断是否存在 + /// + /// 条件 + bool Exists( Expression> condition ); + /// + /// 判断是否存在 + /// + /// 标识列表 + Task ExistsAsync( params TKey[] ids ); + /// + /// 判断是否存在 + /// + /// 条件 + /// 取消令牌 + Task ExistsAsync( Expression> condition, CancellationToken cancellationToken = default ); + /// + /// 查找数量 + /// + /// 条件 + int Count( Expression> condition = null ); + /// + /// 查找数量 + /// + /// 查询条件 + /// 取消令牌 + Task CountAsync( Expression> condition = null, CancellationToken cancellationToken = default ); +} \ No newline at end of file diff --git a/src/Util.Data.Abstractions/Stores/IStore.cs b/src/Util.Data.Abstractions/Stores/IStore.cs new file mode 100644 index 000000000..2a0c4ee5b --- /dev/null +++ b/src/Util.Data.Abstractions/Stores/IStore.cs @@ -0,0 +1,67 @@ +using Util.Domain; + +namespace Util.Data.Stores; + +/// +/// 存储器 +/// +/// 对象类型 +public interface IStore : IStore, IQueryStore + where TEntity : class, IKey { +} + +/// +/// 存储器 +/// +/// 对象类型 +/// 对象标识类型 +public interface IStore : IQueryStore where TEntity : class, IKey { + /// + /// 添加实体 + /// + /// 实体 + /// 取消令牌 + Task AddAsync( TEntity entity, CancellationToken cancellationToken = default ); + /// + /// 添加实体集合 + /// + /// 实体集合 + /// 取消令牌 + Task AddAsync( IEnumerable entities, CancellationToken cancellationToken = default ); + /// + /// 修改实体 + /// + /// 实体 + /// 取消令牌 + Task UpdateAsync( TEntity entity, CancellationToken cancellationToken = default ); + /// + /// 修改实体集合 + /// + /// 实体集合 + /// 取消令牌 + Task UpdateAsync( IEnumerable entities, CancellationToken cancellationToken = default ); + /// + /// 移除实体 + /// + /// 标识 + /// 取消令牌 + Task RemoveAsync( object id, CancellationToken cancellationToken = default ); + /// + /// 移除实体 + /// + /// 实体 + /// 取消令牌 + Task RemoveAsync( TEntity entity, CancellationToken cancellationToken = default ); + /// + /// 移除实体集合 + /// + /// 标识集合 + /// 取消令牌 + Task RemoveAsync( IEnumerable ids, CancellationToken cancellationToken = default ); + /// + /// 移除实体集合 + /// + /// 实体集合 + /// 取消令牌 + Task RemoveAsync( IEnumerable entities, CancellationToken cancellationToken = default ); +} \ No newline at end of file diff --git a/src/Util.Data.Abstractions/Trees/ITreeQueryParameter.cs b/src/Util.Data.Abstractions/Trees/ITreeQueryParameter.cs new file mode 100644 index 000000000..1762ad296 --- /dev/null +++ b/src/Util.Data.Abstractions/Trees/ITreeQueryParameter.cs @@ -0,0 +1,25 @@ +using Util.Data.Queries; + +namespace Util.Data.Trees; + +/// +/// 树形查询参数 +/// +public interface ITreeQueryParameter : IPage { + /// + /// 父标识 + /// + string ParentId { get; set; } + /// + /// 层级 + /// + int? Level { get; set; } + /// + /// 路径 + /// + string Path { get; set; } + /// + /// 启用 + /// + bool? Enabled { get; set; } +} \ No newline at end of file diff --git a/src/Util.Data.Abstractions/Trees/TreeQueryParameter.cs b/src/Util.Data.Abstractions/Trees/TreeQueryParameter.cs new file mode 100644 index 000000000..31b2c5d2d --- /dev/null +++ b/src/Util.Data.Abstractions/Trees/TreeQueryParameter.cs @@ -0,0 +1,36 @@ +using Util.Data.Queries; + +namespace Util.Data.Trees; + +/// +/// 树形查询参数 +/// +public class TreeQueryParameter : QueryParameter, ITreeQueryParameter { + /// + /// 初始化树形查询参数 + /// + public TreeQueryParameter() { + Order = "SortId"; + } + + /// + /// 父标识 + /// + public string ParentId { get; set; } + + /// + /// 层级 + /// + public int? Level { get; set; } + + /// + /// 路径 + /// + public string Path { get; set; } + + /// + /// 启用 + /// + [Display( Name = "util.enabled" )] + public bool? Enabled { get; set; } +} \ No newline at end of file diff --git a/src/Util.Data.Abstractions/Usings.cs b/src/Util.Data.Abstractions/Usings.cs new file mode 100644 index 000000000..644e1e733 --- /dev/null +++ b/src/Util.Data.Abstractions/Usings.cs @@ -0,0 +1,8 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using System.Linq.Expressions; +global using System.Reflection; +global using System.ComponentModel.DataAnnotations; \ No newline at end of file diff --git a/src/Util.Data.Core/01-Util.Data.Core.csproj b/src/Util.Data.Core/01-Util.Data.Core.csproj new file mode 100644 index 000000000..057de461f --- /dev/null +++ b/src/Util.Data.Core/01-Util.Data.Core.csproj @@ -0,0 +1,40 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Data.Core + Util.Data + Util.Data.Core是Util应用框架数据访问基础类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Data.Core.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Data.Core.xml + + + + + True + False + + + + + + + + + + + + + + + + diff --git a/src/Util.Data.Core/DatabaseType.cs b/src/Util.Data.Core/DatabaseType.cs new file mode 100644 index 000000000..33eb71c68 --- /dev/null +++ b/src/Util.Data.Core/DatabaseType.cs @@ -0,0 +1,27 @@ +namespace Util.Data; + +/// +/// 数据库类型 +/// +public enum DatabaseType { + /// + /// Sql Server数据库 + /// + SqlServer, + /// + /// MySql数据库 + /// + MySql, + /// + /// PostgreSQL数据库 + /// + PgSql, + /// + /// Oracle数据库 + /// + Oracle, + /// + /// Sqlite数据库 + /// + Sqlite +} \ No newline at end of file diff --git a/src/Util.Data.Core/DefaultDatabase.cs b/src/Util.Data.Core/DefaultDatabase.cs new file mode 100644 index 000000000..04503a895 --- /dev/null +++ b/src/Util.Data.Core/DefaultDatabase.cs @@ -0,0 +1,26 @@ +namespace Util.Data; + +/// +/// 数据库信息 +/// +public class DefaultDatabase : IDatabase { + /// + /// 数据库连接 + /// + private readonly IDbConnection _connection; + + /// + /// 初始化数据库信息 + /// + /// 数据库连接 + public DefaultDatabase( IDbConnection connection ) { + _connection = connection ?? throw new ArgumentNullException( nameof(connection) ); + } + + /// + /// 获取数据库连接 + /// + public IDbConnection GetConnection() { + return _connection; + } +} \ No newline at end of file diff --git a/src/Util.Data.Core/FilterExtensions.cs b/src/Util.Data.Core/FilterExtensions.cs new file mode 100644 index 000000000..bdc3e1ed9 --- /dev/null +++ b/src/Util.Data.Core/FilterExtensions.cs @@ -0,0 +1,31 @@ +using Util.Data.Filters; + +namespace Util.Data; + +/// +/// 过滤器操作扩展 +/// +public static class FilterExtensions { + /// + /// 启用过滤器 + /// + /// 过滤器类型,范例: IDelete + /// 源 + public static void EnableFilter( this IFilterOperation source ) where TFilterType : class { + source.CheckNull( nameof( source ) ); + if( source is IFilterSwitch filterSwitch ) + filterSwitch.EnableFilter(); + } + + /// + /// 禁用过滤器 + /// + /// 过滤器类型,范例: IDelete + /// 源 + public static IDisposable DisableFilter( this IFilterOperation source ) where TFilterType : class { + source.CheckNull( nameof( source ) ); + if( source is IFilterSwitch filterSwitch ) + return filterSwitch.DisableFilter(); + return DisposeAction.Null; + } +} \ No newline at end of file diff --git a/src/Util.Data.Core/Filters/IFilter.cs b/src/Util.Data.Core/Filters/IFilter.cs new file mode 100644 index 000000000..17d350193 --- /dev/null +++ b/src/Util.Data.Core/Filters/IFilter.cs @@ -0,0 +1,39 @@ +using Util.Dependency; + +namespace Util.Data.Filters; + +/// +/// 数据过滤器 +/// +public interface IFilter : ITransientDependency { + /// + /// 过滤器是否启用 + /// + bool IsEnabled { get; } + /// + /// 实体是否启用过滤器 + /// + /// 实体类型 + bool IsEntityEnabled(); + /// + /// 启用 + /// + void Enable(); + /// + /// 禁用 + /// + IDisposable Disable(); + /// + /// 获取过滤表达式 + /// + /// 实体类型 + /// 参数 + Expression> GetExpression( object state ) where TEntity : class; +} + +/// +/// 数据过滤器 +/// +/// 过滤器类型 +public interface IFilter : IFilter where TFilterType : class { +} \ No newline at end of file diff --git a/src/Util.Data.Core/Filters/IFilterManager.cs b/src/Util.Data.Core/Filters/IFilterManager.cs new file mode 100644 index 000000000..c94eae5aa --- /dev/null +++ b/src/Util.Data.Core/Filters/IFilterManager.cs @@ -0,0 +1,35 @@ +using Util.Dependency; + +namespace Util.Data.Filters; + +/// +/// 数据过滤器管理器 +/// +public interface IFilterManager : IFilterSwitch,IScopeDependency { + /// + /// 获取过滤器 + /// + /// 过滤器类型 + IFilter GetFilter() where TFilterType : class; + /// + /// 获取过滤器 + /// + /// 过滤器类型 + IFilter GetFilter( Type filterType ); + /// + /// 实体是否启用过滤器 + /// + /// 实体类型 + bool IsEntityEnabled(); + /// + /// 过滤器是否启用 + /// + /// 过滤器类型 + bool IsEnabled() where TFilterType : class; + /// + /// 获取过滤表达式 + /// + /// 实体类型 + /// 参数 + Expression> GetExpression( object state ) where TEntity : class; +} \ No newline at end of file diff --git a/src/Util.Data.Core/Filters/IFilterSwitch.cs b/src/Util.Data.Core/Filters/IFilterSwitch.cs new file mode 100644 index 000000000..012d84dff --- /dev/null +++ b/src/Util.Data.Core/Filters/IFilterSwitch.cs @@ -0,0 +1,17 @@ +namespace Util.Data.Filters; + +/// +/// 数据过滤器开关 +/// +public interface IFilterSwitch { + /// + /// 启用过滤器 + /// + /// 过滤器类型 + void EnableFilter() where TFilterType : class; + /// + /// 禁用过滤器 + /// + /// 过滤器类型 + IDisposable DisableFilter() where TFilterType : class; +} \ No newline at end of file diff --git a/src/Util.Data.Core/IDatabase.cs b/src/Util.Data.Core/IDatabase.cs new file mode 100644 index 000000000..166556524 --- /dev/null +++ b/src/Util.Data.Core/IDatabase.cs @@ -0,0 +1,14 @@ +using Util.Aop; + +namespace Util.Data; + +/// +/// 数据库信息 +/// +[Ignore] +public interface IDatabase { + /// + /// 获取数据库连接 + /// + IDbConnection GetConnection(); +} \ No newline at end of file diff --git a/src/Util.Data.Core/IDatabaseFactory.cs b/src/Util.Data.Core/IDatabaseFactory.cs new file mode 100644 index 000000000..61fa8880f --- /dev/null +++ b/src/Util.Data.Core/IDatabaseFactory.cs @@ -0,0 +1,12 @@ +namespace Util.Data; + +/// +/// 数据库工厂 +/// +public interface IDatabaseFactory { + /// + /// 创建数据库信息 + /// + /// 数据库连接字符串 + IDatabase Create( string connection ); +} \ No newline at end of file diff --git a/src/Util.Data.Core/Queries/Boundary.cs b/src/Util.Data.Core/Queries/Boundary.cs new file mode 100644 index 000000000..32f15a83d --- /dev/null +++ b/src/Util.Data.Core/Queries/Boundary.cs @@ -0,0 +1,23 @@ +namespace Util.Data.Queries; + +/// +/// 查询边界 +/// +public enum Boundary { + /// + /// 包含左边 + /// + Left, + /// + /// 包含右边 + /// + Right, + /// + /// 包含两边 + /// + Both, + /// + /// 不包含 + /// + Neither +} \ No newline at end of file diff --git a/src/Util.Data.Core/Queries/Conditions/DateSegmentCondition.cs b/src/Util.Data.Core/Queries/Conditions/DateSegmentCondition.cs new file mode 100644 index 000000000..e6c982f29 --- /dev/null +++ b/src/Util.Data.Core/Queries/Conditions/DateSegmentCondition.cs @@ -0,0 +1,53 @@ +namespace Util.Data.Queries.Conditions; + +/// +/// 日期范围过滤条件 - 不包含时间 +/// +/// 实体类型 +/// 属性类型 +public class DateSegmentCondition : DateTimeSegmentCondition where TEntity : class { + /// + /// 初始化日期范围过滤条件 - 不包含时间 + /// + /// 属性表达式 + /// 最小值 + /// 最大值 + /// 包含边界 + public DateSegmentCondition( Expression> propertyExpression, DateTime? min, DateTime? max, Boundary boundary ) + : base( propertyExpression, min, max, boundary ) { + } + + /// + /// 获取最小值 + /// + protected override Expression GetMinValueExpression() { + var minValue = GetMinValue().SafeValue().Date; + if( GetBoundary() == Boundary.Right || GetBoundary() == Boundary.Neither ) + minValue = minValue.AddDays( 1 ); + return GetMinValueExpression( minValue ); + } + + /// + /// 获取最大值 + /// + protected override Expression GetMaxValueExpression() { + var maxValue = GetMaxValue().SafeValue().Date; + if ( GetBoundary() == Boundary.Right || GetBoundary() == Boundary.Both ) + maxValue = maxValue.AddDays( 1 ); + return GetMaxValueExpression( maxValue ); + } + + /// + /// 创建左操作符 + /// + protected override Operator CreateLeftOperator( Boundary? boundary ) { + return Operator.GreaterEqual; + } + + /// + /// 创建右操作符 + /// + protected override Operator CreateRightOperator( Boundary? boundary ) { + return Operator.Less; + } +} \ No newline at end of file diff --git a/src/Util.Data.Core/Queries/Conditions/DateTimeSegmentCondition.cs b/src/Util.Data.Core/Queries/Conditions/DateTimeSegmentCondition.cs new file mode 100644 index 000000000..f63851321 --- /dev/null +++ b/src/Util.Data.Core/Queries/Conditions/DateTimeSegmentCondition.cs @@ -0,0 +1,64 @@ +using Util.Data.Queries.Conditions.Internal; + +namespace Util.Data.Queries.Conditions; + +/// +/// 日期范围过滤条件 - 包含时间 +/// +/// 实体类型 +/// 属性类型 +public class DateTimeSegmentCondition : SegmentConditionBase where TEntity : class { + /// + /// 日期范围查询参数对象 + /// + private readonly DateTimeQuery _query; + + /// + /// 初始化日期范围过滤条件 - 包含时间 + /// + /// 属性表达式 + /// 最小值 + /// 最大值 + /// 包含边界 + public DateTimeSegmentCondition( Expression> propertyExpression, DateTime? min, DateTime? max, Boundary boundary = Boundary.Both ) + : base( propertyExpression, min, max, boundary ) { + _query = new DateTimeQuery(); + } + + /// + /// 最小值是否大于最大值 + /// + protected override bool IsMinGreaterMax( DateTime? min, DateTime? max ) { + return min > max; + } + + /// + /// 获取最小值表达式 + /// + protected override Expression GetMinValueExpression() { + return GetMinValueExpression( GetMinValue() ); + } + + /// + /// 获取最小值表达式 + /// + protected Expression GetMinValueExpression( DateTime? value ) { + _query.BeginTime = value; + return Expression.Property( Expression.Constant( _query ), "BeginTime" ); + } + + /// + /// 获取最大值表达式 + /// + protected override Expression GetMaxValueExpression() { + return GetMaxValueExpression( GetMaxValue() ); + } + + /// + /// 获取最大值表达式 + /// + protected Expression GetMaxValueExpression( DateTime? value ) { + _query.EndTime = value; + return Expression.Property( Expression.Constant( _query ), "EndTime" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Core/Queries/Conditions/DecimalSegmentCondition.cs b/src/Util.Data.Core/Queries/Conditions/DecimalSegmentCondition.cs new file mode 100644 index 000000000..90d0403f2 --- /dev/null +++ b/src/Util.Data.Core/Queries/Conditions/DecimalSegmentCondition.cs @@ -0,0 +1,50 @@ +using Util.Data.Queries.Conditions.Internal; + +namespace Util.Data.Queries.Conditions; + +/// +/// decimal范围过滤条件 +/// +/// 实体类型 +/// 属性类型 +public class DecimalSegmentCondition : SegmentConditionBase where TEntity : class { + /// + /// 范围查询参数对象 + /// + private readonly DecimalQuery _query; + + /// + /// 初始化decimal范围过滤条件 + /// + /// 属性表达式 + /// 最小值 + /// 最大值 + /// 包含边界 + public DecimalSegmentCondition( Expression> propertyExpression, decimal? min, decimal? max, Boundary boundary = Boundary.Both ) + : base( propertyExpression, min, max, boundary ) { + _query = new DecimalQuery(); + } + + /// + /// 最小值是否大于最大值 + /// + protected override bool IsMinGreaterMax( decimal? min, decimal? max ) { + return min > max; + } + + /// + /// 获取最小值表达式 + /// + protected override Expression GetMinValueExpression() { + _query.MinValue = GetMinValue(); + return Expression.Property( Expression.Constant( _query ), "MinValue" ); + } + + /// + /// 获取最大值表达式 + /// + protected override Expression GetMaxValueExpression() { + _query.MaxValue = GetMaxValue(); + return Expression.Property( Expression.Constant( _query ), "MaxValue" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Core/Queries/Conditions/DefaultCondition.cs b/src/Util.Data.Core/Queries/Conditions/DefaultCondition.cs new file mode 100644 index 000000000..ef04f0e65 --- /dev/null +++ b/src/Util.Data.Core/Queries/Conditions/DefaultCondition.cs @@ -0,0 +1,27 @@ +namespace Util.Data.Queries.Conditions; + +/// +/// 默认查询条件 +/// +/// 实体类型 +public class DefaultCondition : ICondition where TEntity : class { + /// + /// 查询条件 + /// + private readonly Expression> _condition; + + /// + /// 初始化查询条件 + /// + /// 查询条件 + public DefaultCondition( Expression> condition ) { + _condition = condition; + } + + /// + /// 获取查询条件 + /// + public Expression> GetCondition() { + return _condition; + } +} \ No newline at end of file diff --git a/src/Util.Data.Core/Queries/Conditions/DoubleSegmentCondition.cs b/src/Util.Data.Core/Queries/Conditions/DoubleSegmentCondition.cs new file mode 100644 index 000000000..53dd4d151 --- /dev/null +++ b/src/Util.Data.Core/Queries/Conditions/DoubleSegmentCondition.cs @@ -0,0 +1,50 @@ +using Util.Data.Queries.Conditions.Internal; + +namespace Util.Data.Queries.Conditions; + +/// +/// double范围过滤条件 +/// +/// 实体类型 +/// 属性类型 +public class DoubleSegmentCondition : SegmentConditionBase where TEntity : class { + /// + /// 范围查询参数对象 + /// + private readonly DoubleQuery _query; + + /// + /// 初始化double范围过滤条件 + /// + /// 属性表达式 + /// 最小值 + /// 最大值 + /// 包含边界 + public DoubleSegmentCondition( Expression> propertyExpression, double? min, double? max, Boundary boundary = Boundary.Both ) + : base( propertyExpression, min, max, boundary ) { + _query = new DoubleQuery(); + } + + /// + /// 最小值是否大于最大值 + /// + protected override bool IsMinGreaterMax( double? min, double? max ) { + return min > max; + } + + /// + /// 获取最小值表达式 + /// + protected override Expression GetMinValueExpression() { + _query.MinValue = GetMinValue(); + return Expression.Property( Expression.Constant( _query ), "MinValue" ); + } + + /// + /// 获取最大值表达式 + /// + protected override Expression GetMaxValueExpression() { + _query.MaxValue = GetMaxValue(); + return Expression.Property( Expression.Constant( _query ), "MaxValue" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Core/Queries/Conditions/IntSegmentCondition.cs b/src/Util.Data.Core/Queries/Conditions/IntSegmentCondition.cs new file mode 100644 index 000000000..f7c97c592 --- /dev/null +++ b/src/Util.Data.Core/Queries/Conditions/IntSegmentCondition.cs @@ -0,0 +1,50 @@ +using Util.Data.Queries.Conditions.Internal; + +namespace Util.Data.Queries.Conditions; + +/// +/// 整数范围过滤条件 +/// +/// 实体类型 +/// 属性类型 +public class IntSegmentCondition : SegmentConditionBase where TEntity : class { + /// + /// 范围查询参数对象 + /// + private readonly IntQuery _query; + + /// + /// 初始化整数范围过滤条件 + /// + /// 属性表达式 + /// 最小值 + /// 最大值 + /// 包含边界 + public IntSegmentCondition( Expression> propertyExpression, int? min, int? max, Boundary boundary = Boundary.Both ) + : base( propertyExpression,min,max, boundary ) { + _query = new IntQuery(); + } + + /// + /// 最小值是否大于最大值 + /// + protected override bool IsMinGreaterMax( int? min, int? max ) { + return min > max; + } + + /// + /// 获取最小值表达式 + /// + protected override Expression GetMinValueExpression() { + _query.MinValue = GetMinValue(); + return Expression.Property( Expression.Constant( _query ), "MinValue" ); + } + + /// + /// 获取最大值表达式 + /// + protected override Expression GetMaxValueExpression() { + _query.MaxValue = GetMaxValue(); + return Expression.Property( Expression.Constant( _query ), "MaxValue" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Core/Queries/Conditions/Internal/DateTimeQuery.cs b/src/Util.Data.Core/Queries/Conditions/Internal/DateTimeQuery.cs new file mode 100644 index 000000000..c6aec2e6c --- /dev/null +++ b/src/Util.Data.Core/Queries/Conditions/Internal/DateTimeQuery.cs @@ -0,0 +1,15 @@ +namespace Util.Data.Queries.Conditions.Internal; + +/// +/// 日期范围查询参数对象 - 使用该对象的目的是构建参数化条件 +/// +internal class DateTimeQuery { + /// + /// 起始时间 + /// + public DateTime? BeginTime { get; set; } + /// + /// 结束时间 + /// + public DateTime? EndTime { get; set; } +} \ No newline at end of file diff --git a/src/Util.Data.Core/Queries/Conditions/Internal/DecimalQuery.cs b/src/Util.Data.Core/Queries/Conditions/Internal/DecimalQuery.cs new file mode 100644 index 000000000..3b799a1a3 --- /dev/null +++ b/src/Util.Data.Core/Queries/Conditions/Internal/DecimalQuery.cs @@ -0,0 +1,15 @@ +namespace Util.Data.Queries.Conditions.Internal; + +/// +/// decimal范围查询参数对象 - 使用该对象的目的是构建参数化条件 +/// +internal class DecimalQuery { + /// + /// 最小值 + /// + public decimal? MinValue { get; set; } + /// + /// 最大值 + /// + public decimal? MaxValue { get; set; } +} \ No newline at end of file diff --git a/src/Util.Data.Core/Queries/Conditions/Internal/DoubleQuery.cs b/src/Util.Data.Core/Queries/Conditions/Internal/DoubleQuery.cs new file mode 100644 index 000000000..bfb701b5c --- /dev/null +++ b/src/Util.Data.Core/Queries/Conditions/Internal/DoubleQuery.cs @@ -0,0 +1,15 @@ +namespace Util.Data.Queries.Conditions.Internal; + +/// +/// double范围查询参数对象 - 使用该对象的目的是构建参数化条件 +/// +internal class DoubleQuery { + /// + /// 最小值 + /// + public double? MinValue { get; set; } + /// + /// 最大值 + /// + public double? MaxValue { get; set; } +} \ No newline at end of file diff --git a/src/Util.Data.Core/Queries/Conditions/Internal/IntQuery.cs b/src/Util.Data.Core/Queries/Conditions/Internal/IntQuery.cs new file mode 100644 index 000000000..562a5bd4f --- /dev/null +++ b/src/Util.Data.Core/Queries/Conditions/Internal/IntQuery.cs @@ -0,0 +1,15 @@ +namespace Util.Data.Queries.Conditions.Internal; + +/// +/// int范围查询参数对象 - 使用该对象的目的是构建参数化条件 +/// +internal class IntQuery { + /// + /// 最小值 + /// + public int? MinValue { get; set; } + /// + /// 最大值 + /// + public int? MaxValue { get; set; } +} \ No newline at end of file diff --git a/src/Util.Data.Core/Queries/Conditions/OrIfNotEmptyCondition.cs b/src/Util.Data.Core/Queries/Conditions/OrIfNotEmptyCondition.cs new file mode 100644 index 000000000..5d887d5b5 --- /dev/null +++ b/src/Util.Data.Core/Queries/Conditions/OrIfNotEmptyCondition.cs @@ -0,0 +1,37 @@ +namespace Util.Data.Queries.Conditions; + +/// +/// OrIfNotEmpty条件 +/// +/// 实体类型 +public class OrIfNotEmptyCondition : ICondition { + /// + /// 查询条件列表 + /// + private readonly List>> _conditions; + + /// + /// WhereIfNotEmpty条件 + /// + /// 查询条件1 + /// 查询条件2 + /// 查询条件列表 + public OrIfNotEmptyCondition( Expression> condition1, Expression> condition2, params Expression>[] conditions ) { + _conditions = new List>> { condition1, condition2 }; + _conditions.AddRange( conditions ); + } + + /// + /// 获取查询条件 + /// + public Expression> GetCondition() { + Expression> result = null; + foreach( var condition in _conditions ) { + var predicate = new WhereIfNotEmptyCondition( condition ).GetCondition(); + if( predicate == null ) + continue; + result = result.Or( predicate ); + } + return result; + } +} \ No newline at end of file diff --git a/src/Util.Data.Core/Queries/Conditions/SegmentConditionBase.cs b/src/Util.Data.Core/Queries/Conditions/SegmentConditionBase.cs new file mode 100644 index 000000000..faef3771a --- /dev/null +++ b/src/Util.Data.Core/Queries/Conditions/SegmentConditionBase.cs @@ -0,0 +1,170 @@ +using Util.Expressions; +using Util.Helpers; + +namespace Util.Data.Queries.Conditions; + +/// +/// 范围过滤条件 +/// +/// 实体类型 +/// 属性类型 +/// 值类型 +public abstract class SegmentConditionBase : ICondition + where TEntity : class + where TValue : struct { + /// + /// 属性表达式 + /// + private readonly Expression> _propertyExpression; + /// + /// 表达式生成器 + /// + private readonly PredicateExpressionBuilder _builder; + /// + /// 最小值 + /// + private TValue? _min; + /// + /// 最大值 + /// + private TValue? _max; + /// + /// 包含边界 + /// + private readonly Boundary _boundary; + + /// + /// 初始化范围过滤条件 + /// + /// 属性表达式 + /// 最小值 + /// 最大值 + /// 包含边界 + protected SegmentConditionBase( Expression> propertyExpression, TValue? min, TValue? max, Boundary boundary ) { + _builder = new PredicateExpressionBuilder(); + _propertyExpression = propertyExpression; + _min = min; + _max = max; + _boundary = boundary; + } + + /// + /// 获取属性类型 + /// + protected Type GetPropertyType() { + return Lambda.GetType( _propertyExpression ); + } + + /// + /// 获取边界 + /// + protected Boundary GetBoundary() { + return _boundary; + } + + /// + /// 获取查询条件 + /// + public Expression> GetCondition() { + _builder.Clear(); + Adjust( _min, _max ); + CreateLeftExpression(); + CreateRightExpression(); + return _builder.ToLambda(); + } + + /// + /// 当最小值大于最大值时进行校正 + /// + private void Adjust( TValue? min, TValue? max ) { + if( IsMinGreaterMax( min, max ) == false ) + return; + _min = max; + _max = min; + } + + /// + /// 最小值是否大于最大值 + /// + /// 最小值 + /// 最大值 + protected abstract bool IsMinGreaterMax( TValue? min, TValue? max ); + + /// + /// 创建左操作数,即 t => t.Property >= Min + /// + private void CreateLeftExpression() { + if( _min == null ) + return; + var minValueExpression = GetMinValueExpression(); + var expression = Expression.Convert( minValueExpression, GetPropertyType() ); + _builder.Append( _propertyExpression, CreateLeftOperator( _boundary ), expression ); + } + + /// + /// 创建左操作符 + /// + protected virtual Operator CreateLeftOperator( Boundary? boundary ) { + switch( boundary ) { + case Boundary.Left: + return Operator.GreaterEqual; + case Boundary.Both: + return Operator.GreaterEqual; + default: + return Operator.Greater; + } + } + + /// + /// 获取最小值 + /// + protected TValue? GetMinValue() { + return _min; + } + + /// + /// 获取最小值表达式 + /// + protected virtual Expression GetMinValueExpression() { + return Lambda.Constant( _min, _propertyExpression ); + } + + /// + /// 创建右操作数,即 t => t.Property <= Max + /// + private void CreateRightExpression() { + if( _max == null ) + return; + var maxValueExpression = GetMaxValueExpression(); + var expression = Expression.Convert( maxValueExpression, GetPropertyType() ); + _builder.Append( _propertyExpression, CreateRightOperator( _boundary ), expression ); + } + + /// + /// 创建右操作符 + /// + protected virtual Operator CreateRightOperator( Boundary? boundary ) { + switch( boundary ) { + case Boundary.Right: + return Operator.LessEqual; + case Boundary.Both: + return Operator.LessEqual; + default: + return Operator.Less; + } + } + + /// + /// 获取最大值 + /// + protected TValue? GetMaxValue() { + return _max; + } + + /// + /// 获取最大值表达式 + /// + protected virtual Expression GetMaxValueExpression() { + return Lambda.Constant( _max, _propertyExpression ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Core/Queries/Conditions/WhereIfNotEmptyCondition.cs b/src/Util.Data.Core/Queries/Conditions/WhereIfNotEmptyCondition.cs new file mode 100644 index 000000000..1a4416411 --- /dev/null +++ b/src/Util.Data.Core/Queries/Conditions/WhereIfNotEmptyCondition.cs @@ -0,0 +1,34 @@ +using Util.Helpers; +using Util.Properties; + +namespace Util.Data.Queries.Conditions; + +/// +/// WhereIfNotEmpty条件 +/// +/// 实体类型 +public class WhereIfNotEmptyCondition : ICondition { + /// + /// 查询条件 + /// + private readonly Expression> _condition; + + /// + /// WhereIfNotEmpty条件 + /// + /// 查询条件 + public WhereIfNotEmptyCondition( Expression> condition ) { + _condition = condition; + } + + /// + /// 获取查询条件 + /// + public Expression> GetCondition() { + if( _condition == null ) + return null; + if( Lambda.GetConditionCount( _condition ) > 1 ) + throw new InvalidOperationException( string.Format( R.CanOnlyOneCondition, _condition ) ); + return _condition.Value().SafeString().IsEmpty() ? null : _condition; + } +} \ No newline at end of file diff --git a/src/Util.Data.Core/QueryableExtensions.cs b/src/Util.Data.Core/QueryableExtensions.cs new file mode 100644 index 000000000..6c5393d01 --- /dev/null +++ b/src/Util.Data.Core/QueryableExtensions.cs @@ -0,0 +1,182 @@ +using Util.Data.Queries; +using Util.Data.Queries.Conditions; + +namespace Util.Data; + +/// +/// 查询对象扩展 +/// +public static class QueryableExtensions { + + #region Where(添加查询条件对象) + + /// + /// 添加查询条件对象 + /// + /// 实体类型 + /// 源 + /// 查询条件 + public static IQueryable Where( this IQueryable source, ICondition condition ) where TEntity : class { + source.CheckNull( nameof( source ) ); + condition.CheckNull( nameof( condition ) ); + var predicate = condition.GetCondition(); + if( predicate == null ) + return source; + return source.Where( predicate ); + } + + #endregion + + #region WhereIf(根据规则添加查询条件) + + /// + /// 根据规则添加查询条件 + /// + /// 实体类型 + /// 数据源 + /// 查询条件 + /// 该值为true时添加查询条件,否则忽略 + public static IQueryable WhereIf( this IQueryable source, Expression> predicate, bool condition ) where TEntity : class { + source.CheckNull( nameof( source ) ); + return condition ? source.Where( predicate ) : source; + } + + #endregion + + #region WhereIfNotEmpty(根据规则添加查询条件) + + /// + /// 根据规则添加查询条件 + /// + /// 实体类型 + /// 数据源 + /// 查询条件,如果参数值为空,则忽略该查询条件,范例:t => t.Name == "",该查询条件被忽略。 + /// 注意:一次仅能添加一个条件,范例:t => t.Name == "a" && t.Mobile == "123",不支持,将抛出异常 + public static IQueryable WhereIfNotEmpty( this IQueryable source, Expression> condition ) where TEntity : class { + source.CheckNull( nameof( source ) ); + return source.Where( new WhereIfNotEmptyCondition( condition ) ); + } + + #endregion + + #region OrIfNotEmpty(添加Or查询条件) + + /// + /// 添加Or查询条件,对传入的条件使用Or连接 + /// + /// 实体类型 + /// 数据源 + /// 查询条件1,如果参数值为空,则忽略该查询条件,范例:t => t.Name == "",该查询条件被忽略。 + /// 注意:一次仅能添加一个条件,范例:t => t.Name == "a" && t.Mobile == "123",不支持,将抛出异常 + /// 查询条件2,如果参数值为空,则忽略该查询条件,范例:t => t.Name == "",该查询条件被忽略。 + /// 注意:一次仅能添加一个条件,范例:t => t.Name == "a" && t.Mobile == "123",不支持,将抛出异常 + /// 查询条件列表,如果参数值为空,则忽略该查询条件,范例:t => t.Name == "",该查询条件被忽略。 + /// 注意:一次仅能添加一个条件,范例:t => t.Name == "a" && t.Mobile == "123",不支持,将抛出异常 + public static IQueryable OrIfNotEmpty( this IQueryable source, Expression> condition1, + Expression> condition2,params Expression>[] conditions ) where TEntity : class { + source.CheckNull( nameof( source ) ); + return source.Where( new OrIfNotEmptyCondition( condition1, condition2, conditions ) ); + } + + #endregion + + #region Between(添加范围查询条件) + + /// + /// 添加范围查询条件 + /// + /// 实体类型 + /// 数据源 + /// 属性类型 + /// 属性表达式,范例:t => t.Age + /// 最小值 + /// 最大值 + /// 包含边界 + public static IQueryable Between( this IQueryable source, Expression> propertyExpression, int? min, int? max, Boundary boundary = Boundary.Both ) where TEntity : class { + source.CheckNull( nameof( source ) ); + return source.Where( new IntSegmentCondition( propertyExpression, min, max, boundary ) ); + } + + /// + /// 添加范围查询条件 + /// + /// 实体类型 + /// 数据源 + /// 属性类型 + /// 属性表达式,范例:t => t.Age + /// 最小值 + /// 最大值 + /// 包含边界 + public static IQueryable Between( this IQueryable source, Expression> propertyExpression, double? min, double? max, Boundary boundary = Boundary.Both ) where TEntity : class { + source.CheckNull( nameof( source ) ); + return source.Where( new DoubleSegmentCondition( propertyExpression, min, max, boundary ) ); + } + + /// + /// 添加范围查询条件 + /// + /// 实体类型 + /// 数据源 + /// 属性类型 + /// 属性表达式,范例:t => t.Age + /// 最小值 + /// 最大值 + /// 包含边界 + public static IQueryable Between( this IQueryable source, Expression> propertyExpression, decimal? min, decimal? max, Boundary boundary = Boundary.Both ) where TEntity : class { + source.CheckNull( nameof( source ) ); + return source.Where( new DecimalSegmentCondition( propertyExpression, min, max, boundary ) ); + } + + /// + /// 添加范围查询条件 + /// + /// 实体类型 + /// 数据源 + /// 属性类型 + /// 属性表达式,范例:t => t.Time + /// 最小值 + /// 最大值 + /// 是否包含时间 + /// 包含边界 + public static IQueryable Between( this IQueryable source, Expression> propertyExpression, DateTime? min, DateTime? max, bool includeTime = true, Boundary boundary = Boundary.Both ) where TEntity : class { + if( source == null ) + throw new ArgumentNullException( nameof( source ) ); + if( includeTime ) + return source.Where( new DateTimeSegmentCondition( propertyExpression, min, max, boundary ) ); + return source.Where( new DateSegmentCondition( propertyExpression, min, max, boundary ) ); + } + + #endregion + + #region OrderBy(排序) + + /// + /// 排序,说明:如果未设置排序条件,则使用Id排序 + /// + /// 实体类型 + /// 源 + /// 分页参数 + /// 默认排序属性,如果未设置排序条件,则使用该排序属性,范例: "Id" + public static IQueryable OrderBy( this IQueryable source, IPage parameter,string defaultOrder = null ) where TEntity : class { + source.CheckNull( nameof( source ) ); + parameter.CheckNull( nameof( parameter ) ); + InitOrder( source, parameter, defaultOrder ); + return parameter.Order.IsEmpty() ? source : source.OrderBy( parameter.Order ); + } + + /// + /// 初始化排序 + /// + private static void InitOrder( IQueryable source, IPage parameter, string defaultOrder ) { + if( parameter.Order.IsEmpty() == false ) + return; + var expression = source.Expression.SafeString(); + if( expression.Contains( ".OrderBy(" ) || expression.Contains( ".OrderByDescending(" ) ) + return; + if ( defaultOrder.IsEmpty() ) + return; + parameter.Order = defaultOrder; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Core/Trees/TreeCondition.cs b/src/Util.Data.Core/Trees/TreeCondition.cs new file mode 100644 index 000000000..f6adf432a --- /dev/null +++ b/src/Util.Data.Core/Trees/TreeCondition.cs @@ -0,0 +1,52 @@ +using Util.Domain.Trees; + +namespace Util.Data.Trees; + +/// +/// 树形查询条件 +/// +public class TreeCondition : TreeCondition where TEntity : IPath, IEnabled, IParentId { + /// + /// 初始化树型查询条件 + /// + /// 查询参数 + public TreeCondition( ITreeQueryParameter parameter ) : base( parameter ) { + } +} + +/// +/// 树形查询条件 +/// +/// 树形实体类型 +/// 树形实体父标识类型 +public class TreeCondition : ICondition where TEntity : IPath, IEnabled, IParentId { + /// + /// 初始化树形查询条件 + /// + /// 查询参数 + public TreeCondition( ITreeQueryParameter parameter ) { + if ( parameter == null ) + return; + var parentId = Util.Helpers.Convert.To( parameter.ParentId ); + if ( parameter.ParentId.IsEmpty() == false ) + Condition = Condition.And( t => t.ParentId.Equals( parentId ) ); + if ( parameter.Path.IsEmpty() == false ) + Condition = Condition.And( t => t.Path.StartsWith( parameter.Path ) ); + if ( parameter.Level != null ) + Condition = Condition.And( t => t.Level == parameter.Level ); + if ( parameter.Enabled != null ) + Condition = Condition.And( t => t.Enabled == parameter.Enabled ); + } + + /// + /// 查询条件 + /// + protected Expression> Condition { get; set; } + + /// + /// 获取查询条件 + /// + public Expression> GetCondition() { + return Condition; + } +} \ No newline at end of file diff --git a/src/Util.Data.Core/UnitOfWorkActionManager.cs b/src/Util.Data.Core/UnitOfWorkActionManager.cs new file mode 100644 index 000000000..ef1b38252 --- /dev/null +++ b/src/Util.Data.Core/UnitOfWorkActionManager.cs @@ -0,0 +1,35 @@ +using Util.Dependency; + +namespace Util.Data; + +/// +/// 工作单元操作管理器 +/// +[Ioc( 1 )] +public class UnitOfWorkActionManager : IUnitOfWorkActionManager { + /// + /// 操作列表 + /// + private readonly List> _actions; + + /// + /// 初始化工作单元操作管理器 + /// + public UnitOfWorkActionManager() { + _actions = new List>(); + } + + /// + public void Register( Func action ) { + if ( action == null ) + return; + _actions.Add( action ); + } + + /// + public async Task ExecuteAsync() { + foreach ( var action in _actions ) + await action(); + _actions.Clear(); + } +} \ No newline at end of file diff --git a/src/Util.Data.Core/Usings.cs b/src/Util.Data.Core/Usings.cs new file mode 100644 index 000000000..5ad6166fa --- /dev/null +++ b/src/Util.Data.Core/Usings.cs @@ -0,0 +1,8 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Data; +global using System.Linq.Dynamic.Core; +global using System.Linq.Expressions; +global using System.ComponentModel.DataAnnotations; diff --git a/src/Util.Data.Dapper.All/06-Util.Data.Dapper.All.csproj b/src/Util.Data.Dapper.All/06-Util.Data.Dapper.All.csproj new file mode 100644 index 000000000..b1a113887 --- /dev/null +++ b/src/Util.Data.Dapper.All/06-Util.Data.Dapper.All.csproj @@ -0,0 +1,35 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Data.Dapper.All + Util.Data.Dapper + Util.Data.Dapper.All是Util应用框架基于Dapper的数据访问封装类库,包含所有支持的数据库实现 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Data.Dapper.All.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Data.Dapper.All.xml + + + + + True + False + + + + + + + + + + + diff --git a/src/Util.Data.Dapper.All/Metadata/MetadataServiceFactory.cs b/src/Util.Data.Dapper.All/Metadata/MetadataServiceFactory.cs new file mode 100644 index 000000000..6f0d3427a --- /dev/null +++ b/src/Util.Data.Dapper.All/Metadata/MetadataServiceFactory.cs @@ -0,0 +1,73 @@ +using Util.Data.Dapper.Sql; +using Util.Data.Metadata; +using Util.Data.Sql; + +namespace Util.Data.Dapper.Metadata; + +/// +/// 数据库元数据服务工厂 +/// +public class MetadataServiceFactory : IMetadataServiceFactory { + /// + /// 服务提供器 + /// + private readonly IServiceProvider _serviceProvider; + + /// + /// 初始化数据库元数据服务工厂 + /// + /// 服务提供器 + public MetadataServiceFactory( IServiceProvider serviceProvider ) { + _serviceProvider = serviceProvider; + } + + /// + /// 创建数据库元数据服务 + /// + /// 数据库类型 + /// 数据库连接字符串 + public IMetadataService Create( DatabaseType dbType,string connection ) { + switch ( dbType ) { + case DatabaseType.SqlServer: + return CreateSqlServerMetadataService( connection ); + case DatabaseType.PgSql: + return CreatePgSqlMetadataService( connection ); + case DatabaseType.MySql: + return CreateMySqlMetadataService( connection ); + } + throw new NotImplementedException(); + } + + /// + /// 创建Sql Server数据库元数据服务 + /// + private IMetadataService CreateSqlServerMetadataService( string connection ) { + var options = new SqlOptions { + ConnectionString = connection + }; + var sqlQuery = new SqlServerSqlQuery( _serviceProvider, options ); + return new SqlServerMetadataService( sqlQuery ); + } + + /// + /// 创建PostgreSql数据库元数据服务 + /// + private IMetadataService CreatePgSqlMetadataService( string connection ) { + var options = new SqlOptions { + ConnectionString = connection + }; + var sqlQuery = new PostgreSqlQuery( _serviceProvider, options ); + return new PostgreSqlMetadataService( sqlQuery ); + } + + /// + /// 创建MySql数据库元数据服务 + /// + private IMetadataService CreateMySqlMetadataService( string connection ) { + var options = new SqlOptions { + ConnectionString = connection + }; + var sqlQuery = new MySqlQuery( _serviceProvider, options ); + return new MySqlMetadataService( sqlQuery ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.All/Metadata/TypeConverterFactory.cs b/src/Util.Data.Dapper.All/Metadata/TypeConverterFactory.cs new file mode 100644 index 000000000..491091286 --- /dev/null +++ b/src/Util.Data.Dapper.All/Metadata/TypeConverterFactory.cs @@ -0,0 +1,24 @@ +using Util.Data.Metadata; + +namespace Util.Data.Dapper.Metadata; + +/// +/// 数据类型转换器工厂 +/// +public class TypeConverterFactory : ITypeConverterFactory { + /// + /// 创建数据库元数据服务 + /// + /// 数据库类型 + public ITypeConverter Create( DatabaseType dbType ) { + switch ( dbType ) { + case DatabaseType.SqlServer: + return new SqlServerTypeConverter(); + case DatabaseType.PgSql: + return new PostgreSqlTypeConverter(); + case DatabaseType.MySql: + return new MySqlTypeConverter(); + } + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.All/Usings.cs b/src/Util.Data.Dapper.All/Usings.cs new file mode 100644 index 000000000..3e51ec473 --- /dev/null +++ b/src/Util.Data.Dapper.All/Usings.cs @@ -0,0 +1,2 @@ +global using System; +global using Microsoft.Extensions.DependencyInjection.Extensions; diff --git a/src/Util.Data.Dapper.Core/01-Util.Data.Dapper.Core.csproj b/src/Util.Data.Dapper.Core/01-Util.Data.Dapper.Core.csproj new file mode 100644 index 000000000..39feba1fc --- /dev/null +++ b/src/Util.Data.Dapper.Core/01-Util.Data.Dapper.Core.csproj @@ -0,0 +1,54 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Data.Dapper.Core + Util.Data.Dapper + Util.Data.Dapper.Core是Util应用框架基于Dapper的数据访问封装基础类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Data.Dapper.Core.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Data.Dapper.Core.xml + + + + + True + False + + + + + + + + + + + + + + + + + True + True + UtilDataDapperResource.resx + + + + + + ResXFileCodeGenerator + UtilDataDapperResource.Designer.cs + + + + diff --git a/src/Util.Data.Dapper.Core/Infrastructure/DapperServiceRegistrar.cs b/src/Util.Data.Dapper.Core/Infrastructure/DapperServiceRegistrar.cs new file mode 100644 index 000000000..3a2ed1a25 --- /dev/null +++ b/src/Util.Data.Dapper.Core/Infrastructure/DapperServiceRegistrar.cs @@ -0,0 +1,33 @@ +using Util.Data.Dapper.TypeHandlers; +using Util.Infrastructure; + +namespace Util.Data.Dapper.Infrastructure; + +/// +/// Dapper服务注册器 +/// +public class DapperServiceRegistrar : IServiceRegistrar { + /// + /// 获取服务名 + /// + public static string ServiceName => "Util.Data.Dapper.Infrastructure.DapperServiceRegistrar"; + + /// + /// 排序号 + /// + public int OrderId => 810; + + /// + /// 是否启用 + /// + public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName ); + + /// + /// 注册服务 + /// + /// 服务上下文 + public Action Register( ServiceContext serviceContext ) { + SqlMapper.AddTypeHandler( new ExtraPropertiesTypeHandler() ); + return null; + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.Core/Infrastructure/ServiceRegistrarConfigExtensions.cs b/src/Util.Data.Dapper.Core/Infrastructure/ServiceRegistrarConfigExtensions.cs new file mode 100644 index 000000000..05e0d6993 --- /dev/null +++ b/src/Util.Data.Dapper.Core/Infrastructure/ServiceRegistrarConfigExtensions.cs @@ -0,0 +1,26 @@ +using Util.Infrastructure; + +namespace Util.Data.Dapper.Infrastructure; + +/// +/// Dapper服务注册器配置扩展 +/// +public static class ServiceRegistrarConfigExtensions { + /// + /// 启用Dapper服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig EnableDapperServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Enable( DapperServiceRegistrar.ServiceName ); + return config; + } + + /// + ///禁用Dapper服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig DisableDapperServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Disable( DapperServiceRegistrar.ServiceName ); + return config; + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.Core/Properties/UtilDataDapperResource.Designer.cs b/src/Util.Data.Dapper.Core/Properties/UtilDataDapperResource.Designer.cs new file mode 100644 index 000000000..fee857667 --- /dev/null +++ b/src/Util.Data.Dapper.Core/Properties/UtilDataDapperResource.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace Util.Data.Dapper.Properties { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class UtilDataDapperResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal UtilDataDapperResource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Util.Data.Dapper.Properties.UtilDataDapperResource", typeof(UtilDataDapperResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 数据库连接字符串不能为空 的本地化字符串。 + /// + internal static string ConnectionIsEmpty { + get { + return ResourceManager.GetString("ConnectionIsEmpty", resourceCulture); + } + } + + /// + /// 查找类似 数据库工厂不能为空 的本地化字符串。 + /// + internal static string DatabaseFactoryIsNull { + get { + return ResourceManager.GetString("DatabaseFactoryIsNull", resourceCulture); + } + } + } +} diff --git a/src/Util.Data.Dapper.Core/Properties/UtilDataDapperResource.resx b/src/Util.Data.Dapper.Core/Properties/UtilDataDapperResource.resx new file mode 100644 index 000000000..68233c2e8 --- /dev/null +++ b/src/Util.Data.Dapper.Core/Properties/UtilDataDapperResource.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 数据库连接字符串不能为空 + + + 数据库工厂不能为空 + + \ No newline at end of file diff --git a/src/Util.Data.Dapper.Core/Sql/SqlExecutorBase.cs b/src/Util.Data.Dapper.Core/Sql/SqlExecutorBase.cs new file mode 100644 index 000000000..4f3290470 --- /dev/null +++ b/src/Util.Data.Dapper.Core/Sql/SqlExecutorBase.cs @@ -0,0 +1,76 @@ +using Util.Data.Sql; + +namespace Util.Data.Dapper.Sql; + +/// +/// Sql执行器 +/// +public abstract class SqlExecutorBase : SqlQueryBase, ISqlExecutor { + + #region 构造方法 + + /// + /// 初始化Sql执行器 + /// + /// 服务提供器 + /// Sql配置 + /// 数据库信息,用于接入其它数据源,比如EF DbContext + protected SqlExecutorBase( IServiceProvider serviceProvider, SqlOptions options, IDatabase database ) : base( serviceProvider,options, database ) { + } + + #endregion + + #region ExecuteAsync(执行增删改操作) + + /// + /// 执行增删改操作 + /// + /// 执行超时时间,单位:秒 + public virtual async Task ExecuteAsync( int? timeout = null ) { + int result = 0; + try { + if ( ExecuteBefore() == false ) + return 0; + var connection = GetConnection(); + result = await connection.ExecuteAsync( GetSql(), Params, GetTransaction(), timeout ); + return result; + } + catch ( Exception ) { + RollbackTransaction(); + throw; + } + finally { + ExecuteAfter( result ); + } + } + + #endregion + + #region ExecuteProcedureAsync(执行存储过程增删改操作) + + /// + /// 执行存储过程增删改操作 + /// + /// 存储过程 + /// 执行超时时间,单位:秒 + public async Task ExecuteProcedureAsync( string procedure, int? timeout = null ) { + int result = 0; + try { + if( ExecuteBefore() == false ) + return default; + SetSql( GetProcedure( procedure ) ); + var connection = GetConnection(); + result = await connection.ExecuteAsync( GetSql(), Params, GetTransaction(), timeout, GetProcedureCommandType() ); + return result; + } + catch ( Exception ) { + RollbackTransaction(); + throw; + } + finally { + ExecuteAfter( result ); + } + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.Core/Sql/SqlQueryBase.cs b/src/Util.Data.Dapper.Core/Sql/SqlQueryBase.cs new file mode 100644 index 000000000..12f7b1d97 --- /dev/null +++ b/src/Util.Data.Dapper.Core/Sql/SqlQueryBase.cs @@ -0,0 +1,1402 @@ +using Util.Data.Dapper.Properties; +using Util.Data.Sql; +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Clauses; +using Util.Data.Sql.Builders.Core; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Builders.Sets; +using Util.Data.Sql.Configs; +using Util.Data.Sql.Database; +using Util.Helpers; + +namespace Util.Data.Dapper.Sql; + +/// +/// Sql查询对象 +/// +public abstract class SqlQueryBase : ISqlQuery, ISqlPartAccessor, ISqlOptionsAccessor, IGetParameter, IClearParameters, IConnectionManager, ITransactionManager { + + #region 字段 + + /// + /// 数据库信息 + /// + private IDatabase _database; + /// + /// Sql配置 + /// + protected readonly SqlOptions Options; + /// + /// Sql生成器 + /// + private ISqlBuilder _sqlBuilder; + /// + /// 数据库连接 + /// + private IDbConnection _connection; + /// + /// 事务 + /// + private IDbTransaction _transaction; + /// + /// Sql结果字符串 + /// + private string _sql; + /// + /// 动态参数列表 + /// + private DynamicParameters _parameters; + /// + /// 参数字面值解析器 + /// + private IParamLiteralsResolver _paramLiteralsResolver; + + #endregion + + #region 构造方法 + + /// + /// 初始化Sql查询对象 + /// + /// 服务提供器 + /// Sql配置 + /// 数据库信息,用于接入其它数据源,比如EF DbContext + protected SqlQueryBase( IServiceProvider serviceProvider, SqlOptions options, IDatabase database ) { + ServiceProvider = serviceProvider ?? throw new ArgumentNullException( nameof( serviceProvider ) ); + Options = options ?? throw new ArgumentNullException( nameof( options ) ); + Logger = CreateLogger(); + _connection = options.Connection; + _database = database; + ContextId = Id.Create(); + } + + /// + /// 创建日志 + /// + private ILogger CreateLogger() { + var loggerFactory = ServiceProvider.GetService(); + if ( loggerFactory == null ) + return NullLogger.Instance; + return loggerFactory.CreateLogger( Options.LogCategory ); + } + + #endregion + + #region 属性 + + /// + /// 上下文标识 + /// + public string ContextId { get; private set; } + /// + /// 服务提供器 + /// + protected IServiceProvider ServiceProvider { get; } + /// + /// 日志操作 + /// + protected ILogger Logger { get; } + /// + /// 数据库信息 + /// + protected IDatabase Database => _database ??= CreateDatabase(); + /// + /// Sql生成器 + /// + public ISqlBuilder SqlBuilder => _sqlBuilder ??= CreateSqlBuilder(); + /// + /// Sql方言 + /// + public IDialect Dialect => ( (ISqlPartAccessor)SqlBuilder ).Dialect; + /// + /// 参数管理器 + /// + public IParameterManager ParameterManager => ( (ISqlPartAccessor)SqlBuilder ).ParameterManager; + /// + /// 参数字面值解析器 + /// + protected IParamLiteralsResolver ParamLiteralsResolver => _paramLiteralsResolver ??= CreateParamLiteralsResolver(); + /// + /// 起始子句 + /// + public IStartClause StartClause => ( (ISqlPartAccessor)SqlBuilder ).StartClause; + /// + /// Insert子句 + /// + public IFromClause FromClause => ( (ISqlPartAccessor)SqlBuilder ).FromClause; + /// + /// Join子句 + /// + public IJoinClause JoinClause => ( (ISqlPartAccessor)SqlBuilder ).JoinClause; + /// + /// Where子句 + /// + public IWhereClause WhereClause => ( (ISqlPartAccessor)SqlBuilder ).WhereClause; + /// + /// GroupBy子句 + /// + public IInsertClause InsertClause => ( (ISqlPartAccessor)SqlBuilder ).InsertClause; + /// + /// Select子句 + /// + public ISelectClause SelectClause => ( (ISqlPartAccessor)SqlBuilder ).SelectClause; + /// + /// From子句 + /// + public IGroupByClause GroupByClause => ( (ISqlPartAccessor)SqlBuilder ).GroupByClause; + /// + /// OrderBy子句 + /// + public IOrderByClause OrderByClause => ( (ISqlPartAccessor)SqlBuilder ).OrderByClause; + /// + /// 结束子句 + /// + public IEndClause EndClause => ( (ISqlPartAccessor)SqlBuilder ).EndClause; + /// + /// Sql生成器集合 + /// + public ISqlBuilderSet SqlBuilderSet => ( (ISqlPartAccessor)SqlBuilder ).SqlBuilderSet; + /// + /// 获取动态参数列表 + /// + protected DynamicParameters Params { + get { + _parameters = new DynamicParameters(); + ParameterManager.GetParams().ToList().ForEach( t => _parameters.Add( t.Name, t.Value, t.DbType, t.Direction, t.Size, t.Precision, t.Scale ) ); + ParameterManager.GetDynamicParams().ToList().ForEach( _parameters.AddDynamicParams ); + return _parameters; + } + } + + /// + public SqlBuilderResult PreviousSql { get; private set; } + + #endregion + + #region 工厂方法 + + /// + /// 创建Sql生成器 + /// + protected abstract ISqlBuilder CreateSqlBuilder(); + + /// + /// 创建数据库信息 + /// + protected virtual IDatabase CreateDatabase() { + if ( Options.ConnectionString.IsEmpty() ) + throw new InvalidOperationException( UtilDataDapperResource.ConnectionIsEmpty ); + var factory = CreateDatabaseFactory(); + if ( factory == null ) + throw new InvalidOperationException( UtilDataDapperResource.DatabaseFactoryIsNull ); + return factory.Create( Options.ConnectionString ); + } + + /// + /// 创建数据库工厂 + /// + protected abstract IDatabaseFactory CreateDatabaseFactory(); + + /// + /// 创建判断是否存在Sql生成器 + /// + /// Sql生成器 + protected abstract IExistsSqlBuilder CreatExistsSqlBuilder( ISqlBuilder sqlBuilder ); + + /// + /// 创建参数字面值解析器 + /// + protected virtual IParamLiteralsResolver CreateParamLiteralsResolver() { + return new ParamLiteralsResolver(); + } + + #endregion + + #region GetOptions(获取Sql配置) + + /// + /// 获取Sql配置 + /// + public SqlOptions GetOptions() { + return Options; + } + + #endregion + + #region GetParam(获取Sql参数值) + + /// + /// 获取Sql参数值 + /// + /// 参数值类型 + /// 参数名 + public virtual T GetParam( string name ) { + if ( _parameters == null && PreviousSql != null ) + return PreviousSql.GetParam( name ); + return Params.Get( name ); + } + + #endregion + + #region ClearParams(清空Sql参数) + + /// + /// 清空Sql参数 + /// + public void ClearParams() { + ParameterManager.Clear(); + _parameters = null; + } + + #endregion + + #region GetConnection(获取数据库连接) + + /// + /// 获取数据库连接 + /// + public IDbConnection GetConnection() { + if ( _connection != null ) + return _connection; + _connection = Database.GetConnection(); + if ( _connection == null ) + throw new InvalidOperationException( UtilDataDapperResource.ConnectionIsEmpty ); + return _connection; + } + + #endregion + + #region SetConnection(设置数据库连接) + + /// + /// 设置数据库连接 + /// + /// 数据库连接 + public void SetConnection( IDbConnection connection ) { + if ( connection == null ) + return; + _connection = connection; + } + + #endregion + + #region GetTransaction(获取数据库事务) + + /// + /// 获取数据库事务 + /// + public IDbTransaction GetTransaction() { + return _transaction; + } + + #endregion + + #region SetTransaction(设置数据库事务) + + /// + /// 设置数据库事务 + /// + /// 数据库事务 + public void SetTransaction( IDbTransaction transaction ) { + if ( transaction == null ) + return; + _transaction = transaction; + _connection = transaction.Connection; + } + + #endregion + + #region BeginTransaction(开始事务) + + /// + /// 开始事务 + /// + public IDbTransaction BeginTransaction() { + return BeginTransactionImpl( null ); + } + + /// + /// 开始事务 + /// + /// 隔离级别 + public IDbTransaction BeginTransaction( IsolationLevel isolationLevel ) { + return BeginTransactionImpl( isolationLevel ); + } + + /// + /// 开始事务 + /// + private IDbTransaction BeginTransactionImpl( IsolationLevel? isolationLevel ) { + try { + if ( _transaction != null ) + return _transaction; + var connection = GetConnection(); + if ( connection.State == ConnectionState.Closed ) + connection.Open(); + _transaction = isolationLevel == null ? connection.BeginTransaction() : connection.BeginTransaction( isolationLevel.SafeValue() ); + return _transaction; + } + catch { + if ( _connection?.State == ConnectionState.Open ) + _connection?.Close(); + _transaction?.Dispose(); + throw; + } + } + + #endregion + + #region CommitTransaction(提交事务) + + /// + /// 提交事务 + /// + public void CommitTransaction() { + try { + _transaction?.Commit(); + } + catch { + _transaction?.Rollback(); + throw; + } + finally { + if ( _connection?.State == ConnectionState.Open ) + _connection?.Close(); + _transaction?.Dispose(); + _transaction = null; + } + } + + #endregion + + #region RollbackTransaction(回滚事务) + + /// + /// 回滚事务 + /// + public void RollbackTransaction() { + try { + if( _connection.State != ConnectionState.Closed ) + _transaction?.Rollback(); + } + finally { + if ( _connection?.State == ConnectionState.Open ) + _connection?.Close(); + _transaction?.Dispose(); + _transaction = null; + } + } + + #endregion + + #region ExecuteExists(判断是否存在) + + /// + /// 判断是否存在 + /// + public bool ExecuteExists() { + object result = null; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = CreatExistsSqlBuilder( SqlBuilder ).GetSql(); + var connection = GetConnection(); + result = connection.ExecuteScalar( GetSql(), Params, GetTransaction() ); + return Util.Helpers.Convert.ToBool( result ); + } + finally { + ExecuteAfter( result ); + } + } + + #endregion + + #region ExecuteExistsAsync(判断是否存在) + + /// + /// 判断是否存在 + /// + public async Task ExecuteExistsAsync() { + object result = null; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = CreatExistsSqlBuilder( SqlBuilder ).GetSql(); + var connection = GetConnection(); + result = await connection.ExecuteScalarAsync( GetSql(), Params, GetTransaction() ); + return Util.Helpers.Convert.ToBool( result ); + } + finally { + ExecuteAfter( result ); + } + } + + #endregion + + #region ExecuteScalar(获取单值) + + /// + /// 获取单值 + /// + public virtual object ExecuteScalar( int? timeout = null ) { + object result = null; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = connection.ExecuteScalar( GetSql(), Params, GetTransaction(), timeout ); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 获取单值 + /// + public virtual T ExecuteScalar( int? timeout = null ) { + return Util.Helpers.Convert.To( ExecuteScalar( timeout ) ); + } + + #endregion + + #region ExecuteScalarAsync(获取单值) + + /// + /// 获取单值 + /// + public virtual async Task ExecuteScalarAsync( int? timeout = null ) { + object result = null; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = await connection.ExecuteScalarAsync( GetSql(), Params, GetTransaction(), timeout ); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 获取单值 + /// + public virtual async Task ExecuteScalarAsync( int? timeout = null ) { + var result = await ExecuteScalarAsync( timeout ); + return Util.Helpers.Convert.To( result ); + } + + #endregion + + #region ExecuteProcedureScalar(执行存储过程获取单值) + + /// + /// 执行存储过程获取单值 + /// + /// 存储过程 + /// 执行超时时间,单位:秒 + public virtual object ExecuteProcedureScalar( string procedure, int? timeout = null ) { + object result = null; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = connection.ExecuteScalar( GetSql(), Params, GetTransaction(), timeout, GetProcedureCommandType() ); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + + + /// + /// 执行存储过程获取单值 + /// + /// 存储过程 + /// 执行超时时间,单位:秒 + public virtual T ExecuteProcedureScalar( string procedure, int? timeout = null ) { + return Util.Helpers.Convert.To( ExecuteProcedureScalar( procedure, timeout ) ); + } + + #endregion + + #region ExecuteProcedureScalarAsync(执行存储过程获取单值) + + /// + /// 执行存储过程获取单值 + /// + /// 存储过程 + /// 执行超时时间,单位:秒 + public virtual async Task ExecuteProcedureScalarAsync( string procedure, int? timeout = null ) { + object result = null; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = await connection.ExecuteScalarAsync( GetSql(), Params, GetTransaction(), timeout, GetProcedureCommandType() ); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 执行存储过程获取单值 + /// + /// 存储过程 + /// 执行超时时间,单位:秒 + public virtual async Task ExecuteProcedureScalarAsync( string procedure, int? timeout = null ) { + var result = await ExecuteProcedureScalarAsync( procedure, timeout ); + return Util.Helpers.Convert.To( result ); + } + + #endregion + + #region ExecuteSingle(获取单个实体) + + /// + /// 获取单个实体 + /// + public virtual TEntity ExecuteSingle( int? timeout = null ) { + TEntity result = default; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = connection.QueryFirstOrDefault( GetSql(), Params, GetTransaction(), timeout ); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + #endregion + + #region ExecuteSingleAsync(获取单个实体) + + /// + /// 获取单个实体 + /// + public virtual async Task ExecuteSingleAsync( int? timeout = null ) { + TEntity result = default; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = await connection.QueryFirstOrDefaultAsync( GetSql(), Params, GetTransaction(), timeout ); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + #endregion + + #region ExecuteProcedureSingle(执行存储过程获取单个实体) + + /// + /// 执行存储过程获取单个实体 + /// + public virtual TEntity ExecuteProcedureSingle( string procedure, int? timeout = null ) { + TEntity result = default; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = connection.QueryFirstOrDefault( GetSql(), Params, GetTransaction(), timeout, GetProcedureCommandType() ); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + #endregion + + #region ExecuteProcedureSingleAsync(执行存储过程获取单个实体) + + /// + /// 执行存储过程获取单个实体 + /// + public virtual async Task ExecuteProcedureSingleAsync( string procedure, int? timeout = null ) { + TEntity result = default; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = await connection.QueryFirstOrDefaultAsync( GetSql(), Params, GetTransaction(), timeout, GetProcedureCommandType() ); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + #endregion + + #region ExecuteQuery(获取实体集合) + + /// + /// 获取实体集合 + /// + public virtual List ExecuteQuery( int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = connection.Query( GetSql(), Params, GetTransaction(), buffered, timeout ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 获取实体集合 + /// + public virtual List ExecuteQuery( int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = connection.Query( GetSql(), Params, GetTransaction(), buffered, timeout ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 获取实体集合 + /// + public virtual List ExecuteQuery( Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = connection.Query( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 获取实体集合 + /// + public virtual List ExecuteQuery( Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = connection.Query( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 获取实体集合 + /// + public virtual List ExecuteQuery( Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = connection.Query( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 获取实体集合 + /// + public virtual List ExecuteQuery( Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = connection.Query( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 获取实体集合 + /// + public virtual List ExecuteQuery( Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = connection.Query( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 获取实体集合 + /// + public virtual List ExecuteQuery( Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = connection.Query( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + #endregion + + #region ExecuteQueryAsync(获取实体集合) + + /// + /// 获取实体集合 + /// + public virtual async Task> ExecuteQueryAsync( int? timeout = null ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = ( await connection.QueryAsync( GetSql(), Params, GetTransaction(), timeout ) ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 获取实体集合 + /// + public virtual async Task> ExecuteQueryAsync( int? timeout = null ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = ( await connection.QueryAsync( GetSql(), Params, GetTransaction(), timeout ) ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 获取实体集合 + /// + public virtual async Task> ExecuteQueryAsync( Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = ( await connection.QueryAsync( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout ) ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 获取实体集合 + /// + public virtual async Task> ExecuteQueryAsync( Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = ( await connection.QueryAsync( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout ) ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 获取实体集合 + /// + public virtual async Task> ExecuteQueryAsync( Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = ( await connection.QueryAsync( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout ) ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 获取实体集合 + /// + public virtual async Task> ExecuteQueryAsync( Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = ( await connection.QueryAsync( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout ) ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 获取实体集合 + /// + public virtual async Task> ExecuteQueryAsync( Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = ( await connection.QueryAsync( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout ) ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 获取实体集合 + /// + public virtual async Task> ExecuteQueryAsync( Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + var connection = GetConnection(); + result = ( await connection.QueryAsync( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout ) ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + #endregion + + #region ExecuteProcedureQuery(执行存储过程获取实体集合) + + /// + /// 执行存储过程获取实体集合 + /// + public virtual List ExecuteProcedureQuery( string procedure, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = connection.Query( GetSql(), Params, GetTransaction(), buffered, timeout, GetProcedureCommandType() ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 执行存储过程获取实体集合 + /// + public virtual List ExecuteProcedureQuery( string procedure, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = connection.Query( GetSql(), Params, GetTransaction(), buffered, timeout, GetProcedureCommandType() ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 执行存储过程获取实体集合 + /// + public virtual List ExecuteProcedureQuery( string procedure, Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = connection.Query( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout, GetProcedureCommandType() ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 执行存储过程获取实体集合 + /// + public virtual List ExecuteProcedureQuery( string procedure, Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = connection.Query( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout, GetProcedureCommandType() ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 执行存储过程获取实体集合 + /// + public virtual List ExecuteProcedureQuery( string procedure, Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = connection.Query( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout, GetProcedureCommandType() ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 执行存储过程获取实体集合 + /// + public virtual List ExecuteProcedureQuery( string procedure, Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = connection.Query( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout, GetProcedureCommandType() ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 执行存储过程获取实体集合 + /// + public virtual List ExecuteProcedureQuery( string procedure, Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = connection.Query( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout, GetProcedureCommandType() ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 执行存储过程获取实体集合 + /// + public virtual List ExecuteProcedureQuery( string procedure, Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = connection.Query( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout, GetProcedureCommandType() ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + #endregion + + #region ExecuteProcedureQueryAsync(执行存储过程获取实体集合) + + /// + /// 执行存储过程获取实体集合 + /// + public virtual async Task> ExecuteProcedureQueryAsync( string procedure, int? timeout = null ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = ( await connection.QueryAsync( GetSql(), Params, GetTransaction(), timeout, GetProcedureCommandType() ) ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 执行存储过程获取实体集合 + /// + public virtual async Task> ExecuteProcedureQueryAsync( string procedure, int? timeout = null ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = ( await connection.QueryAsync( GetSql(), Params, GetTransaction(), timeout, GetProcedureCommandType() ) ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 执行存储过程获取实体集合 + /// + public virtual async Task> ExecuteProcedureQueryAsync( string procedure, Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = ( await connection.QueryAsync( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout, GetProcedureCommandType() ) ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 执行存储过程获取实体集合 + /// + public virtual async Task> ExecuteProcedureQueryAsync( string procedure, Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = ( await connection.QueryAsync( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout, GetProcedureCommandType() ) ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 执行存储过程获取实体集合 + /// + public virtual async Task> ExecuteProcedureQueryAsync( string procedure, Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = ( await connection.QueryAsync( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout, GetProcedureCommandType() ) ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 执行存储过程获取实体集合 + /// + public virtual async Task> ExecuteProcedureQueryAsync( string procedure, Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = ( await connection.QueryAsync( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout, GetProcedureCommandType() ) ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 执行存储过程获取实体集合 + /// + public virtual async Task> ExecuteProcedureQueryAsync( string procedure, Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = ( await connection.QueryAsync( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout, GetProcedureCommandType() ) ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + /// + /// 执行存储过程获取实体集合 + /// + public virtual async Task> ExecuteProcedureQueryAsync( string procedure, Func map, int? timeout = null, bool buffered = true ) { + List result = default; + try { + if ( ExecuteBefore() == false ) + return default; + _sql = GetProcedure( procedure ); + var connection = GetConnection(); + result = ( await connection.QueryAsync( GetSql(), map, Params, GetTransaction(), buffered, "Id", timeout, GetProcedureCommandType() ) ).ToList(); + return result; + } + finally { + ExecuteAfter( result ); + } + } + + #endregion + + #region 钩子方法 + + /// + /// 执行前操作,返回false停止执行 + /// + protected virtual bool ExecuteBefore() { + return true; + } + + /// + /// 执行后操作 + /// + /// 结果 + protected virtual void ExecuteAfter( object result ) { + SetPreviousSql(); + Clear(); + WriteLog(); + } + + #endregion + + #region GetSql(获取Sql语句) + + /// + /// 获取Sql语句 + /// + public string GetSql() { + return _sql ??= SqlBuilder.GetSql(); + } + + #endregion + + #region SetSql(设置Sql语句) + + /// + /// 设置Sql语句 + /// + /// sql语句或存储过程名 + protected void SetSql( string sql ) { + _sql = sql; + } + + #endregion + + #region GetProcedure(获取存储过程名称) + + /// + /// 获取存储过程名称 + /// + /// 存储过程 + protected virtual string GetProcedure( string procedure ) { + return new TableItem( Dialect, procedure ).ToResult(); + } + + #endregion + + #region GetProcedureCommandType(获取存储过程命令类型) + + /// + /// 获取存储过程命令类型 + /// + protected virtual CommandType GetProcedureCommandType() { + return CommandType.StoredProcedure; + } + + #endregion + + #region SetPreviousSql(设置前一次执行的Sql及参数) + + /// + /// 设置前一次执行的Sql及参数 + /// + protected virtual void SetPreviousSql() { + var sqlParams = GetSqlParams(); + PreviousSql = new SqlBuilderResult( _sql, sqlParams, ParamLiteralsResolver,ParameterManager ); + } + + #endregion + + #region GetSqlParams(获取Sql参数列表) + + /// + /// 获取Sql参数列表 + /// + protected List GetSqlParams() { + var result = new List(); + if ( _parameters == null ) + return result; + foreach ( var parameterName in _parameters.ParameterNames ) { + if ( ParameterManager.Contains( parameterName ) ) { + var param = ParameterManager.GetParam( parameterName ); + if ( param.Direction != ParameterDirection.Input ) + param = new SqlParam( param.Name, _parameters.Get( parameterName ), param.DbType, param.Direction, param.Size, param.Precision, param.Scale ); + result.Add( param ); + continue; + } + var sqlParam = new SqlParam( ParameterManager.NormalizeName( parameterName ), _parameters.Get( parameterName ) ); + result.Add( sqlParam ); + } + return result; + } + + #endregion + + #region Clear(清理) + + /// + /// 清理 + /// + protected void Clear() { + _sql = null; + SqlBuilder.Clear(); + ClearParams(); + } + + #endregion + + #region WriteLog(写日志) + + /// + /// 写日志 + /// + protected virtual void WriteLog() { + if ( Logger.IsEnabled( LogLevel.Trace ) == false ) + return; + var message = new StringBuilder(); + message.AppendLine( "标题: Sql日志" ); + message.AppendLine( "原始Sql:" ); + message.AppendLine( PreviousSql.GetSql() ); + message.AppendLine( "调试Sql:" ); + message.AppendLine( PreviousSql.GetDebugSql() ); + message.AppendLine( "Sql参数:" ); + foreach ( var param in PreviousSql.GetParams() ) { + message.Append( "参数名: " ); + message.Append( param.Name ); + if ( param.Value != null ) { + message.Append( ",参数值: " ); + message.Append( param.Value ); + } + if ( param.DbType != null ) { + message.Append( ",参数类型: " ); + message.Append( param.DbType ); + } + if ( param.Direction != null ) { + message.Append( ",参数方向: " ); + message.Append( param.Direction ); + } + if ( param.Size != null ) { + message.Append( ",Size: " ); + message.Append( param.Size ); + } + if ( param.Precision != null ) { + message.Append( ",Precision: " ); + message.Append( param.Precision ); + } + if ( param.Scale != null ) { + message.Append( ",Scale: " ); + message.Append( param.Scale ); + } + message.AppendLine(); + } + Logger.LogTrace( message.ToString() ); + } + + #endregion + + #region Dispose(释放资源) + + /// + /// 释放资源 + /// + public void Dispose() { + if ( _connection != null ) + _connection.Dispose(); + _transaction = null; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.Core/TypeHandlers/ExtraPropertiesTypeHandler.cs b/src/Util.Data.Dapper.Core/TypeHandlers/ExtraPropertiesTypeHandler.cs new file mode 100644 index 000000000..02e15ccee --- /dev/null +++ b/src/Util.Data.Dapper.Core/TypeHandlers/ExtraPropertiesTypeHandler.cs @@ -0,0 +1,55 @@ +using Util.Domain.Extending; +using Util.SystemTextJson; + +namespace Util.Data.Dapper.TypeHandlers; + +/// +/// 扩展属性类型转换器 +/// +public class ExtraPropertiesTypeHandler : SqlMapper.TypeHandler { + /// + /// 设置值 + /// + /// 参数 + /// 扩展属性值 + public override void SetValue( IDbDataParameter parameter, ExtraPropertyDictionary value ) { + if ( parameter == null ) + return; + if ( value == null ) + return; + parameter.Value = PropertiesToJson( value ); + } + + /// + /// 扩展属性转换为json + /// + private static string PropertiesToJson( ExtraPropertyDictionary extraProperties ) { + var options = new JsonSerializerOptions { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ), + Converters = { + new UtcDateTimeJsonConverter(), + new UtcNullableDateTimeJsonConverter() + } + }; + return Util.Helpers.Json.ToJson( extraProperties, options ); + } + + /// + /// 转换值 + /// + /// json字符串 + public override ExtraPropertyDictionary Parse( object value ) { + return JsonToProperties( value.SafeString() ); + } + + /// + /// json转换为扩展属性 + /// + private static ExtraPropertyDictionary JsonToProperties( string json ) { + if ( json.IsEmpty() || json == "{}" ) + return new ExtraPropertyDictionary(); + var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + return Util.Helpers.Json.ToObject( json, options ) ?? new ExtraPropertyDictionary(); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.Core/Usings.cs b/src/Util.Data.Dapper.Core/Usings.cs new file mode 100644 index 000000000..14a52a9ff --- /dev/null +++ b/src/Util.Data.Dapper.Core/Usings.cs @@ -0,0 +1,14 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Text; +global using System.Data; +global using System.Text.Json; +global using System.Text.Encodings.Web; +global using System.Text.Json.Serialization; +global using System.Text.Unicode; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Logging.Abstractions; +global using Dapper; diff --git a/src/Util.Data.Dapper.MySql/04-Util.Data.Dapper.MySql.csproj b/src/Util.Data.Dapper.MySql/04-Util.Data.Dapper.MySql.csproj new file mode 100644 index 000000000..f471530b8 --- /dev/null +++ b/src/Util.Data.Dapper.MySql/04-Util.Data.Dapper.MySql.csproj @@ -0,0 +1,37 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Data.Dapper.MySql + Util.Data.Dapper + Util.Data.Dapper.MySql是Util应用框架基于Dapper的数据访问封装类库,用于处理MySql数据库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Data.Dapper.MySql.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Data.Dapper.MySql.xml + + + + + True + False + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Util.Data.Dapper.MySql/Metadata/MySqlMetadataService.cs b/src/Util.Data.Dapper.MySql/Metadata/MySqlMetadataService.cs new file mode 100644 index 000000000..e8b3ed1fe --- /dev/null +++ b/src/Util.Data.Dapper.MySql/Metadata/MySqlMetadataService.cs @@ -0,0 +1,77 @@ +using Util.Data.Metadata; +using Util.Data.Sql; + +namespace Util.Data.Dapper.Metadata; + +/// +/// MySql数据库元数据服务 +/// +public class MySqlMetadataService : IMetadataService { + /// + /// Sql查询对象 + /// + private readonly ISqlQuery _sqlQuery; + + /// + /// 初始化MySql数据库元数据服务 + /// + /// Sql查询对象 + public MySqlMetadataService( ISqlQuery sqlQuery ) { + _sqlQuery = sqlQuery ?? throw new ArgumentNullException( nameof( sqlQuery ) ); + } + + /// + /// 获取数据库信息 + /// + public async Task GetDatabaseInfoAsync() { + var result = await GetDatabase(); + var tables = await GetTables(); + result.Tables.AddRange( tables ); + return result; + } + + /// + /// 获取数据库信息 + /// + private async Task GetDatabase() { + return await _sqlQuery + .AppendSelect( "Database() [Id],Database() [Name]" ) + .ToEntityAsync(); + } + + /// + /// 获取表信息 + /// + private async Task> GetTables() { + SetGetTablesSql(); + var dic = new Dictionary(); + var tables = ( await _sqlQuery.ToListAsync( ( table, column ) => { + if ( table == null || column == null ) + return null; + dic.TryAdd( table.Id, table ); + TableInfo result = dic[table.Id]; + result.Columns.Add( column ); + return result; + } ) ).Distinct().ToList(); + return tables; + } + + /// + /// 设置获取表信息Sql + /// + private void SetGetTablesSql() { + _sqlQuery + .Select( "t.Table_Name Id,t.Table_Name Name,t.Table_Comment Comment" ) + .Select( "c.Column_Name Id,c.Column_Name Name,c.Column_Comment Comment" ) + .Select( "c.Data_Type DataType,c.Numeric_Precision Precision,c.Numeric_Scale Scale" ) + .AppendSelect( ",( Case When c.Column_Key = 'PRI' Then 1 Else 0 End ) IsPrimaryKey" ) + .AppendSelect( ",( Case When c.Extra = 'auto_increment' Then 1 Else 0 End ) AS IsAutoIncrement" ) + .AppendSelect( ",( Case When c.Is_Nullable = 'NO' Then 0 Else 1 End ) AS IsNullable" ) + .AppendSelect( ",( Case When c.Column_Type = 'tinyint(1)' Then 1 " ) + .AppendSelect( "When c.Numeric_Precision Is Not Null Then c.Numeric_Precision " ) + .AppendSelect( "When c.Character_Maximum_Length Is Not Null Then c.Character_Maximum_Length Else Null End) Length" ) + .From( "Information_Schema.Tables t" ) + .Join( "Information_Schema.Columns c" ).On( "t.Table_Schema", "c.Table_Schema" ).On( "t.Table_Name", "c.Table_Name" ) + .AppendWhere( "[t].[Table_Schema] = Database()" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.MySql/Metadata/MySqlTypeConverter.cs b/src/Util.Data.Dapper.MySql/Metadata/MySqlTypeConverter.cs new file mode 100644 index 000000000..4af6e13c0 --- /dev/null +++ b/src/Util.Data.Dapper.MySql/Metadata/MySqlTypeConverter.cs @@ -0,0 +1,56 @@ +using Util.Data.Metadata; + +namespace Util.Data.Dapper.Metadata; + +/// +/// MySql数据类型转换器 +/// +public class MySqlTypeConverter : ITypeConverter { + /// + public DbType? ToType( string dataType, int? length = null ) { + if ( dataType.IsEmpty() ) + return null; + switch ( dataType.ToLower() ) { + case "char": + return length == 36 ? DbType.Guid : DbType.String; + case "varchar": + case "tinytext": + case "mediumtext": + case "longtext": + case "text": + return DbType.String; + case "tinyint": + return length == 1 ? DbType.Boolean : DbType.Byte; + case "bit": + return DbType.Boolean; + case "smallint": + return DbType.Int16; + case "integer": + case "int": + case "mediumint": + return DbType.Int32; + case "bigint": + return DbType.Int64; + case "float": + return DbType.Single; + case "double": + return DbType.Double; + case "decimal": + case "numeric": + return DbType.Decimal; + case "date": + return DbType.Date; + case "time": + return DbType.Time; + case "datetime": + case "timestamp": + return DbType.DateTime; + case "tinyblob": + case "mediumblob": + case "longblob": + case "blob": + return DbType.Binary; + } + throw new NotImplementedException( dataType ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.MySql/Sql/AppBuilderExtensions.cs b/src/Util.Data.Dapper.MySql/Sql/AppBuilderExtensions.cs new file mode 100644 index 000000000..2b185c971 --- /dev/null +++ b/src/Util.Data.Dapper.MySql/Sql/AppBuilderExtensions.cs @@ -0,0 +1,127 @@ +using Util.Configs; +using Util.Data.Sql; + +namespace Util.Data.Dapper.Sql; + +/// +/// MySql数据库操作扩展 +/// +public static class AppBuilderExtensions { + + #region AddMySqlQuery(配置MySql Sql查询对象) + + /// + /// 配置MySql Sql查询对象 + /// + /// 应用生成器 + public static IAppBuilder AddMySqlQuery( this IAppBuilder builder ) { + return builder.AddMySqlQuery( "" ); + } + + /// + /// 配置MySql Sql查询对象 + /// + /// 应用生成器 + /// 数据库连接字符串 + public static IAppBuilder AddMySqlQuery( this IAppBuilder builder, string connection ) { + return builder.AddMySqlQuery( connection ); + } + + /// + /// 配置MySql Sql查询对象 + /// + /// 应用生成器 + /// 配置操作 + public static IAppBuilder AddMySqlQuery( this IAppBuilder builder, Action setupAction ) { + return builder.AddMySqlQuery( setupAction ); + } + + /// + /// 配置MySql Sql查询对象 + /// + /// 应用生成器 + /// 数据库连接字符串 + public static IAppBuilder AddMySqlQuery( this IAppBuilder builder, string connection ) + where TService : ISqlQuery + where TImplementation : MySqlQueryBase, TService { + return builder.AddMySqlQuery( t => t.ConnectionString( connection ) ); + } + + /// + /// 配置MySql Sql查询对象 + /// + /// 应用生成器 + /// 配置操作 + public static IAppBuilder AddMySqlQuery( this IAppBuilder builder, Action setupAction ) + where TService : ISqlQuery + where TImplementation : MySqlQueryBase, TService { + var options = new SqlOptions(); + setupAction?.Invoke( options ); + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + services.TryAddTransient( typeof( TService ), typeof( TImplementation ) ); + services.TryAddSingleton( typeof( SqlOptions ), ( sp ) => options ); + } ); + return builder; + } + + #endregion + + #region AddMySqlExecutor(配置MySql Sql执行器) + + /// + /// 配置MySql Sql执行器 + /// + /// 应用生成器 + public static IAppBuilder AddMySqlExecutor( this IAppBuilder builder ) { + return builder.AddMySqlExecutor( "" ); + } + + /// + /// 配置MySql Sql执行器 + /// + /// 应用生成器 + /// 数据库连接字符串 + public static IAppBuilder AddMySqlExecutor( this IAppBuilder builder, string connection ) { + return builder.AddMySqlExecutor( connection ); + } + + /// + /// 配置MySql Sql执行器 + /// + /// 应用生成器 + /// 配置操作 + public static IAppBuilder AddMySqlExecutor( this IAppBuilder builder, Action setupAction ) { + return builder.AddMySqlExecutor( setupAction ); + } + + /// + /// 配置MySql Sql执行器 + /// + /// 应用生成器 + /// 数据库连接字符串 + public static IAppBuilder AddMySqlExecutor( this IAppBuilder builder, string connection ) + where TService : ISqlExecutor + where TImplementation : MySqlExecutorBase, TService { + return builder.AddMySqlExecutor( t => t.ConnectionString( connection ) ); + } + + /// + /// 配置MySql Sql执行器 + /// + /// 应用生成器 + /// 配置操作 + public static IAppBuilder AddMySqlExecutor( this IAppBuilder builder, Action setupAction ) + where TService : ISqlExecutor + where TImplementation : MySqlExecutorBase, TService { + var options = new SqlOptions(); + setupAction?.Invoke( options ); + builder.Host.ConfigureServices( ( context, services ) => { + services.TryAddTransient( typeof( TService ), typeof( TImplementation ) ); + services.TryAddSingleton( typeof( SqlOptions ), ( sp ) => options ); + } ); + return builder; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.MySql/Sql/Builders/MySqlBuilder.cs b/src/Util.Data.Dapper.MySql/Sql/Builders/MySqlBuilder.cs new file mode 100644 index 000000000..b18cdfd28 --- /dev/null +++ b/src/Util.Data.Dapper.MySql/Sql/Builders/MySqlBuilder.cs @@ -0,0 +1,41 @@ +using Util.Data.Sql; +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Caches; +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Dapper.Sql.Builders; + +/// +/// MySql Sql生成器 +/// +public class MySqlBuilder : SqlBuilderBase { + /// + /// 初始化MySql Sql生成器 + /// + /// Sql参数管理器 + public MySqlBuilder( IParameterManager parameterManager = null ) + : base( parameterManager ) { + } + + /// + protected override IDialect CreateDialect() { + return MySqlDialect.Instance; + } + + /// + protected override IColumnCache CreateColumnCache() { + return MySqlColumnCache.Instance; + } + + /// + public override ISqlBuilder New() { + return new MySqlBuilder( ParameterManager ); + } + + /// + public override ISqlBuilder Clone() { + var result = new MySqlBuilder(); + result.Clone( this ); + return result; + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.MySql/Sql/Builders/MySqlColumnCache.cs b/src/Util.Data.Dapper.MySql/Sql/Builders/MySqlColumnCache.cs new file mode 100644 index 000000000..d4cf52628 --- /dev/null +++ b/src/Util.Data.Dapper.MySql/Sql/Builders/MySqlColumnCache.cs @@ -0,0 +1,35 @@ +using Util.Data.Sql.Builders.Caches; + +namespace Util.Data.Dapper.Sql.Builders; + +/// +/// MySql列缓存 +/// +public class MySqlColumnCache : ColumnCacheBase { + /// + /// 列名缓存 + /// + private readonly ConcurrentDictionary _cache; + + /// + /// 封闭构造方法 + /// + private MySqlColumnCache() : base( MySqlDialect.Instance ) { + _cache = new ConcurrentDictionary(); + } + + /// + /// MySql列缓存实例 + /// + public static readonly IColumnCache Instance = new MySqlColumnCache(); + + /// + public override string GetSafeColumns( string columns ) { + return _cache.GetOrAdd( columns.GetHashCode(), key => NormalizeColumns( columns ) ); + } + + /// + public override string GetSafeColumn( string column ) { + return _cache.GetOrAdd( column.GetHashCode(), key => NormalizeColumn( column ) ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.MySql/Sql/Builders/MySqlDialect.cs b/src/Util.Data.Dapper.MySql/Sql/Builders/MySqlDialect.cs new file mode 100644 index 000000000..b86f68b67 --- /dev/null +++ b/src/Util.Data.Dapper.MySql/Sql/Builders/MySqlDialect.cs @@ -0,0 +1,35 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Core; + +namespace Util.Data.Dapper.Sql.Builders; + +/// +/// MySql方言 +/// +public class MySqlDialect : DialectBase { + /// + /// 封闭构造方法 + /// + private MySqlDialect() { + } + + /// + /// MySql方言实例 + /// + public static readonly IDialect Instance = new MySqlDialect(); + + /// + public override string GetOpeningIdentifier() { + return "`"; + } + + /// + public override string GetClosingIdentifier() { + return "`"; + } + + /// + public override string GetPrefix() { + return "@"; + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.MySql/Sql/Builders/MySqlExistsSqlBuilder.cs b/src/Util.Data.Dapper.MySql/Sql/Builders/MySqlExistsSqlBuilder.cs new file mode 100644 index 000000000..ea454bb59 --- /dev/null +++ b/src/Util.Data.Dapper.MySql/Sql/Builders/MySqlExistsSqlBuilder.cs @@ -0,0 +1,53 @@ +using Util.Data.Sql; +using Util.Data.Sql.Builders; + +namespace Util.Data.Dapper.Sql.Builders; + +/// +/// 判断是否存在Sql生成器 +/// +public class MySqlExistsSqlBuilder : IExistsSqlBuilder { + /// + /// Sql生成器 + /// + private readonly ISqlBuilder _sqlBuilder; + + /// + /// 初始化判断是否存在Sql生成器 + /// + /// Sql生成器 + public MySqlExistsSqlBuilder( ISqlBuilder sqlBuilder ) { + _sqlBuilder = sqlBuilder; + } + + /// + /// 获取Sql + /// + public string GetSql() { + InitSelect(); + return GetResult(); + } + + /// + /// 将Select子句初始化为1 + /// + private void InitSelect() { + _sqlBuilder.ClearSelect(); + _sqlBuilder.AppendSelect( "1" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = new StringBuilder(); + result.AppendLine( "Select Case" ); + result.AppendLine( " When Exists (" ); + _sqlBuilder.AppendTo( result ); + result.AppendLine( ")" ); + result.AppendLine( " Then true" ); + result.AppendLine( " Else false " ); + result.Append( "End" ); + return result.ToString(); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.MySql/Sql/MySqlDatabaseFactory.cs b/src/Util.Data.Dapper.MySql/Sql/MySqlDatabaseFactory.cs new file mode 100644 index 000000000..1fd171fb4 --- /dev/null +++ b/src/Util.Data.Dapper.MySql/Sql/MySqlDatabaseFactory.cs @@ -0,0 +1,14 @@ +namespace Util.Data.Dapper.Sql; + +/// +/// MySql数据库工厂 +/// +public class MySqlDatabaseFactory : IDatabaseFactory { + /// + /// 创建数据库信息 + /// + /// 数据库连接字符串 + public IDatabase Create( string connection ) { + return new DefaultDatabase( new MySqlConnection( connection ) ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.MySql/Sql/MySqlExecutor.cs b/src/Util.Data.Dapper.MySql/Sql/MySqlExecutor.cs new file mode 100644 index 000000000..01a46a7b2 --- /dev/null +++ b/src/Util.Data.Dapper.MySql/Sql/MySqlExecutor.cs @@ -0,0 +1,18 @@ +using Util.Data.Sql; + +namespace Util.Data.Dapper.Sql; + +/// +/// MySql Sql执行器 +/// +public class MySqlExecutor : MySqlExecutorBase { + /// + /// 初始化MySql Sql执行器 + /// + /// 服务提供器 + /// Sql配置 + /// 数据库信息,用于接入其它数据源,比如EF DbContext + public MySqlExecutor( IServiceProvider serviceProvider, SqlOptions options, IDatabase database = null ) + : base( serviceProvider, options, database ) { + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.MySql/Sql/MySqlExecutorBase.cs b/src/Util.Data.Dapper.MySql/Sql/MySqlExecutorBase.cs new file mode 100644 index 000000000..5f197dc7b --- /dev/null +++ b/src/Util.Data.Dapper.MySql/Sql/MySqlExecutorBase.cs @@ -0,0 +1,40 @@ +using Util.Data.Dapper.Sql.Builders; +using Util.Data.Sql; +using Util.Data.Sql.Builders; + +namespace Util.Data.Dapper.Sql; + +/// +/// MySql Sql执行器 +/// +public abstract class MySqlExecutorBase : SqlExecutorBase { + /// + /// 初始化MySql Sql执行器 + /// + /// 服务提供器 + /// Sql配置 + /// 数据库信息,用于接入其它数据源,比如EF DbContext + protected MySqlExecutorBase( IServiceProvider serviceProvider, SqlOptions options, IDatabase database ) : base( serviceProvider, options, database ) { + } + + /// + /// 创建Sql生成器 + /// + protected override ISqlBuilder CreateSqlBuilder() { + return new MySqlBuilder(); + } + + /// + /// 创建判断是否存在Sql生成器 + /// + protected override IExistsSqlBuilder CreatExistsSqlBuilder( ISqlBuilder sqlBuilder ) { + return new MySqlExistsSqlBuilder( sqlBuilder ); + } + + /// + /// 创建数据库工厂 + /// + protected override IDatabaseFactory CreateDatabaseFactory() { + return new MySqlDatabaseFactory(); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.MySql/Sql/MySqlQuery.cs b/src/Util.Data.Dapper.MySql/Sql/MySqlQuery.cs new file mode 100644 index 000000000..b728c3a6d --- /dev/null +++ b/src/Util.Data.Dapper.MySql/Sql/MySqlQuery.cs @@ -0,0 +1,18 @@ +using Util.Data.Sql; + +namespace Util.Data.Dapper.Sql; + +/// +/// MySql Sql查询对象 +/// +public class MySqlQuery : MySqlQueryBase { + /// + /// 初始化MySql Sql查询对象 + /// + /// 服务提供器 + /// Sql配置 + /// 数据库信息,用于接入其它数据源,比如EF DbContext + public MySqlQuery( IServiceProvider serviceProvider, SqlOptions options, IDatabase database = null ) + : base( serviceProvider, options, database ) { + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.MySql/Sql/MySqlQueryBase.cs b/src/Util.Data.Dapper.MySql/Sql/MySqlQueryBase.cs new file mode 100644 index 000000000..ff272c6bb --- /dev/null +++ b/src/Util.Data.Dapper.MySql/Sql/MySqlQueryBase.cs @@ -0,0 +1,34 @@ +using Util.Data.Dapper.Sql.Builders; +using Util.Data.Sql; +using Util.Data.Sql.Builders; + +namespace Util.Data.Dapper.Sql; + +/// +/// MySql Sql查询对象 +/// +public abstract class MySqlQueryBase : SqlQueryBase { + /// + /// 初始化MySql Sql查询对象 + /// + /// 服务提供器 + /// Sql配置 + /// 数据库信息,用于接入其它数据源,比如EF DbContext + protected MySqlQueryBase( IServiceProvider serviceProvider, SqlOptions options, IDatabase database ) : base( serviceProvider, options, database ) { + } + + /// + protected override ISqlBuilder CreateSqlBuilder() { + return new MySqlBuilder(); + } + + /// + protected override IExistsSqlBuilder CreatExistsSqlBuilder( ISqlBuilder sqlBuilder ) { + return new MySqlExistsSqlBuilder( sqlBuilder ); + } + + /// + protected override IDatabaseFactory CreateDatabaseFactory() { + return new MySqlDatabaseFactory(); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.MySql/Usings.cs b/src/Util.Data.Dapper.MySql/Usings.cs new file mode 100644 index 000000000..75d67f651 --- /dev/null +++ b/src/Util.Data.Dapper.MySql/Usings.cs @@ -0,0 +1,9 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Text; +global using System.Data; +global using System.Collections.Concurrent; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using MySqlConnector; diff --git a/src/Util.Data.Dapper.Oracle/05-Util.Data.Dapper.Oracle.csproj b/src/Util.Data.Dapper.Oracle/05-Util.Data.Dapper.Oracle.csproj new file mode 100644 index 000000000..be0366b06 --- /dev/null +++ b/src/Util.Data.Dapper.Oracle/05-Util.Data.Dapper.Oracle.csproj @@ -0,0 +1,37 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Data.Dapper.Oracle + Util.Data.Dapper + Util.Data.Dapper.Oracle是Util应用框架基于Dapper的数据访问封装类库,用于处理Oracle数据库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Data.Dapper.Oracle.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Data.Dapper.Oracle.xml + + + + + True + False + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Util.Data.Dapper.Oracle/Sql/AppBuilderExtensions.cs b/src/Util.Data.Dapper.Oracle/Sql/AppBuilderExtensions.cs new file mode 100644 index 000000000..a9a452588 --- /dev/null +++ b/src/Util.Data.Dapper.Oracle/Sql/AppBuilderExtensions.cs @@ -0,0 +1,139 @@ +using Util.Configs; +using Util.Data.Dapper.TypeHandlers; +using Util.Data.Sql; + +namespace Util.Data.Dapper.Sql; + +/// +/// Oracle数据库操作扩展 +/// +public static class AppBuilderExtensions { + + #region AddOracleSqlQuery(配置Oracle Sql查询对象) + + /// + /// 配置Oracle Sql查询对象 + /// + /// 应用生成器 + public static IAppBuilder AddOracleSqlQuery( this IAppBuilder builder ) { + return builder.AddOracleSqlQuery( "" ); + } + + /// + /// 配置Oracle Sql查询对象 + /// + /// 应用生成器 + /// 数据库连接字符串 + public static IAppBuilder AddOracleSqlQuery( this IAppBuilder builder, string connection ) { + return builder.AddOracleSqlQuery( connection ); + } + + /// + /// 配置Oracle Sql查询对象 + /// + /// 应用生成器 + /// 配置操作 + public static IAppBuilder AddOracleSqlQuery( this IAppBuilder builder, Action setupAction ) { + return builder.AddOracleSqlQuery( setupAction ); + } + + /// + /// 配置Oracle Sql查询对象 + /// + /// 应用生成器 + /// 数据库连接字符串 + public static IAppBuilder AddOracleSqlQuery( this IAppBuilder builder, string connection ) + where TService : ISqlQuery + where TImplementation : OracleSqlQueryBase, TService { + return builder.AddOracleSqlQuery( t => t.ConnectionString( connection ) ); + } + + /// + /// 配置Oracle Sql查询对象 + /// + /// 应用生成器 + /// 配置操作 + public static IAppBuilder AddOracleSqlQuery( this IAppBuilder builder, Action setupAction ) + where TService : ISqlQuery + where TImplementation : OracleSqlQueryBase, TService { + var options = new SqlOptions(); + setupAction?.Invoke( options ); + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + services.TryAddTransient( typeof( TService ), typeof( TImplementation ) ); + services.TryAddSingleton( typeof( SqlOptions ), ( sp ) => options ); + RegisterGuidTypeHandler(); + } ); + return builder; + } + + /// + /// 注册Guid类型处理器 + /// + private static void RegisterGuidTypeHandler() { + SqlMapper.RemoveTypeMap( typeof( Guid ) ); + SqlMapper.RemoveTypeMap( typeof( Guid? ) ); + SqlMapper.AddTypeHandler( typeof( Guid ), new GuidTypeHandler() ); + } + + #endregion + + #region AddOracleSqlExecutor(配置Oracle Sql执行器) + + /// + /// 配置Oracle Sql执行器 + /// + /// 应用生成器 + public static IAppBuilder AddOracleSqlExecutor( this IAppBuilder builder ) { + return builder.AddOracleSqlExecutor( "" ); + } + + /// + /// 配置Oracle Sql执行器 + /// + /// 应用生成器 + /// 数据库连接字符串 + public static IAppBuilder AddOracleSqlExecutor( this IAppBuilder builder, string connection ) { + return builder.AddOracleSqlExecutor( connection ); + } + + /// + /// 配置Oracle Sql执行器 + /// + /// 应用生成器 + /// 配置操作 + public static IAppBuilder AddOracleSqlExecutor( this IAppBuilder builder, Action setupAction ) { + return builder.AddOracleSqlExecutor( setupAction ); + } + + /// + /// 配置Oracle Sql执行器 + /// + /// 应用生成器 + /// 数据库连接字符串 + public static IAppBuilder AddOracleSqlExecutor( this IAppBuilder builder, string connection ) + where TService : ISqlExecutor + where TImplementation : OracleSqlExecutorBase, TService { + return builder.AddOracleSqlExecutor( t => t.ConnectionString( connection ) ); + } + + /// + /// 配置Oracle Sql执行器 + /// + /// 应用生成器 + /// 配置操作 + public static IAppBuilder AddOracleSqlExecutor( this IAppBuilder builder, Action setupAction ) + where TService : ISqlExecutor + where TImplementation : OracleSqlExecutorBase, TService { + var options = new SqlOptions(); + setupAction?.Invoke( options ); + builder.Host.ConfigureServices( ( context, services ) => { + services.TryAddTransient( typeof( TService ), typeof( TImplementation ) ); + services.TryAddSingleton( typeof( SqlOptions ), ( sp ) => options ); + RegisterGuidTypeHandler(); + } ); + return builder; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.Oracle/Sql/Builders/OracleColumnCache.cs b/src/Util.Data.Dapper.Oracle/Sql/Builders/OracleColumnCache.cs new file mode 100644 index 000000000..a41defad7 --- /dev/null +++ b/src/Util.Data.Dapper.Oracle/Sql/Builders/OracleColumnCache.cs @@ -0,0 +1,35 @@ +using Util.Data.Sql.Builders.Caches; + +namespace Util.Data.Dapper.Sql.Builders; + +/// +/// Oracle列缓存 +/// +public class OracleColumnCache : ColumnCacheBase { + /// + /// 列名缓存 + /// + private readonly ConcurrentDictionary _cache; + + /// + /// 封闭构造方法 + /// + private OracleColumnCache() : base( OracleDialect.Instance ) { + _cache = new ConcurrentDictionary(); + } + + /// + /// Oracle列缓存实例 + /// + public static readonly IColumnCache Instance = new OracleColumnCache(); + + /// + public override string GetSafeColumns( string columns ) { + return _cache.GetOrAdd( columns.GetHashCode(), key => NormalizeColumns( columns ) ); + } + + /// + public override string GetSafeColumn( string column ) { + return _cache.GetOrAdd( column.GetHashCode(), key => NormalizeColumn( column ) ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.Oracle/Sql/Builders/OracleDialect.cs b/src/Util.Data.Dapper.Oracle/Sql/Builders/OracleDialect.cs new file mode 100644 index 000000000..9824a1511 --- /dev/null +++ b/src/Util.Data.Dapper.Oracle/Sql/Builders/OracleDialect.cs @@ -0,0 +1,35 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Core; + +namespace Util.Data.Dapper.Sql.Builders; + +/// +/// Oracle方言 +/// +public class OracleDialect : DialectBase { + /// + /// 封闭构造方法 + /// + private OracleDialect() { + } + + /// + /// Oracle方言实例 + /// + public static readonly IDialect Instance = new OracleDialect(); + + /// + public override string GetOpeningIdentifier() { + return "\""; + } + + /// + public override string GetClosingIdentifier() { + return "\""; + } + + /// + public override string GetPrefix() { + return ":"; + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.Oracle/Sql/Builders/OracleExistsSqlBuilder.cs b/src/Util.Data.Dapper.Oracle/Sql/Builders/OracleExistsSqlBuilder.cs new file mode 100644 index 000000000..11769a773 --- /dev/null +++ b/src/Util.Data.Dapper.Oracle/Sql/Builders/OracleExistsSqlBuilder.cs @@ -0,0 +1,53 @@ +using Util.Data.Sql; +using Util.Data.Sql.Builders; + +namespace Util.Data.Dapper.Sql.Builders; + +/// +/// 判断是否存在Sql生成器 +/// +public class OracleExistsSqlBuilder : IExistsSqlBuilder { + /// + /// Sql生成器 + /// + private readonly ISqlBuilder _sqlBuilder; + + /// + /// 初始化判断是否存在Sql生成器 + /// + /// Sql生成器 + public OracleExistsSqlBuilder( ISqlBuilder sqlBuilder ) { + _sqlBuilder = sqlBuilder; + } + + /// + /// 获取Sql + /// + public string GetSql() { + InitSelect(); + return GetResult(); + } + + /// + /// 将Select子句初始化为1 + /// + private void InitSelect() { + _sqlBuilder.ClearSelect(); + _sqlBuilder.AppendSelect( "1" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = new StringBuilder(); + result.AppendLine( "Select Case" ); + result.AppendLine( " When Exists (" ); + _sqlBuilder.AppendTo( result ); + result.AppendLine( ")" ); + result.AppendLine( " Then 1" ); + result.AppendLine( " Else 0 " ); + result.Append( "End" ); + return result.ToString(); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.Oracle/Sql/Builders/OracleParameterManager.cs b/src/Util.Data.Dapper.Oracle/Sql/Builders/OracleParameterManager.cs new file mode 100644 index 000000000..38a15df09 --- /dev/null +++ b/src/Util.Data.Dapper.Oracle/Sql/Builders/OracleParameterManager.cs @@ -0,0 +1,30 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Dapper.Sql.Builders; + +/// +/// Oracle Sql参数管理器 +/// +public class OracleParameterManager : ParameterManager { + /// + /// 初始化Oracle Sql参数管理器 + /// + /// Sql方言 + public OracleParameterManager( IDialect dialect ) : base( dialect ) { + } + + /// + /// 初始化Oracle Sql参数管理器 + /// + /// Sql参数管理器 + public OracleParameterManager( ParameterManager manager ) : base( manager ) { + } + + /// + public override string GenerateName() { + var result = $"{Dialect.GetPrefix()}p_{ParamIndex}"; + ParamIndex++; + return result; + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.Oracle/Sql/Builders/OracleSqlBuilder.cs b/src/Util.Data.Dapper.Oracle/Sql/Builders/OracleSqlBuilder.cs new file mode 100644 index 000000000..b31053e96 --- /dev/null +++ b/src/Util.Data.Dapper.Oracle/Sql/Builders/OracleSqlBuilder.cs @@ -0,0 +1,46 @@ +using Util.Data.Sql; +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Caches; +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Dapper.Sql.Builders; + +/// +/// Oracle Sql生成器 +/// +public class OracleSqlBuilder : SqlBuilderBase { + /// + /// 初始化Oracle Sql生成器 + /// + /// Sql参数管理器 + public OracleSqlBuilder( IParameterManager parameterManager = null ) + : base( parameterManager ) { + } + + /// + protected override IDialect CreateDialect() { + return OracleDialect.Instance; + } + + /// + protected override IColumnCache CreateColumnCache() { + return OracleColumnCache.Instance; + } + + /// + public override ISqlBuilder New() { + return new OracleSqlBuilder( ParameterManager ); + } + + /// + protected override IParameterManager CreateParameterManager() { + return new OracleParameterManager( Dialect ); + } + + /// + public override ISqlBuilder Clone() { + var result = new OracleSqlBuilder(); + result.Clone( this ); + return result; + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.Oracle/Sql/OracleDatabaseFactory.cs b/src/Util.Data.Dapper.Oracle/Sql/OracleDatabaseFactory.cs new file mode 100644 index 000000000..6b5d1d2db --- /dev/null +++ b/src/Util.Data.Dapper.Oracle/Sql/OracleDatabaseFactory.cs @@ -0,0 +1,15 @@ +namespace Util.Data.Dapper.Sql; + +/// +/// Oracle数据库工厂 +/// +public class OracleDatabaseFactory : IDatabaseFactory { + /// + /// 创建数据库信息 + /// + /// 数据库连接字符串 + public IDatabase Create( string connection ) { + var con = new OracleConnection( connection ); + return new DefaultDatabase( con ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.Oracle/Sql/OracleSqlExecutor.cs b/src/Util.Data.Dapper.Oracle/Sql/OracleSqlExecutor.cs new file mode 100644 index 000000000..2158c7d50 --- /dev/null +++ b/src/Util.Data.Dapper.Oracle/Sql/OracleSqlExecutor.cs @@ -0,0 +1,18 @@ +using Util.Data.Sql; + +namespace Util.Data.Dapper.Sql; + +/// +/// Oracle Sql执行器 +/// +public class OracleSqlExecutor : OracleSqlExecutorBase { + /// + /// 初始化Oracle Sql执行器 + /// + /// 服务提供器 + /// Sql配置 + /// 数据库信息,用于接入其它数据源,比如EF DbContext + public OracleSqlExecutor( IServiceProvider serviceProvider, SqlOptions options, IDatabase database = null ) + : base( serviceProvider, options, database ) { + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.Oracle/Sql/OracleSqlExecutorBase.cs b/src/Util.Data.Dapper.Oracle/Sql/OracleSqlExecutorBase.cs new file mode 100644 index 000000000..9d59bc1dc --- /dev/null +++ b/src/Util.Data.Dapper.Oracle/Sql/OracleSqlExecutorBase.cs @@ -0,0 +1,40 @@ +using Util.Data.Dapper.Sql.Builders; +using Util.Data.Sql; +using Util.Data.Sql.Builders; + +namespace Util.Data.Dapper.Sql; + +/// +/// Oracle Sql执行器 +/// +public abstract class OracleSqlExecutorBase : SqlExecutorBase { + /// + /// 初始化Oracle Sql执行器 + /// + /// 服务提供器 + /// Sql配置 + /// 数据库信息,用于接入其它数据源,比如EF DbContext + protected OracleSqlExecutorBase( IServiceProvider serviceProvider, SqlOptions options, IDatabase database ) : base( serviceProvider, options, database ) { + } + + /// + /// 创建Sql生成器 + /// + protected override ISqlBuilder CreateSqlBuilder() { + return new OracleSqlBuilder(); + } + + /// + /// 创建判断是否存在Sql生成器 + /// + protected override IExistsSqlBuilder CreatExistsSqlBuilder( ISqlBuilder sqlBuilder ) { + return new OracleExistsSqlBuilder( sqlBuilder ); + } + + /// + /// 创建数据库工厂 + /// + protected override IDatabaseFactory CreateDatabaseFactory() { + return new OracleDatabaseFactory(); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.Oracle/Sql/OracleSqlQuery.cs b/src/Util.Data.Dapper.Oracle/Sql/OracleSqlQuery.cs new file mode 100644 index 000000000..93067a5a0 --- /dev/null +++ b/src/Util.Data.Dapper.Oracle/Sql/OracleSqlQuery.cs @@ -0,0 +1,18 @@ +using Util.Data.Sql; + +namespace Util.Data.Dapper.Sql; + +/// +/// Oracle Sql查询对象 +/// +public class OracleSqlQuery : OracleSqlQueryBase { + /// + /// 初始化Oracle Sql查询对象 + /// + /// 服务提供器 + /// Sql配置 + /// 数据库信息,用于接入其它数据源,比如EF DbContext + public OracleSqlQuery( IServiceProvider serviceProvider, SqlOptions options, IDatabase database = null ) + : base( serviceProvider, options, database ) { + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.Oracle/Sql/OracleSqlQueryBase.cs b/src/Util.Data.Dapper.Oracle/Sql/OracleSqlQueryBase.cs new file mode 100644 index 000000000..3e074ab51 --- /dev/null +++ b/src/Util.Data.Dapper.Oracle/Sql/OracleSqlQueryBase.cs @@ -0,0 +1,34 @@ +using Util.Data.Dapper.Sql.Builders; +using Util.Data.Sql; +using Util.Data.Sql.Builders; + +namespace Util.Data.Dapper.Sql; + +/// +/// Oracle Sql查询对象 +/// +public abstract class OracleSqlQueryBase : SqlQueryBase { + /// + /// 初始化Oracle Sql查询对象 + /// + /// 服务提供器 + /// Sql配置 + /// 数据库信息,用于接入其它数据源,比如EF DbContext + protected OracleSqlQueryBase( IServiceProvider serviceProvider, SqlOptions options, IDatabase database ) : base( serviceProvider, options, database ) { + } + + /// + protected override ISqlBuilder CreateSqlBuilder() { + return new OracleSqlBuilder(); + } + + /// + protected override IExistsSqlBuilder CreatExistsSqlBuilder( ISqlBuilder sqlBuilder ) { + return new OracleExistsSqlBuilder( sqlBuilder ); + } + + /// + protected override IDatabaseFactory CreateDatabaseFactory() { + return new OracleDatabaseFactory(); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.Oracle/TypeHandlers/GuidTypeHandler.cs b/src/Util.Data.Dapper.Oracle/TypeHandlers/GuidTypeHandler.cs new file mode 100644 index 000000000..3ebf80204 --- /dev/null +++ b/src/Util.Data.Dapper.Oracle/TypeHandlers/GuidTypeHandler.cs @@ -0,0 +1,26 @@ +namespace Util.Data.Dapper.TypeHandlers; + +/// +/// Guid类型处理器 +/// +public class GuidTypeHandler : SqlMapper.ITypeHandler { + /// + /// 设置值 + /// + /// 参数 + /// 值 + public void SetValue( IDbDataParameter parameter, object value ) { + var oracleParameter = (OracleParameter)parameter; + oracleParameter.OracleDbType = OracleDbType.Raw; + parameter.Value = value; + } + + /// + /// 转换值 + /// + /// 目标类型 + /// 值 + public object Parse( Type destinationType, object value ) { + return Util.Helpers.Convert.ToGuid( value ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.Oracle/Usings.cs b/src/Util.Data.Dapper.Oracle/Usings.cs new file mode 100644 index 000000000..15a05c57e --- /dev/null +++ b/src/Util.Data.Dapper.Oracle/Usings.cs @@ -0,0 +1,10 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Text; +global using System.Data; +global using System.Collections.Concurrent; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Dapper; +global using Oracle.ManagedDataAccess.Client; diff --git a/src/Util.Data.Dapper.PostgreSql/03-Util.Data.Dapper.PostgreSql.csproj b/src/Util.Data.Dapper.PostgreSql/03-Util.Data.Dapper.PostgreSql.csproj new file mode 100644 index 000000000..dea1cd916 --- /dev/null +++ b/src/Util.Data.Dapper.PostgreSql/03-Util.Data.Dapper.PostgreSql.csproj @@ -0,0 +1,37 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Data.Dapper.PostgreSql + Util.Data.Dapper + Util.Data.Dapper.PostgreSql是Util应用框架基于Dapper的数据访问封装类库,用于处理PostgreSql数据库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Data.Dapper.PostgreSql.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Data.Dapper.PostgreSql.xml + + + + + True + False + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Util.Data.Dapper.PostgreSql/Metadata/PostgreSqlMetadataService.cs b/src/Util.Data.Dapper.PostgreSql/Metadata/PostgreSqlMetadataService.cs new file mode 100644 index 000000000..865057fc7 --- /dev/null +++ b/src/Util.Data.Dapper.PostgreSql/Metadata/PostgreSqlMetadataService.cs @@ -0,0 +1,99 @@ +using Util.Data.Metadata; +using Util.Data.Sql; + +namespace Util.Data.Dapper.Metadata; + +/// +/// PostgreSql数据库元数据服务 +/// +public class PostgreSqlMetadataService : IMetadataService { + /// + /// Sql查询对象 + /// + private readonly ISqlQuery _sqlQuery; + + /// + /// 初始化PostgreSql数据库元数据服务 + /// + /// Sql查询对象 + public PostgreSqlMetadataService( ISqlQuery sqlQuery ) { + _sqlQuery = sqlQuery ?? throw new ArgumentNullException( nameof( sqlQuery ) ); + } + + /// + /// 获取数据库信息 + /// + public async Task GetDatabaseInfoAsync() { + var result = await GetDatabase(); + var tables = await GetTables(); + result.Tables.AddRange( tables ); + return result; + } + + /// + /// 获取数据库信息 + /// + private async Task GetDatabase() { + return await _sqlQuery + .Select( "oid Id,datname Name" ) + .From( "pg_database" ) + .AppendWhere( "datname=current_database()" ) + .ToEntityAsync(); + } + + /// + /// 获取表信息 + /// + private async Task> GetTables() { + SetGetTablesSql(); + var dic = new Dictionary(); + var tables = ( await _sqlQuery.ToListAsync( ( table, column ) => { + if ( table == null || column == null ) + return null; + dic.TryAdd( table.Id, table ); + TableInfo result = dic[table.Id]; + result.Columns.Add( column ); + return result; + } ) ).Distinct().ToList(); + return tables; + } + + /// + /// 设置获取表信息Sql + /// + private void SetGetTablesSql() { + _sqlQuery + .Select( "c1.oid Id,t.tablename Name,t.schemaname Schema" ) + .AppendSelect( ",obj_description(c1.oid) [Comment]" ) + .Select( "c.*" ) + .From( "pg_tables t" ) + .Join( "pg_class c1" ).On( "t.tablename", "c1.relname" ) + .Join( GetColumnsSql(), "c" ).On( "c.table_id", "c1.oid" ) + .In( "t.schemaname", t => t + .Select( "schema_name" ) + .From( "information_schema.schemata" ) + .AppendWhere( "catalog_name=current_database()" ) + .AppendWhere( "schema_name != 'information_schema'" ) + .AppendWhere( "schema_name not like 'pg_%'" ) + ); + } + + /// + /// 获取列信息子查询 + /// + private ISqlBuilder GetColumnsSql() { + return _sqlQuery.NewSqlBuilder() + .Select( "a.attrelid table_id,a.attname Id,a.attname Name,col.udt_name DataType" ) + .Select( "col.numeric_precision Precision,col.numeric_scale Scale" ) + .AppendSelect( ",Coalesce(col.character_maximum_length, col.numeric_precision, -1) [Length]" ) + .AppendSelect( ",( Case a.attnotnull When 't' Then 0 Else 1 End ) [IsNullable]" ) + .AppendSelect( ",( Case a.attnum When con.conkey[[1]] Then 1 Else 0 End ) [IsPrimaryKey]" ) + .AppendSelect( ",( Case When col.is_identity='YES' Then 1 Else 0 End ) [IsAutoIncrement]" ) + .AppendSelect( ",col_description( a.attrelid, a.attnum ) [Comment]" ) + .From( "pg_attribute a" ) + .Join( "pg_class c2" ).On( "a.attrelid", "c2.oid" ) + .Join( "pg_constraint con" ).On( "con.conrelid", "c2.oid" ).AppendOn( "con.contype = 'p'" ) + .Join( "pg_namespace n" ).On( "n.oid", "c2.relnamespace" ) + .Join( "information_schema.columns col" ).On( "col.table_schema", "n.nspname" ).On( "col.table_name", "c2.relname" ).On( "col.column_name", "a.attname" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.PostgreSql/Metadata/PostgreSqlTypeConverter.cs b/src/Util.Data.Dapper.PostgreSql/Metadata/PostgreSqlTypeConverter.cs new file mode 100644 index 000000000..5aefa7744 --- /dev/null +++ b/src/Util.Data.Dapper.PostgreSql/Metadata/PostgreSqlTypeConverter.cs @@ -0,0 +1,52 @@ +using Util.Data.Metadata; + +namespace Util.Data.Dapper.Metadata; + +/// +/// PostgreSql数据类型转换器 +/// +public class PostgreSqlTypeConverter : ITypeConverter { + /// + public DbType? ToType( string dataType, int? length = null ) { + if ( dataType.IsEmpty() ) + return null; + switch ( dataType.ToLower() ) { + case "uuid": + return DbType.Guid; + case "varchar": + case "text": + case "json": + case "jsonb": + case "xml": + return DbType.String; + case "bool": + return DbType.Boolean; + case "char": + return DbType.Byte; + case "int2": + return DbType.Int16; + case "int4": + return DbType.Int32; + case "int8": + return DbType.Int64; + case "float4": + return DbType.Single; + case "float8": + return DbType.Double; + case "numeric": + case "decimal": + return DbType.Decimal; + case "date": + return DbType.Date; + case "time": + case "timetz": + return DbType.Time; + case "timestamp": + case "timestamptz": + return DbType.DateTime; + case "bytea": + return DbType.Binary; + } + throw new NotImplementedException( dataType ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.PostgreSql/Sql/AppBuilderExtensions.cs b/src/Util.Data.Dapper.PostgreSql/Sql/AppBuilderExtensions.cs new file mode 100644 index 000000000..c75667976 --- /dev/null +++ b/src/Util.Data.Dapper.PostgreSql/Sql/AppBuilderExtensions.cs @@ -0,0 +1,129 @@ +using Util.Configs; +using Util.Data.Sql; + +namespace Util.Data.Dapper.Sql; + +/// +/// PostgreSql数据库操作扩展 +/// +public static class AppBuilderExtensions { + + #region AddPgSqlQuery(配置PostgreSql Sql查询对象) + + /// + /// 配置PostgreSql Sql查询对象 + /// + /// 应用生成器 + public static IAppBuilder AddPgSqlQuery( this IAppBuilder builder ) { + return builder.AddPgSqlQuery( "" ); + } + + /// + /// 配置PostgreSql Sql查询对象 + /// + /// 应用生成器 + /// 数据库连接字符串 + public static IAppBuilder AddPgSqlQuery( this IAppBuilder builder, string connection ) { + return builder.AddPgSqlQuery( connection ); + } + + /// + /// 配置PostgreSql Sql查询对象 + /// + /// 应用生成器 + /// 配置操作 + public static IAppBuilder AddPgSqlQuery( this IAppBuilder builder, Action setupAction ) { + return builder.AddPgSqlQuery( setupAction ); + } + + /// + /// 配置PostgreSql Sql查询对象 + /// + /// 应用生成器 + /// 数据库连接字符串 + public static IAppBuilder AddPgSqlQuery( this IAppBuilder builder, string connection ) + where TService : ISqlQuery + where TImplementation : PostgreSqlQueryBase, TService { + return builder.AddPgSqlQuery( t => t.ConnectionString( connection ) ); + } + + /// + /// 配置PostgreSql Sql查询对象 + /// + /// 应用生成器 + /// 配置操作 + public static IAppBuilder AddPgSqlQuery( this IAppBuilder builder, Action setupAction ) + where TService : ISqlQuery + where TImplementation : PostgreSqlQueryBase, TService { + var options = new SqlOptions(); + setupAction?.Invoke( options ); + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + AppContext.SetSwitch( "Npgsql.EnableStoredProcedureCompatMode", true ); + services.TryAddTransient( typeof( TService ), typeof( TImplementation ) ); + services.TryAddSingleton( typeof( SqlOptions ), ( sp ) => options ); + } ); + return builder; + } + + #endregion + + #region AddPgSqlExecutor(配置PostgreSql Sql执行器) + + /// + /// 配置PostgreSql Sql执行器 + /// + /// 应用生成器 + public static IAppBuilder AddPgSqlExecutor( this IAppBuilder builder ) { + return builder.AddPgSqlExecutor( "" ); + } + + /// + /// 配置PostgreSql Sql执行器 + /// + /// 应用生成器 + /// 数据库连接字符串 + public static IAppBuilder AddPgSqlExecutor( this IAppBuilder builder, string connection ) { + return builder.AddPgSqlExecutor( connection ); + } + + /// + /// 配置PostgreSql Sql执行器 + /// + /// 应用生成器 + /// 配置操作 + public static IAppBuilder AddPgSqlExecutor( this IAppBuilder builder, Action setupAction ) { + return builder.AddPgSqlExecutor( setupAction ); + } + + /// + /// 配置PostgreSql Sql执行器 + /// + /// 应用生成器 + /// 数据库连接字符串 + public static IAppBuilder AddPgSqlExecutor( this IAppBuilder builder, string connection ) + where TService : ISqlExecutor + where TImplementation : PostgreSqlExecutorBase, TService { + return builder.AddPgSqlExecutor( t => t.ConnectionString( connection ) ); + } + + /// + /// 配置PostgreSql Sql执行器 + /// + /// 应用生成器 + /// 配置操作 + public static IAppBuilder AddPgSqlExecutor( this IAppBuilder builder, Action setupAction ) + where TService : ISqlExecutor + where TImplementation : PostgreSqlExecutorBase, TService { + var options = new SqlOptions(); + setupAction?.Invoke( options ); + builder.Host.ConfigureServices( ( context, services ) => { + AppContext.SetSwitch( "Npgsql.EnableStoredProcedureCompatMode", true ); + services.TryAddTransient( typeof( TService ), typeof( TImplementation ) ); + services.TryAddSingleton( typeof( SqlOptions ), ( sp ) => options ); + } ); + return builder; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.PostgreSql/Sql/Builders/PostgreSqlBuilder.cs b/src/Util.Data.Dapper.PostgreSql/Sql/Builders/PostgreSqlBuilder.cs new file mode 100644 index 000000000..3cc113410 --- /dev/null +++ b/src/Util.Data.Dapper.PostgreSql/Sql/Builders/PostgreSqlBuilder.cs @@ -0,0 +1,41 @@ +using Util.Data.Sql; +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Caches; +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Dapper.Sql.Builders; + +/// +/// PostgreSql Sql生成器 +/// +public class PostgreSqlBuilder : SqlBuilderBase { + /// + /// 初始化PostgreSql Sql生成器 + /// + /// Sql参数管理器 + public PostgreSqlBuilder( IParameterManager parameterManager = null ) + : base( parameterManager ) { + } + + /// + protected override IDialect CreateDialect() { + return PostgreSqlDialect.Instance; + } + + /// + protected override IColumnCache CreateColumnCache() { + return PostgreSqlColumnCache.Instance; + } + + /// + public override ISqlBuilder New() { + return new PostgreSqlBuilder( ParameterManager ); + } + + /// + public override ISqlBuilder Clone() { + var result = new PostgreSqlBuilder(); + result.Clone( this ); + return result; + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.PostgreSql/Sql/Builders/PostgreSqlColumnCache.cs b/src/Util.Data.Dapper.PostgreSql/Sql/Builders/PostgreSqlColumnCache.cs new file mode 100644 index 000000000..36b3758ba --- /dev/null +++ b/src/Util.Data.Dapper.PostgreSql/Sql/Builders/PostgreSqlColumnCache.cs @@ -0,0 +1,35 @@ +using Util.Data.Sql.Builders.Caches; + +namespace Util.Data.Dapper.Sql.Builders; + +/// +/// PostgreSql列缓存 +/// +public class PostgreSqlColumnCache : ColumnCacheBase { + /// + /// 列名缓存 + /// + private readonly ConcurrentDictionary _cache; + + /// + /// 封闭构造方法 + /// + private PostgreSqlColumnCache() : base( PostgreSqlDialect.Instance ) { + _cache = new ConcurrentDictionary(); + } + + /// + /// PostgreSql列缓存实例 + /// + public static readonly IColumnCache Instance = new PostgreSqlColumnCache(); + + /// + public override string GetSafeColumns( string columns ) { + return _cache.GetOrAdd( columns.GetHashCode(), key => NormalizeColumns( columns ) ); + } + + /// + public override string GetSafeColumn( string column ) { + return _cache.GetOrAdd( column.GetHashCode(), key => NormalizeColumn( column ) ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.PostgreSql/Sql/Builders/PostgreSqlDialect.cs b/src/Util.Data.Dapper.PostgreSql/Sql/Builders/PostgreSqlDialect.cs new file mode 100644 index 000000000..7033634c3 --- /dev/null +++ b/src/Util.Data.Dapper.PostgreSql/Sql/Builders/PostgreSqlDialect.cs @@ -0,0 +1,35 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Core; + +namespace Util.Data.Dapper.Sql.Builders; + +/// +/// PostgreSql方言 +/// +public class PostgreSqlDialect : DialectBase { + /// + /// 封闭构造方法 + /// + private PostgreSqlDialect() { + } + + /// + /// PostgreSql方言实例 + /// + public static readonly IDialect Instance = new PostgreSqlDialect(); + + /// + public override string GetOpeningIdentifier() { + return "\""; + } + + /// + public override string GetClosingIdentifier() { + return "\""; + } + + /// + public override string GetPrefix() { + return "@"; + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.PostgreSql/Sql/Builders/PostgreSqlExistsSqlBuilder.cs b/src/Util.Data.Dapper.PostgreSql/Sql/Builders/PostgreSqlExistsSqlBuilder.cs new file mode 100644 index 000000000..ec64a1f13 --- /dev/null +++ b/src/Util.Data.Dapper.PostgreSql/Sql/Builders/PostgreSqlExistsSqlBuilder.cs @@ -0,0 +1,53 @@ +using Util.Data.Sql; +using Util.Data.Sql.Builders; + +namespace Util.Data.Dapper.Sql.Builders; + +/// +/// 判断是否存在Sql生成器 +/// +public class PostgreSqlExistsSqlBuilder : IExistsSqlBuilder { + /// + /// Sql生成器 + /// + private readonly ISqlBuilder _sqlBuilder; + + /// + /// 初始化判断是否存在Sql生成器 + /// + /// Sql生成器 + public PostgreSqlExistsSqlBuilder( ISqlBuilder sqlBuilder ) { + _sqlBuilder = sqlBuilder; + } + + /// + /// 获取Sql + /// + public string GetSql() { + InitSelect(); + return GetResult(); + } + + /// + /// 将Select子句初始化为1 + /// + private void InitSelect() { + _sqlBuilder.ClearSelect(); + _sqlBuilder.AppendSelect( "1" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = new StringBuilder(); + result.AppendLine( "Select Case" ); + result.AppendLine( " When Exists (" ); + _sqlBuilder.AppendTo( result ); + result.AppendLine( ")" ); + result.AppendLine( " Then Cast(1 As Bit)" ); + result.AppendLine( " Else Cast(0 As Bit) " ); + result.Append( "End" ); + return result.ToString(); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.PostgreSql/Sql/PostgreSqlDatabaseFactory.cs b/src/Util.Data.Dapper.PostgreSql/Sql/PostgreSqlDatabaseFactory.cs new file mode 100644 index 000000000..abcbd0c5d --- /dev/null +++ b/src/Util.Data.Dapper.PostgreSql/Sql/PostgreSqlDatabaseFactory.cs @@ -0,0 +1,14 @@ +namespace Util.Data.Dapper.Sql; + +/// +/// PostgreSql数据库工厂 +/// +public class PostgreSqlDatabaseFactory : IDatabaseFactory { + /// + /// 创建数据库信息 + /// + /// 数据库连接字符串 + public IDatabase Create( string connection ) { + return new DefaultDatabase( new NpgsqlConnection( connection ) ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.PostgreSql/Sql/PostgreSqlExecutor.cs b/src/Util.Data.Dapper.PostgreSql/Sql/PostgreSqlExecutor.cs new file mode 100644 index 000000000..82db4cebe --- /dev/null +++ b/src/Util.Data.Dapper.PostgreSql/Sql/PostgreSqlExecutor.cs @@ -0,0 +1,18 @@ +using Util.Data.Sql; + +namespace Util.Data.Dapper.Sql; + +/// +/// PostgreSql Sql执行器 +/// +public class PostgreSqlExecutor : PostgreSqlExecutorBase { + /// + /// 初始化PostgreSql Sql执行器 + /// + /// 服务提供器 + /// Sql配置 + /// 数据库信息,用于接入其它数据源,比如EF DbContext + public PostgreSqlExecutor( IServiceProvider serviceProvider, SqlOptions options, IDatabase database = null ) + : base( serviceProvider, options, database ) { + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.PostgreSql/Sql/PostgreSqlExecutorBase.cs b/src/Util.Data.Dapper.PostgreSql/Sql/PostgreSqlExecutorBase.cs new file mode 100644 index 000000000..621caf3dc --- /dev/null +++ b/src/Util.Data.Dapper.PostgreSql/Sql/PostgreSqlExecutorBase.cs @@ -0,0 +1,67 @@ +using Util.Data.Dapper.Sql.Builders; +using Util.Data.Sql; +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Core; + +namespace Util.Data.Dapper.Sql; + +/// +/// PostgreSql Sql执行器 +/// +public abstract class PostgreSqlExecutorBase : SqlExecutorBase { + /// + /// 初始化PostgreSql Sql执行器 + /// + /// 服务提供器 + /// Sql配置 + /// 数据库信息,用于接入其它数据源,比如EF DbContext + protected PostgreSqlExecutorBase( IServiceProvider serviceProvider, SqlOptions options, IDatabase database ) : base( serviceProvider, options, database ) { + } + + /// + /// 创建Sql生成器 + /// + protected override ISqlBuilder CreateSqlBuilder() { + return new PostgreSqlBuilder(); + } + + /// + /// 创建判断是否存在Sql生成器 + /// + protected override IExistsSqlBuilder CreatExistsSqlBuilder( ISqlBuilder sqlBuilder ) { + return new PostgreSqlExistsSqlBuilder( sqlBuilder ); + } + + /// + /// 创建数据库工厂 + /// + protected override IDatabaseFactory CreateDatabaseFactory() { + return new PostgreSqlDatabaseFactory(); + } + + /// + /// 获取存储过程名称 + /// + /// 存储过程 + protected override string GetProcedure( string procedure ) { + if ( IsProcedure( procedure ) ) + return Dialect.ReplaceSql( procedure ); + return new TableItem( Dialect, procedure ).ToResult(); + } + + /// + /// 是否存储过程 + /// + protected virtual bool IsProcedure( string procedure ) { + return procedure.Trim().StartsWith( "call ", StringComparison.OrdinalIgnoreCase ); + } + + /// + /// 获取存储过程命令类型 + /// + protected override CommandType GetProcedureCommandType() { + if ( IsProcedure( GetSql() ) ) + return CommandType.Text; + return CommandType.StoredProcedure; + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.PostgreSql/Sql/PostgreSqlQuery.cs b/src/Util.Data.Dapper.PostgreSql/Sql/PostgreSqlQuery.cs new file mode 100644 index 000000000..c0efb16fe --- /dev/null +++ b/src/Util.Data.Dapper.PostgreSql/Sql/PostgreSqlQuery.cs @@ -0,0 +1,18 @@ +using Util.Data.Sql; + +namespace Util.Data.Dapper.Sql; + +/// +/// PostgreSql Sql查询对象 +/// +public class PostgreSqlQuery : PostgreSqlQueryBase { + /// + /// 初始化PostgreSql Sql查询对象 + /// + /// 服务提供器 + /// Sql配置 + /// 数据库信息,用于接入其它数据源,比如EF DbContext + public PostgreSqlQuery( IServiceProvider serviceProvider, SqlOptions options, IDatabase database = null ) + : base( serviceProvider, options, database ) { + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.PostgreSql/Sql/PostgreSqlQueryBase.cs b/src/Util.Data.Dapper.PostgreSql/Sql/PostgreSqlQueryBase.cs new file mode 100644 index 000000000..bf1f5f2c9 --- /dev/null +++ b/src/Util.Data.Dapper.PostgreSql/Sql/PostgreSqlQueryBase.cs @@ -0,0 +1,56 @@ +using Util.Data.Dapper.Sql.Builders; +using Util.Data.Sql; +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Core; + +namespace Util.Data.Dapper.Sql; + +/// +/// PostgreSql Sql查询对象 +/// +public abstract class PostgreSqlQueryBase : SqlQueryBase { + /// + /// 初始化PostgreSql Sql查询对象 + /// + /// 服务提供器 + /// Sql配置 + /// 数据库信息,用于接入其它数据源,比如EF DbContext + protected PostgreSqlQueryBase( IServiceProvider serviceProvider, SqlOptions options, IDatabase database ) : base( serviceProvider, options, database ) { + } + + /// + protected override ISqlBuilder CreateSqlBuilder() { + return new PostgreSqlBuilder(); + } + + /// + protected override IExistsSqlBuilder CreatExistsSqlBuilder( ISqlBuilder sqlBuilder ) { + return new PostgreSqlExistsSqlBuilder( sqlBuilder ); + } + + /// + protected override IDatabaseFactory CreateDatabaseFactory() { + return new PostgreSqlDatabaseFactory(); + } + + /// + protected override string GetProcedure( string procedure ) { + if ( IsProcedure( procedure ) ) + return Dialect.ReplaceSql( procedure ); + return new TableItem( Dialect, procedure ).ToResult(); + } + + /// + /// 是否存储过程 + /// + protected virtual bool IsProcedure( string procedure ) { + return procedure.Trim().StartsWith( "call ", StringComparison.OrdinalIgnoreCase ); + } + + /// + protected override CommandType GetProcedureCommandType() { + if ( IsProcedure( GetSql() ) ) + return CommandType.Text; + return CommandType.StoredProcedure; + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.PostgreSql/Usings.cs b/src/Util.Data.Dapper.PostgreSql/Usings.cs new file mode 100644 index 000000000..1e9fc74e1 --- /dev/null +++ b/src/Util.Data.Dapper.PostgreSql/Usings.cs @@ -0,0 +1,9 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Text; +global using System.Data; +global using System.Collections.Concurrent; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Npgsql; \ No newline at end of file diff --git a/src/Util.Data.Dapper.SqlServer/02-Util.Data.Dapper.SqlServer.csproj b/src/Util.Data.Dapper.SqlServer/02-Util.Data.Dapper.SqlServer.csproj new file mode 100644 index 000000000..988fbdd52 --- /dev/null +++ b/src/Util.Data.Dapper.SqlServer/02-Util.Data.Dapper.SqlServer.csproj @@ -0,0 +1,37 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Data.Dapper.SqlServer + Util.Data.Dapper + Util.Data.Dapper.SqlServer是Util应用框架基于Dapper的数据访问封装类库,用于处理SqlServer数据库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Data.Dapper.SqlServer.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Data.Dapper.SqlServer.xml + + + + + True + False + + + + + + + + + + + + + diff --git a/src/Util.Data.Dapper.SqlServer/Metadata/SqlServerMetadataService.cs b/src/Util.Data.Dapper.SqlServer/Metadata/SqlServerMetadataService.cs new file mode 100644 index 000000000..4e5e84867 --- /dev/null +++ b/src/Util.Data.Dapper.SqlServer/Metadata/SqlServerMetadataService.cs @@ -0,0 +1,108 @@ +using Util.Data.Metadata; +using Util.Data.Sql; + +namespace Util.Data.Dapper.Metadata { + /// + /// Sql Server数据库元数据服务 + /// + public class SqlServerMetadataService : IMetadataService { + /// + /// Sql查询对象 + /// + private readonly ISqlQuery _sqlQuery; + + /// + /// 初始化Sql Server数据库元数据服务 + /// + /// Sql查询对象 + public SqlServerMetadataService( ISqlQuery sqlQuery ) { + _sqlQuery = sqlQuery ?? throw new ArgumentNullException( nameof( sqlQuery ) ); + } + + /// + /// 获取数据库信息 + /// + public async Task GetDatabaseInfoAsync() { + var result = await GetDatabase(); + var tables = await GetTables(); + result.Tables.AddRange( tables ); + return result; + } + + /// + /// 获取数据库信息 + /// + private async Task GetDatabase() { + return await _sqlQuery + .Select( "Dbid Id,Name" ) + .From( "sys.SysDataBases" ) + .Where( "DbId", t => t.Select( "Dbid" ).From( "sys.SysProcesses" ).AppendWhere( "Spid = @@spid" ) ) + .ToEntityAsync(); + } + + /// + /// 获取表信息 + /// + private async Task> GetTables() { + SetGetTablesSql(); + var dic = new Dictionary(); + var tables = ( await _sqlQuery.ToListAsync( ( table, column ) => { + if ( table == null || column == null ) + return null; + dic.TryAdd( table.Id, table ); + TableInfo result = dic[table.Id]; + result.Columns.Add( column ); + return result; + } ) ).Distinct().ToList(); + return tables; + } + + /// + /// 设置获取表信息Sql + /// + private void SetGetTablesSql() { + _sqlQuery + .Select( "o.object_id Id,o.name,s.name Schema,ep.value Comment" ) + .Select( "c.Id,c.name,c.Comment" ) + .AppendSelect( ",(Case When Exists(" ) + .AppendSelect( GetIsPrimaryKeySql() ) + .AppendSelect( ") Then Cast(1 As Bit) Else Cast(0 As Bit) End) As IsPrimaryKey" ) + .Select( "c.is_identity IsAutoIncrement,c.is_nullable IsNullable" ) + .Select( "c.DataType,c.max_length Length,c.precision,c.scale" ) + .From( "sys.Objects o" ) + .LeftJoin( "sys.Schemas s" ).On( "o.schema_id", "s.schema_id" ) + .LeftJoin( "sys.Extended_Properties ep" ).On( "o.object_id", "ep.major_id" ).On( "ep.minor_id", 0 ) + .Join( GetColumnsSql(), "c" ).On( "c.object_id", "o.object_id" ) + .In( "o.type", new[] { 'U' } ); + } + + /// + /// 获取是否主键子查询 + /// + private ISqlBuilder GetIsPrimaryKeySql() { + return _sqlQuery.NewSqlBuilder() + .Select() + .From( "Information_Schema.Key_Column_Usage k" ) + .Join( "Information_Schema.Table_Constraints tc" ) + .On( "k.table_name", "tc.table_name" ) + .On( "k.Constraint_Name", "tc.Constraint_Name" ) + .AppendOn( "tc.Constraint_Type='PRIMARY KEY'" ) + .AppendWhere( "o.name=k.table_name" ) + .AppendWhere( "c.name=k.column_name" ); + } + + /// + /// 获取列信息子查询 + /// + private ISqlBuilder GetColumnsSql() { + return _sqlQuery.NewSqlBuilder() + .Select( "c.object_id,c.column_id Id,c.name,ep.value Comment" ) + .Select( "c.is_identity,c.is_nullable,t.name DataType,c.max_length" ) + .Select( "c.precision,c.scale" ) + .From( "sys.Columns c" ) + .LeftJoin( "sys.Extended_Properties ep" ).On( "c.object_id", "ep.major_id" ).On( "c.column_id", "ep.minor_id" ) + .LeftJoin( "sys.Types t" ).On( "c.system_type_id", "t.system_type_id" ) + .AppendWhere( "t.name!='sysname'" ); + } + } +} diff --git a/src/Util.Data.Dapper.SqlServer/Metadata/SqlServerTypeConverter.cs b/src/Util.Data.Dapper.SqlServer/Metadata/SqlServerTypeConverter.cs new file mode 100644 index 000000000..9df1e38e5 --- /dev/null +++ b/src/Util.Data.Dapper.SqlServer/Metadata/SqlServerTypeConverter.cs @@ -0,0 +1,70 @@ +using Util.Data.Metadata; + +namespace Util.Data.Dapper.Metadata { + /// + /// Sql Server数据类型转换器 + /// + public class SqlServerTypeConverter : ITypeConverter { + /// + public DbType? ToType( string dataType, int? length = null ) { + if ( dataType.IsEmpty() ) + return null; + switch ( dataType.ToLower() ) { + case "uniqueidentifier": + return DbType.Guid; + case "char": + return DbType.AnsiStringFixedLength; + case "nchar": + return DbType.StringFixedLength; + case "varchar": + return DbType.AnsiString; + case "nvarchar": + case "text": + case "ntext": + return DbType.String; + case "bit": + return DbType.Boolean; + case "tinyint": + return DbType.Byte; + case "smallint": + return DbType.Int16; + case "int": + return DbType.Int32; + case "bigint": + return DbType.Int64; + case "real": + return DbType.Single; + case "float": + return DbType.Double; + case "decimal": + case "numeric": + case "money": + case "smallmoney": + return DbType.Decimal; + case "date": + return DbType.Date; + case "time": + return DbType.Time; + case "datetime": + case "smalldatetime": + return DbType.DateTime; + case "datetime2": + return DbType.DateTime2; + case "datetimeoffset": + return DbType.DateTimeOffset; + case "binary": + case "varbinary": + case "varbinary(max)": + case "image": + case "rowversion": + case "timestamp": + return DbType.Binary; + case "xml": + return DbType.Xml; + case "sql_variant": + return DbType.Object; + } + throw new NotImplementedException( dataType ); + } + } +} diff --git a/src/Util.Data.Dapper.SqlServer/Sql/AppBuilderExtensions.cs b/src/Util.Data.Dapper.SqlServer/Sql/AppBuilderExtensions.cs new file mode 100644 index 000000000..185683f5a --- /dev/null +++ b/src/Util.Data.Dapper.SqlServer/Sql/AppBuilderExtensions.cs @@ -0,0 +1,127 @@ +using Util.Configs; +using Util.Data.Sql; + +namespace Util.Data.Dapper.Sql; + +/// +/// Sql Server数据库操作扩展 +/// +public static class AppBuilderExtensions { + + #region AddSqlServerSqlQuery(配置Sql Server Sql查询对象) + + /// + /// 配置Sql Server Sql查询对象 + /// + /// 应用生成器 + public static IAppBuilder AddSqlServerSqlQuery( this IAppBuilder builder ) { + return builder.AddSqlServerSqlQuery( "" ); + } + + /// + /// 配置Sql Server Sql查询对象 + /// + /// 应用生成器 + /// 数据库连接字符串 + public static IAppBuilder AddSqlServerSqlQuery( this IAppBuilder builder, string connection ) { + return builder.AddSqlServerSqlQuery( connection ); + } + + /// + /// 配置Sql Server Sql查询对象 + /// + /// 应用生成器 + /// 配置操作 + public static IAppBuilder AddSqlServerSqlQuery( this IAppBuilder builder, Action setupAction ) { + return builder.AddSqlServerSqlQuery( setupAction ); + } + + /// + /// 配置Sql Server Sql查询对象 + /// + /// 应用生成器 + /// 数据库连接字符串 + public static IAppBuilder AddSqlServerSqlQuery( this IAppBuilder builder, string connection ) + where TService : ISqlQuery + where TImplementation : SqlServerSqlQueryBase, TService { + return builder.AddSqlServerSqlQuery( t => t.ConnectionString( connection ) ); + } + + /// + /// 配置Sql Server Sql查询对象 + /// + /// 应用生成器 + /// 配置操作 + public static IAppBuilder AddSqlServerSqlQuery( this IAppBuilder builder, Action setupAction ) + where TService : ISqlQuery + where TImplementation : SqlServerSqlQueryBase, TService { + var options = new SqlOptions(); + setupAction?.Invoke( options ); + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + services.TryAddTransient( typeof( TService ), typeof( TImplementation ) ); + services.TryAddSingleton( typeof( SqlOptions ), ( sp ) => options ); + } ); + return builder; + } + + #endregion + + #region AddSqlServerSqlExecutor(配置Sql Server Sql执行器) + + /// + /// 配置Sql Server Sql执行器 + /// + /// 应用生成器 + public static IAppBuilder AddSqlServerSqlExecutor( this IAppBuilder builder ) { + return builder.AddSqlServerSqlExecutor( "" ); + } + + /// + /// 配置Sql Server Sql执行器 + /// + /// 应用生成器 + /// 数据库连接字符串 + public static IAppBuilder AddSqlServerSqlExecutor( this IAppBuilder builder, string connection ) { + return builder.AddSqlServerSqlExecutor( connection ); + } + + /// + /// 配置Sql Server Sql执行器 + /// + /// 应用生成器 + /// 配置操作 + public static IAppBuilder AddSqlServerSqlExecutor( this IAppBuilder builder, Action setupAction ) { + return builder.AddSqlServerSqlExecutor( setupAction ); + } + + /// + /// 配置Sql Server Sql执行器 + /// + /// 应用生成器 + /// 数据库连接字符串 + public static IAppBuilder AddSqlServerSqlExecutor( this IAppBuilder builder, string connection ) + where TService : ISqlExecutor + where TImplementation : SqlServerSqlExecutorBase, TService { + return builder.AddSqlServerSqlExecutor( t => t.ConnectionString( connection ) ); + } + + /// + /// 配置Sql Server Sql执行器 + /// + /// 应用生成器 + /// 配置操作 + public static IAppBuilder AddSqlServerSqlExecutor( this IAppBuilder builder, Action setupAction ) + where TService : ISqlExecutor + where TImplementation : SqlServerSqlExecutorBase, TService { + var options = new SqlOptions(); + setupAction?.Invoke( options ); + builder.Host.ConfigureServices( ( context, services ) => { + services.TryAddTransient( typeof( TService ), typeof( TImplementation ) ); + services.TryAddSingleton( typeof( SqlOptions ), ( sp ) => options ); + } ); + return builder; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.SqlServer/Sql/Builders/SqlServerBuilder.cs b/src/Util.Data.Dapper.SqlServer/Sql/Builders/SqlServerBuilder.cs new file mode 100644 index 000000000..6f2e7dcfe --- /dev/null +++ b/src/Util.Data.Dapper.SqlServer/Sql/Builders/SqlServerBuilder.cs @@ -0,0 +1,41 @@ +using Util.Data.Sql; +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Caches; +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Dapper.Sql.Builders; + +/// +/// Sql Server Sql生成器 +/// +public class SqlServerBuilder : SqlBuilderBase { + /// + /// 初始化Sql Server Sql生成器 + /// + /// Sql参数管理器 + public SqlServerBuilder( IParameterManager parameterManager = null ) + : base( parameterManager ) { + } + + /// + protected override IDialect CreateDialect() { + return SqlServerDialect.Instance; + } + + /// + protected override IColumnCache CreateColumnCache() { + return SqlServerColumnCache.Instance; + } + + /// + public override ISqlBuilder New() { + return new SqlServerBuilder( ParameterManager ); + } + + /// + public override ISqlBuilder Clone() { + var result = new SqlServerBuilder(); + result.Clone( this ); + return result; + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.SqlServer/Sql/Builders/SqlServerColumnCache.cs b/src/Util.Data.Dapper.SqlServer/Sql/Builders/SqlServerColumnCache.cs new file mode 100644 index 000000000..8d59693e8 --- /dev/null +++ b/src/Util.Data.Dapper.SqlServer/Sql/Builders/SqlServerColumnCache.cs @@ -0,0 +1,35 @@ +using Util.Data.Sql.Builders.Caches; + +namespace Util.Data.Dapper.Sql.Builders; + +/// +/// Sql Server列缓存 +/// +public class SqlServerColumnCache : ColumnCacheBase { + /// + /// 列名缓存 + /// + private readonly ConcurrentDictionary _cache; + + /// + /// 封闭构造方法 + /// + private SqlServerColumnCache() : base( SqlServerDialect.Instance ) { + _cache = new ConcurrentDictionary(); + } + + /// + /// Sql Server列缓存实例 + /// + public static readonly IColumnCache Instance = new SqlServerColumnCache(); + + /// + public override string GetSafeColumns( string columns ) { + return _cache.GetOrAdd( columns.GetHashCode(), key => NormalizeColumns( columns ) ); + } + + /// + public override string GetSafeColumn( string column ) { + return _cache.GetOrAdd( column.GetHashCode(), key => NormalizeColumn( column ) ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.SqlServer/Sql/Builders/SqlServerDialect.cs b/src/Util.Data.Dapper.SqlServer/Sql/Builders/SqlServerDialect.cs new file mode 100644 index 000000000..e93902cb5 --- /dev/null +++ b/src/Util.Data.Dapper.SqlServer/Sql/Builders/SqlServerDialect.cs @@ -0,0 +1,35 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Core; + +namespace Util.Data.Dapper.Sql.Builders; + +/// +/// Sql Server方言 +/// +public class SqlServerDialect : DialectBase { + /// + /// 封闭构造方法 + /// + private SqlServerDialect() { + } + + /// + /// Sql Server方言实例 + /// + public static readonly IDialect Instance = new SqlServerDialect(); + + /// + public override string GetOpeningIdentifier() { + return "["; + } + + /// + public override string GetClosingIdentifier() { + return "]"; + } + + /// + public override string GetPrefix() { + return "@"; + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.SqlServer/Sql/Builders/SqlServerExistsSqlBuilder.cs b/src/Util.Data.Dapper.SqlServer/Sql/Builders/SqlServerExistsSqlBuilder.cs new file mode 100644 index 000000000..79a5f92bc --- /dev/null +++ b/src/Util.Data.Dapper.SqlServer/Sql/Builders/SqlServerExistsSqlBuilder.cs @@ -0,0 +1,53 @@ +using Util.Data.Sql; +using Util.Data.Sql.Builders; + +namespace Util.Data.Dapper.Sql.Builders; + +/// +/// 判断是否存在Sql生成器 +/// +public class SqlServerExistsSqlBuilder : IExistsSqlBuilder { + /// + /// Sql生成器 + /// + private readonly ISqlBuilder _sqlBuilder; + + /// + /// 初始化判断是否存在Sql生成器 + /// + /// Sql生成器 + public SqlServerExistsSqlBuilder( ISqlBuilder sqlBuilder ) { + _sqlBuilder = sqlBuilder; + } + + /// + /// 获取Sql + /// + public string GetSql() { + InitSelect(); + return GetResult(); + } + + /// + /// 将Select子句初始化为1 + /// + private void InitSelect() { + _sqlBuilder.ClearSelect(); + _sqlBuilder.AppendSelect( "1" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = new StringBuilder(); + result.AppendLine( "Select Case" ); + result.AppendLine( " When Exists (" ); + _sqlBuilder.AppendTo( result ); + result.AppendLine( ")" ); + result.AppendLine( " Then Cast(1 As Bit)" ); + result.AppendLine( " Else Cast(0 As Bit) " ); + result.Append( "End" ); + return result.ToString(); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.SqlServer/Sql/SqlServerDatabaseFactory.cs b/src/Util.Data.Dapper.SqlServer/Sql/SqlServerDatabaseFactory.cs new file mode 100644 index 000000000..15564310f --- /dev/null +++ b/src/Util.Data.Dapper.SqlServer/Sql/SqlServerDatabaseFactory.cs @@ -0,0 +1,14 @@ +namespace Util.Data.Dapper.Sql; + +/// +/// Sql Server数据库工厂 +/// +public class SqlServerDatabaseFactory : IDatabaseFactory { + /// + /// 创建数据库信息 + /// + /// 数据库连接字符串 + public IDatabase Create( string connection ) { + return new DefaultDatabase( new SqlConnection( connection ) ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.SqlServer/Sql/SqlServerSqlExecutor.cs b/src/Util.Data.Dapper.SqlServer/Sql/SqlServerSqlExecutor.cs new file mode 100644 index 000000000..21d15398a --- /dev/null +++ b/src/Util.Data.Dapper.SqlServer/Sql/SqlServerSqlExecutor.cs @@ -0,0 +1,18 @@ +using Util.Data.Sql; + +namespace Util.Data.Dapper.Sql; + +/// +/// Sql Server Sql执行器 +/// +public class SqlServerSqlExecutor : SqlServerSqlExecutorBase { + /// + /// 初始化Sql Server Sql执行器 + /// + /// 服务提供器 + /// Sql配置 + /// 数据库信息,用于接入其它数据源,比如EF DbContext + public SqlServerSqlExecutor( IServiceProvider serviceProvider, SqlOptions options, IDatabase database = null ) + : base( serviceProvider, options, database ) { + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.SqlServer/Sql/SqlServerSqlExecutorBase.cs b/src/Util.Data.Dapper.SqlServer/Sql/SqlServerSqlExecutorBase.cs new file mode 100644 index 000000000..4a9c73024 --- /dev/null +++ b/src/Util.Data.Dapper.SqlServer/Sql/SqlServerSqlExecutorBase.cs @@ -0,0 +1,40 @@ +using Util.Data.Dapper.Sql.Builders; +using Util.Data.Sql; +using Util.Data.Sql.Builders; + +namespace Util.Data.Dapper.Sql; + +/// +/// Sql Server Sql执行器 +/// +public abstract class SqlServerSqlExecutorBase : SqlExecutorBase { + /// + /// 初始化Sql Server Sql执行器 + /// + /// 服务提供器 + /// Sql配置 + /// 数据库信息,用于接入其它数据源,比如EF DbContext + protected SqlServerSqlExecutorBase( IServiceProvider serviceProvider, SqlOptions options, IDatabase database ) : base( serviceProvider, options, database ) { + } + + /// + /// 创建Sql生成器 + /// + protected override ISqlBuilder CreateSqlBuilder() { + return new SqlServerBuilder(); + } + + /// + /// 创建判断是否存在Sql生成器 + /// + protected override IExistsSqlBuilder CreatExistsSqlBuilder( ISqlBuilder sqlBuilder ) { + return new SqlServerExistsSqlBuilder( sqlBuilder ); + } + + /// + /// 创建数据库工厂 + /// + protected override IDatabaseFactory CreateDatabaseFactory() { + return new SqlServerDatabaseFactory(); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.SqlServer/Sql/SqlServerSqlQuery.cs b/src/Util.Data.Dapper.SqlServer/Sql/SqlServerSqlQuery.cs new file mode 100644 index 000000000..5ed4aa172 --- /dev/null +++ b/src/Util.Data.Dapper.SqlServer/Sql/SqlServerSqlQuery.cs @@ -0,0 +1,18 @@ +using Util.Data.Sql; + +namespace Util.Data.Dapper.Sql; + +/// +/// Sql Server Sql查询对象 +/// +public class SqlServerSqlQuery : SqlServerSqlQueryBase { + /// + /// 初始化Sql Server Sql查询对象 + /// + /// 服务提供器 + /// Sql配置 + /// 数据库信息,用于接入其它数据源,比如EF DbContext + public SqlServerSqlQuery( IServiceProvider serviceProvider, SqlOptions options, IDatabase database = null ) + : base( serviceProvider, options, database ) { + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.SqlServer/Sql/SqlServerSqlQueryBase.cs b/src/Util.Data.Dapper.SqlServer/Sql/SqlServerSqlQueryBase.cs new file mode 100644 index 000000000..016ea3d7e --- /dev/null +++ b/src/Util.Data.Dapper.SqlServer/Sql/SqlServerSqlQueryBase.cs @@ -0,0 +1,34 @@ +using Util.Data.Dapper.Sql.Builders; +using Util.Data.Sql; +using Util.Data.Sql.Builders; + +namespace Util.Data.Dapper.Sql; + +/// +/// Sql Server Sql查询对象 +/// +public abstract class SqlServerSqlQueryBase : SqlQueryBase { + /// + /// 初始化Sql Server Sql查询对象 + /// + /// 服务提供器 + /// Sql配置 + /// 数据库信息,用于接入其它数据源,比如EF DbContext + protected SqlServerSqlQueryBase( IServiceProvider serviceProvider, SqlOptions options, IDatabase database ) : base( serviceProvider, options, database ) { + } + + /// + protected override ISqlBuilder CreateSqlBuilder() { + return new SqlServerBuilder(); + } + + /// + protected override IExistsSqlBuilder CreatExistsSqlBuilder( ISqlBuilder sqlBuilder ) { + return new SqlServerExistsSqlBuilder( sqlBuilder ); + } + + /// + protected override IDatabaseFactory CreateDatabaseFactory() { + return new SqlServerDatabaseFactory(); + } +} \ No newline at end of file diff --git a/src/Util.Data.Dapper.SqlServer/Usings.cs b/src/Util.Data.Dapper.SqlServer/Usings.cs new file mode 100644 index 000000000..68c543f15 --- /dev/null +++ b/src/Util.Data.Dapper.SqlServer/Usings.cs @@ -0,0 +1,9 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Text; +global using System.Data; +global using System.Collections.Concurrent; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Microsoft.Data.SqlClient; diff --git a/src/Util.Data.EntityFrameworkCore.MySql/04-Util.Data.EntityFrameworkCore.MySql.csproj b/src/Util.Data.EntityFrameworkCore.MySql/04-Util.Data.EntityFrameworkCore.MySql.csproj new file mode 100644 index 000000000..79f6898f5 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.MySql/04-Util.Data.EntityFrameworkCore.MySql.csproj @@ -0,0 +1,37 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Data.EntityFrameworkCore.MySql + Util.Data.EntityFrameworkCore + Util.Data.EntityFrameworkCore.MySql是Util应用框架基于EntityFrameworkCore的数据访问封装类库,用于处理MySql数据库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Data.EntityFrameworkCore.MySql.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Data.EntityFrameworkCore.MySql.xml + + + + + True + False + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.MySql/AppBuilderExtensions.cs b/src/Util.Data.EntityFrameworkCore.MySql/AppBuilderExtensions.cs new file mode 100644 index 000000000..8c99ca350 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.MySql/AppBuilderExtensions.cs @@ -0,0 +1,72 @@ +using Util.Configs; + +namespace Util.Data.EntityFrameworkCore; + +/// +/// MySql工作单元操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置MySql工作单元 + /// + /// 应用生成器 + /// 数据库连接字符串 + /// 工作单元配置操作 + /// MySql配置操作 + /// 条件,设置为false,跳过配置 + public static IAppBuilder AddMySqlUnitOfWork( this IAppBuilder builder, string connection, Action setupAction = null, + Action mySqlSetupAction = null, bool? condition = null ) + where TService : class, IUnitOfWork + where TImplementation : UnitOfWorkBase, TService { + return builder.AddMySqlUnitOfWork( connection, null, setupAction, mySqlSetupAction, condition ); + } + + /// + /// 配置MySql工作单元 + /// + /// 应用生成器 + /// 数据库连接 + /// 工作单元配置操作 + /// MySql配置操作 + /// 条件,设置为false,跳过配置 + public static IAppBuilder AddMySqlUnitOfWork( this IAppBuilder builder, DbConnection connection, Action setupAction = null, + Action mySqlSetupAction = null, bool? condition = null ) + where TService : class, IUnitOfWork + where TImplementation : UnitOfWorkBase, TService { + return builder.AddMySqlUnitOfWork( null, connection, setupAction, mySqlSetupAction, condition ); + } + + /// + /// 配置MySql工作单元 + /// + private static IAppBuilder AddMySqlUnitOfWork( this IAppBuilder builder, string connectionString, DbConnection connection, Action setupAction, + Action mySqlSetupAction, bool? condition ) + where TService : class, IUnitOfWork + where TImplementation : UnitOfWorkBase, TService { + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + void Action( DbContextOptionsBuilder options ) { + setupAction?.Invoke( options ); + if ( connectionString.IsEmpty() == false ) { + options.UseMySql( connectionString, ServerVersion.AutoDetect( connectionString ), + optionsBuilder => { + optionsBuilder.EnableRetryOnFailure(); + mySqlSetupAction?.Invoke( optionsBuilder ); + } ); + return; + } + if ( connection != null ) { + options.UseMySql( connection, ServerVersion.AutoDetect( (MySqlConnection)connection ), optionsBuilder => { + optionsBuilder.EnableRetryOnFailure(); + mySqlSetupAction?.Invoke( optionsBuilder ); + } ); + } + } + services.AddDbContext( Action ); + if ( condition == false ) + return; + services.AddDbContext( Action ); + } ); + return builder; + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.MySql/MySqlUnitOfWorkBase.cs b/src/Util.Data.EntityFrameworkCore.MySql/MySqlUnitOfWorkBase.cs new file mode 100644 index 000000000..a5b5acd73 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.MySql/MySqlUnitOfWorkBase.cs @@ -0,0 +1,20 @@ +namespace Util.Data.EntityFrameworkCore; + +/// +/// MySql工作单元基类 +/// +public abstract class MySqlUnitOfWorkBase : UnitOfWorkBase { + /// + /// 初始化MySql工作单元 + /// + /// 服务提供器 + /// 配置 + protected MySqlUnitOfWorkBase( IServiceProvider serviceProvider, DbContextOptions options ) + : base( serviceProvider, options ) { + } + + /// + protected override void ConfigTenantConnectionString( DbContextOptionsBuilder optionsBuilder, string connectionString ) { + optionsBuilder.UseMySql( connectionString, ServerVersion.AutoDetect( connectionString ) ); + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.MySql/Usings.cs b/src/Util.Data.EntityFrameworkCore.MySql/Usings.cs new file mode 100644 index 000000000..4a26a3949 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.MySql/Usings.cs @@ -0,0 +1,6 @@ +global using System; +global using System.Data.Common; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.Infrastructure; +global using MySqlConnector; diff --git a/src/Util.Data.EntityFrameworkCore.Oracle/06-Util.Data.EntityFrameworkCore.Oracle.csproj b/src/Util.Data.EntityFrameworkCore.Oracle/06-Util.Data.EntityFrameworkCore.Oracle.csproj new file mode 100644 index 000000000..1754f9bbc --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.Oracle/06-Util.Data.EntityFrameworkCore.Oracle.csproj @@ -0,0 +1,37 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Data.EntityFrameworkCore.Oracle + Util.Data.EntityFrameworkCore + Util.Data.EntityFrameworkCore.Oracle是Util应用框架基于EntityFrameworkCore的数据访问封装类库,用于处理Oracle数据库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Data.EntityFrameworkCore.Oracle.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Data.EntityFrameworkCore.Oracle.xml + + + + + True + False + + + + + + + + + + + + + diff --git a/src/Util.Data.EntityFrameworkCore.Oracle/AppBuilderExtensions.cs b/src/Util.Data.EntityFrameworkCore.Oracle/AppBuilderExtensions.cs new file mode 100644 index 000000000..a71eae876 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.Oracle/AppBuilderExtensions.cs @@ -0,0 +1,76 @@ +using Util.Configs; + +namespace Util.Data.EntityFrameworkCore; + +/// +/// Oracle工作单元操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置Oracle工作单元 + /// + /// 应用生成器 + /// 数据库连接字符串 + /// 工作单元配置操作 + /// Oracle配置操作 + /// 条件,设置为false,跳过配置 + /// 存储时是否将Guid类型转换为字符串类型 + public static IAppBuilder AddOracleUnitOfWork( this IAppBuilder builder, string connection, Action setupAction = null, + Action oracleSetupAction = null, bool? condition = null, bool isGuidToString = true ) + where TService : class, IUnitOfWork + where TImplementation : UnitOfWorkBase, TService { + return builder.AddOracleUnitOfWork( connection, null, setupAction, oracleSetupAction, condition, isGuidToString ); + } + + /// + /// 配置Oracle工作单元 + /// + /// 应用生成器 + /// 数据库连接 + /// 工作单元配置操作 + /// Oracle配置操作 + /// 条件,设置为false,跳过配置 + /// 存储时是否将Guid类型转换为字符串类型 + public static IAppBuilder AddOracleUnitOfWork( this IAppBuilder builder, DbConnection connection, Action setupAction = null, + Action oracleSetupAction = null, bool? condition = null, bool isGuidToString = true ) + where TService : class, IUnitOfWork + where TImplementation : UnitOfWorkBase, TService { + return builder.AddOracleUnitOfWork( null, connection, setupAction, oracleSetupAction, condition, isGuidToString ); + } + + /// + /// 配置Oracle工作单元 + /// + private static IAppBuilder AddOracleUnitOfWork( this IAppBuilder builder, string connectionString, DbConnection connection, Action setupAction, + Action oracleSetupAction, bool? condition, bool isGuidToString ) + where TService : class, IUnitOfWork + where TImplementation : UnitOfWorkBase, TService { + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + void Action( DbContextOptionsBuilder options ) { + setupAction?.Invoke( options ); + if ( connectionString.IsEmpty() == false ) { + options.UseOracle( connectionString, oracleSetupAction ); + return; + } + if ( connection != null ) { + options.UseOracle( connection, oracleSetupAction ); + } + } + services.AddDbContext( Action ); + if ( condition == false ) + return; + services.AddDbContext( Action ); + ConfigOptions(services, isGuidToString); + } ); + return builder; + } + + /// + /// 配置Oracle EF配置项 + /// + private static void ConfigOptions( IServiceCollection services, bool isGuidToString ) { + void Action( OracleEntityFrameworkCoreOptions t ) => t.IsGuidToString = isGuidToString; + services.Configure( Action ); + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.Oracle/OracleEntityFrameworkCoreOptions.cs b/src/Util.Data.EntityFrameworkCore.Oracle/OracleEntityFrameworkCoreOptions.cs new file mode 100644 index 000000000..ec4fa811d --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.Oracle/OracleEntityFrameworkCoreOptions.cs @@ -0,0 +1,11 @@ +namespace Util.Data.EntityFrameworkCore; + +/// +/// Oracle EF配置项 +/// +public class OracleEntityFrameworkCoreOptions { + /// + /// 存储时是否将Guid类型转换为字符串类型 + /// + public bool IsGuidToString { get; set; } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.Oracle/OracleUnitOfWorkBase.cs b/src/Util.Data.EntityFrameworkCore.Oracle/OracleUnitOfWorkBase.cs new file mode 100644 index 000000000..0ab9b0d42 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.Oracle/OracleUnitOfWorkBase.cs @@ -0,0 +1,62 @@ +using Microsoft.Extensions.Options; +using Util.Data.EntityFrameworkCore.ValueComparers; +using Util.Data.EntityFrameworkCore.ValueConverters; +using Util.Domain.Extending; +using Util.Properties; + +namespace Util.Data.EntityFrameworkCore; + +/// +/// Oracle工作单元基类 +/// +public abstract class OracleUnitOfWorkBase : UnitOfWorkBase { + /// + /// 初始化Oracle工作单元 + /// + /// 服务提供器 + /// 配置 + protected OracleUnitOfWorkBase( IServiceProvider serviceProvider, DbContextOptions options ) + : base( serviceProvider, options ) { + } + + /// + protected override void ConfigTenantConnectionString( DbContextOptionsBuilder optionsBuilder, string connectionString ) { + optionsBuilder.UseOracle( connectionString ); + } + + /// + /// 配置扩展属性 + /// + /// 模型生成器 + /// 实体类型 + protected override void ApplyExtraProperties( ModelBuilder modelBuilder, IMutableEntityType entityType ) { + if ( typeof( IExtraProperties ).IsAssignableFrom( entityType.ClrType ) == false ) + return; + modelBuilder.Entity( entityType.ClrType ) + .Property( "ExtraProperties" ) + .HasColumnName( "ExtraProperties" ) + .HasComment( R.ExtraProperties ) + .HasColumnType( "CLOB" ) + .HasConversion( new ExtraPropertiesValueConverter() ) + .Metadata.SetValueComparer( new ExtraPropertyDictionaryValueComparer() ); + } + + /// + /// 配置约定 + /// + protected override void ConfigureConventions( ModelConfigurationBuilder configurationBuilder ) { + base.ConfigureConventions( configurationBuilder ); + ConfigGuidToString( configurationBuilder ); + } + + /// + /// 配置将Guid转换为字符串 + /// + protected virtual void ConfigGuidToString( ModelConfigurationBuilder configurationBuilder ) { + var options = ServiceProvider.GetService>(); + if ( options?.Value.IsGuidToString == false ) + return; + configurationBuilder.Properties().HaveConversion(); + configurationBuilder.Properties().HaveConversion(); + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.Oracle/Usings.cs b/src/Util.Data.EntityFrameworkCore.Oracle/Usings.cs new file mode 100644 index 000000000..797d4972e --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.Oracle/Usings.cs @@ -0,0 +1,6 @@ +global using System; +global using System.Data.Common; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.Metadata; +global using Oracle.EntityFrameworkCore.Infrastructure; \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.PostgreSql/03-Util.Data.EntityFrameworkCore.PostgreSql.csproj b/src/Util.Data.EntityFrameworkCore.PostgreSql/03-Util.Data.EntityFrameworkCore.PostgreSql.csproj new file mode 100644 index 000000000..85e8fb529 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.PostgreSql/03-Util.Data.EntityFrameworkCore.PostgreSql.csproj @@ -0,0 +1,37 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Data.EntityFrameworkCore.PostgreSql + Util.Data.EntityFrameworkCore + Util.Data.EntityFrameworkCore.PostgreSql是Util应用框架基于EntityFrameworkCore的数据访问封装类库,用于处理PostgreSql数据库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Data.EntityFrameworkCore.PostgreSql.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Data.EntityFrameworkCore.PostgreSql.xml + + + + + True + False + + + + + + + + + + + + + diff --git a/src/Util.Data.EntityFrameworkCore.PostgreSql/AppBuilderExtensions.cs b/src/Util.Data.EntityFrameworkCore.PostgreSql/AppBuilderExtensions.cs new file mode 100644 index 000000000..1bd63194b --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.PostgreSql/AppBuilderExtensions.cs @@ -0,0 +1,68 @@ +using Util.Configs; + +namespace Util.Data.EntityFrameworkCore; + +/// +/// PostgreSql工作单元操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置PostgreSql工作单元 + /// + /// 应用生成器 + /// 数据库连接字符串 + /// 工作单元配置操作 + /// PostgreSql配置操作 + /// 是否启用传统时间戳行为 + /// 条件,设置为false,跳过配置 + public static IAppBuilder AddPgSqlUnitOfWork( this IAppBuilder builder, string connection, Action setupAction = null, + Action pgSqlSetupAction = null, bool isEnableLegacyTimestampBehavior = false, bool? condition = null ) + where TService : class, IUnitOfWork + where TImplementation : UnitOfWorkBase, TService { + return builder.AddPgSqlUnitOfWork( connection, null, setupAction, pgSqlSetupAction, isEnableLegacyTimestampBehavior, condition ); + } + + /// + /// 配置PostgreSql工作单元 + /// + /// 应用生成器 + /// 数据库连接 + /// 工作单元配置操作 + /// PostgreSql配置操作 + /// 是否启用传统时间戳行为 + /// 条件,设置为false,跳过配置 + public static IAppBuilder AddPgSqlUnitOfWork( this IAppBuilder builder, DbConnection connection, Action setupAction = null, + Action pgSqlSetupAction = null, bool isEnableLegacyTimestampBehavior = false, bool? condition = null ) + where TService : class, IUnitOfWork + where TImplementation : UnitOfWorkBase, TService { + return builder.AddPgSqlUnitOfWork( null, connection, setupAction, pgSqlSetupAction, isEnableLegacyTimestampBehavior, condition ); + } + + /// + /// 配置PostgreSql工作单元 + /// + private static IAppBuilder AddPgSqlUnitOfWork( this IAppBuilder builder, string connectionString, DbConnection connection, Action setupAction, + Action pgSqlSetupAction, bool isEnableLegacyTimestampBehavior, bool? condition ) + where TService : class, IUnitOfWork + where TImplementation : UnitOfWorkBase, TService { + AppContext.SetSwitch( "Npgsql.EnableLegacyTimestampBehavior", isEnableLegacyTimestampBehavior ); + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + void Action( DbContextOptionsBuilder options ) { + setupAction?.Invoke( options ); + if ( connectionString.IsEmpty() == false ) { + options.UseNpgsql( connectionString, pgSqlSetupAction ); + return; + } + if ( connection != null ) { + options.UseNpgsql( connection, pgSqlSetupAction ); + } + } + services.AddDbContext( Action ); + if ( condition == false ) + return; + services.AddDbContext( Action ); + } ); + return builder; + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.PostgreSql/PgSqlUnitOfWorkBase.cs b/src/Util.Data.EntityFrameworkCore.PostgreSql/PgSqlUnitOfWorkBase.cs new file mode 100644 index 000000000..d202494e5 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.PostgreSql/PgSqlUnitOfWorkBase.cs @@ -0,0 +1,20 @@ +namespace Util.Data.EntityFrameworkCore; + +/// +/// PostgreSql工作单元基类 +/// +public abstract class PgSqlUnitOfWorkBase : UnitOfWorkBase { + /// + /// 初始化PostgreSql工作单元 + /// + /// 服务提供器 + /// 配置 + protected PgSqlUnitOfWorkBase( IServiceProvider serviceProvider, DbContextOptions options ) + : base( serviceProvider, options ) { + } + + /// + protected override void ConfigTenantConnectionString( DbContextOptionsBuilder optionsBuilder, string connectionString ) { + optionsBuilder.UseNpgsql( connectionString ); + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.PostgreSql/Usings.cs b/src/Util.Data.EntityFrameworkCore.PostgreSql/Usings.cs new file mode 100644 index 000000000..80c67f2ec --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.PostgreSql/Usings.cs @@ -0,0 +1,5 @@ +global using System; +global using System.Data.Common; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.EntityFrameworkCore; +global using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.SqlServer/02-Util.Data.EntityFrameworkCore.SqlServer.csproj b/src/Util.Data.EntityFrameworkCore.SqlServer/02-Util.Data.EntityFrameworkCore.SqlServer.csproj new file mode 100644 index 000000000..c5dd32d02 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.SqlServer/02-Util.Data.EntityFrameworkCore.SqlServer.csproj @@ -0,0 +1,36 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Data.EntityFrameworkCore.SqlServer + Util.Data.EntityFrameworkCore + Util.Data.EntityFrameworkCore.SqlServer是Util应用框架基于EntityFrameworkCore的数据访问封装类库,用于处理SqlServer数据库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Data.EntityFrameworkCore.SqlServer.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Data.EntityFrameworkCore.SqlServer.xml + + + + + True + False + + + + + + + + + + + + diff --git a/src/Util.Data.EntityFrameworkCore.SqlServer/AppBuilderExtensions.cs b/src/Util.Data.EntityFrameworkCore.SqlServer/AppBuilderExtensions.cs new file mode 100644 index 000000000..e324c9c3c --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.SqlServer/AppBuilderExtensions.cs @@ -0,0 +1,65 @@ +using Util.Configs; + +namespace Util.Data.EntityFrameworkCore; + +/// +/// Sql Server工作单元操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置Sql Server工作单元 + /// + /// 应用生成器 + /// 数据库连接字符串 + /// 工作单元配置操作 + /// Sql Server配置操作 + /// 条件,设置为false,跳过配置 + public static IAppBuilder AddSqlServerUnitOfWork( this IAppBuilder builder, string connection, Action setupAction = null, + Action sqlServerSetupAction = null, bool? condition = null ) + where TService : class, IUnitOfWork + where TImplementation : UnitOfWorkBase, TService { + return builder.AddSqlServerUnitOfWork( connection, null, setupAction, sqlServerSetupAction, condition ); + } + + /// + /// 配置Sql Server工作单元 + /// + /// 应用生成器 + /// 数据库连接 + /// 工作单元配置操作 + /// Sql Server配置操作 + /// 条件,设置为false,跳过配置 + public static IAppBuilder AddSqlServerUnitOfWork( this IAppBuilder builder, DbConnection connection, Action setupAction = null, + Action sqlServerSetupAction = null, bool? condition = null ) + where TService : class, IUnitOfWork + where TImplementation : UnitOfWorkBase, TService { + return builder.AddSqlServerUnitOfWork( null, connection, setupAction, sqlServerSetupAction, condition ); + } + + /// + /// 配置Sql Server工作单元 + /// + private static IAppBuilder AddSqlServerUnitOfWork( this IAppBuilder builder, string connectionString, DbConnection connection, Action setupAction = null, + Action sqlServerSetupAction = null, bool? condition = null ) + where TService : class, IUnitOfWork + where TImplementation : UnitOfWorkBase, TService { + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + void Action( DbContextOptionsBuilder options ) { + setupAction?.Invoke( options ); + if ( connectionString.IsEmpty() == false ) { + options.UseSqlServer( connectionString, sqlServerSetupAction ); + return; + } + if ( connection != null ) { + options.UseSqlServer( connection, sqlServerSetupAction ); + } + } + services.AddDbContext( Action ); + if ( condition == false ) + return; + services.AddDbContext( Action ); + } ); + return builder; + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.SqlServer/SqlServerUnitOfWorkBase.cs b/src/Util.Data.EntityFrameworkCore.SqlServer/SqlServerUnitOfWorkBase.cs new file mode 100644 index 000000000..e950c5b35 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.SqlServer/SqlServerUnitOfWorkBase.cs @@ -0,0 +1,39 @@ +using Util.Domain; + +namespace Util.Data.EntityFrameworkCore; + +/// +/// SqlServer工作单元基类 +/// +public abstract class SqlServerUnitOfWorkBase : UnitOfWorkBase { + /// + /// 初始化SqlServer工作单元 + /// + /// 服务提供器 + /// 配置 + protected SqlServerUnitOfWorkBase( IServiceProvider serviceProvider,DbContextOptions options ) + : base( serviceProvider, options ) { + } + + /// + protected override void ConfigTenantConnectionString( DbContextOptionsBuilder optionsBuilder, string connectionString ) { + optionsBuilder.UseSqlServer( connectionString ); + } + + /// + protected override void ApplyVersion( ModelBuilder modelBuilder, IMutableEntityType entityType ) { + if( typeof( IVersion ).IsAssignableFrom( entityType.ClrType ) == false ) + return; + modelBuilder.Entity( entityType.ClrType ) + .Property( "Version" ) + .HasColumnName( "Version" ) + .IsRowVersion(); + } + + /// + /// 获取版本号 + /// + protected override byte[] GetVersion() { + return null; + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.SqlServer/Usings.cs b/src/Util.Data.EntityFrameworkCore.SqlServer/Usings.cs new file mode 100644 index 000000000..343815cfa --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.SqlServer/Usings.cs @@ -0,0 +1,6 @@ +global using System; +global using System.Data.Common; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.Metadata; +global using Microsoft.EntityFrameworkCore.Infrastructure; \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.Sqlite/05-Util.Data.EntityFrameworkCore.Sqlite.csproj b/src/Util.Data.EntityFrameworkCore.Sqlite/05-Util.Data.EntityFrameworkCore.Sqlite.csproj new file mode 100644 index 000000000..ea1f2a060 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.Sqlite/05-Util.Data.EntityFrameworkCore.Sqlite.csproj @@ -0,0 +1,37 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Data.EntityFrameworkCore.Sqlite + Util.Data.EntityFrameworkCore + Util.Data.EntityFrameworkCore.Sqlite是Util应用框架基于EntityFrameworkCore的数据访问封装类库,用于处理Sqlite数据库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Data.EntityFrameworkCore.Sqlite.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Data.EntityFrameworkCore.Sqlite.xml + + + + + True + False + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.Sqlite/AppBuilderExtensions.cs b/src/Util.Data.EntityFrameworkCore.Sqlite/AppBuilderExtensions.cs new file mode 100644 index 000000000..7a2ebfa2d --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.Sqlite/AppBuilderExtensions.cs @@ -0,0 +1,65 @@ +using Util.Configs; + +namespace Util.Data.EntityFrameworkCore; + +/// +/// Sqlite工作单元操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置Sqlite工作单元 + /// + /// 应用生成器 + /// 数据库连接字符串 + /// 工作单元配置操作 + /// Sqlite配置操作 + /// 条件,设置为false,跳过配置 + public static IAppBuilder AddSqliteUnitOfWork( this IAppBuilder builder, string connection, Action setupAction = null, + Action sqliteSetupAction = null, bool? condition = null ) + where TService : class, IUnitOfWork + where TImplementation : UnitOfWorkBase, TService { + return builder.AddSqliteUnitOfWork( connection, null, setupAction, sqliteSetupAction, condition ); + } + + /// + /// 配置Sqlite工作单元 + /// + /// 应用生成器 + /// 数据库连接 + /// 工作单元配置操作 + /// Sqlite配置操作 + /// 条件,设置为false,跳过配置 + public static IAppBuilder AddSqliteUnitOfWork( this IAppBuilder builder, DbConnection connection, Action setupAction = null, + Action sqliteSetupAction = null, bool? condition = null ) + where TService : class, IUnitOfWork + where TImplementation : UnitOfWorkBase, TService { + return builder.AddSqliteUnitOfWork( null, connection, setupAction, sqliteSetupAction, condition ); + } + + /// + /// 配置Sqlite工作单元 + /// + private static IAppBuilder AddSqliteUnitOfWork( this IAppBuilder builder, string connectionString, DbConnection connection, Action setupAction, + Action sqliteSetupAction, bool? condition ) + where TService : class, IUnitOfWork + where TImplementation : UnitOfWorkBase, TService { + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + void Action( DbContextOptionsBuilder options ) { + setupAction?.Invoke( options ); + if ( connectionString.IsEmpty() == false ) { + options.UseSqlite( connectionString, sqliteSetupAction ); + return; + } + if ( connection != null ) { + options.UseSqlite( connection, sqliteSetupAction ); + } + } + services.AddDbContext( Action ); + if ( condition == false ) + return; + services.AddDbContext( Action ); + } ); + return builder; + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.Sqlite/SqliteUnitOfWorkBase.cs b/src/Util.Data.EntityFrameworkCore.Sqlite/SqliteUnitOfWorkBase.cs new file mode 100644 index 000000000..700558552 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.Sqlite/SqliteUnitOfWorkBase.cs @@ -0,0 +1,20 @@ +namespace Util.Data.EntityFrameworkCore; + +/// +/// Sqlite工作单元基类 +/// +public abstract class SqliteUnitOfWorkBase : UnitOfWorkBase { + /// + /// 初始化Sqlite工作单元 + /// + /// 服务提供器 + /// 配置 + protected SqliteUnitOfWorkBase( IServiceProvider serviceProvider, DbContextOptions options ) + : base( serviceProvider, options ) { + } + + /// + protected override void ConfigTenantConnectionString( DbContextOptionsBuilder optionsBuilder, string connectionString ) { + optionsBuilder.UseSqlite( connectionString ); + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore.Sqlite/Usings.cs b/src/Util.Data.EntityFrameworkCore.Sqlite/Usings.cs new file mode 100644 index 000000000..731033ee7 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore.Sqlite/Usings.cs @@ -0,0 +1,5 @@ +global using System; +global using System.Data.Common; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.Infrastructure; \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/01-Util.Data.EntityFrameworkCore.csproj b/src/Util.Data.EntityFrameworkCore/01-Util.Data.EntityFrameworkCore.csproj new file mode 100644 index 000000000..a779aebff --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/01-Util.Data.EntityFrameworkCore.csproj @@ -0,0 +1,40 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Data.EntityFrameworkCore + Util.Data.EntityFrameworkCore + Util.Data.EntityFrameworkCore是Util应用框架基于EntityFrameworkCore的数据访问封装基础类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Data.EntityFrameworkCore.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Data.EntityFrameworkCore.xml + + + + + True + False + + + + + + + + + + + + + + + + diff --git a/src/Util.Data.EntityFrameworkCore/FilterExtensions.cs b/src/Util.Data.EntityFrameworkCore/FilterExtensions.cs new file mode 100644 index 000000000..1ff8a3dc4 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/FilterExtensions.cs @@ -0,0 +1,38 @@ +namespace Util.Data.EntityFrameworkCore; + +/// +/// 过滤器操作扩展 +/// +public static class FilterExtensions { + /// + /// 启用逻辑删除过滤器 + /// + /// 源 + public static void EnableDeleteFilter( this IFilterOperation source ) { + source.EnableFilter(); + } + + /// + /// 禁用逻辑删除过滤器 + /// + /// 源 + public static IDisposable DisableDeleteFilter( this IFilterOperation source ) { + return source.DisableFilter(); + } + + /// + /// 启用租户过滤器 + /// + /// 源 + public static void EnableTenantFilter( this IFilterOperation source ) { + source.EnableFilter(); + } + + /// + /// 禁用租户过滤器 + /// + /// 源 + public static IDisposable DisableTenantFilter( this IFilterOperation source ) { + return source.DisableFilter(); + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/Filters/DeleteFilter.cs b/src/Util.Data.EntityFrameworkCore/Filters/DeleteFilter.cs new file mode 100644 index 000000000..fa7a600b5 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/Filters/DeleteFilter.cs @@ -0,0 +1,19 @@ +namespace Util.Data.EntityFrameworkCore.Filters; + +/// +/// 逻辑删除过滤器 +/// +public class DeleteFilter : FilterBase { + /// + /// 获取过滤表达式 + /// + /// 实体类型 + public override Expression> GetExpression( object state ) where TEntity : class { + var unitOfWork = state as UnitOfWorkBase; + Expression> expression = entity => !EF.Property( entity, "IsDeleted" ); + if ( unitOfWork == null ) + return expression; + Expression> isEnabled = entity => !unitOfWork.IsDeleteFilterEnabled; + return isEnabled.Or( expression ); + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/Filters/FilterBase.cs b/src/Util.Data.EntityFrameworkCore/Filters/FilterBase.cs new file mode 100644 index 000000000..c24ce2509 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/Filters/FilterBase.cs @@ -0,0 +1,44 @@ +namespace Util.Data.EntityFrameworkCore.Filters; + +/// +/// 数据过滤器基类 +/// +/// 过滤器类型 +public abstract class FilterBase : IFilter where TFilterType : class { + /// + /// 过滤器是否启用 + /// + public virtual bool IsEnabled { get; private set; } = true; + + /// + /// 实体是否启用过滤器 + /// + /// 实体类型 + public virtual bool IsEntityEnabled() { + return typeof(TFilterType).IsAssignableFrom( typeof(TEntity) ); + } + + /// + /// 启用 + /// + public virtual void Enable() { + IsEnabled = true; + } + + /// + /// 禁用 + /// + public virtual IDisposable Disable() { + if ( IsEnabled == false ) + return DisposeAction.Null; + IsEnabled = false; + return new DisposeAction( Enable ); + } + + /// + /// 获取过滤表达式 + /// + /// 实体类型 + /// 参数 + public abstract Expression> GetExpression( object state ) where TEntity : class; +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/Filters/FilterManager.cs b/src/Util.Data.EntityFrameworkCore/Filters/FilterManager.cs new file mode 100644 index 000000000..05fcbf9de --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/Filters/FilterManager.cs @@ -0,0 +1,119 @@ +namespace Util.Data.EntityFrameworkCore.Filters; + +/// +/// 数据过滤器管理器 +/// +public class FilterManager : IFilterManager { + /// + /// 同步锁 + /// + private static readonly object Sync = new(); + /// + /// 过滤器类型列表 + /// + private static readonly List _filterTypes = new(); + /// + /// 过滤器字典 + /// + private readonly Dictionary _filters = new(); + /// + /// 服务提供器 + /// + private readonly IServiceProvider _serviceProvider; + + /// + /// 初始化数据过滤器管理器 + /// + /// 服务提供器 + public FilterManager( IServiceProvider serviceProvider ) { + _serviceProvider = serviceProvider ?? throw new ArgumentNullException( nameof(serviceProvider) ); + } + + /// + /// 添加过滤器类型 + /// + /// 过滤器类型 + public static void AddFilterType() { + var type = typeof( TFilterType ); + lock ( Sync ) { + if( _filterTypes.Contains( type ) ) + return; + _filterTypes.Add( type ); + } + } + + /// + /// 移除过滤器类型 + /// + /// 过滤器类型 + public static void RemoveFilterType() { + var type = typeof( TFilterType ); + lock( Sync ) { + if( _filterTypes.Contains( type ) ) + _filterTypes.Remove( type ); + } + } + + /// + /// 清空过滤器类型 + /// + public static void ClearFilterTypes() { + _filterTypes.Clear(); + } + + /// + public void EnableFilter() where TFilterType : class { + var filter = GetFilter(); + filter?.Enable(); + } + + /// + public IDisposable DisableFilter() where TFilterType : class { + var filter = GetFilter(); + return filter?.Disable(); + } + + /// + public IFilter GetFilter() where TFilterType : class { + return GetFilter( typeof(TFilterType) ); + } + + /// + public IFilter GetFilter( Type filterType ) { + if( _filters.ContainsKey( filterType ) == false ) { + var serviceType = typeof( IFilter<> ).MakeGenericType( filterType ); + var filter = _serviceProvider.GetService( serviceType ); + _filters.Add( filterType, (IFilter)filter ); + } + return _filters[filterType]; + } + + /// + public bool IsEntityEnabled() { + foreach( var type in _filterTypes ) { + var filter = GetFilter( type ); + if ( filter.IsEntityEnabled() ) + return true; + } + return false; + } + + /// + public bool IsEnabled() where TFilterType : class { + var filter = GetFilter(); + if ( filter == null ) + return false; + return filter.IsEnabled; + } + + /// + public Expression> GetExpression( object state ) where TEntity : class { + Expression> expression = null; + foreach ( var type in _filterTypes ) { + var filter = GetFilter( type ); + if ( filter.IsEntityEnabled() ) + expression = expression.And( filter.GetExpression( state ) ); + } + return expression; + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/Filters/TenantFilter.cs b/src/Util.Data.EntityFrameworkCore/Filters/TenantFilter.cs new file mode 100644 index 000000000..7ce5ed569 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/Filters/TenantFilter.cs @@ -0,0 +1,36 @@ +namespace Util.Data.EntityFrameworkCore.Filters; + +/// +/// 租户过滤器 +/// +public class TenantFilter : FilterBase { + /// + /// 租户管理器 + /// + private readonly ITenantManager _manager; + + /// + /// 初始化租户过滤器 + /// + /// 租户管理器 + public TenantFilter( ITenantManager manager ) { + _manager = manager ?? throw new ArgumentNullException( nameof( manager ) ); + } + + /// + /// 获取过滤表达式 + /// + /// 实体类型 + public override Expression> GetExpression( object state ) where TEntity : class { + if ( _manager.Enabled() == false ) + return null; + if ( _manager.IsDisableTenantFilter() ) + return null; + var unitOfWork = state as UnitOfWorkBase; + if ( unitOfWork == null ) + return null; + Expression> isEnabled = entity => !unitOfWork.IsTenantFilterEnabled; + Expression> expression = entity => EF.Property( entity, "TenantId" ) == unitOfWork.CurrentTenantId; + return isEnabled.Or( expression ); + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/Infrastructure/EntityFrameworkServiceRegistrar.cs b/src/Util.Data.EntityFrameworkCore/Infrastructure/EntityFrameworkServiceRegistrar.cs new file mode 100644 index 000000000..0bc989071 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/Infrastructure/EntityFrameworkServiceRegistrar.cs @@ -0,0 +1,33 @@ +using Util.Data.EntityFrameworkCore.Filters; + +namespace Util.Data.EntityFrameworkCore.Infrastructure; + +/// +/// EntityFrameworkCore服务注册器 +/// +public class EntityFrameworkServiceRegistrar : IServiceRegistrar { + /// + /// 获取服务名 + /// + public static string ServiceName => "Util.Data.EntityFrameworkCore.Infrastructure.EntityFrameworkServiceRegistrar"; + + /// + /// 排序号 + /// + public int OrderId => 710; + + /// + /// 是否启用 + /// + public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName ); + + /// + /// 注册服务 + /// + /// 服务上下文 + public Action Register( ServiceContext serviceContext ) { + FilterManager.AddFilterType(); + FilterManager.AddFilterType(); + return null; + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/Infrastructure/ServiceRegistrarConfigExtensions.cs b/src/Util.Data.EntityFrameworkCore/Infrastructure/ServiceRegistrarConfigExtensions.cs new file mode 100644 index 000000000..67d5f55fa --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/Infrastructure/ServiceRegistrarConfigExtensions.cs @@ -0,0 +1,26 @@ +using Util.Infrastructure; + +namespace Util.Data.EntityFrameworkCore.Infrastructure; + +/// +/// EntityFrameworkCore服务注册器配置扩展 +/// +public static class ServiceRegistrarConfigExtensions { + /// + /// 启用EntityFrameworkCore服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig EnableEntityFrameworkServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Enable( EntityFrameworkServiceRegistrar.ServiceName ); + return config; + } + + /// + ///禁用EntityFrameworkCore服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig DisableEntityFrameworkServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Disable( EntityFrameworkServiceRegistrar.ServiceName ); + return config; + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/Migrations/IMigrationFileService.cs b/src/Util.Data.EntityFrameworkCore/Migrations/IMigrationFileService.cs new file mode 100644 index 000000000..7ec47e44d --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/Migrations/IMigrationFileService.cs @@ -0,0 +1,36 @@ +using Util.Dependency; + +namespace Util.Data.EntityFrameworkCore.Migrations; + +/// +/// 迁移文件服务 +/// +public interface IMigrationFileService : ITransientDependency { + /// + /// 设置迁移目录绝对路径,即Migrations目录的绝对路径 + /// + /// 迁移目录绝对路径 + IMigrationFileService MigrationsPath( string path ); + /// + /// 设置迁移名称 + /// + /// 迁移名称,范例: init + IMigrationFileService MigrationName( string name ); + /// + /// 是否移除所有外键,将删除迁移文件中的外键设置 + /// + IMigrationFileService RemoveForeignKeys(); + /// + /// 获取迁移文件路径 + /// + string GetFilePath(); + /// + /// 获取文件内容 + /// + string GetContent(); + /// + /// 保存文件 + /// + /// 文件绝对路径,传入null则覆盖原文件 + void Save( string filePath = null ); +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/Migrations/IMigrationService.cs b/src/Util.Data.EntityFrameworkCore/Migrations/IMigrationService.cs new file mode 100644 index 000000000..bc3500366 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/Migrations/IMigrationService.cs @@ -0,0 +1,35 @@ +using Util.Dependency; + +namespace Util.Data.EntityFrameworkCore.Migrations; + +/// +/// 迁移服务 +/// +public interface IMigrationService : ITransientDependency { + /// + /// 安装 dotnet-ef 全局工具,执行命令: dotnet tool install -g dotnet-ef + /// 安装特定版本,执行命令: dotnet tool install -g dotnet-ef --version 版本号 + /// + IMigrationService InstallEfTool( string version = null ); + /// + /// 卸载 dotnet-ef 全局工具,执行命令: dotnet tool uninstall -g dotnet-ef + /// + IMigrationService UninstallEfTool(); + /// + /// 更新 dotnet-ef 全局工具,执行命令: dotnet tool update -g dotnet-ef + /// + /// 更新为特定版本,执行命令: dotnet tool update -g dotnet-ef --version 版本号 + IMigrationService UpdateEfTool( string version = null ); + /// + /// 添加迁移,执行命令: dotnet ef migrations add migrationName + /// + /// 迁移名称 + /// 数据上下文项目根目录绝对路径,范例: D:\\Test\\src\\Test.Data.SqlServer + /// 是否移除迁移文件中的所有外键 + IMigrationService AddMigration( string migrationName, string dbContextRootPath, bool isRemoveForeignKeys = false ); + /// + /// 执行迁移,执行命令: dotnet ef database update + /// + /// 数据上下文项目根目录绝对路径,范例: D:\\Test\\src\\Test.Data.SqlServer + void Migrate( string dbContextRootPath ); +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/Migrations/MigrationFileService.cs b/src/Util.Data.EntityFrameworkCore/Migrations/MigrationFileService.cs new file mode 100644 index 000000000..4542cbd90 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/Migrations/MigrationFileService.cs @@ -0,0 +1,87 @@ +using Util.Helpers; + +namespace Util.Data.EntityFrameworkCore.Migrations; + +/// +/// 迁移文件服务 +/// +public class MigrationFileService : IMigrationFileService { + /// + /// 日志 + /// + private readonly ILogger _logger; + /// + /// 迁移目录绝对路径 + /// + private string _migrationsPath; + /// + /// 迁移名称 + /// + private string _migrationName; + /// + /// 是否移除所有外键 + /// + private bool _isRemoveForeignKeys; + + /// + /// 初始化迁移文件服务 + /// + /// 日志 + public MigrationFileService( ILogger logger ) { + _logger = logger ?? NullLogger.Instance; + } + + /// + public IMigrationFileService MigrationsPath( string path ) { + _migrationsPath = path; + return this; + } + + /// + public IMigrationFileService MigrationName( string name ) { + _migrationName = name; + return this; + } + + /// + public IMigrationFileService RemoveForeignKeys() { + _isRemoveForeignKeys = true; + return this; + } + + /// + public string GetFilePath() { + if ( _migrationsPath.IsEmpty() ) + return null; + if ( _migrationName.IsEmpty() ) + return null; + var files = Util.Helpers.File.GetAllFiles( _migrationsPath, "*.cs" ); + var file = files.FirstOrDefault( t => t.Name.EndsWith( $"{_migrationName}.cs" ) ); + if ( file == null ) + return null; + return file.FullName; + } + + /// + public string GetContent() { + var filePath = GetFilePath(); + if ( filePath.IsEmpty() ) + return null; + return Util.Helpers.File.ReadToString( filePath ); + } + + /// + public void Save( string filePath = null ) { + if ( _isRemoveForeignKeys == false ) + return; + if ( filePath.IsEmpty() ) + filePath = GetFilePath(); + var content = GetContent(); + var pattern = @"table.ForeignKey\([\s\S]+?\);"; + var result = Util.Helpers.Regex.Replace( content, pattern, "" ); + pattern = @$"\s+{Common.Line}\s+{Common.Line}"; + result = Util.Helpers.Regex.Replace( result, pattern, Common.Line ); + Util.Helpers.File.Write( filePath, result ); + _logger.LogTrace( $"修改迁移文件并保存成功,路径:{filePath}" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/Migrations/MigrationService.cs b/src/Util.Data.EntityFrameworkCore/Migrations/MigrationService.cs new file mode 100644 index 000000000..0007e9af3 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/Migrations/MigrationService.cs @@ -0,0 +1,104 @@ +using Util.Helpers; + +namespace Util.Data.EntityFrameworkCore.Migrations; + +/// +/// 迁移服务 +/// +public class MigrationService : IMigrationService { + /// + /// 日志 + /// + private readonly ILogger _logger; + /// + /// 迁移文件服务 + /// + private readonly IMigrationFileService _migrationFileService; + + /// + /// 初始化迁移服务 + /// + /// 日志 + /// 迁移文件服务 + public MigrationService( ILogger logger, IMigrationFileService migrationFileService ) { + _logger = logger ?? NullLogger.Instance; + _migrationFileService = migrationFileService ?? throw new ArgumentNullException( nameof( migrationFileService ) ); + } + + /// + public IMigrationService InstallEfTool( string version = null ) { + _logger.LogTrace( "准备安装 dotnet-ef 全局工具." ); + var versionArgs = version.IsEmpty() ? null : $" --version {version}"; + CommandLine.Create( "dotnet", $"tool install -g dotnet-ef{versionArgs}" ) + .OutputToMatch( "dotnet-ef" ) + .Log( _logger ) + .Execute(); + return this; + } + + /// + public IMigrationService UninstallEfTool() { + _logger.LogTrace( "准备卸载 dotnet-ef 全局工具." ); + CommandLine.Create( "dotnet", "tool uninstall -g dotnet-ef" ) + .OutputToMatch( "dotnet-ef" ) + .Log( _logger ) + .Execute(); + return this; + } + + /// + public IMigrationService UpdateEfTool( string version = null ) { + _logger.LogTrace( "准备更新 dotnet-ef 全局工具." ); + var versionArgs = version.IsEmpty() ? null : $" --version {version}"; + CommandLine.Create( "dotnet", $"tool update -g dotnet-ef{versionArgs}" ) + .OutputToMatch( "dotnet-ef" ) + .Log( _logger ) + .Execute(); + return this; + } + + /// + public IMigrationService AddMigration( string migrationName, string dbContextRootPath, bool isRemoveForeignKeys = false ) { + if ( migrationName.IsEmpty() ) + throw new ArgumentException( "必须设置迁移名称" ); + if ( dbContextRootPath.IsEmpty() ) + throw new ArgumentException( "必须设置数据上下文项目根目录绝对路径" ); + _logger.LogTrace( "准备添加 ef 迁移." ); + CommandLine.Create( "dotnet", $"ef migrations add {migrationName}" ) + .WorkingDirectory( dbContextRootPath ) + .OutputToMatch( "Done" ) + .OutputToMatch( "used by an existing migration" ) + .Log( _logger ) + .Execute(); + if ( isRemoveForeignKeys ) + RemoveMigrationFileForeignKeys( migrationName, dbContextRootPath ); + return this; + } + + /// + /// 删除迁移文件中的外键设置 + /// + private void RemoveMigrationFileForeignKeys( string migrationName, string dbContextRootPath ) { + _logger.LogTrace( "准备移除迁移文件中的外键设置." ); + var migrationsPath = Common.JoinPath( dbContextRootPath, "Migrations" ); + _migrationFileService + .MigrationsPath( migrationsPath ) + .MigrationName( migrationName ) + .RemoveForeignKeys() + .Save(); + } + + /// + public void Migrate( string dbContextRootPath ) { + _logger.LogTrace( "准备执行 ef 迁移更新数据库." ); + CommandLine.Create( "dotnet", "ef database update" ) + .WorkingDirectory( dbContextRootPath ) + .OutputToMatch( "The ConnectionString property has not been initialized" ) + .OutputToMatch( "The server was not found or was not accessible" ) + .OutputToMatch( "There is already an object named" ) + .OutputToMatch( "Applying migration" ) + .OutputToMatch( "Done" ) + .Log( _logger ) + .Execute(); + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/QueryableExtensions.cs b/src/Util.Data.EntityFrameworkCore/QueryableExtensions.cs new file mode 100644 index 000000000..cba565c73 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/QueryableExtensions.cs @@ -0,0 +1,47 @@ +using Util.Data.Queries; + +namespace Util.Data.EntityFrameworkCore; + +/// +/// 查询对象扩展 +/// +public static class QueryableExtensions { + + #region ToPageList(获取分页列表) + + /// + /// 获取分页列表 + /// + /// 实体类型 + /// 源 + /// 分页参数 + public static PageList ToPageList( this IQueryable source, IPage parameter ) where TEntity : class { + source.CheckNull( nameof( source ) ); + parameter.CheckNull( nameof( parameter ) ); + if( parameter.Total <= 0 ) + parameter.Total = source.Count(); + var list = source.OrderBy( parameter,"Id" ).Skip( parameter.GetSkipCount() ).Take( parameter.PageSize ).ToList(); + return new PageList( parameter, list ); + } + + #endregion + + #region ToPageListAsync(获取分页列表) + + /// + /// 获取分页列表 + /// + /// 实体类型 + /// 源 + /// 分页参数 + public static async Task> ToPageListAsync( this IQueryable source, IPage parameter ) where TEntity : class { + source.CheckNull( nameof( source ) ); + parameter.CheckNull( nameof( parameter ) ); + if( parameter.Total <= 0 ) + parameter.Total = await source.CountAsync(); + var list = await source.OrderBy( parameter, "Id" ).Skip( parameter.GetSkipCount() ).Take( parameter.PageSize ).ToListAsync(); + return new PageList( parameter, list ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/RepositoryBase.cs b/src/Util.Data.EntityFrameworkCore/RepositoryBase.cs new file mode 100644 index 000000000..5d09bdd06 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/RepositoryBase.cs @@ -0,0 +1,33 @@ +using Util.Domain.Entities; +using Util.Domain.Repositories; + +namespace Util.Data.EntityFrameworkCore; + +/// +/// 仓储基类 +/// +/// 实体类型 +public abstract class RepositoryBase : RepositoryBase, IRepository + where TEntity : class, IAggregateRoot { + /// + /// 初始化仓储 + /// + /// 工作单元 + protected RepositoryBase( IUnitOfWork unitOfWork ) + : base( unitOfWork ) { + } +} + +/// +/// 仓储基类 +/// +/// 实体类型 +/// 实体标识类型 +public abstract class RepositoryBase : StoreBase, IRepository where TEntity : class, IAggregateRoot { + /// + /// 初始化仓储 + /// + /// 工作单元 + protected RepositoryBase( IUnitOfWork unitOfWork ) : base( unitOfWork ) { + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/StoreBase.cs b/src/Util.Data.EntityFrameworkCore/StoreBase.cs new file mode 100644 index 000000000..7ec7824c0 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/StoreBase.cs @@ -0,0 +1,521 @@ +using Util.Data.Queries; +using Util.Data.Stores; +using Util.Exceptions; +using Util.Validation; + +namespace Util.Data.EntityFrameworkCore; + +/// +/// 存储器 +/// +/// 对象类型 +public abstract class StoreBase : StoreBase, IStore + where TEntity : class, IKey { + /// + /// 初始化存储器 + /// + /// 工作单元 + protected StoreBase( IUnitOfWork unitOfWork ) : base( unitOfWork ) { + } +} + +/// +/// 存储器 +/// +/// 实体类型 +/// 实体标识类型 +public abstract class StoreBase : IStore, IFilterSwitch, ITrack where TEntity : class, IKey { + + #region 字段 + + /// + /// 是否已释放 + /// + private bool _disposed; + + #endregion + + #region 构造方法 + + /// + /// 初始化存储器 + /// + /// 工作单元 + protected StoreBase( IUnitOfWork unitOfWork ) { + UnitOfWork = (UnitOfWorkBase)unitOfWork; + } + + #endregion + + #region 属性 + + /// + /// 工作单元 + /// + protected UnitOfWorkBase UnitOfWork { get; } + + /// + /// 实体集 + /// + protected DbSet Set => UnitOfWork.Set(); + + /// + /// 查询时是否跟踪对象 + /// + protected bool IsTracking { get; private set; } = true; + + #endregion + + #region NoTracking(设置为不跟踪实体) + + /// + /// 设置为不跟踪实体 + /// + public void NoTracking() { + IsTracking = false; + } + + #endregion + + #region EnableFilter(启用过滤器) + + /// + /// 启用过滤器 + /// + /// 过滤器类型 + public void EnableFilter() where TFilterType : class { + UnitOfWork.EnableFilter(); + } + + #endregion + + #region DisableFilter(禁用过滤器) + + /// + /// 禁用过滤器 + /// + /// 过滤器类型 + public IDisposable DisableFilter() where TFilterType : class { + return UnitOfWork.DisableFilter(); + } + + #endregion + + #region Find(查找实体) + + /// + public IQueryable Find() { + ThrowIfDisposed(); + if ( IsTracking ) + return Set; + var result = Set.AsNoTracking(); + IsTracking = true; + return result; + } + + /// + public IQueryable Find( ICondition condition ) { + return Find().Where( condition ); + } + + /// + public IQueryable Find( Expression> condition ) { + return Find().Where( condition ); + } + + #endregion + + #region FindById(通过标识查找实体) + + /// + public TEntity FindById( object id ) { + ThrowIfDisposed(); + if ( id.SafeString().IsEmpty() ) + return null; + var key = GetKey( id ); + if ( IsTracking ) + return Set.Find( key ); + return Single( t => t.Id.Equals( key ) ); + } + + /// + /// 获取标识 + /// + protected object GetKey( object id ) { + if ( id is TKey ) + return id; + return Util.Helpers.Convert.To( id ); + } + + #endregion + + #region FindByIdAsync(通过标识查找实体) + + /// + public async Task FindByIdAsync( object id, CancellationToken cancellationToken = default ) { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if ( id.SafeString().IsEmpty() ) + return null; + var key = GetKey( id ); + if ( IsTracking ) + return await Set.FindAsync( new[] { key }, cancellationToken ); + return await SingleAsync( t => t.Id.Equals( key ), cancellationToken ); + } + + #endregion + + #region FindByIds(通过标识列表查找实体列表) + + /// + public virtual List FindByIds( params TKey[] ids ) { + return FindByIds( (IEnumerable)ids ); + } + + /// + public virtual List FindByIds( IEnumerable ids ) { + return ids == null ? null : Find( t => ids.Contains( t.Id ) ).ToList(); + } + + /// + public virtual List FindByIds( string ids ) { + return FindByIds( Util.Helpers.Convert.ToList( ids ) ); + } + + #endregion + + #region FindByIdsAsync(通过标识列表查找实体列表) + + /// + public virtual async Task> FindByIdsAsync( params TKey[] ids ) { + return await FindByIdsAsync( (IEnumerable)ids ); + } + + /// + public virtual async Task> FindByIdsAsync( IEnumerable ids, CancellationToken cancellationToken = default ) { + cancellationToken.ThrowIfCancellationRequested(); + return ids == null ? null : await Find( t => ids.Contains( t.Id ) ).ToListAsync( cancellationToken ); + } + + /// + public virtual async Task> FindByIdsAsync( string ids, CancellationToken cancellationToken = default ) { + return await FindByIdsAsync( Util.Helpers.Convert.ToList( ids ), cancellationToken ); + } + + #endregion + + #region Single(查找单个实体) + + /// + public virtual TEntity Single( Expression> condition ) { + return Find().FirstOrDefault( condition ); + } + + /// + public virtual TEntity Single( Expression> condition, Func, IQueryable> action ) { + if ( action == null ) + return Single( condition ); + return action( Find() ).FirstOrDefault( condition ); + } + + #endregion + + #region SingleAsync(查找单个实体) + + /// + public virtual async Task SingleAsync( Expression> condition, CancellationToken cancellationToken = default ) { + cancellationToken.ThrowIfCancellationRequested(); + return await Find().FirstOrDefaultAsync( condition, cancellationToken ); + } + + /// + public virtual async Task SingleAsync( Expression> condition, Func, IQueryable> action, CancellationToken cancellationToken = default ) { + cancellationToken.ThrowIfCancellationRequested(); + if ( action == null ) + return await SingleAsync( condition, cancellationToken ); + return await action( Find() ).FirstOrDefaultAsync( condition, cancellationToken ); + } + + #endregion + + #region FindAll(查找实体列表) + + /// + public virtual List FindAll( Expression> condition = null ) { + return condition == null ? Find().ToList() : Find( condition ).ToList(); + } + + #endregion + + #region FindAllAsync(查找实体列表) + + /// + public virtual async Task> FindAllAsync( Expression> condition = null, CancellationToken cancellationToken = default ) { + cancellationToken.ThrowIfCancellationRequested(); + if ( condition == null ) + return await Find().ToListAsync( cancellationToken ); + return await Find( condition ).ToListAsync( cancellationToken ); + } + + #endregion + + #region Exists(判断是否存在) + + /// + public virtual bool Exists( params TKey[] ids ) { + return ids != null && Exists( t => ids.Contains( t.Id ) ); + } + + /// + public virtual bool Exists( Expression> condition ) { + return condition != null && Find().Any( condition ); + } + + #endregion + + #region ExistsAsync(判断是否存在) + + /// + public virtual async Task ExistsAsync( params TKey[] ids ) { + return ids != null && await ExistsAsync( t => ids.Contains( t.Id ) ); + } + + /// + public virtual async Task ExistsAsync( Expression> condition, CancellationToken cancellationToken = default ) { + cancellationToken.ThrowIfCancellationRequested(); + return condition != null && await Find().AnyAsync( condition, cancellationToken ); + } + + #endregion + + #region Count(查找数量) + + /// + public virtual int Count( Expression> condition = null ) { + return condition == null ? Find().Count() : Find().Count( condition ); + } + + #endregion + + #region CountAsync(查找数量) + + /// + public virtual async Task CountAsync( Expression> condition = null, CancellationToken cancellationToken = default ) { + cancellationToken.ThrowIfCancellationRequested(); + return condition == null + ? await Find().CountAsync( cancellationToken ) + : await Find().CountAsync( condition, cancellationToken ); + } + + #endregion + + #region AddAsync(添加实体) + + /// + /// 添加实体 + /// + /// 实体 + /// 取消令牌 + public virtual async Task AddAsync( TEntity entity, CancellationToken cancellationToken = default ) { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + Validate( entity ); + await Set.AddAsync( entity, cancellationToken ); + } + + /// + /// 验证 + /// + protected virtual void Validate( IEnumerable entities ) { + entities.CheckNull( nameof( entities ) ); + foreach ( var entity in entities ) + Validate( entity ); + } + + /// + /// 验证 + /// + protected virtual void Validate( TEntity entity ) { + entity.CheckNull( nameof( entity ) ); + if ( entity is IValidation validation ) + validation.Validate(); + } + + /// + /// 添加实体集合 + /// + /// 实体集合 + /// 取消令牌 + public virtual async Task AddAsync( IEnumerable entities, CancellationToken cancellationToken = default ) { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + var list = entities.ToList(); + Validate( list ); + await Set.AddRangeAsync( list, cancellationToken ); + } + + #endregion + + #region UpdateAsync(修改实体) + + /// + public virtual Task UpdateAsync( TEntity entity, CancellationToken cancellationToken = default ) { + cancellationToken.ThrowIfCancellationRequested(); + Update( entity ); + return Task.CompletedTask; + } + + /// + /// 更新 + /// + protected void Update( TEntity entity ) { + ThrowIfDisposed(); + Validate( entity ); + var entry = UnitOfWork.Entry( entity ); + ValidateVersion( entry, entity ); + UpdateEntity( entry, entity ); + } + + /// + /// 验证版本号 + /// + protected virtual void ValidateVersion( EntityEntry entry, TEntity entity ) { + if ( entity is not IVersion current ) + return; + if ( entry.State == EntityState.Added ) + return; + if ( current.Version == null || current.Version.Length == 0 ) { + ThrowConcurrencyException( entity ); + return; + } + var oldVersion = entry.OriginalValues.GetValue( "Version" ); + for ( int i = 0; i < oldVersion.Length; i++ ) { + if ( current.Version[i] != oldVersion[i] ) + ThrowConcurrencyException( entity ); + } + } + + /// + /// 抛出并发异常 + /// + private void ThrowConcurrencyException( TEntity entity ) { + throw new ConcurrencyException( new Exception( $"Type:{typeof( TEntity )},Id:{entity.Id}" ) ); + } + + /// + /// 更新实体 + /// + protected void UpdateEntity( EntityEntry entry, TEntity entity ) { + var oldEntry = UnitOfWork.ChangeTracker.Entries().FirstOrDefault( t => t.Entity.Equals( entity ) ); + if ( oldEntry != null ) { + oldEntry.CurrentValues.SetValues( entity ); + return; + } + if ( entry.State == EntityState.Detached ) + UnitOfWork.Update( entity ); + } + + /// + public virtual Task UpdateAsync( IEnumerable entities, CancellationToken cancellationToken = default ) { + cancellationToken.ThrowIfCancellationRequested(); + Update( entities ); + return Task.CompletedTask; + } + + /// + /// 更新实体集合 + /// + protected void Update( IEnumerable entities ) { + ThrowIfDisposed(); + entities.CheckNull( nameof( entities ) ); + foreach ( var entity in entities ) + Update( entity ); + } + + #endregion + + #region RemoveAsync(移除实体) + + /// + public virtual async Task RemoveAsync( object id, CancellationToken cancellationToken = default ) { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + var entity = await FindByIdAsync( id, cancellationToken ); + Delete( entity ); + } + + /// + /// 删除 + /// + private void Delete( TEntity entity ) { + if ( entity == null ) + return; + if ( entity is IDelete model ) { + model.IsDeleted = true; + return; + } + Set.Remove( entity ); + } + + /// + public virtual async Task RemoveAsync( TEntity entity, CancellationToken cancellationToken = default ) { + if ( entity == null ) + return; + await RemoveAsync( entity.Id, cancellationToken ); + } + + /// + public virtual async Task RemoveAsync( IEnumerable ids, CancellationToken cancellationToken = default ) { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if ( ids == null ) + return; + var entities = await FindByIdsAsync( ids, cancellationToken ); + Delete( entities ); + } + + /// + /// 删除实体集合 + /// + private void Delete( List list ) { + if ( list == null ) + return; + if ( !list.Any() ) + return; + foreach ( var entity in list ) + Delete( entity ); + } + + /// + public virtual async Task RemoveAsync( IEnumerable entities, CancellationToken cancellationToken = default ) { + if ( entities == null ) + return; + await RemoveAsync( entities.Select( t => t.Id ), cancellationToken ); + } + + #endregion + + #region ThrowIfDisposed(已释放则抛出异常) + + /// + /// 已释放则抛出异常 + /// + protected void ThrowIfDisposed() { + if ( _disposed ) { + throw new ObjectDisposedException( GetType().Name ); + } + } + + #endregion + + #region Dispose(释放) + + /// + /// 释放 + /// + public void Dispose() { + _disposed = true; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/Trees/TreeRepositoryBase.cs b/src/Util.Data.EntityFrameworkCore/Trees/TreeRepositoryBase.cs new file mode 100644 index 000000000..550006bb4 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/Trees/TreeRepositoryBase.cs @@ -0,0 +1,42 @@ +using Util.Domain.Trees; + +namespace Util.Data.EntityFrameworkCore.Trees; + +/// +/// 树形仓储 +/// +/// 实体类型 +public abstract class TreeRepositoryBase : TreeRepositoryBase, ITreeRepository + where TEntity : class, ITreeEntity { + /// + /// 初始化树形仓储 + /// + /// 工作单元 + protected TreeRepositoryBase( IUnitOfWork unitOfWork ) : base( unitOfWork ) { + } +} + +/// +/// 树形仓储 +/// +/// 实体类型 +/// 实体标识类型 +/// 父标识类型 +public abstract class TreeRepositoryBase : RepositoryBase, ITreeRepository + where TEntity : class, ITreeEntity { + /// + /// 初始化树形仓储 + /// + /// 工作单元 + protected TreeRepositoryBase( IUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + + /// + /// 获取全部下级实体 + /// + /// 父实体 + public virtual async Task> GetAllChildrenAsync( TEntity parent ) { + var list = await FindAllAsync( t => t.Path.StartsWith( parent.Path ) ); + return list.Where( t => !t.Id.Equals( parent.Id ) ).ToList(); + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/UnitOfWorkBase.cs b/src/Util.Data.EntityFrameworkCore/UnitOfWorkBase.cs new file mode 100644 index 000000000..1bc178a06 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/UnitOfWorkBase.cs @@ -0,0 +1,758 @@ +using Util.Data.EntityFrameworkCore.ValueComparers; +using Util.Data.EntityFrameworkCore.ValueConverters; +using Util.Dates; +using Util.Domain.Auditing; +using Util.Domain.Compare; +using Util.Domain.Events; +using Util.Domain.Extending; +using Util.Events; +using Util.Exceptions; +using Util.Helpers; +using Util.Properties; +using Util.Sessions; + +namespace Util.Data.EntityFrameworkCore; + +/// +/// 工作单元基类 +/// +public abstract class UnitOfWorkBase : DbContext, IUnitOfWork, IFilterSwitch { + + #region 构造方法 + + /// + /// 初始化工作单元 + /// + /// 服务提供器 + /// 配置 + protected UnitOfWorkBase( IServiceProvider serviceProvider, DbContextOptions options ) + : base( options ) { + ServiceProvider = serviceProvider ?? throw new ArgumentNullException( nameof( serviceProvider ) ); + Environment = serviceProvider.GetService(); + FilterManager = ServiceProvider.GetService(); + TenantManager = ServiceProvider.GetService() ?? NullTenantManager.Instance; + Session = serviceProvider.GetService() ?? NullSession.Instance; + EventBus = serviceProvider.GetService() ?? NullEventBus.Instance; + ActionManager = serviceProvider.GetService() ?? NullUnitOfWorkActionManager.Instance; + SaveBeforeEvents = new List(); + SaveAfterEvents = new List(); + } + + #endregion + + #region 属性 + + /// + /// 服务提供器 + /// + protected IServiceProvider ServiceProvider { get; } + /// + /// 执行环境 + /// + protected IHostEnvironment Environment { get; } + /// + /// 用户会话 + /// + protected ISession Session { get; set; } + /// + /// 数据过滤器管理器 + /// + protected IFilterManager FilterManager { get; } + /// + /// 租户管理器 + /// + protected ITenantManager TenantManager { get; } + /// + /// 事件总线 + /// + protected IEventBus EventBus { get; } + /// + /// 工作单元操作管理器 + /// + protected IUnitOfWorkActionManager ActionManager { get; } + /// + /// 保存前发送的事件集合 + /// + protected List SaveBeforeEvents { get; } + /// + /// 保存后发送的事件集合 + /// + protected List SaveAfterEvents { get; } + /// + /// 逻辑删除过滤器是否启用 + /// + public virtual bool IsDeleteFilterEnabled => FilterManager?.IsEnabled() ?? false; + /// + /// 租户过滤器是否启用 + /// + public virtual bool IsTenantFilterEnabled => FilterManager?.IsEnabled() ?? false; + /// + /// 当前租户标识 + /// + public virtual string CurrentTenantId => TenantManager.GetTenantId(); + /// + /// 是否清除字符串两端的空白,默认为true + /// + protected virtual bool IsTrimString => true; + + #endregion + + #region 辅助操作 + + /// + /// 获取用户标识 + /// + protected virtual string GetUserId() { + return Session.UserId; + } + + #endregion + + #region EnableFilter(启用过滤器) + + /// + /// 启用过滤器 + /// + /// 过滤器类型 + public void EnableFilter() where TFilterType : class { + FilterManager?.EnableFilter(); + } + + #endregion + + #region DisableFilter(禁用过滤器) + + /// + /// 禁用过滤器 + /// + /// 过滤器类型 + public IDisposable DisableFilter() where TFilterType : class { + if ( FilterManager == null ) + return DisposeAction.Null; + return FilterManager.DisableFilter(); + } + + #endregion + + #region OnConfiguring(配置) + + /// + /// 配置 + /// + /// 配置生成器 + protected override void OnConfiguring( DbContextOptionsBuilder optionsBuilder ) { + ConfigLog( optionsBuilder ); + ConfigTenant( optionsBuilder ); + } + + #endregion + + #region ConfigLog(配置日志) + + /// + /// 配置日志 + /// + /// 配置生成器 + protected virtual void ConfigLog( DbContextOptionsBuilder optionsBuilder ) { + if ( Environment == null ) + return; + if ( Environment.IsProduction() ) + return; + optionsBuilder.EnableDetailedErrors().EnableSensitiveDataLogging(); + } + + #endregion + + #region ConfigTenant(配置租户) + + /// + /// 配置租户 + /// + /// 配置生成器 + protected virtual void ConfigTenant( DbContextOptionsBuilder optionsBuilder ) { + if ( TenantManager.Enabled() == false ) + return; + if ( TenantManager.AllowMultipleDatabase() == false ) + return; + var tenant = TenantManager.GetTenant(); + if ( tenant == null ) + return; + var name = ConnectionStringNameAttribute.GetName( GetType() ); + var connectionString = tenant.ConnectionStrings.GetConnectionString( name ); + if ( connectionString.IsEmpty() ) + return; + ConfigTenantConnectionString( optionsBuilder, connectionString ); + } + + /// + /// 配置租户连接字符串 + /// + /// 配置生成器 + /// 连接字符串 + protected virtual void ConfigTenantConnectionString( DbContextOptionsBuilder optionsBuilder, string connectionString ) { + } + + #endregion + + #region OnModelCreating(配置模型) + + /// + /// 配置模型 + /// + /// 模型生成器 + protected override void OnModelCreating( ModelBuilder modelBuilder ) { + ApplyConfigurations( modelBuilder ); + foreach ( var entityType in modelBuilder.Model.GetEntityTypes() ) { + ApplyFilters( modelBuilder, entityType ); + ApplyExtraProperties( modelBuilder, entityType ); + ApplyVersion( modelBuilder, entityType ); + ApplyIsDeleted( modelBuilder, entityType ); + ApplyTenantId( modelBuilder, entityType ); + ApplyUtc( modelBuilder, entityType ); + ApplyTrimString( modelBuilder, entityType ); + } + } + + #endregion + + #region ApplyConfigurations(配置实体类型) + + /// + /// 配置实体类型 + /// + /// 模型生成器 + protected virtual void ApplyConfigurations( ModelBuilder modelBuilder ) { + modelBuilder.ApplyConfigurationsFromAssembly( GetType().Assembly ); + } + + #endregion + + #region ApplyFilters(配置过滤器) + + /// + /// 配置过滤器 + /// + /// 模型生成器 + /// 实体类型 + protected virtual void ApplyFilters( ModelBuilder modelBuilder, IMutableEntityType entityType ) { + var method = GetType().GetMethod( nameof( ApplyFiltersImp ), BindingFlags.Instance | BindingFlags.NonPublic ); + method?.MakeGenericMethod( entityType.ClrType ).Invoke( this, new object[] { modelBuilder } ); + } + + /// + /// 配置过滤器 + /// + /// 模型生成器 + protected virtual void ApplyFiltersImp( ModelBuilder modelBuilder ) where TEntity : class { + if ( FilterManager == null ) + return; + if ( FilterManager.IsEntityEnabled() == false ) + return; + var expression = GetFilterExpression(); + if ( expression == null ) + return; + modelBuilder.Entity().HasQueryFilter( expression ); + } + + /// + /// 获取过滤器表达式 + /// + /// 实体类型 + protected virtual Expression> GetFilterExpression() where TEntity : class { + return FilterManager.GetExpression( this ); + } + + #endregion + + #region ApplyExtraProperties(配置扩展属性) + + /// + /// 配置扩展属性 + /// + /// 模型生成器 + /// 实体类型 + protected virtual void ApplyExtraProperties( ModelBuilder modelBuilder, IMutableEntityType entityType ) { + if ( typeof( IExtraProperties ).IsAssignableFrom( entityType.ClrType ) == false ) + return; + modelBuilder.Entity( entityType.ClrType ) + .Property( "ExtraProperties" ) + .HasColumnName( "ExtraProperties" ) + .HasComment( R.ExtraProperties ) + .HasConversion( new ExtraPropertiesValueConverter() ) + .Metadata.SetValueComparer( new ExtraPropertyDictionaryValueComparer() ); + } + + #endregion + + #region ApplyVersion(配置乐观锁) + + /// + /// 配置乐观锁 + /// + /// 模型生成器 + /// 实体类型 + protected virtual void ApplyVersion( ModelBuilder modelBuilder, IMutableEntityType entityType ) { + if ( typeof( IVersion ).IsAssignableFrom( entityType.ClrType ) == false ) + return; + modelBuilder.Entity( entityType.ClrType ) + .Property( "Version" ) + .HasColumnName( "Version" ) + .HasComment( R.Version ) + .IsConcurrencyToken(); + } + + #endregion + + #region ApplyIsDeleted(配置逻辑删除) + + /// + /// 配置逻辑删除 + /// + /// 模型生成器 + /// 实体类型 + protected virtual void ApplyIsDeleted( ModelBuilder modelBuilder, IMutableEntityType entityType ) { + if ( typeof( IDelete ).IsAssignableFrom( entityType.ClrType ) == false ) + return; + modelBuilder.Entity( entityType.ClrType ) + .Property( "IsDeleted" ) + .HasColumnName( "IsDeleted" ) + .HasComment( R.IsDeleted ); + } + + #endregion + + #region ApplyTenantId(配置租户标识) + + /// + /// 配置租户标识 + /// + /// 模型生成器 + /// 实体类型 + protected virtual void ApplyTenantId( ModelBuilder modelBuilder, IMutableEntityType entityType ) { + if ( typeof( ITenant ).IsAssignableFrom( entityType.ClrType ) == false ) + return; + modelBuilder.Entity( entityType.ClrType ) + .Property( "TenantId" ) + .HasColumnName( "TenantId" ) + .HasComment( R.TenantId ); + } + + #endregion + + #region ApplyUtc(配置Utc日期) + + /// + /// 配置Utc日期 + /// + /// 模型生成器 + /// 实体类型 + protected virtual void ApplyUtc( ModelBuilder modelBuilder, IMutableEntityType entityType ) { + if ( TimeOptions.IsUseUtc == false ) + return; + var properties = entityType.ClrType.GetProperties() + .Where( property => ( property.PropertyType == typeof( DateTime ) || property.PropertyType == typeof( DateTime? ) ) && property.CanWrite && + property.GetCustomAttribute() == null ) + .ToList(); + properties.ForEach( property => { + modelBuilder.Entity( entityType.ClrType ) + .Property( property.Name ) + .HasConversion( new DateTimeValueConverter() ); + } ); + } + + #endregion + + #region ApplyTrimString(配置清除空白字符串) + + /// + /// 配置清除空白字符串 + /// + /// 模型生成器 + /// 实体类型 + protected virtual void ApplyTrimString( ModelBuilder modelBuilder, IMutableEntityType entityType ) { + if ( IsTrimString == false ) + return; + var properties = entityType.ClrType.GetProperties() + .Where( property => property.PropertyType == typeof( string ) && property.CanWrite && + property.GetCustomAttribute() == null ) + .ToList(); + properties.ForEach( property => { + modelBuilder.Entity( entityType.ClrType ) + .Property( property.Name ) + .HasConversion( new TrimStringValueConverter() ); + } ); + } + + #endregion + + #region CommitAsync(提交) + + /// + /// 提交,返回影响的行数 + /// + public async Task CommitAsync() { + try { + return await SaveChangesAsync(); + } + catch ( DbUpdateConcurrencyException ex ) { + throw new ConcurrencyException( ex ); + } + } + + #endregion + + #region SaveChangesAsync(保存) + + /// + /// 保存 + /// + public override async Task SaveChangesAsync( CancellationToken cancellationToken = default ) { + await SaveChangesBefore(); + var result = await base.SaveChangesAsync( cancellationToken ); + await SaveChangesAfter(); + return result; + } + + #endregion + + #region SaveChangesBefore(保存前操作) + + /// + /// 保存前操作 + /// + protected virtual async Task SaveChangesBefore() { + foreach ( var entry in ChangeTracker.Entries() ) { + UpdateTenantId( entry ); + AddDomainEvents( entry ); + switch ( entry.State ) { + case EntityState.Added: + AddBefore( entry ); + break; + case EntityState.Modified: + UpdateBefore( entry ); + break; + case EntityState.Deleted: + DeleteBefore( entry ); + break; + } + } + await PublishSaveBeforeEventsAsync(); + } + + #endregion + + #region UpdateTenantId(更新租户标识) + + /// + /// 更新租户标识 + /// + protected virtual void UpdateTenantId( EntityEntry entry ) { + if ( TenantManager.Enabled() == false ) + return; + if ( entry.Entity is not ITenant tenant ) + return; + var tenantId = TenantManager.GetTenantId(); + if ( tenantId.IsEmpty() ) + return; + tenant.TenantId = tenantId; + } + + #endregion + + #region AddDomainEvents(添加领域事件) + + /// + /// 添加领域事件 + /// + protected virtual void AddDomainEvents( EntityEntry entry ) { + if ( entry.Entity is not IDomainEventManager eventManager ) + return; + if ( eventManager.DomainEvents == null ) + return; + foreach ( var domainEvent in eventManager.DomainEvents ) { + if ( domainEvent is IIntegrationEvent ) { + SaveAfterEvents.Add( domainEvent ); + continue; + } + SaveBeforeEvents.Add( domainEvent ); + } + eventManager.ClearDomainEvents(); + } + + #endregion + + #region AddBefore(添加前操作) + + /// + /// 添加前操作 + /// + protected virtual void AddBefore( EntityEntry entry ) { + SetCreationAudited( entry.Entity ); + SetModificationAudited( entry.Entity ); + SetVersion( entry.Entity ); + AddEntityCreatedEvent( entry.Entity ); + } + + #endregion + + #region UpdateBefore(修改前操作) + + /// + /// 修改前操作 + /// + protected virtual void UpdateBefore( EntityEntry entry ) { + SetModificationAudited( entry.Entity ); + SetVersion( entry.Entity ); + AddEntityUpdatedEvent( entry ); + } + + #endregion + + #region DeleteBefore(删除前操作) + + /// + /// 删除前操作 + /// + protected virtual void DeleteBefore( EntityEntry entry ) { + SetModificationAudited( entry.Entity ); + AddEntityDeletedEvent( entry.Entity ); + } + + #endregion + + #region SetCreationAudited(设置创建审计信息) + + /// + /// 设置创建审计信息 + /// + protected virtual void SetCreationAudited( object entity ) { + CreationAuditedSetter.Set( entity, GetUserId() ); + } + + #endregion + + #region SetModificationAudited(设置修改审计信息) + + /// + /// 设置修改审计信息 + /// + protected virtual void SetModificationAudited( object entity ) { + ModificationAuditedSetter.Set( entity, GetUserId() ); + } + + #endregion + + #region SetVersion(设置版本号) + + /// + /// 设置版本号 + /// + protected virtual void SetVersion( object obj ) { + if ( !( obj is IVersion entity ) ) + return; + var version = GetVersion(); + if ( version == null ) + return; + entity.Version = version; + } + + #endregion + + #region GetVersion(获取版本号) + + /// + /// 获取版本号 + /// + protected virtual byte[] GetVersion() { + return Encoding.UTF8.GetBytes( Guid.NewGuid().ToString() ); + } + + #endregion + + #region AddEntityChangedEvent(添加实体变更事件) + + /// + /// 添加实体变更事件 + /// + protected virtual void AddEntityChangedEvent( object entity, EntityChangeType changeType, ChangeValueCollection changeValues = null ) { + var eventType = typeof( EntityChangedEvent<> ).MakeGenericType( entity.GetType() ); + var @event = Reflection.CreateInstance( eventType, entity, changeType, changeValues ); + SaveAfterEvents.Add( @event ); + } + + #endregion + + #region AddEntityCreatedEvent(添加实体创建事件) + + /// + /// 添加实体创建事件 + /// + protected virtual void AddEntityCreatedEvent( object entity ) { + var @event = CreateEntityEvent( typeof( EntityCreatedEvent<> ), entity ); + SaveAfterEvents.Add( @event ); + AddEntityChangedEvent( entity, EntityChangeType.Created ); + } + + /// + /// 创建实体事件 + /// + protected IEvent CreateEntityEvent( Type eventType, object entity, object parameter = null ) { + var eventGenericType = eventType.MakeGenericType( entity.GetType() ); + if ( parameter == null ) + return Reflection.CreateInstance( eventGenericType, entity ); + return Reflection.CreateInstance( eventGenericType, entity, parameter ); + } + + #endregion + + #region AddEntityUpdatedEvent(添加实体修改事件) + + /// + /// 添加实体修改事件 + /// + protected virtual void AddEntityUpdatedEvent( EntityEntry entry ) { + var entity = entry.Entity; + if ( entity is IDelete { IsDeleted: true } ) { + AddEntityDeletedEvent( entity ); + return; + } + var changeValues = GetChangeValues( entry ); + var @event = CreateEntityEvent( typeof( EntityUpdatedEvent<> ), entity, changeValues ); + SaveAfterEvents.Add( @event ); + AddEntityChangedEvent( entity, EntityChangeType.Updated, changeValues ); + } + + #endregion + + #region GetChangeValues(获取变更值集合) + + /// + /// 获取变更值集合 + /// + protected virtual ChangeValueCollection GetChangeValues( EntityEntry entry ) { + var result = new ChangeValueCollection(); + var properties = entry.Metadata.GetProperties(); + foreach ( var property in properties ) { + var propertyEntry = entry.Property( property.Name ); + if ( property.Name == "ExtraProperties" ) { + var changeValues = GetExtraPropertiesChangeValues( property, propertyEntry ); + changeValues.ForEach( value => { + if ( value != null ) + result.Add( value ); + } ); + continue; + } + var changeValue = GetPropertyChangeValue( property, propertyEntry ); + if ( changeValue == null ) + continue; + result.Add( changeValue ); + } + return result; + } + + /// + /// 获取扩展属性变更值集合 + /// + protected virtual List GetExtraPropertiesChangeValues( IProperty property, PropertyEntry propertyEntry ) { + var result = new List(); + if ( propertyEntry.CurrentValue is not ExtraPropertyDictionary currentExtraValue ) + return result; + if ( propertyEntry.OriginalValue is not ExtraPropertyDictionary originalExtraValue ) + return result; + foreach ( var key in currentExtraValue.Keys ) { + currentExtraValue.TryGetValue( key, out var current ); + originalExtraValue.TryGetValue( key, out var original ); + var currentValue = Util.Helpers.Json.ToJson( current ); + var originalValue = Util.Helpers.Json.ToJson( original ); + result.Add( ToChangeValue( key, property.Name, originalValue, currentValue ) ); + } + return result; + } + + /// + /// 转换为变更值 + /// + protected virtual ChangeValue ToChangeValue( string name, string description, string originalValue, string currentValue ) { + if ( originalValue == "[]" ) + originalValue = string.Empty; + if ( currentValue == "[]" ) + currentValue = string.Empty; + if ( originalValue == currentValue ) + return null; + return new ChangeValue( name, description, originalValue, currentValue ); + } + + /// + /// 获取属性变更值 + /// + protected virtual ChangeValue GetPropertyChangeValue( IProperty property, PropertyEntry propertyEntry ) { + var description = Reflection.GetDisplayNameOrDescription( property.PropertyInfo ); + return ToChangeValue( property.Name, description, propertyEntry.OriginalValue.SafeString(), propertyEntry.CurrentValue.SafeString() ); + } + + #endregion + + #region AddEntityDeletedEvent(添加实体删除事件) + + /// + /// 添加实体删除事件 + /// + protected virtual void AddEntityDeletedEvent( object entity ) { + var @event = CreateEntityEvent( typeof( EntityDeletedEvent<> ), entity ); + SaveAfterEvents.Add( @event ); + AddEntityChangedEvent( entity, EntityChangeType.Deleted ); + } + + #endregion + + #region PublishSaveBeforeEventsAsync(发布保存前事件) + + /// + /// 发布保存前事件 + /// + protected virtual async Task PublishSaveBeforeEventsAsync() { + if ( SaveBeforeEvents.Count == 0 ) + return; + var events = new List( SaveBeforeEvents ); + SaveBeforeEvents.Clear(); + await EventBus.PublishAsync( events ); + } + + #endregion + + #region SaveChangesAfter(保存后操作) + + /// + /// 保存后操作 + /// + protected virtual async Task SaveChangesAfter() { + await PublishSaveAfterEventsAsync(); + await ExecuteActionsAsync(); + } + + #endregion + + #region PublishSaveAfterEventsAsync(发布保存后事件) + + /// + /// 发布保存后事件 + /// + protected virtual async Task PublishSaveAfterEventsAsync() { + var events = new List( SaveAfterEvents ); + SaveAfterEvents.Clear(); + await EventBus.PublishAsync( events ); + } + + #endregion + + #region ExecuteActionsAsync(执行工作单元操作集合) + + /// + /// 执行工作单元操作集合 + /// + protected virtual async Task ExecuteActionsAsync() { + await ActionManager.ExecuteAsync(); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/UnitOfWorkExtensions.cs b/src/Util.Data.EntityFrameworkCore/UnitOfWorkExtensions.cs new file mode 100644 index 000000000..c87450ad7 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/UnitOfWorkExtensions.cs @@ -0,0 +1,120 @@ +namespace Util.Data.EntityFrameworkCore; + +/// +/// 工作单元扩展 +/// +public static class UnitOfWorkExtensions { + /// + /// 获取已应用的迁移列表 + /// + /// 工作单元 + public static async Task> GetAppliedMigrationsAsync( this IUnitOfWork source ) { + source.CheckNull( nameof( source ) ); + if ( source is not DbContext unitOfWork ) + return new List(); + var result = await unitOfWork.Database.GetAppliedMigrationsAsync(); + return result.ToList(); + } + + /// + /// 应用迁移 + /// + /// 工作单元 + /// 取消令牌 + public static async Task MigrateAsync( this IUnitOfWork source, CancellationToken cancellationToken = default ) { + source.CheckNull( nameof( source ) ); + if ( source is not DbContext unitOfWork ) + return; + await unitOfWork.Database.MigrateAsync( cancellationToken ); + } + + /// + /// 创建数据库架构 + /// + /// 工作单元 + public static bool EnsureCreated( this IUnitOfWork source ) { + source.CheckNull( nameof( source ) ); + if( source is not DbContext unitOfWork ) + return false; + return unitOfWork.Database.EnsureCreated(); + } + + /// + /// 创建数据库架构 + /// + /// 工作单元 + public static async Task EnsureCreatedAsync( this IUnitOfWork source ) { + source.CheckNull( nameof( source ) ); + if( source is not DbContext unitOfWork ) + return false; + return await unitOfWork.Database.EnsureCreatedAsync(); + } + + /// + /// 删除数据库架构 + /// + /// 工作单元 + public static bool EnsureDeleted( this IUnitOfWork source ) { + source.CheckNull( nameof( source ) ); + if( source is not DbContext unitOfWork ) + return false; + return unitOfWork.Database.EnsureDeleted(); + } + + /// + /// 删除数据库架构 + /// + /// 工作单元 + public static async Task EnsureDeletedAsync( this IUnitOfWork source ) { + source.CheckNull( nameof( source ) ); + if( source is not DbContext unitOfWork ) + return false; + return await unitOfWork.Database.EnsureDeletedAsync(); + } + + /// + /// 清空缓存 + /// + /// 工作单元 + public static void ClearCache( this IUnitOfWork source ) { + source.CheckNull( nameof( source ) ); + if ( source is not DbContext unitOfWork ) + return; + unitOfWork.ChangeTracker.Clear(); + } + + /// + /// 获取更改跟踪调试视图 + /// + /// 工作单元 + public static string GetChangeTrackerDebugView( this IUnitOfWork source ) { + source.CheckNull( nameof( source ) ); + if( source is not DbContext unitOfWork ) + return null; + unitOfWork.ChangeTracker.DetectChanges(); + return unitOfWork.ChangeTracker.DebugView.LongView; + } + + /// + /// 是否更改 + /// + /// 工作单元 + public static bool IsChange( this IUnitOfWork source ) { + source.CheckNull( nameof( source ) ); + if ( source is not DbContext unitOfWork ) + return false; + return unitOfWork.ChangeTracker.Entries().Any( t => t.State == EntityState.Modified ); + } + + /// + /// 是否可连接 + /// + /// 工作单元 + /// 取消令牌 + public static async Task CanConnectAsync( this IUnitOfWork source, CancellationToken cancellationToken = default ) { + source.CheckNull( nameof( source ) ); + if ( source is not DbContext unitOfWork ) + return false; + return await unitOfWork.Database.CanConnectAsync( cancellationToken ); + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/Usings.cs b/src/Util.Data.EntityFrameworkCore/Usings.cs new file mode 100644 index 000000000..8558fc694 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/Usings.cs @@ -0,0 +1,25 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using System.Text; +global using System.Reflection; +global using System.Text.Json; +global using System.Linq.Expressions; +global using System.Text.Encodings.Web; +global using System.Text.Json.Serialization; +global using System.Text.Unicode; +global using System.ComponentModel.DataAnnotations.Schema; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Logging.Abstractions; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.ChangeTracking; +global using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +global using Microsoft.EntityFrameworkCore.Metadata; +global using Util.Tenants; +global using Util.Domain; +global using Util.Infrastructure; +global using Util.Data.Filters; \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/ValueComparers/ExtraPropertyDictionaryValueComparer.cs b/src/Util.Data.EntityFrameworkCore/ValueComparers/ExtraPropertyDictionaryValueComparer.cs new file mode 100644 index 000000000..a03be9127 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/ValueComparers/ExtraPropertyDictionaryValueComparer.cs @@ -0,0 +1,26 @@ +using Util.Domain.Extending; + +namespace Util.Data.EntityFrameworkCore.ValueComparers; + +/// +/// 扩展属性值比较器 +/// +public class ExtraPropertyDictionaryValueComparer : ValueComparer { + /// + /// 初始化扩展属性值比较器 + /// + public ExtraPropertyDictionaryValueComparer() + : base( + ( extraProperties1, extraProperties2 ) => GetJson( extraProperties1 ) == GetJson( extraProperties2 ), + extraProperties => extraProperties.Aggregate( 0, ( key, value ) => HashCode.Combine( key, value.GetHashCode() ) ), + extraProperties => new ExtraPropertyDictionary( extraProperties ) ) { + } + + /// + /// 获取Json + /// + private static string GetJson( ExtraPropertyDictionary extraProperties ) { + var options = new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; + return Util.Helpers.Json.ToJson( extraProperties, options ); + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/ValueConverters/DateTimeValueConverter.cs b/src/Util.Data.EntityFrameworkCore/ValueConverters/DateTimeValueConverter.cs new file mode 100644 index 000000000..9fa848ba7 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/ValueConverters/DateTimeValueConverter.cs @@ -0,0 +1,29 @@ +using Util.Helpers; + +namespace Util.Data.EntityFrameworkCore.ValueConverters; + +/// +/// 日期值转换器,用于处理Utc日期 +/// +public class DateTimeValueConverter : ValueConverter { + /// + /// 初始化日期值转换器 + /// + public DateTimeValueConverter() + : base( date => Normalize( date ), date => ToLocalTime( date ) ) { + } + + /// + /// 转换为标准化日期 + /// + public static DateTime? Normalize( DateTime? date ) { + return date.HasValue ? Time.Normalize( date.Value ) : null; + } + + /// + /// 转换为本地化日期 + /// + public static DateTime? ToLocalTime( DateTime? date ) { + return date.HasValue ? Time.UtcToLocalTime( date.Value ) : null; + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/ValueConverters/ExtraPropertiesValueConverter.cs b/src/Util.Data.EntityFrameworkCore/ValueConverters/ExtraPropertiesValueConverter.cs new file mode 100644 index 000000000..ecdeb91d5 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/ValueConverters/ExtraPropertiesValueConverter.cs @@ -0,0 +1,41 @@ +using Util.Domain.Extending; +using Util.SystemTextJson; + +namespace Util.Data.EntityFrameworkCore.ValueConverters; + +/// +/// 扩展属性值转换器 +/// +public class ExtraPropertiesValueConverter : ValueConverter { + /// + /// 初始化扩展属性值转换器 + /// + public ExtraPropertiesValueConverter() + : base( extraProperties => PropertiesToJson( extraProperties ), json => JsonToProperties( json ) ) { + } + + /// + /// 扩展属性转换为json + /// + private static string PropertiesToJson( ExtraPropertyDictionary extraProperties ) { + var options = new JsonSerializerOptions { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ), + Converters = { + new UtcDateTimeJsonConverter(), + new UtcNullableDateTimeJsonConverter() + } + }; + return Util.Helpers.Json.ToJson( extraProperties, options ); + } + + /// + /// json转换为扩展属性 + /// + private static ExtraPropertyDictionary JsonToProperties( string json ) { + if( json.IsEmpty() || json == "{}" ) + return new ExtraPropertyDictionary(); + var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + return Util.Helpers.Json.ToObject( json, options ) ?? new ExtraPropertyDictionary(); + } +} \ No newline at end of file diff --git a/src/Util.Data.EntityFrameworkCore/ValueConverters/TrimStringValueConverter.cs b/src/Util.Data.EntityFrameworkCore/ValueConverters/TrimStringValueConverter.cs new file mode 100644 index 000000000..ae8c9c1a7 --- /dev/null +++ b/src/Util.Data.EntityFrameworkCore/ValueConverters/TrimStringValueConverter.cs @@ -0,0 +1,13 @@ +namespace Util.Data.EntityFrameworkCore.ValueConverters; + +/// +/// 字符串值转换器,用于清除两端空白 +/// +public class TrimStringValueConverter : ValueConverter { + /// + /// 初始化字符串值转换器 + /// + public TrimStringValueConverter() + : base( value => value.SafeString(), value => value ) { + } +} \ No newline at end of file diff --git a/src/Util.Data.Metadata/03-Util.Data.Metadata.csproj b/src/Util.Data.Metadata/03-Util.Data.Metadata.csproj new file mode 100644 index 000000000..5e464e72b --- /dev/null +++ b/src/Util.Data.Metadata/03-Util.Data.Metadata.csproj @@ -0,0 +1,32 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Data.Metadata + Util.Data.Metadata + Util.Data.Metadata是Util应用框架数据库元数据操作类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Data.Metadata.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Data.Metadata.xml + + + + + True + False + + + + + + + + diff --git a/src/Util.Data.Metadata/ColumnInfo.cs b/src/Util.Data.Metadata/ColumnInfo.cs new file mode 100644 index 000000000..a959fa38e --- /dev/null +++ b/src/Util.Data.Metadata/ColumnInfo.cs @@ -0,0 +1,47 @@ +namespace Util.Data.Metadata; + +/// +/// 列信息 +/// +public class ColumnInfo { + /// + /// 列标识 + /// + public string Id { get; set; } + /// + /// 列名 + /// + public string Name { get; set; } + /// + /// 注释 + /// + public string Comment { get; set; } + /// + /// 是否主键 + /// + public bool IsPrimaryKey { get; set; } + /// + /// 是否自增 + /// + public bool IsAutoIncrement { get; set; } + /// + /// 是否可空 + /// + public bool? IsNullable { get; set; } + /// + /// 数据类型 + /// + public string DataType { get; set; } + /// + /// 长度 + /// + public long Length { get; set; } + /// + /// 精度,即数值位数 + /// + public int? Precision { get; set; } + /// + /// 小数位数 + /// + public int? Scale { get; set; } +} \ No newline at end of file diff --git a/src/Util.Data.Metadata/DatabaseInfo.cs b/src/Util.Data.Metadata/DatabaseInfo.cs new file mode 100644 index 000000000..ede2c2d09 --- /dev/null +++ b/src/Util.Data.Metadata/DatabaseInfo.cs @@ -0,0 +1,26 @@ +namespace Util.Data.Metadata; + +/// +/// 数据库信息 +/// +public class DatabaseInfo { + /// + /// 初始化数据库信息 + /// + public DatabaseInfo() { + Tables = new List(); + } + + /// + /// 数据库标识 + /// + public string Id { get; set; } + /// + /// 数据库名 + /// + public string Name { get; set; } + /// + /// 表信息集合 + /// + public List Tables { get; } +} \ No newline at end of file diff --git a/src/Util.Data.Metadata/IMetadataService.cs b/src/Util.Data.Metadata/IMetadataService.cs new file mode 100644 index 000000000..30e1ca963 --- /dev/null +++ b/src/Util.Data.Metadata/IMetadataService.cs @@ -0,0 +1,11 @@ +namespace Util.Data.Metadata; + +/// +/// 数据库元数据服务 +/// +public interface IMetadataService { + /// + /// 获取数据库信息 + /// + Task GetDatabaseInfoAsync(); +} \ No newline at end of file diff --git a/src/Util.Data.Metadata/IMetadataServiceFactory.cs b/src/Util.Data.Metadata/IMetadataServiceFactory.cs new file mode 100644 index 000000000..5ee6d8a05 --- /dev/null +++ b/src/Util.Data.Metadata/IMetadataServiceFactory.cs @@ -0,0 +1,13 @@ +namespace Util.Data.Metadata; + +/// +/// 数据库元数据服务工厂 +/// +public interface IMetadataServiceFactory : ITransientDependency { + /// + /// 创建数据库元数据服务 + /// + /// 数据库类型 + /// 数据库连接字符串 + IMetadataService Create( DatabaseType dbType, string connection ); +} \ No newline at end of file diff --git a/src/Util.Data.Metadata/ITypeConverter.cs b/src/Util.Data.Metadata/ITypeConverter.cs new file mode 100644 index 000000000..853b4fec9 --- /dev/null +++ b/src/Util.Data.Metadata/ITypeConverter.cs @@ -0,0 +1,13 @@ +namespace Util.Data.Metadata; + +/// +/// 数据类型转换器 +/// +public interface ITypeConverter { + /// + /// 将数据类型转换为DbType + /// + /// 数据类型 + /// 数据长度 + DbType? ToType( string dataType, int? length = null ); +} \ No newline at end of file diff --git a/src/Util.Data.Metadata/ITypeConverterFactory.cs b/src/Util.Data.Metadata/ITypeConverterFactory.cs new file mode 100644 index 000000000..81600a613 --- /dev/null +++ b/src/Util.Data.Metadata/ITypeConverterFactory.cs @@ -0,0 +1,12 @@ +namespace Util.Data.Metadata; + +/// +/// 数据类型转换器工厂 +/// +public interface ITypeConverterFactory : ITransientDependency { + /// + /// 创建数据库元数据服务 + /// + /// 数据库类型 + ITypeConverter Create( DatabaseType dbType ); +} \ No newline at end of file diff --git a/src/Util.Data.Metadata/TableInfo.cs b/src/Util.Data.Metadata/TableInfo.cs new file mode 100644 index 000000000..132fac0f0 --- /dev/null +++ b/src/Util.Data.Metadata/TableInfo.cs @@ -0,0 +1,34 @@ +namespace Util.Data.Metadata; + +/// +/// 表信息 +/// +public class TableInfo { + /// + /// 初始化表信息 + /// + public TableInfo() { + Columns = new List(); + } + + /// + /// 表标识 + /// + public string Id { get; set; } + /// + /// 架构 + /// + public string Schema { get; set; } + /// + /// 表名 + /// + public string Name { get; set; } + /// + /// 注释 + /// + public string Comment { get; set; } + /// + /// 列信息集合 + /// + public List Columns { get; } +} \ No newline at end of file diff --git a/src/Util.Data.Metadata/Usings.cs b/src/Util.Data.Metadata/Usings.cs new file mode 100644 index 000000000..cf43b17d0 --- /dev/null +++ b/src/Util.Data.Metadata/Usings.cs @@ -0,0 +1,4 @@ +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Data; +global using Util.Dependency; diff --git a/src/Util.Data.Sql/02-Util.Data.Sql.csproj b/src/Util.Data.Sql/02-Util.Data.Sql.csproj new file mode 100644 index 000000000..6132caacb --- /dev/null +++ b/src/Util.Data.Sql/02-Util.Data.Sql.csproj @@ -0,0 +1,34 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Data.Sql + Util.Data.Sql + Util.Data.Sql是Util应用框架处理和构造Sql相关的数据访问基础类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Data.Sql.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Data.Sql.xml + + + + + True + False + + + + + + + + + + diff --git a/src/Util.Data.Sql/Builders/Caches/ColumnCacheBase.cs b/src/Util.Data.Sql/Builders/Caches/ColumnCacheBase.cs new file mode 100644 index 000000000..fcb189ee2 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Caches/ColumnCacheBase.cs @@ -0,0 +1,61 @@ +using Util.Data.Sql.Builders.Core; + +namespace Util.Data.Sql.Builders.Caches; + +/// +/// 列缓存基类 +/// +public abstract class ColumnCacheBase : IColumnCache { + /// + /// Sql方言 + /// + protected readonly IDialect Dialect; + + /// + /// 初始化列缓存 + /// + /// Sql方言 + protected ColumnCacheBase( IDialect dialect ) { + Dialect = dialect; + } + + /// + /// 从缓存中获取处理后的列集合 + /// + /// 列集合 + public abstract string GetSafeColumns( string columns ); + + /// + /// 规范化列集合 + /// + /// 列集合 + protected string NormalizeColumns( string columns ) { + if( columns.IsEmpty() ) + return null; + var result = new StringBuilder(); + var items = columns.Split( ',' ).Where( column => column.IsEmpty() == false ).Select( column => new ColumnItem( Dialect, column ) ); + foreach( var item in items ) { + item.AppendTo( result ); + result.Append( "," ); + } + result.RemoveEnd( "," ); + return result.ToString(); + } + + /// + /// 从缓存中获取处理后的列 + /// + /// 列 + public abstract string GetSafeColumn( string column ); + + /// + /// 规范化列 + /// + /// 列 + protected string NormalizeColumn( string column ) { + if( column.IsEmpty() ) + return null; + var item = new ColumnItem( Dialect, column ); + return item.ToResult(); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Caches/IColumnCache.cs b/src/Util.Data.Sql/Builders/Caches/IColumnCache.cs new file mode 100644 index 000000000..4f406c0e3 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Caches/IColumnCache.cs @@ -0,0 +1,17 @@ +namespace Util.Data.Sql.Builders.Caches; + +/// +/// 列缓存 +/// +public interface IColumnCache { + /// + /// 从缓存中获取处理后的列集合 + /// + /// 列集合 + string GetSafeColumns( string columns ); + /// + /// 从缓存中获取处理后的列 + /// + /// 列 + string GetSafeColumn( string column ); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/ClauseBase.cs b/src/Util.Data.Sql/Builders/Clauses/ClauseBase.cs new file mode 100644 index 000000000..505a8be29 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/ClauseBase.cs @@ -0,0 +1,32 @@ +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// Sql子句基类 +/// +public abstract class ClauseBase { + /// + /// Sql生成器 + /// + protected readonly SqlBuilderBase SqlBuilder; + /// + /// Sql方言 + /// + protected readonly IDialect Dialect; + + /// + /// 初始化Sql子句 + /// + /// Sql生成器 + protected ClauseBase( SqlBuilderBase sqlBuilder ) { + SqlBuilder = sqlBuilder ?? throw new ArgumentNullException( nameof( sqlBuilder ) ); + Dialect = SqlBuilder.Dialect; + } + + /// + /// 替换原始Sql,将[]替换为特定Sql转义符 + /// + /// 原始Sql + protected string ReplaceRawSql( string sql ) { + return Dialect.ReplaceSql( sql ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/EndClause.cs b/src/Util.Data.Sql/Builders/Clauses/EndClause.cs new file mode 100644 index 000000000..b8db8510c --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/EndClause.cs @@ -0,0 +1,208 @@ +using Util.Data.Queries; +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// 结束子句 +/// +public class EndClause : ClauseBase, IEndClause { + + #region 字段 + + /// + /// 结束子句结果 + /// + protected readonly StringBuilder Result; + /// + /// 跳过行数参数名 + /// + protected string OffsetParam; + /// + /// 限制行数参数名 + /// + protected string LimitParam; + /// + /// 参数管理器 + /// + protected IParameterManager ParameterManager; + /// + /// 分页 + /// + protected IPage Pager; + + #endregion + + #region 构造方法 + + /// + /// 初始化结束子句 + /// + /// Sql生成器 + /// 结束子句结果 + /// 跳过行数参数名 + /// 限制行数参数名 + /// 分页 + public EndClause( SqlBuilderBase sqlBuilder, StringBuilder result = null, string offsetParam = null, string limitParam = null, IPage pager = null ) : base( sqlBuilder ) { + Result = result ?? new StringBuilder(); + OffsetParam = offsetParam; + LimitParam = limitParam; + Pager = pager; + ParameterManager = sqlBuilder.ParameterManager; + } + + #endregion + + #region Page(设置分页) + + /// + public void Page( IPage page ) { + if( page == null ) + return; + Pager = page; + Skip( page.GetSkipCount() ); + Take( page.PageSize ); + } + + #endregion + + #region Skip(设置跳过行数) + + /// + public void Skip( int count ) { + var param = GetOffsetParam(); + ParameterManager.Add( param, count ); + } + + /// + /// 获取跳过行数参数名 + /// + protected string GetOffsetParam() { + if( OffsetParam.IsEmpty() == false ) + return OffsetParam; + OffsetParam = CreateOffsetParam(); + return OffsetParam; + } + + /// + /// 创建跳过行数参数名 + /// + private string CreateOffsetParam() { + var result = ParameterManager.GenerateName(); + ParameterManager.Add( result, 0 ); + return result; + } + + #endregion + + #region Take(设置获取行数) + + /// + public void Take( int count ) { + var param = GetLimitParam(); + ParameterManager.Add( param, count ); + } + + /// + /// 获取限制行数的参数名 + /// + protected string GetLimitParam() { + if( LimitParam.IsEmpty() == false ) + return LimitParam; + LimitParam = ParameterManager.GenerateName(); + return LimitParam; + } + + #endregion + + #region AppendSql(添加到结束位置) + + /// + /// 添加到结束位置 + /// + /// Sql语句 + /// 是否原样添加 + public void AppendSql( string sql, bool raw ) { + if( string.IsNullOrWhiteSpace( sql ) ) + return; + if( raw ) { + Result.Append( sql ); + return; + } + Result.Append( ReplaceRawSql( sql ) ); + } + + #endregion + + #region Validate(验证) + + /// + public bool Validate() { + if ( Result.Length > 0 ) + return true; + if ( LimitParam.IsEmpty() == false ) + return true; + return false; + } + + #endregion + + #region AppendTo(添加到字符串生成器) + + /// + public void AppendTo( StringBuilder builder ) { + builder.CheckNull( nameof( builder ) ); + if( Validate() == false ) + return; + AppendLimit( builder ); + builder.Append( Result ); + } + + #endregion + + #region AppendLimit(添加行限制) + + /// + /// 添加行限制 + /// + /// 字符串生成器 + protected virtual void AppendLimit( StringBuilder builder ) { + if( LimitParam.IsEmpty() ) + return; + builder.AppendFormat( "Limit {0} OFFSET {1}", GetLimitParam(), GetOffsetParam() ); + } + + #endregion + + #region ClearPage(清理分页) + + /// + public void ClearPage() { + OffsetParam = null; + LimitParam = null; + Pager = null; + } + + #endregion + + #region Clear(清理) + + /// + public void Clear() { + Result.Clear(); + ClearPage(); + } + + #endregion + + #region Clone(复制结束子句) + + /// + public virtual IEndClause Clone( SqlBuilderBase builder ) { + var result = new StringBuilder(); + result.Append( Result ); + return new EndClause( builder, result, OffsetParam, LimitParam, Pager ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/FromClause.cs b/src/Util.Data.Sql/Builders/Clauses/FromClause.cs new file mode 100644 index 000000000..7d546edcc --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/FromClause.cs @@ -0,0 +1,125 @@ +using Util.Data.Sql.Builders.Core; + +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// From子句 +/// +public class FromClause : ClauseBase, IFromClause { + + #region 字段 + + /// + /// From子句结果 + /// + protected readonly StringBuilder Result; + + #endregion + + #region 构造方法 + + /// + /// 初始化From子句 + /// + /// Sql生成器 + /// From子句结果 + public FromClause( SqlBuilderBase sqlBuilder, StringBuilder result = null ) : base( sqlBuilder ) { + Result = result ?? new StringBuilder(); + } + + #endregion + + #region From(设置表) + + /// + public void From( string table ) { + if ( table.IsEmpty() ) + return; + var item = CreateTableItem( table ); + item.AppendTo( Result ); + } + + /// + /// 创建表项 + /// + /// 表名 + protected virtual TableItem CreateTableItem( string table ) { + return new( Dialect, table ); + } + + /// + public void From( ISqlBuilder builder, string alias ) { + if( builder == null ) + return; + var item = new SqlBuilderItem( Dialect, builder, alias ); + item.AppendTo( Result ); + } + + /// + public void From( Action action, string alias ) { + if( action == null ) + return; + var builder = SqlBuilder.New(); + action( builder ); + From( builder, alias ); + } + + #endregion + + #region AppendSql(添加到From子句) + + /// + public void AppendSql( string sql, bool raw ) { + if( string.IsNullOrWhiteSpace( sql ) ) + return; + if( raw ) { + Result.Append( sql ); + return; + } + Result.Append( ReplaceRawSql( sql ) ); + } + + #endregion + + #region Validate(验证) + + /// + public bool Validate() { + return Result.Length > 0; + } + + #endregion + + #region AppendTo(添加到字符串生成器) + + /// + public void AppendTo( StringBuilder builder ) { + builder.CheckNull( nameof( builder ) ); + if( Validate() == false ) + return; + builder.Append( "From " ); + builder.Append( Result ); + } + + #endregion + + #region Clear(清理) + + /// + public void Clear() { + Result.Clear(); + } + + #endregion + + #region Clone(复制From子句) + + /// + public virtual IFromClause Clone( SqlBuilderBase builder ) { + var result = new StringBuilder(); + result.Append( Result ); + return new FromClause( builder, result ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/GroupByClause.cs b/src/Util.Data.Sql/Builders/Clauses/GroupByClause.cs new file mode 100644 index 000000000..316ebf820 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/GroupByClause.cs @@ -0,0 +1,156 @@ +using Util.Data.Sql.Builders.Caches; +using Util.Data.Sql.Builders.Conditions; + +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// Group By子句 +/// +public class GroupByClause : ClauseBase, IGroupByClause { + + #region 字段 + + /// + /// 子句结果 + /// + protected readonly StringBuilder Result; + /// + /// 分组条件 + /// + protected StringBuilder Condition; + /// + /// 列缓存 + /// + protected readonly IColumnCache ColumnCache; + /// + /// Sql条件工厂 + /// + protected readonly IConditionFactory ConditionFactory; + + #endregion + + #region 构造方法 + + /// + /// 初始化Group By子句 + /// + /// Sql生成器 + /// Group By子句结果 + /// 分组条件 + public GroupByClause( SqlBuilderBase sqlBuilder, StringBuilder result = null, StringBuilder condition = null ) : base( sqlBuilder ) { + Result = result ?? new StringBuilder(); + Condition = condition ?? new StringBuilder(); + ColumnCache = sqlBuilder.ColumnCache; + ConditionFactory = sqlBuilder.ConditionFactory; + } + + #endregion + + #region GroupBy(添加分组列) + + /// + public void GroupBy( string columns ) { + if( string.IsNullOrWhiteSpace( columns ) ) + return; + if( Result.Length > 0 ) + Result.Append( "," ); + Result.Append( ColumnCache.GetSafeColumns( columns ) ); + } + + #endregion + + #region Having(添加分组条件) + + /// + public void Having( string expression, object value, Operator @operator = Operator.Equal, bool isParameterization = true ) { + And( ConditionFactory.Create( expression, value, @operator, isParameterization ) ); + } + + /// + /// And连接条件 + /// + private void And( ISqlCondition condition ) { + new AndCondition( condition ).AppendTo( Condition ); + } + + #endregion + + #region AppendGroupBy(添加到Group By子句) + + /// + public void AppendGroupBy( string sql, bool raw ) { + if( string.IsNullOrWhiteSpace( sql ) ) + return; + if( raw ) { + Result.Append( sql ); + return; + } + Result.Append( ReplaceRawSql( sql ) ); + } + + #endregion + + #region AppendHaving(添加到Having子句) + + /// + public void AppendHaving( string sql, bool raw ) { + if( string.IsNullOrWhiteSpace( sql ) ) + return; + if( raw ) { + And( new SqlCondition( sql ) ); + return; + } + And( new SqlCondition( ReplaceRawSql( sql ) ) ); + } + + #endregion + + #region Validate(验证) + + /// + public bool Validate() { + return Result.Length > 0; + } + + #endregion + + #region AppendTo(添加到字符串生成器) + + /// + public void AppendTo( StringBuilder builder ) { + builder.CheckNull( nameof( builder ) ); + if( Validate() == false ) + return; + builder.Append( "Group By " ); + builder.Append( Result ); + if( Condition.Length == 0 ) + return; + builder.Append( " Having " ); + builder.Append( Condition ); + } + + #endregion + + #region Clear(清理) + + /// + public void Clear() { + Result.Clear(); + Condition.Clear(); + } + + #endregion + + #region Clone(复制Group By子句) + + /// + public virtual IGroupByClause Clone( SqlBuilderBase builder ) { + var result = new StringBuilder(); + result.Append( Result ); + var condition = new StringBuilder(); + condition.Append( Condition ); + return new GroupByClause( builder, result, condition ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/IEndClause.cs b/src/Util.Data.Sql/Builders/Clauses/IEndClause.cs new file mode 100644 index 000000000..c219d74d6 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/IEndClause.cs @@ -0,0 +1,43 @@ +using Util.Data.Queries; + +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// 结束子句 +/// +public interface IEndClause : ISqlClause { + /// + /// 设置跳过行数 + /// + /// 跳过的行数 + void Skip( int count ); + /// + /// 设置获取行数 + /// + /// 获取的行数 + void Take( int count ); + /// + /// 设置分页 + /// + /// 分页参数 + void Page( IPage page ); + /// + /// 添加到结束位置 + /// + /// Sql语句 + /// 是否原样添加 + void AppendSql( string sql, bool raw ); + /// + /// 清理分页 + /// + void ClearPage(); + /// + /// 清理 + /// + void Clear(); + /// + /// 复制结束子句 + /// + /// Sql生成器 + IEndClause Clone( SqlBuilderBase builder ); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/IFromClause.cs b/src/Util.Data.Sql/Builders/Clauses/IFromClause.cs new file mode 100644 index 000000000..c92da423a --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/IFromClause.cs @@ -0,0 +1,39 @@ +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// From子句 +/// +public interface IFromClause : ISqlClause { + /// + /// 设置表名 + /// + /// 表名 + void From( string table ); + /// + /// 设置子查询表 + /// + /// Sql生成器 + /// 表别名 + void From( ISqlBuilder builder, string alias ); + /// + /// 设置子查询表 + /// + /// 子查询操作 + /// 表别名 + void From( Action action, string alias ); + /// + /// 添加到From子句 + /// + /// Sql语句 + /// 是否原样添加 + void AppendSql( string sql, bool raw ); + /// + /// 清理 + /// + void Clear(); + /// + /// 复制From子句 + /// + /// Sql生成器 + IFromClause Clone( SqlBuilderBase builder ); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/IGroupByClause.cs b/src/Util.Data.Sql/Builders/Clauses/IGroupByClause.cs new file mode 100644 index 000000000..f7950d59a --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/IGroupByClause.cs @@ -0,0 +1,41 @@ +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// Group By子句 +/// +public interface IGroupByClause : ISqlClause { + /// + /// 添加分组列 + /// + /// 分组字段 + void GroupBy( string columns ); + /// + /// 添加分组条件 + /// + /// 聚合表达式 + /// 值 + /// 运算符 + /// 是否参数化 + void Having( string expression, object value, Operator @operator = Operator.Equal,bool isParameterization = true ); + /// + /// 添加到Group By子句 + /// + /// Sql语句 + /// 是否原样添加 + void AppendGroupBy( string sql, bool raw ); + /// + /// 添加到Having子句 + /// + /// Sql语句 + /// 是否原样添加 + void AppendHaving( string sql, bool raw ); + /// + /// 清理 + /// + void Clear(); + /// + /// 复制Group By子句 + /// + /// Sql生成器 + IGroupByClause Clone( SqlBuilderBase builder ); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/IInsertClause.cs b/src/Util.Data.Sql/Builders/Clauses/IInsertClause.cs new file mode 100644 index 000000000..2c287a0eb --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/IInsertClause.cs @@ -0,0 +1,39 @@ +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// Insert子句 +/// +public interface IInsertClause : ISqlClause { + /// + /// 设置插入的表和列名集合 + /// + /// 列名集合 + /// 表名 + void Insert( string columns, string table = null ); + /// + /// 设置Insert语句插入的值集合 + /// + /// 值集合 + void Values( params object[] values ); + /// + /// 添加到Insert子句 + /// + /// Sql语句 + /// 是否原样添加 + void AppendInsert( string sql, bool raw ); + /// + /// 添加到Values子句 + /// + /// Sql语句 + /// 是否原样添加 + void AppendValues( string sql, bool raw ); + /// + /// 清理 + /// + void Clear(); + /// + /// 复制Insert子句 + /// + /// Sql生成器 + IInsertClause Clone( SqlBuilderBase builder ); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/IJoinClause.cs b/src/Util.Data.Sql/Builders/Clauses/IJoinClause.cs new file mode 100644 index 000000000..264521980 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/IJoinClause.cs @@ -0,0 +1,99 @@ +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// Join子句 +/// +public interface IJoinClause : ISqlClause { + /// + /// 内连接 + /// + /// 表名 + void Join( string table ); + /// + /// 内连接子查询 + /// + /// Sql生成器 + /// 表别名 + void Join( ISqlBuilder builder, string alias ); + /// + /// 内连接子查询 + /// + /// 子查询操作 + /// 表别名 + void Join( Action action, string alias ); + /// + /// 左外连接 + /// + /// 表名 + void LeftJoin( string table ); + /// + /// 左外连接子查询 + /// + /// Sql生成器 + /// 表别名 + void LeftJoin( ISqlBuilder builder, string alias ); + /// + /// 左外连接子查询 + /// + /// 子查询操作 + /// 表别名 + void LeftJoin( Action action, string alias ); + /// + /// 右外连接 + /// + /// 表名 + void RightJoin( string table ); + /// + /// 右外连接子查询 + /// + /// Sql生成器 + /// 表别名 + void RightJoin( ISqlBuilder builder, string alias ); + /// + /// 右外连接子查询 + /// + /// 子查询操作 + /// 表别名 + void RightJoin( Action action, string alias ); + /// + /// 设置连接条件 + /// + /// 列名 + /// 值 + /// 运算符 + /// 是否参数化 + void On( string column, object value, Operator @operator = Operator.Equal, bool isParameterization = false ); + /// + /// 添加到内连接子句 + /// + /// Sql语句 + /// 是否原样添加 + void AppendJoin( string sql, bool raw ); + /// + /// 添加到左外连接子句 + /// + /// Sql语句 + /// 是否原样添加 + void AppendLeftJoin( string sql, bool raw ); + /// + /// 添加到右外连接子句 + /// + /// Sql语句 + /// 是否原样添加 + void AppendRightJoin( string sql, bool raw ); + /// + /// 添加到On子句 + /// + /// Sql语句 + /// 是否原样添加 + void AppendOn( string sql, bool raw ); + /// + /// 清理 + /// + void Clear(); + /// + /// 复制Join子句 + /// + /// Sql生成器 + IJoinClause Clone( SqlBuilderBase builder ); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/IOrderByClause.cs b/src/Util.Data.Sql/Builders/Clauses/IOrderByClause.cs new file mode 100644 index 000000000..4e6918398 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/IOrderByClause.cs @@ -0,0 +1,27 @@ +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// Order By子句 +/// +public interface IOrderByClause : ISqlClause { + /// + /// 排序 + /// + /// 排序列表 + void OrderBy( string order ); + /// + /// 添加到Order By子句 + /// + /// Sql语句 + /// 是否原样添加 + void AppendSql( string sql, bool raw ); + /// + /// 清理 + /// + void Clear(); + /// + /// 复制Order By子句 + /// + /// Sql生成器 + IOrderByClause Clone( SqlBuilderBase builder ); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/ISelectClause.cs b/src/Util.Data.Sql/Builders/Clauses/ISelectClause.cs new file mode 100644 index 000000000..b89cc3f8f --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/ISelectClause.cs @@ -0,0 +1,53 @@ +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// Select子句 +/// +public interface ISelectClause : ISqlClause { + /// + /// 设置星号*列 + /// + void Select(); + /// + /// 设置列名 + /// + /// 列名 + void Select( string columns ); + /// + /// 设置子查询列 + /// + /// Sql生成器 + /// 列别名 + void Select( ISqlBuilder builder, string alias ); + /// + /// 设置子查询列 + /// + /// 子查询操作 + /// 列别名 + void Select( Action action, string alias ); + /// + /// 添加到Select子句 + /// + /// Sql语句 + /// 是否原样添加 + void AppendSql( string sql, bool raw ); + /// + /// 添加子查询到Select子句 + /// + /// Sql生成器 + void AppendSql( ISqlBuilder builder ); + /// + /// 添加子查询到Select子句 + /// + /// 子查询操作 + void AppendSql( Action action ); + /// + /// 清理 + /// + void Clear(); + /// + /// 复制Select子句 + /// + /// Sql生成器 + ISelectClause Clone( SqlBuilderBase builder ); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/ISqlClause.cs b/src/Util.Data.Sql/Builders/Clauses/ISqlClause.cs new file mode 100644 index 000000000..c61c87f58 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/ISqlClause.cs @@ -0,0 +1,11 @@ +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// Sql子句 +/// +public interface ISqlClause : ISqlContent { + /// + /// 验证 + /// + bool Validate(); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/IStartClause.cs b/src/Util.Data.Sql/Builders/Clauses/IStartClause.cs new file mode 100644 index 000000000..f5b90b14d --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/IStartClause.cs @@ -0,0 +1,38 @@ +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// 起始子句 +/// +public interface IStartClause : ISqlClause { + /// + /// 公用表表达式CTE + /// + /// 公用表表达式名称 + /// Sql生成器 + void Cte( string name, ISqlBuilder builder ); + /// + /// 添加到起始位置 + /// + /// Sql语句 + /// 是否原样添加 + void Append( string sql, bool raw ); + /// + /// 添加到起始位置并换行 + /// + /// Sql语句 + /// 是否原样添加 + void AppendLine( string sql, bool raw ); + /// + /// 清理公用表表达式CTE + /// + void ClearCte(); + /// + /// 清理 + /// + void Clear(); + /// + /// 复制起始子句 + /// + /// Sql生成器 + IStartClause Clone( SqlBuilderBase builder ); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/IWhereClause.cs b/src/Util.Data.Sql/Builders/Clauses/IWhereClause.cs new file mode 100644 index 000000000..30c0b307b --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/IWhereClause.cs @@ -0,0 +1,128 @@ +using Util.Data.Queries; + +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// Where子句 +/// +public interface IWhereClause : ISqlClause { + /// + /// 与连接条件 + /// + /// 查询条件 + void And( ISqlCondition condition ); + /// + /// 或连接条件 + /// + /// 查询条件 + void Or( ISqlCondition condition ); + /// + /// 设置查询条件 + /// + /// 列名 + /// 值 + /// 运算符 + void Where( string column, object value, Operator @operator ); + /// + /// 设置子查询条件 + /// + /// 列名 + /// 子查询Sql生成器 + /// 运算符 + void Where( string column, ISqlBuilder builder, Operator @operator ); + /// + /// 设置子查询条件 + /// + /// 列名 + /// 子查询操作 + /// 运算符 + void Where( string column, Action action, Operator @operator ); + /// + /// 设置Is Null查询条件 + /// + /// 列名 + void IsNull( string column ); + /// + /// 设置Is Not Null条件 + /// + /// 列名 + void IsNotNull( string column ); + /// + /// 设置空条件 + /// + /// 列名 + void IsEmpty( string column ); + /// + /// 设置非空条件 + /// + /// 列名 + void IsNotEmpty( string column ); + /// + /// 设置范围查询条件 + /// + /// 列名 + /// 最小值 + /// 最大值 + /// 包含边界 + void Between( string column, int? min, int? max, Boundary boundary ); + /// + /// 设置范围查询条件 + /// + /// 列名 + /// 最小值 + /// 最大值 + /// 包含边界 + void Between( string column, double? min, double? max, Boundary boundary ); + /// + /// 设置范围查询条件 + /// + /// 列名 + /// 最小值 + /// 最大值 + /// 包含边界 + void Between( string column, decimal? min, decimal? max, Boundary boundary ); + /// + /// 设置范围查询条件 + /// + /// 列名 + /// 最小值 + /// 最大值 + /// 是否包含时间 + /// 包含边界 + void Between( string column, DateTime? min, DateTime? max, bool includeTime, Boundary? boundary ); + /// + /// 设置Exists查询条件 + /// + /// 子查询Sql生成器 + void Exists( ISqlBuilder builder ); + /// + /// 设置Exists查询条件 + /// + /// 子查询操作 + void Exists( Action action ); + /// + /// 设置Not Exists查询条件 + /// + /// 子查询Sql生成器 + void NotExists( ISqlBuilder builder ); + /// + /// 设置Not Exists查询条件 + /// + /// 子查询操作 + void NotExists( Action action ); + /// + /// 添加到Where子句 + /// + /// Sql语句 + /// 是否原样添加 + void AppendSql( string sql, bool raw ); + /// + /// 清理 + /// + void Clear(); + /// + /// 复制Where子句 + /// + /// Sql生成器 + IWhereClause Clone( SqlBuilderBase builder ); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/InsertClause.cs b/src/Util.Data.Sql/Builders/Clauses/InsertClause.cs new file mode 100644 index 000000000..493496fa3 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/InsertClause.cs @@ -0,0 +1,180 @@ +using Util.Data.Sql.Builders.Caches; +using Util.Data.Sql.Builders.Core; +using Util.Data.Sql.Builders.Params; +using Util.Helpers; + +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// Insert子句 +/// +public class InsertClause : ClauseBase, IInsertClause { + + #region 字段 + + /// + /// Insert结果 + /// + protected readonly StringBuilder InsertResult; + /// + /// Values结果 + /// + protected readonly StringBuilder ValuesResult; + /// + /// 列缓存 + /// + protected readonly IColumnCache ColumnCache; + /// + /// 参数管理器 + /// + protected readonly IParameterManager ParameterManager; + + #endregion + + #region 构造方法 + + /// + /// 初始化Insert子句 + /// + /// Sql生成器 + /// Insert结果 + /// Values结果 + public InsertClause( SqlBuilderBase sqlBuilder, StringBuilder insertResult = null, StringBuilder valuesResult = null ) : base( sqlBuilder ) { + InsertResult = insertResult ?? new StringBuilder(); + ValuesResult = valuesResult ?? new StringBuilder(); + ColumnCache = sqlBuilder.ColumnCache; + ParameterManager = sqlBuilder.ParameterManager; + } + + #endregion + + #region Insert(设置插入的表和列名集合) + + /// + public void Insert( string columns, string table = null ) { + if ( InsertResult.Length == 0 ) { + if ( table.IsEmpty() ) + return; + var tableItem = new TableItem( Dialect, table ); + tableItem.AppendTo( InsertResult ); + InsertResult.Append( "(" ); + InsertResult.Append( ColumnCache.GetSafeColumns( columns ) ); + InsertResult.Append( ")" ); + return; + } + AppendColumns( ColumnCache.GetSafeColumns( columns ) ); + } + + /// + /// 添加列 + /// + protected void AppendColumns( string columns ) { + InsertResult.RemoveEnd( ")" ); + InsertResult.Append( "," ); + InsertResult.Append( columns ); + InsertResult.Append( ")" ); + } + + #endregion + + #region Values(设置插入的值集合) + + /// + public void Values( params object[] values ) { + if ( values == null ) + return; + if ( ValuesResult.Length > 0 ) + ValuesResult.Append( "," ); + ValuesResult.Append( "(" ); + foreach ( var value in values ) { + var paramName = ParameterManager.GenerateName(); + ValuesResult.AppendFormat( "{0},", paramName ); + ParameterManager.Add( paramName, value ); + } + ValuesResult.RemoveEnd( "," ).Append( ")" ); + } + + #endregion + + #region AppendInsert(添加到Insert子句) + + /// + public void AppendInsert( string sql, bool raw ) { + if ( string.IsNullOrWhiteSpace( sql ) ) + return; + if ( raw ) { + InsertResult.Append( sql ); + return; + } + InsertResult.Append( ReplaceRawSql( sql ) ); + } + + #endregion + + #region AppendValues(添加到Values子句) + + /// + public void AppendValues( string sql, bool raw ) { + if ( string.IsNullOrWhiteSpace( sql ) ) + return; + if ( raw ) { + ValuesResult.Append( sql ); + return; + } + ValuesResult.Append( ReplaceRawSql( sql ) ); + } + + #endregion + + #region Validate(验证) + + /// + public bool Validate() { + return InsertResult.Length != 0; + } + + #endregion + + #region AppendTo(添加到字符串生成器) + + /// + public void AppendTo( StringBuilder builder ) { + builder.CheckNull( nameof( builder ) ); + if ( Validate() == false ) + return; + builder.Append( "Insert Into " ); + builder.Append( InsertResult ); + builder.AppendLine( " " ); + if ( ValuesResult.Length == 0 ) { + builder.RemoveEnd( Common.Line ); + return; + } + builder.Append( "Values" ); + builder.Append( ValuesResult ); + } + + #endregion + + #region Clear(清理) + + /// + public void Clear() { + InsertResult.Clear(); + ValuesResult.Clear(); + } + + #endregion + + #region Clone(复制Insert子句) + + /// + public virtual IInsertClause Clone( SqlBuilderBase builder ) { + var insertResult = new StringBuilder(); + insertResult.Append( InsertResult ); + var valuesResult = new StringBuilder(); + valuesResult.Append( ValuesResult ); + return new InsertClause( builder, insertResult, valuesResult ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/JoinClause.cs b/src/Util.Data.Sql/Builders/Clauses/JoinClause.cs new file mode 100644 index 000000000..0b22b4208 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/JoinClause.cs @@ -0,0 +1,321 @@ +using Util.Data.Sql.Builders.Caches; +using Util.Data.Sql.Builders.Conditions; +using Util.Data.Sql.Builders.Core; +using Util.Helpers; + +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// Join子句 +/// +public class JoinClause : ClauseBase, IJoinClause { + + #region 常量 + + /// + /// Join关键字 + /// + private const string JoinKey = "Join"; + /// + /// Left Join关键字 + /// + private const string LeftJoinKey = "Left Join"; + /// + /// Right Join关键字 + /// + private const string RightJoinKey = "Right Join"; + + #endregion + + #region 字段 + + /// + /// Join子句结果 + /// + protected readonly StringBuilder Result; + /// + /// 连接条件 + /// + protected StringBuilder Condition; + /// + /// 列缓存 + /// + protected readonly IColumnCache ColumnCache; + /// + /// Sql条件工厂 + /// + protected readonly IConditionFactory ConditionFactory; + + #endregion + + #region 构造方法 + + /// + /// 初始化Join子句 + /// + /// Sql生成器 + /// Join子句结果 + /// 连接条件 + public JoinClause( SqlBuilderBase sqlBuilder, StringBuilder result = null, StringBuilder condition = null ) : base( sqlBuilder ) { + Result = result ?? new StringBuilder(); + Condition = condition ?? new StringBuilder(); + ColumnCache = sqlBuilder.ColumnCache; + ConditionFactory = sqlBuilder.ConditionFactory; + } + + #endregion + + #region Join(内连接) + + /// + public void Join( string table ) { + Join( JoinKey, table ); + } + + /// + /// 表连接 + /// + private void Join( string joinType, string table ) { + AppendOn(); + AppendJoin( joinType, table ); + } + + /// + /// 添加On条件 + /// + private void AppendOn() { + if( Condition.Length == 0 ) + return; + Result.Append( " On " ); + Result.Append( Condition ); + Result.AppendLine( " " ); + Condition.Clear(); + } + + /// + /// 添加Join + /// + private void AppendJoin( string joinType, string table ) { + Result.AppendFormat( "{0} ", joinType ); + var item = CreateTableItem( table ); + item.AppendTo( Result ); + } + + /// + /// 创建表项 + /// + /// 表名 + protected virtual TableItem CreateTableItem( string table ) { + return new( Dialect, table ); + } + + /// + public void Join( ISqlBuilder builder, string alias ) { + Join( JoinKey, builder, alias ); + } + + /// + /// 表连接 + /// + private void Join( string joinType, ISqlBuilder builder, string alias ) { + if( builder == null ) + return; + AppendOn(); + Result.AppendFormat( "{0} ", joinType ); + var item = new SqlBuilderItem( Dialect, builder, alias ); + item.AppendTo( Result ); + } + + /// + public void Join( Action action, string alias ) { + if( action == null ) + return; + var builder = SqlBuilder.New(); + action( builder ); + Join( builder, alias ); + } + + #endregion + + #region LeftJoin(左外连接) + + /// + public void LeftJoin( string table ) { + Join( LeftJoinKey, table ); + } + + /// + public void LeftJoin( ISqlBuilder builder, string alias ) { + Join( LeftJoinKey, builder, alias ); + } + + /// + public void LeftJoin( Action action, string alias ) { + if( action == null ) + return; + var builder = SqlBuilder.New(); + action( builder ); + LeftJoin( builder, alias ); + } + + #endregion + + #region RightJoin(右外连接) + + /// + public void RightJoin( string table ) { + Join( RightJoinKey, table ); + } + + /// + public void RightJoin( ISqlBuilder builder, string alias ) { + Join( RightJoinKey, builder, alias ); + } + + /// + public void RightJoin( Action action, string alias ) { + if( action == null ) + return; + var builder = SqlBuilder.New(); + action( builder ); + RightJoin( builder, alias ); + } + + #endregion + + #region On(设置连接条件) + + /// + public void On( string column, object value, Operator @operator = Operator.Equal, bool isParameterization = false ) { + column = ColumnCache.GetSafeColumn( column ); + value = GetValue( value, isParameterization ); + var condition = ConditionFactory.Create( column, value, @operator, isParameterization ); + On( condition ); + } + + /// + /// 获取值,非参数化字符串值被认为是列名,将进行规范化处理 + /// + private object GetValue( object value, bool isParameterization ) { + if( value == null ) + return null; + if( isParameterization ) + return value; + if( value.GetType() != typeof( string ) ) + return value; + return ColumnCache.GetSafeColumn( value.ToString() ); + } + + /// + /// 设置连接条件 + /// + /// 连接条件 + public void On( ISqlCondition condition ) { + if( condition == null ) + return; + new AndCondition( condition ).AppendTo( Condition ); + } + + #endregion + + #region AppendJoin(添加到内连接子句) + + /// + public void AppendJoin( string sql, bool raw ) { + AppendSql( JoinKey, sql, raw ); + } + + /// + /// 添加到连接子句 + /// + private void AppendSql( string joinType, string sql, bool raw ) { + if( string.IsNullOrWhiteSpace( sql ) ) + return; + AppendOn(); + Result.AppendFormat( "{0} ", joinType ); + if( raw ) { + Result.Append( sql ); + return; + } + Result.Append( ReplaceRawSql( sql ) ); + } + + #endregion + + #region AppendLeftJoin(添加到左外连接子句) + + /// + public void AppendLeftJoin( string sql, bool raw ) { + AppendSql( LeftJoinKey, sql, raw ); + } + + #endregion + + #region AppendRightJoin(添加到右外连接子句) + + /// + public void AppendRightJoin( string sql, bool raw ) { + AppendSql( RightJoinKey, sql, raw ); + } + + #endregion + + #region AppendOn(添加到On子句) + + /// + public void AppendOn( string sql, bool raw ) { + if( raw ) { + On( new SqlCondition( sql ) ); + return; + } + On( new SqlCondition( ReplaceRawSql( sql ) ) ); + } + + #endregion + + #region Validate(验证) + + /// + public bool Validate() { + return Result.Length > 0; + } + + #endregion + + #region AppendTo(添加到字符串生成器) + + /// + public void AppendTo( StringBuilder builder ) { + builder.CheckNull( nameof( builder ) ); + if( Validate() == false ) + return; + AppendOn(); + builder.Append( Result ); + builder.RemoveEnd( $" {Common.Line}" ); + } + + #endregion + + #region Clear(清理) + + /// + public void Clear() { + Result.Clear(); + Condition.Clear(); + } + + #endregion + + #region Clone(复制Join子句) + + /// + public virtual IJoinClause Clone( SqlBuilderBase builder ) { + var result = new StringBuilder(); + result.Append( Result ); + var condition = new StringBuilder(); + condition.Append( Condition ); + return new JoinClause( builder, result, condition ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/OrderByClause.cs b/src/Util.Data.Sql/Builders/Clauses/OrderByClause.cs new file mode 100644 index 000000000..3acd35be9 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/OrderByClause.cs @@ -0,0 +1,113 @@ +using Util.Data.Sql.Builders.Caches; +using Util.Data.Sql.Builders.Core; + +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// Order By子句 +/// +public class OrderByClause : ClauseBase, IOrderByClause { + + #region 字段 + + /// + /// 子句结果 + /// + protected readonly StringBuilder Result; + /// + /// 列缓存 + /// + protected readonly IColumnCache ColumnCache; + + #endregion + + #region 构造方法 + + /// + /// 初始化排序子句 + /// + /// Sql生成器 + /// Order By子句结果 + public OrderByClause( SqlBuilderBase sqlBuilder, StringBuilder result = null ) : base( sqlBuilder ) { + Result = result ?? new StringBuilder(); + ColumnCache = sqlBuilder.ColumnCache; + } + + #endregion + + #region OrderBy(排序) + + /// + public void OrderBy( string order ) { + if( string.IsNullOrWhiteSpace( order ) ) + return; + var items = order.Split( ',' ).Where( t => t.IsEmpty() == false ); + foreach( var item in items ) { + var orderItem = new OrderByItem( item ); + Result.Append( ColumnCache.GetSafeColumn( orderItem.Column ) ); + if( orderItem.Desc ) + Result.Append( " Desc" ); + Result.Append( "," ); + } + } + + #endregion + + #region AppendSql(添加到排序子句) + + /// + public void AppendSql( string sql, bool raw ) { + if( string.IsNullOrWhiteSpace( sql ) ) + return; + if( raw ) { + Result.Append( sql ); + return; + } + Result.Append( ReplaceRawSql( sql ) ); + } + + #endregion + + #region Validate(验证) + + /// + public bool Validate() { + return Result.Length > 0; + } + + #endregion + + #region AppendTo(添加到字符串生成器) + + /// + public void AppendTo( StringBuilder builder ) { + builder.CheckNull( nameof( builder ) ); + if( Validate() == false ) + return; + builder.Append( "Order By " ); + builder.Append( Result ); + builder.RemoveEnd( "," ); + } + + #endregion + + #region Clear(清理) + + /// + public void Clear() { + Result.Clear(); + } + + #endregion + + #region Clone(复制Order By子句) + + /// + public virtual IOrderByClause Clone( SqlBuilderBase builder ) { + var result = new StringBuilder(); + result.Append( Result ); + return new OrderByClause( builder, result ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/SelectClause.cs b/src/Util.Data.Sql/Builders/Clauses/SelectClause.cs new file mode 100644 index 000000000..be18cef44 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/SelectClause.cs @@ -0,0 +1,159 @@ +using Util.Data.Sql.Builders.Caches; +using Util.Data.Sql.Builders.Core; + +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// Select子句 +/// +public class SelectClause : ClauseBase, ISelectClause { + + #region 字段 + + /// + /// Select子句结果 + /// + protected readonly StringBuilder Result; + /// + /// 列缓存 + /// + protected readonly IColumnCache ColumnCache; + + #endregion + + #region 构造方法 + + /// + /// 初始化Select子句 + /// + /// Sql生成器 + /// Select子句结果 + public SelectClause( SqlBuilderBase sqlBuilder, StringBuilder result = null ) : base( sqlBuilder ) { + Result = result ?? new StringBuilder(); + ColumnCache = sqlBuilder.ColumnCache; + } + + #endregion + + #region Select(设置列名) + + /// + public void Select() { + Result.Clear(); + Result.Append( "*" ); + } + + /// + public void Select( string columns ) { + if ( columns.IsEmpty() ) + return; + if ( columns == "*" ) { + Select(); + return; + } + AppendColumns( ColumnCache.GetSafeColumns( columns ) ); + } + + /// + /// 添加列 + /// + protected void AppendColumns( string columns ) { + if( Result.Length > 0 ) + Result.RemoveEnd( "," ).Append( "," ); + Result.Append( columns ); + } + + /// + public void Select( ISqlBuilder builder, string alias ) { + if( builder == null ) + return; + if( Result.Length > 0 ) + Result.Append( "," ); + var item = new SqlBuilderItem( Dialect, builder, alias ); + item.AppendTo( Result ); + } + + /// + public void Select( Action action, string alias ) { + if( action == null ) + return; + var builder = SqlBuilder.New(); + action( builder ); + Select( builder, alias ); + } + + #endregion + + #region AppendSql(添加到Select子句) + + /// + public void AppendSql( string sql, bool raw ) { + if( string.IsNullOrWhiteSpace( sql ) ) + return; + if( raw ) { + Result.Append( sql ); + return; + } + Result.Append( ReplaceRawSql( sql ) ); + } + + /// + public void AppendSql( ISqlBuilder builder ) { + if( builder == null ) + return; + builder.AppendTo( Result ); + } + + /// + public void AppendSql( Action action ) { + if( action == null ) + return; + var builder = SqlBuilder.New(); + action( builder ); + AppendSql( builder ); + } + + #endregion + + #region Validate(验证) + + /// + public bool Validate() { + return Result.Length > 0; + } + + #endregion + + #region AppendTo(添加到字符串生成器) + + /// + public void AppendTo( StringBuilder builder ) { + builder.CheckNull( nameof( builder ) ); + if ( Validate() == false ) + return; + builder.Append( "Select " ); + builder.Append( Result ); + } + + #endregion + + #region Clear(清理) + + /// + public void Clear() { + Result.Clear(); + } + + #endregion + + #region Clone(复制Select子句) + + /// + public virtual ISelectClause Clone( SqlBuilderBase builder ) { + var result = new StringBuilder(); + result.Append( Result ); + return new SelectClause( builder, result ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/StartClause.cs b/src/Util.Data.Sql/Builders/Clauses/StartClause.cs new file mode 100644 index 000000000..69c0da70a --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/StartClause.cs @@ -0,0 +1,195 @@ +using Util.Data.Sql.Builders.Caches; +using Util.Data.Sql.Builders.Core; +using Util.Helpers; + +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// 起始子句 +/// +public class StartClause : ClauseBase, IStartClause { + + #region 字段 + + /// + /// 起始子句结果 + /// + protected readonly StringBuilder Result; + /// + /// 公用表表达式CTE项集合 + /// + protected List CteItems; + /// + /// 列缓存 + /// + protected readonly IColumnCache ColumnCache; + + #endregion + + #region 构造方法 + + /// + /// 初始化起始子句 + /// + /// Sql生成器 + /// 起始子句结果 + /// 公用表表达式CTE项集合 + public StartClause( SqlBuilderBase sqlBuilder, StringBuilder result = null, List cteItems = null ) : base( sqlBuilder ) { + Result = result ?? new StringBuilder(); + CteItems = cteItems ?? new List(); + ColumnCache = sqlBuilder.ColumnCache; + } + + #endregion + + #region Cte(公用表表达式CTE) + + /// + /// 公用表表达式CTE + /// + /// 公用表表达式名称 + /// Sql生成器 + public void Cte( string name, ISqlBuilder builder ) { + if( name.IsEmpty() || builder == null ) + return; + name = ColumnCache.GetSafeColumn( name ); + CteItems.Add( new CteItem( name, builder ) ); + } + + #endregion + + #region Append(添加到起始位置) + + /// + /// 添加到起始位置 + /// + /// Sql语句 + /// 是否原样添加 + public void Append( string sql, bool raw ) { + if( string.IsNullOrWhiteSpace( sql ) ) + return; + if( raw ) { + Result.Append( sql ); + return; + } + Result.Append( ReplaceRawSql( sql ) ); + } + + #endregion + + #region AppendLine(添加到起始位置并换行) + + /// + /// 添加到起始位置并换行 + /// + /// Sql语句 + /// 是否原样添加 + public void AppendLine( string sql, bool raw ) { + if( string.IsNullOrWhiteSpace( sql ) ) + return; + if( raw ) { + Result.AppendLine( sql ); + return; + } + Result.AppendLine( ReplaceRawSql( sql ) ); + } + + #endregion + + #region Validate(验证) + + /// + /// 验证 + /// + public bool Validate() { + if( Result.Length > 0 ) + return true; + if( CteItems.Count > 0 ) + return true; + return false; + } + + #endregion + + #region AppendTo(添加到字符串生成器) + + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + public void AppendTo( StringBuilder builder ) { + builder.CheckNull( nameof( builder ) ); + if( Validate() == false ) + return; + AppendCte( builder ); + builder.Append( Result ); + } + + #endregion + + #region AppendCte(添加公用表表达式CTE) + + /// + /// 添加公用表表达式CTE + /// + /// 字符串生成器 + protected virtual void AppendCte( StringBuilder builder ) { + if( CteItems.Count == 0 ) + return; + builder.AppendFormat( "{0} ", GetCteKeyWord() ); + foreach( var item in CteItems ) { + builder.Append( item.Name ); + builder.AppendLine( " " ); + builder.Append( "As (" ); + item.Builder.AppendTo( builder ); + builder.AppendLine( ")," ); + } + builder.RemoveEnd( $",{Common.Line}" ); + } + + /// + /// 获取CTE关键字 + /// + protected virtual string GetCteKeyWord() { + return "With"; + } + + #endregion + + #region ClearCte(清理公用表表达式CTE) + + /// + /// 清理公用表表达式CTE + /// + public void ClearCte() { + CteItems.Clear(); + } + + #endregion + + #region Clear(清理) + + /// + /// 清理 + /// + public void Clear() { + Result.Clear(); + CteItems.Clear(); + } + + #endregion + + #region Clone(复制起始子句) + + /// + /// 复制起始子句 + /// + /// Sql生成器 + public virtual IStartClause Clone( SqlBuilderBase builder ) { + var result = new StringBuilder(); + result.Append( Result ); + return new StartClause( builder, result, CteItems.Select( t => t.Clone() ).ToList() ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Clauses/WhereClause.cs b/src/Util.Data.Sql/Builders/Clauses/WhereClause.cs new file mode 100644 index 000000000..96b68fccd --- /dev/null +++ b/src/Util.Data.Sql/Builders/Clauses/WhereClause.cs @@ -0,0 +1,305 @@ +using Util.Data.Queries; +using Util.Data.Sql.Builders.Caches; +using Util.Data.Sql.Builders.Conditions; + +namespace Util.Data.Sql.Builders.Clauses; + +/// +/// Where子句 +/// +public class WhereClause : ClauseBase,IWhereClause { + + #region 字段 + + /// + /// Where子句结果 + /// + protected readonly StringBuilder Result; + /// + /// 列缓存 + /// + protected readonly IColumnCache ColumnCache; + /// + /// Sql条件工厂 + /// + protected readonly IConditionFactory ConditionFactory; + + #endregion + + #region 构造方法 + + /// + /// 初始化Where子句 + /// + /// Sql生成器 + /// Where子句结果 + public WhereClause( SqlBuilderBase sqlBuilder, StringBuilder result = null ) : base( sqlBuilder ) { + Result = result ?? new StringBuilder(); + ColumnCache = sqlBuilder.ColumnCache; + ConditionFactory = sqlBuilder.ConditionFactory; + } + + #endregion + + #region And + + /// + public void And( ISqlCondition condition ) { + new AndCondition( condition ).AppendTo( Result ); + } + + #endregion + + #region Or + + /// + public void Or( ISqlCondition condition ) { + new OrCondition( condition ).AppendTo( Result ); + } + + #endregion + + #region Where + + /// + public void Where( string column, object value, Operator @operator ) { + column = ColumnCache.GetSafeColumn( column ); + And( ConditionFactory.Create( column, value, @operator ) ); + } + + /// + public void Where( string column, ISqlBuilder builder, Operator @operator ) { + if( builder == null ) + return; + Where( column, (object)builder, @operator ); + } + + /// + public void Where( string column, Action action, Operator @operator ) { + if( action == null ) + return; + var builder = SqlBuilder.New(); + action( builder ); + Where( column, builder, @operator ); + } + + #endregion + + #region IsNull + + /// + public void IsNull( string column ) { + Where( column, (object)null, Operator.Equal ); + } + + #endregion + + #region IsNotNull + + /// + public void IsNotNull( string column ) { + Where( column, (object)null, Operator.NotEqual ); + } + + #endregion + + #region IsEmpty + + /// + public void IsEmpty( string column ) { + column = ColumnCache.GetSafeColumn( column ); + var nullCondition = ConditionFactory.Create( column, null, Operator.Equal ); + var emptyCondition = ConditionFactory.Create( column, "''", Operator.Equal, false ); + And( new OrCondition( nullCondition, emptyCondition ) ); + } + + #endregion + + #region IsNotEmpty + + /// + public void IsNotEmpty( string column ) { + column = ColumnCache.GetSafeColumn( column ); + var notNullCondition = ConditionFactory.Create( column, null, Operator.NotEqual ); + var notEmptyCondition = ConditionFactory.Create( column, "''", Operator.NotEqual, false ); + And( new AndCondition( notNullCondition, notEmptyCondition ) ); + } + + #endregion + + #region Between + + /// + public void Between( string column, int? min, int? max, Boundary boundary ) { + column = ColumnCache.GetSafeColumn( column ); + if( min > max ) { + And( ConditionFactory.Create( column, max, min, boundary ) ); + return; + } + And( ConditionFactory.Create( column, min, max, boundary ) ); + } + + /// + public void Between( string column, double? min, double? max, Boundary boundary ) { + column = ColumnCache.GetSafeColumn( column ); + if( min > max ) { + And( ConditionFactory.Create( column, max, min, boundary ) ); + return; + } + And( ConditionFactory.Create( column, min, max, boundary ) ); + } + + /// + public void Between( string column, decimal? min, decimal? max, Boundary boundary ) { + column = ColumnCache.GetSafeColumn( column ); + if( min > max ) { + And( ConditionFactory.Create( column, max, min, boundary ) ); + return; + } + And( ConditionFactory.Create( column, min, max, boundary ) ); + } + + /// + public void Between( string column, DateTime? min, DateTime? max, bool includeTime, Boundary? boundary ) { + column = ColumnCache.GetSafeColumn( column ); + And( ConditionFactory.Create( column, GetMin( min, max, includeTime ), GetMax( min, max, includeTime ), GetBoundary( boundary, includeTime ) ) ); + } + + /// + /// 获取最小日期 + /// + private DateTime? GetMin( DateTime? min, DateTime? max, bool includeTime ) { + if( min == null ) + return null; + DateTime? result = min; + if( min > max ) + result = max; + if( includeTime ) + return result; + return result.SafeValue().Date; + } + + /// + /// 获取最大日期 + /// + private DateTime? GetMax( DateTime? min, DateTime? max, bool includeTime ) { + if( max == null ) + return null; + DateTime? result = max; + if( min > max ) + result = min; + if( includeTime ) + return result; + return result.SafeValue().Date.AddDays( 1 ); + } + + /// + /// 获取日期范围查询条件边界 + /// + private Boundary GetBoundary( Boundary? boundary, bool includeTime ) { + if( boundary != null ) + return boundary.SafeValue(); + if( includeTime ) + return Boundary.Both; + return Boundary.Left; + } + + #endregion + + #region Exists + + /// + public void Exists( ISqlBuilder builder ) { + if ( builder == null ) + return; + And( new ExistsCondition( builder ) ); + } + + /// + public void Exists( Action action ) { + if ( action == null ) + return; + var builder = SqlBuilder.New(); + action( builder ); + Exists( builder ); + } + + #endregion + + #region NotExists + + /// + public void NotExists( ISqlBuilder builder ) { + if ( builder == null ) + return; + And( new NotExistsCondition( builder ) ); + } + + /// + public void NotExists( Action action ) { + if ( action == null ) + return; + var builder = SqlBuilder.New(); + action( builder ); + NotExists( builder ); + } + + #endregion + + #region AppendSql + + /// + public void AppendSql( string sql, bool raw ) { + if( string.IsNullOrWhiteSpace( sql ) ) + return; + if( raw ) { + And( new SqlCondition( sql ) ); + return; + } + And( new SqlCondition( ReplaceRawSql( sql ) ) ); + } + + #endregion + + #region Validate + + /// + public bool Validate() { + return Result.Length > 0; + } + + #endregion + + #region AppendTo + + /// + public void AppendTo( StringBuilder builder ) { + builder.CheckNull( nameof(builder) ); + if( Validate() == false ) + return; + builder.Append( "Where " ); + builder.Append( Result ); + } + + #endregion + + #region Clear + + /// + public void Clear() { + Result.Clear(); + } + + #endregion + + #region Clone + + /// + public virtual IWhereClause Clone( SqlBuilderBase builder ) { + var result = new StringBuilder(); + result.Append( Result ); + return new WhereClause( builder, result ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/AndCondition.cs b/src/Util.Data.Sql/Builders/Conditions/AndCondition.cs new file mode 100644 index 000000000..ba6cc4031 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/AndCondition.cs @@ -0,0 +1,50 @@ +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// And连接条件 +/// +public class AndCondition : ISqlCondition { + /// + /// Sql条件1 + /// + private readonly ISqlCondition _condition1; + /// + /// Sql条件2 + /// + private readonly ISqlCondition _condition2; + + /// + /// 初始化And连接条件 + /// + /// Sql条件1 + /// Sql条件2 + public AndCondition( ISqlCondition condition1, ISqlCondition condition2 = null ) { + _condition1 = condition1; + _condition2 = condition2; + } + + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + public void AppendTo( StringBuilder builder ) { + if( _condition1 == null && _condition2 == null ) + return; + And( builder, _condition1 ); + And( builder, _condition2 ); + } + + /// + /// 与连接 + /// + private void And( StringBuilder builder,ISqlCondition condition ) { + if ( condition == null || condition == NullCondition.Instance ) + return; + if ( builder.Length == 0 ) { + condition.AppendTo( builder ); + return; + } + builder.Append( " And " ); + condition.AppendTo( builder ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/ContainsCondition.cs b/src/Util.Data.Sql/Builders/Conditions/ContainsCondition.cs new file mode 100644 index 000000000..5f4b557fa --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/ContainsCondition.cs @@ -0,0 +1,40 @@ +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// Sql模糊匹配查询条件 +/// +public class ContainsCondition : SqlConditionBase { + /// + /// 初始化Sql模糊匹配查询条件 + /// + public ContainsCondition( IParameterManager parameterManager, string column, object value, bool isParameterization ) + : base( parameterManager, column, value, isParameterization ) { + } + + /// + /// 获取参数值 + /// + protected override object GetValue() { + return $"%{Value}%"; + } + + /// + /// 添加Sql条件 + /// + /// 字符串生成器 + /// 列名 + /// 值 + protected override void AppendCondition( StringBuilder builder, string column, object value ) { + builder.AppendFormat( "{0} Like {1}", column, value ); + } + + /// + /// 添加非参数化条件 + /// + /// 字符串生成器 + protected override void AppendNonParameterizedCondition( StringBuilder builder ) { + AppendCondition( builder, Column, $"'{GetValue()}'" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/EndsCondition.cs b/src/Util.Data.Sql/Builders/Conditions/EndsCondition.cs new file mode 100644 index 000000000..7d6be6ef6 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/EndsCondition.cs @@ -0,0 +1,40 @@ +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// Sql尾匹配查询条件 +/// +public class EndsCondition : SqlConditionBase { + /// + /// 初始化Sql尾匹配查询条件 + /// + public EndsCondition( IParameterManager parameterManager, string column, object value, bool isParameterization ) + : base( parameterManager, column, value, isParameterization ) { + } + + /// + /// 获取参数值 + /// + protected override object GetValue() { + return $"%{Value}"; + } + + /// + /// 添加Sql条件 + /// + /// 字符串生成器 + /// 列名 + /// 值 + protected override void AppendCondition( StringBuilder builder, string column, object value ) { + builder.AppendFormat( "{0} Like {1}", column, value ); + } + + /// + /// 添加非参数化条件 + /// + /// 字符串生成器 + protected override void AppendNonParameterizedCondition( StringBuilder builder ) { + AppendCondition( builder, Column, $"'{GetValue()}'" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/EqualCondition.cs b/src/Util.Data.Sql/Builders/Conditions/EqualCondition.cs new file mode 100644 index 000000000..97f6f7f9d --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/EqualCondition.cs @@ -0,0 +1,50 @@ +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// Sql相等查询条件 +/// +public class EqualCondition : SqlConditionBase { + /// + /// 初始化Sql相等查询条件 + /// + public EqualCondition( IParameterManager parameterManager, string column, object value, bool isParameterization ) + : base( parameterManager, column, value, isParameterization ) { + } + + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + public override void AppendTo( StringBuilder builder ) { + if ( Value == null ) { + new IsNullCondition( Column ).AppendTo( builder ); + return; + } + base.AppendTo( builder ); + } + + /// + /// 添加Sql条件 + /// + /// 字符串生成器 + /// 列名 + /// 值 + protected override void AppendCondition( StringBuilder builder, string column, object value ) { + builder.AppendFormat( "{0}={1}", column, value ); + } + + /// + /// 添加Sql生成器 + /// + /// 字符串生成器 + /// 列名 + /// Sql生成器 + protected override void AppendSqlBuilder( StringBuilder builder, string column, ISqlBuilder sqlBuilder ) { + builder.AppendFormat( "{0}=", column ); + builder.Append( "(" ); + sqlBuilder.AppendTo( builder ); + builder.Append( ")" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/ExistsCondition.cs b/src/Util.Data.Sql/Builders/Conditions/ExistsCondition.cs new file mode 100644 index 000000000..e7a7f963c --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/ExistsCondition.cs @@ -0,0 +1,32 @@ +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// Exists查询条件 +/// +public class ExistsCondition : ISqlCondition { + /// + /// 子查询Sql生成器 + /// + private readonly ISqlBuilder _sqlBuilder; + + /// + /// 初始化Exists查询条件 + /// + /// 子查询Sql生成器 + public ExistsCondition( ISqlBuilder builder ) { + _sqlBuilder = builder; + } + + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + /// + public void AppendTo( StringBuilder builder ) { + if ( _sqlBuilder == null ) + return; + builder.Append( "Exists (" ); + _sqlBuilder.AppendTo( builder ); + builder.Append( ")" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/GreaterCondition.cs b/src/Util.Data.Sql/Builders/Conditions/GreaterCondition.cs new file mode 100644 index 000000000..8225dc605 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/GreaterCondition.cs @@ -0,0 +1,38 @@ +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// Sql大于查询条件 +/// +public class GreaterCondition : SqlConditionBase { + /// + /// 初始化Sql大于查询条件 + /// + public GreaterCondition( IParameterManager parameterManager, string column, object value, bool isParameterization ) + : base( parameterManager, column, value, isParameterization ) { + } + + /// + /// 添加Sql条件 + /// + /// 字符串生成器 + /// 列名 + /// 值 + protected override void AppendCondition( StringBuilder builder, string column, object value ) { + builder.AppendFormat( "{0}>{1}", column, value ); + } + + /// + /// 添加Sql生成器 + /// + /// 字符串生成器 + /// 列名 + /// Sql生成器 + protected override void AppendSqlBuilder( StringBuilder builder, string column, ISqlBuilder sqlBuilder ) { + builder.AppendFormat( "{0}>", column ); + builder.Append( "(" ); + sqlBuilder.AppendTo( builder ); + builder.Append( ")" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/GreaterEqualCondition.cs b/src/Util.Data.Sql/Builders/Conditions/GreaterEqualCondition.cs new file mode 100644 index 000000000..c25fccaf4 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/GreaterEqualCondition.cs @@ -0,0 +1,38 @@ +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// Sql大于等于查询条件 +/// +public class GreaterEqualCondition : SqlConditionBase { + /// + /// 初始化Sql大于等于查询条件 + /// + public GreaterEqualCondition( IParameterManager parameterManager, string column, object value, bool isParameterization ) + : base( parameterManager, column, value, isParameterization ) { + } + + /// + /// 添加Sql条件 + /// + /// 字符串生成器 + /// 列名 + /// 值 + protected override void AppendCondition( StringBuilder builder, string column, object value ) { + builder.AppendFormat( "{0}>={1}", column, value ); + } + + /// + /// 添加Sql生成器 + /// + /// 字符串生成器 + /// 列名 + /// Sql生成器 + protected override void AppendSqlBuilder( StringBuilder builder, string column, ISqlBuilder sqlBuilder ) { + builder.AppendFormat( "{0}>=", column ); + builder.Append( "(" ); + sqlBuilder.AppendTo( builder ); + builder.Append( ")" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/InCondition.cs b/src/Util.Data.Sql/Builders/Conditions/InCondition.cs new file mode 100644 index 000000000..8646a57d1 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/InCondition.cs @@ -0,0 +1,132 @@ +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// In查询条件 +/// +public class InCondition : ISqlCondition { + /// + /// Sql参数管理器 + /// + protected readonly IParameterManager ParameterManager; + /// + /// 列名 + /// + protected readonly string Column; + /// + /// 列名 + /// + protected readonly object Value; + /// + /// 是否参数化 + /// + protected readonly bool IsParameterization; + + /// + /// 初始化In查询条件 + /// + /// Sql参数管理器 + /// 列名 + /// 值 + /// 是否参数化 + public InCondition( IParameterManager parameterManager, string column, object value, bool isParameterization ) { + ParameterManager = parameterManager ?? throw new ArgumentNullException( nameof( parameterManager ) ); + if( string.IsNullOrWhiteSpace( column ) ) + throw new ArgumentNullException( nameof( column ) ); + Column = column; + Value = value; + IsParameterization = isParameterization; + } + + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + public void AppendTo( StringBuilder builder ) { + if( Value is ISqlBuilder sqlBuilder ) { + AppendSqlBuilder( builder, Column, sqlBuilder ); + return; + } + var values = GetValues(); + if( values.Count == 0 ) + return; + if ( IsParameterization ) { + AppendParameterizedCondition( builder,values ); + return; + } + AppendNonParameterizedCondition( builder,values ); + } + + /// + /// 添加Sql生成器 + /// + /// 字符串生成器 + /// 列名 + /// Sql生成器 + protected virtual void AppendSqlBuilder( StringBuilder builder, string column, ISqlBuilder sqlBuilder ) { + builder.AppendFormat( "{0} {1} ", column, GetOperator() ); + builder.Append( "(" ); + sqlBuilder.AppendTo( builder ); + builder.Append( ")" ); + } + + /// + /// 获取操作符关键字 + /// + protected virtual string GetOperator() { + return "In"; + } + + /// + /// 获取值 + /// + private List GetValues() { + var values = Value as IEnumerable; + if( values == null ) + return null; + var result = new List(); + foreach ( var value in values ) { + if ( value == null ) + continue; + result.Add( value ); + } + return result; + } + + /// + /// 添加参数化条件 + /// + protected virtual void AppendParameterizedCondition( StringBuilder builder, List values ) { + builder.AppendFormat( "{0} {1} (", Column, GetOperator() ); + foreach( var value in values ) { + var paramName = ParameterManager.GenerateName(); + builder.AppendFormat( "{0},", paramName ); + ParameterManager.Add( paramName, value ); + } + builder.RemoveEnd( "," ).Append( ")" ); + } + + /// + /// 添加非参数化条件 + /// + protected virtual void AppendNonParameterizedCondition( StringBuilder builder, List values ) { + builder.AppendFormat( "{0} {1} (", Column, GetOperator() ); + foreach( var value in values ) { + builder.AppendFormat( "{0},", GetFormattedValue( value ) ); + } + builder.RemoveEnd( "," ).Append( ")" ); + } + + /// + /// 获取格式化后的值 + /// + private string GetFormattedValue( object value ) { + switch ( value.GetType().ToString() ) { + case "System.String": + return $"'{value}'"; + default: + return value.ToString(); + } + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/IsNotNullCondition.cs b/src/Util.Data.Sql/Builders/Conditions/IsNotNullCondition.cs new file mode 100644 index 000000000..efdf42ce2 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/IsNotNullCondition.cs @@ -0,0 +1,29 @@ +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// Is Not Null查询条件 +/// +public class IsNotNullCondition : ISqlCondition { + /// + /// 列名 + /// + private readonly string _name; + + /// + /// 初始化Is Not Null查询条件 + /// + /// 列名 + public IsNotNullCondition( string name ) { + _name = name; + } + + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + public void AppendTo( StringBuilder builder ) { + if( _name.IsEmpty() ) + return; + builder.AppendFormat( "{0} Is Not Null", _name ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/IsNullCondition.cs b/src/Util.Data.Sql/Builders/Conditions/IsNullCondition.cs new file mode 100644 index 000000000..1789fa474 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/IsNullCondition.cs @@ -0,0 +1,29 @@ +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// Is Null查询条件 +/// +public class IsNullCondition : ISqlCondition { + /// + /// 列名 + /// + private readonly string _name; + + /// + /// 初始化Is Null查询条件 + /// + /// 列名 + public IsNullCondition( string name ) { + _name = name; + } + + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + public void AppendTo( StringBuilder builder ) { + if ( _name.IsEmpty() ) + return; + builder.AppendFormat( "{0} Is Null", _name ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/LessCondition.cs b/src/Util.Data.Sql/Builders/Conditions/LessCondition.cs new file mode 100644 index 000000000..e0ba66581 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/LessCondition.cs @@ -0,0 +1,38 @@ +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// Sql小于查询条件 +/// +public class LessCondition : SqlConditionBase { + /// + /// 初始化Sql小于查询条件 + /// + public LessCondition( IParameterManager parameterManager, string column, object value, bool isParameterization ) + : base( parameterManager, column, value, isParameterization ) { + } + + /// + /// 添加Sql条件 + /// + /// 字符串生成器 + /// 列名 + /// 值 + protected override void AppendCondition( StringBuilder builder, string column, object value ) { + builder.AppendFormat( "{0}<{1}", column, value ); + } + + /// + /// 添加Sql生成器 + /// + /// 字符串生成器 + /// 列名 + /// Sql生成器 + protected override void AppendSqlBuilder( StringBuilder builder, string column, ISqlBuilder sqlBuilder ) { + builder.AppendFormat( "{0}<", column ); + builder.Append( "(" ); + sqlBuilder.AppendTo( builder ); + builder.Append( ")" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/LessEqualCondition.cs b/src/Util.Data.Sql/Builders/Conditions/LessEqualCondition.cs new file mode 100644 index 000000000..1acecc06d --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/LessEqualCondition.cs @@ -0,0 +1,38 @@ +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// Sql小于等于查询条件 +/// +public class LessEqualCondition : SqlConditionBase { + /// + /// 初始化Sql小于等于查询条件 + /// + public LessEqualCondition( IParameterManager parameterManager, string column, object value, bool isParameterization ) + : base( parameterManager, column, value, isParameterization ) { + } + + /// + /// 添加Sql条件 + /// + /// 字符串生成器 + /// 列名 + /// 值 + protected override void AppendCondition( StringBuilder builder, string column, object value ) { + builder.AppendFormat( "{0}<={1}", column, value ); + } + + /// + /// 添加Sql生成器 + /// + /// 字符串生成器 + /// 列名 + /// Sql生成器 + protected override void AppendSqlBuilder( StringBuilder builder, string column, ISqlBuilder sqlBuilder ) { + builder.AppendFormat( "{0}<=", column ); + builder.Append( "(" ); + sqlBuilder.AppendTo( builder ); + builder.Append( ")" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/NotEqualCondition.cs b/src/Util.Data.Sql/Builders/Conditions/NotEqualCondition.cs new file mode 100644 index 000000000..7c4af6789 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/NotEqualCondition.cs @@ -0,0 +1,50 @@ +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// Sql不相等查询条件 +/// +public class NotEqualCondition : SqlConditionBase { + /// + /// 初始化Sql不相等查询条件 + /// + public NotEqualCondition( IParameterManager parameterManager, string column, object value, bool isParameterization ) + : base( parameterManager, column, value, isParameterization ) { + } + + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + public override void AppendTo( StringBuilder builder ) { + if( Value == null ) { + new IsNotNullCondition( Column ).AppendTo( builder ); + return; + } + base.AppendTo( builder ); + } + + /// + /// 添加Sql条件 + /// + /// 字符串生成器 + /// 列名 + /// 值 + protected override void AppendCondition( StringBuilder builder, string column, object value ) { + builder.AppendFormat( "{0}<>{1}", column, value ); + } + + /// + /// 添加Sql生成器 + /// + /// 字符串生成器 + /// 列名 + /// Sql生成器 + protected override void AppendSqlBuilder( StringBuilder builder, string column, ISqlBuilder sqlBuilder ) { + builder.AppendFormat( "{0}<>", column ); + builder.Append( "(" ); + sqlBuilder.AppendTo( builder ); + builder.Append( ")" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/NotExistsCondition.cs b/src/Util.Data.Sql/Builders/Conditions/NotExistsCondition.cs new file mode 100644 index 000000000..13c74c3f7 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/NotExistsCondition.cs @@ -0,0 +1,32 @@ +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// Not Exists查询条件 +/// +public class NotExistsCondition : ISqlCondition { + /// + /// 子查询Sql生成器 + /// + private readonly ISqlBuilder _sqlBuilder; + + /// + /// 初始化Not Exists查询条件 + /// + /// 子查询Sql生成器 + public NotExistsCondition( ISqlBuilder builder ) { + _sqlBuilder = builder; + } + + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + /// + public void AppendTo( StringBuilder builder ) { + if ( _sqlBuilder == null ) + return; + builder.Append( "Not Exists (" ); + _sqlBuilder.AppendTo( builder ); + builder.Append( ")" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/NotInCondition.cs b/src/Util.Data.Sql/Builders/Conditions/NotInCondition.cs new file mode 100644 index 000000000..de522e237 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/NotInCondition.cs @@ -0,0 +1,26 @@ +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// Not In查询条件 +/// +public class NotInCondition : InCondition { + /// + /// 初始化Not In查询条件 + /// + /// Sql参数管理器 + /// 列名 + /// 值 + /// 是否参数化 + public NotInCondition( IParameterManager parameterManager, string column, object value, bool isParameterization ) + :base( parameterManager, column, value, isParameterization ) { + } + + /// + /// 获取操作符关键字 + /// + protected override string GetOperator() { + return "Not In"; + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/NullCondition.cs b/src/Util.Data.Sql/Builders/Conditions/NullCondition.cs new file mode 100644 index 000000000..a4c39e1ad --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/NullCondition.cs @@ -0,0 +1,24 @@ +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// 空查询条件 +/// +public class NullCondition : ISqlCondition { + /// + /// 封闭构造方法 + /// + private NullCondition() { + } + + /// + /// 空查询条件实例 + /// + public static readonly NullCondition Instance = new NullCondition(); + + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + public void AppendTo( StringBuilder builder ) { + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/OrCondition.cs b/src/Util.Data.Sql/Builders/Conditions/OrCondition.cs new file mode 100644 index 000000000..297d7d1ab --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/OrCondition.cs @@ -0,0 +1,83 @@ +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// Or连接条件 +/// +public class OrCondition : ISqlCondition { + /// + /// Sql条件1 + /// + private readonly ISqlCondition _condition1; + /// + /// Sql条件2 + /// + private readonly ISqlCondition _condition2; + + /// + /// 初始化Or连接条件 + /// + /// Sql条件1 + /// Sql条件2 + public OrCondition( ISqlCondition condition1, ISqlCondition condition2 = null ) { + _condition1 = condition1; + _condition2 = condition2; + if ( condition1 != null && condition1 == NullCondition.Instance ) + _condition1 = null; + if( condition2 != null && condition2 == NullCondition.Instance ) + _condition2 = null; + } + + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + public void AppendTo( StringBuilder builder ) { + if( _condition1 == null && _condition2 == null ) + return; + if ( _condition1 != null && _condition2 != null ) { + AppendAllCondition( builder ); + return; + } + AppendCondition( builder, _condition1 ); + AppendCondition( builder, _condition2 ); + } + + /// + /// 连接两个条件 + /// + private void AppendAllCondition( StringBuilder builder ) { + if( builder.Length == 0 ) { + Or( builder ); + return; + } + builder.Append( " And " ); + Or( builder ); + } + + /// + /// 或操作 + /// + private void Or( StringBuilder builder ) { + builder.Append( "(" ); + _condition1.AppendTo( builder ); + builder.Append( " Or " ); + _condition2.AppendTo( builder ); + builder.Append( ")" ); + } + + /// + /// 连接1个条件 + /// + private void AppendCondition( StringBuilder builder,ISqlCondition condition ) { + if ( condition == null ) + return; + if( builder.Length == 0 ) { + condition.AppendTo( builder ); + return; + } + builder.Insert( 0, '(' ); + builder.Append( " Or " ); + condition.AppendTo( builder ); + builder.Append( ")" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/SegmentCondition.cs b/src/Util.Data.Sql/Builders/Conditions/SegmentCondition.cs new file mode 100644 index 000000000..86fae60f9 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/SegmentCondition.cs @@ -0,0 +1,94 @@ +using Util.Data.Queries; +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// 范围过滤条件 +/// +public class SegmentCondition : ISqlCondition { + /// + /// Sql参数管理器 + /// + protected readonly IParameterManager ParameterManager; + /// + /// 列名 + /// + protected readonly string Column; + /// + /// 最小值 + /// + protected readonly object MinValue; + /// + /// 最大值 + /// + protected readonly object MaxValue; + /// + /// 包含边界 + /// + protected Boundary Boundary; + /// + /// 是否参数化 + /// + protected readonly bool IsParameterization; + + /// + /// 初始化范围过滤条件 + /// + /// Sql参数管理器 + /// 列名 + /// 最小值 + /// 最大值 + /// 包含边界 + /// 是否参数化 + public SegmentCondition( IParameterManager parameterManager, string column, object minValue, object maxValue, Boundary boundary, bool isParameterization ) { + ParameterManager = parameterManager ?? throw new ArgumentNullException( nameof( parameterManager ) ); + if( string.IsNullOrWhiteSpace( column ) ) + throw new ArgumentNullException( nameof( column ) ); + Column = column; + MinValue = minValue; + MaxValue = maxValue; + Boundary = boundary; + IsParameterization = isParameterization; + } + + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + public void AppendTo( StringBuilder builder ) { + new AndCondition( CreateLeftCondition(), CreateRightCondition() ).AppendTo( builder ); + } + + /// + /// 创建左条件 + /// + private ISqlCondition CreateLeftCondition() { + if( string.IsNullOrWhiteSpace( MinValue.SafeString() ) ) + return NullCondition.Instance; + switch( Boundary ) { + case Boundary.Left: + return new GreaterEqualCondition( ParameterManager, Column, MinValue, IsParameterization ); + case Boundary.Both: + return new GreaterEqualCondition( ParameterManager, Column, MinValue, IsParameterization ); + default: + return new GreaterCondition( ParameterManager, Column, MinValue, IsParameterization ); + } + } + + /// + /// 创建右条件 + /// + private ISqlCondition CreateRightCondition() { + if( string.IsNullOrWhiteSpace( MaxValue.SafeString() ) ) + return NullCondition.Instance; + switch( Boundary ) { + case Boundary.Right: + return new LessEqualCondition( ParameterManager, Column, MaxValue, IsParameterization ); + case Boundary.Both: + return new LessEqualCondition( ParameterManager, Column, MaxValue, IsParameterization ); + default: + return new LessCondition( ParameterManager, Column, MaxValue, IsParameterization ); + } + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/SqlCondition.cs b/src/Util.Data.Sql/Builders/Conditions/SqlCondition.cs new file mode 100644 index 000000000..adb3e97a3 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/SqlCondition.cs @@ -0,0 +1,27 @@ +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// Sql查询条件 +/// +public class SqlCondition : ISqlCondition { + /// + /// Sql查询条件 + /// + private readonly string _condition; + + /// + /// 初始化Sql查询条件 + /// + /// 查询条件 + public SqlCondition( string condition ) { + _condition = condition; + } + + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + public void AppendTo( StringBuilder builder ) { + builder.Append( _condition ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/SqlConditionBase.cs b/src/Util.Data.Sql/Builders/Conditions/SqlConditionBase.cs new file mode 100644 index 000000000..5f4267738 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/SqlConditionBase.cs @@ -0,0 +1,108 @@ +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// Sql查询条件 +/// +public abstract class SqlConditionBase : ISqlCondition { + /// + /// Sql参数管理器 + /// + protected readonly IParameterManager ParameterManager; + /// + /// 列名 + /// + protected readonly string Column; + /// + /// 值 + /// + protected readonly object Value; + /// + /// 是否参数化 + /// + protected readonly bool IsParameterization; + + /// + /// 初始化Sql查询条件 + /// + /// Sql参数管理器 + /// 列名 + /// 值 + /// 是否参数化 + protected SqlConditionBase( IParameterManager parameterManager, string column, object value, bool isParameterization ) { + ParameterManager = parameterManager ?? throw new ArgumentNullException( nameof( parameterManager ) ); + if( string.IsNullOrWhiteSpace( column ) ) + throw new ArgumentNullException( nameof( column ) ); + Column = column; + Value = value; + IsParameterization = isParameterization; + } + + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + public virtual void AppendTo( StringBuilder builder ) { + if ( Value is ISqlBuilder sqlBuilder ) { + AppendSqlBuilder( builder,Column, sqlBuilder ); + return; + } + if ( IsParameterization ) { + AppendParameterizedCondition( builder ); + return; + } + AppendNonParameterizedCondition( builder ); + } + + /// + /// 添加Sql生成器 + /// + /// 字符串生成器 + /// 列名 + /// Sql生成器 + protected virtual void AppendSqlBuilder( StringBuilder builder, string column, ISqlBuilder sqlBuilder ) { + throw new NotImplementedException(); + } + + /// + /// 添加参数化条件 + /// + /// 字符串生成器 + protected virtual void AppendParameterizedCondition( StringBuilder builder ) { + var paramName = GenerateParamName(); + var value = GetValue(); + ParameterManager.Add( paramName, value ); + AppendCondition( builder, Column, paramName ); + } + + /// + /// 创建参数名 + /// + protected virtual string GenerateParamName() { + return ParameterManager.GenerateName(); + } + + /// + /// 获取参数值 + /// + protected virtual object GetValue() { + return Value; + } + + /// + /// 添加Sql条件 + /// + /// 字符串生成器 + /// 列名 + /// 值 + protected abstract void AppendCondition( StringBuilder builder,string column, object value ); + + /// + /// 添加非参数化条件 + /// + /// 字符串生成器 + protected virtual void AppendNonParameterizedCondition( StringBuilder builder ) { + AppendCondition( builder, Column, GetValue() ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Conditions/StartsCondition.cs b/src/Util.Data.Sql/Builders/Conditions/StartsCondition.cs new file mode 100644 index 000000000..1009c4892 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Conditions/StartsCondition.cs @@ -0,0 +1,40 @@ +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Sql.Builders.Conditions; + +/// +/// Sql头匹配查询条件 +/// +public class StartsCondition : SqlConditionBase { + /// + /// 初始化Sql头匹配查询条件 + /// + public StartsCondition( IParameterManager parameterManager, string column, object value, bool isParameterization ) + : base( parameterManager, column, value, isParameterization ) { + } + + /// + /// 获取参数值 + /// + protected override object GetValue() { + return $"{Value}%"; + } + + /// + /// 添加Sql条件 + /// + /// 字符串生成器 + /// 列名 + /// 值 + protected override void AppendCondition( StringBuilder builder, string column, object value ) { + builder.AppendFormat( "{0} Like {1}", column, value ); + } + + /// + /// 添加非参数化条件 + /// + /// 字符串生成器 + protected override void AppendNonParameterizedCondition( StringBuilder builder ) { + AppendCondition( builder, Column, $"'{GetValue()}'" ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Core/ColumnItem.cs b/src/Util.Data.Sql/Builders/Core/ColumnItem.cs new file mode 100644 index 000000000..9452a0586 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Core/ColumnItem.cs @@ -0,0 +1,92 @@ +namespace Util.Data.Sql.Builders.Core; + +/// +/// 列项 +/// +public class ColumnItem { + /// + /// Sql方言 + /// + private readonly IDialect _dialect; + + /// + /// 初始化列项 + /// + /// Sql方言 + /// 列名 + public ColumnItem( IDialect dialect, string name ) { + _dialect = dialect; + Resolve( name ); + } + + /// + /// 解析 + /// + private void Resolve( string name ) { + var item = new NameItem( name ); + TableAlias = item.Prefix; + Name = item.Name; + ColumnAlias = item.Alias; + } + + /// + /// 表别名 + /// + public string TableAlias { get; set; } + + /// + /// 列名 + /// + public string Name { get; set; } + + /// + /// 列别名 + /// + public string ColumnAlias { get; set; } + + /// + /// 获取结果 + /// + public string ToResult() { + var builder = new StringBuilder(); + AppendTo( builder ); + return builder.ToString(); + } + + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + public void AppendTo( StringBuilder builder ) { + AppendTableAlias( builder ); + AppendColumn( builder ); + AppendColumnAlias( builder ); + } + + /// + /// 添加表别名 + /// + private void AppendTableAlias( StringBuilder builder ) { + if ( TableAlias.IsEmpty() ) + return; + builder.AppendFormat( "{0}.", _dialect.GetSafeName( TableAlias ) ); + } + + /// + /// 添加列名 + /// + private void AppendColumn( StringBuilder builder ) { + if( Name.IsEmpty() ) + return; + builder.AppendFormat( _dialect.GetSafeName( Name ) ); + } + + /// + /// 添加列别名 + /// + private void AppendColumnAlias( StringBuilder builder ) { + if( ColumnAlias.IsEmpty() ) + return; + builder.AppendFormat( " As {0}", _dialect.GetSafeName( ColumnAlias ) ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Core/CteItem.cs b/src/Util.Data.Sql/Builders/Core/CteItem.cs new file mode 100644 index 000000000..723a81cc4 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Core/CteItem.cs @@ -0,0 +1,33 @@ +namespace Util.Data.Sql.Builders.Core; + +/// +/// 公用表表达式CTE项 +/// +public class CteItem { + /// + /// 初始化公用表表达式CTE项 + /// + /// 公用表表达式名称 + /// Sql生成器 + public CteItem( string name, ISqlBuilder builder ) { + Name = name; + Builder = builder; + } + + /// + /// 公用表表达式名称 + /// + public string Name { get; } + + /// + /// Sql生成器 + /// + public ISqlBuilder Builder { get; } + + /// + /// 复制公用表表达式CTE项 + /// + public CteItem Clone() { + return new CteItem( Name, Builder.Clone() ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Core/DialectBase.cs b/src/Util.Data.Sql/Builders/Core/DialectBase.cs new file mode 100644 index 000000000..6dea19515 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Core/DialectBase.cs @@ -0,0 +1,60 @@ +namespace Util.Data.Sql.Builders.Core; + +/// +/// Sql方言 +/// +public abstract class DialectBase : IDialect { + /// + /// 获取起始转义标识符 + /// + public abstract string GetOpeningIdentifier(); + + /// + /// 获取结束转义标识符 + /// + public abstract string GetClosingIdentifier(); + + /// + /// 获取安全名称 + /// + public virtual string GetSafeName( string name ) { + if( string.IsNullOrWhiteSpace( name ) ) + return string.Empty; + if( name == "*" ) + return name; + return $"{GetOpeningIdentifier()}{FilterName( name )}{GetClosingIdentifier()}"; + } + + /// + /// 过滤名称 + /// + private string FilterName( string name ) { + return name.Trim().RemoveStart( GetOpeningIdentifier() ).RemoveEnd( GetClosingIdentifier() ); + } + + /// + /// 获取参数前缀 + /// + public abstract string GetPrefix(); + + /// + /// Select子句是否支持As关键字 + /// + public virtual bool SupportSelectAs() { + return true; + } + + /// + /// 替换Sql,将[]替换为特定Sql转义符 + /// + /// 原始Sql + public virtual string ReplaceSql( string sql ) { + return sql? + .Replace( "[[", "|&<&|", StringComparison.Ordinal ) + .Replace( "]]", "|&>&|", StringComparison.Ordinal ) + .Replace( "[", GetOpeningIdentifier(), StringComparison.Ordinal ) + .Replace( "]", GetClosingIdentifier(), StringComparison.Ordinal ) + .Replace( "|&<&|", "[", StringComparison.Ordinal ) + .Replace( "|&>&|", "]", StringComparison.Ordinal ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Core/NameItem.cs b/src/Util.Data.Sql/Builders/Core/NameItem.cs new file mode 100644 index 000000000..696fca3c2 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Core/NameItem.cs @@ -0,0 +1,67 @@ +namespace Util.Data.Sql.Builders.Core; + +/// +/// 名称项,用于拆分列名或表名 +/// +public class NameItem { + /// + /// 初始化名称项 + /// + /// 名称 + public NameItem( string name ) { + Resolve( name ); + } + + /// + /// 解析 + /// + private void Resolve( string name ) { + name = Filter( name ); + var asItem = new SplitItem( name ); + Alias = asItem.Right; + var dotItem = new SplitItem( asItem.Left, "." ); + if( dotItem.Right.IsEmpty() ) { + Name = dotItem.Left; + return; + } + Prefix = dotItem.Left; + Name = dotItem.Right; + } + + /// + /// 过滤 + /// + private string Filter( string name ) { + name = FilterAs( name ); + return FilterDotSpace( name ); + } + + /// + /// 过滤as关键字,替换为空格 + /// + private string FilterAs( string name ) { + return name.Replace( " as ", " ",StringComparison.OrdinalIgnoreCase ); + } + + /// + /// 过滤.前后空格 + /// + private string FilterDotSpace( string name ) { + return name.Replace( @" .", "." ).Replace( @". ", "." ); + } + + /// + /// 前缀,范例:a.b As c,值为 a + /// + public string Prefix { get; private set; } + + /// + /// 名称,范例:a.b As c,值为 b + /// + public string Name { get; private set; } + + /// + /// 别名,范例:a.b As c,值为 c + /// + public string Alias { get; private set; } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Core/OrderByItem.cs b/src/Util.Data.Sql/Builders/Core/OrderByItem.cs new file mode 100644 index 000000000..19d0629bc --- /dev/null +++ b/src/Util.Data.Sql/Builders/Core/OrderByItem.cs @@ -0,0 +1,32 @@ +namespace Util.Data.Sql.Builders.Core; + +/// +/// 排序项 +/// +public class OrderByItem { + /// + /// 初始化排序项 + /// + /// 排序表达式 + public OrderByItem( string order ) { + Column = order.SafeString().Trim(); + if ( Column.EndsWith( " Asc",StringComparison.OrdinalIgnoreCase ) ) { + Column = Column.Substring( 0, Column.Length - 3 ).Trim(); + return; + } + if( Column.EndsWith( " Desc", StringComparison.OrdinalIgnoreCase ) ) { + Desc = true; + Column = Column.Substring( 0, Column.Length - 4 ).Trim(); + } + } + + /// + /// 排序列 + /// + public string Column { get; } + + /// + /// 是否倒排 + /// + public bool Desc { get; } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Core/SplitItem.cs b/src/Util.Data.Sql/Builders/Core/SplitItem.cs new file mode 100644 index 000000000..3571d9ed6 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Core/SplitItem.cs @@ -0,0 +1,35 @@ +namespace Util.Data.Sql.Builders.Core; + +/// +/// 拆分项 +/// +public class SplitItem { + /// + /// 初始化拆分项 + /// + /// 值 + /// 拆分字符 + public SplitItem( string value,string separator = " " ) { + if ( string.IsNullOrEmpty( value ) ) + return; + var list = value.Trim().Split( separator ).Where( item => item.IsEmpty() == false ).ToList(); + if( list.Count == 1 ) { + Left = list[0]; + return; + } + if ( list.Count == 2 ) { + Left = list[0]; + Right = list[1]; + } + } + + /// + /// 左拆分值 + /// + public string Left { get; set; } + + /// + /// 右拆分值 + /// + public string Right { get; set; } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Core/SqlBuilderItem.cs b/src/Util.Data.Sql/Builders/Core/SqlBuilderItem.cs new file mode 100644 index 000000000..94b9d9ce7 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Core/SqlBuilderItem.cs @@ -0,0 +1,44 @@ +namespace Util.Data.Sql.Builders.Core; + +/// +/// Sql生成器项 - 用于子查询 +/// +public class SqlBuilderItem { + /// + /// Sql方言 + /// + private readonly IDialect _dialect; + /// + /// Sql生成器实例 + /// + private readonly ISqlBuilder _builder; + /// + /// 别名 + /// + private readonly string _alias; + + /// + /// 初始化Sql生成器列项 + /// + /// Sql方言 + /// Sql生成器实例 + /// 别名 + public SqlBuilderItem( IDialect dialect, ISqlBuilder builder, string alias = null ) { + _dialect = dialect; + _builder = builder; + _alias = alias; + } + + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + public void AppendTo( StringBuilder builder ) { + builder.Append( "(" ); + _builder.AppendTo( builder ); + builder.Append( ")" ); + if ( _alias.IsEmpty() ) + return; + builder.AppendFormat( " As {0}", _dialect.GetSafeName( _alias ) ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Core/TableItem.cs b/src/Util.Data.Sql/Builders/Core/TableItem.cs new file mode 100644 index 000000000..0c47bb345 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Core/TableItem.cs @@ -0,0 +1,99 @@ +namespace Util.Data.Sql.Builders.Core; + +/// +/// 表项 +/// +public class TableItem { + /// + /// Sql方言 + /// + private readonly IDialect _dialect; + + /// + /// 初始化表项 + /// + /// Sql方言 + /// 列名 + public TableItem( IDialect dialect, string name ) { + _dialect = dialect; + Resolve( name ); + } + + /// + /// 解析 + /// + private void Resolve( string name ) { + var item = new NameItem( name ); + Schema = item.Prefix; + Name = item.Name; + TableAlias = item.Alias; + } + + /// + /// 架构名 + /// + public string Schema { get; set; } + + /// + /// 表名 + /// + public string Name { get; set; } + + /// + /// 表别名 + /// + public string TableAlias { get; set; } + + /// + /// 验证是否有效 + /// + public bool Validate() { + return !string.IsNullOrWhiteSpace( Name ); + } + + /// + /// 获取结果 + /// + public string ToResult() { + var builder = new StringBuilder(); + AppendTo( builder ); + return builder.ToString(); + } + + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + public virtual void AppendTo( StringBuilder builder ) { + AppendSchema( builder ); + AppendTable( builder ); + AppendTableAlias( builder ); + } + + /// + /// 添加架构名 + /// + private void AppendSchema( StringBuilder builder ) { + if ( Schema.IsEmpty() ) + return; + builder.AppendFormat( "{0}.", _dialect.GetSafeName( Schema ) ); + } + + /// + /// 添加表名 + /// + private void AppendTable( StringBuilder builder ) { + if( Name.IsEmpty() ) + return; + builder.AppendFormat( _dialect.GetSafeName( Name ) ); + } + + /// + /// 添加表别名 + /// + private void AppendTableAlias( StringBuilder builder ) { + if( TableAlias.IsEmpty() ) + return; + builder.AppendFormat( " {0}", _dialect.GetSafeName( TableAlias ) ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/IConditionFactory.cs b/src/Util.Data.Sql/Builders/IConditionFactory.cs new file mode 100644 index 000000000..4f34fccc1 --- /dev/null +++ b/src/Util.Data.Sql/Builders/IConditionFactory.cs @@ -0,0 +1,26 @@ +using Util.Data.Queries; + +namespace Util.Data.Sql.Builders; + +/// +/// Sql条件工厂 +/// +public interface IConditionFactory { + /// + /// 创建Sql条件 + /// + /// 列名 + /// 值 + /// 操作 + /// 是否参数化 + ISqlCondition Create( string column, object value, Operator @operator, bool isParameterization = true ); + /// + /// 创建Sql范围条件 + /// + /// 列名 + /// 最小值 + /// 最大值 + /// 包含边界 + /// 是否参数化 + ISqlCondition Create( string column, object minValue,object maxValue, Boundary boundary, bool isParameterization = true ); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/IDialect.cs b/src/Util.Data.Sql/Builders/IDialect.cs new file mode 100644 index 000000000..ce2774938 --- /dev/null +++ b/src/Util.Data.Sql/Builders/IDialect.cs @@ -0,0 +1,29 @@ +namespace Util.Data.Sql.Builders; + +/// +/// Sql方言 +/// +public interface IDialect { + /// + /// 获取起始转义标识符 + /// + string GetOpeningIdentifier(); + /// + /// 获取结束转义标识符 + /// + string GetClosingIdentifier(); + /// + /// 获取安全名称 + /// + /// 名称 + string GetSafeName( string name ); + /// + /// 获取参数前缀 + /// + string GetPrefix(); + /// + /// 替换Sql,将[]替换为特定Sql转义符 + /// + /// 原始Sql + string ReplaceSql( string sql ); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/IExistsSqlBuilder.cs b/src/Util.Data.Sql/Builders/IExistsSqlBuilder.cs new file mode 100644 index 000000000..daee8fe3c --- /dev/null +++ b/src/Util.Data.Sql/Builders/IExistsSqlBuilder.cs @@ -0,0 +1,11 @@ +namespace Util.Data.Sql.Builders; + +/// +/// 判断是否存在Sql生成器 +/// +public interface IExistsSqlBuilder { + /// + /// 获取Sql语句 + /// + string GetSql(); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/ISqlContent.cs b/src/Util.Data.Sql/Builders/ISqlContent.cs new file mode 100644 index 000000000..c1f2932cc --- /dev/null +++ b/src/Util.Data.Sql/Builders/ISqlContent.cs @@ -0,0 +1,12 @@ +namespace Util.Data.Sql.Builders; + +/// +/// Sql内容 +/// +public interface ISqlContent { + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + void AppendTo( StringBuilder builder ); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/ISqlPartAccessor.cs b/src/Util.Data.Sql/Builders/ISqlPartAccessor.cs new file mode 100644 index 000000000..d090a314d --- /dev/null +++ b/src/Util.Data.Sql/Builders/ISqlPartAccessor.cs @@ -0,0 +1,59 @@ +using Util.Data.Sql.Builders.Clauses; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Builders.Sets; + +namespace Util.Data.Sql.Builders; + +/// +/// Sql组件访问器 +/// +public interface ISqlPartAccessor { + /// + /// Sql方言 + /// + IDialect Dialect { get; } + /// + /// 参数管理器 + /// + IParameterManager ParameterManager { get; } + /// + /// 起始子句 + /// + IStartClause StartClause { get; } + /// + /// Select子句 + /// + ISelectClause SelectClause { get; } + /// + /// From子句 + /// + IFromClause FromClause { get; } + /// + /// Join子句 + /// + IJoinClause JoinClause { get; } + /// + /// Where子句 + /// + IWhereClause WhereClause { get; } + /// + /// GroupBy子句 + /// + IGroupByClause GroupByClause { get; } + /// + /// OrderBy子句 + /// + IOrderByClause OrderByClause { get; } + /// + /// Insert子句 + /// + IInsertClause InsertClause { get; } + /// + /// 结束子句 + /// + IEndClause EndClause { get; } + /// + /// Sql生成器集合 + /// + ISqlBuilderSet SqlBuilderSet { get; } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Operations/IEnd.cs b/src/Util.Data.Sql/Builders/Operations/IEnd.cs new file mode 100644 index 000000000..ad3c510a8 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Operations/IEnd.cs @@ -0,0 +1,7 @@ +namespace Util.Data.Sql.Builders.Operations; + +/// +/// 结束子句操作 +/// +public interface IEnd { +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Operations/IFrom.cs b/src/Util.Data.Sql/Builders/Operations/IFrom.cs new file mode 100644 index 000000000..0bbd77cf1 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Operations/IFrom.cs @@ -0,0 +1,7 @@ +namespace Util.Data.Sql.Builders.Operations; + +/// +/// From子句操作 +/// +public interface IFrom { +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Operations/IGroupBy.cs b/src/Util.Data.Sql/Builders/Operations/IGroupBy.cs new file mode 100644 index 000000000..15051eba1 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Operations/IGroupBy.cs @@ -0,0 +1,7 @@ +namespace Util.Data.Sql.Builders.Operations; + +/// +/// GroupBy子句操作 +/// +public interface IGroupBy { +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Operations/IInsert.cs b/src/Util.Data.Sql/Builders/Operations/IInsert.cs new file mode 100644 index 000000000..465a653ac --- /dev/null +++ b/src/Util.Data.Sql/Builders/Operations/IInsert.cs @@ -0,0 +1,7 @@ +namespace Util.Data.Sql.Builders.Operations; + +/// +/// 插入子句操作 +/// +public interface IInsert { +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Operations/IJoin.cs b/src/Util.Data.Sql/Builders/Operations/IJoin.cs new file mode 100644 index 000000000..d49f2db24 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Operations/IJoin.cs @@ -0,0 +1,7 @@ +namespace Util.Data.Sql.Builders.Operations; + +/// +/// Join子句操作 +/// +public interface IJoin { +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Operations/IOrderBy.cs b/src/Util.Data.Sql/Builders/Operations/IOrderBy.cs new file mode 100644 index 000000000..4a4b9095f --- /dev/null +++ b/src/Util.Data.Sql/Builders/Operations/IOrderBy.cs @@ -0,0 +1,7 @@ +namespace Util.Data.Sql.Builders.Operations; + +/// +/// OrderBy子句操作 +/// +public interface IOrderBy { +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Operations/ISelect.cs b/src/Util.Data.Sql/Builders/Operations/ISelect.cs new file mode 100644 index 000000000..acbfa4906 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Operations/ISelect.cs @@ -0,0 +1,7 @@ +namespace Util.Data.Sql.Builders.Operations; + +/// +/// Select子句操作 +/// +public interface ISelect { +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Operations/ISet.cs b/src/Util.Data.Sql/Builders/Operations/ISet.cs new file mode 100644 index 000000000..9585acf24 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Operations/ISet.cs @@ -0,0 +1,7 @@ +namespace Util.Data.Sql.Builders.Operations; + +/// +/// Sql集合操作 +/// +public interface ISet { +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Operations/ISqlOperation.cs b/src/Util.Data.Sql/Builders/Operations/ISqlOperation.cs new file mode 100644 index 000000000..47fe6384f --- /dev/null +++ b/src/Util.Data.Sql/Builders/Operations/ISqlOperation.cs @@ -0,0 +1,7 @@ +namespace Util.Data.Sql.Builders.Operations; + +/// +/// Sql操作 +/// +public interface ISqlOperation : ISqlQueryOperation, IInsert { +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Operations/ISqlParameter.cs b/src/Util.Data.Sql/Builders/Operations/ISqlParameter.cs new file mode 100644 index 000000000..723bf7dc6 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Operations/ISqlParameter.cs @@ -0,0 +1,7 @@ +namespace Util.Data.Sql.Builders.Operations; + +/// +/// Sql参数操作 +/// +public interface ISqlParameter { +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Operations/ISqlQueryOperation.cs b/src/Util.Data.Sql/Builders/Operations/ISqlQueryOperation.cs new file mode 100644 index 000000000..a8c3cffc8 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Operations/ISqlQueryOperation.cs @@ -0,0 +1,7 @@ +namespace Util.Data.Sql.Builders.Operations; + +/// +/// Sql查询操作 +/// +public interface ISqlQueryOperation : IStart, ISelect, IFrom, IJoin, IWhere, IGroupBy, IOrderBy, IEnd, ISqlParameter, ISet { +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Operations/IStart.cs b/src/Util.Data.Sql/Builders/Operations/IStart.cs new file mode 100644 index 000000000..7ad690f59 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Operations/IStart.cs @@ -0,0 +1,7 @@ +namespace Util.Data.Sql.Builders.Operations; + +/// +/// 起始子句操作 +/// +public interface IStart { +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Operations/IWhere.cs b/src/Util.Data.Sql/Builders/Operations/IWhere.cs new file mode 100644 index 000000000..56a8e18ed --- /dev/null +++ b/src/Util.Data.Sql/Builders/Operations/IWhere.cs @@ -0,0 +1,7 @@ +namespace Util.Data.Sql.Builders.Operations; + +/// +/// Where子句操作 +/// +public interface IWhere { +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Params/IClearParameters.cs b/src/Util.Data.Sql/Builders/Params/IClearParameters.cs new file mode 100644 index 000000000..a0ea55d1a --- /dev/null +++ b/src/Util.Data.Sql/Builders/Params/IClearParameters.cs @@ -0,0 +1,11 @@ +namespace Util.Data.Sql.Builders.Params; + +/// +/// 清空Sql参数 +/// +public interface IClearParameters { + /// + /// 清空Sql参数 + /// + void ClearParams(); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Params/IGetParameter.cs b/src/Util.Data.Sql/Builders/Params/IGetParameter.cs new file mode 100644 index 000000000..47f7fb763 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Params/IGetParameter.cs @@ -0,0 +1,13 @@ +namespace Util.Data.Sql.Builders.Params; + +/// +/// 获取Sql参数 +/// +public interface IGetParameter { + /// + /// 获取Sql参数值 + /// + /// 参数值类型 + /// 参数名 + T GetParam( string name ); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Params/IParamLiteralsResolver.cs b/src/Util.Data.Sql/Builders/Params/IParamLiteralsResolver.cs new file mode 100644 index 000000000..cbb01afdc --- /dev/null +++ b/src/Util.Data.Sql/Builders/Params/IParamLiteralsResolver.cs @@ -0,0 +1,12 @@ +namespace Util.Data.Sql.Builders.Params; + +/// +/// 参数字面值解析器 +/// +public interface IParamLiteralsResolver { + /// + /// 获取参数字面值 + /// + /// 参数值 + string GetParamLiterals( object value ); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Params/IParameterManager.cs b/src/Util.Data.Sql/Builders/Params/IParameterManager.cs new file mode 100644 index 000000000..0578695fb --- /dev/null +++ b/src/Util.Data.Sql/Builders/Params/IParameterManager.cs @@ -0,0 +1,63 @@ +namespace Util.Data.Sql.Builders.Params; + +/// +/// Sql参数管理器 +/// +public interface IParameterManager { + /// + /// 创建参数名 + /// + string GenerateName(); + /// + /// 标准化参数名 + /// + /// 参数名 + string NormalizeName( string name ); + /// + /// 添加动态参数 + /// + /// 动态参数 + void AddDynamicParams( object param ); + /// + /// 添加参数,如果参数已存在则替换 + /// + /// 参数名 + /// 参数值 + /// 参数类型 + /// 参数方向 + /// 字段长度 + /// 数值有效位数 + /// 数值小数位数 + void Add( string name, object value = null, DbType? dbType = null, ParameterDirection? direction = null, int? size = null, byte? precision = null, byte? scale = null ); + /// + /// 获取动态参数列表 + /// + IReadOnlyList GetDynamicParams(); + /// + /// 获取参数列表 + /// + IReadOnlyList GetParams(); + /// + /// 是否包含参数 + /// + /// 参数名 + bool Contains( string name ); + /// + /// 获取参数 + /// + /// 参数名 + SqlParam GetParam( string name ); + /// + /// 获取参数值 + /// + /// 参数名 + object GetValue( string name ); + /// + /// 清空参数 + /// + void Clear(); + /// + /// 复制Sql参数管理器副本 + /// + IParameterManager Clone(); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Params/ParamLiteralsResolver.cs b/src/Util.Data.Sql/Builders/Params/ParamLiteralsResolver.cs new file mode 100644 index 000000000..8b3fbb6ed --- /dev/null +++ b/src/Util.Data.Sql/Builders/Params/ParamLiteralsResolver.cs @@ -0,0 +1,28 @@ +namespace Util.Data.Sql.Builders.Params; + +/// +/// 参数字面值解析器 +/// +public class ParamLiteralsResolver : IParamLiteralsResolver { + /// + /// 获取参数字面值 + /// + /// 参数值 + public string GetParamLiterals( object value ) { + if( value == null ) + return "''"; + switch( value.GetType().Name.ToLower() ) { + case "boolean": + return Helpers.Convert.ToBool( value ) ? "1" : "0"; + case "int16": + case "int32": + case "int64": + case "single": + case "double": + case "decimal": + return value.SafeString(); + default: + return $"'{value}'"; + } + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Params/ParameterManager.cs b/src/Util.Data.Sql/Builders/Params/ParameterManager.cs new file mode 100644 index 000000000..36bbefb43 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Params/ParameterManager.cs @@ -0,0 +1,162 @@ +namespace Util.Data.Sql.Builders.Params; + +/// +/// Sql参数管理器 +/// +public class ParameterManager : IParameterManager { + /// + /// Sql方言 + /// + protected readonly IDialect Dialect; + /// + /// 参数集合 + /// + protected readonly IDictionary SqlParams; + /// + /// 动态参数集合 + /// + protected readonly List DynamicParams; + /// + /// 参数索引 + /// + protected int ParamIndex; + + /// + /// 初始化Sql参数管理器 + /// + /// Sql方言 + public ParameterManager( IDialect dialect ) { + Dialect = dialect; + ParamIndex = 0; + SqlParams = new Dictionary(); + DynamicParams = new List(); + } + + /// + /// 初始化Sql参数管理器 + /// + /// Sql参数管理器 + public ParameterManager( ParameterManager manager ) { + Dialect = manager.Dialect; + ParamIndex = manager.ParamIndex; + SqlParams = new Dictionary( manager.SqlParams ); + DynamicParams = new List( manager.DynamicParams ); + } + + /// + /// 创建参数名 + /// + public virtual string GenerateName() { + var result = $"{Dialect.GetPrefix()}_p_{ParamIndex}"; + ParamIndex++; + return result; + } + + /// + /// 标准化参数名 + /// + /// 参数名 + public virtual string NormalizeName( string name ) { + if ( name.IsEmpty() ) + return name; + name = name.Trim(); + if ( name.StartsWith( Dialect.GetPrefix() ) ) + return name; + return $"{Dialect.GetPrefix()}{name}"; + } + + /// + /// 添加动态参数 + /// + /// 动态参数 + public virtual void AddDynamicParams( object param ) { + if ( param == null ) + return; + DynamicParams.Add( param ); + } + + /// + /// 添加参数,如果参数已存在则替换 + /// + /// 参数名 + /// 参数值 + /// 参数类型 + /// 参数方向 + /// 字段长度 + /// 数值有效位数 + /// 数值小数位数 + public virtual void Add( string name, object value = null, DbType? dbType = null, ParameterDirection? direction = null, int? size = null, byte? precision = null, byte? scale = null ) { + if ( name.IsEmpty() ) + return; + name = NormalizeName( name ); + if ( SqlParams.ContainsKey( name ) ) + SqlParams.Remove( name ); + value = ConvertValue( value ); + var param = new SqlParam( name, value, dbType, direction, size, precision, scale ); + SqlParams.Add( name, param ); + } + + /// + /// 转换参数值 + /// + /// 参数值 + protected virtual object ConvertValue( object value ) { + return value; + } + + /// + /// 获取动态参数列表 + /// + public IReadOnlyList GetDynamicParams() { + return DynamicParams; + } + + /// + /// 获取参数列表 + /// + public IReadOnlyList GetParams() { + return SqlParams.Values.ToList(); + } + + /// + /// 是否包含参数 + /// + /// 参数名 + public virtual bool Contains( string name ) { + name = NormalizeName( name ); + return SqlParams.ContainsKey( name ); + } + + /// + /// 获取参数 + /// + /// 参数名 + public virtual SqlParam GetParam( string name ) { + name = NormalizeName( name ); + return SqlParams.TryGetValue( name, out var param ) ? param : null; + } + + /// + /// 获取参数值 + /// + /// 参数名 + public virtual object GetValue( string name ) { + name = NormalizeName( name ); + return SqlParams.TryGetValue( name, out var param ) ? param.Value : null; + } + + /// + /// 清空参数 + /// + public virtual void Clear() { + ParamIndex = 0; + SqlParams.Clear(); + } + + /// + /// 复制副本 + /// + public IParameterManager Clone() { + return new ParameterManager( this ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Params/SqlParam.cs b/src/Util.Data.Sql/Builders/Params/SqlParam.cs new file mode 100644 index 000000000..62eddc8e0 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Params/SqlParam.cs @@ -0,0 +1,55 @@ +namespace Util.Data.Sql.Builders.Params; + +/// +/// Sql参数 +/// +public class SqlParam { + /// + /// 初始化Sql参数 + /// + /// 参数名 + /// 参数值 + /// 参数类型 + /// 参数方向 + /// 字段长度 + /// 数值有效位数 + /// 数值小数位数 + public SqlParam( string name, object value, DbType? dbType = null, ParameterDirection? direction = null, int? size = null, byte? precision = null, byte? scale = null ) { + Name = name; + Value = value; + Direction = direction; + DbType = dbType; + Size = size; + Precision = precision; + Scale = scale; + } + + /// + /// 参数名 + /// + public string Name { get; } + /// + /// 参数值 + /// + public object Value { get; } + /// + /// 参数方向 + /// + public ParameterDirection? Direction { get; } + /// + /// 参数类型 + /// + public DbType? DbType { get; } + /// + /// 字段长度 + /// + public int? Size { get; } + /// + /// 数值有效位数 + /// + public byte? Precision { get; } + /// + /// 数值小数位数 + /// + public byte? Scale { get; } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Sets/ISqlBuilderSet.cs b/src/Util.Data.Sql/Builders/Sets/ISqlBuilderSet.cs new file mode 100644 index 000000000..9971558e1 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Sets/ISqlBuilderSet.cs @@ -0,0 +1,60 @@ +namespace Util.Data.Sql.Builders.Sets; + +/// +/// Sql生成器集合 +/// +public interface ISqlBuilderSet { + /// + /// 合并结果集 + /// + /// Sql生成器集合 + void Union( params ISqlBuilder[] builders ); + /// + /// 合并结果集 + /// + /// Sql生成器集合 + void Union( IEnumerable builders ); + /// + /// 合并结果集 + /// + /// Sql生成器集合 + void UnionAll( params ISqlBuilder[] builders ); + /// + /// 合并结果集 + /// + /// Sql生成器集合 + void UnionAll( IEnumerable builders ); + /// + /// 交集 + /// + /// Sql生成器集合 + void Intersect( params ISqlBuilder[] builders ); + /// + /// 交集 + /// + /// Sql生成器集合 + void Intersect( IEnumerable builders ); + /// + /// 差集 + /// + /// Sql生成器集合 + void Except( params ISqlBuilder[] builders ); + /// + /// 差集 + /// + /// Sql生成器集合 + void Except( IEnumerable builders ); + /// + /// 清理 + /// + void Clear(); + /// + /// 获取结果 + /// + string ToResult(); + /// + /// 复制Sql生成器集合 + /// + /// Sql生成器 + ISqlBuilderSet Clone( SqlBuilderBase builder ); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Sets/SqlBuilderSet.cs b/src/Util.Data.Sql/Builders/Sets/SqlBuilderSet.cs new file mode 100644 index 000000000..8bfe35d74 --- /dev/null +++ b/src/Util.Data.Sql/Builders/Sets/SqlBuilderSet.cs @@ -0,0 +1,250 @@ +using Util.Data.Sql.Builders.Clauses; +using Util.Helpers; + +namespace Util.Data.Sql.Builders.Sets; + +/// +/// Sql生成器集合 +/// +public class SqlBuilderSet : ISqlBuilderSet { + + #region 字段 + + /// + /// 主Sql生成器 + /// + protected ISqlBuilder MasterBuilder; + /// + /// Sql生成器集合 + /// + protected List SetItems; + + #endregion + + #region 构造方法 + + /// + /// 初始化Sql生成器集合 + /// + /// 主Sql生成器 + /// Sql生成器集合 + public SqlBuilderSet( ISqlBuilder builder, List setItems = null ) { + MasterBuilder = builder ?? throw new ArgumentNullException( nameof( builder ) ); + SetItems = setItems ?? new List(); + } + + #endregion + + #region Set(集合操作) + + /// + /// 集合操作 + /// + /// 操作符 + /// Sql生成器集合 + protected void Set( string @operator, IEnumerable builders ) { + if( builders == null ) + return; + foreach( var builder in builders ) { + SetItems.Add( new SqlBuilderSetItem( @operator, builder ) ); + } + } + + #endregion + + #region Union(合并结果集) + + /// + /// 合并结果集 + /// + /// Sql生成器集合 + public virtual void Union( params ISqlBuilder[] builders ) { + Set( "Union", builders ); + } + + /// + /// 合并结果集 + /// + /// Sql生成器集合 + public virtual void Union( IEnumerable builders ) { + Set( "Union", builders ); + } + + #endregion + + #region UnionAll(合并结果集) + + /// + /// 合并结果集 + /// + /// Sql生成器集合 + public virtual void UnionAll( params ISqlBuilder[] builders ) { + Set( "Union All", builders ); + } + + /// + /// 合并结果集 + /// + /// Sql生成器集合 + public virtual void UnionAll( IEnumerable builders ) { + Set( "Union All", builders ); + } + + #endregion + + #region Intersect(交集) + + /// + /// 交集 + /// + /// Sql生成器集合 + public virtual void Intersect( params ISqlBuilder[] builders ) { + Set( "Intersect", builders ); + } + + /// + /// 交集 + /// + /// Sql生成器集合 + public virtual void Intersect( IEnumerable builders ) { + Set( "Intersect", builders ); + } + + #endregion + + #region Except(差集) + + /// + /// 差集 + /// + /// Sql生成器集合 + public virtual void Except( params ISqlBuilder[] builders ) { + Set( "Except", builders ); + } + + /// + /// 差集 + /// + /// Sql生成器集合 + public virtual void Except( IEnumerable builders ) { + Set( "Except", builders ); + } + + #endregion + + #region ToResult(获取结果) + + /// + /// 获取结果 + /// + public string ToResult() { + if( SetItems.Count == 0 ) + return GetMasterResult(); + return GetSetResult(); + } + + /// + /// 获取主查询结果 + /// + protected virtual string GetMasterResult() { + var builder = new StringBuilder(); + MasterBuilder.AppendTo( builder ); + return builder.ToString(); + } + + /// + /// 获取集合操作结果 + /// + protected virtual string GetSetResult() { + var builder = new StringBuilder(); + AppendMasterSql( builder ); + AppendSetSql( builder ); + AppendEndSql( builder ); + return builder.ToString(); + } + + /// + /// 添加主查询结果 + /// + protected virtual void AppendMasterSql( StringBuilder result ) { + var accessor = ToSqlPartAccessor( MasterBuilder ); + AppendSql( result, accessor.StartClause ); + result.AppendLine( "(" ); + AppendSql( result, accessor.SelectClause ); + AppendSql( result, accessor.FromClause ); + AppendSql( result, accessor.JoinClause ); + AppendSql( result, accessor.WhereClause ); + AppendSql( result, accessor.GroupByClause ); + result.AppendLine( ")" ); + } + + /// + /// 添加集合Sql + /// + protected virtual void AppendSetSql( StringBuilder result ) { + foreach ( var item in SetItems ) { + var accessor = ToSqlPartAccessor( item.Builder ); + result.AppendFormat( "{0} ", item.Operator ); + result.AppendLine(); + result.AppendLine( "(" ); + AppendSql( result, accessor.SelectClause ); + AppendSql( result, accessor.FromClause ); + AppendSql( result, accessor.JoinClause ); + AppendSql( result, accessor.WhereClause ); + AppendSql( result, accessor.GroupByClause ); + result.AppendLine( ")" ); + } + } + + /// + /// 添加结束Sql + /// + protected virtual void AppendEndSql( StringBuilder result ) { + var accessor = ToSqlPartAccessor( MasterBuilder ); + AppendSql( result, accessor.OrderByClause ); + AppendSql( result, accessor.EndClause ); + result.RemoveEnd( $" {Common.Line}" ); + } + + /// + /// 转换为Sql组件访问器 + /// + protected ISqlPartAccessor ToSqlPartAccessor( ISqlBuilder builder ) { + var accessor = builder as ISqlPartAccessor; + if( accessor == null ) + throw new NotImplementedException( nameof( ISqlPartAccessor ) ); + return accessor; + } + + /// + /// 添加Sql + /// + protected void AppendSql( StringBuilder builder, ISqlClause content ) { + if( content.Validate() == false ) + return; + content.AppendTo( builder ); + builder.AppendLine( " " ); + } + + #endregion + + #region Clear(清理) + + /// + /// 清理 + /// + public void Clear() { + SetItems.Clear(); + } + + #endregion + + #region Clone(复制Sql生成器集合) + + /// + public virtual ISqlBuilderSet Clone( SqlBuilderBase builder ) { + return new SqlBuilderSet( builder, SetItems.Select( t => t.Clone() ).ToList() ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/Sets/SqlBuilderSetItem.cs b/src/Util.Data.Sql/Builders/Sets/SqlBuilderSetItem.cs new file mode 100644 index 000000000..3819a628b --- /dev/null +++ b/src/Util.Data.Sql/Builders/Sets/SqlBuilderSetItem.cs @@ -0,0 +1,33 @@ +namespace Util.Data.Sql.Builders.Sets; + +/// +/// Sql生成器集合项 +/// +public class SqlBuilderSetItem { + /// + /// 初始化Sql生成器集合项 + /// + /// 操作 + /// Sql生成器实例 + public SqlBuilderSetItem( string @operator, ISqlBuilder builder ) { + Operator = @operator; + Builder = builder; + } + + /// + /// 操作 + /// + public string Operator { get; } + + /// + /// Sql生成器 + /// + public ISqlBuilder Builder { get; } + + /// + /// 复制Sql生成器集合项 + /// + public SqlBuilderSetItem Clone() { + return new SqlBuilderSetItem( Operator, Builder.Clone() ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/SqlBuilderBase.cs b/src/Util.Data.Sql/Builders/SqlBuilderBase.cs new file mode 100644 index 000000000..482e42a54 --- /dev/null +++ b/src/Util.Data.Sql/Builders/SqlBuilderBase.cs @@ -0,0 +1,348 @@ +using Util.Data.Sql.Builders.Caches; +using Util.Data.Sql.Builders.Clauses; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Builders.Sets; +using Util.Helpers; + +namespace Util.Data.Sql.Builders; + +/// +/// Sql生成器 +/// +public abstract class SqlBuilderBase : ISqlBuilder, ISqlPartAccessor { + + #region 字段 + + /// + /// Sql方言 + /// + private IDialect _dialect; + /// + /// 列缓存 + /// + private IColumnCache _columnCache; + /// + /// 参数管理器 + /// + private IParameterManager _parameterManager; + /// + /// Sql条件工厂 + /// + private IConditionFactory _conditionFactory; + /// + /// 起始子句 + /// + private IStartClause _startClause; + /// + /// Insert子句 + /// + private IInsertClause _insertClause; + /// + /// Select子句 + /// + private ISelectClause _selectClause; + /// + /// From子句 + /// + private IFromClause _fromClause; + /// + /// Join子句 + /// + private IJoinClause _joinClause; + /// + /// Where子句 + /// + private IWhereClause _whereClause; + /// + /// GroupBy子句 + /// + private IGroupByClause _groupByClause; + /// + /// OrderBy子句 + /// + private IOrderByClause _orderByClause; + /// + /// 结束子句 + /// + private IEndClause _endClause; + /// + /// Sql生成器集合 + /// + private ISqlBuilderSet _sqlBuilderSet; + + #endregion + + #region 构造方法 + + /// + /// 初始化Sql生成器 + /// + /// Sql参数管理器 + protected SqlBuilderBase( IParameterManager parameterManager = null ) { + _parameterManager = parameterManager; + } + + #endregion + + #region 属性 + + /// + /// Sql方言 + /// + public IDialect Dialect => _dialect ??= CreateDialect(); + /// + /// 列缓存 + /// + public IColumnCache ColumnCache => _columnCache ??= CreateColumnCache(); + /// + /// 参数管理器 + /// + public IParameterManager ParameterManager => _parameterManager ??= CreateParameterManager(); + /// + /// Sql条件工厂 + /// + public IConditionFactory ConditionFactory => _conditionFactory ??= CreateConditionFactory(); + /// + /// 起始子句 + /// + public IStartClause StartClause => _startClause ??= CreateStartClause(); + /// + /// Select子句 + /// + public ISelectClause SelectClause => _selectClause ??= CreateSelectClause(); + /// + /// From子句 + /// + public IFromClause FromClause => _fromClause ??= CreateFromClause(); + /// + /// Join子句 + /// + public IJoinClause JoinClause => _joinClause ??= CreateJoinClause(); + /// + /// Where子句 + /// + public IWhereClause WhereClause => _whereClause ??= CreatewWhereClause(); + /// + /// GroupBy子句 + /// + public IGroupByClause GroupByClause => _groupByClause ??= CreateGroupByClause(); + /// + /// OrderBy子句 + /// + public IOrderByClause OrderByClause => _orderByClause ??= CreateOrderByClause(); + /// + /// Insert子句 + /// + public IInsertClause InsertClause => _insertClause ??= CreateInsertClause(); + /// + /// 结束子句 + /// + public IEndClause EndClause => _endClause ??= CreateEndClause(); + /// + /// Sql生成器集合 + /// + public ISqlBuilderSet SqlBuilderSet => _sqlBuilderSet ??= CreateSqlBuilderSet(); + + #endregion + + #region 工厂方法 + + /// + /// 创建Sql方言 + /// + protected abstract IDialect CreateDialect(); + + /// + /// 创建列缓存 + /// + protected abstract IColumnCache CreateColumnCache(); + + /// + /// 创建参数管理器 + /// + protected virtual IParameterManager CreateParameterManager() { + return new ParameterManager( Dialect ); + } + + /// + /// 创建Sql条件工厂 + /// + protected virtual IConditionFactory CreateConditionFactory() { + return new SqlConditionFactory( ParameterManager ); + } + + /// + /// 创建起始子句 + /// + protected virtual IStartClause CreateStartClause() { + return new StartClause( this ); + } + + /// + /// 创建Insert子句 + /// + protected virtual IInsertClause CreateInsertClause() { + return new InsertClause( this ); + } + + /// + /// 创建Select子句 + /// + protected virtual ISelectClause CreateSelectClause() { + return new SelectClause( this ); + } + + /// + /// 创建From子句 + /// + protected virtual IFromClause CreateFromClause() { + return new FromClause( this ); + } + + /// + /// 创建Join子句 + /// + protected virtual IJoinClause CreateJoinClause() { + return new JoinClause( this ); + } + + /// + /// 创建Where子句 + /// + protected virtual IWhereClause CreatewWhereClause() { + return new WhereClause( this ); + } + + /// + /// 创建分组子句 + /// + protected virtual IGroupByClause CreateGroupByClause() { + return new GroupByClause( this ); + } + + /// + /// 创建排序子句 + /// + protected virtual IOrderByClause CreateOrderByClause() { + return new OrderByClause( this ); + } + + /// + /// 创建结束子句 + /// + protected virtual IEndClause CreateEndClause() { + return new EndClause( this ); + } + + /// + /// 创建Sql生成器集合 + /// + protected virtual ISqlBuilderSet CreateSqlBuilderSet() { + return new SqlBuilderSet( this ); + } + + #endregion + + #region Clone(复制Sql生成器) + + /// + public abstract ISqlBuilder Clone(); + + /// + /// 复制Sql生成器 + /// + /// 源Sql生成器 + protected void Clone( SqlBuilderBase source ) { + _dialect = source._dialect; + _columnCache = source._columnCache; + _parameterManager = source._parameterManager?.Clone(); + _startClause = source._startClause?.Clone( this ); + _insertClause = source._insertClause?.Clone( this ); + _selectClause = source._selectClause?.Clone( this ); + _fromClause = source._fromClause?.Clone( this ); + _joinClause = source._joinClause?.Clone( this ); + _whereClause = source._whereClause?.Clone( this ); + _groupByClause = source._groupByClause?.Clone( this ); + _orderByClause = source._orderByClause?.Clone( this ); + _endClause = source._endClause?.Clone( this ); + _sqlBuilderSet = source._sqlBuilderSet?.Clone( this ); + } + + #endregion + + #region Clear(清理) + + /// + /// 清理 + /// + public ISqlBuilder Clear() { + _parameterManager?.Clear(); + _startClause?.Clear(); + _insertClause?.Clear(); + _selectClause?.Clear(); + _fromClause?.Clear(); + _joinClause?.Clear(); + _whereClause?.Clear(); + _groupByClause?.Clear(); + _orderByClause?.Clear(); + _endClause?.Clear(); + _sqlBuilderSet?.Clear(); + return this; + } + + #endregion + + #region New(创建Sql生成器) + + /// + /// 创建Sql生成器 + /// + public abstract ISqlBuilder New(); + + #endregion + + #region GetSql(获取Sql语句) + + /// + /// 生成Sql语句 + /// + public virtual string GetSql() { + return SqlBuilderSet.ToResult(); + } + + #endregion + + #region AppendTo(添加到字符串生成器) + + /// + /// 添加到字符串生成器 + /// + /// 字符串生成器 + public void AppendTo( StringBuilder builder ) { + builder.CheckNull( nameof( builder ) ); + AppendSql( builder, _startClause ); + AppendSql( builder, _insertClause ); + AppendSql( builder, _selectClause ); + AppendSql( builder, _fromClause ); + AppendSql( builder, _joinClause ); + AppendSql( builder, _whereClause ); + AppendSql( builder, _groupByClause ); + AppendSql( builder, _orderByClause ); + AppendSql( builder, _endClause ); + builder.RemoveEnd( $" {Common.Line}" ); + } + + /// + /// 添加Sql + /// + protected void AppendSql( StringBuilder builder, ISqlClause content ) { + if ( content == null ) + return; + if( content.Validate() == false ) + return; + content.AppendTo( builder ); + builder.AppendLine( " " ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/SqlBuilderResult.cs b/src/Util.Data.Sql/Builders/SqlBuilderResult.cs new file mode 100644 index 000000000..5ba258f13 --- /dev/null +++ b/src/Util.Data.Sql/Builders/SqlBuilderResult.cs @@ -0,0 +1,77 @@ +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Sql.Builders; + +/// +/// Sql生成器结果 +/// +public class SqlBuilderResult { + /// + /// Sql生成器结果 + /// + private readonly string _sql; + /// + /// Sql参数列表 + /// + private readonly List _sqlParams; + /// + /// 参数字面值解析器 + /// + private readonly IParamLiteralsResolver _resolver; + /// + /// Sql参数管理器 + /// + private readonly IParameterManager _parameterManager; + + /// + /// 初始化Sql生成器结果 + /// + /// Sql + /// Sql参数列表 + /// 参数字面值解析器 + /// Sql参数管理器 + public SqlBuilderResult( string sql, List sqlParams, IParamLiteralsResolver resolver, IParameterManager parameterManager ) { + _sql = sql; + _sqlParams = sqlParams ?? new List(); + _resolver = resolver; + _parameterManager = parameterManager; + } + + /// + /// 获取Sql语句 + /// + public string GetSql() { + return _sql; + } + + /// + /// 获取Sql参数列表 + /// + public List GetParams() { + return _sqlParams; + } + + /// + /// 获取Sql参数值 + /// + /// 参数值类型 + /// 参数名 + public T GetParam( string name ) { + name = _parameterManager.NormalizeName( name ); + var result = _sqlParams.Find( t => t.Name?.ToUpperInvariant() == name?.ToUpperInvariant() ); + if ( result == null ) + return default; + return Util.Helpers.Convert.To( result.Value ); + } + + /// + /// 获取调试Sql语句 + /// + public virtual string GetDebugSql() { + var sql = GetSql(); + var parameters = GetParams(); + foreach ( var parameter in parameters ) + sql = Regex.Replace( sql, $@"{parameter.Name}\b", _resolver.GetParamLiterals( parameter.Value ) ); + return sql; + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Builders/SqlConditionFactory.cs b/src/Util.Data.Sql/Builders/SqlConditionFactory.cs new file mode 100644 index 000000000..a296ff190 --- /dev/null +++ b/src/Util.Data.Sql/Builders/SqlConditionFactory.cs @@ -0,0 +1,76 @@ +using Util.Data.Queries; +using Util.Data.Sql.Builders.Conditions; +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Sql.Builders; + +/// +/// Sql条件工厂 +/// +public class SqlConditionFactory : IConditionFactory { + /// + /// Sql参数管理器 + /// + private readonly IParameterManager _parameterManager; + + /// + /// 初始化Sql条件工厂 + /// + /// Sql参数管理器 + public SqlConditionFactory( IParameterManager parameterManager ) { + _parameterManager = parameterManager ?? throw new ArgumentNullException( nameof( parameterManager ) ); + } + + /// + public virtual ISqlCondition Create( string column, object value, Operator @operator, bool isParameterization = true ) { + if( IsInCondition( @operator, value ) ) + return new InCondition( _parameterManager, column, value, isParameterization ); + if( IsNotInCondition( @operator, value ) ) + return new NotInCondition( _parameterManager, column, value, isParameterization ); + switch( @operator ) { + case Operator.Equal: + return new EqualCondition( _parameterManager, column, value, isParameterization ); + case Operator.NotEqual: + return new NotEqualCondition( _parameterManager, column, value, isParameterization ); + case Operator.Greater: + return new GreaterCondition( _parameterManager, column, value, isParameterization ); + case Operator.GreaterEqual: + return new GreaterEqualCondition( _parameterManager, column, value, isParameterization ); + case Operator.Less: + return new LessCondition( _parameterManager, column, value, isParameterization ); + case Operator.LessEqual: + return new LessEqualCondition( _parameterManager, column, value, isParameterization ); + case Operator.Contains: + return new ContainsCondition( _parameterManager, column, value, isParameterization ); + case Operator.Starts: + return new StartsCondition( _parameterManager, column, value, isParameterization ); + case Operator.Ends: + return new EndsCondition( _parameterManager, column, value, isParameterization ); + default: + throw new NotImplementedException(); + } + } + + /// + /// 是否In条件 + /// + private bool IsInCondition( Operator @operator, object value ) { + if( @operator == Operator.In ) + return true; + return false; + } + + /// + /// 是否Not In条件 + /// + private bool IsNotInCondition( Operator @operator, object value ) { + if( @operator == Operator.NotIn ) + return true; + return false; + } + + /// + public virtual ISqlCondition Create( string column, object minValue, object maxValue, Boundary boundary, bool isParameterization = true ) { + return new SegmentCondition( _parameterManager, column, minValue, maxValue, boundary, isParameterization ); + } +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Configs/ISqlOptions.cs b/src/Util.Data.Sql/Configs/ISqlOptions.cs new file mode 100644 index 000000000..8f2e059e6 --- /dev/null +++ b/src/Util.Data.Sql/Configs/ISqlOptions.cs @@ -0,0 +1,7 @@ +namespace Util.Data.Sql.Configs; + +/// +/// Sql配置操作 +/// +public interface ISqlOptions { +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Configs/ISqlOptionsAccessor.cs b/src/Util.Data.Sql/Configs/ISqlOptionsAccessor.cs new file mode 100644 index 000000000..78711e154 --- /dev/null +++ b/src/Util.Data.Sql/Configs/ISqlOptionsAccessor.cs @@ -0,0 +1,11 @@ +namespace Util.Data.Sql.Configs; + +/// +/// Sql配置访问器 +/// +public interface ISqlOptionsAccessor { + /// + /// 获取Sql配置 + /// + SqlOptions GetOptions(); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Database/IConnectionManager.cs b/src/Util.Data.Sql/Database/IConnectionManager.cs new file mode 100644 index 000000000..6fcb60f45 --- /dev/null +++ b/src/Util.Data.Sql/Database/IConnectionManager.cs @@ -0,0 +1,16 @@ +namespace Util.Data.Sql.Database; + +/// +/// 数据库连接管理器 +/// +public interface IConnectionManager { + /// + /// 设置数据库连接 + /// + /// 数据库连接 + void SetConnection( IDbConnection connection ); + /// + /// 获取数据库连接 + /// + IDbConnection GetConnection(); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Database/ITransactionManager.cs b/src/Util.Data.Sql/Database/ITransactionManager.cs new file mode 100644 index 000000000..bc07b2888 --- /dev/null +++ b/src/Util.Data.Sql/Database/ITransactionManager.cs @@ -0,0 +1,33 @@ +namespace Util.Data.Sql.Database; + +/// +/// 数据库事务管理器 +/// +public interface ITransactionManager { + /// + /// 设置数据库事务 + /// + /// 数据库事务 + void SetTransaction( IDbTransaction transaction ); + /// + /// 获取数据库事务 + /// + IDbTransaction GetTransaction(); + /// + /// 开始事务 + /// + IDbTransaction BeginTransaction(); + /// + /// 开始事务 + /// + /// 事务隔离级别 + IDbTransaction BeginTransaction( IsolationLevel isolationLevel ); + /// + /// 提交事务 + /// + void CommitTransaction(); + /// + /// 回滚事务 + /// + void RollbackTransaction(); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/EndClauseExtensions.cs b/src/Util.Data.Sql/EndClauseExtensions.cs new file mode 100644 index 000000000..72f910fb2 --- /dev/null +++ b/src/Util.Data.Sql/EndClauseExtensions.cs @@ -0,0 +1,106 @@ +using Util.Data.Queries; +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Operations; + +namespace Util.Data.Sql; + +/// +/// 结束子句操作扩展 +/// +public static class EndClauseExtensions { + + #region Skip(设置跳过行数) + + /// + /// 设置跳过行数 + /// + /// 源 + /// 跳过的行数 + public static T Skip( this T source, int count ) where T : IEnd { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.EndClause.Skip( count ); + return source; + } + + #endregion + + #region Take(设置获取行数) + + /// + /// 设置获取行数 + /// + /// 源 + /// 获取的行数 + public static T Take( this T source, int count ) where T : IEnd { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.EndClause.Take( count ); + return source; + } + + #endregion + + #region Page(设置分页) + + /// + /// 设置分页 + /// + /// 源 + /// 分页参数 + public static T Page( this T source, IPage page ) where T : IEnd { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.EndClause.Page( page ); + return source; + } + + #endregion + + #region AppendEnd(添加到Sql结束位置) + + /// + /// 添加到Sql结束位置 + /// + /// 源 + /// Sql语句 + /// 是否原样添加,默认会将[]替换为特定Sql转义符 + public static T AppendEnd( this T source, string sql, bool raw = false ) where T : IEnd { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.EndClause.AppendSql( sql, raw ); + return source; + } + + #endregion + + #region ClearPage(清理分页) + + /// + /// 清理分页 + /// + /// 源 + public static T ClearPage( this T source ) where T : IEnd { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.EndClause.ClearPage(); + return source; + } + + #endregion + + #region ClearEnd(清理结束子句) + + /// + /// 清理Sql结束子句 + /// + /// 源 + public static T ClearEnd( this T source ) where T : IEnd { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.EndClause.Clear(); + return source; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/FromClauseExtensions.cs b/src/Util.Data.Sql/FromClauseExtensions.cs new file mode 100644 index 000000000..5cd0a851d --- /dev/null +++ b/src/Util.Data.Sql/FromClauseExtensions.cs @@ -0,0 +1,84 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Operations; + +namespace Util.Data.Sql; + +/// +/// From子句操作扩展 +/// +public static class FromClauseExtensions { + + #region From(设置表) + + /// + /// 设置表名 + /// + /// 源 + /// 表名 + public static T From( this T source, string table ) where T : IFrom { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.FromClause.From( table ); + return source; + } + + /// + /// 设置子查询表 + /// + /// 源 + /// Sql生成器 + /// 表别名 + public static T From( this T source, ISqlBuilder builder, string alias ) where T : IFrom { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.FromClause.From( builder, alias ); + return source; + } + + /// + /// 设置子查询表 + /// + /// 源 + /// 子查询操作 + /// 表别名 + public static T From( this T source, Action action, string alias ) where T : IFrom { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.FromClause.From( action, alias ); + return source; + } + + #endregion + + #region AppendFrom(添加到From子句) + + /// + /// 添加到From子句 + /// + /// 源 + /// Sql语句 + /// 是否原样添加,默认会将[]替换为特定Sql转义符 + public static T AppendFrom( this T source, string sql,bool raw = false ) where T : IFrom { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.FromClause.AppendSql( sql, raw ); + return source; + } + + #endregion + + #region ClearFrom(清空From子句) + + /// + /// 清空From子句 + /// + /// 源 + public static T ClearFrom( this T source ) where T : IFrom { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.FromClause.Clear(); + return source; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/GroupByClauseExtensions.cs b/src/Util.Data.Sql/GroupByClauseExtensions.cs new file mode 100644 index 000000000..be601bf36 --- /dev/null +++ b/src/Util.Data.Sql/GroupByClauseExtensions.cs @@ -0,0 +1,94 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Operations; + +namespace Util.Data.Sql; + +/// +/// Group By子句操作扩展 +/// +public static class GroupByClauseExtensions { + + #region GroupBy(添加分组列) + + /// + /// 添加分组列 + /// + /// 源 + /// 分组字段,范例:a.Id,b.Name + public static T GroupBy( this T source, string columns ) where T : IGroupBy { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.GroupByClause.GroupBy( columns ); + return source; + } + + #endregion + + #region Having(添加分组条件) + + /// + /// 添加分组条件 + /// + /// 源 + /// 聚合表达式 + /// 值 + /// 运算符 + /// 是否参数化 + public static T Having( this T source, string expression, object value, Operator @operator = Operator.Equal, bool isParameterization = true ) where T : IGroupBy { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.GroupByClause.Having( expression, value , @operator , isParameterization ); + return source; + } + + #endregion + + #region AppendGroupBy(添加到Group By子句) + + /// + /// 添加到Group By子句 + /// + /// 源 + /// Sql语句 + /// 是否原样添加,默认会将[]替换为特定Sql转义符 + public static T AppendGroupBy( this T source, string sql, bool raw = false ) where T : IGroupBy { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.GroupByClause.AppendGroupBy( sql, raw ); + return source; + } + + #endregion + + #region AppendHaving(添加到Having子句) + + /// + /// 添加到Having子句 + /// + /// 源 + /// Sql语句 + /// 是否原样添加,默认会将[]替换为特定Sql转义符 + public static T AppendHaving( this T source, string sql, bool raw = false ) where T : IGroupBy { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.GroupByClause.AppendHaving( sql, raw ); + return source; + } + + #endregion + + #region ClearGroupBy(清空Group By子句) + + /// + /// 清空Group By子句 + /// + /// 源 + public static T ClearGroupBy( this T source ) where T : IGroupBy { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.GroupByClause.Clear(); + return source; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/ISqlBuilder.cs b/src/Util.Data.Sql/ISqlBuilder.cs new file mode 100644 index 000000000..cbadda9fe --- /dev/null +++ b/src/Util.Data.Sql/ISqlBuilder.cs @@ -0,0 +1,26 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Operations; + +namespace Util.Data.Sql; + +/// +/// Sql生成器 +/// +public interface ISqlBuilder : ISqlContent, ISqlOperation { + /// + /// 复制Sql生成器副本 + /// + ISqlBuilder Clone(); + /// + /// 创建一个新的Sql生成器 + /// + ISqlBuilder New(); + /// + /// 清理 + /// + ISqlBuilder Clear(); + /// + /// 获取Sql语句 + /// + string GetSql(); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/ISqlCondition.cs b/src/Util.Data.Sql/ISqlCondition.cs new file mode 100644 index 000000000..6bc3a9f6c --- /dev/null +++ b/src/Util.Data.Sql/ISqlCondition.cs @@ -0,0 +1,9 @@ +using Util.Data.Sql.Builders; + +namespace Util.Data.Sql; + +/// +/// Sql查询条件 +/// +public interface ISqlCondition : ISqlContent { +} \ No newline at end of file diff --git a/src/Util.Data.Sql/ISqlExecutor.cs b/src/Util.Data.Sql/ISqlExecutor.cs new file mode 100644 index 000000000..d2c65a7ef --- /dev/null +++ b/src/Util.Data.Sql/ISqlExecutor.cs @@ -0,0 +1,20 @@ +using Util.Data.Sql.Builders.Operations; + +namespace Util.Data.Sql; + +/// +/// Sql执行器 +/// +public interface ISqlExecutor : ISqlQuery, ISqlOperation { + /// + /// 执行Sql增删改操作 + /// + /// 执行超时时间,单位:秒 + Task ExecuteAsync( int? timeout = null ); + /// + /// 执行存储过程增删改操作 + /// + /// 存储过程 + /// 执行超时时间,单位:秒 + Task ExecuteProcedureAsync( string procedure, int? timeout = null ); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/ISqlQuery.cs b/src/Util.Data.Sql/ISqlQuery.cs new file mode 100644 index 000000000..d21c2450f --- /dev/null +++ b/src/Util.Data.Sql/ISqlQuery.cs @@ -0,0 +1,465 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Operations; +using Util.Data.Sql.Configs; + +namespace Util.Data.Sql; + +/// +/// Sql查询对象 +/// +public interface ISqlQuery : ISqlQueryOperation, ISqlOptions, IDisposable { + /// + /// 上下文标识 + /// + string ContextId { get; } + /// + /// 前一次执行的Sql及参数 + /// + SqlBuilderResult PreviousSql { get; } + /// + /// 获取Sql生成器 + /// + ISqlBuilder SqlBuilder { get; } + /// + /// 判断是否存在 + /// + bool ExecuteExists(); + /// + /// 判断是否存在 + /// + Task ExecuteExistsAsync(); + /// + /// 获取单值 + /// + /// 执行超时时间,单位:秒 + object ExecuteScalar( int? timeout = null ); + /// + /// 获取单值 + /// + /// 执行超时时间,单位:秒 + T ExecuteScalar( int? timeout = null ); + /// + /// 获取单值 + /// + /// 执行超时时间,单位:秒 + Task ExecuteScalarAsync( int? timeout = null ); + /// + /// 获取单值 + /// + /// 执行超时时间,单位:秒 + Task ExecuteScalarAsync( int? timeout = null ); + /// + /// 执行存储过程获取单值 + /// + /// 存储过程 + /// 执行超时时间,单位:秒 + object ExecuteProcedureScalar( string procedure, int? timeout = null ); + /// + /// 执行存储过程获取单值 + /// + /// 存储过程 + /// 执行超时时间,单位:秒 + T ExecuteProcedureScalar( string procedure, int? timeout = null ); + /// + /// 执行存储过程获取单值 + /// + /// 存储过程 + /// 执行超时时间,单位:秒 + Task ExecuteProcedureScalarAsync( string procedure, int? timeout = null ); + /// + /// 执行存储过程获取单值 + /// + /// 存储过程 + /// 执行超时时间,单位:秒 + Task ExecuteProcedureScalarAsync( string procedure, int? timeout = null ); + /// + /// 获取单个实体 + /// + /// 实体类型 + /// 执行超时时间,单位:秒 + TEntity ExecuteSingle( int? timeout = null ); + /// + /// 获取单个实体 + /// + /// 实体类型 + /// 执行超时时间,单位:秒 + Task ExecuteSingleAsync( int? timeout = null ); + /// + /// 执行存储过程获取单个实体 + /// + /// 实体类型 + /// 存储过程 + /// 执行超时时间,单位:秒 + TEntity ExecuteProcedureSingle( string procedure, int? timeout = null ); + /// + /// 执行存储过程获取单个实体 + /// + /// 实体类型 + /// 存储过程 + /// 执行超时时间,单位:秒 + Task ExecuteProcedureSingleAsync( string procedure, int? timeout = null ); + /// + /// 获取实体集合 + /// + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + List ExecuteQuery( int? timeout = null, bool buffered = true ); + /// + /// 获取实体集合 + /// + /// 实体类型 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + List ExecuteQuery( int? timeout = null, bool buffered = true ); + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 返回的实体类型 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + List ExecuteQuery( Func map, int? timeout = null, bool buffered = true ); + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 返回的实体类型 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + List ExecuteQuery( Func map, int? timeout = null, bool buffered = true ); + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 返回的实体类型 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + List ExecuteQuery( Func map, int? timeout = null, bool buffered = true ); + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 实体类型5 + /// 返回的实体类型 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + List ExecuteQuery( Func map, int? timeout = null, bool buffered = true ); + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 实体类型5 + /// 实体类型6 + /// 返回的实体类型 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + List ExecuteQuery( Func map, int? timeout = null, bool buffered = true ); + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 实体类型5 + /// 实体类型6 + /// 实体类型7 + /// 返回的实体类型 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + List ExecuteQuery( Func map, int? timeout = null, bool buffered = true ); + /// + /// 获取实体集合 + /// + /// 执行超时时间,单位:秒 + Task> ExecuteQueryAsync( int? timeout = null ); + /// + /// 获取实体集合 + /// + /// 实体类型 + /// 执行超时时间,单位:秒 + Task> ExecuteQueryAsync( int? timeout = null ); + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 返回的实体类型 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + Task> ExecuteQueryAsync( Func map, int? timeout = null, bool buffered = true ); + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 返回的实体类型 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + Task> ExecuteQueryAsync( Func map, int? timeout = null, bool buffered = true ); + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 返回的实体类型 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + Task> ExecuteQueryAsync( Func map, int? timeout = null, bool buffered = true ); + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 实体类型5 + /// 返回的实体类型 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + Task> ExecuteQueryAsync( Func map, int? timeout = null, bool buffered = true ); + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 实体类型5 + /// 实体类型6 + /// 返回的实体类型 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + Task> ExecuteQueryAsync( Func map, int? timeout = null, bool buffered = true ); + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 实体类型5 + /// 实体类型6 + /// 实体类型7 + /// 返回的实体类型 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + Task> ExecuteQueryAsync( Func map, int? timeout = null, bool buffered = true ); + /// + /// 执行存储过程获取实体集合 + /// + /// 存储过程 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + List ExecuteProcedureQuery( string procedure, int? timeout = null, bool buffered = true ); + /// + /// 执行存储过程获取实体集合 + /// + /// 实体类型 + /// 存储过程 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + List ExecuteProcedureQuery( string procedure, int? timeout = null, bool buffered = true ); + /// + /// 执行存储过程获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 返回的实体类型 + /// 存储过程 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + List ExecuteProcedureQuery( string procedure, Func map, int? timeout = null, bool buffered = true ); + /// + /// 执行存储过程获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 返回的实体类型 + /// 存储过程 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + List ExecuteProcedureQuery( string procedure, Func map, int? timeout = null, bool buffered = true ); + /// + /// 执行存储过程获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 返回的实体类型 + /// 存储过程 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + List ExecuteProcedureQuery( string procedure, Func map, int? timeout = null, bool buffered = true ); + /// + /// 执行存储过程获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 实体类型5 + /// 返回的实体类型 + /// 存储过程 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + List ExecuteProcedureQuery( string procedure, Func map, int? timeout = null, bool buffered = true ); + /// + /// 执行存储过程获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 实体类型5 + /// 实体类型6 + /// 返回的实体类型 + /// 存储过程 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + List ExecuteProcedureQuery( string procedure, Func map, int? timeout = null, bool buffered = true ); + /// + /// 执行存储过程获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 实体类型5 + /// 实体类型6 + /// 实体类型7 + /// 返回的实体类型 + /// 存储过程 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + List ExecuteProcedureQuery( string procedure, Func map, int? timeout = null, bool buffered = true ); + /// + /// 执行存储过程获取实体集合 + /// + /// 存储过程 + /// 执行超时时间,单位:秒 + Task> ExecuteProcedureQueryAsync( string procedure, int? timeout = null ); + /// + /// 执行存储过程获取实体集合 + /// + /// 实体类型 + /// 存储过程 + /// 执行超时时间,单位:秒 + Task> ExecuteProcedureQueryAsync( string procedure, int? timeout = null ); + /// + /// 执行存储过程获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 返回的实体类型 + /// 存储过程 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + Task> ExecuteProcedureQueryAsync( string procedure, Func map, int? timeout = null, bool buffered = true ); + /// + /// 执行存储过程获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 返回的实体类型 + /// 存储过程 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + Task> ExecuteProcedureQueryAsync( string procedure, Func map, int? timeout = null, bool buffered = true ); + /// + /// 执行存储过程获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 返回的实体类型 + /// 存储过程 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + Task> ExecuteProcedureQueryAsync( string procedure, Func map, int? timeout = null, bool buffered = true ); + /// + /// 执行存储过程获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 实体类型5 + /// 返回的实体类型 + /// 存储过程 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + Task> ExecuteProcedureQueryAsync( string procedure, Func map, int? timeout = null, bool buffered = true ); + /// + /// 执行存储过程获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 实体类型5 + /// 实体类型6 + /// 返回的实体类型 + /// 存储过程 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + Task> ExecuteProcedureQueryAsync( string procedure, Func map, int? timeout = null, bool buffered = true ); + /// + /// 执行存储过程获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 实体类型5 + /// 实体类型6 + /// 实体类型7 + /// 返回的实体类型 + /// 存储过程 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + Task> ExecuteProcedureQueryAsync( string procedure, Func map, int? timeout = null, bool buffered = true ); +} \ No newline at end of file diff --git a/src/Util.Data.Sql/InsertClauseExtensions.cs b/src/Util.Data.Sql/InsertClauseExtensions.cs new file mode 100644 index 000000000..f8b1f6866 --- /dev/null +++ b/src/Util.Data.Sql/InsertClauseExtensions.cs @@ -0,0 +1,97 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Operations; + +namespace Util.Data.Sql; + +/// +/// Insert子句操作扩展 +/// +public static class InsertClauseExtensions { + + #region Insert(设置插入的表和列名集合) + + /// + /// 设置插入的表和列名集合 + /// + /// 源 + /// 列名集合 + /// 表名 + public static T Insert( this T source, string columns, string table = null ) where T : IInsert { + if( source == null ) + throw new ArgumentNullException( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.InsertClause.Insert( columns,table ); + return source; + } + + #endregion + + #region Values(设置插入的值集合) + + /// + /// 设置Insert语句插入的参数值集合 + /// + /// 源 + /// 参数值集合 + public static T Values( this T source, params object[] values ) where T : IInsert { + if( source == null ) + throw new ArgumentNullException( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.InsertClause.Values( values ); + return source; + } + + #endregion + + #region AppendInsert(添加到Insert子句) + + /// + /// 添加到Insert子句 + /// + /// 源 + /// Sql语句 + /// 是否原样添加,默认会将[]替换为特定Sql转义符 + public static T AppendInsert( this T source, string sql,bool raw = false ) where T : IFrom { + if( source == null ) + throw new ArgumentNullException( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.InsertClause.AppendInsert( sql, raw ); + return source; + } + + #endregion + + #region AppendValues(添加到Values子句) + + /// + /// 添加到Values子句 + /// + /// 源 + /// Sql语句 + /// 是否原样添加,默认会将[]替换为特定Sql转义符 + public static T AppendValues( this T source, string sql, bool raw = false ) where T : IFrom { + if( source == null ) + throw new ArgumentNullException( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.InsertClause.AppendValues( sql, raw ); + return source; + } + + #endregion + + #region ClearInsert(清空Insert子句) + + /// + /// 清空Insert子句 + /// + /// 源 + public static T ClearInsert( this T source ) where T : IFrom { + if( source == null ) + throw new ArgumentNullException( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.InsertClause.Clear(); + return source; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/JoinClauseExtensions.cs b/src/Util.Data.Sql/JoinClauseExtensions.cs new file mode 100644 index 000000000..cd4db3468 --- /dev/null +++ b/src/Util.Data.Sql/JoinClauseExtensions.cs @@ -0,0 +1,238 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Operations; + +namespace Util.Data.Sql; + +/// +/// Join子句操作扩展 +/// +public static class JoinClauseExtensions { + + #region Join(内连接) + + /// + /// 内连接 + /// + /// 源 + /// 表名 + public static T Join( this T source, string table ) where T : IJoin { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.JoinClause.Join( table ); + return source; + } + + /// + /// 内连接子查询 + /// + /// 源 + /// Sql生成器 + /// 表别名 + public static T Join( this T source, ISqlBuilder builder, string alias ) where T : IJoin { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.JoinClause.Join( builder, alias ); + return source; + } + + /// + /// 内连接子查询 + /// + /// 源 + /// 子查询操作 + /// 表别名 + public static T Join( this T source, Action action, string alias ) where T : IJoin { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.JoinClause.Join( action, alias ); + return source; + } + + #endregion + + #region LeftJoin(左外连接) + + /// + /// 左外连接 + /// + /// 源 + /// 表名 + public static T LeftJoin( this T source, string table ) where T : IJoin { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.JoinClause.LeftJoin( table ); + return source; + } + + /// + /// 左外连接子查询 + /// + /// 源 + /// Sql生成器 + /// 表别名 + public static T LeftJoin( this T source, ISqlBuilder builder, string alias ) where T : IJoin { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.JoinClause.LeftJoin( builder, alias ); + return source; + } + + /// + /// 左外连接子查询 + /// + /// 源 + /// 子查询操作 + /// 表别名 + public static T LeftJoin( this T source, Action action, string alias ) where T : IJoin { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.JoinClause.LeftJoin( action, alias ); + return source; + } + + #endregion + + #region RightJoin(右外连接) + + /// + /// 右外连接 + /// + /// 源 + /// 表名 + public static T RightJoin( this T source, string table ) where T : IJoin { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.JoinClause.RightJoin( table ); + return source; + } + + /// + /// 右外连接子查询 + /// + /// 源 + /// Sql生成器 + /// 表别名 + public static T RightJoin( this T source, ISqlBuilder builder, string alias ) where T : IJoin { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.JoinClause.RightJoin( builder, alias ); + return source; + } + + /// + /// 右外连接子查询 + /// + /// 源 + /// 子查询操作 + /// 表别名 + public static T RightJoin( this T source, Action action, string alias ) where T : IJoin { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.JoinClause.RightJoin( action, alias ); + return source; + } + + #endregion + + #region On(设置连接条件) + + /// + /// 设置连接条件 + /// + /// 源 + /// 列名 + /// 值 + /// 运算符 + /// 是否参数化 + public static T On( this T source, string column, object value, Operator @operator = Operator.Equal, bool isParameterization = false ) where T : IJoin { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.JoinClause.On( column, value, @operator, isParameterization ); + return source; + } + + #endregion + + #region AppendJoin(添加到内连接子句) + + /// + /// 添加到内连接子句 + /// + /// 源 + /// Sql语句 + /// 是否原样添加,默认会将[]替换为特定Sql转义符 + public static T AppendJoin( this T source, string sql, bool raw = false ) where T : IJoin { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.JoinClause.AppendJoin( sql, raw ); + return source; + } + + #endregion + + #region AppendLeftJoin(添加到左外连接子句) + + /// + /// 添加到左外连接子句 + /// + /// 源 + /// Sql语句 + /// 是否原样添加,默认会将[]替换为特定Sql转义符 + public static T AppendLeftJoin( this T source, string sql, bool raw = false ) where T : IJoin { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.JoinClause.AppendLeftJoin( sql, raw ); + return source; + } + + #endregion + + #region AppendRightJoin(添加到右外连接子句) + + /// + /// 添加到右外连接子句 + /// + /// 源 + /// Sql语句 + /// 是否原样添加,默认会将[]替换为特定Sql转义符 + public static T AppendRightJoin( this T source, string sql, bool raw = false ) where T : IJoin { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.JoinClause.AppendRightJoin( sql, raw ); + return source; + } + + #endregion + + #region AppendOn(添加到On子句) + + /// + /// 添加到On子句 + /// + /// 源 + /// Sql语句 + /// 是否原样添加,默认会将[]替换为特定Sql转义符 + public static T AppendOn( this T source, string sql, bool raw = false ) where T : IJoin { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.JoinClause.AppendOn( sql, raw ); + return source; + } + + #endregion + + #region ClearJoin(清空Join子句) + + /// + /// 清空Join子句 + /// + /// 源 + public static T ClearJoin( this T source ) where T : IJoin { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.JoinClause.Clear(); + return source; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/OrderByClauseExtensions.cs b/src/Util.Data.Sql/OrderByClauseExtensions.cs new file mode 100644 index 000000000..fc6498715 --- /dev/null +++ b/src/Util.Data.Sql/OrderByClauseExtensions.cs @@ -0,0 +1,58 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Operations; + +namespace Util.Data.Sql; + +/// +/// OrderBy子句操作扩展 +/// +public static class OrderByClauseExtensions { + + #region OrderBy(排序) + + /// + /// 排序 + /// + /// 源 + /// 排序列表,范例:a.Id,b.Name desc + public static T OrderBy( this T source, string order ) where T : IOrderBy { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.OrderByClause.OrderBy( order ); + return source; + } + + #endregion + + #region AppendOrderBy(添加到Order By子句) + + /// + /// 添加到Order By子句 + /// + /// 源 + /// Sql语句 + /// 是否原样添加,默认会将[]替换为特定Sql转义符 + public static T AppendOrderBy( this T source, string sql, bool raw = false ) where T : IOrderBy { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.OrderByClause.AppendSql( sql, raw ); + return source; + } + + #endregion + + #region ClearOrderBy(清空Order By子句) + + /// + /// 清空Order By子句 + /// + /// 源 + public static T ClearOrderBy( this T source ) where T : IOrderBy { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.OrderByClause.Clear(); + return source; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/ParameterExtensions.cs b/src/Util.Data.Sql/ParameterExtensions.cs new file mode 100644 index 000000000..fb90bda6e --- /dev/null +++ b/src/Util.Data.Sql/ParameterExtensions.cs @@ -0,0 +1,130 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Operations; +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Sql; + +/// +/// Sql参数操作扩展 +/// +public static class ParameterExtensions { + + #region AddDynamicParams(添加动态参数) + + /// + /// 添加动态参数 + /// + /// 源 + /// 动态参数,范例: new{ A=1,B=2 } + public static T AddDynamicParams( this T source, object param ) where T : ISqlParameter { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.ParameterManager.AddDynamicParams( param ); + return source; + } + + #endregion + + #region AddParam(添加Sql参数) + + /// + /// 添加Sql参数 + /// + /// 源 + /// 参数名 + /// 参数类型 + /// 参数方向 + public static T AddParam( this T source, string name, DbType dbType, ParameterDirection direction ) where T : ISqlParameter { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.ParameterManager.Add( name, null, dbType, direction ); + return source; + } + + /// + /// 添加Sql参数 + /// + /// 源 + /// 参数名 + /// 参数值 + /// 参数类型 + /// 参数方向 + /// 字段长度 + /// 数值有效位数 + /// 数值小数位数 + public static T AddParam( this T source, string name, object value = null, DbType? dbType = null, ParameterDirection? direction = null, int? size = null, byte? precision = null, byte? scale = null ) where T : ISqlParameter { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.ParameterManager.Add( name, value, dbType, direction, size, precision, scale ); + return source; + } + + #endregion + + #region GetParams(获取参数列表) + + /// + /// 获取参数列表 + /// + /// 源 + public static IReadOnlyList GetParams( this ISqlParameter source ) { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + return accessor.ParameterManager.GetParams(); + return default; + } + + #endregion + + #region GetDynamicParams(获取动态参数列表) + + /// + /// 获取动态参数列表 + /// + /// 源 + public static IReadOnlyList GetDynamicParams( this ISqlParameter source ) { + source.CheckNull( nameof( source ) ); + if ( source is ISqlPartAccessor accessor ) + return accessor.ParameterManager.GetDynamicParams(); + return default; + } + + #endregion + + #region GetParam(获取参数值) + + /// + /// 获取参数值 + /// + /// 源 + /// 参数名 + public static T GetParam( this ISqlParameter source, string name ) { + source.CheckNull( nameof( source ) ); + if( source is IGetParameter target ) + return target.GetParam( name ); + if( source is ISqlPartAccessor accessor ) + return (T)accessor.ParameterManager.GetValue( name ); + return default; + } + + #endregion + + #region ClearParams(清空Sql参数) + + /// + /// 清空Sql参数 + /// + /// 源 + public static T ClearParams( this T source ) where T : ISqlParameter { + source.CheckNull( nameof( source ) ); + if( source is IClearParameters target ) { + target.ClearParams(); + return source; + } + if( source is ISqlPartAccessor accessor ) + accessor.ParameterManager.Clear(); + return source; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/SelectClauseExtensions.cs b/src/Util.Data.Sql/SelectClauseExtensions.cs new file mode 100644 index 000000000..c62745320 --- /dev/null +++ b/src/Util.Data.Sql/SelectClauseExtensions.cs @@ -0,0 +1,119 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Operations; + +namespace Util.Data.Sql; + +/// +/// Select子句操作扩展 +/// +public static class SelectClauseExtensions { + + #region Select(设置列) + + /// + /// 设置星号*列 + /// + /// 源 + public static T Select( this T source ) where T : ISelect { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.SelectClause.Select(); + return source; + } + + /// + /// 设置列名 + /// + /// 源 + /// 列名,范例:a.AppId As Id,a.Name + public static T Select( this T source, string columns ) where T : ISelect { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.SelectClause.Select( columns ); + return source; + } + + /// + /// 设置子查询列 + /// + /// 源 + /// Sql生成器 + /// 列别名 + public static T Select( this T source, ISqlBuilder builder, string alias ) where T : ISelect { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.SelectClause.Select( builder, alias ); + return source; + } + + /// + /// 设置子查询列 + /// + /// 源 + /// 子查询操作 + /// 列别名 + public static T Select( this T source, Action action, string alias ) where T : ISelect { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.SelectClause.Select( action, alias ); + return source; + } + + #endregion + + #region AppendSelect(添加到Select子句) + + /// + /// 添加到Select子句 + /// + /// 源 + /// Sql语句 + /// 是否原样添加,默认会将[]替换为特定Sql转义符 + public static T AppendSelect( this T source, string sql, bool raw = false ) where T : ISelect { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.SelectClause.AppendSql( sql, raw ); + return source; + } + + /// + /// 添加子查询到Select子句 + /// + /// 源 + /// Sql生成器 + public static T AppendSelect( this T source, ISqlBuilder builder ) where T : ISelect { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.SelectClause.AppendSql( builder ); + return source; + } + + /// + /// 添加子查询到Select子句 + /// + /// 源 + /// 子查询操作 + public static T AppendSelect( this T source, Action action ) where T : ISelect { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.SelectClause.AppendSql( action ); + return source; + } + + #endregion + + #region ClearSelect(清空Select子句) + + /// + /// 清空Select子句 + /// + /// 源 + public static T ClearSelect( this T source ) where T : ISelect { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.SelectClause.Clear(); + return source; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/SqlBuilderSetExtensions.cs b/src/Util.Data.Sql/SqlBuilderSetExtensions.cs new file mode 100644 index 000000000..b9551bdca --- /dev/null +++ b/src/Util.Data.Sql/SqlBuilderSetExtensions.cs @@ -0,0 +1,137 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Operations; + +namespace Util.Data.Sql; + +/// +/// Sql生成器集合操作扩展 +/// +public static class SqlBuilderSetExtensions { + + #region Union(合并结果集) + + /// + /// 合并结果集,排除重复结果行,并按默认规则排序 + /// + /// 源 + /// Sql生成器集合 + public static T Union( this T source, params ISqlBuilder[] builders ) where T : ISet { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.SqlBuilderSet.Union( builders ); + return source; + } + + /// + /// 合并结果集,排除重复结果行,并按默认规则排序 + /// + /// 源 + /// Sql生成器集合 + public static T Union( this T source, IEnumerable builders ) where T : ISet { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.SqlBuilderSet.Union( builders ); + return source; + } + + #endregion + + #region UnionAll(合并结果集) + + /// + /// 合并结果集,不会排除重复结果行 + /// + /// 源 + /// Sql生成器集合 + public static T UnionAll( this T source, params ISqlBuilder[] builders ) where T : ISet { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.SqlBuilderSet.UnionAll( builders ); + return source; + } + + /// + /// 合并结果集,不会排除重复结果行 + /// + /// 源 + /// Sql生成器集合 + public static T UnionAll( this T source, IEnumerable builders ) where T : ISet { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.SqlBuilderSet.UnionAll( builders ); + return source; + } + + #endregion + + #region Intersect(交集) + + /// + /// 交集 + /// + /// 源 + /// Sql生成器集合 + public static T Intersect( this T source, params ISqlBuilder[] builders ) where T : ISet { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.SqlBuilderSet.Intersect( builders ); + return source; + } + + /// + /// 交集 + /// + /// 源 + /// Sql生成器集合 + public static T Intersect( this T source, IEnumerable builders ) where T : ISet { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.SqlBuilderSet.Intersect( builders ); + return source; + } + + #endregion + + #region Except(差集) + + /// + /// 差集 + /// + /// 源 + /// Sql生成器集合 + public static T Except( this T source, params ISqlBuilder[] builders ) where T : ISet { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.SqlBuilderSet.Except( builders ); + return source; + } + + /// + /// 差集 + /// + /// 源 + /// Sql生成器集合 + public static T Except( this T source, IEnumerable builders ) where T : ISet { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.SqlBuilderSet.Except( builders ); + return source; + } + + #endregion + + #region ClearSets(清理集合) + + /// + /// 清理Union等集合操作 + /// + /// 源 + public static T ClearSets( this T source ) where T : ISet { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.SqlBuilderSet.Clear(); + return source; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/SqlExecutorExtensions.cs b/src/Util.Data.Sql/SqlExecutorExtensions.cs new file mode 100644 index 000000000..50620225a --- /dev/null +++ b/src/Util.Data.Sql/SqlExecutorExtensions.cs @@ -0,0 +1,142 @@ +using Util.Data.Sql.Database; + +namespace Util.Data.Sql; + +/// +/// Sql执行器操作扩展 +/// +public static class SqlExecutorExtensions { + + #region GetConnection(获取数据库连接) + + /// + /// 获取数据库连接 + /// + /// 源 + public static IDbConnection GetConnection( this ISqlExecutor source ) { + source.CheckNull( nameof( source ) ); + if ( source is IConnectionManager manager ) + return manager.GetConnection(); + return null; + } + + #endregion + + #region SetConnection(设置数据库连接) + + /// + /// 设置数据库连接 + /// + /// 源 + /// 数据库连接 + public static ISqlExecutor SetConnection( this ISqlExecutor source, IDbConnection connection ) { + source.CheckNull( nameof( source ) ); + if( source is IConnectionManager manager ) + manager.SetConnection( connection ); + return source; + } + + #endregion + + #region GetTransaction(获取数据库事务) + + /// + /// 获取数据库事务 + /// + /// 源 + public static IDbTransaction GetTransaction( this ISqlExecutor source ) { + source.CheckNull( nameof( source ) ); + if( source is ITransactionManager manager ) + return manager.GetTransaction(); + return null; + } + + #endregion + + #region SetTransaction(设置数据库事务) + + /// + /// 设置数据库事务 + /// + /// 源 + /// 数据库事务 + public static ISqlExecutor SetTransaction( this ISqlExecutor source, IDbTransaction transaction ) { + source.CheckNull( nameof( source ) ); + if( source is ITransactionManager manager ) + manager.SetTransaction( transaction ); + return source; + } + + #endregion + + #region BeginTransaction(开始事务) + + /// + /// 开始事务 + /// + /// 源 + public static IDbTransaction BeginTransaction( this ISqlExecutor source ) { + source.CheckNull( nameof( source ) ); + if( source is ITransactionManager manager ) + return manager.BeginTransaction(); + return null; + } + + /// + /// 开始事务 + /// + /// 源 + /// 事务隔离级别 + public static IDbTransaction BeginTransaction( this ISqlExecutor source, IsolationLevel isolationLevel ) { + source.CheckNull( nameof( source ) ); + if( source is ITransactionManager manager ) + return manager.BeginTransaction( isolationLevel ); + return null; + } + + #endregion + + #region CommitTransaction(提交事务) + + /// + /// 提交事务 + /// + /// 源 + public static ISqlExecutor CommitTransaction( this ISqlExecutor source ) { + source.CheckNull( nameof( source ) ); + if( source is ITransactionManager manager ) + manager.CommitTransaction(); + return source; + } + + #endregion + + #region RollbackTransaction(回滚事务) + + /// + /// 回滚事务 + /// + /// 源 + public static ISqlExecutor RollbackTransaction( this ISqlExecutor source ) { + source.CheckNull( nameof( source ) ); + if( source is ITransactionManager manager ) + manager.RollbackTransaction(); + return source; + } + + #endregion + + #region Clear(清理) + + /// + /// 清理 + /// + /// 源 + public static ISqlExecutor Clear( this ISqlExecutor source ) { + source.CheckNull( nameof( source ) ); + source.SqlBuilder.Clear(); + return source; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/SqlOptions.cs b/src/Util.Data.Sql/SqlOptions.cs new file mode 100644 index 000000000..63bf7527a --- /dev/null +++ b/src/Util.Data.Sql/SqlOptions.cs @@ -0,0 +1,25 @@ +namespace Util.Data.Sql; + +/// +/// Sql配置 +/// +public class SqlOptions : SqlOptions where T: class { +} + +/// +/// Sql配置 +/// +public class SqlOptions { + /// + /// 数据库连接字符串 + /// + public string ConnectionString { get; set; } + /// + /// 数据库连接 + /// + public IDbConnection Connection { get; set; } + /// + /// 日志类别 + /// + public string LogCategory { get; set; } = "Util.Data.Sql"; +} \ No newline at end of file diff --git a/src/Util.Data.Sql/SqlOptionsExtensions.cs b/src/Util.Data.Sql/SqlOptionsExtensions.cs new file mode 100644 index 000000000..d3183439e --- /dev/null +++ b/src/Util.Data.Sql/SqlOptionsExtensions.cs @@ -0,0 +1,52 @@ +namespace Util.Data.Sql; + +/// +/// Sql配置扩展 +/// +public static class SqlOptionsExtensions { + + #region ConnectionString(设置数据库连接字符串) + + /// + /// 设置数据库连接字符串 + /// + /// 源 + /// 数据库连接字符串 + public static SqlOptions ConnectionString( this SqlOptions options, string connectionString ) { + options.CheckNull( nameof(options) ); + options.ConnectionString = connectionString; + return options; + } + + #endregion + + #region Connection(设置数据库连接) + + /// + /// 设置数据库连接 + /// + /// 源 + /// 数据库连接 + public static SqlOptions Connection( this SqlOptions options, IDbConnection connection ) { + options.CheckNull( nameof( options ) ); + options.Connection = connection; + return options; + } + + #endregion + + #region LogCategory(设置日志类别) + + /// + /// 设置日志类别 + /// + /// 源 + /// 日志类别 + public static SqlOptions LogCategory( this SqlOptions options, string logCategory ) { + options.CheckNull( nameof( options ) ); + options.LogCategory = logCategory; + return options; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/SqlQueryExtensions.Query.cs b/src/Util.Data.Sql/SqlQueryExtensions.Query.cs new file mode 100644 index 000000000..7132cee3f --- /dev/null +++ b/src/Util.Data.Sql/SqlQueryExtensions.Query.cs @@ -0,0 +1,299 @@ +namespace Util.Data.Sql; + +/// +/// Sql查询对象操作扩展 - 查询扩展 +/// +public static partial class SqlQueryExtensions { + + #region ToEntity(获取单个实体) + + /// + /// 获取单个实体 + /// + /// 实体类型 + /// 源 + /// 执行超时时间,单位:秒 + public static TEntity ToEntity( this ISqlQuery source, int? timeout = null ) { + source.CheckNull( nameof( source ) ); + return source.ExecuteSingle( timeout ); + } + + #endregion + + #region ToEntityAsync(获取单个实体) + + /// + /// 获取单个实体 + /// + /// 实体类型 + /// 源 + /// 执行超时时间,单位:秒 + public static async Task ToEntityAsync( this ISqlQuery source, int? timeout = null ) { + source.CheckNull( nameof( source ) ); + return await source.ExecuteSingleAsync( timeout ); + } + + #endregion + + #region ToList(获取实体集合) + + /// + /// 获取实体集合 + /// + /// 源 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + public static List ToList( this ISqlQuery source, int? timeout = null, bool buffered = true ) { + source.CheckNull( nameof( source ) ); + return source.ExecuteQuery( timeout, buffered ); + } + + /// + /// 获取实体集合 + /// + /// 实体类型 + /// 源 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + public static List ToList( this ISqlQuery source, int? timeout = null, bool buffered = true ) { + source.CheckNull( nameof( source ) ); + return source.ExecuteQuery( timeout, buffered ); + } + + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 返回的实体类型 + /// 源 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + public static List ToList( this ISqlQuery source, Func map, int? timeout = null, bool buffered = true ) { + source.CheckNull( nameof( source ) ); + return source.ExecuteQuery( map, timeout, buffered ); + } + + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 返回的实体类型 + /// 源 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + public static List ToList( this ISqlQuery source, Func map, int? timeout = null, bool buffered = true ) { + source.CheckNull( nameof( source ) ); + return source.ExecuteQuery( map, timeout, buffered ); + } + + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 返回的实体类型 + /// 源 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + public static List ToList( this ISqlQuery source, Func map, int? timeout = null, bool buffered = true ) { + source.CheckNull( nameof( source ) ); + return source.ExecuteQuery( map, timeout, buffered ); + } + + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 实体类型5 + /// 返回的实体类型 + /// 源 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + public static List ToList( this ISqlQuery source, Func map, int? timeout = null, bool buffered = true ) { + source.CheckNull( nameof( source ) ); + return source.ExecuteQuery( map, timeout, buffered ); + } + + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 实体类型5 + /// 实体类型6 + /// 返回的实体类型 + /// 源 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + public static List ToList( this ISqlQuery source, Func map, int? timeout = null, bool buffered = true ) { + source.CheckNull( nameof( source ) ); + return source.ExecuteQuery( map, timeout, buffered ); + } + + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 实体类型5 + /// 实体类型6 + /// 实体类型7 + /// 返回的实体类型 + /// 源 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + public static List ToList( this ISqlQuery source, Func map, int? timeout = null, bool buffered = true ) { + source.CheckNull( nameof( source ) ); + return source.ExecuteQuery( map, timeout, buffered ); + } + + #endregion + + #region ToListAsync(获取实体集合) + + /// + /// 获取实体集合 + /// + /// 源 + /// 执行超时时间,单位:秒 + public static async Task> ToListAsync( this ISqlQuery source, int? timeout = null ) { + source.CheckNull( nameof( source ) ); + return await source.ExecuteQueryAsync( timeout ); + } + + /// + /// 获取实体集合 + /// + /// 实体类型 + /// 源 + /// 执行超时时间,单位:秒 + public static async Task> ToListAsync( this ISqlQuery source, int? timeout = null ) { + source.CheckNull( nameof( source ) ); + return await source.ExecuteQueryAsync( timeout ); + } + + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 返回的实体类型 + /// 源 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + public static async Task> ToListAsync( this ISqlQuery source, Func map, int? timeout = null, bool buffered = true ) { + source.CheckNull( nameof( source ) ); + return await source.ExecuteQueryAsync( map, timeout, buffered ); + } + + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 返回的实体类型 + /// 源 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + public static async Task> ToListAsync( this ISqlQuery source, Func map, int? timeout = null, bool buffered = true ) { + source.CheckNull( nameof( source ) ); + return await source.ExecuteQueryAsync( map, timeout, buffered ); + } + + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 返回的实体类型 + /// 源 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + public static async Task> ToListAsync( this ISqlQuery source, Func map, int? timeout = null, bool buffered = true ) { + source.CheckNull( nameof( source ) ); + return await source.ExecuteQueryAsync( map, timeout, buffered ); + } + + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 实体类型5 + /// 返回的实体类型 + /// 源 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + public static async Task> ToListAsync( this ISqlQuery source, Func map, int? timeout = null, bool buffered = true ) { + source.CheckNull( nameof( source ) ); + return await source.ExecuteQueryAsync( map, timeout, buffered ); + } + + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 实体类型5 + /// 实体类型6 + /// 返回的实体类型 + /// 源 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + public static async Task> ToListAsync( this ISqlQuery source, Func map, int? timeout = null, bool buffered = true ) { + source.CheckNull( nameof( source ) ); + return await source.ExecuteQueryAsync( map, timeout, buffered ); + } + + /// + /// 获取实体集合 + /// + /// 实体类型1 + /// 实体类型2 + /// 实体类型3 + /// 实体类型4 + /// 实体类型5 + /// 实体类型6 + /// 实体类型7 + /// 返回的实体类型 + /// 源 + /// 映射函数 + /// 执行超时时间,单位:秒 + /// 是否缓存,默认值: true + public static async Task> ToListAsync( this ISqlQuery source, Func map, int? timeout = null, bool buffered = true ) { + source.CheckNull( nameof( source ) ); + return await source.ExecuteQueryAsync( map, timeout, buffered ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/SqlQueryExtensions.Scalar.cs b/src/Util.Data.Sql/SqlQueryExtensions.Scalar.cs new file mode 100644 index 000000000..1c67949ca --- /dev/null +++ b/src/Util.Data.Sql/SqlQueryExtensions.Scalar.cs @@ -0,0 +1,477 @@ +using Util.Helpers; +using Convert = Util.Helpers.Convert; + +namespace Util.Data.Sql; + +/// +/// Sql查询对象操作扩展 - 获取单值扩展 +/// +public static partial class SqlQueryExtensions { + + #region ToStringAsync(获取字符串值) + + /// + /// 获取字符串值 + /// + /// 源 + public static async Task ToStringAsync( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + var result = await source.ExecuteScalarAsync(); + return result.SafeString(); + } + + #endregion + + #region ToInt(获取整型值) + + /// + /// 获取32位整型值 + /// + /// 源 + public static int ToInt( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + return Convert.ToInt( source.ExecuteScalar() ); + } + + #endregion + + #region ToIntAsync(获取整型值) + + /// + /// 获取32位整型值 + /// + /// 源 + public static async Task ToIntAsync( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + var result = await source.ExecuteScalarAsync(); + return Convert.ToInt( result ); + } + + #endregion + + #region ToIntOrNull(获取可空整型值) + + /// + /// 获取32位可空整型值 + /// + /// 源 + public static int? ToIntOrNull( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + return Convert.ToIntOrNull( source.ExecuteScalar() ); + } + + #endregion + + #region ToIntOrNullAsync(获取可空整型值) + + /// + /// 获取32位可空整型值 + /// + /// 源 + public static async Task ToIntOrNullAsync( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + var result = await source.ExecuteScalarAsync(); + return Convert.ToIntOrNull( result ); + } + + #endregion + + #region ToLong(获取整型值) + + /// + /// 获取64位整型值 + /// + /// 源 + public static long ToLong( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + return Convert.ToLong( source.ExecuteScalar() ); + } + + #endregion + + #region ToLongAsync(获取整型值) + + /// + /// 获取64位整型值 + /// + /// 源 + public static async Task ToLongAsync( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + var result = await source.ExecuteScalarAsync(); + return Convert.ToLong( result ); + } + + #endregion + + #region ToLongOrNull(获取可空整型值) + + /// + /// 获取64位可空整型值 + /// + /// 源 + public static long? ToLongOrNull( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + return Convert.ToLongOrNull( source.ExecuteScalar() ); + } + + #endregion + + #region ToLongOrNullAsync(获取可空整型值) + + /// + /// 获取64位可空整型值 + /// + /// 源 + public static async Task ToLongOrNullAsync( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + var result = await source.ExecuteScalarAsync(); + return Convert.ToLongOrNull( result ); + } + + #endregion + + #region ToGuid(获取Guid值) + + /// + /// 获取Guid值 + /// + /// 源 + public static Guid ToGuid( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + return Convert.ToGuid( source.ExecuteScalar() ); + } + + #endregion + + #region ToGuidAsync(获取Guid值) + + /// + /// 获取Guid值 + /// + /// 源 + public static async Task ToGuidAsync( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + var result = await source.ExecuteScalarAsync(); + return Convert.ToGuid( result ); + } + + #endregion + + #region ToGuidOrNull(获取可空Guid值) + + /// + /// 获取可空Guid值 + /// + /// 源 + public static Guid? ToGuidOrNull( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + return Convert.ToGuidOrNull( source.ExecuteScalar() ); + } + + #endregion + + #region ToGuidOrNullAsync(获取可空Guid值) + + /// + /// 获取可空Guid值 + /// + /// 源 + public static async Task ToGuidOrNullAsync( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + var result = await source.ExecuteScalarAsync(); + return Convert.ToGuidOrNull( result ); + } + + #endregion + + #region ToBool(获取布尔值) + + /// + /// 获取布尔值 + /// + /// 源 + public static bool ToBool( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + return Convert.ToBool( source.ExecuteScalar() ); + } + + #endregion + + #region ToBoolAsync(获取布尔值) + + /// + /// 获取布尔值 + /// + /// 源 + public static async Task ToBoolAsync( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + var result = await source.ExecuteScalarAsync(); + return Convert.ToBool( result ); + } + + #endregion + + #region ToBoolOrNull(获取可空布尔值) + + /// + /// 获取可空布尔值 + /// + /// 源 + public static bool? ToBoolOrNull( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + return Convert.ToBoolOrNull( source.ExecuteScalar() ); + } + + #endregion + + #region ToBoolOrNullAsync(获取可空布尔值) + + /// + /// 获取可空布尔值 + /// + /// 源 + public static async Task ToBoolOrNullAsync( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + var result = await source.ExecuteScalarAsync(); + return Convert.ToBoolOrNull( result ); + } + + #endregion + + #region ToFloat(获取浮点值) + + /// + /// 获取32位浮点值 + /// + /// 源 + public static float ToFloat( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + return Convert.ToFloat( source.ExecuteScalar() ); + } + + #endregion + + #region ToFloatAsync(获取浮点值) + + /// + /// 获取32位浮点值 + /// + /// 源 + public static async Task ToFloatAsync( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + var result = await source.ExecuteScalarAsync(); + return Convert.ToFloat( result ); + } + + #endregion + + #region ToFloatOrNull(获取可空浮点值) + + /// + /// 获取32位可空浮点值 + /// + /// 源 + public static float? ToFloatOrNull( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + return Convert.ToFloatOrNull( source.ExecuteScalar() ); + } + + #endregion + + #region ToFloatOrNullAsync(获取可空浮点值) + + /// + /// 获取32位可空浮点值 + /// + /// 源 + public static async Task ToFloatOrNullAsync( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + var result = await source.ExecuteScalarAsync(); + return Convert.ToFloatOrNull( result ); + } + + #endregion + + #region ToDouble(获取浮点值) + + /// + /// 获取64位浮点值 + /// + /// 源 + public static double ToDouble( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + return Convert.ToDouble( source.ExecuteScalar() ); + } + + #endregion + + #region ToDoubleAsync(获取浮点值) + + /// + /// 获取64位浮点值 + /// + /// 源 + public static async Task ToDoubleAsync( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + var result = await source.ExecuteScalarAsync(); + return Convert.ToDouble( result ); + } + + #endregion + + #region ToDoubleOrNull(获取可空浮点值) + + /// + /// 获取64位可空浮点值 + /// + /// 源 + public static double? ToDoubleOrNull( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + return Convert.ToDoubleOrNull( source.ExecuteScalar() ); + } + + #endregion + + #region ToDoubleOrNullAsync(获取可空浮点值) + + /// + /// 获取64位可空浮点值 + /// + /// 源 + public static async Task ToDoubleOrNullAsync( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + var result = await source.ExecuteScalarAsync(); + return Convert.ToDoubleOrNull( result ); + } + + #endregion + + #region ToDecimal(获取浮点值) + + /// + /// 获取128位浮点值 + /// + /// 源 + public static decimal ToDecimal( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + return Convert.ToDecimal( source.ExecuteScalar() ); + } + + #endregion + + #region ToDecimalAsync(获取浮点值) + + /// + /// 获取128位浮点值 + /// + /// 源 + public static async Task ToDecimalAsync( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + var result = await source.ExecuteScalarAsync(); + return Convert.ToDecimal( result ); + } + + #endregion + + #region ToDecimalOrNull(获取可空浮点值) + + /// + /// 获取128位可空浮点值 + /// + /// 源 + public static decimal? ToDecimalOrNull( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + return Convert.ToDecimalOrNull( source.ExecuteScalar() ); + } + + #endregion + + #region ToDecimalOrNullAsync(获取可空浮点值) + + /// + /// 获取128位可空浮点值 + /// + /// 源 + public static async Task ToDecimalOrNullAsync( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + var result = await source.ExecuteScalarAsync(); + return Convert.ToDecimalOrNull( result ); + } + + #endregion + + #region ToDateTime(获取日期值) + + /// + /// 获取日期值 + /// + /// 源 + public static DateTime ToDateTime( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + var result = source.ExecuteScalar(); + return ToLocalTime( result ); + } + + /// + /// 转换为本地时间 + /// + private static DateTime ToLocalTime( object date ) { + var result = Convert.ToDateTimeOrNull( date ); + if( result == null ) + return DateTime.MinValue; + return Time.UtcToLocalTime( result.Value ); + } + + #endregion + + #region ToDateTimeAsync(获取日期值) + + /// + /// 获取日期值 + /// + /// 源 + public static async Task ToDateTimeAsync( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + var result = await source.ExecuteScalarAsync(); + return ToLocalTime( result ); + } + + #endregion + + #region ToDateTimeOrNull(获取可空日期值) + + /// + /// 获取可空日期值 + /// + /// 源 + public static DateTime? ToDateTimeOrNull( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + return ToLocalTimeOrNull( source.ExecuteScalar() ); + } + + /// + /// 转换为本地时间 + /// + private static DateTime? ToLocalTimeOrNull( object date ) { + var result = Convert.ToDateTimeOrNull( date ); + if( result == null ) + return null; + return Time.UtcToLocalTime( result.Value ); + } + + #endregion + + #region ToDateTimeOrNullAsync(获取可空日期值) + + /// + /// 获取可空日期值 + /// + /// 源 + public static async Task ToDateTimeOrNullAsync( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + var result = await source.ExecuteScalarAsync(); + return ToLocalTimeOrNull( result ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/SqlQueryExtensions.cs b/src/Util.Data.Sql/SqlQueryExtensions.cs new file mode 100644 index 000000000..f5bc63ed7 --- /dev/null +++ b/src/Util.Data.Sql/SqlQueryExtensions.cs @@ -0,0 +1,67 @@ +using Util.Data.Sql.Database; + +namespace Util.Data.Sql; + +/// +/// Sql查询对象操作扩展 +/// +public static partial class SqlQueryExtensions { + + #region GetConnection(获取数据库连接) + + /// + /// 获取数据库连接 + /// + /// 源 + public static IDbConnection GetConnection( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + if ( source is IConnectionManager manager ) + return manager.GetConnection(); + return null; + } + + #endregion + + #region SetConnection(设置数据库连接) + + /// + /// 设置数据库连接 + /// + /// 源 + /// 数据库连接 + public static ISqlQuery SetConnection( this ISqlQuery source, IDbConnection connection ) { + source.CheckNull( nameof( source ) ); + if( source is IConnectionManager manager ) + manager.SetConnection( connection ); + return source; + } + + #endregion + + #region Clear(清理) + + /// + /// 清理 + /// + /// 源 + public static ISqlQuery Clear( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + source.SqlBuilder.Clear(); + return source; + } + + #endregion + + #region NewSqlBuilder(创建一个新的Sql生成器) + + /// + /// 创建一个新的Sql生成器 + /// + /// 源 + public static ISqlBuilder NewSqlBuilder( this ISqlQuery source ) { + source.CheckNull( nameof( source ) ); + return source.SqlBuilder.New(); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/StartClauseExtensions.cs b/src/Util.Data.Sql/StartClauseExtensions.cs new file mode 100644 index 000000000..f946b5863 --- /dev/null +++ b/src/Util.Data.Sql/StartClauseExtensions.cs @@ -0,0 +1,105 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Operations; + +namespace Util.Data.Sql; + +/// +/// 起始子句操作扩展 +/// +public static class StartClauseExtensions { + + #region Cte(设置公用表表达式CTE) + + /// + /// 设置公用表表达式CTE + /// + /// 源 + /// 公用表表达式名称 + /// Sql生成器 + public static T Cte( this T source, string name, ISqlBuilder builder ) where T : IStart { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.StartClause.Cte( name, builder ); + return source; + } + + #endregion + + #region Append(添加到Sql起始位置) + + /// + /// 添加到Sql起始位置,与AppendStart功能相同 + /// + /// 源 + /// Sql语句 + /// 是否原样添加,默认会将[]替换为特定Sql转义符 + public static T Append( this T source, string sql, bool raw = false ) where T : IStart { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.StartClause.Append( sql, raw ); + return source; + } + + #endregion + + #region AppendLine(添加到Sql起始位置并换行) + + /// + /// 添加到Sql起始位置并换行 + /// + /// 源 + /// Sql语句 + /// 是否原样添加,默认会将[]替换为特定Sql转义符 + public static T AppendLine( this T source, string sql, bool raw = false ) where T : IStart { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.StartClause.AppendLine( sql, raw ); + return source; + } + + #endregion + + #region AppendStart(添加到Sql起始位置) + + /// + /// 添加到Sql起始位置 + /// + /// 源 + /// Sql语句 + /// 是否原样添加,默认会将[]替换为特定Sql转义符 + public static T AppendStart( this T source, string sql, bool raw = false ) where T : IStart { + return source.Append( sql, raw ); + } + + #endregion + + #region ClearCte(清理公用表表达式CTE) + + /// + /// 清理公用表表达式CTE + /// + /// 源 + public static T ClearCte( this T source ) where T : IStart { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.StartClause.ClearCte(); + return source; + } + + #endregion + + #region ClearStart(清理起始子句) + + /// + /// 清理Sql起始子句 + /// + /// 源 + public static T ClearStart( this T source ) where T : IStart { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.StartClause.Clear(); + return source; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Data.Sql/Usings.cs b/src/Util.Data.Sql/Usings.cs new file mode 100644 index 000000000..c1dab6d39 --- /dev/null +++ b/src/Util.Data.Sql/Usings.cs @@ -0,0 +1,8 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Text; +global using System.Data; +global using System.Text.RegularExpressions; +global using System.Collections; diff --git a/src/Util.Data.Sql/WhereClauseExtensions.cs b/src/Util.Data.Sql/WhereClauseExtensions.cs new file mode 100644 index 000000000..6584f2c58 --- /dev/null +++ b/src/Util.Data.Sql/WhereClauseExtensions.cs @@ -0,0 +1,372 @@ +using Util.Data.Queries; +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Operations; + +namespace Util.Data.Sql; + +/// +/// Where子句操作扩展 +/// +public static class WhereClauseExtensions { + + #region Where + + /// + /// 设置查询条件 + /// + /// 源 + /// 查询条件 + public static T Where( this T source, ISqlCondition condition ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if ( source is ISqlPartAccessor accessor ) + accessor.WhereClause.And( condition ); + return source; + } + + /// + /// 设置查询条件 + /// + /// 源 + /// 列名 + /// 值 + /// 运算符 + public static T Where( this T source, string column, object value, Operator @operator = Operator.Equal ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.WhereClause.Where( column, value, @operator ); + return source; + } + + /// + /// 设置查询条件 + /// + /// 源 + /// 列名 + /// 子查询Sql生成器 + /// 运算符 + public static T Where( this T source, string column, ISqlBuilder builder, Operator @operator = Operator.Equal ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.WhereClause.Where( column, builder, @operator ); + return source; + } + + /// + /// 设置查询条件 + /// + /// 源 + /// 列名 + /// 子查询操作 + /// 运算符 + public static T Where( this T source, string column, Action action, Operator @operator = Operator.Equal ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.WhereClause.Where( column, action, @operator ); + return source; + } + + #endregion + + #region In + + /// + /// 设置In查询条件 + /// + /// 源 + /// 列名 + /// 值集合 + public static T In( this T source, string column, IEnumerable values ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.WhereClause.Where( column, values,Operator.In ); + return source; + } + + /// + /// 设置In子查询条件 + /// + /// 源 + /// 列名 + /// 子查询Sql生成器 + public static T In( this T source, string column, ISqlBuilder builder ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.WhereClause.Where( column, builder, Operator.In ); + return source; + } + + /// + /// 设置In子查询条件 + /// + /// 源 + /// 列名 + /// 子查询操作 + public static T In( this T source, string column, Action action ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.WhereClause.Where( column, action, Operator.In ); + return source; + } + + #endregion + + #region NotIn + + /// + /// 设置Not In查询条件 + /// + /// 源 + /// 列名 + /// 值集合 + public static T NotIn( this T source, string column, IEnumerable values ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.WhereClause.Where( column, values, Operator.NotIn ); + return source; + } + + /// + /// 设置Not In子查询条件 + /// + /// 源 + /// 列名 + /// 子查询Sql生成器 + public static T NotIn( this T source, string column, ISqlBuilder builder ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.WhereClause.Where( column, builder, Operator.NotIn ); + return source; + } + + /// + /// 设置Not In子查询条件 + /// + /// 源 + /// 列名 + /// 子查询操作 + public static T NotIn( this T source, string column, Action action ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.WhereClause.Where( column, action, Operator.NotIn ); + return source; + } + + #endregion + + #region IsNull + + /// + /// 设置Is Null查询条件 + /// + /// 源 + /// 列名 + public static T IsNull( this T source, string column ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if ( source is ISqlPartAccessor accessor ) + accessor.WhereClause.IsNull( column ); + return source; + } + + #endregion + + #region IsNotNull + + /// + /// 设置Is Not Null查询条件 + /// + /// 源 + /// 列名 + public static T IsNotNull( this T source, string column ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if ( source is ISqlPartAccessor accessor ) + accessor.WhereClause.IsNotNull( column ); + return source; + } + + #endregion + + #region IsEmpty + + /// + /// 设置空条件,范例:[Name] Is Null Or [Name]='' + /// + /// 源 + /// 列名 + public static T IsEmpty( this T source, string column ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if ( source is ISqlPartAccessor accessor ) + accessor.WhereClause.IsEmpty( column ); + return source; + } + + #endregion + + #region IsNotEmpty + + /// + /// 设置非空条件,范例:[Name] Is Not Null And [Name]<>'' + /// + /// 源 + /// 列名 + public static T IsNotEmpty( this T source, string column ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if ( source is ISqlPartAccessor accessor ) + accessor.WhereClause.IsNotEmpty( column ); + return source; + } + + #endregion + + #region Between + + /// + /// 添加范围查询条件 + /// + /// 源 + /// 列名 + /// 最小值 + /// 最大值 + /// 包含边界 + public static T Between( this T source, string column, int? min, int? max, Boundary boundary = Boundary.Both ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if ( source is ISqlPartAccessor accessor ) + accessor.WhereClause.Between( column, min, max, boundary ); + return source; + } + + /// + /// 添加范围查询条件 + /// + /// 源 + /// 列名 + /// 最小值 + /// 最大值 + /// 包含边界 + public static T Between( this T source, string column, double? min, double? max, Boundary boundary = Boundary.Both ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if ( source is ISqlPartAccessor accessor ) + accessor.WhereClause.Between( column, min, max, boundary ); + return source; + } + + /// + /// 添加范围查询条件 + /// + /// 源 + /// 列名 + /// 最小值 + /// 最大值 + /// 包含边界 + public static T Between( this T source, string column, decimal? min, decimal? max, Boundary boundary = Boundary.Both ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if ( source is ISqlPartAccessor accessor ) + accessor.WhereClause.Between( column, min, max, boundary ); + return source; + } + + /// + /// 添加范围查询条件 + /// + /// 源 + /// 列名 + /// 最小值 + /// 最大值 + /// 是否包含时间 + /// 包含边界 + public static T Between( this T source, string column, DateTime? min, DateTime? max, bool includeTime = true, Boundary? boundary = null ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if ( source is ISqlPartAccessor accessor ) + accessor.WhereClause.Between( column, min, max, includeTime, boundary ); + return source; + } + + #endregion + + #region Exists + + /// + /// 设置Exists查询条件 + /// + /// 源 + /// 子查询Sql生成器 + public static T Exists( this T source, ISqlBuilder builder ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if ( source is ISqlPartAccessor accessor ) + accessor.WhereClause.Exists( builder ); + return source; + } + + /// + /// 设置Exists查询条件 + /// + /// 源 + /// 子查询操作 + public static T Exists( this T source, Action action ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if ( source is ISqlPartAccessor accessor ) + accessor.WhereClause.Exists( action ); + return source; + } + + #endregion + + #region NotExists + + /// + /// 设置Not Exists查询条件 + /// + /// 源 + /// 子查询Sql生成器 + public static T NotExists( this T source, ISqlBuilder builder ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if ( source is ISqlPartAccessor accessor ) + accessor.WhereClause.NotExists( builder ); + return source; + } + + /// + /// 设置Not Exists查询条件 + /// + /// 源 + /// 子查询操作 + public static T NotExists( this T source, Action action ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if ( source is ISqlPartAccessor accessor ) + accessor.WhereClause.NotExists( action ); + return source; + } + + #endregion + + #region AppendWhere + + /// + /// 添加到Where子句 + /// + /// 源 + /// Sql语句 + /// 是否原样添加,默认会将[]替换为特定Sql转义符 + public static T AppendWhere( this T source, string sql, bool raw = false ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.WhereClause.AppendSql( sql, raw ); + return source; + } + + #endregion + + #region ClearWhere + + /// + /// 清空Where子句 + /// + /// 源 + public static T ClearWhere( this T source ) where T : IWhere { + source.CheckNull( nameof( source ) ); + if( source is ISqlPartAccessor accessor ) + accessor.WhereClause.Clear(); + return source; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Datas/05-Util.Datas.csproj b/src/Util.Datas/05-Util.Datas.csproj deleted file mode 100644 index 1425f2791..000000000 --- a/src/Util.Datas/05-Util.Datas.csproj +++ /dev/null @@ -1,49 +0,0 @@ - - - netstandard2.0 - Util.Datas - util;applicationframework;dotnetcore;aspnetcore; - Util.Datas - Util.Datas - Util.Datas是Util应用框架的数据访问类库。 -Util是一个.net core平台下的应用框架,旨在提升小型团队的开发输出能力,由常用公共操作类(工具类)、分层架构基类、Ui组件,第三方组件封装,第三方业务接口封装,配套代码生成模板,权限等组成。 - 0.2.2-preview - 何镇汐 - 何镇汐 - https://avatars2.githubusercontent.com/u/19421630 - 0.2.2.0 - 0.2.2.0 - 何镇汐 - false - https://github.com/dotnetcore/Util/blob/master/LICENSE.txt - https://github.com/dotnetcore/Util - https://github.com/dotnetcore/Util - git - Util应用框架以MIT开源发布,可随意使用 - - - - ..\..\output\release\ - ..\..\output\release\netstandard2.0\Util.Datas.xml - - - - ..\..\output\release\ - ..\..\output\release\netstandard2.0\Util.Datas.xml - - - - - - - - - - - - - - - - - diff --git a/src/Util.Datas/Ef/Configs/EfConfig.cs b/src/Util.Datas/Ef/Configs/EfConfig.cs deleted file mode 100644 index d2daef725..000000000 --- a/src/Util.Datas/Ef/Configs/EfConfig.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Util.Datas.Ef.Configs { - /// - /// Ef配置 - /// - public class EfConfig { - /// - /// Ef日志级别 - /// - public static EfLogLevel LogLevel = EfLogLevel.Sql; - } -} diff --git a/src/Util.Datas/Ef/Configs/EfLogLevel.cs b/src/Util.Datas/Ef/Configs/EfLogLevel.cs deleted file mode 100644 index 70d0b2d3d..000000000 --- a/src/Util.Datas/Ef/Configs/EfLogLevel.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Util.Datas.Ef.Configs { - /// - /// Ef日志级别 - /// - public enum EfLogLevel { - /// - /// 输出全部日志,包括连接数据库,提交事务等大量信息 - /// - All, - /// - /// 仅输出Sql - /// - Sql, - /// - /// 关闭日志 - /// - Off - } -} diff --git a/src/Util.Datas/Ef/Core/IMap.cs b/src/Util.Datas/Ef/Core/IMap.cs deleted file mode 100644 index 59a3e16e0..000000000 --- a/src/Util.Datas/Ef/Core/IMap.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -namespace Util.Datas.Ef.Core { - /// - /// 映射 - /// - public interface IMap { - /// - /// 映射配置 - /// - /// 模型生成器 - void Map( ModelBuilder builder ); - } -} diff --git a/src/Util.Datas/Ef/Core/MapBase.cs b/src/Util.Datas/Ef/Core/MapBase.cs deleted file mode 100644 index 22c7e32fc..000000000 --- a/src/Util.Datas/Ef/Core/MapBase.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Util.Datas.Ef.Core { - /// - /// 映射配置 - /// - /// 实体类型 - public abstract class MapBase : IMap where TEntity : class { - /// - /// 模型生成器 - /// - protected ModelBuilder ModelBuilder { get; private set; } - - /// - /// 映射配置 - /// - /// 模型生成器 - public void Map( ModelBuilder modelBuilder ) { - ModelBuilder = modelBuilder; - var builder = modelBuilder.Entity(); - MapTable( builder ); - MapVersion( builder ); - MapProperties( builder ); - MapAssociations( builder ); - } - - /// - /// 映射表 - /// - protected abstract void MapTable( EntityTypeBuilder builder ); - - /// - /// 映射乐观离线锁 - /// - protected virtual void MapVersion( EntityTypeBuilder builder ) { - } - - /// - /// 映射属性 - /// - protected virtual void MapProperties( EntityTypeBuilder builder ) { - } - - /// - /// 映射导航属性 - /// - protected virtual void MapAssociations( EntityTypeBuilder builder ) { - } - } -} diff --git a/src/Util.Datas/Ef/Core/PersistentRepositoryBase.cs b/src/Util.Datas/Ef/Core/PersistentRepositoryBase.cs deleted file mode 100644 index ced4dc813..000000000 --- a/src/Util.Datas/Ef/Core/PersistentRepositoryBase.cs +++ /dev/null @@ -1,238 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Util.Datas.Persistence; -using Util.Domains; -using Util.Domains.Repositories; - -namespace Util.Datas.Ef.Core { - /// - /// 仓储 - 配合持久化对象 - /// - /// 实体类型 - /// 持久化对象类型 - public abstract class PersistentRepositoryBase : PersistentRepositoryBase - where TEntity : class, IAggregateRoot - where TPo : class, IPersistentObject { - /// - /// 初始化仓储 - /// - /// 持久化存储 - protected PersistentRepositoryBase( IPersistentStore store ) : base( store ) { - } - } - - /// - /// 仓储 - 配合持久化对象 - /// - /// 实体类型 - /// 持久化对象类型 - /// 实体标识类型 - public abstract class PersistentRepositoryBase : ICompactRepository - where TEntity : class, IAggregateRoot - where TPo : class, IPersistentObject { - /// - /// 持久化存储 - /// - private readonly IPersistentStore _store; - - /// - /// 初始化仓储 - /// - /// 持久化存储 - protected PersistentRepositoryBase( IPersistentStore store ) { - _store = store; - } - - /// - /// 将持久化对象转成实体 - /// - /// 持久化对象 - protected abstract TEntity ToEntity( TPo po ); - - /// - /// 将实体转成持久化对象 - /// - /// 实体 - protected abstract TPo ToPo( TEntity entity ); - - /// - /// 查找实体 - /// - /// 实体标识 - public TEntity Find( object id ) { - return ToEntity( _store.Find( id ) ); - } - - /// - /// 查找实体 - /// - /// 实体标识 - public async Task FindAsync( object id ) { - return ToEntity( await _store.FindAsync( id ) ); - } - - /// - /// 查找实体列表 - /// - /// 实体标识列表 - public List FindByIds( params TKey[] ids ) { - return _store.FindByIds( ids ).Select( ToEntity ).ToList(); - } - - /// - /// 查找实体列表 - /// - /// 实体标识列表 - public List FindByIds( IEnumerable ids ) { - return _store.FindByIds( ids ).Select( ToEntity ).ToList(); - } - - /// - /// 查找实体集合 - /// - /// 实体标识集合 - public async Task> FindByIdsAsync( params TKey[] ids ) { - var pos = await _store.FindByIdsAsync( ids ); - return pos.Select( ToEntity ).ToList(); - } - - /// - /// 查找实体集合 - /// - /// 实体标识集合 - public async Task> FindByIdsAsync( IEnumerable ids ) { - var pos = await _store.FindByIdsAsync( ids ); - return pos.Select( ToEntity ).ToList(); - } - - /// - /// 判断实体是否存在 - /// - /// 实体标识集合 - public bool Exists( params TKey[] ids ) { - return _store.Exists( ids ); - } - - /// - /// 判断实体是否存在 - /// - /// 实体标识集合 - public async Task ExistsAsync( params TKey[] ids ) { - return await _store.ExistsAsync( ids ); - } - - /// - /// 添加实体 - /// - /// 实体 - public void Add( TEntity entity ) { - _store.Add( ToPo( entity ) ); - } - - /// - /// 添加实体集合 - /// - /// 实体集合 - public void Add( IEnumerable entities ) { - _store.Add( entities.Select( ToPo ) ); - } - - /// - /// 添加实体 - /// - /// 实体 - public async Task AddAsync( TEntity entity ) { - await _store.AddAsync( ToPo( entity ) ); - } - - /// - /// 添加实体集合 - /// - /// 实体集合 - public async Task AddAsync( IEnumerable entities ) { - await _store.AddAsync( entities.Select( ToPo ) ); - } - - /// - /// 修改实体 - /// - /// 实体 - public void Update( TEntity entity ) { - _store.Update( ToPo( entity ) ); - } - - /// - /// 修改实体 - /// - /// 实体 - public async Task UpdateAsync( TEntity entity ) { - await _store.UpdateAsync( ToPo( entity ) ); - } - - /// - /// 移除实体 - /// - /// 实体标识 - public void Remove( TKey id ) { - _store.Remove( id ); - } - - /// - /// 移除实体 - /// - /// 实体标识 - public async Task RemoveAsync( TKey id ) { - await _store.RemoveAsync( id ); - } - - /// - /// 移除实体 - /// - /// 实体 - public void Remove( TEntity entity ) { - _store.Remove( ToPo( entity ) ); - } - - /// - /// 移除实体 - /// - /// 实体 - public async Task RemoveAsync( TEntity entity ) { - await _store.RemoveAsync( ToPo( entity ) ); - } - - /// - /// 移除实体集合 - /// - /// 实体编号集合 - public void Remove( IEnumerable ids ) { - _store.Remove( ids ); - } - - /// - /// 移除实体集合 - /// - /// 实体编号集合 - public async Task RemoveAsync( IEnumerable ids ) { - await _store.RemoveAsync( ids ); - } - - /// - /// 移除实体集合 - /// - /// 实体集合 - public void Remove( IEnumerable entities ) { - _store.Remove( entities.Select( ToPo ) ); - } - - /// - /// 移除实体集合 - /// - /// 实体集合 - public async Task RemoveAsync( IEnumerable entities ) { - await _store.RemoveAsync( entities.Select( ToPo ) ); - } - } -} diff --git a/src/Util.Datas/Ef/Core/PersistentStoreBase.cs b/src/Util.Datas/Ef/Core/PersistentStoreBase.cs deleted file mode 100644 index b037581ff..000000000 --- a/src/Util.Datas/Ef/Core/PersistentStoreBase.cs +++ /dev/null @@ -1,346 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Util.Datas.Ef.Internal; -using Util.Datas.Persistence; -using Util.Datas.Queries; -using Util.Datas.UnitOfWorks; -using Util.Domains.Repositories; - -namespace Util.Datas.Ef.Core { - /// - /// 持久化存储 - /// - /// 持久化对象类型 - public abstract class PersistentStoreBase : PersistentStoreBase where TPo : class, IPersistentObject { - /// - /// 初始化持久化存储 - /// - /// 工作单元 - protected PersistentStoreBase( IUnitOfWork unitOfWork ) : base( unitOfWork ) { - } - } - - /// - /// 持久化存储 - /// - /// 持久化对象类型 - /// 持久化对象标识类型 - public abstract class PersistentStoreBase : IPersistentStore where TPo : class, IPersistentObject { - /// - /// 数据上下文包装器 - /// - private readonly DbContextWrapper _wrapper; - - /// - /// 工作单元 - /// - protected UnitOfWorkBase UnitOfWork { get; } - - /// - /// 实体集 - /// - protected DbSet Set { get; } - - /// - /// 初始化持久化存储 - /// - /// 工作单元 - protected PersistentStoreBase( IUnitOfWork unitOfWork ) { - _wrapper = new DbContextWrapper( unitOfWork ); - UnitOfWork = _wrapper.UnitOfWork; - Set = _wrapper.Set; - } - - /// - /// 获取未跟踪的持久化对象集合 - /// - public IQueryable FindAsNoTracking() { - return Set.AsNoTracking(); - } - - /// - /// 查找持久化对象集合 - /// - public IQueryable Find() { - return _wrapper.Find(); - } - - /// - /// 查找持久化对象集合 - /// - /// 条件 - public IQueryable Find( ICriteria criteria ) { - return Find().Where( criteria ); - } - - /// - /// 查找持久化对象 - /// - /// 持久化对象标识 - public TPo Find( object id ) { - return _wrapper.Find( id ); - } - - /// - /// 查找持久化对象 - /// - /// 持久化对象标识 - public async Task FindAsync( object id ) { - return await _wrapper.FindAsync( id ); - } - - /// - /// 查找持久化对象集合 - /// - /// 持久化对象标识集合 - public List FindByIds( params TKey[] ids ) { - return _wrapper.FindByIds( ids ); - } - - /// - /// 查找持久化对象集合 - /// - /// 持久化对象标识集合 - public List FindByIds( IEnumerable ids ) { - return _wrapper.FindByIds( ids ); - } - - /// - /// 查找持久化对象集合 - /// - /// 持久化对象标识集合 - public async Task> FindByIdsAsync( params TKey[] ids ) { - return await _wrapper.FindByIdsAsync( ids ); - } - - /// - /// 查找持久化对象集合 - /// - /// 持久化对象标识集合 - public async Task> FindByIdsAsync( IEnumerable ids ) { - return await _wrapper.FindByIdsAsync( ids ); - } - - /// - /// 获取单个持久化对象 - /// - /// 查询条件 - public TPo Single( Expression> predicate ) { - return _wrapper.Single( predicate ); - } - - /// - /// 获取单个持久化对象 - /// - /// 查询条件 - public async Task SingleAsync( Expression> predicate ) { - return await _wrapper.SingleAsync( predicate ); - } - - /// - /// 判断持久化对象是否存在 - /// - /// 持久化对象标识集合 - public bool Exists( params TKey[] ids ) { - if( ids == null ) - return false; - return Find().Any( t => ids.Contains( t.Id ) ); - } - - /// - /// 判断持久化对象是否存在 - /// - /// 持久化对象标识集合 - public async Task ExistsAsync( params TKey[] ids ) { - if( ids == null ) - return false; - return await Find().AnyAsync( t => ids.Contains( t.Id ) ); - } - - /// - /// 查询 - /// - /// 查询对象 - public List Query( IQueryBase query ) { - return _wrapper.Query( query ); - } - - /// - /// 查询 - /// - /// 查询对象 - public async Task> QueryAsync( IQueryBase query ) { - return await _wrapper.QueryAsync( query ); - } - - /// - /// 查询 - 返回未跟踪的持久化对象 - /// - /// 查询对象 - public List QueryAsNoTracking( IQueryBase query ) { - return _wrapper.QueryAsNoTracking( query ); - } - - /// - /// 查询 - 返回未跟踪的持久化对象 - /// - /// 查询对象 - public async Task> QueryAsNoTrackingAsync( IQueryBase query ) { - return await _wrapper.QueryAsNoTrackingAsync( query ); - } - - /// - /// 分页查询 - /// - /// 查询对象 - public PagerList PagerQuery( IQueryBase query ) { - return _wrapper.PagerQuery( query ); - } - - /// - /// 分页查询 - /// - /// 查询对象 - public async Task> PagerQueryAsync( IQueryBase query ) { - return await _wrapper.PagerQueryAsync( query ); - } - - /// - /// 分页查询 - /// - /// 查询对象 - public PagerList PagerQueryAsNoTracking( IQueryBase query ) { - return _wrapper.PagerQueryAsNoTracking( query ); - } - - /// - /// 分页查询 - /// - /// 查询对象 - public async Task> PagerQueryAsNoTrackingAsync( IQueryBase query ) { - return await _wrapper.PagerQueryAsNoTrackingAsync( query ); - } - - /// - /// 添加持久化对象 - /// - /// 持久化对象 - public void Add( TPo po ) { - _wrapper.Add( po ); - } - - /// - /// 添加持久化对象集合 - /// - /// 持久化对象集合 - public void Add( IEnumerable pos ) { - _wrapper.Add( pos ); - } - - /// - /// 添加持久化对象 - /// - /// 持久化对象 - public async Task AddAsync( TPo po ) { - await _wrapper.AddAsync( po ); - } - - /// - /// 添加持久化对象集合 - /// - /// 持久化对象集合 - public async Task AddAsync( IEnumerable pos ) { - await _wrapper.AddAsync( pos ); - } - - /// - /// 修改持久化对象 - /// - /// 持久化对象 - public void Update( TPo po ) { - if( po == null ) - throw new ArgumentNullException( nameof( po ) ); - var old = Find( po.Id ); - _wrapper.Update( po, old ); - } - - /// - /// 修改持久化对象 - /// - /// 持久化对象 - public async Task UpdateAsync( TPo po ) { - if( po == null ) - throw new ArgumentNullException( nameof( po ) ); - var old = await FindAsync( po.Id ); - _wrapper.Update( po, old ); - } - - /// - /// 移除持久化对象 - /// - /// 持久化对象标识 - public void Remove( TKey id ) { - _wrapper.Remove( id ); - } - - /// - /// 移除持久化对象 - /// - /// 持久化对象标识 - public async Task RemoveAsync( TKey id ) { - await _wrapper.RemoveAsync( id ); - } - - /// - /// 移除持久化对象 - /// - /// 持久化对象 - public void Remove( TPo po ) { - _wrapper.Remove( po ); - } - - /// - /// 移除持久化对象 - /// - /// 持久化对象 - public async Task RemoveAsync( TPo po ) { - await _wrapper.RemoveAsync( po ); - } - - /// - /// 移除持久化对象集合 - /// - /// 持久化对象编号集合 - public void Remove( IEnumerable ids ) { - _wrapper.Remove( ids ); - } - - /// - /// 移除持久化对象集合 - /// - /// 持久化对象编号集合 - public async Task RemoveAsync( IEnumerable ids ) { - await _wrapper.RemoveAsync( ids ); - } - - /// - /// 移除持久化对象集合 - /// - /// 持久化对象集合 - public void Remove( IEnumerable pos ) { - _wrapper.Remove( pos ); - } - - /// - /// 移除持久化对象集合 - /// - /// 持久化对象集合 - public async Task RemoveAsync( IEnumerable pos ) { - await _wrapper.RemoveAsync( pos ); - } - } -} diff --git a/src/Util.Datas/Ef/Core/RepositoryBase.cs b/src/Util.Datas/Ef/Core/RepositoryBase.cs deleted file mode 100644 index b79897e90..000000000 --- a/src/Util.Datas/Ef/Core/RepositoryBase.cs +++ /dev/null @@ -1,422 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Util.Datas.Ef.Internal; -using Util.Datas.Queries; -using Util.Datas.UnitOfWorks; -using Util.Domains; -using Util.Domains.Repositories; - -namespace Util.Datas.Ef.Core { - /// - /// 仓储 - /// - /// 实体类型 - public abstract class RepositoryBase : RepositoryBase, IRepository - where TEntity : class, IAggregateRoot { - /// - /// 初始化仓储 - /// - /// 工作单元 - protected RepositoryBase( IUnitOfWork unitOfWork ) - : base( unitOfWork ) { - } - } - - /// - /// 仓储 - /// - /// 实体类型 - /// 实体标识类型 - public abstract class RepositoryBase : IRepository - where TEntity : class, IAggregateRoot { - /// - /// 数据上下文包装器 - /// - private readonly DbContextWrapper _wrapper; - - /// - /// 工作单元 - /// - protected UnitOfWorkBase UnitOfWork { get; } - - /// - /// 实体集 - /// - protected DbSet Set { get; } - - /// - /// 初始化仓储 - /// - /// 工作单元 - protected RepositoryBase( IUnitOfWork unitOfWork ) { - _wrapper = new DbContextWrapper( unitOfWork ); - UnitOfWork = _wrapper.UnitOfWork; - Set = _wrapper.Set; - } - - /// - /// 获取未跟踪的实体集 - /// - public IQueryable FindAsNoTracking() { - return Set.AsNoTracking(); - } - - /// - /// 查找实体 - /// - public IQueryable Find() { - return _wrapper.Find(); - } - - /// - /// 查找实体集合 - /// - public IQueryable Find( ICriteria criteria ) { - return Find().Where( criteria ); - } - - /// - /// 查找实体集合 - /// - public IQueryable Find( Expression> predicate ) { - return Find().Where( predicate ); - } - - /// - /// 查找实体 - /// - public TEntity Find( object id ) { - return _wrapper.Find( id ); - } - - /// - /// 查找实体 - /// - public async Task FindAsync( object id ) { - return await _wrapper.FindAsync( id ); - } - - /// - /// 查找实体集合 - /// - public List FindByIds( params TKey[] ids ) { - return _wrapper.FindByIds( ids ); - } - - /// - /// 查找实体集合 - /// - public List FindByIds( IEnumerable ids ) { - return _wrapper.FindByIds( ids ); - } - - /// - /// 查找实体集合 - /// - /// 逗号分隔的Id列表,范例:"1,2" - public List FindByIds( string ids ) { - var idList = Util.Helpers.Convert.ToList( ids ); - return FindByIds( idList ); - } - - /// - /// 查找实体集合 - /// - public async Task> FindByIdsAsync( params TKey[] ids ) { - return await _wrapper.FindByIdsAsync( ids ); - } - - /// - /// 查找实体集合 - /// - public async Task> FindByIdsAsync( IEnumerable ids ) { - return await _wrapper.FindByIdsAsync( ids ); - } - - /// - /// 查找实体集合 - /// - /// 逗号分隔的Id列表,范例:"1,2" - public async Task> FindByIdsAsync( string ids ) { - var idList = Util.Helpers.Convert.ToList( ids ); - return await FindByIdsAsync( idList ); - } - - /// - /// 获取单个实体 - /// - public TEntity Single( Expression> predicate ) { - return _wrapper.Single( predicate ); - } - - /// - /// 获取单个实体 - /// - public async Task SingleAsync( Expression> predicate ) { - return await _wrapper.SingleAsync( predicate ); - } - - /// - /// 查找实体集合 - /// - public List FindAll( Expression> predicate = null ) { - if( predicate == null ) - return Find().ToList(); - return Find( predicate ).ToList(); - } - - /// - /// 查找实体集合 - /// - public async Task> FindAllAsync( Expression> predicate = null ) { - if( predicate == null ) - return await Find().ToListAsync(); - return await Find( predicate ).ToListAsync(); - } - - /// - /// 判断实体是否存在 - /// - public bool Exists( Expression> predicate ) { - if( predicate == null ) - return false; - return Find().Any( predicate ); - } - - /// - /// 判断实体是否存在 - /// - /// 实体标识集合 - public bool Exists( params TKey[] ids ) { - if( ids == null ) - return false; - return Exists( t => ids.Contains( t.Id ) ); - } - - /// - /// 判断实体是否存在 - /// - /// 查询条件 - public async Task ExistsAsync( Expression> predicate ) { - if( predicate == null ) - return false; - return await Find().AnyAsync( predicate ); - } - - /// - /// 判断实体是否存在 - /// - /// 实体标识集合 - public async Task ExistsAsync( params TKey[] ids ) { - if( ids == null ) - return false; - return await ExistsAsync( t => ids.Contains( t.Id ) ); - } - - /// - /// 获取实体个数 - /// - /// 查询条件 - public int Count( Expression> predicate = null ) { - if( predicate == null ) - return Set.Count(); - return Set.Count( predicate ); - } - - /// - /// 获取实体个数 - /// - /// 查询条件 - public async Task CountAsync( Expression> predicate = null ) { - if( predicate == null ) - return await Set.CountAsync(); - return await Set.CountAsync( predicate ); - } - - /// - /// 查询 - /// - /// 查询对象 - public List Query( IQueryBase query ) { - return _wrapper.Query( query ); - } - - /// - /// 查询 - /// - /// 查询对象 - public async Task> QueryAsync( IQueryBase query ) { - return await _wrapper.QueryAsync( query ); - } - - /// - /// 查询 - 返回未跟踪的实体 - /// - /// 查询对象 - public List QueryAsNoTracking( IQueryBase query ) { - return _wrapper.QueryAsNoTracking( query ); - } - - /// - /// 查询 - 返回未跟踪的实体 - /// - /// 查询对象 - public async Task> QueryAsNoTrackingAsync( IQueryBase query ) { - return await _wrapper.QueryAsNoTrackingAsync( query ); - } - - /// - /// 分页查询 - /// - /// 查询对象 - public PagerList PagerQuery( IQueryBase query ) { - return _wrapper.PagerQuery( query ); - } - - /// - /// 分页查询 - /// - /// 查询对象 - public async Task> PagerQueryAsync( IQueryBase query ) { - return await _wrapper.PagerQueryAsync( query ); - } - - /// - /// 分页查询 - /// - /// 查询对象 - public PagerList PagerQueryAsNoTracking( IQueryBase query ) { - return _wrapper.PagerQueryAsNoTracking( query ); - } - - /// - /// 分页查询 - /// - /// 查询对象 - public async Task> PagerQueryAsNoTrackingAsync( IQueryBase query ) { - return await _wrapper.PagerQueryAsNoTrackingAsync( query ); - } - - /// - /// 添加实体 - /// - /// 实体 - public void Add( TEntity entity ) { - _wrapper.Add( entity ); - } - - /// - /// 添加实体集合 - /// - /// 实体集合 - public void Add( IEnumerable entities ) { - _wrapper.Add( entities ); - } - - /// - /// 添加实体 - /// - /// 实体 - public async Task AddAsync( TEntity entity ) { - await _wrapper.AddAsync( entity ); - } - - /// - /// 添加实体集合 - /// - /// 实体集合 - public async Task AddAsync( IEnumerable entities ) { - await _wrapper.AddAsync( entities ); - } - - /// - /// 修改实体 - /// - /// 实体 - public void Update( TEntity entity ) { - if( entity == null ) - throw new ArgumentNullException( nameof( entity ) ); - var oldEntity = Find( entity.Id ); - _wrapper.Update( entity, oldEntity ); - } - - /// - /// 异步修改实体 - /// - /// 实体 - public async Task UpdateAsync( TEntity entity ) { - if( entity == null ) - throw new ArgumentNullException( nameof( entity ) ); - var oldEntity = await _wrapper.FindAsync( entity.Id ); - _wrapper.Update( entity, oldEntity ); - } - - /// - /// 移除实体 - /// - /// 实体标识 - public void Remove( TKey id ) { - _wrapper.Remove( id ); - } - - /// - /// 移除实体 - /// - /// 实体标识 - public async Task RemoveAsync( TKey id ) { - await _wrapper.RemoveAsync( id ); - } - - /// - /// 移除实体 - /// - /// 实体 - public void Remove( TEntity entity ) { - _wrapper.Remove( entity ); - } - - /// - /// 移除实体 - /// - /// 实体 - public async Task RemoveAsync( TEntity entity ) { - await _wrapper.RemoveAsync( entity ); - } - - /// - /// 移除实体集合 - /// - /// 实体编号集合 - public void Remove( IEnumerable ids ) { - _wrapper.Remove( ids ); - } - - /// - /// 移除实体集合 - /// - /// 实体编号集合 - public async Task RemoveAsync( IEnumerable ids ) { - await _wrapper.RemoveAsync( ids ); - } - - /// - /// 移除实体集合 - /// - /// 实体集合 - public void Remove( IEnumerable entities ) { - _wrapper.Remove( entities ); - } - - /// - /// 移除实体集合 - /// - /// 实体集合 - public async Task RemoveAsync( IEnumerable entities ) { - await _wrapper.RemoveAsync( entities ); - } - } -} diff --git a/src/Util.Datas/Ef/Core/UnitOfWorkBase.cs b/src/Util.Datas/Ef/Core/UnitOfWorkBase.cs deleted file mode 100644 index 80058238d..000000000 --- a/src/Util.Datas/Ef/Core/UnitOfWorkBase.cs +++ /dev/null @@ -1,251 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.ChangeTracking; -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Util.Datas.Ef.Configs; -using Util.Datas.UnitOfWorks; -using Util.Domains.Auditing; -using Util.Domains.Sessions; -using Util.Exceptions; -using Util.Datas.Ef.Logs; -using Util.Logs; - -namespace Util.Datas.Ef.Core { - /// - /// 工作单元 - /// - public abstract class UnitOfWorkBase : DbContext, IUnitOfWork { - - #region 构造方法 - - /// - /// 初始化Entity Framework工作单元 - /// - /// 配置 - /// 工作单元服务 - protected UnitOfWorkBase( DbContextOptions options, IUnitOfWorkManager manager ) - : base( options ) { - manager?.Register( this ); - TraceId = Guid.NewGuid().ToString(); - Session = Util.Domains.Sessions.Session.Null; - } - - #endregion - - #region 属性 - - /// - /// 跟踪号 - /// - public string TraceId { get; set; } - /// - /// 用户会话 - /// - public ISession Session { get; set; } - - #endregion - - #region OnConfiguring(配置) - - /// - /// 配置 - /// - /// 配置生成器 - protected override void OnConfiguring( DbContextOptionsBuilder builder ) { - EnableLog( builder ); - } - - /// - /// 启用日志 - /// - protected void EnableLog( DbContextOptionsBuilder builder ) { - var log = GetLog(); - if ( IsEnabled( log ) == false ) - return; - builder.EnableSensitiveDataLogging(); - builder.UseLoggerFactory( new LoggerFactory( new[] { GetLogProvider( log ) } ) ); - } - - /// - /// 获取日志操作 - /// - protected virtual ILog GetLog() { - try { - return Log.GetLog( EfLog.TraceLogName ); - } - catch { - return Log.Null; - } - } - - /// - /// 是否启用Ef日志 - /// - private bool IsEnabled( ILog log ) { - return EfConfig.LogLevel != EfLogLevel.Off && log.IsTraceEnabled; - } - - /// - /// 获取日志提供器 - /// - protected virtual ILoggerProvider GetLogProvider( ILog log ) { - return new EfLogProvider( log, this ); - } - - #endregion - - #region Commit(提交) - - /// - /// 提交,返回影响的行数 - /// - public int Commit() { - try { - return SaveChanges(); - } - catch( DbUpdateConcurrencyException ex ) { - throw new ConcurrencyException( ex ); - } - } - - #endregion - - #region CommitAsync(异步提交) - /// - /// 异步提交,返回影响的行数 - /// - public async Task CommitAsync() { - try { - return await SaveChangesAsync(); - } - catch( DbUpdateConcurrencyException ex ) { - throw new ConcurrencyException( ex ); - } - } - #endregion - - #region OnModelCreating(配置映射) - - /// - /// 配置映射 - /// - protected override void OnModelCreating( ModelBuilder modelBuilder ) { - foreach( IMap mapper in GetMaps() ) - mapper.Map( modelBuilder ); - } - - /// - /// 获取映射配置列表 - /// - private IEnumerable GetMaps() { - var result = new List(); - foreach( var assembly in GetAssemblies() ) - result.AddRange( GetMapTypes( assembly ) ); - return result; - } - - /// - /// 获取定义映射配置的程序集列表 - /// - protected virtual Assembly[] GetAssemblies() { - return new[] { GetType().GetTypeInfo().Assembly }; - } - - /// - /// 获取映射类型列表 - /// - /// 程序集 - protected virtual IEnumerable GetMapTypes( Assembly assembly ) { - return Util.Helpers.Reflection.GetTypesByInterface( assembly ); - } - - #endregion - - #region SaveChanges(保存更改) - - /// - /// 保存更改 - /// - public override int SaveChanges() { - SaveChangesBefore(); - return base.SaveChanges(); - } - - /// - /// 保存更改前操作 - /// - protected virtual void SaveChangesBefore() { - foreach( var entry in ChangeTracker.Entries() ) { - switch( entry.State ) { - case EntityState.Added: - InterceptAddedOperation( entry ); - break; - case EntityState.Modified: - InterceptModifiedOperation( entry ); - break; - case EntityState.Deleted: - InterceptDeletedOperation( entry ); - break; - } - } - } - - /// - /// 拦截添加操作 - /// - protected virtual void InterceptAddedOperation( EntityEntry entry ) { - InitCreationAudited( entry ); - InitModificationAudited( entry ); - } - - /// - /// 初始化创建审计信息 - /// - private void InitCreationAudited( EntityEntry entry ) { - CreationAuditedInitializer.Init( entry.Entity, GetSession() ); - } - - /// - /// 获取用户会话 - /// - protected virtual ISession GetSession() { - return Session; - } - - /// - /// 初始化修改审计信息 - /// - private void InitModificationAudited( EntityEntry entry ) { - ModificationAuditedInitializer.Init( entry.Entity, GetSession() ); - } - - /// - /// 拦截修改操作 - /// - protected virtual void InterceptModifiedOperation( EntityEntry entry ) { - InitModificationAudited( entry ); - } - - /// - /// 拦截删除操作 - /// - protected virtual void InterceptDeletedOperation( EntityEntry entry ) { - } - - #endregion - - #region SaveChangesAsync(异步保存更改) - /// - /// 异步保存更改 - /// - public override Task SaveChangesAsync( CancellationToken cancellationToken = default( CancellationToken ) ) { - SaveChangesBefore(); - return base.SaveChangesAsync( cancellationToken ); - } - #endregion - } -} diff --git a/src/Util.Datas/Ef/Extensions.Queryable.cs b/src/Util.Datas/Ef/Extensions.Queryable.cs deleted file mode 100644 index bbbb9d33a..000000000 --- a/src/Util.Datas/Ef/Extensions.Queryable.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Util.Domains.Repositories; - -namespace Util.Datas.Ef { - /// - /// 查询扩展 - /// - public static partial class Extensions { - /// - /// 转换为分页列表 - /// - /// 实体类型 - /// 数据源 - /// 分页对象 - public static async Task> ToPagerListAsync( this IQueryable source, IPager pager ) { - if( source == null ) - throw new ArgumentNullException( nameof( source ) ); - if( pager == null ) - throw new ArgumentNullException( nameof( pager ) ); - var result = new PagerList( pager ); - result.AddRange( await source.ToListAsync() ); - return result; - } - } -} diff --git a/src/Util.Datas/Ef/Extensions.Service.cs b/src/Util.Datas/Ef/Extensions.Service.cs deleted file mode 100644 index dffad0f51..000000000 --- a/src/Util.Datas/Ef/Extensions.Service.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Util.Datas.Ef.Configs; -using Util.Datas.Ef.Core; -using Util.Datas.UnitOfWorks; - -namespace Util.Datas.Ef { - /// - /// 服务扩展 - /// - public static partial class Extensions { - /// - /// 注册工作单元服务 - /// - /// 工作单元接口类型 - /// 工作单元实现类型 - /// 服务集合 - /// 配置操作 - public static IServiceCollection AddUnitOfWork( this IServiceCollection services, Action configAction ) - where TService : class, IUnitOfWork - where TImplementation : UnitOfWorkBase, TService { - services.AddDbContext( configAction ); - services.AddScoped(); - return services; - } - - /// - /// 注册工作单元服务 - /// - /// 工作单元接口类型 - /// 工作单元实现类型 - /// 服务集合 - /// 连接字符串 - /// Ef日志级别 - public static IServiceCollection AddUnitOfWork( this IServiceCollection services, string connection,EfLogLevel level = EfLogLevel.Sql ) - where TService : class, IUnitOfWork - where TImplementation : UnitOfWorkBase, TService { - EfConfig.LogLevel = level; - return AddUnitOfWork( services, builder => { - ConfigConnection( builder, connection ); - } ); - } - - /// - /// 配置连接字符串 - /// - private static void ConfigConnection( DbContextOptionsBuilder builder, string connection ) where TImplementation : UnitOfWorkBase { - var type = typeof( TImplementation ).BaseType; - if( type == typeof( Util.Datas.Ef.SqlServer.UnitOfWork ) ) { - builder.UseSqlServer( connection ); - return; - } - if( type == typeof( Util.Datas.Ef.MySql.UnitOfWork ) ) { - builder.UseMySql( connection ); - return; - } - if( type == typeof( Util.Datas.Ef.PgSql.UnitOfWork ) ) { - builder.UseNpgsql( connection ); - return; - } - } - } -} diff --git a/src/Util.Datas/Ef/Extensions.UnitOfWork.cs b/src/Util.Datas/Ef/Extensions.UnitOfWork.cs deleted file mode 100644 index 5203cb2de..000000000 --- a/src/Util.Datas/Ef/Extensions.UnitOfWork.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Linq; -using Microsoft.EntityFrameworkCore; -using Util.Datas.UnitOfWorks; - -namespace Util.Datas.Ef { - /// - /// Ef工作单元扩展 - /// - public static partial class Extensions { - /// - /// 清空缓存 - /// - public static void ClearCache(this IUnitOfWork unitOfWork) { - var dbContext = unitOfWork as DbContext; - dbContext?.ChangeTracker.Entries().ToList().ForEach( entry => entry.State = EntityState.Detached ); - } - } -} diff --git a/src/Util.Datas/Ef/Internal/DbContextWrapper.cs b/src/Util.Datas/Ef/Internal/DbContextWrapper.cs deleted file mode 100644 index e35cc1303..000000000 --- a/src/Util.Datas/Ef/Internal/DbContextWrapper.cs +++ /dev/null @@ -1,395 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Dynamic.Core; -using System.Linq.Expressions; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Util.Datas.Ef.Core; -using Util.Datas.Queries; -using Util.Datas.UnitOfWorks; -using Util.Domains; -using Util.Domains.Repositories; -using Util.Exceptions; - -namespace Util.Datas.Ef.Internal { - /// - /// 数据上下文包装器 - /// - internal class DbContextWrapper where TEntity : class, IKey, IVersion { - /// - /// 初始化数据上下文包装器 - /// - /// 工作单元 - public DbContextWrapper( IUnitOfWork unitOfWork ) { - UnitOfWork = (UnitOfWorkBase)unitOfWork; - } - - /// - /// 工作单元 - /// - public UnitOfWorkBase UnitOfWork { get; } - - /// - /// 实体集 - /// - public DbSet Set => UnitOfWork.Set(); - - /// - /// 获取未跟踪的实体集 - /// - public IQueryable FindAsNoTracking() { - return Set.AsNoTracking(); - } - - /// - /// 查找实体 - /// - public IQueryable Find() { - return Set; - } - - /// - /// 查找实体 - /// - /// 实体标识 - public TEntity Find( object id ) { - if( id == null ) - return null; - return Set.Find( id ); - } - - /// - /// 查找实体 - /// - /// 实体标识 - public async Task FindAsync( object id ) { - if( id == null ) - return null; - return await Set.FindAsync( id ); - } - - /// - /// 查找实体集合 - /// - /// 实体标识集合 - public List FindByIds( params TKey[] ids ) { - return FindByIds( (IEnumerable)ids ); - } - - /// - /// 查找实体集合 - /// - /// 实体标识集合 - public List FindByIds( IEnumerable ids ) { - if( ids == null ) - return null; - return Find().Where( t => ids.Contains( t.Id ) ).ToList(); - } - - /// - /// 查找实体集合 - /// - /// 实体标识集合 - public async Task> FindByIdsAsync( params TKey[] ids ) { - return await FindByIdsAsync( (IEnumerable)ids ); - } - - /// - /// 查找实体集合 - /// - /// 实体标识集合 - public async Task> FindByIdsAsync( IEnumerable ids ) { - if( ids == null ) - return null; - return await Find().Where( t => ids.Contains( t.Id ) ).ToListAsync(); - } - - /// - /// 获取单个实体 - /// - /// 条件 - public TEntity Single( Expression> predicate ) { - return Find().FirstOrDefault( predicate ); - } - - /// - /// 获取单个实体 - /// - /// 查询条件 - public async Task SingleAsync( Expression> predicate ) { - return await Find().FirstOrDefaultAsync( predicate ); - } - - /// - /// 查询 - /// - /// 查询对象 - public List Query( IQueryBase query ) { - return GetQueryResult( Find(), query ).ToList(); - } - - /// - /// 获取查询结果 - /// - private IQueryable GetQueryResult( IQueryable queryable, IQueryBase query ) { - queryable = queryable.Where( query ); - var order = query.GetOrder(); - if( string.IsNullOrWhiteSpace( order ) ) - return queryable; - return queryable.OrderBy( order ); - } - - /// - /// 查询 - /// - /// 查询对象 - public async Task> QueryAsync( IQueryBase query ) { - return await GetQueryResult( Find(), query ).ToListAsync(); - } - - /// - /// 查询 - 返回未跟踪的实体 - /// - /// 查询对象 - public List QueryAsNoTracking( IQueryBase query ) { - return GetQueryResult( FindAsNoTracking(), query ).ToList(); - } - - /// - /// 查询 - 返回未跟踪的实体 - /// - /// 查询对象 - public async Task> QueryAsNoTrackingAsync( IQueryBase query ) { - return await GetQueryResult( FindAsNoTracking(), query ).ToListAsync(); - } - - /// - /// 分页查询 - /// - /// 查询对象 - public PagerList PagerQuery( IQueryBase query ) { - var pager = query.GetPager(); - return GetPagerQueryResult( Find(), query, pager ).ToPagerList( pager ); - } - - /// - /// 获取分页查询结果 - /// - private IQueryable GetPagerQueryResult( IQueryable queryable, IQueryBase query, IPager pager ) { - var order = pager.Order; - if( string.IsNullOrWhiteSpace( order ) ) - order = "Id"; - return queryable.Where( query ).OrderBy( order ).Pager( pager ); - } - - /// - /// 分页查询 - /// - /// 查询对象 - public async Task> PagerQueryAsync( IQueryBase query ) { - var pager = query.GetPager(); - return await GetPagerQueryResult( Find(), query, pager ).ToPagerListAsync( pager ); - } - - /// - /// 分页查询 - /// - /// 查询对象 - public PagerList PagerQueryAsNoTracking( IQueryBase query ) { - var pager = query.GetPager(); - return GetPagerQueryResult( FindAsNoTracking(), query, pager ).ToPagerList( pager ); - } - - /// - /// 分页查询 - /// - /// 查询对象 - public async Task> PagerQueryAsNoTrackingAsync( IQueryBase query ) { - var pager = query.GetPager(); - return await GetPagerQueryResult( FindAsNoTracking(), query, pager ).ToPagerListAsync( pager ); - } - - /// - /// 添加实体 - /// - /// 实体 - public void Add( TEntity entity ) { - if( entity == null ) - throw new ArgumentNullException( nameof( entity ) ); - Set.Add( entity ); - } - - /// - /// 添加实体集合 - /// - /// 实体集合 - public void Add( IEnumerable entities ) { - if( entities == null ) - throw new ArgumentNullException( nameof( entities ) ); - Set.AddRange( entities ); - } - - /// - /// 添加实体 - /// - /// 实体 - public async Task AddAsync( TEntity entity ) { - if( entity == null ) - throw new ArgumentNullException( nameof( entity ) ); - await Set.AddAsync( entity ); - } - - /// - /// 添加实体集合 - /// - /// 实体集合 - public async Task AddAsync( IEnumerable entities ) { - if( entities == null ) - throw new ArgumentNullException( nameof( entities ) ); - await Set.AddRangeAsync( entities ); - } - - /// - /// 修改实体 - /// - /// 实体 - public virtual void Update( TEntity entity ) { - if( entity == null ) - throw new ArgumentNullException( nameof( entity ) ); - UnitOfWork.Entry( entity ).State = EntityState.Modified; - } - - /// - /// 修改实体 - /// - /// 新实体 - /// 数据库中旧的实体 - public void Update( TEntity newEntity, TEntity oldEntity ) { - if( newEntity == null ) - throw new ArgumentNullException( nameof( newEntity ) ); - if( oldEntity == null ) - throw new ArgumentNullException( nameof( oldEntity ) ); - ValidateVersion( newEntity, oldEntity ); - UnitOfWork.Entry( oldEntity ).CurrentValues.SetValues( newEntity ); - } - - //验证版本号 - private void ValidateVersion( TEntity newEntity, TEntity oldEntity ) { - if( newEntity.Version == null ) - throw new ConcurrencyException(); - for( int i = 0; i < oldEntity.Version.Length; i++ ) { - if( newEntity.Version[i] != oldEntity.Version[i] ) - throw new ConcurrencyException( $"新实体:{newEntity.SafeString()},旧实体:{oldEntity.SafeString()}" ); - } - } - - /// - /// 移除实体 - /// - /// 实体标识 - public void Remove( TKey id ) { - var entity = Find( id ); - Delete( entity ); - } - - /// - /// 删除 - /// - private void Delete( TEntity entity ) { - if ( entity == null ) - return; - if( entity is IDelete model ) { - model.IsDeleted = true; - return; - } - Set.Remove( entity ); - } - - /// - /// 移除实体 - /// - /// 实体标识 - public async Task RemoveAsync( TKey id ) { - var entity = await FindAsync( id ); - Delete( entity ); - } - - /// - /// 移除实体 - /// - public void Remove( TEntity entity ) { - if( entity == null ) - return; - Remove( entity.Id ); - } - - /// - /// 移除实体 - /// - /// 实体 - public async Task RemoveAsync( TEntity entity ) { - if( entity == null ) - return; - await RemoveAsync( entity.Id ); - } - - /// - /// 移除实体集合 - /// - /// 实体编号集合 - public void Remove( IEnumerable ids ) { - if( ids == null ) - return; - var list = FindByIds( ids ); - Delete( list ); - } - - /// - /// 删除实体集合 - /// - private void Delete( List list ) { - if ( list == null ) - return; - if( !list.Any() ) - return; - if( list[0] is IDelete ) { - foreach( var entity in list.Select( t => (IDelete)t ) ) - entity.IsDeleted = true; - return; - } - Set.RemoveRange( list ); - } - - /// - /// 移除实体集合 - /// - /// 实体编号集合 - public async Task RemoveAsync( IEnumerable ids ) { - if( ids == null ) - return; - var list = await FindByIdsAsync( ids ); - Delete( list ); - } - - /// - /// 移除实体集合 - /// - /// 实体集合 - public void Remove( IEnumerable entities ) { - if( entities == null ) - return; - Remove( entities.Select( t => t.Id ) ); - } - - /// - /// 移除实体集合 - /// - /// 实体集合 - public async Task RemoveAsync( IEnumerable entities ) { - if( entities == null ) - return; - await RemoveAsync( entities.Select( t => t.Id ) ); - } - } -} diff --git a/src/Util.Datas/Ef/Internal/Helper.cs b/src/Util.Datas/Ef/Internal/Helper.cs deleted file mode 100644 index 31fdf200f..000000000 --- a/src/Util.Datas/Ef/Internal/Helper.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Text; -using Microsoft.EntityFrameworkCore.ChangeTracking; -using Util.Domains; - -namespace Util.Datas.Ef.Internal { - /// - /// 工具操作 - /// - internal static class Helper { - /// - /// 初始化版本号 - /// - public static void InitVersion( EntityEntry entry ) { - if( !( entry.Entity is IAggregateRoot entity ) ) - return; - entity.Version = Encoding.UTF8.GetBytes( Guid.NewGuid().ToString() ); - } - } -} diff --git a/src/Util.Datas/Ef/Logs/EfLog.cs b/src/Util.Datas/Ef/Logs/EfLog.cs deleted file mode 100644 index 11e1e1a11..000000000 --- a/src/Util.Datas/Ef/Logs/EfLog.cs +++ /dev/null @@ -1,212 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Extensions.Logging; -using Util.Datas.Ef.Configs; -using Util.Datas.Ef.Core; -using Util.Helpers; -using Util.Logs; -using Util.Logs.Extensions; - -namespace Util.Datas.Ef.Logs { - /// - /// Ef日志记录器 - /// - public class EfLog : ILogger { - /// - /// Ef跟踪日志名 - /// - public const string TraceLogName = "EfTraceLog"; - /// - /// 日志操作 - /// - private readonly ILog _log; - /// - /// 工作单元 - /// - private readonly UnitOfWorkBase _unitOfWork; - /// - /// 日志分类 - /// - private readonly string _category; - - /// - /// 初始化Ef日志记录器 - /// - /// 日志操作 - /// 工作单元 - /// 日志分类 - public EfLog( ILog log, UnitOfWorkBase unitOfWork, string category ) { - _log = log ?? throw new ArgumentNullException( nameof( log ) ); - _unitOfWork = unitOfWork ?? throw new ArgumentNullException( nameof( unitOfWork ) ); - _category = category; - } - - /// - /// 日志记录 - /// - /// 状态类型 - /// 日志级别 - /// 事件编号 - /// 状态 - /// 异常 - /// 日志内容 - public void Log( LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter ) { - if( IsEnabled( eventId ) == false ) - return; - _log.Caption( $"执行Ef操作:{_category}" ) - .Content( $"工作单元跟踪号: {_unitOfWork.TraceId}" ) - .Content( $"事件Id: {eventId.Id}" ) - .Content( $"事件名称: {eventId.Name}" ); - AddContent( state ); - _log.Exception( exception ).Trace(); - } - - /// - /// 是否启用Ef日志 - /// - private bool IsEnabled( EventId eventId ) { - if( EfConfig.LogLevel == EfLogLevel.Off ) - return false; - if( EfConfig.LogLevel == EfLogLevel.All ) - return true; - if( eventId.Name == "Microsoft.EntityFrameworkCore.Database.Command.CommandExecuted" ) - return true; - return false; - } - - /// - /// 添加日志内容 - /// - private void AddContent( TState state ) { - if( EfConfig.LogLevel == EfLogLevel.All ) - _log.Content( "事件内容:" ).Content( state.SafeString() ); - if( !( state is IEnumerable list ) ) - return; - var dictionary = new Dictionary(); - foreach( KeyValuePair item in list ) - dictionary.Add( item.Key, item.Value.SafeString() ); - AddDictionary( dictionary ); - } - - /// - /// 添加字典内容 - /// - private void AddDictionary( IDictionary dictionary ) { - AddElapsed( GetValue( dictionary, "elapsed" ) ); - var sqlParams = GetValue( dictionary, "parameters" ); - AddSql( GetValue( dictionary, "commandText" ), sqlParams ); - AddSqlParams( sqlParams ); - } - - /// - /// 获取值 - /// - private string GetValue( IDictionary dictionary, string key ) { - if( dictionary.ContainsKey( key ) ) - return dictionary[key]; - return string.Empty; - } - - /// - /// 添加执行时间 - /// - private void AddElapsed( string value ) { - if( string.IsNullOrWhiteSpace( value ) ) - return; - _log.Content( $"执行时间: {value} 毫秒" ); - } - - /// - /// 添加Sql - /// - private void AddSql( string sql, string sqlParams ) { - if( string.IsNullOrWhiteSpace( sql ) ) - return; - _log.Sql( "原始Sql: " ).Sql( $"{sql}{Common.Line}" ); - sql = sql.Replace( "SET NOCOUNT ON;", "" ); - _log.Sql( $"调试Sql: {Common.Line}{GetSql( sql, sqlParams )}{Common.Line}" ); - } - - /// - /// 添加Sql参数 - /// - private void AddSqlParams( string value ) { - if( string.IsNullOrWhiteSpace( value ) ) - return; - _log.SqlParams( value ); - } - - /// - /// 是否启用 - /// - /// 日志级别 - public bool IsEnabled( LogLevel logLevel ) { - return true; - } - - /// - /// 起始范围 - /// - public IDisposable BeginScope( TState state ) { - return null; - } - - /// - /// 获取Sql - /// - public static string GetSql( string sql, string sqlParams ) { - var parameters = GetSqlParameters( sqlParams ); - foreach ( var parameter in parameters ) - sql = Util.Helpers.Regex.Replace( sql, $@"{parameter.Key}\b", parameter.Value ); - return sql; - } - - /// - /// 获取Sql参数字典 - /// - /// Sql参数 - public static IDictionary GetSqlParameters( string sqlParams ) { - var result = new Dictionary(); - var paramName = GetParamName( sqlParams ); - if( string.IsNullOrWhiteSpace( paramName ) ) - return result; - string pattern = $@",\s*?{paramName}"; - var parameters = Util.Helpers.Regex.Split( sqlParams, pattern ); - foreach( var parameter in parameters ) - AddParameter( result, parameter, paramName ); - return result; - } - - /// - /// 获取参数名 - /// - private static string GetParamName( string sqlParams ) { - string pattern = $@"([@].*?)\d+="; - return Util.Helpers.Regex.GetValue( sqlParams, pattern, "$1" ); - } - - /// - /// 添加参数 - /// - private static void AddParameter( Dictionary result, string parameter, string paramName ) { - string pattern = $@"(?:{paramName})?(\d+)='(.*)'(.*)"; - var values = Util.Helpers.Regex.GetValues( parameter, pattern, new[] { "$1", "$2", "$3" } ).Select( t => t.Value ).ToList(); - if ( values.Count != 3 ) - return; - result.Add( $"{paramName}{values[0]}", GetValue( values[1], values[2] ) ); - } - - /// - /// 获取值 - /// - private static string GetValue( string value, string parameter ) { - value = value.SafeString(); - parameter = parameter.SafeString(); - if( string.IsNullOrWhiteSpace( value ) && parameter.Contains( "DbType = Guid" ) ) - return "null"; - return $"'{value}'"; - } - } -} diff --git a/src/Util.Datas/Ef/Logs/EfLogProvider.cs b/src/Util.Datas/Ef/Logs/EfLogProvider.cs deleted file mode 100644 index 12c522b30..000000000 --- a/src/Util.Datas/Ef/Logs/EfLogProvider.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; -using Util.Datas.Ef.Core; -using Util.Logs; - -namespace Util.Datas.Ef.Logs { - /// - /// Ef日志提供器 - /// - public class EfLogProvider : ILoggerProvider { - /// - /// 日志操作 - /// - private readonly ILog _log; - /// - /// 工作单元 - /// - private readonly UnitOfWorkBase _unitOfWork; - - /// - /// 初始化Ef日志提供器 - /// - /// 日志操作 - /// 工作单元 - public EfLogProvider( ILog log, UnitOfWorkBase unitOfWork ) { - _log = log ?? throw new ArgumentNullException( nameof( log ) ); - _unitOfWork = unitOfWork ?? throw new ArgumentNullException( nameof( unitOfWork ) ); - } - - /// - /// 初始化Ef日志提供器 - /// - /// 日志分类 - public ILogger CreateLogger( string category ) { - return category.StartsWith( "Microsoft.EntityFrameworkCore" ) ? new EfLog( _log, _unitOfWork, category ) : NullLogger.Instance; - } - - /// - /// 释放 - /// - public void Dispose() { - } - } -} diff --git a/src/Util.Datas/Ef/Logs/NullLogger.cs b/src/Util.Datas/Ef/Logs/NullLogger.cs deleted file mode 100644 index 15b070465..000000000 --- a/src/Util.Datas/Ef/Logs/NullLogger.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; - -namespace Util.Datas.Ef.Logs { - /// - /// 空日志记录器 - /// - public class NullLogger : ILogger { - /// - /// 空日志记录器实例 - /// - public static readonly ILogger Instance = new NullLogger(); - - /// - /// 日志记录 - /// - public void Log( LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter ) { - } - - /// - /// 是否启用 - /// - /// 日志级别 - public bool IsEnabled( LogLevel logLevel ) { - return false; - } - - /// - /// 起始范围 - /// - public IDisposable BeginScope( TState state ) { - return null; - } - } -} diff --git a/src/Util.Datas/Ef/MySql/AggregateRootMap.cs b/src/Util.Datas/Ef/MySql/AggregateRootMap.cs deleted file mode 100644 index c3e032444..000000000 --- a/src/Util.Datas/Ef/MySql/AggregateRootMap.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Util.Datas.Ef.Core; -using Util.Domains; - -namespace Util.Datas.Ef.MySql { - /// - /// 聚合根映射配置 - /// - /// 实体类型 - public abstract class AggregateRootMap : MapBase, IMap where TEntity : class, IVersion { - /// - /// 映射乐观离线锁 - /// - protected override void MapVersion( EntityTypeBuilder builder ) { - builder.Property( t => t.Version ).IsConcurrencyToken(); - } - } -} \ No newline at end of file diff --git a/src/Util.Datas/Ef/MySql/EntityMap.cs b/src/Util.Datas/Ef/MySql/EntityMap.cs deleted file mode 100644 index d2f3a8418..000000000 --- a/src/Util.Datas/Ef/MySql/EntityMap.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Util.Datas.Ef.Core; - -namespace Util.Datas.Ef.MySql { - /// - /// 实体映射配置 - /// - /// 实体类型 - public abstract class EntityMap : MapBase, IMap where TEntity : class { - } -} diff --git a/src/Util.Datas/Ef/MySql/IMap.cs b/src/Util.Datas/Ef/MySql/IMap.cs deleted file mode 100644 index 69ede1d35..000000000 --- a/src/Util.Datas/Ef/MySql/IMap.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Datas.Ef.MySql { - /// - /// 映射 - /// - public interface IMap : Util.Datas.Ef.Core.IMap { - } -} \ No newline at end of file diff --git a/src/Util.Datas/Ef/MySql/UnitOfWork.cs b/src/Util.Datas/Ef/MySql/UnitOfWork.cs deleted file mode 100644 index a70392583..000000000 --- a/src/Util.Datas/Ef/MySql/UnitOfWork.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Collections.Generic; -using System.Reflection; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.ChangeTracking; -using Util.Datas.Ef.Core; -using Util.Datas.Ef.Internal; -using Util.Datas.UnitOfWorks; - -namespace Util.Datas.Ef.MySql { - /// - /// MySql工作单元 - /// - public abstract class UnitOfWork: UnitOfWorkBase { - /// - /// 初始化MySql工作单元 - /// - /// 配置 - /// 工作单元服务 - protected UnitOfWork( DbContextOptions options, IUnitOfWorkManager manager ) - : base( options, manager ) { - } - - /// - /// 获取映射类型列表 - /// - /// 程序集 - protected override IEnumerable GetMapTypes( Assembly assembly ) { - return Util.Helpers.Reflection.GetTypesByInterface( assembly ); - } - - /// - /// 拦截添加操作 - /// - protected override void InterceptAddedOperation( EntityEntry entry ) { - base.InterceptAddedOperation( entry ); - Helper.InitVersion( entry ); - } - - /// - /// 拦截修改操作 - /// - protected override void InterceptModifiedOperation( EntityEntry entry ) { - base.InterceptModifiedOperation( entry ); - Helper.InitVersion( entry ); - } - } -} diff --git a/src/Util.Datas/Ef/PgSql/AggregateRootMap.cs b/src/Util.Datas/Ef/PgSql/AggregateRootMap.cs deleted file mode 100644 index 4f439204b..000000000 --- a/src/Util.Datas/Ef/PgSql/AggregateRootMap.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Util.Datas.Ef.Core; -using Util.Domains; - -namespace Util.Datas.Ef.PgSql { - /// - /// 聚合根映射配置 - /// - /// 实体类型 - public abstract class AggregateRootMap : MapBase, IMap where TEntity : class, IVersion { - /// - /// 映射乐观离线锁 - /// - protected override void MapVersion( EntityTypeBuilder builder ) { - builder.Property( t => t.Version ).IsConcurrencyToken(); - } - } -} \ No newline at end of file diff --git a/src/Util.Datas/Ef/PgSql/EntityMap.cs b/src/Util.Datas/Ef/PgSql/EntityMap.cs deleted file mode 100644 index a2bb0f4d3..000000000 --- a/src/Util.Datas/Ef/PgSql/EntityMap.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Util.Datas.Ef.Core; - -namespace Util.Datas.Ef.PgSql { - /// - /// 实体映射配置 - /// - /// 实体类型 - public abstract class EntityMap : MapBase, IMap where TEntity : class { - } -} diff --git a/src/Util.Datas/Ef/PgSql/IMap.cs b/src/Util.Datas/Ef/PgSql/IMap.cs deleted file mode 100644 index ee601fc65..000000000 --- a/src/Util.Datas/Ef/PgSql/IMap.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Datas.Ef.PgSql { - /// - /// 映射 - /// - public interface IMap : Util.Datas.Ef.Core.IMap { - } -} \ No newline at end of file diff --git a/src/Util.Datas/Ef/PgSql/UnitOfWork.cs b/src/Util.Datas/Ef/PgSql/UnitOfWork.cs deleted file mode 100644 index 2120e153b..000000000 --- a/src/Util.Datas/Ef/PgSql/UnitOfWork.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Collections.Generic; -using System.Reflection; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.ChangeTracking; -using Util.Datas.Ef.Core; -using Util.Datas.Ef.Internal; -using Util.Datas.UnitOfWorks; - -namespace Util.Datas.Ef.PgSql { - /// - /// PgSql工作单元 - /// - public abstract class UnitOfWork : UnitOfWorkBase { - /// - /// 初始化PgSql工作单元 - /// - /// 配置 - /// 工作单元服务 - protected UnitOfWork( DbContextOptions options, IUnitOfWorkManager manager ) - : base( options, manager ) { - } - - /// - /// 获取映射类型列表 - /// - /// 程序集 - protected override IEnumerable GetMapTypes( Assembly assembly ) { - return Util.Helpers.Reflection.GetTypesByInterface( assembly ); - } - - /// - /// 拦截添加操作 - /// - protected override void InterceptAddedOperation( EntityEntry entry ) { - base.InterceptAddedOperation( entry ); - Helper.InitVersion( entry ); - } - - /// - /// 拦截修改操作 - /// - protected override void InterceptModifiedOperation( EntityEntry entry ) { - base.InterceptModifiedOperation( entry ); - Helper.InitVersion( entry ); - } - } -} diff --git a/src/Util.Datas/Ef/SqlServer/AggregateRootMap.cs b/src/Util.Datas/Ef/SqlServer/AggregateRootMap.cs deleted file mode 100644 index 3d0ae53f1..000000000 --- a/src/Util.Datas/Ef/SqlServer/AggregateRootMap.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Util.Datas.Ef.Core; -using Util.Domains; - -namespace Util.Datas.Ef.SqlServer { - /// - /// 聚合根映射配置 - /// - /// 实体类型 - public abstract class AggregateRootMap : MapBase, IMap where TEntity : class, IVersion { - /// - /// 映射乐观离线锁 - /// - protected override void MapVersion( EntityTypeBuilder builder ) { - builder.Property( t => t.Version ).IsRowVersion(); - } - } -} \ No newline at end of file diff --git a/src/Util.Datas/Ef/SqlServer/EntityMap.cs b/src/Util.Datas/Ef/SqlServer/EntityMap.cs deleted file mode 100644 index 4da8218c2..000000000 --- a/src/Util.Datas/Ef/SqlServer/EntityMap.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Util.Datas.Ef.Core; - -namespace Util.Datas.Ef.SqlServer { - /// - /// 实体映射配置 - /// - /// 实体类型 - public abstract class EntityMap : MapBase, IMap where TEntity : class { - } -} diff --git a/src/Util.Datas/Ef/SqlServer/IMap.cs b/src/Util.Datas/Ef/SqlServer/IMap.cs deleted file mode 100644 index f9794c6b4..000000000 --- a/src/Util.Datas/Ef/SqlServer/IMap.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Datas.Ef.SqlServer { - /// - /// 映射 - /// - public interface IMap : Util.Datas.Ef.Core.IMap { - } -} \ No newline at end of file diff --git a/src/Util.Datas/Ef/SqlServer/UnitOfWork.cs b/src/Util.Datas/Ef/SqlServer/UnitOfWork.cs deleted file mode 100644 index 42a4c0c6d..000000000 --- a/src/Util.Datas/Ef/SqlServer/UnitOfWork.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Collections.Generic; -using System.Reflection; -using Microsoft.EntityFrameworkCore; -using Util.Datas.Ef.Core; -using Util.Datas.UnitOfWorks; - -namespace Util.Datas.Ef.SqlServer { - /// - /// SqlServer工作单元 - /// - public abstract class UnitOfWork : UnitOfWorkBase { - /// - /// 初始化SqlServer工作单元 - /// - /// 配置 - /// 工作单元服务 - protected UnitOfWork( DbContextOptions options, IUnitOfWorkManager manager ) - : base( options, manager ) { - } - - /// - /// 获取映射类型列表 - /// - /// 程序集 - protected override IEnumerable GetMapTypes( Assembly assembly ) { - return Util.Helpers.Reflection.GetTypesByInterface( assembly ); - } - } -} diff --git a/src/Util.Domain.Biz/02-Util.Domain.Biz.csproj b/src/Util.Domain.Biz/02-Util.Domain.Biz.csproj new file mode 100644 index 000000000..c34b7a73d --- /dev/null +++ b/src/Util.Domain.Biz/02-Util.Domain.Biz.csproj @@ -0,0 +1,33 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Domain.Biz + Util.Domain.Biz + Util.Domain.Biz是Util应用框架领域层基础业务库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Domain.Biz.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Domain.Biz.xml + + + + + True + False + + + + + + + + + diff --git a/src/Util.Domain.Biz/Enums/Gender.cs b/src/Util.Domain.Biz/Enums/Gender.cs new file mode 100644 index 000000000..c84e05170 --- /dev/null +++ b/src/Util.Domain.Biz/Enums/Gender.cs @@ -0,0 +1,17 @@ +namespace Util.Domain.Biz.Enums; + +/// +/// 性别 +/// +public enum Gender { + /// + /// 女 + /// + [Description( "util.female" )] + Female = 1, + /// + /// 男 + /// + [Description( "util.male" )] + Male = 2 +} \ No newline at end of file diff --git a/src/Util.Domain.Biz/Enums/Nation.cs b/src/Util.Domain.Biz/Enums/Nation.cs new file mode 100644 index 000000000..07c95860e --- /dev/null +++ b/src/Util.Domain.Biz/Enums/Nation.cs @@ -0,0 +1,287 @@ +namespace Util.Domain.Biz.Enums; + +/// +/// 民族 +/// +public enum Nation { + /// + /// 汉族 + /// + [Description( "汉族" )] + Hz = 0, + /// + /// 蒙古族 + /// + [Description( "蒙古族" )] + Mgz = 1, + /// + /// 回族 + /// + [Description( "回族" )] + HuiZ = 2, + /// + /// 藏族 + /// + [Description( "藏族" )] + Zz = 3, + /// + /// 维吾尔族 + /// + [Description( "维吾尔族" )] + Wwez = 4, + /// + /// 苗族 + /// + [Description( "苗族" )] + Mz = 5, + /// + /// 彝族 + /// + [Description( "彝族" )] + Yz = 6, + /// + /// 壮族 + /// + [Description( "壮族" )] + ZhuangZ = 7, + /// + /// 布依族 + /// + [Description( "布依族" )] + Byz = 8, + /// + /// 朝鲜族 + /// + [Description( "朝鲜族" )] + Cxz = 9, + /// + /// 满族 + /// + [Description( "满族" )] + ManZ = 10, + /// + /// 侗族 + /// + [Description( "侗族" )] + Tz = 11, + /// + /// 瑶族 + /// + [Description( "瑶族" )] + YaoZ = 12, + /// + /// 白族 + /// + [Description( "白族" )] + Bz = 13,//baizu + /// + /// 土家族 + /// + [Description( "土家族" )] + Tjz = 14, + /// + /// 哈尼族 + /// + [Description( "哈尼族" )] + Hnz = 15, + /// + /// 哈萨克族 + /// + [Description( "哈萨克族" )] + Hskz = 16, + /// + /// 傣族 + /// + [Description( "傣族" )] + Dz = 17, + /// + /// 黎族 + /// + [Description( "黎族" )] + Lz = 18, + /// + /// 傈僳族 + /// + [Description( "傈僳族" )] + Lsz = 19, + /// + /// 佤族 + /// + [Description( "佤族" )] + Wz = 20, + /// + /// 畲族 + /// + [Description( "畲族" )] + Sz = 21, + /// + /// 高山族 + /// + [Description( "高山族" )] + Gsz = 22, + /// + /// 拉祜族 + /// + [Description( "拉祜族" )] + Lhz = 23, + /// + /// 水族 + /// + [Description( "水族" )] + ShuiZ = 24, + /// + /// 东乡族 + /// + [Description( "东乡族" )] + Dxz = 25, + /// + /// 纳西族 + /// + [Description( "纳西族" )] + Nxz = 26, + /// + /// 景颇族 + /// + [Description( "景颇族" )] + Jpz = 27, + /// + /// 柯尔克孜族 + /// + [Description( "柯尔克孜族" )] + Kekzz = 28, + /// + /// 土族 + /// + [Description( "土族" )] + TuZ = 29, + /// + /// 达斡尔族 + /// + [Description( "达斡尔族" )] + Dwez = 30, + /// + /// 仫佬族 + /// + [Description( "仫佬族" )] + Mlz = 31, + /// + /// 羌族 + /// + [Description( "羌族" )] + Qz = 32, + /// + /// 布朗族 + /// + [Description( "布朗族" )] + Blz = 33, + /// + /// 撒拉族 + /// + [Description( "撒拉族" )] + Slz = 34, + /// + /// 毛南族 + /// + [Description( "毛南族" )] + Mnz = 35, + /// + /// 仡佬族 + /// + [Description( "仡佬族" )] + Ylz = 36, + /// + /// 锡伯族 + /// + [Description( "锡伯族" )] + Xbz = 37, + /// + /// 阿昌族 + /// + [Description( "阿昌族" )] + Acz = 38, + /// + /// 普米族 + /// + [Description( "普米族" )] + Pmz = 39, + /// + /// 塔吉克族 + /// + [Description( "塔吉克族" )] + Tjkz = 40, + /// + /// 怒族 + /// + [Description( "怒族" )] + Nz = 41, + /// + /// 乌孜别克族 + /// + [Description( "乌孜别克族" )] + Wzbkz = 42, + /// + /// 俄罗斯族 + /// + [Description( "俄罗斯族" )] + Elsz = 43, + /// + /// 鄂温克族 + /// + [Description( "鄂温克族" )] + Ewkz = 44, + /// + /// 德昂族 + /// + [Description( "德昂族" )] + Daz = 45, + /// + /// 保安族 + /// + [Description( "保安族" )] + Baz = 46, + /// + /// 裕固族 + /// + [Description( "裕固族" )] + Ygz = 47, + /// + /// 京族 + /// + [Description( "京族" )] + Jz = 48, + /// + /// 塔塔尔族 + /// + [Description( "塔塔尔族" )] + Ttrz = 49, + /// + /// 独龙族 + /// + [Description( "独龙族" )] + Dlz = 50, + /// + /// 鄂伦春族 + /// + [Description( "鄂伦春族" )] + Elcz = 51, + /// + /// 赫哲族 + /// + [Description( "赫哲族" )] + Hzz = 52, + /// + /// 门巴族 + /// + [Description( "门巴族" )] + Mbz = 53, + /// + /// 珞巴族 + /// + [Description( "珞巴族" )] + Lbz = 54, + /// + /// 基诺族 + /// + [Description( "基诺族" )] + Jnz = 55 +} \ No newline at end of file diff --git a/src/Util.Domain.Biz/Usings.cs b/src/Util.Domain.Biz/Usings.cs new file mode 100644 index 000000000..5558ed8ff --- /dev/null +++ b/src/Util.Domain.Biz/Usings.cs @@ -0,0 +1 @@ +global using System.ComponentModel; diff --git a/src/Util.Domain/01-Util.Domain.csproj b/src/Util.Domain/01-Util.Domain.csproj new file mode 100644 index 000000000..f614fa6a4 --- /dev/null +++ b/src/Util.Domain/01-Util.Domain.csproj @@ -0,0 +1,51 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Domain + Util.Domain + Util.Domain是Util应用框架领域层基类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Domain.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Domain.xml + + + + + True + False + + + + + + + + + + + + + + True + True + DomainResource.resx + + + + + + PublicResXFileCodeGenerator + DomainResource.Designer.cs + + + + diff --git a/src/Util.Domain/Auditing/CreationAuditedSetter.cs b/src/Util.Domain/Auditing/CreationAuditedSetter.cs new file mode 100644 index 000000000..2ce4078db --- /dev/null +++ b/src/Util.Domain/Auditing/CreationAuditedSetter.cs @@ -0,0 +1,101 @@ +using Util.Helpers; + +namespace Util.Domain.Auditing; + +/// +/// 创建操作审计设置器 +/// +public class CreationAuditedSetter { + /// + /// 实体 + /// + private readonly object _entity; + /// + /// 用户标识 + /// + private readonly string _userId; + + /// + /// 初始化创建操作审计设置器 + /// + /// 实体 + /// 用户标识 + private CreationAuditedSetter( object entity, string userId ) { + _entity = entity; + _userId = userId; + } + + /// + /// 设置创建审计属性 + /// + /// 实体 + /// 用户标识 + public static void Set( object entity, string userId ) { + new CreationAuditedSetter( entity, userId ).Init(); + } + + /// + /// 初始化 + /// + public void Init() { + if ( _entity == null ) + return; + InitCreationTime(); + if ( _userId.IsEmpty() ) + return; + if ( _entity is ICreationAudited entity ) { + if ( IsEmpty( entity.CreatorId ) ) + entity.CreatorId = _userId.ToGuid(); + return; + } + if ( _entity is ICreationAudited entity2 ) { + if ( IsEmpty( entity2.CreatorId ) ) + entity2.CreatorId = _userId.ToGuidOrNull(); + return; + } + if ( _entity is ICreationAudited entity3 ) { + if ( IsEmpty( entity3.CreatorId ) ) + entity3.CreatorId = _userId.ToInt(); + return; + } + if ( _entity is ICreationAudited entity4 ) { + if ( IsEmpty( entity4.CreatorId ) ) + entity4.CreatorId = _userId.ToIntOrNull(); + return; + } + if ( _entity is ICreationAudited entity5 ) { + if ( IsEmpty( entity5.CreatorId ) ) + entity5.CreatorId = _userId.SafeString(); + return; + } + if ( _entity is ICreationAudited entity6 ) { + if ( IsEmpty( entity6.CreatorId ) ) + entity6.CreatorId = _userId.ToLong(); + return; + } + if ( _entity is ICreationAudited entity7 ) { + if ( IsEmpty( entity7.CreatorId ) ) + entity7.CreatorId = _userId.ToLongOrNull(); + return; + } + } + + /// + /// 初始化创建时间 + /// + private void InitCreationTime() { + if ( _entity is ICreationTime entity ) + entity.CreationTime ??= Time.Now; + } + + /// + /// 创建时间是否为空 + /// + private bool IsEmpty( T creatorId ) { + if ( creatorId == null ) + return true; + if ( creatorId.Equals( default(T) ) ) + return true; + return false; + } +} \ No newline at end of file diff --git a/src/Util.Domain/Auditing/IAudited.cs b/src/Util.Domain/Auditing/IAudited.cs new file mode 100644 index 000000000..9e0688e65 --- /dev/null +++ b/src/Util.Domain/Auditing/IAudited.cs @@ -0,0 +1,14 @@ +namespace Util.Domain.Auditing; + +/// +/// 操作审计 +/// +public interface IAudited : ICreationAudited, IModificationAudited { +} + +/// +/// 操作审计 +/// +/// 操作人标识类型 +public interface IAudited : ICreationAudited, IModificationAudited { +} \ No newline at end of file diff --git a/src/Util.Domain/Auditing/ICreationAudited.cs b/src/Util.Domain/Auditing/ICreationAudited.cs new file mode 100644 index 000000000..58838bfc0 --- /dev/null +++ b/src/Util.Domain/Auditing/ICreationAudited.cs @@ -0,0 +1,18 @@ +namespace Util.Domain.Auditing; + +/// +/// 创建操作审计 +/// +public interface ICreationAudited : ICreationAudited { +} + +/// +/// 创建操作审计 +/// +/// 创建人标识类型 +public interface ICreationAudited : ICreationTime { + /// + /// 创建人标识 + /// + TKey CreatorId { get; set; } +} \ No newline at end of file diff --git a/src/Util.Domain/Auditing/ICreationTime.cs b/src/Util.Domain/Auditing/ICreationTime.cs new file mode 100644 index 000000000..8879238d1 --- /dev/null +++ b/src/Util.Domain/Auditing/ICreationTime.cs @@ -0,0 +1,11 @@ +namespace Util.Domain.Auditing; + +/// +/// 创建时间审计 +/// +public interface ICreationTime { + /// + /// 创建时间 + /// + DateTime? CreationTime { get; set; } +} \ No newline at end of file diff --git a/src/Util.Domain/Auditing/ILastModificationTime.cs b/src/Util.Domain/Auditing/ILastModificationTime.cs new file mode 100644 index 000000000..e8ff01d88 --- /dev/null +++ b/src/Util.Domain/Auditing/ILastModificationTime.cs @@ -0,0 +1,11 @@ +namespace Util.Domain.Auditing; + +/// +/// 修改时间审计 +/// +public interface ILastModificationTime { + /// + /// 最后修改时间 + /// + DateTime? LastModificationTime { get; set; } +} \ No newline at end of file diff --git a/src/Util.Domain/Auditing/IModificationAudited.cs b/src/Util.Domain/Auditing/IModificationAudited.cs new file mode 100644 index 000000000..910df33a4 --- /dev/null +++ b/src/Util.Domain/Auditing/IModificationAudited.cs @@ -0,0 +1,18 @@ +namespace Util.Domain.Auditing; + +/// +/// 修改操作审计 +/// +public interface IModificationAudited : IModificationAudited { +} + +/// +/// 修改操作审计 +/// +/// 最后修改人标识类型 +public interface IModificationAudited : ILastModificationTime { + /// + /// 最后修改人标识 + /// + TKey LastModifierId { get; set; } +} \ No newline at end of file diff --git a/src/Util.Domain/Auditing/ModificationAuditedSetter.cs b/src/Util.Domain/Auditing/ModificationAuditedSetter.cs new file mode 100644 index 000000000..ab13e822e --- /dev/null +++ b/src/Util.Domain/Auditing/ModificationAuditedSetter.cs @@ -0,0 +1,83 @@ +using Util.Helpers; + +namespace Util.Domain.Auditing; + +/// +/// 修改操作审计设置器 +/// +public class ModificationAuditedSetter { + /// + /// 实体 + /// + private readonly object _entity; + /// + /// 用户标识 + /// + private readonly string _userId; + + /// + /// 初始化修改操作审计设置器 + /// + /// 实体 + /// 用户标识 + private ModificationAuditedSetter( object entity, string userId ) { + _entity = entity; + _userId = userId; + } + + /// + /// 设置修改审计属性 + /// + /// 实体 + /// 用户标识 + public static void Set( object entity, string userId ) { + new ModificationAuditedSetter( entity, userId ).Init(); + } + + /// + /// 初始化 + /// + public void Init() { + if ( _entity == null ) + return; + InitLastModificationTime(); + if ( _userId.IsEmpty() ) + return; + if ( _entity is IModificationAudited entity ) { + entity.LastModifierId = _userId.ToGuid(); + return; + } + if ( _entity is IModificationAudited entity2 ) { + entity2.LastModifierId = _userId.ToGuidOrNull(); + return; + } + if ( _entity is IModificationAudited entity3 ) { + entity3.LastModifierId = _userId.ToInt(); + return; + } + if ( _entity is IModificationAudited entity4 ) { + entity4.LastModifierId = _userId.ToIntOrNull(); + return; + } + if ( _entity is IModificationAudited entity5 ) { + entity5.LastModifierId = _userId.SafeString(); + return; + } + if ( _entity is IModificationAudited entity6 ) { + entity6.LastModifierId = _userId.ToLong(); + return; + } + if ( _entity is IModificationAudited entity7 ) { + entity7.LastModifierId = _userId.ToLongOrNull(); + return; + } + } + + /// + /// 初始化最后修改时间 + /// + private void InitLastModificationTime() { + if ( _entity is ILastModificationTime entity ) + entity.LastModificationTime = Time.Now; + } +} \ No newline at end of file diff --git a/src/Util.Domain/Compare/ChangeValue.cs b/src/Util.Domain/Compare/ChangeValue.cs new file mode 100644 index 000000000..806451abf --- /dev/null +++ b/src/Util.Domain/Compare/ChangeValue.cs @@ -0,0 +1,46 @@ +using Util.Domain.Properties; + +namespace Util.Domain.Compare; + +/// +/// 变更值 +/// +public class ChangeValue { + /// + /// 初始化变更值 + /// + /// 属性名 + /// 描述 + /// 原始值 + /// 新值 + public ChangeValue( string propertyName, string description, string originalValue, string newValue ) { + PropertyName = propertyName; + Description = description; + OriginalValue = originalValue; + NewValue = newValue; + } + + /// + /// 属性名 + /// + public string PropertyName { get; } + /// + /// 描述 + /// + public string Description { get; } + /// + /// 原始值 + /// + public string OriginalValue { get; } + /// + /// 新值 + /// + public string NewValue { get; } + + /// + /// 输出变更信息 + /// + public override string ToString() { + return string.Format( DomainResource.ChangeValueToString, PropertyName, Description, OriginalValue, NewValue ); + } +} \ No newline at end of file diff --git a/src/Util.Domain/Compare/ChangeValueCollection.cs b/src/Util.Domain/Compare/ChangeValueCollection.cs new file mode 100644 index 000000000..959382042 --- /dev/null +++ b/src/Util.Domain/Compare/ChangeValueCollection.cs @@ -0,0 +1,59 @@ +namespace Util.Domain.Compare; + +/// +/// 变更值集合 +/// +public class ChangeValueCollection : List { + /// + /// 初始化变更值集合 + /// + /// 类型名称 + /// 类型描述 + /// 标识 + public ChangeValueCollection( string typeName = null,string typeDescription = null, string id = null ) { + TypeName = typeName; + TypeDescription = typeDescription; + Id = id; + } + + /// + /// 类型名称 + /// + public string TypeName { get; } + + /// + /// 类型描述 + /// + public string TypeDescription { get; } + + /// + /// 标识 + /// + public string Id { get; } + + /// + /// 添加 + /// + /// 属性名 + /// 描述 + /// 原始值 + /// 新值 + public void Add( string propertyName, string description, string originalValue, string newValue ) { + if( string.IsNullOrWhiteSpace( propertyName ) ) + return; + Add( new ChangeValue( propertyName, description, originalValue, newValue ) ); + } + + /// + /// 输出变更信息 + /// + public override string ToString() { + var result = new StringBuilder(); + result.Append( $"{TypeName}({TypeDescription})," ); + if ( Id.IsEmpty() == false ) + result.Append( $"Id: {Id}," ); + foreach ( var item in this ) + result.Append( $"{item}," ); + return result.ToString().TrimEnd( ',' ); + } +} \ No newline at end of file diff --git a/src/Util.Domain/Compare/ICompareChange.cs b/src/Util.Domain/Compare/ICompareChange.cs new file mode 100644 index 000000000..e7d19a417 --- /dev/null +++ b/src/Util.Domain/Compare/ICompareChange.cs @@ -0,0 +1,15 @@ +using Util.Domain.Entities; + +namespace Util.Domain.Compare; + +/// +/// 通过对象比较获取变更属性集 +/// +/// 领域对象类型 +public interface ICompareChange where T : IDomainObject { + /// + /// 获取变更属性 + /// + /// 其它领域对象 + ChangeValueCollection GetChanges( T other ); +} \ No newline at end of file diff --git a/src/Util.Domain/Compare/KeyListComparator.cs b/src/Util.Domain/Compare/KeyListComparator.cs new file mode 100644 index 000000000..3cca6c044 --- /dev/null +++ b/src/Util.Domain/Compare/KeyListComparator.cs @@ -0,0 +1,46 @@ +namespace Util.Domain.Compare; + +/// +/// 键列表比较器 +/// +/// 标识类型 +public class KeyListComparator { + /// + /// 比较 + /// + /// 新实体集合 + /// 旧实体集合 + public KeyListCompareResult Compare( IEnumerable newList, IEnumerable originalList ) { + newList.CheckNull( nameof(newList) ); + originalList.CheckNull( nameof( originalList ) ); + var newEntities = newList.ToList(); + var originalEntities = originalList.ToList(); + var createList = GetCreateList( newEntities, originalEntities ); + var updateList = GetUpdateList( newEntities, originalEntities ); + var deleteList = GetDeleteList( newEntities, originalEntities ); + return new KeyListCompareResult( createList, updateList, deleteList ); + } + + /// + /// 获取创建列表 + /// + private List GetCreateList( List newList, List originalList ) { + var result = newList.Except( originalList ); + return result.ToList(); + } + + /// + /// 获取更新列表 + /// + private List GetUpdateList( List newList, List originalList ) { + return newList.FindAll( id => originalList.Exists( t => t.Equals( id ) ) ); + } + + /// + /// 获取删除列表 + /// + private List GetDeleteList( List newList, List originalList ) { + var result = originalList.Except( newList ); + return result.ToList(); + } +} \ No newline at end of file diff --git a/src/Util.Domain/Compare/KeyListCompareResult.cs b/src/Util.Domain/Compare/KeyListCompareResult.cs new file mode 100644 index 000000000..3b8e5a4e0 --- /dev/null +++ b/src/Util.Domain/Compare/KeyListCompareResult.cs @@ -0,0 +1,33 @@ +namespace Util.Domain.Compare; + +/// +/// 键列表比较结果 +/// +public class KeyListCompareResult { + /// + /// 初始化键列表比较结果 + /// + /// 创建列表 + /// 更新列表 + /// 删除列表 + public KeyListCompareResult( List createList, List updateList, List deleteList ) { + CreateList = createList; + UpdateList = updateList; + DeleteList = deleteList; + } + + /// + /// 创建列表 + /// + public List CreateList { get; } + + /// + /// 更新列表 + /// + public List UpdateList { get; } + + /// + /// 删除列表 + /// + public List DeleteList { get; } +} \ No newline at end of file diff --git a/src/Util.Domain/Compare/ListComparator.cs b/src/Util.Domain/Compare/ListComparator.cs new file mode 100644 index 000000000..ae7b2da70 --- /dev/null +++ b/src/Util.Domain/Compare/ListComparator.cs @@ -0,0 +1,47 @@ +namespace Util.Domain.Compare; + +/// +/// 实体列表比较器 +/// +/// 实体类型 +/// 标识类型 +public class ListComparator where TEntity : IKey { + /// + /// 比较 + /// + /// 新实体集合 + /// 旧实体集合 + public ListCompareResult Compare( IEnumerable newList, IEnumerable originalList ) { + newList.CheckNull( nameof( newList ) ); + originalList.CheckNull( nameof( originalList ) ); + var newEntities = newList.ToList(); + var originalEntities = originalList.ToList(); + var createList = GetCreateList( newEntities, originalEntities ); + var updateList = GetUpdateList( newEntities, originalEntities ); + var deleteList = GetDeleteList( newEntities, originalEntities ); + return new ListCompareResult( createList, updateList, deleteList ); + } + + /// + /// 获取创建列表 + /// + private List GetCreateList( List newList, List originalList ) { + var result = newList.Except( originalList ); + return result.ToList(); + } + + /// + /// 获取更新列表 + /// + private List GetUpdateList( List newList, List originalList ) { + return newList.FindAll( entity => originalList.Exists( t => t.Id.Equals( entity.Id ) ) ); + } + + /// + /// 获取删除列表 + /// + private List GetDeleteList( List newList, List originalList ) { + var result = originalList.Except( newList ); + return result.ToList(); + } +} \ No newline at end of file diff --git a/src/Util.Domain/Compare/ListCompareResult.cs b/src/Util.Domain/Compare/ListCompareResult.cs new file mode 100644 index 000000000..0fd676c93 --- /dev/null +++ b/src/Util.Domain/Compare/ListCompareResult.cs @@ -0,0 +1,33 @@ +namespace Util.Domain.Compare; + +/// +/// 列表比较结果 +/// +public class ListCompareResult where TEntity : IKey { + /// + /// 初始化列表比较结果 + /// + /// 创建列表 + /// 更新列表 + /// 删除列表 + public ListCompareResult( List createList, List updateList, List deleteList ) { + CreateList = createList; + UpdateList = updateList; + DeleteList = deleteList; + } + + /// + /// 创建列表 + /// + public List CreateList { get; } + + /// + /// 更新列表 + /// + public List UpdateList { get; } + + /// + /// 删除列表 + /// + public List DeleteList { get; } +} \ No newline at end of file diff --git a/src/Util.Domain/Entities/AggregateRoot.cs b/src/Util.Domain/Entities/AggregateRoot.cs new file mode 100644 index 000000000..63758ff36 --- /dev/null +++ b/src/Util.Domain/Entities/AggregateRoot.cs @@ -0,0 +1,64 @@ +using Util.Domain.Events; + +namespace Util.Domain.Entities; + +/// +/// 聚合根 +/// +/// 实体类型 +public abstract class AggregateRoot : AggregateRoot where TEntity : IAggregateRoot { + /// + /// 初始化聚合根 + /// + /// 标识 + protected AggregateRoot( Guid id ) : base( id ) { + } +} + +/// +/// 聚合根 +/// +/// 实体类型 +/// 标识类型 +public abstract class AggregateRoot : EntityBase, IAggregateRoot, IDomainEventManager where TEntity : IAggregateRoot { + /// + /// 领域事件列表 + /// + private List _domainEvents; + + /// + /// 初始化聚合根 + /// + /// 标识 + protected AggregateRoot( TKey id ) : base( id ) { + } + + /// + /// 版本号 + /// + public byte[] Version { get; set; } + + /// + [NotMapped] + public IReadOnlyCollection DomainEvents => _domainEvents?.AsReadOnly(); + + /// + public void AddDomainEvent( IEvent @event ) { + if ( @event == null ) + return; + _domainEvents ??= new List(); + _domainEvents.Add( @event ); + } + + /// + public void RemoveDomainEvent( IEvent @event ) { + if ( @event == null ) + return; + _domainEvents?.Remove( @event ); + } + + /// + public void ClearDomainEvents() { + _domainEvents?.Clear(); + } +} \ No newline at end of file diff --git a/src/Util.Domain/Entities/DomainObjectBase.cs b/src/Util.Domain/Entities/DomainObjectBase.cs new file mode 100644 index 000000000..6d6e3fb7f --- /dev/null +++ b/src/Util.Domain/Entities/DomainObjectBase.cs @@ -0,0 +1,203 @@ +using Util.Domain.Compare; +using Util.Validation; + +namespace Util.Domain.Entities; + +/// +/// 领域对象基类 +/// +public abstract class DomainObjectBase : IDomainObject, ICompareChange where T : IDomainObject { + + #region 字段 + + /// + /// 验证规则集合 + /// + private readonly List _rules; + /// + /// 验证处理器 + /// + private IValidationHandler _handler; + /// + /// 变更值集合 + /// + private ChangeValueCollection _changeValues; + + #endregion + + #region 构造方法 + + /// + /// 初始化领域对象基类 + /// + protected DomainObjectBase() { + _rules = new List(); + _handler = new ThrowHandler(); + } + + #endregion + + #region Validate(验证) + + /// + /// 设置验证处理器 + /// + /// 验证处理器 + public void SetValidationHandler( IValidationHandler handler ) { + if ( handler == null ) + return; + _handler = handler; + } + + /// + /// 添加验证规则列表 + /// + /// 验证规则列表 + public void AddValidationRules( IEnumerable rules ) { + if ( rules == null ) + return; + foreach ( var rule in rules ) + AddValidationRule( rule ); + } + + /// + /// 添加验证规则 + /// + /// 验证规则 + public void AddValidationRule( IValidationRule rule ) { + if ( rule == null ) + return; + _rules.Add( rule ); + } + + /// + /// 验证 + /// + public virtual ValidationResultCollection Validate() { + var result = GetValidationResults(); + HandleValidationResults( result ); + return result; + } + + /// + /// 获取验证结果 + /// + private ValidationResultCollection GetValidationResults() { + var result = DataAnnotationValidation.Validate( this ); + Validate( result ); + foreach ( var rule in _rules ) + result.Add( rule.Validate() ); + return result; + } + + /// + /// 自定义验证钩子方法 + /// + /// 验证结果集合 + protected virtual void Validate( ValidationResultCollection results ) { + } + + /// + /// 处理验证结果 + /// + protected virtual void HandleValidationResults( ValidationResultCollection results ) { + if ( results.IsValid ) + return; + _handler.Handle( results ); + } + + #endregion + + #region GetChanges(获取变更属性) + + /// + /// 获取变更属性 + /// + /// 新对象 + public ChangeValueCollection GetChanges( T newEntity ) { + if ( Equals( newEntity, null ) ) + return new ChangeValueCollection(); + _changeValues = CreateChangeValueCollection( newEntity ); + AddChanges( newEntity ); + return _changeValues; + } + + /// + /// 创建变更值集合 + /// + protected virtual ChangeValueCollection CreateChangeValueCollection( T newEntity ) { + var description = Util.Helpers.Reflection.GetDisplayNameOrDescription( newEntity.GetType() ); + return new ChangeValueCollection( newEntity.GetType().ToString(), description ); + } + + /// + /// 添加变更列表 + /// + /// 新对象 + protected virtual void AddChanges( T newEntity ) { + } + + /// + /// 添加变更 + /// + /// 属性表达式,范例:t => t.Name + /// 新值,范例:newEntity.Name + protected void AddChange( Expression> expression, TValue newValue ) { + var member = Util.Helpers.Lambda.GetMemberExpression( expression ); + var name = Util.Helpers.Lambda.GetMemberName( member ); + var description = Util.Helpers.Reflection.GetDisplayNameOrDescription( member.Member ); + var value = member.Member.GetPropertyValue( this ); + AddChange( name, description, Util.Helpers.Convert.To( value ), newValue ); + } + + /// + /// 添加变更 + /// + /// 属性名 + /// 描述 + /// 旧值,范例:this.Name + /// 新值,范例:newEntity.Name + protected void AddChange( string propertyName, string description, TValue oldValue, TValue newValue ) { + if ( Equals( oldValue, newValue ) ) + return; + string oldValueString = oldValue.SafeString().ToLower().Trim(); + string newValueString = newValue.SafeString().ToLower().Trim(); + if ( oldValueString == newValueString ) + return; + _changeValues.Add( propertyName, description, oldValueString, newValueString ); + } + + /// + /// 添加变更 + /// + /// 旧对象 + /// 新对象 + protected void AddChange( ICompareChange oldObject, TDomainObject newObject ) where TDomainObject : IDomainObject { + if ( Equals( oldObject, null ) ) + return; + if ( Equals( newObject, null ) ) + return; + _changeValues.AddRange( oldObject.GetChanges( newObject ) ); + } + + /// + /// 添加变更 + /// + /// 旧对象列表 + /// 新对象列表 + protected void AddChange( IEnumerable> oldObjects, IEnumerable newObjects ) where TDomainObject : IDomainObject { + if ( Equals( oldObjects, null ) ) + return; + if ( Equals( newObjects, null ) ) + return; + var oldList = oldObjects.ToList(); + var newList = newObjects.ToList(); + for ( int i = 0; i < oldList.Count; i++ ) { + if ( newList.Count <= i ) + return; + AddChange( oldList[i], newList[i] ); + } + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Domain/Entities/EntityBase.cs b/src/Util.Domain/Entities/EntityBase.cs new file mode 100644 index 000000000..524d8cd48 --- /dev/null +++ b/src/Util.Domain/Entities/EntityBase.cs @@ -0,0 +1,138 @@ +using Util.Domain.Compare; +using Util.Domain.Extending; +using Util.Properties; +using Util.Validation; + +namespace Util.Domain.Entities; + +/// +/// 领域实体 +/// +/// 实体类型 +public abstract class EntityBase : EntityBase where TEntity : IEntity { + /// + /// 初始化领域实体 + /// + /// 标识 + protected EntityBase( Guid id ) : base( id ){ + } +} + +/// +/// 领域实体 +/// +/// 实体类型 +/// 标识类型 +public abstract class EntityBase : DomainObjectBase, IEntity where TEntity : IEntity { + /// + /// 初始化领域实体 + /// + /// 标识 + protected EntityBase( TKey id ) { + Id = id; + ExtraProperties = new ExtraPropertyDictionary(); + } + + /// + /// 标识 + /// + [Key] + public TKey Id { get; private set; } + + /// + /// 扩展属性集合 + /// + protected ExtraPropertyDictionary ExtraProperties { get; set; } + + /// + /// 相等运算 + /// + public override bool Equals( object other ) { + return this == ( other as EntityBase ); + } + + /// + /// 获取哈希 + /// + public override int GetHashCode() { + return ReferenceEquals( Id, null ) ? 0 : Id.GetHashCode(); + } + + /// + /// 相等比较 + /// + public static bool operator ==( EntityBase left, EntityBase right ) { + if( (object)left == null && (object)right == null ) + return true; + if( !( left is TEntity ) || !( right is TEntity ) ) + return false; + if( Equals( left.Id, null ) ) + return false; + if( left.Id.Equals( default( TKey ) ) ) + return false; + return left.Id.Equals( right.Id ); + } + + /// + /// 不相等比较 + /// + public static bool operator !=( EntityBase left, EntityBase right ) { + return !( left == right ); + } + + /// + /// 创建变更值集合 + /// + protected override ChangeValueCollection CreateChangeValueCollection( TEntity newEntity ) { + var description = Util.Helpers.Reflection.GetDisplayNameOrDescription( newEntity.GetType() ); + return new ChangeValueCollection( newEntity.GetType().ToString(), description,newEntity.Id.SafeString() ); + } + + /// + /// 初始化 + /// + public virtual void Init() { + InitId(); + } + + /// + /// 初始化标识 + /// + protected virtual void InitId() { + if( typeof( TKey ) == typeof( int ) || typeof( TKey ) == typeof( long ) ) + return; + if( string.IsNullOrWhiteSpace( Id.SafeString() ) || Id.Equals( default( TKey ) ) ) + Id = CreateId(); + } + + /// + /// 创建标识 + /// + protected virtual TKey CreateId() { + return Util.Helpers.Convert.To( Guid.NewGuid() ); + } + + /// + /// 验证 + /// + protected override void Validate( ValidationResultCollection results ) { + ValidateId( results ); + } + + /// + /// 验证标识 + /// + protected virtual void ValidateId( ValidationResultCollection results ) { + if( typeof( TKey ) == typeof( int ) || typeof( TKey ) == typeof( long ) ) + return; + if( string.IsNullOrWhiteSpace( Id.SafeString() ) || Id.Equals( default( TKey ) ) ) + results.Add( new ValidationResult( R.IdIsEmpty ) ); + } + + /// + /// 克隆副本 + /// + public virtual TEntity Clone() { + return this.MapTo(); + } +} \ No newline at end of file diff --git a/src/Util.Domain/Entities/IAggregateRoot.cs b/src/Util.Domain/Entities/IAggregateRoot.cs new file mode 100644 index 000000000..293f725ee --- /dev/null +++ b/src/Util.Domain/Entities/IAggregateRoot.cs @@ -0,0 +1,22 @@ +namespace Util.Domain.Entities; + +/// +/// 聚合根 +/// +public interface IAggregateRoot : IEntity, IVersion { +} + +/// +/// 聚合根 +/// +/// 标识类型 +public interface IAggregateRoot : IEntity, IAggregateRoot { +} + +/// +/// 聚合根 +/// +/// 实体类型 +/// 标识类型 +public interface IAggregateRoot : IEntity, IAggregateRoot where TEntity : IAggregateRoot { +} \ No newline at end of file diff --git a/src/Util.Domain/Entities/IDomainObject.cs b/src/Util.Domain/Entities/IDomainObject.cs new file mode 100644 index 000000000..f911a572e --- /dev/null +++ b/src/Util.Domain/Entities/IDomainObject.cs @@ -0,0 +1,9 @@ +using Util.Validation; + +namespace Util.Domain.Entities; + +/// +/// 领域对象 +/// +public interface IDomainObject : IValidation { +} \ No newline at end of file diff --git a/src/Util.Domain/Entities/IEntity.cs b/src/Util.Domain/Entities/IEntity.cs new file mode 100644 index 000000000..fe600a6c8 --- /dev/null +++ b/src/Util.Domain/Entities/IEntity.cs @@ -0,0 +1,32 @@ +using Util.Domain.Compare; + +namespace Util.Domain.Entities; + +/// +/// 实体 +/// +public interface IEntity : IDomainObject { + /// + /// 初始化 + /// + void Init(); +} + +/// +/// 实体 +/// +/// 标识类型 +public interface IEntity : IKey, IEntity { +} + +/// +/// 实体 +/// +/// 实体类型 +/// 标识类型 +public interface IEntity : ICompareChange, IEntity where TEntity : IEntity { + /// + /// 复制实体 + /// + TEntity Clone(); +} \ No newline at end of file diff --git a/src/Util.Domain/Entities/ValueObjectBase.cs b/src/Util.Domain/Entities/ValueObjectBase.cs new file mode 100644 index 000000000..bc777708e --- /dev/null +++ b/src/Util.Domain/Entities/ValueObjectBase.cs @@ -0,0 +1,57 @@ +namespace Util.Domain.Entities; + +/// +/// 值对象 +/// +/// 值对象类型 +public abstract class ValueObjectBase : DomainObjectBase, IEquatable where TValueObject : ValueObjectBase { + /// + /// 相等性比较 + /// + public bool Equals( TValueObject other ) { + return this == other; + } + + /// + /// 相等性比较 + /// + public override bool Equals( object other ) { + return Equals( other as TValueObject ); + } + + /// + /// 相等性比较 + /// + public static bool operator ==( ValueObjectBase left, ValueObjectBase right ) { + if( (object)left == null || (object)right == null ) + return false; + if( !( left is TValueObject ) || !( right is TValueObject ) ) + return false; + var properties = left.GetType().GetTypeInfo().GetProperties(); + return properties.All( property => property.GetValue( left ) == property.GetValue( right ) ); + } + + /// + /// 不相等比较 + /// + public static bool operator !=( ValueObjectBase left, ValueObjectBase right ) { + return !( left == right ); + } + + /// + /// 获取哈希 + /// + public override int GetHashCode() { + var properties = GetType().GetTypeInfo().GetProperties(); + return properties.Select( property => property.GetValue( this ) ) + .Where( value => value != null ) + .Aggregate( 0, ( current, value ) => current ^ value.GetHashCode() ); + } + + /// + /// 克隆副本 + /// + public virtual TValueObject Clone() { + return Util.Helpers.Convert.To( MemberwiseClone() ); + } +} \ No newline at end of file diff --git a/src/Util.Domain/Events/EntityChangeType.cs b/src/Util.Domain/Events/EntityChangeType.cs new file mode 100644 index 000000000..ba7e51069 --- /dev/null +++ b/src/Util.Domain/Events/EntityChangeType.cs @@ -0,0 +1,19 @@ +namespace Util.Domain.Events; + +/// +/// 实体变更类型 +/// +public enum EntityChangeType { + /// + /// 新增 + /// + Created, + /// + /// 修改 + /// + Updated, + /// + /// 删除 + /// + Deleted +} \ No newline at end of file diff --git a/src/Util.Domain/Events/EntityChangedEvent.cs b/src/Util.Domain/Events/EntityChangedEvent.cs new file mode 100644 index 000000000..5724ae4a8 --- /dev/null +++ b/src/Util.Domain/Events/EntityChangedEvent.cs @@ -0,0 +1,36 @@ +using Util.Domain.Compare; + +namespace Util.Domain.Events; + +/// +/// 实体变更事件,当实体新增,修改,删除时均触发 +/// +/// 实体类型 +public class EntityChangedEvent : IEvent { + /// + /// 初始化实体变更事件 + /// + /// 实体 + /// 变更类型 + /// 变更值集合 + public EntityChangedEvent( TEntity entity, EntityChangeType changeType, ChangeValueCollection changeValues = null ) { + Entity = entity; + ChangeType = changeType; + ChangeValues = changeValues; + } + + /// + /// 实体 + /// + public TEntity Entity { get; } + + /// + /// 变更类型 + /// + public EntityChangeType ChangeType { get; } + + /// + /// 变更值集合 + /// + public ChangeValueCollection ChangeValues { get; } +} \ No newline at end of file diff --git a/src/Util.Domain/Events/EntityCreatedEvent.cs b/src/Util.Domain/Events/EntityCreatedEvent.cs new file mode 100644 index 000000000..2541fd3bb --- /dev/null +++ b/src/Util.Domain/Events/EntityCreatedEvent.cs @@ -0,0 +1,20 @@ +namespace Util.Domain.Events; + +/// +/// 实体创建事件 +/// +/// 实体类型 +public class EntityCreatedEvent : IEvent { + /// + /// 初始化实体创建事件 + /// + /// 实体 + public EntityCreatedEvent( TEntity entity ) { + Entity = entity; + } + + /// + /// 实体 + /// + public TEntity Entity { get; } +} \ No newline at end of file diff --git a/src/Util.Domain/Events/EntityDeletedEvent.cs b/src/Util.Domain/Events/EntityDeletedEvent.cs new file mode 100644 index 000000000..0a670bd3e --- /dev/null +++ b/src/Util.Domain/Events/EntityDeletedEvent.cs @@ -0,0 +1,20 @@ +namespace Util.Domain.Events; + +/// +/// 实体删除事件 +/// +/// 实体类型 +public class EntityDeletedEvent : IEvent { + /// + /// 初始化实体删除事件 + /// + /// 实体 + public EntityDeletedEvent( TEntity entity ) { + Entity = entity; + } + + /// + /// 实体 + /// + public TEntity Entity { get; } +} \ No newline at end of file diff --git a/src/Util.Domain/Events/EntityUpdatedEvent.cs b/src/Util.Domain/Events/EntityUpdatedEvent.cs new file mode 100644 index 000000000..b0b6b3f16 --- /dev/null +++ b/src/Util.Domain/Events/EntityUpdatedEvent.cs @@ -0,0 +1,29 @@ +using Util.Domain.Compare; + +namespace Util.Domain.Events; + +/// +/// 实体修改事件 +/// +/// 实体类型 +public class EntityUpdatedEvent : IEvent { + /// + /// 初始化实体修改事件 + /// + /// 实体 + /// 变更值集合 + public EntityUpdatedEvent( TEntity entity, ChangeValueCollection changeValues ) { + Entity = entity; + ChangeValues = changeValues; + } + + /// + /// 实体 + /// + public TEntity Entity { get; } + + /// + /// 变更值集合 + /// + public ChangeValueCollection ChangeValues { get; } +} \ No newline at end of file diff --git a/src/Util.Domain/Events/IDomainEventManager.cs b/src/Util.Domain/Events/IDomainEventManager.cs new file mode 100644 index 000000000..ad34f746e --- /dev/null +++ b/src/Util.Domain/Events/IDomainEventManager.cs @@ -0,0 +1,25 @@ +namespace Util.Domain.Events; + +/// +/// 领域事件服务 +/// +public interface IDomainEventManager { + /// + /// 领域事件集合 + /// + IReadOnlyCollection DomainEvents { get; } + /// + /// 添加领域事件 + /// + /// 领域事件 + void AddDomainEvent( IEvent @event ); + /// + /// 移除领域事件 + /// + /// 领域事件 + void RemoveDomainEvent( IEvent @event ); + /// + /// 清空领域事件 + /// + void ClearDomainEvents(); +} \ No newline at end of file diff --git a/src/Util.Domain/Extending/ExtraProperty.cs b/src/Util.Domain/Extending/ExtraProperty.cs new file mode 100644 index 000000000..f22acd0a1 --- /dev/null +++ b/src/Util.Domain/Extending/ExtraProperty.cs @@ -0,0 +1,55 @@ +namespace Util.Domain.Extending; + +/// +/// 扩展属性 +/// +/// 扩展属性类型 +public class ExtraProperty where TProperty : class { + /// + /// 扩展属性集合 + /// + private ExtraPropertyDictionary _properties; + /// + /// 属性实例 + /// + private TProperty _property; + /// + /// 属性名 + /// + private readonly string _propertyName; + + /// + /// 初始化扩展属性 + /// + /// 属性名 + public ExtraProperty( string propertyName ) { + _propertyName = propertyName; + } + + /// + /// 获取属性 + /// + /// 扩展属性集合 + public TProperty GetProperty( ExtraPropertyDictionary properties ) { + _properties = properties ?? new ExtraPropertyDictionary(); + var property = _properties.GetProperty( _propertyName ); + if ( property == null ) + return null; + if( _property == property ) + return _property; + _property = property; + _properties.SetProperty( _propertyName, _property ); + return _property; + } + + /// + /// 设置属性 + /// + /// 扩展属性集合 + /// 属性实例 + public void SetProperty( ExtraPropertyDictionary properties,TProperty property ) { + _properties = properties; + _property = property; + _properties.SetProperty( _propertyName, _property ); + } +} \ No newline at end of file diff --git a/src/Util.Domain/Extending/ExtraPropertyDictionary.cs b/src/Util.Domain/Extending/ExtraPropertyDictionary.cs new file mode 100644 index 000000000..8e8fed908 --- /dev/null +++ b/src/Util.Domain/Extending/ExtraPropertyDictionary.cs @@ -0,0 +1,24 @@ +namespace Util.Domain.Extending; + +/// +/// 扩展属性字典 +/// +public class ExtraPropertyDictionary : Dictionary { + /// + /// 初始化扩展属性字典 + /// + public ExtraPropertyDictionary() { + } + + /// + /// 初始化扩展属性字典 + /// + /// 字典 + public ExtraPropertyDictionary( IDictionary dictionary ) : base( dictionary ) { + } + + /// + /// 是否清除字符串两端的空白,默认为true + /// + public bool IsTrimString { get; set; } = true; +} \ No newline at end of file diff --git a/src/Util.Domain/Extending/IExtraProperties.cs b/src/Util.Domain/Extending/IExtraProperties.cs new file mode 100644 index 000000000..22d7c3909 --- /dev/null +++ b/src/Util.Domain/Extending/IExtraProperties.cs @@ -0,0 +1,7 @@ +namespace Util.Domain.Extending; + +/// +/// 扩展属性 +/// +public interface IExtraProperties { +} \ No newline at end of file diff --git a/src/Util.Domain/ExtraPropertyDictionaryExtensions.cs b/src/Util.Domain/ExtraPropertyDictionaryExtensions.cs new file mode 100644 index 000000000..71c114f87 --- /dev/null +++ b/src/Util.Domain/ExtraPropertyDictionaryExtensions.cs @@ -0,0 +1,62 @@ +using System.Text.Json; +using Util.Domain.Extending; +using Util.Helpers; + +namespace Util.Domain; + +/// +/// 扩展属性字典扩展 +/// +public static class ExtraPropertyDictionaryExtensions { + /// + /// 获取属性 + /// + /// 扩展属性字典 + /// 属性名 + public static TProperty GetProperty( this ExtraPropertyDictionary source, string name ) { + source.CheckNull( nameof( source ) ); + if ( source.TryGetValue( name, out var value ) == false ) + return default; + if ( value is JsonElement element ) + return Util.Helpers.Json.ToObject( Util.Helpers.Json.ToJson( element ) ); + return Util.Helpers.Convert.To( value ); + } + + /// + /// 设置属性,如果存在则先移除旧属性,当值为null时移除该属性 + /// + /// 扩展属性字典 + /// 属性名 + /// 属性值 + public static ExtraPropertyDictionary SetProperty( this ExtraPropertyDictionary source, string name, object value ) { + source.CheckNull( nameof( source ) ); + source.RemoveProperty( name ); + if ( value == null ) + return source; + source[name] = GetPropertyValue( source, value ); + return source; + } + + /// + /// 获取属性值 + /// + private static object GetPropertyValue( ExtraPropertyDictionary source, object value ) { + if ( value is string && source.IsTrimString ) + return value.SafeString(); + if ( value is DateTime dateValue ) + return Time.Normalize( dateValue ); + return value; + } + + /// + /// 移除属性 + /// + /// 扩展属性字典 + /// 属性名 + public static ExtraPropertyDictionary RemoveProperty( this ExtraPropertyDictionary source, string name ) { + source.CheckNull( nameof( source ) ); + if ( source.ContainsKey( name ) ) + source.Remove( name ); + return source; + } +} \ No newline at end of file diff --git a/src/Util.Domain/ITreeRepositoryExtensions.cs b/src/Util.Domain/ITreeRepositoryExtensions.cs new file mode 100644 index 000000000..28c71b3c4 --- /dev/null +++ b/src/Util.Domain/ITreeRepositoryExtensions.cs @@ -0,0 +1,49 @@ +using Util.Domain.Trees; + +namespace Util.Domain; + +/// +/// 树形仓储扩展 +/// +public static class ITreeRepositoryExtensions { + /// + /// 更新实体及所有下级节点路径 + /// + /// 仓储 + /// 实体 + public static async Task UpdatePathAsync( this ITreeRepository repository, TEntity entity ) + where TEntity : class, ITreeEntity { + var manager = new UpdatePathManager( repository ); + await manager.UpdatePathAsync( entity ); + } + + /// + /// 获取缺失的父标识列表 + /// + /// 实体类型 + /// 实体列表 + public static List GetMissingParentIds( this IEnumerable entities ) where TEntity : class, ITreeEntity { + return GetMissingParentIds( entities ); + } + + /// + /// 获取缺失的父标识列表 + /// + /// 实体类型 + /// /// 标识类型 + /// 父标识类型 + /// 实体列表 + public static List GetMissingParentIds( this IEnumerable entities ) where TEntity : class, ITreeEntity { + var result = new List(); + if ( entities == null ) + return result; + var list = entities.ToList(); + list.ForEach( entity => { + if ( entity == null ) + return; + result.AddRange( entity.GetParentIdsFromPath().Select( t => t.SafeString() ) ); + } ); + var ids = list.Select( t => t?.Id.SafeString() ); + return result.Except( ids ).ToList(); + } +} \ No newline at end of file diff --git a/src/Util.Domain/ListCompareExtensions.cs b/src/Util.Domain/ListCompareExtensions.cs new file mode 100644 index 000000000..4697d7464 --- /dev/null +++ b/src/Util.Domain/ListCompareExtensions.cs @@ -0,0 +1,72 @@ +using Util.Domain.Compare; + +namespace Util.Domain; + +/// +/// 列表比较器扩展 +/// +public static class ListCompareExtensions { + /// + /// 比较 + /// + /// 实体类型 + /// 新实体集合 + /// 旧实体集合 + public static ListCompareResult Compare( this IEnumerable newList, IEnumerable originalList ) + where TEntity : IKey { + return Compare( newList, originalList ); + } + + /// + /// 比较 + /// + /// 实体类型 + /// 标识类型 + /// 新实体集合 + /// 旧实体集合 + public static ListCompareResult Compare( this IEnumerable newList, IEnumerable originalList ) + where TEntity : IKey { + var comparator = new ListComparator(); + return comparator.Compare( newList, originalList ); + } + + /// + /// 比较 + /// + /// 新实体标识集合 + /// 旧实体标识集合 + public static KeyListCompareResult Compare( this IEnumerable newList, IEnumerable originalList ) { + var comparator = new KeyListComparator(); + return comparator.Compare( newList, originalList ); + } + + /// + /// 比较 + /// + /// 新实体标识集合 + /// 旧实体标识集合 + public static KeyListCompareResult Compare( this IEnumerable newList, IEnumerable originalList ) { + var comparator = new KeyListComparator(); + return comparator.Compare( newList, originalList ); + } + + /// + /// 比较 + /// + /// 新实体标识集合 + /// 旧实体标识集合 + public static KeyListCompareResult Compare( this IEnumerable newList, IEnumerable originalList ) { + var comparator = new KeyListComparator(); + return comparator.Compare( newList, originalList ); + } + + /// + /// 比较 + /// + /// 新实体标识集合 + /// 旧实体标识集合 + public static KeyListCompareResult Compare( this IEnumerable newList, IEnumerable originalList ) { + var comparator = new KeyListComparator(); + return comparator.Compare( newList, originalList ); + } +} \ No newline at end of file diff --git a/src/Util.Domain/Properties/DomainResource.Designer.cs b/src/Util.Domain/Properties/DomainResource.Designer.cs new file mode 100644 index 000000000..b704cd62e --- /dev/null +++ b/src/Util.Domain/Properties/DomainResource.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace Util.Domain.Properties { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class DomainResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DomainResource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Util.Domain.Properties.DomainResource", typeof(DomainResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 {0}({1}),原始值:{2},新值:{3} 的本地化字符串。 + /// + public static string ChangeValueToString { + get { + return ResourceManager.GetString("ChangeValueToString", resourceCulture); + } + } + + /// + /// 查找类似 不允许将节点移动到自己或子节点下 的本地化字符串。 + /// + public static string NotSupportMoveToChildren { + get { + return ResourceManager.GetString("NotSupportMoveToChildren", resourceCulture); + } + } + } +} diff --git a/src/Util.Domain/Properties/DomainResource.resx b/src/Util.Domain/Properties/DomainResource.resx new file mode 100644 index 000000000..9b61dc802 --- /dev/null +++ b/src/Util.Domain/Properties/DomainResource.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + {0}({1}),原始值:{2},新值:{3} + 变更值ToString方法返回消息 + + + 不允许将节点移动到自己或子节点下 + + \ No newline at end of file diff --git a/src/Util.Domain/Repositories/IQueryRepository.cs b/src/Util.Domain/Repositories/IQueryRepository.cs new file mode 100644 index 000000000..eda3bdebb --- /dev/null +++ b/src/Util.Domain/Repositories/IQueryRepository.cs @@ -0,0 +1,19 @@ +using Util.Data.Stores; +using Util.Domain.Entities; + +namespace Util.Domain.Repositories; + +/// +/// 查询仓储 +/// +/// 实体类型 +public interface IQueryRepository : IQueryRepository where TEntity : class, IAggregateRoot { +} + +/// +/// 查询仓储 +/// +/// 实体类型 +/// 实体标识类型 +public interface IQueryRepository : IQueryStore where TEntity : class, IAggregateRoot { +} \ No newline at end of file diff --git a/src/Util.Domain/Repositories/IRepository.cs b/src/Util.Domain/Repositories/IRepository.cs new file mode 100644 index 000000000..9b7e6ba02 --- /dev/null +++ b/src/Util.Domain/Repositories/IRepository.cs @@ -0,0 +1,20 @@ +using Util.Data.Stores; +using Util.Domain.Entities; + +namespace Util.Domain.Repositories; + +/// +/// 仓储 +/// +/// 实体类型 +public interface IRepository : IRepository, IQueryRepository + where TEntity : class, IAggregateRoot { +} + +/// +/// 仓储 +/// +/// 实体类型 +/// 实体标识类型 +public interface IRepository : IQueryRepository, IStore where TEntity : class, IAggregateRoot { +} \ No newline at end of file diff --git a/src/Util.Domain/Services/DomainServiceBase.cs b/src/Util.Domain/Services/DomainServiceBase.cs new file mode 100644 index 000000000..7391a94cd --- /dev/null +++ b/src/Util.Domain/Services/DomainServiceBase.cs @@ -0,0 +1,42 @@ +using Util.Localization; +using Util.Logging; +using Util.Sessions; + +namespace Util.Domain.Services; + +/// +/// 领域服务 +/// +public abstract class DomainServiceBase : IDomainService { + /// + /// 初始化领域服务 + /// + /// 服务提供器 + protected DomainServiceBase( IServiceProvider serviceProvider ) { + ServiceProvider = serviceProvider ?? throw new ArgumentNullException( nameof( serviceProvider ) ); + Session = serviceProvider.GetService() ?? NullSession.Instance; + var logFactory = serviceProvider.GetService(); + Log = logFactory?.CreateLog( GetType() ) ?? NullLog.Instance; + L = serviceProvider.GetService() ?? NullStringLocalizer.Instance; + } + + /// + /// 服务提供器 + /// + protected IServiceProvider ServiceProvider { get; } + + /// + /// 用户会话 + /// + protected ISession Session { get; set; } + + /// + /// 日志操作 + /// + protected ILog Log { get; } + + /// + /// 本地化字符串 + /// + protected IStringLocalizer L { get; set; } +} \ No newline at end of file diff --git a/src/Util.Domain/Services/IDomainService.cs b/src/Util.Domain/Services/IDomainService.cs new file mode 100644 index 000000000..89d829183 --- /dev/null +++ b/src/Util.Domain/Services/IDomainService.cs @@ -0,0 +1,10 @@ +using Util.Aop; +using Util.Dependency; + +namespace Util.Domain.Services; + +/// +/// 领域服务 +/// +public interface IDomainService : IScopeDependency, IAopProxy { +} \ No newline at end of file diff --git a/src/Util.Domain/Services/ParameterBase.cs b/src/Util.Domain/Services/ParameterBase.cs new file mode 100644 index 000000000..2df36a079 --- /dev/null +++ b/src/Util.Domain/Services/ParameterBase.cs @@ -0,0 +1,19 @@ +using Util.Exceptions; +using Util.Validation; + +namespace Util.Domain.Services; + +/// +/// 参数 +/// +public abstract class ParameterBase : IValidation { + /// + /// 验证 + /// + public virtual ValidationResultCollection Validate() { + var result = DataAnnotationValidation.Validate( this ); + if( result.IsValid ) + return ValidationResultCollection.Success; + throw new Warning( result.First().ErrorMessage ); + } +} \ No newline at end of file diff --git a/src/Util.Domain/Trees/IEnabled.cs b/src/Util.Domain/Trees/IEnabled.cs new file mode 100644 index 000000000..75f9d28a9 --- /dev/null +++ b/src/Util.Domain/Trees/IEnabled.cs @@ -0,0 +1,11 @@ +namespace Util.Domain.Trees; + +/// +/// 启用 +/// +public interface IEnabled { + /// + /// 是否启用 + /// + bool Enabled { get; set; } +} \ No newline at end of file diff --git a/src/Util.Domain/Trees/IParentId.cs b/src/Util.Domain/Trees/IParentId.cs new file mode 100644 index 000000000..a84b19246 --- /dev/null +++ b/src/Util.Domain/Trees/IParentId.cs @@ -0,0 +1,12 @@ +namespace Util.Domain.Trees; + +/// +/// 父标识 +/// +/// 父标识类型 +public interface IParentId { + /// + /// 父标识 + /// + TParentId ParentId { get; set; } +} \ No newline at end of file diff --git a/src/Util.Domain/Trees/IPath.cs b/src/Util.Domain/Trees/IPath.cs new file mode 100644 index 000000000..e150bb4d0 --- /dev/null +++ b/src/Util.Domain/Trees/IPath.cs @@ -0,0 +1,15 @@ +namespace Util.Domain.Trees; + +/// +/// 物化路径 +/// +public interface IPath { + /// + /// 路径 + /// + string Path { get; } + /// + /// 层级 + /// + int Level { get; } +} \ No newline at end of file diff --git a/src/Util.Domain/Trees/ISortId.cs b/src/Util.Domain/Trees/ISortId.cs new file mode 100644 index 000000000..c1dcb941d --- /dev/null +++ b/src/Util.Domain/Trees/ISortId.cs @@ -0,0 +1,11 @@ +namespace Util.Domain.Trees; + +/// +/// 排序号 +/// +public interface ISortId { + /// + /// 排序号 + /// + int? SortId { get; set; } +} \ No newline at end of file diff --git a/src/Util.Domain/Trees/ITreeEntity.cs b/src/Util.Domain/Trees/ITreeEntity.cs new file mode 100644 index 000000000..7f2373da0 --- /dev/null +++ b/src/Util.Domain/Trees/ITreeEntity.cs @@ -0,0 +1,26 @@ +using Util.Domain.Entities; + +namespace Util.Domain.Trees; + +/// +/// 树形实体 +/// +/// 实体类型 +/// 实体标识类型 +/// 父标识类型 +public interface ITreeEntity : IAggregateRoot, IParentId, IPath, IEnabled, ISortId where TEntity : ITreeEntity { + /// + /// 初始化路径 + /// + void InitPath(); + /// + /// 初始化路径 + /// + /// 父节点 + void InitPath( TEntity parent ); + /// + /// 从路径中获取所有上级节点标识 + /// + /// 是否排除当前节点,默认排除自身 + List GetParentIdsFromPath( bool excludeSelf = true ); +} \ No newline at end of file diff --git a/src/Util.Domain/Trees/ITreeRepository.cs b/src/Util.Domain/Trees/ITreeRepository.cs new file mode 100644 index 000000000..b12f3f2c2 --- /dev/null +++ b/src/Util.Domain/Trees/ITreeRepository.cs @@ -0,0 +1,25 @@ +using Util.Domain.Repositories; + +namespace Util.Domain.Trees; + +/// +/// 树形仓储 +/// +/// 实体类型 +public interface ITreeRepository : ITreeRepository where TEntity : class, ITreeEntity { +} + +/// +/// 树形仓储 +/// +/// 实体类型 +/// 实体标识类型 +/// 父标识类型 +public interface ITreeRepository : IRepository + where TEntity : class,ITreeEntity { + /// + /// 获取全部下级实体 + /// + /// 父实体 + Task> GetAllChildrenAsync( TEntity parent ); +} \ No newline at end of file diff --git a/src/Util.Domain/Trees/TreeEntityBase.cs b/src/Util.Domain/Trees/TreeEntityBase.cs new file mode 100644 index 000000000..a63fe201a --- /dev/null +++ b/src/Util.Domain/Trees/TreeEntityBase.cs @@ -0,0 +1,98 @@ +using Util.Domain.Entities; + +namespace Util.Domain.Trees; + +/// +/// 树形实体 +/// +/// 树形实体类型 +public abstract class TreeEntityBase : TreeEntityBase where TEntity : ITreeEntity { + /// + /// 初始化树形实体 + /// + /// 标识 + /// 路径 + /// 层级 + protected TreeEntityBase( Guid id, string path, int level ) + : base( id, path, level ) { + } +} + +/// +/// 树形实体 +/// +/// 树形实体类型 +/// 标识类型 +/// 父标识类型 +public abstract class TreeEntityBase : AggregateRoot, ITreeEntity where TEntity : ITreeEntity { + /// + /// 初始化树形实体 + /// + /// 标识 + /// 路径 + /// 层级 + protected TreeEntityBase( TKey id, string path, int level ) + : base( id ) { + Path = path; + Level = level; + } + + /// + /// 父标识 + /// + public TParentId ParentId { get; set; } + + /// + /// 路径 + /// + public virtual string Path { get;private set; } + + /// + /// 层级 + /// + public int Level { get; private set; } + + /// + /// 启用 + /// + public bool Enabled { get; set; } + + /// + /// 排序号 + /// + public int? SortId { get; set; } + + /// + /// 初始化路径 + /// + public virtual void InitPath() { + InitPath( default ); + } + + /// + /// 初始化路径 + /// + /// 父节点 + public virtual void InitPath( TEntity parent ) { + if( Equals( parent, null ) ) { + Level = 1; + Path = $"{Id},"; + return; + } + Level = parent.Level + 1; + Path = $"{parent.Path}{Id},"; + } + + /// + /// 从路径中获取所有上级节点标识 + /// + /// 是否排除当前节点,默认排除自身 + public List GetParentIdsFromPath( bool excludeSelf = true ) { + if( Path.IsEmpty() ) + return new List(); + var result = Path.Split( ',' ).Where( id => !id.IsEmpty() && id != "," ).ToList(); + if( excludeSelf ) + result = result.Where( id => id.SafeString().ToUpperInvariant() != Id.SafeString().ToUpperInvariant() ).ToList(); + return result.Select( Util.Helpers.Convert.To ).ToList(); + } +} \ No newline at end of file diff --git a/src/Util.Domain/Trees/UpdatePathManager.cs b/src/Util.Domain/Trees/UpdatePathManager.cs new file mode 100644 index 000000000..70998408c --- /dev/null +++ b/src/Util.Domain/Trees/UpdatePathManager.cs @@ -0,0 +1,59 @@ +using Util.Data; +using Util.Domain.Properties; +using Util.Exceptions; + +namespace Util.Domain.Trees; + +/// +/// 树形路径更新服务 +/// +public class UpdatePathManager + where TEntity : class, ITreeEntity { + /// + /// 仓储 + /// + private readonly ITreeRepository _repository; + + /// + /// 初始化树型路径更新服务 + /// + /// 仓储 + public UpdatePathManager( ITreeRepository repository ) { + _repository = repository; + } + + /// + /// 更新实体及所有下级节点路径 + /// + /// 实体 + public async Task UpdatePathAsync( TEntity entity ) { + entity.CheckNull( nameof( entity ) ); + if( entity.ParentId.Equals( entity.Id ) ) + throw new Warning( DomainResource.NotSupportMoveToChildren ); + var old = await _repository.NoTracking().FindByIdAsync( entity.Id ); + if( old == null ) + return; + if( entity.ParentId.Equals( old.ParentId ) ) + return; + var children = await _repository.GetAllChildrenAsync( entity ); + if( children.Exists( t => t.Id.Equals( entity.ParentId ) ) ) + throw new Warning( DomainResource.NotSupportMoveToChildren ); + var parent = await _repository.FindByIdAsync( entity.ParentId ); + entity.InitPath( parent ); + UpdateChildrenPath( entity, children ); + await _repository.UpdateAsync( children ); + } + + /// + /// 修改路径 + /// + private void UpdateChildrenPath( TEntity parent, List children ) { + if( parent == null || children == null ) + return; + var list = children.Where( t => t.ParentId.Equals( parent.Id ) ).ToList(); + foreach( var child in list ) { + child.InitPath( parent ); + UpdateChildrenPath( child, children ); + } + } +} \ No newline at end of file diff --git a/src/Util.Domain/Usings.cs b/src/Util.Domain/Usings.cs new file mode 100644 index 000000000..af908a7c7 --- /dev/null +++ b/src/Util.Domain/Usings.cs @@ -0,0 +1,13 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using System.Text; +global using System.Reflection; +global using System.Linq.Expressions; +global using System.ComponentModel.DataAnnotations; +global using System.ComponentModel.DataAnnotations.Schema; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Localization; +global using Util.Events; \ No newline at end of file diff --git a/src/Util.Events.Abstractions/01-Util.Events.Abstractions.csproj b/src/Util.Events.Abstractions/01-Util.Events.Abstractions.csproj new file mode 100644 index 000000000..80c58362f --- /dev/null +++ b/src/Util.Events.Abstractions/01-Util.Events.Abstractions.csproj @@ -0,0 +1,33 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Events.Abstractions + Util.Events + Util.Events.Abstractions是Util应用框架的事件总线操作接口定义类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Events.Abstractions.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Events.Abstractions.xml + + + + + True + False + + + + + + + + + diff --git a/src/Util.Events.Abstractions/EventBusExtensions.cs b/src/Util.Events.Abstractions/EventBusExtensions.cs new file mode 100644 index 000000000..f047bfa2d --- /dev/null +++ b/src/Util.Events.Abstractions/EventBusExtensions.cs @@ -0,0 +1,21 @@ +namespace Util.Events; + +/// +/// 事件总线扩展 +/// +public static class EventBusExtensions { + /// + /// 发布事件 + /// + /// 事件总线 + /// 事件集合 + /// 取消令牌 + public static async Task PublishAsync( this IEventBus eventBus, IEnumerable events, CancellationToken cancellationToken = default ) { + eventBus.CheckNull( nameof( eventBus ) ); + if( events == null ) + return; + foreach( var @event in events ) { + await eventBus.PublishAsync( @event, cancellationToken ); + } + } +} \ No newline at end of file diff --git a/src/Util.Events.Abstractions/IEvent.cs b/src/Util.Events.Abstractions/IEvent.cs new file mode 100644 index 000000000..e6b184dc9 --- /dev/null +++ b/src/Util.Events.Abstractions/IEvent.cs @@ -0,0 +1,7 @@ +namespace Util.Events; + +/// +/// 事件 +/// +public interface IEvent { +} \ No newline at end of file diff --git a/src/Util.Events.Abstractions/IEventBus.cs b/src/Util.Events.Abstractions/IEventBus.cs new file mode 100644 index 000000000..0a0917463 --- /dev/null +++ b/src/Util.Events.Abstractions/IEventBus.cs @@ -0,0 +1,14 @@ +namespace Util.Events; + +/// +/// 事件总线 +/// +public interface IEventBus : ITransientDependency { + /// + /// 发布事件 + /// + /// 事件类型 + /// 事件 + /// 取消令牌 + Task PublishAsync( TEvent @event,CancellationToken cancellationToken = default ) where TEvent : IEvent; +} \ No newline at end of file diff --git a/src/Util.Events.Abstractions/IEventHandler.cs b/src/Util.Events.Abstractions/IEventHandler.cs new file mode 100644 index 000000000..aec830908 --- /dev/null +++ b/src/Util.Events.Abstractions/IEventHandler.cs @@ -0,0 +1,24 @@ +namespace Util.Events; + +/// +/// 事件处理器 +/// +public interface IEventHandler { + /// + /// 是否启用 + /// + bool Enabled { get; } +} + +/// +/// 事件处理器 +/// +/// 事件类型 +public interface IEventHandler : IEventHandler where TEvent : IEvent { + /// + /// 处理事件 + /// + /// 事件 + /// 取消令牌 + Task HandleAsync( TEvent @event, CancellationToken cancellationToken ) ; +} \ No newline at end of file diff --git a/src/Util.Events.Abstractions/IIntegrationEvent.cs b/src/Util.Events.Abstractions/IIntegrationEvent.cs new file mode 100644 index 000000000..73c9df696 --- /dev/null +++ b/src/Util.Events.Abstractions/IIntegrationEvent.cs @@ -0,0 +1,15 @@ +namespace Util.Events; + +/// +/// 集成事件 +/// +public interface IIntegrationEvent : IEvent { + /// + /// 事件标识 + /// + string EventId { get; } + /// + /// 事件发生时间 + /// + DateTime EventTime { get; } +} \ No newline at end of file diff --git a/src/Util.Events.Abstractions/IIntegrationEventBus.cs b/src/Util.Events.Abstractions/IIntegrationEventBus.cs new file mode 100644 index 000000000..ef72eb6af --- /dev/null +++ b/src/Util.Events.Abstractions/IIntegrationEventBus.cs @@ -0,0 +1,87 @@ +namespace Util.Events; + +/// +/// 基于消息的集成事件总线 +/// +public interface IIntegrationEventBus : ITransientDependency { + /// + /// 设置发布订阅配置名称 + /// + /// 发布订阅配置名称 + IIntegrationEventBus PubsubName( string pubsubName ); + /// + /// 设置事件主题,默认为事件类型名称 + /// + /// 事件主题 + IIntegrationEventBus Topic( string topic ); + /// + /// 设置请求头 + /// + /// 键 + /// 值 + IIntegrationEventBus Header( string key, string value ); + /// + /// 设置请求头 + /// + /// 请求头键值对集合 + IIntegrationEventBus Header( IDictionary headers ); + /// + /// 从当前HttpContext导入请求头 + /// + /// 请求头键名 + IIntegrationEventBus ImportHeader( string key ); + /// + /// 从当前HttpContext导入请求头 + /// + /// 请求头键名集合 + IIntegrationEventBus ImportHeader( IEnumerable keys ); + /// + /// 移除请求头 + /// + /// 请求头键名 + IIntegrationEventBus RemoveHeader( string key ); + /// + /// 设置元数据 + /// + /// 键 + /// 值 + IIntegrationEventBus Metadata( string key, string value ); + /// + /// 设置元数据 + /// + /// 元数据键值对集合 + IIntegrationEventBus Metadata( IDictionary metadata ); + /// + /// 移除元数据 + /// + /// 键 + IIntegrationEventBus RemoveMetadata( string key ); + /// + /// 设置云事件属性: type + /// + /// 值 + IIntegrationEventBus Type( string value ); + /// + /// 发布前事件 + /// + /// 发布前操作,返回false取消发布 + IIntegrationEventBus OnBefore( Func, Dictionary, bool> action ); + /// + /// 发布后事件 + /// + /// 发布后操作 + IIntegrationEventBus OnAfter( Func, Dictionary, Task> action ); + /// + /// 发布事件 + /// + /// 事件类型 + /// 事件 + /// 取消令牌 + Task PublishAsync( TEvent @event, CancellationToken cancellationToken = default ) where TEvent : IIntegrationEvent; + /// + /// 重新发布集成事件 + /// + /// 事件标识 + /// 取消令牌 + Task RepublishAsync( string eventId, CancellationToken cancellationToken = default ); +} \ No newline at end of file diff --git a/src/Util.Events.Abstractions/ILocalEventBus.cs b/src/Util.Events.Abstractions/ILocalEventBus.cs new file mode 100644 index 000000000..a8d8d2204 --- /dev/null +++ b/src/Util.Events.Abstractions/ILocalEventBus.cs @@ -0,0 +1,7 @@ +namespace Util.Events; + +/// +/// 基于内存的本地事件总线 +/// +public interface ILocalEventBus : IEventBus { +} \ No newline at end of file diff --git a/src/Util.Events.Abstractions/IntegrationEvent.cs b/src/Util.Events.Abstractions/IntegrationEvent.cs new file mode 100644 index 000000000..abd5876d3 --- /dev/null +++ b/src/Util.Events.Abstractions/IntegrationEvent.cs @@ -0,0 +1,20 @@ +namespace Util.Events; + +/// +/// 集成事件 +/// +public record IntegrationEvent : IIntegrationEvent { + /// + public string EventId { get; init; } + + /// + public DateTime EventTime { get; init; } + + /// + /// 初始化集成事件 + /// + public IntegrationEvent() { + EventId = Guid.NewGuid().ToString(); + EventTime = Util.Helpers.Time.Now; + } +} \ No newline at end of file diff --git a/src/Util.Events.Abstractions/NullEventBus.cs b/src/Util.Events.Abstractions/NullEventBus.cs new file mode 100644 index 000000000..d39a93434 --- /dev/null +++ b/src/Util.Events.Abstractions/NullEventBus.cs @@ -0,0 +1,124 @@ +namespace Util.Events; + +/// +/// 空事件总线 +/// +[Ioc(-9)] +public class NullEventBus : IEventBus { + /// + /// 空事件总线实例 + /// + public static readonly IEventBus Instance = new NullEventBus(); + + /// + public Task PublishAsync( TEvent @event, CancellationToken cancellationToken = default ) where TEvent : IEvent { + return Task.CompletedTask; + } +} + +/// +/// 空事件总线 +/// +[Ioc( -9 )] +public class NullLocalEventBus : ILocalEventBus { + /// + /// 空事件总线实例 + /// + public static readonly ILocalEventBus Instance = new NullLocalEventBus(); + + /// + public Task PublishAsync( TEvent @event, CancellationToken cancellationToken = default ) where TEvent : IEvent { + return Task.CompletedTask; + } +} + +/// +/// 空事件总线 +/// +[Ioc( -9 )] +public class NullIntegrationEventBus : IIntegrationEventBus { + /// + /// 空事件总线实例 + /// + public static readonly IIntegrationEventBus Instance = new NullIntegrationEventBus(); + + /// + public IIntegrationEventBus SendNow( bool isSend = true ) { + return this; + } + + /// + public IIntegrationEventBus PubsubName( string pubsubName ) { + return this; + } + + /// + public IIntegrationEventBus Topic( string topic ) { + return this; + } + + /// + public IIntegrationEventBus Header( string key, string value ) { + return this; + } + + /// + public IIntegrationEventBus Header( IDictionary headers ) { + return this; + } + + /// + public IIntegrationEventBus ImportHeader( string key ) { + return this; + } + + /// + public IIntegrationEventBus ImportHeader( IEnumerable keys ) { + return this; + } + + /// + public IIntegrationEventBus RemoveHeader( string key ) { + return this; + } + + /// + public IIntegrationEventBus Metadata( string key, string value ) { + return this; + } + + /// + public IIntegrationEventBus Metadata( IDictionary metadata ) { + return this; + } + + /// + public IIntegrationEventBus RemoveMetadata( string key ) { + return this; + } + + /// + public IIntegrationEventBus Type( string value ) { + return this; + } + + /// + public IIntegrationEventBus OnBefore( Func, Dictionary, bool> action ) { + return this; + } + + /// + public IIntegrationEventBus OnAfter( Func, Dictionary, Task> action ) { + return this; + } + + /// + public Task PublishAsync( TEvent @event, CancellationToken cancellationToken = default ) where TEvent : IIntegrationEvent { + return Task.CompletedTask; + } + + /// + public Task RepublishAsync( string eventId, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/Util.Events.Abstractions/Usings.cs b/src/Util.Events.Abstractions/Usings.cs new file mode 100644 index 000000000..4d89c62da --- /dev/null +++ b/src/Util.Events.Abstractions/Usings.cs @@ -0,0 +1,5 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Threading; +global using Util.Dependency; \ No newline at end of file diff --git a/src/Util.Events.MediatR/03-Util.Events.MediatR.csproj b/src/Util.Events.MediatR/03-Util.Events.MediatR.csproj new file mode 100644 index 000000000..272e3dbf2 --- /dev/null +++ b/src/Util.Events.MediatR/03-Util.Events.MediatR.csproj @@ -0,0 +1,37 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Events.MediatR + Util.Events + Util.Events.MediatR是Util应用框架基于MediatR的事件总线操作类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Events.MediatR.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Events.MediatR.xml + + + + + True + False + + + + + + + + + + + + + diff --git a/src/Util.Events.MediatR/AppBuilderExtensions.cs b/src/Util.Events.MediatR/AppBuilderExtensions.cs new file mode 100644 index 000000000..738097ba6 --- /dev/null +++ b/src/Util.Events.MediatR/AppBuilderExtensions.cs @@ -0,0 +1,35 @@ +using Util.Configs; + +namespace Util.Events; + +/// +/// MediatR事件总线操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置MediatR事件总线操作 + /// + /// 应用生成器 + public static IAppBuilder AddMediatR( this IAppBuilder builder ) { + builder.CheckNull( nameof( builder ) ); + MediatROptions.IsScan = true; + builder.Host.ConfigureServices( ( context, services ) => { + services.TryAddTransient(); + } ); + return builder; + } + + /// + /// 配置MediatR事件总线操作 + /// + /// 应用生成器 + /// 事件总线配置操作 + public static IAppBuilder AddMediatR( this IAppBuilder builder, Action setupAction ) { + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + services.TryAddTransient(); + services.AddMediatR( setupAction ); + } ); + return builder; + } +} \ No newline at end of file diff --git a/src/Util.Events.MediatR/EventBase.cs b/src/Util.Events.MediatR/EventBase.cs new file mode 100644 index 000000000..ddf40d589 --- /dev/null +++ b/src/Util.Events.MediatR/EventBase.cs @@ -0,0 +1,7 @@ +namespace Util.Events; + +/// +/// 事件 +/// +public abstract class EventBase : IEvent, INotification { +} \ No newline at end of file diff --git a/src/Util.Events.MediatR/EventHandlerBase.cs b/src/Util.Events.MediatR/EventHandlerBase.cs new file mode 100644 index 000000000..e79c44525 --- /dev/null +++ b/src/Util.Events.MediatR/EventHandlerBase.cs @@ -0,0 +1,30 @@ +namespace Util.Events; + +/// +/// 基于MediatR的本地事件处理器基类 +/// +/// 事件类型 +public abstract class EventHandlerBase : INotificationHandler where TEvent : IEvent, INotification { + /// + /// 是否启用 + /// + public virtual bool Enabled => true; + + /// + /// 处理事件 + /// + /// 事件 + /// 取消令牌 + public async Task Handle( TEvent @event, CancellationToken cancellationToken ) { + if ( Enabled == false ) + return; + await HandleAsync( @event, cancellationToken ); + } + + /// + /// 处理事件 + /// + /// 事件 + /// 取消令牌 + public abstract Task HandleAsync( TEvent @event, CancellationToken cancellationToken ); +} \ No newline at end of file diff --git a/src/Util.Events.MediatR/Infrastructure/MediatREventBusServiceRegistrar.cs b/src/Util.Events.MediatR/Infrastructure/MediatREventBusServiceRegistrar.cs new file mode 100644 index 000000000..0ff355c17 --- /dev/null +++ b/src/Util.Events.MediatR/Infrastructure/MediatREventBusServiceRegistrar.cs @@ -0,0 +1,41 @@ +namespace Util.Events.Infrastructure; + +/// +/// MediatR本地事件总线服务注册器 +/// +public class MediatREventBusServiceRegistrar : IServiceRegistrar { + /// + /// 获取服务名 + /// + public static string ServiceName => "Util.Events.MediatR.Infrastructure.LocalEventBusServiceRegistrar"; + + /// + /// 排序号 + /// + public int OrderId => 511; + + /// + /// 是否启用 + /// + public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName ); + + /// + /// 注册服务 + /// + /// 服务上下文 + public Action Register( ServiceContext serviceContext ) { + serviceContext.HostBuilder.ConfigureServices( ( context, services ) => { + RegisterMediatR( services, serviceContext.AssemblyFinder ); + } ); + return null; + } + + /// + /// 注册MediatR + /// + private void RegisterMediatR( IServiceCollection services, IAssemblyFinder finder ) { + if ( MediatROptions.IsScan == false ) + return; + services.AddMediatR( t => t.RegisterServicesFromAssemblies( finder.Find().ToArray() ) ); + } +} \ No newline at end of file diff --git a/src/Util.Events.MediatR/Infrastructure/ServiceRegistrarConfigExtensions.cs b/src/Util.Events.MediatR/Infrastructure/ServiceRegistrarConfigExtensions.cs new file mode 100644 index 000000000..2b1c713a1 --- /dev/null +++ b/src/Util.Events.MediatR/Infrastructure/ServiceRegistrarConfigExtensions.cs @@ -0,0 +1,24 @@ +namespace Util.Events.Infrastructure; + +/// +/// MediatR本地事件总线服务注册器配置扩展 +/// +public static class ServiceRegistrarConfigExtensions { + /// + /// 启用MediatR本地事件总线服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig EnableMediatREventBusServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Enable( MediatREventBusServiceRegistrar.ServiceName ); + return config; + } + + /// + ///禁用MediatR本地事件总线服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig DisableMediatREventBusServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Disable( MediatREventBusServiceRegistrar.ServiceName ); + return config; + } +} \ No newline at end of file diff --git a/src/Util.Events.MediatR/MediatREventBus.cs b/src/Util.Events.MediatR/MediatREventBus.cs new file mode 100644 index 000000000..02b7ec6cb --- /dev/null +++ b/src/Util.Events.MediatR/MediatREventBus.cs @@ -0,0 +1,27 @@ +namespace Util.Events; + +/// +/// 基于MediatR的本地事件总线 +/// +public class MediatREventBus : ILocalEventBus { + /// + /// 事件发布器 + /// + private readonly IMediator _publisher; + + /// + /// 初始化基于MediatR的本地事件总线 + /// + /// 事件发布器 + public MediatREventBus( IMediator publisher ) { + _publisher = publisher ?? throw new ArgumentNullException( nameof( publisher ) ); + } + + /// + public async Task PublishAsync( TEvent @event,CancellationToken cancellationToken = default ) where TEvent : IEvent { + cancellationToken.ThrowIfCancellationRequested(); + if ( @event == null ) + return; + await _publisher.Publish( @event, cancellationToken ); + } +} \ No newline at end of file diff --git a/src/Util.Events.MediatR/MediatROptions.cs b/src/Util.Events.MediatR/MediatROptions.cs new file mode 100644 index 000000000..b6042ceab --- /dev/null +++ b/src/Util.Events.MediatR/MediatROptions.cs @@ -0,0 +1,11 @@ +namespace Util.Events; + +/// +/// MediatR事件总线配置 +/// +public static class MediatROptions { + /// + /// 是否扫描加载程序集 + /// + public static bool IsScan { get; set; } +} \ No newline at end of file diff --git a/src/Util.Events.MediatR/Usings.cs b/src/Util.Events.MediatR/Usings.cs new file mode 100644 index 000000000..9e38359f6 --- /dev/null +++ b/src/Util.Events.MediatR/Usings.cs @@ -0,0 +1,8 @@ +global using System; +global using System.Threading.Tasks; +global using System.Threading; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Microsoft.Extensions.DependencyInjection; +global using MediatR; +global using Util.Infrastructure; +global using Util.Reflections; \ No newline at end of file diff --git a/src/Util.Events/02-Util.Events.csproj b/src/Util.Events/02-Util.Events.csproj new file mode 100644 index 000000000..f1e669113 --- /dev/null +++ b/src/Util.Events/02-Util.Events.csproj @@ -0,0 +1,48 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Events + Util.Events + Util.Events是Util应用框架的事件总线操作核心类库,包含了本地领域事件总线 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Events.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Events.xml + + + + + True + False + + + + + + + + + + + True + True + EventBusResource.resx + + + + + + PublicResXFileCodeGenerator + EventBusResource.Designer.cs + + + + diff --git a/src/Util.Events/04-Util.Events.csproj b/src/Util.Events/04-Util.Events.csproj deleted file mode 100644 index e0260fce9..000000000 --- a/src/Util.Events/04-Util.Events.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - netstandard2.0 - Util.Events - Util.Events - 0.2.2-preview - 何镇汐 - 何镇汐 - Util.Events是Util应用框架的事件总线操作类库。 -Util是一个.net core平台下的应用框架,旨在提升小型团队的开发输出能力,由常用公共操作类(工具类)、分层架构基类、Ui组件,第三方组件封装,第三方业务接口封装,配套代码生成模板,权限等组成。 - 何镇汐 - https://github.com/dotnetcore/Util/blob/master/LICENSE.txt - https://github.com/dotnetcore/Util - https://avatars2.githubusercontent.com/u/19421630 - https://github.com/dotnetcore/Util - git - util;applicationframework;dotnetcore;aspnetcore; - Util应用框架以MIT开源发布,可随意使用 - 0.2.2.0 - 0.2.2.0 - false - - - - ..\..\output\release - ..\..\output\release\netstandard2.0\Util.Events.xml - - - - ..\..\output\release - ..\..\output\release\netstandard2.0\Util.Events.xml - - - - - - - - - - - - - diff --git a/src/Util.Events/Cap/Extensions.Service.cs b/src/Util.Events/Cap/Extensions.Service.cs deleted file mode 100644 index 08cea53d6..000000000 --- a/src/Util.Events/Cap/Extensions.Service.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using DotNetCore.CAP; -using Microsoft.Extensions.DependencyInjection; -using Util.Events.Default; -using Util.Events.Handlers; -using Util.Events.Messages; - -namespace Util.Events.Cap { - /// - /// 事件总线扩展 - /// - public static class Extensions { - /// - /// 注册事件总线服务 - /// - /// 服务集合 - /// 配置操作 - public static void AddEventBus( this IServiceCollection services,Action action ) { - services.AddCap( action ); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - } - } -} diff --git a/src/Util.Events/Cap/MessageEventBus.cs b/src/Util.Events/Cap/MessageEventBus.cs deleted file mode 100644 index 1dbb3b5d0..000000000 --- a/src/Util.Events/Cap/MessageEventBus.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using DotNetCore.CAP; -using Util.Events.Messages; - -namespace Util.Events.Cap { - /// - /// Cap消息事件总线 - /// - public class MessageEventBus : IMessageEventBus { - /// - /// Cap事件发布器 - /// - private readonly ICapPublisher _publisher; - - /// - /// 初始化Cap消息事件总线 - /// - /// Cap事件发布器 - public MessageEventBus( ICapPublisher publisher ) { - _publisher = publisher ?? throw new ArgumentNullException( nameof( publisher ) ); - } - - /// - /// 发布事件 - /// - /// 事件类型 - /// 事件 - public void Publish( TEvent @event ) where TEvent : IMessageEvent { - _publisher.Publish( @event.Target, @event.Data, @event.Callback ); - } - } -} diff --git a/src/Util.Events/Default/EventBus.cs b/src/Util.Events/Default/EventBus.cs deleted file mode 100644 index 7484070d6..000000000 --- a/src/Util.Events/Default/EventBus.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Util.Events.Handlers; -using Util.Events.Messages; -using Util.Logs.Aspects; - -namespace Util.Events.Default { - /// - /// 事件总线 - /// - public class EventBus : IEventBus { - /// - /// 初始化事件总线 - /// - /// 事件处理器服务 - /// 消息事件总线 - public EventBus( IEventHandlerManager manager, IMessageEventBus messageEventBus = null ) { - Manager = manager; - MessageEventBus = messageEventBus; - } - - /// - /// 事件处理器服务 - /// - public IEventHandlerManager Manager { get; set; } - - /// - /// 消息事件总线 - /// - public IMessageEventBus MessageEventBus { get; set; } - - /// - /// 发布事件 - /// - /// 事件类型 - /// 事件 - [TraceLog] - public void Publish( TEvent @event ) where TEvent : IEvent { - SyncHandle( @event ); - if( @event is IMessageEvent messageEvent ) - AsyncHandle( messageEvent ); - } - - /// - /// 同步处理 - 在当前线程处理 - /// - private void SyncHandle( TEvent @event ) where TEvent : IEvent { - var handlers = Manager.GetHandlers(); - if( handlers == null ) - return; - foreach( var handler in handlers ) - handler.Handle( @event ); - } - - /// - /// 异步处理 - 发送到消息中间件 - /// - private void AsyncHandle( IMessageEvent messageEvent ) { - MessageEventBus?.Publish( messageEvent ); - } - } -} diff --git a/src/Util.Events/Default/EventHandlerManager.cs b/src/Util.Events/Default/EventHandlerManager.cs deleted file mode 100644 index adcceca6a..000000000 --- a/src/Util.Events/Default/EventHandlerManager.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Generic; -using Util.Events.Handlers; -using Util.Helpers; - -namespace Util.Events.Default { - /// - /// 事件处理器服务 - /// - public class EventHandlerManager : IEventHandlerManager { - /// - /// 获取事件处理器列表 - /// - /// 事件类型 - public List> GetHandlers() where TEvent : IEvent { - return Ioc.CreateList>(); - } - } -} diff --git a/src/Util.Events/Default/Extensions.Service.cs b/src/Util.Events/Default/Extensions.Service.cs deleted file mode 100644 index 6ef445695..000000000 --- a/src/Util.Events/Default/Extensions.Service.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Util.Events.Handlers; - -namespace Util.Events.Default { - /// - /// 事件总线扩展 - /// - public static class Extensions { - /// - /// 注册事件总线服务 - /// - /// 服务集合 - public static void AddEventBus( this IServiceCollection services ) { - services.AddSingleton(); - services.AddSingleton(); - } - } -} diff --git a/src/Util.Events/EventHandlerBase.cs b/src/Util.Events/EventHandlerBase.cs new file mode 100644 index 000000000..2381a46ae --- /dev/null +++ b/src/Util.Events/EventHandlerBase.cs @@ -0,0 +1,24 @@ +namespace Util.Events; + +/// +/// 本地事件处理器基类 +/// +/// 事件类型 +public abstract class EventHandlerBase : IEventHandler, ILocalEventHandler where TEvent : IEvent { + /// + /// 处理事件 + /// + /// 事件 + /// 取消令牌 + public abstract Task HandleAsync( TEvent @event, CancellationToken cancellationToken ); + + /// + /// 序号 + /// + public virtual int Order => 0; + + /// + /// 是否启用 + /// + public virtual bool Enabled => true; +} \ No newline at end of file diff --git a/src/Util.Events/ILocalEventHandler.cs b/src/Util.Events/ILocalEventHandler.cs new file mode 100644 index 000000000..74c6d9055 --- /dev/null +++ b/src/Util.Events/ILocalEventHandler.cs @@ -0,0 +1,11 @@ +namespace Util.Events; + +/// +/// 本地事件处理器 +/// +public interface ILocalEventHandler : IEventHandler { + /// + /// 序号 + /// + int Order { get; } +} \ No newline at end of file diff --git a/src/Util.Events/Infrastructure/LocalEventBusServiceRegistrar.cs b/src/Util.Events/Infrastructure/LocalEventBusServiceRegistrar.cs new file mode 100644 index 000000000..ecb20c5c5 --- /dev/null +++ b/src/Util.Events/Infrastructure/LocalEventBusServiceRegistrar.cs @@ -0,0 +1,62 @@ +using Util.Infrastructure; +using Util.Reflections; + +namespace Util.Events.Infrastructure; + +/// +/// 本地事件总线服务注册器 +/// +public class LocalEventBusServiceRegistrar : IServiceRegistrar { + /// + /// 获取服务名 + /// + public static string ServiceName => "Util.Events.Infrastructure.LocalEventBusServiceRegistrar"; + + /// + /// 排序号 + /// + public int OrderId => 510; + + /// + /// 是否启用 + /// + public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName ); + + /// + /// 注册服务 + /// + /// 服务上下文 + public Action Register( ServiceContext serviceContext ) { + serviceContext.HostBuilder.ConfigureServices( ( context, services ) => { + RegisterDependency( services ); + RegisterEventHandlers( services, serviceContext.TypeFinder ); + } ); + return null; + } + + /// + /// 注册依赖 + /// + private void RegisterDependency( IServiceCollection services ) { + services.TryAddTransient(); + } + + /// + /// 注册事件处理器 + /// + private void RegisterEventHandlers( IServiceCollection services, ITypeFinder finder ) { + Type handlerType = typeof(IEventHandler<>); + var handlerTypes = GetTypes( finder,handlerType ); + foreach( var handler in handlerTypes ) { + var serviceTypes = handler.FindInterfaces( ( filter, criteria ) => criteria != null && filter.IsGenericType && ( (Type)criteria ).IsAssignableFrom( filter.GetGenericTypeDefinition() ), handlerType ); + serviceTypes.ToList().ForEach( serviceType => services.AddScoped( serviceType, handler ) ); + } + } + + /// + /// 获取类型集合 + /// + private Type[] GetTypes( ITypeFinder finder,Type type ) { + return finder.Find( type ).ToArray(); + } +} \ No newline at end of file diff --git a/src/Util.Events/Infrastructure/ServiceRegistrarConfigExtensions.cs b/src/Util.Events/Infrastructure/ServiceRegistrarConfigExtensions.cs new file mode 100644 index 000000000..bf1f7f5eb --- /dev/null +++ b/src/Util.Events/Infrastructure/ServiceRegistrarConfigExtensions.cs @@ -0,0 +1,26 @@ +using Util.Infrastructure; + +namespace Util.Events.Infrastructure; + +/// +/// 本地事件总线服务注册器配置扩展 +/// +public static class ServiceRegistrarConfigExtensions { + /// + /// 启用本地事件总线服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig EnableLocalEventBusServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Enable( LocalEventBusServiceRegistrar.ServiceName ); + return config; + } + + /// + ///禁用本地事件总线服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig DisableLocalEventBusServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Disable( LocalEventBusServiceRegistrar.ServiceName ); + return config; + } +} \ No newline at end of file diff --git a/src/Util.Events/LocalEventBus.cs b/src/Util.Events/LocalEventBus.cs new file mode 100644 index 000000000..71ce98860 --- /dev/null +++ b/src/Util.Events/LocalEventBus.cs @@ -0,0 +1,59 @@ +namespace Util.Events; + +/// +/// 基于内存的本地事件总线 +/// +public class LocalEventBus : ILocalEventBus { + /// + /// 服务提供器 + /// + private readonly IServiceProvider _serviceProvider; + + /// + /// 初始化本地事件总线 + /// + /// 服务提供器 + public LocalEventBus( IServiceProvider serviceProvider ) { + _serviceProvider = serviceProvider ?? throw new ArgumentNullException( nameof( serviceProvider ) ); + } + + /// + public async Task PublishAsync( TEvent @event,CancellationToken cancellationToken = default ) where TEvent : IEvent { + cancellationToken.ThrowIfCancellationRequested(); + if ( @event == null ) + return; + await PublishLocalEventAsync( @event, cancellationToken ); + } + + /// + /// 发布本地事件 + /// + private async Task PublishLocalEventAsync( TEvent @event, CancellationToken cancellationToken ) where TEvent : IEvent { + var eventType = @event.GetType(); + var handlers = GetEventHandlers( eventType ); + if ( handlers == null ) + return; + foreach ( var handler in handlers.Where( t => t is { Enabled: true } ).OrderBy( t => t.Order ) ) { + var method = typeof( IEventHandler<> ).MakeGenericType( eventType ).GetMethod( "HandleAsync", new[] { eventType,typeof( CancellationToken ) } ); + if ( method == null ) + return; + var result = method.Invoke( handler, new object[] { @event, cancellationToken } ); + if ( result == null ) + return; + await (Task)result; + } + } + + /// + /// 获取事件处理器列表 + /// + private IEnumerable GetEventHandlers( Type eventType ) { + var handlerType = typeof( IEventHandler<> ).MakeGenericType( eventType ); + var serviceType = typeof( IEnumerable<> ).MakeGenericType( handlerType ); + var handlers = _serviceProvider.GetService( serviceType ); + if ( handlers is not IEnumerable eventHandlers ) + yield break; + foreach ( var handler in eventHandlers ) + yield return handler as ILocalEventHandler; + } +} \ No newline at end of file diff --git a/src/Util.Events/Properties/EventBusResource.Designer.cs b/src/Util.Events/Properties/EventBusResource.Designer.cs new file mode 100644 index 000000000..e8f229806 --- /dev/null +++ b/src/Util.Events/Properties/EventBusResource.Designer.cs @@ -0,0 +1,108 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace Util.Events.Properties { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class EventBusResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal EventBusResource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Util.Events.Properties.EventBusResource", typeof(EventBusResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 回调名称 的本地化字符串。 + /// + public static string Callback { + get { + return ResourceManager.GetString("Callback", resourceCulture); + } + } + + /// + /// 查找类似 事件数据 的本地化字符串。 + /// + public static string EventData { + get { + return ResourceManager.GetString("EventData", resourceCulture); + } + } + + /// + /// 查找类似 事件标识 的本地化字符串。 + /// + public static string EventId { + get { + return ResourceManager.GetString("EventId", resourceCulture); + } + } + + /// + /// 查找类似 事件发生时间 的本地化字符串。 + /// + public static string EventTime { + get { + return ResourceManager.GetString("EventTime", resourceCulture); + } + } + + /// + /// 查找类似 消息名称 的本地化字符串。 + /// + public static string MessageName { + get { + return ResourceManager.GetString("MessageName", resourceCulture); + } + } + } +} diff --git a/src/Util.Events/Properties/EventBusResource.resx b/src/Util.Events/Properties/EventBusResource.resx new file mode 100644 index 000000000..f87e9b2db --- /dev/null +++ b/src/Util.Events/Properties/EventBusResource.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 回调名称 + + + 事件数据 + + + 事件标识 + + + 事件发生时间 + + + 消息名称 + + \ No newline at end of file diff --git a/src/Util.Events/Usings.cs b/src/Util.Events/Usings.cs new file mode 100644 index 000000000..e92039a8a --- /dev/null +++ b/src/Util.Events/Usings.cs @@ -0,0 +1,7 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/01-Util.FileStorage.Abstractions.csproj b/src/Util.FileStorage.Abstractions/01-Util.FileStorage.Abstractions.csproj new file mode 100644 index 000000000..e81d9142a --- /dev/null +++ b/src/Util.FileStorage.Abstractions/01-Util.FileStorage.Abstractions.csproj @@ -0,0 +1,33 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.FileStorage.Abstractions + Util.FileStorage + Util.FileStorage.Abstractions是Util应用框架文件存储操作接口定义类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.FileStorage.Abstractions.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.FileStorage.Abstractions.xml + + + + + True + False + + + + + + + + + diff --git a/src/Util.FileStorage.Abstractions/DeleteFileArgs.cs b/src/Util.FileStorage.Abstractions/DeleteFileArgs.cs new file mode 100644 index 000000000..6f14c88ff --- /dev/null +++ b/src/Util.FileStorage.Abstractions/DeleteFileArgs.cs @@ -0,0 +1,13 @@ +namespace Util.FileStorage; + +/// +/// 删除文件方法参数 +/// +public class DeleteFileArgs : FileStorageArgs { + /// + /// 初始化删除文件方法参数 + /// + /// 文件名 + public DeleteFileArgs( string fileName ) : base( fileName ) { + } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/DirectUploadParam.cs b/src/Util.FileStorage.Abstractions/DirectUploadParam.cs new file mode 100644 index 000000000..cac0138a3 --- /dev/null +++ b/src/Util.FileStorage.Abstractions/DirectUploadParam.cs @@ -0,0 +1,55 @@ +namespace Util.FileStorage; + +/// +/// 客户端直传参数 +/// +public class DirectUploadParam { + /// + /// 初始化客户端直传参数 + /// + /// 文件路径 + /// 直传Url + /// 直传数据 + /// 原始文件名 + /// 存储桶名称 + public DirectUploadParam( string filePath, string url, object data = null, string originalFileName = null, string bucket = null ) { + if( filePath.IsEmpty() ) + throw new ArgumentNullException( nameof( filePath ) ); + FilePath = filePath; + Url = url; + Data = data; + FileName = System.IO.Path.GetFileName( filePath ); + Extension = System.IO.Path.GetExtension( FileName )?.TrimStart( '.' ); + OriginalFileName = originalFileName; + Bucket = bucket; + } + + /// + /// 直传Url + /// + public string Url { get; } + /// + /// 直传数据 + /// + public object Data { get; } + /// + /// 文件路径 + /// + public string FilePath { get; } + /// + /// 文件名 + /// + public string FileName { get; } + /// + /// 扩展名 + /// + public string Extension { get; } + /// + /// 原始文件名 + /// + public string OriginalFileName { get; } + /// + /// 存储桶名称 + /// + public string Bucket { get; } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/FileExistsArgs.cs b/src/Util.FileStorage.Abstractions/FileExistsArgs.cs new file mode 100644 index 000000000..a135d4107 --- /dev/null +++ b/src/Util.FileStorage.Abstractions/FileExistsArgs.cs @@ -0,0 +1,13 @@ +namespace Util.FileStorage; + +/// +/// 文件是否存在方法参数 +/// +public class FileExistsArgs : FileStorageArgs { + /// + /// 初始化文件是否存在方法参数 + /// + /// 文件名 + public FileExistsArgs( string fileName ) : base( fileName ) { + } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/FileResult.cs b/src/Util.FileStorage.Abstractions/FileResult.cs new file mode 100644 index 000000000..79f964bf1 --- /dev/null +++ b/src/Util.FileStorage.Abstractions/FileResult.cs @@ -0,0 +1,49 @@ +namespace Util.FileStorage; + +/// +/// 文件处理结果 +/// +public class FileResult { + /// + /// 初始化文件处理结果 + /// + /// 文件路径 + /// 文件大小 + /// 原始文件名 + /// 存储桶名称 + public FileResult( string filePath, long? size, string originalFileName = null, string bucket = null ) { + if( filePath.IsEmpty() ) + throw new ArgumentNullException( nameof( filePath ) ); + FilePath = filePath; + Size = new FileSize( size.SafeValue() ); + FileName = System.IO.Path.GetFileName( filePath ); + Extension = System.IO.Path.GetExtension( FileName )?.TrimStart( '.' ); + OriginalFileName = originalFileName; + Bucket = bucket; + } + + /// + /// 文件路径 + /// + public string FilePath { get; } + /// + /// 文件名 + /// + public string FileName { get; } + /// + /// 扩展名 + /// + public string Extension { get; } + /// + /// 原始文件名 + /// + public string OriginalFileName { get; } + /// + /// 文件大小 + /// + public FileSize Size { get; } + /// + /// 存储桶名称 + /// + public string Bucket { get; } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/FileSize.cs b/src/Util.FileStorage.Abstractions/FileSize.cs new file mode 100644 index 000000000..ed5355565 --- /dev/null +++ b/src/Util.FileStorage.Abstractions/FileSize.cs @@ -0,0 +1,107 @@ +namespace Util.FileStorage; + +/// +/// 文件大小 +/// +public readonly struct FileSize { + /// + /// 文件字节长度 + /// + private readonly long _size; + + /// + /// 初始化文件大小 + /// + /// 文件大小 + /// 文件大小单位 + public FileSize( long size, FileSizeUnit unit = FileSizeUnit.Byte ) { + _size = GetSize( size, unit ); + } + + /// + /// 初始化文件大小 + /// + /// 文件大小 + /// 文件大小单位 + public FileSize( double size, FileSizeUnit unit = FileSizeUnit.Byte ) { + _size = Util.Helpers.Convert.ToLong( GetSize( size, unit ) ); + } + + /// + /// 获取文件大小 + /// + private static long GetSize( long size, FileSizeUnit unit ) { + switch ( unit ) { + case FileSizeUnit.K: + return size * 1024; + case FileSizeUnit.M: + return size * 1024 * 1024; + case FileSizeUnit.G: + return size * 1024 * 1024 * 1024; + default: + return size; + } + } + + /// + /// 获取文件大小 + /// + private static double GetSize( double size, FileSizeUnit unit ) { + switch( unit ) { + case FileSizeUnit.K: + return size * 1024; + case FileSizeUnit.M: + return size * 1024 * 1024; + case FileSizeUnit.G: + return size * 1024 * 1024 * 1024; + default: + return size; + } + } + + /// + /// 文件字节长度 + /// + public long Size => _size; + + /// + /// 获取文件大小,单位:字节 + /// + public int GetSize() { + return Util.Helpers.Convert.ToInt( Size ); + } + + /// + /// 获取文件大小,单位:K + /// + public double GetSizeByK() { + return Util.Helpers.Convert.ToDouble( _size / 1024.0, 2 ); + } + + /// + /// 获取文件大小,单位:M + /// + public double GetSizeByM() { + return Util.Helpers.Convert.ToDouble( _size / 1024.0 / 1024.0, 2 ); + } + + /// + /// 获取文件大小,单位:G + /// + public double GetSizeByG() { + return Util.Helpers.Convert.ToDouble( _size / 1024.0 / 1024.0 / 1024.0, 2 ); + } + + /// + /// 输出描述 + /// + public override string ToString() { + if ( _size >= 1024 * 1024 * 1024 ) + return $"{GetSizeByG()} {FileSizeUnit.G.Description()}"; + if ( _size >= 1024 * 1024 ) + return $"{GetSizeByM()} {FileSizeUnit.M.Description()}"; + if ( _size >= 1024 ) + return $"{GetSizeByK()} {FileSizeUnit.K.Description()}"; + return $"{_size} {FileSizeUnit.Byte.Description()}"; + } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/FileSizeUnit.cs b/src/Util.FileStorage.Abstractions/FileSizeUnit.cs new file mode 100644 index 000000000..ef1703e07 --- /dev/null +++ b/src/Util.FileStorage.Abstractions/FileSizeUnit.cs @@ -0,0 +1,27 @@ +namespace Util.FileStorage; + +/// +/// 文件大小单位 +/// +public enum FileSizeUnit { + /// + /// 字节 + /// + [Description( "B" )] + Byte, + /// + /// K字节 + /// + [Description( "KB" )] + K, + /// + /// M字节 + /// + [Description( "MB" )] + M, + /// + /// G字节 + /// + [Description( "GB" )] + G +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/FileStorageArgs.cs b/src/Util.FileStorage.Abstractions/FileStorageArgs.cs new file mode 100644 index 000000000..577d60fd9 --- /dev/null +++ b/src/Util.FileStorage.Abstractions/FileStorageArgs.cs @@ -0,0 +1,34 @@ +namespace Util.FileStorage; + +/// +/// 文件存储方法参数 +/// +public class FileStorageArgs { + /// + /// 初始化文件存储方法参数 + /// + /// 文件名 + public FileStorageArgs( string fileName ) { + FileName = fileName; + } + + /// + /// 文件名 + /// + public string FileName { get; } + + /// + /// 文件名处理策略 + /// + public string FileNamePolicy { get; set; } + + /// + /// 存储桶名称 + /// + public string BucketName { get; set; } + + /// + /// 存储桶名称处理策略 + /// + public string BucketNamePolicy { get; set; } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/FileStorageInfo.cs b/src/Util.FileStorage.Abstractions/FileStorageInfo.cs new file mode 100644 index 000000000..7a9e1c345 --- /dev/null +++ b/src/Util.FileStorage.Abstractions/FileStorageInfo.cs @@ -0,0 +1,43 @@ +namespace Util.FileStorage; + +/// +/// 文件信息 +/// +public class FileStorageInfo { + /// + /// 文件标识 + /// + public string Id { get; set; } + /// + /// 文件路径 + /// + public string FilePath { get; set; } + /// + /// 文件名,包含扩展名 + /// + public string FileName { get; set; } + /// + /// 文件名,不包含扩展名 + /// + public string FileNameWithoutExtension { get; set; } + /// + /// 扩展名 + /// + public string Extension { get; set; } + /// + /// 文件大小 + /// + public long Size { get; set; } + /// + /// 文件大小描述 + /// + public string SizeDescription { get; set; } + /// + /// 文件访问地址 + /// + public string Url { get; set; } + /// + /// 缩略图访问地址 + /// + public string ThumbUrl { get; set; } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/GenerateDownloadUrlArgs.cs b/src/Util.FileStorage.Abstractions/GenerateDownloadUrlArgs.cs new file mode 100644 index 000000000..6b302caf1 --- /dev/null +++ b/src/Util.FileStorage.Abstractions/GenerateDownloadUrlArgs.cs @@ -0,0 +1,18 @@ +namespace Util.FileStorage; + +/// +/// 生成下载Url方法参数 +/// +public class GenerateDownloadUrlArgs : FileStorageArgs { + /// + /// 初始化生成下载Url方法参数 + /// + /// 文件名 + public GenerateDownloadUrlArgs( string fileName ) : base( fileName ) { + } + + /// + /// 响应内容类型 + /// + public string ResponseContentType { get; set; } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/GenerateTempDownloadUrlArgs.cs b/src/Util.FileStorage.Abstractions/GenerateTempDownloadUrlArgs.cs new file mode 100644 index 000000000..af8772113 --- /dev/null +++ b/src/Util.FileStorage.Abstractions/GenerateTempDownloadUrlArgs.cs @@ -0,0 +1,23 @@ +namespace Util.FileStorage; + +/// +/// 生成临时下载Url方法参数 +/// +public class GenerateTempDownloadUrlArgs : FileStorageArgs { + /// + /// 初始化生成临时下载Url方法参数 + /// + /// 文件名 + public GenerateTempDownloadUrlArgs( string fileName ) : base( fileName ) { + } + + /// + /// 下载地址过期时间,单位:秒 + /// + public int? Expiration { get; set; } + + /// + /// 响应内容类型 + /// + public string ResponseContentType { get; set; } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/GenerateUploadUrlArgs.cs b/src/Util.FileStorage.Abstractions/GenerateUploadUrlArgs.cs new file mode 100644 index 000000000..132eee52f --- /dev/null +++ b/src/Util.FileStorage.Abstractions/GenerateUploadUrlArgs.cs @@ -0,0 +1,56 @@ +namespace Util.FileStorage; + +/// +/// 生成上传Url方法参数 +/// +public class GenerateUploadUrlArgs : FileStorageArgs { + /// + /// 请求头 + /// + private readonly Dictionary _headers; + + /// + /// 初始化生成上传Url方法参数 + /// + /// 文件名 + public GenerateUploadUrlArgs( string fileName ) : base( fileName ) { + _headers = new Dictionary(); + } + + /// + /// 设置内容类型 application/octet-stream + /// + public void SetOctetStream() { + SetContentType( "application/octet-stream" ); + } + + /// + /// 设置内容类型 + /// + public void SetContentType( string contentType ) { + AddHeader( "content-type", contentType ); + } + + /// + /// 添加请求头 + /// + /// 键 + /// 值 + public void AddHeader( string key,string value ) { + if ( _headers.ContainsKey( key ) ) + _headers.Remove( key ); + _headers.Add( key, value ); + } + + /// + /// 获取请求头 + /// + public IDictionary GetHeaders() { + return _headers; + } + + /// + /// 文件大小限制,单位:字节 + /// + public long SizeLimit { get; set; } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/GetFileStreamArgs.cs b/src/Util.FileStorage.Abstractions/GetFileStreamArgs.cs new file mode 100644 index 000000000..de079d6ca --- /dev/null +++ b/src/Util.FileStorage.Abstractions/GetFileStreamArgs.cs @@ -0,0 +1,13 @@ +namespace Util.FileStorage; + +/// +/// 获取文件流方法参数 +/// +public class GetFileStreamArgs : FileStorageArgs { + /// + /// 初始化获取文件流方法参数 + /// + /// 文件名 + public GetFileStreamArgs( string fileName ) : base( fileName ) { + } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/IBucketNameProcessor.cs b/src/Util.FileStorage.Abstractions/IBucketNameProcessor.cs new file mode 100644 index 000000000..bd08175f8 --- /dev/null +++ b/src/Util.FileStorage.Abstractions/IBucketNameProcessor.cs @@ -0,0 +1,12 @@ +namespace Util.FileStorage; + +/// +/// 存储桶名称处理器 +/// +public interface IBucketNameProcessor { + /// + /// 处理存储桶名称 + /// + /// 存储桶名称 + ProcessedName Process( string bucketName ); +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/IBucketNameProcessorFactory.cs b/src/Util.FileStorage.Abstractions/IBucketNameProcessorFactory.cs new file mode 100644 index 000000000..761d7838a --- /dev/null +++ b/src/Util.FileStorage.Abstractions/IBucketNameProcessorFactory.cs @@ -0,0 +1,12 @@ +namespace Util.FileStorage; + +/// +/// 存储桶名称处理器工厂 +/// +public interface IBucketNameProcessorFactory : ITransientDependency { + /// + /// 创建存储桶名称处理器 + /// + /// 存储桶名称处理策略 + IBucketNameProcessor CreateProcessor( string policy ); +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/IFileExtensionInspector.cs b/src/Util.FileStorage.Abstractions/IFileExtensionInspector.cs new file mode 100644 index 000000000..a46ee84ce --- /dev/null +++ b/src/Util.FileStorage.Abstractions/IFileExtensionInspector.cs @@ -0,0 +1,32 @@ +namespace Util.FileStorage; + +/// +/// 文件扩展名检查器 +/// +public interface IFileExtensionInspector : ISingletonDependency { + /// + /// 获取文件扩展名 + /// + /// 文件流 + string GetExtension( Stream stream ); + /// + /// 是否图片文件 + /// + /// 文件流 + bool IsImage( Stream stream ); + /// + /// 是否pdf文件 + /// + /// 文件流 + bool IsPdf( Stream stream ); + /// + /// 是否Office文件 + /// + /// 文件流 + bool IsOffice( Stream stream ); + /// + /// 是否视频文件 + /// + /// 文件流 + bool IsVideo( Stream stream ); +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/IFileNameFilter.cs b/src/Util.FileStorage.Abstractions/IFileNameFilter.cs new file mode 100644 index 000000000..b96b30c11 --- /dev/null +++ b/src/Util.FileStorage.Abstractions/IFileNameFilter.cs @@ -0,0 +1,12 @@ +namespace Util.FileStorage; + +/// +/// 文件名过滤器 +/// +public interface IFileNameFilter : ISingletonDependency { + /// + /// 过滤文件名 + /// + /// 文件名 + string Filter( string fileName ); +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/IFileNameProcessor.cs b/src/Util.FileStorage.Abstractions/IFileNameProcessor.cs new file mode 100644 index 000000000..456d22a50 --- /dev/null +++ b/src/Util.FileStorage.Abstractions/IFileNameProcessor.cs @@ -0,0 +1,12 @@ +namespace Util.FileStorage; + +/// +/// 文件名处理器 +/// +public interface IFileNameProcessor { + /// + /// 处理文件名 + /// + /// 文件名 + ProcessedName Process( string fileName ); +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/IFileNameProcessorFactory.cs b/src/Util.FileStorage.Abstractions/IFileNameProcessorFactory.cs new file mode 100644 index 000000000..2b42493c9 --- /dev/null +++ b/src/Util.FileStorage.Abstractions/IFileNameProcessorFactory.cs @@ -0,0 +1,12 @@ +namespace Util.FileStorage; + +/// +/// 文件名处理器工厂 +/// +public interface IFileNameProcessorFactory : ITransientDependency { + /// + /// 创建文件名处理器 + /// + /// 文件名处理策略 + IFileNameProcessor CreateProcessor( string policy ); +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/IFileStore.cs b/src/Util.FileStorage.Abstractions/IFileStore.cs new file mode 100644 index 000000000..0e4c17ec5 --- /dev/null +++ b/src/Util.FileStorage.Abstractions/IFileStore.cs @@ -0,0 +1,119 @@ +namespace Util.FileStorage; + +/// +/// 文件存储服务 +/// +public interface IFileStore : ILocalFileStore { + /// + /// 获取存储桶名称列表 + /// + /// 取消令牌 + Task> GetBucketNamesAsync( CancellationToken cancellationToken = default ); + /// + /// 存储桶是否存在 + /// + /// 存储桶名称 + /// 存储桶名称处理策略 + /// 取消令牌 + Task BucketExistsAsync( string bucketName,string policy = null, CancellationToken cancellationToken = default ); + /// + /// 创建存储桶 + /// + /// 存储桶名称 + /// 存储桶名称处理策略 + /// 取消令牌 + Task CreateBucketAsync( string bucketName, string policy = null, CancellationToken cancellationToken = default ); + /// + /// 删除存储桶 + /// + /// 存储桶名称 + /// 存储桶名称处理策略 + /// 取消令牌 + Task DeleteBucketAsync( string bucketName, string policy = null, CancellationToken cancellationToken = default ); + /// + /// 文件是否存在 + /// + /// 参数 + /// 取消令牌 + Task FileExistsAsync( FileExistsArgs args, CancellationToken cancellationToken = default ); + /// + /// 获取文件流 + /// + /// 参数 + /// 取消令牌 + Task GetFileStreamAsync( GetFileStreamArgs args, CancellationToken cancellationToken = default ); + /// + /// 保存文件 + /// + /// 参数 + /// 取消令牌 + Task SaveFileAsync( SaveFileArgs args, CancellationToken cancellationToken = default ); + /// + /// 下载远程文件并保存到文件存储 + /// + /// 参数 + /// 取消令牌 + Task SaveFileByUrlAsync( SaveFileByUrlArgs args, CancellationToken cancellationToken = default ); + /// + /// 复制文件 + /// + /// 源文件参数 + /// 目标文件参数 + /// 取消令牌 + Task CopyFileAsync( FileStorageArgs sourceArgs, FileStorageArgs destinationArgs, CancellationToken cancellationToken = default ); + /// + /// 移动文件 + /// + /// 源文件参数 + /// 目标文件参数 + /// 取消令牌 + Task MoveFileAsync( FileStorageArgs sourceArgs, FileStorageArgs destinationArgs, CancellationToken cancellationToken = default ); + /// + /// 删除文件 + /// + /// 参数 + /// 取消令牌 + Task DeleteFileAsync( DeleteFileArgs args, CancellationToken cancellationToken = default ); + /// + /// 生成客户端下载Url + /// + /// 文件名 + /// 取消令牌 + Task GenerateDownloadUrlAsync( string fileName, CancellationToken cancellationToken = default ); + /// + /// 生成客户端下载Url + /// + /// 参数 + /// 取消令牌 + Task GenerateDownloadUrlAsync( GenerateDownloadUrlArgs args, CancellationToken cancellationToken = default ); + /// + /// 生成客户端临时下载Url + /// + /// 文件名 + /// 取消令牌 + Task GenerateTempDownloadUrlAsync( string fileName, CancellationToken cancellationToken = default ); + /// + /// 生成客户端临时下载Url + /// + /// 参数 + /// 取消令牌 + Task GenerateTempDownloadUrlAsync( GenerateTempDownloadUrlArgs args, CancellationToken cancellationToken = default ); + /// + /// 生成客户端直传Url + /// + /// 文件名 + /// 文件名处理策略 + /// 取消令牌 + Task GenerateUploadUrlAsync( string fileName, string policy = null, CancellationToken cancellationToken = default ); + /// + /// 生成客户端直传Url + /// + /// 参数 + /// 取消令牌 + Task GenerateUploadUrlAsync( GenerateUploadUrlArgs args, CancellationToken cancellationToken = default ); + /// + /// 清空存储桶和文件 + /// + /// 取消令牌 + Task ClearAsync( CancellationToken cancellationToken = default ); +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/ILocalFileStore.cs b/src/Util.FileStorage.Abstractions/ILocalFileStore.cs new file mode 100644 index 000000000..96f8b2c71 --- /dev/null +++ b/src/Util.FileStorage.Abstractions/ILocalFileStore.cs @@ -0,0 +1,56 @@ +namespace Util.FileStorage; + +/// +/// 本地文件存储服务 +/// +public interface ILocalFileStore { + /// + /// 文件是否存在 + /// + /// 文件名 + /// 文件名处理策略 + /// 取消令牌 + Task FileExistsAsync( string fileName, string policy = null, CancellationToken cancellationToken = default ); + /// + /// 获取文件流 + /// + /// 文件名 + /// 取消令牌 + Task GetFileStreamAsync( string fileName, CancellationToken cancellationToken = default ); + /// + /// 保存文件 + /// + /// 文件流 + /// 文件名,包含扩展名 + /// 文件名处理策略 + /// 取消令牌 + Task SaveFileAsync( Stream stream, string fileName, string policy = null, CancellationToken cancellationToken = default ); + /// + /// 下载远程文件并保存到文件存储 + /// + /// 远程下载地址 + /// 文件名,包含扩展名 + /// 文件名处理策略 + /// 取消令牌 + Task SaveFileByUrlAsync( string url, string fileName, string policy = null, CancellationToken cancellationToken = default ); + /// + /// 复制文件 + /// + /// 源文件名 + /// 目标文件名 + /// 取消令牌 + Task CopyFileAsync( string sourceFileName, string destinationFileName, CancellationToken cancellationToken = default ); + /// + /// 移动文件 + /// + /// 源文件名 + /// 目标文件名 + /// 取消令牌 + Task MoveFileAsync( string sourceFileName, string destinationFileName, CancellationToken cancellationToken = default ); + /// + /// 删除文件 + /// + /// 文件名 + /// 取消令牌 + Task DeleteFileAsync( string fileName, CancellationToken cancellationToken = default ); +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/ProcessedName.cs b/src/Util.FileStorage.Abstractions/ProcessedName.cs new file mode 100644 index 000000000..ce797ca7d --- /dev/null +++ b/src/Util.FileStorage.Abstractions/ProcessedName.cs @@ -0,0 +1,28 @@ +namespace Util.FileStorage; + +/// +/// 已处理过的名称 +/// +public class ProcessedName { + /// + /// 初始化已处理过的名称 + /// + /// 名称 + /// 原始名称 + public ProcessedName( string name, string originalName = null ) { + if ( name.IsEmpty() ) + throw new ArgumentNullException( name ); + Name = name; + OriginalName = originalName ?? name; + } + + /// + /// 名称 + /// + public string Name { get; } + + /// + /// 原始名称 + /// + public string OriginalName { get; } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/SaveFileArgs.cs b/src/Util.FileStorage.Abstractions/SaveFileArgs.cs new file mode 100644 index 000000000..d65b2d3d6 --- /dev/null +++ b/src/Util.FileStorage.Abstractions/SaveFileArgs.cs @@ -0,0 +1,20 @@ +namespace Util.FileStorage; + +/// +/// 保存文件方法参数 +/// +public class SaveFileArgs : FileStorageArgs { + /// + /// 初始化保存文件方法参数 + /// + /// 文件名 + /// 文件流 + public SaveFileArgs( string fileName, Stream stream ) : base( fileName ) { + Stream = stream; + } + + /// + /// 文件流 + /// + public Stream Stream { get; } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/SaveFileByUrlArgs.cs b/src/Util.FileStorage.Abstractions/SaveFileByUrlArgs.cs new file mode 100644 index 000000000..51311c91a --- /dev/null +++ b/src/Util.FileStorage.Abstractions/SaveFileByUrlArgs.cs @@ -0,0 +1,20 @@ +namespace Util.FileStorage; + +/// +/// 下载远程文件并保存方法参数 +/// +public class SaveFileByUrlArgs : FileStorageArgs { + /// + /// 初始化下载远程文件并保存方法参数 + /// + /// 文件名 + /// 远程文件地址 + public SaveFileByUrlArgs( string fileName, string url ) : base( fileName ) { + Url = url; + } + + /// + /// 远程文件地址 + /// + public string Url { get; } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Abstractions/Usings.cs b/src/Util.FileStorage.Abstractions/Usings.cs new file mode 100644 index 000000000..a14c45150 --- /dev/null +++ b/src/Util.FileStorage.Abstractions/Usings.cs @@ -0,0 +1,11 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Threading; +global using System.IO; +global using System.ComponentModel; +global using System.Linq; +global using Microsoft.Extensions.Options; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Util.Dependency; diff --git a/src/Util.FileStorage.Aliyun/04-Util.FileStorage.Aliyun.csproj b/src/Util.FileStorage.Aliyun/04-Util.FileStorage.Aliyun.csproj new file mode 100644 index 000000000..f132855af --- /dev/null +++ b/src/Util.FileStorage.Aliyun/04-Util.FileStorage.Aliyun.csproj @@ -0,0 +1,37 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.FileStorage.Aliyun + Util.FileStorage.Aliyun + Util.FileStorage.Aliyun是Util应用框架基于阿里云对象存储OSS操作类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.FileStorage.Aliyun.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.FileStorage.Aliyun.xml + + + + + True + False + + + + + + + + + + + + + diff --git a/src/Util.FileStorage.Aliyun/AliyunFileStore.cs b/src/Util.FileStorage.Aliyun/AliyunFileStore.cs new file mode 100644 index 000000000..ac48e1d83 --- /dev/null +++ b/src/Util.FileStorage.Aliyun/AliyunFileStore.cs @@ -0,0 +1,566 @@ +namespace Util.FileStorage.Aliyun; + +/// +/// 阿里云对象存储服务 +/// +public class AliyunFileStore : IAliyunOssFileStore { + + #region 字段 + + /// + /// 配置提供器 + /// + private readonly IAliyunOssConfigProvider _configProvider; + /// + /// 存储桶名称处理器工厂 + /// + private readonly IBucketNameProcessorFactory _bucketNameProcessorFactory; + /// + /// 文件名处理器工厂 + /// + private readonly IFileNameProcessorFactory _fileNameProcessorFactory; + /// + /// 阿里云对象存储配置 + /// + private AliyunOssOptions _config; + /// + /// 阿里云对象存储客户端 + /// + private IOss _client; + /// + /// Http操作 + /// + private readonly IHttpClient _httpClient; + + #endregion + + #region 构造方法 + + /// + /// 初始化阿里云对象存储服务 + /// + /// 配置提供器 + /// 存储桶名称处理器工厂 + /// 文件名处理器工厂 + /// Http操作 + public AliyunFileStore( IAliyunOssConfigProvider configProvider, + IBucketNameProcessorFactory bucketNameProcessorFactory, IFileNameProcessorFactory fileNameProcessorFactory, + IHttpClient httpClient ) { + _configProvider = configProvider ?? throw new ArgumentNullException( nameof( configProvider ) ); + _bucketNameProcessorFactory = bucketNameProcessorFactory ?? throw new ArgumentNullException( nameof( bucketNameProcessorFactory ) ); + _fileNameProcessorFactory = fileNameProcessorFactory ?? throw new ArgumentNullException( nameof( fileNameProcessorFactory ) ); + _httpClient = httpClient ?? throw new ArgumentNullException( nameof( httpClient ) ); + } + + #endregion + + #region InitConfig + + /// + /// 初始化配置 + /// + protected virtual async Task InitConfig() { + if( _config != null ) + return; + _config = await _configProvider.GetConfigAsync(); + } + + #endregion + + #region GetClient + + /// + /// 获取阿里云对象存储客户端 + /// + public virtual async Task GetClient() { + if( _client != null ) + return _client; + await InitConfig(); + _client = new OssClient( GetEndpoint(), _config.AccessKeyId, _config.AccessKeySecret ); + return _client; + } + + /// + /// 获取服务端点 + /// + protected virtual string GetEndpoint() { + var endpoint = _config.Endpoint; + if( endpoint.StartsWith( "http" ) ) + return endpoint; + return $"https://{endpoint}"; + } + + #endregion + + #region GetBucketNamesAsync + + /// + public virtual async Task> GetBucketNamesAsync( CancellationToken cancellationToken = default ) { + var client = await GetClient(); + var result = client.ListBuckets(); + return result.Select( t => t.Name ).ToList(); + } + + #endregion + + #region BucketExistsAsync + + /// + public virtual async Task BucketExistsAsync( string bucketName, string policy = null, CancellationToken cancellationToken = default ) { + if( bucketName.IsEmpty() ) + return false; + var processedBucketName = ProcessBucketName( bucketName, policy ); + return await BucketExistsAsync( processedBucketName, cancellationToken ); + } + + /// + /// 处理存储桶名称 + /// + protected ProcessedName ProcessBucketName( string bucketName, string policy ) { + var processor = _bucketNameProcessorFactory.CreateProcessor( policy ); + return processor.Process( bucketName ); + } + + /// + /// 存储桶是否存在 + /// + protected virtual async Task BucketExistsAsync( ProcessedName bucketName, CancellationToken cancellationToken = default ) { + var client = await GetClient(); + return client.DoesBucketExist( bucketName.Name ); + } + + #endregion + + #region CreateBucketAsync + + /// + public virtual async Task CreateBucketAsync( string bucketName, string policy = null, CancellationToken cancellationToken = default ) { + if( bucketName.IsEmpty() ) + return; + var args = new CreateBucketArgs( bucketName ); + await CreateBucketAsync( args, cancellationToken ); + } + + /// + public async Task CreateBucketAsync( CreateBucketArgs args, CancellationToken cancellationToken = default ) { + var bucketName = ProcessBucketName( args.BucketName, args.BucketNamePolicy ); + var client = await GetClient(); + var exists = await BucketExistsAsync( bucketName, cancellationToken ); + if( exists ) + return; + var request = new CreateBucketRequest( bucketName.Name ) { + ACL = args.Acl, + StorageClass = args.StorageClass, + DataRedundancyType = args.DataRedundancyType + }; + client.CreateBucket( request ); + } + + /// + /// 创建存储桶 + /// + protected virtual async Task CreateBucketAsync( ProcessedName bucketName, CancellationToken cancellationToken = default ) { + var client = await GetClient(); + var exists = await BucketExistsAsync( bucketName, cancellationToken ); + if( exists ) + return; + var request = new CreateBucketRequest( bucketName.Name ); + client.CreateBucket( request ); + } + + #endregion + + #region DeleteBucketAsync + + /// + public async Task DeleteBucketAsync( string bucketName, string policy = null, CancellationToken cancellationToken = default ) { + if( bucketName.IsEmpty() ) + return; + var processedBucketName = ProcessBucketName( bucketName, policy ); + await DeleteBucketAsync( processedBucketName, cancellationToken ); + } + + /// + /// 删除存储桶 + /// + protected virtual async Task DeleteBucketAsync( ProcessedName bucketName, CancellationToken cancellationToken = default ) { + var client = await GetClient(); + var exists = await BucketExistsAsync( bucketName, cancellationToken ); + if( exists == false ) + return; + client.DeleteBucket( bucketName.Name ); + } + + #endregion + + #region FileExistsAsync + + /// + public async Task FileExistsAsync( string fileName, string policy = null, CancellationToken cancellationToken = default ) { + var args = new FileExistsArgs( fileName ) { FileNamePolicy = policy }; + return await FileExistsAsync( args, cancellationToken ); + } + + /// + public async Task FileExistsAsync( FileExistsArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var processedBucketName = await ProcessBucketName( args ); + return await FileExistsAsync( processedFileName, processedBucketName, cancellationToken ); + } + + /// + /// 处理文件名 + /// + protected ProcessedName ProcessFileName( FileStorageArgs args ) { + if( args.FileName.IsEmpty() ) + throw new ArgumentNullException( nameof( args.FileName ) ); + var processor = _fileNameProcessorFactory.CreateProcessor( args.FileNamePolicy ); + return processor.Process( args.FileName ); + } + + /// + /// 处理存储桶名称 + /// + protected async Task ProcessBucketName( FileStorageArgs args ) { + await InitConfig(); + args.BucketName ??= _config.DefaultBucketName; + if( args.BucketName.IsEmpty() ) + throw new ArgumentNullException( nameof( args.BucketName ) ); + var processor = _bucketNameProcessorFactory.CreateProcessor( args.BucketNamePolicy ); + return processor.Process( args.BucketName ); + } + + /// + /// 文件是否存在 + /// + protected async Task FileExistsAsync( ProcessedName fileName, ProcessedName bucketName, CancellationToken cancellationToken = default ) { + var client = await GetClient(); + return client.DoesObjectExist( bucketName.Name, fileName.Name ); + } + + #endregion + + #region GetFileStreamAsync + + /// + public async Task GetFileStreamAsync( string fileName, CancellationToken cancellationToken = default ) { + var args = new GetFileStreamArgs( fileName ); + return await GetFileStreamAsync( args, cancellationToken ); + } + + /// + public async Task GetFileStreamAsync( GetFileStreamArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var processedBucketName = await ProcessBucketName( args ); + return await GetFileStreamAsync( processedFileName, processedBucketName, cancellationToken ); + } + + /// + /// 获取文件流 + /// + protected async Task GetFileStreamAsync( ProcessedName fileName, ProcessedName bucketName, CancellationToken cancellationToken = default ) { + var client = await GetClient(); + var exists = await FileExistsAsync( fileName, bucketName, cancellationToken ); + if( exists == false ) + return null; + var memoryStream = new MemoryStream(); + var result = client.GetObject( bucketName.Name, fileName.Name ); + await using var stream = result.Content; + if( stream == null ) + return memoryStream; + await stream.CopyToAsync( memoryStream, cancellationToken ).ConfigureAwait( false ); + return memoryStream; + } + + #endregion + + #region SaveFileAsync + + /// + public virtual async Task SaveFileAsync( Stream stream, string fileName, string policy = null, CancellationToken cancellationToken = default ) { + var args = new SaveFileArgs( fileName, stream ) { FileNamePolicy = policy }; + return await SaveFileAsync( args, cancellationToken ); + } + + /// + public virtual async Task SaveFileAsync( SaveFileArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var processedBucketName = await ProcessBucketName( args ); + return await SaveFileAsync( args.Stream, processedFileName, processedBucketName, cancellationToken ); + } + + /// + /// 保存文件 + /// + public virtual async Task SaveFileAsync( Stream stream, ProcessedName fileName, ProcessedName bucketName, CancellationToken cancellationToken = default ) { + stream.CheckNull( nameof( stream ) ); + var client = await GetClient(); + var result = client.PutObject( bucketName.Name, fileName.Name, stream ); + if( result.HttpStatusCode == HttpStatusCode.OK ) + return new FileResult( fileName.Name, stream.Length, fileName.OriginalName, bucketName.Name ); + throw new InvalidOperationException( Util.Helpers.File.ReadToString( result.ResponseStream ) ); + } + + #endregion + + #region SaveFileByUrlAsync + + /// + public async Task SaveFileByUrlAsync( string url, string fileName, string policy = null, CancellationToken cancellationToken = default ) { + var args = new SaveFileByUrlArgs( fileName, url ) { FileNamePolicy = policy }; + return await SaveFileByUrlAsync( args, cancellationToken ); + } + + /// + public async Task SaveFileByUrlAsync( SaveFileByUrlArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var processedBucketName = await ProcessBucketName( args ); + var bytes = await _httpClient.Get( args.Url ).GetStreamAsync( cancellationToken ); + await using var stream = new MemoryStream( bytes ); + return await SaveFileAsync( stream, processedFileName, processedBucketName, cancellationToken ); + } + + #endregion + + #region CopyFileAsync + + /// + public async Task CopyFileAsync( string sourceFileName, string destinationFileName, CancellationToken cancellationToken = default ) { + var sourceArgs = new FileStorageArgs( sourceFileName ); + var destinationArgs = new FileStorageArgs( destinationFileName ); + await CopyFileAsync( sourceArgs, destinationArgs, cancellationToken ); + } + + /// + public async Task CopyFileAsync( FileStorageArgs sourceArgs, FileStorageArgs destinationArgs, CancellationToken cancellationToken = default ) { + var processedSourceBucketName = await ProcessBucketName( sourceArgs ); + var processedSourceFileName = ProcessFileName( sourceArgs ); + var processedDestinationBucketName = await ProcessBucketName( destinationArgs ); + var processedDestinationFileName = ProcessFileName( destinationArgs ); + var request = new CopyObjectRequest( processedSourceBucketName.Name, processedSourceFileName.Name, processedDestinationBucketName.Name, processedDestinationFileName.Name ); + var client = await GetClient(); + client.CopyObject( request ); + } + + #endregion + + #region MoveFileAsync + + /// + public async Task MoveFileAsync( string sourceFileName, string destinationFileName, CancellationToken cancellationToken = default ) { + var sourceArgs = new FileStorageArgs( sourceFileName ); + var destinationArgs = new FileStorageArgs( destinationFileName ); + await MoveFileAsync( sourceArgs, destinationArgs, cancellationToken ); + } + + /// + public async Task MoveFileAsync( FileStorageArgs sourceArgs, FileStorageArgs destinationArgs, CancellationToken cancellationToken = default ) { + await CopyFileAsync( sourceArgs, destinationArgs, cancellationToken ); + var processedSourceFileName = ProcessFileName( sourceArgs ); + var processedSourceBucketName = await ProcessBucketName( sourceArgs ); + await DeleteFileAsync( processedSourceFileName, processedSourceBucketName, cancellationToken ); + } + + #endregion + + #region DeleteFileAsync + + /// + public async Task DeleteFileAsync( string fileName,CancellationToken cancellationToken = default ) { + var args = new DeleteFileArgs( fileName ); + await DeleteFileAsync( args, cancellationToken ); + } + + /// + public async Task DeleteFileAsync( DeleteFileArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var processedBucketName = await ProcessBucketName( args ); + await DeleteFileAsync( processedFileName, processedBucketName, cancellationToken ); + } + + /// + /// 删除文件 + /// + /// 文件名 + /// 存储桶名称 + /// 取消令牌 + protected async Task DeleteFileAsync( ProcessedName fileName, ProcessedName bucketName, CancellationToken cancellationToken = default ) { + var client = await GetClient(); + var exists = await FileExistsAsync( fileName, bucketName, cancellationToken ); + if( exists == false ) + return; + client.DeleteObject( bucketName.Name, fileName.Name ); + } + + #endregion + + #region GenerateDownloadUrlAsync + + /// + public async Task GenerateDownloadUrlAsync( string fileName, CancellationToken cancellationToken = default ) { + var args = new GenerateDownloadUrlArgs( fileName ); + return await GenerateDownloadUrlAsync( args, cancellationToken ); + } + + /// + public virtual async Task GenerateDownloadUrlAsync( GenerateDownloadUrlArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var processedBucketName = await ProcessBucketName( args ); + var endpoint = await GetEndPoint( processedBucketName.Name ); + return Util.Helpers.Common.JoinPath( endpoint, processedFileName.Name ); + } + + /// + /// 获取端点 + /// + protected virtual async Task GetEndPoint(string bucketName ) { + await InitConfig(); + var endpoint = _config.Endpoint.RemoveStart( "http://" ).RemoveStart( "https://" ); + return $"https://{bucketName}.{endpoint}"; + } + + #endregion + + #region GenerateTempDownloadUrlAsync + + /// + public async Task GenerateTempDownloadUrlAsync( string fileName, CancellationToken cancellationToken = default ) { + var args = new GenerateTempDownloadUrlArgs( fileName ); + return await GenerateTempDownloadUrlAsync( args, cancellationToken ); + } + + /// + public virtual async Task GenerateTempDownloadUrlAsync( GenerateTempDownloadUrlArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var processedBucketName = await ProcessBucketName( args ); + var expiration = args.Expiration ?? _config.DownloadUrlExpiration; + return await GenerateTempDownloadUrlAsync( processedFileName, processedBucketName, expiration, cancellationToken ); + } + + /// + /// 生成临时下载Url + /// + protected virtual async Task GenerateTempDownloadUrlAsync( ProcessedName fileName, ProcessedName bucketName,int expiration, CancellationToken cancellationToken = default ) { + var client = await GetClient(); + var request = new GeneratePresignedUriRequest( bucketName.Name, fileName.Name, SignHttpMethod.Get ) { + Expiration = DateTime.Now.AddSeconds( expiration ) + }; + var result = client.GeneratePresignedUri( request ); + return result.AbsoluteUri; + } + + #endregion + + #region GenerateUploadUrlAsync + + /// + public async Task GenerateUploadUrlAsync( string fileName, string policy = null, CancellationToken cancellationToken = default ) { + var args = new GenerateUploadUrlArgs( fileName ) { FileNamePolicy = policy }; + return await GenerateUploadUrlAsync( args, cancellationToken ); + } + + /// + public async Task GenerateUploadUrlAsync( GenerateUploadUrlArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var processedBucketName = await ProcessBucketName( args ); + return await GenerateUploadUrlAsync( processedFileName, processedBucketName, args.SizeLimit ); + } + + /// + /// 生成直传Url + /// + protected async Task GenerateUploadUrlAsync( ProcessedName fileName, ProcessedName bucketName, long sizeLimit ) { + await InitConfig(); + var url = CreateGenerateUploadHost( bucketName.Name ); + var data = await CreateGenerateUploadData( fileName, bucketName, sizeLimit ); + return new DirectUploadParam( fileName.Name, url, data, fileName.OriginalName, bucketName.Name ); + } + + /// + /// 创建直传域名 + /// + protected string CreateGenerateUploadHost( string bucketName ) { + var result = new StringBuilder(); + var endpoint = _config.Endpoint.RemoveStart( "https://" ); + endpoint = endpoint.RemoveStart( "http://" ); + result.Append( "https://" ); + result.Append( bucketName ); + result.Append( '.' ); + result.Append( endpoint ); + return result.ToString(); + } + + /// + /// 创建直传数据 + /// + protected async Task CreateGenerateUploadData( ProcessedName fileName, ProcessedName bucketName, long sizeLimit ) { + var policy = await GetPostPolicy( bucketName, sizeLimit ); + var signature = ComputeSignature( _config.AccessKeySecret, policy ); + return new DirectUploadData( fileName.Name, policy, _config.AccessKeyId, signature ); + } + + /// + /// 获取Post策略 + /// + protected virtual async Task GetPostPolicy( ProcessedName bucketName,long sizeLimit ) { + var client = await GetClient(); + var expiration = DateTime.Now.AddSeconds( _config.UploadUrlExpiration ); + var policy = new PolicyConditions(); + policy.AddConditionItem( "bucket", bucketName.Name ); + if( sizeLimit > 0) + policy.AddConditionItem( "content-length-range", 1,sizeLimit ); + var postPolicy = client.GeneratePostPolicy( expiration, policy ); + return Util.Helpers.Convert.ToBase64( postPolicy ); + } + + /// + /// 计算签名 + /// + protected virtual string ComputeSignature( string key, string data ) { + using var algorithm = new HMACSHA1(); + algorithm.Key = Encoding.UTF8.GetBytes( key.ToCharArray() ); + return Convert.ToBase64String( algorithm.ComputeHash( Encoding.UTF8.GetBytes( data.ToCharArray() ) ) ); + } + + #endregion + + #region ClearAsync + + /// + public async Task ClearAsync( CancellationToken cancellationToken = default ) { + var buckets = await GetBucketNamesAsync( cancellationToken ); + if( buckets.Count == 0 ) + return; + foreach( var bucket in buckets ) { + await ClearBucket( bucket ); + } + } + + /// + /// 清空存储桶 + /// + private async Task ClearBucket( string bucket ) { + var client = await GetClient(); + var listObjectsRequest = new ListObjectsRequest( bucket ) { + MaxKeys = 100, + }; + var objects = client.ListObjects( listObjectsRequest ); + if( objects.ObjectSummaries.Any() ) { + var deleteObjectsRequest = new DeleteObjectsRequest( bucket, objects.ObjectSummaries.Select( t => t.Key ).ToList(), true ); + client.DeleteObjects( deleteObjectsRequest ); + await ClearBucket( bucket ); + return; + } + client.DeleteBucket( bucket ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.FileStorage.Aliyun/AliyunOssConfigProvider.cs b/src/Util.FileStorage.Aliyun/AliyunOssConfigProvider.cs new file mode 100644 index 000000000..5491f0066 --- /dev/null +++ b/src/Util.FileStorage.Aliyun/AliyunOssConfigProvider.cs @@ -0,0 +1,34 @@ +namespace Util.FileStorage.Aliyun; + +/// +/// 阿里云对象存储配置提供器 +/// +public class AliyunOssConfigProvider : IAliyunOssConfigProvider { + /// + /// 阿里云对象存储配置 + /// + private readonly AliyunOssOptions _options; + + /// + /// 初始化阿里云对象存储配置提供器 + /// + /// 阿里云对象存储配置 + public AliyunOssConfigProvider( IOptions options ) : this( options.Value ) { + } + + /// + /// 初始化阿里云对象存储配置提供器 + /// + /// 阿里云对象存储配置 + public AliyunOssConfigProvider( AliyunOssOptions options ) { + options.CheckNull( nameof( options ) ); + _options = options; + } + + /// + /// 获取配置 + /// + public Task GetConfigAsync() { + return Task.FromResult( _options ); + } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Aliyun/AliyunOssOptions.cs b/src/Util.FileStorage.Aliyun/AliyunOssOptions.cs new file mode 100644 index 000000000..3de7ea388 --- /dev/null +++ b/src/Util.FileStorage.Aliyun/AliyunOssOptions.cs @@ -0,0 +1,31 @@ +namespace Util.FileStorage.Aliyun; + +/// +/// 阿里云对象存储配置 +/// +public class AliyunOssOptions { + /// + /// 存储服务地址 + /// + public string Endpoint { get; set; } + /// + /// 访问密钥的Id + /// + public string AccessKeyId { get; set; } + /// + /// 访问密钥的密码 + /// + public string AccessKeySecret { get; set; } + /// + /// 默认存储桶名称 + /// + public string DefaultBucketName { get; set; } + /// + /// 上传地址过期时间,单位:秒,默认值: 3600 + /// + public int UploadUrlExpiration { get; set; } = 3600; + /// + /// 下载地址过期时间,单位:秒,默认值: 3600 + /// + public int DownloadUrlExpiration { get; set; } = 3600; +} \ No newline at end of file diff --git a/src/Util.FileStorage.Aliyun/AppBuilderExtensions.cs b/src/Util.FileStorage.Aliyun/AppBuilderExtensions.cs new file mode 100644 index 000000000..73fdb1c49 --- /dev/null +++ b/src/Util.FileStorage.Aliyun/AppBuilderExtensions.cs @@ -0,0 +1,30 @@ +namespace Util.FileStorage.Aliyun; + +/// +/// 阿里云对象存储操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置阿里云对象存储操作 + /// + /// 应用生成器 + public static IAppBuilder AddAliyunOss( this IAppBuilder builder ) { + return builder.AddAliyunOss( null ); + } + + /// + /// 配置阿里云对象存储操作 + /// + /// 应用生成器 + /// 阿里云对象存储配置操作 + public static IAppBuilder AddAliyunOss( this IAppBuilder builder, Action setupAction ) { + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + if ( setupAction != null ) + services.Configure( setupAction ); + services.TryAddTransient(); + services.TryAddTransient(); + } ); + return builder; + } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Aliyun/CreateBucketArgs.cs b/src/Util.FileStorage.Aliyun/CreateBucketArgs.cs new file mode 100644 index 000000000..efc2018fe --- /dev/null +++ b/src/Util.FileStorage.Aliyun/CreateBucketArgs.cs @@ -0,0 +1,29 @@ +namespace Util.FileStorage.Aliyun; + +/// +/// 创建存储桶参数 +/// +public class CreateBucketArgs : FileStorageArgs { + /// + /// 初始化创建存储桶参数 + /// + /// 存储桶名称 + public CreateBucketArgs( string bucketName ) : base( null ) { + BucketName = bucketName; + } + + /// + /// 访问权限 + /// + public CannedAccessControlList Acl { get; set; } = CannedAccessControlList.Private; + + /// + /// 存储类型 + /// + public StorageClass StorageClass { get; set; } = StorageClass.Standard; + + /// + /// 数据冗余类型 + /// + public DataRedundancyType DataRedundancyType { get; set; } = DataRedundancyType.LRS; +} \ No newline at end of file diff --git a/src/Util.FileStorage.Aliyun/DirectUploadData.cs b/src/Util.FileStorage.Aliyun/DirectUploadData.cs new file mode 100644 index 000000000..609f3d946 --- /dev/null +++ b/src/Util.FileStorage.Aliyun/DirectUploadData.cs @@ -0,0 +1,48 @@ +namespace Util.FileStorage.Aliyun; + +/// +/// 客户端直传数据 +/// +public class DirectUploadData { + /// + /// 初始化客户端直传数据 + /// + /// 文件标识 + /// 上传策略 + /// AccessKeyId + /// 签名 + /// 成功操作状态码,默认值: 200 + public DirectUploadData( string key, string policy, string ossAccessKeyId,string signature,string successActionStatus = "200" ) { + Key = key; + Policy = policy; + OssAccessKeyId = ossAccessKeyId; + Signature = signature; + SuccessActionStatus = successActionStatus; + } + + /// + /// 文件标识,使用文件完整路径,范例: a/b/c.jpg + /// + [JsonPropertyName("key")] + public string Key { get; } + /// + /// 上传策略 + /// + [JsonPropertyName( "policy" )] + public string Policy { get; } + /// + /// AccessKeyId + /// + [JsonPropertyName( "OSSAccessKeyId" )] + public string OssAccessKeyId { get; } + /// + /// 签名 + /// + [JsonPropertyName( "Signature" )] + public string Signature { get; } + /// + /// 成功操作状态码 + /// + [JsonPropertyName( "success_action_status" )] + public string SuccessActionStatus { get; } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Aliyun/IAliyunOssConfigProvider.cs b/src/Util.FileStorage.Aliyun/IAliyunOssConfigProvider.cs new file mode 100644 index 000000000..d5f1e338d --- /dev/null +++ b/src/Util.FileStorage.Aliyun/IAliyunOssConfigProvider.cs @@ -0,0 +1,11 @@ +namespace Util.FileStorage.Aliyun; + +/// +/// 阿里云对象存储配置提供器 +/// +public interface IAliyunOssConfigProvider : ITransientDependency { + /// + /// 获取配置 + /// + Task GetConfigAsync(); +} \ No newline at end of file diff --git a/src/Util.FileStorage.Aliyun/IAliyunOssFileStore.cs b/src/Util.FileStorage.Aliyun/IAliyunOssFileStore.cs new file mode 100644 index 000000000..61c21f137 --- /dev/null +++ b/src/Util.FileStorage.Aliyun/IAliyunOssFileStore.cs @@ -0,0 +1,13 @@ +namespace Util.FileStorage.Aliyun; + +/// +/// 阿里云对象存储服务 +/// +public interface IAliyunOssFileStore : IFileStore { + /// + /// 创建存储桶 + /// + /// 创建存储桶参数 + /// 取消令牌 + Task CreateBucketAsync( CreateBucketArgs args, CancellationToken cancellationToken = default ); +} \ No newline at end of file diff --git a/src/Util.FileStorage.Aliyun/Usings.cs b/src/Util.FileStorage.Aliyun/Usings.cs new file mode 100644 index 000000000..7349bc315 --- /dev/null +++ b/src/Util.FileStorage.Aliyun/Usings.cs @@ -0,0 +1,17 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using System.IO; +global using System.Net; +global using System.Text.Json.Serialization; +global using System.Security.Cryptography; +global using System.Text; +global using Microsoft.Extensions.Options; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Aliyun.OSS; +global using Util.Dependency; +global using Util.Http; +global using Util.Configs; \ No newline at end of file diff --git a/src/Util.FileStorage.All/05-Util.FileStorage.All.csproj b/src/Util.FileStorage.All/05-Util.FileStorage.All.csproj new file mode 100644 index 000000000..9467de684 --- /dev/null +++ b/src/Util.FileStorage.All/05-Util.FileStorage.All.csproj @@ -0,0 +1,34 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.FileStorage.All + Util.FileStorage + Util.FileStorage.All是Util应用框架文件存储操作类库,包含所有支持的文件存储实现 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.FileStorage.All.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.FileStorage.All.xml + + + + + True + False + + + + + + + + + + diff --git a/src/Util.FileStorage.All/FileStoreFactory.cs b/src/Util.FileStorage.All/FileStoreFactory.cs new file mode 100644 index 000000000..e00551e9b --- /dev/null +++ b/src/Util.FileStorage.All/FileStoreFactory.cs @@ -0,0 +1,58 @@ +namespace Util.FileStorage; + +/// +/// 文件存储服务工厂 +/// +public class FileStoreFactory : IFileStoreFactory { + /// + /// 文件名处理器工厂 + /// + private readonly IFileNameProcessorFactory _fileNameProcessorFactory; + /// + /// 存储桶名称处理器工厂 + /// + private readonly IBucketNameProcessorFactory _bucketNameProcessorFactory; + /// + /// Http操作 + /// + private readonly IHttpClient _httpClient; + /// + /// Http客户端工厂 + /// + private readonly IHttpClientFactory _httpClientFactory; + + /// + /// 初始化本地文件存储服务 + /// + /// 文件名处理器工厂 + /// 存储桶名称处理器工厂 + /// Http客户端工厂 + /// Http操作 + public FileStoreFactory( IFileNameProcessorFactory fileNameProcessorFactory, IBucketNameProcessorFactory bucketNameProcessorFactory, + IHttpClientFactory httpClientFactory, IHttpClient httpClient ) { + _fileNameProcessorFactory = fileNameProcessorFactory ?? throw new ArgumentNullException( nameof( fileNameProcessorFactory ) ); + _bucketNameProcessorFactory = bucketNameProcessorFactory ?? throw new ArgumentNullException( nameof( bucketNameProcessorFactory ) ); + _httpClientFactory = httpClientFactory ?? throw new ArgumentNullException( nameof( httpClientFactory ) ); + _httpClient = httpClient ?? throw new ArgumentNullException( nameof( httpClient ) ); + } + + /// + public IFileStore Create( LocalStoreOptions options ) { + return new LocalFileStore( new LocalStoreConfigProvider( options ), _fileNameProcessorFactory, _httpClient ); + } + + /// + public IFileStore Create( MinioOptions options ) { + return new MinioFileStore( new MinioConfigProvider( options ), _httpClientFactory, _bucketNameProcessorFactory, _fileNameProcessorFactory, _httpClient ); + } + + /// + public IFileStore Create( AliyunOssOptions options ) { + return new AliyunFileStore( new AliyunOssConfigProvider( options ), _bucketNameProcessorFactory, _fileNameProcessorFactory, _httpClient ); + } + + /// + public IAliyunOssFileStore CreateAliyunOss( AliyunOssOptions options ) { + return new AliyunFileStore( new AliyunOssConfigProvider( options ), _bucketNameProcessorFactory, _fileNameProcessorFactory, _httpClient ); + } +} \ No newline at end of file diff --git a/src/Util.FileStorage.All/IFileStoreFactory.cs b/src/Util.FileStorage.All/IFileStoreFactory.cs new file mode 100644 index 000000000..7978ece93 --- /dev/null +++ b/src/Util.FileStorage.All/IFileStoreFactory.cs @@ -0,0 +1,27 @@ +namespace Util.FileStorage; + +/// +/// 文件存储服务工厂 +/// +public interface IFileStoreFactory : ITransientDependency { + /// + /// 创建文件存储服务 + /// + /// 本地文件存储配置 + IFileStore Create( LocalStoreOptions options ); + /// + /// 创建文件存储服务 + /// + /// Minio配置 + IFileStore Create( MinioOptions options ); + /// + /// 创建文件存储服务 + /// + /// 阿里云对象存储配置 + IFileStore Create( AliyunOssOptions options ); + /// + /// 创建阿里云对象存储服务 + /// + /// 阿里云对象存储配置 + IAliyunOssFileStore CreateAliyunOss( AliyunOssOptions options ); +} \ No newline at end of file diff --git a/src/Util.FileStorage.All/Usings.cs b/src/Util.FileStorage.All/Usings.cs new file mode 100644 index 000000000..55dbe1cfc --- /dev/null +++ b/src/Util.FileStorage.All/Usings.cs @@ -0,0 +1,7 @@ +global using System; +global using System.Net.Http; +global using Util.Http; +global using Util.Dependency; +global using Util.FileStorage.Minio; +global using Util.FileStorage.Local; +global using Util.FileStorage.Aliyun; \ No newline at end of file diff --git a/src/Util.FileStorage.Minio/03-Util.FileStorage.Minio.csproj b/src/Util.FileStorage.Minio/03-Util.FileStorage.Minio.csproj new file mode 100644 index 000000000..f005fb0eb --- /dev/null +++ b/src/Util.FileStorage.Minio/03-Util.FileStorage.Minio.csproj @@ -0,0 +1,41 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.FileStorage.Minio + Util.FileStorage.Minio + Util.FileStorage.Minio是Util应用框架基于Minio文件存储操作类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.FileStorage.Minio.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.FileStorage.Minio.xml + + + + + True + False + + + + + + + + + + + + + + + + + diff --git a/src/Util.FileStorage.Minio/AppBuilderExtensions.cs b/src/Util.FileStorage.Minio/AppBuilderExtensions.cs new file mode 100644 index 000000000..b80732f1f --- /dev/null +++ b/src/Util.FileStorage.Minio/AppBuilderExtensions.cs @@ -0,0 +1,31 @@ +using Util.Configs; + +namespace Util.FileStorage.Minio; + +/// +/// Minio文件存储操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置Minio文件存储操作 + /// + /// 应用生成器 + public static IAppBuilder AddMinio( this IAppBuilder builder ) { + return builder.AddMinio( null ); + } + + /// + /// 配置Minio文件存储操作 + /// + /// 应用生成器 + /// Minio配置操作 + public static IAppBuilder AddMinio( this IAppBuilder builder, Action setupAction ) { + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + if ( setupAction != null ) + services.Configure( setupAction ); + services.TryAddTransient(); + } ); + return builder; + } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Minio/IMinioConfigProvider.cs b/src/Util.FileStorage.Minio/IMinioConfigProvider.cs new file mode 100644 index 000000000..28a524400 --- /dev/null +++ b/src/Util.FileStorage.Minio/IMinioConfigProvider.cs @@ -0,0 +1,11 @@ +namespace Util.FileStorage.Minio; + +/// +/// Minio配置提供器 +/// +public interface IMinioConfigProvider : ITransientDependency { + /// + /// 获取配置 + /// + Task GetConfigAsync(); +} \ No newline at end of file diff --git a/src/Util.FileStorage.Minio/MinioConfigProvider.cs b/src/Util.FileStorage.Minio/MinioConfigProvider.cs new file mode 100644 index 000000000..403f0c1b7 --- /dev/null +++ b/src/Util.FileStorage.Minio/MinioConfigProvider.cs @@ -0,0 +1,34 @@ +namespace Util.FileStorage.Minio; + +/// +/// Minio配置提供器 +/// +public class MinioConfigProvider : IMinioConfigProvider { + /// + /// Minio配置 + /// + private readonly MinioOptions _options; + + /// + /// 初始化Minio配置提供器 + /// + /// Minio配置 + public MinioConfigProvider( IOptions options ) : this( options.Value ) { + } + + /// + /// 初始化Minio配置提供器 + /// + /// Minio配置 + public MinioConfigProvider( MinioOptions options ) { + options.CheckNull( nameof( options ) ); + _options = options; + } + + /// + /// 获取配置 + /// + public Task GetConfigAsync() { + return Task.FromResult( _options ); + } +} \ No newline at end of file diff --git a/src/Util.FileStorage.Minio/MinioFileStore.cs b/src/Util.FileStorage.Minio/MinioFileStore.cs new file mode 100644 index 000000000..bfdf2d220 --- /dev/null +++ b/src/Util.FileStorage.Minio/MinioFileStore.cs @@ -0,0 +1,567 @@ +namespace Util.FileStorage.Minio; + +/// +/// Minio文件存储服务 +/// +public class MinioFileStore : IFileStore { + + #region 字段 + + /// + /// 配置提供器 + /// + private readonly IMinioConfigProvider _configProvider; + /// + /// Http客户端工厂 + /// + private readonly IHttpClientFactory _httpClientFactory; + /// + /// 存储桶名称处理器工厂 + /// + private readonly IBucketNameProcessorFactory _bucketNameProcessorFactory; + /// + /// 文件名处理器工厂 + /// + private readonly IFileNameProcessorFactory _fileNameProcessorFactory; + /// + /// Minio配置 + /// + private MinioOptions _config; + /// + /// Minio客户端 + /// + private IMinioClient _client; + /// + /// Http操作 + /// + private readonly IHttpClient _httpClient; + + #endregion + + #region 构造方法 + + /// + /// 初始化Minio文件存储服务 + /// + /// 配置提供器 + /// Http客户端工厂 + /// 存储桶名称处理器工厂 + /// 文件名处理器工厂 + /// Http操作 + public MinioFileStore( IMinioConfigProvider configProvider, IHttpClientFactory httpClientFactory, + IBucketNameProcessorFactory bucketNameProcessorFactory,IFileNameProcessorFactory fileNameProcessorFactory, + IHttpClient httpClient ) { + _configProvider = configProvider ?? throw new ArgumentNullException( nameof( configProvider ) ); + _httpClientFactory = httpClientFactory ?? throw new ArgumentNullException( nameof( httpClientFactory ) ); + _bucketNameProcessorFactory = bucketNameProcessorFactory ?? throw new ArgumentNullException( nameof( bucketNameProcessorFactory ) ); + _fileNameProcessorFactory = fileNameProcessorFactory ?? throw new ArgumentNullException( nameof( fileNameProcessorFactory ) ); + _httpClient = httpClient ?? throw new ArgumentNullException( nameof( httpClient ) ); + } + + #endregion + + #region InitConfig + + /// + /// 初始化Minio配置 + /// + protected virtual async Task InitConfig() { + if ( _config != null ) + return; + _config = await _configProvider.GetConfigAsync(); + } + + #endregion + + #region GetClient + + /// + /// 获取Minio客户端 + /// + protected virtual async Task GetClient() { + if ( _client != null ) + return _client; + await InitConfig(); + var client = new MinioClient() + .WithEndpoint( GetEndpoint() ) + .WithCredentials( _config.AccessKey, _config.SecretKey ) + .WithSSL( _config.UseSSL ); + var httpClient = GetHttpClient(); + _client = httpClient == null ? client.Build() : client.WithHttpClient( httpClient ).Build(); + return _client; + } + + /// + /// 获取服务端点 + /// + protected virtual string GetEndpoint() { + var endpoint = _config.Endpoint; + if ( endpoint.StartsWith( "http" ) == false ) + return endpoint; + return endpoint.RemoveStart( "https://" ).RemoveStart( "http://" ); + } + + /// + /// 获取Http客户端 + /// + protected virtual HttpClient GetHttpClient() { + if ( _config.HttpClientName.IsEmpty() ) + return null; + return _httpClientFactory.CreateClient( _config.HttpClientName ); + } + + #endregion + + #region GetBucketNamesAsync + + /// + public virtual async Task> GetBucketNamesAsync( CancellationToken cancellationToken = default ) { + var client = await GetClient(); + var result = await client.ListBucketsAsync( cancellationToken ); + return result.Buckets.Select( t => t.Name ).ToList(); + } + + #endregion + + #region BucketExistsAsync + + /// + public virtual async Task BucketExistsAsync( string bucketName, string policy = null, CancellationToken cancellationToken = default ) { + if ( bucketName.IsEmpty() ) + return false; + var processedBucketName = ProcessBucketName( bucketName, policy ); + return await BucketExistsAsync( processedBucketName, cancellationToken ); + } + + /// + /// 处理存储桶名称 + /// + protected ProcessedName ProcessBucketName( string bucketName, string policy ) { + var processor = _bucketNameProcessorFactory.CreateProcessor( policy ); + return processor.Process( bucketName ); + } + + /// + /// 存储桶是否存在 + /// + protected virtual async Task BucketExistsAsync( ProcessedName bucketName, CancellationToken cancellationToken = default ) { + var client = await GetClient(); + var args = new BucketExistsArgs().WithBucket( bucketName.Name ); + return await client.BucketExistsAsync( args, cancellationToken ); + } + + #endregion + + #region CreateBucketAsync + + /// + public virtual async Task CreateBucketAsync( string bucketName, string policy = null, CancellationToken cancellationToken = default ) { + if ( bucketName.IsEmpty() ) + return; + var processedBucketName = ProcessBucketName( bucketName, policy ); + await CreateBucketAsync( processedBucketName, cancellationToken ); + } + + /// + /// 创建存储桶 + /// + protected virtual async Task CreateBucketAsync( ProcessedName bucketName, CancellationToken cancellationToken ) { + var client = await GetClient(); + var exists = await BucketExistsAsync( bucketName, cancellationToken ); + if ( exists ) + return; + var args = new MakeBucketArgs().WithBucket( bucketName.Name ); + await client.MakeBucketAsync( args, cancellationToken ); + } + + #endregion + + #region DeleteBucketAsync + + /// + public async Task DeleteBucketAsync( string bucketName, string policy = null, CancellationToken cancellationToken = default ) { + if ( bucketName.IsEmpty() ) + return; + var processedBucketName = ProcessBucketName( bucketName, policy ); + await DeleteBucketAsync( processedBucketName, cancellationToken ); + } + + /// + /// 删除存储桶 + /// + protected virtual async Task DeleteBucketAsync( ProcessedName bucketName, CancellationToken cancellationToken = default ) { + var client = await GetClient(); + var exists = await BucketExistsAsync( bucketName, cancellationToken ); + if ( exists == false ) + return; + var args = new RemoveBucketArgs().WithBucket( bucketName.Name ); + await client.RemoveBucketAsync( args, cancellationToken ); + } + + /// + /// 安全删除存储桶,不报异常 + /// + protected virtual async Task SafeDeleteBucketAsync( ProcessedName bucketName, CancellationToken cancellationToken = default ) { + try { + var client = await GetClient(); + var args = new RemoveBucketArgs().WithBucket( bucketName.Name ); + await client.RemoveBucketAsync( args, cancellationToken ); + } + catch { + //ignored + } + } + + #endregion + + #region FileExistsAsync + + /// + public async Task FileExistsAsync( string fileName, string policy = null, CancellationToken cancellationToken = default ) { + var args = new FileExistsArgs( fileName ) { FileNamePolicy = policy }; + return await FileExistsAsync( args, cancellationToken ); + } + + /// + public async Task FileExistsAsync( FileExistsArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var processedBucketName = await ProcessBucketName( args ); + return await FileExistsAsync( processedFileName, processedBucketName, cancellationToken ); + } + + /// + /// 处理文件名 + /// + protected ProcessedName ProcessFileName( FileStorageArgs args ) { + if ( args.FileName.IsEmpty() ) + throw new ArgumentNullException( nameof( args.FileName ) ); + var processor = _fileNameProcessorFactory.CreateProcessor( args.FileNamePolicy ); + return processor.Process( args.FileName ); + } + + /// + /// 处理存储桶名称 + /// + protected async Task ProcessBucketName( FileStorageArgs args ) { + await InitConfig(); + args.BucketName ??= _config.DefaultBucketName; + if ( args.BucketName.IsEmpty() ) + throw new ArgumentNullException( nameof( args.BucketName ) ); + var processor = _bucketNameProcessorFactory.CreateProcessor( args.BucketNamePolicy ); + return processor.Process( args.BucketName ); + } + + /// + /// 文件是否存在 + /// + protected async Task FileExistsAsync( ProcessedName fileName, ProcessedName bucketName, CancellationToken cancellationToken = default ) { + var client = await GetClient(); + var args = new StatObjectArgs().WithBucket( bucketName.Name ).WithObject( fileName.Name ); + try { + var result = await client.StatObjectAsync( args, cancellationToken ); + if ( result.Size == 0 ) + return false; + } + catch ( Exception ex ) { + if ( ex is BucketNotFoundException ) + return false; + if ( ex is ObjectNotFoundException ) + return false; + throw; + } + return true; + } + + #endregion + + #region GetFileStreamAsync + + /// + public async Task GetFileStreamAsync( string fileName, CancellationToken cancellationToken = default ) { + var args = new GetFileStreamArgs( fileName ); + return await GetFileStreamAsync( args, cancellationToken ); + } + + /// + public async Task GetFileStreamAsync( GetFileStreamArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var processedBucketName = await ProcessBucketName( args ); + return await GetFileStreamAsync( processedFileName, processedBucketName, cancellationToken ); + } + + /// + /// 获取文件流 + /// + protected async Task GetFileStreamAsync( ProcessedName fileName, ProcessedName bucketName, CancellationToken cancellationToken = default ) { + var client = await GetClient(); + var exists = await FileExistsAsync( fileName, bucketName, cancellationToken ); + if ( exists == false ) + return null; + var memoryStream = new MemoryStream(); + var args = new GetObjectArgs().WithBucket( bucketName.Name ).WithObject( fileName.Name ).WithCallbackStream( + async (stream,token) => { + if ( stream == null ) + return; + await stream.CopyToAsync( memoryStream, token ).ConfigureAwait( false ); + await stream.DisposeAsync(); + } ); + await client.GetObjectAsync( args, cancellationToken ); + return memoryStream; + } + + #endregion + + #region SaveFileAsync + + /// + public virtual async Task SaveFileAsync( Stream stream, string fileName, string policy = null, CancellationToken cancellationToken = default ) { + var args = new SaveFileArgs( fileName, stream ) { FileNamePolicy = policy }; + return await SaveFileAsync( args, cancellationToken ); + } + + /// + public virtual async Task SaveFileAsync( SaveFileArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var processedBucketName = await ProcessBucketName( args ); + return await SaveFileAsync( args.Stream, processedFileName, processedBucketName, cancellationToken ); + } + + /// + /// 保存文件 + /// + public virtual async Task SaveFileAsync( Stream stream, ProcessedName fileName, ProcessedName bucketName, CancellationToken cancellationToken = default ) { + stream.CheckNull( nameof( stream ) ); + await CreateBucketAsync( bucketName, cancellationToken ); + var client = await GetClient(); + var args = new PutObjectArgs() + .WithBucket( bucketName.Name ) + .WithObject( fileName.Name ) + .WithStreamData( stream ) + .WithObjectSize( stream.Length ); + await client.PutObjectAsync( args, cancellationToken ); + return new FileResult( fileName.Name, stream.Length, fileName.OriginalName, bucketName.Name ); + } + + #endregion + + #region SaveFileByUrlAsync + + /// + public async Task SaveFileByUrlAsync( string url, string fileName, string policy = null, CancellationToken cancellationToken = default ) { + var args = new SaveFileByUrlArgs( fileName, url ) { FileNamePolicy = policy }; + return await SaveFileByUrlAsync( args, cancellationToken ); + } + + /// + public async Task SaveFileByUrlAsync( SaveFileByUrlArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var processedBucketName = await ProcessBucketName( args ); + var bytes = await _httpClient.Get( args.Url ).GetStreamAsync( cancellationToken ); + await using var stream = new MemoryStream( bytes ); + return await SaveFileAsync( stream, processedFileName, processedBucketName, cancellationToken ); + } + + #endregion + + #region CopyFileAsync + + /// + public async Task CopyFileAsync( string sourceFileName, string destinationFileName, CancellationToken cancellationToken = default ) { + var sourceArgs = new FileStorageArgs( sourceFileName ); + var destinationArgs = new FileStorageArgs( destinationFileName ); + await CopyFileAsync( sourceArgs, destinationArgs, cancellationToken ); + } + + /// + public async Task CopyFileAsync( FileStorageArgs sourceArgs, FileStorageArgs destinationArgs, CancellationToken cancellationToken = default ) { + var copySourceObjectArgs = await CreateCopySourceObjectArgs( sourceArgs ); + var processedDestinationBucketName = await ProcessBucketName( destinationArgs ); + var processedDestinationFileName = ProcessFileName( destinationArgs ); + var args = new CopyObjectArgs() + .WithBucket( processedDestinationBucketName.Name ) + .WithObject( processedDestinationFileName.Name ) + .WithCopyObjectSource( copySourceObjectArgs ); + var client = await GetClient(); + await client.CopyObjectAsync( args, cancellationToken ); + } + + /// + /// 创建复制源文件参数对象 + /// + private async Task CreateCopySourceObjectArgs( FileStorageArgs sourceArgs ) { + var processedSourceBucketName = await ProcessBucketName( sourceArgs ); + var processedSourceFileName = ProcessFileName( sourceArgs ); + return new CopySourceObjectArgs() + .WithBucket( processedSourceBucketName.Name ) + .WithObject( processedSourceFileName.Name ); + } + + #endregion + + #region MoveFileAsync + + /// + public async Task MoveFileAsync( string sourceFileName, string destinationFileName, CancellationToken cancellationToken = default ) { + var sourceArgs = new FileStorageArgs( sourceFileName ); + var destinationArgs = new FileStorageArgs( destinationFileName ); + await MoveFileAsync( sourceArgs, destinationArgs, cancellationToken ); + } + + /// + public async Task MoveFileAsync( FileStorageArgs sourceArgs, FileStorageArgs destinationArgs, CancellationToken cancellationToken = default ) { + await CopyFileAsync( sourceArgs, destinationArgs, cancellationToken ); + var processedSourceFileName = ProcessFileName( sourceArgs ); + var processedSourceBucketName = await ProcessBucketName( sourceArgs ); + await DeleteFileAsync( processedSourceFileName, processedSourceBucketName, cancellationToken ); + } + + #endregion + + #region DeleteFileAsync + + /// + public async Task DeleteFileAsync( string fileName, CancellationToken cancellationToken = default ) { + var args = new DeleteFileArgs( fileName ); + await DeleteFileAsync( args, cancellationToken ); + } + + /// + public async Task DeleteFileAsync( DeleteFileArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var processedBucketName = await ProcessBucketName( args ); + await DeleteFileAsync( processedFileName, processedBucketName, cancellationToken ); + } + + /// + /// 删除文件 + /// + /// 文件名 + /// 存储桶名称 + /// 取消令牌 + protected async Task DeleteFileAsync( ProcessedName fileName, ProcessedName bucketName, CancellationToken cancellationToken = default ) { + var client = await GetClient(); + var exists = await FileExistsAsync( fileName,bucketName, cancellationToken ); + if ( exists == false ) + return; + var args = new RemoveObjectArgs().WithBucket( bucketName.Name ).WithObject( fileName.Name ); + await client.RemoveObjectAsync( args, cancellationToken ); + } + + #endregion + + #region GenerateDownloadUrlAsync + + /// + public async Task GenerateDownloadUrlAsync( string fileName, CancellationToken cancellationToken = default ) { + var args = new GenerateDownloadUrlArgs( fileName ); + return await GenerateDownloadUrlAsync( args, cancellationToken ); + } + + /// + public virtual async Task GenerateDownloadUrlAsync( GenerateDownloadUrlArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var processedBucketName = await ProcessBucketName( args ); + await InitConfig(); + var schema = _config.UseSSL ? "https" : "http"; + return Util.Helpers.Common.JoinPath( $"{schema}://{GetEndpoint()}", processedBucketName.Name, processedFileName.Name ); + } + + #endregion + + #region GenerateTempDownloadUrlAsync + + /// + public async Task GenerateTempDownloadUrlAsync( string fileName, CancellationToken cancellationToken = default ) { + var args = new GenerateTempDownloadUrlArgs( fileName ); + return await GenerateTempDownloadUrlAsync( args, cancellationToken ); + } + + /// + public async Task GenerateTempDownloadUrlAsync( GenerateTempDownloadUrlArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var processedBucketName = await ProcessBucketName( args ); + var expiration = args.Expiration ?? _config.DownloadUrlExpiration; + return await GenerateTempDownloadUrlAsync( processedFileName, processedBucketName, args.ResponseContentType, expiration ); + } + + /// + /// 生成临时下载Url + /// + protected async Task GenerateTempDownloadUrlAsync( ProcessedName fileName, ProcessedName bucketName, string responseContentType, int expiration ) { + var client = await GetClient(); + responseContentType ??= "application/octet-stream"; + var headers = new Dictionary { { "response-content-type", responseContentType } }; + var args = new PresignedGetObjectArgs() + .WithBucket( bucketName.Name ) + .WithObject( fileName.Name ) + .WithExpiry( expiration ) + .WithHeaders( headers ); + return await client.PresignedGetObjectAsync( args ); + } + + #endregion + + #region GenerateUploadUrlAsync + + /// + public async Task GenerateUploadUrlAsync( string fileName, string policy = null, CancellationToken cancellationToken = default ) { + var args = new GenerateUploadUrlArgs( fileName ) { FileNamePolicy = policy }; + return await GenerateUploadUrlAsync( args, cancellationToken ); + } + + /// + public async Task GenerateUploadUrlAsync( GenerateUploadUrlArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var processedBucketName = await ProcessBucketName( args ); + return await GenerateUploadUrlAsync( processedFileName, processedBucketName, args.GetHeaders(), cancellationToken ); + } + + /// + /// 生成上传Url + /// + protected async Task GenerateUploadUrlAsync( ProcessedName fileName, ProcessedName bucketName, IDictionary headers, CancellationToken cancellationToken = default ) { + var client = await GetClient(); + await CreateBucketAsync( bucketName, cancellationToken ); + var args = new PresignedPutObjectArgs() + .WithBucket( bucketName.Name ) + .WithObject( fileName.Name ) + .WithExpiry( _config.UploadUrlExpiration ) + .WithHeaders( headers ); + var url = await client.PresignedPutObjectAsync( args ); + return new DirectUploadParam( fileName.Name, url, null, fileName.OriginalName, bucketName.Name ); + } + + #endregion + + #region ClearAsync + + /// + public async Task ClearAsync( CancellationToken cancellationToken = default ) { + var client = await GetClient(); + while ( true ) { + var buckets = await GetBucketNamesAsync( cancellationToken ); + if ( buckets.Count == 0 ) + return; + foreach ( var bucket in buckets ) { + await SafeDeleteBucketAsync( new ProcessedName( bucket ), cancellationToken ); + var listObjectsArgs = new ListObjectsArgs().WithBucket( bucket ).WithRecursive( true ); + var list = client.ListObjectsEnumAsync(listObjectsArgs, cancellationToken); + await foreach (var item in list.ConfigureAwait(false)) { + var removeObjectArgs = new RemoveObjectArgs().WithBucket( bucket ).WithObject( item.Key ); + client.RemoveObjectAsync( removeObjectArgs, cancellationToken ).GetAwaiter(); + } + SafeDeleteBucketAsync( new ProcessedName( bucket ), cancellationToken ).GetAwaiter(); + } + } + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.FileStorage.Minio/MinioOptions.cs b/src/Util.FileStorage.Minio/MinioOptions.cs new file mode 100644 index 000000000..2ad4b79bd --- /dev/null +++ b/src/Util.FileStorage.Minio/MinioOptions.cs @@ -0,0 +1,39 @@ +namespace Util.FileStorage.Minio; + +/// +/// Minio配置 +/// +public class MinioOptions { + /// + /// Minio存储服务地址 + /// + public string Endpoint { get; set; } + /// + /// 访问密钥,用来标识帐户的Id + /// + public string AccessKey { get; set; } + /// + /// 密钥,即帐户密码 + /// + public string SecretKey { get; set; } + /// + /// 是否使用Https,默认值: false + /// + public bool UseSSL { get; set; } + /// + /// 默认存储桶名称 + /// + public string DefaultBucketName { get; set; } + /// + /// HttpClient名称 + /// + public string HttpClientName { get; set; } + /// + /// 上传地址过期时间,单位:秒,默认值: 3600 + /// + public int UploadUrlExpiration { get; set; } = 3600; + /// + /// 下载地址过期时间,单位:秒,默认值: 3600 + /// + public int DownloadUrlExpiration { get; set; } = 3600; +} \ No newline at end of file diff --git a/src/Util.FileStorage.Minio/Usings.cs b/src/Util.FileStorage.Minio/Usings.cs new file mode 100644 index 000000000..b63c805e8 --- /dev/null +++ b/src/Util.FileStorage.Minio/Usings.cs @@ -0,0 +1,15 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using System.IO; +global using System.Net.Http; +global using Microsoft.Extensions.Options; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Minio; +global using Minio.Exceptions; +global using Minio.DataModel.Args; +global using Util.Http; +global using Util.Dependency; \ No newline at end of file diff --git a/src/Util.FileStorage/02-Util.FileStorage.csproj b/src/Util.FileStorage/02-Util.FileStorage.csproj new file mode 100644 index 000000000..6facddc38 --- /dev/null +++ b/src/Util.FileStorage/02-Util.FileStorage.csproj @@ -0,0 +1,36 @@ + + + $(NetTargetFramework) + icon.jpg + Util.FileStorage + Util.FileStorage + Util.FileStorage是Util应用框架文件存储操作基础类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.FileStorage.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.FileStorage.xml + + + + + True + False + + + + + + + + + + + + + diff --git a/src/Util.FileStorage/AppBuilderExtensions.cs b/src/Util.FileStorage/AppBuilderExtensions.cs new file mode 100644 index 000000000..7ed880660 --- /dev/null +++ b/src/Util.FileStorage/AppBuilderExtensions.cs @@ -0,0 +1,33 @@ +using Util.Configs; +using Util.FileStorage.Local; + +namespace Util.FileStorage; + +/// +/// 本地文件存储操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置本地文件存储操作,将文件上传到Web服务器目录 + /// + /// 应用生成器 + public static IAppBuilder AddLocalFileStore( this IAppBuilder builder ) { + return builder.AddLocalFileStore( null ); + } + + /// + /// 配置本地文件存储操作,将文件上传到Web服务器目录 + /// + /// 应用生成器 + /// 本地文件存储配置操作 + public static IAppBuilder AddLocalFileStore( this IAppBuilder builder, Action setupAction ) { + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + if( setupAction != null ) + services.Configure( setupAction ); + services.TryAddTransient(); + services.TryAddTransient(); + } ); + return builder; + } +} \ No newline at end of file diff --git a/src/Util.FileStorage/BucketNameProcessor.cs b/src/Util.FileStorage/BucketNameProcessor.cs new file mode 100644 index 000000000..8286d6b12 --- /dev/null +++ b/src/Util.FileStorage/BucketNameProcessor.cs @@ -0,0 +1,14 @@ +namespace Util.FileStorage; + +/// +/// 存储桶名称处理器 +/// +public class BucketNameProcessor : IBucketNameProcessor { + /// + public ProcessedName Process( string bucketName ) { + if ( bucketName.IsEmpty() ) + throw new ArgumentNullException( nameof( bucketName ) ); + var result = bucketName.ToLowerInvariant().Replace( "_","-" ).Replace( ".", "-" ).Trim( '-' ); + return new ProcessedName( result, bucketName ); + } +} \ No newline at end of file diff --git a/src/Util.FileStorage/BucketNameProcessorFactory.cs b/src/Util.FileStorage/BucketNameProcessorFactory.cs new file mode 100644 index 000000000..80a5fd662 --- /dev/null +++ b/src/Util.FileStorage/BucketNameProcessorFactory.cs @@ -0,0 +1,13 @@ +namespace Util.FileStorage; + +/// +/// 存储桶名称处理器工厂 +/// +public class BucketNameProcessorFactory : IBucketNameProcessorFactory { + /// + public IBucketNameProcessor CreateProcessor( string policy ) { + if ( policy.IsEmpty() ) + return new BucketNameProcessor(); + throw new NotImplementedException( $"存储桶名称处理策略 {policy} 未实现." ); + } +} \ No newline at end of file diff --git a/src/Util.FileStorage/FileNameFilter.cs b/src/Util.FileStorage/FileNameFilter.cs new file mode 100644 index 000000000..b8db6b169 --- /dev/null +++ b/src/Util.FileStorage/FileNameFilter.cs @@ -0,0 +1,11 @@ +namespace Util.FileStorage; + +/// +/// 文件名过滤器 +/// +public class FileNameFilter : IFileNameFilter { + /// + public string Filter( string fileName ) { + return fileName; + } +} \ No newline at end of file diff --git a/src/Util.FileStorage/FileNameProcessor.cs b/src/Util.FileStorage/FileNameProcessor.cs new file mode 100644 index 000000000..9efe3cdbb --- /dev/null +++ b/src/Util.FileStorage/FileNameProcessor.cs @@ -0,0 +1,11 @@ +namespace Util.FileStorage; + +/// +/// 默认文件名处理器,不作任何修改 +/// +public class FileNameProcessor : IFileNameProcessor { + /// + public ProcessedName Process( string fileName ) { + return new ProcessedName( fileName ); + } +} \ No newline at end of file diff --git a/src/Util.FileStorage/FileNameProcessorFactory.cs b/src/Util.FileStorage/FileNameProcessorFactory.cs new file mode 100644 index 000000000..c74fdc734 --- /dev/null +++ b/src/Util.FileStorage/FileNameProcessorFactory.cs @@ -0,0 +1,28 @@ +namespace Util.FileStorage; + +/// +/// 文件名处理器工厂 +/// +public class FileNameProcessorFactory : IFileNameProcessorFactory { + /// + /// 用户会话 + /// + private readonly ISession _session; + + /// + /// 初始化文件名处理器工厂 + /// + /// 用户会话 + public FileNameProcessorFactory( ISession session ) { + _session = session ?? NullSession.Instance; + } + + /// + public IFileNameProcessor CreateProcessor( string policy ) { + if ( policy.IsEmpty() ) + return new FileNameProcessor(); + if ( policy.ToUpperInvariant() == UserTimeFileNameProcessor.Policy ) + return new UserTimeFileNameProcessor( _session ); + throw new NotImplementedException( $"文件名处理策略 {policy} 未实现." ); + } +} \ No newline at end of file diff --git a/src/Util.FileStorage/FileStoreExtensions.cs b/src/Util.FileStorage/FileStoreExtensions.cs new file mode 100644 index 000000000..80c7bdf08 --- /dev/null +++ b/src/Util.FileStorage/FileStoreExtensions.cs @@ -0,0 +1,50 @@ +namespace Util.FileStorage; + +/// +/// 文件存储服务操作扩展 +/// +public static class FileStoreExtensions { + /// + /// 保存文件 + /// + /// 文件存储服务 + /// 文件信息 + /// 文件名处理策略 + /// 取消令牌 + public static async Task SaveFileAsync( this ILocalFileStore fileStore, FileInfo fileInfo, string policy = null, CancellationToken cancellationToken = default ) { + fileStore.CheckNull( nameof( fileStore ) ); + var path = fileInfo.FullName; + if ( System.IO.File.Exists( path ) == false ) + throw new FileNotFoundException( "保存文件失败,文件路径不正确.", path ); + var fileName = System.IO.Path.GetFileName( path ); + await using var stream = Util.Helpers.File.ReadToStream( path ); + return await fileStore.SaveFileAsync( stream, fileName, policy, cancellationToken ); + } + + /// + /// 保存文件 + /// + /// 文件存储服务 + /// 文件流 + /// 文件名,包含扩展名 + /// 文件名处理策略 + /// 取消令牌 + public static async Task SaveFileAsync( this ILocalFileStore fileStore,byte[] bytes, string fileName, string policy = null, CancellationToken cancellationToken = default ) { + fileStore.CheckNull( nameof( fileStore ) ); + await using var stream = new MemoryStream( bytes ); + return await fileStore.SaveFileAsync( stream, fileName, policy, cancellationToken ); + } + + /// + /// 获取文件字节流 + /// + /// 文件存储服务 + /// 文件名,包含扩展名 + /// 取消令牌 + public static async Task GetFileBytesAsync( this ILocalFileStore fileStore, string fileName, CancellationToken cancellationToken = default ) { + var stream = await fileStore.GetFileStreamAsync( fileName, cancellationToken ); + await using ( stream ) { + return await Util.Helpers.File.ToBytesAsync( stream, cancellationToken ); + } + } +} \ No newline at end of file diff --git a/src/Util.FileStorage/FileValidation.cs b/src/Util.FileStorage/FileValidation.cs new file mode 100644 index 000000000..a66cc8ca6 --- /dev/null +++ b/src/Util.FileStorage/FileValidation.cs @@ -0,0 +1,23 @@ +namespace Util.FileStorage; + +/// +/// 文件验证操作 +/// +public static class FileValidation { + /// + /// 扩展名是否有效 + /// + /// 文件名,范例: a.jpg + /// 接受的扩展名列表,以逗号分隔,范例: .jpg,.png,.gif + public static bool IsValidExtension( string fileName, string accepts ) { + if( fileName.IsEmpty() ) + return false; + if( accepts.IsEmpty() ) + return true; + var extension = Path.GetExtension( fileName ); + var list = accepts.Split( ',' ).Where( t => t.IsEmpty() == false ).ToList(); + if ( list.Count == 0 ) + return true; + return list.Any( type => type.TrimStart( '.' ) == extension.TrimStart( '.' ) ); + } +} \ No newline at end of file diff --git a/src/Util.FileStorage/Local/ILocalStoreConfigProvider.cs b/src/Util.FileStorage/Local/ILocalStoreConfigProvider.cs new file mode 100644 index 000000000..ee76baafd --- /dev/null +++ b/src/Util.FileStorage/Local/ILocalStoreConfigProvider.cs @@ -0,0 +1,11 @@ +namespace Util.FileStorage.Local; + +/// +/// 本地文件存储配置提供器 +/// +public interface ILocalStoreConfigProvider : ITransientDependency { + /// + /// 获取配置 + /// + Task GetConfigAsync(); +} \ No newline at end of file diff --git a/src/Util.FileStorage/Local/LocalFileStore.cs b/src/Util.FileStorage/Local/LocalFileStore.cs new file mode 100644 index 000000000..75f5f66d1 --- /dev/null +++ b/src/Util.FileStorage/Local/LocalFileStore.cs @@ -0,0 +1,326 @@ +namespace Util.FileStorage.Local; + +/// +/// 本地文件存储服务 +/// +public class LocalFileStore : IFileStore { + + #region 字段 + + /// + /// 配置提供器 + /// + private readonly ILocalStoreConfigProvider _configProvider; + /// + /// 文件名处理器工厂 + /// + private readonly IFileNameProcessorFactory _fileNameProcessorFactory; + /// + /// 本地文件存储配置 + /// + private LocalStoreOptions _config; + /// + /// Http操作 + /// + private readonly IHttpClient _httpClient; + + #endregion + + #region 构造方法 + + /// + /// 初始化本地文件存储服务 + /// + /// 配置提供器 + /// 文件名处理器工厂 + /// Http操作 + public LocalFileStore( ILocalStoreConfigProvider configProvider, IFileNameProcessorFactory fileNameProcessorFactory, IHttpClient httpClient ) { + _configProvider = configProvider ?? throw new ArgumentNullException( nameof( configProvider ) ); + _fileNameProcessorFactory = fileNameProcessorFactory ?? throw new ArgumentNullException( nameof( fileNameProcessorFactory ) ); + _httpClient = httpClient ?? throw new ArgumentNullException( nameof( httpClient ) ); + } + + #endregion + + #region GetConfig + + /// + /// 获取配置 + /// + protected virtual async Task GetConfig() { + return _config ??= await _configProvider.GetConfigAsync(); + } + + #endregion + + #region GetBucketNamesAsync + + /// + public virtual Task> GetBucketNamesAsync( CancellationToken cancellationToken = default ) { + throw new NotImplementedException(); + } + + #endregion + + #region BucketExistsAsync + + /// + public virtual Task BucketExistsAsync( string bucketName, string policy = null, CancellationToken cancellationToken = default ) { + throw new NotImplementedException(); + } + + #endregion + + #region CreateBucketAsync + + /// + public virtual Task CreateBucketAsync( string bucketName, string policy = null, CancellationToken cancellationToken = default ) { + throw new NotImplementedException(); + } + + #endregion + + #region DeleteBucketAsync + + /// + public virtual Task DeleteBucketAsync( string bucketName, string policy = null, CancellationToken cancellationToken = default ) { + throw new NotImplementedException(); + } + + #endregion + + #region FileExistsAsync + + /// + public virtual async Task FileExistsAsync( string fileName, string policy = null, CancellationToken cancellationToken = default ) { + var args = new FileExistsArgs( fileName ) { FileNamePolicy = policy }; + return await FileExistsAsync( args, cancellationToken ); + } + + /// + public virtual async Task FileExistsAsync( FileExistsArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + return await FileExistsAsync( processedFileName, cancellationToken ); + } + + /// + /// 处理文件名 + /// + protected virtual ProcessedName ProcessFileName( FileStorageArgs args ) { + if ( args.FileName.IsEmpty() ) + throw new ArgumentNullException( nameof( args.FileName ) ); + var processor = _fileNameProcessorFactory.CreateProcessor( args.FileNamePolicy ); + return processor.Process( args.FileName ); + } + + /// + /// 文件是否存在 + /// + protected virtual async Task FileExistsAsync( ProcessedName fileName, CancellationToken cancellationToken = default ) { + var filePath = await GetPhysicalPath( fileName ); + return Util.Helpers.File.FileExists( filePath ); + } + + /// + /// 获取文件绝对路径 + /// + protected virtual async Task GetPhysicalPath( ProcessedName fileName ) { + var config = await GetConfig(); + return Path.Combine( GetRootPath(), config.RootPath, fileName.Name ); + } + + /// + /// 获取根路径 + /// + protected virtual string GetRootPath() { + return Util.Helpers.Web.Environment == null ? string.Empty : Util.Helpers.Web.Environment.WebRootPath; + } + + #endregion + + #region GetFileStreamAsync + + /// + public async Task GetFileStreamAsync( string fileName, CancellationToken cancellationToken = default ) { + var args = new GetFileStreamArgs( fileName ); + return await GetFileStreamAsync( args, cancellationToken ); + } + + /// + public async Task GetFileStreamAsync( GetFileStreamArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var filePath = await GetPhysicalPath( processedFileName ); + return await Util.Helpers.File.ReadToMemoryStreamAsync( filePath, cancellationToken ); + } + + #endregion + + #region SaveFileAsync + + /// + public virtual async Task SaveFileAsync( Stream stream, string fileName, string policy = null, CancellationToken cancellationToken = default ) { + var args = new SaveFileArgs( fileName, stream ) { FileNamePolicy = policy }; + return await SaveFileAsync( args, cancellationToken ); + } + + /// + public virtual async Task SaveFileAsync( SaveFileArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + return await SaveFileAsync( args.Stream, processedFileName, cancellationToken ); + } + + /// + /// 保存文件 + /// + public virtual async Task SaveFileAsync( Stream stream, ProcessedName fileName, CancellationToken cancellationToken = default ) { + stream.CheckNull( nameof( stream ) ); + var config = await GetConfig(); + var filePath = Path.Combine( config.RootPath, fileName.Name ); + var physicalPath = Path.Combine( GetRootPath(), filePath ); + await Util.Helpers.File.WriteAsync( physicalPath, stream, cancellationToken ); + return new FileResult( filePath, stream.Length, fileName.OriginalName ); + } + + #endregion + + #region SaveFileByUrlAsync + + /// + public async Task SaveFileByUrlAsync( string url, string fileName, string policy = null, CancellationToken cancellationToken = default ) { + var args = new SaveFileByUrlArgs( fileName, url ) { FileNamePolicy = policy }; + return await SaveFileByUrlAsync( args, cancellationToken ); + } + + /// + public async Task SaveFileByUrlAsync( SaveFileByUrlArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var bytes = await _httpClient.Get( args.Url ).GetStreamAsync( cancellationToken ); + await using var stream = new MemoryStream( bytes ); + return await SaveFileAsync( stream, processedFileName, cancellationToken ); + } + + #endregion + + #region CopyFileAsync + + /// + public async Task CopyFileAsync( string sourceFileName, string destinationFileName, CancellationToken cancellationToken = default ) { + var sourceArgs = new FileStorageArgs( sourceFileName ); + var destinationArgs = new FileStorageArgs( destinationFileName ); + await CopyFileAsync( sourceArgs, destinationArgs, cancellationToken ); + } + + /// + public async Task CopyFileAsync( FileStorageArgs sourceArgs, FileStorageArgs destinationArgs, CancellationToken cancellationToken = default ) { + var processedSourceFileName = ProcessFileName( sourceArgs ); + var processedDestinationFileName = ProcessFileName( destinationArgs ); + var sourceFilePath = await GetPhysicalPath( processedSourceFileName ); + var destinationFilePath = await GetPhysicalPath( processedDestinationFileName ); + Util.Helpers.File.Copy( sourceFilePath, destinationFilePath, true ); + } + + #endregion + + #region MoveFileAsync + + /// + public async Task MoveFileAsync( string sourceFileName, string destinationFileName, CancellationToken cancellationToken = default ) { + var sourceArgs = new FileStorageArgs( sourceFileName ); + var destinationArgs = new FileStorageArgs( destinationFileName ); + await MoveFileAsync( sourceArgs, destinationArgs, cancellationToken ); + } + + /// + public virtual async Task MoveFileAsync( FileStorageArgs sourceArgs, FileStorageArgs destinationArgs, CancellationToken cancellationToken = default ) { + var processedSourceFileName = ProcessFileName( sourceArgs ); + var processedDestinationFileName = ProcessFileName( destinationArgs ); + var sourceFilePath = await GetPhysicalPath( processedSourceFileName ); + var destinationFilePath = await GetPhysicalPath( processedDestinationFileName ); + Util.Helpers.File.Move( sourceFilePath, destinationFilePath, true ); + } + + #endregion + + #region DeleteFileAsync + + /// + public async Task DeleteFileAsync( string fileName, CancellationToken cancellationToken = default ) { + var args = new DeleteFileArgs( fileName ); + await DeleteFileAsync( args, cancellationToken ); + } + + /// + public virtual async Task DeleteFileAsync( DeleteFileArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var filePath = await GetPhysicalPath( processedFileName ); + Util.Helpers.File.Delete( filePath ); + } + + #endregion + + #region GenerateDownloadUrlAsync + + /// + public async Task GenerateDownloadUrlAsync( string fileName, CancellationToken cancellationToken = default ) { + var args = new GenerateDownloadUrlArgs( fileName ); + return await GenerateDownloadUrlAsync( args, cancellationToken ); + } + + /// + public virtual Task GenerateDownloadUrlAsync( GenerateDownloadUrlArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var url = Util.Helpers.Common.JoinPath( Util.Helpers.Web.Host, processedFileName.Name ); + return Task.FromResult( url ); + } + + #endregion + + #region GenerateTempDownloadUrlAsync + + /// + public async Task GenerateTempDownloadUrlAsync( string fileName, CancellationToken cancellationToken = default ) { + var args = new GenerateTempDownloadUrlArgs( fileName ); + return await GenerateTempDownloadUrlAsync( args, cancellationToken ); + } + + /// + public virtual Task GenerateTempDownloadUrlAsync( GenerateTempDownloadUrlArgs args, CancellationToken cancellationToken = default ) { + args.CheckNull( nameof( args ) ); + var processedFileName = ProcessFileName( args ); + var url = Util.Helpers.Common.JoinPath( Util.Helpers.Web.Host, processedFileName.Name ); + return Task.FromResult( url ); + } + + #endregion + + #region GenerateUploadUrlAsync + + /// + public virtual Task GenerateUploadUrlAsync( string fileName, string policy = null, CancellationToken cancellationToken = default ) { + throw new NotImplementedException(); + } + + /// + public virtual Task GenerateUploadUrlAsync( GenerateUploadUrlArgs args, CancellationToken cancellationToken = default ) { + throw new NotImplementedException(); + } + + #endregion + + #region ClearAsync + + /// + public virtual async Task ClearAsync( CancellationToken cancellationToken = default ) { + var config = await GetConfig(); + var filePath = Util.Helpers.Common.GetPhysicalPath( config.RootPath ); + Util.Helpers.File.Delete( filePath ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.FileStorage/Local/LocalStoreConfigProvider.cs b/src/Util.FileStorage/Local/LocalStoreConfigProvider.cs new file mode 100644 index 000000000..65ccc030a --- /dev/null +++ b/src/Util.FileStorage/Local/LocalStoreConfigProvider.cs @@ -0,0 +1,34 @@ +namespace Util.FileStorage.Local; + +/// +/// 本地文件存储配置提供器 +/// +public class LocalStoreConfigProvider : ILocalStoreConfigProvider { + /// + /// 本地文件存储配置 + /// + private readonly LocalStoreOptions _options; + + /// + /// 初始化本地文件存储配置提供器 + /// + /// 本地文件存储配置 + public LocalStoreConfigProvider( IOptions options ) : this( options.Value ) { + } + + /// + /// 初始化本地文件存储配置提供器 + /// + /// 本地文件存储配置 + public LocalStoreConfigProvider( LocalStoreOptions options ) { + options.CheckNull( nameof( options ) ); + _options = options; + } + + /// + /// 获取配置 + /// + public Task GetConfigAsync() { + return Task.FromResult( _options ); + } +} \ No newline at end of file diff --git a/src/Util.FileStorage/Local/LocalStoreOptions.cs b/src/Util.FileStorage/Local/LocalStoreOptions.cs new file mode 100644 index 000000000..3fa7765a4 --- /dev/null +++ b/src/Util.FileStorage/Local/LocalStoreOptions.cs @@ -0,0 +1,11 @@ +namespace Util.FileStorage.Local; + +/// +/// 本地文件存储配置 +/// +public class LocalStoreOptions { + /// + /// 文件存储目录根路径,默认值:"upload" + /// + public string RootPath { get; set; } = "upload"; +} \ No newline at end of file diff --git a/src/Util.FileStorage/Signatures/FileExtensionInspector.cs b/src/Util.FileStorage/Signatures/FileExtensionInspector.cs new file mode 100644 index 000000000..c27008305 --- /dev/null +++ b/src/Util.FileStorage/Signatures/FileExtensionInspector.cs @@ -0,0 +1,58 @@ +namespace Util.FileStorage.Signatures; + +/// +/// 文件扩展名检查器 +/// +public class FileExtensionInspector : IFileExtensionInspector { + /// + /// 文件格式检查器 + /// + private readonly FileFormatInspector _inspector; + + /// + /// 初始化文件扩展名检查器 + /// + public FileExtensionInspector() { + _inspector = new FileFormatInspector(); + } + + /// + public string GetExtension( Stream stream ) { + if ( stream == null ) + return null; + var result = _inspector.DetermineFileFormat( stream ); + return result?.Extension; + } + + /// + public bool IsImage( Stream stream ) { + if( stream == null ) + return false; + var result = _inspector.DetermineFileFormat( stream ); + return result is Image; + } + + /// + public bool IsPdf( Stream stream ) { + if( stream == null ) + return false; + var result = _inspector.DetermineFileFormat( stream ); + return result is Pdf; + } + + /// + public bool IsOffice( Stream stream ) { + if( stream == null ) + return false; + var result = _inspector.DetermineFileFormat( stream ); + return result is OfficeOpenXml or CompoundFileBinary; + } + + /// + public bool IsVideo( Stream stream ) { + if( stream == null ) + return false; + var result = _inspector.DetermineFileFormat( stream ); + return result is Isobmff; + } +} \ No newline at end of file diff --git a/src/Util.FileStorage/UserTimeFileNameProcessor.cs b/src/Util.FileStorage/UserTimeFileNameProcessor.cs new file mode 100644 index 000000000..ab50f1703 --- /dev/null +++ b/src/Util.FileStorage/UserTimeFileNameProcessor.cs @@ -0,0 +1,35 @@ +using Util.Helpers; + +namespace Util.FileStorage; + +/// +/// 基于用户标识和时间的文件名处理器 +/// +public class UserTimeFileNameProcessor : IFileNameProcessor { + /// + /// 策略名称 + /// + public const string Policy = "USERTIME"; + /// + /// 用户会话 + /// + private readonly ISession _session; + + /// + /// 初始化基于用户标识和时间的文件名处理器 + /// + /// 用户会话 + public UserTimeFileNameProcessor( ISession session ) { + _session = session ?? NullSession.Instance; + } + + /// + public ProcessedName Process( string fileName ) { + if ( fileName.IsEmpty() ) + return new ProcessedName( null ); + var extension = Path.GetExtension( fileName ); + var name = $"{Id.Create()}{extension}"; + var result = Util.Helpers.Common.JoinPath( _session.UserId, $"{Time.Now:yyyy-MM-dd}", name ); + return new ProcessedName( result, fileName ); + } +} \ No newline at end of file diff --git a/src/Util.FileStorage/Usings.cs b/src/Util.FileStorage/Usings.cs new file mode 100644 index 000000000..0e6b7d400 --- /dev/null +++ b/src/Util.FileStorage/Usings.cs @@ -0,0 +1,15 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Threading; +global using System.IO; +global using System.ComponentModel; +global using System.Linq; +global using Microsoft.Extensions.Options; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using FileSignatures; +global using FileSignatures.Formats; +global using Util.Dependency; +global using Util.Http; +global using Util.Sessions; diff --git a/src/Util.Generators.Razor/02-Util.Generators.Razor.csproj b/src/Util.Generators.Razor/02-Util.Generators.Razor.csproj new file mode 100644 index 000000000..8678c2050 --- /dev/null +++ b/src/Util.Generators.Razor/02-Util.Generators.Razor.csproj @@ -0,0 +1,35 @@ + + + + $(NetTargetFramework) + Util.Generators.Razor + Util.Generators.Razor + icon.jpg + Util.Generators.Razor是Util应用框架基于Razor模板的代码生成类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Generators.Razor.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Generators.Razor.xml + + + + + True + False + + + + + + + + + + + diff --git a/src/Util.Generators.Razor/Infrastructure/RazorGeneratorServiceRegistrar.cs b/src/Util.Generators.Razor/Infrastructure/RazorGeneratorServiceRegistrar.cs new file mode 100644 index 000000000..42324af09 --- /dev/null +++ b/src/Util.Generators.Razor/Infrastructure/RazorGeneratorServiceRegistrar.cs @@ -0,0 +1,51 @@ +using Util.Generators.Configuration; +using Util.Generators.Contexts; +using Util.Generators.Resources; +using Util.Generators.Templates; +using Util.Infrastructure; + +namespace Util.Generators.Razor.Infrastructure; + +/// +/// Razor生成服务注册器 +/// +public class RazorGeneratorServiceRegistrar : IServiceRegistrar { + /// + /// 获取服务名 + /// + public static string ServiceName => "Util.Generators.Razor.Infrastructure.RazorGeneratorServiceRegistrar"; + + /// + /// 排序号 + /// + public int OrderId => 10010; + + /// + /// 是否启用 + /// + public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName ); + + /// + /// 注册服务 + /// + /// 服务上下文 + public Action Register( ServiceContext serviceContext ) { + serviceContext.HostBuilder.ConfigureServices( ( context, services ) => { + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + } ); + AddFilters(); + return null; + } + + /// + /// 添加模板过滤器 + /// + private void AddFilters() { + TemplateFilterManager.AddFilter( new PartTemplateFilter() ); + } +} \ No newline at end of file diff --git a/src/Util.Generators.Razor/Infrastructure/ServiceRegistrarConfigExtensions.cs b/src/Util.Generators.Razor/Infrastructure/ServiceRegistrarConfigExtensions.cs new file mode 100644 index 000000000..02f241104 --- /dev/null +++ b/src/Util.Generators.Razor/Infrastructure/ServiceRegistrarConfigExtensions.cs @@ -0,0 +1,26 @@ +using Util.Infrastructure; + +namespace Util.Generators.Razor.Infrastructure; + +/// +/// Razor生成服务注册器配置扩展 +/// +public static class ServiceRegistrarConfigExtensions { + /// + /// 启用Razor生成服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig EnableRazorGeneratorServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Enable( RazorGeneratorServiceRegistrar.ServiceName ); + return config; + } + + /// + ///禁用Razor生成服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig DisableRazorGeneratorServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Disable( RazorGeneratorServiceRegistrar.ServiceName ); + return config; + } +} \ No newline at end of file diff --git a/src/Util.Generators.Razor/PartTemplateFilter.cs b/src/Util.Generators.Razor/PartTemplateFilter.cs new file mode 100644 index 000000000..5928cac02 --- /dev/null +++ b/src/Util.Generators.Razor/PartTemplateFilter.cs @@ -0,0 +1,16 @@ +using Util.Generators.Contexts; +using Util.Generators.Templates; + +namespace Util.Generators.Razor; + +/// +/// 部分模板过滤器 +/// +public class PartTemplateFilter : ITemplateFilter { + /// + public bool IsFilter( string path, ProjectContext projectContext ) { + if ( path.IsEmpty() ) + return true; + return path.EndsWith( ".Part.cshtml", StringComparison.OrdinalIgnoreCase ); + } +} \ No newline at end of file diff --git a/src/Util.Generators.Razor/RazorTemplate.cs b/src/Util.Generators.Razor/RazorTemplate.cs new file mode 100644 index 000000000..391666c0f --- /dev/null +++ b/src/Util.Generators.Razor/RazorTemplate.cs @@ -0,0 +1,92 @@ +using Util.Generators.Contexts; +using Util.Generators.Templates; +using Util.Templates; + +namespace Util.Generators.Razor; + +/// +/// Razor模板 +/// +public class RazorTemplate : ITemplate { + /// + /// Razor模板引擎 + /// + protected readonly ITemplateEngine TemplateEngine; + /// + /// 模板文件 + /// + protected readonly FileInfo TemplateFile; + + /// + /// 初始化Razor模板 + /// + /// Razor模板引擎 + /// 模板文件 + public RazorTemplate( ITemplateEngine templateEngine, FileInfo file ) { + TemplateEngine = templateEngine; + TemplateFile = file; + } + + /// + /// 模板路径 + /// + public string Path => TemplateFile.FullName; + + /// + /// 渲染 + /// + /// 实体上下文 + public async Task RenderAsync( EntityContext context ) { + ValidateEntityContext( context ); + InitRelativeRootPath( context ); + var result = await RenderResult( context ); + await WriteFile( context, result ); + return result; + } + + /// + /// 验证实体上下文 + /// + /// 实体上下文 + protected virtual void ValidateEntityContext( EntityContext context ) { + context.CheckNull( nameof( context ) ); + } + + /// + /// 初始化相对根路径 + /// + /// 实体上下文 + protected virtual void InitRelativeRootPath( EntityContext context ) { + var relativeRootPath = GetRelativeRootPath( context ); + context.Output.RelativeRootPath = relativeRootPath; + } + + /// + /// 获取模板相对根路径 + /// + /// 实体上下文 + protected string GetRelativeRootPath( EntityContext context ) { + return TemplateFile.DirectoryName.RemoveStart( context.ProjectContext.GeneratorContext.TemplateRootPath ).RemoveStart( "\\" ).RemoveStart( "/" ); + } + + /// + /// 渲染结果 + /// + /// 实体上下文 + protected virtual async Task RenderResult( EntityContext context ) { + return await TemplateEngine.RenderByPathAsync( TemplateFile.FullName, context ); + } + + /// + /// 写入文件 + /// + /// 实体上下文 + /// 渲染结果 + protected virtual async Task WriteFile( EntityContext context, string result ) { + if ( context.Output.Path.IsEmpty() ) + return; + if ( result.IsEmpty() ) + return; + await Util.Helpers.File.WriteAsync( context.Output.Path, result ); + } +} \ No newline at end of file diff --git a/src/Util.Generators.Razor/RazorTemplateFinder.cs b/src/Util.Generators.Razor/RazorTemplateFinder.cs new file mode 100644 index 000000000..e3eb28476 --- /dev/null +++ b/src/Util.Generators.Razor/RazorTemplateFinder.cs @@ -0,0 +1,57 @@ +using Util.Generators.Contexts; +using Util.Generators.Logs; +using Util.Generators.Templates; +using Util.Templates; + +namespace Util.Generators.Razor; + +/// +/// Razor模板查找器 +/// +public class RazorTemplateFinder : ITemplateFinder { + /// + /// Razor模板引擎 + /// + private readonly ITemplateEngine _templateEngine; + /// + /// 模板过滤器管理器 + /// + private readonly ITemplateFilterManager _filterManager; + /// + /// 日志操作 + /// + private readonly IGeneratorLogger _logger; + + /// + /// 初始化Razor模板查找器 + /// + /// Razor模板引擎 + /// 模板过滤器管理器 + /// 日志操作 + public RazorTemplateFinder( ITemplateEngine templateEngine, ITemplateFilterManager filterManager, IGeneratorLogger logger = null ) { + _templateEngine = templateEngine ?? throw new ArgumentNullException( nameof( templateEngine ) ); + _filterManager = filterManager ?? throw new ArgumentNullException( nameof( filterManager ) ); + _logger = logger ?? NullGeneratorLogger.Instance; + } + + /// + public IEnumerable Find( string rootPath, ProjectContext projectContext ) { + var result = new List(); + var files = GetFiles( rootPath ); + foreach ( var file in files ) { + if ( _filterManager.IsFilter( file.FullName, projectContext ) ) { + _logger.FilterTemplate( file.FullName ); + continue; + } + result.Add( new RazorTemplate( _templateEngine, file ) ); + } + return result; + } + + /// + /// 获取cshtml文件列表 + /// + protected virtual List GetFiles( string rootPath ) { + return Util.Helpers.File.GetAllFiles( rootPath, "*.cshtml" ); + } +} \ No newline at end of file diff --git a/src/Util.Generators.Razor/Usings.cs b/src/Util.Generators.Razor/Usings.cs new file mode 100644 index 000000000..6db9905e7 --- /dev/null +++ b/src/Util.Generators.Razor/Usings.cs @@ -0,0 +1,5 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.IO; +global using Microsoft.Extensions.DependencyInjection.Extensions; \ No newline at end of file diff --git a/src/Util.Generators/01-Util.Generators.csproj b/src/Util.Generators/01-Util.Generators.csproj new file mode 100644 index 000000000..71f8847e6 --- /dev/null +++ b/src/Util.Generators/01-Util.Generators.csproj @@ -0,0 +1,62 @@ + + + + $(NetTargetFramework) + Util.Generators + Util.Generators + icon.jpg + Util.Generators是Util应用框架代码生成基础类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Generators.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Generators.xml + + + + + True + False + + + + + + + + + + + + + + + + True + True + GeneratorResource.resx + + + True + True + GeneratorResource.resx + + + + + + ResXFileCodeGenerator + GeneratorResource.Designer.cs + + + PublicResXFileCodeGenerator + GeneratorResource.Designer.cs + + + + diff --git a/src/Util.Generators/Configuration/ClientOptions.cs b/src/Util.Generators/Configuration/ClientOptions.cs new file mode 100644 index 000000000..7f8f280fc --- /dev/null +++ b/src/Util.Generators/Configuration/ClientOptions.cs @@ -0,0 +1,30 @@ +namespace Util.Generators.Configuration; + +/// +/// 客户端配置项 +/// +public class ClientOptions { + /// + /// 客户端应用名称 + /// + public string AppName { get; set; } + /// + /// 项目端口号 + /// + public string Port { get; set; } + /// + /// 是否启用微前端 + /// + public bool Microfrontend { get; set; } + + /// + /// 复制 + /// + public ClientOptions Clone() { + return new() { + AppName = AppName, + Port = Port, + Microfrontend = Microfrontend + }; + } +} \ No newline at end of file diff --git a/src/Util.Generators/Configuration/GeneratorOptions.cs b/src/Util.Generators/Configuration/GeneratorOptions.cs new file mode 100644 index 000000000..dedb4e89b --- /dev/null +++ b/src/Util.Generators/Configuration/GeneratorOptions.cs @@ -0,0 +1,31 @@ +namespace Util.Generators.Configuration; + +/// +/// 生成器配置项 +/// +public class GeneratorOptions { + /// + /// 初始化生成器配置项 + /// + public GeneratorOptions() { + Projects = new Dictionary(); + Messages = new MessageOptions(); + } + + /// + /// 模板根目录路径 + /// + public string TemplatePath { get; set; } + /// + /// 输出根目录路径 + /// + public string OutputPath { get; set; } + /// + /// 项目列表 + /// + public Dictionary Projects { get; set; } + /// + /// 消息 + /// + public MessageOptions Messages { get; set; } +} \ No newline at end of file diff --git a/src/Util.Generators/Configuration/GeneratorOptionsBuilder.cs b/src/Util.Generators/Configuration/GeneratorOptionsBuilder.cs new file mode 100644 index 000000000..7fd8548c5 --- /dev/null +++ b/src/Util.Generators/Configuration/GeneratorOptionsBuilder.cs @@ -0,0 +1,16 @@ +using Util.Helpers; + +namespace Util.Generators.Configuration; + +/// +/// 生成器配置项构建器 +/// +public class GeneratorOptionsBuilder : IGeneratorOptionsBuilder { + /// + /// 构建生成器配置项 + /// + public Task BuildAsync() { + var result = Config.Get( "Generator" ); + return Task.FromResult( result ); + } +} \ No newline at end of file diff --git a/src/Util.Generators/Configuration/IGeneratorOptionsBuilder.cs b/src/Util.Generators/Configuration/IGeneratorOptionsBuilder.cs new file mode 100644 index 000000000..b9cdec9f7 --- /dev/null +++ b/src/Util.Generators/Configuration/IGeneratorOptionsBuilder.cs @@ -0,0 +1,11 @@ +namespace Util.Generators.Configuration; + +/// +/// 生成器配置项构建器 +/// +public interface IGeneratorOptionsBuilder { + /// + /// 构建生成器配置项 + /// + Task BuildAsync(); +} \ No newline at end of file diff --git a/src/Util.Generators/Configuration/MessageOptions.cs b/src/Util.Generators/Configuration/MessageOptions.cs new file mode 100644 index 000000000..a1b462859 --- /dev/null +++ b/src/Util.Generators/Configuration/MessageOptions.cs @@ -0,0 +1,25 @@ +namespace Util.Generators.Configuration; + +/// +/// 消息配置项 +/// +public class MessageOptions { + /// + /// 必填项消息 + /// + public string RequiredMessage { get; set; } + /// + /// 最大长度消息 + /// + public string MaxLengthMessage { get; set; } + + /// + /// 复制 + /// + public MessageOptions Clone() { + return new() { + RequiredMessage = RequiredMessage, + MaxLengthMessage = MaxLengthMessage + }; + } +} \ No newline at end of file diff --git a/src/Util.Generators/Configuration/ProjectOptions.cs b/src/Util.Generators/Configuration/ProjectOptions.cs new file mode 100644 index 000000000..95ea9dafd --- /dev/null +++ b/src/Util.Generators/Configuration/ProjectOptions.cs @@ -0,0 +1,72 @@ +using Util.Data; + +namespace Util.Generators.Configuration; + +/// +/// 项目配置项 +/// +public class ProjectOptions { + /// + /// 初始化项目配置项 + /// + public ProjectOptions() { + Client = new ClientOptions(); + } + + /// + /// 项目名 + /// + public string Name { get; set; } + /// + /// 数据库类型 + /// + public DatabaseType DbType { get; set; } + /// + /// 目标数据库类型 + /// + public DatabaseType? TargetDbType { get; set; } + /// + /// 数据库连接字符串 + /// + public string ConnectionString { get; set; } + /// + /// 工作单元名称 + /// + public string UnitOfWorkName { get; set; } + /// + /// 是否启用 + /// + public bool Enabled { get; set; } + /// + /// 是否启用Utc + /// + public bool Utc { get; set; } + /// + /// 是否启用多语言 + /// + public bool I18n { get; set; } + /// + /// Web Api项目端口 + /// + public string ApiPort { get; set; } + /// + /// UI项目端口 + /// + public string UiPort { get; set; } + /// + /// 是否启用架构 + /// + public bool EnableSchema { get; set; } + /// + /// 项目类型 + /// + public ProjectType? ProjectType { get; set; } + /// + /// 客户端配置项 + /// + public ClientOptions Client { get; set; } + /// + /// 扩展 + /// + public string Extend { get; set; } +} \ No newline at end of file diff --git a/src/Util.Generators/Configuration/ProjectType.cs b/src/Util.Generators/Configuration/ProjectType.cs new file mode 100644 index 000000000..86c5b6f14 --- /dev/null +++ b/src/Util.Generators/Configuration/ProjectType.cs @@ -0,0 +1,15 @@ +namespace Util.Generators.Configuration; + +/// +/// 项目类型 +/// +public enum ProjectType { + /// + /// 生成Web API相关的项目,不生成UI + /// + WebApi, + /// + /// 生成UI,包含Web API项目 + /// + Ui +} \ No newline at end of file diff --git a/src/Util.Generators/Contexts/DbTypeExtensions.cs b/src/Util.Generators/Contexts/DbTypeExtensions.cs new file mode 100644 index 000000000..153c65dc2 --- /dev/null +++ b/src/Util.Generators/Contexts/DbTypeExtensions.cs @@ -0,0 +1,48 @@ +namespace Util.Generators.Contexts; + +/// +/// 数据类型扩展操作 +/// +public static class DbTypeExtensions { + /// + /// 转换为系统类型 + /// + public static SystemType? ToSystemType( this DbType? type ) { + if( type == null ) + return null; + switch( type ) { + case DbType.Guid: + return SystemType.Guid; + case DbType.AnsiString: + case DbType.String: + case DbType.AnsiStringFixedLength: + case DbType.StringFixedLength: + return SystemType.String; + case DbType.Boolean: + return SystemType.Bool; + case DbType.Byte: + return SystemType.Byte; + case DbType.Int16: + return SystemType.Short; + case DbType.Int32: + return SystemType.Int; + case DbType.Int64: + return SystemType.Long; + case DbType.Single: + return SystemType.Single; + case DbType.Double: + return SystemType.Double; + case DbType.Decimal: + return SystemType.Decimal; + case DbType.Date: + case DbType.Time: + case DbType.DateTime: + case DbType.DateTime2: + case DbType.DateTimeOffset: + return SystemType.DateTime; + case DbType.Binary: + return SystemType.Binary; + } + return null; + } +} \ No newline at end of file diff --git a/src/Util.Generators/Contexts/EntityContext.cs b/src/Util.Generators/Contexts/EntityContext.cs new file mode 100644 index 000000000..cd444a246 --- /dev/null +++ b/src/Util.Generators/Contexts/EntityContext.cs @@ -0,0 +1,151 @@ +using Util.Generators.Properties; + +namespace Util.Generators.Contexts; + +/// +/// 实体上下文 +/// +public class EntityContext { + /// + /// 初始化实体上下文 + /// + /// 项目上下文 + /// 实体名称 + public EntityContext( ProjectContext projectContext, string name ) { + ProjectContext = projectContext ?? throw new ArgumentNullException( nameof( projectContext ) ); + Name = name; + Id = Util.Helpers.Id.Create(); + Output = new Output { + RootPath = System.IO.Path.Combine( projectContext.GeneratorContext.OutputRootPath, projectContext.Name ), + FileNameNoExtension = name + }; + Properties = new List(); + } + + /// + /// 实体标识 + /// + public string Id { get; private init; } + /// + /// 项目上下文 + /// + public ProjectContext ProjectContext { get; } + /// + /// 输出 + /// + public Output Output { get; } + /// + /// 实体名称 + /// + public string Name { get; } + /// + /// 架构名 + /// + public string Schema { get; set; } + /// + /// 描述 + /// + public string Description { get; set; } + /// + /// 属性列表 + /// + public List Properties { get; } + /// + /// 项目名称 + /// + public string ProjectName => ProjectContext.Name; + /// + /// 项目复数名称 + /// + public string PluralName => Name.Pluralize().Pascalize(); + /// + /// 项目Pascal名称 + /// + public string PascalName => Name.Pascalize(); + /// + /// 项目驼峰名称 + /// + public string CamelName => Name.Camelize(); + + /// + /// 标识属性 + /// + public Property Key { + get { + var result = Properties.FirstOrDefault( t => t.IsKey ); + if( result == null ) { + var message = string.Format( GeneratorResource.NotSetPrimaryKey, GetNameWithSchema() ); + throw new InvalidOperationException( message ); + } + return result; + } + } + /// + /// 版本属性 + /// + public Property Version { + get { + return Properties.FirstOrDefault( t => t.IsVersion ); + } + } + + /// + /// 是否包含逻辑删除 + /// + public bool HasIsDeleted => Properties.Any( t => t.IsDeleted ); + + /// + /// 是否包含创建时间 + /// + public bool HasCreationTime => Properties.Any( t => t.IsCreationTime ); + + /// + /// 是否包含树形属性 + /// + public bool HasTree => Properties.Any( t => t.IsTree ); + + /// + /// 是否包含排序号 + /// + public bool HasSortId => Properties.Any( t => t.IsSortId ); + + /// + /// 是否多对多关联表 + /// + public bool IsRelationTable => Properties.Count == 2 && Properties.All( t => t.IsKey ); + + /// + /// 获取带架构的名称 + /// + public string GetNameWithSchema() { + if( Schema.IsEmpty() ) + return Name; + return $"{Schema}.{Name}"; + } + + /// + /// 复制 + /// + public EntityContext Clone() { + var generatorContext = ProjectContext.GeneratorContext.Clone(); + var projectContext = generatorContext.Projects.Find( t => t.Id == ProjectContext.Id ); + if ( projectContext == null ) + throw new InvalidOperationException(); + return projectContext.Entities.Find( t => t.Id == Id ); + } + + /// + /// 复制 + /// + /// 项目上下文 + public EntityContext Clone( ProjectContext projectContext ) { + var result = new EntityContext( projectContext, Name ) { + Id = Id, + Schema = Schema, + Description = Description + }; + Output.CopyTo( result.Output ); + Properties.ForEach( property => result.Properties.Add( property.Clone( result ) ) ); + return result; + } +} \ No newline at end of file diff --git a/src/Util.Generators/Contexts/GeneratorContext.cs b/src/Util.Generators/Contexts/GeneratorContext.cs new file mode 100644 index 000000000..346198fed --- /dev/null +++ b/src/Util.Generators/Contexts/GeneratorContext.cs @@ -0,0 +1,59 @@ +using Util.Generators.Configuration; +using Util.Generators.Properties; +using Util.Validation; + +namespace Util.Generators.Contexts; + +/// +/// 生成器上下文 +/// +public class GeneratorContext { + /// + /// 初始化生成器上下文 + /// + public GeneratorContext() { + Projects = new List(); + } + + /// + /// 模板根目录路径 + /// + [Required( ErrorMessageResourceType = typeof( GeneratorResource ), ErrorMessageResourceName = "TemplateRootPathIsEmpty" )] + public string TemplateRootPath { get; set; } + /// + /// 输出根目录路径 + /// + [Required( ErrorMessageResourceType = typeof( GeneratorResource ), ErrorMessageResourceName = "OutputRootPathIsEmpty" )] + public string OutputRootPath { get; set; } + /// + /// 消息上下文 + /// + public MessageOptions Message { get; set; } + /// + /// 项目上下文列表 + /// + public List Projects { get; } + + /// + /// 验证 + /// + public void Validate() { + var validationResult = DataAnnotationValidation.Validate( this ); + if( validationResult.IsValid == false ) + throw new InvalidOperationException( validationResult.First().ErrorMessage ); + Projects.ForEach( t => t.Validate() ); + } + + /// + /// 复制 + /// + public GeneratorContext Clone() { + var result = new GeneratorContext { + TemplateRootPath = TemplateRootPath, + OutputRootPath = OutputRootPath, + Message = Message?.Clone() + }; + Projects.ForEach( project => result.Projects.Add( project.Clone( result ) ) ); + return result; + } +} \ No newline at end of file diff --git a/src/Util.Generators/Contexts/GeneratorContextBuilder.cs b/src/Util.Generators/Contexts/GeneratorContextBuilder.cs new file mode 100644 index 000000000..86a20b7e3 --- /dev/null +++ b/src/Util.Generators/Contexts/GeneratorContextBuilder.cs @@ -0,0 +1,169 @@ +using Util.Data.Metadata; +using Util.Generators.Configuration; +using Util.Generators.Logs; +using Util.Helpers; + +namespace Util.Generators.Contexts; + +/// +/// 生成器上下文构建器 +/// +public class GeneratorContextBuilder : IGeneratorContextBuilder { + /// + /// 生成器日志操作 + /// + private readonly IGeneratorLogger _logger; + /// + /// 生成器配置项构建器 + /// + private readonly IGeneratorOptionsBuilder _optionsBuilder; + /// + /// 元数据服务工厂 + /// + private readonly IMetadataServiceFactory _metadataServiceFactory; + /// + /// 数据类型转换器工厂 + /// + private readonly ITypeConverterFactory _typeConverterFactory; + + /// + /// 初始化生成器上下文构建器 + /// + /// 日志 + /// 生成器配置项构建器 + /// 元数据服务工厂 + /// 数据类型转换器工厂 + public GeneratorContextBuilder( IGeneratorLogger logger, IGeneratorOptionsBuilder optionsBuilder, IMetadataServiceFactory metadataServiceFactory, ITypeConverterFactory typeConverterFactory ) { + _logger = logger ?? throw new ArgumentNullException( nameof( logger ) ); + _optionsBuilder = optionsBuilder ?? throw new ArgumentNullException( nameof( optionsBuilder ) ); + _metadataServiceFactory = metadataServiceFactory ?? throw new ArgumentNullException( nameof( metadataServiceFactory ) ); + _typeConverterFactory = typeConverterFactory ?? throw new ArgumentNullException( nameof( typeConverterFactory ) ); + } + + /// + /// 创建生成器上下文 + /// + public async Task BuildAsync() { + var options = await GetGeneratorOptions(); + _logger.LogOptions( options ); + _logger.BeginBuildContext(); + var result = new GeneratorContext { + TemplateRootPath = GetPhysicalPath( options.TemplatePath ), + OutputRootPath = GetPhysicalPath( options.OutputPath ), + Message = options.Messages.Clone() + }; + _logger.LogGeneratorContextBaseInfo( result ); + await AddProjectContexts( result, options.Projects.Select( t => t.Value ) ); + result.Validate(); + return result; + } + + /// + /// 获取生成器配置 + /// + protected async Task GetGeneratorOptions() { + return await _optionsBuilder.BuildAsync(); + } + + /// + /// 获取物理路径 + /// + protected virtual string GetPhysicalPath( string path ) { + if ( IsAbsolutePath( path ) ) + return path; + return Util.Helpers.Common.GetPhysicalPath( path ); + } + + /// + /// 是否绝对路径 + /// + protected virtual bool IsAbsolutePath( string path ) { + if ( Common.IsWindows == false && path.StartsWith( "/" ) ) + return true; + if ( path.Contains( @":\" ) ) + return true; + if ( path.Contains( ":/" ) ) + return true; + return false; + } + + /// + /// 添加项目上下文集合 + /// + protected async Task AddProjectContexts( GeneratorContext generatorContext, IEnumerable projectOptions ) { + foreach ( var projectOption in projectOptions ) { + var projectContext = await CreateProjectContext( generatorContext, projectOption ); + generatorContext.Projects.Add( projectContext ); + } + } + + /// + /// 创建项目上下文 + /// + protected async Task CreateProjectContext( GeneratorContext generatorContext, ProjectOptions projectOptions ) { + _logger.BeginBuildProjectContext( projectOptions.Name ); + var result = new ProjectContext( generatorContext ) { + Name = projectOptions.Name, + UnitOfWorkName = projectOptions.UnitOfWorkName, + Client = projectOptions.Client.Clone(), + DbType = projectOptions.DbType, + TargetDbType = projectOptions.TargetDbType ?? projectOptions.DbType, + ConnectionString = projectOptions.ConnectionString, + Enabled = projectOptions.Enabled, + Utc = projectOptions.Utc, + I18n = projectOptions.I18n, + ProjectType = projectOptions.ProjectType, + ApiPort = projectOptions.ApiPort, + UiPort = projectOptions.UiPort, + EnableSchema = projectOptions.EnableSchema, + Extend = projectOptions.Extend + }; + if ( projectOptions.Enabled == false ) + return result; + _logger.BeginGetDbMetadata( projectOptions ); + var databaseInfo = await GetDatabaseInfo( projectOptions ); + _logger.EndGetDbMetadata( databaseInfo ); + var typeConverter = _typeConverterFactory.Create( projectOptions.DbType ); + foreach ( var table in databaseInfo.Tables ) + AddEntityContext( result, table, typeConverter ); + result.Schemas.AddRange( result.Entities.GroupBy( t => t.Schema ).Select( t => t.Key ) ); + _logger.EndProjectContext( result ); + return result; + } + + /// + /// 获取数据库元数据信息 + /// + protected async Task GetDatabaseInfo( ProjectOptions projectOptions ) { + var metadataService = _metadataServiceFactory.Create( projectOptions.DbType, projectOptions.ConnectionString ); + return await metadataService.GetDatabaseInfoAsync(); + } + + /// + /// 添加实体上下文 + /// + protected void AddEntityContext( ProjectContext project, TableInfo table, ITypeConverter typeConverter ) { + var entity = new EntityContext( project, table.Name ) { + Schema = table.Schema, + Description = table.Comment + }; + table.Columns.ForEach( column => { + var length = Util.Helpers.Convert.ToInt( column.Length ); + var property = new Property( entity ) { + Name = column.Name, + Description = column.Comment, + IsKey = column.IsPrimaryKey, + IsRequired = column.IsNullable == false, + Type = typeConverter.ToType( column.DataType, length ), + NativeType = column.DataType, + MaxLength = length, + IsAutoIncrement = column.IsAutoIncrement, + Precision = column.Precision, + Scale = column.Scale + }; + property.SystemType = property.Type.ToSystemType(); + entity.Properties.Add( property ); + } ); + project.Entities.Add( entity ); + } +} \ No newline at end of file diff --git a/src/Util.Generators/Contexts/IGeneratorContextBuilder.cs b/src/Util.Generators/Contexts/IGeneratorContextBuilder.cs new file mode 100644 index 000000000..fb5592984 --- /dev/null +++ b/src/Util.Generators/Contexts/IGeneratorContextBuilder.cs @@ -0,0 +1,11 @@ +namespace Util.Generators.Contexts; + +/// +/// 生成器上下文构建器 +/// +public interface IGeneratorContextBuilder { + /// + /// 创建生成器上下文 + /// + Task BuildAsync(); +} \ No newline at end of file diff --git a/src/Util.Generators/Contexts/Output.cs b/src/Util.Generators/Contexts/Output.cs new file mode 100644 index 000000000..80695acb2 --- /dev/null +++ b/src/Util.Generators/Contexts/Output.cs @@ -0,0 +1,67 @@ +using Util.Helpers; + +namespace Util.Generators.Contexts; + +/// +/// 输出 +/// +public class Output { + /// + /// 根路径 + /// + public string RootPath { get; set; } + /// + /// 相对根路径 + /// + public string RelativeRootPath { get; set; } + /// + /// 输出文件名,不包含扩展名 + /// + public string FileNameNoExtension { get; set; } + /// + /// 输出文件扩展名,范例: ".txt" + /// + public string Extension { get; set; } + /// + /// 命名约定 + /// + public NamingConvention? NamingConvention { get; set; } + + /// + /// 输出路径 + /// + public string Path { + get { + if( RelativeRootPath.IsEmpty() ) + return Common.JoinPath( RootPath, GetFileName() ); + return Common.JoinPath( RootPath, RelativeRootPath, GetFileName() ); + } + } + + /// + /// 获取文件名 + /// + private string GetFileName() { + string result = null; + if( NamingConvention == null ) + result = FileNameNoExtension; + if( NamingConvention == Generators.NamingConvention.PascalCase ) + result = FileNameNoExtension.Pascalize(); + if( NamingConvention == Generators.NamingConvention.CamelCase ) + result = FileNameNoExtension.Camelize(); + return result + Extension; + } + + /// + /// 复制到输出副本 + /// + /// 输出副本 + public void CopyTo( Output copy ) { + copy.CheckNull( nameof( copy ) ); + copy.RootPath = RootPath; + copy.RelativeRootPath = RelativeRootPath; + copy.FileNameNoExtension = FileNameNoExtension; + copy.Extension = Extension; + copy.NamingConvention = NamingConvention; + } +} \ No newline at end of file diff --git a/src/Util.Generators/Contexts/ProjectContext.cs b/src/Util.Generators/Contexts/ProjectContext.cs new file mode 100644 index 000000000..f023f330a --- /dev/null +++ b/src/Util.Generators/Contexts/ProjectContext.cs @@ -0,0 +1,157 @@ +using Util.Data; +using Util.Generators.Configuration; +using Util.Generators.Properties; +using Util.Validation; + +namespace Util.Generators.Contexts; + +/// +/// 项目上下文 +/// +public class ProjectContext { + /// + /// 初始化项目上下文 + /// + public ProjectContext( GeneratorContext generatorContext ) { + GeneratorContext = generatorContext ?? throw new ArgumentNullException( nameof( generatorContext ) ); + Entities = new List(); + Schemas = new List(); + Client = new ClientOptions(); + Id = Util.Helpers.Id.Create(); + } + + /// + /// 项目标识 + /// + public string Id { get; private init; } + + /// + /// 项目名称 + /// + [Required(ErrorMessageResourceType = typeof( GeneratorResource ),ErrorMessageResourceName = "ProjectNameIsEmpty")] + public string Name { get; set; } + + /// + /// 数据库类型 + /// + public DatabaseType DbType { get; set; } + + /// + /// 目标数据库类型 + /// + public DatabaseType TargetDbType { get; set; } + + /// + /// 数据库连接字符串 + /// + public string ConnectionString { get; set; } + + /// + /// 工作单元名称 + /// + public string UnitOfWorkName { get; set; } + + /// + /// 是否启用 + /// + public bool Enabled { get; set; } + + /// + /// 是否启用Utc + /// + public bool Utc { get; set; } + + /// + /// 是否启用多语言 + /// + public bool I18n { get; set; } + + /// + /// Web Api项目端口 + /// + public string ApiPort { get; set; } + + /// + /// UI项目端口 + /// + public string UiPort { get; set; } + + /// + /// 项目类型 + /// + public ProjectType? ProjectType { get; set; } + + /// + /// 是否启用架构 + /// + public bool EnableSchema { get; set; } + + /// + /// 客户端配置 + /// + public ClientOptions Client { get; set; } + + /// + /// 扩展 + /// + public string Extend { get; set; } + + /// + /// 生成器上下文 + /// + public GeneratorContext GeneratorContext { get; } + + /// + /// 实体上下文列表 + /// + public List Entities { get; } + + /// + /// 架构列表 + /// + public List Schemas { get; } + + /// + /// 获取扩展 + /// + public T GetExtend() { + return Util.Helpers.Json.ToObject( Extend ); + } + + /// + /// 验证 + /// + public void Validate() { + var validationResult = DataAnnotationValidation.Validate( this ); + if( validationResult.IsValid ) + return; + throw new InvalidOperationException( validationResult.First().ErrorMessage ); + } + + /// + /// 复制 + /// + /// 生成器上下文 + public ProjectContext Clone( GeneratorContext generatorContext ) { + var result = new ProjectContext( generatorContext ) { + Id = Id, + Name = Name, + UnitOfWorkName = UnitOfWorkName, + DbType = DbType, + TargetDbType = TargetDbType, + ConnectionString = ConnectionString, + Client = Client.Clone(), + Enabled = Enabled, + Utc = Utc, + I18n = I18n, + ProjectType = ProjectType, + ApiPort = ApiPort, + UiPort = UiPort, + EnableSchema = EnableSchema, + Extend = Extend + }; + result.Schemas.AddRange( Schemas ); + Entities.ForEach( entity => result.Entities.Add( entity.Clone( result ) ) ); + return result; + } +} \ No newline at end of file diff --git a/src/Util.Generators/Contexts/Property.cs b/src/Util.Generators/Contexts/Property.cs new file mode 100644 index 000000000..0b0e1f66f --- /dev/null +++ b/src/Util.Generators/Contexts/Property.cs @@ -0,0 +1,195 @@ +namespace Util.Generators.Contexts; + +/// +/// 属性 +/// +public class Property { + /// + /// 初始化属性 + /// + /// 实体上下文 + public Property( EntityContext entity ) { + Entity = entity ?? throw new ArgumentNullException( nameof( entity ) ); + } + + /// + /// 实体上下文 + /// + public EntityContext Entity { get; } + /// + /// 名称 + /// + public string Name { get; set; } + /// + /// 描述 + /// + public string Description { get; set; } + /// + /// 是否标识 + /// + public bool IsKey { get; set; } + /// + /// 是否必填项 + /// + public bool IsRequired { get; set; } + /// + /// 是否自增 + /// + public bool IsAutoIncrement { get; set; } + /// + /// 数据类型 + /// + public DbType? Type { get; set; } + /// + /// 系统类型 + /// + public SystemType? SystemType { get; set; } + /// + /// 原生类型 + /// + public string NativeType { get; set; } + /// + /// 最大长度 + /// + public int? MaxLength { get; set; } + /// + /// 精度,即数值位数 + /// + public int? Precision { get; set; } + /// + /// 小数位数 + /// + public int? Scale { get; set; } + /// + /// 获取字段名称 + /// + public string FieldName => $"_{Name.Camelize()}"; + /// + /// 获取类型名称 + /// + public string TypeName => SystemType.ToTypeString( !IsRequired ); + /// + /// 获取可空类型名称 + /// + public string NullableTypeName => SystemType.ToTypeString( true ); + /// + /// 是否行版本 + /// + public bool IsVersion => Name.ToUpperInvariant() == "Version".ToUpperInvariant() && SystemType == Generators.SystemType.Binary; + /// + /// 是否逻辑删除 + /// + public bool IsDeleted => Name.ToUpperInvariant() == "IsDeleted".ToUpperInvariant() && SystemType == Generators.SystemType.Bool; + /// + /// 是否创建人标识 + /// + public bool IsCreatorId => Name.ToUpperInvariant() == "CreatorId".ToUpperInvariant(); + /// + /// 是否修改人标识 + /// + public bool IsLastModifierId => Name.ToUpperInvariant() == "LastModifierId".ToUpperInvariant(); + /// + /// 是否创建时间 + /// + public bool IsCreationTime => Name.ToUpperInvariant() == "CreationTime".ToUpperInvariant() && SystemType == Generators.SystemType.DateTime; + /// + /// 是否最后修改时间 + /// + public bool IsLastModificationTime => Name.ToUpperInvariant() == "LastModificationTime".ToUpperInvariant() && SystemType == Generators.SystemType.DateTime; + /// + /// 是否拼音简码 + /// + public bool IsPinYin => Name.ToUpperInvariant() == "PinYin".ToUpperInvariant(); + /// + /// 是否排序号 + /// + public bool IsSortId => Name.ToUpperInvariant() == "SortId".ToUpperInvariant(); + /// + /// 是否父标识 + /// + public bool IsParentId => Name.ToUpperInvariant() == "ParentId".ToUpperInvariant(); + /// + /// 是否扩展属性 + /// + public bool IsExtraProperties => Name.ToUpperInvariant() == "ExtraProperties".ToUpperInvariant(); + /// + /// 是否审计属性 + /// + public bool IsAudited => Name.ToUpperInvariant() == "CreatorId".ToUpperInvariant() || + Name.ToUpperInvariant() == "CreationTime".ToUpperInvariant() || + Name.ToUpperInvariant() == "LastModifierId".ToUpperInvariant() || + Name.ToUpperInvariant() == "LastModificationTime".ToUpperInvariant(); + /// + /// 是否字符串 + /// + public bool IsString => SystemType == Generators.SystemType.String; + /// + /// 是否布尔值 + /// + public bool IsBool => SystemType == Generators.SystemType.Bool; + /// + /// 是否日期 + /// + public bool IsDateTime => SystemType == Generators.SystemType.DateTime; + /// + /// 是否整数 + /// + public bool IsInteger => SystemType is Generators.SystemType.Short or Generators.SystemType.Int or Generators.SystemType.Long; + /// + /// 是否浮点数 + /// + public bool IsFloat => SystemType is Generators.SystemType.Single or Generators.SystemType.Double or Generators.SystemType.Decimal; + /// + /// 是否数值类型,包含整数和浮点数 + /// + public bool IsNumber => IsInteger || IsFloat; + /// + /// 是否Guid类型 + /// + public bool IsGuid => SystemType == Generators.SystemType.Guid; + + /// + /// 是否树形属性 + /// + public bool IsTree => Entity.Properties.Exists( t => t.Name.ToUpperInvariant() == "ParentId".ToUpperInvariant() ) && + Entity.Properties.Exists( t => t.Name.ToUpperInvariant() == "Path".ToUpperInvariant() ) && + ( Name.ToUpperInvariant() == "ParentId".ToUpperInvariant() || + Name.ToUpperInvariant() == "Path".ToUpperInvariant() || + Name.ToUpperInvariant() == "Level".ToUpperInvariant() || + Name.ToUpperInvariant() == "SortId".ToUpperInvariant() ); + + /// + /// 属性驼峰名称 + /// + public string CamelName => Name.Camelize(); + + /// + /// 获取属性最大安全长度 + /// + public int GetSafeMaxLength() { + if( NativeType == "nvarchar" ) + return MaxLength.SafeValue() / 2; + return MaxLength.SafeValue(); + } + + /// + /// 复制 + /// + /// 实体上下文 + public Property Clone( EntityContext entityContext ) { + var result = new Property( entityContext ) { + Name = Name, + Description = Description, + IsKey = IsKey, + IsRequired = IsRequired, + Type = Type, + SystemType = SystemType, + NativeType = NativeType, + MaxLength = MaxLength, + IsAutoIncrement = IsAutoIncrement, + Precision = Precision, + Scale = Scale + }; + return result; + } +} \ No newline at end of file diff --git a/src/Util.Generators/Generator.cs b/src/Util.Generators/Generator.cs new file mode 100644 index 000000000..37211021a --- /dev/null +++ b/src/Util.Generators/Generator.cs @@ -0,0 +1,124 @@ +using Util.Generators.Contexts; +using Util.Generators.Logs; +using Util.Generators.Resources; +using Util.Generators.Templates; + +namespace Util.Generators; + +/// +/// 生成器 +/// +public class Generator : IGenerator { + /// + /// 日志 + /// + private readonly IGeneratorLogger _logger; + /// + /// 生成器上下文构建器 + /// + private readonly IGeneratorContextBuilder _contextBuilder; + /// + /// 生成器上下文 + /// + private GeneratorContext _context; + /// + /// 模板查找器 + /// + private readonly ITemplateFinder _templateFinder; + /// + /// 静态资源管理器 + /// + private readonly IResourceManager _resourceManager; + /// + /// 计时器 + /// + private readonly Stopwatch _stopwatch; + + /// + /// 初始化生成器 + /// + /// 日志 + /// 生成器上下文构建器 + /// 模板查找器 + /// 静态资源管理器 + public Generator( IGeneratorLogger logger, IGeneratorContextBuilder contextBuilder, ITemplateFinder templateFinder, IResourceManager resourceManager ) { + _logger = logger; + _contextBuilder = contextBuilder ?? throw new ArgumentNullException( nameof( contextBuilder ) ); + _templateFinder = templateFinder ?? throw new ArgumentNullException( nameof( templateFinder ) ); + _resourceManager = resourceManager ?? throw new ArgumentNullException( nameof( resourceManager ) ); + _stopwatch = new Stopwatch(); + } + + /// + public async Task GenerateAsync() { + try { + _stopwatch.Start(); + _context = await _contextBuilder.BuildAsync(); + foreach ( var project in _context.Projects ) { + await GenerateProject( project ); + } + _logger.EndGenerate( _stopwatch ); + } + catch ( Exception ex ) { + _logger.LogException( ex ); + throw; + } + } + + /// + /// 生成项目 + /// + /// 项目上下文 + protected virtual async Task GenerateProject( ProjectContext projectContext ) { + if ( projectContext.Enabled == false ) { + _logger.DisableProject( projectContext.Name ); + return; + } + var templates = GeTemplates( _context.TemplateRootPath, projectContext ); + _logger.BeginGenerateProject( projectContext.Name ); + foreach ( var entity in projectContext.Entities ) { + await GenerateEntity( entity, templates ); + } + await _resourceManager.ImportsAsync( _context.TemplateRootPath, _context.OutputRootPath, projectContext ); + _logger.EndGenerateProject( projectContext.Name ); + } + + /// + /// 获取模板列表 + /// + /// 模板根路径 + /// 项目上下文 + protected virtual List GeTemplates( string templateRootPath, ProjectContext projectContext ) { + _logger.BeginGeTemplates( projectContext.Name, templateRootPath ); + var result = _templateFinder.Find( templateRootPath, projectContext ).ToList(); + _logger.EndGeTemplates( projectContext.Name, result ); + return result; + } + + /// + /// 生成实体 + /// + /// 实体上下文 + /// 模板列表 + protected virtual async Task GenerateEntity( EntityContext entityContext, List templates ) { + _logger.BeginGenerateEntity( entityContext ); + if ( IsSkip( entityContext ) ) + return; + foreach ( var template in templates ) { + await template.RenderAsync( entityContext.Clone() ); + _logger.RenderTemplate( entityContext, template ); + } + _logger.EndGenerateEntity( entityContext ); + } + + /// + /// 是否跳过 + /// + protected virtual bool IsSkip( EntityContext entityContext ) { + if ( entityContext.IsRelationTable ) { + _logger.SkipRelationTable( entityContext ); + return true; + } + return false; + } +} \ No newline at end of file diff --git a/src/Util.Generators/IGenerator.cs b/src/Util.Generators/IGenerator.cs new file mode 100644 index 000000000..5fac0b1f1 --- /dev/null +++ b/src/Util.Generators/IGenerator.cs @@ -0,0 +1,11 @@ +namespace Util.Generators; + +/// +/// 生成器 +/// +public interface IGenerator { + /// + /// 生成 + /// + Task GenerateAsync(); +} \ No newline at end of file diff --git a/src/Util.Generators/Logs/GeneratorLogger.cs b/src/Util.Generators/Logs/GeneratorLogger.cs new file mode 100644 index 000000000..91c064a08 --- /dev/null +++ b/src/Util.Generators/Logs/GeneratorLogger.cs @@ -0,0 +1,230 @@ +using Util.Data.Metadata; +using Util.Generators.Configuration; +using Util.Generators.Contexts; +using Util.Generators.Resources; +using Util.Generators.Templates; +using Util.Logging; + +namespace Util.Generators.Logs; + +/// +/// 生成器日志操作 +/// +public class GeneratorLogger : IGeneratorLogger { + /// + /// 日志 + /// + private readonly ILog _logger; + + /// + /// 初始化生成器日志操作 + /// + /// 日志 + public GeneratorLogger( ILog logger ) { + _logger = logger ?? Log.Null; + } + + /// + public void LogException( Exception exception ) { + _logger.Exception( exception ).LogError(); + } + + /// + public void LogOptions( GeneratorOptions options ) { + _logger.AppendLine( "读取生成器配置:" ) + .AppendLine( $" 模板根路径: {options.TemplatePath}," ) + .AppendLine( $" 输出根路径: {options.OutputPath}," ) + .AppendLine( $" 消息配置:" ) + .AppendLine( $" 必填项消息: {options.Messages.RequiredMessage}" ) + .AppendLine( $" 最大长度消息: {options.Messages.MaxLengthMessage}" ) + .AppendLine( $" 项目配置:" ); + foreach ( var project in options.Projects.Values ) { + _logger.AppendLine( $" 项目名称: {project.Name}" ) + .AppendLine( $" 数据库类型: {project.DbType}" ) + .AppendLine( $" 目标数据库类型: {project.TargetDbType}" ) + .AppendLine( $" 数据库连接字符串: {project.ConnectionString}" ) + .AppendLine( $" 工作单元名称: {project.UnitOfWorkName}" ) + .AppendLine( $" 是否启用: {project.Enabled.Description()}" ); + } + _logger.LogDebug(); + } + + /// + public void BeginBuildContext() { + _logger.Message( "开始构建生成器上下文" ).LogDebug(); + } + + /// + public void LogGeneratorContextBaseInfo( GeneratorContext context ) { + _logger.AppendLine( "生成器上下文基础信息:" ) + .AppendLine( $" 模板根路径: {context.TemplateRootPath}," ) + .AppendLine( $" 输出根路径: {context.OutputRootPath}," ) + .AppendLine( $" 消息上下文信息:" ) + .AppendLine( $" 必填项消息: {context.Message.RequiredMessage}" ) + .AppendLine( $" 最大长度消息: {context.Message.MaxLengthMessage}" ) + .LogDebug(); + } + + /// + public void BeginBuildProjectContext( string project ) { + _logger.Message( $"开始构建生成器项目上下文: {project}" ).LogDebug(); + } + + /// + public void BeginGetDbMetadata( ProjectOptions projectOptions ) { + _logger.AppendLine( "开始获取数据库元数据," ) + .AppendLine( $" 数据库类型: {projectOptions.DbType}," ) + .AppendLine( $" 数据库连接字符串: {projectOptions.ConnectionString}" ) + .LogDebug(); + } + + /// + public void EndGetDbMetadata( DatabaseInfo info ) { + _logger.AppendLine( "获取数据库元数据完成," ) + .AppendLine( $" 数据库名称: {info.Name}" ) + .LogDebug(); + } + + /// + public void EndProjectContext( ProjectContext context ) { + _logger.AppendLine( "构建生成器项目上下文完成:" ) + .AppendLine( $" 项目名称: {context.Name}," ) + .AppendLine( $" 目标数据库类型: {context.TargetDbType}," ) + .AppendLine( $" 工作单元名称: {context.UnitOfWorkName}," ) + .AppendLine( $" 是否启用: {context.Enabled.Description()}" ); + foreach ( var entity in context.Entities ) { + _logger.AppendLine( $" 实体: {entity.Name}," ) + .AppendLineIf( $" 架构: {entity.Schema},", entity.Schema.IsEmpty() == false ) + .AppendLine( $" 描述: {entity.Description}," ) + .AppendLine( $" 输出根路径: {entity.Output.RootPath}," ) + .AppendLineIf( $" 输出相对根路径: {entity.Output.RelativeRootPath},", entity.Output.RelativeRootPath.IsEmpty() == false ) + .AppendLine( $" 输出文件名: {entity.Output.FileNameNoExtension}," ) + .AppendLineIf( $" 输出文件扩展名: {entity.Output.Extension},", entity.Output.Extension.IsEmpty() == false ) + .AppendLineIf( $" 输出文件命名约定: {entity.Output.NamingConvention},", entity.Output.NamingConvention != null ); + foreach ( var property in entity.Properties ) { + _logger.AppendLine( $" 属性: {property.Name}" ) + .AppendLine( $" 描述: {property.Description}," ) + .AppendLine( $" 是否主键: {property.IsKey.Description()}," ) + .AppendLine( $" 是否必填: {property.IsRequired.Description()}," ) + .AppendLine( $" 是否自增: {property.IsAutoIncrement.Description()}," ) + .AppendLine( $" DbType: {property.Type}," ) + .AppendLine( $" SystemType: {property.SystemType}," ) + .AppendLine( $" NativeType: {property.NativeType}," ) + .AppendLineIf( $" 最大长度: {property.MaxLength},", property.MaxLength.SafeValue() > 0 ) + .AppendLineIf( $" 数值精度: {property.Precision},",property.Precision.SafeValue() != 0 ) + .AppendLineIf( $" 小数位数: {property.Scale}", property.Scale.SafeValue() != 0 ); + } + } + _logger.LogDebug(); + } + + /// + public void BeginGeTemplates( string project, string templateRootPath ) { + _logger.AppendLine( $"开始获取 {project}项目 的模板列表" ) + .AppendLine( $" 模板根路径: {templateRootPath}" ) + .LogDebug(); + } + + /// + public void EndGeTemplates( string project, List templates ) { + _logger.AppendLine( $"获取 {project}项目 的模板列表完成" ); + foreach ( var template in templates ) { + _logger.AppendLine( $" {template.Path}," ); + _logger.Line(); + } + _logger.LogDebug(); + } + + /// + public void FilterTemplate( string path ) { + _logger.AppendLine( $"过滤模板: {path}" ).LogDebug(); + } + + /// + public void BeginGenerateProject( string project ) { + _logger.AppendLine( $"开始生成项目: {project}" ).LogInformation(); + } + + /// + public void DisableProject( string project ) { + _logger.AppendLine( $"项目 {project} 已禁用,不生成该项目代码" ).LogInformation(); + } + + /// + public void BeginGenerateEntity( EntityContext entity ) { + _logger.AppendLine( $"开始生成 {entity.Description}( {entity.Name} ) 全部模板代码" ).LogDebug(); + } + + /// + public void SkipRelationTable( EntityContext entity ) { + _logger.AppendLine( $"{entity.Description}( {entity.Name} ) 是关联表,已跳过生成" ).LogDebug(); + } + + /// + public void RenderTemplate( EntityContext entity, ITemplate template ) { + _logger.AppendLine( $"生成 {entity.Description}( {entity.Name} ) 代码" ) + .AppendLine( $" 模板: {template.Path}" ) + .LogDebug(); + } + + /// + public void EndGenerateEntity( EntityContext entity ) { + _logger.AppendLine( $"{entity.Description}( {entity.Name} ) 代码生成成功" ).LogInformation(); + } + + /// + public void FilterResource( string path ) { + _logger.AppendLine( $"过滤资源: {path}" ).LogDebug(); + } + + /// + public void ImportResource( string templateRootPath, string outputRootPath, string project, Resource resource ) { + _logger.AppendLine( "开始导入静态资源" ) + .AppendLine( $" 模板根目录路径: {templateRootPath}" ) + .AppendLine( $" 输出根目录路径: {outputRootPath}" ) + .AppendLine( $" 项目名称: {project}" ) + .AppendLine( $" 资源路径: {resource.From}" ) + .AppendLine( $" 目标路径: {resource.To}" ) + .LogDebug(); + } + + /// + public void ImportDirectory( string templateRootPath, string outputRootPath, string project, string from, string to ) { + _logger.AppendLine( "开始导入静态资源目录" ) + .AppendLine( $" 模板根目录路径: {templateRootPath}" ) + .AppendLine( $" 输出根目录路径: {outputRootPath}" ) + .AppendLine( $" 项目名称: {project}" ) + .AppendLine( $" 资源路径: {from}" ) + .AppendLine( $" 目标路径: {to}" ) + .LogDebug(); + } + + /// + public void BeginImportFile( string sourceFile, string destFile ) { + _logger.AppendLine( "开始导入静态资源文件" ) + .AppendLine( $" 资源路径: {sourceFile}" ) + .AppendLine( $" 目标路径: {destFile}" ) + .LogDebug(); + } + + /// + public void EndImportFile( string sourceFile, string destFile ) { + _logger.AppendLine( "导入静态资源文件成功" ) + .AppendLine( $" 资源路径: {sourceFile}" ) + .AppendLine( $" 目标路径: {destFile}" ) + .LogInformation(); + } + + /// + public void EndGenerateProject( string project ) { + _logger.AppendLine( $"项目 {project} 生成完成" ).LogInformation(); + } + + /// + /// 生成完成 + /// + /// 计时器 + public void EndGenerate( Stopwatch stopwatch ) { + _logger.AppendLine( $"代码生成完成,耗时: {stopwatch.Elapsed.Description()}" ).LogInformation(); + } +} \ No newline at end of file diff --git a/src/Util.Generators/Logs/IGeneratorLogger.cs b/src/Util.Generators/Logs/IGeneratorLogger.cs new file mode 100644 index 000000000..e095fc2ad --- /dev/null +++ b/src/Util.Generators/Logs/IGeneratorLogger.cs @@ -0,0 +1,144 @@ +using Util.Data.Metadata; +using Util.Dependency; +using Util.Generators.Configuration; +using Util.Generators.Contexts; +using Util.Generators.Resources; +using Util.Generators.Templates; + +namespace Util.Generators.Logs; + +/// +/// 生成器日志操作 +/// +public interface IGeneratorLogger : ITransientDependency { + /// + /// 记录异常 + /// + /// 异常 + void LogException( Exception exception ); + /// + /// 打印生成器配置 + /// + void LogOptions( GeneratorOptions options ); + /// + /// 开始构建生成器上下文 + /// + void BeginBuildContext(); + /// + /// 打印生成器上下文基础信息 + /// + /// 生成器上下文 + void LogGeneratorContextBaseInfo( GeneratorContext context ); + /// + /// 开始构建生成器项目上下文 + /// + /// 项目 + void BeginBuildProjectContext( string project ); + /// + /// 开始获取数据库元数据 + /// + /// 项目配置 + void BeginGetDbMetadata( ProjectOptions projectOptions ); + /// + /// 获取数据库元数据完成 + /// + /// 数据库信息 + void EndGetDbMetadata( DatabaseInfo info ); + /// + /// 构建生成器项目上下文完成 + /// + /// 项目上下文 + void EndProjectContext( ProjectContext context ); + /// + /// 开始获取模板列表 + /// + /// 项目 + /// 模板根路径 + void BeginGeTemplates( string project, string templateRootPath ); + /// + /// 获取模板列表完成 + /// + /// 项目 + /// 模板列表 + void EndGeTemplates( string project, List templates ); + /// + /// 过滤模板 + /// + /// 模板路径 + void FilterTemplate( string path ); + /// + /// 开始生成项目代码 + /// + /// 项目 + void BeginGenerateProject( string project ); + /// + /// 禁用项目代码 + /// + /// 项目 + void DisableProject( string project ); + /// + /// 开始生成实体代码 + /// + /// 实体上下文 + void BeginGenerateEntity( EntityContext entity ); + /// + /// 跳过关联表实体 + /// + /// 实体上下文 + void SkipRelationTable( EntityContext entity ); + /// + /// 渲染模板 + /// + /// 实体上下文 + /// 模板 + public void RenderTemplate( EntityContext entity, ITemplate template ); + /// + /// 生成实体代码完成 + /// + /// 实体上下文 + void EndGenerateEntity( EntityContext entity ); + /// + /// 过滤资源 + /// + /// 资源路径 + void FilterResource( string path ); + /// + /// 开始导入资源 + /// + /// 模板根目录路径 + /// 输出根目录路径 + /// 项目名称 + /// 资源 + void ImportResource( string templateRootPath, string outputRootPath, string project, Resource resource ); + /// + /// 开始导入目录 + /// + /// 模板根目录路径 + /// 输出根目录路径 + /// 项目名称 + /// 资源路径 + /// 目标路径 + void ImportDirectory( string templateRootPath, string outputRootPath, string project, string from, string to ); + /// + /// 开始导入文件 + /// + /// 源文件路径 + /// 目标文件路径 + void BeginImportFile( string sourceFile, string destFile ); + /// + /// 导入文件完成 + /// + /// 源文件路径 + /// 目标文件路径 + void EndImportFile( string sourceFile, string destFile ); + /// + /// 生成项目代码完成 + /// + /// 项目 + void EndGenerateProject( string project ); + /// + /// 生成完成 + /// + /// 计时器 + void EndGenerate( Stopwatch stopwatch ); +} \ No newline at end of file diff --git a/src/Util.Generators/Logs/NullGeneratorLogger.cs b/src/Util.Generators/Logs/NullGeneratorLogger.cs new file mode 100644 index 000000000..21144bc4a --- /dev/null +++ b/src/Util.Generators/Logs/NullGeneratorLogger.cs @@ -0,0 +1,19 @@ +using Util.Logging; + +namespace Util.Generators.Logs; + +/// +/// 空生成器日志操作 +/// +public class NullGeneratorLogger : GeneratorLogger { + /// + /// 初始化空生成器日志操作 + /// + public NullGeneratorLogger() : base( Log.Null ) { + } + + /// + /// 空生成器日志操作实例 + /// + public static readonly IGeneratorLogger Instance = new NullGeneratorLogger(); +} \ No newline at end of file diff --git a/src/Util.Generators/NamingConvention.cs b/src/Util.Generators/NamingConvention.cs new file mode 100644 index 000000000..b5e5cfef5 --- /dev/null +++ b/src/Util.Generators/NamingConvention.cs @@ -0,0 +1,15 @@ +namespace Util.Generators; + +/// +/// 命名约定 +/// +public enum NamingConvention { + /// + /// 帕斯卡命名,所有单词首字母大写 + /// + PascalCase, + /// + /// 驼峰命名,第一个单词首字母小写,其余单词首字母大写 + /// + CamelCase +} \ No newline at end of file diff --git a/src/Util.Generators/Properties/GeneratorResource.Designer.cs b/src/Util.Generators/Properties/GeneratorResource.Designer.cs new file mode 100644 index 000000000..bf8213112 --- /dev/null +++ b/src/Util.Generators/Properties/GeneratorResource.Designer.cs @@ -0,0 +1,99 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace Util.Generators.Properties { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class GeneratorResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal GeneratorResource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Util.Generators.Properties.GeneratorResource", typeof(GeneratorResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 表 {0} 未设置主键 的本地化字符串。 + /// + public static string NotSetPrimaryKey { + get { + return ResourceManager.GetString("NotSetPrimaryKey", resourceCulture); + } + } + + /// + /// 查找类似 输出根目录路径不能为空 的本地化字符串。 + /// + public static string OutputRootPathIsEmpty { + get { + return ResourceManager.GetString("OutputRootPathIsEmpty", resourceCulture); + } + } + + /// + /// 查找类似 项目名称不能为空 的本地化字符串。 + /// + public static string ProjectNameIsEmpty { + get { + return ResourceManager.GetString("ProjectNameIsEmpty", resourceCulture); + } + } + + /// + /// 查找类似 模板根目录路径不能为空 的本地化字符串。 + /// + public static string TemplateRootPathIsEmpty { + get { + return ResourceManager.GetString("TemplateRootPathIsEmpty", resourceCulture); + } + } + } +} diff --git a/src/Util.Generators/Properties/GeneratorResource.resx b/src/Util.Generators/Properties/GeneratorResource.resx new file mode 100644 index 000000000..ed51750e0 --- /dev/null +++ b/src/Util.Generators/Properties/GeneratorResource.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 表 {0} 未设置主键 + + + 输出根目录路径不能为空 + + + 项目名称不能为空 + + + 模板根目录路径不能为空 + + \ No newline at end of file diff --git a/src/Util.Generators/Resources/IResourceManager.cs b/src/Util.Generators/Resources/IResourceManager.cs new file mode 100644 index 000000000..244b9a732 --- /dev/null +++ b/src/Util.Generators/Resources/IResourceManager.cs @@ -0,0 +1,16 @@ +using Util.Generators.Contexts; + +namespace Util.Generators.Resources; + +/// +/// 静态资源管理器 +/// +public interface IResourceManager { + /// + /// 导入静态资源 + /// + /// 模板根目录路径 + /// 输出根目录路径 + /// 项目上下文 + Task ImportsAsync( string templateRootPath, string outputRootPath,ProjectContext project ); +} \ No newline at end of file diff --git a/src/Util.Generators/Resources/Resource.cs b/src/Util.Generators/Resources/Resource.cs new file mode 100644 index 000000000..fbc2d366b --- /dev/null +++ b/src/Util.Generators/Resources/Resource.cs @@ -0,0 +1,26 @@ +namespace Util.Generators.Resources; + +/// +/// 静态资源 +/// +public class Resource { + /// + /// 初始化静态资源 + /// + /// 资源路径 + /// 目标路径 + public Resource( string from,string to ) { + From = from; + To = to; + } + + /// + /// 资源路径 + /// + public string From { get; } + + /// + /// 目标路径 + /// + public string To { get; } +} \ No newline at end of file diff --git a/src/Util.Generators/Resources/ResourceManager.cs b/src/Util.Generators/Resources/ResourceManager.cs new file mode 100644 index 000000000..7a78fb423 --- /dev/null +++ b/src/Util.Generators/Resources/ResourceManager.cs @@ -0,0 +1,107 @@ +using Util.Generators.Contexts; +using Util.Generators.Logs; +using Util.Generators.Templates; + +namespace Util.Generators.Resources; + +/// +/// 静态资源管理器 +/// +public class ResourceManager : IResourceManager { + /// + /// 静态资源列表 + /// + private static readonly List _resources = new(); + /// + /// 模板过滤器管理器 + /// + private readonly ITemplateFilterManager _filterManager; + /// + /// 生成器日志 + /// + private readonly IGeneratorLogger _logger; + + /// + /// 初始化静态资源管理器 + /// + /// 模板过滤器管理器 + /// 生成器日志 + public ResourceManager( ITemplateFilterManager filterManager, IGeneratorLogger logger = null ) { + _filterManager = filterManager ?? throw new ArgumentNullException( nameof( filterManager ) ); + _logger = logger ?? NullGeneratorLogger.Instance; + } + + /// + /// 添加静态资源 + /// + /// 静态资源文件路径,即模板项目中要复制目录或文件的相对模板根路径,范例:src/Presentation/ClientApp/src/assets + /// 复制到输出目录的目标路径,可使用项目占位符{Project},范例:src/{Project}.Ui/ClientApp/src/assets + public static void AddResource( string from,string to ) { + if ( from.IsEmpty() ) + return; + if ( to.IsEmpty() ) + return; + _resources.Add( new Resource( from,to ) ); + } + + /// + public virtual Task ImportsAsync( string templateRootPath, string outputRootPath, ProjectContext project ) { + foreach ( var resource in _resources ) { + _logger.ImportResource( templateRootPath, outputRootPath, project.Name, resource ); + var path = GetResourcePath( templateRootPath, resource.From ); + if ( _filterManager.IsFilter( path, project ) ) { + _logger.FilterResource( path ); + continue; + } + if ( Directory.Exists( path ) ) { + ImportDirectory( templateRootPath, outputRootPath, project.Name, resource.From, resource.To ); + continue; + } + if ( File.Exists( path ) ) { + ImportFile( templateRootPath, outputRootPath, project.Name, resource.From, resource.To, path ); + continue; + } + } + return Task.CompletedTask; + } + + /// + /// 获取资源绝对路径 + /// + private string GetResourcePath( string templateRootPath, string from ) { + return Path.Combine( templateRootPath, from ); + } + + /// + /// 导入目录 + /// + protected virtual void ImportDirectory( string templateRootPath, string outputRootPath, string project, string from,string to ) { + _logger.ImportDirectory( templateRootPath, outputRootPath, project, from, to ); + var path = GetResourcePath( templateRootPath, from ); + var files = Directory.GetFiles( path, "*.*",SearchOption.AllDirectories ); + foreach ( var file in files ) { + ImportFile( templateRootPath, outputRootPath, project, from,to, file ); + } + } + + /// + /// 导入文件 + /// + protected virtual void ImportFile( string templateRootPath, string outputRootPath, string project, string from, string to, string file ) { + var destPath = GetTargetPath( templateRootPath, outputRootPath, project, from, to, file ); + _logger.BeginImportFile( file, destPath ); + var fileInfo = new FileInfo( destPath ); + Directory.CreateDirectory( fileInfo.DirectoryName ); + File.Copy( file, destPath,true ); + _logger.EndImportFile( file, destPath ); + } + + /// + /// 获取目标文件绝对路径 + /// + private string GetTargetPath( string templateRootPath, string outputRootPath, string project, string from, string to, string file ) { + var subPath = file.RemoveStart( GetResourcePath( templateRootPath, from ) ).RemoveStart( "\\" ); + to = to.Replace( "{Project}", project, StringComparison.OrdinalIgnoreCase ); + return Path.Combine( outputRootPath, project, to, subPath ); + } +} \ No newline at end of file diff --git a/src/Util.Generators/SystemType.cs b/src/Util.Generators/SystemType.cs new file mode 100644 index 000000000..272297148 --- /dev/null +++ b/src/Util.Generators/SystemType.cs @@ -0,0 +1,55 @@ +namespace Util.Generators; + +/// +/// 系统类型 +/// +public enum SystemType { + /// + /// 全局唯一标识 + /// + Guid, + /// + /// 字符串 + /// + String, + /// + /// 字节 + /// + Byte, + /// + /// 16位整型 + /// + Short, + /// + /// 32位整型 + /// + Int, + /// + /// 64位整型 + /// + Long, + /// + /// 32位浮点型 + /// + Single, + /// + /// 64位浮点型 + /// + Double, + /// + /// 128位浮点型 + /// + Decimal, + /// + /// 布尔 + /// + Bool, + /// + /// 日期 + /// + DateTime, + /// + /// 字节流 + /// + Binary +} \ No newline at end of file diff --git a/src/Util.Generators/SystemTypeExtensions.cs b/src/Util.Generators/SystemTypeExtensions.cs new file mode 100644 index 000000000..a24481dbe --- /dev/null +++ b/src/Util.Generators/SystemTypeExtensions.cs @@ -0,0 +1,44 @@ +namespace Util.Generators; + +/// +/// 系统类型扩展操作 +/// +public static class SystemTypeExtensions { + /// + /// 获取类型字符串 + /// + /// 系统类型 + /// 是否可空 + public static string ToTypeString( this SystemType? type, bool isNullable ) { + if ( type == null ) + return null; + switch( type ) { + case SystemType.Guid: + return isNullable? "Guid?": "Guid"; + case SystemType.String: + return "string"; + case SystemType.Byte: + return isNullable ? "byte?" : "byte"; + case SystemType.Short: + return isNullable ? "short?" : "short"; + case SystemType.Int: + return isNullable ? "int?" : "int"; + case SystemType.Long: + return isNullable ? "long?" : "long"; + case SystemType.Single: + return isNullable ? "float?" : "float"; + case SystemType.Double: + return isNullable ? "double?" : "double"; + case SystemType.Decimal: + return isNullable ? "decimal?" : "decimal"; + case SystemType.Bool: + return isNullable ? "bool?" : "bool"; + case SystemType.DateTime: + return isNullable ? "DateTime?" : "DateTime"; + case SystemType.Binary: + return "byte[]"; + default: + return null; + } + } +} \ No newline at end of file diff --git a/src/Util.Generators/Templates/ITemplate.cs b/src/Util.Generators/Templates/ITemplate.cs new file mode 100644 index 000000000..5f39b38b0 --- /dev/null +++ b/src/Util.Generators/Templates/ITemplate.cs @@ -0,0 +1,18 @@ +using Util.Generators.Contexts; + +namespace Util.Generators.Templates; + +/// +/// 模板 +/// +public interface ITemplate { + /// + /// 模板路径 + /// + string Path { get; } + /// + /// 渲染 + /// + /// 实体上下文 + Task RenderAsync( EntityContext context ); +} \ No newline at end of file diff --git a/src/Util.Generators/Templates/ITemplateFilter.cs b/src/Util.Generators/Templates/ITemplateFilter.cs new file mode 100644 index 000000000..d0bb80033 --- /dev/null +++ b/src/Util.Generators/Templates/ITemplateFilter.cs @@ -0,0 +1,15 @@ +using Util.Generators.Contexts; + +namespace Util.Generators.Templates; + +/// +/// 模板过滤器 +/// +public interface ITemplateFilter { + /// + /// 是否过滤模板,返回true则不生成该模板 + /// + /// 模板绝对路径 + /// 项目上下文 + bool IsFilter( string path, ProjectContext projectContext ); +} \ No newline at end of file diff --git a/src/Util.Generators/Templates/ITemplateFilterManager.cs b/src/Util.Generators/Templates/ITemplateFilterManager.cs new file mode 100644 index 000000000..c0a3d9465 --- /dev/null +++ b/src/Util.Generators/Templates/ITemplateFilterManager.cs @@ -0,0 +1,19 @@ +using Util.Generators.Contexts; + +namespace Util.Generators.Templates; + +/// +/// 模板过滤器管理器 +/// +public interface ITemplateFilterManager { + /// + /// 获取模板过滤器 + /// + public List GetFilters(); + /// + /// 是否过滤模板,返回true则不生成该模板 + /// + /// 模板根目录绝对路径 + /// 项目上下文 + bool IsFilter( string rootPath, ProjectContext projectContext ); +} \ No newline at end of file diff --git a/src/Util.Generators/Templates/ITemplateFinder.cs b/src/Util.Generators/Templates/ITemplateFinder.cs new file mode 100644 index 000000000..15e782122 --- /dev/null +++ b/src/Util.Generators/Templates/ITemplateFinder.cs @@ -0,0 +1,15 @@ +using Util.Generators.Contexts; + +namespace Util.Generators.Templates; + +/// +/// 模板查找器 +/// +public interface ITemplateFinder { + /// + /// 查找模板列表 + /// + /// 模板根目录绝对路径 + /// 项目上下文 + IEnumerable Find( string rootPath, ProjectContext projectContext ); +} \ No newline at end of file diff --git a/src/Util.Generators/Templates/TemplateFilterManager.cs b/src/Util.Generators/Templates/TemplateFilterManager.cs new file mode 100644 index 000000000..3fa8d27c5 --- /dev/null +++ b/src/Util.Generators/Templates/TemplateFilterManager.cs @@ -0,0 +1,53 @@ +using Util.Generators.Contexts; + +namespace Util.Generators.Templates; + +/// +/// 模板过滤器管理器 +/// +public class TemplateFilterManager : ITemplateFilterManager { + /// + /// 模板过滤器列表 + /// + private static readonly List _filters = new(); + + /// + /// 添加模板过滤器 + /// + /// 模板过滤器 + public static void AddFilter( ITemplateFilter filter ) { + if ( filter == null ) + return; + _filters.Add( filter ); + } + + /// + /// 添加模板过滤器 + /// + /// 模板过滤器类型 + public static void AddFilter( Type type ) { + var filter = Util.Helpers.Reflection.CreateInstance( type ); + AddFilter( filter ); + } + + /// + /// 清空模板过滤器 + /// + public static void Clear() { + _filters.Clear(); + } + + /// + public List GetFilters() { + return _filters; + } + + /// + public bool IsFilter( string rootPath, ProjectContext projectContext ) { + foreach ( var filter in _filters ) { + if ( filter.IsFilter( rootPath, projectContext ) ) + return true; + } + return false; + } +} \ No newline at end of file diff --git a/src/Util.Generators/Usings.cs b/src/Util.Generators/Usings.cs new file mode 100644 index 000000000..97692f7e9 --- /dev/null +++ b/src/Util.Generators/Usings.cs @@ -0,0 +1,9 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Diagnostics; +global using System.IO; +global using System.Data; +global using System.ComponentModel.DataAnnotations; +global using Humanizer; diff --git a/src/Util.Http/07-Util.Http.csproj b/src/Util.Http/07-Util.Http.csproj new file mode 100644 index 000000000..4a052b317 --- /dev/null +++ b/src/Util.Http/07-Util.Http.csproj @@ -0,0 +1,38 @@ + + + + $(NetTargetFramework) + Util + icon.jpg + Util.Http是Util应用框架的Http操作类库 + Util.Http + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Http.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Http.xml + + + + + True + False + + + + + + + + + + + + + + diff --git a/src/Util.Http/Helpers/Http.cs b/src/Util.Http/Helpers/Http.cs new file mode 100644 index 000000000..0db146154 --- /dev/null +++ b/src/Util.Http/Helpers/Http.cs @@ -0,0 +1,13 @@ +using Util.Http; + +namespace Util.Helpers; + +/// +/// Http操作 +/// +public static class Http { + /// + /// Http客户端 + /// + public static IHttpClient Client => Ioc.Create(); +} \ No newline at end of file diff --git a/src/Util.Http/Http/FileData.cs b/src/Util.Http/Http/FileData.cs new file mode 100644 index 000000000..837519a20 --- /dev/null +++ b/src/Util.Http/Http/FileData.cs @@ -0,0 +1,45 @@ +namespace Util.Http; + +/// +/// 文件数据 +/// +public class FileData { + /// + /// 初始化文件数据 + /// + /// 文件流 + /// 文件名 + /// 参数名 + public FileData( Stream stream,string fileName,string name ) { + Stream = stream; + FileName = fileName; + Name = name; + } + + /// + /// 初始化文件数据 + /// + /// 文件路径 + /// 参数名 + public FileData( string filePath, string name ) { + FilePath = filePath; + Name = name; + } + + /// + /// 文件流 + /// + public Stream Stream { get; } + /// + /// 文件路径 + /// + public string FilePath { get; } + /// + /// 文件名 + /// + public string FileName { get; } + /// + /// 参数名 + /// + public string Name { get; } +} \ No newline at end of file diff --git a/src/Util.Http/Http/HttpClientService.cs b/src/Util.Http/Http/HttpClientService.cs new file mode 100644 index 000000000..aa473bc63 --- /dev/null +++ b/src/Util.Http/Http/HttpClientService.cs @@ -0,0 +1,138 @@ +namespace Util.Http; + +/// +/// Http客户端服务 +/// +public class HttpClientService : IHttpClient { + + #region 字段 + + /// + /// Http客户端工厂 + /// + private readonly IHttpClientFactory _httpClientFactory; + /// + /// Http客户端 + /// + private HttpClient _httpClient; + + #endregion + + #region 构造方法 + + /// + /// 初始化Http客户端服务 + /// + /// Http客户端工厂 + public HttpClientService( IHttpClientFactory factory = null ) { + _httpClientFactory = factory; + } + + #endregion + + #region SetHttpClient(设置Http客户端) + /// + /// 设置Http客户端 + /// + /// Http客户端 + public HttpClientService SetHttpClient( HttpClient client ) { + _httpClient = client; + return this; + } + #endregion + + #region Get + + /// + public IHttpRequest Get( string url ) { + return Get( url ); + } + + /// + public IHttpRequest Get( string url, object queryString ) { + return Get( url, queryString ); + } + + /// + public IHttpRequest Get( string url ) where TResult : class { + return Get( url, null ); + } + + /// + public IHttpRequest Get( string url, object queryString ) where TResult : class { + var result = new HttpRequest( _httpClientFactory, _httpClient, HttpMethod.Get, url ); + if ( queryString.SafeString().IsEmpty() ) + return result; + return result.QueryString( queryString ); + } + + #endregion + + #region Post + + /// + public IHttpRequest Post( string url ) { + return Post( url ); + } + + /// + public IHttpRequest Post( string url, object content ) { + return Post( url, content ); + } + + /// + public IHttpRequest Post( string url ) where TResult : class { + return Post( url, null ); + } + + /// + public IHttpRequest Post( string url, object content ) where TResult : class { + var result = new HttpRequest( _httpClientFactory, _httpClient, HttpMethod.Post, url ); + if ( content == null ) + return result; + return result.Content( content ); + } + + #endregion + + #region Put + + /// + public IHttpRequest Put( string url ) { + return Put( url ); + } + + /// + public IHttpRequest Put( string url, object content ) { + return Put( url, content ); + } + + /// + public IHttpRequest Put( string url ) where TResult : class { + return Put( url,null ); + } + + /// + public IHttpRequest Put( string url, object content ) where TResult : class { + var result = new HttpRequest( _httpClientFactory, _httpClient, HttpMethod.Put, url ); + if ( content == null ) + return result; + return result.Content( content ); + } + + #endregion + + #region Delete + + /// + public IHttpRequest Delete( string url ) { + return Delete( url ); + } + + /// + public IHttpRequest Delete( string url ) where TResult : class { + return new HttpRequest( _httpClientFactory, _httpClient, HttpMethod.Delete, url ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Http/Http/HttpContentType.cs b/src/Util.Http/Http/HttpContentType.cs new file mode 100644 index 000000000..0b53796b6 --- /dev/null +++ b/src/Util.Http/Http/HttpContentType.cs @@ -0,0 +1,27 @@ +namespace Util.Http; + +/// +/// 内容类型 +/// +public enum HttpContentType { + /// + /// application/x-www-form-urlencoded + /// + [Description( "application/x-www-form-urlencoded" )] + FormUrlEncoded, + /// + /// application/json + /// + [Description( "application/json" )] + Json, + /// + /// text/xml + /// + [Description( "text/xml" )] + Xml, + /// + /// multipart/form-data + /// + [Description( "multipart/form-data" )] + FormData +} \ No newline at end of file diff --git a/src/Util.Http/Http/HttpRequest.cs b/src/Util.Http/Http/HttpRequest.cs new file mode 100644 index 000000000..0615a272c --- /dev/null +++ b/src/Util.Http/Http/HttpRequest.cs @@ -0,0 +1,1031 @@ +using Util.Helpers; + +namespace Util.Http; + +/// +/// Http请求 +/// +/// 结果类型 +public class HttpRequest : IHttpRequest where TResult : class { + + #region 字段 + + /// + /// Http客户端工厂 + /// + private readonly IHttpClientFactory _httpClientFactory; + /// + /// Http客户端 + /// + private readonly HttpClient _httpClient; + /// + /// Http客户端名称 + /// + private string _httpClientName; + /// + /// Http方法 + /// + private readonly HttpMethod _httpMethod; + /// + /// 服务地址 + /// + private readonly string _url; + /// + /// Json序列化配置 + /// + private JsonSerializerOptions _jsonSerializerOptions; + /// + /// 是否忽略SSL证书 + /// + private bool _ignoreSsl; + /// + /// 响应完成模式 + /// + private HttpCompletionOption _completionOption; + + #endregion + + #region 构造方法 + + /// + /// 初始化Http请求 + /// + /// Http客户端工厂 + /// Http客户端工厂 + /// Http方法 + /// 服务地址 + public HttpRequest( IHttpClientFactory httpClientFactory, HttpClient httpClient, HttpMethod httpMethod, string url ) { + if ( httpClientFactory == null && httpClient == null ) + throw new ArgumentNullException( nameof( httpClientFactory ) ); + if ( url.IsEmpty() ) + throw new ArgumentNullException( nameof( url ) ); + _httpClientFactory = httpClientFactory; + _httpClient = httpClient; + _httpMethod = httpMethod; + _url = url; + HeaderParams = new Dictionary(); + QueryParams = new Dictionary(); + Params = new Dictionary(); + Files = new List(); + Cookies = new Dictionary(); + HttpContentType = Util.Http.HttpContentType.Json.Description(); + CharacterEncoding = System.Text.Encoding.UTF8; + IsUseCookies = true; + IsFileParameterQuotes = true; + _completionOption = HttpCompletionOption.ResponseContentRead; + } + + #endregion + + #region 属性 + + /// + /// 超时间隔 + /// + protected TimeSpan? HttpTimeout { get; private set; } + /// + /// 基地址 + /// + protected string BaseAddressUri { get; private set; } + /// + /// 证书路径 + /// + protected string CertificatePath { get; private set; } + /// + /// 证书密码 + /// + protected string CertificatePassword { get; private set; } + /// + /// 内容类型 + /// + protected string HttpContentType { get; private set; } + /// + /// 字符编码 + /// + protected Encoding CharacterEncoding { get; private set; } + /// + /// 请求头参数集合 + /// + protected IDictionary HeaderParams { get; } + /// + /// Cookie参数集合 + /// + protected IDictionary Cookies { get; } + /// + /// 查询字符串参数集合 + /// + protected IDictionary QueryParams { get; } + /// + /// 参数集合 + /// + protected IDictionary Params { get; } + /// + /// 参数 + /// + protected object Param { get; private set; } + /// + /// 文件数据列表 + /// + protected List Files { get; private set; } + /// + /// 是否自动携带cookie + /// + protected bool IsUseCookies { get; private set; } + /// + /// 文件上传参数是否添加双引号 + /// + protected bool IsFileParameterQuotes { get; private set; } + /// + /// 发送前操作 + /// + protected Func SendBeforeAction { get; private set; } + /// + /// 发送后操作 + /// + protected Func> SendAfterAction { get; private set; } + /// + /// 结果转换操作 + /// + protected Func ConvertAction { get; private set; } + /// + /// 执行成功操作 + /// + protected Action SuccessAction { get; private set; } + /// + /// 执行成功操作 + /// + protected Func SuccessFunc { get; private set; } + /// + /// 执行失败操作 + /// + protected Action FailAction { get; private set; } + /// + /// 执行完成操作 + /// + protected Action CompleteAction { get; private set; } + + #endregion + + #region Timeout(设置超时间隔) + + /// + public IHttpRequest Timeout( TimeSpan timeout ) { + HttpTimeout = timeout; + return this; + } + + #endregion + + #region HttpClientName(设置Http客户端名称) + + /// + public IHttpRequest HttpClientName( string name ) { + _httpClientName = name; + return this; + } + + #endregion + + #region BaseAddress(设置基地址) + + /// + /// 设置基地址 + /// + /// 基地址 + public IHttpRequest BaseAddress( string baseAddress ) { + BaseAddressUri = baseAddress; + return this; + } + + #endregion + + #region ContentType(设置内容类型) + + /// + public IHttpRequest ContentType( HttpContentType contentType ) { + return ContentType( contentType.Description() ); + } + + /// + public IHttpRequest ContentType( string contentType ) { + HttpContentType = contentType; + return this; + } + + #endregion + + #region HttpCompletion(设置响应完成模式) + + /// + public IHttpRequest HttpCompletion( HttpCompletionOption option ) { + _completionOption = option; + return this; + } + + #endregion + + #region Encoding(设置字符编码) + + /// + public IHttpRequest Encoding( string encoding ) { + return Encoding( System.Text.Encoding.GetEncoding( encoding ) ); + } + + /// + public IHttpRequest Encoding( Encoding encoding ) { + CharacterEncoding = encoding; + return this; + } + + #endregion + + #region BearerToken(设置访问令牌) + + /// + public IHttpRequest BearerToken( string token ) { + Header( "Authorization", $"Bearer {token}" ); + return this; + } + + #endregion + + #region Certificate(设置证书) + + /// + public IHttpRequest Certificate( string path, string password ) { + CertificatePath = path; + CertificatePassword = password; + return this; + } + + #endregion + + #region IgnoreSsl(是否忽略SSL证书) + + /// + public IHttpRequest IgnoreSsl() { + _ignoreSsl = true; + return this; + } + + #endregion + + #region JsonSerializerOptions(设置Json序列化配置) + + /// + public IHttpRequest JsonSerializerOptions( JsonSerializerOptions options ) { + _jsonSerializerOptions = options; + return this; + } + + #endregion + + #region GetJsonSerializerOptions(获取Json序列化配置) + + /// + /// 获取Json序列化配置 + /// + protected virtual JsonSerializerOptions GetJsonSerializerOptions() { + if ( _jsonSerializerOptions != null ) + return _jsonSerializerOptions; + return new JsonSerializerOptions { + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + NumberHandling = JsonNumberHandling.AllowReadingFromString, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ), + Converters = { + new DateTimeJsonConverter(), + new NullableDateTimeJsonConverter() + } + }; + } + + #endregion + + #region Header(设置请求头) + + /// + public IHttpRequest Header( string key, string value ) { + if ( key.IsEmpty() ) + return this; + if ( HeaderParams.ContainsKey( key ) ) + HeaderParams.Remove( key ); + HeaderParams.Add( key, value ); + return this; + } + + /// + public IHttpRequest Header( IDictionary headers ) { + if ( headers == null ) + return this; + foreach ( var header in headers ) + Header( header.Key, header.Value ); + return this; + } + + #endregion + + #region QueryString(设置查询字符串) + + /// + public IHttpRequest QueryString( string key, string value ) { + if ( key.IsEmpty() ) + return this; + if ( QueryParams.ContainsKey( key ) ) + QueryParams.Remove( key ); + QueryParams.Add( key, value ); + return this; + } + + /// + public IHttpRequest QueryString( IDictionary queryString ) { + if ( queryString == null ) + return this; + foreach ( var param in queryString ) + QueryString( param.Key, param.Value ); + return this; + } + + /// + /// 设置查询字符串,即url中?后面的参数 + /// + /// 查询字符串对象 + public IHttpRequest QueryString( object queryString ) { + var dic = ToDictionary( queryString ); + foreach ( var param in dic ) + QueryString( param.Key, param.Value.ToString() ); + return this; + } + + #endregion + + #region UseCookies(设置是否自动携带cookie) + + /// + public IHttpRequest UseCookies( bool isUseCookies ) { + IsUseCookies = isUseCookies; + return this; + } + + #endregion + + #region Cookie(设置Cookie) + + /// + public IHttpRequest Cookie( string key, string value ) { + if ( key.IsEmpty() ) + return this; + if ( Cookies.ContainsKey( key ) ) + Cookies.Remove( key ); + Cookies.Add( key, value ); + return this; + } + + /// + public IHttpRequest Cookie( IDictionary cookies ) { + if ( cookies == null ) + return this; + foreach ( var cookie in cookies ) + Cookie( cookie.Key, cookie.Value ); + return this; + } + + #endregion + + #region Content(添加内容参数) + + /// + public IHttpRequest Content( string key, object value ) { + if ( key.IsEmpty() ) + return this; + if ( value == null ) + return this; + if ( Params.ContainsKey( key ) ) + Params.Remove( key ); + Params.Add( key, value ); + return this; + } + + /// + public IHttpRequest Content( IDictionary parameters ) { + if ( parameters == null ) + return this; + foreach ( var param in parameters ) + Content( param.Key, param.Value ); + return this; + } + + /// + public IHttpRequest Content( object value ) { + Param = value; + return this; + } + + #endregion + + #region JsonContent(添加Json参数) + + /// + public IHttpRequest JsonContent( object value ) { + ContentType( Http.HttpContentType.Json ); + return Content( value ); + } + + #endregion + + #region XmlContent(添加Xml参数) + + /// + public IHttpRequest XmlContent( string value ) { + ContentType( Http.HttpContentType.Xml ); + return Content( value ); + } + + #endregion + + #region FileContent(添加文件参数) + + /// + public IHttpRequest FileContent( Stream file, string fileName, string name = "file" ) { + ContentType( Http.HttpContentType.FormData ); + if ( Files.Any( t => t.Name == name ) ) + Files.RemoveAll( t => t.Name == name ); + Files.Add( new FileData( file, fileName, name ) ); + return this; + } + + /// + public IHttpRequest FileContent( string filePath, string name = "file" ) { + ContentType( Http.HttpContentType.FormData ); + if ( Files.Any( t => t.Name == name ) ) + Files.RemoveAll( t => t.Name == name ); + Files.Add( new FileData( filePath, name ) ); + return this; + } + + #endregion + + #region FileParameterQuotes(文件上传参数是否添加双引号) + + /// + public IHttpRequest FileParameterQuotes( bool isQuote = true ) { + IsFileParameterQuotes = isQuote; + return this; + } + + #endregion + + #region OnSendBefore(发送前事件) + + /// + public IHttpRequest OnSendBefore( Func action ) { + SendBeforeAction = action; + return this; + } + + #endregion + + #region OnSendAfter(发送后事件) + + /// + public IHttpRequest OnSendAfter( Func> action ) { + SendAfterAction = action; + return this; + } + + #endregion + + #region OnConvert(结果转换事件) + + /// + public IHttpRequest OnConvert( Func action ) { + ConvertAction = action; + return this; + } + + #endregion + + #region OnSuccess(请求成功事件) + + /// + public IHttpRequest OnSuccess( Action action ) { + SuccessAction = action; + return this; + } + + /// + /// 请求成功事件 + /// + /// 执行成功操作,参数为响应结果 + public IHttpRequest OnSuccess( Func action ) { + SuccessFunc = action; + return this; + } + + #endregion + + #region OnFail(请求失败事件) + + /// + public IHttpRequest OnFail( Action action ) { + FailAction = action; + return this; + } + + #endregion + + #region OnComplete(请求完成事件) + + /// + public IHttpRequest OnComplete( Action action ) { + CompleteAction = action; + return this; + } + + #endregion + + #region GetResultAsync(获取结果) + + /// + public async Task GetResultAsync( CancellationToken cancellationToken = default ) { + var message = await CreateMessage(); + if ( SendBefore( message ) == false ) + return default; + var response = await SendAsync( message, cancellationToken ); + return await SendAfterAsync( response ); + } + + #endregion + + #region CreateMessage(创建请求消息) + + /// + /// 创建请求消息 + /// + protected virtual async Task CreateMessage() { + var message = new HttpRequestMessage( _httpMethod, GetUrl( _url ) ); + AddCookies(); + AddHeaders( message ); + message.Content = await CreateHttpContent(); + return message; + } + + /// + /// 获取服务地址 + /// + /// 服务地址 + protected virtual string GetUrl( string url ) { + return QueryHelpers.AddQueryString( url, QueryParams ); + } + + /// + /// 添加Cookie + /// + protected virtual void AddCookies() { + if ( Cookies.Count == 0 ) + return; + var cookieValues = new List(); + foreach ( var cookie in Cookies ) + cookieValues.Add( new CookieHeaderValue( cookie.Key, cookie.Value ) ); + Header( "Cookie", cookieValues.Select( t => t.ToString() ).Join() ); + } + + /// + /// 添加请求头 + /// + /// 请求消息 + protected virtual void AddHeaders( HttpRequestMessage message ) { + foreach ( var header in HeaderParams ) + message.Headers.Add( header.Key, header.Value ); + } + + /// + /// 创建请求内容 + /// + protected virtual async Task CreateHttpContent() { + var contentType = HttpContentType.SafeString().ToLower(); + switch ( contentType ) { + case "application/x-www-form-urlencoded": + return CreateFormContent(); + case "application/json": + return CreateJsonContent(); + case "text/xml": + return CreateXmlContent(); + case "multipart/form-data": + return await CreateFileUploadContent(); + } + return null; + } + + /// + /// 创建表单内容 + /// + protected virtual HttpContent CreateFormContent() { + return new FormUrlEncodedContent( GetParameters().ToDictionary( t => t.Key, t => t.Value.SafeString() ) ); + } + + /// + /// 获取参数 + /// + protected IDictionary GetParameters() { + var result = new Dictionary( Params ); + var dictionary = ToDictionary( Param ); + if ( dictionary == null ) + return result; + foreach ( var parameter in dictionary ) { + if ( result.ContainsKey( parameter.Key ) ) + continue; + result.Add( parameter.Key, parameter.Value ); + } + return result; + } + + /// + /// 对象转换为字典 + /// + /// 对象 + protected IDictionary ToDictionary( object data ) { + var result = Util.Helpers.Convert.ToDictionary( data ); + return result.Where( t => t.Value != null ).ToDictionary( t => t.Key, t => t.Value ); + } + + /// + /// 创建json内容 + /// + protected virtual HttpContent CreateJsonContent() { + var content = GetJsonContentValue(); + if ( content.IsEmpty() ) + return null; + return new StringContent( content, CharacterEncoding, "application/json" ); + } + + /// + /// 获取json内容值 + /// + private string GetJsonContentValue() { + var options = GetJsonSerializerOptions(); + if ( Param != null && Params.Count > 0 ) + return Json.ToJson( GetParameters(), options ); + if ( Param != null ) + return Json.ToJson( Param, options ); + if ( Params.Count > 0 ) + return Json.ToJson( Params, options ); + return null; + } + + /// + /// 创建xml内容 + /// + protected virtual HttpContent CreateXmlContent() { + return new StringContent( Param.SafeString(), CharacterEncoding, "text/xml" ); + } + + /// + /// 创建文件上传内容 + /// + protected virtual async Task CreateFileUploadContent() { + var result = new MultipartFormDataContent( GetBoundary() ); + AddFileParameters( result ); + await AddFileData( result ); + ClearBoundaryQuotes( result ); + return result; + } + + /// + /// 获取 multipart/form-data 分隔符 + /// + protected virtual string GetBoundary() { + return $"-----{Guid.NewGuid()}"; + } + + /// + /// 添加文件参数 + /// + protected void AddFileParameters( MultipartFormDataContent content ) { + var parameters = GetParameters(); + foreach ( var parameter in parameters ) { + var item = new ByteArrayContent( System.Text.Encoding.UTF8.GetBytes( parameter.Value.SafeString() ) ); + content.Add( item, GetFileParameter( parameter.Key ) ); + } + } + + /// + /// 获取参数 + /// + protected string GetFileParameter( string param ) { + return IsFileParameterQuotes ? "\"" + param + "\"" : param; + } + + /// + /// 添加文件数据 + /// + protected async Task AddFileData( MultipartFormDataContent content ) { + foreach ( var file in Files ) { + if ( file.Stream != null ) { + await using var fileStream = file.Stream; + var bytes = await Util.Helpers.File.ReadToBytesAsync( fileStream ); + AddFileData( content, bytes, file.Name, file.FileName ); + continue; + } + if ( file.FilePath.IsEmpty() ) + continue; + if ( Util.Helpers.File.FileExists( file.FilePath ) == false ) + return; + var fileName = Path.GetFileName( file.FilePath ); + var stream = await Util.Helpers.File.ReadToMemoryStreamAsync( file.FilePath ); + AddFileData( content, stream.ToArray(), file.Name, fileName ); + } + } + + /// + /// 添加文件数据 + /// + protected void AddFileData( MultipartFormDataContent content, byte[] stream, string name, string fileName ) { + if ( stream == null ) + return; + var fileContent = new ByteArrayContent( stream ); + content.Add( fileContent, GetFileParameter( name ), GetFileParameter( fileName ) ); + if ( fileContent.Headers is { ContentDisposition: not null } ) + fileContent.Headers.ContentDisposition.FileNameStar = null; + } + + /// + /// 清除分隔符双引号 + /// + protected void ClearBoundaryQuotes( MultipartFormDataContent content ) { + var boundary = content?.Headers?.ContentType?.Parameters.FirstOrDefault( o => o.Name == "boundary" ); + if ( boundary == null ) + return; + boundary.Value = boundary.Value?.Replace( "\"", null ); + } + + #endregion + + #region SendBefore(发送前操作) + + /// + /// 发送前操作 + /// + /// 请求消息 + protected virtual bool SendBefore( HttpRequestMessage message ) { + if ( SendBeforeAction == null ) + return true; + return SendBeforeAction( message ); + } + + #endregion + + #region SendAsync(发送请求) + + /// + /// 发送请求 + /// + /// 请求消息 + /// 取消令牌 + protected async Task SendAsync( HttpRequestMessage message, CancellationToken cancellationToken ) { + var client = GetClient(); + client.CheckNull( nameof( client ) ); + InitHttpClient( client ); + return await client.SendAsync( message, _completionOption, cancellationToken ); + } + + #endregion + + #region GetClient(获取Http客户端) + + /// + /// 获取Http客户端 + /// + protected HttpClient GetClient() { + if ( _httpClient != null ) + return _httpClient; + var clientHandler = CreateHttpClientHandler(); + InitHttpClientHandler( clientHandler ); + return _httpClientName.IsEmpty() ? _httpClientFactory.CreateClient() : _httpClientFactory.CreateClient( _httpClientName ); + } + + /// + /// 创建Http客户端处理器 + /// + protected HttpClientHandler CreateHttpClientHandler() { + var handlerFactory = _httpClientFactory as IHttpMessageHandlerFactory; + if ( handlerFactory == null ) + return null; + var handler = _httpClientName.IsEmpty() ? handlerFactory.CreateHandler() : handlerFactory.CreateHandler( _httpClientName ); + while ( handler is DelegatingHandler delegatingHandler ) { + handler = delegatingHandler.InnerHandler; + } + return handler as HttpClientHandler; + } + + /// + /// 初始化Http客户端处理器 + /// + /// Http客户端处理器 + protected virtual void InitHttpClientHandler( HttpClientHandler handler ) { + if ( handler == null ) + return; + InitCertificate( handler ); + InitUseCookies( handler ); + IgnoreSsl( handler ); + } + + #endregion + + #region InitCertificate(初始化证书) + + /// + /// 初始化证书 + /// + protected virtual void InitCertificate( HttpClientHandler handler ) { + if ( CertificatePath.IsEmpty() ) + return; + var certificate = new X509Certificate2( CertificatePath, CertificatePassword, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet ); + handler.ClientCertificates.Clear(); + handler.ClientCertificates.Add( certificate ); + } + + #endregion + + #region InitUseCookies(初始化是否携带Cookie) + + /// + /// 初始化是否携带Cookie + /// + protected virtual void InitUseCookies( HttpClientHandler handler ) { + if ( handler.UseCookies != IsUseCookies ) + handler.UseCookies = IsUseCookies; + } + + #endregion + + #region IgnoreSsl(忽略SSL证书错误) + + /// + /// 忽略SSL证书错误 + /// + protected virtual void IgnoreSsl( HttpClientHandler handler ) { + if ( _ignoreSsl == false ) + return; + handler.ServerCertificateCustomValidationCallback ??= ( _, _, _, _ ) => true; + } + + #endregion + + #region InitHttpClient(初始化Http客户端) + + /// + /// 初始化Http客户端 + /// + protected virtual void InitHttpClient( HttpClient client ) { + InitBaseAddress( client ); + InitTimeout( client ); + } + + #endregion + + #region InitBaseAddress(初始化基地址) + + /// + /// 初始化基地址 + /// + protected virtual void InitBaseAddress( HttpClient client ) { + if ( BaseAddressUri.IsEmpty() ) + return; + client.BaseAddress = new Uri( BaseAddressUri ); + } + + #endregion + + #region InitTimeout(初始化超时间隔) + + /// + /// 初始化超时间隔 + /// + protected virtual void InitTimeout( HttpClient client ) { + if ( HttpTimeout == null ) + return; + client.Timeout = HttpTimeout.SafeValue(); + } + + #endregion + + #region SendAfterAsync(发送后操作) + + /// + /// 发送后操作 + /// + /// 响应消息 + protected virtual async Task SendAfterAsync( HttpResponseMessage response ) { + if ( SendAfterAction != null ) + return await SendAfterAction( response ); + string content = null; + try { + content = await response.Content.ReadAsStringAsync(); + if ( response.IsSuccessStatusCode ) + return await SuccessHandlerAsync( response, content ); + FailHandler( response, content ); + return null; + } + finally { + CompleteHandler( response, content ); + } + } + + #endregion + + #region SuccessHandler(成功处理操作) + + /// + /// 成功处理操作 + /// + protected virtual async Task SuccessHandlerAsync( HttpResponseMessage response, string content ) { + var result = ConvertTo( content, response.GetContentType() ); + SuccessAction?.Invoke( result ); + if ( SuccessFunc != null ) + await SuccessFunc( result ); + return result; + } + + #endregion + + #region ConvertTo(将内容转换为结果) + + /// + /// 将内容转换为结果 + /// + /// 内容 + /// 内容类型 + protected virtual TResult ConvertTo( string content, string contentType ) { + if ( ConvertAction != null ) + return ConvertAction( content ); + if ( typeof( TResult ) == typeof( string ) ) + return (TResult)(object)content; + return contentType.SafeString().ToLower() == "application/json" ? Json.ToObject( content, GetJsonSerializerOptions() ) : null; + } + + #endregion + + #region FailHandler(失败处理操作) + + /// + /// 失败处理操作 + /// + protected virtual void FailHandler( HttpResponseMessage response, object content ) { + FailAction?.Invoke( response, content ); + } + + #endregion + + #region CompleteHandler(执行完成操作) + + /// + /// 执行完成操作 + /// + protected virtual void CompleteHandler( HttpResponseMessage response, object content ) { + CompleteAction?.Invoke( response, content ); + } + + #endregion + + #region GetStreamAsync(获取流) + + /// + public async Task GetStreamAsync( CancellationToken cancellationToken = default ) { + var message = await CreateMessage(); + if ( SendBefore( message ) == false ) + return default; + var response = await SendAsync( message, cancellationToken ); + return await GetStream( response ); + } + + /// + /// 发送后操作 + /// + /// 响应消息 + protected virtual async Task GetStream( HttpResponseMessage response ) { + byte[] content = null; + try { + content = await response.Content.ReadAsByteArrayAsync(); + if ( response.IsSuccessStatusCode ) + return content; + FailHandler( response, content ); + return null; + } + finally { + CompleteHandler( response, content ); + } + } + + #endregion + + #region WriteAsync(写入文件) + + /// + public async Task WriteAsync( string filePath, CancellationToken cancellationToken = default ) { + var bytes = await GetStreamAsync( cancellationToken ); + await Util.Helpers.File.WriteAsync( filePath, bytes, cancellationToken ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Http/Http/HttpResponseMessageExtensions.cs b/src/Util.Http/Http/HttpResponseMessageExtensions.cs new file mode 100644 index 000000000..c471d133c --- /dev/null +++ b/src/Util.Http/Http/HttpResponseMessageExtensions.cs @@ -0,0 +1,14 @@ +namespace Util.Http; + +/// +/// Http响应消息扩展 +/// +public static class HttpResponseMessageExtensions { + /// + /// 获取内容类型 + /// + /// Http响应消息 + public static string GetContentType( this HttpResponseMessage response ) { + return response?.Content.Headers.ContentType?.MediaType; + } +} \ No newline at end of file diff --git a/src/Util.Http/Http/IHttpClient.cs b/src/Util.Http/Http/IHttpClient.cs new file mode 100644 index 000000000..904c8383f --- /dev/null +++ b/src/Util.Http/Http/IHttpClient.cs @@ -0,0 +1,83 @@ +namespace Util.Http; + +/// +/// Http客户端 +/// +public interface IHttpClient : ISingletonDependency { + /// + /// 发送Get请求 + /// + /// 服务地址 + IHttpRequest Get( string url ); + /// + /// 发送Get请求 + /// + /// 服务地址 + /// 查询字符串对象 + IHttpRequest Get( string url, object queryString ); + /// + /// 发送Get请求 + /// + /// 服务地址 + IHttpRequest Get( string url ) where TResult : class; + /// + /// 发送Get请求 + /// + /// 服务地址 + /// 查询字符串对象 + IHttpRequest Get( string url, object queryString ) where TResult : class; + /// + /// 发送Post请求 + /// + /// 服务地址 + IHttpRequest Post( string url ); + /// + /// 发送Post请求 + /// + /// 服务地址 + /// 内容参数 + IHttpRequest Post( string url, object content ); + /// + /// 发送Post请求 + /// + /// 服务地址 + IHttpRequest Post( string url ) where TResult : class; + /// + /// 发送Post请求 + /// + /// 服务地址 + /// 内容参数 + IHttpRequest Post( string url, object content ) where TResult : class; + /// + /// 发送Put请求 + /// + /// 服务地址 + IHttpRequest Put( string url ); + /// + /// 发送Put请求 + /// + /// 服务地址 + /// 内容参数 + IHttpRequest Put( string url, object content ); + /// + /// 发送Put请求 + /// + /// 服务地址 + IHttpRequest Put( string url ) where TResult : class; + /// + /// 发送Put请求 + /// + /// 服务地址 + /// 内容参数 + IHttpRequest Put( string url, object content ) where TResult : class; + /// + /// 发送Delete请求 + /// + /// 服务地址 + IHttpRequest Delete( string url ); + /// + /// 发送Delete请求 + /// + /// 服务地址 + IHttpRequest Delete( string url ) where TResult : class; +} \ No newline at end of file diff --git a/src/Util.Http/Http/IHttpRequest.cs b/src/Util.Http/Http/IHttpRequest.cs new file mode 100644 index 000000000..699db099d --- /dev/null +++ b/src/Util.Http/Http/IHttpRequest.cs @@ -0,0 +1,212 @@ +namespace Util.Http; + +/// +/// Http请求 +/// +public interface IHttpRequest { +} + +/// +/// Http请求 +/// +/// 结果类型 +public interface IHttpRequest : IHttpRequest where TResult : class { + /// + /// 设置超时间隔 + /// + /// 超时间隔 + IHttpRequest Timeout( TimeSpan timeout ); + /// + /// 设置外部配置的HttpClient实例名称 + /// + /// HttpClient名称 + IHttpRequest HttpClientName( string name ); + /// + /// 设置基地址 + /// + /// 基地址 + IHttpRequest BaseAddress( string baseAddress ); + /// + /// 设置字符编码 + /// + /// 字符编码,范例:gb2312 + IHttpRequest Encoding( string encoding ); + /// + /// 设置字符编码 + /// + /// 字符编码 + IHttpRequest Encoding( Encoding encoding ); + /// + /// 设置访问令牌 + /// + /// 访问令牌 + IHttpRequest BearerToken( string token ); + /// + /// 设置内容类型 + /// + /// 内容类型 + IHttpRequest ContentType( HttpContentType contentType ); + /// + /// 设置内容类型 + /// + /// 内容类型 + IHttpRequest ContentType( string contentType ); + /// + /// 设置响应完成模式 + /// + /// 响应完成配置 + IHttpRequest HttpCompletion( HttpCompletionOption option ); + /// + /// 设置证书 + /// + /// 证书路径 + /// 证书密码 + IHttpRequest Certificate( string path, string password ); + /// + /// 忽略SSL证书 + /// + IHttpRequest IgnoreSsl(); + /// + /// 设置Json序列化配置 + /// + /// Json序列化配置 + IHttpRequest JsonSerializerOptions( JsonSerializerOptions options ); + /// + /// 设置请求头 + /// + /// 键 + /// 值 + IHttpRequest Header( string key, string value ); + /// + /// 设置请求头 + /// + /// 请求头键值对集合 + IHttpRequest Header( IDictionary headers ); + /// + /// 设置查询字符串,即url中?后面的参数 + /// + /// 键 + /// 值 + IHttpRequest QueryString( string key, string value ); + /// + /// 设置查询字符串,即url中?后面的参数 + /// + /// 查询字符串键值对集合 + IHttpRequest QueryString( IDictionary queryString ); + /// + /// 设置查询字符串,即url中?后面的参数 + /// + /// 查询字符串对象 + IHttpRequest QueryString( object queryString ); + /// + /// 设置是否自动携带cookie + /// + /// 是否自动携带cookie + IHttpRequest UseCookies( bool isUseCookies ); + /// + /// 设置Cookie + /// + /// 键 + /// 值 + IHttpRequest Cookie( string key, string value ); + /// + /// 设置Cookie集合 + /// + /// Cookie集合 + IHttpRequest Cookie( IDictionary cookies ); + /// + /// 添加参数,作为请求内容发送 + /// + /// 键 + /// 值 + IHttpRequest Content( string key, object value ); + /// + /// 添加参数,作为请求内容发送 + /// + /// 参数字典 + IHttpRequest Content( IDictionary parameters ); + /// + /// 添加参数,作为请求内容发送 + /// + /// 值 + IHttpRequest Content( object value ); + /// + /// 添加内容类型为 application/json 的参数 + /// + /// 值 + IHttpRequest JsonContent( object value ); + /// + /// 添加内容类型为 text/xml 的参数 + /// + /// 值 + IHttpRequest XmlContent( string value ); + /// + /// 添加内容类型为 multipart/form-data 的文件参数 + /// + /// 文件流 + /// 文件名 + /// 参数名,默认值: file + IHttpRequest FileContent( Stream file,string fileName, string name = "file" ); + /// + /// 添加内容类型为 multipart/form-data 的文件参数 + /// + /// 文件绝对路径 + /// 参数名,默认值: file + IHttpRequest FileContent( string filePath, string name = "file" ); + /// + /// 文件上传参数是否添加双引号 + /// + /// 是否添加双引号 + IHttpRequest FileParameterQuotes( bool isQuote = true ); + /// + /// 发送前事件 + /// + /// 发送前操作,返回false取消发送 + IHttpRequest OnSendBefore( Func action ); + /// + /// 发送后事件 + /// + /// 发送后操作,自定义解析返回值 + IHttpRequest OnSendAfter( Func> action ); + /// + /// 结果转换事件 + /// + /// 结果转换操作,参数为响应内容 + IHttpRequest OnConvert( Func action ); + /// + /// 请求成功事件 + /// + /// 执行成功操作,参数为响应结果 + IHttpRequest OnSuccess( Action action ); + /// + /// 请求成功事件 + /// + /// 执行成功操作,参数为响应结果 + IHttpRequest OnSuccess( Func action ); + /// + /// 请求失败事件 + /// + /// 执行失败操作,参数为响应消息和响应内容 + IHttpRequest OnFail( Action action ); + /// + /// 请求完成事件,不论成功失败都会执行 + /// + /// 执行完成操作,参数为响应消息和响应内容 + IHttpRequest OnComplete( Action action ); + /// + /// 获取结果 + /// + /// 取消令牌 + Task GetResultAsync( CancellationToken cancellationToken = default ); + /// + /// 获取流 + /// + /// 取消令牌 + Task GetStreamAsync( CancellationToken cancellationToken = default ); + /// + /// 写入文件 + /// + /// 文件绝对路径 + /// 取消令牌 + Task WriteAsync( string filePath, CancellationToken cancellationToken = default ); +} \ No newline at end of file diff --git a/src/Util.Http/Usings.cs b/src/Util.Http/Usings.cs new file mode 100644 index 000000000..f9c1e4efa --- /dev/null +++ b/src/Util.Http/Usings.cs @@ -0,0 +1,18 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using System.Text; +global using System.Text.Json; +global using System.Text.Json.Serialization; +global using System.Net.Http; +global using System.ComponentModel; +global using System.Security.Cryptography.X509Certificates; +global using System.Text.Encodings.Web; +global using System.Text.Unicode; +global using System.IO; +global using Microsoft.Net.Http.Headers; +global using Microsoft.AspNetCore.WebUtilities; +global using Util.Dependency; +global using Util.SystemTextJson; \ No newline at end of file diff --git a/src/Util.IdGenerator/10-Util.IdGenerator.csproj b/src/Util.IdGenerator/10-Util.IdGenerator.csproj new file mode 100644 index 000000000..5ac0a74f6 --- /dev/null +++ b/src/Util.IdGenerator/10-Util.IdGenerator.csproj @@ -0,0 +1,38 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.IdGenerator + Util + Util.IdGenerator是Util应用框架的Id生成器封装类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.IdGenerator.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.IdGenerator.xml + + + + + True + False + + + + + + + + + + + + + + diff --git a/src/Util.IdGenerator/Helpers/Id.IdGenerator.cs b/src/Util.IdGenerator/Helpers/Id.IdGenerator.cs new file mode 100644 index 000000000..4e2cc90b4 --- /dev/null +++ b/src/Util.IdGenerator/Helpers/Id.IdGenerator.cs @@ -0,0 +1,22 @@ +namespace Util.Helpers; + +/// +/// 标识生成器 +/// +public static partial class Id { + /// + /// 使用 YitIdHelper 雪花漂移算法创建标识 + /// + public static long CreateYitId() { + if( string.IsNullOrEmpty( _id.Value ) == false ) + return Convert.ToLong( _id.Value ); + try { + return YitIdHelper.NextId(); + } + catch { + var options = new IdGeneratorOptions( 1 ); + YitIdHelper.SetIdGenerator( options ); + return YitIdHelper.NextId(); + } + } +} \ No newline at end of file diff --git a/src/Util.IdGenerator/Helpers/Id.Nanoid.cs b/src/Util.IdGenerator/Helpers/Id.Nanoid.cs new file mode 100644 index 000000000..aec87838d --- /dev/null +++ b/src/Util.IdGenerator/Helpers/Id.Nanoid.cs @@ -0,0 +1,90 @@ +namespace Util.Helpers; + +/// +/// 标识生成器 +/// +public static partial class Id { + /// + /// 使用 Nanoid 创建标识,返回 21 个字符的标识. + /// + public static string CreateNanoid() { + return CreateNanoid( 21 ); + } + + /// + /// 使用 Nanoid 创建标识,返回 21 个字符的标识. + /// + public static async Task CreateNanoidAsync() { + return await CreateNanoidAsync( 21 ); + } + + /// + /// 使用 Nanoid 创建标识 + /// + /// 字符数 + public static string CreateNanoid( int size ) { + return CreateNanoid( null, size ); + } + + /// + /// 使用 Nanoid 创建标识 + /// + /// 字符数 + public static async Task CreateNanoidAsync( int size ) { + return await CreateNanoidAsync( null, size ); + } + + /// + /// 使用 Nanoid 创建标识 + /// + /// 可选字符,范例 abcd + public static string CreateNanoid( string alphabet ) { + return CreateNanoid( alphabet, 21 ); + } + + /// + /// 使用 Nanoid 创建标识 + /// + /// 可选字符,范例 abcd + public static async Task CreateNanoidAsync( string alphabet ) { + return await CreateNanoidAsync( alphabet, 21 ); + } + + /// + /// 使用 Nanoid 创建标识 + /// + /// 可选字符,范例 abcd + /// 字符数 + public static string CreateNanoid( string alphabet, int size ) { + if( string.IsNullOrEmpty( _id.Value ) == false ) + return _id.Value; + if( string.IsNullOrEmpty( alphabet ) ) + return Nanoid.Generate( size: size ); + return Nanoid.Generate( alphabet, size ); + } + + /// + /// 使用 Nanoid 创建标识 + /// + /// 可选字符,范例 abcd + /// 字符数 + public static async Task CreateNanoidAsync( string alphabet, int size ) { + if( string.IsNullOrEmpty( _id.Value ) == false ) + return _id.Value; + if( string.IsNullOrEmpty( alphabet ) ) + return await Nanoid.GenerateAsync( size: size ); + return await Nanoid.GenerateAsync( alphabet, size ); + } + + /// + /// 使用 Nanoid 创建标识 + /// + /// 随机数 + /// 可选字符,范例 abcd + /// 字符数 + public static string CreateNanoid( System.Random random, string alphabet = "_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", int size = 21 ) { + if( string.IsNullOrEmpty( _id.Value ) == false ) + return _id.Value; + return Nanoid.Generate( random, alphabet, size ); + } +} \ No newline at end of file diff --git a/src/Util.IdGenerator/Helpers/Id.cs b/src/Util.IdGenerator/Helpers/Id.cs new file mode 100644 index 000000000..4f1e70b80 --- /dev/null +++ b/src/Util.IdGenerator/Helpers/Id.cs @@ -0,0 +1,40 @@ +namespace Util.Helpers; + +/// +/// 标识生成器 +/// +public static partial class Id { + /// + /// 标识 + /// + private static readonly AsyncLocal _id = new(); + + /// + /// 设置标识 + /// + /// 标识 + public static void SetId( string id ) { + _id.Value = id; + } + + /// + /// 重置标识 + /// + public static void Reset() { + _id.Value = null; + } + + /// + /// 使用去掉分隔符的Guid创建标识 + /// + public static string Create() { + return string.IsNullOrEmpty( _id.Value ) ? System.Guid.NewGuid().ToString( "N" ) : _id.Value; + } + + /// + /// 创建Guid标识 + /// + public static Guid CreateGuid() { + return string.IsNullOrEmpty( _id.Value ) ? System.Guid.NewGuid() : _id.Value.ToGuid(); + } +} \ No newline at end of file diff --git a/src/Util.IdGenerator/Usings.cs b/src/Util.IdGenerator/Usings.cs new file mode 100644 index 000000000..03275abeb --- /dev/null +++ b/src/Util.IdGenerator/Usings.cs @@ -0,0 +1,5 @@ +global using System; +global using System.Threading; +global using System.Threading.Tasks; +global using NanoidDotNet; +global using Yitter.IdGenerator; \ No newline at end of file diff --git a/src/Util.Images.Avatar/02-Util.Images.Avatar.csproj b/src/Util.Images.Avatar/02-Util.Images.Avatar.csproj new file mode 100644 index 000000000..c304e7624 --- /dev/null +++ b/src/Util.Images.Avatar/02-Util.Images.Avatar.csproj @@ -0,0 +1,33 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Images.Avatar + Util.Images + Util.Images.Avatar是Util应用框架的头像生成操作类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Images.Avatar.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Images.Avatar.xml + + + + + True + False + + + + + + + + + diff --git a/src/Util.Images.Avatar/AvatarManager.cs b/src/Util.Images.Avatar/AvatarManager.cs new file mode 100644 index 000000000..7088f46e4 --- /dev/null +++ b/src/Util.Images.Avatar/AvatarManager.cs @@ -0,0 +1,278 @@ +namespace Util.Images; + +/// +/// 头像服务 +/// +public class AvatarManager : IAvatarManager { + + #region 字段 + + /// + /// 背景色 + /// + private string _backgroundColor; + /// + /// 头像大小 + /// + private int _size; + /// + /// 字体名称 + /// + private string _fontName; + /// + /// 字体大小 + /// + private double _fontSize; + /// + /// 字体颜色 + /// + private string _fontColor; + /// + /// 字体是否粗体 + /// + private bool _isBold; + /// + /// 字体是否斜体 + /// + private bool _isItalic; + /// + /// 字体是否大写 + /// + private bool _isUppercase; + /// + /// 文本 + /// + private string _text; + /// + /// 文本长度 + /// + private int _length; + + #endregion + + #region 构造方法 + + /// + /// 初始化头像服务 + /// + /// 图片服务 + public AvatarManager( IImageManager imageManager ) { + ImageManager = imageManager ?? throw new ArgumentNullException( nameof( imageManager ) ); + _backgroundColor = "5d005d"; + _size = 64; + _fontSize = 0.5; + _fontColor = "ffffff"; + _isUppercase = true; + } + + #endregion + + #region 属性 + + /// + /// 图片服务 + /// + protected IImageManager ImageManager { get; } + + #endregion + + #region BackgroundColor + + /// + public IAvatarManager BackgroundColor( string color ) { + _backgroundColor = color; + return this; + } + + #endregion + + #region Size + + /// + public IAvatarManager Size( int size ) { + _size = size; + return this; + } + + #endregion + + #region Font + + /// + public IAvatarManager Font( string fontName ) { + _fontName = fontName; + return this; + } + + #endregion + + #region FontSize + + /// + public IAvatarManager FontSize( double size ) { + _fontSize = size; + return this; + } + + #endregion + + #region FontColor + + /// + public IAvatarManager FontColor( string color ) { + _fontColor = color; + return this; + } + + #endregion + + #region Bold + + /// + public IAvatarManager Bold( bool isBold = true ) { + _isBold = isBold; + return this; + } + + #endregion + + #region Italic + + /// + public IAvatarManager Italic( bool isItalic = true ) { + _isItalic = isItalic; + return this; + } + + #endregion + + #region Uppercase + + /// + public IAvatarManager Uppercase( bool isUppercase = true ) { + _isUppercase = isUppercase; + return this; + } + + #endregion + + #region Text + + /// + public IAvatarManager Text( string text, int length = 1 ) { + _text = text; + _length = length; + return this; + } + + #endregion + + #region SaveAsync + + /// + public virtual async Task SaveAsync( string path, CancellationToken cancellationToken = default ) { + if ( _text.IsEmpty() ) + return; + var imageWrapper = GetImageWrapper(); + await imageWrapper.SaveAsync( path, cancellationToken ); + } + + /// + /// 获取图片包装器 + /// + private IImageWrapper GetImageWrapper() { + return ImageManager.CreateImage( GetSize(), GetSize(), _backgroundColor ) + .Font( GetFontSize(), GetFontStyle(), _fontName ) + .TextCenter( GetText(), _fontColor ); + } + + /// + /// 获取头像大小 + /// + private int GetSize() { + if ( _size < 16 ) + return 16; + if ( _size > 512 ) + return 512; + return _size; + } + + /// + /// 获取字体大小 + /// + private float GetFontSize() { + if ( _fontSize is >= 0.1 and <= 1 ) + return _size * (float)_fontSize; + if ( _fontSize < 16 ) + return 16; + if ( _fontSize > _size ) + return _size * 0.5f; + return (float)_fontSize; + } + + /// + /// 获取字体样式 + /// + private FontStyle GetFontStyle() { + if ( _isBold && _isItalic ) + return FontStyle.BoldItalic; + if ( _isBold ) + return FontStyle.Bold; + if ( _isItalic ) + return FontStyle.Italic; + return FontStyle.Regular; + } + + /// + /// 获取文本 + /// + private string GetText() { + if ( _text.IsEmpty() ) + return null; + _text = GetPlusSeparatedText(); + if ( _isUppercase ) + _text = _text.ToUpperInvariant(); + if ( _text.Length > _length ) + return _text.Substring( 0, _length ); + return _text; + } + + /// + /// 获取加号分隔文本 + /// + private string GetPlusSeparatedText() { + if ( _text.Contains( "+" ) == false ) + return _text; + _length = 3; + return _text.Split( "+" ).Where( t => t.IsEmpty() == false ).Select( t => t.Trim().Substring( 0, 1 ) ).Join( separator: "" ); + } + + #endregion + + #region ToStreamAsync + + /// + /// 转换为图片流 + /// + /// 取消令牌 + public virtual async Task ToStreamAsync( CancellationToken cancellationToken = default ) { + if ( _text.IsEmpty() ) + return null; + var imageWrapper = GetImageWrapper(); + return await imageWrapper.ToStreamAsync( cancellationToken ); + } + + #endregion + + #region ToBase64 + + /// + public string ToBase64() { + if ( _text.IsEmpty() ) + return null; + var imageWrapper = GetImageWrapper(); + return imageWrapper.ToBase64(); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Images.Avatar/IAvatarManager.cs b/src/Util.Images.Avatar/IAvatarManager.cs new file mode 100644 index 000000000..dd09d9f91 --- /dev/null +++ b/src/Util.Images.Avatar/IAvatarManager.cs @@ -0,0 +1,70 @@ +using Util.Dependency; + +namespace Util.Images; + +/// +/// 头像服务 +/// +public interface IAvatarManager : ITransientDependency{ + /// + /// 设置背景色 + /// + /// 背景色,默认值: 5d005d + IAvatarManager BackgroundColor( string color ); + /// + /// 设置头像大小 + /// + /// 头像图片大小,单位:像素,值范围: 16 - 512,默认值: 64, 范例: 设置16,代表16*16的正方形图片 + IAvatarManager Size( int size ); + /// + /// 设置字体 + /// + /// 字体名称,范例: + IAvatarManager Font( string fontName ); + /// + /// 设置字体大小 + /// + /// 字体大小,值范围可以是16 - 512,单位:像素,值范围也可以是0.1 - 1,单位:百分比,默认值: 0.5,范例: 设置32,代表32像素,设置0.5,代表头像图片大小的一半 + IAvatarManager FontSize( double size ); + /// + /// 设置字体颜色 + /// + /// 字体颜色,默认值: ffffff + IAvatarManager FontColor( string color ); + /// + /// 设置或取消字体粗体,默认值: false + /// + /// 字体是否粗体 + IAvatarManager Bold( bool isBold = true ); + /// + /// 设置或取消字体斜体,默认值: false + /// + /// 字体是否斜体 + IAvatarManager Italic( bool isItalic = true ); + /// + /// 设置或取消字体大写,默认值: true + /// + /// 字体是否大写 + IAvatarManager Uppercase( bool isUppercase = true ); + /// + /// 设置文本 + /// + /// 文本 + /// 文本长度,默认值: 1,范例:文本为abcd,设置2,显示ab + IAvatarManager Text( string text,int length = 1 ); + /// + /// 保存到目标路径 + /// + /// 保存文件的绝对路径 + /// 取消令牌 + Task SaveAsync( string path, CancellationToken cancellationToken = default ); + /// + /// 转换为图片流 + /// + /// 取消令牌 + Task ToStreamAsync( CancellationToken cancellationToken = default ); + /// + /// 转换为Base64字符串 + /// + string ToBase64(); +} \ No newline at end of file diff --git a/src/Util.Images.Avatar/Usings.cs b/src/Util.Images.Avatar/Usings.cs new file mode 100644 index 000000000..759c7debf --- /dev/null +++ b/src/Util.Images.Avatar/Usings.cs @@ -0,0 +1,5 @@ +global using System; +global using System.Threading.Tasks; +global using System.Linq; +global using System.Threading; +global using SixLabors.Fonts; diff --git a/src/Util.Images.ImageSharp/01-Util.Images.ImageSharp.csproj b/src/Util.Images.ImageSharp/01-Util.Images.ImageSharp.csproj new file mode 100644 index 000000000..7e906185c --- /dev/null +++ b/src/Util.Images.ImageSharp/01-Util.Images.ImageSharp.csproj @@ -0,0 +1,38 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Images.ImageSharp + Util.Images + Util.Images.ImageSharp是Util应用框架基于ImageSharp的图片操作类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Images.ImageSharp.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Images.ImageSharp.xml + + + + + True + False + + + + + + + + + + + + + + diff --git a/src/Util.Images.ImageSharp/Commands/ICommand.cs b/src/Util.Images.ImageSharp/Commands/ICommand.cs new file mode 100644 index 000000000..f229e12ec --- /dev/null +++ b/src/Util.Images.ImageSharp/Commands/ICommand.cs @@ -0,0 +1,12 @@ +namespace Util.Images.Commands; + +/// +/// 调用命令 +/// +public interface ICommand { + /// + /// 调用 + /// + /// 图片实例 + void Invoke( Image image ); +} \ No newline at end of file diff --git a/src/Util.Images.ImageSharp/Commands/TextCommand.cs b/src/Util.Images.ImageSharp/Commands/TextCommand.cs new file mode 100644 index 000000000..6c16575b0 --- /dev/null +++ b/src/Util.Images.ImageSharp/Commands/TextCommand.cs @@ -0,0 +1,69 @@ +namespace Util.Images.Commands; + +/// +/// 文本命令 +/// +public class TextCommand : ICommand { + /// + /// 初始化文本命令 + /// + /// 字体 + /// 文本 + /// 颜色 + /// x坐标 + /// y坐标 + public TextCommand( Font font, string text, string color, float x, float y ) { + Font = font; + Text = text; + Color = color; + X = x; + Y = y; + } + + /// + /// 初始化文本命令 + /// + /// 文本配置 + /// 文本 + /// 颜色 + public TextCommand( RichTextOptions options, string text, string color ) { + Options = options; + Text = text; + Color = color; + } + + /// + /// 字体 + /// + public Font Font { get; } + /// + /// 文本 + /// + public string Text { get; } + /// + /// 颜色 + /// + public string Color { get; } + /// + /// x坐标 + /// + public float X { get; } + /// + /// y坐标 + /// + public float Y { get; } + /// + /// 文本配置 + /// + public RichTextOptions Options { get; } + + /// + public void Invoke( Image image ) { + image.CheckNull( nameof( image ) ); + if ( Options == null ) { + image.Mutate( x => x.DrawText( Text, Font, SixLabors.ImageSharp.Color.ParseHex( Color ), new PointF( X, Y ) ) ); + return; + } + image.Mutate( x => x.DrawText( Options, Text, SixLabors.ImageSharp.Color.ParseHex( Color ) ) ); + } +} \ No newline at end of file diff --git a/src/Util.Images.ImageSharp/IImageManager.cs b/src/Util.Images.ImageSharp/IImageManager.cs new file mode 100644 index 000000000..688c5d884 --- /dev/null +++ b/src/Util.Images.ImageSharp/IImageManager.cs @@ -0,0 +1,21 @@ +using Util.Dependency; + +namespace Util.Images; + +/// +/// 图片服务 +/// +public interface IImageManager : ISingletonDependency { + /// + /// 创建图片 + /// + /// 宽度,单位:像素 + /// 高度,单位:像素 + /// 背景色 + IImageWrapper CreateImage( int width, int height,string backgroundColor = null ); + /// + /// 加载图片 + /// + /// 图片绝对路径 + IImageWrapper LoadImage( string path ); +} \ No newline at end of file diff --git a/src/Util.Images.ImageSharp/IImageWrapper.cs b/src/Util.Images.ImageSharp/IImageWrapper.cs new file mode 100644 index 000000000..4728ce7e8 --- /dev/null +++ b/src/Util.Images.ImageSharp/IImageWrapper.cs @@ -0,0 +1,75 @@ +namespace Util.Images; + +/// +/// 图片操作包装器 +/// +public interface IImageWrapper { + /// + /// 设置图片类型,默认值: Png + /// + /// 图片类型 + IImageWrapper ImageType( ImageType type ); + + /// + /// 设置默认字体 + /// + /// 字体名称 + IImageWrapper DefaultFontName( string name ); + + /// + /// 设置字体 + /// + /// 字体大小 + /// 字体样式 + /// 字体名称 + IImageWrapper Font( float size, FontStyle style = FontStyle.Regular, string fontName = null ); + + /// + /// 设置字体 + /// + /// 字体大小 + /// 字体名称 + IImageWrapper Font( float size, string fontName ); + + /// + /// 写入文本 + /// + /// 文本 + /// 颜色 + /// x坐标 + /// y坐标 + IImageWrapper Text( string text, string color, float x, float y ); + + /// + /// 写入文本 + /// + /// 文本 + /// 颜色 + /// 文本配置 + IImageWrapper Text( string text, string color, RichTextOptions options ); + + /// + /// 写入文本并居中显示 + /// + /// 文本 + /// 颜色 + IImageWrapper TextCenter( string text, string color ); + + /// + /// 保存到目标路径 + /// + /// 保存文件的绝对路径 + /// 取消令牌 + Task SaveAsync( string path, CancellationToken cancellationToken = default ); + + /// + /// 转换为图片流 + /// + /// 取消令牌 + Task ToStreamAsync( CancellationToken cancellationToken = default ); + + /// + /// 转换为Base64字符串 + /// + string ToBase64(); +} \ No newline at end of file diff --git a/src/Util.Images.ImageSharp/ImageManager.cs b/src/Util.Images.ImageSharp/ImageManager.cs new file mode 100644 index 000000000..08d7b91a4 --- /dev/null +++ b/src/Util.Images.ImageSharp/ImageManager.cs @@ -0,0 +1,84 @@ +namespace Util.Images; + +/// +/// 图片服务 +/// +public class ImageManager : IImageManager { + /// + /// 字体集合 + /// + private static readonly ConcurrentDictionary _fonts = new(); + + /// + public IImageWrapper CreateImage( int width, int height, string backgroundColor = null ) { + return new ImageWrapper( width, height, backgroundColor ); + } + + /// + public IImageWrapper LoadImage( string path ) { + return new ImageWrapper( path ); + } + + /// + /// 加载字体集合 + /// + /// 字体文件目录的绝对路径 + public static void LoadFonts( string path ) { + if ( Util.Helpers.File.DirectoryExists( path ) == false ) + return; + LoadTtfFonts( path ); + } + + /// + /// 加载ttf字体 + /// + private static void LoadTtfFonts( string path ) { + var files = Util.Helpers.File.GetAllFiles( path, "*.ttf" ); + foreach ( var file in files ) { + var name = System.IO.Path.GetFileNameWithoutExtension( file.Name ); + LoadFont( name, file.FullName ); + } + } + + /// + /// 加载字体 + /// + /// 字体名称 + /// 字体文件绝对路径 + public static void LoadFont( string name, string path ) { + var fonts = new FontCollection(); + var fontFamily = fonts.Add( path ); + _fonts.TryAdd( name, fontFamily ); + } + + /// + /// 获取字体 + /// + /// 字体名称 + public static FontFamily GetFont( string name ) { + var result = GetFontFamily( name ); + return result.Name.IsEmpty() ? SystemFonts.Families.FirstOrDefault() : result; + } + + /// + /// 获取字体 + /// + private static FontFamily GetFontFamily( string name ) { + if ( name.IsEmpty() ) + return new FontFamily(); + if ( _fonts.TryGetValue( name, out var result ) ) + return result; + return SystemFonts.Families.FirstOrDefault( t => t.Name == name ); + } + + /// + /// 获取受支持的字体列表 + /// + public static List GetSupportedFonts() { + var result = new List(); + foreach ( var fontName in _fonts.Keys ) + result.Add( fontName ); + result.AddRange( SystemFonts.Families.Select( t => t.Name ) ); + return result; + } +} \ No newline at end of file diff --git a/src/Util.Images.ImageSharp/ImageSharpExtensions.cs b/src/Util.Images.ImageSharp/ImageSharpExtensions.cs new file mode 100644 index 000000000..281086331 --- /dev/null +++ b/src/Util.Images.ImageSharp/ImageSharpExtensions.cs @@ -0,0 +1,69 @@ +using SixLabors.ImageSharp.Drawing; + +namespace Util.Images; + +/// +/// ImageSharp扩展 +/// +public static class ImageSharpExtensions { + /// + /// 转换颜色类型 + /// + /// 颜色 + public static Color ToImageSharpColor( this System.Drawing.Color color ) { + return Color.FromRgba( color.R, color.G, color.B, color.A ); + } + + /// + /// 图片缩放 + /// + /// 图片 + /// 缩放比例 + public static Image Zoom( this Image image, double scale ) { + var width = Util.Helpers.Convert.ToInt( image.Width * scale ); + var height = Util.Helpers.Convert.ToInt( image.Height * scale ); + image.Mutate( t => { + t.Resize( new Size( width, height ) ); + } ); + return image; + } + + /// + /// 设置圆角 + /// + /// 图片 + /// 角度 + public static Image RoundCorners( this Image image, int cornerRadius ) { + image.Mutate( context => RoundCorners( context, cornerRadius ) ); + return image; + } + + /// + /// 设置圆角 + /// + /// 图片处理上下文 + /// 角度 + private static void RoundCorners( IImageProcessingContext context, int cornerRadius ) { + var size = context.GetCurrentSize(); + var corners = BuildCorners( size.Width, size.Height, cornerRadius ); + context.SetGraphicsOptions( new GraphicsOptions { AlphaCompositionMode = PixelAlphaCompositionMode.DestOut } ); + context.Fill( Color.Red, corners ); + } + + /// + /// 构建圆角 + /// + /// 图片宽度 + /// 图片高度 + /// 角度 + private static IPathCollection BuildCorners( int imageWidth, int imageHeight, float cornerRadius ) { + var rect = new RectangularPolygon( -0.5f, -0.5f, cornerRadius, cornerRadius ); + var cornerTopLeft = rect.Clip( new EllipsePolygon( cornerRadius - 0.5f, cornerRadius - 0.5f, cornerRadius ) ); + var rightPos = imageWidth - cornerTopLeft.Bounds.Width + 1; + var bottomPos = imageHeight - cornerTopLeft.Bounds.Height + 1; + var cornerTopRight = cornerTopLeft.RotateDegree( 90 ).Translate( rightPos, 0 ); + var cornerBottomLeft = cornerTopLeft.RotateDegree( -90 ).Translate( 0, bottomPos ); + var cornerBottomRight = cornerTopLeft.RotateDegree( 180 ).Translate( rightPos, bottomPos ); + return new PathCollection( cornerTopLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight ); + } +} \ No newline at end of file diff --git a/src/Util.Images.ImageSharp/ImageWrapper.cs b/src/Util.Images.ImageSharp/ImageWrapper.cs new file mode 100644 index 000000000..9adccfa47 --- /dev/null +++ b/src/Util.Images.ImageSharp/ImageWrapper.cs @@ -0,0 +1,257 @@ +using Util.Images.Commands; + +namespace Util.Images; + +/// +/// 图片操作包装器 +/// +public class ImageWrapper : IImageWrapper { + + #region 属性 + + /// + /// 调用命令 + /// + private readonly List _commands = new(); + /// + /// 图片类型 + /// + private ImageType _imageType; + /// + /// 宽度 + /// + private readonly int _width; + /// + /// 高度 + /// + private readonly int _height; + /// + /// 背景色 + /// + private readonly string _backgroundColor; + /// + /// 图片加载路径 + /// + private readonly string _loadPath; + /// + /// 默认字符名称 + /// + private static string _defaultFontName; + /// + /// 字体 + /// + private Font _font; + + #endregion + + #region 构造方法 + + /// + /// 初始化图片操作 + /// + /// 宽度 + /// 高度 + /// 背景色 + public ImageWrapper( int width, int height, string backgroundColor ) { + _imageType = Images.ImageType.Png; + _width = width; + _height = height; + _backgroundColor = backgroundColor; + } + + /// + /// 初始化图片操作 + /// + /// 图片路径 + public ImageWrapper( string path ) { + _imageType = Images.ImageType.Png; + _loadPath = path; + } + + #endregion + + #region ImageType + + /// + /// 设置图片类型 + /// + /// 图片类型 + public IImageWrapper ImageType( ImageType type ) { + _imageType = type; + return this; + } + + #endregion + + #region DefaultFontName + + /// + /// 设置默认字体 + /// + /// 字体名称 + public IImageWrapper DefaultFontName( string name ) { + _defaultFontName = name; + return this; + } + + #endregion + + #region Font + + /// + public IImageWrapper Font( float size, FontStyle style = FontStyle.Regular, string fontName = null ) { + if ( fontName.IsEmpty() ) + fontName = _defaultFontName; + var fontFamily = ImageManager.GetFont( fontName ); + _font = new Font( fontFamily, size, style ); + return this; + } + + /// + public IImageWrapper Font( float size, string fontName ) { + return Font( size, FontStyle.Regular, fontName ); + } + + #endregion + + #region Text + + /// + public IImageWrapper Text( string text, string color, float x, float y ) { + _commands.Add( new TextCommand( _font, text, color, x, y ) ); + return this; + } + + /// + public IImageWrapper Text( string text, string color, RichTextOptions options ) { + _commands.Add( new TextCommand( options, text, color ) ); + return this; + } + + #endregion + + #region TextCenter + + /// + public IImageWrapper TextCenter( string text, string color ) { + var options = new RichTextOptions( _font ) { + VerticalAlignment = VerticalAlignment.Center, + HorizontalAlignment = HorizontalAlignment.Center, + Origin = new Vector2( _width / 2, _height / 2 ) + }; + return Text( text, color, options ); + } + + #endregion + + #region SaveAsync + + /// + public async Task SaveAsync( string path, CancellationToken cancellationToken = default ) { + using var image = GetImage(); + _commands.ForEach( command => command.Invoke( image ) ); + Util.Helpers.File.CreateDirectory( path ); + await image.SaveAsync( path, GetImageEncoder(), cancellationToken ); + } + + /// + /// 获取图片实例 + /// + protected Image GetImage() { + if ( _loadPath.IsEmpty() ) + return new Image( _width, _height, GetColor( _backgroundColor ) ); + return Image.Load( _loadPath ); + } + + /// + /// 获取颜色 + /// + /// 颜色值 + protected Color GetColor( string color ) { + return Color.ParseHex( color ); + } + + /// + /// 获取图片编码器 + /// + private IImageEncoder GetImageEncoder() { + return GetImageEncoder( _imageType ); + } + + #endregion + + #region ToStreamAsync + + /// + public async Task ToStreamAsync( CancellationToken cancellationToken = default ) { + using var image = GetImage(); + using var stream = new MemoryStream(); + _commands.ForEach( command => command.Invoke( image ) ); + await image.SaveAsync( stream, GetImageEncoder(), cancellationToken ); + return await Util.Helpers.File.ToBytesAsync( stream, cancellationToken ); + } + + #endregion + + #region ToBase64 + + /// + public string ToBase64() { + using var image = GetImage(); + _commands.ForEach( command => command.Invoke( image ) ); + return image.ToBase64String( GetImageFormat() ); + } + + /// + /// 获取图片格式 + /// + private IImageFormat GetImageFormat() { + return GetImageFormat( _imageType ); + } + + #endregion + + #region GetImageEncoder + + /// + /// 获取图片编码器 + /// + public static IImageEncoder GetImageEncoder( ImageType type ) { + switch ( type ) { + case Images.ImageType.Png: + return new PngEncoder(); + case Images.ImageType.Gif: + return new GifEncoder(); + case Images.ImageType.Bmp: + return new BmpEncoder(); + case Images.ImageType.Jpg: + return new JpegEncoder(); + default: + return new PngEncoder(); + } + } + + #endregion + + #region GetImageFormat + + /// + /// 获取图片格式 + /// + public static IImageFormat GetImageFormat( ImageType type ) { + switch ( type ) { + case Images.ImageType.Png: + return PngFormat.Instance; + case Images.ImageType.Gif: + return GifFormat.Instance; + case Images.ImageType.Bmp: + return BmpFormat.Instance; + case Images.ImageType.Jpg: + return JpegFormat.Instance; + default: + return PngFormat.Instance; + } + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Images.ImageSharp/Usings.cs b/src/Util.Images.ImageSharp/Usings.cs new file mode 100644 index 000000000..c1e13363e --- /dev/null +++ b/src/Util.Images.ImageSharp/Usings.cs @@ -0,0 +1,17 @@ +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using System.IO; +global using System.Collections.Concurrent; +global using System.Numerics; +global using SixLabors.ImageSharp; +global using SixLabors.Fonts; +global using SixLabors.ImageSharp.Drawing.Processing; +global using SixLabors.ImageSharp.Processing; +global using SixLabors.ImageSharp.PixelFormats; +global using SixLabors.ImageSharp.Formats; +global using SixLabors.ImageSharp.Formats.Bmp; +global using SixLabors.ImageSharp.Formats.Gif; +global using SixLabors.ImageSharp.Formats.Jpeg; +global using SixLabors.ImageSharp.Formats.Png; \ No newline at end of file diff --git a/src/Util.Localization/09-Util.Localization.csproj b/src/Util.Localization/09-Util.Localization.csproj new file mode 100644 index 000000000..5f4a996e8 --- /dev/null +++ b/src/Util.Localization/09-Util.Localization.csproj @@ -0,0 +1,41 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Localization + Util.Localization + Util.Localization是Util应用框架的本地化操作类库,Json本地化操作代码从 https://github.com/hishamco/My.Extensions.Localization.Json 整理而来,并进行相应修改 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Localization.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Localization.xml + + + + + True + False + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Util.Localization/AppBuilderExtensions.cs b/src/Util.Localization/AppBuilderExtensions.cs new file mode 100644 index 000000000..452a49636 --- /dev/null +++ b/src/Util.Localization/AppBuilderExtensions.cs @@ -0,0 +1,127 @@ +using Util.Localization.Json; +using Util.Localization.Store; + +namespace Util.Localization; + +/// +/// 本地化操作扩展 +/// +public static class AppBuilderExtensions { + + #region AddJsonLocalization + + /// + /// 配置Json本地化 + /// + /// 应用生成器 + public static IAppBuilder AddJsonLocalization( this IAppBuilder builder ) { + return builder.AddJsonLocalization( "Resources" ); + } + + /// + /// 配置Json本地化 + /// + /// 应用生成器 + /// 资源路径,默认值: Resources + public static IAppBuilder AddJsonLocalization( this IAppBuilder builder, string resourcesPath ) { + return builder.AddJsonLocalization( t => t.ResourcesPath = resourcesPath ); + } + + /// + /// 配置Json本地化 + /// + /// 应用生成器 + /// Json本地化配置操作 + public static IAppBuilder AddJsonLocalization( this IAppBuilder builder, Action setupAction ) { + builder.CheckNull( nameof( builder ) ); + var options = new JsonLocalizationOptions(); + setupAction?.Invoke( options ); + builder.Host.ConfigureServices( ( context, services ) => { + if ( setupAction != null ) { + services.Configure( setupAction ); + void Action( LocalizationOptions localizationOptions ) { + localizationOptions.Expiration = options.Expiration; + localizationOptions.IsLocalizeWarning = options.IsLocalizeWarning; + localizationOptions.Cultures = options.Cultures; + } + services.Configure( (Action)Action ); + } + services.AddMemoryCache(); + services.RemoveAll( typeof( IStringLocalizerFactory ) ); + services.RemoveAll( typeof( IStringLocalizer<> ) ); + services.RemoveAll( typeof( IStringLocalizer ) ); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddTransient( typeof( IStringLocalizer<> ), typeof( StringLocalizer<> ) ); + services.TryAddTransient( typeof( IStringLocalizer ), typeof( StringLocalizer ) ); + } ); + if( options.Cultures == null || options.Cultures.Count == 0 ) + return builder; + builder.Host.ConfigureServices( ( _, services ) => { + ConfigRequestLocalization( services, options ); + } ); + return builder; + } + + /// + /// 配置请求本地化 + /// + private static void ConfigRequestLocalization( IServiceCollection services, LocalizationOptions options ) { + services.Configure( localizationOptions => { + var supportedCultures = options.Cultures.Select( culture => new CultureInfo( culture ) ).ToList(); + localizationOptions.DefaultRequestCulture = new RequestCulture( culture: supportedCultures[0], uiCulture: supportedCultures[0] ); + localizationOptions.SupportedCultures = supportedCultures; + localizationOptions.SupportedUICultures = supportedCultures; + localizationOptions.AddInitialRequestCultureProvider( new CustomRequestCultureProvider( async context => { + var culture = context.Request.Headers.ContentLanguage.FirstOrDefault(); + if ( culture.IsEmpty() ) + culture = "zh-CN"; + return await Task.FromResult( new ProviderCultureResult( culture ) ); + } ) ); + } ); + } + + #endregion + + #region AddStoreLocalization + + /// + /// 配置基于数据存储的本地化 + /// + /// 应用生成器 + public static IAppBuilder AddStoreLocalization( this IAppBuilder builder ) where TStore : ILocalizedStore { + return builder.AddStoreLocalization( options => options.Expiration = 28800 ); + } + + /// + /// 配置基于数据存储的本地化 + /// + /// 应用生成器 + /// 本地化配置操作 + public static IAppBuilder AddStoreLocalization( this IAppBuilder builder, Action setupAction ) where TStore : ILocalizedStore { + builder.CheckNull( nameof( builder ) ); + var options = new LocalizationOptions(); + setupAction?.Invoke( options ); + builder.Host.ConfigureServices( ( context, services ) => { + if ( setupAction != null ) + services.Configure( setupAction ); + services.AddMemoryCache(); + services.RemoveAll( typeof( IStringLocalizerFactory ) ); + services.RemoveAll( typeof( IStringLocalizer<> ) ); + services.RemoveAll( typeof( IStringLocalizer ) ); + services.TryAddSingleton(); + services.TryAddTransient( typeof( IStringLocalizer<> ), typeof( StringLocalizer<> ) ); + services.TryAddTransient( typeof( IStringLocalizer ), typeof( StringLocalizer ) ); + services.TryAddTransient( typeof( ILocalizedStore ), typeof( TStore ) ); + services.TryAddTransient(); + } ); + builder.Host.ConfigureServices( ( _, services ) => { + if ( options.Cultures == null || options.Cultures.Count == 0 ) + return; + ConfigRequestLocalization( services, options ); + } ); + return builder; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Localization/Base/StringLocalizerBase.cs b/src/Util.Localization/Base/StringLocalizerBase.cs new file mode 100644 index 000000000..bb93c83aa --- /dev/null +++ b/src/Util.Localization/Base/StringLocalizerBase.cs @@ -0,0 +1,122 @@ +using Util.Localization.Caching; + +namespace Util.Localization.Base; + +/// +/// 本地化资源查找器 +/// +public abstract class StringLocalizerBase : IStringLocalizer { + /// + /// 缓存 + /// + protected readonly IMemoryCache Cache; + /// + /// 资源类型 + /// + protected readonly string Type; + /// + /// 本地化配置 + /// + protected LocalizationOptions Options; + + /// + /// 初始化本地化资源查找器 + /// + /// 缓存 + /// 资源类型 + /// 本地化配置 + protected StringLocalizerBase( IMemoryCache cache, string type, IOptions options ) { + Cache = cache; + Type = type; + Options = options?.Value ?? new LocalizationOptions(); + } + + /// + public LocalizedString this[string name] { + get { + if( name.IsEmpty() ) + throw new ArgumentNullException( nameof( name ) ); + return GetResult( name ); + } + } + + /// + public LocalizedString this[string name, params object[] arguments] { + get { + if( name.IsEmpty() ) + throw new ArgumentNullException( nameof( name ) ); + return GetResult( name, arguments ); + } + } + + /// + /// 获取本地化字符串结果 + /// + /// 资源名称 + /// 参数列表 + protected virtual LocalizedString GetResult( string name, params object[] arguments ) { + LocalizedString result = null; + var cultures = Util.Helpers.Culture.GetCurrentUICultures(); + foreach( var culture in cultures ) { + result = GetLocalizedStringByCache( culture, name ); + if( result.ResourceNotFound == false ) + return FormatResult( result, name, arguments ); + } + return FormatResult( result, name, arguments ); + } + + /// + /// 从缓存获取本地化字符串 + /// + /// 区域文化 + /// 资源名称 + protected virtual LocalizedString GetLocalizedStringByCache( CultureInfo culture, string name ) { + var key = CacheHelper.GetCacheKey( culture.Name, Type, name ); + return Cache == null ? GetLocalizedString( culture, name ) : Cache.GetOrCreate( key, entry => { + entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds( CacheHelper.GetExpiration( Options ) ); + return GetLocalizedString( culture, name ); + } ); + } + + /// + /// 获取本地化字符串 + /// + /// 区域文化 + /// 资源名称 + protected virtual LocalizedString GetLocalizedString( CultureInfo culture, string name ) { + var value = GetValue( culture, name, Type ); + if( value.IsEmpty() ) + return new LocalizedString( name, string.Empty, true, null ); + return new LocalizedString( name, value, false, null ); + } + + /// + /// 获取资源值 + /// + /// 区域文化 + /// 资源名称 + /// 资源类型 + protected abstract string GetValue( CultureInfo culture, string name, string type ); + + /// + /// 格式化结果 + /// + /// 本地化字符串结果 + /// 资源名称 + /// 参数列表 + protected virtual LocalizedString FormatResult( LocalizedString result, string name, params object[] arguments ) { + if( result == null ) + return new LocalizedString( name, string.Format( name, arguments ), true, null ); + if( result.ResourceNotFound ) + return new LocalizedString( result.Name, string.Format( result.Name, arguments ), true, null ); + if( arguments == null || arguments.Length == 0 ) + return result; + return new LocalizedString( result.Name, string.Format( result.Value, arguments ), false, result.SearchedLocation ); + } + + /// + /// 获取全部本地化字符串 + /// + /// 是否包含父区域 + public abstract IEnumerable GetAllStrings( bool includeParentCultures ); +} \ No newline at end of file diff --git a/src/Util.Localization/Caching/CacheHelper.cs b/src/Util.Localization/Caching/CacheHelper.cs new file mode 100644 index 000000000..04b255245 --- /dev/null +++ b/src/Util.Localization/Caching/CacheHelper.cs @@ -0,0 +1,23 @@ +namespace Util.Localization.Caching; + +/// +/// 缓存辅助操作 +/// +internal static class CacheHelper { + /// + /// 获取缓存键 + /// + /// 区域文化 + /// 资源类型 + /// 资源名称 + public static string GetCacheKey( string culture, string type, string name ) { + return $"{culture}-{type}-{name}"; + } + + /// + /// 获取缓存过期时间间隔 + /// + public static int GetExpiration( LocalizationOptions options ) { + return options.Expiration + Random.Shared.Next( 1200 ); + } +} \ No newline at end of file diff --git a/src/Util.Localization/Json/IPathResolver.cs b/src/Util.Localization/Json/IPathResolver.cs new file mode 100644 index 000000000..090273ed7 --- /dev/null +++ b/src/Util.Localization/Json/IPathResolver.cs @@ -0,0 +1,31 @@ +namespace Util.Localization.Json; + +/// +/// 路径解析器 +/// +public interface IPathResolver { + /// + /// 获取根命名空间 + /// + /// 资源类型所在的程序集 + string GetRootNamespace( Assembly assembly ); + /// + /// 获取资源根目录路径 + /// + /// 资源类型所在的程序集 + /// 配置的根路径 + string GetResourcesRootPath( Assembly assembly,string rootPath ); + /// + /// 获取资源基名称 + /// + /// 资源类型所在的程序集 + /// 资源类型完全限定名 + string GetResourcesBaseName( Assembly assembly,string typeFullName ); + /// + /// 获取Json资源文件绝对路径 + /// + /// 资源根目录路径 + /// 资源基名称 + /// 区域文化 + string GetJsonResourcePath( string rootPath, string baseName,CultureInfo culture ); +} \ No newline at end of file diff --git a/src/Util.Localization/Json/JsonStringLocalizer.cs b/src/Util.Localization/Json/JsonStringLocalizer.cs new file mode 100644 index 000000000..1fd6a2755 --- /dev/null +++ b/src/Util.Localization/Json/JsonStringLocalizer.cs @@ -0,0 +1,136 @@ +using Util.Localization.Base; + +namespace Util.Localization.Json; + +/// +/// Json本地化资源查找器 +/// +public class JsonStringLocalizer : StringLocalizerBase { + /// + /// 路径解析器 + /// + private readonly IPathResolver _pathResolver; + /// + /// 资源根目录路径 + /// + private readonly string _resourcesRootPath; + /// + /// 资源基名称 + /// + private readonly string _resourcesBaseName; + /// + /// 日志 + /// + private readonly ILogger _logger; + /// + /// 资源缓存 + /// + private readonly ConcurrentDictionary>> _resourcesCache; + /// + /// 搜索位置 + /// + private readonly string _searchedLocation; + + /// + /// 初始化Json本地化资源查找器 + /// + /// 路径解析器 + /// 资源根目录路径 + /// 资源基名称 + /// 日志 + /// 缓存 + /// 本地化配置 + public JsonStringLocalizer( IPathResolver pathResolver, string resourcesRootPath, string resourcesBaseName, ILogger logger, + IMemoryCache cache, IOptions options ) + : base( cache, resourcesBaseName, options ) { + _pathResolver = pathResolver; + _resourcesRootPath = resourcesRootPath; + _resourcesBaseName = resourcesBaseName; + _logger = logger ?? NullLogger.Instance; + _resourcesCache = new ConcurrentDictionary>>(); + _searchedLocation = $"{resourcesRootPath}.{resourcesBaseName}"; + } + + /// + /// 获取本地化字符串 + /// + /// 区域文化 + /// 资源名称 + protected override LocalizedString GetLocalizedString( CultureInfo culture, string name ) { + var value = GetValue( culture, name, _resourcesBaseName ) ?? GetValue( culture, name, null ); + if ( value.IsEmpty() ) + return new LocalizedString( name, string.Empty, true, null ); + return new LocalizedString( name, value, false, _searchedLocation ); + } + + /// + /// 获取资源值 + /// + /// 区域文化 + /// 资源名称 + /// 资源类型 + protected override string GetValue( CultureInfo culture, string name, string type ) { + var resources = GetResourcesByCache( culture, type ); + if ( resources == null ) + return null; + var resource = resources.SingleOrDefault( t => t.Key == name ); + var result = resource.Value; + _logger.SearchedLocation( name, _searchedLocation, culture ); + return result; + } + + /// + /// 从缓存获取资源列表 + /// + /// 区域文化 + /// 资源基名称 + protected virtual IEnumerable> GetResourcesByCache( CultureInfo culture, string resourcesBaseName ) { + return _resourcesCache.GetOrAdd( $"{culture.Name}_{resourcesBaseName}", _ => GetResources( culture, resourcesBaseName ) ); + } + + /// + /// 获取资源列表 + /// + /// 区域文化 + /// 资源基名称 + protected virtual IEnumerable> GetResources( CultureInfo culture, string resourcesBaseName ) { + var path = _pathResolver.GetJsonResourcePath( _resourcesRootPath, resourcesBaseName, culture ); + if ( File.Exists( path ) == false ) { + if ( resourcesBaseName.IsEmpty() ) + return null; + path = _pathResolver.GetJsonResourcePath( _resourcesRootPath, resourcesBaseName.Replace( '.', Path.DirectorySeparatorChar ), culture ); + } + if ( File.Exists( path ) == false ) + return null; + var builder = new ConfigurationBuilder() + .AddJsonFile( path, optional: false, reloadOnChange: false ); + var config = builder.Build(); + return config.AsEnumerable(); + } + + /// + /// 获取全部本地化字符串 + /// + /// 是否包含父区域 + public override IEnumerable GetAllStrings( bool includeParentCultures ) { + var result = new List(); + var cultures = Util.Helpers.Culture.GetCurrentUICultures(); + foreach ( var culture in cultures ) { + var resources = ToLocalizedStrings( GetResourcesByCache( culture, _resourcesBaseName ) ); + result.AddRange( resources ); + if ( includeParentCultures == false ) + return result; + } + return result; + } + + /// + /// 转换为LocalizedString集合 + /// + protected virtual IEnumerable ToLocalizedStrings( IEnumerable> resources ) { + var result = new List(); + foreach ( var resource in resources ) + result.Add( new LocalizedString( resource.Key, resource.Value, false, _searchedLocation ) ); + return result; + } +} \ No newline at end of file diff --git a/src/Util.Localization/Json/JsonStringLocalizerFactory.cs b/src/Util.Localization/Json/JsonStringLocalizerFactory.cs new file mode 100644 index 000000000..0b3854957 --- /dev/null +++ b/src/Util.Localization/Json/JsonStringLocalizerFactory.cs @@ -0,0 +1,60 @@ +namespace Util.Localization.Json; + +/// +/// Json本地化资源查找器工厂 +/// +public class JsonStringLocalizerFactory : IStringLocalizerFactory { + /// + /// 资源根目录路径 + /// + private readonly string _rootPath; + /// + /// 路径解析器 + /// + private readonly IPathResolver _pathResolver; + /// + /// 日志工厂 + /// + private readonly ILoggerFactory _loggerFactory; + /// + /// 缓存 + /// + private readonly IMemoryCache _cache; + /// + /// 本地化配置 + /// + private readonly IOptions _options; + + /// + /// 初始化Json本地化资源查找器工厂 + /// + /// 本地化配置 + /// 路径解析器 + /// 日志工厂 + /// 缓存 + public JsonStringLocalizerFactory( IOptions options, IPathResolver pathResolver, ILoggerFactory loggerFactory, IMemoryCache cache = null ) { + _options = options; + _rootPath = options.Value.ResourcesPath; + _pathResolver = pathResolver ?? throw new ArgumentNullException( nameof( pathResolver ) ); + _loggerFactory = loggerFactory ?? throw new ArgumentNullException( nameof(loggerFactory) ); + _cache = cache; + } + + /// + public IStringLocalizer Create( Type resourceSource ) { + resourceSource.CheckNull( nameof( resourceSource ) ); + var assembly = resourceSource.Assembly; + var rootPath = _pathResolver.GetResourcesRootPath( assembly, _rootPath ); + var baseName = _pathResolver.GetResourcesBaseName( assembly, resourceSource.FullName ); + return new JsonStringLocalizer( _pathResolver, rootPath, baseName, _loggerFactory.CreateLogger(), _cache, _options ); + } + + /// + public IStringLocalizer Create( string baseName, string location ) { + location ??= new AssemblyName( GetType().Assembly.FullName ).Name; + var assemblyName = new AssemblyName( location ); + var assembly = Assembly.Load( assemblyName ); + var rootPath = _pathResolver.GetResourcesRootPath( assembly, _rootPath ); + return new JsonStringLocalizer( _pathResolver, rootPath, baseName, _loggerFactory.CreateLogger(), _cache, _options ); + } +} \ No newline at end of file diff --git a/src/Util.Localization/Json/JsonStringLocalizerLoggerExtensions.cs b/src/Util.Localization/Json/JsonStringLocalizerLoggerExtensions.cs new file mode 100644 index 000000000..fbc425cd1 --- /dev/null +++ b/src/Util.Localization/Json/JsonStringLocalizerLoggerExtensions.cs @@ -0,0 +1,19 @@ +namespace Util.Localization.Json; + +/// +/// Json本地化日志扩展 +/// +internal static class JsonStringLocalizerLoggerExtensions { + private static readonly Action _searchedLocation; + + static JsonStringLocalizerLoggerExtensions() { + _searchedLocation = LoggerMessage.Define( + LogLevel.Debug, + 1, + $"{nameof( JsonStringLocalizer )} searched for '{{Key}}' in '{{LocationSearched}}' with culture '{{Culture}}'." ); + } + + public static void SearchedLocation( this ILogger logger, string key, string searchedLocation, CultureInfo culture ) { + _searchedLocation( logger, key, searchedLocation, culture, null ); + } +} \ No newline at end of file diff --git a/src/Util.Localization/Json/PathResolver.cs b/src/Util.Localization/Json/PathResolver.cs new file mode 100644 index 000000000..7419aa7d0 --- /dev/null +++ b/src/Util.Localization/Json/PathResolver.cs @@ -0,0 +1,45 @@ +using Util.Helpers; + +namespace Util.Localization.Json; + +/// +/// 路径解析器 +/// +public class PathResolver : IPathResolver { + /// + public string GetRootNamespace( Assembly assembly ) { + assembly.CheckNull( nameof(assembly) ); + var attribute = assembly.GetCustomAttribute(); + return attribute == null ? assembly.GetName().Name : attribute.RootNamespace; + } + + /// + public string GetResourcesRootPath( Assembly assembly, string rootPath ) { + if ( assembly == null ) + return rootPath; + var attribute = assembly.GetCustomAttribute(); + return attribute == null ? rootPath : attribute.ResourceLocation; + } + + /// + public string GetResourcesBaseName( Assembly assembly, string typeFullName ) { + var rootNamespace = GetRootNamespace( assembly ); + return typeFullName.RemoveStart( $"{rootNamespace}." ); + } + + /// + public string GetJsonResourcePath( string rootPath, string baseName, CultureInfo culture ) { + if( baseName.IsEmpty() ) + return Path.Combine( Common.ApplicationBaseDirectory, rootPath, $"{culture.Name}.json" ); + baseName = FixInnerClassPath( baseName ); + return Path.Combine( Common.ApplicationBaseDirectory, rootPath, $"{baseName}.{culture.Name}.json" ); + } + + /// + /// 修复内部类分隔符+ + /// + private string FixInnerClassPath( string path ) { + const char innerClassSeparator = '+'; + return path.Contains( innerClassSeparator ) ? path.Replace( innerClassSeparator, '.' ) : path; + } +} \ No newline at end of file diff --git a/src/Util.Localization/JsonLocalizationOptions.cs b/src/Util.Localization/JsonLocalizationOptions.cs new file mode 100644 index 000000000..d01168438 --- /dev/null +++ b/src/Util.Localization/JsonLocalizationOptions.cs @@ -0,0 +1,18 @@ +namespace Util.Localization; + +/// +/// Json本地化配置 +/// +public class JsonLocalizationOptions : LocalizationOptions { + /// + /// 初始化Json本地化配置 + /// + public JsonLocalizationOptions() { + ResourcesPath = "Resources"; + } + + /// + /// 资源路径 + /// + public string ResourcesPath { get; set; } +} \ No newline at end of file diff --git a/src/Util.Localization/LocalizationOptions.cs b/src/Util.Localization/LocalizationOptions.cs new file mode 100644 index 000000000..a46c29400 --- /dev/null +++ b/src/Util.Localization/LocalizationOptions.cs @@ -0,0 +1,28 @@ +namespace Util.Localization; + +/// +/// 本地化配置 +/// +public class LocalizationOptions { + /// + /// 初始化本地化配置 + /// + public LocalizationOptions() { + Cultures = new List(); + } + + /// + /// 语言文化 + /// + public IList Cultures { get; set; } + + /// + /// 缓存过期时间间隔,单位: 秒 ,默认值: 28800 + /// + public int Expiration { get; set; } = 28800; + + /// + /// 是否本地化Warning异常消息,默认值: false + /// + public bool IsLocalizeWarning { get; set; } +} \ No newline at end of file diff --git a/src/Util.Localization/LocalizedTypeAttribute.cs b/src/Util.Localization/LocalizedTypeAttribute.cs new file mode 100644 index 000000000..94d80f74a --- /dev/null +++ b/src/Util.Localization/LocalizedTypeAttribute.cs @@ -0,0 +1,38 @@ +namespace Util.Localization; + +/// +/// 本地化资源类型 +/// +public class LocalizedTypeAttribute : Attribute { + /// + /// 本地化资源类型 + /// + public string Type { get; } + + /// + /// 初始化本地化资源类型 + /// + /// 本地化资源类型 + public LocalizedTypeAttribute( string type ) { + Type = type; + } + + /// + /// 获取本地化资源类型,如果未设置则返回类型名 + /// + /// 类型 + public static string GetResourceType() { + return GetResourceType( typeof( T ) ); + } + + /// + /// 获取本地化资源类型,如果未设置则返回类型名 + /// + /// 类型 + public static string GetResourceType( Type type ) { + if ( type == null ) + return null; + var attribute = type.GetCustomAttribute(); + return attribute == null || attribute.Type.IsEmpty() ? type.Name : attribute.Type; + } +} \ No newline at end of file diff --git a/src/Util.Localization/NullStringLocalizer.cs b/src/Util.Localization/NullStringLocalizer.cs new file mode 100644 index 000000000..81e380924 --- /dev/null +++ b/src/Util.Localization/NullStringLocalizer.cs @@ -0,0 +1,25 @@ +namespace Util.Localization; + +/// +/// 本地化资源查找器 +/// +public class NullStringLocalizer : IStringLocalizer { + /// + /// 本地化资源查找器空实例 + /// + public static readonly IStringLocalizer Instance = new NullStringLocalizer(); + + /// + public LocalizedString this[string name] => new( name, name,true ); + + /// + public LocalizedString this[ string name, params object[] arguments ] { + get { + var value = string.Format( name, arguments ); + return new LocalizedString( value, value, true ); + } + } + + /// + public IEnumerable GetAllStrings( bool includeParentCultures ) => new List(); +} \ No newline at end of file diff --git a/src/Util.Localization/Store/ILocalizedManager.cs b/src/Util.Localization/Store/ILocalizedManager.cs new file mode 100644 index 000000000..331477a82 --- /dev/null +++ b/src/Util.Localization/Store/ILocalizedManager.cs @@ -0,0 +1,27 @@ +namespace Util.Localization.Store; + +/// +/// 本地化资源管理器 +/// +public interface ILocalizedManager { + /// + /// 加载全部本地化资源 + /// + void LoadAllResources(); + /// + /// 加载全部本地化资源 + /// + /// 区域文化,范例: zh-CN + void LoadAllResources( string culture ); + /// + /// 加载全部本地化资源 + /// + /// 区域文化,范例: zh-CN + /// 资源类型 + void LoadAllResources( string culture, string type ); + /// + /// 加载指定类型的本地化资源 + /// + /// 资源类型 + void LoadResourcesByType( string type ); +} \ No newline at end of file diff --git a/src/Util.Localization/Store/ILocalizedStore.cs b/src/Util.Localization/Store/ILocalizedStore.cs new file mode 100644 index 000000000..a0d8e4feb --- /dev/null +++ b/src/Util.Localization/Store/ILocalizedStore.cs @@ -0,0 +1,28 @@ +namespace Util.Localization.Store; + +/// +/// 本地化资源存储器 +/// +public interface ILocalizedStore { + /// + /// 获取本地化资源值,未查找到资源返回null + /// + /// 区域文化,范例: zh-CN + /// 资源类型 + /// 资源名称 + string GetValue( string culture, string type, string name ); + /// + /// 获取区域文化列表 + /// + IList GetCultures(); + /// + /// 获取资源类型列表 + /// + IList GetTypes(); + /// + /// 获取本地化资源集合 + /// + /// 区域文化,范例: zh-CN + /// 资源类型 + IDictionary GetResources( string culture, string type ); +} \ No newline at end of file diff --git a/src/Util.Localization/Store/LocalizedManager.cs b/src/Util.Localization/Store/LocalizedManager.cs new file mode 100644 index 000000000..58b708909 --- /dev/null +++ b/src/Util.Localization/Store/LocalizedManager.cs @@ -0,0 +1,93 @@ +using Util.Localization.Caching; + +namespace Util.Localization.Store; + +/// +/// 本地化资源管理器 +/// +public class LocalizedManager : ILocalizedManager { + /// + /// 本地化资源存储器 + /// + private readonly ILocalizedStore _store; + /// + /// 缓存 + /// + private readonly IMemoryCache _cache; + /// + /// 本地化配置 + /// + private readonly LocalizationOptions _options; + + /// + /// 初始化本地化资源管理器 + /// + /// 本地化资源存储器 + /// 缓存 + /// 本地化配置 + public LocalizedManager( ILocalizedStore store, IMemoryCache cache, IOptions options ) { + _store = store ?? throw new ArgumentNullException( nameof( store ) ); + _cache = cache ?? throw new ArgumentNullException( nameof( cache ) ); + _options = options?.Value ?? new LocalizationOptions(); + } + + /// + public virtual void LoadAllResources() { + var cultures = GetCultures(); + foreach ( var culture in cultures ) + LoadAllResources( culture ); + } + + /// + /// 获取区域文化列表 + /// + protected virtual List GetCultures() { + var result = new List { + Util.Helpers.Culture.GetCurrentUICultureName() + }; + var cultures = _store.GetCultures(); + if( cultures is { Count: > 0 } ) + result.AddRange( cultures ); + return result.Distinct().ToList(); + } + + /// + public virtual void LoadAllResources( string culture ) { + if ( culture.IsEmpty() ) + return; + var types = _store.GetTypes(); + if ( types == null || types.Count == 0 ) { + LoadAllResources( culture, null ); + return; + } + foreach ( var type in types ) + LoadAllResources( culture, type ); + } + + /// + public virtual void LoadAllResources( string culture, string type ) { + if ( culture.IsEmpty() ) + return; + var resources = _store.GetResources( culture, type ); + if ( resources == null ) + return; + foreach ( var resource in resources ) + LoadResourceCache( culture, type, resource.Key, resource.Value ); + } + + /// + /// 加载资源缓存 + /// + protected virtual void LoadResourceCache( string culture, string type, string name, string value ) { + var cacheKey = CacheHelper.GetCacheKey( culture, type, name ); + var localizedString = new LocalizedString( name, value, false, null ); + _cache.Set( cacheKey, localizedString, TimeSpan.FromSeconds( CacheHelper.GetExpiration( _options ) ) ); + } + + /// + public void LoadResourcesByType( string type ) { + var cultures = GetCultures(); + foreach ( var culture in cultures ) + LoadAllResources( culture, type ); + } +} \ No newline at end of file diff --git a/src/Util.Localization/Store/StoreStringLocalizer.cs b/src/Util.Localization/Store/StoreStringLocalizer.cs new file mode 100644 index 000000000..7f98ad2b8 --- /dev/null +++ b/src/Util.Localization/Store/StoreStringLocalizer.cs @@ -0,0 +1,104 @@ +using Util.Localization.Base; + +namespace Util.Localization.Store; + +/// +/// 数据存储本地化资源查找器 +/// +public class StoreStringLocalizer : StringLocalizerBase { + /// + /// 日志 + /// + private readonly ILogger _logger; + /// + /// 本地化资源存储器 + /// + private readonly ILocalizedStore _store; + + /// + /// 初始化数据存储本地化资源查找器 + /// + /// 日志 + /// 缓存 + /// 本地化资源存储器 + /// 资源类型 + /// 本地化配置 + public StoreStringLocalizer( ILogger logger, IMemoryCache cache, ILocalizedStore store, string type, IOptions options ) : base( cache, type, options ) { + _logger = logger ?? NullLogger.Instance; + _store = store ?? throw new ArgumentNullException( nameof( store ) ); + } + + /// + /// 获取本地化字符串结果 + /// + /// 资源名称 + /// 参数列表 + protected override LocalizedString GetResult( string name, params object[] arguments ) { + var culture = Util.Helpers.Culture.GetCurrentUICulture(); + var result = GetLocalizedStringByCache( culture, name ); + return FormatResult( result, name, arguments ); + } + + /// + /// 获取资源值 + /// + /// 区域文化 + /// 资源名称 + /// 资源类型 + protected override string GetValue( CultureInfo culture, string name, string type ) { + var result = _store.GetValue( culture.Name, type, name ); + _logger.Searched( name, type, culture ); + return result; + } + + /// + public override IEnumerable GetAllStrings( bool includeParentCultures ) { + var result = new List(); + var cultures = Helpers.Culture.GetCurrentUICultures(); + foreach ( var culture in cultures ) { + var resources = GetAllStrings( culture.Name ); + result.AddRange( resources ); + if ( includeParentCultures == false ) + return result; + } + return result; + } + + /// + /// 获取全部本地化字符串 + /// + /// 区域文化 + protected virtual IEnumerable GetAllStrings( string culture ) { + var result = new List(); + var types = _store.GetTypes(); + if ( types == null || types.Count == 0 ) { + result.AddRange( GetAllStrings( culture, null ) ); + return result; + } + foreach ( var type in types ) + result.AddRange( GetAllStrings( culture, type ) ); + return result; + } + + /// + /// 获取全部本地化字符串 + /// + /// 区域文化 + /// 资源类型 + protected virtual IEnumerable GetAllStrings( string culture, string type ) { + var resources = _store.GetResources( culture, type ); + if ( resources == null ) + return new List(); + return ToLocalizedStrings( resources ); + } + + /// + /// 转换为LocalizedString集合 + /// + protected virtual IEnumerable ToLocalizedStrings( IEnumerable> resources ) { + var result = new List(); + foreach ( var resource in resources ) + result.Add( new LocalizedString( resource.Key, resource.Value, false, null ) ); + return result; + } +} \ No newline at end of file diff --git a/src/Util.Localization/Store/StoreStringLocalizerFactory.cs b/src/Util.Localization/Store/StoreStringLocalizerFactory.cs new file mode 100644 index 000000000..b991efd77 --- /dev/null +++ b/src/Util.Localization/Store/StoreStringLocalizerFactory.cs @@ -0,0 +1,49 @@ +namespace Util.Localization.Store; + +/// +/// 数据存储本地化资源查找器工厂 +/// +public class StoreStringLocalizerFactory : IStringLocalizerFactory { + /// + /// 日志工厂 + /// + private readonly ILoggerFactory _loggerFactory; + /// + /// 本地化资源存储器 + /// + private readonly ILocalizedStore _store; + /// + /// 缓存 + /// + private readonly IMemoryCache _cache; + /// + /// 本地化配置 + /// + private readonly IOptions _options; + + /// + /// 初始化数据存储本地化资源查找器工厂 + /// + /// 本地化配置 + /// 日志工厂 + /// 本地化资源存储器 + /// 缓存 + public StoreStringLocalizerFactory( IOptions options, ILoggerFactory loggerFactory, ILocalizedStore store, IMemoryCache cache ) { + _options = options; + _loggerFactory = loggerFactory ?? throw new ArgumentNullException( nameof( loggerFactory ) ); + _store = store ?? throw new ArgumentNullException( nameof( store ) ); + _cache = cache ?? throw new ArgumentNullException( nameof( cache ) ); + } + + /// + public IStringLocalizer Create( Type resourceSource ) { + resourceSource.CheckNull( nameof( resourceSource ) ); + var type = LocalizedTypeAttribute.GetResourceType( resourceSource ); + return new StoreStringLocalizer( _loggerFactory.CreateLogger(), _cache, _store, type, _options ); + } + + /// + public IStringLocalizer Create( string baseName, string location ) { + return new StoreStringLocalizer( _loggerFactory.CreateLogger(), _cache, _store, baseName, _options ); + } +} \ No newline at end of file diff --git a/src/Util.Localization/Store/StoreStringLocalizerLoggerExtensions.cs b/src/Util.Localization/Store/StoreStringLocalizerLoggerExtensions.cs new file mode 100644 index 000000000..657358125 --- /dev/null +++ b/src/Util.Localization/Store/StoreStringLocalizerLoggerExtensions.cs @@ -0,0 +1,19 @@ +namespace Util.Localization.Store; + +/// +/// 数据存储本地化日志扩展 +/// +internal static class StoreStringLocalizerLoggerExtensions { + private static readonly Action _searched; + + static StoreStringLocalizerLoggerExtensions() { + _searched = LoggerMessage.Define( + LogLevel.Debug, + 1, + $"{nameof( StoreStringLocalizer )} 查找名为 '{{Key}}'的本地化资源, 资源类型为 '{{Type}}',区域文化为 '{{Culture}}'." ); + } + + public static void Searched( this ILogger logger, string key, string type, CultureInfo culture ) { + _searched( logger, key, type, culture, null ); + } +} \ No newline at end of file diff --git a/src/Util.Localization/StringLocalizer.cs b/src/Util.Localization/StringLocalizer.cs new file mode 100644 index 000000000..3626d2e44 --- /dev/null +++ b/src/Util.Localization/StringLocalizer.cs @@ -0,0 +1,29 @@ +namespace Util.Localization; + +/// +/// 本地化资源查找器 +/// +internal class StringLocalizer : IStringLocalizer { + /// + /// 本地化资源查找器 + /// + private readonly IStringLocalizer _localizer; + + /// + /// 初始化本地化资源查找器 + /// + /// 本地化资源查找器工厂 + public StringLocalizer( IStringLocalizerFactory factory ) { + var assemblyName = new AssemblyName( GetType().Assembly.FullName! ); + _localizer = factory.Create( string.Empty, assemblyName.FullName ); + } + + /// + public LocalizedString this[string name] => _localizer[name]; + + /// + public LocalizedString this[string name, params object[] arguments] => _localizer[name, arguments]; + + /// + public IEnumerable GetAllStrings( bool includeParentCultures ) => _localizer.GetAllStrings( includeParentCultures ); +} \ No newline at end of file diff --git a/src/Util.Localization/Usings.cs b/src/Util.Localization/Usings.cs new file mode 100644 index 000000000..4c24a5ba7 --- /dev/null +++ b/src/Util.Localization/Usings.cs @@ -0,0 +1,19 @@ +global using System; +global using System.Collections.Generic; +global using System.Linq; +global using System.IO; +global using System.Reflection; +global using System.Globalization; +global using System.Threading.Tasks; +global using System.Collections.Concurrent; +global using Microsoft.AspNetCore.Builder; +global using Microsoft.AspNetCore.Localization; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Logging.Abstractions; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Microsoft.Extensions.Localization; +global using Microsoft.Extensions.Caching.Memory; +global using Microsoft.Extensions.Options; +global using Util.Configs; \ No newline at end of file diff --git a/src/Util.Logging.Serilog.Exceptionless/03-Util.Logging.Serilog.Exceptionless.csproj b/src/Util.Logging.Serilog.Exceptionless/03-Util.Logging.Serilog.Exceptionless.csproj new file mode 100644 index 000000000..5b4bd2c19 --- /dev/null +++ b/src/Util.Logging.Serilog.Exceptionless/03-Util.Logging.Serilog.Exceptionless.csproj @@ -0,0 +1,39 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Logging.Serilog.Exceptionless + Util.Logging.Serilog + Util.Logging.Serilog.Exceptionless是Util应用框架集成封装Serilog日志操作类库,用于写入Exceptionless + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Logging.Serilog.Exceptionless.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Logging.Serilog.Exceptionless.xml + + + + + True + False + + + + + + + + + + + + + + + diff --git a/src/Util.Logging.Serilog.Exceptionless/AppBuilderExtensions.cs b/src/Util.Logging.Serilog.Exceptionless/AppBuilderExtensions.cs new file mode 100644 index 000000000..7087ef46d --- /dev/null +++ b/src/Util.Logging.Serilog.Exceptionless/AppBuilderExtensions.cs @@ -0,0 +1,131 @@ +using Util.Configs; +using SerilogLog = Serilog.Log; + +namespace Util.Logging.Serilog; + +/// +/// Exceptionless日志操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置Exceptionless日志操作 + /// + /// 应用生成器 + /// 是否清除默认设置的日志提供程序 + public static IAppBuilder AddExceptionless( this IAppBuilder builder, bool isClearProviders = false ) { + return builder.AddExceptionless( null, isClearProviders ); + } + + /// + /// 配置Exceptionless日志操作 + /// + /// 应用生成器 + /// 应用程序名称 + public static IAppBuilder AddExceptionless( this IAppBuilder builder, string appName ) { + return builder.AddExceptionless( null, appName ); + } + + /// + /// 配置Exceptionless日志操作 + /// + /// 应用生成器 + /// Exceptionless日志配置操作 + /// 是否清除默认设置的日志提供程序 + public static IAppBuilder AddExceptionless( this IAppBuilder builder, Action configAction, bool isClearProviders = false ) { + return builder.AddExceptionless( configAction, t => t.IsClearProviders = isClearProviders ); + } + + /// + /// 配置Exceptionless日志操作 + /// + /// 应用生成器 + /// Exceptionless日志配置操作 + /// 应用程序名称 + public static IAppBuilder AddExceptionless( this IAppBuilder builder, Action configAction, string appName ) { + return builder.AddExceptionless( configAction, t => t.Application = appName ); + } + + /// + /// 配置Exceptionless日志操作 + /// + /// 应用生成器 + /// Exceptionless日志配置操作 + /// 日志配置 + public static IAppBuilder AddExceptionless( this IAppBuilder builder, Action configAction, Action setupAction ) { + builder.CheckNull( nameof( builder ) ); + var options = new LogOptions(); + setupAction?.Invoke( options ); + builder.Host.ConfigureServices( ( context, services ) => { + services.AddSingleton(); + services.AddTransient( typeof( ILog<> ), typeof( Log<> ) ); + services.AddTransient( typeof( ILog ), t => t.GetService()?.CreateLog( "default" ) ?? NullLog.Instance ); + var configuration = context.Configuration; + services.AddLogging( loggingBuilder => { + if ( options.IsClearProviders ) + loggingBuilder.ClearProviders(); + ConfigExceptionless( configAction, configuration ); + SerilogLog.Logger = new LoggerConfiguration() + .Enrich.WithProperty( "Application", options.Application ) + .Enrich.FromLogContext() + .Enrich.WithLogLevel() + .Enrich.WithLogContext() + .WriteTo.Exceptionless() + .ReadFrom.Configuration( configuration ) + .ConfigLogLevel( configuration ) + .CreateLogger(); + loggingBuilder.AddSerilog(); + } ); + } ); + return builder; + } + + /// + /// 配置Exceptionless + /// + private static void ConfigExceptionless( Action configAction, IConfiguration configuration ) { + ExceptionlessClient.Default.Startup(); + if ( configAction != null ) { + configAction( ExceptionlessClient.Default.Configuration ); + ConfigLogLevel( configuration, ExceptionlessClient.Default.Configuration ); + return; + } + ExceptionlessClient.Default.Configuration.ReadFromConfiguration( configuration ); + ConfigLogLevel( configuration, ExceptionlessClient.Default.Configuration ); + } + + /// + /// 配置日志级别 + /// + private static void ConfigLogLevel( IConfiguration configuration, ExceptionlessConfiguration options ) { + var section = configuration.GetSection( "Logging:LogLevel" ); + foreach ( var item in section.GetChildren() ) { + if ( item.Key == "Default" ) { + options.Settings.Add( "@@log:*", GetLogLevel( item.Value ) ); + continue; + } + options.Settings.Add( $"@@log:{item.Key}*", GetLogLevel( item.Value ) ); + } + } + + /// + /// 获取日志级别 + /// + private static string GetLogLevel( string logLevel ) { + switch ( logLevel.ToUpperInvariant() ) { + case "TRACE": + return "Trace"; + case "DEBUG": + return "Debug"; + case "INFORMATION": + return "Info"; + case "ERROR": + return "Error"; + case "CRITICAL": + return "Fatal"; + case "NONE": + return "Off"; + default: + return "Warn"; + } + } +} \ No newline at end of file diff --git a/src/Util.Logging.Serilog.Exceptionless/Usings.cs b/src/Util.Logging.Serilog.Exceptionless/Usings.cs new file mode 100644 index 000000000..f5414d916 --- /dev/null +++ b/src/Util.Logging.Serilog.Exceptionless/Usings.cs @@ -0,0 +1,6 @@ +global using System; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Logging; +global using Serilog; +global using Exceptionless; \ No newline at end of file diff --git a/src/Util.Logging.Serilog/02-Util.Logging.Serilog.csproj b/src/Util.Logging.Serilog/02-Util.Logging.Serilog.csproj new file mode 100644 index 000000000..7ea26da67 --- /dev/null +++ b/src/Util.Logging.Serilog/02-Util.Logging.Serilog.csproj @@ -0,0 +1,41 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Logging.Serilog + Util.Logging.Serilog + Util.Logging.Serilog是Util应用框架集成封装Serilog日志框架的核心类库,它包含了写入文件和Seq + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Logging.Serilog.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Logging.Serilog.xml + + + + + True + False + + + + + + + + + + + + + + + + + diff --git a/src/Util.Logging.Serilog/AppBuilderExtensions.cs b/src/Util.Logging.Serilog/AppBuilderExtensions.cs new file mode 100644 index 000000000..b416cbf3d --- /dev/null +++ b/src/Util.Logging.Serilog/AppBuilderExtensions.cs @@ -0,0 +1,70 @@ +using Util.Configs; +using SerilogLog = Serilog.Log; + +namespace Util.Logging.Serilog; + +/// +/// Serilog日志操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置Serilog日志操作 + /// + /// 应用生成器 + public static IAppBuilder AddSerilog( this IAppBuilder builder ) { + return builder.AddSerilog( false ); + } + + /// + /// 配置Serilog日志操作 + /// + /// 应用生成器 + /// 是否清除默认设置的日志提供程序 + public static IAppBuilder AddSerilog( this IAppBuilder builder, bool isClearProviders ) { + return builder.AddSerilog( options => { + options.IsClearProviders = isClearProviders; + } ); + } + + /// + /// 配置Serilog日志操作 + /// + /// 应用生成器 + /// 应用程序名称 + public static IAppBuilder AddSerilog( this IAppBuilder builder, string appName ) { + return builder.AddSerilog( options => { + options.Application = appName; + } ); + } + + /// + /// 配置Serilog日志操作 + /// + /// 应用生成器 + /// 日志配置操作 + public static IAppBuilder AddSerilog( this IAppBuilder builder, Action setupAction ) { + builder.CheckNull( nameof( builder ) ); + var options = new LogOptions(); + setupAction?.Invoke( options ); + builder.Host.ConfigureServices( ( context, services ) => { + services.AddSingleton(); + services.AddTransient( typeof( ILog<> ), typeof( Log<> ) ); + services.AddTransient( typeof( ILog ), t => t.GetService()?.CreateLog( "default" ) ?? NullLog.Instance ); + var configuration = context.Configuration; + services.AddLogging( loggingBuilder => { + if ( options.IsClearProviders ) + loggingBuilder.ClearProviders(); + SerilogLog.Logger = new LoggerConfiguration() + .Enrich.WithProperty( "Application", options.Application ) + .Enrich.FromLogContext() + .Enrich.WithLogLevel() + .Enrich.WithLogContext() + .ReadFrom.Configuration( configuration ) + .ConfigLogLevel( configuration ) + .CreateLogger(); + loggingBuilder.AddSerilog(); + } ); + } ); + return builder; + } +} \ No newline at end of file diff --git a/src/Util.Logging.Serilog/Enrichers/LogContextEnricher.cs b/src/Util.Logging.Serilog/Enrichers/LogContextEnricher.cs new file mode 100644 index 000000000..03882eb1f --- /dev/null +++ b/src/Util.Logging.Serilog/Enrichers/LogContextEnricher.cs @@ -0,0 +1,106 @@ +using Util.Helpers; + +namespace Util.Logging.Serilog.Enrichers; + +/// +/// 日志上下文扩展属性 +/// +public class LogContextEnricher : ILogEventEnricher { + /// + /// 扩展属性 + /// + /// 日志事件 + /// 日志事件属性工厂 + public void Enrich( LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { + var accessor = Ioc.Create(); + if ( accessor == null ) + return; + var context = accessor.Context; + if ( context == null ) + return; + if ( logEvent == null ) + return; + if ( propertyFactory == null ) + return; + RemoveProperties( logEvent ); + AddDuration( context,logEvent, propertyFactory ); + AddTraceId( context, logEvent, propertyFactory ); + AddUserId( context, logEvent, propertyFactory ); + AddApplication( context, logEvent, propertyFactory ); + AddEnvironment( context, logEvent, propertyFactory ); + AddData( context, logEvent, propertyFactory ); + } + + /// + /// 移除默认设置的部分属性 + /// + private void RemoveProperties( LogEvent logEvent ) { + logEvent.RemovePropertyIfPresent( "ActionId" ); + logEvent.RemovePropertyIfPresent( "ActionName" ); + logEvent.RemovePropertyIfPresent( "RequestId" ); + logEvent.RemovePropertyIfPresent( "RequestPath" ); + logEvent.RemovePropertyIfPresent( "ConnectionId" ); + } + + /// + /// 添加执行持续时间 + /// + private void AddDuration( LogContext context, LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { + if ( context?.Stopwatch == null ) + return; + var property = propertyFactory.CreateProperty( "Duration", context.Stopwatch.Elapsed.Description() ); + logEvent.AddOrUpdateProperty( property ); + } + + /// + /// 添加跟踪号 + /// + private void AddTraceId( LogContext context, LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { + if ( context == null || context.TraceId.IsEmpty() ) + return; + var property = propertyFactory.CreateProperty( "TraceId", context.TraceId ); + logEvent.AddOrUpdateProperty( property ); + } + + /// + /// 添加用户标识 + /// + private void AddUserId( LogContext context, LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { + if ( context == null || context.UserId.IsEmpty() ) + return; + var property = propertyFactory.CreateProperty( "UserId", context.UserId ); + logEvent.AddOrUpdateProperty( property ); + } + + /// + /// 添加应用程序 + /// + private void AddApplication( LogContext context, LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { + if ( context == null || context.Application.IsEmpty() ) + return; + var property = propertyFactory.CreateProperty( "Application", context.Application ); + logEvent.AddOrUpdateProperty( property ); + } + + /// + /// 添加执行环境 + /// + private void AddEnvironment( LogContext context, LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { + if ( context == null || context.Environment.IsEmpty() ) + return; + var property = propertyFactory.CreateProperty( "Environment", context.Environment ); + logEvent.AddOrUpdateProperty( property ); + } + + /// + /// 添加扩展数据 + /// + private void AddData( LogContext context, LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { + if ( context?.Data == null || context.Data.Count == 0 ) + return; + foreach ( var item in context.Data ) { + var property = propertyFactory.CreateProperty( item.Key, item.Value ); + logEvent.AddOrUpdateProperty( property ); + } + } +} \ No newline at end of file diff --git a/src/Util.Logging.Serilog/Enrichers/LogLevelEnricher.cs b/src/Util.Logging.Serilog/Enrichers/LogLevelEnricher.cs new file mode 100644 index 000000000..63c22eeed --- /dev/null +++ b/src/Util.Logging.Serilog/Enrichers/LogLevelEnricher.cs @@ -0,0 +1,38 @@ +namespace Util.Logging.Serilog.Enrichers; + +/// +/// 日志级别扩展属性 - 用于显示标准日志级别 +/// +public class LogLevelEnricher : ILogEventEnricher { + /// + /// 扩展属性 + /// + /// 日志事件 + /// 日志事件属性工厂 + public void Enrich( LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) { + var property = propertyFactory.CreateProperty( "LogLevel", GetLogLevel( logEvent.Level ) ); + logEvent.AddOrUpdateProperty( property ); + } + + /// + /// 获取日志级别 + /// + private string GetLogLevel( LogEventLevel logLevel ) { + switch ( logLevel ) { + case LogEventLevel.Verbose: + return "Trace"; + case LogEventLevel.Debug: + return "Debug"; + case LogEventLevel.Information: + return "Information"; + case LogEventLevel.Warning: + return "Warning"; + case LogEventLevel.Error: + return "Error"; + case LogEventLevel.Fatal: + return "Critical"; + default: + return null; + } + } +} \ No newline at end of file diff --git a/src/Util.Logging.Serilog/LogOptions.cs b/src/Util.Logging.Serilog/LogOptions.cs new file mode 100644 index 000000000..b06e1e80b --- /dev/null +++ b/src/Util.Logging.Serilog/LogOptions.cs @@ -0,0 +1,15 @@ +namespace Util.Logging.Serilog; + +/// +/// 日志配置 +/// +public class LogOptions { + /// + /// 是否清除默认设置的日志提供程序 + /// + public bool IsClearProviders { get; set; } + /// + /// 应用程序名称 + /// + public string Application { get; set; } +} \ No newline at end of file diff --git a/src/Util.Logging.Serilog/LoggerConfigurationExtensions.cs b/src/Util.Logging.Serilog/LoggerConfigurationExtensions.cs new file mode 100644 index 000000000..2fe21e3d0 --- /dev/null +++ b/src/Util.Logging.Serilog/LoggerConfigurationExtensions.cs @@ -0,0 +1,47 @@ +namespace Util.Logging.Serilog; + +/// +/// Serilog日志配置操作扩展 +/// +public static class LoggerConfigurationExtensions { + /// + /// 配置Serilog日志级别 + /// + /// Serilog日志配置 + /// 配置 + public static LoggerConfiguration ConfigLogLevel( this LoggerConfiguration source, IConfiguration configuration ) { + source.CheckNull( nameof( source ) ); + configuration.CheckNull( nameof( configuration ) ); + var section = configuration.GetSection( "Logging:LogLevel" ); + foreach ( var item in section.GetChildren() ) { + if ( item.Key == "Default" ) { + source.MinimumLevel.ControlledBy( new LoggingLevelSwitch( GetLogLevel( item.Value ) ) ); + continue; + } + source.MinimumLevel.Override( item.Key, GetLogLevel( item.Value ) ); + } + return source; + } + + /// + /// 获取日志级别 + /// + private static LogEventLevel GetLogLevel( string logLevel ) { + switch ( logLevel.ToUpperInvariant() ) { + case "TRACE": + return LogEventLevel.Verbose; + case "DEBUG": + return LogEventLevel.Debug; + case "INFORMATION": + return LogEventLevel.Information; + case "ERROR": + return LogEventLevel.Error; + case "CRITICAL": + return LogEventLevel.Fatal; + case "NONE": + return LogEventLevel.Fatal; + default: + return LogEventLevel.Warning; + } + } +} \ No newline at end of file diff --git a/src/Util.Logging.Serilog/LoggerEnrichmentConfigurationExtensions.cs b/src/Util.Logging.Serilog/LoggerEnrichmentConfigurationExtensions.cs new file mode 100644 index 000000000..874851b47 --- /dev/null +++ b/src/Util.Logging.Serilog/LoggerEnrichmentConfigurationExtensions.cs @@ -0,0 +1,26 @@ +using Util.Logging.Serilog.Enrichers; + +namespace Util.Logging.Serilog; + +/// +/// Serilog扩展属性配置扩展 +/// +public static class LoggerEnrichmentConfigurationExtensions { + /// + /// 添加日志上下文扩展属性 + /// + /// 日志扩展配置 + public static LoggerConfiguration WithLogContext( this LoggerEnrichmentConfiguration source ) { + source.CheckNull( nameof( source ) ); + return source.With(); + } + + /// + /// 添加日志级别扩展属性 + /// + /// 日志扩展配置 + public static LoggerConfiguration WithLogLevel( this LoggerEnrichmentConfiguration source ) { + source.CheckNull( nameof( source ) ); + return source.With(); + } +} \ No newline at end of file diff --git a/src/Util.Logging.Serilog/Usings.cs b/src/Util.Logging.Serilog/Usings.cs new file mode 100644 index 000000000..e3d303199 --- /dev/null +++ b/src/Util.Logging.Serilog/Usings.cs @@ -0,0 +1,8 @@ +global using System; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Logging; +global using Serilog; +global using Serilog.Core; +global using Serilog.Events; +global using Serilog.Configuration; \ No newline at end of file diff --git a/src/Util.Logging/01-Util.Logging.csproj b/src/Util.Logging/01-Util.Logging.csproj new file mode 100644 index 000000000..0703271a3 --- /dev/null +++ b/src/Util.Logging/01-Util.Logging.csproj @@ -0,0 +1,38 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Logging + Util.Logging + Util.Logging是Util应用框架的日志操作核心类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Logging.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Logging.xml + + + + + True + False + + + + + + + + + + + + + + diff --git a/src/Util.Logging/ILog.cs b/src/Util.Logging/ILog.cs new file mode 100644 index 000000000..e236c0032 --- /dev/null +++ b/src/Util.Logging/ILog.cs @@ -0,0 +1,76 @@ +namespace Util.Logging; + +/// +/// 日志操作 +/// +/// 日志类别 +public interface ILog : ILog { +} + +/// +/// 日志操作 +/// +public interface ILog { + /// + /// 设置日志事件标识 + /// + /// 日志事件标识 + ILog EventId( EventId eventId ); + /// + /// 设置异常 + /// + /// 异常 + ILog Exception( Exception exception ); + /// + /// 设置自定义扩展属性 + /// + /// 属性名 + /// 属性值 + ILog Property( string propertyName, string propertyValue ); + /// + /// 设置日志状态对象 + /// + /// 状态对象 + ILog State( object state ); + /// + /// 设置日志消息 + /// + /// 日志消息 + /// 日志消息参数 + ILog Message( string message, params object[] args ); + /// + /// 是否启用 + /// + /// 日志级别 + bool IsEnabled( LogLevel logLevel ); + /// + /// 开启日志范围 + /// + /// 日志状态类型 + /// 日志状态 + IDisposable BeginScope( TState state ); + /// + /// 写跟踪日志 + /// + ILog LogTrace(); + /// + /// 写调试日志 + /// + ILog LogDebug(); + /// + /// 写信息日志 + /// + ILog LogInformation(); + /// + /// 写警告日志 + /// + ILog LogWarning(); + /// + /// 写错误日志 + /// + ILog LogError(); + /// + /// 写致命日志 + /// + ILog LogCritical(); +} \ No newline at end of file diff --git a/src/Util.Logging/ILogContextAccessor.cs b/src/Util.Logging/ILogContextAccessor.cs new file mode 100644 index 000000000..4b6d4cc25 --- /dev/null +++ b/src/Util.Logging/ILogContextAccessor.cs @@ -0,0 +1,11 @@ +namespace Util.Logging; + +/// +/// 日志上下文访问器 +/// +public interface ILogContextAccessor { + /// + /// 日志上下文 + /// + LogContext Context { get; set; } +} \ No newline at end of file diff --git a/src/Util.Logging/ILogExtensions.cs b/src/Util.Logging/ILogExtensions.cs new file mode 100644 index 000000000..965d36513 --- /dev/null +++ b/src/Util.Logging/ILogExtensions.cs @@ -0,0 +1,71 @@ +namespace Util.Logging; + +/// +/// 日志操作扩展 +/// +public static class ILogExtensions { + /// + /// 添加消息 + /// + /// 配置项 + /// 消息 + /// 日志消息参数 + public static ILog Append( this ILog log,string message, params object[] args ) { + log.CheckNull( nameof( log ) ); + log.Message( message, args ); + return log; + } + + /// + /// 当条件为true添加消息 + /// + /// 配置项 + /// 消息 + /// 条件,值为true则添加消息 + /// 日志消息参数 + public static ILog AppendIf( this ILog log, string message,bool condition, params object[] args ) { + log.CheckNull( nameof( log ) ); + if ( condition ) + log.Message( message, args ); + return log; + } + + /// + /// 添加消息并换行 + /// + /// 配置项 + /// 消息 + /// 日志消息参数 + public static ILog AppendLine( this ILog log, string message, params object[] args ) { + log.CheckNull( nameof( log ) ); + log.Message( message, args ); + log.Message( Environment.NewLine ); + return log; + } + + /// + /// 当条件为true添加消息并换行 + /// + /// 配置项 + /// 消息 + /// 条件,值为true则添加消息 + /// 日志消息参数 + public static ILog AppendLineIf( this ILog log, string message, bool condition, params object[] args ) { + log.CheckNull( nameof( log ) ); + if ( condition ) { + log.Message( message, args ); + log.Message( Environment.NewLine ); + } + return log; + } + + /// + /// 消息换行 + /// + /// 配置项 + public static ILog Line( this ILog log ) { + log.CheckNull( nameof(log) ); + log.Message( Environment.NewLine ); + return log; + } +} \ No newline at end of file diff --git a/src/Util.Logging/ILogFactory.cs b/src/Util.Logging/ILogFactory.cs new file mode 100644 index 000000000..35b0e85da --- /dev/null +++ b/src/Util.Logging/ILogFactory.cs @@ -0,0 +1,17 @@ +namespace Util.Logging; + +/// +/// 日志操作工厂 +/// +public interface ILogFactory { + /// + /// 创建日志操作 + /// + /// 日志类别 + ILog CreateLog( string categoryName ); + /// + /// 创建日志操作 + /// + /// 日志类别类型 + ILog CreateLog( Type type ); +} \ No newline at end of file diff --git a/src/Util.Logging/ILoggerWrapper.cs b/src/Util.Logging/ILoggerWrapper.cs new file mode 100644 index 000000000..42534ce05 --- /dev/null +++ b/src/Util.Logging/ILoggerWrapper.cs @@ -0,0 +1,74 @@ +namespace Util.Logging; + +/// +/// 日志记录包装器 +/// +public interface ILoggerWrapper { + /// + /// 是否启用 + /// + /// 日志级别 + bool IsEnabled( LogLevel logLevel ); + /// 写日志 + /// 日志级别 + /// 日志事件标识 + /// 日志状态对象 + /// 异常 + /// 消息格式化操作 + /// 日志状态类型 + void Log( LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter ); + /// + /// 创建日志范围 + /// + /// 日志状态 + /// 日志状态类型 + IDisposable BeginScope( TState state ); + /// + /// 写跟踪日志 + /// + /// 日志事件标识 + /// 异常 + /// 日志消息 + /// 日志消息参数 + void LogTrace( EventId eventId, Exception exception, string message, params object[] args ); + /// + /// 写调试日志 + /// + /// 日志事件标识 + /// 异常 + /// 日志消息 + /// 日志消息参数 + void LogDebug( EventId eventId, Exception exception, string message, params object[] args ); + /// + /// 写信息日志 + /// + /// 日志事件标识 + /// 异常 + /// 日志消息 + /// 日志消息参数 + void LogInformation( EventId eventId, Exception exception, string message, params object[] args ); + /// + /// 写警告日志 + /// + /// 日志事件标识 + /// 异常 + /// 日志消息 + /// 日志消息参数 + void LogWarning( EventId eventId, Exception exception, string message, params object[] args ); + /// + /// 写错误日志 + /// + /// 日志事件标识 + /// 异常 + /// 日志消息 + /// 日志消息参数 + void LogError( EventId eventId, Exception exception, string message, params object[] args ); + /// + /// 写致命日志 + /// + /// 日志事件标识 + /// 异常 + /// 日志消息 + /// 日志消息参数 + void LogCritical( EventId eventId, Exception exception, string message, params object[] args ); +} \ No newline at end of file diff --git a/src/Util.Logging/Log.cs b/src/Util.Logging/Log.cs new file mode 100644 index 000000000..984366f79 --- /dev/null +++ b/src/Util.Logging/Log.cs @@ -0,0 +1,375 @@ +namespace Util.Logging; + +/// +/// 日志操作 +/// +public class Log : ILog { + + #region 构造方法 + + /// + /// 初始化日志操作 + /// + /// 日志记录包装器 + public Log( ILoggerWrapper logger ) { + Logger = logger ?? throw new ArgumentNullException( nameof( logger ) ); + LogProperties = new Dictionary(); + LogMessage = new StringBuilder(); + LogMessageArgs = new List(); + } + + #endregion + + #region Null(空日志操作实例) + + /// + /// 空日志操作实例 + /// + public static ILog Null = NullLog.Instance; + + #endregion + + #region 属性 + + /// + /// 日志记录包装器 + /// + protected ILoggerWrapper Logger { get; } + /// + /// 日志级别 + /// + protected LogLevel LogLevel { get; set; } + /// + /// 日志事件标识 + /// + protected EventId LogEventId { get; set; } + /// + /// 日志异常 + /// + protected Exception LogException { get; set; } + /// + /// 日志属性集合 + /// + protected IDictionary LogProperties { get; set; } + /// + /// 日志状态 + /// + protected object LogState { get; set; } + /// + /// 日志消息 + /// + protected StringBuilder LogMessage { get; } + /// + /// 日志消息参数 + /// + protected List LogMessageArgs { get; } + + #endregion + + #region EventId(设置日志事件标识) + + /// + public virtual ILog EventId( EventId eventId ) { + LogEventId = eventId; + return this; + } + + #endregion + + #region Exception(设置异常) + + /// + public virtual ILog Exception( Exception exception ) { + LogException = exception; + return this; + } + + #endregion + + #region Property(设置自定义扩展属性) + + /// + public virtual ILog Property( string propertyName, string propertyValue ) { + if( propertyName.IsEmpty() ) + return this; + if( LogProperties.ContainsKey( propertyName ) ) { + LogProperties[propertyName] += propertyValue; + return this; + } + LogProperties.Add( propertyName, propertyValue ); + return this; + } + + #endregion + + #region State(设置日志状态对象) + + /// + public virtual ILog State( object state ) { + LogState = state; + return this; + } + + #endregion + + #region Message(设置日志消息) + + /// + public virtual ILog Message( string message, params object[] args ) { + LogMessage.Append( message ); + LogMessageArgs.AddRange( args ); + return this; + } + + #endregion + + #region IsEnabled(是否启用) + + /// + public virtual bool IsEnabled( LogLevel logLevel ) { + return Logger.IsEnabled( logLevel ); + } + + #endregion + + #region BeginScope(开启日志范围) + + /// + public virtual IDisposable BeginScope( TState state ) { + return Logger.BeginScope( state ); + } + + #endregion + + #region LogTrace(写跟踪日志) + + /// + public virtual ILog LogTrace() { + try { + Init(); + if ( LogMessage.Length > 0 ) { + Logger.LogTrace( LogEventId, LogException, GetMessage(), GetMessageArgs() ); + return this; + } + LogLevel = LogLevel.Trace; + return WriteLog(); + } + finally{ + Clear(); + } + } + + #endregion + + #region LogDebug(写调试日志) + + /// + public virtual ILog LogDebug() { + try { + Init(); + if ( LogMessage.Length > 0 ) { + Logger.LogDebug( LogEventId, LogException, GetMessage(), GetMessageArgs() ); + return this; + } + LogLevel = LogLevel.Debug; + return WriteLog(); + } + finally { + Clear(); + } + } + + #endregion + + #region LogInformation(写信息日志) + + /// + public virtual ILog LogInformation() { + try { + Init(); + if ( LogMessage.Length > 0 ) { + Logger.LogInformation( LogEventId, LogException, GetMessage(), GetMessageArgs() ); + return this; + } + LogLevel = LogLevel.Information; + return WriteLog(); + } + finally { + Clear(); + } + } + + #endregion + + #region LogWarning(写警告日志) + + /// + public virtual ILog LogWarning() { + try { + Init(); + if ( LogMessage.Length > 0 ) { + Logger.LogWarning( LogEventId, LogException, GetMessage(), GetMessageArgs() ); + return this; + } + LogLevel = LogLevel.Warning; + return WriteLog(); + } + finally { + Clear(); + } + } + + #endregion + + #region LogError(写错误日志) + + /// + public virtual ILog LogError() { + try { + Init(); + if ( LogMessage.Length > 0 ) { + Logger.LogError( LogEventId, LogException, GetMessage(), GetMessageArgs() ); + return this; + } + LogLevel = LogLevel.Error; + return WriteLog(); + } + finally { + Clear(); + } + } + + #endregion + + #region LogCritical(写致命日志) + + /// + public virtual ILog LogCritical() { + try { + Init(); + if ( LogMessage.Length > 0 ) { + Logger.LogCritical( LogEventId, LogException, GetMessage(), GetMessageArgs() ); + return this; + } + LogLevel = LogLevel.Critical; + return WriteLog(); + } + finally { + Clear(); + } + } + + #endregion + + #region 辅助方法 + + /// + /// 初始化 + /// + protected virtual void Init() { + ConvertStateToContent(); + } + + /// + /// 将状态对象转换到日志属性集合中 + /// + protected virtual void ConvertStateToContent() { + if( LogState == null ) + return; + var state = Util.Helpers.Convert.ToDictionary( LogState ); + foreach ( var item in state ) { + if ( item.Value.SafeString().IsEmpty() ) + continue; + LogProperties.Add( item ); + } + } + + /// + /// 获取日志消息 + /// + protected virtual string GetMessage() { + if( LogProperties.Count == 0 ) + return LogMessage.ToString(); + var result = new StringBuilder(); + result.Append( "[" ); + foreach ( var item in LogProperties ) { + result.Append( item.Key ); + result.Append( ":{" ); + result.Append( item.Key ); + result.Append( "}," ); + } + result.RemoveEnd( "," ); + result.Append( "]" ); + result.Append( LogMessage ); + return result.ToString(); + } + + /// + /// 获取日志消息参数 + /// + protected virtual object[] GetMessageArgs() { + if( LogProperties.Count == 0 ) + return LogMessageArgs.ToArray(); + var result = new List(); + result.AddRange( LogProperties.Values ); + result.AddRange( LogMessageArgs ); + return result.ToArray(); + } + + /// + /// 写日志 + /// + protected virtual ILog WriteLog() { + Logger.Log( LogLevel, LogEventId, GetContent(), LogException, GetFormatMessage ); + return this; + } + + /// + /// 获取日志内容 + /// + protected IDictionary GetContent() { + if( LogProperties.Count == 0 ) + return null; + return LogProperties; + } + + /// + /// 获取格式化消息 + /// + protected virtual string GetFormatMessage( IDictionary content, Exception exception ) { + if( exception != null ) + return exception.Message; + if( content == null ) + return null; + return GetFormatMessage( content ); + } + + /// + /// 获取格式化消息 + /// + protected virtual string GetFormatMessage( IDictionary content ) { + var result = new StringBuilder(); + foreach( var item in content ) { + result.Append( item.Key ); + result.Append( ":" ); + result.Append( item.Value.SafeString() ); + result.Append( "," ); + } + return result.ToString().RemoveEnd( "," ); + } + + /// + /// 清理状态 + /// + protected virtual void Clear() { + LogLevel = LogLevel.None; + LogEventId = 0; + LogException = null; + LogState = null; + LogProperties = new Dictionary(); + LogMessage.Clear(); + LogMessageArgs.Clear(); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Logging/LogContext.cs b/src/Util.Logging/LogContext.cs new file mode 100644 index 000000000..d43eeb91b --- /dev/null +++ b/src/Util.Logging/LogContext.cs @@ -0,0 +1,38 @@ +namespace Util.Logging; + +/// +/// 日志上下文 +/// +public class LogContext { + /// + /// 初始化日志上下文 + /// + public LogContext() { + Data = new Dictionary(); + } + + /// + /// 计时器 + /// + public Stopwatch Stopwatch { get; set; } + /// + /// 跟踪标识 + /// + public string TraceId { get; set; } + /// + /// 用户标识 + /// + public string UserId { get; set; } + /// + /// 应用程序 + /// + public string Application { get; set; } + /// + /// 执行环境 + /// + public string Environment { get; set; } + /// + /// 扩展数据 + /// + public IDictionary Data { get; } +} \ No newline at end of file diff --git a/src/Util.Logging/LogFactory.cs b/src/Util.Logging/LogFactory.cs new file mode 100644 index 000000000..035208840 --- /dev/null +++ b/src/Util.Logging/LogFactory.cs @@ -0,0 +1,40 @@ +namespace Util.Logging; + +/// +/// 日志操作工厂 +/// +public class LogFactory : ILogFactory { + /// + /// 日志记录器工厂 + /// + private readonly ILoggerFactory _loggerFactory; + + /// + /// 初始化日志操作工厂 + /// + /// 日志记录器工厂 + /// + public LogFactory( ILoggerFactory factory ) { + _loggerFactory = factory ?? throw new ArgumentNullException(nameof(factory)); + } + + /// + /// 创建日志操作 + /// + /// 日志类别 + public ILog CreateLog( string categoryName ) { + var logger = _loggerFactory.CreateLogger( categoryName ); + var wrapper = new LoggerWrapper( logger ); + return new Log( wrapper ); + } + + /// + /// 创建日志操作 + /// + /// 日志类别类型 + public ILog CreateLog( Type type ) { + var logger = _loggerFactory.CreateLogger( type ); + var wrapper = new LoggerWrapper( logger ); + return new Log( wrapper ); + } +} \ No newline at end of file diff --git a/src/Util.Logging/LogT.cs b/src/Util.Logging/LogT.cs new file mode 100644 index 000000000..b8ecd7d75 --- /dev/null +++ b/src/Util.Logging/LogT.cs @@ -0,0 +1,91 @@ +namespace Util.Logging; + +/// +/// 日志操作 +/// +/// 日志类别 +public class Log : ILog { + /// + /// 日志操作 + /// + private readonly ILog _log; + + /// + /// 初始化日志操作 + /// + /// 日志操作工厂 + public Log( ILogFactory factory ) { + factory.CheckNull( nameof(factory) ); + _log = factory.CreateLog(typeof( TCategoryName ) ); + } + + /// + /// 空日志操作实例 + /// + public static ILog Null = NullLog.Instance; + + /// + public ILog EventId( EventId eventId ) { + return _log.EventId( eventId ); + } + + /// + public ILog Exception( Exception exception ) { + return _log.Exception( exception ); + } + + /// + public ILog Property( string propertyName, string propertyValue ) { + return _log.Property( propertyName, propertyValue ); + } + + /// + public ILog State( object state ) { + return _log.State( state ); + } + + /// + public ILog Message( string message, params object[] args ) { + return _log.Message( message, args ); + } + + /// + public bool IsEnabled( LogLevel logLevel ) { + return _log.IsEnabled( logLevel ); + } + + /// + public IDisposable BeginScope( TState state ) { + return _log.BeginScope( state ); + } + + /// + public ILog LogTrace() { + return _log.LogTrace(); + } + + /// + public virtual ILog LogDebug() { + return _log.LogDebug(); + } + + /// + public virtual ILog LogInformation() { + return _log.LogInformation(); + } + + /// + public virtual ILog LogWarning() { + return _log.LogWarning(); + } + + /// + public virtual ILog LogError() { + return _log.LogError(); + } + + /// + public virtual ILog LogCritical() { + return _log.LogCritical(); + } +} \ No newline at end of file diff --git a/src/Util.Logging/LoggerWrapper.cs b/src/Util.Logging/LoggerWrapper.cs new file mode 100644 index 000000000..2ff72c1d4 --- /dev/null +++ b/src/Util.Logging/LoggerWrapper.cs @@ -0,0 +1,64 @@ +namespace Util.Logging; + +/// +/// 日志记录包装器 +/// +public class LoggerWrapper : ILoggerWrapper { + /// + /// 初始化日志记录包装器 + /// + /// 日志记录器 + public LoggerWrapper( ILogger logger ) { + Logger = logger ?? throw new ArgumentNullException( nameof( logger ) ); + } + + /// + /// 日志记录包装器 + /// + protected ILogger Logger { get; } + + /// + public virtual bool IsEnabled( LogLevel logLevel ) { + return Logger.IsEnabled( logLevel ); + } + + /// + public virtual void Log( LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter ) { + Logger.Log( logLevel, eventId, state, exception, formatter ); + } + + /// + public virtual IDisposable BeginScope( TState state ) { + return Logger.BeginScope( state ); + } + + /// + public void LogTrace( EventId eventId, Exception exception, string message, params object[] args ) { + Logger.LogTrace( eventId,exception,message,args ); + } + + /// + public void LogDebug( EventId eventId, Exception exception, string message, params object[] args ) { + Logger.LogDebug( eventId, exception, message, args ); + } + + /// + public void LogInformation( EventId eventId, Exception exception, string message, params object[] args ) { + Logger.LogInformation( eventId, exception, message, args ); + } + + /// + public void LogWarning( EventId eventId, Exception exception, string message, params object[] args ) { + Logger.LogWarning( eventId, exception, message, args ); + } + + /// + public void LogError( EventId eventId, Exception exception, string message, params object[] args ) { + Logger.LogError( eventId, exception, message, args ); + } + + /// + public void LogCritical( EventId eventId, Exception exception, string message, params object[] args ) { + Logger.LogCritical( eventId, exception, message, args ); + } +} \ No newline at end of file diff --git a/src/Util.Logging/NullLog.cs b/src/Util.Logging/NullLog.cs new file mode 100644 index 000000000..10d26a989 --- /dev/null +++ b/src/Util.Logging/NullLog.cs @@ -0,0 +1,152 @@ +namespace Util.Logging; + +/// +/// 空日志操作 +/// +/// 日志类别 +public class NullLog : ILog { + /// + /// 空日志操作实例 + /// + public static readonly ILog Instance = new NullLog(); + + /// + public ILog EventId( EventId eventId ) { + return this; + } + + /// + public ILog Exception( Exception exception ) { + return this; + } + + /// + public ILog Property( string propertyName, string propertyValue ) { + return this; + } + + /// + public ILog State( object state ) { + return this; + } + + /// + public ILog Message( string message, params object[] args ) { + return this; + } + + /// + public bool IsEnabled( LogLevel logLevel ) { + return false; + } + + /// + public IDisposable BeginScope( TState state ) { + return new DisposeAction( () => { } ); + } + + /// + public ILog LogTrace() { + return this; + } + + /// + public ILog LogDebug() { + return this; + } + + /// + public ILog LogInformation() { + return this; + } + + /// + public ILog LogWarning() { + return this; + } + + /// + public ILog LogError() { + return this; + } + + /// + public ILog LogCritical() { + return this; + } +} + +/// +/// 空日志操作 +/// +public class NullLog : ILog { + /// + /// 空日志操作实例 + /// + public static readonly ILog Instance = new NullLog(); + + /// + public ILog EventId( EventId eventId ) { + return this; + } + + /// + public ILog Exception( Exception exception ) { + return this; + } + + /// + public ILog Property( string propertyName, string propertyValue ) { + return this; + } + + /// + public ILog State( object state ) { + return this; + } + + /// + public ILog Message( string message, params object[] args ) { + return this; + } + + /// + public bool IsEnabled( LogLevel logLevel ) { + return false; + } + + /// + public IDisposable BeginScope( TState state ) { + return new DisposeAction( () => { } ); + } + + /// + public ILog LogTrace() { + return this; + } + + /// + public ILog LogDebug() { + return this; + } + + /// + public ILog LogInformation() { + return this; + } + + /// + public ILog LogWarning() { + return this; + } + + /// + public ILog LogError() { + return this; + } + + /// + public ILog LogCritical() { + return this; + } +} \ No newline at end of file diff --git a/src/Util.Logging/Usings.cs b/src/Util.Logging/Usings.cs new file mode 100644 index 000000000..cf49b8375 --- /dev/null +++ b/src/Util.Logging/Usings.cs @@ -0,0 +1,5 @@ +global using System; +global using System.Collections.Generic; +global using System.Diagnostics; +global using System.Text; +global using Microsoft.Extensions.Logging; diff --git a/src/Util.Logs/03-Util.Logs.csproj b/src/Util.Logs/03-Util.Logs.csproj deleted file mode 100644 index c9bd96f7e..000000000 --- a/src/Util.Logs/03-Util.Logs.csproj +++ /dev/null @@ -1,65 +0,0 @@ - - - - netstandard2.0 - Util.Logs - Util.Logs - 何镇汐 - 何镇汐 - Util.Logs是Util应用框架的日志操作类库。 -Util是一个.net core平台下的应用框架,旨在提升小型团队的开发输出能力,由常用公共操作类(工具类)、分层架构基类、Ui组件,第三方组件封装,第三方业务接口封装,配套代码生成模板,权限等组成。 - 何镇汐 - https://github.com/dotnetcore/Util/blob/master/LICENSE.txt - https://github.com/dotnetcore/Util - https://avatars2.githubusercontent.com/u/19421630 - https://github.com/dotnetcore/Util - git - util;applicationframework;dotnetcore;aspnetcore; - Util应用框架以MIT开源发布,可随意使用 - 0.2.2-preview - 0.2.2.0 - 0.2.2.0 - false - - - - ..\..\output\release - ..\..\output\release\netstandard2.0\Util.Logs.xml - - - - ..\..\output\release - ..\..\output\release\netstandard2.0\Util.Logs.xml - - - - - - - - - - - - - - - - - - - - True - True - LogResource.resx - - - - - - PublicResXFileCodeGenerator - LogResource.Designer.cs - - - - diff --git a/src/Util.Logs/Aspects/DebugLogAttribute.cs b/src/Util.Logs/Aspects/DebugLogAttribute.cs deleted file mode 100644 index 073386b27..000000000 --- a/src/Util.Logs/Aspects/DebugLogAttribute.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Util.Logs.Aspects { - /// - /// 调试日志 - /// - public class DebugLogAttribute : LogAttributeBase { - /// - /// 是否启用 - /// - protected override bool Enabled( ILog log ) { - return log.IsDebugEnabled; - } - - /// - /// 写日志 - /// - protected override void WriteLog( ILog log ) { - log.Debug(); - } - } -} diff --git a/src/Util.Logs/Aspects/ErrorLogAttribute.cs b/src/Util.Logs/Aspects/ErrorLogAttribute.cs deleted file mode 100644 index c611c25d4..000000000 --- a/src/Util.Logs/Aspects/ErrorLogAttribute.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Threading.Tasks; -using AspectCore.DynamicProxy; -using AspectCore.DynamicProxy.Parameters; -using Util.Aspects.Base; -using Util.Logs.Extensions; - -namespace Util.Logs.Aspects { - /// - /// 错误日志 - /// - public class ErrorLogAttribute : InterceptorBase { - /// - /// 执行 - /// - public override async Task Invoke( AspectContext context, AspectDelegate next ) { - var methodName = GetMethodName( context ); - var log = Log.GetLog( methodName ); - try { - await next( context ); - } - catch ( Exception ex ) { - log.Class( context.ServiceMethod.DeclaringType.FullName ).Method( methodName ).Exception( ex ); - foreach ( var parameter in context.GetParameters() ) - parameter.AppendTo( log ); - log.Error(); - throw; - } - } - - /// - /// 获取方法名 - /// - private string GetMethodName( AspectContext context ) { - return $"{context.ServiceMethod.DeclaringType.FullName}.{context.ServiceMethod.Name}"; - } - } -} diff --git a/src/Util.Logs/Aspects/LogAttributeBase.cs b/src/Util.Logs/Aspects/LogAttributeBase.cs deleted file mode 100644 index 45c04cc0e..000000000 --- a/src/Util.Logs/Aspects/LogAttributeBase.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Threading.Tasks; -using AspectCore.DynamicProxy; -using AspectCore.DynamicProxy.Parameters; -using Util.Aspects.Base; -using Util.Logs.Extensions; - -namespace Util.Logs.Aspects { - /// - /// 日志操作 - /// - public abstract class LogAttributeBase : InterceptorBase { - /// - /// 执行 - /// - public override async Task Invoke( AspectContext context, AspectDelegate next ) { - var methodName = GetMethodName( context ); - var log = Log.GetLog( methodName ); - if( !Enabled( log ) ) - return; - ExecuteBefore( log, context, methodName ); - await next( context ); - ExecuteAfter( log, context, methodName ); - } - - /// - /// 获取方法名 - /// - private string GetMethodName( AspectContext context ) { - return $"{context.ServiceMethod.DeclaringType.FullName}.{context.ServiceMethod.Name}"; - } - - /// - /// 是否启用 - /// - protected virtual bool Enabled( ILog log ) { - return true; - } - - /// - /// 执行前 - /// - private void ExecuteBefore( ILog log, AspectContext context, string methodName ) { - log.Caption( $"{context.ServiceMethod.Name}方法执行前" ) - .Class( context.ServiceMethod.DeclaringType.FullName ) - .Method( methodName ); - foreach( var parameter in context.GetParameters() ) - parameter.AppendTo( log ); - WriteLog( log ); - } - - /// - /// 写日志 - /// - protected abstract void WriteLog( ILog log ); - - /// - /// 执行后 - /// - private void ExecuteAfter( ILog log, AspectContext context, string methodName ) { - var parameter = context.GetReturnParameter(); - log.Caption( $"{context.ServiceMethod.Name}方法执行后" ) - .Method( methodName ) - .Content( $"返回类型: {parameter.ParameterInfo.ParameterType.FullName},返回值: {parameter.Value.SafeString()}" ); - WriteLog( log ); - } - } -} diff --git a/src/Util.Logs/Aspects/TraceLogAttribute.cs b/src/Util.Logs/Aspects/TraceLogAttribute.cs deleted file mode 100644 index 1438864fe..000000000 --- a/src/Util.Logs/Aspects/TraceLogAttribute.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Util.Logs.Aspects { - /// - /// 跟踪日志 - /// - public class TraceLogAttribute : LogAttributeBase { - /// - /// 是否启用 - /// - protected override bool Enabled( ILog log ) { - return log.IsTraceEnabled; - } - - /// - /// 写日志 - /// - protected override void WriteLog( ILog log ) { - log.Trace(); - } - } -} diff --git a/src/Util.Logs/Contents/ICaption.cs b/src/Util.Logs/Contents/ICaption.cs deleted file mode 100644 index 3b88dc5d7..000000000 --- a/src/Util.Logs/Contents/ICaption.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Util.Logs.Contents { - /// - /// 标题 - /// - public interface ICaption { - /// - /// 标题 - /// - string Caption { get; } - } -} diff --git a/src/Util.Logs/Contents/LogContent.cs b/src/Util.Logs/Contents/LogContent.cs deleted file mode 100644 index 1d20f658f..000000000 --- a/src/Util.Logs/Contents/LogContent.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System.Text; -using Util.Exceptions; -using Util.Logs.Abstractions; - -namespace Util.Logs.Contents { - /// - /// 日志内容 - /// - public class LogContent : ILogContent, ICaption { - /// - /// 初始化日志内容 - /// - public LogContent() { - Params = new StringBuilder(); - Content = new StringBuilder(); - Sql = new StringBuilder(); - SqlParams = new StringBuilder(); - } - - /// - /// 日志名称 - /// - public string LogName { get; set; } - /// - /// 日志级别 - /// - public string Level { get; set; } - /// - /// 跟踪号 - /// - public string TraceId { get; set; } - /// - /// 操作时间 - /// - public string OperationTime { get; set; } - /// - /// 执行时间 - /// - public string Duration { get; set; } - /// - /// IP - /// - public string Ip { get; set; } - /// - /// 主机 - /// - public string Host { get; set; } - /// - /// 线程号 - /// - public string ThreadId { get; set; } - /// - /// 浏览器 - /// - public string Browser { get; set; } - /// - /// 请求地址 - /// - public string Url { get; set; } - /// - /// 业务编号 - /// - public string BusinessId { get; set; } - /// - /// 租户 - /// - public string Tenant { get; set; } - /// - /// 应用程序 - /// - public string Application { get; set; } - /// - /// 模块 - /// - public string Module { get; set; } - /// - /// 类名 - /// - public string Class { get; set; } - /// - /// 方法 - /// - public string Method { get; set; } - /// - /// 参数 - /// - public StringBuilder Params { get; set; } - /// - /// 操作人编号 - /// - public string UserId { get; set; } - /// - /// 操作人 - /// - public string Operator { get; set; } - /// - /// 操作人角色 - /// - public string Role { get; set; } - /// - /// 标题 - /// - public string Caption { get; set; } - /// - /// 内容 - /// - public StringBuilder Content { get; set; } - /// - /// Sql语句 - /// - public StringBuilder Sql { get; set; } - /// - /// Sql参数 - /// - public StringBuilder SqlParams { get; set; } - /// - /// 错误码 - /// - public string ErrorCode { get; set; } - /// - /// 异常 - /// - public Warning Exception { get; set; } - } -} diff --git a/src/Util.Logs/Exceptionless/ExceptionlessProvider.cs b/src/Util.Logs/Exceptionless/ExceptionlessProvider.cs deleted file mode 100644 index f3880b701..000000000 --- a/src/Util.Logs/Exceptionless/ExceptionlessProvider.cs +++ /dev/null @@ -1,163 +0,0 @@ -using System.Linq; -using Exceptionless; -using Microsoft.Extensions.Logging; -using el = Exceptionless; -using NLogs = NLog; -using Util.Logs.Abstractions; -using Util.Logs.Contents; -using Util.Logs.NLog; - -namespace Util.Logs.Exceptionless { - /// - /// Exceptionless日志提供程序 - /// - public class ExceptionlessProvider : ILogProvider { - /// - /// NLog日志操作,用于控制日志级别是否启用 - /// - private readonly NLogs.ILogger _logger; - /// - /// 客户端 - /// - private readonly el.ExceptionlessClient _client; - /// - /// 行号 - /// - private int _line; - - /// - /// 初始化Exceptionless日志提供程序 - /// - /// 日志名称 - public ExceptionlessProvider( string logName ) { - _logger = NLogProvider.GetLogger( logName ); - _client = el.ExceptionlessClient.Default; - } - - /// - /// 日志名称 - /// - public string LogName => _logger.Name; - - /// - /// 调试级别是否启用 - /// - public bool IsDebugEnabled => _logger.IsDebugEnabled; - - /// - /// 跟踪级别是否启用 - /// - public bool IsTraceEnabled => _logger.IsTraceEnabled; - - /// - /// 写日志 - /// - /// 日志等级 - /// 日志内容 - public void WriteLog( LogLevel level, ILogContent content ) { - InitLine(); - var builder = CreateBuilder( level, content ); - SetUser( content ); - SetSource( builder, content ); - SetReferenceId( builder, content ); - AddProperties( builder, content as ILogConvert ); - builder.Submit(); - } - - /// - /// 初始化行号 - /// - private void InitLine() { - _line = 1; - } - - /// - /// 创建事件生成器 - /// - private EventBuilder CreateBuilder( LogLevel level, ILogContent content ) { - if( content.Exception != null ) - return _client.CreateException( content.Exception ); - return _client.CreateLog( GetMessage( content ), ConvertTo( level ) ); - } - - /// - /// 获取日志消息 - /// - /// 日志内容 - private string GetMessage( ILogContent content ) { - var caption = content as ICaption; - if ( caption != null && string.IsNullOrWhiteSpace( caption.Caption ) == false ) - return caption.Caption; - if( content.Content.Length > 0 ) - return content.Content.ToString(); - return content.TraceId; - } - - /// - /// 转换日志等级 - /// - private el.Logging.LogLevel ConvertTo( LogLevel level ) { - switch( level ) { - case LogLevel.Trace: - return el.Logging.LogLevel.Trace; - case LogLevel.Debug: - return el.Logging.LogLevel.Debug; - case LogLevel.Information: - return el.Logging.LogLevel.Info; - case LogLevel.Warning: - return el.Logging.LogLevel.Warn; - case LogLevel.Error: - return el.Logging.LogLevel.Error; - case LogLevel.Critical: - return el.Logging.LogLevel.Fatal; - default: - return el.Logging.LogLevel.Off; - } - } - - /// - /// 设置用户信息 - /// - private void SetUser( ILogContent content ) { - if ( string.IsNullOrWhiteSpace( content.UserId ) ) - return; - _client.Configuration.SetUserIdentity( content.UserId ); - } - - /// - /// 设置来源 - /// - private void SetSource( EventBuilder builder, ILogContent content ) { - if ( string.IsNullOrWhiteSpace( content.Url ) ) - return; - builder.SetSource( content.Url ); - } - - /// - /// 设置跟踪号 - /// - private void SetReferenceId( EventBuilder builder, ILogContent content ) { - builder.SetReferenceId( content.TraceId ); - } - - /// - /// 添加属性集合 - /// - private void AddProperties( EventBuilder builder, ILogConvert content ) { - if ( content == null ) - return; - foreach ( var parameter in content.To().OrderBy( t => t.SortId ) ) { - if ( string.IsNullOrWhiteSpace( parameter.Value.SafeString() ) ) - continue; - builder.SetProperty( $"{GetLine()}. {parameter.Text}", parameter.Value ); - } - } - - /// - /// 获取行号 - /// - private string GetLine() { - return _line++.ToString().PadLeft( 2, '0' ); - } - } -} diff --git a/src/Util.Logs/Exceptionless/ILogConvert.cs b/src/Util.Logs/Exceptionless/ILogConvert.cs deleted file mode 100644 index 35f753837..000000000 --- a/src/Util.Logs/Exceptionless/ILogConvert.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; - -namespace Util.Logs.Exceptionless { - /// - /// 日志转换器 - /// - public interface ILogConvert { - /// - /// 转换 - /// - List To(); - } -} diff --git a/src/Util.Logs/Exceptionless/LogContent.cs b/src/Util.Logs/Exceptionless/LogContent.cs deleted file mode 100644 index 99c5355c6..000000000 --- a/src/Util.Logs/Exceptionless/LogContent.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; - -namespace Util.Logs.Exceptionless { - /// - /// Exceptionless日志内容 - /// - public class LogContent : Util.Logs.Contents.LogContent, ILogConvert { - /// - /// 转换 - /// - public List To() { - return new List { - { new Item( LogResource.LogName, LogName,1) }, - { new Item(LogResource.TraceId, TraceId,2) }, - { new Item(LogResource.OperationTime, OperationTime,3) }, - { new Item(LogResource.Duration, Duration,4) }, - { new Item(LogResource.ThreadId, ThreadId,5) }, - { new Item("Url", Url,6) }, - { new Item(LogResource.UserId, UserId,7) }, - { new Item(LogResource.Operator, Operator,8 ) }, - { new Item(LogResource.Role,Role,9) }, - { new Item(LogResource.BusinessId,BusinessId,10) }, - { new Item(LogResource.Tenant,Tenant,11) }, - { new Item(LogResource.Application,Application,12) }, - { new Item(LogResource.Module,Module ,13) }, - { new Item(LogResource.Class,Class ,14) }, - { new Item(LogResource.Method,Method ,15) }, - { new Item(LogResource.Params,Params.ToString(),16) }, - { new Item(LogResource.Caption,Caption ,17) }, - { new Item(LogResource.Content,Content.ToString(),18) }, - { new Item(LogResource.Sql,Sql.ToString(),19) }, - { new Item(LogResource.SqlParams,SqlParams.ToString(),20) }, - { new Item(LogResource.ErrorCode,Exception?.Code,21) } - }; - } - } -} diff --git a/src/Util.Logs/Exceptionless/LogContext.cs b/src/Util.Logs/Exceptionless/LogContext.cs deleted file mode 100644 index 337da1086..000000000 --- a/src/Util.Logs/Exceptionless/LogContext.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using Util.Contexts; -using Util.Helpers; -using Util.Logs.Internal; - -namespace Util.Logs.Exceptionless { - /// - /// Exceptionless日志上下文 - /// - public class LogContext : Util.Logs.Core.LogContext { - /// - /// 初始化日志上下文 - /// - /// 上下文 - public LogContext( IContext context ) : base( context ) { - } - - /// - /// 创建日志上下文信息 - /// - protected override LogContextInfo CreateInfo() { - return new LogContextInfo { - TraceId = Guid.NewGuid().ToString(), - Stopwatch = GetStopwatch(), - Url = Web.Url - }; - } - } -} diff --git a/src/Util.Logs/Exceptionless/LogProviderFactory.cs b/src/Util.Logs/Exceptionless/LogProviderFactory.cs deleted file mode 100644 index 12669f28f..000000000 --- a/src/Util.Logs/Exceptionless/LogProviderFactory.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Util.Logs.Abstractions; - -namespace Util.Logs.Exceptionless { - /// - /// Exceptionless日志提供程序工厂 - /// - public class LogProviderFactory : ILogProviderFactory { - /// - /// 创建日志提供程序 - /// - /// 日志名称 - /// 日志格式化器 - public ILogProvider Create( string logName, ILogFormat format = null ) { - return new ExceptionlessProvider( logName ); - } - } -} diff --git a/src/Util.Logs/Extensions/Extensions.Aspect.cs b/src/Util.Logs/Extensions/Extensions.Aspect.cs deleted file mode 100644 index 0b6d4b717..000000000 --- a/src/Util.Logs/Extensions/Extensions.Aspect.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using AspectCore.DynamicProxy.Parameters; -using Util.Helpers; - -namespace Util.Logs.Extensions { - /// - /// AOP扩展 - /// - public static partial class Extensions { - /// - /// 添加日志参数 - /// - /// 参数 - /// 参数 - public static void AppendTo( this Parameter parameter, ILog log ) { - log.Params( parameter.ParameterInfo.ParameterType.FullName, parameter.Name, GetParameterValue( parameter ) ); - } - - /// - /// 获取参数值 - /// - private static string GetParameterValue( Parameter parameter ) { - if( Reflection.IsGenericCollection( parameter.RawType ) == false ) - return parameter.Value.SafeString(); - if ( !( parameter.Value is IEnumerable list ) ) - return parameter.Value.SafeString(); - return list.Select( t => t.SafeString() ).Join(); - } - } -} diff --git a/src/Util.Logs/Extensions/Extensions.Exception.cs b/src/Util.Logs/Extensions/Extensions.Exception.cs deleted file mode 100644 index 228beb023..000000000 --- a/src/Util.Logs/Extensions/Extensions.Exception.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using Util.Exceptions; - -namespace Util.Logs.Extensions { - /// - /// 异常扩展 - /// - public static partial class Extensions { - /// - /// 写日志 - /// - /// 异常 - /// 日志 - public static void Log( this Exception exception, ILog log ) { - if( !( exception is Warning warning ) ) { - log.Exception( exception ).Error(); - return; - } - log.Exception( exception, warning.Code ).Warn(); - } - } -} diff --git a/src/Util.Logs/Extensions/Extensions.Log.cs b/src/Util.Logs/Extensions/Extensions.Log.cs deleted file mode 100644 index c66fe2902..000000000 --- a/src/Util.Logs/Extensions/Extensions.Log.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using Util.Exceptions; -using Util.Logs.Contents; - -namespace Util.Logs.Extensions { - /// - /// 日志扩展 - /// - public static partial class Extensions { - /// - /// 设置业务编号 - /// - /// 日志操作 - /// 业务编号 - public static ILog BusinessId( this ILog log, string businessId ) { - return log.Set( content => { - if( string.IsNullOrWhiteSpace( content.BusinessId ) == false ) - content.BusinessId += ","; - content.BusinessId += businessId; - } ); - } - - /// - /// 设置模块 - /// - /// 日志操作 - /// 模块 - public static ILog Module( this ILog log, string module ) { - return log.Set( content => content.Module = module ); - } - - /// - /// 设置类名 - /// - /// 日志操作 - /// 类名 - public static ILog Class( this ILog log, string @class ) { - return log.Set( content => content.Class = @class ); - } - - /// - /// 设置方法 - /// - /// 日志操作 - /// 方法 - public static ILog Method( this ILog log, string method ) { - return log.Set( content => content.Method = method ); - } - - /// - /// 设置参数 - /// - /// 日志操作 - /// 参数类型 - /// 参数名 - /// 参数值 - public static ILog Params( this ILog log, string type, string name,string value ) { - return log.Set( content => - content.AppendLine( content.Params,$"{LogResource.ParameterType}: {type}, {LogResource.ParameterName}: {name}, {LogResource.ParameterValue}: {value}。" ) ); - } - - /// - /// 设置标题 - /// - /// 日志操作 - /// 标题 - public static ILog Caption( this ILog log, string caption ) { - return log.Set( content => content.Caption = caption ); - } - - /// - /// 设置Sql语句 - /// - /// 日志操作 - /// 值 - public static ILog Sql( this ILog log, string value ) { - return log.Set( content => content.AppendLine( content.Sql, value ) ); - } - - /// - /// 设置Sql参数 - /// - /// 日志操作 - /// 值 - public static ILog SqlParams( this ILog log, string value ) { - return log.Set( content => content.AppendLine( content.SqlParams, value ) ); - } - - /// - /// 设置异常 - /// - /// 日志操作 - /// 异常 - /// 错误码 - public static ILog Exception( this ILog log, Exception exception,string errorCode = "" ) { - if ( exception == null ) - return log; - return Exception( log, new Warning( "", errorCode, exception ) ); - } - - /// - /// 设置异常 - /// - /// 日志操作 - /// 异常 - public static ILog Exception( this ILog log, Warning exception ) { - if( exception == null ) - return log; - return log.Set( content => { - content.ErrorCode = exception.Code; - content.Exception = exception; - } ); - } - } -} diff --git a/src/Util.Logs/Extensions/Extensions.Service.cs b/src/Util.Logs/Extensions/Extensions.Service.cs deleted file mode 100644 index 0520e97af..000000000 --- a/src/Util.Logs/Extensions/Extensions.Service.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using Exceptionless; -using Microsoft.Extensions.DependencyInjection; -using Util.Logs.Abstractions; -using Util.Logs.Core; -using Util.Logs.Formats; - -namespace Util.Logs.Extensions { - /// - /// 日志扩展 - /// - public static partial class Extensions { - /// - /// 注册NLog日志操作 - /// - /// 服务集合 - public static void AddNLog( this IServiceCollection services ) { - services.AddScoped(); - services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); - } - - /// - /// 注册Exceptionless日志操作 - /// - /// 服务集合 - /// 配置操作 - public static void AddExceptionless( this IServiceCollection services, Action configAction ) { - services.AddScoped(); - services.AddSingleton( typeof( ILogFormat ), t => NullLogFormat.Instance ); - services.AddScoped(); - services.AddScoped(); - configAction?.Invoke( ExceptionlessClient.Default.Configuration ); - } - } -} diff --git a/src/Util.Logs/Formats/ContentFormat.cs b/src/Util.Logs/Formats/ContentFormat.cs deleted file mode 100644 index ad4f731a8..000000000 --- a/src/Util.Logs/Formats/ContentFormat.cs +++ /dev/null @@ -1,259 +0,0 @@ -using System; -using System.Text; -using Util.Logs.Abstractions; -using Util.Logs.Contents; - -namespace Util.Logs.Formats { - /// - /// 内容格式化器 - /// - public class ContentFormat : ILogFormat { - /// - /// 格式化 - /// - /// 日志内容 - public string Format( ILogContent logContent ) { - if( !( logContent is LogContent content ) ) - return string.Empty; - return Format( content ); - } - - /// - /// 内容格式化器实例 - /// - public static readonly ILogFormat Instance = new ContentFormat(); - - /// - /// 格式化 - /// - protected virtual string Format( LogContent content ) { - int line = 1; - StringBuilder result = new StringBuilder(); - Line1( result, content,ref line ); - Line2( result, content, ref line ); - Line3( result, content, ref line ); - Line4( result, content, ref line ); - Line5( result, content, ref line ); - Line6( result, content, ref line ); - Line7( result, content, ref line ); - Line8( result, content, ref line ); - Line9( result, content, ref line ); - Line10( result, content, ref line ); - Line11( result, content, ref line ); - Line12( result, content, ref line ); - Line13( result, content, ref line ); - Line14( result, content, ref line ); - Finish( result ); - return result.ToString(); - } - - /// - /// 添加行 - /// - protected void AppendLine( StringBuilder result, LogContent content, Action action, ref int line ) { - Append( result, content, action, ref line ); - result.AppendLine(); - } - - /// - /// 添加行 - /// - protected void Append( StringBuilder result, LogContent content, Action action,ref int line ) { - result.AppendFormat( "{0}. ", line++ ); - action( result, content ); - } - - /// - /// 添加日志内容 - /// - protected void Append( StringBuilder result, string caption, string value ) { - if( string.IsNullOrWhiteSpace( value ) ) - return; - result.AppendFormat( "{0}: {1} ", caption, value ); - } - - /// - /// 第1行 - /// - protected void Line1( StringBuilder result, LogContent content,ref int line ) { - AppendLine( result, content, ( r, c ) => { - r.AppendFormat( "{0}: {1} >> ", c.Level, c.LogName ); - r.AppendFormat( "{0}: {1} ", LogResource.TraceId, c.TraceId ); - r.AppendFormat( "{0}: {1} ", LogResource.OperationTime, c.OperationTime ); - if( string.IsNullOrWhiteSpace( c.Duration ) ) - return; - r.AppendFormat( "{0}: {1} ", LogResource.Duration, c.Duration ); - },ref line ); - } - - /// - /// 第2行 - /// - protected void Line2( StringBuilder result, LogContent content, ref int line ) { - AppendLine( result, content, ( r, c ) => { - Append( r, "Ip", c.Ip ); - Append( r, LogResource.Host, c.Host ); - Append( r, LogResource.ThreadId, c.ThreadId ); - }, ref line ); - } - - /// - /// 第3行 - /// - protected void Line3( StringBuilder result, LogContent content, ref int line ) { - if( string.IsNullOrWhiteSpace( content.Browser ) ) - return; - AppendLine( result, content, ( r, c ) => Append( r, LogResource.Browser, c.Browser ), ref line ); - } - - /// - /// 第4行 - /// - protected void Line4( StringBuilder result, LogContent content, ref int line ) { - if( string.IsNullOrWhiteSpace( content.Url ) ) - return; - AppendLine( result, content, ( r, c ) => r.Append( "Url: " + c.Url ), ref line ); - } - - /// - /// 第5行 - /// - protected void Line5( StringBuilder result, LogContent content, ref int line ) { - if( string.IsNullOrWhiteSpace( content.UserId ) && string.IsNullOrWhiteSpace( content.Operator ) - && string.IsNullOrWhiteSpace( content.Role ) ) - return; - AppendLine( result, content, ( r, c ) => { - Append( r, LogResource.UserId, c.UserId ); - Append( r, LogResource.Operator, c.Operator ); - Append( r, LogResource.Role, c.Role ); - }, ref line ); - } - - /// - /// 第6行 - /// - protected void Line6( StringBuilder result, LogContent content, ref int line ) { - if( string.IsNullOrWhiteSpace( content.BusinessId ) && string.IsNullOrWhiteSpace( content.Tenant ) - && string.IsNullOrWhiteSpace( content.Application ) && string.IsNullOrWhiteSpace( content.Module ) ) - return; - AppendLine( result, content, ( r, c ) => { - Append( r, LogResource.BusinessId, c.BusinessId ); - Append( r, LogResource.Tenant, c.Tenant ); - Append( r, LogResource.Application, c.Application ); - Append( r, LogResource.Module, c.Module ); - }, ref line ); - } - - /// - /// 第7行 - /// - protected void Line7( StringBuilder result, LogContent content, ref int line ) { - if( string.IsNullOrWhiteSpace( content.Class ) && string.IsNullOrWhiteSpace( content.Method ) ) - return; - AppendLine( result, content, ( r, c ) => { - Append( r, LogResource.Class, c.Class ); - Append( r, LogResource.Method, c.Method ); - }, ref line ); - } - - /// - /// 第8行 - /// - protected void Line8( StringBuilder result, LogContent content, ref int line ) { - if( content.Params.Length == 0 ) - return; - Append( result, content, ( r, c ) => { - r.AppendLine( $"{LogResource.Params}:" ); - r.Append( c.Params ); - }, ref line ); - } - - /// - /// 第9行 - /// - protected void Line9( StringBuilder result, LogContent content, ref int line ) { - if( string.IsNullOrWhiteSpace( content.Caption ) ) - return; - AppendLine( result, content, ( r, c ) => { - r.AppendFormat( "{0}: {1}", LogResource.Caption, c.Caption ); - }, ref line ); - } - - /// - /// 第10行 - /// - protected void Line10( StringBuilder result, LogContent content, ref int line ) { - if( content.Content.Length == 0 ) - return; - Append( result, content, ( r, c ) => { - r.AppendLine( $"{LogResource.Content}:" ); - r.Append( c.Content ); - }, ref line ); - } - - /// - /// 第11行 - /// - protected void Line11( StringBuilder result, LogContent content, ref int line ) { - if( content.Sql.Length == 0 ) - return; - Append( result, content, ( r, c ) => { - r.AppendLine( $"{LogResource.Sql}:" ); - r.Append( c.Sql ); - }, ref line ); - } - - /// - /// 第12行 - /// - protected void Line12( StringBuilder result, LogContent content, ref int line ) { - if( content.SqlParams.Length == 0 ) - return; - Append( result, content, ( r, c ) => { - r.AppendLine( $"{LogResource.SqlParams}:" ); - r.Append( c.SqlParams ); - }, ref line ); - } - - /// - /// 第13行 - /// - protected void Line13( StringBuilder result, LogContent content, ref int line ) { - if( content.Exception == null ) - return; - AppendLine( result, content, ( r, c ) => { - r.AppendLine( $"{LogResource.Exception}: { GetErrorCode( content.ErrorCode ) }" ); - r.Append( $" { c.Exception.Message }" ); - }, ref line ); - } - - /// - /// 获取错误码 - /// - private string GetErrorCode( string errorCode ) { - if( string.IsNullOrWhiteSpace( errorCode ) ) - return string.Empty; - return $"-- {LogResource.ErrorCode}: {errorCode}"; - } - - /// - /// 第14行 - /// - protected void Line14( StringBuilder result, LogContent content, ref int line ) { - if( content.Exception == null ) - return; - AppendLine( result, content, ( r, c ) => { - r.AppendLine( $"{LogResource.StackTrace}:" ); - r.Append( c.Exception.StackTrace ); - }, ref line ); - } - - /// - /// 结束 - /// - protected void Finish( StringBuilder result ) { - for( int i = 0; i < 125; i++ ) - result.Append( "-" ); - } - } -} diff --git a/src/Util.Logs/Formats/FormatProvider.cs b/src/Util.Logs/Formats/FormatProvider.cs deleted file mode 100644 index eb61f3650..000000000 --- a/src/Util.Logs/Formats/FormatProvider.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using Util.Logs.Abstractions; - -namespace Util.Logs.Formats { - /// - /// 日志格式化提供程序 - /// - public class FormatProvider : IFormatProvider, ICustomFormatter { - /// - /// 日志格式化器 - /// - private readonly ILogFormat _format; - - /// - /// 初始化日志格式化提供程序 - /// - /// 日志格式化器 - public FormatProvider( ILogFormat format ) { - _format = format ?? throw new ArgumentNullException( nameof( format ) ); - } - - /// - /// 格式化 - /// - public string Format( string format, object arg, IFormatProvider formatProvider ) { - if( !( arg is ILogContent content ) ) - return string.Empty; - return _format.Format( content ); - } - - /// - /// 获取格式化器 - /// - public object GetFormat( Type formatType ) { - return formatType == typeof( ICustomFormatter ) ? this : null; - } - } -} diff --git a/src/Util.Logs/Log.cs b/src/Util.Logs/Log.cs deleted file mode 100644 index b30fbac9a..000000000 --- a/src/Util.Logs/Log.cs +++ /dev/null @@ -1,148 +0,0 @@ -using System; -using Util.Domains.Sessions; -using Util.Helpers; -using Util.Logs.Abstractions; -using Util.Logs.Core; -using Util.Logs.Exceptionless; -using Util.Security; - -namespace Util.Logs { - /// - /// 日志操作 - /// - public class Log : LogBase { - /// - /// 类名 - /// - private readonly string _class; - - /// - /// 初始化日志操作 - /// - /// 日志提供程序工厂 - /// 日志上下文 - /// 日志格式器 - /// 用户会话 - public Log( ILogProviderFactory providerFactory, ILogContext context, ILogFormat format, ISession session ) : base( providerFactory.Create( "", format ), context, session ) { - } - - /// - /// 初始化日志操作 - /// - /// 日志提供程序 - /// 日志上下文 - /// 用户会话 - /// 类名 - private Log( ILogProvider provider, ILogContext context, ISession session, string @class ) : base( provider, context, session ) { - _class = @class; - } - - /// - /// 获取日志内容 - /// - protected override LogContent GetContent() { - return new LogContent { Class = _class }; - } - - /// - /// 初始化 - /// - protected override void Init( LogContent content ) { - base.Init( content ); - content.Tenant = Session.GetTenant(); - content.Application = Session.GetApplication(); - content.Operator = Session.GetFullName(); - content.Role = Session.GetRoleName(); - } - - /// - /// 获取日志操作实例 - /// - public static ILog GetLog() { - return GetLog( string.Empty ); - } - - /// - /// 获取日志操作实例 - /// - /// 实例 - public static ILog GetLog( object instance ) { - if( instance == null ) - return GetLog(); - var className = instance.GetType().ToString(); - return GetLog( className, className ); - } - - /// - /// 获取日志操作实例 - /// - /// 日志名称 - public static ILog GetLog( string logName ) { - return GetLog( logName, string.Empty ); - } - - /// - /// 获取日志操作实例 - /// - private static ILog GetLog( string logName, string @class ) { - var providerFactory = GetLogProviderFactory(); - var format = GetLogFormat(); - var context = GetLogContext(); - var session = GetSession(); - return new Log( providerFactory.Create( logName, format ), context, session, @class ); - } - - /// - /// 获取日志提供程序工厂 - /// - private static ILogProviderFactory GetLogProviderFactory() { - try { - return Ioc.Create(); - } - catch { - return new Util.Logs.NLog.LogProviderFactory(); - } - } - - /// - /// 获取日志格式器 - /// - private static ILogFormat GetLogFormat() { - try { - return Ioc.Create(); - } - catch { - return Util.Logs.Formats.ContentFormat.Instance; - } - } - - /// - /// 获取日志上下文 - /// - private static ILogContext GetLogContext() { - try { - return Ioc.Create(); - } - catch { - return NullLogContext.Instance; - } - } - - /// - /// 获取用户会话 - /// - private static ISession GetSession() { - try { - return Ioc.Create(); - } - catch { - return Util.Domains.Sessions.Session.Null; - } - } - - /// - /// 空日志操作 - /// - public static readonly ILog Null = NullLog.Instance; - } -} diff --git a/src/Util.Logs/LogResource.Designer.cs b/src/Util.Logs/LogResource.Designer.cs deleted file mode 100644 index d293de35c..000000000 --- a/src/Util.Logs/LogResource.Designer.cs +++ /dev/null @@ -1,306 +0,0 @@ -//------------------------------------------------------------------------------ -// -// 此代码由工具生成。 -// 运行时版本:4.0.30319.42000 -// -// 对此文件的更改可能会导致不正确的行为,并且如果 -// 重新生成代码,这些更改将会丢失。 -// -//------------------------------------------------------------------------------ - -namespace Util.Logs { - using System; - - - /// - /// 一个强类型的资源类,用于查找本地化的字符串等。 - /// - // 此类是由 StronglyTypedResourceBuilder - // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 - // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen - // (以 /str 作为命令选项),或重新生成 VS 项目。 - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class LogResource { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal LogResource() { - } - - /// - /// 返回此类使用的缓存的 ResourceManager 实例。 - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Util.Logs.LogResource", typeof(LogResource).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// 使用此强类型资源类,为所有资源查找 - /// 重写当前线程的 CurrentUICulture 属性。 - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// 查找类似 应用程序 的本地化字符串。 - /// - public static string Application { - get { - return ResourceManager.GetString("Application", resourceCulture); - } - } - - /// - /// 查找类似 浏览器 的本地化字符串。 - /// - public static string Browser { - get { - return ResourceManager.GetString("Browser", resourceCulture); - } - } - - /// - /// 查找类似 业务编号 的本地化字符串。 - /// - public static string BusinessId { - get { - return ResourceManager.GetString("BusinessId", resourceCulture); - } - } - - /// - /// 查找类似 标题 的本地化字符串。 - /// - public static string Caption { - get { - return ResourceManager.GetString("Caption", resourceCulture); - } - } - - /// - /// 查找类似 类名 的本地化字符串。 - /// - public static string Class { - get { - return ResourceManager.GetString("Class", resourceCulture); - } - } - - /// - /// 查找类似 内容 的本地化字符串。 - /// - public static string Content { - get { - return ResourceManager.GetString("Content", resourceCulture); - } - } - - /// - /// 查找类似 已执行 的本地化字符串。 - /// - public static string Duration { - get { - return ResourceManager.GetString("Duration", resourceCulture); - } - } - - /// - /// 查找类似 错误码 的本地化字符串。 - /// - public static string ErrorCode { - get { - return ResourceManager.GetString("ErrorCode", resourceCulture); - } - } - - /// - /// 查找类似 异常 的本地化字符串。 - /// - public static string Exception { - get { - return ResourceManager.GetString("Exception", resourceCulture); - } - } - - /// - /// 查找类似 主机 的本地化字符串。 - /// - public static string Host { - get { - return ResourceManager.GetString("Host", resourceCulture); - } - } - - /// - /// 查找类似 日志名称 的本地化字符串。 - /// - public static string LogName { - get { - return ResourceManager.GetString("LogName", resourceCulture); - } - } - - /// - /// 查找类似 方法 的本地化字符串。 - /// - public static string Method { - get { - return ResourceManager.GetString("Method", resourceCulture); - } - } - - /// - /// 查找类似 模块 的本地化字符串。 - /// - public static string Module { - get { - return ResourceManager.GetString("Module", resourceCulture); - } - } - - /// - /// 查找类似 操作时间 的本地化字符串。 - /// - public static string OperationTime { - get { - return ResourceManager.GetString("OperationTime", resourceCulture); - } - } - - /// - /// 查找类似 操作人 的本地化字符串。 - /// - public static string Operator { - get { - return ResourceManager.GetString("Operator", resourceCulture); - } - } - - /// - /// 查找类似 参数名 的本地化字符串。 - /// - public static string ParameterName { - get { - return ResourceManager.GetString("ParameterName", resourceCulture); - } - } - - /// - /// 查找类似 参数类型 的本地化字符串。 - /// - public static string ParameterType { - get { - return ResourceManager.GetString("ParameterType", resourceCulture); - } - } - - /// - /// 查找类似 参数值 的本地化字符串。 - /// - public static string ParameterValue { - get { - return ResourceManager.GetString("ParameterValue", resourceCulture); - } - } - - /// - /// 查找类似 参数 的本地化字符串。 - /// - public static string Params { - get { - return ResourceManager.GetString("Params", resourceCulture); - } - } - - /// - /// 查找类似 角色 的本地化字符串。 - /// - public static string Role { - get { - return ResourceManager.GetString("Role", resourceCulture); - } - } - - /// - /// 查找类似 Sql语句 的本地化字符串。 - /// - public static string Sql { - get { - return ResourceManager.GetString("Sql", resourceCulture); - } - } - - /// - /// 查找类似 Sql参数 的本地化字符串。 - /// - public static string SqlParams { - get { - return ResourceManager.GetString("SqlParams", resourceCulture); - } - } - - /// - /// 查找类似 堆栈跟踪 的本地化字符串。 - /// - public static string StackTrace { - get { - return ResourceManager.GetString("StackTrace", resourceCulture); - } - } - - /// - /// 查找类似 租户 的本地化字符串。 - /// - public static string Tenant { - get { - return ResourceManager.GetString("Tenant", resourceCulture); - } - } - - /// - /// 查找类似 线程号 的本地化字符串。 - /// - public static string ThreadId { - get { - return ResourceManager.GetString("ThreadId", resourceCulture); - } - } - - /// - /// 查找类似 跟踪号 的本地化字符串。 - /// - public static string TraceId { - get { - return ResourceManager.GetString("TraceId", resourceCulture); - } - } - - /// - /// 查找类似 操作人编号 的本地化字符串。 - /// - public static string UserId { - get { - return ResourceManager.GetString("UserId", resourceCulture); - } - } - } -} diff --git a/src/Util.Logs/LogResource.resx b/src/Util.Logs/LogResource.resx deleted file mode 100644 index cee91df77..000000000 --- a/src/Util.Logs/LogResource.resx +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 应用程序 - - - 浏览器 - - - 业务编号 - - - 标题 - - - 类名 - - - 内容 - - - 已执行 - - - 错误码 - - - 异常 - - - 主机 - - - 日志名称 - - - 方法 - - - 模块 - - - 操作时间 - - - 操作人 - - - 参数名 - - - 参数类型 - - - 参数值 - - - 参数 - - - 角色 - - - Sql语句 - - - Sql参数 - - - 堆栈跟踪 - - - 租户 - - - 线程号 - - - 跟踪号 - - - 操作人编号 - - \ No newline at end of file diff --git a/src/Util.Logs/NLog/LogProviderFactory.cs b/src/Util.Logs/NLog/LogProviderFactory.cs deleted file mode 100644 index 6d0c87364..000000000 --- a/src/Util.Logs/NLog/LogProviderFactory.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Util.Logs.Abstractions; - -namespace Util.Logs.NLog { - /// - /// NLog日志提供程序工厂 - /// - public class LogProviderFactory : ILogProviderFactory { - /// - /// 创建日志提供程序 - /// - /// 日志名称 - /// 日志格式化器 - public ILogProvider Create( string logName, ILogFormat format = null ) { - return new NLogProvider( logName, format ); - } - } -} diff --git a/src/Util.Logs/NLog/NLogProvider.cs b/src/Util.Logs/NLog/NLogProvider.cs deleted file mode 100644 index 2695ced04..000000000 --- a/src/Util.Logs/NLog/NLogProvider.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; -using NLogs = NLog; -using Util.Logs.Abstractions; -using Util.Logs.Formats; - -namespace Util.Logs.NLog { - /// - /// NLog日志提供程序 - /// - public class NLogProvider : ILogProvider { - /// - /// NLog日志操作 - /// - private readonly NLogs.ILogger _logger; - /// - /// 日志格式化器 - /// - private readonly ILogFormat _format; - - /// - /// 初始化日志 - /// - /// 日志名称 - /// 日志格式化器 - public NLogProvider( string logName, ILogFormat format = null ) { - _logger = GetLogger( logName ); - _format = format; - } - - /// - /// 获取NLog日志操作 - /// - /// 日志名称 - public static NLogs.ILogger GetLogger( string logName ) { - return NLogs.LogManager.GetLogger( logName ); - } - - /// - /// 日志名称 - /// - public string LogName => _logger.Name; - - /// - /// 调试级别是否启用 - /// - public bool IsDebugEnabled => _logger.IsDebugEnabled; - - /// - /// 跟踪级别是否启用 - /// - public bool IsTraceEnabled => _logger.IsTraceEnabled; - - /// - /// 写日志 - /// - /// 日志等级 - /// 日志内容 - public void WriteLog( LogLevel level, ILogContent content ) { - var provider = GetFormatProvider(); - if( provider == null ) { - _logger.Log( ConvertTo( level ), content ); - return; - } - _logger.Log( ConvertTo( level ), provider, content ); - } - - /// - /// 转换日志等级 - /// - private NLogs.LogLevel ConvertTo( LogLevel level ) { - switch( level ) { - case LogLevel.Trace: - return NLogs.LogLevel.Trace; - case LogLevel.Debug: - return NLogs.LogLevel.Debug; - case LogLevel.Information: - return NLogs.LogLevel.Info; - case LogLevel.Warning: - return NLogs.LogLevel.Warn; - case LogLevel.Error: - return NLogs.LogLevel.Error; - case LogLevel.Critical: - return NLogs.LogLevel.Fatal; - default: - return NLogs.LogLevel.Off; - } - } - - /// - /// 获取格式化提供程序 - /// - private IFormatProvider GetFormatProvider() { - if( _format == null ) - return null; - return new FormatProvider( _format ); - } - } -} diff --git a/src/Util.Microservices.Dapr/03-Util.Microservices.Dapr.csproj b/src/Util.Microservices.Dapr/03-Util.Microservices.Dapr.csproj new file mode 100644 index 000000000..62620ee47 --- /dev/null +++ b/src/Util.Microservices.Dapr/03-Util.Microservices.Dapr.csproj @@ -0,0 +1,39 @@ + + + $(NetTargetFramework) + icon.jpg + Util.Microservices.Dapr + Util.Microservices.Dapr + Util.Microservices.Dapr是Util应用框架基于Dapr的微服务操作类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Microservices.Dapr.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Microservices.Dapr.xml + + + + + True + False + + + + + + + + + + + + + + + + diff --git a/src/Util.Microservices.Dapr/AppBuilderExtensions.cs b/src/Util.Microservices.Dapr/AppBuilderExtensions.cs new file mode 100644 index 000000000..0f713dc19 --- /dev/null +++ b/src/Util.Microservices.Dapr/AppBuilderExtensions.cs @@ -0,0 +1,94 @@ +using Util.Configs; +using Util.Helpers; +using Util.SystemTextJson; + +namespace Util.Microservices.Dapr; + +/// +/// Dapr微服务操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置Dapr微服务操作 + /// + /// 应用生成器 + public static IAppBuilder AddDapr( this IAppBuilder builder ) { + return builder.AddDapr( null ); + } + + /// + /// 配置Dapr微服务操作 + /// + /// 应用生成器 + /// Dapr配置操作 + public static IAppBuilder AddDapr( this IAppBuilder builder, Action setupAction ) { + return builder.AddDapr( setupAction, null ); + } + + /// + /// 配置Dapr微服务操作 + /// + /// 应用生成器 + /// Dapr配置操作 + /// Dapr客户端生成操作 + public static IAppBuilder AddDapr( this IAppBuilder builder, Action setupAction, Action buildAction ) { + builder.CheckNull( nameof( builder ) ); + var options = new DaprOptions(); + setupAction?.Invoke( options ); + builder.Host.ConfigureServices( ( context, services ) => { + services.AddDaprClient( clientBuilder => { + clientBuilder.UseJsonSerializationOptions( GetJsonSerializerOptions() ); + buildAction?.Invoke( clientBuilder ); + } ); + if( setupAction != null ) + services.Configure( setupAction ); + } ); + builder.Host.ConfigureAppConfiguration( ( context, configurationBuilder ) => { + AddDaprSecretStore( configurationBuilder, options, buildAction ); + } ); + return builder; + } + + /// + /// 获取Json序列化配置 + /// + private static JsonSerializerOptions GetJsonSerializerOptions() { + return new JsonSerializerOptions { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true, + NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ), + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = { + new DateTimeJsonConverter(), + new NullableDateTimeJsonConverter(), + new EnumJsonConverterFactory() + } + }; + } + + /// + /// 配置Dapr机密存储器 + /// + private static void AddDaprSecretStore( IConfigurationBuilder configurationBuilder , DaprOptions options, Action buildAction ) { + if ( options.SecretStoreName.IsEmpty() ) + return; + try { + configurationBuilder.AddDaprSecretStore( options.SecretStoreName, CreateDaprClient( buildAction ) ); + } + catch ( Exception ) { + if ( Common.IsLinux ) + throw; + } + } + + /// + /// 创建Dapr客户端 + /// + private static DaprClient CreateDaprClient( Action buildAction ) { + var clientBuilder = new DaprClientBuilder(); + clientBuilder.UseJsonSerializationOptions( GetJsonSerializerOptions() ); + buildAction?.Invoke( clientBuilder ); + return clientBuilder.Build(); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/CloudEventHeadersMiddlewareExtensions.cs b/src/Util.Microservices.Dapr/CloudEventHeadersMiddlewareExtensions.cs new file mode 100644 index 000000000..3d8392bfe --- /dev/null +++ b/src/Util.Microservices.Dapr/CloudEventHeadersMiddlewareExtensions.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Builder; +using Util.Microservices.Dapr.Events; + +namespace Util.Microservices.Dapr; + +/// +/// 云事件标头处理中间件扩展 +/// +public static class CloudEventHeadersMiddlewareExtensions { + /// + /// 注册云事件标头处理中间件,注意: 必须在 app.UseCloudEvents() 之前调用 + /// + /// 应用程序生成器 + public static IApplicationBuilder UseCloudEventHeaders( this IApplicationBuilder builder ) { + return builder.UseMiddleware(); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/CommandLines/DaprRunCommand.cs b/src/Util.Microservices.Dapr/CommandLines/DaprRunCommand.cs new file mode 100644 index 000000000..b8514e68c --- /dev/null +++ b/src/Util.Microservices.Dapr/CommandLines/DaprRunCommand.cs @@ -0,0 +1,268 @@ +using System.Net.Sockets; +using Util.Helpers; + +namespace Util.Microservices.Dapr.CommandLines; + +/// +/// dapr run 命令 +/// +public class DaprRunCommand { + /// + /// 日志操作 + /// + private ILogger _logger; + /// + /// 应用标识 + /// + private string _appId; + /// + /// 应用端口 + /// + private int _appPort; + /// + /// 应用访问协议 + /// + private string _appProtocol; + /// + /// Dapr Http端口 + /// + private int _daprHttpPort; + /// + /// Dapr Grpc端口 + /// + private int _daprGrpcPort; + /// + /// 度量端口 + /// + private int _metricsPort; + /// + /// Dapr组件配置目录路径 + /// + private string _componentsPath; + /// + /// Dapr配置目录路径 + /// + private string _configPath; + /// + /// dotnet项目文件路径 + /// + private string _project; + /// + /// 是否使用空闲端口 + /// + private bool _isUseFreePorts; + + /// + /// 封闭构造方法 + /// + private DaprRunCommand() { + _logger = NullLogger.Instance; + } + + /// + /// 创建dapr run命令 + /// + /// 应用标识 + public static DaprRunCommand Create( string appId ) { + return new DaprRunCommand().AppId( appId ); + } + + /// + /// 获取应用协议 + /// + public string GetAppProtocol() { + return _appProtocol; + } + + /// + /// 获取应用端口 + /// + public int GetAppPort() { + return _appPort; + } + + /// + /// 获取Dapr Http端口 + /// + public int GetDaprHttpPort() { + return _daprHttpPort; + } + + /// + /// 获取Dapr Grpc端口 + /// + public int GetDaprGrpcPort() { + return _daprGrpcPort; + } + + /// + /// 获取度量端口 + /// + public int GetMetricsPort() { + return _metricsPort; + } + + /// + /// 设置应用标识 + /// + /// 应用标识 + private DaprRunCommand AppId( string id ) { + _appId = id; + return this; + } + + /// + /// 设置日志操作 + /// + public DaprRunCommand Log( ILogger logger ) { + _logger = logger; + return this; + } + + /// + /// 设置应用端口,如果未设置则自动获取 + /// + /// 应用Http端口 + public DaprRunCommand AppPort( int port ) { + _appPort = port; + return this; + } + + /// + /// 设置Dapr访问应用协议 + /// + /// 访问协议,可选值为: Http,Grpc,默认值为: Http + public DaprRunCommand AppProtocol( string protocol ) { + _appProtocol = protocol; + return this; + } + + /// + /// 设置Dapr Http端口,如果未设置则自动获取 + /// + /// Dapr Http端口 + public DaprRunCommand DaprHttpPort( int port ) { + _daprHttpPort = port; + return this; + } + + /// + /// 设置Dapr Grpc端口,如果未设置则自动获取 + /// + /// Dapr Grpc端口 + public DaprRunCommand DaprGrpcPort( int port ) { + _daprGrpcPort = port; + return this; + } + + /// + /// 设置度量端口,如果未设置则自动获取 + /// + /// 度量端口 + public DaprRunCommand MetricsPort( int port ) { + _metricsPort = port; + return this; + } + + /// + /// 设置Dapr组件配置目录路径 + /// + /// Dapr组件配置目录路径 + public DaprRunCommand ComponentsPath( string path ) { + _componentsPath = path; + return this; + } + + /// + /// 设置Dapr配置文件路径 + /// + /// Dapr配置文件路径 + public DaprRunCommand ConfigPath( string path ) { + _configPath = path; + return this; + } + + /// + /// 设置dotnet项目文件路径 + /// + /// dotnet项目文件路径,范例: ../../test/app.csproj + public DaprRunCommand Project( string project ) { + _project = project; + return this; + } + + /// + /// 设置或取消使用空闲端口 + /// + /// 是否使用空闲端口 + public DaprRunCommand UseFreePorts( bool isUseFreePorts = true ) { + _isUseFreePorts = isUseFreePorts; + return this; + } + + /// + /// 执行命令 + /// + public void Execute() { + CreateCommandLine() + .OutputToMatch( "dapr initialized. Status: Running." ) + .Execute(); + } + + /// + /// 创建命令行操作 + /// + private CommandLine CreateCommandLine() { + if ( _isUseFreePorts ) + GetFreePorts(); + return CommandLine.Create( "dapr" ) + .Log( _logger ) + .Arguments( "run" ) + .Arguments( "--app-id", _appId ) + .ArgumentsIf( _appPort != 0, "--app-port", _appPort.ToString( CultureInfo.InvariantCulture ) ) + .ArgumentsIf( _appProtocol.IsEmpty() == false, "--app-protocol", _appProtocol ) + .ArgumentsIf( _daprHttpPort != 0, "--dapr-http-port", _daprHttpPort.ToString( CultureInfo.InvariantCulture ) ) + .ArgumentsIf( _daprGrpcPort != 0, "--dapr-grpc-port", _daprGrpcPort.ToString( CultureInfo.InvariantCulture ) ) + .ArgumentsIf( _metricsPort != 0, "--metrics-port", _metricsPort.ToString( CultureInfo.InvariantCulture ) ) + .ArgumentsIf( _componentsPath.IsEmpty() == false, "--resources-path", _componentsPath ) + .ArgumentsIf( _configPath.IsEmpty() == false, "--config", _configPath ) + .Arguments( "--log-level", "debug" ) + .ArgumentsIf( _project.IsEmpty() == false, "--" ) + .ArgumentsIf( _project.IsEmpty() == false, "dotnet", "run" ) + .ArgumentsIf( _project.IsEmpty() == false, "--project", _project ) + .ArgumentsIf( _project.IsEmpty() == false && _appPort != 0, "--urls", $"http://localhost:{_appPort.ToString( CultureInfo.InvariantCulture )}" ); + } + + /// + /// 获取空闲端口 + /// + private void GetFreePorts() { + const int NUM_LISTENERS = 4; + IPAddress ip = IPAddress.Loopback; + var listeners = new TcpListener[NUM_LISTENERS]; + var ports = new int[NUM_LISTENERS]; + for ( int i = 0; i < NUM_LISTENERS; i++ ) { + listeners[i] = new TcpListener( ip, 0 ); + listeners[i].Start(); + ports[i] = ( (IPEndPoint)listeners[i].LocalEndpoint ).Port; + } + for ( int i = 0; i < NUM_LISTENERS; i++ ) { + listeners[i].Stop(); + } + if ( _appPort == 0 ) + _appPort = ports[0]; + if ( _daprHttpPort == 0 ) + _daprHttpPort = ports[1]; + if ( _daprGrpcPort == 0 ) + _daprGrpcPort = ports[2]; + if ( _metricsPort == 0 ) + _metricsPort = ports[3]; + } + + /// + /// 获取命令调试文本 + /// + public string GetDebugText() { + return CreateCommandLine().GetDebugText(); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/CommandLines/DaprStopCommand.cs b/src/Util.Microservices.Dapr/CommandLines/DaprStopCommand.cs new file mode 100644 index 000000000..28e016362 --- /dev/null +++ b/src/Util.Microservices.Dapr/CommandLines/DaprStopCommand.cs @@ -0,0 +1,76 @@ +using Util.Helpers; + +namespace Util.Microservices.Dapr.CommandLines; + +/// +/// dapr stop命令 +/// +public class DaprStopCommand { + /// + /// 日志操作 + /// + private ILogger _logger; + /// + /// 应用标识 + /// + private string _appId; + + /// + /// 封闭构造方法 + /// + private DaprStopCommand() { + _logger = NullLogger.Instance; + } + + /// + /// 创建dapr stop命令 + /// + /// 应用标识 + public static DaprStopCommand Create( string appId ) { + return new DaprStopCommand().AppId( appId ); + } + + /// + /// 设置应用标识 + /// + /// 应用标识 + private DaprStopCommand AppId( string id ) { + _appId = id; + return this; + } + + /// + /// 设置日志操作 + /// + public DaprStopCommand Log( ILogger logger ) { + _logger = logger; + return this; + } + + /// + /// 执行命令 + /// + public void Execute() { + CreateCommandLine() + .OutputToMatch( "app stopped successfully" ) + .OutputToMatch( "failed to stop app id" ) + .Execute(); + } + + /// + /// 创建命令行操作 + /// + private CommandLine CreateCommandLine() { + return CommandLine.Create( "dapr" ) + .Log( _logger ) + .Arguments( "stop" ) + .Arguments( "--app-id", _appId ); + } + + /// + /// 获取命令调试文本 + /// + public string GetDebugText() { + return CreateCommandLine().GetDebugText(); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/DaprHelper.cs b/src/Util.Microservices.Dapr/DaprHelper.cs new file mode 100644 index 000000000..c0402c37c --- /dev/null +++ b/src/Util.Microservices.Dapr/DaprHelper.cs @@ -0,0 +1,26 @@ +namespace Util.Microservices.Dapr; + +/// +/// Dapr辅助操作 +/// +public static class DaprHelper { + /// + /// 应用标识环境变量 + /// + public const string DaprAppId = "Dapr_AppId"; + + /// + /// 设置应用标识环境变量 + /// + /// 应用标识 + public static void SetAppId( string appId ) { + Environment.SetEnvironmentVariable( DaprAppId, appId ); + } + + /// + /// 获取应用标识环境变量 + /// + public static string GetAppId() { + return Environment.GetEnvironmentVariable( DaprAppId ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/DaprMicroserviceClient.cs b/src/Util.Microservices.Dapr/DaprMicroserviceClient.cs new file mode 100644 index 000000000..db2575802 --- /dev/null +++ b/src/Util.Microservices.Dapr/DaprMicroserviceClient.cs @@ -0,0 +1,47 @@ +using Util.Microservices.Dapr.Events; +using Util.Microservices.Dapr.ServiceInvocations; +using Util.Microservices.Dapr.StateManagements; + +namespace Util.Microservices.Dapr; + +/// +/// Dapr微服务客户端 +/// +public class DaprMicroserviceClient : IMicroserviceClient { + /// + /// 初始化Dapr微服务客户端 + /// + /// Dapr客户端 + /// Dapr Http客户端 + /// Dapr配置 + /// 应用标识 + /// 日志工厂 + /// 服务提供器 + public DaprMicroserviceClient( DaprClient client, HttpClient httpClient, IOptions options, string appId, ILoggerFactory loggerFactory, IServiceProvider serviceProvider ) { + HttpClient = new HttpClientService().SetHttpClient( httpClient ); + ServiceInvocation = new DaprServiceInvocation( client, options, loggerFactory ).Service( appId ); + IntegrationEventBus = new DaprIntegrationEventBus( client, options, loggerFactory, serviceProvider ); + var keyGenerator = serviceProvider.GetService(); + StateManager = new DaprStateManager( client, options, loggerFactory, keyGenerator ); + } + + /// + /// Http客户端 + /// + public IHttpClient HttpClient { get; } + + /// + /// WebApi服务调用 + /// + public IServiceInvocation ServiceInvocation { get; } + + /// + /// 集成事件总线 + /// + public IIntegrationEventBus IntegrationEventBus { get; } + + /// + /// 状态管理 + /// + public IStateManager StateManager { get; } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/DaprMicroserviceClientFactory.cs b/src/Util.Microservices.Dapr/DaprMicroserviceClientFactory.cs new file mode 100644 index 000000000..d95574423 --- /dev/null +++ b/src/Util.Microservices.Dapr/DaprMicroserviceClientFactory.cs @@ -0,0 +1,124 @@ +namespace Util.Microservices.Dapr; + +/// +/// Dapr微服务客户端工厂 +/// +public class DaprMicroserviceClientFactory : IMicroserviceClientFactory { + /// + /// Dapr客户端 + /// + private readonly DaprClient _client; + /// + /// 应用标识 + /// + private string _appId; + /// + /// Dapr Http端口 + /// + private int _daprHttpPort; + /// + /// Dapr Grpc端口 + /// + private int _daprGrpcPort; + /// + /// Dapr配置 + /// + private readonly IOptions _options; + /// + /// 日志工厂 + /// + private readonly ILoggerFactory _loggerFactory; + /// + /// 服务提供器 + /// + private readonly IServiceProvider _serviceProvider; + + /// + /// 初始化Dapr微服务客户端生成器 + /// + /// Dapr客户端 + /// Dapr配置 + /// 日志工厂 + /// 服务提供器 + public DaprMicroserviceClientFactory( DaprClient client, IOptions options, ILoggerFactory loggerFactory, IServiceProvider serviceProvider ) { + _client = client; + _options = options; + _loggerFactory = loggerFactory; + _serviceProvider = serviceProvider; + } + + /// + /// 设置应用标识 + /// + /// 应用标识 + public void AppId( string appId ) { + _appId = appId; + } + + /// + /// 设置Dapr Http端口 + /// + /// Dapr Http端口 + public void DaprHttpPort( int daprHttpPort ) { + _daprHttpPort = daprHttpPort; + } + + /// + /// 设置Dapr Grpc端口 + /// + /// Dapr Grpc端口 + public void DaprGrpcPort( int daprGrpcPort ) { + _daprGrpcPort = daprGrpcPort; + } + + /// + public virtual IMicroserviceClient Create() { + var client = CreateDaprClient(); + var httpClient = CreateHttpClient(); + return new DaprMicroserviceClient( client, httpClient, _options, _appId, _loggerFactory, _serviceProvider ); + } + + /// + /// 创建Dapr客户端 + /// + protected virtual DaprClient CreateDaprClient() { + var httpEndpoint = GetDaprHttpEndpoint(); + var grpcEndpoint = GetDaprGrpcEndpoint(); + if( httpEndpoint.IsEmpty() && grpcEndpoint.IsEmpty() && _client != null ) + return _client; + var builder = new DaprClientBuilder(); + if ( httpEndpoint.IsEmpty() == false ) + builder.UseHttpEndpoint( httpEndpoint ); + if ( grpcEndpoint.IsEmpty() == false ) + builder.UseGrpcEndpoint( grpcEndpoint ); + return builder.Build(); + } + + /// + /// 获取Dapr Http端点 + /// + protected virtual string GetDaprHttpEndpoint() { + if ( _daprHttpPort == 0 ) + _daprHttpPort = Util.Helpers.Environment.GetEnvironmentVariable( "DAPR_HTTP_ENDPOINT" ); + return _daprHttpPort > 0 ? $"http://localhost:{_daprHttpPort}" : null; + } + + /// + /// 获取Dapr Grpc端点 + /// + protected virtual string GetDaprGrpcEndpoint() { + if ( _daprGrpcPort == 0 ) + _daprGrpcPort = Util.Helpers.Environment.GetEnvironmentVariable( "DAPR_GRPC_ENDPOINT" ); + return _daprGrpcPort > 0 ? $"http://localhost:{_daprGrpcPort}" : null; + } + + /// + /// 创建Dapr Http客户端 + /// + protected virtual HttpClient CreateHttpClient() { + if ( _appId.IsEmpty() ) + return DaprClient.CreateInvokeHttpClient(); + var endpoint = GetDaprHttpEndpoint(); + return endpoint.IsEmpty() ? DaprClient.CreateInvokeHttpClient( _appId ) : DaprClient.CreateInvokeHttpClient( _appId, endpoint ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/DaprOptions.cs b/src/Util.Microservices.Dapr/DaprOptions.cs new file mode 100644 index 000000000..32b5a9880 --- /dev/null +++ b/src/Util.Microservices.Dapr/DaprOptions.cs @@ -0,0 +1,31 @@ +namespace Util.Microservices.Dapr; + +/// +/// Dapr配置 +/// +public class DaprOptions { + /// + /// 初始化Dapr配置 + /// + public DaprOptions() { + ServiceInvocation = new ServiceInvocationOptions(); + Pubsub = new PubsubOptions(); + } + + /// + /// 应用标识 + /// + public string AppId { get; set; } + /// + /// 机密存储器名称 + /// + public string SecretStoreName { get; set; } + /// + /// 服务调用配置 + /// + public ServiceInvocationOptions ServiceInvocation { get; set; } + /// + /// 发布订阅配置 + /// + public PubsubOptions Pubsub { get; set; } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/Events/CloudEvent.cs b/src/Util.Microservices.Dapr/Events/CloudEvent.cs new file mode 100644 index 000000000..31d22b7ae --- /dev/null +++ b/src/Util.Microservices.Dapr/Events/CloudEvent.cs @@ -0,0 +1,50 @@ +namespace Util.Microservices.Dapr.Events; + +/// +/// 云事件消息 +/// +/// 内容数据类型 +public class CloudEvent : CloudEvent { + /// + /// 初始化云事件消息 + /// + /// 事件标识 + /// 事件数据 + public CloudEvent( string id, TData data ) { + Id = id.IsEmpty() ? Guid.NewGuid().ToString() : id; + Data = data; + DataContentType = "application/cloudevents+json"; + Headers = new Dictionary(); + } + + /// + /// 事件标识 + /// + [JsonPropertyName( "id" )] + public string Id { get; } + + /// + /// 事件数据 + /// + [JsonPropertyName( "data" )] + public TData Data { get; set; } + + /// + /// 事件标头 + /// + [JsonPropertyName( "headers" )] + public Dictionary Headers { get; set; } + + /// + /// 事件数据内容类型 + /// + [JsonPropertyName( "datacontenttype" )] + public string DataContentType { get; init; } + + /// + /// 获取事件数据 + /// + public T GetData() { + return (T)(object)Data; + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/Events/CloudEventHeadersMiddleware.cs b/src/Util.Microservices.Dapr/Events/CloudEventHeadersMiddleware.cs new file mode 100644 index 000000000..74a4d2246 --- /dev/null +++ b/src/Util.Microservices.Dapr/Events/CloudEventHeadersMiddleware.cs @@ -0,0 +1,50 @@ +namespace Util.Microservices.Dapr.Events; + +/// +/// 云事件标头处理中间件 +/// +public class CloudEventHeadersMiddleware { + /// + /// 中间件管道 + /// + private readonly RequestDelegate _next; + + /// + /// 初始化云事件标头处理中间件 + /// + /// 中间件管道 + public CloudEventHeadersMiddleware( RequestDelegate next ) { + _next = next; + } + + /// + /// 执行管道 + /// + /// Http上下文 + public async Task InvokeAsync( HttpContext httpContext ) { + if ( _next == null ) + return; + if ( httpContext == null ) { + await _next( httpContext ); + return; + } + await ImportHeaders( httpContext ); + await _next( httpContext ); + } + + /// + /// 导入请求头 + /// + protected virtual async Task ImportHeaders( HttpContext httpContext ) { + if ( httpContext.Request.ContentType != "application/cloudevents+json" ) + return; + httpContext.Request.EnableBuffering(); + var jsonElement = await JsonSerializer.DeserializeAsync( httpContext.Request.Body ); + httpContext.Request.Body.Seek( 0, SeekOrigin.Begin ); + if ( jsonElement.TryGetProperty( "headers", out var headers ) == false ) + return; + var result = headers.Deserialize>(); + foreach ( var item in result ) + httpContext.Request.Headers[item.Key] = item.Value; + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/Events/DaprEventBus.cs b/src/Util.Microservices.Dapr/Events/DaprEventBus.cs new file mode 100644 index 000000000..30e828c6b --- /dev/null +++ b/src/Util.Microservices.Dapr/Events/DaprEventBus.cs @@ -0,0 +1,34 @@ +namespace Util.Microservices.Dapr.Events; + +/// +/// Dapr事件总线 +/// +[Ioc(1)] +public class DaprEventBus : IEventBus { + /// + /// 本地事件总线 + /// + private readonly ILocalEventBus _localEventBus; + /// + /// 集成事件总线 + /// + private readonly IIntegrationEventBus _integrationEventBus; + + /// + /// 初始化Dapr事件总线 + /// + /// 本地事件总线 + /// 集成事件总线 + public DaprEventBus( ILocalEventBus localEventBus, IIntegrationEventBus integrationEventBus ) { + _localEventBus = localEventBus ?? throw new ArgumentNullException( nameof( localEventBus ) ); + _integrationEventBus = integrationEventBus ?? throw new ArgumentNullException( nameof( integrationEventBus ) ); + } + + /// + public async Task PublishAsync( TEvent @event, CancellationToken cancellationToken = default ) where TEvent : IEvent { + await _localEventBus.PublishAsync( @event, cancellationToken ); + if ( @event is not IIntegrationEvent integrationEvent ) + return; + await _integrationEventBus.PublishAsync( integrationEvent, cancellationToken ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/Events/DaprIntegrationEventBus.cs b/src/Util.Microservices.Dapr/Events/DaprIntegrationEventBus.cs new file mode 100644 index 000000000..f7e346152 --- /dev/null +++ b/src/Util.Microservices.Dapr/Events/DaprIntegrationEventBus.cs @@ -0,0 +1,404 @@ +using Util.Helpers; + +namespace Util.Microservices.Dapr.Events; + +/// +/// 基于Dapr的集成事件总线 +/// +public class DaprIntegrationEventBus : IIntegrationEventBus { + + #region 字段 + + /// + /// Dapr客户端 + /// + protected readonly DaprClient Client; + /// + /// 配置 + /// + protected DaprOptions Options; + /// + /// 日志操作 + /// + protected readonly ILogger Logger; + /// + /// 发布订阅回调操作 + /// + protected IPubsubCallback PubsubCallback; + /// + /// 集成事件管理器 + /// + protected IIntegrationEventManager EventManager; + /// + /// 发布订阅配置名称 + /// + protected string Pubsub; + /// + /// 事件主题 + /// + protected string TopicName; + /// + /// 事件类型 + /// + protected string CloudEventType; + /// + /// 请求头集合 + /// + protected Dictionary Headers; + /// + /// 导入请求头键名集合 + /// + protected List ImportHeaderKeys; + /// + /// 移除请求头键名集合 + /// + protected IList RemoveHeaderKeys; + /// + /// 元数据 + /// + protected Dictionary Metadatas; + /// + /// 发布前操作 + /// + protected Func, Dictionary, bool> OnBeforeAction; + /// + /// 发布后操作 + /// + protected Func, Dictionary, Task> OnAfterAction; + + #endregion + + #region 构造方法 + + /// + /// 初始化Dapr集成事件总线 + /// + /// Dapr客户端 + /// Dapr配置 + /// 日志工厂 + /// 服务提供器 + public DaprIntegrationEventBus( DaprClient client, IOptions options, ILoggerFactory loggerFactory, IServiceProvider serviceProvider ) { + Client = client ?? throw new ArgumentNullException( nameof( client ) ); + Options = options?.Value ?? new DaprOptions(); + Logger = loggerFactory?.CreateLogger( typeof( DaprEventBus ) ) ?? NullLogger.Instance; + serviceProvider.CheckNull( nameof( serviceProvider ) ); + PubsubCallback = serviceProvider.GetService() ?? NullPubsubCallback.Instance; + EventManager = serviceProvider.GetRequiredService(); + Headers = new Dictionary(); + ImportHeaderKeys = new List(); + RemoveHeaderKeys = new List(); + Metadatas = new Dictionary(); + } + + #endregion + + #region PubsubName + + /// + public IIntegrationEventBus PubsubName( string pubsubName ) { + Pubsub = pubsubName; + return this; + } + + #endregion + + #region Topic + + /// + public IIntegrationEventBus Topic( string topic ) { + TopicName = topic; + return this; + } + + #endregion + + #region Header + + /// + public IIntegrationEventBus Header( string key, string value ) { + if ( key.IsEmpty() ) + return this; + if ( Headers.ContainsKey( key ) ) + Headers.Remove( key ); + Headers.Add( key, value ); + return this; + } + + /// + public IIntegrationEventBus Header( IDictionary headers ) { + if ( headers == null ) + return this; + foreach ( var header in headers ) + Header( header.Key, header.Value ); + return this; + } + + #endregion + + #region ImportHeader + + /// + /// 从当前HttpContext导入请求头 + /// + /// 请求头键名 + public IIntegrationEventBus ImportHeader( string key ) { + if ( key.IsEmpty() ) + return this; + if ( ImportHeaderKeys.Contains( key ) == false ) + ImportHeaderKeys.Add( key ); + return this; + } + + /// + /// 从当前HttpContext导入请求头 + /// + /// 请求头键名集合 + public IIntegrationEventBus ImportHeader( IEnumerable keys ) { + if ( keys == null ) + return this; + foreach ( var key in keys ) + ImportHeader( key ); + return this; + } + + #endregion + + #region RemoveHeader + + /// + /// 移除请求头 + /// + /// 请求头键名 + public IIntegrationEventBus RemoveHeader( string key ) { + if ( key.IsEmpty() ) + return this; + if ( RemoveHeaderKeys.Contains( key ) == false ) + RemoveHeaderKeys.Add( key ); + return this; + } + + #endregion + + #region Metadata + + /// + public IIntegrationEventBus Metadata( string key, string value ) { + if ( key.IsEmpty() ) + return this; + if ( value.IsEmpty() ) + return this; + RemoveMetadata( key ); + Metadatas.Add( key, value ); + return this; + } + + /// + public IIntegrationEventBus Metadata( IDictionary metadata ) { + if ( metadata == null ) + return this; + foreach ( var item in metadata ) + Metadata( item.Key, item.Value ); + return this; + } + + #endregion + + #region RemoveMetadata + + /// + public IIntegrationEventBus RemoveMetadata( string key ) { + if ( key.IsEmpty() ) + return this; + if ( Metadatas.ContainsKey( key ) ) + Metadatas.Remove( key ); + return this; + } + + #endregion + + #region Type + + /// + public IIntegrationEventBus Type( string value ) { + CloudEventType = value; + return this; + } + + #endregion + + #region OnBefore + + /// + public IIntegrationEventBus OnBefore( Func, Dictionary, bool> action ) { + OnBeforeAction = action; + return this; + } + + #endregion + + #region OnAfter + + /// + public IIntegrationEventBus OnAfter( Func, Dictionary, Task> action ) { + OnAfterAction = action; + return this; + } + + #endregion + + #region PublishAsync + + /// + public virtual async Task PublishAsync( TEvent @event, CancellationToken cancellationToken = default ) where TEvent : IIntegrationEvent { + @event.CheckNull( nameof( @event ) ); + cancellationToken.ThrowIfCancellationRequested(); + ImportHeaders(); + Init( @event ); + if ( OnBeforeAction != null && OnBeforeAction( @event, Headers, Metadatas ) == false ) + return; + await PublishEventAsync( @event, cancellationToken ); + } + + /// + /// 导入请求头 + /// + protected virtual void ImportHeaders() { + RemoveHeaders(); + SetHeaders(); + } + + /// + /// 移除请求头 + /// + private void RemoveHeaders() { + foreach ( var key in RemoveHeaderKeys ) { + Headers.Remove( key ); + ImportHeaderKeys.Remove( key ); + Options.Pubsub.ImportHeaderKeys.Remove( key ); + } + } + + /// + /// 设置请求头 + /// + private void SetHeaders() { + var headers = GetImportHeaders(); + foreach ( var item in headers ) + Headers.TryAdd( item.Key, item.Value ); + } + + /// + /// 获取导入的请求头 + /// + private IDictionary GetImportHeaders() { + var result = new Dictionary(); + ImportHeaderKeys.AddRange( Options.Pubsub.ImportHeaderKeys ); + if ( ImportHeaderKeys.Count == 0 ) + return result; + var headers = Web.Request?.Headers; + if ( headers == null ) + return result; + foreach ( var key in ImportHeaderKeys.Distinct() ) { + if ( headers.TryGetValue( key, out var value ) ) + result.Add( key, value ); + } + return result; + } + + /// + /// 初始化 + /// + protected void Init( IIntegrationEvent integrationEvent ) { + InitPubsubName( integrationEvent ); + InitTopic( integrationEvent ); + } + + /// + /// 初始化发布订阅名称 + /// + protected void InitPubsubName( IIntegrationEvent integrationEvent ) { + if ( Pubsub.IsEmpty() == false ) + return; + Pubsub = PubsubNameAttribute.GetName( integrationEvent.GetType() ); + } + + /// + /// 初始化事件主题 + /// + protected void InitTopic( IIntegrationEvent integrationEvent ) { + if ( TopicName.IsEmpty() == false ) + return; + TopicName = TopicNameAttribute.GetName( integrationEvent.GetType() ); + } + + /// + /// 发布事件 + /// + protected virtual async Task PublishEventAsync( IIntegrationEvent @event, CancellationToken cancellationToken ) { + Logger.LogTrace( "Publishing event {@Event} to {PubsubName}.{Topic}", @event, Pubsub, TopicName ); + var cloudEvent = CreateCloudEvent( @event ); + var argument = CreatePubsubArgument( cloudEvent ); + await PubsubCallback.OnPublishBefore( argument, cancellationToken ); + await Client.PublishEventAsync( Pubsub, TopicName, cloudEvent, Metadatas, cancellationToken ); + await PubsubCallback.OnPublishAfter( argument, cancellationToken ); + if ( OnAfterAction != null ) + await OnAfterAction( @event, Headers, Metadatas ); + } + + /// + /// 创建云事件 + /// + protected CloudEvent CreateCloudEvent( IIntegrationEvent @event ) { + var result = new CloudEvent( @event.EventId, @event ) { + Type = CloudEventType, + Headers = Headers.Count > 0 ? Headers : null + }; + return result; + } + + /// + /// 创建发布订阅参数 + /// + protected PubsubArgument CreatePubsubArgument( CloudEvent cloudEvent ) { + return Metadatas.Count > 0 ? new PubsubArgument( Pubsub, TopicName, cloudEvent, Metadatas ) : new PubsubArgument( Pubsub, TopicName, cloudEvent ); + } + + #endregion + + #region RepublishAsync + + /// + public virtual async Task RepublishAsync( string eventId, CancellationToken cancellationToken = default ) { + if ( Options.Pubsub.EnableEventLog == false ) + return; + if ( eventId.IsEmpty() ) { + Logger.LogWarning( "重新发布集成事件失败,事件标识不能为空." ); + return; + } + var eventLog = await EventManager.GetAsync( eventId, cancellationToken ); + await RepublishAsync( eventLog, cancellationToken ); + } + + /// + /// 重新发布集成事件 + /// + /// 集成事件日志记录 + /// 取消令牌 + protected virtual async Task RepublishAsync( IntegrationEventLog eventLog, CancellationToken cancellationToken = default ) { + eventLog.CheckNull( nameof( eventLog ) ); + eventLog.SubscriptionLogs.ForEach( t => { + if ( t.State == SubscriptionState.Fail ) + t.RetryCount = 0; + } ); + await EventManager.SaveAsync( eventLog, cancellationToken ); + var json = Util.Helpers.Json.ToJson( eventLog.Value ); + var argument = Util.Helpers.Json.ToObject>>( json ); + var cloudEvent = argument.GetEventData>(); + Logger.LogTrace( "Republishing event {@Event} to {PubsubName}.{Topic}", cloudEvent.Data, eventLog.PubsubName, eventLog.Topic ); + await PubsubCallback.OnRepublishBefore( eventLog, cancellationToken ); + await Client.PublishEventAsync( eventLog.PubsubName, eventLog.Topic, cloudEvent, argument.Metadata ?? new Dictionary(), cancellationToken ); + await PubsubCallback.OnRepublishAfter( eventLog, cancellationToken ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/Events/EventLogPubsubCallback.cs b/src/Util.Microservices.Dapr/Events/EventLogPubsubCallback.cs new file mode 100644 index 000000000..c1546eba6 --- /dev/null +++ b/src/Util.Microservices.Dapr/Events/EventLogPubsubCallback.cs @@ -0,0 +1,56 @@ +namespace Util.Microservices.Dapr.Events; + +/// +/// 发布订阅回调操作 - 集成事件日志记录 +/// +public class EventLogPubsubCallback : IPubsubCallback { + /// + /// 初始化发布订阅回调操作 + /// + /// 集成事件管理器 + public EventLogPubsubCallback( IIntegrationEventManager manager ) { + Manager = manager ?? throw new ArgumentNullException( nameof( manager ) ); + } + + /// + /// 集成事件管理器 + /// + protected IIntegrationEventManager Manager; + + /// + public virtual Task OnPublishBefore( PubsubArgument argument, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } + + /// + public virtual async Task OnPublishAfter( PubsubArgument argument, CancellationToken cancellationToken = default ) { + var result = await Manager.CreatePublishLogAsync( argument, cancellationToken ); + if ( result == NullIntegrationEventLog.Instance ) + return; + await Manager.IncrementAsync( cancellationToken ); + } + + /// + public virtual async Task OnSubscriptionBefore( IntegrationEventLog eventLog, string routeUrl, CancellationToken cancellationToken = default ) { + await Manager.CreateSubscriptionLogAsync( eventLog, routeUrl, cancellationToken ); + } + + /// + public virtual async Task OnSubscriptionAfter( string eventId, bool isSuccess,string message, CancellationToken cancellationToken = default ) { + if ( isSuccess ) { + await Manager.SubscriptionSuccessAsync( eventId, cancellationToken ); + return; + } + await Manager.SubscriptionFailAsync( eventId, message, cancellationToken ); + } + + /// + public virtual Task OnRepublishBefore( IntegrationEventLog eventLog, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } + + /// + public virtual Task OnRepublishAfter( IntegrationEventLog eventLog, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/Events/Filters/SubscriptionErroLogFilterAttribute.cs b/src/Util.Microservices.Dapr/Events/Filters/SubscriptionErroLogFilterAttribute.cs new file mode 100644 index 000000000..8cc7e678e --- /dev/null +++ b/src/Util.Microservices.Dapr/Events/Filters/SubscriptionErroLogFilterAttribute.cs @@ -0,0 +1,23 @@ +using Util.Aop; + +namespace Util.Microservices.Dapr.Events.Filters; + +/// +/// 集成事件订阅错误日志过滤器 +/// +public class SubscriptionErrorLogFilterAttribute : ExceptionFilterAttribute { + /// + /// 异常处理 + /// + public override void OnException( ExceptionContext context ) { + if( context == null ) + return; + var log = context.HttpContext.RequestServices.GetService>(); + var exception = context.Exception.GetRawException(); + if( exception is Warning warning ) { + log.LogWarning( warning, $"集成事件订阅处理失败:{exception.Message}" ); + return; + } + log.LogError( exception, $"集成事件订阅处理失败:{exception.Message}" ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/Events/Filters/SubscriptionExceptionHandlerAttribute.cs b/src/Util.Microservices.Dapr/Events/Filters/SubscriptionExceptionHandlerAttribute.cs new file mode 100644 index 000000000..8d0c1c9da --- /dev/null +++ b/src/Util.Microservices.Dapr/Events/Filters/SubscriptionExceptionHandlerAttribute.cs @@ -0,0 +1,27 @@ +using Util.Aop; + +namespace Util.Microservices.Dapr.Events.Filters; + +/// +/// 集成事件订阅异常处理过滤器 +/// +public class SubscriptionExceptionHandlerAttribute : ExceptionFilterAttribute { + /// + /// 异常处理 + /// + public override void OnException( ExceptionContext context ) { + context.ExceptionHandled = true; + var exception = context.Exception.GetRawException(); + if ( exception == null ) + return; + if ( exception is ConcurrencyException concurrencyException ) { + context.Result = PubsubResult.Fail( concurrencyException.Message ); + return; + } + if ( exception is Warning warning ) { + context.Result = PubsubResult.Drop( warning.Message ); + return; + } + context.Result = PubsubResult.Fail( Warning.GetMessage( exception ) ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/Events/Filters/SubscriptionFilterAttribute.cs b/src/Util.Microservices.Dapr/Events/Filters/SubscriptionFilterAttribute.cs new file mode 100644 index 000000000..261e14b46 --- /dev/null +++ b/src/Util.Microservices.Dapr/Events/Filters/SubscriptionFilterAttribute.cs @@ -0,0 +1,96 @@ +using Microsoft.AspNetCore.Http.Extensions; + +namespace Util.Microservices.Dapr.Events.Filters; + +/// +/// 集成事件订阅处理过滤器 +/// +public class SubscriptionFilterAttribute : ActionFilterAttribute { + /// + /// 执行 + /// + public override async Task OnActionExecutionAsync( ActionExecutingContext context, ActionExecutionDelegate next ) { + if ( context == null ) + return; + if ( next == null ) + return; + if ( context.HttpContext.Request.ContentType != "application/cloudevents+json" ) + return; + var log = context.HttpContext.RequestServices.GetService>() ?? NullLogger.Instance; + try { + var body = await GetBodyJsonElement( context ); + var routeUrl = context.HttpContext.Request.GetDisplayUrl(); + var eventId = GetEventId( body ); + if ( eventId.IsEmpty() ) { + log.LogError( "集成事件标识为空,body:{@body},routeUrl:{routeUrl}", body, routeUrl ); + return; + } + log.LogTrace( "准备处理集成事件订阅,eventId={@eventId},routeUrl={routeUrl}", eventId, routeUrl ); + var manager = context.HttpContext.RequestServices.GetRequiredService(); + var eventLog = await GetEventLog( manager, eventId ); + if ( eventLog == null ) { + log.LogError( "集成事件为空,eventId={@eventId},body:{@body},routeUrl:{routeUrl}", eventId, body, routeUrl ); + return; + } + if ( manager.CanSubscription( eventLog ) == false ) { + log.LogDebug( "集成事件订阅无需处理,eventId={@eventId},body:{@body},routeUrl={routeUrl}", eventId, body, routeUrl ); + if ( manager.IsSubscriptionSuccess( eventLog ) ) + context.Result = PubsubResult.Success; + return; + } + var callback = context.HttpContext.RequestServices.GetRequiredService(); + await callback.OnSubscriptionBefore( eventLog, routeUrl ); + var executedContext = await next(); + OnActionExecuted( executedContext ); + if ( executedContext.Result is PubsubResult result ) { + await callback.OnSubscriptionAfter( eventId, result == PubsubResult.Success, result.Message ); + return; + } + if ( executedContext.Exception != null ) { + await callback.OnSubscriptionAfter( eventId, false,Warning.GetMessage( executedContext.Exception ) ); + return; + } + } + catch ( Exception exception ) { + log.LogError( exception, "集成事件订阅处理过滤器执行失败." ); + throw; + } + } + + /// + /// 获取请求正文Json元素 + /// + protected async Task GetBodyJsonElement( ActionExecutingContext context ) { + var body = await GetBody( context ); + var client = context.HttpContext.RequestServices.GetService(); + return await Util.Helpers.Json.ToObjectAsync( body, client?.JsonSerializerOptions ); + } + + /// + /// 获取请求正文 + /// + protected async Task GetBody( ActionExecutingContext context ) { + context.HttpContext.Request.EnableBuffering(); + return await Util.Helpers.File.ReadToBytesAsync( context.HttpContext.Request.Body ); + } + + /// + /// 获取集成事件标识 + /// + protected string GetEventId( JsonElement body ) { + return body.TryGetProperty( "eventId", out var eventId ) ? eventId.Deserialize() : null; + } + + /// + /// 获取集成事件 + /// + protected async Task GetEventLog( IIntegrationEventManager manager,string eventId ) { + for ( var i = 0; i < 100; i++ ) { + var eventLog = await manager.GetAsync( eventId ); + if ( eventLog != null ) + return eventLog; + await Task.Delay( 100 ); + } + return null; + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/Events/GetAppIdService.cs b/src/Util.Microservices.Dapr/Events/GetAppIdService.cs new file mode 100644 index 000000000..7de6923fc --- /dev/null +++ b/src/Util.Microservices.Dapr/Events/GetAppIdService.cs @@ -0,0 +1,37 @@ +using Microsoft.AspNetCore.Hosting; + +namespace Util.Microservices.Dapr.Events; + +/// +/// 获取应用标识服务 +/// +public class GetAppIdService : IGetAppId { + /// + /// Dapr配置 + /// + private readonly DaprOptions _options; + /// + /// 主机环境 + /// + private readonly IWebHostEnvironment _environment; + + /// + /// 初始化获取应用标识服务 + /// + /// Dapr配置 + /// 主机环境 + public GetAppIdService( IOptions options, IWebHostEnvironment environment = null ) { + _options = options?.Value ?? new DaprOptions(); + _environment = environment; + } + + /// + /// 获取应用标识 + /// + public string GetAppId() { + var result = DaprHelper.GetAppId(); + if ( result.IsEmpty() == false ) + return result; + return _options.AppId.IsEmpty() ? _environment?.ApplicationName : _options.AppId; + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/Events/IGetAppId.cs b/src/Util.Microservices.Dapr/Events/IGetAppId.cs new file mode 100644 index 000000000..8b436a1d1 --- /dev/null +++ b/src/Util.Microservices.Dapr/Events/IGetAppId.cs @@ -0,0 +1,11 @@ +namespace Util.Microservices.Dapr.Events; + +/// +/// 获取应用标识 +/// +public interface IGetAppId : ISingletonDependency { + /// + /// 获取应用标识 + /// + string GetAppId(); +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/Events/IntegrationEventCount.cs b/src/Util.Microservices.Dapr/Events/IntegrationEventCount.cs new file mode 100644 index 000000000..5626b2878 --- /dev/null +++ b/src/Util.Microservices.Dapr/Events/IntegrationEventCount.cs @@ -0,0 +1,17 @@ +namespace Util.Microservices.Dapr.Events; + +/// +/// 集成事件计数 +/// +public class IntegrationEventCount : IDataKey, IETag { + /// + public string Id { get; set; } + + /// + public string ETag { get; set; } + + /// + /// 集成事件总条数 + /// + public int? Count { get; set; } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/Events/IntegrationEventLogStore.cs b/src/Util.Microservices.Dapr/Events/IntegrationEventLogStore.cs new file mode 100644 index 000000000..3a6e33890 --- /dev/null +++ b/src/Util.Microservices.Dapr/Events/IntegrationEventLogStore.cs @@ -0,0 +1,60 @@ +namespace Util.Microservices.Dapr.Events; + +/// +/// 集成事件日志记录存储器 +/// +public class IntegrationEventLogStore : IIntegrationEventLogStore { + /// + /// 初始化集成事件日志记录存储器 + /// + /// 状态管理操作 + /// 配置 + public IntegrationEventLogStore( IStateManager stateManager, IOptions options ) { + StateManager = stateManager ?? throw new ArgumentNullException( nameof( stateManager ) ); + StateManager.StoreName( options?.Value.Pubsub?.EventLogStoreName ?? "statestore" ); + } + + /// + /// 集成事件总条数键名 + /// + public const string KeyCount = "IntegrationEventCount"; + /// + /// 状态管理操作 + /// + protected IStateManager StateManager; + + /// + public virtual async Task GetAsync( string eventId, CancellationToken cancellationToken = default ) { + return await StateManager.GetByIdAsync( eventId, cancellationToken ); + } + + /// + public virtual async Task SaveAsync( IntegrationEventLog eventLog, CancellationToken cancellationToken = default ) { + await StateManager.SaveAsync( eventLog, cancellationToken ); + } + + /// + public virtual async Task IncrementAsync( CancellationToken cancellationToken = default ) { + var result = await GetIntegrationEventCount( cancellationToken ); + result.Count = result.Count.SafeValue() + 1; + await StateManager.SaveAsync( result, cancellationToken, KeyCount ); + } + + /// + /// 获取集成事件计数 + /// + protected async Task GetIntegrationEventCount( CancellationToken cancellationToken ) { + return await StateManager.GetAsync( KeyCount,cancellationToken ) ?? new IntegrationEventCount(); + } + + /// + public virtual async Task GetCountAsync( CancellationToken cancellationToken = default ) { + var result = await GetIntegrationEventCount( cancellationToken ); + return result.Count.SafeValue(); + } + + /// + public virtual async Task ClearCountAsync( CancellationToken cancellationToken = default ) { + await StateManager.RemoveAsync( KeyCount, cancellationToken ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/Events/IntegrationEventManager.cs b/src/Util.Microservices.Dapr/Events/IntegrationEventManager.cs new file mode 100644 index 000000000..3ffe00cb0 --- /dev/null +++ b/src/Util.Microservices.Dapr/Events/IntegrationEventManager.cs @@ -0,0 +1,455 @@ +using Util.Helpers; + +namespace Util.Microservices.Dapr.Events; + +/// +/// 集成事件管理器 +/// +public class IntegrationEventManager : IIntegrationEventManager { + + #region 字段 + + /// + /// 集成事件日志记录存储器 + /// + protected IIntegrationEventLogStore Store; + /// + /// 用户会话 + /// + protected Util.Sessions.ISession Session; + /// + /// 获取应用标识服务 + /// + protected IGetAppId GetAppIdService; + /// + /// 配置 + /// + protected DaprOptions Options; + /// + /// 日志 + /// + protected ILogger Log; + + #endregion + + #region 构造方法 + + /// + /// 初始化集成事件日志记录存储器 + /// + /// 集成事件日志记录存储器 + /// 用户会话 + /// 获取应用标识服务 + /// Dapr配置 + /// 日志 + public IntegrationEventManager( IIntegrationEventLogStore store, Util.Sessions.ISession session, IGetAppId getAppIdService, + IOptions options, ILogger logger ) { + Store = store ?? throw new ArgumentNullException( nameof( store ) ); + Session = session ?? Util.Sessions.NullSession.Instance; + GetAppIdService = getAppIdService ?? throw new ArgumentNullException( nameof( getAppIdService ) ); + Options = options?.Value ?? new DaprOptions(); + Log = logger ?? NullLogger.Instance; + } + + #endregion + + #region IncrementAsync + + /// + public virtual async Task IncrementAsync( CancellationToken cancellationToken = default ) { + try { + await Store.IncrementAsync( cancellationToken ); + } + catch ( ConcurrencyException ) { + Log.LogDebug( "更新集成事件计数出现并发异常,即将重试" ); + await IncrementAsync( cancellationToken ); + } + catch ( Exception exception ) { + Log.LogError( exception, "更新集成事件计数失败" ); + } + } + + #endregion + + #region GetCountAsync + + /// + public virtual async Task GetCountAsync( CancellationToken cancellationToken = default ) { + return await Store.GetCountAsync( cancellationToken ); + } + + #endregion + + #region ClearCountAsync + + /// + public virtual async Task ClearCountAsync( CancellationToken cancellationToken = default ) { + await Store.ClearCountAsync( cancellationToken ); + } + + #endregion + + #region GetAsync + + /// + public virtual async Task GetAsync( string eventId, CancellationToken cancellationToken = default ) { + if ( Options.Pubsub.EnableEventLog == false ) + return NullIntegrationEventLog.Instance; + return await Store.GetAsync( eventId, cancellationToken ); + } + + #endregion + + #region SaveAsync + + /// + /// 保存集成事件日志记录 + /// + /// 集成事件日志记录 + /// 取消令牌 + public virtual async Task SaveAsync( IntegrationEventLog eventLog, CancellationToken cancellationToken = default ) { + eventLog.CheckNull( nameof( eventLog ) ); + await Store.SaveAsync( eventLog, cancellationToken ); + } + + #endregion + + #region CanSubscriptionAsync + + /// + public async Task CanSubscriptionAsync( string eventId, CancellationToken cancellationToken = default ) { + if ( Options.Pubsub.EnableEventLog == false ) + return true; + var eventLog = await GetAsync( eventId, cancellationToken ); + return CanSubscription( eventLog ); + } + + #endregion + + #region CanSubscription + + /// + public virtual bool CanSubscription( IntegrationEventLog eventLog ) { + if ( Options.Pubsub.EnableEventLog == false ) + return true; + if ( eventLog == null ) + return false; + var subscriptionLog = GetSubscriptionLog( eventLog ); + if ( subscriptionLog == null ) + return true; + return subscriptionLog.State == SubscriptionState.Fail && subscriptionLog.RetryCount.SafeValue() < Options.Pubsub.MaxRetry; + } + + /// + /// 获取当前应用的订阅日志 + /// + protected SubscriptionLog GetSubscriptionLog( IntegrationEventLog eventLog ) { + var appId = GetAppId( eventLog.Id ); + return GetSubscriptionLog( eventLog,appId ); + } + + /// + /// 获取应用标识 + /// + protected string GetAppId( string eventId ) { + var appId = GetAppIdService.GetAppId(); + if ( appId.IsEmpty() ) + throw new Warning( $"应用标识不能为空,eventId:{eventId}" ); + return appId; + } + + /// + /// 获取当前应用的订阅日志 + /// + protected SubscriptionLog GetSubscriptionLog( IntegrationEventLog eventLog,string appId ) { + return eventLog.SubscriptionLogs.Find( t => t.AppId == appId ); + } + + #endregion + + #region IsSubscriptionSuccess + + /// + public virtual bool IsSubscriptionSuccess( IntegrationEventLog eventLog ) { + if ( Options.Pubsub.EnableEventLog == false ) + return true; + var subscriptionLog = GetSubscriptionLog( eventLog ); + return subscriptionLog is { State: SubscriptionState.Success }; + } + + #endregion + + #region CreatePublishLogAsync + + /// + public virtual async Task CreatePublishLogAsync( PubsubArgument argument, CancellationToken cancellationToken = default ) { + if ( Options.Pubsub.EnableEventLog == false ) + return NullIntegrationEventLog.Instance; + var result = CreateEventLog( argument ); + if ( result == null ) + return NullIntegrationEventLog.Instance; + await SaveAsync( result, cancellationToken ); + Log.LogDebug( "创建集成事件发布日志记录成功,EventLog={@EventLog}", result ); + return result; + } + + /// + /// 创建集成事件日志 + /// + protected virtual IntegrationEventLog CreateEventLog( PubsubArgument argument ) { + if ( argument.EventData is not CloudEvent cloudEvent ) + return null; + if ( cloudEvent.Data is not IIntegrationEvent integrationEvent ) + return null; + var appId = GetAppId( integrationEvent.EventId ); + if ( appId.IsEmpty() ) + throw new Warning( $"应用标识不能为空,eventId:{cloudEvent.Id}" ); + var result = new IntegrationEventLog { + Id = integrationEvent.EventId, + AppId = appId, + UserId = Session.UserId, + PubsubName = argument.Pubsub, + Topic = argument.Topic, + Value = argument, + State = EventState.Published, + PublishTime = integrationEvent.EventTime, + LastModificationTime = Time.Now + }; + return result; + } + + #endregion + + #region CreateSubscriptionLogAsync + + /// + public virtual async Task CreateSubscriptionLogAsync( string eventId, string routeUrl, CancellationToken cancellationToken = default ) { + if ( Options.Pubsub.EnableEventLog == false ) + return NullIntegrationEventLog.Instance; + if ( eventId.IsEmpty() ) { + Log.LogWarning( "创建集成事件订阅日志记录失败,事件标识不能为空,routeUrl={@routeUrl}", routeUrl ); + return NullIntegrationEventLog.Instance; + } + var eventLog = await GetAsync( eventId, cancellationToken ); + return await CreateSubscriptionLogAsync( eventLog, routeUrl, cancellationToken ); + } + + /// + public virtual async Task CreateSubscriptionLogAsync( IntegrationEventLog eventLog, string routeUrl, CancellationToken cancellationToken = default ) { + if ( Options.Pubsub.EnableEventLog == false ) + return NullIntegrationEventLog.Instance; + eventLog.CheckNull( nameof( eventLog ) ); + if ( CanSubscription( eventLog ) == false ) + return eventLog; + AddSubscriptionLog( eventLog, routeUrl ); + eventLog.State = EventState.Processing; + eventLog.LastModificationTime = Time.Now; + try { + await SaveAsync( eventLog, cancellationToken ); + Log.LogDebug( "创建集成事件订阅日志记录成功,EventLog={@EventLog}", ToDebugEventLog( eventLog ) ); + } + catch ( ConcurrencyException ) { + Log.LogDebug( "创建集成事件订阅日志记录出现并发异常,即将重试,eventId={eventId},routeUrl={routeUrl}", eventLog.Id, routeUrl ); + return await CreateSubscriptionLogAsync( eventLog.Id, routeUrl, cancellationToken ); + } + catch ( Exception exception ) { + Log.LogError( exception, "创建集成事件订阅日志记录失败,EventLog={@EventLog}", ToDebugEventLog( eventLog ) ); + } + return eventLog; + } + + /// + /// 转换为调试状态 + /// + protected IntegrationEventLog>> ToDebugEventLog( IntegrationEventLog eventLog ) { + var json = Util.Helpers.Json.ToJson( eventLog.Value ); + var result = new IntegrationEventLog>> { + Id = eventLog.Id, + UserId = eventLog.UserId, + AppId = eventLog.AppId, + LastModificationTime = eventLog.LastModificationTime, + DataType = eventLog.DataType, + ETag = eventLog.ETag, + PublishTime = eventLog.PublishTime, + State = eventLog.State, + PubsubName = eventLog.PubsubName, + Topic = eventLog.Topic, + SubscriptionLogs = eventLog.SubscriptionLogs, + Value = Util.Helpers.Json.ToObject>>( json ) + }; + if( result.Value is { EventData: not null } ) + result.Value.EventData.Data = Util.Helpers.Json.ToJson( result.Value.EventData.Data ); + return result; + } + + /// + /// 添加订阅日志 + /// + protected virtual void AddSubscriptionLog( IntegrationEventLog eventLog, string routeUrl ) { + var appId = GetAppId( eventLog.Id ); + var subscriptionLog = eventLog.SubscriptionLogs.Find( t => t.AppId == appId ); + if ( subscriptionLog == null ) { + subscriptionLog = CreateSubscriptionLog( appId, routeUrl ); + eventLog.SubscriptionLogs.Add( subscriptionLog ); + return; + } + subscriptionLog.State = SubscriptionState.Processing; + subscriptionLog.LastModificationTime = Time.Now; + subscriptionLog.RetryCount = subscriptionLog.RetryCount.SafeValue() + 1; + UpdateRetryTime( subscriptionLog ); + } + + /// + /// 创建订阅日志 + /// + protected virtual SubscriptionLog CreateSubscriptionLog( string appId, string routeUrl ) { + return new SubscriptionLog { + AppId = appId, + RouteUrl = routeUrl, + State = SubscriptionState.Processing, + SubscriptionTime = Time.Now, + LastModificationTime = Time.Now, + }; + } + + /// + /// 更新重试时间 + /// + protected virtual void UpdateRetryTime( SubscriptionLog subscriptionLog ) { + if ( subscriptionLog.RetryLogs.Count == 0 ) + return; + var retryLog = subscriptionLog.RetryLogs.MaxBy( t => t.Number ); + retryLog.RetryTime = Time.Now; + } + + #endregion + + #region SubscriptionSuccessAsync + + /// + public virtual async Task SubscriptionSuccessAsync( string eventId, CancellationToken cancellationToken = default ) { + if ( Options.Pubsub.EnableEventLog == false ) + return NullIntegrationEventLog.Instance; + if ( eventId.IsEmpty() ) { + Log.LogWarning( "SubscriptionSuccessAsync更新集成事件订阅日志记录失败,事件标识不能为空." ); + return NullIntegrationEventLog.Instance; + } + var eventLog = await GetAsync( eventId, cancellationToken ); + return await SubscriptionSuccessAsync( eventLog, cancellationToken ); + } + + /// + /// 集成事件订阅处理成功 + /// + /// 集成事件日志记录 + /// 取消令牌 + public virtual async Task SubscriptionSuccessAsync( IntegrationEventLog eventLog, CancellationToken cancellationToken = default ) { + if ( Options.Pubsub.EnableEventLog == false ) + return NullIntegrationEventLog.Instance; + eventLog.CheckNull( nameof( eventLog ) ); + var appId = GetAppId( eventLog.Id ); + var subscriptionLog = GetSubscriptionLog( eventLog, appId ); + if ( subscriptionLog == null ) { + Log.LogWarning( "更新集成事件订阅日志记录失败,未找到订阅日志记录,appId={appId},EventLog={@EventLog}", appId,ToDebugEventLog( eventLog ) ); + return NullIntegrationEventLog.Instance; + } + subscriptionLog.State = SubscriptionState.Success; + subscriptionLog.LastModificationTime = Time.Now; + eventLog.LastModificationTime = Time.Now; + UpdateEventLogState( eventLog ); + try { + await SaveAsync( eventLog, cancellationToken ); + Log.LogDebug( "集成事件订阅处理成功,appId={appId},EventLog={@EventLog}", appId,ToDebugEventLog( eventLog ) ); + } + catch ( ConcurrencyException ) { + Log.LogDebug( "更新集成事件订阅日志记录出现并发异常,即将重试,eventId={eventId},appId={appId}", eventLog.Id, appId ); + return await SubscriptionSuccessAsync( eventLog.Id, cancellationToken ); + } + catch ( Exception exception ) { + Log.LogError( exception, "更新集成事件订阅日志记录失败,appId={appId},EventLog={@EventLog}", appId,ToDebugEventLog( eventLog ) ); + } + return eventLog; + } + + /// + /// 更新集成事件日志状态 + /// + protected void UpdateEventLogState( IntegrationEventLog eventLog ) { + if ( eventLog.SubscriptionLogs.All( t => t.State == SubscriptionState.Success ) ) { + eventLog.State = EventState.Success; + return; + } + if ( eventLog.SubscriptionLogs.Any( t => t.State == SubscriptionState.Processing ) ) { + eventLog.State = EventState.Processing; + return; + } + eventLog.State = EventState.Fail; + } + + #endregion + + #region SubscriptionFailAsync + + /// + /// 集成事件订阅处理失败 + /// + /// 事件标识 + /// 消息 + /// 取消令牌 + public virtual async Task SubscriptionFailAsync( string eventId, string message, CancellationToken cancellationToken = default ) { + if ( Options.Pubsub.EnableEventLog == false ) + return NullIntegrationEventLog.Instance; + if ( eventId.IsEmpty() ) { + Log.LogWarning( "SubscriptionFailAsync更新集成事件订阅日志记录失败,事件标识不能为空." ); + return NullIntegrationEventLog.Instance; + } + var eventLog = await GetAsync( eventId, cancellationToken ); + return await SubscriptionFailAsync( eventLog, message, cancellationToken ); + } + + /// + /// 集成事件订阅处理失败 + /// + /// 集成事件日志记录 + /// 消息 + /// 取消令牌 + public virtual async Task SubscriptionFailAsync( IntegrationEventLog eventLog, string message, CancellationToken cancellationToken = default ) { + if ( Options.Pubsub.EnableEventLog == false ) + return NullIntegrationEventLog.Instance; + eventLog.CheckNull( nameof( eventLog ) ); + var appId = GetAppId( eventLog.Id ); + var subscriptionLog = GetSubscriptionLog( eventLog, appId ); + if ( subscriptionLog == null ) { + Log.LogWarning( "更新集成事件订阅日志记录失败,未找到订阅日志记录,appId={appId},EventLog={@EventLog}", appId, ToDebugEventLog( eventLog ) ); + return NullIntegrationEventLog.Instance; + } + subscriptionLog.State = SubscriptionState.Fail; + subscriptionLog.LastModificationTime = Time.Now; + subscriptionLog.RetryLogs.Add( CreateSubscriptionRetryLog( subscriptionLog, message ) ); + eventLog.LastModificationTime = Time.Now; + UpdateEventLogState( eventLog ); + try { + await SaveAsync( eventLog, cancellationToken ); + Log.LogDebug( "集成事件订阅处理失败,appId={appId},EventLog={@EventLog}", appId, ToDebugEventLog( eventLog ) ); + } + catch ( ConcurrencyException ) { + Log.LogDebug( "更新集成事件订阅日志记录出现并发异常,即将重试,eventId={eventId},appId={appId}", eventLog.Id, appId ); + return await SubscriptionFailAsync( eventLog.Id, message, cancellationToken ); + } + catch ( Exception exception ) { + Log.LogError( exception, "更新集成事件订阅日志记录失败,appId={appId},EventLog={@EventLog}", appId, ToDebugEventLog( eventLog ) ); + } + return eventLog; + } + + /// + /// 创建订阅重试记录 + /// + protected SubscriptionRetryLog CreateSubscriptionRetryLog( SubscriptionLog subscriptionLog, string message ) { + var retryLog = subscriptionLog.RetryLogs.MaxBy( t => t.Number ); + var maxNumber = retryLog?.Number ?? 0; + return new SubscriptionRetryLog { Number = maxNumber + 1, Message = message }; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/Events/NullPubsubCallback.cs b/src/Util.Microservices.Dapr/Events/NullPubsubCallback.cs new file mode 100644 index 000000000..c3aa49cc9 --- /dev/null +++ b/src/Util.Microservices.Dapr/Events/NullPubsubCallback.cs @@ -0,0 +1,42 @@ +namespace Util.Microservices.Dapr.Events; + +/// +/// 空发布订阅回调操作 +/// +[Ioc(-9)] +public class NullPubsubCallback : IPubsubCallback { + /// + /// 空发布订阅回调操作实例 + /// + public static readonly IPubsubCallback Instance = new NullPubsubCallback(); + + /// + public Task OnPublishBefore( PubsubArgument argument, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } + + /// + public Task OnPublishAfter( PubsubArgument argument, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } + + /// + public Task OnSubscriptionBefore( IntegrationEventLog integrationEventLog, string routeUrl, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } + + /// + public Task OnSubscriptionAfter( string eventId, bool isSuccess,string message, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } + + /// + public Task OnRepublishBefore( IntegrationEventLog eventLog, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } + + /// + public Task OnRepublishAfter( IntegrationEventLog eventLog, CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/Events/PubsubResult.cs b/src/Util.Microservices.Dapr/Events/PubsubResult.cs new file mode 100644 index 000000000..931a25a78 --- /dev/null +++ b/src/Util.Microservices.Dapr/Events/PubsubResult.cs @@ -0,0 +1,65 @@ +namespace Util.Microservices.Dapr.Events; + +/// +/// Dapr发布订阅结果 +/// +public class PubsubResult : JsonResult { + /// + /// 状态 + /// + public string Status { get; } + + /// + /// 消息 + /// + public string Message { get; } + + /// + /// 初始化发布订阅结果 + /// + /// 状态 + /// 消息 + public PubsubResult( string status, string message = null ) : base( null ) { + Status = status; + Message = message; + SerializerSettings = GetOptions(); + } + + /// + /// 获取Json序列化配置 + /// + private JsonSerializerOptions GetOptions() { + return new JsonSerializerOptions { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + } + + /// + /// 执行结果 + /// + public override Task ExecuteResultAsync( ActionContext context ) { + Value = new { Status }; + return base.ExecuteResultAsync( context ); + } + + /// + /// 成功 + /// + public static readonly PubsubResult Success = new( "SUCCESS" ); + + /// + /// 失败并重试 + /// + /// 错误消息 + public static PubsubResult Fail( string message ) { + return new PubsubResult( "RETRY", message ); + } + + /// + /// 失败并删除消息 + /// + /// 错误消息 + public static PubsubResult Drop( string message ) { + return new PubsubResult( "DROP", message ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/Events/TopicAttribute.cs b/src/Util.Microservices.Dapr/Events/TopicAttribute.cs new file mode 100644 index 000000000..fba6e12a4 --- /dev/null +++ b/src/Util.Microservices.Dapr/Events/TopicAttribute.cs @@ -0,0 +1,34 @@ +using Topic = Dapr.TopicAttribute; + +namespace Util.Microservices.Dapr.Events; + +/// +/// 事件主题 +/// +[AttributeUsage( AttributeTargets.All, AllowMultiple = true )] +public class TopicAttribute : Topic { + /// + /// 初始化事件主题 + /// + /// 事件主题 + public TopicAttribute( string name ) : base( "pubsub", name ) { + } + + /// + /// 初始化事件主题 + /// + /// 发布订阅组件名称 + /// 事件主题 + public TopicAttribute( string pubsubName, string name ) : base( pubsubName, name ) { + } + + /// + /// 初始化事件主题 + /// + /// 发布订阅组件名称 + /// 事件主题 + /// 匹配规则,范例: "event.type ==\"v1\"" + /// 匹配优先级 + public TopicAttribute( string pubsubName, string name, string match, int priority ) : base( pubsubName, name, match, priority ) { + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/IntegrationEventControllerBase.cs b/src/Util.Microservices.Dapr/IntegrationEventControllerBase.cs new file mode 100644 index 000000000..e6d258431 --- /dev/null +++ b/src/Util.Microservices.Dapr/IntegrationEventControllerBase.cs @@ -0,0 +1,42 @@ +using Util.Microservices.Dapr.Events; +using Util.Microservices.Dapr.Events.Filters; + +namespace Util.Microservices.Dapr; + +/// +/// 集成事件控制器基类 +/// +[ApiController] +[SubscriptionFilter] +[SubscriptionExceptionHandler( Order = 1 )] +[SubscriptionErrorLogFilter( Order = 2 )] +[Route( "api/[controller]" )] +public abstract class IntegrationEventControllerBase : ControllerBase { + /// + /// 会话 + /// + protected virtual Util.Sessions.ISession Session => Util.Sessions.UserSession.Instance; + + /// + /// 返回成功消息 + /// + protected IActionResult Success() { + return PubsubResult.Success; + } + + /// + /// 失败并重试 + /// + /// 错误消息 + protected IActionResult Fail( string message ) { + return PubsubResult.Fail( message ); + } + + /// + /// 失败并删除消息 + /// + /// 错误消息 + protected IActionResult Drop( string message ) { + return PubsubResult.Drop( message ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/MicroserviceClientFactoryExtensions.cs b/src/Util.Microservices.Dapr/MicroserviceClientFactoryExtensions.cs new file mode 100644 index 000000000..b8f702a19 --- /dev/null +++ b/src/Util.Microservices.Dapr/MicroserviceClientFactoryExtensions.cs @@ -0,0 +1,39 @@ +namespace Util.Microservices.Dapr; + +/// +/// 微服务客户端工厂扩展 +/// +public static class MicroserviceClientFactoryExtensions { + /// + /// 设置应用标识 + /// + /// 微服务客户端工厂 + /// 应用标识 + public static IMicroserviceClientFactory AppId( this IMicroserviceClientFactory source,string appId ) { + if ( source is DaprMicroserviceClientFactory factory ) + factory.AppId( appId ); + return source; + } + + /// + /// 设置Dapr Http端口 + /// + /// 微服务客户端工厂 + /// Dapr Http端口 + public static IMicroserviceClientFactory DaprHttpPort( this IMicroserviceClientFactory source, int daprHttpPort ) { + if ( source is DaprMicroserviceClientFactory factory ) + factory.DaprHttpPort( daprHttpPort ); + return source; + } + + /// + /// 设置Dapr Grpc端口 + /// + /// 微服务客户端工厂 + /// Dapr Grpc端口 + public static IMicroserviceClientFactory DaprGrpcPort( this IMicroserviceClientFactory source, int daprGrpcPort ) { + if ( source is DaprMicroserviceClientFactory factory ) + factory.DaprGrpcPort( daprGrpcPort ); + return source; + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/PubsubOptions.cs b/src/Util.Microservices.Dapr/PubsubOptions.cs new file mode 100644 index 000000000..712e8a48f --- /dev/null +++ b/src/Util.Microservices.Dapr/PubsubOptions.cs @@ -0,0 +1,39 @@ +namespace Util.Microservices.Dapr; + +/// +/// 发布订阅配置 +/// +public class PubsubOptions { + /// + /// 初始化发布订阅配置 + /// + public PubsubOptions() { + EventLogStoreName = "event-state-store"; + ImportHeaderKeys = new List { + "Authorization", + "x-correlation-id" + }; + EnableEventLog = true; + MaxRetry = 3; + } + + /// + /// 是否启用集成事件日志记录,默认值: true + /// + public bool EnableEventLog { get; set; } + + /// + /// 集成事件日志记录状态存储组件名称,默认值: event-state-store + /// + public string EventLogStoreName { get; set; } + + /// + /// 需要导入的请求头键名集合,从当前Http上下文导入 + /// + public IList ImportHeaderKeys { get; set; } + + /// + /// 最大订阅失败重试次数,默认值: 3 + /// + public int MaxRetry { get; set; } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/ServiceInvocationOptions.cs b/src/Util.Microservices.Dapr/ServiceInvocationOptions.cs new file mode 100644 index 000000000..a972567d7 --- /dev/null +++ b/src/Util.Microservices.Dapr/ServiceInvocationOptions.cs @@ -0,0 +1,63 @@ +using Util.Microservices.Dapr.ServiceInvocations; + +namespace Util.Microservices.Dapr; + +/// +/// 服务调用配置 +/// +public class ServiceInvocationOptions { + /// + /// 初始化服务调用配置 + /// + public ServiceInvocationOptions() { + RequestFilters = new List(); + ResponseFilters = new List(); + ImportHeaderKeys = new List { + "Authorization", + "x-correlation-id", + "Content-Language" + }; + IsUnpackResult = true; + } + + /// + /// 是否使用ServiceResult对结果解包 + /// + public bool IsUnpackResult { get; set; } + /// + /// 服务状态码转换事件,参数为业务状态码或Http状态码,取决于是否使用约定结果类型 + /// + public Func OnState { get; set; } + /// + /// 调用前事件 + /// + public Func OnBefore { get; set; } + /// + /// 调用成功事件 + /// + public Func OnSuccess { get; set; } + /// + /// 调用失败事件 + /// + public Func OnFail { get; set; } + /// + /// 调用未授权事件 + /// + public Func OnUnauthorized { get; set; } + /// + /// 调用完成事件 + /// + public Func OnComplete { get; set; } + /// + /// 请求过滤器集合 + /// + public IList RequestFilters { get; set; } + /// + /// 响应过滤器集合 + /// + public IList ResponseFilters { get; set; } + /// + /// 需要导入的请求头键名集合,从当前Http上下文导入 + /// + public IList ImportHeaderKeys { get; set; } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/ServiceInvocations/DaprServiceInvocation.cs b/src/Util.Microservices.Dapr/ServiceInvocations/DaprServiceInvocation.cs new file mode 100644 index 000000000..f85622570 --- /dev/null +++ b/src/Util.Microservices.Dapr/ServiceInvocations/DaprServiceInvocation.cs @@ -0,0 +1,544 @@ +namespace Util.Microservices.Dapr.ServiceInvocations; + +/// +/// Dapr WebApi服务调用操作 +/// +public class DaprServiceInvocation : DaprServiceInvocationBase, IServiceInvocation { + + #region 构造方法 + + /// + /// 初始化Dapr WebApi服务调用操作 + /// + /// Dapr客户端 + /// Dapr配置 + /// 日志工厂 + public DaprServiceInvocation( DaprClient client, IOptions options, ILoggerFactory loggerFactory ) : base( client, options, loggerFactory ) { + } + + #endregion + + #region 事件 + + /// + /// 调用前事件 + /// + protected Func OnBeforeAction { get; set; } + /// + /// 响应结果转换事件 + /// + protected Func> OnResultAction { get; set; } + /// + /// 调用后事件 + /// + protected Action OnAfterAction { get; set; } + /// + /// 调用成功事件 + /// + protected Func OnSuccessAction { get; set; } + /// + /// 调用失败事件 + /// + protected Func OnFailAction { get; set; } + /// + /// 调用未授权事件 + /// + protected Func OnUnauthorizedAction { get; set; } + /// + /// 调用完成事件 + /// + protected Func OnCompleteAction { get; set; } + + /// + public IServiceInvocation OnBefore( Func action ) { + OnBeforeAction = action; + return this; + } + + /// + /// 响应结果转换事件 + /// + /// 响应结果转换操作 + public IServiceInvocation OnResult( Func> action ) { + OnResultAction = action; + return this; + } + + /// + /// 调用后事件 + /// + /// 调用后处理操作 + public IServiceInvocation OnAfter( Action action ) { + OnAfterAction = action; + return this; + } + + /// + public IServiceInvocation OnSuccess( Func action ) { + if ( action != null ) + OnSuccessAction = ( request, response, result ) => action( request, response, (TResponse)result ); + return this; + } + + /// + /// 调用失败事件 + /// + /// 调用失败处理操作 + public IServiceInvocation OnFail( Func action ) { + OnFailAction = action; + return this; + } + + /// + /// 调用未授权事件 + /// + /// 调用未授权处理操作 + public IServiceInvocation OnUnauthorized( Func action ) { + OnUnauthorizedAction = action; + return this; + } + + /// + /// 调用完成事件 + /// + /// 调用完成处理操作 + public IServiceInvocation OnComplete( Func action ) { + OnCompleteAction = action; + return this; + } + + #endregion + + #region 方法调用重载 + + /// + public async Task InvokeAsync( string methodName, CancellationToken cancellationToken = default ) { + await InvokeAsync( methodName, HttpMethod.Get, cancellationToken ); + } + + /// + public async Task InvokeAsync( string methodName, HttpMethod httpMethod, CancellationToken cancellationToken = default ) { + await InvokeAsync( methodName, null, httpMethod, cancellationToken ); + } + + /// + public async Task InvokeAsync( string methodName, object data, CancellationToken cancellationToken = default ) { + await InvokeAsync( methodName, data, HttpMethod.Post, cancellationToken ); + } + + /// + public async Task InvokeAsync( string methodName, object data, HttpMethod httpMethod, CancellationToken cancellationToken = default ) { + await InvokeAsync( methodName, data, httpMethod, cancellationToken ); + } + + /// + public async Task InvokeAsync( string methodName, CancellationToken cancellationToken = default ) { + return await InvokeAsync( methodName, HttpMethod.Get, cancellationToken ); + } + + /// + public async Task InvokeAsync( string methodName, HttpMethod httpMethod, CancellationToken cancellationToken = default ) { + return await InvokeAsync( methodName, null, httpMethod, cancellationToken ); + } + + /// + public async Task InvokeAsync( string methodName, object data, CancellationToken cancellationToken = default ) { + return await InvokeAsync( methodName, data, HttpMethod.Get, cancellationToken ); + } + + /// + public async Task InvokeAsync( string methodName, object data, HttpMethod httpMethod, CancellationToken cancellationToken = default ) { + return await InvokeAsync( methodName, data, httpMethod, cancellationToken ); + } + + /// + public async Task InvokeAsync( string methodName, TRequest data, CancellationToken cancellationToken = default ) { + return await InvokeAsync( methodName, data, HttpMethod.Get, cancellationToken ); + } + + #endregion + + #region InvokeAsync + + /// + public async Task InvokeAsync( string methodName, TRequest data, HttpMethod httpMethod, CancellationToken cancellationToken = default ) { + Log.LogTrace( "准备调用服务方法,AppId:{AppId},MethodName:{MethodName}", AppId, methodName ); + var request = CreateRequest( methodName, data, httpMethod ); + request = FilterRequest( request ); + if ( await InvokeBefore( methodName, data, httpMethod, request ) == false ) + return default; + HttpResponseMessage response = null; + try { + response = await Client.InvokeMethodWithResponseAsync( request, cancellationToken ); + response = FilterResponse( response ); + OnAfterAction?.Invoke( response ); + var result = await ToResult( response, cancellationToken ); + await InvokeAfter( methodName, data, httpMethod, request, response, result ); + return result.Data; + } + catch ( Warning ) { + throw; + } + catch ( Exception exception ) { + await FailHandlerAsync( methodName, data, httpMethod, request, response, exception, null ); + } + finally { + await CompleteHandlerAsync( methodName, data, httpMethod, request, response ); + } + return default; + } + + #endregion + + #region CreateRequest + + /// + /// 创建请求消息 + /// + protected virtual HttpRequestMessage CreateRequest( string methodName, TRequest data, HttpMethod httpMethod ) { + var result = CreateInvokeMethodRequest( methodName, data, httpMethod ); + AddHeaders( result, methodName ); + return result; + } + + /// + /// 创建请求消息 + /// + private HttpRequestMessage CreateInvokeMethodRequest( string methodName, TRequest data, HttpMethod httpMethod ) { + methodName = GetMethodName( methodName ); + methodName = GetMethodNameWithQueryString( methodName, data, httpMethod ); + if ( data == null || httpMethod == HttpMethod.Get ) + return Client.CreateInvokeMethodRequest( httpMethod, AppId, methodName ); + return Client.CreateInvokeMethodRequest( httpMethod, AppId, methodName, data ); + } + + /// + /// 获取方法名 + /// + /// 方法名 + protected virtual string GetMethodName( string methodName ) { + if ( methodName.IsEmpty() ) + return string.Empty; + if ( methodName.StartsWith( "/" ) ) + return methodName; + return $"/api/{methodName}"; + } + + /// + /// 获取带查询字符串的方法名 + /// + protected virtual string GetMethodNameWithQueryString( string methodName, TRequest data, HttpMethod httpMethod ) { + if ( httpMethod != HttpMethod.Get ) + return methodName; + if ( data == null ) + return methodName; + return QueryHelpers.AddQueryString( methodName, ToDictionary( data ) ); + } + + /// + /// 对象转换为字典 + /// + /// 对象 + protected IDictionary ToDictionary( object data ) { + var result = Util.Helpers.Convert.ToDictionary( data ); + return result.Where( t => t.Value != null ).ToDictionary( t => t.Key, t => t.Value.SafeString() ); + } + + /// + /// 添加请求头 + /// + protected virtual void AddHeaders( HttpRequestMessage message, string methodName ) { + RemoveHeaders(); + var headers = GetHeaders(); + foreach ( var key in headers.Keys ) { + bool? success = message.Headers.TryAddWithoutValidation( key, headers[key].ToArray() ); + if ( success.SafeValue() ) + continue; + message.Content ??= new StringContent( string.Empty ); + success = message.Content.Headers.TryAddWithoutValidation( key, headers[key].ToArray() ); + if ( success.SafeValue() ) + continue; + Log.LogWarning( "添加请求头失败,Key:{RequestHeaderKey},AppId:{AppId},MethodName:{MethodName}", key, AppId, methodName ); + } + } + + /// + /// 移除请求头 + /// + private void RemoveHeaders() { + foreach ( var key in RemoveHeaderKeys ) { + Headers.Remove( key ); + ImportHeaderKeys.Remove( key ); + Options.ServiceInvocation.ImportHeaderKeys.Remove( key ); + } + } + + /// + /// 获取请求头 + /// + private IDictionary GetHeaders() { + var result = GetImportHeaders(); + foreach ( var key in Headers.Keys ) { + if ( result.ContainsKey( key ) ) + result.Remove( key ); + result.Add( key, Headers[key] ); + } + return result; + } + + /// + /// 获取导入的请求头 + /// + private IDictionary GetImportHeaders() { + var result = new Dictionary(); + ImportHeaderKeys.AddRange( Options.ServiceInvocation.ImportHeaderKeys ); + if ( ImportHeaderKeys.Count == 0 ) + return result; + var headers = Util.Helpers.Web.Request?.Headers; + if ( headers == null ) + return result; + foreach ( var key in ImportHeaderKeys.Distinct() ) { + if ( headers.TryGetValue( key, out var value ) ) + result.Add( key, value ); + } + return result; + } + + #endregion + + #region InvokeBefore + + /// + /// 调用前操作 + /// + protected async Task InvokeBefore( string methodName, object data, HttpMethod httpMethod, HttpRequestMessage message ) { + if( Options?.ServiceInvocation?.OnBefore != null ) + await Options.ServiceInvocation.OnBefore( new ServiceInvocationArgument( AppId, methodName, httpMethod, data, message ) ); + if ( OnBeforeAction == null ) + return true; + return OnBeforeAction( message ); + } + + #endregion + + #region FilterRequest + + /// + /// 过滤请求 + /// + /// 请求消息 + protected virtual HttpRequestMessage FilterRequest( HttpRequestMessage request ) { + var context = new RequestContext( request, Util.Helpers.Web.HttpContext ); + var requestFilters = Options?.ServiceInvocation?.RequestFilters; + if ( requestFilters == null || requestFilters.Count == 0 ) + return request; + foreach ( var filter in requestFilters.Where( t => t is { Enabled: true } ).OrderBy( t => t.Order ) ) + filter.Handle( context ); + return context.RequestMessage; + } + + #endregion + + #region FilterResponse + + /// + /// 过滤响应 + /// + /// 响应消息 + protected virtual HttpResponseMessage FilterResponse( HttpResponseMessage response ) { + var context = new ResponseContext( response, Util.Helpers.Web.HttpContext ); + var responseFilters = Options?.ServiceInvocation?.ResponseFilters; + if ( responseFilters == null || responseFilters.Count == 0 ) + return response; + foreach ( var filter in responseFilters.Where( t => t is { Enabled: true } ).OrderBy( t => t.Order ) ) + filter.Handle( context ); + return context.ResponseMessage; + } + + #endregion + + #region ToResult + + /// + /// 转换结果 + /// + /// 响应消息 + /// 取消令牌 + protected virtual async Task> ToResult( HttpResponseMessage response, CancellationToken cancellationToken ) { + if ( IsUnpackResult ) + return await ToUnpackResult( response, cancellationToken ); + return await ToNotUnpackResult( response, cancellationToken ); + } + + /// + /// 转换结果 - 使用ServiceResult解包 + /// + /// 响应消息 + /// 取消令牌 + protected virtual async Task> ToUnpackResult( HttpResponseMessage response, CancellationToken cancellationToken ) { + response.EnsureSuccessStatusCode(); + if ( OnResultAction == null ) + return await response.Content.ReadFromJsonAsync>( Client.JsonSerializerOptions, cancellationToken ); + var objResult = await OnResultAction( response, Client.JsonSerializerOptions, cancellationToken ); + var result = objResult as ServiceResult; + result!.CheckNull( nameof( result ) ); + return result; + } + + /// + /// 转换结果 - 不解包 + /// + /// 响应消息 + /// 取消令牌 + protected virtual async Task> ToNotUnpackResult( HttpResponseMessage response, CancellationToken cancellationToken ) { + var result = new ServiceResult { + Code = GetNotUnpackStateCode( response ) + }; + if ( result.Code != StateCode.Ok ) { + result.Message = await response.Content.ReadAsStringAsync( cancellationToken ); + return result; + } + if ( OnResultAction == null ) { + var json = await response.Content.ReadAsStringAsync( cancellationToken ); + if ( json.IsEmpty() ) + return result; + result.Data = Util.Helpers.Json.ToObject( json, Client.JsonSerializerOptions ); + return result; + } + var content = await OnResultAction( response, Client.JsonSerializerOptions, cancellationToken ); + result.Data = Util.Helpers.Convert.To( content ); + return result; + } + + /// + /// 获取未解包结果的状态码 + /// + protected virtual string GetNotUnpackStateCode( HttpResponseMessage response ) { + if ( response.IsSuccessStatusCode ) + return StateCode.Ok; + if ( response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden ) + return StateCode.Unauthorized; + return StateCode.Fail; + } + + #endregion + + #region InvokeAfter + + /// + /// 调用方法后操作 + /// + protected virtual async Task InvokeAfter( string methodName, object data, HttpMethod httpMethod, HttpRequestMessage request, HttpResponseMessage response, ServiceResult result ) { + var state = ToState( result ); + if ( state == ServiceState.Ok ) { + await SuccessHandlerAsync( methodName, data, httpMethod, request, response, result.Data ); + return; + } + if ( state == ServiceState.Fail ) { + await FailHandlerAsync( methodName, data, httpMethod, request, response, null, result?.Message ); + return; + } + if ( state == ServiceState.Unauthorized ) { + await UnauthorizedHandlerAsync( methodName, data, httpMethod, request, response ); + return; + } + } + + #endregion + + #region ToState + + /// + /// 转换为服务状态 + /// + protected virtual ServiceState ToState( ServiceResult result ) { + if ( result == null ) + return ServiceState.Fail; + if ( OnStateAction != null ) + return OnStateAction( result.Code ); + switch ( result.Code ) { + case StateCode.Ok: + return ServiceState.Ok; + case StateCode.Unauthorized: + return ServiceState.Unauthorized; + default: + return ServiceState.Fail; + } + } + + #endregion + + #region SuccessHandlerAsync + + /// + /// 成功处理操作 + /// + protected virtual async Task SuccessHandlerAsync( string methodName, object data, HttpMethod httpMethod, HttpRequestMessage request, HttpResponseMessage response, TResponse result ) { + Log.LogTrace( "调用服务成功,AppId:{AppId},MethodName:{MethodName}", AppId, methodName ); + if ( Options?.ServiceInvocation?.OnSuccess != null ) + await Options.ServiceInvocation.OnSuccess( new ServiceInvocationArgument( AppId, methodName, httpMethod, data, request, response, result ) ); + if ( OnSuccessAction == null ) + return; + await OnSuccessAction( request, response, result ); + } + + #endregion + + #region FailHandlerAsync + + /// + /// 失败处理操作 + /// + protected virtual async Task FailHandlerAsync( string methodName, object data, HttpMethod httpMethod, HttpRequestMessage request, HttpResponseMessage response, Exception exception, string message ) { + if ( exception == null ) + Log.LogWarning( "调用服务失败,AppId:{AppId},MethodName:{MethodName}", AppId, methodName ); + else { + Log.LogError( exception, "调用服务失败,AppId:{AppId},MethodName:{MethodName}", AppId, methodName ); + } + if ( Options?.ServiceInvocation?.OnFail != null ) + await Options.ServiceInvocation.OnFail( new ServiceInvocationArgument( AppId, methodName, httpMethod, data, request, response, null, exception, message ) ); + if ( OnFailAction != null ) { + await OnFailAction( request, response, exception ); + return; + } + if ( exception != null ) + throw new InvocationException( AppId, methodName, exception, response ); + throw new Warning( message ); + } + + #endregion + + #region UnauthorizedHandlerAsync + + /// + /// 未授权处理操作 + /// + protected virtual async Task UnauthorizedHandlerAsync( string methodName, object data, HttpMethod httpMethod, HttpRequestMessage request, HttpResponseMessage response ) { + Log.LogWarning( "调用未授权的服务,AppId:{AppId},MethodName:{MethodName}", AppId, methodName ); + if ( Options?.ServiceInvocation?.OnUnauthorized != null ) + await Options.ServiceInvocation.OnUnauthorized( new ServiceInvocationArgument( AppId, methodName, httpMethod, data, request, response ) ); + if ( OnUnauthorizedAction != null ) { + await OnUnauthorizedAction( request, response ); + return; + } + throw new Warning( R.UnauthorizedMessage, code: StateCode.Unauthorized ); + } + + #endregion + + #region CompleteHandlerAsync + + /// + /// 调用完成操作 + /// + protected virtual async Task CompleteHandlerAsync( string methodName, object data, HttpMethod httpMethod, HttpRequestMessage request, HttpResponseMessage response ) { + Log.LogTrace( "调用服务完成,AppId:{AppId},MethodName:{MethodName}", AppId, methodName ); + if ( Options?.ServiceInvocation?.OnComplete != null ) + await Options.ServiceInvocation.OnComplete( new ServiceInvocationArgument( AppId, methodName, httpMethod, data, request, response ) ); + if ( OnCompleteAction == null ) + return; + await OnCompleteAction( request, response ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/ServiceInvocations/DaprServiceInvocationBase.cs b/src/Util.Microservices.Dapr/ServiceInvocations/DaprServiceInvocationBase.cs new file mode 100644 index 000000000..92094d0b8 --- /dev/null +++ b/src/Util.Microservices.Dapr/ServiceInvocations/DaprServiceInvocationBase.cs @@ -0,0 +1,140 @@ +namespace Util.Microservices.Dapr.ServiceInvocations; + +/// +/// Dapr服务调用操作 +/// +/// 服务调用接口类型 +public abstract class DaprServiceInvocationBase : IServiceInvocationBase where TServiceInvocation : IServiceInvocationBase { + /// + /// Dapr客户端 + /// + protected DaprClient Client { get; } + /// + /// 配置 + /// + protected DaprOptions Options { get; } + /// + /// 日志 + /// + protected ILogger Log { get; set; } + /// + /// 应用标识 + /// + protected string AppId { get; set; } + /// + /// 是否对结果解包 + /// + protected bool IsUnpackResult { get; set; } + /// + /// 请求头集合 + /// + protected IDictionary Headers { get; set; } + /// + /// 导入请求头键名集合 + /// + protected List ImportHeaderKeys { get; set; } + /// + /// 移除请求头键名集合 + /// + protected IList RemoveHeaderKeys { get; set; } + /// + /// 服务状态码转换事件 + /// + protected Func OnStateAction { get; set; } + + /// + /// 初始化Dapr服务调用操作 + /// + /// Dapr客户端 + /// Dapr配置 + /// 日志工厂 + protected DaprServiceInvocationBase( DaprClient client,IOptions options, ILoggerFactory loggerFactory ) { + Client = client ?? throw new ArgumentNullException( nameof( client ) ); + Options = options?.Value ?? new DaprOptions(); + Log = loggerFactory?.CreateLogger( typeof( IServiceInvocation ) ) ?? NullLogger.Instance; + Headers = new Dictionary(); + ImportHeaderKeys = new List(); + RemoveHeaderKeys = new List(); + IsUnpackResult = Options.ServiceInvocation.IsUnpackResult; + } + + /// + public TServiceInvocation Service( string appId ) { + AppId = appId; + return Return(); + } + + /// + /// 返回 + /// + private TServiceInvocation Return() { + return (TServiceInvocation)(object)this; + } + + /// + public TServiceInvocation UnpackResult( bool isUnpack ) { + IsUnpackResult = isUnpack; + return Return(); + } + + /// + public TServiceInvocation BearerToken( string token ) { + Header( "Authorization",$"Bearer {token}" ); + return Return(); + } + + /// + public TServiceInvocation Header( string key, string value ) { + if ( key.IsEmpty() ) + return Return(); + if ( Headers.ContainsKey( key ) ) + Headers.Remove( key ); + Headers.Add( key, value ); + return Return(); + } + + /// + public TServiceInvocation Header( IDictionary headers ) { + if ( headers == null ) + return Return(); + foreach ( var header in headers ) + Header( header.Key, header.Value ); + return Return(); + } + + /// + public TServiceInvocation ImportHeader( string key ) { + if ( key.IsEmpty() ) + return Return(); + if( ImportHeaderKeys.Contains( key ) == false ) + ImportHeaderKeys.Add( key ); + return Return(); + } + + /// + public TServiceInvocation ImportHeader( IEnumerable keys ) { + if ( keys == null ) + return Return(); + foreach ( var key in keys ) + ImportHeader( key ); + return Return(); + } + + /// + public TServiceInvocation RemoveHeader( string key ) { + if ( key.IsEmpty() ) + return Return(); + if ( RemoveHeaderKeys.Contains( key ) == false ) + RemoveHeaderKeys.Add( key ); + return Return(); + } + + /// + /// 服务状态码转换事件 + /// + /// 转换为服务状态码,参数为业务状态码或Http状态码,取决于是否使用约定结果类型 + public TServiceInvocation OnState( Func action ) { + OnStateAction = action; + return Return(); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/ServiceInvocations/RequestFilterBase.cs b/src/Util.Microservices.Dapr/ServiceInvocations/RequestFilterBase.cs new file mode 100644 index 000000000..100c6edce --- /dev/null +++ b/src/Util.Microservices.Dapr/ServiceInvocations/RequestFilterBase.cs @@ -0,0 +1,20 @@ +namespace Util.Microservices.Dapr.ServiceInvocations; + +/// +/// WebApi服务调用请求过滤器 +/// +public abstract class RequestFilterBase : IRequestFilter { + /// + /// 是否启用 + /// + public bool Enabled => true; + /// + /// 序号 + /// + public int Order => 0; + /// + /// 处理请求 + /// + /// 请求上下文 + public abstract void Handle( RequestContext context ); +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/ServiceInvocations/ResponseFilterBase.cs b/src/Util.Microservices.Dapr/ServiceInvocations/ResponseFilterBase.cs new file mode 100644 index 000000000..ff1a965e3 --- /dev/null +++ b/src/Util.Microservices.Dapr/ServiceInvocations/ResponseFilterBase.cs @@ -0,0 +1,20 @@ +namespace Util.Microservices.Dapr.ServiceInvocations; + +/// +/// WebApi服务调用响应过滤器 +/// +public abstract class ResponseFilterBase : IResponseFilter { + /// + /// 是否启用 + /// + public bool Enabled => true; + /// + /// 序号 + /// + public int Order => 0; + /// + /// 处理响应 + /// + /// 响应上下文 + public abstract void Handle( ResponseContext context ); +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/ServiceInvocations/ServiceInvocationArgument.cs b/src/Util.Microservices.Dapr/ServiceInvocations/ServiceInvocationArgument.cs new file mode 100644 index 000000000..7d3848b22 --- /dev/null +++ b/src/Util.Microservices.Dapr/ServiceInvocations/ServiceInvocationArgument.cs @@ -0,0 +1,25 @@ +namespace Util.Microservices.Dapr.ServiceInvocations; + +/// +/// 服务调用参数 +/// +/// 应用标识 +/// 调用方法名称 +/// Http方法 +/// 请求参数 +/// Http请求消息 +/// Http响应消息 +/// 响应结果 +/// 异常 +/// 消息 +public record ServiceInvocationArgument( + string AppId, + string MethodName, + HttpMethod HttpMethod, + object RequestData, + HttpRequestMessage RequestMessage, + HttpResponseMessage ResponseMessage = null, + object Result = null, + Exception Exception = null, + string Message = null +); \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/StateConditionExtensions.cs b/src/Util.Microservices.Dapr/StateConditionExtensions.cs new file mode 100644 index 000000000..5c7652edc --- /dev/null +++ b/src/Util.Microservices.Dapr/StateConditionExtensions.cs @@ -0,0 +1,50 @@ +using Util.Microservices.Dapr.StateManagements.Queries.Conditions; + +namespace Util.Microservices.Dapr; + +/// +/// 状态对象查询条件扩展 +/// +public static class StateConditionExtensions { + /// + /// 使用And条件连接 + /// + /// 源查询条件 + /// 目标查询条件 + public static IStateCondition And( this IStateCondition source, IStateCondition condition ) { + if ( condition == null ) + return source; + if ( source == null ) + return condition; + if ( source is AndCondition andCondition ) { + andCondition.AddCondition( condition ); + return andCondition; + } + if ( condition is AndCondition andCondition2 ) { + andCondition2.AddCondition( source ); + return andCondition2; + } + return new AndCondition( source, condition ); + } + + /// + /// 使用Or条件连接 + /// + /// 源查询条件 + /// 目标查询条件 + public static IStateCondition Or( this IStateCondition source, IStateCondition condition ) { + if ( condition == null ) + return source; + if ( source == null ) + return condition; + if ( source is OrCondition orCondition ) { + orCondition.AddCondition( condition ); + return orCondition; + } + if ( condition is OrCondition orCondition2 ) { + orCondition2.AddCondition( source ); + return orCondition2; + } + return new OrCondition( source, condition ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/StateManagements/DaprStateManager.cs b/src/Util.Microservices.Dapr/StateManagements/DaprStateManager.cs new file mode 100644 index 000000000..d86ce746d --- /dev/null +++ b/src/Util.Microservices.Dapr/StateManagements/DaprStateManager.cs @@ -0,0 +1,47 @@ +namespace Util.Microservices.Dapr.StateManagements; + +/// +/// Dapr状态管理 +/// +public class DaprStateManager : DaprStateManagerBase, IStateManager { + /// + /// 初始化Dapr状态管理 + /// + /// Dapr客户端 + /// Dapr配置 + /// 日志工厂 + /// 状态存储键生成器 + public DaprStateManager( DaprClient client, IOptions options, ILoggerFactory loggerFactory, IKeyGenerator keyGenerator ) + : base(client,options,loggerFactory,keyGenerator){ + } + + /// + public IStateManager OrderBy( Expression> expression ) { + Sort.OrderBy( expression ); + return this; + } + + /// + public IStateManager OrderByDescending( Expression> expression ) { + Sort.OrderByDescending( expression ); + return this; + } + + /// + public IStateManager Equal( Expression> expression, object value ) { + Filter.Equal( expression, value ); + return this; + } + + /// + public IStateManager In( Expression> expression, IEnumerable values ) { + Filter.In( expression, values ); + return this; + } + + /// + public IStateManager In( Expression> expression, params object[] values ) { + Filter.In( expression, values ); + return this; + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/StateManagements/DaprStateManagerBase.Query.cs b/src/Util.Microservices.Dapr/StateManagements/DaprStateManagerBase.Query.cs new file mode 100644 index 000000000..b883ed679 --- /dev/null +++ b/src/Util.Microservices.Dapr/StateManagements/DaprStateManagerBase.Query.cs @@ -0,0 +1,274 @@ +using Util.Data.Queries; +using Util.Helpers; +using Util.Microservices.Dapr.StateManagements.Queries; + +namespace Util.Microservices.Dapr.StateManagements; + +/// +/// Dapr状态管理 - 查询 +/// +public partial class DaprStateManagerBase { + + #region Limit + + /// + /// 添加分页大小 + /// + /// 分页大小 + public TStateManager Limit( int pageSize ) { + Page.Limit = pageSize; + return Return(); + } + + #endregion + + #region Token + + /// + /// 添加迭代令牌 + /// + /// 迭代令牌 + public TStateManager Token( int token ) { + Page.Token = token.ToString(); + return Return(); + } + + #endregion + + #region OrderBy + + /// + public TStateManager OrderBy( string orderBy ) { + Sort.OrderBy( orderBy ); + return Return(); + } + + #endregion + + #region Equal + + /// + public TStateManager Equal( string property, object value ) { + Filter.Equal( property, value ); + return Return(); + } + + #endregion + + #region EqualIf + + /// + public TStateManager EqualIf( string property, object value, bool condition ) { + return condition ? Equal( property, value ) : Return(); + } + + #endregion + + #region In + + /// + public TStateManager In( string property, IEnumerable values ) { + Filter.In( property, values ); + return Return(); + } + + /// + public TStateManager In( string property, params object[] values ) { + Filter.In( property, values ); + return Return(); + } + + #endregion + + #region And + + /// + public TStateManager And( params IStateCondition[] conditions ) { + if ( conditions == null ) + return Return(); + foreach ( var condition in conditions ) + Filter.And( condition ); + return Return(); + } + + #endregion + + #region Or + + /// + public TStateManager Or( params IStateCondition[] conditions ) { + if ( conditions == null ) + return Return(); + foreach ( var condition in conditions ) + Filter.Or( condition ); + return Return(); + } + + #endregion + + #region GetKeyByIdAsync + + /// + /// 通过标识获取存储键 + /// + /// 值类型 + /// 标识 + /// 取消令牌 + public virtual async Task GetKeyByIdAsync( string id, CancellationToken cancellationToken = default ) where TValue : IDataKey { + if ( id.IsEmpty() ) + return null; + SetDataTypeCondition(); + Page.Limit = 1; + Equal( "id", id ); + var query = CreateQuery(); + var response = await Client.QueryStateAsync( GetStoreName(), query, Metadatas, cancellationToken ); + var result = response.Results.FirstOrDefault(); + ClearQuery(); + return result?.Key; + } + + #endregion + + #region GetStateAndETagAsync + + /// + public virtual async Task<(TValue value, string etag)> GetStateAndETagAsync( string key, CancellationToken cancellationToken = default ) { + var result = await Client.GetStateAndETagAsync( GetStoreName(), key, ConsistencyMode, Metadatas, cancellationToken ); + ClearQuery(); + return result; + } + + #endregion + + #region GetAsync + + /// + public virtual async Task GetAsync( string key, CancellationToken cancellationToken = default ) { + if ( IsETag() == false ) { + var state = await Client.GetStateAsync( GetStoreName(), key, ConsistencyMode, Metadatas, cancellationToken ); + ClearQuery(); + return state; + } + var result = await GetStateAndETagAsync( key, cancellationToken ); + if ( result.value == null ) + return default; + if ( result.value is IETag eTag ) + eTag.ETag = result.etag; + return result.value; + } + + /// + public virtual async Task> GetAsync( IReadOnlyList keys, int? parallelism = 0, CancellationToken cancellationToken = default ) { + var result = new List(); + var items = await Client.GetBulkStateAsync( GetStoreName(), keys, parallelism, Metadatas, cancellationToken ); + if ( items == null || items.Count == 0 ) + return result; + foreach ( var item in items ) { + var state = Json.ToObject( item.Value, SerializerOptions ); + if ( state is IETag eTag ) + eTag.ETag = item.ETag; + result.Add( state ); + } + ClearQuery(); + return result; + } + + #endregion + + #region GetByIdAsync + + /// + public virtual async Task GetByIdAsync( string id, CancellationToken cancellationToken = default ) where TValue : IDataKey { + if ( id.IsEmpty() ) + return default; + var key = KeyGenerator.CreateKey( id ); + var result = await GetAsync( key, cancellationToken ); + if ( result != null ) + return result; + key = await GetKeyByIdAsync( id, cancellationToken ); + return await GetAsync( key, cancellationToken ); + } + + #endregion + + #region SingleAsync + + /// + public virtual async Task SingleAsync( CancellationToken cancellationToken = default ) where TValue : IDataKey { + SetDataTypeCondition(); + Page.Limit = 1; + var result = await QueryAsync( cancellationToken ); + var value = result.FirstOrDefault(); + return await GetByIdAsync( value?.Id, cancellationToken ); + } + + #endregion + + #region GetAllAsync + + /// + /// 获取全部数据 + /// + /// 值类型 + /// 取消令牌 + public virtual async Task> GetAllAsync( CancellationToken cancellationToken = default ) { + SetDataTypeCondition(); + return await QueryAsync( cancellationToken ); + } + + #endregion + + #region QueryAsync + + /// + /// 查询 + /// + /// 值类型 + /// 取消令牌 + public virtual async Task> QueryAsync( CancellationToken cancellationToken = default ) { + var result = new List(); + var query = CreateQuery(); + var response = await Client.QueryStateAsync( GetStoreName(), query, Metadatas, cancellationToken ); + ClearQuery(); + if ( response == null ) + return result; + foreach ( var item in response.Results ) { + if ( item.Data is IETag eTag ) + eTag.ETag = item.ETag; + result.Add( item.Data ); + } + return result; + } + + /// + /// 创建状态查询对象 + /// + protected virtual string CreateQuery() { + var query = new StateQuery { + Filter = Filter.GetCondition(), + Sort = Sort, + Page = Page + }; + return Json.ToJson( query, SerializerOptions ); + } + + #endregion + + #region PageQueryAsync + + /// + /// 分页查询 + /// + /// 值类型 + /// 分页参数 + /// 取消令牌 + public virtual async Task> PageQueryAsync( IPage page, CancellationToken cancellationToken = default ) { + Sort.OrderBy( page.Order ); + Page.Limit = page.PageSize; + Page.Token = page.GetSkipCount() > 0 ? page.GetSkipCount().ToString() : null; + var data = await QueryAsync( cancellationToken ); + return new PageList( page, data ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/StateManagements/DaprStateManagerBase.cs b/src/Util.Microservices.Dapr/StateManagements/DaprStateManagerBase.cs new file mode 100644 index 000000000..4e8bae550 --- /dev/null +++ b/src/Util.Microservices.Dapr/StateManagements/DaprStateManagerBase.cs @@ -0,0 +1,470 @@ +using Util.Helpers; +using Util.Microservices.Dapr.StateManagements.Queries; + +namespace Util.Microservices.Dapr.StateManagements; + +/// +/// Dapr状态管理 +/// +public partial class DaprStateManagerBase : IStateManagerBase where TStateManager : IStateManagerBase { + + #region 字段 + + /// + /// Dapr客户端 + /// + protected readonly DaprClient Client; + /// + /// 配置 + /// + protected DaprOptions Options; + /// + /// 日志操作 + /// + protected readonly ILogger Logger; + /// + /// 状态存储键生成器 + /// + protected IKeyGenerator KeyGenerator; + /// + /// 元数据 + /// + protected Dictionary Metadatas; + /// + /// 一致性模式 + /// + protected ConsistencyMode ConsistencyMode; + /// + /// 事务参数列表 + /// + protected List StateTransactionRequests; + /// + /// Json序列化配置 + /// + protected JsonSerializerOptions SerializerOptions; + /// + /// 是否开启事务 + /// + protected bool IsTransaction; + /// + /// 过滤条件 + /// + protected StateFilter Filter; + /// + /// 排序条件 + /// + protected StateSort Sort; + /// + /// 分页条件 + /// + protected StatePage Page; + /// + /// 状态组件名称 + /// + private string _storeName; + + #endregion + + #region 构造方法 + + /// + /// 初始化Dapr状态管理 + /// + /// Dapr客户端 + /// Dapr配置 + /// 日志工厂 + /// 状态存储键生成器 + public DaprStateManagerBase( DaprClient client, IOptions options, ILoggerFactory loggerFactory, IKeyGenerator keyGenerator ) { + Client = client ?? throw new ArgumentNullException( nameof( client ) ); + Options = options?.Value ?? new DaprOptions(); + Logger = loggerFactory?.CreateLogger( typeof( DaprStateManager ) ) ?? NullLogger.Instance; + KeyGenerator = keyGenerator ?? throw new ArgumentNullException( nameof( keyGenerator ) ); + Metadatas = new Dictionary(); + ConsistencyMode = ConsistencyMode.Eventual; + StateTransactionRequests = new List(); + Filter = new StateFilter(); + Sort = new StateSort(); + Page = new StatePage(); + } + + #endregion + + #region 辅助操作 + + /// + /// 返回 + /// + private TStateManager Return() { + return (TStateManager)(object)this; + } + + /// + /// 获取状态组件名称 + /// + protected string GetStoreName() { + return _storeName.IsEmpty() ? "statestore" : _storeName; + } + + /// + /// 获取存储键 + /// + protected virtual string GetKey( string key, TValue value ) where TValue : IDataKey { + if ( key.IsEmpty() == false ) + return key; + return KeyGenerator.CreateKey( value.Id ); + } + + /// + /// 是否实现IDataType接口 + /// + protected bool IsDataType() { + return typeof( TValue ).IsAssignableTo( typeof( IDataType ) ); + } + + /// + /// 是否实现IDataKey接口 + /// + protected bool IsDataKey() { + return typeof( TValue ).IsAssignableTo( typeof( IDataKey ) ); + } + + /// + /// 是否实现IETag接口 + /// + protected bool IsETag() { + return typeof( TValue ).IsAssignableTo( typeof( IETag ) ); + } + + /// + /// 设置数据类型查询条件 + /// + protected void SetDataTypeCondition() { + EqualIf( "dataType", typeof( TValue ).FullName, IsDataType() ); + } + + #endregion + + #region StoreName + + /// + public TStateManager StoreName( string storeName ) { + _storeName = storeName; + return Return(); + } + + #endregion + + #region Clear + + /// + public TStateManager Clear() { + Metadatas.Clear(); + ConsistencyMode = ConsistencyMode.Eventual; + StateTransactionRequests.Clear(); + IsTransaction = false; + ClearQuery(); + return Return(); + } + + /// + /// 清理查询条件 + /// + protected void ClearQuery() { + Filter.Clear(); + Sort.Clear(); + Page = new StatePage(); + } + + #endregion + + #region BeginTransaction + + /// + public TStateManager BeginTransaction() { + IsTransaction = true; + return Return(); + } + + #endregion + + #region JsonSerializerOptions + + /// + public TStateManager JsonSerializerOptions( JsonSerializerOptions options ) { + SerializerOptions = options; + return Return(); + } + + #endregion + + #region Metadata + + /// + /// 设置元数据 + /// + /// 键 + /// 值 + public TStateManager Metadata( string key, string value ) { + if ( key.IsEmpty() ) + return Return(); + if ( value.IsEmpty() ) + return Return(); + RemoveMetadata( key ); + Metadatas.Add( key, value ); + return Return(); + } + + /// + /// 设置元数据 + /// + /// 元数据键值对集合 + public TStateManager Metadata( IDictionary metadata ) { + if ( metadata == null ) + return Return(); + foreach ( var item in metadata ) + Metadata( item.Key, item.Value ); + return Return(); + } + + #endregion + + #region RemoveMetadata + + /// + /// 移除元数据 + /// + /// 键 + public TStateManager RemoveMetadata( string key ) { + if ( key.IsEmpty() ) + return Return(); + if ( Metadatas.ContainsKey( key ) ) + Metadatas.Remove( key ); + return Return(); + } + + #endregion + + #region ContentType + + /// + public TStateManager ContentType( string type ) { + Metadata( "contentType", type ); + return Return(); + } + + #endregion + + #region JsonType + + /// + /// 设置contentType为application/json + /// + public TStateManager JsonType() { + return ContentType( "application/json" ); + } + + #endregion + + #region AddAsync + + /// + public virtual async Task AddAsync( string key, TValue value, CancellationToken cancellationToken = default ) { + if ( key.IsEmpty() ) + throw new ArgumentNullException( nameof( key ) ); + InitAdd( value ); + if ( IsTransaction == false ) { + await Client.SaveStateAsync( GetStoreName(), key, value, CreateStateOptions(), Metadatas, cancellationToken ); + Clear(); + return; + } + var request = new StateTransactionRequest( key, ToBytes( value ), StateOperationType.Upsert, null, Metadatas, CreateStateOptions() ); + StateTransactionRequests.Add( request ); + } + + /// + /// 初始化新增 + /// + protected virtual void InitAdd( TValue value ) { + InitId( value ); + InitDataType( value ); + InitJsonType(); + } + + /// + /// 初始化标识 + /// + protected virtual void InitId( TValue value ) { + if ( value is not IDataKey data ) + return; + if ( data.Id.IsEmpty() ) + data.Id = Guid.NewGuid().ToString(); + } + + /// + /// 初始化数据类型 + /// + protected virtual void InitDataType( TValue value ) { + if ( value is IDataType dataType ) + dataType.DataType = typeof( TValue ).FullName; + } + + /// + /// 初始化Json内容类型 + /// + protected virtual void InitJsonType() { + var type = typeof( TValue ); + if ( type == typeof( string ) ) + return; + JsonType(); + } + + /// + /// 创建状态配置 + /// + protected StateOptions CreateStateOptions() { + return new StateOptions { + Consistency = ConsistencyMode, + Concurrency = ConcurrencyMode.FirstWrite + }; + } + + /// + /// 转换为字节数组 + /// + protected virtual byte[] ToBytes( TValue value ) { + return Json.ToBytes( value, SerializerOptions ); + } + + #endregion + + #region UpdateAsync + + /// + public async Task UpdateAsync( string key, TValue value, string etag, CancellationToken cancellationToken = default ) { + if ( key.IsEmpty() ) + throw new ArgumentNullException( nameof( key ) ); + InitJsonType(); + if ( IsTransaction == false ) { + try { + var result = await Client.TrySaveStateAsync( GetStoreName(), key, value, etag, CreateStateOptions(), Metadatas, cancellationToken ); + Clear(); + return result; + } + catch ( DaprException ex ) { + Clear(); + var exception = ex.InnerException; + if ( exception != null && exception.Message.Contains( "E11000" ) ) + return false; + throw; + } + } + var request = new StateTransactionRequest( key, ToBytes( value ), StateOperationType.Upsert, etag, Metadatas, CreateStateOptions() ); + StateTransactionRequests.Add( request ); + return true; + } + + #endregion + + #region RemoveAsync + + /// + public async Task RemoveAsync( string key, CancellationToken cancellationToken = default ) { + if ( key.IsEmpty() ) + throw new ArgumentNullException( nameof( key ) ); + if ( IsTransaction == false ) { + await Client.DeleteStateAsync( GetStoreName(), key, CreateStateOptions(), Metadatas, cancellationToken ); + Clear(); + return; + } + var request = new StateTransactionRequest( key, null, StateOperationType.Delete, null, Metadatas, CreateStateOptions() ); + StateTransactionRequests.Add( request ); + } + + #endregion + + #region RemoveByIdAsync + + /// + /// 通过标识删除数据 + /// + /// 值类型 + /// 标识 + /// 取消令牌 + public async Task RemoveByIdAsync( string id, CancellationToken cancellationToken = default ) where TValue : IDataKey { + var key = await GetKeyByIdAsync( id, cancellationToken ); + await RemoveAsync( key, cancellationToken ); + } + + #endregion + + #region SaveAsync + + /// + public async Task SaveAsync( TValue value, CancellationToken cancellationToken = default, string key = null ) where TValue : IDataKey, IETag { + value.CheckNull( nameof( value ) ); + InitSave( value ); + key = GetKey( key, value ); + if ( IsTransaction == false ) { + var result = await UpdateAsync( key, value, value.ETag ?? string.Empty, cancellationToken ); + if ( result == false ) + throw new ConcurrencyException( $"保存状态失败,key:{key}" ); + Clear(); + return key; + } + var bytes = ToBytes( value ); + var request = new StateTransactionRequest( key, bytes, StateOperationType.Upsert, value.ETag ?? string.Empty, Metadatas, CreateStateOptions() ); + StateTransactionRequests.Add( request ); + return key; + } + + /// + /// 初始化保存数据 + /// + protected virtual void InitSave( TValue value ) where TValue : IDataKey, IETag { + InitId( value ); + InitDataType( value ); + } + + /// + public async Task SaveAsync( IEnumerable values, CancellationToken cancellationToken = default ) where TValue : IDataKey, IETag { + values.CheckNull( nameof( values ) ); + InitJsonType(); + var items = ToSaveStateItems( values ); + if ( IsTransaction == false ) { + await Client.SaveBulkStateAsync( GetStoreName(), items, cancellationToken ); + Clear(); + return; + } + foreach ( var item in items ) { + var bytes = ToBytes( item.Value ); + var request = new StateTransactionRequest( item.Key, bytes, StateOperationType.Upsert, item.ETag ?? string.Empty, Metadatas, CreateStateOptions() ); + StateTransactionRequests.Add( request ); + } + } + + /// + /// 转换为SaveStateItems + /// + protected List> ToSaveStateItems( IEnumerable values ) where TValue : IDataKey, IETag { + var result = new List>(); + foreach ( var value in values ) { + InitSave( value ); + var key = GetKey( null, value ); + var item = new SaveStateItem( key, value, value.ETag, CreateStateOptions(), Metadatas ); + result.Add( item ); + } + return result; + } + + #endregion + + #region CommitAsync + + /// + public async Task CommitAsync( CancellationToken cancellationToken = default ) { + await Client.ExecuteStateTransactionAsync( GetStoreName(), StateTransactionRequests, Metadatas, cancellationToken ); + Clear(); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/StateManagements/DaprStateManagerOfT.cs b/src/Util.Microservices.Dapr/StateManagements/DaprStateManagerOfT.cs new file mode 100644 index 000000000..9c39c2e8d --- /dev/null +++ b/src/Util.Microservices.Dapr/StateManagements/DaprStateManagerOfT.cs @@ -0,0 +1,47 @@ +namespace Util.Microservices.Dapr.StateManagements; + +/// +/// Dapr状态管理 +/// +public class DaprStateManager : DaprStateManagerBase>, IStateManager { + /// + /// 初始化Dapr状态管理 + /// + /// Dapr客户端 + /// Dapr配置 + /// 日志工厂 + /// 状态存储键生成器 + public DaprStateManager( DaprClient client, IOptions options, ILoggerFactory loggerFactory, IKeyGenerator keyGenerator ) + : base( client, options, loggerFactory, keyGenerator ) { + } + + /// + public IStateManager OrderBy( Expression> expression ) { + Sort.OrderBy( expression ); + return this; + } + + /// + public IStateManager OrderByDescending( Expression> expression ) { + Sort.OrderByDescending( expression ); + return this; + } + + /// + public IStateManager Equal( Expression> expression, object value ) { + Filter.Equal( expression, value ); + return this; + } + + /// + public IStateManager In( Expression> expression, IEnumerable values ) { + Filter.In( expression, values ); + return this; + } + + /// + public IStateManager In( Expression> expression, params object[] values ) { + Filter.In( expression, values ); + return this; + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/StateManagements/IKeyGenerator.cs b/src/Util.Microservices.Dapr/StateManagements/IKeyGenerator.cs new file mode 100644 index 000000000..7eada0239 --- /dev/null +++ b/src/Util.Microservices.Dapr/StateManagements/IKeyGenerator.cs @@ -0,0 +1,13 @@ +namespace Util.Microservices.Dapr.StateManagements; + +/// +/// 状态存储键生成器 +/// +public interface IKeyGenerator : ISingletonDependency { + /// + /// 创建状态存储键 + /// + /// 值类型 + /// 标识 + string CreateKey( string id ) where TValue : IDataKey; +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/StateManagements/KeyGenerator.cs b/src/Util.Microservices.Dapr/StateManagements/KeyGenerator.cs new file mode 100644 index 000000000..0dd293ff8 --- /dev/null +++ b/src/Util.Microservices.Dapr/StateManagements/KeyGenerator.cs @@ -0,0 +1,11 @@ +namespace Util.Microservices.Dapr.StateManagements; + +/// +/// 状态存储键生成器 +/// +public class KeyGenerator : IKeyGenerator { + /// + public string CreateKey( string id ) where TValue : IDataKey { + return id.IsEmpty() ? null : $"{typeof( TValue ).Name!.Replace( ".", "_" )}_{id}"; + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/StateManagements/Queries/Conditions/AndCondition.cs b/src/Util.Microservices.Dapr/StateManagements/Queries/Conditions/AndCondition.cs new file mode 100644 index 000000000..10cba173b --- /dev/null +++ b/src/Util.Microservices.Dapr/StateManagements/Queries/Conditions/AndCondition.cs @@ -0,0 +1,41 @@ +namespace Util.Microservices.Dapr.StateManagements.Queries.Conditions; + +/// +/// And连接条件 +/// +public class AndCondition : IStateCondition { + /// + /// 初始化And连接条件 + /// + /// 条件1 + /// 条件2 + public AndCondition( IStateCondition condition1, IStateCondition condition2 = null ) { + if ( condition1 == null && condition2 == null ) + return; + And = new List(); + AddCondition( condition1 ); + AddCondition( condition2 ); + } + + /// + /// And连接条件 + /// + [JsonPropertyName( "AND" )] + public List And { get; set; } + + /// + /// 添加条件 + /// + /// 条件 + public void AddCondition( IStateCondition condition ) { + if ( condition == null ) + return; + if ( condition is AndCondition andCondition ) { + foreach ( var item in andCondition.And ) { + And.Add( item ); + } + return; + } + And.Add( condition ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/StateManagements/Queries/Conditions/EqualCondition.cs b/src/Util.Microservices.Dapr/StateManagements/Queries/Conditions/EqualCondition.cs new file mode 100644 index 000000000..99b1fa0ec --- /dev/null +++ b/src/Util.Microservices.Dapr/StateManagements/Queries/Conditions/EqualCondition.cs @@ -0,0 +1,23 @@ +namespace Util.Microservices.Dapr.StateManagements.Queries.Conditions; + +/// +/// 状态对象相等查询条件 +/// +public class EqualCondition : IStateCondition { + /// + /// 初始化状态对象相等查询条件 + /// + /// 属性名 + /// 属性值 + public EqualCondition( string property, object value ) { + if ( property.IsEmpty() ) + return; + Equal = new Dictionary { { property, value } }; + } + + /// + /// 相等操作 + /// + [JsonPropertyName( "EQ" )] + public Dictionary Equal { get; set; } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/StateManagements/Queries/Conditions/InCondition.cs b/src/Util.Microservices.Dapr/StateManagements/Queries/Conditions/InCondition.cs new file mode 100644 index 000000000..502acf364 --- /dev/null +++ b/src/Util.Microservices.Dapr/StateManagements/Queries/Conditions/InCondition.cs @@ -0,0 +1,23 @@ +namespace Util.Microservices.Dapr.StateManagements.Queries.Conditions; + +/// +/// 状态对象In查询条件 +/// +public class InCondition : IStateCondition { + /// + /// 初始化状态对象In查询条件 + /// + /// 属性名 + /// 属性值 + public InCondition( string property, IEnumerable values ) { + if ( property.IsEmpty() ) + return; + In = new Dictionary> { { property, values } }; + } + + /// + /// In操作 + /// + [JsonPropertyName( "IN" )] + public Dictionary> In { get; set; } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/StateManagements/Queries/Conditions/OrCondition.cs b/src/Util.Microservices.Dapr/StateManagements/Queries/Conditions/OrCondition.cs new file mode 100644 index 000000000..060c80aed --- /dev/null +++ b/src/Util.Microservices.Dapr/StateManagements/Queries/Conditions/OrCondition.cs @@ -0,0 +1,41 @@ +namespace Util.Microservices.Dapr.StateManagements.Queries.Conditions; + +/// +/// Or连接条件 +/// +public class OrCondition : IStateCondition { + /// + /// 初始化Or连接条件 + /// + /// 条件1 + /// 条件2 + public OrCondition( IStateCondition condition1, IStateCondition condition2 = null ) { + if ( condition1 == null && condition2 == null ) + return; + Or = new List(); + AddCondition( condition1 ); + AddCondition( condition2 ); + } + + /// + /// Or连接条件 + /// + [JsonPropertyName( "OR" )] + public List Or { get; set; } + + /// + /// 添加条件 + /// + /// 条件 + public void AddCondition( IStateCondition condition ) { + if ( condition == null ) + return; + if ( condition is OrCondition orCondition ) { + foreach ( var item in orCondition.Or ) { + Or.Add( item ); + } + return; + } + Or.Add( condition ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/StateManagements/Queries/OrderByItem.cs b/src/Util.Microservices.Dapr/StateManagements/Queries/OrderByItem.cs new file mode 100644 index 000000000..a745be93a --- /dev/null +++ b/src/Util.Microservices.Dapr/StateManagements/Queries/OrderByItem.cs @@ -0,0 +1,34 @@ +namespace Util.Microservices.Dapr.StateManagements.Queries; + +/// +/// 排序项 +/// +public class OrderByItem { + /// + /// 初始化排序项 + /// + /// 排序表达式 + public OrderByItem( string order ) { + Key = order.SafeString().Trim(); + if ( Key.EndsWith( " Asc", StringComparison.OrdinalIgnoreCase ) ) { + Key = Key.Substring( 0, Key.Length - 3 ).Trim(); + return; + } + if ( Key.EndsWith( " Desc", StringComparison.OrdinalIgnoreCase ) ) { + Order = "DESC"; + Key = Key.Substring( 0, Key.Length - 4 ).Trim(); + } + } + + /// + /// 排序属性 + /// + [JsonPropertyName( "key" )] + public string Key { get; } + + /// + /// 排序方向,可选值: ASC,DESC + /// + [JsonPropertyName( "order" )] + public string Order { get; } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/StateManagements/Queries/StateFilter.cs b/src/Util.Microservices.Dapr/StateManagements/Queries/StateFilter.cs new file mode 100644 index 000000000..bd026058b --- /dev/null +++ b/src/Util.Microservices.Dapr/StateManagements/Queries/StateFilter.cs @@ -0,0 +1,84 @@ +using Util.Microservices.Dapr.StateManagements.Queries.Conditions; + +namespace Util.Microservices.Dapr.StateManagements.Queries; + +/// +/// 状态过滤器 +/// +public class StateFilter { + /// + /// 过滤条件 + /// + private IStateCondition _condition; + + /// + /// 获取过滤条件 + /// + public object GetCondition() { + return _condition; + } + + /// + /// 清理 + /// + public StateFilter Clear() { + _condition = null; + return this; + } + + /// + /// 相等条件 + /// + /// 属性名 + /// 属性值 + public StateFilter Equal( string property, object value ) { + _condition = _condition.And( new EqualCondition( property, value ) ); + return this; + } + + /// + /// In条件 + /// + /// 属性名 + /// 属性值 + public StateFilter In( string property, IEnumerable values ) { + _condition = _condition.And( new InCondition( property, values ) ); + return this; + } + + /// + /// And连接 + /// + /// 查询条件 + public StateFilter And( IStateCondition condition ) { + _condition = _condition.And( condition ); + return this; + } + + /// + /// Or连接 + /// + /// 查询条件 + public StateFilter Or( IStateCondition condition ) { + _condition = _condition.Or( condition ); + return this; + } + + /// + /// 相等条件 + /// + /// 属性名表达式 + /// 属性值 + public StateFilter Equal( Expression> expression, object value ) { + return Equal( StateQueryHelper.GetProperty( expression ), value ); + } + + /// + /// In条件 + /// + /// 属性名表达式 + /// 属性值 + public StateFilter In( Expression> expression, IEnumerable values ) { + return In( StateQueryHelper.GetProperty( expression ), values ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/StateManagements/Queries/StatePage.cs b/src/Util.Microservices.Dapr/StateManagements/Queries/StatePage.cs new file mode 100644 index 000000000..31c721953 --- /dev/null +++ b/src/Util.Microservices.Dapr/StateManagements/Queries/StatePage.cs @@ -0,0 +1,18 @@ +namespace Util.Microservices.Dapr.StateManagements.Queries; + +/// +/// 状态分页 +/// +public class StatePage { + /// + /// 分页大小,相对于PageSize + /// + [JsonPropertyName( "limit" )] + public int? Limit { get; set; } + + /// + /// 迭代令牌 + /// + [JsonPropertyName( "token" )] + public string Token { get; set; } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/StateManagements/Queries/StateQuery.cs b/src/Util.Microservices.Dapr/StateManagements/Queries/StateQuery.cs new file mode 100644 index 000000000..391e99b8e --- /dev/null +++ b/src/Util.Microservices.Dapr/StateManagements/Queries/StateQuery.cs @@ -0,0 +1,22 @@ +namespace Util.Microservices.Dapr.StateManagements.Queries; + +/// +/// 状态查询对象 +/// +public class StateQuery { + /// + /// 过滤条件 + /// + [JsonPropertyName( "filter" )] + public object Filter { get; set; } + /// + /// 排序条件 + /// + [JsonPropertyName( "sort" )] + public StateSort Sort { get; set; } + /// + /// 分页条件 + /// + [JsonPropertyName( "page" )] + public StatePage Page { get; set; } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/StateManagements/Queries/StateQueryHelper.cs b/src/Util.Microservices.Dapr/StateManagements/Queries/StateQueryHelper.cs new file mode 100644 index 000000000..ed64668eb --- /dev/null +++ b/src/Util.Microservices.Dapr/StateManagements/Queries/StateQueryHelper.cs @@ -0,0 +1,16 @@ +using Util.Helpers; + +namespace Util.Microservices.Dapr.StateManagements.Queries; + +/// +/// 状态查询辅助操作 +/// +public static class StateQueryHelper { + /// + /// 获取属性名 + /// + public static string GetProperty( Expression> expression ) { + var property = Lambda.GetName( expression ); + return property.IsEmpty() ? null : property.Split( '.' ).Select( Util.Helpers.String.FirstLowerCase ).Join( separator: "." ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/StateManagements/Queries/StateSort.cs b/src/Util.Microservices.Dapr/StateManagements/Queries/StateSort.cs new file mode 100644 index 000000000..89cf62dde --- /dev/null +++ b/src/Util.Microservices.Dapr/StateManagements/Queries/StateSort.cs @@ -0,0 +1,51 @@ +namespace Util.Microservices.Dapr.StateManagements.Queries; + +/// +/// 状态排序 +/// +public class StateSort : List { + /// + /// 添加升序排序属性 + /// + /// 升序排序属性 + public void OrderBy( string property ) { + Add( property ); + } + + /// + /// 添加降序排序属性 + /// + /// 降序排序属性 + public void OrderByDescending( string property ) { + Add( $"{property} DESC" ); + } + + /// + /// 添加排序条件 + /// + /// 排序条件 + public void Add( string orderBy ) { + if ( orderBy.IsEmpty() ) + return; + var items = orderBy.Split( "," ); + foreach ( var item in items ) { + Add( new OrderByItem( item ) ); + } + } + + /// + /// 添加升序排序属性 + /// + /// 属性表达式 + public void OrderBy( Expression> expression ) { + OrderBy( StateQueryHelper.GetProperty( expression ) ); + } + + /// + /// 添加降序排序属性 + /// + /// 属性表达式 + public void OrderByDescending( Expression> expression ) { + OrderByDescending( StateQueryHelper.GetProperty( expression ) ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/StateManagerExtensions.cs b/src/Util.Microservices.Dapr/StateManagerExtensions.cs new file mode 100644 index 000000000..b7a8f49ee --- /dev/null +++ b/src/Util.Microservices.Dapr/StateManagerExtensions.cs @@ -0,0 +1,38 @@ +namespace Util.Microservices.Dapr; + +/// +/// 状态管理操作扩展 +/// +public static class StateManagerExtensions { + + #region EqualIfNotEmpty + + /// + /// 根据规则添加相等查询条件,如果参数值为空,则忽略该查询条件 + /// + /// 状态管理操作 + /// 属性名 + /// 属性值,如果为空,则忽略该查询条件 + public static TStateManager EqualIfNotEmpty( this TStateManager source, string property, object value ) where TStateManager : IStateManagerBase { + source.CheckNull( nameof( source ) ); + return value.SafeString().IsEmpty() ? source : source.Equal( property, value ); + } + + #endregion + + #region InIf + + /// + /// 根据规则添加In查询条件 + /// + /// 状态管理操作 + /// 属性名 + /// 属性值 + /// 该值为true时添加查询条件,否则忽略 + public static TStateManager InIf( this TStateManager source, string property, IEnumerable values, bool condition ) where TStateManager : IStateManagerBase { + source.CheckNull( nameof( source ) ); + return condition ? source.In( property, values ) : source; + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Microservices.Dapr/Usings.cs b/src/Util.Microservices.Dapr/Usings.cs new file mode 100644 index 000000000..974fc4b3f --- /dev/null +++ b/src/Util.Microservices.Dapr/Usings.cs @@ -0,0 +1,36 @@ +global using System; +global using System.Threading.Tasks; +global using System.Threading; +global using System.Globalization; +global using System.Net.Http; +global using System.Linq; +global using System.Collections.Generic; +global using System.Net.Http.Json; +global using System.Text.Json; +global using System.IO; +global using System.Text.Encodings.Web; +global using System.Text.Json.Serialization; +global using System.Text.Unicode; +global using System.Net; +global using System.Linq.Expressions; +global using Microsoft.AspNetCore.Mvc.Filters; +global using Microsoft.Extensions.Primitives; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Options; +global using Microsoft.Extensions.Logging.Abstractions; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.AspNetCore.WebUtilities; +global using Microsoft.AspNetCore.Mvc; +global using Microsoft.AspNetCore.Http; +global using Dapr; +global using Dapr.Client; +global using Dapr.Extensions.Configuration; +global using Util.Http; +global using Util.Applications; +global using Util.Exceptions; +global using Util.Properties; +global using Util.Dependency; +global using Util.Events; +global using Util.Data; +global using Util.Microservices.Events; \ No newline at end of file diff --git a/src/Util.Microservices.HealthChecks/04-Util.Microservices.HealthChecks.csproj b/src/Util.Microservices.HealthChecks/04-Util.Microservices.HealthChecks.csproj new file mode 100644 index 000000000..8172f72b4 --- /dev/null +++ b/src/Util.Microservices.HealthChecks/04-Util.Microservices.HealthChecks.csproj @@ -0,0 +1,41 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Microservices.HealthChecks + Util.Microservices.HealthChecks + Util.Microservices.HealthChecks是Util应用框架的健康检查操作类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Microservices.HealthChecks.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Microservices.HealthChecks.xml + + + + + True + False + + + + + + + + + + + + + + + + + diff --git a/src/Util.Microservices.HealthChecks/Dapr/DaprHealthCheck.cs b/src/Util.Microservices.HealthChecks/Dapr/DaprHealthCheck.cs new file mode 100644 index 000000000..2276c425c --- /dev/null +++ b/src/Util.Microservices.HealthChecks/Dapr/DaprHealthCheck.cs @@ -0,0 +1,34 @@ +namespace Util.Microservices.HealthChecks.Dapr; + +/// +/// Dapr健康检查 +/// +public class DaprHealthCheck : IHealthCheck { + /// + /// Dapr客户端 + /// + private readonly DaprClient _client; + + /// + /// 初始化Dapr健康检查 + /// + /// Dapr客户端 + public DaprHealthCheck( DaprClient client ) { + _client = client ?? throw new ArgumentNullException( nameof( client ) ); + } + + /// + /// 健康检查 + /// + /// 健康检查上下文 + /// 取消令牌 + public async Task CheckHealthAsync( HealthCheckContext context, CancellationToken cancellationToken = default ) { + try { + var healthy = await _client.CheckHealthAsync( cancellationToken ); + return healthy ? HealthCheckResult.Healthy() : new HealthCheckResult( context.Registration.FailureStatus ); + } + catch ( Exception exception ) { + return HealthCheckResult.Unhealthy( exception.Message, exception ); + } + } +} diff --git a/src/Util.Microservices.HealthChecks/EntityFrameworkCore/UnitOfWorkHealthCheck.cs b/src/Util.Microservices.HealthChecks/EntityFrameworkCore/UnitOfWorkHealthCheck.cs new file mode 100644 index 000000000..929ac675c --- /dev/null +++ b/src/Util.Microservices.HealthChecks/EntityFrameworkCore/UnitOfWorkHealthCheck.cs @@ -0,0 +1,36 @@ +namespace Util.Microservices.HealthChecks.EntityFrameworkCore; + +/// +/// 工作单元健康检查 +/// +/// 工作单元类型 +public class UnitOfWorkHealthCheck : IHealthCheck where TUnitOfWork : IUnitOfWork { + /// + /// 工作单元 + /// + private readonly TUnitOfWork _unitOfWork; + + /// + /// 初始化工作单元健康检查 + /// + /// 工作单元 + public UnitOfWorkHealthCheck( TUnitOfWork unitOfWork ) { + _unitOfWork = unitOfWork ?? throw new ArgumentNullException( nameof( unitOfWork ) ); + } + + /// + /// 健康检查 + /// + /// 健康检查上下文 + /// 取消令牌 + public async Task CheckHealthAsync( HealthCheckContext context, CancellationToken cancellationToken = default ) { + context.CheckNull( nameof( context ) ); + try { + var healthy = await _unitOfWork.CanConnectAsync( cancellationToken ); + return healthy ? HealthCheckResult.Healthy() : new HealthCheckResult( context.Registration.FailureStatus ); + } + catch ( Exception exception ) { + return HealthCheckResult.Unhealthy( exception.Message, exception ); + } + } +} diff --git a/src/Util.Microservices.HealthChecks/HealthCheckBuilderExtensions.cs b/src/Util.Microservices.HealthChecks/HealthCheckBuilderExtensions.cs new file mode 100644 index 000000000..7bfc153f0 --- /dev/null +++ b/src/Util.Microservices.HealthChecks/HealthCheckBuilderExtensions.cs @@ -0,0 +1,53 @@ +using Util.Microservices.HealthChecks.Dapr; +using Util.Microservices.HealthChecks.EntityFrameworkCore; + +namespace Util.Microservices.HealthChecks; + +/// +/// 健康检查生成器扩展 +/// +public static class HealthCheckBuilderExtensions { + /// + /// 配置Dapr健康检查 + /// + /// 健康检查生成器 + /// 健康检查器名称,默认值:dapr + /// 标签集合 + /// 检测失败状态,默认值:HealthStatus.Unhealthy + public static IHealthChecksBuilder AddDapr( this IHealthChecksBuilder builder, string name = "dapr", IEnumerable tags = null, HealthStatus? failureStatus = null ) { + builder.CheckNull( nameof( builder ) ); + failureStatus ??= HealthStatus.Unhealthy; + tags ??= Enumerable.Empty(); + return builder.AddCheck( name, failureStatus, tags ); + } + + /// + /// 配置工作单元健康检查 + /// + /// 健康检查生成器 + /// 健康检查器名称,默认值:unitOfWork + /// 标签集合 + /// 检测失败状态,默认值:HealthStatus.Unhealthy + public static IHealthChecksBuilder AddUnitOfWork( this IHealthChecksBuilder builder, string name = "unitOfWork", IEnumerable tags = null, HealthStatus? failureStatus = null ) + where TUnitOfWork: IUnitOfWork { + builder.CheckNull( nameof( builder ) ); + failureStatus ??= HealthStatus.Unhealthy; + tags ??= Enumerable.Empty(); + return builder.AddCheck>( name, failureStatus, tags ); + } + + /// + /// 配置Url健康检查 + /// + /// 健康检查生成器 + /// 请求地址 + /// 健康检查器名称 + /// 标签集合 + /// 检测失败状态,默认值:HealthStatus.Unhealthy + public static IHealthChecksBuilder AddUrl( this IHealthChecksBuilder builder,string url, string name, IEnumerable tags = null, HealthStatus? failureStatus = null ) { + builder.CheckNull( nameof( builder ) ); + failureStatus ??= HealthStatus.Unhealthy; + tags ??= Enumerable.Empty(); + return builder.AddUrlGroup( new Uri( url ), name, failureStatus, tags ); + } +} diff --git a/src/Util.Microservices.HealthChecks/Usings.cs b/src/Util.Microservices.HealthChecks/Usings.cs new file mode 100644 index 000000000..db51d0656 --- /dev/null +++ b/src/Util.Microservices.HealthChecks/Usings.cs @@ -0,0 +1,10 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Diagnostics.HealthChecks; +global using Dapr.Client; +global using Util.Data; +global using Util.Data.EntityFrameworkCore; diff --git a/src/Util.Microservices.Polly/02-Util.Microservices.Polly.csproj b/src/Util.Microservices.Polly/02-Util.Microservices.Polly.csproj new file mode 100644 index 000000000..fa9768e2c --- /dev/null +++ b/src/Util.Microservices.Polly/02-Util.Microservices.Polly.csproj @@ -0,0 +1,36 @@ + + + $(NetTargetFramework) + icon.jpg + Util.Microservices.Polly + Util.Microservices + Util.Microservices.Polly是Util应用框架基于Polly的弹性处理操作类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Microservices.Polly.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Microservices.Polly.xml + + + + + True + False + + + + + + + + + + + + + diff --git a/src/Util.Microservices.Polly/IPolicy.cs b/src/Util.Microservices.Polly/IPolicy.cs new file mode 100644 index 000000000..de58966b3 --- /dev/null +++ b/src/Util.Microservices.Polly/IPolicy.cs @@ -0,0 +1,18 @@ +using Util.Dependency; + +namespace Util.Microservices; + +/// +/// 弹性处理策略 +/// +public interface IPolicy : ITransientDependency { + /// + /// 重试 + /// + IRetryPolicyHandler Retry(); + /// + /// 重试 + /// + /// 重试次数 + IRetryPolicyHandler Retry( int count ); +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/IRetryPolicy.TResult.cs b/src/Util.Microservices.Polly/IRetryPolicy.TResult.cs new file mode 100644 index 000000000..93085a31b --- /dev/null +++ b/src/Util.Microservices.Polly/IRetryPolicy.TResult.cs @@ -0,0 +1,35 @@ +namespace Util.Microservices; + +/// +/// 重试处理策略 +/// +public interface IRetryPolicy { + /// + /// 持续重试 + /// + IRetryPolicy Forever(); + /// + /// 重试等待 + /// + IRetryPolicy Wait(); + /// + /// 重试等待 + /// + /// 重试等待时间提供函数 + IRetryPolicy Wait( Func sleepDurationProvider ); + /// + /// 重试回调函数 + /// + /// 重试回调函数 + IRetryPolicy OnRetry( Action, int> action ); + /// + /// 执行操作 + /// + /// 操作 + TResult Execute( Func action ); + /// + /// 执行操作 + /// + /// 操作 + Task ExecuteAsync( Func> action ); +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/IRetryPolicy.cs b/src/Util.Microservices.Polly/IRetryPolicy.cs new file mode 100644 index 000000000..03ce11c7a --- /dev/null +++ b/src/Util.Microservices.Polly/IRetryPolicy.cs @@ -0,0 +1,35 @@ +namespace Util.Microservices; + +/// +/// 重试处理策略 +/// +public interface IRetryPolicy { + /// + /// 持续重试 + /// + IRetryPolicy Forever(); + /// + /// 重试等待 + /// + IRetryPolicy Wait(); + /// + /// 重试等待 + /// + /// 重试等待时间提供函数 + IRetryPolicy Wait( Func sleepDurationProvider ); + /// + /// 重试回调函数 + /// + /// 重试回调函数 + IRetryPolicy OnRetry( Action action ); + /// + /// 执行操作 + /// + /// 操作 + void Execute( Action action ); + /// + /// 执行操作 + /// + /// 操作 + Task ExecuteAsync( Func action ); +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/IRetryPolicyHandler.cs b/src/Util.Microservices.Polly/IRetryPolicyHandler.cs new file mode 100644 index 000000000..8f7440b44 --- /dev/null +++ b/src/Util.Microservices.Polly/IRetryPolicyHandler.cs @@ -0,0 +1,18 @@ +namespace Util.Microservices; + +/// +/// 重试策略处理器 +/// +public interface IRetryPolicyHandler { + /// + /// 指定触发重试的结果处理条件 + /// + /// 结果类型 + /// 触发重试的结果处理条件 + IRetryPolicy HandleResult( Func action ); + /// + /// 指定触发重试的异常 + /// + /// 异常类型 + IRetryPolicy HandleException() where TException : Exception; +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/Polly/EmptyPolicy.cs b/src/Util.Microservices.Polly/Polly/EmptyPolicy.cs new file mode 100644 index 000000000..bbe9b8216 --- /dev/null +++ b/src/Util.Microservices.Polly/Polly/EmptyPolicy.cs @@ -0,0 +1,21 @@ +namespace Util.Microservices.Polly; + +/// +/// 空弹性处理策略 +/// +public class EmptyPolicy : IPolicy { + /// + /// 空弹性处理策略实例 + /// + public static readonly IPolicy Instance = new EmptyPolicy(); + + /// + public IRetryPolicyHandler Retry() { + return EmptyRetryPolicyHandler.Instance; + } + + /// + public IRetryPolicyHandler Retry( int count ) { + return EmptyRetryPolicyHandler.Instance; + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/Polly/EmptyRetryPolicy.cs b/src/Util.Microservices.Polly/Polly/EmptyRetryPolicy.cs new file mode 100644 index 000000000..78bd15e40 --- /dev/null +++ b/src/Util.Microservices.Polly/Polly/EmptyRetryPolicy.cs @@ -0,0 +1,83 @@ +namespace Util.Microservices.Polly; + +/// +/// 空重试处理策略 +/// +public class EmptyRetryPolicy : IRetryPolicy { + /// + /// 空重试处理策略实例 + /// + public static readonly IRetryPolicy Instance = new EmptyRetryPolicy(); + + /// + public IRetryPolicy Forever() { + return this; + } + + /// + public IRetryPolicy Wait() { + return this; + } + + /// + public IRetryPolicy Wait( Func sleepDurationProvider ) { + return this; + } + + /// + public IRetryPolicy OnRetry( Action action ) { + return this; + } + + /// + public void Execute( Action action ) { + action?.Invoke(); + } + + /// + public async Task ExecuteAsync( Func action ) { + if ( action == null ) + return; + await action(); + } +} + +/// +/// 空重试处理策略 +/// +public class EmptyRetryPolicy : IRetryPolicy { + /// + /// 空重试处理策略实例 + /// + public static readonly IRetryPolicy Instance = new EmptyRetryPolicy(); + + /// + public IRetryPolicy Forever() { + return this; + } + + /// + public IRetryPolicy Wait() { + return this; + } + + /// + public IRetryPolicy Wait( Func sleepDurationProvider ) { + return this; + } + + /// + public IRetryPolicy OnRetry( Action, int> action ) { + return this; + } + + /// + public TResult Execute( Func action ) { + return action == null ? default : action(); + } + + /// + public async Task ExecuteAsync( Func> action ) { + return action == null ? default : await action(); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/Polly/EmptyRetryPolicyHandler.cs b/src/Util.Microservices.Polly/Polly/EmptyRetryPolicyHandler.cs new file mode 100644 index 000000000..a89901338 --- /dev/null +++ b/src/Util.Microservices.Polly/Polly/EmptyRetryPolicyHandler.cs @@ -0,0 +1,21 @@ +namespace Util.Microservices.Polly; + +/// +/// 空重试策略处理器 +/// +public class EmptyRetryPolicyHandler : IRetryPolicyHandler { + /// + /// 空重试策略处理器 + /// + public static readonly IRetryPolicyHandler Instance = new EmptyRetryPolicyHandler(); + + /// + public IRetryPolicy HandleResult( Func action ) { + return EmptyRetryPolicy.Instance; + } + + /// + public IRetryPolicy HandleException() where TException : Exception { + return EmptyRetryPolicy.Instance; + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/Polly/PollyPolicy.cs b/src/Util.Microservices.Polly/Polly/PollyPolicy.cs new file mode 100644 index 000000000..55355bc16 --- /dev/null +++ b/src/Util.Microservices.Polly/Polly/PollyPolicy.cs @@ -0,0 +1,19 @@ +using Util.Dependency; + +namespace Util.Microservices.Polly; + +/// +/// Polly弹性处理策略 +/// +[Ioc(1)] +public class PollyPolicy : IPolicy { + /// + public IRetryPolicyHandler Retry() { + return new PollyRetryPolicyHandler(); + } + + /// + public IRetryPolicyHandler Retry( int count ) { + return new PollyRetryPolicyHandler( count ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/Polly/PollyRetryPolicy.TResult.cs b/src/Util.Microservices.Polly/Polly/PollyRetryPolicy.TResult.cs new file mode 100644 index 000000000..0ba2cc78a --- /dev/null +++ b/src/Util.Microservices.Polly/Polly/PollyRetryPolicy.TResult.cs @@ -0,0 +1,186 @@ +namespace Util.Microservices.Polly; + +/// +/// Polly重试处理策略 +/// +public class PollyRetryPolicy : IRetryPolicy { + + #region 字段 + + /// + /// Polly策略生成器 + /// + private readonly PolicyBuilder _policyBuilder; + /// + /// 重试次数 + /// + private readonly int? _count; + /// + /// 是否持续重试 + /// + private bool _isForever; + /// + /// 是否等待 + /// + private bool _isWait; + /// + /// 重试等待时间提供函数 + /// + private Func _sleepDurationProvider; + /// + /// 重试回调函数 + /// + private Action, int> _onRetry; + + #endregion + + #region 构造方法 + + /// + /// 初始化Polly重试处理策略 + /// + /// Polly策略生成器 + /// 重试次数 + public PollyRetryPolicy( PolicyBuilder policyBuilder, int? count ) { + _policyBuilder = policyBuilder ?? throw new ArgumentNullException( nameof( policyBuilder ) ); + _count = count; + _isForever = false; + _isWait = false; + } + + #endregion + + #region Forever + + /// + public IRetryPolicy Forever() { + _isForever = true; + return this; + } + + #endregion + + #region Wait + + /// + public IRetryPolicy Wait() { + _isWait = true; + return this; + } + + /// + public IRetryPolicy Wait( Func sleepDurationProvider ) { + _isWait = true; + _sleepDurationProvider = sleepDurationProvider; + return this; + } + + #endregion + + #region OnRetry + + /// + public IRetryPolicy OnRetry( Action, int> action ) { + _onRetry = action; + return this; + } + + #endregion + + #region Execute + + /// + public TResult Execute( Func action ) { + return CreateRetryPolicy().Execute( action ); + } + + /// + /// 创建重试策略 + /// + private RetryPolicy CreateRetryPolicy() { + if ( _isWait == false ) + return CreateRetryPolicyByNoWait(); + return CreateRetryPolicyByWait(); + } + + /// + /// 创建重试策略 - 不等待 + /// + private RetryPolicy CreateRetryPolicyByNoWait() { + if ( _isForever ) + return _policyBuilder.RetryForever( OnRetry ); + return _policyBuilder.Retry( GetRetryCount(), OnRetry ); + } + + /// + /// 重试处理函数 + /// + private void OnRetry( DelegateResult result, int times ) { + if ( _onRetry == null ) + return; + _onRetry( result, times ); + } + + /// + /// 获取重试次数 + /// + private int GetRetryCount() { + return _count > 0 ? _count.SafeValue() : 1; + } + + /// + /// 创建重试策略 - 等待 + /// + private RetryPolicy CreateRetryPolicyByWait() { + if ( _isForever ) + return _policyBuilder.WaitAndRetryForever( GetSleepDurationProvider(), ( e, i, t ) => OnRetry( e, i ) ); + return _policyBuilder.WaitAndRetry( GetRetryCount(), GetSleepDurationProvider(), ( e, t, i, c ) => OnRetry( e, i ) ); + } + + /// + /// 获取重试等待时间提供函数 + /// + private Func GetSleepDurationProvider() { + if ( _sleepDurationProvider == null ) + return retryAttempt => TimeSpan.FromSeconds( Math.Pow( 2, retryAttempt ) ); + return _sleepDurationProvider; + } + + #endregion + + #region ExecuteAsync + + /// + public Task ExecuteAsync( Func> action ) { + return CreateAsyncRetryPolicy().ExecuteAsync( action ); + } + + /// + /// 创建异步重试策略 + /// + private AsyncRetryPolicy CreateAsyncRetryPolicy() { + if ( _isWait == false ) + return CreateAsyncRetryPolicyByNoWait(); + return CreateAsyncRetryPolicyByWait(); + } + + /// + /// 创建异步重试策略 - 不等待 + /// + private AsyncRetryPolicy CreateAsyncRetryPolicyByNoWait() { + if ( _isForever ) + return _policyBuilder.RetryForeverAsync( OnRetry ); + return _policyBuilder.RetryAsync( GetRetryCount(), OnRetry ); + } + + /// + /// 创建异步重试策略 - 等待 + /// + private AsyncRetryPolicy CreateAsyncRetryPolicyByWait() { + if ( _isForever ) + return _policyBuilder.WaitAndRetryForeverAsync( GetSleepDurationProvider(), ( e, i, t ) => OnRetry( e, i ) ); + return _policyBuilder.WaitAndRetryAsync( GetRetryCount(), GetSleepDurationProvider(), ( e, t, i, c ) => OnRetry( e, i ) ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/Polly/PollyRetryPolicy.cs b/src/Util.Microservices.Polly/Polly/PollyRetryPolicy.cs new file mode 100644 index 000000000..5f2095059 --- /dev/null +++ b/src/Util.Microservices.Polly/Polly/PollyRetryPolicy.cs @@ -0,0 +1,186 @@ +namespace Util.Microservices.Polly; + +/// +/// Polly重试处理策略 +/// +public class PollyRetryPolicy : IRetryPolicy { + + #region 字段 + + /// + /// Polly策略生成器 + /// + private readonly PolicyBuilder _policyBuilder; + /// + /// 重试次数 + /// + private readonly int? _count; + /// + /// 是否持续重试 + /// + private bool _isForever; + /// + /// 是否等待 + /// + private bool _isWait; + /// + /// 重试等待时间提供函数 + /// + private Func _sleepDurationProvider; + /// + /// 重试回调函数 + /// + private Action _onRetry; + + #endregion + + #region 构造方法 + + /// + /// 初始化Polly重试处理策略 + /// + /// Polly策略生成器 + /// 重试次数 + public PollyRetryPolicy( PolicyBuilder policyBuilder, int? count ) { + _policyBuilder = policyBuilder ?? throw new ArgumentNullException( nameof( policyBuilder ) ); + _count = count; + _isForever = false; + _isWait = false; + } + + #endregion + + #region Forever + + /// + public IRetryPolicy Forever() { + _isForever = true; + return this; + } + + #endregion + + #region Wait + + /// + public IRetryPolicy Wait() { + _isWait = true; + return this; + } + + /// + public IRetryPolicy Wait( Func sleepDurationProvider ) { + _isWait = true; + _sleepDurationProvider = sleepDurationProvider; + return this; + } + + #endregion + + #region OnRetry + + /// + public IRetryPolicy OnRetry( Action action ) { + _onRetry = action; + return this; + } + + #endregion + + #region Execute + + /// + public void Execute( Action action ) { + CreateRetryPolicy().Execute( action ); + } + + /// + /// 创建重试策略 + /// + private RetryPolicy CreateRetryPolicy() { + if ( _isWait == false ) + return CreateRetryPolicyByNoWait(); + return CreateRetryPolicyByWait(); + } + + /// + /// 创建重试策略 - 不等待 + /// + private RetryPolicy CreateRetryPolicyByNoWait() { + if ( _isForever ) + return _policyBuilder.RetryForever( OnRetry ); + return _policyBuilder.Retry( GetRetryCount(), OnRetry ); + } + + /// + /// 重试处理函数 + /// + private void OnRetry( Exception exception, int times ) { + if ( _onRetry == null ) + return; + _onRetry( exception, times ); + } + + /// + /// 获取重试次数 + /// + private int GetRetryCount() { + return _count > 0 ? _count.SafeValue() : 1; + } + + /// + /// 创建重试策略 - 等待 + /// + private RetryPolicy CreateRetryPolicyByWait() { + if ( _isForever ) + return _policyBuilder.WaitAndRetryForever( GetSleepDurationProvider(), ( e, i, t ) => OnRetry( e, i ) ); + return _policyBuilder.WaitAndRetry( GetRetryCount(), GetSleepDurationProvider(), ( e, t, i, c ) => OnRetry( e, i ) ); + } + + /// + /// 获取重试等待时间提供函数 + /// + private Func GetSleepDurationProvider() { + if ( _sleepDurationProvider == null ) + return retryAttempt => TimeSpan.FromSeconds( Math.Pow( 2, retryAttempt ) ); + return _sleepDurationProvider; + } + + #endregion + + #region ExecuteAsync + + /// + public Task ExecuteAsync( Func action ) { + return CreateAsyncRetryPolicy().ExecuteAsync( action ); + } + + /// + /// 创建异步重试策略 + /// + private AsyncRetryPolicy CreateAsyncRetryPolicy() { + if ( _isWait == false ) + return CreateAsyncRetryPolicyByNoWait(); + return CreateAsyncRetryPolicyByWait(); + } + + /// + /// 创建异步重试策略 - 不等待 + /// + private AsyncRetryPolicy CreateAsyncRetryPolicyByNoWait() { + if ( _isForever ) + return _policyBuilder.RetryForeverAsync( OnRetry ); + return _policyBuilder.RetryAsync( GetRetryCount(), OnRetry ); + } + + /// + /// 创建异步重试策略 - 等待 + /// + private AsyncRetryPolicy CreateAsyncRetryPolicyByWait() { + if ( _isForever ) + return _policyBuilder.WaitAndRetryForeverAsync( GetSleepDurationProvider(), ( e, i, t ) => OnRetry( e, i ) ); + return _policyBuilder.WaitAndRetryAsync( GetRetryCount(), GetSleepDurationProvider(), ( e, t, i, c ) => OnRetry( e, i ) ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/Polly/PollyRetryPolicyHandler.cs b/src/Util.Microservices.Polly/Polly/PollyRetryPolicyHandler.cs new file mode 100644 index 000000000..93166102d --- /dev/null +++ b/src/Util.Microservices.Polly/Polly/PollyRetryPolicyHandler.cs @@ -0,0 +1,29 @@ +namespace Util.Microservices.Polly; + +/// +/// Polly重试策略处理器 +/// +public class PollyRetryPolicyHandler : IRetryPolicyHandler { + /// + /// 重试次数 + /// + private readonly int? _count; + + /// + /// 初始化Polly重试策略处理器 + /// + /// 重试次数 + public PollyRetryPolicyHandler( int? count = null ) { + _count = count; + } + + /// + public IRetryPolicy HandleResult( Func action ) { + return new PollyRetryPolicy( Policy.HandleResult( action ), _count ); + } + + /// + public IRetryPolicy HandleException() where TException : Exception { + return new PollyRetryPolicy( Policy.Handle(), _count ); + } +} \ No newline at end of file diff --git a/src/Util.Microservices.Polly/Usings.cs b/src/Util.Microservices.Polly/Usings.cs new file mode 100644 index 000000000..f266e098d --- /dev/null +++ b/src/Util.Microservices.Polly/Usings.cs @@ -0,0 +1,4 @@ +global using System; +global using System.Threading.Tasks; +global using Polly; +global using Polly.Retry; \ No newline at end of file diff --git a/src/Util.Microservices/01-Util.Microservices.csproj b/src/Util.Microservices/01-Util.Microservices.csproj new file mode 100644 index 000000000..fd0338c6d --- /dev/null +++ b/src/Util.Microservices/01-Util.Microservices.csproj @@ -0,0 +1,33 @@ + + + $(NetTargetFramework) + icon.jpg + Util.Microservices + Util.Microservices + Util.Microservices是Util应用框架的微服务操作核心类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Microservices.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Microservices.xml + + + + + True + False + + + + + + + + + + diff --git a/src/Util.Microservices/Events/EventState.cs b/src/Util.Microservices/Events/EventState.cs new file mode 100644 index 000000000..49e71ac9b --- /dev/null +++ b/src/Util.Microservices/Events/EventState.cs @@ -0,0 +1,27 @@ +namespace Util.Microservices.Events; + +/// +/// 事件状态 +/// +public enum EventState { + /// + /// 已发布 + /// + [Description("util.eventState.published")] + Published = 1, + /// + /// 处理中 + /// + [Description("util.eventState.processing")] + Processing = 2, + /// + /// 所有订阅全部成功完成 + /// + [Description("util.eventState.success")] + Success = 3, + /// + /// 失败 + /// + [Description("util.eventState.fail")] + Fail = 4 +} \ No newline at end of file diff --git a/src/Util.Microservices/Events/IIntegrationEventLogStore.cs b/src/Util.Microservices/Events/IIntegrationEventLogStore.cs new file mode 100644 index 000000000..d5cc3b457 --- /dev/null +++ b/src/Util.Microservices/Events/IIntegrationEventLogStore.cs @@ -0,0 +1,34 @@ +namespace Util.Microservices.Events; + +/// +/// 集成事件日志记录存储器 +/// +public interface IIntegrationEventLogStore : ITransientDependency { + /// + /// 获取集成事件日志记录 + /// + /// 事件标识 + /// 取消令牌 + Task GetAsync( string eventId, CancellationToken cancellationToken = default ); + /// + /// 保存集成事件日志记录 + /// + /// 集成事件日志记录 + /// 取消令牌 + Task SaveAsync( IntegrationEventLog eventLog, CancellationToken cancellationToken = default ); + /// + /// 集成事件总条数加1 + /// + /// 取消令牌 + Task IncrementAsync( CancellationToken cancellationToken = default ); + /// + /// 获取集成事件总条数 + /// + /// 取消令牌 + Task GetCountAsync( CancellationToken cancellationToken = default ); + /// + /// 清空集成事件总条数 + /// + /// 取消令牌 + Task ClearCountAsync( CancellationToken cancellationToken = default ); +} \ No newline at end of file diff --git a/src/Util.Microservices/Events/IIntegrationEventManager.cs b/src/Util.Microservices/Events/IIntegrationEventManager.cs new file mode 100644 index 000000000..ca1ca66b9 --- /dev/null +++ b/src/Util.Microservices/Events/IIntegrationEventManager.cs @@ -0,0 +1,96 @@ +namespace Util.Microservices.Events; + +/// +/// 集成事件管理器 +/// +public interface IIntegrationEventManager : ITransientDependency { + /// + /// 集成事件总条数加1 + /// + /// 取消令牌 + Task IncrementAsync( CancellationToken cancellationToken = default ); + /// + /// 获取集成事件总条数 + /// + /// 取消令牌 + Task GetCountAsync( CancellationToken cancellationToken = default ); + /// + /// 清空集成事件总条数 + /// + /// 取消令牌 + Task ClearCountAsync( CancellationToken cancellationToken = default ); + /// + /// 获取集成事件日志记录 + /// + /// 事件标识 + /// 取消令牌 + Task GetAsync( string eventId, CancellationToken cancellationToken = default ); + /// + /// 保存集成事件日志记录 + /// + /// 集成事件日志记录 + /// 取消令牌 + Task SaveAsync( IntegrationEventLog eventLog, CancellationToken cancellationToken = default ); + /// + /// 是否允许处理订阅 + /// + /// 事件标识 + /// 取消令牌 + Task CanSubscriptionAsync( string eventId, CancellationToken cancellationToken = default ); + /// + /// 是否允许处理订阅 + /// + /// 集成事件日志记录 + bool CanSubscription( IntegrationEventLog eventLog ); + /// + /// 订阅是否已处理成功 + /// + /// 集成事件日志记录 + bool IsSubscriptionSuccess( IntegrationEventLog eventLog ); + /// + /// 创建集成事件发布日志记录 + /// + /// 发布订阅参数 + /// 取消令牌 + Task CreatePublishLogAsync( PubsubArgument argument, CancellationToken cancellationToken = default ); + /// + /// 创建集成事件订阅日志记录 + /// + /// 事件标识 + /// 订阅地址 + /// 取消令牌 + Task CreateSubscriptionLogAsync( string eventId, string routeUrl, CancellationToken cancellationToken = default ); + /// + /// 创建集成事件订阅日志记录 + /// + /// 集成事件日志记录 + /// 订阅地址 + /// 取消令牌 + Task CreateSubscriptionLogAsync( IntegrationEventLog eventLog, string routeUrl, CancellationToken cancellationToken = default ); + /// + /// 集成事件订阅处理成功 + /// + /// 事件标识 + /// 取消令牌 + Task SubscriptionSuccessAsync( string eventId, CancellationToken cancellationToken = default ); + /// + /// 集成事件订阅处理成功 + /// + /// 集成事件日志记录 + /// 取消令牌 + Task SubscriptionSuccessAsync( IntegrationEventLog eventLog, CancellationToken cancellationToken = default ); + /// + /// 集成事件订阅处理失败 + /// + /// 事件标识 + /// 消息 + /// 取消令牌 + Task SubscriptionFailAsync( string eventId, string message, CancellationToken cancellationToken = default ); + /// + /// 集成事件订阅处理失败 + /// + /// 集成事件日志记录 + /// 消息 + /// 取消令牌 + Task SubscriptionFailAsync( IntegrationEventLog eventLog, string message, CancellationToken cancellationToken = default ); +} \ No newline at end of file diff --git a/src/Util.Microservices/Events/IPubsubCallback.cs b/src/Util.Microservices/Events/IPubsubCallback.cs new file mode 100644 index 000000000..827ce9462 --- /dev/null +++ b/src/Util.Microservices/Events/IPubsubCallback.cs @@ -0,0 +1,46 @@ +namespace Util.Microservices.Events; + +/// +/// 发布订阅回调操作 +/// +public interface IPubsubCallback : ISingletonDependency { + /// + /// 发布前操作 + /// + /// 发布订阅参数 + /// 取消令牌 + Task OnPublishBefore( PubsubArgument argument, CancellationToken cancellationToken = default ); + /// + /// 发布后操作 + /// + /// 发布订阅参数 + /// 取消令牌 + Task OnPublishAfter( PubsubArgument argument, CancellationToken cancellationToken = default ); + /// + /// 订阅前操作 + /// + /// 集成事件日志 + /// 订阅地址 + /// 取消令牌 + Task OnSubscriptionBefore( IntegrationEventLog integrationEventLog,string routeUrl, CancellationToken cancellationToken = default ); + /// + /// 订阅后操作 + /// + /// 事件标识 + /// 是否处理成功 + /// 消息 + /// 取消令牌 + Task OnSubscriptionAfter( string eventId, bool isSuccess, string message, CancellationToken cancellationToken = default ); + /// + /// 重新发布前操作 + /// + /// 集成事件日志记录 + /// 取消令牌 + Task OnRepublishBefore( IntegrationEventLog eventLog, CancellationToken cancellationToken = default ); + /// + /// 重新发布后操作 + /// + /// 集成事件日志记录 + /// 取消令牌 + Task OnRepublishAfter( IntegrationEventLog eventLog, CancellationToken cancellationToken = default ); +} \ No newline at end of file diff --git a/src/Util.Microservices/Events/IntegrationEventLog.cs b/src/Util.Microservices/Events/IntegrationEventLog.cs new file mode 100644 index 000000000..9916dba79 --- /dev/null +++ b/src/Util.Microservices/Events/IntegrationEventLog.cs @@ -0,0 +1,73 @@ +namespace Util.Microservices.Events; + +/// +/// 空集成事件日志记录 +/// +public class NullIntegrationEventLog : IntegrationEventLog { + /// + /// 空集成事件日志记录实例 + /// + public static readonly IntegrationEventLog Instance = new NullIntegrationEventLog(); +} + +/// +/// 集成事件日志记录 +/// +public class IntegrationEventLog : IntegrationEventLog { +} + +/// +/// 集成事件日志记录 +/// +public class IntegrationEventLog : StateBase { + /// + /// 初始化集成事件日志记录 + /// + public IntegrationEventLog() { + SubscriptionLogs = new List(); + } + + /// + /// 应用标识 + /// + public string AppId { get; set; } + /// + /// 用户标识 + /// + public string UserId { get; set; } + /// + /// 发布订阅组件名称 + /// + public string PubsubName { get; set; } + /// + /// 事件主题 + /// + public string Topic { get; set; } + /// + /// 事件数据 + /// + public TValue Value { get; set; } + /// + /// 事件状态 + /// + public EventState State { get; set; } + /// + /// 发布时间 + /// + public DateTime PublishTime { get; set; } + /// + /// 最后修改时间 + /// + public DateTime LastModificationTime { get; set; } + /// + /// 订阅日志记录列表 + /// + public List SubscriptionLogs { get; set; } + + /// + /// 获取事件数据 + /// + public T GetValue() { + return (T)(object)Value; + } +} \ No newline at end of file diff --git a/src/Util.Microservices/Events/PubsubArgument.cs b/src/Util.Microservices/Events/PubsubArgument.cs new file mode 100644 index 000000000..120035b45 --- /dev/null +++ b/src/Util.Microservices/Events/PubsubArgument.cs @@ -0,0 +1,28 @@ +namespace Util.Microservices.Events; + +/// +/// 发布订阅参数 +/// +/// 发布订阅配置名称 +/// 事件主题 +/// 事件数据 +/// 元数据 +public record PubsubArgument( string Pubsub, string Topic, object EventData, Dictionary Metadata = null ) + : PubsubArgument( Pubsub, Topic, EventData, Metadata ) { +} + +/// +/// 发布订阅参数 +/// +/// 发布订阅配置名称 +/// 事件主题 +/// 事件数据 +/// 元数据 +public record PubsubArgument( string Pubsub, string Topic, TEventData EventData, Dictionary Metadata = null ) { + /// + /// 获取事件数据 + /// + public T GetEventData() { + return (T)(object)EventData; + } +} \ No newline at end of file diff --git a/src/Util.Microservices/Events/SubscriptionLog.cs b/src/Util.Microservices/Events/SubscriptionLog.cs new file mode 100644 index 000000000..827f48e3c --- /dev/null +++ b/src/Util.Microservices/Events/SubscriptionLog.cs @@ -0,0 +1,42 @@ +namespace Util.Microservices.Events; + +/// +/// 订阅日志记录 +/// +public class SubscriptionLog { + /// + /// 初始化订阅日志记录 + /// + public SubscriptionLog() { + RetryLogs = new List(); + } + + /// + /// 应用标识 + /// + public string AppId { get; set; } + /// + /// 订阅地址 + /// + public string RouteUrl { get; set; } + /// + /// 重试次数 + /// + public int? RetryCount { get; set; } + /// + /// 订阅状态 + /// + public SubscriptionState State { get; set; } + /// + /// 订阅时间 + /// + public DateTime SubscriptionTime { get; set; } + /// + /// 最后修改时间 + /// + public DateTime LastModificationTime { get; set; } + /// + /// 订阅重试日志记录列表 + /// + public List RetryLogs { get; set; } +} \ No newline at end of file diff --git a/src/Util.Microservices/Events/SubscriptionRetryLog.cs b/src/Util.Microservices/Events/SubscriptionRetryLog.cs new file mode 100644 index 000000000..28053ddd3 --- /dev/null +++ b/src/Util.Microservices/Events/SubscriptionRetryLog.cs @@ -0,0 +1,19 @@ +namespace Util.Microservices.Events; + +/// +/// 订阅重试日志记录 +/// +public class SubscriptionRetryLog { + /// + /// 重试序号 + /// + public int Number { get; set; } + /// + /// 重试时间 + /// + public DateTime? RetryTime { get; set; } + /// + /// 失败消息 + /// + public string Message { get; set; } +} \ No newline at end of file diff --git a/src/Util.Microservices/Events/SubscriptionState.cs b/src/Util.Microservices/Events/SubscriptionState.cs new file mode 100644 index 000000000..a66c146c5 --- /dev/null +++ b/src/Util.Microservices/Events/SubscriptionState.cs @@ -0,0 +1,22 @@ +namespace Util.Microservices.Events; + +/// +/// 订阅状态 +/// +public enum SubscriptionState { + /// + /// 处理中 + /// + [Description( "util.subscriptionState.processing" )] + Processing = 1, + /// + /// 成功完成 + /// + [Description( "util.subscriptionState.success" )] + Success = 2, + /// + /// 失败 + /// + [Description( "util.subscriptionState.fail" )] + Fail = 3 +} \ No newline at end of file diff --git a/src/Util.Microservices/IDataType.cs b/src/Util.Microservices/IDataType.cs new file mode 100644 index 000000000..546816a1e --- /dev/null +++ b/src/Util.Microservices/IDataType.cs @@ -0,0 +1,11 @@ +namespace Util.Microservices; + +/// +/// 数据类型 +/// +public interface IDataType { + /// + /// 数据类型 + /// + string DataType { get; set; } +} \ No newline at end of file diff --git a/src/Util.Microservices/IETag.cs b/src/Util.Microservices/IETag.cs new file mode 100644 index 000000000..9e9c7392d --- /dev/null +++ b/src/Util.Microservices/IETag.cs @@ -0,0 +1,11 @@ +namespace Util.Microservices; + +/// +/// 乐观锁 +/// +public interface IETag { + /// + /// 并发标记 + /// + string ETag { get; set; } +} \ No newline at end of file diff --git a/src/Util.Microservices/IMicroserviceClient.cs b/src/Util.Microservices/IMicroserviceClient.cs new file mode 100644 index 000000000..7ee1e0ef0 --- /dev/null +++ b/src/Util.Microservices/IMicroserviceClient.cs @@ -0,0 +1,26 @@ +using Util.Events; +using Util.Http; + +namespace Util.Microservices; + +/// +/// 微服务客户端 +/// +public interface IMicroserviceClient { + /// + /// Http客户端 + /// + IHttpClient HttpClient { get; } + /// + /// WebApi服务调用 + /// + IServiceInvocation ServiceInvocation { get; } + /// + /// 集成事件总线 + /// + IIntegrationEventBus IntegrationEventBus { get; } + /// + /// 状态管理 + /// + IStateManager StateManager { get; } +} \ No newline at end of file diff --git a/src/Util.Microservices/IMicroserviceClientFactory.cs b/src/Util.Microservices/IMicroserviceClientFactory.cs new file mode 100644 index 000000000..c77b99e62 --- /dev/null +++ b/src/Util.Microservices/IMicroserviceClientFactory.cs @@ -0,0 +1,11 @@ +namespace Util.Microservices; + +/// +/// 微服务客户端工厂 +/// +public interface IMicroserviceClientFactory : ITransientDependency { + /// + /// 创建微服务客户端 + /// + IMicroserviceClient Create(); +} \ No newline at end of file diff --git a/src/Util.Microservices/IRequestFilter.cs b/src/Util.Microservices/IRequestFilter.cs new file mode 100644 index 000000000..e55a82872 --- /dev/null +++ b/src/Util.Microservices/IRequestFilter.cs @@ -0,0 +1,20 @@ +namespace Util.Microservices; + +/// +/// WebApi服务调用请求过滤器 +/// +public interface IRequestFilter { + /// + /// 是否启用 + /// + bool Enabled { get; } + /// + /// 序号 + /// + int Order { get; } + /// + /// 处理请求 + /// + /// 请求上下文 + void Handle( RequestContext context ); +} \ No newline at end of file diff --git a/src/Util.Microservices/IResponseFilter.cs b/src/Util.Microservices/IResponseFilter.cs new file mode 100644 index 000000000..1e1dadab7 --- /dev/null +++ b/src/Util.Microservices/IResponseFilter.cs @@ -0,0 +1,20 @@ +namespace Util.Microservices; + +/// +/// WebApi服务调用响应过滤器 +/// +public interface IResponseFilter { + /// + /// 是否启用 + /// + bool Enabled { get; } + /// + /// 序号 + /// + int Order { get; } + /// + /// 处理响应 + /// + /// 响应上下文 + void Handle( ResponseContext context ); +} \ No newline at end of file diff --git a/src/Util.Microservices/IServiceInvocation.cs b/src/Util.Microservices/IServiceInvocation.cs new file mode 100644 index 000000000..71b14bf99 --- /dev/null +++ b/src/Util.Microservices/IServiceInvocation.cs @@ -0,0 +1,114 @@ +namespace Util.Microservices; + +/// +/// WebApi服务调用 +/// +public interface IServiceInvocation : IServiceInvocationBase,ITransientDependency { + /// + /// 调用前事件 + /// + /// 调用前操作,返回false取消调用 + IServiceInvocation OnBefore( Func action ); + /// + /// 响应结果转换事件 + /// + /// 响应结果转换操作 + IServiceInvocation OnResult( Func> action ); + /// + /// 调用后事件 + /// + /// 调用后处理操作 + IServiceInvocation OnAfter( Action action ); + /// + /// 调用成功事件 + /// + /// 响应结果类型 + /// 调用成功处理操作 + IServiceInvocation OnSuccess( Func action ); + /// + /// 调用失败事件 + /// + /// 调用失败处理操作 + IServiceInvocation OnFail( Func action ); + /// + /// 调用未授权事件 + /// + /// 调用未授权处理操作 + IServiceInvocation OnUnauthorized( Func action ); + /// + /// 调用完成事件 + /// + /// 调用完成处理操作 + IServiceInvocation OnComplete( Func action ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// 取消令牌 + Task InvokeAsync( string methodName, CancellationToken cancellationToken = default ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// Http方法 + /// 取消令牌 + Task InvokeAsync( string methodName, HttpMethod httpMethod, CancellationToken cancellationToken = default ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// 请求参数 + /// 取消令牌 + Task InvokeAsync( string methodName,object data, CancellationToken cancellationToken = default ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// 请求参数 + /// Http方法 + /// 取消令牌 + Task InvokeAsync( string methodName, object data, HttpMethod httpMethod, CancellationToken cancellationToken = default ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// 取消令牌 + Task InvokeAsync( string methodName, CancellationToken cancellationToken = default ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// Http方法 + /// 取消令牌 + Task InvokeAsync( string methodName, HttpMethod httpMethod, CancellationToken cancellationToken = default ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// 请求参数 + /// 取消令牌 + Task InvokeAsync( string methodName, object data, CancellationToken cancellationToken = default ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// 请求参数 + /// Http方法 + /// 取消令牌 + Task InvokeAsync( string methodName, object data, HttpMethod httpMethod, CancellationToken cancellationToken = default ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// 请求参数 + /// 取消令牌 + Task InvokeAsync( string methodName, TRequest data, CancellationToken cancellationToken = default ); + /// + /// 调用服务方法 + /// + /// 服务方法名 + /// 请求参数 + /// Http方法 + /// 取消令牌 + Task InvokeAsync( string methodName, TRequest data, HttpMethod httpMethod, CancellationToken cancellationToken = default ); +} \ No newline at end of file diff --git a/src/Util.Microservices/IServiceInvocationBase.cs b/src/Util.Microservices/IServiceInvocationBase.cs new file mode 100644 index 000000000..f158fbbd5 --- /dev/null +++ b/src/Util.Microservices/IServiceInvocationBase.cs @@ -0,0 +1,54 @@ +namespace Util.Microservices; + +/// +/// 服务调用 +/// +/// 服务调用接口类型 +public interface IServiceInvocationBase where TServiceInvocation : IServiceInvocationBase { + /// + /// 设置应用标识,即服务名 + /// + /// 应用标识,即服务名 + TServiceInvocation Service( string appId ); + /// + /// 设置或取消对约定结果进行解包处理,默认为true + /// + /// 是否对结果进行解包处理 + TServiceInvocation UnpackResult( bool isUnpack ); + /// + /// 设置访问令牌 + /// + /// 访问令牌 + TServiceInvocation BearerToken( string token ); + /// + /// 设置请求头 + /// + /// 键 + /// 值 + TServiceInvocation Header( string key, string value ); + /// + /// 设置请求头 + /// + /// 请求头键值对集合 + TServiceInvocation Header( IDictionary headers ); + /// + /// 从当前HttpContext导入请求头 + /// + /// 请求头键名 + TServiceInvocation ImportHeader( string key ); + /// + /// 从当前HttpContext导入请求头 + /// + /// 请求头键名集合 + TServiceInvocation ImportHeader( IEnumerable keys ); + /// + /// 移除请求头 + /// + /// 请求头键名 + TServiceInvocation RemoveHeader( string key ); + /// + /// 服务状态码转换事件 + /// + /// 转换为服务状态码,参数为业务状态码或Http状态码,取决于是否使用约定结果类型 + TServiceInvocation OnState( Func action ); +} \ No newline at end of file diff --git a/src/Util.Microservices/IStateCondition.cs b/src/Util.Microservices/IStateCondition.cs new file mode 100644 index 000000000..edce44ff6 --- /dev/null +++ b/src/Util.Microservices/IStateCondition.cs @@ -0,0 +1,7 @@ +namespace Util.Microservices; + +/// +/// 状态对象查询条件 +/// +public interface IStateCondition { +} \ No newline at end of file diff --git a/src/Util.Microservices/IStateManager.cs b/src/Util.Microservices/IStateManager.cs new file mode 100644 index 000000000..7a57488f1 --- /dev/null +++ b/src/Util.Microservices/IStateManager.cs @@ -0,0 +1,69 @@ +namespace Util.Microservices; + +/// +/// 状态管理 +/// +public interface IStateManager : IStateManagerBase { + /// + /// 设置升序排序属性 + /// + /// 属性表达式 + IStateManager OrderBy( Expression> expression ); + /// + /// 设置降序排序属性 + /// + /// 属性表达式 + IStateManager OrderByDescending( Expression> expression ); + /// + /// 设置相等条件 + /// + /// 属性表达式 + /// 属性值 + IStateManager Equal( Expression> expression, object value ); + /// + /// 设置In条件 + /// + /// 属性表达式 + /// 属性值 + IStateManager In( Expression> expression, IEnumerable values ); + /// + /// 设置In条件 + /// + /// 属性表达式 + /// 属性值 + IStateManager In( Expression> expression, params object[] values ); +} + +/// +/// 状态管理 +/// +public interface IStateManager : IStateManagerBase> { + /// + /// 设置升序排序属性 + /// + /// 属性表达式 + IStateManager OrderBy( Expression> expression ); + /// + /// 设置降序排序属性 + /// + /// 属性表达式 + IStateManager OrderByDescending( Expression> expression ); + /// + /// 设置相等条件 + /// + /// 属性名表达式 + /// 属性值 + IStateManager Equal( Expression> expression, object value ); + /// + /// 设置In条件 + /// + /// 属性名表达式 + /// 属性值 + IStateManager In( Expression> expression, IEnumerable values ); + /// + /// 设置In条件 + /// + /// 属性名表达式 + /// 属性值 + IStateManager In( Expression> expression, params object[] values ); +} \ No newline at end of file diff --git a/src/Util.Microservices/IStateManagerBase.cs b/src/Util.Microservices/IStateManagerBase.cs new file mode 100644 index 000000000..1ebb45ea9 --- /dev/null +++ b/src/Util.Microservices/IStateManagerBase.cs @@ -0,0 +1,209 @@ +using Util.Data.Queries; + +namespace Util.Microservices; + +/// +/// 状态管理 +/// +public interface IStateManagerBase : ITransientDependency where TStateManager : IStateManagerBase { + /// + /// 设置状态组件名称 + /// + /// 状态组件名称 + TStateManager StoreName( string storeName ); + /// + /// 清理 + /// + TStateManager Clear(); + /// + /// 开始事务 + /// + TStateManager BeginTransaction(); + /// + /// 设置Json序列化配置 + /// + /// Json序列化配置 + TStateManager JsonSerializerOptions( JsonSerializerOptions options ); + /// + /// 设置元数据 + /// + /// 键 + /// 值 + TStateManager Metadata( string key, string value ); + /// + /// 设置元数据 + /// + /// 元数据键值对集合 + TStateManager Metadata( IDictionary metadata ); + /// + /// 移除元数据 + /// + /// 键 + TStateManager RemoveMetadata( string key ); + /// + /// 设置内容类型: contentType + /// + /// 内容类型 + TStateManager ContentType( string type ); + /// + /// 添加分页大小 + /// + /// 分页大小 + TStateManager Limit( int pageSize ); + /// + /// 添加迭代令牌,即分页跳过行数 + /// + /// 迭代令牌,即分页跳过行数 + TStateManager Token( int token ); + /// + /// 添加排序条件 + /// + /// 排序条件,范例: a,b desc + TStateManager OrderBy( string orderBy ); + /// + /// 设置相等条件 + /// + /// 属性名 + /// 属性值 + TStateManager Equal( string property, object value ); + /// + /// 根据规则添加相等查询条件 + /// + /// 属性名 + /// 属性值 + /// 该值为true时添加查询条件,否则忽略 + TStateManager EqualIf( string property, object value, bool condition ); + /// + /// 设置In条件 + /// + /// 属性名 + /// 属性值 + TStateManager In( string property, IEnumerable values ); + /// + /// 设置In条件 + /// + /// 属性名 + /// 属性值 + TStateManager In( string property, params object[] values ); + /// + /// 设置And连接条件 + /// + /// 属性名 + TStateManager And( params IStateCondition[] conditions ); + /// + /// 设置Or连接条件 + /// + /// 属性名 + TStateManager Or( params IStateCondition[] conditions ); + /// + /// 添加数据 + /// + /// 值类型 + /// 键名 + /// 值 + /// 取消令牌 + Task AddAsync( string key, TValue value, CancellationToken cancellationToken = default ); + /// + /// 更新数据 + /// + /// 值类型 + /// 键名 + /// 值 + /// 并发标记 + /// 取消令牌 + Task UpdateAsync( string key, TValue value, string etag, CancellationToken cancellationToken = default ); + /// + /// 删除数据 + /// + /// 键名 + /// 取消令牌 + Task RemoveAsync( string key, CancellationToken cancellationToken = default ); + /// + /// 通过标识删除数据 + /// + /// 值类型 + /// 标识 + /// 取消令牌 + Task RemoveByIdAsync( string id, CancellationToken cancellationToken = default ) where TValue : IDataKey; + /// + /// 保存数据,返回存储键名 + /// + /// 值类型 + /// 值 + /// 取消令牌 + /// 键名 + Task SaveAsync( TValue value, CancellationToken cancellationToken = default, string key = null ) where TValue : IDataKey, IETag; + /// + /// 批量保存数据 + /// + /// 值类型 + /// 值 + /// 取消令牌 + Task SaveAsync( IEnumerable values, CancellationToken cancellationToken = default ) where TValue : IDataKey, IETag; + /// + /// 提交事务 + /// + /// 取消令牌 + Task CommitAsync( CancellationToken cancellationToken = default ); + /// + /// 通过标识获取存储键 + /// + /// 值类型 + /// 标识 + /// 取消令牌 + Task GetKeyByIdAsync( string id, CancellationToken cancellationToken = default ) where TValue : IDataKey; + /// + /// 获取数据和并发标记 + /// + /// 值类型 + /// 键名 + /// 取消令牌 + Task<(TValue value, string etag)> GetStateAndETagAsync( string key, CancellationToken cancellationToken = default ); + /// + /// 通过键名获取数据 + /// + /// 值类型 + /// 键名 + /// 取消令牌 + Task GetAsync( string key, CancellationToken cancellationToken = default ); + /// + /// 通过键名获取数据 + /// + /// 值类型 + /// 键列表 + /// 并行度 + /// 取消令牌 + Task> GetAsync( IReadOnlyList keys, int? parallelism = 0, CancellationToken cancellationToken = default ); + /// + /// 通过标识获取数据 + /// + /// 值类型 + /// 标识 + /// 取消令牌 + Task GetByIdAsync( string id, CancellationToken cancellationToken = default ) where TValue : IDataKey; + /// + /// 获取指定类型的单条数据 + /// + /// 值类型 + /// 取消令牌 + Task SingleAsync( CancellationToken cancellationToken = default ) where TValue : IDataKey; + /// + /// 获取指定类型的全部数据 + /// + /// 值类型 + /// 取消令牌 + Task> GetAllAsync( CancellationToken cancellationToken = default ); + /// + /// 查询 + /// + /// 值类型 + /// 取消令牌 + Task> QueryAsync( CancellationToken cancellationToken = default ); + /// + /// 分页查询 + /// + /// 值类型 + /// 分页参数 + /// 取消令牌 + Task> PageQueryAsync( IPage page, CancellationToken cancellationToken = default ); +} \ No newline at end of file diff --git a/src/Util.Microservices/PubsubNameAttribute.cs b/src/Util.Microservices/PubsubNameAttribute.cs new file mode 100644 index 000000000..ac9d4d787 --- /dev/null +++ b/src/Util.Microservices/PubsubNameAttribute.cs @@ -0,0 +1,38 @@ +namespace Util.Microservices; + +/// +/// 发布订阅名称 +/// +public class PubsubNameAttribute : Attribute { + /// + /// 发布订阅名称 + /// + public string Name { get; } + + /// + /// 初始化发布订阅名称 + /// + /// 发布订阅名称 + public PubsubNameAttribute( string name ) { + Name = name; + } + + /// + /// 获取发布订阅名称,未设置则返回默认值: pubsub + /// + /// 类型 + public static string GetName() { + return GetName( typeof( T ) ); + } + + /// + /// 获取发布订阅名称 + /// + /// 类型 + public static string GetName( Type type ) { + if ( type == null ) + return null; + var attribute = type.GetCustomAttribute(); + return attribute == null || attribute.Name.IsEmpty() ? "pubsub" : attribute.Name; + } +} \ No newline at end of file diff --git a/src/Util.Microservices/RequestContext.cs b/src/Util.Microservices/RequestContext.cs new file mode 100644 index 000000000..efad92337 --- /dev/null +++ b/src/Util.Microservices/RequestContext.cs @@ -0,0 +1,26 @@ +namespace Util.Microservices; + +/// +/// WebApi服务调用请求上下文 +/// +public class RequestContext { + /// + /// 初始化WebApi服务调用请求上下文 + /// + /// 请求消息 + /// Http上下文 + public RequestContext( HttpRequestMessage requestMessage, HttpContext context ) { + RequestMessage = requestMessage; + HttpContext = context; + } + + /// + /// 请求消息 + /// + public HttpRequestMessage RequestMessage { get; } + + /// + /// Http上下文 + /// + public HttpContext HttpContext { get; } +} \ No newline at end of file diff --git a/src/Util.Microservices/ResponseContext.cs b/src/Util.Microservices/ResponseContext.cs new file mode 100644 index 000000000..51d2baec0 --- /dev/null +++ b/src/Util.Microservices/ResponseContext.cs @@ -0,0 +1,26 @@ +namespace Util.Microservices; + +/// +/// WebApi服务调用响应上下文 +/// +public class ResponseContext { + /// + /// 初始化WebApi服务调用响应上下文 + /// + /// 响应消息 + /// Http上下文 + public ResponseContext( HttpResponseMessage responseMessage, HttpContext context ) { + ResponseMessage = responseMessage; + HttpContext = context; + } + + /// + /// 响应消息 + /// + public HttpResponseMessage ResponseMessage { get; } + + /// + /// Http上下文 + /// + public HttpContext HttpContext { get; } +} \ No newline at end of file diff --git a/src/Util.Microservices/ServiceResult.cs b/src/Util.Microservices/ServiceResult.cs new file mode 100644 index 000000000..69d14f2fb --- /dev/null +++ b/src/Util.Microservices/ServiceResult.cs @@ -0,0 +1,19 @@ +namespace Util.Microservices; + +/// +/// 服务约定结果 +/// +public class ServiceResult { + /// + /// 状态码 + /// + public string Code { get; set; } + /// + /// 消息 + /// + public string Message { get; set; } + /// + /// 数据 + /// + public TData Data { get; set; } +} \ No newline at end of file diff --git a/src/Util.Microservices/ServiceState.cs b/src/Util.Microservices/ServiceState.cs new file mode 100644 index 000000000..9ce4a42fd --- /dev/null +++ b/src/Util.Microservices/ServiceState.cs @@ -0,0 +1,19 @@ +namespace Util.Microservices; + +/// +/// 服务状态码 +/// +public enum ServiceState { + /// + /// 失败 + /// + Fail, + /// + /// 成功 + /// + Ok, + /// + /// 未授权 + /// + Unauthorized +} \ No newline at end of file diff --git a/src/Util.Microservices/StateBase.cs b/src/Util.Microservices/StateBase.cs new file mode 100644 index 000000000..fcfed080a --- /dev/null +++ b/src/Util.Microservices/StateBase.cs @@ -0,0 +1,15 @@ +namespace Util.Microservices; + +/// +/// 状态对象基类 +/// +public class StateBase : IDataKey, IDataType, IETag { + /// + public string Id { get; set; } + + /// + public string DataType { get; set; } + + /// + public string ETag { get; set; } +} \ No newline at end of file diff --git a/src/Util.Microservices/TopicNameAttribute.cs b/src/Util.Microservices/TopicNameAttribute.cs new file mode 100644 index 000000000..e1c16bc2b --- /dev/null +++ b/src/Util.Microservices/TopicNameAttribute.cs @@ -0,0 +1,38 @@ +namespace Util.Microservices; + +/// +/// 事件主题名称 +/// +public class TopicNameAttribute : Attribute { + /// + /// 事件主题名称 + /// + public string Name { get; } + + /// + /// 初始化事件主题名称 + /// + /// 事件主题名称 + public TopicNameAttribute( string name ) { + Name = name; + } + + /// + /// 获取事件主题名称 + /// + /// 类型 + public static string GetName() { + return GetName( typeof( T ) ); + } + + /// + /// 获取事件主题名称 + /// + /// 类型 + public static string GetName( Type type ) { + if ( type == null ) + return null; + var attribute = type.GetCustomAttribute(); + return attribute == null || attribute.Name.IsEmpty() ? type.Name : attribute.Name; + } +} \ No newline at end of file diff --git a/src/Util.Microservices/Usings.cs b/src/Util.Microservices/Usings.cs new file mode 100644 index 000000000..6763120d6 --- /dev/null +++ b/src/Util.Microservices/Usings.cs @@ -0,0 +1,12 @@ +global using System; +global using System.Collections.Generic; +global using System.Threading.Tasks; +global using System.Threading; +global using System.Net.Http; +global using System.Text.Json; +global using System.Linq.Expressions; +global using System.ComponentModel; +global using System.Reflection; +global using Microsoft.AspNetCore.Http; +global using Util.Dependency; +global using Util.Data; \ No newline at end of file diff --git a/src/Util.ObjectMapping.AutoMapper/02-Util.ObjectMapping.AutoMapper.csproj b/src/Util.ObjectMapping.AutoMapper/02-Util.ObjectMapping.AutoMapper.csproj new file mode 100644 index 000000000..b862159bb --- /dev/null +++ b/src/Util.ObjectMapping.AutoMapper/02-Util.ObjectMapping.AutoMapper.csproj @@ -0,0 +1,36 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.ObjectMapping.AutoMapper + Util.ObjectMapping + Util.ObjectMapping.AutoMapper是Util应用框架的AutoMapper对象映射封装类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.ObjectMapping.AutoMapper.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.ObjectMapping.AutoMapper.xml + + + + + True + False + + + + + + + + + + + + diff --git a/src/Util.ObjectMapping.AutoMapper/AutoMapperExpressionExtensions.cs b/src/Util.ObjectMapping.AutoMapper/AutoMapperExpressionExtensions.cs new file mode 100644 index 000000000..e94c420dd --- /dev/null +++ b/src/Util.ObjectMapping.AutoMapper/AutoMapperExpressionExtensions.cs @@ -0,0 +1,13 @@ +namespace Util.ObjectMapping; + +/// +/// AutoMapper配置表达式扩展 +/// +public static class AutoMapperExpressionExtensions { + /// + /// 忽略属性 + /// + public static IMappingExpression Ignore( this IMappingExpression mappingExpression, Expression> destinationMember ) { + return mappingExpression.ForMember( destinationMember, options => options.Ignore() ); + } +} \ No newline at end of file diff --git a/src/Util.ObjectMapping.AutoMapper/IAutoMapperConfig.cs b/src/Util.ObjectMapping.AutoMapper/IAutoMapperConfig.cs new file mode 100644 index 000000000..01c7a883a --- /dev/null +++ b/src/Util.ObjectMapping.AutoMapper/IAutoMapperConfig.cs @@ -0,0 +1,12 @@ +namespace Util.ObjectMapping; + +/// +/// AutoMapper配置 +/// +public interface IAutoMapperConfig { + /// + /// 配置映射 + /// + /// 配置映射表达式 + void Config( IMapperConfigurationExpression expression ); +} \ No newline at end of file diff --git a/src/Util.ObjectMapping.AutoMapper/Infrastructure/AutoMapperServiceRegistrar.cs b/src/Util.ObjectMapping.AutoMapper/Infrastructure/AutoMapperServiceRegistrar.cs new file mode 100644 index 000000000..c04fdb51e --- /dev/null +++ b/src/Util.ObjectMapping.AutoMapper/Infrastructure/AutoMapperServiceRegistrar.cs @@ -0,0 +1,41 @@ +using Util.Helpers; +using Util.Infrastructure; + +namespace Util.ObjectMapping.Infrastructure; + +/// +/// AutoMapper服务注册器 +/// +public class AutoMapperServiceRegistrar : IServiceRegistrar { + /// + /// 获取服务名 + /// + public static string ServiceName => "Util.ObjectMapping.Infrastructure.AutoMapperServiceRegistrar"; + + /// + /// 排序号 + /// + public int OrderId => 300; + + /// + /// 是否启用 + /// + public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName ); + + /// + /// 注册服务 + /// + /// 服务上下文 + public Action Register( ServiceContext serviceContext ) { + var types = serviceContext.TypeFinder.Find(); + var instances = types.Select( type => Reflection.CreateInstance( type ) ).ToList(); + var expression = new MapperConfigurationExpression(); + instances.ForEach( t => t.Config( expression ) ); + var mapper = new ObjectMapper( expression ); + ObjectMapperExtensions.SetMapper( mapper ); + serviceContext.HostBuilder.ConfigureServices( ( context, services ) => { + services.AddSingleton( mapper ); + } ); + return null; + } +} \ No newline at end of file diff --git a/src/Util.ObjectMapping.AutoMapper/Infrastructure/ServiceRegistrarConfigExtensions.cs b/src/Util.ObjectMapping.AutoMapper/Infrastructure/ServiceRegistrarConfigExtensions.cs new file mode 100644 index 000000000..657bbb1f1 --- /dev/null +++ b/src/Util.ObjectMapping.AutoMapper/Infrastructure/ServiceRegistrarConfigExtensions.cs @@ -0,0 +1,26 @@ +using Util.Infrastructure; + +namespace Util.ObjectMapping.Infrastructure; + +/// +/// AutoMapper服务注册器配置扩展 +/// +public static class ServiceRegistrarConfigExtensions { + /// + /// 启用AutoMapper服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig EnableAutoMapperServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Enable( AutoMapperServiceRegistrar.ServiceName ); + return config; + } + + /// + ///禁用AutoMapper服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig DisableAutoMapperServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Disable( AutoMapperServiceRegistrar.ServiceName ); + return config; + } +} \ No newline at end of file diff --git a/src/Util.ObjectMapping.AutoMapper/ObjectMapper.cs b/src/Util.ObjectMapping.AutoMapper/ObjectMapper.cs new file mode 100644 index 000000000..4b9e69f8b --- /dev/null +++ b/src/Util.ObjectMapping.AutoMapper/ObjectMapper.cs @@ -0,0 +1,127 @@ +using Util.Helpers; + +namespace Util.ObjectMapping; + +/// +/// AutoMapper对象映射器 +/// +public class ObjectMapper : IObjectMapper { + /// + /// 最大递归获取结果次数 + /// + private const int MaxGetResultCount = 16; + /// + /// 同步锁 + /// + private static readonly object Sync = new(); + /// + /// 配置表达式 + /// + private readonly MapperConfigurationExpression _configExpression; + /// + /// 配置提供器 + /// + private IConfigurationProvider _config; + /// + /// 对象映射器 + /// + private IMapper _mapper; + + /// + /// 初始化AutoMapper对象映射器 + /// + /// 配置表达式 + public ObjectMapper( MapperConfigurationExpression expression ) { + _configExpression = expression ?? throw new ArgumentNullException( nameof( expression ) ); + _config = new MapperConfiguration( expression ); + _mapper = _config.CreateMapper(); + } + + /// + /// 将源对象映射到目标对象 + /// + /// 源类型 + /// 目标类型 + /// 源对象 + public TDestination Map( TSource source ) { + return Map( source, default ); + } + + /// + /// 将源对象映射到目标对象 + /// + /// 源类型 + /// 目标类型 + /// 源对象 + /// 目标对象 + public TDestination Map( TSource source, TDestination destination ) { + if ( source == null ) + return default; + var sourceType = GetType( source ); + var destinationType = GetType( destination ); + return GetResult( sourceType, destinationType, source, destination,0 ); + } + + /// + /// 获取类型 + /// + private Type GetType( T obj ) { + if( obj == null ) + return GetType( typeof( T ) ); + return GetType( obj.GetType() ); + } + + /// + /// 获取类型 + /// + private Type GetType( Type type ) { + return Reflection.GetElementType( type ); + } + + /// + /// 获取结果 + /// + private TDestination GetResult( Type sourceType, Type destinationType, object source, TDestination destination,int i ) { + try { + if ( i >= MaxGetResultCount ) + return default; + i += 1; + if ( Exists( sourceType, destinationType ) ) + return GetResult( source, destination ); + lock ( Sync ) { + if ( Exists( sourceType, destinationType ) ) + return GetResult( source, destination ); + ConfigMap( sourceType, destinationType ); + } + return GetResult( source, destination ); + } + catch ( AutoMapperMappingException ex ) { + if ( ex.InnerException != null && ex.InnerException.Message.StartsWith( "Missing type map configuration" ) ) + return GetResult( GetType( ex.MemberMap.SourceType ), GetType( ex.MemberMap.DestinationType ), source, destination,i ); + throw; + } + } + + /// + /// 是否已存在映射配置 + /// + private bool Exists( Type sourceType, Type destinationType ) { + return _config.Internal().FindTypeMapFor( sourceType, destinationType ) != null; + } + + /// + /// 获取映射结果 + /// + private TDestination GetResult( TSource source, TDestination destination ) { + return _mapper.Map( source, destination ); + } + + /// + /// 动态配置映射 + /// + private void ConfigMap( Type sourceType, Type destinationType ) { + _configExpression.CreateMap( sourceType, destinationType ); + _config = new MapperConfiguration( _configExpression ); + _mapper = _config.CreateMapper(); + } +} \ No newline at end of file diff --git a/src/Util.ObjectMapping.AutoMapper/Usings.cs b/src/Util.ObjectMapping.AutoMapper/Usings.cs new file mode 100644 index 000000000..a442e3fe0 --- /dev/null +++ b/src/Util.ObjectMapping.AutoMapper/Usings.cs @@ -0,0 +1,6 @@ +global using System; +global using System.Linq; +global using System.Linq.Expressions; +global using Microsoft.Extensions.DependencyInjection; +global using AutoMapper; +global using AutoMapper.Internal; diff --git a/src/Util.QrCode.ZXing/03-Util.QrCode.ZXing.csproj b/src/Util.QrCode.ZXing/03-Util.QrCode.ZXing.csproj new file mode 100644 index 000000000..ef6f08182 --- /dev/null +++ b/src/Util.QrCode.ZXing/03-Util.QrCode.ZXing.csproj @@ -0,0 +1,38 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.QrCode.ZXing + Util.QrCode + Util.QrCode.ZXing是Util应用框架基于ZXing.Net的条形码操作类库,用于二维码生成 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.QrCode.ZXing.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.QrCode.ZXing.xml + + + + + True + False + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Util.QrCode.ZXing/ErrorCorrectionLevel.cs b/src/Util.QrCode.ZXing/ErrorCorrectionLevel.cs new file mode 100644 index 000000000..16fcb3a16 --- /dev/null +++ b/src/Util.QrCode.ZXing/ErrorCorrectionLevel.cs @@ -0,0 +1,23 @@ +namespace Util.QrCode; + +/// +/// 容错级别 +/// +public enum ErrorCorrectionLevel { + /// + /// 可以纠正最大7%的错误 + /// + L, + /// + /// 可以纠正最大15%的错误 + /// + M, + /// + /// 可以纠正最大25%的错误 + /// + Q, + /// + /// 可以纠正最大30%的错误 + /// + H +} \ No newline at end of file diff --git a/src/Util.QrCode.ZXing/IQrCodeService.cs b/src/Util.QrCode.ZXing/IQrCodeService.cs new file mode 100644 index 000000000..bfc278fc1 --- /dev/null +++ b/src/Util.QrCode.ZXing/IQrCodeService.cs @@ -0,0 +1,86 @@ +using Util.Images; + +namespace Util.QrCode; + +/// +/// 二维码服务 +/// +public interface IQrCodeService : ITransientDependency { + /// + /// 设置二维码内容 + /// + /// 二维码内容 + IQrCodeService Content( string content ); + /// + /// 设置二维码尺寸 + /// + /// 二维码尺寸 + IQrCodeService Size( QrSize size ); + /// + /// 设置二维码尺寸 + /// + /// 二维码尺寸 + IQrCodeService Size( int size ); + /// + /// 容错处理 + /// + /// 容错级别 + IQrCodeService Correction( ErrorCorrectionLevel level ); + /// + /// 设置图片类型 + /// + /// 图片类型 + IQrCodeService ImageType( ImageType type ); + /// + /// 设置前景色 + /// + /// 前景色 + IQrCodeService Color( Color color ); + /// + /// 设置背景色 + /// + /// 背景色 + IQrCodeService BgColor( Color color ); + /// + /// 设置边距 + /// + /// 边距,单位:像素 + IQrCodeService Margin( int margin ); + /// + /// 设置图标 + /// + /// 图标绝对路径 + IQrCodeService Icon( string path ); + /// + /// 获取二维码流 + /// + Stream ToStream(); + /// + /// 获取二维码字节流 + /// + byte[] ToBytes(); + /// + /// 获取二维码Base64字符串 + /// + string ToBase64(); + /// + /// 保存二维码图片 + /// + /// 文件绝对路径 + void Save( string path ); + /// + /// 获取二维码流 + /// + /// 取消令牌 + Task ToStreamAsync( CancellationToken cancellationToken = default ); + /// + /// 获取二维码字节流 + /// + Task ToBytesAsync( CancellationToken cancellationToken = default ); + /// + /// 保存二维码图片 + /// + /// 文件绝对路径 + /// 取消令牌 + Task SaveAsync( string path, CancellationToken cancellationToken = default ); +} \ No newline at end of file diff --git a/src/Util.QrCode.ZXing/QrSize.cs b/src/Util.QrCode.ZXing/QrSize.cs new file mode 100644 index 000000000..eeea34713 --- /dev/null +++ b/src/Util.QrCode.ZXing/QrSize.cs @@ -0,0 +1,19 @@ +namespace Util.QrCode; + +/// +/// 二维码尺寸 +/// +public enum QrSize { + /// + /// 小 + /// + Small = 100, + /// + /// 中 + /// + Middle = 200, + /// + /// 大 + /// + Large = 300 +} \ No newline at end of file diff --git a/src/Util.QrCode.ZXing/Usings.cs b/src/Util.QrCode.ZXing/Usings.cs new file mode 100644 index 000000000..f7ef8bbe2 --- /dev/null +++ b/src/Util.QrCode.ZXing/Usings.cs @@ -0,0 +1,12 @@ +global using System; +global using System.Threading.Tasks; +global using System.Threading; +global using System.Drawing; +global using System.IO; +global using SixLabors.ImageSharp.Formats; +global using SixLabors.ImageSharp.Processing; +global using ZXing; +global using ZXing.QrCode; +global using ZXing.ImageSharp.Rendering; +global using SixLabors.ImageSharp.PixelFormats; +global using Util.Dependency; \ No newline at end of file diff --git a/src/Util.QrCode.ZXing/ZXing/ZXingQrCodeService.cs b/src/Util.QrCode.ZXing/ZXing/ZXingQrCodeService.cs new file mode 100644 index 000000000..0641a5f46 --- /dev/null +++ b/src/Util.QrCode.ZXing/ZXing/ZXingQrCodeService.cs @@ -0,0 +1,228 @@ +using Util.Images; +using SixLabors.ImageSharp; +using CorrectionLevel = ZXing.QrCode.Internal.ErrorCorrectionLevel; + +namespace Util.QrCode.ZXing; + +/// +/// ZXing二维码服务 +/// +public class ZXingQrCodeService : IQrCodeService { + /// + /// 内容 + /// + private string _content; + /// + /// 二维码尺寸 + /// + private int _size; + /// + /// 容错级别 + /// + private CorrectionLevel _level; + /// + /// 图片类型 + /// + private ImageType _imageType; + /// + /// 前景色 + /// + private System.Drawing.Color _foreground; + /// + /// 背景色 + /// + private System.Drawing.Color _background; + /// + /// 边距 + /// + private int _margin; + /// + /// 图标路径 + /// + private string _iconPath; + + /// + /// 初始化二维码服务 + /// + public ZXingQrCodeService() { + _level = CorrectionLevel.L; + _imageType = Images.ImageType.Png; + _foreground = System.Drawing.Color.Black; + _background = System.Drawing.Color.White; + _margin = 0; + Size( 160 ); + } + + /// + public IQrCodeService Content( string content ) { + _content = content; + return this; + } + + /// + public IQrCodeService Size( QrSize size ) { + return Size( size.Value().SafeValue() ); + } + + /// + public IQrCodeService Size( int size ) { + _size = size; + return this; + } + + /// + public IQrCodeService Correction( ErrorCorrectionLevel level ) { + switch ( level ) { + case ErrorCorrectionLevel.L: + _level = CorrectionLevel.L; + break; + case ErrorCorrectionLevel.M: + _level = CorrectionLevel.M; + break; + case ErrorCorrectionLevel.Q: + _level = CorrectionLevel.Q; + break; + case ErrorCorrectionLevel.H: + _level = CorrectionLevel.H; + break; + } + return this; + } + + /// + public IQrCodeService ImageType( ImageType type ) { + _imageType = type; + return this; + } + + /// + public IQrCodeService Color( System.Drawing.Color color ) { + _foreground = color; + return this; + } + + /// + public IQrCodeService BgColor( System.Drawing.Color color ) { + _background = color; + return this; + } + + /// + public IQrCodeService Margin( int margin ) { + _margin = margin; + return this; + } + + /// + public IQrCodeService Icon( string path ) { + _iconPath = path; + return this; + } + + /// + public Stream ToStream() { + var bytes = ToBytes(); + return new MemoryStream( bytes ); + } + + /// + public byte[] ToBytes() { + using var image = GetImage(); + using var stream = new MemoryStream(); + image.Save( stream, GetImageEncoder() ); + return stream.ToArray(); + } + + /// + public string ToBase64() { + using var image = GetImage(); + return image.ToBase64String( GetImageFormat() ); + } + + /// + /// 获取图片格式 + /// + private IImageFormat GetImageFormat() { + return ImageWrapper.GetImageFormat( _imageType ); + } + + /// + public void Save( string path ) { + if ( path.IsEmpty() ) + return; + Util.Helpers.File.CreateDirectory( path ); + using var image = GetImage(); + image.Save( path, GetImageEncoder() ); + } + + /// + public async Task ToStreamAsync( CancellationToken cancellationToken = default ) { + var bytes = await ToBytesAsync( cancellationToken ); + return new MemoryStream( bytes ); + } + + /// + public async Task ToBytesAsync( CancellationToken cancellationToken = default ) { + using var image = GetImage(); + using var stream = new MemoryStream(); + await image.SaveAsync( stream, GetImageEncoder(), cancellationToken ); + return stream.ToArray(); + } + + /// + /// 获取二维码图片 + /// + private Image GetImage() { + if ( _content.IsEmpty() ) + throw new ArgumentException( "必须设置内容" ); + var writer = new BarcodeWriter> { + Format = BarcodeFormat.QR_CODE, + Options = new QrCodeEncodingOptions { + CharacterSet = "UTF-8", + ErrorCorrection = _level, + Margin = _margin, + Width = _size, + Height = _size + }, + Renderer = new ImageSharpRenderer { + Foreground = _foreground.ToImageSharpColor(), + Background = _background.ToImageSharpColor() + } + }; + var result = writer.Write( _content ); + return _iconPath.IsEmpty() ? result : MergeImage( result, Image.Load( _iconPath ) ); + } + + /// + /// 合并图片 + /// + private Image MergeImage( Image image, Image icon ) { + var margin = 10 - _margin; + if ( margin <= 0 ) + margin = 5; + var width = ( image.Width * margin - 46 * margin ) * 1.0f / 46; + icon.Zoom( width / icon.Width ); + icon.RoundCorners( 7 ); + image.RoundCorners( 7 ); + image.Mutate( x => { + x.DrawImage( icon, new SixLabors.ImageSharp.Point( ( image.Width - icon.Width ) / 2, ( image.Height - icon.Height ) / 2 ), 1 ); + } ); + return image; + } + + /// + /// 获取图片编码器 + /// + private IImageEncoder GetImageEncoder() { + return ImageWrapper.GetImageEncoder( _imageType ); + } + + /// + public async Task SaveAsync( string path, CancellationToken cancellationToken = default ) { + if ( path.IsEmpty() ) + return; + Util.Helpers.File.CreateDirectory( path ); + using var image = GetImage(); + await image.SaveAsync( path, GetImageEncoder(), cancellationToken ); + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Hangfire/03-Util.Scheduling.Hangfire.csproj b/src/Util.Scheduling.Hangfire/03-Util.Scheduling.Hangfire.csproj new file mode 100644 index 000000000..5bce45ea0 --- /dev/null +++ b/src/Util.Scheduling.Hangfire/03-Util.Scheduling.Hangfire.csproj @@ -0,0 +1,37 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Scheduling.Hangfire + Util.Scheduling + Util.Scheduling.Hangfire是Util应用框架基于Hangfire的后台任务封装类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Scheduling.Hangfire.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Scheduling.Hangfire.xml + + + + + True + False + + + + + + + + + + + + + diff --git a/src/Util.Scheduling.Hangfire/AppBuilderExtensions.cs b/src/Util.Scheduling.Hangfire/AppBuilderExtensions.cs new file mode 100644 index 000000000..57265644a --- /dev/null +++ b/src/Util.Scheduling.Hangfire/AppBuilderExtensions.cs @@ -0,0 +1,86 @@ +using Util.Configs; + +namespace Util.Scheduling; + +/// +/// Hangfire后台任务操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置Hangfire后台任务操作 + /// + /// 应用生成器 + /// 配置操作 + public static IAppBuilder AddHangfire( this IAppBuilder builder, Action setupAction ) { + return builder.AddHangfire( setupAction, true ); + } + + /// + /// 配置Hangfire后台任务操作 + /// + /// 应用生成器 + /// 配置操作 + /// 服务器配置操作 + public static IAppBuilder AddHangfire( this IAppBuilder builder, Action setupAction, Action serverSetupAction ) { + return builder.AddHangfire( setupAction, serverSetupAction, true ); + } + + /// + /// 配置Hangfire后台任务操作 + /// + /// 应用生成器 + /// 配置操作 + /// 是否扫描加载后台任务 + public static IAppBuilder AddHangfire( this IAppBuilder builder, Action setupAction, bool isScanJobs ) { + return builder.AddHangfire( setupAction, null, isScanJobs ); + } + + /// + /// 配置Hangfire后台任务操作 + /// + /// 应用生成器 + /// 配置操作 + /// 服务器配置操作 + /// 是否扫描加载后台任务 + public static IAppBuilder AddHangfire( this IAppBuilder builder, Action setupAction, Action serverSetupAction, bool isScanJobs ) { + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + AddHangfire( services, setupAction, serverSetupAction ); + AddSchedulerBuilder( services ); + AddSchedulerOptions( services, isScanJobs ); + AddHostedService( services ); + } ); + return builder; + } + + /// + /// 添加Hangfire服务 + /// + private static void AddHangfire( IServiceCollection services, Action setupAction, Action serverSetupAction ) { + services.AddHangfire( setupAction ); + if ( serverSetupAction != null ) + services.Configure( serverSetupAction ); + } + + /// + /// 添加后台任务调度器服务 + /// + private static void AddSchedulerBuilder( IServiceCollection services ) { + services.TryAddSingleton(); + } + + /// + /// 添加后台任务调度器配置 + /// + private static void AddSchedulerOptions( IServiceCollection services, bool isScanJobs ) { + void Action( SchedulerOptions t ) => t.IsScanJobs = isScanJobs; + services.Configure( (Action)Action ); + } + + /// + /// 添加启动服务 + /// + private static void AddHostedService( IServiceCollection services ) { + services.AddHostedService(); + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Hangfire/HangfireExecutionContext.cs b/src/Util.Scheduling.Hangfire/HangfireExecutionContext.cs new file mode 100644 index 000000000..ba2925749 --- /dev/null +++ b/src/Util.Scheduling.Hangfire/HangfireExecutionContext.cs @@ -0,0 +1,43 @@ +namespace Util.Scheduling; + +/// +/// Hangfire执行上下文 +/// +public class HangfireExecutionContext { + /// + /// 参数 + /// + private readonly object _data; + + /// + /// 初始化Hangfire执行上下文 + /// + /// 服务提供器 + /// 服务提供器 + public HangfireExecutionContext( IServiceProvider serviceProvider,object data ) { + ServiceProvider = serviceProvider ?? throw new ArgumentNullException( nameof( serviceProvider ) ); + _data = data; + } + + /// + /// 服务提供器 + /// + public IServiceProvider ServiceProvider { get; } + + /// + /// 获取服务 + /// + /// 服务类型 + public T GetService() { + return ServiceProvider.GetService(); + } + + /// + /// 获取参数 + /// + /// 参数类型 + public T GetData() { + var result = Util.Helpers.Convert.To( _data ); + return result ?? Util.Helpers.Json.ToObject( _data.SafeString() ); + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Hangfire/HangfireJobInfo.cs b/src/Util.Scheduling.Hangfire/HangfireJobInfo.cs new file mode 100644 index 000000000..6935300ea --- /dev/null +++ b/src/Util.Scheduling.Hangfire/HangfireJobInfo.cs @@ -0,0 +1,15 @@ +namespace Util.Scheduling; + +/// +/// Hangfire任务信息 +/// +public class HangfireJobInfo : IJobInfo { + /// + /// 任务标识 + /// + public string Id { get; set; } + /// + /// 队列名称 + /// + public string Queue { get; set; } = "default"; +} \ No newline at end of file diff --git a/src/Util.Scheduling.Hangfire/HangfireScheduler.cs b/src/Util.Scheduling.Hangfire/HangfireScheduler.cs new file mode 100644 index 000000000..db2e6c401 --- /dev/null +++ b/src/Util.Scheduling.Hangfire/HangfireScheduler.cs @@ -0,0 +1,35 @@ +namespace Util.Scheduling; + +/// +/// Hangfire调度器 +/// +public class HangfireScheduler : IScheduler { + /// + /// Hangfire后台任务服务器 + /// + private BackgroundJobServer _jobServer; + /// + /// 服务器配置 + /// + private readonly BackgroundJobServerOptions _options; + + /// + /// 初始化Hangfire调度器 + /// + /// 服务器配置 + public HangfireScheduler( BackgroundJobServerOptions options ) { + _options = options; + } + + /// + public virtual Task StartAsync( CancellationToken cancellationToken = default ) { + _jobServer = new BackgroundJobServer( _options ); + return Task.CompletedTask; + } + + /// + public virtual Task StopAsync( CancellationToken cancellationToken = default ) { + _jobServer.Dispose(); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Hangfire/HangfireSchedulerManager.cs b/src/Util.Scheduling.Hangfire/HangfireSchedulerManager.cs new file mode 100644 index 000000000..681ed52a4 --- /dev/null +++ b/src/Util.Scheduling.Hangfire/HangfireSchedulerManager.cs @@ -0,0 +1,57 @@ +using Microsoft.Extensions.Options; +using Util.Reflections; + +namespace Util.Scheduling; + +/// +/// Hangfire调度管理器 +/// +public class HangfireSchedulerManager : SchedulerManagerBase { + /// + /// 服务器配置 + /// + private readonly BackgroundJobServerOptions _serverOptions; + + /// + /// 初始化Hangfire调度管理器 + /// + /// 服务范围工厂 + /// 类型查找器 + /// 服务器配置 + public HangfireSchedulerManager( IServiceScopeFactory scopeFactory, ITypeFinder typeFinder, + IOptions serverOptions ) : base( scopeFactory, typeFinder ) { + _serverOptions = serverOptions.Value; + } + + /// + public override Task AddJobAsync( IJob job ) { + if ( job == null ) + return null; + job.Config(); + var result = ScheduleJob( job as JobBase ); + return Task.FromResult( result ); + } + + /// + /// 调度任务 + /// + private string ScheduleJob( JobBase job ) { + if ( job == null ) + return null; + var jobInfo = job.GetJobInfo(); + var trigger = job.GetTrigger(); + if ( trigger.GetCron().IsEmpty() == false ) { + var id = jobInfo.GetId(); + if ( id.IsEmpty() ) + id = job.GetType().FullName; + RecurringJob.AddOrUpdate( id, jobInfo.GetQueue(), () => job.Execute( job.Data ), trigger.GetCron() ); + return id; + } + return trigger.GetDelay() == null ? BackgroundJob.Enqueue( () => job.Execute( job.Data ) ) : BackgroundJob.Schedule( () => job.Execute( job.Data ), trigger.GetDelay().SafeValue() ); + } + + /// + public override Task GetSchedulerAsync() { + return Task.FromResult( (IScheduler)new HangfireScheduler(_serverOptions) ); + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Hangfire/HangfireTrigger.cs b/src/Util.Scheduling.Hangfire/HangfireTrigger.cs new file mode 100644 index 000000000..22f63a0c4 --- /dev/null +++ b/src/Util.Scheduling.Hangfire/HangfireTrigger.cs @@ -0,0 +1,15 @@ +namespace Util.Scheduling; + +/// +/// Hangfire触发器 +/// +public class HangfireTrigger : IJobTrigger { + /// + /// 延迟执行时间间隔 + /// + public TimeSpan? Delay { get; set; } + /// + /// Cron表达式 + /// + public string Cron { get; set; } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Hangfire/IJobInfoExtensions.cs b/src/Util.Scheduling.Hangfire/IJobInfoExtensions.cs new file mode 100644 index 000000000..628abe247 --- /dev/null +++ b/src/Util.Scheduling.Hangfire/IJobInfoExtensions.cs @@ -0,0 +1,48 @@ +namespace Util.Scheduling; + +/// +/// Hangfire任务信息扩展 +/// +public static class IJobInfoExtensions { + /// + /// 设置任务标识 + /// + /// 任务信息 + /// 任务标识 + public static IJobInfo Id( this IJobInfo source,string id ) { + if ( source is HangfireJobInfo job ) + job.Id = id; + return source; + } + + /// + /// 获取任务标识 + /// + /// 任务信息 + public static string GetId( this IJobInfo source ) { + if ( source is HangfireJobInfo job ) + return job.Id; + return null; + } + + /// + /// 设置队列名称 + /// + /// 任务信息 + /// 队列名称 + public static IJobInfo Queue( this IJobInfo source, string queue ) { + if ( source is HangfireJobInfo job ) + job.Queue = queue; + return source; + } + + /// + /// 获取队列名称 + /// + /// 任务信息 + public static string GetQueue( this IJobInfo source ) { + if ( source is HangfireJobInfo job ) + return job.Queue; + return "default"; + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Hangfire/IJobTriggerExtensions.cs b/src/Util.Scheduling.Hangfire/IJobTriggerExtensions.cs new file mode 100644 index 000000000..a4c80b8df --- /dev/null +++ b/src/Util.Scheduling.Hangfire/IJobTriggerExtensions.cs @@ -0,0 +1,249 @@ +namespace Util.Scheduling; + +/// +/// Quartz任务触发器扩展 +/// +public static class IJobTriggerExtensions { + /// + /// 设置延迟执行时间间隔 + /// + /// 任务触发器 + /// 延迟执行时间间隔 + public static IJobTrigger Delay( this IJobTrigger source, TimeSpan value ) { + source.CheckNull( nameof( source ) ); + if ( source is HangfireTrigger trigger ) + trigger.Delay = value; + return source; + } + + /// + /// 设置Cron表达式 + /// + /// 任务触发器 + /// Cron表达式 + public static IJobTrigger Cron( this IJobTrigger source, string cron ) { + source.CheckNull( nameof( source ) ); + if ( source is HangfireTrigger trigger ) { + trigger.Cron = cron; + } + return source; + } + + /// + /// 获取延迟执行时间间隔 + /// + /// 任务触发器 + public static TimeSpan? GetDelay( this IJobTrigger source ) { + source.CheckNull( nameof( source ) ); + if ( source is HangfireTrigger trigger ) + return trigger.Delay; + return null; + } + + /// + /// 获取Cron表达式 + /// + /// 任务触发器 + public static string GetCron( this IJobTrigger source ) { + source.CheckNull( nameof( source ) ); + if ( source is HangfireTrigger trigger ) + return trigger.Cron; + return null; + } + + /// + /// 设置每分钟重复执行 + /// + /// 任务触发器 + public static IJobTrigger Minutely( this IJobTrigger source ) { + source.CheckNull( nameof( source ) ); + if ( source is HangfireTrigger trigger ) + trigger.Cron = Hangfire.Cron.Minutely(); + return source; + } + + /// + /// 设置每小时重复执行 + /// + /// 任务触发器 + public static IJobTrigger Hourly( this IJobTrigger source ) { + return source.Hourly( 0 ); + } + + /// + /// 设置每小时重复执行 + /// + /// 任务触发器 + /// 在几分执行 + public static IJobTrigger Hourly( this IJobTrigger source, int minute ) { + source.CheckNull( nameof( source ) ); + if ( source is HangfireTrigger trigger ) + trigger.Cron = Hangfire.Cron.Hourly( minute ); + return source; + } + + /// + /// 设置每天重复执行 + /// + /// 任务触发器 + public static IJobTrigger Daily( this IJobTrigger source ) { + return source.Daily( 0 ); + } + + /// + /// 设置每天重复执行 + /// + /// 任务触发器 + /// 在几点执行 + public static IJobTrigger Daily( this IJobTrigger source, int hour ) { + return source.Daily( hour, 0 ); + } + + /// + /// 设置每天重复执行 + /// + /// 任务触发器 + /// 在几点执行 + /// 在几分执行 + public static IJobTrigger Daily( this IJobTrigger source, int hour, int minute ) { + source.CheckNull( nameof( source ) ); + if ( source is HangfireTrigger trigger ) + trigger.Cron = Hangfire.Cron.Daily( hour, minute ); + return source; + } + + /// + /// 设置每周重复执行 + /// + /// 任务触发器 + public static IJobTrigger Weekly( this IJobTrigger source ) { + return source.Weekly( DayOfWeek.Monday ); + } + + /// + /// 设置每周重复执行 + /// + /// 任务触发器 + /// 在星期几执行 + public static IJobTrigger Weekly( this IJobTrigger source, DayOfWeek dayOfWeek ) { + return source.Weekly( dayOfWeek, 0 ); + } + + /// + /// 设置每周重复执行 + /// + /// 任务触发器 + /// 在星期几执行 + /// 在几点执行 + public static IJobTrigger Weekly( this IJobTrigger source, DayOfWeek dayOfWeek, int hour ) { + return source.Weekly( dayOfWeek, hour, 0 ); + } + + /// + /// 设置每周重复执行 + /// + /// 任务触发器 + /// 在星期几执行 + /// 在几点执行 + /// 在几分执行 + public static IJobTrigger Weekly( this IJobTrigger source, DayOfWeek dayOfWeek, int hour, int minute ) { + source.CheckNull( nameof( source ) ); + if ( source is HangfireTrigger trigger ) + trigger.Cron = Hangfire.Cron.Weekly( dayOfWeek, hour, minute ); + return source; + } + + /// + /// 设置每月重复执行 + /// + /// 任务触发器 + public static IJobTrigger Monthly( this IJobTrigger source ) { + return source.Monthly( 1 ); + } + + /// + /// 设置每月重复执行 + /// + /// 任务触发器 + /// 在第几天执行 + public static IJobTrigger Monthly( this IJobTrigger source, int day ) { + return source.Monthly( day, 0 ); + } + + /// + /// 设置每月重复执行 + /// + /// 任务触发器 + /// 在第几天执行 + /// 在几点执行 + public static IJobTrigger Monthly( this IJobTrigger source, int day, int hour ) { + return source.Monthly( day, hour, 0 ); + } + + /// + /// 设置每月重复执行 + /// + /// 任务触发器 + /// 在第几天执行 + /// 在几点执行 + /// 在几分执行 + public static IJobTrigger Monthly( this IJobTrigger source, int day, int hour, int minute ) { + source.CheckNull( nameof( source ) ); + if ( source is HangfireTrigger trigger ) + trigger.Cron = Hangfire.Cron.Monthly( day, hour, minute ); + return source; + } + + /// + /// 设置每年重复执行 + /// + /// 任务触发器 + public static IJobTrigger Yearly( this IJobTrigger source ) { + return source.Yearly( 1 ); + } + + /// + /// 设置每年重复执行 + /// + /// 任务触发器 + /// 在几月执行 + public static IJobTrigger Yearly( this IJobTrigger source, int month ) { + return source.Yearly( month,1 ); + } + + /// + /// 设置每年重复执行 + /// + /// 任务触发器 + /// 在几月执行 + /// 在第几天执行 + public static IJobTrigger Yearly( this IJobTrigger source, int month, int day ) { + return source.Yearly( month, day,0 ); + } + + /// + /// 设置每年重复执行 + /// + /// 任务触发器 + /// 在几月执行 + /// 在第几天执行 + /// 在几点执行 + public static IJobTrigger Yearly( this IJobTrigger source, int month, int day, int hour ) { + return source.Yearly( month, day, hour, 0 ); + } + + /// + /// 设置每年重复执行 + /// + /// 任务触发器 + /// 在几月执行 + /// 在第几天执行 + /// 在几点执行 + /// 在几分执行 + public static IJobTrigger Yearly( this IJobTrigger source, int month, int day, int hour, int minute ) { + source.CheckNull( nameof( source ) ); + if ( source is HangfireTrigger trigger ) + trigger.Cron = Hangfire.Cron.Yearly( month, day, hour, minute ); + return source; + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Hangfire/ISchedulerManagerExtensions.cs b/src/Util.Scheduling.Hangfire/ISchedulerManagerExtensions.cs new file mode 100644 index 000000000..08cbcd3bf --- /dev/null +++ b/src/Util.Scheduling.Hangfire/ISchedulerManagerExtensions.cs @@ -0,0 +1,286 @@ +namespace Util.Scheduling; + +/// +/// Hangfire后台任务调度管理器扩展 +/// +public static class ISchedulerManagerExtensions { + /// + /// 执行入队操作 + /// + /// 后台任务调度管理器 + /// 后台操作 + public static string Enqueue( this ISchedulerManager source, Expression actionExpression ) { + return BackgroundJob.Enqueue( actionExpression ); + } + + /// + /// 执行入队操作 + /// + /// 后台任务调度管理器 + /// 后台操作 + public static string Enqueue( this ISchedulerManager source, Expression> actionExpression ) { + return BackgroundJob.Enqueue( actionExpression ); + } + + /// + /// 执行入队操作 + /// + /// 后台任务调度管理器 + /// 后台操作 + public static string Enqueue( this ISchedulerManager source, Expression> actionExpression ) { + return BackgroundJob.Enqueue( actionExpression ); + } + + /// + /// 执行入队操作 + /// + /// 后台任务调度管理器 + /// 后台操作 + public static string Enqueue( this ISchedulerManager source, Expression> actionExpression ) { + return BackgroundJob.Enqueue( actionExpression ); + } + + /// + /// 执行延迟操作 + /// + /// 后台任务调度管理器 + /// 后台操作 + /// 延迟执行时间间隔 + public static string Schedule( this ISchedulerManager source, Expression actionExpression, TimeSpan delay ) { + return BackgroundJob.Schedule( actionExpression, delay ); + } + + /// + /// 执行延迟操作 + /// + /// 后台任务调度管理器 + /// 后台操作 + /// 延迟执行时间间隔 + public static string Schedule( this ISchedulerManager source, Expression> actionExpression, TimeSpan delay ) { + return BackgroundJob.Schedule( actionExpression, delay ); + } + + /// + /// 执行延迟操作 + /// + /// 后台任务调度管理器 + /// 后台操作 + /// 延迟执行时间 + public static string Schedule( this ISchedulerManager source, Expression actionExpression, DateTimeOffset dateTime ) { + return BackgroundJob.Schedule( actionExpression, dateTime ); + } + + /// + /// 执行延迟操作 + /// + /// 后台任务调度管理器 + /// 后台操作 + /// 延迟执行时间 + public static string Schedule( this ISchedulerManager source, Expression> actionExpression, DateTimeOffset dateTime ) { + return BackgroundJob.Schedule( actionExpression, dateTime ); + } + + /// + /// 执行延迟操作 + /// + /// 后台任务调度管理器 + /// 后台操作 + /// 延迟执行时间间隔 + public static string Schedule( this ISchedulerManager source, Expression> actionExpression, TimeSpan delay ) { + return BackgroundJob.Schedule( actionExpression, delay ); + } + + /// + /// 执行延迟操作 + /// + /// 后台任务调度管理器 + /// 后台操作 + /// 延迟执行时间 + public static string Schedule( this ISchedulerManager source, Expression> actionExpression, DateTimeOffset dateTime ) { + return BackgroundJob.Schedule( actionExpression, dateTime ); + } + + /// + /// 执行延迟操作 + /// + /// 后台任务调度管理器 + /// 后台操作 + /// 延迟执行时间间隔 + public static string Schedule( this ISchedulerManager source, Expression> actionExpression, TimeSpan delay ) { + return BackgroundJob.Schedule( actionExpression, delay ); + } + + /// + /// 执行延迟操作 + /// + /// 后台任务调度管理器 + /// 后台操作 + /// 延迟执行时间 + public static string Schedule( this ISchedulerManager source, Expression> actionExpression, DateTimeOffset dateTime ) { + return BackgroundJob.Schedule( actionExpression, dateTime ); + } + + /// + /// 执行延续操作 + /// + /// 后台任务调度管理器 + /// 父操作标识 + /// 后台操作 + public static string Continue( this ISchedulerManager source, string parentId, Expression actionExpression ) { + return BackgroundJob.ContinueJobWith( parentId,actionExpression ); + } + + /// + /// 执行延续操作 + /// + /// 后台任务调度管理器 + /// 父操作标识 + /// 后台操作 + /// 配置 + public static string Continue( this ISchedulerManager source, string parentId, Expression actionExpression, JobContinuationOptions options ) { + return BackgroundJob.ContinueJobWith( parentId, actionExpression, options ); + } + + /// + /// 执行延续操作 + /// + /// 后台任务调度管理器 + /// 父操作标识 + /// 后台操作 + public static string Continue( this ISchedulerManager source, string parentId, Expression> actionExpression ) { + return BackgroundJob.ContinueJobWith( parentId, actionExpression ); + } + + /// + /// 执行延续操作 + /// + /// 后台任务调度管理器 + /// 父操作标识 + /// 后台操作 + /// 配置 + public static string Continue( this ISchedulerManager source, string parentId, Expression> actionExpression, JobContinuationOptions options ) { + return BackgroundJob.ContinueJobWith( parentId, actionExpression, options ); + } + + /// + /// 执行延续操作 + /// + /// 后台任务调度管理器 + /// 父操作标识 + /// 后台操作 + public static string Continue( this ISchedulerManager source, string parentId, Expression> actionExpression ) { + return BackgroundJob.ContinueJobWith( parentId, actionExpression ); + } + + /// + /// 执行延续操作 + /// + /// 后台任务调度管理器 + /// 父操作标识 + /// 后台操作 + /// 配置 + public static string Continue( this ISchedulerManager source, string parentId, Expression> actionExpression, JobContinuationOptions options ) { + return BackgroundJob.ContinueJobWith( parentId, actionExpression, options ); + } + + /// + /// 执行延续操作 + /// + /// 后台任务调度管理器 + /// 父操作标识 + /// 后台操作 + public static string Continue( this ISchedulerManager source, string parentId, Expression> actionExpression ) { + return BackgroundJob.ContinueJobWith( parentId, actionExpression ); + } + + /// + /// 执行延续操作 + /// + /// 后台任务调度管理器 + /// 父操作标识 + /// 后台操作 + /// 配置 + public static string Continue( this ISchedulerManager source, string parentId, Expression> actionExpression, JobContinuationOptions options ) { + return BackgroundJob.ContinueJobWith( parentId, actionExpression, options ); + } + + /// + /// 执行重复操作 + /// + /// 后台任务调度管理器 + /// 任务标识 + /// 后台操作 + /// Cron表达式 + /// 队列名称 + /// 任务配置 + public static void Repeat( this ISchedulerManager source, string id, Expression actionExpression, string cron, string queue = "default", RecurringJobOptions options = null ) { + if ( options == null ) { + RecurringJob.AddOrUpdate( id, queue, actionExpression, cron ); + return; + } + RecurringJob.AddOrUpdate( id, queue, actionExpression, cron, options ); + } + + /// + /// 执行重复操作 + /// + /// 后台任务调度管理器 + /// 任务标识 + /// 后台操作 + /// Cron表达式 + /// 队列名称 + /// 任务配置 + public static void Repeat( this ISchedulerManager source, string id, Expression> actionExpression, string cron, string queue = "default", RecurringJobOptions options = null ) { + if ( options == null ) { + RecurringJob.AddOrUpdate( id, queue, actionExpression, cron ); + return; + } + RecurringJob.AddOrUpdate( id, queue, actionExpression, cron, options ); + } + + /// + /// 执行重复操作 + /// + /// 后台任务调度管理器 + /// 任务标识 + /// 后台操作 + /// Cron表达式 + /// 队列名称 + /// 任务配置 + public static void Repeat( this ISchedulerManager source, string id, Expression> actionExpression, string cron, string queue = "default", RecurringJobOptions options = null ) { + if ( options == null ) { + RecurringJob.AddOrUpdate( id, queue, actionExpression, cron ); + return; + } + RecurringJob.AddOrUpdate( id, queue, actionExpression, cron, options ); + } + + /// + /// 执行重复操作 + /// + /// 后台任务调度管理器 + /// 任务标识 + /// 后台操作 + /// Cron表达式 + /// 队列名称 + /// 任务配置 + public static void Repeat( this ISchedulerManager source, string id, Expression> actionExpression, string cron, string queue = "default", RecurringJobOptions options = null ) { + if ( options == null ) { + RecurringJob.AddOrUpdate( id, queue, actionExpression, cron ); + return; + } + RecurringJob.AddOrUpdate( id, queue, actionExpression, cron, options ); + } + + /// + /// 删除任务 + /// + /// 后台任务调度管理器 + /// 任务标识 + public static void Remove( this ISchedulerManager source, string id ) { + var result = BackgroundJob.Delete( id ); + if ( result ) + return; + RecurringJob.RemoveIfExists( id ); + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Hangfire/JobBase.cs b/src/Util.Scheduling.Hangfire/JobBase.cs new file mode 100644 index 000000000..e12d9946a --- /dev/null +++ b/src/Util.Scheduling.Hangfire/JobBase.cs @@ -0,0 +1,86 @@ +using Util.Helpers; + +namespace Util.Scheduling; + +/// +/// 任务 +/// +public abstract class JobBase : IJob { + /// + /// 任务信息 + /// + private IJobInfo _jobInfo; + /// + /// 触发器 + /// + private IJobTrigger _trigger; + /// + /// 任务标识 + /// + public string Id { get; set; } + /// + /// 参数 + /// + public object Data { get; set; } + + /// + /// 配置 + /// + public void Config() { + _jobInfo = new HangfireJobInfo(); + _trigger = new HangfireTrigger(); + ConfigId( _jobInfo ); + ConfigDetail( _jobInfo ); + ConfigTrigger( _trigger ); + } + + /// + /// 配置任务标识 + /// + protected virtual void ConfigId( IJobInfo job ) { + if ( Id.IsEmpty() ) + return; + job.Id( Id ); + } + + /// + /// 配置任务信息 + /// + protected virtual void ConfigDetail( IJobInfo job ) { + } + + /// + /// 配置触发器 + /// + protected virtual void ConfigTrigger( IJobTrigger trigger ) { + } + + /// + /// 执行任务 + /// + /// 参数 + public virtual async Task Execute( object data ) { + using var scope = Ioc.ServiceScopeFactory.CreateScope(); + await Execute( new HangfireExecutionContext( scope.ServiceProvider, data ) ); + } + + /// + /// 执行任务 + /// + /// 执行上下文 + protected abstract Task Execute( HangfireExecutionContext context ); + + /// + /// 获取任务信息 + /// + public IJobInfo GetJobInfo() { + return _jobInfo; + } + + /// + /// 获取触发器 + /// + public IJobTrigger GetTrigger() { + return _trigger; + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Hangfire/Usings.cs b/src/Util.Scheduling.Hangfire/Usings.cs new file mode 100644 index 000000000..06d6dcc0a --- /dev/null +++ b/src/Util.Scheduling.Hangfire/Usings.cs @@ -0,0 +1,7 @@ +global using System; +global using System.Threading.Tasks; +global using System.Threading; +global using System.Linq.Expressions; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Hangfire; diff --git a/src/Util.Scheduling.Quartz/02-Util.Scheduling.Quartz.csproj b/src/Util.Scheduling.Quartz/02-Util.Scheduling.Quartz.csproj new file mode 100644 index 000000000..664c54422 --- /dev/null +++ b/src/Util.Scheduling.Quartz/02-Util.Scheduling.Quartz.csproj @@ -0,0 +1,38 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Scheduling.Quartz + Util.Scheduling + Util.Scheduling.Quartz是Util应用框架基于Quartz的后台任务封装类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Scheduling.Quartz.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Scheduling.Quartz.xml + + + + + True + False + + + + + + + + + + + + + + diff --git a/src/Util.Scheduling.Quartz/AppBuilderExtensions.cs b/src/Util.Scheduling.Quartz/AppBuilderExtensions.cs new file mode 100644 index 000000000..fbaffcfe6 --- /dev/null +++ b/src/Util.Scheduling.Quartz/AppBuilderExtensions.cs @@ -0,0 +1,81 @@ +using Util.Configs; + +namespace Util.Scheduling; + +/// +/// Quartz后台任务操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置Quartz后台任务操作 + /// + /// 应用生成器 + /// 是否扫描加载后台任务 + public static IAppBuilder AddQuartz( this IAppBuilder builder, bool isScanJobs = true ) { + return builder.AddQuartz( null, isScanJobs ); + } + + /// + /// 配置Quartz后台任务操作 + /// + /// 应用生成器 + /// 配置操作 + public static IAppBuilder AddQuartz( this IAppBuilder builder, Action setupAction ) { + return builder.AddQuartz( setupAction, true ); + } + + /// + /// 配置Quartz后台任务操作 + /// + /// 应用生成器 + /// 配置操作 + /// 是否扫描加载后台任务 + public static IAppBuilder AddQuartz( this IAppBuilder builder, Action setupAction, bool isScanJobs ) { + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + AddQuartz( services, setupAction ); + AddSchedulerBuilder( services ); + AddSchedulerOptions( services, isScanJobs ); + AddHostedService( services ); + } ); + return builder; + } + + /// + /// 添加Quartz服务 + /// + private static void AddQuartz( IServiceCollection services, Action setupAction ) { + if ( setupAction == null ) { + services.AddQuartz( options => { + options.UseMicrosoftDependencyInjectionJobFactory(); + } ); + return; + } + services.AddQuartz( options => { + options.UseMicrosoftDependencyInjectionJobFactory(); + setupAction( options ); + } ); + } + + /// + /// 添加后台任务调度器服务 + /// + private static void AddSchedulerBuilder( IServiceCollection services ) { + services.TryAddSingleton(); + } + + /// + /// 添加后台任务调度器配置 + /// + private static void AddSchedulerOptions( IServiceCollection services, bool isScanJobs ) { + void Action( SchedulerOptions t ) => t.IsScanJobs = isScanJobs; + services.Configure( (Action)Action ); + } + + /// + /// 添加启动服务 + /// + private static void AddHostedService( IServiceCollection services ) { + services.AddHostedService(); + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Quartz/CronHelper.cs b/src/Util.Scheduling.Quartz/CronHelper.cs new file mode 100644 index 000000000..7ecba3a73 --- /dev/null +++ b/src/Util.Scheduling.Quartz/CronHelper.cs @@ -0,0 +1,90 @@ +namespace Util.Scheduling; + +/// +/// Cron表达式操作 +/// +public static class CronHelper { + /// + /// 每分钟重复执行 + /// + /// 在第几秒执行 + public static string Minutely( int second ) { + return $"{second} * * * * ? *"; + } + + /// + /// 每小时重复执行 + /// + /// 在第几分执行 + /// 在第几秒执行 + public static string Hourly( int minute, int second ) { + return $"{second} {minute} * * * ? *"; + } + + /// + /// 每天重复执行 + /// + /// 在几点执行 + /// 在第几分执行 + /// 在第几秒执行 + public static string Daily( int hour, int minute, int second ) { + return $"{second} {minute} {hour} * * ? *"; + } + + /// + /// 每周重复执行 + /// + /// 在星期几执行 + /// 在几点执行 + /// 在第几分执行 + /// 在第几秒执行 + public static string Weekly( DayOfWeek dayOfWeek, int hour, int minute, int second ) { + return $"{second} {minute} {hour} ? * {GetDayOfWeek(dayOfWeek)} *"; + } + + /// + /// 获取星期缩写 + /// + private static string GetDayOfWeek( DayOfWeek dayOfWeek ) { + switch ( dayOfWeek ) { + case DayOfWeek.Monday: + return "MON"; + case DayOfWeek.Tuesday: + return "TUE"; + case DayOfWeek.Wednesday: + return "WED"; + case DayOfWeek.Thursday: + return "THUR"; + case DayOfWeek.Friday: + return "FRI"; + case DayOfWeek.Saturday: + return "SAT"; + case DayOfWeek.Sunday: + return "SUN"; + } + return null; + } + + /// + /// 每月重复执行 + /// + /// 在第几天执行 + /// 在几点执行 + /// 在第几分执行 + /// 在第几秒执行 + public static string Monthly( int day, int hour, int minute, int second ) { + return $"{second} {minute} {hour} {day} * ? *"; + } + + /// + /// 每年重复执行 + /// + /// 在几月执行 + /// 在第几天执行 + /// 在几点执行 + /// 在第几分执行 + /// 在第几秒执行 + public static string Yearly( int month, int day, int hour, int minute, int second ) { + return $"{second} {minute} {hour} {day} {month} ? *"; + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Quartz/IJobExtensions.cs b/src/Util.Scheduling.Quartz/IJobExtensions.cs new file mode 100644 index 000000000..78cb88044 --- /dev/null +++ b/src/Util.Scheduling.Quartz/IJobExtensions.cs @@ -0,0 +1,26 @@ +namespace Util.Scheduling; + +/// +/// Quartz任务扩展 +/// +public static class IJobExtensions { + /// + /// 获取Quartz任务信息 + /// + /// 任务 + public static IJobDetail GetDetail( this IJob source ) { + if ( source is JobBase job ) + return job.GetJobDetail(); + return null; + } + + /// + /// 获取Quartz任务触发器 + /// + /// 任务 + public static ITrigger GetTrigger( this IJob source ) { + if ( source is JobBase job ) + return job.GetTrigger(); + return null; + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Quartz/IJobInfoExtensions.cs b/src/Util.Scheduling.Quartz/IJobInfoExtensions.cs new file mode 100644 index 000000000..bbfe6068a --- /dev/null +++ b/src/Util.Scheduling.Quartz/IJobInfoExtensions.cs @@ -0,0 +1,48 @@ +namespace Util.Scheduling; + +/// +/// Quartz任务信息扩展 +/// +public static class IJobInfoExtensions { + /// + /// 设置任务名称 + /// + /// 任务信息 + /// 任务名称 + public static IJobInfo Name( this IJobInfo source,string name ) { + if ( source is QuartzJobInfo job ) + job.Name = name; + return source; + } + + /// + /// 设置任务组名称 + /// + /// 任务信息 + /// 任务组名称 + public static IJobInfo Group( this IJobInfo source, string name ) { + if ( source is QuartzJobInfo job ) + job.Group = name; + return source; + } + + /// + /// 获取任务名称 + /// + /// 任务信息 + public static string GetName( this IJobInfo source ) { + if ( source is QuartzJobInfo job ) + return job.Name; + return null; + } + + /// + /// 获取任务组名称 + /// + /// 任务信息 + public static string GetGroup( this IJobInfo source ) { + if ( source is QuartzJobInfo job ) + return job.Group; + return null; + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Quartz/IJobTriggerExtensions.cs b/src/Util.Scheduling.Quartz/IJobTriggerExtensions.cs new file mode 100644 index 000000000..25bf01bcc --- /dev/null +++ b/src/Util.Scheduling.Quartz/IJobTriggerExtensions.cs @@ -0,0 +1,394 @@ +namespace Util.Scheduling; + +/// +/// Quartz任务触发器扩展 +/// +public static class IJobTriggerExtensions { + /// + /// 设置触发器名称 + /// + /// 任务触发器 + /// 触发器名称 + public static IJobTrigger Name( this IJobTrigger source, string name ) { + if ( source is QuartzTrigger trigger ) + trigger.Name = name; + return source; + } + + /// + /// 设置触发器组名称 + /// + /// 任务触发器 + /// 触发器组名称 + public static IJobTrigger Group( this IJobTrigger source, string name ) { + if ( source is QuartzTrigger trigger ) + trigger.Group = name; + return source; + } + + /// + /// 设置重复执行次数 + /// + /// 任务触发器 + /// 重复执行次数,设置为null,表示持续重复执行 + public static IJobTrigger RepeatCount( this IJobTrigger source, int? value ) { + if ( source is QuartzTrigger trigger ) + trigger.RepeatCount = value; + return source; + } + + /// + /// 设置重复执行时间间隔 + /// + /// 任务触发器 + /// 重复执行时间间隔 + public static IJobTrigger Interval( this IJobTrigger source, TimeSpan value ) { + if ( source is QuartzTrigger trigger ) + trigger.Interval = value; + return source; + } + + /// + /// 设置重复执行时间间隔 + /// + /// 任务触发器 + /// 重复执行时间间隔,单位:小时 + public static IJobTrigger IntervalInHours( this IJobTrigger source, int value ) { + if ( source is QuartzTrigger trigger ) + trigger.IntervalInHours = value; + return source; + } + + /// + /// 设置重复执行时间间隔 + /// + /// 任务触发器 + /// 重复执行时间间隔,单位:分 + public static IJobTrigger IntervalInMinutes( this IJobTrigger source, int value ) { + if ( source is QuartzTrigger trigger ) + trigger.IntervalInMinutes = value; + return source; + } + + /// + /// 设置重复执行时间间隔 + /// + /// 任务触发器 + /// 重复执行时间间隔,单位:秒 + public static IJobTrigger IntervalInSeconds( this IJobTrigger source, int value ) { + if ( source is QuartzTrigger trigger ) + trigger.IntervalInSeconds = value; + return source; + } + + /// + /// 设置执行起始时间 + /// + /// 任务触发器 + /// 执行起始时间 + public static IJobTrigger StartTime( this IJobTrigger source, DateTimeOffset value ) { + if ( source is QuartzTrigger trigger ) + trigger.StartTime = value; + return source; + } + + /// + /// 设置执行结束时间 + /// + /// 任务触发器 + /// 执行结束时间 + public static IJobTrigger EndTime( this IJobTrigger source, DateTimeOffset value ) { + if ( source is QuartzTrigger trigger ) + trigger.EndTime = value; + return source; + } + + /// + /// 设置Cron表达式 + /// + /// 任务触发器 + /// Cron表达式 + public static IJobTrigger Cron( this IJobTrigger source, string cron ) { + if ( source is QuartzTrigger trigger ) { + trigger.Cron = cron; + } + return source; + } + + /// + /// 设置Cron表达式 + /// + /// 任务触发器 + /// Cron表达式 + /// Cron表达式配置操作 + public static IJobTrigger Cron( this IJobTrigger source, string cron, Action action ) { + if ( source is QuartzTrigger trigger ) { + trigger.Cron = cron; + trigger.CronScheduleAction = action; + } + return source; + } + + /// + /// 设置每分钟重复执行 + /// + /// 任务触发器 + public static IJobTrigger Minutely( this IJobTrigger source ) { + return source.Minutely( 0 ); + } + + /// + /// 设置每分钟重复执行 + /// + /// 任务触发器 + /// 在第几秒执行 + public static IJobTrigger Minutely( this IJobTrigger source, int second ) { + source.CheckNull( nameof( source ) ); + if ( source is QuartzTrigger trigger ) + trigger.Cron = CronHelper.Minutely( second ); + return source; + } + + /// + /// 设置每小时重复执行 + /// + /// 任务触发器 + public static IJobTrigger Hourly( this IJobTrigger source ) { + return source.Hourly( 0 ); + } + + /// + /// 设置每小时重复执行 + /// + /// 任务触发器 + /// 在第几分执行 + public static IJobTrigger Hourly( this IJobTrigger source, int minute ) { + return source.Hourly( minute, 0 ); + } + + /// + /// 设置每小时重复执行 + /// + /// 任务触发器 + /// 在第几分执行 + /// 在第几秒执行 + public static IJobTrigger Hourly( this IJobTrigger source, int minute, int second ) { + source.CheckNull( nameof( source ) ); + if ( source is QuartzTrigger trigger ) + trigger.Cron = CronHelper.Hourly( minute, second ); + return source; + } + + /// + /// 设置每天重复执行 + /// + /// 任务触发器 + public static IJobTrigger Daily( this IJobTrigger source ) { + return source.Daily( 0 ); + } + + /// + /// 设置每天重复执行 + /// + /// 任务触发器 + /// 在几点执行 + public static IJobTrigger Daily( this IJobTrigger source, int hour ) { + return source.Daily( hour, 0 ); + } + + /// + /// 设置每天重复执行 + /// + /// 任务触发器 + /// 在几点执行 + /// 在第几分执行 + public static IJobTrigger Daily( this IJobTrigger source, int hour, int minute ) { + return source.Daily( hour, minute, 0 ); + } + + /// + /// 设置每天重复执行 + /// + /// 任务触发器 + /// 在几点执行 + /// 在第几分执行 + /// 在第几秒执行 + public static IJobTrigger Daily( this IJobTrigger source, int hour, int minute, int second ) { + source.CheckNull( nameof( source ) ); + if ( source is QuartzTrigger trigger ) + trigger.Cron = CronHelper.Daily( hour, minute, second ); + return source; + } + + /// + /// 设置每周重复执行 + /// + /// 任务触发器 + public static IJobTrigger Weekly( this IJobTrigger source ) { + return source.Weekly( DayOfWeek.Monday ); + } + + /// + /// 设置每周重复执行 + /// + /// 任务触发器 + /// 在星期几执行 + public static IJobTrigger Weekly( this IJobTrigger source, DayOfWeek dayOfWeek ) { + return source.Weekly( dayOfWeek, 0 ); + } + + /// + /// 设置每周重复执行 + /// + /// 任务触发器 + /// 在星期几执行 + /// 在几点执行 + public static IJobTrigger Weekly( this IJobTrigger source, DayOfWeek dayOfWeek, int hour ) { + return source.Weekly( dayOfWeek, hour, 0 ); + } + + /// + /// 设置每周重复执行 + /// + /// 任务触发器 + /// 在星期几执行 + /// 在几点执行 + /// 在第几分执行 + public static IJobTrigger Weekly( this IJobTrigger source, DayOfWeek dayOfWeek, int hour, int minute ) { + return source.Weekly( dayOfWeek, hour, minute, 0 ); + } + + /// + /// 设置每周重复执行 + /// + /// 任务触发器 + /// 在星期几执行 + /// 在几点执行 + /// 在第几分执行 + /// 在第几秒执行 + public static IJobTrigger Weekly( this IJobTrigger source, DayOfWeek dayOfWeek, int hour, int minute, int second ) { + source.CheckNull( nameof( source ) ); + if ( source is QuartzTrigger trigger ) + trigger.Cron = CronHelper.Weekly( dayOfWeek, hour, minute, second ); + return source; + } + + /// + /// 设置每月重复执行 + /// + /// 任务触发器 + public static IJobTrigger Monthly( this IJobTrigger source ) { + return source.Monthly( 1 ); + } + + /// + /// 设置每月重复执行 + /// + /// 任务触发器 + /// 在第几天执行 + public static IJobTrigger Monthly( this IJobTrigger source, int day ) { + return source.Monthly( day, 0 ); + } + + /// + /// 设置每月重复执行 + /// + /// 任务触发器 + /// 在第几天执行 + /// 在几点执行 + public static IJobTrigger Monthly( this IJobTrigger source, int day, int hour ) { + return source.Monthly( day, hour, 0 ); + } + + /// + /// 设置每月重复执行 + /// + /// 任务触发器 + /// 在第几天执行 + /// 在几点执行 + /// 在第几分执行 + public static IJobTrigger Monthly( this IJobTrigger source, int day, int hour, int minute ) { + return source.Monthly( day, hour, minute, 0 ); + } + + /// + /// 设置每月重复执行 + /// + /// 任务触发器 + /// 在第几天执行 + /// 在几点执行 + /// 在第几分执行 + /// 在第几秒执行 + public static IJobTrigger Monthly( this IJobTrigger source, int day, int hour, int minute, int second ) { + source.CheckNull( nameof( source ) ); + if ( source is QuartzTrigger trigger ) + trigger.Cron = CronHelper.Monthly( day, hour, minute, second ); + return source; + } + + /// + /// 设置每年重复执行 + /// + /// 任务触发器 + public static IJobTrigger Yearly( this IJobTrigger source ) { + return source.Yearly( 1 ); + } + + /// + /// 设置每年重复执行 + /// + /// 任务触发器 + /// 在几月执行 + public static IJobTrigger Yearly( this IJobTrigger source, int month ) { + return source.Yearly( month, 1 ); + } + + /// + /// 设置每年重复执行 + /// + /// 任务触发器 + /// 在几月执行 + /// 在第几天执行 + public static IJobTrigger Yearly( this IJobTrigger source, int month, int day ) { + return source.Yearly( month, day,0 ); + } + + /// + /// 设置每年重复执行 + /// + /// 任务触发器 + /// 在几月执行 + /// 在第几天执行 + /// 在几点执行 + public static IJobTrigger Yearly( this IJobTrigger source, int month, int day, int hour ) { + return source.Yearly( month, day, hour, 0 ); + } + + /// + /// 设置每年重复执行 + /// + /// 任务触发器 + /// 在几月执行 + /// 在第几天执行 + /// 在几点执行 + /// 在第几分执行 + public static IJobTrigger Yearly( this IJobTrigger source, int month, int day, int hour, int minute ) { + return source.Yearly( month, day, hour, minute, 0 ); + } + + /// + /// 设置每年重复执行 + /// + /// 任务触发器 + /// 在几月执行 + /// 在第几天执行 + /// 在几点执行 + /// 在第几分执行 + /// 在第几秒执行 + public static IJobTrigger Yearly( this IJobTrigger source, int month, int day, int hour, int minute, int second ) { + source.CheckNull( nameof( source ) ); + if ( source is QuartzTrigger trigger ) + trigger.Cron = CronHelper.Yearly( month, day, hour, minute, second ); + return source; + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Quartz/JobBase.cs b/src/Util.Scheduling.Quartz/JobBase.cs new file mode 100644 index 000000000..4454ad3ce --- /dev/null +++ b/src/Util.Scheduling.Quartz/JobBase.cs @@ -0,0 +1,99 @@ +using Util.Helpers; + +namespace Util.Scheduling; + +/// +/// 任务 +/// +public abstract class JobBase : IJob, Quartz.IJob { + /// + /// 参数键名 + /// + public const string DataKey = "Util_Job_Data"; + /// + /// 任务信息 + /// + private IJobDetail _jobDetail; + /// + /// 触发器 + /// + private ITrigger _trigger; + + /// + /// 参数 + /// + public object Data { get; set; } + + /// + /// 配置 + /// + public void Config() { + var jobInfo = new QuartzJobInfo(); + var trigger = new QuartzTrigger(); + ConfigDetail( jobInfo ); + ConfigTrigger( trigger ); + _jobDetail = CreateJobDetail( jobInfo ); + _trigger = trigger.ToTrigger(); + Config( _jobDetail, _trigger ); + } + + /// + /// 配置任务信息 + /// + protected virtual void ConfigDetail( IJobInfo job ) { + } + + /// + /// 配置触发器 + /// + protected virtual void ConfigTrigger( IJobTrigger trigger ) { + } + + /// + /// 创建任务信息 + /// + private IJobDetail CreateJobDetail( IJobInfo job ) { + var builder = JobBuilder.Create( GetType() ) + .WithIdentity( job.GetName(), job.GetGroup() ); + if ( Data != null ) + builder.UsingJobData( DataKey, Util.Helpers.Json.ToJson( Data ) ); + return builder.Build(); + } + + /// + /// 配置任务 + /// + /// 任务信息 + /// 任务触发器 + protected virtual void Config( IJobDetail jobDetail, ITrigger trigger ) { + } + + /// + /// 执行任务 + /// + /// 执行上下文 + public async Task Execute( IJobExecutionContext context ) { + using var scope = Ioc.ServiceScopeFactory.CreateScope(); + await Execute( new QuartzExecutionContext( scope.ServiceProvider, context ) ); + } + + /// + /// 执行任务 + /// + /// 执行上下文 + protected abstract Task Execute( QuartzExecutionContext context ); + + /// + /// 获取任务信息 + /// + public IJobDetail GetJobDetail() { + return _jobDetail; + } + + /// + /// 获取触发器 + /// + public ITrigger GetTrigger() { + return _trigger; + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Quartz/QuartzExecutionContext.cs b/src/Util.Scheduling.Quartz/QuartzExecutionContext.cs new file mode 100644 index 000000000..d9fb52eb2 --- /dev/null +++ b/src/Util.Scheduling.Quartz/QuartzExecutionContext.cs @@ -0,0 +1,43 @@ +namespace Util.Scheduling; + +/// +/// Quartz执行上下文 +/// +public class QuartzExecutionContext { + /// + /// 初始化Quartz执行上下文 + /// + /// 服务提供器 + /// 执行上下文 + public QuartzExecutionContext( IServiceProvider serviceProvider, IJobExecutionContext context ) { + ServiceProvider = serviceProvider ?? throw new ArgumentNullException( nameof( serviceProvider ) ); + Context = context ?? throw new ArgumentNullException( nameof( context ) ); + } + + /// + /// 服务提供器 + /// + public IServiceProvider ServiceProvider { get; } + + /// + /// 执行上下文 + /// + public IJobExecutionContext Context { get; } + + /// + /// 获取服务 + /// + /// 服务类型 + public T GetService() { + return ServiceProvider.GetService(); + } + + /// + /// 获取参数 + /// + /// 参数类型 + public T GetData() { + var json = Context.JobDetail.JobDataMap.GetString( JobBase.DataKey ); + return Util.Helpers.Json.ToObject( json ); + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Quartz/QuartzJobInfo.cs b/src/Util.Scheduling.Quartz/QuartzJobInfo.cs new file mode 100644 index 000000000..fc526c91d --- /dev/null +++ b/src/Util.Scheduling.Quartz/QuartzJobInfo.cs @@ -0,0 +1,15 @@ +namespace Util.Scheduling; + +/// +/// Quartz任务信息 +/// +public class QuartzJobInfo : IJobInfo { + /// + /// 任务名称 + /// + public string Name { get; set; } + /// + /// 任务组名称 + /// + public string Group { get; set; } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Quartz/QuartzScheduler.cs b/src/Util.Scheduling.Quartz/QuartzScheduler.cs new file mode 100644 index 000000000..411112200 --- /dev/null +++ b/src/Util.Scheduling.Quartz/QuartzScheduler.cs @@ -0,0 +1,47 @@ +namespace Util.Scheduling; + +/// +/// Quartz调度器 +/// +public class QuartzScheduler : IScheduler { + /// + /// Quartz调度器 + /// + private readonly Quartz.IScheduler _scheduler; + + /// + /// 初始化Quartz调度器 + /// + /// Quartz调度器 + public QuartzScheduler( Quartz.IScheduler scheduler ) { + _scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler)); + } + + /// + public async Task StartAsync( CancellationToken cancellationToken = default ) { + if ( _scheduler.IsStarted ) + return; + await _scheduler.Start( cancellationToken ); + } + + /// + /// 暂停 + /// + public async Task PauseAsync() { + await _scheduler.PauseAll(); + } + + /// + /// 恢复 + /// + public async Task ResumeAsync() { + await _scheduler.ResumeAll(); + } + + /// + public async Task StopAsync( CancellationToken cancellationToken = default ) { + if ( _scheduler.IsShutdown ) + return; + await _scheduler.Shutdown( true, cancellationToken ); + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Quartz/QuartzSchedulerManager.cs b/src/Util.Scheduling.Quartz/QuartzSchedulerManager.cs new file mode 100644 index 000000000..daa23bca3 --- /dev/null +++ b/src/Util.Scheduling.Quartz/QuartzSchedulerManager.cs @@ -0,0 +1,41 @@ +using Util.Reflections; + +namespace Util.Scheduling; + +/// +/// Quartz调度管理器 +/// +public class QuartzSchedulerManager : SchedulerManagerBase { + /// + /// Quartz调度器工厂 + /// + private readonly ISchedulerFactory _factory; + + /// + /// 初始化Quartz调度管理器 + /// + /// 服务范围工厂 + /// 类型查找器 + /// Quartz调度器工厂 + public QuartzSchedulerManager( IServiceScopeFactory scopeFactory, ITypeFinder typeFinder, ISchedulerFactory factory ) + : base( scopeFactory, typeFinder ) { + _factory = factory ?? throw new ArgumentNullException( nameof( factory ) ); + } + + /// + public override async Task AddJobAsync( IJob job ) { + if ( job == null ) + return null; + job.Config(); + var scheduler = await _factory.GetScheduler(); + var detail = job.GetDetail(); + await scheduler.ScheduleJob( detail, job.GetTrigger() ); + return null; + } + + /// + public override async Task GetSchedulerAsync() { + var scheduler = await _factory.GetScheduler(); + return new QuartzScheduler( scheduler ); + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Quartz/QuartzTrigger.cs b/src/Util.Scheduling.Quartz/QuartzTrigger.cs new file mode 100644 index 000000000..9dc4fe0cc --- /dev/null +++ b/src/Util.Scheduling.Quartz/QuartzTrigger.cs @@ -0,0 +1,134 @@ +namespace Util.Scheduling; + +/// +/// Quartz触发器 +/// +public class QuartzTrigger : IJobTrigger { + /// + /// 触发器名称 + /// + public string Name { get; set; } + /// + /// 触发器组名称 + /// + public string Group { get; set; } + /// + /// 重复执行次数,默认返回null,表示持续重复执行 + /// + public int? RepeatCount { get; set; } + /// + /// 重复执行时间间隔 + /// + public TimeSpan? Interval { get; set; } + /// + /// 重复执行时间间隔,单位:小时 + /// + public int? IntervalInHours { get; set; } + /// + /// 重复执行时间间隔,单位:分 + /// + public int? IntervalInMinutes { get; set; } + /// + /// 重复执行时间间隔,单位:秒 + /// + public int? IntervalInSeconds { get; set; } + /// + /// 执行起始时间 + /// + public DateTimeOffset? StartTime { get; set; } + /// + /// 执行结束时间 + /// + public DateTimeOffset? EndTime { get; set; } + /// + /// Cron表达式 + /// + public string Cron { get; set; } + /// + /// Cron表达式配置操作 + /// + public Action CronScheduleAction { get; set; } + + /// + /// 转换为Quartz触发器 + /// + public ITrigger ToTrigger() { + Init(); + var build = TriggerBuilder.Create() + .WithIdentity( Name, Group ) + .WithSimpleSchedule( simpleScheduleBuilder => { + SetRepeatCount( simpleScheduleBuilder ); + SetInterval( simpleScheduleBuilder ); + } ); + SetStartTime( build ); + SetEndTime( build ); + SetCron( build ); + return build.Build(); + } + + /// + /// 初始化 + /// + private void Init() { + if ( Interval == null && IntervalInHours == null && IntervalInMinutes == null && IntervalInSeconds == null && Cron.IsEmpty() ) + IntervalInMinutes = 1; + } + + /// + /// 设置重复执行次数 + /// + private void SetRepeatCount( SimpleScheduleBuilder builder ) { + if ( RepeatCount == null ) { + builder.RepeatForever(); + return; + } + builder.WithRepeatCount( RepeatCount.Value ); + } + + /// + /// 设置重复执行时间间隔 + /// + private void SetInterval( SimpleScheduleBuilder builder ) { + if ( Interval != null ) + builder.WithInterval( Interval.Value ); + if ( IntervalInHours != null ) + builder.WithIntervalInHours( IntervalInHours.Value ); + if ( IntervalInMinutes != null ) + builder.WithIntervalInMinutes( IntervalInMinutes.Value ); + if ( IntervalInSeconds != null ) + builder.WithIntervalInSeconds( IntervalInSeconds.Value ); + } + + /// + /// 设置执行起始时间 + /// + private void SetStartTime( TriggerBuilder builder ) { + if ( StartTime == null ) { + builder.StartNow(); + return; + } + builder.StartAt( StartTime.Value ); + } + + /// + /// 设置执行结束时间 + /// + private void SetEndTime( TriggerBuilder builder ) { + if ( EndTime == null ) + return; + builder.EndAt( EndTime.Value ); + } + + /// + /// 设置Cron表达式 + /// + private void SetCron( TriggerBuilder builder ) { + if ( Cron == null ) + return; + if ( CronScheduleAction == null ) { + builder.WithCronSchedule( Cron ); + return; + } + builder.WithCronSchedule( Cron, CronScheduleAction ); + } +} \ No newline at end of file diff --git a/src/Util.Scheduling.Quartz/Usings.cs b/src/Util.Scheduling.Quartz/Usings.cs new file mode 100644 index 000000000..7224f3c14 --- /dev/null +++ b/src/Util.Scheduling.Quartz/Usings.cs @@ -0,0 +1,6 @@ +global using System; +global using System.Threading.Tasks; +global using System.Threading; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Quartz; diff --git a/src/Util.Scheduling/01-Util.Scheduling.csproj b/src/Util.Scheduling/01-Util.Scheduling.csproj new file mode 100644 index 000000000..904068590 --- /dev/null +++ b/src/Util.Scheduling/01-Util.Scheduling.csproj @@ -0,0 +1,33 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Scheduling + Util.Scheduling + Util.Scheduling是Util应用框架后台任务封装基础类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Scheduling.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Scheduling.xml + + + + + True + False + + + + + + + + + diff --git a/src/Util.Scheduling/HostService.cs b/src/Util.Scheduling/HostService.cs new file mode 100644 index 000000000..e15e66403 --- /dev/null +++ b/src/Util.Scheduling/HostService.cs @@ -0,0 +1,46 @@ +namespace Util.Scheduling; + +/// +/// 托管服务 +/// +public class HostService : IHostedService { + /// + /// 调度管理器 + /// + private readonly ISchedulerManager _manager; + /// + /// 调度器配置 + /// + private readonly SchedulerOptions _options; + /// + /// 调度器 + /// + private IScheduler _scheduler; + + /// + /// 初始化托管服务 + /// + /// 调度管理器 + /// 调度器配置 + public HostService( ISchedulerManager manager, IOptions options ) { + _manager = manager ?? throw new ArgumentNullException( nameof( manager ) ); + _options = options?.Value ?? new SchedulerOptions(); + _scheduler = NullScheduler.Instance; + } + + /// + /// 启动服务 + /// + public async Task StartAsync( CancellationToken cancellationToken ) { + await _manager.ScanJobsAsync( _options.IsScanJobs ); + _scheduler = await _manager.GetSchedulerAsync(); + await _scheduler.StartAsync( cancellationToken ); + } + + /// + /// 停止服务 + /// + public async Task StopAsync( CancellationToken cancellationToken ) { + await _scheduler.StopAsync(cancellationToken); + } +} \ No newline at end of file diff --git a/src/Util.Scheduling/IJob.cs b/src/Util.Scheduling/IJob.cs new file mode 100644 index 000000000..cddfd3d41 --- /dev/null +++ b/src/Util.Scheduling/IJob.cs @@ -0,0 +1,11 @@ +namespace Util.Scheduling; + +/// +/// 后台任务 +/// +public interface IJob { + /// + /// 配置 + /// + void Config(); +} \ No newline at end of file diff --git a/src/Util.Scheduling/IJobInfo.cs b/src/Util.Scheduling/IJobInfo.cs new file mode 100644 index 000000000..747846f86 --- /dev/null +++ b/src/Util.Scheduling/IJobInfo.cs @@ -0,0 +1,7 @@ +namespace Util.Scheduling; + +/// +/// 后台任务信息 +/// +public interface IJobInfo { +} \ No newline at end of file diff --git a/src/Util.Scheduling/IJobTrigger.cs b/src/Util.Scheduling/IJobTrigger.cs new file mode 100644 index 000000000..7e7428d12 --- /dev/null +++ b/src/Util.Scheduling/IJobTrigger.cs @@ -0,0 +1,7 @@ +namespace Util.Scheduling; + +/// +/// 后台任务触发器 +/// +public interface IJobTrigger { +} \ No newline at end of file diff --git a/src/Util.Scheduling/IScan.cs b/src/Util.Scheduling/IScan.cs new file mode 100644 index 000000000..f7c2bfd0e --- /dev/null +++ b/src/Util.Scheduling/IScan.cs @@ -0,0 +1,7 @@ +namespace Util.Scheduling; + +/// +/// 自动扫描,后台任务实现该接口,当关闭全局扫描时也会自动加入 +/// +public interface IScan { +} \ No newline at end of file diff --git a/src/Util.Scheduling/IScheduler.cs b/src/Util.Scheduling/IScheduler.cs new file mode 100644 index 000000000..5fec38f8f --- /dev/null +++ b/src/Util.Scheduling/IScheduler.cs @@ -0,0 +1,17 @@ +namespace Util.Scheduling; + +/// +/// 后台任务调度器 +/// +public interface IScheduler { + /// + /// 启动 + /// + /// 取消令牌 + Task StartAsync( CancellationToken cancellationToken = default ); + /// + /// 停止 + /// + /// 取消令牌 + Task StopAsync( CancellationToken cancellationToken = default ); +} \ No newline at end of file diff --git a/src/Util.Scheduling/ISchedulerManager.cs b/src/Util.Scheduling/ISchedulerManager.cs new file mode 100644 index 000000000..99cf5ba3e --- /dev/null +++ b/src/Util.Scheduling/ISchedulerManager.cs @@ -0,0 +1,26 @@ +namespace Util.Scheduling; + +/// +/// 后台任务调度管理器 +/// +public interface ISchedulerManager { + /// + /// 添加任务 + /// + /// 任务类型 + Task AddJobAsync() where TJob : IJob, new(); + /// + /// 添加任务 + /// + /// 任务 + Task AddJobAsync( IJob job ); + /// + /// 扫描添加任务 + /// + /// 是否扫描所有任务 + Task ScanJobsAsync( bool isScanAllJobs = true ); + /// + /// 获取后台任务调度器 + /// + Task GetSchedulerAsync(); +} \ No newline at end of file diff --git a/src/Util.Scheduling/NullScheduler.cs b/src/Util.Scheduling/NullScheduler.cs new file mode 100644 index 000000000..aa02e2bd5 --- /dev/null +++ b/src/Util.Scheduling/NullScheduler.cs @@ -0,0 +1,39 @@ +namespace Util.Scheduling; + +/// +/// 空后台任务调度器 +/// +public class NullScheduler : IScheduler { + /// + /// 空调度器实例 + /// + public static readonly IScheduler Instance = new NullScheduler(); + + /// + /// 启动 + /// + public Task StartAsync( CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } + + /// + /// 暂停 + /// + public Task PauseAsync() { + return Task.CompletedTask; + } + + /// + /// 恢复 + /// + public Task ResumeAsync() { + return Task.CompletedTask; + } + + /// + /// 停止 + /// + public Task StopAsync( CancellationToken cancellationToken = default ) { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/Util.Scheduling/SchedulerManagerBase.cs b/src/Util.Scheduling/SchedulerManagerBase.cs new file mode 100644 index 000000000..ed9e4b802 --- /dev/null +++ b/src/Util.Scheduling/SchedulerManagerBase.cs @@ -0,0 +1,50 @@ +using Util.Helpers; +using Util.Reflections; + +namespace Util.Scheduling; + +/// +/// 调度管理器 +/// +public abstract class SchedulerManagerBase : ISchedulerManager { + /// + /// 类型查找器 + /// + private readonly ITypeFinder _typeFinder; + + /// + /// 初始化调度管理器 + /// + /// 服务范围工厂 + /// 类型查找器 + protected SchedulerManagerBase( IServiceScopeFactory scopeFactory, ITypeFinder typeFinder ) { + Ioc.ServiceScopeFactory = scopeFactory ?? throw new ArgumentNullException( nameof( scopeFactory ) ); + _typeFinder = typeFinder ?? throw new ArgumentNullException( nameof( typeFinder ) ); + } + + /// + public virtual async Task AddJobAsync() where TJob : IJob, new() { + return await AddJobAsync( new TJob() ); + } + + /// + public abstract Task AddJobAsync( IJob job ); + + /// + public virtual async Task ScanJobsAsync( bool isScanAllJobs = true ) { + var types = _typeFinder.Find() ?? new List(); + if ( isScanAllJobs ) { + types.AddRange( _typeFinder.Find() ); + types = types.DistinctBy( t => t.FullName ).ToList(); + } + foreach ( var type in types ) { + var job = Reflection.CreateInstance( type ); + if ( job == null ) + continue; + await AddJobAsync( job ); + } + } + + /// + public abstract Task GetSchedulerAsync(); +} \ No newline at end of file diff --git a/src/Util.Scheduling/SchedulerOptions.cs b/src/Util.Scheduling/SchedulerOptions.cs new file mode 100644 index 000000000..0eff7c645 --- /dev/null +++ b/src/Util.Scheduling/SchedulerOptions.cs @@ -0,0 +1,11 @@ +namespace Util.Scheduling; + +/// +/// 后台任务调度器配置 +/// +public class SchedulerOptions { + /// + /// 是否扫描加载后台任务 + /// + public bool IsScanJobs { get; set; } +} \ No newline at end of file diff --git a/src/Util.Scheduling/Usings.cs b/src/Util.Scheduling/Usings.cs new file mode 100644 index 000000000..8b8642015 --- /dev/null +++ b/src/Util.Scheduling/Usings.cs @@ -0,0 +1,8 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.Options; \ No newline at end of file diff --git a/src/Util.Security/02-Util.Security.csproj b/src/Util.Security/02-Util.Security.csproj deleted file mode 100644 index 991abec1c..000000000 --- a/src/Util.Security/02-Util.Security.csproj +++ /dev/null @@ -1,44 +0,0 @@ - - - - netstandard2.0 - Util.Security - Util.Security - 何镇汐 - 何镇汐 - 何镇汐 - Util.Security是Util应用框架的权限类库。 -Util是一个.net core平台下的应用框架,旨在提升小型团队的开发输出能力,由常用公共操作类(工具类)、分层架构基类、Ui组件,第三方组件封装,第三方业务接口封装,配套代码生成模板,权限等组成。 - - Util应用框架以MIT开源发布,可随意使用 - https://github.com/dotnetcore/Util/blob/master/LICENSE.txt - https://github.com/dotnetcore/Util - https://avatars2.githubusercontent.com/u/19421630 - https://github.com/dotnetcore/Util - git - util;applicationframework;dotnetcore;aspnetcore; - 0.2.2-preview - 0.2.2.0 - 0.2.2.0 - false - - - - ..\..\output\release\ - ..\..\output\release\netstandard2.0\Util.Security.xml - - - - ..\..\output\release\ - ..\..\output\release\netstandard2.0\Util.Security.xml - - - - - - - - - - - diff --git a/src/Util.Security/05-Util.Security.csproj b/src/Util.Security/05-Util.Security.csproj new file mode 100644 index 000000000..ba3d83f4e --- /dev/null +++ b/src/Util.Security/05-Util.Security.csproj @@ -0,0 +1,37 @@ + + + + $(NetTargetFramework) + Util + icon.jpg + Util.Security是Util应用框架的安全相关类库 + Util.Security + + + + .\obj\Debug\$(NetTargetFramework)\Util.Security.xml + + + + + + .\obj\Release\$(NetTargetFramework)\Util.Security.xml + + + + + True + False + + + + + + + + + + + + + diff --git a/src/Util.Security/Extensions.Session.cs b/src/Util.Security/Extensions.Session.cs deleted file mode 100644 index 9a7966815..000000000 --- a/src/Util.Security/Extensions.Session.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Util.Domains.Sessions; - -namespace Util.Security { - /// - /// 用户会话扩展 - /// - public static class Extensions { - /// - /// 获取当前应用程序 - /// - /// 用户会话 - public static string GetApplication( this ISession session ) { - return ""; - } - - /// - /// 获取当前租户 - /// - /// 用户会话 - public static string GetTenant( this ISession session ) { - return ""; - } - - /// - /// 获取当前操作人姓名 - /// - /// 用户会话 - public static string GetFullName( this ISession session ) { - return ""; - } - - /// - /// 获取当前操作人角色名 - /// - /// 用户会话 - public static string GetRoleName( this ISession session ) { - return ""; - } - } -} diff --git a/src/Util.Security/Helpers/Encrypt.cs b/src/Util.Security/Helpers/Encrypt.cs new file mode 100644 index 000000000..77ccb71bd --- /dev/null +++ b/src/Util.Security/Helpers/Encrypt.cs @@ -0,0 +1,389 @@ +using RSAExtensions; + +namespace Util.Helpers; + +/// +/// 加密操作 +/// +public static class Encrypt { + + #region Md5加密 + + /// + /// Md5加密,返回16位结果 + /// + /// 值 + public static string Md5By16( string value ) { + return Md5By16( value, Encoding.UTF8 ); + } + + /// + /// Md5加密,返回16位结果 + /// + /// 值 + /// 字符编码 + public static string Md5By16( string value, Encoding encoding ) { + return Md5( value, encoding, 4, 8 ); + } + + /// + /// Md5加密 + /// + private static string Md5( string value, Encoding encoding, int? startIndex, int? length ) { + if( string.IsNullOrWhiteSpace( value ) ) + return string.Empty; + var md5 = MD5.Create(); + string result; + try { + var hash = md5.ComputeHash( encoding.GetBytes( value ) ); + result = startIndex == null ? BitConverter.ToString( hash ) : BitConverter.ToString( hash, startIndex.SafeValue(), length.SafeValue() ); + } + finally { + md5.Clear(); + } + return result.Replace( "-", "" ); + } + + /// + /// Md5加密,返回32位结果 + /// + /// 值 + public static string Md5By32( string value ) { + return Md5By32( value, Encoding.UTF8 ); + } + + /// + /// Md5加密,返回32位结果 + /// + /// 值 + /// 字符编码 + public static string Md5By32( string value, Encoding encoding ) { + return Md5( value, encoding, null, null ); + } + + #endregion + + #region DES加密 + + /// + /// DES密钥,24位字符串 + /// + public static string DesKey = "#s^un2ye21fcv%|f0XpR,+vh"; + + /// + /// DES加密 + /// + /// 待加密的值 + public static string DesEncrypt( object value ) { + return DesEncrypt( value, DesKey ); + } + + /// + /// DES加密 + /// + /// 待加密的值 + /// 密钥,24位 + /// 编码 + /// 加密模式 + /// 填充模式 + public static string DesEncrypt( object value, string key, Encoding encoding = null, CipherMode cipherMode = CipherMode.ECB, PaddingMode paddingMode = PaddingMode.PKCS7 ) { + string text = value.SafeString(); + if( ValidateDes( text, key ) == false ) + return string.Empty; + using var transform = CreateDesProvider( key, cipherMode, paddingMode ).CreateEncryptor(); + return GetEncryptResult( text, encoding, transform ); + } + + /// + /// 验证Des加密参数 + /// + private static bool ValidateDes( string text, string key ) { + if( text.IsEmpty() || key.IsEmpty() ) + return false; + return key.Length == 24; + } + + /// + /// 创建Des加密服务提供程序 + /// + private static TripleDES CreateDesProvider( string key, CipherMode cipherMode, PaddingMode paddingMode ) { + var result = TripleDES.Create(); + result.Key = Encoding.ASCII.GetBytes( key ); + result.Mode = cipherMode; + result.Padding = paddingMode; + return result; + } + + /// + /// 获取加密结果 + /// + private static string GetEncryptResult( string value, Encoding encoding, ICryptoTransform transform ) { + encoding ??= Encoding.UTF8; + var bytes = encoding.GetBytes( value ); + var result = transform.TransformFinalBlock( bytes, 0, bytes.Length ); + return System.Convert.ToBase64String( result ); + } + + /// + /// DES解密 + /// + /// 加密后的值 + public static string DesDecrypt( object value ) { + return DesDecrypt( value, DesKey ); + } + + /// + /// DES解密 + /// + /// 加密后的值 + /// 密钥,24位 + /// 编码 + /// 加密模式 + /// 填充模式 + public static string DesDecrypt( object value, string key, Encoding encoding = null, CipherMode cipherMode = CipherMode.ECB, PaddingMode paddingMode = PaddingMode.PKCS7 ) { + string text = value.SafeString(); + if( !ValidateDes( text, key ) ) + return string.Empty; + using var transform = CreateDesProvider( key, cipherMode, paddingMode ).CreateDecryptor(); + return GetDecryptResult( text, encoding, transform ); + } + + /// + /// 获取解密结果 + /// + private static string GetDecryptResult( string value, Encoding encoding, ICryptoTransform transform ) { + encoding ??= Encoding.UTF8; + var bytes = System.Convert.FromBase64String( value ); + var result = transform.TransformFinalBlock( bytes, 0, bytes.Length ); + return encoding.GetString( result ); + } + + #endregion + + #region AES加密 + + /// + /// 128位0向量 + /// + private static byte[] _iv; + /// + /// 128位0向量 + /// + private static byte[] Iv { + get { + if( _iv == null ) { + var size = 16; + _iv = new byte[size]; + for( int i = 0; i < size; i++ ) + _iv[i] = 0; + } + return _iv; + } + } + + /// + /// AES密钥 + /// + public static string AesKey = "QaP1AF8utIarcBqdhYTZpVGbiNQ9M6IL"; + + /// + /// AES加密 + /// + /// 待加密的值 + public static string AesEncrypt( string value ) { + return AesEncrypt( value, AesKey ); + } + + /// + /// AES加密 + /// + /// 待加密的值 + /// 密钥 + /// 编码 + /// 加密模式 + /// 填充模式 + /// 初始化向量 + public static string AesEncrypt( string value, string key, Encoding encoding = null, CipherMode cipherMode = CipherMode.CBC, PaddingMode paddingMode = PaddingMode.PKCS7, byte[] iv = null ) { + if( value.IsEmpty() || key.IsEmpty() ) + return string.Empty; + iv ??= Iv; + var aes = CreateAes( key, cipherMode, paddingMode, iv ); + using var transform = aes.CreateEncryptor( aes.Key, aes.IV ); + return GetEncryptResult( value, encoding, transform ); + } + + /// + /// 创建Aes + /// + private static Aes CreateAes( string key, CipherMode cipherMode, PaddingMode paddingMode, byte[] iv ) { + var result = Aes.Create(); + result.Key = Encoding.ASCII.GetBytes( key ); + result.Mode = cipherMode; + result.Padding = paddingMode; + result.IV = iv; + return result; + } + + /// + /// AES解密 + /// + /// 加密后的值 + public static string AesDecrypt( string value ) { + return AesDecrypt( value, AesKey ); + } + + /// + /// AES解密 + /// + /// 加密后的值 + /// 密钥 + /// 编码 + /// 加密模式 + /// 填充模式 + /// 初始化向量 + public static string AesDecrypt( string value, string key, Encoding encoding = null, CipherMode cipherMode = CipherMode.CBC, PaddingMode paddingMode = PaddingMode.PKCS7, byte[] iv = null ) { + if( value.IsEmpty() || key.IsEmpty() ) + return string.Empty; + iv ??= Iv; + var aes = CreateAes( key, cipherMode, paddingMode, iv ); + using var transform = aes.CreateDecryptor( aes.Key, aes.IV ); + return GetDecryptResult( value, encoding, transform ); + } + + #endregion + + #region HmacSha256加密 + + /// + /// HMACSHA256加密 + /// + /// 值 + /// 密钥 + /// 字符编码 + /// 是否允许空值 + public static string HmacSha256( string value, string key, Encoding encoding = null, bool allowEmptyValue = false ) { + if( value.IsEmpty() || key.IsEmpty() ) { + if( allowEmptyValue == false ) + return string.Empty; + value = string.Empty; + } + encoding ??= Encoding.UTF8; + var sha256 = new HMACSHA256( Encoding.ASCII.GetBytes( key ) ); + var hash = sha256.ComputeHash( encoding.GetBytes( value ) ); + return string.Join( "", hash.ToList().Select( t => t.ToString( "x2" ) ).ToArray() ); + } + + #endregion + + #region RSA加密 + + /// + /// RSA签名 + /// + /// 待加密的值 + /// 私钥 + /// 编码 + /// 加密算法,默认值: HashAlgorithmName.SHA1 + /// Rsa密钥类型,默认值: Pkcs1 + public static string RsaSign( string value, string privateKey, Encoding encoding = null, HashAlgorithmName? hashAlgorithm = null, RSAKeyType rsaKeyType = RSAKeyType.Pkcs1 ) { + if( value.IsEmpty() || privateKey.IsEmpty() ) + return string.Empty; + var rsa = RSA.Create(); + ImportPrivateKey( rsa, privateKey, rsaKeyType ); + encoding ??= Encoding.UTF8; + hashAlgorithm ??= HashAlgorithmName.SHA1; + var result = rsa.SignData( encoding.GetBytes( value ), hashAlgorithm.Value, RSASignaturePadding.Pkcs1 ); + return System.Convert.ToBase64String( result ); + } + + /// + /// 导入私钥 + /// + private static void ImportPrivateKey( RSA rsa, string privateKey, RSAKeyType rsaKeyType ) { + rsa.ImportPrivateKey( rsaKeyType, privateKey ); + } + + /// + /// Rsa验签 + /// + /// 待验签的值 + /// 公钥 + /// 签名 + /// 编码 + /// 加密算法,默认值: HashAlgorithmName.SHA1 + public static bool RsaVerify( string value, string publicKey, string sign, Encoding encoding = null, HashAlgorithmName? hashAlgorithm = null ) { + if( value.IsEmpty() || publicKey.IsEmpty() || sign.IsEmpty() ) + return false; + var rsa = RSA.Create(); + ImportPublicKey( rsa, publicKey ); + encoding ??= Encoding.UTF8; + var signData = System.Convert.FromBase64String( sign ); + hashAlgorithm ??= HashAlgorithmName.SHA1; + return rsa.VerifyData( encoding.GetBytes( value ), signData, hashAlgorithm.Value, RSASignaturePadding.Pkcs1 ); + } + + /// + /// 导入公钥 + /// + private static void ImportPublicKey( RSA rsa, string publicKey ) { + var key = System.Convert.FromBase64String( publicKey ); + rsa.ImportSubjectPublicKeyInfo( key, out _ ); + } + + /// + /// RSA加密 + /// + /// 待加密的值 + /// 公钥 + public static string RsaEncrypt( string value, string publicKey ) { + if( value.IsEmpty() || publicKey.IsEmpty() ) + return string.Empty; + var rsa = RSA.Create(); + ImportPublicKey( rsa, publicKey ); + return rsa.EncryptBigData( value, RSAEncryptionPadding.Pkcs1 ); + } + + /// + /// RSA解密 + /// + /// 加密后的值 + /// 私钥 + public static string RsaDecrypt( string value, string privateKey ) { + if( value.IsEmpty() || privateKey.IsEmpty() ) + return string.Empty; + var rsa = RSA.Create(); + ImportPrivateKey( rsa, privateKey, RSAKeyType.Pkcs1 ); + return rsa.DecryptBigData( value, RSAEncryptionPadding.Pkcs1 ); + } + + #endregion + + #region 生成RSA公钥和私钥对 + + /// + /// 生成RSA公钥和私钥对,返回值Item1为公钥,Item2为私钥 + /// + public static (string, string) CreateRsaKey() { + var rsa = RSA.Create(); + var publicKey = FormatPublicKey( rsa.ExportSubjectPublicKeyInfoPem() ); + var privateKey = rsa.ExportPrivateKey( RSAKeyType.Pkcs1 ); + return (publicKey, privateKey); + } + + /// + /// 格式化公钥 + /// + private static string FormatPublicKey( string key ) { + return key.Replace( "-----BEGIN RSA PRIVATE KEY-----", "" ) + .Replace( "-----END RSA PRIVATE KEY-----", "" ) + .Replace( "-----BEGIN RSA PUBLIC KEY-----", "" ) + .Replace( "-----END RSA PUBLIC KEY-----", "" ) + .Replace( "-----BEGIN PRIVATE KEY-----", "" ) + .Replace( "-----END PRIVATE KEY-----", "" ) + .Replace( "-----BEGIN PUBLIC KEY-----", "" ) + .Replace( "-----END PUBLIC KEY-----", "" ) + .Replace( Environment.NewLine, "" ); + } + + #endregion +} \ No newline at end of file diff --git a/src/Util.Security/Security/Authentication/UnauthenticatedIdentity.cs b/src/Util.Security/Security/Authentication/UnauthenticatedIdentity.cs new file mode 100644 index 000000000..0d6302b25 --- /dev/null +++ b/src/Util.Security/Security/Authentication/UnauthenticatedIdentity.cs @@ -0,0 +1,16 @@ +namespace Util.Security.Authentication; + +/// +/// 未认证的身份标识 +/// +public class UnauthenticatedIdentity : ClaimsIdentity { + /// + /// 是否认证 + /// + public override bool IsAuthenticated => false; + + /// + /// 未认证的身份标识实例 + /// + public static readonly UnauthenticatedIdentity Instance = new(); +} \ No newline at end of file diff --git a/src/Util.Security/Security/Authentication/UnauthenticatedPrincipal.cs b/src/Util.Security/Security/Authentication/UnauthenticatedPrincipal.cs new file mode 100644 index 000000000..ba5040e8c --- /dev/null +++ b/src/Util.Security/Security/Authentication/UnauthenticatedPrincipal.cs @@ -0,0 +1,22 @@ +namespace Util.Security.Authentication; + +/// +/// 未认证安全主体 +/// +public class UnauthenticatedPrincipal : ClaimsPrincipal { + /// + /// 初始化未认证安全主体 + /// + private UnauthenticatedPrincipal(){ + } + + /// + /// 未认证安全主体 + /// + public static readonly UnauthenticatedPrincipal Instance = new(); + + /// + /// 身份标识 + /// + public override IIdentity Identity => UnauthenticatedIdentity.Instance; +} \ No newline at end of file diff --git a/src/Util.Security/Security/Authorization/DefaultPermissionManager.cs b/src/Util.Security/Security/Authorization/DefaultPermissionManager.cs new file mode 100644 index 000000000..49367c961 --- /dev/null +++ b/src/Util.Security/Security/Authorization/DefaultPermissionManager.cs @@ -0,0 +1,18 @@ +using System.Threading; + +namespace Util.Security.Authorization; + +/// +/// 默认权限管理器 +/// +public class DefaultPermissionManager : IPermissionManager { + /// + public bool HasPermission( string resourceUri ) { + return true; + } + + /// + public Task HasPermissionAsync( string resourceUri, CancellationToken cancellationToken = default ) { + return Task.FromResult( true ); + } +} \ No newline at end of file diff --git a/src/Util.Security/Security/Authorization/IPermissionManager.cs b/src/Util.Security/Security/Authorization/IPermissionManager.cs new file mode 100644 index 000000000..d4b9fc758 --- /dev/null +++ b/src/Util.Security/Security/Authorization/IPermissionManager.cs @@ -0,0 +1,20 @@ +using System.Threading; + +namespace Util.Security.Authorization; + +/// +/// 权限管理器 +/// +public interface IPermissionManager { + /// + /// 检查当前用户是否具有该资源的访问权限,返回true表示允许访问,false表示拒绝访问 + /// + /// 资源标识 + bool HasPermission( string resourceUri ); + /// + /// 检查当前用户是否具有该资源的访问权限,返回true表示允许访问,false表示拒绝访问 + /// + /// 资源标识 + /// 取消令牌 + Task HasPermissionAsync( string resourceUri, CancellationToken cancellationToken = default ); +} \ No newline at end of file diff --git a/src/Util.Security/Security/ClaimTypes.cs b/src/Util.Security/Security/ClaimTypes.cs new file mode 100644 index 000000000..08ccf5204 --- /dev/null +++ b/src/Util.Security/Security/ClaimTypes.cs @@ -0,0 +1,43 @@ +namespace Util.Security; + +/// +/// 声明类型 +/// +public static class ClaimTypes { + /// + /// sub,用户标识 + /// + public static string UserId { get; set; } = "sub"; + /// + /// name,姓名 + /// + public static string Name { get; set; } = "name"; + /// + /// email,电子邮件 + /// + public static string Email { get; set; } = "email"; + /// + /// phone_number,手机号 + /// + public static string PhoneNumber { get; set; } = "phone_number"; + /// + /// application_id,应用程序标识 + /// + public static string ApplicationId { get; set; } = "application_id"; + /// + /// application_code,应用程序编码 + /// + public static string ApplicationCode { get; set; } = "application_code"; + /// + /// application_name,应用程序名称 + /// + public static string ApplicationName { get; set; } = "application_name"; + /// + /// tenant_id,租户标识 + /// + public static string TenantId { get; set; } = "tenant_id"; + /// + /// role,角色 + /// + public static string Role { get; set; } = "role"; +} \ No newline at end of file diff --git a/src/Util.Security/Security/Encryptors/IEncryptor.cs b/src/Util.Security/Security/Encryptors/IEncryptor.cs new file mode 100644 index 000000000..5a2fcbac5 --- /dev/null +++ b/src/Util.Security/Security/Encryptors/IEncryptor.cs @@ -0,0 +1,17 @@ +namespace Util.Security.Encryptors; + +/// +/// 加密器 +/// +public interface IEncryptor { + /// + /// 加密 + /// + /// 原始数据 + string Encrypt( string data ); + /// + /// 解密 + /// + /// 已加密数据 + string Decrypt( string data ); +} \ No newline at end of file diff --git a/src/Util.Security/Security/Encryptors/NullEncryptor.cs b/src/Util.Security/Security/Encryptors/NullEncryptor.cs new file mode 100644 index 000000000..6a81b9b93 --- /dev/null +++ b/src/Util.Security/Security/Encryptors/NullEncryptor.cs @@ -0,0 +1,27 @@ +namespace Util.Security.Encryptors; + +/// +/// 空加密器 +/// +public class NullEncryptor : IEncryptor { + /// + /// 空加密器实例 + /// + public static readonly IEncryptor Instance = new NullEncryptor(); + + /// + /// 加密 + /// + /// 原始数据 + public string Encrypt( string data ) { + return string.Empty; + } + + /// + /// 解密 + /// + /// 已加密数据 + public string Decrypt( string data ) { + return string.Empty; + } +} \ No newline at end of file diff --git a/src/Util.Security/Usings.cs b/src/Util.Security/Usings.cs new file mode 100644 index 000000000..38dc23016 --- /dev/null +++ b/src/Util.Security/Usings.cs @@ -0,0 +1,7 @@ +global using System; +global using System.Threading.Tasks; +global using System.Linq; +global using System.Text; +global using System.Security.Cryptography; +global using System.Security.Claims; +global using System.Security.Principal; \ No newline at end of file diff --git a/src/Util.Templates.Handlebars/03-Util.Templates.Handlebars.csproj b/src/Util.Templates.Handlebars/03-Util.Templates.Handlebars.csproj new file mode 100644 index 000000000..54cbf9a56 --- /dev/null +++ b/src/Util.Templates.Handlebars/03-Util.Templates.Handlebars.csproj @@ -0,0 +1,37 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Templates.Handlebars + Util.Templates + Util.Templates.Handlebars是Util应用框架Handlebars模板操作类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Templates.Handlebars.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Templates.Handlebars.xml + + + + + True + False + + + + + + + + + + + + + diff --git a/src/Util.Templates.Handlebars/HandlebarsDotNet/HandlebarsTemplateEngine.cs b/src/Util.Templates.Handlebars/HandlebarsDotNet/HandlebarsTemplateEngine.cs new file mode 100644 index 000000000..6b27fc7ae --- /dev/null +++ b/src/Util.Templates.Handlebars/HandlebarsDotNet/HandlebarsTemplateEngine.cs @@ -0,0 +1,70 @@ +namespace Util.Templates.HandlebarsDotNet; + +/// +/// Handlebars模板引擎 +/// +public class HandlebarsTemplateEngine : IHandlebarsTemplateEngine { + /// + /// 模板缓存 + /// + protected static readonly ConcurrentDictionary> TemplateCache = new(); + /// + /// 文本编码器 + /// + private ITextEncoder _encoder; + + /// + /// 清除缓存 + /// + public static void ClearCache() { + TemplateCache.Clear(); + } + + /// + /// 设置文本编码器 + /// + /// 文本编码器 + public IHandlebarsTemplateEngine Encoder( ITextEncoder encoder ) { + _encoder = encoder; + return this; + } + + /// + /// 设置 Html 编码器 + /// + public IHandlebarsTemplateEngine HtmlEncoder() { + return Encoder( new HtmlEncoder() ); + } + + /// + /// 渲染模板 + /// + /// 模板字符串 + /// 模板数据 + public virtual string Render( string template, object data = null ) { + if ( string.IsNullOrWhiteSpace( template ) ) + return null; + var compiledTemplate = GetCompiledTemplateFromCache( template ); + return compiledTemplate?.Invoke( data ); + } + + /// + /// 从缓存中获取已编译模板 + /// + protected virtual HandlebarsTemplate GetCompiledTemplateFromCache( string template ) { + return TemplateCache.GetOrAdd( template.GetHashCode(), _ => { + var handlebars = Handlebars.Create(); + handlebars.Configuration.TextEncoder = _encoder; + return handlebars.Compile( template ); + } ); + } + + /// + /// 渲染模板 + /// + /// 模板字符串 + /// 模板数据 + public virtual Task RenderAsync( string template, object data = null ) { + return Task.FromResult( Render( template, data ) ); + } +} \ No newline at end of file diff --git a/src/Util.Templates.Handlebars/HandlebarsDotNet/IHandlebarsTemplateEngine.cs b/src/Util.Templates.Handlebars/HandlebarsDotNet/IHandlebarsTemplateEngine.cs new file mode 100644 index 000000000..b07de2a5b --- /dev/null +++ b/src/Util.Templates.Handlebars/HandlebarsDotNet/IHandlebarsTemplateEngine.cs @@ -0,0 +1,16 @@ +namespace Util.Templates.HandlebarsDotNet; + +/// +/// Handlebars模板引擎 +/// +public interface IHandlebarsTemplateEngine : ITemplateEngine { + /// + /// 设置文本编码器 + /// + /// 文本编码器 + IHandlebarsTemplateEngine Encoder( ITextEncoder encoder ); + /// + /// 设置 Html 编码器 + /// + IHandlebarsTemplateEngine HtmlEncoder(); +} \ No newline at end of file diff --git a/src/Util.Templates.Handlebars/Infrastructure/HandlebarsServiceRegistrar.cs b/src/Util.Templates.Handlebars/Infrastructure/HandlebarsServiceRegistrar.cs new file mode 100644 index 000000000..d6d4ed31d --- /dev/null +++ b/src/Util.Templates.Handlebars/Infrastructure/HandlebarsServiceRegistrar.cs @@ -0,0 +1,36 @@ +using Util.Infrastructure; +using Util.Templates.HandlebarsDotNet; + +namespace Util.Templates.Infrastructure; + +/// +/// Handlebars模板引擎服务注册器 +/// +public class HandlebarsServiceRegistrar : IServiceRegistrar { + /// + /// 获取服务名 + /// + public static string ServiceName => "Util.Templates.Infrastructure.HandlebarsServiceRegistrar"; + + /// + /// 排序号 + /// + public int OrderId => 620; + + /// + /// 是否启用 + /// + public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName ); + + /// + /// 注册服务 + /// + /// 服务上下文 + public Action Register( ServiceContext serviceContext ) { + serviceContext.HostBuilder.ConfigureServices( ( context, services ) => { + services.AddSingleton(); + services.AddSingleton(); + } ); + return null; + } +} \ No newline at end of file diff --git a/src/Util.Templates.Handlebars/Infrastructure/ServiceRegistrarConfigExtensions.cs b/src/Util.Templates.Handlebars/Infrastructure/ServiceRegistrarConfigExtensions.cs new file mode 100644 index 000000000..5cdf71cd4 --- /dev/null +++ b/src/Util.Templates.Handlebars/Infrastructure/ServiceRegistrarConfigExtensions.cs @@ -0,0 +1,26 @@ +using Util.Infrastructure; + +namespace Util.Templates.Infrastructure; + +/// +/// Handlebars模板引擎服务注册器配置扩展 +/// +public static class ServiceRegistrarConfigExtensions { + /// + /// 启用Handlebars模板引擎服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig EnableHandlebarsServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Enable( HandlebarsServiceRegistrar.ServiceName ); + return config; + } + + /// + ///禁用Handlebars模板引擎服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig DisableHandlebarsServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Disable( HandlebarsServiceRegistrar.ServiceName ); + return config; + } +} \ No newline at end of file diff --git a/src/Util.Templates.Handlebars/Usings.cs b/src/Util.Templates.Handlebars/Usings.cs new file mode 100644 index 000000000..c12b94586 --- /dev/null +++ b/src/Util.Templates.Handlebars/Usings.cs @@ -0,0 +1,5 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Concurrent; +global using Microsoft.Extensions.DependencyInjection; +global using HandlebarsDotNet; \ No newline at end of file diff --git a/src/Util.Templates.Razor/02-Util.Templates.Razor.csproj b/src/Util.Templates.Razor/02-Util.Templates.Razor.csproj new file mode 100644 index 000000000..a9548559b --- /dev/null +++ b/src/Util.Templates.Razor/02-Util.Templates.Razor.csproj @@ -0,0 +1,43 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Templates.Razor + Util.Templates + Util.Templates.Razor是Util应用框架Razor模板操作类库,使用开源项目RazorEngineCore作为Razor解析基础库,由于某些操作不符合Util的使用习惯,故将源码复制到本类库,并进行了相应修改 + + + + true + + .\obj\Debug\$(NetTargetFramework)\Util.Templates.Razor.xml + + + + true + + .\obj\Release\$(NetTargetFramework)\Util.Templates.Razor.xml + + + + + + + + + True + False + + + + + + + + + + + + + diff --git a/src/Util.Templates.Razor/Infrastructure/RazorServiceRegistrar.cs b/src/Util.Templates.Razor/Infrastructure/RazorServiceRegistrar.cs new file mode 100644 index 000000000..3d3d67b31 --- /dev/null +++ b/src/Util.Templates.Razor/Infrastructure/RazorServiceRegistrar.cs @@ -0,0 +1,66 @@ +using Util.Infrastructure; +using Util.Reflections; +using Util.Templates.Razor; +using Util.Templates.Razor.Filters; + +namespace Util.Templates.Infrastructure; + +/// +/// Razor模板引擎服务注册器 +/// +public class RazorServiceRegistrar : IServiceRegistrar { + /// + /// 获取服务名 + /// + public static string ServiceName => "Util.Templates.Infrastructure.RazorServiceRegistrar"; + + /// + /// 排序号 + /// + public int OrderId => 610; + + /// + /// 是否启用 + /// + public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName ); + + /// + /// 注册服务 + /// + /// 服务上下文 + public Action Register( ServiceContext serviceContext ) { + serviceContext.HostBuilder.ConfigureServices( ( context, services ) => { + RegisterDependency( services ); + } ); + RegisterAssemblyReference( serviceContext.TypeFinder ); + RegisterFilters(); + return null; + } + + /// + /// 注册依赖 + /// + private void RegisterDependency( IServiceCollection services ) { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + } + + /// + /// 注册程序集引用 + /// + private void RegisterAssemblyReference( ITypeFinder finder ) { + RazorTemplateEngine.AddAssemblyReference( "System.Collections" ); + if( !( finder is AppDomainTypeFinder typeFinder ) ) + return; + typeFinder.GetAssemblies().ForEach( RazorTemplateEngine.AddAssemblyReference ); + } + + /// + /// 注册模板过滤器 + /// + private void RegisterFilters() { + RazorTemplateEngine.AddFilter( new ModelFilter() ); + RazorTemplateEngine.AddFilter( new PartialAsyncFilter() ); + } +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/Infrastructure/ServiceRegistrarConfigExtensions.cs b/src/Util.Templates.Razor/Infrastructure/ServiceRegistrarConfigExtensions.cs new file mode 100644 index 000000000..600dc5e7f --- /dev/null +++ b/src/Util.Templates.Razor/Infrastructure/ServiceRegistrarConfigExtensions.cs @@ -0,0 +1,26 @@ +using Util.Infrastructure; + +namespace Util.Templates.Infrastructure; + +/// +/// Razor模板引擎服务注册器配置扩展 +/// +public static class ServiceRegistrarConfigExtensions { + /// + /// 启用Razor模板引擎服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig EnableRazorServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Enable( RazorServiceRegistrar.ServiceName ); + return config; + } + + /// + ///禁用Razor模板引擎服务注册器 + /// + /// 服务注册器配置 + public static ServiceRegistrarConfig DisableRazorServiceRegistrar( this ServiceRegistrarConfig config ) { + ServiceRegistrarConfig.Disable( RazorServiceRegistrar.ServiceName ); + return config; + } +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/Razor/Filters/ModelFilter.cs b/src/Util.Templates.Razor/Razor/Filters/ModelFilter.cs new file mode 100644 index 000000000..fa63b8d20 --- /dev/null +++ b/src/Util.Templates.Razor/Razor/Filters/ModelFilter.cs @@ -0,0 +1,14 @@ +namespace Util.Templates.Razor.Filters; + +/// +/// 模型过滤器,对模板中的@model进行替换 +/// +public class ModelFilter : ITemplateFilter { + /// + /// 过滤模板 + /// + /// 模板字符串 + public string Filter( string template ) { + return Util.Helpers.Regex.Replace( template, @"@model\s+(\w+)", "@inherits RazorEngineCore.RazorEngineTemplateBase<$1>" ); + } +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/Razor/Filters/PartialAsyncFilter.cs b/src/Util.Templates.Razor/Razor/Filters/PartialAsyncFilter.cs new file mode 100644 index 000000000..b8c2b1c9a --- /dev/null +++ b/src/Util.Templates.Razor/Razor/Filters/PartialAsyncFilter.cs @@ -0,0 +1,14 @@ +namespace Util.Templates.Razor.Filters; + +/// +/// 部分视图过滤器,移除模板中的await +/// +public class PartialAsyncFilter : ITemplateFilter { + /// + /// 过滤模板 + /// + /// 模板字符串 + public string Filter( string template ) { + return Util.Helpers.Regex.Replace( template, @"await\s+Html.PartialAsync", "Html.PartialAsync" ); + } +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/Razor/Helpers/HtmlHelper.cs b/src/Util.Templates.Razor/Razor/Helpers/HtmlHelper.cs new file mode 100644 index 000000000..6a4759815 --- /dev/null +++ b/src/Util.Templates.Razor/Razor/Helpers/HtmlHelper.cs @@ -0,0 +1,34 @@ +namespace Util.Templates.Razor.Helpers; + +/// +/// HtmlHelper +/// +public class HtmlHelper { + /// + /// Razor模板引擎 + /// + private readonly ITemplateEngine _engine; + + /// + /// 初始化HtmlHelper + /// + /// Razor模板引擎 + public HtmlHelper( ITemplateEngine engine ) { + _engine = engine; + } + + /// + /// 引用部分视图 + /// + public string Partial( string partialViewName, object model ) { + var path = Util.Helpers.Common.GetPhysicalPath( partialViewName ); + return _engine.RenderByPath( path, model ); + } + + /// + /// 引用部分视图 + /// + public string PartialAsync( string partialViewName, object model ) { + return Partial( partialViewName, model ); + } +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/Razor/IRazorTemplateEngine.cs b/src/Util.Templates.Razor/Razor/IRazorTemplateEngine.cs new file mode 100644 index 000000000..bd7c89ce3 --- /dev/null +++ b/src/Util.Templates.Razor/Razor/IRazorTemplateEngine.cs @@ -0,0 +1,7 @@ +namespace Util.Templates.Razor; + +/// +/// Razor模板引擎 +/// +public interface IRazorTemplateEngine : ITemplateEngine { +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/Razor/RazorTemplateEngine.cs b/src/Util.Templates.Razor/Razor/RazorTemplateEngine.cs new file mode 100644 index 000000000..396b80f21 --- /dev/null +++ b/src/Util.Templates.Razor/Razor/RazorTemplateEngine.cs @@ -0,0 +1,169 @@ +namespace Util.Templates.Razor; + +/// +/// Razor模板引擎 +/// +public class RazorTemplateEngine : IRazorTemplateEngine { + /// + /// 模板过滤器 + /// + private static readonly ConcurrentDictionary _filters = new(); + /// + /// 程序集引用列表 + /// + private static readonly ConcurrentDictionary _assemblies = new(); + /// + /// 模板缓存 + /// + protected static readonly ConcurrentDictionary TemplateCache = new(); + /// + /// 是否自动加载程序集引用列表 + /// + private static bool _isAutoLoadAssemblies; + /// + /// Razor模板引擎 + /// + protected readonly IRazorEngine RazorEngine; + + /// + /// 初始化Razor模板引擎 + /// + /// Razor模板引擎 + public RazorTemplateEngine( IRazorEngine razorEngine ) { + RazorEngine = razorEngine ?? throw new ArgumentNullException( nameof( razorEngine ) ); + _isAutoLoadAssemblies = true; + } + + /// + /// 清除模板缓存 + /// + public static void ClearTemplateCache() { + TemplateCache.Clear(); + } + + /// + /// 添加模板过滤器 + /// + /// 模板过滤器 + public static void AddFilter( ITemplateFilter filter ) { + if ( filter == null ) + return; + _filters.TryAdd( filter.GetType().FullName, filter ); + } + + /// + /// 清除模板过滤器 + /// + public static void ClearFilters() { + _filters.Clear(); + } + + /// + /// 添加程序集引用 + /// + /// 程序集 + public static void AddAssemblyReference( Assembly assembly ) { + _assemblies.TryAdd( assembly.FullName, assembly ); + } + + /// + /// 添加程序集引用 + /// + /// 程序集 + public static void AddAssemblyReference( string assembly ) { + AddAssemblyReference( Assembly.Load( assembly ) ); + } + + /// + /// 禁止自动加载程序集引用列表 + /// + public static void DisableAutoLoadAssemblies() { + _isAutoLoadAssemblies = false; + } + + /// + /// 渲染模板 + /// + /// 模板字符串 + /// 模板数据 + public string Render( string template, object data = null ) { + return Render( template, data, null ); + } + + /// + /// 渲染模板 + /// + /// 模板字符串 + /// 模板数据 + /// 模板编译配置操作 + public string Render( string template, object data, Action builderAction ) { + if ( string.IsNullOrWhiteSpace( template ) ) + return null; + var compiledTemplate = GetCompiledTemplateFromCache( template, builderAction ); + return compiledTemplate?.Run( data ); + } + + /// + /// 从缓存中获取已编译模板 + /// + private IRazorEngineCompiledTemplate GetCompiledTemplateFromCache( string template, Action builderAction ) { + return TemplateCache.GetOrAdd( template.GetHashCode(), t => { + template = FilterTemplate( template ); + return GetCompiledTemplate( template, builderAction ); + } ); + } + + /// + /// 过滤模板 + /// + private string FilterTemplate( string template ) { + foreach ( var filter in _filters.Values ) + template = filter.Filter( template ); + return template; + } + + /// + /// 获取已编译模板 + /// + private IRazorEngineCompiledTemplate GetCompiledTemplate( string template, Action builderAction ) { + return RazorEngine.Compile( template, builder => { + LoadAssemblies( builder ); + builderAction?.Invoke( builder ); + } ); + } + + /// + /// 加载程序集引用列表 + /// + private void LoadAssemblies( IRazorEngineCompilationOptionsBuilder builder ) { + if ( _isAutoLoadAssemblies == false ) + return; + foreach ( var assembly in _assemblies.Values ) { + builder.AddAssemblyReference( assembly ); + } + } + + /// + /// 渲染模板 + /// + /// 模板字符串 + /// 模板数据 + public virtual async Task RenderAsync( string template, object data = null ) { + return await RenderAsync( template, data, null ); + } + + /// + /// 渲染模板 + /// + /// 模板字符串 + /// 模板数据 + /// 模板编译配置操作 + public async Task RenderAsync( string template, object data, Action builderAction ) { + if ( string.IsNullOrWhiteSpace( template ) ) + return null; + var compiledTemplate = GetCompiledTemplateFromCache( template, builderAction ); + if ( compiledTemplate == null ) + return null; + return await compiledTemplate.RunAsync( data ); + } +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/Razor/TemplateEngineExtensions.cs b/src/Util.Templates.Razor/Razor/TemplateEngineExtensions.cs new file mode 100644 index 000000000..d0a5daf68 --- /dev/null +++ b/src/Util.Templates.Razor/Razor/TemplateEngineExtensions.cs @@ -0,0 +1,134 @@ +namespace Util.Templates.Razor; + +/// +/// Razor模板引擎扩展 +/// +public static class TemplateEngineExtensions { + /// + /// 清除模板缓存 + /// + /// Razor模板引擎 + public static void ClearTemplateCache( this ITemplateEngine templateEngine ) { + if( templateEngine == null ) + throw new ArgumentNullException( nameof( templateEngine ) ); + RazorTemplateEngine.ClearTemplateCache(); + } + + /// + /// 渲染模板 + /// + /// Razor模板引擎 + /// 模板字符串 + /// 模板数据 + /// 模板编译配置操作 + public static string Render( this ITemplateEngine templateEngine, string template, object data, Action builderAction ) { + if( templateEngine == null ) + throw new ArgumentNullException( nameof( templateEngine ) ); + if( !( templateEngine is RazorTemplateEngine engine ) ) + return null; + return engine.Render( template, data, builderAction ); + } + + /// + /// 渲染模板 + /// + /// Razor模板引擎 + /// 模板字符串 + /// 模板数据 + /// 模板编译配置操作 + public static async Task RenderAsync( this ITemplateEngine templateEngine, string template, object data, Action builderAction ) { + if( templateEngine == null ) + throw new ArgumentNullException( nameof( templateEngine ) ); + if( !( templateEngine is RazorTemplateEngine engine ) ) + return null; + return await engine.RenderAsync( template, data, builderAction ); + } + + /// + /// 通过路径渲染模板 + /// + /// 模板引擎 + /// 模板文件绝对路径 + /// 模板数据 + /// 模板编译配置操作 + public static string RenderByPath( this ITemplateEngine templateEngine, string filePath, object data, Action builderAction ) { + if( templateEngine == null ) + throw new ArgumentNullException( nameof( templateEngine ) ); + var template = Util.Helpers.File.ReadToString( filePath ); + return templateEngine.Render( template, data, builderAction ); + } + + /// + /// 通过路径渲染模板 + /// + /// 模板引擎 + /// 模板文件绝对路径 + /// 模板数据 + /// 模板编译配置操作 + public static async Task RenderByPathAsync( this ITemplateEngine templateEngine, string filePath, object data, Action builderAction ) { + if( templateEngine == null ) + throw new ArgumentNullException( nameof( templateEngine ) ); + var template = await Util.Helpers.File.ReadToStringAsync( filePath ); + return await templateEngine.RenderAsync( template, data, builderAction ); + } + + /// + /// 渲染模板并保存到文件 + /// + /// 模板引擎 + /// 模板字符串 + /// 模板数据 + /// 目标文件绝对路径 + /// 模板编译配置操作 + public static void Save( this ITemplateEngine templateEngine, string template, object data, string filePath, Action builderAction ) { + if( templateEngine == null ) + throw new ArgumentNullException( nameof( templateEngine ) ); + var result = templateEngine.Render( template, data, builderAction ); + Util.Helpers.File.Write( filePath, result ); + } + + /// + /// 渲染模板并保存到文件 + /// + /// 模板引擎 + /// 模板字符串 + /// 模板数据 + /// 目标文件绝对路径 + /// 模板编译配置操作 + public static async Task SaveAsync( this ITemplateEngine templateEngine, string template, object data, string filePath, Action builderAction ) { + if( templateEngine == null ) + throw new ArgumentNullException( nameof( templateEngine ) ); + var result = await templateEngine.RenderAsync( template, data, builderAction ); + await Util.Helpers.File.WriteAsync( filePath, result ); + } + + /// + /// 通过路径渲染模板并保存到文件 + /// + /// 模板引擎 + /// 模板文件绝对路径 + /// 模板数据 + /// 目标文件绝对路径 + /// 模板编译配置操作 + public static void SaveByPath( this ITemplateEngine templateEngine, string templatePath, object data, string filePath, Action builderAction ) { + if( templateEngine == null ) + throw new ArgumentNullException( nameof( templateEngine ) ); + var result = templateEngine.RenderByPath( templatePath, data, builderAction ); + Util.Helpers.File.Write( filePath, result ); + } + + /// + /// 渲染模板并保存到文件 + /// + /// 模板引擎 + /// 模板文件绝对路径 + /// 模板数据 + /// 目标文件绝对路径 + /// 模板编译配置操作 + public static async Task SaveByPathAsync( this ITemplateEngine templateEngine, string templatePath, object data, string filePath, Action builderAction ) { + if( templateEngine == null ) + throw new ArgumentNullException( nameof( templateEngine ) ); + var result = await templateEngine.RenderByPathAsync( templatePath, data, builderAction ); + await Util.Helpers.File.WriteAsync( filePath, result ); + } +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/RazorEngineCore/AnonymousTypeWrapper.cs b/src/Util.Templates.Razor/RazorEngineCore/AnonymousTypeWrapper.cs new file mode 100644 index 000000000..851f05939 --- /dev/null +++ b/src/Util.Templates.Razor/RazorEngineCore/AnonymousTypeWrapper.cs @@ -0,0 +1,62 @@ +using System.Collections; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Reflection; + +namespace RazorEngineCore; + +public class AnonymousTypeWrapper : DynamicObject +{ + private readonly object model; + + public AnonymousTypeWrapper(object model) + { + this.model = model; + } + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + PropertyInfo propertyInfo = this.model.GetType().GetProperty(binder.Name); + + if (propertyInfo == null) + { + result = null; + return false; + } + + result = propertyInfo.GetValue(this.model, null); + + if (result == null) + { + return true; + } + + var type = result.GetType(); + + if (result.IsAnonymous()) + { + result = new AnonymousTypeWrapper(result); + } + + bool isEnumerable = typeof(IEnumerable).IsAssignableFrom(type); + + if (isEnumerable && !(result is string)) + { + result = ((IEnumerable) result) + .Select(e => + { + if (e.IsAnonymous()) + { + return new AnonymousTypeWrapper(e); + } + + return e; + }) + .ToList(); + } + + + return true; + } +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/RazorEngineCore/IRazorEngine.cs b/src/Util.Templates.Razor/RazorEngineCore/IRazorEngine.cs new file mode 100644 index 000000000..6863ae107 --- /dev/null +++ b/src/Util.Templates.Razor/RazorEngineCore/IRazorEngine.cs @@ -0,0 +1,17 @@ +using System; +using System.Threading.Tasks; + +namespace RazorEngineCore; + +public interface IRazorEngine +{ + IRazorEngineCompiledTemplate Compile(string content, Action builderAction = null) + where T : IRazorEngineTemplate; + + Task> CompileAsync(string content, Action builderAction = null) + where T : IRazorEngineTemplate; + + IRazorEngineCompiledTemplate Compile(string content, Action builderAction = null); + + Task CompileAsync(string content, Action builderAction = null); +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/RazorEngineCore/IRazorEngineCompilationOptionsBuilder.cs b/src/Util.Templates.Razor/RazorEngineCore/IRazorEngineCompilationOptionsBuilder.cs new file mode 100644 index 000000000..fcf23e693 --- /dev/null +++ b/src/Util.Templates.Razor/RazorEngineCore/IRazorEngineCompilationOptionsBuilder.cs @@ -0,0 +1,22 @@ +using System; +using System.Reflection; +using Microsoft.CodeAnalysis; + +namespace RazorEngineCore; + +public interface IRazorEngineCompilationOptionsBuilder +{ + RazorEngineCompilationOptions Options { get; set; } + + void AddAssemblyReferenceByName(string assemblyName); + + void AddAssemblyReference(Assembly assembly); + + void AddAssemblyReference(Type type); + + void AddMetadataReference(MetadataReference reference); + + void AddUsing(string namespaceName); + + void Inherits(Type type); +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/RazorEngineCore/IRazorEngineCompiledTemplate.cs b/src/Util.Templates.Razor/RazorEngineCore/IRazorEngineCompiledTemplate.cs new file mode 100644 index 000000000..6fffdff18 --- /dev/null +++ b/src/Util.Templates.Razor/RazorEngineCore/IRazorEngineCompiledTemplate.cs @@ -0,0 +1,19 @@ +using System.IO; +using System.Threading.Tasks; + +namespace RazorEngineCore; + +public interface IRazorEngineCompiledTemplate +{ + void SaveToStream(Stream stream); + + Task SaveToStreamAsync(Stream stream); + + void SaveToFile(string fileName); + + Task SaveToFileAsync(string fileName); + + string Run(object model = null); + + Task RunAsync(object model = null); +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/RazorEngineCore/IRazorEngineCompiledTemplateT.cs b/src/Util.Templates.Razor/RazorEngineCore/IRazorEngineCompiledTemplateT.cs new file mode 100644 index 000000000..71e2151f4 --- /dev/null +++ b/src/Util.Templates.Razor/RazorEngineCore/IRazorEngineCompiledTemplateT.cs @@ -0,0 +1,21 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +namespace RazorEngineCore; + +public interface IRazorEngineCompiledTemplate + where T : IRazorEngineTemplate +{ + void SaveToStream(Stream stream); + + Task SaveToStreamAsync(Stream stream); + + void SaveToFile(string fileName); + + Task SaveToFileAsync(string fileName); + + string Run(Action initializer); + + Task RunAsync(Action initializer); +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/RazorEngineCore/IRazorEngineTemplate.cs b/src/Util.Templates.Razor/RazorEngineCore/IRazorEngineTemplate.cs new file mode 100644 index 000000000..2f795a24e --- /dev/null +++ b/src/Util.Templates.Razor/RazorEngineCore/IRazorEngineTemplate.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Util.Templates.Razor.Helpers; + +namespace RazorEngineCore; + +public interface IRazorEngineTemplate +{ + dynamic Model { get; set; } + HtmlHelper Html { get; set; } + void WriteLiteral(string literal = null); + void Write(object obj = null); + void BeginWriteAttribute(string name, string prefix, int prefixOffset, string suffix, int suffixOffset, int attributeValuesCount); + void WriteAttributeValue(string prefix, int prefixOffset, object value, int valueOffset, int valueLength, bool isLiteral); + void EndWriteAttribute(); + Task ExecuteAsync(); + string Result(); +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/RazorEngineCore/ObjectExtenders.cs b/src/Util.Templates.Razor/RazorEngineCore/ObjectExtenders.cs new file mode 100644 index 000000000..a9c21e83e --- /dev/null +++ b/src/Util.Templates.Razor/RazorEngineCore/ObjectExtenders.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace RazorEngineCore; + +public static class ObjectExtenders +{ + public static ExpandoObject ToExpando(this object obj) + { + ExpandoObject expando = new ExpandoObject(); + IDictionary dictionary = expando; + + foreach (var property in obj.GetType().GetProperties()) + { + dictionary.Add(property.Name, property.GetValue(obj)); + } + + return expando; + } + + public static bool IsAnonymous(this object obj) + { + Type type = obj.GetType(); + + return Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false) + && type.IsGenericType && type.Name.Contains("AnonymousType") + && (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$")) + && type.Attributes.HasFlag(TypeAttributes.NotPublic); + } +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/RazorEngineCore/README.md b/src/Util.Templates.Razor/RazorEngineCore/README.md new file mode 100644 index 000000000..36467caf0 --- /dev/null +++ b/src/Util.Templates.Razor/RazorEngineCore/README.md @@ -0,0 +1,233 @@ +# RazorEngineCore +.NET5 Razor Template Engine. No legacy code. +* .NET 5.0 +* .NET Standard 2.0 +* .NET Framework 4.7.2 +* Windows / Linux +* Publish as single file supported + +[![NuGet](https://img.shields.io/nuget/dt/RazorEngineCore.svg?style=flat-square)](https://www.nuget.org/packages/RazorEngineCore) +[![NuGet](https://img.shields.io/nuget/v/RazorEngineCore.svg?style=flat-square)](https://www.nuget.org/packages/RazorEngineCore) +[![Gitter](https://img.shields.io/gitter/room/RazorEngineCore/community?style=flat-square)](https://gitter.im/RazorEngineCore/community?utm_source=badge&utm_medium=badge&utm_content=badge) + + + +Every single star makes maintainer happy! ⭐ + +## NuGet +``` +Install-Package RazorEngineCore +``` + +## Articles +* [CodeProject: Building String Razor Template Engine with Bare Hands](https://www.codeproject.com/Articles/5260233/Building-String-Razor-Template-Engine-with-Bare-Ha) + +## Wiki +* [Strongly typed model](https://github.com/adoconnection/RazorEngineCore/wiki/Strongly-typed-model) +* [@Include and @Layout](https://github.com/adoconnection/RazorEngineCore/wiki/@Include-and-@Layout) +* [@Raw](https://github.com/adoconnection/RazorEngineCore/wiki/@Raw) +* [@Inject and referencing other assemblies](https://github.com/adoconnection/RazorEngineCore/wiki/@Inject-and-referencing-other-assemblies) +* [Switch from RazorEngine cshtml templates](https://github.com/adoconnection/RazorEngineCore/wiki/Switch-from-RazorEngine-cshtml-templates) +* [Azure Functions FileNotFoundException workaround](https://github.com/adoconnection/RazorEngineCore/wiki/Azure-Functions-FileNotFoundException-workaround) +* [@Html implementation example](https://github.com/adoconnection/RazorEngineCore/wiki/@Html-implementation-example) + +## Extensions +* [wdcossey/RazorEngineCore.Extensions](https://github.com/wdcossey/RazorEngineCore.Extensions) + +## Examples + +#### Basic usage +```cs +IRazorEngine razorEngine = new RazorEngine(); +IRazorEngineCompiledTemplate template = razorEngine.Compile("Hello @Model.Name"); + +string result = template.Run(new +{ + Name = "Alexander" +}); + +Console.WriteLine(result); +``` + +#### Strongly typed model +```cs +IRazorEngine razorEngine = new RazorEngine(); +string templateText = "Hello @Model.Name"; + +// yeah, heavy definition +IRazorEngineCompiledTemplate> template = razorEngine.Compile>(templateText); + +string result = template.Run(instance => +{ + instance.Model = new TestModel() + { + Name = "Hello", + Items = new[] {3, 1, 2} + }; +}); + +Console.WriteLine(result); +``` + +#### Save / Load compiled templates +Most expensive task is to compile template, you should not compile template every time you need to run it +```cs +IRazorEngine razorEngine = new RazorEngine(); +IRazorEngineCompiledTemplate template = razorEngine.Compile("Hello @Model.Name"); + +// save to file +template.SaveToFile("myTemplate.dll"); + +//save to stream +MemoryStream memoryStream = new MemoryStream(); +template.SaveToStream(memoryStream); +``` + +```cs +IRazorEngineCompiledTemplate template1 = RazorEngineCompiledTemplate.LoadFromFile("myTemplate.dll"); +IRazorEngineCompiledTemplate template2 = RazorEngineCompiledTemplate.LoadFromStream(myStream); +``` + +```cs +IRazorEngineCompiledTemplate template1 = RazorEngineCompiledTemplate.LoadFromFile("myTemplate.dll"); +IRazorEngineCompiledTemplate template2 = RazorEngineCompiledTemplate.LoadFromStream(myStream); +``` + +#### Caching +RazorEngineCore is not responsible for caching. Each team and project has their own caching frameworks and conventions therefore making it impossible to have builtin solution for all possible needs. + +If you dont have one, use following static ConcurrentDictionary example as a simplest thread safe solution. + +```cs +private static ConcurrentDictionary TemplateCache = new ConcurrentDictionary(); +``` + +```cs +private string RenderTemplate(string template, object model) +{ + int hashCode = template.GetHashCode(); + + IRazorEngineCompiledTemplate compiledTemplate = TemplateCache.GetOrAdd(hashCode, i => + { + RazorEngine razorEngine = new RazorEngine(); + return razorEngine.Compile(Content); + }); + + return compiledTemplate.Run(model); +} +``` + +#### Template functions +ASP.NET Core way of defining template functions: +``` + + @{ RecursionTest(3); } + + +@{ + void RecursionTest(int level) + { + if (level <= 0) + { + return; + } + +
LEVEL: @level
+ @{ RecursionTest(level - 1); } + } +} +``` +output: +``` +
LEVEL: 3
+
LEVEL: 2
+
LEVEL: 1
+``` + +#### Helpers and custom members +```cs +string content = @"Hello @A, @B, @Decorator(123)"; + +IRazorEngine razorEngine = new RazorEngine(); +IRazorEngineCompiledTemplate template = razorEngine.Compile(content); + +string result = template.Run(instance => +{ + instance.A = 10; + instance.B = "Alex"; +}); + +Console.WriteLine(result); +``` +```cs +public class CustomModel : RazorEngineTemplateBase +{ + public int A { get; set; } + public string B { get; set; } + + public string Decorator(object value) + { + return "-=" + value + "=-"; + } +} +``` + +#### Referencing assemblies +Keep your templates as simple as possible, if you need to inject "unusual" assemblies most likely you are doing it wrong. +Writing `@using System.IO` in template will not reference System.IO assembly, use builder to manually reference it. + +```cs +IRazorEngine razorEngine = new RazorEngine(); +IRazorEngineCompiledTemplate compiledTemplate = razorEngine.Compile(templateText, builder => +{ + builder.AddAssemblyReferenceByName("System.Security"); // by name + builder.AddAssemblyReference(typeof(System.IO.File)); // by type + builder.AddAssemblyReference(Assembly.Load("source")); // by reference +}); + +string result = compiledTemplate.Run(new { name = "Hello" }); +``` + + +#### Credits +This package is inspired by [Simon Mourier SO post](https://stackoverflow.com/a/47756437/267736) + + +#### Changelog +* 2021.3.1 + * fixed NET5 publish as single file (thanks [@jddj007-hydra](https://github.com/jddj007-hydra)) + * AnonymousTypeWrapper array handling fix + * System.Collections referenced by default + * Microsoft.AspNetCore.Razor.Language 3.1.8 -> 5.0.3 + * Microsoft.CodeAnalysis.CSharp 3.7.0 -> 3.8.0 +* 2020.10.1 + * Linux fix for #34 + * Microsoft.AspNetCore.Razor.Language 3.1.5 -> 3.1.8 + * Microsoft.CodeAnalysis.CSharp 3.6.0 -> 3.7.0 +* 2020.9.1 + * .NET 4.7.2 support (thanks [@krmr](https://github.com/krmr)) +* 2020.6.1 + * Reference assemblies by Metadata (thanks [@Merlin04](https://github.com/Merlin04)) + * Expose GeneratedCode in RazorEngineCompilationException + * Microsoft.AspNetCore.Razor.Language 3.1.4 -> 3.1.5 +* 2020.5.2 + * IRazorEngineTemplate interface + * RazorEngineTemplateBase methods go virtual +* 2020.5.1 + * Async methods (thanks [@wdcossey](https://github.com/wdcossey)) + * Microsoft.AspNetCore.Razor.Language 3.1.1 -> 3.1.4 + * Microsoft.CodeAnalysis.CSharp 3.4.0 -> 3.6.0 +* 2020.3.3 + * Model with generic type arguments compiling fix +* 2020.3.2 + * External assembly referencing + * Linq included by default +* 2020.3.1 + * In attribute rendering fix #4 +* 2020.2.4 + * Null values in model correct handling + * Null model fix + * Netstandard2 insted of netcore3.1 +* 2020.2.3 + * Html attribute rendering fix + * Html attribute rendering tests diff --git a/src/Util.Templates.Razor/RazorEngineCore/RazorEngine.cs b/src/Util.Templates.Razor/RazorEngineCore/RazorEngine.cs new file mode 100644 index 000000000..a17b930f9 --- /dev/null +++ b/src/Util.Templates.Razor/RazorEngineCore/RazorEngine.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor.Language; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Emit; +using System.Reflection.Metadata; + +namespace RazorEngineCore; + +public class RazorEngine : IRazorEngine +{ + public IRazorEngineCompiledTemplate Compile(string content, Action builderAction = null) where T : IRazorEngineTemplate + { + IRazorEngineCompilationOptionsBuilder compilationOptionsBuilder = new RazorEngineCompilationOptionsBuilder(); + + compilationOptionsBuilder.AddAssemblyReference(typeof(T).Assembly); + compilationOptionsBuilder.Inherits(typeof(T)); + + builderAction?.Invoke(compilationOptionsBuilder); + + MemoryStream memoryStream = this.CreateAndCompileToStream(content, compilationOptionsBuilder.Options); + + return new RazorEngineCompiledTemplate(memoryStream); + } + + public Task> CompileAsync(string content, Action builderAction = null) where T : IRazorEngineTemplate + { + return Task.Factory.StartNew(() => this.Compile(content: content, builderAction: builderAction)); + } + + public IRazorEngineCompiledTemplate Compile(string content, Action builderAction = null) + { + IRazorEngineCompilationOptionsBuilder compilationOptionsBuilder = new RazorEngineCompilationOptionsBuilder(); + compilationOptionsBuilder.Inherits(typeof(RazorEngineTemplateBase)); + + builderAction?.Invoke(compilationOptionsBuilder); + + MemoryStream memoryStream = this.CreateAndCompileToStream(content, compilationOptionsBuilder.Options); + + return new RazorEngineCompiledTemplate(memoryStream); + } + + public Task CompileAsync(string content, Action builderAction = null) + { + return Task.Factory.StartNew(() => this.Compile(content: content, builderAction: builderAction)); + } + + private MemoryStream CreateAndCompileToStream(string templateSource, RazorEngineCompilationOptions options) + { + templateSource = this.WriteDirectives(templateSource, options); + + RazorProjectEngine engine = RazorProjectEngine.Create( + RazorConfiguration.Default, + RazorProjectFileSystem.Create(@"."), + (builder) => + { + builder.SetNamespace(options.TemplateNamespace); + }); + + string fileName = Path.GetRandomFileName(); + + RazorSourceDocument document = RazorSourceDocument.Create(templateSource, fileName); + + RazorCodeDocument codeDocument = engine.Process( + document, + null, + new List(), + new List()); + + RazorCSharpDocument razorCSharpDocument = codeDocument.GetCSharpDocument(); + + SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(razorCSharpDocument.GeneratedCode); + + CSharpCompilation compilation = CSharpCompilation.Create( + fileName, + new[] + { + syntaxTree + }, + options.ReferencedAssemblies + .Select(ass => + { +#if NETSTANDARD2_0 + return MetadataReference.CreateFromFile(ass.Location); +#else + unsafe + { + ass.TryGetRawMetadata(out byte* blob, out int length); + var moduleMetadata = ModuleMetadata.CreateFromMetadata((IntPtr)blob, length); + var assemblyMetadata = AssemblyMetadata.Create(moduleMetadata); + var metadataReference = assemblyMetadata.GetReference(); + return metadataReference; + } +#endif + }) + .Concat(options.MetadataReferences) + .ToList(), + new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); + + MemoryStream memoryStream = new MemoryStream(); + + EmitResult emitResult = compilation.Emit(memoryStream); + + if (!emitResult.Success) + { + List errors = emitResult.Diagnostics.ToList(); + + RazorEngineCompilationException exception = new RazorEngineCompilationException($"Unable to compile template: {errors.FirstOrDefault()}") + { + Errors = errors, + GeneratedCode = razorCSharpDocument.GeneratedCode + }; + + throw exception; + } + + memoryStream.Position = 0; + + return memoryStream; + } + + private string WriteDirectives(string content, RazorEngineCompilationOptions options) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.AppendLine($"@inherits {options.Inherits}"); + + foreach (string entry in options.DefaultUsings) + { + stringBuilder.AppendLine($"@using {entry}"); + } + + stringBuilder.Append(content); + + return stringBuilder.ToString(); + } +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/RazorEngineCore/RazorEngineCompilationException.cs b/src/Util.Templates.Razor/RazorEngineCore/RazorEngineCompilationException.cs new file mode 100644 index 000000000..56fab6ae5 --- /dev/null +++ b/src/Util.Templates.Razor/RazorEngineCore/RazorEngineCompilationException.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using Microsoft.CodeAnalysis; + +namespace RazorEngineCore; + +public class RazorEngineCompilationException : RazorEngineException +{ + public RazorEngineCompilationException() + { + } + + protected RazorEngineCompilationException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + public RazorEngineCompilationException(string message) : base(message) + { + } + + public RazorEngineCompilationException(string message, Exception innerException) : base(message, innerException) + { + } + + public List Errors { get; set; } + public string GeneratedCode { get; set; } +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/RazorEngineCore/RazorEngineCompilationOptions.cs b/src/Util.Templates.Razor/RazorEngineCore/RazorEngineCompilationOptions.cs new file mode 100644 index 000000000..8f4cf3c5b --- /dev/null +++ b/src/Util.Templates.Razor/RazorEngineCore/RazorEngineCompilationOptions.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using Microsoft.CodeAnalysis; +using System; + +namespace RazorEngineCore; + +public class RazorEngineCompilationOptions +{ + public HashSet ReferencedAssemblies { get; set; } + + public HashSet MetadataReferences { get; set; } = new HashSet(); + public string TemplateNamespace { get; set; } = "TemplateNamespace"; + public string Inherits { get; set; } = "RazorEngineCore.RazorEngineTemplateBase"; + + public HashSet DefaultUsings { get; set; } = new HashSet() + { + "System.Linq", + "System.Collections", + "System.Collections.Generic" + }; + + public RazorEngineCompilationOptions() + { + bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + bool isFullFramework = RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase); + + if (isWindows && isFullFramework) + { + this.ReferencedAssemblies = new HashSet() + { + typeof(object).Assembly, + Assembly.Load(new AssemblyName("Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")), + typeof(RazorEngineTemplateBase).Assembly, + typeof(System.Runtime.GCSettings).Assembly, + typeof(System.Collections.IList).Assembly, + typeof(System.Collections.Generic.IEnumerable<>).Assembly, + typeof(System.Linq.Enumerable).Assembly, + typeof(System.Linq.Expressions.Expression).Assembly, + Assembly.Load(new AssemblyName("netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51")) + }; + } + + if (isWindows && !isFullFramework) // i.e. NETCore + { + this.ReferencedAssemblies = new HashSet() + { + typeof(object).Assembly, + Assembly.Load(new AssemblyName("Microsoft.CSharp")), + typeof(RazorEngineTemplateBase).Assembly, + Assembly.Load(new AssemblyName("System.Runtime")), + typeof(System.Collections.IList).Assembly, + typeof(System.Collections.Generic.IEnumerable<>).Assembly, + Assembly.Load(new AssemblyName("System.Linq")), + Assembly.Load(new AssemblyName("System.Linq.Expressions")), + Assembly.Load(new AssemblyName("netstandard")) + }; + } + + if (!isWindows) + { + this.ReferencedAssemblies = new HashSet() + { + typeof(object).Assembly, + Assembly.Load(new AssemblyName("Microsoft.CSharp")), + typeof(RazorEngineTemplateBase).Assembly, + Assembly.Load(new AssemblyName("System.Runtime")), + typeof(System.Collections.IList).Assembly, + typeof(System.Collections.Generic.IEnumerable<>).Assembly, + Assembly.Load(new AssemblyName("System.Linq")), + Assembly.Load(new AssemblyName("System.Linq.Expressions")), + Assembly.Load(new AssemblyName("netstandard")) + }; + } + } +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/RazorEngineCore/RazorEngineCompilationOptionsBuilder.cs b/src/Util.Templates.Razor/RazorEngineCore/RazorEngineCompilationOptionsBuilder.cs new file mode 100644 index 000000000..1ca524596 --- /dev/null +++ b/src/Util.Templates.Razor/RazorEngineCore/RazorEngineCompilationOptionsBuilder.cs @@ -0,0 +1,70 @@ +using System; +using System.Linq; +using System.Reflection; +using Microsoft.CodeAnalysis; + +namespace RazorEngineCore; + +public class RazorEngineCompilationOptionsBuilder : IRazorEngineCompilationOptionsBuilder +{ + public RazorEngineCompilationOptions Options { get; set; } + + public RazorEngineCompilationOptionsBuilder(RazorEngineCompilationOptions options = null) + { + this.Options = options ?? new RazorEngineCompilationOptions(); + } + + public void AddAssemblyReferenceByName(string assemblyName) + { + Assembly assembly = Assembly.Load(new AssemblyName(assemblyName)); + this.AddAssemblyReference(assembly); + } + + public void AddAssemblyReference(Assembly assembly) + { + this.Options.ReferencedAssemblies.Add(assembly); + } + + public void AddAssemblyReference(Type type) + { + this.AddAssemblyReference(type.Assembly); + + foreach (Type argumentType in type.GenericTypeArguments) + { + this.AddAssemblyReference(argumentType); + } + } + + public void AddMetadataReference(MetadataReference reference) + { + this.Options.MetadataReferences.Add(reference); + } + + public void AddUsing(string namespaceName) + { + this.Options.DefaultUsings.Add(namespaceName); + } + + public void Inherits(Type type) + { + this.Options.Inherits = this.RenderTypeName(type); + this.AddAssemblyReference(type); + } + + private string RenderTypeName(Type type) + { + string result = type.Namespace + "." + type.Name; + + if (result.Contains('`')) + { + result = result.Substring(0, result.IndexOf("`")); + } + + if (type.GenericTypeArguments.Length == 0) + { + return result; + } + + return result + "<" + string.Join(",", type.GenericTypeArguments.Select(this.RenderTypeName)) + ">"; + } +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/RazorEngineCore/RazorEngineCompiledTemplate.cs b/src/Util.Templates.Razor/RazorEngineCore/RazorEngineCompiledTemplate.cs new file mode 100644 index 000000000..ea8b672bf --- /dev/null +++ b/src/Util.Templates.Razor/RazorEngineCore/RazorEngineCompiledTemplate.cs @@ -0,0 +1,109 @@ +using System; +using System.IO; +using System.Reflection; +using System.Threading.Tasks; +using Util.Templates.Razor; +using Util.Templates.Razor.Helpers; + +namespace RazorEngineCore; + +public class RazorEngineCompiledTemplate : IRazorEngineCompiledTemplate +{ + private readonly MemoryStream assemblyByteCode; + private readonly Type templateType; + + internal RazorEngineCompiledTemplate(MemoryStream assemblyByteCode) + { + this.assemblyByteCode = assemblyByteCode; + + Assembly assembly = Assembly.Load(assemblyByteCode.ToArray()); + this.templateType = assembly.GetType("TemplateNamespace.Template"); + } + + public static IRazorEngineCompiledTemplate LoadFromFile(string fileName) + { + return LoadFromFileAsync(fileName: fileName).GetAwaiter().GetResult(); + } + + public static async Task LoadFromFileAsync(string fileName) + { + MemoryStream memoryStream = new MemoryStream(); + + using (FileStream fileStream = new FileStream( + path: fileName, + mode: FileMode.Open, + access: FileAccess.Read, + share: FileShare.None, + bufferSize: 4096, + useAsync: true)) + { + await fileStream.CopyToAsync(memoryStream); + } + + return new RazorEngineCompiledTemplate(memoryStream); + } + + public static IRazorEngineCompiledTemplate LoadFromStream(Stream stream) + { + return LoadFromStreamAsync(stream).GetAwaiter().GetResult(); + } + + public static async Task LoadFromStreamAsync(Stream stream) + { + MemoryStream memoryStream = new MemoryStream(); + await stream.CopyToAsync(memoryStream); + memoryStream.Position = 0; + + return new RazorEngineCompiledTemplate(memoryStream); + } + + public void SaveToStream(Stream stream) + { + this.SaveToStreamAsync(stream).GetAwaiter().GetResult(); + } + + public Task SaveToStreamAsync(Stream stream) + { + return this.assemblyByteCode.CopyToAsync(stream); + } + + public void SaveToFile(string fileName) + { + this.SaveToFileAsync(fileName).GetAwaiter().GetResult(); + } + + public Task SaveToFileAsync(string fileName) + { + using (FileStream fileStream = new FileStream( + path: fileName, + mode: FileMode.OpenOrCreate, + access: FileAccess.Write, + share: FileShare.None, + bufferSize: 4096, + useAsync: true)) + { + return assemblyByteCode.CopyToAsync(fileStream); + } + } + + public string Run(object model = null) + { + return this.RunAsync(model).GetAwaiter().GetResult(); + } + + public async Task RunAsync(object model = null) + { + if (model != null && model.IsAnonymous()) + { + model = new AnonymousTypeWrapper(model); + } + + IRazorEngineTemplate instance = (IRazorEngineTemplate) Activator.CreateInstance(this.templateType); + instance.Model = model; + instance.Html = new HtmlHelper( new RazorTemplateEngine( new RazorEngine() ) ); + + await instance.ExecuteAsync(); + + return instance.Result(); + } +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/RazorEngineCore/RazorEngineCompiledTemplateT.cs b/src/Util.Templates.Razor/RazorEngineCore/RazorEngineCompiledTemplateT.cs new file mode 100644 index 000000000..1245293d1 --- /dev/null +++ b/src/Util.Templates.Razor/RazorEngineCore/RazorEngineCompiledTemplateT.cs @@ -0,0 +1,101 @@ +using System; +using System.IO; +using System.Reflection; +using System.Threading.Tasks; + +namespace RazorEngineCore; + +public class RazorEngineCompiledTemplate : IRazorEngineCompiledTemplate where T : IRazorEngineTemplate +{ + private readonly MemoryStream assemblyByteCode; + private readonly Type templateType; + + internal RazorEngineCompiledTemplate(MemoryStream assemblyByteCode) + { + this.assemblyByteCode = assemblyByteCode; + + Assembly assembly = Assembly.Load(assemblyByteCode.ToArray()); + this.templateType = assembly.GetType("TemplateNamespace.Template"); + } + + public static IRazorEngineCompiledTemplate LoadFromFile(string fileName) + { + return LoadFromFileAsync(fileName: fileName).GetAwaiter().GetResult(); + } + + public static async Task> LoadFromFileAsync(string fileName) + { + MemoryStream memoryStream = new MemoryStream(); + + using (FileStream fileStream = new FileStream( + path: fileName, + mode: FileMode.Open, + access: FileAccess.Read, + share: FileShare.None, + bufferSize: 4096, + useAsync: true)) + { + await fileStream.CopyToAsync(memoryStream); + } + + return new RazorEngineCompiledTemplate(memoryStream); + } + + public static IRazorEngineCompiledTemplate LoadFromStream(Stream stream) + { + return LoadFromStreamAsync(stream).GetAwaiter().GetResult(); + } + + public static async Task> LoadFromStreamAsync(Stream stream) + { + MemoryStream memoryStream = new MemoryStream(); + await stream.CopyToAsync(memoryStream); + memoryStream.Position = 0; + + return new RazorEngineCompiledTemplate(memoryStream); + } + + public void SaveToStream(Stream stream) + { + this.SaveToStreamAsync(stream).GetAwaiter().GetResult(); + } + + public Task SaveToStreamAsync(Stream stream) + { + return this.assemblyByteCode.CopyToAsync(stream); + } + + public void SaveToFile(string fileName) + { + this.SaveToFileAsync(fileName).GetAwaiter().GetResult(); + } + + public Task SaveToFileAsync(string fileName) + { + using (FileStream fileStream = new FileStream( + path: fileName, + mode: FileMode.CreateNew, + access: FileAccess.Write, + share: FileShare.None, + bufferSize: 4096, + useAsync: true)) + { + return assemblyByteCode.CopyToAsync(fileStream); + } + } + + public string Run(Action initializer) + { + return this.RunAsync(initializer).GetAwaiter().GetResult(); + } + + public async Task RunAsync(Action initializer) + { + T instance = (T) Activator.CreateInstance(this.templateType); + initializer(instance); + + await instance.ExecuteAsync(); + + return instance.Result(); + } +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/RazorEngineCore/RazorEngineException.cs b/src/Util.Templates.Razor/RazorEngineCore/RazorEngineException.cs new file mode 100644 index 000000000..13b0f2c50 --- /dev/null +++ b/src/Util.Templates.Razor/RazorEngineCore/RazorEngineException.cs @@ -0,0 +1,23 @@ +using System; +using System.Runtime.Serialization; + +namespace RazorEngineCore; + +public class RazorEngineException : Exception +{ + public RazorEngineException() + { + } + + protected RazorEngineException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + public RazorEngineException(string message) : base(message) + { + } + + public RazorEngineException(string message, Exception innerException) : base(message, innerException) + { + } +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/RazorEngineCore/RazorEngineTemplateBase.cs b/src/Util.Templates.Razor/RazorEngineCore/RazorEngineTemplateBase.cs new file mode 100644 index 000000000..0aedbce72 --- /dev/null +++ b/src/Util.Templates.Razor/RazorEngineCore/RazorEngineTemplateBase.cs @@ -0,0 +1,54 @@ +using System.Text; +using System.Threading.Tasks; +using Util.Templates.Razor.Helpers; + +namespace RazorEngineCore; + +public abstract class RazorEngineTemplateBase : IRazorEngineTemplate +{ + private readonly StringBuilder stringBuilder = new StringBuilder(); + + private string attributeSuffix = null; + + public dynamic Model { get; set; } + + public HtmlHelper Html { get; set; } + + public virtual void WriteLiteral(string literal = null) + { + this.stringBuilder.Append(literal); + } + + public virtual void Write(object obj = null) + { + this.stringBuilder.Append(obj); + } + + public virtual void BeginWriteAttribute(string name, string prefix, int prefixOffset, string suffix, int suffixOffset, int attributeValuesCount) + { + this.attributeSuffix = suffix; + this.stringBuilder.Append(prefix); + } + + public virtual void WriteAttributeValue(string prefix, int prefixOffset, object value, int valueOffset, int valueLength, bool isLiteral) + { + this.stringBuilder.Append(prefix); + this.stringBuilder.Append(value); + } + + public virtual void EndWriteAttribute() + { + this.stringBuilder.Append(this.attributeSuffix); + this.attributeSuffix = null; + } + + public virtual Task ExecuteAsync() + { + return Task.CompletedTask; + } + + public virtual string Result() + { + return this.stringBuilder.ToString(); + } +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/RazorEngineCore/RazorEngineTemplateBaseT.cs b/src/Util.Templates.Razor/RazorEngineCore/RazorEngineTemplateBaseT.cs new file mode 100644 index 000000000..ffe473c69 --- /dev/null +++ b/src/Util.Templates.Razor/RazorEngineCore/RazorEngineTemplateBaseT.cs @@ -0,0 +1,6 @@ +namespace RazorEngineCore; + +public abstract class RazorEngineTemplateBase : RazorEngineTemplateBase +{ + public new T Model { get; set; } +} \ No newline at end of file diff --git a/src/Util.Templates.Razor/Usings.cs b/src/Util.Templates.Razor/Usings.cs new file mode 100644 index 000000000..536414893 --- /dev/null +++ b/src/Util.Templates.Razor/Usings.cs @@ -0,0 +1,11 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Text; +global using System.IO; +global using System.Reflection; +global using System.Runtime.InteropServices; +global using System.Collections.Concurrent; +global using Microsoft.Extensions.DependencyInjection; +global using RazorEngineCore; \ No newline at end of file diff --git a/src/Util.Templates/01-Util.Templates.csproj b/src/Util.Templates/01-Util.Templates.csproj new file mode 100644 index 000000000..ea61fca32 --- /dev/null +++ b/src/Util.Templates/01-Util.Templates.csproj @@ -0,0 +1,33 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Templates + Util.Templates + Util.Templates是Util应用框架模板操作基础类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Templates.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Templates.xml + + + + + True + False + + + + + + + + + diff --git a/src/Util.Templates/ITemplateEngine.cs b/src/Util.Templates/ITemplateEngine.cs new file mode 100644 index 000000000..9a7757988 --- /dev/null +++ b/src/Util.Templates/ITemplateEngine.cs @@ -0,0 +1,19 @@ +namespace Util.Templates; + +/// +/// 模板引擎 +/// +public interface ITemplateEngine { + /// + /// 渲染模板 + /// + /// 模板字符串 + /// 模板数据 + string Render( string template, object data = null ); + /// + /// 渲染模板 + /// + /// 模板字符串 + /// 模板数据 + Task RenderAsync( string template, object data = null ); +} \ No newline at end of file diff --git a/src/Util.Templates/ITemplateFilter.cs b/src/Util.Templates/ITemplateFilter.cs new file mode 100644 index 000000000..01d84a52b --- /dev/null +++ b/src/Util.Templates/ITemplateFilter.cs @@ -0,0 +1,12 @@ +namespace Util.Templates; + +/// +/// 模板过滤器 +/// +public interface ITemplateFilter { + /// + /// 过滤模板 + /// + /// 模板字符串 + string Filter( string template ); +} \ No newline at end of file diff --git a/src/Util.Templates/TemplateEngineExtensions.cs b/src/Util.Templates/TemplateEngineExtensions.cs new file mode 100644 index 000000000..6fbe0b4b0 --- /dev/null +++ b/src/Util.Templates/TemplateEngineExtensions.cs @@ -0,0 +1,88 @@ +namespace Util.Templates; + +/// +/// 模板引擎扩展 +/// +public static class TemplateEngineExtensions { + /// + /// 通过路径渲染模板 + /// + /// 模板引擎 + /// 模板文件绝对路径 + /// 模板数据 + public static string RenderByPath( this ITemplateEngine templateEngine, string filePath, object data = null ) { + if( templateEngine == null ) + throw new ArgumentNullException( nameof( templateEngine ) ); + var template = Util.Helpers.File.ReadToString( filePath ); + return templateEngine.Render( template, data ); + } + + /// + /// 通过路径渲染模板 + /// + /// 模板引擎 + /// 模板文件绝对路径 + /// 模板数据 + public static async Task RenderByPathAsync( this ITemplateEngine templateEngine, string filePath, object data = null ) { + if( templateEngine == null ) + throw new ArgumentNullException( nameof( templateEngine ) ); + var template = await Util.Helpers.File.ReadToStringAsync( filePath ); + return await templateEngine.RenderAsync( template, data ); + } + + /// + /// 渲染模板并保存到文件 + /// + /// 模板引擎 + /// 模板字符串 + /// 模板数据 + /// 目标文件绝对路径 + public static void Save( this ITemplateEngine templateEngine, string template, object data,string filePath ) { + if( templateEngine == null ) + throw new ArgumentNullException( nameof( templateEngine ) ); + var result = templateEngine.Render( template, data ); + Util.Helpers.File.Write( filePath, result ); + } + + /// + /// 渲染模板并保存到文件 + /// + /// 模板引擎 + /// 模板字符串 + /// 模板数据 + /// 目标文件绝对路径 + public static async Task SaveAsync( this ITemplateEngine templateEngine, string template, object data, string filePath ) { + if( templateEngine == null ) + throw new ArgumentNullException( nameof( templateEngine ) ); + var result = await templateEngine.RenderAsync( template, data ); + await Util.Helpers.File.WriteAsync( filePath, result ); + } + + /// + /// 通过路径渲染模板并保存到文件 + /// + /// 模板引擎 + /// 模板文件绝对路径 + /// 模板数据 + /// 目标文件绝对路径 + public static void SaveByPath( this ITemplateEngine templateEngine, string templatePath, object data, string filePath ) { + if( templateEngine == null ) + throw new ArgumentNullException( nameof( templateEngine ) ); + var result = templateEngine.RenderByPath( templatePath, data ); + Util.Helpers.File.Write( filePath, result ); + } + + /// + /// 渲染模板并保存到文件 + /// + /// 模板引擎 + /// 模板文件绝对路径 + /// 模板数据 + /// 目标文件绝对路径 + public static async Task SaveByPathAsync( this ITemplateEngine templateEngine, string templatePath, object data, string filePath ) { + if( templateEngine == null ) + throw new ArgumentNullException( nameof( templateEngine ) ); + var result = await templateEngine.RenderByPathAsync( templatePath, data ); + await Util.Helpers.File.WriteAsync( filePath, result ); + } +} \ No newline at end of file diff --git a/src/Util.Templates/Usings.cs b/src/Util.Templates/Usings.cs new file mode 100644 index 000000000..aa3cc77fe --- /dev/null +++ b/src/Util.Templates/Usings.cs @@ -0,0 +1,2 @@ +global using System; +global using System.Threading.Tasks; \ No newline at end of file diff --git a/src/Util.Tenants.Abstractions/01-Util.Tenants.Abstractions.csproj b/src/Util.Tenants.Abstractions/01-Util.Tenants.Abstractions.csproj new file mode 100644 index 000000000..4de393cfc --- /dev/null +++ b/src/Util.Tenants.Abstractions/01-Util.Tenants.Abstractions.csproj @@ -0,0 +1,34 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Tenants.Abstractions + Util.Tenants + Util.Tenants.Abstractions是Util应用框架多租户接口定义类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Tenants.Abstractions.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Tenants.Abstractions.xml + + + + + True + False + + + + + + + + + + diff --git a/src/Util.Tenants.Abstractions/IDomainTenantResolver.cs b/src/Util.Tenants.Abstractions/IDomainTenantResolver.cs new file mode 100644 index 000000000..701934b69 --- /dev/null +++ b/src/Util.Tenants.Abstractions/IDomainTenantResolver.cs @@ -0,0 +1,12 @@ +namespace Util.Tenants; + +/// +/// 域名租户解析器 +/// +public interface IDomainTenantResolver : ITransientDependency { + /// + /// 解析租户标识 + /// + /// 域名,范例: a.test.com + Task ResolveTenantIdAsync( string host ); +} \ No newline at end of file diff --git a/src/Util.Tenants.Abstractions/ISwitchTenantManager.cs b/src/Util.Tenants.Abstractions/ISwitchTenantManager.cs new file mode 100644 index 000000000..ae37c3e09 --- /dev/null +++ b/src/Util.Tenants.Abstractions/ISwitchTenantManager.cs @@ -0,0 +1,20 @@ +namespace Util.Tenants; + +/// +/// 切换租户管理器 +/// +public interface ISwitchTenantManager : ITransientDependency { + /// + /// 获取切换的租户标识 + /// + string GetSwitchTenantId(); + /// + /// 切换租户标识 + /// + /// 租户标识 + Task SwitchTenantAsync( string tenantId ); + /// + /// 重置租户标识 + /// + Task ResetTenantAsync(); +} \ No newline at end of file diff --git a/src/Util.Tenants.Abstractions/ITenant.cs b/src/Util.Tenants.Abstractions/ITenant.cs new file mode 100644 index 000000000..38e4b5ca4 --- /dev/null +++ b/src/Util.Tenants.Abstractions/ITenant.cs @@ -0,0 +1,11 @@ +namespace Util.Tenants; + +/// +/// 租户 +/// +public interface ITenant { + /// + /// 租户标识 + /// + string TenantId { get; set; } +} \ No newline at end of file diff --git a/src/Util.Tenants.Abstractions/ITenantManager.cs b/src/Util.Tenants.Abstractions/ITenantManager.cs new file mode 100644 index 000000000..88220611f --- /dev/null +++ b/src/Util.Tenants.Abstractions/ITenantManager.cs @@ -0,0 +1,31 @@ +namespace Util.Tenants; + +/// +/// 租户管理器 +/// +public interface ITenantManager : IScopeDependency { + /// + /// 是否启用多租户架构 + /// + bool Enabled(); + /// + /// 是否允许使用多数据库 + /// + bool AllowMultipleDatabase(); + /// + /// 是否平台供应商 + /// + bool IsHost(); + /// + /// 获取当前租户标识 + /// + string GetTenantId(); + /// + /// 获取当前租户配置 + /// + TenantInfo GetTenant(); + /// + /// 是否禁用租户过滤器 + /// + bool IsDisableTenantFilter(); +} \ No newline at end of file diff --git a/src/Util.Tenants.Abstractions/ITenantStore.cs b/src/Util.Tenants.Abstractions/ITenantStore.cs new file mode 100644 index 000000000..7442d1744 --- /dev/null +++ b/src/Util.Tenants.Abstractions/ITenantStore.cs @@ -0,0 +1,12 @@ +namespace Util.Tenants; + +/// +/// 租户存储器 +/// +public interface ITenantStore : IScopeDependency { + /// + /// 获取当前租户配置 + /// + /// 租户标识 + TenantInfo GetTenant( string tenantId ); +} \ No newline at end of file diff --git a/src/Util.Tenants.Abstractions/IViewAllTenantManager.cs b/src/Util.Tenants.Abstractions/IViewAllTenantManager.cs new file mode 100644 index 000000000..b4ebd6132 --- /dev/null +++ b/src/Util.Tenants.Abstractions/IViewAllTenantManager.cs @@ -0,0 +1,19 @@ +namespace Util.Tenants; + +/// +/// 查看租户管理器 +/// +public interface IViewAllTenantManager : ITransientDependency { + /// + /// 是否禁用租户过滤器 + /// + bool IsDisableTenantFilter(); + /// + /// 启用查看所有租户数据,仅对单一数据库模式有效 + /// + Task EnableViewAllAsync(); + /// + /// 禁用查看所有租户数据,仅对单一数据库模式有效 + /// + Task DisableViewAllAsync(); +} \ No newline at end of file diff --git a/src/Util.Tenants.Abstractions/NullTenantManager.cs b/src/Util.Tenants.Abstractions/NullTenantManager.cs new file mode 100644 index 000000000..35983690f --- /dev/null +++ b/src/Util.Tenants.Abstractions/NullTenantManager.cs @@ -0,0 +1,42 @@ +namespace Util.Tenants; + +/// +/// 空租户管理器 +/// +[Ioc( -9 )] +public class NullTenantManager : ITenantManager { + /// + /// 租户管理器空实例 + /// + public static readonly ITenantManager Instance = new NullTenantManager(); + + /// + public bool Enabled() { + return false; + } + + /// + public bool AllowMultipleDatabase() { + return false; + } + + /// + public bool IsHost() { + return false; + } + + /// + public bool IsDisableTenantFilter() { + return false; + } + + /// + public string GetTenantId() { + return null; + } + + /// + public TenantInfo GetTenant() { + return null; + } +} \ No newline at end of file diff --git a/src/Util.Tenants.Abstractions/NullTenantStore.cs b/src/Util.Tenants.Abstractions/NullTenantStore.cs new file mode 100644 index 000000000..61372cbc0 --- /dev/null +++ b/src/Util.Tenants.Abstractions/NullTenantStore.cs @@ -0,0 +1,20 @@ +namespace Util.Tenants; + +/// +/// 空租户存储器 +/// +[Ioc(-9)] +public class NullTenantStore : ITenantStore { + /// + /// 空租户存储器 + /// + public static readonly ITenantStore Instance = new NullTenantStore(); + + /// + /// 获取当前租户配置 + /// + /// 租户标识 + public TenantInfo GetTenant( string tenantId ) { + return null; + } +} \ No newline at end of file diff --git a/src/Util.Tenants.Abstractions/TenantInfo.cs b/src/Util.Tenants.Abstractions/TenantInfo.cs new file mode 100644 index 000000000..46c00811a --- /dev/null +++ b/src/Util.Tenants.Abstractions/TenantInfo.cs @@ -0,0 +1,17 @@ +using Util.Data; + +namespace Util.Tenants; + +/// +/// 租户信息 +/// +public class TenantInfo { + /// + /// 租户标识 + /// + public string TenantId { get; set; } + /// + /// 连接字符串集合 + /// + public ConnectionStringCollection ConnectionStrings { get; set; } +} \ No newline at end of file diff --git a/src/Util.Tenants.Abstractions/Usings.cs b/src/Util.Tenants.Abstractions/Usings.cs new file mode 100644 index 000000000..5c84008de --- /dev/null +++ b/src/Util.Tenants.Abstractions/Usings.cs @@ -0,0 +1,10 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using System.Text; +global using System.Reflection; +global using System.Linq.Expressions; +global using System.ComponentModel.DataAnnotations; +global using Util.Dependency; \ No newline at end of file diff --git a/src/Util.Tenants/02-Util.Tenants.csproj b/src/Util.Tenants/02-Util.Tenants.csproj new file mode 100644 index 000000000..2f238715f --- /dev/null +++ b/src/Util.Tenants/02-Util.Tenants.csproj @@ -0,0 +1,34 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Tenants + Util.Tenants + Util.Tenants是Util应用框架多租户实现类库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Tenants.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Tenants.xml + + + + + True + False + + + + + + + + + + diff --git a/src/Util.Tenants/AppBuilderExtensions.cs b/src/Util.Tenants/AppBuilderExtensions.cs new file mode 100644 index 000000000..7567a421a --- /dev/null +++ b/src/Util.Tenants/AppBuilderExtensions.cs @@ -0,0 +1,35 @@ +using Util.Configs; +using Util.Tenants.Resolvers; + +namespace Util.Tenants; + +/// +/// 租户操作扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置租户操作 + /// + /// 应用生成器 + public static IAppBuilder AddTenant( this IAppBuilder builder ) { + return builder.AddTenant( null ); + } + + /// + /// 配置租户操作 + /// + /// 应用生成器 + /// 租户配置操作 + public static IAppBuilder AddTenant( this IAppBuilder builder, Action setupAction ) { + builder.CheckNull( nameof( builder ) ); + var options = new TenantOptions { + IsEnabled = true + }; + setupAction?.Invoke( options ); + builder.Host.ConfigureServices( ( context, services ) => { + services.TryAddSingleton(); + services.TryAddSingleton>( new OptionsWrapper( options ) ); + } ); + return builder; + } +} \ No newline at end of file diff --git a/src/Util.Tenants/ITenantResolver.cs b/src/Util.Tenants/ITenantResolver.cs new file mode 100644 index 000000000..afa9b42a3 --- /dev/null +++ b/src/Util.Tenants/ITenantResolver.cs @@ -0,0 +1,16 @@ +namespace Util.Tenants; + +/// +/// 租户解析器 +/// +public interface ITenantResolver { + /// + /// 解析租户标识 + /// + /// Http上下文 + Task ResolveAsync( HttpContext context ); + /// + /// 优先级,值越大则优先解析 + /// + int Priority { get; set; } +} \ No newline at end of file diff --git a/src/Util.Tenants/Managements/SwitchTenantManager.cs b/src/Util.Tenants/Managements/SwitchTenantManager.cs new file mode 100644 index 000000000..2fb660057 --- /dev/null +++ b/src/Util.Tenants/Managements/SwitchTenantManager.cs @@ -0,0 +1,30 @@ +using Util.Helpers; + +namespace Util.Tenants.Managements; + +/// +/// 切换租户管理器 +/// +public class SwitchTenantManager : ISwitchTenantManager { + /// + /// 切换租户键名 + /// + public const string Key = "x-switch-tenant"; + + /// + public string GetSwitchTenantId() { + return Web.GetCookie( Key ); + } + + /// + public Task SwitchTenantAsync( string tenantId ) { + Web.SetCookie( Key, tenantId ); + return Task.CompletedTask; + } + + /// + public Task ResetTenantAsync() { + Web.RemoveCookie( Key ); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/Util.Tenants/Managements/TenantManager.cs b/src/Util.Tenants/Managements/TenantManager.cs new file mode 100644 index 000000000..d787f6764 --- /dev/null +++ b/src/Util.Tenants/Managements/TenantManager.cs @@ -0,0 +1,105 @@ +namespace Util.Tenants.Managements; + +/// +/// 租户管理器 +/// +public class TenantManager : ITenantManager { + /// + /// 当前租户标识 + /// + private static readonly AsyncLocal _currentTenantId; + + /// + /// 初始化租户管理器 + /// + static TenantManager() { + _currentTenantId = new AsyncLocal(); + } + + /// + /// 初始化租户管理器 + /// + /// 租户存储器 + /// 查看租户管理器 + /// 切换租户管理器 + /// 用户会话 + /// 租户配置 + public TenantManager( ITenantStore tenantStore, IViewAllTenantManager viewAllTenantManager, ISwitchTenantManager switchTenantManager, + Sessions.ISession session, IOptions options ) { + TenantStore = tenantStore ?? throw new ArgumentNullException( nameof( tenantStore ) ); + ViewAllTenantManager = viewAllTenantManager ?? throw new ArgumentNullException( nameof( viewAllTenantManager ) ); + SwitchTenantManager = switchTenantManager ?? throw new ArgumentNullException( nameof( switchTenantManager ) ); + Session = session ?? throw new ArgumentNullException( nameof( session ) ); + Options = options?.Value ?? TenantOptions.Null; + } + + /// + /// 租户存储器 + /// + protected ITenantStore TenantStore { get; } + /// + /// 查看租户管理器 + /// + protected IViewAllTenantManager ViewAllTenantManager { get; } + /// + /// 切换租户管理器 + /// + protected ISwitchTenantManager SwitchTenantManager { get; } + /// + /// 用户会话 + /// + protected Sessions.ISession Session { get; } + /// + /// 租户配置 + /// + protected TenantOptions Options { get; } + + /// + /// 当前租户标识 + /// + public static string CurrentTenantId { + get => _currentTenantId.Value; + set => _currentTenantId.Value = value; + } + + /// + public virtual bool Enabled() { + return Options.IsEnabled; + } + + /// + public virtual bool AllowMultipleDatabase() { + return Options.IsAllowMultipleDatabase; + } + + /// + public virtual bool IsHost() { + if ( Session.IsAuthenticated == false ) + return false; + if ( Session.UserId.IsEmpty() ) + return false; + if ( Session.TenantId != CurrentTenantId ) + return false; + return Session.TenantId == Options.DefaultTenantId; + } + + /// + public virtual string GetTenantId() { + if ( IsHost() == false ) + return CurrentTenantId; + var switchTenantId = SwitchTenantManager.GetSwitchTenantId(); + return switchTenantId.IsEmpty() ? CurrentTenantId : switchTenantId; + } + + /// + public virtual TenantInfo GetTenant() { + return TenantStore.GetTenant( GetTenantId() ); + } + + /// + public virtual bool IsDisableTenantFilter() { + if ( IsHost() == false ) + return false; + return ViewAllTenantManager.IsDisableTenantFilter(); + } +} \ No newline at end of file diff --git a/src/Util.Tenants/Managements/ViewAllTenantManager.cs b/src/Util.Tenants/Managements/ViewAllTenantManager.cs new file mode 100644 index 000000000..ad382e662 --- /dev/null +++ b/src/Util.Tenants/Managements/ViewAllTenantManager.cs @@ -0,0 +1,33 @@ +using Util.Helpers; + +namespace Util.Tenants.Managements; + +/// +/// 查看租户管理器 +/// +public class ViewAllTenantManager : IViewAllTenantManager { + /// + /// 查看所有租户键名 + /// + public const string Key = "x-view-all-tenant"; + + /// + public bool IsDisableTenantFilter() { + var result = Web.GetCookie( Key ); + if ( result.IsEmpty() ) + return false; + return result.ToBool(); + } + + /// + public Task EnableViewAllAsync() { + Web.SetCookie( Key,"true" ); + return Task.CompletedTask; + } + + /// + public Task DisableViewAllAsync() { + Web.RemoveCookie( Key ); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/Util.Tenants/Middlewares/TenantMiddleware.cs b/src/Util.Tenants/Middlewares/TenantMiddleware.cs new file mode 100644 index 000000000..6cf3e0525 --- /dev/null +++ b/src/Util.Tenants/Middlewares/TenantMiddleware.cs @@ -0,0 +1,45 @@ +using Util.Tenants.Managements; + +namespace Util.Tenants.Middlewares; + +/// +/// 租户处理中间件 +/// +public class TenantMiddleware { + /// + /// 中间件管道 + /// + private readonly RequestDelegate _next; + + /// + /// 初始化租户处理中间件 + /// + /// 中间件管道 + public TenantMiddleware( RequestDelegate next ) { + _next = next; + } + + /// + /// 执行管道 + /// + /// Http上下文 + public async Task InvokeAsync( HttpContext httpContext ) { + if ( _next == null ) + return; + if ( httpContext == null ) { + await _next( httpContext ); + return; + } + var log = httpContext.RequestServices.GetService>() ?? NullLogger.Instance; + var resolver = httpContext.RequestServices.GetRequiredService(); + try { + var tenantId = await resolver.ResolveAsync( httpContext ); + TenantManager.CurrentTenantId = tenantId; + await _next( httpContext ); + } + catch ( Exception exception ) { + log.LogError( exception, "租户处理中间件发生异常" ); + throw; + } + } +} \ No newline at end of file diff --git a/src/Util.Tenants/Resolvers/CookieTenantResolver.cs b/src/Util.Tenants/Resolvers/CookieTenantResolver.cs new file mode 100644 index 000000000..532abe9a2 --- /dev/null +++ b/src/Util.Tenants/Resolvers/CookieTenantResolver.cs @@ -0,0 +1,16 @@ +namespace Util.Tenants.Resolvers; + +/// +/// 基于Cookie的租户解析器 +/// +public class CookieTenantResolver : TenantResolverBase { + /// + /// 解析租户标识 + /// + protected override Task Resolve( HttpContext context ) { + var key = GetTenantKey( context ); + var tenantId = context.Request.Cookies[key]; + GetLog( context ).LogTrace( $"执行Cookie租户解析器,{key}={tenantId}" ); + return Task.FromResult( tenantId ); + } +} \ No newline at end of file diff --git a/src/Util.Tenants/Resolvers/DefaultTenantResolver.cs b/src/Util.Tenants/Resolvers/DefaultTenantResolver.cs new file mode 100644 index 000000000..d521f07bf --- /dev/null +++ b/src/Util.Tenants/Resolvers/DefaultTenantResolver.cs @@ -0,0 +1,40 @@ +namespace Util.Tenants.Resolvers; + +/// +/// 默认租户解析器 +/// +public class DefaultTenantResolver : ITenantResolver { + /// + /// 租户配置 + /// + private readonly TenantOptions _options; + /// + public int Priority { get; set; } + + /// + /// 初始化默认租户解析器 + /// + /// 租户配置 + public DefaultTenantResolver( IOptions options ) { + _options = options?.Value ?? TenantOptions.Null; + } + + /// + public async Task ResolveAsync( HttpContext context ) { + if ( context == null ) + return null; + if ( _options.IsEnabled == false ) + return null; + var session = context.RequestServices.GetService(); + if ( session is { IsAuthenticated: true } ) + return session.TenantId; + if ( _options.Resolvers == null ) + return null; + foreach ( var resolver in _options.Resolvers.OrderByDescending( t => t.Priority ) ) { + var result = await resolver.ResolveAsync( context ); + if ( result.IsEmpty() == false ) + return result; + } + return null; + } +} \ No newline at end of file diff --git a/src/Util.Tenants/Resolvers/DomainTenantResolver.cs b/src/Util.Tenants/Resolvers/DomainTenantResolver.cs new file mode 100644 index 000000000..9cdc0d472 --- /dev/null +++ b/src/Util.Tenants/Resolvers/DomainTenantResolver.cs @@ -0,0 +1,59 @@ +namespace Util.Tenants.Resolvers; + +/// +/// 基于域名的租户解析器 +/// +public class DomainTenantResolver : TenantResolverBase { + /// + /// 初始化基于域名的租户解析器 + /// + public DomainTenantResolver() { + } + + /// + /// 初始化基于域名的租户解析器 + /// + /// 域名格式字符串,范例:{0}.a.com + public DomainTenantResolver( string domainFormat ) { + DomainFormat = domainFormat; + } + + /// + /// 初始化基于域名的租户解析器 + /// + /// 域名映射配置,键为域名,值为租户标识 + public DomainTenantResolver( IDictionary domainMap ) { + DomainMap = domainMap; + } + + /// + /// 域名格式字符串 + /// + public string DomainFormat { get; } + + /// + /// 域名映射配置 + /// + public IDictionary DomainMap { get; } + + /// + /// 解析租户标识 + /// + protected override async Task Resolve( HttpContext context ) { + var host = DomainTenantResolverHelper.RemoveDomainPrefix( context.Request.Host.Value ); + if ( host.IsEmpty() ) + return null; + var result = await Resolve( context, host ); + GetLog( context ).LogTrace( $"执行域名租户解析器,host={host},tenantId={result}" ); + return result; + } + + /// + /// 解析租户标识 + /// + private async Task Resolve( HttpContext context, string host ) { + var store = context.RequestServices.GetService(); + var map = await DomainTenantResolverHelper.CombineMapAsync( DomainMap,store ); + return DomainTenantResolverHelper.ResolveTenantId( host, map, DomainFormat ); + } +} \ No newline at end of file diff --git a/src/Util.Tenants/Resolvers/DomainTenantResolverHelper.cs b/src/Util.Tenants/Resolvers/DomainTenantResolverHelper.cs new file mode 100644 index 000000000..f1b2c9485 --- /dev/null +++ b/src/Util.Tenants/Resolvers/DomainTenantResolverHelper.cs @@ -0,0 +1,101 @@ +namespace Util.Tenants.Resolvers; + +/// +/// 租户域名解析器辅助操作 +/// +public class DomainTenantResolverHelper { + /// + /// 解析租户标识 + /// + /// 域名 + /// 域名映射配置 + /// 域名格式字符串 + public static string ResolveTenantId( string domain, IDictionary domainMap, string domainFormat ) { + if ( domain.IsEmpty() ) + return null; + var host = RemoveDomainPrefix( domain ); + if ( host.IsEmpty() ) + return null; + if ( domainMap is { Count: > 0 } ) { + var result = ResolveByMap( host, domainMap ); + if ( result.IsEmpty() == false ) + return result; + } + domainFormat = domainFormat.RemoveStart( "," ).RemoveEnd( "," ); + return domainFormat.IsEmpty() ? ResolveBySplit( host ): ResolveByFormat( host, domainFormat ); + } + + /// + /// 移除域名前缀 + /// + public static string RemoveDomainPrefix( string domain ) { + return domain.RemoveStart( "http://" ).RemoveStart( "https://" ); + } + + /// + /// 通过域名映射配置解析 + /// + private static string ResolveByMap( string host, IDictionary domainMap ) { + foreach ( var item in domainMap ) { + if ( item.Key == host ) + return item.Value; + } + return null; + } + + /// + /// 通过域名格式字符串解析 + /// + private static string ResolveByFormat( string host, string domainFormat ) { + var formats = GetDomainFormats( domainFormat ); + foreach ( var format in formats ) { + if ( format.IsEmpty() ) + continue; + var result = Util.Helpers.String.Extract( host, RemoveDomainPrefix( format ) ).FirstOrDefault().Value; + if ( result.IsEmpty() == false ) + return result; + } + return null; + } + + /// + /// 获取域名格式列表 + /// + private static IEnumerable GetDomainFormats( string domainFormat ) { + if ( domainFormat.IndexOf( ",", StringComparison.Ordinal ) >= 0 ) + return domainFormat.Split( ',' ); + if ( domainFormat.IndexOf( ";", StringComparison.Ordinal ) >= 0 ) + return domainFormat.Split( ';' ); + return new[] { domainFormat }; + } + + /// + /// 通过拆分解析 + /// + private static string ResolveBySplit( string host ) { + var items = host.Split( '.' ); + if ( items.Length > 2 ) + return items[0]; + return null; + } + + /// + /// 合并域名映射字典和租户域名存储器中的域名映射项 + /// + /// 域名映射字典 + /// 租户域名存储器 + public static async Task> CombineMapAsync( IDictionary domainMap, ITenantDomainStore store) { + if ( store == null ) + return domainMap; + var map = await store.GetAsync(); + if ( map == null || map.Count == 0 ) + return domainMap; + domainMap ??= new Dictionary(); + foreach ( var item in map ) { + if ( domainMap.ContainsKey( item.Key ) == false ) { + domainMap.Add( item.Key, item.Value ); + } + } + return domainMap; + } +} \ No newline at end of file diff --git a/src/Util.Tenants/Resolvers/HeaderTenantResolver.cs b/src/Util.Tenants/Resolvers/HeaderTenantResolver.cs new file mode 100644 index 000000000..a5cfc95d8 --- /dev/null +++ b/src/Util.Tenants/Resolvers/HeaderTenantResolver.cs @@ -0,0 +1,17 @@ +namespace Util.Tenants.Resolvers; + +/// +/// 基于请求头的租户解析器 +/// +public class HeaderTenantResolver : TenantResolverBase { + /// + /// 解析租户标识 + /// + protected override Task Resolve( HttpContext context ) { + var key = GetTenantKey( context ); + context.Request.Headers.TryGetValue( key, out var result ); + var tenantId = result.FirstOrDefault(); + GetLog( context ).LogTrace( $"执行请求头租户解析器,{key}={tenantId}" ); + return Task.FromResult( tenantId ); + } +} \ No newline at end of file diff --git a/src/Util.Tenants/Resolvers/ITenantDomainStore.cs b/src/Util.Tenants/Resolvers/ITenantDomainStore.cs new file mode 100644 index 000000000..16c9038dd --- /dev/null +++ b/src/Util.Tenants/Resolvers/ITenantDomainStore.cs @@ -0,0 +1,13 @@ +using Util.Dependency; + +namespace Util.Tenants.Resolvers; + +/// +/// 租户域名存储器 +/// +public interface ITenantDomainStore : ITransientDependency { + /// + /// 获取租户域名映射,键为域名,值为租户标识 + /// + Task> GetAsync(); +} \ No newline at end of file diff --git a/src/Util.Tenants/Resolvers/Internal/DomainTenantResolver.cs b/src/Util.Tenants/Resolvers/Internal/DomainTenantResolver.cs new file mode 100644 index 000000000..631a8ff0b --- /dev/null +++ b/src/Util.Tenants/Resolvers/Internal/DomainTenantResolver.cs @@ -0,0 +1,44 @@ +namespace Util.Tenants.Resolvers.Internal; + +/// +/// 域名租户解析器 +/// +internal class DomainTenantResolver : IDomainTenantResolver { + /// + /// 租户域名存储器 + /// + private readonly ITenantDomainStore _store; + /// + /// 租户配置 + /// + private readonly TenantOptions _options; + + /// + /// 初始化域名租户解析器 + /// + /// 租户域名存储器 + /// 租户配置 + public DomainTenantResolver( ITenantDomainStore store, IOptions options ) { + _store = store ?? throw new ArgumentNullException( nameof( store ) ); + _options = options?.Value ?? new TenantOptions(); + } + + /// + public async Task ResolveTenantIdAsync( string host ) { + if ( host.IsEmpty() ) + return null; + var domainMap = new Dictionary(); + var domainFormat = string.Empty; + var resolvers = _options.Resolvers.GetResolvers(); + foreach ( var resolver in resolvers ) { + if ( resolver.DomainMap != null ) { + foreach ( var item in resolver.DomainMap ) { + domainMap.Add( item.Key, item.Value ); + } + } + domainFormat += $",{resolver.DomainFormat}"; + } + var map = await DomainTenantResolverHelper.CombineMapAsync( domainMap, _store ); + return DomainTenantResolverHelper.ResolveTenantId( host, map, domainFormat ); + } +} \ No newline at end of file diff --git a/src/Util.Tenants/Resolvers/NullTenantDomainStore.cs b/src/Util.Tenants/Resolvers/NullTenantDomainStore.cs new file mode 100644 index 000000000..b75ff31de --- /dev/null +++ b/src/Util.Tenants/Resolvers/NullTenantDomainStore.cs @@ -0,0 +1,31 @@ +using Util.Dependency; + +namespace Util.Tenants.Resolvers; + +/// +/// 空租户域名存储器 +/// +[Ioc(-9)] +public class NullTenantDomainStore : ITenantDomainStore { + /// + /// 结果 + /// + private readonly IDictionary _result; + + /// + /// 初始化空租户域名存储器 + /// + public NullTenantDomainStore() { + _result = new Dictionary(); + } + + /// + /// 空租户域名存储器 + /// + public static readonly ITenantDomainStore Instance = new NullTenantDomainStore(); + + /// + public Task> GetAsync() { + return Task.FromResult(_result); + } +} \ No newline at end of file diff --git a/src/Util.Tenants/Resolvers/QueryStringTenantResolver.cs b/src/Util.Tenants/Resolvers/QueryStringTenantResolver.cs new file mode 100644 index 000000000..d07cf200c --- /dev/null +++ b/src/Util.Tenants/Resolvers/QueryStringTenantResolver.cs @@ -0,0 +1,16 @@ +namespace Util.Tenants.Resolvers; + +/// +/// 基于查询字符串的租户解析器 +/// +public class QueryStringTenantResolver : TenantResolverBase { + /// + /// 解析租户标识 + /// + protected override Task Resolve( HttpContext context ) { + var key = GetTenantKey( context ); + var tenantId = context.Request.Query[key].FirstOrDefault(); + GetLog( context ).LogTrace( $"执行查询字符串租户解析器,{key}={tenantId}" ); + return Task.FromResult( tenantId ); + } +} \ No newline at end of file diff --git a/src/Util.Tenants/Resolvers/TenantResolverBase.cs b/src/Util.Tenants/Resolvers/TenantResolverBase.cs new file mode 100644 index 000000000..be0e03ee6 --- /dev/null +++ b/src/Util.Tenants/Resolvers/TenantResolverBase.cs @@ -0,0 +1,36 @@ +namespace Util.Tenants.Resolvers; + +/// +/// 租户解析器 +/// +public abstract class TenantResolverBase : ITenantResolver { + /// + public int Priority { get; set; } + + /// + public async Task ResolveAsync( HttpContext context ) { + if ( context == null ) + return null; + return await Resolve( context ); + } + + /// + /// 解析租户标识 + /// + protected abstract Task Resolve( HttpContext context ); + + /// + /// 获取租户键名 + /// + protected string GetTenantKey( HttpContext context ) { + var options = context.RequestServices.GetRequiredService>(); + return options.Value.TenantKey; + } + + /// + /// 获取日志记录器 + /// + protected ILogger GetLog( HttpContext context ) { + return context.RequestServices.GetService>() ?? NullLogger.Instance; + } +} \ No newline at end of file diff --git a/src/Util.Tenants/TenantMiddlewareExtensions.cs b/src/Util.Tenants/TenantMiddlewareExtensions.cs new file mode 100644 index 000000000..24cafa643 --- /dev/null +++ b/src/Util.Tenants/TenantMiddlewareExtensions.cs @@ -0,0 +1,16 @@ +using Util.Tenants.Middlewares; + +namespace Util.Tenants; + +/// +/// 租户处理中间件扩展 +/// +public static class TenantMiddlewareExtensions { + /// + /// 注册租户处理中间件,注意: 必须在 app.UseAuthentication() 之后调用 + /// + /// 应用程序生成器 + public static IApplicationBuilder UseTenant( this IApplicationBuilder builder ) { + return builder.UseMiddleware(); + } +} \ No newline at end of file diff --git a/src/Util.Tenants/TenantOptions.cs b/src/Util.Tenants/TenantOptions.cs new file mode 100644 index 000000000..fd9880786 --- /dev/null +++ b/src/Util.Tenants/TenantOptions.cs @@ -0,0 +1,55 @@ +using Util.Tenants.Resolvers; + +namespace Util.Tenants; + +/// +/// 租户配置 +/// +public class TenantOptions { + /// + /// 初始化租户配置 + /// + public TenantOptions() { + TenantKey = DefaultTenantKey; + Resolvers = new TenantResolverCollection { + new HeaderTenantResolver{ Priority = 9 }, + new QueryStringTenantResolver{ Priority = 7 }, + new CookieTenantResolver{ Priority = 5 } + }; + } + + /// + /// 租户配置空实例 + /// + public static readonly TenantOptions Null = new(); + + /// + /// 默认租户键名,默认值: x-tenant-id + /// + public const string DefaultTenantKey = "x-tenant-id"; + + /// + /// 是否启用多租户架构 + /// + public bool IsEnabled { get; set; } + + /// + /// 是否允许使用多数据库 + /// + public bool IsAllowMultipleDatabase { get; set; } + + /// + /// 默认租户标识 + /// + public string DefaultTenantId { get; set; } + + /// + /// 租户键名,默认值: x-tenant-id + /// + public string TenantKey { get; set; } + + /// + /// 租户解析器集合 + /// + public TenantResolverCollection Resolvers { get; } +} \ No newline at end of file diff --git a/src/Util.Tenants/TenantResolverCollection.cs b/src/Util.Tenants/TenantResolverCollection.cs new file mode 100644 index 000000000..4fe379f58 --- /dev/null +++ b/src/Util.Tenants/TenantResolverCollection.cs @@ -0,0 +1,136 @@ +namespace Util.Tenants; + +/// +/// 租户解析器集合 +/// +public class TenantResolverCollection : IEnumerable { + /// + /// 租户解析器列表 + /// + private readonly Dictionary _resolvers; + + /// + /// 初始化租户解析器集合 + /// + public TenantResolverCollection() { + _resolvers = new Dictionary(); + } + + /// + /// 获取枚举器 + /// + IEnumerator IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + /// + /// 获取枚举器 + /// + public IEnumerator GetEnumerator() { + return GetResolvers().GetEnumerator(); + } + + /// + /// 获取租户解析器列表 + /// + public List GetResolvers() { + return _resolvers + .Select( t => t.Value ) + .OrderByDescending( t => t.Priority ) + .ToList(); + } + + /// + /// 获取租户解析器列表 + /// + public List GetResolvers() where TResolver : ITenantResolver { + return _resolvers + .Where( t => t.Value.GetType() == typeof( TResolver ) ) + .Select( t => (TResolver)t.Value ) + .OrderByDescending( t => t.Priority ) + .ToList(); + } + + /// + /// 获取租户解析器 + /// + /// 租户解析器类型 + public TResolver GetResolver() where TResolver: ITenantResolver { + var key = typeof( TResolver ).FullName; + return GetResolver( key ); + } + + /// + /// 获取租户解析器 + /// + /// 租户解析器类型 + /// 租户解析器键名 + public TResolver GetResolver( string key ) where TResolver : ITenantResolver { + var result = _resolvers.FirstOrDefault( t => t.Key == key ); + if ( result.Value == null ) + return default; + return (TResolver)result.Value; + } + + /// + /// 添加租户解析器 + /// + /// 租户解析器 + public void Add( ITenantResolver resolver ) { + if ( resolver == null ) + return; + var key = resolver.GetType().FullName; + Add( key, resolver ); + } + + /// + /// 添加租户解析器 + /// + /// 租户解析器键名 + /// 租户解析器 + public void Add( string key, ITenantResolver resolver ) { + if ( key.IsEmpty() ) + return; + if ( resolver == null ) + return; + Remove( key ); + _resolvers.Add( key, resolver ); + } + + /// + /// 添加租户解析器列表 + /// + /// 租户解析器列表 + public void Add( IList resolvers ) { + if ( resolvers == null ) + return; + foreach ( var resolver in resolvers ) + Add( resolver ); + } + + /// + /// 移除租户解析器 + /// + /// 租户解析器类型 + public void Remove() where TResolver : ITenantResolver { + var key = typeof( TResolver ).FullName; + Remove( key ); + } + + /// + /// 移除租户解析器 + /// + /// 租户解析器键名 + public void Remove( string key ) { + if ( key.IsEmpty() ) + return; + _resolvers.Remove( key ); + } + + /// + /// 清空租户解析器 + /// + public void Clear() { + _resolvers.Clear(); + } +} \ No newline at end of file diff --git a/src/Util.Tenants/Usings.cs b/src/Util.Tenants/Usings.cs new file mode 100644 index 000000000..6782ec2b8 --- /dev/null +++ b/src/Util.Tenants/Usings.cs @@ -0,0 +1,13 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Collections; +global using System.Threading; +global using Microsoft.AspNetCore.Http; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Options; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Logging.Abstractions; +global using Microsoft.AspNetCore.Builder; \ No newline at end of file diff --git a/src/Util.Tools.QrCode/01-Util.Tools.QrCode.csproj b/src/Util.Tools.QrCode/01-Util.Tools.QrCode.csproj deleted file mode 100644 index 1501db5d4..000000000 --- a/src/Util.Tools.QrCode/01-Util.Tools.QrCode.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - - netstandard2.0 - Util.Tools.QrCode - Util.Tools.QrCode - 0.2.2-preview - 何镇汐 - 何镇汐 - Util.Tools.QrCode是Util应用框架的二维码操作类库。 -Util是一个.net core平台下的应用框架,旨在提升小型团队的开发输出能力,由常用公共操作类(工具类)、分层架构基类、Ui组件,第三方组件封装,第三方业务接口封装,配套代码生成模板,权限等组成。 - 何镇汐 - https://github.com/dotnetcore/Util/blob/master/LICENSE.txt - https://github.com/dotnetcore/Util - https://avatars2.githubusercontent.com/u/19421630 - https://github.com/dotnetcore/Util - git - util;applicationframework;dotnetcore;aspnetcore; - Util应用框架以MIT开源发布,可随意使用 - 0.2.2.0 - 0.2.2.0 - false - - - - ..\..\output\release - ..\..\output\release\netstandard2.0\Util.Tools.QrCode.xml - - - - ..\..\output\release - ..\..\output\release\netstandard2.0\Util.Tools.QrCode.xml - - - - - - - - - - - diff --git a/src/Util.Tools.QrCode/ErrorCorrectionLevel.cs b/src/Util.Tools.QrCode/ErrorCorrectionLevel.cs deleted file mode 100644 index bca5fc8b7..000000000 --- a/src/Util.Tools.QrCode/ErrorCorrectionLevel.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Util.Tools.QrCode { - /// - /// 容错级别 - /// - public enum ErrorCorrectionLevel { - /// - /// 可以纠正最大7%的错误 - /// - L, - /// - /// 可以纠正最大15%的错误 - /// - M, - /// - /// 可以纠正最大25%的错误 - /// - Q, - /// - /// 可以纠正最大30%的错误 - /// - H - } -} diff --git a/src/Util.Tools.QrCode/IQrCodeService.cs b/src/Util.Tools.QrCode/IQrCodeService.cs deleted file mode 100644 index 38634e2d3..000000000 --- a/src/Util.Tools.QrCode/IQrCodeService.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Util.Tools.QrCode { - /// - /// 二维码服务 - /// - public interface IQrCodeService { - /// - /// 设置二维码尺寸 - /// - /// 二维码尺寸 - IQrCodeService Size( QrSize size ); - /// - /// 设置二维码尺寸 - /// - /// 二维码尺寸 - IQrCodeService Size( int size ); - /// - /// 容错处理 - /// - /// 容错级别 - IQrCodeService Correction( ErrorCorrectionLevel level ); - /// - /// 生成二维码并保存到指定位置,返回二维码图片完整路径 - /// - /// 内容 - string Save( string content ); - } -} diff --git a/src/Util.Tools.QrCode/QrCoder/QRCoderService.cs b/src/Util.Tools.QrCode/QrCoder/QRCoderService.cs deleted file mode 100644 index d70a601c9..000000000 --- a/src/Util.Tools.QrCode/QrCoder/QRCoderService.cs +++ /dev/null @@ -1,90 +0,0 @@ -using QRCoder; -using Util.Files; - -namespace Util.Tools.QrCode.QrCoder { - /// - /// QRCoder二维码服务 - /// - public class QrCoderService : IQrCodeService { - /// - /// 文件存储服务 - /// - private readonly IFileStore _fileStore; - /// - /// 二维码尺寸 - /// - private int _size; - /// - /// 容错级别 - /// - private QRCodeGenerator.ECCLevel _level; - - /// - /// 初始化QRCoder组件二维码服务 - /// - /// 文件存储服务 - public QrCoderService( IFileStore fileStore ) { - _fileStore = fileStore; - _size = 10; - _level = QRCodeGenerator.ECCLevel.L; - } - - /// - /// 设置二维码尺寸 - /// - /// 二维码尺寸 - public IQrCodeService Size( QrSize size ) { - return Size( size.Value() ); - } - - /// - /// 设置二维码尺寸 - /// - /// 二维码尺寸 - public IQrCodeService Size( int size ) { - _size = size; - return this; - } - - /// - /// 容错处理 - /// - /// 容错级别 - public IQrCodeService Correction( ErrorCorrectionLevel level ) { - switch( level ) { - case ErrorCorrectionLevel.L: - _level = QRCodeGenerator.ECCLevel.L; - break; - case ErrorCorrectionLevel.M: - _level = QRCodeGenerator.ECCLevel.M; - break; - case ErrorCorrectionLevel.Q: - _level = QRCodeGenerator.ECCLevel.Q; - break; - case ErrorCorrectionLevel.H: - _level = QRCodeGenerator.ECCLevel.H; - break; - } - return this; - } - - /// - /// 生成二维码并保存到指定位置 - /// - /// 内容 - public string Save( string content ) { - var qrCode = CreateQrCode( content ); - return _fileStore.Save( qrCode, "jpg" ); - } - - /// - /// 创建二维码 - /// - private byte[] CreateQrCode( string content ) { - QRCodeGenerator generator = new QRCodeGenerator(); - QRCodeData data = generator.CreateQrCode( content, _level ); - BitmapByteQRCode qrCode = new BitmapByteQRCode( data ); - return qrCode.GetGraphic( _size ); - } - } -} diff --git a/src/Util.Tools.QrCode/QrSize.cs b/src/Util.Tools.QrCode/QrSize.cs deleted file mode 100644 index 67e1b0c49..000000000 --- a/src/Util.Tools.QrCode/QrSize.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Util.Tools.QrCode { - /// - /// 二维码尺寸 - /// - public enum QrSize { - /// - /// 小 - /// - Small = 5, - /// - /// 中 - /// - Middle = 10, - /// - /// 大 - /// - Large = 20 - } -} diff --git a/src/Util.Tools.Sms/02-Util.Tools.Sms.csproj b/src/Util.Tools.Sms/02-Util.Tools.Sms.csproj deleted file mode 100644 index e3be15d39..000000000 --- a/src/Util.Tools.Sms/02-Util.Tools.Sms.csproj +++ /dev/null @@ -1,39 +0,0 @@ - - - - netstandard2.0 - Util.Tools.Sms - Util.Tools.Sms - 0.2.2-preview - 0.2.2.0 - 0.2.2.0 - 何镇汐 - 何镇汐 - Util.Tools.Sms是Util应用框架的短信操作类库。 -Util是一个.net core平台下的应用框架,旨在提升小型团队的开发输出能力,由常用公共操作类(工具类)、分层架构基类、Ui组件,第三方组件封装,第三方业务接口封装,配套代码生成模板,权限等组成。 - 何镇汐 - https://github.com/dotnetcore/Util/blob/master/LICENSE.txt - https://github.com/dotnetcore/Util - https://avatars2.githubusercontent.com/u/19421630 - https://github.com/dotnetcore/Util - git - util;applicationframework;dotnetcore;aspnetcore; - Util应用框架以MIT开源发布,可随意使用 - false - - - - ..\..\output\release - ..\..\output\release\netstandard2.0\Util.Tools.Sms.xml - - - - ..\..\output\release - ..\..\output\release\netstandard2.0\Util.Tools.Sms.xml - - - - - - - diff --git a/src/Util.Tools.Sms/ISmsService.cs b/src/Util.Tools.Sms/ISmsService.cs deleted file mode 100644 index 5d5713216..000000000 --- a/src/Util.Tools.Sms/ISmsService.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Threading.Tasks; - -namespace Util.Tools.Sms { - /// - /// 短信服务 - /// - public interface ISmsService { - /// - /// 发送短信 - /// - /// 手机号 - /// 内容 - SmsResult Send( string mobile, string content ); - /// - /// 发送短信 - /// - /// 手机号 - /// 内容 - Task SendAsync( string mobile, string content ); - } -} diff --git a/src/Util.Tools.Sms/LuoSiMao/ISmsConfigProvider.cs b/src/Util.Tools.Sms/LuoSiMao/ISmsConfigProvider.cs deleted file mode 100644 index c9a877b05..000000000 --- a/src/Util.Tools.Sms/LuoSiMao/ISmsConfigProvider.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Util.Tools.Sms.LuoSiMao { - /// - /// 短信配置提供器 - /// - public interface ISmsConfigProvider { - /// - /// 获取配置 - /// - SmsConfig GetConfig(); - } -} diff --git a/src/Util.Tools.Sms/LuoSiMao/LuoSiMaoResult.cs b/src/Util.Tools.Sms/LuoSiMao/LuoSiMaoResult.cs deleted file mode 100644 index e2fe0c955..000000000 --- a/src/Util.Tools.Sms/LuoSiMao/LuoSiMaoResult.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Util.Tools.Sms.LuoSiMao { - /// - /// LuoSiMao短信接口返回结果 - /// - public class LuoSiMaoResult { - /// - /// 错误代码 - /// - public string error { get; set; } - - /// - /// 返回信息 - /// - public string msg { get; set; } - } -} diff --git a/src/Util.Tools.Sms/LuoSiMao/SmsConfig.cs b/src/Util.Tools.Sms/LuoSiMao/SmsConfig.cs deleted file mode 100644 index 884b32082..000000000 --- a/src/Util.Tools.Sms/LuoSiMao/SmsConfig.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Util.Tools.Sms.LuoSiMao { - /// - /// LuoSiMao短信配置 - /// - public class SmsConfig { - /// - /// 初始化短信配置 - /// - /// 密钥 - public SmsConfig( string key ) { - Key = key; - } - - /// - /// 密钥 - /// - public string Key { get; } - } -} diff --git a/src/Util.Tools.Sms/LuoSiMao/SmsService.cs b/src/Util.Tools.Sms/LuoSiMao/SmsService.cs deleted file mode 100644 index b8d1013a6..000000000 --- a/src/Util.Tools.Sms/LuoSiMao/SmsService.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Text; -using System.Threading.Tasks; -using Util.Helpers; - -namespace Util.Tools.Sms.LuoSiMao { - /// - /// 短信服务 - /// - public class SmsService : ISmsService { - /// - /// 短信配置提供器 - /// - private readonly ISmsConfigProvider _configProvider; - - /// - /// 初始化短信服务 - /// - /// 短信配置提供器 - public SmsService( ISmsConfigProvider configProvider ) { - configProvider.CheckNull( nameof( configProvider ) ); - _configProvider = configProvider; - } - - /// - /// 发送短信 - /// - /// 手机号 - /// 内容 - public SmsResult Send( string mobile, string content ) { - return Async.Run( async () => await SendAsync( mobile, content ) ); - } - - /// - /// 发送短信 - /// - /// 手机号 - /// 内容 - public async Task SendAsync( string mobile, string content ) { - return await Web.Client().Post( "https://sms-api.luosimao.com/v1/send.json" ) - .Header( "Authorization", GetAuthorization() ) - .Data( "mobile", mobile ) - .Data( "message", content ) - .ResultAsync().ContinueWith( ( task, state ) => CreateResult( task.Result ),null ); - } - - /// - /// 获取授权头信息 - /// - private string GetAuthorization() { - var config = _configProvider.GetConfig(); - return $"Basic {System.Convert.ToBase64String( Encoding.UTF8.GetBytes( $"api:{config.Key}" ) )}"; - } - - /// - /// 创建结果 - /// - private SmsResult CreateResult( string message ) { - var result = Json.ToObject( message ); - result.CheckNull( nameof( result ) ); - if( result.error == "0" ) - return SmsResult.Ok; - if( result.msg == "WRONG_MOBILE" ) - return new SmsResult( false, message, SmsErrorCode.MobileError ); - return new SmsResult( false, message ); - } - } -} diff --git a/src/Util.Tools.Sms/SmsErrorCode.cs b/src/Util.Tools.Sms/SmsErrorCode.cs deleted file mode 100644 index 84990866e..000000000 --- a/src/Util.Tools.Sms/SmsErrorCode.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.ComponentModel; - -namespace Util.Tools.Sms { - /// - /// 短信错误码 - /// - public enum SmsErrorCode { - /// - /// 发送成功 - /// - Ok, - /// - /// 手机号错误 - /// - [Description("手机号错误")] - MobileError - } -} diff --git a/src/Util.Tools.Sms/SmsResult.cs b/src/Util.Tools.Sms/SmsResult.cs deleted file mode 100644 index bc1478838..000000000 --- a/src/Util.Tools.Sms/SmsResult.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Util.Tools.Sms { - /// - /// 短信接口返回结果 - /// - public class SmsResult { - /// - /// 初始化短信接口返回结果 - /// - /// 是否发送成功 - /// 短信提供商返回的原始消息 - /// 短信错误码 - public SmsResult( bool success = true, string raw = "", SmsErrorCode errorCode = SmsErrorCode.Ok ) { - Success = success; - Raw = raw; - ErrorCode = errorCode; - } - - /// - /// 是否发送成功 - /// - public bool Success { get; } - - /// - /// 短信错误码 - /// - public SmsErrorCode ErrorCode { get; } - - /// - /// 短信提供商返回的原始消息 - /// - public string Raw { get; } - - /// - /// 成功消息 - /// - public static SmsResult Ok { get; } = new SmsResult(); - } -} diff --git a/src/Util.Ui.Abstractions/01-Util.Ui.Abstractions.csproj b/src/Util.Ui.Abstractions/01-Util.Ui.Abstractions.csproj deleted file mode 100644 index e182911f3..000000000 --- a/src/Util.Ui.Abstractions/01-Util.Ui.Abstractions.csproj +++ /dev/null @@ -1,41 +0,0 @@ - - - - netstandard2.0 - Util.Ui.Abstractions - Util.Ui - 0.2.2-preview - 何镇汐 - 何镇汐 - 何镇汐 - Util.Ui.Abstractions是Util应用框架的Ui抽象层类库。 -Util是一个.net core平台下的应用框架,旨在提升小型团队的开发输出能力,由常用公共操作类(工具类)、分层架构基类、Ui组件,第三方组件封装,第三方业务接口封装,配套代码生成模板,权限等组成。 - https://github.com/dotnetcore/Util/blob/master/LICENSE.txt - https://github.com/dotnetcore/Util - https://avatars2.githubusercontent.com/u/19421630 - https://github.com/dotnetcore/Util - git - util;applicationframework;dotnetcore;aspnetcore; - Util应用框架以MIT开源发布,可随意使用 - 0.2.2.0 - 0.2.2.0 - false - - - - ..\..\output\release\ - ..\..\output\release\netstandard2.0\Util.Ui.Abstractions.xml - - - - ..\..\output\release\ - ..\..\output\release\netstandard2.0\Util.Ui.Abstractions.xml - - - - - - - - - diff --git a/src/Util.Ui.Abstractions/01-Util.Ui.Abstractions.csproj.user b/src/Util.Ui.Abstractions/01-Util.Ui.Abstractions.csproj.user deleted file mode 100644 index baf24173c..000000000 --- a/src/Util.Ui.Abstractions/01-Util.Ui.Abstractions.csproj.user +++ /dev/null @@ -1,6 +0,0 @@ - - - - false - - \ No newline at end of file diff --git a/src/Util.Ui.Abstractions/Components/IAnchor.cs b/src/Util.Ui.Abstractions/Components/IAnchor.cs deleted file mode 100644 index 2a903de4a..000000000 --- a/src/Util.Ui.Abstractions/Components/IAnchor.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Util.Ui.Operations; -using Util.Ui.Operations.Events; -using Util.Ui.Operations.Navigation; -using Util.Ui.Operations.Styles; - -namespace Util.Ui.Components { - /// - /// 链接 - /// - public interface IAnchor : IComponent, ILink, IText, IDisabled, IColor, ITooltip, IButtonStyle, IOnClick { - } -} diff --git a/src/Util.Ui.Abstractions/Components/IButton.cs b/src/Util.Ui.Abstractions/Components/IButton.cs deleted file mode 100644 index 2bc5a586a..000000000 --- a/src/Util.Ui.Abstractions/Components/IButton.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Util.Ui.Operations; -using Util.Ui.Operations.Events; -using Util.Ui.Operations.Styles; - -namespace Util.Ui.Components { - /// - /// 按钮 - /// - public interface IButton : IText, IDisabled, IColor, ITooltip, IButtonStyle, IOnClick { - } -} diff --git a/src/Util.Ui.Abstractions/Components/ICheckBox.cs b/src/Util.Ui.Abstractions/Components/ICheckBox.cs deleted file mode 100644 index f3614610e..000000000 --- a/src/Util.Ui.Abstractions/Components/ICheckBox.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Util.Ui.Operations; -using Util.Ui.Operations.Events; -using Util.Ui.Operations.Forms; -using Util.Ui.Operations.Forms.Validations; -using Util.Ui.Operations.Styles; - -namespace Util.Ui.Components { - /// - /// 复选框 - /// - public interface ICheckBox : IComponent, IName, ILabel, IDisabled, IModel, IRequired, IOnChange, ILabelPosition,IColor { - } -} diff --git a/src/Util.Ui.Abstractions/Components/IComponent.cs b/src/Util.Ui.Abstractions/Components/IComponent.cs deleted file mode 100644 index 186e5a89d..000000000 --- a/src/Util.Ui.Abstractions/Components/IComponent.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Microsoft.AspNetCore.Html; - -namespace Util.Ui.Components { - /// - /// 组件 - /// - public interface IComponent : IOption, IHtmlContent { - } -} diff --git a/src/Util.Ui.Abstractions/Components/IContainer.cs b/src/Util.Ui.Abstractions/Components/IContainer.cs deleted file mode 100644 index 90bdf2cd3..000000000 --- a/src/Util.Ui.Abstractions/Components/IContainer.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace Util.Ui.Components { - /// - /// 容器 - /// - public interface IContainer : IOption { - } - - /// - /// 容器 - /// - /// 包装器类型 - public interface IContainer : IContainer where TWrapper : IDisposable { - /// - /// 准备渲染容器 - /// - TWrapper Begin(); - } -} diff --git a/src/Util.Ui.Abstractions/Components/IFormControl.cs b/src/Util.Ui.Abstractions/Components/IFormControl.cs deleted file mode 100644 index d0307de44..000000000 --- a/src/Util.Ui.Abstractions/Components/IFormControl.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Util.Ui.Operations; -using Util.Ui.Operations.Events; -using Util.Ui.Operations.Forms; -using Util.Ui.Operations.Forms.Validations; - -namespace Util.Ui.Components { - /// - /// 表单控件 - /// - public interface IFormControl : IComponent,IName,IDisabled, IPlaceholder, IHint, IPrefix, ISuffix, IModel, IRequired, IOnChange, IOnFocus, IOnBlur, IOnKeyup, IOnKeydown { - } -} diff --git a/src/Util.Ui.Abstractions/Components/IIcon.cs b/src/Util.Ui.Abstractions/Components/IIcon.cs deleted file mode 100644 index 0c8ec1522..000000000 --- a/src/Util.Ui.Abstractions/Components/IIcon.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Util.Ui.Operations; -using Util.Ui.Operations.Effects; -using Util.Ui.Operations.Styles; - -namespace Util.Ui.Components { - /// - /// 图标 - /// - public interface IIcon : IComponent, ISetIcon, IAttribute,IClass, IStyle, ISpin { - } -} diff --git a/src/Util.Ui.Abstractions/Components/IOption.cs b/src/Util.Ui.Abstractions/Components/IOption.cs deleted file mode 100644 index 1cd55e914..000000000 --- a/src/Util.Ui.Abstractions/Components/IOption.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Components { - /// - /// 配置项 - /// - public interface IOption { - } -} diff --git a/src/Util.Ui.Abstractions/Components/IRadio.cs b/src/Util.Ui.Abstractions/Components/IRadio.cs deleted file mode 100644 index 6ea7372c2..000000000 --- a/src/Util.Ui.Abstractions/Components/IRadio.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Util.Ui.Operations; -using Util.Ui.Operations.Datas; -using Util.Ui.Operations.Events; -using Util.Ui.Operations.Forms; -using Util.Ui.Operations.Forms.Validations; - -namespace Util.Ui.Components { - /// - /// 单选框 - /// - public interface IRadio : IComponent, IName, ILabel, IDisabled, IModel, IRequired, IOnChange, ILabelPosition, IUrl, IDataSource, IItem { - /// - /// 绑定枚举 - /// - /// 枚举类型 - IRadio Enum(); - } -} diff --git a/src/Util.Ui.Abstractions/Components/ISelect.cs b/src/Util.Ui.Abstractions/Components/ISelect.cs deleted file mode 100644 index 379c7c86b..000000000 --- a/src/Util.Ui.Abstractions/Components/ISelect.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Util.Ui.Operations.Datas; -using Util.Ui.Operations.Forms; - -namespace Util.Ui.Components { - /// - /// 下拉列表 - /// - public interface ISelect : IFormControl, IUrl, IDataSource, IResetOption, IMultiple, IItem { - /// - /// 绑定枚举 - /// - /// 枚举类型 - ISelect Enum(); - } -} \ No newline at end of file diff --git a/src/Util.Ui.Abstractions/Components/ISlideToggle.cs b/src/Util.Ui.Abstractions/Components/ISlideToggle.cs deleted file mode 100644 index 404d11e80..000000000 --- a/src/Util.Ui.Abstractions/Components/ISlideToggle.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Util.Ui.Operations; -using Util.Ui.Operations.Events; -using Util.Ui.Operations.Forms; -using Util.Ui.Operations.Forms.Validations; -using Util.Ui.Operations.Styles; - -namespace Util.Ui.Components { - /// - /// 滑动开关 - /// - public interface ISlideToggle : IComponent, IName, ILabel, IDisabled, IModel, IRequired, IOnChange, ILabelPosition, IColor { - } -} \ No newline at end of file diff --git a/src/Util.Ui.Abstractions/Components/ITextBox.cs b/src/Util.Ui.Abstractions/Components/ITextBox.cs deleted file mode 100644 index c8de26384..000000000 --- a/src/Util.Ui.Abstractions/Components/ITextBox.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Util.Ui.Operations.Forms; -using Util.Ui.Operations.Forms.Validations; - -namespace Util.Ui.Components { - /// - /// 文本框 - /// - public interface ITextBox : IFormControl, IMinLength, IMaxLength, IReadOnly { - } -} diff --git a/src/Util.Ui.Abstractions/Components/Internal/IOptionConfig.cs b/src/Util.Ui.Abstractions/Components/Internal/IOptionConfig.cs deleted file mode 100644 index db230dc22..000000000 --- a/src/Util.Ui.Abstractions/Components/Internal/IOptionConfig.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using Util.Ui.Configs; - -namespace Util.Ui.Components.Internal { - /// - /// 组件配置 - /// - public interface IOptionConfig { - /// - /// 配置 - /// - /// 配置类型 - /// 配置操作 - void Config( Action configAction ) where TConfig : IConfig; - } -} diff --git a/src/Util.Ui.Abstractions/Components/Internal/IRenderEnd.cs b/src/Util.Ui.Abstractions/Components/Internal/IRenderEnd.cs deleted file mode 100644 index 63aa1bad7..000000000 --- a/src/Util.Ui.Abstractions/Components/Internal/IRenderEnd.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Util.Ui.Components.Internal { - /// - /// 容器渲染结束操作 - /// - public interface IRenderEnd { - /// - /// 渲染结束 - /// - void End(); - } -} diff --git a/src/Util.Ui.Abstractions/Configs/IConfig.cs b/src/Util.Ui.Abstractions/Configs/IConfig.cs deleted file mode 100644 index 8a8bb7192..000000000 --- a/src/Util.Ui.Abstractions/Configs/IConfig.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Collections.Generic; -using Microsoft.AspNetCore.Html; -using Microsoft.AspNetCore.Razor.TagHelpers; - -namespace Util.Ui.Configs { - /// - /// 基础配置 - /// - public interface IConfig { - /// - /// 全部属性集合 - /// - TagHelperAttributeList AllAttributes { get; } - /// - /// 输出属性集合,TagHelper中未明确定义的属性从该集合获取 - /// - TagHelperAttributeList OutputAttributes { get; } - /// - /// 内容 - /// - IHtmlContent Content { get; set; } - /// - /// 属性集合是否包含指定属性 - /// - /// 属性名 - bool Contains( string name ); - /// - /// 获取属性值 - /// - /// 属性名 - string GetValue( string name ); - /// - /// 获取属性值 - /// - /// 目标类型 - /// 属性名 - T GetValue( string name ); - /// - /// 获取布尔属性值 - /// - /// 属性名 - string GetBoolValue( string name ); - /// - /// 设置属性 - /// - /// 属性名 - /// 值 - void SetAttribute( string name, object value ); - /// - /// 移除属性 - /// - /// 属性名 - void Remove( string name ); - /// - /// 添加类 - /// - void AddClass( string @class ); - /// - /// 获取类列表 - /// - List GetClassList(); - /// - /// 验证 - /// - string Validate(); - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Datas/IDataSource.cs b/src/Util.Ui.Abstractions/Operations/Datas/IDataSource.cs deleted file mode 100644 index 6585f307c..000000000 --- a/src/Util.Ui.Abstractions/Operations/Datas/IDataSource.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Datas { - /// - /// 数据源 - /// - public interface IDataSource { - } -} \ No newline at end of file diff --git a/src/Util.Ui.Abstractions/Operations/Datas/IItem.cs b/src/Util.Ui.Abstractions/Operations/Datas/IItem.cs deleted file mode 100644 index 9fa2ec124..000000000 --- a/src/Util.Ui.Abstractions/Operations/Datas/IItem.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Datas { - /// - /// 列表项 - /// - public interface IItem { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Datas/IUrl.cs b/src/Util.Ui.Abstractions/Operations/Datas/IUrl.cs deleted file mode 100644 index aae7f525a..000000000 --- a/src/Util.Ui.Abstractions/Operations/Datas/IUrl.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Datas { - /// - /// Url - /// - public interface IUrl { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Effects/ISpin.cs b/src/Util.Ui.Abstractions/Operations/Effects/ISpin.cs deleted file mode 100644 index 26d8a4ae3..000000000 --- a/src/Util.Ui.Abstractions/Operations/Effects/ISpin.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Effects { - /// - /// 旋转 - /// - public interface ISpin { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Events/IOnBlur.cs b/src/Util.Ui.Abstractions/Operations/Events/IOnBlur.cs deleted file mode 100644 index 47247e0a0..000000000 --- a/src/Util.Ui.Abstractions/Operations/Events/IOnBlur.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Events { - /// - /// 失去焦点事件 - /// - public interface IOnBlur { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Events/IOnChange.cs b/src/Util.Ui.Abstractions/Operations/Events/IOnChange.cs deleted file mode 100644 index cf788a477..000000000 --- a/src/Util.Ui.Abstractions/Operations/Events/IOnChange.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Events { - /// - /// 变更事件 - /// - public interface IOnChange { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Events/IOnClick.cs b/src/Util.Ui.Abstractions/Operations/Events/IOnClick.cs deleted file mode 100644 index 9d3a892c0..000000000 --- a/src/Util.Ui.Abstractions/Operations/Events/IOnClick.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Events { - /// - /// 单击事件 - /// - public interface IOnClick { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Events/IOnFocus.cs b/src/Util.Ui.Abstractions/Operations/Events/IOnFocus.cs deleted file mode 100644 index bac746fd3..000000000 --- a/src/Util.Ui.Abstractions/Operations/Events/IOnFocus.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Events { - /// - /// 获得焦点事件 - /// - public interface IOnFocus { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Events/IOnKeydown.cs b/src/Util.Ui.Abstractions/Operations/Events/IOnKeydown.cs deleted file mode 100644 index dc45fd286..000000000 --- a/src/Util.Ui.Abstractions/Operations/Events/IOnKeydown.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Events { - /// - /// 键盘按下事件 - /// - public interface IOnKeydown { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Events/IOnKeyup.cs b/src/Util.Ui.Abstractions/Operations/Events/IOnKeyup.cs deleted file mode 100644 index be4570343..000000000 --- a/src/Util.Ui.Abstractions/Operations/Events/IOnKeyup.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Events { - /// - /// 键盘按键事件 - /// - public interface IOnKeyup { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Events/IOnSubmit.cs b/src/Util.Ui.Abstractions/Operations/Events/IOnSubmit.cs deleted file mode 100644 index b9050bb34..000000000 --- a/src/Util.Ui.Abstractions/Operations/Events/IOnSubmit.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Events { - /// - /// 提交事件 - /// - public interface IOnSubmit { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Forms/IHint.cs b/src/Util.Ui.Abstractions/Operations/Forms/IHint.cs deleted file mode 100644 index 66e813586..000000000 --- a/src/Util.Ui.Abstractions/Operations/Forms/IHint.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Forms { - /// - /// 提示 - /// - public interface IHint { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Forms/ILabelPosition.cs b/src/Util.Ui.Abstractions/Operations/Forms/ILabelPosition.cs deleted file mode 100644 index 3856ccbd9..000000000 --- a/src/Util.Ui.Abstractions/Operations/Forms/ILabelPosition.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Forms { - /// - /// 标签位置 - /// - public interface ILabelPosition { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Forms/IModel.cs b/src/Util.Ui.Abstractions/Operations/Forms/IModel.cs deleted file mode 100644 index 8a8b8cddd..000000000 --- a/src/Util.Ui.Abstractions/Operations/Forms/IModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Forms { - /// - /// 模型绑定 - /// - public interface IModel { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Forms/IMultiple.cs b/src/Util.Ui.Abstractions/Operations/Forms/IMultiple.cs deleted file mode 100644 index 9e68727dc..000000000 --- a/src/Util.Ui.Abstractions/Operations/Forms/IMultiple.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Forms { - /// - /// 多选 - /// - public interface IMultiple { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Forms/IPlaceholder.cs b/src/Util.Ui.Abstractions/Operations/Forms/IPlaceholder.cs deleted file mode 100644 index ce3287ec9..000000000 --- a/src/Util.Ui.Abstractions/Operations/Forms/IPlaceholder.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Forms { - /// - /// 占位提示符 - /// - public interface IPlaceholder { - } -} \ No newline at end of file diff --git a/src/Util.Ui.Abstractions/Operations/Forms/IPrefix.cs b/src/Util.Ui.Abstractions/Operations/Forms/IPrefix.cs deleted file mode 100644 index 97e36db02..000000000 --- a/src/Util.Ui.Abstractions/Operations/Forms/IPrefix.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Forms { - /// - /// 前缀 - /// - public interface IPrefix { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Forms/IReadOnly.cs b/src/Util.Ui.Abstractions/Operations/Forms/IReadOnly.cs deleted file mode 100644 index d481cde98..000000000 --- a/src/Util.Ui.Abstractions/Operations/Forms/IReadOnly.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Forms { - /// - /// 只读 - /// - public interface IReadOnly { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Forms/IResetOption.cs b/src/Util.Ui.Abstractions/Operations/Forms/IResetOption.cs deleted file mode 100644 index 89585c27f..000000000 --- a/src/Util.Ui.Abstractions/Operations/Forms/IResetOption.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Forms { - /// - /// 重置项 - /// - public interface IResetOption { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Forms/ISuffix.cs b/src/Util.Ui.Abstractions/Operations/Forms/ISuffix.cs deleted file mode 100644 index ee7cd9ebf..000000000 --- a/src/Util.Ui.Abstractions/Operations/Forms/ISuffix.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Forms { - /// - /// 后缀 - /// - public interface ISuffix { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Forms/Validations/IMaxLength.cs b/src/Util.Ui.Abstractions/Operations/Forms/Validations/IMaxLength.cs deleted file mode 100644 index 98db34f9f..000000000 --- a/src/Util.Ui.Abstractions/Operations/Forms/Validations/IMaxLength.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Forms.Validations { - /// - /// 最大长度验证 - /// - public interface IMaxLength { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Forms/Validations/IMinLength.cs b/src/Util.Ui.Abstractions/Operations/Forms/Validations/IMinLength.cs deleted file mode 100644 index 4a514b3f1..000000000 --- a/src/Util.Ui.Abstractions/Operations/Forms/Validations/IMinLength.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Forms.Validations { - /// - /// 最小长度验证 - /// - public interface IMinLength { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Forms/Validations/IRequired.cs b/src/Util.Ui.Abstractions/Operations/Forms/Validations/IRequired.cs deleted file mode 100644 index 993c93637..000000000 --- a/src/Util.Ui.Abstractions/Operations/Forms/Validations/IRequired.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Forms.Validations { - /// - /// 必填项验证 - /// - public interface IRequired { - } -} \ No newline at end of file diff --git a/src/Util.Ui.Abstractions/Operations/IAttribute.cs b/src/Util.Ui.Abstractions/Operations/IAttribute.cs deleted file mode 100644 index d893dfab5..000000000 --- a/src/Util.Ui.Abstractions/Operations/IAttribute.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations { - /// - /// 属性 - /// - public interface IAttribute { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/IDisabled.cs b/src/Util.Ui.Abstractions/Operations/IDisabled.cs deleted file mode 100644 index afccf5d1c..000000000 --- a/src/Util.Ui.Abstractions/Operations/IDisabled.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations { - /// - /// 禁用 - /// - public interface IDisabled { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/IHeight.cs b/src/Util.Ui.Abstractions/Operations/IHeight.cs deleted file mode 100644 index b9512c23f..000000000 --- a/src/Util.Ui.Abstractions/Operations/IHeight.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations { - /// - /// 高度 - /// - public interface IHeight { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/ILabel.cs b/src/Util.Ui.Abstractions/Operations/ILabel.cs deleted file mode 100644 index c0868b508..000000000 --- a/src/Util.Ui.Abstractions/Operations/ILabel.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations { - /// - /// 标签 - /// - public interface ILabel { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/IName.cs b/src/Util.Ui.Abstractions/Operations/IName.cs deleted file mode 100644 index 3d69df520..000000000 --- a/src/Util.Ui.Abstractions/Operations/IName.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations { - /// - /// 名称 - /// - public interface IName { - } -} \ No newline at end of file diff --git a/src/Util.Ui.Abstractions/Operations/ISetIcon.cs b/src/Util.Ui.Abstractions/Operations/ISetIcon.cs deleted file mode 100644 index 5dd6953b2..000000000 --- a/src/Util.Ui.Abstractions/Operations/ISetIcon.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations { - /// - /// 设置图标 - /// - public interface ISetIcon { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/IText.cs b/src/Util.Ui.Abstractions/Operations/IText.cs deleted file mode 100644 index 013e66240..000000000 --- a/src/Util.Ui.Abstractions/Operations/IText.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations { - /// - /// 文本 - /// - public interface IText { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/ITooltip.cs b/src/Util.Ui.Abstractions/Operations/ITooltip.cs deleted file mode 100644 index 533ef0b51..000000000 --- a/src/Util.Ui.Abstractions/Operations/ITooltip.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations { - /// - /// 提示 - /// - public interface ITooltip { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Navigation/ILink.cs b/src/Util.Ui.Abstractions/Operations/Navigation/ILink.cs deleted file mode 100644 index 35024fe1e..000000000 --- a/src/Util.Ui.Abstractions/Operations/Navigation/ILink.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Navigation { - /// - /// 链接 - /// - public interface ILink { - } -} \ No newline at end of file diff --git a/src/Util.Ui.Abstractions/Operations/Navigation/IMenuId.cs b/src/Util.Ui.Abstractions/Operations/Navigation/IMenuId.cs deleted file mode 100644 index 3271d8150..000000000 --- a/src/Util.Ui.Abstractions/Operations/Navigation/IMenuId.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Navigation { - /// - /// 菜单标识 - /// - public interface IMenuId { - } -} \ No newline at end of file diff --git a/src/Util.Ui.Abstractions/Operations/Styles/IBackgroundColor.cs b/src/Util.Ui.Abstractions/Operations/Styles/IBackgroundColor.cs deleted file mode 100644 index af116e770..000000000 --- a/src/Util.Ui.Abstractions/Operations/Styles/IBackgroundColor.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Styles { - /// - /// 背景色 - /// - public interface IBackgroundColor { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Styles/IButtonStyle.cs b/src/Util.Ui.Abstractions/Operations/Styles/IButtonStyle.cs deleted file mode 100644 index 16afa1c3b..000000000 --- a/src/Util.Ui.Abstractions/Operations/Styles/IButtonStyle.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Styles { - /// - /// 按钮样式 - /// - public interface IButtonStyle { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Styles/IClass.cs b/src/Util.Ui.Abstractions/Operations/Styles/IClass.cs deleted file mode 100644 index 54d0d6aff..000000000 --- a/src/Util.Ui.Abstractions/Operations/Styles/IClass.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Styles { - /// - /// 类 - /// - public interface IClass { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Styles/IColor.cs b/src/Util.Ui.Abstractions/Operations/Styles/IColor.cs deleted file mode 100644 index 6b6cbad04..000000000 --- a/src/Util.Ui.Abstractions/Operations/Styles/IColor.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Styles { - /// - /// 颜色 - /// - public interface IColor { - } -} diff --git a/src/Util.Ui.Abstractions/Operations/Styles/IStyle.cs b/src/Util.Ui.Abstractions/Operations/Styles/IStyle.cs deleted file mode 100644 index a64151055..000000000 --- a/src/Util.Ui.Abstractions/Operations/Styles/IStyle.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Styles { - /// - /// 样式 - /// - public interface IStyle { - } -} diff --git a/src/Util.Ui.Abstractions/Renders/IRender.cs b/src/Util.Ui.Abstractions/Renders/IRender.cs deleted file mode 100644 index 28e10b74d..000000000 --- a/src/Util.Ui.Abstractions/Renders/IRender.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.IO; -using Microsoft.AspNetCore.Html; - -namespace Util.Ui.Renders { - /// - /// 渲染器 - /// - public interface IRender : IHtmlContent { - /// - /// 渲染起始标签 - /// - /// 流写入器 - void RenderStartTag( TextWriter writer ); - /// - /// 渲染结束标签 - /// - /// 流写入器 - void RenderEndTag( TextWriter writer ); - } -} \ No newline at end of file diff --git a/src/Util.Ui.Abstractions/Services/IContext.cs b/src/Util.Ui.Abstractions/Services/IContext.cs deleted file mode 100644 index c4c31413d..000000000 --- a/src/Util.Ui.Abstractions/Services/IContext.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.AspNetCore.Mvc.Rendering; - -namespace Util.Ui.Services { - /// - /// Ui上下文 - /// - public interface IContext { - /// - /// HtmlHelper - /// - IHtmlHelper Helper { get; } - } -} diff --git a/src/Util.Ui.Angular/02-Util.Ui.Angular.csproj b/src/Util.Ui.Angular/02-Util.Ui.Angular.csproj new file mode 100644 index 000000000..5a3d18120 --- /dev/null +++ b/src/Util.Ui.Angular/02-Util.Ui.Angular.csproj @@ -0,0 +1,32 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Ui.Angular是Util应用框架的Angular封装基类库 + Util.Ui.Angular + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Ui.Angular.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Ui.Angular.xml + + + + + True + False + + + + + + + + + diff --git a/src/Util.Ui.Angular/03-Util.Ui.Angular.csproj b/src/Util.Ui.Angular/03-Util.Ui.Angular.csproj deleted file mode 100644 index 66ec104d6..000000000 --- a/src/Util.Ui.Angular/03-Util.Ui.Angular.csproj +++ /dev/null @@ -1,44 +0,0 @@ - - - - netstandard2.0 - Util.Ui.Angular - Util.Ui - 0.2.2-preview - 何镇汐 - 何镇汐 - Util.Ui.Angular是Util应用框架的Angular组件类库,集成封装Material,PrimeNG等组件。 -Util是一个.net core平台下的应用框架,旨在提升小型团队的开发输出能力,由常用公共操作类(工具类)、分层架构基类、Ui组件,第三方组件封装,第三方业务接口封装,配套代码生成模板,权限等组成。 - 何镇汐 - https://github.com/dotnetcore/Util/blob/master/LICENSE.txt - https://github.com/dotnetcore/Util - https://avatars2.githubusercontent.com/u/19421630 - https://github.com/dotnetcore/Util - git - util;applicationframework;dotnetcore;aspnetcore; - Util应用框架以MIT开源发布,可随意使用 - 0.2.2.0 - 0.2.2.0 - false - - - - ..\..\output\release - ..\..\output\release\netstandard2.0\Util.Ui.Angular.xml - - - - ..\..\output\release - ..\..\output\release\netstandard2.0\Util.Ui.Angular.xml - - - - - - - - - - - - diff --git a/src/Util.Ui.Angular/Angular/Builders/ContainerBuilder.cs b/src/Util.Ui.Angular/Angular/Builders/ContainerBuilder.cs deleted file mode 100644 index 4d5575b59..000000000 --- a/src/Util.Ui.Angular/Angular/Builders/ContainerBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Angular.Builders { - /// - /// ng-container生成器 - /// - public class ContainerBuilder : TagBuilder { - /// - /// 初始化ng-container生成器 - /// - public ContainerBuilder() : base( "ng-container" ) { - } - } -} diff --git a/src/Util.Ui.Angular/Angular/Builders/RouterOutletBuilder.cs b/src/Util.Ui.Angular/Angular/Builders/RouterOutletBuilder.cs deleted file mode 100644 index 641bc5e5d..000000000 --- a/src/Util.Ui.Angular/Angular/Builders/RouterOutletBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Angular.Builders { - /// - /// router-outlet生成器 - /// - public class RouterOutletBuilder : TagBuilder { - /// - /// 初始化router-outlet生成器 - /// - public RouterOutletBuilder() : base( "router-outlet" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Angular/Builders/TemplateBuilder.cs b/src/Util.Ui.Angular/Angular/Builders/TemplateBuilder.cs deleted file mode 100644 index f5d41c315..000000000 --- a/src/Util.Ui.Angular/Angular/Builders/TemplateBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Angular.Builders { - /// - /// ng-template模板生成器 - /// - public class TemplateBuilder : TagBuilder { - /// - /// 初始化ng-template模板生成器 - /// - public TemplateBuilder() : base( "ng-template" ) { - } - } -} diff --git a/src/Util.Ui.Angular/Angular/Renders/ContainerRender.cs b/src/Util.Ui.Angular/Angular/Renders/ContainerRender.cs deleted file mode 100644 index 1ac64ac2a..000000000 --- a/src/Util.Ui.Angular/Angular/Renders/ContainerRender.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Util.Ui.Angular.Builders; -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Extensions; -using Util.Ui.Renders; - -namespace Util.Ui.Angular.Renders { - /// - /// ng-container容器渲染器 - /// - public class ContainerRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化ng-container容器渲染器 - /// - /// 配置 - public ContainerRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new ContainerBuilder(); - builder.AddAttribute( "id", _config.GetValue( UiConst.Id ) ); - builder.SetContent( _config.Content ); - builder.AddOutputAttributes( _config ); - return builder; - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Angular/Renders/RouterOutletRender.cs b/src/Util.Ui.Angular/Angular/Renders/RouterOutletRender.cs deleted file mode 100644 index 73d139905..000000000 --- a/src/Util.Ui.Angular/Angular/Renders/RouterOutletRender.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Util.Ui.Angular.Builders; -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Renders; - -namespace Util.Ui.Angular.Renders { - /// - /// router-outlet路由出口渲染器 - /// - public class RouterOutletRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化router-outlet路由出口渲染器 - /// - /// 配置 - public RouterOutletRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - return new RouterOutletBuilder(); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Angular/TagHelpers/ContainerTagHelper.cs b/src/Util.Ui.Angular/Angular/TagHelpers/ContainerTagHelper.cs deleted file mode 100644 index 8d5c47b82..000000000 --- a/src/Util.Ui.Angular/Angular/TagHelpers/ContainerTagHelper.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Angular.Renders; -using Util.Ui.Configs; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Angular.TagHelpers { - /// - /// ng-container容器 - /// - [HtmlTargetElement( "util-container" )] - public class ContainerTagHelper : TagHelperBase { - /// - /// 渲染器 - /// - private ContainerRender _render; - - /// - /// 渲染 - /// - public override void Process( TagHelperContext context, TagHelperOutput output ) { - base.Process( context, output ); - if( _render != null ) - WriteLog( _render, "渲染ng-container容器" ); - } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - _render = new ContainerRender( new Config( context ) ); - return _render; - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Angular/TagHelpers/RouterOutletTagHelper.cs b/src/Util.Ui.Angular/Angular/TagHelpers/RouterOutletTagHelper.cs deleted file mode 100644 index e071c38fa..000000000 --- a/src/Util.Ui.Angular/Angular/TagHelpers/RouterOutletTagHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Angular.Renders; -using Util.Ui.Configs; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Angular.TagHelpers { - /// - /// router-outlet路由出口 - /// - [HtmlTargetElement( "util-router-outlet" )] - public class RouterOutletTagHelper : TagHelperBase { - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new RouterOutletRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Builders/AngularTagBuilder.cs b/src/Util.Ui.Angular/Builders/AngularTagBuilder.cs new file mode 100644 index 000000000..d4e0a3946 --- /dev/null +++ b/src/Util.Ui.Angular/Builders/AngularTagBuilder.cs @@ -0,0 +1,65 @@ +using Microsoft.AspNetCore.Mvc.Rendering; +using Util.Ui.Angular.Extensions; +using Util.Ui.Configs; +using Util.Ui.Extensions; + +namespace Util.Ui.Angular.Builders; + +/// +/// Angular标签生成器 +/// +public abstract class AngularTagBuilder : Util.Ui.Builders.TagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化Angular标签生成器 + /// + /// 配置 + /// 标签名称,范例:div + /// 渲染模式 + protected AngularTagBuilder( Config config, string tagName, TagRenderMode renderMode = TagRenderMode.Normal ) : base( tagName, renderMode ) { + _config = config ?? throw new ArgumentNullException( nameof( config ) ); + } + + /// + public override void Config() { + ConfigBase( _config ); + ConfigContent( _config ); + } + + /// + /// 基础配置 + /// + /// 配置 + public override void ConfigBase( Config config ) { + base.ConfigBase( config ); + this.Angular( config ); + ConfigId( config ); + ConfigOnContextmenu( config ); + } + + /// + /// 配置标识 + /// + protected virtual void ConfigId( Config config ) { + this.RawId( config ); + this.Id( config ); + } + + /// + /// 配置右键上下文菜单事件 + /// + protected virtual void ConfigOnContextmenu( Config config ) { + AttributeIfNotEmpty( "(contextmenu)", _config.GetValue( UiConst.OnContextmenu ) ); + } + + /// + /// 配置内容元素 + /// + protected virtual void ConfigContent( Config config ) { + config.Content.AppendTo( this ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Configs/AngularConst.cs b/src/Util.Ui.Angular/Configs/AngularConst.cs new file mode 100644 index 000000000..6119edfd2 --- /dev/null +++ b/src/Util.Ui.Angular/Configs/AngularConst.cs @@ -0,0 +1,1843 @@ +namespace Util.Ui.Angular.Configs; + +/// +/// Angular常量 +/// +public static class AngularConst { + /// + /// 原始Id + /// + public const string RawId = "raw-id"; + /// + /// ngIf指令 + /// + public const string NgIf = "ng-if"; + /// + /// ngSwitch指令 + /// + public const string NgSwitch = "ng-switch"; + /// + /// *ngSwitchCase指令 + /// + public const string NgSwitchCase = "ng-switch-case"; + /// + /// *ngSwitchDefault指令 + /// + public const string NgSwitchDefault = "ng-switch-default"; + /// + /// *ngTemplateOutlet指令 + /// + public const string NgTemplateOutlet = "ng-template-outlet"; + /// + /// ngTemplateOutletContext + /// + public const string NgTemplateOutletContext = "ng-template-outlet-context"; + /// + /// ngFor指令 + /// + public const string NgFor = "ng-for"; + /// + /// ngClass指令 + /// + public const string NgClass = "ng-class"; + /// + /// ngStyle指令 + /// + public const string NgStyle = "ng-style"; + /// + /// ngModel指令 + /// + public const string NgModel = "ng-model"; + /// + /// 路由链接 + /// + public const string BindLink = "bind-link"; + /// + /// 触发操作 + /// + public const string BindTriggerAction = "bind-trigger-action"; + /// + /// 可选 + /// + public const string BindOptional = "bind-optional"; + /// + /// 当前锚点 + /// + public const string BindCurrentAnchor = "bind-current-anchor"; + /// + /// 可选帮助 + /// + public const string BindOptionalHelp = "bind-optional-help"; + /// + /// 可选帮助颜色 + /// + public const string BindOptionalHelpColor = "bind-optional-help-color"; + /// + /// 弹出确认自动聚焦 + /// + public const string BindPopconfirmAutoFocus = "bind-popconfirm-auto-focus"; + /// + /// 间距 + /// + public const string BindGutter = "bind-gutter"; + /// + /// 可见高度 + /// + public const string BindVisibilityHeight = "bind-visibility-height"; + /// + /// 持续时间 + /// + public const string BindDuration = "bind-duration"; + /// + /// Logo + /// + public const string BindLogo = "bind-logo"; + /// + /// 等级 + /// + public const string BindLevel = "bind-level"; + /// + /// 图片 + /// + public const string BindImage = "bind-image"; + /// + /// 填充 + /// + public const string BindPadding = "bind-padding"; + /// + /// Html + /// + public const string BindHtml = "bind-html"; + /// + /// 背景色 + /// + public const string BindBgColor = "bind-bg-color"; + /// + /// 访问控制 + /// + public const string BindAcl = "bind-acl"; + /// + /// 标签文本 + /// + public const string BindLabelText = "bind-label-text"; + /// + /// 触发器 + /// + public const string BindTrigger = "bind-trigger"; + /// + /// 首页 + /// + public const string BindHome = "bind-home"; + /// + /// 首页链接 + /// + public const string BindHomeLink = "bind-home-link"; + /// + /// 首页国际化 + /// + public const string BindHomeI18n = "bind-home-i18n"; + /// + /// 自动面包屑 + /// + public const string BindAutoBreadcrumb = "bind-auto-breadcrumb"; + /// + /// 递归面包屑 + /// + public const string BindRecursiveBreadcrumb = "bind-recursive-breadcrumb"; + /// + /// 禁用透明度 + /// + public const string BindDisabledAlpha = "bind-disabled-alpha"; + /// + /// 定宽 + /// + public const string BindWide = "bind-wide"; + /// + /// 固定 + /// + public const string BindFixed = "bind-fixed"; + /// + /// 显示文本 + /// + public const string BindShowText = "bind-show-text"; + /// + /// 行数 + /// + public const string BindLines = "bind-lines"; + /// + /// 长度 + /// + public const string BindLength = "bind-length"; + /// + /// 全宽识别 + /// + public const string BindFullWidthRecognition = "bind-full-width-recognition"; + /// + /// 尾巴 + /// + public const string BindTail = "bind-tail"; + /// + /// 名称 + /// + public const string BindName = "bind-name"; + /// + /// 标题 + /// + public const string BindTitle = "bind-title"; + /// + /// 自动设置标题 + /// + public const string BindAutoTitle = "bind-auto-title"; + /// + /// 同步标题 + /// + public const string BindSyncTitle = "bind-sync-title"; + /// + /// 文本 + /// + public const string BindText = "bind-text"; + /// + /// 默认值 + /// + public const string BindDefaultValue = "bind-default-value"; + /// + /// 值 + /// + public const string BindValue = "bind-value"; + /// + /// 工具提示 + /// + public const string BindTooltip = "bind-tooltip"; + /// + /// 提示 + /// + public const string BindPlaceholder = "bind-placeholder"; + /// + /// 标签 + /// + public const string BindLabel = "bind-label"; + /// + /// 活动 + /// + public const string BindActive = "bind-active"; + /// + /// 单位 + /// + public const string BindUnit = "bind-unit"; + /// + /// 模式 + /// + public const string BindPattern = "bind-pattern"; + /// + /// FontAwesome图标 + /// + public const string BindFontAwesomeIcon = "bind-font-awesome-icon"; + /// + /// Material图标 + /// + public const string BindMaterialIcon = "bind-material-icon"; + /// + /// Url + /// + public const string BindUrl = "bind-url"; + /// + /// 删除Url + /// + public const string BindDeleteUrl = "bind-delete-url"; + /// + /// 加载地址 + /// + public const string BindLoadUrl = "bind-load-url"; + /// + /// 查询地址 + /// + public const string BindQueryUrl = "bind-query-url"; + /// + /// 加载子节点地址 + /// + public const string BindLoadChildrenUrl = "bind-load-children-url"; + /// + /// 上传Url + /// + public const string BindUploadUrl = "bind-upload-url"; + /// + /// 保存Url + /// + public const string BindSaveUrl = "bind-save-url"; + /// + /// 类型 + /// + public const string BindType = "bind-type"; + /// + /// 大小 + /// + public const string BindSize = "bind-size"; + /// + /// 主题 + /// + public const string BindTheme = "bind-theme"; + /// + /// 禁用 + /// + public const string BindDisabled = "bind-disabled"; + /// + /// 数据源 + /// + public const string BindData = "bind-data"; + /// + /// 消息 + /// + public const string BindMessage = "bind-message"; + /// + /// 颜色 + /// + public const string BindColor = "bind-color"; + /// + /// 形状 + /// + public const string BindShape = "bind-shape"; + /// + /// 持续旋转 + /// + public const string BindSpin = "bind-spin"; + /// + /// 旋转 + /// + public const string BindRotate = "bind-rotate"; + /// + /// 危险 + /// + public const string BindDanger = "bind-danger"; + /// + /// 加载 + /// + public const string BindLoading = "bind-loading"; + /// + /// 幽灵 + /// + public const string BindGhost = "bind-ghost"; + /// + /// 块 + /// + public const string BindBlock = "bind-block"; + /// + /// 可编辑 + /// + public const string BindEditable = "bind-editable"; + /// + /// 内容 + /// + public const string BindContent = "bind-content"; + /// + /// 内容 + /// + public const string BindonContent = "bindon-content"; + /// + /// 编辑图标 + /// + public const string BindEditIcon = "bind-edit-icon"; + /// + /// 编辑提示 + /// + public const string BindEditTooltip = "bind-edit-tooltip"; + /// + /// 可复制 + /// + public const string BindCopyable = "bind-copyable"; + /// + /// 复制文本 + /// + public const string BindCopyText = "bind-copy-text"; + /// + /// 省略 + /// + public const string BindEllipsis = "bind-ellipsis"; + /// + /// 可展开 + /// + public const string BindExpandable = "bind-expandable"; + /// + /// 省略行数 + /// + public const string BindEllipsisRows = "bind-ellipsis-rows"; + /// + /// 后缀 + /// + public const string BindSuffix = "bind-suffix"; + /// + /// 虚线 + /// + public const string BindDashed = "bind-dashed"; + /// + /// 方向 + /// + public const string BindOrientation = "bind-orientation"; + /// + /// 平淡 + /// + public const string BindPlain = "bind-plain"; + /// + /// 跨度 + /// + public const string BindSpan = "bind-span"; + /// + /// 偏移 + /// + public const string BindOffset = "bind-offset"; + /// + /// 拉 + /// + public const string BindPull = "bind-pull"; + /// + /// 推 + /// + public const string BindPush = "bind-push"; + /// + /// 对齐 + /// + public const string BindJustify = "bind-justify"; + /// + /// 对齐 + /// + public const string BindAlign = "bind-align"; + /// + /// 标题对齐 + /// + public const string BindTitleAlign = "bind-title-align"; + /// + /// 序号 + /// + public const string BindOrder = "bind-order"; + /// + /// flex + /// + public const string BindFlex = "bind-flex"; + /// + /// 宽度 + /// + public const string BindWidth = "bind-width"; + /// + /// 最小宽度 + /// + public const string BindMinWidth = "bind-min-width"; + /// + /// 最大宽度 + /// + public const string BindMaxWidth = "bind-max-width"; + /// + /// 高度 + /// + public const string BindHeight = "bind-height"; + /// + /// 断点 + /// + public const string BindBreakpoint = "bind-breakpoint"; + /// + /// 收缩宽度 + /// + public const string BindCollapsedWidth = "bind-collapsed-width"; + /// + /// 可收缩 + /// + public const string BindCollapsible = "bind-collapsible"; + /// + /// 收缩 + /// + public const string BindCollapsed = "bind-collapsed"; + /// + /// 收缩 + /// + public const string BindonCollapsed = "bindon-collapsed"; + /// + /// 翻转箭头 + /// + public const string BindReverseArrow = "bind-reverse-arrow"; + /// + /// 方向 + /// + public const string BindDirection = "bindon-direction"; + /// + /// 顶部偏移量 + /// + public const string BindOffsetTop = "bind-offset-top"; + /// + /// 底部偏移量 + /// + public const string BindOffsetBottom = "bind-offset-bottom"; + /// + /// 路由链接 + /// + public const string RouterLink = "router-link"; + /// + /// 路由链接 + /// + public const string BindRouterLink = "bind-router-link"; + /// + /// 活动路由链接 + /// + public const string RouterLinkActive = "router-link-active"; + /// + /// 活动路由链接 + /// + public const string BindRouterLinkActive = "bind-router-link-active"; + /// + /// 分隔符 + /// + public const string BindSeparator = "bind-separator"; + /// + /// 查询参数处理方式 + /// + public const string BindQueryParamsHandling = "bind-query-params-handling"; + /// + /// 自动生成 + /// + public const string BindAutoGenerate = "bind-auto-generate"; + /// + /// 位置 + /// + public const string BindPlacement = "bind-placement"; + /// + /// 下拉菜单位置 + /// + public const string BindDropdownMenuPlacement = "bind-dropdown-menu-placement"; + /// + /// 下拉菜单触发方式 + /// + public const string BindDropdownMenuTrigger = "bind-dropdown-menu-trigger"; + /// + /// 点击隐藏下拉菜单 + /// + public const string BindDropdownMenuClickHide = "bind-dropdown-menu-click-hide"; + /// + /// 下拉菜单可见性 + /// + public const string BindDropdownMenuVisible = "bind-dropdown-menu-visible"; + /// + /// 下拉菜单可见性 + /// + public const string BindonDropdownMenuVisible = "bindon-dropdown-menu-visible"; + /// + /// 下拉菜单根元素类名 + /// + public const string BindDropdownMenuOverlayClassName = "bind-dropdown-menu-overlay-class-name"; + /// + /// 下拉菜单根元素样式 + /// + public const string BindDropdownMenuOverlayStyle = "bind-dropdown-menu-overlay-style"; + /// + /// 模式 + /// + public const string BindMode = "bind-mode"; + /// + /// 选中 + /// + public const string BindSelected = "bind-selected"; + /// + /// 图标 + /// + public const string BindIcon = "bind-icon"; + /// + /// 图标尺寸 + /// + public const string BindIconSize = "bind-icon-size"; + /// + /// href + /// + public const string BindHref = "bind-href"; + /// + /// rel + /// + public const string BindRel = "bind-rel"; + /// + /// 目标 + /// + public const string BindTarget = "bind-target"; + /// + /// 打开 + /// + public const string BindOpen = "bind-open"; + /// + /// 打开 + /// + public const string BindonOpen = "bindon-open"; + /// + /// 内嵌折叠 + /// + public const string BindInlineCollapsed = "bind-inline-collapsed"; + /// + /// 匹配路由 + /// + public const string BindMatchRouter = "bind-match-router"; + /// + /// 精确匹配路由 + /// + public const string BindMatchRouterExact = "bind-match-router-exact"; + /// + /// 菜单类名 + /// + public const string BindMenuClassName = "bind-menu-class-name"; + /// + /// 内嵌缩进 + /// + public const string BindInlineIndent = "bind-inline-indent"; + /// + /// 允许选中 + /// + public const string BindSelectable = "bind-selectable"; + /// + /// 副标题 + /// + public const string BindSubtitle = "bind-subtitle"; + /// + /// 返回图标 + /// + public const string BindBackIcon = "bind-back-icon"; + /// + /// 总数 + /// + public const string BindTotal = "bind-total"; + /// + /// 当前页 + /// + public const string BindPageIndex = "bind-page-index"; + /// + /// 当前页 + /// + public const string BindonPageIndex = "bindon-page-index"; + /// + /// 是否显示大小选择 + /// + public const string BindShowSizeChanger = "bind-show-size-changer"; + /// + /// 每页大小 + /// + public const string BindPageSize = "bind-page-size"; + /// + /// 每页大小 + /// + public const string BindonPageSize = "bindon-page-size"; + /// + /// 显示快速跳转 + /// + public const string BindShowQuickJumper = "bind-show-quick-jumper"; + /// + /// 简单 + /// + public const string BindSimple = "bind-simple"; + /// + /// 响应式 + /// + public const string BindResponsive = "bind-responsive"; + /// + /// 一页隐藏 + /// + public const string BindHideOnSinglePage = "bind-hide-on-single-page"; + /// + /// 当前 + /// + public const string BindCurrent = "bind-current"; + /// + /// 描述 + /// + public const string BindDescription = "bind-description"; + /// + /// 起始索引 + /// + public const string BindStartIndex = "bind-start-index"; + /// + /// 状态 + /// + public const string BindStatus = "bind-status"; + /// + /// 点状进度 + /// + public const string BindProgressDot = "bind-progress-dot"; + /// + /// 标签位置 + /// + public const string BindLabelPlacement = "bind-label-placement"; + /// + /// 模型绑定 + /// + public const string BindNgModel = "bind-ng-model"; + /// + /// 前置添加 + /// + public const string BindAddOnBefore = "bind-add-on-before"; + /// + /// 后置添加 + /// + public const string BindAddOnAfter = "bind-add-on-after"; + /// + /// 添加前置图标 + /// + public const string BindAddOnBeforeIcon = "bind-add-on-before-icon"; + /// + /// 添加后置图标 + /// + public const string BindAddOnAfterIcon = "bind-add-on-after-icon"; + /// + /// 前缀 + /// + public const string BindPrefix = "bind-prefix"; + /// + /// 前缀图标 + /// + public const string BindPrefixIcon = "bind-prefix-icon"; + /// + /// 后缀图标 + /// + public const string BindSuffixIcon = "bind-suffix-icon"; + /// + /// 搜索 + /// + public const string BindSearch = "bind-search"; + /// + /// 自适应大小 + /// + public const string BindAutosize = "bind-autosize"; + /// + /// 布局 + /// + public const string BindLayout = "bind-layout"; + /// + /// 不显示冒号 + /// + public const string BindNoColon = "bind-no-colon"; + /// + /// 提示图标 + /// + public const string BindTooltipIcon = "bind-tooltip-icon"; + /// + /// 不显示冒号 + /// + public const string BindRequired = "bind-required"; + /// + /// NzFor + /// + public const string BindNzFor = "bind-nz-for"; + /// + /// 提示信息 + /// + public const string BindTooltipTitle = "bind-tooltip-title"; + /// + /// 额外 + /// + public const string BindExtra = "bind-extra"; + /// + /// 成功提示 + /// + public const string BindSuccessTip = "bind-success-tip"; + /// + /// 警告提示 + /// + public const string BindWarningTip = "bind-warning-tip"; + /// + /// 错误提示 + /// + public const string BindErrorTip = "bind-error-tip"; + /// + /// 校验中提示 + /// + public const string BindValidatingTip = "bind-validating-tip"; + /// + /// 表单控件名 + /// + public const string BindFormControlName = "bind-form-control-name"; + /// + /// 最大字符数 + /// + public const string BindMaxCharacterCount = "bind-max-character-count"; + /// + /// 行数 + /// + public const string BindRows = "bind-rows"; + /// + /// 列数 + /// + public const string BindColumns = "bind-columns"; + /// + /// 紧凑 + /// + public const string BindCompact = "bind-compact"; + /// + /// 回填 + /// + public const string BindBackfill = "bind-backfill"; + /// + /// 高亮第一项 + /// + public const string BindDefaultActiveFirstOption = "bind-default-active-first-option"; + /// + /// 根元素类名 + /// + public const string BindOverlayClassName = "bind-overlay-class-name"; + /// + /// 根元素样式 + /// + public const string BindOverlayStyle = "bind-overlay-style"; + /// + /// 允许清除 + /// + public const string BindAllowClear = "bind-allow-clear"; + /// + /// 自动获取焦点 + /// + public const string BindAutoFocus = "bind-auto-focus"; + /// + /// 选择变更 + /// + public const string BindChangeOnSelect = "bind-change-on-select"; + /// + /// 列类名 + /// + public const string BindColumnClassName = "bind-column-class-name"; + /// + /// 展开图标 + /// + public const string BindExpandIcon = "bind-expand-icon"; + /// + /// 展开方式 + /// + public const string BindExpandTrigger = "bind-expand-trigger"; + /// + /// 标签属性 + /// + public const string BindLabelProperty = "bind-label-property"; + /// + /// 菜单样式 + /// + public const string BindMenuStyle = "bind-menu-style"; + /// + /// 空内容 + /// + public const string BindNotFoundContent = "bind-not-found-content"; + /// + /// 显示箭头 + /// + public const string BindShowArrow = "bind-show-arrow"; + /// + /// 显示输入框 + /// + public const string BindShowInput = "bind-show-input"; + /// + /// 显示搜索 + /// + public const string BindShowSearch = "bind-show-search"; + /// + /// 值属性 + /// + public const string BindValueProperty = "bind-value-property"; + /// + /// 默认选中值 + /// + public const string BindDefaultPickerValue = "bind-default-picker-value"; + /// + /// 下拉样式类名 + /// + public const string BindDropdownClassName = "bind-dropdown-class-name"; + /// + /// 格式化 + /// + public const string BindFormat = "bind-format"; + /// + /// 只读 + /// + public const string BindInputReadonly = "bind-input-readonly"; + /// + /// 弹出样式 + /// + public const string BindPopupStyle = "bind-popup-style"; + /// + /// 额外页脚 + /// + public const string BindRenderExtraFooter = "bind-render-extra-footer"; + /// + /// 无边框 + /// + public const string BindBorderless = "bind-borderless"; + /// + /// 显示时间 + /// + public const string BindShowTime = "bind-show-time"; + /// + /// 显示今天 + /// + public const string BindShowToday = "bind-show-today"; + /// + /// 显示当前 + /// + public const string BindShowNow = "bind-show-now"; + /// + /// 最大值 + /// + public const string BindMax = "bind-max"; + /// + /// 最小值 + /// + public const string BindMin = "bind-min"; + /// + /// 精度 + /// + public const string BindPrecision = "bind-precision"; + /// + /// 精度模式 + /// + public const string BindPrecisionMode = "bind-precision-mode"; + /// + /// 步 + /// + public const string BindStep = "bind-step"; + /// + /// 输入模式 + /// + public const string BindInputMode = "bind-input-mode"; + /// + /// 只读 + /// + public const string BindReadonly = "bind-readonly"; + /// + /// 按钮样式 + /// + public const string BindButtonStyle = "bind-button-style"; + /// + /// 允许半选 + /// + public const string BindAllowHalf = "bind-allow-half"; + /// + /// 数量 + /// + public const string BindCount = "bind-count"; + /// + /// 自动清空搜索值 + /// + public const string BindAutoClearSearchValue = "bind-auto-clear-search-value"; + /// + /// 下拉菜单匹配选择宽度 + /// + public const string BindDropdownMatchSelectWidth = "bind-dropdown-match-select-width"; + /// + /// 下拉样式 + /// + public const string BindDropdownStyle = "bind-dropdown-style"; + /// + /// 服务端搜索 + /// + public const string BindServerSearch = "bind-server-search"; + /// + /// 最大多选数量 + /// + public const string BindMaxMultipleCount = "bind-max-multiple-count"; + /// + /// 最大标签数量 + /// + public const string BindMaxTagCount = "bind-max-tag-count"; + /// + /// 最大标签占位符 + /// + public const string BindMaxTagPlaceholder = "bind-max-tag-placeholder"; + /// + /// 选项高度 + /// + public const string BindOptionHeightPx = "bind-option-height-px"; + /// + /// 选项溢出数量 + /// + public const string BindOptionOverflowSize = "bind-option-overflow-size"; + /// + /// 隐藏 + /// + public const string BindHide = "bind-hide"; + /// + /// 自定义内容 + /// + public const string BindCustomContent = "bind-custom-content"; + /// + /// 点点 + /// + public const string BindDots = "bind-dots"; + /// + /// 包含 + /// + public const string BindIncluded = "bind-included"; + /// + /// 范围 + /// + public const string BindRange = "bind-range"; + /// + /// 垂直 + /// + public const string BindVertical = "bind-vertical"; + /// + /// 反向 + /// + public const string BindReverse = "bind-reverse"; + /// + /// 提示可见性 + /// + public const string BindTooltipVisible = "bind-tooltip-visible"; + /// + /// 提示位置 + /// + public const string BindTooltipPlacement = "bind-tooltip-placement"; + /// + /// 选中时内容 + /// + public const string BindCheckedChildren = "bind-checked-children"; + /// + /// 未选中时内容 + /// + public const string BindUncheckedChildren = "bind-unchecked-children"; + /// + /// 控制 + /// + public const string BindControl = "bind-control"; + /// + /// 允许清空 + /// + public const string BindAllowEmpty = "bind-allow-empty"; + /// + /// 清空文本 + /// + public const string BindClearText = "bind-clear-text"; + /// + /// 默认值 + /// + public const string BindDefaultOpenValue = "bind-default-open-value"; + /// + /// 隐藏禁用选项 + /// + public const string BindHideDisabledOptions = "bind-hide-disabled-options"; + /// + /// 小时间隔 + /// + public const string BindHourStep = "bind-hour-step"; + /// + /// 分钟间隔 + /// + public const string BindMinuteStep = "bind-minute-step"; + /// + /// 秒间隔 + /// + public const string BindSecondStep = "bind-second-step"; + /// + /// 弹出层样式类名 + /// + public const string BindPopupClassName = "bind-popup-class-name"; + /// + /// 12小时制 + /// + public const string BindUse12Hours = "bind-use12-hours"; + /// + /// 项目单位 + /// + public const string BindItemUnit = "bind-item-unit"; + /// + /// 项目列表单位 + /// + public const string BindItemsUnit = "bind-items-unit"; + /// + /// 搜索占位提示 + /// + public const string BindSearchPlaceholder = "bind-search-placeholder"; + /// + /// 显示图标 + /// + public const string BindShowIcon = "bind-show-icon"; + /// + /// 多选 + /// + public const string BindMultiple = "bind-multiple"; + /// + /// 隐藏未匹配 + /// + public const string BindHideUnmatched = "bind-hide-unmatched"; + /// + /// 可勾选 + /// + public const string BindCheckable = "bind-checkable"; + /// + /// 严格勾选 + /// + public const string BindCheckStrictly = "bind-check-strictly"; + /// + /// 显示展开 + /// + public const string BindShowExpand = "bind-show-expand"; + /// + /// 显示线 + /// + public const string BindShowLine = "bind-show-line"; + /// + /// 异步数据 + /// + public const string BindAsyncData = "bind-async-data"; + /// + /// 默认展开所有节点 + /// + public const string BindDefaultExpandAll = "bind-default-expand-all"; + /// + /// 虚拟高度 + /// + public const string BindVirtualHeight = "bind-virtual-height"; + /// + /// 虚拟项大小 + /// + public const string BindVirtualItemSize = "bind-virtual-item-size"; + /// + /// 虚拟缓冲区最大高度 + /// + public const string BindVirtualMaxBufferPx = "bind-virtual-max-buffer-px"; + /// + /// 虚拟缓冲区最小高度 + /// + public const string BindVirtualMinBufferPx = "bind-virtual-min-buffer-px"; + /// + /// 接受 + /// + public const string BindAccept = "bind-accept"; + /// + /// 操作 + /// + public const string BindAction = "bind-action"; + /// + /// 目录 + /// + public const string BindDirectory = "bind-directory"; + /// + /// 文件列表 + /// + public const string BindonFileList = "bindon-file-list"; + /// + /// 限制 + /// + public const string BindLimit = "bind-limit"; + /// + /// 文件类型 + /// + public const string BindFileType = "bind-file-type"; + /// + /// 头部 + /// + public const string BindHeaders = "bind-headers"; + /// + /// 列表类型 + /// + public const string BindListType = "bind-list-type"; + /// + /// 显示上传列表 + /// + public const string BindShowUploadList = "bind-show-upload-list"; + /// + /// 显示按钮 + /// + public const string BindShowButton = "bind-show-button"; + /// + /// 携带凭据 + /// + public const string BindWithCredentials = "bind-with-credentials"; + /// + /// 点击打开文件对话框 + /// + public const string BindOpenFileDialogOnClick = "bind-open-file-dialog-on-click"; + /// + /// 间隙 + /// + public const string BindGap = "bind-gap"; + /// + /// 源路径 + /// + public const string BindSrc = "bind-src"; + /// + /// 源路径 + /// + public const string BindSrcSet = "bind-src-set"; + /// + /// alt + /// + public const string BindAlt = "bind-alt"; + /// + /// 独立 + /// + public const string BindStandalone = "bind-standalone"; + /// + /// 点 + /// + public const string BindDot = "bind-dot"; + /// + /// 显示点 + /// + public const string BindShowDot = "bind-show-dot"; + /// + /// 溢出数量 + /// + public const string BindOverflowCount = "bind-overflow-count"; + /// + /// 显示0 + /// + public const string BindShowZero = "bind-show-zero"; + /// + /// 模式 + /// + public const string BindonMode = "bindon-mode"; + /// + /// 全屏 + /// + public const string BindFullscreen = "bind-fullscreen"; + /// + /// 可悬停 + /// + public const string BindHoverable = "bind-hoverable"; + /// + /// 自动播放 + /// + public const string BindAutoPlay = "bind-auto-play"; + /// + /// 自动播放速度 + /// + public const string BindAutoPlaySpeed = "bind-auto-play-speed"; + /// + /// 指示点位置 + /// + public const string BindDotPosition = "bind-dot-position"; + /// + /// 效果 + /// + public const string BindEffect = "bind-effect"; + /// + /// 启用划动 + /// + public const string BindEnableSwipe = "bind-enable-swipe"; + /// + /// 手风琴 + /// + public const string BindAccordion = "bind-accordion"; + /// + /// 有边框 + /// + public const string BindBordered = "bind-bordered"; + /// + /// 扩展图标位置 + /// + public const string BindExpandIconPosition = "bind-expand-icon-position"; + /// + /// 头 + /// + public const string BindHeader = "bind-header"; + /// + /// 扩展图标 + /// + public const string BindExpandedIcon = "bind-expanded-icon"; + /// + /// 激活 + /// + public const string BindonActive = "bindon-active"; + /// + /// 作者 + /// + public const string BindAuthor = "bind-author"; + /// + /// 日期 + /// + public const string BindDatetime = "bind-datetime"; + /// + /// 列 + /// + public const string BindColumn = "bind-column"; + /// + /// 冒号 + /// + public const string BindColon = "bind-colon"; + /// + /// 未找到图片 + /// + public const string BindNotFoundImage = "bind-not-found-image"; + /// + /// 未找到页脚 + /// + public const string BindNotFoundFooter = "bind-not-found-footer"; + /// + /// 回退 + /// + public const string BindFallback = "bind-fallback"; + /// + /// 禁止预览 + /// + public const string BindDisablePreview = "bind-disable-preview"; + /// + /// 导航时关闭 + /// + public const string BindCloseOnNavigation = "bind-close-on-navigation"; + /// + /// 页脚 + /// + public const string BindFooter = "bind-footer"; + /// + /// 项布局 + /// + public const string BindItemLayout = "bind-item-layout"; + /// + /// 分割 + /// + public const string BindSplit = "bind-split"; + /// + /// 无结果 + /// + public const string BindNoResult = "bind-no-result"; + /// + /// 非flex布局 + /// + public const string BindNoFlex = "bind-no-flex"; + /// + /// 头像 + /// + public const string BindAvatar = "bind-avatar"; + /// + /// 气泡框标题 + /// + public const string BindPopoverTitle = "bind-popover-title"; + /// + /// 气泡框内容 + /// + public const string BindPopoverContent = "bind-popover-content"; + /// + /// 气泡框触发器 + /// + public const string BindPopoverTrigger = "bind-popover-trigger"; + /// + /// 气泡框位置 + /// + public const string BindPopoverPlacement = "bind-popover-placement"; + /// + /// 气泡框源元素 + /// + public const string BindPopoverOrigin = "bind-popover-origin"; + /// + /// 气泡框可见性 + /// + public const string BindPopoverVisible = "bind-popover-visible"; + /// + /// 气泡框可见性 + /// + public const string BindonPopoverVisible = "bindon-popover-visible"; + /// + /// 气泡框移入延时 + /// + public const string BindPopoverMouseEnterDelay = "bind-popover-mouse-enter-delay"; + /// + /// 气泡框移出延时 + /// + public const string BindPopoverMouseLeaveDelay = "bind-popover-mouse-leave-delay"; + /// + /// 气泡框类名 + /// + public const string BindPopoverOverlayClassName = "bind-popover-overlay-class-name"; + /// + /// 气泡框背景 + /// + public const string BindPopoverBackdrop = "bind-popover-backdrop"; + /// + /// 前端分页 + /// + public const string BindFrontPagination = "bind-front-pagination"; + /// + /// 显示分页 + /// + public const string BindShowPagination = "bind-show-pagination"; + /// + /// 分页位置 + /// + public const string BindPaginationPosition = "bind-pagination-position"; + /// + /// 分页类型 + /// + public const string BindPaginationType = "bind-pagination-type"; + /// + /// 外边框 + /// + public const string BindOuterBordered = "bind-outer-bordered"; + /// + /// 延迟加载 + /// + public const string BindLoadingDelay = "bind-loading-delay"; + /// + /// 模板模式 + /// + public const string BindTemplateMode = "bind-template-mode"; + /// + /// 显示复选框 + /// + public const string BindShowCheckbox = "bind-show-checkbox"; + /// + /// 选中 + /// + public const string BindChecked = "bind-checked"; + /// + /// 选中 + /// + public const string BindonChecked = "bindon-checked"; + /// + /// 显示行选择 + /// + public const string BindShowRowSelection = "bind-show-row-selection"; + /// + /// 显示排序 + /// + public const string BindShowSort = "bind-show-sort"; + /// + /// 排序方向 + /// + public const string BindSortOrder = "bind-sort-order"; + /// + /// 排序方向 + /// + public const string BindonSortOrder = "bindon-sort-order"; + /// + /// 显示过滤 + /// + public const string BindShowFilter = "bind-show-filter"; + /// + /// 多选过滤器 + /// + public const string BindFilterMultiple = "bind-filter-multiple"; + /// + /// 左 + /// + public const string BindLeft = "bind-left"; + /// + /// 右 + /// + public const string BindRight = "bind-right"; + /// + /// 折行 + /// + public const string BindBreakWord = "bind-break-word"; + /// + /// 合并列 + /// + public const string BindColspan = "bind-colspan"; + /// + /// 合并行 + /// + public const string BindRowspan = "bind-rowspan"; + /// + /// 列标识 + /// + public const string BindColumnKey = "bind-column-key"; + /// + /// 展开 + /// + public const string BindExpand = "bind-expand"; + /// + /// 展开 + /// + public const string BindonExpand = "bindon-expand"; + /// + /// 缩进大小 + /// + public const string BindIndentSize = "bind-indent-size"; + /// + /// 可见 + /// + public const string BindVisible = "bind-visible"; + /// + /// 可见 + /// + public const string BindonVisible = "bindon-visible"; + /// + /// 有背景 + /// + public const string BindHasBackdrop = "bind-has-backdrop"; + /// + /// 项大小 + /// + public const string BindItemSize = "bind-item-size"; + /// + /// 选中索引 + /// + public const string BindSelectedIndex = "bind-selected-index"; + /// + /// 选中索引 + /// + public const string BindonSelectedIndex = "bindon-selected-index"; + /// + /// 动画 + /// + public const string BindAnimated = "bind-animated"; + /// + /// 标签位置 + /// + public const string BindTabPosition = "bind-tab-position"; + /// + /// 标签间隙 + /// + public const string BindTabBarGutter = "bind-tab-bar-gutter"; + /// + /// 隐藏全部 + /// + public const string BindHideAll = "bind-hide-all"; + /// + /// 链接路由 + /// + public const string BindLinkRouter = "bind-link-router"; + /// + /// 严格模式匹配路由 + /// + public const string BindLinkExact = "bind-link-exact"; + /// + /// 居中 + /// + public const string BindCentered = "bind-centered"; + /// + /// 隐藏添加 + /// + public const string BindHideAdd = "bind-hide-add"; + /// + /// 添加图标 + /// + public const string BindAddIcon = "bind-add-icon"; + /// + /// 强制渲染 + /// + public const string BindForceRender = "bind-force-render"; + /// + /// 可关闭 + /// + public const string BindClosable = "bind-closable"; + /// + /// 关闭图标 + /// + public const string BindCloseIcon = "bind-close-icon"; + /// + /// 待定 + /// + public const string BindPending = "bind-pending"; + /// + /// 待定点 + /// + public const string BindPendingDot = "bind-pending-dot"; + /// + /// 位置 + /// + public const string BindPosition = "bind-position"; + /// + /// 提示触发行为 + /// + public const string BindTooltipTrigger = "bind-tooltip-trigger"; + /// + /// 提示颜色 + /// + public const string BindTooltipColor = "bind-tooltip-color"; + /// + /// 提示可见性 + /// + public const string BindonTooltipVisible = "bindon-tooltip-visible"; + /// + /// 提示移入延时 + /// + public const string BindTooltipMouseEnterDelay = "bind-tooltip-mouse-enter-delay"; + /// + /// 提示移出延时 + /// + public const string BindTooltipMouseLeaveDelay = "bind-tooltip-mouse-leave-delay"; + /// + /// 提示类名 + /// + public const string BindTooltipOverlayClassName = "bind-tooltip-overlay-class-name"; + /// + /// 节点占一行 + /// + public const string BindBlockNode = "bind-block-node"; + /// + /// 可拖拽 + /// + public const string BindDraggable = "bind-draggable"; + /// + /// 展开所有 + /// + public const string BindExpandAll = "bind-expand-all"; + /// + /// 搜索值 + /// + public const string BindSearchValue = "bind-search-value"; + /// + /// 搜索值 + /// + public const string BindonSearchValue = "bindon-search-value"; + /// + /// 目录树 + /// + public const string BindDirectoryTree = "bind-directory-tree"; + /// + /// 最大缓冲区 + /// + public const string BindMaxBufferPx = "bind-max-buffer-px"; + /// + /// 最小缓冲区 + /// + public const string BindMinBufferPx = "bind-min-buffer-px"; + /// + /// 横幅 + /// + public const string BindBanner = "bind-banner"; + /// + /// 可关闭 + /// + public const string BindCloseable = "bind-closeable"; + /// + /// 关闭文本 + /// + public const string BindCloseText = "bind-close-text"; + /// + /// 图标类型 + /// + public const string BindIconType = "bind-icon-type"; + /// + /// 遮罩可关闭 + /// + public const string BindMaskClosable = "bind-mask-closable"; + /// + /// 遮罩 + /// + public const string BindMask = "bind-mask"; + /// + /// 键盘 + /// + public const string BindKeyboard = "bind-keyboard"; + /// + /// X坐标偏移量 + /// + public const string BindOffsetX = "bind-offset-x"; + /// + /// Y坐标偏移量 + /// + public const string BindOffsetY = "bind-offset-y"; + /// + /// 包装类名 + /// + public const string BindWrapClassName = "bind-wrap-class-name"; + /// + /// z-index + /// + public const string BindZIndex = "bind-z-index"; + /// + /// 禁用确定 + /// + public const string BindOkDisabled = "bind-ok-disabled"; + /// + /// 禁用取消 + /// + public const string BindCancelDisabled = "bind-cancel-disabled"; + /// + /// 类名 + /// + public const string BindClassName = "bind-class-name"; + /// + /// 确认文字 + /// + public const string BindOkText = "bind-ok-text"; + /// + /// 取消文字 + /// + public const string BindCancelText = "bind-cancel-text"; + /// + /// 确认类型 + /// + public const string BindOkType = "bind-ok-type"; + /// + /// 确认危险 + /// + public const string BindOkDanger = "bind-ok-danger"; + /// + /// 自动聚焦 + /// + public const string BindAutofocus = "bind-auto-focus"; + /// + /// 弹出确认标题 + /// + public const string BindPopconfirmTitle = "bind-popconfirm-title"; + /// + /// 弹出确认触发行为 + /// + public const string BindPopconfirmTrigger = "bind-popconfirm-trigger"; + /// + /// 弹出确认位置 + /// + public const string BindPopconfirmPlacement = "bind-popconfirm-placement"; + /// + /// 弹出确认源元素 + /// + public const string BindPopconfirmOrigin = "bind-popconfirm-origin"; + /// + /// 弹出确认是否可见 + /// + public const string BindonPopconfirmVisible = "bindon-popconfirm-visible"; + /// + /// 弹出确认显示箭头 + /// + public const string BindPopconfirmShowArrow = "bind-popconfirm-show-arrow"; + /// + /// 弹出确认移入延时 + /// + public const string BindPopconfirmMouseEnterDelay = "bind-popconfirm-mouse-enter-delay"; + /// + /// 弹出确认移出延时 + /// + public const string BindPopconfirmMouseLeaveDelay = "bind-popconfirm-mouse-leave-delay"; + /// + /// 弹出确认样式类名 + /// + public const string BindPopconfirmOverlayClassName = "bind-popconfirm-overlay-class-name"; + /// + /// 弹出确认背景 + /// + public const string BindPopconfirmBackdrop = "bind-popconfirm-backdrop"; + /// + /// 弹出确认取消文字 + /// + public const string BindPopconfirmCancelText = "bind-popconfirm-cancel-text"; + /// + /// 弹出确认确定文字 + /// + public const string BindPopconfirmOkText = "bind-popconfirm-ok-text"; + /// + /// 弹出确认确定类型 + /// + public const string BindPopconfirmOkType = "bind-popconfirm-ok-type"; + /// + /// 弹出确认条件 + /// + public const string BindPopconfirmCondition = "bind-popconfirm-condition"; + /// + /// 弹出确认图标 + /// + public const string BindPopconfirmIcon = "bind-popconfirm-icon"; + /// + /// 百分比 + /// + public const string BindPercent = "bind-percent"; + /// + /// 显示信息 + /// + public const string BindShowInfo = "bind-show-info"; + /// + /// 端点形状 + /// + public const string BindStrokeLinecap = "bind-stroke-linecap"; + /// + /// 线条颜色 + /// + public const string BindStrokeColor = "bind-stroke-color"; + /// + /// 成功百分比 + /// + public const string BindSuccessPercent = "bind-success-percent"; + /// + /// 线条宽度 + /// + public const string BindStrokeWidth = "bind-stroke-width"; + /// + /// 步数 + /// + public const string BindSteps = "bind-steps"; + /// + /// 缺口角度 + /// + public const string BindGapDegree = "bind-gap-degree"; + /// + /// 缺口位置 + /// + public const string BindGapPosition = "bind-gap-position"; + /// + /// 圆形 + /// + public const string BindRound = "bind-round"; + /// + /// 延迟 + /// + public const string BindDelay = "bind-delay"; + /// + /// 旋转 + /// + public const string BindSpinning = "bind-spinning"; + /// + /// 提示 + /// + public const string BindTip = "bind-tip"; + /// + /// 固定 + /// + public const string BindAffix = "bind-affix"; + /// + /// 边界 + /// + public const string BindBounds = "bind-bounds"; + /// + /// 固定显示圆点 + /// + public const string BindShowInkInFixed = "bind-show-ink-in-fixed"; + /// + /// 容器 + /// + public const string BindContainer = "bind-container"; + /// + /// 排序 + /// + public const string BindSort = "bind-sort"; + /// + /// 必填项验证消息 + /// + public const string BindRequiredMessage = "bind-required-message"; + /// + /// 最小长度验证消息 + /// + public const string BindMinLengthMessage = "bind-min-length-message"; + /// + /// 最大长度验证消息 + /// + public const string BindMaxLengthMessage = "bind-max-length-message"; + /// + /// 电子邮件错误消息 + /// + public const string BindEmailMessage = "bind-email-message"; + /// + /// 模式错误消息 + /// + public const string BindPatternMessage = "bind-pattern-message"; + /// + /// 不绑定文件列表 + /// + public const string NotBindFileList = "not-bind-file-list"; + /// + /// 自动换行 + /// + public const string BindWrap = "bind-wrap"; + /// + /// 单元格控件 + /// + public const string BindCellControl = "bind-cell-control"; + /// + /// 双色图标颜色 + /// + public const string BindTwotoneColor = "bind-twotone-color"; + /// + /// Iconfont图标 + /// + public const string BindIconFont = "bind-icon-font"; + /// + /// 路由标签 + /// + public const string BindRouteLabel = "bind-route-label"; + /// + /// 标识 + /// + public const string BindNzId = "bind-nz-id"; + /// + /// 自定义头部 + /// + public const string BindCustomHeader = "bind-custom-header"; + /// + /// 标签对齐方式 + /// + public const string BindLabelAlign = "bind-label-align"; +} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Enums/QueryParamsHandling.cs b/src/Util.Ui.Angular/Enums/QueryParamsHandling.cs new file mode 100644 index 000000000..f3d8b1640 --- /dev/null +++ b/src/Util.Ui.Angular/Enums/QueryParamsHandling.cs @@ -0,0 +1,19 @@ +using System.ComponentModel; + +namespace Util.Ui.NgZorro.Enums; + +/// +/// 路由器链接查询参数处理方式 +/// +public enum QueryParamsHandling { + /// + /// merge, 将新参数与当前参数合并 + /// + [Description( "merge" )] + Merge, + /// + /// preserve, 保留当前参数 + /// + [Description( "preserve" )] + Preserve +} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Extensions/Extensions.Component.cs b/src/Util.Ui.Angular/Extensions/Extensions.Component.cs deleted file mode 100644 index c4f53dbc4..000000000 --- a/src/Util.Ui.Angular/Extensions/Extensions.Component.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Components.Internal; -using Util.Ui.Configs; -using Util.Ui.Operations; - -namespace Util.Ui.Extensions { - /// - /// 表单扩展 - /// - public static partial class Extensions { - /// - /// 禁用 - /// - /// 组件类型 - /// 组件实例 - /// 禁用表达式 - public static TComponent Disable( this TComponent component, string disabled ) where TComponent : IOption, IDisabled { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Disabled, disabled ); - } ); - return component; - } - } -} diff --git a/src/Util.Ui.Angular/Extensions/Extensions.HtmlHelper.cs b/src/Util.Ui.Angular/Extensions/Extensions.HtmlHelper.cs deleted file mode 100644 index 805d20235..000000000 --- a/src/Util.Ui.Angular/Extensions/Extensions.HtmlHelper.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.AspNetCore.Mvc.Rendering; -using Util.Ui.Services; - -namespace Util.Ui.Extensions { - /// - /// HtmlHelper扩展 - /// - public static partial class Extensions { - /// - /// Angular组件服务 - /// - /// HtmlHelper - public static IUiService Ui( this IHtmlHelper helper ) { - return new UiService( helper ); - } - } -} diff --git a/src/Util.Ui.Angular/Extensions/Extensions.UiService.cs b/src/Util.Ui.Angular/Extensions/Extensions.UiService.cs deleted file mode 100644 index a70a8fdf0..000000000 --- a/src/Util.Ui.Angular/Extensions/Extensions.UiService.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using Util.Ui.Components; -using Util.Ui.Material.Buttons; -using Util.Ui.Material.Forms; -using Util.Ui.Material.Icons; -using Util.Ui.Material.Menus; -using Util.Ui.Material.Tabs; -using Util.Ui.Services; - -namespace Util.Ui.Extensions { - /// - /// 组件服务扩展 - /// - public static partial class Extensions { - /// - /// 链接 - /// - /// 组件服务 - public static IAnchor A( this IUiService service ) { - return new Anchor(); - } - - /// - /// 图标 - /// - /// 组件服务 - public static IIcon Icon( this IUiService service ) { - return new Icon(); - } - - /// - /// 单选框 - /// - /// 组件服务 - public static IRadio Radio( this IUiService service ) { - return new Radio(); - } - - /// - /// 复选框 - /// - /// 组件服务 - public static ICheckBox CheckBox( this IUiService service ) { - return new CheckBox(); - } - - /// - /// 滑动开关 - /// - /// 组件服务 - public static ISlideToggle SlideToggle( this IUiService service ) { - return new SlideToggle(); - } - - /// - /// 下拉列表 - /// - /// 组件服务 - public static ISelect Select( this IUiService service ) { - return new Select(); - } - - /// - /// 文本框 - /// - /// 组件服务 - public static ITextBox TextBox( this IUiService service ) { - return new TextBox(); - } - - /// - /// 表单 - /// - /// 组件服务 - public static IForm Form( this IUiService service ) { - if( !( service is IContext context ) ) - throw new NotSupportedException(); - return new Form( context.Helper.ViewContext.Writer ); - } - - /// - /// 按钮 - /// - /// 组件服务 - public static Material.Buttons.IButton Button( this IUiService service ) { - if( !( service is IContext context ) ) - throw new NotSupportedException(); - return new Button( context.Helper.ViewContext.Writer ); - } - - /// - /// 菜单 - /// - /// 组件服务 - public static IMenu Menu( this IUiService service ) { - if( !( service is IContext context ) ) - throw new NotSupportedException(); - return new Menu( context.Helper.ViewContext.Writer ); - } - - /// - /// 选项卡组 - /// - /// 组件服务 - public static ITabGroup Tabs( this IUiService service ) { - if( !( service is IContext context ) ) - throw new NotSupportedException(); - return new TabGroup( context.Helper.ViewContext.Writer ); - } - - /// - /// 导航选项卡 - /// - /// 组件服务 - public static ITabNavigation NavTabs( this IUiService service ) { - if( !( service is IContext context ) ) - throw new NotSupportedException(); - return new TabNavigation( context.Helper.ViewContext.Writer ); - } - } -} diff --git a/src/Util.Ui.Angular/Extensions/TagBuilderExtensions.cs b/src/Util.Ui.Angular/Extensions/TagBuilderExtensions.cs new file mode 100644 index 000000000..6f3511966 --- /dev/null +++ b/src/Util.Ui.Angular/Extensions/TagBuilderExtensions.cs @@ -0,0 +1,335 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.Angular.Extensions; + +/// +/// Angular标签生成器扩展 +/// +public static class TagBuilderExtensions { + /// + /// 添加Angular指令 + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + public static TBuilder Angular( this TBuilder builder, Config config ) where TBuilder : TagBuilder { + builder + .NgIf( config ) + .NgSwitch( config ).NgSwitchCase( config ).NgSwitchDefault( config ) + .NgFor( config ) + .NgClass( config ) + .NgStyle( config ) + .Acl( config ) + .BindAcl( config ); + return builder; + } + + /// + /// *ngIf + /// + /// 生成器类型 + /// 生成器实例 + /// 值 + public static TBuilder NgIf( this TBuilder builder, string value ) where TBuilder : TagBuilder { + builder.AttributeIfNotEmpty( "*ngIf", value ); + return builder; + } + + /// + /// *ngIf + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + public static TBuilder NgIf( this TBuilder builder, Config config ) where TBuilder : TagBuilder { + builder.NgIf( config.GetValue( AngularConst.NgIf ) ); + return builder; + } + + /// + /// [ngSwitch] + /// + /// 生成器类型 + /// 生成器实例 + /// 值 + public static TBuilder NgSwitch( this TBuilder builder, string value ) where TBuilder : TagBuilder { + builder.AttributeIfNotEmpty( "[ngSwitch]", value ); + return builder; + } + + /// + /// [ngSwitch] + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + public static TBuilder NgSwitch( this TBuilder builder, Config config ) where TBuilder : TagBuilder { + builder.NgSwitch( config.GetValue( AngularConst.NgSwitch ) ); + return builder; + } + + /// + /// *ngSwitchCase + /// + /// 生成器类型 + /// 生成器实例 + /// 值 + public static TBuilder NgSwitchCase( this TBuilder builder, string value ) where TBuilder : TagBuilder { + builder.AttributeIfNotEmpty( "*ngSwitchCase", value ); + return builder; + } + + /// + /// *ngSwitchCase + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + public static TBuilder NgSwitchCase( this TBuilder builder, Config config ) where TBuilder : TagBuilder { + builder.NgSwitchCase( config.GetValue( AngularConst.NgSwitchCase ) ); + return builder; + } + + /// + /// *ngSwitchDefault + /// + /// 生成器类型 + /// 生成器实例 + /// 值,为true时添加指令 + public static TBuilder NgSwitchDefault( this TBuilder builder, bool? value ) where TBuilder : TagBuilder { + if ( value == true ) + builder.Attribute( "*ngSwitchDefault" ); + return builder; + } + + /// + /// *ngSwitchDefault + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + public static TBuilder NgSwitchDefault( this TBuilder builder, Config config ) where TBuilder : TagBuilder { + return builder.NgSwitchDefault( config.GetValue( AngularConst.NgSwitchDefault ) ); + } + + /// + /// *ngFor + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + public static TBuilder NgFor( this TBuilder builder, Config config ) where TBuilder : TagBuilder { + builder.NgFor( config.GetValue( AngularConst.NgFor ) ); + return builder; + } + + /// + /// *ngFor + /// + /// 生成器类型 + /// 生成器实例 + /// 值 + public static TBuilder NgFor( this TBuilder builder, string value ) where TBuilder : TagBuilder { + builder.AttributeIfNotEmpty( "*ngFor", value ); + return builder; + } + + /// + /// [ngClass] + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + public static TBuilder NgClass( this TBuilder builder, Config config ) where TBuilder : TagBuilder { + builder.AttributeIfNotEmpty( "[ngClass]", config.GetValue( AngularConst.NgClass ) ); + return builder; + } + + /// + /// [ngStyle] + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + public static TBuilder NgStyle( this TBuilder builder, Config config ) where TBuilder : TagBuilder { + builder.AttributeIfNotEmpty( "[ngStyle]", config.GetValue( AngularConst.NgStyle ) ); + return builder; + } + + /// + /// *aclIf,设置访问控制 + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + public static TBuilder Acl( this TBuilder builder, Config config ) where TBuilder : TagBuilder { + var acl = config.GetValue( UiConst.Acl ); + var templateId = config.GetValue( UiConst.AclElseTemplateId ); + if ( acl.IsEmpty() ) + return builder; + if ( templateId.IsEmpty() ) { + builder.Attribute( "*aclIf", GetAcl( acl ) ); + return builder; + } + builder.Attribute( "*aclIf", $"{GetAcl( acl )}; else {templateId}" ); + return builder; + } + + /// + /// 获取访问控制值 + /// + private static string GetAcl( string acl ) { + if ( acl.Contains( "||" ) ) + return SplitOr( acl ); + if ( acl.Contains( "&&" ) ) + return SplitAnd( acl ); + return GetSafeAcl( acl ); + } + + /// + /// 使用||拆分资源标识 + /// + private static string SplitOr( string acl ) { + var list = acl.Split( "||" ); + var result = new StringBuilder(); + result.Append( "[" ); + foreach ( var item in list ) { + if ( item.IsEmpty() ) + continue; + result.Append( $"{GetSafeAcl( item )}," ); + } + result.RemoveEnd( "," ); + result.Append( "]" ); + return result.ToString(); + } + + /// + /// 使用&&拆分资源标识 + /// + private static string SplitAnd( string acl ) { + var list = acl.Split( "&&" ); + var result = new StringBuilder(); + result.Append( "{role:[" ); + foreach ( var item in list ) { + if ( item.IsEmpty() ) + continue; + result.Append( $"{GetSafeAcl( item )}," ); + } + result.RemoveEnd( "," ); + result.Append( "],mode:'allOf'}" ); + return result.ToString(); + } + + /// + /// 安全获取资源标识 + /// + private static string GetSafeAcl( string acl ) { + if ( acl.StartsWith( "[" ) || acl.Contains( "'" ) ) + return acl.SafeString(); + return $"'{acl.SafeString()}'"; + } + + /// + /// [acl],设置访问控制 + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + public static TBuilder BindAcl( this TBuilder builder, Config config ) where TBuilder : TagBuilder { + builder.AttributeIfNotEmpty( "[acl]", config.GetValue( AngularConst.BindAcl ) ); + return builder; + } + + /// + /// [(ngModel)] + /// + /// 生成器类型 + /// 生成器实例 + /// 值 + public static TBuilder NgModel( this TBuilder builder, string value ) where TBuilder : TagBuilder { + builder.AttributeIfNotEmpty( "[(ngModel)]", value ); + return builder; + } + + /// + /// [ngModel] + /// + /// 生成器类型 + /// 生成器实例 + /// 值 + public static TBuilder BindNgModel( this TBuilder builder, string value ) where TBuilder : TagBuilder { + builder.AttributeIfNotEmpty( "[ngModel]", value ); + return builder; + } + + /// + /// 添加[(ngModel)]和[ngModel] + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + public static TBuilder NgModel( this TBuilder builder, Config config ) where TBuilder : TagBuilder { + builder.NgModel( config.GetValue( AngularConst.NgModel ) ); + builder.BindNgModel( config.GetValue( AngularConst.BindNgModel ) ); + return builder; + } + + /// + /// 添加单击事件(click) + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + public static TBuilder OnClick( this TBuilder builder, Config config ) where TBuilder : TagBuilder { + return builder.OnClick( config.GetValue( UiConst.OnClick ) ); + } + + /// + /// 添加单击事件(click) + /// + /// 生成器类型 + /// 生成器实例 + /// 事件处理函数 + public static TBuilder OnClick( this TBuilder builder, string onclick ) where TBuilder : TagBuilder { + builder.AttributeIfNotEmpty( "(click)", onclick ); + return builder; + } + + /// + /// 添加原始Id + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + public static TBuilder RawId( this TBuilder builder, Config config ) where TBuilder : TagBuilder { + builder.AttributeIfNotEmpty( "id", config.GetValue( AngularConst.RawId ) ); + return builder; + } + + /// + /// 添加引用变量 + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + /// 引用变量的值 + public static TBuilder Id( this TBuilder builder, Config config, string value = null ) where TBuilder : TagBuilder { + return builder.Id( config.GetValue( UiConst.Id ), value ); + } + + /// + /// 添加引用变量 + /// + /// 生成器类型 + /// 生成器实例 + /// 引用变量名 + /// 引用变量值 + public static TBuilder Id( this TBuilder builder, string name, string value = null ) where TBuilder : TagBuilder { + if ( string.IsNullOrWhiteSpace( name ) == false ) + builder.Attribute( $"#{name}", value ); + return builder; + } +} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Buttons/Anchor.cs b/src/Util.Ui.Angular/Material/Buttons/Anchor.cs deleted file mode 100644 index 7e8f1af0a..000000000 --- a/src/Util.Ui.Angular/Material/Buttons/Anchor.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Material.Buttons.Renders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Buttons { - /// - /// 链接 - /// - public class Anchor : ComponentBase, IAnchor { - /// - /// 获取渲染器 - /// - protected override IRender GetRender() { - return new AnchorRender( OptionConfig ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Buttons/Builders/ButtonWrapperBuilder.cs b/src/Util.Ui.Angular/Material/Buttons/Builders/ButtonWrapperBuilder.cs deleted file mode 100644 index d916868e5..000000000 --- a/src/Util.Ui.Angular/Material/Buttons/Builders/ButtonWrapperBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Buttons.Builders { - /// - /// Mat按钮包装器生成器 - /// - public class ButtonWrapperBuilder : TagBuilder { - /// - /// 初始化Mat按钮包装器生成器 - /// - public ButtonWrapperBuilder() : base( "mat-button-wrapper" ) { - } - } -} diff --git a/src/Util.Ui.Angular/Material/Buttons/Button.cs b/src/Util.Ui.Angular/Material/Buttons/Button.cs deleted file mode 100644 index f32c611a1..000000000 --- a/src/Util.Ui.Angular/Material/Buttons/Button.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.IO; -using Util.Ui.Components; -using Util.Ui.Configs; -using Util.Ui.Material.Buttons.Configs; -using Util.Ui.Material.Buttons.Renders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Buttons { - /// - /// 按钮 - /// - public class Button : ContainerBase, IButton { - /// - /// 配置 - /// - private readonly ButtonConfig _config; - - /// - /// 初始化按钮 - /// - /// 流写入器 - public Button( TextWriter writer ) : base( writer ) { - _config = new ButtonConfig(); - } - - /// - /// 获取配置 - /// - protected override IConfig GetConfig() { - return _config; - } - - /// - /// 获取渲染器 - /// - protected override IRender GetRender() { - if( _config.IsMenu ) - return new ButtonRender( _config ); - return new ButtonWrapperRender( _config ); - } - - /// - /// 获取容器包装器 - /// - protected override IDisposable GetWrapper() { - return new ContainerWrapper( this ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Buttons/Configs/ButtonConfig.cs b/src/Util.Ui.Angular/Material/Buttons/Configs/ButtonConfig.cs deleted file mode 100644 index 690aec5cf..000000000 --- a/src/Util.Ui.Angular/Material/Buttons/Configs/ButtonConfig.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.Generic; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Menus.Datas; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Buttons.Configs { - /// - /// 按钮配置 - /// - public class ButtonConfig : Config { - /// - /// 初始化按钮配置 - /// - public ButtonConfig() { - } - - /// - /// 初始化按钮配置 - /// - /// 上下文 - public ButtonConfig( Context context ) : base( context ) { - } - - /// - /// 是否菜单操作 - /// - public bool IsMenu => Contains( MaterialConst.MenuId ); - - /// - /// 菜单数据列表 - /// - public List Data { get; set; } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Buttons/IButton.cs b/src/Util.Ui.Angular/Material/Buttons/IButton.cs deleted file mode 100644 index 9d50e5588..000000000 --- a/src/Util.Ui.Angular/Material/Buttons/IButton.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using Util.Ui.Components; -using Util.Ui.Operations.Navigation; - -namespace Util.Ui.Material.Buttons { - /// - /// 按钮 - /// - public interface IButton : IComponent, IContainer, Components.IButton, IMenuId { - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Buttons/Renders/AnchorRender.cs b/src/Util.Ui.Angular/Material/Buttons/Renders/AnchorRender.cs deleted file mode 100644 index 7345405c5..000000000 --- a/src/Util.Ui.Angular/Material/Buttons/Renders/AnchorRender.cs +++ /dev/null @@ -1,111 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Renders; -using Util.Ui.Material.Enums; - -namespace Util.Ui.Material.Buttons.Renders { - /// - /// 链接渲染器 - /// - public class AnchorRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化链接渲染器 - /// - /// 配置 - public AnchorRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new AnchorBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigText( builder ); - ConfigStyle( builder ); - ConfigColor( builder ); - ConfigDisabled( builder ); - ConfigTooltip( builder ); - ConfigLink( builder ); - ConfigEvents( builder ); - ConfigContent( builder ); - } - - /// - /// 配置文本 - /// - private void ConfigText( TagBuilder builder ) { - if( _config.Contains( UiConst.Text ) ) - builder.SetContent( _config.GetValue( UiConst.Text ) ); - } - - /// - /// 设置样式 - /// - private void ConfigStyle( TagBuilder builder ) { - if( _config.Contains( UiConst.Styles ) ) { - builder.AddAttribute( _config.GetValue( UiConst.Styles )?.Description() ); - return; - } - builder.AddAttribute( ButtonStyle.Raised.Description() ); - } - - /// - /// 配置颜色 - /// - private void ConfigColor( TagBuilder builder ) { - builder.AddAttribute( UiConst.Color, _config.GetValue( UiConst.Color ).ToLower() ); - } - - /// - /// 配置禁用 - /// - private void ConfigDisabled( TagBuilder builder ) { - builder.AddAttribute( "[disabled]", _config.GetBoolValue( UiConst.Disabled ) ); - } - - /// - /// 配置提示 - /// - private void ConfigTooltip( TagBuilder builder ) { - builder.AddAttribute( "matTooltip", _config.GetValue( UiConst.Tooltip ) ); - } - - /// - /// 配置链接 - /// - private void ConfigLink( TagBuilder builder ) { - builder.AddAttribute( "routerLink", _config.GetValue( UiConst.Link ) ); - } - - /// - /// 配置事件 - /// - private void ConfigEvents( TagBuilder builder ) { - builder.AddAttribute( "(click)", _config.GetValue( UiConst.OnClick ) ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Contains( UiConst.Text ) ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Buttons/Renders/ButtonRender.cs b/src/Util.Ui.Angular/Material/Buttons/Renders/ButtonRender.cs deleted file mode 100644 index cf210ed3b..000000000 --- a/src/Util.Ui.Angular/Material/Buttons/Renders/ButtonRender.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System.Linq; -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Buttons.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Renders; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Extensions; -using Util.Ui.Material.Menus; - -namespace Util.Ui.Material.Buttons.Renders { - /// - /// 按钮渲染器 - /// - public class ButtonRender : RenderBase { - /// - /// 配置 - /// - private readonly ButtonConfig _config; - - /// - /// 初始化按钮渲染器 - /// - /// 配置 - public ButtonRender( ButtonConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new ButtonBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - private void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigText( builder ); - ConfigType( builder ); - ConfigStyle( builder ); - ConfigColor( builder ); - ConfigDisabled( builder ); - ConfigTooltip( builder ); - ConfigMenu( builder ); - ConfigContent( builder ); - } - - /// - /// 配置文本 - /// - private void ConfigText( TagBuilder builder ) { - if( _config.Contains( UiConst.Text ) ) - builder.SetContent( _config.GetValue( UiConst.Text ) ); - } - - /// - /// 配置类型 - /// - private void ConfigType( TagBuilder builder ) { - builder.AddAttribute( UiConst.Type, "button" ); - } - - /// - /// 设置样式 - /// - private void ConfigStyle( TagBuilder builder ) { - if( _config.Contains( UiConst.Styles ) ) { - builder.AddAttribute( _config.GetValue( UiConst.Styles )?.Description() ); - return; - } - builder.AddAttribute( ButtonStyle.Raised.Description() ); - } - - /// - /// 配置颜色 - /// - private void ConfigColor( TagBuilder builder ) { - builder.AddAttribute( UiConst.Color, _config.GetValue( UiConst.Color ).ToLower() ); - } - - /// - /// 配置禁用 - /// - private void ConfigDisabled( TagBuilder builder ) { - builder.AddAttribute( "[disabled]", _config.GetBoolValue( UiConst.Disabled ) ); - } - - /// - /// 配置提示 - /// - private void ConfigTooltip( TagBuilder builder ) { - builder.AddAttribute( "matTooltip", _config.GetValue( UiConst.Tooltip ) ); - } - - /// - /// 配置菜单 - /// - private void ConfigMenu( TagBuilder builder ) { - builder.AddAttribute( "[matMenuTriggerFor]", _config.GetValue( MaterialConst.MenuId ) ); - AddMenus( builder ); - } - - /// - /// 添加菜单 - /// - private void AddMenus( TagBuilder builder ) { - _config.Data?.Select( data => new Menu().Data( data ) ).ToList() - .ForEach( menu => builder.AppendContent( menu ) ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Contains( UiConst.Text ) ) - return; - builder.AppendContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Buttons/Renders/ButtonWrapperRender.cs b/src/Util.Ui.Angular/Material/Buttons/Renders/ButtonWrapperRender.cs deleted file mode 100644 index f7d3a689d..000000000 --- a/src/Util.Ui.Angular/Material/Buttons/Renders/ButtonWrapperRender.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System.IO; -using System.Text.Encodings.Web; -using Util.Ui.Angular.Builders; -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Buttons.Builders; -using Util.Ui.Renders; -using Util.Ui.Material.Enums; - -namespace Util.Ui.Material.Buttons.Renders { - /// - /// 按钮包装器渲染器 - /// - public class ButtonWrapperRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - /// - /// 模板生成器 - /// - private readonly TemplateBuilder _templateBuilder; - - /// - /// 初始化按钮包装器渲染器 - /// - /// 配置 - public ButtonWrapperRender( IConfig config ) : base( config ) { - _config = config; - _templateBuilder = new TemplateBuilder(); - } - - /// - /// 渲染 - /// - /// 流写入器 - /// 编码 - public override void WriteTo( TextWriter writer, HtmlEncoder encoder ) { - if ( _config.Contains( UiConst.Text ) || _config.Content == null ) { - Builder.WriteTo( writer, encoder ); - return; - } - _templateBuilder.SetContent( _config.Content ); - Builder.SetContent( _templateBuilder ); - Builder.WriteTo( writer, encoder ); - } - - /// - /// 渲染起始标签 - /// - /// 流写入器 - public override void RenderStartTag( TextWriter writer ) { - Builder.SetContent( _templateBuilder ); - Builder.RenderStartTag( writer ); - _templateBuilder.RenderStartTag( writer ); - } - - /// - /// 渲染结束标签 - /// - /// 流写入器 - public override void RenderEndTag( TextWriter writer ) { - _templateBuilder.RenderEndTag( writer ); - Builder.RenderEndTag( writer ); - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new ButtonWrapperBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigText( builder ); - ConfigType( builder ); - ConfigStyle( builder ); - ConfigColor( builder ); - ConfigDisabled( builder ); - ConfigTooltip( builder ); - ConfigEvents( builder ); - } - - /// - /// 配置文本 - /// - private void ConfigText( TagBuilder builder ) { - builder.AddAttribute( UiConst.Text, _config.GetValue( UiConst.Text ) ); - } - - /// - /// 配置类型 - /// - private void ConfigType( TagBuilder builder ) { - builder.AddAttribute( UiConst.Type, _config.GetValue( UiConst.Type ).ToLower() ); - } - - /// - /// 设置样式 - /// - private void ConfigStyle( TagBuilder builder ) { - builder.AddAttribute( UiConst.Style, _config.GetValue( UiConst.Styles )?.Description() ); - } - - /// - /// 配置颜色 - /// - private void ConfigColor( TagBuilder builder ) { - builder.AddAttribute( UiConst.Color, _config.GetValue( UiConst.Color ).ToLower() ); - } - - /// - /// 配置禁用 - /// - private void ConfigDisabled( TagBuilder builder ) { - builder.AddAttribute( "[disabled]", _config.GetBoolValue( UiConst.Disabled ) ); - } - - /// - /// 配置提示 - /// - private void ConfigTooltip( TagBuilder builder ) { - builder.AddAttribute( UiConst.Tooltip, _config.GetValue( UiConst.Tooltip ) ); - } - - /// - /// 配置事件 - /// - private void ConfigEvents( TagBuilder builder ) { - builder.AddAttribute( "(onClick)", _config.GetValue( UiConst.OnClick ) ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Buttons/TagHelpers/ATagHelper.cs b/src/Util.Ui.Angular/Material/Buttons/TagHelpers/ATagHelper.cs deleted file mode 100644 index eee0381a4..000000000 --- a/src/Util.Ui.Angular/Material/Buttons/TagHelpers/ATagHelper.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Buttons.Renders; -using Util.Ui.Material.Enums; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Buttons.TagHelpers { - /// - /// 链接 - /// - [HtmlTargetElement("util-a")] - public class ATagHelper : TagHelperBase { - /// - /// 文本 - /// - public string Text { get; set; } - /// - /// 样式 - /// - public ButtonStyle Styles { get; set; } - /// - /// 颜色 - /// - public Color Color { get; set; } - /// - /// 禁用 - /// - public string Disabled { get; set; } - /// - /// 提示 - /// - public string Tooltip { get; set; } - /// - /// 路由链接地址 - /// - public string Link { get; set; } - /// - /// 单击事件处理函数,范例:handle() - /// - public string OnClick { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new AnchorRender( new Config( context ) ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Buttons/TagHelpers/ButtonTagHelper.cs b/src/Util.Ui.Angular/Material/Buttons/TagHelpers/ButtonTagHelper.cs deleted file mode 100644 index bda243ba3..000000000 --- a/src/Util.Ui.Angular/Material/Buttons/TagHelpers/ButtonTagHelper.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Material.Buttons.Configs; -using Util.Ui.Material.Buttons.Renders; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Buttons.TagHelpers { - /// - /// 按钮 - /// - [HtmlTargetElement("util-button")] - public class ButtonTagHelper : TagHelperBase { - /// - /// 文本 - /// - public string Text { get; set; } - /// - /// 类型 - /// - public ButtonType Type { get; set; } - /// - /// 样式 - /// - public ButtonStyle Styles { get; set; } - /// - /// 颜色 - /// - public Color Color { get; set; } - /// - /// 禁用 - /// - public string Disabled { get; set; } - /// - /// 提示 - /// - public string Tooltip { get; set; } - /// - /// 单击事件处理函数,范例:handle() - /// - public string OnClick { get; set; } - /// - /// 菜单标识 - /// - public string MenuId { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - var config = new ButtonConfig( context ); - if( config.Contains( MaterialConst.MenuId ) ) - return new ButtonRender( config ); - return new ButtonWrapperRender( config ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Cards/Builders/CardActionBuilder.cs b/src/Util.Ui.Angular/Material/Cards/Builders/CardActionBuilder.cs deleted file mode 100644 index 7548161a3..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Builders/CardActionBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Cards.Builders { - /// - /// Mat卡片操作生成器 - /// - public class CardActionBuilder : TagBuilder { - /// - /// 初始化卡片操作生成器 - /// - public CardActionBuilder() : base( "mat-card-actions" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Builders/CardAvatarBuilder.cs b/src/Util.Ui.Angular/Material/Cards/Builders/CardAvatarBuilder.cs deleted file mode 100644 index 45e032d9b..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Builders/CardAvatarBuilder.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Microsoft.AspNetCore.Mvc.Rendering; -using TagBuilder = Util.Ui.Builders.TagBuilder; - -namespace Util.Ui.Material.Cards.Builders { - /// - /// Mat卡片头像生成器 - /// - public class CardAvatarBuilder : TagBuilder { - /// - /// 初始化卡片头像生成器 - /// - public CardAvatarBuilder() : base( "img", TagRenderMode.SelfClosing ) { - AddAttribute( "mat-card-avatar" ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Builders/CardBuilder.cs b/src/Util.Ui.Angular/Material/Cards/Builders/CardBuilder.cs deleted file mode 100644 index 8dd63c443..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Builders/CardBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Cards.Builders { - /// - /// Mat卡片生成器 - /// - public class CardBuilder : TagBuilder { - /// - /// 初始化卡片生成器 - /// - public CardBuilder() : base( "mat-card" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Builders/CardContentBuilder.cs b/src/Util.Ui.Angular/Material/Cards/Builders/CardContentBuilder.cs deleted file mode 100644 index 0550657e8..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Builders/CardContentBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Cards.Builders { - /// - /// Mat卡片内容生成器 - /// - public class CardContentBuilder : TagBuilder { - /// - /// 初始化卡片内容生成器 - /// - public CardContentBuilder() : base( "mat-card-content" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Builders/CardFooterBuilder.cs b/src/Util.Ui.Angular/Material/Cards/Builders/CardFooterBuilder.cs deleted file mode 100644 index 11333fac3..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Builders/CardFooterBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Cards.Builders { - /// - /// Mat卡片底部生成器 - /// - public class CardFooterBuilder : TagBuilder { - /// - /// 初始化卡片底部生成器 - /// - public CardFooterBuilder() : base( "mat-card-footer" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Builders/CardHeaderBuilder.cs b/src/Util.Ui.Angular/Material/Cards/Builders/CardHeaderBuilder.cs deleted file mode 100644 index 7ba8d3e42..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Builders/CardHeaderBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Cards.Builders { - /// - /// Mat卡片头部生成器 - /// - public class CardHeaderBuilder : TagBuilder { - /// - /// 初始化卡片头部生成器 - /// - public CardHeaderBuilder() : base( "mat-card-header" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Builders/CardImageBuilder.cs b/src/Util.Ui.Angular/Material/Cards/Builders/CardImageBuilder.cs deleted file mode 100644 index bfb27344a..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Builders/CardImageBuilder.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Microsoft.AspNetCore.Mvc.Rendering; -using TagBuilder = Util.Ui.Builders.TagBuilder; - -namespace Util.Ui.Material.Cards.Builders { - /// - /// Mat卡片图片生成器 - /// - public class CardImageBuilder : TagBuilder { - /// - /// 初始化卡片图片生成器 - /// - public CardImageBuilder() : base( "img",TagRenderMode.SelfClosing ) { - AddAttribute( "mat-card-image" ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Builders/CardSubTitleBuilder.cs b/src/Util.Ui.Angular/Material/Cards/Builders/CardSubTitleBuilder.cs deleted file mode 100644 index dbbbca504..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Builders/CardSubTitleBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Cards.Builders { - /// - /// Mat卡片副标题生成器 - /// - public class CardSubTitleBuilder : TagBuilder { - /// - /// 初始化卡片副标题生成器 - /// - public CardSubTitleBuilder() : base( "mat-card-subtitle" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Builders/CardTitleBuilder.cs b/src/Util.Ui.Angular/Material/Cards/Builders/CardTitleBuilder.cs deleted file mode 100644 index f9a6d41bf..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Builders/CardTitleBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Cards.Builders { - /// - /// Mat卡片标题生成器 - /// - public class CardTitleBuilder : TagBuilder { - /// - /// 初始化卡片标题生成器 - /// - public CardTitleBuilder() : base( "mat-card-title" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Builders/CardTitleGroupBuilder.cs b/src/Util.Ui.Angular/Material/Cards/Builders/CardTitleGroupBuilder.cs deleted file mode 100644 index 589823786..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Builders/CardTitleGroupBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Cards.Builders { - /// - /// Mat卡片标题组生成器 - /// - public class CardTitleGroupBuilder : TagBuilder { - /// - /// 初始化卡片标题组生成器 - /// - public CardTitleGroupBuilder() : base( "mat-card-title-group" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Renders/CardActionRender.cs b/src/Util.Ui.Angular/Material/Cards/Renders/CardActionRender.cs deleted file mode 100644 index 0203b92f1..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Renders/CardActionRender.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Builders; -using Util.Ui.Material.Enums; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Cards.Renders { - /// - /// 卡片操作渲染器 - /// - public class CardActionRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化卡片操作渲染器 - /// - /// 配置 - public CardActionRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new CardActionBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigAlign( builder ); - ConfigContent( builder ); - } - - /// - /// 配置对齐方式 - /// - private void ConfigAlign( TagBuilder builder ) { - if( _config.Contains( UiConst.Align ) == false ) - return; - builder.AddAttribute( UiConst.Align, _config.GetValue( UiConst.Align ) == XPosition.Left ? "start" : "end" ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Renders/CardAvatarRender.cs b/src/Util.Ui.Angular/Material/Cards/Renders/CardAvatarRender.cs deleted file mode 100644 index 04011897b..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Renders/CardAvatarRender.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Cards.Renders { - /// - /// 卡片头像渲染器 - /// - public class CardAvatarRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化卡片头像渲染器 - /// - /// 配置 - public CardAvatarRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new CardAvatarBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigSrc( builder ); - } - - /// - /// 配置路径 - /// - private void ConfigSrc( TagBuilder builder ) { - builder.AddAttribute( UiConst.Src, _config.GetValue( UiConst.Src ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Renders/CardContentRender.cs b/src/Util.Ui.Angular/Material/Cards/Renders/CardContentRender.cs deleted file mode 100644 index e42e12684..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Renders/CardContentRender.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Cards.Renders { - /// - /// 卡片内容渲染器 - /// - public class CardContentRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化卡片内容渲染器 - /// - /// 配置 - public CardContentRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new CardContentBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigContent( builder ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Renders/CardFooterRender.cs b/src/Util.Ui.Angular/Material/Cards/Renders/CardFooterRender.cs deleted file mode 100644 index e4d6ecba0..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Renders/CardFooterRender.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Cards.Renders { - /// - /// 卡片底部渲染器 - /// - public class CardFooterRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化卡片底部渲染器 - /// - /// 配置 - public CardFooterRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new CardFooterBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigContent( builder ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Renders/CardHeaderRender.cs b/src/Util.Ui.Angular/Material/Cards/Renders/CardHeaderRender.cs deleted file mode 100644 index dde09eedb..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Renders/CardHeaderRender.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Cards.Renders { - /// - /// 卡片头部渲染器 - /// - public class CardHeaderRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化卡片头部渲染器 - /// - /// 配置 - public CardHeaderRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new CardHeaderBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigContent( builder ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Renders/CardImageRender.cs b/src/Util.Ui.Angular/Material/Cards/Renders/CardImageRender.cs deleted file mode 100644 index 7c10a7bd1..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Renders/CardImageRender.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Cards.Renders { - /// - /// 卡片图片渲染器 - /// - public class CardImageRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化卡片图片渲染器 - /// - /// 配置 - public CardImageRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new CardImageBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigSrc( builder ); - } - - /// - /// 配置路径 - /// - private void ConfigSrc( TagBuilder builder ) { - builder.AddAttribute( UiConst.Src, _config.GetValue( UiConst.Src ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Renders/CardRender.cs b/src/Util.Ui.Angular/Material/Cards/Renders/CardRender.cs deleted file mode 100644 index 9a467b4ce..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Renders/CardRender.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Cards.Renders { - /// - /// 卡片渲染器 - /// - public class CardRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化卡片渲染器 - /// - /// 配置 - public CardRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new CardBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigContent( builder ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Renders/CardSubTitleRender.cs b/src/Util.Ui.Angular/Material/Cards/Renders/CardSubTitleRender.cs deleted file mode 100644 index 8d06ee331..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Renders/CardSubTitleRender.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Cards.Renders { - /// - /// 卡片副标题渲染器 - /// - public class CardSubTitleRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化卡片副标题渲染器 - /// - /// 配置 - public CardSubTitleRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new CardSubTitleBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigContent( builder ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Renders/CardTitleGroupRender.cs b/src/Util.Ui.Angular/Material/Cards/Renders/CardTitleGroupRender.cs deleted file mode 100644 index 20611ac9d..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Renders/CardTitleGroupRender.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Cards.Renders { - /// - /// 卡片标题组渲染器 - /// - public class CardTitleGroupRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化卡片标题组渲染器 - /// - /// 配置 - public CardTitleGroupRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new CardTitleGroupBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigContent( builder ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Renders/CardTitleImageRender.cs b/src/Util.Ui.Angular/Material/Cards/Renders/CardTitleImageRender.cs deleted file mode 100644 index 157b1b99a..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Renders/CardTitleImageRender.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Cards.Renders { - /// - /// 卡片标题图片渲染器 - /// - public class CardTitleImageRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化卡片标题图片渲染器 - /// - /// 配置 - public CardTitleImageRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new ImageBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigSize( builder ); - ConfigSrc( builder ); - } - - /// - /// 配置大小 - /// - private void ConfigSize( TagBuilder builder ) { - var size = _config.GetValue( UiConst.Size ) ?? CardTitleImageSize.Middle; - builder.AddAttribute( size.Description() ); - } - - /// - /// 配置路径 - /// - private void ConfigSrc( TagBuilder builder ) { - builder.AddAttribute( UiConst.Src, _config.GetValue( UiConst.Src ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/Renders/CardTitleRender.cs b/src/Util.Ui.Angular/Material/Cards/Renders/CardTitleRender.cs deleted file mode 100644 index e6aa93cf9..000000000 --- a/src/Util.Ui.Angular/Material/Cards/Renders/CardTitleRender.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Cards.Renders { - /// - /// 卡片标题渲染器 - /// - public class CardTitleRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化卡片标题渲染器 - /// - /// 配置 - public CardTitleRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new CardTitleBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigContent( builder ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardActionTagHelper.cs b/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardActionTagHelper.cs deleted file mode 100644 index 1432faf2b..000000000 --- a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardActionTagHelper.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Renders; -using Util.Ui.Material.Enums; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Cards.TagHelpers { - /// - /// 卡片操作 - /// - [HtmlTargetElement( "util-card-actions" )] - public class CardActionTagHelper : TagHelperBase { - /// - /// 对齐方式 - /// - public XPosition Align { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new CardActionRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardAvatarTagHelper.cs b/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardAvatarTagHelper.cs deleted file mode 100644 index 86284185c..000000000 --- a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardAvatarTagHelper.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Cards.TagHelpers { - /// - /// 卡片头像,该标签应放到 util-card-header 中 - /// - [HtmlTargetElement( "util-card-header-avatar", TagStructure = TagStructure.WithoutEndTag )] - public class CardAvatarTagHelper : TagHelperBase { - /// - /// 图片路径 - /// - public string Src { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new CardAvatarRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardContentTagHelper.cs b/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardContentTagHelper.cs deleted file mode 100644 index 38a13e85d..000000000 --- a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardContentTagHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Cards.TagHelpers { - /// - /// 卡片内容 - /// - [HtmlTargetElement( "util-card-content" )] - public class CardContentTagHelper : TagHelperBase { - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new CardContentRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardFooterTagHelper.cs b/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardFooterTagHelper.cs deleted file mode 100644 index d9e89f409..000000000 --- a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardFooterTagHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Cards.TagHelpers { - /// - /// 卡片底部 - /// - [HtmlTargetElement( "util-card-footer" )] - public class CardFooterTagHelper : TagHelperBase { - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new CardFooterRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardHeaderTagHelper.cs b/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardHeaderTagHelper.cs deleted file mode 100644 index 784ec5df3..000000000 --- a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardHeaderTagHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Cards.TagHelpers { - /// - /// 卡片头部 - /// - [HtmlTargetElement( "util-card-header" )] - public class CardHeaderTagHelper : TagHelperBase { - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new CardHeaderRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardImageTagHelper.cs b/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardImageTagHelper.cs deleted file mode 100644 index ba346a6d5..000000000 --- a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardImageTagHelper.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Cards.TagHelpers { - /// - /// 卡片图片 - /// - [HtmlTargetElement( "util-card-image", TagStructure = TagStructure.WithoutEndTag )] - public class CardImageTagHelper : TagHelperBase { - /// - /// 图片路径 - /// - public string Src { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new CardImageRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardSubTitleTagHelper.cs b/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardSubTitleTagHelper.cs deleted file mode 100644 index bf808cf12..000000000 --- a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardSubTitleTagHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Cards.TagHelpers { - /// - /// 卡片副标题 - /// - [HtmlTargetElement( "util-card-subtitle" )] - public class CardSubTitleTagHelper : TagHelperBase { - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new CardSubTitleRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardTagHelper.cs b/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardTagHelper.cs deleted file mode 100644 index 4a4d7ed24..000000000 --- a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardTagHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Cards.TagHelpers { - /// - /// 卡片 - /// - [HtmlTargetElement( "util-card" )] - public class CardTagHelper : TagHelperBase { - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new CardRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardTitleGroupTagHelper.cs b/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardTitleGroupTagHelper.cs deleted file mode 100644 index 7d76c1931..000000000 --- a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardTitleGroupTagHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Cards.TagHelpers { - /// - /// 卡片标题组 - /// - [HtmlTargetElement( "util-card-title-group" )] - public class CardTitleGroupTagHelper : TagHelperBase { - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new CardTitleGroupRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardTitleImageTagHelper.cs b/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardTitleImageTagHelper.cs deleted file mode 100644 index 6b2ba44c8..000000000 --- a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardTitleImageTagHelper.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Renders; -using Util.Ui.Material.Enums; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Cards.TagHelpers { - /// - /// 卡片标题图片,该标签应放到 util-card-title-group 中 - /// - [HtmlTargetElement( "util-card-title-image",TagStructure = TagStructure.WithoutEndTag)] - public class CardTitleImageTagHelper : TagHelperBase { - /// - /// 图片路径 - /// - public string Src { get; set; } - /// - /// 图片大小 - /// - public CardTitleImageSize Size { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new CardTitleImageRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardTitleTagHelper.cs b/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardTitleTagHelper.cs deleted file mode 100644 index e9398953e..000000000 --- a/src/Util.Ui.Angular/Material/Cards/TagHelpers/CardTitleTagHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Cards.TagHelpers { - /// - /// 卡片标题 - /// - [HtmlTargetElement( "util-card-title" )] - public class CardTitleTagHelper : TagHelperBase { - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new CardTitleRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Commons/Configs/MaterialConst.cs b/src/Util.Ui.Angular/Material/Commons/Configs/MaterialConst.cs deleted file mode 100644 index 5b3380cb1..000000000 --- a/src/Util.Ui.Angular/Material/Commons/Configs/MaterialConst.cs +++ /dev/null @@ -1,115 +0,0 @@ -namespace Util.Ui.Material.Commons.Configs { - /// - /// Material常量 - /// - public static class MaterialConst { - /// - /// 占位提示浮动位置 - /// - public const string FloatPlaceholder = "float-placeholder"; - /// - /// 启用重置项 - /// - public const string EnableResetOption = "enable-reset-option"; - /// - /// 重置项文本 - /// - public const string ResetOptionText = "reset-option-text"; - /// - /// 起始提示 - /// - public const string StartHint = "start-hint"; - /// - /// 结束提示 - /// - public const string EndHint = "end-hint"; - /// - /// 后缀文本 - /// - public const string SuffixText = "suffix-text"; - /// - /// 后缀FontAwesome图标 - /// - public const string SuffixFontAwesomeIcon = "suffix-font-awesome-icon"; - /// - /// 后缀Material图标 - /// - public const string SuffixMaterialIcon = "suffix-material-icon"; - /// - /// 后缀图标单击事件 - /// - public const string OnSuffixIconClick = "on-suffix-icon-click"; - /// - /// 是否显示清除按钮 - /// - public const string ShowClearButton = "show-clear-button"; - /// - /// 最小行数 - /// - public const string MinRows = "min-rows"; - /// - /// 最大行数 - /// - public const string MaxRows = "max-rows"; - /// - /// 起始视图 - /// - public const string StartView = "start-view"; - /// - /// 触摸模式 - /// - public const string TouchUi = "touch-ui"; - /// - /// 最小日期 - /// - public const string MinDate = "min-date"; - /// - /// 最大日期 - /// - public const string MaxDate = "max-date"; - /// - /// 是否显示标签 - /// - public const string ShowLabel = "show-label"; - /// - /// 菜单标识 - /// - public const string MenuId = "menu-id"; - /// - /// 动态高度 - /// - public const string DynamicHeight = "dynamic-height"; - /// - /// 标题位置 - /// - public const string HeaderPosition = "header-position"; - /// - /// 禁用关闭 - /// - public const string DisableClose = "disable-close"; - /// - /// 插入 - /// - public const string Inset = "inset"; - /// - /// 隐藏开关 - /// - public const string HideToggle = "hide-toggle"; - /// - /// 显示模式 - /// - public const string DisplayMode = "display-mode"; - /// - /// 折叠高度 - /// - public const string CollapsedHeight = "collapsed-height"; - /// - /// 展开高度 - /// - public const string ExpandedHeight = "expanded-height"; - /// - /// 单元格间距 - /// - public const string GutterSize = "gutter-size"; - } -} diff --git a/src/Util.Ui.Angular/Material/Commons/Internal/Helper.cs b/src/Util.Ui.Angular/Material/Commons/Internal/Helper.cs deleted file mode 100644 index 32ed9f36a..000000000 --- a/src/Util.Ui.Angular/Material/Commons/Internal/Helper.cs +++ /dev/null @@ -1,212 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.Linq.Expressions; -using System.Reflection; -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Util.Helpers; -using Util.Ui.Attributes; -using Util.Ui.Configs; -using Util.Ui.Material.Forms.Configs; - -namespace Util.Ui.Material.Commons.Internal { - /// - /// 辅助操作 - /// - internal static class Helper { - /// - /// 初始化基础配置 - /// - /// 模型类型 - /// 属性类型 - /// 配置 - /// 属性表达式 - /// 成员 - public static void Init( IConfig config, Expression> expression, MemberInfo member ) { - Type modelType = Common.GetType(); - var propertyName = Lambda.GetName( expression ); - Init( config, modelType, member, propertyName ); - } - - /// - /// 初始化基础配置 - /// - /// 配置 - /// 属性表达式 - /// 成员 - public static void Init( IConfig config, ModelExpression expression, MemberInfo member ) { - Type modelType = expression.Metadata.ContainerType; - var propertyName = expression.Name; - Init( config, modelType, member, propertyName ); - } - - /// - /// 初始化基础配置 - /// - private static void Init( IConfig config, Type modelType, MemberInfo member, string propertyName ) { - config.SetAttribute( UiConst.Name, Util.Helpers.String.FirstLowerCase( propertyName ) ); - var displayName = Reflection.GetDisplayNameOrDescription( member ); - config.SetAttribute( UiConst.Placeholder, displayName ); - config.SetAttribute( UiConst.Label, displayName ); - InitModel( config, modelType, member, propertyName ); - InitRequired( config, member ); - } - - /// - /// 初始化模型绑定 - /// - private static void InitModel( IConfig config, Type modelType, MemberInfo member, string propertyName ) { - var model = GetModel( GetModelName( modelType ), GetPropertyName( member, propertyName ) ); - if( string.IsNullOrWhiteSpace( model ) ) - return; - config.SetAttribute( UiConst.Model, model ); - } - - /// - /// 获取模型绑定 - /// - private static string GetModel( string modelName, string propertyName ) { - modelName = Util.Helpers.String.FirstLowerCase( modelName ); - propertyName = Util.Helpers.String.FirstLowerCase( propertyName ); - if( string.IsNullOrWhiteSpace( modelName ) || string.IsNullOrWhiteSpace( propertyName ) ) - return string.Empty; - return $"{modelName}&&{modelName}.{propertyName}"; - } - - /// - /// 获取模型名称 - /// - private static string GetModelName( Type modelType ) { - if( modelType.GetCustomAttribute() is ModelAttribute attribute ) - return attribute.Ignore ? string.Empty : attribute.Model; - return modelType.Name; - } - - /// - /// 获取属性名称 - /// - private static string GetPropertyName( MemberInfo member, string propertyName ) { - var attribute = member.GetCustomAttribute(); - return attribute == null ? propertyName : attribute.Ignore ? string.Empty : attribute.Model; ; - } - - /// - /// 初始化必填项验证 - /// - /// 配置 - /// 成员 - private static void InitRequired( IConfig config, MemberInfo member ) { - var attribute = member.GetCustomAttribute(); - if( attribute == null ) - return; - config.SetAttribute( UiConst.Required, true ); - config.SetAttribute( UiConst.RequiredMessage, attribute.ErrorMessage ); - } - - /// - /// 初始化数据类型 - /// - /// 配置 - /// 成员 - public static void InitDataType( TextBoxConfig config, MemberInfo member ) { - if( Reflection.IsDate( member ) ) { - config.IsDatePicker = true; - return; - } - if( Reflection.IsNumber( member ) ) { - config.Number(); - return; - } - InitDataType( config, member.GetCustomAttribute() ); - } - - /// - /// 初始化数据类型 - /// - private static void InitDataType( TextBoxConfig config, DataTypeAttribute attribute ) { - if( attribute == null ) - return; - switch ( attribute.DataType ) { - case DataType.Date: - case DataType.DateTime: - case DataType.Time: - config.IsDatePicker = true; - break; - case DataType.MultilineText: - config.IsTextArea = true; - break; - case DataType.EmailAddress: - config.Email(); - break; - case DataType.Password: - config.Password(); - break; - } - } - - /// - /// 初始化验证 - /// - /// 配置 - /// 成员 - public static void InitValidation( TextBoxConfig config, MemberInfo member ) { - InitLength( config, member ); - InitEmail( config, member ); - } - - /// - /// 初始化字符串长度验证 - /// - private static void InitLength( TextBoxConfig config, MemberInfo member ) { - InitStringLength( config, member ); - InitMinLength( config, member ); - InitMaxLength( config, member ); - } - - /// - /// 初始化字符串长度验证 - /// - private static void InitStringLength( TextBoxConfig config, MemberInfo member ) { - var attribute = member.GetCustomAttribute(); - if( attribute == null ) - return; - if( attribute.MinimumLength > 0 ) - config.SetAttribute( UiConst.MinLength, attribute.MinimumLength ); - if( attribute.MaximumLength > 0 ) - config.SetAttribute( UiConst.MaxLength, attribute.MaximumLength ); - } - - /// - /// 初始化字符串最小长度验证 - /// - private static void InitMinLength( TextBoxConfig config, MemberInfo member ) { - var attribute = member.GetCustomAttribute(); - if( attribute == null ) - return; - config.SetAttribute( UiConst.MinLength, attribute.Length ); - config.SetAttribute( UiConst.MinLengthMessage, attribute.ErrorMessage ); - } - - /// - /// 初始化字符串最大长度验证 - /// - private static void InitMaxLength( TextBoxConfig config, MemberInfo member ) { - var attribute = member.GetCustomAttribute(); - if( attribute == null ) - return; - config.SetAttribute( UiConst.MaxLength, attribute.Length ); - } - - /// - /// 初始化电子邮件验证 - /// - private static void InitEmail( TextBoxConfig config, MemberInfo member ) { - var attribute = member.GetCustomAttribute(); - if( attribute == null ) - return; - config.Email(); - if( attribute.ErrorMessage.Contains( "field is not a valid e-mail address" ) ) - return; - config.SetAttribute( UiConst.EmailMessage, attribute.ErrorMessage ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Dividers/Builders/DividerBuilder.cs b/src/Util.Ui.Angular/Material/Dividers/Builders/DividerBuilder.cs deleted file mode 100644 index ef7e3c2dc..000000000 --- a/src/Util.Ui.Angular/Material/Dividers/Builders/DividerBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Dividers.Builders { - /// - /// Mat分隔线生成器 - /// - public class DividerBuilder : TagBuilder { - /// - /// 初始化分隔线生成器 - /// - public DividerBuilder() : base( "mat-divider" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Dividers/Renders/DividerRender.cs b/src/Util.Ui.Angular/Material/Dividers/Renders/DividerRender.cs deleted file mode 100644 index 13b374f00..000000000 --- a/src/Util.Ui.Angular/Material/Dividers/Renders/DividerRender.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Dividers.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Dividers.Renders { - /// - /// 分隔线渲染器 - /// - public class DividerRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化分隔线渲染器 - /// - /// 配置 - public DividerRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new DividerBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigInset( builder ); - ConfigVertical( builder ); - ConfigContent( builder ); - } - - /// - /// 配置间距 - /// - private void ConfigInset( TagBuilder builder ) { - builder.AddAttribute( MaterialConst.Inset, _config.GetBoolValue( MaterialConst.Inset ) ); - } - - /// - /// 配置垂直方向 - /// - private void ConfigVertical( TagBuilder builder ) { - builder.AddAttribute( UiConst.Vertical, _config.GetBoolValue( UiConst.Vertical ) ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Dividers/TagHelpers/DividerTagHelper.cs b/src/Util.Ui.Angular/Material/Dividers/TagHelpers/DividerTagHelper.cs deleted file mode 100644 index cb180b93f..000000000 --- a/src/Util.Ui.Angular/Material/Dividers/TagHelpers/DividerTagHelper.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Dividers.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Dividers.TagHelpers { - /// - /// 分隔线 - /// - [HtmlTargetElement( "util-divider",TagStructure = TagStructure.WithoutEndTag)] - public class DividerTagHelper : TagHelperBase { - /// - /// 分隔线两端是否存在间距 - /// - public bool Inset { get; set; } - /// - /// 垂直方向 - /// - public bool Vertical { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new DividerRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Enums/AccordionDisplayMode.cs b/src/Util.Ui.Angular/Material/Enums/AccordionDisplayMode.cs deleted file mode 100644 index 438695ab4..000000000 --- a/src/Util.Ui.Angular/Material/Enums/AccordionDisplayMode.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Util.Ui.Material.Enums { - /// - /// 手风琴显示模式 - /// - public enum AccordionDisplayMode { - /// - /// 默认,展开的面板间存在间距 - /// - Default, - /// - /// 展开的面板间没有间距 - /// - Flat - } -} diff --git a/src/Util.Ui.Angular/Material/Enums/ButtonStyle.cs b/src/Util.Ui.Angular/Material/Enums/ButtonStyle.cs deleted file mode 100644 index e9fa9dad3..000000000 --- a/src/Util.Ui.Angular/Material/Enums/ButtonStyle.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.ComponentModel; - -namespace Util.Ui.Material.Enums { - /// - /// 按钮样式 - /// - public enum ButtonStyle { - /// - /// mat-button - /// - [Description( "mat-button" )] - Default, - /// - /// mat-raised-button - /// - [Description( "mat-raised-button" )] - Raised, - /// - /// mat-icon-button - /// - [Description( "mat-icon-button" )] - Icon, - /// - /// mat-fab - /// - [Description( "mat-fab" )] - Fab, - /// - /// mat-mini-fab - /// - [Description( "mat-mini-fab" )] - MiniFab - } -} diff --git a/src/Util.Ui.Angular/Material/Enums/ButtonType.cs b/src/Util.Ui.Angular/Material/Enums/ButtonType.cs deleted file mode 100644 index 156c718c1..000000000 --- a/src/Util.Ui.Angular/Material/Enums/ButtonType.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Util.Ui.Material.Enums { - /// - /// 按钮类型 - /// - public enum ButtonType { - /// - /// 重置 - /// - Reset, - /// - /// 提交 - /// - Submit - } -} diff --git a/src/Util.Ui.Angular/Material/Enums/CardTitleImageSize.cs b/src/Util.Ui.Angular/Material/Enums/CardTitleImageSize.cs deleted file mode 100644 index 1f8fa42b5..000000000 --- a/src/Util.Ui.Angular/Material/Enums/CardTitleImageSize.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.ComponentModel; - -namespace Util.Ui.Material.Enums { - /// - /// 卡片标题图片大小 - /// - public enum CardTitleImageSize { - /// - /// 小 - /// - [Description( "mat-card-sm-image" )] - Small, - /// - /// 中 - /// - [Description( "mat-card-md-image" )] - Middle, - /// - /// 大 - /// - [Description( "mat-card-lg-image" )] - Large - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Enums/Color.cs b/src/Util.Ui.Angular/Material/Enums/Color.cs deleted file mode 100644 index e17d8bba3..000000000 --- a/src/Util.Ui.Angular/Material/Enums/Color.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Util.Ui.Material.Enums { - /// - /// 颜色 - /// - public enum Color { - /// - /// 深蓝色 - /// - Primary, - /// - /// 桃红色 - /// - Accent, - /// - /// 大红色 - /// - Warn - } -} diff --git a/src/Util.Ui.Angular/Material/Enums/DateView.cs b/src/Util.Ui.Angular/Material/Enums/DateView.cs deleted file mode 100644 index 75219f2be..000000000 --- a/src/Util.Ui.Angular/Material/Enums/DateView.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.ComponentModel; - -namespace Util.Ui.Material.Enums { - /// - /// 日期视图 - /// - public enum DateView { - /// - /// 年 - /// - [Description( "year" )] - Year, - /// - /// 月 - /// - [Description( "month" )] - Month - } -} diff --git a/src/Util.Ui.Angular/Material/Enums/FloatType.cs b/src/Util.Ui.Angular/Material/Enums/FloatType.cs deleted file mode 100644 index 448cedc32..000000000 --- a/src/Util.Ui.Angular/Material/Enums/FloatType.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.ComponentModel; - -namespace Util.Ui.Material.Enums { - /// - /// 占位符浮动类型 - /// - public enum FloatType { - /// - /// 默认 - /// - [Description( "auto" )] - Auto, - /// - /// 设置文本后隐藏占位符 - /// - [Description( "never" )] - Never, - /// - /// 始终显示浮动占位符 - /// - [Description( "always" )] - Always - } -} diff --git a/src/Util.Ui.Angular/Material/Enums/Position.cs b/src/Util.Ui.Angular/Material/Enums/Position.cs deleted file mode 100644 index 7deb51ae3..000000000 --- a/src/Util.Ui.Angular/Material/Enums/Position.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.ComponentModel; - -namespace Util.Ui.Material.Enums { - /// - /// X轴位置 - /// - public enum XPosition { - /// - /// 左边 - /// - [Description( "before" )] - Left, - /// - /// 右边 - /// - [Description( "after" )] - Right - } - - /// - /// Y轴位置 - /// - public enum YPosition { - /// - /// 上方 - /// - [Description( "above" )] - Above, - /// - /// 下方 - /// - [Description( "below" )] - Below - } -} diff --git a/src/Util.Ui.Angular/Material/Enums/SideNavMode.cs b/src/Util.Ui.Angular/Material/Enums/SideNavMode.cs deleted file mode 100644 index 439851d11..000000000 --- a/src/Util.Ui.Angular/Material/Enums/SideNavMode.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Util.Ui.Material.Enums { - /// - /// 导航侧边栏显示模式 - /// - public enum SideNavMode { - /// - /// 侧边栏导航区域显示在内容区域的上方,内容区域被遮罩 - /// - Over, - /// - /// 侧边栏导航区域显示在内容区域的侧面,内容区域被遮罩 - /// - Push, - /// - /// 侧边栏导航区域与内容区域并排显示 - /// - Side - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Enums/TextBoxType.cs b/src/Util.Ui.Angular/Material/Enums/TextBoxType.cs deleted file mode 100644 index 09ca07856..000000000 --- a/src/Util.Ui.Angular/Material/Enums/TextBoxType.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.ComponentModel; - -namespace Util.Ui.Material.Enums { - /// - /// 文本框类型 - /// - public enum TextBoxType { - /// - /// 文本框 - /// - [Description( "text" )] - Text, - /// - /// 密码框 - /// - [Description( "password" )] - Password, - /// - /// 数字框 - /// - [Description( "number" )] - Number, - /// - /// 电子邮件框 - /// - [Description( "email" )] - Email - } -} diff --git a/src/Util.Ui.Angular/Material/Extensions/Extensions.Button.cs b/src/Util.Ui.Angular/Material/Extensions/Extensions.Button.cs deleted file mode 100644 index 07e54b6cd..000000000 --- a/src/Util.Ui.Angular/Material/Extensions/Extensions.Button.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Components.Internal; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Operations.Navigation; -using Util.Ui.Operations.Styles; - -namespace Util.Ui.Material.Extensions { - /// - /// 按钮扩展 - /// - public static partial class Extensions { - /// - /// 重置 - /// - /// 组件类型 - /// 组件实例 - public static TComponent Reset( this TComponent component ) where TComponent : IButton { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Type, ButtonType.Reset ); - } ); - return component; - } - - /// - /// 提交 - /// - /// 组件类型 - /// 组件实例 - public static TComponent Submit( this TComponent component ) where TComponent : IButton { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Type, ButtonType.Submit ); - } ); - return component; - } - - /// - /// 设置样式 - /// - /// 组件类型 - /// 组件实例 - /// 样式 - public static TComponent Style( this TComponent component, ButtonStyle style ) where TComponent : IButtonStyle { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Styles, style ); - } ); - return component; - } - - /// - /// 设置路由链接地址 - /// - /// 组件类型 - /// 组件实例 - /// 路由链接地址 - public static TComponent Link( this TComponent component, string routerLink ) where TComponent : ILink { - var option = component as IOptionConfig; - option?.Config( config => { - if( string.IsNullOrWhiteSpace( routerLink ) ) - return; - config.SetAttribute( UiConst.Link, routerLink ); - } ); - return component; - } - } -} diff --git a/src/Util.Ui.Angular/Material/Extensions/Extensions.CheckBox.cs b/src/Util.Ui.Angular/Material/Extensions/Extensions.CheckBox.cs deleted file mode 100644 index b239e756e..000000000 --- a/src/Util.Ui.Angular/Material/Extensions/Extensions.CheckBox.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Components.Internal; -using Util.Ui.Configs; - -namespace Util.Ui.Material.Extensions { - /// - /// 复选框扩展 - /// - public static partial class Extensions { - /// - /// 设置不确定样式 - /// - /// 组件类型 - /// 组件实例 - /// 表达式 - public static TComponent Indeterminate( this TComponent component, string expression ) where TComponent : ICheckBox { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Indeterminate, expression ); - } ); - return component; - } - } -} diff --git a/src/Util.Ui.Angular/Material/Extensions/Extensions.Form.cs b/src/Util.Ui.Angular/Material/Extensions/Extensions.Form.cs deleted file mode 100644 index ffe7fb3b3..000000000 --- a/src/Util.Ui.Angular/Material/Extensions/Extensions.Form.cs +++ /dev/null @@ -1,211 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Components.Internal; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Operations; -using Util.Ui.Operations.Forms; - -namespace Util.Ui.Material.Extensions { - /// - /// 表单扩展 - /// - public static partial class Extensions { - /// - /// 设置占位提示符 - /// - /// 组件类型 - /// 组件实例 - /// 占位提示文本 - /// 占位符浮动类型 - public static TComponent Placeholder( this TComponent component, string text, FloatType? type ) where TComponent : IComponent, IPlaceholder { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Placeholder, text ); - config.SetAttribute( MaterialConst.FloatPlaceholder, type ); - } ); - return component; - } - - /// - /// 设置提示,显示在控件下方 - /// - /// 组件类型 - /// 组件实例 - /// 提示文本 - /// 显示到右侧 - public static TComponent Hint( this TComponent component, string text, bool right = false ) where TComponent : IComponent, IHint { - var option = component as IOptionConfig; - option?.Config( config => { - if( right ) { - config.SetAttribute( MaterialConst.EndHint, text ); - return; - } - config.SetAttribute( MaterialConst.StartHint, text ); - } ); - return component; - } - - /// - /// 启用重置项,重置项显示在列表的第一行,用于清空当前选择的值 - /// - /// 组件类型 - /// 组件实例 - /// 重置项文本 - public static TComponent EnableResetOption( this TComponent component, string text ) where TComponent : IComponent, IResetOption { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( MaterialConst.EnableResetOption, true ); - config.SetAttribute( MaterialConst.ResetOptionText, text ); - } ); - return component; - } - - /// - /// 启用重置项,重置项显示在列表的第一行,用于清空当前选择的值 - /// - /// 组件类型 - /// 组件实例 - /// 是否启用 - public static TComponent EnableResetOption( this TComponent component, bool enabled ) where TComponent : IComponent, IResetOption { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( MaterialConst.EnableResetOption, enabled ); - } ); - return component; - } - - /// - /// 启用多选 - /// - /// 组件类型 - /// 组件实例 - public static TComponent Multiple( this TComponent component ) where TComponent : IComponent, IMultiple { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Multiple, true ); - } ); - return component; - } - - /// - /// 设置前缀 - /// - /// 组件类型 - /// 组件实例 - /// 文本 - public static TComponent Prefix( this TComponent component, string text ) where TComponent : IComponent, IPrefix { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Prefix, text ); - } ); - return component; - } - - /// - /// 设置后缀 - /// - /// 组件类型 - /// 组件实例 - /// 文本 - public static TComponent Suffix( this TComponent component, string text ) where TComponent : IComponent, IPrefix { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( MaterialConst.SuffixText, text ); - } ); - return component; - } - - /// - /// 设置后缀 - /// - /// 组件类型 - /// 组件实例 - /// FontAwesome图标 - /// 图标单击事件处理函数,范例:click() - public static TComponent Suffix( this TComponent component, FontAwesomeIcon icon, string onClick = null ) where TComponent : IComponent, IPrefix { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( MaterialConst.SuffixFontAwesomeIcon, icon ); - config.SetAttribute( MaterialConst.OnSuffixIconClick, onClick ); - } ); - return component; - } - - /// - /// 设置后缀 - /// - /// 组件类型 - /// 组件实例 - /// Material图标 - /// 图标单击事件处理函数,范例:click() - public static TComponent Suffix( this TComponent component, MaterialIcon icon, string onClick = null ) where TComponent : IComponent, IPrefix { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( MaterialConst.SuffixMaterialIcon, icon ); - config.SetAttribute( MaterialConst.OnSuffixIconClick, onClick ); - } ); - return component; - } - - /// - /// 设置只读 - /// - /// 组件类型 - /// 组件实例 - /// 是否只读 - public static TComponent ReadOnly( this TComponent component, bool readOnly = true ) where TComponent : IOption, IReadOnly { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.ReadOnly, readOnly ); - } ); - return component; - } - - /// - /// 设置只读 - /// - /// 组件类型 - /// 组件实例 - /// 只读表达式 - public static TComponent ReadOnly( this TComponent component, string readOnly ) where TComponent : IComponent, IReadOnly { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.ReadOnly, readOnly ); - } ); - return component; - } - - /// - /// 设置标签 - /// - /// 组件类型 - /// 组件实例 - /// 文本 - public static TComponent Label( this TComponent component, string text ) where TComponent : ILabel { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Label, text ); - } ); - return component; - } - - /// - /// 设置标签 - /// - /// 组件类型 - /// 组件实例 - /// 文本 - /// 是否显示到左边 - public static TComponent Label( this TComponent component, string text, bool? left ) where TComponent : ILabelPosition { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Label, text ); - if( left != null ) - config.SetAttribute( UiConst.Position, left.SafeValue() ? XPosition.Left : XPosition.Right ); - } ); - return component; - } - } -} diff --git a/src/Util.Ui.Angular/Material/Extensions/Extensions.Menu.cs b/src/Util.Ui.Angular/Material/Extensions/Extensions.Menu.cs deleted file mode 100644 index f7f9236e4..000000000 --- a/src/Util.Ui.Angular/Material/Extensions/Extensions.Menu.cs +++ /dev/null @@ -1,90 +0,0 @@ -using Util.Ui.Components.Internal; -using Util.Ui.Configs; -using Util.Ui.Extensions; -using Util.Ui.Material.Buttons; -using Util.Ui.Material.Buttons.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Menus; -using Util.Ui.Material.Menus.Configs; -using Util.Ui.Material.Menus.Datas; -using Util.Ui.Operations.Navigation; - -namespace Util.Ui.Material.Extensions { - /// - /// 菜单扩展 - /// - public static partial class Extensions { - /// - /// 设置菜单位置 - /// - /// 组件类型 - /// 组件实例 - /// X轴位置 - /// Y轴位置 - public static TComponent Position( this TComponent component, XPosition? xPosition, YPosition? yPosition = null ) where TComponent : IMenu { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.XPosition, xPosition ); - config.SetAttribute( UiConst.YPosition, yPosition ); - } ); - return component; - } - - /// - /// 设置弹出菜单是否与触发按钮重叠 - /// - /// 组件类型 - /// 组件实例 - /// 是否重叠,false代表不重叠 - public static TComponent Overlap( this TComponent component, bool? isOverlap = false ) where TComponent : IMenu { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Overlap, isOverlap ); - } ); - return component; - } - - /// - /// 设置菜单数据 - /// - /// 组件类型 - /// 组件实例 - /// 菜单数据 - public static TComponent Data( this TComponent component, MenuData data ) where TComponent : IMenu { - var option = component as IOptionConfig; - option?.Config( config => { - config.Data = data; - } ); - return component.Id( data.Id ).Position( data.XPosition, data.YPosition ).Overlap( data.Overlap ); - } - - /// - /// 设置菜单 - /// - /// 组件类型 - /// 组件实例 - /// 菜单节点集合 - public static TComponent Menu( this TComponent component, MenuNodeCollection nodes ) where TComponent : IButton { - var option = component as IOptionConfig; - option?.Config( config => { - config.Data = nodes.ToMenuDatas(); - } ); - return component.Menu( nodes.RootId ); - } - - /// - /// 设置菜单 - /// - /// 组件类型 - /// 组件实例 - /// 菜单标识 - public static TComponent Menu( this TComponent component, string menuId ) where TComponent : IMenuId { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( MaterialConst.MenuId, menuId ); - } ); - return component; - } - } -} diff --git a/src/Util.Ui.Angular/Material/Extensions/Extensions.Radio.cs b/src/Util.Ui.Angular/Material/Extensions/Extensions.Radio.cs deleted file mode 100644 index 3c29dcf65..000000000 --- a/src/Util.Ui.Angular/Material/Extensions/Extensions.Radio.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Components.Internal; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; - -namespace Util.Ui.Material.Extensions { - /// - /// 单选框扩展 - /// - public static partial class Extensions { - /// - /// 设置标签 - /// - /// 组件类型 - /// 组件实例 - /// 是否显示标签 - public static TComponent Label( this TComponent component, bool showLabel ) where TComponent : IRadio { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( MaterialConst.ShowLabel, showLabel ); - } ); - return component; - } - - /// - /// 垂直布局 - /// - /// 组件类型 - /// 组件实例 - public static TComponent Vertical( this TComponent component ) where TComponent : IRadio { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Vertical, true ); - } ); - return component; - } - } -} diff --git a/src/Util.Ui.Angular/Material/Extensions/Extensions.Select.cs b/src/Util.Ui.Angular/Material/Extensions/Extensions.Select.cs deleted file mode 100644 index 8a42b860d..000000000 --- a/src/Util.Ui.Angular/Material/Extensions/Extensions.Select.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Components.Internal; -using Util.Ui.Configs; -using Util.Ui.Material.Forms.Configs; -using Util.Ui.Operations.Datas; - -namespace Util.Ui.Material.Extensions { - /// - /// 下拉列表扩展 - /// - public static partial class Extensions { - /// - /// 显示模板,值用{0}表示,范例:当前选中:{0} ,显示为 当前选中:1,2,3 - /// - /// 组件类型 - /// 组件实例 - /// 模板 - public static TComponent Template( this TComponent component, string template ) where TComponent : ISelect { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Template, template ); - } ); - return component; - } - - /// - /// 添加项 - /// - /// 组件类型 - /// 组件实例 - /// 文本 - /// 值 - /// 组 - public static TComponent Add( this TComponent component, string text, object value,string group = null ) where TComponent : IItem { - var option = component as IOptionConfig; - option?.Config( config => { - config.AddItems( new Item( text, value, null, group ) ); - } ); - return component; - } - - /// - /// 添加项 - /// - /// 组件类型 - /// 组件实例 - /// 列表项集合 - public static TComponent Add( this TComponent component, params Item[] items ) where TComponent : IItem { - var option = component as IOptionConfig; - option?.Config( config => { - if ( items == null ) - return; - config.AddItems( items ); - } ); - return component; - } - - /// - /// 绑定bool - /// - /// 组件类型 - /// 组件实例 - public static TComponent Bool( this TComponent component ) where TComponent : IItem { - return component.Add( "是", true ).Add( "否", false ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Extensions/Extensions.Style.cs b/src/Util.Ui.Angular/Material/Extensions/Extensions.Style.cs deleted file mode 100644 index bf3180c77..000000000 --- a/src/Util.Ui.Angular/Material/Extensions/Extensions.Style.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Components.Internal; -using Util.Ui.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Operations.Styles; - -namespace Util.Ui.Material.Extensions { - /// - /// 样式扩展 - /// - public static partial class Extensions { - /// - /// 设置颜色 - /// - /// 组件类型 - /// 组件实例 - /// 颜色 - public static TComponent Color( this TComponent component,Color color ) where TComponent : IOption, IColor { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Color, color.ToString().ToLower() ); - } ); - return component; - } - - /// - /// 设置背景色 - /// - /// 组件类型 - /// 组件实例 - /// 颜色 - public static TComponent BackgroundColor( this TComponent component, Color color ) where TComponent : IOption, IBackgroundColor { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.BackgroundColor, color.ToString().ToLower() ); - } ); - return component; - } - } -} diff --git a/src/Util.Ui.Angular/Material/Extensions/Extensions.Tab.cs b/src/Util.Ui.Angular/Material/Extensions/Extensions.Tab.cs deleted file mode 100644 index 3befee732..000000000 --- a/src/Util.Ui.Angular/Material/Extensions/Extensions.Tab.cs +++ /dev/null @@ -1,79 +0,0 @@ -using Util.Ui.Components.Internal; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Tabs; - -namespace Util.Ui.Material.Extensions { - /// - /// 选项卡扩展 - /// - public static partial class Extensions { - /// - /// 设置为动态高度 - /// - /// 组件类型 - /// 组件实例 - public static TComponent DynamicHeight( this TComponent component ) where TComponent : ITabGroup { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( MaterialConst.DynamicHeight, true ); - } ); - return component; - } - - /// - /// 拉伸选项卡 - /// - /// 组件类型 - /// 组件实例 - public static TComponent Stretch( this TComponent component ) where TComponent : ITabGroup { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Stretch, true ); - } ); - return component; - } - - /// - /// 选中指定索引的选项卡 - /// - /// 组件类型 - /// 组件实例 - /// 索引表达式 - public static TComponent Select( this TComponent component, string expression ) where TComponent : ITabGroup { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.SelectedIndex, expression ); - } ); - return component; - } - - /// - /// 设置选项卡位置 - /// - /// 组件类型 - /// 组件实例 - /// 选项卡位置 - public static TComponent HeaderPosition( this TComponent component, YPosition position ) where TComponent : ITabGroup { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( MaterialConst.HeaderPosition, position ); - } ); - return component; - } - - /// - /// 延迟加载 - /// - /// 组件类型 - /// 组件实例 - public static TComponent LazyLoad( this TComponent component ) where TComponent : ITab { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.LazyLoad, true ); - } ); - return component; - } - } -} diff --git a/src/Util.Ui.Angular/Material/Extensions/Extensions.TextBox.cs b/src/Util.Ui.Angular/Material/Extensions/Extensions.TextBox.cs deleted file mode 100644 index 9293135db..000000000 --- a/src/Util.Ui.Angular/Material/Extensions/Extensions.TextBox.cs +++ /dev/null @@ -1,121 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Components.Internal; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Forms.Configs; - -namespace Util.Ui.Material.Extensions { - /// - /// 文本框扩展 - /// - public static partial class Extensions { - /// - /// 设置为密码框 - /// - /// 组件类型 - /// 组件实例 - public static TComponent Password( this TComponent component ) where TComponent : ITextBox { - var option = component as IOptionConfig; - option?.Config( config => { - config.Password(); - } ); - return component; - } - - /// - /// 只能输入数字 - /// - /// 组件类型 - /// 组件实例 - public static TComponent Number( this TComponent component ) where TComponent : ITextBox { - var option = component as IOptionConfig; - option?.Config( config => { - config.Number(); - } ); - return component; - } - - /// - /// 电子邮件验证 - /// - /// 组件类型 - /// 组件实例 - /// 电子邮件验证错误消息 - public static TComponent Email( this TComponent component,string message = null ) where TComponent : ITextBox { - var option = component as IOptionConfig; - option?.Config( config => { - config.Email(); - config.SetAttribute( UiConst.EmailMessage, message ); - } ); - return component; - } - - /// - /// 是否显示清除按钮 - /// - /// 组件类型 - /// 组件实例 - /// 是否显示清除按钮 - public static TComponent ShowClearButton( this TComponent component,bool isShow ) where TComponent : ITextBox { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( MaterialConst.ShowClearButton, isShow ); - } ); - return component; - } - - /// - /// 转换为多行文本框 - /// - /// 组件类型 - /// 组件实例 - /// 最小行数 - /// 最大行数 - public static TComponent ToTextArea( this TComponent component, int? minRows = null,int? maxRows = null ) where TComponent : ITextBox { - var option = component as IOptionConfig; - option?.Config( config => { - config.IsTextArea = true; - config.SetAttribute( MaterialConst.MinRows, minRows ); - config.SetAttribute( MaterialConst.MaxRows, maxRows ); - } ); - return component; - } - - /// - /// 转换为日期选择框 - /// - /// 组件类型 - /// 组件实例 - /// 最小日期限制,范例:2000-1-1 - /// 最大日期限制,范例:2000-1-1 - public static TComponent ToDatePicker( this TComponent component, string minDate = null, string maxDate = null ) where TComponent : ITextBox { - var option = component as IOptionConfig; - option?.Config( config => { - config.IsDatePicker = true; - config.SetAttribute( MaterialConst.MinDate, minDate ); - config.SetAttribute( MaterialConst.MaxDate, maxDate ); - } ); - return component; - } - - /// - /// 转换为日期选择框 - /// - /// 组件类型 - /// 组件实例 - /// 宽度 - /// 起始视图 - /// 触摸模式 - public static TComponent ToDatePicker( this TComponent component, double width, DateView? startView = null,bool? touchUi = null ) where TComponent : ITextBox { - var option = component as IOptionConfig; - option?.Config( config => { - config.IsDatePicker = true; - config.SetAttribute( UiConst.Width, width ); - config.SetAttribute( MaterialConst.StartView, startView ); - config.SetAttribute( MaterialConst.TouchUi, touchUi ); - } ); - return component; - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Builders/CheckBoxBuilder.cs b/src/Util.Ui.Angular/Material/Forms/Builders/CheckBoxBuilder.cs deleted file mode 100644 index 9d6cefb06..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Builders/CheckBoxBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Forms.Builders { - /// - /// Mat复选框生成器 - /// - class CheckBoxBuilder : TagBuilder { - /// - /// 初始化Mat复选框生成器 - /// - public CheckBoxBuilder( ) : base( "mat-checkbox" ) { - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Builders/DatePickerWrapperBuilder.cs b/src/Util.Ui.Angular/Material/Forms/Builders/DatePickerWrapperBuilder.cs deleted file mode 100644 index 1e29072c1..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Builders/DatePickerWrapperBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Forms.Builders { - /// - /// Mat日期选择包装器生成器 - /// - public class DatePickerWrapperBuilder : TagBuilder { - /// - /// 初始化Mat日期选择包装器生成器 - /// - public DatePickerWrapperBuilder() : base( "mat-datepicker-wrapper" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Forms/Builders/FormBuilder.cs b/src/Util.Ui.Angular/Material/Forms/Builders/FormBuilder.cs deleted file mode 100644 index 1e2e39c0e..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Builders/FormBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Forms.Builders { - /// - /// 表单生成器 - /// - public class FormBuilder : TagBuilder { - /// - /// 初始化表单生成器 - /// - public FormBuilder() : base( "form" ) { - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Builders/RadioWrapperBuilder.cs b/src/Util.Ui.Angular/Material/Forms/Builders/RadioWrapperBuilder.cs deleted file mode 100644 index b2678ca2b..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Builders/RadioWrapperBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Forms.Builders { - /// - /// Mat单选框包装器生成器 - /// - class RadioWrapperBuilder : TagBuilder { - /// - /// 初始化Mat单选框包装器生成器 - /// - public RadioWrapperBuilder( ) : base( "mat-radio-wrapper" ) { - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Builders/SelectWrapperBuilder.cs b/src/Util.Ui.Angular/Material/Forms/Builders/SelectWrapperBuilder.cs deleted file mode 100644 index ef6f82931..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Builders/SelectWrapperBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Forms.Builders { - /// - /// Mat下拉列表包装器生成器 - /// - class SelectWrapperBuilder : TagBuilder { - /// - /// 初始化Mat下拉列表包装器生成器 - /// - public SelectWrapperBuilder( ) : base( "mat-select-wrapper" ) { - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Builders/SlideToggleBuilder.cs b/src/Util.Ui.Angular/Material/Forms/Builders/SlideToggleBuilder.cs deleted file mode 100644 index fa542570e..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Builders/SlideToggleBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Forms.Builders { - /// - /// Mat滑动开关生成器 - /// - class SlideToggleBuilder : TagBuilder { - /// - /// 初始化Mat滑动开关生成器 - /// - public SlideToggleBuilder() : base( "mat-slide-toggle" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Forms/Builders/TextAreaWrapperBuilder.cs b/src/Util.Ui.Angular/Material/Forms/Builders/TextAreaWrapperBuilder.cs deleted file mode 100644 index 5129612b4..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Builders/TextAreaWrapperBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Forms.Builders { - /// - /// Mat多行文本框包装器生成器 - /// - public class TextAreaWrapperBuilder : TagBuilder { - /// - /// 初始化Mat多行文本框包装器生成器 - /// - public TextAreaWrapperBuilder() : base( "mat-textarea-wrapper" ) { - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Builders/TextBoxWrapperBuilder.cs b/src/Util.Ui.Angular/Material/Forms/Builders/TextBoxWrapperBuilder.cs deleted file mode 100644 index 076cbf3f9..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Builders/TextBoxWrapperBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Forms.Builders { - /// - /// Mat文本框包装器生成器 - /// - public class TextBoxWrapperBuilder : TagBuilder { - /// - /// 初始化Mat文本框包装器生成器 - /// - public TextBoxWrapperBuilder() : base( "mat-textbox-wrapper" ) { - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/CheckBox.cs b/src/Util.Ui.Angular/Material/Forms/CheckBox.cs deleted file mode 100644 index 1568b51e6..000000000 --- a/src/Util.Ui.Angular/Material/Forms/CheckBox.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Configs; -using Util.Ui.Material.Forms.Renders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Forms { - /// - /// 复选框 - /// - public class CheckBox : ComponentBase, ICheckBox { - /// - /// 配置 - /// - private readonly Config _config; - - /// - /// 初始化复选框 - /// - public CheckBox() { - _config = new Config(); - } - - /// - /// 获取配置 - /// - protected override IConfig GetConfig() { - return _config; - } - - /// - /// 获取渲染器 - /// - protected override IRender GetRender() { - return new CheckBoxRender( _config ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Configs/SelectConfig.cs b/src/Util.Ui.Angular/Material/Forms/Configs/SelectConfig.cs deleted file mode 100644 index 9ba618470..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Configs/SelectConfig.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; -using Util.Ui.Configs; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Forms.Configs { - /// - /// 下拉列表配置 - /// - public class SelectConfig : Config { - /// - /// 列表项集合 - /// - public readonly List Items = new List(); - - /// - /// 初始化下拉列表配置 - /// - public SelectConfig() { - } - - /// - /// 初始化下拉列表配置 - /// - /// TagHelper上下文 - public SelectConfig( Context context ) : base( context ) { - } - - /// - /// 添加项集合 - /// - /// 列表项集合 - public void AddItems( params Item[] items ) { - Items.AddRange( items ); - } - - /// - /// 添加布尔 - /// - public void AddBool() { - AddItems( new Item( "是", true ), new Item( "否", false ) ); - } - - /// - /// 添加枚举 - /// - /// 枚举类型 - public void AddEnum( Type type ) { - AddItems( Util.Helpers.Enum.GetItems( type ).ToArray() ); - } - - /// - /// 获取验证消息 - /// - public override string GetValidateMessage() { - if ( !Contains( UiConst.Url ) && !Contains( UiConst.DataSource ) ) - return "请设置数据源或Url"; - return string.Empty; - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Configs/TextBoxConfig.cs b/src/Util.Ui.Angular/Material/Forms/Configs/TextBoxConfig.cs deleted file mode 100644 index 0f86963a9..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Configs/TextBoxConfig.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Util.Ui.Configs; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Forms.Configs { - /// - /// 文本框配置 - /// - public class TextBoxConfig : Config { - /// - /// 初始化文本框配置 - /// - public TextBoxConfig() { - } - - /// - /// 初始化文本框配置 - /// - /// TagHelper上下文 - public TextBoxConfig( Context context ) : base( context ) { - } - - /// - /// 是否多行文本框 - /// - public bool IsTextArea { get; set; } - - /// - /// 是否日期选择框 - /// - public bool IsDatePicker { get; set; } - - /// - /// 设置为数值类型 - /// - public void Number() { - SetAttribute( UiConst.Type, "number" ); - } - - /// - /// 设置为密码类型 - /// - public void Password() { - SetAttribute( UiConst.Type, "password" ); - } - - /// - /// 设置为Email类型 - /// - public void Email() { - SetAttribute( UiConst.Type, "email" ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Form.cs b/src/Util.Ui.Angular/Material/Forms/Form.cs deleted file mode 100644 index 1da6ba1a3..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Form.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.IO; -using Util.Ui.Components; -using Util.Ui.Configs; -using Util.Ui.Material.Forms.Renders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Forms { - /// - /// 表单 - /// - public class Form : ContainerBase, IForm { - /// - /// 配置 - /// - private readonly Config _config; - - /// - /// 初始化表单 - /// - /// 流写入器 - public Form( TextWriter writer ) : base( writer ) { - _config = new Config(); - } - - /// - /// 获取配置 - /// - protected override IConfig GetConfig() { - return _config; - } - - /// - /// 获取渲染器 - /// - protected override IRender GetRender() { - return new FormRender( _config ); - } - - /// - /// 获取容器包装器 - /// - protected override IDisposable GetWrapper() { - return new ContainerWrapper( this ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/IForm.cs b/src/Util.Ui.Angular/Material/Forms/IForm.cs deleted file mode 100644 index f1e9daa1f..000000000 --- a/src/Util.Ui.Angular/Material/Forms/IForm.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using Util.Ui.Components; -using Util.Ui.Operations.Events; - -namespace Util.Ui.Material.Forms { - /// - /// 表单 - /// - public interface IForm : IContainer, IOnSubmit { - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Models/ModelCheckBox.cs b/src/Util.Ui.Angular/Material/Forms/Models/ModelCheckBox.cs deleted file mode 100644 index 794168267..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Models/ModelCheckBox.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Linq.Expressions; -using System.Reflection; -using Util.Helpers; -using Util.Ui.Material.Commons.Internal; - -namespace Util.Ui.Material.Forms.Models { - /// - /// 模型复选框 - /// - /// 模型类型 - /// 属性类型 - public class ModelCheckBox : CheckBox { - /// - /// 初始化模型复选框 - /// - /// 属性表达式 - public ModelCheckBox( Expression> expression ) { - if( expression == null ) - return; - _expression = expression; - _memberInfo = Lambda.GetMember( _expression ); - Init(); - } - - /// - /// 属性表达式 - /// - private readonly Expression> _expression; - /// - /// 成员 - /// - private readonly MemberInfo _memberInfo; - - /// - /// 初始化 - /// - private void Init() { - Helper.Init( OptionConfig, _expression, _memberInfo ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Models/ModelRadio.cs b/src/Util.Ui.Angular/Material/Forms/Models/ModelRadio.cs deleted file mode 100644 index 589522a05..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Models/ModelRadio.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Linq.Expressions; -using System.Reflection; -using Util.Helpers; -using Util.Ui.Material.Commons.Internal; -using Util.Ui.Material.Extensions; - -namespace Util.Ui.Material.Forms.Models { - /// - /// 模型单选框 - /// - /// 模型类型 - /// 属性类型 - public class ModelRadio : Radio { - /// - /// 初始化模型单选框 - /// - /// 属性表达式 - public ModelRadio( Expression> expression ) { - if( expression == null ) - return; - _expression = expression; - _memberInfo = Lambda.GetMember( _expression ); - Init(); - } - - /// - /// 属性表达式 - /// - private readonly Expression> _expression; - /// - /// 成员 - /// - private readonly MemberInfo _memberInfo; - - /// - /// 初始化 - /// - private void Init() { - Helper.Init( OptionConfig, _expression, _memberInfo ); - InitType(); - } - - /// - /// 根据类型初始化 - /// - private void InitType() { - if( Reflection.IsBool( _memberInfo ) ) - this.Bool(); - else if( Reflection.IsEnum( _memberInfo ) ) - Enum(); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Models/ModelSelect.cs b/src/Util.Ui.Angular/Material/Forms/Models/ModelSelect.cs deleted file mode 100644 index 5fac4bc3f..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Models/ModelSelect.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Linq.Expressions; -using System.Reflection; -using Util.Helpers; -using Util.Ui.Material.Commons.Internal; -using Util.Ui.Material.Extensions; - -namespace Util.Ui.Material.Forms.Models { - /// - /// 模型下拉列表 - /// - /// 模型类型 - /// 属性类型 - public class ModelSelect : Select { - /// - /// 初始化模型下拉列表 - /// - /// 属性表达式 - public ModelSelect( Expression> expression ) { - if( expression == null ) - return; - _expression = expression; - _memberInfo = Lambda.GetMember( _expression ); - Init(); - } - - /// - /// 属性表达式 - /// - private readonly Expression> _expression; - /// - /// 成员 - /// - private readonly MemberInfo _memberInfo; - - /// - /// 初始化 - /// - private void Init() { - Helper.Init( OptionConfig, _expression, _memberInfo ); - InitType(); - } - - /// - /// 根据类型初始化 - /// - private void InitType() { - if( Reflection.IsBool( _memberInfo ) ) - this.Bool(); - else if( Reflection.IsEnum( _memberInfo ) ) - Enum(); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Models/ModelSlideToggle.cs b/src/Util.Ui.Angular/Material/Forms/Models/ModelSlideToggle.cs deleted file mode 100644 index 5f1a03203..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Models/ModelSlideToggle.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Linq.Expressions; -using System.Reflection; -using Util.Helpers; -using Util.Ui.Material.Commons.Internal; - -namespace Util.Ui.Material.Forms.Models { - /// - /// 模型滑动开关 - /// - /// 模型类型 - /// 属性类型 - public class ModelSlideToggle : SlideToggle { - /// - /// 初始化模型滑动开关 - /// - /// 属性表达式 - public ModelSlideToggle( Expression> expression ) { - if( expression == null ) - return; - _expression = expression; - _memberInfo = Lambda.GetMember( _expression ); - Init(); - } - - /// - /// 属性表达式 - /// - private readonly Expression> _expression; - /// - /// 成员 - /// - private readonly MemberInfo _memberInfo; - - /// - /// 初始化 - /// - private void Init() { - Helper.Init( OptionConfig, _expression, _memberInfo ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Models/ModelTextBox.cs b/src/Util.Ui.Angular/Material/Forms/Models/ModelTextBox.cs deleted file mode 100644 index 9cf78dc2e..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Models/ModelTextBox.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Linq.Expressions; -using System.Reflection; -using Util.Helpers; -using Util.Ui.Material.Commons.Internal; -using Util.Ui.Material.Forms.Configs; - -namespace Util.Ui.Material.Forms.Models { - /// - /// 模型文本框 - /// - /// 模型类型 - /// 属性类型 - public class ModelTextBox : TextBox { - /// - /// 初始化模型文本框 - /// - /// 属性表达式 - public ModelTextBox( Expression> expression ) { - if( expression == null ) - return; - _expression = expression; - _memberInfo = Lambda.GetMember( _expression ); - _config = (TextBoxConfig)OptionConfig; - Init(); - } - - /// - /// 属性表达式 - /// - private readonly Expression> _expression; - /// - /// 成员 - /// - private readonly MemberInfo _memberInfo; - /// - /// 配置 - /// - private readonly TextBoxConfig _config; - - /// - /// 初始化 - /// - private void Init() { - Helper.Init( _config, _expression, _memberInfo ); - Helper.InitDataType( _config, _memberInfo ); - Helper.InitValidation( _config, _memberInfo ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Forms/Radio.cs b/src/Util.Ui.Angular/Material/Forms/Radio.cs deleted file mode 100644 index 4192f372c..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Radio.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Configs; -using Util.Ui.Material.Extensions; -using Util.Ui.Material.Forms.Configs; -using Util.Ui.Material.Forms.Renders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Forms { - /// - /// 单选框 - /// - public class Radio : ComponentBase, IRadio { - /// - /// 配置 - /// - private readonly SelectConfig _config; - - /// - /// 初始化单选框 - /// - public Radio() { - _config = new SelectConfig(); - } - - /// - /// 获取配置 - /// - protected override IConfig GetConfig() { - return _config; - } - - /// - /// 获取渲染器 - /// - protected override IRender GetRender() { - return new RadioRender( _config ); - } - - /// - /// 绑定枚举 - /// - /// 枚举类型 - public IRadio Enum() { - return this.Add( Util.Helpers.Enum.GetItems().ToArray() ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Forms/Renders/CheckBoxRender.cs b/src/Util.Ui.Angular/Material/Forms/Renders/CheckBoxRender.cs deleted file mode 100644 index 19cd5b15c..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Renders/CheckBoxRender.cs +++ /dev/null @@ -1,119 +0,0 @@ -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Forms.Builders; -using Util.Ui.Material.Forms.Resolvers; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Forms.Renders { - /// - /// 复选框渲染器 - /// - public class CheckBoxRender : RenderBase { - /// - /// 配置 - /// - private readonly Config _config; - - /// - /// 初始化复选框渲染器 - /// - /// 配置 - public CheckBoxRender( Config config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - ResolveExpression(); - var builder = new CheckBoxBuilder(); - Config( builder ); - return builder; - } - - /// - /// 解析属性表达式 - /// - protected void ResolveExpression() { - if( _config.Contains( UiConst.For ) == false ) - return; - var expression = _config.GetValue( UiConst.For ); - CheckBoxExpressionResolver.Init( expression, _config ); - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigName( builder ); - ConfigLabel( builder ); - ConfigDisabled( builder ); - ConfigColor( builder ); - ConfigModel( builder ); - ConfigIndeterminate( builder ); - ConfigRequired( builder ); - ConfigEvents( builder ); - } - - /// - /// 配置名称 - /// - private void ConfigName( TagBuilder builder ) { - builder.AddAttribute( UiConst.Name, _config.GetValue( UiConst.Name ) ); - } - - /// - /// 配置标签 - /// - private void ConfigLabel( TagBuilder builder ) { - builder.SetContent( _config.GetValue( UiConst.Label ) ); - builder.AddAttribute( "labelPosition", _config.GetValue( UiConst.Position )?.Description() ); - } - - /// - /// 配置禁用 - /// - private void ConfigDisabled( TagBuilder builder ) { - builder.AddAttribute( "[disabled]", _config.GetBoolValue( UiConst.Disabled ) ); - } - - /// - /// 配置颜色 - /// - private void ConfigColor( TagBuilder builder ) { - builder.AddAttribute( UiConst.Color, _config.GetValue( UiConst.Color )?.ToLower() ); - } - - /// - /// 配置模型绑定 - /// - private void ConfigModel( TagBuilder builder ) { - builder.AddAttribute( "[(ngModel)]", _config.GetValue( UiConst.Model ) ); - } - - /// - /// 配置不确定样式 - /// - private void ConfigIndeterminate( TagBuilder builder ) { - builder.AddAttribute( "[indeterminate]", _config.GetValue( UiConst.Indeterminate ) ); - } - - /// - /// 配置必填项 - /// - private void ConfigRequired( TagBuilder builder ) { - builder.AddAttribute( "[required]", _config.GetBoolValue( UiConst.Required ) ); - } - - /// - /// 配置事件 - /// - private void ConfigEvents( TagBuilder builder ) { - builder.AddAttribute( "(change)", _config.GetValue( UiConst.OnChange ) ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Renders/FormControlRenderBase.cs b/src/Util.Ui.Angular/Material/Forms/Renders/FormControlRenderBase.cs deleted file mode 100644 index 474d6383f..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Renders/FormControlRenderBase.cs +++ /dev/null @@ -1,115 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Forms.Renders { - /// - /// 表单控件渲染器 - /// - public abstract class FormControlRenderBase : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化下拉列表渲染器 - /// - /// 下拉列表配置 - protected FormControlRenderBase( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigName( builder ); - ConfigDisabled( builder ); - ConfigPlaceholder( builder ); - ConfigHint( builder ); - ConfigPrefix( builder ); - ConfigSuffix( builder ); - ConfigModel( builder ); - ConfigRequired( builder ); - ConfigEvents( builder ); - } - - /// - /// 配置名称 - /// - private void ConfigName( TagBuilder builder ) { - builder.AddAttribute( UiConst.Name, _config.GetValue( UiConst.Name ) ); - } - - /// - /// 配置禁用 - /// - private void ConfigDisabled( TagBuilder builder ) { - builder.AddAttribute( "[disabled]", _config.GetBoolValue( UiConst.Disabled ) ); - } - - /// - /// 配置占位符 - /// - private void ConfigPlaceholder( TagBuilder builder ) { - builder.AddAttribute( UiConst.Placeholder, _config.GetValue( UiConst.Placeholder ) ); - builder.AddAttribute( "floatPlaceholder", _config.GetValue( MaterialConst.FloatPlaceholder )?.Description() ); - } - - /// - /// 配置提示 - /// - private void ConfigHint( TagBuilder builder ) { - builder.AddAttribute( "startHint", _config.GetValue( MaterialConst.StartHint ) ); - builder.AddAttribute( "endHint", _config.GetValue( MaterialConst.EndHint ) ); - } - - /// - /// 配置前缀 - /// - private void ConfigPrefix( TagBuilder builder ) { - builder.AddAttribute( "prefixText", _config.GetValue( UiConst.Prefix ) ); - } - - /// - /// 配置后缀 - /// - private void ConfigSuffix( TagBuilder builder ) { - builder.AddAttribute( "suffixText", _config.GetValue( MaterialConst.SuffixText ) ); - builder.AddAttribute( "suffixFontAwesomeIcon", _config.GetValue( MaterialConst.SuffixFontAwesomeIcon )?.Description() ); - builder.AddAttribute( "suffixMaterialIcon", _config.GetValue( MaterialConst.SuffixMaterialIcon )?.Description() ); - builder.AddAttribute( "(onSuffixIconClick)", _config.GetValue( MaterialConst.OnSuffixIconClick ) ); - } - - /// - /// 配置模型绑定 - /// - private void ConfigModel( TagBuilder builder ) { - builder.AddAttribute( "[(model)]", _config.GetValue( UiConst.Model ) ); - } - - /// - /// 配置必填项 - /// - private void ConfigRequired( TagBuilder builder ) { - builder.AddAttribute( "[required]", _config.GetBoolValue( UiConst.Required ) ); - builder.AddAttribute( "requiredMessage", _config.GetValue( UiConst.RequiredMessage ) ); - } - - /// - /// 配置事件 - /// - private void ConfigEvents( TagBuilder builder ) { - builder.AddAttribute( "(onChange)", _config.GetValue( UiConst.OnChange ) ); - builder.AddAttribute( "(onFocus)", _config.GetValue( UiConst.OnFocus ) ); - builder.AddAttribute( "(onBlur)", _config.GetValue( UiConst.OnBlur ) ); - builder.AddAttribute( "(onKeyup)", _config.GetValue( UiConst.OnKeyup ) ); - builder.AddAttribute( "(onKeydown)", _config.GetValue( UiConst.OnKeydown ) ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Renders/FormRender.cs b/src/Util.Ui.Angular/Material/Forms/Renders/FormRender.cs deleted file mode 100644 index bb7e2927e..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Renders/FormRender.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Forms.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Forms.Renders { - /// - /// 表单渲染器 - /// - public class FormRender : RenderBase { - /// - /// 配置 - /// - private readonly Config _config; - - /// - /// 初始化表单渲染器 - /// - /// 配置 - public FormRender( Config config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new FormBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - private void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigEvents( builder ); - ConfigContent( builder ); - } - - /// - /// 配置标识 - /// - protected override void ConfigId( TagBuilder builder ) { - if( _config.Contains( UiConst.Id ) ) - builder.AddAttribute( $"#{_config.GetValue( UiConst.Id )}", "ngForm" ); - } - - /// - /// 配置事件 - /// - private void ConfigEvents( TagBuilder builder ) { - builder.AddAttribute( "(ngSubmit)", _config.GetValue( UiConst.OnSubmit ) ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - builder.SetContent( _config.Content ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Renders/RadioRender.cs b/src/Util.Ui.Angular/Material/Forms/Renders/RadioRender.cs deleted file mode 100644 index b52302fda..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Renders/RadioRender.cs +++ /dev/null @@ -1,133 +0,0 @@ -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Forms.Builders; -using Util.Ui.Material.Forms.Configs; -using Util.Ui.Material.Forms.Resolvers; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Forms.Renders { - /// - /// 单选框渲染器 - /// - public class RadioRender : RenderBase { - /// - /// 配置 - /// - private readonly SelectConfig _config; - - /// - /// 初始化单选框渲染器 - /// - /// 配置 - public RadioRender( SelectConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - ResolveExpression(); - var builder = new RadioWrapperBuilder(); - Config( builder ); - return builder; - } - - /// - /// 解析属性表达式 - /// - private void ResolveExpression() { - if( _config.Contains( UiConst.For ) == false ) - return; - var expression = _config.GetValue( UiConst.For ); - SelectExpressionResolver.Init( expression, _config ); - } - - /// - /// 配置 - /// - private void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigName( builder ); - ConfigLabel( builder ); - ConfigDisabled( builder ); - ConfigModel( builder ); - ConfigRequired( builder ); - ConfigEvents( builder ); - ConfigUrl( builder ); - ConfigDataSource( builder ); - } - - /// - /// 配置名称 - /// - private void ConfigName( TagBuilder builder ) { - builder.AddAttribute( UiConst.Name, _config.GetValue( UiConst.Name ) ); - } - - /// - /// 配置标签 - /// - private void ConfigLabel( TagBuilder builder ) { - builder.AddAttribute( "[vertical]", _config.GetBoolValue( UiConst.Vertical ) ); - builder.AddAttribute( "[showLabel]", _config.GetBoolValue( MaterialConst.ShowLabel ) ); - builder.AddAttribute( "label", _config.GetValue( UiConst.Label ) ); - builder.AddAttribute( "labelPosition", _config.GetValue( UiConst.Position )?.Description() ); - } - - /// - /// 配置禁用 - /// - private void ConfigDisabled( TagBuilder builder ) { - builder.AddAttribute( "[disabled]", _config.GetBoolValue( UiConst.Disabled ) ); - } - - /// - /// 配置模型绑定 - /// - private void ConfigModel( TagBuilder builder ) { - builder.AddAttribute( "[(model)]", _config.GetValue( UiConst.Model ) ); - } - - /// - /// 配置必填项 - /// - private void ConfigRequired( TagBuilder builder ) { - builder.AddAttribute( "[required]", _config.GetBoolValue( UiConst.Required ) ); - } - - /// - /// 配置事件 - /// - private void ConfigEvents( TagBuilder builder ) { - builder.AddAttribute( "(onChange)", _config.GetValue( UiConst.OnChange ) ); - } - - /// - /// 配置Url - /// - private void ConfigUrl( TagBuilder builder ) { - builder.AddAttribute( UiConst.Url, _config.GetValue( UiConst.Url ) ); - } - - /// - /// 配置数据源 - /// - private void ConfigDataSource( TagBuilder builder ) { - AddItems(); - builder.AddAttribute( "[dataSource]", _config.GetValue( UiConst.DataSource ) ); - } - - /// - /// 添加项集合 - /// - private void AddItems() { - if( _config.Items.Count == 0 ) - return; - _config.SetAttribute( UiConst.DataSource, Util.Helpers.Json.ToJson( _config.Items, true ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Forms/Renders/SelectRender.cs b/src/Util.Ui.Angular/Material/Forms/Renders/SelectRender.cs deleted file mode 100644 index 9802d386c..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Renders/SelectRender.cs +++ /dev/null @@ -1,105 +0,0 @@ -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Forms.Builders; -using Util.Ui.Material.Forms.Configs; -using Util.Ui.Material.Forms.Resolvers; - -namespace Util.Ui.Material.Forms.Renders { - /// - /// 下拉列表渲染器 - /// - public class SelectRender : FormControlRenderBase { - /// - /// 下拉列表配置 - /// - private readonly SelectConfig _config; - - /// - /// 初始化下拉列表渲染器 - /// - /// 下拉列表配置 - public SelectRender( SelectConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - ResolveExpression(); - var builder = new SelectWrapperBuilder(); - base.Config( builder ); - ConfigSelect( builder ); - return builder; - } - - /// - /// 解析属性表达式 - /// - private void ResolveExpression() { - if( _config.Contains( UiConst.For ) == false ) - return; - var expression = _config.GetValue( UiConst.For ); - SelectExpressionResolver.Init( expression, _config ); - } - - /// - /// 配置 - /// - private void ConfigSelect( SelectWrapperBuilder builder ) { - ConfigUrl( builder ); - ConfigDataSource( builder ); - ConfigResetOption( builder ); - ConfigMultiple( builder ); - ConfigTemplate( builder ); - } - - /// - /// 配置Url - /// - private void ConfigUrl( SelectWrapperBuilder builder ) { - builder.AddAttribute( UiConst.Url, _config.GetValue( UiConst.Url ) ); - } - - /// - /// 配置数据源 - /// - private void ConfigDataSource( SelectWrapperBuilder builder ) { - AddItems(); - builder.AddAttribute( "[dataSource]", _config.GetValue( UiConst.DataSource ) ); - } - - /// - /// 添加项集合 - /// - private void AddItems() { - if( _config.Items.Count == 0 ) - return; - _config.SetAttribute( UiConst.DataSource, Util.Helpers.Json.ToJson( _config.Items, true ) ); - } - - /// - /// 配置重置项 - /// - private void ConfigResetOption( SelectWrapperBuilder builder ) { - builder.AddAttribute( "[enableResetOption]", _config.GetBoolValue( MaterialConst.EnableResetOption ) ); - builder.AddAttribute( "resetOptionText", _config.GetValue( MaterialConst.ResetOptionText ) ); - } - - /// - /// 配置多选 - /// - private void ConfigMultiple( SelectWrapperBuilder builder ) { - builder.AddAttribute( "[multiple]", _config.GetBoolValue( UiConst.Multiple ) ); - } - - /// - /// 配置显示模板 - /// - private void ConfigTemplate( SelectWrapperBuilder builder ) { - builder.AddAttribute( UiConst.Template, _config.GetValue( UiConst.Template ) ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Renders/SlideToggleRender.cs b/src/Util.Ui.Angular/Material/Forms/Renders/SlideToggleRender.cs deleted file mode 100644 index 01a246866..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Renders/SlideToggleRender.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Forms.Builders; - -namespace Util.Ui.Material.Forms.Renders { - /// - /// 滑动开关渲染器 - /// - public class SlideToggleRender : CheckBoxRender { - /// - /// 初始化滑动开关渲染器 - /// - /// 配置 - public SlideToggleRender( Config config ) : base( config ) { - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - ResolveExpression(); - var builder = new SlideToggleBuilder(); - Config( builder ); - return builder; - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Renders/TextBoxRender.cs b/src/Util.Ui.Angular/Material/Forms/Renders/TextBoxRender.cs deleted file mode 100644 index b2af6fdee..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Renders/TextBoxRender.cs +++ /dev/null @@ -1,147 +0,0 @@ -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Forms.Builders; -using Util.Ui.Material.Forms.Configs; -using Util.Ui.Material.Forms.Resolvers; - -namespace Util.Ui.Material.Forms.Renders { - /// - /// 文本框渲染器 - /// - public class TextBoxRender : FormControlRenderBase { - /// - /// 配置 - /// - private readonly TextBoxConfig _config; - - /// - /// 初始化文本框渲染器 - /// - /// 文本框配置 - public TextBoxRender( TextBoxConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - ResolveExpression(); - var builder = CreateBuilder(); - base.Config( builder ); - ConfigTextArea( builder ); - ConfigDatePicker( builder ); - ConfigTextBox( builder ); - return builder; - } - - /// - /// 解析属性表达式 - /// - private void ResolveExpression() { - if( _config.Contains( UiConst.For ) == false ) - return; - var expression = _config.GetValue( UiConst.For ); - TextBoxExpressionResolver.Init( expression, _config ); - } - - /// - /// 创建标签生成器 - /// - private TagBuilder CreateBuilder() { - if( _config.IsTextArea ) - return new TextAreaWrapperBuilder(); - if( _config.IsDatePicker ) - return new DatePickerWrapperBuilder(); - return new TextBoxWrapperBuilder(); - } - - /// - /// 配置多行文本框 - /// - private void ConfigTextArea( TagBuilder builder ) { - if( _config.IsTextArea == false ) - return; - builder.AddAttribute( "[minRows]", _config.GetValue( MaterialConst.MinRows ) ); - builder.AddAttribute( "[maxRows]", _config.GetValue( MaterialConst.MaxRows ) ); - } - - /// - /// 配置日期选择框 - /// - private void ConfigDatePicker( TagBuilder builder ) { - if( _config.IsDatePicker == false ) - return; - builder.AddAttribute( "[width]", _config.GetValue( UiConst.Width ) ); - builder.AddAttribute( "startView", _config.GetValue( MaterialConst.StartView )?.Description() ); - builder.AddAttribute( "[touchUi]", _config.GetBoolValue( MaterialConst.TouchUi ) ); - builder.AddAttribute( "minDate", _config.GetValue( MaterialConst.MinDate ) ); - builder.AddAttribute( "maxDate", _config.GetValue( MaterialConst.MaxDate ) ); - } - - /// - /// 配置文本框 - /// - private void ConfigTextBox( TagBuilder builder ) { - ConfigType( builder ); - ConfigReadOnly( builder ); - ConfigShowClearButton( builder ); - ConfigValidations( builder ); - } - - /// - /// 配置类型 - /// - private void ConfigType( TagBuilder builder ) { - builder.AddAttribute( UiConst.Type, _config.GetValue( UiConst.Type )?.Description() ); - } - - /// - /// 配置只读 - /// - private void ConfigReadOnly( TagBuilder builder ) { - builder.AddAttribute( "[readonly]", _config.GetBoolValue( UiConst.ReadOnly ) ); - } - - /// - /// 配置是否显示清除按钮 - /// - private void ConfigShowClearButton( TagBuilder builder ) { - builder.AddAttribute( "[showClearButton]", _config.GetBoolValue( MaterialConst.ShowClearButton ) ); - } - - /// - /// 配置验证操作 - /// - private void ConfigValidations( TagBuilder builder ) { - ConfigEmail( builder ); - ConfigMinLength( builder ); - ConfigMaxLength( builder ); - } - - /// - /// 配置Email验证 - /// - private void ConfigEmail( TagBuilder builder ) { - builder.AddAttribute( "emailMessage", _config.GetValue( UiConst.EmailMessage ) ); - } - - /// - /// 配置最小长度验证 - /// - private void ConfigMinLength( TagBuilder builder ) { - builder.AddAttribute( "[minLength]", _config.GetValue( UiConst.MinLength ) ); - builder.AddAttribute( "minLengthMessage", _config.GetValue( UiConst.MinLengthMessage ) ); - } - - /// - /// 配置最大长度验证 - /// - private void ConfigMaxLength( TagBuilder builder ) { - builder.AddAttribute( "[maxLength]", _config.GetValue( UiConst.MaxLength ) ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Resolvers/CheckBoxExpressionResolver.cs b/src/Util.Ui.Angular/Material/Forms/Resolvers/CheckBoxExpressionResolver.cs deleted file mode 100644 index 1f5f15fb0..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Resolvers/CheckBoxExpressionResolver.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Reflection; -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Util.Ui.Configs; -using Util.Ui.Extensions; -using Util.Ui.Material.Commons.Internal; - -namespace Util.Ui.Material.Forms.Resolvers { - /// - /// 复选框表达式解析器 - /// - public class CheckBoxExpressionResolver { - /// - /// 初始化复选框表达式解析器 - /// - /// 属性表达式 - /// 配置 - private CheckBoxExpressionResolver( ModelExpression expression, Config config ) { - if( expression == null || config == null ) - return; - _expression = expression; - _config = config; - _memberInfo = expression.GetMemberInfo(); - } - - /// - /// 属性表达式 - /// - private readonly ModelExpression _expression; - - /// - /// 配置 - /// - private readonly Config _config; - - /// - /// 成员 - /// - private readonly MemberInfo _memberInfo; - - /// - /// 初始化 - /// - /// 属性表达式 - /// 配置 - public static void Init( ModelExpression expression, Config config ) { - new CheckBoxExpressionResolver( expression, config ).Init(); - } - - /// - /// 初始化 - /// - private void Init() { - Helper.Init( _config, _expression, _memberInfo ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Resolvers/SelectExpressionResolver.cs b/src/Util.Ui.Angular/Material/Forms/Resolvers/SelectExpressionResolver.cs deleted file mode 100644 index 02a9bafc6..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Resolvers/SelectExpressionResolver.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Reflection; -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Util.Helpers; -using Util.Ui.Extensions; -using Util.Ui.Material.Commons.Internal; -using Util.Ui.Material.Forms.Configs; - -namespace Util.Ui.Material.Forms.Resolvers { - /// - /// 下拉列表表达式解析器 - /// - public class SelectExpressionResolver { - /// - /// 初始化下拉列表表达式解析器 - /// - /// 属性表达式 - /// 配置 - private SelectExpressionResolver( ModelExpression expression, SelectConfig config ) { - if( expression == null || config == null ) - return; - _expression = expression; - _config = config; - _memberInfo = expression.GetMemberInfo(); - } - - /// - /// 属性表达式 - /// - private readonly ModelExpression _expression; - - /// - /// 配置 - /// - private readonly SelectConfig _config; - - /// - /// 成员 - /// - private readonly MemberInfo _memberInfo; - - /// - /// 初始化 - /// - /// 属性表达式 - /// 配置 - public static void Init( ModelExpression expression, SelectConfig config ) { - new SelectExpressionResolver( expression, config ).Init(); - } - - /// - /// 初始化 - /// - private void Init() { - Helper.Init( _config, _expression, _memberInfo ); - InitType(); - } - - /// - /// 根据类型初始化 - /// - private void InitType() { - if ( Reflection.IsBool( _memberInfo ) ) - _config.AddBool(); - else if ( Reflection.IsEnum( _memberInfo ) ) - _config.AddEnum( _expression.Metadata.ModelType ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Resolvers/TextBoxExpressionResolver.cs b/src/Util.Ui.Angular/Material/Forms/Resolvers/TextBoxExpressionResolver.cs deleted file mode 100644 index 8ede649fc..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Resolvers/TextBoxExpressionResolver.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Reflection; -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Util.Ui.Extensions; -using Util.Ui.Material.Commons.Internal; -using Util.Ui.Material.Forms.Configs; - -namespace Util.Ui.Material.Forms.Resolvers { - /// - /// 文本框表达式解析器 - /// - public class TextBoxExpressionResolver { - /// - /// 初始化文本框表达式解析器 - /// - /// 属性表达式 - /// 配置 - private TextBoxExpressionResolver( ModelExpression expression, TextBoxConfig config ) { - if( expression == null || config == null ) - return; - _expression = expression; - _config = config; - _memberInfo = expression.GetMemberInfo(); - } - - /// - /// 属性表达式 - /// - private readonly ModelExpression _expression; - /// - /// 配置 - /// - private readonly TextBoxConfig _config; - /// - /// 成员 - /// - private readonly MemberInfo _memberInfo; - - /// - /// 初始化 - /// - /// 属性表达式 - /// 配置 - public static void Init( ModelExpression expression, TextBoxConfig config ) { - new TextBoxExpressionResolver( expression, config ).Init(); - } - - /// - /// 初始化 - /// - private void Init() { - Helper.Init( _config, _expression, _memberInfo ); - Helper.InitDataType( _config, _memberInfo ); - Helper.InitValidation( _config, _memberInfo ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/Select.cs b/src/Util.Ui.Angular/Material/Forms/Select.cs deleted file mode 100644 index 3982fcc16..000000000 --- a/src/Util.Ui.Angular/Material/Forms/Select.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Configs; -using Util.Ui.Material.Extensions; -using Util.Ui.Material.Forms.Configs; -using Util.Ui.Material.Forms.Renders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Forms { - /// - /// 下拉列表 - /// - public class Select : ComponentBase, ISelect { - /// - /// 下拉列表配置 - /// - private readonly SelectConfig _config; - - /// - /// 初始化下拉列表 - /// - public Select() { - _config = new SelectConfig(); - } - - /// - /// 获取配置 - /// - protected override IConfig GetConfig() { - return _config; - } - - /// - /// 获取渲染器 - /// - protected override IRender GetRender() { - return new SelectRender( _config ); - } - - /// - /// 绑定枚举 - /// - /// 枚举类型 - public ISelect Enum() { - return this.Add( Util.Helpers.Enum.GetItems().ToArray() ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/SlideToggle.cs b/src/Util.Ui.Angular/Material/Forms/SlideToggle.cs deleted file mode 100644 index 7a4c8d7ec..000000000 --- a/src/Util.Ui.Angular/Material/Forms/SlideToggle.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Configs; -using Util.Ui.Material.Forms.Renders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Forms { - /// - /// 滑动开关 - /// - public class SlideToggle : ComponentBase, ISlideToggle { - /// - /// 配置 - /// - private readonly Config _config; - - /// - /// 初始化滑动开关 - /// - public SlideToggle() { - _config = new Config(); - } - - /// - /// 获取配置 - /// - protected override IConfig GetConfig() { - return _config; - } - - /// - /// 获取渲染器 - /// - protected override IRender GetRender() { - return new SlideToggleRender( _config ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Forms/TagHelpers/CheckBoxTagHelper.cs b/src/Util.Ui.Angular/Material/Forms/TagHelpers/CheckBoxTagHelper.cs deleted file mode 100644 index a7c082ba4..000000000 --- a/src/Util.Ui.Angular/Material/Forms/TagHelpers/CheckBoxTagHelper.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Forms.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Forms.TagHelpers { - /// - /// 复选框 - /// - [HtmlTargetElement( "util-checkbox" )] - public class CheckBoxTagHelper : TagHelperBase { - /// - /// 控件的名称 - /// - public string Name { get; set; } - /// - /// 标签文本 - /// - public string Label { get; set; } - /// - /// 标签位置 - /// - public XPosition Position { get; set; } - /// - /// 禁用 - /// - public string Disabled { get; set; } - /// - /// 颜色 - /// - public Color Color { get; set; } - /// - /// 属性表达式 - /// - public ModelExpression For { get; set; } - /// - /// 模型绑定 - /// - public string Model { get; set; } - /// - /// 设置不确定样式 - /// - public string Indeterminate { get; set; } - /// - /// 必填项 - /// - public bool Required { get; set; } - /// - /// 变更事件处理函数,范例:handle() - /// - public string OnChange { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new CheckBoxRender( new Config( context ) ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/TagHelpers/DatePickerTagHelper.cs b/src/Util.Ui.Angular/Material/Forms/TagHelpers/DatePickerTagHelper.cs deleted file mode 100644 index 7a10ef2ca..000000000 --- a/src/Util.Ui.Angular/Material/Forms/TagHelpers/DatePickerTagHelper.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Forms.Configs; -using Util.Ui.Material.Forms.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Forms.TagHelpers { - /// - /// 日期选择 - /// - [HtmlTargetElement( "util-datepicker" )] - public class DatePickerTagHelper : FormControlTagHelperBase { - /// - /// 是否显示清除按钮 - /// - public bool ShowClearButton { get; set; } - /// - /// 只读 - /// - public bool Readonly { get; set; } - /// - /// 宽度 - /// - public double Width { get; set; } - /// - /// 起始视图 - /// - public DateView StartView { get; set; } - /// - /// 触摸模式 - /// - public bool TouchUi { get; set; } - /// - /// 最小日期限制,范例:2000-1-1 - /// - public string MinDate { get; set; } - /// - /// 最大日期限制,范例:2000-1-1 - /// - public string MaxDate { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new TextBoxRender( new TextBoxConfig( context ) { IsDatePicker = true } ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Forms/TagHelpers/FormControlTagHelperBase.cs b/src/Util.Ui.Angular/Material/Forms/TagHelpers/FormControlTagHelperBase.cs deleted file mode 100644 index 4d684f6b1..000000000 --- a/src/Util.Ui.Angular/Material/Forms/TagHelpers/FormControlTagHelperBase.cs +++ /dev/null @@ -1,92 +0,0 @@ -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Util.Ui.Enums; -using Util.Ui.Material.Enums; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Forms.TagHelpers { - /// - /// 表单控件 - /// - public abstract class FormControlTagHelperBase : TagHelperBase { - /// - /// 控件的名称 - /// - public string Name { get; set; } - /// - /// 禁用 - /// - public string Disabled { get; set; } - /// - /// 占位提示符 - /// - public string Placeholder { get; set; } - /// - /// 占位符浮动类型 - /// - public FloatType FloatPlaceholder { get; set; } - /// - /// 起始提示,显示在控件左下方 - /// - public string StartHint { get; set; } - /// - /// 结束提示,显示在控件右下方 - /// - public string EndHint { get; set; } - /// - /// 前缀 - /// - public string Prefix { get; set; } - /// - /// 后缀文本 - /// - public string SuffixText { get; set; } - /// - /// 后缀FontAwesome图标 - /// - public FontAwesomeIcon SuffixFontAwesomeIcon { get; set; } - /// - /// 后缀Material图标 - /// - public MaterialIcon SuffixMaterialIcon { get; set; } - /// - /// 后缀图标单击事件,范例:click() - /// - public string OnSuffixIconClick { get; set; } - /// - /// 属性表达式 - /// - public ModelExpression For { get; set; } - /// - /// 模型绑定 - /// - public string Model { get; set; } - /// - /// 必填项 - /// - public bool Required { get; set; } - /// - /// 必填项错误消息 - /// - public string RequiredMessage { get; set; } - /// - /// 变更事件处理函数,范例:handle() - /// - public string OnChange { get; set; } - /// - /// 获得焦点事件处理函数,范例:handle() - /// - public string OnFocus { get; set; } - /// - /// 失去焦点事件处理函数,范例:handle() - /// - public string OnBlur { get; set; } - /// - /// 键盘按键事件处理函数,范例:handle() - /// - public string OnKeyup { get; set; } - /// - /// 键盘按下事件处理函数,范例:handle() - /// - public string OnKeydown { get; set; } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/TagHelpers/FormTagHelper.cs b/src/Util.Ui.Angular/Material/Forms/TagHelpers/FormTagHelper.cs deleted file mode 100644 index 96eaf9164..000000000 --- a/src/Util.Ui.Angular/Material/Forms/TagHelpers/FormTagHelper.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Forms.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Forms.TagHelpers { - /// - /// 表单 - /// - [HtmlTargetElement( "util-form" )] - public class FormTagHelper : TagHelperBase { - /// - /// 提交事件处理函数,范例:handle() - /// - public string OnSubmit { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new FormRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Forms/TagHelpers/RadioTagHelper.cs b/src/Util.Ui.Angular/Material/Forms/TagHelpers/RadioTagHelper.cs deleted file mode 100644 index 68696fac6..000000000 --- a/src/Util.Ui.Angular/Material/Forms/TagHelpers/RadioTagHelper.cs +++ /dev/null @@ -1,72 +0,0 @@ -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Forms.Configs; -using Util.Ui.Material.Forms.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Forms.TagHelpers { - /// - /// 单选框 - /// - [HtmlTargetElement( "util-radio" )] - public class RadioTagHelper : TagHelperBase { - /// - /// 控件的名称 - /// - public string Name { get; set; } - /// - /// 是否垂直布局 - /// - public bool Vertical { get; set; } - /// - /// 是否显示标签 - /// - public bool ShowLabel { get; set; } - /// - /// 标签文本 - /// - public string Label { get; set; } - /// - /// 标签位置 - /// - public XPosition Position { get; set; } - /// - /// 请求地址 - /// - public string Url { get; set; } - /// - /// 数据源 - /// - public string DatasSource { get; set; } - /// - /// 禁用 - /// - public string Disabled { get; set; } - /// - /// 属性表达式 - /// - public ModelExpression For { get; set; } - /// - /// 模型绑定 - /// - public string Model { get; set; } - /// - /// 必填项 - /// - public bool Required { get; set; } - /// - /// 变更事件处理函数,范例:handle() - /// - public string OnChange { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new RadioRender( new SelectConfig( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Forms/TagHelpers/SelectTagHelper.cs b/src/Util.Ui.Angular/Material/Forms/TagHelpers/SelectTagHelper.cs deleted file mode 100644 index 471f1682c..000000000 --- a/src/Util.Ui.Angular/Material/Forms/TagHelpers/SelectTagHelper.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Material.Forms.Configs; -using Util.Ui.Material.Forms.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Forms.TagHelpers { - /// - /// 下拉列表 - /// - [HtmlTargetElement( "util-select" )] - public class SelectTagHelper : FormControlTagHelperBase { - /// - /// 请求地址 - /// - public string Url { get; set; } - /// - /// 数据源 - /// - public string DatasSource { get; set; } - /// - /// 启用重置项,重置项显示在列表的第一行,用于清空当前选择的值 - /// - public bool EnableResetOption { get; set; } - /// - /// 重置项文本,重置项显示在列表的第一行,用于清空当前选择的值 - /// - public string ResetOptionText { get; set; } - /// - /// 启用多选 - /// - public bool Multiple { get; set; } - /// - /// 显示模板,值用{0}表示,范例:当前选中:{0} ,显示为 当前选中:1,2,3 - /// - public string Template { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new SelectRender( new SelectConfig( context ) ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/TagHelpers/SlideToggleTagHelper.cs b/src/Util.Ui.Angular/Material/Forms/TagHelpers/SlideToggleTagHelper.cs deleted file mode 100644 index a013023b8..000000000 --- a/src/Util.Ui.Angular/Material/Forms/TagHelpers/SlideToggleTagHelper.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Forms.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Forms.TagHelpers { - /// - /// 滑动开关 - /// - [HtmlTargetElement( "util-slide-toggle" )] - public class SlideToggleTagHelper : TagHelperBase { - /// - /// 控件的名称 - /// - public string Name { get; set; } - /// - /// 标签文本 - /// - public string Label { get; set; } - /// - /// 标签位置 - /// - public XPosition Position { get; set; } - /// - /// 禁用 - /// - public string Disabled { get; set; } - /// - /// 颜色 - /// - public Color Color { get; set; } - /// - /// 属性表达式 - /// - public ModelExpression For { get; set; } - /// - /// 模型绑定 - /// - public string Model { get; set; } - /// - /// 必填项 - /// - public bool Required { get; set; } - /// - /// 变更事件处理函数,范例:handle() - /// - public string OnChange { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new SlideToggleRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Forms/TagHelpers/TextAreaTagHelper.cs b/src/Util.Ui.Angular/Material/Forms/TagHelpers/TextAreaTagHelper.cs deleted file mode 100644 index 8faabab6c..000000000 --- a/src/Util.Ui.Angular/Material/Forms/TagHelpers/TextAreaTagHelper.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Material.Forms.Configs; -using Util.Ui.Material.Forms.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Forms.TagHelpers { - /// - /// 多行文本框 - /// - [HtmlTargetElement( "util-textarea" )] - public class TextAreaTagHelper : FormControlTagHelperBase { - /// - /// 是否显示清除按钮 - /// - public bool ShowClearButton { get; set; } - /// - /// 只读 - /// - public bool Readonly { get; set; } - /// - /// 最小长度 - /// - public string MinLength { get; set; } - /// - /// 最小长度错误消息 - /// - public string MinLengthMessage { get; set; } - /// - /// 最大长度 - /// - public string MaxLength { get; set; } - /// - /// 最小行数 - /// - public string MinRows { get; set; } - /// - /// 最大行数 - /// - public string MaxRows { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new TextBoxRender( new TextBoxConfig( context ) { IsTextArea = true } ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/TagHelpers/TextBoxTagHelper.cs b/src/Util.Ui.Angular/Material/Forms/TagHelpers/TextBoxTagHelper.cs deleted file mode 100644 index 6bf1d1187..000000000 --- a/src/Util.Ui.Angular/Material/Forms/TagHelpers/TextBoxTagHelper.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Forms.Configs; -using Util.Ui.Material.Forms.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Forms.TagHelpers { - /// - /// 文本框 - /// - [HtmlTargetElement( "util-textbox" )] - public class TextBoxTagHelper : FormControlTagHelperBase { - /// - /// 是否显示清除按钮 - /// - public bool ShowClearButton { get; set; } - /// - /// 只读 - /// - public bool Readonly { get; set; } - /// - /// 文本框类型 - /// - public TextBoxType Type { get; set; } - /// - /// 电子邮件错误消息 - /// - public string EmailMessage { get; set; } - /// - /// 最小长度 - /// - public string MinLength { get; set; } - /// - /// 最小长度错误消息 - /// - public string MinLengthMessage { get; set; } - /// - /// 最大长度 - /// - public string MaxLength { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new TextBoxRender( new TextBoxConfig( context ) ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Forms/TextBox.cs b/src/Util.Ui.Angular/Material/Forms/TextBox.cs deleted file mode 100644 index fbb2c08c1..000000000 --- a/src/Util.Ui.Angular/Material/Forms/TextBox.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Configs; -using Util.Ui.Material.Forms.Configs; -using Util.Ui.Material.Forms.Renders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Forms { - /// - /// 文本框 - /// - public class TextBox : ComponentBase,ITextBox { - /// - /// 配置 - /// - private readonly TextBoxConfig _config; - - /// - /// 初始化文本框 - /// - public TextBox() { - _config = new TextBoxConfig(); - } - - /// - /// 获取配置 - /// - protected override IConfig GetConfig() { - return _config; - } - - /// - /// 获取渲染器 - /// - protected override IRender GetRender() { - return new TextBoxRender( _config ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Grids/Builders/GridBuilder.cs b/src/Util.Ui.Angular/Material/Grids/Builders/GridBuilder.cs deleted file mode 100644 index b46ff488e..000000000 --- a/src/Util.Ui.Angular/Material/Grids/Builders/GridBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Grids.Builders { - /// - /// Mat栅格生成器 - /// - public class GridBuilder : TagBuilder { - /// - /// 初始化栅格生成器 - /// - public GridBuilder() : base( "mat-grid-list" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Grids/Builders/GridColumnBuilder.cs b/src/Util.Ui.Angular/Material/Grids/Builders/GridColumnBuilder.cs deleted file mode 100644 index 397ed5f37..000000000 --- a/src/Util.Ui.Angular/Material/Grids/Builders/GridColumnBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Grids.Builders { - /// - /// Mat栅格列生成器 - /// - public class GridColumnBuilder : TagBuilder { - /// - /// 初始化栅格列生成器 - /// - public GridColumnBuilder() : base( "mat-grid-tile" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Grids/Renders/GridColumnRender.cs b/src/Util.Ui.Angular/Material/Grids/Renders/GridColumnRender.cs deleted file mode 100644 index d2e07dd45..000000000 --- a/src/Util.Ui.Angular/Material/Grids/Renders/GridColumnRender.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Grids.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Grids.Renders { - /// - /// 栅格列渲染器 - /// - public class GridColumnRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化栅格列渲染器 - /// - /// 配置 - public GridColumnRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new GridColumnBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigSpan( builder ); - ConfigContent( builder ); - } - - /// - /// 配置合并 - /// - private void ConfigSpan( TagBuilder builder ) { - builder.AddAttribute( "colspan", _config.GetValue( UiConst.Colspan ) ); - builder.AddAttribute( "rowspan", _config.GetValue( UiConst.Rowspan ) ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Grids/Renders/GridRender.cs b/src/Util.Ui.Angular/Material/Grids/Renders/GridRender.cs deleted file mode 100644 index a867005f3..000000000 --- a/src/Util.Ui.Angular/Material/Grids/Renders/GridRender.cs +++ /dev/null @@ -1,85 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Grids.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Grids.Renders { - /// - /// 栅格渲染器 - /// - public class GridRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化栅格渲染器 - /// - /// 配置 - public GridRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new GridBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigColumns( builder ); - ConfigRowHeight( builder ); - ConfigGutterSize( builder ); - ConfigContent( builder ); - } - - /// - /// 配置列数 - /// - private void ConfigColumns( TagBuilder builder ) { - builder.AddAttribute( "cols", _config.GetValue( UiConst.Columns ) ); - } - - /// - /// 配置行高 - /// - private void ConfigRowHeight( TagBuilder builder ) { - if ( _config.Contains( UiConst.RowHeight ) == false ) - return; - var value = _config.GetValue( UiConst.RowHeight ); - if ( Util.Helpers.Validation.IsNumber( value ) ) - value = $"{value}px"; - builder.AddAttribute( "rowHeight", value ); - } - - /// - /// 配置单元格间距 - /// - private void ConfigGutterSize( TagBuilder builder ) { - if( _config.Contains( MaterialConst.GutterSize ) == false ) - return; - var value = _config.GetValue( MaterialConst.GutterSize ); - if( Util.Helpers.Validation.IsNumber( value ) ) - value = $"{value}px"; - builder.AddAttribute( "gutterSize", value ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Grids/TagHelpers/GridColumnTagHelper.cs b/src/Util.Ui.Angular/Material/Grids/TagHelpers/GridColumnTagHelper.cs deleted file mode 100644 index f99dbfc20..000000000 --- a/src/Util.Ui.Angular/Material/Grids/TagHelpers/GridColumnTagHelper.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Grids.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Grids.TagHelpers { - /// - /// 栅格列 - /// - [HtmlTargetElement( "util-grid-column" )] - public class GridColumnTagHelper : TagHelperBase { - /// - /// 合并列 - /// - public int Colspan { get; set; } - /// - /// 合并行 - /// - public int Rowspan { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new GridColumnRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Grids/TagHelpers/GridTagHelper.cs b/src/Util.Ui.Angular/Material/Grids/TagHelpers/GridTagHelper.cs deleted file mode 100644 index c893144b6..000000000 --- a/src/Util.Ui.Angular/Material/Grids/TagHelpers/GridTagHelper.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Grids.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Grids.TagHelpers { - /// - /// 栅格 - /// - [HtmlTargetElement( "util-grid" )] - public class GridTagHelper : TagHelperBase { - /// - /// 列数 - /// - public int Columns { get; set; } - /// - /// 行高,可指定单位,如果仅传入数值,则单位为px - /// - public string RowHeight { get; set; } - /// - /// 单元格间距,可指定单位,如果仅传入数值,则单位为px - /// - public string GutterSize { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new GridRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Icons/Builders/FontAwesomeIconBuilder.cs b/src/Util.Ui.Angular/Material/Icons/Builders/FontAwesomeIconBuilder.cs deleted file mode 100644 index e3dc0b839..000000000 --- a/src/Util.Ui.Angular/Material/Icons/Builders/FontAwesomeIconBuilder.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Enums; - -namespace Util.Ui.Material.Icons.Builders { - /// - /// FontAwesome图标生成器 - /// - public class FontAwesomeIconBuilder : TagBuilder { - /// - /// 初始化FontAwesome图标生成器 - /// - public FontAwesomeIconBuilder() : base( "i" ) { - } - - /// - /// 设置图标 - /// - /// 配置 - /// 键 - public void SetIcon( IConfig config, string key = UiConst.FontAwesomeIcon ) { - Class( config.GetValue( key ).GetIcon() ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Icons/Builders/MaterialIconBuilder.cs b/src/Util.Ui.Angular/Material/Icons/Builders/MaterialIconBuilder.cs deleted file mode 100644 index 553c830b4..000000000 --- a/src/Util.Ui.Angular/Material/Icons/Builders/MaterialIconBuilder.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Enums; - -namespace Util.Ui.Material.Icons.Builders { - /// - /// Material图标生成器 - /// - public class MaterialIconBuilder : TagBuilder { - /// - /// 初始化图标生成器 - /// - public MaterialIconBuilder() : base( "mat-icon" ) { - } - - /// - /// 设置图标 - /// - /// 配置 - public void SetIcon( IConfig config) { - SetContent( config.GetValue( UiConst.MaterialIcon )?.Description() ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Icons/Builders/StackIconBuilder.cs b/src/Util.Ui.Angular/Material/Icons/Builders/StackIconBuilder.cs deleted file mode 100644 index 31eebd22a..000000000 --- a/src/Util.Ui.Angular/Material/Icons/Builders/StackIconBuilder.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Icons.Builders { - /// - /// 堆叠图标生成器 - /// - public class StackIconBuilder : TagBuilder { - /// - /// 初始化堆叠图标生成器 - /// - public StackIconBuilder() : base( "span" ) { - Class( "fa-stack" ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Icons/Configs/IconConfig.cs b/src/Util.Ui.Angular/Material/Icons/Configs/IconConfig.cs deleted file mode 100644 index 06c2a787b..000000000 --- a/src/Util.Ui.Angular/Material/Icons/Configs/IconConfig.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Util.Ui.Configs; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Icons.Configs { - /// - /// 图标配置 - /// - public class IconConfig : Config { - /// - /// 初始化图标配置 - /// - public IconConfig() { - } - - /// - /// 初始化图标配置 - /// - /// TagHelper上下文 - public IconConfig( Context context ) : base( context ) { - } - - /// - /// 验证 - /// - public override string GetValidateMessage() { - if ( !Contains( UiConst.FontAwesomeIcon ) && !Contains( UiConst.MaterialIcon ) ) - return "请设置FontAwesome或Material属性"; - return string.Empty; - } - } -} diff --git a/src/Util.Ui.Angular/Material/Icons/Icon.cs b/src/Util.Ui.Angular/Material/Icons/Icon.cs deleted file mode 100644 index 9ee0623ed..000000000 --- a/src/Util.Ui.Angular/Material/Icons/Icon.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Configs; -using Util.Ui.Material.Icons.Configs; -using Util.Ui.Material.Icons.Renders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Icons { - /// - /// 图标 - /// - public class Icon : ComponentBase, IIcon { - /// - /// 获取渲染器 - /// - protected override IRender GetRender() { - return new IconRender( OptionConfig ); - } - - /// - /// 获取配置 - /// - protected override IConfig GetConfig() { - return new IconConfig(); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Icons/Renders/IconRender.cs b/src/Util.Ui.Angular/Material/Icons/Renders/IconRender.cs deleted file mode 100644 index e6368bfb8..000000000 --- a/src/Util.Ui.Angular/Material/Icons/Renders/IconRender.cs +++ /dev/null @@ -1,142 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Extensions; -using Util.Ui.Material.Icons.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Icons.Renders { - /// - /// 图标渲染器 - /// - public class IconRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化图标渲染器 - /// - /// 配置 - public IconRender( IConfig config ) : base( config ){ - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - TagBuilder result = GetIconBuilder(); - if ( result == TagBuilder.Null ) - return result; - Config( result ); - return result; - } - - /// - /// 获取图标生成器 - /// - private TagBuilder GetIconBuilder() { - if( IsStackIcon() ) - return GetStackIconBuilder(); - return GetSingleIconBuilder(); - } - - /// - /// 是否堆叠图标 - /// - private bool IsStackIcon() { - return _config.Contains( UiConst.Child ); - } - - /// - /// 获取堆叠图标标签生成器 - /// - private TagBuilder GetStackIconBuilder() { - var result = new StackIconBuilder(); - var parentIcon = GetSingleIconBuilder().Class( "fa-stack-2x" ); - var childIcon = GetFontAwesomeBuilder( UiConst.Child ).Class( "fa-stack-1x" ); - if ( _config.Contains( UiConst.ChildClass ) ) - childIcon.Class( _config.GetValue( UiConst.ChildClass ) ); - result.AppendContent( parentIcon ); - result.AppendContent( childIcon ); - return result; - } - - /// - /// 获取单个图标生成器 - /// - private TagBuilder GetSingleIconBuilder() { - TagBuilder result = TagBuilder.Null; - if( _config.Contains( UiConst.FontAwesomeIcon ) ) - result = GetFontAwesomeBuilder( UiConst.FontAwesomeIcon ); - if( _config.Contains( UiConst.MaterialIcon ) ) - result = GetMaterialIconBuilder(); - result.AddOutputAttributes( _config ); - result.Class( _config ); - return result; - } - - /// - /// 获取Font Awesome图标标签生成器 - /// - private FontAwesomeIconBuilder GetFontAwesomeBuilder( string key ) { - var builder = new FontAwesomeIconBuilder(); - builder.SetIcon( _config, key ); - return builder; - } - - /// - /// 获取Material图标标签生成器 - /// - private MaterialIconBuilder GetMaterialIconBuilder() { - var builder = new MaterialIconBuilder(); - builder.SetIcon( _config ); - return builder; - } - - /// - /// 公共配置 - /// - private void Config( TagBuilder builder ) { - builder.Style( _config ); - ConfigId( builder ); - ConfigSize( builder ); - ConfigSpin( builder ); - ConfigRotate( builder ); - } - - /// - /// 配置标识 - /// - protected override void ConfigId( TagBuilder builder ) { - if( _config.Contains( UiConst.Id ) ) - builder.Attribute( UiConst.Id, _config.GetValue( UiConst.Id ), true ); - } - - /// - /// 配置图标大小 - /// - private void ConfigSize( TagBuilder builder ) { - if( _config.Contains( UiConst.Size ) ) - builder.Class( _config.GetValue( UiConst.Size ).Description() ); - } - - /// - /// 配置图标旋转 - /// - private void ConfigSpin( TagBuilder builder ) { - if( _config.GetValue( UiConst.Spin ) ) - builder.Class( "fa-spin" ); - } - - /// - /// 配置图标旋转 - /// - private void ConfigRotate( TagBuilder builder ) { - if( _config.Contains( UiConst.Rotate ) ) - builder.Class( _config.GetValue( UiConst.Rotate ).Description() ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Icons/TagHelpers/IconTagHelper.cs b/src/Util.Ui.Angular/Material/Icons/TagHelpers/IconTagHelper.cs deleted file mode 100644 index 8620c1bbb..000000000 --- a/src/Util.Ui.Angular/Material/Icons/TagHelpers/IconTagHelper.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Enums; -using Util.Ui.Material.Icons.Configs; -using Util.Ui.Material.Icons.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Icons.TagHelpers { - /// - /// 图标 - /// - [HtmlTargetElement( "util-icon" )] - public class IconTagHelper : TagHelperBase { - /// - /// Font Awesome图标 - /// - public FontAwesomeIcon FontAwesomeIcon { get; set; } - /// - /// Material图标 - /// - public MaterialIcon MaterialIcon { get; set; } - /// - /// 图标大小 - /// - public IconSize Size { get; set; } - /// - /// 持续旋转 - /// - public bool Spin { get; set; } - /// - /// 旋转 - /// - public RotateType Rotate { get; set; } - /// - /// 子图标 - /// - public FontAwesomeIcon Child { get; set; } - /// - /// 子图标class - /// - public string ChildClass { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new IconRender( new IconConfig( context ) ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Menus/Builders/MenuBuilder.cs b/src/Util.Ui.Angular/Material/Menus/Builders/MenuBuilder.cs deleted file mode 100644 index 4e7d59d0e..000000000 --- a/src/Util.Ui.Angular/Material/Menus/Builders/MenuBuilder.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Util.Ui.Angular.Builders; -using Util.Ui.Builders; - -namespace Util.Ui.Material.Menus.Builders { - /// - /// Material菜单生成器 - /// - public class MenuBuilder : TagBuilder { - /// - /// 模板生成器 - /// - private readonly TemplateBuilder _templateBuilder; - - /// - /// 初始化菜单生成器 - /// - public MenuBuilder() : base( "mat-menu" ) { - _templateBuilder = new TemplateBuilder(); - _templateBuilder.AddAttribute( "matMenuContent", "", false ); - SetContent( _templateBuilder ); - } - - /// - /// 模板生成器 - /// - public TemplateBuilder TemplateBuilder => _templateBuilder; - } -} diff --git a/src/Util.Ui.Angular/Material/Menus/Configs/MenuConfig.cs b/src/Util.Ui.Angular/Material/Menus/Configs/MenuConfig.cs deleted file mode 100644 index a7a9ada70..000000000 --- a/src/Util.Ui.Angular/Material/Menus/Configs/MenuConfig.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Util.Ui.Configs; -using Util.Ui.Material.Menus.Datas; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Menus.Configs { - /// - /// 菜单配置 - /// - public class MenuConfig : Config { - /// - /// 初始化菜单配置 - /// - public MenuConfig() { - } - - /// - /// 初始化菜单配置 - /// - /// 上下文 - public MenuConfig( Context context ) : base( context ) { - } - - /// - /// 菜单数据 - /// - public MenuData Data { get; set; } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Menus/Datas/MenuData.cs b/src/Util.Ui.Angular/Material/Menus/Datas/MenuData.cs deleted file mode 100644 index ed1d899e2..000000000 --- a/src/Util.Ui.Angular/Material/Menus/Datas/MenuData.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; -using Util.Ui.Material.Enums; - -namespace Util.Ui.Material.Menus.Datas { - /// - /// 菜单数据 - /// - public class MenuData { - /// - /// 初始化菜单数据 - /// - public MenuData() { - Items = new List(); - } - - /// - /// 菜单项列表 - /// - public List Items { get; set; } - /// - /// 标识 - /// - public string Id { get; set; } - /// - /// X轴位置 - /// - public XPosition? XPosition { get; set; } - /// - /// Y轴位置 - /// - public YPosition? YPosition { get; set; } - /// - /// 弹出菜单是否与触发按钮重叠 - /// - public bool? Overlap { get; set; } - } -} diff --git a/src/Util.Ui.Angular/Material/Menus/Datas/MenuItemData.cs b/src/Util.Ui.Angular/Material/Menus/Datas/MenuItemData.cs deleted file mode 100644 index 83aaba385..000000000 --- a/src/Util.Ui.Angular/Material/Menus/Datas/MenuItemData.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Util.Ui.Enums; - -namespace Util.Ui.Material.Menus.Datas { - /// - /// 菜单项数据 - /// - public class MenuItemData { - /// - /// 标签 - /// - public string Label { get; set; } - /// - /// Material图标 - /// - public MaterialIcon? MaterialIcon { get; set; } - /// - /// Font Awesome图标 - /// - public FontAwesomeIcon? FontAwesomeIcon { get; set; } - /// - /// 禁用 - /// - public string Disabled { get; set; } - /// - /// 路由链接地址 - /// - public string Link { get; set; } - /// - /// 单击事件处理函数,范例:handle() - /// - public string OnClick { get; set; } - /// - /// 子菜单标识 - /// - public string MenuId { get; set; } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Menus/Datas/MenuNode.cs b/src/Util.Ui.Angular/Material/Menus/Datas/MenuNode.cs deleted file mode 100644 index ecfb898b2..000000000 --- a/src/Util.Ui.Angular/Material/Menus/Datas/MenuNode.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System.Collections.Generic; -using Util.Ui.Enums; - -namespace Util.Ui.Material.Menus.Datas { - /// - /// 菜单节点 - /// - public class MenuNode { - /// - /// 标识 - /// - public string Id { get; set; } - /// - /// 父标识 - /// - public string ParentId { get; set; } - /// - /// 标签 - /// - public string Label { get; set; } - /// - /// Material图标 - /// - public MaterialIcon? MaterialIcon { get; set; } - /// - /// Font Awesome图标 - /// - public FontAwesomeIcon? FontAwesomeIcon { get; set; } - /// - /// 禁用 - /// - public string Disabled { get; set; } - /// - /// 路由链接地址 - /// - public string Link { get; set; } - /// - /// 单击事件处理函数,范例:handle() - /// - public string OnClick { get; set; } - - /// - /// 转换为菜单项数据 - /// - /// 菜单节点列表 - public MenuItemData ToMenuItemData( List nodes ) { - return new MenuItemData { - Label = Label, - MaterialIcon = MaterialIcon, - FontAwesomeIcon = FontAwesomeIcon, - Disabled = Disabled, - Link = Link, - OnClick = OnClick, - MenuId = nodes.Exists( t => t.ParentId == Id && string.IsNullOrWhiteSpace( t.ParentId ) == false ) ? GetMenuId( Id ) : string.Empty - }; - } - - /// - /// 获取菜单标识 - /// - /// 标识 - public static string GetMenuId( string id ) { - return $"m_{id}"; - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Menus/Datas/MenuNodeCollection.cs b/src/Util.Ui.Angular/Material/Menus/Datas/MenuNodeCollection.cs deleted file mode 100644 index 28d0d2041..000000000 --- a/src/Util.Ui.Angular/Material/Menus/Datas/MenuNodeCollection.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Util.Helpers; -using Util.Ui.Material.Enums; - -namespace Util.Ui.Material.Menus.Datas { - /// - /// 菜单节点集合 - /// - public class MenuNodeCollection { - /// - /// 初始化菜单数据 - /// - public MenuNodeCollection() { - Nodes = new List(); - RootId = $"m_{Id.Guid()}"; - } - - /// - /// 菜单节点列表 - /// - public List Nodes { get; set; } - /// - /// 根节点标识 - /// - public string RootId { get; set; } - /// - /// X轴位置 - /// - public XPosition? XPosition { get; set; } - /// - /// Y轴位置 - /// - public YPosition? YPosition { get; set; } - /// - /// 弹出菜单是否与触发按钮重叠 - /// - public bool? Overlap { get; set; } - - /// - /// 转换为菜单数据列表 - /// - public List ToMenuDatas() { - var result = GetMenuDatas(); - InitRootMenu( result ); - return result; - } - - /// - /// 获取菜单数据列表 - /// - private List GetMenuDatas() { - var groupNodes = from node in Nodes - group node by node.ParentId; - return groupNodes.Select( t => new MenuData { - Id = GetMenuId( t.Key ), - Items = t.Select( node => node.ToMenuItemData( Nodes ) ).ToList() - } ).ToList(); - } - - /// - /// 获取菜单标识 - /// - private string GetMenuId( string parentId ) { - if ( string.IsNullOrWhiteSpace( parentId ) ) - return RootId; - return MenuNode.GetMenuId( parentId ); - } - - /// - /// 初始化根菜单 - /// - private void InitRootMenu( List datas ) { - var data = datas.Find( t => t.Id == RootId ); - data.XPosition = XPosition; - data.YPosition = YPosition; - data.Overlap = Overlap; - } - } -} diff --git a/src/Util.Ui.Angular/Material/Menus/IMenu.cs b/src/Util.Ui.Angular/Material/Menus/IMenu.cs deleted file mode 100644 index 650515313..000000000 --- a/src/Util.Ui.Angular/Material/Menus/IMenu.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Material.Menus.Wrappers; - -namespace Util.Ui.Material.Menus { - /// - /// 菜单 - /// - public interface IMenu : IContainer { - } -} diff --git a/src/Util.Ui.Angular/Material/Menus/IMenuItem.cs b/src/Util.Ui.Angular/Material/Menus/IMenuItem.cs deleted file mode 100644 index 2b662dc6b..000000000 --- a/src/Util.Ui.Angular/Material/Menus/IMenuItem.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Operations; -using Util.Ui.Operations.Events; -using Util.Ui.Operations.Navigation; - -namespace Util.Ui.Material.Menus { - /// - /// 菜单项 - /// - public interface IMenuItem : IComponent,ILabel,ISetIcon,IDisabled,ILink,IOnClick, IMenuId { - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Menus/Menu.cs b/src/Util.Ui.Angular/Material/Menus/Menu.cs deleted file mode 100644 index bb7dd0d73..000000000 --- a/src/Util.Ui.Angular/Material/Menus/Menu.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.IO; -using Util.Ui.Components; -using Util.Ui.Configs; -using Util.Ui.Material.Menus.Configs; -using Util.Ui.Material.Menus.Renders; -using Util.Ui.Material.Menus.Wrappers; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Menus { - /// - /// 菜单 - /// - public class Menu : ContainerBase, IMenu { - /// - /// 配置 - /// - private readonly MenuConfig _config; - - /// - /// 初始化菜单 - /// - public Menu() : this( null ) { - } - - /// - /// 初始化菜单 - /// - /// 流写入器 - public Menu( TextWriter writer ) : base( writer ) { - _config = new MenuConfig(); - } - - /// - /// 获取配置 - /// - protected override IConfig GetConfig() { - return _config; - } - - /// - /// 获取渲染器 - /// - protected override IRender GetRender() { - return new MenuRender( _config ); - } - - /// - /// 获取容器包装器 - /// - protected override IMenuWrapper GetWrapper() { - return new MenuWrapper( this ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Menus/MenuItem.cs b/src/Util.Ui.Angular/Material/Menus/MenuItem.cs deleted file mode 100644 index cc3d94dbe..000000000 --- a/src/Util.Ui.Angular/Material/Menus/MenuItem.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Extensions; -using Util.Ui.Material.Extensions; -using Util.Ui.Material.Menus.Datas; -using Util.Ui.Material.Menus.Renders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Menus { - /// - /// 菜单项 - /// - public class MenuItem : ComponentBase, IMenuItem { - /// - /// 初始化菜单项 - /// - public MenuItem() { - } - - /// - /// 初始化菜单项 - /// - /// 菜单项数据 - public MenuItem( MenuItemData data ) { - this.Label( data.Label ).Icon( data.FontAwesomeIcon ).Icon( data.MaterialIcon ) - .Disable( data.Disabled ).Link( data.Link ).OnClick( data.OnClick ).Menu( data.MenuId ); - } - - /// - /// 获取渲染器 - /// - protected override IRender GetRender() { - return new MenuItemRender( OptionConfig ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Menus/Renders/MenuItemRender.cs b/src/Util.Ui.Angular/Material/Menus/Renders/MenuItemRender.cs deleted file mode 100644 index 203b52f5f..000000000 --- a/src/Util.Ui.Angular/Material/Menus/Renders/MenuItemRender.cs +++ /dev/null @@ -1,126 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Icons.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Menus.Renders { - /// - /// 菜单项渲染器 - /// - public class MenuItemRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化菜单项渲染器 - /// - /// 配置 - public MenuItemRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = CreateBuilder(); - builder.AddAttribute( "mat-menu-item" ); - Config( builder ); - return builder; - } - - /// - /// 创建标签生成器 - /// - private TagBuilder CreateBuilder() { - if( _config.Contains( UiConst.Link ) ) - return new AnchorBuilder(); - return new ButtonBuilder(); - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigContent( builder ); - ConfigDisabled( builder ); - ConfigEvents( builder ); - ConfigLink( builder ); - ConfigMenu( builder ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - ConfigMaterialIcon( builder ); - ConfigFontAwesomeIcon( builder ); - ConfigLabel( builder ); - } - - /// - /// 配置Material图标 - /// - private void ConfigMaterialIcon( TagBuilder builder ) { - if( _config.Contains( UiConst.MaterialIcon ) == false ) - return; - var iconBuilder = new MaterialIconBuilder(); - iconBuilder.SetIcon( _config ); - builder.AppendContent( iconBuilder ); - } - - /// - /// 配置FontAwesome图标 - /// - private void ConfigFontAwesomeIcon( TagBuilder builder ) { - if( _config.Contains( UiConst.FontAwesomeIcon ) == false ) - return; - var iconBuilder = new FontAwesomeIconBuilder(); - iconBuilder.SetIcon( _config ); - builder.AppendContent( iconBuilder ); - } - - /// - /// 配置标签 - /// - private void ConfigLabel( TagBuilder builder ) { - if( _config.Contains( UiConst.Label ) == false ) - return; - var spanBuilder = new SpanBuilder(); - spanBuilder.SetContent( _config.GetValue( UiConst.Label ) ); - builder.AppendContent( spanBuilder ); - } - - /// - /// 配置禁用 - /// - private void ConfigDisabled( TagBuilder builder ) { - builder.AddAttribute( "[disabled]", _config.GetBoolValue( UiConst.Disabled ) ); - } - - /// - /// 配置事件 - /// - private void ConfigEvents( TagBuilder builder ) { - builder.AddAttribute( "(click)", _config.GetValue( UiConst.OnClick ) ); - } - - /// - /// 配置链接 - /// - private void ConfigLink( TagBuilder builder ) { - builder.AddAttribute( "routerLink", _config.GetValue( UiConst.Link ) ); - } - - /// - /// 配置子菜单 - /// - private void ConfigMenu( TagBuilder builder ) { - builder.AddAttribute( "[matMenuTriggerFor]", _config.GetValue( MaterialConst.MenuId ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Menus/Renders/MenuRender.cs b/src/Util.Ui.Angular/Material/Menus/Renders/MenuRender.cs deleted file mode 100644 index 858d25b73..000000000 --- a/src/Util.Ui.Angular/Material/Menus/Renders/MenuRender.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System.IO; -using System.Text.Encodings.Web; -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Menus.Builders; -using Util.Ui.Material.Menus.Configs; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Menus.Renders { - /// - /// 菜单渲染器 - /// - public class MenuRender : RenderBase { - /// - /// 配置 - /// - private readonly MenuConfig _config; - - /// - /// 初始化菜单渲染器 - /// - /// 配置 - public MenuRender( MenuConfig config ) : base( config ) { - _config = config; - } - - /// - /// 渲染 - /// - /// 流写入器 - /// 编码 - public override void WriteTo( TextWriter writer, HtmlEncoder encoder ) { - var builder = (MenuBuilder)Builder; - builder.TemplateBuilder.AppendContent( _config.Content ); - Builder.WriteTo( writer, encoder ); - } - - /// - /// 渲染起始标签 - /// - /// 流写入器 - public override void RenderStartTag( TextWriter writer ) { - var builder = (MenuBuilder)Builder; - builder.RenderStartTag( writer ); - builder.TemplateBuilder.RenderStartTag( writer ); - } - - /// - /// 渲染结束标签 - /// - /// 流写入器 - public override void RenderEndTag( TextWriter writer ) { - var builder = (MenuBuilder)Builder; - builder.TemplateBuilder.RenderEndTag( writer ); - builder.RenderEndTag( writer ); - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new MenuBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( MenuBuilder builder ) { - ConfigId( builder ); - ConfigPosition( builder ); - ConfigOverlap( builder ); - ConfigItems( builder ); - } - - /// - /// 配置标识 - /// - protected override void ConfigId( TagBuilder builder ) { - if( _config.Contains( UiConst.Id ) ) { - builder.AddAttribute( $"#{_config.GetValue( UiConst.Id )}", "matMenu" ); - return; - } - builder.AddAttribute( "#menu", "matMenu" ); - } - - /// - /// 配置位置 - /// - private void ConfigPosition( TagBuilder builder ) { - builder.AddAttribute( "xPosition", _config.GetValue( UiConst.XPosition )?.Description() ); - builder.AddAttribute( "yPosition", _config.GetValue( UiConst.YPosition )?.Description() ); - } - - /// - /// 配置重叠 - /// - private void ConfigOverlap( TagBuilder builder ) { - builder.AddAttribute( "[overlapTrigger]", _config.GetBoolValue( UiConst.Overlap ) ); - } - - /// - /// 配置菜单项 - /// - private void ConfigItems( MenuBuilder builder ) { - _config.Data?.Items.ForEach( item => { - builder.TemplateBuilder.AppendContent( new MenuItem( item ) ); - } ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Menus/TagHelpers/MenuItemTagHelper.cs b/src/Util.Ui.Angular/Material/Menus/TagHelpers/MenuItemTagHelper.cs deleted file mode 100644 index c99bfdfdc..000000000 --- a/src/Util.Ui.Angular/Material/Menus/TagHelpers/MenuItemTagHelper.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Material.Menus.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Menus.TagHelpers { - /// - /// 菜单项 - /// - [HtmlTargetElement( "util-menu-item" )] - public class MenuItemTagHelper : TagHelperBase { - /// - /// 标签 - /// - public string Label { get; set; } - /// - /// Material图标 - /// - public MaterialIcon MaterialIcon { get; set; } - /// - /// Font Awesome图标 - /// - public FontAwesomeIcon FontAwesomeIcon { get; set; } - /// - /// 禁用 - /// - public string Disabled { get; set; } - /// - /// 路由链接地址 - /// - public string Link { get; set; } - /// - /// 单击事件处理函数,范例:handle() - /// - public string OnClick { get; set; } - /// - /// 子菜单标识 - /// - public string MenuId { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new MenuItemRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Menus/TagHelpers/MenuTagHelper.cs b/src/Util.Ui.Angular/Material/Menus/TagHelpers/MenuTagHelper.cs deleted file mode 100644 index e8893948b..000000000 --- a/src/Util.Ui.Angular/Material/Menus/TagHelpers/MenuTagHelper.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Menus.Configs; -using Util.Ui.Material.Menus.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Menus.TagHelpers { - /// - /// 菜单 - /// - [HtmlTargetElement( "util-menu" )] - public class MenuTagHelper : TagHelperBase { - /// - /// X轴位置 - /// - public XPosition XPosition { get; set; } - /// - /// Y轴位置 - /// - public YPosition YPosition { get; set; } - /// - /// 弹出菜单是否与触发按钮重叠 - /// - public bool Overlap { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new MenuRender( new MenuConfig( context ) ); - } - } -} diff --git a/src/Util.Ui.Angular/Material/Menus/Wrappers/IMenuWrapper.cs b/src/Util.Ui.Angular/Material/Menus/Wrappers/IMenuWrapper.cs deleted file mode 100644 index 2b85e04c4..000000000 --- a/src/Util.Ui.Angular/Material/Menus/Wrappers/IMenuWrapper.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace Util.Ui.Material.Menus.Wrappers { - /// - /// 菜单包装器 - /// - public interface IMenuWrapper : IDisposable { - /// - /// 菜单项 - /// - IMenuItem Item(); - } -} diff --git a/src/Util.Ui.Angular/Material/Menus/Wrappers/MenuWrapper.cs b/src/Util.Ui.Angular/Material/Menus/Wrappers/MenuWrapper.cs deleted file mode 100644 index 97d37d526..000000000 --- a/src/Util.Ui.Angular/Material/Menus/Wrappers/MenuWrapper.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Util.Ui.Components.Internal; - -namespace Util.Ui.Material.Menus.Wrappers { - /// - /// 菜单包装器 - /// - public class MenuWrapper : IMenuWrapper { - /// - /// 菜单 - /// - private readonly IMenu _container; - - /// - /// 初始化菜单包装器 - /// - /// 菜单 - public MenuWrapper( IMenu menu ) { - _container = menu; - } - - /// - /// 渲染结束 - /// - public void Dispose() { - if( _container is IRenderEnd container ) - container.End(); - } - - /// - /// 菜单项 - /// - public IMenuItem Item() { - return new MenuItem(); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/Builders/AccordionBuilder.cs b/src/Util.Ui.Angular/Material/Panels/Builders/AccordionBuilder.cs deleted file mode 100644 index 422b60018..000000000 --- a/src/Util.Ui.Angular/Material/Panels/Builders/AccordionBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Panels.Builders { - /// - /// Mat手风琴生成器 - /// - public class AccordionBuilder : TagBuilder { - /// - /// 初始化手风琴生成器 - /// - public AccordionBuilder() : base( "mat-accordion" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/Builders/PanelActionBuilder.cs b/src/Util.Ui.Angular/Material/Panels/Builders/PanelActionBuilder.cs deleted file mode 100644 index 7788fce4f..000000000 --- a/src/Util.Ui.Angular/Material/Panels/Builders/PanelActionBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Panels.Builders { - /// - /// Mat面板操作生成器 - /// - public class PanelActionBuilder : TagBuilder { - /// - /// 初始化面板操作生成器 - /// - public PanelActionBuilder() : base( "mat-action-row" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/Builders/PanelBuilder.cs b/src/Util.Ui.Angular/Material/Panels/Builders/PanelBuilder.cs deleted file mode 100644 index fd1d6ea08..000000000 --- a/src/Util.Ui.Angular/Material/Panels/Builders/PanelBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Panels.Builders { - /// - /// Mat面板生成器 - /// - public class PanelBuilder : TagBuilder { - /// - /// 初始化面板生成器 - /// - public PanelBuilder() : base( "mat-expansion-panel" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/Builders/PanelDescriptionBuilder.cs b/src/Util.Ui.Angular/Material/Panels/Builders/PanelDescriptionBuilder.cs deleted file mode 100644 index fca2e3f68..000000000 --- a/src/Util.Ui.Angular/Material/Panels/Builders/PanelDescriptionBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Panels.Builders { - /// - /// Mat面板描述生成器 - /// - public class PanelDescriptionBuilder : TagBuilder { - /// - /// 初始化面板描述生成器 - /// - public PanelDescriptionBuilder() : base( "mat-panel-description" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/Builders/PanelHeaderBuilder.cs b/src/Util.Ui.Angular/Material/Panels/Builders/PanelHeaderBuilder.cs deleted file mode 100644 index 475ffc53a..000000000 --- a/src/Util.Ui.Angular/Material/Panels/Builders/PanelHeaderBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Panels.Builders { - /// - /// Mat面板头部生成器 - /// - public class PanelHeaderBuilder : TagBuilder { - /// - /// 初始化面板头部生成器 - /// - public PanelHeaderBuilder() : base( "mat-expansion-panel-header" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/Builders/PanelTitleBuilder.cs b/src/Util.Ui.Angular/Material/Panels/Builders/PanelTitleBuilder.cs deleted file mode 100644 index 69dbf2071..000000000 --- a/src/Util.Ui.Angular/Material/Panels/Builders/PanelTitleBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Panels.Builders { - /// - /// Mat面板标题生成器 - /// - public class PanelTitleBuilder : TagBuilder { - /// - /// 初始化面板标题生成器 - /// - public PanelTitleBuilder() : base( "mat-panel-title" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/Renders/AccordionRender.cs b/src/Util.Ui.Angular/Material/Panels/Renders/AccordionRender.cs deleted file mode 100644 index 8af36a615..000000000 --- a/src/Util.Ui.Angular/Material/Panels/Renders/AccordionRender.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Panels.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Panels.Renders { - /// - /// 手风琴渲染器 - /// - public class AccordionRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化手风琴渲染器 - /// - /// 配置 - public AccordionRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new AccordionBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigMultiple( builder ); - ConfigDisplayMode( builder ); - ConfigContent( builder ); - } - - /// - /// 配置多展开状态 - /// - private void ConfigMultiple( TagBuilder builder ) { - builder.AddAttribute( "multi", _config.GetBoolValue( UiConst.Multiple ) ); - } - - /// - /// 配置显示模式 - /// - private void ConfigDisplayMode( TagBuilder builder ) { - builder.AddAttribute( "displayMode", _config.GetBoolValue( MaterialConst.DisplayMode ) ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/Renders/PanelActionRender.cs b/src/Util.Ui.Angular/Material/Panels/Renders/PanelActionRender.cs deleted file mode 100644 index 82c846150..000000000 --- a/src/Util.Ui.Angular/Material/Panels/Renders/PanelActionRender.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Panels.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Panels.Renders { - /// - /// 面板操作渲染器 - /// - public class PanelActionRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化面板操作渲染器 - /// - /// 配置 - public PanelActionRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new PanelActionBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigContent( builder ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/Renders/PanelContentRender.cs b/src/Util.Ui.Angular/Material/Panels/Renders/PanelContentRender.cs deleted file mode 100644 index 246316de1..000000000 --- a/src/Util.Ui.Angular/Material/Panels/Renders/PanelContentRender.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Util.Ui.Angular.Builders; -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Panels.Renders { - /// - /// 面板内容渲染器 - /// - public class PanelContentRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化面板内容渲染器 - /// - /// 配置 - public PanelContentRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new TemplateBuilder(); - builder.AddAttribute( "matExpansionPanelContent" ); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigContent( builder ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/Renders/PanelDescriptionRender.cs b/src/Util.Ui.Angular/Material/Panels/Renders/PanelDescriptionRender.cs deleted file mode 100644 index 3fbb5a905..000000000 --- a/src/Util.Ui.Angular/Material/Panels/Renders/PanelDescriptionRender.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Panels.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Panels.Renders { - /// - /// 面板描述渲染器 - /// - public class PanelDescriptionRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化面板描述渲染器 - /// - /// 配置 - public PanelDescriptionRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new PanelDescriptionBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigContent( builder ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/Renders/PanelHeaderRender.cs b/src/Util.Ui.Angular/Material/Panels/Renders/PanelHeaderRender.cs deleted file mode 100644 index a150c1d60..000000000 --- a/src/Util.Ui.Angular/Material/Panels/Renders/PanelHeaderRender.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Panels.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Panels.Renders { - /// - /// 面板头部渲染器 - /// - public class PanelHeaderRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化面板头部渲染器 - /// - /// 配置 - public PanelHeaderRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new PanelHeaderBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigHeight( builder ); - ConfigContent( builder ); - } - - /// - /// 配置高度 - /// - private void ConfigHeight( TagBuilder builder ) { - if( _config.Contains( MaterialConst.CollapsedHeight ) ) - builder.AddAttribute( "collapsedHeight", $"{_config.GetValue( MaterialConst.CollapsedHeight )}px" ); - if( _config.Contains( MaterialConst.ExpandedHeight ) ) - builder.AddAttribute( "expandedHeight", $"{_config.GetValue( MaterialConst.ExpandedHeight )}px" ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/Renders/PanelRender.cs b/src/Util.Ui.Angular/Material/Panels/Renders/PanelRender.cs deleted file mode 100644 index bc1111a74..000000000 --- a/src/Util.Ui.Angular/Material/Panels/Renders/PanelRender.cs +++ /dev/null @@ -1,84 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Panels.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Panels.Renders { - /// - /// 面板渲染器 - /// - public class PanelRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化面板渲染器 - /// - /// 配置 - public PanelRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new PanelBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigHideToggle( builder ); - ConfigExpanded( builder ); - ConfigDisabled( builder ); - ConfigEvents( builder ); - ConfigContent( builder ); - } - - /// - /// 配置隐藏折叠开关 - /// - private void ConfigHideToggle( TagBuilder builder ) { - builder.AddAttribute( "hideToggle", _config.GetBoolValue( MaterialConst.HideToggle ) ); - } - - /// - /// 配置展开 - /// - private void ConfigExpanded( TagBuilder builder ) { - builder.AddAttribute( "[expanded]", _config.GetValue( UiConst.Expanded ) ); - } - - /// - /// 配置禁用 - /// - private void ConfigDisabled( TagBuilder builder ) { - builder.AddAttribute( "[disabled]", _config.GetValue( UiConst.Disabled ) ); - } - - /// - /// 配置事件 - /// - private void ConfigEvents( TagBuilder builder ) { - builder.AddAttribute( "(opened)", _config.GetValue( UiConst.OnOpen ) ); - builder.AddAttribute( "(closed)", _config.GetValue( UiConst.OnClose ) ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/Renders/PanelTitleRender.cs b/src/Util.Ui.Angular/Material/Panels/Renders/PanelTitleRender.cs deleted file mode 100644 index 494957c6c..000000000 --- a/src/Util.Ui.Angular/Material/Panels/Renders/PanelTitleRender.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Panels.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Panels.Renders { - /// - /// 面板标题渲染器 - /// - public class PanelTitleRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化面板标题渲染器 - /// - /// 配置 - public PanelTitleRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new PanelTitleBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigContent( builder ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/TagHelpers/AccordionTagHelper.cs b/src/Util.Ui.Angular/Material/Panels/TagHelpers/AccordionTagHelper.cs deleted file mode 100644 index edbaf6a29..000000000 --- a/src/Util.Ui.Angular/Material/Panels/TagHelpers/AccordionTagHelper.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Panels.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Panels.TagHelpers { - /// - /// 手风琴 - /// - [HtmlTargetElement( "util-accordion" )] - public class AccordionTagHelper : TagHelperBase { - /// - /// 允许多个面板同时处于展开状态 - /// - public bool Multiple { get; set; } - /// - /// 显示模式,设置为Flat则展开的面板间没有间距 - /// - public AccordionDisplayMode DisplayMode { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new AccordionRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/TagHelpers/PanelActionTagHelper.cs b/src/Util.Ui.Angular/Material/Panels/TagHelpers/PanelActionTagHelper.cs deleted file mode 100644 index ced35e76d..000000000 --- a/src/Util.Ui.Angular/Material/Panels/TagHelpers/PanelActionTagHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Panels.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Panels.TagHelpers { - /// - /// 面板操作 - /// - [HtmlTargetElement( "util-panel-action" )] - public class PanelActionTagHelper : TagHelperBase { - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new PanelActionRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/TagHelpers/PanelContentTagHelper.cs b/src/Util.Ui.Angular/Material/Panels/TagHelpers/PanelContentTagHelper.cs deleted file mode 100644 index 86bea0bc5..000000000 --- a/src/Util.Ui.Angular/Material/Panels/TagHelpers/PanelContentTagHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Panels.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Panels.TagHelpers { - /// - /// 面板内容 - /// - [HtmlTargetElement( "util-panel-content" )] - public class PanelContentTagHelper : TagHelperBase { - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new PanelContentRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/TagHelpers/PanelDescriptionTagHelper.cs b/src/Util.Ui.Angular/Material/Panels/TagHelpers/PanelDescriptionTagHelper.cs deleted file mode 100644 index 39c32ae52..000000000 --- a/src/Util.Ui.Angular/Material/Panels/TagHelpers/PanelDescriptionTagHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Panels.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Panels.TagHelpers { - /// - /// 面板描述 - /// - [HtmlTargetElement( "util-panel-description" )] - public class PanelDescriptionTagHelper : TagHelperBase { - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new PanelDescriptionRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/TagHelpers/PanelHeaderTagHelper.cs b/src/Util.Ui.Angular/Material/Panels/TagHelpers/PanelHeaderTagHelper.cs deleted file mode 100644 index f1dc1b34d..000000000 --- a/src/Util.Ui.Angular/Material/Panels/TagHelpers/PanelHeaderTagHelper.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Panels.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Panels.TagHelpers { - /// - /// 面板头部 - /// - [HtmlTargetElement( "util-panel-header" )] - public class PanelHeaderTagHelper : TagHelperBase { - /// - /// 面板折叠时标题的高度 - /// - public double CollapsedHeight { get; set; } - /// - /// 面板展开时标题的高度 - /// - public double ExpandedHeight { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new PanelHeaderRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/TagHelpers/PanelTagHelper.cs b/src/Util.Ui.Angular/Material/Panels/TagHelpers/PanelTagHelper.cs deleted file mode 100644 index 55d097923..000000000 --- a/src/Util.Ui.Angular/Material/Panels/TagHelpers/PanelTagHelper.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Panels.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Panels.TagHelpers { - /// - /// 面板 - /// - [HtmlTargetElement( "util-panel" )] - public class PanelTagHelper : TagHelperBase { - /// - /// 面板打开事件 - /// - public string OnOpen { get; set; } - /// - /// 面板关闭事件 - /// - public string OnClose { get; set; } - /// - /// 隐藏折叠开关 - /// - public bool HideToggle { get; set; } - /// - /// 展开面板 - /// - public string Expanded { get; set; } - /// - /// 禁用面板 - /// - public string Disabled { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new PanelRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Panels/TagHelpers/PanelTitleTagHelper.cs b/src/Util.Ui.Angular/Material/Panels/TagHelpers/PanelTitleTagHelper.cs deleted file mode 100644 index 00b737fb5..000000000 --- a/src/Util.Ui.Angular/Material/Panels/TagHelpers/PanelTitleTagHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Panels.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Panels.TagHelpers { - /// - /// 面板标题 - /// - [HtmlTargetElement( "util-panel-title" )] - public class PanelTitleTagHelper : TagHelperBase { - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new PanelTitleRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/SideNavs/Builders/DrawerBuilder.cs b/src/Util.Ui.Angular/Material/SideNavs/Builders/DrawerBuilder.cs deleted file mode 100644 index edbdbc277..000000000 --- a/src/Util.Ui.Angular/Material/SideNavs/Builders/DrawerBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.SideNavs.Builders { - /// - /// Material侧边栏导航生成器 - /// - public class DrawerBuilder : TagBuilder { - /// - /// 初始化侧边栏导航生成器 - /// - public DrawerBuilder() : base( "mat-drawer" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/SideNavs/Builders/DrawerContainerBuilder.cs b/src/Util.Ui.Angular/Material/SideNavs/Builders/DrawerContainerBuilder.cs deleted file mode 100644 index 2fdadce6d..000000000 --- a/src/Util.Ui.Angular/Material/SideNavs/Builders/DrawerContainerBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.SideNavs.Builders { - /// - /// Material侧边栏导航容器生成器 - /// - public class DrawerContainerBuilder : TagBuilder { - /// - /// 初始化侧边栏导航容器生成器 - /// - public DrawerContainerBuilder() : base( "mat-drawer-container" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/SideNavs/Builders/DrawerContentBuilder.cs b/src/Util.Ui.Angular/Material/SideNavs/Builders/DrawerContentBuilder.cs deleted file mode 100644 index 0e380aae4..000000000 --- a/src/Util.Ui.Angular/Material/SideNavs/Builders/DrawerContentBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.SideNavs.Builders { - /// - /// Material侧边栏导航内容生成器 - /// - public class DrawerContentBuilder : TagBuilder { - /// - /// 初始化侧边栏导航内容生成器 - /// - public DrawerContentBuilder() : base( "mat-drawer-content" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/SideNavs/Builders/SideNavBuilder.cs b/src/Util.Ui.Angular/Material/SideNavs/Builders/SideNavBuilder.cs deleted file mode 100644 index 7f57301c5..000000000 --- a/src/Util.Ui.Angular/Material/SideNavs/Builders/SideNavBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.SideNavs.Builders { - /// - /// Material侧边栏导航生成器 - /// - public class SideNavBuilder : TagBuilder { - /// - /// 初始化侧边栏导航生成器 - /// - public SideNavBuilder() : base( "mat-sidenav" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/SideNavs/Builders/SideNavContainerBuilder.cs b/src/Util.Ui.Angular/Material/SideNavs/Builders/SideNavContainerBuilder.cs deleted file mode 100644 index 6c988a95f..000000000 --- a/src/Util.Ui.Angular/Material/SideNavs/Builders/SideNavContainerBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.SideNavs.Builders { - /// - /// Material侧边栏导航容器生成器 - /// - public class SideNavContainerBuilder : TagBuilder { - /// - /// 初始化侧边栏导航容器生成器 - /// - public SideNavContainerBuilder() : base( "mat-sidenav-container" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/SideNavs/Builders/SideNavContentBuilder.cs b/src/Util.Ui.Angular/Material/SideNavs/Builders/SideNavContentBuilder.cs deleted file mode 100644 index 33d0d140a..000000000 --- a/src/Util.Ui.Angular/Material/SideNavs/Builders/SideNavContentBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.SideNavs.Builders { - /// - /// Material侧边栏导航内容生成器 - /// - public class SideNavContentBuilder : TagBuilder { - /// - /// 初始化侧边栏导航内容生成器 - /// - public SideNavContentBuilder() : base( "mat-sidenav-content" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/SideNavs/Renders/DrawerContainerRender.cs b/src/Util.Ui.Angular/Material/SideNavs/Renders/DrawerContainerRender.cs deleted file mode 100644 index 16b37d161..000000000 --- a/src/Util.Ui.Angular/Material/SideNavs/Renders/DrawerContainerRender.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.SideNavs.Builders; - -namespace Util.Ui.Material.SideNavs.Renders { - /// - /// 侧边栏导航容器渲染器 - /// - public class DrawerContainerRender : SideNavContainerRender { - /// - /// 初始化侧边栏导航容器渲染器 - /// - /// 配置 - public DrawerContainerRender( IConfig config ) : base( config ) { - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new DrawerContainerBuilder(); - Config( builder ); - return builder; - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/SideNavs/Renders/DrawerContentRender.cs b/src/Util.Ui.Angular/Material/SideNavs/Renders/DrawerContentRender.cs deleted file mode 100644 index 67f3d8b56..000000000 --- a/src/Util.Ui.Angular/Material/SideNavs/Renders/DrawerContentRender.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.SideNavs.Builders; - -namespace Util.Ui.Material.SideNavs.Renders { - /// - /// 侧边栏导航内容渲染器 - /// - public class DrawerContentRender : SideNavContentRender { - /// - /// 初始化侧边栏导航内容渲染器 - /// - /// 配置 - public DrawerContentRender( IConfig config ) : base( config ) { - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new DrawerContentBuilder(); - Config( builder ); - return builder; - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/SideNavs/Renders/DrawerRender.cs b/src/Util.Ui.Angular/Material/SideNavs/Renders/DrawerRender.cs deleted file mode 100644 index c667ed597..000000000 --- a/src/Util.Ui.Angular/Material/SideNavs/Renders/DrawerRender.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.SideNavs.Builders; - -namespace Util.Ui.Material.SideNavs.Renders { - /// - /// 侧边栏导航渲染器 - /// - public class DrawerRender : SideNavRender { - /// - /// 初始化侧边栏导航渲染器 - /// - /// 配置 - public DrawerRender( IConfig config ) : base( config ) { - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new DrawerBuilder(); - Config( builder ); - return builder; - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/SideNavs/Renders/SideNavContainerRender.cs b/src/Util.Ui.Angular/Material/SideNavs/Renders/SideNavContainerRender.cs deleted file mode 100644 index 032622cfb..000000000 --- a/src/Util.Ui.Angular/Material/SideNavs/Renders/SideNavContainerRender.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Extensions; -using Util.Ui.Material.SideNavs.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.SideNavs.Renders { - /// - /// 侧边栏导航容器渲染器 - /// - public class SideNavContainerRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化侧边栏导航容器渲染器 - /// - /// 配置 - public SideNavContainerRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new SideNavContainerBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - builder.Class( _config ); - ConfigId( builder ); - ConfigFullscreen( builder ); - ConfigAutoSize( builder ); - ConfigContent( builder ); - } - - /// - /// 配置全屏 - /// - private void ConfigFullscreen( TagBuilder builder ) { - if ( _config.GetValue( UiConst.Fullscreen ) ) - builder.AddAttribute( UiConst.Fullscreen ); - } - - /// - /// 配置自动调整大小 - /// - private void ConfigAutoSize( TagBuilder builder ) { - builder.AddAttribute( "autosize", _config.GetBoolValue( UiConst.AutoSize ) ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/SideNavs/Renders/SideNavContentRender.cs b/src/Util.Ui.Angular/Material/SideNavs/Renders/SideNavContentRender.cs deleted file mode 100644 index deb35dcc3..000000000 --- a/src/Util.Ui.Angular/Material/SideNavs/Renders/SideNavContentRender.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Extensions; -using Util.Ui.Material.SideNavs.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.SideNavs.Renders { - /// - /// 侧边栏导航内容渲染器 - /// - public class SideNavContentRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化侧边栏导航内容渲染器 - /// - /// 配置 - public SideNavContentRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new SideNavContentBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - builder.Class( _config ); - ConfigId( builder ); - ConfigContent( builder ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/SideNavs/Renders/SideNavRender.cs b/src/Util.Ui.Angular/Material/SideNavs/Renders/SideNavRender.cs deleted file mode 100644 index 87a85d71a..000000000 --- a/src/Util.Ui.Angular/Material/SideNavs/Renders/SideNavRender.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Extensions; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.SideNavs.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.SideNavs.Renders { - /// - /// 侧边栏导航渲染器 - /// - public class SideNavRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化侧边栏导航渲染器 - /// - /// 配置 - public SideNavRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new SideNavBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigStyle( builder ); - ConfigPosition( builder ); - ConfigOpened( builder ); - ConfigMode( builder ); - ConfigDisableClose( builder ); - ConfigContent( builder ); - } - - /// - /// 配置样式 - /// - private void ConfigStyle( TagBuilder builder ) { - builder.Class( _config ); - if( _config.Contains( UiConst.Width ) ) - builder.AddAttribute( UiConst.Style, $"width:{_config.GetValue( UiConst.Width )}px" ); - } - - /// - /// 配置位置 - /// - private void ConfigPosition( TagBuilder builder ) { - if ( _config.Contains( UiConst.Position ) == false ) - return; - builder.AddAttribute( UiConst.Position, _config.GetValue( UiConst.Position ) == XPosition.Left ? "start": "end" ); - } - - /// - /// 配置是否打开 - /// - private void ConfigOpened( TagBuilder builder ) { - builder.AddAttribute( UiConst.Opened, _config.GetBoolValue( UiConst.Opened ) ); - } - - /// - /// 配置打开模式 - /// - private void ConfigMode( TagBuilder builder ) { - builder.AddAttribute( UiConst.Mode, _config.GetValue( UiConst.Mode )?.ToLower() ); - } - - /// - /// 配置禁用关闭 - /// - private void ConfigDisableClose( TagBuilder builder ) { - builder.AddAttribute( "disableClose", _config.GetBoolValue( MaterialConst.DisableClose ) ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/SideNavs/TagHelpers/DrawerContainerTagHelper.cs b/src/Util.Ui.Angular/Material/SideNavs/TagHelpers/DrawerContainerTagHelper.cs deleted file mode 100644 index 2abc1130d..000000000 --- a/src/Util.Ui.Angular/Material/SideNavs/TagHelpers/DrawerContainerTagHelper.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.SideNavs.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.SideNavs.TagHelpers { - /// - /// 侧边栏导航容器 - /// - [HtmlTargetElement( "util-drawer-container" )] - public class DrawerContainerTagHelper : TagHelperBase { - /// - /// 自动调整大小 - /// - public bool AutoSize { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new DrawerContainerRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/SideNavs/TagHelpers/DrawerContentTagHelper.cs b/src/Util.Ui.Angular/Material/SideNavs/TagHelpers/DrawerContentTagHelper.cs deleted file mode 100644 index f8433e580..000000000 --- a/src/Util.Ui.Angular/Material/SideNavs/TagHelpers/DrawerContentTagHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.SideNavs.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.SideNavs.TagHelpers { - /// - /// 侧边栏内容区域 - /// - [HtmlTargetElement( "util-drawer-content" )] - public class DrawerContentTagHelper : TagHelperBase { - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new DrawerContentRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/SideNavs/TagHelpers/DrawerTagHelper.cs b/src/Util.Ui.Angular/Material/SideNavs/TagHelpers/DrawerTagHelper.cs deleted file mode 100644 index 597638dc4..000000000 --- a/src/Util.Ui.Angular/Material/SideNavs/TagHelpers/DrawerTagHelper.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.SideNavs.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.SideNavs.TagHelpers { - /// - /// 侧边栏导航区域 - /// - [HtmlTargetElement( "util-drawer" )] - public class DrawerTagHelper : TagHelperBase { - /// - /// 位置 - /// - public XPosition Position { get; set; } - /// - /// 是否打开 - /// - public bool Opened { get; set; } - /// - /// 显示模式 - /// - public SideNavMode Mode { get; set; } - /// - /// 禁用ESC键或点击背景关闭 - /// - public bool DisableClose { get; set; } - /// - /// 宽度,单位:px - /// - public double Width { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new DrawerRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/SideNavs/TagHelpers/SideNavContainerTagHelper.cs b/src/Util.Ui.Angular/Material/SideNavs/TagHelpers/SideNavContainerTagHelper.cs deleted file mode 100644 index 9a27741f1..000000000 --- a/src/Util.Ui.Angular/Material/SideNavs/TagHelpers/SideNavContainerTagHelper.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.SideNavs.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.SideNavs.TagHelpers { - /// - /// 侧边栏导航容器 - /// - [HtmlTargetElement( "util-sidenav-container" )] - public class SideNavContainerTagHelper : TagHelperBase { - /// - /// 全屏 - /// - public bool Fullscreen { get; set; } - /// - /// 自动调整大小 - /// - public bool AutoSize { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new SideNavContainerRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/SideNavs/TagHelpers/SideNavContentTagHelper.cs b/src/Util.Ui.Angular/Material/SideNavs/TagHelpers/SideNavContentTagHelper.cs deleted file mode 100644 index b032b1eab..000000000 --- a/src/Util.Ui.Angular/Material/SideNavs/TagHelpers/SideNavContentTagHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.SideNavs.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.SideNavs.TagHelpers { - /// - /// 侧边栏内容区域 - /// - [HtmlTargetElement( "util-sidenav-content" )] - public class SideNavContentTagHelper : TagHelperBase { - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new SideNavContentRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/SideNavs/TagHelpers/SideNavTagHelper.cs b/src/Util.Ui.Angular/Material/SideNavs/TagHelpers/SideNavTagHelper.cs deleted file mode 100644 index d30edddd8..000000000 --- a/src/Util.Ui.Angular/Material/SideNavs/TagHelpers/SideNavTagHelper.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.SideNavs.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.SideNavs.TagHelpers { - /// - /// 侧边栏导航区域 - /// - [HtmlTargetElement( "util-sidenav" )] - public class SideNavTagHelper : TagHelperBase { - /// - /// 位置 - /// - public XPosition Position { get; set; } - /// - /// 是否打开 - /// - public bool Opened { get; set; } - /// - /// 显示模式 - /// - public SideNavMode Mode { get; set; } - /// - /// 禁用ESC键或点击背景关闭 - /// - public bool DisableClose { get; set; } - /// - /// 宽度,单位:px - /// - public double Width { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new SideNavRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/Builders/TabBuilder.cs b/src/Util.Ui.Angular/Material/Tabs/Builders/TabBuilder.cs deleted file mode 100644 index ebe03eb53..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/Builders/TabBuilder.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Util.Ui.Angular.Builders; -using Util.Ui.Builders; - -namespace Util.Ui.Material.Tabs.Builders { - /// - /// Material选项卡生成器 - /// - public class TabBuilder : TagBuilder { - /// - /// 初始化选项卡生成器 - /// - public TabBuilder() : base( "mat-tab" ) { - HeaderTemplateBuilder = new TemplateBuilder(); - HeaderTemplateBuilder.AddAttribute( "mat-tab-label" ); - ContenTemplateBuilder = new TemplateBuilder(); - ContenTemplateBuilder.AddAttribute( "matTabContent" ); - } - - /// - /// 标题模板生成器 - /// - public TemplateBuilder HeaderTemplateBuilder { get; set; } - - /// - /// 内容模板生成器 - /// - public TemplateBuilder ContenTemplateBuilder { get; set; } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/Builders/TabGroupBuilder.cs b/src/Util.Ui.Angular/Material/Tabs/Builders/TabGroupBuilder.cs deleted file mode 100644 index ba100331f..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/Builders/TabGroupBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Tabs.Builders { - /// - /// Material选项卡组生成器 - /// - public class TabGroupBuilder : TagBuilder { - /// - /// 初始化选项卡组生成器 - /// - public TabGroupBuilder() : base( "mat-tab-group" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/Builders/TabLinkBuilder.cs b/src/Util.Ui.Angular/Material/Tabs/Builders/TabLinkBuilder.cs deleted file mode 100644 index 49c58380f..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/Builders/TabLinkBuilder.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Util.Helpers; -using Util.Ui.Builders; -using Util.Ui.Configs; - -namespace Util.Ui.Material.Tabs.Builders { - /// - /// Material链接选项卡生成器 - /// - public class TabLinkBuilder : TagBuilder { - /// - /// 初始化链接选项卡生成器 - /// - public TabLinkBuilder() : base( "a" ) { - AddAttribute( "mat-tab-link" ).AddAttribute( "routerLinkActive" ); - } - - /// - /// 配置路由链接 - /// - /// 配置 - public void ConfigRouterLink( IConfig config ) { - var id = config.GetValue( UiConst.Id ); - if( string.IsNullOrWhiteSpace( id ) ) - id = $"{Id.Guid()}"; - id = $"m_{id}"; - AddAttribute( $"#{id}", "routerLinkActive" ); - AddAttribute( "[active]", $"{id}.isActive" ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/Builders/TabNavigationBuilder.cs b/src/Util.Ui.Angular/Material/Tabs/Builders/TabNavigationBuilder.cs deleted file mode 100644 index b9074ca90..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/Builders/TabNavigationBuilder.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Tabs.Builders { - /// - /// Material导航选项卡生成器 - /// - public class TabNavigationBuilder : TagBuilder { - /// - /// 初始化导航选项卡生成器 - /// - public TabNavigationBuilder() : base( "nav" ) { - AddAttribute( "mat-tab-nav-bar" ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/ITab.cs b/src/Util.Ui.Angular/Material/Tabs/ITab.cs deleted file mode 100644 index 5ecae90c3..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/ITab.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using Util.Ui.Components; -using Util.Ui.Operations; - -namespace Util.Ui.Material.Tabs { - /// - /// 选项卡 - /// - public interface ITab : IContainer, ILabel, ISetIcon,IDisabled { - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/ITabGroup.cs b/src/Util.Ui.Angular/Material/Tabs/ITabGroup.cs deleted file mode 100644 index 4e059329c..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/ITabGroup.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Material.Tabs.Wrappers; -using Util.Ui.Operations; -using Util.Ui.Operations.Styles; - -namespace Util.Ui.Material.Tabs { - /// - /// 选项卡组 - /// - public interface ITabGroup:IContainer,IColor,IBackgroundColor, IHeight { - } -} diff --git a/src/Util.Ui.Angular/Material/Tabs/ITabLink.cs b/src/Util.Ui.Angular/Material/Tabs/ITabLink.cs deleted file mode 100644 index 9d0056b9b..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/ITabLink.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Operations; -using Util.Ui.Operations.Navigation; - -namespace Util.Ui.Material.Tabs { - /// - /// 链接选项卡 - /// - public interface ITabLink : IComponent,ILink, ILabel, ISetIcon, IDisabled { - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/ITabNavigation.cs b/src/Util.Ui.Angular/Material/Tabs/ITabNavigation.cs deleted file mode 100644 index a140d1055..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/ITabNavigation.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Material.Tabs.Wrappers; -using Util.Ui.Operations.Styles; - -namespace Util.Ui.Material.Tabs { - /// - /// 导航选项卡 - /// - public interface ITabNavigation : IContainer, IColor, IBackgroundColor { - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/Renders/TabGroupRender.cs b/src/Util.Ui.Angular/Material/Tabs/Renders/TabGroupRender.cs deleted file mode 100644 index 5f778c71e..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/Renders/TabGroupRender.cs +++ /dev/null @@ -1,97 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Tabs.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Tabs.Renders { - /// - /// 选项卡组渲染器 - /// - public class TabGroupRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化选项卡组渲染器 - /// - /// 配置 - public TabGroupRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new TabGroupBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigColor( builder ); - ConfigHeight( builder ); - ConfigStretch( builder ); - ConfigSelectedIndex( builder ); - ConfigHeaderPosition( builder ); - ConfigContent( builder ); - } - - /// - /// 配置颜色 - /// - private void ConfigColor( TagBuilder builder ) { - builder.AddAttribute( "backgroundColor", _config.GetValue( UiConst.BackgroundColor )?.ToLower() ); - builder.AddAttribute( "color", _config.GetValue( UiConst.Color )?.ToLower() ); - } - - /// - /// 配置高度 - /// - private void ConfigHeight( TagBuilder builder ) { - if( _config.Contains( UiConst.Height ) ) - builder.AddAttribute( "style", $"height:{_config.GetValue( UiConst.Height )}px" ); - if ( _config.GetValue( MaterialConst.DynamicHeight ) ) - builder.AddAttribute( "dynamicHeight" ); - } - - /// - /// 配置拉伸选项卡 - /// - private void ConfigStretch( TagBuilder builder ) { - if ( _config.GetValue( UiConst.Stretch ) ) - builder.AddAttribute( "mat-stretch-tabs" ); - } - - /// - /// 配置选中索引 - /// - private void ConfigSelectedIndex( TagBuilder builder ) { - builder.AddAttribute( "[(selectedIndex)]", _config.GetValue( UiConst.SelectedIndex ) ); - } - - /// - /// 配置标题位置 - /// - private void ConfigHeaderPosition( TagBuilder builder ) { - builder.AddAttribute( "headerPosition", _config.GetValue( MaterialConst.HeaderPosition )?.Description() ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if ( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/Renders/TabLinkRender.cs b/src/Util.Ui.Angular/Material/Tabs/Renders/TabLinkRender.cs deleted file mode 100644 index 1fcd9c930..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/Renders/TabLinkRender.cs +++ /dev/null @@ -1,99 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Icons.Builders; -using Util.Ui.Material.Tabs.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Tabs.Renders { - /// - /// 链接选项卡渲染器 - /// - public class TabLinkRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化链接选项卡渲染器 - /// - /// 配置 - public TabLinkRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new TabLinkBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TabLinkBuilder builder ) { - ConfigId( builder ); - builder.ConfigRouterLink( _config ); - ConfigLink( builder ); - ConfigCaption( builder ); - ConfigDisabled( builder ); - } - - /// - /// 配置路由链接 - /// - private void ConfigLink( TagBuilder builder ) { - builder.AddAttribute( "routerLink", _config.GetValue( UiConst.Link ) ); - } - - /// - /// 配置标题 - /// - private void ConfigCaption( TagBuilder builder ) { - ConfigMaterialIcon( builder ); - ConfigFontAwesomeIcon( builder ); - ConfigLabel( builder ); - } - - /// - /// 配置Material图标 - /// - private void ConfigMaterialIcon( TagBuilder builder ) { - if( _config.Contains( UiConst.MaterialIcon ) == false ) - return; - var iconBuilder = new MaterialIconBuilder(); - iconBuilder.SetIcon( _config ); - builder.AppendContent( iconBuilder ); - } - - /// - /// 配置FontAwesome图标 - /// - private void ConfigFontAwesomeIcon( TagBuilder builder ) { - if( _config.Contains( UiConst.FontAwesomeIcon ) == false ) - return; - var iconBuilder = new FontAwesomeIconBuilder(); - iconBuilder.SetIcon( _config ); - builder.AppendContent( iconBuilder ); - } - - /// - /// 配置标签 - /// - private void ConfigLabel( TagBuilder builder ) { - if( _config.Contains( UiConst.Label ) == false ) - return; - builder.AppendContent( _config.GetValue( UiConst.Label ) ); - } - - /// - /// 配置禁用 - /// - private void ConfigDisabled( TagBuilder builder ) { - builder.AddAttribute( "[disabled]", _config.GetBoolValue( UiConst.Disabled ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/Renders/TabNavigationRender.cs b/src/Util.Ui.Angular/Material/Tabs/Renders/TabNavigationRender.cs deleted file mode 100644 index e6a5998cf..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/Renders/TabNavigationRender.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System.IO; -using System.Text.Encodings.Web; -using Util.Ui.Angular.Builders; -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Tabs.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Tabs.Renders { - /// - /// 导航选项卡渲染器 - /// - public class TabNavigationRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - /// - /// router-outlet生成器 - /// - private readonly RouterOutletBuilder _routerOutletBuilder; - - /// - /// 初始化导航选项卡渲染器 - /// - /// 配置 - public TabNavigationRender( IConfig config ) : base( config ) { - _config = config; - _routerOutletBuilder = new RouterOutletBuilder(); - } - - /// - /// 渲染 - /// - /// 流写入器 - /// 编码 - public override void WriteTo( TextWriter writer, HtmlEncoder encoder ) { - Builder.WriteTo( writer, encoder ); - _routerOutletBuilder.WriteTo( writer, encoder ); - } - - /// - /// 渲染结束标签 - /// - /// 流写入器 - public override void RenderEndTag( TextWriter writer ) { - Builder.RenderEndTag( writer ); - _routerOutletBuilder.WriteTo( writer, HtmlEncoder.Default ); - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new TabNavigationBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigColor( builder ); - ConfigContent( builder ); - } - - /// - /// 配置颜色 - /// - private void ConfigColor( TagBuilder builder ) { - builder.AddAttribute( "backgroundColor", _config.GetValue( UiConst.BackgroundColor )?.ToLower() ); - builder.AddAttribute( "color", _config.GetValue( UiConst.Color )?.ToLower() ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/Renders/TabRender.cs b/src/Util.Ui.Angular/Material/Tabs/Renders/TabRender.cs deleted file mode 100644 index d25fa279a..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/Renders/TabRender.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System.IO; -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Icons.Builders; -using Util.Ui.Material.Tabs.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Tabs.Renders { - /// - /// 选项卡渲染器 - /// - public class TabRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化选项卡渲染器 - /// - /// 配置 - public TabRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 渲染起始标签 - /// - /// 流写入器 - public override void RenderStartTag( TextWriter writer ) { - var builder = (TabBuilder)Builder; - builder.RenderStartTag( writer ); - if( Builder.HasInnerHtml ) - Builder.RenderBody( writer ); - if( _config.GetValue( UiConst.LazyLoad ) ) - builder.ContenTemplateBuilder.RenderStartTag( writer ); - } - - /// - /// 渲染结束标签 - /// - /// 流写入器 - public override void RenderEndTag( TextWriter writer ) { - var builder = (TabBuilder)Builder; - if( _config.GetValue( UiConst.LazyLoad ) ) - builder.ContenTemplateBuilder.RenderEndTag( writer ); - builder.RenderEndTag( writer ); - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new TabBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TabBuilder builder ) { - ConfigId( builder ); - ConfigCaption( builder ); - ConfigDisabled( builder ); - ConfigContent( builder ); - } - - /// - /// 配置标题 - /// - private void ConfigCaption( TabBuilder builder ) { - ConfigMaterialIcon( builder.HeaderTemplateBuilder ); - ConfigFontAwesomeIcon( builder.HeaderTemplateBuilder ); - ConfigLabel( builder.HeaderTemplateBuilder ); - builder.AppendContent( builder.HeaderTemplateBuilder ); - } - - /// - /// 配置Material图标 - /// - private void ConfigMaterialIcon( TagBuilder builder ) { - if( _config.Contains( UiConst.MaterialIcon ) == false ) - return; - var iconBuilder = new MaterialIconBuilder(); - iconBuilder.SetIcon( _config ); - builder.AppendContent( iconBuilder ); - } - - /// - /// 配置FontAwesome图标 - /// - private void ConfigFontAwesomeIcon( TagBuilder builder ) { - if( _config.Contains( UiConst.FontAwesomeIcon ) == false ) - return; - var iconBuilder = new FontAwesomeIconBuilder(); - iconBuilder.SetIcon( _config ); - builder.AppendContent( iconBuilder ); - } - - /// - /// 配置标签 - /// - private void ConfigLabel( TagBuilder builder ) { - if( _config.Contains( UiConst.Label ) == false ) - return; - builder.AppendContent( _config.GetValue( UiConst.Label ) ); - } - - /// - /// 配置禁用 - /// - private void ConfigDisabled( TagBuilder builder ) { - builder.AddAttribute( "[disabled]", _config.GetBoolValue( UiConst.Disabled ) ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TabBuilder builder ) { - if( _config.Content == null ) - return; - if ( _config.GetValue( UiConst.LazyLoad ) == false ) { - builder.AppendContent( _config.Content ); - return; - } - builder.ContenTemplateBuilder.AppendContent( _config.Content ); - builder.AppendContent( builder.ContenTemplateBuilder ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/Tab.cs b/src/Util.Ui.Angular/Material/Tabs/Tab.cs deleted file mode 100644 index 5d4eb42be..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/Tab.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.IO; -using Util.Ui.Components; -using Util.Ui.Material.Tabs.Renders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Tabs { - /// - /// 选项卡 - /// - public class Tab : ContainerBase, ITab { - /// - /// 初始化选项卡 - /// - /// 流写入器 - public Tab( TextWriter writer ) : base( writer ) { - } - - /// - /// 获取渲染器 - /// - protected override IRender GetRender() { - return new TabRender( OptionConfig ); - } - - /// - /// 获取容器包装器 - /// - protected override IDisposable GetWrapper() { - return new ContainerWrapper( this ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/TabGroup.cs b/src/Util.Ui.Angular/Material/Tabs/TabGroup.cs deleted file mode 100644 index 7feec6e85..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/TabGroup.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.IO; -using Util.Ui.Components; -using Util.Ui.Material.Tabs.Renders; -using Util.Ui.Material.Tabs.Wrappers; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Tabs { - /// - /// 选项卡组 - /// - public class TabGroup : ContainerBase, ITabGroup { - /// - /// 初始化选项卡组 - /// - /// 流写入器 - public TabGroup( TextWriter writer ) : base( writer ) { - } - - /// - /// 获取渲染器 - /// - protected override IRender GetRender() { - return new TabGroupRender( OptionConfig ); - } - - /// - /// 获取容器包装器 - /// - protected override ITabGroupWrapper GetWrapper() { - return new TabGroupWrapper( this ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/TabLink.cs b/src/Util.Ui.Angular/Material/Tabs/TabLink.cs deleted file mode 100644 index e3315bb9a..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/TabLink.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Material.Tabs.Renders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Tabs { - /// - /// 链接选项卡 - /// - public class TabLink : ComponentBase, ITabLink { - /// - /// 获取渲染器 - /// - protected override IRender GetRender() { - return new TabLinkRender( OptionConfig ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/TabNavigation.cs b/src/Util.Ui.Angular/Material/Tabs/TabNavigation.cs deleted file mode 100644 index 02073edc0..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/TabNavigation.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.IO; -using Util.Ui.Components; -using Util.Ui.Material.Tabs.Renders; -using Util.Ui.Material.Tabs.Wrappers; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Tabs { - /// - /// 导航选项卡 - /// - public class TabNavigation : ContainerBase, ITabNavigation { - /// - /// 初始化导航选项卡 - /// - /// 流写入器 - public TabNavigation( TextWriter writer ) : base( writer ) { - } - - /// - /// 获取渲染器 - /// - protected override IRender GetRender() { - return new TabNavigationRender( OptionConfig ); - } - - /// - /// 获取容器包装器 - /// - protected override ITabNavigationWrapper GetWrapper() { - return new TabNavigationWrapper( this ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/TagHelpers/TabGroupTagHelper.cs b/src/Util.Ui.Angular/Material/Tabs/TagHelpers/TabGroupTagHelper.cs deleted file mode 100644 index 3a5af5630..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/TagHelpers/TabGroupTagHelper.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Tabs.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Tabs.TagHelpers { - /// - /// 选项卡组 - /// - [HtmlTargetElement( "util-tabs" )] - public class TabGroupTagHelper : TagHelperBase { - /// - /// 背景色 - /// - public Color BackgroundColor { get; set; } - /// - /// 主题色 - /// - public Color Color { get; set; } - /// - /// 固定高度,单位:px - /// - public double Height { get; set; } - /// - /// 动态高度 - /// - public bool DynamicHeight { get; set; } - /// - /// 拉伸选项卡 - /// - public bool Stretch { get; set; } - /// - /// 索引表达式 - /// - public string SelectedIndex { get; set; } - /// - /// 选项卡位置 - /// - public YPosition HeaderPosition { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new TabGroupRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/TagHelpers/TabLinkTagHelper.cs b/src/Util.Ui.Angular/Material/Tabs/TagHelpers/TabLinkTagHelper.cs deleted file mode 100644 index f9fde803a..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/TagHelpers/TabLinkTagHelper.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Material.Tabs.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Tabs.TagHelpers { - /// - /// 导航选项卡 - /// - [HtmlTargetElement( "util-nav-tab" )] - public class TabLinkTagHelper : TagHelperBase { - /// - /// 路由链接 - /// - public string Link { get; set; } - /// - /// 标签 - /// - public string Label { get; set; } - /// - /// Material图标 - /// - public MaterialIcon MaterialIcon { get; set; } - /// - /// Font Awesome图标 - /// - public FontAwesomeIcon FontAwesomeIcon { get; set; } - /// - /// 禁用 - /// - public string Disabled { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new TabLinkRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/TagHelpers/TabNavigationTagHelper.cs b/src/Util.Ui.Angular/Material/Tabs/TagHelpers/TabNavigationTagHelper.cs deleted file mode 100644 index 26b188c86..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/TagHelpers/TabNavigationTagHelper.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Tabs.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Tabs.TagHelpers { - /// - /// 导航选项卡组 - /// - [HtmlTargetElement( "util-nav-tabs" )] - public class TabNavigationTagHelper : TagHelperBase { - /// - /// 背景色 - /// - public Color BackgroundColor { get; set; } - /// - /// 主题色 - /// - public Color Color { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new TabNavigationRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/TagHelpers/TabTagHelper.cs b/src/Util.Ui.Angular/Material/Tabs/TagHelpers/TabTagHelper.cs deleted file mode 100644 index b23dca490..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/TagHelpers/TabTagHelper.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Material.Tabs.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Tabs.TagHelpers { - /// - /// 选项卡 - /// - [HtmlTargetElement( "util-tab" )] - public class TabTagHelper : TagHelperBase { - /// - /// 标签 - /// - public string Label { get; set; } - /// - /// Material图标 - /// - public MaterialIcon MaterialIcon { get; set; } - /// - /// Font Awesome图标 - /// - public FontAwesomeIcon FontAwesomeIcon { get; set; } - /// - /// 禁用 - /// - public string Disabled { get; set; } - /// - /// 延迟加载 - /// - public bool LazyLoad { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new TabRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/Wrappers/ITabGroupWrapper.cs b/src/Util.Ui.Angular/Material/Tabs/Wrappers/ITabGroupWrapper.cs deleted file mode 100644 index 86f4a9181..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/Wrappers/ITabGroupWrapper.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace Util.Ui.Material.Tabs.Wrappers { - /// - /// 选项卡组包装器 - /// - public interface ITabGroupWrapper : IDisposable { - /// - /// 选项卡 - /// - ITab Tab(); - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/Wrappers/ITabNavigationWrapper.cs b/src/Util.Ui.Angular/Material/Tabs/Wrappers/ITabNavigationWrapper.cs deleted file mode 100644 index 6e6b59841..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/Wrappers/ITabNavigationWrapper.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace Util.Ui.Material.Tabs.Wrappers { - /// - /// 导航选项卡包装器 - /// - public interface ITabNavigationWrapper : IDisposable { - /// - /// 选项卡 - /// - ITabLink Tab(); - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/Wrappers/TabGroupWrapper.cs b/src/Util.Ui.Angular/Material/Tabs/Wrappers/TabGroupWrapper.cs deleted file mode 100644 index f0313adfd..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/Wrappers/TabGroupWrapper.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Util.Ui.Components.Internal; - -namespace Util.Ui.Material.Tabs.Wrappers { - /// - /// 选项卡组包装器 - /// - public class TabGroupWrapper : ITabGroupWrapper { - /// - /// 选项卡组 - /// - private readonly TabGroup _container; - - /// - /// 初始化选项卡组包装器 - /// - /// 选项卡组 - public TabGroupWrapper( TabGroup tabs ) { - _container = tabs; - } - - /// - /// 渲染结束 - /// - public void Dispose() { - if( _container is IRenderEnd container ) - container.End(); - } - - /// - /// 选项卡 - /// - public ITab Tab() { - return new Tab( _container.Writer ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Tabs/Wrappers/TabNavigationWrapper.cs b/src/Util.Ui.Angular/Material/Tabs/Wrappers/TabNavigationWrapper.cs deleted file mode 100644 index 870780616..000000000 --- a/src/Util.Ui.Angular/Material/Tabs/Wrappers/TabNavigationWrapper.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Util.Ui.Components.Internal; - -namespace Util.Ui.Material.Tabs.Wrappers { - /// - /// 导航选项卡包装器 - /// - public class TabNavigationWrapper : ITabNavigationWrapper { - /// - /// 导航选项卡 - /// - private readonly TabNavigation _container; - - /// - /// 初始化导航选项卡包装器 - /// - /// 导航选项卡 - public TabNavigationWrapper( TabNavigation tabs ) { - _container = tabs; - } - - /// - /// 渲染结束 - /// - public void Dispose() { - if( _container is IRenderEnd container ) - container.End(); - } - - /// - /// 选项卡 - /// - public ITabLink Tab() { - return new TabLink(); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Toolbars/Builders/ToolbarBuilder.cs b/src/Util.Ui.Angular/Material/Toolbars/Builders/ToolbarBuilder.cs deleted file mode 100644 index e9a92eff1..000000000 --- a/src/Util.Ui.Angular/Material/Toolbars/Builders/ToolbarBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Toolbars.Builders { - /// - /// Mat工具栏生成器 - /// - public class ToolbarBuilder : TagBuilder { - /// - /// 初始化工具栏生成器 - /// - public ToolbarBuilder() : base( "mat-toolbar" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Toolbars/Builders/ToolbarFillRemainBuilder.cs b/src/Util.Ui.Angular/Material/Toolbars/Builders/ToolbarFillRemainBuilder.cs deleted file mode 100644 index bccc8b4bc..000000000 --- a/src/Util.Ui.Angular/Material/Toolbars/Builders/ToolbarFillRemainBuilder.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Toolbars.Builders { - /// - /// Mat工具栏填充项生成器 - /// - public class ToolbarFillRemainBuilder : TagBuilder { - /// - /// 初始化工具栏填充项生成器 - /// - public ToolbarFillRemainBuilder() : base( "span" ) { - Class( "toolbar-fill-remain" ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Toolbars/Builders/ToolbarRowBuilder.cs b/src/Util.Ui.Angular/Material/Toolbars/Builders/ToolbarRowBuilder.cs deleted file mode 100644 index 07e0ee17e..000000000 --- a/src/Util.Ui.Angular/Material/Toolbars/Builders/ToolbarRowBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Util.Ui.Builders; - -namespace Util.Ui.Material.Toolbars.Builders { - /// - /// Mat工具栏项生成器 - /// - public class ToolbarRowBuilder : TagBuilder { - /// - /// 初始化工具栏项生成器 - /// - public ToolbarRowBuilder() : base( "mat-toolbar-row" ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Toolbars/Renders/ToolbarFillRemainRender.cs b/src/Util.Ui.Angular/Material/Toolbars/Renders/ToolbarFillRemainRender.cs deleted file mode 100644 index 42713522e..000000000 --- a/src/Util.Ui.Angular/Material/Toolbars/Renders/ToolbarFillRemainRender.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Toolbars.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Toolbars.Renders { - /// - /// 工具栏填充项渲染器 - /// - public class ToolbarFillRemainRender : RenderBase { - /// - /// 初始化工具栏填充项渲染器 - /// - /// 配置 - public ToolbarFillRemainRender( IConfig config ) : base( config ) { - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - return new ToolbarFillRemainBuilder(); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Toolbars/Renders/ToolbarRender.cs b/src/Util.Ui.Angular/Material/Toolbars/Renders/ToolbarRender.cs deleted file mode 100644 index 5f7ccf5f6..000000000 --- a/src/Util.Ui.Angular/Material/Toolbars/Renders/ToolbarRender.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Extensions; -using Util.Ui.Material.Toolbars.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Toolbars.Renders { - /// - /// 工具栏渲染器 - /// - public class ToolbarRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化工具栏渲染器 - /// - /// 配置 - public ToolbarRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new ToolbarBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - builder.Style( _config ); - ConfigId( builder ); - ConfigColor( builder ); - ConfigContent( builder ); - } - - /// - /// 配置颜色 - /// - private void ConfigColor( TagBuilder builder ) { - builder.AddAttribute( "color", _config.GetValue( UiConst.Color )?.ToLower() ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Toolbars/Renders/ToolbarRowRender.cs b/src/Util.Ui.Angular/Material/Toolbars/Renders/ToolbarRowRender.cs deleted file mode 100644 index b4d77197e..000000000 --- a/src/Util.Ui.Angular/Material/Toolbars/Renders/ToolbarRowRender.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; -using Util.Ui.Material.Toolbars.Builders; -using Util.Ui.Renders; - -namespace Util.Ui.Material.Toolbars.Renders { - /// - /// 工具栏项渲染器 - /// - public class ToolbarRowRender : RenderBase { - /// - /// 配置 - /// - private readonly IConfig _config; - - /// - /// 初始化工具栏项渲染器 - /// - /// 配置 - public ToolbarRowRender( IConfig config ) : base( config ) { - _config = config; - } - - /// - /// 获取标签生成器 - /// - protected override TagBuilder GetTagBuilder() { - var builder = new ToolbarRowBuilder(); - Config( builder ); - return builder; - } - - /// - /// 配置 - /// - protected void Config( TagBuilder builder ) { - ConfigId( builder ); - ConfigContent( builder ); - } - - /// - /// 配置内容 - /// - private void ConfigContent( TagBuilder builder ) { - if( _config.Content == null ) - return; - builder.SetContent( _config.Content ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Toolbars/TagHelpers/ToolbarFillRemainTagHelper.cs b/src/Util.Ui.Angular/Material/Toolbars/TagHelpers/ToolbarFillRemainTagHelper.cs deleted file mode 100644 index d28682e6a..000000000 --- a/src/Util.Ui.Angular/Material/Toolbars/TagHelpers/ToolbarFillRemainTagHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Toolbars.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Toolbars.TagHelpers { - /// - /// 工具栏填充项 - /// - [HtmlTargetElement( "util-toolbar-fill-remain",TagStructure = TagStructure.WithoutEndTag)] - public class ToolbarFillRemainTagHelper : TagHelperBase { - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new ToolbarFillRemainRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Toolbars/TagHelpers/ToolbarRowTagHelper.cs b/src/Util.Ui.Angular/Material/Toolbars/TagHelpers/ToolbarRowTagHelper.cs deleted file mode 100644 index 94b70f873..000000000 --- a/src/Util.Ui.Angular/Material/Toolbars/TagHelpers/ToolbarRowTagHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Toolbars.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Toolbars.TagHelpers { - /// - /// 工具栏项 - /// - [HtmlTargetElement( "util-toolbar-row" )] - public class ToolbarRowTagHelper : TagHelperBase { - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new ToolbarRowRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Material/Toolbars/TagHelpers/ToolbarTagHelper.cs b/src/Util.Ui.Angular/Material/Toolbars/TagHelpers/ToolbarTagHelper.cs deleted file mode 100644 index 87dd8370e..000000000 --- a/src/Util.Ui.Angular/Material/Toolbars/TagHelpers/ToolbarTagHelper.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Toolbars.Renders; -using Util.Ui.Renders; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Material.Toolbars.TagHelpers { - /// - /// 工具栏 - /// - [HtmlTargetElement( "util-toolbar" )] - public class ToolbarTagHelper : TagHelperBase { - /// - /// 颜色 - /// - public Color Color { get; set; } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected override IRender GetRender( Context context ) { - return new ToolbarRender( new Config( context ) ); - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Services/IUiService.cs b/src/Util.Ui.Angular/Services/IUiService.cs deleted file mode 100644 index ce0b39c5b..000000000 --- a/src/Util.Ui.Angular/Services/IUiService.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Linq.Expressions; -using Util.Ui.Components; - -namespace Util.Ui.Services { - /// - /// 组件服务 - /// - public interface IUiService { - /// - /// 单选框 - /// - /// 属性类型 - /// 属性表达式 - IRadio Radio( Expression> expression ); - /// - /// 复选框 - /// - /// 属性类型 - /// 属性表达式 - ICheckBox CheckBox( Expression> expression ); - /// - /// 滑动开关 - /// - /// 属性类型 - /// 属性表达式 - ISlideToggle SlideToggle( Expression> expression ); - /// - /// 下拉列表 - /// - /// 属性类型 - /// 属性表达式 - ISelect Select( Expression> expression ); - /// - /// 文本框 - /// - /// 属性类型 - /// 属性表达式 - ITextBox TextBox( Expression> expression ); - } -} diff --git a/src/Util.Ui.Angular/Services/UiService.cs b/src/Util.Ui.Angular/Services/UiService.cs deleted file mode 100644 index 411fa196d..000000000 --- a/src/Util.Ui.Angular/Services/UiService.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Linq.Expressions; -using Microsoft.AspNetCore.Mvc.Rendering; -using Util.Ui.Components; -using Util.Ui.Material.Forms.Models; - -namespace Util.Ui.Services { - /// - /// 组件服务 - /// - /// 模型类型 - public class UiService : UiServiceBase, IUiService { - /// - /// 初始化组件服务 - /// - /// HtmlHelper - public UiService( IHtmlHelper helper ) : base( helper ) { - } - - /// - /// 单选框 - /// - /// 属性类型 - /// 属性表达式 - public IRadio Radio( Expression> expression ) { - return new ModelRadio( expression ); - } - - /// - /// 复选框 - /// - /// 属性类型 - /// 属性表达式 - public ICheckBox CheckBox( Expression> expression ) { - return new ModelCheckBox( expression ); - } - - /// - /// 滑动开关 - /// - /// 属性类型 - /// 属性表达式 - public ISlideToggle SlideToggle( Expression> expression ) { - return new ModelSlideToggle( expression ); - } - - /// - /// 下拉列表 - /// - /// 属性类型 - /// 属性表达式 - public ISelect Select( Expression> expression ) { - return new ModelSelect( expression ); - } - - /// - /// 文本框 - /// - /// 属性类型 - /// 属性表达式 - public ITextBox TextBox( Expression> expression ) { - return new ModelTextBox( expression ); - } - } -} diff --git a/src/Util.Ui.Angular/TagHelpers/AngularTagHelperBase.cs b/src/Util.Ui.Angular/TagHelpers/AngularTagHelperBase.cs new file mode 100644 index 000000000..5cd2e6b44 --- /dev/null +++ b/src/Util.Ui.Angular/TagHelpers/AngularTagHelperBase.cs @@ -0,0 +1,57 @@ +using Util.Ui.TagHelpers; + +namespace Util.Ui.Angular.TagHelpers; + +/// +/// angular TagHelper基类 +/// +public abstract class AngularTagHelperBase : TagHelperBase { + /// + /// id,标签的id属性 + /// + public string RawId { get; set; } + /// + /// *ngIf + /// + public string NgIf { get; set; } + /// + /// [ngSwitch] + /// + public string NgSwitch { get; set; } + /// + /// *ngSwitchCase + /// + public string NgSwitchCase { get; set; } + /// + /// *ngSwitchDefault + /// + public bool NgSwitchDefault { get; set; } + /// + /// *ngFor,范例:let item of items + /// + public string NgFor { get; set; } + /// + /// [ngClass] + /// + public string NgClass { get; set; } + /// + /// [ngStyle] + /// + public string NgStyle { get; set; } + /// + /// *aclIf,访问控制,设置资源标识,有权则显示,多个资源or条件控制,使用 || 运算符,范例: a || b, and条件控制使用 && 运行算符,范例: a && b + /// + public string Acl { get; set; } + /// + /// 与acl属性配合使用,设置无权显示区域的ng-template组件标识 + /// + public string AclElseTemplateId { get; set; } + /// + /// [acl],访问控制 + /// + public string BindAcl { get; set; } + /// + /// (contextmenu),右键上下文菜单事件,类型: EventEmitter<MouseEvent> + /// + public string OnContextmenu { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.Angular/Usings.cs b/src/Util.Ui.Angular/Usings.cs new file mode 100644 index 000000000..f85384e33 --- /dev/null +++ b/src/Util.Ui.Angular/Usings.cs @@ -0,0 +1,3 @@ +global using System; +global using Microsoft.AspNetCore.Html; +global using Microsoft.AspNetCore.Razor.TagHelpers; diff --git a/src/Util.Ui.Core/02-Util.Ui.Core.csproj b/src/Util.Ui.Core/02-Util.Ui.Core.csproj deleted file mode 100644 index d8ee47f07..000000000 --- a/src/Util.Ui.Core/02-Util.Ui.Core.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - netstandard2.0 - Util.Ui.Core - Util.Ui - 0.2.2-preview - 何镇汐 - 何镇汐 - 何镇汐 - Util.Ui.Core是Util应用框架的Ui核心类库。 -Util是一个.net core平台下的应用框架,旨在提升小型团队的开发输出能力,由常用公共操作类(工具类)、分层架构基类、Ui组件,第三方组件封装,第三方业务接口封装,配套代码生成模板,权限等组成。 - https://github.com/dotnetcore/Util/blob/master/LICENSE.txt - https://github.com/dotnetcore/Util - https://avatars2.githubusercontent.com/u/19421630 - https://github.com/dotnetcore/Util - git - util;applicationframework;dotnetcore;aspnetcore; - Util应用框架以MIT开源发布,可随意使用 - 0.2.2.0 - 0.2.2.0 - false - - - - ..\..\output\release - ..\..\output\release\netstandard2.0\Util.Ui.Core.xml - - - - ..\..\output\release - ..\..\output\release\netstandard2.0\Util.Ui.Core.xml - - - - - - - - - - - - - diff --git a/src/Util.Ui.Core/Attributes/ModelAttribute.cs b/src/Util.Ui.Core/Attributes/ModelAttribute.cs deleted file mode 100644 index 782e3a0b7..000000000 --- a/src/Util.Ui.Core/Attributes/ModelAttribute.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; - -namespace Util.Ui.Attributes { - /// - /// 模型绑定 - /// - [AttributeUsage( AttributeTargets.Class | AttributeTargets.Property )] - public class ModelAttribute : Attribute { - /// - /// 初始化模型绑定 - /// - /// 模型 - public ModelAttribute( string model = "" ) { - Model = model; - } - - /// - /// 模型 - /// - public string Model { get; set; } - - /// - /// 忽略模型绑定,设置为true则不设置模型绑定 - /// - public bool Ignore { get; set; } - } -} diff --git a/src/Util.Ui.Core/Builders/AnchorBuilder.cs b/src/Util.Ui.Core/Builders/AnchorBuilder.cs deleted file mode 100644 index 53b3f7abf..000000000 --- a/src/Util.Ui.Core/Builders/AnchorBuilder.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Util.Ui.Builders { - /// - /// 链接生成器 - /// - public class AnchorBuilder : TagBuilder { - /// - /// 初始化链接生成器 - /// - public AnchorBuilder() : base( "a" ) { - } - } -} diff --git a/src/Util.Ui.Core/Builders/ButtonBuilder.cs b/src/Util.Ui.Core/Builders/ButtonBuilder.cs deleted file mode 100644 index 0ca24abe2..000000000 --- a/src/Util.Ui.Core/Builders/ButtonBuilder.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Util.Ui.Builders { - /// - /// 按钮生成器 - /// - public class ButtonBuilder : TagBuilder { - /// - /// 初始化按钮生成器 - /// - public ButtonBuilder() : base( "button" ) { - } - } -} diff --git a/src/Util.Ui.Core/Builders/EmptyTagBuilder.cs b/src/Util.Ui.Core/Builders/EmptyTagBuilder.cs deleted file mode 100644 index 6acbbd688..000000000 --- a/src/Util.Ui.Core/Builders/EmptyTagBuilder.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.IO; -using System.Text.Encodings.Web; - -namespace Util.Ui.Builders { - /// - /// 空标签生成器 - /// - public class EmptyTagBuilder : TagBuilder { - /// - /// 初始化空标签生成器 - /// - public EmptyTagBuilder() : base( "i" ) { - } - - /// - /// 写入流 - /// - /// 流写入器 - /// Html编码 - public override void WriteTo( TextWriter writer, HtmlEncoder encoder ) { - } - - /// - /// 输出 - /// - public override string ToString() { - return string.Empty; - } - } -} diff --git a/src/Util.Ui.Core/Builders/ImageBuilder.cs b/src/Util.Ui.Core/Builders/ImageBuilder.cs deleted file mode 100644 index 2759f270c..000000000 --- a/src/Util.Ui.Core/Builders/ImageBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.AspNetCore.Mvc.Rendering; - -namespace Util.Ui.Builders { - /// - /// 图片生成器 - /// - public class ImageBuilder : TagBuilder { - /// - /// 初始化图片生成器 - /// - public ImageBuilder() : base( "img", TagRenderMode.SelfClosing ) { - } - } -} \ No newline at end of file diff --git a/src/Util.Ui.Core/Builders/SpanBuilder.cs b/src/Util.Ui.Core/Builders/SpanBuilder.cs deleted file mode 100644 index 6b7d97f0d..000000000 --- a/src/Util.Ui.Core/Builders/SpanBuilder.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Util.Ui.Builders { - /// - /// span生成器 - /// - public class SpanBuilder : TagBuilder { - /// - /// 初始化span生成器 - /// - public SpanBuilder() : base( "span" ) { - } - } -} diff --git a/src/Util.Ui.Core/Builders/TagBuilder.cs b/src/Util.Ui.Core/Builders/TagBuilder.cs deleted file mode 100644 index ed275b6b3..000000000 --- a/src/Util.Ui.Core/Builders/TagBuilder.cs +++ /dev/null @@ -1,186 +0,0 @@ -using System.IO; -using System.Text.Encodings.Web; -using Microsoft.AspNetCore.Html; -using Microsoft.AspNetCore.Mvc.Rendering; -using Microsoft.AspNetCore.Razor.TagHelpers; - -namespace Util.Ui.Builders { - /// - /// 标签生成器,注意:已禁用Html编码 - /// - public class TagBuilder : IHtmlContent { - /// - /// 标签生成器 - /// - private readonly Microsoft.AspNetCore.Mvc.Rendering.TagBuilder _tagBuilder; - - /// - /// 初始化标签生成器 - /// - /// 标签名称,范例:div - /// 渲染模式 - public TagBuilder( string tagName, TagRenderMode renderMode = TagRenderMode.Normal ) { - _tagBuilder = new Microsoft.AspNetCore.Mvc.Rendering.TagBuilder( tagName ) { TagRenderMode = renderMode }; - } - - /// - /// 空标签生成器 - /// - public static readonly TagBuilder Null = new EmptyTagBuilder(); - - /// - /// Html内容 - /// - public IHtmlContentBuilder InnerHtml => _tagBuilder.InnerHtml; - - /// - /// 是否包含Html内容 - /// - public bool HasInnerHtml => _tagBuilder.HasInnerHtml; - - /// - /// 获取标签生成器 - /// - public Microsoft.AspNetCore.Mvc.Rendering.TagBuilder GetTagBuilder() { - return _tagBuilder; - } - - /// - /// 添加class属性 - /// - /// class属性值 - public TagBuilder Class( string @class ) { - _tagBuilder.AddCssClass( @class ); - return this; - } - - /// - /// 添加属性,当属性名已存在则忽略,也可进行替换 - /// - /// 属性名 - /// 属性值 - /// 是否替换已存在的属性 - public TagBuilder Attribute( string name, string value, bool replaceExisting = false ) { - _tagBuilder.MergeAttribute( name, value, replaceExisting ); - return this; - } - - /// - /// 添加属性,当属性名已存在则忽略 - /// - /// 属性名 - /// 属性值 - /// 当值为空时忽略 - public TagBuilder AddAttribute( string name, string value, bool ignoreIfValueIsEmpty = true ) { - if( ignoreIfValueIsEmpty && string.IsNullOrWhiteSpace( value ) ) - return this; - Attribute( name, value ); - return this; - } - - /// - /// 添加属性 - /// - /// 属性名 - public TagBuilder AddAttribute( string name ) { - return Attribute( name, string.Empty ); ; - } - - /// - /// 添加内容 - /// - /// 内容 - public TagBuilder AppendContent( string content ) { - _tagBuilder.InnerHtml.AppendHtml( content ); - return this; - } - - /// - /// 添加内容 - /// - /// 内容 - public TagBuilder AppendContent( IHtmlContent content ) { - _tagBuilder.InnerHtml.AppendHtml( content ); - return this; - } - - /// - /// 添加内容 - /// - /// 标签生成器 - public TagBuilder AppendContent( TagBuilder tagBuilder ) { - AppendContent( tagBuilder.GetTagBuilder() ); - return this; - } - - /// - /// 设置内容 - /// - /// 内容 - public TagBuilder SetContent( string content ) { - _tagBuilder.InnerHtml.SetHtmlContent( content ); - return this; - } - - /// - /// 设置内容 - /// - /// 内容 - public TagBuilder SetContent( IHtmlContent content ) { - _tagBuilder.InnerHtml.SetHtmlContent( content ); - return this; - } - - /// - /// 设置内容 - /// - /// 标签生成器 - public TagBuilder SetContent( TagBuilder tagBuilder ) { - SetContent( tagBuilder.GetTagBuilder() ); - return this; - } - - /// - /// 写入文本流 - /// - /// 流写入器 - /// 编码 - public virtual void WriteTo( TextWriter writer, HtmlEncoder encoder ) { - _tagBuilder.WriteTo( writer, NullHtmlEncoder.Default ); - } - - /// - /// 渲染起始标签 - /// - /// 流写入器 - public void RenderStartTag( TextWriter writer ) { - _tagBuilder.RenderStartTag().WriteTo( writer, NullHtmlEncoder.Default ); - } - - /// - /// 渲染内容 - /// - /// 流写入器 - public void RenderBody( TextWriter writer ) { - _tagBuilder.RenderBody().WriteTo( writer, NullHtmlEncoder.Default ); - } - - /// - /// 渲染结束标签 - /// - /// 流写入器 - public void RenderEndTag( TextWriter writer ) { - _tagBuilder.RenderEndTag().WriteTo( writer, NullHtmlEncoder.Default ); - } - - /// - /// 获取Html结果 - /// - public override string ToString() { - using( var writer = new StringWriter() ) { - _tagBuilder.WriteTo( writer, NullHtmlEncoder.Default ); - return writer.ToString(); - } - } - } -} diff --git a/src/Util.Ui.Core/Components/ComponentBase.cs b/src/Util.Ui.Core/Components/ComponentBase.cs deleted file mode 100644 index 1ea1b75b7..000000000 --- a/src/Util.Ui.Core/Components/ComponentBase.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System; -using System.IO; -using System.Text.Encodings.Web; -using Util.Logs; -using Util.Logs.Extensions; -using Util.Ui.Components.Internal; -using Util.Ui.Configs; -using Util.Ui.Renders; - -namespace Util.Ui.Components { - /// - /// 组件 - /// - public abstract class ComponentBase : IComponent, IOptionConfig { - /// - /// 渲染器 - /// - private IRender _render; - /// - /// 渲染器 - /// - protected IRender Render => _render ?? ( _render = GetRender() ); - /// - /// 获取渲染器 - /// - protected abstract IRender GetRender(); - - /// - /// 配置 - /// - private IConfig _config; - /// - /// 配置 - /// - protected IConfig OptionConfig => _config ?? ( _config = GetConfig() ); - /// - /// 获取配置 - /// - protected virtual IConfig GetConfig() { - return new Config(); - } - - /// - /// 配置 - /// - /// 配置类型 - /// 配置方法 - public void Config( Action configAction ) where TConfig : IConfig { - if( configAction == null ) - throw new ArgumentNullException( nameof( configAction ) ); - IConfig config = OptionConfig; - configAction( (TConfig)config ); - } - - /// - /// 写入文本流 - /// - /// 流写入器 - /// 编码 - public void WriteTo( TextWriter writer, HtmlEncoder encoder ) { - RenderBefore( writer, encoder ); - RenderContent( writer , encoder ); - RenderAfter( writer, encoder ); - } - - /// - /// 渲染前操作 - /// - /// 流写入器 - /// 编码 - protected virtual void RenderBefore( TextWriter writer, HtmlEncoder encoder ) { - } - - /// - /// 渲染操作 - /// - /// 流写入器 - /// 编码 - private void RenderContent( TextWriter writer, HtmlEncoder encoder ) { - Render.WriteTo( writer, encoder ); - } - - /// - /// 渲染后操作 - /// - /// 流写入器 - /// 编码 - protected virtual void RenderAfter( TextWriter writer, HtmlEncoder encoder ) { - } - - /// - /// 输出组件Html - /// - public override string ToString() { - return Render.ToString(); - } - - /// - /// 写日志 - /// - protected void WriteLog( string caption ) { - var log = GetLog(); - if( log.IsTraceEnabled == false ) - return; - log.Class( GetType().FullName ) - .Caption( caption ) - .Content( ToString() ) - .Trace(); - } - - /// - /// 获取日志操作 - /// - private ILog GetLog() { - try { - return Log.GetLog( TraceLogName ); - } - catch { - return Log.Null; - } - } - - /// - /// 跟踪日志名 - /// - public const string TraceLogName = "UiTraceLog"; - } -} diff --git a/src/Util.Ui.Core/Components/ContainerBase.cs b/src/Util.Ui.Core/Components/ContainerBase.cs deleted file mode 100644 index 8082292b5..000000000 --- a/src/Util.Ui.Core/Components/ContainerBase.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.IO; -using Util.Ui.Components.Internal; - -namespace Util.Ui.Components { - /// - /// 容器 - /// - /// 容器包装器类型 - public abstract class ContainerBase : ComponentBase, IContainer, IRenderEnd - where TWrapper : IDisposable { - /// - /// 初始化容器 - /// - /// 流写入器 - protected ContainerBase( TextWriter writer ) { - Writer = writer; - } - - /// - /// 流写入器 - /// - public TextWriter Writer { get; set; } - - /// - /// 准备渲染容器 - /// - public TWrapper Begin() { - if( Writer == null ) - throw new ArgumentNullException( "TextWriter未设置" ); - Render.RenderStartTag( Writer ); - return GetWrapper(); - } - - /// - /// 获取容器包装器 - /// - protected abstract TWrapper GetWrapper(); - - /// - /// 容器渲染结束 - /// - public void End() { - Render.RenderEndTag( Writer ); - } - } -} diff --git a/src/Util.Ui.Core/Components/ContainerWrapper.cs b/src/Util.Ui.Core/Components/ContainerWrapper.cs deleted file mode 100644 index 1fc5a4bf8..000000000 --- a/src/Util.Ui.Core/Components/ContainerWrapper.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using Util.Ui.Components.Internal; - -namespace Util.Ui.Components { - /// - /// 容器包装器 - /// - public class ContainerWrapper : IDisposable { - /// - /// 容器 - /// - private readonly IContainer _container; - - /// - /// 初始化容器包装器 - /// - /// 容器 - public ContainerWrapper( IContainer container ) { - _container = container; - } - - /// - /// 渲染结束 - /// - public void Dispose() { - if( _container is IRenderEnd container ) - container.End(); - } - } -} diff --git a/src/Util.Ui.Core/Configs/Config.cs b/src/Util.Ui.Core/Configs/Config.cs deleted file mode 100644 index 42eab8c53..000000000 --- a/src/Util.Ui.Core/Configs/Config.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System.Collections.Generic; -using Microsoft.AspNetCore.Html; -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.TagHelpers; - -namespace Util.Ui.Configs { - /// - /// 配置 - /// - public class Config : IConfig { - /// - /// 类 - /// - private readonly List _classList; - - /// - /// 初始化配置 - /// - public Config() : this( null, null, null ) { - } - - /// - /// 初始化配置 - /// - /// TagHelper上下文 - public Config( Context context ) : this( context.AllAttributes, context.OutputAttributes, context.Content ) { - } - - /// - /// 初始化配置 - /// - /// 全部属性集合 - /// 输出属性集合,TagHelper中未明确定义的属性从该集合获取 - /// 内容 - public Config( TagHelperAttributeList allAttributes, TagHelperAttributeList outputAttributes, IHtmlContent content ) { - _classList = new List(); - AllAttributes = allAttributes ?? new TagHelperAttributeList(); - OutputAttributes = outputAttributes ?? new TagHelperAttributeList(); - Content = content; - } - - /// - /// 全部属性集合 - /// - public TagHelperAttributeList AllAttributes { get; } - - /// - /// 输出属性集合,TagHelper中未明确定义的属性从该集合获取 - /// - public TagHelperAttributeList OutputAttributes { get; } - - /// - /// 内容 - /// - public IHtmlContent Content { get; set; } - - /// - /// 属性集合是否包含指定属性 - /// - /// 属性名 - public bool Contains( string name ) { - return AllAttributes.ContainsName( name ); - } - - /// - /// 获取属性值 - /// - /// 属性名 - public string GetValue( string name ) { - return Contains( name ) ? AllAttributes[name].Value.SafeString() : string.Empty; - } - - /// - /// 获取属性值 - /// - /// 目标类型 - /// 属性名 - public T GetValue( string name ) { - return Contains( name ) ? Util.Helpers.Convert.To( AllAttributes[name].Value ) : default(T); - } - - /// - /// 获取布尔属性值 - /// - /// 属性名 - public string GetBoolValue( string name ) { - return Util.Helpers.String.FirstLowerCase( GetValue( name ) ); - } - - /// - /// 设置属性 - /// - /// 属性名 - /// 值 - public void SetAttribute( string name,object value ) { - AllAttributes.SetAttribute( new TagHelperAttribute( name,value ) ); - } - - /// - /// 移除属性 - /// - /// 属性名 - public void Remove( string name ) { - AllAttributes.RemoveAll( name ); - } - - /// - /// 添加类 - /// - public void AddClass( string @class ) { - if( string.IsNullOrWhiteSpace( @class ) ) - return; - if( _classList.Contains( @class ) ) - return; - _classList.Add( @class ); - } - - /// - /// 获取类列表 - /// - public List GetClassList() { - return _classList; - } - - /// - /// 是否验证 - /// - public static bool IsValidate { get; set; } = true; - - /// - /// 验证 - /// - public string Validate() { - if ( IsValidate ) - return GetValidateMessage(); - return string.Empty; - } - - /// - /// 获取验证消息 - /// - public virtual string GetValidateMessage() { - return string.Empty; - } - } -} diff --git a/src/Util.Ui.Core/Configs/UiConst.cs b/src/Util.Ui.Core/Configs/UiConst.cs deleted file mode 100644 index a9bb26128..000000000 --- a/src/Util.Ui.Core/Configs/UiConst.cs +++ /dev/null @@ -1,276 +0,0 @@ -namespace Util.Ui.Configs { - /// - /// Ui常量 - /// - public static class UiConst { - /// - /// 名称 - /// - public const string Name = "name"; - /// - /// 类名 - /// - public const string Class = "class"; - /// - /// 样式 - /// - public const string Style = "style"; - /// - /// 样式 - /// - public const string Styles = "styles"; - /// - /// 颜色 - /// - public const string Color = "color"; - /// - /// 背景色 - /// - public const string BackgroundColor = "background-color"; - /// - /// 类型 - /// - public const string Type = "type"; - /// - /// 标识 - /// - public const string Id = "id"; - /// - /// 文本 - /// - public const string Text = "text"; - /// - /// 禁用 - /// - public const string Disabled = "disabled"; - /// - /// 只读 - /// - public const string ReadOnly = "readonly"; - /// - /// 单击事件 - /// - public const string OnClick = "on-click"; - /// - /// 变更事件 - /// - public const string OnChange = "on-change"; - /// - /// 获得焦点事件 - /// - public const string OnFocus = "on-focus"; - /// - /// 失去焦点事件 - /// - public const string OnBlur = "on-blur"; - /// - /// 键盘按键事件 - /// - public const string OnKeyup = "on-keyup"; - /// - /// 键盘按下事件 - /// - public const string OnKeydown = "on-keydown"; - /// - /// 提交事件 - /// - public const string OnSubmit = "on-submit"; - /// - /// 打开事件 - /// - public const string OnOpen = "on-open"; - /// - /// 关闭事件 - /// - public const string OnClose = "on-close"; - /// - /// 图标 - /// - public const string Icon = "icon"; - /// - /// Font Awesome图标 - /// - public const string FontAwesomeIcon = "font-awesome-icon"; - /// - /// Material图标 - /// - public const string MaterialIcon = "material-icon"; - /// - /// 大小 - /// - public const string Size = "size"; - /// - /// 持续旋转 - /// - public const string Spin = "spin"; - /// - /// 旋转 - /// - public const string Rotate = "rotate"; - /// - /// 子组件 - /// - public const string Child = "child"; - /// - /// 子组件class - /// - public const string ChildClass = "child-class"; - /// - /// Url - /// - public const string Url = "url"; - /// - /// 数据源 - /// - public const string DataSource = "datas-source"; - /// - /// 占位提示 - /// - public const string Placeholder = "placeholder"; - /// - /// 多选 - /// - public const string Multiple = "multiple"; - /// - /// 模型绑定 - /// - public const string Model = "model"; - /// - /// 必填项 - /// - public const string Required = "required"; - /// - /// 必填项错误消息 - /// - public const string RequiredMessage = "required-message"; - /// - /// 最小长度 - /// - public const string MinLength = "min-length"; - /// - /// 最小长度错误消息 - /// - public const string MinLengthMessage = "min-length-message"; - /// - /// 最大长度 - /// - public const string MaxLength = "max-length"; - /// - /// 电子邮件错误消息 - /// - public const string EmailMessage = "email-message"; - /// - /// 模板 - /// - public const string Template = "template"; - /// - /// 前缀 - /// - public const string Prefix = "prefix"; - /// - /// 宽度 - /// - public const string Width = "width"; - /// - /// 高度 - /// - public const string Height = "height"; - /// - /// 提示 - /// - public const string Tooltip = "tooltip"; - /// - /// 链接 - /// - public const string Link = "link"; - /// - /// 标签 - /// - public const string Label = "label"; - /// - /// 位置 - /// - public const string Position = "position"; - /// - /// 不确定 - /// - public const string Indeterminate = "indeterminate"; - /// - /// 垂直 - /// - public const string Vertical = "vertical"; - /// - /// for - /// - public const string For = "for"; - /// - /// X轴位置 - /// - public const string XPosition = "x-position"; - /// - /// Y轴位置 - /// - public const string YPosition = "y-position"; - /// - /// 重叠 - /// - public const string Overlap = "overlap"; - /// - /// 拉伸 - /// - public const string Stretch = "stretch"; - /// - /// 选中索引 - /// - public const string SelectedIndex = "selected-index"; - /// - /// 延迟加载 - /// - public const string LazyLoad = "lazy-load"; - /// - /// 全屏 - /// - public const string Fullscreen = "fullscreen"; - /// - /// 打开 - /// - public const string Opened = "opened"; - /// - /// 模式 - /// - public const string Mode = "mode"; - /// - /// 自动调整大小 - /// - public const string AutoSize = "auto-size"; - /// - /// 对齐 - /// - public const string Align = "align"; - /// - /// 源路径 - /// - public const string Src = "src"; - /// - /// 展开 - /// - public const string Expanded = "expanded"; - /// - /// 列 - /// - public const string Columns = "columns"; - /// - /// 行高 - /// - public const string RowHeight = "row-height"; - /// - /// 合并列 - /// - public const string Colspan = "colspan"; - /// - /// 合并行 - /// - public const string Rowspan = "rowspan"; - - } -} diff --git a/src/Util.Ui.Core/Datas/TreeNode.cs b/src/Util.Ui.Core/Datas/TreeNode.cs deleted file mode 100644 index 0fa3b7fc9..000000000 --- a/src/Util.Ui.Core/Datas/TreeNode.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Util.Ui.Datas { - /// - /// 树节点 - /// - public class TreeNode { - /// - /// 标识 - /// - public string Id { get; set; } - /// - /// 父标识 - /// - public string ParentId { get; set; } - /// - /// 路径 - /// - public string Path { get; set; } - /// - /// 级数 - /// - public int? Level { get; set; } - /// - /// 文本 - /// - public string Text { get; set; } - } -} diff --git a/src/Util.Ui.Core/Enums/FontAwesomeIcon.cs b/src/Util.Ui.Core/Enums/FontAwesomeIcon.cs deleted file mode 100644 index 721945454..000000000 --- a/src/Util.Ui.Core/Enums/FontAwesomeIcon.cs +++ /dev/null @@ -1,3403 +0,0 @@ -using System.ComponentModel; - -namespace Util.Ui.Enums { - /// - /// Font Awesome图标 - /// - public enum FontAwesomeIcon { - /// - /// fa-glass - /// - [Description( "fa-glass" )] - Glass, - /// - /// fa-music - /// - [Description( "fa-music" )] - Music, - /// - /// fa-search - /// - [Description( "fa-search" )] - Search, - /// - /// fa-envelope-o - /// - [Description( "fa-envelope-o" )] - EnvelopeO, - /// - /// fa-heart - /// - [Description( "fa-heart" )] - Heart, - /// - /// fa-star - /// - [Description( "fa-star" )] - Star, - /// - /// fa-star-o - /// - [Description( "fa-star-o" )] - StarO, - /// - /// fa-user - /// - [Description( "fa-user" )] - User, - /// - /// fa-film - /// - [Description( "fa-film" )] - Film, - /// - /// fa-th-large - /// - [Description( "fa-th-large" )] - ThLarge, - /// - /// fa-th - /// - [Description( "fa-th" )] - Th, - /// - /// fa-th-list - /// - [Description( "fa-th-list" )] - ThList, - /// - /// fa-check - /// - [Description( "fa-check" )] - Check, - /// - /// fa-times - /// - [Description( "fa-times" )] - Times, - /// - /// fa-search-plus - /// - [Description( "fa-search-plus" )] - SearchPlus, - /// - /// fa-search-minus - /// - [Description( "fa-search-minus" )] - SearchMinus, - /// - /// fa-power-off - /// - [Description( "fa-power-off" )] - PowerOff, - /// - /// fa-signal - /// - [Description( "fa-signal" )] - Signal, - /// - /// fa-cog - /// - [Description( "fa-cog" )] - Cog, - /// - /// fa-trash-o - /// - [Description( "fa-trash-o" )] - TrashO, - /// - /// fa-home - /// - [Description( "fa-home" )] - Home, - /// - /// fa-file-o - /// - [Description( "fa-file-o" )] - FileO, - /// - /// fa-clock-o - /// - [Description( "fa-clock-o" )] - ClockO, - /// - /// fa-road - /// - [Description( "fa-road" )] - Road, - /// - /// fa-download - /// - [Description( "fa-download" )] - Download, - /// - /// fa-arrow-circle-o-down - /// - [Description( "fa-arrow-circle-o-down" )] - ArrowCircleODown, - /// - /// fa-arrow-circle-o-up - /// - [Description( "fa-arrow-circle-o-up" )] - ArrowCircleOUp, - /// - /// fa-inbox - /// - [Description( "fa-inbox" )] - Inbox, - /// - /// fa-play-circle-o - /// - [Description( "fa-play-circle-o" )] - PlayCircleO, - /// - /// fa-repeat - /// - [Description( "fa-repeat" )] - Repeat, - /// - /// fa-refresh - /// - [Description( "fa-refresh" )] - Refresh, - /// - /// fa-list-alt - /// - [Description( "fa-list-alt" )] - ListAlt, - /// - /// fa-lock - /// - [Description( "fa-lock" )] - Lock, - /// - /// fa-flag - /// - [Description( "fa-flag" )] - Flag, - /// - /// fa-headphones - /// - [Description( "fa-headphones" )] - Headphones, - /// - /// fa-volume-off - /// - [Description( "fa-volume-off" )] - VolumeOff, - /// - /// fa-volume-down - /// - [Description( "fa-volume-down" )] - VolumeDown, - /// - /// fa-volume-up - /// - [Description( "fa-volume-up" )] - VolumeUp, - /// - /// fa-qrcode - /// - [Description( "fa-qrcode" )] - Qrcode, - /// - /// fa-barcode - /// - [Description( "fa-barcode" )] - Barcode, - /// - /// fa-tag - /// - [Description( "fa-tag" )] - Tag, - /// - /// fa-tags - /// - [Description( "fa-tags" )] - Tags, - /// - /// fa-book - /// - [Description( "fa-book" )] - Book, - /// - /// fa-bookmark - /// - [Description( "fa-bookmark" )] - Bookmark, - /// - /// fa-print - /// - [Description( "fa-print" )] - Print, - /// - /// fa-camera - /// - [Description( "fa-camera" )] - Camera, - /// - /// fa-font - /// - [Description( "fa-font" )] - Font, - /// - /// fa-bold - /// - [Description( "fa-bold" )] - Bold, - /// - /// fa-italic - /// - [Description( "fa-italic" )] - Italic, - /// - /// fa-text-height - /// - [Description( "fa-text-height" )] - TextHeight, - /// - /// fa-text-width - /// - [Description( "fa-text-width" )] - TextWidth, - /// - /// fa-align-left - /// - [Description( "fa-align-left" )] - AlignLeft, - /// - /// fa-align-center - /// - [Description( "fa-align-center" )] - AlignCenter, - /// - /// fa-align-right - /// - [Description( "fa-align-right" )] - AlignRight, - /// - /// fa-align-justify - /// - [Description( "fa-align-justify" )] - AlignJustify, - /// - /// fa-list - /// - [Description( "fa-list" )] - List, - /// - /// fa-outdent - /// - [Description( "fa-outdent" )] - Outdent, - /// - /// fa-indent - /// - [Description( "fa-indent" )] - Indent, - /// - /// fa-video-camera - /// - [Description( "fa-video-camera" )] - VideoCamera, - /// - /// fa-picture-o - /// - [Description( "fa-picture-o" )] - PictureO, - /// - /// fa-pencil - /// - [Description( "fa-pencil" )] - Pencil, - /// - /// fa-map-marker - /// - [Description( "fa-map-marker" )] - MapMarker, - /// - /// fa-adjust - /// - [Description( "fa-adjust" )] - Adjust, - /// - /// fa-tint - /// - [Description( "fa-tint" )] - Tint, - /// - /// fa-pencil-square-o - /// - [Description( "fa-pencil-square-o" )] - PencilSquareO, - /// - /// fa-share-square-o - /// - [Description( "fa-share-square-o" )] - ShareSquareO, - /// - /// fa-check-square-o - /// - [Description( "fa-check-square-o" )] - CheckSquareO, - /// - /// fa-arrows - /// - [Description( "fa-arrows" )] - Arrows, - /// - /// fa-step-backward - /// - [Description( "fa-step-backward" )] - StepBackward, - /// - /// fa-fast-backward - /// - [Description( "fa-fast-backward" )] - FastBackward, - /// - /// fa-backward - /// - [Description( "fa-backward" )] - Backward, - /// - /// fa-play - /// - [Description( "fa-play" )] - Play, - /// - /// fa-pause - /// - [Description( "fa-pause" )] - Pause, - /// - /// fa-stop - /// - [Description( "fa-stop" )] - Stop, - /// - /// fa-forward - /// - [Description( "fa-forward" )] - Forward, - /// - /// fa-fast-forward - /// - [Description( "fa-fast-forward" )] - FastForward, - /// - /// fa-step-forward - /// - [Description( "fa-step-forward" )] - StepForward, - /// - /// fa-eject - /// - [Description( "fa-eject" )] - Eject, - /// - /// fa-chevron-left - /// - [Description( "fa-chevron-left" )] - ChevronLeft, - /// - /// fa-chevron-right - /// - [Description( "fa-chevron-right" )] - ChevronRight, - /// - /// fa-plus-circle - /// - [Description( "fa-plus-circle" )] - PlusCircle, - /// - /// fa-minus-circle - /// - [Description( "fa-minus-circle" )] - MinusCircle, - /// - /// fa-times-circle - /// - [Description( "fa-times-circle" )] - TimesCircle, - /// - /// fa-check-circle - /// - [Description( "fa-check-circle" )] - CheckCircle, - /// - /// fa-question-circle - /// - [Description( "fa-question-circle" )] - QuestionCircle, - /// - /// fa-info-circle - /// - [Description( "fa-info-circle" )] - InfoCircle, - /// - /// fa-crosshairs - /// - [Description( "fa-crosshairs" )] - Crosshairs, - /// - /// fa-times-circle-o - /// - [Description( "fa-times-circle-o" )] - TimesCircleO, - /// - /// fa-check-circle-o - /// - [Description( "fa-check-circle-o" )] - CheckCircleO, - /// - /// fa-ban - /// - [Description( "fa-ban" )] - Ban, - /// - /// fa-arrow-left - /// - [Description( "fa-arrow-left" )] - ArrowLeft, - /// - /// fa-arrow-right - /// - [Description( "fa-arrow-right" )] - ArrowRight, - /// - /// fa-arrow-up - /// - [Description( "fa-arrow-up" )] - ArrowUp, - /// - /// fa-arrow-down - /// - [Description( "fa-arrow-down" )] - ArrowDown, - /// - /// fa-share - /// - [Description( "fa-share" )] - Share, - /// - /// fa-expand - /// - [Description( "fa-expand" )] - Expand, - /// - /// fa-compress - /// - [Description( "fa-compress" )] - Compress, - /// - /// fa-plus - /// - [Description( "fa-plus" )] - Plus, - /// - /// fa-minus - /// - [Description( "fa-minus" )] - Minus, - /// - /// fa-asterisk - /// - [Description( "fa-asterisk" )] - Asterisk, - /// - /// fa-exclamation-circle - /// - [Description( "fa-exclamation-circle" )] - ExclamationCircle, - /// - /// fa-gift - /// - [Description( "fa-gift" )] - Gift, - /// - /// fa-leaf - /// - [Description( "fa-leaf" )] - Leaf, - /// - /// fa-fire - /// - [Description( "fa-fire" )] - Fire, - /// - /// fa-eye - /// - [Description( "fa-eye" )] - Eye, - /// - /// fa-eye-slash - /// - [Description( "fa-eye-slash" )] - EyeSlash, - /// - /// fa-exclamation-triangle - /// - [Description( "fa-exclamation-triangle" )] - ExclamationTriangle, - /// - /// fa-plane - /// - [Description( "fa-plane" )] - Plane, - /// - /// fa-calendar - /// - [Description( "fa-calendar" )] - Calendar, - /// - /// fa-random - /// - [Description( "fa-random" )] - Random, - /// - /// fa-comment - /// - [Description( "fa-comment" )] - Comment, - /// - /// fa-magnet - /// - [Description( "fa-magnet" )] - Magnet, - /// - /// fa-chevron-up - /// - [Description( "fa-chevron-up" )] - ChevronUp, - /// - /// fa-chevron-down - /// - [Description( "fa-chevron-down" )] - ChevronDown, - /// - /// fa-retweet - /// - [Description( "fa-retweet" )] - Retweet, - /// - /// fa-shopping-cart - /// - [Description( "fa-shopping-cart" )] - ShoppingCart, - /// - /// fa-folder - /// - [Description( "fa-folder" )] - Folder, - /// - /// fa-folder-open - /// - [Description( "fa-folder-open" )] - FolderOpen, - /// - /// fa-arrows-v - /// - [Description( "fa-arrows-v" )] - ArrowsV, - /// - /// fa-arrows-h - /// - [Description( "fa-arrows-h" )] - ArrowsH, - /// - /// fa-bar-chart - /// - [Description( "fa-bar-chart" )] - BarChart, - /// - /// fa-twitter-square - /// - [Description( "fa-twitter-square" )] - TwitterSquare, - /// - /// fa-facebook-square - /// - [Description( "fa-facebook-square" )] - FacebookSquare, - /// - /// fa-camera-retro - /// - [Description( "fa-camera-retro" )] - CameraRetro, - /// - /// fa-key - /// - [Description( "fa-key" )] - Key, - /// - /// fa-cogs - /// - [Description( "fa-cogs" )] - Cogs, - /// - /// fa-comments - /// - [Description( "fa-comments" )] - Comments, - /// - /// fa-thumbs-o-up - /// - [Description( "fa-thumbs-o-up" )] - ThumbsOUp, - /// - /// fa-thumbs-o-down - /// - [Description( "fa-thumbs-o-down" )] - ThumbsODown, - /// - /// fa-star-half - /// - [Description( "fa-star-half" )] - StarHalf, - /// - /// fa-heart-o - /// - [Description( "fa-heart-o" )] - HeartO, - /// - /// fa-sign-out - /// - [Description( "fa-sign-out" )] - SignOut, - /// - /// fa-linkedin-square - /// - [Description( "fa-linkedin-square" )] - LinkedinSquare, - /// - /// fa-thumb-tack - /// - [Description( "fa-thumb-tack" )] - ThumbTack, - /// - /// fa-external-link - /// - [Description( "fa-external-link" )] - ExternalLink, - /// - /// fa-sign-in - /// - [Description( "fa-sign-in" )] - SignIn, - /// - /// fa-trophy - /// - [Description( "fa-trophy" )] - Trophy, - /// - /// fa-github-square - /// - [Description( "fa-github-square" )] - GithubSquare, - /// - /// fa-upload - /// - [Description( "fa-upload" )] - Upload, - /// - /// fa-lemon-o - /// - [Description( "fa-lemon-o" )] - LemonO, - /// - /// fa-phone - /// - [Description( "fa-phone" )] - Phone, - /// - /// fa-square-o - /// - [Description( "fa-square-o" )] - SquareO, - /// - /// fa-bookmark-o - /// - [Description( "fa-bookmark-o" )] - BookmarkO, - /// - /// fa-phone-square - /// - [Description( "fa-phone-square" )] - PhoneSquare, - /// - /// fa-twitter - /// - [Description( "fa-twitter" )] - Twitter, - /// - /// fa-facebook - /// - [Description( "fa-facebook" )] - Facebook, - /// - /// fa-github - /// - [Description( "fa-github" )] - Github, - /// - /// fa-unlock - /// - [Description( "fa-unlock" )] - Unlock, - /// - /// fa-credit-card - /// - [Description( "fa-credit-card" )] - CreditCard, - /// - /// fa-rss - /// - [Description( "fa-rss" )] - Rss, - /// - /// fa-hdd-o - /// - [Description( "fa-hdd-o" )] - HddO, - /// - /// fa-bullhorn - /// - [Description( "fa-bullhorn" )] - Bullhorn, - /// - /// fa-bell - /// - [Description( "fa-bell" )] - Bell, - /// - /// fa-certificate - /// - [Description( "fa-certificate" )] - Certificate, - /// - /// fa-hand-o-right - /// - [Description( "fa-hand-o-right" )] - HandORight, - /// - /// fa-hand-o-left - /// - [Description( "fa-hand-o-left" )] - HandOLeft, - /// - /// fa-hand-o-up - /// - [Description( "fa-hand-o-up" )] - HandOUp, - /// - /// fa-hand-o-down - /// - [Description( "fa-hand-o-down" )] - HandODown, - /// - /// fa-arrow-circle-left - /// - [Description( "fa-arrow-circle-left" )] - ArrowCircleLeft, - /// - /// fa-arrow-circle-right - /// - [Description( "fa-arrow-circle-right" )] - ArrowCircleRight, - /// - /// fa-arrow-circle-up - /// - [Description( "fa-arrow-circle-up" )] - ArrowCircleUp, - /// - /// fa-arrow-circle-down - /// - [Description( "fa-arrow-circle-down" )] - ArrowCircleDown, - /// - /// fa-globe - /// - [Description( "fa-globe" )] - Globe, - /// - /// fa-wrench - /// - [Description( "fa-wrench" )] - Wrench, - /// - /// fa-tasks - /// - [Description( "fa-tasks" )] - Tasks, - /// - /// fa-filter - /// - [Description( "fa-filter" )] - Filter, - /// - /// fa-briefcase - /// - [Description( "fa-briefcase" )] - Briefcase, - /// - /// fa-arrows-alt - /// - [Description( "fa-arrows-alt" )] - ArrowsAlt, - /// - /// fa-users - /// - [Description( "fa-users" )] - Users, - /// - /// fa-link - /// - [Description( "fa-link" )] - Link, - /// - /// fa-cloud - /// - [Description( "fa-cloud" )] - Cloud, - /// - /// fa-flask - /// - [Description( "fa-flask" )] - Flask, - /// - /// fa-scissors - /// - [Description( "fa-scissors" )] - Scissors, - /// - /// fa-files-o - /// - [Description( "fa-files-o" )] - FilesO, - /// - /// fa-paperclip - /// - [Description( "fa-paperclip" )] - Paperclip, - /// - /// fa-floppy-o - /// - [Description( "fa-floppy-o" )] - FloppyO, - /// - /// fa-square - /// - [Description( "fa-square" )] - Square, - /// - /// fa-bars - /// - [Description( "fa-bars" )] - Bars, - /// - /// fa-list-ul - /// - [Description( "fa-list-ul" )] - ListUl, - /// - /// fa-list-ol - /// - [Description( "fa-list-ol" )] - ListOl, - /// - /// fa-strikethrough - /// - [Description( "fa-strikethrough" )] - Strikethrough, - /// - /// fa-underline - /// - [Description( "fa-underline" )] - Underline, - /// - /// fa-table - /// - [Description( "fa-table" )] - Table, - /// - /// fa-magic - /// - [Description( "fa-magic" )] - Magic, - /// - /// fa-truck - /// - [Description( "fa-truck" )] - Truck, - /// - /// fa-pinterest - /// - [Description( "fa-pinterest" )] - Pinterest, - /// - /// fa-pinterest-square - /// - [Description( "fa-pinterest-square" )] - PinterestSquare, - /// - /// fa-google-plus-square - /// - [Description( "fa-google-plus-square" )] - GooglePlusSquare, - /// - /// fa-google-plus - /// - [Description( "fa-google-plus" )] - GooglePlus, - /// - /// fa-money - /// - [Description( "fa-money" )] - Money, - /// - /// fa-caret-down - /// - [Description( "fa-caret-down" )] - CaretDown, - /// - /// fa-caret-up - /// - [Description( "fa-caret-up" )] - CaretUp, - /// - /// fa-caret-left - /// - [Description( "fa-caret-left" )] - CaretLeft, - /// - /// fa-caret-right - /// - [Description( "fa-caret-right" )] - CaretRight, - /// - /// fa-columns - /// - [Description( "fa-columns" )] - Columns, - /// - /// fa-sort - /// - [Description( "fa-sort" )] - Sort, - /// - /// fa-sort-desc - /// - [Description( "fa-sort-desc" )] - SortDesc, - /// - /// fa-sort-asc - /// - [Description( "fa-sort-asc" )] - SortAsc, - /// - /// fa-envelope - /// - [Description( "fa-envelope" )] - Envelope, - /// - /// fa-linkedin - /// - [Description( "fa-linkedin" )] - Linkedin, - /// - /// fa-undo - /// - [Description( "fa-undo" )] - Undo, - /// - /// fa-gavel - /// - [Description( "fa-gavel" )] - Gavel, - /// - /// fa-tachometer - /// - [Description( "fa-tachometer" )] - Tachometer, - /// - /// fa-comment-o - /// - [Description( "fa-comment-o" )] - CommentO, - /// - /// fa-comments-o - /// - [Description( "fa-comments-o" )] - CommentsO, - /// - /// fa-bolt - /// - [Description( "fa-bolt" )] - Bolt, - /// - /// fa-sitemap - /// - [Description( "fa-sitemap" )] - Sitemap, - /// - /// fa-umbrella - /// - [Description( "fa-umbrella" )] - Umbrella, - /// - /// fa-clipboard - /// - [Description( "fa-clipboard" )] - Clipboard, - /// - /// fa-lightbulb-o - /// - [Description( "fa-lightbulb-o" )] - LightbulbO, - /// - /// fa-exchange - /// - [Description( "fa-exchange" )] - Exchange, - /// - /// fa-cloud-download - /// - [Description( "fa-cloud-download" )] - CloudDownload, - /// - /// fa-cloud-upload - /// - [Description( "fa-cloud-upload" )] - CloudUpload, - /// - /// fa-user-md - /// - [Description( "fa-user-md" )] - UserMd, - /// - /// fa-stethoscope - /// - [Description( "fa-stethoscope" )] - Stethoscope, - /// - /// fa-suitcase - /// - [Description( "fa-suitcase" )] - Suitcase, - /// - /// fa-bell-o - /// - [Description( "fa-bell-o" )] - BellO, - /// - /// fa-coffee - /// - [Description( "fa-coffee" )] - Coffee, - /// - /// fa-cutlery - /// - [Description( "fa-cutlery" )] - Cutlery, - /// - /// fa-file-text-o - /// - [Description( "fa-file-text-o" )] - FileTextO, - /// - /// fa-building-o - /// - [Description( "fa-building-o" )] - BuildingO, - /// - /// fa-hospital-o - /// - [Description( "fa-hospital-o" )] - HospitalO, - /// - /// fa-ambulance - /// - [Description( "fa-ambulance" )] - Ambulance, - /// - /// fa-medkit - /// - [Description( "fa-medkit" )] - Medkit, - /// - /// fa-fighter-jet - /// - [Description( "fa-fighter-jet" )] - FighterJet, - /// - /// fa-beer - /// - [Description( "fa-beer" )] - Beer, - /// - /// fa-h-square - /// - [Description( "fa-h-square" )] - HSquare, - /// - /// fa-plus-square - /// - [Description( "fa-plus-square" )] - PlusSquare, - /// - /// fa-angle-double-left - /// - [Description( "fa-angle-double-left" )] - AngleDoubleLeft, - /// - /// fa-angle-double-right - /// - [Description( "fa-angle-double-right" )] - AngleDoubleRight, - /// - /// fa-angle-double-up - /// - [Description( "fa-angle-double-up" )] - AngleDoubleUp, - /// - /// fa-angle-double-down - /// - [Description( "fa-angle-double-down" )] - AngleDoubleDown, - /// - /// fa-angle-left - /// - [Description( "fa-angle-left" )] - AngleLeft, - /// - /// fa-angle-right - /// - [Description( "fa-angle-right" )] - AngleRight, - /// - /// fa-angle-up - /// - [Description( "fa-angle-up" )] - AngleUp, - /// - /// fa-angle-down - /// - [Description( "fa-angle-down" )] - AngleDown, - /// - /// fa-desktop - /// - [Description( "fa-desktop" )] - Desktop, - /// - /// fa-laptop - /// - [Description( "fa-laptop" )] - Laptop, - /// - /// fa-tablet - /// - [Description( "fa-tablet" )] - Tablet, - /// - /// fa-mobile - /// - [Description( "fa-mobile" )] - Mobile, - /// - /// fa-circle-o - /// - [Description( "fa-circle-o" )] - CircleO, - /// - /// fa-quote-left - /// - [Description( "fa-quote-left" )] - QuoteLeft, - /// - /// fa-quote-right - /// - [Description( "fa-quote-right" )] - QuoteRight, - /// - /// fa-spinner - /// - [Description( "fa-spinner" )] - Spinner, - /// - /// fa-circle - /// - [Description( "fa-circle" )] - Circle, - /// - /// fa-reply - /// - [Description( "fa-reply" )] - Reply, - /// - /// fa-github-alt - /// - [Description( "fa-github-alt" )] - GithubAlt, - /// - /// fa-folder-o - /// - [Description( "fa-folder-o" )] - FolderO, - /// - /// fa-folder-open-o - /// - [Description( "fa-folder-open-o" )] - FolderOpenO, - /// - /// fa-smile-o - /// - [Description( "fa-smile-o" )] - SmileO, - /// - /// fa-frown-o - /// - [Description( "fa-frown-o" )] - FrownO, - /// - /// fa-meh-o - /// - [Description( "fa-meh-o" )] - MehO, - /// - /// fa-gamepad - /// - [Description( "fa-gamepad" )] - Gamepad, - /// - /// fa-keyboard-o - /// - [Description( "fa-keyboard-o" )] - KeyboardO, - /// - /// fa-flag-o - /// - [Description( "fa-flag-o" )] - FlagO, - /// - /// fa-flag-checkered - /// - [Description( "fa-flag-checkered" )] - FlagCheckered, - /// - /// fa-terminal - /// - [Description( "fa-terminal" )] - Terminal, - /// - /// fa-code - /// - [Description( "fa-code" )] - Code, - /// - /// fa-reply-all - /// - [Description( "fa-reply-all" )] - ReplyAll, - /// - /// fa-star-half-o - /// - [Description( "fa-star-half-o" )] - StarHalfO, - /// - /// fa-location-arrow - /// - [Description( "fa-location-arrow" )] - LocationArrow, - /// - /// fa-crop - /// - [Description( "fa-crop" )] - Crop, - /// - /// fa-code-fork - /// - [Description( "fa-code-fork" )] - CodeFork, - /// - /// fa-chain-broken - /// - [Description( "fa-chain-broken" )] - ChainBroken, - /// - /// fa-question - /// - [Description( "fa-question" )] - Question, - /// - /// fa-info - /// - [Description( "fa-info" )] - Info, - /// - /// fa-exclamation - /// - [Description( "fa-exclamation" )] - Exclamation, - /// - /// fa-superscript - /// - [Description( "fa-superscript" )] - Superscript, - /// - /// fa-subscript - /// - [Description( "fa-subscript" )] - Subscript, - /// - /// fa-eraser - /// - [Description( "fa-eraser" )] - Eraser, - /// - /// fa-puzzle-piece - /// - [Description( "fa-puzzle-piece" )] - PuzzlePiece, - /// - /// fa-microphone - /// - [Description( "fa-microphone" )] - Microphone, - /// - /// fa-microphone-slash - /// - [Description( "fa-microphone-slash" )] - MicrophoneSlash, - /// - /// fa-shield - /// - [Description( "fa-shield" )] - Shield, - /// - /// fa-calendar-o - /// - [Description( "fa-calendar-o" )] - CalendarO, - /// - /// fa-fire-extinguisher - /// - [Description( "fa-fire-extinguisher" )] - FireExtinguisher, - /// - /// fa-rocket - /// - [Description( "fa-rocket" )] - Rocket, - /// - /// fa-maxcdn - /// - [Description( "fa-maxcdn" )] - Maxcdn, - /// - /// fa-chevron-circle-left - /// - [Description( "fa-chevron-circle-left" )] - ChevronCircleLeft, - /// - /// fa-chevron-circle-right - /// - [Description( "fa-chevron-circle-right" )] - ChevronCircleRight, - /// - /// fa-chevron-circle-up - /// - [Description( "fa-chevron-circle-up" )] - ChevronCircleUp, - /// - /// fa-chevron-circle-down - /// - [Description( "fa-chevron-circle-down" )] - ChevronCircleDown, - /// - /// fa-html5 - /// - [Description( "fa-html5" )] - Html5, - /// - /// fa-css3 - /// - [Description( "fa-css3" )] - Css3, - /// - /// fa-anchor - /// - [Description( "fa-anchor" )] - Anchor, - /// - /// fa-unlock-alt - /// - [Description( "fa-unlock-alt" )] - UnlockAlt, - /// - /// fa-bullseye - /// - [Description( "fa-bullseye" )] - Bullseye, - /// - /// fa-ellipsis-h - /// - [Description( "fa-ellipsis-h" )] - EllipsisH, - /// - /// fa-ellipsis-v - /// - [Description( "fa-ellipsis-v" )] - EllipsisV, - /// - /// fa-rss-square - /// - [Description( "fa-rss-square" )] - RssSquare, - /// - /// fa-play-circle - /// - [Description( "fa-play-circle" )] - PlayCircle, - /// - /// fa-ticket - /// - [Description( "fa-ticket" )] - Ticket, - /// - /// fa-minus-square - /// - [Description( "fa-minus-square" )] - MinusSquare, - /// - /// fa-minus-square-o - /// - [Description( "fa-minus-square-o" )] - MinusSquareO, - /// - /// fa-level-up - /// - [Description( "fa-level-up" )] - LevelUp, - /// - /// fa-level-down - /// - [Description( "fa-level-down" )] - LevelDown, - /// - /// fa-check-square - /// - [Description( "fa-check-square" )] - CheckSquare, - /// - /// fa-pencil-square - /// - [Description( "fa-pencil-square" )] - PencilSquare, - /// - /// fa-external-link-square - /// - [Description( "fa-external-link-square" )] - ExternalLinkSquare, - /// - /// fa-share-square - /// - [Description( "fa-share-square" )] - ShareSquare, - /// - /// fa-compass - /// - [Description( "fa-compass" )] - Compass, - /// - /// fa-caret-square-o-down - /// - [Description( "fa-caret-square-o-down" )] - CaretSquareODown, - /// - /// fa-caret-square-o-up - /// - [Description( "fa-caret-square-o-up" )] - CaretSquareOUp, - /// - /// fa-caret-square-o-right - /// - [Description( "fa-caret-square-o-right" )] - CaretSquareORight, - /// - /// fa-eur - /// - [Description( "fa-eur" )] - Eur, - /// - /// fa-gbp - /// - [Description( "fa-gbp" )] - Gbp, - /// - /// fa-usd - /// - [Description( "fa-usd" )] - Usd, - /// - /// fa-inr - /// - [Description( "fa-inr" )] - Inr, - /// - /// fa-jpy - /// - [Description( "fa-jpy" )] - Jpy, - /// - /// fa-rub - /// - [Description( "fa-rub" )] - Rub, - /// - /// fa-krw - /// - [Description( "fa-krw" )] - Krw, - /// - /// fa-btc - /// - [Description( "fa-btc" )] - Btc, - /// - /// fa-file - /// - [Description( "fa-file" )] - File, - /// - /// fa-file-text - /// - [Description( "fa-file-text" )] - FileText, - /// - /// fa-sort-alpha-asc - /// - [Description( "fa-sort-alpha-asc" )] - SortAlphaAsc, - /// - /// fa-sort-alpha-desc - /// - [Description( "fa-sort-alpha-desc" )] - SortAlphaDesc, - /// - /// fa-sort-amount-asc - /// - [Description( "fa-sort-amount-asc" )] - SortAmountAsc, - /// - /// fa-sort-amount-desc - /// - [Description( "fa-sort-amount-desc" )] - SortAmountDesc, - /// - /// fa-sort-numeric-asc - /// - [Description( "fa-sort-numeric-asc" )] - SortNumericAsc, - /// - /// fa-sort-numeric-desc - /// - [Description( "fa-sort-numeric-desc" )] - SortNumericDesc, - /// - /// fa-thumbs-up - /// - [Description( "fa-thumbs-up" )] - ThumbsUp, - /// - /// fa-thumbs-down - /// - [Description( "fa-thumbs-down" )] - ThumbsDown, - /// - /// fa-youtube-square - /// - [Description( "fa-youtube-square" )] - YoutubeSquare, - /// - /// fa-youtube - /// - [Description( "fa-youtube" )] - Youtube, - /// - /// fa-xing - /// - [Description( "fa-xing" )] - Xing, - /// - /// fa-xing-square - /// - [Description( "fa-xing-square" )] - XingSquare, - /// - /// fa-youtube-play - /// - [Description( "fa-youtube-play" )] - YoutubePlay, - /// - /// fa-dropbox - /// - [Description( "fa-dropbox" )] - Dropbox, - /// - /// fa-stack-overflow - /// - [Description( "fa-stack-overflow" )] - StackOverflow, - /// - /// fa-instagram - /// - [Description( "fa-instagram" )] - Instagram, - /// - /// fa-flickr - /// - [Description( "fa-flickr" )] - Flickr, - /// - /// fa-adn - /// - [Description( "fa-adn" )] - Adn, - /// - /// fa-bitbucket - /// - [Description( "fa-bitbucket" )] - Bitbucket, - /// - /// fa-bitbucket-square - /// - [Description( "fa-bitbucket-square" )] - BitbucketSquare, - /// - /// fa-tumblr - /// - [Description( "fa-tumblr" )] - Tumblr, - /// - /// fa-tumblr-square - /// - [Description( "fa-tumblr-square" )] - TumblrSquare, - /// - /// fa-long-arrow-down - /// - [Description( "fa-long-arrow-down" )] - LongArrowDown, - /// - /// fa-long-arrow-up - /// - [Description( "fa-long-arrow-up" )] - LongArrowUp, - /// - /// fa-long-arrow-left - /// - [Description( "fa-long-arrow-left" )] - LongArrowLeft, - /// - /// fa-long-arrow-right - /// - [Description( "fa-long-arrow-right" )] - LongArrowRight, - /// - /// fa-apple - /// - [Description( "fa-apple" )] - Apple, - /// - /// fa-windows - /// - [Description( "fa-windows" )] - Windows, - /// - /// fa-android - /// - [Description( "fa-android" )] - Android, - /// - /// fa-linux - /// - [Description( "fa-linux" )] - Linux, - /// - /// fa-dribbble - /// - [Description( "fa-dribbble" )] - Dribbble, - /// - /// fa-skype - /// - [Description( "fa-skype" )] - Skype, - /// - /// fa-foursquare - /// - [Description( "fa-foursquare" )] - Foursquare, - /// - /// fa-trello - /// - [Description( "fa-trello" )] - Trello, - /// - /// fa-female - /// - [Description( "fa-female" )] - Female, - /// - /// fa-male - /// - [Description( "fa-male" )] - Male, - /// - /// fa-gratipay - /// - [Description( "fa-gratipay" )] - Gratipay, - /// - /// fa-sun-o - /// - [Description( "fa-sun-o" )] - SunO, - /// - /// fa-moon-o - /// - [Description( "fa-moon-o" )] - MoonO, - /// - /// fa-archive - /// - [Description( "fa-archive" )] - Archive, - /// - /// fa-bug - /// - [Description( "fa-bug" )] - Bug, - /// - /// fa-vk - /// - [Description( "fa-vk" )] - Vk, - /// - /// fa-weibo - /// - [Description( "fa-weibo" )] - Weibo, - /// - /// fa-renren - /// - [Description( "fa-renren" )] - Renren, - /// - /// fa-pagelines - /// - [Description( "fa-pagelines" )] - Pagelines, - /// - /// fa-stack-exchange - /// - [Description( "fa-stack-exchange" )] - StackExchange, - /// - /// fa-arrow-circle-o-right - /// - [Description( "fa-arrow-circle-o-right" )] - ArrowCircleORight, - /// - /// fa-arrow-circle-o-left - /// - [Description( "fa-arrow-circle-o-left" )] - ArrowCircleOLeft, - /// - /// fa-caret-square-o-left - /// - [Description( "fa-caret-square-o-left" )] - CaretSquareOLeft, - /// - /// fa-dot-circle-o - /// - [Description( "fa-dot-circle-o" )] - DotCircleO, - /// - /// fa-wheelchair - /// - [Description( "fa-wheelchair" )] - Wheelchair, - /// - /// fa-vimeo-square - /// - [Description( "fa-vimeo-square" )] - VimeoSquare, - /// - /// fa-try - /// - [Description( "fa-try" )] - Try, - /// - /// fa-plus-square-o - /// - [Description( "fa-plus-square-o" )] - PlusSquareO, - /// - /// fa-space-shuttle - /// - [Description( "fa-space-shuttle" )] - SpaceShuttle, - /// - /// fa-slack - /// - [Description( "fa-slack" )] - Slack, - /// - /// fa-envelope-square - /// - [Description( "fa-envelope-square" )] - EnvelopeSquare, - /// - /// fa-wordpress - /// - [Description( "fa-wordpress" )] - Wordpress, - /// - /// fa-openid - /// - [Description( "fa-openid" )] - Openid, - /// - /// fa-university - /// - [Description( "fa-university" )] - University, - /// - /// fa-graduation-cap - /// - [Description( "fa-graduation-cap" )] - GraduationCap, - /// - /// fa-yahoo - /// - [Description( "fa-yahoo" )] - Yahoo, - /// - /// fa-google - /// - [Description( "fa-google" )] - Google, - /// - /// fa-reddit - /// - [Description( "fa-reddit" )] - Reddit, - /// - /// fa-reddit-square - /// - [Description( "fa-reddit-square" )] - RedditSquare, - /// - /// fa-stumbleupon-circle - /// - [Description( "fa-stumbleupon-circle" )] - StumbleuponCircle, - /// - /// fa-stumbleupon - /// - [Description( "fa-stumbleupon" )] - Stumbleupon, - /// - /// fa-delicious - /// - [Description( "fa-delicious" )] - Delicious, - /// - /// fa-digg - /// - [Description( "fa-digg" )] - Digg, - /// - /// fa-pied-piper-pp - /// - [Description( "fa-pied-piper-pp" )] - PiedPiperPp, - /// - /// fa-pied-piper-alt - /// - [Description( "fa-pied-piper-alt" )] - PiedPiperAlt, - /// - /// fa-drupal - /// - [Description( "fa-drupal" )] - Drupal, - /// - /// fa-joomla - /// - [Description( "fa-joomla" )] - Joomla, - /// - /// fa-language - /// - [Description( "fa-language" )] - Language, - /// - /// fa-fax - /// - [Description( "fa-fax" )] - Fax, - /// - /// fa-building - /// - [Description( "fa-building" )] - Building, - /// - /// fa-child - /// - [Description( "fa-child" )] - Child, - /// - /// fa-paw - /// - [Description( "fa-paw" )] - Paw, - /// - /// fa-spoon - /// - [Description( "fa-spoon" )] - Spoon, - /// - /// fa-cube - /// - [Description( "fa-cube" )] - Cube, - /// - /// fa-cubes - /// - [Description( "fa-cubes" )] - Cubes, - /// - /// fa-behance - /// - [Description( "fa-behance" )] - Behance, - /// - /// fa-behance-square - /// - [Description( "fa-behance-square" )] - BehanceSquare, - /// - /// fa-steam - /// - [Description( "fa-steam" )] - Steam, - /// - /// fa-steam-square - /// - [Description( "fa-steam-square" )] - SteamSquare, - /// - /// fa-recycle - /// - [Description( "fa-recycle" )] - Recycle, - /// - /// fa-car - /// - [Description( "fa-car" )] - Car, - /// - /// fa-taxi - /// - [Description( "fa-taxi" )] - Taxi, - /// - /// fa-tree - /// - [Description( "fa-tree" )] - Tree, - /// - /// fa-spotify - /// - [Description( "fa-spotify" )] - Spotify, - /// - /// fa-deviantart - /// - [Description( "fa-deviantart" )] - Deviantart, - /// - /// fa-soundcloud - /// - [Description( "fa-soundcloud" )] - Soundcloud, - /// - /// fa-database - /// - [Description( "fa-database" )] - Database, - /// - /// fa-file-pdf-o - /// - [Description( "fa-file-pdf-o" )] - FilePdfO, - /// - /// fa-file-word-o - /// - [Description( "fa-file-word-o" )] - FileWordO, - /// - /// fa-file-excel-o - /// - [Description( "fa-file-excel-o" )] - FileExcelO, - /// - /// fa-file-powerpoint-o - /// - [Description( "fa-file-powerpoint-o" )] - FilePowerpointO, - /// - /// fa-file-image-o - /// - [Description( "fa-file-image-o" )] - FileImageO, - /// - /// fa-file-archive-o - /// - [Description( "fa-file-archive-o" )] - FileArchiveO, - /// - /// fa-file-audio-o - /// - [Description( "fa-file-audio-o" )] - FileAudioO, - /// - /// fa-file-video-o - /// - [Description( "fa-file-video-o" )] - FileVideoO, - /// - /// fa-file-code-o - /// - [Description( "fa-file-code-o" )] - FileCodeO, - /// - /// fa-vine - /// - [Description( "fa-vine" )] - Vine, - /// - /// fa-codepen - /// - [Description( "fa-codepen" )] - Codepen, - /// - /// fa-jsfiddle - /// - [Description( "fa-jsfiddle" )] - Jsfiddle, - /// - /// fa-life-ring - /// - [Description( "fa-life-ring" )] - LifeRing, - /// - /// fa-circle-o-notch - /// - [Description( "fa-circle-o-notch" )] - CircleONotch, - /// - /// fa-rebel - /// - [Description( "fa-rebel" )] - Rebel, - /// - /// fa-empire - /// - [Description( "fa-empire" )] - Empire, - /// - /// fa-git-square - /// - [Description( "fa-git-square" )] - GitSquare, - /// - /// fa-git - /// - [Description( "fa-git" )] - Git, - /// - /// fa-hacker-news - /// - [Description( "fa-hacker-news" )] - HackerNews, - /// - /// fa-tencent-weibo - /// - [Description( "fa-tencent-weibo" )] - TencentWeibo, - /// - /// fa-qq - /// - [Description( "fa-qq" )] - Qq, - /// - /// fa-weixin - /// - [Description( "fa-weixin" )] - Weixin, - /// - /// fa-paper-plane - /// - [Description( "fa-paper-plane" )] - PaperPlane, - /// - /// fa-paper-plane-o - /// - [Description( "fa-paper-plane-o" )] - PaperPlaneO, - /// - /// fa-history - /// - [Description( "fa-history" )] - History, - /// - /// fa-circle-thin - /// - [Description( "fa-circle-thin" )] - CircleThin, - /// - /// fa-header - /// - [Description( "fa-header" )] - Header, - /// - /// fa-paragraph - /// - [Description( "fa-paragraph" )] - Paragraph, - /// - /// fa-sliders - /// - [Description( "fa-sliders" )] - Sliders, - /// - /// fa-share-alt - /// - [Description( "fa-share-alt" )] - ShareAlt, - /// - /// fa-share-alt-square - /// - [Description( "fa-share-alt-square" )] - ShareAltSquare, - /// - /// fa-bomb - /// - [Description( "fa-bomb" )] - Bomb, - /// - /// fa-futbol-o - /// - [Description( "fa-futbol-o" )] - FutbolO, - /// - /// fa-tty - /// - [Description( "fa-tty" )] - Tty, - /// - /// fa-binoculars - /// - [Description( "fa-binoculars" )] - Binoculars, - /// - /// fa-plug - /// - [Description( "fa-plug" )] - Plug, - /// - /// fa-slideshare - /// - [Description( "fa-slideshare" )] - Slideshare, - /// - /// fa-twitch - /// - [Description( "fa-twitch" )] - Twitch, - /// - /// fa-yelp - /// - [Description( "fa-yelp" )] - Yelp, - /// - /// fa-newspaper-o - /// - [Description( "fa-newspaper-o" )] - NewspaperO, - /// - /// fa-wifi - /// - [Description( "fa-wifi" )] - Wifi, - /// - /// fa-calculator - /// - [Description( "fa-calculator" )] - Calculator, - /// - /// fa-paypal - /// - [Description( "fa-paypal" )] - Paypal, - /// - /// fa-google-wallet - /// - [Description( "fa-google-wallet" )] - GoogleWallet, - /// - /// fa-cc-visa - /// - [Description( "fa-cc-visa" )] - CcVisa, - /// - /// fa-cc-mastercard - /// - [Description( "fa-cc-mastercard" )] - CcMastercard, - /// - /// fa-cc-discover - /// - [Description( "fa-cc-discover" )] - CcDiscover, - /// - /// fa-cc-amex - /// - [Description( "fa-cc-amex" )] - CcAmex, - /// - /// fa-cc-paypal - /// - [Description( "fa-cc-paypal" )] - CcPaypal, - /// - /// fa-cc-stripe - /// - [Description( "fa-cc-stripe" )] - CcStripe, - /// - /// fa-bell-slash - /// - [Description( "fa-bell-slash" )] - BellSlash, - /// - /// fa-bell-slash-o - /// - [Description( "fa-bell-slash-o" )] - BellSlashO, - /// - /// fa-trash - /// - [Description( "fa-trash" )] - Trash, - /// - /// fa-copyright - /// - [Description( "fa-copyright" )] - Copyright, - /// - /// fa-at - /// - [Description( "fa-at" )] - At, - /// - /// fa-eyedropper - /// - [Description( "fa-eyedropper" )] - Eyedropper, - /// - /// fa-paint-brush - /// - [Description( "fa-paint-brush" )] - PaintBrush, - /// - /// fa-birthday-cake - /// - [Description( "fa-birthday-cake" )] - BirthdayCake, - /// - /// fa-area-chart - /// - [Description( "fa-area-chart" )] - AreaChart, - /// - /// fa-pie-chart - /// - [Description( "fa-pie-chart" )] - PieChart, - /// - /// fa-line-chart - /// - [Description( "fa-line-chart" )] - LineChart, - /// - /// fa-lastfm - /// - [Description( "fa-lastfm" )] - Lastfm, - /// - /// fa-lastfm-square - /// - [Description( "fa-lastfm-square" )] - LastfmSquare, - /// - /// fa-toggle-off - /// - [Description( "fa-toggle-off" )] - ToggleOff, - /// - /// fa-toggle-on - /// - [Description( "fa-toggle-on" )] - ToggleOn, - /// - /// fa-bicycle - /// - [Description( "fa-bicycle" )] - Bicycle, - /// - /// fa-bus - /// - [Description( "fa-bus" )] - Bus, - /// - /// fa-ioxhost - /// - [Description( "fa-ioxhost" )] - Ioxhost, - /// - /// fa-angellist - /// - [Description( "fa-angellist" )] - Angellist, - /// - /// fa-cc - /// - [Description( "fa-cc" )] - Cc, - /// - /// fa-ils - /// - [Description( "fa-ils" )] - Ils, - /// - /// fa-meanpath - /// - [Description( "fa-meanpath" )] - Meanpath, - /// - /// fa-buysellads - /// - [Description( "fa-buysellads" )] - Buysellads, - /// - /// fa-connectdevelop - /// - [Description( "fa-connectdevelop" )] - Connectdevelop, - /// - /// fa-dashcube - /// - [Description( "fa-dashcube" )] - Dashcube, - /// - /// fa-forumbee - /// - [Description( "fa-forumbee" )] - Forumbee, - /// - /// fa-leanpub - /// - [Description( "fa-leanpub" )] - Leanpub, - /// - /// fa-sellsy - /// - [Description( "fa-sellsy" )] - Sellsy, - /// - /// fa-shirtsinbulk - /// - [Description( "fa-shirtsinbulk" )] - Shirtsinbulk, - /// - /// fa-simplybuilt - /// - [Description( "fa-simplybuilt" )] - Simplybuilt, - /// - /// fa-skyatlas - /// - [Description( "fa-skyatlas" )] - Skyatlas, - /// - /// fa-cart-plus - /// - [Description( "fa-cart-plus" )] - CartPlus, - /// - /// fa-cart-arrow-down - /// - [Description( "fa-cart-arrow-down" )] - CartArrowDown, - /// - /// fa-diamond - /// - [Description( "fa-diamond" )] - Diamond, - /// - /// fa-ship - /// - [Description( "fa-ship" )] - Ship, - /// - /// fa-user-secret - /// - [Description( "fa-user-secret" )] - UserSecret, - /// - /// fa-motorcycle - /// - [Description( "fa-motorcycle" )] - Motorcycle, - /// - /// fa-street-view - /// - [Description( "fa-street-view" )] - StreetView, - /// - /// fa-heartbeat - /// - [Description( "fa-heartbeat" )] - Heartbeat, - /// - /// fa-venus - /// - [Description( "fa-venus" )] - Venus, - /// - /// fa-mars - /// - [Description( "fa-mars" )] - Mars, - /// - /// fa-mercury - /// - [Description( "fa-mercury" )] - Mercury, - /// - /// fa-transgender - /// - [Description( "fa-transgender" )] - Transgender, - /// - /// fa-transgender-alt - /// - [Description( "fa-transgender-alt" )] - TransgenderAlt, - /// - /// fa-venus-double - /// - [Description( "fa-venus-double" )] - VenusDouble, - /// - /// fa-mars-double - /// - [Description( "fa-mars-double" )] - MarsDouble, - /// - /// fa-venus-mars - /// - [Description( "fa-venus-mars" )] - VenusMars, - /// - /// fa-mars-stroke - /// - [Description( "fa-mars-stroke" )] - MarsStroke, - /// - /// fa-mars-stroke-v - /// - [Description( "fa-mars-stroke-v" )] - MarsStrokeV, - /// - /// fa-mars-stroke-h - /// - [Description( "fa-mars-stroke-h" )] - MarsStrokeH, - /// - /// fa-neuter - /// - [Description( "fa-neuter" )] - Neuter, - /// - /// fa-genderless - /// - [Description( "fa-genderless" )] - Genderless, - /// - /// fa-facebook-official - /// - [Description( "fa-facebook-official" )] - FacebookOfficial, - /// - /// fa-pinterest-p - /// - [Description( "fa-pinterest-p" )] - PinterestP, - /// - /// fa-whatsapp - /// - [Description( "fa-whatsapp" )] - Whatsapp, - /// - /// fa-server - /// - [Description( "fa-server" )] - Server, - /// - /// fa-user-plus - /// - [Description( "fa-user-plus" )] - UserPlus, - /// - /// fa-user-times - /// - [Description( "fa-user-times" )] - UserTimes, - /// - /// fa-bed - /// - [Description( "fa-bed" )] - Bed, - /// - /// fa-viacoin - /// - [Description( "fa-viacoin" )] - Viacoin, - /// - /// fa-train - /// - [Description( "fa-train" )] - Train, - /// - /// fa-subway - /// - [Description( "fa-subway" )] - Subway, - /// - /// fa-medium - /// - [Description( "fa-medium" )] - Medium, - /// - /// fa-y-combinator - /// - [Description( "fa-y-combinator" )] - YCombinator, - /// - /// fa-optin-monster - /// - [Description( "fa-optin-monster" )] - OptinMonster, - /// - /// fa-opencart - /// - [Description( "fa-opencart" )] - Opencart, - /// - /// fa-expeditedssl - /// - [Description( "fa-expeditedssl" )] - Expeditedssl, - /// - /// fa-battery-full - /// - [Description( "fa-battery-full" )] - BatteryFull, - /// - /// fa-battery-three-quarters - /// - [Description( "fa-battery-three-quarters" )] - BatteryThreeQuarters, - /// - /// fa-battery-half - /// - [Description( "fa-battery-half" )] - BatteryHalf, - /// - /// fa-battery-quarter - /// - [Description( "fa-battery-quarter" )] - BatteryQuarter, - /// - /// fa-battery-empty - /// - [Description( "fa-battery-empty" )] - BatteryEmpty, - /// - /// fa-mouse-pointer - /// - [Description( "fa-mouse-pointer" )] - MousePointer, - /// - /// fa-i-cursor - /// - [Description( "fa-i-cursor" )] - ICursor, - /// - /// fa-object-group - /// - [Description( "fa-object-group" )] - ObjectGroup, - /// - /// fa-object-ungroup - /// - [Description( "fa-object-ungroup" )] - ObjectUngroup, - /// - /// fa-sticky-note - /// - [Description( "fa-sticky-note" )] - StickyNote, - /// - /// fa-sticky-note-o - /// - [Description( "fa-sticky-note-o" )] - StickyNoteO, - /// - /// fa-cc-jcb - /// - [Description( "fa-cc-jcb" )] - CcJcb, - /// - /// fa-cc-diners-club - /// - [Description( "fa-cc-diners-club" )] - CcDinersClub, - /// - /// fa-clone - /// - [Description( "fa-clone" )] - Clone, - /// - /// fa-balance-scale - /// - [Description( "fa-balance-scale" )] - BalanceScale, - /// - /// fa-hourglass-o - /// - [Description( "fa-hourglass-o" )] - HourglassO, - /// - /// fa-hourglass-start - /// - [Description( "fa-hourglass-start" )] - HourglassStart, - /// - /// fa-hourglass-half - /// - [Description( "fa-hourglass-half" )] - HourglassHalf, - /// - /// fa-hourglass-end - /// - [Description( "fa-hourglass-end" )] - HourglassEnd, - /// - /// fa-hourglass - /// - [Description( "fa-hourglass" )] - Hourglass, - /// - /// fa-hand-rock-o - /// - [Description( "fa-hand-rock-o" )] - HandRockO, - /// - /// fa-hand-paper-o - /// - [Description( "fa-hand-paper-o" )] - HandPaperO, - /// - /// fa-hand-scissors-o - /// - [Description( "fa-hand-scissors-o" )] - HandScissorsO, - /// - /// fa-hand-lizard-o - /// - [Description( "fa-hand-lizard-o" )] - HandLizardO, - /// - /// fa-hand-spock-o - /// - [Description( "fa-hand-spock-o" )] - HandSpockO, - /// - /// fa-hand-pointer-o - /// - [Description( "fa-hand-pointer-o" )] - HandPointerO, - /// - /// fa-hand-peace-o - /// - [Description( "fa-hand-peace-o" )] - HandPeaceO, - /// - /// fa-trademark - /// - [Description( "fa-trademark" )] - Trademark, - /// - /// fa-registered - /// - [Description( "fa-registered" )] - Registered, - /// - /// fa-creative-commons - /// - [Description( "fa-creative-commons" )] - CreativeCommons, - /// - /// fa-gg - /// - [Description( "fa-gg" )] - Gg, - /// - /// fa-gg-circle - /// - [Description( "fa-gg-circle" )] - GgCircle, - /// - /// fa-tripadvisor - /// - [Description( "fa-tripadvisor" )] - Tripadvisor, - /// - /// fa-odnoklassniki - /// - [Description( "fa-odnoklassniki" )] - Odnoklassniki, - /// - /// fa-odnoklassniki-square - /// - [Description( "fa-odnoklassniki-square" )] - OdnoklassnikiSquare, - /// - /// fa-get-pocket - /// - [Description( "fa-get-pocket" )] - GetPocket, - /// - /// fa-wikipedia-w - /// - [Description( "fa-wikipedia-w" )] - WikipediaW, - /// - /// fa-safari - /// - [Description( "fa-safari" )] - Safari, - /// - /// fa-chrome - /// - [Description( "fa-chrome" )] - Chrome, - /// - /// fa-firefox - /// - [Description( "fa-firefox" )] - Firefox, - /// - /// fa-opera - /// - [Description( "fa-opera" )] - Opera, - /// - /// fa-internet-explorer - /// - [Description( "fa-internet-explorer" )] - InternetExplorer, - /// - /// fa-television - /// - [Description( "fa-television" )] - Television, - /// - /// fa-contao - /// - [Description( "fa-contao" )] - Contao, - /// - /// fa-500px - /// - [Description( "fa-500px" )] - _500px, - /// - /// fa-amazon - /// - [Description( "fa-amazon" )] - Amazon, - /// - /// fa-calendar-plus-o - /// - [Description( "fa-calendar-plus-o" )] - CalendarPlusO, - /// - /// fa-calendar-minus-o - /// - [Description( "fa-calendar-minus-o" )] - CalendarMinusO, - /// - /// fa-calendar-times-o - /// - [Description( "fa-calendar-times-o" )] - CalendarTimesO, - /// - /// fa-calendar-check-o - /// - [Description( "fa-calendar-check-o" )] - CalendarCheckO, - /// - /// fa-industry - /// - [Description( "fa-industry" )] - Industry, - /// - /// fa-map-pin - /// - [Description( "fa-map-pin" )] - MapPin, - /// - /// fa-map-signs - /// - [Description( "fa-map-signs" )] - MapSigns, - /// - /// fa-map-o - /// - [Description( "fa-map-o" )] - MapO, - /// - /// fa-map - /// - [Description( "fa-map" )] - Map, - /// - /// fa-commenting - /// - [Description( "fa-commenting" )] - Commenting, - /// - /// fa-commenting-o - /// - [Description( "fa-commenting-o" )] - CommentingO, - /// - /// fa-houzz - /// - [Description( "fa-houzz" )] - Houzz, - /// - /// fa-vimeo - /// - [Description( "fa-vimeo" )] - Vimeo, - /// - /// fa-black-tie - /// - [Description( "fa-black-tie" )] - BlackTie, - /// - /// fa-fonticons - /// - [Description( "fa-fonticons" )] - Fonticons, - /// - /// fa-reddit-alien - /// - [Description( "fa-reddit-alien" )] - RedditAlien, - /// - /// fa-edge - /// - [Description( "fa-edge" )] - Edge, - /// - /// fa-credit-card-alt - /// - [Description( "fa-credit-card-alt" )] - CreditCardAlt, - /// - /// fa-codiepie - /// - [Description( "fa-codiepie" )] - Codiepie, - /// - /// fa-modx - /// - [Description( "fa-modx" )] - Modx, - /// - /// fa-fort-awesome - /// - [Description( "fa-fort-awesome" )] - FortAwesome, - /// - /// fa-usb - /// - [Description( "fa-usb" )] - Usb, - /// - /// fa-product-hunt - /// - [Description( "fa-product-hunt" )] - ProductHunt, - /// - /// fa-mixcloud - /// - [Description( "fa-mixcloud" )] - Mixcloud, - /// - /// fa-scribd - /// - [Description( "fa-scribd" )] - Scribd, - /// - /// fa-pause-circle - /// - [Description( "fa-pause-circle" )] - PauseCircle, - /// - /// fa-pause-circle-o - /// - [Description( "fa-pause-circle-o" )] - PauseCircleO, - /// - /// fa-stop-circle - /// - [Description( "fa-stop-circle" )] - StopCircle, - /// - /// fa-stop-circle-o - /// - [Description( "fa-stop-circle-o" )] - StopCircleO, - /// - /// fa-shopping-bag - /// - [Description( "fa-shopping-bag" )] - ShoppingBag, - /// - /// fa-shopping-basket - /// - [Description( "fa-shopping-basket" )] - ShoppingBasket, - /// - /// fa-hashtag - /// - [Description( "fa-hashtag" )] - Hashtag, - /// - /// fa-bluetooth - /// - [Description( "fa-bluetooth" )] - Bluetooth, - /// - /// fa-bluetooth-b - /// - [Description( "fa-bluetooth-b" )] - BluetoothB, - /// - /// fa-percent - /// - [Description( "fa-percent" )] - Percent, - /// - /// fa-gitlab - /// - [Description( "fa-gitlab" )] - Gitlab, - /// - /// fa-wpbeginner - /// - [Description( "fa-wpbeginner" )] - Wpbeginner, - /// - /// fa-wpforms - /// - [Description( "fa-wpforms" )] - Wpforms, - /// - /// fa-envira - /// - [Description( "fa-envira" )] - Envira, - /// - /// fa-universal-access - /// - [Description( "fa-universal-access" )] - UniversalAccess, - /// - /// fa-wheelchair-alt - /// - [Description( "fa-wheelchair-alt" )] - WheelchairAlt, - /// - /// fa-question-circle-o - /// - [Description( "fa-question-circle-o" )] - QuestionCircleO, - /// - /// fa-blind - /// - [Description( "fa-blind" )] - Blind, - /// - /// fa-audio-description - /// - [Description( "fa-audio-description" )] - AudioDescription, - /// - /// fa-volume-control-phone - /// - [Description( "fa-volume-control-phone" )] - VolumeControlPhone, - /// - /// fa-braille - /// - [Description( "fa-braille" )] - Braille, - /// - /// fa-assistive-listening-systems - /// - [Description( "fa-assistive-listening-systems" )] - AssistiveListeningSystems, - /// - /// fa-american-sign-language-interpreting - /// - [Description( "fa-american-sign-language-interpreting" )] - AmericanSignLanguageInterpreting, - /// - /// fa-deaf - /// - [Description( "fa-deaf" )] - Deaf, - /// - /// fa-glide - /// - [Description( "fa-glide" )] - Glide, - /// - /// fa-glide-g - /// - [Description( "fa-glide-g" )] - GlideG, - /// - /// fa-sign-language - /// - [Description( "fa-sign-language" )] - SignLanguage, - /// - /// fa-low-vision - /// - [Description( "fa-low-vision" )] - LowVision, - /// - /// fa-viadeo - /// - [Description( "fa-viadeo" )] - Viadeo, - /// - /// fa-viadeo-square - /// - [Description( "fa-viadeo-square" )] - ViadeoSquare, - /// - /// fa-snapchat - /// - [Description( "fa-snapchat" )] - Snapchat, - /// - /// fa-snapchat-ghost - /// - [Description( "fa-snapchat-ghost" )] - SnapchatGhost, - /// - /// fa-snapchat-square - /// - [Description( "fa-snapchat-square" )] - SnapchatSquare, - /// - /// fa-pied-piper - /// - [Description( "fa-pied-piper" )] - PiedPiper, - /// - /// fa-first-order - /// - [Description( "fa-first-order" )] - FirstOrder, - /// - /// fa-yoast - /// - [Description( "fa-yoast" )] - Yoast, - /// - /// fa-themeisle - /// - [Description( "fa-themeisle" )] - Themeisle, - /// - /// fa-google-plus-official - /// - [Description( "fa-google-plus-official" )] - GooglePlusOfficial, - /// - /// fa-font-awesome - /// - [Description( "fa-font-awesome" )] - FontAwesome, - /// - /// fa-handshake-o - /// - [Description( "fa-handshake-o" )] - HandshakeO, - /// - /// fa-envelope-open - /// - [Description( "fa-envelope-open" )] - EnvelopeOpen, - /// - /// fa-envelope-open-o - /// - [Description( "fa-envelope-open-o" )] - EnvelopeOpenO, - /// - /// fa-linode - /// - [Description( "fa-linode" )] - Linode, - /// - /// fa-address-book - /// - [Description( "fa-address-book" )] - AddressBook, - /// - /// fa-address-book-o - /// - [Description( "fa-address-book-o" )] - AddressBookO, - /// - /// fa-address-card - /// - [Description( "fa-address-card" )] - AddressCard, - /// - /// fa-address-card-o - /// - [Description( "fa-address-card-o" )] - AddressCardO, - /// - /// fa-user-circle - /// - [Description( "fa-user-circle" )] - UserCircle, - /// - /// fa-user-circle-o - /// - [Description( "fa-user-circle-o" )] - UserCircleO, - /// - /// fa-user-o - /// - [Description( "fa-user-o" )] - UserO, - /// - /// fa-id-badge - /// - [Description( "fa-id-badge" )] - IdBadge, - /// - /// fa-id-card - /// - [Description( "fa-id-card" )] - IdCard, - /// - /// fa-id-card-o - /// - [Description( "fa-id-card-o" )] - IdCardO, - /// - /// fa-quora - /// - [Description( "fa-quora" )] - Quora, - /// - /// fa-free-code-camp - /// - [Description( "fa-free-code-camp" )] - FreeCodeCamp, - /// - /// fa-telegram - /// - [Description( "fa-telegram" )] - Telegram, - /// - /// fa-thermometer-full - /// - [Description( "fa-thermometer-full" )] - ThermometerFull, - /// - /// fa-thermometer-three-quarters - /// - [Description( "fa-thermometer-three-quarters" )] - ThermometerThreeQuarters, - /// - /// fa-thermometer-half - /// - [Description( "fa-thermometer-half" )] - ThermometerHalf, - /// - /// fa-thermometer-quarter - /// - [Description( "fa-thermometer-quarter" )] - ThermometerQuarter, - /// - /// fa-thermometer-empty - /// - [Description( "fa-thermometer-empty" )] - ThermometerEmpty, - /// - /// fa-shower - /// - [Description( "fa-shower" )] - Shower, - /// - /// fa-bath - /// - [Description( "fa-bath" )] - Bath, - /// - /// fa-podcast - /// - [Description( "fa-podcast" )] - Podcast, - /// - /// fa-window-maximize - /// - [Description( "fa-window-maximize" )] - WindowMaximize, - /// - /// fa-window-minimize - /// - [Description( "fa-window-minimize" )] - WindowMinimize, - /// - /// fa-window-restore - /// - [Description( "fa-window-restore" )] - WindowRestore, - /// - /// fa-window-close - /// - [Description( "fa-window-close" )] - WindowClose, - /// - /// fa-window-close-o - /// - [Description( "fa-window-close-o" )] - WindowCloseO, - /// - /// fa-bandcamp - /// - [Description( "fa-bandcamp" )] - Bandcamp, - /// - /// fa-grav - /// - [Description( "fa-grav" )] - Grav, - /// - /// fa-etsy - /// - [Description( "fa-etsy" )] - Etsy, - /// - /// fa-imdb - /// - [Description( "fa-imdb" )] - Imdb, - /// - /// fa-ravelry - /// - [Description( "fa-ravelry" )] - Ravelry, - /// - /// fa-eercast - /// - [Description( "fa-eercast" )] - Eercast, - /// - /// fa-microchip - /// - [Description( "fa-microchip" )] - Microchip, - /// - /// fa-snowflake-o - /// - [Description( "fa-snowflake-o" )] - SnowflakeO, - /// - /// fa-superpowers - /// - [Description( "fa-superpowers" )] - Superpowers, - /// - /// fa-wpexplorer - /// - [Description( "fa-wpexplorer" )] - Wpexplorer, - /// - /// fa-meetup - /// - [Description( "fa-meetup" )] - Meetup, - } - - /// - /// FontAwesome图标扩展 - /// - public static class FontAwesomeIconExtensions { - /// - /// 获取图标 - /// - public static string GetIcon( this FontAwesomeIcon icon ) { - return $"fa {icon.Description()}"; - } - - /// - /// 获取图标 - /// - public static string GetIcon( this FontAwesomeIcon? icon ) { - return icon == null ? string.Empty : icon.Value.GetIcon(); - } - } -} diff --git a/src/Util.Ui.Core/Enums/IconSize.cs b/src/Util.Ui.Core/Enums/IconSize.cs deleted file mode 100644 index 17dd98d3d..000000000 --- a/src/Util.Ui.Core/Enums/IconSize.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.ComponentModel; - -namespace Util.Ui.Enums { - /// - /// 图标大小 - /// - public enum IconSize { - /// - /// 1.3倍 - /// - [Description( "fa-lg" )] - Large, - /// - /// 2倍 - /// - [Description( "fa-2x" )] - Large2X, - /// - /// 3倍 - /// - [Description( "fa-3x" )] - Large3X, - /// - /// 4倍 - /// - [Description( "fa-4x" )] - Large4X, - /// - /// 5倍 - /// - [Description( "fa-5x" )] - Large5X - } -} diff --git a/src/Util.Ui.Core/Enums/MaterialIcon.cs b/src/Util.Ui.Core/Enums/MaterialIcon.cs deleted file mode 100644 index f692fc76e..000000000 --- a/src/Util.Ui.Core/Enums/MaterialIcon.cs +++ /dev/null @@ -1,4669 +0,0 @@ -using System.ComponentModel; - -namespace Util.Ui.Enums { - /// - /// Material图标 - /// - public enum MaterialIcon { - /// - /// 3d_rotation - /// - [Description( "3d_rotation" )] - _3d_rotation, - /// - /// ac_unit - /// - [Description( "ac_unit" )] - Ac_Unit, - /// - /// access_alarm - /// - [Description( "access_alarm" )] - Access_Alarm, - /// - /// access_alarms - /// - [Description( "access_alarms" )] - Access_Alarms, - /// - /// access_time - /// - [Description( "access_time" )] - Access_Time, - /// - /// accessibility - /// - [Description( "accessibility" )] - Accessibility, - /// - /// accessible - /// - [Description( "accessible" )] - Accessible, - /// - /// account_balance - /// - [Description( "account_balance" )] - Account_Balance, - /// - /// account_balance_wallet - /// - [Description( "account_balance_wallet" )] - Account_Balance_Wallet, - /// - /// account_box - /// - [Description( "account_box" )] - Account_Box, - /// - /// account_circle - /// - [Description( "account_circle" )] - Account_Circle, - /// - /// adb - /// - [Description( "adb" )] - Adb, - /// - /// add - /// - [Description( "add" )] - Add, - /// - /// add_a_photo - /// - [Description( "add_a_photo" )] - Add_A_Photo, - /// - /// add_alarm - /// - [Description( "add_alarm" )] - Add_Alarm, - /// - /// add_alert - /// - [Description( "add_alert" )] - Add_Alert, - /// - /// add_box - /// - [Description( "add_box" )] - Add_Box, - /// - /// add_circle - /// - [Description( "add_circle" )] - Add_Circle, - /// - /// add_circle_outline - /// - [Description( "add_circle_outline" )] - Add_Circle_Outline, - /// - /// add_location - /// - [Description( "add_location" )] - Add_Location, - /// - /// add_shopping_cart - /// - [Description( "add_shopping_cart" )] - Add_Shopping_Cart, - /// - /// add_to_photos - /// - [Description( "add_to_photos" )] - Add_To_Photos, - /// - /// add_to_queue - /// - [Description( "add_to_queue" )] - Add_To_Queue, - /// - /// adjust - /// - [Description( "adjust" )] - Adjust, - /// - /// airline_seat_flat - /// - [Description( "airline_seat_flat" )] - Airline_Seat_Flat, - /// - /// airline_seat_flat_angled - /// - [Description( "airline_seat_flat_angled" )] - Airline_Seat_Flat_Angled, - /// - /// airline_seat_individual_suite - /// - [Description( "airline_seat_individual_suite" )] - Airline_Seat_Individual_Suite, - /// - /// airline_seat_legroom_extra - /// - [Description( "airline_seat_legroom_extra" )] - Airline_Seat_Legroom_Extra, - /// - /// airline_seat_legroom_normal - /// - [Description( "airline_seat_legroom_normal" )] - Airline_Seat_Legroom_Normal, - /// - /// airline_seat_legroom_reduced - /// - [Description( "airline_seat_legroom_reduced" )] - Airline_Seat_Legroom_Reduced, - /// - /// airline_seat_recline_extra - /// - [Description( "airline_seat_recline_extra" )] - Airline_Seat_Recline_Extra, - /// - /// airline_seat_recline_normal - /// - [Description( "airline_seat_recline_normal" )] - Airline_Seat_Recline_Normal, - /// - /// airplanemode_active - /// - [Description( "airplanemode_active" )] - Airplanemode_Active, - /// - /// airplanemode_inactive - /// - [Description( "airplanemode_inactive" )] - Airplanemode_Inactive, - /// - /// airplay - /// - [Description( "airplay" )] - Airplay, - /// - /// airport_shuttle - /// - [Description( "airport_shuttle" )] - Airport_Shuttle, - /// - /// alarm - /// - [Description( "alarm" )] - Alarm, - /// - /// alarm_add - /// - [Description( "alarm_add" )] - Alarm_Add, - /// - /// alarm_off - /// - [Description( "alarm_off" )] - Alarm_Off, - /// - /// alarm_on - /// - [Description( "alarm_on" )] - Alarm_On, - /// - /// album - /// - [Description( "album" )] - Album, - /// - /// all_inclusive - /// - [Description( "all_inclusive" )] - All_Inclusive, - /// - /// all_out - /// - [Description( "all_out" )] - All_Out, - /// - /// android - /// - [Description( "android" )] - Android, - /// - /// announcement - /// - [Description( "announcement" )] - Announcement, - /// - /// apps - /// - [Description( "apps" )] - Apps, - /// - /// archive - /// - [Description( "archive" )] - Archive, - /// - /// arrow_back - /// - [Description( "arrow_back" )] - Arrow_Back, - /// - /// arrow_downward - /// - [Description( "arrow_downward" )] - Arrow_Downward, - /// - /// arrow_drop_down - /// - [Description( "arrow_drop_down" )] - Arrow_Drop_Down, - /// - /// arrow_drop_down_circle - /// - [Description( "arrow_drop_down_circle" )] - Arrow_Drop_Down_Circle, - /// - /// arrow_drop_up - /// - [Description( "arrow_drop_up" )] - Arrow_Drop_Up, - /// - /// arrow_forward - /// - [Description( "arrow_forward" )] - Arrow_Forward, - /// - /// arrow_upward - /// - [Description( "arrow_upward" )] - Arrow_Upward, - /// - /// art_track - /// - [Description( "art_track" )] - Art_Track, - /// - /// aspect_ratio - /// - [Description( "aspect_ratio" )] - Aspect_Ratio, - /// - /// assessment - /// - [Description( "assessment" )] - Assessment, - /// - /// assignment - /// - [Description( "assignment" )] - Assignment, - /// - /// assignment_ind - /// - [Description( "assignment_ind" )] - Assignment_Ind, - /// - /// assignment_late - /// - [Description( "assignment_late" )] - Assignment_Late, - /// - /// assignment_return - /// - [Description( "assignment_return" )] - Assignment_Return, - /// - /// assignment_returned - /// - [Description( "assignment_returned" )] - Assignment_Returned, - /// - /// assignment_turned_in - /// - [Description( "assignment_turned_in" )] - Assignment_Turned_In, - /// - /// assistant - /// - [Description( "assistant" )] - Assistant, - /// - /// assistant_photo - /// - [Description( "assistant_photo" )] - Assistant_Photo, - /// - /// attach_file - /// - [Description( "attach_file" )] - Attach_File, - /// - /// attach_money - /// - [Description( "attach_money" )] - Attach_Money, - /// - /// attachment - /// - [Description( "attachment" )] - Attachment, - /// - /// audiotrack - /// - [Description( "audiotrack" )] - Audiotrack, - /// - /// autorenew - /// - [Description( "autorenew" )] - Autorenew, - /// - /// av_timer - /// - [Description( "av_timer" )] - Av_Timer, - /// - /// backspace - /// - [Description( "backspace" )] - Backspace, - /// - /// backup - /// - [Description( "backup" )] - Backup, - /// - /// battery_alert - /// - [Description( "battery_alert" )] - Battery_Alert, - /// - /// battery_charging_full - /// - [Description( "battery_charging_full" )] - Battery_Charging_Full, - /// - /// battery_full - /// - [Description( "battery_full" )] - Battery_Full, - /// - /// battery_std - /// - [Description( "battery_std" )] - Battery_Std, - /// - /// battery_unknown - /// - [Description( "battery_unknown" )] - Battery_Unknown, - /// - /// beach_access - /// - [Description( "beach_access" )] - Beach_Access, - /// - /// beenhere - /// - [Description( "beenhere" )] - Beenhere, - /// - /// block - /// - [Description( "block" )] - Block, - /// - /// bluetooth - /// - [Description( "bluetooth" )] - Bluetooth, - /// - /// bluetooth_audio - /// - [Description( "bluetooth_audio" )] - Bluetooth_Audio, - /// - /// bluetooth_connected - /// - [Description( "bluetooth_connected" )] - Bluetooth_Connected, - /// - /// bluetooth_disabled - /// - [Description( "bluetooth_disabled" )] - Bluetooth_Disabled, - /// - /// bluetooth_searching - /// - [Description( "bluetooth_searching" )] - Bluetooth_Searching, - /// - /// blur_circular - /// - [Description( "blur_circular" )] - Blur_Circular, - /// - /// blur_linear - /// - [Description( "blur_linear" )] - Blur_Linear, - /// - /// blur_off - /// - [Description( "blur_off" )] - Blur_Off, - /// - /// blur_on - /// - [Description( "blur_on" )] - Blur_On, - /// - /// book - /// - [Description( "book" )] - Book, - /// - /// bookmark - /// - [Description( "bookmark" )] - Bookmark, - /// - /// bookmark_border - /// - [Description( "bookmark_border" )] - Bookmark_Border, - /// - /// border_all - /// - [Description( "border_all" )] - Border_All, - /// - /// border_bottom - /// - [Description( "border_bottom" )] - Border_Bottom, - /// - /// border_clear - /// - [Description( "border_clear" )] - Border_Clear, - /// - /// border_color - /// - [Description( "border_color" )] - Border_Color, - /// - /// border_horizontal - /// - [Description( "border_horizontal" )] - Border_Horizontal, - /// - /// border_inner - /// - [Description( "border_inner" )] - Border_Inner, - /// - /// border_left - /// - [Description( "border_left" )] - Border_Left, - /// - /// border_outer - /// - [Description( "border_outer" )] - Border_Outer, - /// - /// border_right - /// - [Description( "border_right" )] - Border_Right, - /// - /// border_style - /// - [Description( "border_style" )] - Border_Style, - /// - /// border_top - /// - [Description( "border_top" )] - Border_Top, - /// - /// border_vertical - /// - [Description( "border_vertical" )] - Border_Vertical, - /// - /// branding_watermark - /// - [Description( "branding_watermark" )] - Branding_Watermark, - /// - /// brightness_1 - /// - [Description( "brightness_1" )] - Brightness_1, - /// - /// brightness_2 - /// - [Description( "brightness_2" )] - Brightness_2, - /// - /// brightness_3 - /// - [Description( "brightness_3" )] - Brightness_3, - /// - /// brightness_4 - /// - [Description( "brightness_4" )] - Brightness_4, - /// - /// brightness_5 - /// - [Description( "brightness_5" )] - Brightness_5, - /// - /// brightness_6 - /// - [Description( "brightness_6" )] - Brightness_6, - /// - /// brightness_7 - /// - [Description( "brightness_7" )] - Brightness_7, - /// - /// brightness_auto - /// - [Description( "brightness_auto" )] - Brightness_Auto, - /// - /// brightness_high - /// - [Description( "brightness_high" )] - Brightness_High, - /// - /// brightness_low - /// - [Description( "brightness_low" )] - Brightness_Low, - /// - /// brightness_medium - /// - [Description( "brightness_medium" )] - Brightness_Medium, - /// - /// broken_image - /// - [Description( "broken_image" )] - Broken_Image, - /// - /// brush - /// - [Description( "brush" )] - Brush, - /// - /// bubble_chart - /// - [Description( "bubble_chart" )] - Bubble_Chart, - /// - /// bug_report - /// - [Description( "bug_report" )] - Bug_Report, - /// - /// build - /// - [Description( "build" )] - Build, - /// - /// burst_mode - /// - [Description( "burst_mode" )] - Burst_Mode, - /// - /// business - /// - [Description( "business" )] - Business, - /// - /// business_center - /// - [Description( "business_center" )] - Business_Center, - /// - /// cached - /// - [Description( "cached" )] - Cached, - /// - /// cake - /// - [Description( "cake" )] - Cake, - /// - /// call - /// - [Description( "call" )] - Call, - /// - /// call_end - /// - [Description( "call_end" )] - Call_End, - /// - /// call_made - /// - [Description( "call_made" )] - Call_Made, - /// - /// call_merge - /// - [Description( "call_merge" )] - Call_Merge, - /// - /// call_missed - /// - [Description( "call_missed" )] - Call_Missed, - /// - /// call_missed_outgoing - /// - [Description( "call_missed_outgoing" )] - Call_Missed_Outgoing, - /// - /// call_received - /// - [Description( "call_received" )] - Call_Received, - /// - /// call_split - /// - [Description( "call_split" )] - Call_Split, - /// - /// call_to_action - /// - [Description( "call_to_action" )] - Call_To_Action, - /// - /// camera - /// - [Description( "camera" )] - Camera, - /// - /// camera_alt - /// - [Description( "camera_alt" )] - Camera_Alt, - /// - /// camera_enhance - /// - [Description( "camera_enhance" )] - Camera_Enhance, - /// - /// camera_front - /// - [Description( "camera_front" )] - Camera_Front, - /// - /// camera_rear - /// - [Description( "camera_rear" )] - Camera_Rear, - /// - /// camera_roll - /// - [Description( "camera_roll" )] - Camera_Roll, - /// - /// cancel - /// - [Description( "cancel" )] - Cancel, - /// - /// card_giftcard - /// - [Description( "card_giftcard" )] - Card_Giftcard, - /// - /// card_membership - /// - [Description( "card_membership" )] - Card_Membership, - /// - /// card_travel - /// - [Description( "card_travel" )] - Card_Travel, - /// - /// casino - /// - [Description( "casino" )] - Casino, - /// - /// cast - /// - [Description( "cast" )] - Cast, - /// - /// cast_connected - /// - [Description( "cast_connected" )] - Cast_Connected, - /// - /// center_focus_strong - /// - [Description( "center_focus_strong" )] - Center_Focus_Strong, - /// - /// center_focus_weak - /// - [Description( "center_focus_weak" )] - Center_Focus_Weak, - /// - /// change_history - /// - [Description( "change_history" )] - Change_History, - /// - /// chat - /// - [Description( "chat" )] - Chat, - /// - /// chat_bubble - /// - [Description( "chat_bubble" )] - Chat_Bubble, - /// - /// chat_bubble_outline - /// - [Description( "chat_bubble_outline" )] - Chat_Bubble_Outline, - /// - /// check - /// - [Description( "check" )] - Check, - /// - /// check_box - /// - [Description( "check_box" )] - Check_Box, - /// - /// check_box_outline_blank - /// - [Description( "check_box_outline_blank" )] - Check_Box_Outline_Blank, - /// - /// check_circle - /// - [Description( "check_circle" )] - Check_Circle, - /// - /// chevron_left - /// - [Description( "chevron_left" )] - Chevron_Left, - /// - /// chevron_right - /// - [Description( "chevron_right" )] - Chevron_Right, - /// - /// child_care - /// - [Description( "child_care" )] - Child_Care, - /// - /// child_friendly - /// - [Description( "child_friendly" )] - Child_Friendly, - /// - /// chrome_reader_mode - /// - [Description( "chrome_reader_mode" )] - Chrome_Reader_Mode, - /// - /// class - /// - [Description( "class" )] - Class, - /// - /// clear - /// - [Description( "clear" )] - Clear, - /// - /// clear_all - /// - [Description( "clear_all" )] - Clear_All, - /// - /// close - /// - [Description( "close" )] - Close, - /// - /// closed_caption - /// - [Description( "closed_caption" )] - Closed_Caption, - /// - /// cloud - /// - [Description( "cloud" )] - Cloud, - /// - /// cloud_circle - /// - [Description( "cloud_circle" )] - Cloud_Circle, - /// - /// cloud_done - /// - [Description( "cloud_done" )] - Cloud_Done, - /// - /// cloud_download - /// - [Description( "cloud_download" )] - Cloud_Download, - /// - /// cloud_off - /// - [Description( "cloud_off" )] - Cloud_Off, - /// - /// cloud_queue - /// - [Description( "cloud_queue" )] - Cloud_Queue, - /// - /// cloud_upload - /// - [Description( "cloud_upload" )] - Cloud_Upload, - /// - /// code - /// - [Description( "code" )] - Code, - /// - /// collections - /// - [Description( "collections" )] - Collections, - /// - /// collections_bookmark - /// - [Description( "collections_bookmark" )] - Collections_Bookmark, - /// - /// color_lens - /// - [Description( "color_lens" )] - Color_Lens, - /// - /// colorize - /// - [Description( "colorize" )] - Colorize, - /// - /// comment - /// - [Description( "comment" )] - Comment, - /// - /// compare - /// - [Description( "compare" )] - Compare, - /// - /// compare_arrows - /// - [Description( "compare_arrows" )] - Compare_Arrows, - /// - /// computer - /// - [Description( "computer" )] - Computer, - /// - /// confirmation_number - /// - [Description( "confirmation_number" )] - Confirmation_Number, - /// - /// contact_mail - /// - [Description( "contact_mail" )] - Contact_Mail, - /// - /// contact_phone - /// - [Description( "contact_phone" )] - Contact_Phone, - /// - /// contacts - /// - [Description( "contacts" )] - Contacts, - /// - /// content_copy - /// - [Description( "content_copy" )] - Content_Copy, - /// - /// content_cut - /// - [Description( "content_cut" )] - Content_Cut, - /// - /// content_paste - /// - [Description( "content_paste" )] - Content_Paste, - /// - /// control_point - /// - [Description( "control_point" )] - Control_Point, - /// - /// control_point_duplicate - /// - [Description( "control_point_duplicate" )] - Control_Point_Duplicate, - /// - /// copyright - /// - [Description( "copyright" )] - Copyright, - /// - /// create - /// - [Description( "create" )] - Create, - /// - /// create_new_folder - /// - [Description( "create_new_folder" )] - Create_New_Folder, - /// - /// credit_card - /// - [Description( "credit_card" )] - Credit_Card, - /// - /// crop - /// - [Description( "crop" )] - Crop, - /// - /// crop_16_9 - /// - [Description( "crop_16_9" )] - Crop_16_9, - /// - /// crop_3_2 - /// - [Description( "crop_3_2" )] - Crop_3_2, - /// - /// crop_5_4 - /// - [Description( "crop_5_4" )] - Crop_5_4, - /// - /// crop_7_5 - /// - [Description( "crop_7_5" )] - Crop_7_5, - /// - /// crop_din - /// - [Description( "crop_din" )] - Crop_Din, - /// - /// crop_free - /// - [Description( "crop_free" )] - Crop_Free, - /// - /// crop_landscape - /// - [Description( "crop_landscape" )] - Crop_Landscape, - /// - /// crop_original - /// - [Description( "crop_original" )] - Crop_Original, - /// - /// crop_portrait - /// - [Description( "crop_portrait" )] - Crop_Portrait, - /// - /// crop_rotate - /// - [Description( "crop_rotate" )] - Crop_Rotate, - /// - /// crop_square - /// - [Description( "crop_square" )] - Crop_Square, - /// - /// dashboard - /// - [Description( "dashboard" )] - Dashboard, - /// - /// data_usage - /// - [Description( "data_usage" )] - Data_Usage, - /// - /// date_range - /// - [Description( "date_range" )] - Date_Range, - /// - /// dehaze - /// - [Description( "dehaze" )] - Dehaze, - /// - /// delete - /// - [Description( "delete" )] - Delete, - /// - /// delete_forever - /// - [Description( "delete_forever" )] - Delete_Forever, - /// - /// delete_sweep - /// - [Description( "delete_sweep" )] - Delete_Sweep, - /// - /// description - /// - [Description( "description" )] - Description, - /// - /// desktop_mac - /// - [Description( "desktop_mac" )] - Desktop_Mac, - /// - /// desktop_windows - /// - [Description( "desktop_windows" )] - Desktop_Windows, - /// - /// details - /// - [Description( "details" )] - Details, - /// - /// developer_board - /// - [Description( "developer_board" )] - Developer_Board, - /// - /// developer_mode - /// - [Description( "developer_mode" )] - Developer_Mode, - /// - /// device_hub - /// - [Description( "device_hub" )] - Device_Hub, - /// - /// devices - /// - [Description( "devices" )] - Devices, - /// - /// devices_other - /// - [Description( "devices_other" )] - Devices_Other, - /// - /// dialer_sip - /// - [Description( "dialer_sip" )] - Dialer_Sip, - /// - /// dialpad - /// - [Description( "dialpad" )] - Dialpad, - /// - /// directions - /// - [Description( "directions" )] - Directions, - /// - /// directions_bike - /// - [Description( "directions_bike" )] - Directions_Bike, - /// - /// directions_boat - /// - [Description( "directions_boat" )] - Directions_Boat, - /// - /// directions_bus - /// - [Description( "directions_bus" )] - Directions_Bus, - /// - /// directions_car - /// - [Description( "directions_car" )] - Directions_Car, - /// - /// directions_railway - /// - [Description( "directions_railway" )] - Directions_Railway, - /// - /// directions_run - /// - [Description( "directions_run" )] - Directions_Run, - /// - /// directions_subway - /// - [Description( "directions_subway" )] - Directions_Subway, - /// - /// directions_transit - /// - [Description( "directions_transit" )] - Directions_Transit, - /// - /// directions_walk - /// - [Description( "directions_walk" )] - Directions_Walk, - /// - /// disc_full - /// - [Description( "disc_full" )] - Disc_Full, - /// - /// dns - /// - [Description( "dns" )] - Dns, - /// - /// do_not_disturb - /// - [Description( "do_not_disturb" )] - Do_Not_Disturb, - /// - /// do_not_disturb_alt - /// - [Description( "do_not_disturb_alt" )] - Do_Not_Disturb_Alt, - /// - /// do_not_disturb_off - /// - [Description( "do_not_disturb_off" )] - Do_Not_Disturb_Off, - /// - /// do_not_disturb_on - /// - [Description( "do_not_disturb_on" )] - Do_Not_Disturb_On, - /// - /// dock - /// - [Description( "dock" )] - Dock, - /// - /// domain - /// - [Description( "domain" )] - Domain, - /// - /// done - /// - [Description( "done" )] - Done, - /// - /// done_all - /// - [Description( "done_all" )] - Done_All, - /// - /// donut_large - /// - [Description( "donut_large" )] - Donut_Large, - /// - /// donut_small - /// - [Description( "donut_small" )] - Donut_Small, - /// - /// drafts - /// - [Description( "drafts" )] - Drafts, - /// - /// drag_handle - /// - [Description( "drag_handle" )] - Drag_Handle, - /// - /// drive_eta - /// - [Description( "drive_eta" )] - Drive_Eta, - /// - /// dvr - /// - [Description( "dvr" )] - Dvr, - /// - /// edit - /// - [Description( "edit" )] - Edit, - /// - /// edit_location - /// - [Description( "edit_location" )] - Edit_Location, - /// - /// eject - /// - [Description( "eject" )] - Eject, - /// - /// email - /// - [Description( "email" )] - Email, - /// - /// enhanced_encryption - /// - [Description( "enhanced_encryption" )] - Enhanced_Encryption, - /// - /// equalizer - /// - [Description( "equalizer" )] - Equalizer, - /// - /// error - /// - [Description( "error" )] - Error, - /// - /// error_outline - /// - [Description( "error_outline" )] - Error_Outline, - /// - /// euro_symbol - /// - [Description( "euro_symbol" )] - Euro_Symbol, - /// - /// ev_station - /// - [Description( "ev_station" )] - Ev_Station, - /// - /// event - /// - [Description( "event" )] - Event, - /// - /// event_available - /// - [Description( "event_available" )] - Event_Available, - /// - /// event_busy - /// - [Description( "event_busy" )] - Event_Busy, - /// - /// event_note - /// - [Description( "event_note" )] - Event_Note, - /// - /// event_seat - /// - [Description( "event_seat" )] - Event_Seat, - /// - /// exit_to_app - /// - [Description( "exit_to_app" )] - Exit_To_App, - /// - /// expand_less - /// - [Description( "expand_less" )] - Expand_Less, - /// - /// expand_more - /// - [Description( "expand_more" )] - Expand_More, - /// - /// explicit - /// - [Description( "explicit" )] - Explicit, - /// - /// explore - /// - [Description( "explore" )] - Explore, - /// - /// exposure - /// - [Description( "exposure" )] - Exposure, - /// - /// exposure_neg_1 - /// - [Description( "exposure_neg_1" )] - Exposure_Neg_1, - /// - /// exposure_neg_2 - /// - [Description( "exposure_neg_2" )] - Exposure_Neg_2, - /// - /// exposure_plus_1 - /// - [Description( "exposure_plus_1" )] - Exposure_Plus_1, - /// - /// exposure_plus_2 - /// - [Description( "exposure_plus_2" )] - Exposure_Plus_2, - /// - /// exposure_zero - /// - [Description( "exposure_zero" )] - Exposure_Zero, - /// - /// extension - /// - [Description( "extension" )] - Extension, - /// - /// face - /// - [Description( "face" )] - Face, - /// - /// fast_forward - /// - [Description( "fast_forward" )] - Fast_Forward, - /// - /// fast_rewind - /// - [Description( "fast_rewind" )] - Fast_Rewind, - /// - /// favorite - /// - [Description( "favorite" )] - Favorite, - /// - /// favorite_border - /// - [Description( "favorite_border" )] - Favorite_Border, - /// - /// featured_play_list - /// - [Description( "featured_play_list" )] - Featured_Play_List, - /// - /// featured_video - /// - [Description( "featured_video" )] - Featured_Video, - /// - /// feedback - /// - [Description( "feedback" )] - Feedback, - /// - /// fiber_dvr - /// - [Description( "fiber_dvr" )] - Fiber_Dvr, - /// - /// fiber_manual_record - /// - [Description( "fiber_manual_record" )] - Fiber_Manual_Record, - /// - /// fiber_new - /// - [Description( "fiber_new" )] - Fiber_New, - /// - /// fiber_pin - /// - [Description( "fiber_pin" )] - Fiber_Pin, - /// - /// fiber_smart_record - /// - [Description( "fiber_smart_record" )] - Fiber_Smart_Record, - /// - /// file_download - /// - [Description( "file_download" )] - File_Download, - /// - /// file_upload - /// - [Description( "file_upload" )] - File_Upload, - /// - /// filter - /// - [Description( "filter" )] - Filter, - /// - /// filter_1 - /// - [Description( "filter_1" )] - Filter_1, - /// - /// filter_2 - /// - [Description( "filter_2" )] - Filter_2, - /// - /// filter_3 - /// - [Description( "filter_3" )] - Filter_3, - /// - /// filter_4 - /// - [Description( "filter_4" )] - Filter_4, - /// - /// filter_5 - /// - [Description( "filter_5" )] - Filter_5, - /// - /// filter_6 - /// - [Description( "filter_6" )] - Filter_6, - /// - /// filter_7 - /// - [Description( "filter_7" )] - Filter_7, - /// - /// filter_8 - /// - [Description( "filter_8" )] - Filter_8, - /// - /// filter_9 - /// - [Description( "filter_9" )] - Filter_9, - /// - /// filter_9_plus - /// - [Description( "filter_9_plus" )] - Filter_9_Plus, - /// - /// filter_b_and_w - /// - [Description( "filter_b_and_w" )] - Filter_B_And_W, - /// - /// filter_center_focus - /// - [Description( "filter_center_focus" )] - Filter_Center_Focus, - /// - /// filter_drama - /// - [Description( "filter_drama" )] - Filter_Drama, - /// - /// filter_frames - /// - [Description( "filter_frames" )] - Filter_Frames, - /// - /// filter_hdr - /// - [Description( "filter_hdr" )] - Filter_Hdr, - /// - /// filter_list - /// - [Description( "filter_list" )] - Filter_List, - /// - /// filter_none - /// - [Description( "filter_none" )] - Filter_None, - /// - /// filter_tilt_shift - /// - [Description( "filter_tilt_shift" )] - Filter_Tilt_Shift, - /// - /// filter_vintage - /// - [Description( "filter_vintage" )] - Filter_Vintage, - /// - /// find_in_page - /// - [Description( "find_in_page" )] - Find_In_Page, - /// - /// find_replace - /// - [Description( "find_replace" )] - Find_Replace, - /// - /// fingerprint - /// - [Description( "fingerprint" )] - Fingerprint, - /// - /// first_page - /// - [Description( "first_page" )] - First_Page, - /// - /// fitness_center - /// - [Description( "fitness_center" )] - Fitness_Center, - /// - /// flag - /// - [Description( "flag" )] - Flag, - /// - /// flare - /// - [Description( "flare" )] - Flare, - /// - /// flash_auto - /// - [Description( "flash_auto" )] - Flash_Auto, - /// - /// flash_off - /// - [Description( "flash_off" )] - Flash_Off, - /// - /// flash_on - /// - [Description( "flash_on" )] - Flash_On, - /// - /// flight - /// - [Description( "flight" )] - Flight, - /// - /// flight_land - /// - [Description( "flight_land" )] - Flight_Land, - /// - /// flight_takeoff - /// - [Description( "flight_takeoff" )] - Flight_Takeoff, - /// - /// flip - /// - [Description( "flip" )] - Flip, - /// - /// flip_to_back - /// - [Description( "flip_to_back" )] - Flip_To_Back, - /// - /// flip_to_front - /// - [Description( "flip_to_front" )] - Flip_To_Front, - /// - /// folder - /// - [Description( "folder" )] - Folder, - /// - /// folder_open - /// - [Description( "folder_open" )] - Folder_Open, - /// - /// folder_shared - /// - [Description( "folder_shared" )] - Folder_Shared, - /// - /// folder_special - /// - [Description( "folder_special" )] - Folder_Special, - /// - /// font_download - /// - [Description( "font_download" )] - Font_Download, - /// - /// format_align_center - /// - [Description( "format_align_center" )] - Format_Align_Center, - /// - /// format_align_justify - /// - [Description( "format_align_justify" )] - Format_Align_Justify, - /// - /// format_align_left - /// - [Description( "format_align_left" )] - Format_Align_Left, - /// - /// format_align_right - /// - [Description( "format_align_right" )] - Format_Align_Right, - /// - /// format_bold - /// - [Description( "format_bold" )] - Format_Bold, - /// - /// format_clear - /// - [Description( "format_clear" )] - Format_Clear, - /// - /// format_color_fill - /// - [Description( "format_color_fill" )] - Format_Color_Fill, - /// - /// format_color_reset - /// - [Description( "format_color_reset" )] - Format_Color_Reset, - /// - /// format_color_text - /// - [Description( "format_color_text" )] - Format_Color_Text, - /// - /// format_indent_decrease - /// - [Description( "format_indent_decrease" )] - Format_Indent_Decrease, - /// - /// format_indent_increase - /// - [Description( "format_indent_increase" )] - Format_Indent_Increase, - /// - /// format_italic - /// - [Description( "format_italic" )] - Format_Italic, - /// - /// format_line_spacing - /// - [Description( "format_line_spacing" )] - Format_Line_Spacing, - /// - /// format_list_bulleted - /// - [Description( "format_list_bulleted" )] - Format_List_Bulleted, - /// - /// format_list_numbered - /// - [Description( "format_list_numbered" )] - Format_List_Numbered, - /// - /// format_paint - /// - [Description( "format_paint" )] - Format_Paint, - /// - /// format_quote - /// - [Description( "format_quote" )] - Format_Quote, - /// - /// format_shapes - /// - [Description( "format_shapes" )] - Format_Shapes, - /// - /// format_size - /// - [Description( "format_size" )] - Format_Size, - /// - /// format_strikethrough - /// - [Description( "format_strikethrough" )] - Format_Strikethrough, - /// - /// format_textdirection_l_to_r - /// - [Description( "format_textdirection_l_to_r" )] - Format_Textdirection_L_To_R, - /// - /// format_textdirection_r_to_l - /// - [Description( "format_textdirection_r_to_l" )] - Format_Textdirection_R_To_L, - /// - /// format_underlined - /// - [Description( "format_underlined" )] - Format_Underlined, - /// - /// forum - /// - [Description( "forum" )] - Forum, - /// - /// forward - /// - [Description( "forward" )] - Forward, - /// - /// forward_10 - /// - [Description( "forward_10" )] - Forward_10, - /// - /// forward_30 - /// - [Description( "forward_30" )] - Forward_30, - /// - /// forward_5 - /// - [Description( "forward_5" )] - Forward_5, - /// - /// free_breakfast - /// - [Description( "free_breakfast" )] - Free_Breakfast, - /// - /// fullscreen - /// - [Description( "fullscreen" )] - Fullscreen, - /// - /// fullscreen_exit - /// - [Description( "fullscreen_exit" )] - Fullscreen_Exit, - /// - /// functions - /// - [Description( "functions" )] - Functions, - /// - /// g_translate - /// - [Description( "g_translate" )] - G_Translate, - /// - /// gamepad - /// - [Description( "gamepad" )] - Gamepad, - /// - /// games - /// - [Description( "games" )] - Games, - /// - /// gavel - /// - [Description( "gavel" )] - Gavel, - /// - /// gesture - /// - [Description( "gesture" )] - Gesture, - /// - /// get_app - /// - [Description( "get_app" )] - Get_App, - /// - /// gif - /// - [Description( "gif" )] - Gif, - /// - /// golf_course - /// - [Description( "golf_course" )] - Golf_Course, - /// - /// gps_fixed - /// - [Description( "gps_fixed" )] - Gps_Fixed, - /// - /// gps_not_fixed - /// - [Description( "gps_not_fixed" )] - Gps_Not_Fixed, - /// - /// gps_off - /// - [Description( "gps_off" )] - Gps_Off, - /// - /// grade - /// - [Description( "grade" )] - Grade, - /// - /// gradient - /// - [Description( "gradient" )] - Gradient, - /// - /// grain - /// - [Description( "grain" )] - Grain, - /// - /// graphic_eq - /// - [Description( "graphic_eq" )] - Graphic_Eq, - /// - /// grid_off - /// - [Description( "grid_off" )] - Grid_Off, - /// - /// grid_on - /// - [Description( "grid_on" )] - Grid_On, - /// - /// group - /// - [Description( "group" )] - Group, - /// - /// group_add - /// - [Description( "group_add" )] - Group_Add, - /// - /// group_work - /// - [Description( "group_work" )] - Group_Work, - /// - /// hd - /// - [Description( "hd" )] - Hd, - /// - /// hdr_off - /// - [Description( "hdr_off" )] - Hdr_Off, - /// - /// hdr_on - /// - [Description( "hdr_on" )] - Hdr_On, - /// - /// hdr_strong - /// - [Description( "hdr_strong" )] - Hdr_Strong, - /// - /// hdr_weak - /// - [Description( "hdr_weak" )] - Hdr_Weak, - /// - /// headset - /// - [Description( "headset" )] - Headset, - /// - /// headset_mic - /// - [Description( "headset_mic" )] - Headset_Mic, - /// - /// healing - /// - [Description( "healing" )] - Healing, - /// - /// hearing - /// - [Description( "hearing" )] - Hearing, - /// - /// help - /// - [Description( "help" )] - Help, - /// - /// help_outline - /// - [Description( "help_outline" )] - Help_Outline, - /// - /// high_quality - /// - [Description( "high_quality" )] - High_Quality, - /// - /// highlight - /// - [Description( "highlight" )] - Highlight, - /// - /// highlight_off - /// - [Description( "highlight_off" )] - Highlight_Off, - /// - /// history - /// - [Description( "history" )] - History, - /// - /// home - /// - [Description( "home" )] - Home, - /// - /// hot_tub - /// - [Description( "hot_tub" )] - Hot_Tub, - /// - /// hotel - /// - [Description( "hotel" )] - Hotel, - /// - /// hourglass_empty - /// - [Description( "hourglass_empty" )] - Hourglass_Empty, - /// - /// hourglass_full - /// - [Description( "hourglass_full" )] - Hourglass_Full, - /// - /// http - /// - [Description( "http" )] - Http, - /// - /// https - /// - [Description( "https" )] - Https, - /// - /// image - /// - [Description( "image" )] - Image, - /// - /// image_aspect_ratio - /// - [Description( "image_aspect_ratio" )] - Image_Aspect_Ratio, - /// - /// import_contacts - /// - [Description( "import_contacts" )] - Import_Contacts, - /// - /// import_export - /// - [Description( "import_export" )] - Import_Export, - /// - /// important_devices - /// - [Description( "important_devices" )] - Important_Devices, - /// - /// inbox - /// - [Description( "inbox" )] - Inbox, - /// - /// indeterminate_check_box - /// - [Description( "indeterminate_check_box" )] - Indeterminate_Check_Box, - /// - /// info - /// - [Description( "info" )] - Info, - /// - /// info_outline - /// - [Description( "info_outline" )] - Info_Outline, - /// - /// input - /// - [Description( "input" )] - Input, - /// - /// insert_chart - /// - [Description( "insert_chart" )] - Insert_Chart, - /// - /// insert_comment - /// - [Description( "insert_comment" )] - Insert_Comment, - /// - /// insert_drive_file - /// - [Description( "insert_drive_file" )] - Insert_Drive_File, - /// - /// insert_emoticon - /// - [Description( "insert_emoticon" )] - Insert_Emoticon, - /// - /// insert_invitation - /// - [Description( "insert_invitation" )] - Insert_Invitation, - /// - /// insert_link - /// - [Description( "insert_link" )] - Insert_Link, - /// - /// insert_photo - /// - [Description( "insert_photo" )] - Insert_Photo, - /// - /// invert_colors - /// - [Description( "invert_colors" )] - Invert_Colors, - /// - /// invert_colors_off - /// - [Description( "invert_colors_off" )] - Invert_Colors_Off, - /// - /// iso - /// - [Description( "iso" )] - Iso, - /// - /// keyboard - /// - [Description( "keyboard" )] - Keyboard, - /// - /// keyboard_arrow_down - /// - [Description( "keyboard_arrow_down" )] - Keyboard_Arrow_Down, - /// - /// keyboard_arrow_left - /// - [Description( "keyboard_arrow_left" )] - Keyboard_Arrow_Left, - /// - /// keyboard_arrow_right - /// - [Description( "keyboard_arrow_right" )] - Keyboard_Arrow_Right, - /// - /// keyboard_arrow_up - /// - [Description( "keyboard_arrow_up" )] - Keyboard_Arrow_Up, - /// - /// keyboard_backspace - /// - [Description( "keyboard_backspace" )] - Keyboard_Backspace, - /// - /// keyboard_capslock - /// - [Description( "keyboard_capslock" )] - Keyboard_Capslock, - /// - /// keyboard_hide - /// - [Description( "keyboard_hide" )] - Keyboard_Hide, - /// - /// keyboard_return - /// - [Description( "keyboard_return" )] - Keyboard_Return, - /// - /// keyboard_tab - /// - [Description( "keyboard_tab" )] - Keyboard_Tab, - /// - /// keyboard_voice - /// - [Description( "keyboard_voice" )] - Keyboard_Voice, - /// - /// kitchen - /// - [Description( "kitchen" )] - Kitchen, - /// - /// label - /// - [Description( "label" )] - Label, - /// - /// label_outline - /// - [Description( "label_outline" )] - Label_Outline, - /// - /// landscape - /// - [Description( "landscape" )] - Landscape, - /// - /// language - /// - [Description( "language" )] - Language, - /// - /// laptop - /// - [Description( "laptop" )] - Laptop, - /// - /// laptop_chromebook - /// - [Description( "laptop_chromebook" )] - Laptop_Chromebook, - /// - /// laptop_mac - /// - [Description( "laptop_mac" )] - Laptop_Mac, - /// - /// laptop_windows - /// - [Description( "laptop_windows" )] - Laptop_Windows, - /// - /// last_page - /// - [Description( "last_page" )] - Last_Page, - /// - /// launch - /// - [Description( "launch" )] - Launch, - /// - /// layers - /// - [Description( "layers" )] - Layers, - /// - /// layers_clear - /// - [Description( "layers_clear" )] - Layers_Clear, - /// - /// leak_add - /// - [Description( "leak_add" )] - Leak_Add, - /// - /// leak_remove - /// - [Description( "leak_remove" )] - Leak_Remove, - /// - /// lens - /// - [Description( "lens" )] - Lens, - /// - /// library_add - /// - [Description( "library_add" )] - Library_Add, - /// - /// library_books - /// - [Description( "library_books" )] - Library_Books, - /// - /// library_music - /// - [Description( "library_music" )] - Library_Music, - /// - /// lightbulb_outline - /// - [Description( "lightbulb_outline" )] - Lightbulb_Outline, - /// - /// line_style - /// - [Description( "line_style" )] - Line_Style, - /// - /// line_weight - /// - [Description( "line_weight" )] - Line_Weight, - /// - /// linear_scale - /// - [Description( "linear_scale" )] - Linear_Scale, - /// - /// link - /// - [Description( "link" )] - Link, - /// - /// linked_camera - /// - [Description( "linked_camera" )] - Linked_Camera, - /// - /// list - /// - [Description( "list" )] - List, - /// - /// live_help - /// - [Description( "live_help" )] - Live_Help, - /// - /// live_tv - /// - [Description( "live_tv" )] - Live_Tv, - /// - /// local_activity - /// - [Description( "local_activity" )] - Local_Activity, - /// - /// local_airport - /// - [Description( "local_airport" )] - Local_Airport, - /// - /// local_atm - /// - [Description( "local_atm" )] - Local_Atm, - /// - /// local_bar - /// - [Description( "local_bar" )] - Local_Bar, - /// - /// local_cafe - /// - [Description( "local_cafe" )] - Local_Cafe, - /// - /// local_car_wash - /// - [Description( "local_car_wash" )] - Local_Car_Wash, - /// - /// local_convenience_store - /// - [Description( "local_convenience_store" )] - Local_Convenience_Store, - /// - /// local_dining - /// - [Description( "local_dining" )] - Local_Dining, - /// - /// local_drink - /// - [Description( "local_drink" )] - Local_Drink, - /// - /// local_florist - /// - [Description( "local_florist" )] - Local_Florist, - /// - /// local_gas_station - /// - [Description( "local_gas_station" )] - Local_Gas_Station, - /// - /// local_grocery_store - /// - [Description( "local_grocery_store" )] - Local_Grocery_Store, - /// - /// local_hospital - /// - [Description( "local_hospital" )] - Local_Hospital, - /// - /// local_hotel - /// - [Description( "local_hotel" )] - Local_Hotel, - /// - /// local_laundry_service - /// - [Description( "local_laundry_service" )] - Local_Laundry_Service, - /// - /// local_library - /// - [Description( "local_library" )] - Local_Library, - /// - /// local_mall - /// - [Description( "local_mall" )] - Local_Mall, - /// - /// local_movies - /// - [Description( "local_movies" )] - Local_Movies, - /// - /// local_offer - /// - [Description( "local_offer" )] - Local_Offer, - /// - /// local_parking - /// - [Description( "local_parking" )] - Local_Parking, - /// - /// local_pharmacy - /// - [Description( "local_pharmacy" )] - Local_Pharmacy, - /// - /// local_phone - /// - [Description( "local_phone" )] - Local_Phone, - /// - /// local_pizza - /// - [Description( "local_pizza" )] - Local_Pizza, - /// - /// local_play - /// - [Description( "local_play" )] - Local_Play, - /// - /// local_post_office - /// - [Description( "local_post_office" )] - Local_Post_Office, - /// - /// local_printshop - /// - [Description( "local_printshop" )] - Local_Printshop, - /// - /// local_see - /// - [Description( "local_see" )] - Local_See, - /// - /// local_shipping - /// - [Description( "local_shipping" )] - Local_Shipping, - /// - /// local_taxi - /// - [Description( "local_taxi" )] - Local_Taxi, - /// - /// location_city - /// - [Description( "location_city" )] - Location_City, - /// - /// location_disabled - /// - [Description( "location_disabled" )] - Location_Disabled, - /// - /// location_off - /// - [Description( "location_off" )] - Location_Off, - /// - /// location_on - /// - [Description( "location_on" )] - Location_On, - /// - /// location_searching - /// - [Description( "location_searching" )] - Location_Searching, - /// - /// lock - /// - [Description( "lock" )] - Lock, - /// - /// lock_open - /// - [Description( "lock_open" )] - Lock_Open, - /// - /// lock_outline - /// - [Description( "lock_outline" )] - Lock_Outline, - /// - /// looks - /// - [Description( "looks" )] - Looks, - /// - /// looks_3 - /// - [Description( "looks_3" )] - Looks_3, - /// - /// looks_4 - /// - [Description( "looks_4" )] - Looks_4, - /// - /// looks_5 - /// - [Description( "looks_5" )] - Looks_5, - /// - /// looks_6 - /// - [Description( "looks_6" )] - Looks_6, - /// - /// looks_one - /// - [Description( "looks_one" )] - Looks_One, - /// - /// looks_two - /// - [Description( "looks_two" )] - Looks_Two, - /// - /// loop - /// - [Description( "loop" )] - Loop, - /// - /// loupe - /// - [Description( "loupe" )] - Loupe, - /// - /// low_priority - /// - [Description( "low_priority" )] - Low_Priority, - /// - /// loyalty - /// - [Description( "loyalty" )] - Loyalty, - /// - /// mail - /// - [Description( "mail" )] - Mail, - /// - /// mail_outline - /// - [Description( "mail_outline" )] - Mail_Outline, - /// - /// map - /// - [Description( "map" )] - Map, - /// - /// markunread - /// - [Description( "markunread" )] - Markunread, - /// - /// markunread_mailbox - /// - [Description( "markunread_mailbox" )] - Markunread_Mailbox, - /// - /// memory - /// - [Description( "memory" )] - Memory, - /// - /// menu - /// - [Description( "menu" )] - Menu, - /// - /// merge_type - /// - [Description( "merge_type" )] - Merge_Type, - /// - /// message - /// - [Description( "message" )] - Message, - /// - /// mic - /// - [Description( "mic" )] - Mic, - /// - /// mic_none - /// - [Description( "mic_none" )] - Mic_None, - /// - /// mic_off - /// - [Description( "mic_off" )] - Mic_Off, - /// - /// mms - /// - [Description( "mms" )] - Mms, - /// - /// mode_comment - /// - [Description( "mode_comment" )] - Mode_Comment, - /// - /// mode_edit - /// - [Description( "mode_edit" )] - Mode_Edit, - /// - /// monetization_on - /// - [Description( "monetization_on" )] - Monetization_On, - /// - /// money_off - /// - [Description( "money_off" )] - Money_Off, - /// - /// monochrome_photos - /// - [Description( "monochrome_photos" )] - Monochrome_Photos, - /// - /// mood - /// - [Description( "mood" )] - Mood, - /// - /// mood_bad - /// - [Description( "mood_bad" )] - Mood_Bad, - /// - /// more - /// - [Description( "more" )] - More, - /// - /// more_horiz - /// - [Description( "more_horiz" )] - More_Horiz, - /// - /// more_vert - /// - [Description( "more_vert" )] - More_Vert, - /// - /// motorcycle - /// - [Description( "motorcycle" )] - Motorcycle, - /// - /// mouse - /// - [Description( "mouse" )] - Mouse, - /// - /// move_to_inbox - /// - [Description( "move_to_inbox" )] - Move_To_Inbox, - /// - /// movie - /// - [Description( "movie" )] - Movie, - /// - /// movie_creation - /// - [Description( "movie_creation" )] - Movie_Creation, - /// - /// movie_filter - /// - [Description( "movie_filter" )] - Movie_Filter, - /// - /// multiline_chart - /// - [Description( "multiline_chart" )] - Multiline_Chart, - /// - /// music_note - /// - [Description( "music_note" )] - Music_Note, - /// - /// music_video - /// - [Description( "music_video" )] - Music_Video, - /// - /// my_location - /// - [Description( "my_location" )] - My_Location, - /// - /// nature - /// - [Description( "nature" )] - Nature, - /// - /// nature_people - /// - [Description( "nature_people" )] - Nature_People, - /// - /// navigate_before - /// - [Description( "navigate_before" )] - Navigate_Before, - /// - /// navigate_next - /// - [Description( "navigate_next" )] - Navigate_Next, - /// - /// navigation - /// - [Description( "navigation" )] - Navigation, - /// - /// near_me - /// - [Description( "near_me" )] - Near_Me, - /// - /// network_cell - /// - [Description( "network_cell" )] - Network_Cell, - /// - /// network_check - /// - [Description( "network_check" )] - Network_Check, - /// - /// network_locked - /// - [Description( "network_locked" )] - Network_Locked, - /// - /// network_wifi - /// - [Description( "network_wifi" )] - Network_Wifi, - /// - /// new_releases - /// - [Description( "new_releases" )] - New_Releases, - /// - /// next_week - /// - [Description( "next_week" )] - Next_Week, - /// - /// nfc - /// - [Description( "nfc" )] - Nfc, - /// - /// no_encryption - /// - [Description( "no_encryption" )] - No_Encryption, - /// - /// no_sim - /// - [Description( "no_sim" )] - No_Sim, - /// - /// not_interested - /// - [Description( "not_interested" )] - Not_Interested, - /// - /// note - /// - [Description( "note" )] - Note, - /// - /// note_add - /// - [Description( "note_add" )] - Note_Add, - /// - /// notifications - /// - [Description( "notifications" )] - Notifications, - /// - /// notifications_active - /// - [Description( "notifications_active" )] - Notifications_Active, - /// - /// notifications_none - /// - [Description( "notifications_none" )] - Notifications_None, - /// - /// notifications_off - /// - [Description( "notifications_off" )] - Notifications_Off, - /// - /// notifications_paused - /// - [Description( "notifications_paused" )] - Notifications_Paused, - /// - /// offline_pin - /// - [Description( "offline_pin" )] - Offline_Pin, - /// - /// ondemand_video - /// - [Description( "ondemand_video" )] - Ondemand_Video, - /// - /// opacity - /// - [Description( "opacity" )] - Opacity, - /// - /// open_in_browser - /// - [Description( "open_in_browser" )] - Open_In_Browser, - /// - /// open_in_new - /// - [Description( "open_in_new" )] - Open_In_New, - /// - /// open_with - /// - [Description( "open_with" )] - Open_With, - /// - /// pages - /// - [Description( "pages" )] - Pages, - /// - /// pageview - /// - [Description( "pageview" )] - Pageview, - /// - /// palette - /// - [Description( "palette" )] - Palette, - /// - /// pan_tool - /// - [Description( "pan_tool" )] - Pan_Tool, - /// - /// panorama - /// - [Description( "panorama" )] - Panorama, - /// - /// panorama_fish_eye - /// - [Description( "panorama_fish_eye" )] - Panorama_Fish_Eye, - /// - /// panorama_horizontal - /// - [Description( "panorama_horizontal" )] - Panorama_Horizontal, - /// - /// panorama_vertical - /// - [Description( "panorama_vertical" )] - Panorama_Vertical, - /// - /// panorama_wide_angle - /// - [Description( "panorama_wide_angle" )] - Panorama_Wide_Angle, - /// - /// party_mode - /// - [Description( "party_mode" )] - Party_Mode, - /// - /// pause - /// - [Description( "pause" )] - Pause, - /// - /// pause_circle_filled - /// - [Description( "pause_circle_filled" )] - Pause_Circle_Filled, - /// - /// pause_circle_outline - /// - [Description( "pause_circle_outline" )] - Pause_Circle_Outline, - /// - /// payment - /// - [Description( "payment" )] - Payment, - /// - /// people - /// - [Description( "people" )] - People, - /// - /// people_outline - /// - [Description( "people_outline" )] - People_Outline, - /// - /// perm_camera_mic - /// - [Description( "perm_camera_mic" )] - Perm_Camera_Mic, - /// - /// perm_contact_calendar - /// - [Description( "perm_contact_calendar" )] - Perm_Contact_Calendar, - /// - /// perm_data_setting - /// - [Description( "perm_data_setting" )] - Perm_Data_Setting, - /// - /// perm_device_information - /// - [Description( "perm_device_information" )] - Perm_Device_Information, - /// - /// perm_identity - /// - [Description( "perm_identity" )] - Perm_Identity, - /// - /// perm_media - /// - [Description( "perm_media" )] - Perm_Media, - /// - /// perm_phone_msg - /// - [Description( "perm_phone_msg" )] - Perm_Phone_Msg, - /// - /// perm_scan_wifi - /// - [Description( "perm_scan_wifi" )] - Perm_Scan_Wifi, - /// - /// person - /// - [Description( "person" )] - Person, - /// - /// person_add - /// - [Description( "person_add" )] - Person_Add, - /// - /// person_outline - /// - [Description( "person_outline" )] - Person_Outline, - /// - /// person_pin - /// - [Description( "person_pin" )] - Person_Pin, - /// - /// person_pin_circle - /// - [Description( "person_pin_circle" )] - Person_Pin_Circle, - /// - /// personal_video - /// - [Description( "personal_video" )] - Personal_Video, - /// - /// pets - /// - [Description( "pets" )] - Pets, - /// - /// phone - /// - [Description( "phone" )] - Phone, - /// - /// phone_android - /// - [Description( "phone_android" )] - Phone_Android, - /// - /// phone_bluetooth_speaker - /// - [Description( "phone_bluetooth_speaker" )] - Phone_Bluetooth_Speaker, - /// - /// phone_forwarded - /// - [Description( "phone_forwarded" )] - Phone_Forwarded, - /// - /// phone_in_talk - /// - [Description( "phone_in_talk" )] - Phone_In_Talk, - /// - /// phone_iphone - /// - [Description( "phone_iphone" )] - Phone_Iphone, - /// - /// phone_locked - /// - [Description( "phone_locked" )] - Phone_Locked, - /// - /// phone_missed - /// - [Description( "phone_missed" )] - Phone_Missed, - /// - /// phone_paused - /// - [Description( "phone_paused" )] - Phone_Paused, - /// - /// phonelink - /// - [Description( "phonelink" )] - Phonelink, - /// - /// phonelink_erase - /// - [Description( "phonelink_erase" )] - Phonelink_Erase, - /// - /// phonelink_lock - /// - [Description( "phonelink_lock" )] - Phonelink_Lock, - /// - /// phonelink_off - /// - [Description( "phonelink_off" )] - Phonelink_Off, - /// - /// phonelink_ring - /// - [Description( "phonelink_ring" )] - Phonelink_Ring, - /// - /// phonelink_setup - /// - [Description( "phonelink_setup" )] - Phonelink_Setup, - /// - /// photo - /// - [Description( "photo" )] - Photo, - /// - /// photo_album - /// - [Description( "photo_album" )] - Photo_Album, - /// - /// photo_camera - /// - [Description( "photo_camera" )] - Photo_Camera, - /// - /// photo_filter - /// - [Description( "photo_filter" )] - Photo_Filter, - /// - /// photo_library - /// - [Description( "photo_library" )] - Photo_Library, - /// - /// photo_size_select_actual - /// - [Description( "photo_size_select_actual" )] - Photo_Size_Select_Actual, - /// - /// photo_size_select_large - /// - [Description( "photo_size_select_large" )] - Photo_Size_Select_Large, - /// - /// photo_size_select_small - /// - [Description( "photo_size_select_small" )] - Photo_Size_Select_Small, - /// - /// picture_as_pdf - /// - [Description( "picture_as_pdf" )] - Picture_As_Pdf, - /// - /// picture_in_picture - /// - [Description( "picture_in_picture" )] - Picture_In_Picture, - /// - /// picture_in_picture_alt - /// - [Description( "picture_in_picture_alt" )] - Picture_In_Picture_Alt, - /// - /// pie_chart - /// - [Description( "pie_chart" )] - Pie_Chart, - /// - /// pie_chart_outlined - /// - [Description( "pie_chart_outlined" )] - Pie_Chart_Outlined, - /// - /// pin_drop - /// - [Description( "pin_drop" )] - Pin_Drop, - /// - /// place - /// - [Description( "place" )] - Place, - /// - /// play_arrow - /// - [Description( "play_arrow" )] - Play_Arrow, - /// - /// play_circle_filled - /// - [Description( "play_circle_filled" )] - Play_Circle_Filled, - /// - /// play_circle_outline - /// - [Description( "play_circle_outline" )] - Play_Circle_Outline, - /// - /// play_for_work - /// - [Description( "play_for_work" )] - Play_For_Work, - /// - /// playlist_add - /// - [Description( "playlist_add" )] - Playlist_Add, - /// - /// playlist_add_check - /// - [Description( "playlist_add_check" )] - Playlist_Add_Check, - /// - /// playlist_play - /// - [Description( "playlist_play" )] - Playlist_Play, - /// - /// plus_one - /// - [Description( "plus_one" )] - Plus_One, - /// - /// poll - /// - [Description( "poll" )] - Poll, - /// - /// polymer - /// - [Description( "polymer" )] - Polymer, - /// - /// pool - /// - [Description( "pool" )] - Pool, - /// - /// portable_wifi_off - /// - [Description( "portable_wifi_off" )] - Portable_Wifi_Off, - /// - /// portrait - /// - [Description( "portrait" )] - Portrait, - /// - /// power - /// - [Description( "power" )] - Power, - /// - /// power_input - /// - [Description( "power_input" )] - Power_Input, - /// - /// power_settings_new - /// - [Description( "power_settings_new" )] - Power_Settings_New, - /// - /// pregnant_woman - /// - [Description( "pregnant_woman" )] - Pregnant_Woman, - /// - /// present_to_all - /// - [Description( "present_to_all" )] - Present_To_All, - /// - /// print - /// - [Description( "print" )] - Print, - /// - /// priority_high - /// - [Description( "priority_high" )] - Priority_High, - /// - /// public - /// - [Description( "public" )] - Public, - /// - /// publish - /// - [Description( "publish" )] - Publish, - /// - /// query_builder - /// - [Description( "query_builder" )] - Query_Builder, - /// - /// question_answer - /// - [Description( "question_answer" )] - Question_Answer, - /// - /// queue - /// - [Description( "queue" )] - Queue, - /// - /// queue_music - /// - [Description( "queue_music" )] - Queue_Music, - /// - /// queue_play_next - /// - [Description( "queue_play_next" )] - Queue_Play_Next, - /// - /// radio - /// - [Description( "radio" )] - Radio, - /// - /// radio_button_checked - /// - [Description( "radio_button_checked" )] - Radio_Button_Checked, - /// - /// radio_button_unchecked - /// - [Description( "radio_button_unchecked" )] - Radio_Button_Unchecked, - /// - /// rate_review - /// - [Description( "rate_review" )] - Rate_Review, - /// - /// receipt - /// - [Description( "receipt" )] - Receipt, - /// - /// recent_actors - /// - [Description( "recent_actors" )] - Recent_Actors, - /// - /// record_voice_over - /// - [Description( "record_voice_over" )] - Record_Voice_Over, - /// - /// redeem - /// - [Description( "redeem" )] - Redeem, - /// - /// redo - /// - [Description( "redo" )] - Redo, - /// - /// refresh - /// - [Description( "refresh" )] - Refresh, - /// - /// remove - /// - [Description( "remove" )] - Remove, - /// - /// remove_circle - /// - [Description( "remove_circle" )] - Remove_Circle, - /// - /// remove_circle_outline - /// - [Description( "remove_circle_outline" )] - Remove_Circle_Outline, - /// - /// remove_from_queue - /// - [Description( "remove_from_queue" )] - Remove_From_Queue, - /// - /// remove_red_eye - /// - [Description( "remove_red_eye" )] - Remove_Red_Eye, - /// - /// remove_shopping_cart - /// - [Description( "remove_shopping_cart" )] - Remove_Shopping_Cart, - /// - /// reorder - /// - [Description( "reorder" )] - Reorder, - /// - /// repeat - /// - [Description( "repeat" )] - Repeat, - /// - /// repeat_one - /// - [Description( "repeat_one" )] - Repeat_One, - /// - /// replay - /// - [Description( "replay" )] - Replay, - /// - /// replay_10 - /// - [Description( "replay_10" )] - Replay_10, - /// - /// replay_30 - /// - [Description( "replay_30" )] - Replay_30, - /// - /// replay_5 - /// - [Description( "replay_5" )] - Replay_5, - /// - /// reply - /// - [Description( "reply" )] - Reply, - /// - /// reply_all - /// - [Description( "reply_all" )] - Reply_All, - /// - /// report - /// - [Description( "report" )] - Report, - /// - /// report_problem - /// - [Description( "report_problem" )] - Report_Problem, - /// - /// restaurant - /// - [Description( "restaurant" )] - Restaurant, - /// - /// restaurant_menu - /// - [Description( "restaurant_menu" )] - Restaurant_Menu, - /// - /// restore - /// - [Description( "restore" )] - Restore, - /// - /// restore_page - /// - [Description( "restore_page" )] - Restore_Page, - /// - /// ring_volume - /// - [Description( "ring_volume" )] - Ring_Volume, - /// - /// room - /// - [Description( "room" )] - Room, - /// - /// room_service - /// - [Description( "room_service" )] - Room_Service, - /// - /// rotate_90_degrees_ccw - /// - [Description( "rotate_90_degrees_ccw" )] - Rotate_90_Degrees_Ccw, - /// - /// rotate_left - /// - [Description( "rotate_left" )] - Rotate_Left, - /// - /// rotate_right - /// - [Description( "rotate_right" )] - Rotate_Right, - /// - /// rounded_corner - /// - [Description( "rounded_corner" )] - Rounded_Corner, - /// - /// router - /// - [Description( "router" )] - Router, - /// - /// rowing - /// - [Description( "rowing" )] - Rowing, - /// - /// rss_feed - /// - [Description( "rss_feed" )] - Rss_Feed, - /// - /// rv_hookup - /// - [Description( "rv_hookup" )] - Rv_Hookup, - /// - /// satellite - /// - [Description( "satellite" )] - Satellite, - /// - /// save - /// - [Description( "save" )] - Save, - /// - /// scanner - /// - [Description( "scanner" )] - Scanner, - /// - /// schedule - /// - [Description( "schedule" )] - Schedule, - /// - /// school - /// - [Description( "school" )] - School, - /// - /// screen_lock_landscape - /// - [Description( "screen_lock_landscape" )] - Screen_Lock_Landscape, - /// - /// screen_lock_portrait - /// - [Description( "screen_lock_portrait" )] - Screen_Lock_Portrait, - /// - /// screen_lock_rotation - /// - [Description( "screen_lock_rotation" )] - Screen_Lock_Rotation, - /// - /// screen_rotation - /// - [Description( "screen_rotation" )] - Screen_Rotation, - /// - /// screen_share - /// - [Description( "screen_share" )] - Screen_Share, - /// - /// sd_card - /// - [Description( "sd_card" )] - Sd_Card, - /// - /// sd_storage - /// - [Description( "sd_storage" )] - Sd_Storage, - /// - /// search - /// - [Description( "search" )] - Search, - /// - /// security - /// - [Description( "security" )] - Security, - /// - /// select_all - /// - [Description( "select_all" )] - Select_All, - /// - /// send - /// - [Description( "send" )] - Send, - /// - /// sentiment_dissatisfied - /// - [Description( "sentiment_dissatisfied" )] - Sentiment_Dissatisfied, - /// - /// sentiment_neutral - /// - [Description( "sentiment_neutral" )] - Sentiment_Neutral, - /// - /// sentiment_satisfied - /// - [Description( "sentiment_satisfied" )] - Sentiment_Satisfied, - /// - /// sentiment_very_dissatisfied - /// - [Description( "sentiment_very_dissatisfied" )] - Sentiment_Very_Dissatisfied, - /// - /// sentiment_very_satisfied - /// - [Description( "sentiment_very_satisfied" )] - Sentiment_Very_Satisfied, - /// - /// settings - /// - [Description( "settings" )] - Settings, - /// - /// settings_applications - /// - [Description( "settings_applications" )] - Settings_Applications, - /// - /// settings_backup_restore - /// - [Description( "settings_backup_restore" )] - Settings_Backup_Restore, - /// - /// settings_bluetooth - /// - [Description( "settings_bluetooth" )] - Settings_Bluetooth, - /// - /// settings_brightness - /// - [Description( "settings_brightness" )] - Settings_Brightness, - /// - /// settings_cell - /// - [Description( "settings_cell" )] - Settings_Cell, - /// - /// settings_ethernet - /// - [Description( "settings_ethernet" )] - Settings_Ethernet, - /// - /// settings_input_antenna - /// - [Description( "settings_input_antenna" )] - Settings_Input_Antenna, - /// - /// settings_input_component - /// - [Description( "settings_input_component" )] - Settings_Input_Component, - /// - /// settings_input_composite - /// - [Description( "settings_input_composite" )] - Settings_Input_Composite, - /// - /// settings_input_hdmi - /// - [Description( "settings_input_hdmi" )] - Settings_Input_Hdmi, - /// - /// settings_input_svideo - /// - [Description( "settings_input_svideo" )] - Settings_Input_Svideo, - /// - /// settings_overscan - /// - [Description( "settings_overscan" )] - Settings_Overscan, - /// - /// settings_phone - /// - [Description( "settings_phone" )] - Settings_Phone, - /// - /// settings_power - /// - [Description( "settings_power" )] - Settings_Power, - /// - /// settings_remote - /// - [Description( "settings_remote" )] - Settings_Remote, - /// - /// settings_system_daydream - /// - [Description( "settings_system_daydream" )] - Settings_System_Daydream, - /// - /// settings_voice - /// - [Description( "settings_voice" )] - Settings_Voice, - /// - /// share - /// - [Description( "share" )] - Share, - /// - /// shop - /// - [Description( "shop" )] - Shop, - /// - /// shop_two - /// - [Description( "shop_two" )] - Shop_Two, - /// - /// shopping_basket - /// - [Description( "shopping_basket" )] - Shopping_Basket, - /// - /// shopping_cart - /// - [Description( "shopping_cart" )] - Shopping_Cart, - /// - /// short_text - /// - [Description( "short_text" )] - Short_Text, - /// - /// show_chart - /// - [Description( "show_chart" )] - Show_Chart, - /// - /// shuffle - /// - [Description( "shuffle" )] - Shuffle, - /// - /// signal_cellular_4_bar - /// - [Description( "signal_cellular_4_bar" )] - Signal_Cellular_4_Bar, - /// - /// signal_cellular_connected_no_internet_4_bar - /// - [Description( "signal_cellular_connected_no_internet_4_bar" )] - Signal_Cellular_Connected_No_Internet_4_Bar, - /// - /// signal_cellular_no_sim - /// - [Description( "signal_cellular_no_sim" )] - Signal_Cellular_No_Sim, - /// - /// signal_cellular_null - /// - [Description( "signal_cellular_null" )] - Signal_Cellular_Null, - /// - /// signal_cellular_off - /// - [Description( "signal_cellular_off" )] - Signal_Cellular_Off, - /// - /// signal_wifi_4_bar - /// - [Description( "signal_wifi_4_bar" )] - Signal_Wifi_4_Bar, - /// - /// signal_wifi_4_bar_lock - /// - [Description( "signal_wifi_4_bar_lock" )] - Signal_Wifi_4_Bar_Lock, - /// - /// signal_wifi_off - /// - [Description( "signal_wifi_off" )] - Signal_Wifi_Off, - /// - /// sim_card - /// - [Description( "sim_card" )] - Sim_Card, - /// - /// sim_card_alert - /// - [Description( "sim_card_alert" )] - Sim_Card_Alert, - /// - /// skip_next - /// - [Description( "skip_next" )] - Skip_Next, - /// - /// skip_previous - /// - [Description( "skip_previous" )] - Skip_Previous, - /// - /// slideshow - /// - [Description( "slideshow" )] - Slideshow, - /// - /// slow_motion_video - /// - [Description( "slow_motion_video" )] - Slow_Motion_Video, - /// - /// smartphone - /// - [Description( "smartphone" )] - Smartphone, - /// - /// smoke_free - /// - [Description( "smoke_free" )] - Smoke_Free, - /// - /// smoking_rooms - /// - [Description( "smoking_rooms" )] - Smoking_Rooms, - /// - /// sms - /// - [Description( "sms" )] - Sms, - /// - /// sms_failed - /// - [Description( "sms_failed" )] - Sms_Failed, - /// - /// snooze - /// - [Description( "snooze" )] - Snooze, - /// - /// sort - /// - [Description( "sort" )] - Sort, - /// - /// sort_by_alpha - /// - [Description( "sort_by_alpha" )] - Sort_By_Alpha, - /// - /// spa - /// - [Description( "spa" )] - Spa, - /// - /// space_bar - /// - [Description( "space_bar" )] - Space_Bar, - /// - /// speaker - /// - [Description( "speaker" )] - Speaker, - /// - /// speaker_group - /// - [Description( "speaker_group" )] - Speaker_Group, - /// - /// speaker_notes - /// - [Description( "speaker_notes" )] - Speaker_Notes, - /// - /// speaker_notes_off - /// - [Description( "speaker_notes_off" )] - Speaker_Notes_Off, - /// - /// speaker_phone - /// - [Description( "speaker_phone" )] - Speaker_Phone, - /// - /// spellcheck - /// - [Description( "spellcheck" )] - Spellcheck, - /// - /// star - /// - [Description( "star" )] - Star, - /// - /// star_border - /// - [Description( "star_border" )] - Star_Border, - /// - /// star_half - /// - [Description( "star_half" )] - Star_Half, - /// - /// stars - /// - [Description( "stars" )] - Stars, - /// - /// stay_current_landscape - /// - [Description( "stay_current_landscape" )] - Stay_Current_Landscape, - /// - /// stay_current_portrait - /// - [Description( "stay_current_portrait" )] - Stay_Current_Portrait, - /// - /// stay_primary_landscape - /// - [Description( "stay_primary_landscape" )] - Stay_Primary_Landscape, - /// - /// stay_primary_portrait - /// - [Description( "stay_primary_portrait" )] - Stay_Primary_Portrait, - /// - /// stop - /// - [Description( "stop" )] - Stop, - /// - /// stop_screen_share - /// - [Description( "stop_screen_share" )] - Stop_Screen_Share, - /// - /// storage - /// - [Description( "storage" )] - Storage, - /// - /// store - /// - [Description( "store" )] - Store, - /// - /// store_mall_directory - /// - [Description( "store_mall_directory" )] - Store_Mall_Directory, - /// - /// straighten - /// - [Description( "straighten" )] - Straighten, - /// - /// streetview - /// - [Description( "streetview" )] - Streetview, - /// - /// strikethrough_s - /// - [Description( "strikethrough_s" )] - Strikethrough_S, - /// - /// style - /// - [Description( "style" )] - Style, - /// - /// subdirectory_arrow_left - /// - [Description( "subdirectory_arrow_left" )] - Subdirectory_Arrow_Left, - /// - /// subdirectory_arrow_right - /// - [Description( "subdirectory_arrow_right" )] - Subdirectory_Arrow_Right, - /// - /// subject - /// - [Description( "subject" )] - Subject, - /// - /// subscriptions - /// - [Description( "subscriptions" )] - Subscriptions, - /// - /// subtitles - /// - [Description( "subtitles" )] - Subtitles, - /// - /// subway - /// - [Description( "subway" )] - Subway, - /// - /// supervisor_account - /// - [Description( "supervisor_account" )] - Supervisor_Account, - /// - /// surround_sound - /// - [Description( "surround_sound" )] - Surround_Sound, - /// - /// swap_calls - /// - [Description( "swap_calls" )] - Swap_Calls, - /// - /// swap_horiz - /// - [Description( "swap_horiz" )] - Swap_Horiz, - /// - /// swap_vert - /// - [Description( "swap_vert" )] - Swap_Vert, - /// - /// swap_vertical_circle - /// - [Description( "swap_vertical_circle" )] - Swap_Vertical_Circle, - /// - /// switch_camera - /// - [Description( "switch_camera" )] - Switch_Camera, - /// - /// switch_video - /// - [Description( "switch_video" )] - Switch_Video, - /// - /// sync - /// - [Description( "sync" )] - Sync, - /// - /// sync_disabled - /// - [Description( "sync_disabled" )] - Sync_Disabled, - /// - /// sync_problem - /// - [Description( "sync_problem" )] - Sync_Problem, - /// - /// system_update - /// - [Description( "system_update" )] - System_Update, - /// - /// system_update_alt - /// - [Description( "system_update_alt" )] - System_Update_Alt, - /// - /// tab - /// - [Description( "tab" )] - Tab, - /// - /// tab_unselected - /// - [Description( "tab_unselected" )] - Tab_Unselected, - /// - /// tablet - /// - [Description( "tablet" )] - Tablet, - /// - /// tablet_android - /// - [Description( "tablet_android" )] - Tablet_Android, - /// - /// tablet_mac - /// - [Description( "tablet_mac" )] - Tablet_Mac, - /// - /// tag_faces - /// - [Description( "tag_faces" )] - Tag_Faces, - /// - /// tap_and_play - /// - [Description( "tap_and_play" )] - Tap_And_Play, - /// - /// terrain - /// - [Description( "terrain" )] - Terrain, - /// - /// text_fields - /// - [Description( "text_fields" )] - Text_Fields, - /// - /// text_format - /// - [Description( "text_format" )] - Text_Format, - /// - /// textsms - /// - [Description( "textsms" )] - Textsms, - /// - /// texture - /// - [Description( "texture" )] - Texture, - /// - /// theaters - /// - [Description( "theaters" )] - Theaters, - /// - /// thumb_down - /// - [Description( "thumb_down" )] - Thumb_Down, - /// - /// thumb_up - /// - [Description( "thumb_up" )] - Thumb_Up, - /// - /// thumbs_up_down - /// - [Description( "thumbs_up_down" )] - Thumbs_Up_Down, - /// - /// time_to_leave - /// - [Description( "time_to_leave" )] - Time_To_Leave, - /// - /// timelapse - /// - [Description( "timelapse" )] - Timelapse, - /// - /// timeline - /// - [Description( "timeline" )] - Timeline, - /// - /// timer - /// - [Description( "timer" )] - Timer, - /// - /// timer_10 - /// - [Description( "timer_10" )] - Timer_10, - /// - /// timer_3 - /// - [Description( "timer_3" )] - Timer_3, - /// - /// timer_off - /// - [Description( "timer_off" )] - Timer_Off, - /// - /// title - /// - [Description( "title" )] - Title, - /// - /// toc - /// - [Description( "toc" )] - Toc, - /// - /// today - /// - [Description( "today" )] - Today, - /// - /// toll - /// - [Description( "toll" )] - Toll, - /// - /// tonality - /// - [Description( "tonality" )] - Tonality, - /// - /// touch_app - /// - [Description( "touch_app" )] - Touch_App, - /// - /// toys - /// - [Description( "toys" )] - Toys, - /// - /// track_changes - /// - [Description( "track_changes" )] - Track_Changes, - /// - /// traffic - /// - [Description( "traffic" )] - Traffic, - /// - /// train - /// - [Description( "train" )] - Train, - /// - /// tram - /// - [Description( "tram" )] - Tram, - /// - /// transfer_within_a_station - /// - [Description( "transfer_within_a_station" )] - Transfer_Within_A_Station, - /// - /// transform - /// - [Description( "transform" )] - Transform, - /// - /// translate - /// - [Description( "translate" )] - Translate, - /// - /// trending_down - /// - [Description( "trending_down" )] - Trending_Down, - /// - /// trending_flat - /// - [Description( "trending_flat" )] - Trending_Flat, - /// - /// trending_up - /// - [Description( "trending_up" )] - Trending_Up, - /// - /// tune - /// - [Description( "tune" )] - Tune, - /// - /// turned_in - /// - [Description( "turned_in" )] - Turned_In, - /// - /// turned_in_not - /// - [Description( "turned_in_not" )] - Turned_In_Not, - /// - /// tv - /// - [Description( "tv" )] - Tv, - /// - /// unarchive - /// - [Description( "unarchive" )] - Unarchive, - /// - /// undo - /// - [Description( "undo" )] - Undo, - /// - /// unfold_less - /// - [Description( "unfold_less" )] - Unfold_Less, - /// - /// unfold_more - /// - [Description( "unfold_more" )] - Unfold_More, - /// - /// update - /// - [Description( "update" )] - Update, - /// - /// usb - /// - [Description( "usb" )] - Usb, - /// - /// verified_user - /// - [Description( "verified_user" )] - Verified_User, - /// - /// vertical_align_bottom - /// - [Description( "vertical_align_bottom" )] - Vertical_Align_Bottom, - /// - /// vertical_align_center - /// - [Description( "vertical_align_center" )] - Vertical_Align_Center, - /// - /// vertical_align_top - /// - [Description( "vertical_align_top" )] - Vertical_Align_Top, - /// - /// vibration - /// - [Description( "vibration" )] - Vibration, - /// - /// video_call - /// - [Description( "video_call" )] - Video_Call, - /// - /// video_label - /// - [Description( "video_label" )] - Video_Label, - /// - /// video_library - /// - [Description( "video_library" )] - Video_Library, - /// - /// videocam - /// - [Description( "videocam" )] - Videocam, - /// - /// videocam_off - /// - [Description( "videocam_off" )] - Videocam_Off, - /// - /// videogame_asset - /// - [Description( "videogame_asset" )] - Videogame_Asset, - /// - /// view_agenda - /// - [Description( "view_agenda" )] - View_Agenda, - /// - /// view_array - /// - [Description( "view_array" )] - View_Array, - /// - /// view_carousel - /// - [Description( "view_carousel" )] - View_Carousel, - /// - /// view_column - /// - [Description( "view_column" )] - View_Column, - /// - /// view_comfy - /// - [Description( "view_comfy" )] - View_Comfy, - /// - /// view_compact - /// - [Description( "view_compact" )] - View_Compact, - /// - /// view_day - /// - [Description( "view_day" )] - View_Day, - /// - /// view_headline - /// - [Description( "view_headline" )] - View_Headline, - /// - /// view_list - /// - [Description( "view_list" )] - View_List, - /// - /// view_module - /// - [Description( "view_module" )] - View_Module, - /// - /// view_quilt - /// - [Description( "view_quilt" )] - View_Quilt, - /// - /// view_stream - /// - [Description( "view_stream" )] - View_Stream, - /// - /// view_week - /// - [Description( "view_week" )] - View_Week, - /// - /// vignette - /// - [Description( "vignette" )] - Vignette, - /// - /// visibility - /// - [Description( "visibility" )] - Visibility, - /// - /// visibility_off - /// - [Description( "visibility_off" )] - Visibility_Off, - /// - /// voice_chat - /// - [Description( "voice_chat" )] - Voice_Chat, - /// - /// voicemail - /// - [Description( "voicemail" )] - Voicemail, - /// - /// volume_down - /// - [Description( "volume_down" )] - Volume_Down, - /// - /// volume_mute - /// - [Description( "volume_mute" )] - Volume_Mute, - /// - /// volume_off - /// - [Description( "volume_off" )] - Volume_Off, - /// - /// volume_up - /// - [Description( "volume_up" )] - Volume_Up, - /// - /// vpn_key - /// - [Description( "vpn_key" )] - Vpn_Key, - /// - /// vpn_lock - /// - [Description( "vpn_lock" )] - Vpn_Lock, - /// - /// wallpaper - /// - [Description( "wallpaper" )] - Wallpaper, - /// - /// warning - /// - [Description( "warning" )] - Warning, - /// - /// watch - /// - [Description( "watch" )] - Watch, - /// - /// watch_later - /// - [Description( "watch_later" )] - Watch_Later, - /// - /// wb_auto - /// - [Description( "wb_auto" )] - Wb_Auto, - /// - /// wb_cloudy - /// - [Description( "wb_cloudy" )] - Wb_Cloudy, - /// - /// wb_incandescent - /// - [Description( "wb_incandescent" )] - Wb_Incandescent, - /// - /// wb_iridescent - /// - [Description( "wb_iridescent" )] - Wb_Iridescent, - /// - /// wb_sunny - /// - [Description( "wb_sunny" )] - Wb_Sunny, - /// - /// wc - /// - [Description( "wc" )] - Wc, - /// - /// web - /// - [Description( "web" )] - Web, - /// - /// web_asset - /// - [Description( "web_asset" )] - Web_Asset, - /// - /// weekend - /// - [Description( "weekend" )] - Weekend, - /// - /// whatshot - /// - [Description( "whatshot" )] - Whatshot, - /// - /// widgets - /// - [Description( "widgets" )] - Widgets, - /// - /// wifi - /// - [Description( "wifi" )] - Wifi, - /// - /// wifi_lock - /// - [Description( "wifi_lock" )] - Wifi_Lock, - /// - /// wifi_tethering - /// - [Description( "wifi_tethering" )] - Wifi_Tethering, - /// - /// work - /// - [Description( "work" )] - Work, - /// - /// wrap_text - /// - [Description( "wrap_text" )] - Wrap_Text, - /// - /// youtube_searched_for - /// - [Description( "youtube_searched_for" )] - Youtube_Searched_For, - /// - /// zoom_in - /// - [Description( "zoom_in" )] - Zoom_In, - /// - /// zoom_out - /// - [Description( "zoom_out" )] - Zoom_Out, - /// - /// zoom_out_map - /// - [Description( "zoom_out_map" )] - Zoom_Out_Map, - } -} diff --git a/src/Util.Ui.Core/Enums/RotateType.cs b/src/Util.Ui.Core/Enums/RotateType.cs deleted file mode 100644 index fecd29059..000000000 --- a/src/Util.Ui.Core/Enums/RotateType.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.ComponentModel; - -namespace Util.Ui.Enums { - /// - /// 图标旋转类型 - /// - public enum RotateType { - /// - /// 默认 - /// - [Description( "normal" )] - Default, - /// - /// 顺时针旋转90度 - /// - [Description( "fa-rotate-90" )] - Rotate90, - /// - /// 顺时针旋转180度 - /// - [Description( "fa-rotate-180" )] - Rotate180, - /// - /// 顺时针旋转270度 - /// - [Description( "fa-rotate-270" )] - Rotate270, - /// - /// 水平翻转 - /// - [Description( "fa-flip-horizontal" )] - FlipHorizontal, - /// - /// 垂直翻转 - /// - [Description( "fa-flip-vertical" )] - FlipVertical - } -} diff --git a/src/Util.Ui.Core/Extensions/Extensions.Component.Data.cs b/src/Util.Ui.Core/Extensions/Extensions.Component.Data.cs deleted file mode 100644 index 67f1d98fb..000000000 --- a/src/Util.Ui.Core/Extensions/Extensions.Component.Data.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Components.Internal; -using Util.Ui.Configs; -using Util.Ui.Operations.Datas; - -namespace Util.Ui.Extensions { - /// - /// 组件扩展 - 数据 - /// - public static partial class Extensions { - /// - /// 设置Url - /// - /// 组件类型 - /// 组件实例 - /// Url - public static TComponent Url( this TComponent component, string url ) where TComponent : IComponent, IUrl { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Url, url ); - } ); - return component; - } - - /// - /// 设置数据源 - /// - /// 组件类型 - /// 组件实例 - /// 数据源 - public static TComponent DataSource( this TComponent component, string dataSource ) where TComponent : IComponent, IDataSource { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.DataSource, dataSource ); - } ); - return component; - } - } -} diff --git a/src/Util.Ui.Core/Extensions/Extensions.Component.Event.cs b/src/Util.Ui.Core/Extensions/Extensions.Component.Event.cs deleted file mode 100644 index d7764d037..000000000 --- a/src/Util.Ui.Core/Extensions/Extensions.Component.Event.cs +++ /dev/null @@ -1,109 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Components.Internal; -using Util.Ui.Configs; -using Util.Ui.Operations.Events; - -namespace Util.Ui.Extensions { - /// - /// 组件扩展 - 事件 - /// - public static partial class Extensions { - /// - /// 单击事件处理函数 - /// - /// 组件类型 - /// 组件实例 - /// 单击事件处理函数,范例:handle() - public static TComponent OnClick( this TComponent component, string handler ) where TComponent : IOption, IOnClick { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.OnClick,handler ); - } ); - return component; - } - - /// - /// 变更事件处理函数 - /// - /// 组件类型 - /// 组件实例 - /// 变更事件处理函数,范例:handle() - public static TComponent OnChange( this TComponent component, string handler ) where TComponent : IOption, IOnChange { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.OnChange, handler ); - } ); - return component; - } - - /// - /// 获得焦点事件处理函数 - /// - /// 组件类型 - /// 组件实例 - /// 获得焦点事件处理函数,范例:handle() - public static TComponent OnFocus( this TComponent component, string handler ) where TComponent : IOption, IOnFocus { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.OnFocus, handler ); - } ); - return component; - } - - /// - /// 失去焦点事件处理函数 - /// - /// 组件类型 - /// 组件实例 - /// 失去焦点事件处理函数,范例:handle() - public static TComponent OnBlur( this TComponent component, string handler ) where TComponent : IOption, IOnBlur { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.OnBlur, handler ); - } ); - return component; - } - - /// - /// 键盘按键事件处理函数 - /// - /// 组件类型 - /// 组件实例 - /// 键盘按键事件处理函数,范例:handle() - public static TComponent OnKeyup( this TComponent component, string handler ) where TComponent : IOption, IOnKeyup { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.OnKeyup, handler ); - } ); - return component; - } - - /// - /// 键盘按下事件处理函数 - /// - /// 组件类型 - /// 组件实例 - /// 键盘按下事件处理函数,范例:handle() - public static TComponent OnKeydown( this TComponent component, string handler ) where TComponent : IOption, IOnKeydown { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.OnKeydown, handler ); - } ); - return component; - } - - /// - /// 提交事件处理函数 - /// - /// 组件类型 - /// 组件实例 - /// 提交事件处理函数,范例:handle() - public static TComponent OnSubmit( this TComponent component, string handler ) where TComponent : IOption, IOnSubmit { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.OnSubmit, handler ); - } ); - return component; - } - } -} diff --git a/src/Util.Ui.Core/Extensions/Extensions.Component.Form.cs b/src/Util.Ui.Core/Extensions/Extensions.Component.Form.cs deleted file mode 100644 index 0a6602f45..000000000 --- a/src/Util.Ui.Core/Extensions/Extensions.Component.Form.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Components.Internal; -using Util.Ui.Configs; -using Util.Ui.Operations.Forms; - -namespace Util.Ui.Extensions { - /// - /// 组件扩展 - 表单 - /// - public static partial class Extensions { - /// - /// 设置占位提示符 - /// - /// 组件类型 - /// 组件实例 - /// 文本 - public static TComponent Placeholder( this TComponent component, string text ) where TComponent : IComponent, IPlaceholder { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Placeholder, text ); - } ); - return component; - } - - /// - /// 模型绑定 - /// - /// 组件类型 - /// 组件实例 - /// 模型 - public static TComponent Model( this TComponent component, string model ) where TComponent : IModel { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Model, model ); - } ); - return component; - } - } -} diff --git a/src/Util.Ui.Core/Extensions/Extensions.Component.Icon.cs b/src/Util.Ui.Core/Extensions/Extensions.Component.Icon.cs deleted file mode 100644 index e1abbefc8..000000000 --- a/src/Util.Ui.Core/Extensions/Extensions.Component.Icon.cs +++ /dev/null @@ -1,104 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Components.Internal; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Operations; -using Util.Ui.Operations.Effects; - -namespace Util.Ui.Extensions { - /// - /// 组件扩展 - 图标 - /// - public static partial class Extensions { - /// - /// 图标 - /// - /// 组件类型 - /// 组件实例 - /// Font Awesome图标 - public static TComponent Icon( this TComponent component, FontAwesomeIcon? icon ) where TComponent : IOption, ISetIcon { - var option = component as IOptionConfig; - option?.Config( config => { - if ( icon == null ) - return; - config.SetAttribute( UiConst.FontAwesomeIcon, icon ); - } ); - return component; - } - - /// - /// 图标 - /// - /// 组件类型 - /// 组件实例 - /// Material图标 - public static TComponent Icon( this TComponent component, MaterialIcon? icon ) where TComponent : IOption, ISetIcon { - var option = component as IOptionConfig; - option?.Config( config => { - if( icon == null ) - return; - config.SetAttribute( UiConst.MaterialIcon, icon ); - } ); - return component; - } - - /// - /// 设置图标大小 - /// - /// 组件类型 - /// 组件实例 - /// 图标大小 - public static TComponent Size( this TComponent component, IconSize size ) where TComponent : IIcon { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Size, size ); - } ); - return component; - } - - /// - /// 持续旋转 - /// - /// 组件类型 - /// 组件实例 - /// 是否旋转 - public static TComponent Spin( this TComponent component, bool isSpin = true ) where TComponent : IComponent,ISpin { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Spin, isSpin ); - } ); - return component; - } - - /// - /// 旋转 - /// - /// 组件类型 - /// 组件实例 - /// 旋转类型 - public static TComponent Rotate( this TComponent component, RotateType type ) where TComponent : IIcon { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Rotate, type ); - } ); - return component; - } - - /// - /// 子图标 - /// - /// 组件类型 - /// 组件实例 - /// 子图标 - /// 子图标class - public static TComponent Child( this TComponent component, FontAwesomeIcon childIcon,string @class = null ) where TComponent : IIcon { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Child, childIcon.ToString() ); - if( string.IsNullOrWhiteSpace( @class ) == false ) - config.SetAttribute( UiConst.ChildClass, @class ); - } ); - return component; - } - } -} diff --git a/src/Util.Ui.Core/Extensions/Extensions.Component.Style.cs b/src/Util.Ui.Core/Extensions/Extensions.Component.Style.cs deleted file mode 100644 index e63a69452..000000000 --- a/src/Util.Ui.Core/Extensions/Extensions.Component.Style.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Components.Internal; -using Util.Ui.Configs; -using Util.Ui.Operations.Styles; - -namespace Util.Ui.Extensions { - /// - /// 组件扩展 - 样式 - /// - public static partial class Extensions { - /// - /// 设置class - /// - /// 组件类型 - /// 组件实例 - /// css类名 - public static TComponent Class( this TComponent component, string @class ) where TComponent : IOption, IClass { - var option = component as IOptionConfig; - option?.Config( config => { - config.AddClass( @class ); - } ); - return component; - } - - /// - /// 设置样式 - /// - /// 组件类型 - /// 组件实例 - /// 样式 - public static TComponent Style( this TComponent component, string style ) where TComponent : IOption, IStyle { - var option = component as IOptionConfig; - option?.Config( config => { - config.OutputAttributes.Add( UiConst.Style, style ); - } ); - return component; - } - } -} diff --git a/src/Util.Ui.Core/Extensions/Extensions.Component.Validation.cs b/src/Util.Ui.Core/Extensions/Extensions.Component.Validation.cs deleted file mode 100644 index 3667a8562..000000000 --- a/src/Util.Ui.Core/Extensions/Extensions.Component.Validation.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Components.Internal; -using Util.Ui.Configs; -using Util.Ui.Operations.Forms.Validations; - -namespace Util.Ui.Extensions { - /// - /// 组件扩展 - 验证 - /// - public static partial class Extensions { - /// - /// 必填项 - /// - /// 组件类型 - /// 组件实例 - /// 错误消息 - public static TComponent Required( this TComponent component, string message = null ) where TComponent : IRequired { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Required,true ); - config.SetAttribute( UiConst.RequiredMessage, message ); - } ); - return component; - } - - /// - /// 最小长度 - /// - /// 组件类型 - /// 组件实例 - /// 最小长度 - /// 错误消息 - public static TComponent MinLength( this TComponent component, int minLength, string message = null ) where TComponent : IComponent, IMinLength { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.MinLength,minLength ); - config.SetAttribute( UiConst.MinLengthMessage,message ); - } ); - return component; - } - - /// - /// 最大长度 - /// - /// 组件类型 - /// 组件实例 - /// 最大长度 - public static TComponent MaxLength( this TComponent component, int maxLength ) where TComponent : IComponent, IMaxLength { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.MaxLength, maxLength ); - } ); - return component; - } - } -} diff --git a/src/Util.Ui.Core/Extensions/Extensions.Component.cs b/src/Util.Ui.Core/Extensions/Extensions.Component.cs deleted file mode 100644 index 99733fb3a..000000000 --- a/src/Util.Ui.Core/Extensions/Extensions.Component.cs +++ /dev/null @@ -1,123 +0,0 @@ -using Util.Ui.Components; -using Util.Ui.Components.Internal; -using Util.Ui.Configs; -using Util.Ui.Operations; - -namespace Util.Ui.Extensions { - /// - /// 组件扩展 - /// - public static partial class Extensions { - /// - /// 添加属性 - /// - /// 组件类型 - /// 组件实例 - /// 属性名 - /// 属性值 - public static TComponent Attribute( this TComponent component, string name, string value ) where TComponent : IOption,IAttribute { - var option = component as IOptionConfig; - option?.Config( config => { - config.OutputAttributes.Add( name, value ); - } ); - return component; - } - - /// - /// 添加属性 - /// - /// 组件类型 - /// 组件实例 - /// 属性值 - public static TComponent Attribute( this TComponent component, string value ) where TComponent : IOption, IAttribute { - var option = component as IOptionConfig; - option?.Config( config => { - config.OutputAttributes.Add( value, null ); - } ); - return component; - } - - /// - /// 设置标识 - /// - /// 组件类型 - /// 组件实例 - /// 组件标识 - public static TComponent Id( this TComponent component, string id ) where TComponent : IOption { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Id, id ); - } ); - return component; - } - - /// - /// 设置名称 - /// - /// 组件类型 - /// 组件实例 - /// 组件名称 - public static TComponent Name( this TComponent component, string name ) where TComponent : IOption, IName { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Name, name ); - } ); - return component; - } - - /// - /// 设置文本 - /// - /// 组件类型 - /// 组件实例 - /// 文本 - public static TComponent Text( this TComponent component, string text ) where TComponent : IOption, IText { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Text, text ); - } ); - return component; - } - - /// - /// 禁用 - /// - /// 组件类型 - /// 组件实例 - public static TComponent Disable( this TComponent component ) where TComponent : IOption, IDisabled { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Disabled, true ); - } ); - return component; - } - - /// - /// 提示 - /// - /// 组件类型 - /// 组件实例 - /// 提示 - public static TComponent Tooltip( this TComponent component,string tooltip ) where TComponent : IOption, ITooltip { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Tooltip, tooltip ); - } ); - return component; - } - - /// - /// 设置高度 - /// - /// 组件类型 - /// 组件实例 - /// 高度 - public static TComponent Height( this TComponent component, double height ) where TComponent : IOption, IHeight { - var option = component as IOptionConfig; - option?.Config( config => { - config.SetAttribute( UiConst.Height, height ); - } ); - return component; - } - } -} diff --git a/src/Util.Ui.Core/Extensions/Extensions.ModelExpression.cs b/src/Util.Ui.Core/Extensions/Extensions.ModelExpression.cs deleted file mode 100644 index 65b06c44e..000000000 --- a/src/Util.Ui.Core/Extensions/Extensions.ModelExpression.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Reflection; -using Microsoft.AspNetCore.Mvc.ViewFeatures; - -namespace Util.Ui.Extensions { - /// - /// 模型表达式扩展 - /// - public static partial class Extensions { - /// - /// 获取成员 - /// - /// 模型表达式 - public static MemberInfo GetMemberInfo( this ModelExpression expression ) { - var members = expression.Metadata.ContainerType.GetMember( expression.Metadata.PropertyName ); - return members.Length == 0 ? null : members[0]; - } - } -} diff --git a/src/Util.Ui.Core/Extensions/Extensions.TagBuilder.cs b/src/Util.Ui.Core/Extensions/Extensions.TagBuilder.cs deleted file mode 100644 index 270df0641..000000000 --- a/src/Util.Ui.Core/Extensions/Extensions.TagBuilder.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Util.Ui.Builders; -using Util.Ui.Configs; - -namespace Util.Ui.Extensions { - /// - /// 标签生成器扩展 - /// - public static partial class Extensions { - /// - /// 添加输出属性列表 - /// - /// 标签生成器 - /// 配置 - public static TagBuilder AddOutputAttributes( this TagBuilder builder, IConfig config ) { - foreach( var attribute in config.OutputAttributes ) { - if( attribute.Name.ToLower() == UiConst.Class ) - continue; - builder.Attribute( attribute.Name, attribute.Value.SafeString() ); - } - return builder; - } - - /// - /// 添加样式 - /// - /// 标签生成器 - /// 配置 - public static TagBuilder Style( this TagBuilder builder, IConfig config ) { - if( config.OutputAttributes.ContainsName( UiConst.Style ) && config.Contains( UiConst.Style ) == false ) - config.AllAttributes.SetAttribute( UiConst.Style, config.OutputAttributes[UiConst.Style].Value.SafeString() ); - if ( config.Contains( UiConst.Style ) ) - builder.Attribute( UiConst.Style, config.GetValue( UiConst.Style ) ); - return builder; - } - - /// - /// 添加class - /// - /// 标签生成器 - /// 配置 - public static TagBuilder Class( this TagBuilder builder, IConfig config ) { - if( config.OutputAttributes.ContainsName( UiConst.Class ) && config.Contains( UiConst.Class ) == false ) - config.AllAttributes.SetAttribute( UiConst.Class, config.OutputAttributes[UiConst.Class].Value.SafeString() ); - if( config.Contains( UiConst.Class ) ) - config.AddClass( config.GetValue( UiConst.Class ) ); - config.GetClassList().ForEach( s => builder.Class( s ) ); - return builder; - } - } -} diff --git a/src/Util.Ui.Core/Renders/RenderBase.cs b/src/Util.Ui.Core/Renders/RenderBase.cs deleted file mode 100644 index 2c4700dc2..000000000 --- a/src/Util.Ui.Core/Renders/RenderBase.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.IO; -using System.Text.Encodings.Web; -using Util.Ui.Builders; -using Util.Ui.Configs; - -namespace Util.Ui.Renders { - /// - /// 渲染器 - /// - public abstract class RenderBase : IRender { - /// - /// 配置 - /// - private readonly IConfig _config; - /// - /// 标签生成器 - /// - private TagBuilder _builder; - - /// - /// 初始化渲染器 - /// - /// 配置 - protected RenderBase( IConfig config ) { - _config = config; - } - - /// - /// 标签生成器 - /// - protected TagBuilder Builder => _builder ?? ( _builder = GetTagBuilder() ); - - /// - /// 获取标签生成器 - /// - protected abstract TagBuilder GetTagBuilder(); - - /// - /// 渲染 - /// - /// 流写入器 - /// 编码 - public virtual void WriteTo( TextWriter writer, HtmlEncoder encoder ) { - Builder.WriteTo( writer, encoder ); - } - - /// - /// 渲染起始标签 - /// - /// 流写入器 - public virtual void RenderStartTag( TextWriter writer ) { - Builder.RenderStartTag( writer ); - if( Builder.HasInnerHtml ) - Builder.RenderBody( writer ); - } - - /// - /// 渲染结束标签 - /// - /// 流写入器 - public virtual void RenderEndTag( TextWriter writer ) { - Builder.RenderEndTag( writer ); - } - - /// - /// 输出组件Html - /// - public override string ToString() { - var validateMessage = _config.Validate(); - return string.IsNullOrWhiteSpace( validateMessage ) ? Builder.ToString() : $"验证失败:{validateMessage}"; - } - - /// - /// 配置标识 - /// - protected virtual void ConfigId( TagBuilder builder ) { - if( _config.Contains( UiConst.Id ) ) - builder.AddAttribute( $"#{_config.GetValue( UiConst.Id )}", "", false ); - } - } -} diff --git a/src/Util.Ui.Core/Services/UiServiceBase.cs b/src/Util.Ui.Core/Services/UiServiceBase.cs deleted file mode 100644 index 056199415..000000000 --- a/src/Util.Ui.Core/Services/UiServiceBase.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Mvc.Rendering; - -namespace Util.Ui.Services { - /// - /// 组件服务 - /// - public class UiServiceBase : IContext { - /// - /// 初始化组件服务 - /// - /// HtmlHelper - public UiServiceBase( IHtmlHelper helper ) { - Helper = helper; - } - - /// - /// HtmlHelper - /// - public IHtmlHelper Helper { get; } - } -} diff --git a/src/Util.Ui.Core/TagHelpers/Context.cs b/src/Util.Ui.Core/TagHelpers/Context.cs deleted file mode 100644 index 86e0ce78f..000000000 --- a/src/Util.Ui.Core/TagHelpers/Context.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Microsoft.AspNetCore.Html; -using Microsoft.AspNetCore.Razor.TagHelpers; - -namespace Util.Ui.TagHelpers { - /// - /// TagHelper上下文 - /// - public class Context { - /// - /// 初始化TagHelper上下文 - /// - /// TagHelper上下文 - /// TagHelper输出 - /// 内容 - public Context( TagHelperContext context, TagHelperOutput output, IHtmlContent content ) { - AllAttributes = new TagHelperAttributeList( context.AllAttributes ) ; - OutputAttributes = output.Attributes; - Content = content; - } - - /// - /// 全部属性集合 - /// - public TagHelperAttributeList AllAttributes { get; } - - /// - /// 输出属性集合,TagHelper中未明确定义的属性从该集合获取 - /// - public TagHelperAttributeList OutputAttributes { get; } - - /// - /// 内容 - /// - public IHtmlContent Content { get; } - } -} diff --git a/src/Util.Ui.Core/TagHelpers/TagHelperBase.cs b/src/Util.Ui.Core/TagHelpers/TagHelperBase.cs deleted file mode 100644 index 7b9ed7587..000000000 --- a/src/Util.Ui.Core/TagHelpers/TagHelperBase.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Logs; -using Util.Logs.Extensions; -using Util.Ui.Components; -using Util.Ui.Renders; - -namespace Util.Ui.TagHelpers { - /// - /// TagHelper - /// - public abstract class TagHelperBase : TagHelper { - /// - /// 标识 - /// - public string Id { get; set; } - - /// - /// 渲染 - /// - public override async void Process( TagHelperContext context, TagHelperOutput output ) { - var content = await output.GetChildContentAsync(); - var render = GetRender( new Context( context, output, content ) ); - output.SuppressOutput(); - output.PostElement.SetHtmlContent( render ); - } - - /// - /// 获取渲染器 - /// - /// 上下文 - protected abstract IRender GetRender( Context context ); - - /// - /// 写日志 - /// - protected void WriteLog( IRender render,string caption ) { - var log = GetLog(); - if( log.IsTraceEnabled == false ) - return; - log.Class( GetType().FullName ) - .Caption( caption ) - .Content( render.ToString() ) - .Trace(); - } - - /// - /// 获取日志操作 - /// - private ILog GetLog() { - try { - return Log.GetLog( ComponentBase.TraceLogName ); - } - catch { - return Log.Null; - } - } - } -} diff --git a/src/Util.Ui.NgAlain/04-Util.Ui.NgAlain.csproj b/src/Util.Ui.NgAlain/04-Util.Ui.NgAlain.csproj new file mode 100644 index 000000000..3d8c736e4 --- /dev/null +++ b/src/Util.Ui.NgAlain/04-Util.Ui.NgAlain.csproj @@ -0,0 +1,34 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Ui.NgAlain是Util应用框架的ng-alain封装类库 + Util.Ui.NgAlain + Util.Ui.NgAlain + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Ui.NgAlain.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Ui.NgAlain.xml + + + + + True + False + + + + + + + + + + diff --git a/src/Util.Ui.NgAlain/Components/Ellipsis/Builders/EllipsisBuilder.cs b/src/Util.Ui.NgAlain/Components/Ellipsis/Builders/EllipsisBuilder.cs new file mode 100644 index 000000000..f641478e3 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Ellipsis/Builders/EllipsisBuilder.cs @@ -0,0 +1,75 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; + +namespace Util.Ui.NgAlain.Components.Ellipsis.Builders; + +/// +/// 文本省略标签生成器 +/// +public class EllipsisBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 初始化文本省略标签生成器 + /// + /// 配置 + public EllipsisBuilder( Config config ) : base( config, "ellipsis" ) { + _config = config; + } + + /// + /// 配置工具提示 + /// + public EllipsisBuilder Tooltip() { + AttributeIfNotEmpty( "[tooltip]", _config.GetValue( UiConst.Tooltip ) ); + return this; + } + + /// + /// 配置文本最大长度 + /// + public EllipsisBuilder Length() { + AttributeIfNotEmpty( "[length]", _config.GetValue( UiConst.Length ) ); + return this; + } + + /// + /// 配置文本最大行数 + /// + public EllipsisBuilder Lines() { + AttributeIfNotEmpty( "[lines]", _config.GetValue( UiConst.Lines ) ); + return this; + } + + /// + /// 配置是否将全角字符的长度视为2来计算字符串长度 + /// + public EllipsisBuilder FullWidthRecognition() { + AttributeIfNotEmpty( "[fullWidthRecognition]", _config.GetValue( UiConst.FullWidthRecognition ) ); + return this; + } + + /// + /// 配置溢出尾巴 + /// + public EllipsisBuilder Tail() { + AttributeIfNotEmpty( "tail", _config.GetValue( UiConst.Tail ) ); + AttributeIfNotEmpty( "[tail]", _config.GetValue( AngularConst.BindTail ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Tooltip().Length().Lines().FullWidthRecognition().Tail(); + var value = _config.GetValue( UiConst.Value ); + if ( value.IsEmpty() ) + return; + AppendContent( "{{" + value + "}}" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Ellipsis/EllipsisTagHelper.cs b/src/Util.Ui.NgAlain/Components/Ellipsis/EllipsisTagHelper.cs new file mode 100644 index 000000000..55bc4f7a3 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Ellipsis/EllipsisTagHelper.cs @@ -0,0 +1,61 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Ellipsis.Helpers; +using Util.Ui.NgAlain.Components.Ellipsis.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.Ellipsis; + +/// +/// 文本省略组件,生成的标签为<ellipsis></ellipsis> +/// +[HtmlTargetElement( "util-ellipsis" )] +public class EllipsisTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 属性表达式 + /// + public ModelExpression For { get; set; } + /// + /// [tooltip],是否显示文本完整内容的提示,默认值: false + /// + public string Tooltip { get; set; } + /// + /// [length],在按照长度截取下的文本最大字符数,超过则截取省略 + /// + public string Length { get; set; } + /// + /// [lines],在按照行数截取下最大的行数,超过则截取省略 + /// + public string Lines { get; set; } + /// + /// [fullWidthRecognition],是否将全角字符的长度视为2来计算字符串长度,默认值: false + /// + public string FullWidthRecognition { get; set; } + /// + /// tail,指定溢出尾巴,默认值: ... + /// + public string Tail { get; set; } + /// + /// [tail],指定溢出尾巴,默认值: ... + /// + public string BindTail { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var loader = new EllipsisExpressionLoader(); + loader.Load( _config ); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new EllipsisRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Ellipsis/Helpers/EllipsisExpressionLoader.cs b/src/Util.Ui.NgAlain/Components/Ellipsis/Helpers/EllipsisExpressionLoader.cs new file mode 100644 index 000000000..39ca02606 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Ellipsis/Helpers/EllipsisExpressionLoader.cs @@ -0,0 +1,19 @@ +using Util.Ui.Configs; +using Util.Ui.Expressions; +using Util.Ui.NgZorro.Expressions; + +namespace Util.Ui.NgAlain.Components.Ellipsis.Helpers; + +/// +/// 文本省略表达式加载器 +/// +public class EllipsisExpressionLoader : NgZorroExpressionLoaderBase { + /// + /// 加载模型信息 + /// + /// 配置 + /// 模型表达式信息 + protected override void Load( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.Value, info.SafePropertyName, false ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Ellipsis/Renders/EllipsisRender.cs b/src/Util.Ui.NgAlain/Components/Ellipsis/Renders/EllipsisRender.cs new file mode 100644 index 000000000..b52834e9b --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Ellipsis/Renders/EllipsisRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Ellipsis.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.Ellipsis.Renders; + +/// +/// 文本省略渲染器 +/// +public class EllipsisRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化文本省略渲染器 + /// + /// 配置 + public EllipsisRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new EllipsisBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new EllipsisBuilder( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/FooterToolbars/Builders/FooterToolbarBuilder.cs b/src/Util.Ui.NgAlain/Components/FooterToolbars/Builders/FooterToolbarBuilder.cs new file mode 100644 index 000000000..97c5037e0 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/FooterToolbars/Builders/FooterToolbarBuilder.cs @@ -0,0 +1,47 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; + +namespace Util.Ui.NgAlain.Components.FooterToolbars.Builders; + +/// +/// 底部工具栏生成器 +/// +public class FooterToolbarBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 初始化底部工具栏生成器 + /// + /// 配置 + public FooterToolbarBuilder( Config config ) : base( config, "footer-toolbar" ) { + _config = config; + } + + /// + /// 配置错误收集 + /// + public FooterToolbarBuilder ErrorCollect() { + AttributeIfNotEmpty( "[errorCollect]", _config.GetValue( UiConst.ErrorCollect ) ); + return this; + } + + /// + /// 配置额外信息 + /// + public FooterToolbarBuilder Extra() { + AttributeIfNotEmpty( "extra", _config.GetValue( UiConst.Extra ) ); + AttributeIfNotEmpty( "[extra]", _config.GetValue( AngularConst.BindExtra ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + ErrorCollect().Extra(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/FooterToolbars/FooterToolbarTagHelper.cs b/src/Util.Ui.NgAlain/Components/FooterToolbars/FooterToolbarTagHelper.cs new file mode 100644 index 000000000..4adb60a48 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/FooterToolbars/FooterToolbarTagHelper.cs @@ -0,0 +1,32 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.FooterToolbars.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.FooterToolbars; + +/// +/// ng-alain 底部工具栏,生成的标签为<footer-toolbar></footer-toolbar> +/// +[HtmlTargetElement( "util-footer-toolbar" )] +public class FooterToolbarTagHelper : AngularTagHelperBase { + /// + /// [errorCollect],是否收集表单错误,需要放入 form 标签内, 默认值: false + /// + public string ErrorCollect { get; set; } + /// + /// extra,额外信息,显示在左边 + /// + public string Extra { get; set; } + /// + /// [extra],额外信息,显示在左边 + /// + public string BindExtra { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new FooterToolbarRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/FooterToolbars/Renders/FooterToolbarRender.cs b/src/Util.Ui.NgAlain/Components/FooterToolbars/Renders/FooterToolbarRender.cs new file mode 100644 index 000000000..4e6b6b208 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/FooterToolbars/Renders/FooterToolbarRender.cs @@ -0,0 +1,40 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.FooterToolbars.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.FooterToolbars.Renders; + +/// +/// 底部工具栏渲染器 +/// +public class FooterToolbarRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化底部工具栏渲染器 + /// + /// 配置 + public FooterToolbarRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new FooterToolbarBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new FooterToolbarRender( _config.CopyRemoveAttributes() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/I18n/Builders/I18nBuilder.cs b/src/Util.Ui.NgAlain/Components/I18n/Builders/I18nBuilder.cs new file mode 100644 index 000000000..0ca1635c6 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/I18n/Builders/I18nBuilder.cs @@ -0,0 +1,56 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgAlain.Components.I18n.Builders; + +/// +/// i18n多语言标签生成器 +/// +public class I18nBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 初始化i18n多语言标签生成器 + /// + /// 配置 + public I18nBuilder( Config config ) : base( config, "span" ) { + _config = config; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + ConfigInnerHtml(); + } + + /// + /// 配置innerHTML属性 + /// + private void ConfigInnerHtml() { + var key = _config.GetValue( UiConst.Key ); + if ( key.IsEmpty() ) + return; + Attribute( "[innerHTML]", $"'{key}'|i18n{GetParam()}" ); + } + + /// + /// 获取参数 + /// + private string GetParam() { + var result = new StringBuilder(); + var param = _config.GetValue( UiConst.Param ); + if ( param.IsEmpty() ) + return null; + result.Append( ":" ); + if ( param.StartsWith( "{" ) == false ) + result.Append( "{" ); + result.Append( param ); + if ( param.EndsWith( "}" ) == false ) + result.Append( "}" ); + return result.ToString(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/I18n/I18nTagHelper.cs b/src/Util.Ui.NgAlain/Components/I18n/I18nTagHelper.cs new file mode 100644 index 000000000..9054e1e5a --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/I18n/I18nTagHelper.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.I18n.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.I18n; + +/// +/// i18n多语言显示组件,支持显示html标签,生成的标签为<span></span> +/// +[HtmlTargetElement( "util-i18n", TagStructure = TagStructure.WithoutEndTag )] +public class I18nTagHelper : AngularTagHelperBase { + /// + /// 多语言配置键,范例: util.ok + /// + public string Key { get; set; } + /// + /// 传递给i18n管道的参数,范例: a:2,b:'c' + /// + public string Param { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new I18nRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/I18n/Renders/I18nRender.cs b/src/Util.Ui.NgAlain/Components/I18n/Renders/I18nRender.cs new file mode 100644 index 000000000..98705e1f6 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/I18n/Renders/I18nRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.I18n.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.I18n.Renders; + +/// +/// i18n多语言渲染器 +/// +public class I18nRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化i18n多语言渲染器 + /// + /// 配置 + public I18nRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new I18nBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new I18nRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/PageHeaders/Builders/PageHeaderBuilder.cs b/src/Util.Ui.NgAlain/Components/PageHeaders/Builders/PageHeaderBuilder.cs new file mode 100644 index 000000000..61642b839 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/PageHeaders/Builders/PageHeaderBuilder.cs @@ -0,0 +1,196 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgAlain.Components.PageHeaders.Builders; + +/// +/// 页头标签生成器 +/// +public class PageHeaderBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 初始化页头标签生成器 + /// + /// 配置 + public PageHeaderBuilder( Config config ) : base( config, "page-header" ) { + _config = config; + } + + /// + /// 配置标题 + /// + public PageHeaderBuilder Title() { + SetTitle( _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[title]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 设置标题 + /// + private void SetTitle( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[title]", value ); + return; + } + AttributeIfNotEmpty( "title", value ); + } + + /// + /// 配置自动设置标题 + /// + public PageHeaderBuilder AutoTitle() { + AttributeIfNotEmpty( "[autoTitle]", _config.GetValue( UiConst.AutoTitle ) ); + return this; + } + + /// + /// 配置同步标题 + /// + public PageHeaderBuilder SyncTitle() { + AttributeIfNotEmpty( "[syncTitle]", _config.GetValue( UiConst.SyncTitle ) ); + return this; + } + + /// + /// 配置首页文本 + /// + public PageHeaderBuilder Home() { + AttributeIfNotEmpty( "home", _config.GetValue( UiConst.Home ) ); + AttributeIfNotEmpty( "[home]", _config.GetValue( AngularConst.BindHome ) ); + return this; + } + + /// + /// 配置首页链接 + /// + public PageHeaderBuilder HomeLink() { + AttributeIfNotEmpty( "homeLink", _config.GetValue( UiConst.HomeLink ) ); + AttributeIfNotEmpty( "[homeLink]", _config.GetValue( AngularConst.BindHomeLink ) ); + return this; + } + + /// + /// 配置首页国际化 + /// + public PageHeaderBuilder HomeI18n() { + AttributeIfNotEmpty( "homeI18n", _config.GetValue( UiConst.HomeI18n ) ); + AttributeIfNotEmpty( "[homeI18n]", _config.GetValue( AngularConst.BindHomeI18n ) ); + return this; + } + + /// + /// 配置自动生成导航 + /// + public PageHeaderBuilder AutoBreadcrumb() { + AttributeIfNotEmpty( "[autoBreadcrumb]", _config.GetValue( UiConst.AutoBreadcrumb ) ); + return this; + } + + /// + /// 配置递归查找导航 + /// + public PageHeaderBuilder RecursiveBreadcrumb() { + AttributeIfNotEmpty( "[recursiveBreadcrumb]", _config.GetValue( UiConst.RecursiveBreadcrumb ) ); + return this; + } + + /// + /// 配置加载状态 + /// + public PageHeaderBuilder Loading() { + AttributeIfNotEmpty( "[loading]", _config.GetValue( UiConst.Loading ) ); + return this; + } + + /// + /// 配置是否定宽 + /// + public PageHeaderBuilder Wide() { + AttributeIfNotEmpty( "[wide]", _config.GetValue( UiConst.Wide ) ); + return this; + } + + /// + /// 配置是否固定模式 + /// + public PageHeaderBuilder Fixed() { + AttributeIfNotEmpty( "[fixed]", _config.GetValue( UiConst.Fixed ) ); + return this; + } + + /// + /// 配置固定偏移值 + /// + public PageHeaderBuilder FixedOffsetTop() { + AttributeIfNotEmpty( "[fixedOffsetTop]", _config.GetValue( UiConst.FixedOffsetTop ) ); + return this; + } + + /// + /// 配置自定义导航区域 + /// + public PageHeaderBuilder Breadcrumb() { + AttributeIfNotEmpty( "[breadcrumb]", _config.GetValue( UiConst.Breadcrumb ) ); + return this; + } + + /// + /// 配置自定义Logo区域 + /// + public PageHeaderBuilder Logo() { + AttributeIfNotEmpty( "[logo]", _config.GetValue( UiConst.Logo ) ); + return this; + } + + /// + /// 配置自定义操作区域 + /// + public PageHeaderBuilder Action() { + AttributeIfNotEmpty( "[action]", _config.GetValue( UiConst.Action ) ); + return this; + } + + /// + /// 配置自定义内容区域 + /// + public PageHeaderBuilder Content() { + AttributeIfNotEmpty( "[content]", _config.GetValue( UiConst.Content ) ); + return this; + } + + /// + /// 配置自定义额外信息区域 + /// + public PageHeaderBuilder Extra() { + AttributeIfNotEmpty( "[extra]", _config.GetValue( UiConst.Extra ) ); + return this; + } + + /// + /// 配置自定义标签区域 + /// + public PageHeaderBuilder Tab() { + AttributeIfNotEmpty( "[tab]", _config.GetValue( UiConst.Tab ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Title().AutoTitle().SyncTitle() + .Home().HomeLink().HomeI18n() + .AutoBreadcrumb().RecursiveBreadcrumb() + .Loading().Wide().Fixed().FixedOffsetTop() + .Breadcrumb().Logo().Action().Content().Extra().Tab(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/PageHeaders/PageHeaderTagHelper.cs b/src/Util.Ui.NgAlain/Components/PageHeaders/PageHeaderTagHelper.cs new file mode 100644 index 000000000..58fb35917 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/PageHeaders/PageHeaderTagHelper.cs @@ -0,0 +1,108 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.PageHeaders.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.PageHeaders; + +/// +/// ng-alain 页头,生成的标签为<page-header></page-header> +/// +[HtmlTargetElement( "util-page-header-x" )] +public class PageHeaderTagHelper : AngularTagHelperBase { + /// + /// title,标题,支持i18n + /// + public string Title { get; set; } + /// + /// [title],标题 + /// + public string BindTitle { get; set; } + /// + /// [autoTitle],是否自动生成标题,以当前路由从主菜单中定位,默认值: true + /// + public string AutoTitle { get; set; } + /// + /// [syncTitle],是否自动将标题同步至 TitleService、ReuseService 下,仅 title 为 string 类型时有效,默认值: true + /// + public string SyncTitle { get; set; } + /// + /// home,首页文本,若指定空表示不显示,默认值: 首页 + /// + public string Home { get; set; } + /// + /// [home],首页文本,若指定空表示不显示,默认值: 首页 + /// + public string BindHome { get; set; } + /// + /// homeLink,首页链接,默认值: / + /// + public string HomeLink { get; set; } + /// + /// [homeLink],首页链接,默认值: / + /// + public string BindHomeLink { get; set; } + /// + /// homeI18n,首页链接国际化参数 + /// + public string HomeI18n { get; set; } + /// + /// [homeI18n],首页链接国际化参数 + /// + public string BindHomeI18n { get; set; } + /// + /// [autoBreadcrumb],是否自动生成导航,以当前路由从主菜单中定位,默认值: true + /// + public string AutoBreadcrumb { get; set; } + /// + /// [recursiveBreadcrumb],是否自动向上递归查找,范例: 菜单数据源包含 /ware,则 /ware/1 也视为 /ware 项,默认值: false + /// + public string RecursiveBreadcrumb { get; set; } + /// + /// [loading],是否加载中,默认值: false + /// + public string Loading { get; set; } + /// + /// [wide],是否定宽,默认值: false + /// + public string Wide { get; set; } + /// + /// [fixed],是否固定模式,默认值: false + /// + public string Fixed { get; set; } + /// + /// [fixedOffsetTop],固定偏移值,默认值: 64 + /// + public string FixedOffsetTop { get; set; } + /// + /// [breadcrumb],自定义导航区域,类型: TemplateRef<void> + /// + public string Breadcrumb { get; set; } + /// + /// [logo],自定义Logo区域,类型: TemplateRef<void> + /// + public string Logo { get; set; } + /// + /// [action],自定义操作区域,类型: TemplateRef<void> + /// + public string Action { get; set; } + /// + /// [content],自定义内容区域,类型: TemplateRef<void> + /// + public string Content { get; set; } + /// + /// [extra],自定义额外信息区域,类型: TemplateRef<void> + /// + public string Extra { get; set; } + /// + /// [tab],自定义标签区域,类型: TemplateRef<void> + /// + public string Tab { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new PageHeaderRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/PageHeaders/Renders/PageHeaderRender.cs b/src/Util.Ui.NgAlain/Components/PageHeaders/Renders/PageHeaderRender.cs new file mode 100644 index 000000000..c69a2c6ff --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/PageHeaders/Renders/PageHeaderRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.PageHeaders.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.PageHeaders.Renders; + +/// +/// 页头渲染器 +/// +public class PageHeaderRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化页头渲染器 + /// + /// 配置 + public PageHeaderRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new PageHeaderBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new PageHeaderRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sg/Builders/SgBuilder.cs b/src/Util.Ui.NgAlain/Components/Sg/Builders/SgBuilder.cs new file mode 100644 index 000000000..0dd59e34e --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sg/Builders/SgBuilder.cs @@ -0,0 +1,37 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgAlain.Components.Sg.Builders; + +/// +/// 简易栅格列标签生成器 +/// +public class SgBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 初始化简易栅格列标签生成器 + /// + /// 配置 + public SgBuilder( Config config ) : base( config, "sg" ) { + _config = config; + } + + /// + /// 配置列跨度 + /// + public SgBuilder Span() { + AttributeIfNotEmpty( "[col]", _config.GetValue( UiConst.Span ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Span(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sg/Builders/SgContainerBuilder.cs b/src/Util.Ui.NgAlain/Components/Sg/Builders/SgContainerBuilder.cs new file mode 100644 index 000000000..1e64e9c5d --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sg/Builders/SgContainerBuilder.cs @@ -0,0 +1,47 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; + +namespace Util.Ui.NgAlain.Components.Sg.Builders; + +/// +/// 简易栅格容器标签生成器 +/// +public class SgContainerBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 初始化简易栅格容器标签生成器 + /// + /// 配置 + public SgContainerBuilder( Config config ) : base( config, "div" ) { + _config = config; + base.Attribute( "sg-container" ); + } + + /// + /// 配置间距 + /// + public SgContainerBuilder Gutter() { + AttributeIfNotEmpty( "[gutter]", _config.GetValue( UiConst.Gutter ) ); + return this; + } + + /// + /// 配置列数 + /// + public SgContainerBuilder Column() { + AttributeIfNotEmpty( "[col]", _config.GetValue( UiConst.Column ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Gutter().Column(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sg/Renders/SgContainerRender.cs b/src/Util.Ui.NgAlain/Components/Sg/Renders/SgContainerRender.cs new file mode 100644 index 000000000..1f2f061bd --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sg/Renders/SgContainerRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Sg.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.Sg.Renders; + +/// +/// 简易栅格容器渲染器 +/// +public class SgContainerRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化简易栅格容器渲染器 + /// + /// 配置 + public SgContainerRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new SgContainerBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new SgContainerRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sg/Renders/SgRender.cs b/src/Util.Ui.NgAlain/Components/Sg/Renders/SgRender.cs new file mode 100644 index 000000000..0ad6417dd --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sg/Renders/SgRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Sg.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.Sg.Renders; + +/// +/// 简易栅格列渲染器 +/// +public class SgRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化简易栅格列渲染器 + /// + /// 配置 + public SgRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new SgBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new SgRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sg/SgContainerTagHelper.cs b/src/Util.Ui.NgAlain/Components/Sg/SgContainerTagHelper.cs new file mode 100644 index 000000000..4794cbfb6 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sg/SgContainerTagHelper.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Sg.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.Sg; + +/// +/// ng-alain 简易栅格容器组件,生成的标签为<div sg-container></div> +/// +[HtmlTargetElement( "util-row-x" )] +[HtmlTargetElement( "util-sg-container" )] +public class SgContainerTagHelper : AngularTagHelperBase { + /// + /// [gutter], 间距,默认值: 32 + /// + public string Gutter { get; set; } + /// + /// [col], 一行显示几列,默认值: 2 + /// + public string Column { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new SgContainerRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sg/SgTagHelper.cs b/src/Util.Ui.NgAlain/Components/Sg/SgTagHelper.cs new file mode 100644 index 000000000..f5a74ef81 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sg/SgTagHelper.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Sg.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.Sg; + +/// +/// ng-alain 简易栅格列组件,生成的标签为<sg></sg> +/// +[HtmlTargetElement( "util-column-x" )] +[HtmlTargetElement( "util-sg" )] +public class SgTagHelper : AngularTagHelperBase { + /// + /// [col], 列跨度,占几列 + /// + public string Span { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new SgRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sv/Builders/SvBuilder.cs b/src/Util.Ui.NgAlain/Components/Sv/Builders/SvBuilder.cs new file mode 100644 index 000000000..1b3ac9713 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sv/Builders/SvBuilder.cs @@ -0,0 +1,210 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.Configs; +using Util.Ui.Extensions; +using Util.Ui.NgAlain.Enums; +using Util.Ui.NgZorro.Components.Buttons.Builders; +using Util.Ui.NgZorro.Components.Icons.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgAlain.Components.Sv.Builders; + +/// +/// 查看列标签生成器 +/// +public class SvBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 初始化查看列标签生成器 + /// + /// 配置 + public SvBuilder( Config config ) : base( config, "sv" ) { + _config = config; + } + + /// + /// 配置列跨度 + /// + public SvBuilder Column() { + AttributeIfNotEmpty( "[col]", _config.GetValue( UiConst.Column ) ); + return this; + } + + /// + /// 配置标签 + /// + public SvBuilder Label() { + SetLabel( _config.GetValue( UiConst.Label ) ); + AttributeIfNotEmpty( "[label]", _config.GetValue( AngularConst.BindLabel ) ); + return this; + } + + /// + /// 设置标签文本 + /// + private void SetLabel( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[label]", value ); + return; + } + AttributeIfNotEmpty( "label", value ); + } + + /// + /// 配置单位 + /// + public SvBuilder Unit() { + AttributeIfNotEmpty( "unit", _config.GetValue( UiConst.Unit ) ); + AttributeIfNotEmpty( "[unit]", _config.GetValue( AngularConst.BindUnit ) ); + return this; + } + + /// + /// 配置显示默认文本 + /// + public SvBuilder Default() { + AttributeIfNotEmpty( "[default]", _config.GetValue( UiConst.Default ) ); + return this; + } + + /// + /// 配置类型 + /// + public SvBuilder Type() { + AttributeIfNotEmpty( "type", _config.GetValue( UiConst.Type )?.Description() ); + AttributeIfNotEmpty( "[type]", _config.GetValue( AngularConst.BindType ) ); + return this; + } + + /// + /// 配置标签可选信息 + /// + public SvBuilder Optional() { + AttributeIfNotEmpty( "optional", _config.GetValue( UiConst.Optional ) ); + AttributeIfNotEmpty( "[optional]", _config.GetValue( AngularConst.BindOptional ) ); + AttributeIfNotEmpty( "optionalHelp", _config.GetValue( UiConst.OptionalHelp ) ); + AttributeIfNotEmpty( "[optionalHelp]", _config.GetValue( AngularConst.BindOptionalHelp ) ); + AttributeIfNotEmpty( "optionalHelpColor", _config.GetValue( UiConst.OptionalHelpColor ) ); + AttributeIfNotEmpty( "[optionalHelpColor]", _config.GetValue( AngularConst.BindOptionalHelpColor ) ); + return this; + } + + /// + /// 配置冒号 + /// + public SvBuilder NoColon() { + AttributeIfNotEmpty( "[noColon]", _config.GetValue( UiConst.NoColon ) ); + return this; + } + + /// + /// 配置是否隐藏标签 + /// + public SvBuilder HideLabel() { + AttributeIfNotEmpty( "[hideLabel]", _config.GetValue( UiConst.HideLabel ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Column().Label().Unit().Default().Type() + .Optional().NoColon().HideLabel(); + } + + /// + /// 配置内容 + /// + protected override void ConfigContent( Config config ) { + if ( config.Content.IsEmpty() == false ) { + config.Content.AppendTo( this ); + return; + } + ConfigValue(); + CopyToClipboard(); + } + + /// + /// 配置值 + /// + private void ConfigValue() { + var dataType = _config.GetValue( UiConst.Type ); + var value = _config.GetValue( UiConst.Value ); + if ( value.IsEmpty() ) + return; + if ( dataType == DataType.Bool ) { + LoadBool( value ); + return; + } + if ( dataType == DataType.Date ) { + LoadDate( value ); + return; + } + if ( dataType == DataType.Number ) { + LoadNumber( value ); + return; + } + SetContent( "{{" + GetValue( value ) + "}}" ); + } + + /// + /// 加载布尔值 + /// + protected void LoadBool( string value ) { + AppendContent( new IconBuilder( _config ).Type( AntDesignIcon.Check.Description() ).Attribute( "*ngIf", $"{value}" ) ); + AppendContent( new IconBuilder( _config ).Type( AntDesignIcon.Close.Description() ).Attribute( "*ngIf", $"!({value})" ) ); + } + + /// + /// 加载日期值 + /// + protected void LoadDate( string value ) { + var format = _config.GetValue( UiConst.DateFormat ); + if ( format.IsEmpty() ) + format = "yyyy-MM-dd HH:mm:ss"; + SetContent( $"{{{{{value}|date:\"{format}\"}}}}" ); + } + + /// + /// 加载数值 + /// + protected void LoadNumber( string value ) { + SetContent( "{{" + value + "}}" ); + } + + /// + /// 获取值 + /// + private string GetValue( string value ) { + var enabledI18n = _config.GetValue( UiConst.ValueI18n ); + if ( enabledI18n ) + return $"{value}|i18n"; + return value; + } + + /// + /// 配置复制到剪贴板 + /// + private void CopyToClipboard() { + if ( _config.GetValue( UiConst.Clipboard ) != true ) + return; + var value = _config.GetValue( UiConst.Value ); + if ( value.IsEmpty() ) + return; + _config.SetAttribute( UiConst.CopyToClipboard, value ); + var buttonBuilder = new ButtonBuilder( _config ); + buttonBuilder.Attribute( "nz-button" ); + buttonBuilder.NgIf( value ); + buttonBuilder.CopyToClipboard().Type().Icon(); + AppendContent( buttonBuilder ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sv/Builders/SvContainerBuilder.cs b/src/Util.Ui.NgAlain/Components/Sv/Builders/SvContainerBuilder.cs new file mode 100644 index 000000000..c23f70e55 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sv/Builders/SvContainerBuilder.cs @@ -0,0 +1,107 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Enums; + +namespace Util.Ui.NgAlain.Components.Sv.Builders; + +/// +/// 查看容器标签生成器 +/// +public class SvContainerBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 初始化查看容器标签生成器 + /// + /// 配置 + public SvContainerBuilder( Config config ) : base( config, "sv-container" ) { + _config = config; + } + + /// + /// 配置列数 + /// + public SvContainerBuilder Column() { + AttributeIfNotEmpty( "[col]", _config.GetValue( UiConst.Column ) ); + return this; + } + + /// + /// 配置尺寸 + /// + public SvContainerBuilder Size() { + AttributeIfNotEmpty( "size", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[size]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置布局 + /// + public SvContainerBuilder Layout() { + AttributeIfNotEmpty( "layout", _config.GetValue( UiConst.Layout )?.Description() ); + AttributeIfNotEmpty( "[layout]", _config.GetValue( AngularConst.BindLayout ) ); + return this; + } + + /// + /// 配置间距 + /// + public SvContainerBuilder Gutter() { + AttributeIfNotEmpty( "[gutter]", _config.GetValue( UiConst.Gutter ) ); + return this; + } + + /// + /// 配置标签文本宽度 + /// + public SvContainerBuilder LabelWidth() { + AttributeIfNotEmpty( "[labelWidth]", _config.GetValue( UiConst.LabelWidth ) ); + return this; + } + + /// + /// 配置显示默认文本 + /// + public SvContainerBuilder Default() { + AttributeIfNotEmpty( "[default]", _config.GetValue( UiConst.Default ) ); + return this; + } + + /// + /// 配置标题 + /// + public SvContainerBuilder Title() { + AttributeIfNotEmpty( "title", _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[title]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 配置冒号 + /// + public SvContainerBuilder NoColon() { + AttributeIfNotEmpty( "[noColon]", _config.GetValue( UiConst.NoColon ) ); + return this; + } + + /// + /// 配置是否显示边框 + /// + public SvContainerBuilder Bordered() { + AttributeIfNotEmpty( "[bordered]", _config.GetValue( UiConst.Bordered ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Column().Size().Layout().Gutter().LabelWidth() + .Default().Title().NoColon().Bordered(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sv/Builders/SvTitleBuilder.cs b/src/Util.Ui.NgAlain/Components/Sv/Builders/SvTitleBuilder.cs new file mode 100644 index 000000000..1c5987495 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sv/Builders/SvTitleBuilder.cs @@ -0,0 +1,47 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgAlain.Components.Sv.Builders; + +/// +/// 查看标题标签生成器 +/// +public class SvTitleBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化查看标题标签生成器 + /// + /// 配置 + public SvTitleBuilder( Config config ) : base( config, "sv-title" ) { + _config = config; + } + + /// + /// 配置标签 + /// + public SvTitleBuilder Title() { + var options = NgZorroOptionsService.GetOptions(); + var title = _config.GetValue( UiConst.Title ); + if ( options.EnableI18n ) { + this.AppendContentByI18n( title ); + return this; + } + AppendContent(title); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Title(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sv/Builders/SvValueBuilder.cs b/src/Util.Ui.NgAlain/Components/Sv/Builders/SvValueBuilder.cs new file mode 100644 index 000000000..9136d127d --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sv/Builders/SvValueBuilder.cs @@ -0,0 +1,67 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Enums; + +namespace Util.Ui.NgAlain.Components.Sv.Builders; + +/// +/// 查看值标签生成器 +/// +public class SvValueBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 初始化查看值标签生成器 + /// + /// 配置 + public SvValueBuilder( Config config ) : base( config, "sv-value" ) { + _config = config; + } + + /// + /// 配置前缀 + /// + public SvValueBuilder Prefix() { + AttributeIfNotEmpty( "prefix", _config.GetValue( UiConst.Prefix ) ); + AttributeIfNotEmpty( "[prefix]", _config.GetValue( AngularConst.BindPrefix ) ); + return this; + } + + /// + /// 配置单位 + /// + public SvValueBuilder Unit() { + AttributeIfNotEmpty( "unit", _config.GetValue( UiConst.Unit ) ); + AttributeIfNotEmpty( "[unit]", _config.GetValue( AngularConst.BindUnit ) ); + return this; + } + + /// + /// 配置文字提示 + /// + public SvValueBuilder Tooltip() { + AttributeIfNotEmpty( "tooltip", _config.GetValue( UiConst.Tooltip ) ); + AttributeIfNotEmpty( "[tooltip]", _config.GetValue( AngularConst.BindTooltip ) ); + return this; + } + + /// + /// 配置尺寸 + /// + public SvValueBuilder Size() { + AttributeIfNotEmpty( "size", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[size]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Prefix().Unit().Tooltip().Size(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sv/Helpers/SvExpressionLoader.cs b/src/Util.Ui.NgAlain/Components/Sv/Helpers/SvExpressionLoader.cs new file mode 100644 index 000000000..efa80a453 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sv/Helpers/SvExpressionLoader.cs @@ -0,0 +1,47 @@ +using Util.Ui.Configs; +using Util.Ui.Expressions; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Expressions; + +namespace Util.Ui.NgAlain.Components.Sv.Helpers; + +/// +/// 查看列表达式加载器 +/// +public class SvExpressionLoader : NgZorroExpressionLoaderBase { + /// + /// 加载模型信息 + /// + /// 配置 + /// 模型表达式信息 + protected override void Load( Config config, ModelExpressionInfo info ) { + LoadLabel( config, info ); + LoadValue( config, info ); + } + + /// + /// 加载标签 + /// + protected void LoadLabel( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.Label, info.DisplayName, false ); + } + + /// + /// 加载值 + /// + protected virtual void LoadValue( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.Value, info.SafePropertyName, false ); + if ( info.IsBool ) { + config.SetAttribute( UiConst.Type, DataType.Bool ); + return; + } + if ( info.IsDate ) { + config.SetAttribute( UiConst.Type, DataType.Date ); + return; + } + if ( info.IsNumber ) { + config.SetAttribute( UiConst.Type, DataType.Number ); + return; + } + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sv/Renders/SvContainerRender.cs b/src/Util.Ui.NgAlain/Components/Sv/Renders/SvContainerRender.cs new file mode 100644 index 000000000..30b10f582 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sv/Renders/SvContainerRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Sv.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.Sv.Renders; + +/// +/// 查看容器渲染器 +/// +public class SvContainerRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化查看容器渲染器 + /// + /// 配置 + public SvContainerRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new SvContainerBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new SvContainerRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sv/Renders/SvRender.cs b/src/Util.Ui.NgAlain/Components/Sv/Renders/SvRender.cs new file mode 100644 index 000000000..04d7205bd --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sv/Renders/SvRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Sv.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.Sv.Renders; + +/// +/// 查看列渲染器 +/// +public class SvRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化查看列渲染器 + /// + /// 配置 + public SvRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new SvBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new SvRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sv/Renders/SvTitleRender.cs b/src/Util.Ui.NgAlain/Components/Sv/Renders/SvTitleRender.cs new file mode 100644 index 000000000..2578d95a7 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sv/Renders/SvTitleRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Sv.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.Sv.Renders; + +/// +/// 查看标题渲染器 +/// +public class SvTitleRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化查看标题渲染器 + /// + /// 配置 + public SvTitleRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new SvTitleBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new SvTitleRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sv/Renders/SvValueRender.cs b/src/Util.Ui.NgAlain/Components/Sv/Renders/SvValueRender.cs new file mode 100644 index 000000000..aa00360d2 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sv/Renders/SvValueRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Sv.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.Sv.Renders; + +/// +/// 查看值渲染器 +/// +public class SvValueRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化查看值渲染器 + /// + /// 配置 + public SvValueRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new SvValueBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new SvValueRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sv/SvContainerTagHelper.cs b/src/Util.Ui.NgAlain/Components/Sv/SvContainerTagHelper.cs new file mode 100644 index 000000000..759f8fca2 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sv/SvContainerTagHelper.cs @@ -0,0 +1,70 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Sv.Renders; +using Util.Ui.NgAlain.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.Sv; + +/// +/// ng-alain 查看容器组件,生成的标签为<sv-container></sv-container> +/// +[HtmlTargetElement( "util-descriptions-x" )] +[HtmlTargetElement( "util-sv-container" )] +public class SvContainerTagHelper : AngularTagHelperBase { + /// + /// [col], 一行显示几列,默认值: 2 + /// + public string Column { get; set; } + /// + /// size, 尺寸, 可选项: 'default','small','large' + /// + public SvSize Size { get; set; } + /// + /// [size], 尺寸, 可选项: 'default','small','large' + /// + public string BindSize { get; set; } + /// + /// layout, 布局, 可选项: 'horizontal','vertical', 默认值: 'horizontal' + /// + public SvLayout Layout { get; set; } + /// + /// [layout], 布局, 可选项: 'horizontal','vertical', 默认值: 'horizontal' + /// + public string BindLayout { get; set; } + /// + /// [gutter], 间距, 默认值: 32 + /// + public string Gutter { get; set; } + /// + /// [labelWidth], 标签文本宽度 + /// + public string LabelWidth { get; set; } + /// + /// [default], 是否显示默认文本, 默认值: true + /// + public string Default { get; set; } + /// + /// title, 标题,类型: string | TemplateRef<void> + /// + public string Title { get; set; } + /// + /// [title], 标题,类型: string | TemplateRef<void> + /// + public string BindTitle { get; set; } + /// + /// [noColon], 是否不显示标签后面的冒号, 默认值: false + /// + public string NoColon { get; set; } + /// + /// [bordered], 是否显示边框, 默认值: false + /// + public string Bordered { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new SvContainerRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sv/SvTagHelper.cs b/src/Util.Ui.NgAlain/Components/Sv/SvTagHelper.cs new file mode 100644 index 000000000..5ea5bea7d --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sv/SvTagHelper.cs @@ -0,0 +1,133 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Sv.Helpers; +using Util.Ui.NgAlain.Components.Sv.Renders; +using Util.Ui.NgAlain.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.Sv; + +/// +/// ng-alain 查看列组件,生成的标签为<sv></sv> +/// +[HtmlTargetElement( "util-descriptions-x-item" )] +[HtmlTargetElement( "util-sv" )] +public class SvTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 扩展属性, 属性表达式 + /// + public ModelExpression For { get; set; } + /// + /// [col], 占几列显示 + /// + public string Column { get; set; } + /// + /// label, 标签, 支持i18n, 类型: string | TemplateRef<void> + /// + public string Label { get; set; } + /// + /// [label], 标签, 类型: string | TemplateRef<void> + /// + public string BindLabel { get; set; } + /// + /// unit, 单位, 类型: string | TemplateRef<void> + /// + public string Unit { get; set; } + /// + /// [unit], 单位, 类型: string | TemplateRef<void> + /// + public string BindUnit { get; set; } + /// + /// [default], 是否显示默认文本 + /// + public string Default { get; set; } + /// + /// type, 类型, 可选值: 'primary','success','danger','warning' + /// + public SvType Type { get; set; } + /// + /// [type], 类型, 可选值: 'primary','success','danger','warning' + /// + public string BindType { get; set; } + /// + /// optional, 标签可选信息, 类型: string | TemplateRef<void> + /// + public string Optional { get; set; } + /// + /// [optional], 标签可选信息, 类型: string | TemplateRef<void> + /// + public string BindOptional { get; set; } + /// + /// optionalHelp, 标签可选帮助, 类型: string | TemplateRef<void> + /// + public string OptionalHelp { get; set; } + /// + /// [optionalHelp], 标签可选帮助, 类型: string | TemplateRef<void> + /// + public string BindOptionalHelp { get; set; } + /// + /// optionalHelpColor, 标签可选帮助背景颜色 + /// + public string OptionalHelpColor { get; set; } + /// + /// [optionalHelpColor], 标签可选帮助背景颜色 + /// + public string BindOptionalHelpColor { get; set; } + /// + /// [noColon], 是否不显示标签后面的冒号, 默认值: false + /// + public string NoColon { get; set; } + /// + /// [hideLabel], 是否隐藏标签, 默认值: false + /// + public string HideLabel { get; set; } + /// + /// 扩展属性,内容值,自动包含在 {{}} 中,范例: 设置为 model.name, 内容为 {{model.name}} + /// + public string Value { get; set; } + /// + /// 扩展属性,值是否支持多语言,设置为 true 添加i18n管道,默认值: false, 范例: value="model.name",value-i18n="true", 内容为 {{model.name|i18n}} + /// + public bool ValueI18n { get; set; } + /// + /// 日期格式化字符串,默认值: yyyy-MM-dd HH:mm:ss,仅在使用属性表达式For时有效,格式说明: + /// 1. 年 - yyyy + /// 2. 月 - MM + /// 3. 日 - dd + /// 4. 时 - HH + /// 5. 分 - mm + /// 6. 秒 - ss + /// 7. 毫秒 - SSS + /// + public string DateFormat { get; set; } + /// + /// 扩展属性 [cdkCopyToClipboard],是否允许复制到剪贴板,注意: 需要设置 value 属性 + /// + public bool Clipboard { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + LoadExpression(); + } + + /// + /// 加载表达式 + /// + private void LoadExpression() { + var loader = new SvExpressionLoader(); + loader.Load( _config ); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new SvRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sv/SvTitleTagHelper.cs b/src/Util.Ui.NgAlain/Components/Sv/SvTitleTagHelper.cs new file mode 100644 index 000000000..5e89d1198 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sv/SvTitleTagHelper.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Sv.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.Sv; + +/// +/// ng-alain 查看标题组件,生成的标签为<sv-title></sv-title> +/// +[HtmlTargetElement( "util-descriptions-x-title" )] +[HtmlTargetElement( "util-sv-title" )] +public class SvTitleTagHelper : AngularTagHelperBase { + /// + /// 扩展属性, 标题, 支持i18n + /// + public string Title { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new SvTitleRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Sv/SvValueTagHelper.cs b/src/Util.Ui.NgAlain/Components/Sv/SvValueTagHelper.cs new file mode 100644 index 000000000..8f39381b9 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Sv/SvValueTagHelper.cs @@ -0,0 +1,54 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Sv.Renders; +using Util.Ui.NgAlain.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.Sv; + +/// +/// ng-alain 查看值组件,生成的标签为<sv-value></sv-value> +/// +[HtmlTargetElement( "util-descriptions-x-value" )] +[HtmlTargetElement( "util-sv-value" )] +public class SvValueTagHelper : AngularTagHelperBase { + /// + /// prefix, 前缀 + /// + public string Prefix { get; set; } + /// + /// [prefix], 前缀 + /// + public string BindPrefix { get; set; } + /// + /// unit, 单位 + /// + public string Unit { get; set; } + /// + /// [unit], 单位 + /// + public string BindUnit { get; set; } + /// + /// tooltip, 文字提示, 类型: string | TemplateRef<void> + /// + public string Tooltip { get; set; } + /// + /// [tooltip], 文字提示, 类型: string | TemplateRef<void> + /// + public string BindTooltip { get; set; } + /// + /// size, 尺寸, 可选值: 'default','small','large', 默认值: default + /// + public SvValueSize Size { get; set; } + /// + /// [size], 尺寸, 可选值: 'default','small','large', 默认值: default + /// + public string BindSize { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new SvValueRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Tinymce/Builders/TinymceBuilder.cs b/src/Util.Ui.NgAlain/Components/Tinymce/Builders/TinymceBuilder.cs new file mode 100644 index 000000000..28afd23e5 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Tinymce/Builders/TinymceBuilder.cs @@ -0,0 +1,153 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Enums; +using Util.Ui.NgZorro.Components.Base; + +namespace Util.Ui.NgAlain.Components.Tinymce.Builders; + +/// +/// Tinymce富文本标签生成器 +/// +public class TinymceBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 标识 + /// + private string _id; + + /// + /// 初始化Tinymce富文本标签生成器 + /// + /// 配置 + public TinymceBuilder( Config config ) : base( config, "tinymce" ) { + _config = config; + } + + /// + /// 扩展标识 + /// + public string ExtendId => $"x_{GetId()}"; + + /// + /// 获取标识 + /// + private string GetId() { + if ( _id.IsEmpty() == false ) + return _id; + _id = _config.GetValue( UiConst.Id ); + if ( _id.IsEmpty() ) + _id = Util.Helpers.Id.Create(); + return _id; + } + + /// + /// 配置品牌 + /// + public TinymceBuilder Branding() { + AttributeIfNotEmpty( "[branding]", _config.GetValue( UiConst.Branding ) ); + return this; + } + + /// + /// 配置是否允许粘贴图片 + /// + public TinymceBuilder PasteDataImages() { + AttributeIfNotEmpty( "[pasteDataImages]", _config.GetValue( UiConst.PasteDataImages ) ); + return this; + } + + /// + /// 配置菜单栏 + /// + public TinymceBuilder Menubar() { + AttributeIfNotEmpty( "menubar", _config.GetValue( UiConst.Menubar ) ); + return this; + } + + /// + /// 配置工具栏模式 + /// + public TinymceBuilder ToolbarMode() { + AttributeIfNotEmpty( "toolbarMode", _config.GetValue( UiConst.ToolbarMode )?.Description() ); + return this; + } + + /// + /// 配置插件列表 + /// + public TinymceBuilder Plugins() { + AttributeIfNotEmpty( "plugins", _config.GetValue( UiConst.Plugins ) ); + return this; + } + + /// + /// 配置工具栏 + /// + public TinymceBuilder Toolbar() { + AttributeIfNotEmpty( "toolbar", _config.GetValue( UiConst.Toolbar ) ); + return this; + } + + /// + /// 配置禁用 + /// + public TinymceBuilder Disabled() { + AttributeIfNotEmpty( "[disabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置占位提示 + /// + public TinymceBuilder Placeholder() { + AttributeIfNotEmpty( "placeholder", _config.GetValue( UiConst.Placeholder ) ); + AttributeIfNotEmpty( "[placeholder]", _config.GetValue( AngularConst.BindPlaceholder ) ); + return this; + } + + /// + /// 配置内容变更事件 + /// + public TinymceBuilder OnChange() { + AttributeIfNotEmpty( "[change]", _config.GetValue( UiConst.OnChange ) ); + return this; + } + + /// + /// 配置config + /// + private void ConfigConfig() { + var config = _config.GetValue( UiConst.Config ); + if ( config.IsEmpty() ) { + config = $"{ExtendId}.config"; + } + AttributeIfNotEmpty( "[config]", config ); + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + NgModel().FormControl().OnModelChange() + .Required().RequiredMessage() + .TableEdit().ValidationExtend() + .Name().Branding().PasteDataImages() + .Menubar().ToolbarMode().Plugins().Toolbar() + .Disabled().Placeholder() + .OnChange(); + ConfigConfig(); + EnableExtend(); + } + + /// + /// 启用扩展 + /// + public void EnableExtend() { + Attribute( $"#{ExtendId}", "xTinymceExtend" ); + Attribute( "x-tinymce-extend" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Tinymce/Renders/TinymceRender.cs b/src/Util.Ui.NgAlain/Components/Tinymce/Renders/TinymceRender.cs new file mode 100644 index 000000000..e5e141218 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Tinymce/Renders/TinymceRender.cs @@ -0,0 +1,40 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.Extensions; +using Util.Ui.NgAlain.Components.Tinymce.Builders; +using Util.Ui.NgZorro.Components.Base; + +namespace Util.Ui.NgAlain.Components.Tinymce.Renders; + +/// +/// Tinymce富文本渲染器 +/// +public class TinymceRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化Tinymce富文本渲染器 + /// + /// 配置 + public TinymceRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new TinymceBuilder( _config ); + builder.Config(); + _config.Content.AppendTo( builder ); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new TinymceRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Components/Tinymce/TinymceTagHelper.cs b/src/Util.Ui.NgAlain/Components/Tinymce/TinymceTagHelper.cs new file mode 100644 index 000000000..63c6df250 --- /dev/null +++ b/src/Util.Ui.NgAlain/Components/Tinymce/TinymceTagHelper.cs @@ -0,0 +1,89 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Tinymce.Renders; +using Util.Ui.NgAlain.Enums; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Inputs.Helpers; +using Util.Ui.Renders; + +namespace Util.Ui.NgAlain.Components.Tinymce; + +/// +/// Tinymce富文本编辑器,生成的标签为<tinymce></tinymce> +/// +[HtmlTargetElement( "util-tinymce" )] +public class TinymceTagHelper : FormControlTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// [branding],是否显示品牌,设为false时,隐藏编辑器界面右下角的“Powered by Tiny”,默认值: false + /// + public string Branding { get; set; } + /// + /// [pasteDataImages],是否允许粘贴图片,默认值: true + /// + public string PasteDataImages { get; set; } + /// + /// menubar,菜单栏,可以是布尔值,设置为false隐藏菜单栏,也可以是空格分隔的菜单列表,范例: file edit insert view format table tools help + /// + public string Menubar { get; set; } + /// + /// toolbarMode,工具栏模式,当工具栏按钮超出编辑器宽度时的排列方式,默认值: wrap + /// + public TinymceToolbarMode ToolbarMode { get; set; } + /// + /// plugins,空格分隔的插件列表 + /// + public string Plugins { get; set; } + /// + /// toolbar,空格分隔的工具栏 + /// + public string Toolbar { get; set; } + /// + /// [config],配置 + /// + public string Config { get; set; } + /// + /// [disabled],是否禁用 + /// + public string Disabled { get; set; } + /// + /// [required],是否必填项 + /// + public string Required { get; set; } + /// + /// 扩展属性 requiredMessage,必填项验证消息 + /// + public string RequiredMessage { get; set; } + /// + /// 扩展属性 [requiredMessage],必填项验证消息 + /// + public string BindRequiredMessage { get; set; } + /// + /// placeholder,占位文本 + /// + public string Placeholder { get; set; } + /// + /// [placeholder],占位文本 + /// + public string BindPlaceholder { get; set; } + /// + /// [change],内容变更事件,类型: (html: string) => void; + /// + public string OnChange { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new InputService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new TinymceRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Enums/SvLayout.cs b/src/Util.Ui.NgAlain/Enums/SvLayout.cs new file mode 100644 index 000000000..f4f17a6f3 --- /dev/null +++ b/src/Util.Ui.NgAlain/Enums/SvLayout.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgAlain.Enums; + +/// +/// 查看布局 +/// +public enum SvLayout { + /// + /// 水平布局 + /// + [Description( "horizontal" )] + Horizontal, + /// + /// 垂直布局 + /// + [Description( "vertical" )] + Vertical +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Enums/SvSize.cs b/src/Util.Ui.NgAlain/Enums/SvSize.cs new file mode 100644 index 000000000..979c6b703 --- /dev/null +++ b/src/Util.Ui.NgAlain/Enums/SvSize.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgAlain.Enums; + +/// +/// 查看大小 +/// +public enum SvSize { + /// + /// 默认 + /// + [Description( "default" )] + Default, + /// + /// 小尺寸 + /// + [Description( "small" )] + Small, + /// + /// 大尺寸 + /// + [Description( "large" )] + Large +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Enums/SvType.cs b/src/Util.Ui.NgAlain/Enums/SvType.cs new file mode 100644 index 000000000..577f0ec91 --- /dev/null +++ b/src/Util.Ui.NgAlain/Enums/SvType.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgAlain.Enums; + +/// +/// 查看类型 +/// +public enum SvType { + /// + /// primary + /// + [Description( "primary" )] + Primary, + /// + /// success + /// + [Description( "success" )] + Success, + /// + /// danger + /// + [Description( "danger" )] + Danger, + /// + /// warning + /// + [Description( "warning" )] + Warning +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Enums/SvValueSize.cs b/src/Util.Ui.NgAlain/Enums/SvValueSize.cs new file mode 100644 index 000000000..feb4910e7 --- /dev/null +++ b/src/Util.Ui.NgAlain/Enums/SvValueSize.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgAlain.Enums; + +/// +/// 查看值大小 +/// +public enum SvValueSize { + /// + /// 默认 + /// + [Description( "default" )] + Default, + /// + /// 小尺寸 + /// + [Description( "small" )] + Small, + /// + /// 大尺寸 + /// + [Description( "large" )] + Large +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Enums/TinymceToolbarMode.cs b/src/Util.Ui.NgAlain/Enums/TinymceToolbarMode.cs new file mode 100644 index 000000000..77fde7415 --- /dev/null +++ b/src/Util.Ui.NgAlain/Enums/TinymceToolbarMode.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgAlain.Enums; + +/// +/// Tinymce工具栏模式 +/// +public enum TinymceToolbarMode { + /// + /// 浮动模式,点击工具栏...图标,溢出的工具栏在下方浮动显示 + /// + [Description( "floating" )] + Floating, + /// + /// 滑动模式,点击工具栏...图标,溢出的工具栏滑动到下方显示 + /// + [Description( "sliding" )] + Sliding, + /// + /// 滚动模式,工具栏溢出时出现滚动条 + /// + [Description( "scrolling" )] + Scrolling, + /// + /// 包装模式,溢出的工具栏显示在主工具栏下方的一个或多个工具栏上 + /// + [Description( "wrap" )] + wrap +} \ No newline at end of file diff --git a/src/Util.Ui.NgAlain/Usings.cs b/src/Util.Ui.NgAlain/Usings.cs new file mode 100644 index 000000000..69e0737bf --- /dev/null +++ b/src/Util.Ui.NgAlain/Usings.cs @@ -0,0 +1,3 @@ +global using System.Text; +global using System.ComponentModel; +global using Microsoft.AspNetCore.Html; diff --git a/src/Util.Ui.NgZorro/03-Util.Ui.NgZorro.csproj b/src/Util.Ui.NgZorro/03-Util.Ui.NgZorro.csproj new file mode 100644 index 000000000..222ce3fc6 --- /dev/null +++ b/src/Util.Ui.NgZorro/03-Util.Ui.NgZorro.csproj @@ -0,0 +1,39 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Ui.NgZorro是Util应用框架的ng-zorro封装类库 + Util.Ui.NgZorro + Util.Ui.NgZorro + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Ui.NgZorro.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Ui.NgZorro.xml + + + + + True + False + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/AppBuilderExtensions.cs b/src/Util.Ui.NgZorro/AppBuilderExtensions.cs new file mode 100644 index 000000000..d2aebb284 --- /dev/null +++ b/src/Util.Ui.NgZorro/AppBuilderExtensions.cs @@ -0,0 +1,71 @@ +using Microsoft.Extensions.DependencyInjection.Extensions; +using Util.Configs; +using Util.Ui.Razor; +using Util.Ui.Razor.Internal; + +namespace Util.Ui.NgZorro; + +/// +/// NgZorro配置扩展 +/// +public static class AppBuilderExtensions { + /// + /// 配置NgZorro依赖服务 + /// + /// 应用生成器 + public static IAppBuilder AddNgZorro( this IAppBuilder builder ) { + return builder.AddNgZorro( null ); + } + + /// + /// 配置NgZorro依赖服务 + /// + /// 应用生成器 + /// NgZorro配置操作 + public static IAppBuilder AddNgZorro( this IAppBuilder builder, Action setupAction ) { + builder.CheckNull( nameof( builder ) ); + builder.Host.ConfigureServices( ( context, services ) => { + var options = new NgZorroOptions(); + setupAction?.Invoke( options ); + services.AddRazorPages( razorPageOptions => { + razorPageOptions.RootDirectory = options.RazorRootDirectory; + } ).AddRazorRuntimeCompilation().AddConventions(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + if ( options.EnableWatchRazor ) + services.AddHostedService(); + ConfigSpaStaticFiles( services, options ); + ConfigRazorOptions( services, options ); + ConfigNgZorroOptions( services, setupAction ); + } ); + return builder; + } + + /// + /// 配置Spa静态文件 + /// + private static void ConfigSpaStaticFiles( IServiceCollection services, NgZorroOptions options ) { + services.AddSpaStaticFiles( configuration => { + configuration.RootPath = options.RootPath; + } ); + } + + /// + /// 配置Razor + /// + private static void ConfigRazorOptions( IServiceCollection services, NgZorroOptions options ) { + void Action( RazorOptions razorOptions ) { + options.MapTo( razorOptions ); + } + services.Configure( (Action)Action ); + } + + /// + /// 配置NgZorro + /// + private static void ConfigNgZorroOptions( IServiceCollection services, Action setupAction ) { + if ( setupAction != null ) + services.Configure( setupAction ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Affixes/AffixTagHelper.cs b/src/Util.Ui.NgZorro/Components/Affixes/AffixTagHelper.cs new file mode 100644 index 000000000..dbd82d2a0 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Affixes/AffixTagHelper.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Affixes.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Affixes; + +/// +/// 固钉,生成的标签为<nz-affix></nz-affix> +/// +[HtmlTargetElement( "util-affix" )] +public class AffixTagHelper : AngularTagHelperBase { + /// + /// [nzOffsetTop],顶部偏移量,距离窗口顶部达到指定偏移量后触发,默认值为0,范例: 10 + /// + public string OffsetTop { get; set; } + /// + /// [nzOffsetBottom],底部偏移量,距离窗口底部达到指定偏移量后触发,范例: 10 + /// + public string OffsetBottom { get; set; } + /// + /// [nzTarget],相对该目标元素进行固定,需要监听其滚动事件的元素,默认为 window + /// + public string Target { get; set; } + /// + /// (nzChange),固定状态改变时触发的回调函数,事件参数为bool类型,true表示触发固定状态 + /// + public string OnChange { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new AffixRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Affixes/Builders/AffixBuilder.cs b/src/Util.Ui.NgZorro/Components/Affixes/Builders/AffixBuilder.cs new file mode 100644 index 000000000..0cef89c6d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Affixes/Builders/AffixBuilder.cs @@ -0,0 +1,61 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Affixes.Builders; + +/// +/// 固钉标签生成器 +/// +public class AffixBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化固钉标签生成器 + /// + /// 配置 + public AffixBuilder( Config config ) : base( config,"nz-affix" ) { + _config = config; + } + + /// + /// 配置顶部偏移量 + /// + public AffixBuilder OffsetTop() { + AttributeIfNotEmpty( "[nzOffsetTop]", _config.GetValue( UiConst.OffsetTop ) ); + return this; + } + + /// + /// 配置底部偏移量 + /// + public AffixBuilder OffsetBottom() { + AttributeIfNotEmpty( "[nzOffsetBottom]", _config.GetValue( UiConst.OffsetBottom ) ); + return this; + } + + /// + /// 配置目标 + /// + public AffixBuilder Target() { + AttributeIfNotEmpty( "[nzTarget]", _config.GetValue( UiConst.Target ) ); + return this; + } + + /// + /// 配置事件 + /// + public AffixBuilder Events() { + AttributeIfNotEmpty( "(nzChange)", _config.GetValue( UiConst.OnChange ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + OffsetTop().OffsetBottom().Target().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Affixes/Renders/AffixRender.cs b/src/Util.Ui.NgZorro/Components/Affixes/Renders/AffixRender.cs new file mode 100644 index 000000000..fff7992f2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Affixes/Renders/AffixRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Affixes.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Affixes.Renders; + +/// +/// 固钉渲染器 +/// +public class AffixRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化固钉渲染器 + /// + /// 配置 + public AffixRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new AffixBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new AffixRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Alerts/AlertTagHelper.cs b/src/Util.Ui.NgZorro/Components/Alerts/AlertTagHelper.cs new file mode 100644 index 000000000..3bf696872 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Alerts/AlertTagHelper.cs @@ -0,0 +1,100 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Alerts.Helpers; +using Util.Ui.NgZorro.Components.Alerts.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Alerts; + +/// +/// 警告提示,生成的标签为<nz-alert></nz-alert> +/// +[HtmlTargetElement( "util-alert" )] +public class AlertTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// [nzBanner],是否顶部公告, 类型: boolean, 默认值: false + /// + public string Banner { get; set; } + /// + /// [nzCloseable],是否可关闭,类型: boolean, 默认值: false + /// + public string Closeable { get; set; } + /// + /// nzCloseText,自定义关闭按钮显示文字,类型: string | TemplateRef<void> + /// + public string CloseText { get; set; } + /// + /// [nzCloseText],自定义关闭按钮显示文字,类型: string | TemplateRef<void> + /// + public string BindCloseText { get; set; } + /// + /// nzDescription,描述,类型: string | TemplateRef<void> + /// + public string Description { get; set; } + /// + /// [nzDescription],描述,类型: string | TemplateRef<void> + /// + public string BindDescription { get; set; } + /// + /// nzMessage,警告提示内容,类型: string | TemplateRef<void> + /// + public string Message { get; set; } + /// + /// [nzMessage],警告提示内容,类型: string | TemplateRef<void> + /// + public string BindMessage { get; set; } + /// + /// [nzShowIcon],是否显示图标, 类型: boolean, 默认值: false, nzBanner 模式下默认值为 true + /// + public string ShowIcon { get; set; } + /// + /// nzIconType,自定义图标类型 + /// + public AntDesignIcon IconType { get; set; } + /// + /// [nzIconType],自定义图标类型 + /// + public string BindIconType { get; set; } + /// + /// [nzIcon],自定义图标,类型: string | TemplateRef<void> + /// + public string Icon { get; set; } + /// + /// nzType,警告类型,指定警告提示的样式,可选值: 'success' | 'info' | 'warning' | 'error',默认值: 'info',nzBanner 模式下默认值为 'warning' + /// + public AlertType Type { get; set; } + /// + /// [nzType],警告类型,指定警告提示的样式,可选值: 'success' | 'info' | 'warning' | 'error',默认值: 'info',nzBanner 模式下默认值为 'warning' + /// + public string BindType { get; set; } + /// + /// [nzAction],自定义操作项,类型: string | TemplateRef<void> + /// + public string Action { get; set; } + /// + /// (nzOnClose),关闭事件,类型: EventEmitter<void> + /// + public string OnClose { get; set; } + + /// + /// 渲染前操作 + /// + /// 上下文 + /// 输出 + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new AlertShareConfigService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new AlertRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Alerts/Builders/AlertBuilder.cs b/src/Util.Ui.NgZorro/Components/Alerts/Builders/AlertBuilder.cs new file mode 100644 index 000000000..4d9c88b29 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Alerts/Builders/AlertBuilder.cs @@ -0,0 +1,132 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Alerts.Builders; + +/// +/// 警告提示标签生成器 +/// +public class AlertBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化警告提示标签生成器 + /// + public AlertBuilder( Config config ) : base( config, "nz-alert" ) { + _config = config; + } + + /// + /// 配置是否顶部公告 + /// + public AlertBuilder Banner() { + AttributeIfNotEmpty( "[nzBanner]", _config.GetValue( UiConst.Banner ) ); + return this; + } + + /// + /// 配置是否可关闭 + /// + public AlertBuilder Closeable() { + AttributeIfNotEmpty( "[nzCloseable]", _config.GetValue( UiConst.Closeable ) ); + return this; + } + + /// + /// 配置关闭按钮文字 + /// + public AlertBuilder CloseText() { + AttributeIfNotEmpty( "nzCloseText", _config.GetValue( UiConst.CloseText ) ); + AttributeIfNotEmpty( "[nzCloseText]", _config.GetValue( AngularConst.BindCloseText ) ); + return this; + } + + /// + /// 配置描述 + /// + public AlertBuilder Description() { + AttributeIfNotEmpty( "nzDescription", _config.GetValue( UiConst.Description ) ); + AttributeIfNotEmpty( "[nzDescription]", _config.GetValue( AngularConst.BindDescription ) ); + return this; + } + + /// + /// 配置提示内容 + /// + public AlertBuilder Message() { + AttributeIfNotEmpty( "nzMessage", _config.GetValue( UiConst.Message ) ); + BindMessage( _config.GetValue( AngularConst.BindMessage ) ); + return this; + } + + /// + /// 配置提示内容 + /// + public AlertBuilder BindMessage( string value ) { + AttributeIfNotEmpty( "[nzMessage]", value ); + return this; + } + + /// + /// 配置是否显示图标 + /// + public AlertBuilder ShowIcon() { + if ( _config.GetValue( UiConst.IconType ).IsEmpty() == false || + _config.GetValue( AngularConst.BindIconType ).IsEmpty() == false || + _config.GetValue( UiConst.Icon ).IsEmpty() == false ) { + Attribute( "[nzShowIcon]", "true" ); + return this; + } + AttributeIfNotEmpty( "[nzShowIcon]", _config.GetValue( UiConst.ShowIcon ) ); + return this; + } + + /// + /// 配置图标 + /// + public AlertBuilder Icon() { + AttributeIfNotEmpty( "nzIconType", _config.GetValue( UiConst.IconType )?.Description() ); + AttributeIfNotEmpty( "[nzIconType]", _config.GetValue( AngularConst.BindIconType ) ); + AttributeIfNotEmpty( "[nzIcon]", _config.GetValue( UiConst.Icon ) ); + return this; + } + + /// + /// 配置警告类型 + /// + public AlertBuilder Type() { + AttributeIfNotEmpty( "nzType", _config.GetValue( UiConst.Type )?.Description() ); + AttributeIfNotEmpty( "[nzType]", _config.GetValue( AngularConst.BindType ) ); + return this; + } + + /// + /// 配置自定义操作项 + /// + public AlertBuilder Action() { + AttributeIfNotEmpty( "[nzAction]", _config.GetValue( UiConst.Action ) ); + return this; + } + + /// + /// 配置事件 + /// + public AlertBuilder Events() { + AttributeIfNotEmpty( "(nzOnClose)", _config.GetValue( UiConst.OnClose ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + Banner().Closeable().CloseText().Description() + .Message().ShowIcon().Icon().Type().Action() + .Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Alerts/Configs/AlertShareConfig.cs b/src/Util.Ui.NgZorro/Components/Alerts/Configs/AlertShareConfig.cs new file mode 100644 index 000000000..4db99e05d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Alerts/Configs/AlertShareConfig.cs @@ -0,0 +1,31 @@ +using Util.Helpers; + +namespace Util.Ui.NgZorro.Components.Alerts.Configs; + +/// +/// 警告提示共享配置 +/// +public class AlertShareConfig { + /// + /// 标识 + /// + private readonly string _id; + + /// + /// 初始化警告提示共享配置 + /// + /// 标识 + public AlertShareConfig( string id ) { + _id = id.IsEmpty() ? Id.Create() : id; + } + + /// + /// 是否自动创建ng-template + /// + public bool? IsAutoCreateTemplate { get; set; } + + /// + /// 模板标识 + /// + public string TemplateId => $"t_{_id}"; +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Alerts/Helpers/AlertShareConfigService.cs b/src/Util.Ui.NgZorro/Components/Alerts/Helpers/AlertShareConfigService.cs new file mode 100644 index 000000000..100fab723 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Alerts/Helpers/AlertShareConfigService.cs @@ -0,0 +1,62 @@ +using Util.Ui.NgZorro.Components.Alerts.Configs; + +namespace Util.Ui.NgZorro.Components.Alerts.Helpers; + +/// +/// 警告提示共享服务 +/// +public class AlertShareConfigService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 警告提示共享配置 + /// + private AlertShareConfig _shareConfig; + + /// + /// 初始化输入框组合共享服务 + /// + /// 配置 + public AlertShareConfigService( Config config ) { + _config = config; + } + + /// + /// 设置自动创建ng-template + /// + /// 是否自动创建 + public void AutoCreateTemplate( bool value ) { + _shareConfig.IsAutoCreateTemplate = value; + } + + /// + /// 初始化表单共享配置 + /// + public void Init() { + InitShareConfig(); + } + + /// + /// 初始化 + /// + private void InitShareConfig() { + _shareConfig = GetShareConfig(); + _config.SetValueToItems( _shareConfig ); + } + + /// + /// 获取共享配置 + /// + private AlertShareConfig GetShareConfig() { + return _config.GetValueFromItems() ?? new AlertShareConfig( GetId() ); + } + + /// + /// 获取标识 + /// + private string GetId() { + return _config.GetValue( UiConst.Id ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Alerts/Renders/AlertRender.cs b/src/Util.Ui.NgZorro/Components/Alerts/Renders/AlertRender.cs new file mode 100644 index 000000000..c11176291 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Alerts/Renders/AlertRender.cs @@ -0,0 +1,94 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Alerts.Builders; +using Util.Ui.NgZorro.Components.Alerts.Configs; +using Util.Ui.NgZorro.Components.Templates.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Alerts.Renders; + +/// +/// 警告提示渲染器 +/// +public class AlertRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 警告提示共享配置 + /// + private readonly AlertShareConfig _shareConfig; + + /// + /// 初始化警告提示渲染器 + /// + /// 配置 + public AlertRender( Config config ) { + _config = config; + _shareConfig = GetShareConfig(); + } + + /// + /// 获取共享配置 + /// + private AlertShareConfig GetShareConfig() { + return _config.GetValueFromItems(); + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new AlertBuilder( _config ); + builder.Config(); + SetContent( builder ); + return builder; + } + + /// + /// 设置内容 + /// + private void SetContent( AlertBuilder alertBuilder ) { + if ( IsAutoCreateTemplate() == false ) { + _config.Content.AppendTo( alertBuilder ); + return; + } + alertBuilder.BindMessage( _shareConfig.TemplateId ); + var template = CreateTemplate(); + alertBuilder.SetContent( template ); + _config.Content.AppendTo( template ); + } + + /// + /// 是否自动创建ng-template + /// + private bool IsAutoCreateTemplate() { + if ( _shareConfig.IsAutoCreateTemplate == false ) + return false; + if ( _config.Content.IsEmpty() ) + return false; + if ( _config.Contains( AngularConst.BindMessage ) ) + return false; + return true; + } + + /// + /// 创建模板 + /// + private TagBuilder CreateTemplate() { + if ( IsAutoCreateTemplate() == false ) + return new EmptyContainerTagBuilder(); + var result = new TemplateBuilder( _config ); + result.Id( _shareConfig.TemplateId ); + return result; + } + + /// + public override IHtmlContent Clone() { + return new AlertRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Anchors/AnchorTagHelper.cs b/src/Util.Ui.NgZorro/Components/Anchors/AnchorTagHelper.cs new file mode 100644 index 000000000..f29e209ac --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Anchors/AnchorTagHelper.cs @@ -0,0 +1,76 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Anchors.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Anchors; + +/// +/// 锚点,生成的标签为<nz-anchor></nz-anchor> +/// +[HtmlTargetElement( "util-anchor" )] +public class AnchorTagHelper : AngularTagHelperBase { + /// + /// [nzAffix],是否固定模式,默认值: true + /// + public string Affix { get; set; } + /// + /// [nzBounds],锚点区域边界,单位:像素,默认值: 5 + /// + public string Bounds { get; set; } + /// + /// [nzOffsetTop],距离窗口顶部达到指定偏移量后触发,类型:number + /// + public string OffsetTop { get; set; } + /// + /// [nzShowInkInFixed],固定模式是否显示小圆点,默认值: false + /// + public string ShowInkInFixed { get; set; } + /// + /// nzContainer,指定滚动的容器,类型: string | HTMLElement,默认值: 'window' + /// + public string Container { get; set; } + /// + /// [nzContainer],指定滚动的容器,类型: string | HTMLElement,默认值: 'window' + /// + public string BindContainer { get; set; } + /// + /// nzCurrentAnchor,自定义高亮的锚点 + /// + public string CurrentAnchor { get; set; } + /// + /// [nzCurrentAnchor],自定义高亮的锚点 + /// + public string BindCurrentAnchor { get; set; } + /// + /// [nzTargetOffset],锚点滚动偏移量,默认与 offsetTop 相同, 类型: number + /// + public string TargetOffset { get; set; } + /// + /// nzDirection,设置导航方向, 可选值: 'vertical' | 'horizontal', 默认值: 'vertical' + /// + public AnchorDirection Direction { get; set; } + /// + /// [nzDirection],设置导航方向, 可选值: 'vertical' | 'horizontal', 默认值: 'vertical' + /// + public string BindDirection { get; set; } + /// + /// (nzClick),单击事件,类型: EventEmitter<string> + /// + public string OnClick { get; set; } + /// + /// (nzChange),锚点链接变更事件,类型: EventEmitter<string> + /// + public string OnChange { get; set; } + /// + /// (nzScroll), 滚动事件,滚动至指定锚点时触发,类型: EventEmitter<NzAnchorLinkComponent> + /// + public string OnScroll { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new AnchorRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Anchors/Builders/AnchorBuilder.cs b/src/Util.Ui.NgZorro/Components/Anchors/Builders/AnchorBuilder.cs new file mode 100644 index 000000000..3ee672b9c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Anchors/Builders/AnchorBuilder.cs @@ -0,0 +1,110 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Anchors.Builders; + +/// +/// 锚点标签生成器 +/// +public class AnchorBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化锚点标签生成器 + /// + public AnchorBuilder( Config config ) : base( config,"nz-anchor" ) { + _config = config; + } + + /// + /// 配置是否固定模式 + /// + public AnchorBuilder Affix() { + AttributeIfNotEmpty( "[nzAffix]", _config.GetValue( UiConst.Affix ) ); + return this; + } + + /// + /// 配置区域边界 + /// + public AnchorBuilder Bounds() { + AttributeIfNotEmpty( "[nzBounds]", _config.GetValue( UiConst.Bounds ) ); + return this; + } + + /// + /// 配置顶部偏移量 + /// + public AnchorBuilder OffsetTop() { + AttributeIfNotEmpty( "[nzOffsetTop]", _config.GetValue( UiConst.OffsetTop ) ); + return this; + } + + /// + /// 配置固定模式是否显示小圆点 + /// + public AnchorBuilder ShowInkInFixed() { + AttributeIfNotEmpty( "[nzShowInkInFixed]", _config.GetValue( UiConst.ShowInkInFixed ) ); + return this; + } + + /// + /// 配置容器 + /// + public AnchorBuilder Container() { + AttributeIfNotEmpty( "nzContainer", _config.GetValue( UiConst.Container ) ); + AttributeIfNotEmpty( "[nzContainer]", _config.GetValue( AngularConst.BindContainer ) ); + return this; + } + + /// + /// 配置自定义高亮的锚点 + /// + public AnchorBuilder CurrentAnchor() { + AttributeIfNotEmpty( "nzCurrentAnchor", _config.GetValue( UiConst.CurrentAnchor ) ); + AttributeIfNotEmpty( "[nzCurrentAnchor]", _config.GetValue( AngularConst.BindCurrentAnchor ) ); + return this; + } + + /// + /// 配置锚点滚动偏移量 + /// + public AnchorBuilder TargetOffset() { + AttributeIfNotEmpty( "[nzTargetOffset]", _config.GetValue( UiConst.TargetOffset ) ); + return this; + } + + /// + /// 配置方向 + /// + public AnchorBuilder Direction() { + AttributeIfNotEmpty( "nzDirection", _config.GetValue( UiConst.Direction )?.Description() ); + AttributeIfNotEmpty( "[nzDirection]", _config.GetValue( AngularConst.BindDirection ) ); + return this; + } + + /// + /// 配置事件 + /// + public AnchorBuilder Events() { + AttributeIfNotEmpty( "(nzClick)", _config.GetValue( UiConst.OnClick ) ); + AttributeIfNotEmpty( "(nzChange)", _config.GetValue( UiConst.OnChange ) ); + AttributeIfNotEmpty( "(nzScroll)", _config.GetValue( UiConst.OnScroll ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Affix().Bounds().OffsetTop().ShowInkInFixed() + .Container().CurrentAnchor().TargetOffset() + .Direction() + .Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Anchors/Builders/LinkBuilder.cs b/src/Util.Ui.NgZorro/Components/Anchors/Builders/LinkBuilder.cs new file mode 100644 index 000000000..98b247819 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Anchors/Builders/LinkBuilder.cs @@ -0,0 +1,57 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Enums; + +namespace Util.Ui.NgZorro.Components.Anchors.Builders; + +/// +/// 链接标签生成器 +/// +public class LinkBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化链接标签生成器 + /// + public LinkBuilder( Config config ) : base( config, "nz-link" ) { + _config = config; + } + + /// + /// 配置锚点链接 + /// + public LinkBuilder Href() { + AttributeIfNotEmpty( "nzHref", _config.GetValue( UiConst.Href ) ); + AttributeIfNotEmpty( "[nzHref]", _config.GetValue( AngularConst.BindHref ) ); + return this; + } + + /// + /// 配置链接打开目标 + /// + public LinkBuilder Target() { + AttributeIfNotEmpty( "nzTarget", _config.GetValue( UiConst.Target )?.Description() ); + AttributeIfNotEmpty( "[nzTarget]", _config.GetValue( AngularConst.BindTarget ) ); + return this; + } + + /// + /// 配置标题 + /// + public LinkBuilder Title() { + AttributeIfNotEmpty( "nzTitle", _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Href().Target().Title(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Anchors/LinkTagHelper.cs b/src/Util.Ui.NgZorro/Components/Anchors/LinkTagHelper.cs new file mode 100644 index 000000000..1c6cb0704 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Anchors/LinkTagHelper.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.Enums; +using Util.Ui.NgZorro.Components.Anchors.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Anchors; + +/// +/// 锚点链接,生成的标签为<nz-link></nz-link> +/// +[HtmlTargetElement( "util-link" )] +public class LinkTagHelper : AngularTagHelperBase { + /// + /// nzHref,锚点链接 + /// + public string Href { get; set; } + /// + /// [nzHref],锚点链接 + /// + public string BindHref { get; set; } + /// + /// nzTarget,链接打开目标 + /// + public ATarget Target { get; set; } + /// + /// [nzTarget],链接打开目标 + /// + public string BindTarget { get; set; } + /// + /// nzTitle,标题,类型: string | TemplateRef<void> + /// + public string Title { get; set; } + /// + /// [nzTitle],标题,类型: string | TemplateRef<void> + /// + public string BindTitle { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new LinkRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Anchors/Renders/AnchorRender.cs b/src/Util.Ui.NgZorro/Components/Anchors/Renders/AnchorRender.cs new file mode 100644 index 000000000..c3b1f961f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Anchors/Renders/AnchorRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Anchors.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Anchors.Renders; + +/// +/// 锚点渲染器 +/// +public class AnchorRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化锚点渲染器 + /// + /// 配置 + public AnchorRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new AnchorBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new AnchorRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Anchors/Renders/LinkRender.cs b/src/Util.Ui.NgZorro/Components/Anchors/Renders/LinkRender.cs new file mode 100644 index 000000000..256f65e31 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Anchors/Renders/LinkRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Anchors.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Anchors.Renders; + +/// +/// 链接渲染器 +/// +public class LinkRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化链接渲染器 + /// + /// 配置 + public LinkRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new LinkBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new LinkRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Autocompletes/AutoOptionGroupTagHelper.cs b/src/Util.Ui.NgZorro/Components/Autocompletes/AutoOptionGroupTagHelper.cs new file mode 100644 index 000000000..956df93ea --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Autocompletes/AutoOptionGroupTagHelper.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Autocompletes.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Autocompletes; + +/// +/// 自动完成选项组,生成的标签为<nz-auto-optgroup></nz-auto-optgroup>,放入自动完成组件 util-autocomplete 中使用 +/// +[HtmlTargetElement( "util-auto-option-group" )] +public class AutoOptionGroupTagHelper : AngularTagHelperBase { + /// + /// nzLabel,组名 + /// + public string Label { get; set; } + /// + /// [nzLabel],组名 + /// + public string BindLabel { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new AutoOptionGroupRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Autocompletes/AutoOptionTagHelper.cs b/src/Util.Ui.NgZorro/Components/Autocompletes/AutoOptionTagHelper.cs new file mode 100644 index 000000000..14fefb9a9 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Autocompletes/AutoOptionTagHelper.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Autocompletes.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Autocompletes; + +/// +/// 自动完成项,生成的标签为<nz-auto-option></nz-auto-option> +/// +[HtmlTargetElement( "util-auto-option" )] +public class AutoOptionTagHelper : AngularTagHelperBase { + /// + /// nzValue,绑定到触发元素 ngModel 的值 + /// + public string Value { get; set; } + /// + /// [nzValue],绑定到触发元素 ngModel 的值 + /// + public string BindValue { get; set; } + /// + /// nzLabel,填入触发元素显示的值 + /// + public string Label { get; set; } + /// + /// [nzLabel],填入触发元素显示的值 + /// + public string BindLabel { get; set; } + /// + /// [nzDisabled],禁用选项, 默认值: false + /// + public string Disabled { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new AutoOptionRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Autocompletes/AutocompleteTagHelper.cs b/src/Util.Ui.NgZorro/Components/Autocompletes/AutocompleteTagHelper.cs new file mode 100644 index 000000000..1c2c10f14 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Autocompletes/AutocompleteTagHelper.cs @@ -0,0 +1,108 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Autocompletes.Helpers; +using Util.Ui.NgZorro.Components.Autocompletes.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Autocompletes; + +/// +/// 自动完成,生成的标签为<nz-autocomplete></nz-autocomplete> +/// +[HtmlTargetElement( "util-autocomplete" )] +public class AutocompleteTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 属性表达式 + /// + public ModelExpression For { get; set; } + /// + /// 扩展属性,是否启用扩展指令,当设置 url 或 data 属性时自动启用 + /// + public bool EnableExtend { get; set; } + /// + /// 扩展属性 [autoLoad],初始化时是否自动加载数据,设置成 false 则手工加载,默认值: true + /// + public string AutoLoad { get; set; } + /// + /// 扩展属性 [(queryParam)],查询参数 + /// + public string QueryParam { get; set; } + /// + /// 扩展属性 order,排序条件,范例: creationTime desc + /// + public string Sort { get; set; } + /// + /// 扩展属性 [order],排序条件,范例: creationTime desc + /// + public string BindSort { get; set; } + /// + /// 扩展属性 url,Api地址 + /// + public string Url { get; set; } + /// + /// 扩展属性 [url],Api地址 + /// + public string BindUrl { get; set; } + /// + /// 扩展属性 [data],数据源, 类型: SelectItem[] + /// + public string Data { get; set; } + /// + /// [nzBackfill],使用键盘选择选项时,是否把当前高亮项的值即时回填到输入框中, 默认值: false + /// + public string Backfill { get; set; } + /// + /// [nzDataSource],数据源 + /// + public string Datasource { get; set; } + /// + /// [nzDefaultActiveFirstOption],是否默认高亮第一项, 默认值: true + /// + public string DefaultActiveFirstOption { get; set; } + /// + /// [nzWidth],宽度,单位:px + /// + public string Width { get; set; } + /// + /// nzOverlayClassName,下拉根元素类名 + /// + public string OverlayClassName { get; set; } + /// + /// [nzOverlayClassName],下拉根元素类名 + /// + public string BindOverlayClassName { get; set; } + /// + /// nzOverlayStyle,下拉根元素样式 + /// + public string OverlayStyle { get; set; } + /// + /// [nzOverlayStyle],下拉根元素样式 + /// + public string BindOverlayStyle { get; set; } + /// + /// [compareWith],比较算法函数,函数定义:(o1: any, o2: any) => boolean + /// + public string CompareWith { get; set; } + /// + /// 扩展事件 (onLoad),数据加载完成事件,类型: EventEmitter<any>,参数为服务端返回结果 + /// + public string OnLoad { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new AutocompleteService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new AutocompleteRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Autocompletes/Builders/AutoOptionBuilder.cs b/src/Util.Ui.NgZorro/Components/Autocompletes/Builders/AutoOptionBuilder.cs new file mode 100644 index 000000000..c756ed958 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Autocompletes/Builders/AutoOptionBuilder.cs @@ -0,0 +1,99 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Extensions; + +namespace Util.Ui.NgZorro.Components.Autocompletes.Builders; + +/// +/// 自动完成项标签生成器 +/// +public class AutoOptionBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化自动完成项标签生成器 + /// + public AutoOptionBuilder( Config config ) : base( config,"nz-auto-option" ) { + _config = config; + } + + /// + /// 配置值 + /// + public AutoOptionBuilder Value() { + AttributeIfNotEmpty( "nzValue", _config.GetValue( UiConst.Value ) ); + BindValue( _config.GetValue( AngularConst.BindValue ) ); + return this; + } + + /// + /// 配置值 + /// + public AutoOptionBuilder BindValue( string value ) { + AttributeIfNotEmpty( "[nzValue]", value ); + return this; + } + + /// + /// 配置标签 + /// + public AutoOptionBuilder Label() { + AttributeIfNotEmpty( "nzLabel", _config.GetValue( UiConst.Label ) ); + BindLabel( _config.GetValue( AngularConst.BindLabel ) ); + return this; + } + + /// + /// 配置标签 + /// + public AutoOptionBuilder BindLabel( string value ) { + AttributeIfNotEmpty( "[nzLabel]", value ); + return this; + } + + /// + /// 配置禁用 + /// + public AutoOptionBuilder Disabled() { + Disabled( _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置禁用 + /// + public AutoOptionBuilder Disabled( string value ) { + AttributeIfNotEmpty( "[nzDisabled]", value ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Value().Label().Disabled(); + } + + /// + /// 配置内容 + /// + protected override void ConfigContent(Config config) { + if ( config.Content.IsEmpty() == false ) { + config.Content.AppendTo( this ); + return; + } + var label = config.GetValue( UiConst.Label ); + if ( string.IsNullOrWhiteSpace( label ) == false ) { + AppendContent( label ); + return; + } + var bindLabel = config.GetValue( AngularConst.BindLabel ); + if ( string.IsNullOrWhiteSpace( bindLabel ) ) + return; + AppendContent( $"{{{{{bindLabel}}}}}" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Autocompletes/Builders/AutoOptionGroupBuilder.cs b/src/Util.Ui.NgZorro/Components/Autocompletes/Builders/AutoOptionGroupBuilder.cs new file mode 100644 index 000000000..591769460 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Autocompletes/Builders/AutoOptionGroupBuilder.cs @@ -0,0 +1,54 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; + +namespace Util.Ui.NgZorro.Components.Autocompletes.Builders; + +/// +/// 自动完成选项组标签生成器 +/// +public class AutoOptionGroupBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化自动完成选项组标签生成器 + /// + /// 配置 + public AutoOptionGroupBuilder( Config config ) : base( config, "nz-auto-optgroup" ) { + _config = config; + } + + /// + /// 配置组名 + /// + public AutoOptionGroupBuilder Label() { + Label( _config.GetValue( UiConst.Label ) ); + return BindLabel( _config.GetValue( AngularConst.BindLabel ) ); + } + + /// + /// 配置组名 + /// + public AutoOptionGroupBuilder Label( string value ) { + AttributeIfNotEmpty( "nzLabel", value ); + return this; + } + + /// + /// 配置组名 + /// + public AutoOptionGroupBuilder BindLabel( string value ) { + AttributeIfNotEmpty( "[nzLabel]", value ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Label(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Autocompletes/Builders/AutocompleteBuilder.cs b/src/Util.Ui.NgZorro/Components/Autocompletes/Builders/AutocompleteBuilder.cs new file mode 100644 index 000000000..485a29ed8 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Autocompletes/Builders/AutocompleteBuilder.cs @@ -0,0 +1,278 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Configs; + +namespace Util.Ui.NgZorro.Components.Autocompletes.Builders; + +/// +/// 自动完成标签生成器 +/// +public class AutocompleteBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 标识 + /// + private string _id; + + /// + /// 初始化自动完成标签生成器 + /// + public AutocompleteBuilder( Config config ) : base( config, "nz-autocomplete" ) { + _config = config; + } + + /// + /// 扩展标识 + /// + protected string ExtendId => $"x_{GetId()}"; + + /// + /// 获取标识 + /// + private string GetId() { + if ( _id.IsEmpty() == false ) + return _id; + _id = _config.GetValue( UiConst.Id ); + if ( _id.IsEmpty() ) + _id = Util.Helpers.Id.Create(); + return _id; + } + + /// + /// 配置回填选中项 + /// + public AutocompleteBuilder Backfill() { + AttributeIfNotEmpty( "[nzBackfill]", _config.GetValue( UiConst.Backfill ) ); + return this; + } + + /// + /// 配置数据源 + /// + public AutocompleteBuilder DataSource() { + AttributeIfNotEmpty( "[nzDataSource]", _config.GetValue( UiConst.Datasource ) ); + return this; + } + + /// + /// 配置高亮第一项 + /// + public AutocompleteBuilder DefaultActiveFirstOption() { + AttributeIfNotEmpty( "[nzDefaultActiveFirstOption]", _config.GetValue( UiConst.DefaultActiveFirstOption ) ); + return this; + } + + /// + /// 配置宽度 + /// + public AutocompleteBuilder Width() { + AttributeIfNotEmpty( "[nzWidth]", _config.GetValue( UiConst.Width ) ); + return this; + } + + /// + /// 配置下拉根元素类名 + /// + public AutocompleteBuilder OverlayClassName() { + AttributeIfNotEmpty( "nzOverlayClassName", _config.GetValue( UiConst.OverlayClassName ) ); + AttributeIfNotEmpty( "[nzOverlayClassName]", _config.GetValue( AngularConst.BindOverlayClassName ) ); + return this; + } + + /// + /// 配置下拉根元素样式 + /// + public AutocompleteBuilder OverlayStyle() { + AttributeIfNotEmpty( "nzOverlayStyle", _config.GetValue( UiConst.OverlayStyle ) ); + AttributeIfNotEmpty( "[nzOverlayStyle]", _config.GetValue( AngularConst.BindOverlayStyle ) ); + return this; + } + + /// + /// 配置比较算法函数 + /// + public AutocompleteBuilder CompareWith() { + AttributeIfNotEmpty( "[compareWith]", _config.GetValue( UiConst.CompareWith ) ); + return this; + } + + /// + /// 配置自动加载 + /// + private AutocompleteBuilder AutoLoad() { + AttributeIfNotEmpty( "[autoLoad]", _config.GetValue( UiConst.AutoLoad ) ); + return this; + } + + /// + /// 配置查询参数 + /// + private AutocompleteBuilder QueryParam() { + AttributeIfNotEmpty( "[(queryParam)]", _config.GetValue( UiConst.QueryParam ) ); + return this; + } + + /// + /// 配置排序条件 + /// + private AutocompleteBuilder Sort() { + AttributeIfNotEmpty( "order", _config.GetValue( UiConst.Sort ) ); + AttributeIfNotEmpty( "[order]", _config.GetValue( AngularConst.BindSort ) ); + return this; + } + + /// + /// 配置Api地址 + /// + private AutocompleteBuilder Url() { + AttributeIfNotEmpty( "url", _config.GetValue( UiConst.Url ) ); + AttributeIfNotEmpty( "[url]", _config.GetValue( AngularConst.BindUrl ) ); + return this; + } + + /// + /// 配置数据源 + /// + private AutocompleteBuilder Data() { + AttributeIfNotEmpty( "[data]", _config.GetValue( UiConst.Data ) ); + return this; + } + + /// + /// 配置事件 + /// + public AutocompleteBuilder Events() { + AttributeIfNotEmpty( "(onLoad)", _config.GetValue( UiConst.OnLoad ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Backfill().DataSource().DefaultActiveFirstOption().Width() + .OverlayClassName().OverlayStyle().CompareWith() + .AutoLoad().QueryParam().Sort().Url().Data().Events(); + EnableExtend(); + } + + /// + /// 启用扩展 + /// + public AutocompleteBuilder EnableExtend() { + if ( IsEnableExtend() == false ) + return this; + Attribute( $"#{ExtendId}", "xSelectExtend" ); + Attribute( "x-select-extend" ); + ConfigOptions(); + return this; + } + + /// + /// 是否启用基础扩展 + /// + public bool IsEnableExtend() { + if ( GetEnableExtend() == false ) { + return false; + } + if ( GetEnableExtend() == true || + GetUrl().IsEmpty() == false || + GetBindUrl().IsEmpty() == false || + GetData().IsEmpty() == false ) { + return true; + } + return false; + } + + /// + /// 获取启用扩展属性 + /// + private bool? GetEnableExtend() { + return _config.GetValue( UiConst.EnableExtend ); + } + + /// + /// 获取地址 + /// + private string GetUrl() { + return _config.GetValue( UiConst.Url ); + } + + /// + /// 获取地址 + /// + private string GetBindUrl() { + return _config.GetValue( AngularConst.BindUrl ); + } + + /// + /// 获取数据源 + /// + private string GetData() { + return _config.GetValue( UiConst.Data ); + } + + /// + /// 配置数据项 + /// + private void ConfigOptions() { + AppendContent( $"@if({ExtendId}.isGroup)" ); + AppendContent( "{" ); + ConfigOptionGroup(); + AppendContent( "}} @else {" ); + ConfigOption(); + AppendContent( "}" ); + } + + /// + /// 配置选项组 + /// + private void ConfigOptionGroup() { + AppendContent( $"@for(group of {ExtendId}.optionGroups;track group.text )" ); + AppendContent( "{" ); + var options = NgZorroOptionsService.GetOptions(); + var groupBuilder = new AutoOptionGroupBuilder( _config ); + AppendContent( groupBuilder ); + groupBuilder.BindLabel( options.EnableI18n ? "group.text|i18n" : "group.text" ); + groupBuilder.AppendContent( "@for(item of group.value;track item.value)" ); + groupBuilder.AppendContent( "{" ); + var optionBuilder = new AutoOptionBuilder( _config ); + groupBuilder.AppendContent( optionBuilder ); + ConfigOptionLabel( optionBuilder ); + optionBuilder.BindValue( "item.value" ); + optionBuilder.Disabled( "item.disabled" ); + groupBuilder.AppendContent( "}" ); + } + + /// + /// 配置选项 + /// + private void ConfigOption() { + AppendContent( $"@for(item of {ExtendId}.options;track item.value)" ); + AppendContent( "{" ); + var optionBuilder = new AutoOptionBuilder( _config ); + AppendContent( optionBuilder ); + ConfigOptionLabel( optionBuilder ); + optionBuilder.BindValue( "item.value" ); + optionBuilder.Disabled( "item.disabled" ); + AppendContent( "}" ); + } + + /// + /// 配置标签文本 + /// + private void ConfigOptionLabel( AutoOptionBuilder optionBuilder ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + optionBuilder.BindLabel( "item.text|i18n" ); + optionBuilder.SetContent( "{{" + "item.text|i18n" + "}}" ); + return; + } + optionBuilder.BindLabel( "item.text" ); + optionBuilder.SetContent( "{{item.text}}" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Autocompletes/Helpers/AutocompleteExpressionLoader.cs b/src/Util.Ui.NgZorro/Components/Autocompletes/Helpers/AutocompleteExpressionLoader.cs new file mode 100644 index 000000000..2d69eb422 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Autocompletes/Helpers/AutocompleteExpressionLoader.cs @@ -0,0 +1,89 @@ +using Util.Ui.Expressions; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Expressions; + +namespace Util.Ui.NgZorro.Components.Autocompletes.Helpers; + +/// +/// 自动完成表达式加载器 +/// +public class AutocompleteExpressionLoader : NgZorroExpressionLoaderBase { + /// + /// 加载模型信息 + /// + /// 配置 + /// 模型表达式信息 + protected override void Load( Config config, ModelExpressionInfo info ) { + LoadId( config, info ); + LoadData( config, info ); + } + + /// + /// 加载标识 + /// + protected virtual void LoadId( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.Id, $"auto_{GeKebaberizePropertyName( config, info )}", false ); + } + + /// + /// 加载数据源 + /// + protected virtual void LoadData( Config config, ModelExpressionInfo info ) { + LoadBool( config, info ); + LoadEnum( config, info ); + } + + /// + /// 加载布尔值 + /// + protected virtual void LoadBool( Config config, ModelExpressionInfo info ) { + if ( info.IsBool == false ) + return; + config.SetAttribute( UiConst.Data, GetBoolData(), false ); + } + + /// + /// 获取布尔类型数据 + /// + private string GetBoolData() { + var result = new StringBuilder(); + result.Append( "[{" ); + result.Append( $"'text':'{GetYes()}','value':true,'sortId':1" ); + result.Append( "}," ); + result.Append( "{" ); + result.Append( $"'text':'{GetNo()}','value':false,'sortId':2" ); + result.Append( "}]" ); + return result.ToString(); + } + + /// + /// 获取yes文本 + /// + private string GetYes() { + var options = NgZorroOptionsService.GetOptions(); + return options.EnableI18n ? I18nKeys.Yes : "是"; + } + + /// + /// 获取no文本 + /// + private string GetNo() { + var options = NgZorroOptionsService.GetOptions(); + return options.EnableI18n ? I18nKeys.No : "否"; + } + + /// + /// 加载枚举值 + /// + protected virtual void LoadEnum( Config config, ModelExpressionInfo info ) { + if ( info.IsEnum == false ) + return; + var items = Util.Helpers.Enum.GetItems( info.ModelType ); + var options = new JsonSerializerOptions { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ) + }; + var result = Util.Helpers.Json.ToJson( items, options, toSingleQuotes: true ); + config.SetAttribute( UiConst.Data, result, false ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Autocompletes/Helpers/AutocompleteService.cs b/src/Util.Ui.NgZorro/Components/Autocompletes/Helpers/AutocompleteService.cs new file mode 100644 index 000000000..1cc94f2af --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Autocompletes/Helpers/AutocompleteService.cs @@ -0,0 +1,34 @@ +namespace Util.Ui.NgZorro.Components.Autocompletes.Helpers; + +/// +/// 自动完成服务 +/// +public class AutocompleteService { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化自动完成服务 + /// + /// 配置 + public AutocompleteService( Config config ) { + _config = config; + } + + /// + /// 初始化 + /// + public void Init() { + LoadExpression(); + } + + /// + /// 加载表达式 + /// + private void LoadExpression() { + var loader = new AutocompleteExpressionLoader(); + loader.Load( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Autocompletes/Renders/AutoOptionGroupRender.cs b/src/Util.Ui.NgZorro/Components/Autocompletes/Renders/AutoOptionGroupRender.cs new file mode 100644 index 000000000..23649ab06 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Autocompletes/Renders/AutoOptionGroupRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Autocompletes.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Autocompletes.Renders; + +/// +/// 自动完成选项组渲染器 +/// +public class AutoOptionGroupRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化自动完成选项组渲染器 + /// + /// 配置 + public AutoOptionGroupRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new AutoOptionGroupBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new AutoOptionGroupRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Autocompletes/Renders/AutoOptionRender.cs b/src/Util.Ui.NgZorro/Components/Autocompletes/Renders/AutoOptionRender.cs new file mode 100644 index 000000000..e0aa37c92 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Autocompletes/Renders/AutoOptionRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Autocompletes.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Autocompletes.Renders; + +/// +/// 自动完成项渲染器 +/// +public class AutoOptionRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化自动完成项渲染器 + /// + /// 配置 + public AutoOptionRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new AutoOptionBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new AutoOptionRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Autocompletes/Renders/AutocompleteRender.cs b/src/Util.Ui.NgZorro/Components/Autocompletes/Renders/AutocompleteRender.cs new file mode 100644 index 000000000..55821ec9f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Autocompletes/Renders/AutocompleteRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Autocompletes.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Autocompletes.Renders; + +/// +/// 自动完成渲染器 +/// +public class AutocompleteRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化自动完成渲染器 + /// + /// 配置 + public AutocompleteRender( Config config ){ + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new AutocompleteBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new AutocompleteRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Avatars/AvatarGroupTagHelper.cs b/src/Util.Ui.NgZorro/Components/Avatars/AvatarGroupTagHelper.cs new file mode 100644 index 000000000..d73461192 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Avatars/AvatarGroupTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Avatars.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Avatars; + +/// +/// 头像组,生成的标签为<nz-avatar-group></nz-avatar-group> +/// +[HtmlTargetElement( "util-avatar-group" )] +public class AvatarGroupTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new AvatarGroupRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Avatars/AvatarTagHelper.cs b/src/Util.Ui.NgZorro/Components/Avatars/AvatarTagHelper.cs new file mode 100644 index 000000000..c2896adad --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Avatars/AvatarTagHelper.cs @@ -0,0 +1,84 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Avatars.Renders; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Avatars; + +/// +/// 头像,生成的标签为<nz-avatar></nz-avatar> +/// +[HtmlTargetElement( "util-avatar" )] +public class AvatarTagHelper : TooltipTagHelperBase { + /// + /// nzIcon,头像图标 + /// + public AntDesignIcon Icon { get; set; } + /// + /// [nzIcon],头像图标 + /// + public string BindIcon { get; set; } + /// + /// nzShape,头像形状,可选值: 'circle' | 'square',默认值: 'circle' + /// + public AvatarShape Shape { get; set; } + /// + /// [nzShape],头像形状,可选值: 'circle' | 'square',默认值: 'circle' + /// + public string BindShape { get; set; } + /// + /// nzSize,头像尺寸,可选值: 'large' | 'small' | 'default' | number, 默认值: 'default' + /// + public AvatarSize Size { get; set; } + /// + /// [nzSize],头像尺寸,可选值: 'large' | 'small' | 'default' | number, 默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// [nzGap],文本距离左右两侧边界,单位:像素, 类型: number, 默认值: 4 + /// + public string Gap { get; set; } + /// + /// nzSrc,图片头像地址 + /// + public string Src { get; set; } + /// + /// [nzSrc],图片头像地址 + /// + public string BindSrc { get; set; } + /// + /// nzSrcSet,图片头像响应式资源地址 + /// + public string SrcSet { get; set; } + /// + /// [nzSrcSet],图片头像响应式资源地址 + /// + public string BindSrcSet { get; set; } + /// + /// nzAlt,图片无法显示时的替代文本 + /// + public string Alt { get; set; } + /// + /// [nzAlt],图片无法显示时的替代文本 + /// + public string BindAlt { get; set; } + /// + /// nzText,文本 + /// + public string Text { get; set; } + /// + /// [nzText],文本 + /// + public string BindText { get; set; } + /// + /// (nzError),图片加载失败事件,调用 preventDefault 方法会阻止组件默认的 fallback 行为,类型: EventEmitter<Event> + /// + public string OnError { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new AvatarRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Avatars/Builders/AvatarBuilder.cs b/src/Util.Ui.NgZorro/Components/Avatars/Builders/AvatarBuilder.cs new file mode 100644 index 000000000..eeb29ba08 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Avatars/Builders/AvatarBuilder.cs @@ -0,0 +1,113 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Directives.Popconfirms; +using Util.Ui.NgZorro.Directives.Popover; +using Util.Ui.NgZorro.Directives.Tooltips; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Avatars.Builders; + +/// +/// 头像标签生成器 +/// +public class AvatarBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化头像标签生成器 + /// + public AvatarBuilder( Config config ) : base( config,"nz-avatar" ) { + _config = config; + } + + /// + /// 配置图标 + /// + public AvatarBuilder Icon() { + AttributeIfNotEmpty( "nzIcon", _config.GetValue( UiConst.Icon )?.Description() ); + AttributeIfNotEmpty( "[nzIcon]", _config.GetValue( AngularConst.BindIcon ) ); + return this; + } + + /// + /// 配置形状 + /// + public AvatarBuilder Shape() { + AttributeIfNotEmpty( "nzShape", _config.GetValue( UiConst.Shape )?.Description() ); + AttributeIfNotEmpty( "[nzShape]", _config.GetValue( AngularConst.BindShape ) ); + return this; + } + + /// + /// 配置大小 + /// + public AvatarBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置文本距离两侧间距 + /// + public AvatarBuilder Gap() { + AttributeIfNotEmpty( "[nzGap]", _config.GetValue( UiConst.Gap ) ); + return this; + } + + /// + /// 配置图片头像地址 + /// + public AvatarBuilder Src() { + AttributeIfNotEmpty( "nzSrc", _config.GetValue( UiConst.Src ) ); + AttributeIfNotEmpty( "[nzSrc]", _config.GetValue( AngularConst.BindSrc ) ); + return this; + } + + /// + /// 配置图片头像响应式资源地址 + /// + public AvatarBuilder SrcSet() { + AttributeIfNotEmpty( "nzSrcSet", _config.GetValue( UiConst.SrcSet ) ); + AttributeIfNotEmpty( "[nzSrcSet]", _config.GetValue( AngularConst.BindSrcSet ) ); + return this; + } + + /// + /// 配置图片无法显示时的替代文本 + /// + public AvatarBuilder Alt() { + AttributeIfNotEmpty( "nzAlt", _config.GetValue( UiConst.Alt ) ); + AttributeIfNotEmpty( "[nzAlt]", _config.GetValue( AngularConst.BindAlt ) ); + return this; + } + + /// + /// 配置文本 + /// + public AvatarBuilder Text() { + AttributeIfNotEmpty( "nzText", _config.GetValue( UiConst.Text ) ); + AttributeIfNotEmpty( "[nzText]", _config.GetValue( AngularConst.BindText ) ); + return this; + } + + /// + /// 配置事件 + /// + public AvatarBuilder Events() { + AttributeIfNotEmpty( "(nzError)", _config.GetValue( UiConst.OnError ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + this.Tooltip( _config ).Popover( _config ).Popconfirm( _config ); + Icon().Shape().Size().Gap().Src().SrcSet().Alt().Text().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Avatars/Builders/AvatarGroupBuilder.cs b/src/Util.Ui.NgZorro/Components/Avatars/Builders/AvatarGroupBuilder.cs new file mode 100644 index 000000000..d8214d100 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Avatars/Builders/AvatarGroupBuilder.cs @@ -0,0 +1,14 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Avatars.Builders; + +/// +/// 头像组标签生成器 +/// +public class AvatarGroupBuilder : AngularTagBuilder { + /// + /// 初始化头像组标签生成器 + /// + public AvatarGroupBuilder( Config config ) : base( config,"nz-avatar-group" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Avatars/Renders/AvatarGroupRender.cs b/src/Util.Ui.NgZorro/Components/Avatars/Renders/AvatarGroupRender.cs new file mode 100644 index 000000000..7c9fc874e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Avatars/Renders/AvatarGroupRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Avatars.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Avatars.Renders; + +/// +/// 头像组渲染器 +/// +public class AvatarGroupRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化头像组渲染器 + /// + /// 配置 + public AvatarGroupRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new AvatarGroupBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new AvatarGroupRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Avatars/Renders/AvatarRender.cs b/src/Util.Ui.NgZorro/Components/Avatars/Renders/AvatarRender.cs new file mode 100644 index 000000000..57fd67581 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Avatars/Renders/AvatarRender.cs @@ -0,0 +1,49 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Avatars.Builders; +using Util.Ui.NgZorro.Components.Comments.Configs; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Avatars.Renders; + +/// +/// 头像渲染器 +/// +public class AvatarRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化头像渲染器 + /// + /// 配置 + public AvatarRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new AvatarBuilder( _config ); + builder.Config(); + ConfigCommentAvatar( builder ); + return builder; + } + + /// + /// 配置评论头像 + /// + private void ConfigCommentAvatar( TagBuilder builder ) { + var shareConfig = _config.GetValueFromItems(); + if( shareConfig == null ) + return; + builder.Attribute( "nz-comment-avatar" ); + } + + /// + public override IHtmlContent Clone() { + return new AvatarRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/BackTops/BackTopTagHelper.cs b/src/Util.Ui.NgZorro/Components/BackTops/BackTopTagHelper.cs new file mode 100644 index 000000000..0497bf73d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/BackTops/BackTopTagHelper.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.BackTops.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.BackTops; + +/// +/// 回到顶部,生成的标签为<nz-back-top></nz-back-top> +/// +[HtmlTargetElement( "util-back-top" )] +public class BackTopTagHelper : AngularTagHelperBase { + /// + /// [nzTemplate],自定义内容, 类型: TemplateRef<void> + /// + public string Template { get; set; } + /// + /// [nzVisibilityHeight],滚动高度达到该值才显示回到顶部按钮, 默认值: 400 + /// + public string VisibilityHeight { get; set; } + /// + /// [nzTarget],设置监听目标元素, 类型: string | Element, 默认值: window + /// + public string Target { get; set; } + /// + /// [nzDuration],回到顶部所需时间,单位: 毫秒, 默认值: 450 + /// + public string Duration { get; set; } + /// + /// (nzClick), 单击回到顶部按钮事件,类型: EventEmitter<boolean> + /// + public string OnClick { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new BackTopRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/BackTops/Builders/BackTopBuilder.cs b/src/Util.Ui.NgZorro/Components/BackTops/Builders/BackTopBuilder.cs new file mode 100644 index 000000000..ec90907b6 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/BackTops/Builders/BackTopBuilder.cs @@ -0,0 +1,116 @@ +using Util.Applications; +using Util.Helpers; +using Util.Ui.Angular.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Templates.Builders; +using Config = Util.Ui.Configs.Config; + +namespace Util.Ui.NgZorro.Components.BackTops.Builders; + +/// +/// 回到顶部标签生成器 +/// +public class BackTopBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 模板标识 + /// + private string _templateId; + + /// + /// 初始化回到顶部标签生成器 + /// + /// 配置 + public BackTopBuilder( Config config ) : base( config, "nz-back-top" ) { + _config = config; + } + + /// + /// 配置自定义内容 + /// + public BackTopBuilder Template() { + Template( _config.GetValue( UiConst.Template ) ); + return this; + } + + /// + /// 配置模板标识 + /// + /// 模板标识 + public BackTopBuilder Template( string templateId ) { + AttributeIfNotEmpty( "[nzTemplate]", templateId ); + return this; + } + + /// + /// 配置可见高度 + /// + public BackTopBuilder VisibilityHeight() { + AttributeIfNotEmpty( "[nzVisibilityHeight]", _config.GetValue( UiConst.VisibilityHeight ) ); + return this; + } + + /// + /// 配置监听目标 + /// + public BackTopBuilder Target() { + AttributeIfNotEmpty( "[nzTarget]", _config.GetValue( UiConst.Target ) ); + return this; + } + + /// + /// 配置持续时间 + /// + public BackTopBuilder Duration() { + AttributeIfNotEmpty( "[nzDuration]", _config.GetValue( UiConst.Duration ) ); + return this; + } + + /// + /// 配置事件 + /// + public BackTopBuilder Events() { + AttributeIfNotEmpty( "(nzClick)", _config.GetValue( UiConst.OnClick ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Template().VisibilityHeight().Target().Duration().Events(); + } + + /// + /// 配置内容 + /// + protected override void ConfigContent( Config config ) { + if ( config.Content.IsEmpty() ) + return; + Template( GetTemplateId() ); + var templateBuilder = new TemplateBuilder( _config ); + templateBuilder.Attribute( $"#{GetTemplateId()}" ); + templateBuilder.AppendContent( config.Content ); + AppendContent( templateBuilder ); + } + + /// + /// 获取模板标识 + /// + private string GetTemplateId() { + if ( _templateId.IsEmpty() == false ) + return _templateId; + _templateId = _config.GetValue( UiConst.Template ); + if ( _templateId.IsEmpty() == false ) + return _templateId; + var id = _config.GetValue( UiConst.Id ); + if ( id.IsEmpty() ) + id = Id.Create(); + _templateId = $"tpl_{id}"; + return _templateId; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/BackTops/Renders/BackTopRender.cs b/src/Util.Ui.NgZorro/Components/BackTops/Renders/BackTopRender.cs new file mode 100644 index 000000000..e468e80f0 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/BackTops/Renders/BackTopRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.BackTops.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.BackTops.Renders; + +/// +/// 回到顶部渲染器 +/// +public class BackTopRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化回到顶部渲染器 + /// + /// 配置 + public BackTopRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new BackTopBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new BackTopRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Badges/BadgeTagHelper.cs b/src/Util.Ui.NgZorro/Components/Badges/BadgeTagHelper.cs new file mode 100644 index 000000000..593dde5d7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Badges/BadgeTagHelper.cs @@ -0,0 +1,92 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Badges.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Badges; + +/// +/// 徽标,生成的标签为<nz-badge></nz-badge> +/// +[HtmlTargetElement( "util-badge" )] +public class BadgeTagHelper : AngularTagHelperBase { + /// + /// [nzStandalone],是否独立使用,类型: boolean + /// + public string Standalone { get; set; } + /// + /// nzColor,小圆点的颜色 + /// + public AntDesignColor ColorType { get; set; } + /// + /// nzColor,小圆点的颜色 + /// + public string Color { get; set; } + /// + /// [nzColor],小圆点的颜色 + /// + public string BindColor { get; set; } + /// + /// [nzCount],显示数字,大于 `overflow-count` 时显示为 `99+`,为 0 时隐藏,类型: number | TemplateRef<void> + /// + public string Count { get; set; } + /// + /// [nzDot],是否不显示数字,只显示一个小红点,类型: boolean, 默认值: false + /// + public string Dot { get; set; } + /// + /// [nzShowDot],是否显示小红点,类型: boolean, 默认值: true + /// + public string ShowDot { get; set; } + /// + /// [nzOverflowCount],封顶数字,类型: number,默认值: 99 + /// + public string OverflowCount { get; set; } + /// + /// [nzShowZero],当数值为 0 时,是否显示徽标, 类型: boolean, 默认值: false + /// + public string ShowZero { get; set; } + /// + /// nzStatus,设置徽标为状态点,可选值: 'success' | 'processing' | 'default' | 'error' | 'warning' + /// + public BadgeStatus Status { get; set; } + /// + /// [nzStatus],设置徽标为状态点,可选值: 'success' | 'processing' | 'default' | 'error' | 'warning' + /// + public string BindStatus { get; set; } + /// + /// nzText,设置状态点的文本,支持多语言,仅在设置了 status 时有效 + /// + public string Text { get; set; } + /// + /// [nzText],设置状态点的文本,仅在设置了 status 时有效,类型: string | TemplateRef<void> + /// + public string BindText { get; set; } + /// + /// nzTitle,设置鼠标放在状态点上时显示的文字,为 null 时隐藏,类型: string | null,默认值: nzCount + /// + public string Title { get; set; } + /// + /// [nzTitle],设置鼠标放在状态点上时显示的文字,为 null 时隐藏,类型: string | null,默认值: nzCount + /// + public string BindTitle { get; set; } + /// + /// [nzOffset],设置状态点的位置偏移,格式为 [left, top],表示状态点距默认位置左侧、上方的偏移量,类型: [number, number] + /// + public string Offset { get; set; } + /// + /// nzSize,设置徽标尺寸,仅在设置了 `count` 时有效, 可选值: default | small , 默认值: default + /// + public BadgeSize Size { get; set; } + /// + /// [nzSize],设置徽标尺寸,仅在设置了 `count` 时有效, 可选值: default | small , 默认值: default + /// + public string BindSize { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new BadgeRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Badges/Builders/BadgeBuilder.cs b/src/Util.Ui.NgZorro/Components/Badges/Builders/BadgeBuilder.cs new file mode 100644 index 000000000..f7e7ef849 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Badges/Builders/BadgeBuilder.cs @@ -0,0 +1,147 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Badges.Builders; + +/// +/// 徽标标签生成器 +/// +public class BadgeBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化徽标标签生成器 + /// + public BadgeBuilder( Config config ) : base( config,"nz-badge" ) { + _config = config; + } + + /// + /// 配置是否独立使用 + /// + public BadgeBuilder Standalone() { + AttributeIfNotEmpty( "[nzStandalone]", _config.GetValue( UiConst.Standalone ) ); + return this; + } + + /// + /// 配置颜色 + /// + public BadgeBuilder Color() { + AttributeIfNotEmpty( "nzColor", _config.GetValue( UiConst.ColorType )?.Description() ); + AttributeIfNotEmpty( "nzColor", _config.GetValue( UiConst.Color ) ); + AttributeIfNotEmpty( "[nzColor]", _config.GetValue( AngularConst.BindColor ) ); + return this; + } + + /// + /// 配置显示的数字 + /// + public BadgeBuilder Count() { + AttributeIfNotEmpty( "[nzCount]", _config.GetValue( UiConst.Count ) ); + return this; + } + + /// + /// 配置是否不显示数字,仅显示小红点 + /// + public BadgeBuilder Dot() { + AttributeIfNotEmpty( "[nzDot]", _config.GetValue( UiConst.Dot ) ); + return this; + } + + /// + /// 配置是否显示小红点 + /// + public BadgeBuilder ShowDot() { + AttributeIfNotEmpty( "[nzShowDot]", _config.GetValue( UiConst.ShowDot ) ); + return this; + } + + /// + /// 配置封顶数字 + /// + public BadgeBuilder OverflowCount() { + AttributeIfNotEmpty( "[nzOverflowCount]", _config.GetValue( UiConst.OverflowCount ) ); + return this; + } + + /// + /// 配置是否显示0值 + /// + public BadgeBuilder ShowZero() { + AttributeIfNotEmpty( "[nzShowZero]", _config.GetValue( UiConst.ShowZero ) ); + return this; + } + + /// + /// 配置状态 + /// + public BadgeBuilder Status() { + AttributeIfNotEmpty( "nzStatus", _config.GetValue( UiConst.Status )?.Description() ); + AttributeIfNotEmpty( "[nzStatus]", _config.GetValue( AngularConst.BindStatus ) ); + return this; + } + + /// + /// 配置状态点文本 + /// + public BadgeBuilder Text() { + SetText( _config.GetValue( UiConst.Text ) ); + AttributeIfNotEmpty( "[nzText]", _config.GetValue( AngularConst.BindText ) ); + return this; + } + + /// + /// 设置状态点文本 + /// + private void SetText( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzText]", value ); + return; + } + AttributeIfNotEmpty( "nzText", value ); + } + + /// + /// 配置状态点提示 + /// + public BadgeBuilder Title() { + AttributeIfNotEmpty( "nzTitle", _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 配置状态点位置偏移 + /// + public BadgeBuilder Offset() { + AttributeIfNotEmpty( "[nzOffset]", _config.GetValue( UiConst.Offset ) ); + return this; + } + + /// + /// 配置尺寸 + /// + public BadgeBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Standalone().Color().Count().Dot().ShowDot().OverflowCount() + .ShowZero().Status().Text().Title().Offset().Size(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Badges/Builders/RibbonBuilder.cs b/src/Util.Ui.NgZorro/Components/Badges/Builders/RibbonBuilder.cs new file mode 100644 index 000000000..99ad20734 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Badges/Builders/RibbonBuilder.cs @@ -0,0 +1,72 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Badges.Builders; + +/// +/// 缎带徽标标签生成器 +/// +public class RibbonBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化缎带徽标标签生成器 + /// + public RibbonBuilder( Config config ) : base( config, "nz-ribbon" ) { + _config = config; + } + + /// + /// 配置颜色 + /// + public RibbonBuilder Color() { + AttributeIfNotEmpty( "nzColor", _config.GetValue( UiConst.ColorType )?.Description() ); + AttributeIfNotEmpty( "nzColor", _config.GetValue( UiConst.Color ) ); + AttributeIfNotEmpty( "[nzColor]", _config.GetValue( AngularConst.BindColor ) ); + return this; + } + + /// + /// 配置位置 + /// + public RibbonBuilder Placement() { + AttributeIfNotEmpty( "nzPlacement", _config.GetValue( UiConst.Placement )?.Description() ); + AttributeIfNotEmpty( "[nzPlacement]", _config.GetValue( AngularConst.BindPlacement ) ); + return this; + } + + /// + /// 配置文本内容 + /// + public RibbonBuilder Text() { + SetText( _config.GetValue( UiConst.Text ) ); + AttributeIfNotEmpty( "[nzText]", _config.GetValue( AngularConst.BindText ) ); + return this; + } + + /// + /// 设置文本内容 + /// + private void SetText( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzText]", value ); + return; + } + AttributeIfNotEmpty( "nzText", value ); + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Color().Placement().Text(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Badges/Renders/BadgeRender.cs b/src/Util.Ui.NgZorro/Components/Badges/Renders/BadgeRender.cs new file mode 100644 index 000000000..42cdd596b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Badges/Renders/BadgeRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Badges.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Badges.Renders; + +/// +/// 徽标渲染器 +/// +public class BadgeRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化徽标渲染器 + /// + /// 配置 + public BadgeRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new BadgeBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new BadgeRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Badges/Renders/RibbonRender.cs b/src/Util.Ui.NgZorro/Components/Badges/Renders/RibbonRender.cs new file mode 100644 index 000000000..93a3db440 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Badges/Renders/RibbonRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Badges.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Badges.Renders; + +/// +/// 缎带徽标渲染器 +/// +public class RibbonRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化缎带徽标渲染器 + /// + /// 配置 + public RibbonRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new RibbonBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new RibbonRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Badges/RibbonTagHelper.cs b/src/Util.Ui.NgZorro/Components/Badges/RibbonTagHelper.cs new file mode 100644 index 000000000..dfc214fc7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Badges/RibbonTagHelper.cs @@ -0,0 +1,48 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Badges.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Badges; + +/// +/// 缎带徽标,生成的标签为<nz-ribbon></nz-ribbon> +/// +[HtmlTargetElement( "util-ribbon" )] +public class RibbonTagHelper : AngularTagHelperBase { + /// + /// nzColor,颜色 + /// + public AntDesignColor ColorType { get; set; } + /// + /// nzColor,颜色 + /// + public string Color { get; set; } + /// + /// [nzColor],颜色 + /// + public string BindColor { get; set; } + /// + /// nzPlacement,位置,可选值: 'start' | 'end',默认值: 'end' + /// + public RibbonPlacement Placement { get; set; } + /// + /// [nzPlacement],位置,可选值: 'start' | 'end',默认值: 'end' + /// + public string BindPlacement { get; set; } + /// + /// nzText,文本内容,支持多语言 + /// + public string Text { get; set; } + /// + /// [nzText],文本内容 + /// + public string BindText { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new RibbonRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Base/ButtonBuilderBase.cs b/src/Util.Ui.NgZorro/Components/Base/ButtonBuilderBase.cs new file mode 100644 index 000000000..7e2cd1688 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Base/ButtonBuilderBase.cs @@ -0,0 +1,814 @@ +using Microsoft.AspNetCore.Mvc.Rendering; +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Components.Icons.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Directives.Popconfirms; +using Util.Ui.NgZorro.Directives.Popover; +using Util.Ui.NgZorro.Directives.Tooltips; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Base; + +/// +/// 按钮标签生成器基类 +/// +public abstract class ButtonBuilderBase : AngularTagBuilder where TBuilder : ButtonBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 标识 + /// + private readonly string _id; + + /// + /// 初始化按钮标签生成器基类 + /// + /// 配置 + /// 标签名称,范例:div + /// 渲染模式 + protected ButtonBuilderBase( Config config, string tagName, TagRenderMode renderMode = TagRenderMode.Normal ) : base( config, tagName, renderMode ) { + _config = config; + _id = Util.Helpers.Id.Create(); + } + + /// + /// 配置按钮类型 + /// + public TBuilder Type() { + AttributeIfNotEmpty( "nzType", _config.GetValue( UiConst.Type )?.Description() ); + AttributeIfNotEmpty( "[nzType]", _config.GetValue( AngularConst.BindType ) ); + return (TBuilder)this; + } + + /// + /// 配置按钮尺寸 + /// + public TBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return (TBuilder)this; + } + + /// + /// 配置按钮形状 + /// + public TBuilder Shape() { + AttributeIfNotEmpty( "nzShape", _config.GetValue( UiConst.Shape )?.Description() ); + AttributeIfNotEmpty( "[nzShape]", _config.GetValue( AngularConst.BindShape ) ); + return (TBuilder)this; + } + + /// + /// 配置禁用 + /// + public TBuilder Disabled() { + AttributeIfNotEmpty( "[disabled]", _config.GetValue( UiConst.Disabled ) ); + return (TBuilder)this; + } + + /// + /// 配置危险按钮 + /// + public virtual TBuilder Danger() { + AttributeIfNotEmpty( "[nzDanger]", _config.GetValue( UiConst.Danger ) ); + return (TBuilder)this; + } + + /// + /// 配置加载状态 + /// + public TBuilder Loading() { + AttributeIfNotEmpty( "[nzLoading]", _config.GetValue( UiConst.Loading ) ); + return (TBuilder)this; + } + + /// + /// 配置幽灵按钮 + /// + public TBuilder Ghost() { + AttributeIfNotEmpty( "[nzGhost]", _config.GetValue( UiConst.Ghost ) ); + return (TBuilder)this; + } + + /// + /// 配置Block按钮 + /// + public TBuilder Block() { + AttributeIfNotEmpty( "[nzBlock]", _config.GetValue( UiConst.Block ) ); + return (TBuilder)this; + } + + /// + /// 配置图标 + /// + public TBuilder Icon() { + if ( _config.Contains( UiConst.Icon ) == false ) + return (TBuilder)this; + var iconBuilder = new IconBuilder( _config ); + iconBuilder.Icon(); + AppendContent( iconBuilder ); + return (TBuilder)this; + } + + /// + /// 配置图标 + /// + public TBuilder Icon( AntDesignIcon icon ) { + var iconBuilder = new IconBuilder( _config ); + iconBuilder.Type( icon ); + AppendContent( iconBuilder ); + return (TBuilder)this; + } + + /// + /// 配置下拉菜单 + /// + public TBuilder DropdownMenu() { + var menu = _config.GetValue( UiConst.DropdownMenu ); + if ( string.IsNullOrWhiteSpace( menu ) ) + return (TBuilder)this; + Attribute( "nz-dropdown" ).Attribute( "[nzDropdownMenu]", menu ); + return (TBuilder)this; + } + + /// + /// 配置下拉菜单弹出位置 + /// + public TBuilder DropdownMenuPlacement() { + AttributeIfNotEmpty( "nzPlacement", _config.GetValue( UiConst.DropdownMenuPlacement )?.Description() ); + AttributeIfNotEmpty( "[nzPlacement]", _config.GetValue( AngularConst.BindDropdownMenuPlacement ) ); + return (TBuilder)this; + } + + /// + /// 配置下拉菜单触发方式 + /// + public TBuilder DropdownMenuTrigger() { + AttributeIfNotEmpty( "nzTrigger", _config.GetValue( UiConst.DropdownMenuTrigger )?.Description() ); + AttributeIfNotEmpty( "[nzTrigger]", _config.GetValue( AngularConst.BindDropdownMenuTrigger ) ); + return (TBuilder)this; + } + + /// + /// 配置点击隐藏下拉菜单 + /// + public TBuilder DropdownMenuClickHide() { + AttributeIfNotEmpty( "[nzClickHide]", _config.GetValue( UiConst.DropdownMenuClickHide ) ); + return (TBuilder)this; + } + + /// + /// 配置下拉菜单可见性 + /// + public TBuilder DropdownMenuVisible() { + AttributeIfNotEmpty( "[nzVisible]", _config.GetValue( UiConst.DropdownMenuVisible ) ); + AttributeIfNotEmpty( "[(nzVisible)]", _config.GetValue( AngularConst.BindonDropdownMenuVisible ) ); + return (TBuilder)this; + } + + /// + /// 配置下拉菜单根元素类名 + /// + public TBuilder DropdownMenuOverlayClassName() { + AttributeIfNotEmpty( "nzOverlayClassName", _config.GetValue( UiConst.DropdownMenuOverlayClassName ) ); + AttributeIfNotEmpty( "[nzOverlayClassName]", _config.GetValue( AngularConst.BindDropdownMenuOverlayClassName ) ); + return (TBuilder)this; + } + + /// + /// 配置下拉菜单根元素样式 + /// + public TBuilder DropdownMenuOverlayStyle() { + AttributeIfNotEmpty( "[nzOverlayStyle]", _config.GetValue( UiConst.DropdownMenuOverlayStyle ) ); + return (TBuilder)this; + } + + /// + /// 配置间距项 + /// + public TBuilder SpaceItem() { + this.SpaceItem( _config ); + return (TBuilder)this; + } + + /// + /// 配置Ok文本 + /// + public TBuilder TextOk() { + var value = _config.GetValue( UiConst.TextOk ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Ok ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Ok" ); + return (TBuilder)this; + } + + /// + /// 配置Cancel文本 + /// + public TBuilder TextCancel() { + var value = _config.GetValue( UiConst.TextCancel ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Cancel ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Cancel" ); + return (TBuilder)this; + } + + /// + /// 配置Create文本 + /// + public TBuilder TextCreate() { + var value = _config.GetValue( UiConst.TextCreate ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Create ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Create" ); + return (TBuilder)this; + } + + /// + /// 配置Update文本 + /// + public TBuilder TextUpdate() { + var value = _config.GetValue( UiConst.TextUpdate ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Update ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Update" ); + return (TBuilder)this; + } + + /// + /// 配置Delete文本 + /// + public TBuilder TextDelete() { + var value = _config.GetValue( UiConst.TextDelete ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Delete ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Delete" ); + return (TBuilder)this; + } + + /// + /// 配置Detail文本 + /// + public TBuilder TextDetail() { + var value = _config.GetValue( UiConst.TextDetail ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Detail ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Detail" ); + return (TBuilder)this; + } + + /// + /// 配置Query文本 + /// + public TBuilder TextQuery() { + var value = _config.GetValue( UiConst.TextQuery ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Query ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Query" ); + return (TBuilder)this; + } + + /// + /// 配置Refresh文本 + /// + public TBuilder TextRefresh() { + var value = _config.GetValue( UiConst.TextRefresh ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Refresh ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Refresh" ); + return (TBuilder)this; + } + + /// + /// 配置Save文本 + /// + public TBuilder TextSave() { + var value = _config.GetValue( UiConst.TextSave ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Save ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Save" ); + return (TBuilder)this; + } + + /// + /// 配置Enable文本 + /// + public TBuilder TextEnable() { + var value = _config.GetValue( UiConst.TextEnable ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Enable ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Enable" ); + return (TBuilder)this; + } + + /// + /// 配置Disable文本 + /// + public TBuilder TextDisable() { + var value = _config.GetValue( UiConst.TextDisable ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Disable ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Disable" ); + return (TBuilder)this; + } + + /// + /// 配置Select All文本 + /// + public TBuilder TextSelectAll() { + var value = _config.GetValue( UiConst.TextSelectAll ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.SelectAll ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Select All" ); + return (TBuilder)this; + } + + /// + /// 配置Deselect All文本 + /// + public TBuilder TextDeselectAll() { + var value = _config.GetValue( UiConst.TextDeselectAll ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.DeselectAll ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Deselect All" ); + return (TBuilder)this; + } + + /// + /// 配置Upload文本 + /// + public TBuilder TextUpload() { + var value = _config.GetValue( UiConst.TextUpload ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Upload ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Upload" ); + return (TBuilder)this; + } + + /// + /// 配置Download文本 + /// + public TBuilder TextDownload() { + var value = _config.GetValue( UiConst.TextDownload ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Download ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Download" ); + return (TBuilder)this; + } + + /// + /// 配置Publish文本 + /// + public TBuilder TextPublish() { + var value = _config.GetValue( UiConst.TextPublish ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Publish ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Publish" ); + return (TBuilder)this; + } + + /// + /// 配置Run文本 + /// + public TBuilder TextRun() { + var value = _config.GetValue( UiConst.TextRun ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Run ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Run" ); + return (TBuilder)this; + } + + /// + /// 配置Start文本 + /// + public TBuilder TextStart() { + var value = _config.GetValue( UiConst.TextStart ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Start ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Start" ); + return (TBuilder)this; + } + + /// + /// 配置Stop文本 + /// + public TBuilder TextStop() { + var value = _config.GetValue( UiConst.TextStop ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Stop ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Stop" ); + return (TBuilder)this; + } + + /// + /// 配置Add文本 + /// + public TBuilder TextAdd() { + var value = _config.GetValue( UiConst.TextAdd ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Add ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Add" ); + return (TBuilder)this; + } + + /// + /// 配置Remove文本 + /// + public TBuilder TextRemove() { + var value = _config.GetValue( UiConst.TextRemove ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Remove ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Remove" ); + return (TBuilder)this; + } + + /// + /// 配置Open文本 + /// + public TBuilder TextOpen() { + var value = _config.GetValue( UiConst.TextOpen ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Open ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Open" ); + return (TBuilder)this; + } + + /// + /// 配置Close文本 + /// + public TBuilder TextClose() { + var value = _config.GetValue( UiConst.TextClose ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Close ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Close" ); + return (TBuilder)this; + } + + /// + /// 配置Send文本 + /// + public TBuilder TextSend() { + var value = _config.GetValue( UiConst.TextSend ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Send ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Send" ); + return (TBuilder)this; + } + + /// + /// 配置Clear文本 + /// + public TBuilder TextClear() { + var value = _config.GetValue( UiConst.TextClear ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Clear ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Clear" ); + return (TBuilder)this; + } + + /// + /// 配置Import文本 + /// + public TBuilder TextImport() { + var value = _config.GetValue( UiConst.TextImport ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Import ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Import" ); + return (TBuilder)this; + } + + /// + /// 配置Export文本 + /// + public TBuilder TextExport() { + var value = _config.GetValue( UiConst.TextExport ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Export ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Export" ); + return (TBuilder)this; + } + + /// + /// 配置Reset文本 + /// + public TBuilder TextReset() { + var value = _config.GetValue( UiConst.TextReset ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Reset ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "Reset" ); + return (TBuilder)this; + } + + /// + /// 配置Unedit文本 + /// + public TBuilder TextUnedit() { + var value = _config.GetValue( UiConst.TextUnedit ); + if ( value != true ) + return (TBuilder)this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Unedit ); + return (TBuilder)this; + } + _config.SetAttribute( UiConst.Text, "UnEdit" ); + return (TBuilder)this; + } + + /// + /// 配置文本 + /// + public TBuilder Text() { + var options = NgZorroOptionsService.GetOptions(); + var text = _config.GetValue( UiConst.Text ); + if ( text.IsEmpty() ) + return (TBuilder)this; + if ( options.EnableI18n ) { + this.AppendContentByI18n( text ); + return (TBuilder)this; + } + AppendContent( text ); + return (TBuilder)this; + } + + /// + /// 配置单击事件 + /// + public TBuilder OnClick() { + if ( IsValidateForm() ) { + var value = _config.GetValue( UiConst.OnClick ); + value = $"{GetButtonExtendId()}.validateForm();{value}"; + _config.SetAttribute( UiConst.OnClick, value ); + } + this.OnClick( _config ); + return (TBuilder)this; + } + + /// + /// 配置下拉菜单显示状态变化事件 + /// + public TBuilder OnVisibleChange() { + AttributeIfNotEmpty( "(nzVisibleChange)", _config.GetValue( UiConst.OnVisibleChange ) ); + return (TBuilder)this; + } + + /// + /// 配置按钮 + /// + protected TBuilder ConfigButton() { + EnableExtend(); + return Type().Size().Shape().Disabled().Danger().Loading().Ghost().Block().Icon() + .DropdownMenu().DropdownMenuPlacement().DropdownMenuTrigger().DropdownMenuClickHide() + .DropdownMenuVisible().DropdownMenuOverlayClassName().DropdownMenuOverlayStyle() + .SpaceItem().Tooltip( _config ).Popconfirm( _config ).Popover( _config ) + .TextOk().TextCancel().TextCreate().TextUpdate().TextDelete() + .TextDetail().TextQuery().TextRefresh().TextSave().TextEnable() + .TextDisable().TextSelectAll().TextDeselectAll() + .TextUpload().TextDownload().TextPublish().TextRun() + .TextStart().TextStop().TextAdd().TextRemove() + .TextOpen().TextClose().TextSend().TextClear() + .TextImport().TextExport().TextReset().TextUnedit() + .Text() + .OnClick().OnVisibleChange(); + } + + /// + /// 启用扩展 + /// + protected void EnableExtend() { + if ( IsEnableExtend() == false ) + return; + Attribute( "x-button-extend" ); + Attribute( $"#{GetButtonExtendId()}", "xButtonExtend" ); + DisableButton(); + } + + /// + /// 是否启用扩展 + /// + protected virtual bool IsEnableExtend() { + if ( IsValidateForm() ) + return true; + if ( IsScreenFull() ) + return true; + return false; + } + + /// + /// 是否验证表单 + /// + protected bool IsValidateForm() { + return _config.GetValue( UiConst.ValidateForm ) == true; + } + + /// + /// 是否全屏 + /// + protected bool IsScreenFull() { + return _config.Contains( UiConst.Fullscreen ); + } + + /// + /// 获取按钮扩展指令标识 + /// + protected string GetButtonExtendId() { + var id = _config.GetValue( UiConst.Id ); + if ( id.IsEmpty() ) + id = _id; + return $"x_{id}"; + } + + /// + /// 表单验证禁用按钮 + /// + protected void DisableButton() { + if ( IsValidateForm() == false ) + return; + var formShareConfig = _config.GetValueFromItems(); + if ( formShareConfig == null ) + return; + Attribute( "[disabled]", $"{GetButtonExtendId()}.isDisabled({formShareConfig.FormId}.invalid)" ); + } + + /// + /// 获取全屏外层容器样式类名 + /// + protected string GetFullscreenWrapClass() { + var className = _config.GetValue( UiConst.FullscreenWrapClass ); + if ( className.IsEmpty() ) { + if ( _config.Contains( UiConst.FullscreenPack ) || _config.Contains( UiConst.FullscreenTitle ) ) + return ",null"; + return null; + } + return $",'{className}'"; + } + + /// + /// 获取全屏包装 + /// + protected string GetFullscreenPack() { + var isPack = _config.GetBoolValue( UiConst.FullscreenPack ); + if ( isPack.IsEmpty() ) { + if ( _config.Contains( UiConst.FullscreenTitle ) ) + return ",true"; + return null; + } + return $",{isPack}"; + } + + /// + /// 获取全屏标题 + /// + protected string GetFullscreenTitle() { + var title = _config.GetValue( UiConst.FullscreenTitle ); + if ( title.IsEmpty() ) + return null; + return $",'{title}'"; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Base/ButtonTagHelperBase.cs b/src/Util.Ui.NgZorro/Components/Base/ButtonTagHelperBase.cs new file mode 100644 index 000000000..9fbb7f3b1 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Base/ButtonTagHelperBase.cs @@ -0,0 +1,266 @@ +using Util.Ui.Enums; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Base; + +/// +/// 按钮基类 +/// +public abstract class ButtonTagHelperBase : TooltipTagHelperBase { + /// + /// 扩展属性,全屏切换,设置需要全屏的内容区引用变量,范例 <div #id> , 传入 id + /// + public string Fullscreen { get; set; } + /// + /// 扩展属性,全屏标题 + /// + public string FullscreenTitle { get; set; } + /// + /// 扩展属性,进入全屏时,外层容器添加的样式类名, 设置为true,则设置默认样式类 x-fullscreen + /// + public string FullscreenWrapClass { get; set; } + /// + /// 扩展属性,全屏是否创建标题和页脚进行包装,默认值: true, 设置为 false 则完全受控,fullscreen-title 和 fullscreen-wrap-class 将无效 + /// + public bool FullscreenPack { get; set; } + /// + /// 扩展属性,内容文本,支持多语言 + /// + public string Text { get; set; } + /// + /// 扩展属性,是否显示ok文本,默认文本为'Ok',i18n文本为'util.ok' + /// + public bool TextOk { get; set; } + /// + /// 扩展属性,是否显示cancel文本,默认文本为'Cancel',i18n文本为'util.cancel' + /// + public bool TextCancel { get; set; } + /// + /// 扩展属性,是否显示create文本,默认文本为'Create',i18n文本为'util.create' + /// + public bool TextCreate { get; set; } + /// + /// 扩展属性,是否显示update文本,默认文本为'Update',i18n文本为'util.update' + /// + public bool TextUpdate { get; set; } + /// + /// 扩展属性,是否显示delete文本,默认文本为'Delete',i18n文本为'util.delete' + /// + public bool TextDelete { get; set; } + /// + /// 扩展属性,是否显示detail文本,默认文本为'Detail',i18n文本为'util.detail' + /// + public bool TextDetail { get; set; } + /// + /// 扩展属性,是否显示query文本,默认文本为'Query',i18n文本为'util.query' + /// + public bool TextQuery { get; set; } + /// + /// 扩展属性,是否显示refresh文本,默认文本为'Refresh',i18n文本为'util.refresh' + /// + public bool TextRefresh { get; set; } + /// + /// 扩展属性,是否显示save文本,默认文本为'Save',i18n文本为'util.save' + /// + public bool TextSave { get; set; } + /// + /// 扩展属性,是否显示enable文本,默认文本为'Enable',i18n文本为'util.enable' + /// + public bool TextEnable { get; set; } + /// + /// 扩展属性,是否显示disable文本,默认文本为'Disable',i18n文本为'util.disable' + /// + public bool TextDisable { get; set; } + /// + /// 扩展属性,是否显示select all文本,默认文本为'Select All',i18n文本为'util.selectAll' + /// + public bool TextSelectAll { get; set; } + /// + /// 扩展属性,是否显示deselect all文本,默认文本为'Deselect All',i18n文本为'util.deselectAll' + /// + public bool TextDeselectAll { get; set; } + /// + /// 扩展属性,是否显示upload文本,默认文本为'Upload',i18n文本为'util.upload' + /// + public bool TextUpload { get; set; } + /// + /// 扩展属性,是否显示download文本,默认文本为'Download',i18n文本为'util.download' + /// + public bool TextDownload { get; set; } + /// + /// 扩展属性,是否显示publish文本,默认文本为'Publish',i18n文本为'util.publish' + /// + public bool TextPublish { get; set; } + /// + /// 扩展属性,是否显示run文本,默认文本为'Run',i18n文本为'util.run' + /// + public bool TextRun { get; set; } + /// + /// 扩展属性,是否显示start文本,默认文本为'Start',i18n文本为'util.start' + /// + public bool TextStart { get; set; } + /// + /// 扩展属性,是否显示stop文本,默认文本为'Stop',i18n文本为'util.stop' + /// + public bool TextStop { get; set; } + /// + /// 扩展属性,是否显示add文本,默认文本为'Add',i18n文本为'util.add' + /// + public bool TextAdd { get; set; } + /// + /// 扩展属性,是否显示remove文本,默认文本为'Remove',i18n文本为'util.remove' + /// + public bool TextRemove { get; set; } + /// + /// 扩展属性,是否显示open文本,默认文本为'Open',i18n文本为'util.open' + /// + public bool TextOpen { get; set; } + /// + /// 扩展属性,是否显示close文本,默认文本为'Close',i18n文本为'util.close' + /// + public bool TextClose { get; set; } + /// + /// 扩展属性,是否显示send文本,默认文本为'Send',i18n文本为'util.send' + /// + public bool TextSend { get; set; } + /// + /// 扩展属性,是否显示clear文本,默认文本为'Clear',i18n文本为'util.clear' + /// + public bool TextClear { get; set; } + /// + /// 扩展属性,是否显示import文本,默认文本为'Import',i18n文本为'util.import' + /// + public bool TextImport { get; set; } + /// + /// 扩展属性,是否显示export文本,默认文本为'Export',i18n文本为'util.export' + /// + public bool TextExport { get; set; } + /// + /// 扩展属性,是否显示reset文本,默认文本为'Reset',i18n文本为'util.reset' + /// + public bool TextReset { get; set; } + /// + /// 扩展属性,是否显示unedit文本,默认文本为'UnEdit',i18n文本为'util.unedit' + /// + public bool TextUnedit { get; set; } + /// + /// [nzDropdownMenu],设置下拉菜单 + /// + public string DropdownMenu { get; set; } + /// + /// nzPlacement,下拉菜单弹出位置, 可选值: bottomLeft' | 'bottomCenter' | 'bottomRight' | 'topLeft' | 'topCenter' | 'topRight' + /// + public DropdownMenuPlacement DropdownMenuPlacement { get; set; } + /// + /// [nzPlacement],下拉菜单弹出位置, 可选值: bottomLeft' | 'bottomCenter' | 'bottomRight' | 'topLeft' | 'topCenter' | 'topRight' + /// + public string BindDropdownMenuPlacement { get; set; } + /// + /// nzTrigger,下拉菜单触发方式 + /// + public DropdownMenuTrigger DropdownMenuTrigger { get; set; } + /// + /// [nzTrigger],下拉菜单触发方式 + /// + public string BindDropdownMenuTrigger { get; set; } + /// + /// [nzClickHide],点击后是否隐藏下拉菜单,默认值:true + /// + public string DropdownMenuClickHide { get; set; } + /// + /// [nzVisible],下拉菜单是否可见 + /// + public string DropdownMenuVisible { get; set; } + /// + /// [(nzVisible)],下拉菜单是否可见 + /// + public string BindonDropdownMenuVisible { get; set; } + /// + /// nzOverlayClassName,下拉菜单根元素类名 + /// + public string DropdownMenuOverlayClassName { get; set; } + /// + /// [nzOverlayClassName],下拉菜单根元素类名 + /// + public string BindDropdownMenuOverlayClassName { get; set; } + /// + /// [nzOverlayStyle],下拉菜单根元素样式 + /// + public string DropdownMenuOverlayStyle { get; set; } + /// + /// *nzSpaceItem,值为 true 时设置为间距项,放入 <util-space> 中使用 + /// + public bool SpaceItem { get; set; } + /// + /// 扩展属性,是否查询表单链接,折叠时显示`展开`文本,展开时显示`收起`文本 + /// + public bool IsSearch { get; set; } + /// + /// 扩展属性,显示表格设置,参数值为表格标识 + /// + public string ShowTableSettings { get; set; } + /// + /// href,链接地址 + /// + public string Href { get; set; } + /// + /// [href],链接地址 + /// + public string BindHref { get; set; } + /// + /// target,链接打开目标, 可选值: '_self' | '_blank' | '_parent' | '_top' + /// + public ATarget Target { get; set; } + /// + /// [target],链接打开目标, 可选值: '_self' | '_blank' | '_parent' | '_top' + /// + public string BindTarget { get; set; } + /// + /// rel,指定当前文档与被链接文档的关系 + /// + public string Rel { get; set; } + /// + /// [rel],指定当前文档与被链接文档的关系 + /// + public string BindRel { get; set; } + /// + /// routerLink,路由链接地址 + /// + public string RouterLink { get; set; } + /// + /// [routerLink],路由链接地址 + /// + public string BindRouterLink { get; set; } + /// + /// routerLinkActive,设置当前活动路由链接的css类 + /// + public string RouterLinkActive { get; set; } + /// + /// [routerLinkActive],设置当前活动路由链接的css类 + /// + public string BindRouterLinkActive { get; set; } + /// + /// [queryParams],路由查询参数 + /// + public string QueryParams { get; set; } + /// + /// queryParamsHandling,路由链接查询参数处理方式, 可选值: 'merge' | 'preserve', merge 将新参数与当前参数合并, preserve 保留当前参数. + /// + public QueryParamsHandling QueryParamsHandling { get; set; } + /// + /// [queryParamsHandling],路由链接查询参数处理方式, 可选值: 'merge' | 'preserve', merge 将新参数与当前参数合并, preserve 保留当前参数. + /// + public string BindQueryParamsHandling { get; set; } + /// + /// *nzTabLink nz-tab-link, 选项卡路由联动 + /// + public string TabLink { get; set; } + /// + /// (click),单击事件 + /// + public string OnClick { get; set; } + /// + /// (nzVisibleChange),下拉菜单显示状态变化事件 + /// + public string OnVisibleChange { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Base/ColumnBuilderBase.cs b/src/Util.Ui.NgZorro/Components/Base/ColumnBuilderBase.cs new file mode 100644 index 000000000..7bfd51ed1 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Base/ColumnBuilderBase.cs @@ -0,0 +1,439 @@ +using Microsoft.AspNetCore.Mvc.Rendering; +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Grids.Helpers; + +namespace Util.Ui.NgZorro.Components.Base; + +/// +/// 栅格列标签生成器基类 +/// +public abstract class ColumnBuilderBase : AngularTagBuilder where TBuilder : ColumnBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化栅格列标签生成器基类 + /// + /// 配置 + /// 标签名称,范例:div + /// 渲染模式 + protected ColumnBuilderBase( Config config, string tagName, TagRenderMode renderMode = TagRenderMode.Normal ) : base( config, tagName, renderMode ) { + _config = config; + } + + /// + /// 配置跨度 + /// + public virtual TBuilder Span() { + AttributeIfNotEmpty( "[nzSpan]", _config.GetValue( UiConst.Span ) ); + return (TBuilder)this; + } + + /// + /// 配置偏移量 + /// + public virtual TBuilder Offset() { + AttributeIfNotEmpty( "[nzOffset]", _config.GetValue( UiConst.Offset ) ); + return (TBuilder)this; + } + + /// + /// 配置左移 + /// + public virtual TBuilder Pull() { + AttributeIfNotEmpty( "[nzPull]", _config.GetValue( UiConst.Pull ) ); + return (TBuilder)this; + } + + /// + /// 配置右移 + /// + public virtual TBuilder Push() { + AttributeIfNotEmpty( "[nzPush]", _config.GetValue( UiConst.Push ) ); + return (TBuilder)this; + } + + /// + /// 配置栅格顺序 + /// + public virtual TBuilder Order() { + AttributeIfNotEmpty( "[nzOrder]", _config.GetValue( UiConst.Order ) ); + return (TBuilder)this; + } + + /// + /// 配置Flex布局 + /// + public virtual TBuilder Flex() { + AttributeIfNotEmpty( "nzFlex", _config.GetValue( UiConst.Flex ) ); + AttributeIfNotEmpty( "[nzFlex]", _config.GetValue( AngularConst.BindFlex ) ); + return (TBuilder)this; + } + + /// + /// 配置Xs超窄尺寸响应式栅格 + /// + public TBuilder ConfigXs() { + AttributeIfNotEmpty( "[nzXs]", GetXs() ); + var model = new GridModel { + Span = GetXsSpan(), + Offset = GetXsOffset(), + Order = GetXsOrder(), + Pull = GetXsPull(), + Push = GetXsPush() + }; + AttributeIfNotEmpty( "[nzXs]", model.ToJson() ); + return (TBuilder)this; + } + + /// + /// 获取Xs超窄尺寸响应式栅格 + /// + protected virtual string GetXs() { + return _config.GetValue( UiConst.Xs ); + } + + /// + /// 获取Xs超窄尺寸跨度 + /// + protected virtual int? GetXsSpan() { + return _config.GetValue( UiConst.XsSpan ); + } + + /// + /// 获取Xs超窄尺寸偏移量 + /// + protected virtual int? GetXsOffset() { + return _config.GetValue( UiConst.XsOffset ); + } + + /// + /// 获取Xs超窄尺寸顺序 + /// + protected virtual int? GetXsOrder() { + return _config.GetValue( UiConst.XsOrder ); + } + + /// + /// 获取Xs超窄尺寸左移 + /// + protected virtual int? GetXsPull() { + return _config.GetValue( UiConst.XsPull ); + } + + /// + /// 获取Xs超窄尺寸右移 + /// + protected virtual int? GetXsPush() { + return _config.GetValue( UiConst.XsPush ); + } + + /// + /// 配置Sm窄尺寸响应式栅格 + /// + public TBuilder ConfigSm() { + AttributeIfNotEmpty( "[nzSm]", GetSm() ); + var model = new GridModel { + Span = GetSmSpan(), + Offset = GetSmOffset(), + Order = GetSmOrder(), + Pull = GetSmPull(), + Push = GetSmPush() + }; + AttributeIfNotEmpty( "[nzSm]", model.ToJson() ); + return (TBuilder)this; + } + + /// + /// 获取Sm窄尺寸响应式栅格 + /// + protected virtual string GetSm() { + return _config.GetValue( UiConst.Sm ); + } + + /// + /// 获取Sm窄尺寸跨度 + /// + protected virtual int? GetSmSpan() { + return _config.GetValue( UiConst.SmSpan ); + } + + /// + /// 获取Sm窄尺寸偏移量 + /// + protected virtual int? GetSmOffset() { + return _config.GetValue( UiConst.SmOffset ); + } + + /// + /// 获取Sm窄尺寸顺序 + /// + protected virtual int? GetSmOrder() { + return _config.GetValue( UiConst.SmOrder ); + } + + /// + /// 获取Sm窄尺寸左移 + /// + protected virtual int? GetSmPull() { + return _config.GetValue( UiConst.SmPull ); + } + + /// + /// 获取Sm窄尺寸右移 + /// + protected virtual int? GetSmPush() { + return _config.GetValue( UiConst.SmPush ); + } + + /// + /// 配置Md中尺寸响应式栅格 + /// + public TBuilder ConfigMd() { + AttributeIfNotEmpty( "[nzMd]", GetMd() ); + return (TBuilder)this; + } + + /// + /// 获取Md中尺寸响应式栅格 + /// + protected virtual string GetMd() { + var result = _config.GetValue( UiConst.Md ); + if ( result.IsEmpty() == false ) + return result; + var model = new GridModel { + Span = GetMdSpan(), + Offset = GetMdOffset(), + Order = GetMdOrder(), + Pull = GetMdPull(), + Push = GetMdPush() + }; + return model.ToJson(); + } + + /// + /// 获取Md中尺寸跨度 + /// + protected virtual int? GetMdSpan() { + return _config.GetValue( UiConst.MdSpan ); + } + + /// + /// 获取Md中尺寸偏移量 + /// + protected virtual int? GetMdOffset() { + return _config.GetValue( UiConst.MdOffset ); + } + + /// + /// 获取Md中尺寸顺序 + /// + protected virtual int? GetMdOrder() { + return _config.GetValue( UiConst.MdOrder ); + } + + /// + /// 获取Md中尺寸左移 + /// + protected virtual int? GetMdPull() { + return _config.GetValue( UiConst.MdPull ); + } + + /// + /// 获取Md中尺寸右移 + /// + protected virtual int? GetMdPush() { + return _config.GetValue( UiConst.MdPush ); + } + + /// + /// 配置Lg宽尺寸响应式栅格 + /// + public TBuilder ConfigLg() { + AttributeIfNotEmpty( "[nzLg]", GetLg() ); + return (TBuilder)this; + } + + /// + /// 获取Lg宽尺寸响应式栅格 + /// + protected virtual string GetLg() { + var result = _config.GetValue( UiConst.Lg ); + if ( result.IsEmpty() == false ) + return result; + var model = new GridModel { + Span = GetLgSpan(), + Offset = GetLgOffset(), + Order = GetLgOrder(), + Pull = GetLgPull(), + Push = GetLgPush() + }; + return model.ToJson(); + } + + /// + /// 获取Lg宽尺寸跨度 + /// + protected virtual int? GetLgSpan() { + return _config.GetValue( UiConst.LgSpan ); + } + + /// + /// 获取Lg宽尺寸偏移量 + /// + protected virtual int? GetLgOffset() { + return _config.GetValue( UiConst.LgOffset ); + } + + /// + /// 获取Lg宽尺寸顺序 + /// + protected virtual int? GetLgOrder() { + return _config.GetValue( UiConst.LgOrder ); + } + + /// + /// 获取Lg宽尺寸左移 + /// + protected virtual int? GetLgPull() { + return _config.GetValue( UiConst.LgPull ); + } + + /// + /// 获取Lg宽尺寸右移 + /// + protected virtual int? GetLgPush() { + return _config.GetValue( UiConst.LgPush ); + } + + /// + /// 配置Xl超宽尺寸响应式栅格 + /// + public TBuilder ConfigXl() { + AttributeIfNotEmpty( "[nzXl]", GetXl() ); + return (TBuilder)this; + } + + /// + /// 获取Xl超宽尺寸响应式栅格 + /// + protected virtual string GetXl() { + var result = _config.GetValue( UiConst.Xl ); + if ( result.IsEmpty() == false ) + return result; + var model = new GridModel { + Span = GetXlSpan(), + Offset = GetXlOffset(), + Order = GetXlOrder(), + Pull = GetXlPull(), + Push = GetXlPush() + }; + return model.ToJson(); + } + + /// + /// 获取Xl超宽尺寸跨度 + /// + protected virtual int? GetXlSpan() { + return _config.GetValue( UiConst.XlSpan ); + } + + /// + /// 获取Xl超宽尺寸偏移量 + /// + protected virtual int? GetXlOffset() { + return _config.GetValue( UiConst.XlOffset ); + } + + /// + /// 获取Xl超宽尺寸顺序 + /// + protected virtual int? GetXlOrder() { + return _config.GetValue( UiConst.XlOrder ); + } + + /// + /// 获取Xl超宽尺寸左移 + /// + protected virtual int? GetXlPull() { + return _config.GetValue( UiConst.XlPull ); + } + + /// + /// 获取Xl超宽尺寸右移 + /// + protected virtual int? GetXlPush() { + return _config.GetValue( UiConst.XlPush ); + } + + /// + /// 配置Xxl极宽尺寸响应式栅格 + /// + public TBuilder ConfigXxl() { + AttributeIfNotEmpty( "[nzXXl]", GetXxl() ); + return (TBuilder)this; + } + + /// + /// 获取Xxl极宽尺寸响应式栅格 + /// + protected virtual string GetXxl() { + var result = _config.GetValue( UiConst.Xxl ); + if ( result.IsEmpty() == false ) + return result; + var model = new GridModel { + Span = GetXxlSpan(), + Offset = GetXxlOffset(), + Order = GetXxlOrder(), + Pull = GetXxlPull(), + Push = GetXxlPush() + }; + return model.ToJson(); + } + + /// + /// 获取Xxl极宽尺寸跨度 + /// + protected virtual int? GetXxlSpan() { + return _config.GetValue( UiConst.XxlSpan ); + } + + /// + /// 获取Xxl极宽尺寸偏移量 + /// + protected virtual int? GetXxlOffset() { + return _config.GetValue( UiConst.XxlOffset ); + } + + /// + /// 获取Xxl极宽尺寸顺序 + /// + protected virtual int? GetXxlOrder() { + return _config.GetValue( UiConst.XxlOrder ); + } + + /// + /// 获取Xxl极宽尺寸左移 + /// + protected virtual int? GetXxlPull() { + return _config.GetValue( UiConst.XxlPull ); + } + + /// + /// 获取Xxl极宽尺寸右移 + /// + protected virtual int? GetXxlPush() { + return _config.GetValue( UiConst.XxlPush ); + } + + /// + /// 配置栅格列 + /// + protected TBuilder ConfigColumn() { + return Span().Offset().Pull().Push().Order().Flex() + .ConfigXs().ConfigSm().ConfigMd().ConfigLg().ConfigXl().ConfigXxl(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Base/ColumnTagHelperBase.cs b/src/Util.Ui.NgZorro/Components/Base/ColumnTagHelperBase.cs new file mode 100644 index 000000000..410695e8c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Base/ColumnTagHelperBase.cs @@ -0,0 +1,181 @@ +using Util.Ui.Angular.TagHelpers; + +namespace Util.Ui.NgZorro.Components.Base; + +/// +/// 栅格列基类 +/// +public abstract class ColumnTagHelperBase : AngularTagHelperBase { + /// + /// [nzSpan],24栅格占位格数,可选值: 0 - 24, 为 0 时隐藏 + /// + public string Span { get; set; } + /// + /// [nzOffset],栅格向右偏移的格数,即栅格左侧的间隔格数,间隔内不能有栅格,范例: 设置值为4,将元素向右侧偏移4格宽度 + /// + public string Offset { get; set; } + /// + /// [nzOrder],栅格顺序 + /// + public new string Order { get; set; } + /// + /// [nzPull],栅格向左移动的格数 + /// + public string Pull { get; set; } + /// + /// [nzPush],栅格向右移动的格数 + /// + public string Push { get; set; } + /// + /// nzFlex,Flex布局 + /// + public string Flex { get; set; } + /// + /// [nzFlex],Flex布局 + /// + public string BindFlex { get; set; } + /// + /// [nzXs], <576px超窄尺寸响应式栅格,可以设置一个数字,相当于[nzXs]="{span:数字}",也可以设置为对象,范例:{span:5,offset:1} + /// + public string Xs { get; set; } + /// + /// <576px超窄尺寸响应式栅格占位格数,可选值: 0 - 24, 为 0 时隐藏,范例:设置值为 6,生成 [nzXs]="{span:6}" + /// + public int XsSpan { get; set; } + /// + /// <576px超窄尺寸响应式栅格向右偏移的格数,间隔内不能有栅格,范例: 设置值为 6,将元素向右侧偏移了6格宽度,生成 [nzXs]="{offset:6}" + /// + public int XsOffset { get; set; } + /// + /// <576px超窄尺寸响应式栅格顺序,范例: 设置值为 6,生成 [nzXs]="{order:6}" + /// + public int XsOrder { get; set; } + /// + /// <576px超窄尺寸响应式栅格向左移动的格数,范例: 设置值为 6,生成 [nzXs]="{pull:6}" + /// + public int XsPull { get; set; } + /// + /// <576px超窄尺寸响应式栅格向右移动的格数,范例: 设置值为 6,生成 [nzXs]="{push:6}" + /// + public int XsPush { get; set; } + /// + /// [nzSm], ≥576px窄尺寸响应式栅格,可以设置一个数字,相当于[nzSm]="{span:数字}",也可以设置为对象,范例:{span:5,offset:1} + /// + public string Sm { get; set; } + /// + /// ≥576px窄尺寸响应式栅格占位格数,可选值: 0 - 24, 为 0 时隐藏,范例:设置值为 6,生成 [nzSm]="{span:6}" + /// + public int SmSpan { get; set; } + /// + /// ≥576px窄尺寸响应式栅格向右偏移的格数,间隔内不能有栅格,范例: 设置值为 6,将元素向右侧偏移了6格宽度,生成 [nzSm]="{offset:6}" + /// + public int SmOffset { get; set; } + /// + /// ≥576px窄尺寸响应式栅格顺序,范例: 设置值为 6,生成 [nzSm]="{order:6}" + /// + public int SmOrder { get; set; } + /// + /// ≥576px窄尺寸响应式栅格向左移动的格数,范例: 设置值为 6,生成 [nzSm]="{pull:6}" + /// + public int SmPull { get; set; } + /// + /// ≥576px窄尺寸响应式栅格向右移动的格数,范例: 设置值为 6,生成 [nzSm]="{push:6}" + /// + public int SmPush { get; set; } + /// + /// [nzMd], ≥768px中尺寸响应式栅格,可以设置一个数字,相当于[nzMd]="{span:数字}",也可以设置为对象,范例:{span:5,offset:1} + /// + public string Md { get; set; } + /// + /// ≥768px中尺寸响应式栅格占位格数,可选值: 0 - 24, 为 0 时隐藏,范例:设置值为 6,生成 [nzMd]="{span:6}" + /// + public int MdSpan { get; set; } + /// + /// ≥768px中尺寸响应式栅格向右偏移的格数,间隔内不能有栅格,范例: 设置值为 6,将元素向右侧偏移了6格宽度,生成 [nzMd]="{offset:6}" + /// + public int MdOffset { get; set; } + /// + /// ≥768px中尺寸响应式栅格顺序,范例: 设置值为 6,生成 [nzMd]="{order:6}" + /// + public int MdOrder { get; set; } + /// + /// ≥768px中尺寸响应式栅格向左移动的格数,范例: 设置值为 6,生成 [nzMd]="{pull:6}" + /// + public int MdPull { get; set; } + /// + /// ≥768px中尺寸响应式栅格向右移动的格数,范例: 设置值为 6,生成 [nzMd]="{push:6}" + /// + public int MdPush { get; set; } + /// + /// [nzLg], ≥992px宽尺寸响应式栅格,可以设置一个数字,相当于 [nzLg]="{span:数字}",也可以设置为对象,范例: {span:5,offset:1} + /// + public string Lg { get; set; } + /// + /// ≥992px宽尺寸响应式栅格占位格数,可选值: 0 - 24, 为 0 时隐藏,范例:设置值为 6,生成 [nzLg]="{span:6}" + /// + public int LgSpan { get; set; } + /// + /// ≥992px宽尺寸响应式栅格向右偏移的格数,间隔内不能有栅格,范例: 设置值为 6,将元素向右侧偏移了6格宽度,生成 [nzLg]="{offset:6}" + /// + public int LgOffset { get; set; } + /// + /// ≥992px宽尺寸响应式栅格顺序,范例: 设置值为 6,生成 [nzLg]="{order:6}" + /// + public int LgOrder { get; set; } + /// + /// ≥992px宽尺寸响应式栅格向左移动的格数,范例: 设置值为 6,生成 [nzLg]="{pull:6}" + /// + public int LgPull { get; set; } + /// + /// ≥992px宽尺寸响应式栅格向右移动的格数,范例: 设置值为 6,生成 [nzLg]="{push:6}" + /// + public int LgPush { get; set; } + /// + /// [nzXl], ≥1200px超宽尺寸响应式栅格,可以设置一个数字,相当于[nzXl]="{span:数字}",也可以设置为对象,范例:{span:5,offset:1} + /// + public string Xl { get; set; } + /// + /// ≥1200px超宽尺寸响应式栅格占位格数,可选值: 0 - 24, 为 0 时隐藏,范例:设置值为 6,生成 [nzXl]="{span:6}" + /// + public int XlSpan { get; set; } + /// + /// ≥1200px超宽尺寸响应式栅格向右偏移的格数,间隔内不能有栅格,范例: 设置值为 6,将元素向右侧偏移了6格宽度,生成 [nzXl]="{offset:6}" + /// + public int XlOffset { get; set; } + /// + /// ≥1200px超宽尺寸响应式栅格顺序,范例: 设置值为 6,生成 [nzXl]="{order:6}" + /// + public int XlOrder { get; set; } + /// + /// ≥1200px超宽尺寸响应式栅格向左移动的格数,范例: 设置值为 6,生成 [nzXl]="{pull:6}" + /// + public int XlPull { get; set; } + /// + /// ≥1200px超宽尺寸响应式栅格向右移动的格数,范例: 设置值为 6,生成 [nzXl]="{push:6}" + /// + public int XlPush { get; set; } + /// + /// [nzXXl], ≥1600px极宽尺寸响应式栅格,可以设置一个数字,相当于[nzXXl]="{span:数字}",也可以设置为对象,范例:{span:5,offset:1} + /// + public string Xxl { get; set; } + /// + /// ≥1600px极宽尺寸响应式栅格占位格数,可选值: 0 - 24, 为 0 时隐藏,范例:设置值为 6,生成[nzXXl]="{span:6}" + /// + public int XxlSpan { get; set; } + /// + /// ≥1600px极宽尺寸响应式栅格向右偏移的格数,间隔内不能有栅格,范例: 设置值为 6,将元素向右侧偏移了6格宽度,生成 [nzXXl]="{offset:6}" + /// + public int XxlOffset { get; set; } + /// + /// ≥1600px极宽尺寸响应式栅格顺序,范例: 设置值为 6,生成 [nzXXl]="{order:6}" + /// + public int XxlOrder { get; set; } + /// + /// ≥1600px极宽尺寸响应式栅格向左移动的格数,范例: 设置值为 6,生成 [nzXXl]="{pull:6}" + /// + public int XxlPull { get; set; } + /// + /// ≥1600px极宽尺寸响应式栅格向右移动的格数,范例: 设置值为 6,生成 [nzXXl]="{push:6}" + /// + public int XxlPush { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Base/DatePickerTagHelperBase.cs b/src/Util.Ui.NgZorro/Components/Base/DatePickerTagHelperBase.cs new file mode 100644 index 000000000..0217f3aae --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Base/DatePickerTagHelperBase.cs @@ -0,0 +1,173 @@ +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Base; + +/// +/// 日期选择基类 +/// +public abstract class DatePickerTagHelperBase : FormControlTagHelperBase { + /// + /// nzId,组件内部 input 的 id 值 + /// + public string NzId { get; set; } + /// + /// [nzId],组件内部 input 的 id 值 + /// + public string BindNzId { get; set; } + /// + /// [nzAllowClear],是否允许清除内容, 类型: boolean, 默认值: true + /// + public string AllowClear { get; set; } + /// + /// [nzAutoFocus],是否自动聚焦, 类型: boolean, 默认值: false + /// + public string AutoFocus { get; set; } + /// + /// [nzBackdrop],浮层是否应带有背景板, 类型: boolean, 默认值: false + /// + public string Backdrop { get; set; } + /// + /// nzMode,选择模式,可选值: 'date' | 'week' | 'month' | 'year', 默认值: 'date' + /// + public DatePickerMode Mode { get; set; } + /// + /// [nzMode],选择模式,可选值: 'date' | 'week' | 'month' | 'year', 默认值: 'date' + /// + public string BindMode { get; set; } + /// + /// [nzShowTime],是否显示时间选择,注意:该属性仅在 nzMode="date" 时有效,可传入时间选择器配置参数, 类型: object | boolean + /// + public string ShowTime { get; set; } + /// + /// nzFormat,格式化字符串,格式说明: + /// 1. 年 - yyyy + /// 2. 月 - MM + /// 3. 日 - dd + /// 4. 时 - HH + /// 5. 分 - mm + /// 6. 秒 - ss + /// 7. 毫秒 - SSS + /// 8. 周 - ww + /// + public string Format { get; set; } + /// + /// [nzFormat],格式化字符串,格式说明: + /// 1. 年 - yyyy + /// 2. 月 - MM + /// 3. 日 - dd + /// 4. 时 - HH + /// 5. 分 - mm + /// 6. 秒 - ss + /// 7. 毫秒 - SSS + /// 8. 周 - ww + /// + public string BindFormat { get; set; } + /// + /// nzSize,输入框尺寸, 可选值: 'default' | 'small' | 'large', 默认值: 'default' + /// + public InputSize Size { get; set; } + /// + /// [nzSize],输入框尺寸, 可选值: 'default' | 'small' | 'large', 默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// [nzDisabled],是否禁用, 类型: boolean, 默认值: false + /// + public string Disabled { get; set; } + /// + /// [nzBorderless],是否移除边框, 类型: boolean, 默认值: false + /// + public string Borderless { get; set; } + /// + /// [nzInline],是否内联模式, 类型: boolean, 默认值: false + /// + public string Inline { get; set; } + /// + /// [nzShowWeekNumber],是否在每一行显示周数, 类型: boolean, 默认值: false + /// + public string ShowWeekNumber { get; set; } + /// + /// [nzDisabledDate],不可选择日期函数,类型:(current: Date) => boolean + /// + public string DisabledDate { get; set; } + /// + /// nzPlaceHolder,输入框占位文本 + /// + public string Placeholder { get; set; } + /// + /// [nzPlaceHolder],输入框占位文本 + /// + public string BindPlaceholder { get; set; } + /// + /// nzRenderExtraFooter,添加额外页脚, 类型: TemplateRef | string | (() => TemplateRef | string) + /// + public string RenderExtraFooter { get; set; } + /// + /// [nzRenderExtraFooter],添加额外页脚, 类型: TemplateRef | string | (() => TemplateRef | string) + /// + public string BindRenderExtraFooter { get; set; } + /// + /// [nzDateRender],自定义日期单元格内容,注意:该属性仅在 nzMode="date" 时有效, 类型: string | TemplateRef<Date> | ((d: Date) => TemplateRef<Date> | string) + /// + public string DateRender { get; set; } + /// + /// nzStatus,校验状态, 可选值: 'error' | 'warning' + /// + public FormControlStatus Status { get; set; } + /// + /// [nzStatus],校验状态, 可选值: 'error' | 'warning' + /// + public string BindStatus { get; set; } + /// + /// nzPlacement,日期选择框弹出位置, 可选值: 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topRight', 默认值: 'bottomLeft' + /// + public DatePickerPlacement Placement { get; set; } + /// + /// [nzPlacement],日期选择框弹出位置, 可选值: 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topRight', 默认值: 'bottomLeft' + /// + public string BindPlacement { get; set; } + /// + /// nzDropdownClassName,弹出日历样式类名 + /// + public string DropdownClassName { get; set; } + /// + /// [nzDropdownClassName],弹出日历样式类名 + /// + public string BindDropdownClassName { get; set; } + /// + /// [nzPopupStyle],弹出日历样式, 类型: object + /// + public string PopupStyle { get; set; } + /// + /// [nzInputReadOnly],为 input 标签设置只读属性,避免在移动设备上触发小键盘, 类型: boolean, 默认值: false + /// + public string InputReadonly { get; set; } + /// + /// [nzLocale],国际化配置, 类型: object + /// + public string Locale { get; set; } + /// + /// nzSuffixIcon,后缀图标 + /// + public AntDesignIcon SuffixIcon { get; set; } + /// + /// [nzSuffixIcon],后缀图标, 类型: string | TemplateRef + /// + public string BindSuffixIcon { get; set; } + /// + /// [required],是否必填项, 类型: boolean, 默认值: false + /// + public string Required { get; set; } + /// + /// 扩展属性 requiredMessage,必填项验证消息 + /// + public string RequiredMessage { get; set; } + /// + /// 扩展属性 [requiredMessage],必填项验证消息 + /// + public string BindRequiredMessage { get; set; } + /// + /// (nzOnOpenChange),弹出关闭日历事件, 类型: EventEmitter<boolean> + /// + public string OnOpenChange { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Base/FormContainerTagHelperBase.cs b/src/Util.Ui.NgZorro/Components/Base/FormContainerTagHelperBase.cs new file mode 100644 index 000000000..4c0ec914a --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Base/FormContainerTagHelperBase.cs @@ -0,0 +1,351 @@ +namespace Util.Ui.NgZorro.Components.Base; + +/// +/// 表单容器基类 +/// +public abstract class FormContainerTagHelperBase : RowTagHelperBase { + /// + /// 扩展属性,自动设置 <nz-form-label> 的 nzFor 属性,如果未设置控件Id,则自动创建Id, 类型: boolean, 默认值: false + /// + public bool AutoNzFor { get; set; } + /// + /// 扩展属性,[nzHasFeedback],是否显示校验状态反馈图标,类型: boolean, 默认值: false + /// + public bool HasFeedback { get; set; } + /// + /// 扩展属性,标签栅格占位格数,设置 nz-form-label 的 [nzSpan] 属性,可选值: 0 - 24, 为 0 时隐藏 + /// + public string LabelSpan { get; set; } + /// + /// 扩展属性,标签栅格向右偏移的格数,即栅格左侧的间隔格数,间隔内不可以有栅格,设置 nz-form-label 的 [nzOffset] 属性,范例: 设置值为4,将标签向右侧偏移4格宽度 + /// + public string LabelOffset { get; set; } + /// + /// 扩展属性,标签栅格顺序,设置 nz-form-label 的 [nzOrder] 属性 + /// + public string LabelOrder { get; set; } + /// + /// 扩展属性,标签栅格向左移动的格数,设置 nz-form-label 的 [nzPull] 属性 + /// + public string LabelPull { get; set; } + /// + /// 扩展属性,标签栅格向右移动的格数,设置 nz-form-label 的 [nzPush] 属性 + /// + public string LabelPush { get; set; } + /// + /// 扩展属性,标签Flex布局,设置 nz-form-label 的 [nzFlex] 属性 + /// + public string LabelFlex { get; set; } + /// + /// 扩展属性,标签<576px超窄尺寸响应式栅格,设置 nz-form-label 的 [nzXs] 属性,可以设置一个数字,相当于[nzXs]="{span:数字}",也可以设置为对象,范例:{span:5,offset:1} + /// + public string LabelXs { get; set; } + /// + /// 扩展属性,标签<576px超窄尺寸响应式栅格占位格数,设置 nz-form-label 的 [nzXs] 属性,可选值: 0 - 24, 为 0 时隐藏,范例:设置值为6,生成[nzXs]="{span:6}" + /// + public int LabelXsSpan { get; set; } + /// + /// 扩展属性,标签<576px超窄尺寸响应式栅格向右偏移的格数,即栅格左侧的间隔格数,间隔内不可以有栅格,设置 nz-form-label 的 [nzXs] 属性,范例: 设置值为6,将元素向右侧偏移了6格宽度,生成[nzXs]="{offset:6}" + /// + public int LabelXsOffset { get; set; } + /// + /// 扩展属性,标签<576px超窄尺寸响应式栅格顺序,设置 nz-form-label 的 [nzXs] 属性,范例: 设置值为6,生成[nzXs]="{order:6}" + /// + public int LabelXsOrder { get; set; } + /// + /// 扩展属性,标签<576px超窄尺寸响应式栅格向左移动的格数,设置 nz-form-label 的 [nzXs] 属性,范例: 设置值为6,生成[nzXs]="{pull:6}" + /// + public int LabelXsPull { get; set; } + /// + /// 扩展属性,标签<576px超窄尺寸响应式栅格向右移动的格数,设置 nz-form-label 的 [nzXs] 属性,范例: 设置值为6,生成[nzXs]="{push:6}" + /// + public int LabelXsPush { get; set; } + /// + /// 扩展属性,标签≥576px窄尺寸响应式栅格,设置 nz-form-label 的 [nzSm] 属性,可以设置一个数字,相当于[nzSm]="{span:数字}",也可以设置为对象,范例:{span:5,offset:1} + /// + public string LabelSm { get; set; } + /// + /// 扩展属性,标签≥576px窄尺寸响应式栅格占位格数,设置 nz-form-label 的 [nzSm] 属性,可选值: 0 - 24, 为 0 时隐藏,范例:设置值为6,生成[nzSm]="{span:6}" + /// + public int LabelSmSpan { get; set; } + /// + /// 扩展属性,标签≥576px窄尺寸响应式栅格向右偏移的格数,即栅格左侧的间隔格数,间隔内不可以有栅格,设置 nz-form-label 的 [nzSm] 属性,范例: 设置值为6,将元素向右侧偏移了6格宽度,生成[nzSm]="{offset:6}" + /// + public int LabelSmOffset { get; set; } + /// + /// 扩展属性,标签≥576px窄尺寸响应式栅格顺序,设置 nz-form-label 的 [nzSm] 属性,范例: 设置值为6,生成[nzSm]="{order:6}" + /// + public int LabelSmOrder { get; set; } + /// + /// 扩展属性,标签≥576px窄尺寸响应式栅格向左移动的格数,设置 nz-form-label 的 [nzSm] 属性,范例: 设置值为6,生成[nzSm]="{pull:6}" + /// + public int LabelSmPull { get; set; } + /// + /// 扩展属性,标签≥576px窄尺寸响应式栅格向右移动的格数,设置 nz-form-label 的 [nzSm] 属性,范例: 设置值为6,生成[nzSm]="{push:6}" + /// + public int LabelSmPush { get; set; } + /// + /// 扩展属性,标签≥768px中尺寸响应式栅格,设置 nz-form-label 的 [nzMd] 属性,可以设置一个数字,相当于[nzMd]="{span:数字}",也可以设置为对象,范例:{span:5,offset:1} + /// + public string LabelMd { get; set; } + /// + /// 扩展属性,标签≥768px中尺寸响应式栅格占位格数,设置 nz-form-label 的 [nzMd] 属性,可选值: 0 - 24, 为 0 时隐藏,范例:设置值为6,生成[nzMd]="{span:6}" + /// + public int LabelMdSpan { get; set; } + /// + /// 扩展属性,标签≥768px中尺寸响应式栅格向右偏移的格数,即栅格左侧的间隔格数,间隔内不可以有栅格,设置 nz-form-label 的 [nzMd] 属性,范例: 设置值为6,将元素向右侧偏移了6格宽度,生成[nzMd]="{offset:6}" + /// + public int LabelMdOffset { get; set; } + /// + /// 扩展属性,标签≥768px中尺寸响应式栅格顺序,设置 nz-form-label 的 [nzMd] 属性,范例: 设置值为6,生成[nzMd]="{order:6}" + /// + public int LabelMdOrder { get; set; } + /// + /// 扩展属性,标签≥768px中尺寸响应式栅格向左移动的格数,设置 nz-form-label 的 [nzMd] 属性,范例: 设置值为6,生成[nzMd]="{pull:6}" + /// + public int LabelMdPull { get; set; } + /// + /// 扩展属性,标签≥768px中尺寸响应式栅格向右移动的格数,设置 nz-form-label 的 [nzMd] 属性,范例: 设置值为6,生成[nzMd]="{push:6}" + /// + public int LabelMdPush { get; set; } + /// + /// 扩展属性,标签≥992px宽尺寸响应式栅格,设置 nz-form-label 的 [nzLg] 属性,可以设置一个数字,相当于[nzLg]="{span:数字}",也可以设置为对象,范例:{span:5,offset:1} + /// + public string LabelLg { get; set; } + /// + /// 扩展属性,标签≥992px宽尺寸响应式栅格占位格数,设置 nz-form-label 的 [nzLg] 属性,可选值: 0 - 24, 为 0 时隐藏,范例:设置值为6,生成[nzLg]="{span:6}" + /// + public int LabelLgSpan { get; set; } + /// + /// 扩展属性,标签≥992px宽尺寸响应式栅格向右偏移的格数,即栅格左侧的间隔格数,间隔内不可以有栅格,设置 nz-form-label 的 [nzLg] 属性,范例: 设置值为6,将元素向右侧偏移了6格宽度,生成[nzLg]="{offset:6}" + /// + public int LabelLgOffset { get; set; } + /// + /// 扩展属性,标签≥992px宽尺寸响应式栅格顺序,设置 nz-form-label 的 [nzLg] 属性,范例: 设置值为6,生成[nzLg]="{order:6}" + /// + public int LabelLgOrder { get; set; } + /// + /// 扩展属性,标签≥992px宽尺寸响应式栅格向左移动的格数,设置 nz-form-label 的 [nzLg] 属性,范例: 设置值为6,生成[nzLg]="{pull:6}" + /// + public int LabelLgPull { get; set; } + /// + /// 扩展属性,标签≥992px宽尺寸响应式栅格向右移动的格数,设置 nz-form-label 的 [nzLg] 属性,范例: 设置值为6,生成[nzLg]="{push:6}" + /// + public int LabelLgPush { get; set; } + /// + /// 扩展属性,标签≥1200px超宽尺寸响应式栅格,设置 nz-form-label 的 [nzXl] 属性,可以设置一个数字,相当于[nzXl]="{span:数字}",也可以设置为对象,范例:{span:5,offset:1} + /// + public string LabelXl { get; set; } + /// + /// 扩展属性,标签≥1200px超宽尺寸响应式栅格占位格数,设置 nz-form-label 的 [nzXl] 属性,可选值: 0 - 24, 为 0 时隐藏,范例:设置值为6,生成[nzXl]="{span:6}" + /// + public int LabelXlSpan { get; set; } + /// + /// 扩展属性,标签≥1200px超宽尺寸响应式栅格向右偏移的格数,即栅格左侧的间隔格数,间隔内不可以有栅格,设置 nz-form-label 的 [nzXl] 属性,范例: 设置值为6,将元素向右侧偏移了6格宽度,生成[nzXl]="{offset:6}" + /// + public int LabelXlOffset { get; set; } + /// + /// 扩展属性,标签≥1200px超宽尺寸响应式栅格顺序,设置 nz-form-label 的 [nzXl] 属性,范例: 设置值为6,生成[nzXl]="{order:6}" + /// + public int LabelXlOrder { get; set; } + /// + /// 扩展属性,标签≥1200px超宽尺寸响应式栅格向左移动的格数,设置 nz-form-label 的 [nzXl] 属性,范例: 设置值为6,生成[nzXl]="{pull:6}" + /// + public int LabelXlPull { get; set; } + /// + /// 扩展属性,标签≥1200px超宽尺寸响应式栅格向右移动的格数,设置 nz-form-label 的 [nzXl] 属性,范例: 设置值为6,生成[nzXl]="{push:6}" + /// + public int LabelXlPush { get; set; } + /// + /// 扩展属性,标签≥1600px极宽尺寸响应式栅格,设置 nz-form-label 的 [nzXXl] 属性,可以设置一个数字,相当于[nzXXl]="{span:数字}",也可以设置为对象,范例:{span:5,offset:1} + /// + public string LabelXxl { get; set; } + /// + /// 扩展属性,标签≥1600px极宽尺寸响应式栅格占位格数,设置 nz-form-label 的 [nzXXl] 属性,可选值: 0 - 24, 为 0 时隐藏,范例:设置值为6,生成[nzXXl]="{span:6}" + /// + public int LabelXxlSpan { get; set; } + /// + /// 扩展属性,标签≥1600px极宽尺寸响应式栅格向右偏移的格数,即栅格左侧的间隔格数,间隔内不可以有栅格,设置 nz-form-label 的 [nzXXl] 属性,范例: 设置值为6,将元素向右侧偏移了6格宽度,生成[nzXXl]="{offset:6}" + /// + public int LabelXxlOffset { get; set; } + /// + /// 扩展属性,标签≥1600px极宽尺寸响应式栅格顺序,设置 nz-form-label 的 [nzXXl] 属性,范例: 设置值为6,生成[nzXXl]="{order:6}" + /// + public int LabelXxlOrder { get; set; } + /// + /// 扩展属性,标签≥1600px极宽尺寸响应式栅格向左移动的格数,设置 nz-form-label 的 [nzXXl] 属性,范例: 设置值为6,生成[nzXXl]="{pull:6}" + /// + public int LabelXxlPull { get; set; } + /// + /// 扩展属性,标签≥1600px极宽尺寸响应式栅格向右移动的格数,设置 nz-form-label 的 [nzXXl] 属性,范例: 设置值为6,生成[nzXXl]="{push:6}" + /// + public int LabelXxlPush { get; set; } + /// + /// 扩展属性,控件栅格占位格数,自动创建nz-form-control,nz-form-item容器标签,并设置 nz-form-control 的[nzSpan]属性,可选值: 0 - 24, 为 0 时隐藏 + /// + public string ControlSpan { get; set; } + /// + /// 扩展属性,控件栅格向右偏移的格数,即栅格左侧的间隔格数,间隔内不可以有栅格,自动创建nz-form-control,nz-form-item容器标签,并设置 nz-form-control 的 [nzOffset] 属性,范例: 设置值为4,将标签向右侧偏移4格宽度 + /// + public string ControlOffset { get; set; } + /// + /// 扩展属性,控件栅格顺序,自动创建nz-form-control,nz-form-item容器标签,并设置 nz-form-control 的 [nzOrder] 属性 + /// + public string ControlOrder { get; set; } + /// + /// 扩展属性,控件栅格向左移动的格数,自动创建nz-form-control,nz-form-item容器标签,并设置 nz-form-control 的 [nzPull] 属性 + /// + public string ControlPull { get; set; } + /// + /// 扩展属性,控件栅格向右移动的格数,自动创建nz-form-control,nz-form-item容器标签,并设置 nz-form-control 的 [nzPush] 属性 + /// + public string ControlPush { get; set; } + /// + /// 扩展属性,控件Flex布局,自动创建nz-form-control,nz-form-item容器标签,并设置 nz-form-control 的 [nzFlex] 属性 + /// + public string ControlFlex { get; set; } + /// + /// 扩展属性,控件<576px超窄尺寸响应式栅格,设置 nz-form-control 的 [nzXs] 属性,可以设置一个数字,相当于[nzXs]="{span:数字}",也可以设置为对象,范例:{span:5,offset:1} + /// + public string ControlXs { get; set; } + /// + /// 扩展属性,控件<576px超窄尺寸响应式栅格占位格数,设置 nz-form-control 的 [nzXs] 属性,可选值: 0 - 24, 为 0 时隐藏,范例:设置值为6,生成[nzXs]="{span:6}" + /// + public int ControlXsSpan { get; set; } + /// + /// 扩展属性,控件<576px超窄尺寸响应式栅格向右偏移的格数,即栅格左侧的间隔格数,间隔内不可以有栅格,设置 nz-form-control 的 [nzXs] 属性,范例: 设置值为6,将元素向右侧偏移了6格宽度,生成[nzXs]="{offset:6}" + /// + public int ControlXsOffset { get; set; } + /// + /// 扩展属性,控件<576px超窄尺寸响应式栅格顺序,设置 nz-form-control 的 [nzXs] 属性,范例: 设置值为6,生成[nzXs]="{order:6}" + /// + public int ControlXsOrder { get; set; } + /// + /// 扩展属性,控件<576px超窄尺寸响应式栅格向左移动的格数,设置 nz-form-control 的 [nzXs] 属性,范例: 设置值为6,生成[nzXs]="{pull:6}" + /// + public int ControlXsPull { get; set; } + /// + /// 扩展属性,控件<576px超窄尺寸响应式栅格向右移动的格数,设置 nz-form-control 的 [nzXs] 属性,范例: 设置值为6,生成[nzXs]="{push:6}" + /// + public int ControlXsPush { get; set; } + /// + /// 扩展属性,控件≥576px窄尺寸响应式栅格,设置 nz-form-control 的 [nzSm] 属性,可以设置一个数字,相当于[nzSm]="{span:数字}",也可以设置为对象,范例:{span:5,offset:1} + /// + public string ControlSm { get; set; } + /// + /// 扩展属性,控件≥576px窄尺寸响应式栅格占位格数,设置 nz-form-control 的 [nzSm] 属性,可选值: 0 - 24, 为 0 时隐藏,范例:设置值为6,生成[nzSm]="{span:6}" + /// + public int ControlSmSpan { get; set; } + /// + /// 扩展属性,控件≥576px窄尺寸响应式栅格向右偏移的格数,即栅格左侧的间隔格数,间隔内不可以有栅格,设置 nz-form-control 的 [nzSm] 属性,范例: 设置值为6,将元素向右侧偏移了6格宽度,生成[nzSm]="{offset:6}" + /// + public int ControlSmOffset { get; set; } + /// + /// 扩展属性,控件≥576px窄尺寸响应式栅格顺序,设置 nz-form-control 的 [nzSm] 属性,范例: 设置值为6,生成[nzSm]="{order:6}" + /// + public int ControlSmOrder { get; set; } + /// + /// 扩展属性,控件≥576px窄尺寸响应式栅格向左移动的格数,设置 nz-form-control 的 [nzSm] 属性,范例: 设置值为6,生成[nzSm]="{pull:6}" + /// + public int ControlSmPull { get; set; } + /// + /// 扩展属性,控件≥576px窄尺寸响应式栅格向右移动的格数,设置 nz-form-control 的 [nzSm] 属性,范例: 设置值为6,生成[nzSm]="{push:6}" + /// + public int ControlSmPush { get; set; } + /// + /// 扩展属性,控件≥768px中尺寸响应式栅格,设置 nz-form-control 的 [nzMd] 属性,可以设置一个数字,相当于[nzMd]="{span:数字}",也可以设置为对象,范例:{span:5,offset:1} + /// + public string ControlMd { get; set; } + /// + /// 扩展属性,控件≥768px中尺寸响应式栅格占位格数,设置 nz-form-control 的 [nzMd] 属性,可选值: 0 - 24, 为 0 时隐藏,范例:设置值为6,生成[nzMd]="{span:6}" + /// + public int ControlMdSpan { get; set; } + /// + /// 扩展属性,控件≥768px中尺寸响应式栅格向右偏移的格数,即栅格左侧的间隔格数,间隔内不可以有栅格,设置 nz-form-control 的 [nzMd] 属性,范例: 设置值为6,将元素向右侧偏移了6格宽度,生成[nzMd]="{offset:6}" + /// + public int ControlMdOffset { get; set; } + /// + /// 扩展属性,控件≥768px中尺寸响应式栅格顺序,设置 nz-form-control 的 [nzMd] 属性,范例: 设置值为6,生成[nzMd]="{order:6}" + /// + public int ControlMdOrder { get; set; } + /// + /// 扩展属性,控件≥768px中尺寸响应式栅格向左移动的格数,设置 nz-form-control 的 [nzMd] 属性,范例: 设置值为6,生成[nzMd]="{pull:6}" + /// + public int ControlMdPull { get; set; } + /// + /// 扩展属性,控件≥768px中尺寸响应式栅格向右移动的格数,设置 nz-form-control 的 [nzMd] 属性,范例: 设置值为6,生成[nzMd]="{push:6}" + /// + public int ControlMdPush { get; set; } + /// + /// 扩展属性,控件≥992px宽尺寸响应式栅格,设置 nz-form-control 的 [nzLg] 属性,可以设置一个数字,相当于[nzLg]="{span:数字}",也可以设置为对象,范例:{span:5,offset:1} + /// + public string ControlLg { get; set; } + /// + /// 扩展属性,控件≥992px宽尺寸响应式栅格占位格数,设置 nz-form-control 的 [nzLg] 属性,可选值: 0 - 24, 为 0 时隐藏,范例:设置值为6,生成[nzLg]="{span:6}" + /// + public int ControlLgSpan { get; set; } + /// + /// 扩展属性,控件≥992px宽尺寸响应式栅格向右偏移的格数,即栅格左侧的间隔格数,间隔内不可以有栅格,设置 nz-form-control 的 [nzLg] 属性,范例: 设置值为6,将元素向右侧偏移了6格宽度,生成[nzLg]="{offset:6}" + /// + public int ControlLgOffset { get; set; } + /// + /// 扩展属性,控件≥992px宽尺寸响应式栅格顺序,设置 nz-form-control 的 [nzLg] 属性,范例: 设置值为6,生成[nzLg]="{order:6}" + /// + public int ControlLgOrder { get; set; } + /// + /// 扩展属性,控件≥992px宽尺寸响应式栅格向左移动的格数,设置 nz-form-control 的 [nzLg] 属性,范例: 设置值为6,生成[nzLg]="{pull:6}" + /// + public int ControlLgPull { get; set; } + /// + /// 扩展属性,控件≥992px宽尺寸响应式栅格向右移动的格数,设置 nz-form-control 的 [nzLg] 属性,范例: 设置值为6,生成[nzLg]="{push:6}" + /// + public int ControlLgPush { get; set; } + /// + /// 扩展属性,控件≥1200px超宽尺寸响应式栅格,设置 nz-form-control 的 [nzXl] 属性,可以设置一个数字,相当于[nzXl]="{span:数字}",也可以设置为对象,范例:{span:5,offset:1} + /// + public string ControlXl { get; set; } + /// + /// 扩展属性,控件≥1200px超宽尺寸响应式栅格占位格数,设置 nz-form-control 的 [nzXl] 属性,可选值: 0 - 24, 为 0 时隐藏,范例:设置值为6,生成[nzXl]="{span:6}" + /// + public int ControlXlSpan { get; set; } + /// + /// 扩展属性,控件≥1200px超宽尺寸响应式栅格向右偏移的格数,即栅格左侧的间隔格数,间隔内不可以有栅格,设置 nz-form-control 的 [nzXl] 属性,范例: 设置值为6,将元素向右侧偏移了6格宽度,生成[nzXl]="{offset:6}" + /// + public int ControlXlOffset { get; set; } + /// + /// 扩展属性,控件≥1200px超宽尺寸响应式栅格顺序,设置 nz-form-control 的 [nzXl] 属性,范例: 设置值为6,生成[nzXl]="{order:6}" + /// + public int ControlXlOrder { get; set; } + /// + /// 扩展属性,控件≥1200px超宽尺寸响应式栅格向左移动的格数,设置 nz-form-control 的 [nzXl] 属性,范例: 设置值为6,生成[nzXl]="{pull:6}" + /// + public int ControlXlPull { get; set; } + /// + /// 扩展属性,控件≥1200px超宽尺寸响应式栅格向右移动的格数,设置 nz-form-control 的 [nzXl] 属性,范例: 设置值为6,生成[nzXl]="{push:6}" + /// + public int ControlXlPush { get; set; } + /// + /// 扩展属性,控件≥1600px极宽尺寸响应式栅格,设置 nz-form-control 的 [nzXXl] 属性,可以设置一个数字,相当于[nzXXl]="{span:数字}",也可以设置为对象,范例:{span:5,offset:1} + /// + public string ControlXxl { get; set; } + /// + /// 扩展属性,控件≥1600px极宽尺寸响应式栅格占位格数,设置 nz-form-control 的 [nzXXl] 属性,可选值: 0 - 24, 为 0 时隐藏,范例:设置值为6,生成[nzXXl]="{span:6}" + /// + public int ControlXxlSpan { get; set; } + /// + /// 扩展属性,控件≥1600px极宽尺寸响应式栅格向右偏移的格数,即栅格左侧的间隔格数,间隔内不可以有栅格,设置 nz-form-control 的 [nzXXl] 属性,范例: 设置值为6,将元素向右侧偏移了6格宽度,生成[nzXXl]="{offset:6}" + /// + public int ControlXxlOffset { get; set; } + /// + /// 扩展属性,控件≥1600px极宽尺寸响应式栅格顺序,设置 nz-form-control 的 [nzXXl] 属性,范例: 设置值为6,生成[nzXXl]="{order:6}" + /// + public int ControlXxlOrder { get; set; } + /// + /// 扩展属性,控件≥1600px极宽尺寸响应式栅格向左移动的格数,设置 nz-form-control 的 [nzXXl] 属性,范例: 设置值为6,生成[nzXXl]="{pull:6}" + /// + public int ControlXxlPull { get; set; } + /// + /// 扩展属性,控件≥1600px极宽尺寸响应式栅格向右移动的格数,设置 nz-form-control 的 [nzXXl] 属性,范例: 设置值为6,生成[nzXXl]="{push:6}" + /// + public int ControlXxlPush { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Base/FormControlBuilderBase.cs b/src/Util.Ui.NgZorro/Components/Base/FormControlBuilderBase.cs new file mode 100644 index 000000000..0c1748ab2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Base/FormControlBuilderBase.cs @@ -0,0 +1,210 @@ +using Microsoft.AspNetCore.Mvc.Rendering; +using Util.Helpers; +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Extensions; +using Config = Util.Ui.Configs.Config; + +namespace Util.Ui.NgZorro.Components.Base; + +/// +/// 表单控件标签生成器基类 +/// +public abstract class FormControlBuilderBase : AngularTagBuilder where TBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表单项共享配置 + /// + protected readonly FormItemShareConfig FormItemShareConfig; + + /// + /// 初始化表单控件标签生成器 + /// + /// 配置 + /// 标签名称,范例:div + /// 渲染模式 + protected FormControlBuilderBase( Config config, string tagName, TagRenderMode renderMode = TagRenderMode.Normal ) : base( config, tagName, renderMode ) { + _config = config; + FormItemShareConfig = GetShareConfig(); + } + + /// + /// 获取表单项共享配置 + /// + private FormItemShareConfig GetShareConfig() { + return _config.GetValueFromItems() ?? new FormItemShareConfig(); + } + + /// + /// 配置名称 + /// + public virtual TBuilder Name() { + var bindName = _config.GetValue( AngularConst.BindName ); + if ( bindName.IsEmpty() == false ) { + Attribute( "[name]", bindName ); + return (TBuilder)this; + } + Name( _config.GetValue( UiConst.Name ) ); + return (TBuilder)this; + } + + /// + /// 配置名称 + /// + public TBuilder Name( string value ) { + AttributeIfNotEmpty( "name", value ); + return (TBuilder)this; + } + + /// + /// 配置模型绑定 + /// + public virtual TBuilder NgModel() { + this.NgModel( _config ); + return (TBuilder)this; + } + + /// + /// 配置表单控件 + /// + public TBuilder FormControl() { + AttributeIfNotEmpty( "[formControl]", _config.GetValue( UiConst.FormControl ) ); + AttributeIfNotEmpty( "formControlName", _config.GetValue( UiConst.FormControlName ) ); + AttributeIfNotEmpty( "[formControlName]", _config.GetValue( AngularConst.BindFormControlName ) ); + return (TBuilder)this; + } + + /// + /// 配置模型变更事件 + /// + public virtual TBuilder OnModelChange() { + AttributeIfNotEmpty( "(ngModelChange)", _config.GetValue( UiConst.OnModelChange ) ); + return (TBuilder)this; + } + + /// + /// 配置必填项验证 + /// + public virtual TBuilder Required() { + AttributeIfNotEmpty( "[required]", _config.GetValue( UiConst.Required ) ); + return (TBuilder)this; + } + + /// + /// 配置必填项消息 + /// + public virtual TBuilder RequiredMessage() { + AttributeIfNotEmpty( "requiredMessage", _config.GetValue( UiConst.RequiredMessage ) ); + AttributeIfNotEmpty( "[requiredMessage]", _config.GetValue( AngularConst.BindRequiredMessage ) ); + return (TBuilder)this; + } + + /// + /// 配置最小长度验证 + /// + public virtual TBuilder MinLength() { + AttributeIfNotEmpty( "[minlength]", _config.GetValue( UiConst.MinLength ) ); + return (TBuilder)this; + } + + /// + /// 配置最小长度验证消息 + /// + public virtual TBuilder MinLengthMessage() { + AttributeIfNotEmpty( "minLengthMessage", _config.GetValue( UiConst.MinLengthMessage ) ); + AttributeIfNotEmpty( "[minLengthMessage]", _config.GetValue( AngularConst.BindMinLengthMessage ) ); + return (TBuilder)this; + } + + /// + /// 配置最大长度验证 + /// + public virtual TBuilder MaxLength() { + AttributeIfNotEmpty( "[maxlength]", _config.GetValue( UiConst.MaxLength ) ); + return (TBuilder)this; + } + + /// + /// 配置电子邮件验证消息 + /// + public virtual TBuilder EmailMessage() { + AttributeIfNotEmpty( "emailMessage", _config.GetValue( UiConst.EmailMessage ) ); + AttributeIfNotEmpty( "[emailMessage]", _config.GetValue( AngularConst.BindEmailMessage ) ); + return (TBuilder)this; + } + + /// + /// 配置正则表达式验证 + /// + public virtual TBuilder Pattern() { + AttributeIfNotEmpty( "pattern", _config.GetValue( UiConst.Pattern ) ); + AttributeIfNotEmpty( "[pattern]", _config.GetValue( AngularConst.BindPattern ) ); + AttributeIfNotEmpty( "patternMessage", _config.GetValue( UiConst.PatternMessage ) ); + AttributeIfNotEmpty( "[patternMessage]", _config.GetValue( AngularConst.BindPatternMessage ) ); + return (TBuilder)this; + } + + /// + /// 配置间距项 + /// + public virtual TBuilder SpaceItem() { + if ( FormItemShareConfig.FormItemCreated ) + return (TBuilder)this; + this.SpaceItem( FormItemShareConfig.SpaceItem ); + return (TBuilder)this; + } + + /// + /// 配置表单属性 + /// + protected TBuilder ConfigForm() { + return NgModel().FormControl().OnModelChange() + .Required().RequiredMessage() + .MinLength().MinLengthMessage() + .MaxLength().EmailMessage() + .Pattern().SpaceItem() + .TableEdit().ValidationExtend(); + } + + /// + /// 配置表格编辑 + /// + protected TBuilder TableEdit() { + var config = GetTableColumnShareConfig(); + if ( config == null || config.IsEnableEdit == false ) + return (TBuilder)this; + var id = _config.GetValue( UiConst.Id ); + if ( id.IsEmpty() ) { + id = $"c_{Id.Create()}"; + this.Id( id ); + } + Attribute( "[x-edit-control]", id ); + Attribute( "[editRow]", config.RowId ); + return (TBuilder)this; + } + + /// + /// 获取表格列共享配置 + /// + public TableColumnShareConfig GetTableColumnShareConfig() { + return _config.GetValueFromItems(); + } + + /// + /// 配置验证扩展 + /// + public TBuilder ValidationExtend() { + if ( FormItemShareConfig.IsValidationExtend ) { + Attribute( "x-validation-extend" ); + Attribute( $"#{FormItemShareConfig.ValidationExtendId}", "xValidationExtend" ); + AttributeIfNotEmpty( "displayName", FormItemShareConfig.LabelText ); + } + return (TBuilder)this; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Base/FormControlContainerTagHelperBase.cs b/src/Util.Ui.NgZorro/Components/Base/FormControlContainerTagHelperBase.cs new file mode 100644 index 000000000..41fd9b6bf --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Base/FormControlContainerTagHelperBase.cs @@ -0,0 +1,47 @@ +namespace Util.Ui.NgZorro.Components.Base; + +/// +/// 表单控件容器基类 +/// +public abstract class FormControlContainerTagHelperBase : FormContainerTagHelperBase { + /// + /// 扩展属性, 标签文本,自动创建nz-form-label,nz-form-control,nz-form-item容器标签,并设置nz-form-label的内容,支持i18n + /// + public string LabelText { get; set; } + /// + /// 扩展属性, 标签文本,自动创建nz-form-label,nz-form-control,nz-form-item容器标签,并设置nz-form-label的内容 + /// + public string BindLabelText { get; set; } + /// + /// 扩展属性, 标签宽度 + /// + public string LabelWidth { get; set; } + /// + /// 扩展属性, 是否显示标签 nz-form-label + /// + public bool ShowLabel { get; set; } + /// + /// 显示额外提示信息,自动创建nz-form-control,nz-form-item容器标签,并设置nz-form-control的 nzExtra 属性 + /// + public string Extra { get; set; } + /// + /// 显示额外提示信息,自动创建nz-form-control,nz-form-item容器标签,并设置nz-form-control的 [nzExtra] 属性 + /// + public string BindExtra { get; set; } + /// + /// 校验状态为成功时的提示信息,自动创建nz-form-control,nz-form-item容器标签,并设置nz-form-control的 nzSuccessTip 属性 + /// + public string SuccessTip { get; set; } + /// + /// 校验状态为警告时的提示信息,自动创建nz-form-control,nz-form-item容器标签,并设置nz-form-control的 nzWarningTip 属性 + /// + public string WarningTip { get; set; } + /// + /// 校验状态为错误时的提示信息,自动创建nz-form-control,nz-form-item容器标签,并设置nz-form-control的 nzErrorTip 属性 + /// + public string ErrorTip { get; set; } + /// + /// 正在校验时的提示信息,自动创建nz-form-control,nz-form-item容器标签,并设置nz-form-control的 nzValidatingTip 属性 + /// + public string ValidatingTip { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Base/FormControlRenderBase.cs b/src/Util.Ui.NgZorro/Components/Base/FormControlRenderBase.cs new file mode 100644 index 000000000..0ca8d94e5 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Base/FormControlRenderBase.cs @@ -0,0 +1,177 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Forms.Builders; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Components.Templates.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Extensions; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Base; + +/// +/// 表单控件渲染器基类 +/// +public abstract class FormControlRenderBase : IRender { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表单项共享配置 + /// + protected readonly FormItemShareConfig ShareConfig; + /// + /// 标识 + /// + private string _id; + + /// + /// 初始化表单控件渲染器基类 + /// + /// 配置 + protected FormControlRenderBase( Config config ) { + _config = config; + ShareConfig = GetFormItemShareConfig(); + } + + /// + /// 获取表单项共享配置 + /// + protected FormItemShareConfig GetFormItemShareConfig() { + return _config.GetValueFromItems() ?? new FormItemShareConfig(); + } + + /// + /// 获取标识 + /// + protected virtual string GetId() { + if ( _id.IsEmpty() == false ) + return _id; + _id = _config.GetValue( UiConst.Id ); + if ( _id.IsEmpty() == false ) + return _id; + return _id = Util.Helpers.Id.Create(); + } + + /// + /// 渲染 + /// + /// 流写入器 + /// 编码 + public void WriteTo( TextWriter writer, HtmlEncoder encoder ) { + Init(); + var formItem = GetFormItem(); + var formLabel = GetFormLabel(); + var formControl = GetFormControl(); + formItem.AppendContent( formLabel ).AppendContent( formControl ); + formItem.WriteTo( writer, encoder ); + } + + /// + /// 初始化 + /// + protected virtual void Init() { + } + + /// + /// 获取表单项 + /// + protected virtual TagBuilder GetFormItem() { + TagBuilder builder = new EmptyContainerTagBuilder(); + if (_config.Id == ShareConfig.Id && ShareConfig.AutoCreateFormItem == true) { + builder = new FormItemBuilder(_config.CopyRemoveAttributes()); + ShareConfig.FormItemCreated = true; + } + builder.Config(); + return builder; + } + + /// + /// 获取表单标签 + /// + protected virtual TagBuilder GetFormLabel() { + if ( _config.Id == ShareConfig.Id && ShareConfig.AutoCreateFormLabel == true ) { + var builder = new FormLabelBuilder( GetFormLabelConfig() ); + builder.Config(); + SetLabelText( builder ); + SetBindLabelText( builder ); + return builder; + } + return new EmptyContainerTagBuilder(); + } + + /// + /// 获取表单标签配置 + /// + protected Config GetFormLabelConfig() { + var result = _config.CopyRemoveAttributes(); + result.Content = null; + return result; + } + + /// + /// 设置表单标签文本 + /// + protected virtual void SetLabelText( FormLabelBuilder builder ) { + if ( ShareConfig.LabelText.IsEmpty() ) + return; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + builder.AppendContentByI18n( ShareConfig.LabelText ); + return; + } + builder.SetContent( ShareConfig.LabelText ); + } + + /// + /// 设置表单标签文本 + /// + protected virtual void SetBindLabelText( FormLabelBuilder builder ) { + if ( ShareConfig.BindLabelText.IsEmpty() ) + return; + builder.SetContent( "{{" + ShareConfig.BindLabelText + "}}" ); + } + + /// + /// 获取表单控件 + /// + protected virtual TagBuilder GetFormControl() { + TagBuilder builder = new EmptyContainerTagBuilder(); + if ( _config.Id == ShareConfig.Id && ShareConfig.AutoCreateFormControl == true ) + builder = new FormControlBuilder( _config.CopyRemoveAttributes() ); + builder.Config(); + AppendControl( builder ); + if ( IsAppendValidationTempalte() ) + AppendValidationTempalte( builder ); + return builder; + } + + /// + /// 添加表单控件 + /// + protected virtual void AppendControl( TagBuilder formControlBuilder ) { + } + + /// + /// 是否添加验证模板 + /// + protected virtual bool IsAppendValidationTempalte() { + return true; + } + + /// + /// 添加验证模板 + /// + protected virtual void AppendValidationTempalte( TagBuilder formControlBuilder ) { + if ( ShareConfig.ValidationTempalteId.IsEmpty() ) + return; + var templateBuilder = new TemplateBuilder( _config.CopyRemoveAttributes() ); + templateBuilder.Id( ShareConfig.ValidationTempalteId ); + templateBuilder.SetContent( $"{{{{{ShareConfig.ValidationExtendId}.getErrorMessage()}}}}" ); + formControlBuilder.AppendContent( templateBuilder ); + } + + /// + public abstract IHtmlContent Clone(); +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Base/FormControlTagHelperBase.cs b/src/Util.Ui.NgZorro/Components/Base/FormControlTagHelperBase.cs new file mode 100644 index 000000000..a1b563c22 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Base/FormControlTagHelperBase.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; + +namespace Util.Ui.NgZorro.Components.Base; + +/// +/// 表单控件基类 +/// +public abstract class FormControlTagHelperBase : FormControlContainerTagHelperBase { + /// + /// 属性表达式 + /// + public ModelExpression For { get; set; } + /// + /// [(ngModel)],模型双向绑定 + /// + public string NgModel { get; set; } + /// + /// [ngModel],模型绑定 + /// + public string BindNgModel { get; set; } + /// + /// [formControl],表单控件实例 + /// + public string FormControl { get; set; } + /// + /// formControlName,表单控件名 + /// + public string FormControlName { get; set; } + /// + /// [formControlName],表单控件名 + /// + public string BindFormControlName { get; set; } + /// + /// (ngModelChange),模型变化事件 + /// + public string OnModelChange { get; set; } + /// + /// name,名称 + /// + public string Name { get; set; } + /// + /// [name],名称 + /// + public string BindName { get; set; } + /// + /// *nzSpaceItem,值为 true 时设置为间距项,放入 <util-space> 中使用 + /// + public bool SpaceItem { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Base/RowBuilderBase.cs b/src/Util.Ui.NgZorro/Components/Base/RowBuilderBase.cs new file mode 100644 index 000000000..f337d1004 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Base/RowBuilderBase.cs @@ -0,0 +1,98 @@ +using Microsoft.AspNetCore.Mvc.Rendering; +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Base; + +/// +/// 栅格行标签生成器基类 +/// +public abstract class RowBuilderBase : AngularTagBuilder where TBuilder : RowBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化栅格行标签生成器基类 + /// + /// 配置 + /// 标签名称,范例:div + /// 渲染模式 + protected RowBuilderBase( Config config, string tagName, TagRenderMode renderMode = TagRenderMode.Normal ) : base( config,tagName, renderMode ) { + _config = config; + } + + /// + /// 配置对齐 + /// + public virtual TBuilder Align() { + Align( _config.GetValue( UiConst.Align ) ); + BindAlign( _config.GetValue( AngularConst.BindAlign ) ); + return (TBuilder)this; + } + + /// + /// 配置对齐 + /// + /// 对齐方式 + protected void Align( Align? align ) { + AttributeIfNotEmpty( "nzAlign", align?.Description(),true ); + } + + /// + /// 配置对齐 + /// + /// 对齐方式 + protected void BindAlign( string align ) { + AttributeIfNotEmpty( "[nzAlign]", align, true ); + } + + /// + /// 配置间隔 + /// + public virtual TBuilder Gutter() { + Gutter( _config.GetValue( UiConst.Gutter ) ); + return (TBuilder)this; + } + + /// + /// 配置间隔 + /// + protected void Gutter( string gutter ) { + AttributeIfNotEmpty( "[nzGutter]", gutter, true ); + } + + /// + /// 配置水平排列方式 + /// + public virtual TBuilder Justify() { + Justify( _config.GetValue( UiConst.Justify ) ); + BindJustify( _config.GetValue( AngularConst.BindJustify ) ); + return (TBuilder)this; + } + + /// + /// 配置水平排列方式 + /// + /// 排列方式 + protected void Justify( Justify? justify ) { + AttributeIfNotEmpty( "nzJustify", justify?.Description(), true ); + } + + /// + /// 配置水平排列方式 + /// + /// 排列方式 + protected void BindJustify( string justify ) { + AttributeIfNotEmpty( "[nzJustify]", justify, true ); + } + + /// + /// 配置栅格行 + /// + protected TBuilder ConfigRow() { + return Align().Gutter().Justify(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Base/RowTagHelperBase.cs b/src/Util.Ui.NgZorro/Components/Base/RowTagHelperBase.cs new file mode 100644 index 000000000..0be202a5b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Base/RowTagHelperBase.cs @@ -0,0 +1,30 @@ +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Base; + +/// +/// 栅格行基类 +/// +public abstract class RowTagHelperBase : AngularTagHelperBase { + /// + /// nzAlign,垂直对齐方式 + /// + public Align Align { get; set; } + /// + /// [nzAlign],垂直对齐方式,可选值: 'top' | 'middle' | 'bottom' + /// + public string BindAlign { get; set; } + /// + /// [nzGutter],栅格间隔,可以是数字,也可以是响应式写法,范例:{ xs: 8, sm: 16, md: 24, lg: 32, xl: 32, xxl: 32 }。或者使用数组形式同时设置 [水平间距, 垂直间距],范例:[16, 24]。取值推荐使用16+8n,n是自然数,单位为像素。 + /// + public string Gutter { get; set; } + /// + /// nzJustify,水平排列方式 + /// + public Justify Justify { get; set; } + /// + /// [nzJustify],水平排列方式,可选值: 'start' | 'end' | 'center' | 'space-around' | 'space-between' + /// + public string BindJustify { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Base/TooltipTagHelperBase.cs b/src/Util.Ui.NgZorro/Components/Base/TooltipTagHelperBase.cs new file mode 100644 index 000000000..a1ab55197 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Base/TooltipTagHelperBase.cs @@ -0,0 +1,314 @@ +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Base; + +/// +/// 文字提示基类 +/// +public abstract class TooltipTagHelperBase : AngularTagHelperBase { + /// + /// nzTooltipTitle,提示文字,支持多语言 + /// + public string TooltipTitle { get; set; } + /// + /// [nzTooltipTitle],提示文字,类型: string | TemplateRef<void> + /// + public string BindTooltipTitle { get; set; } + /// + /// 扩展属性,nzTooltipTitle是否显示update文本,默认文本为'Update',i18n文本为'util.update' + /// + public bool TooltipTitleUpdate { get; set; } + /// + /// 扩展属性,nzTooltipTitle是否显示delete文本,默认文本为'Delete',i18n文本为'util.delete' + /// + public bool TooltipTitleDelete { get; set; } + /// + /// 扩展属性,nzTooltipTitle是否显示detail文本,默认文本为'Detail',i18n文本为'util.detail' + /// + public bool TooltipTitleDetail { get; set; } + /// + /// nzTooltipPlacement,提示框位置,类型: 'top' | 'left' | 'right' | 'bottom' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom' | Array<string>,默认值: 'top' + /// + public TooltipPlacement TooltipPlacement { get; set; } + /// + /// [nzTooltipPlacement],提示框位置,类型: 'top' | 'left' | 'right' | 'bottom' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom' | Array<string>,默认值: 'top' + /// + public string BindTooltipPlacement { get; set; } + /// + /// [nzTooltipArrowPointAtCenter],箭头是否指向锚点的中心, 类型: boolean, 默认值: false + /// + public string TooltipArrowPointAtCenter { get; set; } + /// + /// [nzTooltipTitleContext],提示文字模板上下文, 类型: object, 范例: { $implicit: 'value' } + /// + public string TooltipTitleContext { get; set; } + /// + /// nzTooltipTrigger,文字提示触发行为,可选值: 'click' | 'focus' | 'hover' | null,为 null 时不响应光标事件,默认值: 'hover' + /// + public TooltipTrigger TooltipTrigger { get; set; } + /// + /// [nzTooltipTrigger],文字提示触发行为,可选值: 'click' | 'focus' | 'hover' | null,为 null 时不响应光标事件,默认值: 'hover' + /// + public string BindTooltipTrigger { get; set; } + /// + /// nzTooltipColor,扩展属性, 文字提示背景颜色 + /// + public AntDesignColor TooltipColorType { get; set; } + /// + /// nzTooltipColor,文字提示背景颜色 + /// + public string TooltipColor { get; set; } + /// + /// [nzTooltipColor],文字提示背景颜色 + /// + public string BindTooltipColor { get; set; } + /// + /// [nzTooltipOrigin],文字提示定位元素,类型: ElementRef + /// + public string TooltipOrigin { get; set; } + /// + /// [nzTooltipVisible],提示框是否可见,默认值: false + /// + public string TooltipVisible { get; set; } + /// + /// [(nzTooltipVisible)],提示框是否可见,默认值: false + /// + public string BindonTooltipVisible { get; set; } + /// + /// [nzTooltipMouseEnterDelay],鼠标移入后延时多久才显示提示框,单位:秒,默认值: 0.15 + /// + public string TooltipMouseEnterDelay { get; set; } + /// + /// [nzTooltipMouseLeaveDelay],鼠标移出后延时多久才隐藏提示框,单位:秒,默认值: 0.1 + /// + public string TooltipMouseLeaveDelay { get; set; } + /// + /// nzTooltipOverlayClassName,提示框样式类名 + /// + public string TooltipOverlayClassName { get; set; } + /// + /// [nzTooltipOverlayClassName],提示框样式类名 + /// + public string BindTooltipOverlayClassName { get; set; } + /// + /// [nzTooltipOverlayStyle],提示框样式,类型: object + /// + public string TooltipOverlayStyle { get; set; } + /// + /// (nzTooltipVisibleChange),提示框显示状态变化事件,类型: EventEmitter<boolean> + /// + public string OnTooltipVisibleChange { get; set; } + /// + /// nzPopoverTitle,气泡卡片标题,支持多语言 + /// + public string PopoverTitle { get; set; } + /// + /// [nzPopoverTitle],气泡卡片标题,类型: string | TemplateRef<void> + /// + public string BindPopoverTitle { get; set; } + /// + /// nzPopoverContent,气泡卡片内容 + /// + public string PopoverContent { get; set; } + /// + /// [nzPopoverContent],气泡卡片内容,类型: string | TemplateRef<void> + /// + public string BindPopoverContent { get; set; } + /// + /// nzPopoverPlacement,气泡卡片位置,类型: 'top' | 'left' | 'right' | 'bottom' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom' | Array<string>,默认值: 'top' + /// + public PopoverPlacement PopoverPlacement { get; set; } + /// + /// [nzPopoverPlacement],气泡卡片位置,类型: 'top' | 'left' | 'right' | 'bottom' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom' | Array<string>,默认值: 'top' + /// + public string BindPopoverPlacement { get; set; } + /// + /// nzPopoverTrigger,气泡卡片触发行为,为 null 时不响应光标事件,可选值: 'click' | 'focus' | 'hover' | null,默认值: 'hover' + /// + public PopoverTrigger PopoverTrigger { get; set; } + /// + /// [nzPopoverTrigger],气泡卡片触发行为,为 null 时不响应光标事件,可选值: 'click' | 'focus' | 'hover' | null,默认值: 'hover' + /// + public string BindPopoverTrigger { get; set; } + /// + /// [nzPopoverVisible],气泡卡片是否可见,默认值: false + /// + public string PopoverVisible { get; set; } + /// + /// [(nzPopoverVisible)],气泡卡片是否可见,默认值: false + /// + public string BindonPopoverVisible { get; set; } + /// + /// [nzPopoverArrowPointAtCenter],箭头是否指向锚点的中心, 类型: boolean, 默认值: false + /// + public string PopoverArrowPointAtCenter { get; set; } + /// + /// [nzPopoverOrigin],气泡卡片定位元素,类型: ElementRef + /// + public string PopoverOrigin { get; set; } + /// + /// [nzPopoverMouseEnterDelay],鼠标移入后延时多久才显示气泡卡片,单位:秒,默认值: 0.15 + /// + public string PopoverMouseEnterDelay { get; set; } + /// + /// [nzPopoverMouseLeaveDelay],鼠标移出后延时多久才隐藏气泡卡片,单位:秒,默认值: 0.1 + /// + public string PopoverMouseLeaveDelay { get; set; } + /// + /// nzPopoverOverlayClassName,气泡卡片样式类名 + /// + public string PopoverOverlayClassName { get; set; } + /// + /// [nzPopoverOverlayClassName],气泡卡片样式类名 + /// + public string BindPopoverOverlayClassName { get; set; } + /// + /// [nzPopoverOverlayStyle],气泡卡片样式,类型: object + /// + public string PopoverOverlayStyle { get; set; } + /// + /// [nzPopoverBackdrop],气泡卡片浮层是否带背景,默认值: false + /// + public string PopoverBackdrop { get; set; } + /// + /// (nzPopoverVisibleChange),气泡卡片显示状态变化事件,类型: EventEmitter<boolean> + /// + public string OnPopoverVisibleChange { get; set; } + /// + /// nzPopconfirmTitle,气泡确认框标题,支持多语言 + /// + public string PopconfirmTitle { get; set; } + /// + /// [nzPopconfirmTitle],气泡确认框标题,类型: string | TemplateRef<void> + /// + public string BindPopconfirmTitle { get; set; } + /// + /// nzPopconfirmTrigger,气泡确认框触发行为,可选值: 'click' | 'focus' | 'hover' | null,为 null 时不响应光标事件,默认值: 'hover' + /// + public PopconfirmTrigger PopconfirmTrigger { get; set; } + /// + /// [nzPopconfirmTrigger],气泡确认框触发行为,可选值: 'click' | 'focus' | 'hover' | null,为 null 时不响应光标事件,默认值: 'hover' + /// + public string BindPopconfirmTrigger { get; set; } + /// + /// nzPopconfirmPlacement,气泡确认框位置,类型: 'top' | 'left' | 'right' | 'bottom' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom' | Array<string>,默认值: 'top' + /// + public PopconfirmPlacement PopconfirmPlacement { get; set; } + /// + /// [nzPopconfirmPlacement],气泡确认框位置,类型: 'top' | 'left' | 'right' | 'bottom' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom' | Array<string>,默认值: 'top' + /// + public string BindPopconfirmPlacement { get; set; } + /// + /// [nzPopconfirmOrigin],气泡确认框定位元素,类型: ElementRef + /// + public string PopconfirmOrigin { get; set; } + /// + /// [nzPopconfirmVisible],气泡确认框是否可见,默认值: false + /// + public string PopconfirmVisible { get; set; } + /// + /// [(nzPopconfirmVisible)],气泡确认框是否可见,默认值: false + /// + public string BindonPopconfirmVisible { get; set; } + /// + /// [nzPopconfirmShowArrow],气泡确认框是否显示箭头,默认值: true + /// + public string PopconfirmShowArrow { get; set; } + /// + /// [nzPopconfirmArrowPointAtCenter],箭头是否指向锚点的中心,默认值: false + /// + public string PopconfirmArrowPointAtCenter { get; set; } + /// + /// [nzPopconfirmMouseEnterDelay],鼠标移入后延时多久才显示气泡确认框,单位:秒,默认值: 0.15 + /// + public string PopconfirmMouseEnterDelay { get; set; } + /// + /// [nzPopconfirmMouseLeaveDelay],鼠标移出后延时多久才隐藏气泡确认框,单位:秒,默认值: 0.1 + /// + public string PopconfirmMouseLeaveDelay { get; set; } + /// + /// nzPopconfirmOverlayClassName,气泡确认框样式类名 + /// + public string PopconfirmOverlayClassName { get; set; } + /// + /// [nzPopconfirmOverlayClassName],气泡确认框样式类名 + /// + public string BindPopconfirmOverlayClassName { get; set; } + /// + /// [nzPopconfirmOverlayStyle],气泡确认框样式,类型: object + /// + public string PopconfirmOverlayStyle { get; set; } + /// + /// [nzPopconfirmBackdrop],气泡确认框浮层是否应带背景,默认值: false + /// + public string PopconfirmBackdrop { get; set; } + /// + /// nzCancelText,气泡确认框取消按钮文字,默认值: '取消' + /// + public string PopconfirmCancelText { get; set; } + /// + /// [nzCancelText],气泡确认框取消按钮文字,默认值: '取消' + /// + public string BindPopconfirmCancelText { get; set; } + /// + /// nzOkText,气泡确认框确认按钮文字,默认值: '确定' + /// + public string PopconfirmOkText { get; set; } + /// + /// [nzOkText],气泡确认框确认按钮文字,默认值: '确定' + /// + public string BindPopconfirmOkText { get; set; } + /// + /// nzOkType,气泡确认框确认按钮类型,默认值: 'primary' + /// + public ButtonType PopconfirmOkType { get; set; } + /// + /// [nzOkType],气泡确认框确认按钮类型,默认值: 'primary' + /// + public string BindPopconfirmOkType { get; set; } + /// + /// [nzOkDanger],气泡确认框确认按钮是否为危险按钮,类型: boolean, 默认值: false + /// + public string PopconfirmOkDanger { get; set; } + /// + /// [nzOkDisabled],气泡确认框是否禁用确认按钮,类型: boolean, 默认值: false + /// + public string PopconfirmOkDisabled { get; set; } + /// + /// nzAutoFocus,气泡确认框按钮自动聚焦,类型: null | 'ok' | 'cancel',默认值: null + /// + public PopconfirmAutoFocus PopconfirmAutoFocus { get; set; } + /// + /// [nzAutoFocus],气泡确认框按钮自动聚焦,类型: null | 'ok' | 'cancel',默认值: null + /// + public string BindPopconfirmAutoFocus { get; set; } + /// + /// [nzCondition],是否直接触发 `on-popconfirm-confirm` 操作而不弹出气泡框,默认值: false + /// + public string PopconfirmCondition { get; set; } + /// + /// nzIcon,气泡确认框自定义图标,类型: string | TemplateRef<void> + /// + public AntDesignIcon PopconfirmIcon { get; set; } + /// + /// [nzIcon],气泡确认框自定义图标,类型: string | TemplateRef<void> + /// + public string BindPopconfirmIcon { get; set; } + /// + /// [nzBeforeConfirm],确认前操作钩子,返回 false 中止执行 on-popconfirm-confirm 操作,支持异步,类型: (() => Observable<boolean> | Promise<boolean> | boolean) | null,默认值: null + /// + public string PopconfirmBeforeConfirm { get; set; } + /// + /// (nzPopconfirmVisibleChange),气泡确认框显示状态变化事件,类型: EventEmitter<boolean> + /// + public string OnPopconfirmVisibleChange { get; set; } + /// + /// (nzOnCancel),气泡确认框取消事件,点击取消按钮时触发,类型: EventEmitter<void> + /// + public string OnPopconfirmCancel { get; set; } + /// + /// (nzOnConfirm),气泡确认框确认事件,点击确认按钮时触发,类型: EventEmitter<void> + /// + public string OnPopconfirmConfirm { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Breadcrumbs/BreadcrumbItemTagHelper.cs b/src/Util.Ui.NgZorro/Components/Breadcrumbs/BreadcrumbItemTagHelper.cs new file mode 100644 index 000000000..d059c30bf --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Breadcrumbs/BreadcrumbItemTagHelper.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Breadcrumbs.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Breadcrumbs; + +/// +/// 面包屑子项,生成的标签为<nz-breadcrumb-item></nz-breadcrumb-item>,放在<util-breadcrumb></util-breadcrumb>中使用 +/// +[HtmlTargetElement( "util-breadcrumb-item" )] +public class BreadcrumbItemTagHelper : AngularTagHelperBase { + /// + /// [nzOverlay],用于支持下拉菜单 + /// + public string Overlay { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new BreadcrumbItemRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Breadcrumbs/BreadcrumbSeparatorTagHelper.cs b/src/Util.Ui.NgZorro/Components/Breadcrumbs/BreadcrumbSeparatorTagHelper.cs new file mode 100644 index 000000000..1bebfbd5e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Breadcrumbs/BreadcrumbSeparatorTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Breadcrumbs.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Breadcrumbs; + +/// +/// 面包屑分隔符,生成的标签为<nz-breadcrumb-separator></nz-breadcrumb-separator>,放在<util-breadcrumb></util-breadcrumb>中使用 +/// +[HtmlTargetElement( "util-breadcrumb-separator" )] +public class BreadcrumbSeparatorTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new BreadcrumbSeparatorRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Breadcrumbs/BreadcrumbTagHelper.cs b/src/Util.Ui.NgZorro/Components/Breadcrumbs/BreadcrumbTagHelper.cs new file mode 100644 index 000000000..b1af50c8f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Breadcrumbs/BreadcrumbTagHelper.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.BreadCrumbs.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Breadcrumbs; + +/// +/// 面包屑,生成的标签为<nz-breadcrumb></nz-breadcrumb> +/// +[HtmlTargetElement( "util-breadcrumb" )] +public class BreadcrumbTagHelper : AngularTagHelperBase { + /// + /// nzSeparator,分隔符,默认值: '/' + /// + public string Separator { get; set; } + /// + /// [nzSeparator],分隔符,设置为null隐藏分隔符, 默认值: '/' + /// + public string BindSeparator { get; set; } + /// + /// [nzAutoGenerate],通过配置 router.data 自动生成面包屑 + /// + public string AutoGenerate { get; set; } + /// + /// nzRouteLabel,自定义 route data 属性名称, nzAutoGenerate 为 true 时生效,默认值: 'breadcrumb' + /// + public string RouteLabel { get; set; } + /// + /// [nzRouteLabel],自定义 route data 属性名称, nzAutoGenerate 为 true 时生效,默认值: 'breadcrumb' + /// + public string BindRouteLabel { get; set; } + /// + /// [nzRouteLabelFn],格式化面包屑导航项的显示文字,通常用于在国际化应用中翻译键值, nzAutoGenerate 为 true 时生效 + /// + public string RouteLabelFn { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new BreadcrumbRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Breadcrumbs/Builders/BreadcrumbBuilder.cs b/src/Util.Ui.NgZorro/Components/Breadcrumbs/Builders/BreadcrumbBuilder.cs new file mode 100644 index 000000000..90c098056 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Breadcrumbs/Builders/BreadcrumbBuilder.cs @@ -0,0 +1,69 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.PageHeaders.Configs; +using Util.Ui.NgZorro.Configs; + +namespace Util.Ui.NgZorro.Components.BreadCrumbs.Builders; + +/// +/// 面包屑标签生成器 +/// +public class BreadcrumbBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化面包屑标签生成器 + /// + /// 配置 + public BreadcrumbBuilder( Config config ) : base( config,"nz-breadcrumb" ) { + _config = config; + } + + /// + /// 配置分隔符 + /// + public BreadcrumbBuilder Separator() { + AttributeIfNotEmpty( "nzSeparator", _config.GetValue( UiConst.Separator ) ); + AttributeIfNotEmpty( "[nzSeparator]", _config.GetValue( AngularConst.BindSeparator ) ); + return this; + } + + /// + /// 配置自动生成 + /// + public BreadcrumbBuilder AutoGenerate() { + AttributeIfNotEmpty( "[nzAutoGenerate]", _config.GetValue( UiConst.AutoGenerate ) ); + return this; + } + + /// + /// 配置路由属性名 + /// + public BreadcrumbBuilder RouteLabel() { + AttributeIfNotEmpty( "nzRouteLabel", _config.GetValue( UiConst.RouteLabel ) ); + AttributeIfNotEmpty( "[nzRouteLabel]", _config.GetValue( AngularConst.BindRouteLabel ) ); + AttributeIfNotEmpty( "[nzRouteLabelFn]", _config.GetValue( UiConst.RouteLabelFn ) ); + return this; + } + + /// + /// 配置页头面包屑 + /// + public BreadcrumbBuilder PageHeader() { + var shareConfig = _config.GetValueFromItems(); + if ( shareConfig != null ) + Attribute( "nz-page-header-breadcrumb" ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Separator().AutoGenerate().RouteLabel().PageHeader(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Breadcrumbs/Builders/BreadcrumbItemBuilder.cs b/src/Util.Ui.NgZorro/Components/Breadcrumbs/Builders/BreadcrumbItemBuilder.cs new file mode 100644 index 000000000..accbdd761 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Breadcrumbs/Builders/BreadcrumbItemBuilder.cs @@ -0,0 +1,38 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.BreadCrumbs.Builders; + +/// +/// 面包屑项标签生成器 +/// +public class BreadcrumbItemBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化面包屑项标签生成器 + /// + /// 配置 + public BreadcrumbItemBuilder( Config config ) : base( config,"nz-breadcrumb-item" ) { + _config = config; + } + + /// + /// 配置弹出层 + /// + public BreadcrumbItemBuilder Overlay() { + AttributeIfNotEmpty( "[nzOverlay]", _config.GetValue( UiConst.Overlay ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Overlay(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Breadcrumbs/Builders/BreadcrumbSeparatorBuilder.cs b/src/Util.Ui.NgZorro/Components/Breadcrumbs/Builders/BreadcrumbSeparatorBuilder.cs new file mode 100644 index 000000000..cbe4a30cd --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Breadcrumbs/Builders/BreadcrumbSeparatorBuilder.cs @@ -0,0 +1,16 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.BreadCrumbs.Builders; + +/// +/// 面包屑分隔符标签生成器 +/// +public class BreadcrumbSeparatorBuilder : AngularTagBuilder { + /// + /// 初始化面包屑分隔符标签生成器 + /// + /// 配置 + public BreadcrumbSeparatorBuilder( Config config ) : base( config,"nz-breadcrumb-separator" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Breadcrumbs/Renders/BreadcrumbItemRender.cs b/src/Util.Ui.NgZorro/Components/Breadcrumbs/Renders/BreadcrumbItemRender.cs new file mode 100644 index 000000000..3addcc62f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Breadcrumbs/Renders/BreadcrumbItemRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.BreadCrumbs.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Breadcrumbs.Renders; + +/// +/// 面包屑项渲染器 +/// +public class BreadcrumbItemRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化面包屑项渲染器 + /// + /// 配置 + public BreadcrumbItemRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new BreadcrumbItemBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new BreadcrumbItemRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Breadcrumbs/Renders/BreadcrumbRender.cs b/src/Util.Ui.NgZorro/Components/Breadcrumbs/Renders/BreadcrumbRender.cs new file mode 100644 index 000000000..082644810 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Breadcrumbs/Renders/BreadcrumbRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.BreadCrumbs.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.BreadCrumbs.Renders; + +/// +/// 面包屑渲染器 +/// +public class BreadcrumbRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化面包屑渲染器 + /// + /// 配置 + public BreadcrumbRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new BreadcrumbBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new BreadcrumbRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Breadcrumbs/Renders/BreadcrumbSeparatorRender.cs b/src/Util.Ui.NgZorro/Components/Breadcrumbs/Renders/BreadcrumbSeparatorRender.cs new file mode 100644 index 000000000..44e83ce7b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Breadcrumbs/Renders/BreadcrumbSeparatorRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.BreadCrumbs.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Breadcrumbs.Renders; + +/// +/// 面包屑分隔符渲染器 +/// +public class BreadcrumbSeparatorRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化面包屑分隔符渲染器 + /// + /// 配置 + public BreadcrumbSeparatorRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new BreadcrumbSeparatorBuilder(_config); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new BreadcrumbSeparatorRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Buttons/ATagHelper.cs b/src/Util.Ui.NgZorro/Components/Buttons/ATagHelper.cs new file mode 100644 index 000000000..807d60754 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Buttons/ATagHelper.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Buttons.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Buttons; + +/// +/// 链接,生成的标签为<a></a> +/// +[HtmlTargetElement( "util-a" )] +public class ATagHelper : ButtonTagHelperBase { + /// + /// 扩展属性,是否危险状态,默认值: false + /// + public bool Danger { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ARender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Buttons/Builders/ABuilder.cs b/src/Util.Ui.NgZorro/Components/Buttons/Builders/ABuilder.cs new file mode 100644 index 000000000..425f8abfb --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Buttons/Builders/ABuilder.cs @@ -0,0 +1,184 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.Enums; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Icons.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Directives.Tooltips; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Buttons.Builders; + +/// +/// a标签生成器 +/// +public class ABuilder : ButtonBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化a标签生成器 + /// + public ABuilder( Config config ) : base( config, "a" ) { + _config = config; + } + + /// + /// 配置链接地址 + /// + public ABuilder Href() { + AttributeIfNotEmpty( "href", _config.GetValue( UiConst.Href ) ); + AttributeIfNotEmpty( "[href]", _config.GetValue( AngularConst.BindHref ) ); + return this; + } + + /// + /// 配置链接打开目标 + /// + public ABuilder Target() { + AttributeIfNotEmpty( "target", _config.GetValue( UiConst.Target )?.Description() ); + AttributeIfNotEmpty( "[target]", _config.GetValue( AngularConst.BindTarget ) ); + return this; + } + + /// + /// 配置链接关系 + /// + public ABuilder Rel() { + AttributeIfNotEmpty( "rel", _config.GetValue( UiConst.Rel ) ); + AttributeIfNotEmpty( "[rel]", _config.GetValue( AngularConst.BindRel ) ); + return this; + } + + /// + /// 配置路由链接 + /// + public ABuilder RouterLink() { + AttributeIfNotEmpty( "routerLink", _config.GetValue( AngularConst.RouterLink ) ); + AttributeIfNotEmpty( "[routerLink]", _config.GetValue( AngularConst.BindRouterLink ) ); + AttributeIfNotEmpty( "routerLinkActive", _config.GetValue( AngularConst.RouterLinkActive ) ); + AttributeIfNotEmpty( "[routerLinkActive]", _config.GetValue( AngularConst.BindRouterLinkActive ) ); + AttributeIfNotEmpty( "[queryParams]", _config.GetValue( UiConst.QueryParams ) ); + AttributeIfNotEmpty( "queryParamsHandling", _config.GetValue( UiConst.QueryParamsHandling )?.Description() ); + AttributeIfNotEmpty( "[queryParamsHandling]", _config.GetValue( AngularConst.BindQueryParamsHandling ) ); + return this; + } + + /// + /// 配置选项卡路由联动 + /// + public ABuilder TabLink() { + var value = _config.GetValue( UiConst.TabLink ); + if ( value.IsEmpty() && _config.Contains( UiConst.TabLink ) ) + value = "true"; + if ( value.IsEmpty() ) + return this; + if ( value == "true" ) + value = string.Empty; + Attribute( "*nzTabLink", value ); + Attribute( "nz-tab-link" ); + return this; + } + + /// + /// 配置危险按钮 + /// + public override ABuilder Danger() { + var result = _config.GetValue( UiConst.Danger ); + if ( result == true ) { + Class( "ant-btn-dangerous" ); + } + return this; + } + + /// + /// 配置查询表单链接 + /// + public ABuilder IsSearch() { + var isSearch = _config.GetValue( UiConst.IsSearch ); + if ( isSearch != true ) + return this; + this.OnClick( "expand=!expand" ); + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) + AppendContent( "{{" + $"expand?('{I18nKeys.Collapse}'|i18n):('{I18nKeys.Expand}'|i18n)" + "}}" ); + else + AppendContent( "{{expand?'收起':'展开'}}" ); + var icon = new IconBuilder( _config ); + icon.BindType( "expand?'up':'down'" ); + AppendContent( icon ); + return this; + } + + /// + /// 配置显示表格设置 + /// + public ABuilder ShowTableSettings() { + var tableId = _config.GetValue( UiConst.ShowTableSettings ); + if ( tableId.IsEmpty() ) + return this; + this.OnClick( $"ts_{tableId}.show()" ); + Class( "card-tool-icon-btn" ); + var options = NgZorroOptionsService.GetOptions(); + this.TooltipTitle( options.EnableI18n ? "util.tableSettings" : "表格设置" ); + var icon = new IconBuilder( _config ); + icon.Type( AntDesignIcon.Setting ); + AppendContent( icon ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + ConfigButton().Href().Target().Rel().RouterLink().TabLink() + .DropdownMenu().DropdownMenuPlacement() + .IsSearch().ShowTableSettings(); + Fullscreen(); + } + + /// + /// 配置全屏 + /// + public ABuilder Fullscreen() { + var value = _config.GetValue( UiConst.Fullscreen ); + if ( value.IsEmpty() ) + return this; + this.OnClick( $"{GetButtonExtendId()}.fullscreen({value}{GetFullscreenWrapClass()}{GetFullscreenPack()}{GetFullscreenTitle()})" ); + Class( "card-tool-icon-btn" ); + AppendContent( GetFullscreenIcon() ); + AppendContent( GetFullscreenExitIcon() ); + var options = NgZorroOptionsService.GetOptions(); + if (options.EnableI18n) { + this.BindTooltipTitle( $"({GetButtonExtendId()}.isFullscreen?'util.fullscreenExit':'util.fullscreen')|i18n" ); + return this; + } + this.BindTooltipTitle( $"{GetButtonExtendId()}.isFullscreen?'退出全屏':'全屏'" ); + return this; + } + + /// + /// 获取全屏图标 + /// + private IconBuilder GetFullscreenIcon() { + var icon = new IconBuilder( _config ); + icon.Theme( IconTheme.Outline ); + icon.Type( AntDesignIcon.Fullscreen ); + icon.NgIf( $"!{GetButtonExtendId()}.isFullscreen" ); + return icon; + } + + /// + /// 获取退出全屏图标 + /// + private IconBuilder GetFullscreenExitIcon() { + var icon = new IconBuilder( _config ); + icon.Theme( IconTheme.Outline ); + icon.Type( AntDesignIcon.FullscreenExit ); + icon.NgIf( $"{GetButtonExtendId()}.isFullscreen" ); + return icon; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Buttons/Builders/ButtonBuilder.cs b/src/Util.Ui.NgZorro/Components/Buttons/Builders/ButtonBuilder.cs new file mode 100644 index 000000000..8ad648e35 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Buttons/Builders/ButtonBuilder.cs @@ -0,0 +1,109 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Directives.Tooltips; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Buttons.Builders; + +/// +/// 按钮标签生成器 +/// +public class ButtonBuilder : ButtonBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化按钮标签生成器 + /// + public ButtonBuilder( Config config ) : base( config,"button" ) { + _config = config; + } + + /// + /// 设置提交按钮类型 + /// + public ButtonBuilder IsSubmit() { + AttributeIf( "type", "submit",_config.GetValue( UiConst.IsSubmit ) == true,true ); + return this; + } + + /// + /// 设置按钮类型 + /// + public ButtonBuilder ButtonType() { + var formShareConfig = _config.GetValueFromItems(); + if ( formShareConfig != null ) + Attribute( "type", "button" ); + return this; + } + + /// + /// 配置复制到剪贴板 + /// + public ButtonBuilder CopyToClipboard() { + var value = _config.GetValue( UiConst.CopyToClipboard ); + if( value.IsEmpty() ) + return this; + EnableExtend(); + _config.SetAttribute( UiConst.Type, Util.Ui.NgZorro.Enums.ButtonType.Text ); + _config.SetAttribute( UiConst.Icon,AntDesignIcon.Copy ); + this.OnClick($"{GetButtonExtendId()}.copyToClipboard({value})"); + var options = NgZorroOptionsService.GetOptions(); + if( options.EnableI18n ) { + this.BindTooltipTitle( $"'{I18nKeys.CopyToClipboard}'|i18n" ); + return this; + } + this.TooltipTitle( "复制到剪贴板" ); + return this; + } + + /// + /// 配置全屏 + /// + public ButtonBuilder Fullscreen() { + var value = _config.GetValue( UiConst.Fullscreen ); + if ( value.IsEmpty() ) + return this; + this.OnClick( $"{GetButtonExtendId()}.fullscreen({value}{GetFullscreenWrapClass()}{GetFullscreenPack()}{GetFullscreenTitle()})" ); + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + AppendContent( "{{" + $"({GetButtonExtendId()}.isFullscreen?'{I18nKeys.FullscreenExit}':'{I18nKeys.Fullscreen}')|i18n" + "}}" ); + return this; + } + AppendContent( "{{"+ $"{GetButtonExtendId()}.isFullscreen?'退出全屏':'全屏'" + "}}" ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + CopyToClipboard(); + Fullscreen(); + ConfigButton(); + base.Config(); + IsSubmit().ButtonType(); + } + + /// + /// 是否启用扩展 + /// + protected override bool IsEnableExtend() { + if ( base.IsEnableExtend() ) + return true; + if ( IsCopyToClipboard() ) + return true; + return false; + } + + /// + /// 是否复制到剪贴板 + /// + protected bool IsCopyToClipboard() { + return _config.Contains( UiConst.CopyToClipboard ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Buttons/Builders/ButtonGroupBuilder.cs b/src/Util.Ui.NgZorro/Components/Buttons/Builders/ButtonGroupBuilder.cs new file mode 100644 index 000000000..ee1437723 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Buttons/Builders/ButtonGroupBuilder.cs @@ -0,0 +1,40 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Buttons.Builders; + +/// +/// nz-button-group标签生成器 +/// +public class ButtonGroupBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化nz-button-group标签生成器 + /// + /// 配置 + public ButtonGroupBuilder( Config config ) : base( config,"nz-button-group" ) { + _config = config; + } + + /// + /// 配置按钮组尺寸 + /// + public ButtonGroupBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Size(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Buttons/ButtonGroupTagHelper.cs b/src/Util.Ui.NgZorro/Components/Buttons/ButtonGroupTagHelper.cs new file mode 100644 index 000000000..2c1f03ef4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Buttons/ButtonGroupTagHelper.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Buttons.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Buttons; + +/// +/// 按钮组,生成的标签为<nz-button-group></nz-button-group> +/// +[HtmlTargetElement( "util-button-group" )] +public class ButtonGroupTagHelper : AngularTagHelperBase { + /// + /// nzSize,按钮尺寸 + /// + public ButtonSize Size { get; set; } + /// + /// [nzSize],按钮尺寸 + /// + public string BindSize { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ButtonGroupRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Buttons/ButtonTagHelper.cs b/src/Util.Ui.NgZorro/Components/Buttons/ButtonTagHelper.cs new file mode 100644 index 000000000..ae1f32046 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Buttons/ButtonTagHelper.cs @@ -0,0 +1,80 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Buttons.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Buttons; + +/// +/// 按钮,生成的标签为<button nz-button></button>,当按钮类型为链接按钮时,生成的标签为<a nz-button nzType="link"></a> +/// +[HtmlTargetElement( "util-button" )] +public class ButtonTagHelper : ButtonTagHelperBase { + /// + /// nzType,按钮类型,可选值: 'primary'|'dashed'|'link'|'text' + /// + public ButtonType Type { get; set; } + /// + /// [nzType],按钮类型,可选值: 'primary'|'dashed'|'link'|'text' + /// + public string BindType { get; set; } + /// + /// nzSize,按钮尺寸, 可选值: 'large'|'small'|'default', 默认值: 'default' + /// + public ButtonSize Size { get; set; } + /// + /// [nzSize],按钮尺寸, 可选值: 'large'|'small'|'default', 默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// nzShape,按钮形状, 可选值: 'circle'|'round' + /// + public ButtonShape Shape { get; set; } + /// + /// [nzShape],按钮形状, 可选值: 'circle'|'round' + /// + public string BindShape { get; set; } + /// + /// [disabled],是否禁用, 默认值: false + /// + public string Disabled { get; set; } + /// + /// [nzDanger],是否危险状态, 默认值: false + /// + public string Danger { get; set; } + /// + /// [nzLoading],是否加载状态, 默认值: false + /// + public string Loading { get; set; } + /// + /// [nzGhost],是否幽灵按钮,幽灵按钮将按钮的内容反色,背景变为透明,常用在有色背景上, 默认值: false + /// + public string Ghost { get; set; } + /// + /// [nzBlock],将按钮宽度调整为父容器宽度, 默认值: false + /// + public string Block { get; set; } + /// + /// 扩展属性, 设置图标 + /// + public AntDesignIcon Icon { get; set; } + /// + /// 扩展属性, 当按钮在表单中, 是否验证表单,如果设置为 true, 表单无效时禁用按钮, 默认值: false + /// + public bool ValidateForm { get; set; } + /// + /// 扩展属性, 是否提交按钮,如果设置为 true,添加 type='submit', 默认值: false + /// + public bool IsSubmit { get; set; } + /// + /// 扩展属性,将内容复制到剪贴板,设置复制内容的引用 + /// + public string CopyToClipboard { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ButtonRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Buttons/Renders/ARender.cs b/src/Util.Ui.NgZorro/Components/Buttons/Renders/ARender.cs new file mode 100644 index 000000000..d039ff8b6 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Buttons/Renders/ARender.cs @@ -0,0 +1,58 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Buttons.Builders; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Buttons.Renders; + +/// +/// 链接渲染器 +/// +public class ARender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化链接渲染器 + /// + /// 配置 + public ARender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + if ( IsHide() ) + return new EmptyTagBuilder(); + var builder = new ABuilder( _config ); + builder.Config(); + return builder; + } + + /// + /// 是否隐藏标签 + /// + private bool IsHide() { + var isSearch = _config.GetValue( UiConst.IsSearch ); + if ( isSearch != true ) + return false; + var formShareConfig = GetFormShareConfig(); + return formShareConfig.GetConditionCount() <= formShareConfig.SearchFormShowNumber; + } + + /// + /// 获取表单共享配置 + /// + private FormShareConfig GetFormShareConfig() { + return _config.GetValueFromItems() ?? new FormShareConfig(); + } + + /// + public override IHtmlContent Clone() { + return new ARender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Buttons/Renders/ButtonGroupRender.cs b/src/Util.Ui.NgZorro/Components/Buttons/Renders/ButtonGroupRender.cs new file mode 100644 index 000000000..34361a68a --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Buttons/Renders/ButtonGroupRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Buttons.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Buttons.Renders; + +/// +/// 按钮组渲染器 +/// +public class ButtonGroupRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化按钮组渲染器 + /// + /// 配置 + public ButtonGroupRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ButtonGroupBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ButtonGroupRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Buttons/Renders/ButtonRender.cs b/src/Util.Ui.NgZorro/Components/Buttons/Renders/ButtonRender.cs new file mode 100644 index 000000000..e45e6ef8f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Buttons/Renders/ButtonRender.cs @@ -0,0 +1,76 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Buttons.Builders; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Buttons.Renders; + +/// +/// 按钮渲染器 +/// +public class ButtonRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化按钮渲染器 + /// + /// 配置 + public ButtonRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = CreateTagBuilder(); + builder.Attribute( "nz-button" ); + builder.Config(); + return builder; + } + + /// + /// 创建标签生成器 + /// + private TagBuilder CreateTagBuilder() { + if ( GetButtonType() != ButtonType.Link ) + return new ButtonBuilder( _config ); + if ( IsHide() ) + return new EmptyTagBuilder(); + return new ABuilder( _config ); + } + + /// + /// 获取按钮类型 + /// + private ButtonType? GetButtonType() { + return _config.GetValue( UiConst.Type ); + } + + /// + /// 是否隐藏标签 + /// + private bool IsHide() { + var isSearch = _config.GetValue( UiConst.IsSearch ); + if ( isSearch != true ) + return false; + var formShareConfig = GetFormShareConfig(); + return formShareConfig.GetConditionCount() <= formShareConfig.SearchFormShowNumber; + } + + /// + /// 获取表单共享配置 + /// + private FormShareConfig GetFormShareConfig() { + return _config.GetValueFromItems() ?? new FormShareConfig(); + } + + /// + public override IHtmlContent Clone() { + return new ButtonRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Calendars/Builders/CalendarBuilder.cs b/src/Util.Ui.NgZorro/Components/Calendars/Builders/CalendarBuilder.cs new file mode 100644 index 000000000..f81d765c7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Calendars/Builders/CalendarBuilder.cs @@ -0,0 +1,94 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Calendars.Builders; + +/// +/// 日历标签生成器 +/// +public class CalendarBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化日历标签生成器 + /// + public CalendarBuilder( Config config ) : base( config, "nz-calendar" ) { + _config = config; + } + + /// + /// 配置显示模式 + /// + public CalendarBuilder Mode() { + AttributeIfNotEmpty( "nzMode", _config.GetValue( UiConst.Mode )?.Description() ); + AttributeIfNotEmpty( "[nzMode]", _config.GetValue( AngularConst.BindMode ) ); + AttributeIfNotEmpty( "[(nzMode)]", _config.GetValue( AngularConst.BindonMode ) ); + return this; + } + + /// + /// 配置是否全屏显示 + /// + public CalendarBuilder Fullscreen() { + AttributeIfNotEmpty( "[nzFullscreen]", _config.GetValue( UiConst.Fullscreen ) ); + return this; + } + + /// + /// 配置自定义渲染日期单元格模板 + /// + public CalendarBuilder DateCell() { + AttributeIfNotEmpty( "[nzDateCell]", _config.GetValue( UiConst.DateCell ) ); + AttributeIfNotEmpty( "[nzDateFullCell]", _config.GetValue( UiConst.DateFullCell ) ); + return this; + } + + /// + /// 配置自定义渲染月单元格模板 + /// + public CalendarBuilder MonthCell() { + AttributeIfNotEmpty( "[nzMonthCell]", _config.GetValue( UiConst.MonthCell ) ); + AttributeIfNotEmpty( "[nzMonthFullCell]", _config.GetValue( UiConst.MonthFullCell ) ); + return this; + } + + /// + /// 配置不可选择日期函数 + /// + public CalendarBuilder DisabledDate() { + AttributeIfNotEmpty( "[nzDisabledDate]", _config.GetValue( UiConst.DisabledDate ) ); + return this; + } + + /// + /// 配置自定义头部内容 + /// + public CalendarBuilder CustomHeader() { + AttributeIfNotEmpty( "nzCustomHeader", _config.GetValue( UiConst.CustomHeader ) ); + AttributeIfNotEmpty( "[nzCustomHeader]", _config.GetValue( AngularConst.BindCustomHeader ) ); + return this; + } + + /// + /// 配置事件 + /// + public CalendarBuilder Events() { + AttributeIfNotEmpty( "(nzPanelChange)", _config.GetValue( UiConst.OnPanelChange ) ); + AttributeIfNotEmpty( "(nzSelectChange)", _config.GetValue( UiConst.OnSelectChange ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + this.NgModel( _config ); + Mode().Fullscreen().DateCell().MonthCell().DisabledDate().CustomHeader().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Calendars/CalendarTagHelper.cs b/src/Util.Ui.NgZorro/Components/Calendars/CalendarTagHelper.cs new file mode 100644 index 000000000..989e5dd17 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Calendars/CalendarTagHelper.cs @@ -0,0 +1,80 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Calendars.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Calendars; + +/// +/// 日历,生成的标签为<nz-calendar></nz-calendar> +/// +[HtmlTargetElement( "util-calendar" )] +public class CalendarTagHelper : AngularTagHelperBase { + /// + /// [(ngModel)],展示日期,双向绑定, 类型: Date, 默认值: 当前日期 + /// + public string NgModel { get; set; } + /// + /// [ngModel],展示日期, 类型: Date, 默认值: 当前日期 + /// + public string BindNgModel { get; set; } + /// + /// nzMode,显示模式,可选值: 'month' | 'year',默认值: 'month' + /// + public CalendarMode Mode { get; set; } + /// + /// [nzMode],显示模式,可选值: 'month' | 'year',默认值: 'month' + /// + public string BindMode { get; set; } + /// + /// [(nzMode)],显示模式,可选值: 'month' | 'year',默认值: 'month' + /// + public string BindonMode { get; set; } + /// + /// [nzFullscreen],是否全屏显示, 类型: boolean, 默认值: true + /// + public string Fullscreen { get; set; } + /// + /// [nzDateCell],自定义日期单元格,模版内容追加到单元格,类型: TemplateRef<Date> + /// + public string DateCell { get; set; } + /// + /// [nzDateFullCell],自定义日期单元格,模版内容覆盖单元格,类型: TemplateRef<Date> + /// + public string DateFullCell { get; set; } + /// + /// [nzMonthCell],自定义月份单元格,模版内容追加到单元格,类型: TemplateRef<Date> + /// + public string MonthCell { get; set; } + /// + /// [nzMonthFullCell],自定义月份单元格,模版内容覆盖单元格,类型: TemplateRef<Date> + /// + public string MonthFullCell { get; set; } + /// + /// [nzDisabledDate],不可选择日期函数,类型: (current: Date) => boolean + /// + public string DisabledDate { get; set; } + /// + /// nzCustomHeader,自定义头部内容, 类型: string | TemplateRef<void> + /// + public string CustomHeader { get; set; } + /// + /// [nzCustomHeader],自定义头部内容, 类型: string | TemplateRef<void> + /// + public string BindCustomHeader { get; set; } + /// + /// (nzPanelChange),面板变化事件,类型: EventEmitter<{ date: Date, mode: 'month' | 'year' }> + /// + public string OnPanelChange { get; set; } + /// + /// (nzSelectChange),选择日期变化事件,类型: EventEmitter<Date> + /// + public string OnSelectChange { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new CalendarRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Calendars/Renders/CalendarRender.cs b/src/Util.Ui.NgZorro/Components/Calendars/Renders/CalendarRender.cs new file mode 100644 index 000000000..43cc238a2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Calendars/Renders/CalendarRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Calendars.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Calendars.Renders; + +/// +/// 日历渲染器 +/// +public class CalendarRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化日历渲染器 + /// + /// 配置 + public CalendarRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new CalendarBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new CalendarRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Cards/Builders/CardBuilder.cs b/src/Util.Ui.NgZorro/Components/Cards/Builders/CardBuilder.cs new file mode 100644 index 000000000..f3919a158 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Cards/Builders/CardBuilder.cs @@ -0,0 +1,142 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Cards.Builders; + +/// +/// 卡片标签生成器 +/// +public class CardBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化卡片标签生成器 + /// + /// 配置 + public CardBuilder( Config config ) : base( config, "nz-card" ) { + _config = config; + } + + /// + /// 配置标题 + /// + public CardBuilder Title() { + SetTitle( _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 设置标题 + /// + private void SetTitle( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzTitle]", value ); + return; + } + AttributeIfNotEmpty( "nzTitle", value ); + } + + /// + /// 配置操作组 + /// + public CardBuilder Actions() { + AttributeIfNotEmpty( "[nzActions]", _config.GetValue( UiConst.Actions ) ); + return this; + } + + /// + /// 配置内容区域自定义样式 + /// + public CardBuilder BodyStyle() { + AttributeIfNotEmpty( "[nzBodyStyle]", _config.GetValue( UiConst.BodyStyle ) ); + return this; + } + + /// + /// 配置是否显示边框 + /// + public CardBuilder Bordered() { + AttributeIfNotEmpty( "[nzBordered]", _config.GetValue( UiConst.Bordered ) ); + return this; + } + + /// + /// 配置封面 + /// + public CardBuilder Cover() { + AttributeIfNotEmpty( "[nzCover]", _config.GetValue( UiConst.Cover ) ); + return this; + } + + /// + /// 配置右上角操作区域 + /// + public CardBuilder Extra() { + AttributeIfNotEmpty( "[nzExtra]", _config.GetValue( UiConst.Extra ) ); + return this; + } + + /// + /// 配置鼠标滑过时是否可浮起 + /// + public CardBuilder Hoverable() { + AttributeIfNotEmpty( "[nzHoverable]", _config.GetValue( UiConst.Hoverable ) ); + return this; + } + + /// + /// 配置是否加载状态 + /// + public CardBuilder Loading() { + AttributeIfNotEmpty( "[nzLoading]", _config.GetValue( UiConst.Loading ) ); + return this; + } + + /// + /// 配置卡片类型 + /// + public CardBuilder Type() { + var type = _config.GetValue( UiConst.Type ); + if ( type == CardType.Inner ) + Attribute( "nzType", "inner" ); + AttributeIfNotEmpty( "[nzType]", _config.GetValue( AngularConst.BindType ) ); + return this; + } + + /// + /// 配置尺寸 + /// + public CardBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置事件 + /// + public CardBuilder Event() { + this.OnClick( _config ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Title().Actions().BodyStyle().Bordered().Cover().Extra() + .Hoverable().Loading().Type().Size() + .SpaceItem( _config ) + .Event(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Cards/Builders/CardGridBuilder.cs b/src/Util.Ui.NgZorro/Components/Cards/Builders/CardGridBuilder.cs new file mode 100644 index 000000000..efdf2a7f7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Cards/Builders/CardGridBuilder.cs @@ -0,0 +1,47 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Extensions; + +namespace Util.Ui.NgZorro.Components.Cards.Builders; + +/// +/// 网格内嵌卡片标签生成器 +/// +public class CardGridBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化网格内嵌卡片标签生成器 + /// + /// 配置 + public CardGridBuilder( Config config ) : base( config,"div" ) { + base.Attribute( "nz-card-grid" ); + _config = config; + } + + /// + /// 配置鼠标滑过时是否可浮起 + /// + public CardGridBuilder Hoverable() { + AttributeIfNotEmpty( "[nzHoverable]", _config.GetValue( UiConst.Hoverable ) ); + return this; + } + + /// + /// 配置单击事件 + /// + public CardGridBuilder OnClick() { + this.OnClick( _config ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Hoverable().OnClick(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Cards/Builders/CardMetaBuilder.cs b/src/Util.Ui.NgZorro/Components/Cards/Builders/CardMetaBuilder.cs new file mode 100644 index 000000000..62e249339 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Cards/Builders/CardMetaBuilder.cs @@ -0,0 +1,70 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Cards.Builders; + +/// +/// 卡片元信息标签生成器 +/// +public class CardMetaBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化卡片元信息标签生成器 + /// + /// 配置 + public CardMetaBuilder( Config config ) : base( config,"nz-card-meta" ) { + _config = config; + } + + /// + /// 配置标题 + /// + public CardMetaBuilder Title() { + SetTitle( _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 设置表单标签文本 + /// + private void SetTitle( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzTitle]", value ); + return; + } + AttributeIfNotEmpty( "nzTitle", value ); + } + + /// + /// 配置头像 + /// + public CardMetaBuilder Avatar() { + AttributeIfNotEmpty( "[nzAvatar]", _config.GetValue( UiConst.Avatar ) ); + return this; + } + + /// + /// 配置描述 + /// + public CardMetaBuilder Description() { + AttributeIfNotEmpty( "nzDescription", _config.GetValue( UiConst.Description ) ); + AttributeIfNotEmpty( "[nzDescription]", _config.GetValue( AngularConst.BindDescription ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Title().Avatar().Description(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Cards/Builders/CardTabBuilder.cs b/src/Util.Ui.NgZorro/Components/Cards/Builders/CardTabBuilder.cs new file mode 100644 index 000000000..033536d2f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Cards/Builders/CardTabBuilder.cs @@ -0,0 +1,22 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Cards.Builders; + +/// +/// 卡片标签页标签生成器 +/// +public class CardTabBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化卡片标签页标签生成器 + /// + /// 配置 + public CardTabBuilder( Config config ) : base( config,"nz-card-tab" ) { + _config = config; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Cards/CardGridTagHelper.cs b/src/Util.Ui.NgZorro/Components/Cards/CardGridTagHelper.cs new file mode 100644 index 000000000..06f744356 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Cards/CardGridTagHelper.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Cards.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Cards; + +/// +/// 网格内嵌卡片,生成的标签为<div nz-card-grid></div> +/// +[HtmlTargetElement( "util-card-grid" )] +public class CardGridTagHelper : AngularTagHelperBase { + /// + /// [nzHoverable],鼠标滑过时是否可浮起, 类型: boolean, 默认值: true + /// + public string Hoverable { get; set; } + /// + /// (click),单击事件 + /// + public string OnClick { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new CardGridRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Cards/CardMetaTagHelper.cs b/src/Util.Ui.NgZorro/Components/Cards/CardMetaTagHelper.cs new file mode 100644 index 000000000..128fe129a --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Cards/CardMetaTagHelper.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Cards.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Cards; + +/// +/// 卡片元信息,生成的标签为<nz-card-meta></nz-card-meta> +/// +[HtmlTargetElement( "util-card-meta" )] +public class CardMetaTagHelper : AngularTagHelperBase { + /// + /// nzTitle,标题,支持多语言 + /// + public string Title { get; set; } + /// + /// [nzTitle],标题,类型: string|TemplateRef<void> + /// + public string BindTitle { get; set; } + /// + /// [nzAvatar],头像,类型: TemplateRef<void> + /// + public string Avatar { get; set; } + /// + /// nzDescription,描述,类型: string|TemplateRef<void> + /// + public string Description { get; set; } + /// + /// [nzDescription],描述,类型: string|TemplateRef<void> + /// + public string BindDescription { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new CardMetaRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Cards/CardTabTagHelper.cs b/src/Util.Ui.NgZorro/Components/Cards/CardTabTagHelper.cs new file mode 100644 index 000000000..f670c7691 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Cards/CardTabTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Cards.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Cards; + +/// +/// 卡片标签页,生成的标签为<nz-card-tab></nz-card-tab> +/// +[HtmlTargetElement( "util-card-tab" )] +public class CardTabTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new CardTabRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Cards/CardTagHelper.cs b/src/Util.Ui.NgZorro/Components/Cards/CardTagHelper.cs new file mode 100644 index 000000000..a51d59561 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Cards/CardTagHelper.cs @@ -0,0 +1,80 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Cards.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Cards; + +/// +/// 卡片,生成的标签为<nz-card></nz-card> +/// +[HtmlTargetElement( "util-card")] +public class CardTagHelper : AngularTagHelperBase { + /// + /// nzTitle,标题,支持多语言 + /// + public string Title { get; set; } + /// + /// [nzTitle],标题,类型: string|TemplateRef<void> + /// + public string BindTitle { get; set; } + /// + /// [nzActions],卡片操作组,位于卡片底部,类型: Array<TemplateRef<void>> + /// + public string Actions { get; set; } + /// + /// [nzBodyStyle],内容区域自定义样式,类型: { [key: string]: string } + /// + public string BodyStyle { get; set; } + /// + /// [nzBordered],是否显示边框, 类型: boolean, 默认值: true + /// + public string Bordered { get; set; } + /// + /// [nzCover],卡片封面,类型: TemplateRef<void> + /// + public string Cover { get; set; } + /// + /// [nzExtra],卡片右上角操作区域,类型: string | TemplateRef<void> + /// + public string Extra { get; set; } + /// + /// [nzHoverable],鼠标滑过时是否可浮起, 类型: boolean, 默认值: false + /// + public string Hoverable { get; set; } + /// + /// [nzLoading],是否加载状态, 类型: boolean, 默认值: false + /// + public string Loading { get; set; } + /// + /// nzType,卡片类型,可设置为 inner 或 不设置 + /// + public CardType Type { get; set; } + /// + /// [nzType],卡片类型,可设置为 inner 或 不设置 + /// + public string BindType { get; set; } + /// + /// nzSize,卡片尺寸,可选值: 'default'|'small',默认值: 'default' + /// + public CardSize Size { get; set; } + /// + /// [nzSize],卡片尺寸,可选值: 'default'|'small',默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// *nzSpaceItem,值为 true 时设置为间距项,放入 <util-space> 中使用 + /// + public bool SpaceItem { get; set; } + /// + /// (click),单击事件 + /// + public string OnClick { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new CardRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Cards/Renders/CardGridRender.cs b/src/Util.Ui.NgZorro/Components/Cards/Renders/CardGridRender.cs new file mode 100644 index 000000000..ef5e45be3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Cards/Renders/CardGridRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Cards.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Cards.Renders; + +/// +/// 网格内嵌卡片渲染器 +/// +public class CardGridRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化网格内嵌卡片渲染器 + /// + /// 配置 + public CardGridRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new CardGridBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new CardGridRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Cards/Renders/CardMetaRender.cs b/src/Util.Ui.NgZorro/Components/Cards/Renders/CardMetaRender.cs new file mode 100644 index 000000000..401df8f87 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Cards/Renders/CardMetaRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Cards.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Cards.Renders; + +/// +/// 卡片元信息渲染器 +/// +public class CardMetaRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化卡片元信息渲染器 + /// + /// 配置 + public CardMetaRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new CardMetaBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new CardMetaRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Cards/Renders/CardRender.cs b/src/Util.Ui.NgZorro/Components/Cards/Renders/CardRender.cs new file mode 100644 index 000000000..d506e87ad --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Cards/Renders/CardRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Cards.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Cards.Renders; + +/// +/// 卡片渲染器 +/// +public class CardRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化卡片渲染器 + /// + /// 配置 + public CardRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new CardBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new CardRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Cards/Renders/CardTabRender.cs b/src/Util.Ui.NgZorro/Components/Cards/Renders/CardTabRender.cs new file mode 100644 index 000000000..979229af2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Cards/Renders/CardTabRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Cards.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Cards.Renders; + +/// +/// 卡片标签页渲染器 +/// +public class CardTabRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化卡片标签页渲染器 + /// + /// 配置 + public CardTabRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new CardTabBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new CardTabRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Carousels/Builders/CarouselBuilder.cs b/src/Util.Ui.NgZorro/Components/Carousels/Builders/CarouselBuilder.cs new file mode 100644 index 000000000..91c1b6232 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Carousels/Builders/CarouselBuilder.cs @@ -0,0 +1,108 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Carousels.Builders; + +/// +/// 走马灯标签生成器 +/// +public class CarouselBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化走马灯标签生成器 + /// + /// 配置 + public CarouselBuilder( Config config ) : base( config,"nz-carousel" ) { + _config = config; + } + + /// + /// 配置是否自动切换 + /// + public CarouselBuilder AutoPlay() { + AttributeIfNotEmpty( "[nzAutoPlay]", _config.GetValue( UiConst.AutoPlay ) ); + return this; + } + + /// + /// 配置是否自动切换 + /// + public CarouselBuilder AutoPlaySpeed() { + AttributeIfNotEmpty( "[nzAutoPlaySpeed]", _config.GetValue( UiConst.AutoPlaySpeed ) ); + return this; + } + + /// + /// 配置指示点渲染模板 + /// + public CarouselBuilder DotRender() { + AttributeIfNotEmpty( "[nzDotRender]", _config.GetValue( UiConst.DotRender ) ); + return this; + } + + /// + /// 配置指示点位置 + /// + public CarouselBuilder DotPosition() { + AttributeIfNotEmpty( "nzDotPosition", _config.GetValue( UiConst.DotPosition )?.Description() ); + AttributeIfNotEmpty( "[nzDotPosition]", _config.GetValue( AngularConst.BindDotPosition ) ); + return this; + } + + /// + /// 配置是否显示指示点 + /// + public CarouselBuilder Dots() { + AttributeIfNotEmpty( "[nzDots]", _config.GetValue( UiConst.Dots ) ); + return this; + } + + /// + /// 配置动画效果 + /// + public CarouselBuilder Effect() { + AttributeIfNotEmpty( "nzEffect", _config.GetValue( UiConst.Effect )?.Description() ); + AttributeIfNotEmpty( "[nzEffect]", _config.GetValue( AngularConst.BindEffect ) ); + return this; + } + + /// + /// 配置是否支持手势划动切换 + /// + public CarouselBuilder EnableSwipe() { + AttributeIfNotEmpty( "[nzEnableSwipe]", _config.GetValue( UiConst.EnableSwipe ) ); + return this; + } + + /// + /// 配置是否支持循环 + /// + public CarouselBuilder Loop() { + AttributeIfNotEmpty( "[nzLoop]", _config.GetValue( UiConst.Loop ) ); + return this; + } + + /// + /// 配置事件 + /// + public CarouselBuilder Events() { + AttributeIfNotEmpty( "(nzAfterChange)", _config.GetValue( UiConst.OnAfterChange ) ); + AttributeIfNotEmpty( "(nzBeforeChange)", _config.GetValue( UiConst.OnBeforeChange ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + AutoPlay().AutoPlaySpeed().DotRender().DotPosition() + .Dots().Effect().EnableSwipe().Loop() + .Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Carousels/Builders/CarouselContentBuilder.cs b/src/Util.Ui.NgZorro/Components/Carousels/Builders/CarouselContentBuilder.cs new file mode 100644 index 000000000..95c4637a4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Carousels/Builders/CarouselContentBuilder.cs @@ -0,0 +1,22 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Carousels.Builders; + +/// +/// 走马灯内容标签生成器 +/// +public class CarouselContentBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化走马灯内容标签生成器 + /// + /// 配置 + public CarouselContentBuilder( Config config ) : base( config, "div" ) { + _config = config; + base.Attribute( "nz-carousel-content" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Carousels/CarouselContentTagHelper.cs b/src/Util.Ui.NgZorro/Components/Carousels/CarouselContentTagHelper.cs new file mode 100644 index 000000000..045917d54 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Carousels/CarouselContentTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Carousels.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Carousels; + +/// +/// 走马灯内容区域,生成的标签为<div nz-carousel-content></div> +/// +[HtmlTargetElement( "util-carousel-content" )] +public class CarouselContentTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new CarouselContentRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Carousels/CarouselTagHelper.cs b/src/Util.Ui.NgZorro/Components/Carousels/CarouselTagHelper.cs new file mode 100644 index 000000000..a9f805732 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Carousels/CarouselTagHelper.cs @@ -0,0 +1,68 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Carousels.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Carousels; + +/// +/// 走马灯,生成的标签为<nz-carousel></nz-carousel> +/// +[HtmlTargetElement( "util-carousel" )] +public class CarouselTagHelper : AngularTagHelperBase { + /// + /// [nzAutoPlay],是否自动切换,类型: boolean, 默认值: false + /// + public string AutoPlay { get; set; } + /// + /// [nzAutoPlaySpeed],切换时间, 单位:毫秒,当设置为 0 时不切换,默认值: 3000 + /// + public string AutoPlaySpeed { get; set; } + /// + /// [nzDotRender],指示点渲染模板,类型: TemplateRef<{ $implicit: number }> + /// + public string DotRender { get; set; } + /// + /// nzDotPosition,指示点位置,可选值: 'top' | 'bottom' | 'left' | 'right',默认值: 'bottom' + /// + public CarouselDotPosition DotPosition { get; set; } + /// + /// [nzDotPosition],指示点位置,可选值: 'top' | 'bottom' | 'left' | 'right',默认值: 'bottom' + /// + public string BindDotPosition { get; set; } + /// + /// [nzDots],是否显示指示点,类型: boolean, 默认值: true + /// + public string Dots { get; set; } + /// + /// nzEffect,动画效果,可选值: 'scrollx' | 'fade',默认值: 'scrollx' + /// + public CarouselEffect Effect { get; set; } + /// + /// [nzEffect],动画效果,可选值: 'scrollx' | 'fade',默认值: 'scrollx' + /// + public string BindEffect { get; set; } + /// + /// [nzEnableSwipe],是否支持手势划动切换,类型: boolean, 默认值: true + /// + public string EnableSwipe { get; set; } + /// + /// [nzLoop],是否支持循环,类型: boolean, 默认值: true + /// + public string Loop { get; set; } + /// + /// (nzBeforeChange),切换面板前事件,类型: EventEmitter<{ from: number; to: number }> + /// + public string OnBeforeChange { get; set; } + /// + /// (nzAfterChange),切换面板事件,类型: EventEmitter<number> + /// + public string OnAfterChange { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new CarouselRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Carousels/Renders/CarouselContentRender.cs b/src/Util.Ui.NgZorro/Components/Carousels/Renders/CarouselContentRender.cs new file mode 100644 index 000000000..7ede11085 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Carousels/Renders/CarouselContentRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Carousels.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Carousels.Renders; + +/// +/// 走马灯内容渲染器 +/// +public class CarouselContentRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化走马灯内容渲染器 + /// + /// 配置 + public CarouselContentRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new CarouselContentBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new CarouselContentRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Carousels/Renders/CarouselRender.cs b/src/Util.Ui.NgZorro/Components/Carousels/Renders/CarouselRender.cs new file mode 100644 index 000000000..851836ebc --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Carousels/Renders/CarouselRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Carousels.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Carousels.Renders; + +/// +/// 走马灯渲染器 +/// +public class CarouselRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化走马灯渲染器 + /// + /// 配置 + public CarouselRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new CarouselBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new CarouselRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Cascaders/Builders/CascaderBuilder.cs b/src/Util.Ui.NgZorro/Components/Cascaders/Builders/CascaderBuilder.cs new file mode 100644 index 000000000..c7589500d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Cascaders/Builders/CascaderBuilder.cs @@ -0,0 +1,265 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Cascaders.Builders; + +/// +/// 级联选择标签生成器 +/// +public class CascaderBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化级联选择标签生成器 + /// + /// 配置 + public CascaderBuilder( Config config ) : base( config, "nz-cascader" ) { + _config = config; + } + + /// + /// 配置允许清除 + /// + public CascaderBuilder AllowClear() { + AttributeIfNotEmpty( "[nzAllowClear]", _config.GetValue( UiConst.AllowClear ) ); + return this; + } + + /// + /// 配置自动聚焦 + /// + public CascaderBuilder AutoFocus() { + AttributeIfNotEmpty( "[nzAutoFocus]", _config.GetValue( UiConst.AutoFocus ) ); + return this; + } + + /// + /// 配置变更函数 + /// + public CascaderBuilder ChangeOn() { + AttributeIfNotEmpty( "[nzChangeOn]", _config.GetValue( UiConst.ChangeOn ) ); + return this; + } + + /// + /// 配置选择变更 + /// + public CascaderBuilder ChangeOnSelect() { + AttributeIfNotEmpty( "[nzChangeOnSelect]", _config.GetValue( UiConst.ChangeOnSelect ) ); + return this; + } + + /// + /// 配置列类名 + /// + public CascaderBuilder ColumnClassName() { + AttributeIfNotEmpty( "nzColumnClassName", _config.GetValue( UiConst.ColumnClassName ) ); + AttributeIfNotEmpty( "[nzColumnClassName]", _config.GetValue( AngularConst.BindColumnClassName ) ); + return this; + } + + /// + /// 配置禁用 + /// + public CascaderBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置展开图标 + /// + public CascaderBuilder ExpandIcon() { + AttributeIfNotEmpty( "nzExpandIcon", _config.GetValue( UiConst.ExpandIcon )?.Description() ); + AttributeIfNotEmpty( "[nzExpandIcon]", _config.GetValue( AngularConst.BindExpandIcon ) ); + return this; + } + + /// + /// 配置展开方式 + /// + public CascaderBuilder ExpandTrigger() { + AttributeIfNotEmpty( "nzExpandTrigger", _config.GetValue( UiConst.ExpandTrigger )?.Description() ); + AttributeIfNotEmpty( "[nzExpandTrigger]", _config.GetValue( AngularConst.BindExpandTrigger ) ); + return this; + } + + /// + /// 配置触发操作 + /// + public CascaderBuilder TriggerAction() { + AttributeIfNotEmpty( "nzTriggerAction", _config.GetValue( UiConst.TriggerAction )?.Description() ); + AttributeIfNotEmpty( "[nzTriggerAction]", _config.GetValue( AngularConst.BindTriggerAction ) ); + return this; + } + + /// + /// 配置标签属性名 + /// + public CascaderBuilder LabelProperty() { + AttributeIfNotEmpty( "nzLabelProperty", _config.GetValue( UiConst.LabelProperty ) ); + AttributeIfNotEmpty( "[nzLabelProperty]", _config.GetValue( AngularConst.BindLabelProperty ) ); + return this; + } + + /// + /// 配置标签渲染模板 + /// + public CascaderBuilder LabelRender() { + AttributeIfNotEmpty( "[nzLabelRender]", _config.GetValue( UiConst.LabelRender ) ); + return this; + } + + /// + /// 配置动态加载函数 + /// + public CascaderBuilder LoadData() { + AttributeIfNotEmpty( "[nzLoadData]", _config.GetValue( UiConst.LoadData ) ); + return this; + } + + /// + /// 配置浮层类名 + /// + public CascaderBuilder MenuClassName() { + AttributeIfNotEmpty( "nzMenuClassName", _config.GetValue( UiConst.MenuClassName ) ); + AttributeIfNotEmpty( "[nzMenuClassName]", _config.GetValue( AngularConst.BindMenuClassName ) ); + return this; + } + + /// + /// 配置浮层样式 + /// + public CascaderBuilder MenuStyle() { + AttributeIfNotEmpty( "[nzMenuStyle]", _config.GetValue( UiConst.MenuStyle ) ); + return this; + } + + /// + /// 配置空列表默认内容 + /// + public CascaderBuilder NotFoundContent() { + AttributeIfNotEmpty( "nzNotFoundContent", _config.GetValue( UiConst.NotFoundContent ) ); + AttributeIfNotEmpty( "[nzNotFoundContent]", _config.GetValue( AngularConst.BindNotFoundContent ) ); + return this; + } + + /// + /// 配置选项渲染模板 + /// + public CascaderBuilder OptionRender() { + AttributeIfNotEmpty( "[nzOptionRender]", _config.GetValue( UiConst.OptionRender ) ); + return this; + } + + /// + /// 配置可选项数据源 + /// + public CascaderBuilder Options() { + AttributeIfNotEmpty( "[nzOptions]", _config.GetValue( UiConst.Options ) ); + return this; + } + + /// + /// 配置占位提示 + /// + public CascaderBuilder Placeholder() { + AttributeIfNotEmpty( "nzPlaceHolder", _config.GetValue( UiConst.Placeholder ) ); + AttributeIfNotEmpty( "[nzPlaceHolder]", _config.GetValue( AngularConst.BindPlaceholder ) ); + return this; + } + + /// + /// 配置显示箭头 + /// + public CascaderBuilder ShowArrow() { + AttributeIfNotEmpty( "[nzShowArrow]", _config.GetValue( UiConst.ShowArrow ) ); + return this; + } + + /// + /// 配置显示输入框 + /// + public CascaderBuilder ShowInput() { + AttributeIfNotEmpty( "[nzShowInput]", _config.GetValue( UiConst.ShowInput ) ); + return this; + } + + /// + /// 配置显示搜索 + /// + public CascaderBuilder ShowSearch() { + AttributeIfNotEmpty( "[nzShowSearch]", _config.GetValue( UiConst.ShowSearch ) ); + return this; + } + + /// + /// 配置输入框大小 + /// + public CascaderBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置选择框后缀图标 + /// + public CascaderBuilder SuffixIcon() { + AttributeIfNotEmpty( "nzSuffixIcon", _config.GetValue( UiConst.SuffixIcon )?.Description() ); + AttributeIfNotEmpty( "[nzSuffixIcon]", _config.GetValue( AngularConst.BindSuffixIcon ) ); + return this; + } + + /// + /// 配置值属性名 + /// + public CascaderBuilder ValueProperty() { + AttributeIfNotEmpty( "nzValueProperty", _config.GetValue( UiConst.ValueProperty ) ); + AttributeIfNotEmpty( "[nzValueProperty]", _config.GetValue( AngularConst.BindValueProperty ) ); + return this; + } + + /// + /// 配置展开图标 + /// + public CascaderBuilder Status() { + AttributeIfNotEmpty( "nzStatus", _config.GetValue( UiConst.Status )?.Description() ); + AttributeIfNotEmpty( "[nzStatus]", _config.GetValue( AngularConst.BindStatus ) ); + return this; + } + + /// + /// 配置浮层是否应带有背景板 + /// + public CascaderBuilder Backdrop() { + AttributeIfNotEmpty( "[nzBackdrop]", _config.GetValue( UiConst.Backdrop ) ); + return this; + } + + /// + /// 配置事件 + /// + public CascaderBuilder Events() { + AttributeIfNotEmpty( "(nzClear)", _config.GetValue( UiConst.OnClear ) ); + AttributeIfNotEmpty( "(nzVisibleChange)", _config.GetValue( UiConst.OnVisibleChange ) ); + AttributeIfNotEmpty( "(nzSelectionChange)", _config.GetValue( UiConst.OnSelectionChange ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + ConfigForm().Name().AllowClear().AutoFocus().ChangeOn().ChangeOnSelect().ColumnClassName() + .Disabled().ExpandIcon().ExpandTrigger().TriggerAction().LabelProperty().LabelRender().LoadData() + .MenuClassName().MenuStyle().NotFoundContent().OptionRender().Options() + .Placeholder().ShowArrow().ShowInput().ShowSearch().Size().SuffixIcon() + .ValueProperty().Status().Backdrop().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Cascaders/CascaderTagHelper.cs b/src/Util.Ui.NgZorro/Components/Cascaders/CascaderTagHelper.cs new file mode 100644 index 000000000..4c955135b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Cascaders/CascaderTagHelper.cs @@ -0,0 +1,197 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Cascaders.Renders; +using Util.Ui.NgZorro.Components.Selects.Helpers; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Cascaders; + +/// +/// 级联选择,生成的标签为<nz-cascader></nz-cascader> +/// +[HtmlTargetElement( "util-cascader" )] +public class CascaderTagHelper : FormControlTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// [nzAllowClear],是否允许清除,类型: boolean, 默认值: true + /// + public string AllowClear { get; set; } + /// + /// [nzAutoFocus],是否自动聚焦,当存在输入框时, 类型: boolean, 默认值: false + /// + public string AutoFocus { get; set; } + /// + /// [nzChangeOn],点击父级菜单项时,可通过该函数判断是否允许值的变化,类型: (option: any, index: number) => boolean + /// + public string ChangeOn { get; set; } + /// + /// [nzChangeOnSelect],点选每级菜单项时,是否值都会变化, 类型: boolean, 默认值: false + /// + public string ChangeOnSelect { get; set; } + /// + /// nzColumnClassName,自定义浮层列类名 + /// + public string ColumnClassName { get; set; } + /// + /// [nzColumnClassName],自定义浮层列类名 + /// + public string BindColumnClassName { get; set; } + /// + /// [nzDisabled],是否禁用, 默认值: false + /// + public string Disabled { get; set; } + /// + /// nzExpandIcon,自定义次级菜单展开图标 + /// + public AntDesignIcon ExpandIcon { get; set; } + /// + /// [nzExpandIcon],自定义次级菜单展开图标, 类型: string|TemplateRef<void> + /// + public string BindExpandIcon { get; set; } + /// + /// nzExpandTrigger,次级菜单的展开方式, 类型: 'click'|'hover',默认值: 'click' + /// + public CascaderExpandTrigger ExpandTrigger { get; set; } + /// + /// [nzExpandTrigger],次级菜单的展开方式, 类型: 'click'|'hover',默认值: 'click' + /// + public string BindExpandTrigger { get; set; } + /// + /// nzTriggerAction,触发操作, 类型: 'click'|'hover',默认值: 'click' + /// + public CascaderTriggerAction TriggerAction { get; set; } + /// + /// [nzTriggerAction],触发操作, 类型: 'click'|'hover',默认值: 'click' + /// + public string BindTriggerAction { get; set; } + /// + /// nzLabelProperty,选项显示文本的属性名, 默认值: 'label' + /// + public string LabelProperty { get; set; } + /// + /// [nzLabelProperty],选项显示文本的属性名, 默认值: 'label' + /// + public string BindLabelProperty { get; set; } + /// + /// nzValueProperty,选项实际值的属性名,默认值: 'value' + /// + public string ValueProperty { get; set; } + /// + /// [nzValueProperty],选项实际值的属性名,默认值: 'value' + /// + public string BindValueProperty { get; set; } + /// + /// [nzLabelRender],选择后展示的渲染模板, 类型: TemplateRef<any> + /// + public string LabelRender { get; set; } + /// + /// [nzLoadData],动态加载选项函数,如果提供了ngModel初始值,且未提供nzOptions值,则会立即触发动态加载,函数定义: (option: any, index?: index) => PromiseLike<any> + /// + public string LoadData { get; set; } + /// + /// nzMenuClassName,自定义浮层类名 + /// + public string MenuClassName { get; set; } + /// + /// [nzMenuClassName],自定义浮层类名 + /// + public string BindMenuClassName { get; set; } + /// + /// [nzMenuStyle],自定义浮层样式 + /// + public string MenuStyle { get; set; } + /// + /// nzNotFoundContent,下拉列表为空时显示的内容, 类型: string | TemplateRef<void> + /// + public string NotFoundContent { get; set; } + /// + /// [nzNotFoundContent],下拉列表为空时显示的内容, 类型: string | TemplateRef<void> + /// + public string BindNotFoundContent { get; set; } + /// + /// [nzOptionRender],选项的渲染模板, 类型: TemplateRef<{ $implicit: NzCascaderOption, index: number }> + /// + public string OptionRender { get; set; } + /// + /// [nzOptions],可选项数据源, 类型: object[] + /// + public string Options { get; set; } + /// + /// nzPlaceHolder,输入框占位文本, 默认值: '请选择' + /// + public string Placeholder { get; set; } + /// + /// [nzPlaceHolder],输入框占位文本, 默认值: '请选择' + /// + public string BindPlaceholder { get; set; } + /// + /// [nzShowArrow],是否显示箭头,类型: boolean, 默认值: true + /// + public string ShowArrow { get; set; } + /// + /// [nzShowInput],是否显示输入框,默认值: true + /// + public string ShowInput { get; set; } + /// + /// [nzShowSearch],是否支持搜索,默认情况下对 label 进行全匹配搜索,不能和 [nzLoadData] 同时使用, 类型: boolean | NzShowSearchOptions ,默认值: false + /// + public string ShowSearch { get; set; } + /// + /// nzSize,输入框尺寸,可选值: 'large'|'small'|'default', 默认值: 'default' + /// + public InputSize Size { get; set; } + /// + /// [nzSize],输入框尺寸,可选值: 'large'|'small'|'default', 默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// nzSuffixIcon,选择框后缀图标 + /// + public AntDesignIcon SuffixIcon { get; set; } + /// + /// [nzSuffixIcon],选择框后缀图标, 类型: string | TemplateRef<void> + /// + public string BindSuffixIcon { get; set; } + /// + /// nzStatus,校验状态, 可选值: 'error' | 'warning' + /// + public FormControlStatus Status { get; set; } + /// + /// [nzStatus],校验状态, 可选值: 'error' | 'warning' + /// + public string BindStatus { get; set; } + /// + /// [nzBackdrop],浮层是否应带有背景板,类型: boolean, 默认值: false + /// + public string Backdrop { get; set; } + /// + /// (nzClear),清除事件,清除值时触发, 类型: EventEmitter<void> + /// + public string OnClear { get; set; } + /// + /// (nzVisibleChange),显示状态变化事件,菜单浮层显示/隐藏时触发, 类型: EventEmitter<boolean> + /// + public string OnVisibleChange { get; set; } + /// + /// (nzSelectionChange),选中状态变化事件,值发生变化时触发, 类型: EventEmitter<NzCascaderOption[]> + /// + public string OnSelectionChange { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new SelectService( _config ); + service.DisableAutoNzFor(); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new CascaderRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Cascaders/Renders/CascaderRender.cs b/src/Util.Ui.NgZorro/Components/Cascaders/Renders/CascaderRender.cs new file mode 100644 index 000000000..c4681c9d5 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Cascaders/Renders/CascaderRender.cs @@ -0,0 +1,39 @@ +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Cascaders.Builders; + +namespace Util.Ui.NgZorro.Components.Cascaders.Renders; + +/// +/// 级联选择渲染器 +/// +public class CascaderRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化级联选择渲染器 + /// + /// 配置 + public CascaderRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new CascaderBuilder( _config ); + builder.Config(); + _config.Content.AppendTo( builder ); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new CascaderRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Checkboxes/Builders/CheckboxBuilder.cs b/src/Util.Ui.NgZorro/Components/Checkboxes/Builders/CheckboxBuilder.cs new file mode 100644 index 000000000..338361cf7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Checkboxes/Builders/CheckboxBuilder.cs @@ -0,0 +1,113 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Checkboxes.Builders; + +/// +/// 复选框标签生成器 +/// +public class CheckboxBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化复选框标签生成器 + /// + /// 配置 + public CheckboxBuilder( Config config ) : base( config,"label" ) { + _config = config; + base.Attribute( "nz-checkbox" ); + } + + /// + /// 配置内部id + /// + public CheckboxBuilder NzId() { + AttributeIfNotEmpty( "nzId", _config.GetValue( UiConst.NzId ) ); + AttributeIfNotEmpty( "[nzId]", _config.GetValue( AngularConst.BindNzId ) ); + return this; + } + + /// + /// 配置自动获取焦点 + /// + public CheckboxBuilder AutoFocus() { + AttributeIfNotEmpty( "[nzAutoFocus]", _config.GetValue( UiConst.AutoFocus ) ); + return this; + } + + /// + /// 配置禁用 + /// + public CheckboxBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置中间状态 + /// + public CheckboxBuilder Indeterminate() { + AttributeIfNotEmpty( "[nzIndeterminate]", _config.GetValue( UiConst.Indeterminate ) ); + return this; + } + + /// + /// 配置选中状态 + /// + public CheckboxBuilder Checked() { + AttributeIfNotEmpty( "[nzChecked]", _config.GetValue( UiConst.Checked ) ); + return this; + } + + /// + /// 配置值 + /// + public CheckboxBuilder Value() { + AttributeIfNotEmpty( "nzValue", _config.GetValue( UiConst.Value ) ); + AttributeIfNotEmpty( "[nzValue]", _config.GetValue( AngularConst.BindValue ) ); + return this; + } + + /// + /// 配置标签 + /// + public CheckboxBuilder Label() { + var options = NgZorroOptionsService.GetOptions(); + var text = _config.GetValue( UiConst.Label ); + if ( text.IsEmpty() == false ) { + if ( options.EnableI18n ) { + this.AppendContentByI18n( text ); + return this; + } + SetContent( _config.GetValue( UiConst.Label ) ); + return this; + } + var bindLabel = _config.GetValue( AngularConst.BindLabel ); + if( string.IsNullOrWhiteSpace( bindLabel ) ) + return this; + SetContent( $"{{{{{bindLabel}}}}}" ); + return this; + } + + /// + /// 配置事件 + /// + public CheckboxBuilder Events() { + AttributeIfNotEmpty( "(nzCheckedChange)", _config.GetValue( UiConst.OnCheckedChange ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + ConfigForm().NzId().Name().AutoFocus().Disabled().Indeterminate().Checked() + .Value().Label().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Checkboxes/Builders/CheckboxGroupBuilder.cs b/src/Util.Ui.NgZorro/Components/Checkboxes/Builders/CheckboxGroupBuilder.cs new file mode 100644 index 000000000..713e8c940 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Checkboxes/Builders/CheckboxGroupBuilder.cs @@ -0,0 +1,36 @@ +using Util.Ui.NgZorro.Components.Base; + +namespace Util.Ui.NgZorro.Components.Checkboxes.Builders; + +/// +/// 复选框组合标签生成器 +/// +public class CheckboxGroupBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化复选框组合标签生成器 + /// + public CheckboxGroupBuilder( Config config ) : base( config,"nz-checkbox-group" ) { + _config = config; + } + + /// + /// 配置禁用 + /// + public CheckboxGroupBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + ConfigForm().Name().Disabled(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Checkboxes/Builders/CheckboxWrapperBuilder.cs b/src/Util.Ui.NgZorro/Components/Checkboxes/Builders/CheckboxWrapperBuilder.cs new file mode 100644 index 000000000..4c8a49a5c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Checkboxes/Builders/CheckboxWrapperBuilder.cs @@ -0,0 +1,37 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Checkboxes.Builders; + +/// +/// 复选框包装器标签生成器 +/// +public class CheckboxWrapperBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化复选框包装器标签生成器 + /// + /// 配置 + public CheckboxWrapperBuilder( Config config ) : base( config,"nz-checkbox-wrapper" ) { + _config = config; + } + + /// + /// 配置事件 + /// + public CheckboxWrapperBuilder Events() { + AttributeIfNotEmpty( "(nzOnChange)", _config.GetValue( UiConst.OnChange ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Checkboxes/CheckboxGroupTagHelper.cs b/src/Util.Ui.NgZorro/Components/Checkboxes/CheckboxGroupTagHelper.cs new file mode 100644 index 000000000..6246917bd --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Checkboxes/CheckboxGroupTagHelper.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Checkboxes.Helpers; +using Util.Ui.NgZorro.Components.Checkboxes.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Checkboxes; + +/// +/// 复选框组合,生成的标签为<nz-checkbox-group></nz-checkbox-group> +/// +[HtmlTargetElement( "util-checkbox-group" )] +public class CheckboxGroupTagHelper : FormControlTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// [nzDisabled],是否禁用全部复选框, 默认值: false + /// + public string Disabled { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new CheckboxService( _config ); + service.DisableAutoNzFor(); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new CheckboxGroupRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Checkboxes/CheckboxTagHelper.cs b/src/Util.Ui.NgZorro/Components/Checkboxes/CheckboxTagHelper.cs new file mode 100644 index 000000000..8e813b365 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Checkboxes/CheckboxTagHelper.cs @@ -0,0 +1,75 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Checkboxes.Helpers; +using Util.Ui.NgZorro.Components.Checkboxes.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Checkboxes; + +/// +/// 复选框,生成的标签为<label nz-checkbox></label> +/// +[HtmlTargetElement( "util-checkbox" )] +public class CheckboxTagHelper : FormControlTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// nzId,组件内部的 id + /// + public string NzId { get; set; } + /// + /// [nzId],组件内部的 id + /// + public string BindNzId { get; set; } + /// + /// [nzAutoFocus],是否自动获取焦点, 类型: boolean, 默认值: false + /// + public string AutoFocus { get; set; } + /// + /// [nzDisabled],是否禁用, 默认值: false + /// + public string Disabled { get; set; } + /// + /// [nzIndeterminate],是否中间状态,当复选框未全部选中或全部取消时,全选复选框的状态, 类型: boolean, 默认值: false + /// + public string Indeterminate { get; set; } + /// + /// nzValue,值,与 <util-checkbox-wrapper> 的 on-change 配合使用 + /// + public string Value { get; set; } + /// + /// [nzValue],值,与 <util-checkbox-wrapper> 的 on-change 配合使用 + /// + public string BindValue { get; set; } + /// + /// 扩展属性, 标签文本, 支持多语言 + /// + public string Label { get; set; } + /// + /// 扩展属性, 标签文本 + /// + public string BindLabel { get; set; } + /// + /// [nzChecked],是否选中, 类型: boolean, 默认值: false + /// + public string Checked { get; set; } + /// + /// (nzCheckedChange),选中状态变化事件, 类型: EventEmitter<boolean> + /// + public string OnCheckedChange { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new CheckboxService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new CheckboxRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Checkboxes/CheckboxWrapperTagHelper.cs b/src/Util.Ui.NgZorro/Components/Checkboxes/CheckboxWrapperTagHelper.cs new file mode 100644 index 000000000..728bc1e8d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Checkboxes/CheckboxWrapperTagHelper.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Checkboxes.Renders; +using Util.Ui.NgZorro.Components.Forms.Helpers; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Checkboxes; + +/// +/// 复选框包装器,生成的标签为<nz-checkbox-wrapper></nz-checkbox-wrapper> +/// +[HtmlTargetElement( "util-checkbox-wrapper" )] +public class CheckboxWrapperTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// (nzOnChange),选中数据变化事件,类型: EventEmitter<any[]> + /// + public string OnChange { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var formItemShareService = new FormItemShareService( _config ); + formItemShareService.Init(); + formItemShareService.InitId(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new CheckboxWrapperRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Checkboxes/Helpers/CheckboxExpressionLoader.cs b/src/Util.Ui.NgZorro/Components/Checkboxes/Helpers/CheckboxExpressionLoader.cs new file mode 100644 index 000000000..454423f0e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Checkboxes/Helpers/CheckboxExpressionLoader.cs @@ -0,0 +1,50 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Expressions; +using Util.Ui.NgZorro.Expressions; + +namespace Util.Ui.NgZorro.Components.Checkboxes.Helpers; + +/// +/// 复选框表达式加载器 +/// +public class CheckboxExpressionLoader : NgZorroExpressionLoaderBase { + /// + /// 加载模型信息 + /// + /// 配置 + /// 模型表达式信息 + protected override void Load( Config config, ModelExpressionInfo info ) { + LoadLabel( config, info ); + LoadId( config, info ); + LoadName( config, info ); + LoadNgModel( config, info ); + } + + /// + /// 加载标签 + /// + protected virtual void LoadLabel( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.LabelText, info.DisplayName, false ); + } + + /// + /// 加载标识 + /// + protected virtual void LoadId( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.Id, GeKebaberizePropertyName( config, info ), false ); + } + + /// + /// 加载名称 + /// + protected virtual void LoadName( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.Name, GeKebaberizePropertyName( config, info ), false ); + } + + /// + /// 加载模型绑定 + /// + protected virtual void LoadNgModel( Config config, ModelExpressionInfo info ) { + config.SetAttribute( AngularConst.NgModel, info.SafePropertyName, false ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Checkboxes/Helpers/CheckboxService.cs b/src/Util.Ui.NgZorro/Components/Checkboxes/Helpers/CheckboxService.cs new file mode 100644 index 000000000..f87003e88 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Checkboxes/Helpers/CheckboxService.cs @@ -0,0 +1,68 @@ +using Util.Ui.NgZorro.Components.Forms.Helpers; + +namespace Util.Ui.NgZorro.Components.Checkboxes.Helpers; + +/// +/// 复选框服务 +/// +public class CheckboxService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 是否禁用自动设置表单标签 nzFor + /// + private bool _isDisableAutoNzFor; + + /// + /// 初始化复选框服务 + /// + /// 配置 + public CheckboxService( Config config ) { + _config = config; + } + + /// + /// 禁用自动设置表单标签 nzFor + /// + public void DisableAutoNzFor() { + _isDisableAutoNzFor = true; + } + + /// + /// 初始化 + /// + public void Init() { + LoadExpression(); + InitFormShareService(); + InitFormItemShareService(); + } + + /// + /// 加载表达式 + /// + private void LoadExpression() { + var loader = new CheckboxExpressionLoader(); + loader.Load( _config ); + } + + /// + /// 初始化表单共享服务 + /// + private void InitFormShareService() { + var service = new FormShareService( _config ); + service.Init(); + } + + /// + /// 初始化表单项共享服务 + /// + private void InitFormItemShareService() { + var service = new FormItemShareService( _config ); + if ( _isDisableAutoNzFor ) + service.DisableAutoNzFor(); + service.Init(); + service.InitId(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Checkboxes/Renders/CheckboxGroupRender.cs b/src/Util.Ui.NgZorro/Components/Checkboxes/Renders/CheckboxGroupRender.cs new file mode 100644 index 000000000..ad2f110d0 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Checkboxes/Renders/CheckboxGroupRender.cs @@ -0,0 +1,39 @@ +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Checkboxes.Builders; + +namespace Util.Ui.NgZorro.Components.Checkboxes.Renders; + +/// +/// 复选框组合渲染器 +/// +public class CheckboxGroupRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化复选框组合渲染器 + /// + /// 配置 + public CheckboxGroupRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new CheckboxGroupBuilder( _config ); + builder.Config(); + _config.Content.AppendTo( builder ); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new CheckboxGroupRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Checkboxes/Renders/CheckboxRender.cs b/src/Util.Ui.NgZorro/Components/Checkboxes/Renders/CheckboxRender.cs new file mode 100644 index 000000000..c3f2d9bf6 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Checkboxes/Renders/CheckboxRender.cs @@ -0,0 +1,57 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Checkboxes.Builders; + +namespace Util.Ui.NgZorro.Components.Checkboxes.Renders; + +/// +/// 复选框渲染器 +/// +public class CheckboxRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化复选框渲染器 + /// + /// 配置 + public CheckboxRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 初始化 + /// + protected override void Init() { + SetControlId(); + } + + /// + /// 设置控件Id + /// + protected void SetControlId() { + if ( ShareConfig.AutoNzFor != true ) + return; + _config.SetAttribute( UiConst.NzId, ShareConfig.ControlId ); + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new CheckboxBuilder( _config ); + builder.Config(); + if( _config.Content.IsEmpty() == false ) + builder.SetContent( _config.Content ); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new CheckboxRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Checkboxes/Renders/CheckboxWrapperRender.cs b/src/Util.Ui.NgZorro/Components/Checkboxes/Renders/CheckboxWrapperRender.cs new file mode 100644 index 000000000..f865bb070 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Checkboxes/Renders/CheckboxWrapperRender.cs @@ -0,0 +1,39 @@ +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Checkboxes.Builders; + +namespace Util.Ui.NgZorro.Components.Checkboxes.Renders; + +/// +/// 复选框包装器渲染器 +/// +public class CheckboxWrapperRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化复选框包装器渲染器 + /// + /// 配置 + public CheckboxWrapperRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new CheckboxWrapperBuilder( _config ); + builder.Config(); + _config.Content.AppendTo( builder ); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new CheckboxWrapperRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Collapses/Builders/CollapseBuilder.cs b/src/Util.Ui.NgZorro/Components/Collapses/Builders/CollapseBuilder.cs new file mode 100644 index 000000000..11442fbf8 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Collapses/Builders/CollapseBuilder.cs @@ -0,0 +1,64 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Collapses.Builders; + +/// +/// 折叠标签生成器 +/// +public class CollapseBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化折叠标签生成器 + /// + /// 配置 + public CollapseBuilder( Config config ) : base( config,"nz-collapse" ) { + _config = config; + } + + /// + /// 配置是否手风琴 + /// + public CollapseBuilder Accordion() { + AttributeIfNotEmpty( "[nzAccordion]", _config.GetValue( UiConst.Accordion ) ); + return this; + } + + /// + /// 配置是否有边框 + /// + public CollapseBuilder Bordered() { + AttributeIfNotEmpty( "[nzBordered]", _config.GetValue( UiConst.Bordered ) ); + return this; + } + + /// + /// 配置是否幽灵面板 + /// + public CollapseBuilder Ghost() { + AttributeIfNotEmpty( "[nzGhost]", _config.GetValue( UiConst.Ghost ) ); + return this; + } + + /// + /// 配置图标位置 + /// + public CollapseBuilder ExpandIconPosition() { + AttributeIfNotEmpty( "nzExpandIconPosition", _config.GetValue( UiConst.ExpandIconPosition )?.Description() ); + AttributeIfNotEmpty( "[nzExpandIconPosition]", _config.GetValue( AngularConst.BindExpandIconPosition ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Accordion().Bordered().Ghost().ExpandIconPosition(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Collapses/Builders/CollapsePanelBuilder.cs b/src/Util.Ui.NgZorro/Components/Collapses/Builders/CollapsePanelBuilder.cs new file mode 100644 index 000000000..525f9bda4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Collapses/Builders/CollapsePanelBuilder.cs @@ -0,0 +1,91 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Collapses.Builders; + +/// +/// 折叠面板标签生成器 +/// +public class CollapsePanelBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化折叠面板标签生成器 + /// + /// 配置 + public CollapsePanelBuilder( Config config ) : base( config,"nz-collapse-panel" ) { + _config = config; + } + + /// + /// 配置是否禁用 + /// + public CollapsePanelBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置面板头内容 + /// + public CollapsePanelBuilder Header() { + AttributeIfNotEmpty( "nzHeader", _config.GetValue( UiConst.Header ) ); + AttributeIfNotEmpty( "[nzHeader]", _config.GetValue( AngularConst.BindHeader ) ); + return this; + } + + /// + /// 配置图标 + /// + public CollapsePanelBuilder ExpandedIcon() { + AttributeIfNotEmpty( "nzExpandedIcon", _config.GetValue( UiConst.ExpandedIcon )?.Description() ); + AttributeIfNotEmpty( "[nzExpandedIcon]", _config.GetValue( AngularConst.BindExpandedIcon ) ); + return this; + } + + /// + /// 配置右上角额外内容 + /// + public CollapsePanelBuilder Extra() { + AttributeIfNotEmpty( "nzExtra", _config.GetValue( UiConst.Extra ) ); + AttributeIfNotEmpty( "[nzExtra]", _config.GetValue( AngularConst.BindExtra ) ); + return this; + } + + /// + /// 配置是否显示箭头 + /// + public CollapsePanelBuilder ShowArrow() { + AttributeIfNotEmpty( "[nzShowArrow]", _config.GetValue( UiConst.ShowArrow ) ); + return this; + } + + /// + /// 配置是否展开面板 + /// + public CollapsePanelBuilder Active() { + AttributeIfNotEmpty( "[nzActive]", _config.GetValue( UiConst.Active ) ); + AttributeIfNotEmpty( "[(nzActive)]", _config.GetValue( AngularConst.BindonActive ) ); + return this; + } + + /// + /// 配置事件 + /// + public CollapsePanelBuilder Events() { + AttributeIfNotEmpty( "(nzActiveChange)", _config.GetValue( UiConst.OnActiveChange ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Disabled().Header().ExpandedIcon().Extra().ShowArrow().Active().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Collapses/CollapsePanelTagHelper.cs b/src/Util.Ui.NgZorro/Components/Collapses/CollapsePanelTagHelper.cs new file mode 100644 index 000000000..79b525319 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Collapses/CollapsePanelTagHelper.cs @@ -0,0 +1,64 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Collapses.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Collapses; + +/// +/// 折叠面板,生成的标签为<nz-collapse-panel></nz-collapse-panel> +/// +[HtmlTargetElement( "util-collapse-panel" )] +public class CollapsePanelTagHelper : AngularTagHelperBase { + /// + /// [nzDisabled],是否禁用, 类型: boolean, 默认值: false + /// + public string Disabled { get; set; } + /// + /// nzHeader,面板头内容,类型: string | TemplateRef<void> + /// + public string Header { get; set; } + /// + /// [nzHeader],面板头内容,类型: string | TemplateRef<void> + /// + public string BindHeader { get; set; } + /// + /// nzExpandedIcon,自定义切换图标,类型: string | TemplateRef<void> + /// + public AntDesignIcon ExpandedIcon { get; set; } + /// + /// [nzExpandedIcon],自定义切换图标,类型: string | TemplateRef<void> + /// + public string BindExpandedIcon { get; set; } + /// + /// nzExtra,自定义面板右上角区域内容,类型: string | TemplateRef<void> + /// + public string Extra { get; set; } + /// + /// [nzExtra],自定义面板右上角区域内容,类型: string | TemplateRef<void> + /// + public string BindExtra { get; set; } + /// + /// [nzShowArrow],是否显示箭头, 类型: boolean, 默认值: true + /// + public string ShowArrow { get; set; } + /// + /// [nzActive],是否展开面板, 类型: boolean, 默认值: false + /// + public string Active { get; set; } + /// + /// [(nzActive)],是否展开面板, 类型: boolean, 默认值: false + /// + public string BindonActive { get; set; } + /// + /// (nzActiveChange),面板展开变化事件,类型: EventEmitter<boolean> + /// + public string OnActiveChange { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new CollapsePanelRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Collapses/CollapseTagHelper.cs b/src/Util.Ui.NgZorro/Components/Collapses/CollapseTagHelper.cs new file mode 100644 index 000000000..4059b956c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Collapses/CollapseTagHelper.cs @@ -0,0 +1,40 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Collapses.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Collapses; + +/// +/// 折叠,生成的标签为<nz-collapse></nz-collapse> +/// +[HtmlTargetElement( "util-collapse" )] +public class CollapseTagHelper : AngularTagHelperBase { + /// + /// [nzAccordion],是否手风琴,即只能展开一个面板,类型: boolean, 默认值: false + /// + public string Accordion { get; set; } + /// + /// [nzBordered],是否显示边框,类型: boolean, 默认值: true + /// + public string Bordered { get; set; } + /// + /// [nzGhost],是否使折叠面板透明且无边框,类型: boolean, 默认值: false + /// + public string Ghost { get; set; } + /// + /// nzExpandIconPosition,折叠图标位置,可选值: 'left' | 'right' ,默认值: 'left' + /// + public CollapseIconPosition ExpandIconPosition { get; set; } + /// + /// [nzExpandIconPosition],折叠图标位置,可选值: 'left' | 'right' ,默认值: 'left' + /// + public string BindExpandIconPosition { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new CollapseRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Collapses/Renders/CollapsePanelRender.cs b/src/Util.Ui.NgZorro/Components/Collapses/Renders/CollapsePanelRender.cs new file mode 100644 index 000000000..62b379e07 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Collapses/Renders/CollapsePanelRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Collapses.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Collapses.Renders; + +/// +/// 折叠面板渲染器 +/// +public class CollapsePanelRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化折叠面板渲染器 + /// + /// 配置 + public CollapsePanelRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new CollapsePanelBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new CollapsePanelRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Collapses/Renders/CollapseRender.cs b/src/Util.Ui.NgZorro/Components/Collapses/Renders/CollapseRender.cs new file mode 100644 index 000000000..2f8d6b608 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Collapses/Renders/CollapseRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Collapses.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Collapses.Renders; + +/// +/// 折叠渲染器 +/// +public class CollapseRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化折叠渲染器 + /// + /// 配置 + public CollapseRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new CollapseBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new CollapseRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/ColorPickers/Builders/ColorBlockBuilder.cs b/src/Util.Ui.NgZorro/Components/ColorPickers/Builders/ColorBlockBuilder.cs new file mode 100644 index 000000000..7a9f31888 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/ColorPickers/Builders/ColorBlockBuilder.cs @@ -0,0 +1,57 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.ColorPickers.Builders; + +/// +/// 颜色块标签生成器 +/// +public class ColorBlockBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化颜色块标签生成器 + /// + /// 配置 + public ColorBlockBuilder( Config config ) : base( config, "nz-color-block" ) { + _config = config; + } + + /// + /// 配置颜色 + /// + public ColorBlockBuilder Color() { + AttributeIfNotEmpty( "nzColor", _config.GetValue( UiConst.Color ) ); + AttributeIfNotEmpty( "[nzColor]", _config.GetValue( AngularConst.BindColor ) ); + return this; + } + + /// + /// 配置控件尺寸 + /// + public ColorBlockBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置事件 + /// + public ColorBlockBuilder Events() { + AttributeIfNotEmpty( "(nzOnClick)", _config.GetValue( UiConst.OnClick ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Color().Size().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/ColorPickers/Builders/ColorPickerBuilder.cs b/src/Util.Ui.NgZorro/Components/ColorPickers/Builders/ColorPickerBuilder.cs new file mode 100644 index 000000000..74801e753 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/ColorPickers/Builders/ColorPickerBuilder.cs @@ -0,0 +1,154 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.ColorPickers.Builders; + +/// +/// 颜色选择标签生成器 +/// +public class ColorPickerBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化颜色选择标签生成器 + /// + /// 配置 + public ColorPickerBuilder( Config config ) : base( config, "nz-color-picker" ) { + _config = config; + } + + /// + /// 配置标题 + /// + public ColorPickerBuilder Title() { + SetTitle( _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 设置表单标签文本 + /// + private void SetTitle( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzTitle]", value ); + return; + } + AttributeIfNotEmpty( "nzTitle", value ); + } + + /// + /// 配置颜色默认值 + /// + public ColorPickerBuilder DefaultValue() { + AttributeIfNotEmpty( "nzDefaultValue", _config.GetValue( UiConst.DefaultValue ) ); + AttributeIfNotEmpty( "[nzDefaultValue]", _config.GetValue( AngularConst.BindDefaultValue ) ); + return this; + } + + /// + /// 配置颜色值 + /// + public ColorPickerBuilder Value() { + AttributeIfNotEmpty( "nzValue", _config.GetValue( UiConst.Value ) ); + AttributeIfNotEmpty( "[nzValue]", _config.GetValue( AngularConst.BindValue ) ); + return this; + } + + /// + /// 配置显示颜色文本 + /// + public ColorPickerBuilder ShowText() { + AttributeIfNotEmpty( "[nzShowText]", _config.GetValue( UiConst.ShowText ) ); + return this; + } + + /// + /// 配置控件尺寸 + /// + public ColorPickerBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置禁用 + /// + public ColorPickerBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置禁用透明度 + /// + public ColorPickerBuilder DisabledAlpha() { + AttributeIfNotEmpty( "[nzDisabledAlpha]", _config.GetValue( UiConst.DisabledAlpha ) ); + return this; + } + + /// + /// 配置触发模式 + /// + public ColorPickerBuilder Trigger() { + AttributeIfNotEmpty( "nzTrigger", _config.GetValue( UiConst.Trigger )?.Description() ); + AttributeIfNotEmpty( "[nzTrigger]", _config.GetValue( AngularConst.BindTrigger ) ); + AttributeIfNotEmpty( "[nzFlipFlop]", _config.GetValue( UiConst.FlipFlop ) ); + return this; + } + + /// + /// 配置允许清除 + /// + public ColorPickerBuilder AllowClear() { + AttributeIfNotEmpty( "[nzAllowClear]", _config.GetValue( UiConst.AllowClear ) ); + return this; + } + + /// + /// 配置颜色格式 + /// + public ColorPickerBuilder Format() { + AttributeIfNotEmpty( "nzFormat", _config.GetValue( UiConst.Format )?.Description() ); + AttributeIfNotEmpty( "[nzFormat]", _config.GetValue( AngularConst.BindFormat ) ); + return this; + } + + /// + /// 配置显示弹出窗口 + /// + public ColorPickerBuilder Open() { + AttributeIfNotEmpty( "[nzOpen]", _config.GetValue( UiConst.Open ) ); + return this; + } + + /// + /// 配置事件 + /// + public ColorPickerBuilder Events() { + AttributeIfNotEmpty( "(nzOnChange)", _config.GetValue( UiConst.OnChange ) ); + AttributeIfNotEmpty( "(nzOnClear)", _config.GetValue( UiConst.OnClear ) ); + AttributeIfNotEmpty( "(nzOnFormatChange)", _config.GetValue( UiConst.OnFormatChange ) ); + AttributeIfNotEmpty( "(nzOnOpenChange)", _config.GetValue( UiConst.OnOpenChange ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + ConfigForm().Name().DefaultValue().Value() + .Title().ShowText().Size().Disabled().DisabledAlpha() + .Trigger().AllowClear().Format().Open() + .Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/ColorPickers/ColorBlockTagHelper.cs b/src/Util.Ui.NgZorro/Components/ColorPickers/ColorBlockTagHelper.cs new file mode 100644 index 000000000..ebc0d02d0 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/ColorPickers/ColorBlockTagHelper.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.ColorPickers.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.ColorPickers; + +/// +/// 颜色块,生成的标签为<nz-color-block></nz-color-block> +/// +[HtmlTargetElement( "util-color-block" )] +public class ColorBlockTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// nzColor, 颜色值, 默认值: #1677ff + /// + public string Color { get; set; } + /// + /// [nzColor], 颜色值, 默认值: #1677ff + /// + public string BindColor { get; set; } + /// + /// nzSize,颜色块尺寸, 可选值: 'default' | 'small' | 'large' , 默认值: 'default' + /// + public InputSize Size { get; set; } + /// + /// [nzSize],颜色块尺寸, 可选值: 'default' | 'small' | 'large' , 默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// (nzOnClick),点击事件 ,类型: EventEmitter<boolean> + /// + public string OnClick { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new ColorBlockRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/ColorPickers/ColorPickerTagHelper.cs b/src/Util.Ui.NgZorro/Components/ColorPickers/ColorPickerTagHelper.cs new file mode 100644 index 000000000..e7113c68e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/ColorPickers/ColorPickerTagHelper.cs @@ -0,0 +1,121 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.ColorPickers.Renders; +using Util.Ui.NgZorro.Components.Selects.Helpers; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.ColorPickers; + +/// +/// 颜色选择器,生成的标签为<nz-color-picker></nz-color-picker> +/// +[HtmlTargetElement( "util-color-picker" )] +public class ColorPickerTagHelper : FormControlTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// nzTitle,标题,支持多语言 + /// + public string Title { get; set; } + /// + /// [nzTitle],标题,类型: string|TemplateRef<void> + /// + public string BindTitle { get; set; } + /// + /// nzDefaultValue, 颜色默认值, 类型: string|NzColor + /// + public string DefaultValue { get; set; } + /// + /// [nzDefaultValue], 颜色默认值, 类型: string|NzColor + /// + public string BindDefaultValue { get; set; } + /// + /// nzValue, 颜色值, 类型: string|NzColor + /// + public string Value { get; set; } + /// + /// [nzValue], 颜色值, 类型: string|NzColor + /// + public string BindValue { get; set; } + /// + /// [nzShowText],是否显示颜色文本,类型: boolean, 默认值: false + /// + public string ShowText { get; set; } + /// + /// nzSize,触发器尺寸, 可选值: 'default' | 'small' | 'large' , 默认值: 'default' + /// + public InputSize Size { get; set; } + /// + /// [nzSize],触发器尺寸, 可选值: 'default' | 'small' | 'large' , 默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// [nzDisabled],是否禁用,类型: boolean, 默认值: false + /// + public string Disabled { get; set; } + /// + /// [nzDisabledAlpha],是否禁用透明度, 类型: boolean, 默认值: false + /// + public string DisabledAlpha { get; set; } + /// + /// nzTrigger,触发模式, 可选值: hover|click ,默认值: click + /// + public ColorPickerTrigger Trigger { get; set; } + /// + /// [nzTrigger],触发模式, 可选值: hover|click ,默认值: click + /// + public string BindTrigger { get; set; } + /// + /// [nzFlipFlop],自定义触发元素, 类型: TemplateRef<void> + /// + public string FlipFlop { get; set; } + /// + /// [nzAllowClear], 是否允许清除选择的颜色, 类型: boolean, 默认值: false + /// + public string AllowClear { get; set; } + /// + /// nzFormat,颜色格式, 可选值: rgb|hex|hsb ,默认值: hex + /// + public ColorPickerFormat Format { get; set; } + /// + /// [nzFormat],颜色格式, 可选值: rgb|hex|hsb ,默认值: hex + /// + public string BindFormat { get; set; } + /// + /// [nzOpen],是否显示弹出窗口 ,类型: boolean, 默认值: false + /// + public string Open { get; set; } + /// + /// (nzOnChange),颜色变化事件 ,类型: EventEmitter<{color: NzColor;format: string}> + /// + public string OnChange { get; set; } + /// + /// (nzOnClear),清除颜色事件 ,类型: EventEmitter<boolean> + /// + public string OnClear { get; set; } + /// + /// (nzOnFormatChange),颜色格式变化事件 ,类型: EventEmitter<'rgb'|'hex'|'hsb'> + /// + public string OnFormatChange { get; set; } + /// + /// (nzOnOpenChange),打开颜色面板事件 ,类型: EventEmitter<boolean> + /// + public string OnOpenChange { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new SelectService( _config ); + service.DisableAutoNzFor(); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new ColorPickerRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/ColorPickers/Renders/ColorBlockRender.cs b/src/Util.Ui.NgZorro/Components/ColorPickers/Renders/ColorBlockRender.cs new file mode 100644 index 000000000..8f76b83a7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/ColorPickers/Renders/ColorBlockRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.ColorPickers.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.ColorPickers.Renders; + +/// +/// 颜色块渲染器 +/// +public class ColorBlockRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化颜色块渲染器 + /// + /// 配置 + public ColorBlockRender( Config config ){ + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ColorBlockBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ColorBlockRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/ColorPickers/Renders/ColorPickerRender.cs b/src/Util.Ui.NgZorro/Components/ColorPickers/Renders/ColorPickerRender.cs new file mode 100644 index 000000000..ff0a2b57a --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/ColorPickers/Renders/ColorPickerRender.cs @@ -0,0 +1,39 @@ +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.ColorPickers.Builders; + +namespace Util.Ui.NgZorro.Components.ColorPickers.Renders; + +/// +/// 颜色选择渲染器 +/// +public class ColorPickerRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化颜色选择渲染器 + /// + /// 配置 + public ColorPickerRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new ColorPickerBuilder( _config ); + builder.Config(); + _config.Content.AppendTo( builder ); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new ColorPickerRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Comments/Builders/CommentActionBuilder.cs b/src/Util.Ui.NgZorro/Components/Comments/Builders/CommentActionBuilder.cs new file mode 100644 index 000000000..990457bdf --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Comments/Builders/CommentActionBuilder.cs @@ -0,0 +1,22 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Comments.Builders; + +/// +/// 评论操作标签生成器 +/// +public class CommentActionBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化评论操作标签生成器 + /// + /// 配置 + public CommentActionBuilder( Config config ) : base( config,"nz-comment-action" ) { + _config = config; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Comments/Builders/CommentBuilder.cs b/src/Util.Ui.NgZorro/Components/Comments/Builders/CommentBuilder.cs new file mode 100644 index 000000000..e3f6ecb0e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Comments/Builders/CommentBuilder.cs @@ -0,0 +1,47 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; + +namespace Util.Ui.NgZorro.Components.Comments.Builders; + +/// +/// 评论标签生成器 +/// +public class CommentBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化评论标签生成器 + /// + public CommentBuilder( Config config ) : base( config,"nz-comment" ) { + _config = config; + } + + /// + /// 配置作者 + /// + public CommentBuilder Author() { + AttributeIfNotEmpty( "nzAuthor", _config.GetValue( UiConst.Author ) ); + AttributeIfNotEmpty( "[nzAuthor]", _config.GetValue( AngularConst.BindAuthor ) ); + return this; + } + + /// + /// 配置作者 + /// + public CommentBuilder Datetime() { + AttributeIfNotEmpty( "nzDatetime", _config.GetValue( UiConst.Datetime ) ); + AttributeIfNotEmpty( "[nzDatetime]", _config.GetValue( AngularConst.BindDatetime ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Author().Datetime(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Comments/Builders/CommentContentBuilder.cs b/src/Util.Ui.NgZorro/Components/Comments/Builders/CommentContentBuilder.cs new file mode 100644 index 000000000..babc4692d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Comments/Builders/CommentContentBuilder.cs @@ -0,0 +1,22 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Comments.Builders; + +/// +/// 评论内容标签生成器 +/// +public class CommentContentBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化评论内容标签生成器 + /// + /// 配置 + public CommentContentBuilder( Config config ) : base( config,"nz-comment-content" ) { + _config = config; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Comments/CommentActionTagHelper.cs b/src/Util.Ui.NgZorro/Components/Comments/CommentActionTagHelper.cs new file mode 100644 index 000000000..20e6b57e6 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Comments/CommentActionTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Comments.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Comments; + +/// +/// 评论操作,生成的标签为<nz-comment-action></nz-comment-action> +/// +[HtmlTargetElement( "util-comment-action" )] +public class CommentActionTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new CommentActionRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Comments/CommentContentTagHelper.cs b/src/Util.Ui.NgZorro/Components/Comments/CommentContentTagHelper.cs new file mode 100644 index 000000000..d95a3940b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Comments/CommentContentTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Comments.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Comments; + +/// +/// 评论内容,生成的标签为<nz-comment-content></nz-comment-content> +/// +[HtmlTargetElement( "util-comment-content" )] +public class CommentContentTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new CommentContentRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Comments/CommentTagHelper.cs b/src/Util.Ui.NgZorro/Components/Comments/CommentTagHelper.cs new file mode 100644 index 000000000..dbefc8924 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Comments/CommentTagHelper.cs @@ -0,0 +1,46 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Comments.Configs; +using Util.Ui.NgZorro.Components.Comments.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Comments; + +/// +/// 评论,生成的标签为<nz-comment></nz-comment> +/// +[HtmlTargetElement( "util-comment" )] +public class CommentTagHelper : AngularTagHelperBase { + /// + /// nzAuthor,作者,类型: string | TemplateRef<void> + /// + public string Author { get; set; } + /// + /// [nzAuthor],作者,类型: string | TemplateRef<void> + /// + public string BindAuthor { get; set; } + /// + /// nzDatetime,时间描述,类型: string | TemplateRef<void> + /// + public string Datetime { get; set; } + /// + /// [nzDatetime],时间描述,类型: string | TemplateRef<void> + /// + public string BindDatetime { get; set; } + + /// + /// 渲染前操作 + /// + /// 上下文 + /// 输出 + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + context.SetValueToItems( new CommentShareConfig() ); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new CommentRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Comments/Configs/CommentShareConfig.cs b/src/Util.Ui.NgZorro/Components/Comments/Configs/CommentShareConfig.cs new file mode 100644 index 000000000..d05fb4156 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Comments/Configs/CommentShareConfig.cs @@ -0,0 +1,7 @@ +namespace Util.Ui.NgZorro.Components.Comments.Configs; + +/// +/// 评论共享配置 +/// +public class CommentShareConfig { +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Comments/Renders/CommentActionRender.cs b/src/Util.Ui.NgZorro/Components/Comments/Renders/CommentActionRender.cs new file mode 100644 index 000000000..812d0e3e6 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Comments/Renders/CommentActionRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Comments.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Comments.Renders; + +/// +/// 评论操作渲染器 +/// +public class CommentActionRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化评论操作渲染器 + /// + /// 配置 + public CommentActionRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new CommentActionBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new CommentActionRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Comments/Renders/CommentContentRender.cs b/src/Util.Ui.NgZorro/Components/Comments/Renders/CommentContentRender.cs new file mode 100644 index 000000000..4af1f5a76 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Comments/Renders/CommentContentRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Comments.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Comments.Renders; + +/// +/// 评论内容渲染器 +/// +public class CommentContentRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化评论内容渲染器 + /// + /// 配置 + public CommentContentRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new CommentContentBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new CommentContentRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Comments/Renders/CommentRender.cs b/src/Util.Ui.NgZorro/Components/Comments/Renders/CommentRender.cs new file mode 100644 index 000000000..95ee9eb47 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Comments/Renders/CommentRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Comments.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Comments.Renders; + +/// +/// 评论渲染器 +/// +public class CommentRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化评论渲染器 + /// + /// 配置 + public CommentRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new CommentBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new CommentRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Containers/Builders/ContainerBuilder.cs b/src/Util.Ui.NgZorro/Components/Containers/Builders/ContainerBuilder.cs new file mode 100644 index 000000000..5ffd2a438 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Containers/Builders/ContainerBuilder.cs @@ -0,0 +1,33 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Containers.Builders; + +/// +/// ng-container标签生成器 +/// +public class ContainerBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化ng-container标签生成器 + /// + /// 配置 + public ContainerBuilder( Config config ) : base( config, "ng-container" ) { + _config = config; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + this.SpaceItem(_config); + AttributeIfNotEmpty( "*ngTemplateOutlet", _config.GetValue( AngularConst.NgTemplateOutlet ) ); + AttributeIfNotEmpty( "*nzMentionSuggestion", _config.GetValue( UiConst.MentionSuggestion ) ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Containers/Builders/RouterOutletBuilder.cs b/src/Util.Ui.NgZorro/Components/Containers/Builders/RouterOutletBuilder.cs new file mode 100644 index 000000000..705037268 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Containers/Builders/RouterOutletBuilder.cs @@ -0,0 +1,15 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Containers.Builders; + +/// +/// router-outlet标签生成器 +/// +public class RouterOutletBuilder : AngularTagBuilder { + /// + /// 初始化router-outlet标签生成器 + /// + /// 配置 + public RouterOutletBuilder( Config config ) : base( config, "router-outlet" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Containers/ContainerTagHelper.cs b/src/Util.Ui.NgZorro/Components/Containers/ContainerTagHelper.cs new file mode 100644 index 000000000..6b4af6e1e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Containers/ContainerTagHelper.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Containers.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Containers; + +/// +/// 容器,生成的标签为<ng-container></ng-container> +/// +[HtmlTargetElement( "util-container" )] +public class ContainerTagHelper : AngularTagHelperBase { + /// + /// *ngTemplateOutlet,模板出口 + /// + public string NgTemplateOutlet { get; set; } + /// + /// *nzMentionSuggestion,提及建议渲染模板 + /// + public string MentionSuggestion { get; set; } + /// + /// *nzSpaceItem,值为 true 时设置为间距项,放入 <util-space> 中使用 + /// + public bool SpaceItem { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ContainerRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Containers/Renders/ContainerRender.cs b/src/Util.Ui.NgZorro/Components/Containers/Renders/ContainerRender.cs new file mode 100644 index 000000000..5f1938db3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Containers/Renders/ContainerRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Containers.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Containers.Renders; + +/// +/// ng-container容器渲染器 +/// +public class ContainerRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化ng-container容器渲染器 + /// + /// 配置 + public ContainerRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ContainerBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ContainerRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Containers/Renders/RouterOutletRender.cs b/src/Util.Ui.NgZorro/Components/Containers/Renders/RouterOutletRender.cs new file mode 100644 index 000000000..ca91e032a --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Containers/Renders/RouterOutletRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Containers.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Containers.Renders; + +/// +/// router-outlet路由出口渲染器 +/// +public class RouterOutletRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化router-outlet路由出口渲染器 + /// + /// 配置 + public RouterOutletRender( Config config ) { + _config = config ?? throw new ArgumentNullException( nameof( config ) ); + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new RouterOutletBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new RouterOutletRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Containers/RouterOutletTagHelper.cs b/src/Util.Ui.NgZorro/Components/Containers/RouterOutletTagHelper.cs new file mode 100644 index 000000000..b1238ac71 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Containers/RouterOutletTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Containers.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Containers; + +/// +/// 路由出口,生成的标签为<router-outlet></router-outlet> +/// +[HtmlTargetElement( "util-router-outlet" )] +public class RouterOutletTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new RouterOutletRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/DatePickers/Builders/DatePickerBuilder.cs b/src/Util.Ui.NgZorro/Components/DatePickers/Builders/DatePickerBuilder.cs new file mode 100644 index 000000000..36956d051 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/DatePickers/Builders/DatePickerBuilder.cs @@ -0,0 +1,284 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.DatePickers.Builders; + +/// +/// 日期选择标签生成器 +/// +public class DatePickerBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化日期选择标签生成器 + /// + /// 配置 + public DatePickerBuilder( Config config ) : base( config, "nz-date-picker" ) { + _config = config; + } + + /// + /// 配置输入框标识 + /// + public DatePickerBuilder NzId() { + AttributeIfNotEmpty( "nzId", _config.GetValue( UiConst.NzId ) ); + AttributeIfNotEmpty( "[nzId]", _config.GetValue( AngularConst.BindNzId ) ); + return this; + } + + /// + /// 配置允许清除 + /// + public DatePickerBuilder AllowClear() { + AttributeIfNotEmpty( "[nzAllowClear]", _config.GetValue( UiConst.AllowClear ) ); + return this; + } + + /// + /// 配置自动聚焦 + /// + public DatePickerBuilder AutoFocus() { + AttributeIfNotEmpty( "[nzAutoFocus]", _config.GetValue( UiConst.AutoFocus ) ); + return this; + } + + /// + /// 配置浮层是否应带有背景板 + /// + public DatePickerBuilder Backdrop() { + AttributeIfNotEmpty( "[nzBackdrop]", _config.GetValue( UiConst.Backdrop ) ); + return this; + } + + /// + /// 配置默认面板日期 + /// + public DatePickerBuilder DefaultPickerValue() { + AttributeIfNotEmpty( "nzDefaultPickerValue", _config.GetValue( UiConst.DefaultPickerValue ) ); + AttributeIfNotEmpty( "[nzDefaultPickerValue]", _config.GetValue( AngularConst.BindDefaultPickerValue ) ); + return this; + } + + /// + /// 配置禁用 + /// + public DatePickerBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置不可选择日期函数 + /// + public DatePickerBuilder DisabledDate() { + AttributeIfNotEmpty( "[nzDisabledDate]", _config.GetValue( UiConst.DisabledDate ) ); + return this; + } + + /// + /// 配置不可选择时间函数 + /// + public DatePickerBuilder DisabledTime() { + AttributeIfNotEmpty( "[nzDisabledTime]", _config.GetValue( UiConst.DisabledTime ) ); + return this; + } + + /// + /// 配置弹出日历样式类名 + /// + public DatePickerBuilder DropdownClassName() { + AttributeIfNotEmpty( "nzDropdownClassName", _config.GetValue( UiConst.DropdownClassName ) ); + AttributeIfNotEmpty( "[nzDropdownClassName]", _config.GetValue( AngularConst.BindDropdownClassName ) ); + return this; + } + + /// + /// 配置弹出日历样式 + /// + public DatePickerBuilder PopupStyle() { + AttributeIfNotEmpty( "[nzPopupStyle]", _config.GetValue( UiConst.PopupStyle ) ); + return this; + } + + /// + /// 配置日期格式 + /// + public DatePickerBuilder Format() { + var format = _config.GetValue( UiConst.Format ); + if ( format.IsEmpty() == false ) { + Attribute( "nzFormat", format ); + return this; + } + var bindFormat = _config.GetValue( AngularConst.BindFormat ); + if ( bindFormat.IsEmpty() == false ) { + Attribute( "[nzFormat]", bindFormat ); + return this; + } + if ( _config.GetValue( UiConst.Mode ) == DatePickerMode.Week ) + Attribute( "nzFormat", "yyyy-ww" ); + return this; + } + + /// + /// 配置只读 + /// + public DatePickerBuilder InputReadonly() { + AttributeIfNotEmpty( "[nzInputReadOnly]", _config.GetValue( UiConst.InputReadonly ) ); + return this; + } + + /// + /// 配置国际化 + /// + public DatePickerBuilder Locale() { + AttributeIfNotEmpty( "[nzLocale]", _config.GetValue( UiConst.Locale ) ); + return this; + } + + /// + /// 配置模式 + /// + public DatePickerBuilder Mode() { + AttributeIfNotEmpty( "nzMode", _config.GetValue( UiConst.Mode )?.Description() ); + AttributeIfNotEmpty( "[nzMode]", _config.GetValue( AngularConst.BindMode ) ); + return this; + } + + /// + /// 配置占位提示 + /// + public DatePickerBuilder Placeholder() { + AttributeIfNotEmpty( "nzPlaceHolder", _config.GetValue( UiConst.Placeholder ) ); + AttributeIfNotEmpty( "[nzPlaceHolder]", _config.GetValue( AngularConst.BindPlaceholder ) ); + return this; + } + + /// + /// 配置额外页脚 + /// + public DatePickerBuilder RenderExtraFooter() { + AttributeIfNotEmpty( "nzRenderExtraFooter", _config.GetValue( UiConst.RenderExtraFooter ) ); + AttributeIfNotEmpty( "[nzRenderExtraFooter]", _config.GetValue( AngularConst.BindRenderExtraFooter ) ); + return this; + } + + /// + /// 配置输入框大小 + /// + public DatePickerBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置后缀图标 + /// + public DatePickerBuilder SuffixIcon() { + AttributeIfNotEmpty( "nzSuffixIcon", _config.GetValue( UiConst.SuffixIcon )?.Description() ); + AttributeIfNotEmpty( "[nzSuffixIcon]", _config.GetValue( AngularConst.BindSuffixIcon ) ); + return this; + } + + /// + /// 配置移除边框 + /// + public DatePickerBuilder Borderless() { + AttributeIfNotEmpty( "[nzBorderless]", _config.GetValue( UiConst.Borderless ) ); + return this; + } + + /// + /// 配置内联模式 + /// + public DatePickerBuilder Inline() { + AttributeIfNotEmpty( "[nzInline]", _config.GetValue( UiConst.Inline ) ); + return this; + } + + /// + /// 配置显示周数 + /// + public DatePickerBuilder ShowWeekNumber() { + AttributeIfNotEmpty( "[nzShowWeekNumber]", _config.GetValue( UiConst.ShowWeekNumber ) ); + return this; + } + + /// + /// 配置自定义日期单元格内容 + /// + public DatePickerBuilder DateRender() { + AttributeIfNotEmpty( "[nzDateRender]", _config.GetValue( UiConst.DateRender ) ); + return this; + } + + /// + /// 配置显示时间选择 + /// + public DatePickerBuilder ShowTime() { + AttributeIfNotEmpty( "[nzShowTime]", _config.GetValue( UiConst.ShowTime ) ); + return this; + } + + /// + /// 配置显示“今天”按钮 + /// + public DatePickerBuilder ShowToday() { + AttributeIfNotEmpty( "[nzShowToday]", _config.GetValue( UiConst.ShowToday ) ); + return this; + } + + /// + /// 配置显示“此刻”按钮 + /// + public DatePickerBuilder ShowNow() { + AttributeIfNotEmpty( "[nzShowNow]", _config.GetValue( UiConst.ShowNow ) ); + return this; + } + + /// + /// 配置校验状态 + /// + public DatePickerBuilder Status() { + AttributeIfNotEmpty( "nzStatus", _config.GetValue( UiConst.Status )?.Description() ); + AttributeIfNotEmpty( "[nzStatus]", _config.GetValue( AngularConst.BindStatus ) ); + return this; + } + + /// + /// 配置校验状态 + /// + public DatePickerBuilder Placement() { + AttributeIfNotEmpty( "nzPlacement", _config.GetValue( UiConst.Placement )?.Description() ); + AttributeIfNotEmpty( "[nzPlacement]", _config.GetValue( AngularConst.BindPlacement ) ); + return this; + } + + /// + /// 配置事件 + /// + public DatePickerBuilder Events() { + AttributeIfNotEmpty( "(nzOnOpenChange)", _config.GetValue( UiConst.OnOpenChange ) ); + AttributeIfNotEmpty( "(nzOnOk)", _config.GetValue( UiConst.OnOk ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + ConfigForm().Name().NzId().AllowClear().AutoFocus() + .Backdrop().DefaultPickerValue() + .Disabled().DisabledDate().DisabledTime() + .DropdownClassName().PopupStyle().Format().InputReadonly().Locale().Mode() + .Placeholder().RenderExtraFooter().Size().SuffixIcon().Borderless().Inline() + .ShowWeekNumber() + .DateRender().ShowTime().ShowToday().ShowNow().Status().Placement() + .Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/DatePickers/Builders/RangePickerBuilder.cs b/src/Util.Ui.NgZorro/Components/DatePickers/Builders/RangePickerBuilder.cs new file mode 100644 index 000000000..27da188ba --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/DatePickers/Builders/RangePickerBuilder.cs @@ -0,0 +1,335 @@ +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Enums; +using Config = Util.Ui.Configs.Config; + +namespace Util.Ui.NgZorro.Components.DatePickers.Builders; + +/// +/// 日期范围选择标签生成器 +/// +public class RangePickerBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 标识 + /// + private string _id; + + /// + /// 初始化日期范围选择标签生成器 + /// + /// 配置 + public RangePickerBuilder( Config config ) : base( config, "nz-range-picker" ) { + _config = config; + } + + /// + /// 配置输入框标识 + /// + public RangePickerBuilder NzId() { + AttributeIfNotEmpty( "nzId", _config.GetValue( UiConst.NzId ) ); + AttributeIfNotEmpty( "[nzId]", _config.GetValue( AngularConst.BindNzId ) ); + return this; + } + + /// + /// 配置允许清除 + /// + public RangePickerBuilder AllowClear() { + AttributeIfNotEmpty( "[nzAllowClear]", _config.GetValue( UiConst.AllowClear ) ); + return this; + } + + /// + /// 配置自动聚焦 + /// + public RangePickerBuilder AutoFocus() { + AttributeIfNotEmpty( "[nzAutoFocus]", _config.GetValue( UiConst.AutoFocus ) ); + return this; + } + + /// + /// 配置浮层是否应带有背景板 + /// + public RangePickerBuilder Backdrop() { + AttributeIfNotEmpty( "[nzBackdrop]", _config.GetValue( UiConst.Backdrop ) ); + return this; + } + + /// + /// 配置默认面板日期 + /// + public RangePickerBuilder DefaultPickerValue() { + AttributeIfNotEmpty( "nzDefaultPickerValue", _config.GetValue( UiConst.DefaultPickerValue ) ); + AttributeIfNotEmpty( "[nzDefaultPickerValue]", _config.GetValue( AngularConst.BindDefaultPickerValue ) ); + return this; + } + + /// + /// 配置禁用 + /// + public RangePickerBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置不可选择日期函数 + /// + public RangePickerBuilder DisabledDate() { + AttributeIfNotEmpty( "[nzDisabledDate]", _config.GetValue( UiConst.DisabledDate ) ); + return this; + } + + /// + /// 配置不可选择时间函数 + /// + public RangePickerBuilder DisabledTime() { + AttributeIfNotEmpty( "[nzDisabledTime]", _config.GetValue( UiConst.DisabledTime ) ); + return this; + } + + /// + /// 配置弹出日历样式类名 + /// + public RangePickerBuilder DropdownClassName() { + AttributeIfNotEmpty( "nzDropdownClassName", _config.GetValue( UiConst.DropdownClassName ) ); + AttributeIfNotEmpty( "[nzDropdownClassName]", _config.GetValue( AngularConst.BindDropdownClassName ) ); + return this; + } + + /// + /// 配置弹出日历样式 + /// + public RangePickerBuilder PopupStyle() { + AttributeIfNotEmpty( "[nzPopupStyle]", _config.GetValue( UiConst.PopupStyle ) ); + return this; + } + + /// + /// 配置日期格式 + /// + public RangePickerBuilder Format() { + AttributeIfNotEmpty( "nzFormat", _config.GetValue( UiConst.Format ) ); + AttributeIfNotEmpty( "[nzFormat]", _config.GetValue( AngularConst.BindFormat ) ); + return this; + } + + /// + /// 配置只读 + /// + public RangePickerBuilder InputReadonly() { + AttributeIfNotEmpty( "[nzInputReadOnly]", _config.GetValue( UiConst.InputReadonly ) ); + return this; + } + + /// + /// 配置国际化 + /// + public RangePickerBuilder Locale() { + AttributeIfNotEmpty( "[nzLocale]", _config.GetValue( UiConst.Locale ) ); + return this; + } + + /// + /// 配置模式 + /// + public RangePickerBuilder Mode() { + AttributeIfNotEmpty( "nzMode", _config.GetValue( UiConst.Mode )?.Description() ); + AttributeIfNotEmpty( "[nzMode]", _config.GetValue( AngularConst.BindMode ) ); + return this; + } + + /// + /// 配置占位提示 + /// + public RangePickerBuilder Placeholder() { + AttributeIfNotEmpty( "nzPlaceHolder", _config.GetValue( UiConst.Placeholder ) ); + AttributeIfNotEmpty( "[nzPlaceHolder]", _config.GetValue( AngularConst.BindPlaceholder ) ); + return this; + } + + /// + /// 配置额外页脚 + /// + public RangePickerBuilder RenderExtraFooter() { + AttributeIfNotEmpty( "nzRenderExtraFooter", _config.GetValue( UiConst.RenderExtraFooter ) ); + AttributeIfNotEmpty( "[nzRenderExtraFooter]", _config.GetValue( AngularConst.BindRenderExtraFooter ) ); + return this; + } + + /// + /// 配置自定义日期单元格内容 + /// + public RangePickerBuilder DateRender() { + AttributeIfNotEmpty( "[nzDateRender]", _config.GetValue( UiConst.DateRender ) ); + return this; + } + + /// + /// 配置输入框大小 + /// + public RangePickerBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置后缀图标 + /// + public RangePickerBuilder SuffixIcon() { + AttributeIfNotEmpty( "nzSuffixIcon", _config.GetValue( UiConst.SuffixIcon )?.Description() ); + AttributeIfNotEmpty( "[nzSuffixIcon]", _config.GetValue( AngularConst.BindSuffixIcon ) ); + return this; + } + + /// + /// 配置移除边框 + /// + public RangePickerBuilder Borderless() { + AttributeIfNotEmpty( "[nzBorderless]", _config.GetValue( UiConst.Borderless ) ); + return this; + } + + /// + /// 配置内联模式 + /// + public RangePickerBuilder Inline() { + AttributeIfNotEmpty( "[nzInline]", _config.GetValue( UiConst.Inline ) ); + return this; + } + + /// + /// 配置显示周数 + /// + public RangePickerBuilder ShowWeekNumber() { + AttributeIfNotEmpty( "[nzShowWeekNumber]", _config.GetValue( UiConst.ShowWeekNumber ) ); + return this; + } + + /// + /// 配置预设时间范围 + /// + public RangePickerBuilder Ranges() { + AttributeIfNotEmpty( "[nzRanges]", _config.GetValue( UiConst.Ranges ) ); + return this; + } + + /// + /// 配置分隔符 + /// + public RangePickerBuilder Separator() { + AttributeIfNotEmpty( "nzSeparator", _config.GetValue( UiConst.Separator ) ); + AttributeIfNotEmpty( "[nzSeparator]", _config.GetValue( AngularConst.BindSeparator ) ); + return this; + } + + /// + /// 配置显示时间选择 + /// + public RangePickerBuilder ShowTime() { + AttributeIfNotEmpty( "[nzShowTime]", _config.GetValue( UiConst.ShowTime ) ); + return this; + } + + /// + /// 配置校验状态 + /// + public RangePickerBuilder Status() { + AttributeIfNotEmpty( "nzStatus", _config.GetValue( UiConst.Status )?.Description() ); + AttributeIfNotEmpty( "[nzStatus]", _config.GetValue( AngularConst.BindStatus ) ); + return this; + } + + /// + /// 配置校验状态 + /// + public RangePickerBuilder Placement() { + AttributeIfNotEmpty( "nzPlacement", _config.GetValue( UiConst.Placement )?.Description() ); + AttributeIfNotEmpty( "[nzPlacement]", _config.GetValue( AngularConst.BindPlacement ) ); + return this; + } + + /// + /// 配置事件 + /// + public RangePickerBuilder Events() { + AttributeIfNotEmpty( "(nzOnOpenChange)", _config.GetValue( UiConst.OnOpenChange ) ); + AttributeIfNotEmpty( "(nzOnCalendarChange)", _config.GetValue( UiConst.OnCalendarChange ) ); + AttributeIfNotEmpty( "(nzOnOk)", _config.GetValue( UiConst.OnOk ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + ConfigForm().NzId().Name().AllowClear().AutoFocus() + .Backdrop().DefaultPickerValue() + .Disabled().DisabledDate().DisabledTime() + .DropdownClassName().PopupStyle() + .Format().InputReadonly().Locale().Mode() + .Placeholder().RenderExtraFooter().DateRender() + .Size().SuffixIcon().Borderless().Inline().ShowWeekNumber() + .Ranges().Separator().ShowTime().Status().Placement() + .Events(); + EnableExtend(); + } + + /// + /// 启用扩展 + /// + private void EnableExtend() { + if ( IsEnableExtend() == false ) + return; + Attribute( "x-range-picker-extend" ); + Attribute( $"#{ExtendId}", "xRangePickerExtend" ); + Attribute( "[(ngModel)]", $"{ExtendId}.rangeDates", true ); + BeginDate(); + EndDate(); + } + + /// + /// 扩展标识 + /// + private string ExtendId { + get { + if ( _id.IsEmpty() ) + _id = _config.GetValue( UiConst.Id ); + if ( _id.IsEmpty() ) + _id = Id.Create(); + return $"x_{_id}"; + } + } + + /// + /// 是否启用扩展 + /// + private bool IsEnableExtend() { + if ( _config.Contains( UiConst.BeginDate ) ) + return true; + if ( _config.Contains( UiConst.EndDate ) ) + return true; + return false; + } + + /// + /// 配置起始日期 + /// + private void BeginDate() { + AttributeIfNotEmpty( "[(beginDate)]", _config.GetValue( UiConst.BeginDate ) ); + } + + /// + /// 配置结束日期 + /// + private void EndDate() { + AttributeIfNotEmpty( "[(endDate)]", _config.GetValue( UiConst.EndDate ) ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/DatePickers/DatePickerTagHelper.cs b/src/Util.Ui.NgZorro/Components/DatePickers/DatePickerTagHelper.cs new file mode 100644 index 000000000..c40337868 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/DatePickers/DatePickerTagHelper.cs @@ -0,0 +1,55 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.DatePickers.Renders; +using Util.Ui.NgZorro.Components.Inputs.Helpers; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.DatePickers; + +/// +/// 日期选择,生成的标签为<nz-date-picker></nz-date-picker> +/// +[HtmlTargetElement( "util-date-picker" )] +public class DatePickerTagHelper : DatePickerTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// nzDefaultPickerValue,默认面板日期,类型: Date + /// + public string DefaultPickerValue { get; set; } + /// + /// [nzDefaultPickerValue],默认面板日期,类型: Date + /// + public string BindDefaultPickerValue { get; set; } + /// + /// [nzDisabledTime],不可选择时间函数,类型:(current: Date) => { nzDisabledHours, nzDisabledMinutes, nzDisabledSeconds } + /// + public string DisabledTime { get; set; } + /// + /// [nzShowToday],是否显示“今天”按钮,注意:仅在 nzMode="date" 时有效 + /// + public string ShowToday { get; set; } + /// + /// [nzShowNow],是否显示“此刻”按钮,注意:仅在 nzMode="date",且 [nzShowTime]="true" 时有效 + /// + public string ShowNow { get; set; } + /// + /// (nzOnOk),点击确定按钮事件, 类型: EventEmitter<Date> + /// + public string OnOk { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new InputService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new DatePickerRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/DatePickers/Helpers/RangePickerExpressionLoader.cs b/src/Util.Ui.NgZorro/Components/DatePickers/Helpers/RangePickerExpressionLoader.cs new file mode 100644 index 000000000..9c161dff1 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/DatePickers/Helpers/RangePickerExpressionLoader.cs @@ -0,0 +1,69 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Expressions; +using Util.Ui.NgZorro.Expressions; + +namespace Util.Ui.NgZorro.Components.DatePickers.Helpers; + +/// +/// 日期范围选择表达式加载器 +/// +public class RangePickerExpressionLoader : NgZorroExpressionLoaderBase { + /// + /// 加载模型信息 + /// + /// 配置 + /// 模型表达式信息 + protected override void Load( Config config, ModelExpressionInfo info ) { + LoadLabel( config, info ); + LoadId( config, info ); + LoadName( config, info ); + LoadDate( config, info ); + LoadRequired( config, info ); + } + + /// + /// 加载标签 + /// + protected virtual void LoadLabel( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.LabelText, info.DisplayName, false ); + } + + /// + /// 加载标识 + /// + protected virtual void LoadId( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.Id, GeKebaberizePropertyName( config, info ), false ); + } + + /// + /// 加载名称 + /// + protected virtual void LoadName( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.Name, GeKebaberizePropertyName( config, info ), false ); + } + + /// + /// 加载日期 + /// + protected virtual void LoadDate( Config config, ModelExpressionInfo info ) { + config.SetAttribute( AngularConst.NgModel, info.SafePropertyName ); + if (info.ExpressionPropertyName == UiConst.ForBegin) { + config.SetAttribute( UiConst.BeginDate, info.SafePropertyName ); + return; + } + if ( info.ExpressionPropertyName == UiConst.ForEnd ) { + config.SetAttribute( UiConst.EndDate, info.SafePropertyName ); + return; + } + } + + /// + /// 加载必填项验证 + /// + protected virtual void LoadRequired( Config config, ModelExpressionInfo info ) { + if ( info.IsRequired == false ) + return; + config.SetAttribute( UiConst.Required, "true", false ); + config.SetAttribute( UiConst.RequiredMessage, info.RequiredMessage, false ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/DatePickers/Helpers/RangePickerService.cs b/src/Util.Ui.NgZorro/Components/DatePickers/Helpers/RangePickerService.cs new file mode 100644 index 000000000..8739cf573 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/DatePickers/Helpers/RangePickerService.cs @@ -0,0 +1,35 @@ +namespace Util.Ui.NgZorro.Components.DatePickers.Helpers; + +/// +/// 日期范围选择服务 +/// +public class RangePickerService { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化日期范围选择服务 + /// + /// 配置 + public RangePickerService( Config config ) { + _config = config; + } + + /// + /// 初始化 + /// + public void Init() { + LoadExpression(); + } + + /// + /// 加载表达式 + /// + private void LoadExpression() { + var loader = new RangePickerExpressionLoader(); + loader.Load( _config, UiConst.ForBegin ); + loader.Load( _config, UiConst.ForEnd ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/DatePickers/RangePickerTagHelper.cs b/src/Util.Ui.NgZorro/Components/DatePickers/RangePickerTagHelper.cs new file mode 100644 index 000000000..c43d0215e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/DatePickers/RangePickerTagHelper.cs @@ -0,0 +1,83 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.DatePickers.Helpers; +using Util.Ui.NgZorro.Components.DatePickers.Renders; +using Util.Ui.NgZorro.Components.Inputs.Helpers; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.DatePickers; + +/// +/// 日期范围选择,生成的标签为<nz-range-picker></nz-range-picker> +/// +[HtmlTargetElement( "util-range-picker" )] +public class RangePickerTagHelper : DatePickerTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// nzDefaultPickerValue,默认面板日期,类型: Date[] + /// + public string DefaultPickerValue { get; set; } + /// + /// [nzDefaultPickerValue],默认面板日期,类型: Date[] + /// + public string BindDefaultPickerValue { get; set; } + /// + /// [nzDisabledTime],不可选择时间函数,类型:(current: Date, partial: 'start' | 'end') => { nzDisabledHours, nzDisabledMinutes, nzDisabledSeconds } + /// + public string DisabledTime { get; set; } + /// + /// [nzRanges],预设时间范围,类型:{ [ key: string ]: Date[] } | { [ key: string ]: () => Date[] } + /// + public string Ranges { get; set; } + /// + /// nzSeparator,分隔符,默认值:'~' + /// + public string Separator { get; set; } + /// + /// [nzSeparator],分隔符,默认值:'~' + /// + public string BindSeparator { get; set; } + /// + /// [(beginDate)],扩展属性, 双向绑定起始日期 + /// + public string BeginDate { get; set; } + /// + /// [(endDate)],扩展属性, 双向绑定结束日期 + /// + public string EndDate { get; set; } + /// + /// 起始日期属性表达式 + /// + public ModelExpression ForBegin { get; set; } + /// + /// 结束日期属性表达式 + /// + public ModelExpression ForEnd { get; set; } + /// + /// (nzOnCalendarChange),待选日期变化事件, 类型: EventEmitter<Date[]> + /// + public string OnCalendarChange { get; set; } + /// + /// (nzOnOk),点击确定按钮事件, 类型: EventEmitter<Date[]> + /// + public string OnOk { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var rangePickerService = new RangePickerService( _config ); + rangePickerService.Init(); + var service = new InputService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new RangePickerRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/DatePickers/Renders/DatePickerRender.cs b/src/Util.Ui.NgZorro/Components/DatePickers/Renders/DatePickerRender.cs new file mode 100644 index 000000000..48fc8f8ba --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/DatePickers/Renders/DatePickerRender.cs @@ -0,0 +1,55 @@ +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.DatePickers.Builders; + +namespace Util.Ui.NgZorro.Components.DatePickers.Renders; + +/// +/// 日期选择渲染器 +/// +public class DatePickerRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化日期选择渲染器 + /// + /// 配置 + public DatePickerRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 初始化 + /// + protected override void Init() { + SetControlId(); + } + + /// + /// 设置控件Id + /// + protected void SetControlId() { + if ( ShareConfig.AutoNzFor != true ) + return; + _config.SetAttribute( UiConst.NzId, ShareConfig.ControlId ); + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new DatePickerBuilder( _config ); + builder.Config(); + _config.Content.AppendTo( builder ); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new DatePickerRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/DatePickers/Renders/RangePickerRender.cs b/src/Util.Ui.NgZorro/Components/DatePickers/Renders/RangePickerRender.cs new file mode 100644 index 000000000..ac1a937a0 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/DatePickers/Renders/RangePickerRender.cs @@ -0,0 +1,55 @@ +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.DatePickers.Builders; + +namespace Util.Ui.NgZorro.Components.DatePickers.Renders; + +/// +/// 日期范围选择渲染器 +/// +public class RangePickerRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化日期范围选择渲染器 + /// + /// 配置 + public RangePickerRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 初始化 + /// + protected override void Init() { + SetControlId(); + } + + /// + /// 设置控件Id + /// + protected void SetControlId() { + if ( ShareConfig.AutoNzFor != true ) + return; + _config.SetAttribute( UiConst.NzId, ShareConfig.ControlId ); + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new RangePickerBuilder( _config ); + builder.Config(); + _config.Content.AppendTo( builder ); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new RangePickerRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Descriptions/Builders/DescriptionBuilder.cs b/src/Util.Ui.NgZorro/Components/Descriptions/Builders/DescriptionBuilder.cs new file mode 100644 index 000000000..712cbf9b9 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Descriptions/Builders/DescriptionBuilder.cs @@ -0,0 +1,128 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Grids.Helpers; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Descriptions.Builders; + +/// +/// 描述列表标签生成器 +/// +public class DescriptionBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化描述列表标签生成器 + /// + /// 配置 + public DescriptionBuilder( Config config ) : base( config, "nz-descriptions" ) { + _config = config; + } + + /// + /// 配置标题 + /// + public DescriptionBuilder Title() { + SetTitle( _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 设置标题 + /// + private void SetTitle( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzTitle]", value ); + return; + } + AttributeIfNotEmpty( "nzTitle", value ); + } + + /// + /// 配置操作区域 + /// + public DescriptionBuilder Extra() { + AttributeIfNotEmpty( "nzExtra", _config.GetValue( UiConst.Extra ) ); + AttributeIfNotEmpty( "[nzExtra]", _config.GetValue( AngularConst.BindExtra ) ); + return this; + } + + /// + /// 配置是否显示边框 + /// + public DescriptionBuilder Bordered() { + AttributeIfNotEmpty( "[nzBordered]", _config.GetValue( UiConst.Bordered ) ); + return this; + } + + /// + /// 配置一行包含的描述列表项数量 + /// + public DescriptionBuilder Column() { + var column = _config.GetValue( UiConst.Column ); + if ( column.IsEmpty() == false ) + return Column( column ); + var value = _config.GetValue( UiConst.XxlColumn ); + if ( value == null ) + return this; + var model = new ColumnModel { + Xs = 1, + Sm = 1, + Md = 1, + Lg = 1, + Xl = 2, + Xxl = value + }; + Column( model.ToJson() ); + return this; + } + + /// + /// 配置一行包含的描述列表项数量 + /// + public DescriptionBuilder Column( string column ) { + AttributeIfNotEmpty( "[nzColumn]", column ); + return this; + } + + /// + /// 配置列表大小 + /// + public DescriptionBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置列表布局 + /// + public DescriptionBuilder Layout() { + AttributeIfNotEmpty( "nzLayout", _config.GetValue( UiConst.Layout )?.Description() ); + AttributeIfNotEmpty( "[nzLayout]", _config.GetValue( AngularConst.BindLayout ) ); + return this; + } + + /// + /// 配置是否显示冒号 + /// + public DescriptionBuilder Colon() { + AttributeIfNotEmpty( "[nzColon]", _config.GetValue( UiConst.Colon ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Title().Extra().Bordered().Column().Size().Layout().Colon(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Descriptions/Builders/DescriptionItemBuilder.cs b/src/Util.Ui.NgZorro/Components/Descriptions/Builders/DescriptionItemBuilder.cs new file mode 100644 index 000000000..2c650013c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Descriptions/Builders/DescriptionItemBuilder.cs @@ -0,0 +1,154 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Buttons.Builders; +using Util.Ui.NgZorro.Components.Icons.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Descriptions.Builders; + +/// +/// 描述列表项标签生成器 +/// +public class DescriptionItemBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化描述列表项标签生成器 + /// + /// 配置 + public DescriptionItemBuilder( Config config ) : base( config, "nz-descriptions-item" ) { + _config = config; + } + + /// + /// 配置标题 + /// + public DescriptionItemBuilder Title() { + SetTitle( _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 设置标题 + /// + private void SetTitle( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzTitle]", value ); + return; + } + AttributeIfNotEmpty( "nzTitle", value ); + } + + /// + /// 配置列跨度 + /// + public DescriptionItemBuilder Span() { + AttributeIfNotEmpty( "nzSpan", _config.GetValue( UiConst.Span ) ); + AttributeIfNotEmpty( "[nzSpan]", _config.GetValue( AngularConst.BindSpan ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Title().Span(); + } + + /// + /// 配置内容 + /// + protected override void ConfigContent( Config config ) { + if ( config.Content.IsEmpty() == false ) { + config.Content.AppendTo( this ); + return; + } + ConfigValue(); + CopyToClipboard(); + } + + /// + /// 配置值 + /// + private void ConfigValue() { + var dataType = _config.GetValue( UiConst.Type ); + var value = _config.GetValue( UiConst.Value ); + if ( value.IsEmpty() ) + return; + if ( dataType == DataType.Bool ) { + LoadBool( value ); + return; + } + if ( dataType == DataType.Date ) { + LoadDate( value ); + return; + } + if ( dataType == DataType.Number ) { + LoadNumber( value ); + return; + } + SetContent( "{{" + GetValue( value ) + "}}" ); + } + + /// + /// 加载布尔值 + /// + protected void LoadBool( string value ) { + AppendContent( new IconBuilder( _config ).Type( AntDesignIcon.Check.Description() ).Attribute( "*ngIf", $"{value}" ) ); + AppendContent( new IconBuilder( _config ).Type( AntDesignIcon.Close.Description() ).Attribute( "*ngIf", $"!({value})" ) ); + } + + /// + /// 加载日期值 + /// + protected void LoadDate( string value ) { + var format = _config.GetValue( UiConst.DateFormat ); + if ( format.IsEmpty() ) + format = "yyyy-MM-dd HH:mm:ss"; + SetContent( $"{{{{{value}|date:\"{format}\"}}}}" ); + } + + /// + /// 加载数值 + /// + protected void LoadNumber( string value ) { + SetContent( "{{" + value + "}}" ); + } + + /// + /// 获取值 + /// + private string GetValue( string value ) { + var enabledI18n = _config.GetValue( UiConst.ValueI18n ); + if ( enabledI18n ) + return $"{value}|i18n"; + return value; + } + + /// + /// 配置复制到剪贴板 + /// + private void CopyToClipboard() { + if ( _config.GetValue( UiConst.Clipboard ) != true ) + return; + var value = _config.GetValue( UiConst.Value ); + if ( value.IsEmpty() ) + return; + _config.SetAttribute( UiConst.CopyToClipboard, value ); + var buttonBuilder = new ButtonBuilder( _config ); + buttonBuilder.Attribute( "nz-button" ); + buttonBuilder.NgIf( value ); + buttonBuilder.CopyToClipboard().Type().Icon(); + AppendContent( buttonBuilder ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Descriptions/DescriptionItemTagHelper.cs b/src/Util.Ui.NgZorro/Components/Descriptions/DescriptionItemTagHelper.cs new file mode 100644 index 000000000..ad34fa6da --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Descriptions/DescriptionItemTagHelper.cs @@ -0,0 +1,83 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Descriptions.Renders; +using Util.Ui.NgZorro.Components.Display.Helpers; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Descriptions; + +/// +/// 描述列表项,生成的标签为<nz-descriptions-item></nz-descriptions-item> +/// +[HtmlTargetElement( "util-descriptions-item" )] +public class DescriptionItemTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 扩展属性, 属性表达式 + /// + public ModelExpression For { get; set; } + /// + /// nzTitle,标题,支持多语言 + /// + public string Title { get; set; } + /// + /// [nzTitle],标题,类型: string|TemplateRef<void> + /// + public string BindTitle { get; set; } + /// + /// 扩展属性,内容变量值的数据类型 + /// + public DescriptionDataType Type { get; set; } + /// + /// 扩展属性,内容值,自动包含在 {{}} 中,范例: 设置为 model.name, 内容为 {{model.name}} + /// + public string Value { get; set; } + /// + /// 扩展属性,值是否支持多语言,设置为 true 添加 i18n 管道,默认值: false, 范例: value="model.name",value-i18n="true", 内容为 {{model.name|i18n}} + /// + public bool ValueI18n { get; set; } + /// + /// [nzSpan],包含列的数量, 类型: number, 默认值: 1 + /// + public string Span { get; set; } + /// + /// 日期格式化字符串,默认值: yyyy-MM-dd HH:mm:ss,仅在 type="Date" 时有效,格式说明: + /// 1. 年 - yyyy + /// 2. 月 - MM + /// 3. 日 - dd + /// 4. 时 - HH + /// 5. 分 - mm + /// 6. 秒 - ss + /// 7. 毫秒 - SSS + /// + public string DateFormat { get; set; } + /// + /// 扩展属性 [cdkCopyToClipboard],是否允许复制到剪贴板,注意: 需要设置 value 属性,默认值: false + /// + public bool Clipboard { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + LoadExpression(); + } + + /// + /// 加载表达式 + /// + private void LoadExpression() { + var loader = new DisplayExpressionLoader(); + loader.Load( _config ); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new DescriptionItemRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Descriptions/DescriptionTagHelper.cs b/src/Util.Ui.NgZorro/Components/Descriptions/DescriptionTagHelper.cs new file mode 100644 index 000000000..1c2ba186e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Descriptions/DescriptionTagHelper.cs @@ -0,0 +1,68 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Descriptions.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Descriptions; + +/// +/// 描述列表,生成的标签为<nz-descriptions></nz-descriptions> +/// +[HtmlTargetElement( "util-descriptions" )] +public class DescriptionTagHelper : AngularTagHelperBase { + /// + /// nzTitle,标题,显示在最顶部,支持多语言 + /// + public string Title { get; set; } + /// + /// [nzTitle],标题,显示在最顶部,类型: string|TemplateRef<void> + /// + public string BindTitle { get; set; } + /// + /// nzExtra,操作区域,显示在右上方,类型: string|TemplateRef<void> + /// + public string Extra { get; set; } + /// + /// [nzExtra],操作区域,显示在右上方,类型: string|TemplateRef<void> + /// + public string BindExtra { get; set; } + /// + /// [nzBordered],是否显示边框,类型: boolean, 默认值:false + /// + public string Bordered { get; set; } + /// + /// [nzColumn],一行包含的描述列表项数量,可以是数字,也可以写成响应式对象,类型: number|object,范例:{ xs: 8, sm: 16, md: 24},默认值: { xxl: 3, xl: 3, lg: 3, md: 3, sm: 2, xs: 1 } + /// + public string Column { get; set; } + /// + /// 扩展属性 [nzColumn],当 ≥1600px 极宽尺寸,一行包含的描述列表项数量,生成 {xs:1,sm:1,md:1,lg:1,xl:2,xxl:value} + /// + public int XxlColumn { get; set; } + /// + /// nzSize,列表尺寸,显示边框时生效,类型: 'default' | 'middle' | 'small',默认值: 'default' + /// + public DescriptionSize Size { get; set; } + /// + /// [nzSize],列表尺寸,显示边框时生效,类型: 'default' | 'middle' | 'small',默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// nzLayout,列表布局,类型: 'horizontal' | 'vertical',默认值: 'horizontal' + /// + public DescriptionLayout Layout { get; set; } + /// + /// [nzLayout],列表布局,类型: 'horizontal' | 'vertical',默认值: 'horizontal' + /// + public string BindLayout { get; set; } + /// + /// [nzColon],是否在标题后显示冒号,类型: boolean, 默认值:true + /// + public string Colon { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new DescriptionRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Descriptions/Renders/DescriptionItemRender.cs b/src/Util.Ui.NgZorro/Components/Descriptions/Renders/DescriptionItemRender.cs new file mode 100644 index 000000000..aa5ab4d94 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Descriptions/Renders/DescriptionItemRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Descriptions.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Descriptions.Renders; + +/// +/// 描述列表项渲染器 +/// +public class DescriptionItemRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化描述列表项渲染器 + /// + /// 配置 + public DescriptionItemRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new DescriptionItemBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new DescriptionItemRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Descriptions/Renders/DescriptionRender.cs b/src/Util.Ui.NgZorro/Components/Descriptions/Renders/DescriptionRender.cs new file mode 100644 index 000000000..f5a7f3695 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Descriptions/Renders/DescriptionRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Descriptions.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Descriptions.Renders; + +/// +/// 描述列表渲染器 +/// +public class DescriptionRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化描述列表渲染器 + /// + /// 配置 + public DescriptionRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new DescriptionBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new DescriptionRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Display/Builders/DisplayBuilder.cs b/src/Util.Ui.NgZorro/Components/Display/Builders/DisplayBuilder.cs new file mode 100644 index 000000000..c8f6355cb --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Display/Builders/DisplayBuilder.cs @@ -0,0 +1,128 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Buttons.Builders; +using Util.Ui.NgZorro.Components.Typographies.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Display.Builders; + +/// +/// 数据项展示标签生成器 +/// +public class DisplayBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化数据项展示标签生成器 + /// + /// 配置 + public DisplayBuilder( Config config ) : base( config, "span" ) { + _config = config; + } + + /// + /// 配置值 + /// + public DisplayBuilder Value() { + var dataType = _config.GetValue( UiConst.Type ); + var value = _config.GetValue( UiConst.Value ); + if ( value.IsEmpty() ) + return this; + if ( dataType == DataType.Bool ) { + LoadBool( value ); + return this; + } + if ( dataType == DataType.Date ) { + LoadDate( value ); + return this; + } + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) + SetContent( "{{" + $"{value}|i18n" + "}}" ); + else + SetContent( "{{" + value + "}}" ); + return this; + } + + /// + /// 加载布尔值 + /// + protected void LoadBool( string value ) { + var options = NgZorroOptionsService.GetOptions(); + var result = new StringBuilder(); + result.Append( "{{" ); + if ( options.EnableI18n ) + result.Append( $"({value}?'{I18nKeys.Yes}':'{I18nKeys.No}')|i18n" ); + else + result.Append( $"{value}?'是':'否'" ); + result.Append( "}}" ); + SetContent( result.ToString() ); + } + + /// + /// 加载日期值 + /// + protected void LoadDate( string value ) { + var format = _config.GetValue( UiConst.DateFormat ); + if ( format.IsEmpty() ) + format = "yyyy-MM-dd HH:mm:ss"; + SetContent( $"{{{{{value}|date:\"{format}\"}}}}" ); + } + + /// + /// 配置显示标签 + /// + public DisplayBuilder ShowLabel() { + var showLabel = _config.GetValue( UiConst.ShowLabel ); + if ( showLabel != true ) + return this; + var title = _config.GetValue( UiConst.Title ); + return SetLabel( title ); + } + + /// + /// 配置标签 + /// + private DisplayBuilder SetLabel( string value ) { + if ( value.IsEmpty() ) + return this; + var options = NgZorroOptionsService.GetOptions(); + var labelBuilder = new SpanBuilder( _config ); + labelBuilder.Class( "mr-sm" ); + if ( options.EnableI18n ) { + labelBuilder.AppendContentByI18n( value ); + } else { + labelBuilder.AppendContent( value ); + } + labelBuilder.AppendContent( ":" ); + PreBuilder = labelBuilder; + return this; + } + + /// + public override void Config() { + base.Config(); + Value().ShowLabel(); + CopyToClipboard(); + } + + /// + /// 配置复制到剪贴板 + /// + public void CopyToClipboard() { + if ( _config.GetValue( UiConst.Clipboard ) != true ) + return; + var value = _config.GetValue( UiConst.Value ); + _config.SetAttribute( UiConst.CopyToClipboard, value ); + var buttonBuilder = new ButtonBuilder( _config ); + buttonBuilder.Attribute( "nz-button" ); + buttonBuilder.NgIf( value ); + buttonBuilder.CopyToClipboard().Type().Icon(); + PostBuilder = buttonBuilder; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Display/DisplayTagHelper.cs b/src/Util.Ui.NgZorro/Components/Display/DisplayTagHelper.cs new file mode 100644 index 000000000..6a251f4a6 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Display/DisplayTagHelper.cs @@ -0,0 +1,91 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Display.Helpers; +using Util.Ui.NgZorro.Components.Display.Renders; +using Util.Ui.NgZorro.Components.Forms.Helpers; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Display; + +/// +/// 数据项展示,生成的标签为<span></span> +/// +[HtmlTargetElement( "util-display" )] +public class DisplayTagHelper : FormContainerTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 属性表达式 + /// + public ModelExpression For { get; set; } + /// + /// 值,范例: 设置 a,结果为 {{a}} + /// + public string Value { get; set; } + /// + /// 数据类型 + /// + public DataType Type { get; set; } + /// + /// 是否显示左侧的标签,默认不显示,注意:设置For属性时生效 + /// + public bool ShowLabel { get; set; } + /// + /// 日期格式化字符串,默认值: yyyy-MM-dd HH:mm:ss,仅在使用属性表达式For时有效,格式说明: + /// 1. 年 - yyyy + /// 2. 月 - MM + /// 3. 日 - dd + /// 4. 时 - HH + /// 5. 分 - mm + /// 6. 秒 - ss + /// 7. 毫秒 - SSS + /// + public string DateFormat { get; set; } + /// + /// [cdkCopyToClipboard],是否允许复制到剪贴板 + /// + public bool Clipboard { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + LoadExpression(); + InitFormShareService(); + InitFormItemShareService(); + } + + /// + /// 加载表达式 + /// + private void LoadExpression() { + var loader = new DisplayExpressionLoader(); + loader.Load( _config ); + } + + /// + /// 初始化表单共享服务 + /// + private void InitFormShareService() { + var service = new FormShareService( _config ); + service.Init(); + } + + /// + /// 初始化表单项共享服务 + /// + private void InitFormItemShareService() { + var service = new FormItemShareService( _config ); + service.Init(); + service.InitId(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new DisplayRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Display/Helpers/DisplayExpressionLoader.cs b/src/Util.Ui.NgZorro/Components/Display/Helpers/DisplayExpressionLoader.cs new file mode 100644 index 000000000..3b7e27183 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Display/Helpers/DisplayExpressionLoader.cs @@ -0,0 +1,60 @@ +using Util.Ui.Expressions; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Expressions; + +namespace Util.Ui.NgZorro.Components.Display.Helpers; + +/// +/// 数据项展示表达式加载器 +/// +public class DisplayExpressionLoader : NgZorroExpressionLoaderBase { + /// + /// 加载模型信息 + /// + /// 配置 + /// 模型表达式信息 + protected override void Load( Config config, ModelExpressionInfo info ) { + LoadTitle( config, info ); + LoadValue( config, info ); + } + + /// + /// 加载标题 + /// + protected void LoadTitle( Config config, ModelExpressionInfo info ) { + if ( IsEnabledFormLabel( config ) ) { + config.SetAttribute( UiConst.LabelText, info.DisplayName, false ); + return; + } + config.SetAttribute( UiConst.Title, info.DisplayName, false ); + } + + /// + /// 是否启用表单标签 + /// + private bool IsEnabledFormLabel( Config config ) { + if ( config.GetValueFromItems() != null && config.GetValue( UiConst.ShowLabel ) == true ) + return true; + return false; + } + + /// + /// 加载值 + /// + protected virtual void LoadValue( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.Value, info.SafePropertyName, false ); + if ( info.IsBool ) { + config.SetAttribute( UiConst.Type, DataType.Bool ); + return; + } + if ( info.IsDate ) { + config.SetAttribute( UiConst.Type, DataType.Date ); + return; + } + if ( info.IsNumber ) { + config.SetAttribute( UiConst.Type, DataType.Number ); + return; + } + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Display/Renders/DisplayRender.cs b/src/Util.Ui.NgZorro/Components/Display/Renders/DisplayRender.cs new file mode 100644 index 000000000..d9df08c9e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Display/Renders/DisplayRender.cs @@ -0,0 +1,46 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Display.Builders; + +namespace Util.Ui.NgZorro.Components.Display.Renders; + +/// +/// 数据项展示标签渲染器 +/// +public class DisplayRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化数据项展示标签渲染器 + /// + /// 配置 + public DisplayRender( Config config ) : base( config ) { + _config = config ?? throw new ArgumentNullException( nameof( config ) ); + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var display = GetDisplayBuilder(); + formControlBuilder.AppendContent( display ); + } + + /// + /// 获取标签生成器 + /// + protected TagBuilder GetDisplayBuilder() { + var builder = new DisplayBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new DisplayRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Dividers/Builders/DividerBuilder.cs b/src/Util.Ui.NgZorro/Components/Dividers/Builders/DividerBuilder.cs new file mode 100644 index 000000000..227f15fdd --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Dividers/Builders/DividerBuilder.cs @@ -0,0 +1,120 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Templates.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Dividers.Builders; + +/// +/// 分隔线标签生成器 +/// +public class DividerBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 标识 + /// + private string _id; + + /// + /// 初始化分隔线标签生成器 + /// + /// 配置 + public DividerBuilder( Config config ) : base( config, "nz-divider" ) { + _config = config; + } + + /// + /// 配置虚线 + /// + public DividerBuilder Dashed() { + AttributeIfNotEmpty( "[nzDashed]", _config.GetValue( UiConst.Dashed ) ); + return this; + } + + /// + /// 配置类型 + /// + public DividerBuilder Type() { + AttributeIfNotEmpty( "nzType", _config.GetValue( UiConst.Type )?.Description() ); + AttributeIfNotEmpty( "[nzType]", _config.GetValue( AngularConst.BindType ) ); + return this; + } + + /// + /// 配置文字 + /// + public DividerBuilder Text() { + SetText( _config.GetValue( UiConst.Text ) ); + AttributeIfNotEmpty( "[nzText]", _config.GetValue( AngularConst.BindText ) ); + return this; + } + + /// + /// 设置文字 + /// + private void SetText( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzText]", value ); + return; + } + AttributeIfNotEmpty( "nzText", value ); + } + + /// + /// 配置文字方向 + /// + public DividerBuilder Orientation() { + AttributeIfNotEmpty( "nzOrientation", _config.GetValue( UiConst.Orientation )?.Description() ); + AttributeIfNotEmpty( "[nzOrientation]", _config.GetValue( AngularConst.BindOrientation ) ); + return this; + } + + /// + /// 配置正文样式 + /// + public DividerBuilder Plain() { + AttributeIfNotEmpty( "[nzPlain]", _config.GetValue( UiConst.Plain ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Dashed().Type().Text().Orientation().Plain(); + } + + /// + /// 配置内容 + /// + protected override void ConfigContent( Config config ) { + if ( config.Content.IsEmpty() ) + return; + var id = $"m_{GetId()}"; + Attribute( "[nzText]", id ); + var templateBuilder = new TemplateBuilder( config ); + templateBuilder.Attribute( $"#{id}" ); + templateBuilder.AppendContent( config.Content ); + AppendContent( templateBuilder ); + } + + /// + /// 获取标识 + /// + private string GetId() { + if ( _id.IsEmpty() == false ) + return _id; + _id = _config.GetValue( UiConst.Id ); + if ( _id.IsEmpty() ) + _id = Util.Helpers.Id.Create(); + return _id; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Dividers/DividerTagHelper.cs b/src/Util.Ui.NgZorro/Components/Dividers/DividerTagHelper.cs new file mode 100644 index 000000000..d7a4978e0 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Dividers/DividerTagHelper.cs @@ -0,0 +1,52 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Dividers.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Dividers; + +/// +/// 分隔线,生成的标签为<nz-divider></nz-divider> +/// +[HtmlTargetElement( "util-divider")] +public class DividerTagHelper : AngularTagHelperBase { + /// + /// [nzDashed],是否虚线, 默认值: false + /// + public string Dashed { get; set; } + /// + /// nzType,水平还是垂直类型,默认为水平类型 + /// + public DividerType Type { get; set; } + /// + /// [nzType],水平还是垂直类型,默认为水平类型 + /// + public string BindType { get; set; } + /// + /// nzText,文字 + /// + public string Text { get; set; } + /// + /// [nzText],文字 + /// + public string BindText { get; set; } + /// + /// nzOrientation,文字方向 + /// + public DividerOrientation Orientation { get; set; } + /// + /// [nzOrientation],文字方向 + /// + public string BindOrientation { get; set; } + /// + /// [nzPlain],文字是否显示为普通正文样式, 默认值: false + /// + public string Plain { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new DividerRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Dividers/Renders/DividerRender.cs b/src/Util.Ui.NgZorro/Components/Dividers/Renders/DividerRender.cs new file mode 100644 index 000000000..c04c621fb --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Dividers/Renders/DividerRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Dividers.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Dividers.Renders; + +/// +/// 分隔线渲染器 +/// +public class DividerRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化分隔线渲染器 + /// + /// 配置 + public DividerRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new DividerBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new DividerRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Drawers/Builders/DrawerBuilder.cs b/src/Util.Ui.NgZorro/Components/Drawers/Builders/DrawerBuilder.cs new file mode 100644 index 000000000..3951a2904 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Drawers/Builders/DrawerBuilder.cs @@ -0,0 +1,225 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Cards.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Drawers.Builders; + +/// +/// 抽屉标签生成器 +/// +public class DrawerBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化抽屉标签生成器 + /// + /// 配置 + public DrawerBuilder( Config config ) : base( config, "nz-drawer" ) { + _config = config; + } + + /// + /// 配置标题 + /// + public DrawerBuilder Title() { + SetTitle( _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 设置标题 + /// + private void SetTitle( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzTitle]", value ); + return; + } + AttributeIfNotEmpty( "nzTitle", value ); + } + + /// + /// 配置是否可关闭 + /// + public DrawerBuilder Closable() { + AttributeIfNotEmpty( "[nzClosable]", _config.GetValue( UiConst.Closable ) ); + return this; + } + + /// + /// 配置关闭图标 + /// + public DrawerBuilder CloseIcon() { + AttributeIfNotEmpty( "nzCloseIcon", _config.GetValue( UiConst.CloseIcon )?.Description() ); + AttributeIfNotEmpty( "[nzCloseIcon]", _config.GetValue( AngularConst.BindCloseIcon ) ); + return this; + } + + /// + /// 配置右上角操作区域 + /// + public DrawerBuilder Extra() { + AttributeIfNotEmpty( "[nzExtra]", _config.GetValue( UiConst.Extra ) ); + return this; + } + + /// + /// 配置点击遮罩是否允许关闭 + /// + public DrawerBuilder MaskClosable() { + AttributeIfNotEmpty( "[nzMaskClosable]", _config.GetValue( UiConst.MaskClosable ) ); + return this; + } + + /// + /// 配置是否显示遮罩 + /// + public DrawerBuilder Mask() { + AttributeIfNotEmpty( "[nzMask]", _config.GetValue( UiConst.Mask ) ); + AttributeIfNotEmpty( "[nzMask]", _config.GetValue( AngularConst.BindMask ) ); + return this; + } + + /// + /// 配置导航时是否关闭 + /// + public DrawerBuilder CloseOnNavigation() { + AttributeIfNotEmpty( "[nzCloseOnNavigation]", _config.GetValue( UiConst.CloseOnNavigation ) ); + return this; + } + + /// + /// 配置遮罩样式 + /// + public DrawerBuilder MaskStyle() { + AttributeIfNotEmpty( "[nzMaskStyle]", _config.GetValue( UiConst.MaskStyle ) ); + return this; + } + + /// + /// 配置是否支持键盘ESC键关闭 + /// + public DrawerBuilder Keyboard() { + AttributeIfNotEmpty( "[nzKeyboard]", _config.GetValue( UiConst.Keyboard ) ); + return this; + } + + /// + /// 配置主体样式 + /// + public DrawerBuilder BodyStyle() { + AttributeIfNotEmpty( "[nzBodyStyle]", _config.GetValue( UiConst.BodyStyle ) ); + return this; + } + + /// + /// 配置页脚 + /// + public DrawerBuilder Footer() { + AttributeIfNotEmpty( "nzFooter", _config.GetValue( UiConst.Footer ) ); + AttributeIfNotEmpty( "[nzFooter]", _config.GetValue( AngularConst.BindFooter ) ); + return this; + } + + /// + /// 配置是否可见 + /// + public DrawerBuilder Visible() { + AttributeIfNotEmpty( "[nzVisible]", _config.GetValue( UiConst.Visible ) ); + AttributeIfNotEmpty( "[(nzVisible)]", _config.GetValue( AngularConst.BindonVisible ) ); + return this; + } + + /// + /// 配置抽屉方向 + /// + public DrawerBuilder Placement() { + AttributeIfNotEmpty( "nzPlacement", _config.GetValue( UiConst.Placement )?.Description() ); + AttributeIfNotEmpty( "[nzPlacement]", _config.GetValue( AngularConst.BindPlacement ) ); + return this; + } + + /// + /// 配置尺寸 + /// + public DrawerBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置宽度 + /// + public DrawerBuilder Width() { + AttributeIfNotEmpty( "[nzWidth]", _config.GetValue( UiConst.Width ) ); + return this; + } + + /// + /// 配置高度 + /// + public DrawerBuilder Height() { + AttributeIfNotEmpty( "[nzHeight]", _config.GetValue( UiConst.Height ) ); + return this; + } + + /// + /// 配置X坐标偏移量 + /// + public DrawerBuilder OffsetX() { + AttributeIfNotEmpty( "[nzOffsetX]", _config.GetValue( UiConst.OffsetX ) ); + return this; + } + + /// + /// 配置Y坐标偏移量 + /// + public DrawerBuilder OffsetY() { + AttributeIfNotEmpty( "[nzOffsetY]", _config.GetValue( UiConst.OffsetY ) ); + return this; + } + + /// + /// 配置对话框外层容器样式类名 + /// + public DrawerBuilder WrapClassName() { + AttributeIfNotEmpty( "nzWrapClassName", _config.GetValue( UiConst.WrapClassName ) ); + AttributeIfNotEmpty( "[nzWrapClassName]", _config.GetValue( AngularConst.BindWrapClassName ) ); + return this; + } + + /// + /// 配置z-index + /// + public DrawerBuilder ZIndex() { + AttributeIfNotEmpty( "[nzZIndex]", _config.GetValue( UiConst.ZIndex ) ); + return this; + } + + /// + /// 配置事件 + /// + public DrawerBuilder Events() { + AttributeIfNotEmpty( "(nzOnClose)", _config.GetValue( UiConst.OnClose ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Closable().CloseIcon().Extra().MaskClosable().Mask().CloseOnNavigation() + .MaskStyle().Keyboard().BodyStyle().Title().Footer().Visible() + .Placement().Size().Width().Height().OffsetX().OffsetY().WrapClassName() + .ZIndex().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Drawers/Builders/DrawerContentBuilder.cs b/src/Util.Ui.NgZorro/Components/Drawers/Builders/DrawerContentBuilder.cs new file mode 100644 index 000000000..4dcb865ef --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Drawers/Builders/DrawerContentBuilder.cs @@ -0,0 +1,16 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Drawers.Builders; + +/// +/// 抽屉内容标签生成器 +/// +public class DrawerContentBuilder : AngularTagBuilder { + /// + /// 初始化抽屉内容标签生成器 + /// + /// 配置 + public DrawerContentBuilder( Config config ) : base( config, "ng-container" ) { + base.Attribute( "*nzDrawerContent" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Drawers/Builders/DrawerFooterBuilder.cs b/src/Util.Ui.NgZorro/Components/Drawers/Builders/DrawerFooterBuilder.cs new file mode 100644 index 000000000..6784d39ff --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Drawers/Builders/DrawerFooterBuilder.cs @@ -0,0 +1,16 @@ +using Util.Ui.NgZorro.Components.Flex.Builders; + +namespace Util.Ui.NgZorro.Components.Drawers.Builders; + +/// +/// 抽屉页脚标签生成器 +/// +public class DrawerFooterBuilder : FlexBuilder { + /// + /// 初始化抽屉页脚标签生成器 + /// + /// 配置 + public DrawerFooterBuilder( Config config ) : base( config ) { + base.Attribute( "*xDrawerFooter" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Drawers/Configs/DrawerShareConfig.cs b/src/Util.Ui.NgZorro/Components/Drawers/Configs/DrawerShareConfig.cs new file mode 100644 index 000000000..8790518da --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Drawers/Configs/DrawerShareConfig.cs @@ -0,0 +1,7 @@ +namespace Util.Ui.NgZorro.Components.Drawers.Configs; + +/// +/// 抽屉共享配置 +/// +public class DrawerShareConfig { +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Drawers/DrawerContentTagHelper.cs b/src/Util.Ui.NgZorro/Components/Drawers/DrawerContentTagHelper.cs new file mode 100644 index 000000000..3651ff63d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Drawers/DrawerContentTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Drawers.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Drawers; + +/// +/// 抽屉内容,生成的标签为<ng-container *nzDrawerContent></ng-container> +/// +[HtmlTargetElement( "util-drawer-content" )] +public class DrawerContentTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new DrawerContentRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Drawers/DrawerFooterTagHelper.cs b/src/Util.Ui.NgZorro/Components/Drawers/DrawerFooterTagHelper.cs new file mode 100644 index 000000000..fa305a54e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Drawers/DrawerFooterTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Drawers.Renders; +using Util.Ui.NgZorro.Components.Flex; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Drawers; + +/// +/// 抽屉页脚,生成的标签为<div *xDrawerFooter></div> +/// +[HtmlTargetElement( "util-drawer-footer" )] +public class DrawerFooterTagHelper : FlexTagHelper { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new DrawerFooterRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Drawers/DrawerTagHelper.cs b/src/Util.Ui.NgZorro/Components/Drawers/DrawerTagHelper.cs new file mode 100644 index 000000000..51925dbd8 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Drawers/DrawerTagHelper.cs @@ -0,0 +1,144 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Drawers.Helpers; +using Util.Ui.NgZorro.Components.Drawers.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Drawers; + +/// +/// 抽屉,生成的标签为<nz-drawer></nz-drawer> +/// +[HtmlTargetElement( "util-drawer" )] +public class DrawerTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// nzTitle,标题,支持多语言 + /// + public string Title { get; set; } + /// + /// [nzTitle],标题,类型: string | TemplateRef<void> + /// + public string BindTitle { get; set; } + /// + /// [nzClosable],是否显示左上角的关闭按钮, 类型: boolean, 默认值: true + /// + public string Closable { get; set; } + /// + /// nzCloseIcon,自定义关闭图标,类型: string | TemplateRef<void> | null,默认值: 'close' + /// + public AntDesignIcon CloseIcon { get; set; } + /// + /// [nzCloseIcon],自定义关闭图标,类型: string | TemplateRef<void> | null,默认值: 'close' + /// + public string BindCloseIcon { get; set; } + /// + /// [nzExtra],右上角的操作区域,类型: string | TemplateRef<void> + /// + public string Extra { get; set; } + /// + /// [nzMaskClosable],点击遮罩是否允许关闭,类型: boolean, 默认值: true + /// + public string MaskClosable { get; set; } + /// + /// [nzKeyboard],按下ESC键是否允许关闭,类型: boolean, 默认值: true + /// + public string Keyboard { get; set; } + /// + /// [nzMask],是否显示遮罩,类型: boolean, 默认值: true + /// + public string Mask { get; set; } + /// + /// [nzCloseOnNavigation],当用户在历史中前进/后退时是否关闭抽屉,类型: boolean, 默认值: true + /// + public string CloseOnNavigation { get; set; } + /// + /// [nzMaskStyle],遮罩样式,类型: object + /// + public string MaskStyle { get; set; } + /// + /// [nzBodyStyle],抽屉主体样式,类型: object + /// + public string BodyStyle { get; set; } + /// + /// nzFooter,页脚,类型: string | TemplateRef<void> + /// + public string Footer { get; set; } + /// + /// [nzFooter],页脚,类型: string | TemplateRef<void> + /// + public string BindFooter { get; set; } + /// + /// [nzVisible],是否可见, 类型: boolean + /// + public string Visible { get; set; } + /// + /// [(nzVisible)],是否可见, 类型: boolean + /// + public string BindonVisible { get; set; } + /// + /// nzPlacement,抽屉方向,可选值: 'top' | 'right' | 'bottom' | 'left',默认值: 'right' + /// + public DrawerPlacement Placement { get; set; } + /// + /// [nzPlacement],抽屉方向,可选值: 'top' | 'right' | 'bottom' | 'left',默认值: 'right' + /// + public string BindPlacement { get; set; } + /// + /// nzSize,抽屉尺寸,可选值: 'default' | 'large',默认值: 'default' + /// + public DrawerSize Size { get; set; } + /// + /// [nzSize],抽屉尺寸,可选值: 'default' | 'large',默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// [nzWidth],宽度, 只在方向为 'right'或'left' 时生效,类型: number | string + /// + public string Width { get; set; } + /// + /// [nzHeight],高度, 只在方向为 'top'或'bottom' 时生效,类型: number | string + /// + public string Height { get; set; } + /// + /// [nzOffsetX],X坐标偏移量,单位:像素,只在方向为 'right'或'left' 时生效,类型: number,默认值: 0 + /// + public string OffsetX { get; set; } + /// + /// [nzOffsetY],Y坐标偏移量,单位:像素,只在方向为 'top'或'bottom' 时生效,类型: number,默认值: 0 + /// + public string OffsetY { get; set; } + /// + /// nzWrapClassName,抽屉外层容器样式类名 + /// + public string WrapClassName { get; set; } + /// + /// [nzWrapClassName],抽屉外层容器样式类名 + /// + public string BindWrapClassName { get; set; } + /// + /// [nzZIndex],z-index,类型: number,默认值: 1000 + /// + public string ZIndex { get; set; } + /// + /// (nzOnClose),关闭事件,类型: EventEmitter<MouseEvent> + /// + public string OnClose { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new DrawerService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new DrawerRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Drawers/Helpers/DrawerService.cs b/src/Util.Ui.NgZorro/Components/Drawers/Helpers/DrawerService.cs new file mode 100644 index 000000000..6a54a6a8f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Drawers/Helpers/DrawerService.cs @@ -0,0 +1,40 @@ +using Util.Ui.NgZorro.Components.Drawers.Configs; + +namespace Util.Ui.NgZorro.Components.Drawers.Helpers; + +/// +/// 抽屉服务 +/// +public class DrawerService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 抽屉共享配置 + /// + private DrawerShareConfig _shareConfig; + + /// + /// 初始化抽屉服务 + /// + /// 配置 + public DrawerService( Config config ) { + _config = config; + } + + /// + /// 初始化 + /// + public void Init() { + CreateDrawerShareConfig(); + } + + /// + /// 创建抽屉共享配置 + /// + private void CreateDrawerShareConfig() { + _shareConfig = new DrawerShareConfig(); + _config.SetValueToItems( _shareConfig ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Drawers/Renders/DrawerContentRender.cs b/src/Util.Ui.NgZorro/Components/Drawers/Renders/DrawerContentRender.cs new file mode 100644 index 000000000..6e88cfc87 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Drawers/Renders/DrawerContentRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Drawers.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Drawers.Renders; + +/// +/// 抽屉内容渲染器 +/// +public class DrawerContentRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化抽屉内容渲染器 + /// + /// 配置 + public DrawerContentRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new DrawerContentBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new DrawerContentRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Drawers/Renders/DrawerFooterRender.cs b/src/Util.Ui.NgZorro/Components/Drawers/Renders/DrawerFooterRender.cs new file mode 100644 index 000000000..cb7b83e00 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Drawers/Renders/DrawerFooterRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Drawers.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Drawers.Renders; + +/// +/// 抽屉页脚渲染器 +/// +public class DrawerFooterRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化抽屉页脚渲染器 + /// + /// 配置 + public DrawerFooterRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new DrawerFooterBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new DrawerFooterRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Drawers/Renders/DrawerRender.cs b/src/Util.Ui.NgZorro/Components/Drawers/Renders/DrawerRender.cs new file mode 100644 index 000000000..1199e1163 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Drawers/Renders/DrawerRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Drawers.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Drawers.Renders; + +/// +/// 抽屉渲染器 +/// +public class DrawerRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化抽屉渲染器 + /// + /// 配置 + public DrawerRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new DrawerBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new DrawerRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Dropdowns/Builders/DropdownMenuBuilder.cs b/src/Util.Ui.NgZorro/Components/Dropdowns/Builders/DropdownMenuBuilder.cs new file mode 100644 index 000000000..95d073eaa --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Dropdowns/Builders/DropdownMenuBuilder.cs @@ -0,0 +1,80 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Dropdowns.Configs; + +namespace Util.Ui.NgZorro.Components.Dropdowns.Builders; + +/// +/// 下拉菜单标签生成器 +/// +public class DropdownMenuBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化下拉菜单标签生成器 + /// + /// 配置 + public DropdownMenuBuilder( Config config ) : base( config,"nz-dropdown-menu" ) { + _config = config; + } + + /// + /// 配置标识 + /// + protected override void ConfigId( Config config ) { + if ( config.Contains( UiConst.Id ) ) + Attribute( $"#{config.GetValue( UiConst.Id )}", "nzDropdownMenu" ); + AttributeIfNotEmpty( "id", config.GetValue( AngularConst.RawId ) ); + } + + /// + /// 配置内容元素 + /// + protected override void ConfigContent( Config config ) { + var shareConfig = GetShareConfig(); + if ( shareConfig.AutoCreateUl == false ) { + config.Content.AppendTo( this ); + return; + } + var ulBuilder = CreateUlBuilder(); + ConfigSelectable( ulBuilder ); + ConfigEvents( ulBuilder ); + SetContent( ulBuilder ); + _config.Content.AppendTo( ulBuilder ); + } + + /// + /// 获取下拉菜单共享配置 + /// + private DropdownMenuShareConfig GetShareConfig() { + return _config.GetValueFromItems() ?? new DropdownMenuShareConfig(); + } + + /// + /// 创建ul标签生成器 + /// + private TagBuilder CreateUlBuilder() { + var builder = new UlBuilder(); + builder.Attribute( "nz-menu" ); + return builder; + } + + /// + /// 配置允许选中 + /// + private void ConfigSelectable( TagBuilder builder ) { + builder.AttributeIfNotEmpty( "[nzSelectable]", _config.GetValue( UiConst.Selectable ) ); + } + + /// + /// 配置事件 + /// + private void ConfigEvents( TagBuilder builder ) { + builder.AttributeIfNotEmpty( "(nzClick)", _config.GetValue( UiConst.OnClick ) ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Dropdowns/Configs/DropdownMenuShareConfig.cs b/src/Util.Ui.NgZorro/Components/Dropdowns/Configs/DropdownMenuShareConfig.cs new file mode 100644 index 000000000..864e88132 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Dropdowns/Configs/DropdownMenuShareConfig.cs @@ -0,0 +1,11 @@ +namespace Util.Ui.NgZorro.Components.Dropdowns.Configs; + +/// +/// 下拉菜单共享配置 +/// +public class DropdownMenuShareConfig { + /// + /// 自动创建 ul + /// + public bool? AutoCreateUl { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Dropdowns/DropdownMenuTagHelper.cs b/src/Util.Ui.NgZorro/Components/Dropdowns/DropdownMenuTagHelper.cs new file mode 100644 index 000000000..63fe20280 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Dropdowns/DropdownMenuTagHelper.cs @@ -0,0 +1,40 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Dropdowns.Helpers; +using Util.Ui.NgZorro.Components.Dropdowns.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Dropdowns; + +/// +/// 下拉菜单,生成的标签为<nz-dropdown-menu><ul nz-menu></ul></nz-dropdown-menu> +/// +[HtmlTargetElement( "util-dropdown-menu" )] +public class DropdownMenuTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + + /// + /// [nzSelectable],是否允许选中,默认值:false,注意:该属性设置在<ul nz-menu></ul>上 + /// + public string Selectable { get; set; } + /// + /// (nzClick),单击菜单项事件处理函数,注意:该属性设置在<ul nz-menu></ul>上 + /// + public string OnClick { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new DropdownMenuService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new DropdownMenuRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Dropdowns/Helpers/DropdownMenuService.cs b/src/Util.Ui.NgZorro/Components/Dropdowns/Helpers/DropdownMenuService.cs new file mode 100644 index 000000000..5a7679a88 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Dropdowns/Helpers/DropdownMenuService.cs @@ -0,0 +1,57 @@ +using Util.Ui.NgZorro.Components.Dropdowns.Configs; + +namespace Util.Ui.NgZorro.Components.Dropdowns.Helpers; + +/// +/// 下拉菜单共享服务 +/// +public class DropdownMenuService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 下拉菜单共享配置 + /// + private DropdownMenuShareConfig _shareConfig; + + /// + /// 初始化表单项共享服务 + /// + /// 配置 + public DropdownMenuService( Config config ) { + _config = config; + } + + /// + /// 设置自动创建 ul + /// + /// 是否自动创建 + public void AutoCreateUl( bool value ) { + if ( _shareConfig.AutoCreateUl == false ) + return; + _shareConfig.AutoCreateUl = value; + } + + /// + /// 初始化 + /// + public void Init() { + InitShareConfig(); + } + + /// + /// 初始化 + /// + private void InitShareConfig() { + _shareConfig = GetShareConfig(); + _config.SetValueToItems( _shareConfig ); + } + + /// + /// 获取下拉菜单共享配置 + /// + private DropdownMenuShareConfig GetShareConfig() { + return _config.GetValueFromItems() ?? new DropdownMenuShareConfig(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Dropdowns/Renders/DropdownMenuRender.cs b/src/Util.Ui.NgZorro/Components/Dropdowns/Renders/DropdownMenuRender.cs new file mode 100644 index 000000000..80714c958 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Dropdowns/Renders/DropdownMenuRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Dropdowns.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Dropdowns.Renders; + +/// +/// 下拉菜单渲染器 +/// +public class DropdownMenuRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化下拉菜单渲染器 + /// + /// 配置 + public DropdownMenuRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new DropdownMenuBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new DropdownMenuRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Empties/Builders/EmptyBuilder.cs b/src/Util.Ui.NgZorro/Components/Empties/Builders/EmptyBuilder.cs new file mode 100644 index 000000000..a10ddc4d0 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Empties/Builders/EmptyBuilder.cs @@ -0,0 +1,57 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; + +namespace Util.Ui.NgZorro.Components.Empties.Builders; + +/// +/// 空状态标签生成器 +/// +public class EmptyBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化空状态标签生成器 + /// + /// 配置 + public EmptyBuilder( Config config ) : base( config,"nz-empty" ) { + _config = config; + } + + /// + /// 配置图片 + /// + public EmptyBuilder NotFoundImage() { + AttributeIfNotEmpty( "nzNotFoundImage", _config.GetValue( UiConst.NotFoundImage ) ); + AttributeIfNotEmpty( "[nzNotFoundImage]", _config.GetValue( AngularConst.BindNotFoundImage ) ); + return this; + } + + /// + /// 配置内容 + /// + public EmptyBuilder NotFoundContent() { + AttributeIfNotEmpty( "nzNotFoundContent", _config.GetValue( UiConst.NotFoundContent ) ); + AttributeIfNotEmpty( "[nzNotFoundContent]", _config.GetValue( AngularConst.BindNotFoundContent ) ); + return this; + } + + /// + /// 配置底部内容 + /// + public EmptyBuilder NotFoundFooter() { + AttributeIfNotEmpty( "nzNotFoundFooter", _config.GetValue( UiConst.NotFoundFooter ) ); + AttributeIfNotEmpty( "[nzNotFoundFooter]", _config.GetValue( AngularConst.BindNotFoundFooter ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + NotFoundImage().NotFoundContent().NotFoundFooter(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Empties/EmptyTagHelper.cs b/src/Util.Ui.NgZorro/Components/Empties/EmptyTagHelper.cs new file mode 100644 index 000000000..6915a6cfd --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Empties/EmptyTagHelper.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Empties.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Empties; + +/// +/// 空状态,生成的标签为<nz-empty></nz-empty> +/// +[HtmlTargetElement( "util-empty" )] +public class EmptyTagHelper : AngularTagHelperBase { + /// + /// nzNotFoundImage,设置显示图片,设置字符串值表示图片地址,值为 `simple` 时显示简单风格图片, 类型: string | TemplateRef<void> + /// + public string NotFoundImage { get; set; } + /// + /// [nzNotFoundImage],设置显示图片,设置字符串值表示图片地址,值为 `simple` 时显示简单风格图片,类型: string | TemplateRef<void> + /// + public string BindNotFoundImage { get; set; } + /// + /// nzNotFoundContent,设置描述内容,类型: string | TemplateRef<void> | null + /// + public string NotFoundContent { get; set; } + /// + /// [nzNotFoundContent],设置描述内容,类型: string | TemplateRef<void> | null + /// + public string BindNotFoundContent { get; set; } + /// + /// nzNotFoundFooter,设置底部内容,类型: string | TemplateRef<void> + /// + public string NotFoundFooter { get; set; } + /// + /// [nzNotFoundFooter],设置底部内容,类型: string | TemplateRef<void> + /// + public string BindNotFoundFooter { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new EmptyRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Empties/Renders/EmptyRender.cs b/src/Util.Ui.NgZorro/Components/Empties/Renders/EmptyRender.cs new file mode 100644 index 000000000..50a92ddb8 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Empties/Renders/EmptyRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Empties.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Empties.Renders; + +/// +/// 空状态渲染器 +/// +public class EmptyRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化空状态渲染器 + /// + /// 配置 + public EmptyRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new EmptyBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new EmptyRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Flex/Builders/FlexBuilder.cs b/src/Util.Ui.NgZorro/Components/Flex/Builders/FlexBuilder.cs new file mode 100644 index 000000000..30c4783e2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Flex/Builders/FlexBuilder.cs @@ -0,0 +1,85 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Flex.Builders; + +/// +/// 弹性布局栅格标签生成器 +/// +public class FlexBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化弹性布局栅格标签生成器 + /// + /// 配置 + public FlexBuilder( Config config ) : base( config,"div" ) { + _config = config; + base.Attribute("nz-flex"); + } + + /// + /// 配置是否垂直布局 + /// + public FlexBuilder Vertical() { + AttributeIfNotEmpty( "[nzVertical]", _config.GetValue( UiConst.Vertical ) ); + return this; + } + + /// + /// 配置对齐方式 + /// + public FlexBuilder Justify() { + AttributeIfNotEmpty( "nzJustify", _config.GetValue( UiConst.Justify )?.Description() ); + AttributeIfNotEmpty( "[nzJustify]", _config.GetValue( AngularConst.BindJustify ) ); + return this; + } + + /// + /// 配置垂直对齐方式 + /// + public FlexBuilder Align() { + AttributeIfNotEmpty( "nzAlign", _config.GetValue( UiConst.Align )?.Description() ); + AttributeIfNotEmpty( "[nzAlign]", _config.GetValue( AngularConst.BindAlign ) ); + return this; + } + + /// + /// 配置间距 + /// + public FlexBuilder Gap() { + AttributeIfNotEmpty( "nzGap", _config.GetValue( UiConst.Gap )?.Description() ); + AttributeIfNotEmpty( "[nzGap]", _config.GetValue( AngularConst.BindGap ) ); + return this; + } + + /// + /// 配置自动换行 + /// + public FlexBuilder Wrap() { + AttributeIfNotEmpty( "nzWrap", _config.GetValue( UiConst.Wrap )?.Description() ); + AttributeIfNotEmpty( "[nzWrap]", _config.GetValue( AngularConst.BindWrap ) ); + return this; + } + + /// + /// 配置flex + /// + public FlexBuilder Flex() { + AttributeIfNotEmpty( "nzFlex", _config.GetValue( UiConst.Flex ) ); + AttributeIfNotEmpty( "[nzFlex]", _config.GetValue( AngularConst.BindFlex ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Vertical().Justify().Align().Gap().Wrap().Flex(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Flex/FlexTagHelper.cs b/src/Util.Ui.NgZorro/Components/Flex/FlexTagHelper.cs new file mode 100644 index 000000000..e0371f277 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Flex/FlexTagHelper.cs @@ -0,0 +1,64 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Flex.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Flex; + +/// +/// 弹性布局栅格,生成的标签为<div nz-flex></div> +/// +[HtmlTargetElement( "util-flex" )] +public class FlexTagHelper : AngularTagHelperBase { + /// + /// [nzVertical],是否垂直布局, 默认值: false + /// + public string Vertical { get; set; } + /// + /// nzJustify,对齐方式, 可选值: flex-start,center,flex-end,space-between,space-around,space-evenly + /// + public FlexJustify Justify { get; set; } + /// + /// [nzJustify],对齐方式, 可选值: flex-start,center,flex-end,space-between,space-around,space-evenly + /// + public string BindJustify { get; set; } + /// + /// nzAlign,垂直对齐方式, 可选值: flex-start,center,flex-end + /// + public FlexAlign Align { get; set; } + /// + /// [nzAlign],垂直对齐方式, 可选值: flex-start,center,flex-end + /// + public string BindAlign { get; set; } + /// + /// nzGap,元素之间的间距, 可选值: small,middle,large + /// + public FlexGap Gap { get; set; } + /// + /// [nzGap],元素之间的间距, 可选值: small,middle,large,自定义数值 + /// + public string BindGap { get; set; } + /// + /// nzWrap,自动换行, 可选值: wrap,wrap-reverse,nowrap + /// + public FlexWrap Wrap { get; set; } + /// + /// [nzWrap],自动换行, 可选值: wrap,wrap-reverse,nowrap + /// + public string BindWrap { get; set; } + /// + /// nzFlex + /// + public string Flex { get; set; } + /// + /// [nzFlex] + /// + public string BindFlex { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new FlexRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Flex/Renders/FlexRender.cs b/src/Util.Ui.NgZorro/Components/Flex/Renders/FlexRender.cs new file mode 100644 index 000000000..b52ab7155 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Flex/Renders/FlexRender.cs @@ -0,0 +1,39 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Flex.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Flex.Renders; + +/// +/// 弹性布局栅格渲染器 +/// +public class FlexRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化弹性布局栅格渲染器 + /// + /// 配置 + public FlexRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new FlexBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new FlexRender( _config.CopyRemoveAttributes() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/Builders/FormBuilder.cs b/src/Util.Ui.NgZorro/Components/Forms/Builders/FormBuilder.cs new file mode 100644 index 000000000..effab0478 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/Builders/FormBuilder.cs @@ -0,0 +1,173 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Forms.Builders; + +/// +/// 表单标签生成器 +/// +public class FormBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表单共享配置 + /// + private readonly FormShareConfig _shareConfig; + + /// + /// 初始化表单标签生成器 + /// + /// 配置 + public FormBuilder( Config config ) : base( config, "form" ) { + base.Attribute( "nz-form" ); + _config = config; + _shareConfig = _config.GetValueFromItems(); + } + + /// + /// 配置布局方式 + /// + public FormBuilder Layout() { + AttributeIfNotEmpty( "nzLayout", _config.GetValue( UiConst.Layout )?.Description() ); + AttributeIfNotEmpty( "[nzLayout]", _config.GetValue( AngularConst.BindLayout ) ); + return this; + } + + /// + /// 配置自动提示 + /// + public FormBuilder AutoTips() { + AttributeIfNotEmpty( "[nzAutoTips]", _config.GetValue( UiConst.AutoTips ) ); + return this; + } + + /// + /// 配置禁用自动提示 + /// + public FormBuilder DisableAutoTips() { + AttributeIfNotEmpty( "[nzDisableAutoTips]", _config.GetValue( UiConst.DisableAutoTips ) ); + return this; + } + + /// + /// 配置不显示标签冒号 + /// + public FormBuilder NoColon() { + AttributeIfNotEmpty( "[nzNoColon]", _config.GetValue( UiConst.NoColon ) ); + return this; + } + + /// + /// 配置标签文本是否换行 + /// + public FormBuilder LabelWrap() { + AttributeIfNotEmpty( "[nzLabelWrap]", _config.GetValue( UiConst.LabelWrap ) ); + return this; + } + + /// + /// 配置标签文本对齐方式 + /// + public FormBuilder LabelAlign() { + AttributeIfNotEmpty( "nzLabelAlign", _config.GetValue( UiConst.LabelAlign )?.Description() ); + AttributeIfNotEmpty( "[nzLabelAlign]", _config.GetValue( AngularConst.BindLabelAlign ) ); + return this; + } + + /// + /// 配置标签提示图标 + /// + public FormBuilder TooltipIcon() { + AttributeIfNotEmpty( "nzTooltipIcon", _config.GetValue( UiConst.TooltipIcon )?.Description() ); + AttributeIfNotEmpty( "[nzTooltipIcon]", _config.GetValue( AngularConst.BindTooltipIcon ) ); + return this; + } + + /// + /// 配置自动完成 + /// + public FormBuilder AutoComplete() { + var isAutoComplete = _config.GetValue( UiConst.Autocomplete ); + if ( isAutoComplete == null ) + return this; + if ( isAutoComplete == true ) { + Attribute( "autocomplete", "on" ); + return this; + } + Attribute( "autocomplete", "off" ); + return this; + } + + /// + /// 配置表单组 + /// + public FormBuilder FormGroup() { + AttributeIfNotEmpty( "[formGroup]", GetFormGroup() ); + return this; + } + + /// + /// 获取表单组 + /// + private string GetFormGroup() { + return _config.GetValue( UiConst.FormGroup ); + } + + /// + /// 配置是否不验证表单 + /// + public FormBuilder Novalidate() { + AttributeIf( "novalidate", _config.GetValue( UiConst.Novalidate ) == true ); + return this; + } + + /// + /// 配置事件 + /// + public FormBuilder Events() { + AttributeIfNotEmpty( "(ngSubmit)", _config.GetValue( UiConst.OnSubmit ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Layout().AutoTips().DisableAutoTips().NoColon().LabelWrap().LabelAlign() + .TooltipIcon().AutoComplete().FormGroup().Novalidate() + .Events(); + AutocompleteOff(); + } + + /// + /// 配置标识 + /// + protected override void ConfigId( Config config ) { + this.RawId( _config ); + if ( Util.Helpers.Environment.IsTest && _config.Contains( UiConst.Id ) == false ) + return; + if ( _shareConfig.IsSearch.SafeValue() && _config.Contains( UiConst.Id ) == false ) + return; + if ( GetFormGroup().IsEmpty() == false ) { + AttributeIf( $"#{_shareConfig.FormId}", _shareConfig.FormId.IsEmpty() == false ); + return; + } + Attribute( $"#{_shareConfig.FormId}", "ngForm" ); + } + + /// + /// 配置全局设置 autocomplete="off" + /// + protected void AutocompleteOff() { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableAutocompleteOff ) + Attribute( "autocomplete", "off" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/Builders/FormControlBuilder.cs b/src/Util.Ui.NgZorro/Components/Forms/Builders/FormControlBuilder.cs new file mode 100644 index 000000000..c5664181c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/Builders/FormControlBuilder.cs @@ -0,0 +1,595 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Forms.Builders; + +/// +/// 表单域标签生成器 +/// +public class FormControlBuilder : ColumnBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化表单域标签生成器 + /// + /// 配置 + public FormControlBuilder( Config config ) : base( config, "nz-form-control" ) { + _config = config; + } + + /// + /// 获取表单项共享配置 + /// + private FormItemShareConfig GetFormItemShareConfig() { + return _config.GetValueFromItems() ?? new FormItemShareConfig(); + } + + /// + /// 配置校验状态 + /// + public FormControlBuilder ValidateStatus() { + AttributeIfNotEmpty( "[nzValidateStatus]", _config.GetValue( UiConst.ValidateStatus ) ); + return this; + } + + /// + /// 配置展示校验状态图标 + /// + public FormControlBuilder HasFeedback() { + if ( _config.Contains( UiConst.HasFeedback ) ) { + AttributeIfNotEmpty( "[nzHasFeedback]", _config.GetValue( UiConst.HasFeedback ) ); + return this; + } + var shareConfig = GetFormItemShareConfig(); + if ( shareConfig.IsValidationExtend == false ) + return this; + if ( shareConfig.HasFeedback == true ) + Attribute( "[nzHasFeedback]", "true" ); + return this; + } + + /// + /// 配置额外提示 + /// + public FormControlBuilder Extra() { + Extra( _config.GetValue( UiConst.Extra ) ); + BindExtra( _config.GetValue( AngularConst.BindExtra ) ); + return this; + } + + /// + /// 配置额外提示 + /// + private void Extra( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzExtra]", value ); + return; + } + AttributeIfNotEmpty( "nzExtra", value ); + } + + /// + /// 配置额外提示 + /// + private void BindExtra( string value ) { + AttributeIfNotEmpty( "[nzExtra]", value ); + } + + /// + /// 配置校验成功状态提示 + /// + public FormControlBuilder SuccessTip() { + AttributeIfNotEmpty( "nzSuccessTip", _config.GetValue( UiConst.SuccessTip ) ); + AttributeIfNotEmpty( "[nzSuccessTip]", _config.GetValue( AngularConst.BindSuccessTip ) ); + return this; + } + + /// + /// 配置校验警告状态提示 + /// + public FormControlBuilder WarningTip() { + AttributeIfNotEmpty( "nzWarningTip", _config.GetValue( UiConst.WarningTip ) ); + AttributeIfNotEmpty( "[nzWarningTip]", _config.GetValue( AngularConst.BindWarningTip ) ); + return this; + } + + /// + /// 配置校验错误状态提示 + /// + public FormControlBuilder ErrorTip() { + AttributeIfNotEmpty( "nzErrorTip", _config.GetValue( UiConst.ErrorTip ) ); + AttributeIfNotEmpty( "[nzErrorTip]", GetBindErrorTip() ); + return this; + } + + /// + /// 获取校验错误状态提示 + /// + protected string GetBindErrorTip() { + var result = _config.GetValue( AngularConst.BindErrorTip ); + if ( result.IsEmpty() == false ) + return result; + var shareConfig = GetFormItemShareConfig(); + return shareConfig.BindErrorTip; + } + + /// + /// 配置校验中状态提示 + /// + public FormControlBuilder ValidatingTip() { + AttributeIfNotEmpty( "nzValidatingTip", _config.GetValue( UiConst.ValidatingTip ) ); + AttributeIfNotEmpty( "[nzValidatingTip]", _config.GetValue( AngularConst.BindValidatingTip ) ); + return this; + } + + /// + /// 配置自动提示 + /// + public FormControlBuilder AutoTips() { + AttributeIfNotEmpty( "[nzAutoTips]", _config.GetValue( UiConst.AutoTips ) ); + return this; + } + + /// + /// 配置禁用自动提示 + /// + public FormControlBuilder DisableAutoTips() { + AttributeIfNotEmpty( "[nzDisableAutoTips]", _config.GetValue( UiConst.DisableAutoTips ) ); + return this; + } + + /// + /// 配置跨度 + /// + public override FormControlBuilder Span() { + if ( _config.Contains( UiConst.Span ) || _config.Contains( AngularConst.BindSpan ) ) + return base.Span(); + var shareConfig = GetFormItemShareConfig(); + AttributeIfNotEmpty( "[nzSpan]", shareConfig.ControlSpan ); + return this; + } + + /// + /// 配置偏移量 + /// + public override FormControlBuilder Offset() { + if ( _config.Contains( UiConst.Offset ) || _config.Contains( AngularConst.BindOffset ) ) + return base.Offset(); + var shareConfig = GetFormItemShareConfig(); + AttributeIfNotEmpty( "[nzOffset]", shareConfig.ControlOffset ); + return this; + } + + /// + /// 配置栅格顺序 + /// + public override FormControlBuilder Order() { + if ( _config.Contains( UiConst.Order ) || _config.Contains( AngularConst.BindOrder ) ) + return base.Order(); + var shareConfig = GetFormItemShareConfig(); + AttributeIfNotEmpty( "[nzOrder]", shareConfig.ControlOrder ); + return this; + } + + /// + /// 配置栅格左移 + /// + public override FormControlBuilder Pull() { + if ( _config.Contains( UiConst.Pull ) || _config.Contains( AngularConst.BindPull ) ) + return base.Pull(); + var shareConfig = GetFormItemShareConfig(); + AttributeIfNotEmpty( "[nzPull]", shareConfig.ControlPull ); + return this; + } + + /// + /// 配置栅格右移 + /// + public override FormControlBuilder Push() { + if ( _config.Contains( UiConst.Push ) || _config.Contains( AngularConst.BindPush ) ) + return base.Push(); + var shareConfig = GetFormItemShareConfig(); + AttributeIfNotEmpty( "[nzPush]", shareConfig.ControlPush ); + return this; + } + + /// + /// 配置Flex布局 + /// + public override FormControlBuilder Flex() { + if ( _config.Contains( UiConst.Flex ) || _config.Contains( AngularConst.BindFlex ) ) + return base.Flex(); + var shareConfig = GetFormItemShareConfig(); + AttributeIfNotEmpty( "[nzFlex]", shareConfig.ControlFlex ); + return this; + } + + /// + /// 获取Xs超窄尺寸响应式栅格 + /// + protected override string GetXs() { + if ( _config.Contains( UiConst.Xs ) ) + return base.GetXs(); + var shareConfig = GetFormItemShareConfig(); + if ( shareConfig.ControlXs.IsEmpty() == false ) + return shareConfig.ControlXs; + return base.GetXs(); + } + + /// + /// 获取Xs超窄尺寸跨度 + /// + protected override int? GetXsSpan() { + if ( _config.Contains( UiConst.XsSpan ) ) + return base.GetXsSpan(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlXsSpan; + } + + /// + /// 获取Xs超窄尺寸偏移量 + /// + protected override int? GetXsOffset() { + if ( _config.Contains( UiConst.XsOffset ) ) + return base.GetXsOffset(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlXsOffset; + } + + /// + /// 获取Xs超窄尺寸顺序 + /// + protected override int? GetXsOrder() { + if ( _config.Contains( UiConst.XsOrder ) ) + return base.GetXsOrder(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlXsOrder; + } + + /// + /// 获取Xs超窄尺寸左移 + /// + protected override int? GetXsPull() { + if ( _config.Contains( UiConst.XsPull ) ) + return base.GetXsPull(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlXsPull; + } + + /// + /// 获取Xs超窄尺寸右移 + /// + protected override int? GetXsPush() { + if ( _config.Contains( UiConst.XsPush ) ) + return base.GetXsPush(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlXsPush; + } + + /// + /// 获取Sm窄尺寸响应式栅格 + /// + protected override string GetSm() { + if ( _config.Contains( UiConst.Sm ) ) + return base.GetSm(); + var shareConfig = GetFormItemShareConfig(); + if ( shareConfig.ControlSm.IsEmpty() == false ) + return shareConfig.ControlSm; + return base.GetSm(); + } + + /// + /// 获取Sm窄尺寸跨度 + /// + protected override int? GetSmSpan() { + if ( _config.Contains( UiConst.SmSpan ) ) + return base.GetSmSpan(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlSmSpan; + } + + /// + /// 获取Sm窄尺寸偏移量 + /// + protected override int? GetSmOffset() { + if ( _config.Contains( UiConst.SmOffset ) ) + return base.GetSmOffset(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlSmOffset; + } + + /// + /// 获取Sm窄尺寸顺序 + /// + protected override int? GetSmOrder() { + if ( _config.Contains( UiConst.SmOrder ) ) + return base.GetSmOrder(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlSmOrder; + } + + /// + /// 获取Sm窄尺寸左移 + /// + protected override int? GetSmPull() { + if ( _config.Contains( UiConst.SmPull ) ) + return base.GetSmPull(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlSmPull; + } + + /// + /// 获取Sm窄尺寸右移 + /// + protected override int? GetSmPush() { + if ( _config.Contains( UiConst.SmPush ) ) + return base.GetSmPush(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlSmPush; + } + + /// + /// 获取Md中尺寸响应式栅格 + /// + protected override string GetMd() { + if ( _config.Contains( UiConst.Md ) ) + return base.GetMd(); + var shareConfig = GetFormItemShareConfig(); + if ( shareConfig.ControlMd.IsEmpty() == false ) + return shareConfig.ControlMd; + return base.GetMd(); + } + + /// + /// 获取Md中尺寸跨度 + /// + protected override int? GetMdSpan() { + if ( _config.Contains( UiConst.MdSpan ) ) + return base.GetMdSpan(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlMdSpan; + } + + /// + /// 获取Md中尺寸偏移量 + /// + protected override int? GetMdOffset() { + if ( _config.Contains( UiConst.MdOffset ) ) + return base.GetMdOffset(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlMdOffset; + } + + /// + /// 获取Md中尺寸顺序 + /// + protected override int? GetMdOrder() { + if ( _config.Contains( UiConst.MdOrder ) ) + return base.GetMdOrder(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlMdOrder; + } + + /// + /// 获取Md中尺寸左移 + /// + protected override int? GetMdPull() { + if ( _config.Contains( UiConst.MdPull ) ) + return base.GetMdPull(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlMdPull; + } + + /// + /// 获取Md中尺寸右移 + /// + protected override int? GetMdPush() { + if ( _config.Contains( UiConst.MdPush ) ) + return base.GetMdPush(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlMdPush; + } + + /// + /// 获取Lg宽尺寸响应式栅格 + /// + protected override string GetLg() { + if ( _config.Contains( UiConst.Lg ) ) + return base.GetLg(); + var shareConfig = GetFormItemShareConfig(); + if ( shareConfig.ControlLg.IsEmpty() == false ) + return shareConfig.ControlLg; + return base.GetLg(); + } + + /// + /// 获取Lg宽尺寸跨度 + /// + protected override int? GetLgSpan() { + if ( _config.Contains( UiConst.LgSpan ) ) + return base.GetLgSpan(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlLgSpan; + } + + /// + /// 获取Lg宽尺寸偏移量 + /// + protected override int? GetLgOffset() { + if ( _config.Contains( UiConst.LgOffset ) ) + return base.GetLgOffset(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlLgOffset; + } + + /// + /// 获取Lg宽尺寸顺序 + /// + protected override int? GetLgOrder() { + if ( _config.Contains( UiConst.LgOrder ) ) + return base.GetLgOrder(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlLgOrder; + } + + /// + /// 获取Lg宽尺寸左移 + /// + protected override int? GetLgPull() { + if ( _config.Contains( UiConst.LgPull ) ) + return base.GetLgPull(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlLgPull; + } + + /// + /// 获取Lg宽尺寸右移 + /// + protected override int? GetLgPush() { + if ( _config.Contains( UiConst.LgPush ) ) + return base.GetLgPush(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlLgPush; + } + + /// + /// 获取Xl超宽尺寸响应式栅格 + /// + protected override string GetXl() { + if ( _config.Contains( UiConst.Xl ) ) + return base.GetXl(); + var shareConfig = GetFormItemShareConfig(); + if ( shareConfig.ControlXl.IsEmpty() == false ) + return shareConfig.ControlXl; + return base.GetXl(); + } + + /// + /// 获取Xl超宽尺寸跨度 + /// + protected override int? GetXlSpan() { + if ( _config.Contains( UiConst.XlSpan ) ) + return base.GetXlSpan(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlXlSpan; + } + + /// + /// 获取Xl超宽尺寸偏移量 + /// + protected override int? GetXlOffset() { + if ( _config.Contains( UiConst.XlOffset ) ) + return base.GetXlOffset(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlXlOffset; + } + + /// + /// 获取Xl超宽尺寸顺序 + /// + protected override int? GetXlOrder() { + if ( _config.Contains( UiConst.XlOrder ) ) + return base.GetXlOrder(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlXlOrder; + } + + /// + /// 获取Xl超宽尺寸左移 + /// + protected override int? GetXlPull() { + if ( _config.Contains( UiConst.XlPull ) ) + return base.GetXlPull(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlXlPull; + } + + /// + /// 获取Xl超宽尺寸右移 + /// + protected override int? GetXlPush() { + if ( _config.Contains( UiConst.XlPush ) ) + return base.GetXlPush(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlXlPush; + } + + /// + /// 获取Xxl极宽尺寸响应式栅格 + /// + protected override string GetXxl() { + if ( _config.Contains( UiConst.Xxl ) ) + return base.GetXxl(); + var shareConfig = GetFormItemShareConfig(); + if ( shareConfig.ControlXxl.IsEmpty() == false ) + return shareConfig.ControlXxl; + return base.GetXxl(); + } + + /// + /// 获取Xxl极宽尺寸跨度 + /// + protected override int? GetXxlSpan() { + if ( _config.Contains( UiConst.XxlSpan ) ) + return base.GetXxlSpan(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlXxlSpan; + } + + /// + /// 获取Xxl极宽尺寸偏移量 + /// + protected override int? GetXxlOffset() { + if ( _config.Contains( UiConst.XxlOffset ) ) + return base.GetXxlOffset(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlXxlOffset; + } + + /// + /// 获取Xxl极宽尺寸顺序 + /// + protected override int? GetXxlOrder() { + if ( _config.Contains( UiConst.XxlOrder ) ) + return base.GetXxlOrder(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlXxlOrder; + } + + /// + /// 获取Xxl极宽尺寸左移 + /// + protected override int? GetXxlPull() { + if ( _config.Contains( UiConst.XxlPull ) ) + return base.GetXxlPull(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlXxlPull; + } + + /// + /// 获取Xxl极宽尺寸右移 + /// + protected override int? GetXxlPush() { + if ( _config.Contains( UiConst.XxlPush ) ) + return base.GetXxlPush(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.ControlXxlPush; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + ConfigColumn().ValidateStatus().HasFeedback().Extra() + .SuccessTip().WarningTip().ErrorTip().ValidatingTip() + .AutoTips().DisableAutoTips(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/Builders/FormItemBuilder.cs b/src/Util.Ui.NgZorro/Components/Forms/Builders/FormItemBuilder.cs new file mode 100644 index 000000000..52ae95f7e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/Builders/FormItemBuilder.cs @@ -0,0 +1,105 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Forms.Builders; + +/// +/// 表单项标签生成器 +/// +public class FormItemBuilder : RowBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表单项共享配置 + /// + private readonly FormItemShareConfig _shareConfig; + + /// + /// 初始化表单项标签生成器 + /// + /// 配置 + public FormItemBuilder( Config config ) : base( config, "nz-form-item" ) { + _config = config; + _shareConfig = GetFormItemShareConfig(); + } + + /// + /// 获取表单项共享配置 + /// + private FormItemShareConfig GetFormItemShareConfig() { + return _config.GetValueFromItems() ?? new FormItemShareConfig(); + } + + /// + /// 配置对齐 + /// + public override FormItemBuilder Align() { + Align( _shareConfig.Align ); + BindAlign( _shareConfig.BindAlign ); + return base.Align(); + } + + /// + /// 配置间隔 + /// + public override FormItemBuilder Gutter() { + Gutter( _shareConfig.Gutter ); + return base.Gutter(); + } + + /// + /// 配置水平排列方式 + /// + public override FormItemBuilder Justify() { + Justify( _shareConfig.Justify ); + BindJustify( _shareConfig.BindJustify ); + return base.Justify(); + } + + /// + /// 配置间距项 + /// + public FormItemBuilder SpaceItem() { + this.SpaceItem( _shareConfig.SpaceItem ); + return this; + } + + /// + /// ngIf + /// + public FormItemBuilder AngularIf() { + if ( _shareConfig.NgIf.IsEmpty() == false ) + this.NgIf( _shareConfig.NgIf ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + SpaceItem().AngularIf().ConfigRow().TableEdit(); + } + + /// + /// 配置表格编辑 + /// + private void TableEdit() { + var config = GetTableColumnShareConfig(); + if ( config == null ) + return; + Class( "mb0" ); + } + + /// + /// 获取表格列共享配置 + /// + public TableColumnShareConfig GetTableColumnShareConfig() { + return _config.GetValueFromItems(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/Builders/FormLabelBuilder.cs b/src/Util.Ui.NgZorro/Components/Forms/Builders/FormLabelBuilder.cs new file mode 100644 index 000000000..036571841 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/Builders/FormLabelBuilder.cs @@ -0,0 +1,592 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Forms.Builders; + +/// +/// 表单标签标签生成器 +/// +public class FormLabelBuilder : ColumnBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化表单标签标签生成器 + /// + public FormLabelBuilder( Config config ) : base( config, "nz-form-label" ) { + _config = config; + } + + /// + /// 获取表单项共享配置 + /// + private FormItemShareConfig GetFormItemShareConfig() { + return _config.GetValueFromItems() ?? new FormItemShareConfig(); + } + + /// + /// 配置必填样式 + /// + public FormLabelBuilder Required() { + AttributeIfNotEmpty( "[nzRequired]", _config.GetValue( UiConst.Required ) ); + return this; + } + + /// + /// 配置不显示冒号 + /// + public FormLabelBuilder NoColon() { + AttributeIfNotEmpty( "[nzNoColon]", _config.GetValue( UiConst.NoColon ) ); + return this; + } + + /// + /// 配置标签文本是否换行 + /// + public FormLabelBuilder LabelWrap() { + AttributeIfNotEmpty( "[nzLabelWrap]", _config.GetValue( UiConst.LabelWrap ) ); + return this; + } + + /// + /// 配置标签文本对齐方式 + /// + public FormLabelBuilder LabelAlign() { + AttributeIfNotEmpty( "nzLabelAlign", _config.GetValue( UiConst.LabelAlign )?.Description() ); + AttributeIfNotEmpty( "[nzLabelAlign]", _config.GetValue( AngularConst.BindLabelAlign ) ); + return this; + } + + /// + /// 配置NzFor + /// + public FormLabelBuilder NzFor() { + if ( _config.Contains( UiConst.NzFor ) || _config.Contains( AngularConst.BindNzFor ) ) { + AttributeIfNotEmpty( "nzFor", _config.GetValue( UiConst.NzFor ) ); + AttributeIfNotEmpty( "[nzFor]", _config.GetValue( AngularConst.BindNzFor ) ); + return this; + } + var shareConfig = GetFormItemShareConfig(); + if ( shareConfig.AutoNzFor != true ) + return this; + AttributeIfNotEmpty( "nzFor", shareConfig.ControlId ); + return this; + } + + /// + /// 配置提示信息 + /// + public FormLabelBuilder TooltipTitle() { + AttributeIfNotEmpty( "nzTooltipTitle", _config.GetValue( UiConst.TooltipTitle ) ); + AttributeIfNotEmpty( "[nzTooltipTitle]", _config.GetValue( AngularConst.BindTooltipTitle ) ); + return this; + } + + /// + /// 配置提示图标 + /// + public FormLabelBuilder TooltipIcon() { + AttributeIfNotEmpty( "nzTooltipIcon", _config.GetValue( UiConst.TooltipIcon )?.Description() ); + AttributeIfNotEmpty( "[nzTooltipIcon]", _config.GetValue( AngularConst.BindTooltipIcon ) ); + return this; + } + + /// + /// 配置跨度 + /// + public override FormLabelBuilder Span() { + if ( _config.Contains( UiConst.Span ) || _config.Contains( AngularConst.BindSpan ) ) + return base.Span(); + var shareConfig = GetFormItemShareConfig(); + AttributeIfNotEmpty( "[nzSpan]", shareConfig.LabelSpan ); + return this; + } + + /// + /// 配置偏移量 + /// + public override FormLabelBuilder Offset() { + if( _config.Contains( UiConst.Offset ) || _config.Contains( AngularConst.BindOffset ) ) + return base.Offset(); + var shareConfig = GetFormItemShareConfig(); + AttributeIfNotEmpty( "[nzOffset]", shareConfig.LabelOffset ); + return this; + } + + /// + /// 配置栅格顺序 + /// + public override FormLabelBuilder Order() { + if( _config.Contains( UiConst.Order ) || _config.Contains( AngularConst.BindOrder ) ) + return base.Order(); + var shareConfig = GetFormItemShareConfig(); + AttributeIfNotEmpty( "[nzOrder]", shareConfig.LabelOrder ); + return this; + } + + /// + /// 配置栅格左移 + /// + public override FormLabelBuilder Pull() { + if( _config.Contains( UiConst.Pull ) || _config.Contains( AngularConst.BindPull ) ) + return base.Pull(); + var shareConfig = GetFormItemShareConfig(); + AttributeIfNotEmpty( "[nzPull]", shareConfig.LabelPull ); + return this; + } + + /// + /// 配置栅格右移 + /// + public override FormLabelBuilder Push() { + if( _config.Contains( UiConst.Push ) || _config.Contains( AngularConst.BindPush ) ) + return base.Push(); + var shareConfig = GetFormItemShareConfig(); + AttributeIfNotEmpty( "[nzPush]", shareConfig.LabelPush ); + return this; + } + + /// + /// 配置Flex布局 + /// + public override FormLabelBuilder Flex() { + if( _config.Contains( UiConst.Flex ) || _config.Contains( AngularConst.BindFlex ) ) + return base.Flex(); + var shareConfig = GetFormItemShareConfig(); + AttributeIfNotEmpty( "[nzFlex]", shareConfig.LabelFlex ); + return this; + } + + /// + /// 获取Xs超窄尺寸响应式栅格 + /// + protected override string GetXs() { + if ( _config.Contains( UiConst.Xs ) ) + return base.GetXs(); + var shareConfig = GetFormItemShareConfig(); + if ( shareConfig.LabelXs.IsEmpty() == false ) + return shareConfig.LabelXs; + return base.GetXs(); + } + + /// + /// 获取Xs超窄尺寸跨度 + /// + protected override int? GetXsSpan() { + if( _config.Contains( UiConst.XsSpan ) ) + return base.GetXsSpan(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelXsSpan; + } + + /// + /// 获取Xs超窄尺寸偏移量 + /// + protected override int? GetXsOffset() { + if( _config.Contains( UiConst.XsOffset ) ) + return base.GetXsOffset(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelXsOffset; + } + + /// + /// 获取Xs超窄尺寸顺序 + /// + protected override int? GetXsOrder() { + if( _config.Contains( UiConst.XsOrder ) ) + return base.GetXsOrder(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelXsOrder; + } + + /// + /// 获取Xs超窄尺寸左移 + /// + protected override int? GetXsPull() { + if( _config.Contains( UiConst.XsPull ) ) + return base.GetXsPull(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelXsPull; + } + + /// + /// 获取Xs超窄尺寸右移 + /// + protected override int? GetXsPush() { + if( _config.Contains( UiConst.XsPush ) ) + return base.GetXsPush(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelXsPush; + } + + /// + /// 获取Sm窄尺寸响应式栅格 + /// + protected override string GetSm() { + if ( _config.Contains( UiConst.Sm ) ) + return base.GetSm(); + var shareConfig = GetFormItemShareConfig(); + if ( shareConfig.LabelSm.IsEmpty() == false ) + return shareConfig.LabelSm; + return base.GetSm(); + } + + /// + /// 获取Sm窄尺寸跨度 + /// + protected override int? GetSmSpan() { + if( _config.Contains( UiConst.SmSpan ) ) + return base.GetSmSpan(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelSmSpan; + } + + /// + /// 获取Sm窄尺寸偏移量 + /// + protected override int? GetSmOffset() { + if( _config.Contains( UiConst.SmOffset ) ) + return base.GetSmOffset(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelSmOffset; + } + + /// + /// 获取Sm窄尺寸顺序 + /// + protected override int? GetSmOrder() { + if( _config.Contains( UiConst.SmOrder ) ) + return base.GetSmOrder(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelSmOrder; + } + + /// + /// 获取Sm窄尺寸左移 + /// + protected override int? GetSmPull() { + if( _config.Contains( UiConst.SmPull ) ) + return base.GetSmPull(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelSmPull; + } + + /// + /// 获取Sm窄尺寸右移 + /// + protected override int? GetSmPush() { + if( _config.Contains( UiConst.SmPush ) ) + return base.GetSmPush(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelSmPush; + } + + /// + /// 获取Md中尺寸响应式栅格 + /// + protected override string GetMd() { + if ( _config.Contains( UiConst.Md ) ) + return base.GetMd(); + var shareConfig = GetFormItemShareConfig(); + if ( shareConfig.LabelMd.IsEmpty() == false ) + return shareConfig.LabelMd; + return base.GetMd(); + } + + /// + /// 获取Md中尺寸跨度 + /// + protected override int? GetMdSpan() { + if( _config.Contains( UiConst.MdSpan ) ) + return base.GetMdSpan(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelMdSpan; + } + + /// + /// 获取Md中尺寸偏移量 + /// + protected override int? GetMdOffset() { + if( _config.Contains( UiConst.MdOffset ) ) + return base.GetMdOffset(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelMdOffset; + } + + /// + /// 获取Md中尺寸顺序 + /// + protected override int? GetMdOrder() { + if( _config.Contains( UiConst.MdOrder ) ) + return base.GetMdOrder(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelMdOrder; + } + + /// + /// 获取Md中尺寸左移 + /// + protected override int? GetMdPull() { + if( _config.Contains( UiConst.MdPull ) ) + return base.GetMdPull(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelMdPull; + } + + /// + /// 获取Md中尺寸右移 + /// + protected override int? GetMdPush() { + if( _config.Contains( UiConst.MdPush ) ) + return base.GetMdPush(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelMdPush; + } + + /// + /// 获取Lg宽尺寸响应式栅格 + /// + protected override string GetLg() { + if ( _config.Contains( UiConst.Lg ) ) + return base.GetLg(); + var shareConfig = GetFormItemShareConfig(); + if ( shareConfig.LabelLg.IsEmpty() == false ) + return shareConfig.LabelLg; + return base.GetLg(); + } + + /// + /// 获取Lg宽尺寸跨度 + /// + protected override int? GetLgSpan() { + if( _config.Contains( UiConst.LgSpan ) ) + return base.GetLgSpan(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelLgSpan; + } + + /// + /// 获取Lg宽尺寸偏移量 + /// + protected override int? GetLgOffset() { + if( _config.Contains( UiConst.LgOffset ) ) + return base.GetLgOffset(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelLgOffset; + } + + /// + /// 获取Lg宽尺寸顺序 + /// + protected override int? GetLgOrder() { + if( _config.Contains( UiConst.LgOrder ) ) + return base.GetLgOrder(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelLgOrder; + } + + /// + /// 获取Lg宽尺寸左移 + /// + protected override int? GetLgPull() { + if( _config.Contains( UiConst.LgPull ) ) + return base.GetLgPull(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelLgPull; + } + + /// + /// 获取Lg宽尺寸右移 + /// + protected override int? GetLgPush() { + if( _config.Contains( UiConst.LgPush ) ) + return base.GetLgPush(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelLgPush; + } + + /// + /// 获取Xl超宽尺寸响应式栅格 + /// + protected override string GetXl() { + if ( _config.Contains( UiConst.Xl ) ) + return base.GetXl(); + var shareConfig = GetFormItemShareConfig(); + if ( shareConfig.LabelXl.IsEmpty() == false ) + return shareConfig.LabelXl; + return base.GetXl(); + } + + /// + /// 获取Xl超宽尺寸跨度 + /// + protected override int? GetXlSpan() { + if( _config.Contains( UiConst.XlSpan ) ) + return base.GetXlSpan(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelXlSpan; + } + + /// + /// 获取Xl超宽尺寸偏移量 + /// + protected override int? GetXlOffset() { + if( _config.Contains( UiConst.XlOffset ) ) + return base.GetXlOffset(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelXlOffset; + } + + /// + /// 获取Xl超宽尺寸顺序 + /// + protected override int? GetXlOrder() { + if( _config.Contains( UiConst.XlOrder ) ) + return base.GetXlOrder(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelXlOrder; + } + + /// + /// 获取Xl超宽尺寸左移 + /// + protected override int? GetXlPull() { + if( _config.Contains( UiConst.XlPull ) ) + return base.GetXlPull(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelXlPull; + } + + /// + /// 获取Xl超宽尺寸右移 + /// + protected override int? GetXlPush() { + if( _config.Contains( UiConst.XlPush ) ) + return base.GetXlPush(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelXlPush; + } + + /// + /// 获取Xxl极宽尺寸响应式栅格 + /// + protected override string GetXxl() { + if ( _config.Contains( UiConst.Xxl ) ) + return base.GetXxl(); + var shareConfig = GetFormItemShareConfig(); + if ( shareConfig.LabelXxl.IsEmpty() == false ) + return shareConfig.LabelXxl; + return base.GetXxl(); + } + + /// + /// 获取Xxl极宽尺寸跨度 + /// + protected override int? GetXxlSpan() { + if( _config.Contains( UiConst.XxlSpan ) ) + return base.GetXxlSpan(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelXxlSpan; + } + + /// + /// 获取Xxl极宽尺寸偏移量 + /// + protected override int? GetXxlOffset() { + if( _config.Contains( UiConst.XxlOffset ) ) + return base.GetXxlOffset(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelXxlOffset; + } + + /// + /// 获取Xxl极宽尺寸顺序 + /// + protected override int? GetXxlOrder() { + if( _config.Contains( UiConst.XxlOrder ) ) + return base.GetXxlOrder(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelXxlOrder; + } + + /// + /// 获取Xxl极宽尺寸左移 + /// + protected override int? GetXxlPull() { + if( _config.Contains( UiConst.XxlPull ) ) + return base.GetXxlPull(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelXxlPull; + } + + /// + /// 获取Xxl极宽尺寸右移 + /// + protected override int? GetXxlPush() { + if( _config.Contains( UiConst.XxlPush ) ) + return base.GetXxlPush(); + var shareConfig = GetFormItemShareConfig(); + return shareConfig.LabelXxlPush; + } + + /// + /// 配置文本 + /// + public FormLabelBuilder Text() { + return SetText( _config.GetValue( UiConst.Text ) ); + } + + /// + /// 配置内容 + /// + private FormLabelBuilder SetText( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( value.IsEmpty() ) + return this; + if ( options.EnableI18n ) { + this.AppendContentByI18n( value ); + return this; + } + AppendContent( value ); + return this; + } + + /// + /// 配置宽度 + /// + public FormLabelBuilder Width() { + var shareConfig = GetFormItemShareConfig(); + if( shareConfig.LabelWidth.IsEmpty() == false ) + Style( _config, "width", shareConfig.LabelWidth ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + Width(); + base.Config(); + ConfigColumn().Required().NoColon().LabelWrap().LabelAlign() + .NzFor().TooltipTitle().TooltipIcon(); + Text(); + } + + /// + /// 配置内容 + /// + protected override void ConfigContent( Config config ) { + if ( config.Content.IsEmpty() ) { + SetText( config.GetValue( UiConst.Title ) ); + return; + } + base.ConfigContent( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/Builders/FormSplitBuilder.cs b/src/Util.Ui.NgZorro/Components/Forms/Builders/FormSplitBuilder.cs new file mode 100644 index 000000000..038e6b39b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/Builders/FormSplitBuilder.cs @@ -0,0 +1,20 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Forms.Builders; + +/// +/// 表单分隔符标签生成器 +/// +public class FormSplitBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化表单分隔符标签生成器 + /// + public FormSplitBuilder( Config config ) : base( config,"nz-form-split" ) { + _config = config; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/Builders/FormTextBuilder.cs b/src/Util.Ui.NgZorro/Components/Forms/Builders/FormTextBuilder.cs new file mode 100644 index 000000000..86f08255c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/Builders/FormTextBuilder.cs @@ -0,0 +1,15 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Forms.Builders; + +/// +/// 表单文本标签生成器 +/// +public class FormTextBuilder : AngularTagBuilder { + /// + /// 初始化表单文本标签生成器 + /// + /// 配置 + public FormTextBuilder( Config config ) : base( config,"nz-form-text" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/Configs/FormItemShareConfig.cs b/src/Util.Ui.NgZorro/Components/Forms/Configs/FormItemShareConfig.cs new file mode 100644 index 000000000..66777535e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/Configs/FormItemShareConfig.cs @@ -0,0 +1,83 @@ +namespace Util.Ui.NgZorro.Components.Forms.Configs; + +/// +/// 表单项共享配置 +/// +public class FormItemShareConfig : FormShareConfig { + /// + /// 标识 + /// + public string Id { get; set; } + /// + /// nz-form-item 已创建 + /// + public bool FormItemCreated { get; set; } + /// + /// 自动创建nz-form-item + /// + public bool? AutoCreateFormItem { get; set; } + /// + /// 自动创建nz-form-label + /// + public bool? AutoCreateFormLabel { get; set; } + /// + /// 自动创建nz-form-control + /// + public bool? AutoCreateFormControl { get; set; } + /// + /// 控件标识 + /// + public string ControlId { get; set; } + /// + /// 标签文本 + /// + public string LabelText { get; set; } + /// + /// 标签文本 + /// + public string BindLabelText { get; set; } + /// + /// [nzErrorTip] + /// + public string BindErrorTip { get; set; } + /// + /// 是否存在额外提示 + /// + public bool HasExtra { get; set; } + /// + /// 是否存在成功提示 + /// + public bool HasSuccessTip { get; set; } + /// + /// 是否存在警告提示 + /// + public bool HasWarningTip { get; set; } + /// + /// 是否存在错误提示 + /// + public bool HasErrorTip { get; set; } + /// + /// 是否存在校验提示 + /// + public bool HasValidatingTip { get; set; } + /// + /// 是否启用验证扩展 + /// + public bool IsValidationExtend { get; set; } + /// + /// 验证扩展标识 + /// + public string ValidationExtendId { get; set; } + /// + /// 验证模板标识 + /// + public string ValidationTempalteId { get; set; } + /// + /// ngIf* + /// + public string NgIf { get; set; } + /// + /// 是否间距项 + /// + public bool SpaceItem { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/Configs/FormShareConfig.cs b/src/Util.Ui.NgZorro/Components/Forms/Configs/FormShareConfig.cs new file mode 100644 index 000000000..5e4a5a070 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/Configs/FormShareConfig.cs @@ -0,0 +1,450 @@ +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Forms.Configs; + +/// +/// 表单共享配置 +/// +public class FormShareConfig { + /// + /// 查询表单栅格列标识 + /// + private readonly List _columnIds; + + /// + /// 初始化表单共享配置 + /// + public FormShareConfig() { + _columnIds = new List(); + } + + /// + /// 表单标识 + /// + public string FormId { get; set; } + + /// + /// 获取查询条件数量 + /// + public int GetConditionCount() { + return _columnIds.Count - 1; + } + + /// + /// 添加查询表单栅格列标识 + /// + /// 栅格列标识 + public void AddColumnId( string columnId ) { + _columnIds.Add( columnId ); + } + + /// + /// 是否查询表单最后一个操作栅格 + /// + /// 栅格列标识 + public bool IsActionColumn( string columnId ) { + if ( GetConditionCount() == 0 ) + return false; + return _columnIds.LastOrDefault() == columnId; + } + + /// + /// 是否隐藏查询条件 + /// + /// 栅格列标识 + public bool IsHide( string columnId ) { + var index = _columnIds.FindIndex( id => id == columnId ); + if ( SearchFormShowNumber.SafeValue() == 0 ) + SearchFormShowNumber = 3; + return index >= SearchFormShowNumber; + } + + /// + /// form 已创建 + /// + public bool FormCreated { get; set; } + /// + /// 是否查询表单 + /// + public bool? IsSearch { get; set; } + /// + /// 查询表单初始显示的查询条件数量 + /// + public int? SearchFormShowNumber { get; set; } + /// + /// 查询表单每行显示几列 + /// + public int? SearchFormColumnsNumber { get; set; } + /// + /// 是否显示标签 + /// + public bool? ShowLabel { get; set; } + /// + /// 标签宽度 + /// + public string LabelWidth { get; set; } + /// + /// 垂直对齐方式 + /// + public Align? Align { get; set; } + /// + /// 垂直对齐方式 + /// + public string BindAlign { get; set; } + /// + /// 栅格间隔 + /// + public string Gutter { get; set; } + /// + /// 水平排列方式 + /// + public Justify? Justify { get; set; } + /// + /// 水平排列方式 + /// + public string BindJustify { get; set; } + /// + /// 自动设置表单标签 nzFor 属性 + /// + public bool? AutoNzFor { get; set; } + /// + /// 展示校验状态图标 + /// + public bool? HasFeedback { get; set; } + /// + /// 标签跨度 + /// + public string LabelSpan { get; set; } + /// + /// 标签偏移量 + /// + public string LabelOffset { get; set; } + /// + /// 标签栅格顺序 + /// + public string LabelOrder { get; set; } + /// + /// 标签栅格左移 + /// + public string LabelPull { get; set; } + /// + /// 标签栅格右移 + /// + public string LabelPush { get; set; } + /// + /// 标签Flex布局 + /// + public string LabelFlex { get; set; } + /// + /// 标签超窄尺寸响应式栅格 + /// + public string LabelXs { get; set; } + /// + /// 标签超窄尺寸响应式栅格跨度 + /// + public int? LabelXsSpan { get; set; } + /// + /// 标签超窄尺寸响应式栅格偏移量 + /// + public int? LabelXsOffset { get; set; } + /// + /// 标签超窄尺寸响应式栅格顺序 + /// + public int? LabelXsOrder { get; set; } + /// + /// 标签超窄尺寸响应式栅格左移 + /// + public int? LabelXsPull { get; set; } + /// + /// 标签超窄尺寸响应式栅格右移 + /// + public int? LabelXsPush { get; set; } + /// + /// 标签窄尺寸响应式栅格 + /// + public string LabelSm { get; set; } + /// + /// 标签窄尺寸响应式栅格跨度 + /// + public int? LabelSmSpan { get; set; } + /// + /// 标签窄尺寸响应式栅格偏移量 + /// + public int? LabelSmOffset { get; set; } + /// + /// 标签窄尺寸响应式栅格顺序 + /// + public int? LabelSmOrder { get; set; } + /// + /// 标签窄尺寸响应式栅格左移 + /// + public int? LabelSmPull { get; set; } + /// + /// 标签窄尺寸响应式栅格右移 + /// + public int? LabelSmPush { get; set; } + /// + /// 标签中尺寸响应式栅格 + /// + public string LabelMd { get; set; } + /// + /// 标签中尺寸响应式栅格跨度 + /// + public int? LabelMdSpan { get; set; } + /// + /// 标签中尺寸响应式栅格偏移量 + /// + public int? LabelMdOffset { get; set; } + /// + /// 标签中尺寸响应式栅格顺序 + /// + public int? LabelMdOrder { get; set; } + /// + /// 标签中尺寸响应式栅格左移 + /// + public int? LabelMdPull { get; set; } + /// + /// 标签中尺寸响应式栅格右移 + /// + public int? LabelMdPush { get; set; } + /// + /// 标签宽尺寸响应式栅格 + /// + public string LabelLg { get; set; } + /// + /// 标签宽尺寸响应式栅格跨度 + /// + public int? LabelLgSpan { get; set; } + /// + /// 标签宽尺寸响应式栅格偏移量 + /// + public int? LabelLgOffset { get; set; } + /// + /// 标签宽尺寸响应式栅格顺序 + /// + public int? LabelLgOrder { get; set; } + /// + /// 标签宽尺寸响应式栅格左移 + /// + public int? LabelLgPull { get; set; } + /// + /// 标签宽尺寸响应式栅格右移 + /// + public int? LabelLgPush { get; set; } + /// + /// 标签超宽尺寸响应式栅格 + /// + public string LabelXl { get; set; } + /// + /// 标签超宽尺寸响应式栅格跨度 + /// + public int? LabelXlSpan { get; set; } + /// + /// 标签超宽尺寸响应式栅格偏移量 + /// + public int? LabelXlOffset { get; set; } + /// + /// 标签超宽尺寸响应式栅格顺序 + /// + public int? LabelXlOrder { get; set; } + /// + /// 标签超宽尺寸响应式栅格左移 + /// + public int? LabelXlPull { get; set; } + /// + /// 标签超宽尺寸响应式栅格右移 + /// + public int? LabelXlPush { get; set; } + /// + /// 标签极宽尺寸响应式栅格 + /// + public string LabelXxl { get; set; } + /// + /// 标签极宽尺寸响应式栅格跨度 + /// + public int? LabelXxlSpan { get; set; } + /// + /// 标签极宽尺寸响应式栅格偏移量 + /// + public int? LabelXxlOffset { get; set; } + /// + /// 标签极宽尺寸响应式栅格顺序 + /// + public int? LabelXxlOrder { get; set; } + /// + /// 标签极宽尺寸响应式栅格左移 + /// + public int? LabelXxlPull { get; set; } + /// + /// 标签极宽尺寸响应式栅格右移 + /// + public int? LabelXxlPush { get; set; } + /// + /// 控件跨度 + /// + public string ControlSpan { get; set; } + /// + /// 控件偏移量 + /// + public string ControlOffset { get; set; } + /// + /// 控件栅格顺序 + /// + public string ControlOrder { get; set; } + /// + /// 控件栅格左移 + /// + public string ControlPull { get; set; } + /// + /// 控件栅格右移 + /// + public string ControlPush { get; set; } + /// + /// 控件Flex布局 + /// + public string ControlFlex { get; set; } + /// + /// 控件超窄尺寸响应式栅格 + /// + public string ControlXs { get; set; } + /// + /// 控件超窄尺寸响应式栅格跨度 + /// + public int? ControlXsSpan { get; set; } + /// + /// 控件超窄尺寸响应式栅格偏移量 + /// + public int? ControlXsOffset { get; set; } + /// + /// 控件超窄尺寸响应式栅格顺序 + /// + public int? ControlXsOrder { get; set; } + /// + /// 控件超窄尺寸响应式栅格左移 + /// + public int? ControlXsPull { get; set; } + /// + /// 控件超窄尺寸响应式栅格右移 + /// + public int? ControlXsPush { get; set; } + /// + /// 控件窄尺寸响应式栅格 + /// + public string ControlSm { get; set; } + /// + /// 控件窄尺寸响应式栅格跨度 + /// + public int? ControlSmSpan { get; set; } + /// + /// 控件窄尺寸响应式栅格偏移量 + /// + public int? ControlSmOffset { get; set; } + /// + /// 控件窄尺寸响应式栅格顺序 + /// + public int? ControlSmOrder { get; set; } + /// + /// 控件窄尺寸响应式栅格左移 + /// + public int? ControlSmPull { get; set; } + /// + /// 控件窄尺寸响应式栅格右移 + /// + public int? ControlSmPush { get; set; } + /// + /// 控件中尺寸响应式栅格 + /// + public string ControlMd { get; set; } + /// + /// 控件中尺寸响应式栅格跨度 + /// + public int? ControlMdSpan { get; set; } + /// + /// 控件中尺寸响应式栅格偏移量 + /// + public int? ControlMdOffset { get; set; } + /// + /// 控件中尺寸响应式栅格顺序 + /// + public int? ControlMdOrder { get; set; } + /// + /// 控件中尺寸响应式栅格左移 + /// + public int? ControlMdPull { get; set; } + /// + /// 控件中尺寸响应式栅格右移 + /// + public int? ControlMdPush { get; set; } + /// + /// 控件宽尺寸响应式栅格 + /// + public string ControlLg { get; set; } + /// + /// 控件宽尺寸响应式栅格跨度 + /// + public int? ControlLgSpan { get; set; } + /// + /// 控件宽尺寸响应式栅格偏移量 + /// + public int? ControlLgOffset { get; set; } + /// + /// 控件宽尺寸响应式栅格顺序 + /// + public int? ControlLgOrder { get; set; } + /// + /// 控件宽尺寸响应式栅格左移 + /// + public int? ControlLgPull { get; set; } + /// + /// 控件宽尺寸响应式栅格右移 + /// + public int? ControlLgPush { get; set; } + /// + /// 控件超宽尺寸响应式栅格 + /// + public string ControlXl { get; set; } + /// + /// 控件超宽尺寸响应式栅格跨度 + /// + public int? ControlXlSpan { get; set; } + /// + /// 控件超宽尺寸响应式栅格偏移量 + /// + public int? ControlXlOffset { get; set; } + /// + /// 控件超宽尺寸响应式栅格顺序 + /// + public int? ControlXlOrder { get; set; } + /// + /// 控件超宽尺寸响应式栅格左移 + /// + public int? ControlXlPull { get; set; } + /// + /// 控件超宽尺寸响应式栅格右移 + /// + public int? ControlXlPush { get; set; } + /// + /// 控件极宽尺寸响应式栅格 + /// + public string ControlXxl { get; set; } + /// + /// 控件极宽尺寸响应式栅格跨度 + /// + public int? ControlXxlSpan { get; set; } + /// + /// 控件极宽尺寸响应式栅格偏移量 + /// + public int? ControlXxlOffset { get; set; } + /// + /// 控件极宽尺寸响应式栅格顺序 + /// + public int? ControlXxlOrder { get; set; } + /// + /// 控件极宽尺寸响应式栅格左移 + /// + public int? ControlXxlPull { get; set; } + /// + /// 控件极宽尺寸响应式栅格右移 + /// + public int? ControlXxlPush { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/FormContainerTagHelper.cs b/src/Util.Ui.NgZorro/Components/Forms/FormContainerTagHelper.cs new file mode 100644 index 000000000..699648249 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/FormContainerTagHelper.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Containers.Renders; +using Util.Ui.NgZorro.Components.Forms.Helpers; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Forms; + +/// +/// 表单容器,生成的标签为<ng-container></ng-container> +/// +[HtmlTargetElement( "util-form-container" )] +public class FormContainerTagHelper : FormContainerTagHelperBase { + /// + /// 配置 + /// + private Config _config; + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new FormShareService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new ContainerRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/FormControlTagHelper.cs b/src/Util.Ui.NgZorro/Components/Forms/FormControlTagHelper.cs new file mode 100644 index 000000000..131d376ad --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/FormControlTagHelper.cs @@ -0,0 +1,88 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Forms.Helpers; +using Util.Ui.NgZorro.Components.Forms.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Forms; + +/// +/// 表单域,生成的标签为<nz-form-control></nz-form-control> +/// +[HtmlTargetElement( "util-form-control" )] +public class FormControlTagHelper : ColumnTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// [nzValidateStatus],校验状态,可选值: 'success' | 'warning' | 'error' | 'validating' | FormControl | NgModel + /// + public string ValidateStatus { get; set; } + /// + /// [nzHasFeedback],是否显示校验状态反馈图标,类型: boolean, 默认值: false + /// + public string HasFeedback { get; set; } + /// + /// nzExtra,额外提示信息, 类型: string | TemplateRef<void> + /// + public string Extra { get; set; } + /// + /// [nzExtra],额外提示信息, 类型: string | TemplateRef<void> + /// + public string BindExtra { get; set; } + /// + /// nzSuccessTip,校验成功提示信息, 类型: string \| TemplateRef<{ $implicit: FormControl \| NgModel }> + /// + public string SuccessTip { get; set; } + /// + /// [nzSuccessTip],校验成功提示信息, 类型: string \| TemplateRef<{ $implicit: FormControl \| NgModel }> + /// + public string BindSuccessTip { get; set; } + /// + /// nzWarningTip,校验状态为警告时的提示信息, 类型: string \| TemplateRef<{ $implicit: FormControl \| NgModel }> + /// + public string WarningTip { get; set; } + /// + /// [nzWarningTip],校验状态为警告时的提示信息, 类型: string \| TemplateRef<{ $implicit: FormControl \| NgModel }> + /// + public string BindWarningTip { get; set; } + /// + /// nzErrorTip,校验状态为错误时的提示信息, 类型: string \| TemplateRef<{ $implicit: FormControl \| NgModel }> + /// + public string ErrorTip { get; set; } + /// + /// [nzErrorTip],校验状态为错误时的提示信息, 类型: string \| TemplateRef<{ $implicit: FormControl \| NgModel }> + /// + public string BindErrorTip { get; set; } + /// + /// nzValidatingTip,正在校验时的提示信息, 类型: string \| TemplateRef<{ $implicit: FormControl \| NgModel }> + /// + public string ValidatingTip { get; set; } + /// + /// [nzValidatingTip],正在校验时的提示信息, 类型: string \| TemplateRef<{ $implicit: FormControl \| NgModel }> + /// + public string BindValidatingTip { get; set; } + /// + /// [nzAutoTips],自动提示, 类型: Record<string, Record<string, string>> + /// + public string AutoTips { get; set; } + /// + /// [nzDisableAutoTips],是否禁用自动提示, 类型: boolean, 默认值: false + /// + public string DisableAutoTips { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new FormItemShareService( _config ); + service.Init(); + service.AutoCreateFormControl( false ); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new FormControlRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/FormItemTagHelper.cs b/src/Util.Ui.NgZorro/Components/Forms/FormItemTagHelper.cs new file mode 100644 index 000000000..e0184c5e0 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/FormItemTagHelper.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Forms.Helpers; +using Util.Ui.NgZorro.Components.Forms.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Forms; + +/// +/// 表单项,生成的标签为<nz-form-item></nz-form-item> +/// +[HtmlTargetElement( "util-form-item" )] +public class FormItemTagHelper : RowTagHelperBase { + /// + /// 配置 + /// + private Config _config; + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new FormItemShareService( _config ); + service.Init(); + service.AutoCreateFormItem( false ); + service.Created(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new FormItemRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/FormLabelTagHelper.cs b/src/Util.Ui.NgZorro/Components/Forms/FormLabelTagHelper.cs new file mode 100644 index 000000000..1e7b1f771 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/FormLabelTagHelper.cs @@ -0,0 +1,96 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Expressions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Forms.Helpers; +using Util.Ui.NgZorro.Components.Forms.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Forms; + +/// +/// 表单标签,生成的标签为<nz-form-label></nz-form-label> +/// +[HtmlTargetElement( "util-form-label" )] +public class FormLabelTagHelper : ColumnTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 扩展属性,内容文本,支持多语言 + /// + public string Text { get; set; } + /// + /// 扩展属性,属性表达式 + /// + public ModelExpression For { get; set; } + /// + /// [nzRequired],是否必填项,显示红色星号, 类型: boolean, 默认值: false + /// + public string Required { get; set; } + /// + /// [nzNoColon],是否不显示冒号, 类型: boolean, 默认值: false + /// + public string NoColon { get; set; } + /// + /// [nzLabelWrap],标签文本是否换行, 类型: boolean, 默认值: false + /// + public string LabelWrap { get; set; } + /// + /// nzLabelAlign,标签文本对齐方式, 类型: 'left' | 'right', 默认值: 'right' + /// + public LabelAlign LabelAlign { get; set; } + /// + /// [nzLabelAlign],标签文本对齐方式, 类型: 'left' | 'right', 默认值: 'right' + /// + public string BindLabelAlign { get; set; } + /// + /// nzFor,设置标签指向的组件Id,注意:应设置组件的 raw-id 或 nz-id,即原始Id属性 + /// + public string NzFor { get; set; } + /// + /// [nzFor],设置标签指向的组件Id,注意:应设置组件的 raw-id 或 nz-id,即原始Id属性 + /// + public string BindNzFor { get; set; } + /// + /// nzTooltipTitle,提示信息,类型: string | TemplateRef<void> + /// + public string TooltipTitle { get; set; } + /// + /// [nzTooltipTitle],提示信息,类型: string | TemplateRef<void> + /// + public string BindTooltipTitle { get; set; } + /// + /// nzTooltipIcon,提示图标 + /// + public AntDesignIcon TooltipIcon { get; set; } + /// + /// [nzTooltipIcon],提示图标, 类型: string | NzFormTooltipIcon + /// + public string BindTooltipIcon { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new FormItemShareService( _config ); + service.Init(); + service.AutoCreateFormLabel( false ); + LoadExpression(); + } + + /// + /// 加载表达式 + /// + private void LoadExpression() { + var loader = new ExpressionLoader(); + loader.Load( _config ); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new FormLabelRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/FormSplitTagHelper.cs b/src/Util.Ui.NgZorro/Components/Forms/FormSplitTagHelper.cs new file mode 100644 index 000000000..761ff0aed --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/FormSplitTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Forms.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Forms; + +/// +/// 表单分隔符,生成的标签为<nz-form-split></nz-form-split> +/// +[HtmlTargetElement( "util-form-split" )] +public class FormSplitTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new FormSplitRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/FormTagHelper.cs b/src/Util.Ui.NgZorro/Components/Forms/FormTagHelper.cs new file mode 100644 index 000000000..6083bf491 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/FormTagHelper.cs @@ -0,0 +1,98 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Forms.Helpers; +using Util.Ui.NgZorro.Components.Forms.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Forms; + +/// +/// 表单,生成的标签为<form nz-form></form> +/// +[HtmlTargetElement( "util-form" )] +public class FormTagHelper : FormContainerTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// nzLayout,布局方式,可选值: 'horizontal' | 'vertical' | 'inline' ,默认值: 'horizontal' + /// + public FormLayout Layout { get; set; } + /// + /// [nzLayout],布局方式,可选值: 'horizontal' | 'vertical' | 'inline' ,默认值: 'horizontal' + /// + public string BindLayout { get; set; } + /// + /// [nzAutoTips],自动提示,配置 <util-form-control> 的 auto-tips 默认值, 类型: Record<string, Record<string, string>> + /// + public string AutoTips { get; set; } + /// + /// [nzDisableAutoTips],是否禁用自动提示,配置 <util-form-control> 的 disable-auto-tips 默认值, 类型: boolean, 默认值: false + /// + public string DisableAutoTips { get; set; } + /// + /// [nzNoColon],是否不显示表单标签的冒号,配置 <util-form-label> 的 no-colon 默认值, 类型: boolean, 默认值: false + /// + public string NoColon { get; set; } + /// + /// [nzLabelWrap],表单标签文本是否换行,配置 <util-form-label> 的 label-wrap 默认值, 类型: boolean, 默认值: false + /// + public string LabelWrap { get; set; } + /// + /// nzLabelAlign,表单标签文本对齐方式, 配置 <util-form-label> 的 label-align 默认值, 类型: 'left' | 'right', 默认值: 'right' + /// + public LabelAlign LabelAlign { get; set; } + /// + /// [nzLabelAlign],表单标签文本对齐方式, 配置 <util-form-label> 的 label-align 默认值, 类型: 'left' | 'right', 默认值: 'right' + /// + public string BindLabelAlign { get; set; } + /// + /// nzTooltipIcon,表单标签提示图标,配置 <util-form-label> 的 tooltip-icon 默认值, 类型: string | { type: string; theme: ThemeType },默认值: { type: 'question-circle', theme: 'outline' } + /// + public AntDesignIcon TooltipIcon { get; set; } + /// + /// [nzTooltipIcon],表单标签提示图标,配置 <util-form-label> 的 tooltip-icon 默认值, 类型: string | { type: string; theme: ThemeType },默认值: { type: 'question-circle', theme: 'outline' } + /// + public string BindTooltipIcon { get; set; } + /// + /// autocomplete,是否打开浏览器自动完成功能,设置为 false 关闭浏览器自动完成 + /// + public bool Autocomplete { get; set; } + /// + /// [formGroup],表单组实例,类型: FormGroup + /// + public string FormGroup { get; set; } + /// + /// 扩展属性,是否自动创建表单标签 <nz-form-label>, 类型: boolean + /// + public bool ShowLabel { get; set; } + /// + /// 扩展属性,表单标签宽度, 可设置数值,单位: px, 范例: 100, 表示 "100px", 也可自行设置单位, 范例: "20%" + /// + public string LabelWidth { get; set; } + /// + /// novalidate,是否不验证表单,类型: boolean, 默认值: false + /// + public bool Novalidate { get; set; } + /// + /// (ngSubmit),表单提交事件,类型: EventEmitter<void> + /// + public string OnSubmit { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new FormShareService( _config ); + service.Init(); + service.Created(); + service.SetFormId(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new FormRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/FormTextTagHelper.cs b/src/Util.Ui.NgZorro/Components/Forms/FormTextTagHelper.cs new file mode 100644 index 000000000..b6bd24e83 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/FormTextTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Forms.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Forms; + +/// +/// 表单文本,生成的标签为<nz-form-text></nz-form-text> +/// +[HtmlTargetElement( "util-form-text" )] +public class FormTextTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new FormTextRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/Helpers/FormItemShareService.cs b/src/Util.Ui.NgZorro/Components/Forms/Helpers/FormItemShareService.cs new file mode 100644 index 000000000..232ef29ff --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/Helpers/FormItemShareService.cs @@ -0,0 +1,424 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Components.Tables.Configs; + +namespace Util.Ui.NgZorro.Components.Forms.Helpers; + +/// +/// 表单项共享服务 +/// +public class FormItemShareService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表单项共享配置 + /// + private FormItemShareConfig _shareConfig; + /// + /// 是否禁用自动设置表单标签 nzFor + /// + private bool _isDisableAutoNzFor; + + /// + /// 初始化表单项共享服务 + /// + /// 配置 + public FormItemShareService( Config config ) { + _config = config; + } + + /// + /// 设置 nz-form-item 已创建 + /// + public void Created() { + _shareConfig.FormItemCreated = true; + } + + /// + /// 禁用自动设置表单标签 nzFor + /// + public void DisableAutoNzFor() { + _isDisableAutoNzFor = true; + } + + /// + /// 初始化控件Id + /// + public void InitId() { + if ( _shareConfig.Id.IsEmpty() == false ) + return; + _shareConfig.Id = _config.Id; + } + + /// + /// 设置自动创建nz-form-item + /// + /// 是否自动创建 + public void AutoCreateFormItem( bool value ) { + if ( _shareConfig.AutoCreateFormItem == false ) + return; + _shareConfig.AutoCreateFormItem = value; + } + + /// + /// 设置自动创建nz-form-label + /// + /// 是否自动创建 + public void AutoCreateFormLabel( bool value ) { + if ( _shareConfig.AutoCreateFormLabel == false ) + return; + _shareConfig.AutoCreateFormLabel = value; + } + + /// + /// 设置自动创建nz-form-control + /// + /// 是否自动创建 + public void AutoCreateFormControl( bool value ) { + if ( _shareConfig.AutoCreateFormControl == false ) + return; + _shareConfig.AutoCreateFormControl = value; + } + + /// + /// 初始化 + /// + public void Init() { + InitFormItemShareConfig(); + MapToItemShareConfig(); + InitControlId(); + SetSpaceItem(); + SetShowLabel(); + SetLabelText(); + SetBindLabelText(); + SetLabelWidth(); + SetExtra(); + SetSuccessTip(); + SetWarningTip(); + SetErrorTip(); + SetValidatingTip(); + AutoCreateFormContainer(); + DisableLabelForTableEdit(); + SetNgIf(); + SetAutoNzFor(); + } + + /// + /// 初始化 + /// + private void InitFormItemShareConfig() { + _shareConfig = GetFormItemShareConfig(); + _config.SetValueToItems( _shareConfig ); + } + + /// + /// 获取表单项共享配置 + /// + private FormItemShareConfig GetFormItemShareConfig() { + return _config.GetValueFromItems() ?? new FormItemShareConfig(); + } + + /// + /// 初始化控件Id + /// + public void InitControlId() { + if ( _shareConfig.AutoNzFor != true ) + return; + if ( _shareConfig.ControlId.IsEmpty() == false ) + return; + _shareConfig.ControlId = GetControlId(); + } + + /// + /// 获取控件Id + /// + private string GetControlId() { + var nzId = _config.GetValue( UiConst.NzId ); + if ( nzId.IsEmpty() == false ) + return nzId; + var id = _config.GetValue( AngularConst.RawId ); + if ( id.IsEmpty() == false ) + return id; + id = _config.GetValue( UiConst.Id ); + if ( id.IsEmpty() == false ) + return $"control_{_shareConfig.FormId}_{id}"; + return $"control_{_shareConfig.FormId}_{Util.Helpers.Id.Create()}"; + } + + /// + /// 将表单共享配置映射到表单项共享配置 + /// + private void MapToItemShareConfig() { + var formShareConfig = GetFormShareConfig(); + formShareConfig.MapTo( _shareConfig ); + } + + /// + /// 获取表单共享配置 + /// + private FormShareConfig GetFormShareConfig() { + return _config.GetValueFromItems() ?? new FormShareConfig(); + } + + /// + /// 设置间距项 + /// + private void SetSpaceItem() { + var value = _config.GetValue( UiConst.SpaceItem ); + if ( value == null ) + return; + _shareConfig.SpaceItem = value.SafeValue(); + } + + /// + /// 设置是否显示标签 + /// + private void SetShowLabel() { + if ( _shareConfig.ShowLabel == false ) { + _shareConfig.AutoCreateFormLabel = false; + return; + } + var value = _config.GetValueFromAttributes( UiConst.ShowLabel ); + if ( value == false ) + _shareConfig.AutoCreateFormLabel = false; + } + + /// + /// 设置表单标签文本 + /// + private void SetLabelText() { + var value = _config.GetValueFromAttributes( UiConst.LabelText ); + if ( string.IsNullOrWhiteSpace( value ) ) + return; + _shareConfig.LabelText = value; + AutoCreateFormLabel( true ); + } + + /// + /// 设置表单标签文本 + /// + private void SetBindLabelText() { + var value = _config.GetValueFromAttributes( AngularConst.BindLabelText ); + if ( string.IsNullOrWhiteSpace( value ) ) + return; + _shareConfig.BindLabelText = value; + AutoCreateFormLabel( true ); + } + + /// + /// 设置表单标签宽度 + /// + private void SetLabelWidth() { + var value = _config.GetValueFromAttributes( UiConst.LabelWidth ); + if ( string.IsNullOrWhiteSpace( value ) ) + return; + _shareConfig.LabelWidth = value; + } + + /// + /// 设置额外提示 + /// + private void SetExtra() { + if ( _config.Contains( UiConst.Extra ) || _config.Contains( AngularConst.BindExtra ) ) + _shareConfig.HasExtra = true; + } + + /// + /// 设置成功提示 + /// + private void SetSuccessTip() { + if ( _config.Contains( UiConst.SuccessTip ) || _config.Contains( AngularConst.BindSuccessTip ) ) + _shareConfig.HasSuccessTip = true; + } + + /// + /// 设置警告提示 + /// + private void SetWarningTip() { + if ( _config.Contains( UiConst.WarningTip ) || _config.Contains( AngularConst.BindWarningTip ) ) + _shareConfig.HasWarningTip = true; + } + + /// + /// 设置错误提示 + /// + private void SetErrorTip() { + if ( _config.Contains( UiConst.ErrorTip ) || _config.Contains( AngularConst.BindErrorTip ) ) + _shareConfig.HasErrorTip = true; + if ( _config.Contains( AngularConst.BindErrorTip ) ) + _shareConfig.BindErrorTip = _config.GetValue( AngularConst.BindErrorTip ); + } + + /// + /// 设置校验提示 + /// + private void SetValidatingTip() { + if ( _config.Contains( UiConst.ValidatingTip ) || _config.Contains( AngularConst.BindValidatingTip ) ) + _shareConfig.HasValidatingTip = true; + } + + /// + /// 自动创建表单控件容器 + /// + private void AutoCreateFormContainer() { + if ( IsAutoCreateFormContainer() == false ) + return; + AutoCreateFormItem( true ); + AutoCreateFormControl( true ); + } + + /// + /// 是否自动创建表单控件容器 + /// + private bool IsAutoCreateFormContainer() { + if ( _shareConfig.LabelText.IsEmpty() == false ) + return true; + if ( _shareConfig.BindLabelText.IsEmpty() == false ) + return true; + if ( _shareConfig.Align != null ) + return true; + if ( _shareConfig.BindAlign.IsEmpty() == false ) + return true; + if ( _shareConfig.Gutter.IsEmpty() == false ) + return true; + if ( _shareConfig.Justify != null ) + return true; + if ( _shareConfig.BindJustify.IsEmpty() == false ) + return true; + if ( _shareConfig.HasExtra ) + return true; + if ( _shareConfig.HasSuccessTip ) + return true; + if ( _shareConfig.HasWarningTip ) + return true; + if ( _shareConfig.HasErrorTip ) + return true; + if ( _shareConfig.HasValidatingTip ) + return true; + if ( _shareConfig.ControlSpan.IsEmpty() == false ) + return true; + if ( _shareConfig.ControlOffset.IsEmpty() == false ) + return true; + if ( _shareConfig.ControlOrder.IsEmpty() == false ) + return true; + if ( _shareConfig.ControlPull.IsEmpty() == false ) + return true; + if ( _shareConfig.ControlPush.IsEmpty() == false ) + return true; + if ( _shareConfig.ControlFlex.IsEmpty() == false ) + return true; + if ( _shareConfig.ControlXs.IsEmpty() == false ) + return true; + if ( _shareConfig.ControlXsSpan != null ) + return true; + if ( _shareConfig.ControlXsOffset != null ) + return true; + if ( _shareConfig.ControlXsOrder != null ) + return true; + if ( _shareConfig.ControlXsPull != null ) + return true; + if ( _shareConfig.ControlXsPush != null ) + return true; + if ( _shareConfig.ControlSm.IsEmpty() == false ) + return true; + if ( _shareConfig.ControlSmSpan != null ) + return true; + if ( _shareConfig.ControlSmOffset != null ) + return true; + if ( _shareConfig.ControlSmOrder != null ) + return true; + if ( _shareConfig.ControlSmPull != null ) + return true; + if ( _shareConfig.ControlSmPush != null ) + return true; + if ( _shareConfig.ControlMd.IsEmpty() == false ) + return true; + if ( _shareConfig.ControlMdSpan != null ) + return true; + if ( _shareConfig.ControlMdOffset != null ) + return true; + if ( _shareConfig.ControlMdOrder != null ) + return true; + if ( _shareConfig.ControlMdPull != null ) + return true; + if ( _shareConfig.ControlMdPush != null ) + return true; + if ( _shareConfig.ControlLg.IsEmpty() == false ) + return true; + if ( _shareConfig.ControlLgSpan != null ) + return true; + if ( _shareConfig.ControlLgOffset != null ) + return true; + if ( _shareConfig.ControlLgOrder != null ) + return true; + if ( _shareConfig.ControlLgPull != null ) + return true; + if ( _shareConfig.ControlLgPush != null ) + return true; + if ( _shareConfig.ControlXl.IsEmpty() == false ) + return true; + if ( _shareConfig.ControlXlSpan != null ) + return true; + if ( _shareConfig.ControlXlOffset != null ) + return true; + if ( _shareConfig.ControlXlOrder != null ) + return true; + if ( _shareConfig.ControlXlPull != null ) + return true; + if ( _shareConfig.ControlXlPush != null ) + return true; + if ( _shareConfig.ControlXxl.IsEmpty() == false ) + return true; + if ( _shareConfig.ControlXxlSpan != null ) + return true; + if ( _shareConfig.ControlXxlOffset != null ) + return true; + if ( _shareConfig.ControlXxlOrder != null ) + return true; + if ( _shareConfig.ControlXxlPull != null ) + return true; + if ( _shareConfig.ControlXxlPush != null ) + return true; + return false; + } + + /// + /// 表格编辑时禁用标签 + /// + private void DisableLabelForTableEdit() { + var config = GetTableColumnShareConfig(); + if ( config == null ) + return; + _shareConfig.AutoCreateFormLabel = false; + } + + /// + /// 获取表格列共享配置 + /// + public TableColumnShareConfig GetTableColumnShareConfig() { + return _config.GetValueFromItems(); + } + + /// + /// 设置ngIf* + /// + private void SetNgIf() { + var value = _config.GetValueFromAttributes( AngularConst.NgIf ); + if ( value.IsEmpty() ) + return; + if ( _shareConfig.AutoCreateFormItem != true ) + return; + _shareConfig.NgIf = value; + _config.RemoveAttribute( AngularConst.NgIf ); + } + + /// + /// 设置自动设置表单标签 nzFor + /// + public void SetAutoNzFor() { + if ( _isDisableAutoNzFor ) + _shareConfig.AutoNzFor = false; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/Helpers/FormShareService.cs b/src/Util.Ui.NgZorro/Components/Forms/Helpers/FormShareService.cs new file mode 100644 index 000000000..335eef852 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/Helpers/FormShareService.cs @@ -0,0 +1,1108 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Forms.Helpers; + +/// +/// 表单共享服务 +/// +public class FormShareService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表单共享配置 + /// + private readonly FormShareConfig _shareConfig; + + /// + /// 设置 form 已创建 + /// + public void Created() { + _shareConfig.FormCreated = true; + } + + /// + /// 初始化表单共享服务 + /// + /// 配置 + public FormShareService( Config config ) { + _config = config; + _shareConfig = new FormShareConfig(); + } + + /// + /// 设置查询表单 + /// + public void SetSearchForm() { + _shareConfig.IsSearch = true; + _shareConfig.SearchFormShowNumber = _config.GetValueFromAttributes( UiConst.ShowNumber ); + _shareConfig.SearchFormColumnsNumber = _config.GetValueFromAttributes( UiConst.ColumnsNumber ); + } + + /// + /// 设置表单标识 + /// + public void SetFormId() { + var id = _config.GetValue( UiConst.Id ); + if ( id.IsEmpty() == false ) { + _shareConfig.FormId = id; + return; + } + var formGroup = _config.GetValue( UiConst.FormGroup ); + if ( formGroup.IsEmpty() ) + _shareConfig.FormId = "form"; + } + + /// + /// 初始化表单共享配置 + /// + public void Init() { + InitFormShareConfig(); + SetShowLabel(); + SetLabelWidth(); + SetAlign(); + SetBindAlign(); + SetGutter(); + SetJustify(); + SetBindJustify(); + SetAutoNzFor(); + SetHasFeedback(); + SetLabelSpan(); + SetLabelOffset(); + SetLabelOrder(); + SetLabelPull(); + SetLabelPush(); + SetLabelFlex(); + SetLabelXs(); + SetLabelXsSpan(); + SetLabelXsOffset(); + SetLabelXsOrder(); + SetLabelXsPull(); + SetLabelXsPush(); + SetLabelSm(); + SetLabelSmSpan(); + SetLabelSmOffset(); + SetLabelSmOrder(); + SetLabelSmPull(); + SetLabelSmPush(); + SetLabelMd(); + SetLabelMdSpan(); + SetLabelMdOffset(); + SetLabelMdOrder(); + SetLabelMdPull(); + SetLabelMdPush(); + SetLabelLg(); + SetLabelLgSpan(); + SetLabelLgOffset(); + SetLabelLgOrder(); + SetLabelLgPull(); + SetLabelLgPush(); + SetLabelXl(); + SetLabelXlSpan(); + SetLabelXlOffset(); + SetLabelXlOrder(); + SetLabelXlPull(); + SetLabelXlPush(); + SetLabelXxl(); + SetLabelXxlSpan(); + SetLabelXxlOffset(); + SetLabelXxlOrder(); + SetLabelXxlPull(); + SetLabelXxlPush(); + SetControlSpan(); + SetControlOffset(); + SetControlOrder(); + SetControlPull(); + SetControlPush(); + SetControlFlex(); + SetControlXs(); + SetControlXsSpan(); + SetControlXsOffset(); + SetControlXsOrder(); + SetControlXsPull(); + SetControlXsPush(); + SetControlSm(); + SetControlSmSpan(); + SetControlSmOffset(); + SetControlSmOrder(); + SetControlSmPull(); + SetControlSmPush(); + SetControlMd(); + SetControlMdSpan(); + SetControlMdOffset(); + SetControlMdOrder(); + SetControlMdPull(); + SetControlMdPush(); + SetControlLg(); + SetControlLgSpan(); + SetControlLgOffset(); + SetControlLgOrder(); + SetControlLgPull(); + SetControlLgPush(); + SetControlXl(); + SetControlXlSpan(); + SetControlXlOffset(); + SetControlXlOrder(); + SetControlXlPull(); + SetControlXlPush(); + SetControlXxl(); + SetControlXxlSpan(); + SetControlXxlOffset(); + SetControlXxlOrder(); + SetControlXxlPull(); + SetControlXxlPush(); + } + + /// + /// 初始化表单共享配置 + /// + private void InitFormShareConfig() { + var shareConfig = _config.GetValueFromItems(); + if ( shareConfig != null ) + shareConfig.MapTo( _shareConfig ); + _config.SetValueToItems( _shareConfig ); + } + + /// + /// 设置是否显示标签 + /// + private void SetShowLabel() { + var value = _config.GetValueFromAttributes( UiConst.ShowLabel ); + if ( value == false ) + _shareConfig.ShowLabel = false; + } + + /// + /// 设置表单标签宽度 + /// + private void SetLabelWidth() { + var value = _config.GetValueFromAttributes( UiConst.LabelWidth ); + if ( string.IsNullOrWhiteSpace( value ) ) + return; + _shareConfig.LabelWidth = value; + } + + /// + /// 设置垂直对齐方式 + /// + private void SetAlign() { + var value = _config.GetValue( UiConst.Align ); + if ( value == null ) + return; + _shareConfig.Align = value.SafeValue(); + } + + /// + /// 设置垂直对齐方式 + /// + private void SetBindAlign() { + var value = _config.GetValue( AngularConst.BindAlign ); + if ( value.IsEmpty() ) + return; + _shareConfig.BindAlign = value; + } + + /// + /// 设置栅格间隔 + /// + private void SetGutter() { + var value = _config.GetValue( UiConst.Gutter ); + if ( value.IsEmpty() ) + return; + _shareConfig.Gutter = value; + } + + /// + /// 设置水平排列方式 + /// + private void SetJustify() { + var value = _config.GetValue( UiConst.Justify ); + if ( value == null ) + return; + _shareConfig.Justify = value.SafeValue(); + } + + /// + /// 设置水平排列方式 + /// + private void SetBindJustify() { + var value = _config.GetValue( AngularConst.BindJustify ); + if ( value.IsEmpty() ) + return; + _shareConfig.BindJustify = value; + } + + /// + /// 设置自动设置表单标签 nzFor 属性 + /// + private void SetAutoNzFor() { + var value = IsEnableAutoNzFor(); + if ( value != null ) + _shareConfig.AutoNzFor = value; + } + + /// + /// 是否启用自动设置表单标签 nzFor + /// + private bool? IsEnableAutoNzFor() { + var value = _config.GetValue( UiConst.AutoNzFor ); + if (value != null) + return value; + if( _shareConfig.AutoNzFor == null ) + return NgZorroOptionsService.GetOptions().EnableAutoNzFor; + return null; + } + + /// + /// 设置展示校验状态图标 + /// + private void SetHasFeedback() { + var value = _config.GetValue( UiConst.HasFeedback ); + if ( value != null ) + _shareConfig.HasFeedback = value; + } + + /// + /// 设置表单标签跨度 + /// + private void SetLabelSpan() { + var value = _config.GetValue( UiConst.LabelSpan ); + if ( value.IsEmpty() ) + return; + _shareConfig.LabelSpan = value; + } + + /// + /// 设置表单标签偏移量 + /// + private void SetLabelOffset() { + var value = _config.GetValue( UiConst.LabelOffset ); + if ( value.IsEmpty() ) + return; + _shareConfig.LabelOffset = value; + } + + /// + /// 设置表单标签栅格顺序 + /// + private void SetLabelOrder() { + var value = _config.GetValue( UiConst.LabelOrder ); + if ( value.IsEmpty() ) + return; + _shareConfig.LabelOrder = value; + } + + /// + /// 设置表单标签栅格左移 + /// + private void SetLabelPull() { + var value = _config.GetValue( UiConst.LabelPull ); + if ( value.IsEmpty() ) + return; + _shareConfig.LabelPull = value; + } + + /// + /// 设置表单标签栅格右移 + /// + private void SetLabelPush() { + var value = _config.GetValue( UiConst.LabelPush ); + if ( value.IsEmpty() ) + return; + _shareConfig.LabelPush = value; + } + + /// + /// 设置表单标签Flex布局 + /// + private void SetLabelFlex() { + var value = _config.GetValue( UiConst.LabelFlex ); + if ( value.IsEmpty() ) + return; + _shareConfig.LabelFlex = value; + } + + /// + /// 设置表单标签超窄尺寸响应式栅格 + /// + private void SetLabelXs() { + var value = _config.GetValue( UiConst.LabelXs ); + if ( value.IsEmpty() ) + return; + _shareConfig.LabelXs = value; + } + + /// + /// 设置表单标签超窄尺寸响应式栅格跨度 + /// + private void SetLabelXsSpan() { + var value = _config.GetValue( UiConst.LabelXsSpan ); + if ( value == null ) + return; + _shareConfig.LabelXsSpan = value; + } + + /// + /// 设置表单标签超窄尺寸响应式栅格偏移量 + /// + private void SetLabelXsOffset() { + var value = _config.GetValue( UiConst.LabelXsOffset ); + if ( value == null ) + return; + _shareConfig.LabelXsOffset = value; + } + + /// + /// 设置表单标签超窄尺寸响应式栅格顺序 + /// + private void SetLabelXsOrder() { + var value = _config.GetValue( UiConst.LabelXsOrder ); + if ( value == null ) + return; + _shareConfig.LabelXsOrder = value; + } + + /// + /// 设置表单标签超窄尺寸响应式栅格左移 + /// + private void SetLabelXsPull() { + var value = _config.GetValue( UiConst.LabelXsPull ); + if ( value == null ) + return; + _shareConfig.LabelXsPull = value; + } + + /// + /// 设置表单标签超窄尺寸响应式栅格右移 + /// + private void SetLabelXsPush() { + var value = _config.GetValue( UiConst.LabelXsPush ); + if ( value == null ) + return; + _shareConfig.LabelXsPush = value; + } + + /// + /// 设置表单标签窄尺寸响应式栅格 + /// + private void SetLabelSm() { + var value = _config.GetValue( UiConst.LabelSm ); + if ( value.IsEmpty() ) + return; + _shareConfig.LabelSm = value; + } + + /// + /// 设置表单标签窄尺寸响应式栅格跨度 + /// + private void SetLabelSmSpan() { + var value = _config.GetValue( UiConst.LabelSmSpan ); + if ( value == null ) + return; + _shareConfig.LabelSmSpan = value; + } + + /// + /// 设置表单标签窄尺寸响应式栅格偏移量 + /// + private void SetLabelSmOffset() { + var value = _config.GetValue( UiConst.LabelSmOffset ); + if ( value == null ) + return; + _shareConfig.LabelSmOffset = value; + } + + /// + /// 设置表单标签窄尺寸响应式栅格顺序 + /// + private void SetLabelSmOrder() { + var value = _config.GetValue( UiConst.LabelSmOrder ); + if ( value == null ) + return; + _shareConfig.LabelSmOrder = value; + } + + /// + /// 设置表单标签窄尺寸响应式栅格左移 + /// + private void SetLabelSmPull() { + var value = _config.GetValue( UiConst.LabelSmPull ); + if ( value == null ) + return; + _shareConfig.LabelSmPull = value; + } + + /// + /// 设置表单标签窄尺寸响应式栅格右移 + /// + private void SetLabelSmPush() { + var value = _config.GetValue( UiConst.LabelSmPush ); + if ( value == null ) + return; + _shareConfig.LabelSmPush = value; + } + + /// + /// 设置表单标签中尺寸响应式栅格 + /// + private void SetLabelMd() { + var value = _config.GetValue( UiConst.LabelMd ); + if ( value.IsEmpty() ) + return; + _shareConfig.LabelMd = value; + } + + /// + /// 设置表单标签中尺寸响应式栅格跨度 + /// + private void SetLabelMdSpan() { + var value = _config.GetValue( UiConst.LabelMdSpan ); + if ( value == null ) + return; + _shareConfig.LabelMdSpan = value; + } + + /// + /// 设置表单标签中尺寸响应式栅格偏移量 + /// + private void SetLabelMdOffset() { + var value = _config.GetValue( UiConst.LabelMdOffset ); + if ( value == null ) + return; + _shareConfig.LabelMdOffset = value; + } + + /// + /// 设置表单标签中尺寸响应式栅格顺序 + /// + private void SetLabelMdOrder() { + var value = _config.GetValue( UiConst.LabelMdOrder ); + if ( value == null ) + return; + _shareConfig.LabelMdOrder = value; + } + + /// + /// 设置表单标签中尺寸响应式栅格左移 + /// + private void SetLabelMdPull() { + var value = _config.GetValue( UiConst.LabelMdPull ); + if ( value == null ) + return; + _shareConfig.LabelMdPull = value; + } + + /// + /// 设置表单标签中尺寸响应式栅格右移 + /// + private void SetLabelMdPush() { + var value = _config.GetValue( UiConst.LabelMdPush ); + if ( value == null ) + return; + _shareConfig.LabelMdPush = value; + } + + /// + /// 设置表单标签宽尺寸响应式栅格 + /// + private void SetLabelLg() { + var value = _config.GetValue( UiConst.LabelLg ); + if ( value.IsEmpty() ) + return; + _shareConfig.LabelLg = value; + } + + /// + /// 设置表单标签宽尺寸响应式栅格跨度 + /// + private void SetLabelLgSpan() { + var value = _config.GetValue( UiConst.LabelLgSpan ); + if ( value == null ) + return; + _shareConfig.LabelLgSpan = value; + } + + /// + /// 设置表单标签宽尺寸响应式栅格偏移量 + /// + private void SetLabelLgOffset() { + var value = _config.GetValue( UiConst.LabelLgOffset ); + if ( value == null ) + return; + _shareConfig.LabelLgOffset = value; + } + + /// + /// 设置表单标签宽尺寸响应式栅格顺序 + /// + private void SetLabelLgOrder() { + var value = _config.GetValue( UiConst.LabelLgOrder ); + if ( value == null ) + return; + _shareConfig.LabelLgOrder = value; + } + + /// + /// 设置表单标签宽尺寸响应式栅格左移 + /// + private void SetLabelLgPull() { + var value = _config.GetValue( UiConst.LabelLgPull ); + if ( value == null ) + return; + _shareConfig.LabelLgPull = value; + } + + /// + /// 设置表单标签宽尺寸响应式栅格右移 + /// + private void SetLabelLgPush() { + var value = _config.GetValue( UiConst.LabelLgPush ); + if ( value == null ) + return; + _shareConfig.LabelLgPush = value; + } + + /// + /// 设置表单标签超宽尺寸响应式栅格 + /// + private void SetLabelXl() { + var value = _config.GetValue( UiConst.LabelXl ); + if ( value.IsEmpty() ) + return; + _shareConfig.LabelXl = value; + } + + /// + /// 设置表单标签超宽尺寸响应式栅格跨度 + /// + private void SetLabelXlSpan() { + var value = _config.GetValue( UiConst.LabelXlSpan ); + if ( value == null ) + return; + _shareConfig.LabelXlSpan = value; + } + + /// + /// 设置表单标签超宽尺寸响应式栅格偏移量 + /// + private void SetLabelXlOffset() { + var value = _config.GetValue( UiConst.LabelXlOffset ); + if ( value == null ) + return; + _shareConfig.LabelXlOffset = value; + } + + /// + /// 设置表单标签超宽尺寸响应式栅格顺序 + /// + private void SetLabelXlOrder() { + var value = _config.GetValue( UiConst.LabelXlOrder ); + if ( value == null ) + return; + _shareConfig.LabelXlOrder = value; + } + + /// + /// 设置表单标签超宽尺寸响应式栅格左移 + /// + private void SetLabelXlPull() { + var value = _config.GetValue( UiConst.LabelXlPull ); + if ( value == null ) + return; + _shareConfig.LabelXlPull = value; + } + + /// + /// 设置表单标签超宽尺寸响应式栅格右移 + /// + private void SetLabelXlPush() { + var value = _config.GetValue( UiConst.LabelXlPush ); + if ( value == null ) + return; + _shareConfig.LabelXlPush = value; + } + + /// + /// 设置表单标签极宽尺寸响应式栅格 + /// + private void SetLabelXxl() { + var value = _config.GetValue( UiConst.LabelXxl ); + if ( value.IsEmpty() ) + return; + _shareConfig.LabelXxl = value; + } + + /// + /// 设置表单标签极宽尺寸响应式栅格跨度 + /// + private void SetLabelXxlSpan() { + var value = _config.GetValue( UiConst.LabelXxlSpan ); + if ( value == null ) + return; + _shareConfig.LabelXxlSpan = value; + } + + /// + /// 设置表单标签极宽尺寸响应式栅格偏移量 + /// + private void SetLabelXxlOffset() { + var value = _config.GetValue( UiConst.LabelXxlOffset ); + if ( value == null ) + return; + _shareConfig.LabelXxlOffset = value; + } + + /// + /// 设置表单标签极宽尺寸响应式栅格顺序 + /// + private void SetLabelXxlOrder() { + var value = _config.GetValue( UiConst.LabelXxlOrder ); + if ( value == null ) + return; + _shareConfig.LabelXxlOrder = value; + } + + /// + /// 设置表单标签极宽尺寸响应式栅格左移 + /// + private void SetLabelXxlPull() { + var value = _config.GetValue( UiConst.LabelXxlPull ); + if ( value == null ) + return; + _shareConfig.LabelXxlPull = value; + } + + /// + /// 设置表单标签极宽尺寸响应式栅格右移 + /// + private void SetLabelXxlPush() { + var value = _config.GetValue( UiConst.LabelXxlPush ); + if ( value == null ) + return; + _shareConfig.LabelXxlPush = value; + } + + /// + /// 设置表单控件跨度 + /// + private void SetControlSpan() { + var value = _config.GetValue( UiConst.ControlSpan ); + if ( value.IsEmpty() ) + return; + _shareConfig.ControlSpan = value; + } + + /// + /// 设置表单控件偏移量 + /// + private void SetControlOffset() { + var value = _config.GetValue( UiConst.ControlOffset ); + if ( value.IsEmpty() ) + return; + _shareConfig.ControlOffset = value; + } + + /// + /// 设置表单控件栅格顺序 + /// + private void SetControlOrder() { + var value = _config.GetValue( UiConst.ControlOrder ); + if ( value.IsEmpty() ) + return; + _shareConfig.ControlOrder = value; + } + + /// + /// 设置表单控件栅格左移 + /// + private void SetControlPull() { + var value = _config.GetValue( UiConst.ControlPull ); + if ( value.IsEmpty() ) + return; + _shareConfig.ControlPull = value; + } + + /// + /// 设置表单控件栅格右移 + /// + private void SetControlPush() { + var value = _config.GetValue( UiConst.ControlPush ); + if ( value.IsEmpty() ) + return; + _shareConfig.ControlPush = value; + } + + /// + /// 设置表单控件Flex布局 + /// + private void SetControlFlex() { + var value = _config.GetValue( UiConst.ControlFlex ); + if ( value.IsEmpty() ) + return; + _shareConfig.ControlFlex = value; + } + + /// + /// 设置表单控件超窄尺寸响应式栅格 + /// + private void SetControlXs() { + var value = _config.GetValue( UiConst.ControlXs ); + if ( value.IsEmpty() ) + return; + _shareConfig.ControlXs = value; + } + + /// + /// 设置表单控件超窄尺寸响应式栅格跨度 + /// + private void SetControlXsSpan() { + var value = _config.GetValue( UiConst.ControlXsSpan ); + if ( value == null ) + return; + _shareConfig.ControlXsSpan = value; + } + + /// + /// 设置表单控件超窄尺寸响应式栅格偏移量 + /// + private void SetControlXsOffset() { + var value = _config.GetValue( UiConst.ControlXsOffset ); + if ( value == null ) + return; + _shareConfig.ControlXsOffset = value; + } + + /// + /// 设置表单控件超窄尺寸响应式栅格顺序 + /// + private void SetControlXsOrder() { + var value = _config.GetValue( UiConst.ControlXsOrder ); + if ( value == null ) + return; + _shareConfig.ControlXsOrder = value; + } + + /// + /// 设置表单控件超窄尺寸响应式栅格左移 + /// + private void SetControlXsPull() { + var value = _config.GetValue( UiConst.ControlXsPull ); + if ( value == null ) + return; + _shareConfig.ControlXsPull = value; + } + + /// + /// 设置表单控件超窄尺寸响应式栅格右移 + /// + private void SetControlXsPush() { + var value = _config.GetValue( UiConst.ControlXsPush ); + if ( value == null ) + return; + _shareConfig.ControlXsPush = value; + } + + /// + /// 设置表单控件窄尺寸响应式栅格 + /// + private void SetControlSm() { + var value = _config.GetValue( UiConst.ControlSm ); + if ( value.IsEmpty() ) + return; + _shareConfig.ControlSm = value; + } + + /// + /// 设置表单控件窄尺寸响应式栅格跨度 + /// + private void SetControlSmSpan() { + var value = _config.GetValue( UiConst.ControlSmSpan ); + if ( value == null ) + return; + _shareConfig.ControlSmSpan = value; + } + + /// + /// 设置表单控件窄尺寸响应式栅格偏移量 + /// + private void SetControlSmOffset() { + var value = _config.GetValue( UiConst.ControlSmOffset ); + if ( value == null ) + return; + _shareConfig.ControlSmOffset = value; + } + + /// + /// 设置表单控件窄尺寸响应式栅格顺序 + /// + private void SetControlSmOrder() { + var value = _config.GetValue( UiConst.ControlSmOrder ); + if ( value == null ) + return; + _shareConfig.ControlSmOrder = value; + } + + /// + /// 设置表单控件窄尺寸响应式栅格左移 + /// + private void SetControlSmPull() { + var value = _config.GetValue( UiConst.ControlSmPull ); + if ( value == null ) + return; + _shareConfig.ControlSmPull = value; + } + + /// + /// 设置表单控件窄尺寸响应式栅格右移 + /// + private void SetControlSmPush() { + var value = _config.GetValue( UiConst.ControlSmPush ); + if ( value == null ) + return; + _shareConfig.ControlSmPush = value; + } + + /// + /// 设置表单控件中尺寸响应式栅格 + /// + private void SetControlMd() { + var value = _config.GetValue( UiConst.ControlMd ); + if ( value.IsEmpty() ) + return; + _shareConfig.ControlMd = value; + } + + /// + /// 设置表单控件中尺寸响应式栅格跨度 + /// + private void SetControlMdSpan() { + var value = _config.GetValue( UiConst.ControlMdSpan ); + if ( value == null ) + return; + _shareConfig.ControlMdSpan = value; + } + + /// + /// 设置表单控件中尺寸响应式栅格偏移量 + /// + private void SetControlMdOffset() { + var value = _config.GetValue( UiConst.ControlMdOffset ); + if ( value == null ) + return; + _shareConfig.ControlMdOffset = value; + } + + /// + /// 设置表单控件中尺寸响应式栅格顺序 + /// + private void SetControlMdOrder() { + var value = _config.GetValue( UiConst.ControlMdOrder ); + if ( value == null ) + return; + _shareConfig.ControlMdOrder = value; + } + + /// + /// 设置表单控件中尺寸响应式栅格左移 + /// + private void SetControlMdPull() { + var value = _config.GetValue( UiConst.ControlMdPull ); + if ( value == null ) + return; + _shareConfig.ControlMdPull = value; + } + + /// + /// 设置表单控件中尺寸响应式栅格右移 + /// + private void SetControlMdPush() { + var value = _config.GetValue( UiConst.ControlMdPush ); + if ( value == null ) + return; + _shareConfig.ControlMdPush = value; + } + + /// + /// 设置表单控件宽尺寸响应式栅格 + /// + private void SetControlLg() { + var value = _config.GetValue( UiConst.ControlLg ); + if ( value.IsEmpty() ) + return; + _shareConfig.ControlLg = value; + } + + /// + /// 设置表单控件宽尺寸响应式栅格跨度 + /// + private void SetControlLgSpan() { + var value = _config.GetValue( UiConst.ControlLgSpan ); + if ( value == null ) + return; + _shareConfig.ControlLgSpan = value; + } + + /// + /// 设置表单控件宽尺寸响应式栅格偏移量 + /// + private void SetControlLgOffset() { + var value = _config.GetValue( UiConst.ControlLgOffset ); + if ( value == null ) + return; + _shareConfig.ControlLgOffset = value; + } + + /// + /// 设置表单控件宽尺寸响应式栅格顺序 + /// + private void SetControlLgOrder() { + var value = _config.GetValue( UiConst.ControlLgOrder ); + if ( value == null ) + return; + _shareConfig.ControlLgOrder = value; + } + + /// + /// 设置表单控件宽尺寸响应式栅格左移 + /// + private void SetControlLgPull() { + var value = _config.GetValue( UiConst.ControlLgPull ); + if ( value == null ) + return; + _shareConfig.ControlLgPull = value; + } + + /// + /// 设置表单控件宽尺寸响应式栅格右移 + /// + private void SetControlLgPush() { + var value = _config.GetValue( UiConst.ControlLgPush ); + if ( value == null ) + return; + _shareConfig.ControlLgPush = value; + } + + /// + /// 设置表单控件超宽尺寸响应式栅格 + /// + private void SetControlXl() { + var value = _config.GetValue( UiConst.ControlXl ); + if ( value.IsEmpty() ) + return; + _shareConfig.ControlXl = value; + } + + /// + /// 设置表单控件超宽尺寸响应式栅格跨度 + /// + private void SetControlXlSpan() { + var value = _config.GetValue( UiConst.ControlXlSpan ); + if ( value == null ) + return; + _shareConfig.ControlXlSpan = value; + } + + /// + /// 设置表单控件超宽尺寸响应式栅格偏移量 + /// + private void SetControlXlOffset() { + var value = _config.GetValue( UiConst.ControlXlOffset ); + if ( value == null ) + return; + _shareConfig.ControlXlOffset = value; + } + + /// + /// 设置表单控件超宽尺寸响应式栅格顺序 + /// + private void SetControlXlOrder() { + var value = _config.GetValue( UiConst.ControlXlOrder ); + if ( value == null ) + return; + _shareConfig.ControlXlOrder = value; + } + + /// + /// 设置表单控件超宽尺寸响应式栅格左移 + /// + private void SetControlXlPull() { + var value = _config.GetValue( UiConst.ControlXlPull ); + if ( value == null ) + return; + _shareConfig.ControlXlPull = value; + } + + /// + /// 设置表单控件超宽尺寸响应式栅格右移 + /// + private void SetControlXlPush() { + var value = _config.GetValue( UiConst.ControlXlPush ); + if ( value == null ) + return; + _shareConfig.ControlXlPush = value; + } + + /// + /// 设置表单控件极宽尺寸响应式栅格 + /// + private void SetControlXxl() { + var value = _config.GetValue( UiConst.ControlXxl ); + if ( value.IsEmpty() ) + return; + _shareConfig.ControlXxl = value; + } + + /// + /// 设置表单控件极宽尺寸响应式栅格跨度 + /// + private void SetControlXxlSpan() { + var value = _config.GetValue( UiConst.ControlXxlSpan ); + if ( value == null ) + return; + _shareConfig.ControlXxlSpan = value; + } + + /// + /// 设置表单控件极宽尺寸响应式栅格偏移量 + /// + private void SetControlXxlOffset() { + var value = _config.GetValue( UiConst.ControlXxlOffset ); + if ( value == null ) + return; + _shareConfig.ControlXxlOffset = value; + } + + /// + /// 设置表单控件极宽尺寸响应式栅格顺序 + /// + private void SetControlXxlOrder() { + var value = _config.GetValue( UiConst.ControlXxlOrder ); + if ( value == null ) + return; + _shareConfig.ControlXxlOrder = value; + } + + /// + /// 设置表单控件极宽尺寸响应式栅格左移 + /// + private void SetControlXxlPull() { + var value = _config.GetValue( UiConst.ControlXxlPull ); + if ( value == null ) + return; + _shareConfig.ControlXxlPull = value; + } + + /// + /// 设置表单控件极宽尺寸响应式栅格右移 + /// + private void SetControlXxlPush() { + var value = _config.GetValue( UiConst.ControlXxlPush ); + if ( value == null ) + return; + _shareConfig.ControlXxlPush = value; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/Helpers/ValidationService.cs b/src/Util.Ui.NgZorro/Components/Forms/Helpers/ValidationService.cs new file mode 100644 index 000000000..5e438f908 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/Helpers/ValidationService.cs @@ -0,0 +1,176 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Forms.Helpers; + +/// +/// 验证服务 +/// +public class ValidationService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表单项共享配置 + /// + private FormItemShareConfig _shareConfig; + + /// + /// 初始化输入框服务 + /// + /// 配置 + public ValidationService( Config config ) { + _config = config; + } + + /// + /// 初始化 + /// + public void Init() { + InitFormItemShareConfig(); + if ( IsExtend() == false ) + return; + EnableExtend(); + InitValidationExtendId(); + InitValidationTempalteId(); + InitErrorTip(); + } + + /// + /// 初始化 + /// + private void InitFormItemShareConfig() { + _shareConfig = GetShareConfig(); + _config.SetValueToItems( _shareConfig ); + } + + /// + /// 获取表单项共享配置 + /// + private FormItemShareConfig GetShareConfig() { + return _config.GetValueFromItems() ?? new FormItemShareConfig(); + } + + /// + /// 是否扩展 + /// + private bool IsExtend() { + if ( _config.Contains( UiConst.ErrorTip ) || _config.Contains( AngularConst.BindErrorTip ) ) + return false; + if ( HasNgModel() == false ) + return false; + if ( HasRequired() ) + return true; + if ( HasMinLength() ) + return true; + if ( HasMaxLength() ) + return true; + if ( HasPattern() ) + return true; + if ( HasBindPattern() ) + return true; + if ( HasEmailType() ) + return true; + return false; + } + + /// + /// 是否设置ngModel属性 + /// + private bool HasNgModel() { + return _config.Contains( AngularConst.NgModel ); + } + + /// + /// 是否设置必填项属性 + /// + private bool HasRequired() { + return _config.Contains( UiConst.Required ); + } + + /// + /// 是否设置最小长度属性 + /// + private bool HasMinLength() { + return _config.Contains( UiConst.MinLength ); + } + + /// + /// 是否设置正则表达式属性 + /// + private bool HasPattern() { + return _config.Contains( UiConst.Pattern ); + } + + /// + /// 是否设置正则表达式属性 + /// + private bool HasBindPattern() { + return _config.Contains( AngularConst.BindPattern ); + } + + /// + /// 是否设置最大长度属性 + /// + private bool HasMaxLength() { + return _config.Contains( UiConst.MaxLength ); + } + + /// + /// 是否设置电子邮件类型属性 + /// + private bool HasEmailType() { + var type = _config.GetValue( UiConst.Type ); + return type == InputType.Email; + } + + /// + /// 启用验证扩展 + /// + private void EnableExtend() { + _shareConfig.IsValidationExtend = true; + } + + /// + /// 初始化验证扩展标识 + /// + private void InitValidationExtendId() { + _shareConfig.ValidationExtendId = GetValidationExtendId(); + } + + /// + /// 获取验证扩展标识 + /// + private string GetValidationExtendId() { + var id = _config.GetValue( UiConst.Id ); + if ( id.IsEmpty() ) + id = Util.Helpers.Id.Create(); + return $"v_{id}"; + } + + /// + /// 初始化验证模板标识 + /// + private void InitValidationTempalteId() { + _shareConfig.ValidationTempalteId = GetValidationTempalteId(); + } + + /// + /// 获取验证模板标识 + /// + private string GetValidationTempalteId() { + var id = _config.GetValue( UiConst.Id ); + if ( id.IsEmpty() ) + id = Util.Helpers.Id.Create(); + return $"vt_{id}"; + } + + /// + /// 初始化错误提示 + /// + private void InitErrorTip() { + _config.SetAttribute( AngularConst.BindErrorTip, _shareConfig.ValidationTempalteId ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/Renders/FormControlRender.cs b/src/Util.Ui.NgZorro/Components/Forms/Renders/FormControlRender.cs new file mode 100644 index 000000000..849badba9 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/Renders/FormControlRender.cs @@ -0,0 +1,39 @@ +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Forms.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Forms.Renders; + +/// +/// 表单域渲染器 +/// +public class FormControlRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化表单域渲染器 + /// + /// 配置 + public FormControlRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new FormControlBuilder( _config ); + builder.Config(); + _config.Content.AppendTo( builder ); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new FormControlRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/Renders/FormItemRender.cs b/src/Util.Ui.NgZorro/Components/Forms/Renders/FormItemRender.cs new file mode 100644 index 000000000..cf5de4209 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/Renders/FormItemRender.cs @@ -0,0 +1,72 @@ +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Forms.Builders; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Forms.Renders; + +/// +/// 表单项渲染器 +/// +public class FormItemRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表单项共享配置 + /// + private readonly FormItemShareConfig _shareConfig; + + /// + /// 初始化表单项渲染器 + /// + /// 配置 + /// 共享配置 + public FormItemRender( Config config, FormItemShareConfig shareConfig = null ) { + _config = config; + _shareConfig = shareConfig ?? GetFormItemShareConfig(); + } + + /// + /// 获取表单项共享配置 + /// + private FormItemShareConfig GetFormItemShareConfig() { + return _config.GetValueFromItems() ?? new FormItemShareConfig(); + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new FormItemBuilder( _config ); + builder.Config(); + var formLabel = GetFormLabel(); + builder.AppendContent( formLabel ); + _config.Content.AppendTo( builder ); + return builder; + } + + /// + /// 获取表单标签 + /// + private TagBuilder GetFormLabel() { + if ( _shareConfig.AutoCreateFormLabel == true ) { + _shareConfig.AutoCreateFormLabel = false; + var builder = new FormLabelBuilder( _config ); + builder.Config(); + if ( _shareConfig.LabelText.IsEmpty() == false ) + builder.SetContent( _shareConfig.LabelText ); + if ( _shareConfig.BindLabelText.IsEmpty() == false ) + builder.SetContent( "{{" + _shareConfig.LabelText + "}}" ); + return builder; + } + return new EmptyContainerTagBuilder(); + } + + /// + public override IHtmlContent Clone() { + return new FormItemRender( _config.Copy(), _shareConfig.MapTo() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/Renders/FormLabelRender.cs b/src/Util.Ui.NgZorro/Components/Forms/Renders/FormLabelRender.cs new file mode 100644 index 000000000..2ae1114c7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/Renders/FormLabelRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Forms.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Forms.Renders; + +/// +/// 表单标签渲染器 +/// +public class FormLabelRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化表单标签渲染器 + /// + /// 配置 + public FormLabelRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new FormLabelBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new FormLabelRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/Renders/FormRender.cs b/src/Util.Ui.NgZorro/Components/Forms/Renders/FormRender.cs new file mode 100644 index 000000000..d27d36a58 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/Renders/FormRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Forms.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Forms.Renders; + +/// +/// 表单渲染器 +/// +public class FormRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化表单渲染器 + /// + /// 配置 + public FormRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new FormBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new FormRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/Renders/FormSplitRender.cs b/src/Util.Ui.NgZorro/Components/Forms/Renders/FormSplitRender.cs new file mode 100644 index 000000000..3b7639649 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/Renders/FormSplitRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Forms.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Forms.Renders; + +/// +/// 表单分隔符渲染器 +/// +public class FormSplitRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化表单分隔符渲染器 + /// + /// 配置 + public FormSplitRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new FormSplitBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new FormSplitRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/Renders/FormTextRender.cs b/src/Util.Ui.NgZorro/Components/Forms/Renders/FormTextRender.cs new file mode 100644 index 000000000..4f64f29d2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/Renders/FormTextRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Forms.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Forms.Renders; + +/// +/// 表单文本渲染器 +/// +public class FormTextRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化表单文本渲染器 + /// + /// 配置 + public FormTextRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new FormTextBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new FormTextRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Forms/SearchFormTagHelper.cs b/src/Util.Ui.NgZorro/Components/Forms/SearchFormTagHelper.cs new file mode 100644 index 000000000..56679fc30 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Forms/SearchFormTagHelper.cs @@ -0,0 +1,40 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Forms.Helpers; +using Util.Ui.NgZorro.Components.Forms.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Forms; + +/// +/// 查询表单,生成的标签为<form nz-form></form> +/// +[HtmlTargetElement( "util-search-form" )] +public class SearchFormTagHelper : FormTagHelper { + /// + /// 配置 + /// + private Config _config; + /// + /// 扩展属性, 初始显示的查询条件数量,默认值: 3, 超过该数量的查询条件将被隐藏,注意: 该数量不包含操作 + /// + public int ShowNumber { get; set; } + /// + /// 扩展属性,每行显示几列,仅对 xxl 宽度有效,默认值: 4 , 可选值: 4,6 + /// + public int ColumnsNumber { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new FormShareService( _config ); + service.SetSearchForm(); + service.Init(); + service.Created(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new FormRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Grids/Builders/ColumnBuilder.cs b/src/Util.Ui.NgZorro/Components/Grids/Builders/ColumnBuilder.cs new file mode 100644 index 000000000..d6bc28fe2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Grids/Builders/ColumnBuilder.cs @@ -0,0 +1,167 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Forms.Configs; + +namespace Util.Ui.NgZorro.Components.Grids.Builders; + +/// +/// 栅格列生成器 +/// +public class ColumnBuilder : ColumnBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表单共享配置 + /// + private FormShareConfig _formShareConfig; + /// + /// 标识 + /// + private readonly string _id; + + /// + /// 初始化栅格列生成器 + /// + public ColumnBuilder( Config config, string id ) : base( config, "div" ) { + _config = config; + _formShareConfig = GetFormShareConfig(); + _id = id; + base.Attribute( "nz-col" ); + } + + /// + /// 获取表单共享配置 + /// + public FormShareConfig GetFormShareConfig() { + return _formShareConfig ??= _config.GetValueFromItems() ?? new FormShareConfig(); + } + + /// + /// 配置 + /// + public override void Config() { + ConfigSearchForm(); + base.Config(); + ConfigColumn(); + } + + /// + /// 配置搜索表单 + /// + protected virtual void ConfigSearchForm() { + if ( _formShareConfig == null ) + return; + if ( _formShareConfig.IsSearch.SafeValue() == false ) + return; + if ( _formShareConfig.IsActionColumn( _id ) ) { + ConfigSearchFormActionColumn(); + return; + } + ConfigSearchFormConditionColumn(); + ConfigHideSearchFormItem(); + } + + /// + /// 配置搜索表单操作区域栅格属性 + /// + protected virtual void ConfigSearchFormActionColumn() { + _config.SetAttribute( UiConst.Xs, 24 ); + _config.SetAttribute( UiConst.Sm, 24 ); + _config.SetAttribute( UiConst.Md, GetSearchFormActionMd() ); + _config.SetAttribute( UiConst.Lg, GetSearchFormActionLg() ); + _config.SetAttribute( UiConst.Xl, GetSearchFormActionXl() ); + _config.SetAttribute( UiConst.Xxl, GetSearchFormActionXxl() ); + } + + /// + /// 获取搜索表单操作区域中尺寸栅格值 + /// + protected virtual string GetSearchFormActionMd() { + var result = GetMd(); + if ( result.IsEmpty() == false ) + return result; + return "{" + $"span:expand?{GetSearchFormActionSpan( 2 )}:{GetSearchFormActionSpan( 2, false )}" + "}"; + } + + /// + /// 获取搜索表单操作区域宽尺寸栅格值 + /// + protected virtual string GetSearchFormActionLg() { + var result = GetLg(); + if ( result.IsEmpty() == false ) + return result; + return "{" + $"span:expand?{GetSearchFormActionSpan( 3 )}:{GetSearchFormActionSpan( 3, false )}" + "}"; + } + + /// + /// 获取搜索表单操作区域超宽尺寸栅格值 + /// + protected virtual string GetSearchFormActionXl() { + var result = GetXl(); + if ( result.IsEmpty() == false ) + return result; + return "{" + $"span:expand?{GetSearchFormActionSpan( 3 )}:{GetSearchFormActionSpan( 3, false )}" + "}"; + } + + /// + /// 获取搜索表单操作区域极宽尺寸栅格值 + /// + protected virtual string GetSearchFormActionXxl() { + var result = GetXxl(); + if ( result.IsEmpty() == false ) + return result; + return "{" + $"span:expand?{GetSearchFormActionSpan( GetXxlColumnNumber() )}:{GetSearchFormActionSpan( GetXxlColumnNumber(), false )}" + "}"; + } + + /// + /// 获取搜索表单操作区域跨度 + /// + /// 一行几列 + /// 是否展开 + protected virtual int GetSearchFormActionSpan( int columnNumber, bool expand = true ) { + var count = GetConditionCount( expand ); + var remainder = count % columnNumber; + return 24 - remainder * 24 / columnNumber; + } + + /// + /// 获取有效的查询条件数量 + /// + private int GetConditionCount( bool expand ) { + if ( expand ) + return _formShareConfig.GetConditionCount(); + if ( _formShareConfig.GetConditionCount() > _formShareConfig.SearchFormShowNumber ) + return _formShareConfig.SearchFormShowNumber.SafeValue(); + return _formShareConfig.GetConditionCount(); + } + + /// + /// 获取Xxl宽度每行列数 + /// + private int GetXxlColumnNumber() { + return _formShareConfig.SearchFormColumnsNumber.SafeValue() >= 6 ? 6 : 4; + } + + /// + /// 配置搜索表单查询条件栅格属性 + /// + protected virtual void ConfigSearchFormConditionColumn() { + _config.SetAttribute( UiConst.Xs, 24 ); + _config.SetAttribute( UiConst.Sm, 24 ); + _config.SetAttribute( UiConst.Md, 12 ); + _config.SetAttribute( UiConst.Lg, 8 ); + _config.SetAttribute( UiConst.Xl, 8 ); + _config.SetAttribute( UiConst.Xxl, 24 / GetXxlColumnNumber() ); + } + + /// + /// 隐藏查询条件 + /// + protected virtual void ConfigHideSearchFormItem() { + if ( _formShareConfig.IsHide( _id ) == false ) + return; + this.NgIf( "expand" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Grids/Builders/RowBuilder.cs b/src/Util.Ui.NgZorro/Components/Grids/Builders/RowBuilder.cs new file mode 100644 index 000000000..d7ca727ec --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Grids/Builders/RowBuilder.cs @@ -0,0 +1,30 @@ +using Util.Ui.NgZorro.Components.Base; + +namespace Util.Ui.NgZorro.Components.Grids.Builders; + +/// +/// 栅格行标签生成器 +/// +public class RowBuilder : RowBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化栅格行标签生成器 + /// + /// 配置 + public RowBuilder( Config config ) : base( config,"div" ) { + base.Attribute( "nz-row" ); + _config = config; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + ConfigRow(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Grids/ColumnTagHelper.cs b/src/Util.Ui.NgZorro/Components/Grids/ColumnTagHelper.cs new file mode 100644 index 000000000..e55848f48 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Grids/ColumnTagHelper.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Grids.Helpers; +using Util.Ui.NgZorro.Components.Grids.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Grids; + +/// +/// 栅格列,生成的标签为<div nz-col></div> +/// +[HtmlTargetElement( "util-col" )] +[HtmlTargetElement( "util-column" )] +public class ColumnTagHelper : ColumnTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 标识 + /// + private readonly string _id; + + /// + /// 初始化栅格列 + /// + public ColumnTagHelper() { + _id = Guid.NewGuid().ToString(); + } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new ColumnService( _config ); + service.Init( _id ); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ColumnRender( config, _id ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Grids/Helpers/ColumnModel.cs b/src/Util.Ui.NgZorro/Components/Grids/Helpers/ColumnModel.cs new file mode 100644 index 000000000..ea67544c4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Grids/Helpers/ColumnModel.cs @@ -0,0 +1,49 @@ +using Util.Helpers; + +namespace Util.Ui.NgZorro.Components.Grids.Helpers; + +/// +/// 栅格列模型 +/// +public class ColumnModel { + /// + /// xs + /// + public int? Xs { get; set; } + + /// + /// sm + /// + public int? Sm { get; set; } + + /// + /// md + /// + public int? Md { get; set; } + + /// + /// lg + /// + public int? Lg { get; set; } + + /// + /// xl + /// + public int? Xl { get; set; } + + /// + /// xxl + /// + public int? Xxl { get; set; } + + /// + /// 输出json + /// + public string ToJson() { + var result = Json.ToJson( this,new JsonSerializerOptions { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + },true ); + return result == "{}" ? null : result; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Grids/Helpers/ColumnService.cs b/src/Util.Ui.NgZorro/Components/Grids/Helpers/ColumnService.cs new file mode 100644 index 000000000..62179e74a --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Grids/Helpers/ColumnService.cs @@ -0,0 +1,33 @@ +using Util.Ui.NgZorro.Components.Forms.Configs; +namespace Util.Ui.NgZorro.Components.Grids.Helpers; + +/// +/// 栅格列服务 +/// +public class ColumnService { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化输入框组合共享服务 + /// + /// 配置 + public ColumnService( Config config ) { + _config = config; + } + + /// + /// 初始化 + /// + /// 标识 + public void Init( string id ) { + var formShareConfig = _config.GetValueFromItems(); + if ( formShareConfig == null ) + return; + if ( formShareConfig.IsSearch.SafeValue() == false ) + return; + formShareConfig.AddColumnId( id ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Grids/Helpers/GridModel.cs b/src/Util.Ui.NgZorro/Components/Grids/Helpers/GridModel.cs new file mode 100644 index 000000000..ebedbba4b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Grids/Helpers/GridModel.cs @@ -0,0 +1,44 @@ +using Util.Helpers; + +namespace Util.Ui.NgZorro.Components.Grids.Helpers; + +/// +/// 栅格参数实体 +/// +public class GridModel { + /// + /// 占位格数 + /// + public int? Span { get; set; } + + /// + /// 向右偏移的格数 + /// + public int? Offset { get; set; } + + /// + /// 顺序 + /// + public int? Order { get; set; } + + /// + /// 向左移动的格数 + /// + public int? Pull { get; set; } + + /// + /// 向右移动的格数 + /// + public int? Push { get; set; } + + /// + /// 输出json + /// + public string ToJson() { + var result = Json.ToJson( this,new JsonSerializerOptions { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + },true ); + return result == "{}" ? null : result; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Grids/Renders/ColumnRender.cs b/src/Util.Ui.NgZorro/Components/Grids/Renders/ColumnRender.cs new file mode 100644 index 000000000..88afd47d5 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Grids/Renders/ColumnRender.cs @@ -0,0 +1,43 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Grids.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Grids.Renders; + +/// +/// 栅格列渲染器 +/// +public class ColumnRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 标识 + /// + private string _id; + + /// + /// 初始化栅格列渲染器 + /// + public ColumnRender( Config config, string id ) { + _config = config; + _id = id; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ColumnBuilder( _config, _id ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ColumnRender( _config.CopyRemoveAttributes(), _id ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Grids/Renders/RowRender.cs b/src/Util.Ui.NgZorro/Components/Grids/Renders/RowRender.cs new file mode 100644 index 000000000..9a8e47e48 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Grids/Renders/RowRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Grids.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Grids.Renders; + +/// +/// 栅格行渲染器 +/// +public class RowRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化栅格行渲染器 + /// + /// 配置 + public RowRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new RowBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new RowRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Grids/RowTagHelper.cs b/src/Util.Ui.NgZorro/Components/Grids/RowTagHelper.cs new file mode 100644 index 000000000..f9d23b165 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Grids/RowTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Grids.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Grids; + +/// +/// 栅格行,生成的标签为<div nz-row></div> +/// +[HtmlTargetElement( "util-row")] +public class RowTagHelper : RowTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new RowRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/HashCodes/Builders/HashCodeBuilder.cs b/src/Util.Ui.NgZorro/Components/HashCodes/Builders/HashCodeBuilder.cs new file mode 100644 index 000000000..9451d1dee --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/HashCodes/Builders/HashCodeBuilder.cs @@ -0,0 +1,84 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.HashCodes.Builders; + +/// +/// 哈希码标签生成器 +/// +public class HashCodeBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化哈希码标签生成器 + /// + /// 配置 + public HashCodeBuilder( Config config ) : base( config, "nz-hash-code" ) { + _config = config; + } + + /// + /// 配置值 + /// + public HashCodeBuilder Value() { + AttributeIfNotEmpty( "nzValue", _config.GetValue( UiConst.Value ) ); + AttributeIfNotEmpty( "[nzValue]", _config.GetValue( AngularConst.BindValue ) ); + return this; + } + + /// + /// 配置标题 + /// + public HashCodeBuilder Title() { + AttributeIfNotEmpty( "nzTitle", _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 配置标志 + /// + public HashCodeBuilder Logo() { + AttributeIfNotEmpty( "nzLogo", _config.GetValue( UiConst.Logo ) ); + AttributeIfNotEmpty( "[nzLogo]", _config.GetValue( AngularConst.BindLogo ) ); + return this; + } + + /// + /// 配置展示模式 + /// + public HashCodeBuilder Mode() { + AttributeIfNotEmpty( "nzMode", _config.GetValue( UiConst.Mode )?.Description() ); + AttributeIfNotEmpty( "[nzMode]", _config.GetValue( AngularConst.BindMode ) ); + return this; + } + + /// + /// 配置样式 + /// + public HashCodeBuilder Type() { + AttributeIfNotEmpty( "nzType", _config.GetValue( UiConst.Type )?.Description() ); + AttributeIfNotEmpty( "[nzType]", _config.GetValue( AngularConst.BindType ) ); + return this; + } + + /// + /// 配置事件 + /// + public HashCodeBuilder Events() { + AttributeIfNotEmpty( "(nzOnCopy)", _config.GetValue( UiConst.OnCopy ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Value().Title().Logo().Mode().Type().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/HashCodes/HashCodeTagHelper.cs b/src/Util.Ui.NgZorro/Components/HashCodes/HashCodeTagHelper.cs new file mode 100644 index 000000000..0340e5098 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/HashCodes/HashCodeTagHelper.cs @@ -0,0 +1,64 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.HashCodes.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.HashCodes; + +/// +/// 哈希码,生成的标签为<nz-hash-code></nz-hash-code> +/// +[HtmlTargetElement( "util-hash-code" )] +public class HashCodeTagHelper : AngularTagHelperBase { + /// + /// nzValue,值 + /// + public string Value { get; set; } + /// + /// [nzValue],值 + /// + public string BindValue { get; set; } + /// + /// nzTitle,标题, 默认值: HashCode + /// + public string Title { get; set; } + /// + /// [nzTitle],标题, 默认值: HashCode + /// + public string BindTitle { get; set; } + /// + /// nzLogo,设置右上角的标志, 类型: TemplateRef<void> | string + /// + public string Logo { get; set; } + /// + /// [nzLogo],设置右上角的标志, 类型: TemplateRef<void> | string + /// + public string BindLogo { get; set; } + /// + /// nzMode,展示模式, 可选值: single | double | strip | rect , 默认值: double + /// + public HashCodeMode Mode { get; set; } + /// + /// [nzMode],展示模式, 可选值: single | double | strip | rect , 默认值: double + /// + public string BindMode { get; set; } + /// + /// nzType,样式, 可选值: defalut | primary , 默认值: primary + /// + public HashCodeType Type { get; set; } + /// + /// [nzType],样式, 可选值: defalut | primary , 默认值: primary + /// + public string BindType { get; set; } + /// + /// (nzOnCopy), 复制事件,类型: EventEmitter<string> + /// + public string OnCopy { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new HashCodeRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/HashCodes/Renders/HashCodeRender.cs b/src/Util.Ui.NgZorro/Components/HashCodes/Renders/HashCodeRender.cs new file mode 100644 index 000000000..e25adafac --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/HashCodes/Renders/HashCodeRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.HashCodes.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.HashCodes.Renders; + +/// +/// 哈希码渲染器 +/// +public class HashCodeRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化哈希码渲染器 + /// + /// 配置 + public HashCodeRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new HashCodeBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new HashCodeRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Icons/Builders/IconBuilder.cs b/src/Util.Ui.NgZorro/Components/Icons/Builders/IconBuilder.cs new file mode 100644 index 000000000..faa49f08d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Icons/Builders/IconBuilder.cs @@ -0,0 +1,174 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.TreeViews.Configs; +using Util.Ui.NgZorro.Directives.Popconfirms; +using Util.Ui.NgZorro.Directives.Popover; +using Util.Ui.NgZorro.Directives.Tooltips; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Icons.Builders; + +/// +/// 图标标签生成器 +/// +public class IconBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化图标标签生成器 + /// + /// 配置 + public IconBuilder( Config config ) : base( config, "span" ) { + base.Attribute( "nz-icon" ); + _config = config; + } + + /// + /// 配置图标 + /// + public IconBuilder Icon() { + AttributeIfNotEmpty( "nzType", _config.GetValue( UiConst.Icon )?.Description() ); + return this; + } + + /// + /// 配置图标类型 + /// + public IconBuilder Type() { + return Type( _config.GetValue( UiConst.Type ) ) + .BindType( _config.GetValue( AngularConst.BindType ) ); + } + + /// + /// 配置图标类型 + /// + public IconBuilder Type( AntDesignIcon? type ) { + return Type( type?.Description() ); + } + + /// + /// 配置图标类型 + /// + public IconBuilder Type( string type ) { + AttributeIfNotEmpty( "nzType", type ); + return this; + } + + /// + /// 配置图标类型 + /// + public IconBuilder BindType( string type ) { + AttributeIfNotEmpty( "[nzType]", type ); + return this; + } + + /// + /// 配置图标主题 + /// + public IconBuilder Theme() { + return Theme( _config.GetValue( UiConst.Theme ) ) + .BindTheme( _config.GetValue( AngularConst.BindTheme ) ); + } + + /// + /// 配置图标主题 + /// + /// 主题 + public IconBuilder Theme( IconTheme? theme ) { + return Theme( theme?.Description() ); + } + + /// + /// 配置图标主题 + /// + /// 主题 + public IconBuilder Theme( string theme ) { + AttributeIfNotEmpty( "nzTheme", theme ); + return this; + } + + /// + /// 配置图标主题 + /// + /// 主题 + public IconBuilder BindTheme( string theme ) { + AttributeIfNotEmpty( "[nzTheme]", theme ); + return this; + } + + /// + /// 配置双色图标主题色 + /// + public IconBuilder Color() { + var value = _config.GetValue( UiConst.TwotoneColorType )?.Description(); + if ( value.IsEmpty() == false ) + _config.SetAttribute( UiConst.TwotoneColor, value ); + if ( _config.Contains( UiConst.TwotoneColor ) || _config.Contains( AngularConst.BindTwotoneColor ) ) + Attribute( "nzTheme", IconTheme.Twotone.Description() ); + AttributeIfNotEmpty( "nzTwotoneColor", _config.GetValue( UiConst.TwotoneColor ) ); + AttributeIfNotEmpty( "[nzTwotoneColor]", _config.GetValue( AngularConst.BindTwotoneColor ) ); + return this; + } + + /// + /// 配置图标旋转 + /// + public IconBuilder Spin() { + AttributeIfNotEmpty( "[nzSpin]", _config.GetValue( UiConst.Spin ) ); + return this; + } + + /// + /// 配置旋转 + /// + public IconBuilder Rotate() { + AttributeIfNotEmpty( "[nzRotate]", _config.GetValue( UiConst.Rotate ) ); + return this; + } + + /// + /// 配置Iconfont图标 + /// + public IconBuilder IconFont() { + AttributeIfNotEmpty( "nzIconfont", _config.GetValue( UiConst.IconFont ) ); + AttributeIfNotEmpty( "[nzIconfont]", _config.GetValue( AngularConst.BindIconFont ) ); + return this; + } + + /// + /// 配置事件 + /// + public IconBuilder Events() { + this.OnClick( _config ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + this.Tooltip( _config ).Popover( _config ).Popconfirm( _config ); + Type().Theme().Color().Spin().Rotate().IconFont().Events(); + AddTreeNodeToggleIcon(); + } + + /// + /// 添加TreeNodeToggle图标指令 + /// + private void AddTreeNodeToggleIcon() { + var shareConfig = _config.GetValueFromItems(); + if ( shareConfig == null ) + return; + if ( shareConfig.ActiveIcon ) { + Attribute( "nzTreeNodeToggleActiveIcon" ); + return; + } + if ( shareConfig.RotateIcon ) + Attribute( "nzTreeNodeToggleRotateIcon" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Icons/IconTagHelper.cs b/src/Util.Ui.NgZorro/Components/Icons/IconTagHelper.cs new file mode 100644 index 000000000..2f7fc7438 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Icons/IconTagHelper.cs @@ -0,0 +1,68 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Icons.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Icons; + +/// +/// 图标,生成的标签为<span nz-icon></span> +/// +[HtmlTargetElement( "util-icon" )] +public class IconTagHelper : TooltipTagHelperBase { + /// + /// nzType,图标类型 + /// + public AntDesignIcon Type { get; set; } + /// + /// [nzType],图标类型 + /// + public string BindType { get; set; } + /// + /// nzTheme,图标主题 + /// + public IconTheme Theme { get; set; } + /// + /// [nzTheme],图标主题 + /// + public string BindTheme { get; set; } + /// + /// [nzSpin],持续旋转 + /// + public string Spin { get; set; } + /// + /// [nzRotate],旋转角度 + /// + public string Rotate { get; set; } + /// + /// nzTwotoneColor,扩展属性, 双色图标主题色,注意:仅适用双色图标主题 + /// + public AntDesignColor TwotoneColorType { get; set; } + /// + /// nzTwotoneColor,双色图标主题色,注意:仅适用双色图标主题 + /// + public string TwotoneColor { get; set; } + /// + /// [nzTwotoneColor],双色图标主题色,注意:仅适用双色图标主题 + /// + public string BindTwotoneColor { get; set; } + /// + /// nzIconfont,Iconfont图标 + /// + public string IconFont { get; set; } + /// + /// [nzIconfont],Iconfont图标 + /// + public string BindIconFont { get; set; } + /// + /// (click),单击事件 + /// + public string OnClick { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new IconRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Icons/Renders/IconRender.cs b/src/Util.Ui.NgZorro/Components/Icons/Renders/IconRender.cs new file mode 100644 index 000000000..8c9a291cd --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Icons/Renders/IconRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Icons.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Icons.Renders; + +/// +/// 图标渲染器 +/// +public class IconRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化图标渲染器 + /// + /// 配置 + public IconRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new IconBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new IconRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Images/Builders/ImageBuilder.cs b/src/Util.Ui.NgZorro/Components/Images/Builders/ImageBuilder.cs new file mode 100644 index 000000000..64b0dc0a2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Images/Builders/ImageBuilder.cs @@ -0,0 +1,133 @@ +using Microsoft.AspNetCore.Mvc.Rendering; +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Helpers; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Images.Builders; + +/// +/// 图片标签生成器 +/// +public class ImageBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化图片标签生成器 + /// + public ImageBuilder( Config config ) : base( config,"img", TagRenderMode.SelfClosing ) { + _config = config; + base.Attribute( "nz-image" ); + } + + /// + /// 配置图片地址 + /// + public ImageBuilder Src() { + AttributeIfNotEmpty( "nzSrc", _config.GetValue( UiConst.Src ) ); + AttributeIfNotEmpty( "[nzSrc]", _config.GetValue( AngularConst.BindSrc ) ); + return this; + } + + /// + /// 配置加载失败容错地址 + /// + public ImageBuilder Fallback() { + AttributeIfNotEmpty( "nzFallback", _config.GetValue( UiConst.Fallback ) ); + AttributeIfNotEmpty( "[nzFallback]", _config.GetValue( AngularConst.BindFallback ) ); + return this; + } + + /// + /// 配置加载失败容错地址 + /// + public ImageBuilder Placeholder() { + AttributeIfNotEmpty( "nzPlaceholder", _config.GetValue( UiConst.Placeholder ) ); + AttributeIfNotEmpty( "[nzPlaceholder]", _config.GetValue( AngularConst.BindPlaceholder ) ); + return this; + } + + /// + /// 配置是否禁止预览 + /// + public ImageBuilder DisablePreview() { + AttributeIfNotEmpty( "[nzDisablePreview]", _config.GetValue( UiConst.DisablePreview ) ); + return this; + } + + /// + /// 配置导航时是否关闭预览 + /// + public ImageBuilder CloseOnNavigation() { + AttributeIfNotEmpty( "[nzCloseOnNavigation]", _config.GetValue( UiConst.CloseOnNavigation ) ); + return this; + } + + /// + /// 配置文字方向 + /// + public ImageBuilder Direction() { + AttributeIfNotEmpty( "nzDirection", _config.GetValue( UiConst.Direction )?.Description() ); + AttributeIfNotEmpty( "[nzDirection]", _config.GetValue( AngularConst.BindDirection ) ); + return this; + } + + /// + /// 配置宽度 + /// + public ImageBuilder Width() { + var width = SizeHelper.GetValue( _config.GetValue( UiConst.Width ) ); + AttributeIfNotEmpty( "width", width ); + AttributeIfNotEmpty( "[width]", _config.GetValue( AngularConst.BindWidth ) ); + return this; + } + + /// + /// 配置高度 + /// + public ImageBuilder Height() { + var height = SizeHelper.GetValue( _config.GetValue( UiConst.Height )); + AttributeIfNotEmpty( "height", height ); + AttributeIfNotEmpty( "[height]", _config.GetValue( AngularConst.BindHeight ) ); + return this; + } + + /// + /// 配置文本描述 + /// + public ImageBuilder Alt() { + AttributeIfNotEmpty( "alt", _config.GetValue( UiConst.Alt ) ); + AttributeIfNotEmpty( "[alt]", _config.GetValue( AngularConst.BindAlt ) ); + return this; + } + + /// + /// 配置缩放的每步倍数 + /// + public ImageBuilder ScaleStep() { + AttributeIfNotEmpty( "[nzScaleStep]", _config.GetValue( UiConst.ScaleStep ) ); + return this; + } + + /// + /// 配置事件 + /// + public ImageBuilder Events() { + AttributeIfNotEmpty( "(load)", _config.GetValue( UiConst.OnLoad ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Src().Fallback().Placeholder().DisablePreview().CloseOnNavigation().Direction() + .Width().Height().Alt().ScaleStep().Events() + .SpaceItem( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Images/Builders/ImageGroupBuilder.cs b/src/Util.Ui.NgZorro/Components/Images/Builders/ImageGroupBuilder.cs new file mode 100644 index 000000000..7b1ee2df3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Images/Builders/ImageGroupBuilder.cs @@ -0,0 +1,36 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Images.Builders; + +/// +/// 图片分组标签生成器 +/// +public class ImageGroupBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化图片分组标签生成器 + /// + public ImageGroupBuilder( Config config ) : base( config, "nz-image-group" ) { + _config = config; + } + + /// + /// 配置缩放的每步倍数 + /// + public ImageGroupBuilder ScaleStep() { + AttributeIfNotEmpty( "[nzScaleStep]", _config.GetValue( UiConst.ScaleStep ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + ScaleStep(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Images/ImageGroupTagHelper.cs b/src/Util.Ui.NgZorro/Components/Images/ImageGroupTagHelper.cs new file mode 100644 index 000000000..0d61609e8 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Images/ImageGroupTagHelper.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Images.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Images; + +/// +/// 图片分组,生成的标签为<nz-image-group></nz-image-group> +/// +[HtmlTargetElement( "util-image-group" )] +public class ImageGroupTagHelper : AngularTagHelperBase { + /// + /// [nzScaleStep], `1 + nzScaleStep` 为缩放放大的每步倍数, 类型: number + /// + public string ScaleStep { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ImageGroupRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Images/ImageTagHelper.cs b/src/Util.Ui.NgZorro/Components/Images/ImageTagHelper.cs new file mode 100644 index 000000000..a2f9ec6d9 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Images/ImageTagHelper.cs @@ -0,0 +1,96 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Images.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Images; + +/// +/// 图片,生成的标签为<img nz-image /> +/// +[HtmlTargetElement( "util-image" )] +public class ImageTagHelper : AngularTagHelperBase { + /// + /// nzSrc,图片地址 + /// + public string Src { get; set; } + /// + /// [nzSrc],图片地址 + /// + public string BindSrc { get; set; } + /// + /// nzFallback,加载失败容错地址 + /// + public string Fallback { get; set; } + /// + /// [nzFallback],加载失败容错地址 + /// + public string BindFallback { get; set; } + /// + /// nzPlaceholder,加载占位地址 + /// + public string Placeholder { get; set; } + /// + /// [nzPlaceholder],加载占位地址 + /// + public string BindPlaceholder { get; set; } + /// + /// [nzDisablePreview],是否禁止预览, 类型: boolean, 默认值: false + /// + public string DisablePreview { get; set; } + /// + /// [nzCloseOnNavigation],导航时是否关闭预览,类型: boolean, 默认值: false + /// + public string CloseOnNavigation { get; set; } + /// + /// nzDirection,文字方向,可选值: 'ltr' | 'rtl',默认值: 'ltr' + /// + public Direction Direction { get; set; } + /// + /// [nzDirection],文字方向,可选值: 'ltr' | 'rtl',默认值: 'ltr' + /// + public string BindDirection { get; set; } + /// + /// width,图片宽度, 可设置为数值,单位 px, 也可自行设置单位 + /// + public string Width { get; set; } + /// + /// [width],图片宽度 + /// + public string BindWidth { get; set; } + /// + /// height,图片高度, 可设置为数值,单位 px, 也可自行设置单位 + /// + public string Height { get; set; } + /// + /// [height],图片高度 + /// + public string BindHeight { get; set; } + /// + /// alt,文本描述 + /// + public string Alt { get; set; } + /// + /// [alt],文本描述 + /// + public string BindAlt { get; set; } + /// + /// [nzScaleStep], `1 + nzScaleStep` 为缩放放大的每步倍数, 类型: number, 默认值: 0.5 + /// + public string ScaleStep { get; set; } + /// + /// *nzSpaceItem,值为 true 时设置为间距项,放入 <util-space> 中使用 + /// + public bool SpaceItem { get; set; } + /// + /// (load),图片加载完成事件 + /// + public string OnLoad { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ImageRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Images/Renders/ImageGroupRender.cs b/src/Util.Ui.NgZorro/Components/Images/Renders/ImageGroupRender.cs new file mode 100644 index 000000000..4920c3ff7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Images/Renders/ImageGroupRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Images.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Images.Renders; + +/// +/// 图片分组渲染器 +/// +public class ImageGroupRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化图片分组渲染器 + /// + /// 配置 + public ImageGroupRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ImageGroupBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ImageGroupRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Images/Renders/ImageRender.cs b/src/Util.Ui.NgZorro/Components/Images/Renders/ImageRender.cs new file mode 100644 index 000000000..7d25cc64d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Images/Renders/ImageRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Images.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Images.Renders; + +/// +/// 图片渲染器 +/// +public class ImageRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化图片渲染器 + /// + /// 配置 + public ImageRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ImageBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ImageRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/InputNumbers/Builders/InputNumberBuilder.cs b/src/Util.Ui.NgZorro/Components/InputNumbers/Builders/InputNumberBuilder.cs new file mode 100644 index 000000000..2b2f70777 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/InputNumbers/Builders/InputNumberBuilder.cs @@ -0,0 +1,150 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.InputNumbers.Builders; + +/// +/// 数字输入框标签生成器 +/// +public class InputNumberBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化数字输入框标签生成器 + /// + /// 配置 + public InputNumberBuilder( Config config ) : base( config, "nz-input-number" ) { + _config = config; + } + + /// + /// 配置输入框标识 + /// + public InputNumberBuilder InputId() { + AttributeIfNotEmpty( "nzId", _config.GetValue( UiConst.InputId ) ); + return this; + } + + /// + /// 配置自动聚焦 + /// + public InputNumberBuilder AutoFocus() { + AttributeIfNotEmpty( "[nzAutoFocus]", _config.GetValue( UiConst.AutoFocus ) ); + return this; + } + + /// + /// 配置禁用 + /// + public InputNumberBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置最大值 + /// + public InputNumberBuilder Max() { + AttributeIfNotEmpty( "[nzMax]", _config.GetValue( UiConst.Max ) ); + return this; + } + + /// + /// 配置最小值 + /// + public InputNumberBuilder Min() { + AttributeIfNotEmpty( "[nzMin]", _config.GetValue( UiConst.Min ) ); + return this; + } + + /// + /// 配置格式化函数 + /// + public InputNumberBuilder Formatter() { + AttributeIfNotEmpty( "[nzFormatter]", _config.GetValue( UiConst.Formatter ) ); + return this; + } + + /// + /// 配置转换函数 + /// + public InputNumberBuilder Parser() { + AttributeIfNotEmpty( "[nzParser]", _config.GetValue( UiConst.Parser ) ); + return this; + } + + /// + /// 配置精度 + /// + public InputNumberBuilder Precision() { + AttributeIfNotEmpty( "[nzPrecision]", _config.GetValue( UiConst.Precision ) ); + return this; + } + + /// + /// 配置精度模式 + /// + public InputNumberBuilder PrecisionMode() { + AttributeIfNotEmpty( "nzPrecisionMode", _config.GetValue( UiConst.PrecisionMode )?.Description() ); + AttributeIfNotEmpty( "[nzPrecisionMode]", _config.GetValue( AngularConst.BindPrecisionMode ) ); + return this; + } + + /// + /// 配置输入框大小 + /// + public InputNumberBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置步数 + /// + public InputNumberBuilder Step() { + AttributeIfNotEmpty( "[nzStep]", _config.GetValue( UiConst.Step ) ); + return this; + } + + /// + /// 配置输入模式 + /// + public InputNumberBuilder InputMode() { + AttributeIfNotEmpty( "nzInputMode", _config.GetValue( UiConst.InputMode )?.Description() ); + AttributeIfNotEmpty( "[nzInputMode]", _config.GetValue( AngularConst.BindInputMode ) ); + return this; + } + + /// + /// 配置占位提示 + /// + public InputNumberBuilder Placeholder() { + AttributeIfNotEmpty( "nzPlaceHolder", _config.GetValue( UiConst.Placeholder ) ); + AttributeIfNotEmpty( "[nzPlaceHolder]", _config.GetValue( AngularConst.BindPlaceholder ) ); + return this; + } + + /// + /// 配置事件 + /// + public InputNumberBuilder Events() { + AttributeIfNotEmpty( "(nzFocus)", _config.GetValue( UiConst.OnFocus ) ); + AttributeIfNotEmpty( "(nzBlur)", _config.GetValue( UiConst.OnBlur ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + ConfigForm().Name().InputId().AutoFocus().Disabled().Max().Min().Formatter().Parser() + .Precision().PrecisionMode().Size().Step().InputMode().Placeholder() + .Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/InputNumbers/Builders/InputNumberGroupBuilder.cs b/src/Util.Ui.NgZorro/Components/InputNumbers/Builders/InputNumberGroupBuilder.cs new file mode 100644 index 000000000..c6cd014b3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/InputNumbers/Builders/InputNumberGroupBuilder.cs @@ -0,0 +1,151 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Inputs.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.InputNumbers.Builders; + +/// +/// 数字输入框组合标签生成器 +/// +public class InputNumberGroupBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 输入框组合共享配置 + /// + private readonly InputGroupShareConfig _shareConfig; + + /// + /// 初始化数字输入框组合标签生成器 + /// + /// 配置 + public InputNumberGroupBuilder( Config config ) : base( config, "nz-input-number-group" ) { + _config = config; + _shareConfig = GetInputGroupShareConfig(); + } + + /// + /// 获取输入框组合共享配置 + /// + private InputGroupShareConfig GetInputGroupShareConfig() { + return _config.GetValueFromItems() ?? new InputGroupShareConfig(); + } + + /// + /// 配置前置标签 + /// + public InputNumberGroupBuilder AddOnBefore() { + AttributeIfNotEmpty( "nzAddOnBefore", _config.GetValue( UiConst.AddOnBefore )); + AttributeIfNotEmpty( "[nzAddOnBefore]", _config.GetValue( AngularConst.BindAddOnBefore ) ); + AttributeIfNotEmpty( "nzAddOnBefore", _shareConfig.AddOnBefore ); + AttributeIfNotEmpty( "[nzAddOnBefore]", _shareConfig.BindAddOnBefore ); + return this; + } + + /// + /// 配置后置标签 + /// + public InputNumberGroupBuilder AddOnAfter() { + AttributeIfNotEmpty( "nzAddOnAfter", _config.GetValue( UiConst.AddOnAfter ) ); + AttributeIfNotEmpty( "[nzAddOnAfter]", _config.GetValue( AngularConst.BindAddOnAfter ) ); + AttributeIfNotEmpty( "nzAddOnAfter", _shareConfig.AddOnAfter ); + AttributeIfNotEmpty( "[nzAddOnAfter]", _shareConfig.BindAddOnAfter ); + return this; + } + + /// + /// 配置前置图标 + /// + public InputNumberGroupBuilder AddOnBeforeIcon() { + AttributeIfNotEmpty( "nzAddOnBeforeIcon", _config.GetValue( UiConst.AddOnBeforeIcon )?.Description() ); + AttributeIfNotEmpty( "[nzAddOnBeforeIcon]", _config.GetValue( AngularConst.BindAddOnBeforeIcon ) ); + AttributeIfNotEmpty( "nzAddOnBeforeIcon", _shareConfig.AddOnBeforeIcon?.Description() ); + AttributeIfNotEmpty( "[nzAddOnBeforeIcon]", _shareConfig.BindAddOnBeforeIcon ); + return this; + } + + /// + /// 配置后置图标 + /// + public InputNumberGroupBuilder AddOnAfterIcon() { + AttributeIfNotEmpty( "nzAddOnAfterIcon", _config.GetValue( UiConst.AddOnAfterIcon )?.Description() ); + AttributeIfNotEmpty( "[nzAddOnAfterIcon]", _config.GetValue( AngularConst.BindAddOnAfterIcon ) ); + AttributeIfNotEmpty( "nzAddOnAfterIcon", _shareConfig.AddOnAfterIcon?.Description() ); + AttributeIfNotEmpty( "[nzAddOnAfterIcon]", _shareConfig.BindAddOnAfterIcon ); + return this; + } + + /// + /// 配置前缀 + /// + public InputNumberGroupBuilder Prefix() { + AttributeIfNotEmpty( "nzPrefix", _config.GetValue( UiConst.Prefix ) ); + AttributeIfNotEmpty( "[nzPrefix]", _config.GetValue( AngularConst.BindPrefix ) ); + AttributeIfNotEmpty( "nzPrefix", _shareConfig.Prefix ); + AttributeIfNotEmpty( "[nzPrefix]", _shareConfig.BindPrefix ); + return this; + } + + /// + /// 配置后缀 + /// + public InputNumberGroupBuilder Suffix() { + AttributeIfNotEmpty( "nzSuffix", _config.GetValue( UiConst.Suffix ) ); + AttributeIfNotEmpty( "[nzSuffix]", _config.GetValue( AngularConst.BindSuffix ) ); + AttributeIfNotEmpty( "nzSuffix", _shareConfig.Suffix ); + AttributeIfNotEmpty( "[nzSuffix]", _shareConfig.BindSuffix ); + return this; + } + + /// + /// 配置前缀图标 + /// + public InputNumberGroupBuilder PrefixIcon() { + AttributeIfNotEmpty( "nzPrefixIcon", _config.GetValue( UiConst.PrefixIcon )?.Description() ); + AttributeIfNotEmpty( "[nzPrefixIcon]", _config.GetValue( AngularConst.BindPrefixIcon ) ); + AttributeIfNotEmpty( "nzPrefixIcon", _shareConfig.PrefixIcon?.Description() ); + AttributeIfNotEmpty( "[nzPrefixIcon]", _shareConfig.BindPrefixIcon ); + return this; + } + + /// + /// 配置后缀图标 + /// + public InputNumberGroupBuilder SuffixIcon() { + AttributeIfNotEmpty( "nzSuffixIcon", _config.GetValue( UiConst.SuffixIcon )?.Description() ); + AttributeIfNotEmpty( "[nzSuffixIcon]", _config.GetValue( AngularConst.BindSuffixIcon ) ); + AttributeIfNotEmpty( "nzSuffixIcon", _shareConfig.SuffixIcon?.Description() ); + AttributeIfNotEmpty( "[nzSuffixIcon]", _shareConfig.BindSuffixIcon ); + return this; + } + + /// + /// 配置输入框大小 + /// + public InputNumberGroupBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置紧凑模式 + /// + public InputNumberGroupBuilder Compact() { + AttributeIfNotEmpty( "[nzCompact]", _config.GetValue( UiConst.Compact ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + AddOnBefore().AddOnAfter().AddOnBeforeIcon().AddOnAfterIcon() + .Prefix().Suffix().PrefixIcon().SuffixIcon() + .Size().Compact(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/InputNumbers/InputNumberGroupTagHelper.cs b/src/Util.Ui.NgZorro/Components/InputNumbers/InputNumberGroupTagHelper.cs new file mode 100644 index 000000000..6adf356fd --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/InputNumbers/InputNumberGroupTagHelper.cs @@ -0,0 +1,113 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Forms.Helpers; +using Util.Ui.NgZorro.Components.InputNumbers.Renders; +using Util.Ui.NgZorro.Components.Inputs.Helpers; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.InputNumbers; + +/// +/// 数字输入框组合,生成的标签为<nz-input-number-group></nz-input-number-group> +/// +[HtmlTargetElement( "util-input-number-group" )] +public class InputNumberGroupTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// nzAddOnBefore,设置前置标签 + /// + public string AddOnBefore { get; set; } + /// + /// [nzAddOnBefore],设置前置标签 + /// + public string BindAddOnBefore { get; set; } + /// + /// nzAddOnAfter,设置后置标签 + /// + public string AddOnAfter { get; set; } + /// + /// [nzAddOnAfter],设置后置标签 + /// + public string BindAddOnAfter { get; set; } + /// + /// nzAddOnBeforeIcon,设置前置图标 + /// + public AntDesignIcon AddOnBeforeIcon { get; set; } + /// + /// [nzAddOnBeforeIcon],设置前置图标 + /// + public string BindAddOnBeforeIcon { get; set; } + /// + /// nzAddOnAfterIcon,设置后置图标 + /// + public AntDesignIcon AddOnAfterIcon { get; set; } + /// + /// [nzAddOnAfterIcon],设置后置图标 + /// + public string BindAddOnAfterIcon { get; set; } + /// + /// nzPrefix,设置前缀 + /// + public string Prefix { get; set; } + /// + /// [nzPrefix],设置前缀 + /// + public string BindPrefix { get; set; } + /// + /// nzSuffix,设置后缀 + /// + public string Suffix { get; set; } + /// + /// [nzSuffix],设置后缀 + /// + public string BindSuffix { get; set; } + /// + /// nzPrefixIcon,设置前缀图标 + /// + public AntDesignIcon PrefixIcon { get; set; } + /// + /// [nzPrefixIcon],设置前缀图标 + /// + public string BindPrefixIcon { get; set; } + /// + /// nzSuffixIcon,设置后缀图标 + /// + public AntDesignIcon SuffixIcon { get; set; } + /// + /// [nzSuffixIcon],设置后缀图标 + /// + public string BindSuffixIcon { get; set; } + /// + /// nzSize,设置输入框大小, 可选值: 'default' | 'small' | 'large' + /// + public InputSize Size { get; set; } + /// + /// [nzSize],设置输入框大小, 可选值: 'default' | 'small' | 'large' + /// + public string BindSize { get; set; } + /// + /// [nzCompact],是否紧凑模式, 默认值: false + /// + public string Compact { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var formItemShareService = new FormItemShareService( _config ); + formItemShareService.Init(); + formItemShareService.InitId(); + var inputGroupShareService = new InputGroupShareService( _config ); + inputGroupShareService.Init(); + inputGroupShareService.AutoCreateInputGroup( false ); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new InputNumberGroupRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/InputNumbers/InputNumberTagHelper.cs b/src/Util.Ui.NgZorro/Components/InputNumbers/InputNumberTagHelper.cs new file mode 100644 index 000000000..c4b28f831 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/InputNumbers/InputNumberTagHelper.cs @@ -0,0 +1,120 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.InputNumbers.Renders; +using Util.Ui.NgZorro.Components.Inputs.Helpers; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.InputNumbers; + +/// +/// 数字输入框,生成的标签为<nz-input-number></nz-input-number> +/// +[HtmlTargetElement( "util-input-number" )] +public class InputNumberTagHelper : FormControlTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// nzId,组件内部 input 的 id 值 + /// + public string InputId { get; set; } + /// + /// [nzAutoFocus],是否自动聚焦 + /// + public string AutoFocus { get; set; } + /// + /// [nzDisabled],是否禁用 + /// + public string Disabled { get; set; } + /// + /// [required],是否必填项 + /// + public string Required { get; set; } + /// + /// 扩展属性 requiredMessage,必填项验证消息 + /// + public string RequiredMessage { get; set; } + /// + /// 扩展属性 [requiredMessage],必填项验证消息 + /// + public string BindRequiredMessage { get; set; } + /// + /// [nzMax],最大值 + /// + public string Max { get; set; } + /// + /// [nzMin],最小值 + /// + public string Min { get; set; } + /// + /// [nzFormatter],格式化函数,函数定义: (value: number | string) => string | number + /// + public string Formatter { get; set; } + /// + /// [nzParser],转换函数,指定从 nzFormatter 里转换回数字的方式,和 nzFormatter 搭配使用,函数定义: (value: string) => string | number + /// + public string Parser { get; set; } + /// + /// [nzPrecision],精度,即小数位数 + /// + public string Precision { get; set; } + /// + /// nzPrecisionMode,数值精度的取值方式,可选值: 'cut' | 'toFixed' | ((value: number | string, precision?: number) => number) + /// + public InputNumberPrecisionMode PrecisionMode { get; set; } + /// + /// nzPrecisionMode,数值精度的取值方式,可选值: 'cut' | 'toFixed' | ((value: number | string, precision?: number) => number) + /// + public string BindPrecisionMode { get; set; } + /// + /// nzSize,输入框大小, 可选值: 'default' | 'small' | 'large' + /// + public InputSize Size { get; set; } + /// + /// [nzSize],输入框大小, 可选值: 'default' | 'small' | 'large' + /// + public string BindSize { get; set; } + /// + /// [nzStep],每次改变步数,可以为小数 + /// + public string Step { get; set; } + /// + /// nzInputMode,输入模式,提供了用户在编辑元素或其内容时可能输入的数据类型的提示,默认值:decimal + /// + public InputMode InputMode { get; set; } + /// + /// [nzInputMode],输入模式,提供了用户在编辑元素或其内容时可能输入的数据类型的提示,默认值:decimal + /// + public string BindInputMode { get; set; } + /// + /// nzPlaceHolder,输入框占位文本 + /// + public string Placeholder { get; set; } + /// + /// [nzPlaceHolder],输入框占位文本 + /// + public string BindPlaceholder { get; set; } + /// + /// (nzFocus),获得焦点事件 + /// + public string OnFocus { get; set; } + /// + /// (nzBlur),失去焦点事件 + /// + public string OnBlur { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new InputService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new InputNumberRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/InputNumbers/Renders/InputNumberGroupRender.cs b/src/Util.Ui.NgZorro/Components/InputNumbers/Renders/InputNumberGroupRender.cs new file mode 100644 index 000000000..8de53b3a8 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/InputNumbers/Renders/InputNumberGroupRender.cs @@ -0,0 +1,46 @@ +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.InputNumbers.Builders; + +namespace Util.Ui.NgZorro.Components.InputNumbers.Renders; + +/// +/// 数字输入框组合渲染器 +/// +public class InputNumberGroupRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化数字输入框组合渲染器 + /// + /// 配置 + public InputNumberGroupRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new InputNumberGroupBuilder( _config ); + builder.Config(); + _config.Content.AppendTo( builder ); + formControlBuilder.AppendContent( builder ); + } + + /// + /// 是否添加验证模板 + /// + protected override bool IsAppendValidationTempalte() { + return false; + } + + /// + public override IHtmlContent Clone() { + return new InputNumberGroupRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/InputNumbers/Renders/InputNumberRender.cs b/src/Util.Ui.NgZorro/Components/InputNumbers/Renders/InputNumberRender.cs new file mode 100644 index 000000000..fb41e9374 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/InputNumbers/Renders/InputNumberRender.cs @@ -0,0 +1,39 @@ +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.InputNumbers.Builders; + +namespace Util.Ui.NgZorro.Components.InputNumbers.Renders; + +/// +/// 数字输入框渲染器 +/// +public class InputNumberRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化数字输入框渲染器 + /// + /// 配置 + public InputNumberRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new InputNumberBuilder( _config ); + builder.Config(); + _config.Content.AppendTo( builder ); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new InputNumberRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Inputs/Builders/InputBuilder.cs b/src/Util.Ui.NgZorro/Components/Inputs/Builders/InputBuilder.cs new file mode 100644 index 000000000..e664aac1c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Inputs/Builders/InputBuilder.cs @@ -0,0 +1,221 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Inputs.Configs; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Inputs.Builders; + +/// +/// 输入框标签生成器 +/// +public class InputBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化输入框标签生成器 + /// + /// 配置 + public InputBuilder( Config config ) : base( config, "input", Microsoft.AspNetCore.Mvc.Rendering.TagRenderMode.SelfClosing ) { + base.Attribute( "nz-input" ); + _config = config; + } + + /// + /// 配置占位符 + /// + public virtual InputBuilder Placeholder() { + AttributeIfNotEmpty( "placeholder", _config.GetValue( UiConst.Placeholder ) ); + AttributeIfNotEmpty( "[placeholder]", _config.GetValue( AngularConst.BindPlaceholder ) ); + return this; + } + + /// + /// 配置禁用 + /// + public InputBuilder Disabled() { + AttributeIfNotEmpty( "[disabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置只读 + /// + public InputBuilder Readonly() { + AttributeIfNotEmpty( "[readOnly]", _config.GetValue( UiConst.Readonly ) ); + return this; + } + + /// + /// 配置输入框大小 + /// + public InputBuilder Size() { + SetSize(); + SetBindSize(); + return this; + } + + /// + /// 设置输入框大小 + /// + private void SetSize() { + var shareConfig = GetInputGroupShareConfig(); + if ( shareConfig.Size == null ) + return; + if ( shareConfig.InputGroupCreated ) + return; + AttributeIfNotEmpty( "nzSize", shareConfig.Size?.Description() ); + } + + /// + /// 设置输入框大小 + /// + private void SetBindSize() { + var shareConfig = GetInputGroupShareConfig(); + if ( shareConfig.BindSize.IsEmpty() ) + return; + if ( shareConfig.InputGroupCreated ) + return; + AttributeIfNotEmpty( "[nzSize]", shareConfig.BindSize ); + } + + /// + /// 配置校验状态 + /// + public InputBuilder Status() { + SetStatus(); + SetBindStatus(); + return this; + } + + /// + /// 设置校验状态 + /// + private void SetStatus() { + var shareConfig = GetInputGroupShareConfig(); + if ( shareConfig.Status == null ) + return; + if ( shareConfig.InputGroupCreated ) + return; + AttributeIfNotEmpty( "nzStatus", shareConfig.Status?.Description() ); + } + + /// + /// 设置校验状态 + /// + private void SetBindStatus() { + var shareConfig = GetInputGroupShareConfig(); + if ( shareConfig.BindStatus.IsEmpty() ) + return; + if ( shareConfig.InputGroupCreated ) + return; + AttributeIfNotEmpty( "[nzStatus]", shareConfig.BindStatus ); + } + + /// + /// 获取输入框组合共享配置 + /// + private InputGroupShareConfig GetInputGroupShareConfig() { + return _config.GetValueFromItems() ?? new InputGroupShareConfig(); + } + + /// + /// 配置输入框类型 + /// + public InputBuilder Type() { + var type = _config.GetValue( UiConst.Type ); + if ( type == InputType.Password ) + return this; + if ( type == InputType.Email ) { + Attribute( "[email]", "true" ); + } + AttributeIfNotEmpty( "type", type?.Description() ); + AttributeIfNotEmpty( "[type]", _config.GetValue( AngularConst.BindType ) ); + return this; + } + + /// + /// 配置自动完成 + /// + public InputBuilder Autocomplete() { + var autocompleteId = _config.GetValue( UiConst.Autocomplete ); + if ( autocompleteId.IsEmpty() ) + return this; + AttributeIfNotEmpty( "[nzAutocomplete]", autocompleteId ); + var autocompleteSearchKeyword = _config.GetValue( UiConst.AutocompleteSearchKeyword ); + if ( autocompleteSearchKeyword == true ) + OnInput( $"x_{autocompleteId}.search($event.target)" ); + return this; + } + + /// + /// 配置手机号验证 + /// + public InputBuilder ValidatePhone() { + AttributeIfNotEmpty( "[isInvalidPhone]", _config.GetValue( UiConst.IsInvalidPhone ) ); + return this; + } + + /// + /// 配置身份证验证 + /// + public InputBuilder ValidateIdCard() { + AttributeIfNotEmpty( "[isInvalidIdCard]", _config.GetValue( UiConst.IsInvalidIdCard ) ); + return this; + } + + /// + /// 配置必填项验证 + /// + public override InputBuilder Required() { + AttributeIfNotEmpty( "[x-required-extend]", _config.GetValue( UiConst.Required ) ); + return this; + } + + /// + /// 配置事件 + /// + public InputBuilder Events() { + OnInput( _config.GetValue( UiConst.OnInput ) ); + AttributeIfNotEmpty( "(blur)", _config.GetValue( UiConst.OnBlur ) ); + AttributeIfNotEmpty( "(keyup.enter)", _config.GetValue( UiConst.OnKeyupEnter ) ); + AttributeIfNotEmpty( "(keydown.enter)", _config.GetValue( UiConst.OnKeydownEnter ) ); + return this; + } + + /// + /// 输入事件 + /// + public InputBuilder OnInput( string value ) { + AttributeIfNotEmpty( "(input)", value ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + ConfigForm().Name().Placeholder().Disabled().Readonly() + .Size().Status() + .Type().Autocomplete() + .ValidatePhone().ValidateIdCard() + .Events(); + } + + /// + /// 配置间距项 + /// + public override InputBuilder SpaceItem() { + if ( FormItemShareConfig.FormItemCreated ) + return this; + var inputGroupShareConfig = GetInputGroupShareConfig(); + if ( inputGroupShareConfig.InputGroupCreated ) + return this; + this.SpaceItem( FormItemShareConfig.SpaceItem ); + return this; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Inputs/Builders/InputGroupBuilder.cs b/src/Util.Ui.NgZorro/Components/Inputs/Builders/InputGroupBuilder.cs new file mode 100644 index 000000000..4f060f6d8 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Inputs/Builders/InputGroupBuilder.cs @@ -0,0 +1,295 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Components.Inputs.Configs; +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Extensions; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Icons.Builders; +using Util.Ui.NgZorro.Components.Templates.Builders; +using Util.Ui.NgZorro.Extensions; +using Util.Ui.NgZorro.Components.Forms.Configs; + +namespace Util.Ui.NgZorro.Components.Inputs.Builders; + +/// +/// 输入框组合标签生成器 +/// +public class InputGroupBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 输入框组合共享配置 + /// + private readonly InputGroupShareConfig _shareConfig; + + /// + /// 初始化输入框组合标签生成器 + /// + /// 配置 + public InputGroupBuilder( Config config ) : base( config, "nz-input-group" ) { + _config = config; + _shareConfig = GetInputGroupShareConfig(); + } + + /// + /// 获取输入框组合共享配置 + /// + private InputGroupShareConfig GetInputGroupShareConfig() { + return _config.GetValueFromItems() ?? new InputGroupShareConfig(); + } + + /// + /// 配置前置标签 + /// + public InputGroupBuilder AddOnBefore() { + AttributeIfNotEmpty( "nzAddOnBefore", _config.GetValue( UiConst.AddOnBefore ) ); + AttributeIfNotEmpty( "[nzAddOnBefore]", _config.GetValue( AngularConst.BindAddOnBefore ) ); + AttributeIfNotEmpty( "nzAddOnBefore", _shareConfig.AddOnBefore ); + AttributeIfNotEmpty( "[nzAddOnBefore]", _shareConfig.BindAddOnBefore ); + return this; + } + + /// + /// 配置后置标签 + /// + public InputGroupBuilder AddOnAfter() { + AttributeIfNotEmpty( "nzAddOnAfter", _config.GetValue( UiConst.AddOnAfter ) ); + AttributeIfNotEmpty( "[nzAddOnAfter]", _config.GetValue( AngularConst.BindAddOnAfter ) ); + AttributeIfNotEmpty( "nzAddOnAfter", _shareConfig.AddOnAfter ); + AttributeIfNotEmpty( "[nzAddOnAfter]", _shareConfig.BindAddOnAfter ); + return this; + } + + /// + /// 配置前置图标 + /// + public InputGroupBuilder AddOnBeforeIcon() { + AttributeIfNotEmpty( "nzAddOnBeforeIcon", _config.GetValue( UiConst.AddOnBeforeIcon )?.Description() ); + AttributeIfNotEmpty( "[nzAddOnBeforeIcon]", _config.GetValue( AngularConst.BindAddOnBeforeIcon ) ); + AttributeIfNotEmpty( "nzAddOnBeforeIcon", _shareConfig.AddOnBeforeIcon?.Description() ); + AttributeIfNotEmpty( "[nzAddOnBeforeIcon]", _shareConfig.BindAddOnBeforeIcon ); + return this; + } + + /// + /// 配置后置图标 + /// + public InputGroupBuilder AddOnAfterIcon() { + AttributeIfNotEmpty( "nzAddOnAfterIcon", _config.GetValue( UiConst.AddOnAfterIcon )?.Description() ); + AttributeIfNotEmpty( "[nzAddOnAfterIcon]", _config.GetValue( AngularConst.BindAddOnAfterIcon ) ); + AttributeIfNotEmpty( "nzAddOnAfterIcon", _shareConfig.AddOnAfterIcon?.Description() ); + AttributeIfNotEmpty( "[nzAddOnAfterIcon]", _shareConfig.BindAddOnAfterIcon ); + return this; + } + + /// + /// 配置前缀 + /// + public InputGroupBuilder Prefix() { + AttributeIfNotEmpty( "nzPrefix", _shareConfig.Prefix ); + AttributeIfNotEmpty( "[nzPrefix]", _shareConfig.BindPrefix ); + return this; + } + + /// + /// 配置后缀 + /// + public InputGroupBuilder Suffix() { + AttributeIfNotEmpty( "nzSuffix", _shareConfig.Suffix ); + AttributeIfNotEmpty( "[nzSuffix]", _shareConfig.BindSuffix ); + if (_shareConfig.Suffix.IsEmpty() == false || _shareConfig.BindSuffix.IsEmpty() == false ) + return this; + if ( IsAllowClear() || IsPassword() ) + AttributeIfNotEmpty( "[nzSuffix]", GetSuffixTemplateId() ); + return this; + } + + /// + /// 是否允许清除 + /// + private bool IsAllowClear() { + return _shareConfig.AllowClear; + } + + /// + /// 是否密码类型 + /// + private bool IsPassword() { + return _shareConfig.IsPassword; + } + + /// + /// 获取后置图标模板标识 + /// + protected string GetSuffixTemplateId() { + return $"tmp_{_shareConfig.InputId}"; + } + + /// + /// 配置前缀图标 + /// + public InputGroupBuilder PrefixIcon() { + AttributeIfNotEmpty( "nzPrefixIcon", _config.GetValue( UiConst.PrefixIcon )?.Description() ); + AttributeIfNotEmpty( "[nzPrefixIcon]", _config.GetValue( AngularConst.BindPrefixIcon ) ); + AttributeIfNotEmpty( "nzPrefixIcon", _shareConfig.PrefixIcon?.Description() ); + AttributeIfNotEmpty( "[nzPrefixIcon]", _shareConfig.BindPrefixIcon ); + return this; + } + + /// + /// 配置后缀图标 + /// + public InputGroupBuilder SuffixIcon() { + AttributeIfNotEmpty( "nzSuffixIcon", _config.GetValue( UiConst.SuffixIcon )?.Description() ); + AttributeIfNotEmpty( "[nzSuffixIcon]", _config.GetValue( AngularConst.BindSuffixIcon ) ); + AttributeIfNotEmpty( "nzSuffixIcon", _shareConfig.SuffixIcon?.Description() ); + AttributeIfNotEmpty( "[nzSuffixIcon]", _shareConfig.BindSuffixIcon ); + return this; + } + + /// + /// 配置搜索 + /// + public InputGroupBuilder Search() { + AttributeIfNotEmpty( "[nzSearch]", _config.GetValue( UiConst.Search ) ); + return this; + } + + /// + /// 配置输入框大小 + /// + public InputGroupBuilder Size() { + AttributeIfNotEmpty( "nzSize", _shareConfig.Size?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _shareConfig.BindSize ); + return this; + } + + /// + /// 配置校验状态 + /// + public InputGroupBuilder Status() { + AttributeIfNotEmpty( "nzStatus", _shareConfig.Status?.Description() ); + AttributeIfNotEmpty( "[nzStatus]", _shareConfig.BindStatus ); + return this; + } + + /// + /// 配置紧凑模式 + /// + public InputGroupBuilder Compact() { + AttributeIfNotEmpty( "[nzCompact]", _config.GetValue( UiConst.Compact ) ); + return this; + } + + /// + /// 配置间距项 + /// + public InputGroupBuilder SpaceItem() { + var formItemShareConfig = GetShareConfig(); + if ( formItemShareConfig.FormItemCreated ) + return this; + this.SpaceItem( formItemShareConfig.SpaceItem ); + return this; + } + + /// + /// 获取表单项共享配置 + /// + private FormItemShareConfig GetShareConfig() { + return _config.GetValueFromItems() ?? new FormItemShareConfig(); + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + AddOnBefore().AddOnAfter().AddOnBeforeIcon().AddOnAfterIcon() + .Prefix().Suffix().PrefixIcon().SuffixIcon() + .Search().Size().Status().Compact().SpaceItem(); + Class( _shareConfig.Class ); + AppendContent( GetSuffixTemplate() ); + } + + /// + /// 获取后置图标模板 + /// + private TagBuilder GetSuffixTemplate() { + if ( NeedCreateSuffixTemplate() == false ) + return new EmptyTagBuilder(); + var templateBuilder = new TemplateBuilder( _config ); + templateBuilder.Id( GetSuffixTemplateId() ); + templateBuilder.AppendContent( GetPasswordIconBuilder() ); + templateBuilder.AppendContent( GetClearIconBuilder() ); + return templateBuilder; + } + + /// + /// 是否需要创建后置图标模板 + /// + private bool NeedCreateSuffixTemplate() { + if ( _config.GetValue( UiConst.Suffix ).IsEmpty() == false ) + return false; + if ( _config.GetValue( AngularConst.BindSuffix ).IsEmpty() == false ) + return false; + if ( _shareConfig.Suffix.IsEmpty() == false ) + return false; + if ( _shareConfig.BindSuffix.IsEmpty() == false ) + return false; + return IsAllowClear() || IsPassword(); + } + + /// + /// 获取密码图标标签生成器 + /// + private TagBuilder GetPasswordIconBuilder() { + if ( IsPassword() == false ) + return new EmptyTagBuilder(); + var builder = new IconBuilder( _config ); + builder.BindType( $"xi_{_shareConfig.InputId}.passwordVisible?'eye-invisible':'eye'" ); + builder.OnClick( $"xi_{_shareConfig.InputId}.passwordVisible = !xi_{_shareConfig.InputId}.passwordVisible" ); + return builder; + } + + /// + /// 获取清除图标标签生成器 + /// + private TagBuilder GetClearIconBuilder() { + if ( IsAllowClear() == false ) + return new EmptyTagBuilder(); + var builder = new IconBuilder( _config ); + builder.Class( "ant-input-clear-icon" ); + builder.Theme( IconTheme.Fill ).Type( AntDesignIcon.CloseCircle ); + builder.NgIf( $"{GetNgModelId()}.value" ); + SetClearIconReset( builder ); + return builder; + } + + /// + /// 设置清除操作 + /// + private void SetClearIconReset( IconBuilder builder ) { + var shareConfig = GetFormItemShareConfig(); + if ( shareConfig.IsValidationExtend == false ) { + builder.OnClick( $"{GetNgModelId()}.reset()" ); + return; + } + builder.OnClick( $"{shareConfig.ValidationExtendId}.reset()" ); + } + + /// + /// 获取表单项共享配置 + /// + private FormItemShareConfig GetFormItemShareConfig() { + return _config.GetValueFromItems() ?? new FormItemShareConfig(); + } + + /// + /// 获取ngModel引用变量名称 + /// + protected string GetNgModelId() { + return $"model_{_shareConfig.InputId}"; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Inputs/Builders/TextareaBuilder.cs b/src/Util.Ui.NgZorro/Components/Inputs/Builders/TextareaBuilder.cs new file mode 100644 index 000000000..62b91ff36 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Inputs/Builders/TextareaBuilder.cs @@ -0,0 +1,162 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Inputs.Configs; +using Util.Ui.NgZorro.Components.Inputs.Helpers; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Inputs.Builders; + +/// +/// 文本域标签生成器 +/// +public class TextareaBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化文本域标签生成器 + /// + /// 配置 + public TextareaBuilder( Config config ) : base( config, "textarea" ) { + base.Attribute( "nz-input" ); + _config = config; + } + + /// + /// 配置占位符 + /// + public virtual TextareaBuilder Placeholder() { + AttributeIfNotEmpty( "placeholder", _config.GetValue( UiConst.Placeholder ) ); + AttributeIfNotEmpty( "[placeholder]", _config.GetValue( AngularConst.BindPlaceholder ) ); + return this; + } + + /// + /// 配置禁用 + /// + public TextareaBuilder Disabled() { + AttributeIfNotEmpty( "[disabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置只读 + /// + public TextareaBuilder Readonly() { + AttributeIfNotEmpty( "[readOnly]", _config.GetValue( UiConst.Readonly ) ); + return this; + } + + /// + /// 配置行数 + /// + public TextareaBuilder Rows() { + AttributeIfNotEmpty( "[rows]", _config.GetValue( UiConst.Rows ) ); + return this; + } + + /// + /// 配置列数 + /// + public TextareaBuilder Columns() { + AttributeIfNotEmpty( "[cols]", _config.GetValue( UiConst.Columns ) ); + return this; + } + + /// + /// 配置自适应内容高度 + /// + public TextareaBuilder Autosize() { + AttributeIfNotEmpty( "[nzAutosize]", _config.GetValue( UiConst.Autosize ) ); + var model = new RowsModel { MinRows = _config.GetValue( UiConst.MinRows ), MaxRows = _config.GetValue( UiConst.MaxRows ) }; + AttributeIfNotEmpty( "[nzAutosize]", model.ToJson() ); + return this; + } + + /// + /// 配置输入框大小 + /// + public TextareaBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置必填项验证 + /// + public override TextareaBuilder Required() { + AttributeIfNotEmpty( "[x-required-extend]", _config.GetValue( UiConst.Required ) ); + return this; + } + + /// + /// 配置自动完成 + /// + public TextareaBuilder Autocomplete() { + var autocompleteId = _config.GetValue( UiConst.Autocomplete ); + if ( autocompleteId.IsEmpty() ) + return this; + AttributeIfNotEmpty( "[nzAutocomplete]", autocompleteId ); + var autocompleteSearchKeyword = _config.GetValue( UiConst.AutocompleteSearchKeyword ); + if ( autocompleteSearchKeyword == true ) + OnInput( $"x_{autocompleteId}.search($event.target)" ); + return this; + } + + /// + /// 配置事件 + /// + public TextareaBuilder Events() { + OnInput( _config.GetValue( UiConst.OnInput ) ); + AttributeIfNotEmpty( "(blur)", _config.GetValue( UiConst.OnBlur ) ); + AttributeIfNotEmpty( "(keyup.enter)", _config.GetValue( UiConst.OnKeyupEnter ) ); + AttributeIfNotEmpty( "(keydown.enter)", _config.GetValue( UiConst.OnKeydownEnter ) ); + return this; + } + + /// + /// 输入事件 + /// + public TextareaBuilder OnInput( string value ) { + AttributeIfNotEmpty( "(input)", value ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + ConfigForm().Name().Placeholder().Disabled().Readonly().Size().Rows().Columns() + .Autosize().Size().Autocomplete() + .Events() + .Required().RequiredMessage() + .MinLength().MinLengthMessage() + .MaxLength() + .ValidationExtend(); + } + + /// + /// 配置间距项 + /// + public override TextareaBuilder SpaceItem() { + if ( FormItemShareConfig.FormItemCreated ) + return this; + var inputGroupShareConfig = GetInputGroupShareConfig(); + if ( inputGroupShareConfig.InputGroupCreated ) + return this; + this.SpaceItem( FormItemShareConfig.SpaceItem ); + return this; + } + + /// + /// 获取输入框组合共享配置 + /// + private InputGroupShareConfig GetInputGroupShareConfig() { + return _config.GetValueFromItems() ?? new InputGroupShareConfig(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Inputs/Builders/TextareaCountBuilder.cs b/src/Util.Ui.NgZorro/Components/Inputs/Builders/TextareaCountBuilder.cs new file mode 100644 index 000000000..68046ec8e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Inputs/Builders/TextareaCountBuilder.cs @@ -0,0 +1,45 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Inputs.Builders; + +/// +/// 文本域计数标签生成器 +/// +public class TextareaCountBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化文本域计数标签生成器 + /// + /// 配置 + public TextareaCountBuilder( Config config ) : base( config,"nz-textarea-count" ) { + _config = config; + } + + /// + /// 配置数字提示最大值 + /// + public TextareaCountBuilder MaxCharacterCount() { + AttributeIfNotEmpty( "[nzMaxCharacterCount]", _config.GetValue( UiConst.MaxCharacterCount ) ); + return this; + } + + /// + /// 配置数字提示最大值计算函数 + /// + public TextareaCountBuilder ComputeCharacterCount() { + AttributeIfNotEmpty( "[nzComputeCharacterCount]", _config.GetValue( UiConst.ComputeCharacterCount ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + MaxCharacterCount().ComputeCharacterCount(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Inputs/Configs/InputGroupShareConfig.cs b/src/Util.Ui.NgZorro/Components/Inputs/Configs/InputGroupShareConfig.cs new file mode 100644 index 000000000..023b27fa3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Inputs/Configs/InputGroupShareConfig.cs @@ -0,0 +1,117 @@ +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Inputs.Configs; + +/// +/// 输入框组合共享配置 +/// +public class InputGroupShareConfig { + /// + /// 是否已创建 nz-input-group + /// + public bool InputGroupCreated { get; set; } + /// + /// 自动创建nz-input-group + /// + public bool? AutoCreateInputGroup { get; set; } + /// + /// 设置前置标签 + /// + public string AddOnBefore { get; set; } + /// + /// 设置前置标签 + /// + public string BindAddOnBefore { get; set; } + /// + /// 设置后置标签 + /// + public string AddOnAfter { get; set; } + /// + /// 设置后置标签 + /// + public string BindAddOnAfter { get; set; } + /// + /// 设置前置图标 + /// + public AntDesignIcon? AddOnBeforeIcon { get; set; } + /// + /// 设置前置图标 + /// + public string BindAddOnBeforeIcon { get; set; } + /// + /// 设置后置图标 + /// + public AntDesignIcon? AddOnAfterIcon { get; set; } + /// + /// 设置后置图标 + /// + public string BindAddOnAfterIcon { get; set; } + /// + /// 设置前缀 + /// + public string Prefix { get; set; } + /// + /// 设置前缀 + /// + public string BindPrefix { get; set; } + /// + /// 设置后缀 + /// + public string Suffix { get; set; } + /// + /// 设置后缀 + /// + public string BindSuffix { get; set; } + /// + /// 设置前缀图标 + /// + public AntDesignIcon? PrefixIcon { get; set; } + /// + /// 设置前缀图标 + /// + public string BindPrefixIcon { get; set; } + /// + /// 设置后缀图标 + /// + public AntDesignIcon? SuffixIcon { get; set; } + /// + /// 设置后缀图标 + /// + public string BindSuffixIcon { get; set; } + /// + /// 尺寸 + /// + public InputSize? Size { get; set; } + /// + /// 尺寸 + /// + public string BindSize { get; set; } + /// + /// 检验状态 + /// + public FormControlStatus? Status { get; set; } + /// + /// 检验状态 + /// + public string BindStatus { get; set; } + /// + /// 是否允许清除内容 + /// + public bool AllowClear { get; set; } + /// + /// 是否密码框 + /// + public bool IsPassword { get; set; } + /// + /// 输入框标识 + /// + public string InputId { get; set; } + /// + /// 样式类 + /// + public string Class { get; set; } + /// + /// ngIf* + /// + public string NgIf { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Inputs/Helpers/InputExpressionLoader.cs b/src/Util.Ui.NgZorro/Components/Inputs/Helpers/InputExpressionLoader.cs new file mode 100644 index 000000000..9eb784d3c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Inputs/Helpers/InputExpressionLoader.cs @@ -0,0 +1,187 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Expressions; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Expressions; +using Util.Validation.Validators; + +namespace Util.Ui.NgZorro.Components.Inputs.Helpers; + +/// +/// 输入框表达式加载器 +/// +public class InputExpressionLoader : NgZorroExpressionLoaderBase { + /// + /// 加载模型信息 + /// + /// 配置 + /// 模型表达式信息 + protected override void Load( Config config, ModelExpressionInfo info ) { + LoadLabel( config, info ); + LoadId( config, info ); + LoadName( config, info ); + LoadNgModel( config, info ); + LoadMinRows( config, info ); + LoadVirtualHeight( config, info ); + LoadKeys( config, info ); + LoadPassword( config, info ); + LoadRequired( config, info ); + LoadMinLength( config, info ); + LoadMaxLength( config, info ); + LoadMin( config, info ); + LoadMax( config, info ); + LoadEmail( config, info ); + LoadRegex( config, info ); + LoadPhone( config, info ); + LoadIdCard( config, info ); + } + + /// + /// 加载标签 + /// + protected virtual void LoadLabel( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.LabelText, info.DisplayName, false ); + } + + /// + /// 加载标识 + /// + protected virtual void LoadId( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.Id, GeKebaberizePropertyName( config, info ), false ); + } + + /// + /// 加载名称 + /// + protected virtual void LoadName( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.Name, GeKebaberizePropertyName( config, info ), false ); + } + + /// + /// 加载模型绑定 + /// + protected virtual void LoadNgModel( Config config, ModelExpressionInfo info ) { + config.SetAttribute( AngularConst.NgModel, info.SafePropertyName, false ); + } + + /// + /// 加载文本域最小行数 + /// + protected virtual void LoadMinRows( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.MinRows, 3, false ); + } + + /// + /// 加载下拉树虚拟高度 + /// + protected virtual void LoadVirtualHeight( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.VirtualHeight, "300px", false ); + } + + /// + /// 加载下拉树标识列表 + /// + protected virtual void LoadKeys( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.LoadKeys, info.SafePropertyName, false ); + } + + /// + /// 加载密码类型 + /// + protected virtual void LoadPassword( Config config, ModelExpressionInfo info ) { + if ( info.IsPassword == false ) + return; + config.SetAttribute( UiConst.Type, "password", false ); + } + + /// + /// 加载必填项验证 + /// + protected virtual void LoadRequired( Config config, ModelExpressionInfo info ) { + if ( info.IsRequired == false ) + return; + config.SetAttribute( UiConst.Required, "true", false ); + config.SetAttribute( UiConst.RequiredMessage, info.RequiredMessage, false ); + } + + /// + /// 加载最小长度验证 + /// + protected virtual void LoadMinLength( Config config, ModelExpressionInfo info ) { + if ( info.MinLength == null ) + return; + config.SetAttribute( UiConst.MinLength, info.MinLength, false ); + config.SetAttribute( UiConst.MinLengthMessage, info.MinLengthMessage, false ); + } + + /// + /// 加载最大长度验证 + /// + protected virtual void LoadMaxLength( Config config, ModelExpressionInfo info ) { + if ( info.MaxLength == null ) + return; + config.SetAttribute( UiConst.MaxLength, info.MaxLength, false ); + config.SetAttribute( UiConst.MaxLengthMessage, info.MaxLengthMessage, false ); + } + + /// + /// 加载最小值验证 + /// + protected virtual void LoadMin( Config config, ModelExpressionInfo info ) { + if ( info.Min == null ) + return; + config.SetAttribute( UiConst.Min, info.Min, false ); + config.SetAttribute( UiConst.MinMessage, info.MinMessage, false ); + } + + /// + /// 加载最大值验证 + /// + protected virtual void LoadMax( Config config, ModelExpressionInfo info ) { + if ( info.Max == null ) + return; + config.SetAttribute( UiConst.Max, info.Max, false ); + config.SetAttribute( UiConst.MaxMessage, info.MaxMessage, false ); + } + + /// + /// 加载电子邮件验证 + /// + protected virtual void LoadEmail( Config config, ModelExpressionInfo info ) { + if ( info.IsEmail == false ) + return; + config.SetAttribute( UiConst.Type, InputType.Email, false ); + config.SetAttribute( UiConst.EmailMessage, info.EmailMessage, false ); + } + + /// + /// 加载正则表达式验证 + /// + protected virtual void LoadRegex( Config config, ModelExpressionInfo info ) { + if ( info.IsRegularExpression == false ) + return; + config.SetAttribute( UiConst.Pattern, info.Pattern, false ); + config.SetAttribute( UiConst.PatternMessage, info.RegularExpressionMessage, false ); + + } + /// + /// 加载手机号验证 + /// + protected virtual void LoadPhone( Config config, ModelExpressionInfo info ) { + if ( info.IsPhone == false ) + return; + config.SetAttribute( UiConst.IsInvalidPhone, "true", false ); + config.SetAttribute( UiConst.Pattern, ValidatePattern.MobilePhonePattern, false ); + config.SetAttribute( UiConst.PatternMessage, info.PhoneMessage, false ); + } + + /// + /// 加载身份证验证 + /// + protected virtual void LoadIdCard( Config config, ModelExpressionInfo info ) { + if ( info.IsIdCard == false ) + return; + config.SetAttribute( UiConst.IsInvalidIdCard, "true", false ); + config.SetAttribute( UiConst.Pattern, ValidatePattern.IdCardPattern, false ); + config.SetAttribute( UiConst.PatternMessage, info.IdCardMessage, false ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Inputs/Helpers/InputGroupShareService.cs b/src/Util.Ui.NgZorro/Components/Inputs/Helpers/InputGroupShareService.cs new file mode 100644 index 000000000..5542e617c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Inputs/Helpers/InputGroupShareService.cs @@ -0,0 +1,228 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Inputs.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Inputs.Helpers; + +/// +/// 输入框组合共享服务 +/// +public class InputGroupShareService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 输入框组合共享配置 + /// + private InputGroupShareConfig _shareConfig; + + /// + /// 初始化输入框组合共享服务 + /// + /// 配置 + public InputGroupShareService( Config config ) { + _config = config; + } + + /// + /// 设置nz-input-group已创建 + /// + public void Created() { + _shareConfig.InputGroupCreated = true; + } + + /// + /// 设置自动创建nz-input-group + /// + /// 是否自动创建 + public void AutoCreateInputGroup( bool value ) { + if ( _shareConfig.AutoCreateInputGroup == false ) + return; + _shareConfig.AutoCreateInputGroup = value; + } + + /// + /// 设置输入框标识 + /// + /// 输入框标识 + public void SetInputId( string id ) { + _shareConfig.InputId = id; + } + + /// + /// 设置样式类 + /// + /// 样式类 + public void SetClass( string value ) { + _shareConfig.Class = value; + } + + /// + /// 初始化输入框组合共享配置 + /// + public void Init() { + InitShareConfig(); + SetShareConfig(); + AutoCreateInputGroup(); + SetNgIf(); + } + + /// + /// 初始化 + /// + private void InitShareConfig() { + _shareConfig = GetShareConfig(); + _config.SetValueToItems( _shareConfig ); + } + + /// + /// 获取共享配置 + /// + private InputGroupShareConfig GetShareConfig() { + return _config.GetValueFromItems() ?? new InputGroupShareConfig(); + } + + /// + /// 设置共享配置 + /// + private void SetShareConfig() { + if ( _shareConfig.AddOnBefore.IsEmpty() ) + _shareConfig.AddOnBefore = _config.GetValue( UiConst.AddOnBefore ); + if ( _shareConfig.BindAddOnBefore.IsEmpty() ) + _shareConfig.BindAddOnBefore = _config.GetValue( AngularConst.BindAddOnBefore ); + if ( _shareConfig.AddOnAfter.IsEmpty() ) + _shareConfig.AddOnAfter = _config.GetValue( UiConst.AddOnAfter ); + if ( _shareConfig.BindAddOnAfter.IsEmpty() ) + _shareConfig.BindAddOnAfter = _config.GetValue( AngularConst.BindAddOnAfter ); + _shareConfig.AddOnBeforeIcon ??= _config.GetValue( UiConst.AddOnBeforeIcon ); + if ( _shareConfig.BindAddOnBeforeIcon.IsEmpty() ) + _shareConfig.BindAddOnBeforeIcon = _config.GetValue( AngularConst.BindAddOnBeforeIcon ); + _shareConfig.AddOnAfterIcon ??= _config.GetValue( UiConst.AddOnAfterIcon ); + if ( _shareConfig.BindAddOnAfterIcon.IsEmpty() ) + _shareConfig.BindAddOnAfterIcon = _config.GetValue( AngularConst.BindAddOnAfterIcon ); + if ( _shareConfig.Prefix.IsEmpty() ) + _shareConfig.Prefix = _config.GetValue( UiConst.Prefix ); + if ( _shareConfig.BindPrefix.IsEmpty() ) + _shareConfig.BindPrefix = _config.GetValue( AngularConst.BindPrefix ); + if ( _shareConfig.Suffix.IsEmpty() ) + _shareConfig.Suffix = _config.GetValue( UiConst.Suffix ); + if ( _shareConfig.BindSuffix.IsEmpty() ) + _shareConfig.BindSuffix = _config.GetValue( AngularConst.BindSuffix ); + _shareConfig.PrefixIcon ??= _config.GetValue( UiConst.PrefixIcon ); + if ( _shareConfig.BindPrefixIcon.IsEmpty() ) + _shareConfig.BindPrefixIcon = _config.GetValue( AngularConst.BindPrefixIcon ); + _shareConfig.SuffixIcon ??= _config.GetValue( UiConst.SuffixIcon ); + if ( _shareConfig.BindSuffixIcon.IsEmpty() ) + _shareConfig.BindSuffixIcon = _config.GetValue( AngularConst.BindSuffixIcon ); + _shareConfig.Size ??= _config.GetValue( UiConst.Size ); + if ( _shareConfig.BindSize.IsEmpty() ) + _shareConfig.BindSize = _config.GetValue( AngularConst.BindSize ); + _shareConfig.Status ??= _config.GetValue( UiConst.Status ); + if ( _shareConfig.BindStatus.IsEmpty() ) + _shareConfig.BindStatus = _config.GetValue( AngularConst.BindStatus ); + _shareConfig.AllowClear = IsAllowClear(); + _shareConfig.IsPassword = IsPassword(); + } + + /// + /// 是否允许清除 + /// + private bool IsAllowClear() { + if ( IsFormControl() ) + return false; + if ( _config.GetValue( AngularConst.NgModel ).IsEmpty() ) + return false; + var isAllowClear = _config.GetValue( UiConst.AllowClear ); + if ( isAllowClear == null ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableAllowClear ) + return true; + } + return isAllowClear.SafeValue(); + } + + /// + /// 是否表单控件 + /// + private bool IsFormControl() { + if ( _config.GetValue( UiConst.FormControl ).IsEmpty() == false ) + return true; + if ( _config.GetValue( UiConst.FormControlName ).IsEmpty() == false ) + return true; + if ( _config.GetValue( AngularConst.BindFormControlName ).IsEmpty() == false ) + return true; + return false; + } + + /// + /// 是否密码类型 + /// + private bool IsPassword() { + return _config.GetValue( UiConst.Type ) == InputType.Password; + } + + /// + /// 自动创建nz-input-group + /// + private void AutoCreateInputGroup() { + if ( IsAutoCreateInputGroup() == false ) + return; + AutoCreateInputGroup( true ); + } + + /// + /// 是否自动创建nz-input-group + /// + private bool IsAutoCreateInputGroup() { + if ( _shareConfig.AutoCreateInputGroup == false ) + return false; + if ( string.IsNullOrWhiteSpace( _shareConfig.AddOnBefore ) == false ) + return true; + if ( string.IsNullOrWhiteSpace( _shareConfig.BindAddOnBefore ) == false ) + return true; + if ( string.IsNullOrWhiteSpace( _shareConfig.AddOnAfter ) == false ) + return true; + if ( string.IsNullOrWhiteSpace( _shareConfig.BindAddOnAfter ) == false ) + return true; + if ( _shareConfig.AddOnBeforeIcon != null ) + return true; + if ( string.IsNullOrWhiteSpace( _shareConfig.BindAddOnBeforeIcon ) == false ) + return true; + if ( _shareConfig.AddOnAfterIcon != null ) + return true; + if ( string.IsNullOrWhiteSpace( _shareConfig.BindAddOnAfterIcon ) == false ) + return true; + if ( string.IsNullOrWhiteSpace( _shareConfig.Prefix ) == false ) + return true; + if ( string.IsNullOrWhiteSpace( _shareConfig.BindPrefix ) == false ) + return true; + if ( string.IsNullOrWhiteSpace( _shareConfig.Suffix ) == false ) + return true; + if ( string.IsNullOrWhiteSpace( _shareConfig.BindSuffix ) == false ) + return true; + if ( _shareConfig.PrefixIcon != null ) + return true; + if ( string.IsNullOrWhiteSpace( _shareConfig.BindPrefixIcon ) == false ) + return true; + if ( _shareConfig.SuffixIcon != null ) + return true; + if ( string.IsNullOrWhiteSpace( _shareConfig.BindSuffixIcon ) == false ) + return true; + return false; + } + + /// + /// 设置ngIf* + /// + private void SetNgIf() { + var value = _config.GetValueFromAttributes( AngularConst.NgIf ); + if ( value.IsEmpty() ) + return; + if ( _shareConfig.AllowClear == false ) + return; + _shareConfig.NgIf = value; + _config.RemoveAttribute( AngularConst.NgIf ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Inputs/Helpers/InputService.cs b/src/Util.Ui.NgZorro/Components/Inputs/Helpers/InputService.cs new file mode 100644 index 000000000..45800791d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Inputs/Helpers/InputService.cs @@ -0,0 +1,89 @@ +using Util.Ui.NgZorro.Components.Forms.Helpers; + +namespace Util.Ui.NgZorro.Components.Inputs.Helpers; + +/// +/// 输入框服务 +/// +public class InputService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 输入框组样式类 + /// + private readonly string _inputGroupClass; + + /// + /// 初始化输入框服务 + /// + /// 配置 + /// 输入框组样式类 + public InputService( Config config, string inputGroupClass = null ) { + _config = config; + _inputGroupClass = inputGroupClass; + } + + /// + /// 初始化 + /// + public void Init() { + LoadExpression(); + InitValidationService(); + InitFormShareService(); + InitFormItemShareService(); + InitInputGroupShareService(); + } + + /// + /// 加载表达式 + /// + private void LoadExpression() { + var loader = new InputExpressionLoader(); + loader.Load( _config ); + } + + /// + /// 初始化验证服务 + /// + private void InitValidationService() { + var service = new ValidationService( _config ); + service.Init(); + } + + /// + /// 初始化表单共享服务 + /// + private void InitFormShareService() { + var service = new FormShareService( _config ); + service.Init(); + } + + /// + /// 初始化表单项共享服务 + /// + private void InitFormItemShareService() { + var service = new FormItemShareService( _config ); + service.Init(); + service.InitId(); + } + + /// + /// 初始化输入框组合共享服务 + /// + private void InitInputGroupShareService() { + var service = new InputGroupShareService( _config ); + service.Init(); + service.SetInputId( GetId() ); + service.SetClass( _inputGroupClass ); + } + + /// + /// 获取输入框标识 + /// + private string GetId() { + var id = _config.GetValue( UiConst.Id ); + return id.IsEmpty() ? Util.Helpers.Id.Create() : id; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Inputs/Helpers/RowsModel.cs b/src/Util.Ui.NgZorro/Components/Inputs/Helpers/RowsModel.cs new file mode 100644 index 000000000..229a4cb63 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Inputs/Helpers/RowsModel.cs @@ -0,0 +1,28 @@ +using Util.Helpers; + +namespace Util.Ui.NgZorro.Components.Inputs.Helpers; + +/// +/// 行高模型 +/// +public class RowsModel { + /// + /// 最小行数 + /// + public int? MinRows { get; set; } + /// + /// 最大行数 + /// + public int? MaxRows { get; set; } + + /// + /// 输出json + /// + public string ToJson() { + var result = Json.ToJson( this, new JsonSerializerOptions { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }, true ); + return result == "{}" ? null : result; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Inputs/InputGroupTagHelper.cs b/src/Util.Ui.NgZorro/Components/Inputs/InputGroupTagHelper.cs new file mode 100644 index 000000000..981512d2d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Inputs/InputGroupTagHelper.cs @@ -0,0 +1,126 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Forms.Helpers; +using Util.Ui.NgZorro.Components.Inputs.Helpers; +using Util.Ui.NgZorro.Components.Inputs.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Inputs; + +/// +/// 输入框组合,生成的标签为<nz-input-group></nz-input-group> +/// +[HtmlTargetElement( "util-input-group" )] +public class InputGroupTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// nzAddOnBefore,设置前置标签 + /// + public string AddOnBefore { get; set; } + /// + /// [nzAddOnBefore],设置前置标签 + /// + public string BindAddOnBefore { get; set; } + /// + /// nzAddOnAfter,设置后置标签 + /// + public string AddOnAfter { get; set; } + /// + /// [nzAddOnAfter],设置后置标签 + /// + public string BindAddOnAfter { get; set; } + /// + /// nzAddOnBeforeIcon,设置前置图标 + /// + public AntDesignIcon AddOnBeforeIcon { get; set; } + /// + /// [nzAddOnBeforeIcon],设置前置图标 + /// + public string BindAddOnBeforeIcon { get; set; } + /// + /// nzAddOnAfterIcon,设置后置图标 + /// + public AntDesignIcon AddOnAfterIcon { get; set; } + /// + /// [nzAddOnAfterIcon],设置后置图标 + /// + public string BindAddOnAfterIcon { get; set; } + /// + /// nzPrefix,设置前缀 + /// + public string Prefix { get; set; } + /// + /// [nzPrefix],设置前缀 + /// + public string BindPrefix { get; set; } + /// + /// nzSuffix,设置后缀 + /// + public string Suffix { get; set; } + /// + /// [nzSuffix],设置后缀 + /// + public string BindSuffix { get; set; } + /// + /// nzPrefixIcon,设置前缀图标 + /// + public AntDesignIcon PrefixIcon { get; set; } + /// + /// [nzPrefixIcon],设置前缀图标 + /// + public string BindPrefixIcon { get; set; } + /// + /// nzSuffixIcon,设置后缀图标 + /// + public AntDesignIcon SuffixIcon { get; set; } + /// + /// [nzSuffixIcon],设置后缀图标 + /// + public string BindSuffixIcon { get; set; } + /// + /// [nzSearch],是否用于搜索, 默认值: false + /// + public string Search { get; set; } + /// + /// nzSize,设置输入框大小, 可选值: 'default' | 'small' | 'large' + /// + public InputSize Size { get; set; } + /// + /// [nzSize],设置输入框大小, 可选值: 'default' | 'small' | 'large' + /// + public string BindSize { get; set; } + /// + /// [nzCompact],是否紧凑模式, 默认值: false + /// + public string Compact { get; set; } + /// + /// nzStatus,校验状态, 可选值: 'error' | 'warning' + /// + public FormControlStatus Status { get; set; } + /// + /// [nzStatus],校验状态, 可选值: 'error' | 'warning' + /// + public string BindStatus { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var formItemShareService = new FormItemShareService( _config ); + formItemShareService.Init(); + formItemShareService.InitId(); + var inputGroupShareService = new InputGroupShareService( _config ); + inputGroupShareService.Init(); + inputGroupShareService.AutoCreateInputGroup( false ); + inputGroupShareService.Created(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new InputGroupRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Inputs/InputTagHelper.cs b/src/Util.Ui.NgZorro/Components/Inputs/InputTagHelper.cs new file mode 100644 index 000000000..534a17640 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Inputs/InputTagHelper.cs @@ -0,0 +1,216 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Inputs.Helpers; +using Util.Ui.NgZorro.Components.Inputs.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Inputs; + +/// +/// 输入框,生成的标签为<input nz-input /> +/// +[HtmlTargetElement( "util-input",TagStructure = TagStructure.WithoutEndTag)] +public class InputTagHelper : FormControlTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 扩展属性 [x-required-extend],是否必填项, 类型: boolean, 默认值: false + /// + public string Required { get; set; } + /// + /// 扩展属性 requiredMessage,必填项验证消息 + /// + public string RequiredMessage { get; set; } + /// + /// 扩展属性 [requiredMessage],必填项验证消息 + /// + public string BindRequiredMessage { get; set; } + /// + /// [minlength],最小长度验证 + /// + public string MinLength { get; set; } + /// + /// 扩展属性 minLengthMessage,最小长度验证消息 + /// + public string MinLengthMessage { get; set; } + /// + /// 扩展属性 [minLengthMessage],最小长度验证消息 + /// + public string BindMinLengthMessage { get; set; } + /// + /// [maxlength],最大长度验证 + /// + public string MaxLength { get; set; } + /// + /// 扩展属性 emailMessage,电子邮件验证消息 + /// + public string EmailMessage { get; set; } + /// + /// 扩展属性 [emailMessage],电子邮件验证消息 + /// + public string BindEmailMessage { get; set; } + /// + /// pattern,正则表达式验证 + /// + public string Pattern { get; set; } + /// + /// [pattern],正则表达式验证 + /// + public string BindPattern { get; set; } + /// + /// 扩展属性 patternMessage,正则表达式错误消息 + /// + public string PatternMessage { get; set; } + /// + /// 扩展属性 [patternMessage],正则表达式错误消息 + /// + public string BindPatternMessage { get; set; } + /// + /// placeholder,占位符提示信息 + /// + public string Placeholder { get; set; } + /// + /// [placeholder],占位符提示信息 + /// + public string BindPlaceholder { get; set; } + /// + /// [disabled],是否禁用, 默认值: false + /// + public string Disabled { get; set; } + /// + /// [readOnly],只读, 默认值: false + /// + public string Readonly { get; set; } + /// + /// nzSize,输入框大小, 可选值: 'default' | 'small' | 'large' + /// + public InputSize Size { get; set; } + /// + /// [nzSize],输入框大小, 可选值: 'default' | 'small' | 'large' + /// + public string BindSize { get; set; } + /// + /// nzStatus,校验状态, 可选值: 'error' | 'warning' + /// + public FormControlStatus Status { get; set; } + /// + /// [nzStatus],校验状态, 可选值: 'error' | 'warning' + /// + public string BindStatus { get; set; } + /// + /// type,输入框类型, 可选值: 'text' | 'password' | 'number' | 'email' + /// + public InputType Type { get; set; } + /// + /// [type],输入框类型, 可选值: 'text' | 'password' | 'number' | 'email' + /// + public string BindType { get; set; } + /// + /// nzAddOnBefore,设置前置标签,自动创建<nz-input-group></nz-input-group> + /// + public string AddOnBefore { get; set; } + /// + /// [nzAddOnBefore],设置前置标签,自动创建<nz-input-group></nz-input-group> + /// + public string BindAddOnBefore { get; set; } + /// + /// nzAddOnAfter,设置后置标签,自动创建<nz-input-group></nz-input-group> + /// + public string AddOnAfter { get; set; } + /// + /// [nzAddOnAfter],设置后置标签,自动创建<nz-input-group></nz-input-group> + /// + public string BindAddOnAfter { get; set; } + /// + /// nzAddOnBeforeIcon,设置前置图标,自动创建<nz-input-group></nz-input-group> + /// + public AntDesignIcon AddOnBeforeIcon { get; set; } + /// + /// [nzAddOnBeforeIcon],设置前置图标,自动创建<nz-input-group></nz-input-group> + /// + public string BindAddOnBeforeIcon { get; set; } + /// + /// nzAddOnAfterIcon,设置后置图标,自动创建<nz-input-group></nz-input-group> + /// + public AntDesignIcon AddOnAfterIcon { get; set; } + /// + /// [nzAddOnAfterIcon],设置后置图标,自动创建<nz-input-group></nz-input-group> + /// + public string BindAddOnAfterIcon { get; set; } + /// + /// nzPrefix,设置前缀,自动创建<nz-input-group></nz-input-group> + /// + public string Prefix { get; set; } + /// + /// [nzPrefix],设置前缀,自动创建<nz-input-group></nz-input-group> + /// + public string BindPrefix { get; set; } + /// + /// nzSuffix,设置后缀,自动创建<nz-input-group></nz-input-group> + /// + public string Suffix { get; set; } + /// + /// [nzSuffix],设置后缀,自动创建<nz-input-group></nz-input-group> + /// + public string BindSuffix { get; set; } + /// + /// nzPrefixIcon,设置前缀图标,自动创建<nz-input-group></nz-input-group> + /// + public AntDesignIcon PrefixIcon { get; set; } + /// + /// [nzPrefixIcon],设置前缀图标,自动创建<nz-input-group></nz-input-group> + /// + public string BindPrefixIcon { get; set; } + /// + /// nzSuffixIcon,设置后缀图标,自动创建<nz-input-group></nz-input-group> + /// + public AntDesignIcon SuffixIcon { get; set; } + /// + /// [nzSuffixIcon],设置后缀图标,自动创建<nz-input-group></nz-input-group> + /// + public string BindSuffixIcon { get; set; } + /// + /// 扩展属性, 是否允许清除内容,设置为 true 将显示清除图标,注意:必须设置ngModel + /// + public bool AllowClear { get; set; } + /// + /// [nzAutocomplete],设置自动完成组件的引用 + /// + public string Autocomplete { get; set; } + /// + /// 扩展属性,自动完成是否启用服务端搜索关键字,当在文本框中输入时, 将输入值以查询参数 keyword 发送到 Api,默认值:false + /// + public bool AutocompleteSearchKeyword { get; set; } + /// + /// (input),输入事件 + /// + public string OnInput { get; set; } + /// + /// (blur),失去焦点事件 + /// + public string OnBlur { get; set; } + /// + /// (keyup.enter),按下回车键事件,按键被松开时触发 + /// + public string OnKeyupEnter { get; set; } + /// + /// (keydown.enter),按下回车键事件,按键被按下时触发 + /// + public string OnKeydownEnter { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new InputService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new InputRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Inputs/Renders/InputGroupRender.cs b/src/Util.Ui.NgZorro/Components/Inputs/Renders/InputGroupRender.cs new file mode 100644 index 000000000..9b0adb9d1 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Inputs/Renders/InputGroupRender.cs @@ -0,0 +1,76 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Containers.Builders; +using Util.Ui.NgZorro.Components.Inputs.Builders; +using Util.Ui.NgZorro.Components.Inputs.Configs; + +namespace Util.Ui.NgZorro.Components.Inputs.Renders; + +/// +/// 输入框组合渲染器 +/// +public class InputGroupRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化输入框组合渲染器 + /// + /// 配置 + public InputGroupRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var container = GetContainerBuilder(); + var builder = new InputGroupBuilder( _config ); + builder.Config(); + _config.Content.AppendTo( builder ); + container.AppendContent( builder ); + formControlBuilder.AppendContent( container ); + } + + /// + /// 获取容器生成器 + /// + public TagBuilder GetContainerBuilder() { + TagBuilder container = new EmptyContainerTagBuilder(); + var ngIf = _config.GetValue( AngularConst.NgIf ); + var shareConfig = GetInputGroupShareConfig(); + if ( ngIf.IsEmpty() ) + ngIf = shareConfig.NgIf; + if ( ngIf.IsEmpty() ) + return container; + _config.RemoveAttribute( AngularConst.NgIf ); + container = new ContainerBuilder( _config ); + container.NgIf( ngIf ); + return container; + } + + /// + /// 获取输入框组合共享配置 + /// + private InputGroupShareConfig GetInputGroupShareConfig() { + return _config.GetValueFromItems() ?? new InputGroupShareConfig(); + } + + /// + /// 是否添加验证模板 + /// + protected override bool IsAppendValidationTempalte() { + return false; + } + + /// + public override IHtmlContent Clone() { + return new InputGroupRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Inputs/Renders/InputRender.cs b/src/Util.Ui.NgZorro/Components/Inputs/Renders/InputRender.cs new file mode 100644 index 000000000..7b34190af --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Inputs/Renders/InputRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Inputs.Builders; +using Util.Ui.NgZorro.Configs; + +namespace Util.Ui.NgZorro.Components.Inputs.Renders; + +/// +/// 输入框渲染器 +/// +public class InputRender : InputRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化输入框渲染器 + /// + /// 配置 + public InputRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 获取输入框标签生成器 + /// + protected override TagBuilder GetInputBuilder() { + var builder = new InputBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new InputRender( _config.CopyRemoveAttributes() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Inputs/Renders/InputRenderBase.cs b/src/Util.Ui.NgZorro/Components/Inputs/Renders/InputRenderBase.cs new file mode 100644 index 000000000..34626ec05 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Inputs/Renders/InputRenderBase.cs @@ -0,0 +1,220 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Containers.Builders; +using Util.Ui.NgZorro.Components.Inputs.Builders; +using Util.Ui.NgZorro.Components.Inputs.Configs; +using Util.Ui.NgZorro.Components.Mentions.Configs; +using Util.Ui.NgZorro.Configs; + +namespace Util.Ui.NgZorro.Components.Inputs.Renders; + +/// +/// 输入框渲染器基类 +/// +public abstract class InputRenderBase : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化输入框渲染器基类 + /// + /// 配置 + protected InputRenderBase( Config config ) : base( config ) { + _config = config; + } + + /// + /// 初始化 + /// + protected override void Init() { + SetControlId(); + } + + /// + /// 设置控件Id + /// + protected void SetControlId() { + if ( ShareConfig.AutoNzFor != true ) + return; + _config.SetAttribute( AngularConst.RawId, ShareConfig.ControlId ); + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var container = GetInputGroupContainerBuilder(); + var inputGroup = GetInputGroup(); + var input = GetInput(); + inputGroup.AppendContent( input ); + container.AppendContent( inputGroup ); + formControlBuilder.AppendContent( container ); + } + + /// + /// 获取容器生成器 + /// + public TagBuilder GetInputGroupContainerBuilder() { + TagBuilder container = new EmptyContainerTagBuilder(); + var ngIf = _config.GetValue( AngularConst.NgIf ); + var shareConfig = GetInputGroupShareConfig(); + if ( ngIf.IsEmpty() ) + ngIf = shareConfig.NgIf; + if ( ngIf.IsEmpty() ) + return container; + _config.RemoveAttribute( AngularConst.NgIf ); + container = new ContainerBuilder( _config ); + container.NgIf( ngIf ); + return container; + } + + /// + /// 是否允许清除 + /// + private bool IsAllowClear() { + var shareConfig = GetInputGroupShareConfig(); + return shareConfig.AllowClear; + } + + /// + /// 获取输入框组合共享配置 + /// + private InputGroupShareConfig GetInputGroupShareConfig() { + return _config.GetValueFromItems() ?? new InputGroupShareConfig(); + } + + /// + /// 是否密码类型 + /// + private bool IsPassword() { + var shareConfig = GetInputGroupShareConfig(); + return shareConfig.IsPassword; + } + + /// + /// 获取输入框组合 + /// + private TagBuilder GetInputGroup() { + if ( NeedCreateInputGroup() ) { + var shareConfig = GetInputGroupShareConfig(); + shareConfig.InputGroupCreated = true; + return GetInputGroupBuilder(); + } + return new EmptyContainerTagBuilder(); + } + + /// + /// 是否需要创建输入框组合 + /// + protected virtual bool NeedCreateInputGroup() { + var shareConfig = GetInputGroupShareConfig(); + if ( shareConfig.AutoCreateInputGroup == false ) + return false; + if ( shareConfig.AutoCreateInputGroup == true ) + return true; + if ( IsAllowClear() ) + return true; + if ( IsPassword() ) + return true; + return false; + } + + /// + /// 获取输入框组合标签生成器 + /// + private TagBuilder GetInputGroupBuilder() { + var builder = new InputGroupBuilder( _config.CopyRemoveAttributes() ); + builder.Config(); + return builder; + } + + /// + /// 获取输入框 + /// + private TagBuilder GetInput() { + var builder = GetInputBuilder(); + ExportNgModel( builder ); + AddInputExtendDirective( builder ); + AddPasswordType( builder ); + ConfigMentionTrigger( builder ); + return builder; + } + + /// + /// 获取输入框标签生成器 + /// + protected abstract TagBuilder GetInputBuilder(); + + /// + /// 导出ngModel + /// + public void ExportNgModel( TagBuilder builder ) { + if ( NeedExportNgModel() == false ) + return; + builder.Attribute( $"#{GetNgModelId()}", "ngModel" ); + } + + /// + /// 获取ngModel引用变量名称 + /// + protected string GetNgModelId() { + var shareConfig = GetInputGroupShareConfig(); + return $"model_{shareConfig.InputId}"; + } + + /// + /// 是否需要导出ngModel + /// + private bool NeedExportNgModel() { + var shareConfig = GetInputGroupShareConfig(); + if ( shareConfig.Suffix.IsEmpty() == false || shareConfig.BindSuffix.IsEmpty() == false ) + return false; + if ( IsAllowClear() ) + return true; + return false; + } + + /// + /// 添加输入框扩展指令 + /// + private void AddInputExtendDirective( TagBuilder builder ) { + if ( NeedAddInputExtendDirective() == false ) + return; + var shareConfig = GetInputGroupShareConfig(); + builder.Attribute( "x-input-extend" ); + builder.Attribute( $"#xi_{shareConfig.InputId}", "xInputExtend" ); + } + + /// + /// 是否需要添加输入框扩展指令 + /// + private bool NeedAddInputExtendDirective() { + if ( IsPassword() ) + return true; + return false; + } + + /// + /// 添加密码类型 + /// + private void AddPasswordType( TagBuilder builder ) { + if ( IsPassword() == false ) + return; + var shareConfig = GetInputGroupShareConfig(); + builder.Attribute( "[type]", $"xi_{shareConfig.InputId}.passwordVisible?'text':'password'" ); + } + + /// + /// 配置提及触发元素 + /// + private void ConfigMentionTrigger( TagBuilder builder ) { + var shareConfig = _config.GetValueFromItems(); + if ( shareConfig == null ) + return; + builder.Attribute( "nzMentionTrigger" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Inputs/Renders/TextareaCountRender.cs b/src/Util.Ui.NgZorro/Components/Inputs/Renders/TextareaCountRender.cs new file mode 100644 index 000000000..47e8e239c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Inputs/Renders/TextareaCountRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Inputs.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Inputs.Renders; + +/// +/// 文本域计数渲染器 +/// +public class TextareaCountRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化文本域计数渲染器 + /// + /// 配置 + public TextareaCountRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TextareaCountBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TextareaCountRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Inputs/Renders/TextareaRender.cs b/src/Util.Ui.NgZorro/Components/Inputs/Renders/TextareaRender.cs new file mode 100644 index 000000000..b1414cb10 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Inputs/Renders/TextareaRender.cs @@ -0,0 +1,36 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Inputs.Builders; + +namespace Util.Ui.NgZorro.Components.Inputs.Renders; + +/// +/// 文本域渲染器 +/// +public class TextareaRender : InputRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化文本域渲染器 + /// + /// 配置 + public TextareaRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 获取输入框标签生成器 + /// + protected override TagBuilder GetInputBuilder() { + var builder = new TextareaBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TextareaRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Inputs/TextareaCountTagHelper.cs b/src/Util.Ui.NgZorro/Components/Inputs/TextareaCountTagHelper.cs new file mode 100644 index 000000000..8bf9f036f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Inputs/TextareaCountTagHelper.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Inputs.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Inputs; + +/// +/// 文本域计数,生成的标签为<nz-textarea-count></nz-textarea-count> +/// +[HtmlTargetElement( "util-textarea-count" )] +public class TextareaCountTagHelper : AngularTagHelperBase { + /// + /// [nzMaxCharacterCount],文本域数字提示显示的最大值 + /// + public string MaxCharacterCount { get; set; } + /// + /// [nzComputeCharacterCount],文本域数字提示最大值计算函数 + /// + public string ComputeCharacterCount { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new TextareaCountRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Inputs/TextareaTagHelper.cs b/src/Util.Ui.NgZorro/Components/Inputs/TextareaTagHelper.cs new file mode 100644 index 000000000..9c89283d2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Inputs/TextareaTagHelper.cs @@ -0,0 +1,136 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Inputs.Helpers; +using Util.Ui.NgZorro.Components.Inputs.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Inputs; + +/// +/// 多行文本域,生成的标签为<textarea nz-input></textarea> +/// +[HtmlTargetElement( "util-textarea" )] +public class TextareaTagHelper : FormControlTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 扩展属性 [x-required-extend],是否必填项, 类型: boolean, 默认值: false + /// + public string Required { get; set; } + /// + /// 扩展属性 requiredMessage,必填项验证消息 + /// + public string RequiredMessage { get; set; } + /// + /// 扩展属性 [requiredMessage],必填项验证消息 + /// + public string BindRequiredMessage { get; set; } + /// + /// [minlength],最小长度验证 + /// + public string MinLength { get; set; } + /// + /// 扩展属性 minLengthMessage,最小长度验证消息 + /// + public string MinLengthMessage { get; set; } + /// + /// 扩展属性 [minLengthMessage],最小长度验证消息 + /// + public string BindMinLengthMessage { get; set; } + /// + /// [maxlength],最大长度验证 + /// + public string MaxLength { get; set; } + /// + /// [disabled],是否禁用, 默认值: false + /// + public string Disabled { get; set; } + /// + /// [readOnly],只读, 默认值: false + /// + public string Readonly { get; set; } + /// + /// placeholder,占位符提示信息 + /// + public string Placeholder { get; set; } + /// + /// [placeholder],占位符提示信息 + /// + public string BindPlaceholder { get; set; } + /// + /// [rows],行数 + /// + public string Rows { get; set; } + /// + /// [cols],列数 + /// + public string Columns { get; set; } + /// + /// [nzAutosize],自适应内容高度,可设置为布尔值,或对象形式{ minRows: number, maxRows: number },范例: { minRows: 2, maxRows: 6 } + /// + public string Autosize { get; set; } + /// + /// [nzAutosize],最小行数,设置为 { minRows: number } + /// + public int MinRows { get; set; } + /// + /// [nzAutosize],最大行数,设置为 { maxRows: number } + /// + public int MaxRows { get; set; } + /// + /// nzSize,设置输入框大小, 可选值: 'default' | 'small' | 'large' + /// + public InputSize Size { get; set; } + /// + /// [nzSize],设置输入框大小, 可选值: 'default' | 'small' | 'large' + /// + public string BindSize { get; set; } + /// + /// 扩展属性, 是否允许清除内容,设置为 true 将显示清除图标,注意:必须设置ngModel + /// + public bool AllowClear { get; set; } + /// + /// [nzAutocomplete],设置自动完成组件的引用 + /// + public string Autocomplete { get; set; } + /// + /// 扩展属性,自动完成是否启用服务端搜索关键字,在搜索框输入时,设置查询参数的Keyword属性并发送请求,默认值:false + /// + public bool AutocompleteSearchKeyword { get; set; } + /// + /// (input),输入事件 + /// + public string OnInput { get; set; } + /// + /// (blur),失去焦点事件 + /// + public string OnBlur { get; set; } + /// + /// (keyup.enter),按下回车键事件,按键被松开时触发 + /// + public string OnKeyupEnter { get; set; } + /// + /// (keydown.enter),按下回车键事件,按键被按下时触发 + /// + public string OnKeydownEnter { get; set; } + + /// + /// 渲染前操作 + /// + /// 上下文 + /// 输出 + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new InputService( _config, "ant-input-affix-wrapper-textarea-with-clear-btn" ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new TextareaRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Label/Builders/LabelBuilder.cs b/src/Util.Ui.NgZorro/Components/Label/Builders/LabelBuilder.cs new file mode 100644 index 000000000..1156ce30f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Label/Builders/LabelBuilder.cs @@ -0,0 +1,59 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Label.Builders; + +/// +/// 标签生成器 +/// +public class LabelBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化标签生成器 + /// + /// 配置 + public LabelBuilder( Config config ) : base( config, "span" ) { + _config = config; + } + + /// + /// 配置文本 + /// + public LabelBuilder Text() { + return SetText( _config.GetValue( UiConst.Text ) ); + } + + /// + /// 配置标题 + /// + public LabelBuilder Title() { + return SetText( _config.GetValue( UiConst.Title ) ); + } + + /// + /// 配置内容 + /// + private LabelBuilder SetText( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( value.IsEmpty() ) + return this; + if ( options.EnableI18n ) { + this.AppendContentByI18n( value ); + return this; + } + AppendContent( value ); + return this; + } + + /// + public override void Config() { + base.Config(); + Text().Title(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Label/LabelTagHelper.cs b/src/Util.Ui.NgZorro/Components/Label/LabelTagHelper.cs new file mode 100644 index 000000000..43fa0ef00 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Label/LabelTagHelper.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Display.Helpers; +using Util.Ui.NgZorro.Components.Label.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Label; + +/// +/// 标签,生成的标签为<span></span> +/// +[HtmlTargetElement( "util-label" )] +public class LabelTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 内容文本,支持i18n + /// + public string Text { get; set; } + /// + /// 属性表达式 + /// + public ModelExpression For { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + LoadExpression(); + } + + /// + /// 加载表达式 + /// + private void LoadExpression() { + var loader = new DisplayExpressionLoader(); + loader.Load( _config ); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new LabelRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Label/Renders/LabelRender.cs b/src/Util.Ui.NgZorro/Components/Label/Renders/LabelRender.cs new file mode 100644 index 000000000..1c7ff5b66 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Label/Renders/LabelRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Label.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Label.Renders; + +/// +/// 标签渲染器 +/// +public class LabelRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化标签渲染器 + /// + /// 配置 + public LabelRender( Config config ) { + _config = config ?? throw new ArgumentNullException( nameof( config ) ); + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new LabelBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new LabelRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Layouts/Builders/ContentBuilder.cs b/src/Util.Ui.NgZorro/Components/Layouts/Builders/ContentBuilder.cs new file mode 100644 index 000000000..5d995aadf --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Layouts/Builders/ContentBuilder.cs @@ -0,0 +1,16 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Layouts.Builders; + +/// +/// 内容区布局标签生成器 +/// +public class ContentBuilder : AngularTagBuilder { + /// + /// 初始化内容区布局标签生成器 + /// + /// 配置 + public ContentBuilder( Config config ) : base( config,"nz-content" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Layouts/Builders/FooterBuilder.cs b/src/Util.Ui.NgZorro/Components/Layouts/Builders/FooterBuilder.cs new file mode 100644 index 000000000..45a226671 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Layouts/Builders/FooterBuilder.cs @@ -0,0 +1,16 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Layouts.Builders; + +/// +/// 底部布局标签生成器 +/// +public class FooterBuilder : AngularTagBuilder { + /// + /// 初始化底部布局标签生成器 + /// + /// 配置 + public FooterBuilder( Config config ) : base( config,"nz-footer" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Layouts/Builders/HeaderBuilder.cs b/src/Util.Ui.NgZorro/Components/Layouts/Builders/HeaderBuilder.cs new file mode 100644 index 000000000..69f8e3ebf --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Layouts/Builders/HeaderBuilder.cs @@ -0,0 +1,16 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Layouts.Builders; + +/// +/// 顶部布局标签生成器 +/// +public class HeaderBuilder : AngularTagBuilder { + /// + /// 初始化顶部布局标签生成器 + /// + /// 配置 + public HeaderBuilder( Config config ) : base( config,"nz-header" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Layouts/Builders/LayoutBuilder.cs b/src/Util.Ui.NgZorro/Components/Layouts/Builders/LayoutBuilder.cs new file mode 100644 index 000000000..35f2dba46 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Layouts/Builders/LayoutBuilder.cs @@ -0,0 +1,16 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Layouts.Builders; + +/// +/// 布局标签生成器 +/// +public class LayoutBuilder : AngularTagBuilder { + /// + /// 初始化布局标签生成器 + /// + /// 配置 + public LayoutBuilder( Config config ) : base( config,"nz-layout" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Layouts/Builders/SiderBuilder.cs b/src/Util.Ui.NgZorro/Components/Layouts/Builders/SiderBuilder.cs new file mode 100644 index 000000000..4b120b72f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Layouts/Builders/SiderBuilder.cs @@ -0,0 +1,110 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Layouts.Builders; + +/// +/// 侧边栏布局标签生成器 +/// +public class SiderBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化侧边栏布局标签生成器 + /// + /// 配置 + public SiderBuilder( Config config ) : base( config,"nz-sider" ) { + _config = config; + } + + /// + /// 配置宽度 + /// + public SiderBuilder Width() { + AttributeIfNotEmpty( "[nzWidth]", _config.GetValue( UiConst.Width ) ); + return this; + } + + /// + /// 配置主题 + /// + public SiderBuilder Theme() { + AttributeIfNotEmpty( "nzTheme", _config.GetValue( UiConst.Theme )?.Description() ); + AttributeIfNotEmpty( "[nzTheme]", _config.GetValue( AngularConst.BindTheme ) ); + return this; + } + + /// + /// 配置响应式布局断点 + /// + public SiderBuilder Breakpoint() { + AttributeIfNotEmpty( "nzBreakpoint", _config.GetValue( UiConst.Breakpoint )?.Description() ); + AttributeIfNotEmpty( "[nzBreakpoint]", _config.GetValue( AngularConst.BindBreakpoint ) ); + return this; + } + + /// + /// 配置收缩宽度 + /// + public SiderBuilder CollapsedWidth() { + AttributeIfNotEmpty( "[nzCollapsedWidth]", _config.GetValue( UiConst.CollapsedWidth ) ); + return this; + } + + /// + /// 配置可收缩 + /// + public SiderBuilder Collapsible() { + AttributeIfNotEmpty( "[nzCollapsible]", _config.GetValue( UiConst.Collapsible ) ); + return this; + } + + /// + /// 配置收缩状态 + /// + public SiderBuilder Collapsed() { + AttributeIfNotEmpty( "[nzCollapsed]", _config.GetValue( UiConst.Collapsed ) ); + AttributeIfNotEmpty( "[(nzCollapsed)]", _config.GetValue( AngularConst.BindonCollapsed ) ); + return this; + } + + /// + /// 配置翻转折叠提示箭头方向 + /// + public SiderBuilder ReverseArrow() { + AttributeIfNotEmpty( "[nzReverseArrow]", _config.GetValue( UiConst.ReverseArrow ) ); + return this; + } + + /// + /// 配置自定义触发器 + /// + public SiderBuilder Trigger() { + AttributeIfNotEmpty( "[nzTrigger]", _config.GetValue( UiConst.Trigger ) ); + AttributeIfNotEmpty( "[nzZeroTrigger]", _config.GetValue( UiConst.ZeroTrigger ) ); + return this; + } + + /// + /// 配置事件 + /// + public SiderBuilder Events() { + AttributeIfNotEmpty( "(nzCollapsedChange)", _config.GetValue( UiConst.OnCollapsedChange ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Width().Theme().Breakpoint().CollapsedWidth().Collapsible() + .Collapsed().ReverseArrow().Trigger() + .Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Layouts/ContentTagHelper.cs b/src/Util.Ui.NgZorro/Components/Layouts/ContentTagHelper.cs new file mode 100644 index 000000000..1b4b91082 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Layouts/ContentTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Layouts.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Layouts; + +/// +/// 内容区布局,生成的标签为<nz-content></nz-content>,它的父容器为<util-layout></util-layout>,即<nz-layout></nz-layout> +/// +[HtmlTargetElement( "util-content" )] +public class ContentTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ContentRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Layouts/FooterTagHelper.cs b/src/Util.Ui.NgZorro/Components/Layouts/FooterTagHelper.cs new file mode 100644 index 000000000..5a98f6f44 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Layouts/FooterTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Layouts.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Layouts; + +/// +/// 底部布局,生成的标签为<nz-footer></nz-footer>,它的父容器为<util-layout></util-layout>,即<nz-layout></nz-layout> +/// +[HtmlTargetElement( "util-footer" )] +public class FooterTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new FooterRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Layouts/HeaderTagHelper.cs b/src/Util.Ui.NgZorro/Components/Layouts/HeaderTagHelper.cs new file mode 100644 index 000000000..33f69d721 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Layouts/HeaderTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Layouts.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Layouts; + +/// +/// 顶部布局,生成的标签为<nz-header></nz-header>,它的父容器为<util-layout></util-layout>,即<nz-layout></nz-layout> +/// +[HtmlTargetElement( "util-header" )] +public class HeaderTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new HeaderRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Layouts/LayoutTagHelper.cs b/src/Util.Ui.NgZorro/Components/Layouts/LayoutTagHelper.cs new file mode 100644 index 000000000..3e1034def --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Layouts/LayoutTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Layouts.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Layouts; + +/// +/// 布局容器,生成的标签为<nz-layout></nz-layout> +/// +[HtmlTargetElement( "util-layout" )] +public class LayoutTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new LayoutRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Layouts/Renders/ContentRender.cs b/src/Util.Ui.NgZorro/Components/Layouts/Renders/ContentRender.cs new file mode 100644 index 000000000..fb5b3d197 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Layouts/Renders/ContentRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Layouts.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Layouts.Renders; + +/// +/// 内容区布局渲染器 +/// +public class ContentRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化内容区布局渲染器 + /// + /// 配置 + public ContentRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ContentBuilder(_config); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ContentRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Layouts/Renders/FooterRender.cs b/src/Util.Ui.NgZorro/Components/Layouts/Renders/FooterRender.cs new file mode 100644 index 000000000..a3fd19e6e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Layouts/Renders/FooterRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Layouts.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Layouts.Renders; + +/// +/// 底部布局渲染器 +/// +public class FooterRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化底部布局渲染器 + /// + /// 配置 + public FooterRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new FooterBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new FooterRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Layouts/Renders/HeaderRender.cs b/src/Util.Ui.NgZorro/Components/Layouts/Renders/HeaderRender.cs new file mode 100644 index 000000000..0371267ff --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Layouts/Renders/HeaderRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Layouts.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Layouts.Renders; + +/// +/// 顶部布局渲染器 +/// +public class HeaderRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化顶部布局渲染器 + /// + /// 配置 + public HeaderRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new HeaderBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new HeaderRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Layouts/Renders/LayoutRender.cs b/src/Util.Ui.NgZorro/Components/Layouts/Renders/LayoutRender.cs new file mode 100644 index 000000000..f3163fc79 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Layouts/Renders/LayoutRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Layouts.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Layouts.Renders; + +/// +/// 布局渲染器 +/// +public class LayoutRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化布局渲染器 + /// + /// 配置 + public LayoutRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new LayoutBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new LayoutRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Layouts/Renders/SiderRender.cs b/src/Util.Ui.NgZorro/Components/Layouts/Renders/SiderRender.cs new file mode 100644 index 000000000..beee9d663 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Layouts/Renders/SiderRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Layouts.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Layouts.Renders; + +/// +/// 侧边栏布局渲染器 +/// +public class SiderRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化侧边栏布局渲染器 + /// + /// 配置 + public SiderRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new SiderBuilder(_config); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new SiderRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Layouts/SiderTagHelper.cs b/src/Util.Ui.NgZorro/Components/Layouts/SiderTagHelper.cs new file mode 100644 index 000000000..0907f5f00 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Layouts/SiderTagHelper.cs @@ -0,0 +1,72 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Layouts.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Layouts; + +/// +/// 侧边栏布局,生成的标签为<nz-sider></nz-sider>,它的父容器为<util-layout></util-layout>,即<nz-layout></nz-layout> +/// +[HtmlTargetElement( "util-sider" )] +public class SiderTagHelper : AngularTagHelperBase { + /// + /// [nzWidth],宽度, 默认值: 200 + /// + public string Width { get; set; } + /// + /// nzTheme,主题颜色,可选值: 'light' | 'dark' + /// + public SiderTheme Theme { get; set; } + /// + /// [nzTheme],主题颜色,可选值: 'light' | 'dark' + /// + public string BindTheme { get; set; } + /// + /// nzBreakpoint,触发响应式布局的断点,窗口宽度小于断点触发宽度时,侧边栏缩小为 nzCollapsedWidth 宽度,若将 nzCollapsedWidth 设置为0,会出现特殊触发器,可选值: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl',断点触发宽度:{ xs: '480px', sm: '768px', md: '992px', lg: '1200px', xl: '1600px', xxl: '1600px' } + /// + public GridSize Breakpoint { get; set; } + /// + /// [nzBreakpoint],触发响应式布局的断点,窗口宽度小于断点触发宽度时,侧边栏缩小为 nzCollapsedWidth 宽度,若将 nzCollapsedWidth 设置为0,会出现特殊触发器,可选值: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl',断点触发宽度:{ xs: '480px', sm: '768px', md: '992px', lg: '1200px', xl: '1600px', xxl: '1600px' } + /// + public string BindBreakpoint { get; set; } + /// + /// [nzCollapsedWidth],收缩宽度,设置为 0 会出现特殊触发器, 默认值: 64 + /// + public string CollapsedWidth { get; set; } + /// + /// [nzCollapsible],是否可收缩, 默认值: false + /// + public string Collapsible { get; set; } + /// + /// [nzCollapsed],是否收起状态, 默认值: false + /// + public string Collapsed { get; set; } + /// + /// [(nzCollapsed)],是否收起状态, 默认值: false + /// + public string BindonCollapsed { get; set; } + /// + /// [nzReverseArrow],翻转折叠提示箭头的方向,当侧边栏在右边时使用, 默认值: false + /// + public string ReverseArrow { get; set; } + /// + /// [nzTrigger],自定义触发器,即收缩按钮,设置为 null 时隐藏触发器 + /// + public string Trigger { get; set; } + /// + /// [nzZeroTrigger],自定义 nzCollapsedWidth 为0时的特殊触发器 + /// + public string ZeroTrigger { get; set; } + /// + /// (nzCollapsedChange),展开收起状态变化事件,注意:自定义触发器不会触发该事件, 类型: EventEmitter<boolean> + /// + public string OnCollapsedChange { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new SiderRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Builders/ListBuilder.cs b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListBuilder.cs new file mode 100644 index 000000000..1d7737587 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListBuilder.cs @@ -0,0 +1,123 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Lists.Builders; + +/// +/// 列表标签生成器 +/// +public class ListBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表标签生成器 + /// + public ListBuilder( Config config ) : base( config, "nz-list" ) { + _config = config; + } + + /// + /// 配置是否显示边框 + /// + public ListBuilder Bordered() { + AttributeIfNotEmpty( "[nzBordered]", _config.GetValue( UiConst.Bordered ) ); + return this; + } + + /// + /// 配置列表底部 + /// + public ListBuilder Footer() { + AttributeIfNotEmpty( "nzFooter", _config.GetValue( UiConst.Footer ) ); + AttributeIfNotEmpty( "[nzFooter]", _config.GetValue( AngularConst.BindFooter ) ); + return this; + } + + /// + /// 配置列表头部 + /// + public ListBuilder Header() { + AttributeIfNotEmpty( "nzHeader", _config.GetValue( UiConst.Header ) ); + AttributeIfNotEmpty( "[nzHeader]", _config.GetValue( AngularConst.BindHeader ) ); + return this; + } + + /// + /// 配置列表项布局方式 + /// + public ListBuilder ItemLayout() { + AttributeIfNotEmpty( "nzItemLayout", _config.GetValue( UiConst.ItemLayout )?.Description() ); + AttributeIfNotEmpty( "[nzItemLayout]", _config.GetValue( AngularConst.BindItemLayout ) ); + return this; + } + + /// + /// 配置是否加载状态 + /// + public ListBuilder Loading() { + AttributeIfNotEmpty( "[nzLoading]", _config.GetValue( UiConst.Loading ) ); + return this; + } + + /// + /// 配置列表大小 + /// + public ListBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置是否显示分割线 + /// + public ListBuilder Split() { + AttributeIfNotEmpty( "[nzSplit]", _config.GetValue( UiConst.Split ) ); + return this; + } + + /// + /// 配置栅格 + /// + public ListBuilder Grid() { + var value = _config.GetValue( UiConst.Grid ); + if ( value.IsEmpty() ) + return this; + if ( value == "true" ) { + Attribute( "nzGrid" ); + return this; + } + Attribute( "[nzGrid]", value ); + return this; + } + + /// + /// 配置数据源 + /// + public ListBuilder DataSource() { + AttributeIfNotEmpty( "[nzDataSource]", _config.GetValue( UiConst.Datasource ) ); + return this; + } + + /// + /// 配置自定义项模板 + /// + public ListBuilder RenderItem() { + AttributeIfNotEmpty( "[nzRenderItem]", _config.GetValue( UiConst.RenderItem ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Bordered().Footer().Header().ItemLayout() + .Loading().Size().Split().Grid() + .DataSource().RenderItem(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Builders/ListEmptyBuilder.cs b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListEmptyBuilder.cs new file mode 100644 index 000000000..b22ab4e4e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListEmptyBuilder.cs @@ -0,0 +1,39 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; + +namespace Util.Ui.NgZorro.Components.Lists.Builders; + +/// +/// 列表空内容标签生成器 +/// +public class ListEmptyBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表空内容标签生成器 + /// + /// 配置 + public ListEmptyBuilder( Config config ) : base( config,"nz-list-empty" ) { + _config = config; + } + + /// + /// 配置空内容显示文本 + /// + public ListEmptyBuilder NoResult() { + AttributeIfNotEmpty( "nzNoResult", _config.GetValue( UiConst.NoResult ) ); + AttributeIfNotEmpty( "[nzNoResult]", _config.GetValue( AngularConst.BindNoResult ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + NoResult(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Builders/ListFooterBuilder.cs b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListFooterBuilder.cs new file mode 100644 index 000000000..7386e5a7e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListFooterBuilder.cs @@ -0,0 +1,22 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Lists.Builders; + +/// +/// 列表底部标签生成器 +/// +public class ListFooterBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表底部标签生成器 + /// + /// 配置 + public ListFooterBuilder( Config config ) : base( config,"nz-list-footer" ) { + _config = config; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Builders/ListHeaderBuilder.cs b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListHeaderBuilder.cs new file mode 100644 index 000000000..49b9d7e74 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListHeaderBuilder.cs @@ -0,0 +1,22 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Lists.Builders; + +/// +/// 列表头部标签生成器 +/// +public class ListHeaderBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表头部标签生成器 + /// + /// 配置 + public ListHeaderBuilder( Config config ) : base( config,"nz-list-header" ) { + _config = config; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemActionBuilder.cs b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemActionBuilder.cs new file mode 100644 index 000000000..bc66f88c6 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemActionBuilder.cs @@ -0,0 +1,22 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Lists.Builders; + +/// +/// 列表项操作项标签生成器 +/// +public class ListItemActionBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表项操作项标签生成器 + /// + /// 配置 + public ListItemActionBuilder( Config config ) : base( config,"nz-list-item-action" ) { + _config = config; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemActionsBuilder.cs b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemActionsBuilder.cs new file mode 100644 index 000000000..0407bf796 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemActionsBuilder.cs @@ -0,0 +1,23 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Lists.Builders; + +/// +/// 列表项操作标签生成器 +/// +public class ListItemActionsBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表项操作标签生成器 + /// + /// 配置 + public ListItemActionsBuilder( Config config ) : base( config,"ul" ) { + _config = config; + base.Attribute( "nz-list-item-actions" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemBuilder.cs b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemBuilder.cs new file mode 100644 index 000000000..0c647cb16 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemBuilder.cs @@ -0,0 +1,70 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; + +namespace Util.Ui.NgZorro.Components.Lists.Builders; + +/// +/// 列表项标签生成器 +/// +public class ListItemBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表项标签生成器 + /// + public ListItemBuilder( Config config ) : base( config,"nz-list-item" ) { + _config = config; + } + + /// + /// 配置列表项操作 + /// + public ListItemBuilder Actions() { + AttributeIfNotEmpty( "[nzActions]", _config.GetValue( UiConst.Actions ) ); + return this; + } + + /// + /// 配置列表项内容 + /// + public ListItemBuilder Content() { + AttributeIfNotEmpty( "nzContent", _config.GetValue( UiConst.Content ) ); + AttributeIfNotEmpty( "[nzContent]", _config.GetValue( AngularConst.BindContent ) ); + return this; + } + + /// + /// 配置列表项扩展 + /// + public ListItemBuilder Extra() { + AttributeIfNotEmpty( "[nzExtra]", _config.GetValue( UiConst.Extra ) ); + return this; + } + + /// + /// 配置是否非flex布局 + /// + public ListItemBuilder NoFlex() { + AttributeIfNotEmpty( "[nzNoFlex]", _config.GetValue( UiConst.NoFlex ) ); + return this; + } + + /// + /// 配置虚拟滚动循环 + /// + public ListItemBuilder VirtualFor() { + AttributeIfNotEmpty( "*cdkVirtualFor", _config.GetValue( UiConst.VirtualFor ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Actions().Content().Extra().NoFlex().VirtualFor(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemExtraBuilder.cs b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemExtraBuilder.cs new file mode 100644 index 000000000..6814a5c27 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemExtraBuilder.cs @@ -0,0 +1,22 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Lists.Builders; + +/// +/// 列表项扩展标签生成器 +/// +public class ListItemExtraBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表项扩展标签生成器 + /// + /// 配置 + public ListItemExtraBuilder( Config config ) : base( config,"nz-list-item-extra" ) { + _config = config; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemMetaAvatarBuilder.cs b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemMetaAvatarBuilder.cs new file mode 100644 index 000000000..e1370b08c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemMetaAvatarBuilder.cs @@ -0,0 +1,39 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; + +namespace Util.Ui.NgZorro.Components.Lists.Builders; + +/// +/// 列表项元信息头像标签生成器 +/// +public class ListItemMetaAvatarBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表项元信息头像标签生成器 + /// + /// 配置 + public ListItemMetaAvatarBuilder( Config config ) : base( config,"nz-list-item-meta-avatar" ) { + _config = config; + } + + /// + /// 配置头像地址 + /// + public ListItemMetaAvatarBuilder Src() { + AttributeIfNotEmpty( "nzSrc", _config.GetValue( UiConst.Src ) ); + AttributeIfNotEmpty( "[nzSrc]", _config.GetValue( AngularConst.BindSrc ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Src(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemMetaBuilder.cs b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemMetaBuilder.cs new file mode 100644 index 000000000..9f0232b51 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemMetaBuilder.cs @@ -0,0 +1,57 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Lists.Builders; + +/// +/// 列表项元信息标签生成器 +/// +public class ListItemMetaBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表项元信息标签生成器 + /// + public ListItemMetaBuilder( Config config ) : base( config,"nz-list-item-meta" ) { + _config = config; + } + + /// + /// 配置头像图标 + /// + public ListItemMetaBuilder Avatar() { + AttributeIfNotEmpty( "nzAvatar", _config.GetValue( UiConst.Avatar ) ); + AttributeIfNotEmpty( "[nzAvatar]", _config.GetValue( AngularConst.BindAvatar ) ); + return this; + } + + /// + /// 配置描述 + /// + public ListItemMetaBuilder Description() { + AttributeIfNotEmpty( "nzDescription", _config.GetValue( UiConst.Description ) ); + AttributeIfNotEmpty( "[nzDescription]", _config.GetValue( AngularConst.BindDescription ) ); + return this; + } + + /// + /// 配置标题 + /// + public ListItemMetaBuilder Title() { + AttributeIfNotEmpty( "nzTitle", _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Avatar().Description().Title(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemMetaDescriptionBuilder.cs b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemMetaDescriptionBuilder.cs new file mode 100644 index 000000000..b974ad244 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemMetaDescriptionBuilder.cs @@ -0,0 +1,21 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Lists.Builders; + +/// +/// 列表项元信息描述标签生成器 +/// +public class ListItemMetaDescriptionBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表项元信息描述标签生成器 + /// + public ListItemMetaDescriptionBuilder( Config config ) : base( config,"nz-list-item-meta-description" ) { + _config = config; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemMetaTitleBuilder.cs b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemMetaTitleBuilder.cs new file mode 100644 index 000000000..800eea534 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListItemMetaTitleBuilder.cs @@ -0,0 +1,22 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Lists.Builders; + +/// +/// 列表项元信息标题标签生成器 +/// +public class ListItemMetaTitleBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表项元信息标题标签生成器 + /// + /// 配置 + public ListItemMetaTitleBuilder( Config config ) : base( config, "nz-list-item-meta-title" ) { + _config = config; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Builders/ListLoadMoreBuilder.cs b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListLoadMoreBuilder.cs new file mode 100644 index 000000000..3e77e83bc --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListLoadMoreBuilder.cs @@ -0,0 +1,22 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Lists.Builders; + +/// +/// 列表加载更多标签生成器 +/// +public class ListLoadMoreBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表加载更多标签生成器 + /// + /// 配置 + public ListLoadMoreBuilder( Config config ) : base( config,"nz-list-load-more" ) { + _config = config; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Builders/ListPaginationBuilder.cs b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListPaginationBuilder.cs new file mode 100644 index 000000000..838282859 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Builders/ListPaginationBuilder.cs @@ -0,0 +1,22 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Lists.Builders; + +/// +/// 列表分页标签生成器 +/// +public class ListPaginationBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表分页标签生成器 + /// + /// 配置 + public ListPaginationBuilder( Config config ) : base( config,"nz-list-pagination" ) { + _config = config; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/ListEmptyTagHelper.cs b/src/Util.Ui.NgZorro/Components/Lists/ListEmptyTagHelper.cs new file mode 100644 index 000000000..64ed1e7d8 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/ListEmptyTagHelper.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Lists.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists; + +/// +/// 列表空内容,生成的标签为<nz-list-empty></nz-list-empty> +/// +[HtmlTargetElement( "util-list-empty" )] +public class ListEmptyTagHelper : AngularTagHelperBase { + /// + /// nzNoResult,空内容显示文本 + /// + public string NoResult { get; set; } + /// + /// [nzNoResult],空内容显示文本 + /// + public string BindNoResult { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ListEmptyRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/ListFooterTagHelper.cs b/src/Util.Ui.NgZorro/Components/Lists/ListFooterTagHelper.cs new file mode 100644 index 000000000..2fd25c32b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/ListFooterTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Lists.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists; + +/// +/// 列表底部区域,生成的标签为<nz-list-footer></nz-list-footer> +/// +[HtmlTargetElement( "util-list-footer" )] +public class ListFooterTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ListFooterRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/ListHeaderTagHelper.cs b/src/Util.Ui.NgZorro/Components/Lists/ListHeaderTagHelper.cs new file mode 100644 index 000000000..aadaf5587 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/ListHeaderTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Lists.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists; + +/// +/// 列表头部区域,生成的标签为<nz-list-header></nz-list-header> +/// +[HtmlTargetElement( "util-list-header" )] +public class ListHeaderTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ListHeaderRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/ListItemActionTagHelper.cs b/src/Util.Ui.NgZorro/Components/Lists/ListItemActionTagHelper.cs new file mode 100644 index 000000000..ac471ed1c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/ListItemActionTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Lists.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists; + +/// +/// 列表项操作项,生成的标签为<nz-list-item-action></nz-list-item-action> +/// +[HtmlTargetElement( "util-list-item-action" )] +public class ListItemActionTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ListItemActionRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/ListItemActionsTagHelper.cs b/src/Util.Ui.NgZorro/Components/Lists/ListItemActionsTagHelper.cs new file mode 100644 index 000000000..98b4d2559 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/ListItemActionsTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Lists.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists; + +/// +/// 列表项操作区域,生成的标签为<ul nz-list-item-actions></ul> +/// +[HtmlTargetElement( "util-list-item-actions" )] +public class ListItemActionsTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ListItemActionsRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/ListItemExtraTagHelper.cs b/src/Util.Ui.NgZorro/Components/Lists/ListItemExtraTagHelper.cs new file mode 100644 index 000000000..5081b401e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/ListItemExtraTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Lists.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists; + +/// +/// 列表项扩展区域,生成的标签为<nz-list-item-extra></nz-list-item-extra> +/// +[HtmlTargetElement( "util-list-item-extra" )] +public class ListItemExtraTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ListItemExtraRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/ListItemMetaAvatarTagHelper.cs b/src/Util.Ui.NgZorro/Components/Lists/ListItemMetaAvatarTagHelper.cs new file mode 100644 index 000000000..5eda97784 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/ListItemMetaAvatarTagHelper.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Lists.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists; + +/// +/// 列表项元信息头像,生成的标签为<nz-list-item-meta-avatar></nz-list-item-meta-avatar> +/// +[HtmlTargetElement( "util-list-item-meta-avatar" )] +public class ListItemMetaAvatarTagHelper : AngularTagHelperBase { + /// + /// nzSrc,头像地址 + /// + public string Src { get; set; } + /// + /// [nzSrc],头像地址 + /// + public string BindSrc { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ListItemMetaAvatarRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/ListItemMetaDescriptionTagHelper.cs b/src/Util.Ui.NgZorro/Components/Lists/ListItemMetaDescriptionTagHelper.cs new file mode 100644 index 000000000..d0d6c01a5 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/ListItemMetaDescriptionTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Lists.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists; + +/// +/// 列表项元信息描述,生成的标签为<nz-list-item-meta-description></nz-list-item-meta-description> +/// +[HtmlTargetElement( "util-list-item-meta-description" )] +public class ListItemMetaDescriptionTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ListItemMetaDescriptionRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/ListItemMetaTagHelper.cs b/src/Util.Ui.NgZorro/Components/Lists/ListItemMetaTagHelper.cs new file mode 100644 index 000000000..29c33ef5b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/ListItemMetaTagHelper.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Lists.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists; + +/// +/// 列表项元信息,生成的标签为<nz-list-item-meta></nz-list-item-meta> +/// +[HtmlTargetElement( "util-list-item-meta" )] +public class ListItemMetaTagHelper : AngularTagHelperBase { + /// + /// nzAvatar,头像图标,类型: string | TemplateRef<void> + /// + public string Avatar { get; set; } + /// + /// [nzAvatar],头像图标,类型: string | TemplateRef<void> + /// + public string BindAvatar { get; set; } + /// + /// nzDescription,描述内容,类型: string | TemplateRef<void> + /// + public string Description { get; set; } + /// + /// [nzDescription],描述内容,类型: string | TemplateRef<void> + /// + public string BindDescription { get; set; } + /// + /// nzTitle,标题,类型: string | TemplateRef<void> + /// + public string Title { get; set; } + /// + /// [nzTitle],标题,类型: string | TemplateRef<void> + /// + public string BindTitle { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ListItemMetaRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/ListItemMetaTitleTagHelper.cs b/src/Util.Ui.NgZorro/Components/Lists/ListItemMetaTitleTagHelper.cs new file mode 100644 index 000000000..6389c108e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/ListItemMetaTitleTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Lists.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists; + +/// +/// 列表项元信息标题,生成的标签为<nz-list-item-meta-title></nz-list-item-meta-title> +/// +[HtmlTargetElement( "util-list-item-meta-title" )] +public class ListItemMetaTitleTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ListItemMetaTitleRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/ListItemTagHelper.cs b/src/Util.Ui.NgZorro/Components/Lists/ListItemTagHelper.cs new file mode 100644 index 000000000..76a2d63d2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/ListItemTagHelper.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Lists.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists; + +/// +/// 列表项,生成的标签为<nz-list-item></nz-list-item> +/// +[HtmlTargetElement( "util-list-item" )] +public class ListItemTagHelper : AngularTagHelperBase { + /// + /// [nzActions],设置列表项操作,类型: Array<TemplateRef<void>>,默认值: [] + /// + public string Actions { get; set; } + /// + /// nzContent,设置列表项内容,类型: string + /// + public string Content { get; set; } + /// + /// [nzContent],设置列表项内容,类型: TemplateRef<void> + /// + public string BindContent { get; set; } + /// + /// [nzExtra],设置列表项扩展,类型: TemplateRef<void> + /// + public string Extra { get; set; } + /// + /// [nzNoFlex],是否非 flex 布局渲染,默认值: false + /// + public string NoFlex { get; set; } + /// + /// *cdkVirtualFor,虚拟滚动循环,范例: let item of items + /// + public string VirtualFor { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ListItemRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/ListLoadMoreTagHelper.cs b/src/Util.Ui.NgZorro/Components/Lists/ListLoadMoreTagHelper.cs new file mode 100644 index 000000000..75cc0b4e5 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/ListLoadMoreTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Lists.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists; + +/// +/// 列表加载更多区域,生成的标签为<nz-list-load-more></nz-list-load-more> +/// +[HtmlTargetElement( "util-list-load-more" )] +public class ListLoadMoreTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ListLoadMoreRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/ListPaginationTagHelper.cs b/src/Util.Ui.NgZorro/Components/Lists/ListPaginationTagHelper.cs new file mode 100644 index 000000000..2c01ffe59 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/ListPaginationTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Lists.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists; + +/// +/// 列表分页区域,生成的标签为<nz-list-pagination></nz-list-pagination> +/// +[HtmlTargetElement( "util-list-pagination" )] +public class ListPaginationTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ListPaginationRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/ListTagHelper.cs b/src/Util.Ui.NgZorro/Components/Lists/ListTagHelper.cs new file mode 100644 index 000000000..b98d0f0b2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/ListTagHelper.cs @@ -0,0 +1,76 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Lists.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists; + +/// +/// 列表,生成的标签为<nz-list></nz-list> +/// +[HtmlTargetElement( "util-list" )] +public class ListTagHelper : AngularTagHelperBase { + /// + /// [nzBordered],是否显示边框,默认值: false + /// + public string Bordered { get; set; } + /// + /// nzHeader,列表头部,类型: string + /// + public string Header { get; set; } + /// + /// [nzHeader],列表头部,类型: string | TemplateRef<void> + /// + public string BindHeader { get; set; } + /// + /// nzFooter,列表底部,类型: string + /// + public string Footer { get; set; } + /// + /// [nzFooter],列表底部,类型: string | TemplateRef<void> + /// + public string BindFooter { get; set; } + /// + /// nzItemLayout,列表项布局方式,可选值: 'vertical' | 'horizontal',默认值: 'horizontal' + /// + public ListItemLayout ItemLayout { get; set; } + /// + /// [nzItemLayout],列表项布局方式,可选值: 'vertical' | 'horizontal',默认值: 'horizontal' + /// + public string BindItemLayout { get; set; } + /// + /// [nzLoading],是否加载状态,默认值: false + /// + public string Loading { get; set; } + /// + /// nzSize,列表大小,可选值: 'large' | 'small' | 'default',默认值: 'default' + /// + public ListSize Size { get; set; } + /// + /// [nzSize],列表大小,可选值: 'large' | 'small' | 'default',默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// [nzSplit],是否显示分割线,默认值: true + /// + public string Split { get; set; } + /// + /// [nzGrid],是否支持栅格列表,默认值: false + /// + public string Grid { get; set; } + /// + /// [nzDataSource],设置数据源 + /// + public string Datasource { get; set; } + /// + /// [nzRenderItem],设置自定义项模板,类型: TemplateRef<{ $implicit: NzSafeAny; index: number }> + /// + public string RenderItem { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ListRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Renders/ListEmptyRender.cs b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListEmptyRender.cs new file mode 100644 index 000000000..455c85114 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListEmptyRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists.Renders; + +/// +/// 列表空内容渲染器 +/// +public class ListEmptyRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表空内容渲染器 + /// + /// 配置 + public ListEmptyRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ListEmptyBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ListEmptyRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Renders/ListFooterRender.cs b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListFooterRender.cs new file mode 100644 index 000000000..6b2f99779 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListFooterRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists.Renders; + +/// +/// 列表底部渲染器 +/// +public class ListFooterRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表底部渲染器 + /// + /// 配置 + public ListFooterRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ListFooterBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ListFooterRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Renders/ListHeaderRender.cs b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListHeaderRender.cs new file mode 100644 index 000000000..ffc10b91e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListHeaderRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists.Renders; + +/// +/// 列表头部渲染器 +/// +public class ListHeaderRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表头部渲染器 + /// + /// 配置 + public ListHeaderRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ListHeaderBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ListHeaderRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemActionRender.cs b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemActionRender.cs new file mode 100644 index 000000000..f029a58e1 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemActionRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists.Renders; + +/// +/// 列表项操作项渲染器 +/// +public class ListItemActionRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表项操作项渲染器 + /// + /// 配置 + public ListItemActionRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ListItemActionBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ListItemActionRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemActionsRender.cs b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemActionsRender.cs new file mode 100644 index 000000000..a3805d8b4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemActionsRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists.Renders; + +/// +/// 列表项操作渲染器 +/// +public class ListItemActionsRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表项操作渲染器 + /// + /// 配置 + public ListItemActionsRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ListItemActionsBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ListItemActionsRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemExtraRender.cs b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemExtraRender.cs new file mode 100644 index 000000000..8393b0e55 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemExtraRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists.Renders; + +/// +/// 列表项扩展渲染器 +/// +public class ListItemExtraRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表项扩展渲染器 + /// + /// 配置 + public ListItemExtraRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ListItemExtraBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ListItemExtraRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemMetaAvatarRender.cs b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemMetaAvatarRender.cs new file mode 100644 index 000000000..2e4441c4e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemMetaAvatarRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists.Renders; + +/// +/// 列表项元信息头像渲染器 +/// +public class ListItemMetaAvatarRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表项元信息头像渲染器 + /// + /// 配置 + public ListItemMetaAvatarRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ListItemMetaAvatarBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ListItemMetaAvatarRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemMetaDescriptionRender.cs b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemMetaDescriptionRender.cs new file mode 100644 index 000000000..a10310aec --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemMetaDescriptionRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists.Renders; + +/// +/// 列表项元信息描述渲染器 +/// +public class ListItemMetaDescriptionRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表项元信息描述渲染器 + /// + /// 配置 + public ListItemMetaDescriptionRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ListItemMetaDescriptionBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ListItemMetaDescriptionRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemMetaRender.cs b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemMetaRender.cs new file mode 100644 index 000000000..88a2f6c83 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemMetaRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists.Renders; + +/// +/// 列表项元信息渲染器 +/// +public class ListItemMetaRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表项元信息渲染器 + /// + /// 配置 + public ListItemMetaRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ListItemMetaBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ListItemMetaRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemMetaTitleRender.cs b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemMetaTitleRender.cs new file mode 100644 index 000000000..8f5faa7fa --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemMetaTitleRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists.Renders; + +/// +/// 列表项元信息标题渲染器 +/// +public class ListItemMetaTitleRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表项元信息标题渲染器 + /// + /// 配置 + public ListItemMetaTitleRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ListItemMetaTitleBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ListItemMetaTitleRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemRender.cs b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemRender.cs new file mode 100644 index 000000000..fd895b8b5 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListItemRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists.Renders; + +/// +/// 列表项渲染器 +/// +public class ListItemRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表项渲染器 + /// + /// 配置 + public ListItemRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ListItemBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ListItemRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Renders/ListLoadMoreRender.cs b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListLoadMoreRender.cs new file mode 100644 index 000000000..dfe121d48 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListLoadMoreRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists.Renders; + +/// +/// 列表加载更多渲染器 +/// +public class ListLoadMoreRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表加载更多渲染器 + /// + /// 配置 + public ListLoadMoreRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ListLoadMoreBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ListLoadMoreRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Renders/ListPaginationRender.cs b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListPaginationRender.cs new file mode 100644 index 000000000..c2661d540 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListPaginationRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists.Renders; + +/// +/// 列表分页渲染器 +/// +public class ListPaginationRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表分页渲染器 + /// + /// 配置 + public ListPaginationRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ListPaginationBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ListPaginationRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Lists/Renders/ListRender.cs b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListRender.cs new file mode 100644 index 000000000..e1ced8120 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Lists/Renders/ListRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Lists.Renders; + +/// +/// 列表渲染器 +/// +public class ListRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化列表渲染器 + /// + /// 配置 + public ListRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ListBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ListRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Mentions/Builders/MentionBuilder.cs b/src/Util.Ui.NgZorro/Components/Mentions/Builders/MentionBuilder.cs new file mode 100644 index 000000000..c27cb510d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Mentions/Builders/MentionBuilder.cs @@ -0,0 +1,117 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Mentions.Builders; + +/// +/// 提及标签生成器 +/// +public class MentionBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化提及标签生成器 + /// + public MentionBuilder( Config config ) : base( config,"nz-mention" ) { + _config = config; + } + + /// + /// 配置建议内容 + /// + public MentionBuilder Suggestions() { + AttributeIfNotEmpty( "[nzSuggestions]", _config.GetValue( UiConst.Suggestions ) ); + return this; + } + + /// + /// 配置加载状态 + /// + public MentionBuilder Loading() { + AttributeIfNotEmpty( "[nzLoading]", _config.GetValue( UiConst.Loading ) ); + return this; + } + + /// + /// 配置取值函数 + /// + public MentionBuilder ValueWith() { + AttributeIfNotEmpty( "[nzValueWith]", _config.GetValue( UiConst.ValueWith ) ); + return this; + } + + /// + /// 配置触发前缀 + /// + public MentionBuilder Prefix() { + AttributeIfNotEmpty( "[nzPrefix]", _config.GetValue( UiConst.Prefix ) ); + return this; + } + + /// + /// 配置建议框位置 + /// + public MentionBuilder Placement() { + AttributeIfNotEmpty( "nzPlacement", _config.GetValue( UiConst.Placement )?.Description() ); + AttributeIfNotEmpty( "[nzPlacement]", _config.GetValue( AngularConst.BindPlacement ) ); + return this; + } + + /// + /// 配置空建议默认内容 + /// + public MentionBuilder NotFoundContent() { + AttributeIfNotEmpty( "nzNotFoundContent", _config.GetValue( UiConst.NotFoundContent ) ); + AttributeIfNotEmpty( "[nzNotFoundContent]", _config.GetValue( AngularConst.BindNotFoundContent ) ); + return this; + } + + /// + /// 配置指定提及的触发元素 + /// + public MentionBuilder MentionTrigger() { + AttributeIfNotEmpty( "[nzMentionTrigger]", _config.GetValue( UiConst.MentionTrigger ) ); + return this; + } + + /// + /// 配置自定义建议渲染模板 + /// + public MentionBuilder MentionSuggestion() { + AttributeIfNotEmpty( "[nzMentionSuggestion]", _config.GetValue( UiConst.MentionSuggestion ) ); + return this; + } + + /// + /// 配置校验状态 + /// + public MentionBuilder Status() { + AttributeIfNotEmpty( "nzStatus", _config.GetValue( UiConst.Status )?.Description() ); + AttributeIfNotEmpty( "[nzStatus]", _config.GetValue( AngularConst.BindStatus ) ); + return this; + } + + /// + /// 配置事件 + /// + public MentionBuilder Events() { + AttributeIfNotEmpty( "(nzOnSelect)", _config.GetValue( UiConst.OnSelect ) ); + AttributeIfNotEmpty( "(nzOnSearchChange)", _config.GetValue( UiConst.OnSearchChange ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + Suggestions().Loading().ValueWith().Prefix().Placement() + .NotFoundContent().MentionTrigger().MentionSuggestion() + .Status() + .Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Mentions/Configs/MentionShareConfig.cs b/src/Util.Ui.NgZorro/Components/Mentions/Configs/MentionShareConfig.cs new file mode 100644 index 000000000..d22834cf7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Mentions/Configs/MentionShareConfig.cs @@ -0,0 +1,7 @@ +namespace Util.Ui.NgZorro.Components.Mentions.Configs; + +/// +/// 提及共享配置 +/// +public class MentionShareConfig { +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Mentions/MentionTagHelper.cs b/src/Util.Ui.NgZorro/Components/Mentions/MentionTagHelper.cs new file mode 100644 index 000000000..4fb80a53d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Mentions/MentionTagHelper.cs @@ -0,0 +1,96 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Forms.Helpers; +using Util.Ui.NgZorro.Components.Mentions.Configs; +using Util.Ui.NgZorro.Components.Mentions.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Mentions; + +/// +/// 提及,生成的标签为<nz-mention></nz-mention> +/// +[HtmlTargetElement( "util-mention" )] +public class MentionTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// [nzSuggestions],建议内容,类型:any[] + /// + public string Suggestions { get; set; } + /// + /// [nzLoading],加载状态,类型: boolean, 默认值: false + /// + public string Loading { get; set; } + /// + /// [nzValueWith],建议选项的取值函数,函数定义: (any) => string | (value: string) => string + /// + public string ValueWith { get; set; } + /// + /// [nzPrefix],触发弹出下拉框的字符,类型: string | string[],默认值: '@', 范例: ['#', '@'] + /// + public string Prefix { get; set; } + /// + /// nzPlacement,建议框位置,可选值: 'bottom' | 'top',默认值: 'bottom' + /// + public MentionPlacement Placement { get; set; } + /// + /// [nzPlacement],建议框位置,可选值: 'bottom' | 'top',默认值: 'bottom' + /// + public string BindPlacement { get; set; } + /// + /// nzNotFoundContent,建议列表为空时显示的内容,默认值: '无匹配结果,轻敲空格完成输入' + /// + public string NotFoundContent { get; set; } + /// + /// [nzNotFoundContent],建议列表为空时显示的内容,默认值: '无匹配结果,轻敲空格完成输入' + /// + public string BindNotFoundContent { get; set; } + /// + /// [nzMentionTrigger],指定提及的触发元素,类型: HTMLTextAreaElement | HTMLInputElement + /// + public string MentionTrigger { get; set; } + /// + /// [nzMentionSuggestion],自定义建议渲染模板,类型: TemplateRef<any> + /// + public string MentionSuggestion { get; set; } + /// + /// nzStatus,校验状态, 可选值: 'error' | 'warning' + /// + public FormControlStatus Status { get; set; } + /// + /// [nzStatus],校验状态, 可选值: 'error' | 'warning' + /// + public string BindStatus { get; set; } + /// + /// (nzOnSelect),选择事件,类型: EventEmitter<any> + /// + public string OnSelect { get; set; } + /// + /// (nzOnSearchChange),搜索改变事件,当输入框中 @ 变化时触发,类型: EventEmitter<MentionOnSearchTypes> + /// + public string OnSearchChange { get; set; } + + /// + /// 渲染前操作 + /// + /// 上下文 + /// 输出 + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var formItemShareService = new FormItemShareService( _config ); + formItemShareService.Init(); + formItemShareService.InitId(); + context.SetValueToItems( new MentionShareConfig() ); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new MentionRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Mentions/Renders/MentionRender.cs b/src/Util.Ui.NgZorro/Components/Mentions/Renders/MentionRender.cs new file mode 100644 index 000000000..6086637ae --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Mentions/Renders/MentionRender.cs @@ -0,0 +1,40 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Mentions.Builders; + +namespace Util.Ui.NgZorro.Components.Mentions.Renders; + +/// +/// 提及渲染器 +/// +public class MentionRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化提及渲染器 + /// + /// 配置 + public MentionRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new MentionBuilder( _config ); + builder.Config(); + _config.Content.AppendTo( builder ); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new MentionRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Menus/Builders/MenuBuilder.cs b/src/Util.Ui.NgZorro/Components/Menus/Builders/MenuBuilder.cs new file mode 100644 index 000000000..9d21622f3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Menus/Builders/MenuBuilder.cs @@ -0,0 +1,85 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Menus.Builders; + +/// +/// 菜单标签生成器 +/// +public class MenuBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化菜单标签生成器 + /// + /// 配置 + public MenuBuilder( Config config ) : base( config, "ul" ) { + _config = config; + base.Attribute( "nz-menu" ); + } + + /// + /// 配置模式 + /// + public MenuBuilder Model() { + AttributeIfNotEmpty( "nzMode", _config.GetValue( UiConst.Mode )?.Description() ); + AttributeIfNotEmpty( "[nzMode]", _config.GetValue( AngularConst.BindMode ) ); + return this; + } + + /// + /// 配置允许选中 + /// + public MenuBuilder Selectable() { + AttributeIfNotEmpty( "[nzSelectable]", _config.GetValue( UiConst.Selectable ) ); + return this; + } + + /// + /// 配置主题 + /// + public MenuBuilder Theme() { + AttributeIfNotEmpty( "nzTheme", _config.GetValue( UiConst.Theme )?.Description() ); + AttributeIfNotEmpty( "[nzTheme]", _config.GetValue( AngularConst.BindTheme ) ); + return this; + } + + /// + /// 配置内嵌模式折叠状态 + /// + public MenuBuilder InlineCollapsed() { + AttributeIfNotEmpty( "[nzInlineCollapsed]", _config.GetValue( UiConst.InlineCollapsed ) ); + return this; + } + + /// + /// 配置内嵌模式缩进宽度 + /// + public MenuBuilder InlineIndent() { + AttributeIfNotEmpty( "[nzInlineIndent]", _config.GetValue( UiConst.InlineIndent ) ); + AttributeIfNotEmpty( "[nzInlineIndent]", _config.GetValue( AngularConst.BindInlineIndent ) ); + return this; + } + + /// + /// 配置事件 + /// + public MenuBuilder Events() { + AttributeIfNotEmpty( "(nzClick)", _config.GetValue( UiConst.OnClick ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Model().Selectable().Theme() + .InlineCollapsed().InlineIndent() + .Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Menus/Builders/MenuDividerBuilder.cs b/src/Util.Ui.NgZorro/Components/Menus/Builders/MenuDividerBuilder.cs new file mode 100644 index 000000000..9ce6cb614 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Menus/Builders/MenuDividerBuilder.cs @@ -0,0 +1,16 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Menus.Builders; + +/// +/// 菜单分隔线标签生成器 +/// +public class MenuDividerBuilder : AngularTagBuilder { + /// + /// 初始化菜单分隔线标签生成器 + /// + /// 配置 + public MenuDividerBuilder( Config config ) : base( config, "li" ) { + base.Attribute( "nz-menu-divider" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Menus/Builders/MenuGroupBuilder.cs b/src/Util.Ui.NgZorro/Components/Menus/Builders/MenuGroupBuilder.cs new file mode 100644 index 000000000..b69e37133 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Menus/Builders/MenuGroupBuilder.cs @@ -0,0 +1,65 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Menus.Builders; + +/// +/// 菜单组标签生成器 +/// +public class MenuGroupBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化菜单组标签生成器 + /// + /// 配置 + public MenuGroupBuilder( Config config ) : base( config,"li" ) { + _config = config; + base.Attribute( "nz-menu-group" ); + } + + /// + /// 配置标题 + /// + public MenuGroupBuilder Title() { + SetTitle( _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 设置标题 + /// + private void SetTitle( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzTitle]", value ); + return; + } + AttributeIfNotEmpty( "nzTitle", value ); + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Title(); + } + + /// + /// 配置内容元素 + /// + protected override void ConfigContent( Config config ) { + var ulBuilder = new UlBuilder(); + SetContent( ulBuilder ); + _config.Content.AppendTo( ulBuilder ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Menus/Builders/MenuItemBuilder.cs b/src/Util.Ui.NgZorro/Components/Menus/Builders/MenuItemBuilder.cs new file mode 100644 index 000000000..2b5cd8a6f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Menus/Builders/MenuItemBuilder.cs @@ -0,0 +1,203 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Icons.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Directives.Popconfirms; +using Util.Ui.NgZorro.Directives.Popover; +using Util.Ui.NgZorro.Directives.Tooltips; + +namespace Util.Ui.NgZorro.Components.Menus.Builders; + +/// +/// 菜单项标签生成器 +/// +public class MenuItemBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化菜单项标签生成器 + /// + public MenuItemBuilder( Config config ) : base( config, "li" ) { + _config = config; + base.Attribute( "nz-menu-item" ); + } + + /// + /// 配置Update文本 + /// + public MenuItemBuilder TextUpdate() { + var value = _config.GetValue( UiConst.TextUpdate ); + if ( value != true ) + return this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Update ); + return this; + } + _config.SetAttribute( UiConst.Text, "Update" ); + return this; + } + + /// + /// 配置Delete文本 + /// + public MenuItemBuilder TextDelete() { + var value = _config.GetValue( UiConst.TextDelete ); + if ( value != true ) + return this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Delete ); + return this; + } + _config.SetAttribute( UiConst.Text, "Delete" ); + return this; + } + + /// + /// 配置Detail文本 + /// + public MenuItemBuilder TextDetail() { + var value = _config.GetValue( UiConst.TextDetail ); + if ( value != true ) + return this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Detail ); + return this; + } + _config.SetAttribute( UiConst.Text, "Detail" ); + return this; + } + + /// + /// 配置Enable文本 + /// + public MenuItemBuilder TextEnable() { + var value = _config.GetValue( UiConst.TextEnable ); + if ( value != true ) + return this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Enable ); + return this; + } + _config.SetAttribute( UiConst.Text, "Enable" ); + return this; + } + + /// + /// 配置Disable文本 + /// + public MenuItemBuilder TextDisable() { + var value = _config.GetValue( UiConst.TextDisable ); + if ( value != true ) + return this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, I18nKeys.Disable ); + return this; + } + _config.SetAttribute( UiConst.Text, "Disable" ); + return this; + } + + /// + /// 配置文本 + /// + public MenuItemBuilder Text() { + var options = NgZorroOptionsService.GetOptions(); + var text = _config.GetValue( UiConst.Text ); + if ( text.IsEmpty() ) + return this; + if ( options.EnableI18n ) { + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{" ); + result.Append( $"'{text}'|i18n" ); + result.Append( "}}" ); + result.Append( "" ); + AppendContent( result.ToString() ); + return this; + } + AppendContent( $"{text}" ); + return this; + } + + /// + /// 配置禁用 + /// + public MenuItemBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置选中 + /// + public MenuItemBuilder Selected() { + AttributeIfNotEmpty( "[nzSelected]", _config.GetValue( UiConst.Selected ) ); + return this; + } + + /// + /// 配置危险状态 + /// + public MenuItemBuilder Danger() { + AttributeIfNotEmpty( "[nzDanger]", _config.GetValue( UiConst.Danger ) ); + return this; + } + + /// + /// 配置匹配路由 + /// + public MenuItemBuilder MatchRouter() { + AttributeIfNotEmpty( "[nzMatchRouter]", _config.GetValue( UiConst.MatchRouter ) ); + AttributeIfNotEmpty( "[nzMatchRouterExact]", _config.GetValue( UiConst.MatchRouterExact ) ); + return this; + } + + /// + /// 配置图标 + /// + public MenuItemBuilder Icon() { + if ( _config.Contains( UiConst.Icon ) == false ) + return this; + var iconBuilder = new IconBuilder( _config ); + iconBuilder.Class( "mr-sm" ); + iconBuilder.Icon(); + AppendContent( iconBuilder ); + return this; + } + + /// + /// 配置左内边距 + /// + public MenuItemBuilder PaddingLeft() { + AttributeIfNotEmpty( "[nzPaddingLeft]", _config.GetValue( UiConst.PaddingLeft ) ); + return this; + } + + /// + /// 配置事件 + /// + public MenuItemBuilder Events() { + this.OnClick( _config ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + this.Tooltip( _config ).Popover( _config ).Popconfirm( _config ); + Icon(); + TextUpdate().TextDelete().TextDetail().TextEnable().TextDisable().Text(). + Disabled().Selected().Danger().MatchRouter().PaddingLeft() + .Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Menus/Builders/SubMenuBuilder.cs b/src/Util.Ui.NgZorro/Components/Menus/Builders/SubMenuBuilder.cs new file mode 100644 index 000000000..33ffa8e8b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Menus/Builders/SubMenuBuilder.cs @@ -0,0 +1,127 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Menus.Builders; + +/// +/// 子菜单标签生成器 +/// +public class SubMenuBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化子菜单标签生成器 + /// + /// 配置 + public SubMenuBuilder( Config config ) : base( config,"li" ) { + _config = config; + base.Attribute( "nz-submenu" ); + } + + /// + /// 配置标题 + /// + public SubMenuBuilder Title() { + SetTitle( _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 设置标题 + /// + private void SetTitle( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzTitle]", value ); + return; + } + AttributeIfNotEmpty( "nzTitle", value ); + } + + /// + /// 配置禁用 + /// + public SubMenuBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置图标 + /// + public SubMenuBuilder Icon() { + AttributeIfNotEmpty( "nzIcon", _config.GetValue( UiConst.Icon )?.Description() ); + AttributeIfNotEmpty( "[nzIcon]", _config.GetValue( AngularConst.BindIcon ) ); + return this; + } + + /// + /// 配置展开 + /// + public SubMenuBuilder Open() { + AttributeIfNotEmpty( "[nzOpen]", _config.GetValue( UiConst.Open ) ); + AttributeIfNotEmpty( "[(nzOpen)]", _config.GetValue( AngularConst.BindonOpen ) ); + return this; + } + + /// + /// 配置自定义子菜单容器类名 + /// + public SubMenuBuilder MenuClassName() { + AttributeIfNotEmpty( "nzMenuClassName", _config.GetValue( UiConst.MenuClassName ) ); + AttributeIfNotEmpty( "[nzMenuClassName]", _config.GetValue( AngularConst.BindMenuClassName ) ); + return this; + } + + /// + /// 配置菜单弹出位置 + /// + public SubMenuBuilder Placement() { + AttributeIfNotEmpty( "nzPlacement", _config.GetValue( UiConst.Placement )?.Description() ); + AttributeIfNotEmpty( "[nzPlacement]", _config.GetValue( AngularConst.BindPlacement ) ); + return this; + } + + /// + /// 配置左内边距 + /// + public SubMenuBuilder PaddingLeft() { + AttributeIfNotEmpty( "[nzPaddingLeft]", _config.GetValue( UiConst.PaddingLeft ) ); + return this; + } + + /// + /// 配置事件 + /// + public SubMenuBuilder Events() { + AttributeIfNotEmpty( "(nzOpenChange)", _config.GetValue( UiConst.OnOpenChange ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Title().Disabled().Icon().Open().MenuClassName().Placement().PaddingLeft() + .Events(); + } + + /// + /// 配置内容元素 + /// + protected override void ConfigContent( Config config ) { + var ulBuilder = new UlBuilder(); + SetContent( ulBuilder ); + config.Content.AppendTo( ulBuilder ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Menus/MenuDividerTagHelper.cs b/src/Util.Ui.NgZorro/Components/Menus/MenuDividerTagHelper.cs new file mode 100644 index 000000000..4e3b7cc62 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Menus/MenuDividerTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Menus.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Menus; + +/// +/// 菜单分隔线,生成的标签为<li nz-menu-divider></li> +/// +[HtmlTargetElement( "util-menu-divider" )] +public class MenuDividerTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new MenuDividerRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Menus/MenuGroupTagHelper.cs b/src/Util.Ui.NgZorro/Components/Menus/MenuGroupTagHelper.cs new file mode 100644 index 000000000..c1c1acbd3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Menus/MenuGroupTagHelper.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Menus.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Menus; + +/// +/// 菜单组,生成的标签为<li nz-menu-group><ul></ul></li> +/// +[HtmlTargetElement( "util-menu-group" )] +public class MenuGroupTagHelper : AngularTagHelperBase { + /// + /// nzTitle,标题,支持多语言 + /// + public string Title { get; set; } + /// + /// [nzTitle],标题 + /// + public string BindTitle { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new MenuGroupRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Menus/MenuItemTagHelper.cs b/src/Util.Ui.NgZorro/Components/Menus/MenuItemTagHelper.cs new file mode 100644 index 000000000..d2b205aec --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Menus/MenuItemTagHelper.cs @@ -0,0 +1,76 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Menus.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Menus; + +/// +/// 菜单项,生成的标签为<li nz-menu-item></li> +/// +[HtmlTargetElement( "util-menu-item")] +public class MenuItemTagHelper : TooltipTagHelperBase { + /// + /// 扩展属性,内容文本,支持多语言 + /// + public string Text { get; set; } + /// + /// 扩展属性,是否显示update文本,默认文本为'Update',i18n文本为'util.update' + /// + public bool TextUpdate { get; set; } + /// + /// 扩展属性,是否显示delete文本,默认文本为'Delete',i18n文本为'util.delete' + /// + public bool TextDelete { get; set; } + /// + /// 扩展属性,是否显示detail文本,默认文本为'Detail',i18n文本为'util.detail' + /// + public bool TextDetail { get; set; } + /// + /// 扩展属性,是否显示enable文本,默认文本为'Enable',i18n文本为'util.enable' + /// + public bool TextEnable { get; set; } + /// + /// 扩展属性,是否显示disable文本,默认文本为'Disable',i18n文本为'util.disable' + /// + public bool TextDisable { get; set; } + /// + /// 扩展属性,设置图标 + /// + public AntDesignIcon Icon { get; set; } + /// + /// [nzDisabled],是否禁用, 默认值: false + /// + public string Disabled { get; set; } + /// + /// [nzSelected],是否被选中, 默认值: false + /// + public string Selected { get; set; } + /// + /// [nzDanger],是否危险状态, 默认值: false + /// + public string Danger { get; set; } + /// + /// [nzMatchRouter],是否根据 routerLink 自动设置选中状态, 默认值: false + /// + public string MatchRouter { get; set; } + /// + /// [nzMatchRouterExact],是否路由完整精确匹配, 默认值: false + /// + public string MatchRouterExact { get; set; } + /// + /// [nzPaddingLeft],左内边距,单位: 像素, 类型: number + /// + public string PaddingLeft { get; set; } + /// + /// (click),单击事件 + /// + public string OnClick { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new MenuItemRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Menus/MenuTagHelper.cs b/src/Util.Ui.NgZorro/Components/Menus/MenuTagHelper.cs new file mode 100644 index 000000000..1267c5c26 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Menus/MenuTagHelper.cs @@ -0,0 +1,66 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Dropdowns.Helpers; +using Util.Ui.NgZorro.Components.Menus.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Menus; + +/// +/// 菜单,生成的标签为<ul nz-menu></ul> +/// +[HtmlTargetElement( "util-menu")] +public class MenuTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + + /// + /// nzMode,菜单模式,支持垂直、水平、和内嵌模式三种,可选值: 'vertical' | 'horizontal' | 'inline',默认值: 'vertical' + /// + public MenuMode Mode { get; set; } + /// + /// [nzMode],菜单模式,支持垂直、水平、和内嵌模式三种,可选值: 'vertical' | 'horizontal' | 'inline',默认值: 'vertical' + /// + public string BindMode { get; set; } + /// + /// [nzSelectable],是否允许选中,类型: boolean, 默认值:true + /// + public string Selectable { get; set; } + /// + /// nzTheme,主题颜色,可选值: 'light' | 'dark', 默认值: 'light' + /// + public MenuTheme Theme { get; set; } + /// + /// [nzTheme],主题颜色,可选值: 'light' | 'dark', 默认值: 'light' + /// + public string BindTheme { get; set; } + /// + /// [nzInlineCollapsed],菜单是否收起,内嵌模式 `nzMode='inline'` 有效, 类型: boolean + /// + public string InlineCollapsed { get; set; } + /// + /// [nzInlineIndent],菜单缩进宽度,内嵌模式 `nzMode='inline'` 有效, 类型: number, 默认值:24 + /// + public string InlineIndent { get; set; } + /// + /// (nzClick),点击菜单项事件,类型: EventEmitter<NzMenuItemDirective> + /// + public string OnClick { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new DropdownMenuService( _config ); + service.Init(); + service.AutoCreateUl( false ); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new MenuRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Menus/Renders/MenuDividerRender.cs b/src/Util.Ui.NgZorro/Components/Menus/Renders/MenuDividerRender.cs new file mode 100644 index 000000000..17232cab9 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Menus/Renders/MenuDividerRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Menus.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Menus.Renders; + +/// +/// 菜单分隔线渲染器 +/// +public class MenuDividerRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化菜单分隔线渲染器 + /// + /// 配置 + public MenuDividerRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new MenuDividerBuilder(_config); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new MenuDividerRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Menus/Renders/MenuGroupRender.cs b/src/Util.Ui.NgZorro/Components/Menus/Renders/MenuGroupRender.cs new file mode 100644 index 000000000..f82adedbe --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Menus/Renders/MenuGroupRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Menus.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Menus.Renders; + +/// +/// 菜单组渲染器 +/// +public class MenuGroupRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化菜单组渲染器 + /// + /// 配置 + public MenuGroupRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new MenuGroupBuilder(_config); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new MenuGroupRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Menus/Renders/MenuItemRender.cs b/src/Util.Ui.NgZorro/Components/Menus/Renders/MenuItemRender.cs new file mode 100644 index 000000000..53ad2fe3b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Menus/Renders/MenuItemRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Menus.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Menus.Renders; + +/// +/// 菜单项渲染器 +/// +public class MenuItemRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化菜单项渲染器 + /// + /// 配置 + public MenuItemRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new MenuItemBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new MenuItemRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Menus/Renders/MenuRender.cs b/src/Util.Ui.NgZorro/Components/Menus/Renders/MenuRender.cs new file mode 100644 index 000000000..6c7f2398b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Menus/Renders/MenuRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Menus.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Menus.Renders; + +/// +/// 菜单渲染器 +/// +public class MenuRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化菜单渲染器 + /// + /// 配置 + public MenuRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new MenuBuilder(_config); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new MenuRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Menus/Renders/SubMenuRender.cs b/src/Util.Ui.NgZorro/Components/Menus/Renders/SubMenuRender.cs new file mode 100644 index 000000000..2971f30aa --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Menus/Renders/SubMenuRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Menus.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Menus.Renders; + +/// +/// 子菜单渲染器 +/// +public class SubMenuRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化子菜单渲染器 + /// + /// 配置 + public SubMenuRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new SubMenuBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new SubMenuRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Menus/SubMenuTagHelper.cs b/src/Util.Ui.NgZorro/Components/Menus/SubMenuTagHelper.cs new file mode 100644 index 000000000..cbd1675f2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Menus/SubMenuTagHelper.cs @@ -0,0 +1,72 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Menus.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Menus; + +/// +/// 子菜单,生成的标签为<li nz-submenu><ul></ul></li> +/// +[HtmlTargetElement( "util-submenu" )] +public class SubMenuTagHelper : AngularTagHelperBase { + /// + /// nzTitle,标题,支持多语言 + /// + public string Title { get; set; } + /// + /// [nzTitle],标题 + /// + public string BindTitle { get; set; } + /// + /// [nzDisabled],是否禁用, 默认值: false + /// + public string Disabled { get; set; } + /// + /// nzIcon,图标 + /// + public AntDesignIcon Icon { get; set; } + /// + /// [nzIcon],图标 + /// + public string BindIcon { get; set; } + /// + /// [nzOpen],是否展开, 默认值: false + /// + public string Open { get; set; } + /// + /// [(nzOpen)],是否展开, 默认值: false + /// + public string BindonOpen { get; set; } + /// + /// nzMenuClassName,自定义子菜单容器类名 + /// + public string MenuClassName { get; set; } + /// + /// [nzMenuClassName],自定义子菜单容器类名 + /// + public string BindMenuClassName { get; set; } + /// + /// nzPlacement,菜单弹出位置, 可选值: bottomLeft' | 'bottomCenter' | 'bottomRight' | 'topLeft' | 'topCenter' | 'topRight', 默认值: 'bottomLeft' + /// + public DropdownMenuPlacement Placement { get; set; } + /// + /// [nzPlacement],菜单弹出位置, 可选值: bottomLeft' | 'bottomCenter' | 'bottomRight' | 'topLeft' | 'topCenter' | 'topRight', 默认值: 'bottomLeft' + /// + public string BindPlacement { get; set; } + /// + /// [nzPaddingLeft],左内边距,单位: 像素, 类型: number + /// + public string PaddingLeft { get; set; } + /// + /// (nzOpenChange),展开状态变化事件, 类型: EventEmitter<boolean> + /// + public string OnOpenChange { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new SubMenuRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Modals/Builders/ModalBuilder.cs b/src/Util.Ui.NgZorro/Components/Modals/Builders/ModalBuilder.cs new file mode 100644 index 000000000..2d2c416cd --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Modals/Builders/ModalBuilder.cs @@ -0,0 +1,311 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Modals.Builders; + +/// +/// 对话框标签生成器 +/// +public class ModalBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化对话框标签生成器 + /// + /// 配置 + public ModalBuilder( Config config ) : base( config,"nz-modal" ) { + _config = config; + } + + /// + /// 配置标题 + /// + public ModalBuilder Title() { + SetTitle( _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 设置标题 + /// + private void SetTitle( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzTitle]", value ); + return; + } + AttributeIfNotEmpty( "nzTitle", value ); + } + + /// + /// 配置是否显示遮罩 + /// + public ModalBuilder Mask() { + AttributeIfNotEmpty( "[nzMask]", _config.GetValue( UiConst.Mask ) ); + return this; + } + + /// + /// 配置点击遮罩是否允许关闭 + /// + public ModalBuilder MaskClosable() { + AttributeIfNotEmpty( "[nzMaskClosable]", _config.GetValue( UiConst.MaskClosable ) ); + return this; + } + + /// + /// 配置导航时是否关闭 + /// + public ModalBuilder CloseOnNavigation() { + AttributeIfNotEmpty( "[nzCloseOnNavigation]", _config.GetValue( UiConst.CloseOnNavigation ) ); + return this; + } + + /// + /// 配置是否可见 + /// + public ModalBuilder Visible() { + AttributeIfNotEmpty( "[(nzVisible)]", _config.GetValue( UiConst.Visible ) ); + return this; + } + + /// + /// 配置是否可关闭 + /// + public ModalBuilder Closable() { + AttributeIfNotEmpty( "[nzClosable]", _config.GetValue( UiConst.Closable ) ); + return this; + } + + /// + /// 配置模态框是否可拖动 + /// + public ModalBuilder Draggable() { + AttributeIfNotEmpty( "[nzDraggable]", _config.GetValue( UiConst.Draggable ) ); + return this; + } + + /// + /// 配置确定按钮是否加载状态 + /// + public ModalBuilder OkLoading() { + AttributeIfNotEmpty( "[nzOkLoading]", _config.GetValue( UiConst.OkLoading ) ); + return this; + } + + /// + /// 配置是否禁用确定按钮 + /// + public ModalBuilder OkDisabled() { + AttributeIfNotEmpty( "[nzOkDisabled]", _config.GetValue( UiConst.OkDisabled ) ); + return this; + } + + /// + /// 配置取消按钮是否加载状态 + /// + public ModalBuilder CancelLoading() { + AttributeIfNotEmpty( "[nzCancelLoading]", _config.GetValue( UiConst.CancelLoading ) ); + return this; + } + + /// + /// 配置是否禁用取消按钮 + /// + public ModalBuilder CancelDisabled() { + AttributeIfNotEmpty( "[nzCancelDisabled]", _config.GetValue( UiConst.CancelDisabled ) ); + return this; + } + + /// + /// 配置是否支持键盘ESC键关闭 + /// + public ModalBuilder Keyboard() { + AttributeIfNotEmpty( "[nzKeyboard]", _config.GetValue( UiConst.Keyboard ) ); + return this; + } + + /// + /// 配置是否垂直居中显示 + /// + public ModalBuilder Centered() { + AttributeIfNotEmpty( "[nzCentered]", _config.GetValue( UiConst.Centered ) ); + return this; + } + + /// + /// 配置内容 + /// + public ModalBuilder Content() { + AttributeIfNotEmpty( "[nzContent]", _config.GetValue( UiConst.Content ) ); + return this; + } + + /// + /// 配置组件参数 + /// + public ModalBuilder ComponentParams() { + AttributeIfNotEmpty( "[nzComponentParams]", _config.GetValue( UiConst.ComponentParams ) ); + return this; + } + + /// + /// 配置底部内容 + /// + public ModalBuilder Footer() { + AttributeIfNotEmpty( "[nzFooter]", _config.GetValue( UiConst.Footer ) ); + return this; + } + + /// + /// 配置z-index + /// + public ModalBuilder ZIndex() { + AttributeIfNotEmpty( "[nzZIndex]", _config.GetValue( UiConst.ZIndex ) ); + return this; + } + + /// + /// 配置宽度 + /// + public ModalBuilder Width() { + AttributeIfNotEmpty( "[nzWidth]", _config.GetValue( UiConst.Width ) ); + return this; + } + + /// + /// 配置对话框外层容器样式类名 + /// + public ModalBuilder WrapClassName() { + AttributeIfNotEmpty( "nzWrapClassName", _config.GetValue( UiConst.WrapClassName ) ); + AttributeIfNotEmpty( "[nzWrapClassName]", _config.GetValue( AngularConst.BindWrapClassName ) ); + return this; + } + + /// + /// 配置对话框样式类名 + /// + public ModalBuilder ClassName() { + AttributeIfNotEmpty( "nzClassName", _config.GetValue( UiConst.ClassName ) ); + AttributeIfNotEmpty( "[nzClassName]", _config.GetValue( AngularConst.BindClassName ) ); + return this; + } + + /// + /// 配置浮层样式 + /// + public ModalBuilder Style() { + AttributeIfNotEmpty( "[nzStyle]", _config.GetValue( UiConst.NzStyle ) ); + return this; + } + + /// + /// 配置关闭图标 + /// + public ModalBuilder CloseIcon() { + AttributeIfNotEmpty( "nzCloseIcon", _config.GetValue( UiConst.CloseIcon )?.Description() ); + AttributeIfNotEmpty( "[nzCloseIcon]", _config.GetValue( AngularConst.BindCloseIcon ) ); + return this; + } + + /// + /// 配置遮罩样式 + /// + public ModalBuilder MaskStyle() { + AttributeIfNotEmpty( "[nzMaskStyle]", _config.GetValue( UiConst.MaskStyle ) ); + return this; + } + + /// + /// 配置主体样式 + /// + public ModalBuilder BodyStyle() { + AttributeIfNotEmpty( "[nzBodyStyle]", _config.GetValue( UiConst.BodyStyle ) ); + return this; + } + + /// + /// 配置确认按钮文字 + /// + public ModalBuilder OkText() { + AttributeIfNotEmpty( "nzOkText", _config.GetValue( UiConst.OkText ) ); + AttributeIfNotEmpty( "[nzOkText]", _config.GetValue( AngularConst.BindOkText ) ); + return this; + } + + /// + /// 配置取消按钮文字 + /// + public ModalBuilder CancelText() { + AttributeIfNotEmpty( "nzCancelText", _config.GetValue( UiConst.CancelText ) ); + AttributeIfNotEmpty( "[nzCancelText]", _config.GetValue( AngularConst.BindCancelText ) ); + return this; + } + + /// + /// 配置确认按钮类型 + /// + public ModalBuilder OkType() { + AttributeIfNotEmpty( "nzOkType", _config.GetValue( UiConst.OkType )?.Description() ); + AttributeIfNotEmpty( "[nzOkType]", _config.GetValue( AngularConst.BindOkType ) ); + return this; + } + + /// + /// 配置确认按钮是否危险按钮 + /// + public ModalBuilder OkDanger() { + AttributeIfNotEmpty( "[nzOkDanger]", _config.GetValue( UiConst.OkDanger ) ); + return this; + } + + /// + /// 配置图标类型 + /// + public ModalBuilder IconType() { + AttributeIfNotEmpty( "nzIconType", _config.GetValue( UiConst.IconType )?.Description() ); + AttributeIfNotEmpty( "[nzIconType]", _config.GetValue( AngularConst.BindIconType ) ); + return this; + } + + /// + /// 配置自动聚焦 + /// + public ModalBuilder Autofocus() { + AttributeIfNotEmpty( "nzAutofocus", _config.GetValue( UiConst.Autofocus )?.Description() ); + AttributeIfNotEmpty( "[nzAutofocus]", _config.GetValue( AngularConst.BindAutofocus ) ); + return this; + } + + /// + /// 配置事件 + /// + public ModalBuilder Events() { + AttributeIfNotEmpty( "(nzOnOk)", _config.GetValue( UiConst.OnOk ) ); + AttributeIfNotEmpty( "(nzOnCancel)", _config.GetValue( UiConst.OnCancel ) ); + AttributeIfNotEmpty( "(nzAfterOpen)", _config.GetValue( UiConst.OnAfterOpen ) ); + AttributeIfNotEmpty( "(nzAfterClose)", _config.GetValue( UiConst.OnAfterClose ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Mask().MaskClosable().CloseOnNavigation().Visible().Closable().Draggable() + .OkLoading().OkDisabled().CancelLoading().CancelDisabled() + .Keyboard().Centered().Content().ComponentParams().Footer() + .ZIndex().Width().WrapClassName().ClassName().Style() + .Title().CloseIcon().MaskStyle().BodyStyle().OkText() + .CancelText().OkType().OkDanger().IconType().Autofocus() + .Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Modals/Builders/ModalContentBuilder.cs b/src/Util.Ui.NgZorro/Components/Modals/Builders/ModalContentBuilder.cs new file mode 100644 index 000000000..4a4b8c442 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Modals/Builders/ModalContentBuilder.cs @@ -0,0 +1,16 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Modals.Builders; + +/// +/// 对话框内容标签生成器 +/// +public class ModalContentBuilder : AngularTagBuilder { + /// + /// 初始化对话框内容标签生成器 + /// + /// 配置 + public ModalContentBuilder( Config config ) : base( config,"div" ) { + base.Attribute( "*nzModalContent" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Modals/Builders/ModalFooterBuilder.cs b/src/Util.Ui.NgZorro/Components/Modals/Builders/ModalFooterBuilder.cs new file mode 100644 index 000000000..8bcd025b8 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Modals/Builders/ModalFooterBuilder.cs @@ -0,0 +1,17 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Modals.Builders; + +/// +/// 对话框页脚标签生成器 +/// +public class ModalFooterBuilder : AngularTagBuilder { + /// + /// 初始化对话框页脚标签生成器 + /// + /// 配置 + public ModalFooterBuilder( Config config ) : base( config,"div" ) { + base.Attribute( "*nzModalFooter" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Modals/Builders/ModalTitleBuilder.cs b/src/Util.Ui.NgZorro/Components/Modals/Builders/ModalTitleBuilder.cs new file mode 100644 index 000000000..32c72f5d3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Modals/Builders/ModalTitleBuilder.cs @@ -0,0 +1,16 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Modals.Builders; + +/// +/// 对话框标题标签生成器 +/// +public class ModalTitleBuilder : AngularTagBuilder { + /// + /// 初始化对话框标题标签生成器 + /// + /// 配置 + public ModalTitleBuilder( Config config ) : base( config,"div" ) { + base.Attribute( "*nzModalTitle" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Modals/Configs/ModalShareConfig.cs b/src/Util.Ui.NgZorro/Components/Modals/Configs/ModalShareConfig.cs new file mode 100644 index 000000000..af3ccac43 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Modals/Configs/ModalShareConfig.cs @@ -0,0 +1,7 @@ +namespace Util.Ui.NgZorro.Components.Modals.Configs; + +/// +/// 对话框共享配置 +/// +public class ModalShareConfig { +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Modals/Helpers/ModalService.cs b/src/Util.Ui.NgZorro/Components/Modals/Helpers/ModalService.cs new file mode 100644 index 000000000..b5909518e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Modals/Helpers/ModalService.cs @@ -0,0 +1,40 @@ +using Util.Ui.NgZorro.Components.Modals.Configs; + +namespace Util.Ui.NgZorro.Components.Modals.Helpers; + +/// +/// 对话框服务 +/// +public class ModalService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 对话框共享配置 + /// + private ModalShareConfig _shareConfig; + + /// + /// 初始化对话框服务 + /// + /// 配置 + public ModalService( Config config ) { + _config = config; + } + + /// + /// 初始化 + /// + public void Init() { + CreateModalShareConfig(); + } + + /// + /// 创建对话框共享配置 + /// + private void CreateModalShareConfig() { + _shareConfig = new ModalShareConfig(); + _config.SetValueToItems( _shareConfig ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Modals/ModalContentTagHelper.cs b/src/Util.Ui.NgZorro/Components/Modals/ModalContentTagHelper.cs new file mode 100644 index 000000000..e4ca845e1 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Modals/ModalContentTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Modals.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Modals; + +/// +/// 对话框内容,生成的标签为<div *nzModalContent></div> +/// +[HtmlTargetElement( "util-modal-content" )] +public class ModalContentTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ModalContentRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Modals/ModalFooterTagHelper.cs b/src/Util.Ui.NgZorro/Components/Modals/ModalFooterTagHelper.cs new file mode 100644 index 000000000..f810eb027 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Modals/ModalFooterTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Modals.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Modals; + +/// +/// 对话框页脚,生成的标签为<div *nzModalFooter></div> +/// +[HtmlTargetElement( "util-modal-footer" )] +public class ModalFooterTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ModalFooterRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Modals/ModalTagHelper.cs b/src/Util.Ui.NgZorro/Components/Modals/ModalTagHelper.cs new file mode 100644 index 000000000..0ffb1239d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Modals/ModalTagHelper.cs @@ -0,0 +1,204 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Modals.Helpers; +using Util.Ui.NgZorro.Components.Modals.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Modals; + +/// +/// 对话框,生成的标签为<nz-modal></nz-modal> +/// +[HtmlTargetElement( "util-modal" )] +public class ModalTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// nzTitle,标题,支持多语言 + /// + public string Title { get; set; } + /// + /// [nzTitle],标题,类型: string | TemplateRef + /// + public string BindTitle { get; set; } + /// + /// [nzMask],是否显示遮罩,类型: boolean, 默认值: true + /// + public string Mask { get; set; } + /// + /// [nzMaskClosable],点击遮罩是否允许关闭,类型: boolean, 默认值: true + /// + public string MaskClosable { get; set; } + /// + /// [nzCloseOnNavigation],当用户在历史中前进/后退时是否关闭对话框, 类型: boolean, 默认值: true + /// + public string CloseOnNavigation { get; set; } + /// + /// [(nzVisible)],是否可见,类型: boolean, 默认值: false + /// + public string Visible { get; set; } + /// + /// [nzClosable],是否显示关闭按钮,类型: boolean, 默认值: true + /// + public string Closable { get; set; } + /// + /// [nzDraggable],是否可拖动,类型: boolean, 默认值: false + /// + public string Draggable { get; set; } + /// + /// [nzOkLoading],确定按钮是否加载状态,类型: boolean, 默认值: false + /// + public string OkLoading { get; set; } + /// + /// [nzOkDisabled],是否禁用确定按钮,类型: boolean, 默认值: false + /// + public string OkDisabled { get; set; } + /// + /// [nzCancelLoading],取消按钮是否加载状态,类型: boolean, 默认值: false + /// + public string CancelLoading { get; set; } + /// + /// [nzCancelDisabled],是否禁用取消按钮,默认值: false + /// + public string CancelDisabled { get; set; } + /// + /// [nzKeyboard],按下ESC键是否允许关闭, 类型: boolean, 默认值: true + /// + public string Keyboard { get; set; } + /// + /// [nzCentered],是否垂直居中显示,类型: boolean, 默认值: false + /// + public string Centered { get; set; } + /// + /// [nzContent],内容,类型: string | TemplateRef | Component | ng-content + /// + public string Content { get; set; } + /// + /// [nzComponentParams],组件参数,当 nzContent 为组件时作为实例属性,为 TemplateRef 时作为模版变量,类型: object + /// + public string ComponentParams { get; set; } + /// + /// [nzFooter],底部内容,1. 仅在普通模式下有效。2. 可通过传入 ModalButtonOptions 来最大程度自定义按钮。3. 当不需要底部时,可以设为 null。类型: string | TemplateRef | ModalButtonOptions,默认为确定取消按钮. + /// + public string Footer { get; set; } + /// + /// [nzZIndex],z-index,类型: number,默认值: 1000 + /// + public string ZIndex { get; set; } + /// + /// [nzWidth],宽度, 使用数字时,默认单位为 px,类型: number | string, 默认值: 520 + /// + public string Width { get; set; } + /// + /// nzWrapClassName,对话框外层容器样式类名 + /// + public string WrapClassName { get; set; } + /// + /// [nzWrapClassName],对话框外层容器样式类名 + /// + public string BindWrapClassName { get; set; } + /// + /// nzClassName,对话框样式类名 + /// + public string ClassName { get; set; } + /// + /// [nzClassName],对话框样式类名 + /// + public string BindClassName { get; set; } + /// + /// [nzStyle],浮层样式,类型: object + /// + public string NzStyle { get; set; } + /// + /// nzCloseIcon,关闭按钮图标,类型: string | TemplateRef<void>,默认值: 'close' + /// + public AntDesignIcon CloseIcon { get; set; } + /// + /// [nzCloseIcon],关闭按钮图标,类型: string | TemplateRef<void>,默认值: 'close' + /// + public string BindCloseIcon { get; set; } + /// + /// [nzMaskStyle],遮罩样式,类型: object + /// + public string MaskStyle { get; set; } + /// + /// [nzBodyStyle],主体样式,类型: object + /// + public string BodyStyle { get; set; } + /// + /// nzOkText,确认按钮文字,设为 null 表示不显示确认按钮,若在普通模式下使用了 nzFooter 参数,则该值无效,类型: string | null,默认值: '确定' + /// + public string OkText { get; set; } + /// + /// [nzOkText],确认按钮文字,设为 null 表示不显示确认按钮,若在普通模式下使用了 nzFooter 参数,则该值无效,类型: string | null,默认值: '确定' + /// + public string BindOkText { get; set; } + /// + /// nzCancelText,取消按钮文字,设为 null 表示不显示取消按钮,若在普通模式下使用了 nzFooter 参数,则该值无效,类型: string | null,默认值: '取消' + /// + public string CancelText { get; set; } + /// + /// [nzCancelText],取消按钮文字,设为 null 表示不显示取消按钮,若在普通模式下使用了 nzFooter 参数,则该值无效,类型: string | null,默认值: '取消' + /// + public string BindCancelText { get; set; } + /// + /// nzOkType,确认按钮类型,可选值: 'primary'|'dashed'|'link'|'text',默认值: 'primary' + /// + public ButtonType OkType { get; set; } + /// + /// [nzOkType],确认按钮类型,可选值: 'primary'|'dashed'|'link'|'text',默认值: 'primary' + /// + public string BindOkType { get; set; } + /// + /// [nzOkDanger],确认按钮是否危险按钮,类型: boolean, 默认值: false + /// + public string OkDanger { get; set; } + /// + /// nzIconType,图标类型,仅确认框模式下有效,默认值: 'question-circle' + /// + public AntDesignIcon IconType { get; set; } + /// + /// [nzIconType],图标类型,仅确认框模式下有效,默认值: 'question-circle' + /// + public string BindIconType { get; set; } + /// + /// nzAutofocus,自动聚焦及聚焦位置,为 null 时禁用,可选值: 'ok' | 'cancel' | 'auto' | null,默认值: 'auto' + /// + public ModalAutofocus Autofocus { get; set; } + /// + /// [nzAutofocus],自动聚焦及聚焦位置,为 null 时禁用,可选值: 'ok' | 'cancel' | 'auto' | null,默认值: 'auto' + /// + public string BindAutofocus { get; set; } + /// + /// (nzOnOk),确定事件,点击确定按钮时触发,类型: EventEmitter<any> + /// + public string OnOk { get; set; } + /// + /// (nzOnCancel),取消事件,点击取消按钮时触发,类型: EventEmitter<any> + /// + public string OnCancel { get; set; } + /// + /// (nzAfterOpen),打开后事件,打开对话框后触发, 类型: EventEmitter<void> + /// + public string OnAfterOpen { get; set; } + /// + /// (nzAfterClose),关闭后事件,对话框完全关闭后触发,类型: EventEmitter<any> + /// + public string OnAfterClose { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new ModalService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new ModalRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Modals/ModalTitleTagHelper.cs b/src/Util.Ui.NgZorro/Components/Modals/ModalTitleTagHelper.cs new file mode 100644 index 000000000..227dc0f8b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Modals/ModalTitleTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Modals.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Modals; + +/// +/// 对话框标题,生成的标签为<div *nzModalTitle></div> +/// +[HtmlTargetElement( "util-modal-title" )] +public class ModalTitleTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ModalTitleRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Modals/Renders/ModalContentRender.cs b/src/Util.Ui.NgZorro/Components/Modals/Renders/ModalContentRender.cs new file mode 100644 index 000000000..2583800d5 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Modals/Renders/ModalContentRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Modals.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Modals.Renders; + +/// +/// 对话框内容渲染器 +/// +public class ModalContentRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化对话框内容渲染器 + /// + /// 配置 + public ModalContentRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ModalContentBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ModalContentRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Modals/Renders/ModalFooterRender.cs b/src/Util.Ui.NgZorro/Components/Modals/Renders/ModalFooterRender.cs new file mode 100644 index 000000000..dff8aea5d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Modals/Renders/ModalFooterRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Modals.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Modals.Renders; + +/// +/// 对话框页脚渲染器 +/// +public class ModalFooterRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化对话框页脚渲染器 + /// + /// 配置 + public ModalFooterRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ModalFooterBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ModalFooterRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Modals/Renders/ModalRender.cs b/src/Util.Ui.NgZorro/Components/Modals/Renders/ModalRender.cs new file mode 100644 index 000000000..ae9e05c58 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Modals/Renders/ModalRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Modals.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Modals.Renders; + +/// +/// 对话框渲染器 +/// +public class ModalRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化对话框渲染器 + /// + /// 配置 + public ModalRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ModalBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ModalRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Modals/Renders/ModalTitleRender.cs b/src/Util.Ui.NgZorro/Components/Modals/Renders/ModalTitleRender.cs new file mode 100644 index 000000000..2d12c029b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Modals/Renders/ModalTitleRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Modals.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Modals.Renders; + +/// +/// 对话框标题渲染器 +/// +public class ModalTitleRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化对话框标题渲染器 + /// + /// 配置 + public ModalTitleRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ModalTitleBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ModalTitleRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderBuilder.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderBuilder.cs new file mode 100644 index 000000000..d6d0c10e4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderBuilder.cs @@ -0,0 +1,112 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.PageHeaders.Builders; + +/// +/// 页头标签生成器 +/// +public class PageHeaderBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 初始化页头标签生成器 + /// + /// 配置 + public PageHeaderBuilder( Config config ) : base( config, "nz-page-header" ) { + _config = config; + } + + /// + /// 配置透明背景 + /// + public PageHeaderBuilder Ghost() { + AttributeIfNotEmpty( "[nzGhost]", _config.GetValue( UiConst.Ghost ) ); + return this; + } + + /// + /// 配置标题 + /// + public PageHeaderBuilder Title() { + SetTitle( _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 设置标题 + /// + private void SetTitle( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzTitle]", value ); + return; + } + AttributeIfNotEmpty( "nzTitle", value ); + } + + /// + /// 配置子标题 + /// + public PageHeaderBuilder Subtitle() { + SetSubtitle( _config.GetValue( UiConst.Subtitle ) ); + AttributeIfNotEmpty( "[nzSubtitle]", _config.GetValue( AngularConst.BindSubtitle ) ); + return this; + } + + /// + /// 设置子标题 + /// + private void SetSubtitle( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzSubtitle]", value ); + return; + } + AttributeIfNotEmpty( "nzSubtitle", value ); + } + + /// + /// 配置显示返回按钮 + /// + public PageHeaderBuilder ShowBack() { + if ( _config.Contains( UiConst.BackIcon ) ) + return this; + if ( _config.Contains( AngularConst.BindBackIcon ) ) + return this; + if ( _config.GetValue( UiConst.ShowBack ) == true ) + Attribute( "nzBackIcon" ); + return this; + } + + /// + /// 配置返回按钮图标 + /// + public PageHeaderBuilder BackIcon() { + AttributeIfNotEmpty( "nzBackIcon", _config.GetValue( UiConst.BackIcon )?.Description() ); + AttributeIfNotEmpty( "[nzBackIcon]", _config.GetValue( AngularConst.BindBackIcon ) ); + return this; + } + + /// + /// 配置事件 + /// + public PageHeaderBuilder Events() { + AttributeIfNotEmpty( "(nzBack)", _config.GetValue( UiConst.OnBack ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Ghost().Title().Subtitle().ShowBack().BackIcon().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderContentBuilder.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderContentBuilder.cs new file mode 100644 index 000000000..0629e4db2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderContentBuilder.cs @@ -0,0 +1,16 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.PageHeaders.Builders; + +/// +/// 页头内容标签生成器 +/// +public class PageHeaderContentBuilder : AngularTagBuilder { + /// + /// 初始化页头内容标签生成器 + /// + /// 配置 + public PageHeaderContentBuilder( Config config ) : base( config,"nz-page-header-content" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderExtraBuilder.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderExtraBuilder.cs new file mode 100644 index 000000000..fc5a5fc6e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderExtraBuilder.cs @@ -0,0 +1,16 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.PageHeaders.Builders; + +/// +/// 页头标题行尾操作区标签生成器 +/// +public class PageHeaderExtraBuilder : AngularTagBuilder { + /// + /// 初始化页头标题行尾操作区标签生成器 + /// + /// 配置 + public PageHeaderExtraBuilder( Config config ) : base( config,"nz-page-header-extra" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderFooterBuilder.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderFooterBuilder.cs new file mode 100644 index 000000000..257fa61c6 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderFooterBuilder.cs @@ -0,0 +1,16 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.PageHeaders.Builders; + +/// +/// 页头底部标签生成器 +/// +public class PageHeaderFooterBuilder : AngularTagBuilder { + /// + /// 初始化页头底部标签生成器 + /// + /// 配置 + public PageHeaderFooterBuilder( Config config ) : base( config,"nz-page-header-footer" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderSubTitleBuilder.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderSubTitleBuilder.cs new file mode 100644 index 000000000..cca19359b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderSubTitleBuilder.cs @@ -0,0 +1,16 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.PageHeaders.Builders; + +/// +/// 页头子标题标签生成器 +/// +public class PageHeaderSubTitleBuilder : AngularTagBuilder { + /// + /// 初始化页头子标题标签生成器 + /// + /// 配置 + public PageHeaderSubTitleBuilder( Config config ) : base( config,"nz-page-header-subtitle" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderTagsBuilder.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderTagsBuilder.cs new file mode 100644 index 000000000..a7f5a95d5 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderTagsBuilder.cs @@ -0,0 +1,16 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.PageHeaders.Builders; + +/// +/// 页头标签列表标签生成器 +/// +public class PageHeaderTagsBuilder : AngularTagBuilder { + /// + /// 初始化页头标签列表标签生成器 + /// + /// 配置 + public PageHeaderTagsBuilder( Config config ) : base( config,"nz-page-header-tags" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderTitleBuilder.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderTitleBuilder.cs new file mode 100644 index 000000000..bc4938136 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/Builders/PageHeaderTitleBuilder.cs @@ -0,0 +1,16 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.PageHeaders.Builders; + +/// +/// 页头标题标签生成器 +/// +public class PageHeaderTitleBuilder : AngularTagBuilder { + /// + /// 初始化页头标题标签生成器 + /// + /// 配置 + public PageHeaderTitleBuilder( Config config ) : base( config, "nz-page-header-title" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/Configs/PageHeaderShareConfig.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/Configs/PageHeaderShareConfig.cs new file mode 100644 index 000000000..b5a1ca60a --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/Configs/PageHeaderShareConfig.cs @@ -0,0 +1,7 @@ +namespace Util.Ui.NgZorro.Components.PageHeaders.Configs; + +/// +/// 页头共享配置 +/// +public class PageHeaderShareConfig { +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderContentTagHelper.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderContentTagHelper.cs new file mode 100644 index 000000000..049267267 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderContentTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.PageHeaders.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.PageHeaders; + +/// +/// 页头内容,生成的标签为<nz-page-header-content></nz-page-header-content> +/// +[HtmlTargetElement( "util-page-header-content" )] +public class PageHeaderContentTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new PageHeaderContentRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderExtraTagHelper.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderExtraTagHelper.cs new file mode 100644 index 000000000..0c2497983 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderExtraTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.PageHeaders.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.PageHeaders; + +/// +/// 页头标题行尾操作区,生成的标签为<nz-page-header-extra></nz-page-header-extra> +/// +[HtmlTargetElement( "util-page-header-extra" )] +public class PageHeaderExtraTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new PageHeaderExtraRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderFooterTagHelper.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderFooterTagHelper.cs new file mode 100644 index 000000000..aff4fa8ac --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderFooterTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.PageHeaders.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.PageHeaders; + +/// +/// 页头底部,生成的标签为<nz-page-header-footer></nz-page-header-footer> +/// +[HtmlTargetElement( "util-page-header-footer" )] +public class PageHeaderFooterTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new PageHeaderFooterRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderSubTitleTagHelper.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderSubTitleTagHelper.cs new file mode 100644 index 000000000..210f6f236 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderSubTitleTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.PageHeaders.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.PageHeaders; + +/// +/// 页头子标题,生成的标签为<nz-page-header-subtitle></nz-page-header-subtitle> +/// +[HtmlTargetElement( "util-page-header-subtitle" )] +public class PageHeaderSubTitleTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new PageHeaderSubTitleRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderTagHelper.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderTagHelper.cs new file mode 100644 index 000000000..f000dfedd --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderTagHelper.cs @@ -0,0 +1,67 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.PageHeaders.Configs; +using Util.Ui.NgZorro.Components.PageHeaders.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.PageHeaders; + +/// +/// 页头,生成的标签为<nz-page-header></nz-page-header> +/// +[HtmlTargetElement( "util-page-header" )] +public class PageHeaderTagHelper : AngularTagHelperBase { + /// + /// [nzGhost],是否使背景色透明,默认值:true + /// + public string Ghost { get; set; } + /// + /// nzTitle,标题, 支持多语言 + /// + public string Title { get; set; } + /// + /// [nzTitle],标题 + /// + public string BindTitle { get; set; } + /// + /// nzSubtitle,子标题, 支持多语言 + /// + public string Subtitle { get; set; } + /// + /// [nzSubtitle],子标题 + /// + public string BindSubtitle { get; set; } + /// + /// 扩展属性,是否显示默认返回按钮, 默认值: false + /// + public bool ShowBack { get; set; } + /// + /// nzBackIcon,设置返回按钮图标 + /// + public AntDesignIcon BackIcon { get; set; } + /// + /// [nzBackIcon],设置返回按钮图标 + /// + public string BindBackIcon { get; set; } + /// + /// (nzBack),返回按钮点击事件, 类型: EventEmitter<void> + /// + public string OnBack { get; set; } + + /// + /// 渲染前操作 + /// + /// 上下文 + /// 输出 + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + context.SetValueToItems( new PageHeaderShareConfig() ); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new PageHeaderRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderTagsTagHelper.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderTagsTagHelper.cs new file mode 100644 index 000000000..f8eb2d590 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderTagsTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.PageHeaders.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.PageHeaders; + +/// +/// 页头标签列表,生成的标签为<nz-page-header-tags></nz-page-header-tags> +/// +[HtmlTargetElement( "util-page-header-tags" )] +public class PageHeaderTagsTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new PageHeaderTagsRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderTitleTagHelper.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderTitleTagHelper.cs new file mode 100644 index 000000000..705e427de --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/PageHeaderTitleTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.PageHeaders.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.PageHeaders; + +/// +/// 页头标题,生成的标签为<nz-page-header-title></nz-page-header-title> +/// +[HtmlTargetElement( "util-page-header-title" )] +public class PageHeaderTitleTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new PageHeaderTitleRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderContentRender.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderContentRender.cs new file mode 100644 index 000000000..0d1ca8ed8 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderContentRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.PageHeaders.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.PageHeaders.Renders; + +/// +/// 页头内容 +/// +public class PageHeaderContentRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化页头内容 + /// + /// 配置 + public PageHeaderContentRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new PageHeaderContentBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new PageHeaderContentRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderExtraRender.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderExtraRender.cs new file mode 100644 index 000000000..fbe9ff5e7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderExtraRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.PageHeaders.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.PageHeaders.Renders; + +/// +/// 页头标题行尾操作区渲染器 +/// +public class PageHeaderExtraRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化页头标题行尾操作区渲染器 + /// + /// 配置 + public PageHeaderExtraRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new PageHeaderExtraBuilder(_config); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new PageHeaderExtraRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderFooterRender.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderFooterRender.cs new file mode 100644 index 000000000..c7ce42c1e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderFooterRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.PageHeaders.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.PageHeaders.Renders; + +/// +/// 页头底部渲染器 +/// +public class PageHeaderFooterRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化页头底部渲染器 + /// + /// 配置 + public PageHeaderFooterRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new PageHeaderFooterBuilder(_config); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new PageHeaderFooterRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderRender.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderRender.cs new file mode 100644 index 000000000..d2782b170 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.PageHeaders.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.PageHeaders.Renders; + +/// +/// 页头渲染器 +/// +public class PageHeaderRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化页头渲染器 + /// + /// 配置 + public PageHeaderRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new PageHeaderBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new PageHeaderRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderSubTitleRender.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderSubTitleRender.cs new file mode 100644 index 000000000..ddea54261 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderSubTitleRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.PageHeaders.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.PageHeaders.Renders; + +/// +/// 页头子标题渲染器 +/// +public class PageHeaderSubTitleRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化页头子标题渲染器 + /// + /// 配置 + public PageHeaderSubTitleRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new PageHeaderSubTitleBuilder(_config); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new PageHeaderSubTitleRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderTagsRender.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderTagsRender.cs new file mode 100644 index 000000000..b1f714165 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderTagsRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.PageHeaders.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.PageHeaders.Renders; + +/// +/// 页头标签列表渲染器 +/// +public class PageHeaderTagsRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化页头标签列表渲染器 + /// + /// 配置 + public PageHeaderTagsRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new PageHeaderTagsBuilder(_config); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new PageHeaderTagsRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderTitleRender.cs b/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderTitleRender.cs new file mode 100644 index 000000000..6565a6121 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/PageHeaders/Renders/PageHeaderTitleRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.PageHeaders.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.PageHeaders.Renders; + +/// +/// 页头标题渲染器 +/// +public class PageHeaderTitleRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化页头标题渲染器 + /// + /// 配置 + public PageHeaderTitleRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new PageHeaderTitleBuilder(_config); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new PageHeaderTitleRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Paginations/Builders/PaginationBuilder.cs b/src/Util.Ui.NgZorro/Components/Paginations/Builders/PaginationBuilder.cs new file mode 100644 index 000000000..2154c5918 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Paginations/Builders/PaginationBuilder.cs @@ -0,0 +1,149 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Paginations.Builders; + +/// +/// 分页标签生成器 +/// +public class PaginationBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化分页标签生成器 + /// + /// 配置 + public PaginationBuilder( Config config ) : base( config,"nz-pagination" ) { + _config = config; + } + + /// + /// 配置总数 + /// + public PaginationBuilder Total() { + AttributeIfNotEmpty( "[nzTotal]", _config.GetValue( UiConst.Total ) ); + return this; + } + + /// + /// 配置当前页 + /// + public PaginationBuilder PageIndex() { + AttributeIfNotEmpty( "[nzPageIndex]", _config.GetValue( UiConst.PageIndex ) ); + AttributeIfNotEmpty( "[(nzPageIndex)]", _config.GetValue( AngularConst.BindonPageIndex ) ); + return this; + } + + /// + /// 配置每页显示行数 + /// + public PaginationBuilder PageSize() { + AttributeIfNotEmpty( "[nzPageSize]", _config.GetValue( UiConst.PageSize ) ); + AttributeIfNotEmpty( "[(nzPageSize)]", _config.GetValue( AngularConst.BindonPageSize ) ); + return this; + } + + /// + /// 配置显示改变分页大小按钮 + /// + public PaginationBuilder ShowSizeChanger() { + AttributeIfNotEmpty( "[nzShowSizeChanger]", _config.GetValue( UiConst.ShowSizeChanger ) ); + return this; + } + + /// + /// 配置显示快速跳转 + /// + public PaginationBuilder ShowQuickJumper() { + AttributeIfNotEmpty( "[nzShowQuickJumper]", _config.GetValue( UiConst.ShowQuickJumper ) ); + return this; + } + + /// + /// 配置禁用 + /// + public PaginationBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置尺寸 + /// + public PaginationBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置显示总行数和当前数据范围的模板 + /// + public PaginationBuilder ShowTotal() { + AttributeIfNotEmpty( "[nzShowTotal]", _config.GetValue( UiConst.ShowTotal ) ); + return this; + } + + /// + /// 配置简单分页 + /// + public PaginationBuilder Simple() { + AttributeIfNotEmpty( "[nzSimple]", _config.GetValue( UiConst.Simple ) ); + return this; + } + + /// + /// 配置响应式 + /// + public PaginationBuilder Responsive() { + AttributeIfNotEmpty( "[nzResponsive]", _config.GetValue( UiConst.Responsive ) ); + return this; + } + + /// + /// 配置每页行数选择列表 + /// + public PaginationBuilder PageSizeOptions() { + AttributeIfNotEmpty( "[nzPageSizeOptions]", _config.GetValue( UiConst.PageSizeOptions ) ); + return this; + } + + /// + /// 配置自定义页码结构 + /// + public PaginationBuilder ItemRender() { + AttributeIfNotEmpty( "[nzItemRender]", _config.GetValue( UiConst.ItemRender ) ); + return this; + } + + /// + /// 配置只有一页时是否隐藏分页器 + /// + public PaginationBuilder HideOnSinglePage() { + AttributeIfNotEmpty( "[nzHideOnSinglePage]", _config.GetValue( UiConst.HideOnSinglePage ) ); + return this; + } + + /// + /// 配置事件 + /// + public PaginationBuilder Events() { + AttributeIfNotEmpty( "(nzPageIndexChange)", _config.GetValue( UiConst.OnPageIndexChange ) ); + AttributeIfNotEmpty( "(nzPageSizeChange)", _config.GetValue( UiConst.OnPageSizeChange ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Total().PageIndex().PageSize().ShowSizeChanger().ShowQuickJumper().Disabled().Size() + .ShowTotal().Simple().Responsive().PageSizeOptions().ItemRender().HideOnSinglePage() + .Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Paginations/PaginationTagHelper.cs b/src/Util.Ui.NgZorro/Components/Paginations/PaginationTagHelper.cs new file mode 100644 index 000000000..044dd8cf1 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Paginations/PaginationTagHelper.cs @@ -0,0 +1,92 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Paginations.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Paginations; + +/// +/// 分页,生成的标签为<nz-pagination></nz-pagination> +/// +[HtmlTargetElement( "util-pagination" )] +public class PaginationTagHelper : AngularTagHelperBase { + /// + /// [nzTotal],总行数 + /// + public string Total { get; set; } + /// + /// [nzPageIndex],当前页, 默认值: 1 + /// + public string PageIndex { get; set; } + /// + /// [(nzPageIndex)],当前页, 默认值: 1 + /// + public string BindonPageIndex { get; set; } + /// + /// [nzPageSize],每页显示行数, 默认值: 10 + /// + public string PageSize { get; set; } + /// + /// [(nzPageSize)],每页显示行数, 默认值: 10 + /// + public string BindonPageSize { get; set; } + /// + /// [nzShowSizeChanger],是否显示改变分页大小按钮, 默认值: false + /// + public string ShowSizeChanger { get; set; } + /// + /// [nzShowQuickJumper],是否显示快速跳转, 默认值: false + /// + public string ShowQuickJumper { get; set; } + /// + /// [nzDisabled],是否禁用, 默认值: false + /// + public string Disabled { get; set; } + /// + /// nzSize,分页尺寸,可选值: 'default' | 'small' + /// + public PaginationSize Size { get; set; } + /// + /// [nzSize],分页尺寸,可选值: 'default' | 'small' + /// + public string BindSize { get; set; } + /// + /// [nzShowTotal],自定义显示总行数和当前数据范围, 类型: TemplateRef<{ $implicit: number, range: [ number, number ] }> + /// + public string ShowTotal { get; set; } + /// + /// [nzSimple],是否显示为简单分页, 类型: boolean + /// + public string Simple { get; set; } + /// + /// [nzResponsive],是否响应式,根据屏幕宽度自动调整尺寸,未指定 nzSize 时有效, 类型: boolean, 默认值: false + /// + public string Responsive { get; set; } + /// + /// [nzPageSizeOptions],设置每页显示行数选择列表,默认值: [10, 20, 30, 40] + /// + public string PageSizeOptions { get; set; } + /// + /// [nzItemRender],自定义页码结构, 类型: TemplateRef<{ $implicit: 'page' \| 'prev' \| 'next'\| 'prev_5'\| 'next_5', page: number }> + /// + public string ItemRender { get; set; } + /// + /// [nzHideOnSinglePage],只有一页时是否隐藏分页器,类型: boolean, 默认值: false + /// + public string HideOnSinglePage { get; set; } + /// + /// (nzPageIndexChange),页码变化事件,类型: EventEmitter<number> + /// + public string OnPageIndexChange { get; set; } + /// + /// (nzPageSizeChange),每页显示行数变化事件,类型: EventEmitter<number> + /// + public string OnPageSizeChange { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new PaginationRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Paginations/Renders/PaginationRender.cs b/src/Util.Ui.NgZorro/Components/Paginations/Renders/PaginationRender.cs new file mode 100644 index 000000000..7da23f145 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Paginations/Renders/PaginationRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Paginations.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Paginations.Renders; + +/// +/// 分页渲染器 +/// +public class PaginationRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化分页渲染器 + /// + /// 配置 + public PaginationRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new PaginationBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new PaginationRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Progresses/Builders/ProgressBuilder.cs b/src/Util.Ui.NgZorro/Components/Progresses/Builders/ProgressBuilder.cs new file mode 100644 index 000000000..c44b26de3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Progresses/Builders/ProgressBuilder.cs @@ -0,0 +1,156 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Directives.Popconfirms; +using Util.Ui.NgZorro.Directives.Popover; +using Util.Ui.NgZorro.Directives.Tooltips; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Progresses.Builders; + +/// +/// 进度条标签生成器 +/// +public class ProgressBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化进度条标签生成器 + /// + /// 配置 + public ProgressBuilder( Config config ) : base( config,"nz-progress" ) { + _config = config; + } + + /// + /// 配置类型 + /// + public ProgressBuilder Type() { + AttributeIfNotEmpty( "nzType", _config.GetValue( UiConst.Type )?.Description() ); + AttributeIfNotEmpty( "[nzType]", _config.GetValue( AngularConst.BindType ) ); + return this; + } + + /// + /// 配置尺寸 + /// + public ProgressBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置格式化函数 + /// + public ProgressBuilder Format() { + AttributeIfNotEmpty( "[nzFormat]", _config.GetValue( UiConst.Format ) ); + return this; + } + + /// + /// 配置百分比 + /// + public ProgressBuilder Percent() { + AttributeIfNotEmpty( "[nzPercent]", _config.GetValue( UiConst.Percent ) ); + return this; + } + + /// + /// 配置是否显示进度信息 + /// + public ProgressBuilder ShowInfo() { + AttributeIfNotEmpty( "[nzShowInfo]", _config.GetValue( UiConst.ShowInfo ) ); + return this; + } + + /// + /// 配置状态 + /// + public ProgressBuilder Status() { + AttributeIfNotEmpty( "nzStatus", _config.GetValue( UiConst.Status )?.Description() ); + AttributeIfNotEmpty( "[nzStatus]", _config.GetValue( AngularConst.BindStatus ) ); + return this; + } + + /// + /// 配置端点形状 + /// + public ProgressBuilder StrokeLinecap() { + AttributeIfNotEmpty( "nzStrokeLinecap", _config.GetValue( UiConst.StrokeLinecap )?.Description() ); + AttributeIfNotEmpty( "[nzStrokeLinecap]", _config.GetValue( AngularConst.BindStrokeLinecap ) ); + return this; + } + + /// + /// 配置颜色 + /// + public ProgressBuilder StrokeColor() { + AttributeIfNotEmpty( "nzStrokeColor", _config.GetValue( UiConst.StrokeColorType )?.Description() ); + AttributeIfNotEmpty( "nzStrokeColor", _config.GetValue( UiConst.StrokeColor ) ); + AttributeIfNotEmpty( "[nzStrokeColor]", _config.GetValue( AngularConst.BindStrokeColor ) ); + return this; + } + + /// + /// 配置已完成分段百分比 + /// + public ProgressBuilder SuccessPercent() { + AttributeIfNotEmpty( "[nzSuccessPercent]", _config.GetValue( UiConst.SuccessPercent ) ); + return this; + } + + /// + /// 配置线条宽度 + /// + public ProgressBuilder StrokeWidth() { + AttributeIfNotEmpty( "[nzStrokeWidth]", _config.GetValue( UiConst.StrokeWidth ) ); + return this; + } + + /// + /// 配置总步数 + /// + public ProgressBuilder Steps() { + AttributeIfNotEmpty( "[nzSteps]", _config.GetValue( UiConst.Steps ) ); + return this; + } + + /// + /// 配置画布宽度 + /// + public ProgressBuilder Width() { + AttributeIfNotEmpty( "[nzWidth]", _config.GetValue( UiConst.Width ) ); + return this; + } + + /// + /// 配置缺口角度 + /// + public ProgressBuilder GapDegree() { + AttributeIfNotEmpty( "[nzGapDegree]", _config.GetValue( UiConst.GapDegree ) ); + return this; + } + + /// + /// 配置缺口位置 + /// + public ProgressBuilder GapPosition() { + AttributeIfNotEmpty( "nzGapPosition", _config.GetValue( UiConst.GapPosition )?.Description() ); + AttributeIfNotEmpty( "[nzGapPosition]", _config.GetValue( AngularConst.BindGapPosition ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + this.Tooltip( _config ).Popover( _config ).Popconfirm( _config ); + Type().Size().Format().Percent().ShowInfo().Status().StrokeLinecap() + .StrokeColor().SuccessPercent().StrokeWidth().Steps().Width() + .GapDegree().GapPosition(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Progresses/ProgressTagHelper.cs b/src/Util.Ui.NgZorro/Components/Progresses/ProgressTagHelper.cs new file mode 100644 index 000000000..3e0a07365 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Progresses/ProgressTagHelper.cs @@ -0,0 +1,104 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Progresses.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Progresses; + +/// +/// 进度条,生成的标签为<nz-progress></nz-progress> +/// +[HtmlTargetElement( "util-progress" )] +public class ProgressTagHelper : TooltipTagHelperBase { + /// + /// nzType,进度条类型,可选值: 'line' | 'circle' | 'dashboard',默认值: 'line' + /// + public ProgressType Type { get; set; } + /// + /// [nzType],进度条类型,可选值: 'line' | 'circle' | 'dashboard',默认值: 'line' + /// + public string BindType { get; set; } + /// + /// nzSize,尺寸,可选值: 'default' | 'small' ,默认值: 'default' + /// + public ProgressSize Size { get; set; } + /// + /// [nzSize],尺寸,可选值: 'default' | 'small' ,默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// [nzFormat],格式化函数,用于自定义显示内容,类型: (percent: number) => string | TemplateRef<{ $implicit: number }>,默认值: percent => percent + '%' + /// + public string Format { get; set; } + /// + /// [nzPercent],百分比,类型: number,默认值: 0 + /// + public string Percent { get; set; } + /// + /// [nzShowInfo],是否显示进度数值或状态图标,默认值: true + /// + public string ShowInfo { get; set; } + /// + /// nzStatus,进度条状态,可选值: 'success' | 'exception' | 'active' | 'normal' + /// + public ProgressStatus Status { get; set; } + /// + /// [nzStatus],进度条状态,可选值: 'success' | 'exception' | 'active' | 'normal' + /// + public string BindStatus { get; set; } + /// + /// nzStrokeLinecap,进度条端点形状,可选值: 'round' | 'square',默认值: 'round' + /// + public ProgressStrokeLinecap StrokeLinecap { get; set; } + /// + /// [nzStrokeLinecap],进度条端点形状,可选值: 'round' | 'square',默认值: 'round' + /// + public string BindStrokeLinecap { get; set; } + /// + /// nzStrokeColor,扩展属性, 进度条颜色 + /// + public AntDesignColor StrokeColorType { get; set; } + /// + /// nzStrokeColor,进度条颜色,传入对象时为渐变,类型: string | { from: string; to: string: direction: string; [percent: string]: string } + /// + public string StrokeColor { get; set; } + /// + /// [nzStrokeColor],进度条颜色,传入对象时为渐变,类型: string | { from: string; to: string: direction: string; [percent: string]: string } + /// + public string BindStrokeColor { get; set; } + /// + /// [nzSuccessPercent],已完成的分段百分比,类型: number,默认值: 0 + /// + public string SuccessPercent { get; set; } + /// + /// [nzStrokeWidth],线条宽度,当 nzType="line" 时,单位为像素,默认值为8。当 nzType为 "circle" 或 "dashboard" 时,单位是进度条画布宽度的百分比,默认值为6。 + /// + public string StrokeWidth { get; set; } + /// + /// [nzSteps],进度条总步数,仅在 nzType="line" 时有效,类型: number + /// + public string Steps { get; set; } + /// + /// [nzWidth],画布宽度,单位为像素,当 nzType为 "circle" 或 "dashboard" 时有效,默认值为132。 + /// + public string Width { get; set; } + /// + /// [nzGapDegree],仪表盘进度条缺口角度,可取值 0 ~ 360,当 nzType="dashboard" 时有效,默认值为0。 + /// + public string GapDegree { get; set; } + /// + /// nzGapPosition,表盘进度条缺口位置,当 nzType="dashboard" 时有效,可选值: 'top' | 'right' | 'bottom' | 'left',默认值: 'top' + /// + public ProgressGapPosition GapPosition { get; set; } + /// + /// [nzGapPosition],表盘进度条缺口位置,当 nzType="dashboard" 时有效,可选值: 'top' | 'right' | 'bottom' | 'left',默认值: 'top' + /// + public string BindGapPosition { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ProgressRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Progresses/Renders/ProgressRender.cs b/src/Util.Ui.NgZorro/Components/Progresses/Renders/ProgressRender.cs new file mode 100644 index 000000000..8bd3f57c8 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Progresses/Renders/ProgressRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Progresses.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Progresses.Renders; + +/// +/// 进度条渲染器 +/// +public class ProgressRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化进度条渲染器 + /// + /// 配置 + public ProgressRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ProgressBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ProgressRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/QrCodes/Builders/QrCodeBuilder.cs b/src/Util.Ui.NgZorro/Components/QrCodes/Builders/QrCodeBuilder.cs new file mode 100644 index 000000000..0ce45af01 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/QrCodes/Builders/QrCodeBuilder.cs @@ -0,0 +1,127 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.QrCodes.Builders; + +/// +/// 二维码标签生成器 +/// +public class QrCodeBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化二维码标签生成器 + /// + /// 配置 + public QrCodeBuilder( Config config ) : base( config, "nz-qrcode" ) { + _config = config; + } + + /// + /// 配置值 + /// + public QrCodeBuilder Value() { + AttributeIfNotEmpty( "nzValue", _config.GetValue( UiConst.Value ) ); + AttributeIfNotEmpty( "[nzValue]", _config.GetValue( AngularConst.BindValue ) ); + return this; + } + + /// + /// 配置二维码颜色 + /// + public QrCodeBuilder Color() { + AttributeIfNotEmpty( "nzColor", _config.GetValue( UiConst.Color ) ); + AttributeIfNotEmpty( "[nzColor]", _config.GetValue( AngularConst.BindColor ) ); + return this; + } + + /// + /// 配置背景色 + /// + public QrCodeBuilder BgColor() { + AttributeIfNotEmpty( "nzBgColor", _config.GetValue( UiConst.BgColor ) ); + AttributeIfNotEmpty( "[nzBgColor]", _config.GetValue( AngularConst.BindBgColor ) ); + return this; + } + + /// + /// 配置尺寸 + /// + public QrCodeBuilder Size() { + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( UiConst.Size ) ); + return this; + } + + /// + /// 配置填充 + /// + public QrCodeBuilder Padding() { + AttributeIfNotEmpty( "[nzPadding]", _config.GetValue( UiConst.Padding ) ); + return this; + } + + /// + /// 配置图标地址 + /// + public QrCodeBuilder Icon() { + AttributeIfNotEmpty( "nzIcon", _config.GetValue( UiConst.Icon ) ); + AttributeIfNotEmpty( "[nzIcon]", _config.GetValue( AngularConst.BindIcon ) ); + return this; + } + + /// + /// 配置图标尺寸 + /// + public QrCodeBuilder IconSize() { + AttributeIfNotEmpty( "[nzIconSize]", _config.GetValue( UiConst.IconSize ) ); + return this; + } + + /// + /// 配置边框 + /// + public QrCodeBuilder Bordered() { + AttributeIfNotEmpty( "[nzBordered]", _config.GetValue( UiConst.Bordered ) ); + return this; + } + + /// + /// 配置状态 + /// + public QrCodeBuilder Status() { + AttributeIfNotEmpty( "nzStatus", _config.GetValue( UiConst.Status ).Description() ); + AttributeIfNotEmpty( "[nzStatus]", _config.GetValue( AngularConst.BindStatus ) ); + return this; + } + + /// + /// 配置容错级别 + /// + public QrCodeBuilder Level() { + AttributeIfNotEmpty( "nzLevel", _config.GetValue( UiConst.Level ).Description() ); + AttributeIfNotEmpty( "[nzLevel]", _config.GetValue( AngularConst.BindLevel ) ); + return this; + } + + /// + /// 配置事件 + /// + public QrCodeBuilder Events() { + AttributeIfNotEmpty( "(nzRefresh)", _config.GetValue( UiConst.OnRefresh ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Value().Color().BgColor().Size().Padding().Icon().IconSize() + .Bordered().Status().Level() + .Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/QrCodes/QrCodeTagHelper.cs b/src/Util.Ui.NgZorro/Components/QrCodes/QrCodeTagHelper.cs new file mode 100644 index 000000000..71bce6501 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/QrCodes/QrCodeTagHelper.cs @@ -0,0 +1,88 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.QrCodes.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.QrCodes; + +/// +/// 二维码,生成的标签为<nz-qrcode></nz-qrcode> +/// +[HtmlTargetElement( "util-qrcode" )] +public class QrCodeTagHelper : AngularTagHelperBase { + /// + /// nzValue,值 + /// + public string Value { get; set; } + /// + /// [nzValue],值 + /// + public string BindValue { get; set; } + /// + /// nzColor,颜色, 默认值: #000 + /// + public string Color { get; set; } + /// + /// [nzColor],颜色, 默认值: #000 + /// + public string BindColor { get; set; } + /// + /// nzBgColor,背景色, 默认值: #FFFFFF + /// + public string BgColor { get; set; } + /// + /// [nzBgColor],背景色, 默认值: #FFFFFF + /// + public string BindBgColor { get; set; } + /// + /// [nzSize],尺寸, 类型: number, 默认值: 160 + /// + public string Size { get; set; } + /// + /// [nzPadding], 填充, 类型: number | number[] , 默认值: 0 + /// + public string Padding { get; set; } + /// + /// nzIcon,图标地址 + /// + public string Icon { get; set; } + /// + /// [nzIcon],图标地址 + /// + public string BindIcon { get; set; } + /// + /// [nzIconSize],图标尺寸,类型: number, 默认值: 40 + /// + public string IconSize { get; set; } + /// + /// [nzBordered],是否显示边框,类型: boolean, 默认值: true + /// + public string Bordered { get; set; } + /// + /// nzStatus,状态, 可选值: 'active'|'expired' |'loading' , 默认值: 'active' + /// + public QrCodeStatus Status { get; set; } + /// + /// [nzStatus],状态, 可选值: 'active'|'expired' |'loading' , 默认值: 'active' + /// + public string BindStatus { get; set; } + /// + /// nzLevel,容错级别, 可选值: 'L'|'M'|'Q'|'H', 默认值: 'M' + /// + public QrCodeCorrectionLevel Level { get; set; } + /// + /// [nzLevel],容错级别, 可选值: 'L'|'M'|'Q'|'H', 默认值: 'M' + /// + public string BindLevel { get; set; } + /// + /// (nzRefresh), "点击刷新"链接单击事件,类型: EventEmitter<string> + /// + public string OnRefresh { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new QrCodeRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/QrCodes/Renders/QrCodeRender.cs b/src/Util.Ui.NgZorro/Components/QrCodes/Renders/QrCodeRender.cs new file mode 100644 index 000000000..e2d0fdead --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/QrCodes/Renders/QrCodeRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.QrCodes.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.QrCodes.Renders; + +/// +/// 二维码渲染器 +/// +public class QrCodeRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化二维码渲染器 + /// + /// 配置 + public QrCodeRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new QrCodeBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new QrCodeRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Radios/Builders/RadioBuilder.cs b/src/Util.Ui.NgZorro/Components/Radios/Builders/RadioBuilder.cs new file mode 100644 index 000000000..54021d86d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Radios/Builders/RadioBuilder.cs @@ -0,0 +1,164 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Radios.Configs; +using Util.Ui.NgZorro.Components.Selects.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Radios.Builders; + +/// +/// 单选框标签生成器 +/// +public class RadioBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 单选框组合共享配置 + /// + private readonly RadioGroupShareConfig _shareConfig; + + /// + /// 初始化单选框标签生成器 + /// + /// 配置 + public RadioBuilder( Config config ) : base( config,"label" ) { + _config = config; + _shareConfig = GetShareConfig(); + } + + /// + /// 获取单选框组合共享配置 + /// + private RadioGroupShareConfig GetShareConfig() { + return _config.GetValueFromItems() ?? new RadioGroupShareConfig(); + } + + /// + /// 配置名称 + /// + public override RadioBuilder Name() { + if ( _shareConfig.IsAutoCreateRadioGroup == true ) + return this; + return base.Name(); + } + + /// + /// 配置自动获取焦点 + /// + public RadioBuilder AutoFocus() { + AttributeIfNotEmpty( "[nzAutoFocus]", _config.GetValue( UiConst.AutoFocus ) ); + return this; + } + + /// + /// 配置禁用 + /// + public RadioBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置值 + /// + public RadioBuilder Value() { + AttributeIfNotEmpty( "nzValue", _config.GetValue( UiConst.Value ) ); + AttributeIfNotEmpty( "[nzValue]", _config.GetValue( AngularConst.BindValue ) ); + return this; + } + + /// + /// 配置标签 + /// + public RadioBuilder Label() { + var options = NgZorroOptionsService.GetOptions(); + var text = _config.GetValue( UiConst.Label ); + if ( text.IsEmpty() == false ) { + if ( options.EnableI18n ) { + this.AppendContentByI18n( text ); + return this; + } + SetContent( _config.GetValue( UiConst.Label ) ); + return this; + } + var bindLabel = _config.GetValue( AngularConst.BindLabel ); + if ( string.IsNullOrWhiteSpace( bindLabel ) ) + return this; + SetContent( $"{{{{{bindLabel}}}}}" ); + return this; + } + + /// + /// 配置事件 + /// + public RadioBuilder Events() { + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + NgModel().FormControl().OnModelChange() + .TableEdit().SpaceItem() + .Name().AutoFocus().Disabled().Value() + .Label().Events(); + } + + /// + /// 配置模型绑定 + /// + public override RadioBuilder NgModel() { + if ( _shareConfig.IsAutoCreateRadioGroup != true ) + base.NgModel(); + return this; + } + + /// + /// 数据绑定扩展 + /// + public void Extend() { + Attribute( "[nzValue]", "item.value" ); + Attribute( "[nzDisabled]", "item.disabled" ); + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + SetContent( "{{item.text|i18n}}" ); + return; + } + SetContent( "{{item.text}}" ); + } + + /// + /// 配置模型变更事件 + /// + public override RadioBuilder OnModelChange() { + if ( _shareConfig.IsAutoCreateRadioGroup == true ) + return this; + return base.OnModelChange(); + } + + /// + /// 配置间距项 + /// + public override RadioBuilder SpaceItem() { + if ( FormItemShareConfig.FormItemCreated ) + return this; + var radioGroupShareConfig = GetRadioGroupShareConfig(); + if ( radioGroupShareConfig.RadioGroupCreated ) + return this; + this.SpaceItem( FormItemShareConfig.SpaceItem ); + return this; + } + + /// + /// 获取输入框组合共享配置 + /// + private RadioGroupShareConfig GetRadioGroupShareConfig() { + return _config.GetValueFromItems() ?? new RadioGroupShareConfig(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Radios/Builders/RadioGroupBuilder.cs b/src/Util.Ui.NgZorro/Components/Radios/Builders/RadioGroupBuilder.cs new file mode 100644 index 000000000..edd329945 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Radios/Builders/RadioGroupBuilder.cs @@ -0,0 +1,143 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Radios.Configs; +using Util.Ui.NgZorro.Components.Selects.Builders; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Radios.Builders; + +/// +/// 单选框组合标签生成器 +/// +public class RadioGroupBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 单选框组合共享配置 + /// + private readonly RadioGroupShareConfig _shareConfig; + + /// + /// 初始化单选框组合标签生成器 + /// + /// 配置 + public RadioGroupBuilder( Config config ) : base( config, "nz-radio-group" ) { + _config = config; + _shareConfig = GetShareConfig(); + } + + /// + /// 获取单选框组合共享配置 + /// + private RadioGroupShareConfig GetShareConfig() { + return _config.GetValueFromItems() ?? new RadioGroupShareConfig(); + } + + /// + /// 配置所有单选按钮名称 + /// + public override RadioGroupBuilder Name() { + AttributeIfNotEmpty( "nzName", _config.GetValue( UiConst.Name ) ); + AttributeIfNotEmpty( "[nzName]", _config.GetValue( AngularConst.BindName ) ); + AttributeIfNotEmpty( "name", _config.GetValue( UiConst.Name ) ); + AttributeIfNotEmpty( "[name]", _config.GetValue( AngularConst.BindName ) ); + return this; + } + + /// + /// 配置禁用 + /// + public RadioGroupBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置尺寸 + /// + public RadioGroupBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置风格 + /// + public RadioGroupBuilder ButtonStyle() { + AttributeIfNotEmpty( "nzButtonStyle", _config.GetValue( UiConst.ButtonStyle )?.Description() ); + AttributeIfNotEmpty( "[nzButtonStyle]", _config.GetValue( AngularConst.BindButtonStyle ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + ConfigForm().Name().Disabled().Size().ButtonStyle() + .Data().Url().AutoLoad().QueryParam().Sort() + .SpaceItem().OnLoad(); + } + + /// + /// 数据源扩展 + /// + public RadioGroupBuilder Data() { + AttributeIfNotEmpty( "[data]", _config.GetValue( UiConst.Data ) ); + return this; + } + + /// + /// Api地址扩展 + /// + public RadioGroupBuilder Url() { + AttributeIfNotEmpty( "url", _config.GetValue( UiConst.Url ) ); + AttributeIfNotEmpty( "[url]", _config.GetValue( AngularConst.BindUrl ) ); + return this; + } + + /// + /// 选择框扩展 + /// + public RadioGroupBuilder SelectExtend() { + Attribute( $"#{_shareConfig.ExtendId}", "xSelectExtend" ); + Attribute( "x-select-extend" ); + return this; + } + + /// + /// 配置自动加载 + /// + public RadioGroupBuilder AutoLoad() { + AttributeIfNotEmpty( "[autoLoad]", _config.GetBoolValue( UiConst.AutoLoad ) ); + return this; + } + + /// + /// 配置查询参数 + /// + public RadioGroupBuilder QueryParam() { + AttributeIfNotEmpty( "[(queryParam)]", _config.GetValue( UiConst.QueryParam ) ); + return this; + } + + /// + /// 配置排序条件 + /// + public RadioGroupBuilder Sort() { + AttributeIfNotEmpty( "order", _config.GetValue( UiConst.Sort ) ); + AttributeIfNotEmpty( "[order]", _config.GetValue( AngularConst.BindSort ) ); + return this; + } + + /// + /// 配置加载完成事件 + /// + public RadioGroupBuilder OnLoad() { + AttributeIfNotEmpty( "(onLoad)", _config.GetValue( UiConst.OnLoad ) ); + return this; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Radios/Configs/RadioGroupShareConfig.cs b/src/Util.Ui.NgZorro/Components/Radios/Configs/RadioGroupShareConfig.cs new file mode 100644 index 000000000..77ca7f986 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Radios/Configs/RadioGroupShareConfig.cs @@ -0,0 +1,49 @@ +namespace Util.Ui.NgZorro.Components.Radios.Configs; + +/// +/// 单选框组合共享配置 +/// +public class RadioGroupShareConfig { + /// + /// 初始化单选框组合共享配置 + /// + /// 标识 + public RadioGroupShareConfig( string id = null ) { + Id = id.IsEmpty() ? Util.Helpers.Id.Create() : id; + } + + /// + /// 标识 + /// + public string Id { get; } + + /// + /// 扩展标识 + /// + public string ExtendId => $"x_{Id}"; + + /// + /// 是否已创建 nz-radio-group + /// + public bool RadioGroupCreated { get; set; } + + /// + /// 是否自动创建单选框组合 + /// + public bool? IsAutoCreateRadioGroup { get; set; } + + /// + /// 是否自动创建单选框 + /// + public bool? IsAutoCreateRadio { get; set; } + + /// + /// 单选框组合是否设置扩展属性 + /// + public bool IsRadioGroupExtend { get; set; } + + /// + /// 单选框是否设置扩展属性 + /// + public bool IsRadioExtend { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Radios/Helpers/RadioGroupService.cs b/src/Util.Ui.NgZorro/Components/Radios/Helpers/RadioGroupService.cs new file mode 100644 index 000000000..c581bae5f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Radios/Helpers/RadioGroupService.cs @@ -0,0 +1,160 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Forms.Helpers; +using Util.Ui.NgZorro.Components.Radios.Configs; +using Util.Ui.NgZorro.Components.Selects.Helpers; + +namespace Util.Ui.NgZorro.Components.Radios.Helpers; + +/// +/// 单选框组合服务 +/// +public class RadioGroupService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 单选框组合共享配置 + /// + private RadioGroupShareConfig _shareConfig; + + /// + /// 初始化单选框组合服务 + /// + /// 配置 + public RadioGroupService( Config config ) { + _config = config; + } + + /// + /// 设置nz-radio-group已创建 + /// + public void Created() { + _shareConfig.RadioGroupCreated = true; + } + + /// + /// 初始化 + /// + public void Init() { + LoadExpression(); + InitShareConfig(); + InitValidationService(); + InitFormShareService(); + InitFormItemShareService(); + EnableExtend(); + DisableAutoCreateRadioGroup(); + EnableAutoCreateRadio(); + } + + /// + /// 加载表达式 + /// + private void LoadExpression() { + var loader = new SelectExpressionLoader(); + loader.Load( _config ); + } + + /// + /// 初始化共享配置 + /// + private void InitShareConfig() { + _shareConfig = new RadioGroupShareConfig( GetRadioId() ); + _config.SetValueToItems( _shareConfig ); + } + + /// + /// 获取单选框标识 + /// + private string GetRadioId() { + return _config.GetValue( UiConst.Id ); + } + + /// + /// 初始化验证服务 + /// + private void InitValidationService() { + var service = new ValidationService( _config ); + service.Init(); + } + + /// + /// 初始化表单共享服务 + /// + private void InitFormShareService() { + var service = new FormShareService( _config ); + service.Init(); + } + + /// + /// 初始化表单项共享服务 + /// + private void InitFormItemShareService() { + var service = new FormItemShareService( _config ); + service.Init(); + service.InitId(); + } + + /// + /// 启用扩展 + /// + private void EnableExtend() { + if ( IsExtend() == false ) + return; + _shareConfig.IsRadioGroupExtend = true; + _shareConfig.IsRadioExtend = true; + } + + /// + /// 是否扩展 + /// + private bool IsExtend() { + if ( GetEnableExtend() == false ) + return false; + if ( GetEnableExtend() == true ) + return true; + if ( HasData() ) + return true; + if ( HasUrl() ) + return true; + return false; + } + + /// + /// 获取启用扩展属性 + /// + private bool? GetEnableExtend() { + return _config.GetValue( UiConst.EnableExtend ); + } + + /// + /// 是否设置数据源属性 + /// + private bool HasData() { + return _config.Contains( UiConst.Data ); + } + + /// + /// 是否设置Api地址 + /// + private bool HasUrl() { + if ( _config.Contains( UiConst.Url ) ) + return true; + return _config.Contains( AngularConst.BindUrl ); + } + + /// + /// 禁用自动创建单选框组合 + /// + private void DisableAutoCreateRadioGroup() { + _shareConfig.IsAutoCreateRadioGroup = false; + } + + /// + /// 启用自动创建单选框 + /// + private void EnableAutoCreateRadio() { + if ( IsExtend() ) + _shareConfig.IsAutoCreateRadio = true; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Radios/Helpers/RadioService.cs b/src/Util.Ui.NgZorro/Components/Radios/Helpers/RadioService.cs new file mode 100644 index 000000000..186ae09ff --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Radios/Helpers/RadioService.cs @@ -0,0 +1,169 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Forms.Helpers; +using Util.Ui.NgZorro.Components.Radios.Configs; +using Util.Ui.NgZorro.Components.Selects.Helpers; + +namespace Util.Ui.NgZorro.Components.Radios.Helpers; + +/// +/// 单选框服务 +/// +public class RadioService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 单选框组合共享配置 + /// + private RadioGroupShareConfig _shareConfig; + + /// + /// 初始化单选框服务 + /// + /// 配置 + public RadioService( Config config ) { + _config = config; + } + + /// + /// 初始化 + /// + public void Init() { + LoadExpression(); + InitShareConfig(); + InitValidationService(); + InitFormShareService(); + InitFormItemShareService(); + EnableExtend(); + DisableAutoCreateRadio(); + EnableAutoCreateRadioGroup(); + } + + /// + /// 加载表达式 + /// + private void LoadExpression() { + var loader = new SelectExpressionLoader(); + loader.Load( _config ); + } + + /// + /// 初始化共享配置 + /// + private void InitShareConfig() { + _shareConfig = GetShareConfig(); + if ( _shareConfig != null ) + return; + _shareConfig = new RadioGroupShareConfig( GetRadioId() ); + _config.SetValueToItems( _shareConfig ); + } + + /// + /// 获取单选框标识 + /// + private string GetRadioId() { + return _config.GetValue( UiConst.Id ); + } + + /// + /// 获取单选框组合共享配置 + /// + private RadioGroupShareConfig GetShareConfig() { + return _config.GetValueFromItems(); + } + + /// + /// 初始化验证服务 + /// + private void InitValidationService() { + var service = new ValidationService( _config ); + service.Init(); + } + + /// + /// 初始化表单共享服务 + /// + private void InitFormShareService() { + var service = new FormShareService( _config ); + service.Init(); + } + + /// + /// 初始化表单项共享服务 + /// + private void InitFormItemShareService() { + var service = new FormItemShareService( _config ); + service.Init(); + service.InitId(); + } + + /// + /// 启用扩展 + /// + private void EnableExtend() { + if ( IsExtend() == false ) { + _shareConfig.IsRadioExtend = false; + return; + } + _shareConfig.IsRadioExtend = true; + if ( _shareConfig.IsAutoCreateRadioGroup == false ) + return; + _shareConfig.IsRadioGroupExtend = true; + } + + /// + /// 是否扩展 + /// + private bool IsExtend() { + if ( GetEnableExtend() == false ) + return false; + if ( GetEnableExtend() == true ) + return true; + if ( HasData() ) + return true; + if ( HasUrl() ) + return true; + return false; + } + + /// + /// 获取启用扩展属性 + /// + private bool? GetEnableExtend() { + return _config.GetValue( UiConst.EnableExtend ); + } + + /// + /// 是否设置数据源属性 + /// + private bool HasData() { + return _config.Contains( UiConst.Data ); + } + + /// + /// 是否设置Api地址 + /// + private bool HasUrl() { + if ( _config.Contains( UiConst.Url ) ) + return true; + return _config.Contains( AngularConst.BindUrl ); + } + + /// + /// 禁用自动创建单选框组合 + /// + private void DisableAutoCreateRadio() { + _shareConfig.IsAutoCreateRadio = false; + } + + /// + /// 启用自动创建单选框组合 + /// + private void EnableAutoCreateRadioGroup() { + if ( _shareConfig.IsAutoCreateRadioGroup == false ) + return; + if ( IsExtend() ) + _shareConfig.IsAutoCreateRadioGroup = true; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Radios/RadioGroupTagHelper.cs b/src/Util.Ui.NgZorro/Components/Radios/RadioGroupTagHelper.cs new file mode 100644 index 000000000..dd925fa60 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Radios/RadioGroupTagHelper.cs @@ -0,0 +1,98 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Radios.Helpers; +using Util.Ui.NgZorro.Components.Radios.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Radios; + +/// +/// 单选框组合,生成的标签为<nz-radio-group></nz-radio-group> +/// +[HtmlTargetElement( "util-radio-group" )] +public class RadioGroupTagHelper : FormControlTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// nzName,将单选框设置为同一组选项 + /// + public new string Name { get; set; } + /// + /// [nzName],将单选框设置为同一组选项 + /// + public new string BindName { get; set; } + /// + /// nzSize,尺寸,只对按钮样式生效,可选值: 'large' | 'small' | 'default', 默认值: 'default' + /// + public ButtonSize Size { get; set; } + /// + /// [nzSize],尺寸,只对按钮样式生效,可选值: 'large' | 'small' | 'default', 默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// [nzDisabled],是否禁用,类型: boolean, 默认值: false + /// + public string Disabled { get; set; } + /// + /// nzButtonStyle,风格样式,可选值: 'outline' | 'solid',默认值: 'outline' + /// + public RadioStyle ButtonStyle { get; set; } + /// + /// [nzButtonStyle],风格样式,可选值: 'outline' | 'solid',默认值: 'outline' + /// + public string BindButtonStyle { get; set; } + /// + /// 扩展属性,是否启用扩展指令,当设置 url 或 data 属性时自动启用,默认值: false + /// + public bool EnableExtend { get; set; } + /// + /// 扩展属性 [data],数据源 + /// + public string Data { get; set; } + /// + /// 扩展属性 url,Api地址 + /// + public string Url { get; set; } + /// + /// 扩展属性 [url],Api地址 + /// + public string BindUrl { get; set; } + /// + /// 扩展属性 [autoLoad],初始化时是否自动加载数据,默认值: true,设置成 false 手工加载 + /// + public bool AutoLoad { get; set; } + /// + /// 扩展属性 [(queryParam)],查询参数 + /// + public string QueryParam { get; set; } + /// + /// 扩展属性 order,排序条件,范例: creationTime desc + /// + public string Sort { get; set; } + /// + /// 扩展属性 [order],排序条件,范例: creationTime desc + /// + public string BindSort { get; set; } + /// + /// 扩展事件 (onLoad),数据加载完成事件,类型: EventEmitter<any>,参数为服务端返回结果 + /// + public string OnLoad { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + base.ProcessBefore( context, output ); + _config = new Config( context, output ); + var service = new RadioGroupService( _config ); + service.Init(); + service.Created(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new RadioGroupRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Radios/RadioTagHelper.cs b/src/Util.Ui.NgZorro/Components/Radios/RadioTagHelper.cs new file mode 100644 index 000000000..4bce76c68 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Radios/RadioTagHelper.cs @@ -0,0 +1,111 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Radios.Helpers; +using Util.Ui.NgZorro.Components.Radios.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Radios; + +/// +/// 按钮样式的单选框,生成的标签为<label nz-radio-button></label> +/// +[HtmlTargetElement( "util-radio-button" )] +public class RadioButtonTagHelper : RadioTagHelper { + /// + /// 获取指令名称 + /// + protected override string GetDirectiveName() { + return "nz-radio-button"; + } +} + +/// +/// 单选框,生成的标签为<label nz-radio></label> +/// +[HtmlTargetElement( "util-radio" )] +public class RadioTagHelper : FormControlTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// [nzAutoFocus],是否自动获取焦点,类型: boolean, 默认值: false + /// + public string AutoFocus { get; set; } + /// + /// [nzDisabled],是否禁用,类型: boolean, 默认值: false + /// + public string Disabled { get; set; } + /// + /// nzValue,值 + /// + public string Value { get; set; } + /// + /// [nzValue],值 + /// + public string BindValue { get; set; } + /// + /// 扩展属性,是否启用扩展指令,当设置 url 或 data 属性时自动启用,默认值: false + /// + public bool EnableExtend { get; set; } + /// + /// 扩展属性,标签文本,支持多语言 + /// + public string Label { get; set; } + /// + /// 扩展属性,标签文本 + /// + public string BindLabel { get; set; } + /// + /// 扩展属性 [data],数据源 + /// + public string Data { get; set; } + /// + /// 扩展属性 url,Api地址 + /// + public string Url { get; set; } + /// + /// 扩展属性 [url],Api地址 + /// + public string BindUrl { get; set; } + /// + /// 扩展属性 [autoLoad],初始化时是否自动加载数据,默认值: true,设置成 false 手工加载 + /// + public bool AutoLoad { get; set; } + /// + /// 扩展属性 [(queryParam)],查询参数 + /// + public string QueryParam { get; set; } + /// + /// 扩展属性 order,排序条件,范例: creationTime desc + /// + public string Sort { get; set; } + /// + /// 扩展属性 [order],排序条件,范例: creationTime desc + /// + public string BindSort { get; set; } + /// + /// 扩展事件 (onLoad),数据加载完成事件,类型: EventEmitter<any>,参数为服务端返回结果 + /// + public string OnLoad { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new RadioService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new RadioRender( _config, GetDirectiveName() ); + } + + /// + /// 获取指令名称 + /// + protected virtual string GetDirectiveName() { + return "nz-radio"; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Radios/Renders/RadioGroupRender.cs b/src/Util.Ui.NgZorro/Components/Radios/Renders/RadioGroupRender.cs new file mode 100644 index 000000000..97a6f7afa --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Radios/Renders/RadioGroupRender.cs @@ -0,0 +1,75 @@ +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Radios.Builders; +using Util.Ui.NgZorro.Components.Radios.Configs; + +namespace Util.Ui.NgZorro.Components.Radios.Renders; + +/// +/// 单选框组合渲染器 +/// +public class RadioGroupRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 单选框组合共享配置 + /// + private readonly RadioGroupShareConfig _shareConfig; + + /// + /// 初始化单选框组合渲染器 + /// + /// 配置 + public RadioGroupRender( Config config ) : base( config ) { + _config = config; + _shareConfig = GetShareConfig(); + } + + /// + /// 获取单选框组合共享配置 + /// + private RadioGroupShareConfig GetShareConfig() { + return _config.GetValueFromItems() ?? new RadioGroupShareConfig(); + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new RadioGroupBuilder( _config ); + builder.Config(); + if ( _shareConfig.IsRadioExtend && _shareConfig.IsAutoCreateRadioGroup != true ) { + builder.AppendContent( $"@for(item of {_shareConfig.ExtendId}.options;track item.value )" ); + builder.AppendContent( "{" ); + } + _config.Content.AppendTo( builder ); + if ( _shareConfig.IsRadioGroupExtend ) + builder.SelectExtend(); + builder.AppendContent( GetRadioBuilder() ); + if ( _shareConfig.IsRadioExtend && _shareConfig.IsAutoCreateRadioGroup != true ) { + builder.AppendContent( "}" ); + } + formControlBuilder.AppendContent( builder ); + } + + /// + /// 获取单选框标签生成器 + /// + private TagBuilder GetRadioBuilder() { + if ( _shareConfig.IsAutoCreateRadio != true ) + return new EmptyContainerTagBuilder(); + var builder = new RadioBuilder( _config ); + builder.Attribute( "nz-radio" ); + if ( _shareConfig.IsRadioExtend ) + builder.Extend(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new RadioGroupRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Radios/Renders/RadioRender.cs b/src/Util.Ui.NgZorro/Components/Radios/Renders/RadioRender.cs new file mode 100644 index 000000000..6712c2fee --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Radios/Renders/RadioRender.cs @@ -0,0 +1,92 @@ +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Radios.Builders; +using Util.Ui.NgZorro.Components.Radios.Configs; + +namespace Util.Ui.NgZorro.Components.Radios.Renders; + +/// +/// 单选框渲染器 +/// +public class RadioRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 单选框组合共享配置 + /// + private readonly RadioGroupShareConfig _shareConfig; + /// + /// 指令名称 + /// + private readonly string _directiveName; + + /// + /// 初始化单选框渲染器 + /// + /// 配置 + /// 指令名称 + public RadioRender( Config config, string directiveName ) : base( config ) { + _config = config; + _shareConfig = GetShareConfig(); + _directiveName = directiveName; + } + + /// + /// 获取单选框组合共享配置 + /// + private RadioGroupShareConfig GetShareConfig() { + return _config.GetValueFromItems() ?? new RadioGroupShareConfig(); + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var groupBuilder = GetRadioGroupBuilder(); + if ( _shareConfig.IsRadioExtend && _shareConfig.IsAutoCreateRadioGroup == true ) { + groupBuilder.AppendContent( $"@for(item of {_shareConfig.ExtendId}.options;track item.value )" ); + groupBuilder.AppendContent( "{" ); + } + groupBuilder.AppendContent( GetRadioBuilder() ); + if ( _shareConfig.IsRadioExtend && _shareConfig.IsAutoCreateRadioGroup == true ) { + groupBuilder.AppendContent( "}" ); + } + formControlBuilder.AppendContent( groupBuilder ); + } + + /// + /// 获取单选框组合标签生成器 + /// + private TagBuilder GetRadioGroupBuilder() { + if ( _shareConfig.IsAutoCreateRadioGroup != true ) + return new EmptyContainerTagBuilder(); + var groupBuilder = new RadioGroupBuilder( _config ); + return groupBuilder.SelectExtend().AutoLoad() + .Data().Url().QueryParam().Sort() + .Name().NgModel().OnModelChange() + .Required().RequiredMessage() + .ValidationExtend().OnLoad(); + } + + /// + /// 获取单选框标签生成器 + /// + private TagBuilder GetRadioBuilder() { + var builder = new RadioBuilder( _config ); + builder.Attribute( _directiveName ); + builder.Config(); + if ( _config.Content.IsEmpty() == false ) + builder.SetContent( _config.Content ); + if ( _shareConfig.IsRadioExtend ) + builder.Extend(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new RadioRender( _config.Copy(), _directiveName ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Rates/Builders/RateBuilder.cs b/src/Util.Ui.NgZorro/Components/Rates/Builders/RateBuilder.cs new file mode 100644 index 000000000..0e3883275 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Rates/Builders/RateBuilder.cs @@ -0,0 +1,97 @@ +using Util.Ui.NgZorro.Components.Base; + +namespace Util.Ui.NgZorro.Components.Rates.Builders; + +/// +/// 评分标签生成器 +/// +public class RateBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化评分标签生成器 + /// + /// 配置 + public RateBuilder( Config config ) : base( config, "nz-rate" ) { + _config = config; + } + + /// + /// 配置允许清除 + /// + public RateBuilder AllowClear() { + AttributeIfNotEmpty( "[nzAllowClear]", _config.GetValue( UiConst.AllowClear ) ); + return this; + } + + /// + /// 配置允许半选 + /// + public RateBuilder AllowHalf() { + AttributeIfNotEmpty( "[nzAllowHalf]", _config.GetValue( UiConst.AllowHalf ) ); + return this; + } + + /// + /// 配置自动聚焦 + /// + public RateBuilder AutoFocus() { + AttributeIfNotEmpty( "[nzAutoFocus]", _config.GetValue( UiConst.AutoFocus ) ); + return this; + } + + /// + /// 配置自定义字符 + /// + public RateBuilder Character() { + AttributeIfNotEmpty( "[nzCharacter]", _config.GetValue( UiConst.Character ) ); + return this; + } + + /// + /// 配置数量 + /// + public RateBuilder Count() { + AttributeIfNotEmpty( "[nzCount]", _config.GetValue( UiConst.Count ) ); + return this; + } + + /// + /// 配置禁用 + /// + public RateBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置提示信息 + /// + public RateBuilder Tooltips() { + AttributeIfNotEmpty( "[nzTooltips]", _config.GetValue( UiConst.Tooltips ) ); + return this; + } + + /// + /// 配置事件 + /// + public RateBuilder Events() { + AttributeIfNotEmpty( "(nzOnFocus)", _config.GetValue( UiConst.OnFocus ) ); + AttributeIfNotEmpty( "(nzOnBlur)", _config.GetValue( UiConst.OnBlur ) ); + AttributeIfNotEmpty( "(nzOnHoverChange)", _config.GetValue( UiConst.OnHoverChange ) ); + AttributeIfNotEmpty( "(nzOnKeyDown)", _config.GetValue( UiConst.OnKeydown ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + ConfigForm().AllowClear().AllowHalf().AutoFocus() + .Character().Count().Disabled().Tooltips().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Rates/RateTagHelper.cs b/src/Util.Ui.NgZorro/Components/Rates/RateTagHelper.cs new file mode 100644 index 000000000..42cbc310a --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Rates/RateTagHelper.cs @@ -0,0 +1,95 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Rates.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Rates; + +/// +/// 评分,生成的标签为<nz-rate></nz-rate> +/// +[HtmlTargetElement( "util-rate" )] +public class RateTagHelper : AngularTagHelperBase { + /// + /// [(ngModel)],模型双向绑定 + /// + public string NgModel { get; set; } + /// + /// [ngModel],模型绑定 + /// + public string BindNgModel { get; set; } + /// + /// [formControl],表单控件实例 + /// + public string FormControl { get; set; } + /// + /// formControlName,表单控件名 + /// + public string FormControlName { get; set; } + /// + /// [formControlName],表单控件名 + /// + public string BindFormControlName { get; set; } + /// + /// (ngModelChange),模型变化事件 + /// + public string OnModelChange { get; set; } + /// + /// name,名称 + /// + public string Name { get; set; } + /// + /// [name],名称 + /// + public string BindName { get; set; } + /// + /// [nzAllowClear],是否允许再次点击清除,类型: boolean, 默认值: true + /// + public string AllowClear { get; set; } + /// + /// [nzAllowHalf],是否允许半选,类型: boolean, 默认值: false + /// + public string AllowHalf { get; set; } + /// + /// [nzAutoFocus],是否自动聚焦,类型: boolean, 默认值: false + /// + public string AutoFocus { get; set; } + /// + /// [nzCharacter],自定义字符,将星星替换为其他字符,类型:TemplateRef</void>,默认值: <span nz-icon nzType="star"></span> + /// + public string Character { get; set; } + /// + /// [nzCount],星星显示数量,类型: number, 默认值: 5 + /// + public string Count { get; set; } + /// + /// [nzDisabled],是否禁用,类型: boolean, 默认值: false + /// + public string Disabled { get; set; } + /// + /// [nzTooltips],自定义每项的提示信息,类型: string[],范例: ['terrible', 'bad', 'normal', 'good', 'wonderful'] + /// + public string Tooltips { get; set; } + /// + /// (nzOnFocus),获得焦点事件,类型: EventEmitter<FocusEvent> + /// + public string OnFocus { get; set; } + /// + /// (nzOnBlur),失去焦点事件,类型: EventEmitter<FocusEvent> + /// + public string OnBlur { get; set; } + /// + /// (nzOnHoverChange),鼠标滑过事件,类型: EventEmitter<number> + /// + public string OnHoverChange { get; set; } + /// + /// (nzOnKeyDown),键盘按下事件,类型: EventEmitter<KeyboardEvent> + /// + public string OnKeydown { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new RateRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Rates/Renders/RateRender.cs b/src/Util.Ui.NgZorro/Components/Rates/Renders/RateRender.cs new file mode 100644 index 000000000..0293b2bdc --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Rates/Renders/RateRender.cs @@ -0,0 +1,39 @@ +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Rates.Builders; + +namespace Util.Ui.NgZorro.Components.Rates.Renders; + +/// +/// 评分渲染器 +/// +public class RateRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化评分渲染器 + /// + /// 配置 + public RateRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new RateBuilder( _config ); + builder.Config(); + _config.Content.AppendTo( builder ); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new RateRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Resizable/Builders/ResizeHandleBuilder.cs b/src/Util.Ui.NgZorro/Components/Resizable/Builders/ResizeHandleBuilder.cs new file mode 100644 index 000000000..636044405 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Resizable/Builders/ResizeHandleBuilder.cs @@ -0,0 +1,53 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Resizable.Builders; + +/// +/// 调整尺寸手柄标签生成器 +/// +public class ResizeHandleBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化调整尺寸手柄标签生成器 + /// + public ResizeHandleBuilder( Config config ) : base( config, "nz-resize-handle" ) { + _config = config; + } + + /// + /// 配置调整方向 + /// + public ResizeHandleBuilder Direction( ResizeDirection direction ) { + AttributeIfNotEmpty( "nzDirection", direction.Description() ); + return this; + } + + /// + /// 配置调整方向 + /// + public ResizeHandleBuilder BindDirection( string direction ) { + AttributeIfNotEmpty( "[nzDirection]", direction ); + return this; + } + + /// + /// 配置光标类型 + /// + public ResizeHandleBuilder CursorType( CursorType cursor ) { + AttributeIfNotEmpty( "nzCursorType", cursor.Description() ); + return this; + } + + /// + /// 配置光标类型 + /// + public ResizeHandleBuilder BindCursorType( string cursor ) { + AttributeIfNotEmpty( "[nzCursorType]", cursor ); + return this; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Results/Builders/ResultBuilder.cs b/src/Util.Ui.NgZorro/Components/Results/Builders/ResultBuilder.cs new file mode 100644 index 000000000..af9bc3e28 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Results/Builders/ResultBuilder.cs @@ -0,0 +1,75 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Results.Builders; + +/// +/// 结果标签生成器 +/// +public class ResultBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化结果标签生成器 + /// + /// 配置 + public ResultBuilder( Config config ) : base( config,"nz-result" ) { + _config = config; + } + + /// + /// 配置标题 + /// + public ResultBuilder Title() { + AttributeIfNotEmpty( "nzTitle", _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 配置副标题 + /// + public ResultBuilder SubTitle() { + AttributeIfNotEmpty( "nzSubTitle", _config.GetValue( UiConst.Subtitle ) ); + AttributeIfNotEmpty( "[nzSubTitle]", _config.GetValue( AngularConst.BindSubtitle ) ); + return this; + } + + /// + /// 配置状态 + /// + public ResultBuilder Status() { + AttributeIfNotEmpty( "nzStatus", _config.GetValue( UiConst.Status )?.Description() ); + AttributeIfNotEmpty( "[nzStatus]", _config.GetValue( AngularConst.BindStatus ) ); + return this; + } + + /// + /// 配置图标 + /// + public ResultBuilder Icon() { + AttributeIfNotEmpty( "nzIcon", _config.GetValue( UiConst.Icon )?.Description() ); + AttributeIfNotEmpty( "[nzIcon]", _config.GetValue( AngularConst.BindIcon ) ); + return this; + } + + /// + /// 配置操作区 + /// + public ResultBuilder Extra() { + AttributeIfNotEmpty( "[nzExtra]", _config.GetValue( UiConst.Extra ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Title().SubTitle().Status().Icon().Extra(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Results/Builders/ResultContentBuilder.cs b/src/Util.Ui.NgZorro/Components/Results/Builders/ResultContentBuilder.cs new file mode 100644 index 000000000..5f180c6a4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Results/Builders/ResultContentBuilder.cs @@ -0,0 +1,22 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Results.Builders; + +/// +/// 结果内容标签生成器 +/// +public class ResultContentBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化结果内容标签生成器 + /// + /// 配置 + public ResultContentBuilder( Config config ) : base( config,"div" ) { + _config = config; + base.Attribute( "nz-result-content" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Results/Builders/ResultExtraBuilder.cs b/src/Util.Ui.NgZorro/Components/Results/Builders/ResultExtraBuilder.cs new file mode 100644 index 000000000..f293490d0 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Results/Builders/ResultExtraBuilder.cs @@ -0,0 +1,23 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Results.Builders; + +/// +/// 结果操作区标签生成器 +/// +public class ResultExtraBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化结果操作区标签生成器 + /// + /// 配置 + public ResultExtraBuilder( Config config ) : base( config,"div" ) { + _config = config; + base.Attribute( "nz-result-extra" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Results/Builders/ResultIconBuilder.cs b/src/Util.Ui.NgZorro/Components/Results/Builders/ResultIconBuilder.cs new file mode 100644 index 000000000..b5710b96c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Results/Builders/ResultIconBuilder.cs @@ -0,0 +1,22 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Results.Builders; + +/// +/// 结果图标标签生成器 +/// +public class ResultIconBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化结果图标标签生成器 + /// + /// 配置 + public ResultIconBuilder( Config config ) : base( config,"div" ) { + _config = config; + base.Attribute( "nz-result-icon" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Results/Builders/ResultSubtitleBuilder.cs b/src/Util.Ui.NgZorro/Components/Results/Builders/ResultSubtitleBuilder.cs new file mode 100644 index 000000000..57afb332c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Results/Builders/ResultSubtitleBuilder.cs @@ -0,0 +1,22 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Results.Builders; + +/// +/// 结果副标题标签生成器 +/// +public class ResultSubtitleBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化结果副标题标签生成器 + /// + /// 配置 + public ResultSubtitleBuilder( Config config ) : base( config,"div" ) { + _config = config; + base.Attribute( "nz-result-subtitle" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Results/Builders/ResultTitleBuilder.cs b/src/Util.Ui.NgZorro/Components/Results/Builders/ResultTitleBuilder.cs new file mode 100644 index 000000000..55692bb78 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Results/Builders/ResultTitleBuilder.cs @@ -0,0 +1,22 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Results.Builders; + +/// +/// 结果标题标签生成器 +/// +public class ResultTitleBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化结果标题标签生成器 + /// + /// 配置 + public ResultTitleBuilder( Config config ) : base( config,"div" ) { + _config = config; + base.Attribute( "nz-result-title" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Results/Renders/ResultContentRender.cs b/src/Util.Ui.NgZorro/Components/Results/Renders/ResultContentRender.cs new file mode 100644 index 000000000..e5e82bc39 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Results/Renders/ResultContentRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Results.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Results.Renders; + +/// +/// 结果内容渲染器 +/// +public class ResultContentRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化结果内容渲染器 + /// + /// 配置 + public ResultContentRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ResultContentBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ResultContentRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Results/Renders/ResultExtraRender.cs b/src/Util.Ui.NgZorro/Components/Results/Renders/ResultExtraRender.cs new file mode 100644 index 000000000..94a46833c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Results/Renders/ResultExtraRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Results.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Results.Renders; + +/// +/// 结果操作区渲染器 +/// +public class ResultExtraRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化结果操作区渲染器 + /// + /// 配置 + public ResultExtraRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ResultExtraBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ResultExtraRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Results/Renders/ResultIconRender.cs b/src/Util.Ui.NgZorro/Components/Results/Renders/ResultIconRender.cs new file mode 100644 index 000000000..9c30d9bd4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Results/Renders/ResultIconRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Results.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Results.Renders; + +/// +/// 结果图标渲染器 +/// +public class ResultIconRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化结果图标渲染器 + /// + /// 配置 + public ResultIconRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ResultIconBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ResultIconRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Results/Renders/ResultRender.cs b/src/Util.Ui.NgZorro/Components/Results/Renders/ResultRender.cs new file mode 100644 index 000000000..112d954c9 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Results/Renders/ResultRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Results.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Results.Renders; + +/// +/// 结果渲染器 +/// +public class ResultRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化结果渲染器 + /// + /// 配置 + public ResultRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ResultBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ResultRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Results/Renders/ResultSubtitleRender.cs b/src/Util.Ui.NgZorro/Components/Results/Renders/ResultSubtitleRender.cs new file mode 100644 index 000000000..3c6fd8ec3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Results/Renders/ResultSubtitleRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Results.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Results.Renders; + +/// +/// 结果副标题渲染器 +/// +public class ResultSubtitleRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化结果副标题渲染器 + /// + /// 配置 + public ResultSubtitleRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ResultSubtitleBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ResultSubtitleRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Results/Renders/ResultTitleRender.cs b/src/Util.Ui.NgZorro/Components/Results/Renders/ResultTitleRender.cs new file mode 100644 index 000000000..90562da1f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Results/Renders/ResultTitleRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Results.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Results.Renders; + +/// +/// 结果标题渲染器 +/// +public class ResultTitleRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化结果标题渲染器 + /// + /// 配置 + public ResultTitleRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ResultTitleBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ResultTitleRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Results/ResultContentTagHelper.cs b/src/Util.Ui.NgZorro/Components/Results/ResultContentTagHelper.cs new file mode 100644 index 000000000..c19009e28 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Results/ResultContentTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Results.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Results; + +/// +/// 结果内容区,生成的标签为<div nz-result-content></div> +/// +[HtmlTargetElement( "util-result-content" )] +public class ResultContentTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ResultContentRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Results/ResultExtraTagHelper.cs b/src/Util.Ui.NgZorro/Components/Results/ResultExtraTagHelper.cs new file mode 100644 index 000000000..9daadae8e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Results/ResultExtraTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Results.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Results; + +/// +/// 结果操作区,生成的标签为<div nz-result-extra></div> +/// +[HtmlTargetElement( "util-result-extra" )] +public class ResultExtraTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ResultExtraRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Results/ResultIconTagHelper.cs b/src/Util.Ui.NgZorro/Components/Results/ResultIconTagHelper.cs new file mode 100644 index 000000000..5953d5c10 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Results/ResultIconTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Results.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Results; + +/// +/// 结果图标区,生成的标签为<div nz-result-icon></div> +/// +[HtmlTargetElement( "util-result-icon" )] +public class ResultIconTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ResultIconRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Results/ResultSubtitleTagHelper.cs b/src/Util.Ui.NgZorro/Components/Results/ResultSubtitleTagHelper.cs new file mode 100644 index 000000000..b8e11af74 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Results/ResultSubtitleTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Results.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Results; + +/// +/// 结果副标题区,生成的标签为<div nz-result-subtitle></div> +/// +[HtmlTargetElement( "util-result-subtitle" )] +public class ResultSubtitleTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ResultSubtitleRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Results/ResultTagHelper.cs b/src/Util.Ui.NgZorro/Components/Results/ResultTagHelper.cs new file mode 100644 index 000000000..a3ac9d8b6 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Results/ResultTagHelper.cs @@ -0,0 +1,56 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Results.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Results; + +/// +/// 结果,生成的标签为<nz-result></nz-result> +/// +[HtmlTargetElement( "util-result" )] +public class ResultTagHelper : AngularTagHelperBase { + /// + /// nzTitle,标题,类型: string | TemplateRef<void> + /// + public string Title { get; set; } + /// + /// [nzTitle],标题,类型: string | TemplateRef<void> + /// + public string BindTitle { get; set; } + /// + /// nzSubTitle,副标题,类型: string | TemplateRef<void> + /// + public string Subtitle { get; set; } + /// + /// [nzSubTitle],副标题,类型: string | TemplateRef<void> + /// + public string BindSubtitle { get; set; } + /// + /// nzStatus,结果状态,决定图标和颜色,可选值: 'success' | 'error' | 'info' | 'warning'| '404' | '403' | '500',默认值: 'info' + /// + public ResultStatus Status { get; set; } + /// + /// [nzStatus],结果状态,决定图标和颜色,可选值: 'success' | 'error' | 'info' | 'warning'| '404' | '403' | '500',默认值: 'info' + /// + public string BindStatus { get; set; } + /// + /// nzIcon,图标,类型: string | TemplateRef<void> + /// + public AntDesignIcon Icon { get; set; } + /// + /// [nzIcon],图标,类型: string | TemplateRef<void> + /// + public string BindIcon { get; set; } + /// + /// [nzExtra],操作区,类型: string | TemplateRef<void> + /// + public string Extra { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ResultRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Results/ResultTitleTagHelper.cs b/src/Util.Ui.NgZorro/Components/Results/ResultTitleTagHelper.cs new file mode 100644 index 000000000..bf1322317 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Results/ResultTitleTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Results.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Results; + +/// +/// 结果标题区,生成的标签为<div nz-result-title></div> +/// +[HtmlTargetElement( "util-result-title" )] +public class ResultTitleTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ResultTitleRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Segments/Builders/SegmentedBuilder.cs b/src/Util.Ui.NgZorro/Components/Segments/Builders/SegmentedBuilder.cs new file mode 100644 index 000000000..4ff73a22f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Segments/Builders/SegmentedBuilder.cs @@ -0,0 +1,215 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Segments.Builders; + +/// +/// 分段控制器标签生成器 +/// +public class SegmentedBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 标识 + /// + private string _id; + + /// + /// 初始化分段控制器标签生成器 + /// + /// 配置 + public SegmentedBuilder( Config config ) : base( config, "nz-segmented" ) { + _config = config; + } + + /// + /// 扩展标识 + /// + protected string ExtendId => $"x_{GetId()}"; + + /// + /// 获取标识 + /// + private string GetId() { + if ( _id.IsEmpty() == false ) + return _id; + _id = _config.GetValue( UiConst.Id ); + if ( _id.IsEmpty() ) + _id = Util.Helpers.Id.Create(); + return _id; + } + + /// + /// 配置将宽度调整为父元素宽度 + /// + public SegmentedBuilder Block() { + AttributeIfNotEmpty( "[nzBlock]", _config.GetValue( UiConst.Block ) ); + return this; + } + + /// + /// 配置禁用 + /// + public SegmentedBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置选择框大小 + /// + public SegmentedBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置选项列表 + /// + public SegmentedBuilder Options() { + AttributeIfNotEmpty( "[nzOptions]", _config.GetValue( UiConst.Options ) ); + return this; + } + + /// + /// 配置自动加载 + /// + private SegmentedBuilder AutoLoad() { + AttributeIfNotEmpty( "[autoLoad]", _config.GetValue( UiConst.AutoLoad ) ); + return this; + } + + /// + /// 配置查询参数 + /// + private SegmentedBuilder QueryParam() { + AttributeIfNotEmpty( "[(queryParam)]", _config.GetValue( UiConst.QueryParam ) ); + return this; + } + + /// + /// 配置Api地址 + /// + private SegmentedBuilder Url() { + AttributeIfNotEmpty( "url", _config.GetValue( UiConst.Url ) ); + AttributeIfNotEmpty( "[url]", _config.GetValue( AngularConst.BindUrl ) ); + return this; + } + + /// + /// 配置数据源 + /// + private SegmentedBuilder Data() { + AttributeIfNotEmpty( "[data]", _config.GetValue( UiConst.Data ) ); + return this; + } + + /// + /// 配置值绑定 + /// + private SegmentedBuilder Value() { + AttributeIfNotEmpty( "[(value)]", _config.GetValue( UiConst.Value ) ); + return this; + } + + /// + /// 配置自定义渲染选项 + /// + private SegmentedBuilder LabelTemplate() { + AttributeIfNotEmpty( "[nzLabelTemplate]", _config.GetValue( UiConst.LabelTemplate ) ); + return this; + } + + /// + /// 配置事件 + /// + public SegmentedBuilder Events() { + AttributeIfNotEmpty( "(nzValueChange)", GetOnIndexChange() ); + AttributeIfNotEmpty( "(valueChange)", _config.GetValue( UiConst.OnValueChange ) ); + AttributeIfNotEmpty( "(onLoad)", _config.GetValue( UiConst.OnLoad ) ); + return this; + } + + /// + /// 获取索引变化事件 + /// + private string GetOnIndexChange() { + var value = _config.GetValue( UiConst.OnIndexChange ); + if ( IsEnableExtend() == false ) + return value; + if (value.IsEmpty()) + return $"{ExtendId}.handleValueChange($event)"; + return $"{ExtendId}.handleValueChange($event);{value}"; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + ConfigForm().Name() + .Block().Disabled().Size() + .Options().AutoLoad().QueryParam() + .Url().Data().Value().LabelTemplate() + .Events(); + EnableExtend(); + } + + /// + /// 启用扩展 + /// + public SegmentedBuilder EnableExtend() { + if ( IsEnableExtend() == false ) + return this; + Attribute( $"#{ExtendId}", "xSegmentedExtend" ); + Attribute( "x-segmented-extend" ); + Attribute( "[nzOptions]", $"{ExtendId}.options" ); + Attribute( "[(ngModel)]", $"{ExtendId}.index", true ); + return this; + } + + /// + /// 是否启用基础扩展 + /// + public bool IsEnableExtend() { + if ( GetEnableExtend() == false ) { + return false; + } + return GetEnableExtend() == true || + GetUrl().IsEmpty() == false || + GetBindUrl().IsEmpty() == false || + GetData().IsEmpty() == false; + } + + /// + /// 获取启用扩展属性 + /// + private bool? GetEnableExtend() { + return _config.GetValue( UiConst.EnableExtend ); + } + + /// + /// 获取地址 + /// + private string GetUrl() { + return _config.GetValue( UiConst.Url ); + } + + /// + /// 获取地址 + /// + private string GetBindUrl() { + return _config.GetValue( AngularConst.BindUrl ); + } + + /// + /// 获取数据源 + /// + private string GetData() { + return _config.GetValue( UiConst.Data ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Segments/Helpers/SegmentedExpressionLoader.cs b/src/Util.Ui.NgZorro/Components/Segments/Helpers/SegmentedExpressionLoader.cs new file mode 100644 index 000000000..d77815755 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Segments/Helpers/SegmentedExpressionLoader.cs @@ -0,0 +1,19 @@ +using Util.Ui.Expressions; +using Util.Ui.NgZorro.Components.Selects.Helpers; + +namespace Util.Ui.NgZorro.Components.Segments.Helpers; + +/// +/// 分段控制器表达式加载器 +/// +public class SegmentedExpressionLoader : SelectExpressionLoader { + /// + /// 加载模型信息 + /// + /// 配置 + /// 模型表达式信息 + protected override void Load( Config config, ModelExpressionInfo info ) { + base.Load( config, info ); + config.SetAttribute( UiConst.Value, info.SafePropertyName, false ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Segments/Helpers/SegmentedService.cs b/src/Util.Ui.NgZorro/Components/Segments/Helpers/SegmentedService.cs new file mode 100644 index 000000000..726216cbd --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Segments/Helpers/SegmentedService.cs @@ -0,0 +1,55 @@ +using Util.Ui.NgZorro.Components.Forms.Helpers; + +namespace Util.Ui.NgZorro.Components.Segments.Helpers; + +/// +/// 分段控制器服务 +/// +public class SegmentedService { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化分段控制器服务 + /// + /// 配置 + public SegmentedService( Config config ) { + _config = config; + } + + /// + /// 初始化 + /// + public void Init() { + LoadExpression(); + InitFormShareService(); + InitFormItemShareService(); + } + + /// + /// 加载表达式 + /// + private void LoadExpression() { + var loader = new SegmentedExpressionLoader(); + loader.Load( _config ); + } + + /// + /// 初始化表单共享服务 + /// + private void InitFormShareService() { + var service = new FormShareService( _config ); + service.Init(); + } + + /// + /// 初始化表单项共享服务 + /// + private void InitFormItemShareService() { + var service = new FormItemShareService( _config ); + service.Init(); + service.InitId(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Segments/Renders/SegmentedRender.cs b/src/Util.Ui.NgZorro/Components/Segments/Renders/SegmentedRender.cs new file mode 100644 index 000000000..44e61695d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Segments/Renders/SegmentedRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Segments.Builders; + +namespace Util.Ui.NgZorro.Components.Segments.Renders; + +/// +/// 分段控制器渲染器 +/// +public class SegmentedRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化分段控制器渲染器 + /// + /// 配置 + public SegmentedRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new SegmentedBuilder( _config ); + builder.Config(); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new SegmentedRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Segments/SegmentedTagHelper.cs b/src/Util.Ui.NgZorro/Components/Segments/SegmentedTagHelper.cs new file mode 100644 index 000000000..705a79bd9 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Segments/SegmentedTagHelper.cs @@ -0,0 +1,96 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Segments.Helpers; +using Util.Ui.NgZorro.Components.Segments.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Segments; + +/// +/// 分段控制器,生成的标签为<nz-segmented></nz-segmented> +/// +[HtmlTargetElement( "util-segmented" )] +public class SegmentedTagHelper : FormControlTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 扩展属性,是否启用扩展指令,当设置Url或Data属性时自动启用 + /// + public bool EnableExtend { get; set; } + /// + /// 扩展属性 [autoLoad],初始化时是否自动加载数据,默认为true,设置成false则手工加载 + /// + public string AutoLoad { get; set; } + /// + /// 扩展属性 [(queryParam)],查询参数 + /// + public string QueryParam { get; set; } + /// + /// 扩展属性 url,Api地址 + /// + public string Url { get; set; } + /// + /// 扩展属性 [url],Api地址 + /// + public string BindUrl { get; set; } + /// + /// 扩展属性 [data],数据源 + /// + public string Data { get; set; } + /// + /// 扩展属性 [(value)],双向绑定选项值 + /// + public string Value { get; set; } + /// + /// [nzDisabled],是否禁用, 类型: boolean, 默认值: false + /// + public string Disabled { get; set; } + /// + /// nzSize,尺寸,可选值: 'large'|'small'|'default', 默认值: default + /// + public InputSize Size { get; set; } + /// + /// [nzSize],尺寸,可选值: 'large'|'small'|'default', 默认值: default + /// + public string BindSize { get; set; } + /// + /// [nzOptions],选项列表,类型: string[] | number[] | Array<{ label: string; value: string | number; icon: string; disabled?: boolean; useTemplate?: boolean }> + /// + public string Options { get; set; } + /// + /// [nzBlock],是否将宽度调整为父元素宽度,类型: boolean, 默认值: false + /// + public string Block { get; set; } + /// + /// [nzLabelTemplate],自定义渲染选项, 类型: TemplateRef<{ $implicit: NzSegmentedOption; index: number }> + /// + public string LabelTemplate { get; set; } + /// + /// (nzValueChange),选中项索引变化事件,参数为选中项的索引, 类型: EventEmitter<number> + /// + public string OnIndexChange { get; set; } + /// + /// (valueChange),扩展事件, 选中值变化事件,参数为选中项的值, 类型: EventEmitter<any> + /// + public string OnValueChange { get; set; } + /// + /// 扩展事件 (onLoad),数据加载完成事件,类型: EventEmitter<any>,参数为服务端返回结果 + /// + public string OnLoad { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new SegmentedService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new SegmentedRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Selects/Builders/OptionBuilder.cs b/src/Util.Ui.NgZorro/Components/Selects/Builders/OptionBuilder.cs new file mode 100644 index 000000000..23b8d0aaf --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Selects/Builders/OptionBuilder.cs @@ -0,0 +1,112 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; + +namespace Util.Ui.NgZorro.Components.Selects.Builders; + +/// +/// 选项标签生成器 +/// +public class OptionBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化选项标签生成器 + /// + /// 配置 + public OptionBuilder( Config config ) : base( config,"nz-option" ) { + _config = config; + } + + /// + /// 配置禁用 + /// + public OptionBuilder Disabled() { + Disabled( _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置禁用 + /// + public OptionBuilder Disabled( string value ) { + AttributeIfNotEmpty( "[nzDisabled]", value ); + return this; + } + + /// + /// 配置标签 + /// + public OptionBuilder Label() { + Label( _config.GetValue( UiConst.Label ) ); + return BindLabel( _config.GetValue( AngularConst.BindLabel ) ); + } + + /// + /// 配置标签 + /// + public OptionBuilder Label( string value ) { + if ( string.IsNullOrEmpty( value ) ) + return this; + Attribute( "nzLabel", value ); + return this; + } + + /// + /// 配置标签 + /// + public OptionBuilder BindLabel( string value ) { + AttributeIfNotEmpty( "[nzLabel]", value ); + return this; + } + + /// + /// 配置值 + /// + public OptionBuilder Value() { + Value( _config.GetValue( UiConst.Value ) ); + return BindValue( _config.GetValue( AngularConst.BindValue ) ); + } + + /// + /// 配置值 + /// + public OptionBuilder Value( string value ) { + AttributeIfNotEmpty( "nzValue", value ); + return this; + } + + /// + /// 配置值 + /// + public OptionBuilder BindValue( string value ) { + AttributeIfNotEmpty( "[nzValue]", value ); + return this; + } + + /// + /// 配置隐藏 + /// + public OptionBuilder Hide() { + AttributeIfNotEmpty( "[nzHide]", _config.GetValue( UiConst.Hide ) ); + return this; + } + + /// + /// 配置是否自定义内容 + /// + public OptionBuilder CustomContent() { + AttributeIfNotEmpty( "[nzCustomContent]", _config.GetValue( UiConst.CustomContent ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Disabled().Label().Value().Hide().CustomContent(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Selects/Builders/OptionGroupBuilder.cs b/src/Util.Ui.NgZorro/Components/Selects/Builders/OptionGroupBuilder.cs new file mode 100644 index 000000000..cb5286b9e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Selects/Builders/OptionGroupBuilder.cs @@ -0,0 +1,55 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Selects.Builders; + +/// +/// 选项组标签生成器 +/// +public class OptionGroupBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化选项组标签生成器 + /// + /// 配置 + public OptionGroupBuilder( Config config ) : base( config,"nz-option-group" ) { + _config = config; + } + + /// + /// 配置组名 + /// + public OptionGroupBuilder Label() { + Label( _config.GetValue( UiConst.Label ) ); + return BindLabel( _config.GetValue( AngularConst.BindLabel ) ); + } + + /// + /// 配置组名 + /// + public OptionGroupBuilder Label( string value ) { + AttributeIfNotEmpty( "nzLabel", value ); + return this; + } + + /// + /// 配置组名 + /// + public OptionGroupBuilder BindLabel( string value ) { + AttributeIfNotEmpty( "[nzLabel]", value ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Label(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Selects/Builders/SelectBuilder.cs b/src/Util.Ui.NgZorro/Components/Selects/Builders/SelectBuilder.cs new file mode 100644 index 000000000..282f576cd --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Selects/Builders/SelectBuilder.cs @@ -0,0 +1,647 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Containers.Builders; +using Util.Ui.NgZorro.Components.Spins.Builders; +using Util.Ui.NgZorro.Components.Templates.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Selects.Builders; + +/// +/// 选择器标签生成器 +/// +public class SelectBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 标识 + /// + private string _id; + + /// + /// 初始化选择器标签生成器 + /// + /// 配置 + public SelectBuilder( Config config ) : base( config, "nz-select" ) { + _config = config; + } + + /// + /// 扩展标识 + /// + protected string ExtendId => $"x_{GetId()}"; + + /// + /// 获取标识 + /// + private string GetId() { + if ( _id.IsEmpty() == false ) + return _id; + _id = _config.GetValue( UiConst.Id ); + if ( _id.IsEmpty() ) + _id = Util.Helpers.Id.Create(); + return _id; + } + + /// + /// 配置内部id + /// + public SelectBuilder NzId() { + AttributeIfNotEmpty( "nzId", _config.GetValue( UiConst.NzId ) ); + AttributeIfNotEmpty( "[nzId]", _config.GetValue( AngularConst.BindNzId ) ); + return this; + } + + /// + /// 配置比较算法函数 + /// + public SelectBuilder CompareWith() { + AttributeIfNotEmpty( "[compareWith]", _config.GetValue( UiConst.CompareWith ) ); + return this; + } + + /// + /// 配置清除搜索值 + /// + public SelectBuilder AutoClearSearchValue() { + AttributeIfNotEmpty( "[nzAutoClearSearchValue]", _config.GetValue( UiConst.AutoClearSearchValue ) ); + return this; + } + + /// + /// 配置允许清除 + /// + public SelectBuilder AllowClear() { + AttributeIfNotEmpty( "[nzAllowClear]", _config.GetValue( UiConst.AllowClear ) ); + return this; + } + + /// + /// 配置移除边框 + /// + public SelectBuilder Borderless() { + AttributeIfNotEmpty( "[nzBorderless]", _config.GetValue( UiConst.Borderless ) ); + return this; + } + + /// + /// 配置打开下拉菜单 + /// + public SelectBuilder Open() { + AttributeIfNotEmpty( "[nzOpen]", _config.GetValue( UiConst.Open ) ); + AttributeIfNotEmpty( "[(nzOpen)]", _config.GetValue( AngularConst.BindonOpen ) ); + return this; + } + + /// + /// 配置自动聚焦 + /// + public SelectBuilder AutoFocus() { + AttributeIfNotEmpty( "[nzAutoFocus]", _config.GetValue( UiConst.AutoFocus ) ); + return this; + } + + /// + /// 配置禁用 + /// + public SelectBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置下拉菜单样式类名 + /// + public SelectBuilder DropdownClassName() { + AttributeIfNotEmpty( "nzDropdownClassName", _config.GetValue( UiConst.DropdownClassName ) ); + AttributeIfNotEmpty( "[nzDropdownClassName]", _config.GetValue( AngularConst.BindDropdownClassName ) ); + return this; + } + + /// + /// 配置下拉菜单样式 + /// + public SelectBuilder DropdownStyle() { + AttributeIfNotEmpty( "[nzDropdownStyle]", _config.GetValue( UiConst.DropdownStyle ) ); + return this; + } + + /// + /// 配置下拉菜单和选择器同宽 + /// + public SelectBuilder DropdownMatchSelectWidth() { + AttributeIfNotEmpty( "[nzDropdownMatchSelectWidth]", _config.GetValue( UiConst.DropdownMatchSelectWidth ) ); + return this; + } + + /// + /// 配置自定义模板 + /// + public SelectBuilder CustomTemplate() { + AttributeIfNotEmpty( "[nzCustomTemplate]", _config.GetValue( UiConst.CustomTemplate ) ); + return this; + } + + /// + /// 配置服务端搜索 + /// + public SelectBuilder ServerSearch() { + ServerSearch( _config.GetValue( UiConst.ServerSearch ) ); + return this; + } + + /// + /// 配置服务端搜索 + /// + public SelectBuilder ServerSearch( string value ) { + AttributeIfNotEmpty( "[nzServerSearch]", value ); + return this; + } + + /// + /// 配置过滤项函数 + /// + public SelectBuilder FilterOption() { + AttributeIfNotEmpty( "[nzFilterOption]", _config.GetValue( UiConst.FilterOption ) ); + return this; + } + + /// + /// 配置最大多选数量 + /// + public SelectBuilder MaxMultipleCount() { + AttributeIfNotEmpty( "[nzMaxMultipleCount]", _config.GetValue( UiConst.MaxMultipleCount ) ); + return this; + } + + /// + /// 配置选择模式 + /// + public SelectBuilder Mode() { + AttributeIfNotEmpty( "nzMode", _config.GetValue( UiConst.Mode )?.Description() ); + AttributeIfNotEmpty( "[nzMode]", _config.GetValue( AngularConst.BindMode ) ); + return this; + } + + /// + /// 配置空列表默认内容 + /// + public SelectBuilder NotFoundContent() { + AttributeIfNotEmpty( "nzNotFoundContent", _config.GetValue( UiConst.NotFoundContent ) ); + AttributeIfNotEmpty( "[nzNotFoundContent]", _config.GetValue( AngularConst.BindNotFoundContent ) ); + return this; + } + + /// + /// 配置占位提示 + /// + public SelectBuilder Placeholder() { + AttributeIfNotEmpty( "nzPlaceHolder", _config.GetValue( UiConst.Placeholder ) ); + AttributeIfNotEmpty( "[nzPlaceHolder]", _config.GetValue( AngularConst.BindPlaceholder ) ); + return this; + } + + /// + /// 配置显示箭头 + /// + public SelectBuilder ShowArrow() { + AttributeIfNotEmpty( "[nzShowArrow]", _config.GetValue( UiConst.ShowArrow ) ); + return this; + } + + /// + /// 配置显示搜索 + /// + public SelectBuilder ShowSearch() { + ShowSearch( _config.GetValue( UiConst.ShowSearch ) ); + return this; + } + + /// + /// 配置显示搜索 + /// + public SelectBuilder ShowSearch( string value ) { + AttributeIfNotEmpty( "[nzShowSearch]", value ); + return this; + } + + /// + /// 配置下拉扩展模板 + /// + public SelectBuilder DropdownRender() { + DropdownRender( _config.GetValue( UiConst.DropdownRender ) ); + return this; + } + + /// + /// 配置下拉扩展模板 + /// + public SelectBuilder DropdownRender( string value ) { + AttributeIfNotEmpty( "[nzDropdownRender]", value ); + return this; + } + + /// + /// 配置选择框大小 + /// + public SelectBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置后缀图标 + /// + public SelectBuilder SuffixIcon() { + AttributeIfNotEmpty( "nzSuffixIcon", _config.GetValue( UiConst.SuffixIcon )?.Description() ); + AttributeIfNotEmpty( "[nzSuffixIcon]", _config.GetValue( AngularConst.BindSuffixIcon ) ); + return this; + } + + /// + /// 配置清除图标 + /// + public SelectBuilder RemoveIcon() { + AttributeIfNotEmpty( "[nzRemoveIcon]", _config.GetValue( UiConst.RemoveIcon ) ); + return this; + } + + /// + /// 配置清空图标 + /// + public SelectBuilder ClearIcon() { + AttributeIfNotEmpty( "[nzClearIcon]", _config.GetValue( UiConst.ClearIcon ) ); + return this; + } + + /// + /// 配置选中项图标 + /// + public SelectBuilder MenuItemSelectedIcon() { + AttributeIfNotEmpty( "[nzMenuItemSelectedIcon]", _config.GetValue( UiConst.MenuItemSelectedIcon ) ); + return this; + } + + /// + /// 配置自动分词分隔符 + /// + public SelectBuilder TokenSeparators() { + AttributeIfNotEmpty( "[nzTokenSeparators]", _config.GetValue( UiConst.TokenSeparators ) ); + return this; + } + + /// + /// 配置加载状态 + /// + public SelectBuilder Loading() { + return Loading( _config.GetValue( UiConst.Loading ) ); + } + + /// + /// 配置加载状态 + /// + public SelectBuilder Loading( string loading ) { + AttributeIfNotEmpty( "[nzLoading]", loading ); + return this; + } + + /// + /// 配置最大标签数量 + /// + public SelectBuilder MaxTagCount() { + AttributeIfNotEmpty( "[nzMaxTagCount]", _config.GetValue( UiConst.MaxTagCount ) ); + return this; + } + + /// + /// 配置最大标签占位符 + /// + public SelectBuilder MaxTagPlaceholder() { + AttributeIfNotEmpty( "[nzMaxTagPlaceholder]", _config.GetValue( UiConst.MaxTagPlaceholder ) ); + return this; + } + + /// + /// 配置选项列表 + /// + public SelectBuilder Options() { + AttributeIfNotEmpty( "[nzOptions]", _config.GetValue( UiConst.Options ) ); + return this; + } + + /// + /// 配置选项高度 + /// + public SelectBuilder OptionHeightPx() { + AttributeIfNotEmpty( "[nzOptionHeightPx]", _config.GetValue( UiConst.OptionHeightPx ) ); + return this; + } + + /// + /// 配置最大显示选项数量 + /// + public SelectBuilder OptionOverflowSize() { + AttributeIfNotEmpty( "[nzOptionOverflowSize]", _config.GetValue( UiConst.OptionOverflowSize ) ); + return this; + } + + /// + /// 配置自动加载 + /// + private SelectBuilder AutoLoad() { + AttributeIfNotEmpty( "[autoLoad]", _config.GetBoolValue( UiConst.AutoLoad ) ); + return this; + } + + /// + /// 配置查询参数 + /// + private SelectBuilder QueryParam() { + AttributeIfNotEmpty( "[(queryParam)]", _config.GetValue( UiConst.QueryParam ) ); + return this; + } + + /// + /// 配置排序条件 + /// + private SelectBuilder Sort() { + AttributeIfNotEmpty( "order", _config.GetValue( UiConst.Sort ) ); + AttributeIfNotEmpty( "[order]", _config.GetValue( AngularConst.BindSort ) ); + return this; + } + + /// + /// 配置Api地址 + /// + private SelectBuilder Url() { + AttributeIfNotEmpty( "url", _config.GetValue( UiConst.Url ) ); + AttributeIfNotEmpty( "[url]", _config.GetValue( AngularConst.BindUrl ) ); + return this; + } + + /// + /// 配置数据源 + /// + private SelectBuilder Data() { + AttributeIfNotEmpty( "[data]", _config.GetValue( UiConst.Data ) ); + return this; + } + + /// + /// 配置必填项验证 + /// + public override SelectBuilder Required() { + AttributeIfNotEmpty( "[x-required-extend]", _config.GetValue( UiConst.Required ) ); + return this; + } + + /// + /// 配置事件 + /// + public SelectBuilder Events() { + OnSearch( _config.GetValue( UiConst.OnSearch ) ); + OnScrollToBottom( _config.GetValue( UiConst.OnScrollToBottom ) ); + AttributeIfNotEmpty( "(nzOpenChange)", _config.GetValue( UiConst.OnOpenChange ) ); + AttributeIfNotEmpty( "(nzFocus)", _config.GetValue( UiConst.OnFocus ) ); + AttributeIfNotEmpty( "(nzBlur)", _config.GetValue( UiConst.OnBlur ) ); + AttributeIfNotEmpty( "(onLoad)", _config.GetValue( UiConst.OnLoad ) ); + return this; + } + + /// + /// 配置搜索事件 + /// + public SelectBuilder OnSearch( string value ) { + AttributeIfNotEmpty( "(nzOnSearch)", value ); + return this; + } + + /// + /// 配置滚动事件 + /// + public SelectBuilder OnScrollToBottom( string value ) { + AttributeIfNotEmpty( "(nzScrollToBottom)", value ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + ConfigForm().NzId().Name().CompareWith().AutoClearSearchValue().AllowClear() + .Borderless().Open().AutoFocus().Disabled() + .DropdownClassName().DropdownStyle().DropdownMatchSelectWidth() + .CustomTemplate().ServerSearch().FilterOption().MaxMultipleCount() + .Mode().NotFoundContent().Placeholder() + .ShowArrow().ShowSearch().Size().DropdownRender() + .SuffixIcon().RemoveIcon().ClearIcon().MenuItemSelectedIcon() + .TokenSeparators().Loading().MaxTagCount().MaxTagPlaceholder() + .Options().OptionHeightPx().OptionOverflowSize() + .AutoLoad().QueryParam().Sort().Url().Data() + .Events(); + ConfigDefaultOptionText(); + EnableExtend(); + } + + /// + /// 配置默认项文本 + /// + private void ConfigDefaultOptionText() { + var value = _config.AllAttributes[UiConst.DefaultOptionText]?.Value?.ToString(); + var optionBuilder = new OptionBuilder( _config ); + if ( string.IsNullOrEmpty( value ) == false ) { + optionBuilder.Label( value ); + AppendContent( optionBuilder ); + return; + } + if ( IsEnableDefaultOptionText() == false ) + return; + optionBuilder.BindLabel( $"'{I18nKeys.DefaultOptionText}'|i18n" ); + AppendContent( optionBuilder ); + } + + /// + /// 是否启用默认项文本 + /// + private bool IsEnableDefaultOptionText() { + var mode = _config.GetValue( UiConst.Mode ); + if ( mode == SelectMode.Multiple || mode == SelectMode.Tags ) + return false; + var showDefaultOption = _config.GetValue( UiConst.ShowDefaultOption ); + if ( showDefaultOption != null ) + return showDefaultOption.SafeValue(); + var options = NgZorroOptionsService.GetOptions(); + return options is { EnableDefaultOptionText: true }; + } + + /// + /// 启用扩展 + /// + public SelectBuilder EnableExtend() { + if ( IsEnableExtend() == false ) + return this; + Attribute( $"#{ExtendId}", "xSelectExtend" ); + Attribute( "x-select-extend" ); + EnableLoading(); + EnableSearchKeyword(); + EnableScrollLoad(); + ConfigOption(); + ConfigOptionGroup(); + return this; + } + + /// + /// 是否启用基础扩展 + /// + public bool IsEnableExtend() { + if ( GetEnableExtend() == false ) { + return false; + } + return GetEnableExtend() == true || + GetUrl().IsEmpty() == false || + GetBindUrl().IsEmpty() == false || + GetData().IsEmpty() == false || + IsEnableDefaultOptionText(); + } + + /// + /// 获取启用扩展属性 + /// + private bool? GetEnableExtend() { + return _config.GetValue( UiConst.EnableExtend ); + } + + /// + /// 获取地址 + /// + private string GetUrl() { + return _config.GetValue( UiConst.Url ); + } + + /// + /// 获取地址 + /// + private string GetBindUrl() { + return _config.GetValue( AngularConst.BindUrl ); + } + + /// + /// 获取数据源 + /// + private string GetData() { + return _config.GetValue( UiConst.Data ); + } + + /// + /// 启用加载状态 + /// + private void EnableLoading() { + if ( GetUrl().IsEmpty() && GetBindUrl().IsEmpty() ) + return; + Loading( $"{ExtendId}.loading" ); + } + + /// + /// 启用搜索关键字 + /// + private void EnableSearchKeyword() { + var result = _config.GetValue( UiConst.SearchKeyword ); + if ( result != true ) + return; + ShowSearch( "true" ); + ServerSearch( "true" ); + OnSearch( $"{ExtendId}.search($event)" ); + AttributeIfNotEmpty( "[searchDelay]", _config.GetValue( UiConst.SearchDelay ) ); + } + + /// + /// 启用滚动加载 + /// + private void EnableScrollLoad() { + var result = _config.GetValue( UiConst.ScrollLoad ); + if ( result != true ) + return; + ServerSearch( "true" ); + OnScrollToBottom( $"{ExtendId}.scrollToBottom()" ); + AttributeIfNotEmpty( "[isScrollLoad]", "true" ); + ConfigDropdownTemplate(); + } + + /// + /// 配置下拉加载图标 + /// + private void ConfigDropdownTemplate() { + var templateId = $"tpl_{ExtendId}"; + DropdownRender( templateId ); + var templateBuilder = new TemplateBuilder( _config ); + templateBuilder.Id( templateId ); + var spinBuilder = new SpinBuilder( _config ); + spinBuilder.NgIf( $"{ExtendId}.loading" ); + templateBuilder.AppendContent( spinBuilder ); + PostBuilder = templateBuilder; + } + + /// + /// 配置选项 + /// + private void ConfigOption() { + var containerBuilder = new ContainerBuilder( _config ); + containerBuilder.NgIf( $"!{ExtendId}.isGroup" ); + var optionBuilder = new OptionBuilder( _config ); + containerBuilder.AppendContent( optionBuilder ); + optionBuilder.NgFor( $"let item of {ExtendId}.options" ); + ConfigBindLabel( optionBuilder ); + optionBuilder.BindValue( "item.value" ); + optionBuilder.Disabled( "item.disabled" ); + AppendContent( containerBuilder ); + } + + /// + /// 配置标签文本 + /// + private void ConfigBindLabel( OptionBuilder optionBuilder ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + optionBuilder.BindLabel( "item.text|i18n" ); + return; + } + optionBuilder.BindLabel( "item.text" ); + } + + /// + /// 配置选项组 + /// + private void ConfigOptionGroup() { + var containerBuilder = new ContainerBuilder( _config ); + containerBuilder.NgIf( $"{ExtendId}.isGroup" ); + var groupBuilder = new OptionGroupBuilder( _config ); + containerBuilder.AppendContent( groupBuilder ); + groupBuilder.NgFor( $"let group of {ExtendId}.optionGroups" ); + ConfigGroupBindLabel( groupBuilder ); + var optionBuilder = new OptionBuilder( _config ); + groupBuilder.AppendContent( optionBuilder ); + optionBuilder.NgFor( "let item of group.value" ); + ConfigBindLabel( optionBuilder ); + optionBuilder.BindValue( "item.value" ); + optionBuilder.Disabled( "item.disabled" ); + AppendContent( containerBuilder ); + } + + /// + /// 配置选项组标签文本 + /// + private void ConfigGroupBindLabel( OptionGroupBuilder groupBuilder ) { + var options = NgZorroOptionsService.GetOptions(); + if( options.EnableI18n ) { + groupBuilder.BindLabel( "group.text|i18n" ); + return; + } + groupBuilder.BindLabel( "group.text" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Selects/Helpers/SelectExpressionLoader.cs b/src/Util.Ui.NgZorro/Components/Selects/Helpers/SelectExpressionLoader.cs new file mode 100644 index 000000000..e6e16d728 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Selects/Helpers/SelectExpressionLoader.cs @@ -0,0 +1,135 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Expressions; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Expressions; + +namespace Util.Ui.NgZorro.Components.Selects.Helpers; + +/// +/// 选择框表达式加载器 +/// +public class SelectExpressionLoader : NgZorroExpressionLoaderBase { + /// + /// 加载模型信息 + /// + /// 配置 + /// 模型表达式信息 + protected override void Load( Config config, ModelExpressionInfo info ) { + var formShareConfig = GetFormShareConfig( config ); + LoadLabel( config, info, formShareConfig ); + LoadId( config, info ); + LoadName( config, info ); + LoadNgModel( config, info ); + LoadData( config, info ); + LoadRequired( config, info ); + } + + /// + /// 获取表单共享配置 + /// + private FormShareConfig GetFormShareConfig( Config config ) { + return config.GetValueFromItems() ?? new FormShareConfig(); + } + + /// + /// 加载标签 + /// + protected virtual void LoadLabel( Config config, ModelExpressionInfo info, FormShareConfig formShareConfig ) { + if( formShareConfig.FormCreated ) + config.SetAttribute( UiConst.LabelText, info.DisplayName, false ); + } + + /// + /// 加载标识 + /// + protected virtual void LoadId( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.Id, GeKebaberizePropertyName( config, info ), false ); + } + + /// + /// 加载名称 + /// + protected virtual void LoadName( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.Name, GeKebaberizePropertyName( config, info ), false ); + } + + /// + /// 加载模型绑定 + /// + protected virtual void LoadNgModel( Config config, ModelExpressionInfo info ) { + config.SetAttribute( AngularConst.NgModel, info.SafePropertyName, false ); + } + + /// + /// 加载数据源 + /// + protected virtual void LoadData( Config config, ModelExpressionInfo info ) { + LoadBool( config, info ); + LoadEnum( config, info ); + } + + /// + /// 加载布尔值 + /// + protected virtual void LoadBool( Config config, ModelExpressionInfo info ) { + if ( info.IsBool == false ) + return; + config.SetAttribute( UiConst.Data, GetBoolData(), false ); + } + + /// + /// 获取布尔类型数据 + /// + private string GetBoolData() { + var result = new StringBuilder(); + result.Append( "[{" ); + result.Append( $"'text':'{GetYes()}','value':true,'sortId':1" ); + result.Append( "}," ); + result.Append( "{" ); + result.Append( $"'text':'{GetNo()}','value':false,'sortId':2" ); + result.Append( "}]" ); + return result.ToString(); + } + + /// + /// 获取yes文本 + /// + private string GetYes() { + var options = NgZorroOptionsService.GetOptions(); + return options.EnableI18n ? I18nKeys.Yes : "是"; + } + + /// + /// 获取no文本 + /// + private string GetNo() { + var options = NgZorroOptionsService.GetOptions(); + return options.EnableI18n ? I18nKeys.No : "否"; + } + + /// + /// 加载枚举值 + /// + protected virtual void LoadEnum( Config config, ModelExpressionInfo info ) { + if ( info.IsEnum == false ) + return; + var items = Util.Helpers.Enum.GetItems( info.ModelType ); + var options = new JsonSerializerOptions { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ) + }; + var result = Util.Helpers.Json.ToJson( items, options, toSingleQuotes: true ); + config.SetAttribute( UiConst.Data, result, false ); + } + + /// + /// 加载必填项验证 + /// + protected virtual void LoadRequired( Config config, ModelExpressionInfo info ) { + if ( info.IsRequired == false ) + return; + config.SetAttribute( UiConst.Required, "true", false ); + config.SetAttribute( UiConst.RequiredMessage, info.RequiredMessage, false ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Selects/Helpers/SelectService.cs b/src/Util.Ui.NgZorro/Components/Selects/Helpers/SelectService.cs new file mode 100644 index 000000000..8c6859d62 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Selects/Helpers/SelectService.cs @@ -0,0 +1,77 @@ +using Util.Ui.NgZorro.Components.Forms.Helpers; + +namespace Util.Ui.NgZorro.Components.Selects.Helpers; + +/// +/// 选择器服务 +/// +public class SelectService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 是否禁用自动设置表单标签 nzFor + /// + private bool _isDisableAutoNzFor; + + /// + /// 初始化选择器服务 + /// + /// 配置 + public SelectService( Config config ) { + _config = config; + } + + /// + /// 禁用自动设置表单标签 nzFor + /// + public void DisableAutoNzFor() { + _isDisableAutoNzFor = true; + } + + /// + /// 初始化 + /// + public void Init() { + LoadExpression(); + InitValidationService(); + InitFormShareService(); + InitFormItemShareService(); + } + + /// + /// 加载表达式 + /// + private void LoadExpression() { + var loader = new SelectExpressionLoader(); + loader.Load( _config ); + } + + /// + /// 初始化验证服务 + /// + private void InitValidationService() { + var service = new ValidationService( _config ); + service.Init(); + } + + /// + /// 初始化表单共享服务 + /// + private void InitFormShareService() { + var service = new FormShareService( _config ); + service.Init(); + } + + /// + /// 初始化表单项共享服务 + /// + private void InitFormItemShareService() { + var service = new FormItemShareService( _config ); + if( _isDisableAutoNzFor ) + service.DisableAutoNzFor(); + service.Init(); + service.InitId(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Selects/OptionGroupTagHelper.cs b/src/Util.Ui.NgZorro/Components/Selects/OptionGroupTagHelper.cs new file mode 100644 index 000000000..2830b67dd --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Selects/OptionGroupTagHelper.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Selects.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Selects; + +/// +/// 下拉选项组,生成的标签为<nz-option-group></nz-option-group>,放入下拉选择器 util-select 中使用 +/// +[HtmlTargetElement( "util-option-group" )] +public class OptionGroupTagHelper : AngularTagHelperBase { + /// + /// nzLabel,组名 + /// + public string Label { get; set; } + /// + /// [nzLabel],组名 + /// + public string BindLabel { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new OptionGroupRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Selects/OptionTagHelper.cs b/src/Util.Ui.NgZorro/Components/Selects/OptionTagHelper.cs new file mode 100644 index 000000000..df315621b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Selects/OptionTagHelper.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Selects.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Selects; + +/// +/// 下拉选项,生成的标签为<nz-option></nz-option>,放入下拉选择器 util-select 中使用 +/// +[HtmlTargetElement( "util-option" )] +public class OptionTagHelper : AngularTagHelperBase { + /// + /// [nzDisabled],是否禁用 + /// + public string Disabled { get; set; } + /// + /// nzLabel,标签 + /// + public string Label { get; set; } + /// + /// [nzLabel],标签 + /// + public string BindLabel { get; set; } + /// + /// nzValue,nz-select 中 ngModel 的值 + /// + public string Value { get; set; } + /// + /// [nzValue],nz-select 中 ngModel 的值 + /// + public string BindValue { get; set; } + /// + /// [nzHide],是否隐藏 + /// + public string Hide { get; set; } + /// + /// [nzCustomContent],是否自定义在下拉菜单中的Template内容,当为 true 时,nz-option 包裹的内容将直接渲染在下拉菜单中,默认值: false + /// + public string CustomContent { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new OptionRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Selects/Renders/OptionGroupRender.cs b/src/Util.Ui.NgZorro/Components/Selects/Renders/OptionGroupRender.cs new file mode 100644 index 000000000..b990bc491 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Selects/Renders/OptionGroupRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Selects.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Selects.Renders; + +/// +/// 选项组渲染器 +/// +public class OptionGroupRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化选项组渲染器 + /// + /// 配置 + public OptionGroupRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new OptionGroupBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new OptionGroupRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Selects/Renders/OptionRender.cs b/src/Util.Ui.NgZorro/Components/Selects/Renders/OptionRender.cs new file mode 100644 index 000000000..29a64d243 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Selects/Renders/OptionRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Selects.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Selects.Renders; + +/// +/// 选项渲染器 +/// +public class OptionRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化选项渲染器 + /// + /// 配置 + public OptionRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new OptionBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new OptionRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Selects/Renders/SelectRender.cs b/src/Util.Ui.NgZorro/Components/Selects/Renders/SelectRender.cs new file mode 100644 index 000000000..7feb09fc3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Selects/Renders/SelectRender.cs @@ -0,0 +1,53 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Selects.Builders; + +namespace Util.Ui.NgZorro.Components.Selects.Renders; + +/// +/// 选择器渲染器 +/// +public class SelectRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化选择器渲染器 + /// + /// 配置 + public SelectRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 初始化 + /// + protected override void Init() { + SetControlId(); + } + + /// + /// 设置控件Id + /// + protected void SetControlId() { + if ( ShareConfig.AutoNzFor != true ) + return; + _config.SetAttribute( UiConst.NzId, ShareConfig.ControlId ); + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new SelectBuilder( _config ); + builder.Config(); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new SelectRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Selects/SelectTagHelper.cs b/src/Util.Ui.NgZorro/Components/Selects/SelectTagHelper.cs new file mode 100644 index 000000000..da14bb46c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Selects/SelectTagHelper.cs @@ -0,0 +1,276 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Selects.Helpers; +using Util.Ui.NgZorro.Components.Selects.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Selects; + +/// +/// 选择器,生成的标签为<nz-select></nz-select> +/// +[HtmlTargetElement( "util-select" )] +public class SelectTagHelper : FormControlTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 扩展属性,是否启用扩展指令,当设置 url 或 data 属性时自动启用,默认值: false + /// + public bool EnableExtend { get; set; } + /// + /// 扩展属性 [autoLoad],初始化时是否自动加载数据,默认值: true,设置成 false 手工加载 + /// + public bool AutoLoad { get; set; } + /// + /// 扩展属性 [(queryParam)],查询参数 + /// + public string QueryParam { get; set; } + /// + /// 扩展属性 order,排序条件,范例: creationTime desc + /// + public string Sort { get; set; } + /// + /// 扩展属性 [order],排序条件,范例: creationTime desc + /// + public string BindSort { get; set; } + /// + /// 扩展属性 url,Api地址 + /// + public string Url { get; set; } + /// + /// 扩展属性 [url],Api地址 + /// + public string BindUrl { get; set; } + /// + /// 扩展属性 [data],数据源 + /// + public string Data { get; set; } + /// + /// 扩展属性,是否显示默认项,默认项显示在列表的第一行 + /// + public bool ShowDefaultOption { get; set; } + /// + /// 扩展属性,默认项文本,默认项显示在列表的第一行 + /// + public string DefaultOptionText { get; set; } + /// + /// 扩展属性,是否启用服务端搜索关键字,在搜索框输入时,设置查询参数的Keyword属性并发送请求,默认值:false + /// + public bool SearchKeyword { get; set; } + /// + /// 扩展属性 [searchDelay],搜索关键字延迟时间,单位:毫秒,默认值: 500 + /// + public int SearchDelay { get; set; } + /// + /// 扩展属性 [isScrollLoad],滚动到底部是否自动加载,默认值:false + /// + public bool ScrollLoad { get; set; } + /// + /// [required],是否必填项 + /// + public string Required { get; set; } + /// + /// 扩展属性 requiredMessage,必填项验证消息 + /// + public string RequiredMessage { get; set; } + /// + /// 扩展属性 [requiredMessage],必填项验证消息 + /// + public string BindRequiredMessage { get; set; } + /// + /// [compareWith],比较算法函数,函数定义:(o1: any, o2: any) => boolean + /// + public string CompareWith { get; set; } + /// + /// [nzAutoClearSearchValue],是否在选中项后清空搜索框,只在 mode 为 multiple 或 tags 时有效,默认值: true + /// + public string AutoClearSearchValue { get; set; } + /// + /// [nzAllowClear],允许清除,默认值: false + /// + public string AllowClear { get; set; } + /// + /// [nzBorderless],是否移除边框 + /// + public string Borderless { get; set; } + /// + /// [nzOpen],是否打开下拉菜单 + /// + public string Open { get; set; } + /// + /// [(nzOpen)],是否打开下拉菜单 + /// + public string BindonOpen { get; set; } + /// + /// [nzAutoFocus],是否自动聚焦,默认值: false + /// + public string AutoFocus { get; set; } + /// + /// [nzDisabled],是否禁用 + /// + public string Disabled { get; set; } + /// + /// nzDropdownClassName,下拉菜单样式类名 + /// + public string DropdownClassName { get; set; } + /// + /// [nzDropdownClassName],下拉菜单样式类名 + /// + public string BindDropdownClassName { get; set; } + /// + /// [nzDropdownStyle],下拉菜单样式,类型: { [key: string]: string; },范例: { 'max-height': '300px' } + /// + public string DropdownStyle { get; set; } + /// + /// [nzDropdownMatchSelectWidth],是否下拉菜单和选择器同宽,默认值: true + /// + public string DropdownMatchSelectWidth { get; set; } + /// + /// [nzCustomTemplate],自定义选择框模板,类型:TemplateRef<{ $implicit: NzOptionComponent }> + /// + public string CustomTemplate { get; set; } + /// + /// [nzServerSearch],是否使用服务端搜索,当为 true 时,将不在前端对 nz-option 进行过滤,默认值: false + /// + public string ServerSearch { get; set; } + /// + /// [nzFilterOption],过滤项函数,接收 inputValue,option 两个参数,当 option 符合筛选条件时,应返回 true,反之则返回 false。函数定义: (input?: string, option?: NzOptionComponent) => boolean; + /// + public string FilterOption { get; set; } + /// + /// [nzMaxMultipleCount],允许选中的最大数量 + /// + public string MaxMultipleCount { get; set; } + /// + /// nzMode,选择模式,可选模式: 'multiple' | 'tags' | 'default',默认值: 'default' + /// + public SelectMode Mode { get; set; } + /// + /// [nzMode],选择模式,可选模式: 'multiple' | 'tags' | 'default',默认值: 'default' + /// + public string BindMode { get; set; } + /// + /// nzNotFoundContent,当下拉列表为空时显示的内容,类型: string | TemplateRef</void> + /// + public string NotFoundContent { get; set; } + /// + /// [nzNotFoundContent],当下拉列表为空时显示的内容,类型: string | TemplateRef</void> + /// + public string BindNotFoundContent { get; set; } + /// + /// nzPlaceHolder,占位提示 + /// + public string Placeholder { get; set; } + /// + /// [nzPlaceHolder],占位提示 + /// + public string BindPlaceholder { get; set; } + /// + /// [nzShowArrow],是否显示下拉小箭头,默认值: 单选为 true,多选为 false + /// + public string ShowArrow { get; set; } + /// + /// [nzShowSearch],使单选模式可搜索,默认值: false + /// + public string ShowSearch { get; set; } + /// + /// nzSize,选择框大小,可选值: 'large'|'small'|'default',默认值: 'default' + /// + public InputSize Size { get; set; } + /// + /// [nzSize],选择框大小,可选值: 'large'|'small'|'default',默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// nzSuffixIcon,选择框后缀图标,类型: string | TemplateRef</any> + /// + public AntDesignIcon SuffixIcon { get; set; } + /// + /// [nzSuffixIcon],选择框后缀图标,类型: string | TemplateRef</any> + /// + public string BindSuffixIcon { get; set; } + /// + /// [nzRemoveIcon],多选框清除图标,类型: TemplateRef</any> + /// + public string RemoveIcon { get; set; } + /// + /// [nzClearIcon],多选框清空图标,类型: TemplateRef</any> + /// + public string ClearIcon { get; set; } + /// + /// [nzMenuItemSelectedIcon],当前选中项图标,类型: TemplateRef</any> + /// + public string MenuItemSelectedIcon { get; set; } + /// + /// [nzTokenSeparators],在 tags 和 multiple 模式下自动分词的分隔符,类型: string[] + /// + public string TokenSeparators { get; set; } + /// + /// [nzLoading],加载状态,类型: boolean,默认值: false + /// + public string Loading { get; set; } + /// + /// [nzMaxTagCount],最多显示的标签数量,类型: number + /// + public string MaxTagCount { get; set; } + /// + /// [nzMaxTagPlaceholder],隐藏标签时显示的占位内容,类型: TemplateRef<{ $implicit: any[] }> + /// + public string MaxTagPlaceholder { get; set; } + /// + /// [nzOptions],选项列表,可以取代 nz-option,类型: Array<{ label: string | TemplateRef<any>; value: any; disabled?: boolean; hide?: boolean; groupLabel?: string | TemplateRef<any>;}> + /// + public string Options { get; set; } + /// + /// [nzOptionHeightPx],下拉菜单中每个选项的高度,单位像素,默认值: 32 + /// + public string OptionHeightPx { get; set; } + /// + /// [nzOptionOverflowSize],下拉菜单中最多展示的选项数量,超出部分滚动,默认值: 8 + /// + public string OptionOverflowSize { get; set; } + /// + /// [nzDropdownRender],设置下拉扩展模板,类型: TemplateRef + /// + public string DropdownRender { get; set; } + /// + /// (nzOpenChange),下拉菜单打开状态变更事件,类型: EventEmitter<boolean> + /// + public string OnOpenChange { get; set; } + /// + /// (nzScrollToBottom),下拉列表滚动到底部事件,类型: EventEmitter<any> + /// + public string OnScrollToBottom { get; set; } + /// + /// (nzOnSearch),文本框值变更事件,类型: EventEmitter<string> + /// + public string OnSearch { get; set; } + /// + /// (nzFocus),获得焦点事件,类型: EventEmitter<any> + /// + public string OnFocus { get; set; } + /// + /// (nzBlur),失去焦点事件,类型: EventEmitter<any> + /// + public string OnBlur { get; set; } + /// + /// 扩展事件 (onLoad),数据加载完成事件,类型: EventEmitter<any>,参数为服务端返回结果 + /// + public string OnLoad { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new SelectService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new SelectRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Skeletons/Builders/SkeletonBuilder.cs b/src/Util.Ui.NgZorro/Components/Skeletons/Builders/SkeletonBuilder.cs new file mode 100644 index 000000000..0ba3f5c83 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Skeletons/Builders/SkeletonBuilder.cs @@ -0,0 +1,78 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; + +namespace Util.Ui.NgZorro.Components.Skeletons.Builders; + +/// +/// 骨架屏标签生成器 +/// +public class SkeletonBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化骨架屏标签生成器 + /// + /// 配置 + public SkeletonBuilder( Config config ) : base( config,"nz-skeleton" ) { + _config = config; + } + + /// + /// 配置是否显示动画效果 + /// + public SkeletonBuilder Active() { + AttributeIfNotEmpty( "[nzActive]", _config.GetValue( UiConst.Active ) ); + return this; + } + + /// + /// 配置是否显示头像占位图 + /// + public SkeletonBuilder Avatar() { + AttributeIfNotEmpty( "[nzAvatar]", _config.GetValue( UiConst.Avatar ) ); + return this; + } + + /// + /// 配置是否加载状态 + /// + public SkeletonBuilder Loading() { + AttributeIfNotEmpty( "[nzLoading]", _config.GetValue( UiConst.Loading ) ); + return this; + } + + /// + /// 配置是否显示段落占位图 + /// + public SkeletonBuilder Paragraph() { + AttributeIfNotEmpty( "[nzParagraph]", _config.GetValue( UiConst.Paragraph ) ); + return this; + } + + /// + /// 配置是否显示标题占位图 + /// + public SkeletonBuilder Title() { + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( UiConst.Title ) ); + return this; + } + + /// + /// 配置是否显示圆角 + /// + public SkeletonBuilder Round() { + AttributeIfNotEmpty( "[nzRound]", _config.GetValue( UiConst.Round ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Active().Avatar().Loading().Paragraph().Title().Round(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Skeletons/Builders/SkeletonElementBuilder.cs b/src/Util.Ui.NgZorro/Components/Skeletons/Builders/SkeletonElementBuilder.cs new file mode 100644 index 000000000..1fef05407 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Skeletons/Builders/SkeletonElementBuilder.cs @@ -0,0 +1,70 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Skeletons.Builders; + +/// +/// 骨架屏元素标签生成器 +/// +public class SkeletonElementBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化骨架屏元素标签生成器 + /// + /// 配置 + public SkeletonElementBuilder( Config config ) : base( config, "nz-skeleton-element" ) { + _config = config; + } + + /// + /// 配置类型 + /// + public SkeletonElementBuilder Type() { + AttributeIfNotEmpty( "nzType", _config.GetValue( UiConst.Type )?.Description() ); + AttributeIfNotEmpty( "[nzType]", _config.GetValue( AngularConst.BindType ) ); + return this; + } + + /// + /// 配置是否显示动画效果 + /// + public SkeletonElementBuilder Active() { + AttributeIfNotEmpty( "[nzActive]", _config.GetValue( UiConst.Active ) ); + return this; + } + + /// + /// 配置大小 + /// + public SkeletonElementBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置形状 + /// + public SkeletonElementBuilder Shape() { + AttributeIfNotEmpty( "nzShape", _config.GetValue( UiConst.Shape ) ); + AttributeIfNotEmpty( "nzShape", _config.GetValue( UiConst.ButtonShape )?.Description() ); + AttributeIfNotEmpty( "nzShape", _config.GetValue( UiConst.AvatarShape )?.Description() ); + AttributeIfNotEmpty( "[nzShape]", _config.GetValue( AngularConst.BindShape ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + this.SpaceItem( _config ); + Type().Active().Size().Shape(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Skeletons/Renders/SkeletonElementRender.cs b/src/Util.Ui.NgZorro/Components/Skeletons/Renders/SkeletonElementRender.cs new file mode 100644 index 000000000..0117a660d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Skeletons/Renders/SkeletonElementRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Skeletons.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Skeletons.Renders; + +/// +/// 骨架屏元素渲染器 +/// +public class SkeletonElementRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化骨架屏元素渲染器 + /// + /// 配置 + public SkeletonElementRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new SkeletonElementBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new SkeletonElementRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Skeletons/Renders/SkeletonRender.cs b/src/Util.Ui.NgZorro/Components/Skeletons/Renders/SkeletonRender.cs new file mode 100644 index 000000000..6dfd336c4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Skeletons/Renders/SkeletonRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Skeletons.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Skeletons.Renders; + +/// +/// 骨架屏渲染器 +/// +public class SkeletonRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化骨架屏渲染器 + /// + /// 配置 + public SkeletonRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new SkeletonBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new SkeletonRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Skeletons/SkeletonElementTagHelper.cs b/src/Util.Ui.NgZorro/Components/Skeletons/SkeletonElementTagHelper.cs new file mode 100644 index 000000000..603381a68 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Skeletons/SkeletonElementTagHelper.cs @@ -0,0 +1,60 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Skeletons.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Skeletons; + +/// +/// 骨架屏元素,生成的标签为<nz-skeleton-element></nz-skeleton-element> +/// +[HtmlTargetElement( "util-skeleton-element" )] +public class SkeletonElementTagHelper : AngularTagHelperBase { + /// + /// nzType,骨架屏元素类型,可选值: 'button' | 'avatar' | 'input' | 'image' + /// + public SkeletonElementType Type { get; set; } + /// + /// [nzType],骨架屏元素类型,可选值: 'button' | 'avatar' | 'input' | 'image' + /// + public string BindType { get; set; } + /// + /// [nzActive],是否显示动画效果,类型: boolean,默认值: false + /// + public string Active { get; set; } + /// + /// nzSize,大小,可选值: 'large' | 'small' | 'default',默认值: 'default' + /// + public SkeletonElementSize Size { get; set; } + /// + /// [nzSize],大小,可选值: 'large' | 'small' | 'default',当 nzType="avatar" 时,可以设置为数字,默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// nzShape,形状,当 nzType="avatar" 时,可选值为: 'circle' | 'square',当 nzType="button" 时,可选值为: 'square' | 'circle' | 'round' | 'default' + /// + public string Shape { get; set; } + /// + /// nzShape,形状,当 nzType="avatar" 时有效,可选值: 'circle' | 'square',默认值: 'square' + /// + public SkeletonAvatarShape AvatarShape { get; set; } + /// + /// nzShape,形状,当 nzType="button" 时有效,可选值: 'square' | 'circle' | 'round' | 'default',默认值: 'default' + /// + public SkeletonButtonShape ButtonShape { get; set; } + /// + /// [nzShape],形状,当 nzType="avatar" 时有效,可选值: 'circle' | 'square',默认值: 'square' + /// + public string BindShape { get; set; } + /// + /// *nzSpaceItem,值为 true 时设置为间距项,放入 <util-space> 中使用 + /// + public bool SpaceItem { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new SkeletonElementRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Skeletons/SkeletonTagHelper.cs b/src/Util.Ui.NgZorro/Components/Skeletons/SkeletonTagHelper.cs new file mode 100644 index 000000000..83ea1caec --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Skeletons/SkeletonTagHelper.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Skeletons.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Skeletons; + +/// +/// 骨架屏,生成的标签为<nz-skeleton></nz-skeleton> +/// +[HtmlTargetElement( "util-skeleton" )] +public class SkeletonTagHelper : AngularTagHelperBase { + /// + /// [nzActive],是否显示动画效果,类型: boolean,默认值: false + /// + public string Active { get; set; } + /// + /// [nzAvatar],是否显示头像占位图,类型: boolean | NzSkeletonAvatar,默认值: false + /// + public string Avatar { get; set; } + /// + /// [nzLoading],是否加载状态, 值为 true 时,显示占位图,false则直接显示子组件,类型: boolean + /// + public string Loading { get; set; } + /// + /// [nzParagraph],是否显示段落占位图,类型: boolean | NzSkeletonParagraph,默认值: true + /// + public string Paragraph { get; set; } + /// + /// [nzTitle],是否显示标题占位图,类型: boolean | NzSkeletonTitle,默认值: true + /// + public string Title { get; set; } + /// + /// [nzRound],段落和标题是否显示圆角,类型: boolean,默认值: false + /// + public string Round { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new SkeletonRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Sliders/Builders/SliderBuilder.cs b/src/Util.Ui.NgZorro/Components/Sliders/Builders/SliderBuilder.cs new file mode 100644 index 000000000..c1bb4d5b3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Sliders/Builders/SliderBuilder.cs @@ -0,0 +1,148 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Sliders.Builders; + +/// +/// 滑动输入条标签生成器 +/// +public class SliderBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化滑动输入条标签生成器 + /// + /// 配置 + public SliderBuilder( Config config ) : base( config, "nz-slider" ) { + _config = config; + } + + /// + /// 配置禁用 + /// + public SliderBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置是否只能拖拽到刻度 + /// + public SliderBuilder Dots() { + AttributeIfNotEmpty( "[nzDots]", _config.GetValue( UiConst.Dots ) ); + return this; + } + + /// + /// 配置是否包含 + /// + public SliderBuilder Included() { + AttributeIfNotEmpty( "[nzIncluded]", _config.GetValue( UiConst.Included ) ); + return this; + } + + /// + /// 配置刻度标记 + /// + public SliderBuilder Marks() { + AttributeIfNotEmpty( "[nzMarks]", _config.GetValue( UiConst.Marks ) ); + return this; + } + + /// + /// 配置最大值 + /// + public SliderBuilder Max() { + AttributeIfNotEmpty( "[nzMax]", _config.GetValue( UiConst.Max ) ); + return this; + } + + /// + /// 配置最小值 + /// + public SliderBuilder Min() { + AttributeIfNotEmpty( "[nzMin]", _config.GetValue( UiConst.Min ) ); + return this; + } + + /// + /// 配置双滑块模式 + /// + public SliderBuilder Range() { + AttributeIfNotEmpty( "[nzRange]", _config.GetValue( UiConst.Range ) ); + return this; + } + + /// + /// 配置步长 + /// + public SliderBuilder Step() { + AttributeIfNotEmpty( "[nzStep]", _config.GetValue( UiConst.Step ) ); + return this; + } + + /// + /// 配置提示格式化函数 + /// + public SliderBuilder TipFormatter() { + AttributeIfNotEmpty( "[nzTipFormatter]", _config.GetValue( UiConst.TipFormatter ) ); + return this; + } + + /// + /// 配置垂直方向 + /// + public SliderBuilder Vertical() { + AttributeIfNotEmpty( "[nzVertical]", _config.GetValue( UiConst.Vertical ) ); + return this; + } + + /// + /// 配置反向 + /// + public SliderBuilder Reverse() { + AttributeIfNotEmpty( "[nzReverse]", _config.GetValue( UiConst.Reverse ) ); + return this; + } + + /// + /// 配置提示可见性 + /// + public SliderBuilder TooltipVisible() { + AttributeIfNotEmpty( "nzTooltipVisible", _config.GetValue( UiConst.TooltipVisible )?.Description() ); + AttributeIfNotEmpty( "[nzTooltipVisible]", _config.GetValue( AngularConst.BindTooltipVisible ) ); + return this; + } + + /// + /// 配置提示位置 + /// + public SliderBuilder TooltipPlacement() { + AttributeIfNotEmpty( "nzTooltipPlacement", _config.GetValue( UiConst.TooltipPlacement )?.Description() ); + AttributeIfNotEmpty( "[nzTooltipPlacement]", _config.GetValue( AngularConst.BindTooltipPlacement ) ); + return this; + } + + /// + /// 配置事件 + /// + public SliderBuilder Events() { + AttributeIfNotEmpty( "(nzOnAfterChange)", _config.GetValue( UiConst.OnAfterChange ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + ConfigForm().Disabled().Dots().Included().Marks() + .Max().Min().Range().Step().TipFormatter().Vertical() + .Reverse().TooltipVisible().TooltipPlacement() + .Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Sliders/Renders/SliderRender.cs b/src/Util.Ui.NgZorro/Components/Sliders/Renders/SliderRender.cs new file mode 100644 index 000000000..523da914d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Sliders/Renders/SliderRender.cs @@ -0,0 +1,40 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Sliders.Builders; + +namespace Util.Ui.NgZorro.Components.Sliders.Renders; + +/// +/// 滑动输入条渲染器 +/// +public class SliderRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化滑动输入条渲染器 + /// + /// 配置 + public SliderRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new SliderBuilder( _config ); + builder.Config(); + _config.Content.AppendTo( builder ); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new SliderRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Sliders/SliderTagHelper.cs b/src/Util.Ui.NgZorro/Components/Sliders/SliderTagHelper.cs new file mode 100644 index 000000000..1df9b10dd --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Sliders/SliderTagHelper.cs @@ -0,0 +1,84 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Sliders.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Sliders; + +/// +/// 滑动输入条,生成的标签为<nz-slider></nz-slider> +/// +[HtmlTargetElement( "util-slider" )] +public class SliderTagHelper : FormControlTagHelperBase { + /// + /// [nzDisabled],是否禁用,默认值: false + /// + public string Disabled { get; set; } + /// + /// [nzDots],是否只能拖拽到刻度上,默认值: false + /// + public string Dots { get; set; } + /// + /// [nzIncluded],是否包含关系,marks 不为空对象时有效,值为 true 时表示值为包含关系,false 表示并列,默认值: true + /// + public string Included { get; set; } + /// + /// [nzMarks],刻度标记,key 的类型必须为 number 且取值在闭区间 [min, max] 内,每个标签可以单独设置样式,范例:{ 0: '0°C', 26: '26°C', 37: '37°C', 100: {style: {color: '#f50'},label: '<strong>100°C</strong>'} } + /// + public string Marks { get; set; } + /// + /// [nzMax],最大值,默认值: 100 + /// + public string Max { get; set; } + /// + /// [nzMin],最小值,默认值: 0 + /// + public string Min { get; set; } + /// + /// [nzRange],是否启用双滑块模式,默认值: false + /// + public string Range { get; set; } + /// + /// [nzStep],步长,取值必须大于 0,并且可被 (max - min) 整除。当 marks 不为空对象时,可以设置 step 为 null,此时可选值仅有 marks 标出来的部分。默认值: 1 + /// + public string Step { get; set; } + /// + /// [nzTipFormatter],提示格式化函数,把当前值传给该函数,并在 Tooltip 中显示它的返回值,若为 null,则隐藏 Tooltip,函数定义: (value: number) => string + /// + public string TipFormatter { get; set; } + /// + /// [nzVertical],是否垂直方向,默认值: false + /// + public string Vertical { get; set; } + /// + /// [nzReverse],是否反向,默认值: false + /// + public string Reverse { get; set; } + /// + /// nzTooltipVisible,提示可见性,可选值: 'default' | 'always' | 'never',值为 always 时总是显示,值为 never 时在任何情况下都不显示 + /// + public SliderTooltipVisible TooltipVisible { get; set; } + /// + /// [nzTooltipVisible],提示可见性,可选值: 'default' | 'always' | 'never',值为 always 时总是显示,值为 never 时在任何情况下都不显示 + /// + public string BindTooltipVisible { get; set; } + /// + /// nzTooltipPlacement,提示位置,可选值: 'top' | 'left' | 'right' | 'bottom' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom' + /// + public TooltipPlacement TooltipPlacement { get; set; } + /// + /// [nzTooltipPlacement],提示位置,可选值: 'top' | 'left' | 'right' | 'bottom' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom' + /// + public string BindTooltipPlacement { get; set; } + /// + /// (nzOnAfterChange),变更后事件,与 onmouseup 触发时机一致,把当前值作为参数传入,类型: EventEmitter<number[] | number> + /// + public string OnAfterChange { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new SliderRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Spaces/Builders/SpaceBuilder.cs b/src/Util.Ui.NgZorro/Components/Spaces/Builders/SpaceBuilder.cs new file mode 100644 index 000000000..4ad8fbac6 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Spaces/Builders/SpaceBuilder.cs @@ -0,0 +1,74 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Spaces.Builders; + +/// +/// 间距标签生成器 +/// +public class SpaceBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化间距标签生成器 + /// + /// 配置 + public SpaceBuilder( Config config ) : base( config,"nz-space" ) { + _config = config; + } + + /// + /// 配置间距大小 + /// + public SpaceBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置间距方向 + /// + public SpaceBuilder Direction() { + AttributeIfNotEmpty( "nzDirection", _config.GetValue( UiConst.Direction )?.Description() ); + AttributeIfNotEmpty( "[nzDirection]", _config.GetValue( AngularConst.BindDirection ) ); + return this; + } + + /// + /// 配置对齐方式 + /// + public SpaceBuilder Align() { + AttributeIfNotEmpty( "nzAlign", _config.GetValue( UiConst.Align )?.Description() ); + AttributeIfNotEmpty( "[nzAlign]", _config.GetValue( AngularConst.BindAlign ) ); + return this; + } + + /// + /// 配置设置分隔符 + /// + public SpaceBuilder Split() { + AttributeIfNotEmpty( "[nzSplit]", _config.GetValue( UiConst.Split ) ); + return this; + } + + /// + /// 配置自动换行 + /// + public SpaceBuilder Wrap() { + AttributeIfNotEmpty( "[nzWrap]", _config.GetValue( UiConst.Wrap ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Size().Direction().Align().Split().Wrap(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Spaces/Renders/SpaceRender.cs b/src/Util.Ui.NgZorro/Components/Spaces/Renders/SpaceRender.cs new file mode 100644 index 000000000..74b65c8c0 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Spaces/Renders/SpaceRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Spaces.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Spaces.Renders; + +/// +/// 间距渲染器 +/// +public class SpaceRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化间距渲染器 + /// + /// 配置 + public SpaceRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new SpaceBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new SpaceRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Spaces/SpaceTagHelper.cs b/src/Util.Ui.NgZorro/Components/Spaces/SpaceTagHelper.cs new file mode 100644 index 000000000..4889b15ef --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Spaces/SpaceTagHelper.cs @@ -0,0 +1,52 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Spaces.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Spaces; + +/// +/// 间距,生成的标签为<nz-space></nz-space> +/// +[HtmlTargetElement( "util-space")] +public class SpaceTagHelper : AngularTagHelperBase { + /// + /// nzSize,间距大小,可选值: 'small' | 'middle' | 'large', 默认值: small + /// + public SpaceSize Size { get; set; } + /// + /// [nzSize],间距大小,可选值: 'small' | 'middle' | 'large', 默认值: small + /// + public string BindSize { get; set; } + /// + /// nzDirection,间距方向,水平方向或垂直方向,默认水平方向,可选值: 'horizontal' | 'vertical' + /// + public SpaceDirection Direction { get; set; } + /// + /// [nzDirection],间距方向,水平方向或垂直方向,默认水平方向,可选值: 'horizontal' | 'vertical' + /// + public string BindDirection { get; set; } + /// + /// nzAlign,对齐方式,可选值: 'start' | 'end' | 'center' | 'baseline' + /// + public SpaceAlign Align { get; set; } + /// + /// [nzAlign],对齐方式,可选值: 'start' | 'end' | 'center' | 'baseline' + /// + public string BindAlign { get; set; } + /// + /// [nzSplit],设置分隔符,类型: TemplateRef<void> + /// + public string Split { get; set; } + /// + /// [nzWrap],是否自动换行,仅在 `nzDirection` 为 `horizontal` 时有效,类型: boolean , 默认值: false + /// + public string Wrap { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new SpaceRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Spins/Builders/SpinBuilder.cs b/src/Util.Ui.NgZorro/Components/Spins/Builders/SpinBuilder.cs new file mode 100644 index 000000000..ba37a6ed3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Spins/Builders/SpinBuilder.cs @@ -0,0 +1,81 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Spins.Builders; + +/// +/// 加载中标签生成器 +/// +public class SpinBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化加载中标签生成器 + /// + /// 配置 + public SpinBuilder( Config config ) : base( config,"nz-spin" ) { + _config = config; + } + + /// + /// 配置延迟显示时间 + /// + public SpinBuilder Delay() { + AttributeIfNotEmpty( "[nzDelay]", _config.GetValue( UiConst.Delay ) ); + return this; + } + + /// + /// 配置加载指示符 + /// + public SpinBuilder Indicator() { + AttributeIfNotEmpty( "[nzIndicator]", _config.GetValue( UiConst.Indicator ) ); + return this; + } + + /// + /// 配置大小 + /// + public SpinBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置是否旋转 + /// + public SpinBuilder Spinning() { + AttributeIfNotEmpty( "[nzSpinning]", _config.GetValue( UiConst.Spinning ) ); + return this; + } + + /// + /// 配置是否简单模式 + /// + public SpinBuilder Simple() { + AttributeIfNotEmpty( "[nzSimple]", _config.GetValue( UiConst.Simple ) ); + return this; + } + + /// + /// 配置提示文字 + /// + public SpinBuilder Tip() { + AttributeIfNotEmpty( "nzTip", _config.GetValue( UiConst.Tip ) ); + AttributeIfNotEmpty( "[nzTip]", _config.GetValue( AngularConst.BindTip ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Delay().Indicator().Size().Spinning().Simple().Tip(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Spins/Renders/SpinRender.cs b/src/Util.Ui.NgZorro/Components/Spins/Renders/SpinRender.cs new file mode 100644 index 000000000..b6d9f3151 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Spins/Renders/SpinRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Spins.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Spins.Renders; + +/// +/// 加载中渲染器 +/// +public class SpinRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化加载中渲染器 + /// + /// 配置 + public SpinRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new SpinBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new SpinRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Spins/SpinTagHelper.cs b/src/Util.Ui.NgZorro/Components/Spins/SpinTagHelper.cs new file mode 100644 index 000000000..7559d44d4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Spins/SpinTagHelper.cs @@ -0,0 +1,52 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Spins.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Spins; + +/// +/// 加载中,生成的标签为<nz-spin></nz-spin> +/// +[HtmlTargetElement( "util-spin" )] +public class SpinTagHelper : AngularTagHelperBase { + /// + /// [nzDelay],延迟显示加载效果的时间(防止闪烁),单位:毫秒 + /// + public string Delay { get; set; } + /// + /// [nzIndicator],加载指示符,自定义加载图标,类型: TemplateRef<void> + /// + public string Indicator { get; set; } + /// + /// nzSize,大小,可选值: 'large' | 'small' | 'default',默认值: 'default' + /// + public SpinSize Size { get; set; } + /// + /// [nzSize],大小,可选值: 'large' | 'small' | 'default',默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// [nzSpinning],是否旋转,默认值: true + /// + public string Spinning { get; set; } + /// + /// [nzSimple],是否简单模式,如果不包裹其它元素,设置为true,默认值: false + /// + public string Simple { get; set; } + /// + /// nzTip,当作为包裹元素时,可以自定义提示文字 + /// + public string Tip { get; set; } + /// + /// [nzTip],当作为包裹元素时,可以自定义提示文字 + /// + public string BindTip { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new SpinRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Statistics/Builders/CountdownBuilder.cs b/src/Util.Ui.NgZorro/Components/Statistics/Builders/CountdownBuilder.cs new file mode 100644 index 000000000..197327d12 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Statistics/Builders/CountdownBuilder.cs @@ -0,0 +1,46 @@ +using Util.Ui.Angular.Configs; + +namespace Util.Ui.NgZorro.Components.Statistics.Builders; + +/// +/// 倒计时标签生成器 +/// +public class CountdownBuilder : StatisticBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化倒计时标签生成器 + /// + /// 配置 + public CountdownBuilder( Config config ) : base( config,"nz-countdown" ) { + _config = config; + } + + /// + /// 配置格式化 + /// + public CountdownBuilder Format() { + AttributeIfNotEmpty( "nzFormat", _config.GetValue( UiConst.Format ) ); + AttributeIfNotEmpty( "[nzFormat]", _config.GetValue( AngularConst.BindFormat ) ); + return this; + } + + /// + /// 配置事件 + /// + public CountdownBuilder Events() { + AttributeIfNotEmpty( "(nzCountdownFinish)", _config.GetValue( UiConst.OnCountdownFinish ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Format().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Statistics/Builders/StatisticBuilder.cs b/src/Util.Ui.NgZorro/Components/Statistics/Builders/StatisticBuilder.cs new file mode 100644 index 000000000..5a5291f0f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Statistics/Builders/StatisticBuilder.cs @@ -0,0 +1,104 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Statistics.Builders; + +/// +/// 统计标签生成器 +/// +public class StatisticBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化统计标签生成器 + /// + /// 配置 + public StatisticBuilder( Config config ) : this( config, "nz-statistic" ) { + } + + /// + /// 初始化统计标签生成器 + /// + /// 配置 + /// 标签名 + public StatisticBuilder( Config config, string tag ) : base( config, tag ) { + _config = config; + } + + /// + /// 配置标题 + /// + public StatisticBuilder Title() { + SetTitle( _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 设置标题 + /// + private void SetTitle( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzTitle]", value ); + return; + } + AttributeIfNotEmpty( "nzTitle", value ); + } + + /// + /// 配置数值 + /// + public StatisticBuilder Value() { + AttributeIfNotEmpty( "nzValue", _config.GetValue( UiConst.Value ) ); + AttributeIfNotEmpty( "[nzValue]", _config.GetValue( AngularConst.BindValue ) ); + return this; + } + + /// + /// 配置前缀 + /// + public StatisticBuilder Prefix() { + AttributeIfNotEmpty( "nzPrefix", _config.GetValue( UiConst.Prefix ) ); + AttributeIfNotEmpty( "[nzPrefix]", _config.GetValue( AngularConst.BindPrefix ) ); + return this; + } + + /// + /// 配置后缀 + /// + public StatisticBuilder Suffix() { + AttributeIfNotEmpty( "nzSuffix", _config.GetValue( UiConst.Suffix ) ); + AttributeIfNotEmpty( "[nzSuffix]", _config.GetValue( AngularConst.BindSuffix ) ); + return this; + } + + /// + /// 配置样式 + /// + public StatisticBuilder ValueStyle() { + AttributeIfNotEmpty( "[nzValueStyle]", _config.GetValue( UiConst.ValueStyle ) ); + return this; + } + + /// + /// 配置模板 + /// + public StatisticBuilder ValueTemplate() { + AttributeIfNotEmpty( "[nzValueTemplate]", _config.GetValue( UiConst.ValueTemplate ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Title().Value().Prefix().Suffix().ValueStyle().ValueTemplate(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Statistics/CountdownTagHelper.cs b/src/Util.Ui.NgZorro/Components/Statistics/CountdownTagHelper.cs new file mode 100644 index 000000000..458554ba7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Statistics/CountdownTagHelper.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Statistics.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Statistics; + +/// +/// 倒计时,生成的标签为<nz-countdown></nz-countdown> +/// +[HtmlTargetElement( "util-countdown" )] +public class CountdownTagHelper : StatisticTagHelper { + /// + /// nzFormat,格式化字符串,默认值: 'HH:mm:ss',格式说明:年(Y) 月(M) 日(D) 时(H) 分(m) 秒(s) 毫秒(S) + /// + public string Format { get; set; } + /// + /// [nzFormat],格式化字符串,默认值: 'HH:mm:ss',格式说明:年(Y) 月(M) 日(D) 时(H) 分(m) 秒(s) 毫秒(S) + /// + public string BindFormat { get; set; } + /// + /// (nzCountdownFinish),倒计时完成事件, 类型: EventEmitter<void> + /// + public string OnCountdownFinish { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new CountdownRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Statistics/Renders/CountdownRender.cs b/src/Util.Ui.NgZorro/Components/Statistics/Renders/CountdownRender.cs new file mode 100644 index 000000000..668622449 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Statistics/Renders/CountdownRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Statistics.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Statistics.Renders; + +/// +/// 倒计时渲染器 +/// +public class CountdownRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化倒计时渲染器 + /// + /// 配置 + public CountdownRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new CountdownBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new CountdownRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Statistics/Renders/StatisticRender.cs b/src/Util.Ui.NgZorro/Components/Statistics/Renders/StatisticRender.cs new file mode 100644 index 000000000..4405d47db --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Statistics/Renders/StatisticRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Statistics.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Statistics.Renders; + +/// +/// 统计渲染器 +/// +public class StatisticRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化统计渲染器 + /// + /// 配置 + public StatisticRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new StatisticBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new StatisticRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Statistics/StatisticTagHelper.cs b/src/Util.Ui.NgZorro/Components/Statistics/StatisticTagHelper.cs new file mode 100644 index 000000000..35d1226f2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Statistics/StatisticTagHelper.cs @@ -0,0 +1,59 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Statistics.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Statistics; + +/// +/// 统计,生成的标签为<nz-statistic></nz-statistic> +/// +[HtmlTargetElement( "util-statistic" )] +public class StatisticTagHelper : AngularTagHelperBase { + /// + /// nzTitle,数值标题,支持多语言 + /// + public string Title { get; set; } + /// + /// [nzTitle],数值标题,类型: string | TemplateRef<void> + /// + public string BindTitle { get; set; } + /// + /// nzValue,数值,类型: string | number + /// + public string Value { get; set; } + /// + /// [nzValue],数值,类型: string | number + /// + public string BindValue { get; set; } + /// + /// nzPrefix,数值前缀,类型: string | TemplateRef<void> + /// + public string Prefix { get; set; } + /// + /// [nzPrefix],数值前缀,类型: string | TemplateRef<void> + /// + public string BindPrefix { get; set; } + /// + /// nzSuffix,数值后缀,类型: string | TemplateRef<void> + /// + public string Suffix { get; set; } + /// + /// [nzSuffix],数值后缀,类型: string | TemplateRef<void> + /// + public string BindSuffix { get; set; } + /// + /// [nzValueStyle],数值样式,类型: Object, 范例: { color: '#3F8600' } + /// + public string ValueStyle { get; set; } + /// + /// [nzValueTemplate],数值展示自定义模板,类型: TemplateRef<{ $implicit: string | number }> + /// + public string ValueTemplate { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new StatisticRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Steps/Builders/StepBuilder.cs b/src/Util.Ui.NgZorro/Components/Steps/Builders/StepBuilder.cs new file mode 100644 index 000000000..4cce05df2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Steps/Builders/StepBuilder.cs @@ -0,0 +1,118 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Steps.Builders; + +/// +/// 步骤标签生成器 +/// +public class StepBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化步骤标签生成器 + /// + /// 配置 + public StepBuilder( Config config ) : base( config,"nz-step" ) { + _config = config; + } + + /// + /// 配置标题 + /// + public StepBuilder Title() { + SetTitle( _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 设置标题 + /// + private void SetTitle( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzTitle]", value ); + return; + } + AttributeIfNotEmpty( "nzTitle", value ); + } + + /// + /// 配置子标题 + /// + public StepBuilder Subtitle() { + SetSubtitle( _config.GetValue( UiConst.Subtitle ) ); + AttributeIfNotEmpty( "[nzSubtitle]", _config.GetValue( AngularConst.BindSubtitle ) ); + return this; + } + + /// + /// 设置子标题 + /// + private void SetSubtitle( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzSubtitle]", value ); + return; + } + AttributeIfNotEmpty( "nzSubtitle", value ); + } + + /// + /// 配置描述 + /// + public StepBuilder Description() { + AttributeIfNotEmpty( "nzDescription", _config.GetValue( UiConst.Description ) ); + AttributeIfNotEmpty( "[nzDescription]", _config.GetValue( AngularConst.BindDescription ) ); + return this; + } + + /// + /// 配置图标 + /// + public StepBuilder Icon() { + AttributeIfNotEmpty( "nzIcon", _config.GetValue( UiConst.Icon )?.Description() ); + AttributeIfNotEmpty( "[nzIcon]", _config.GetValue( AngularConst.BindIcon ) ); + return this; + } + + /// + /// 配置状态 + /// + public StepBuilder Status() { + AttributeIfNotEmpty( "nzStatus", _config.GetValue( UiConst.Status )?.Description() ); + AttributeIfNotEmpty( "[nzStatus]", _config.GetValue( AngularConst.BindStatus ) ); + return this; + } + + /// + /// 配置禁用 + /// + public StepBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置进度百分比 + /// + public StepBuilder Percentage() { + AttributeIfNotEmpty( "[nzPercentage]", _config.GetValue( UiConst.Percentage ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Title().Subtitle().Description().Icon().Status().Disabled().Percentage(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Steps/Builders/StepsBuilder.cs b/src/Util.Ui.NgZorro/Components/Steps/Builders/StepsBuilder.cs new file mode 100644 index 000000000..4a873ebfe --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Steps/Builders/StepsBuilder.cs @@ -0,0 +1,108 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Steps.Builders; + +/// +/// 步骤条标签生成器 +/// +public class StepsBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化步骤条标签生成器 + /// + /// 配置 + public StepsBuilder( Config config ) : base( config,"nz-steps" ) { + _config = config; + } + + /// + /// 配置类型 + /// + public StepsBuilder Type() { + AttributeIfNotEmpty( "nzType", _config.GetValue( UiConst.Type )?.Description() ); + AttributeIfNotEmpty( "[nzType]", _config.GetValue( AngularConst.BindType ) ); + return this; + } + + /// + /// 配置当前步骤 + /// + public StepsBuilder Current() { + AttributeIfNotEmpty( "[nzCurrent]", _config.GetValue( UiConst.Current ) ); + return this; + } + + /// + /// 配置尺寸 + /// + public StepsBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置起始序号 + /// + public StepsBuilder StartIndex() { + AttributeIfNotEmpty( "[nzStartIndex]", _config.GetValue( UiConst.StartIndex ) ); + return this; + } + + /// + /// 配置方向 + /// + public StepsBuilder Direction() { + AttributeIfNotEmpty( "nzDirection", _config.GetValue( UiConst.Direction )?.Description() ); + AttributeIfNotEmpty( "[nzDirection]", _config.GetValue( AngularConst.BindDirection ) ); + return this; + } + + /// + /// 配置状态 + /// + public StepsBuilder Status() { + AttributeIfNotEmpty( "nzStatus", _config.GetValue( UiConst.Status )?.Description() ); + AttributeIfNotEmpty( "[nzStatus]", _config.GetValue( AngularConst.BindStatus ) ); + return this; + } + + /// + /// 配置点状 + /// + public StepsBuilder ProgressDot() { + AttributeIfNotEmpty( "[nzProgressDot]", _config.GetValue( UiConst.ProgressDot ) ); + return this; + } + + /// + /// 配置标签位置 + /// + public StepsBuilder LabelPlacement() { + AttributeIfNotEmpty( "nzLabelPlacement", _config.GetValue( UiConst.LabelPlacement )?.Description() ); + AttributeIfNotEmpty( "[nzLabelPlacement]", _config.GetValue( AngularConst.BindLabelPlacement ) ); + return this; + } + + /// + /// 配置事件 + /// + public StepsBuilder Events() { + AttributeIfNotEmpty( "(nzIndexChange)", _config.GetValue( UiConst.OnIndexChange ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Type().Current().Size().StartIndex().Direction().Status().ProgressDot().LabelPlacement().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Steps/Renders/StepRender.cs b/src/Util.Ui.NgZorro/Components/Steps/Renders/StepRender.cs new file mode 100644 index 000000000..dc1143ffb --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Steps/Renders/StepRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Steps.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Steps.Renders; + +/// +/// 步骤渲染器 +/// +public class StepRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化步骤渲染器 + /// + /// 配置 + public StepRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new StepBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new StepRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Steps/Renders/StepsRender.cs b/src/Util.Ui.NgZorro/Components/Steps/Renders/StepsRender.cs new file mode 100644 index 000000000..0d2e0c927 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Steps/Renders/StepsRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Steps.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Steps.Renders; + +/// +/// 步骤条渲染器 +/// +public class StepsRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化步骤条渲染器 + /// + /// 配置 + public StepsRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new StepsBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new StepsRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Steps/StepTagHelper.cs b/src/Util.Ui.NgZorro/Components/Steps/StepTagHelper.cs new file mode 100644 index 000000000..d85eff168 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Steps/StepTagHelper.cs @@ -0,0 +1,68 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Steps.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Steps; + +/// +/// 步骤,生成的标签为<nz-step></nz-step>,放在<util-steps></util-steps>中使用 +/// +[HtmlTargetElement( "util-step" )] +public class StepTagHelper : AngularTagHelperBase { + /// + /// nzTitle,标题,支持多语言 + /// + public string Title { get; set; } + /// + /// [nzTitle],标题,类型: string | TemplateRef<void> + /// + public string BindTitle { get; set; } + /// + /// nzSubtitle,子标题,支持多语言 + /// + public string Subtitle { get; set; } + /// + /// [nzSubtitle],子标题,类型: string | TemplateRef<void> + /// + public string BindSubtitle { get; set; } + /// + /// nzDescription,描述 + /// + public string Description { get; set; } + /// + /// [nzDescription],描述,类型: string | TemplateRef<void> + /// + public string BindDescription { get; set; } + /// + /// nzIcon,图标 + /// + public AntDesignIcon Icon { get; set; } + /// + /// [nzIcon],图标,类型: string | string[] | Set<string> | { [klass: string]: any; } | TemplateRef<void> + /// + public string BindIcon { get; set; } + /// + /// nzStatus,状态,当不配置该属性时,使用 <util-steps> 的 current 指定状态,可选值: 'wait' | 'process' | 'finish' | 'error',默认值: 'wait' + /// + public StepStatus Status { get; set; } + /// + /// [nzStatus],状态,当不配置该属性时,使用 <util-steps> 的 current 指定状态,可选值: 'wait' | 'process' | 'finish' | 'error',默认值: 'wait' + /// + public string BindStatus { get; set; } + /// + /// [nzDisabled],是否禁用点击, 默认值: false + /// + public string Disabled { get; set; } + /// + /// [nzPercentage],当前状态为 `process` 的步骤所显示的进度百分比, 类型: number + /// + public string Percentage { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new StepRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Steps/StepsTagHelper.cs b/src/Util.Ui.NgZorro/Components/Steps/StepsTagHelper.cs new file mode 100644 index 000000000..656e97571 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Steps/StepsTagHelper.cs @@ -0,0 +1,76 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Steps.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Steps; + +/// +/// 步骤条,生成的标签为<nz-steps></nz-steps> +/// +[HtmlTargetElement( "util-steps" )] +public class StepsTagHelper : AngularTagHelperBase { + /// + /// nzType,步骤条类型,可选值: 'default' | 'navigation', 默认值: 'default' + /// + public StepsType Type { get; set; } + /// + /// [nzType],步骤条类型,可选值: 'default' | 'navigation', 默认值: 'default' + /// + public string BindType { get; set; } + /// + /// [nzCurrent],指定当前步骤,从 `0` 开始记数。在 <util-step> 中,可通过 status 覆盖状态,类型: number, 默认值: 0 + /// + public string Current { get; set; } + /// + /// nzSize,步骤条尺寸,可选值: 'default' | 'small', 默认值: 'default' + /// + public StepsSize Size { get; set; } + /// + /// [nzSize],步骤条尺寸,可选值: 'default' | 'small', 默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// [nzStartIndex],起始位置的序号,类型: number, 默认值: 0 + /// + public string StartIndex { get; set; } + /// + /// nzDirection,步骤条方向,可选值: 'vertical' | 'horizontal' ,默认值: 'horizontal' + /// + public StepsDirection Direction { get; set; } + /// + /// [nzDirection],步骤条方向,可选值: 'vertical' | 'horizontal' ,默认值: 'horizontal' + /// + public string BindDirection { get; set; } + /// + /// nzStatus,当前步骤的状态,可选值: 'wait' | 'process' | 'finish' | 'error',默认值: 'process' + /// + public StepStatus Status { get; set; } + /// + /// [nzStatus],当前步骤的状态,可选值: 'wait' | 'process' | 'finish' | 'error',默认值: 'process' + /// + public string BindStatus { get; set; } + /// + /// [nzProgressDot],点状步骤条, 类型: boolean | TemplateRef<{ $implicit: TemplateRef<void>, status: string, index: number }>, 默认值: false + /// + public string ProgressDot { get; set; } + /// + /// nzLabelPlacement,标签位置,可选值: 'horizontal' | 'vertical', 默认值: 'horizontal' + /// + public StepsLabelPlacement LabelPlacement { get; set; } + /// + /// [nzLabelPlacement],标签位置,可选值: 'horizontal' | 'vertical', 默认值: 'horizontal' + /// + public string BindLabelPlacement { get; set; } + /// + /// (nzIndexChange),步骤索引变化事件,点击步骤时触发,类型: EventEmitter<number> + /// + public string OnIndexChange { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new StepsRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Switches/Builders/SwitchBuilder.cs b/src/Util.Ui.NgZorro/Components/Switches/Builders/SwitchBuilder.cs new file mode 100644 index 000000000..dc4c2f128 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Switches/Builders/SwitchBuilder.cs @@ -0,0 +1,92 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Switches.Builders; + +/// +/// 开关标签生成器 +/// +public class SwitchBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化开关标签生成器 + /// + /// 配置 + public SwitchBuilder( Config config ) : base( config, "nz-switch" ) { + _config = config; + } + + /// + /// 配置禁用 + /// + public SwitchBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置选中显示标签 + /// + public SwitchBuilder CheckedChildren() { + AttributeIfNotEmpty( "nzCheckedChildren", _config.GetValue( UiConst.CheckedChildren ) ); + AttributeIfNotEmpty( "[nzCheckedChildren]", _config.GetValue( AngularConst.BindCheckedChildren ) ); + return this; + } + + /// + /// 配置未选中显示标签 + /// + public SwitchBuilder UncheckedChildren() { + AttributeIfNotEmpty( "nzUnCheckedChildren", _config.GetValue( UiConst.UncheckedChildren ) ); + AttributeIfNotEmpty( "[nzUnCheckedChildren]", _config.GetValue( AngularConst.BindUncheckedChildren ) ); + return this; + } + + /// + /// 配置尺寸 + /// + public SwitchBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置加载状态 + /// + public SwitchBuilder Loading() { + AttributeIfNotEmpty( "[nzLoading]", _config.GetValue( UiConst.Loading ) ); + return this; + } + + /// + /// 配置用户控制状态 + /// + public SwitchBuilder Control() { + AttributeIfNotEmpty( "[nzControl]", _config.GetValue( UiConst.Control ) ); + return this; + } + + /// + /// 配置事件 + /// + public SwitchBuilder Events() { + this.OnClick( _config ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + ConfigForm().Name().Disabled().CheckedChildren().UncheckedChildren() + .Size().Loading().Control().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Switches/Renders/SwitchRender.cs b/src/Util.Ui.NgZorro/Components/Switches/Renders/SwitchRender.cs new file mode 100644 index 000000000..8d752b9d3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Switches/Renders/SwitchRender.cs @@ -0,0 +1,41 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Switches.Builders; + +namespace Util.Ui.NgZorro.Components.Switches.Renders; + +/// +/// 开关渲染器 +/// +public class SwitchRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化开关渲染器 + /// + /// 配置 + public SwitchRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new SwitchBuilder( _config ); + builder.Config(); + if( _config.Content.IsEmpty() == false ) + builder.SetContent( _config.Content ); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new SwitchRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Switches/SwitchTagHelper.cs b/src/Util.Ui.NgZorro/Components/Switches/SwitchTagHelper.cs new file mode 100644 index 000000000..ef6d6eb4a --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Switches/SwitchTagHelper.cs @@ -0,0 +1,72 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Checkboxes.Helpers; +using Util.Ui.NgZorro.Components.Switches.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Switches; + +/// +/// 开关,生成的标签为<nz-switch></nz-switch> +/// +[HtmlTargetElement( "util-switch" )] +public class SwitchTagHelper : FormControlTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// [nzDisabled],是否禁用 + /// + public string Disabled { get; set; } + /// + /// nzCheckedChildren,选中时显示的标签内容,类型: string | TemplateRef<void> + /// + public string CheckedChildren { get; set; } + /// + /// [nzCheckedChildren],选中时显示的标签内容,类型: string | TemplateRef<void> + /// + public string BindCheckedChildren { get; set; } + /// + /// nzUnCheckedChildren,未选中时显示的标签内容,类型: string | TemplateRef<void> + /// + public string UncheckedChildren { get; set; } + /// + /// [nzUnCheckedChildren],未选中时显示的标签内容,类型: string | TemplateRef<void> + /// + public string BindUncheckedChildren { get; set; } + /// + /// nzSize,可选值: 'small' | 'default',默认值: 'default' + /// + public SwitchSize Size { get; set; } + /// + /// [nzSize],可选值: 'small' | 'default',默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// [nzLoading],加载状态,类型: boolean + /// + public string Loading { get; set; } + /// + /// [nzControl],是否完全由用户控制状态,默认值: false + /// + public string Control { get; set; } + /// + /// (click),单击事件 + /// + public string OnClick { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new CheckboxService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new SwitchRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/EditContentLoader.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/EditContentLoader.cs new file mode 100644 index 000000000..eb112c7f2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/EditContentLoader.cs @@ -0,0 +1,45 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Builders.Contents; + +/// +/// 编辑模式非第一列内容加载器 +/// +public class EditContentLoader : ITableColumnContentLoader { + /// + public void Load( TableColumnBuilder builder, IHtmlContent displayContent ) { + if ( builder.GetConfig().Content.IsEmpty() ) + throw new InvalidOperationException( "编辑列内容未设置" ); + if ( builder.GetTableColumnShareConfig().IsAutoCreateControl == false ) { + builder.SetContent( builder.GetConfig().Content ); + return; + } + var displayBuilder = CreateDisplayBuilder( builder ); + displayBuilder.SetContent( displayContent ); + var controlBuilder = CreateControlBuilder( builder ); + controlBuilder.SetContent( builder.GetConfig().Content ); + } + + /// + /// 创建表格编辑列显示区域标签生成器 + /// + private TagBuilder CreateDisplayBuilder( TableColumnBuilder builder ) { + var result = new TableColumnDisplayBuilder( builder.GetConfig().CopyRemoveAttributes() ); + result.Config(); + builder.AppendContent( result ); + return result; + } + + /// + /// 创建表格编辑列控件区域标签生成器 + /// + private TableColumnControlBuilder CreateControlBuilder( TableColumnBuilder builder ) { + var result = new TableColumnControlBuilder( builder.GetConfig().CopyRemoveAttributes() ); + result.Config(); + builder.AppendContent( result ); + return result; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/EditFirstColumnContentLoader.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/EditFirstColumnContentLoader.cs new file mode 100644 index 000000000..506fa8d2f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/EditFirstColumnContentLoader.cs @@ -0,0 +1,28 @@ +namespace Util.Ui.NgZorro.Components.Tables.Builders.Contents; + +/// +/// 编辑模式第一列内容加载器 +/// +public class EditFirstColumnContentLoader : NoEditFirstColumnContentLoader { + /// + /// 初始化编辑模式第一列内容加载器 + /// + /// 表格列选择框创建服务 + public EditFirstColumnContentLoader( ISelectCreateService service ) : base( service ) { + } + + /// + /// 获取显示内容 + /// + protected override IHtmlContent GetDisplayContent( TableColumnBuilder builder, IHtmlContent htmlContent ) { + return htmlContent; + } + + /// + /// 添加内容 + /// + protected override void AddContent( TableColumnBuilder builder, IHtmlContent content ) { + var loader = new EditContentLoader(); + loader.Load( builder, content ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/ISelectCreateService.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/ISelectCreateService.cs new file mode 100644 index 000000000..a2a96003c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/ISelectCreateService.cs @@ -0,0 +1,24 @@ +namespace Util.Ui.NgZorro.Components.Tables.Builders.Contents; + +/// +/// 表格列选择框创建服务 +/// +public interface ISelectCreateService { + /// + /// 创建表格列复选框 + /// + /// 表格单元格标签生成器 + /// 内容 + void CreateCheckbox( TableColumnBuilder builder, IHtmlContent content ); + /// + /// 创建表格列单选框 + /// + /// 表格单元格标签生成器 + /// 内容 + void CreateRadio( TableColumnBuilder builder, IHtmlContent content ); + /// + /// 是否将内容添加到选择框 + /// + /// 表格单元格标签生成器 + bool IsAddContentToSelect( TableColumnBuilder builder ); +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/ITableColumnContent.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/ITableColumnContent.cs new file mode 100644 index 000000000..c48e9137b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/ITableColumnContent.cs @@ -0,0 +1,12 @@ +namespace Util.Ui.NgZorro.Components.Tables.Builders.Contents; + +/// +/// 表格列内容 +/// +public interface ITableColumnContent { + /// + /// 获取显示内容 + /// + /// 表格单元格标签生成器 + IHtmlContent GetDisplayContent( TableColumnBuilder builder ); +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/ITableColumnContentLoader.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/ITableColumnContentLoader.cs new file mode 100644 index 000000000..2ecae1faa --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/ITableColumnContentLoader.cs @@ -0,0 +1,13 @@ +namespace Util.Ui.NgZorro.Components.Tables.Builders.Contents; + +/// +/// 表格列内容加载器 +/// +public interface ITableColumnContentLoader { + /// + /// 加载内容 + /// + /// 表格单元格标签生成器 + /// 显示内容 + void Load( TableColumnBuilder builder, IHtmlContent displayContent ); +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/NoEditContentLoader.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/NoEditContentLoader.cs new file mode 100644 index 000000000..db54b0638 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/NoEditContentLoader.cs @@ -0,0 +1,24 @@ +using Util.Ui.Extensions; + +namespace Util.Ui.NgZorro.Components.Tables.Builders.Contents; + +/// +/// 非编辑模式非第一列内容加载器 +/// +public class NoEditContentLoader : ITableColumnContentLoader { + /// + public void Load( TableColumnBuilder builder, IHtmlContent content ) { + content = GetContent( builder, content ); + builder.SetContent( content ); + } + + /// + /// 获取内容 + /// + private IHtmlContent GetContent( TableColumnBuilder builder, IHtmlContent htmlContent ) { + var content = builder.GetConfig().Content; + if ( content.IsEmpty() == false ) + return content; + return htmlContent; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/NoEditFirstColumnContentLoader.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/NoEditFirstColumnContentLoader.cs new file mode 100644 index 000000000..49beeec58 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/NoEditFirstColumnContentLoader.cs @@ -0,0 +1,84 @@ +using Util.Ui.Extensions; + +namespace Util.Ui.NgZorro.Components.Tables.Builders.Contents; + +/// +/// 非编辑模式第一列内容加载器 +/// +public class NoEditFirstColumnContentLoader : ITableColumnContentLoader { + /// + /// 表格列选择框创建服务 + /// + private readonly ISelectCreateService _selectCreateService; + + /// + /// 初始化非编辑模式第一列内容加载器 + /// + /// 表格列选择框创建服务 + public NoEditFirstColumnContentLoader( ISelectCreateService service ) { + _selectCreateService = service ?? throw new ArgumentNullException( nameof( service ) ); + } + + /// + public virtual void Load( TableColumnBuilder builder, IHtmlContent displayContent ) { + displayContent = GetDisplayContent( builder, displayContent ); + AddCheckbox( builder, displayContent ); + AddRadio( builder, displayContent ); + AddLineNumber( builder ); + if ( _selectCreateService.IsAddContentToSelect( builder ) ) + return; + AddContent( builder, displayContent ); + } + + /// + /// 获取显示内容 + /// + protected virtual IHtmlContent GetDisplayContent( TableColumnBuilder builder, IHtmlContent htmlContent ) { + var content = builder.GetConfig().Content; + if ( content.IsEmpty() == false ) + return content; + return htmlContent; + } + + /// + /// 添加复选框 + /// + protected virtual void AddCheckbox( TableColumnBuilder builder, IHtmlContent content ) { + if ( builder.GetTableColumnShareConfig().IsShowCheckbox == false ) + return; + _selectCreateService.CreateCheckbox( builder, content ); + } + + /// + /// 添加单选框 + /// + protected virtual void AddRadio( TableColumnBuilder builder, IHtmlContent content ) { + if ( builder.GetTableColumnShareConfig().IsShowCheckbox ) + return; + if ( builder.GetTableColumnShareConfig().IsShowRadio == false ) + return; + _selectCreateService.CreateRadio( builder, content ); + } + + /// + /// 添加序号 + /// + protected virtual void AddLineNumber( TableColumnBuilder builder ) { + if ( builder.GetTableColumnShareConfig().IsShowLineNumber == false ) + return; + var lineNumberBuilder = new TableColumnBuilder( builder.GetConfig(), builder.GetTableColumnShareConfig() ); + lineNumberBuilder.AddLineNumber(); + if ( builder.PreBuilder == null ) { + builder.PreBuilder = lineNumberBuilder; + return; + } + builder.PreBuilder.PostBuilder = lineNumberBuilder; + } + + /// + /// 添加内容 + /// + protected virtual void AddContent( TableColumnBuilder builder, IHtmlContent content ) { + builder.SetContent( content ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/TableColumnBoolContent.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/TableColumnBoolContent.cs new file mode 100644 index 000000000..e52c94a71 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/TableColumnBoolContent.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Util.Ui.NgZorro.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Builders.Contents; + +/// +/// 表格单元格布尔内容 +/// +public class TableColumnBoolContent : ITableColumnContent { + /// + public IHtmlContent GetDisplayContent( TableColumnBuilder builder ) { + var column = builder.GetConfig().GetValue( UiConst.Column ); + if ( column.IsEmpty() ) + return null; + var options = NgZorroOptionsService.GetOptions(); + var result = GetResult( options, column ); + return new StringHtmlContent( result ); + } + + /// + /// 获取结果 + /// + private string GetResult(NgZorroOptions options,string column) { + if ( options.GetTableColumnBoolContentAction != null ) + return options.GetTableColumnBoolContentAction( column ); + var result = new StringBuilder(); + result.AppendLine( $"" ); + result.AppendLine( $"" ); + return result.ToString(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/TableColumnContentService.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/TableColumnContentService.cs new file mode 100644 index 000000000..744dd0998 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/TableColumnContentService.cs @@ -0,0 +1,93 @@ +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Tables.Builders.Contents; + +/// +/// 表格单元格内容服务 +/// +public class TableColumnContentService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格列选择框创建服务 + /// + private readonly ISelectCreateService _service; + + /// + /// 初始化表格单元格内容 + /// + /// 表格单元格标签生成器 + /// 表格列选择框创建服务 + public TableColumnContentService( TableColumnBuilder builder, ISelectCreateService service ) { + Builder = builder ?? throw new ArgumentNullException( nameof( builder ) ); + _service = service ?? throw new ArgumentNullException( nameof( service ) ); + _config = builder.GetConfig(); + ShareConfig = builder.GetTableColumnShareConfig(); + } + + /// + /// 表格单元格标签生成器 + /// + protected TableColumnBuilder Builder { get; } + + /// + /// 表格列共享配置 + /// + protected TableColumnShareConfig ShareConfig { get; } + + /// + /// 配置内容 + /// + public void Config() { + var displayContent = GetDisplayContent(); + var loader = CreateContentLoader(); + loader.Load( Builder, displayContent ); + } + + /// + /// 获取显示内容 + /// + private IHtmlContent GetDisplayContent() { + var content = GetTableColumnContent(); + return content.GetDisplayContent( Builder ); + } + + /// + /// 获取表格单元格内容 + /// + private ITableColumnContent GetTableColumnContent() { + var type = _config.GetValue( UiConst.Type ); + switch ( type ) { + case TableColumnType.Bool: + return new TableColumnBoolContent(); + case TableColumnType.Date: + return new TableColumnDateContent(); + case TableColumnType.Enum: + return new TableColumnEnumContent(); + default: + return new TableColumnTextContent(); + } + } + + /// + /// 创建表格单元格内容加载器 + /// + private ITableColumnContentLoader CreateContentLoader() { + if ( IsEdit ) { + if ( ShareConfig.IsFirst ) + return new EditFirstColumnContentLoader( _service ); + return new EditContentLoader(); + } + if ( ShareConfig.IsFirst ) + return new NoEditFirstColumnContentLoader( _service ); + return new NoEditContentLoader(); + } + + /// + /// 是否编辑模式 + /// + private bool IsEdit => _config.GetValue( UiConst.IsEdit ); +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/TableColumnDateContent.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/TableColumnDateContent.cs new file mode 100644 index 000000000..54340fa67 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/TableColumnDateContent.cs @@ -0,0 +1,42 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; + +namespace Util.Ui.NgZorro.Components.Tables.Builders.Contents; + +/// +/// 表格单元格日期内容 +/// +public class TableColumnDateContent : ITableColumnContent { + /// + public IHtmlContent GetDisplayContent( TableColumnBuilder builder ) { + var config = builder.GetConfig(); + var column = config.GetValue( UiConst.Column ); + if( column.IsEmpty() ) + return null; + var result = GetResult( config,column ); + return new StringHtmlContent( result ); + } + + /// + /// 获取结果 + /// + private string GetResult( Config config, string column ) { + var result = new StringBuilder(); + result.Append( "{{" ); + result.Append( $"row.{column}|date:'{GetFormat( config )}'" ); + result.Append( "}}" ); + return result.ToString(); + } + + /// + /// 获取日期格式 + /// + private string GetFormat( Config config ) { + var format = config.GetValue( UiConst.DateFormat ); + if( format.IsEmpty() == false ) + return format; + var showDateOnly = config.GetValue( UiConst.ShowDateOnly ); + if( showDateOnly ) + return "yyyy-MM-dd"; + return "yyyy-MM-dd HH:mm"; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/TableColumnEnumContent.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/TableColumnEnumContent.cs new file mode 100644 index 000000000..fb4ba1dbf --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/TableColumnEnumContent.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; + +namespace Util.Ui.NgZorro.Components.Tables.Builders.Contents; + +/// +/// 表格单元格枚举内容 +/// +public class TableColumnEnumContent : ITableColumnContent { + /// + public IHtmlContent GetDisplayContent( TableColumnBuilder builder ) { + var column = builder.GetConfig().GetValue( UiConst.Column ); + var content = builder.GetConfig().GetValue( UiConst.EnumContent ); + return column.IsEmpty() ? null : new StringHtmlContent( content ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/TableColumnTextContent.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/TableColumnTextContent.cs new file mode 100644 index 000000000..18df3a8ff --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/TableColumnTextContent.cs @@ -0,0 +1,45 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Util.Ui.Angular.Extensions; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Buttons.Builders; + +namespace Util.Ui.NgZorro.Components.Tables.Builders.Contents; + +/// +/// 表格单元格文本内容 +/// +public class TableColumnTextContent : ITableColumnContent { + /// + public IHtmlContent GetDisplayContent( TableColumnBuilder builder ) { + var config = builder.GetConfig(); + var column = config.GetValue( UiConst.Column ); + if ( column.IsEmpty() ) + return null; + var columnBuilder = new StringHtmlContent( $"{{{{row.{column}}}}}" ); + var buttonBuilder = GetCopyToClipboardButton( config.Copy() ); + if ( buttonBuilder == null ) + return columnBuilder; + var containerBuilder = new EmptyContainerTagBuilder(); + containerBuilder.AppendContent( columnBuilder ); + containerBuilder.AppendContent( buttonBuilder ); + return containerBuilder; + } + + /// + /// 获取复制到剪贴板按钮 + /// + private IHtmlContent GetCopyToClipboardButton( Config config ) { + if ( config.GetValue( UiConst.Clipboard ) != true ) + return null; + var id = config.GetValue( UiConst.Id ); + var column = config.GetValue( UiConst.Column ); + var value = $"row.{column}"; + config.SetAttribute( UiConst.Id, $"btn_{id}_{column.Kebaberize().Replace( "-", "_" )}" ); + config.SetAttribute( UiConst.CopyToClipboard, value ); + var buttonBuilder = new ButtonBuilder( config ); + buttonBuilder.Attribute( "nz-button" ); + buttonBuilder.NgIf( value ); + buttonBuilder.Id( config ).CopyToClipboard().Type().Icon(); + return buttonBuilder; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/TableSelectCreateService.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/TableSelectCreateService.cs new file mode 100644 index 000000000..f5f0fc6fc --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/Contents/TableSelectCreateService.cs @@ -0,0 +1,28 @@ +namespace Util.Ui.NgZorro.Components.Tables.Builders.Contents; + +/// +/// 表格列选择框创建服务 +/// +public class TableSelectCreateService : ISelectCreateService { + /// + public void CreateCheckbox( TableColumnBuilder builder, IHtmlContent content ) { + var columnBuilder = new TableColumnBuilder( builder.GetConfig(), builder.GetTableColumnShareConfig() ); + columnBuilder.AddCheckbox(); + builder.PreBuilder = columnBuilder; + } + + /// + public void CreateRadio( TableColumnBuilder builder, IHtmlContent content ) { + var columnBuilder = new TableColumnBuilder( builder.GetConfig(), builder.GetTableColumnShareConfig() ); + columnBuilder.AddRadio(); + builder.PreBuilder = columnBuilder; + } + + /// + /// 是否将内容添加到选择框 + /// + /// 表格单元格标签生成器 + public bool IsAddContentToSelect( TableColumnBuilder builder ) { + return false; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/FilterTriggerBuilder.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/FilterTriggerBuilder.cs new file mode 100644 index 000000000..7bdef6cfe --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/FilterTriggerBuilder.cs @@ -0,0 +1,71 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Builders; + +/// +/// 表头单元格过滤器触发按钮标签生成器 +/// +public class FilterTriggerBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化表头单元格过滤器触发按钮标签生成器 + /// + /// 配置 + public FilterTriggerBuilder( Config config ) : base( config,"nz-filter-trigger" ) { + _config = config; + } + + /// + /// 配置下拉菜单 + /// + public FilterTriggerBuilder DropdownMenu() { + AttributeIfNotEmpty( "[nzDropdownMenu]", _config.GetValue( UiConst.DropdownMenu ) ); + return this; + } + + /// + /// 配置是否显示下拉菜单 + /// + public FilterTriggerBuilder Visible() { + AttributeIfNotEmpty( "[nzVisible]", _config.GetValue( UiConst.Visible ) ); + AttributeIfNotEmpty( "[(nzVisible)]", _config.GetValue( AngularConst.BindonVisible ) ); + return this; + } + + /// + /// 配置是否激活选中图标效果 + /// + public FilterTriggerBuilder Active() { + AttributeIfNotEmpty( "[nzActive]", _config.GetValue( UiConst.Active ) ); + return this; + } + + /// + /// 配置是否附带背景 + /// + public FilterTriggerBuilder HasBackdrop() { + AttributeIfNotEmpty( "[nzHasBackdrop]", _config.GetValue( UiConst.HasBackdrop ) ); + return this; + } + + /// + /// 配置事件 + /// + public FilterTriggerBuilder Events() { + AttributeIfNotEmpty( "(nzVisibleChange)", _config.GetValue( UiConst.OnVisibleChange ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + DropdownMenu().Visible().Active().HasBackdrop().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableBodyBuilder.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableBodyBuilder.cs new file mode 100644 index 000000000..574288217 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableBodyBuilder.cs @@ -0,0 +1,79 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Extensions; +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Components.Tables.Helpers; +using Util.Ui.NgZorro.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Builders; + +/// +/// 表格主体标签生成器 +/// +public class TableBodyBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格共享配置 + /// + private TableShareConfig _tableShareConfig; + + /// + /// 初始化表格主体标签生成器 + /// + /// 配置 + public TableBodyBuilder( Config config ) : base( config,"tbody" ) { + _config = config; + _tableShareConfig = GetTableShareConfig(); + } + + /// + /// 获取配置 + /// + public Config GetConfig() { + return _config; + } + + /// + /// 获取表格共享配置 + /// + public TableShareConfig GetTableShareConfig() { + return _tableShareConfig ??= _config.GetValueFromItems() ?? new TableShareConfig(); + } + + /// + /// 创建表格主体行标签生成器 + /// + public virtual TagBuilder CreateTableBodyRowBuilder() { + return new TableBodyRowBuilder( _config.CopyRemoveAttributes() ); + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + ConfigAutoCreate(); + ConfigContent(); + } + + /// + /// 配置自动创建嵌套结构 + /// + public void ConfigAutoCreate() { + var service = new TableBodyAutoCreateService( this ); + service.Init(); + } + + /// + /// 配置内容 + /// + public void ConfigContent() { + if ( _tableShareConfig.BodyRowAutoCreated ) + return; + _config.Content.AppendTo( this ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableBodyRowBuilder.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableBodyRowBuilder.cs new file mode 100644 index 000000000..73441ba5f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableBodyRowBuilder.cs @@ -0,0 +1,108 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Builders; + +/// +/// 表格主体行标签生成器 +/// +public class TableBodyRowBuilder : TableRowBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格共享配置 + /// + private TableShareConfig _shareConfig; + + /// + /// 初始化表格主体行标签生成器 + /// + public TableBodyRowBuilder( Config config ) : base( config ) { + _config = config; + _shareConfig = GetShareConfig(); + } + + /// + /// 获取表格共享配置 + /// + private TableShareConfig GetShareConfig() { + return _shareConfig ??= _config.GetValueFromItems() ?? new TableShareConfig(); + } + + /// + /// 行标识 + /// + protected string RowId => _shareConfig.RowId; + + /// + /// 表格编辑扩展标识 + /// + protected string EditId => _shareConfig.TableEditId; + + /// + /// 表格扩展标识 + /// + protected string TableExtendId => TableShareConfig.TableExtendId; + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + ConfigTableExtend(); + ConfigEdit(); + ConfigTableRowCheckedClass(); + ConfigContent(); + } + + /// + /// 配置表格主体行基础扩展属性 + /// + protected virtual void ConfigTableExtend() { + if ( TableShareConfig.IsEnableExtend == false ) + return; + if ( TableShareConfig.IsAutoCreateBodyRow == false ) + return; + ConfigFor(); + } + + /// + /// 配置行默认ngFor属性 + /// + private void ConfigFor() { + this.NgFor( $"let row of {TableExtendId}.dataSource;index as index" ); + } + + /// + /// 配置行编辑模式 + /// + protected void ConfigEdit() { + if ( TableShareConfig.IsEnableEdit == false ) + return; + Attribute( "[x-edit-row]", "row" ); + Attribute( $"#{RowId}", "xEditRow", true ); + Attribute( "(click)", $"{EditId}.clickEdit(row.id)", append: true ); + Attribute( "(dblclick)", $"{EditId}.dblClickEdit(row.id)", append: true ); + } + + /// + /// 配置勾选样式 + /// + protected void ConfigTableRowCheckedClass() { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableTableRowCheckedClass == false ) + return; + Attribute( "[class.table-row-checked]", $"{TableShareConfig.TableExtendId}.isChecked(row)" ); + } + + /// + /// 配置内容 + /// + protected void ConfigContent() { + _config.Content.AppendTo( this ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableBuilder.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableBuilder.cs new file mode 100644 index 000000000..0f70f92cf --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableBuilder.cs @@ -0,0 +1,663 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Components.Tables.Helpers; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Tables.Builders; + +/// +/// 表格标签生成器 +/// +public class TableBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格共享配置 + /// + private TableShareConfig _shareConfig; + + /// + /// 初始化表格标签生成器 + /// + /// 配置 + public TableBuilder( Config config ) : base( config, "nz-table" ) { + _config = config; + _shareConfig = GetShareConfig(); + } + + /// + /// 获取配置 + /// + public Config GetConfig() { + return _config; + } + + /// + /// 获取表格共享配置 + /// + public TableShareConfig GetShareConfig() { + return _shareConfig ??= _config.GetValueFromItems() ?? new TableShareConfig(); + } + + /// + /// 创建表头标签生成器 + /// + public virtual TableHeadBuilder CreateTableHeadBuilder() { + return new TableHeadBuilder( _config.CopyRemoveAttributes() ); + } + + /// + /// 创建表格主体标签生成器 + /// + public virtual TableBodyBuilder CreateTableBodyBuilder() { + return new TableBodyBuilder( _config.CopyRemoveAttributes() ); + } + + /// + /// 表格扩展标识 + /// + protected string ExtendId => _shareConfig.TableExtendId; + + /// + /// 表格编辑扩展标识 + /// + protected string EditId => _shareConfig.TableEditId; + + /// + /// 配置数据 + /// + public TableBuilder Data() { + if ( _shareConfig.IsEnableExtend == false ) + return Data( _config.GetValue( UiConst.Data ) ); + Data( $"{ExtendId}.dataSource" ); + AttributeIfNotEmpty( "[dataSource]", _config.GetValue( UiConst.Data ) ); + return this; + } + + /// + /// 配置数据 + /// + protected TableBuilder Data( string data ) { + AttributeIfNotEmpty( "[nzData]", data ); + return this; + } + + /// + /// 配置是否前端分页 + /// + public TableBuilder FrontPagination() { + FrontPagination( _config.GetValue( UiConst.FrontPagination ) ); + return this; + } + + /// + /// 配置是否前端分页 + /// + protected TableBuilder FrontPagination( string value ) { + AttributeIfNotEmpty( "[nzFrontPagination]", value ); + return this; + } + + /// + /// 配置数据总行数 + /// + public TableBuilder Total() { + Total( _config.GetValue( UiConst.Total ) ); + return this; + } + + /// + /// 配置数据总行数 + /// + protected TableBuilder Total( string value ) { + AttributeIfNotEmpty( "[nzTotal]", value ); + return this; + } + + /// + /// 配置当前页 + /// + public TableBuilder PageIndex() { + AttributeIfNotEmpty( "[nzPageIndex]", _config.GetValue( UiConst.PageIndex ) ); + BindonPageIndex( _config.GetValue( AngularConst.BindonPageIndex ) ); + return this; + } + + /// + /// 配置当前页 + /// + protected TableBuilder BindonPageIndex( string value ) { + AttributeIfNotEmpty( "[(nzPageIndex)]", value ); + return this; + } + + /// + /// 配置每页显示行数 + /// + public TableBuilder PageSize() { + AttributeIfNotEmpty( "[nzPageSize]", _config.GetValue( UiConst.PageSize ) ); + BindonPageSize( _config.GetValue( AngularConst.BindonPageSize ) ); + return this; + } + + /// + /// 配置每页显示行数 + /// + protected TableBuilder BindonPageSize( string value ) { + AttributeIfNotEmpty( "[(nzPageSize)]", value ); + return this; + } + + /// + /// 配置是否显示分页 + /// + public TableBuilder ShowPagination() { + ShowPagination( _config.GetValue( UiConst.ShowPagination ) ); + return this; + } + + /// + /// 配置是否显示分页 + /// + public TableBuilder ShowPagination( string value ) { + AttributeIfNotEmpty( "[nzShowPagination]", value ); + return this; + } + + /// + /// 配置分页位置 + /// + public TableBuilder PaginationPosition() { + AttributeIfNotEmpty( "nzPaginationPosition", _config.GetValue( UiConst.PaginationPosition )?.Description() ); + AttributeIfNotEmpty( "[nzPaginationPosition]", _config.GetValue( AngularConst.BindPaginationPosition ) ); + return this; + } + + /// + /// 配置分页类型 + /// + public TableBuilder PaginationType() { + AttributeIfNotEmpty( "nzPaginationType", _config.GetValue( UiConst.PaginationType )?.Description() ); + AttributeIfNotEmpty( "[nzPaginationType]", _config.GetValue( AngularConst.BindPaginationType ) ); + return this; + } + + /// + /// 配置是否显示边框 + /// + public TableBuilder Bordered() { + if ( _shareConfig.IsEnableTableSettings ) + return this; + AttributeIfNotEmpty( "[nzBordered]", _config.GetValue( UiConst.Bordered ) ); + return this; + } + + /// + /// 配置是否显示外边框 + /// + public TableBuilder OuterBordered() { + AttributeIfNotEmpty( "[nzOuterBordered]", _config.GetValue( UiConst.OuterBordered ) ); + return this; + } + + /// + /// 配置列宽度配置 + /// + public TableBuilder WidthConfig() { + AttributeIfNotEmpty( "[nzWidthConfig]", _config.GetValue( UiConst.WidthConfig ) ); + return this; + } + + /// + /// 配置表格尺寸 + /// + public TableBuilder Size() { + if ( _shareConfig.IsEnableTableSettings ) + return this; + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置是否加载状态 + /// + public TableBuilder Loading() { + return Loading( _config.GetValue( UiConst.Loading ) ); + } + + /// + /// 配置是否加载状态 + /// + public TableBuilder Loading( string value ) { + AttributeIfNotEmpty( "[nzLoading]", value ); + return this; + } + + /// + /// 配置加载指示符 + /// + public TableBuilder LoadingIndicator() { + AttributeIfNotEmpty( "[nzLoadingIndicator]", _config.GetValue( UiConst.LoadingIndicator ) ); + return this; + } + + /// + /// 配置延迟显示加载时间 + /// + public TableBuilder LoadingDelay() { + AttributeIfNotEmpty( "[nzLoadingDelay]", _config.GetValue( UiConst.LoadingDelay ) ); + return this; + } + + /// + /// 配置滚动 + /// + public TableBuilder Scroll() { + if ( _shareConfig.IsEnableTableSettings ) { + Scroll( $"{_shareConfig.TableSettingsId}.scroll" ); + return this; + } + Scroll( _config.GetValue( UiConst.Scroll ) ); + var scroll = new ScrollInfo( _config.GetValue( UiConst.ScrollWidth ), _config.GetValue( UiConst.ScrollHeight ) ); + if ( scroll.IsNull == false ) + Scroll( Util.Helpers.Json.ToJson( scroll, new JsonOptions { IgnoreNullValues = true, ToSingleQuotes = true } ) ); + return this; + } + + /// + /// 配置滚动 + /// + public TableBuilder Scroll( string value ) { + AttributeIfNotEmpty( "[nzScroll]", value ); + return this; + } + + /// + /// 配置标题 + /// + public TableBuilder Title() { + AttributeIfNotEmpty( "nzTitle", _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 配置底部 + /// + public TableBuilder Footer() { + AttributeIfNotEmpty( "nzFooter", _config.GetValue( UiConst.Footer ) ); + AttributeIfNotEmpty( "[nzFooter]", _config.GetValue( AngularConst.BindFooter ) ); + return this; + } + + /// + /// 配置无数据显示内容 + /// + public TableBuilder NoResult() { + AttributeIfNotEmpty( "nzNoResult", _config.GetValue( UiConst.NoResult ) ); + AttributeIfNotEmpty( "[nzNoResult]", _config.GetValue( AngularConst.BindNoResult ) ); + return this; + } + + /// + /// 配置每页行数选择列表 + /// + public TableBuilder PageSizeOptions() { + var options = _config.GetValue( UiConst.PageSizeOptions ); + if ( options.IsEmpty() ) + return this; + Attribute( "[nzPageSizeOptions]", $"{GetShareConfig().TableExtendId}.pageSizeOptions" ); + Attribute( "[pageSizeOptions]", options ); + return this; + } + + /// + /// 配置是否显示快速跳转 + /// + public TableBuilder ShowQuickJumper() { + ShowQuickJumper( _config.GetValue( UiConst.ShowQuickJumper ) ); + return this; + } + + /// + /// 配置是否显示快速跳转 + /// + public TableBuilder ShowQuickJumper( string value ) { + AttributeIfNotEmpty( "[nzShowQuickJumper]", value ); + return this; + } + + /// + /// 配置是否显示改变分页大小按钮 + /// + public TableBuilder ShowSizeChanger() { + ShowSizeChanger( _config.GetValue( UiConst.ShowSizeChanger ) ); + return this; + } + + /// + /// 配置是否显示改变分页大小按钮 + /// + protected TableBuilder ShowSizeChanger( string value ) { + AttributeIfNotEmpty( "[nzShowSizeChanger]", value ); + return this; + } + + /// + /// 配置显示总行数和当前数据范围的模板 + /// + public TableBuilder ShowTotal() { + return ShowTotal( _config.GetValue( UiConst.ShowTotal ) ); + } + + /// + /// 配置显示总行数和当前数据范围的模板 + /// + protected TableBuilder ShowTotal( string value ) { + AttributeIfNotEmpty( "[nzShowTotal]", value ); + return this; + } + + /// + /// 配置自定义页码结构 + /// + public TableBuilder ItemRender() { + AttributeIfNotEmpty( "[nzItemRender]", _config.GetValue( UiConst.ItemRender ) ); + return this; + } + + /// + /// 配置只有一页时是否隐藏分页器 + /// + public TableBuilder HideOnSinglePage() { + AttributeIfNotEmpty( "[nzHideOnSinglePage]", _config.GetValue( UiConst.HideOnSinglePage ) ); + return this; + } + + /// + /// 配置是否显示简单分页 + /// + public TableBuilder Simple() { + AttributeIfNotEmpty( "[nzSimple]", _config.GetValue( UiConst.Simple ) ); + return this; + } + + /// + /// 配置是否模板模式 + /// + public TableBuilder TemplateMode() { + AttributeIfNotEmpty( "[nzTemplateMode]", _config.GetValue( UiConst.TemplateMode ) ); + return this; + } + + /// + /// 配置虚拟滚动列高 + /// + public TableBuilder VirtualItemSize() { + AttributeIfNotEmpty( "[nzVirtualItemSize]", _config.GetValue( UiConst.VirtualItemSize ) ); + return this; + } + + /// + /// 配置虚拟滚动缓冲区最大高度 + /// + public TableBuilder VirtualMaxBufferPx() { + AttributeIfNotEmpty( "[nzVirtualMaxBufferPx]", _config.GetValue( UiConst.VirtualMaxBufferPx ) ); + return this; + } + + /// + /// 配置虚拟滚动缓冲区最小高度 + /// + public TableBuilder VirtualMinBufferPx() { + AttributeIfNotEmpty( "[nzVirtualMinBufferPx]", _config.GetValue( UiConst.VirtualMinBufferPx ) ); + return this; + } + + /// + /// 配置虚拟滚动跟踪函数 + /// + public TableBuilder VirtualForTrackBy() { + AttributeIfNotEmpty( "[nzVirtualForTrackBy]", _config.GetValue( UiConst.VirtualForTrackBy ) ); + return this; + } + + /// + /// 配置表格布局 + /// + public TableBuilder Layout() { + if ( _shareConfig.IsEnableEllipsis ) + _config.SetAttribute( UiConst.Layout, TableLayout.Fixed.Description() ); + AttributeIfNotEmpty( "nzTableLayout", _config.GetValue( UiConst.Layout )?.Description() ); + AttributeIfNotEmpty( "[nzTableLayout]", _config.GetValue( AngularConst.BindLayout ) ); + + return this; + } + + /// + /// 配置查询参数 + /// + public TableBuilder QueryParam() { + AttributeIfNotEmpty( "[(queryParam)]", _config.GetValue( UiConst.QueryParam ) ); + return this; + } + + /// + /// 配置Api地址 + /// + public TableBuilder Url() { + AttributeIfNotEmpty( "url", _config.GetValue( UiConst.Url ) ); + AttributeIfNotEmpty( "[url]", _config.GetValue( AngularConst.BindUrl ) ); + return this; + } + + /// + /// 配置删除地址 + /// + public TableBuilder DeleteUrl() { + AttributeIfNotEmpty( "deleteUrl", _config.GetValue( UiConst.DeleteUrl ) ); + AttributeIfNotEmpty( "[deleteUrl]", _config.GetValue( AngularConst.BindDeleteUrl ) ); + return this; + } + + /// + /// 配置排序条件 + /// + public TableBuilder Sort() { + AttributeIfNotEmpty( "order", _config.GetValue( UiConst.Sort ) ); + AttributeIfNotEmpty( "[order]", _config.GetValue( AngularConst.BindSort ) ); + return this; + } + + /// + /// 配置自动加载 + /// + public TableBuilder AutoLoad() { + AttributeIfNotEmpty( "[autoLoad]", _config.GetBoolValue( UiConst.AutoLoad ) ); + return this; + } + + /// + /// 配置标识列表 + /// + public TableBuilder ConfigKeys() { + AttributeIfNotEmpty( "[checkedKeys]", _config.GetValue( UiConst.CheckedKeys ) ); + return this; + } + + /// + /// 配置自定义列 + /// + public TableBuilder CustomColumn() { + CustomColumn( _config.GetValue( UiConst.CustomColumn ) ); + return this; + } + + /// + /// 配置自定义列 + /// + public TableBuilder CustomColumn( string value ) { + AttributeIfNotEmpty( "[nzCustomColumn]", value ); + return this; + } + + /// + /// 配置启用自定义列 + /// + public TableBuilder EnableCustomColumn() { + if ( _shareConfig.IsEnableCustomColumn == false ) + return this; + CustomColumn( $"{_shareConfig.TableSettingsId}.columns" ); + return this; + } + + /// + /// 配置事件 + /// + public TableBuilder Events() { + return OnPageIndexChange( _config.GetValue( UiConst.OnPageIndexChange ) ) + .OnPageSizeChange( _config.GetValue( UiConst.OnPageSizeChange ) ) + .OnCurrentPageDataChange( _config.GetValue( UiConst.OnCurrentPageDataChange ) ) + .OnQueryParams( _config.GetValue( UiConst.OnQueryParams ) ) + .OnLoad( _config.GetValue( UiConst.OnLoad ) ); + } + + /// + /// 页索引变化事件 + /// + protected TableBuilder OnPageIndexChange( string value ) { + AttributeIfNotEmpty( "(nzPageIndexChange)", value ); + return this; + } + + /// + /// 页大小变化事件 + /// + protected TableBuilder OnPageSizeChange( string value ) { + AttributeIfNotEmpty( "(nzPageSizeChange)", value ); + return this; + } + + /// + /// 当前页数据变化事件 + /// + protected TableBuilder OnCurrentPageDataChange( string value ) { + AttributeIfNotEmpty( "(nzCurrentPageDataChange)", value ); + return this; + } + + /// + /// 查询参数变化事件 + /// + protected TableBuilder OnQueryParams( string value ) { + AttributeIfNotEmpty( "(nzQueryParams)", value ); + return this; + } + + /// + /// 数据加载完成事件 + /// + protected TableBuilder OnLoad( string value ) { + AttributeIfNotEmpty( "(onLoad)", value ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + Data().FrontPagination().Total().PageIndex().PageSize() + .ShowPagination().PaginationPosition().PaginationType() + .Bordered().OuterBordered().WidthConfig().Size() + .Loading().LoadingIndicator().LoadingDelay() + .Scroll().Title().Footer().NoResult().PageSizeOptions() + .ShowQuickJumper().ShowSizeChanger().ShowTotal() + .ItemRender().HideOnSinglePage().Simple().TemplateMode() + .VirtualItemSize().VirtualMaxBufferPx().VirtualMinBufferPx().VirtualForTrackBy() + .Layout() + .QueryParam().Url().DeleteUrl() + .Sort().AutoLoad().ConfigKeys() + .CustomColumn().EnableCustomColumn() + .Events(); + ConfigAutoCreate(); + if ( _shareConfig.IsEnableExtend ) + ConfigExtend(); + ConfigEdit(); + ConfigTableSettingsExtend(); + ConfigContent(); + } + + /// + /// 配置自动创建嵌套结构 + /// + private void ConfigAutoCreate() { + var service = new TableAutoCreateService( this ); + service.Init(); + } + + /// + /// 配置表格扩展指令及默认属性 + /// + protected virtual void ConfigExtend() { + Attribute( "x-table-extend" ); + Attribute( $"#{ExtendId}", "xTableExtend" ); + Attribute( "[nzPageSizeOptions]", $"{GetShareConfig().TableExtendId}.pageSizeOptions" ); + ConfigDefault(); + } + + /// + /// 配置默认属性 + /// + protected void ConfigDefault() { + Loading( $"{ExtendId}.loading" ); + ShowSizeChanger( "true" ); + ShowQuickJumper( "true" ); + FrontPagination( "false" ); + Total( $"{ExtendId}.total" ); + OnPageIndexChange( $"{ExtendId}.pageIndexChange($event)" ); + OnPageSizeChange( $"{ExtendId}.pageSizeChange($event)" ); + BindonPageIndex( $"{ExtendId}.queryParam.page" ); + BindonPageSize( $"{ExtendId}.queryParam.pageSize" ); + ShowTotal( _shareConfig.TotalTemplateId ); + } + + /// + /// 配置编辑模式 + /// + private void ConfigEdit() { + if ( _shareConfig.IsEnableEdit == false ) + return; + Attribute( "x-edit-table" ); + Attribute( $"#{EditId}", "xEditTable" ); + AttributeIfNotEmpty( "[isBatch]", _config.GetBoolValue( UiConst.IsBatchEdit ) ); + } + + /// + /// 配置表格设置扩展 + /// + private void ConfigTableSettingsExtend() { + if ( _shareConfig.IsEnableTableSettings == false ) + return; + Attribute( "[nzSize]", $"{_shareConfig.TableSettingsId}.size" ); + Attribute( "[nzBordered]", $"{_shareConfig.TableSettingsId}.bordered" ); + } + + /// + /// 配置内容 + /// + private void ConfigContent() { + if ( _shareConfig.BodyAutoCreated ) + return; + if ( _shareConfig.BodyRowAutoCreated ) + return; + _config.Content.AppendTo( this ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableColumnBuilder.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableColumnBuilder.cs new file mode 100644 index 000000000..c591b6aff --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableColumnBuilder.cs @@ -0,0 +1,375 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Tables.Builders.Contents; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Directives.Popconfirms; +using Util.Ui.NgZorro.Directives.Popover; +using Util.Ui.NgZorro.Directives.Tooltips; + +namespace Util.Ui.NgZorro.Components.Tables.Builders; + +/// +/// 表格单元格标签生成器 +/// +public class TableColumnBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格列共享配置 + /// + private readonly TableColumnShareConfig _shareConfig; + + /// + /// 初始化表格单元格标签生成器 + /// + public TableColumnBuilder( Config config, TableColumnShareConfig shareConfig ) : base( config, "td" ) { + _config = config; + _shareConfig = shareConfig; + } + + /// + /// 获取配置 + /// + public Config GetConfig() { + return _config; + } + + /// + /// 获取表格列共享配置 + /// + public TableColumnShareConfig GetTableColumnShareConfig() { + return _shareConfig; + } + + /// + /// 配置是否显示复选框 + /// + public TableColumnBuilder ShowCheckbox() { + AttributeIfNotEmpty( "[nzShowCheckbox]", _config.GetValue( UiConst.ShowCheckbox ) ); + return this; + } + + /// + /// 配置是否禁用复选框 + /// + public TableColumnBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置复选框是否中间状态 + /// + public TableColumnBuilder Indeterminate() { + AttributeIfNotEmpty( "[nzIndeterminate]", _config.GetValue( UiConst.Indeterminate ) ); + return this; + } + + /// + /// 配置是否选中复选框 + /// + public TableColumnBuilder Checked() { + AttributeIfNotEmpty( "[nzChecked]", _config.GetValue( UiConst.Checked ) ); + AttributeIfNotEmpty( "[(nzChecked)]", _config.GetValue( AngularConst.BindonChecked ) ); + return this; + } + + /// + /// 配置是否显示展开按钮 + /// + public TableColumnBuilder ShowExpand() { + AttributeIfNotEmpty( "[nzShowExpand]", _config.GetValue( UiConst.ShowExpand ) ); + return this; + } + + /// + /// 配置是否已展开 + /// + public TableColumnBuilder Expand() { + AttributeIfNotEmpty( "[nzExpand]", _config.GetValue( UiConst.Expand ) ); + AttributeIfNotEmpty( "[(nzExpand)]", _config.GetValue( AngularConst.BindonExpand ) ); + return this; + } + + /// + /// 配置左侧距离 + /// + public TableColumnBuilder Left() { + if ( _shareConfig.IsEnableFixedColumn ) { + BindLeft( $"{_shareConfig.TableSettingsId}.isLeft('{_shareConfig.Title}')" ); + return this; + } + Left( _shareConfig.IsLeft ); + BindLeft( _config.GetValue( AngularConst.BindLeft ) ); + return this; + } + + /// + /// 配置左侧距离 + /// + public TableColumnBuilder Left( string value, string title ) { + if ( _shareConfig.IsEnableFixedColumn ) { + BindLeft( $"{_shareConfig.TableSettingsId}.isLeft('{title}')" ); + return this; + } + Left( value ); + return this; + } + + /// + /// 配置左侧距离 + /// + public TableColumnBuilder Left( string value ) { + if ( value.IsEmpty() ) + return this; + if ( value.SafeString().ToLower() == "true" ) + return BindLeft( "true" ); + Attribute( "nzLeft", value ); + return this; + } + + /// + /// 配置左侧距离 + /// + public TableColumnBuilder BindLeft( string value ) { + AttributeIfNotEmpty( "[nzLeft]", value ); + return this; + } + + /// + /// 配置右侧距离 + /// + public TableColumnBuilder Right() { + if ( _shareConfig.IsEnableFixedColumn ) { + BindRight( $"{_shareConfig.TableSettingsId}.isRight('{_shareConfig.Title}')" ); + return this; + } + Right( _shareConfig.IsRight ); + BindRight( _config.GetValue( AngularConst.BindRight ) ); + return this; + } + + /// + /// 配置右侧距离 + /// + public TableColumnBuilder Right( string value, string title ) { + if ( _shareConfig.IsEnableFixedColumn ) { + BindRight( $"{_shareConfig.TableSettingsId}.isRight('{title}')" ); + return this; + } + Right( value ); + return this; + } + + /// + /// 配置右侧距离 + /// + public TableColumnBuilder Right( string value ) { + if ( value.IsEmpty() ) + return this; + if ( value.SafeString().ToLower() == "true" ) + return BindRight( "true" ); + Attribute( "nzRight", value ); + return this; + } + + /// + /// 配置右侧距离 + /// + public TableColumnBuilder BindRight( string value ) { + AttributeIfNotEmpty( "[nzRight]", value ); + return this; + } + + /// + /// 配置对齐方式 + /// + public virtual TableColumnBuilder Align() { + if ( _shareConfig.IsEnableTableSettings ) { + BindAlign( $"{_shareConfig.TableSettingsId}.getAlign('{_shareConfig.Title}')" ); + return this; + } + AttributeIfNotEmpty( "nzAlign", _shareConfig.Align ); + BindAlign( _config.GetValue( AngularConst.BindAlign ) ); + return this; + } + + /// + /// 配置对齐方式 + /// + public virtual TableColumnBuilder Align( string title ) { + if ( _shareConfig.IsEnableTableSettings ) + BindAlign( $"{_shareConfig.TableSettingsId}.getAlign('{title}')" ); + return this; + } + + /// + /// 配置对齐方式 + /// + public TableColumnBuilder BindAlign( string value ) { + AttributeIfNotEmpty( "[nzAlign]", value ); + return this; + } + + /// + /// 配置是否折行显示 + /// + public TableColumnBuilder BreakWord() { + AttributeIfNotEmpty( "[nzBreakWord]", _config.GetValue( UiConst.BreakWord ) ); + return this; + } + + /// + /// 配置自动省略 + /// + public TableColumnBuilder Ellipsis() { + if ( _shareConfig.IsEnableTableSettings ) { + BindEllipsis( $"{_shareConfig.TableSettingsId}.getEllipsis('{_shareConfig.Title}')" ); + return this; + } + BindEllipsis( _config.GetValue( UiConst.Ellipsis ) ); + return this; + } + + /// + /// 配置自动省略 + /// + public TableColumnBuilder BindEllipsis( string value ) { + AttributeIfNotEmpty( "[nzEllipsis]", value ); + return this; + } + + /// + /// 配置缩进宽度 + /// + public TableColumnBuilder IndentSize() { + AttributeIfNotEmpty( "[nzIndentSize]", _config.GetValue( UiConst.IndentSize ) ); + return this; + } + + /// + /// 配置单元格控件 + /// + public virtual TableColumnBuilder CellControl() { + CellControl( _config.GetValue( UiConst.CellControl ) ); + AttributeIfNotEmpty( "[nzCellControl]", _config.GetValue( AngularConst.BindCellControl ) ); + return this; + } + + /// + /// 配置单元格控件 + /// + /// 值 + public virtual TableColumnBuilder CellControl( string value ) { + AttributeIfNotEmpty( "nzCellControl", value ); + return this; + } + + /// + /// 配置启用自定义列 + /// + public TableColumnBuilder EnableCustomColumn() { + if ( _shareConfig.IsEnableCustomColumn == false ) + return this; + CellControl( _shareConfig.CellControl ); + return this; + } + + /// + /// 配置事件 + /// + public TableColumnBuilder Events() { + AttributeIfNotEmpty( "(nzCheckedChange)", _config.GetValue( UiConst.OnCheckedChange ) ); + AttributeIfNotEmpty( "(nzExpandChange)", _config.GetValue( UiConst.OnExpandChange ) ); + this.OnClick( _config ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + this.Tooltip( _config ).Popover( _config ).Popconfirm( _config ); + ShowCheckbox().Disabled().Indeterminate().Checked() + .ShowExpand().Expand() + .Left().Right().Align().BreakWord().Ellipsis() + .IndentSize().CellControl().EnableCustomColumn() + .Events(); + ConfigContent(); + } + + /// + /// 配置内容 + /// + protected virtual void ConfigContent() { + var service = new TableColumnContentService( this, new TableSelectCreateService() ); + service.Config(); + } + + /// + /// 添加复选框 + /// + public virtual void AddCheckbox() { + var title = "util.checkbox"; + Attribute( "[nzShowCheckbox]", "true" ); + Attribute( "(click)", "$event.stopPropagation()" ); + Attribute( "(nzCheckedChange)", $"{_shareConfig.TableExtendId}.toggle(row)" ); + Attribute( "[nzChecked]", $"{_shareConfig.TableExtendId}.isChecked(row)" ); + if ( _shareConfig.IsEnableCustomColumn ) + Attribute( "nzCellControl", title ); + Left( _shareConfig.IsCheckboxLeft, title ); + if ( _shareConfig.IsEnableFixedColumn ) + Right( _shareConfig.IsCheckboxLeft, title ); + Align( title ); + } + + /// + /// 添加复选框 + /// + /// 复选框生成器 + public virtual void AddCheckbox( TagBuilder checkBoxBuilder ) { + AppendContent( checkBoxBuilder ); + Left( _shareConfig.IsCheckboxLeft ); + } + + /// + /// 添加单选框 + /// + public virtual void AddRadio() { + var radioBuilder = new TableColumnRadioBuilder( _config, _shareConfig.TableExtendId ); + AddRadio( radioBuilder ); + } + + /// + /// 添加单选框 + /// + public virtual void AddRadio( TagBuilder radioBuilder ) { + var title = "util.radio"; + AppendContent( radioBuilder ); + if ( _shareConfig.IsEnableCustomColumn ) + Attribute( "nzCellControl", title ); + Left( _shareConfig.IsRadioLeft, title ); + if ( _shareConfig.IsEnableFixedColumn ) + Right( _shareConfig.IsRadioLeft, title ); + Align( title ); + } + + /// + /// 添加序号 + /// + public void AddLineNumber() { + var title = "util.lineNumber"; + AppendContent( "{{row.lineNumber}}" ); + if ( _shareConfig.IsEnableCustomColumn ) + Attribute( "nzCellControl", title ); + Left( _shareConfig.IsLineNumberLeft, title ); + if ( _shareConfig.IsEnableFixedColumn ) + Right( _shareConfig.IsLineNumberLeft, title ); + Align( title ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableColumnControlBuilder.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableColumnControlBuilder.cs new file mode 100644 index 000000000..21fa9f554 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableColumnControlBuilder.cs @@ -0,0 +1,45 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Extensions; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Builders; + +/// +/// 表格编辑列控件区域标签生成器 +/// +public class TableColumnControlBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格列共享配置 + /// + private readonly TableColumnShareConfig _shareConfig; + + /// + /// 初始化表格编辑列控件区域标签生成器 + /// + public TableColumnControlBuilder( Config config ) : base( config,"ng-template" ) { + _config = config; + _shareConfig = GetTableColumnShareConfig(); + } + + /// + /// 获取表格列共享配置 + /// + private TableColumnShareConfig GetTableColumnShareConfig() { + return _config.GetValueFromItems(); + } + + /// + protected override void ConfigId( Config config ) { + this.RawId( _config ); + if ( config.Contains( UiConst.Id ) ) { + this.Id( _config.GetValue( UiConst.Id ) ); + return; + } + this.Id( _shareConfig?.ControlTemplateId ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableColumnDisplayBuilder.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableColumnDisplayBuilder.cs new file mode 100644 index 000000000..38c7ac427 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableColumnDisplayBuilder.cs @@ -0,0 +1,45 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Tables.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Builders; + +/// +/// 表格编辑列显示区域标签生成器 +/// +public class TableColumnDisplayBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格列共享配置 + /// + private readonly TableColumnShareConfig _shareConfig; + + /// + /// 初始化表格编辑列显示区域标签生成器 + /// + /// 配置 + public TableColumnDisplayBuilder( Config config ) : base( config, "ng-container" ) { + _config = config; + _shareConfig = GetTableColumnShareConfig(); + } + + /// + /// 获取表格列共享配置 + /// + private TableColumnShareConfig GetTableColumnShareConfig() { + return _config.GetValueFromItems(); + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + if ( _shareConfig == null ) + return; + this.NgIf( $"{_shareConfig.TableEditId}.editId !== row.id;else {_shareConfig.ControlTemplateId}" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableColumnRadioBuilder.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableColumnRadioBuilder.cs new file mode 100644 index 000000000..877be2c37 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableColumnRadioBuilder.cs @@ -0,0 +1,25 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Tables.Builders; + +/// +/// 表格单元格单选框标签生成器 +/// +public class TableColumnRadioBuilder : AngularTagBuilder { + /// + /// 初始化表格单元格单选框标签生成器 + /// + /// 配置 + /// 表格扩展标识 + /// 内容 + public TableColumnRadioBuilder( Config config,string tableExtendId, IHtmlContent content = null ) : base( config, "label" ) { + base.Attribute( "nz-radio" ); + base.Attribute( "(click)", "$event.stopPropagation()" ); + base.Attribute( "name", $"r_{tableExtendId}" ); + base.Attribute( "[ngModel]", $"{tableExtendId}.isChecked(row)" ); + base.Attribute( "(ngModelChange)", $"{tableExtendId}.checkRowOnly(row)" ); + if ( content == null ) + return; + base.SetContent( content ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableHeadBuilder.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableHeadBuilder.cs new file mode 100644 index 000000000..dd3b95167 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableHeadBuilder.cs @@ -0,0 +1,78 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Extensions; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Components.Tables.Helpers; +using Util.Ui.NgZorro.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Builders; + +/// +/// 表头标签生成器 +/// +public class TableHeadBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格共享配置 + /// + private TableShareConfig _tableShareConfig; + + /// + /// 初始化表头标签生成器 + /// + /// 配置 + public TableHeadBuilder( Config config ) : base( config,"thead" ) { + _config = config; + _tableShareConfig = GetTableShareConfig(); + } + + /// + /// 获取配置 + /// + public Config GetConfig() { + return _config; + } + + /// + /// 获取表格共享配置 + /// + public TableShareConfig GetTableShareConfig() { + return _tableShareConfig ??= _config.GetValueFromItems() ?? new TableShareConfig(); + } + + /// + /// 创建表头行标签生成器 + /// + public virtual TableHeadRowBuilder CreateTableHeadRowBuilder() { + return new TableHeadRowBuilder( _config.CopyRemoveAttributes() ); + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + ConfigAutoCreate(); + ConfigContent(); + } + + /// + /// 配置自动创建嵌套结构 + /// + public void ConfigAutoCreate() { + var service = new TableHeadAutoCreateService( this ); + service.Init(); + } + + /// + /// 配置内容 + /// + public void ConfigContent() { + if ( _tableShareConfig.HeadRowAutoCreated ) + return; + _config.Content.AppendTo( this ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableHeadColumnBuilder.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableHeadColumnBuilder.cs new file mode 100644 index 000000000..6c5b22462 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableHeadColumnBuilder.cs @@ -0,0 +1,740 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Resizable.Builders; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Components.Tables.Helpers; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Tables.Builders; + +/// +/// 表头单元格标签生成器 +/// +public class TableHeadColumnBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表头列共享配置 + /// + private readonly TableHeadColumnShareConfig _shareConfig; + + /// + /// 初始化表头单元格标签生成器 + /// + /// 配置 + /// 表头列共享配置 + public TableHeadColumnBuilder( Config config, TableHeadColumnShareConfig shareConfig ) : base( config, "th" ) { + _config = config; + _shareConfig = shareConfig; + } + + /// + /// 配置是否显示复选框 + /// + public TableHeadColumnBuilder ShowCheckbox() { + AttributeIfNotEmpty( "[nzShowCheckbox]", _config.GetValue( UiConst.ShowCheckbox ) ); + return this; + } + + /// + /// 配置是否禁用复选框 + /// + public TableHeadColumnBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置复选框是否中间状态 + /// + public TableHeadColumnBuilder Indeterminate() { + AttributeIfNotEmpty( "[nzIndeterminate]", _config.GetValue( UiConst.Indeterminate ) ); + return this; + } + + /// + /// 配置是否选中复选框 + /// + public TableHeadColumnBuilder Checked() { + AttributeIfNotEmpty( "[nzChecked]", _config.GetValue( UiConst.Checked ) ); + AttributeIfNotEmpty( "[(nzChecked)]", _config.GetValue( AngularConst.BindonChecked ) ); + return this; + } + + /// + /// 配置是否显示下拉选择 + /// + public TableHeadColumnBuilder ShowRowSelection() { + AttributeIfNotEmpty( "[nzShowRowSelection]", _config.GetValue( UiConst.ShowRowSelection ) ); + return this; + } + + /// + /// 配置下拉选择的内容 + /// + public TableHeadColumnBuilder Selections() { + AttributeIfNotEmpty( "[nzSelections]", _config.GetValue( UiConst.Selections ) ); + return this; + } + + /// + /// 配置是否显示排序 + /// + public TableHeadColumnBuilder ShowSort() { + ShowSort( _config.GetValue( UiConst.ShowSort ) ); + return this; + } + + /// + /// 配置是否显示排序 + /// + public TableHeadColumnBuilder ShowSort( string value ) { + AttributeIfNotEmpty( "[nzShowSort]", value ); + return this; + } + + /// + /// 配置排序函数 + /// + public TableHeadColumnBuilder SortFn() { + SortFn( _config.GetValue( UiConst.SortFn ) ); + return this; + } + + /// + /// 配置排序函数 + /// + public TableHeadColumnBuilder SortFn( string value ) { + AttributeIfNotEmpty( "[nzSortFn]", value ); + return this; + } + + /// + /// 配置支持的排序方式 + /// + public TableHeadColumnBuilder SortDirections() { + AttributeIfNotEmpty( "[nzSortDirections]", _config.GetValue( UiConst.SortDirections ) ); + return this; + } + + /// + /// 配置排序方向 + /// + public TableHeadColumnBuilder SortOrder() { + AttributeIfNotEmpty( "nzSortOrder", _config.GetValue( UiConst.SortOrder ) ); + AttributeIfNotEmpty( "[nzSortOrder]", _config.GetValue( AngularConst.BindSortOrder ) ); + AttributeIfNotEmpty( "[(nzSortOrder)]", _config.GetValue( AngularConst.BindonSortOrder ) ); + return this; + } + + /// + /// 配置排序 + /// + public TableHeadColumnBuilder Sort() { + Sort( _config.GetValue( UiConst.Sort ) ); + return this; + } + + /// + /// 排序 + /// + protected void Sort( string order ) { + if ( order.IsEmpty() ) + return; + ShowSort( "true" ); + SortFn( "true" ); + OnSortOrderChange( $"{_shareConfig.TableExtendId}.sortChange('{order}',$event)" ); + } + + /// + /// 配置是否显示过滤 + /// + public TableHeadColumnBuilder ShowFilter() { + AttributeIfNotEmpty( "[nzShowFilter]", _config.GetValue( UiConst.ShowFilter ) ); + return this; + } + + /// + /// 配置过滤函数 + /// + public TableHeadColumnBuilder FilterFn() { + AttributeIfNotEmpty( "[nzFilterFn]", _config.GetValue( UiConst.FilterFn ) ); + return this; + } + + /// + /// 配置过滤器 + /// + public TableHeadColumnBuilder Filters() { + AttributeIfNotEmpty( "[nzFilters]", _config.GetValue( UiConst.Filters ) ); + return this; + } + + /// + /// 配置是否多选过滤器 + /// + public TableHeadColumnBuilder FilterMultiple() { + AttributeIfNotEmpty( "[nzFilterMultiple]", _config.GetValue( UiConst.FilterMultiple ) ); + return this; + } + + /// + /// 配置列宽 + /// + public TableHeadColumnBuilder Width() { + if ( _shareConfig.IsEnableCustomColumn ) + return this; + var width = _config.GetValue( UiConst.Width ); + if ( _shareConfig.IsEnableResizable ) { + var title = _shareConfig.Title; + Attribute( "[nzWidth]", GetWidthByTableSettings( title, width ) ); + return this; + } + this.Width( width ); + BindWidth( _config.GetValue( AngularConst.BindWidth ) ); + return this; + } + + /// + /// 配置列宽 + /// + public TableHeadColumnBuilder BindWidth( string value ) { + AttributeIfNotEmpty( "[nzWidth]", value ); + return this; + } + + /// + /// 配置左侧距离 + /// + public TableHeadColumnBuilder Left() { + if ( _shareConfig.IsEnableFixedColumn ) { + var title = _shareConfig.Title; + BindLeft( $"{_shareConfig.TableSettingsId}.isLeft('{title}')" ); + return this; + } + Left( _config.GetValue( UiConst.Left ) ); + BindLeft( _config.GetValue( AngularConst.BindLeft ) ); + return this; + } + + /// + /// 配置左侧距离 + /// + public TableHeadColumnBuilder Left( string value ) { + if ( value.IsEmpty() ) + return this; + if ( value.SafeString().ToLower() == "true" ) + return BindLeft( "true" ); + Attribute( "nzLeft", value ); + return this; + } + + /// + /// 配置左侧距离 + /// + public TableHeadColumnBuilder BindLeft( string value ) { + AttributeIfNotEmpty( "[nzLeft]", value ); + return this; + } + + /// + /// 配置左侧距离 + /// + public TableHeadColumnBuilder Left( string value, string title ) { + if ( _shareConfig.IsEnableFixedColumn ) { + BindLeft( $"{_shareConfig.TableSettingsId}.isLeft('{title}')" ); + return this; + } + Left( value ); + return this; + } + + /// + /// 配置右侧距离 + /// + public TableHeadColumnBuilder Right() { + if ( _shareConfig.IsEnableFixedColumn ) { + var title = _shareConfig.Title; + BindRight( $"{_shareConfig.TableSettingsId}.isRight('{title}')" ); + return this; + } + Right( _config.GetValue( UiConst.Right ) ); + BindRight( _config.GetValue( AngularConst.BindRight ) ); + return this; + } + + /// + /// 配置右侧距离 + /// + public TableHeadColumnBuilder Right( string value ) { + if ( value.IsEmpty() ) + return this; + if ( value.SafeString().ToLower() == "true" ) + return BindRight( "true" ); + Attribute( "nzRight", value ); + return this; + } + + /// + /// 配置右侧距离 + /// + public TableHeadColumnBuilder BindRight( string value ) { + AttributeIfNotEmpty( "[nzRight]", value ); + return this; + } + + /// + /// 配置右侧距离 + /// + public TableHeadColumnBuilder Right( string value, string title ) { + if ( _shareConfig.IsEnableFixedColumn ) { + BindRight( $"{_shareConfig.TableSettingsId}.isRight('{title}')" ); + return this; + } + Right( value ); + return this; + } + + /// + /// 配置对齐方式 + /// + public virtual TableHeadColumnBuilder Align() { + if ( _shareConfig.IsEnableTableSettings ) + return this; + AttributeIfNotEmpty( "nzAlign", _config.GetValue( UiConst.Align )?.Description() ); + BindAlign( _config.GetValue( AngularConst.BindAlign ) ); + return this; + } + + /// + /// 配置对齐方式 + /// + public TableHeadColumnBuilder BindAlign( string value ) { + AttributeIfNotEmpty( "[nzAlign]", value ); + return this; + } + + /// + /// 配置标题对齐方式 + /// + public virtual TableHeadColumnBuilder TitleAlign() { + if ( _shareConfig.IsEnableTableSettings ) { + var title = _shareConfig.Title; + BindTitleAlign( $"{_shareConfig.TableSettingsId}.getTitleAlign('{title}')" ); + return this; + } + AttributeIfNotEmpty( "titleAlign", _shareConfig.TitleAlign ); + BindTitleAlign( _config.GetValue( AngularConst.BindTitleAlign ) ); + return this; + } + + /// + /// 配置标题对齐方式 + /// + public virtual TableHeadColumnBuilder TitleAlign( string title ) { + if ( _shareConfig.IsEnableTableSettings ) + BindTitleAlign( $"{_shareConfig.TableSettingsId}.getTitleAlign('{title}')" ); + return this; + } + + /// + /// 配置标题对齐方式 + /// + public TableHeadColumnBuilder BindTitleAlign( string value ) { + AttributeIfNotEmpty( "[titleAlign]", value ); + return this; + } + + /// + /// 配置是否折行显示 + /// + public TableHeadColumnBuilder BreakWord() { + AttributeIfNotEmpty( "[nzBreakWord]", _config.GetValue( UiConst.BreakWord ) ); + return this; + } + + /// + /// 配置自动省略 + /// + public TableHeadColumnBuilder Ellipsis() { + if ( _shareConfig.IsEnableTableSettings ) + return this; + AttributeIfNotEmpty( "[nzEllipsis]", _config.GetValue( UiConst.Ellipsis ) ); + return this; + } + + /// + /// 配置列跨度 + /// + public TableHeadColumnBuilder Colspan() { + AttributeIfNotEmpty( "[colSpan]", _config.GetValue( UiConst.Colspan ) ); + return this; + } + + /// + /// 配置行跨度 + /// + public TableHeadColumnBuilder Rowspan() { + Rowspan( _config.GetValue( UiConst.Rowspan ) ); + return this; + } + + /// + /// 配置行跨度 + /// + public TableHeadColumnBuilder Rowspan( string value ) { + AttributeIfNotEmpty( "[rowSpan]", value ); + return this; + } + + /// + /// 配置列标识 + /// + public TableHeadColumnBuilder ColumnKey() { + AttributeIfNotEmpty( "nzColumnKey", _config.GetValue( UiConst.ColumnKey ) ); + AttributeIfNotEmpty( "[nzColumnKey]", _config.GetValue( AngularConst.BindColumnKey ) ); + return this; + } + + /// + /// 配置Operation标题 + /// + public TableHeadColumnBuilder TitleOperation() { + var value = _config.GetValue( UiConst.TitleOperation ); + if ( value != true ) + return this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Title, I18nKeys.Operation ); + return this; + } + _config.SetAttribute( UiConst.Title, "Operation" ); + return this; + } + + /// + /// 配置标题 + /// + public TableHeadColumnBuilder Title() { + Title( _config.GetValue( UiConst.Title ) ); + return this; + } + + /// + /// 配置标题 + /// + /// 标题 + public virtual void Title( string title ) { + var options = NgZorroOptionsService.GetOptions(); + if ( title.IsEmpty() ) + return; + if ( options.EnableI18n ) { + this.AppendContentByI18n( title ); + return; + } + AppendContent( title ); + } + + /// + /// 配置单元格控件 + /// + public virtual TableHeadColumnBuilder CellControl() { + CellControl( _config.GetValue( UiConst.CellControl ) ); + AttributeIfNotEmpty( "[nzCellControl]", _config.GetValue( AngularConst.BindCellControl ) ); + return this; + } + + /// + /// 配置单元格控件 + /// + /// 值 + public virtual TableHeadColumnBuilder CellControl( string value ) { + AttributeIfNotEmpty( "nzCellControl", value ); + return this; + } + + /// + /// 配置启用自定义列 + /// + public TableHeadColumnBuilder EnableCustomColumn() { + return EnableCustomColumn( _shareConfig.CellControl ); + } + + /// + /// 配置启用自定义列 + /// + public TableHeadColumnBuilder EnableCustomColumn( string cellControl ) { + if ( _shareConfig.IsEnableCustomColumn == false ) + return this; + CellControl( cellControl ); + return this; + } + + /// + /// 配置启用拖动调整列宽 + /// + public TableHeadColumnBuilder EnableResizable() { + if ( _shareConfig.IsEnableResizable == false ) + return this; + return Resizable( _shareConfig.Title ); + } + + /// + /// 配置拖动调整列宽 + /// + public virtual TableHeadColumnBuilder Resizable( string title ) { + Attribute( "nz-resizable" ); + Attribute( "nzPreview" ); + Attribute( "nzBounds", "window" ); + Attribute( "(nzResizeEnd)", $"{_shareConfig.TableSettingsId}.handleResize($event,'{title}')" ); + AppendHandleBuilder(); + return this; + } + + /// + /// 添加拖动手柄生成器 + /// + protected void AppendHandleBuilder() { + var handleBuilder = new ResizeHandleBuilder( _config ); + handleBuilder.Direction( ResizeDirection.Right ); + AppendContent( handleBuilder ); + } + + /// + /// 配置事件 + /// + public TableHeadColumnBuilder Events() { + AttributeIfNotEmpty( "(nzCheckedChange)", _config.GetValue( UiConst.OnCheckedChange ) ); + AttributeIfNotEmpty( "(nzFilterChange)", _config.GetValue( UiConst.OnFilterChange ) ); + OnSortOrderChange( _config.GetValue( UiConst.OnSortOrderChange ) ); + return this; + } + + /// + /// 排序事件 + /// + public TableHeadColumnBuilder OnSortOrderChange( string value ) { + AttributeIfNotEmpty( "(nzSortOrderChange)", value ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + ShowCheckbox().Disabled().Indeterminate().Checked() + .ShowRowSelection().Selections() + .ShowSort().SortFn().SortDirections().SortOrder().Sort() + .ShowFilter().FilterFn().Filters().FilterMultiple() + .Width().Left().Right().Align().BreakWord().Ellipsis() + .Colspan().Rowspan().ColumnKey() + .TitleOperation().Title().TitleAlign() + .CellControl().EnableCustomColumn().EnableResizable() + .Events() + .CheckBox().Radio().LineNumber(); + } + + /// + /// 添加复选框 + /// + public TableHeadColumnBuilder CheckBox() { + if ( _shareConfig.IsShowCheckbox == false ) + return this; + if ( _shareConfig.IsFirst == false ) + return this; + AddCheckBox(); + return this; + } + + /// + /// 添加复选框 + /// + protected virtual void AddCheckBox( string title = null ) { + var checkboxBuilder = new TableHeadColumnBuilder( _config, _shareConfig ); + checkboxBuilder.ConfigCheckBox(); + PreBuilder = checkboxBuilder; + } + + /// + /// 配置复选框 + /// + public void ConfigCheckBox() { + var title = "util.checkbox"; + Attribute( "[nzShowCheckbox]", "true" ); + Attribute( "(nzCheckedChange)", $"{_shareConfig.TableExtendId}.masterToggle()" ); + Attribute( "[nzChecked]", $"{_shareConfig.TableExtendId}.isMasterChecked()" ); + Attribute( "[nzDisabled]", $"!{_shareConfig.TableExtendId}.dataSource.length" ); + Attribute( "[nzIndeterminate]", $"{_shareConfig.TableExtendId}.isMasterIndeterminate()" ); + EnableCustomColumn( title ); + if ( _shareConfig.IsEnableCustomColumn == false ) + BindWidth( $"{_shareConfig.TableExtendId}.config.table.checkboxWidth" ); + Left( _shareConfig.IsCheckboxLeft, title ); + if ( _shareConfig.IsEnableFixedColumn ) + Right( _shareConfig.IsCheckboxLeft, title ); + TitleAlign( title ); + ConfigRowSpan(); + } + + /// + /// 配置行跨度 + /// + protected void ConfigRowSpan() { + if (_shareConfig.HeadRowNumber < 2) + return; + Rowspan(_shareConfig.HeadRowNumber.ToString()); + } + + /// + /// 添加单选框列头 + /// + public TableHeadColumnBuilder Radio() { + if ( _shareConfig.IsShowCheckbox ) + return this; + if ( _shareConfig.IsShowRadio == false ) + return this; + if ( _shareConfig.IsFirst == false ) + return this; + AddRadio(); + return this; + } + + /// + /// 添加单选框列头 + /// + protected virtual void AddRadio( string title = null ) { + var radioTitle = "util.radio"; + var radioBuilder = new TableHeadColumnBuilder( _config, _shareConfig ); + radioBuilder.EnableCustomColumn( radioTitle ); + radioBuilder.TitleAlign( radioTitle ); + radioBuilder.Left( _shareConfig.IsRadioLeft, radioTitle ); + if ( _shareConfig.IsEnableFixedColumn ) + radioBuilder.Right( _shareConfig.IsRadioLeft, radioTitle ); + if ( _shareConfig.IsEnableCustomColumn == false ) + radioBuilder.BindWidth( $"{_shareConfig.TableExtendId}.config.table.radioWidth" ); + radioBuilder.ConfigRowSpan(); + PreBuilder = radioBuilder; + } + + /// + /// 配置序号 + /// + public TableHeadColumnBuilder LineNumber() { + if ( _shareConfig.IsShowLineNumber == false ) + return this; + if ( _shareConfig.IsFirst == false ) + return this; + AddLineNumber(); + return this; + } + + /// + /// 添加序号 + /// + protected virtual void AddLineNumber() { + var title = "util.lineNumber"; + var lineNumberBuilder = new TableHeadColumnBuilder( _config, _shareConfig ); + lineNumberBuilder.ConfigLineNumber(); + lineNumberBuilder.EnableCustomColumn( title ); + lineNumberBuilder.TitleAlign( title ); + lineNumberBuilder.ConfigRowSpan(); + if ( PreBuilder == null ) { + PreBuilder = lineNumberBuilder; + return; + } + PreBuilder.PostBuilder = lineNumberBuilder; + } + + /// + /// 配置序号 + /// + public void ConfigLineNumber() { + var options = NgZorroOptionsService.GetOptions(); + Title( options.EnableI18n ? "util.lineNumber" : "序号" ); + if ( _shareConfig.IsEnableCustomColumn == false ) + BindWidth( $"{_shareConfig.TableExtendId}.config.table.lineNumberWidth" ); + Left( _shareConfig.IsLineNumberLeft, "util.lineNumber" ); + if ( _shareConfig.IsEnableFixedColumn ) + Right( _shareConfig.IsLineNumberLeft, "util.lineNumber" ); + } + + /// + /// 添加表头单元格 + /// + /// 列 + public virtual void AddColumn( ColumnInfo column ) { + if ( column.IsFirst ) { + if ( _shareConfig.IsShowCheckbox ) + AddCheckBox(); + if ( _shareConfig.IsShowCheckbox == false && _shareConfig.IsShowRadio ) + AddRadio(); + if ( _shareConfig.IsShowLineNumber ) + AddLineNumber(); + } + if ( column.IsSort ) + Sort( column.Column ); + Title( column.Title ); + SetColumnWidth( column ); + Left( column.IsLeft, column.Title ); + Right( column.IsRight, column.Title ); + SetAcl( column.Acl, column.AclElseTemplateId ); + EnableCustomColumn( column.GetCellControl() ); + EnableResizable( column ); + TitleAlign( column.Title ); + } + + /// + /// 设置列宽 + /// + protected void SetColumnWidth( ColumnInfo column ) { + if ( _shareConfig.IsEnableCustomColumn ) + return; + if ( IsEnableResizable( column ) ) { + Attribute( "[nzWidth]", GetWidthByTableSettings( column.Title, column.Width ) ); + return; + } + this.Width( column.Width ); + } + + /// + /// 从表格设置获取宽度 + /// + protected string GetWidthByTableSettings( string title, string width ) { + if ( width.IsEmpty() ) + return $"{_shareConfig.TableSettingsId}.getWidth('{title}')"; + return $"{_shareConfig.TableSettingsId}.getWidth('{title}','{width}')"; + } + + /// + /// 配置访问控制列表 + /// + public TableHeadColumnBuilder SetAcl( string acl, string aclElseTemplateId ) { + _config.SetAttribute( UiConst.Acl, acl ); + _config.SetAttribute( UiConst.AclElseTemplateId, aclElseTemplateId ); + this.Acl( _config ); + return this; + } + + /// + /// 配置启用拖动调整列宽 + /// + protected void EnableResizable( ColumnInfo column ) { + if ( IsEnableResizable( column ) == false ) + return; + Resizable( column.Title ); + } + + /// + /// 是否启用拖动调整列宽 + /// + protected bool IsEnableResizable( ColumnInfo column ) { + if ( column.IsEnableResizable ) + return true; + return _shareConfig.IsEnableResizable; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableHeadRowBuilder.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableHeadRowBuilder.cs new file mode 100644 index 000000000..09bee41a4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableHeadRowBuilder.cs @@ -0,0 +1,79 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Components.Tables.Helpers; +using Util.Ui.NgZorro.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Builders; + +/// +/// 表头行标签生成器 +/// +public class TableHeadRowBuilder : TableRowBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化表格主体行标签生成器 + /// + public TableHeadRowBuilder( Config config ) : base( config ) { + _config = config; + } + + /// + /// 创建表头单元格标签生成器 + /// + public virtual TableHeadColumnBuilder CreateTableHeadColumnBuilder() { + return new TableHeadColumnBuilder( _config.CopyRemoveAttributes(), GetTableHeadColumnShareConfig() ); + } + + /// + /// 获取表头列共享配置 + /// + protected TableHeadColumnShareConfig GetTableHeadColumnShareConfig() { + return _config.GetValueFromItems() ?? new TableHeadColumnShareConfig( GetTableShareConfig() ); + } + + /// + /// 获取表格共享配置 + /// + private TableShareConfig GetTableShareConfig() { + return _config.GetValueFromItems() ?? new TableShareConfig(); + } + + /// + /// 配置事件 + /// + public override TableRowBuilder Events() { + this.OnClick( _config ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + ConfigAutoCreate(); + ConfigContent(); + } + + /// + /// 配置自动创建嵌套结构 + /// + public void ConfigAutoCreate() { + var service = new TableHeadRowAutoCreateService( this ); + service.Init(); + } + + /// + /// 配置内容 + /// + public void ConfigContent() { + if ( TableShareConfig.HeadColumnAutoCreated ) + return; + _config.Content.AppendTo( this ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableRowBuilder.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableRowBuilder.cs new file mode 100644 index 000000000..7ba53d207 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableRowBuilder.cs @@ -0,0 +1,146 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Tables.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Builders; + +/// +/// 表格行标签生成器 +/// +public class TableRowBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格共享配置 + /// + private TableShareConfig _tableShareConfig; + /// + /// 点击事件列表 + /// + private readonly List _onClick; + + /// + /// 初始化表格行标签生成器 + /// + /// 配置 + public TableRowBuilder( Config config ) : base( config, "tr" ) { + _config = config; + _onClick = []; + } + + /// + /// 获取配置 + /// + public Config GetConfig() { + return _config; + } + + /// + /// 获取表格共享配置 + /// + public TableShareConfig TableShareConfig => _tableShareConfig ??= _config.GetValueFromItems() ?? new TableShareConfig(); + + /// + /// 配置当前列是否展开 + /// + public TableRowBuilder Expand() { + AttributeIfNotEmpty( "[nzExpand]", _config.GetValue( UiConst.Expand ) ); + return this; + } + + /// + /// 配置事件 + /// + public virtual TableRowBuilder Events() { + ConfigOnClick(); + return this; + } + + /// + /// 配置单击事件 + /// + private void ConfigOnClick() { + AddOnClick(); + this.OnClick( _onClick.Join( separator: ";" ) ); + } + + /// + /// 添加单击事件 + /// + private void AddOnClick() { + AddOnClick( _config.GetValue( UiConst.OnClick ) ); + AddOnClick( TableShareConfig.OnClickRow ); + if ( IsSelectOnClick() ) + AddOnClick( $"{TableShareConfig.TableExtendId}.toggleSelect(row)" ); + if ( IsSelectOnlyOnClick() ) + AddOnClick( $"{TableShareConfig.TableExtendId}.selectRowOnly(row)" ); + if ( IsCheckRowOnClick() ) + AddOnClick( $"{TableShareConfig.TableExtendId}.toggle(row)" ); + if ( IsCheckRowOnlyOnClick() ) + AddOnClick( $"{TableShareConfig.TableExtendId}.checkRowOnly(row)" ); + } + + /// + /// 添加单击事件 + /// + private void AddOnClick( string onClick ) { + if ( onClick.IsEmpty() ) + return; + _onClick.Add( onClick ); + } + + /// + /// 是否单击选中行 + /// + private bool IsSelectOnClick() { + var result = _config.GetValue( UiConst.SelectOnClick ); + return result || TableShareConfig.SelectOnClickRow; + } + + /// + /// 是否单击仅选中该行 + /// + private bool IsSelectOnlyOnClick() { + var result = _config.GetValue( UiConst.SelectOnlyOnClick ); + return result || TableShareConfig.SelectOnlyOnClickRow; + } + + /// + /// 是否单击选中复选框 + /// + private bool IsCheckRowOnClick() { + if ( TableShareConfig.IsShowCheckbox == false ) + return false; + var result = _config.GetValue( UiConst.CheckOnClick ); + return result || TableShareConfig.CheckOnClickRow; + } + + /// + /// 是否单击选中单选框 + /// + private bool IsCheckRowOnlyOnClick() { + if ( TableShareConfig.IsShowRadio == false ) + return false; + var result = _config.GetValue( UiConst.CheckOnClick ); + return result || TableShareConfig.CheckOnClickRow; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + Expand().Events(); + ConfigSelect(); + } + + /// + /// 配置选中行 + /// + private void ConfigSelect() { + if ( IsSelectOnClick() || IsSelectOnlyOnClick() ) + Attribute( "[class.table-row-selected]", $"{TableShareConfig.TableExtendId}.isSelected(row)" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/TableSettingsBuilder.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableSettingsBuilder.cs new file mode 100644 index 000000000..d33054437 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/TableSettingsBuilder.cs @@ -0,0 +1,176 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Components.Tables.Helpers; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Tables.Builders; + +/// +/// 表格设置标签生成器 +/// +public class TableSettingsBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格共享配置 + /// + private TableShareConfig _tableShareConfig; + + /// + /// 初始化表格设置标签生成器 + /// + /// 配置 + public TableSettingsBuilder( Config config ) : base( config, NgZorroOptionsService.GetOptions().TableSettingsTag ) { + _config = config; + _tableShareConfig = GetTableShareConfig(); + } + + /// + /// 获取表格共享配置 + /// + public TableShareConfig GetTableShareConfig() { + return _tableShareConfig ??= _config.GetValueFromItems() ?? new TableShareConfig(); + } + + /// + /// 设置表格尺寸 + /// + public TableSettingsBuilder IsTreeTable() { + if ( _tableShareConfig.IsTreeTable == false ) + return this; + Attribute( "[isTreeTable]", "true" ); + return this; + } + + /// + /// 设置表格尺寸 + /// + public TableSettingsBuilder Size() { + AttributeIfNotEmpty( "initSize", _config.GetValue( UiConst.Size )?.Description() ); + return this; + } + + /// + /// 设置表格边框 + /// + public TableSettingsBuilder Bordered() { + AttributeIfNotEmpty( "[initBordered]", _config.GetValue( UiConst.Bordered ) ); + return this; + } + + /// + /// 设置滚动宽高 + /// + public TableSettingsBuilder Scroll() { + AttributeIfNotEmpty( "scrollHeight", _tableShareConfig.ScrollHeight ); + AttributeIfNotEmpty( "scrollWidth", _tableShareConfig.ScrollWidth ); + AttributeIfNotEmpty( "[initScroll]", _config.GetValue( UiConst.Scroll ) ); + return this; + } + + /// + /// 配置是否启用固定列 + /// + public TableSettingsBuilder EnableFixedColumn() { + if ( _tableShareConfig.IsEnableFixedColumn ) + Attribute( "[enableFixedColumn]", "true" ); + return this; + } + + /// + /// 配置初始设置的列集合 + /// + public TableSettingsBuilder InitColumns() { + if ( _tableShareConfig.ColumnNumber == 0 ) + return this; + Attribute( "[initColumns]", GetColumnsJson() ); + return this; + } + + /// + /// 获取自定义列Json + /// + private string GetColumnsJson() { + var result = new List(); + AddRadioColumn( result ); + AddCheckboxColumn( result ); + AddLineNumberColumn( result ); + result.AddRange( _tableShareConfig.Columns.Where( column => column.IsInner == false ).Select( column => column.ToCustomColumn() ) ); + var json = Util.Helpers.Json.ToJson( result, new JsonOptions { ToSingleQuotes = true, IgnoreNullValues = true } ); + return json.Replace( $"'{GetCheckboxWidth()}'", GetCheckboxWidth() ) + .Replace( $"'{GetRadioWidth()}'", GetRadioWidth() ) + .Replace( $"'{GetLineNumberWidth()}'", GetLineNumberWidth() ); + } + + /// + /// 获取表格复选框宽度 + /// + private string GetCheckboxWidth() { + return $"{_tableShareConfig.TableExtendId}.config.table.checkboxWidth"; + } + + /// + /// 获取表格单选框宽度 + /// + private string GetRadioWidth() { + return $"{_tableShareConfig.TableExtendId}.config.table.radioWidth"; + } + + /// + /// 获取表格序号宽度 + /// + private string GetLineNumberWidth() { + return $"{_tableShareConfig.TableExtendId}.config.table.lineNumberWidth"; + } + + /// + /// 添加单选列信息 + /// + private void AddRadioColumn( List result ) { + if ( _tableShareConfig.IsTreeTable ) + return; + if ( _tableShareConfig.IsShowRadio == false ) + return; + var options = NgZorroOptionsService.GetOptions(); + result.Add( new CustomColumn( "util.radio", GetRadioWidth(), _tableShareConfig.GetRadioLeft(), align: options.TableRadioAlign.Description() ) ); + } + + /// + /// 添加复选列信息 + /// + private void AddCheckboxColumn( List result ) { + if ( _tableShareConfig.IsTreeTable ) + return; + if ( _tableShareConfig.IsShowCheckbox == false ) + return; + var options = NgZorroOptionsService.GetOptions(); + result.Add( new CustomColumn( "util.checkbox", GetCheckboxWidth(), _tableShareConfig.GetCheckboxLeft(), align: options.TableCheckboxAlign.Description() ) ); + } + + /// + /// 添加序号信息 + /// + private void AddLineNumberColumn( List result ) { + if ( _tableShareConfig.IsTreeTable ) + return; + if ( _tableShareConfig.IsShowLineNumber == false ) + return; + var options = NgZorroOptionsService.GetOptions(); + result.Add( new CustomColumn( "util.lineNumber", GetLineNumberWidth(), _tableShareConfig.GetLineNumberLeft(), align: options.TableLineNumberAlign.Description() ) ); + } + + /// + /// 配置 + /// + public override void Config() { + if ( _tableShareConfig.Key.IsEmpty() ) + throw new ArgumentNullException( "表格存储标识属性 Key 必须设置!" ); + Attribute( $"#{_tableShareConfig.TableSettingsId}" ); + Attribute( "key", _tableShareConfig.Key ); + IsTreeTable().Size().Bordered().Scroll().EnableFixedColumn(); + InitColumns(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Builders/TotalTemplateBuilder.cs b/src/Util.Ui.NgZorro/Components/Tables/Builders/TotalTemplateBuilder.cs new file mode 100644 index 000000000..8a910426a --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Builders/TotalTemplateBuilder.cs @@ -0,0 +1,43 @@ +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Components.Templates.Builders; + +namespace Util.Ui.NgZorro.Components.Tables.Builders; + +/// +/// 表格总行数模板标签生成器 +/// +public class TotalTemplateBuilder : TemplateBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格共享配置 + /// + private TableShareConfig _tableShareConfig; + + /// + /// 初始化表格行标签生成器 + /// + public TotalTemplateBuilder( Config config ) : base( config ) { + _config = config; + _tableShareConfig = GetTableShareConfig(); + } + + /// + /// 获取表格共享配置 + /// + public TableShareConfig GetTableShareConfig() { + return _tableShareConfig ??= _config.GetValueFromItems() ?? new TableShareConfig(); + } + + /// + /// 配置 + /// + public override void Config() { + Attribute( $"#{_tableShareConfig.TotalTemplateId}" ); + Attribute( "let-range", "range" ); + Attribute( "let-total" ); + SetContent( "{{ 'util.tableTotalTemplate'|i18n:{start:range[0],end:range[1],total:total} }}" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Configs/TableBodyShareConfig.cs b/src/Util.Ui.NgZorro/Components/Tables/Configs/TableBodyShareConfig.cs new file mode 100644 index 000000000..b3e091dee --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Configs/TableBodyShareConfig.cs @@ -0,0 +1,7 @@ +namespace Util.Ui.NgZorro.Components.Tables.Configs; + +/// +/// 表格主体共享配置 +/// +public class TableBodyShareConfig { +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Configs/TableColumnDisplayShareConfig.cs b/src/Util.Ui.NgZorro/Components/Tables/Configs/TableColumnDisplayShareConfig.cs new file mode 100644 index 000000000..99e708fce --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Configs/TableColumnDisplayShareConfig.cs @@ -0,0 +1,7 @@ +namespace Util.Ui.NgZorro.Components.Tables.Configs; + +/// +/// 表格编辑列显示区域共享配置 +/// +public class TableColumnDisplayShareConfig { +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Configs/TableColumnShareConfig.cs b/src/Util.Ui.NgZorro/Components/Tables/Configs/TableColumnShareConfig.cs new file mode 100644 index 000000000..3fe37b518 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Configs/TableColumnShareConfig.cs @@ -0,0 +1,257 @@ +using Util.Helpers; +using Util.Ui.NgZorro.Components.Tables.Helpers; + +namespace Util.Ui.NgZorro.Components.Tables.Configs; + +/// +/// 表格列共享配置 +/// +public class TableColumnShareConfig { + /// + /// 表格共享配置 + /// + private readonly TableShareConfig _tableShareConfig; + /// + /// 编辑控件模板标识 + /// + private string _controlTemplateId; + + /// + /// 初始化表格列共享配置 + /// + /// 表格共享配置 + public TableColumnShareConfig( TableShareConfig tableShareConfig ) { + _tableShareConfig = tableShareConfig; + IsAutoCreateControl = true; + Index = _tableShareConfig.ColumnNumber; + _tableShareConfig.ColumnNumber++; + Column = Id.Create(); + } + + /// + /// 表格设置组件标识 + /// + public string TableSettingsId => _tableShareConfig.TableSettingsId; + + /// + /// 是否树形表格 + /// + public bool IsTreeTable => _tableShareConfig.IsTreeTable; + + /// + /// 是否启用基础扩展 + /// + public bool IsEnableExtend => _tableShareConfig.IsEnableExtend; + + /// + /// 是否启用编辑模式 + /// + public bool IsEnableEdit => _tableShareConfig.IsEnableEdit; + + /// + /// 是否启用表格设置 + /// + public bool IsEnableTableSettings => _tableShareConfig.IsEnableTableSettings; + + /// + /// 是否启用自定义列 + /// + public bool IsEnableCustomColumn => _tableShareConfig.IsEnableCustomColumn; + + /// + /// 表格设置是否启用固定列 + /// + public bool IsEnableFixedColumn => _tableShareConfig.IsEnableFixedColumn; + + /// + /// 表格扩展标识 + /// + public string TableExtendId => _tableShareConfig.TableExtendId; + + /// + /// 表格编辑扩展标识 + /// + public string TableEditId => _tableShareConfig.TableEditId; + + /// + /// 表格主体行标识 + /// + public string RowId => _tableShareConfig.RowId; + + /// + /// 标题 + /// + public string Title { + get { + var column = _tableShareConfig.GetColumn( Index ); + return column.Title; + } + } + + /// + /// 列名 + /// + public string Column { get; set; } + + /// + /// 索引 + /// + public int Index { get; set; } + + /// + /// 自定义列标识 + /// + public string CellControl { + get { + var column = _tableShareConfig.GetColumn( Index ); + return column.GetCellControl(); + } + } + + /// + /// 对齐方式 + /// + public string Align { + get { + var column = _tableShareConfig.GetColumn( Index ); + return column.Align; + } + } + + /// + /// 固定左侧 + /// + public string IsLeft { + get { + var column = _tableShareConfig.GetColumn( Index ); + return column.IsLeft; + } + } + + /// + /// 固定右侧 + /// + public string IsRight { + get { + var column = _tableShareConfig.GetColumn( Index ); + return column.IsRight; + } + } + + /// + /// 编辑控件模板标识 + /// + public string ControlTemplateId => _controlTemplateId.IsEmpty() ? $"{TableEditId}_{Column}" : _controlTemplateId; + + /// + /// 是否自动创建编辑列控件区域 + /// + public bool IsAutoCreateControl { get; set; } + + /// + /// 是否显示复选框 + /// + public bool IsShowCheckbox => _tableShareConfig.IsShowCheckbox; + + /// + /// 是否显示单选框 + /// + public bool IsShowRadio => _tableShareConfig.IsShowRadio; + + /// + /// 是否仅能选择单选框叶节点 + /// + public bool IsCheckLeafOnly => _tableShareConfig.IsCheckLeafOnly; + + /// + /// 是否显示序号 + /// + public bool IsShowLineNumber => _tableShareConfig.IsShowLineNumber; + + /// + /// 固定复选框到左侧 + /// + public string IsCheckboxLeft => _tableShareConfig.IsCheckboxLeft; + + /// + /// 固定单选框到左侧 + /// + public string IsRadioLeft => _tableShareConfig.IsRadioLeft; + + /// + /// 固定序号到左侧 + /// + public string IsLineNumberLeft => _tableShareConfig.IsLineNumberLeft; + + /// + /// 是否第一列 + /// + public bool IsFirst => Index == 0; + + /// + /// 设置编辑控件模板标识 + /// + /// 编辑控件模板标识 + public void SetControlTemplateId( string controlTemplateId ) { + _controlTemplateId = controlTemplateId; + } + + /// + /// 添加列 + /// + /// 列信息 + public void AddColumn( ColumnInfo column ) { + if ( column == null ) + return; + column.Index = Index; + Column = column.Column; + var headColumn = _tableShareConfig.GetHeadColumn( Index ); + if ( headColumn != null ) { + if ( headColumn.Title.IsEmpty() == false ) + column.Title = headColumn.Title; + if ( headColumn.CellControl.IsEmpty() == false ) + column.CellControl = headColumn.CellControl; + if ( headColumn.Align.IsEmpty() == false ) + column.Align = headColumn.Align; + if ( headColumn.TitleAlign.IsEmpty() == false ) + column.TitleAlign = headColumn.TitleAlign; + if ( headColumn.Width.IsEmpty() == false ) + column.Width = headColumn.Width; + if ( headColumn.Ellipsis != null ) + column.Ellipsis = headColumn.Ellipsis; + if ( headColumn.IsLeft != null ) + column.IsLeft = headColumn.IsLeft; + if ( headColumn.IsRight != null ) + column.IsRight = headColumn.IsRight; + } + _tableShareConfig.Columns.Add( column ); + if ( column.IsEnableResizable ) + _tableShareConfig.IsEnableResizable = true; + InitLeft(); + } + + /// + /// 初始化左侧固定 + /// + private void InitLeft() { + if ( IsFirst && IsLeft == "true" ) { + _tableShareConfig.IsCheckboxLeft = "true"; + _tableShareConfig.IsRadioLeft = "true"; + _tableShareConfig.IsLineNumberLeft = "true"; + } + } + + /// + /// 启用编辑模式 + /// + public void EnableEdit() { + _tableShareConfig.IsEnableEdit = true; + } + + /// + /// 启用自动省略 + /// + public void EnableEllipsis() { + _tableShareConfig.IsEnableEllipsis = true; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Configs/TableHeadColumnShareConfig.cs b/src/Util.Ui.NgZorro/Components/Tables/Configs/TableHeadColumnShareConfig.cs new file mode 100644 index 000000000..f7a05a68f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Configs/TableHeadColumnShareConfig.cs @@ -0,0 +1,210 @@ +using Util.Ui.NgZorro.Components.Tables.Helpers; + +namespace Util.Ui.NgZorro.Components.Tables.Configs; + +/// +/// 表头列共享配置 +/// +public class TableHeadColumnShareConfig { + /// + /// 表格共享配置 + /// + private readonly TableShareConfig _tableShareConfig; + + /// + /// 初始化表头列共享配置 + /// + /// 表格共享配置 + public TableHeadColumnShareConfig( TableShareConfig tableShareConfig ) { + _tableShareConfig = tableShareConfig; + Index = _tableShareConfig.HeadColumnNumber; + _tableShareConfig.HeadColumnNumber++; + } + + /// + /// 是否树形表格 + /// + public bool IsTreeTable => _tableShareConfig.IsTreeTable; + + /// + /// 表格扩展标识 + /// + public string TableExtendId => _tableShareConfig.TableExtendId; + + /// + /// 表格编辑扩展标识 + /// + public string TableEditId => _tableShareConfig.TableEditId; + + /// + /// 是否显示复选框 + /// + public bool IsShowCheckbox => _tableShareConfig.IsShowCheckbox; + + /// + /// 是否显示单选框 + /// + public bool IsShowRadio => _tableShareConfig.IsShowRadio; + + /// + /// 是否显示序号 + /// + public bool IsShowLineNumber => _tableShareConfig.IsShowLineNumber; + + /// + /// 表头行数 + /// + public int HeadRowNumber => _tableShareConfig.HeadRowNumber; + + /// + /// 固定复选框到左侧 + /// + public string IsCheckboxLeft => _tableShareConfig.IsCheckboxLeft; + + /// + /// 固定单选框到左侧 + /// + public string IsRadioLeft => _tableShareConfig.IsRadioLeft; + + /// + /// 固定序号到左侧 + /// + public string IsLineNumberLeft => _tableShareConfig.IsLineNumberLeft; + + /// + /// 是否自动创建表头单元格th + /// + public bool IsAutoCreateHeadColumn { + get => _tableShareConfig.IsAutoCreateHeadColumn; + set => _tableShareConfig.IsAutoCreateHeadColumn = value; + } + + /// + /// 索引 + /// + public int Index { get; set; } + + /// + /// 是否第一列 + /// + public bool IsFirst => Index == 0; + + /// + /// 是否最后一列 + /// + public bool IsLast => _tableShareConfig.Columns.Count == Index + 1; + + /// + /// 是否启用自定义列 + /// + public bool IsEnableCustomColumn => _tableShareConfig.IsEnableCustomColumn; + + /// + /// 表格设置是否启用固定列 + /// + public bool IsEnableFixedColumn => _tableShareConfig.IsEnableFixedColumn; + + /// + /// 是否启用拖动调整列宽 + /// + public bool IsEnableResizable { + get { + var headColumn = _tableShareConfig.GetHeadColumn( Index ); + if ( headColumn is { IsEnableResizable: true } ) + return true; + if ( _tableShareConfig.IsEnableResizable == false ) + return false; + if ( _tableShareConfig.IsEnableCustomColumn ) + return true; + return _tableShareConfig.Columns.Count <= 1 || IsLast == false; + } + } + + /// + /// 是否启用表格设置 + /// + public bool IsEnableTableSettings { + get { + if ( _tableShareConfig.ColumnNumber == 0 ) + return false; + if ( IsEnableCustomColumn ) + return true; + if ( IsEnableFixedColumn ) + return true; + var headColumn = _tableShareConfig.GetHeadColumn( Index ); + if ( headColumn is { IsEnableResizable: true } ) + return true; + return _tableShareConfig.IsEnableResizable; + } + } + + /// + /// 自定义列标识 + /// + public string CellControl { + get { + var column = _tableShareConfig.GetColumn( Index ); + return column?.GetCellControl(); + } + } + + /// + /// 标题 + /// + public string Title { + get { + var column = _tableShareConfig.GetColumn( Index ); + return column?.Title; + } + } + + /// + /// 对齐方式 + /// + public string Align { + get { + var column = _tableShareConfig.GetColumn( Index ); + return column?.Align; + } + } + + /// + /// 标题对齐方式 + /// + public string TitleAlign { + get { + var column = _tableShareConfig.GetHeadColumn( Index ); + return column?.TitleAlign; + } + } + + /// + /// 表格设置组件标识 + /// + public string TableSettingsId => _tableShareConfig.TableSettingsId; + + /// + /// 添加列 + /// + /// 列信息 + public void AddColumn( HeadColumnInfo column ) { + if ( column == null ) + return; + column.Index = Index; + _tableShareConfig.HeadColumns.Add( column ); + if ( column.IsEnableResizable == true ) + _tableShareConfig.IsEnableResizable = true; + InitLeft( column ); + } + + /// + /// 初始化左侧固定 + /// + private void InitLeft( HeadColumnInfo column ) { + if ( IsFirst && column.IsLeft == "true" ) { + _tableShareConfig.IsCheckboxLeft = "true"; + _tableShareConfig.IsRadioLeft = "true"; + _tableShareConfig.IsLineNumberLeft = "true"; + } + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Configs/TableHeadShareConfig.cs b/src/Util.Ui.NgZorro/Components/Tables/Configs/TableHeadShareConfig.cs new file mode 100644 index 000000000..8bc450890 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Configs/TableHeadShareConfig.cs @@ -0,0 +1,7 @@ +namespace Util.Ui.NgZorro.Components.Tables.Configs; + +/// +/// 表格头共享配置 +/// +public class TableHeadShareConfig { +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Configs/TableShareConfig.cs b/src/Util.Ui.NgZorro/Components/Tables/Configs/TableShareConfig.cs new file mode 100644 index 000000000..8f5f0aca4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Configs/TableShareConfig.cs @@ -0,0 +1,301 @@ +using Util.Ui.NgZorro.Components.Tables.Helpers; + +namespace Util.Ui.NgZorro.Components.Tables.Configs; + +/// +/// 表格共享配置 +/// +public class TableShareConfig { + /// + /// 表格主体行标识 + /// + private string _rowId; + + /// + /// 初始化表格共享配置 + /// + /// 表格标识 + public TableShareConfig( string id = null ) { + Id = id.IsEmpty() ? Util.Helpers.Id.Create() : id; + HeadColumns = new List(); + Columns = new List(); + IsAutoCreateHead = true; + IsAutoCreateHeadRow = true; + IsAutoCreateHeadColumn = true; + IsAutoCreateBody = true; + IsAutoCreateBodyRow = true; + HeadRowNumber = 0; + } + + /// + /// 表格标识 + /// + public string Id { get; } + + /// + /// 表格存储标识 - 保存表格设置参数时作为存储键 + /// + public string Key { get; set; } + + /// + /// 表格扩展标识 + /// + public string TableExtendId => $"x_{Id}"; + + /// + /// 表格编辑扩展标识 + /// + public string TableEditId => $"e_{Id}"; + + /// + /// 表格主体行标识 + /// + public string RowId => _rowId.IsEmpty() ? $"{Id}_row" : _rowId; + + /// + /// 表格设置组件标识 + /// + public string TableSettingsId => $"ts_{Id}"; + + /// + /// 设置表格主体行标识 + /// + /// 行标识 + public void SetRowId( string rowId ) { + _rowId = rowId; + } + + /// + /// 表格总行数模板标识 + /// + public string TotalTemplateId => $"total_{Id}"; + + /// + /// 表头列信息集合 + /// + public List HeadColumns { get; } + + /// + /// 列信息集合 + /// + public List Columns { get; } + + /// + /// 表头列数 + /// + public int HeadColumnNumber { get; set; } + + /// + /// 列数 + /// + public int ColumnNumber { get; set; } + + /// + /// 表头行数 + /// + public int HeadRowNumber { get; set; } + + /// + /// 是否自动创建表头thead + /// + public bool IsAutoCreateHead { get; set; } + + /// + /// 表头thead已自动创建 + /// + public bool HeadAutoCreated { get; set; } + + /// + /// 是否自动创建表头行tr + /// + public bool IsAutoCreateHeadRow { get; set; } + + /// + /// 表头行tr已自动创建 + /// + public bool HeadRowAutoCreated { get; set; } + + /// + /// 是否自动创建表头单元格th + /// + public bool IsAutoCreateHeadColumn { get; set; } + + /// + /// 表头单元格th已自动创建 + /// + public bool HeadColumnAutoCreated { get; set; } + + /// + /// 是否自动创建表格主体tbody + /// + public bool IsAutoCreateBody { get; set; } + + /// + /// 表格主体tbody已自动创建 + /// + public bool BodyAutoCreated { get; set; } + + /// + /// 是否自动创建表格主体行tr + /// + public bool IsAutoCreateBodyRow { get; set; } + + /// + /// 表格主体行tr已自动创建 + /// + public bool BodyRowAutoCreated { get; set; } + + /// + /// 是否启用基础扩展 + /// + public bool IsEnableExtend { get; set; } + + /// + /// 是否启用自动省略 + /// + public bool IsEnableEllipsis { get; set; } + + /// + /// 是否启用编辑模式 + /// + public bool IsEnableEdit { get; set; } + + /// + /// 是否启用表格设置 + /// + public bool IsEnableTableSettings { + get { + if ( ColumnNumber == 0 ) + return false; + if ( IsEnableCustomColumn ) + return true; + if ( IsEnableResizable ) + return true; + if ( IsEnableFixedColumn ) + return true; + return false; + } + } + + /// + /// 是否启用自定义列 + /// + public bool IsEnableCustomColumn { get; set; } + + /// + /// 是否启用拖动调整列宽 + /// + public bool IsEnableResizable { get; set; } + + /// + /// 表格设置是否启用固定列 + /// + public bool IsEnableFixedColumn { get; set; } + + /// + /// 是否显示复选框 + /// + public bool IsShowCheckbox { get; set; } + + /// + /// 是否显示单选框 + /// + public bool IsShowRadio { get; set; } + + /// + /// 是否显示序号 + /// + public bool IsShowLineNumber { get; set; } + + /// + /// 固定复选框到左侧 + /// + public string IsCheckboxLeft { get; set; } + + /// + /// 固定单选框到左侧 + /// + public string IsRadioLeft { get; set; } + + /// + /// 固定序号到左侧 + /// + public string IsLineNumberLeft { get; set; } + + /// + /// 是否树形表格 + /// + public bool IsTreeTable { get; set; } + + /// + /// 是否仅能选择单选框叶节点 + /// + public bool IsCheckLeafOnly { get; set; } + + /// + /// 行单击事件 + /// + public string OnClickRow { get; set; } + + /// + /// 点击是否选中行 + /// + public bool SelectOnClickRow { get; set; } + + /// + /// 点击是否仅选中该行 + /// + public bool SelectOnlyOnClickRow { get; set; } + + /// + /// 点击行是否选中复选框 + /// + public bool CheckOnClickRow { get; set; } + + /// + /// 滚动宽度 + /// + public string ScrollWidth { get; set; } + + /// + /// 滚动高度 + /// + public string ScrollHeight { get; set; } + + /// + /// 获取列信息 + /// + /// 列索引 + public ColumnInfo GetColumn( int index ) { + return Columns.Find( t => t.Index == index ); + } + + /// + /// 获取表头列信息 + /// + /// 列索引 + public HeadColumnInfo GetHeadColumn( int index ) { + return HeadColumns.Find( t => t.Index == index ); + } + + /// + /// 固定复选框到左侧 + /// + public bool? GetCheckboxLeft() { + return IsCheckboxLeft.IsEmpty() ? null : true; + } + + /// + /// 固定单选框到左侧 + /// + public bool? GetRadioLeft() { + return IsRadioLeft.IsEmpty() ? null : true; + } + + /// + /// 固定序号到左侧 + /// + public bool? GetLineNumberLeft() { + return IsLineNumberLeft.IsEmpty() ? null : true; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/FilterTriggerTagHelper.cs b/src/Util.Ui.NgZorro/Components/Tables/FilterTriggerTagHelper.cs new file mode 100644 index 000000000..e2dab9710 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/FilterTriggerTagHelper.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Tables.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tables; + +/// +/// 表头单元格过滤器触发按钮,生成的标签为<nz-filter-trigger></nz-filter-trigger> +/// +[HtmlTargetElement( "util-filter-trigger" )] +public class FilterTriggerTagHelper : AngularTagHelperBase { + /// + /// [nzDropdownMenu],下拉菜单组件,类型: NzDropdownMenuComponent + /// + public string DropdownMenu { get; set; } + /// + /// [nzVisible],是否显示下拉菜单 + /// + public string Visible { get; set; } + /// + /// [(nzVisible)],是否显示下拉菜单 + /// + public string BindonVisible { get; set; } + /// + /// [nzActive],是否激活选中图标效果,默认值: false + /// + public string Active { get; set; } + /// + /// [nzHasBackdrop],是否附带背景,默认值: false + /// + public string HasBackdrop { get; set; } + /// + /// (nzVisibleChange),下拉菜单显示状态变化事件,参数为 nzVisible,类型: EventEmitter<boolean> + /// + public string OnVisibleChange { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new FilterTriggerRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Helpers/ColumnInfo.cs b/src/Util.Ui.NgZorro/Components/Tables/Helpers/ColumnInfo.cs new file mode 100644 index 000000000..9237dd84f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Helpers/ColumnInfo.cs @@ -0,0 +1,87 @@ +namespace Util.Ui.NgZorro.Components.Tables.Helpers; + +/// +/// 列配置信息 +/// +public class ColumnInfo { + /// + /// 索引 + /// + public int Index { get; set; } + /// + /// 标题 + /// + public string Title { get; set; } + /// + /// 列名 + /// + public string Column { get; set; } + /// + /// 自定义列标识 + /// + public string CellControl { get; set; } + /// + /// 对齐方式 + /// + public string Align { get; set; } + /// + /// 标题对齐方式 + /// + public string TitleAlign { get; set; } + /// + /// 是否自动省略 + /// + public bool? Ellipsis { get; set; } + /// + /// 是否排序 + /// + public bool IsSort { get; set; } + /// + /// 宽度 + /// + public string Width { get; set; } + /// + /// 是否第一列 + /// + public bool IsFirst { get; set; } + /// + /// 左侧固定 + /// + public string IsLeft { get; set; } + /// + /// 右侧固定 + /// + public string IsRight { get; set; } + /// + /// 访问控制列表 + /// + public string Acl { get; set; } + /// + /// 访问控制列表模板标识 + /// + public string AclElseTemplateId { get; set; } + /// + /// 是否启用拖动调整列宽 + /// + public bool IsEnableResizable { get; set; } + /// + /// 是否内部列 + /// + public bool IsInner { get; set; } + + /// + /// 转换为自定义列 + /// + public CustomColumn ToCustomColumn() { + bool? isLeft = IsLeft.IsEmpty() ? null : true; + bool? isRight = IsRight.IsEmpty() ? null : true; + return new CustomColumn( GetCellControl(), Width, isLeft, isRight, TitleAlign, Align, Ellipsis, Acl ); + } + + /// + /// 获取自定义列标识 + /// + public string GetCellControl() { + return CellControl.IsEmpty() ? Title : CellControl; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Helpers/CustomColumn.cs b/src/Util.Ui.NgZorro/Components/Tables/Helpers/CustomColumn.cs new file mode 100644 index 000000000..467b2ddac --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Helpers/CustomColumn.cs @@ -0,0 +1,81 @@ +namespace Util.Ui.NgZorro.Components.Tables.Helpers; + +/// +/// 自定义列信息 +/// +public class CustomColumn { + /// + /// 初始化自定义列信息 + /// + /// 标题 + /// 宽度 + /// 固定到左侧 + /// 固定到右侧 + /// 标题对齐方式 + /// 内容对齐方式 + /// 是否自动省略 + /// 访问控制 + public CustomColumn( string title, string width, bool? left = null, bool? right = null, + string titleAlign = null,string align = null, bool? ellipsis = null, string acl = null ) { + Title = title; + if ( width.IsEmpty() == false ) + Width = width; + Left = left; + Right = right; + if ( titleAlign.IsEmpty() == false ) + TitleAlign = titleAlign; + if ( align.IsEmpty() == false ) + Align = align; + Ellipsis = ellipsis; + if ( acl.IsEmpty() == false ) + Acl = acl; + } + + /// + /// 标题 + /// + [JsonPropertyName( "title" )] + public string Title { get; } + + /// + /// 宽度 + /// + [JsonPropertyName( "width" )] + public string Width { get; } + + /// + /// 固定到左侧 + /// + [JsonPropertyName( "left" )] + public bool? Left { get; } + + /// + /// 固定到右侧 + /// + [JsonPropertyName( "right" )] + public bool? Right { get; } + + /// + /// 标题对齐方式 + /// + [JsonPropertyName( "titleAlign" )] + public string TitleAlign { get; } + + /// + /// 内容对齐方式 + /// + [JsonPropertyName( "align" )] + public string Align { get; } + + /// + /// 是否自动省略 + /// + [JsonPropertyName( "ellipsis" )] + public bool? Ellipsis { get; } + + /// + /// 访问控制 + /// + [JsonPropertyName( "acl" )] + public string Acl { get; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Helpers/HeadColumnInfo.cs b/src/Util.Ui.NgZorro/Components/Tables/Helpers/HeadColumnInfo.cs new file mode 100644 index 000000000..88bcc894c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Helpers/HeadColumnInfo.cs @@ -0,0 +1,47 @@ +namespace Util.Ui.NgZorro.Components.Tables.Helpers; + +/// +/// 表头列配置信息 +/// +public class HeadColumnInfo { + /// + /// 索引 + /// + public int Index { get; set; } + /// + /// 标题 + /// + public string Title { get; set; } + /// + /// 宽度 + /// + public string Width { get; set; } + /// + /// 自定义列标识 + /// + public string CellControl { get; set; } + /// + /// 对齐方式 + /// + public string Align { get; set; } + /// + /// 标题对齐方式 + /// + public string TitleAlign { get; set; } + /// + /// 是否自动省略 + /// + public bool? Ellipsis { get; set; } + /// + /// 左侧固定 + /// + public string IsLeft { get; set; } + /// + /// 右侧固定 + /// + public string IsRight { get; set; } + /// + /// 是否启用拖动调整列宽 + /// + public bool? IsEnableResizable { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Helpers/ScrollInfo.cs b/src/Util.Ui.NgZorro/Components/Tables/Helpers/ScrollInfo.cs new file mode 100644 index 000000000..b9ae558a9 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Helpers/ScrollInfo.cs @@ -0,0 +1,45 @@ +namespace Util.Ui.NgZorro.Components.Tables.Helpers; + +/// +/// 滚动信息 +/// +public class ScrollInfo { + /// + /// 初始化滚动信息 + /// + /// 宽度 + /// 高度 + public ScrollInfo( string width, string height ) { + Width = GetPixelValue( width ); + Height = GetPixelValue( height ); + } + + /// + /// 获取像素值,如果传入数字,后加px,否则按原样返回 + /// + private string GetPixelValue( string value ) { + if ( value.IsEmpty() ) + return null; + if ( Util.Helpers.Validation.IsNumber( value ) ) + return $"{value}px"; + return value; + } + + /// + /// 宽度 + /// + [JsonPropertyName( "x" )] + public string Width { get; } + + /// + /// 高度 + /// + [JsonPropertyName( "y" )] + public string Height { get; } + + /// + /// 是否为空 + /// + [JsonIgnore] + public bool IsNull => Width.IsEmpty() && Height.IsEmpty(); +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableAutoCreateService.cs b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableAutoCreateService.cs new file mode 100644 index 000000000..e88d390f3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableAutoCreateService.cs @@ -0,0 +1,84 @@ +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.NgZorro.Components.Tables.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Helpers; + +/// +/// 表格嵌套结构自动创建服务 +/// +public class TableAutoCreateService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格共享配置 + /// + private readonly TableShareConfig _shareConfig; + /// + /// 表格标签生成器 + /// + private readonly TableBuilder _tableBuilder; + + /// + /// 初始化表格嵌套结构自动创建服务 + /// + /// 表格标签生成器 + public TableAutoCreateService( TableBuilder tableBuilder ) { + _tableBuilder = tableBuilder ?? throw new ArgumentNullException( nameof( tableBuilder ) ); + _config = tableBuilder.GetConfig(); + _shareConfig = tableBuilder.GetShareConfig(); + } + + /// + /// 自动创建嵌套结构 + /// + public void Init() { + if ( _shareConfig.Columns.Count == 0 ) + return; + CancelAutoCreate(); + AddHead(); + AddBody(); + } + + /// + /// 取消自动创建 + /// + private void CancelAutoCreate() { + var result = _config.GetValueFromAttributes( UiConst.EnableAutoCreate ); + if ( result == false ) { + _shareConfig.IsAutoCreateHead = false; + _shareConfig.IsAutoCreateHeadRow = false; + _shareConfig.IsAutoCreateHeadColumn = false; + _shareConfig.IsAutoCreateBody = false; + _shareConfig.IsAutoCreateBodyRow = false; + } + } + + /// + /// 添加表头 + /// + private void AddHead() { + if ( _shareConfig.IsAutoCreateHead == false ) + return; + _shareConfig.HeadAutoCreated = true; + var headBuilder = _tableBuilder.CreateTableHeadBuilder(); + _tableBuilder.AppendContent( headBuilder ); + headBuilder.ConfigAutoCreate(); + headBuilder.ConfigContent(); + } + + /// + /// 添加内容 + /// + private void AddBody() { + if ( _shareConfig.IsAutoCreateBody == false ) + return; + _shareConfig.BodyAutoCreated = true; + var bodyBuilder = _tableBuilder.CreateTableBodyBuilder(); + _tableBuilder.AppendContent( bodyBuilder ); + bodyBuilder.ConfigAutoCreate(); + bodyBuilder.ConfigContent(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableBodyAutoCreateService.cs b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableBodyAutoCreateService.cs new file mode 100644 index 000000000..d9beb1d19 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableBodyAutoCreateService.cs @@ -0,0 +1,64 @@ +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.NgZorro.Components.Tables.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Helpers; + +/// +/// 表格主体嵌套结构自动创建服务 +/// +public class TableBodyAutoCreateService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格共享配置 + /// + private readonly TableShareConfig _shareConfig; + /// + /// 表格主体标签生成器 + /// + private readonly TableBodyBuilder _bodyBuilder; + + /// + /// 初始化表格主体嵌套结构自动创建服务 + /// + /// 表格主体标签生成器 + public TableBodyAutoCreateService( TableBodyBuilder bodyBuilder ) { + _bodyBuilder = bodyBuilder ?? throw new ArgumentNullException( nameof( bodyBuilder ) ); + _config = _bodyBuilder.GetConfig(); + _shareConfig = _bodyBuilder.GetTableShareConfig(); + } + + /// + /// 自动创建嵌套结构 + /// + public void Init() { + if ( _shareConfig.Columns.Count == 0 ) + return; + CancelAutoCreate(); + AddBodyRow(); + } + + /// + /// 取消自动创建 + /// + private void CancelAutoCreate() { + var result = _config.GetValueFromAttributes( UiConst.EnableAutoCreate ); + if ( result == false ) + _shareConfig.IsAutoCreateBodyRow = false; + } + + /// + /// 添加表格主体行 + /// + private void AddBodyRow() { + if ( _shareConfig.IsAutoCreateBodyRow == false ) + return; + _shareConfig.BodyRowAutoCreated = true; + var rowBuilder = _bodyBuilder.CreateTableBodyRowBuilder(); + _bodyBuilder.AppendContent( rowBuilder ); + rowBuilder.Config(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableBodyService.cs b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableBodyService.cs new file mode 100644 index 000000000..898f9f0a7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableBodyService.cs @@ -0,0 +1,60 @@ +using Util.Ui.NgZorro.Components.Tables.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Helpers; + +/// +/// 表格主体服务 +/// +public class TableBodyService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格主体共享配置 + /// + private TableBodyShareConfig _shareConfig; + /// + /// 表格共享配置 + /// + private readonly TableShareConfig _tableShareConfig; + + /// + /// 初始化表格主体服务 + /// + /// 配置 + public TableBodyService( Config config ) { + _config = config; + _tableShareConfig = GetTableShareConfig(); + } + + /// + /// 获取表格共享配置 + /// + private TableShareConfig GetTableShareConfig() { + return _config.GetValueFromItems() ?? new TableShareConfig(); + } + + /// + /// 初始化 + /// + public void Init() { + CreateTableBodyShareConfig(); + CancelAutoCreateBody(); + } + + /// + /// 创建表格主体共享配置 + /// + private void CreateTableBodyShareConfig() { + _shareConfig = new TableBodyShareConfig(); + _config.SetValueToItems( _shareConfig ); + } + + /// + /// 取消自动创建表格主体 + /// + private void CancelAutoCreateBody() { + _tableShareConfig.IsAutoCreateBody = false; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableColumnControlService.cs b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableColumnControlService.cs new file mode 100644 index 000000000..03ea8f5e2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableColumnControlService.cs @@ -0,0 +1,59 @@ +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Helpers; + +/// +/// 表格编辑列控件区域服务 +/// +public class TableColumnControlService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格列共享配置 + /// + private readonly TableColumnShareConfig _shareConfig; + + /// + /// 初始化表格编辑列控件区域服务 + /// + /// 配置 + public TableColumnControlService( Config config ) { + _config = config; + _shareConfig = GetTableShareConfig(); + } + + /// + /// 获取表格列共享配置 + /// + private TableColumnShareConfig GetTableShareConfig() { + return _config.GetValueFromItems() ?? new TableColumnShareConfig(new TableShareConfig()); + } + + /// + /// 初始化 + /// + public void Init() { + CancelAutoCreateControl(); + ConfigControlTemplateId(); + } + + /// + /// 取消自动创建编辑列控件区域 + /// + private void CancelAutoCreateControl() { + _shareConfig.IsAutoCreateControl = false; + } + + /// + /// 配置编辑控件模板标识 + /// + private void ConfigControlTemplateId() { + var id = _config.GetValue( UiConst.Id ); + if ( id.IsEmpty() ) + return; + _shareConfig.SetControlTemplateId( id ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableColumnDisplayService.cs b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableColumnDisplayService.cs new file mode 100644 index 000000000..80b0f3514 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableColumnDisplayService.cs @@ -0,0 +1,40 @@ +using Util.Ui.NgZorro.Components.Tables.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Helpers; + +/// +/// 表格编辑列显示服务 +/// +public class TableColumnDisplayService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格编辑列显示区域共享配置 + /// + private TableColumnDisplayShareConfig _shareConfig; + + /// + /// 初始化表格编辑列显示服务 + /// + /// 配置 + public TableColumnDisplayService( Config config ) { + _config = config; + } + + /// + /// 初始化 + /// + public void Init() { + CreateShareConfig(); + } + + /// + /// 创建共享配置 + /// + private void CreateShareConfig() { + _shareConfig = new TableColumnDisplayShareConfig(); + _config.SetValueToItems( _shareConfig ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableColumnExpressionLoader.cs b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableColumnExpressionLoader.cs new file mode 100644 index 000000000..091e41c41 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableColumnExpressionLoader.cs @@ -0,0 +1,91 @@ +using Util.Ui.Expressions; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Tables.Helpers; + +/// +/// 表格列表达式加载器 +/// +public class TableColumnExpressionLoader : ExpressionLoaderBase { + /// + /// 加载模型信息 + /// + /// 配置 + /// 模型表达式信息 + protected override void Load( Config config, ModelExpressionInfo info ) { + LoadName( config, info ); + LoadDisplayName( config, info ); + LoadSort( config, info ); + LoadType( config, info ); + } + + /// + /// 加载属性名 + /// + protected virtual void LoadName( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.Column, info.PropertyName, false ); + } + + /// + /// 加载显示名称 + /// + protected virtual void LoadDisplayName( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.Title, info.DisplayName, false ); + } + + /// + /// 加载排序 + /// + protected virtual void LoadSort( Config config, ModelExpressionInfo info ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableTableSort == false ) + return; + var shareConfig = config.GetValueFromItems(); + if ( shareConfig == null || shareConfig.IsTreeTable ) + return; + config.SetAttribute( UiConst.Sort, true, false ); + } + + /// + /// 加载类型 + /// + protected virtual void LoadType( Config config, ModelExpressionInfo info ) { + if ( info.IsBool ) { + config.SetAttribute( UiConst.Type, TableColumnType.Bool ); + return; + } + if ( info.IsDate ) { + config.SetAttribute( UiConst.Type, TableColumnType.Date ); + return; + } + if ( info.IsEnum ) { + config.SetAttribute( UiConst.Type, TableColumnType.Enum ); + config.SetAttribute( UiConst.EnumContent, GetEnumContent( config, info ) ); + return; + } + } + + /// + /// 获取枚举内容 + /// + private string GetEnumContent( Config config, ModelExpressionInfo info ) { + var options = NgZorroOptionsService.GetOptions(); + var result = new StringBuilder(); + var column = config.GetValue( UiConst.Column ); + var items = Util.Helpers.Enum.GetItems( info.ModelType ); + items.ForEach( item => { + result.Append( $"" ); + if ( options.EnableI18n ) { + result.Append( "{{" ); + result.Append( $"'{item.Text}'|i18n" ); + result.Append( "}}" ); + } else { + result.Append( item.Text ); + } + result.Append( "" ); + } ); + return result.ToString(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableColumnService.cs b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableColumnService.cs new file mode 100644 index 000000000..b55c27ebd --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableColumnService.cs @@ -0,0 +1,141 @@ +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Tables.Helpers; + +/// +/// 表格列服务 +/// +public class TableColumnService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格列共享配置 + /// + private TableColumnShareConfig _shareConfig; + + /// + /// 初始化表格列服务 + /// + /// 配置 + public TableColumnService( Config config ) { + _config = config; + } + + /// + /// 初始化 + /// + public void Init() { + CreateTableColumnShareConfig(); + LoadExpression(); + EnableEdit(); + EnableEllipsis(); + AddToColumns(); + } + + /// + /// 创建表格列共享配置 + /// + private void CreateTableColumnShareConfig() { + _shareConfig = new TableColumnShareConfig( GetTableShareConfig() ); + _config.SetValueToItems( _shareConfig ); + } + + /// + /// 获取表格共享配置 + /// + private TableShareConfig GetTableShareConfig() { + return _config.GetValueFromItems() ?? new TableShareConfig(); + } + + /// + /// 加载表达式 + /// + private void LoadExpression() { + var expressionLoader = new TableColumnExpressionLoader(); + expressionLoader.Load( _config ); + } + + /// + /// 启用编辑模式 + /// + private void EnableEdit() { + var result = _config.GetValue( UiConst.IsEdit ); + if ( result ) + _shareConfig.EnableEdit(); + } + + /// + /// 启用自动省略 + /// + private void EnableEllipsis() { + var result = _config.GetValue( UiConst.Ellipsis ); + if ( result ) + _shareConfig.EnableEllipsis(); + } + + /// + /// 将当前列添加到表格列集合中 + /// + private void AddToColumns() { + _shareConfig.AddColumn( GetColumnInfo() ); + } + + /// + /// 获取列信息 + /// + private ColumnInfo GetColumnInfo() { + var result = new ColumnInfo { + Title = GetTitle(), + Column = _config.GetValue( UiConst.Column ), + CellControl = _config.GetValue( UiConst.CellControl ), + Align = _config.GetValue( UiConst.Align )?.Description(), + Width = _config.GetValue( UiConst.Width ), + Ellipsis = _config.GetValue( UiConst.Ellipsis ), + IsSort = _config.GetValue( UiConst.Sort ), + IsFirst = _shareConfig.IsFirst, + IsLeft = _config.GetValue( UiConst.Left ), + IsRight = _config.GetValue( UiConst.Right ), + Acl = _config.GetValue( UiConst.Acl ), + AclElseTemplateId = _config.GetValue( UiConst.AclElseTemplateId ), + IsEnableResizable = _config.GetValue( UiConst.EnableResizable ) + }; + if ( IsInColumnDisplay() ) + result.IsInner = true; + return result; + } + + /// + /// 获取标题 + /// + private string GetTitle() { + var result = _config.GetValue( UiConst.Title ); + if ( result.IsEmpty() == false ) + return result; + return GetOperationTitle(); + } + + /// + /// 获取Operation标题 + /// + private string GetOperationTitle() { + var value = _config.GetValue( UiConst.TitleOperation ); + if ( value != true ) + return null; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) + return "util.operation"; + return "Operation"; + } + + /// + /// 是否包含在显示列中 + /// + private bool IsInColumnDisplay() { + var displayShareConfig = _config.GetValueFromItems(); + return displayShareConfig != null; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableHeadAutoCreateService.cs b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableHeadAutoCreateService.cs new file mode 100644 index 000000000..733993bd2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableHeadAutoCreateService.cs @@ -0,0 +1,68 @@ +using Util.Ui.Configs; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.NgZorro.Components.Tables.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Helpers; + +/// +/// 表格头嵌套结构自动创建服务 +/// +public class TableHeadAutoCreateService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格共享配置 + /// + private readonly TableShareConfig _shareConfig; + /// + /// 表格头标签生成器 + /// + private readonly TableHeadBuilder _headBuilder; + + /// + /// 初始化表格头嵌套结构自动创建服务 + /// + /// 表格头标签生成器 + public TableHeadAutoCreateService( TableHeadBuilder headBuilder ) { + _headBuilder = headBuilder ?? throw new ArgumentNullException( nameof( headBuilder ) ); + _config = _headBuilder.GetConfig(); + _shareConfig = _headBuilder.GetTableShareConfig(); + } + + /// + /// 自动创建嵌套结构 + /// + public void Init() { + CancelAutoCreate(); + AddHeadRow(); + } + + /// + /// 取消自动创建 + /// + private void CancelAutoCreate() { + var result = _config.GetValueFromAttributes( UiConst.EnableAutoCreate ); + if ( result == false ) { + _shareConfig.IsAutoCreateHeadRow = false; + _shareConfig.IsAutoCreateHeadColumn = false; + } + } + + /// + /// 添加表头行 + /// + private void AddHeadRow() { + if ( _shareConfig.Columns.Count == 0 && _config.Content.IsEmpty() ) + return; + if ( _shareConfig.IsAutoCreateHeadRow == false ) + return; + _shareConfig.HeadRowAutoCreated = true; + var rowBuilder = _headBuilder.CreateTableHeadRowBuilder(); + _headBuilder.AppendContent( rowBuilder ); + rowBuilder.ConfigAutoCreate(); + rowBuilder.ConfigContent(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableHeadColumnExpressionLoader.cs b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableHeadColumnExpressionLoader.cs new file mode 100644 index 000000000..7129113c2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableHeadColumnExpressionLoader.cs @@ -0,0 +1,33 @@ +using Util.Ui.Expressions; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Helpers; + +/// +/// 表头单元格表达式加载器 +/// +public class TableHeadColumnExpressionLoader : ExpressionLoader { + /// + /// 加载模型信息 + /// + /// 配置 + /// 模型表达式信息 + protected override void Load( Config config, ModelExpressionInfo info ) { + base.Load( config, info ); + LoadSort( config, info ); + } + + /// + /// 加载排序 + /// + protected virtual void LoadSort( Config config, ModelExpressionInfo info ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableTableSort == false ) + return; + var shareConfig = config.GetValueFromItems(); + if ( shareConfig == null || shareConfig.IsTreeTable ) + return; + config.SetAttribute( UiConst.Sort, info.PropertyName, false ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableHeadColumnService.cs b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableHeadColumnService.cs new file mode 100644 index 000000000..936aaaac1 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableHeadColumnService.cs @@ -0,0 +1,107 @@ +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Tables.Helpers; + +/// +/// 表头单元格服务 +/// +public class TableHeadColumnService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格共享配置 + /// + private TableHeadColumnShareConfig _shareConfig; + + /// + /// 初始化表头单元格服务 + /// + /// 配置 + public TableHeadColumnService( Config config ) { + _config = config; + } + + /// + /// 初始化 + /// + public void Init() { + CreateTableHeadColumnShareConfig(); + LoadExpression(); + CancelAutoCreateHeadColumn(); + SetColumn(); + } + + /// + /// 创建表头列共享配置 + /// + private void CreateTableHeadColumnShareConfig() { + _shareConfig = new TableHeadColumnShareConfig( GetTableShareConfig() ); + _config.SetValueToItems( _shareConfig ); + } + + /// + /// 获取表格共享配置 + /// + private TableShareConfig GetTableShareConfig() { + return _config.GetValueFromItems() ?? new TableShareConfig(); + } + + /// + /// 加载表达式 + /// + private void LoadExpression() { + var expressionLoader = new TableHeadColumnExpressionLoader(); + expressionLoader.Load( _config ); + } + + /// + /// 取消自动创建表头单元格 + /// + private void CancelAutoCreateHeadColumn() { + _shareConfig.IsAutoCreateHeadColumn = false; + } + + /// + /// 设置表头列 + /// + public void SetColumn() { + _shareConfig.AddColumn( new HeadColumnInfo { + Title = GetTitle(), + Width = _config.GetValue( UiConst.Width ), + Align = _config.GetValue( UiConst.Align )?.Description(), + TitleAlign = _config.GetValue( UiConst.TitleAlign )?.Description(), + Ellipsis = _config.GetValue( UiConst.Ellipsis ), + IsLeft = _config.GetValue( UiConst.Left ), + IsRight = _config.GetValue( UiConst.Right ), + CellControl = _config.GetValue( UiConst.CellControl ), + IsEnableResizable = _config.GetValue( UiConst.EnableResizable ) + } ); + } + + /// + /// 获取标题 + /// + private string GetTitle() { + var result = _config.GetValue( UiConst.Title ); + if ( result.IsEmpty() == false ) + return result; + return GetOperationTitle(); + } + + /// + /// 获取Operation标题 + /// + private string GetOperationTitle() { + var value = _config.GetValue( UiConst.TitleOperation ); + if ( value != true ) + return null; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) + return "util.operation"; + return "Operation"; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableHeadRowAutoCreateService.cs b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableHeadRowAutoCreateService.cs new file mode 100644 index 000000000..8608aa88e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableHeadRowAutoCreateService.cs @@ -0,0 +1,67 @@ +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.NgZorro.Components.Tables.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Helpers; + +/// +/// 表头行嵌套结构自动创建服务 +/// +public class TableHeadRowAutoCreateService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格共享配置 + /// + private readonly TableShareConfig _shareConfig; + /// + /// 表格行标签生成器 + /// + private readonly TableHeadRowBuilder _rowBuilder; + + /// + /// 初始化表头行嵌套结构自动创建服务 + /// + /// 表格行标签生成器 + public TableHeadRowAutoCreateService( TableHeadRowBuilder rowBuilder ) { + _rowBuilder = rowBuilder ?? throw new ArgumentNullException( nameof( rowBuilder ) ); + _config = _rowBuilder.GetConfig(); + _shareConfig = _rowBuilder.TableShareConfig; + } + + /// + /// 自动创建嵌套结构 + /// + public void Init() { + CancelAutoCreate(); + AddHeadColumns(); + } + + /// + /// 取消自动创建 + /// + private void CancelAutoCreate() { + var result = _config.GetValueFromAttributes( UiConst.EnableAutoCreate ); + if ( result == false ) { + _shareConfig.IsAutoCreateHeadColumn = false; + } + } + + /// + /// 添加表头单元格 + /// + private void AddHeadColumns() { + if ( _shareConfig.Columns.Count == 0 ) + return; + if ( _shareConfig.IsAutoCreateHeadColumn == false ) + return; + _shareConfig.HeadColumnAutoCreated = true; + foreach ( var column in _shareConfig.Columns ) { + var columnBuilder = _rowBuilder.CreateTableHeadColumnBuilder(); + columnBuilder.AddColumn( column ); + _rowBuilder.AppendContent( columnBuilder ); + } + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableHeadService.cs b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableHeadService.cs new file mode 100644 index 000000000..7a3b9c381 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableHeadService.cs @@ -0,0 +1,61 @@ +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Helpers; + +/// +/// 表格头服务 +/// +public class TableHeadService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格头共享配置 + /// + private TableHeadShareConfig _shareConfig; + /// + /// 表格共享配置 + /// + private readonly TableShareConfig _tableShareConfig; + + /// + /// 初始化表格头服务 + /// + /// 配置 + public TableHeadService( Config config ) { + _config = config; + _tableShareConfig = GetTableShareConfig(); + } + + /// + /// 获取表格共享配置 + /// + private TableShareConfig GetTableShareConfig() { + return _config.GetValueFromItems() ?? new TableShareConfig(); + } + + /// + /// 初始化 + /// + public void Init() { + CreateTableHeadShareConfig(); + CancelAutoCreateHead(); + } + + /// + /// 创建表格头共享配置 + /// + private void CreateTableHeadShareConfig() { + _shareConfig = new TableHeadShareConfig(); + _config.SetValueToItems( _shareConfig ); + } + + /// + /// 取消自动创建表头 + /// + private void CancelAutoCreateHead() { + _tableShareConfig.IsAutoCreateHead = false; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableRowService.cs b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableRowService.cs new file mode 100644 index 000000000..3262d0d22 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableRowService.cs @@ -0,0 +1,97 @@ +using Util.Ui.NgZorro.Components.Tables.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Helpers; + +/// +/// 表格行服务 +/// +public class TableRowService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格共享配置 + /// + private readonly TableShareConfig _shareConfig; + + /// + /// 初始化表格行服务 + /// + /// 配置 + public TableRowService( Config config ) { + _config = config; + _shareConfig = GetTableShareConfig(); + } + + /// + /// 获取表格共享配置 + /// + private TableShareConfig GetTableShareConfig() { + return _config.GetValueFromItems() ?? new TableShareConfig(); + } + + /// + /// 是否表头行 + /// + public bool IsHeadRow => GetTableHeadShareConfig() != null; + + /// + /// 获取表格头共享配置 + /// + private TableHeadShareConfig GetTableHeadShareConfig() { + return _config.GetValueFromItems(); + } + + /// + /// 获取表格主体共享配置 + /// + private TableBodyShareConfig GetTableBodyShareConfig() { + return _config.GetValueFromItems(); + } + + /// + /// 初始化 + /// + public void Init() { + ConfigRowId(); + CancelAutoCreateHeadRow(); + CancelAutoCreateBodyRow(); + ConfigHeadRowNumber(); + } + + /// + /// 配置表格主体行标识 + /// + private void ConfigRowId() { + if ( IsHeadRow ) + return; + _shareConfig.SetRowId( _config.GetValue( UiConst.Id ) ); + } + + /// + /// 取消自动创建表头行 + /// + private void CancelAutoCreateHeadRow() { + if ( IsHeadRow ) + _shareConfig.IsAutoCreateHeadRow = false; + } + + /// + /// 取消自动创建表格主体行 + /// + private void CancelAutoCreateBodyRow() { + if ( IsHeadRow ) + return; + _shareConfig.IsAutoCreateBodyRow = false; + } + + /// + /// 配置表头行数 + /// + private void ConfigHeadRowNumber() { + if ( IsHeadRow == false ) + return; + _shareConfig.HeadRowNumber++; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableService.cs b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableService.cs new file mode 100644 index 000000000..82ec7da7e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Helpers/TableService.cs @@ -0,0 +1,230 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Tables.Configs; + +namespace Util.Ui.NgZorro.Components.Tables.Helpers; + +/// +/// 表格服务 +/// +public class TableService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格共享配置 + /// + private TableShareConfig _shareConfig; + + /// + /// 初始化表格服务 + /// + /// 配置 + public TableService( Config config ) { + _config = config; + } + + /// + /// 获取共享配置 + /// + public TableShareConfig GetShareConfig() { + return _shareConfig; + } + + /// + /// 初始化树形表格标识 + /// + public void InitTreeTable() { + _shareConfig.IsTreeTable = true; + } + + /// + /// 初始化 + /// + public void Init() { + CreateShareConfig(); + SetTableSettings(); + SetScroll(); + SetIsShowCheckbox(); + SetIsShowRadio(); + SetIsCheckLeafOnly(); + SetIsShowLineNumber(); + SetOnClickRow(); + EnableExtend(); + } + + /// + /// 创建共享配置 + /// + private void CreateShareConfig() { + _shareConfig = new TableShareConfig( GetTableId() ); + _config.SetValueToItems( _shareConfig ); + } + + /// + /// 获取表格标识 + /// + private string GetTableId() { + return _config.GetValue( UiConst.Id ); + } + + /// + /// 设置表格设置参数 + /// + private void SetTableSettings() { + _shareConfig.Key = GetKey(); + if ( _config.GetValue( UiConst.EnableTableSettings ) ) { + _shareConfig.IsEnableCustomColumn = true; + _shareConfig.IsEnableResizable = true; + _shareConfig.IsEnableFixedColumn = true; + return; + } + _shareConfig.IsEnableCustomColumn = _config.GetValue( UiConst.EnableCustomColumn ); + _shareConfig.IsEnableResizable = _config.GetValue( UiConst.EnableResizable ); + _shareConfig.IsEnableFixedColumn = _config.GetValue( UiConst.EnableFixedColumn ); + } + + /// + /// 获取表格参数存储标识 + /// + private string GetKey() { + var result = _config.GetValue( UiConst.Key ); + return result.IsEmpty() ? _config.GetValue( UiConst.Id ) : result; + } + + /// + /// 设置表格滚动参数 + /// + private void SetScroll() { + _shareConfig.ScrollWidth = _config.GetValue( UiConst.ScrollWidth ); + _shareConfig.ScrollHeight = _config.GetValue( UiConst.ScrollHeight ); + } + + /// + /// 设置是否显示复选框 + /// + private void SetIsShowCheckbox() { + _shareConfig.IsShowCheckbox = _config.GetValue( UiConst.ShowCheckbox ); + _shareConfig.IsCheckboxLeft = _config.GetValue( UiConst.CheckboxLeft ); + } + + /// + /// 设置是否显示单选框 + /// + private void SetIsShowRadio() { + _shareConfig.IsShowRadio = _config.GetValue( UiConst.ShowRadio ); + _shareConfig.IsRadioLeft = _config.GetValue( UiConst.RadioLeft ); + } + + /// + /// 设置是否仅能选择单选框叶节点 + /// + private void SetIsCheckLeafOnly() { + _shareConfig.IsCheckLeafOnly = _config.GetValue( UiConst.CheckLeafOnly ); + } + + /// + /// 设置是否显示序号 + /// + private void SetIsShowLineNumber() { + _shareConfig.IsShowLineNumber = _config.GetValue( UiConst.ShowLineNumber ); + _shareConfig.IsLineNumberLeft = _config.GetValue( UiConst.LineNumberLeft ); + } + + /// + /// 设置单击行事件 + /// + private void SetOnClickRow() { + _shareConfig.OnClickRow = _config.GetValue( UiConst.OnClickRow ); + _shareConfig.SelectOnClickRow = _config.GetValue( UiConst.SelectOnClickRow ); + _shareConfig.SelectOnlyOnClickRow = _config.GetValue( UiConst.SelectOnlyOnClickRow ); + _shareConfig.CheckOnClickRow = _config.GetValue( UiConst.CheckOnClickRow ); + } + + /// + /// 启用基础扩展 + /// + private void EnableExtend() { + if ( GetEnableExtend() == false ) { + _shareConfig.IsEnableExtend = false; + return; + } + if ( GetEnableExtend() == true || + GetUrl().IsEmpty() == false || + GetBindUrl().IsEmpty() == false || + GetLoadUrl().IsEmpty() == false || + GetBindLoadUrl().IsEmpty() == false || + GetQueryUrl().IsEmpty() == false || + GetBindQueryUrl().IsEmpty() == false || + GetLoadChildrenUrl().IsEmpty() == false || + GetBindLoadChildrenUrl().IsEmpty() == false || + _shareConfig.IsShowCheckbox || + _shareConfig.IsShowRadio || + _shareConfig.IsShowLineNumber ) { + _shareConfig.IsEnableExtend = true; + } + } + + /// + /// 获取启用扩展属性 + /// + private bool? GetEnableExtend() { + return _config.GetValue( UiConst.EnableExtend ); + } + + /// + /// 获取地址 + /// + private string GetUrl() { + return _config.GetValue( UiConst.Url ); + } + + /// + /// 获取地址 + /// + private string GetBindUrl() { + return _config.GetValue( AngularConst.BindUrl ); + } + + /// + /// 获取加载地址 + /// + private string GetLoadUrl() { + return _config.GetValue( UiConst.LoadUrl ); + } + + /// + /// 获取加载地址 + /// + private string GetBindLoadUrl() { + return _config.GetValue( AngularConst.BindLoadUrl ); + } + + /// + /// 获取查询地址 + /// + private string GetQueryUrl() { + return _config.GetValue( UiConst.QueryUrl ); + } + + /// + /// 获取查询地址 + /// + private string GetBindQueryUrl() { + return _config.GetValue( AngularConst.BindQueryUrl ); + } + + /// + /// 获取加载下级节点地址 + /// + private string GetLoadChildrenUrl() { + return _config.GetValue( UiConst.LoadChildrenUrl ); + } + + /// + /// 获取加载下级节点地址 + /// + private string GetBindLoadChildrenUrl() { + return _config.GetValue( AngularConst.BindLoadChildrenUrl ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Renders/FilterTriggerRender.cs b/src/Util.Ui.NgZorro/Components/Tables/Renders/FilterTriggerRender.cs new file mode 100644 index 000000000..a6eca9ada --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Renders/FilterTriggerRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tables.Renders; + +/// +/// 表头单元格过滤器触发按钮渲染器 +/// +public class FilterTriggerRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化表头单元格过滤器触发按钮渲染器 + /// + /// 配置 + public FilterTriggerRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new FilterTriggerBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new FilterTriggerRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Renders/TableBodyRender.cs b/src/Util.Ui.NgZorro/Components/Tables/Renders/TableBodyRender.cs new file mode 100644 index 000000000..34a1be5db --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Renders/TableBodyRender.cs @@ -0,0 +1,39 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tables.Renders; + +/// +/// 表格主体渲染器 +/// +public class TableBodyRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化表格主体渲染器 + /// + /// 配置 + public TableBodyRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TableBodyBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TableBodyRender( _config.CopyRemoveAttributes() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Renders/TableColumnControlRender.cs b/src/Util.Ui.NgZorro/Components/Tables/Renders/TableColumnControlRender.cs new file mode 100644 index 000000000..714af91a1 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Renders/TableColumnControlRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tables.Renders; + +/// +/// 表格编辑列控件区域渲染器 +/// +public class TableColumnControlRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化表格编辑列控件区域渲染器 + /// + /// 配置 + public TableColumnControlRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TableColumnControlBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TableColumnControlRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Renders/TableColumnDisplayRender.cs b/src/Util.Ui.NgZorro/Components/Tables/Renders/TableColumnDisplayRender.cs new file mode 100644 index 000000000..ae6de7b89 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Renders/TableColumnDisplayRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tables.Renders; + +/// +/// 表格编辑列显示区域渲染器 +/// +public class TableColumnDisplayRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化表格编辑列显示区域渲染器 + /// + /// 配置 + public TableColumnDisplayRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TableColumnDisplayBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TableColumnDisplayRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Renders/TableColumnRender.cs b/src/Util.Ui.NgZorro/Components/Tables/Renders/TableColumnRender.cs new file mode 100644 index 000000000..3d8db977f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Renders/TableColumnRender.cs @@ -0,0 +1,68 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Components.TreeTables.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tables.Renders; + +/// +/// 表格单元格渲染器 +/// +public class TableColumnRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格列共享配置 + /// + private TableColumnShareConfig _shareConfig; + + /// + /// 初始化表格单元格渲染器 + /// + /// 配置 + /// 共享配置 + public TableColumnRender( Config config, TableColumnShareConfig shareConfig = null ) { + _config = config; + _shareConfig = shareConfig ?? GetTableColumnShareConfig(); + } + + /// + /// 获取表格列共享配置 + /// + protected TableColumnShareConfig GetTableColumnShareConfig() { + return _shareConfig ??= _config.GetValueFromItems() ?? new TableColumnShareConfig( GetTableShareConfig() ); + } + + /// + /// 获取表格共享配置 + /// + private TableShareConfig GetTableShareConfig() { + return _config.GetValueFromItems() ?? new TableShareConfig(); + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = CreateTableColumnBuilder(); + builder.Config(); + return builder; + } + + /// + /// 创建表格单元格标签生成器 + /// + private TableColumnBuilder CreateTableColumnBuilder() { + if( _shareConfig.IsTreeTable ) + return new TreeTableColumnBuilder( _config, _shareConfig ); + return new TableColumnBuilder( _config, _shareConfig ); + } + + /// + public override IHtmlContent Clone() { + return new TableColumnRender( _config.Copy(), new TableColumnShareConfig( GetTableShareConfig() ) ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Renders/TableHeadColumnRender.cs b/src/Util.Ui.NgZorro/Components/Tables/Renders/TableHeadColumnRender.cs new file mode 100644 index 000000000..e80a47a09 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Renders/TableHeadColumnRender.cs @@ -0,0 +1,69 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Components.TreeTables.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tables.Renders; + +/// +/// 表头单元格渲染器 +/// +public class TableHeadColumnRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表头列共享配置 + /// + private readonly TableHeadColumnShareConfig _shareConfig; + + /// + /// 初始化表头单元格渲染器 + /// + /// 配置 + /// 共享配置 + public TableHeadColumnRender( Config config, TableHeadColumnShareConfig shareConfig = null ) { + _config = config; + _shareConfig = shareConfig ?? GetTableHeadColumnShareConfig(); + } + + /// + /// 获取表头列共享配置 + /// + protected TableHeadColumnShareConfig GetTableHeadColumnShareConfig() { + return _config.GetValueFromItems() ?? new TableHeadColumnShareConfig( GetTableShareConfig() ); + } + + /// + /// 获取表格共享配置 + /// + private TableShareConfig GetTableShareConfig() { + return _config.GetValueFromItems() ?? new TableShareConfig(); + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = CreateTableHeadColumnBuilder(); + builder.Config(); + return builder; + } + + /// + /// 创建表头单元格标签生成器 + /// + private TableHeadColumnBuilder CreateTableHeadColumnBuilder() { + if ( _shareConfig.IsTreeTable ) + return new TreeTableHeadColumnBuilder( _config, _shareConfig ); + return new TableHeadColumnBuilder( _config, _shareConfig ); + } + + /// + public override IHtmlContent Clone() { + return new TableHeadColumnRender( _config.Copy(), new TableHeadColumnShareConfig( GetTableShareConfig() ) ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Renders/TableHeadRender.cs b/src/Util.Ui.NgZorro/Components/Tables/Renders/TableHeadRender.cs new file mode 100644 index 000000000..cd722c38e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Renders/TableHeadRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tables.Renders; + +/// +/// 表头渲染器 +/// +public class TableHeadRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化表头渲染器 + /// + /// 配置 + public TableHeadRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TableHeadBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TableHeadRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Renders/TableRender.cs b/src/Util.Ui.NgZorro/Components/Tables/Renders/TableRender.cs new file mode 100644 index 000000000..fc9a3c036 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Renders/TableRender.cs @@ -0,0 +1,82 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tables.Renders; + +/// +/// 表格渲染器 +/// +public class TableRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格共享配置 + /// + private TableShareConfig _shareConfig; + + /// + /// 初始化表格渲染器 + /// + /// 配置 + /// 共享配置 + public TableRender( Config config, TableShareConfig shareConfig = null ) { + _config = config; + _shareConfig = shareConfig ?? GetShareConfig(); + } + + /// + /// 获取表格共享配置 + /// + private TableShareConfig GetShareConfig() { + return _shareConfig ??= _config.GetValueFromItems() ?? new TableShareConfig(); + } + + /// + public override void WriteTo( TextWriter writer, HtmlEncoder encoder ) { + base.WriteTo( writer, encoder ); + RenderTotalTemplate( writer, encoder ); + RenderTableSettings( writer, encoder ); + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TableBuilder( _config ); + builder.Config(); + return builder; + } + + /// + /// 渲染表格总行数模板 + /// + protected void RenderTotalTemplate( TextWriter writer, HtmlEncoder encoder ) { + if ( _shareConfig.IsEnableExtend == false ) + return; + var builder = new TotalTemplateBuilder( _config ); + builder.Config(); + builder.WriteTo( writer, encoder ); + } + + /// + /// 渲染表格设置组件 + /// + protected void RenderTableSettings( TextWriter writer, HtmlEncoder encoder ) { + if ( _shareConfig.IsEnableTableSettings == false ) + return; + var builder = new TableSettingsBuilder( _config ); + builder.Config(); + builder.WriteTo( writer, encoder ); + } + + /// + public override IHtmlContent Clone() { + return new TableRender( _config.CopyRemoveAttributes(), _shareConfig.MapTo() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/Renders/TableRowRender.cs b/src/Util.Ui.NgZorro/Components/Tables/Renders/TableRowRender.cs new file mode 100644 index 000000000..b8fc5288a --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/Renders/TableRowRender.cs @@ -0,0 +1,79 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Components.TreeTables.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tables.Renders; + +/// +/// 表格行渲染器 +/// +public class TableRowRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化表格行渲染器 + /// + /// 配置 + public TableRowRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = GetTableRowBuilder(); + builder.Config(); + return builder; + } + + /// + /// 获取表格行标签生成器 + /// + private TableRowBuilder GetTableRowBuilder() { + if ( IsHeadRow ) { + if ( IsTreeTable ) + return new TreeTableHeadRowBuilder( _config ); + return new TableHeadRowBuilder( _config ); + } + if ( IsTreeTable ) + return new TreeTableBodyRowBuilder( _config ); + return new TableBodyRowBuilder( _config ); + } + + /// + /// 是否表头行 + /// + private bool IsHeadRow => GetTableHeadShareConfig() != null; + + /// + /// 是否树形表格 + /// + private bool IsTreeTable => GetTableShareConfig().IsTreeTable; + + /// + /// 获取表格头共享配置 + /// + private TableHeadShareConfig GetTableHeadShareConfig() { + return _config.GetValueFromItems(); + } + + /// + /// 获取表格共享配置 + /// + private TableShareConfig GetTableShareConfig() { + return _config.GetValueFromItems() ?? new TableShareConfig(); + } + + /// + public override IHtmlContent Clone() { + return new TableRowRender( _config.CopyRemoveAttributes() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/TableBodyTagHelper.cs b/src/Util.Ui.NgZorro/Components/Tables/TableBodyTagHelper.cs new file mode 100644 index 000000000..af7f0e446 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/TableBodyTagHelper.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Tables.Helpers; +using Util.Ui.NgZorro.Components.Tables.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tables; + +/// +/// 表格主体,生成的标签为<tbody></tbody> +/// +[HtmlTargetElement( "util-tbody" )] +public class TableBodyTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 扩展属性, 是否启用自动创建嵌套结构,默认为 true + /// + public bool EnableAutoCreate { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new TableBodyService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new TableBodyRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/TableColumnControlTagHelper.cs b/src/Util.Ui.NgZorro/Components/Tables/TableColumnControlTagHelper.cs new file mode 100644 index 000000000..47cb7e763 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/TableColumnControlTagHelper.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Tables.Helpers; +using Util.Ui.NgZorro.Components.Tables.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tables; + +/// +/// 表格编辑列控件区域,生成的标签为<ng-template></ng-template> +/// +[HtmlTargetElement( "util-td-control", ParentTag = "util-td" )] +public class TableColumnControlTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new TableColumnControlService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new TableColumnControlRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/TableColumnDisplayTagHelper.cs b/src/Util.Ui.NgZorro/Components/Tables/TableColumnDisplayTagHelper.cs new file mode 100644 index 000000000..4826d3ff3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/TableColumnDisplayTagHelper.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Tables.Helpers; +using Util.Ui.NgZorro.Components.Tables.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tables; + +/// +/// 表格编辑列显示区域,生成的标签为<ng-container></ng-container> +/// +[HtmlTargetElement( "util-td-display", ParentTag = "util-td" )] +public class TableColumnDisplayTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new TableColumnDisplayService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new TableColumnDisplayRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/TableColumnTagHelper.cs b/src/Util.Ui.NgZorro/Components/Tables/TableColumnTagHelper.cs new file mode 100644 index 000000000..5f9353e52 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/TableColumnTagHelper.cs @@ -0,0 +1,176 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Tables.Helpers; +using Util.Ui.NgZorro.Components.Tables.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tables; + +/// +/// 表格单元格,生成的标签为<td></td> +/// +[HtmlTargetElement( "util-td" )] +public class TableColumnTagHelper : TooltipTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 扩展属性, 属性表达式 + /// + public ModelExpression For { get; set; } + /// + /// 扩展属性,是否启用排序 + /// + public bool Sort { get; set; } + /// + /// 标题,扩展属性,支持i18n + /// + public string Title { get; set; } + /// + /// operation标题,扩展属性,是否显示operation标题文本,默认文本为'Operation',i18n文本为'util.operation' + /// + public bool TitleOperation { get; set; } + /// + /// 列名,扩展属性 + /// + public string Column { get; set; } + /// + /// 表格列类型,扩展属性 + /// + public TableColumnType Type { get; set; } + /// + /// 扩展属性,日期格式化字符串,默认值: yyyy-MM-dd HH:mm,当类型为日期时有效,格式说明: + /// 1. 年 - yyyy + /// 2. 月 - MM + /// 3. 日 - dd + /// 4. 时 - HH + /// 5. 分 - mm + /// 6. 秒 - ss + /// 7. 毫秒 - SSS + /// + public string DateFormat { get; set; } + /// + /// 扩展属性,是否仅显示日期,当类型为日期时有效 + /// + public bool ShowDateOnly { get; set; } + /// + /// 是否编辑列,扩展属性,默认值:false + /// + public bool IsEdit { get; set; } + /// + /// 扩展属性,列宽度,表头未分组时可用,设置数值,则默认单位为px,范例:100,表示100px,也可以使用百分比,范例: 20% + /// + public string Width { get; set; } + /// + /// [nzShowCheckbox],是否显示复选框,注意:此属性为ng-zorro原生属性,未提供复选框联动功能,请设置表格上的show-checkbox属性 + /// + public string ShowCheckbox { get; set; } + /// + /// [nzDisabled],是否禁用复选框 + /// + public string Disabled { get; set; } + /// + /// [nzIndeterminate],复选框是否中间状态 + /// + public string Indeterminate { get; set; } + /// + /// [nzChecked],是否选中复选框 + /// + public string Checked { get; set; } + /// + /// [(nzChecked)],是否选中复选框 + /// + public string BindonChecked { get; set; } + /// + /// [nzShowExpand],是否显示展开按钮 + /// + public string ShowExpand { get; set; } + /// + /// [nzExpand],是否已展开 + /// + public string Expand { get; set; } + /// + /// [(nzExpand)],是否已展开 + /// + public string BindonExpand { get; set; } + /// + /// nzLeft,左侧距离,用于固定左侧列,当为 true 时自动计算,为 false 时停止固定,类型: string | boolean + /// + public string Left { get; set; } + /// + /// [nzLeft],左侧距离,用于固定左侧列,当为 true 时自动计算,为 false 时停止固定,类型: string | boolean + /// + public string BindLeft { get; set; } + /// + /// nzRight,右侧距离,用于固定右侧列,当为 true 时自动计算,为 false 时停止固定,类型: string | boolean + /// + public string Right { get; set; } + /// + /// [nzRight],右侧距离,用于固定右侧列,当为 true 时自动计算,为 false 时停止固定,类型: string | boolean + /// + public string BindRight { get; set; } + /// + /// nzAlign,设置列内容的对齐方式,可选值: 'left' | 'right' | 'center' + /// + public TableHeadColumnAlign Align { get; set; } + /// + /// [nzAlign],设置列内容的对齐方式,可选值: 'left' | 'right' | 'center' + /// + public string BindAlign { get; set; } + /// + /// [nzBreakWord],是否折行显示,默认值: false + /// + public string BreakWord { get; set; } + /// + /// [nzEllipsis],超过宽度将自动省略,暂不支持和排序筛选一起使用,仅当表格布局为 nzTableLayout="fixed" 时可用,默认值: false + /// + public string Ellipsis { get; set; } + /// + /// [nzIndentSize],展示树形数据时,每层缩进的宽度,单位: px + /// + public string IndentSize { get; set; } + /// + /// nzCellControl,设置列的位置,该值为 NzCustomColumn 类型中 value 字段的值 + /// + public string CellControl { get; set; } + /// + /// [nzCellControl],设置列的位置,该值为 NzCustomColumn 类型中 value 字段的值 + /// + public string BindCellControl { get; set; } + /// + /// 扩展属性,是否启用拖动调整列宽 + /// + public bool EnableResizable { get; set; } + /// + /// 扩展属性 [cdkCopyToClipboard],是否允许复制到剪贴板,注意: 需要设置 column 属性 + /// + public bool Clipboard { get; set; } + /// + /// (nzCheckedChange),复选框选中状态变化事件,类型: EventEmitter<boolean> + /// + public string OnCheckedChange { get; set; } + /// + /// (nzExpandChange),展开状态变化事件,类型: EventEmitter<boolean> + /// + public string OnExpandChange { get; set; } + /// + /// (click),单击事件 + /// + public string OnClick { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new TableColumnService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new TableColumnRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/TableHeadColumnTagHelper.cs b/src/Util.Ui.NgZorro/Components/Tables/TableHeadColumnTagHelper.cs new file mode 100644 index 000000000..fb3dd6ed7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/TableHeadColumnTagHelper.cs @@ -0,0 +1,205 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Tables.Helpers; +using Util.Ui.NgZorro.Components.Tables.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tables; + +/// +/// 表头单元格,生成的标签为<th></th> +/// +[HtmlTargetElement( "util-th" )] +public class TableHeadColumnTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 属性表达式 + /// + public ModelExpression For { get; set; } + /// + /// 扩展属性,设置排序属性 + /// + public string Sort { get; set; } + /// + /// 标题,扩展属性,支持i18n + /// + public string Title { get; set; } + /// + /// operation标题,扩展属性,是否显示operation标题文本,默认文本为'Operation',i18n文本为'util.operation' + /// + public bool TitleOperation { get; set; } + /// + /// [nzShowCheckbox],是否显示复选框 + /// + public string ShowCheckbox { get; set; } + /// + /// [nzDisabled],是否禁用复选框 + /// + public string Disabled { get; set; } + /// + /// [nzIndeterminate],复选框是否中间状态 + /// + public string Indeterminate { get; set; } + /// + /// [nzChecked],是否选中复选框 + /// + public string Checked { get; set; } + /// + /// [(nzChecked)],是否选中复选框 + /// + public string BindonChecked { get; set; } + /// + /// [nzShowRowSelection],是否显示下拉选择 + /// + public string ShowRowSelection { get; set; } + /// + /// [nzSelections],下拉选择的内容,类型: Array<{ text: string, onSelect: any }> + /// + public string Selections { get; set; } + /// + /// [nzShowSort],是否显示排序 + /// + public string ShowSort { get; set; } + /// + /// [nzSortFn],排序函数,前端排序传入函数,服务端排序传入 true,函数定义: (a: any, b: any, sortOrder?: string) => number,说明:参考 Array.sort 的 compareFunction + /// + public string SortFn { get; set; } + /// + /// [nzSortDirections],支持的排序方式,取值为 'ascend', 'descend', null,类型: Array<'ascend' | 'descend' | null>,默认值: ['ascend', 'descend', null] + /// + public string SortDirections { get; set; } + /// + /// nzSortOrder,当前排序方向,可选值: 'ascend' | 'descend',默认值: 'ascend' + /// + public string SortOrder { get; set; } + /// + /// [nzSortOrder],当前排序方向,可选值: 'ascend' | 'descend',默认值: 'ascend' + /// + public string BindSortOrder { get; set; } + /// + /// [(nzSortOrder)],当前排序方向,可选值: 'ascend' | 'descend',默认值: 'ascend' + /// + public string BindonSortOrder { get; set; } + /// + /// [nzShowFilter],是否显示过滤 + /// + public string ShowFilter { get; set; } + /// + /// [nzFilterFn],过滤函数,前端排序过滤传入函数,服务端排序传入 true,函数定义: (value: any, data: any) => boolean + /// + public string FilterFn { get; set; } + /// + /// [nzFilters],过滤器内容,类型: Array<{ text: string; value: any; byDefault?: boolean }> + /// + public string Filters { get; set; } + /// + /// [nzFilterMultiple],是否多选过滤器,默认值: true + /// + public string FilterMultiple { get; set; } + /// + /// nzWidth,列宽度,表头未分组时可用,设置数值,则默认单位为px,范例:100,表示100px,也可以使用百分比,范例: 20% + /// + public string Width { get; set; } + /// + /// [nzWidth],列宽度,表头未分组时可用,可以使用像素值,也可以使用百分比 + /// + public string BindWidth { get; set; } + /// + /// nzLeft,左侧距离,用于固定左侧列,当为 true 时自动计算,为 false 时停止固定,类型: string | boolean + /// + public string Left { get; set; } + /// + /// [nzLeft],左侧距离,用于固定左侧列,当为 true 时自动计算,为 false 时停止固定,类型: string | boolean + /// + public string BindLeft { get; set; } + /// + /// nzRight,右侧距离,用于固定右侧列,当为 true 时自动计算,为 false 时停止固定,类型: string | boolean + /// + public string Right { get; set; } + /// + /// [nzRight],右侧距离,用于固定右侧列,当为 true 时自动计算,为 false 时停止固定,类型: string | boolean + /// + public string BindRight { get; set; } + /// + /// nzAlign,设置列内容的对齐方式,可选值: 'left' | 'right' | 'center' + /// + public TableHeadColumnAlign Align { get; set; } + /// + /// [nzAlign],设置列内容的对齐方式,可选值: 'left' | 'right' | 'center' + /// + public string BindAlign { get; set; } + /// + /// titleAlign,扩展属性,设置列标题的对齐方式,可选值: 'left' | 'right' | 'center' + /// + public TableHeadColumnAlign TitleAlign { get; set; } + /// + /// [titleAlign],扩展属性,设置列标题的对齐方式,可选值: 'left' | 'right' | 'center' + /// + public string BindTitleAlign { get; set; } + /// + /// [nzBreakWord],是否折行显示,默认值: false + /// + public string BreakWord { get; set; } + /// + /// [nzEllipsis],超过宽度将自动省略,暂不支持和排序筛选一起使用,仅当表格布局为 nzTableLayout="fixed" 时可用,默认值: false + /// + public string Ellipsis { get; set; } + /// + /// [colSpan],列跨度,每单元格中扩展列的数量 + /// + public string Colspan { get; set; } + /// + /// [rowSpan],行跨度,每单元格中扩展行的数量 + /// + public string Rowspan { get; set; } + /// + /// nzColumnKey,当前列标识,用于服务端筛选和排序使用 + /// + public string ColumnKey { get; set; } + /// + /// [nzColumnKey],当前列标识,用于服务端筛选和排序使用 + /// + public string BindColumnKey { get; set; } + /// + /// nzCellControl,设置列的位置,该值为 NzCustomColumn 类型中 value 字段的值 + /// + public string CellControl { get; set; } + /// + /// [nzCellControl],设置列的位置,该值为 NzCustomColumn 类型中 value 字段的值 + /// + public string BindCellControl { get; set; } + /// + /// 扩展属性,是否启用拖动调整列宽 + /// + public bool EnableResizable { get; set; } + /// + /// (nzCheckedChange),复选框选中状态变化事件,类型: EventEmitter<boolean> + /// + public string OnCheckedChange { get; set; } + /// + /// (nzSortOrderChange),排序方向变化事件,类型: EventEmitter<'descend' | 'ascend' | null> + /// + public string OnSortOrderChange { get; set; } + /// + /// (nzFilterChange),过滤条件变化事件,类型: EventEmitter<any[] | any> + /// + public string OnFilterChange { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new TableHeadColumnService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new TableHeadColumnRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/TableHeadTagHelper.cs b/src/Util.Ui.NgZorro/Components/Tables/TableHeadTagHelper.cs new file mode 100644 index 000000000..896245cd8 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/TableHeadTagHelper.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Tables.Helpers; +using Util.Ui.NgZorro.Components.Tables.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tables; + +/// +/// 表头,生成的标签为<thead></thead> +/// +[HtmlTargetElement( "util-thead" )] +public class TableHeadTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 扩展属性,是否启用自动创建嵌套结构,默认为 true + /// + public bool EnableAutoCreate { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new TableHeadService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new TableHeadRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/TableRowTagHelper.cs b/src/Util.Ui.NgZorro/Components/Tables/TableRowTagHelper.cs new file mode 100644 index 000000000..b46d237d0 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/TableRowTagHelper.cs @@ -0,0 +1,55 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Tables.Helpers; +using Util.Ui.NgZorro.Components.Tables.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tables; + +/// +/// 表格行,生成的标签为<tr></tr> +/// +[HtmlTargetElement( "util-tr" )] +public class TableRowTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 扩展属性,是否启用自动创建嵌套结构,默认为 true + /// + public bool EnableAutoCreate { get; set; } + /// + /// 扩展属性,点击表格行时是否选中该行,可同时选中多行,选中行被添加名为 table-row-selected 的 class类名, 默认值: false + /// + public bool SelectOnClick { get; set; } + /// + /// 扩展属性,点击表格行时是否选中该行,仅选中该行,取消其它选中的行,选中行被添加名为 table-row-selected 的 class类名, 默认值: false + /// + public bool SelectOnlyOnClick { get; set; } + /// + /// 扩展属性, 点击表格行时是否选中复选框或单选框, 默认值: false + /// + public bool CheckOnClick { get; set; } + /// + /// [nzExpand],当前列是否展开,与 td 上的 nzExpand 属性配合使用 + /// + public string Expand { get; set; } + /// + /// (click),行单击事件,使用row访问行对象,范例:on-click="click(row)" + /// + public string OnClick { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new TableRowService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new TableRowRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tables/TableTagHelper.cs b/src/Util.Ui.NgZorro/Components/Tables/TableTagHelper.cs new file mode 100644 index 000000000..bdba4ec02 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tables/TableTagHelper.cs @@ -0,0 +1,336 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Tables.Helpers; +using Util.Ui.NgZorro.Components.Tables.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tables; + +/// +/// 表格,生成的标签为<nz-table></nz-table> +/// +[HtmlTargetElement( "util-table" )] +public class TableTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 扩展属性,表格标识,保存表格设置参数时作为存储键, 必须全局唯一 + /// + public string Key { get; set; } + /// + /// 扩展属性,是否启用自动创建嵌套结构,默认为 true + /// + public bool EnableAutoCreate { get; set; } + /// + /// 扩展属性,是否启用扩展指令,当设置Api地址时自动启用,默认为 false + /// + public bool EnableExtend { get; set; } + /// + /// 扩展属性[(queryParam)],查询参数 + /// + public string QueryParam { get; set; } + /// + /// 扩展属性 url,Api地址 + /// + public string Url { get; set; } + /// + /// 扩展属性 [url],Api地址 + /// + public string BindUrl { get; set; } + /// + /// 扩展属性 deleteUrl,删除地址,注意:由于支持批量删除,所以采用Post提交,如果未设置该属性,则使用Url属性构造删除地址,范例:/api/test/delete + /// + public string DeleteUrl { get; set; } + /// + /// 扩展属性 [deleteUrl],删除地址,注意:由于支持批量删除,所以采用Post提交,如果未设置该属性,则使用Url属性构造删除地址,范例:/api/test/delete + /// + public string BindDeleteUrl { get; set; } + /// + /// 扩展属性 [autoLoad],初始化时是否自动加载数据,默认为true,设置成false则手工加载 + /// + public bool AutoLoad { get; set; } + /// + /// 扩展属性,是否显示复选框,已提供复选框联动功能 + /// + public bool ShowCheckbox { get; set; } + /// + /// 扩展属性,是否显示单选框 + /// + public bool ShowRadio { get; set; } + /// + /// 扩展属性,是否显示序号 + /// + public bool ShowLineNumber { get; set; } + /// + /// 扩展属性,固定左侧复选框列,当设置ShowCheckbox为true有效 + /// + public string CheckboxLeft { get; set; } + /// + /// 扩展属性,固定左侧单选框列,当设置ShowRadio为true有效 + /// + public string RadioLeft { get; set; } + /// + /// 扩展属性,固定左侧序号列,当设置ShowLineNumber为true有效 + /// + public string LineNumberLeft { get; set; } + /// + /// 扩展属性 [checkedKeys],选中的标识列表,用于还原选中的复选框或单选框,可以是单个Id或Id数组,范例:'1' 或 ['1','2'] + /// + public string CheckedKeys { get; set; } + /// + /// 扩展属性 order,排序条件,范例: creationTime desc + /// + public string Sort { get; set; } + /// + /// 扩展属性 [order],排序条件,范例: creationTime desc + /// + public string BindSort { get; set; } + /// + /// 扩展属性,点击表格行时是否选中该行,可同时选中多行,选中行被添加名为 table-row-selected 的 class类名, 默认值: false + /// + public bool SelectOnClickRow { get; set; } + /// + /// 扩展属性,点击表格行时是否选中该行,仅选中该行,取消其它选中的行,选中行被添加名为 table-row-selected 的 class类名, 默认值: false + /// + public bool SelectOnlyOnClickRow { get; set; } + /// + /// 扩展属性, 点击表格行时是否选中复选框或单选框, 默认值: false + /// + public bool CheckOnClickRow { get; set; } + /// + /// [nzData],数据,类型: any[] + /// + public string Data { get; set; } + /// + /// [nzFrontPagination],是否前端分页,默认值: true + /// + public string FrontPagination { get; set; } + /// + /// [nzTotal],数据总行数,当服务端渲染时传入 + /// + public string Total { get; set; } + /// + /// [nzPageIndex],当前页 + /// + public string PageIndex { get; set; } + /// + /// [(nzPageIndex)],当前页 + /// + public string BindonPageIndex { get; set; } + /// + /// [nzPageSize],每页显示行数 + /// + public string PageSize { get; set; } + /// + /// [(nzPageSize)],每页显示行数 + /// + public string BindonPageSize { get; set; } + /// + /// [nzShowPagination],是否显示分页,默认值: true + /// + public string ShowPagination { get; set; } + /// + /// nzPaginationPosition,指定分页显示的位置,可选值: 'top' | 'bottom' | 'both',默认值: 'bottom' + /// + public TablePaginationPosition PaginationPosition { get; set; } + /// + /// [nzPaginationPosition],指定分页显示的位置,可选值: 'top' | 'bottom' | 'both',默认值: 'bottom' + /// + public string BindPaginationPosition { get; set; } + /// + /// nzPaginationType,指定分页显示的尺寸,可选值: 'default' | 'small',默认值: 'default' + /// + public TablePaginationSize PaginationType { get; set; } + /// + /// [nzPaginationType],指定分页显示的尺寸,可选值: 'default' | 'small',默认值: 'default' + /// + public string BindPaginationType { get; set; } + /// + /// [nzBordered],是否显示外边框和列边框,默认值: false + /// + public string Bordered { get; set; } + /// + /// [nzOuterBordered],是否显示外边框,默认值: false + /// + public string OuterBordered { get; set; } + /// + /// [nzWidthConfig],表头分组时指定每列宽度,与 th 的 [nzWidth] 不可混用,类型: string[],默认值: [] + /// + public string WidthConfig { get; set; } + /// + /// nzSize,表格大小,可选值: 'middle' | 'small' | 'default',默认值: 'default' + /// + public TableSize Size { get; set; } + /// + /// [nzSize],表格大小,可选值: 'middle' | 'small' | 'default',默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// [nzLoading],是否加载状态,默认值: false + /// + public string Loading { get; set; } + /// + /// [nzLoadingIndicator],加载指示符,类型: TemplateRef<void> + /// + public string LoadingIndicator { get; set; } + /// + /// [nzLoadingDelay],延迟显示加载效果的时间(防止闪烁),默认值: 0 + /// + public string LoadingDelay { get; set; } + /// + /// [nzScroll],用于支持横向或纵向滚动,设置滚动区域的宽高度,范例: { x: "300px", y: "300px" } + /// + public string Scroll { get; set; } + /// + /// 扩展属性 [nzScroll], 内容区滚动高度,用于支持固定表头,范例:400,表示 [nzScroll]="{ y: '400px' }" + /// + public string ScrollHeight { get; set; } + /// + /// 扩展属性 [nzScroll], 滚动宽度,用于支持固定列,范例:400,表示 [nzScroll]="{ x: '400px' }" + /// + public string ScrollWidth { get; set; } + /// + /// nzTitle,表格标题,类型: string | TemplateRef<void> + /// + public string Title { get; set; } + /// + /// [nzTitle],表格标题,类型: string | TemplateRef<void> + /// + public string BindTitle { get; set; } + /// + /// nzFooter,表格底部,类型: string | TemplateRef<void> + /// + public string Footer { get; set; } + /// + /// [nzFooter],表格底部,类型: string | TemplateRef<void> + /// + public string BindFooter { get; set; } + /// + /// nzNoResult,无数据时显示内容,类型: string | TemplateRef<void> + /// + public string NoResult { get; set; } + /// + /// [nzNoResult],无数据时显示内容,类型: string | TemplateRef<void> + /// + public string BindNoResult { get; set; } + /// + /// [nzPageSizeOptions],每页行数选择列表,类型: number[],默认值: [ 10, 20, 30, 40, 50 ] + /// + public string PageSizeOptions { get; set; } + /// + /// [nzShowQuickJumper],是否显示分页快速跳转,默认值: false + /// + public string ShowQuickJumper { get; set; } + /// + /// [nzShowSizeChanger],是否显示改变分页大小按钮,默认值: false,当启用扩展时自动设置为 true + /// + public string ShowSizeChanger { get; set; } + /// + /// [nzShowTotal],设置显示总行数和当前数据范围的模板,类型: TemplateRef<{ $implicit: number, range: [ number, number ] }> + /// + public string ShowTotal { get; set; } + /// + /// [nzItemRender],自定义页码结构,类型: TemplateRef<{ $implicit: 'page' | 'prev' | 'next', page: number }> + /// + public string ItemRender { get; set; } + /// + /// [nzHideOnSinglePage],只有一页时是否隐藏分页器,默认值: false + /// + public string HideOnSinglePage { get; set; } + /// + /// [nzSimple],是否显示简单分页 + /// + public string Simple { get; set; } + /// + /// [nzTemplateMode],是否模板模式,无需将数据传递给 nzData,默认值: false + /// + public string TemplateMode { get; set; } + /// + /// [nzVirtualItemSize],虚拟滚动时每一列的高度,单位:像素,默认值: 0 + /// + public string VirtualItemSize { get; set; } + /// + /// [nzVirtualMaxBufferPx],虚拟滚动缓冲区最大高度,单位:像素,默认值: 200 + /// + public string VirtualMaxBufferPx { get; set; } + /// + /// [nzVirtualMinBufferPx],虚拟滚动缓冲区最小高度,低于该值时将加载新结构,单位:像素,默认值: 100 + /// + public string VirtualMinBufferPx { get; set; } + /// + /// [nzVirtualForTrackBy],虚拟滚动数据 TrackByFunction 函数,类型: TrackByFunction<T> + /// + public string VirtualForTrackBy { get; set; } + /// + /// nzTableLayout,表格布局,可选值: 'fixed' | 'auto' + /// + public TableLayout Layout { get; set; } + /// + /// [nzTableLayout],表格布局,可选值: 'fixed' | 'auto' + /// + public string BindLayout { get; set; } + /// + /// [nzCustomColumn],自定义列, 控制表格列的展示与排序, 类型: NzCustomColumn[], 注意: 开启后 nzWidthConfig 和 th 的 nzWidth 将不生效 + /// + public string CustomColumn { get; set; } + /// + /// 扩展属性,是否启用表格设置,将启用表格设置的全部功能, 默认值: false + /// + public bool EnableTableSettings { get; set; } + /// + /// 扩展属性,是否启用自定义列,默认值: false + /// + public bool EnableCustomColumn { get; set; } + /// + /// 扩展属性,是否启用拖动调整列宽,默认值: false + /// + public bool EnableResizable { get; set; } + /// + /// 扩展属性,是否启用固定列,默认值: false + /// + public bool EnableFixedColumn { get; set; } + /// + /// 扩展属性 [isBatch],是否批量编辑, false表示单行编辑模式,true表示批量编辑模式,一次性保存所有更改, 默认值: false + /// + public bool IsBatchEdit { get; set; } + /// + /// (nzPageIndexChange),页码变化事件,类型: EventEmitter<number> + /// + public string OnPageIndexChange { get; set; } + /// + /// (nzPageSizeChange),每页显示行数变化事件,类型: EventEmitter<number> + /// + public string OnPageSizeChange { get; set; } + /// + /// (nzCurrentPageDataChange),当前页数据变化事件,类型: EventEmitter<any[]> + /// + public string OnCurrentPageDataChange { get; set; } + /// + /// (nzQueryParams),查询参数变化事件,当服务端分页、筛选、排序时,用于获得参数,类型: EventEmitter<NzTableQueryParams> + /// + public string OnQueryParams { get; set; } + /// + /// 扩展事件 (onLoad),数据加载完成事件,类型: EventEmitter<any>,参数为服务端返回结果 + /// + public string OnLoad { get; set; } + /// + /// 扩展事件,行单击事件,使用row访问行对象,范例:on-click-row="click(row)" + /// + public string OnClickRow { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new TableService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new TableRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tabs/Builders/TabBuilder.cs b/src/Util.Ui.NgZorro/Components/Tabs/Builders/TabBuilder.cs new file mode 100644 index 000000000..4facfebfd --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tabs/Builders/TabBuilder.cs @@ -0,0 +1,121 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Templates.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Tabs.Builders; + +/// +/// 标签标签生成器 +/// +public class TabBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化标签标签生成器 + /// + public TabBuilder( Config config ) : base( config, "nz-tab" ) { + _config = config; + } + + /// + /// 配置标题 + /// + public TabBuilder Title() { + SetTitle( _config.GetValue( UiConst.Title ) ); + AttributeIfNotEmpty( "[nzTitle]", _config.GetValue( AngularConst.BindTitle ) ); + return this; + } + + /// + /// 设置标题 + /// + private void SetTitle( string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + this.AttributeByI18n( "[nzTitle]", value ); + return; + } + AttributeIfNotEmpty( "nzTitle", value ); + } + + /// + /// 配置是否强制渲染 + /// + public TabBuilder ForceRender() { + AttributeIfNotEmpty( "[nzForceRender]", _config.GetValue( UiConst.ForceRender ) ); + return this; + } + + /// + /// 配置是否禁用 + /// + public TabBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置是否显示关闭按钮 + /// + public TabBuilder Closable() { + AttributeIfNotEmpty( "[nzClosable]", _config.GetValue( UiConst.Closable ) ); + return this; + } + + /// + /// 配置关闭按钮图标 + /// + public TabBuilder CloseIcon() { + AttributeIfNotEmpty( "nzCloseIcon", _config.GetValue( UiConst.CloseIcon )?.Description() ); + AttributeIfNotEmpty( "[nzCloseIcon]", _config.GetValue( AngularConst.BindCloseIcon ) ); + return this; + } + + /// + /// 配置事件 + /// + public TabBuilder Events() { + AttributeIfNotEmpty( "(nzClick)", _config.GetValue( UiConst.OnClick ) ); + AttributeIfNotEmpty( "(nzSelect)", _config.GetValue( UiConst.OnSelect ) ); + AttributeIfNotEmpty( "(nzDeselect)", _config.GetValue( UiConst.OnDeselect ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Title().ForceRender().Disabled().Closable().CloseIcon() + .Events(); + } + + /// + /// 配置内容 + /// + protected override void ConfigContent( Config config ) { + var isLazy = _config.GetValue( UiConst.Lazy ); + if ( isLazy != true ) { + base.ConfigContent( config ); + return; + } + var templateBuilder = new TemplateBuilder( _config ); + templateBuilder.Tab( true ); + config.Content.AppendTo( templateBuilder ); + SetContent( templateBuilder ); + } + + /// + /// 配置右键上下文菜单事件 + /// + protected override void ConfigOnContextmenu( Config config ) { + AttributeIfNotEmpty( "(nzContextmenu)", _config.GetValue( UiConst.OnContextmenu ) ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tabs/Builders/TabSetBuilder.cs b/src/Util.Ui.NgZorro/Components/Tabs/Builders/TabSetBuilder.cs new file mode 100644 index 000000000..de3c61592 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tabs/Builders/TabSetBuilder.cs @@ -0,0 +1,180 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Tabs.Builders; + +/// +/// 标签页标签生成器 +/// +public class TabSetBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化标签页标签生成器 + /// + /// 配置 + public TabSetBuilder( Config config ) : base( config,"nz-tabset" ) { + _config = config; + } + + /// + /// 配置选中标签索引 + /// + public TabSetBuilder SelectedIndex() { + AttributeIfNotEmpty( "[nzSelectedIndex]", _config.GetValue( UiConst.SelectedIndex ) ); + AttributeIfNotEmpty( "[(nzSelectedIndex)]", _config.GetValue( AngularConst.BindonSelectedIndex ) ); + return this; + } + + /// + /// 配置是否使用动画切换选项卡 + /// + public TabSetBuilder Animated() { + AttributeIfNotEmpty( "[nzAnimated]", _config.GetValue( UiConst.Animated ) ); + return this; + } + + /// + /// 配置标签大小 + /// + public TabSetBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置标签栏附加内容 + /// + public TabSetBuilder TabBarExtraContent() { + AttributeIfNotEmpty( "[nzTabBarExtraContent]", _config.GetValue( UiConst.TabBarExtraContent ) ); + return this; + } + + /// + /// 配置标签栏样式 + /// + public TabSetBuilder TabBarStyle() { + AttributeIfNotEmpty( "[nzTabBarStyle]", _config.GetValue( UiConst.TabBarStyle ) ); + return this; + } + + /// + /// 配置标签位置 + /// + public TabSetBuilder TabPosition() { + AttributeIfNotEmpty( "nzTabPosition", _config.GetValue( UiConst.TabPosition )?.Description() ); + AttributeIfNotEmpty( "[nzTabPosition]", _config.GetValue( AngularConst.BindTabPosition ) ); + return this; + } + + /// + /// 配置标签类型 + /// + public TabSetBuilder Type() { + AttributeIfNotEmpty( "nzType", _config.GetValue( UiConst.Type )?.Description() ); + AttributeIfNotEmpty( "[nzType]", _config.GetValue( AngularConst.BindType ) ); + return this; + } + + /// + /// 配置标签间隙 + /// + public TabSetBuilder TabBarGutter() { + AttributeIfNotEmpty( "[nzTabBarGutter]", _config.GetValue( UiConst.TabBarGutter ) ); + return this; + } + + /// + /// 配置是否隐藏全部标签 + /// + public TabSetBuilder HideAll() { + AttributeIfNotEmpty( "[nzHideAll]", _config.GetValue( UiConst.HideAll ) ); + return this; + } + + /// + /// 配置是否支持路由联动 + /// + public TabSetBuilder LinkRouter() { + AttributeIfNotEmpty( "[nzLinkRouter]", _config.GetValue( UiConst.LinkRouter ) ); + return this; + } + + /// + /// 配置是否以严格模式匹配路由联动 + /// + public TabSetBuilder LinkExact() { + AttributeIfNotEmpty( "[nzLinkExact]", _config.GetValue( UiConst.LinkExact ) ); + return this; + } + + /// + /// 配置标签守卫函数 + /// + public TabSetBuilder CanDeactivate() { + AttributeIfNotEmpty( "[nzCanDeactivate]", _config.GetValue( UiConst.CanDeactivate ) ); + return this; + } + + /// + /// 配置标签是否居中显示 + /// + public TabSetBuilder Centered() { + AttributeIfNotEmpty( "[nzCentered]", _config.GetValue( UiConst.Centered ) ); + return this; + } + + /// + /// 配置是否隐藏添加按钮 + /// + public TabSetBuilder HideAdd() { + AttributeIfNotEmpty( "[nzHideAdd]", _config.GetValue( UiConst.HideAdd ) ); + return this; + } + + /// + /// 配置添加按钮图标 + /// + public TabSetBuilder AddIcon() { + AttributeIfNotEmpty( "nzAddIcon", _config.GetValue( UiConst.AddIcon )?.Description() ); + AttributeIfNotEmpty( "[nzAddIcon]", _config.GetValue( AngularConst.BindAddIcon ) ); + return this; + } + + /// + /// 配置关闭按钮图标 + /// + public TabSetBuilder CloseIcon() { + AttributeIfNotEmpty( "nzCloseIcon", _config.GetValue( UiConst.CloseIcon )?.Description() ); + AttributeIfNotEmpty( "[nzCloseIcon]", _config.GetValue( AngularConst.BindCloseIcon ) ); + return this; + } + + /// + /// 配置事件 + /// + public TabSetBuilder Events() { + AttributeIfNotEmpty( "(nzSelectedIndexChange)", _config.GetValue( UiConst.OnSelectedIndexChange ) ); + AttributeIfNotEmpty( "(nzSelectChange)", _config.GetValue( UiConst.OnSelectChange ) ); + AttributeIfNotEmpty( "(nzAdd)", _config.GetValue( UiConst.OnAdd ) ); + AttributeIfNotEmpty( "(nzClose)", _config.GetValue( UiConst.OnClose ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + SelectedIndex().Animated().Size().Type() + .TabBarExtraContent().TabBarStyle().TabPosition().TabBarGutter() + .HideAll().LinkRouter().LinkExact().CanDeactivate().Centered() + .HideAdd().AddIcon().CloseIcon() + .Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tabs/Renders/TabRender.cs b/src/Util.Ui.NgZorro/Components/Tabs/Renders/TabRender.cs new file mode 100644 index 000000000..f6c7dc7e0 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tabs/Renders/TabRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tabs.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tabs.Renders; + +/// +/// 标签渲染器 +/// +public class TabRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化标签渲染器 + /// + /// 配置 + public TabRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TabBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TabRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tabs/Renders/TabSetRender.cs b/src/Util.Ui.NgZorro/Components/Tabs/Renders/TabSetRender.cs new file mode 100644 index 000000000..db00e2fe4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tabs/Renders/TabSetRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tabs.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tabs.Renders; + +/// +/// 标签页渲染器 +/// +public class TabSetRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化标签页渲染器 + /// + /// 配置 + public TabSetRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TabSetBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TabSetRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tabs/TabSetTagHelper.cs b/src/Util.Ui.NgZorro/Components/Tabs/TabSetTagHelper.cs new file mode 100644 index 000000000..d684e81f3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tabs/TabSetTagHelper.cs @@ -0,0 +1,124 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Tabs.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tabs; + +/// +/// 标签页,生成的标签为<nz-tabset></nz-tabset> +/// +[HtmlTargetElement( "util-tabset" )] +public class TabSetTagHelper : AngularTagHelperBase { + /// + /// [nzSelectedIndex],选中标签索引,即当前激活标签面板的序号,类型: number + /// + public string SelectedIndex { get; set; } + /// + /// [(nzSelectedIndex)],选中标签索引,即当前激活标签面板的序号,类型: number + /// + public string BindonSelectedIndex { get; set; } + /// + /// [nzAnimated],是否使用动画切换选项卡,类型: boolean | {inkBar:boolean, tabPane:boolean},默认值: true, 当 nzType 为 'card' 时为 false + /// + public string Animated { get; set; } + /// + /// nzSize,标签尺寸,可选值: 'large' | 'small' | 'default',默认值: 'default' + /// + public TabSize Size { get; set; } + /// + /// [nzSize],标签尺寸,可选值: 'large' | 'small' | 'default',默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// [nzTabBarExtraContent],标签栏附加内容,显示在标签栏右边,类型: TemplateRef<void> + /// + public string TabBarExtraContent { get; set; } + /// + /// [nzTabBarStyle],标签栏样式,类型: object + /// + public string TabBarStyle { get; set; } + /// + /// nzTabPosition,标签位置,可选值: 'top' | 'right' | 'bottom' | 'left',默认值: 'top' + /// + public TabPosition TabPosition { get; set; } + /// + /// [nzTabPosition],标签位置,可选值: 'top' | 'right' | 'bottom' | 'left',默认值: 'top' + /// + public string BindTabPosition { get; set; } + /// + /// nzType,标签类型,可选值: 'line' | 'card' | 'editable-card',默认值: 'line' + /// + public TabType Type { get; set; } + /// + /// [nzType],标签类型,可选值: 'line' | 'card' | 'editable-card',默认值: 'line' + /// + public string BindType { get; set; } + /// + /// [nzTabBarGutter],标签间隙, 类型: number + /// + public string TabBarGutter { get; set; } + /// + /// [nzHideAll],是否隐藏全部标签,默认值: false + /// + public string HideAll { get; set; } + /// + /// [nzLinkRouter],是否支持路由联动,默认值: false + /// + public string LinkRouter { get; set; } + /// + /// [nzLinkExact],是否以严格模式匹配路由联动,默认值: true + /// + public string LinkExact { get; set; } + /// + /// [nzCanDeactivate],标签守卫函数,决定标签是否可以被切换,类型: NzTabsCanDeactivateFn + /// + public string CanDeactivate { get; set; } + /// + /// [nzCentered],标签是否居中显示,默认值: false + /// + public string Centered { get; set; } + /// + /// [nzHideAdd],是否隐藏添加按钮,当 nzType 为 'editable-card' 时有效,默认值: false + /// + public string HideAdd { get; set; } + /// + /// nzAddIcon,添加按钮图标,当 nzType 为 'editable-card' 时有效,类型: string | TemplateRef<void> + /// + public AntDesignIcon AddIcon { get; set; } + /// + /// [nzAddIcon],添加按钮图标,当 nzType 为 'editable-card' 时有效,类型: string | TemplateRef<void> + /// + public string BindAddIcon { get; set; } + /// + /// nzCloseIcon,关闭按钮图标,当 nzType 为 'editable-card' 时有效,类型: string | TemplateRef<void> + /// + public AntDesignIcon CloseIcon { get; set; } + /// + /// [nzCloseIcon],关闭按钮图标,当 nzType 为 'editable-card' 时有效,类型: string | TemplateRef<void> + /// + public string BindCloseIcon { get; set; } + /// + /// (nzSelectedIndexChange),选中标签索引变化事件,类型: EventEmitter<number> + /// + public string OnSelectedIndexChange { get; set; } + /// + /// (nzSelectChange),选中标签变化事件,类型: EventEmitter<{index: number,tab: NzTabComponent}> + /// + public string OnSelectChange { get; set; } + /// + /// (nzAdd),添加标签事件,点击添加按钮时触发, nzType='editable-card' 时有效 + /// + public string OnAdd { get; set; } + /// + /// (nzClose),关闭标签事件,点击删除按钮时触发, nzType='editable-card' 时有效, 类型: EventEmitter<{ index: number }> + /// + public string OnClose { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new TabSetRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tabs/TabTagHelper.cs b/src/Util.Ui.NgZorro/Components/Tabs/TabTagHelper.cs new file mode 100644 index 000000000..a1566cc07 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tabs/TabTagHelper.cs @@ -0,0 +1,64 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Tabs.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tabs; + +/// +/// 标签页选项卡,生成的标签为<nz-tab></nz-tab> +/// +[HtmlTargetElement( "util-tab" )] +public class TabTagHelper : AngularTagHelperBase { + /// + /// 扩展属性,是否延迟加载,设置为 true 则创建 ng-template 包装内容 + /// + public bool Lazy { get; set; } + /// + /// nzTitle,标题,支持多语言 + /// + public string Title { get; set; } + /// + /// [nzTitle],标题,类型: string | TemplateRef<TabTemplateContext> + /// + public string BindTitle { get; set; } + /// + /// [nzForceRender],隐藏时是否强制渲染DOM结构,默认值: false + /// + public string ForceRender { get; set; } + /// + /// [nzDisabled],是否禁用 + /// + public string Disabled { get; set; } + /// + /// [nzClosable],是否显示关闭按钮,当父标签页 nz-tabset 的 nzType 为 'editable-card'时可用,默认值: false + /// + public string Closable { get; set; } + /// + /// nzCloseIcon,关闭按钮图标,当父标签页 nz-tabset 的 nzType 为 'editable-card'时可用,类型: string | TemplateRef<void> + /// + public AntDesignIcon CloseIcon { get; set; } + /// + /// [nzCloseIcon],关闭按钮图标,当父标签页 nz-tabset 的 nzType 为 'editable-card'时可用,类型: string | TemplateRef<void> + /// + public string BindCloseIcon { get; set; } + /// + /// (nzClick),单击事件,类型: EventEmitter<void> + /// + public string OnClick { get; set; } + /// + /// (nzSelect),选中事件,类型: EventEmitter<void> + /// + public string OnSelect { get; set; } + /// + /// (nzDeselect),取消选中事件,类型: EventEmitter<void> + /// + public string OnDeselect { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new TabRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tags/Builders/TagContainerTagBuilder.cs b/src/Util.Ui.NgZorro/Components/Tags/Builders/TagContainerTagBuilder.cs new file mode 100644 index 000000000..7090fb376 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tags/Builders/TagContainerTagBuilder.cs @@ -0,0 +1,140 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; + +namespace Util.Ui.NgZorro.Components.Tags.Builders; + +/// +/// 标签容器标签生成器 +/// +public class TagContainerTagBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 标识 + /// + private string _id; + + /// + /// 初始化标签标签生成器 + /// + /// 配置 + /// 标识 + public TagContainerTagBuilder( Config config, string id ) : base( config, "ng-container" ) { + _config = config; + _id = id; + } + + /// + /// 扩展标识 + /// + public string ExtendId => $"x_{GetId()}"; + + /// + /// 获取标识 + /// + private string GetId() { + if( _id.IsEmpty() == false ) + return _id; + _id = _config.GetValue( UiConst.Id ); + if( _id.IsEmpty() ) + _id = Util.Helpers.Id.Create(); + return _id; + } + + /// + /// 配置事件 + /// + public TagContainerTagBuilder Events() { + AttributeIfNotEmpty( "(onLoad)", _config.GetValue( UiConst.OnLoad ) ); + AttributeIfNotEmpty( "(selectedTextChange)", _config.GetValue( UiConst.OnSelectedTextChange ) ); + AttributeIfNotEmpty( "(selectedValueChange)", _config.GetValue( UiConst.OnSelectedValueChange ) ); + AttributeIfNotEmpty( "(allSelectedChange)", _config.GetValue( UiConst.OnAllSelectedChange ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Events(); + EnableExtend(); + AutoLoad().QueryParam().Url().Data() + .AllSelected().SelectedText().SelectedValue(); + } + + /// + /// 启用扩展 + /// + public TagContainerTagBuilder EnableExtend() { + Attribute( $"#{ExtendId}", "xTagExtend" ); + Attribute( "x-tag-extend" ); + return this; + } + + /// + /// 配置自动加载 + /// + private TagContainerTagBuilder AutoLoad() { + AttributeIfNotEmpty( "[autoLoad]", _config.GetBoolValue( UiConst.AutoLoad ) ); + return this; + } + + /// + /// 配置查询参数 + /// + private TagContainerTagBuilder QueryParam() { + AttributeIfNotEmpty( "[(queryParam)]", _config.GetValue( UiConst.QueryParam ) ); + return this; + } + + /// + /// 配置Api地址 + /// + private TagContainerTagBuilder Url() { + AttributeIfNotEmpty( "url", _config.GetValue( UiConst.Url ) ); + AttributeIfNotEmpty( "[url]", _config.GetValue( AngularConst.BindUrl ) ); + return this; + } + + /// + /// 配置数据源 + /// + private TagContainerTagBuilder Data() { + AttributeIfNotEmpty( "[data]", _config.GetValue( UiConst.Data ) ); + return this; + } + + /// + /// 配置全部选中 + /// + private TagContainerTagBuilder AllSelected() { + AttributeIfNotEmpty( "[(allSelected)]", _config.GetValue( UiConst.AllSelected ) ); + return this; + } + + /// + /// 配置选中文本 + /// + private TagContainerTagBuilder SelectedText() { + AttributeIfNotEmpty( "[(selectedText)]", _config.GetValue( UiConst.SelectedText ) ); + return this; + } + + /// + /// 配置选中值 + /// + private TagContainerTagBuilder SelectedValue() { + AttributeIfNotEmpty( "[(selectedValue)]", _config.GetValue( UiConst.SelectedValue ) ); + return this; + } + + /// + /// 配置内容 + /// + /// 配置 + protected override void ConfigContent( Config config ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tags/Builders/TagTagBuilder.cs b/src/Util.Ui.NgZorro/Components/Tags/Builders/TagTagBuilder.cs new file mode 100644 index 000000000..2732a8c8b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tags/Builders/TagTagBuilder.cs @@ -0,0 +1,181 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Tags.Builders; + +/// +/// 标签标签生成器 +/// +public class TagTagBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化标签标签生成器 + /// + /// 配置 + /// 是否启用扩展 + /// 扩展标识 + public TagTagBuilder( Config config,bool isEnableExtend,string extendId ) : base( config,"nz-tag" ) { + _config = config; + IsEnableExtend = isEnableExtend; + ExtendId = extendId; + } + + /// + /// 是否启用扩展 + /// + protected bool IsEnableExtend { get; } + /// + /// 扩展标识 + /// + protected string ExtendId { get; } + + /// + /// 配置模式 + /// + public TagTagBuilder Mode() { + AttributeIfNotEmpty( "nzMode", _config.GetValue( UiConst.Mode )?.Description() ); + AttributeIfNotEmpty( "[nzMode]", _config.GetValue( AngularConst.BindMode ) ); + return this; + } + + /// + /// 配置是否选中 + /// + public TagTagBuilder Checked() { + AttributeIfNotEmpty( "[nzChecked]", _config.GetValue( UiConst.Checked ) ); + AttributeIfNotEmpty( "[(nzChecked)]", _config.GetValue( AngularConst.BindonChecked ) ); + return this; + } + + /// + /// 配置颜色 + /// + public TagTagBuilder Color() { + AttributeIfNotEmpty( "nzColor", _config.GetValue( UiConst.ColorType )?.Description() ); + AttributeIfNotEmpty( "nzColor", _config.GetValue( UiConst.Color ) ); + AttributeIfNotEmpty( "[nzColor]", _config.GetValue( AngularConst.BindColor ) ); + return this; + } + + /// + /// 配置边框 + /// + public TagTagBuilder Bordered() { + AttributeIfNotEmpty( "[nzBordered]", _config.GetValue( UiConst.Bordered ) ); + return this; + } + + /// + /// 配置事件 + /// + public TagTagBuilder Events() { + AttributeIfNotEmpty( "(click)", _config.GetValue( UiConst.OnClick ) ); + AttributeIfNotEmpty( "(nzOnClose)", _config.GetValue( UiConst.OnClose ) ); + AttributeIfNotEmpty( "(nzCheckedChange)", _config.GetValue( UiConst.OnCheckedChange ) ); + return this; + } + + /// + /// 配置Enabled文本 + /// + public TagTagBuilder TextEnabled() { + var value = _config.GetValue( UiConst.TextEnabled ); + if ( value != true ) + return this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, "util.enabled" ); + return this; + } + _config.SetAttribute( UiConst.Text, "Enabled" ); + return this; + } + + /// + /// 配置Not Enabled文本 + /// + public TagTagBuilder TextNotEnabled() { + var value = _config.GetValue( UiConst.TextNotEnabled ); + if ( value != true ) + return this; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + _config.SetAttribute( UiConst.Text, "util.notEnabled" ); + return this; + } + _config.SetAttribute( UiConst.Text, "Not Enabled" ); + return this; + } + + /// + /// 配置文本 + /// + public TagTagBuilder Text() { + var options = NgZorroOptionsService.GetOptions(); + var text = _config.GetValue( UiConst.Text ); + if ( text.IsEmpty() ) + return this; + if ( options.EnableI18n ) { + this.AppendContentByI18n( text ); + return this; + } + AppendContent( text ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Mode().Checked().Color().Bordered().Events(); + TextEnabled().TextNotEnabled().Text(); + SetChecked(); + } + + /// + /// 设置选中状态 + /// + private void SetChecked() { + if ( IsEnableExtend == false ) + return; + var mode = _config.GetValue( UiConst.Mode ); + if ( mode != TagMode.Checkable ) + return; + if ( _config.GetValue( UiConst.Checked ).IsEmpty() == false ) + return; + if( _config.GetValue( AngularConst.BindChecked ).IsEmpty() == false ) + return; + if( _config.GetValue( AngularConst.BindonChecked ).IsEmpty() == false ) + return; + Attribute( "[nzChecked]", "item.selected" ); + if ( _config.GetValue( UiConst.OnCheckedChange ).IsEmpty() == false ) + return; + Attribute( "(nzCheckedChange)", $"{ExtendId}.selectItem($event,item.text)" ); + } + + /// + /// 配置内容 + /// + protected override void ConfigContent( Config config ) { + if ( config.Content.IsEmpty() && IsEnableExtend ) { + var options = NgZorroOptionsService.GetOptions(); + if( options.EnableI18n ) { + AppendContent( "{{item.text|i18n}}" ); + return; + } + AppendContent( "{{item.text}}" ); + return; + } + base.ConfigContent( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tags/Helpers/TagExpressionLoader.cs b/src/Util.Ui.NgZorro/Components/Tags/Helpers/TagExpressionLoader.cs new file mode 100644 index 000000000..eeef53699 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tags/Helpers/TagExpressionLoader.cs @@ -0,0 +1,40 @@ +using Util.Ui.Expressions; +using Util.Ui.NgZorro.Expressions; + +namespace Util.Ui.NgZorro.Components.Tags.Helpers; + +/// +/// 标签表达式加载器 +/// +public class TagExpressionLoader : NgZorroExpressionLoaderBase { + /// + /// 加载模型信息 + /// + /// 配置 + /// 模型表达式信息 + protected override void Load( Config config, ModelExpressionInfo info ) { + LoadData( config, info ); + } + + /// + /// 加载数据源 + /// + protected virtual void LoadData( Config config, ModelExpressionInfo info ) { + LoadEnum( config, info ); + } + + /// + /// 加载枚举值 + /// + protected virtual void LoadEnum( Config config, ModelExpressionInfo info ) { + if ( info.IsEnum == false ) + return; + var items = Util.Helpers.Enum.GetItems( info.ModelType ); + var options = new JsonSerializerOptions { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ) + }; + var result = Util.Helpers.Json.ToJson( items, options, toSingleQuotes: true ); + config.SetAttribute( UiConst.Data, result, false ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tags/Renders/TagRender.cs b/src/Util.Ui.NgZorro/Components/Tags/Renders/TagRender.cs new file mode 100644 index 000000000..e12979873 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tags/Renders/TagRender.cs @@ -0,0 +1,92 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Tags.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Tags.Renders; + +/// +/// 标签渲染器 +/// +public class TagRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化标签渲染器 + /// + /// 配置 + public TagRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var config = _config.Copy(); + config.RemoveAttribute( UiConst.Id ); + var containerTagBuilder = new TagContainerTagBuilder( config, _config.GetValue( UiConst.Id ) ); + containerTagBuilder.Config(); + TagBuilder container = IsEnableExtend() ? containerTagBuilder : new EmptyContainerTagBuilder(); + if (IsEnableExtend()) { + container.AppendContent($"@for(item of {containerTagBuilder.ExtendId}.options;track item.text)"); + container.AppendContent( "{" ); + } + var builder = new TagTagBuilder( _config, IsEnableExtend(), containerTagBuilder.ExtendId ); + container.AppendContent( builder ); + builder.Config(); + if ( IsEnableExtend() ) { + container.AppendContent( "}" ); + } + return container; + } + + /// + /// 是否启用基础扩展 + /// + public bool IsEnableExtend() { + if ( GetEnableExtend() == false ) + return false; + return GetEnableExtend() == true || + GetUrl().IsEmpty() == false || + GetBindUrl().IsEmpty() == false || + GetData().IsEmpty() == false; + } + + /// + /// 获取启用扩展属性 + /// + private bool? GetEnableExtend() { + return _config.GetValue( UiConst.EnableExtend ); + } + + /// + /// 获取地址 + /// + private string GetUrl() { + return _config.GetValue( UiConst.Url ); + } + + /// + /// 获取地址 + /// + private string GetBindUrl() { + return _config.GetValue( AngularConst.BindUrl ); + } + + /// + /// 获取数据源 + /// + private string GetData() { + return _config.GetValue( UiConst.Data ); + } + + /// + public override IHtmlContent Clone() { + return new TagRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Tags/TagTagHelper.cs b/src/Util.Ui.NgZorro/Components/Tags/TagTagHelper.cs new file mode 100644 index 000000000..e109d93da --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Tags/TagTagHelper.cs @@ -0,0 +1,146 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Tags.Helpers; +using Util.Ui.NgZorro.Components.Tags.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; +using TagMode = Util.Ui.NgZorro.Enums.TagMode; + +namespace Util.Ui.NgZorro.Components.Tags; + +/// +/// 标签,生成的标签为<nz-tag></nz-tag> +/// +[HtmlTargetElement( "util-tag" )] +public class TagTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 属性表达式 + /// + public ModelExpression For { get; set; } + /// + /// 扩展属性,是否启用扩展指令,当设置 url 或 data 属性时自动启用,默认为 false + /// + public bool EnableExtend { get; set; } + /// + /// 扩展属性 [autoLoad],初始化时是否自动加载数据,默认为 true,设置成 false 手工加载 + /// + public string AutoLoad { get; set; } + /// + /// 扩展属性 [(allSelected)],是否选中全部标签 + /// + public string AllSelected { get; set; } + /// + /// 扩展属性 [(selectedText)],选中标签文本,多个标签文本使用 | 分隔 + /// + public string SelectedText { get; set; } + /// + /// 扩展属性 [(selectedValue)],选中标签值,多个标签值使用 , 分隔 + /// + public string SelectedValue { get; set; } + /// + /// 扩展属性 [(queryParam)],查询参数 + /// + public string QueryParam { get; set; } + /// + /// 扩展属性 url,Api地址 + /// + public string Url { get; set; } + /// + /// 扩展属性 [url],Api地址 + /// + public string BindUrl { get; set; } + /// + /// 扩展属性 [data],数据源 + /// + public string Data { get; set; } + /// + /// 扩展属性,内容文本,支持i18n + /// + public string Text { get; set; } + /// + /// 扩展属性,是否显示Enabled文本,默认文本为'Enabled',i18n文本为'util.enabled' + /// + public bool TextEnabled { get; set; } + /// + /// 扩展属性,是否显示Not Enabled文本,默认文本为'Not Enabled',i18n文本为'util.notEnabled' + /// + public bool TextNotEnabled { get; set; } + /// + /// nzMode,模式,可选值: 'closeable' | 'default' | 'checkable',默认值: 'default' + /// + public TagMode Mode { get; set; } + /// + /// [nzMode],模式,可选值: 'closeable' | 'default' | 'checkable',默认值: 'default' + /// + public string BindMode { get; set; } + /// + /// [nzChecked],是否选中,在 nzMode="checkable" 时可用,默认值: false + /// + public string Checked { get; set; } + /// + /// [(nzChecked)],是否选中,在 nzMode="checkable" 时可用,默认值: false + /// + public string BindonChecked { get; set; } + /// + /// nzColor,扩展属性, 颜色类型 + /// + public TagColor ColorType { get; set; } + /// + /// nzColor,颜色 + /// + public string Color { get; set; } + /// + /// [nzColor],颜色 + /// + public string BindColor { get; set; } + /// + /// [nzBordered],是否有边框, 类型: boolean, 默认值: true + /// + public string Bordered { get; set; } + /// + /// (click),单击事件 + /// + public string OnClick { get; set; } + /// + /// (nzOnClose),关闭事件,在 nzMode="closable" 时可用,类型: EventEmitter<MouseEvent> + /// + public string OnClose { get; set; } + /// + /// (nzCheckedChange),选中状态变化事件,在 nzMode="checkable" 时可用,类型: EventEmitter<void> + /// + public string OnCheckedChange { get; set; } + /// + /// 扩展事件 (onLoad),数据加载完成事件,类型: EventEmitter<any>,参数为服务端返回结果 + /// + public string OnLoad { get; set; } + /// + /// 扩展事件 (selectedTextChange),选中文本变化事件,类型: EventEmitter<string>,参数为选中标签的文本,以 | 分隔 + /// + public string OnSelectedTextChange { get; set; } + /// + /// 扩展事件 (selectedValueChange),选中值变化事件,类型: EventEmitter<string>,参数为选中标签的值,以 , 分隔 + /// + public string OnSelectedValueChange { get; set; } + /// + /// 扩展事件 (allSelectedChange),全部选中状态变化事件,类型: EventEmitter<boolean> + /// + public string OnAllSelectedChange { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var loader = new TagExpressionLoader(); + loader.Load( _config ); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new TagRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Templates/Builders/TemplateBuilder.cs b/src/Util.Ui.NgZorro/Components/Templates/Builders/TemplateBuilder.cs new file mode 100644 index 000000000..7ef4e2812 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Templates/Builders/TemplateBuilder.cs @@ -0,0 +1,55 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; + +namespace Util.Ui.NgZorro.Components.Templates.Builders; + +/// +/// ng-template标签生成器 +/// +public class TemplateBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化ng-template标签生成器 + /// + /// 配置 + public TemplateBuilder( Config config ) : base( config, "ng-template" ) { + _config = config; + } + + /// + /// 配置选项卡是否延迟加载 + /// + public TemplateBuilder Tab() { + Tab( _config.GetValue( UiConst.Tab ) == true ); + return this; + } + + /// + /// 配置选项卡是否延迟加载 + /// + public TemplateBuilder Tab( bool value ) { + AttributeIf( "nz-tab", value ); + return this; + } + + /// + /// 配置模板出口 + /// + public TemplateBuilder TemplateOutlet() { + AttributeIfNotEmpty( "[ngTemplateOutlet]", _config.GetValue( AngularConst.NgTemplateOutlet ) ); + AttributeIfNotEmpty( "[ngTemplateOutletContext]", _config.GetValue( AngularConst.NgTemplateOutletContext ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Tab().TemplateOutlet(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Templates/Renders/TemplateRender.cs b/src/Util.Ui.NgZorro/Components/Templates/Renders/TemplateRender.cs new file mode 100644 index 000000000..587648ebe --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Templates/Renders/TemplateRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Templates.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Templates.Renders; + +/// +/// ng-template模板渲染器 +/// +public class TemplateRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化模板渲染器 + /// + /// 配置 + public TemplateRender( Config config ) { + _config = config ?? throw new ArgumentNullException( nameof( config ) ); + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TemplateBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TemplateRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Templates/TemplateTagHelper.cs b/src/Util.Ui.NgZorro/Components/Templates/TemplateTagHelper.cs new file mode 100644 index 000000000..6fd55b672 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Templates/TemplateTagHelper.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Templates.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Templates; + +/// +/// 模板,生成的标签为<ng-template></ng-template> +/// +[HtmlTargetElement( "util-template" )] +public class TemplateTagHelper : AngularTagHelperBase { + /// + /// 扩展属性 nz-tab,选项卡是否延迟加载, 默认值: false + /// + public bool Tab { get; set; } + /// + /// [ngTemplateOutlet], 模板出口 + /// + public string NgTemplateOutlet { get; set; } + /// + /// [ngTemplateOutletContext], 模板出口上下文 + /// + public string NgTemplateOutletContext { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new TemplateRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TimePickers/Builders/TimePickerBuilder.cs b/src/Util.Ui.NgZorro/Components/TimePickers/Builders/TimePickerBuilder.cs new file mode 100644 index 000000000..e443049d8 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TimePickers/Builders/TimePickerBuilder.cs @@ -0,0 +1,202 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.TimePickers.Builders; + +/// +/// 时间选择标签生成器 +/// +public class TimePickerBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化时间选择标签生成器 + /// + /// 配置 + public TimePickerBuilder( Config config ) : base( config, "nz-time-picker" ) { + _config = config; + } + + /// + /// 配置底部自定义内容 + /// + public TimePickerBuilder AddOn() { + AttributeIfNotEmpty( "[nzAddOn]", _config.GetValue( UiConst.AddOn ) ); + return this; + } + + /// + /// 配置允许清除 + /// + public TimePickerBuilder AllowEmpty() { + AttributeIfNotEmpty( "[nzAllowEmpty]", _config.GetValue( UiConst.AllowEmpty ) ); + return this; + } + + /// + /// 配置清除按钮显示文本 + /// + public TimePickerBuilder ClearText() { + AttributeIfNotEmpty( "nzClearText", _config.GetValue( UiConst.ClearText ) ); + AttributeIfNotEmpty( "[nzClearText]", _config.GetValue( AngularConst.BindClearText ) ); + return this; + } + + /// + /// 配置自动聚焦 + /// + public TimePickerBuilder AutoFocus() { + AttributeIfNotEmpty( "[nzAutoFocus]", _config.GetValue( UiConst.AutoFocus ) ); + return this; + } + + /// + /// 配置默认值 + /// + public TimePickerBuilder DefaultOpenValue() { + AttributeIfNotEmpty( "nzDefaultOpenValue", _config.GetValue( UiConst.DefaultOpenValue ) ); + AttributeIfNotEmpty( "[nzDefaultOpenValue]", _config.GetValue( AngularConst.BindDefaultOpenValue ) ); + return this; + } + + /// + /// 配置禁用 + /// + public TimePickerBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置不可选择小时函数 + /// + public TimePickerBuilder DisabledHours() { + AttributeIfNotEmpty( "[nzDisabledHours]", _config.GetValue( UiConst.DisabledHours ) ); + return this; + } + + /// + /// 配置不可选择分钟函数 + /// + public TimePickerBuilder DisabledMinutes() { + AttributeIfNotEmpty( "[nzDisabledMinutes]", _config.GetValue( UiConst.DisabledMinutes ) ); + return this; + } + + /// + /// 配置不可选择秒函数 + /// + public TimePickerBuilder DisabledSeconds() { + AttributeIfNotEmpty( "[nzDisabledSeconds]", _config.GetValue( UiConst.DisabledSeconds ) ); + return this; + } + + /// + /// 配置时间格式 + /// + public TimePickerBuilder Format() { + AttributeIfNotEmpty( "nzFormat", _config.GetValue( UiConst.Format ) ); + AttributeIfNotEmpty( "[nzFormat]", _config.GetValue( AngularConst.BindFormat ) ); + return this; + } + + /// + /// 配置隐藏禁止选择的选项 + /// + public TimePickerBuilder HideDisabledOptions() { + AttributeIfNotEmpty( "[nzHideDisabledOptions]", _config.GetValue( UiConst.HideDisabledOptions ) ); + return this; + } + + /// + /// 配置小时选项间隔 + /// + public TimePickerBuilder HourStep() { + AttributeIfNotEmpty( "[nzHourStep]", _config.GetValue( UiConst.HourStep ) ); + return this; + } + + /// + /// 配置分钟选项间隔 + /// + public TimePickerBuilder MinuteStep() { + AttributeIfNotEmpty( "[nzMinuteStep]", _config.GetValue( UiConst.MinuteStep ) ); + return this; + } + + /// + /// 配置秒选项间隔 + /// + public TimePickerBuilder SecondStep() { + AttributeIfNotEmpty( "[nzSecondStep]", _config.GetValue( UiConst.SecondStep ) ); + return this; + } + + /// + /// 配置面板是否打开 + /// + public TimePickerBuilder Open() { + AttributeIfNotEmpty( "[nzOpen]", _config.GetValue( UiConst.Open ) ); + AttributeIfNotEmpty( "[(nzOpen)]", _config.GetValue( AngularConst.BindonOpen ) ); + return this; + } + + /// + /// 配置占位提示 + /// + public TimePickerBuilder Placeholder() { + AttributeIfNotEmpty( "nzPlaceHolder", _config.GetValue( UiConst.Placeholder ) ); + AttributeIfNotEmpty( "[nzPlaceHolder]", _config.GetValue( AngularConst.BindPlaceholder ) ); + return this; + } + + /// + /// 配置弹出层样式类名 + /// + public TimePickerBuilder PopupClassName() { + AttributeIfNotEmpty( "nzPopupClassName", _config.GetValue( UiConst.PopupClassName ) ); + AttributeIfNotEmpty( "[nzPopupClassName]", _config.GetValue( AngularConst.BindPopupClassName ) ); + return this; + } + + /// + /// 配置12小时制 + /// + public TimePickerBuilder Use12Hours() { + AttributeIfNotEmpty( "[nzUse12Hours]", _config.GetValue( UiConst.Use12Hours ) ); + return this; + } + + /// + /// 配置后缀图标 + /// + public TimePickerBuilder SuffixIcon() { + AttributeIfNotEmpty( "nzSuffixIcon", _config.GetValue( UiConst.SuffixIcon )?.Description() ); + AttributeIfNotEmpty( "[nzSuffixIcon]", _config.GetValue( AngularConst.BindSuffixIcon ) ); + return this; + } + + /// + /// 配置事件 + /// + public TimePickerBuilder Events() { + AttributeIfNotEmpty( "(nzOpenChange)", _config.GetValue( UiConst.OnOpenChange ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + ConfigForm().Name().AddOn().AllowEmpty().AutoFocus().ClearText().DefaultOpenValue() + .Disabled().DisabledHours().DisabledMinutes().DisabledSeconds() + .Format().HideDisabledOptions().HourStep().MinuteStep().SecondStep() + .Open().Placeholder().PopupClassName().Use12Hours().SuffixIcon() + .Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TimePickers/Renders/TimePickerRender.cs b/src/Util.Ui.NgZorro/Components/TimePickers/Renders/TimePickerRender.cs new file mode 100644 index 000000000..b674626d1 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TimePickers/Renders/TimePickerRender.cs @@ -0,0 +1,40 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.TimePickers.Builders; + +namespace Util.Ui.NgZorro.Components.TimePickers.Renders; + +/// +/// 时间选择渲染器 +/// +public class TimePickerRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化时间选择渲染器 + /// + /// 配置 + public TimePickerRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new TimePickerBuilder( _config ); + builder.Config(); + _config.Content.AppendTo( builder ); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new TimePickerRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TimePickers/TimePickerTagHelper.cs b/src/Util.Ui.NgZorro/Components/TimePickers/TimePickerTagHelper.cs new file mode 100644 index 000000000..2cd7f5031 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TimePickers/TimePickerTagHelper.cs @@ -0,0 +1,140 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Inputs.Helpers; +using Util.Ui.NgZorro.Components.TimePickers.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.TimePickers; + +/// +/// 时间选择,生成的标签为<nz-time-picker></nz-time-picker> +/// +[HtmlTargetElement( "util-time-picker" )] +public class TimePickerTagHelper : FormControlTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// [nzAddOn],选择框底部显示自定义内容,类型: TemplateRef<void> + /// + public string AddOn { get; set; } + /// + /// [nzAllowEmpty],是否展示清除按钮,默认值: true + /// + public string AllowEmpty { get; set; } + /// + /// nzClearText,清除按钮的提示文本,默认值: 'clear' + /// + public string ClearText { get; set; } + /// + /// [nzClearText],清除按钮的提示文本,默认值: 'clear' + /// + public string BindClearText { get; set; } + /// + /// [nzAutoFocus],是否自动聚焦,默认值: false + /// + public string AutoFocus { get; set; } + /// + /// nzDefaultOpenValue,打开时默认选中的值,当 [ngModel] 不存在时有效,类型: Date,默认值: new Date() + /// + public string DefaultOpenValue { get; set; } + /// + /// [nzDefaultOpenValue],打开时默认选中的值,当 [ngModel] 不存在时有效,类型: Date,默认值: new Date() + /// + public string BindDefaultOpenValue { get; set; } + /// + /// [nzDisabled],是否禁用 + /// + public string Disabled { get; set; } + /// + /// [nzDisabledHours],不可选择小时函数,函数定义:() => number[] + /// + public string DisabledHours { get; set; } + /// + /// [nzDisabledMinutes],不可选择分钟函数,函数定义:(hour: number) => number[] + /// + public string DisabledMinutes { get; set; } + /// + /// [nzDisabledSeconds],不可选择秒函数,函数定义:(hour: number, minute: number) => number[] + /// + public string DisabledSeconds { get; set; } + /// + /// nzFormat,格式化字符串,格式说明:时(HH) 分(mm) 秒(ss) 毫秒(SSS),默认值:"HH:mm:ss" + /// + public string Format { get; set; } + /// + /// [nzFormat],格式化字符串,格式说明:时(HH) 分(mm) 秒(ss) 毫秒(SSS),默认值:"HH:mm:ss" + /// + public string BindFormat { get; set; } + /// + /// [nzHideDisabledOptions],是否隐藏禁止选择的选项,默认值: false + /// + public string HideDisabledOptions { get; set; } + /// + /// [nzHourStep],小时选项间隔,默认值: 1 + /// + public string HourStep { get; set; } + /// + /// [nzMinuteStep],分钟选项间隔,默认值: 1 + /// + public string MinuteStep { get; set; } + /// + /// [nzSecondStep],秒选项间隔,默认值: 1 + /// + public string SecondStep { get; set; } + /// + /// [nzOpen],面板是否打开,默认值: false + /// + public string Open { get; set; } + /// + /// [(nzOpen)],面板是否打开,默认值: false + /// + public string BindonOpen { get; set; } + /// + /// nzPlaceHolder,输入框占位文本 + /// + public string Placeholder { get; set; } + /// + /// [nzPlaceHolder],输入框占位文本 + /// + public string BindPlaceholder { get; set; } + /// + /// nzPopupClassName,弹出层样式类名 + /// + public string PopupClassName { get; set; } + /// + /// [nzPopupClassName],弹出层样式类名 + /// + public string BindPopupClassName { get; set; } + /// + /// [nzUse12Hours],是否使用12小时制,为 true 时 format 默认为 h:mm:ss a,默认值: false + /// + public string Use12Hours { get; set; } + /// + /// nzSuffixIcon,后缀图标 + /// + public AntDesignIcon SuffixIcon { get; set; } + /// + /// [nzSuffixIcon],后缀图标 + /// + public string BindSuffixIcon { get; set; } + /// + /// (nzOpenChange),打开关闭面板事件 + /// + public string OnOpenChange { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new InputService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new TimePickerRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Timelines/Builders/TimelineBuilder.cs b/src/Util.Ui.NgZorro/Components/Timelines/Builders/TimelineBuilder.cs new file mode 100644 index 000000000..77ce1c68c --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Timelines/Builders/TimelineBuilder.cs @@ -0,0 +1,66 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Timelines.Builders; + +/// +/// 时间轴标签生成器 +/// +public class TimelineBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化时间轴标签生成器 + /// + public TimelineBuilder( Config config ) : base( config,"nz-timeline" ) { + _config = config; + } + + /// + /// 配置幽灵节点 + /// + public TimelineBuilder Pending() { + AttributeIfNotEmpty( "nzPending", _config.GetValue( UiConst.Pending ) ); + AttributeIfNotEmpty( "[nzPending]", _config.GetValue( AngularConst.BindPending ) ); + return this; + } + + /// + /// 配置幽灵节点时间图点 + /// + public TimelineBuilder PendingDot() { + AttributeIfNotEmpty( "nzPendingDot", _config.GetValue( UiConst.PendingDot ) ); + AttributeIfNotEmpty( "[nzPendingDot]", _config.GetValue( AngularConst.BindPendingDot ) ); + return this; + } + + /// + /// 配置是否倒序排列 + /// + public TimelineBuilder Reverse() { + AttributeIfNotEmpty( "[nzReverse]", _config.GetValue( UiConst.Reverse ) ); + return this; + } + + /// + /// 配置模式 + /// + public TimelineBuilder Mode() { + AttributeIfNotEmpty( "nzMode", _config.GetValue( UiConst.Mode )?.Description() ); + AttributeIfNotEmpty( "[nzMode]", _config.GetValue( AngularConst.BindMode ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Pending().PendingDot().Reverse().Mode(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Timelines/Builders/TimelineItemBuilder.cs b/src/Util.Ui.NgZorro/Components/Timelines/Builders/TimelineItemBuilder.cs new file mode 100644 index 000000000..1f3df992b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Timelines/Builders/TimelineItemBuilder.cs @@ -0,0 +1,66 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Timelines.Builders; + +/// +/// 时间轴节点标签生成器 +/// +public class TimelineItemBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化时间轴节点标签生成器 + /// + public TimelineItemBuilder( Config config ) : base( config,"nz-timeline-item" ) { + _config = config; + } + + /// + /// 配置颜色 + /// + public TimelineItemBuilder Color() { + AttributeIfNotEmpty( "nzColor", _config.GetValue( UiConst.ColorType )?.Description() ); + AttributeIfNotEmpty( "nzColor", _config.GetValue( UiConst.Color ) ); + AttributeIfNotEmpty( "[nzColor]", _config.GetValue( AngularConst.BindColor ) ); + return this; + } + + /// + /// 配置自定义时间轴点 + /// + public TimelineItemBuilder Dot() { + AttributeIfNotEmpty( "[nzDot]", _config.GetValue( UiConst.Dot ) ); + return this; + } + + /// + /// 配置自定义节点位置 + /// + public TimelineItemBuilder Position() { + AttributeIfNotEmpty( "nzPosition", _config.GetValue( UiConst.Position )?.Description() ); + AttributeIfNotEmpty( "[nzPosition]", _config.GetValue( AngularConst.BindPosition ) ); + return this; + } + + /// + /// 配置标签 + /// + public TimelineItemBuilder Label() { + AttributeIfNotEmpty( "nzLabel", _config.GetValue( UiConst.Label ) ); + AttributeIfNotEmpty( "[nzLabel]", _config.GetValue( AngularConst.BindLabel ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Color().Dot().Position().Label(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Timelines/Renders/TimelineItemRender.cs b/src/Util.Ui.NgZorro/Components/Timelines/Renders/TimelineItemRender.cs new file mode 100644 index 000000000..731183479 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Timelines/Renders/TimelineItemRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Timelines.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Timelines.Renders; + +/// +/// 时间轴节点渲染器 +/// +public class TimelineItemRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化时间轴节点渲染器 + /// + /// 配置 + public TimelineItemRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TimelineItemBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TimelineItemRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Timelines/Renders/TimelineRender.cs b/src/Util.Ui.NgZorro/Components/Timelines/Renders/TimelineRender.cs new file mode 100644 index 000000000..fe24ca79e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Timelines/Renders/TimelineRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Timelines.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Timelines.Renders; + +/// +/// 时间轴渲染器 +/// +public class TimelineRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化时间轴渲染器 + /// + /// 配置 + public TimelineRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TimelineBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TimelineRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Timelines/TimelineItemTagHelper.cs b/src/Util.Ui.NgZorro/Components/Timelines/TimelineItemTagHelper.cs new file mode 100644 index 000000000..2c956c35e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Timelines/TimelineItemTagHelper.cs @@ -0,0 +1,52 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Timelines.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Timelines; + +/// +/// 时间轴节点,生成的标签为<nz-timeline-item></nz-timeline-item> +/// +[HtmlTargetElement( "util-timeline-item" )] +public class TimelineItemTagHelper : AngularTagHelperBase { + /// + /// nzColor,扩展属性,指定圆圈颜色 + /// + public AntDesignColor ColorType { get; set; } + /// + /// nzColor,指定圆圈颜色,范例: 'blue' | 'red' | 'green' | 'gray' ,默认值: 'blue' + /// + public string Color { get; set; } + /// + /// [nzColor],指定圆圈颜色,范例: 'blue' | 'red' | 'green' | 'gray' ,默认值: 'blue' + /// + public string BindColor { get; set; } + /// + /// [nzDot],自定义时间轴点,类型: string | TemplateRef<void> + /// + public string Dot { get; set; } + /// + /// nzPosition,自定义节点位置,仅当时间轴的 nzMode 为 custom 时有效,可选值: 'left' | 'right' + /// + public TimelineItemPosition Position { get; set; } + /// + /// [nzPosition],自定义节点位置,仅当时间轴的 nzMode 为 custom 时有效,可选值: 'left' | 'right' + /// + public string BindPosition { get; set; } + /// + /// nzLabel,设置标签,用于单独显示时间 + /// + public string Label { get; set; } + /// + /// [nzLabel],设置标签,用于单独显示时间, 类型: string | TemplateRef<void> + /// + public string BindLabel { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new TimelineItemRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Timelines/TimelineTagHelper.cs b/src/Util.Ui.NgZorro/Components/Timelines/TimelineTagHelper.cs new file mode 100644 index 000000000..84a3825ba --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Timelines/TimelineTagHelper.cs @@ -0,0 +1,48 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Timelines.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Timelines; + +/// +/// 时间轴,生成的标签为<nz-timeline></nz-timeline> +/// +[HtmlTargetElement( "util-timeline" )] +public class TimelineTagHelper : AngularTagHelperBase { + /// + /// nzPending,设置幽灵节点的内容,幽灵节点显示在最后 + /// + public string Pending { get; set; } + /// + /// [nzPending],设置幽灵节点的内容,幽灵节点显示在最后,如果设置为 true 则显示默认幽灵节点,也可使用模板进行内容定制, 类型: string | boolean | TemplateRef<void>,默认值: false + /// + public string BindPending { get; set; } + /// + /// nzPendingDot,设置幽灵节点时间轴点 + /// + public string PendingDot { get; set; } + /// + /// [nzPendingDot],设置幽灵节点时间轴点,类型: string | TemplateRef<void>,默认值: <i nz-icon nzType="loading"></i> + /// + public string BindPendingDot { get; set; } + /// + /// [nzReverse],是否倒序排列,默认值: false + /// + public string Reverse { get; set; } + /// + /// nzMode,显示模式,可以改变时间轴和内容的相对位置,可选值: 'left' | 'alternate' | 'right' | 'custom' + /// + public TimelineMode Mode { get; set; } + /// + /// [nzMode],显示模式,可以改变时间轴和内容的相对位置,可选值: 'left' | 'alternate' | 'right' | 'custom' + /// + public string BindMode { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new TimelineRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Transfers/Builders/TransferBuilder.cs b/src/Util.Ui.NgZorro/Components/Transfers/Builders/TransferBuilder.cs new file mode 100644 index 000000000..31d0a0f18 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Transfers/Builders/TransferBuilder.cs @@ -0,0 +1,186 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.Transfers.Builders; + +/// +/// 穿梭框标签生成器 +/// +public class TransferBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化穿梭框标签生成器 + /// + /// 配置 + public TransferBuilder( Config config ) : base( config, "nz-transfer" ) { + _config = config; + } + + /// + /// 配置数据源 + /// + public TransferBuilder Datasource() { + AttributeIfNotEmpty( "[nzDataSource]", _config.GetValue( UiConst.Datasource ) ); + return this; + } + + /// + /// 配置禁用 + /// + public TransferBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置标题集合 + /// + public TransferBuilder Titles() { + AttributeIfNotEmpty( "[nzTitles]", _config.GetValue( UiConst.Titles ) ); + return this; + } + + /// + /// 配置操作按钮标题集合 + /// + public TransferBuilder Operations() { + AttributeIfNotEmpty( "[nzOperations]", _config.GetValue( UiConst.Operations ) ); + return this; + } + + /// + /// 配置样式 + /// + public TransferBuilder ListStyle() { + AttributeIfNotEmpty( "[nzListStyle]", _config.GetValue( UiConst.ListStyle ) ); + return this; + } + + /// + /// 配置单数项目单位 + /// + public TransferBuilder ItemUnit() { + AttributeIfNotEmpty( "nzItemUnit", _config.GetValue( UiConst.ItemUnit ) ); + AttributeIfNotEmpty( "[nzItemUnit]", _config.GetValue( AngularConst.BindItemUnit ) ); + return this; + } + + /// + /// 配置复数项目单位 + /// + public TransferBuilder ItemsUnit() { + AttributeIfNotEmpty( "nzItemsUnit", _config.GetValue( UiConst.ItemsUnit ) ); + AttributeIfNotEmpty( "[nzItemsUnit]", _config.GetValue( AngularConst.BindItemsUnit ) ); + return this; + } + + /// + /// 配置渲染列表 + /// + public TransferBuilder RenderList() { + AttributeIfNotEmpty( "[nzRenderList]", _config.GetValue( UiConst.RenderList ) ); + return this; + } + + /// + /// 配置行渲染模板 + /// + public TransferBuilder Render() { + AttributeIfNotEmpty( "[nzRender]", _config.GetValue( UiConst.Render ) ); + return this; + } + + /// + /// 配置底部渲染模板 + /// + public TransferBuilder Footer() { + AttributeIfNotEmpty( "[nzFooter]", _config.GetValue( UiConst.Footer ) ); + return this; + } + + /// + /// 配置显示搜索 + /// + public TransferBuilder ShowSearch() { + AttributeIfNotEmpty( "[nzShowSearch]", _config.GetValue( UiConst.ShowSearch ) ); + return this; + } + + /// + /// 配置过滤项函数 + /// + public TransferBuilder FilterOption() { + AttributeIfNotEmpty( "[nzFilterOption]", _config.GetValue( UiConst.FilterOption ) ); + return this; + } + + /// + /// 配置搜索框占位提示 + /// + public TransferBuilder SearchPlaceholder() { + AttributeIfNotEmpty( "nzSearchPlaceholder", _config.GetValue( UiConst.SearchPlaceholder ) ); + AttributeIfNotEmpty( "[nzSearchPlaceholder]", _config.GetValue( AngularConst.BindSearchPlaceholder ) ); + return this; + } + + /// + /// 配置空列表默认内容 + /// + public TransferBuilder NotFoundContent() { + AttributeIfNotEmpty( "nzNotFoundContent", _config.GetValue( UiConst.NotFoundContent ) ); + AttributeIfNotEmpty( "[nzNotFoundContent]", _config.GetValue( AngularConst.BindNotFoundContent ) ); + return this; + } + + /// + /// 配置可移动项函数 + /// + public TransferBuilder CanMove() { + AttributeIfNotEmpty( "[nzCanMove]", _config.GetValue( UiConst.CanMove ) ); + return this; + } + + /// + /// 配置选中项键列表 + /// + public TransferBuilder SelectedKeys() { + AttributeIfNotEmpty( "[nzSelectedKeys]", _config.GetValue( UiConst.SelectedKeys ) ); + return this; + } + + /// + /// 配置目标项键列表 + /// + public TransferBuilder TargetKeys() { + AttributeIfNotEmpty( "[nzTargetKeys]", _config.GetValue( UiConst.TargetKeys ) ); + return this; + } + + /// + /// 配置事件 + /// + public TransferBuilder Events() { + AttributeIfNotEmpty( "(nzChange)", _config.GetValue( UiConst.OnChange ) ); + AttributeIfNotEmpty( "(nzSearchChange)", _config.GetValue( UiConst.OnSearchChange ) ); + AttributeIfNotEmpty( "(nzSelectChange)", _config.GetValue( UiConst.OnSelectChange ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + Datasource().Disabled().Titles().Operations() + .ListStyle().ItemUnit().ItemsUnit() + .RenderList().Render().Footer() + .ShowSearch().FilterOption().SearchPlaceholder() + .NotFoundContent().CanMove().SelectedKeys().TargetKeys() + .Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Transfers/Renders/TransferRender.cs b/src/Util.Ui.NgZorro/Components/Transfers/Renders/TransferRender.cs new file mode 100644 index 000000000..a2f07ea83 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Transfers/Renders/TransferRender.cs @@ -0,0 +1,40 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Transfers.Builders; + +namespace Util.Ui.NgZorro.Components.Transfers.Renders; + +/// +/// 穿梭框渲染器 +/// +public class TransferRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化穿梭框渲染器 + /// + /// 配置 + public TransferRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new TransferBuilder( _config ); + builder.Config(); + _config.Content.AppendTo( builder ); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new TransferRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Transfers/TransferTagHelper.cs b/src/Util.Ui.NgZorro/Components/Transfers/TransferTagHelper.cs new file mode 100644 index 000000000..84a9e65a8 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Transfers/TransferTagHelper.cs @@ -0,0 +1,115 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Transfers.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Transfers; + +/// +/// 穿梭框,生成的标签为<nz-transfer></nz-transfer> +/// +[HtmlTargetElement( "util-transfer" )] +public class TransferTagHelper : AngularTagHelperBase { + /// + /// [nzDataSource],数据源 + /// + public string Datasource { get; set; } + /// + /// [nzDisabled],是否禁用 + /// + public string Disabled { get; set; } + /// + /// [nzTitles],标题集合,顺序从左至右,类型: string[],默认值: ['', ''],范例: ['Source', 'Target'] + /// + public string Titles { get; set; } + /// + /// [nzOperations],操作按钮标题集合,顺序从下至上,类型: string[],默认值: ['', ''],范例: ['to right', 'to left'] + /// + public string Operations { get; set; } + /// + /// [nzListStyle],自定义样式,等同 ngStyle,范例: { 'width.px': 300, 'height.px': 300 } + /// + public string ListStyle { get; set; } + /// + /// nzItemUnit,项目单位,只有一个项目时显示的单位,默认值: '项' + /// + public string ItemUnit { get; set; } + /// + /// [nzItemUnit],项目单位,只有一个项目时显示的单位,默认值: '项' + /// + public string BindItemUnit { get; set; } + /// + /// nzItemsUnit,项目单位,存在多个项目时显示的单位,默认值: '项' + /// + public string ItemsUnit { get; set; } + /// + /// [nzItemsUnit],项目单位,存在多个项目时显示的单位,默认值: '项' + /// + public string BindItemsUnit { get; set; } + /// + /// [nzRenderList],渲染列表,类型: Array<TemplateRef<void> | null> + /// + public string RenderList { get; set; } + /// + /// [nzRender], 每行数据渲染模板,类型: TemplateRef<void> + /// + public string Render { get; set; } + /// + /// [nzFooter], 底部渲染模板,类型: TemplateRef<void> + /// + public string Footer { get; set; } + /// + /// [nzShowSearch],是否显示搜索框,默认值: false + /// + public string ShowSearch { get; set; } + /// + /// [nzFilterOption],过滤项函数,接收 inputValue,item 两个参数,当 item 符合筛选条件时,应返回 true,反之则返回 false。函数定义: (inputValue: string, item: TransferItem) => boolean; + /// + public string FilterOption { get; set; } + /// + /// nzSearchPlaceholder,搜索框占位提示 + /// + public string SearchPlaceholder { get; set; } + /// + /// [nzSearchPlaceholder],搜索框占位提示 + /// + public string BindSearchPlaceholder { get; set; } + /// + /// nzNotFoundContent,当列表为空时显示的内容 + /// + public string NotFoundContent { get; set; } + /// + /// [nzNotFoundContent],当列表为空时显示的内容 + /// + public string BindNotFoundContent { get; set; } + /// + /// [nzCanMove],可移动项函数,用于穿梭时二次校验,注意:穿梭组件内部始终只保留一份数据,二次校验过程中需取消穿梭项则直接删除该项,函数定义: (arg: TransferCanMove) => Observable<TransferItem[]> + /// + public string CanMove { get; set; } + /// + /// [nzSelectedKeys],设置被选中的键列表,类型: string[] + /// + public string SelectedKeys { get; set; } + /// + /// [nzTargetKeys],设置显示在右侧框的键列表,类型: string[] + /// + public string TargetKeys { get; set; } + /// + /// (nzChange),转移改变事件,选项在两栏之间转移时触发,类型: EventEmitter<TransferChange> + /// + public string OnChange { get; set; } + /// + /// (nzSearchChange),搜索改变事件,搜索框内容改变时触发,类型: EventEmitter<TransferSearchChange> + /// + public string OnSearchChange { get; set; } + /// + /// (nzSelectChange),选中状态改变事件,选中项发生改变时触发,类型: EventEmitter<TransferSearchChange> + /// + public string OnSelectChange { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new TransferRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeSelects/Builders/TreeSelectBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeSelects/Builders/TreeSelectBuilder.cs new file mode 100644 index 000000000..c41aea450 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeSelects/Builders/TreeSelectBuilder.cs @@ -0,0 +1,387 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Trees.Helpers; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.TreeSelects.Builders; + +/// +/// 树形选择标签生成器 +/// +public class TreeSelectBuilder : FormControlBuilderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 树形服务 + /// + private readonly TreeService _service; + + /// + /// 初始化树选择标签生成器 + /// + /// 配置 + public TreeSelectBuilder( Config config ) : base( config, "nz-tree-select" ) { + _config = config; + _service = new TreeService( _config ); + } + + /// + /// 扩展标识 + /// + private string ExtendId => _service.ExtendId; + + /// + /// 配置输入框标识 + /// + public TreeSelectBuilder NzId() { + AttributeIfNotEmpty( "nzId", _config.GetValue( UiConst.NzId ) ); + AttributeIfNotEmpty( "[nzId]", _config.GetValue( AngularConst.BindNzId ) ); + return this; + } + + /// + /// 配置允许清除 + /// + public TreeSelectBuilder AllowClear() { + AttributeIfNotEmpty( "[nzAllowClear]", _config.GetValue( UiConst.AllowClear ) ); + return this; + } + + /// + /// 配置占位提示 + /// + public TreeSelectBuilder Placeholder() { + AttributeIfNotEmpty( "nzPlaceHolder", _config.GetValue( UiConst.Placeholder ) ); + AttributeIfNotEmpty( "[nzPlaceHolder]", _config.GetValue( AngularConst.BindPlaceholder ) ); + return this; + } + + /// + /// 配置禁用 + /// + public TreeSelectBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置是否显示图标 + /// + public TreeSelectBuilder ShowIcon() { + AttributeIfNotEmpty( "[nzShowIcon]", _config.GetValue( UiConst.ShowIcon ) ); + return this; + } + + /// + /// 配置显示搜索 + /// + public TreeSelectBuilder ShowSearch() { + AttributeIfNotEmpty( "[nzShowSearch]", _config.GetValue( UiConst.ShowSearch ) ); + return this; + } + + /// + /// 配置空列表默认内容 + /// + public TreeSelectBuilder NotFoundContent() { + AttributeIfNotEmpty( "nzNotFoundContent", _config.GetValue( UiConst.NotFoundContent ) ); + AttributeIfNotEmpty( "[nzNotFoundContent]", _config.GetValue( AngularConst.BindNotFoundContent ) ); + return this; + } + + /// + /// 配置下拉菜单和选择器同宽 + /// + public TreeSelectBuilder DropdownMatchSelectWidth() { + AttributeIfNotEmpty( "[nzDropdownMatchSelectWidth]", _config.GetValue( UiConst.DropdownMatchSelectWidth ) ); + return this; + } + + /// + /// 配置下拉菜单样式 + /// + public TreeSelectBuilder DropdownStyle() { + AttributeIfNotEmpty( "[nzDropdownStyle]", _config.GetValue( UiConst.DropdownStyle ) ); + return this; + } + + /// + /// 配置下拉菜单样式类名 + /// + public TreeSelectBuilder DropdownClassName() { + AttributeIfNotEmpty( "nzDropdownClassName", _config.GetValue( UiConst.DropdownClassName ) ); + AttributeIfNotEmpty( "[nzDropdownClassName]", _config.GetValue( AngularConst.BindDropdownClassName ) ); + return this; + } + + /// + /// 配置是否支持多选 + /// + public TreeSelectBuilder Multiple() { + AttributeIfNotEmpty( "[nzMultiple]", _config.GetValue( UiConst.Multiple ) ); + return this; + } + + /// + /// 配置是否搜索隐藏未匹配节点 + /// + public TreeSelectBuilder HideUnmatched() { + AttributeIfNotEmpty( "[nzHideUnMatched]", _config.GetValue( UiConst.HideUnmatched ) ); + return this; + } + + /// + /// 配置选择框大小 + /// + public TreeSelectBuilder Size() { + AttributeIfNotEmpty( "nzSize", _config.GetValue( UiConst.Size )?.Description() ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置选择框弹出位置 + /// + public TreeSelectBuilder Placement() { + AttributeIfNotEmpty( "nzPlacement", _config.GetValue( UiConst.Placement )?.Description() ); + AttributeIfNotEmpty( "[nzPlacement]", _config.GetValue( AngularConst.BindPlacement ) ); + return this; + } + + /// + /// 配置节点前是否添加复选框 + /// + public TreeSelectBuilder Checkable() { + Checkable( _config.GetValue( UiConst.Checkable ) ); + return this; + } + + /// + /// 配置节点前是否添加复选框 + /// + public TreeSelectBuilder Checkable( string value ) { + AttributeIfNotEmpty( "[nzCheckable]", value ); + return this; + } + + /// + /// 配置严格勾选 + /// + public TreeSelectBuilder CheckStrictly() { + AttributeIfNotEmpty( "[nzCheckStrictly]", _config.GetValue( UiConst.CheckStrictly ) ); + return this; + } + + /// + /// 配置是否显示展开图标 + /// + public TreeSelectBuilder ShowExpand() { + AttributeIfNotEmpty( "[nzShowExpand]", _config.GetValue( UiConst.ShowExpand ) ); + return this; + } + + /// + /// 配置是否显示连接线 + /// + public TreeSelectBuilder ShowLine() { + AttributeIfNotEmpty( "[nzShowLine]", _config.GetValue( UiConst.ShowLine ) ); + return this; + } + + /// + /// 配置是否异步加载 + /// + public TreeSelectBuilder AsyncData() { + AttributeIfNotEmpty( "[nzAsyncData]", _config.GetValue( UiConst.AsyncData ) ); + return this; + } + + /// + /// 配置节点数据 + /// + public TreeSelectBuilder Nodes() { + Nodes( _config.GetValue( UiConst.Nodes ) ); + return this; + } + + /// + /// 配置节点数据 + /// + public TreeSelectBuilder Nodes( string value ) { + AttributeIfNotEmpty( "[nzNodes]", value ); + return this; + } + + /// + /// 配置是否默认展开所有节点 + /// + public TreeSelectBuilder DefaultExpandAll() { + AttributeIfNotEmpty( "[nzDefaultExpandAll]", _config.GetValue( UiConst.ExpandAll ) ); + return this; + } + + /// + /// 配置默认展开节点的键集合 + /// + public TreeSelectBuilder ExpandedKeys() { + AttributeIfNotEmpty( "[nzExpandedKeys]", _config.GetValue( UiConst.ExpandedKeys ) ); + return this; + } + + /// + /// 配置显示函数 + /// + public TreeSelectBuilder DisplayWith() { + AttributeIfNotEmpty( "[nzDisplayWith]", _config.GetValue( UiConst.DisplayWith ) ); + return this; + } + + /// + /// 配置最大标签数量 + /// + public TreeSelectBuilder MaxTagCount() { + AttributeIfNotEmpty( "[nzMaxTagCount]", _config.GetValue( UiConst.MaxTagCount ) ); + return this; + } + + /// + /// 配置最大标签占位符 + /// + public TreeSelectBuilder MaxTagPlaceholder() { + AttributeIfNotEmpty( "[nzMaxTagPlaceholder]", _config.GetValue( UiConst.MaxTagPlaceholder ) ); + return this; + } + + /// + /// 配置自定义节点模板 + /// + public TreeSelectBuilder TreeTemplate() { + AttributeIfNotEmpty( "[nzTreeTemplate]", _config.GetValue( UiConst.TreeTemplate ) ); + return this; + } + + /// + /// 配置虚拟高度 + /// + public TreeSelectBuilder VirtualHeight() { + AttributeIfNotEmpty( "nzVirtualHeight", _config.GetValue( UiConst.VirtualHeight ) ); + AttributeIfNotEmpty( "[nzVirtualHeight]", _config.GetValue( AngularConst.BindVirtualHeight ) ); + return this; + } + + /// + /// 配置虚拟滚动列高 + /// + public TreeSelectBuilder VirtualItemSize() { + AttributeIfNotEmpty( "[nzVirtualItemSize]", _config.GetValue( UiConst.VirtualItemSize ) ); + return this; + } + + /// + /// 配置虚拟滚动缓冲区最大高度 + /// + public TreeSelectBuilder VirtualMaxBufferPx() { + AttributeIfNotEmpty( "[nzVirtualMaxBufferPx]", _config.GetValue( UiConst.VirtualMaxBufferPx ) ); + return this; + } + + /// + /// 配置虚拟滚动缓冲区最小高度 + /// + public TreeSelectBuilder VirtualMinBufferPx() { + AttributeIfNotEmpty( "[nzVirtualMinBufferPx]", _config.GetValue( UiConst.VirtualMinBufferPx ) ); + return this; + } + + /// + /// 配置宽度 + /// + public TreeSelectBuilder Width() { + var style = _config.GetValue( UiConst.Style ); + if ( style.Contains( "width" ) ) + return this; + var width = _config.GetValue( UiConst.Width ); + if ( width.IsEmpty() ) + return this; + if ( Util.Helpers.Validation.IsNumber( width ) ) + width = $"{width}px"; + if ( style.IsEmpty() == false ) + style += ";"; + style = $"{style}width:{width}"; + _config.SetAttribute( UiConst.Style, style ); + return this; + } + + /// + /// 配置校验状态 + /// + public TreeSelectBuilder Status() { + AttributeIfNotEmpty( "nzStatus", _config.GetValue( UiConst.Status )?.Description() ); + AttributeIfNotEmpty( "[nzStatus]", _config.GetValue( AngularConst.BindStatus ) ); + return this; + } + + /// + /// 配置浮层是否应带有背景板 + /// + public TreeSelectBuilder Backdrop() { + AttributeIfNotEmpty( "[nzBackdrop]", _config.GetValue( UiConst.Backdrop ) ); + return this; + } + + /// + /// 配置事件 + /// + public TreeSelectBuilder Events() { + OnExpandChange( _config.GetValue( UiConst.OnExpandChange ) ); + return this; + } + + /// + /// 展开收缩节点事件 + /// + public TreeSelectBuilder OnExpandChange( string value ) { + AttributeIfNotEmpty( "(nzExpandChange)", value ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + ConfigForm().NzId().Name().AllowClear().Placeholder().Disabled().ShowIcon() + .ShowSearch().NotFoundContent().DropdownMatchSelectWidth() + .DropdownStyle().DropdownClassName().Multiple() + .HideUnmatched().Size().Placement().Checkable().CheckStrictly() + .ShowExpand().ShowLine().AsyncData().Nodes() + .DefaultExpandAll().ExpandedKeys().DisplayWith() + .MaxTagCount().MaxTagPlaceholder().TreeTemplate() + .VirtualHeight().VirtualItemSize().VirtualMaxBufferPx().VirtualMinBufferPx() + .Width().Status().Backdrop() + .Events(); + base.ConfigBase( _config ); + _service.ConfigBuilder( this ); + ConfigDefault(); + } + + /// + /// 配置默认属性 + /// + private void ConfigDefault() { + if ( _service.IsEnableExtend() == false ) + return; + Nodes( $"{ExtendId}.dataSource" ) + .LoadKeys() + .OnExpandChange( $"{ExtendId}.expandChange($event)" ); + } + + /// + /// 配置加载标识列表 + /// + public TreeSelectBuilder LoadKeys() { + AttributeIfNotEmpty( "[loadKeys]", _config.GetValue( UiConst.LoadKeys ) ); + return this; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeSelects/Renders/TreeSelectRender.cs b/src/Util.Ui.NgZorro/Components/TreeSelects/Renders/TreeSelectRender.cs new file mode 100644 index 000000000..9d0e919b7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeSelects/Renders/TreeSelectRender.cs @@ -0,0 +1,55 @@ +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.TreeSelects.Builders; + +namespace Util.Ui.NgZorro.Components.TreeSelects.Renders; + +/// +/// 树选择渲染器 +/// +public class TreeSelectRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化树选择渲染器 + /// + /// 配置 + public TreeSelectRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 初始化 + /// + protected override void Init() { + SetControlId(); + } + + /// + /// 设置控件Id + /// + protected void SetControlId() { + if ( ShareConfig.AutoNzFor != true ) + return; + _config.SetAttribute( UiConst.NzId, ShareConfig.ControlId ); + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new TreeSelectBuilder( _config ); + builder.Config(); + _config.Content.AppendTo( builder ); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new TreeSelectRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeSelects/TreeSelectTagHelper.cs b/src/Util.Ui.NgZorro/Components/TreeSelects/TreeSelectTagHelper.cs new file mode 100644 index 000000000..249ea789f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeSelects/TreeSelectTagHelper.cs @@ -0,0 +1,289 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Applications.Trees; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Inputs.Helpers; +using Util.Ui.NgZorro.Components.TreeSelects.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.TreeSelects; + +/// +/// 树形选择,生成的标签为<nz-tree-select></nz-tree-select> +/// +[HtmlTargetElement( "util-tree-select" )] +public class TreeSelectTagHelper : FormControlTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// nzId,组件内部 input 的 id 值 + /// + public string NzId { get; set; } + /// + /// [nzId],组件内部 input 的 id 值 + /// + public string BindNzId { get; set; } + /// + /// [nzNodes],数据源,类型: NzTreeNodeOptions[] + /// + public string Nodes { get; set; } + /// + /// nzPlaceHolder,占位提示 + /// + public string Placeholder { get; set; } + /// + /// [nzPlaceHolder],占位提示 + /// + public string BindPlaceholder { get; set; } + /// + /// [nzDefaultExpandAll],是否默认展开所有节点,类型: boolean, 默认值: false + /// + public string ExpandAll { get; set; } + /// + /// [nzExpandedKeys],指定展开的节点标识列表,类型: string[] + /// + public string ExpandedKeys { get; set; } + /// + /// [nzShowSearch],是否显示搜索框,类型: boolean, 默认值: false + /// + public string ShowSearch { get; set; } + /// + /// [nzAllowClear],是否允许清除,类型: boolean, 默认值: true + /// + public string AllowClear { get; set; } + /// + /// [nzDisabled],是否禁用,类型: boolean, 默认值: false + /// + public string Disabled { get; set; } + /// + /// [nzMultiple],是否支持多选,类型: boolean, 默认值: false,当设置 nzCheckable 时为 true + /// + public string Multiple { get; set; } + /// + /// [nzCheckable],节点前是否添加复选框,类型: boolean, 默认值: false + /// + public string Checkable { get; set; } + /// + /// [nzCheckStrictly],严格勾选,父子节点选中状态不再关联,类型: boolean, 默认值: false + /// + public string CheckStrictly { get; set; } + /// + /// [nzShowIcon],是否显示图标,类型: boolean, 默认值: false + /// + public string ShowIcon { get; set; } + /// + /// [nzShowExpand],节点前是否显示展开图标,类型: boolean, 默认值: true + /// + public string ShowExpand { get; set; } + /// + /// [nzShowLine],是否显示连接线,类型: boolean, 默认值: false + /// + public string ShowLine { get; set; } + /// + /// [nzAsyncData],是否异步加载,类型: boolean, 默认值: false + /// + public string AsyncData { get; set; } + /// + /// [nzMaxTagCount],最多显示的标签数量,类型: number + /// + public string MaxTagCount { get; set; } + /// + /// [nzMaxTagPlaceholder],隐藏标签时显示的占位内容,类型: TemplateRef<{ $implicit: NzTreeNode[] }> + /// + public string MaxTagPlaceholder { get; set; } + /// + /// [nzHideUnMatched],是否搜索时隐藏未匹配的节点,类型: boolean, 默认值: false + /// + public string HideUnmatched { get; set; } + /// + /// nzSize,选择框尺寸,可选值: 'large'|'small'|'default',默认值: 'default' + /// + public InputSize Size { get; set; } + /// + /// [nzSize],选择框尺寸,可选值: 'large'|'small'|'default',默认值: 'default' + /// + public string BindSize { get; set; } + /// + /// nzPlacement,选择框弹出位置,可选值: 'bottomLeft'| 'bottomRight' | 'topLeft' | 'topRight',默认值: 'bottomLeft' + /// + public TreeSelectPlacement Placement { get; set; } + /// + /// [nzPlacement],选择框弹出位置,可选值: 'bottomLeft'| 'bottomRight' | 'topLeft' | 'topRight',默认值: 'bottomLeft' + /// + public string BindPlacement { get; set; } + /// + /// nzVirtualHeight,虚拟滚动的总高度,范例: '300px' + /// + public string VirtualHeight { get; set; } + /// + /// [nzVirtualHeight],虚拟滚动的总高度,范例: '300px' + /// + public string BindVirtualHeight { get; set; } + /// + /// [nzVirtualItemSize],虚拟滚动时每一列的高度,类型: number, 单位:像素, 默认值: 28 + /// + public string VirtualItemSize { get; set; } + /// + /// [nzVirtualMaxBufferPx],虚拟滚动缓冲区最大高度,类型: number, 单位:像素,默认值: 500 + /// + public string VirtualMaxBufferPx { get; set; } + /// + /// [nzVirtualMinBufferPx],虚拟滚动缓冲区最小高度,低于该值时将加载新结构,类型: number, 单位:像素,默认值: 28 + /// + public string VirtualMinBufferPx { get; set; } + /// + /// [nzDropdownMatchSelectWidth],是否下拉菜单和选择器同宽,类型: boolean, 默认值: true + /// + public string DropdownMatchSelectWidth { get; set; } + /// + /// [nzDropdownStyle],下拉菜单样式,类型: { [key: string]: string; },范例: { 'max-height': '300px' } + /// + public string DropdownStyle { get; set; } + /// + /// nzDropdownClassName,下拉菜单样式类名 + /// + public string DropdownClassName { get; set; } + /// + /// [nzDropdownClassName],下拉菜单样式类名 + /// + public string BindDropdownClassName { get; set; } + /// + /// nzNotFoundContent,当下拉列表为空时显示的内容 + /// + public string NotFoundContent { get; set; } + /// + /// [nzNotFoundContent],当下拉列表为空时显示的内容 + /// + public string BindNotFoundContent { get; set; } + /// + /// nzStatus,校验状态, 可选值: 'error' | 'warning' + /// + public FormControlStatus Status { get; set; } + /// + /// [nzStatus],校验状态, 可选值: 'error' | 'warning' + /// + public string BindStatus { get; set; } + /// + /// [nzBackdrop],浮层是否应带有背景板,类型: boolean, 默认值: false + /// + public string Backdrop { get; set; } + /// + /// [nzDisplayWith],在输入框显示所选节点值的函数,类型: (node: NzTreeNode) => string,默认值: (node: NzTreeNode) => node.title + /// + public string DisplayWith { get; set; } + /// + /// [nzTreeTemplate],自定义节点模板,类型: TemplateRef<{ $implicit: NzTreeNode }> + /// + public string TreeTemplate { get; set; } + /// + /// 扩展属性,是否启用扩展指令,当设置Api地址时自动启用,默认为 false + /// + public bool EnableExtend { get; set; } + /// + /// 扩展属性 [autoLoad],初始化时是否自动加载数据,默认为true,设置成false则手工加载 + /// + public bool AutoLoad { get; set; } + /// + /// 扩展属性 loadMode,加载模式,默认为同步加载 + /// + public LoadMode LoadMode { get; set; } + /// + /// 扩展属性 [isExpandForRootAsync],根节点异步加载模式是否展开子节点,默认为 true + /// + public bool ExpandForRootAsync { get; set; } + /// + /// 扩展属性[(queryParam)],查询参数 + /// + public string QueryParam { get; set; } + /// + /// 扩展属性 order,排序条件,范例: creationTime desc + /// + public string Sort { get; set; } + /// + /// 扩展属性 [order],排序条件,范例: creationTime desc + /// + public string BindSort { get; set; } + /// + /// 扩展属性 url,Api地址 + /// + public string Url { get; set; } + /// + /// 扩展属性 [url],Api地址 + /// + public string BindUrl { get; set; } + /// + /// 扩展属性 loadUrl,首次加载地址,对于异步请求,仅加载第一级节点,如果未设置该属性,则使用 url 地址 + /// + public string LoadUrl { get; set; } + /// + /// 扩展属性 [loadUrl],首次加载地址,对于异步请求,仅加载第一级节点,如果未设置该属性,则使用 url 地址 + /// + public string BindLoadUrl { get; set; } + /// + /// 扩展属性 queryUrl,查询地址,如果未设置该属性,则使用 url 地址 + /// + public string QueryUrl { get; set; } + /// + /// 扩展属性 [queryUrl],查询地址,如果未设置该属性,则使用 url 地址 + /// + public string BindQueryUrl { get; set; } + /// + /// 扩展属性 loadChildrenUrl,加载子节点地址,如果未设置该属性,则使用 url 地址 + /// + public string LoadChildrenUrl { get; set; } + /// + /// 扩展属性 [loadChildrenUrl],加载子节点地址,如果未设置该属性,则使用 url 地址 + /// + public string BindLoadChildrenUrl { get; set; } + /// + /// 扩展属性,宽度,如果设置数字,则单位为px,范例:100,表示style="width:100px",也可以设置百分比,范例:25%,表示style="width:25%" + /// + public string Width { get; set; } + /// + /// [required],是否必填项, 类型: boolean, 默认值: false + /// + public string Required { get; set; } + /// + /// 扩展属性 requiredMessage,必填项验证消息 + /// + public string RequiredMessage { get; set; } + /// + /// 扩展属性 [requiredMessage],必填项验证消息 + /// + public string BindRequiredMessage { get; set; } + /// + /// (nzExpandChange),展开收缩节点事件,类型: EventEmitter<NzFormatEmitEvent> + /// + public string OnExpandChange { get; set; } + /// + /// 扩展属性 [onLoadChildrenBefore],子节点加载前事件,返回false停止加载,类型: (node:NzTreeNode) => boolean,参数为父节点 + /// + public string OnLoadChildrenBefore { get; set; } + /// + /// 扩展事件 (onLoadChildren),子节点加载完成事件,参数为{ node: NzTreeNode, result } + /// + public string OnLoadChildren { get; set; } + /// + /// 扩展事件 (onExpand),节点展开事件,参数为 NzTreeNode + /// + public string OnExpand { get; set; } + /// + /// 扩展事件 (onCollapse),节点折叠事件,参数为 NzTreeNode + /// + public string OnCollapse { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new InputService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new TreeSelectRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/Contents/TreeTableSelectCreateService.cs b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/Contents/TreeTableSelectCreateService.cs new file mode 100644 index 000000000..63d813ae2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/Contents/TreeTableSelectCreateService.cs @@ -0,0 +1,48 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Containers.Builders; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.NgZorro.Components.Tables.Builders.Contents; + +namespace Util.Ui.NgZorro.Components.TreeTables.Builders.Contents; + +/// +/// 树形表格列选择框创建服务 +/// +public class TreeTableSelectCreateService : ISelectCreateService { + /// + public void CreateCheckbox( TableColumnBuilder builder, IHtmlContent content ) { + var checkBoxBuilder = new TreeTableCheckBoxBuilder( builder.GetTableColumnShareConfig().TableExtendId ); + checkBoxBuilder.AppendContent( content ); + builder.AddCheckbox( checkBoxBuilder ); + } + + /// + public void CreateRadio( TableColumnBuilder builder, IHtmlContent content ) { + var config = builder.GetConfig(); + var shareConfig = builder.GetTableColumnShareConfig(); + var tableExtendId = shareConfig.TableExtendId; + if ( shareConfig.IsCheckLeafOnly == false ) { + builder.AddRadio( new TableColumnRadioBuilder( config, tableExtendId, content ) ); + return; + } + var radioBuilder = new TreeTableColumnRadioBuilder( config, tableExtendId, content ); + builder.AddRadio( radioBuilder ); + var containerBuilder = new ContainerBuilder( config ); + containerBuilder.NgIf( $"!{tableExtendId}.isShowRadio(row)" ); + containerBuilder.SetContent( content ); + builder.AppendContent( containerBuilder ); + } + + /// + /// 是否将内容添加到选择框 + /// + /// 表格单元格标签生成器 + public bool IsAddContentToSelect( TableColumnBuilder builder ) { + var shareConfig = builder.GetTableColumnShareConfig(); + if ( shareConfig.IsShowCheckbox ) + return true; + if ( shareConfig.IsShowRadio ) + return true; + return false; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableBodyBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableBodyBuilder.cs new file mode 100644 index 000000000..04b8743a0 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableBodyBuilder.cs @@ -0,0 +1,48 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.NgZorro.Configs; + +namespace Util.Ui.NgZorro.Components.TreeTables.Builders; + +/// +/// 树形表格主体标签生成器 +/// +public class TreeTableBodyBuilder : TableBodyBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化树形表格主体标签生成器 + /// + /// 配置 + public TreeTableBodyBuilder( Config config ) : base( config ) { + _config = config; + } + + /// + /// 创建表格主体行标签生成器 + /// + public override TagBuilder CreateTableBodyRowBuilder() { + return CreateContainerBuilder(); + } + + /// + /// 创建容器标签生成器 + /// + private TreeTableContainerBuilder CreateContainerBuilder() { + var shareConfig = GetTableShareConfig(); + var containerBuilder = new TreeTableContainerBuilder( _config.CopyRemoveAttributes(), CreateRowBuilder() ); + containerBuilder.NgFor( $"let row of {shareConfig.TableExtendId}.dataSource;index as index" ); + return containerBuilder; + } + + /// + /// 创建行标签生成器 + /// + private TreeTableBodyRowBuilder CreateRowBuilder() { + return new TreeTableBodyRowBuilder( _config.CopyRemoveAttributes() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableBodyRowBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableBodyRowBuilder.cs new file mode 100644 index 000000000..a318b2ad7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableBodyRowBuilder.cs @@ -0,0 +1,32 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Tables.Builders; + +namespace Util.Ui.NgZorro.Components.TreeTables.Builders; + +/// +/// 树形表格主体行标签生成器 +/// +public class TreeTableBodyRowBuilder : TableBodyRowBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化树形表格主体行标签生成器 + /// + public TreeTableBodyRowBuilder( Config config ) : base( config ) { + _config = config; + } + + /// + /// 配置表格主体行基础扩展属性 + /// + protected override void ConfigTableExtend() { + if ( TableShareConfig.IsEnableExtend == false ) + return; + if ( TableShareConfig.IsAutoCreateBodyRow == false ) + return; + this.NgIf( $"{TableExtendId}.isShow(row)" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableBuilder.cs new file mode 100644 index 000000000..ff208f5c4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableBuilder.cs @@ -0,0 +1,125 @@ +using Util.Applications.Trees; +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.NgZorro.Configs; + +namespace Util.Ui.NgZorro.Components.TreeTables.Builders; + +/// +/// 树形表格标签生成器 +/// +public class TreeTableBuilder : TableBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化树形表格标签生成器 + /// + /// 配置 + public TreeTableBuilder( Config config ) : base( config ) { + _config = config; + } + + /// + public override TableHeadBuilder CreateTableHeadBuilder() { + return new TreeTableHeadBuilder( _config.CopyRemoveAttributes() ); + } + + /// + public override TableBodyBuilder CreateTableBodyBuilder() { + return new TreeTableBodyBuilder( _config.CopyRemoveAttributes() ); + } + + /// + /// 配置加载地址 + /// + public TreeTableBuilder LoadUrl() { + AttributeIfNotEmpty( "loadUrl", _config.GetValue( UiConst.LoadUrl ) ); + AttributeIfNotEmpty( "[loadUrl]", _config.GetValue( AngularConst.BindLoadUrl ) ); + return this; + } + + /// + /// 配置查询地址 + /// + public TreeTableBuilder QueryUrl() { + AttributeIfNotEmpty( "queryUrl", _config.GetValue( UiConst.QueryUrl ) ); + AttributeIfNotEmpty( "[queryUrl]", _config.GetValue( AngularConst.BindQueryUrl ) ); + return this; + } + + /// + /// 配置加载子节点地址 + /// + public TreeTableBuilder LoadChildrenUrl() { + AttributeIfNotEmpty( "loadChildrenUrl", _config.GetValue( UiConst.LoadChildrenUrl ) ); + AttributeIfNotEmpty( "[loadChildrenUrl]", _config.GetValue( AngularConst.BindLoadChildrenUrl ) ); + return this; + } + + /// + /// 配置加载模式 + /// + public TreeTableBuilder LoadMode() { + var mode = _config.GetValue( UiConst.LoadMode ); + if ( mode == Applications.Trees.LoadMode.Sync ) + ShowPagination( "false" ); + AttributeIfNotEmpty( "loadMode", mode?.Value().SafeString() ); + return this; + } + + /// + /// 配置展开 + /// + public TreeTableBuilder ExpandAll() { + AttributeIfNotEmpty( "[isExpandAll]", _config.GetBoolValue( UiConst.ExpandAll ) ); + return this; + } + + /// + /// 配置根节点异步加载模式是否展开子节点 + /// + public TreeTableBuilder ExpandForRootAsync() { + AttributeIfNotEmpty( "[isExpandForRootAsync]", _config.GetBoolValue( UiConst.ExpandForRootAsync ) ); + return this; + } + + /// + /// 配置仅能选择叶节点 + /// + public TreeTableBuilder CheckLeafOnly() { + AttributeIfNotEmpty( "[isCheckLeafOnly]", _config.GetBoolValue( UiConst.CheckLeafOnly ) ); + return this; + } + + /// + /// 配置事件 + /// + public new TreeTableBuilder Events() { + AttributeIfNotEmpty( "[onLoadChildrenBefore]", _config.GetValue( UiConst.OnLoadChildrenBefore ) ); + AttributeIfNotEmpty( "(onLoadChildren)", _config.GetValue( UiConst.OnLoadChildren ) ); + AttributeIfNotEmpty( "(onExpand)", _config.GetValue( UiConst.OnExpand ) ); + AttributeIfNotEmpty( "(onCollapse)", _config.GetValue( UiConst.OnCollapse ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + LoadUrl().QueryUrl().LoadChildrenUrl() + .LoadMode().ExpandAll().ExpandForRootAsync().CheckLeafOnly() + .Events(); + } + + /// + protected override void ConfigExtend() { + Attribute( "x-tree-table-extend" ); + Attribute( $"#{ExtendId}", "xTreeTableExtend" ); + ConfigDefault(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableCheckBoxBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableCheckBoxBuilder.cs new file mode 100644 index 000000000..a3b7a8d3a --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableCheckBoxBuilder.cs @@ -0,0 +1,19 @@ +using Util.Ui.Builders; + +namespace Util.Ui.NgZorro.Components.TreeTables.Builders; + +/// +/// NgZorro树形表格复选框生成器 +/// +public class TreeTableCheckBoxBuilder : TagBuilder { + /// + /// 初始化NgZorro树形表格复选框生成器 + /// + /// 表格扩展标识 + public TreeTableCheckBoxBuilder( string tableExtendId ) : base( "label" ) { + base.Attribute( "nz-checkbox" ); + base.Attribute( "[nzIndeterminate]", $"{tableExtendId}.isIndeterminate(row)" ); + base.Attribute( "[nzChecked]", $"{tableExtendId}.isChecked(row)" ); + base.Attribute( "(nzCheckedChange)", $"{tableExtendId}.toggle(row)" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableColumnBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableColumnBuilder.cs new file mode 100644 index 000000000..5b56e6654 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableColumnBuilder.cs @@ -0,0 +1,86 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.NgZorro.Components.Tables.Builders.Contents; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Components.TreeTables.Builders.Contents; + +namespace Util.Ui.NgZorro.Components.TreeTables.Builders; + +/// +/// 树形表格单元格标签生成器 +/// +public class TreeTableColumnBuilder : TableColumnBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表格列共享配置 + /// + private readonly TableColumnShareConfig _shareConfig; + + /// + /// 初始化树形表格单元格标签生成器 + /// + public TreeTableColumnBuilder( Config config, TableColumnShareConfig shareConfig ) : base( config, shareConfig ) { + _config = config; + _shareConfig = shareConfig; + } + + /// + /// 配置内容 + /// + protected override void ConfigContent() { + var service = new TableColumnContentService( this,new TreeTableSelectCreateService() ); + service.Config(); + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + ConfigDefault(); + } + + /// + /// 配置默认属性 + /// + protected void ConfigDefault() { + if ( _shareConfig.IsFirst == false ) + return; + if ( _shareConfig.IsEnableExtend == false ) + return; + var tableExtendId = _shareConfig.TableExtendId; + Attribute( "[nzIndentSize]", $"row.level*{tableExtendId}.config.table.indentUnitWidth" ); + Attribute( "[nzShowExpand]", $"!{tableExtendId}.isLeaf(row)" ); + Attribute( "[nzExpand]", $"{tableExtendId}.isExpand(row)" ); + Attribute( "(nzExpandChange)", $"{tableExtendId}.collapse(row,$event)" ); + } + + /// + /// 配置对齐方式 + /// + public override TableColumnBuilder Align() { + if (_shareConfig.IsFirst) + return this; + if ( _shareConfig.IsEnableTableSettings ) { + BindAlign( $"{_shareConfig.TableSettingsId}.getAlign('{_shareConfig.Title}')" ); + return this; + } + AttributeIfNotEmpty( "nzAlign", _shareConfig.Align ); + BindAlign( _config.GetValue( AngularConst.BindAlign ) ); + return this; + } + + /// + /// 配置对齐方式 + /// + public override TableColumnBuilder Align( string title ) { + if ( _shareConfig.IsFirst ) + return this; + if ( _shareConfig.IsEnableTableSettings ) + BindAlign( $"{_shareConfig.TableSettingsId}.getAlign('{title}')" ); + return this; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableColumnRadioBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableColumnRadioBuilder.cs new file mode 100644 index 000000000..d8bc14d07 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableColumnRadioBuilder.cs @@ -0,0 +1,28 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Extensions; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.TreeTables.Builders; + +/// +/// 树形表格单元格单选框标签生成器 +/// +public class TreeTableColumnRadioBuilder : AngularTagBuilder { + /// + /// 初始化树形表格单元格单选框标签生成器 + /// + /// 配置 + /// 表格扩展标识 + /// 内容 + public TreeTableColumnRadioBuilder( Config config,string tableExtendId, IHtmlContent content = null ) : base( config, "label" ) { + base.Attribute( "nz-radio" ); + base.Attribute( "(click)", "$event.stopPropagation()" ); + this.NgIf( $"{tableExtendId}.isShowRadio(row)" ); + base.Attribute( "name", $"r_{tableExtendId}" ); + base.Attribute( "[ngModel]", $"{tableExtendId}.isChecked(row)" ); + base.Attribute( "(ngModelChange)", $"{tableExtendId}.checkRowOnly(row)" ); + if ( content == null ) + return; + base.SetContent( content ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableContainerBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableContainerBuilder.cs new file mode 100644 index 000000000..a0a0381ab --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableContainerBuilder.cs @@ -0,0 +1,35 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.TreeTables.Builders; + +/// +/// 树形表格主体行容器标签生成器 +/// +public class TreeTableContainerBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 树形表格主体行标签生成器 + /// + private readonly TreeTableBodyRowBuilder _rowBuilder; + + /// + /// 初始化树形表格主体行容器标签生成器 + /// + /// 配置 + /// 树形表格主体行标签生成器 + public TreeTableContainerBuilder( Config config, TreeTableBodyRowBuilder rowBuilder ) : base( config, "ng-container" ) { + _config = config; + _rowBuilder = rowBuilder; + } + + /// + /// 配置 + /// + public override void Config() { + _rowBuilder.Config(); + AppendContent( _rowBuilder ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableHeadBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableHeadBuilder.cs new file mode 100644 index 000000000..65f5b2f5a --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableHeadBuilder.cs @@ -0,0 +1,30 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.NgZorro.Configs; + +namespace Util.Ui.NgZorro.Components.TreeTables.Builders; + +/// +/// 树形表头标签生成器 +/// +public class TreeTableHeadBuilder : TableHeadBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化树形表头标签生成器 + /// + /// 配置 + public TreeTableHeadBuilder( Config config ) : base( config ) { + _config = config; + } + + /// + /// 创建表头行标签生成器 + /// + public override TableHeadRowBuilder CreateTableHeadRowBuilder() { + return new TreeTableHeadRowBuilder( _config.CopyRemoveAttributes() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableHeadColumnBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableHeadColumnBuilder.cs new file mode 100644 index 000000000..4583f510b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableHeadColumnBuilder.cs @@ -0,0 +1,170 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Components.Tables.Helpers; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +namespace Util.Ui.NgZorro.Components.TreeTables.Builders; + +/// +/// 树形表头单元格标签生成器 +/// +public class TreeTableHeadColumnBuilder : TableHeadColumnBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 表头列共享配置 + /// + private readonly TableHeadColumnShareConfig _shareConfig; + + /// + /// 初始化树形表头单元格标签生成器 + /// + /// 配置 + /// 表头列共享配置 + public TreeTableHeadColumnBuilder( Config config, TableHeadColumnShareConfig shareConfig ) : base( config, shareConfig ) { + _config = config; + _shareConfig = shareConfig; + } + + /// + /// 配置标题对齐方式 + /// + public override TableHeadColumnBuilder TitleAlign() { + if ( _shareConfig.IsFirst ) + return this; + if ( _shareConfig.IsEnableTableSettings ) { + var title = _config.GetValue( UiConst.Title ); + BindTitleAlign( $"{_shareConfig.TableSettingsId}.getTitleAlign('{title}')" ); + return this; + } + AttributeIfNotEmpty( "titleAlign", _shareConfig.TitleAlign ); + BindTitleAlign( _config.GetValue( AngularConst.BindTitleAlign ) ); + return this; + } + + /// + /// 配置标题对齐方式 + /// + public override TableHeadColumnBuilder TitleAlign( string title ) { + if ( _shareConfig.IsFirst ) + return this; + if ( _shareConfig.IsEnableTableSettings ) + BindTitleAlign( $"{_shareConfig.TableSettingsId}.getTitleAlign('{title}')" ); + return this; + } + + /// + /// 配置对齐方式 + /// + public override TableHeadColumnBuilder Align() { + if ( _shareConfig.IsFirst ) + return this; + if ( _shareConfig.IsEnableTableSettings ) + return this; + AttributeIfNotEmpty( "nzAlign", _config.GetValue( UiConst.Align )?.Description() ); + BindAlign( _config.GetValue( AngularConst.BindAlign ) ); + return this; + } + + /// + protected override void ConfigContent( Config config ) { + if ( _shareConfig.IsFirst && _shareConfig.IsShowCheckbox ) + return; + base.ConfigContent( config ); + } + + /// + /// 添加表头单元格 + /// + /// 列 + public override void AddColumn( ColumnInfo column ) { + if ( column.IsFirst ) + AddFirstColumn( column ); + else + Title( column.Title ); + if ( column.IsSort ) + Sort( column.Column ); + SetColumnWidth( column ); + Left( column.IsLeft, column.Title ); + Right( column.IsRight, column.Title ); + SetAcl( column.Acl, column.AclElseTemplateId ); + EnableCustomColumn( column.GetCellControl() ); + EnableResizable( column ); + TitleAlign( column.Title ); + } + + /// + /// 添加首列 + /// + private void AddFirstColumn( ColumnInfo column ) { + if ( _shareConfig.IsShowCheckbox ) { + AddCheckBox( column.Title ); + return; + } + if ( _shareConfig.IsShowCheckbox == false && _shareConfig.IsShowRadio ) { + AddRadio( column.Title ); + return; + } + if ( _shareConfig.IsShowLineNumber ) { + AddLineNumber(); + return; + } + Title( column.Title ); + } + + /// + /// 配置拖动调整列宽 + /// + public override TableHeadColumnBuilder Resizable( string title ) { + Attribute( "nz-resizable" ); + Attribute( "nzPreview" ); + Attribute( "nzBounds", "window" ); + Attribute( "(nzResizeEnd)", $"{_shareConfig.TableSettingsId}.handleResize($event,'{title}')" ); + if (_shareConfig.IsFirst) { + if( _shareConfig.IsShowCheckbox ) + return this; + if ( _shareConfig.IsShowRadio ) + return this; + } + AppendHandleBuilder(); + return this; + } + + /// + protected override void AddCheckBox( string title = null ) { + title = GetTitle( title ); + if ( title.IsEmpty() ) + title = _config.Content?.GetContent(); + var checkboxBuilder = new TreeTableMasterCheckBoxBuilder( _shareConfig.TableExtendId, title ); + SetContent( checkboxBuilder ); + if ( _shareConfig.IsEnableResizable ) + AppendHandleBuilder(); + Left( _shareConfig.IsCheckboxLeft ); + } + + /// + /// 获取标题 + /// + /// 标题 + private string GetTitle( string title ) { + title ??= _config.GetValue( UiConst.Title ); + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) + return "{{" + $"'{title}'|i18n" + "}}"; + return title; + } + + /// + protected override void AddRadio( string title = null ) { + title = GetTitle( title ); + if ( title.IsEmpty() ) + title = _config.Content?.GetContent(); + SetContent( title ); + if ( _shareConfig.IsEnableResizable ) + AppendHandleBuilder(); + Left( _shareConfig.IsRadioLeft ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableHeadRowBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableHeadRowBuilder.cs new file mode 100644 index 000000000..650f6eb44 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableHeadRowBuilder.cs @@ -0,0 +1,29 @@ +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Tables.Builders; +using Util.Ui.NgZorro.Configs; + +namespace Util.Ui.NgZorro.Components.TreeTables.Builders; + +/// +/// 树形表头行标签生成器 +/// +public class TreeTableHeadRowBuilder : TableHeadRowBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化树形表格主体行标签生成器 + /// + public TreeTableHeadRowBuilder( Config config ) : base( config ) { + _config = config; + } + + /// + /// 创建表头单元格标签生成器 + /// + public override TableHeadColumnBuilder CreateTableHeadColumnBuilder() { + return new TreeTableHeadColumnBuilder( _config.CopyRemoveAttributes(), GetTableHeadColumnShareConfig() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableMasterCheckBoxBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableMasterCheckBoxBuilder.cs new file mode 100644 index 000000000..e6724c1df --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeTables/Builders/TreeTableMasterCheckBoxBuilder.cs @@ -0,0 +1,21 @@ +using Util.Ui.Builders; + +namespace Util.Ui.NgZorro.Components.TreeTables.Builders; + +/// +/// NgZorro树形表格标题复选框生成器 +/// +public class TreeTableMasterCheckBoxBuilder : TagBuilder { + /// + /// 初始化NgZorro树形表格标题复选框生成器 + /// + /// 表格扩展标识 + /// 标题 + public TreeTableMasterCheckBoxBuilder( string tableExtendId, string title ) : base( "label" ) { + base.Attribute( "nz-checkbox" ); + base.Attribute( "[nzIndeterminate]", $"{tableExtendId}.isMasterIndeterminate()" ); + base.Attribute( "[nzChecked]", $"{tableExtendId}.isMasterChecked()" ); + base.Attribute( "(nzCheckedChange)", $"{tableExtendId}.masterToggle()" ); + base.SetContent( $"{title}" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeTables/Renders/TreeTableRender.cs b/src/Util.Ui.NgZorro/Components/TreeTables/Renders/TreeTableRender.cs new file mode 100644 index 000000000..dc42d29c6 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeTables/Renders/TreeTableRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Tables.Renders; +using Util.Ui.NgZorro.Components.TreeTables.Builders; + +namespace Util.Ui.NgZorro.Components.TreeTables.Renders; + +/// +/// 树形表格渲染器 +/// +public class TreeTableRender : TableRender { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化树形表格渲染器 + /// + /// 配置 + public TreeTableRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TreeTableBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TreeTableRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeTables/TreeTableTagHelper.cs b/src/Util.Ui.NgZorro/Components/TreeTables/TreeTableTagHelper.cs new file mode 100644 index 000000000..284dd7a1a --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeTables/TreeTableTagHelper.cs @@ -0,0 +1,89 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Applications.Trees; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.NgZorro.Components.Tables.Helpers; +using Util.Ui.NgZorro.Components.TreeTables.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.TreeTables; + +/// +/// 树形表格,生成的标签为<nz-table></nz-table> +/// +[HtmlTargetElement( "util-tree-table" )] +public class TreeTableTagHelper : TableTagHelper { + /// + /// 配置 + /// + private Config _config; + /// + /// 扩展属性 loadUrl,首次加载地址,对于异步请求,仅加载第一级节点,如果未设置该属性,则使用 Url 属性地址 + /// + public string LoadUrl { get; set; } + /// + /// 扩展属性 [loadUrl],首次加载地址,对于异步请求,仅加载第一级节点,如果未设置该属性,则使用 Url 属性地址 + /// + public string BindLoadUrl { get; set; } + /// + /// 扩展属性 queryUrl,查询地址,如果未设置该属性,则使用 Url 属性地址 + /// + public string QueryUrl { get; set; } + /// + /// 扩展属性 [queryUrl],查询地址,如果未设置该属性,则使用 Url 属性地址 + /// + public string BindQueryUrl { get; set; } + /// + /// 扩展属性 loadChildrenUrl,加载子节点地址,如果未设置该属性,则使用 Url 属性地址 + /// + public string LoadChildrenUrl { get; set; } + /// + /// 扩展属性 [loadChildrenUrl],加载子节点地址,如果未设置该属性,则使用 Url 属性地址 + /// + public string BindLoadChildrenUrl { get; set; } + /// + /// 扩展属性 loadMode,加载模式,默认为同步加载 + /// + public LoadMode LoadMode { get; set; } + /// + /// 扩展属性 [isExpandAll],是否展开所有节点,仅对同步加载有效,默认为 false + /// + public bool ExpandAll { get; set; } + /// + /// 扩展属性 [isExpandForRootAsync],根节点异步加载模式是否展开子节点,默认为 true + /// + public bool ExpandForRootAsync { get; set; } + /// + /// 扩展属性 [isCheckLeafOnly],是否只能选择叶节点,仅对单选框有效,类型: boolean,默认为 false + /// + public string CheckLeafOnly { get; set; } + /// + /// 扩展属性 [onLoadChildrenBefore],子节点加载前事件,返回false停止加载,类型: (node) => boolean,参数为父节点 + /// + public string OnLoadChildrenBefore { get; set; } + /// + /// 扩展事件 (onLoadChildren),子节点加载完成事件,参数为{ node: TreeNode, result: PageList<any> } + /// + public string OnLoadChildren { get; set; } + /// + /// 扩展事件 (onExpand),节点展开事件,参数为 TreeNode + /// + public string OnExpand { get; set; } + /// + /// 扩展事件 (onCollapse),节点折叠事件,参数为 TreeNode + /// + public string OnCollapse { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new TableService( _config ); + service.Init(); + service.InitTreeTable(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new TreeTableRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/Builders/TreeNodeBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeViews/Builders/TreeNodeBuilder.cs new file mode 100644 index 000000000..791a06457 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/Builders/TreeNodeBuilder.cs @@ -0,0 +1,53 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.TreeViews.Builders; + +/// +/// 树节点标签生成器 +/// +public class TreeNodeBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化树节点标签生成器 + /// + /// 配置 + public TreeNodeBuilder( Config config ) : base( config,"nz-tree-node" ) { + _config = config; + } + + /// + /// 配置树节点定义指令 + /// + public TreeNodeBuilder TreeNodeDef() { + AttributeIfNotEmpty( "*nzTreeNodeDef", _config.GetValue( UiConst.TreeNodeDef ) ); + return this; + } + + /// + /// 配置树节点内边距 + /// + public TreeNodeBuilder TreeNodePadding() { + AttributeIf( "nzTreeNodePadding", _config.Contains( UiConst.TreeNodePadding ) ); + return this; + } + + /// + /// 配置树节点内边距 + /// + public TreeNodeBuilder TreeNodeIndentLine() { + AttributeIf( "nzTreeNodeIndentLine", _config.Contains( UiConst.TreeNodeIndentLine ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + TreeNodeDef().TreeNodePadding().TreeNodeIndentLine(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/Builders/TreeNodeCheckboxBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeViews/Builders/TreeNodeCheckboxBuilder.cs new file mode 100644 index 000000000..445a82d34 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/Builders/TreeNodeCheckboxBuilder.cs @@ -0,0 +1,63 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.TreeViews.Builders; + +/// +/// 树节点复选框标签生成器 +/// +public class TreeNodeCheckboxBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化树节点复选框标签生成器 + /// + /// 配置 + public TreeNodeCheckboxBuilder( Config config ) : base( config,"nz-tree-node-checkbox" ) { + _config = config; + } + + /// + /// 配置是否勾选 + /// + public TreeNodeCheckboxBuilder Checked() { + AttributeIfNotEmpty( "[nzChecked]", _config.GetValue( UiConst.Checked ) ); + return this; + } + + /// + /// 配置是否半选 + /// + public TreeNodeCheckboxBuilder Indeterminate() { + AttributeIfNotEmpty( "[nzIndeterminate]", _config.GetValue( UiConst.Indeterminate ) ); + return this; + } + + /// + /// 配置是否禁用 + /// + public TreeNodeCheckboxBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置事件 + /// + public TreeNodeCheckboxBuilder Events() { + AttributeIfNotEmpty( "(nzClick)", _config.GetValue( UiConst.OnClick ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Checked().Indeterminate().Disabled().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/Builders/TreeNodeOptionBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeViews/Builders/TreeNodeOptionBuilder.cs new file mode 100644 index 000000000..d497fa538 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/Builders/TreeNodeOptionBuilder.cs @@ -0,0 +1,55 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.TreeViews.Builders; + +/// +/// 树节点可选项标签生成器 +/// +public class TreeNodeOptionBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化树节点可选项标签生成器 + /// + /// 配置 + public TreeNodeOptionBuilder( Config config ) : base( config,"nz-tree-node-option" ) { + _config = config; + } + + /// + /// 配置是否选中 + /// + public TreeNodeOptionBuilder Selected() { + AttributeIfNotEmpty( "[nzSelected]", _config.GetValue( UiConst.Selected ) ); + return this; + } + + /// + /// 配置是否禁用 + /// + public TreeNodeOptionBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置事件 + /// + public TreeNodeOptionBuilder Events() { + AttributeIfNotEmpty( "(nzClick)", _config.GetValue( UiConst.OnClick ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Selected().Disabled().Events(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/Builders/TreeNodeToggleBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeViews/Builders/TreeNodeToggleBuilder.cs new file mode 100644 index 000000000..425de6551 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/Builders/TreeNodeToggleBuilder.cs @@ -0,0 +1,45 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.TreeViews.Builders; + +/// +/// 树节点切换标签生成器 +/// +public class TreeNodeToggleBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化树节点切换标签生成器 + /// + /// 配置 + public TreeNodeToggleBuilder( Config config ) : base( config,"nz-tree-node-toggle" ) { + _config = config; + } + + /// + /// 配置空操作切换 + /// + public TreeNodeToggleBuilder TreeNodeNoopToggle() { + AttributeIf( "nzTreeNodeNoopToggle", _config.Contains( UiConst.TreeNodeNoopToggle ) ); + return this; + } + + /// + /// 配置是否递归展开/收起 + /// + public TreeNodeToggleBuilder Recursive() { + AttributeIfNotEmpty( "[nzTreeNodeToggleRecursive]", _config.GetValue( UiConst.Recursive ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + TreeNodeNoopToggle().Recursive(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/Builders/TreeViewBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeViews/Builders/TreeViewBuilder.cs new file mode 100644 index 000000000..19cb2d386 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/Builders/TreeViewBuilder.cs @@ -0,0 +1,70 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.TreeViews.Builders; + +/// +/// 树视图标签生成器 +/// +public class TreeViewBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化树视图标签生成器 + /// + public TreeViewBuilder( Config config ) : base( config,"nz-tree-view" ) { + _config = config; + } + + /// + /// 配置树控制器 + /// + public TreeViewBuilder TreeControl() { + AttributeIfNotEmpty( "[nzTreeControl]", _config.GetValue( UiConst.TreeControl ) ); + return this; + } + + /// + /// 配置数据源 + /// + public TreeViewBuilder DataSource() { + AttributeIfNotEmpty( "[nzDataSource]", _config.GetValue( UiConst.Datasource ) ); + return this; + } + + /// + /// 配置是否文件夹样式 + /// + public TreeViewBuilder DirectoryTree() { + AttributeIfNotEmpty( "[nzDirectoryTree]", _config.GetValue( UiConst.DirectoryTree ) ); + return this; + } + + /// + /// 配置节点是否占整行 + /// + public TreeViewBuilder BlockNode() { + AttributeIfNotEmpty( "[nzBlockNode]", _config.GetValue( UiConst.BlockNode ) ); + return this; + } + + /// + /// 配置跟踪函数 + /// + public TreeViewBuilder TrackBy() { + AttributeIfNotEmpty( "[trackBy]", _config.GetValue( UiConst.TrackBy ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + TreeControl().DataSource().DirectoryTree().BlockNode().TrackBy(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/Builders/TreeVirtualScrollViewBuilder.cs b/src/Util.Ui.NgZorro/Components/TreeViews/Builders/TreeVirtualScrollViewBuilder.cs new file mode 100644 index 000000000..7765785e0 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/Builders/TreeVirtualScrollViewBuilder.cs @@ -0,0 +1,87 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; + +namespace Util.Ui.NgZorro.Components.TreeViews.Builders; + +/// +/// 虚拟滚动树视图标签生成器 +/// +public class TreeVirtualScrollViewBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化虚拟滚动树视图标签生成器 + /// + /// 配置 + public TreeVirtualScrollViewBuilder( Config config ) : base( config,"nz-tree-virtual-scroll-view" ) { + _config = config; + } + + /// + /// 配置树控制器 + /// + public TreeVirtualScrollViewBuilder TreeControl() { + AttributeIfNotEmpty( "[nzTreeControl]", _config.GetValue( UiConst.TreeControl ) ); + return this; + } + + /// + /// 配置数据源 + /// + public TreeVirtualScrollViewBuilder DataSource() { + AttributeIfNotEmpty( "[nzDataSource]", _config.GetValue( UiConst.Datasource ) ); + return this; + } + + /// + /// 配置是否文件夹样式 + /// + public TreeVirtualScrollViewBuilder DirectoryTree() { + AttributeIfNotEmpty( "[nzDirectoryTree]", _config.GetValue( UiConst.DirectoryTree ) ); + return this; + } + + /// + /// 配置节点是否占整行 + /// + public TreeVirtualScrollViewBuilder BlockNode() { + AttributeIfNotEmpty( "[nzBlockNode]", _config.GetValue( UiConst.BlockNode ) ); + return this; + } + + /// + /// 配置虚拟滚动列高 + /// + public TreeVirtualScrollViewBuilder ItemSize() { + AttributeIfNotEmpty( "[nzItemSize]", _config.GetValue( UiConst.ItemSize ) ); + return this; + } + + /// + /// 配置虚拟滚动缓冲区最大高度 + /// + public TreeVirtualScrollViewBuilder MaxBufferPx() { + AttributeIfNotEmpty( "[nzMaxBufferPx]", _config.GetValue( UiConst.MaxBufferPx ) ); + return this; + } + + /// + /// 配置虚拟滚动缓冲区最小高度 + /// + public TreeVirtualScrollViewBuilder MinBufferPx() { + AttributeIfNotEmpty( "[nzMinBufferPx]", _config.GetValue( UiConst.MinBufferPx ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + TreeControl().DataSource().DirectoryTree().BlockNode() + .ItemSize().MaxBufferPx().MinBufferPx(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/Configs/TreeNodeToggleShareConfig.cs b/src/Util.Ui.NgZorro/Components/TreeViews/Configs/TreeNodeToggleShareConfig.cs new file mode 100644 index 000000000..e165194f1 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/Configs/TreeNodeToggleShareConfig.cs @@ -0,0 +1,15 @@ +namespace Util.Ui.NgZorro.Components.TreeViews.Configs; + +/// +/// 树视图节点切换共享配置 +/// +public class TreeNodeToggleShareConfig { + /// + /// 是否为图标添加 nzTreeNodeToggleRotateIcon 指令 + /// + public bool RotateIcon { get; set; } = true; + /// + /// 是否为图标添加 nzTreeNodeToggleActiveIcon 指令 + /// + public bool ActiveIcon { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/Helpers/TreeNodeToggleService.cs b/src/Util.Ui.NgZorro/Components/TreeViews/Helpers/TreeNodeToggleService.cs new file mode 100644 index 000000000..474c88c5f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/Helpers/TreeNodeToggleService.cs @@ -0,0 +1,60 @@ +using Util.Ui.NgZorro.Components.TreeViews.Configs; + +namespace Util.Ui.NgZorro.Components.TreeViews.Helpers; + +/// +/// 树视图节点切换服务 +/// +public class TreeNodeToggleService { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 树视图节点切换共享配置 + /// + private TreeNodeToggleShareConfig _shareConfig; + + /// + /// 初始化树视图节点切换服务 + /// + /// 配置 + public TreeNodeToggleService( Config config ) { + _config = config; + } + + /// + /// 初始化 + /// + public void Init() { + CreateShareConfig(); + InitRotateIcon(); + InitActiveIcon(); + } + + /// + /// 创建对话框共享配置 + /// + private void CreateShareConfig() { + _shareConfig = new TreeNodeToggleShareConfig(); + _config.SetValueToItems( _shareConfig ); + } + + /// + /// 初始化 nzTreeNodeToggleRotateIcon + /// + private void InitRotateIcon() { + var value = _config.GetValue( UiConst.RotateIcon ); + if ( value == false ) + _shareConfig.RotateIcon = false; + } + + /// + /// 初始化 nzTreeNodeToggleActiveIcon + /// + private void InitActiveIcon() { + var value = _config.GetValue( UiConst.ActiveIcon ); + if ( value == true ) + _shareConfig.ActiveIcon = true; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/Renders/TreeNodeCheckboxRender.cs b/src/Util.Ui.NgZorro/Components/TreeViews/Renders/TreeNodeCheckboxRender.cs new file mode 100644 index 000000000..e5a860e99 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/Renders/TreeNodeCheckboxRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.TreeViews.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.TreeViews.Renders; + +/// +/// 树节点复选框渲染器 +/// +public class TreeNodeCheckboxRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化树节点复选框渲染器 + /// + /// 配置 + public TreeNodeCheckboxRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TreeNodeCheckboxBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TreeNodeCheckboxRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/Renders/TreeNodeOptionRender.cs b/src/Util.Ui.NgZorro/Components/TreeViews/Renders/TreeNodeOptionRender.cs new file mode 100644 index 000000000..a63fee06b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/Renders/TreeNodeOptionRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.TreeViews.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.TreeViews.Renders; + +/// +/// 树节点可选项渲染器 +/// +public class TreeNodeOptionRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化树节点可选项渲染器 + /// + /// 配置 + public TreeNodeOptionRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TreeNodeOptionBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TreeNodeOptionRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/Renders/TreeNodeRender.cs b/src/Util.Ui.NgZorro/Components/TreeViews/Renders/TreeNodeRender.cs new file mode 100644 index 000000000..3c0598d60 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/Renders/TreeNodeRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.TreeViews.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.TreeViews.Renders; + +/// +/// 树节点渲染器 +/// +public class TreeNodeRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化树节点渲染器 + /// + /// 配置 + public TreeNodeRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TreeNodeBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TreeNodeRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/Renders/TreeNodeToggleRender.cs b/src/Util.Ui.NgZorro/Components/TreeViews/Renders/TreeNodeToggleRender.cs new file mode 100644 index 000000000..cce254bd5 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/Renders/TreeNodeToggleRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.TreeViews.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.TreeViews.Renders; + +/// +/// 树节点切换渲染器 +/// +public class TreeNodeToggleRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化树节点切换渲染器 + /// + /// 配置 + public TreeNodeToggleRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TreeNodeToggleBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TreeNodeToggleRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/Renders/TreeViewRender.cs b/src/Util.Ui.NgZorro/Components/TreeViews/Renders/TreeViewRender.cs new file mode 100644 index 000000000..8687d2512 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/Renders/TreeViewRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.TreeViews.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.TreeViews.Renders; + +/// +/// 树视图渲染器 +/// +public class TreeViewRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化树视图渲染器 + /// + /// 配置 + public TreeViewRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TreeViewBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TreeViewRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/Renders/TreeVirtualScrollViewRender.cs b/src/Util.Ui.NgZorro/Components/TreeViews/Renders/TreeVirtualScrollViewRender.cs new file mode 100644 index 000000000..da13cec4e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/Renders/TreeVirtualScrollViewRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.TreeViews.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.TreeViews.Renders; + +/// +/// 虚拟滚动树视图渲染器 +/// +public class TreeVirtualScrollViewRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化虚拟滚动树视图渲染器 + /// + /// 配置 + public TreeVirtualScrollViewRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TreeVirtualScrollViewBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TreeVirtualScrollViewRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/TreeNodeCheckboxTagHelper.cs b/src/Util.Ui.NgZorro/Components/TreeViews/TreeNodeCheckboxTagHelper.cs new file mode 100644 index 000000000..c70aaddab --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/TreeNodeCheckboxTagHelper.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.TreeViews.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.TreeViews; + +/// +/// 树视图节点复选框,生成的标签为<nz-tree-node-checkbox></nz-tree-node-checkbox> +/// +[HtmlTargetElement( "util-tree-node-checkbox" )] +public class TreeNodeCheckboxTagHelper : AngularTagHelperBase { + /// + /// [nzChecked],是否勾选,类型: boolean, 默认值: false + /// + public string Checked { get; set; } + /// + /// [nzIndeterminate],是否半选,类型: boolean, 默认值: false + /// + public string Indeterminate { get; set; } + /// + /// [nzDisabled],是否禁用,类型: boolean, 默认值: false + /// + public string Disabled { get; set; } + /// + /// (nzClick),单击事件,类型: EventEmitter<MouseEvent> + /// + public string OnClick { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new TreeNodeCheckboxRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/TreeNodeOptionTagHelper.cs b/src/Util.Ui.NgZorro/Components/TreeViews/TreeNodeOptionTagHelper.cs new file mode 100644 index 000000000..1708dfdc5 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/TreeNodeOptionTagHelper.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.TreeViews.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.TreeViews; + +/// +/// 树视图节点可选项,定义树节点中的可选择部分,生成的标签为<nz-tree-node-option></nz-tree-node-option> +/// +[HtmlTargetElement( "util-tree-node-option" )] +public class TreeNodeOptionTagHelper : AngularTagHelperBase { + /// + /// [nzSelected],是否选中,类型: boolean, 默认值: false + /// + public string Selected { get; set; } + /// + /// [nzDisabled],是否禁用,类型: boolean, 默认值: false + /// + public string Disabled { get; set; } + /// + /// (nzClick),单击事件,类型: EventEmitter<MouseEvent> + /// + public string OnClick { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new TreeNodeOptionRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/TreeNodeTagHelper.cs b/src/Util.Ui.NgZorro/Components/TreeViews/TreeNodeTagHelper.cs new file mode 100644 index 000000000..443cd750a --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/TreeNodeTagHelper.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.TreeViews.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.TreeViews; + +/// +/// 树视图节点,生成的标签为<nz-tree-node></nz-tree-node> +/// +[HtmlTargetElement( "util-tree-node" )] +public class TreeNodeTagHelper : AngularTagHelperBase { + /// + /// *nzTreeNodeDef,树节点定义指令,范例: "let node; when: hasChild", `when` 用于定义是否使用此节点的方法,类型: `(index: number, nodeData: T) => boolean`, 优先匹配第一个返回 `true` 的节点。如果没有返回 `true` 的节点,则匹配未定义此方法的节点 + /// + public string TreeNodeDef { get; set; } + /// + /// nzTreeNodePadding,以添加内边距的方式显示节点缩进,性能最好 + /// + public string TreeNodePadding { get; set; } + /// + /// nzTreeNodeIndentLine,以添加缩进线的方式显示节点缩进 + /// + public string TreeNodeIndentLine { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new TreeNodeRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/TreeNodeToggleTagHelper.cs b/src/Util.Ui.NgZorro/Components/TreeViews/TreeNodeToggleTagHelper.cs new file mode 100644 index 000000000..44e98a185 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/TreeNodeToggleTagHelper.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.TreeViews.Helpers; +using Util.Ui.NgZorro.Components.TreeViews.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.TreeViews; + +/// +/// 树视图节点切换,生成的标签为<nz-tree-node-toggle></nz-tree-node-toggle> +/// +[HtmlTargetElement( "util-tree-node-toggle" )] +public class TreeNodeToggleTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// nzTreeNodeNoopToggle,不做任何操作的切换部分,可用于占位或者显示图标 + /// + public string TreeNodeNoopToggle { get; set; } + /// + /// [nzTreeNodeToggleRecursive],是否递归展开/收起, 类型: boolean, 默认值: false + /// + public string Recursive { get; set; } + /// + /// 是否为图标添加 nzTreeNodeToggleRotateIcon 指令,定义切换部分中的图标,会随着展开收起状态自动旋转, 类型: boolean, 默认值: true + /// + public bool RotateIcon { get; set; } + /// + /// 是否为图标添加 nzTreeNodeToggleActiveIcon 指令,定义切换部分中的图标,使其具有激活状态的样式,可用于 loading 图标, 类型: boolean, 默认值: false + /// + public bool ActiveIcon { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new TreeNodeToggleService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new TreeNodeToggleRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/TreeViewTagHelper.cs b/src/Util.Ui.NgZorro/Components/TreeViews/TreeViewTagHelper.cs new file mode 100644 index 000000000..014650aad --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/TreeViewTagHelper.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.TreeViews.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.TreeViews; + +/// +/// 树视图,生成的标签为<nz-tree-view></nz-tree-view> +/// +[HtmlTargetElement( "util-tree-view" )] +public class TreeViewTagHelper : AngularTagHelperBase { + /// + /// [nzTreeControl],树控制器,类型: TreeControl + /// + public string TreeControl { get; set; } + /// + /// [nzDataSource],数据源,数组数据,类型: DataSource<T> | Observable<T[]> | T[] + /// + public string Datasource { get; set; } + /// + /// [nzDirectoryTree],是否以文件夹样式显示节点,类型: boolean, 默认值: false + /// + public string DirectoryTree { get; set; } + /// + /// [nzBlockNode],节点是否占据整行,类型: boolean, 默认值: false + /// + public string BlockNode { get; set; } + /// + /// [trackBy],跟踪函数 + /// + public string TrackBy { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new TreeViewRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/TreeViews/TreeVirtualScrollViewTagHelper.cs b/src/Util.Ui.NgZorro/Components/TreeViews/TreeVirtualScrollViewTagHelper.cs new file mode 100644 index 000000000..e0aed0071 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/TreeViews/TreeVirtualScrollViewTagHelper.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.TreeViews.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.TreeViews; + +/// +/// 虚拟滚动树视图,生成的标签为<nz-tree-virtual-scroll-view></nz-tree-virtual-scroll-view> +/// +[HtmlTargetElement( "util-tree-virtual-scroll-view" )] +public class TreeVirtualScrollViewTagHelper : TreeViewTagHelper { + /// + /// [nzItemSize],节点大小,虚拟滚动时每一列的高度,类型: number, 单位:像素,默认值: 28 + /// + public string ItemSize { get; set; } + /// + /// [nzMaxBufferPx],虚拟滚动缓冲区最大高度,需要渲染新节点时的缓冲区大小,类型: number, 单位:像素,默认值: 28 * 10 + /// + public string MaxBufferPx { get; set; } + /// + /// [nzMinBufferPx],虚拟滚动缓冲区最小高度,超出渲染区的最小缓存区大小,类型: number, 单位:像素,默认值: 28 * 5 + /// + public string MinBufferPx { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new TreeVirtualScrollViewRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Trees/Builders/TreeBuilder.cs b/src/Util.Ui.NgZorro/Components/Trees/Builders/TreeBuilder.cs new file mode 100644 index 000000000..bfc2e33ba --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Trees/Builders/TreeBuilder.cs @@ -0,0 +1,340 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.Trees.Helpers; + +namespace Util.Ui.NgZorro.Components.Trees.Builders; + +/// +/// 树形控件标签生成器 +/// +public class TreeBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 树形服务 + /// + private readonly TreeService _service; + + /// + /// 初始化树形控件标签生成器 + /// + /// 配置 + public TreeBuilder( Config config ) : base( config, "nz-tree" ) { + _config = config; + _service = new TreeService( _config ); + } + + /// + /// 扩展标识 + /// + private string ExtendId => _service.ExtendId; + + /// + /// 配置数据 + /// + public TreeBuilder Data() { + return Data( _config.GetValue( UiConst.Data ) ); + } + + /// + /// 配置数据 + /// + public TreeBuilder Data( string data ) { + AttributeIfNotEmpty( "[nzData]", data ); + return this; + } + + /// + /// 配置是否节点占一行 + /// + public TreeBuilder BlockNode() { + AttributeIfNotEmpty( "[nzBlockNode]", _config.GetValue( UiConst.BlockNode ) ); + return this; + } + + /// + /// 配置节点前是否显示复选框 + /// + public TreeBuilder Checkable() { + Checkable( _config.GetValue( UiConst.Checkable ) ); + return this; + } + + /// + /// 配置节点前是否显示复选框 + /// + public TreeBuilder Checkable( string value ) { + AttributeIfNotEmpty( "[nzCheckable]", value ); + return this; + } + + /// + /// 配置节点前是否显示展开图标 + /// + public TreeBuilder ShowExpand() { + AttributeIfNotEmpty( "[nzShowExpand]", _config.GetValue( UiConst.ShowExpand ) ); + return this; + } + + /// + /// 配置是否显示连接线 + /// + public TreeBuilder ShowLine() { + AttributeIfNotEmpty( "[nzShowLine]", _config.GetValue( UiConst.ShowLine ) ); + return this; + } + + /// + /// 配置自定义展开图标 + /// + public TreeBuilder ExpandedIcon() { + AttributeIfNotEmpty( "[nzExpandedIcon]", _config.GetValue( UiConst.ExpandedIcon ) ); + return this; + } + + /// + /// 配置是否显示节点文本前图标 + /// + public TreeBuilder ShowIcon() { + AttributeIfNotEmpty( "[nzShowIcon]", _config.GetValue( UiConst.ShowIcon ) ); + return this; + } + + /// + /// 配置是否异步加载 + /// + public TreeBuilder AsyncData() { + AsyncData( _config.GetValue( UiConst.AsyncData ) ); + return this; + } + + /// + /// 配置是否异步加载 + /// + public TreeBuilder AsyncData( string value ) { + AttributeIfNotEmpty( "[nzAsyncData]", value ); + return this; + } + + /// + /// 配置节点是否可拖拽 + /// + public TreeBuilder Draggable() { + AttributeIfNotEmpty( "[nzDraggable]", _config.GetValue( UiConst.Draggable ) ); + return this; + } + + /// + /// 配置是否支持点选多个节点 + /// + public TreeBuilder Multiple() { + AttributeIfNotEmpty( "[nzMultiple]", _config.GetValue( UiConst.Multiple ) ); + return this; + } + + /// + /// 配置是否隐藏未匹配节点 + /// + public TreeBuilder HideUnMatched() { + AttributeIfNotEmpty( "[nzHideUnMatched]", _config.GetValue( UiConst.HideUnmatched ) ); + return this; + } + + /// + /// 配置严格勾选 + /// + public TreeBuilder CheckStrictly() { + AttributeIfNotEmpty( "[nzCheckStrictly]", _config.GetValue( UiConst.CheckStrictly ) ); + return this; + } + + /// + /// 配置自定义节点模板 + /// + public TreeBuilder TreeTemplate() { + AttributeIfNotEmpty( "[nzTreeTemplate]", _config.GetValue( UiConst.TreeTemplate ) ); + return this; + } + + /// + /// 配置是否默认展开所有节点 + /// + public TreeBuilder ExpandAll() { + AttributeIfNotEmpty( "[nzExpandAll]", _config.GetValue( UiConst.ExpandAll ) ); + return this; + } + + /// + /// 配置展开节点的键集合 + /// + public TreeBuilder ExpandedKeys() { + ExpandedKeys( _config.GetValue( UiConst.ExpandedKeys ) ); + return this; + } + + /// + /// 配置展开节点的键集合 + /// + /// 键集合 + public TreeBuilder ExpandedKeys( string keys ) { + AttributeIfNotEmpty( "[nzExpandedKeys]", keys ); + return this; + } + + /// + /// 配置勾选节点复选框的键集合 + /// + public TreeBuilder CheckedKeys() { + CheckedKeys( _config.GetValue( UiConst.CheckedKeys ) ); + return this; + } + + /// + /// 配置勾选节点复选框的键集合 + /// + /// 键集合 + public TreeBuilder CheckedKeys( string keys ) { + AttributeIfNotEmpty( "[nzCheckedKeys]", keys ); + return this; + } + + /// + /// 配置选中节点的键集合 + /// + public TreeBuilder SelectedKeys() { + SelectedKeys( _config.GetValue( UiConst.SelectedKeys ) ); + return this; + } + + /// + /// 配置选中节点的键集合 + /// + /// 键集合 + public TreeBuilder SelectedKeys( string keys ) { + AttributeIfNotEmpty( "[nzSelectedKeys]", keys ); + return this; + } + + /// + /// 配置搜索值 + /// + public TreeBuilder SearchValue() { + AttributeIfNotEmpty( "nzSearchValue", _config.GetValue( UiConst.SearchValue ) ); + AttributeIfNotEmpty( "[nzSearchValue]", _config.GetValue( AngularConst.BindSearchValue ) ); + AttributeIfNotEmpty( "[(nzSearchValue)]", _config.GetValue( AngularConst.BindonSearchValue ) ); + return this; + } + + /// + /// 配置自定义搜索方法 + /// + public TreeBuilder SearchFunc() { + AttributeIfNotEmpty( "[nzSearchFunc]", _config.GetValue( UiConst.SearchFunc ) ); + return this; + } + + /// + /// 配置拖拽前函数 + /// + public TreeBuilder BeforeDrop() { + AttributeIfNotEmpty( "[nzBeforeDrop]", _config.GetValue( UiConst.BeforeDrop ) ); + return this; + } + + /// + /// 配置虚拟高度 + /// + public TreeBuilder VirtualHeight() { + AttributeIfNotEmpty( "nzVirtualHeight", _config.GetValue( UiConst.VirtualHeight ) ); + AttributeIfNotEmpty( "[nzVirtualHeight]", _config.GetValue( AngularConst.BindVirtualHeight ) ); + return this; + } + + /// + /// 配置虚拟滚动列高 + /// + public TreeBuilder VirtualItemSize() { + AttributeIfNotEmpty( "[nzVirtualItemSize]", _config.GetValue( UiConst.VirtualItemSize ) ); + return this; + } + + /// + /// 配置虚拟滚动缓冲区最大高度 + /// + public TreeBuilder VirtualMaxBufferPx() { + AttributeIfNotEmpty( "[nzVirtualMaxBufferPx]", _config.GetValue( UiConst.VirtualMaxBufferPx ) ); + return this; + } + + /// + /// 配置虚拟滚动缓冲区最小高度 + /// + public TreeBuilder VirtualMinBufferPx() { + AttributeIfNotEmpty( "[nzVirtualMinBufferPx]", _config.GetValue( UiConst.VirtualMinBufferPx ) ); + return this; + } + + /// + /// 配置事件 + /// + public TreeBuilder Events() { + AttributeIfNotEmpty( "(nzClick)", _config.GetValue( UiConst.OnClick ) ); + AttributeIfNotEmpty( "(nzDblClick)", _config.GetValue( UiConst.OnDblClick ) ); + AttributeIfNotEmpty( "(nzCheckBoxChange)", _config.GetValue( UiConst.OnCheckboxChange ) ); + OnExpandChange( _config.GetValue( UiConst.OnExpandChange ) ); + AttributeIfNotEmpty( "(nzSearchValueChange)", _config.GetValue( UiConst.OnSearchValueChange ) ); + AttributeIfNotEmpty( "(nzOnDragStart)", _config.GetValue( UiConst.OnDragStart ) ); + AttributeIfNotEmpty( "(nzOnDragEnter)", _config.GetValue( UiConst.OnDragEnter ) ); + AttributeIfNotEmpty( "(nzOnDragOver)", _config.GetValue( UiConst.OnDragOver ) ); + AttributeIfNotEmpty( "(nzOnDragLeave)", _config.GetValue( UiConst.OnDragLeave ) ); + AttributeIfNotEmpty( "(nzOnDrop)", _config.GetValue( UiConst.OnDrop ) ); + AttributeIfNotEmpty( "(nzOnDragEnd)", _config.GetValue( UiConst.OnDragEnd ) ); + AttributeIfNotEmpty( "(onLoad)", _config.GetValue( UiConst.OnLoad ) ); + return this; + } + + /// + /// 展开收缩节点事件 + /// + public TreeBuilder OnExpandChange( string value ) { + AttributeIfNotEmpty( "(nzExpandChange)", value ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Data().BlockNode().Checkable().ShowExpand().ShowLine().ExpandedIcon().ShowIcon() + .AsyncData().Draggable().Multiple().HideUnMatched().CheckStrictly().TreeTemplate().ExpandAll() + .ExpandedKeys().CheckedKeys().SelectedKeys() + .SearchValue().SearchFunc().BeforeDrop() + .VirtualHeight().VirtualItemSize().VirtualMaxBufferPx().VirtualMinBufferPx() + .Events(); + _service.ConfigBuilder( this ); + ConfigDefault(); + } + + /// + /// 配置默认属性 + /// + private void ConfigDefault() { + if ( _service.IsEnableExtend() == false ) + return; + Checkable( "true" ). + Data( $"{ExtendId}.dataSource" ) + .CheckedKeys( $"{ExtendId}.checkedKeys" ) + .SelectedKeys( $"{ExtendId}.selectedKeys" ) + .OnExpandChange( $"{ExtendId}.expandChange($event)" ); + } + + /// + /// 配置右键上下文菜单事件 + /// + protected override void ConfigOnContextmenu( Config config ) { + AttributeIfNotEmpty( "(nzContextMenu)", _config.GetValue( UiConst.OnContextmenu ) ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Trees/Helpers/TreeService.cs b/src/Util.Ui.NgZorro/Components/Trees/Helpers/TreeService.cs new file mode 100644 index 000000000..e4b9b7ff4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Trees/Helpers/TreeService.cs @@ -0,0 +1,243 @@ +using Util.Applications.Trees; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Builders; +using Config = Util.Ui.Configs.Config; + +namespace Util.Ui.NgZorro.Components.Trees.Helpers; + +/// +/// 树形服务 +/// +public class TreeService { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 标识 + /// + private string _id; + + /// + /// 初始化树形服务 + /// + /// 配置 + public TreeService( Config config ) { + _config = config; + } + + /// + /// 扩展标识 + /// + public string ExtendId => $"x_{GetId()}"; + + /// + /// 获取标识 + /// + private string GetId() { + if ( _id.IsEmpty() == false ) + return _id; + _id = _config.GetValue( UiConst.Id ); + if ( _id.IsEmpty() ) + _id = Id.Create(); + return _id; + } + + /// + /// 是否启用基础扩展 + /// + public bool IsEnableExtend() { + if ( GetEnableExtend() == false ) { + return false; + } + if ( GetEnableExtend() == true || + GetUrl().IsEmpty() == false || + GetBindUrl().IsEmpty() == false || + GetLoadUrl().IsEmpty() == false || + GetBindLoadUrl().IsEmpty() == false || + GetQueryUrl().IsEmpty() == false || + GetBindQueryUrl().IsEmpty() == false || + GetLoadChildrenUrl().IsEmpty() == false || + GetBindLoadChildrenUrl().IsEmpty() == false ) { + return true; + } + return false; + } + + /// + /// 获取启用扩展属性 + /// + private bool? GetEnableExtend() { + return _config.GetValue( UiConst.EnableExtend ); + } + + /// + /// 获取地址 + /// + private string GetUrl() { + return _config.GetValue( UiConst.Url ); + } + + /// + /// 获取地址 + /// + private string GetBindUrl() { + return _config.GetValue( AngularConst.BindUrl ); + } + + /// + /// 获取加载地址 + /// + private string GetLoadUrl() { + return _config.GetValue( UiConst.LoadUrl ); + } + + /// + /// 获取加载地址 + /// + private string GetBindLoadUrl() { + return _config.GetValue( AngularConst.BindLoadUrl ); + } + + /// + /// 获取查询地址 + /// + private string GetQueryUrl() { + return _config.GetValue( UiConst.QueryUrl ); + } + + /// + /// 获取查询地址 + /// + private string GetBindQueryUrl() { + return _config.GetValue( AngularConst.BindQueryUrl ); + } + + /// + /// 获取加载下级节点地址 + /// + private string GetLoadChildrenUrl() { + return _config.GetValue( UiConst.LoadChildrenUrl ); + } + + /// + /// 获取加载下级节点地址 + /// + private string GetBindLoadChildrenUrl() { + return _config.GetValue( AngularConst.BindLoadChildrenUrl ); + } + + /// + /// 配置标签生成器 + /// + /// 标签生成器 + public void ConfigBuilder( TagBuilder builder ) { + Url( builder ); + LoadUrl( builder ); + QueryUrl( builder ); + LoadChildrenUrl( builder ); + AutoLoad( builder ); + QueryParam( builder ); + Sort( builder ); + ExpandForRootAsync( builder ); + LoadMode( builder ); + Events( builder ); + ConfigExtend( builder ); + } + + /// + /// 配置Api地址 + /// + private void Url( TagBuilder builder ) { + builder.AttributeIfNotEmpty( "url", _config.GetValue( UiConst.Url ) ); + builder.AttributeIfNotEmpty( "[url]", _config.GetValue( AngularConst.BindUrl ) ); + } + + /// + /// 配置加载地址 + /// + private void LoadUrl( TagBuilder builder ) { + builder.AttributeIfNotEmpty( "loadUrl", _config.GetValue( UiConst.LoadUrl ) ); + builder.AttributeIfNotEmpty( "[loadUrl]", _config.GetValue( AngularConst.BindLoadUrl ) ); + } + + /// + /// 配置查询地址 + /// + private void QueryUrl( TagBuilder builder ) { + builder.AttributeIfNotEmpty( "queryUrl", _config.GetValue( UiConst.QueryUrl ) ); + builder.AttributeIfNotEmpty( "[queryUrl]", _config.GetValue( AngularConst.BindQueryUrl ) ); + } + + /// + /// 配置加载子节点地址 + /// + private void LoadChildrenUrl( TagBuilder builder ) { + builder.AttributeIfNotEmpty( "loadChildrenUrl", _config.GetValue( UiConst.LoadChildrenUrl ) ); + builder.AttributeIfNotEmpty( "[loadChildrenUrl]", _config.GetValue( AngularConst.BindLoadChildrenUrl ) ); + } + + /// + /// 配置自动加载 + /// + private void AutoLoad( TagBuilder builder ) { + builder.AttributeIfNotEmpty( "[autoLoad]", _config.GetBoolValue( UiConst.AutoLoad ) ); + } + + /// + /// 配置查询参数 + /// + private void QueryParam( TagBuilder builder ) { + builder.AttributeIfNotEmpty( "[(queryParam)]", _config.GetValue( UiConst.QueryParam ) ); + } + + /// + /// 配置排序条件 + /// + private void Sort( TagBuilder builder ) { + builder.AttributeIfNotEmpty( "order", _config.GetValue( UiConst.Sort ) ); + builder.AttributeIfNotEmpty( "[order]", _config.GetValue( AngularConst.BindSort ) ); + } + + /// + /// 配置根节点异步加载模式是否展开子节点 + /// + private void ExpandForRootAsync( TagBuilder builder ) { + builder.AttributeIfNotEmpty( "[isExpandForRootAsync]", _config.GetBoolValue( UiConst.ExpandForRootAsync ) ); + } + + /// + /// 配置加载模式 + /// + private void LoadMode( TagBuilder builder ) { + var value = _config.GetValue( UiConst.LoadMode ); + if ( value == null ) + return; + builder.AttributeIfNotEmpty( "loadMode", value.Value().SafeString() ); + if ( value == Applications.Trees.LoadMode.Sync ) + return; + builder.AttributeIfNotEmpty( "[nzAsyncData]", "true" ); + } + + /// + /// 配置事件 + /// + private void Events( TagBuilder builder ) { + builder.AttributeIfNotEmpty( "[onLoadChildrenBefore]", _config.GetValue( UiConst.OnLoadChildrenBefore ) ); + builder.AttributeIfNotEmpty( "(onLoadChildren)", _config.GetValue( UiConst.OnLoadChildren ) ); + builder.AttributeIfNotEmpty( "(onExpand)", _config.GetValue( UiConst.OnExpand ) ); + builder.AttributeIfNotEmpty( "(onCollapse)", _config.GetValue( UiConst.OnCollapse ) ); + } + + /// + /// 配置树形扩展指令及默认属性 + /// + public void ConfigExtend( TagBuilder builder ) { + if ( IsEnableExtend() == false ) + return; + builder.Attribute( "x-tree-extend" ); + builder.Attribute( $"#{ExtendId}", "xTreeExtend" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Trees/Renders/TreeRender.cs b/src/Util.Ui.NgZorro/Components/Trees/Renders/TreeRender.cs new file mode 100644 index 000000000..2b4f78d54 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Trees/Renders/TreeRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Trees.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Trees.Renders; + +/// +/// 树形控件渲染器 +/// +public class TreeRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化树形控件渲染器 + /// + /// 配置 + public TreeRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TreeBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TreeRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Trees/TreeTagHelper.cs b/src/Util.Ui.NgZorro/Components/Trees/TreeTagHelper.cs new file mode 100644 index 000000000..6944d51c6 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Trees/TreeTagHelper.cs @@ -0,0 +1,256 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Applications.Trees; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.Trees.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Trees; + +/// +/// 树形,生成的标签为<nz-tree></nz-tree> +/// +[HtmlTargetElement( "util-tree" )] +public class TreeTagHelper : AngularTagHelperBase { + /// + /// [nzData],数据源,类型: NzTreeNodeOptions[] | NzTreeNode[],默认值: [] + /// + public string Data { get; set; } + /// + /// [nzCheckedKeys],指定选中复选框的节点标识列表,类型: string[] + /// + public string CheckedKeys { get; set; } + /// + /// [nzExpandedKeys],指定展开的节点标识列表,类型: string[] + /// + public string ExpandedKeys { get; set; } + /// + /// [nzSelectedKeys],指定选中的节点标识列表,类型: string[] + /// + public string SelectedKeys { get; set; } + /// + /// [nzCheckable],节点前是否显示复选框,类型: boolean, 默认值: false + /// + public string Checkable { get; set; } + /// + /// [nzCheckStrictly],严格勾选,父子节点选中状态不再关联,类型: boolean, 默认值: false + /// + public string CheckStrictly { get; set; } + /// + /// [nzDraggable],节点是否可拖拽,类型: boolean, 默认值: false + /// + public string Draggable { get; set; } + /// + /// [nzMultiple],是否支持点选多个节点,类型: boolean, 默认值: false + /// + public string Multiple { get; set; } + /// + /// [nzExpandAll],是否默认展开所有节点,类型: boolean, 默认值: false + /// + public string ExpandAll { get; set; } + /// + /// [nzBlockNode],节点是否占据一行,类型: boolean, 默认值: false + /// + public string BlockNode { get; set; } + /// + /// [nzAsyncData],是否异步加载,类型: boolean, 默认值: false + /// + public string AsyncData { get; set; } + /// + /// [nzShowLine],是否显示连接线,类型: boolean, 默认值: false + /// + public string ShowLine { get; set; } + /// + /// [nzShowExpand],节点前是否显示展开图标,类型: boolean, 默认值: true + /// + public string ShowExpand { get; set; } + /// + /// [nzShowIcon],是否显示节点文本前图标,没有默认样式,类型: boolean,默认值: false + /// + public string ShowIcon { get; set; } + /// + /// [nzBeforeDrop],拖拽前二次校验函数,决定是否允许放置,类型: (confirm: NzFormatBeforeDropEvent) => Observable<boolean> + /// + public string BeforeDrop { get; set; } + /// + /// [nzExpandedIcon],自定义展开图标,类型: TemplateRef<{ $implicit: NzTreeNode }> + /// + public string ExpandedIcon { get; set; } + /// + /// [nzTreeTemplate],自定义节点模板,类型: TemplateRef<{ $implicit: NzTreeNode }> + /// + public string TreeTemplate { get; set; } + /// + /// nzSearchValue,搜索值,高亮节点,默认值: null + /// + public string SearchValue { get; set; } + /// + /// [nzSearchValue],搜索值,高亮节点,默认值: null + /// + public string BindSearchValue { get; set; } + /// + /// [(nzSearchValue)],搜索值,高亮节点,默认值: null + /// + public string BindonSearchValue { get; set; } + /// + /// [nzSearchFunc],自定义搜索方法,类型: (node: NzTreeNodeOptions) => boolean,默认值: null + /// + public string SearchFunc { get; set; } + /// + /// [nzHideUnMatched],是否隐藏搜索未匹配节点,类型: boolean, 默认值: false + /// + public string HideUnmatched { get; set; } + /// + /// nzVirtualHeight,虚拟滚动的总高度,范例: '300px' + /// + public string VirtualHeight { get; set; } + /// + /// [nzVirtualHeight],虚拟滚动的总高度,范例: '300px' + /// + public string BindVirtualHeight { get; set; } + /// + /// [nzVirtualItemSize],虚拟滚动时每一列的高度,类型: number, 单位:像素, 默认值: 28 + /// + public string VirtualItemSize { get; set; } + /// + /// [nzVirtualMaxBufferPx],虚拟滚动缓冲区最大高度,类型: number, 单位:像素,默认值: 500 + /// + public string VirtualMaxBufferPx { get; set; } + /// + /// [nzVirtualMinBufferPx],虚拟滚动缓冲区最小高度,低于该值时将加载新结构,类型: number, 单位:像素,默认值: 28 + /// + public string VirtualMinBufferPx { get; set; } + /// + /// (nzClick),单击事件,点击树节点时触发,类型: EventEmitter<NzFormatEmitEvent> + /// + public string OnClick { get; set; } + /// + /// (nzDblClick),双击事件,双击树节点时触发,类型: EventEmitter<NzFormatEmitEvent> + /// + public string OnDblClick { get; set; } + /// + /// (nzContextMenu),上下文菜单事件,右键点击树节点时触发,类型: EventEmitter<NzFormatEmitEvent> + /// + public new string OnContextmenu { get; set; } + /// + /// (nzCheckBoxChange),树节点复选框选中状态变化事件,点击树节点复选框时触发,类型: EventEmitter<NzFormatEmitEvent> + /// + public string OnCheckboxChange { get; set; } + /// + /// (nzExpandChange),展开收缩节点事件,点击展开树节点图标时触发,类型: EventEmitter<NzFormatEmitEvent> + /// + public string OnExpandChange { get; set; } + /// + /// (nzSearchValueChange),搜索值变化事件,搜索节点时触发,与 nzSearchValue 配合使用,类型: EventEmitter<NzFormatEmitEvent> + /// + public string OnSearchValueChange { get; set; } + /// + /// (nzOnDragStart),开始拖拽时触发,类型: EventEmitter<NzFormatEmitEvent> + /// + public string OnDragStart { get; set; } + /// + /// (nzOnDragEnter),拖拽树节点进入目标容器范围时触发,类型: EventEmitter<NzFormatEmitEvent> + /// + public string OnDragEnter { get; set; } + /// + /// (nzOnDragOver),拖拽树节点在目标容器范围内移动时触发,类型: EventEmitter<NzFormatEmitEvent> + /// + public string OnDragOver { get; set; } + /// + /// (nzOnDragLeave),拖拽树节点离开目标容器范围时触发,类型: EventEmitter<NzFormatEmitEvent> + /// + public string OnDragLeave { get; set; } + /// + /// (nzOnDragEnd),拖拽结束时触发,类型: EventEmitter<NzFormatEmitEvent> + /// + public string OnDragEnd { get; set; } + /// + /// (nzOnDrop),鼠标在拖放目标上释放时触发,类型: EventEmitter<NzFormatEmitEvent> + /// + public string OnDrop { get; set; } + /// + /// 扩展属性,是否启用扩展指令,当设置Api地址时自动启用 + /// + public bool EnableExtend { get; set; } + /// + /// 扩展属性 [autoLoad],初始化时是否自动加载数据,默认为true,设置成false则手工加载 + /// + public bool AutoLoad { get; set; } + /// + /// 扩展属性 url,Api地址 + /// + public string Url { get; set; } + /// + /// 扩展属性 [url],Api地址 + /// + public string BindUrl { get; set; } + /// + /// 扩展属性 loadUrl,首次加载地址,对于异步请求,仅加载第一级节点,如果未设置该属性,则使用 url 地址 + /// + public string LoadUrl { get; set; } + /// + /// 扩展属性 [loadUrl],首次加载地址,对于异步请求,仅加载第一级节点,如果未设置该属性,则使用 url 地址 + /// + public string BindLoadUrl { get; set; } + /// + /// 扩展属性 queryUrl,查询地址,如果未设置该属性,则使用 url 地址 + /// + public string QueryUrl { get; set; } + /// + /// 扩展属性 [queryUrl],查询地址,如果未设置该属性,则使用 url 地址 + /// + public string BindQueryUrl { get; set; } + /// + /// 扩展属性 loadChildrenUrl,加载子节点地址,如果未设置该属性,则使用 url 地址 + /// + public string LoadChildrenUrl { get; set; } + /// + /// 扩展属性 [loadChildrenUrl],加载子节点地址,如果未设置该属性,则使用 url 地址 + /// + public string BindLoadChildrenUrl { get; set; } + /// + /// 扩展属性 loadMode,加载模式,默认为同步加载 + /// + public LoadMode LoadMode { get; set; } + /// + /// 扩展属性 [isExpandForRootAsync],根节点异步加载模式是否展开子节点,默认为 true + /// + public bool ExpandForRootAsync { get; set; } + /// + /// 扩展属性[(queryParam)],查询参数 + /// + public string QueryParam { get; set; } + /// + /// 扩展属性 order,排序条件,范例: creationTime desc + /// + public string Sort { get; set; } + /// + /// 扩展属性 [order],排序条件,范例: creationTime desc + /// + public string BindSort { get; set; } + /// + /// 扩展属性 [onLoadChildrenBefore],子节点加载前事件,返回false停止加载,类型: (parentNode:NzTreeNode,queryParam) => boolean,参数为父节点和查询参数 + /// + public string OnLoadChildrenBefore { get; set; } + /// + /// 扩展事件 (onLoadChildren),子节点加载完成事件,类型: EventEmitter<{ node: NzTreeNode, result }> + /// + public string OnLoadChildren { get; set; } + /// + /// 扩展事件 (onExpand),节点展开事件,类型: EventEmitter<NzTreeNode> + /// + public string OnExpand { get; set; } + /// + /// 扩展事件 (onCollapse),节点折叠事件,类型: EventEmitter<NzTreeNode> + /// + public string OnCollapse { get; set; } + /// + /// 扩展事件 (onLoad),数据加载完成事件,参数为服务端返回结果 + /// + public string OnLoad { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new TreeRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Typographies/ArticleTagHelper.cs b/src/Util.Ui.NgZorro/Components/Typographies/ArticleTagHelper.cs new file mode 100644 index 000000000..b8f900a9f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Typographies/ArticleTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Typographies.Builders; + +namespace Util.Ui.NgZorro.Components.Typographies; + +/// +/// 文章,生成的标签为<article nz-typography></article> +/// +[HtmlTargetElement( "util-article" )] +public class ArticleTagHelper : TypographyTagHelper { + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder( Config config ) { + return new ArticleBuilder( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Typographies/Builders/ArticleBuilder.cs b/src/Util.Ui.NgZorro/Components/Typographies/Builders/ArticleBuilder.cs new file mode 100644 index 000000000..9b8ebfec0 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Typographies/Builders/ArticleBuilder.cs @@ -0,0 +1,15 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Typographies.Builders; + +/// +/// article标签生成器 +/// +public class ArticleBuilder : AngularTagBuilder { + /// + /// 初始化article标签生成器 + /// 配置 + /// + public ArticleBuilder( Config config ) : base( config, "article" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Typographies/Builders/DivBuilder.cs b/src/Util.Ui.NgZorro/Components/Typographies/Builders/DivBuilder.cs new file mode 100644 index 000000000..b2107ab79 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Typographies/Builders/DivBuilder.cs @@ -0,0 +1,21 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Typographies.Builders; + +/// +/// div生成器 +/// +public class DivBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化div生成器 + /// + /// 配置 + public DivBuilder( Config config ) : base( config, "div" ) { + _config = config; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Typographies/Builders/HBuilder.cs b/src/Util.Ui.NgZorro/Components/Typographies/Builders/HBuilder.cs new file mode 100644 index 000000000..d635a9f2f --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Typographies/Builders/HBuilder.cs @@ -0,0 +1,69 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Typographies.Builders; + +/// +/// h1标签生成器 +/// +public class H1Builder : AngularTagBuilder { + /// + /// 初始化h1标签生成器 + /// + public H1Builder( Config config ) : base( config,"h1" ) { + } +} + +/// +/// h2标签生成器 +/// +public class H2Builder : AngularTagBuilder { + /// + /// 初始化h2标签生成器 + /// + public H2Builder( Config config ) : base( config, "h2" ) { + } +} + +/// +/// h3标签生成器 +/// +public class H3Builder : AngularTagBuilder { + /// + /// 初始化h3标签生成器 + /// + public H3Builder( Config config ) : base( config, "h3" ) { + } +} + +/// +/// h4标签生成器 +/// +public class H4Builder : AngularTagBuilder { + /// + /// 初始化h4标签生成器 + /// + public H4Builder( Config config ) : base( config, "h4" ) { + } +} + +/// +/// h5标签生成器 +/// +public class H5Builder : AngularTagBuilder { + /// + /// 初始化h5标签生成器 + /// + public H5Builder( Config config ) : base( config, "h5" ) { + } +} + +/// +/// h6标签生成器 +/// +public class H6Builder : AngularTagBuilder { + /// + /// 初始化h6标签生成器 + /// + public H6Builder( Config config ) : base( config, "h6" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Typographies/Builders/PBuilder.cs b/src/Util.Ui.NgZorro/Components/Typographies/Builders/PBuilder.cs new file mode 100644 index 000000000..afa52e19e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Typographies/Builders/PBuilder.cs @@ -0,0 +1,14 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Typographies.Builders; + +/// +/// p标签生成器 +/// +public class PBuilder : AngularTagBuilder { + /// + /// 初始化p标签生成器 + /// + public PBuilder( Config config ) : base( config,"p" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Typographies/Builders/SpanBuilder.cs b/src/Util.Ui.NgZorro/Components/Typographies/Builders/SpanBuilder.cs new file mode 100644 index 000000000..fcf46ab34 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Typographies/Builders/SpanBuilder.cs @@ -0,0 +1,14 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.Typographies.Builders; + +/// +/// span生成器 +/// +public class SpanBuilder : AngularTagBuilder { + /// + /// 初始化span生成器 + /// + public SpanBuilder( Config config ) : base( config,"span" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Typographies/DivTagHelper.cs b/src/Util.Ui.NgZorro/Components/Typographies/DivTagHelper.cs new file mode 100644 index 000000000..2ad3fad6d --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Typographies/DivTagHelper.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Typographies.Builders; + +namespace Util.Ui.NgZorro.Components.Typographies; + +/// +/// div,生成的标签为<div></div> +/// +[HtmlTargetElement( "util-div" )] +public class DivTagHelper : TypographyTagHelper { + /// + /// 是否排版组件 + /// + public bool Typography { get; set; } + + /// + protected override bool IsEnableTypography() { + return false; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder( Config config ) { + return new DivBuilder( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Typographies/HTagHelper.cs b/src/Util.Ui.NgZorro/Components/Typographies/HTagHelper.cs new file mode 100644 index 000000000..248695e26 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Typographies/HTagHelper.cs @@ -0,0 +1,83 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Typographies.Builders; + +namespace Util.Ui.NgZorro.Components.Typographies; + +/// +/// h1,生成的标签为<h1 nz-typography></h1> +/// +[HtmlTargetElement( "util-h1" )] +public class H1TagHelper : TypographyTagHelper { + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder( Config config ) { + return new H1Builder( config ); + } +} + +/// +/// h2,生成的标签为<h2 nz-typography></h2> +/// +[HtmlTargetElement( "util-h2" )] +public class H2TagHelper : TypographyTagHelper { + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder( Config config ) { + return new H2Builder( config ); + } +} + +/// +/// h3,生成的标签为<h3 nz-typography></h3> +/// +[HtmlTargetElement( "util-h3" )] +public class H3TagHelper : TypographyTagHelper { + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder( Config config ) { + return new H3Builder( config ); + } +} + +/// +/// h4,生成的标签为<h4 nz-typography></h4> +/// +[HtmlTargetElement( "util-h4" )] +public class H4TagHelper : TypographyTagHelper { + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder( Config config ) { + return new H4Builder( config ); + } +} + +/// +/// h5,生成的标签为<h5 nz-typography></h5> +/// +[HtmlTargetElement( "util-h5" )] +public class H5TagHelper : TypographyTagHelper { + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder( Config config ) { + return new H5Builder( config ); + } +} + +/// +/// h6,生成的标签为<h6 nz-typography></h6> +/// +[HtmlTargetElement( "util-h6" )] +public class H6TagHelper : TypographyTagHelper { + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder( Config config ) { + return new H6Builder( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Typographies/PTagHelper.cs b/src/Util.Ui.NgZorro/Components/Typographies/PTagHelper.cs new file mode 100644 index 000000000..6d690c8da --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Typographies/PTagHelper.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Typographies.Builders; + +namespace Util.Ui.NgZorro.Components.Typographies; + +/// +/// 段落,生成的标签为<p nz-typography></p> +/// +[HtmlTargetElement( "util-p" )] +public class PTagHelper : TypographyTagHelper { + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder( Config config ) { + return new PBuilder( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Typographies/Renders/SpanRender.cs b/src/Util.Ui.NgZorro/Components/Typographies/Renders/SpanRender.cs new file mode 100644 index 000000000..f7bbab51e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Typographies/Renders/SpanRender.cs @@ -0,0 +1,82 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.Typographies.Renders; + +/// +/// span排版组件渲染器 +/// +public class SpanRender : TypographyRender { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化span排版组件渲染器 + /// + /// 配置 + /// 标签生成器 + public SpanRender( Config config, TagBuilder builder ) : base( config, builder ) { + _config = config; + } + + /// + protected override void Config( TagBuilder builder ) { + ConfigHtml( builder ); + } + + /// + /// 配置事件 + /// + /// 标签生成器 + protected void ConfigHtml( TagBuilder builder ) { + var result = _config.GetValue( UiConst.Html ); + result = result.IsEmpty() ? _config.GetValue( AngularConst.BindHtml ) : $"'{result}'"; + builder.AttributeIfNotEmpty( "[innerHTML]", result ); + } + + /// + /// 配置事件 + /// + /// 标签生成器 + protected override void ConfigEvents( TagBuilder builder ) { + } + + /// + /// 配置内容 + /// + protected override void ConfigContent( TagBuilder builder ) { + var childTagBuilder = GetChildTagBuilder(); + if ( childTagBuilder == null ) { + base.ConfigContent( builder ); + return; + } + _config.Content.AppendTo( childTagBuilder ); + builder.AppendContent( childTagBuilder ); + } + + /// + /// 获取子标签生成器 + /// + private TagBuilder GetChildTagBuilder() { + var tag = _config.GetValue( UiConst.ChildTag ); + switch ( tag ) { + case SpanChildTag.Code: + return new CodeBuilder(); + case SpanChildTag.Keyboard: + return new KbdBuilder(); + case SpanChildTag.Mark: + return new MarkBuilder(); + case SpanChildTag.Strong: + return new StrongBuilder(); + case SpanChildTag.Underline: + return new UBuilder(); + case SpanChildTag.Delete: + return new DelBuilder(); + } + return null; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Typographies/Renders/TypographyRender.cs b/src/Util.Ui.NgZorro/Components/Typographies/Renders/TypographyRender.cs new file mode 100644 index 000000000..d59da5089 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Typographies/Renders/TypographyRender.cs @@ -0,0 +1,216 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Directives.Popconfirms; +using Util.Ui.NgZorro.Directives.Popover; +using Util.Ui.NgZorro.Directives.Tooltips; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Typographies.Renders; + +/// +/// 排版组件渲染器 +/// +public class TypographyRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 标签生成器 + /// + private readonly TagBuilder _builder; + + /// + /// 初始化排版组件渲染器 + /// + /// 配置 + /// 标签生成器 + public TypographyRender( Config config, TagBuilder builder ) { + _config = config; + _builder = builder; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + ConfigDefault(); + ConfigNzContent(); + ConfigEditable(); + ConfigEditIcon(); + ConfigEditTooltip(); + ConfigCopyable(); + ConfigCopyText(); + ConfigCopyIcons(); + ConfigCopyTooltips(); + ConfigType(); + ConfigDisabled(); + ConfigEllipsis(); + ConfigExpandable(); + ConfigEllipsisRows(); + ConfigSuffix(); + ConfigEvents(); + Config( _builder ); + _builder.Angular( _config ); + _builder.Tooltip( _config ).Popconfirm( _config ).Popover( _config ).SpaceItem( _config ); + _builder.ConfigBase( _config ); + ConfigContent( _builder ); + return _builder; + } + + /// + /// 配置默认设置 + /// + private void ConfigDefault() { + var isTypography = _config.GetValue( UiConst.Typography ); + if( isTypography == true ) + _builder.Attribute( "nz-typography" ); + } + + /// + /// 配置内容属性 + /// + private void ConfigNzContent() { + _builder.AttributeIfNotEmpty( "nzContent", _config.GetValue( UiConst.Content ) ); + _builder.AttributeIfNotEmpty( "[nzContent]", _config.GetValue( AngularConst.BindContent ) ); + _builder.AttributeIfNotEmpty( "[(nzContent)]", _config.GetValue( AngularConst.BindonContent ) ); + } + + /// + /// 配置可编辑 + /// + private void ConfigEditable() { + _builder.AttributeIfNotEmpty( "[nzEditable]", _config.GetValue( UiConst.Editable ) ); + } + + /// + /// 配置编辑图标 + /// + private void ConfigEditIcon() { + _builder.AttributeIfNotEmpty( "nzEditIcon", _config.GetValue( UiConst.EditIcon )?.Description() ); + _builder.AttributeIfNotEmpty( "[nzEditIcon]", _config.GetValue( AngularConst.BindEditIcon ) ); + } + + /// + /// 配置编辑按钮工具提示 + /// + private void ConfigEditTooltip() { + _builder.AttributeIfNotEmpty( "nzEditTooltip", _config.GetValue( UiConst.EditTooltip ) ); + _builder.AttributeIfNotEmpty( "[nzEditTooltip]", _config.GetValue( AngularConst.BindEditTooltip ) ); + } + + /// + /// 配置可复制 + /// + private void ConfigCopyable() { + _builder.AttributeIfNotEmpty( "[nzCopyable]", _config.GetValue( UiConst.Copyable ) ); + } + + /// + /// 配置复制文本 + /// + private void ConfigCopyText() { + _builder.AttributeIfNotEmpty( "nzCopyText", _config.GetValue( UiConst.CopyText ) ); + _builder.AttributeIfNotEmpty( "[nzCopyText]", _config.GetValue( AngularConst.BindCopyText ) ); + } + + /// + /// 配置复制图标 + /// + private void ConfigCopyIcons() { + _builder.AttributeIfNotEmpty( "[nzCopyIcons]", _config.GetValue( UiConst.CopyIcons ) ); + if( _config.Contains( UiConst.CopyIcon ) ) + _builder.Attribute( "[nzCopyIcons]", $"['{_config.GetValue( UiConst.CopyIcon )?.Description()}','check']" ); + } + + /// + /// 配置复制提示 + /// + private void ConfigCopyTooltips() { + _builder.AttributeIfNotEmpty( "[nzCopyTooltips]", _config.GetValue( UiConst.CopyTooltips ) ); + } + + /// + /// 配置文本类型 + /// + private void ConfigType() { + _builder.AttributeIfNotEmpty( "nzType", _config.GetValue( UiConst.Type )?.Description() ); + _builder.AttributeIfNotEmpty( "[nzType]", _config.GetValue( AngularConst.BindType ) ); + } + + /// + /// 配置禁用文本 + /// + private void ConfigDisabled() { + _builder.AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + } + + /// + /// 配置省略文本 + /// + private void ConfigEllipsis() { + _builder.AttributeIfNotEmpty( "[nzEllipsis]", _config.GetValue( UiConst.Ellipsis ) ); + } + + /// + /// 配置省略可展开 + /// + private void ConfigExpandable() { + _builder.AttributeIfNotEmpty( "[nzExpandable]", _config.GetValue( UiConst.Expandable ) ); + } + + /// + /// 配置省略行数 + /// + private void ConfigEllipsisRows() { + _builder.AttributeIfNotEmpty( "[nzEllipsisRows]", _config.GetValue( UiConst.EllipsisRows ) ); + } + + /// + /// 配置省略后缀 + /// + private void ConfigSuffix() { + _builder.AttributeIfNotEmpty( "nzSuffix", _config.GetValue( UiConst.Suffix ) ); + _builder.AttributeIfNotEmpty( "[nzSuffix]", _config.GetValue( AngularConst.BindSuffix ) ); + } + + /// + /// 配置事件 + /// + private void ConfigEvents() { + _builder.AttributeIfNotEmpty( "(nzContentChange)", _config.GetValue( UiConst.OnContentChange ) ); + _builder.AttributeIfNotEmpty( "(nzExpandChange)", _config.GetValue( UiConst.OnExpandChange ) ); + _builder.AttributeIfNotEmpty( "(nzOnEllipsis)", _config.GetValue( UiConst.OnEllipsis ) ); + ConfigEvents( _builder ); + } + + /// + /// 配置 + /// + /// 标签生成器 + protected virtual void Config( TagBuilder builder ) { + } + + /// + /// 配置事件 + /// + /// 标签生成器 + protected virtual void ConfigEvents( TagBuilder builder ) { + } + + /// + /// 配置内容 + /// + protected virtual void ConfigContent( TagBuilder builder ) { + _config.Content.AppendTo( builder ); + } + + /// + public override IHtmlContent Clone() { + return new TypographyRender( _config.Copy(), _builder ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Typographies/SpanTagHelper.cs b/src/Util.Ui.NgZorro/Components/Typographies/SpanTagHelper.cs new file mode 100644 index 000000000..188f2b9fb --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Typographies/SpanTagHelper.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Typographies.Builders; +using Util.Ui.NgZorro.Components.Typographies.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Typographies; + +/// +/// span,生成的标签为<span></span> +/// +[HtmlTargetElement( "util-span" )] +public class SpanTagHelper : TypographyTagHelper { + /// + /// 子标签 + /// + public SpanChildTag ChildTag { get; set; } + /// + /// 是否排版组件 + /// + public bool Typography { get; set; } + /// + /// [innerHTML], 设置Html,值添加单引号 + /// + public string Html { get; set; } + /// + /// [innerHTML], 设置Html + /// + public string BindHtml { get; set; } + + /// + protected override bool IsEnableTypography() { + return false; + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new SpanRender( config, GetTagBuilder( config ) ); + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder( Config config ) { + return new SpanBuilder( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Typographies/TypographyTagHelper.cs b/src/Util.Ui.NgZorro/Components/Typographies/TypographyTagHelper.cs new file mode 100644 index 000000000..ede8af3ff --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Typographies/TypographyTagHelper.cs @@ -0,0 +1,137 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Typographies.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Typographies; + +/// +/// 排版组件基类 +/// +public abstract class TypographyTagHelper : TooltipTagHelperBase { + /// + /// [nzEditable],是否可编辑,需要配合 [nzContent] 使用 + /// + public string Editable { get; set; } + /// + /// nzEditIcon,自定义编辑图标 + /// + public AntDesignIcon EditIcon { get; set; } + /// + /// [nzEditIcon],自定义编辑图标 + /// + public string BindEditIcon { get; set; } + /// + /// nzEditTooltip,编辑按钮提示信息 + /// + public string EditTooltip { get; set; } + /// + /// [nzEditTooltip],编辑按钮提示信息,设置为null清除提示 + /// + public string BindEditTooltip { get; set; } + /// + /// [nzCopyable],是否可复制,需要配合 [nzContent] 使用 + /// + public string Copyable { get; set; } + /// + /// nzCopyText,自定义被复制的文本 + /// + public string CopyText { get; set; } + /// + /// [nzCopyText],自定义被复制的文本 + /// + public string BindCopyText { get; set; } + /// + /// [nzCopyIcons],自定义复制图标,默认值:['copy', 'check'] + /// + public string CopyIcons { get; set; } + /// + /// [nzCopyIcons],自定义复制图标,将默认值 ['copy','check'] 中的 copy 图标替换为自定义图标 + /// + public AntDesignIcon CopyIcon { get; set; } + /// + /// [nzCopyTooltips],自定义复制提示,设置为null清除提示,范例:['复制', '已复制'] + /// + public string CopyTooltips { get; set; } + /// + /// nzType,文本类型 + /// + public TextType Type { get; set; } + /// + /// [nzType],文本类型 + /// + public string BindType { get; set; } + /// + /// [nzDisabled],禁用文本 + /// + public string Disabled { get; set; } + /// + /// [nzEllipsis],自动溢出省略,动态内容时需要配合 [nzContent] 使用 + /// + public string Ellipsis { get; set; } + /// + /// [nzExpandable],自动溢出省略时是否可展开 + /// + public string Expandable { get; set; } + /// + /// [nzEllipsisRows],自动溢出省略时省略行数 + /// + public string EllipsisRows { get; set; } + /// + /// nzSuffix,自动溢出省略时的文本后缀 + /// + public string Suffix { get; set; } + /// + /// [nzSuffix],自动溢出省略时的文本后缀 + /// + public string BindSuffix { get; set; } + /// + /// nzContent,内容 + /// + public string Content { get; set; } + /// + /// [nzContent],内容 + /// + public string BindContent { get; set; } + /// + /// [(nzContent)],内容 + /// + public string BindonContent { get; set; } + /// + /// *nzSpaceItem,值为 true 时设置为间距项,放入 <util-space> 中使用 + /// + public bool SpaceItem { get; set; } + /// + /// (nzContentChange),当用户提交编辑内容时触发 + /// + public string OnContentChange { get; set; } + /// + /// (nzExpandChange),当展开省略文本时触发 + /// + public string OnExpandChange { get; set; } + /// + /// (nzOnEllipsis),当省略状态变化时触发 + /// + public string OnEllipsis { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + config.SetAttribute( UiConst.Typography, IsEnableTypography(),false ); + return new TypographyRender( config, GetTagBuilder( config ) ); + } + + /// + /// 是否启用排版指令 + /// + protected virtual bool IsEnableTypography() { + return true; + } + + /// + /// 获取标签生成器 + /// + protected abstract TagBuilder GetTagBuilder( Config config ); +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Upload/Builders/UploadBuilder.cs b/src/Util.Ui.NgZorro/Components/Upload/Builders/UploadBuilder.cs new file mode 100644 index 000000000..91ce98308 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Upload/Builders/UploadBuilder.cs @@ -0,0 +1,464 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.Angular.Extensions; +using Util.Ui.NgZorro.Components.Buttons.Builders; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Components.Inputs.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Components.Upload.Builders; + +/// +/// 上传标签生成器 +/// +public class UploadBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + /// + /// 标识 + /// + private string _id; + + /// + /// 初始化上传标签生成器 + /// + /// 配置 + public UploadBuilder( Config config ) : base( config,"nz-upload" ) { + _config = config; + } + + /// + /// 扩展标识 + /// + protected string ExtendId => $"u_{GetId()}"; + + /// + /// 获取标识 + /// + protected string GetId() { + if ( _id.IsEmpty() == false ) + return _id; + _id = _config.GetValue( UiConst.Id ); + if ( _id.IsEmpty() ) + _id = Util.Helpers.Id.Create(); + return _id; + } + + /// + /// 配置接受上传的文件类型 + /// + public UploadBuilder Accept() { + AttributeIfNotEmpty( "nzAccept", _config.GetValue( UiConst.Accept ) ); + AttributeIfNotEmpty( "[nzAccept]", _config.GetValue( AngularConst.BindAccept ) ); + return this; + } + + /// + /// 配置上传地址 + /// + public UploadBuilder Action() { + AttributeIfNotEmpty( "nzAction", _config.GetValue( UiConst.Action ) ); + AttributeIfNotEmpty( "[nzAction]", _config.GetValue( AngularConst.BindAction ) ); + return this; + } + + /// + /// 配置是否支持上传文件夹 + /// + public UploadBuilder Directory() { + AttributeIfNotEmpty( "[nzDirectory]", _config.GetValue( UiConst.Directory ) ); + return this; + } + + /// + /// 配置上传前钩子函数 + /// + public UploadBuilder BeforeUpload() { + AttributeIfNotEmpty( "[nzBeforeUpload]", _config.GetValue( UiConst.BeforeUpload ) ); + return this; + } + + /// + /// 配置自定义上传实现 + /// + public UploadBuilder CustomRequest() { + AttributeIfNotEmpty( "[nzCustomRequest]", _config.GetValue( UiConst.CustomRequest ) ); + return this; + } + + /// + /// 配置上传参数 + /// + public UploadBuilder Data() { + AttributeIfNotEmpty( "[nzData]", _config.GetValue( UiConst.Data ) ); + return this; + } + + /// + /// 配置禁用 + /// + public UploadBuilder Disabled() { + AttributeIfNotEmpty( "[nzDisabled]", _config.GetValue( UiConst.Disabled ) ); + return this; + } + + /// + /// 配置文件列表 + /// + public UploadBuilder FileList() { + AttributeIfNotEmpty( "[nzFileList]", _config.GetValue( UiConst.FileList ) ); + AttributeIfNotEmpty( "[(nzFileList)]", _config.GetValue( AngularConst.BindonFileList ) ); + return this; + } + + /// + /// 配置限制单次最多上传数量 + /// + public UploadBuilder Limit() { + AttributeIfNotEmpty( "[nzLimit]", _config.GetValue( UiConst.Limit ) ); + AttributeIfNotEmpty( "[nzLimit]", _config.GetValue( AngularConst.BindLimit ) ); + return this; + } + + /// + /// 配置限制文件大小 + /// + public UploadBuilder Size() { + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( UiConst.Size ) ); + AttributeIfNotEmpty( "[nzSize]", _config.GetValue( AngularConst.BindSize ) ); + return this; + } + + /// + /// 配置限制文件类型 + /// + public UploadBuilder FileType() { + AttributeIfNotEmpty( "nzFileType", _config.GetValue( UiConst.FileType ) ); + AttributeIfNotEmpty( "[nzFileType]", _config.GetValue( AngularConst.BindFileType ) ); + return this; + } + + /// + /// 配置过滤器 + /// + public UploadBuilder Filter() { + return Filter( _config.GetValue( UiConst.Filter ) ); + } + + /// + /// 配置过滤器 + /// + public UploadBuilder Filter( string filter ) { + AttributeIfNotEmpty( "[nzFilter]", filter ); + return this; + } + + /// + /// 配置请求头部 + /// + public UploadBuilder Headers() { + AttributeIfNotEmpty( "[nzHeaders]", _config.GetValue( UiConst.Headers ) ); + return this; + } + + /// + /// 配置列表类型 + /// + public UploadBuilder ListType() { + AttributeIfNotEmpty( "nzListType", _config.GetValue( UiConst.ListType )?.Description() ); + AttributeIfNotEmpty( "[nzListType]", _config.GetValue( AngularConst.BindListType ) ); + return this; + } + + /// + /// 配置是否支持多选文件 + /// + public UploadBuilder Multiple() { + AttributeIfNotEmpty( "[nzMultiple]", _config.GetValue( UiConst.Multiple ) ); + return this; + } + + /// + /// 配置参数名 + /// + public UploadBuilder Name() { + AttributeIfNotEmpty( "nzName", _config.GetValue( UiConst.Name ) ); + AttributeIfNotEmpty( "[nzName]", _config.GetValue( AngularConst.BindName ) ); + return this; + } + + /// + /// 配置是否显示上传列表 + /// + public UploadBuilder ShowUploadList() { + AttributeIfNotEmpty( "[nzShowUploadList]", _config.GetValue( UiConst.ShowUploadList ) ); + return this; + } + + /// + /// 配置是否显示上传按钮 + /// + public UploadBuilder ShowButton() { + AttributeIfNotEmpty( "[nzShowButton]", _config.GetValue( UiConst.ShowButton ) ); + return this; + } + + /// + /// 配置上传时是否携带cookie + /// + public UploadBuilder WithCredentials() { + AttributeIfNotEmpty( "[nzWithCredentials]", _config.GetValue( UiConst.WithCredentials ) ); + return this; + } + + /// + /// 配置点击是否打开文件对话框 + /// + public UploadBuilder OpenFileDialogOnClick() { + AttributeIfNotEmpty( "[nzOpenFileDialogOnClick]", _config.GetValue( UiConst.OpenFileDialogOnClick ) ); + return this; + } + + /// + /// 配置点击预览函数 + /// + public UploadBuilder Preview() { + AttributeIfNotEmpty( "[nzPreview]", _config.GetValue( UiConst.Preview ) ); + return this; + } + + /// + /// 配置自定义文件预览函数 + /// + public UploadBuilder PreviewFile() { + AttributeIfNotEmpty( "[nzPreviewFile]", _config.GetValue( UiConst.PreviewFile ) ); + return this; + } + + /// + /// 配置预览文件是否有效图片函数 + /// + public UploadBuilder PreviewIsImage() { + AttributeIfNotEmpty( "[nzPreviewIsImage]", _config.GetValue( UiConst.PreviewIsImage ) ); + return this; + } + + /// + /// 配置移除文件函数 + /// + public UploadBuilder Remove() { + AttributeIfNotEmpty( "[nzRemove]", _config.GetValue( UiConst.Remove ) ); + return this; + } + + /// + /// 配置下载文件函数 + /// + public UploadBuilder Download() { + AttributeIfNotEmpty( "[nzDownload]", _config.GetValue( UiConst.Download ) ); + return this; + } + + /// + /// 配置转换文件函数 + /// + public UploadBuilder TransformFile() { + AttributeIfNotEmpty( "[nzTransformFile]", _config.GetValue( UiConst.TransformFile ) ); + return this; + } + + /// + /// 配置图标渲染模板 + /// + public UploadBuilder IconRender() { + AttributeIfNotEmpty( "[nzIconRender]", _config.GetValue( UiConst.IconRender ) ); + return this; + } + + /// + /// 配置文件列表渲染模板 + /// + public UploadBuilder FileListRender() { + AttributeIfNotEmpty( "[nzFileListRender]", _config.GetValue( UiConst.FileListRender ) ); + return this; + } + + /// + /// 配置事件 + /// + public UploadBuilder Events() { + AttributeIfNotEmpty( "(nzChange)", _config.GetValue( UiConst.OnChange ) ); + AttributeIfNotEmpty( "(modelChange)", _config.GetValue( UiConst.OnModelChange ) ); + AttributeIfNotEmpty( "(onUploadComplete)", _config.GetValue( UiConst.OnUploadComplete ) ); + return this; + } + + /// + /// 配置模型绑定 + /// + public UploadBuilder Model() { + AttributeIfNotEmpty( "[(model)]", _config.GetValue( AngularConst.NgModel ) ); + return this; + } + + /// + /// 配置间距项 + /// + public UploadBuilder SpaceItem() { + var shareConfig = GetShareConfig(); + if ( shareConfig.FormItemCreated ) + return this; + this.SpaceItem( shareConfig.SpaceItem ); + return this; + } + + /// + /// 获取表单项共享配置 + /// + private FormItemShareConfig GetShareConfig() { + return _config.GetValueFromItems() ?? new FormItemShareConfig(); + } + + /// + /// 配置 + /// + public override void Config() { + base.ConfigBase( _config ); + Accept().Action().Directory().BeforeUpload().CustomRequest() + .Data().Disabled().FileList().Limit().Size().FileType().Filter() + .Headers().ListType().Multiple().Name() + .ShowUploadList().ShowButton().WithCredentials().OpenFileDialogOnClick() + .Preview().PreviewFile().PreviewIsImage() + .Remove().Download().TransformFile() + .IconRender().FileListRender() + .Events(); + Model().SpaceItem(); + AddButton(); + EnableExtend(); + } + + /// + /// 添加按钮 + /// + private void AddButton() { + var buttonBuilder = new ButtonBuilder( _config ); + buttonBuilder.Attribute( "nz-button" ); + buttonBuilder.Icon( AntDesignIcon.Upload ); + buttonBuilder.AppendContentByI18n( I18nKeys.Upload ); + AppendContent( buttonBuilder ); + } + + /// + /// 启用扩展 + /// + public UploadBuilder EnableExtend() { + if ( IsEnableExtend() == false ) + return this; + Attribute( $"#{ExtendId}", "xUploadExtend" ); + Attribute( "x-upload-extend" ); + AddInput(); + SetFileList(); + HandleChange(); + ClearFiles(); + ModelToFilesDebounceTime(); + SetFilter(); + return this; + } + + /// + /// 是否启用基础扩展 + /// + public bool IsEnableExtend() { + if ( GetEnableExtend() == false ) { + return false; + } + return GetEnableExtend() == true || + IsContainsModel() || + GetRequired() == true; + } + + /// + /// 获取启用扩展属性 + /// + protected bool? GetEnableExtend() { + return _config.GetValue( UiConst.EnableExtend ); + } + + /// + /// 是否包含模型绑定 + /// + protected bool IsContainsModel() { + return _config.GetValue( AngularConst.NgModel ).IsEmpty() == false; + } + + /// + /// 获取是否必填项 + /// + protected bool? GetRequired() { + return _config.GetValue( UiConst.Required ); + } + + /// + /// 添加输入框,用于支持验证 + /// + protected void AddInput() { + if ( GetRequired() != true ) + return; + var inputBuilder = new InputBuilder( _config ); + inputBuilder.Attribute( "style", "display: none" ); + inputBuilder.Name( $"input_{GetId()}" ) + .ValidationExtend() + .BindNgModel( $"{ExtendId}.inputValue" ) + .Attribute( "[required]","true" ); + PostBuilder = inputBuilder; + } + + /// + /// 配置文件列表 + /// + public void SetFileList() { + if( _config.GetValue( AngularConst.NotBindFileList ) ) + return; + if ( _config.Contains( UiConst.FileList ) ) + return; + if ( _config.Contains( AngularConst.BindonFileList ) ) + return; + Attribute( "[(nzFileList)]", $"{ExtendId}.files" ); + } + + /// + /// 配置文件状态变更处理函数 + /// + public void HandleChange() { + if( _config.Contains( UiConst.OnChange ) ) + return; + Attribute( "(nzChange)", $"{ExtendId}.handleChange($event)" ); + } + + /// + /// 配置是否清除文件列表 + /// + public void ClearFiles() { + AttributeIfNotEmpty( "[isClearFiles]", _config.GetValue( UiConst.ClearFiles ) ); + } + + /// + /// 配置模型数据转换为上传文件列表的延迟时间 + /// + public void ModelToFilesDebounceTime() { + AttributeIfNotEmpty( "[modelToFilesDebounceTime]", _config.GetValue( UiConst.ModelToFilesDebounceTime ) ); + } + + /// + /// 配置过滤器 + /// + public void SetFilter() { + if( _config.Contains( UiConst.Filter ) ) + return; + Filter( $"{ExtendId}.filters" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Upload/Helpers/UploadExpressionLoader.cs b/src/Util.Ui.NgZorro/Components/Upload/Helpers/UploadExpressionLoader.cs new file mode 100644 index 000000000..8f7603ce2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Upload/Helpers/UploadExpressionLoader.cs @@ -0,0 +1,53 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Expressions; +using Util.Ui.NgZorro.Expressions; + +namespace Util.Ui.NgZorro.Components.Upload.Helpers; + +/// +/// 上传组件表达式加载器 +/// +public class UploadExpressionLoader : NgZorroExpressionLoaderBase { + /// + /// 加载模型信息 + /// + /// 配置 + /// 模型表达式信息 + protected override void Load( Config config, ModelExpressionInfo info ) { + LoadLabel( config, info ); + LoadId( config, info ); + LoadNgModel( config, info ); + LoadRequired( config, info ); + } + + /// + /// 加载标签 + /// + protected virtual void LoadLabel( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.LabelText, info.DisplayName, false ); + } + + /// + /// 加载标识 + /// + protected virtual void LoadId( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.Id, GeKebaberizePropertyName( config, info ), false ); + } + + /// + /// 加载模型绑定 + /// + protected virtual void LoadNgModel( Config config, ModelExpressionInfo info ) { + config.SetAttribute( AngularConst.NgModel, info.SafePropertyName, false ); + } + + /// + /// 加载必填项验证 + /// + protected virtual void LoadRequired( Config config, ModelExpressionInfo info ) { + if ( info.IsRequired == false ) + return; + config.SetAttribute( UiConst.Required, "true", false ); + config.SetAttribute( UiConst.RequiredMessage, info.RequiredMessage, false ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Upload/Helpers/UploadService.cs b/src/Util.Ui.NgZorro/Components/Upload/Helpers/UploadService.cs new file mode 100644 index 000000000..63dea0997 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Upload/Helpers/UploadService.cs @@ -0,0 +1,64 @@ +using Util.Ui.NgZorro.Components.Forms.Helpers; + +namespace Util.Ui.NgZorro.Components.Upload.Helpers; + +/// +/// 上传组件服务 +/// +public class UploadService { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化上传组件服务 + /// + /// 配置 + public UploadService( Config config ) { + _config = config; + } + + /// + /// 初始化 + /// + public void Init() { + LoadExpression(); + InitValidationService(); + InitFormShareService(); + InitFormItemShareService(); + } + + /// + /// 加载表达式 + /// + private void LoadExpression() { + var loader = new UploadExpressionLoader(); + loader.Load( _config ); + } + + /// + /// 初始化验证服务 + /// + private void InitValidationService() { + var service = new ValidationService( _config ); + service.Init(); + } + + /// + /// 初始化表单共享服务 + /// + private void InitFormShareService() { + var service = new FormShareService( _config ); + service.Init(); + } + + /// + /// 初始化表单项共享服务 + /// + private void InitFormItemShareService() { + var service = new FormItemShareService( _config ); + service.Init(); + service.InitId(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Upload/Renders/UploadRender.cs b/src/Util.Ui.NgZorro/Components/Upload/Renders/UploadRender.cs new file mode 100644 index 000000000..3bb4b3d63 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Upload/Renders/UploadRender.cs @@ -0,0 +1,39 @@ +using Util.Ui.Builders; +using Util.Ui.Extensions; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Upload.Builders; + +namespace Util.Ui.NgZorro.Components.Upload.Renders; + +/// +/// 上传渲染器 +/// +public class UploadRender : FormControlRenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化上传渲染器 + /// + /// 配置 + public UploadRender( Config config ) : base( config ) { + _config = config; + } + + /// + /// 添加表单控件 + /// + protected override void AppendControl( TagBuilder formControlBuilder ) { + var builder = new UploadBuilder( _config ); + builder.Config(); + _config.Content.SetTo( builder ); + formControlBuilder.AppendContent( builder ); + } + + /// + public override IHtmlContent Clone() { + return new UploadRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/Upload/UploadTagHelper.cs b/src/Util.Ui.NgZorro/Components/Upload/UploadTagHelper.cs new file mode 100644 index 000000000..c32775dda --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/Upload/UploadTagHelper.cs @@ -0,0 +1,221 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Base; +using Util.Ui.NgZorro.Components.Upload.Helpers; +using Util.Ui.NgZorro.Components.Upload.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.Upload; + +/// +/// 上传,生成的标签为<nz-upload></nz-upload> +/// +[HtmlTargetElement( "util-upload" )] +public class UploadTagHelper : FormControlContainerTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 扩展属性,属性表达式 + /// + public ModelExpression For { get; set; } + /// + /// 扩展属性,[(model)],模型双向绑定 + /// + public string NgModel { get; set; } + /// + /// 扩展属性,是否启用扩展指令 + /// + public bool EnableExtend { get; set; } + /// + /// 扩展属性 [isClearFiles],是否清除文件列表 + /// + public string ClearFiles { get; set; } + /// + /// 扩展属性 [modelToFilesDebounceTime],模型数据转换为上传文件列表的延迟时间,单位:毫秒,默认值: 100 + /// + public string ModelToFilesDebounceTime { get; set; } + /// + /// 扩展属性 [required],是否必填项 + /// + public string Required { get; set; } + /// + /// 扩展属性 requiredMessage,必填项验证消息 + /// + public string RequiredMessage { get; set; } + /// + /// 扩展属性 [requiredMessage],必填项验证消息 + /// + public string BindRequiredMessage { get; set; } + /// + /// nzAccept,接受上传的文件类型,范例: '.doc,.docx,application/msword' + /// + public string Accept { get; set; } + /// + /// [nzAccept],接受上传的文件类型,范例: '.doc,.docx,application/msword' + /// + public string BindAccept { get; set; } + /// + /// nzAction,上传地址,必选参数,类型: string | ((file: NzUploadFile) => string | Observable<string>) + /// + public string Action { get; set; } + /// + /// [nzAction],上传地址,必选参数,类型: string | ((file: NzUploadFile) => string | Observable<string>) + /// + public string BindAction { get; set; } + /// + /// [nzDirectory],是否支持上传文件夹,默认值: false + /// + public string Directory { get; set; } + /// + /// [nzBeforeUpload],上传前钩子函数,参数为上传的文件,返回 false 停止上传,注意:务必使用 => 定义处理方法,函数定义: (file: NzUploadFile, fileList: NzUploadFile[]) => boolean | Observable<boolean> + /// + public string BeforeUpload { get; set; } + /// + /// [nzCustomRequest],自定义上传实现,覆盖默认上传行为,注意:务必使用 => 定义处理方法,函数定义: (item) => Subscription + /// + public string CustomRequest { get; set; } + /// + /// [nzData],上传参数,可以是返回上传参数的方法,注意:务必使用 => 定义处理方法,类型: Object | ((file: NzUploadFile) => Object | Observable<{}>) + /// + public string Data { get; set; } + /// + /// [nzDisabled],是否禁用 + /// + public string Disabled { get; set; } + /// + /// [nzFileList],文件列表,类型: NzUploadFile[] + /// + public string FileList { get; set; } + /// + /// [(nzFileList)],文件列表,类型: NzUploadFile[] + /// + public string BindonFileList { get; set; } + /// + /// 扩展属性,不自动设置 nzFileList 文件列表 + /// + public bool NotBindFileList { get; set; } + /// + /// [nzLimit],限制单次最多上传数量,nzMultiple 打开时有效,0 表示不限,默认值: 0 + /// + public string Limit { get; set; } + /// + /// [nzSize],限制文件大小,单位:KB,0 表示不限,默认值: 0 + /// + public string Size { get; set; } + /// + /// nzFileType,限制文件类型,范例: image/png,image/jpeg,image/gif,image/bmp + /// + public string FileType { get; set; } + /// + /// [nzFileType],限制文件类型,范例: image/png,image/jpeg,image/gif,image/bmp + /// + public string BindFileType { get; set; } + /// + /// [nzFilter],自定义过滤器,类型: UploadFilter[] + /// + public string Filter { get; set; } + /// + /// [nzHeaders],设置上传的请求头部,类型: Object | ((file: NzUploadFile) => Object | Observable<{}>),范例: { authorization: 'authorization-text' } + /// + public string Headers { get; set; } + /// + /// nzListType,上传列表的内建样式,可选值: 'text' | 'picture' | 'picture-card',默认值: 'text' + /// + public UploadListType ListType { get; set; } + /// + /// [nzListType],上传列表的内建样式,可选值: 'text' | 'picture' | 'picture-card',默认值: 'text' + /// + public string BindListType { get; set; } + /// + /// [nzMultiple],是否支持多选文件,开启后按住 ctrl 可选择多个文件,默认值: false + /// + public string Multiple { get; set; } + /// + /// nzName,发送到服务端的文件参数名,默认值: 'file' + /// + public string Name { get; set; } + /// + /// [nzName],发送到服务端的文件参数名,默认值: 'file' + /// + public string BindName { get; set; } + /// + /// [nzShowUploadList],是否显示上传列表,类型: boolean | { showPreviewIcon?: boolean, showRemoveIcon?: boolean, showDownloadIcon?: boolean },默认值: true + /// + public string ShowUploadList { get; set; } + /// + /// [nzShowButton],是否显示上传按钮,默认值: true + /// + public string ShowButton { get; set; } + /// + /// [nzWithCredentials],上传时是否携带cookie,默认值: false + /// + public string WithCredentials { get; set; } + /// + /// [nzOpenFileDialogOnClick],点击是否打开文件对话框,默认值: true + /// + public string OpenFileDialogOnClick { get; set; } + /// + /// [nzPreview],点击文件链接或点击预览图标时的回调函数,注意:务必使用 => 定义处理方法,类型: (file: NzUploadFile) => void + /// + public string Preview { get; set; } + /// + /// [nzPreviewFile],自定义文件预览实现函数,注意:务必使用 => 定义处理方法,类型: (file: NzUploadFile) => Observable<dataURL: string> + /// + public string PreviewFile { get; set; } + /// + /// [nzPreviewIsImage],预览文件是否有效图片函数,用于图片URL为非标准格式,注意:务必使用 => 定义处理方法,类型: (file: NzUploadFile) => boolean + /// + public string PreviewIsImage { get; set; } + /// + /// [nzRemove],点击移除文件时的回调函数,返回值为 false 不移除,注意:务必使用 => 定义处理方法,类型: (file: NzUploadFile) => boolean | Observable<boolean> + /// + public string Remove { get; set; } + /// + /// [nzDownload],点击下载文件时的回调函数,如果没有指定,则默认跳转到文件 url 对应的标签页,类型: (file: NzUploadFile) => void + /// + public string Download { get; set; } + /// + /// [nzTransformFile],在上传之前转换文件函数,类型: (file: NzUploadFile) => NzUploadTransformFileType + /// + public string TransformFile { get; set; } + /// + /// [nzIconRender],图标渲染模板,自定义显示图标,类型: TemplateRef<{ $implicit: NzUploadFile }> + /// + public string IconRender { get; set; } + /// + /// [nzFileListRender],文件列表渲染模板,自定义显示整个列表,类型: TemplateRef<{ $implicit: NzUploadFile[] }> + /// + public string FileListRender { get; set; } + /// + /// 扩展属性 *nzSpaceItem,值为true时设置为间距项,放入 nz-space 组件中使用 + /// + public bool SpaceItem { get; set; } + /// + /// (nzChange),上传文件改变事件,类型: EventEmitter<NzUploadChangeParam> + /// + public string OnChange { get; set; } + /// + /// 扩展事件 (modelChange),模型变更事件 + /// + public string OnModelChange { get; set; } + /// + /// 扩展事件 (onUploadComplete),上传完成事件,全部文件上传完成时触发,类型: EventEmitter<any>,参数为模型数据model + /// + public string OnUploadComplete { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var service = new UploadService( _config ); + service.Init(); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new UploadRender( _config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/VirtualScrolls/Builders/VirtualScrollBuilder.cs b/src/Util.Ui.NgZorro/Components/VirtualScrolls/Builders/VirtualScrollBuilder.cs new file mode 100644 index 000000000..51a5e38a8 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/VirtualScrolls/Builders/VirtualScrollBuilder.cs @@ -0,0 +1,49 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.NgZorro.Components.VirtualScrolls.Builders; + +/// +/// 虚拟滚动标签生成器 +/// +public class VirtualScrollBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化虚拟滚动标签生成器 + /// + /// 配置 + public VirtualScrollBuilder( Config config ) : base( config,"ng-template" ) { + _config = config; + base.Attribute( "nz-virtual-scroll" ); + } + + /// + /// 配置数据引用 + /// + public VirtualScrollBuilder LetData() { + if( _config.Contains( UiConst.LetData ) ) + Attribute( "let-data" ); + return this; + } + + /// + /// 配置索引引用 + /// + public VirtualScrollBuilder LetIndex() { + if( _config.Contains( UiConst.LetIndex ) ) + Attribute( "let-index", "index" ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + LetData().LetIndex(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/VirtualScrolls/Builders/VirtualScrollViewportBuilder.cs b/src/Util.Ui.NgZorro/Components/VirtualScrolls/Builders/VirtualScrollViewportBuilder.cs new file mode 100644 index 000000000..177bf2bf9 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/VirtualScrolls/Builders/VirtualScrollViewportBuilder.cs @@ -0,0 +1,37 @@ +using Util.Ui.Angular.Builders; + +namespace Util.Ui.NgZorro.Components.VirtualScrolls.Builders; + +/// +/// 虚拟滚动窗口标签生成器 +/// +public class VirtualScrollViewportBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化虚拟滚动窗口标签生成器 + /// + /// 配置 + public VirtualScrollViewportBuilder( Config config ) : base( config,"cdk-virtual-scroll-viewport" ) { + _config = config; + } + + /// + /// 配置列高 + /// + public VirtualScrollViewportBuilder ItemSize() { + AttributeIfNotEmpty( "[itemSize]", _config.GetValue( UiConst.ItemSize ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + ItemSize(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/VirtualScrolls/Renders/VirtualScrollRender.cs b/src/Util.Ui.NgZorro/Components/VirtualScrolls/Renders/VirtualScrollRender.cs new file mode 100644 index 000000000..2e6cdb1ab --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/VirtualScrolls/Renders/VirtualScrollRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.VirtualScrolls.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.VirtualScrolls.Renders; + +/// +/// 虚拟滚动渲染器 +/// +public class VirtualScrollRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化虚拟滚动渲染器 + /// + /// 配置 + public VirtualScrollRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new VirtualScrollBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new VirtualScrollRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/VirtualScrolls/Renders/VirtualScrollViewportRender.cs b/src/Util.Ui.NgZorro/Components/VirtualScrolls/Renders/VirtualScrollViewportRender.cs new file mode 100644 index 000000000..e2b25a407 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/VirtualScrolls/Renders/VirtualScrollViewportRender.cs @@ -0,0 +1,38 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.VirtualScrolls.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.VirtualScrolls.Renders; + +/// +/// 虚拟滚动窗口渲染器 +/// +public class VirtualScrollViewportRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化虚拟滚动窗口渲染器 + /// + /// 配置 + public VirtualScrollViewportRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new VirtualScrollViewportBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new VirtualScrollViewportRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/VirtualScrolls/VirtualScrollTagHelper.cs b/src/Util.Ui.NgZorro/Components/VirtualScrolls/VirtualScrollTagHelper.cs new file mode 100644 index 000000000..35fcefa1e --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/VirtualScrolls/VirtualScrollTagHelper.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.VirtualScrolls.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.VirtualScrolls; + +/// +/// 虚拟滚动,生成的标签为<ng-template nz-virtual-scroll></ng-template> +/// +[HtmlTargetElement( "util-virtual-scroll" )] +public class VirtualScrollTagHelper : AngularTagHelperBase { + /// + /// let-data,导出数据引用,使用变量 data 引用数据 + /// + public string LetData { get; set; } + /// + /// let-index="index",导出索引引用,使用变量 index 引用索引 + /// + public string LetIndex { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new VirtualScrollRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/VirtualScrolls/VirtualScrollViewportTagHelper.cs b/src/Util.Ui.NgZorro/Components/VirtualScrolls/VirtualScrollViewportTagHelper.cs new file mode 100644 index 000000000..6ae0cbd86 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/VirtualScrolls/VirtualScrollViewportTagHelper.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.VirtualScrolls.Renders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.VirtualScrolls; + +/// +/// 虚拟滚动窗口,生成的标签为<cdk-virtual-scroll-viewport></cdk-virtual-scroll-viewport> +/// +[HtmlTargetElement( "util-virtual-scroll-viewport" )] +public class VirtualScrollViewportTagHelper : AngularTagHelperBase { + /// + /// itemSize,列的高度,单位:px,类型: number + /// + public string ItemSize { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new VirtualScrollViewportRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/WaterMarks/Builders/WaterMarkBuilder.cs b/src/Util.Ui.NgZorro/Components/WaterMarks/Builders/WaterMarkBuilder.cs new file mode 100644 index 000000000..ce676a01b --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/WaterMarks/Builders/WaterMarkBuilder.cs @@ -0,0 +1,139 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Configs; +using Util.Ui.NgZorro.Components.WaterMarks.Helpers; +using Util.Ui.NgZorro.Enums; + +namespace Util.Ui.NgZorro.Components.WaterMarks.Builders; + +/// +/// 水印标签生成器 +/// +public class WaterMarkBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化水印标签生成器 + /// + /// 配置 + public WaterMarkBuilder( Config config ) : base( config, "nz-water-mark" ) { + _config = config; + } + + /// + /// 配置水印文字内容 + /// + public WaterMarkBuilder Content() { + AttributeIfNotEmpty( "nzContent", _config.GetValue( UiConst.Content ) ); + AttributeIfNotEmpty( "[nzContent]", _config.GetValue( AngularConst.BindContent ) ); + return this; + } + + /// + /// 配置水印宽度 + /// + public WaterMarkBuilder Width() { + AttributeIfNotEmpty( "[nzWidth]", _config.GetValue( UiConst.Width ) ); + AttributeIfNotEmpty( "[nzWidth]", _config.GetValue( AngularConst.BindWidth ) ); + return this; + } + + /// + /// 配置水印高度 + /// + public WaterMarkBuilder Height() { + AttributeIfNotEmpty( "[nzHeight]", _config.GetValue( UiConst.Height ) ); + AttributeIfNotEmpty( "[nzHeight]", _config.GetValue( AngularConst.BindHeight ) ); + return this; + } + + /// + /// 配置旋转角度 + /// + public WaterMarkBuilder Rotate() { + AttributeIfNotEmpty( "[nzRotate]", _config.GetValue( UiConst.Rotate ) ); + AttributeIfNotEmpty( "[nzRotate]", _config.GetValue( AngularConst.BindRotate ) ); + return this; + } + + /// + /// 配置z-index + /// + public WaterMarkBuilder ZIndex() { + AttributeIfNotEmpty( "[nzZIndex]", _config.GetValue( UiConst.ZIndex ) ); + AttributeIfNotEmpty( "[nzZIndex]", _config.GetValue( AngularConst.BindZIndex ) ); + return this; + } + + /// + /// 配置水印图片地址 + /// + public WaterMarkBuilder Image() { + AttributeIfNotEmpty( "nzImage", _config.GetValue( UiConst.Image ) ); + AttributeIfNotEmpty( "[nzImage]", _config.GetValue( AngularConst.BindImage ) ); + return this; + } + + /// + /// 配置文字样式 + /// + public WaterMarkBuilder Font() { + AttributeIfNotEmpty( "[nzFont]", GetFont() ); + return this; + } + + /// + /// 获取字体 + /// + private string GetFont() { + var result = _config.GetValue( UiConst.Font ); + if ( result.IsEmpty() == false ) + return result; + var fontType = new FontType { + Color = GetFontColor(), + FontSize = _config.GetValue( UiConst.FontSize ), + FontWeight = _config.GetValue( UiConst.FontWeight ), + FontFamily = _config.GetValue( UiConst.FontFamily ), + FontStyle = _config.GetValue( UiConst.FontStyle ) + }; + result = Util.Helpers.Json.ToJson( fontType, new JsonOptions { ToSingleQuotes = true } ); + if ( result == "{}" ) + result = null; + return result; + } + + /// + /// 获取字体颜色 + /// + private string GetFontColor() { + var result = _config.GetValue( UiConst.FontColor ); + return result.IsEmpty() ? _config.GetValue( UiConst.FontColorType )?.Description() : result; + } + + /// + /// 配置间距 + /// + public WaterMarkBuilder Gap() { + AttributeIfNotEmpty( "[nzGap]", _config.GetValue( UiConst.Gap ) ); + return this; + } + + /// + /// 配置偏移量 + /// + public WaterMarkBuilder Offset() { + AttributeIfNotEmpty( "[nzOffset]", _config.GetValue( UiConst.Offset ) ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Content().Width().Height().Rotate() + .ZIndex().Image().Font().Gap().Offset(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/WaterMarks/Helpers/FontType.cs b/src/Util.Ui.NgZorro/Components/WaterMarks/Helpers/FontType.cs new file mode 100644 index 000000000..180597449 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/WaterMarks/Helpers/FontType.cs @@ -0,0 +1,32 @@ +namespace Util.Ui.NgZorro.Components.WaterMarks.Helpers; + +/// +/// 字体类型 +/// +internal class FontType { + /// + /// 字体颜色 + /// + [JsonPropertyName("color")] + public string Color { get; set; } + /// + /// 字体大小 + /// + [JsonPropertyName( "fontSize" )] + public double? FontSize { get; set; } + /// + /// 字体粗细 + /// + [JsonPropertyName( "fontWeight" )] + public string FontWeight { get; set; } + /// + /// 字体类型 + /// + [JsonPropertyName( "fontFamily" )] + public string FontFamily { get; set; } + /// + /// 字体样式 + /// + [JsonPropertyName( "fontStyle" )] + public string FontStyle { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/WaterMarks/Renders/WaterMarkRender.cs b/src/Util.Ui.NgZorro/Components/WaterMarks/Renders/WaterMarkRender.cs new file mode 100644 index 000000000..3a08282c9 --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/WaterMarks/Renders/WaterMarkRender.cs @@ -0,0 +1,37 @@ +using Util.Ui.Builders; +using Util.Ui.NgZorro.Components.WaterMarks.Builders; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.WaterMarks.Renders; + +/// +/// 水印渲染器 +/// +public class WaterMarkRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化水印渲染器 + /// + /// 配置 + public WaterMarkRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new WaterMarkBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new WaterMarkRender( _config.Copy() ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Components/WaterMarks/WaterMarkTagHelper.cs b/src/Util.Ui.NgZorro/Components/WaterMarks/WaterMarkTagHelper.cs new file mode 100644 index 000000000..109d287ff --- /dev/null +++ b/src/Util.Ui.NgZorro/Components/WaterMarks/WaterMarkTagHelper.cs @@ -0,0 +1,88 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.NgZorro.Components.WaterMarks.Renders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.Renders; + +namespace Util.Ui.NgZorro.Components.WaterMarks; + +/// +/// 水印,生成的标签为<nz-water-mark></nz-water-mark> +/// +[HtmlTargetElement( "util-water-mark" )] +public class WaterMarkTagHelper : AngularTagHelperBase { + /// + /// nzContent,水印文字内容, 类型: string | string[] + /// + public string Content { get; set; } + /// + /// [nzContent],水印文字内容, 类型: string | string[] + /// + public string BindContent { get; set; } + /// + /// [nzWidth],水印宽度, 默认值: 120 + /// + public string Width { get; set; } + /// + /// [nzHeight],水印高度, 默认值: 64 + /// + public string Height { get; set; } + /// + /// [nzRotate],旋转角度, 默认值: -22 + /// + public string Rotate { get; set; } + /// + /// [nzZIndex],水印元素的 z-index, 默认值: 9 + /// + public string ZIndex { get; set; } + /// + /// nzImage,水印图片地址 + /// + public string Image { get; set; } + /// + /// [nzImage],水印图片地址 + /// + public string BindImage { get; set; } + /// + /// [nzFont],文字样式 + /// + public string Font { get; set; } + /// + /// [nzFont],扩展属性, 字体颜色 + /// + public AntDesignColor FontColorType { get; set; } + /// + /// [nzFont],字体颜色, 默认值: rgba(0,0,0,.15) + /// + public string FontColor { get; set; } + /// + /// [nzFont],字体大小, 默认值: 16 + /// + public double FontSize { get; set; } + /// + /// [nzFont],字体粗细, 可选值: normal | light | weight | number, 默认值: normal + /// + public string FontWeight { get; set; } + /// + /// [nzFont],字体类型, 默认值: sans-serif + /// + public string FontFamily { get; set; } + /// + /// [nzFont],字体样式, 可选值: none | normal | italic | oblique, 默认值: normal + /// + public string FontStyle { get; set; } + /// + /// [nzGap],水印之间的间距, 类型: [number, number], 默认值: [100, 100] + /// + public string Gap { get; set; } + /// + /// [nzOffset],水印距离容器左上角的偏移量, 类型: [number, number], 默认值: [nzGap[0]/2, nzGap[1]/2] + /// + public string Offset { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new WaterMarkRender( config ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Configs/ConfigExtensions.cs b/src/Util.Ui.NgZorro/Configs/ConfigExtensions.cs new file mode 100644 index 000000000..3259110df --- /dev/null +++ b/src/Util.Ui.NgZorro/Configs/ConfigExtensions.cs @@ -0,0 +1,41 @@ +using Util.Ui.Angular.Configs; + +namespace Util.Ui.NgZorro.Configs; + +/// +/// 配置扩展 +/// +public static class ConfigExtensions { + /// + /// 复制并移除属性,保留部分属性 + /// + /// 配置 + public static Config CopyRemoveAttributes( this Config config ) { + var result = config.Copy(); + result.OutputAttributes.Clear(); + result.AllAttributes.Clear(); + LoadConfig( config, result, UiConst.Required ); + LoadConfig( config, result, UiConst.RequiredMessage ); + LoadConfig( config, result, UiConst.Suffix ); + LoadConfig( config, result, AngularConst.BindSuffix ); + LoadConfig( config, result, UiConst.Extra ); + LoadConfig( config, result, AngularConst.BindExtra ); + LoadConfig( config, result, UiConst.ErrorTip ); + LoadConfig( config, result, AngularConst.BindErrorTip ); + LoadConfig( config, result, UiConst.SuccessTip ); + LoadConfig( config, result, AngularConst.BindSuccessTip ); + LoadConfig( config, result, UiConst.ValidatingTip ); + LoadConfig( config, result, AngularConst.BindValidatingTip ); + LoadConfig( config, result, UiConst.WarningTip ); + LoadConfig( config, result, AngularConst.BindWarningTip ); + return result; + } + + /// + /// 加载配置 + /// + private static void LoadConfig( Config from, Config to, string name ) { + var value = from.GetValue( name ); + to.SetAttribute( name, value ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Configs/I18nKeys.cs b/src/Util.Ui.NgZorro/Configs/I18nKeys.cs new file mode 100644 index 000000000..6f2b7e329 --- /dev/null +++ b/src/Util.Ui.NgZorro/Configs/I18nKeys.cs @@ -0,0 +1,159 @@ +namespace Util.Ui.NgZorro.Configs; + +/// +/// I18n多语言键 +/// +public class I18nKeys { + /// + /// util.ok + /// + public const string Ok = "util.ok"; + /// + /// util.cancel + /// + public const string Cancel = "util.cancel"; + /// + /// util.create + /// + public const string Create = "util.create"; + /// + /// util.update + /// + public const string Update = "util.update"; + /// + /// util.delete + /// + public const string Delete = "util.delete"; + /// + /// util.detail + /// + public const string Detail = "util.detail"; + /// + /// util.query + /// + public const string Query = "util.query"; + /// + /// util.refresh + /// + public const string Refresh = "util.refresh"; + /// + /// util.save + /// + public const string Save = "util.save"; + /// + /// util.yes + /// + public const string Yes = "util.yes"; + /// + /// util.no + /// + public const string No = "util.no"; + /// + /// util.operation + /// + public const string Operation = "util.operation"; + /// + /// util.enable + /// + public const string Enable = "util.enable"; + /// + /// util.disable + /// + public const string Disable = "util.disable"; + /// + /// util.selectAll + /// + public const string SelectAll = "util.selectAll"; + /// + /// util.deselectAll + /// + public const string DeselectAll = "util.deselectAll"; + /// + /// util.defaultOptionText + /// + public const string DefaultOptionText = "util.defaultOptionText"; + /// + /// util.upload + /// + public const string Upload = "util.upload"; + /// + /// util.download + /// + public const string Download = "util.download"; + /// + /// util.publish + /// + public const string Publish = "util.publish"; + /// + /// util.run + /// + public const string Run = "util.run"; + /// + /// util.start + /// + public const string Start = "util.start"; + /// + /// util.stop + /// + public const string Stop = "util.stop"; + /// + /// util.add + /// + public const string Add = "util.add"; + /// + /// util.remove + /// + public const string Remove = "util.remove"; + /// + /// util.open + /// + public const string Open = "util.open"; + /// + /// util.close + /// + public const string Close = "util.close"; + /// + /// util.send + /// + public const string Send = "util.send"; + /// + /// util.clear + /// + public const string Clear = "util.clear"; + /// + /// util.import + /// + public const string Import = "util.import"; + /// + /// util.export + /// + public const string Export = "util.export"; + /// + /// util.reset + /// + public const string Reset = "util.reset"; + /// + /// util.copyToClipboard + /// + public const string CopyToClipboard = "util.copyToClipboard"; + /// + /// util.fullscreen + /// + public const string Fullscreen = "util.fullscreen"; + /// + /// util.fullscreenExit + /// + public const string FullscreenExit = "util.fullscreenExit"; + /// + /// util.expand + /// + public const string Expand = "util.expand"; + /// + /// util.collapse + /// + public const string Collapse = "util.collapse"; + /// + /// util.unedit + /// + public const string Unedit = "util.unedit"; +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Configs/NgZorroOptionsService.cs b/src/Util.Ui.NgZorro/Configs/NgZorroOptionsService.cs new file mode 100644 index 000000000..15d2451a7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Configs/NgZorroOptionsService.cs @@ -0,0 +1,40 @@ +using Microsoft.Extensions.Options; +using Util.Helpers; + +namespace Util.Ui.NgZorro.Configs; + +/// +/// NgZorro配置服务 +/// +public static class NgZorroOptionsService { + /// + /// NgZorro配置 + /// + private static readonly AsyncLocal _options = new(); + + /// + /// 获取NgZorro配置 + /// + public static NgZorroOptions GetOptions() { + if ( _options.Value != null ) + return _options.Value; + var options = Ioc.Create>(); + if ( options == null ) + return new NgZorroOptions(); + return options.Value; + } + + /// + /// 设置NgZorro配置 + /// + public static void SetOptions( NgZorroOptions options ) { + _options.Value = options; + } + + /// + /// 清空NgZorro配置 + /// + public static void ClearOptions() { + _options.Value = null; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Controllers/GenerateHtmlController.cs b/src/Util.Ui.NgZorro/Controllers/GenerateHtmlController.cs new file mode 100644 index 000000000..906559337 --- /dev/null +++ b/src/Util.Ui.NgZorro/Controllers/GenerateHtmlController.cs @@ -0,0 +1,28 @@ +using Util.Ui.Razor; + +namespace Util.Ui.NgZorro.Controllers; + +/// +/// Razor生成Html控制器 +/// +[ApiController] +[Route( "api/html" )] +public class GenerateHtmlController : ControllerBase { + /// + /// 生成所有Razor页面的Html + /// + [HttpGet] + public async Task GenerateAsync() { + var message = new StringBuilder(); + var result = await HtmlGenerator.GenerateAsync(); + message.AppendLine( "======================= 欢迎使用 Util 应用框架 - 开始生成全部Razor页面html =======================" ); + message.AppendLine(); + message.AppendLine(); + foreach ( var path in result ) + message.AppendLine( path ); + message.AppendLine(); + message.AppendLine(); + message.Append( "======================================== html文件生成完成 ========================================" ); + return message.ToString(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Controllers/NgZorroTreeControllerBase.cs b/src/Util.Ui.NgZorro/Controllers/NgZorroTreeControllerBase.cs new file mode 100644 index 000000000..df1039778 --- /dev/null +++ b/src/Util.Ui.NgZorro/Controllers/NgZorroTreeControllerBase.cs @@ -0,0 +1,60 @@ +using Util.Applications.Controllers; +using Util.Applications.Dtos; +using Util.Applications.Trees; +using Util.Data.Trees; +using Util.Ui.NgZorro.Services; + +namespace Util.Ui.NgZorro.Controllers; + +/// +/// NgZorro树形控制器 +/// +/// 参数类型 +/// 查询参数类型 +public abstract class NgZorroTreeControllerBase : NgZorroTreeControllerBase + where TDto : TreeDtoBase, new() + where TQuery : class, ITreeQueryParameter { + /// + /// 初始化树形控制器 + /// + /// 树形服务 + protected NgZorroTreeControllerBase( ITreeService service ) : base( service ) { + } +} + +/// +/// NgZorro树形控制器 +/// +/// 参数类型 +/// 创建参数类型 +/// 修改参数类型 +/// 查询参数类型 +public abstract class NgZorroTreeControllerBase : TreeControllerBase + where TDto : TreeDtoBase, new() + where TCreateRequest : IRequest, new() + where TUpdateRequest : IDto, new() + where TQuery : class, ITreeQueryParameter { + /// + /// 树形服务 + /// + private readonly ITreeService _service; + + /// + /// 初始化树形控制器 + /// + /// 树形服务 + protected NgZorroTreeControllerBase( ITreeService service ) : base( service ) { + _service = service; + } + + /// + /// 树形查询 + /// + /// 查询参数 + protected async Task TreeQueryAsync( TQuery query ) { + var action = new NgZorroTreeQueryAction( _service, GetLoadMode(), GetOperation( query ), + GetMaxPageSize(), IsFirstLoad(), IsExpandAll(), IsExpandForRootAsync(), GetLoadKeys( query ), QueryBefore, QueryAfter ); + var result = await action.QueryAsync( query ); + return Success( result ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Controllers/NgZorroTreeQueryControllerBase.cs b/src/Util.Ui.NgZorro/Controllers/NgZorroTreeQueryControllerBase.cs new file mode 100644 index 000000000..28878ef71 --- /dev/null +++ b/src/Util.Ui.NgZorro/Controllers/NgZorroTreeQueryControllerBase.cs @@ -0,0 +1,39 @@ +using Util.Applications.Controllers; +using Util.Applications.Trees; +using Util.Data.Trees; +using Util.Ui.NgZorro.Services; + +namespace Util.Ui.NgZorro.Controllers; + +/// +/// NgZorro树形查询控制器 +/// +/// 参数类型 +/// 查询参数类型 +public abstract class NgZorroTreeQueryControllerBase : TreeQueryControllerBase + where TDto : TreeDtoBase, new() + where TQuery : class, ITreeQueryParameter { + /// + /// 树形查询服务 + /// + private readonly ITreeQueryService _service; + + /// + /// 初始化树形查询控制器 + /// + /// 树形服务 + protected NgZorroTreeQueryControllerBase( ITreeQueryService service ) : base( service ) { + _service = service; + } + + /// + /// 树形查询 + /// + /// 查询参数 + protected async Task TreeQueryAsync( TQuery query ) { + var action = new NgZorroTreeQueryAction( _service, GetLoadMode(), GetOperation( query ), + GetMaxPageSize(), IsFirstLoad(), IsExpandAll(), IsExpandForRootAsync(), GetLoadKeys( query ), QueryBefore, QueryAfter ); + var result = await action.QueryAsync( query ); + return Success( result ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Data/NgZorroTreeNode.cs b/src/Util.Ui.NgZorro/Data/NgZorroTreeNode.cs new file mode 100644 index 000000000..dcfc6a041 --- /dev/null +++ b/src/Util.Ui.NgZorro/Data/NgZorroTreeNode.cs @@ -0,0 +1,63 @@ +namespace Util.Ui.NgZorro.Data; + +/// +/// Ng-Zorro树节点 +/// +public class NgZorroTreeNode { + /// + /// 标识 + /// + public string Key { get; set; } + /// + /// 标题 + /// + public string Title { get; set; } + /// + /// 图标 + /// + public string Icon { get; set; } + /// + /// 是否叶节点 + /// + public bool IsLeaf { get; set; } + /// + /// 是否展开 + /// + public bool Expanded { get; set; } + /// + /// 是否禁用 + /// + public bool Disabled { get; set; } + /// + /// 是否禁用复选框 + /// + public bool DisableCheckbox { get; set; } + /// + /// 是否可选中 + /// + public bool Selectable { get; set; } = true; + /// + /// 复选框是否被选中 + /// + public bool Checked { get; set; } + /// + /// 当前节点是否被选中 + /// + public bool Selected { get; set; } + /// + /// 原始节点 + /// + public object OriginalNode { get; set; } + /// + /// 子节点列表 + /// + public List Children { get; set; } + + /// + /// 获取原始节点 + /// + /// 原始节点类型 + public TNode GetOriginalNode() { + return Util.Helpers.Convert.To( OriginalNode ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Data/NgZorroTreeResult.cs b/src/Util.Ui.NgZorro/Data/NgZorroTreeResult.cs new file mode 100644 index 000000000..1958ba909 --- /dev/null +++ b/src/Util.Ui.NgZorro/Data/NgZorroTreeResult.cs @@ -0,0 +1,28 @@ +namespace Util.Ui.NgZorro.Data; + +/// +/// Ng-Zorro树形结果 +/// +public class NgZorroTreeResult { + /// + /// 初始化树形结果 + /// + public NgZorroTreeResult() { + Nodes = new List(); + CheckedKeys = new List(); + SelectedKeys = new List(); + } + + /// + /// 树节点列表 + /// + public List Nodes { get; set; } + /// + /// 复选框选中节点标识列表 + /// + public List CheckedKeys { get; set; } + /// + /// 选中节点标识列表 + /// + public List SelectedKeys { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Data/TreeResult.cs b/src/Util.Ui.NgZorro/Data/TreeResult.cs new file mode 100644 index 000000000..5491dc0a7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Data/TreeResult.cs @@ -0,0 +1,57 @@ +using Util.Applications.Trees; + +namespace Util.Ui.NgZorro.Data; + +/// +/// 树形结果 +/// +public class TreeResult : TreeResultBase where TNode : TreeDtoBase { + /// + /// 树形结果 + /// + private readonly NgZorroTreeResult _result; + + /// + /// 初始化树形结果 + /// + /// 树形参数列表 + /// 是否异步加载 + /// 所有节点是否全部展开 + public TreeResult( IEnumerable data, bool async = false, bool allExpand = false ) : base( data, async, allExpand ) { + _result = new NgZorroTreeResult(); + } + + /// + /// 转换为目标节点 + /// + protected override NgZorroTreeNode ToDestinationNode( TNode dto ) { + List children = dto.Children; + dto.Children = null; + var result = new NgZorroTreeNode { + Key = dto.Id, + Title = dto.GetText(), + Icon = dto.Icon, + Disabled = !dto.Enabled.SafeValue(), + Expanded = dto.Expanded.SafeValue(), + Checked = dto.Checked.SafeValue(), + DisableCheckbox = dto.DisableCheckbox.SafeValue(), + Selectable = dto.Selectable.SafeValue(), + Selected = dto.Selected.SafeValue(), + IsLeaf = dto.Leaf.SafeValue(), + OriginalNode = dto, + Children = children.Select( ToDestinationNode ).ToList() + }; + return result; + } + + /// + /// 转换为结果 + /// + protected override NgZorroTreeResult ToResult( List nodes ) { + var data = GetData(); + _result.CheckedKeys = data.Where( t => t.Checked == true ).Select( t => t.Id ).ToList(); + _result.SelectedKeys = data.Where( t => t.Selected == true ).Select( t => t.Id ).ToList(); + _result.Nodes = nodes; + return _result; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Directives/Popconfirms/TagBuilderExtensions.cs b/src/Util.Ui.NgZorro/Directives/Popconfirms/TagBuilderExtensions.cs new file mode 100644 index 000000000..d005630d2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Directives/Popconfirms/TagBuilderExtensions.cs @@ -0,0 +1,92 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Directives.Popconfirms; + +/// +/// 气泡确认框扩展 +/// +public static class TagBuilderExtensions { + /// + /// 气泡确认框指令扩展 + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + public static TBuilder Popconfirm( this TBuilder builder, Config config ) where TBuilder : TagBuilder { + builder.PopconfirmTitle( config.GetValue( UiConst.PopconfirmTitle ) ); + builder.BindPopconfirmTitle( config.GetValue( AngularConst.BindPopconfirmTitle ) ); + builder.AttributeIfNotEmpty( "nzPopconfirmTrigger", config.GetValue( UiConst.PopconfirmTrigger )?.Description() ); + builder.AttributeIfNotEmpty( "[nzPopconfirmTrigger]", config.GetValue( AngularConst.BindPopconfirmTrigger ) ); + builder.AttributeIfNotEmpty( "nzPopconfirmPlacement", config.GetValue( UiConst.PopconfirmPlacement )?.Description() ); + builder.AttributeIfNotEmpty( "[nzPopconfirmPlacement]", config.GetValue( AngularConst.BindPopconfirmPlacement ) ); + builder.AttributeIfNotEmpty( "[nzPopconfirmOrigin]", config.GetValue( UiConst.PopconfirmOrigin ) ); + builder.AttributeIfNotEmpty( "[nzPopconfirmVisible]", config.GetValue( UiConst.PopconfirmVisible ) ); + builder.AttributeIfNotEmpty( "[(nzPopconfirmVisible)]", config.GetValue( AngularConst.BindonPopconfirmVisible ) ); + builder.AttributeIfNotEmpty( "[nzPopconfirmShowArrow]", config.GetValue( UiConst.PopconfirmShowArrow ) ); + builder.AttributeIfNotEmpty( "[nzPopconfirmArrowPointAtCenter]", config.GetValue( UiConst.PopconfirmArrowPointAtCenter ) ); + builder.AttributeIfNotEmpty( "[nzPopconfirmMouseEnterDelay]", config.GetValue( UiConst.PopconfirmMouseEnterDelay ) ); + builder.AttributeIfNotEmpty( "[nzPopconfirmMouseLeaveDelay]", config.GetValue( UiConst.PopconfirmMouseLeaveDelay ) ); + builder.AttributeIfNotEmpty( "nzPopconfirmOverlayClassName", config.GetValue( UiConst.PopconfirmOverlayClassName ) ); + builder.AttributeIfNotEmpty( "[nzPopconfirmOverlayClassName]", config.GetValue( AngularConst.BindPopconfirmOverlayClassName ) ); + builder.AttributeIfNotEmpty( "[nzPopconfirmOverlayStyle]", config.GetValue( UiConst.PopconfirmOverlayStyle ) ); + builder.AttributeIfNotEmpty( "[nzPopconfirmBackdrop]", config.GetValue( UiConst.PopconfirmBackdrop ) ); + builder.AttributeIfNotEmpty( "nzCancelText", config.GetValue( UiConst.PopconfirmCancelText ) ); + builder.AttributeIfNotEmpty( "[nzCancelText]", config.GetValue( AngularConst.BindPopconfirmCancelText ) ); + builder.AttributeIfNotEmpty( "nzOkText", config.GetValue( UiConst.PopconfirmOkText ) ); + builder.AttributeIfNotEmpty( "[nzOkText]", config.GetValue( AngularConst.BindPopconfirmOkText ) ); + builder.AttributeIfNotEmpty( "nzOkType", config.GetValue( UiConst.PopconfirmOkType )?.Description() ); + builder.AttributeIfNotEmpty( "[nzOkType]", config.GetValue( AngularConst.BindPopconfirmOkType ) ); + builder.AttributeIfNotEmpty( "[nzOkDanger]", config.GetValue( UiConst.PopconfirmOkDanger ) ); + builder.AttributeIfNotEmpty( "[nzOkDisabled]", config.GetValue( UiConst.PopconfirmOkDisabled ) ); + builder.AttributeIfNotEmpty( "nzAutoFocus", config.GetValue( UiConst.PopconfirmAutoFocus )?.Description() ); + builder.AttributeIfNotEmpty( "[nzAutoFocus]", config.GetValue( AngularConst.BindPopconfirmAutoFocus ) ); + builder.AttributeIfNotEmpty( "[nzCondition]", config.GetValue( UiConst.PopconfirmCondition ) ); + builder.AttributeIfNotEmpty( "nzIcon", config.GetValue( UiConst.PopconfirmIcon )?.Description() ); + builder.AttributeIfNotEmpty( "[nzIcon]", config.GetValue( AngularConst.BindPopconfirmIcon ) ); + builder.AttributeIfNotEmpty( "[nzBeforeConfirm]", config.GetValue( UiConst.PopconfirmBeforeConfirm ) ); + builder.AttributeIfNotEmpty( "(nzPopconfirmVisibleChange)", config.GetValue( UiConst.OnPopconfirmVisibleChange ) ); + builder.AttributeIfNotEmpty( "(nzOnCancel)", config.GetValue( UiConst.OnPopconfirmCancel ) ); + builder.AttributeIfNotEmpty( "(nzOnConfirm)", config.GetValue( UiConst.OnPopconfirmConfirm ) ); + return builder; + } + + /// + /// nzPopconfirmTitle,气泡确认框标题 + /// + /// 生成器类型 + /// 生成器实例 + /// 值 + public static TBuilder PopconfirmTitle( this TBuilder builder, string value ) where TBuilder : TagBuilder { + builder.AttributeIf( "nz-popconfirm", value.IsEmpty() == false ); + SetPopconfirmTitle( builder, value ); + return builder; + } + + /// + /// 设置标题 + /// + public static void SetPopconfirmTitle( TagBuilder builder, string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + builder.AttributeByI18n( "[nzPopconfirmTitle]", value ); + return; + } + builder.AttributeIfNotEmpty( "nzPopconfirmTitle", value ); + } + + /// + /// [nzPopconfirmTitle],气泡确认框标题 + /// + /// 生成器类型 + /// 生成器实例 + /// 值 + public static TBuilder BindPopconfirmTitle( this TBuilder builder, string value ) where TBuilder : TagBuilder { + builder.AttributeIf( "nz-popconfirm", value.IsEmpty() == false ); + builder.AttributeIfNotEmpty( "[nzPopconfirmTitle]", value ); + return builder; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Directives/Popover/TagBuilderExtensions.cs b/src/Util.Ui.NgZorro/Directives/Popover/TagBuilderExtensions.cs new file mode 100644 index 000000000..ddd67fcd1 --- /dev/null +++ b/src/Util.Ui.NgZorro/Directives/Popover/TagBuilderExtensions.cs @@ -0,0 +1,79 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Directives.Popover; + +/// +/// 气泡框扩展 +/// +public static class TagBuilderExtensions { + /// + /// 气泡框指令扩展 + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + public static TBuilder Popover( this TBuilder builder, Config config ) where TBuilder : TagBuilder { + builder.AttributeIf( "nz-popover", string.IsNullOrWhiteSpace( config.GetValue( UiConst.PopoverContent ) ) == false ); + builder.AttributeIf( "nz-popover", string.IsNullOrWhiteSpace( config.GetValue( AngularConst.BindPopoverContent ) ) == false ); + builder.PopoverTitle( config.GetValue( UiConst.PopoverTitle ) ); + builder.BindPopoverTitle( config.GetValue( AngularConst.BindPopoverTitle ) ); + builder.AttributeIfNotEmpty( "nzPopoverContent", config.GetValue( UiConst.PopoverContent ) ); + builder.AttributeIfNotEmpty( "[nzPopoverContent]", config.GetValue( AngularConst.BindPopoverContent ) ); + builder.AttributeIfNotEmpty( "nzPopoverTrigger", config.GetValue( UiConst.PopoverTrigger )?.Description() ); + builder.AttributeIfNotEmpty( "[nzPopoverTrigger]", config.GetValue( AngularConst.BindPopoverTrigger ) ); + builder.AttributeIfNotEmpty( "nzPopoverPlacement", config.GetValue( UiConst.PopoverPlacement )?.Description() ); + builder.AttributeIfNotEmpty( "[nzPopoverPlacement]", config.GetValue( AngularConst.BindPopoverPlacement ) ); + builder.AttributeIfNotEmpty( "[nzPopoverArrowPointAtCenter]", config.GetValue( UiConst.PopoverArrowPointAtCenter ) ); + builder.AttributeIfNotEmpty( "[nzPopoverOrigin]", config.GetValue( UiConst.PopoverOrigin ) ); + builder.AttributeIfNotEmpty( "[nzPopoverVisible]", config.GetValue( UiConst.PopoverVisible ) ); + builder.AttributeIfNotEmpty( "[(nzPopoverVisible)]", config.GetValue( AngularConst.BindonPopoverVisible ) ); + builder.AttributeIfNotEmpty( "[nzPopoverMouseEnterDelay]", config.GetValue( UiConst.PopoverMouseEnterDelay ) ); + builder.AttributeIfNotEmpty( "[nzPopoverMouseLeaveDelay]", config.GetValue( UiConst.PopoverMouseLeaveDelay ) ); + builder.AttributeIfNotEmpty( "nzPopoverOverlayClassName", config.GetValue( UiConst.PopoverOverlayClassName ) ); + builder.AttributeIfNotEmpty( "[nzPopoverOverlayClassName]", config.GetValue( AngularConst.BindPopoverOverlayClassName ) ); + builder.AttributeIfNotEmpty( "[nzPopoverOverlayStyle]", config.GetValue( UiConst.PopoverOverlayStyle ) ); + builder.AttributeIfNotEmpty( "[nzPopoverBackdrop]", config.GetValue( UiConst.PopoverBackdrop ) ); + builder.AttributeIfNotEmpty( "(nzPopoverVisibleChange)", config.GetValue( UiConst.OnPopoverVisibleChange ) ); + return builder; + } + + /// + /// nzPopoverTitle,气泡卡片标题 + /// + /// 生成器类型 + /// 生成器实例 + /// 值 + public static TBuilder PopoverTitle( this TBuilder builder, string value ) where TBuilder : TagBuilder { + builder.AttributeIf( "nz-popover", value.IsEmpty() == false ); + SetPopoverTitle( builder, value ); + return builder; + } + + /// + /// 设置标题 + /// + public static void SetPopoverTitle( TagBuilder builder, string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + builder.AttributeByI18n( "[nzPopoverTitle]", value ); + return; + } + builder.AttributeIfNotEmpty( "nzPopoverTitle", value ); + } + + /// + /// [nzPopoverTitle],气泡卡片标题 + /// + /// 生成器类型 + /// 生成器实例 + /// 值 + public static TBuilder BindPopoverTitle( this TBuilder builder, string value ) where TBuilder : TagBuilder { + builder.AttributeIf( "nz-popover", value.IsEmpty() == false ); + builder.AttributeIfNotEmpty( "[nzPopoverTitle]", value ); + return builder; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Directives/Tooltips/TagBuilderExtensions.cs b/src/Util.Ui.NgZorro/Directives/Tooltips/TagBuilderExtensions.cs new file mode 100644 index 000000000..0900724a3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Directives/Tooltips/TagBuilderExtensions.cs @@ -0,0 +1,132 @@ +using Util.Ui.Angular.Configs; +using Util.Ui.Builders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Extensions; + +namespace Util.Ui.NgZorro.Directives.Tooltips; + +/// +/// 文字提示扩展 +/// +public static class TagBuilderExtensions { + /// + /// 文字提示指令扩展 + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + public static TBuilder Tooltip( this TBuilder builder, Config config ) where TBuilder : TagBuilder { + TooltipTitleUpdate( builder, config ); + TooltipTitleDelete( builder, config ); + TooltipTitleDetail( builder, config ); + builder.TooltipTitle( config.GetValue( UiConst.TooltipTitle ) ); + builder.BindTooltipTitle( config.GetValue( AngularConst.BindTooltipTitle ) ); + builder.AttributeIfNotEmpty( "nzTooltipPlacement", config.GetValue( UiConst.TooltipPlacement )?.Description() ); + builder.AttributeIfNotEmpty( "[nzTooltipPlacement]", config.GetValue( AngularConst.BindTooltipPlacement ) ); + builder.AttributeIfNotEmpty( "[nzTooltipArrowPointAtCenter]", config.GetValue( UiConst.TooltipArrowPointAtCenter ) ); + builder.AttributeIfNotEmpty( "[nzTooltipTitleContext]", config.GetValue( UiConst.TooltipTitleContext ) ); + builder.AttributeIfNotEmpty( "nzTooltipTrigger", config.GetValue( UiConst.TooltipTrigger )?.Description() ); + builder.AttributeIfNotEmpty( "[nzTooltipTrigger]", config.GetValue( AngularConst.BindTooltipTrigger ) ); + builder.AttributeIfNotEmpty( "nzTooltipColor", config.GetValue( UiConst.TooltipColorType )?.Description() ); + builder.AttributeIfNotEmpty( "nzTooltipColor", config.GetValue( UiConst.TooltipColor ) ); + builder.AttributeIfNotEmpty( "[nzTooltipColor]", config.GetValue( AngularConst.BindTooltipColor ) ); + builder.AttributeIfNotEmpty( "[nzTooltipOrigin]", config.GetValue( UiConst.TooltipOrigin ) ); + builder.AttributeIfNotEmpty( "[nzTooltipVisible]", config.GetValue( UiConst.TooltipVisible ) ); + builder.AttributeIfNotEmpty( "[(nzTooltipVisible)]", config.GetValue( AngularConst.BindonTooltipVisible ) ); + builder.AttributeIfNotEmpty( "[nzTooltipMouseEnterDelay]", config.GetValue( UiConst.TooltipMouseEnterDelay ) ); + builder.AttributeIfNotEmpty( "[nzTooltipMouseLeaveDelay]", config.GetValue( UiConst.TooltipMouseLeaveDelay ) ); + builder.AttributeIfNotEmpty( "nzTooltipOverlayClassName", config.GetValue( UiConst.TooltipOverlayClassName ) ); + builder.AttributeIfNotEmpty( "[nzTooltipOverlayClassName]", config.GetValue( AngularConst.BindTooltipOverlayClassName ) ); + builder.AttributeIfNotEmpty( "[nzTooltipOverlayStyle]", config.GetValue( UiConst.TooltipOverlayStyle ) ); + builder.AttributeIfNotEmpty( "(nzTooltipVisibleChange)", config.GetValue( UiConst.OnTooltipVisibleChange ) ); + return builder; + } + + /// + /// 设置nzTooltipTitle为update + /// + /// 生成器实例 + /// 配置 + private static void TooltipTitleUpdate( TagBuilder builder, Config config ) { + var value = config.GetValue( UiConst.TooltipTitleUpdate ); + if ( value != true ) + return; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + builder.TooltipTitle( I18nKeys.Update ); + return; + } + builder.TooltipTitle( "Update" ); + } + + /// + /// 设置nzTooltipTitle为delete + /// + /// 生成器实例 + /// 配置 + private static void TooltipTitleDelete( TagBuilder builder, Config config ) { + var value = config.GetValue( UiConst.TooltipTitleDelete ); + if ( value != true ) + return; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + builder.TooltipTitle( I18nKeys.Delete ); + return; + } + builder.TooltipTitle( "Delete" ); + } + + /// + /// 设置nzTooltipTitle为detail + /// + /// 生成器实例 + /// 配置 + private static void TooltipTitleDetail( TagBuilder builder, Config config ) { + var value = config.GetValue( UiConst.TooltipTitleDetail ); + if ( value != true ) + return; + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + builder.TooltipTitle( I18nKeys.Detail ); + return; + } + builder.TooltipTitle( "Detail" ); + } + + /// + /// nzTooltipTitle,提示文字 + /// + /// 生成器类型 + /// 生成器实例 + /// 值 + public static TBuilder TooltipTitle( this TBuilder builder, string value ) where TBuilder : TagBuilder { + builder.AttributeIf( "nz-tooltip", value.IsEmpty() == false ); + SetTooltipTitle( builder, value ); + return builder; + } + + /// + /// 设置标题 + /// + public static void SetTooltipTitle( TagBuilder builder, string value ) { + var options = NgZorroOptionsService.GetOptions(); + if ( options.EnableI18n ) { + builder.AttributeByI18n( "[nzTooltipTitle]", value ); + return; + } + builder.AttributeIfNotEmpty( "nzTooltipTitle", value ); + } + + /// + /// [nzTooltipTitle],提示文字 + /// + /// 生成器类型 + /// 生成器实例 + /// 值 + public static TBuilder BindTooltipTitle( this TBuilder builder, string value ) where TBuilder : TagBuilder { + builder.AttributeIf( "nz-tooltip", value.IsEmpty() == false ); + builder.AttributeIfNotEmpty( "[nzTooltipTitle]", value ); + return builder; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/AlertType.cs b/src/Util.Ui.NgZorro/Enums/AlertType.cs new file mode 100644 index 000000000..3faafcf00 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/AlertType.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 警告提示类型 +/// +public enum AlertType { + /// + /// 成功 + /// + [Description( "success" )] + Success, + /// + /// 信息 + /// + [Description( "info" )] + Info, + /// + /// 警告 + /// + [Description( "warning" )] + Warning, + /// + /// 错误 + /// + [Description( "error" )] + Error +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/Align.cs b/src/Util.Ui.NgZorro/Enums/Align.cs new file mode 100644 index 000000000..93dfe8d39 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/Align.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 垂直对齐方式 +/// +public enum Align { + /// + /// 顶部对齐 + /// + [Description( "top" )] + Top, + /// + /// 中间对齐 + /// + [Description( "middle" )] + Middle, + /// + /// 底部对齐 + /// + [Description( "bottom" )] + Bottom +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/AnchorDirection.cs b/src/Util.Ui.NgZorro/Enums/AnchorDirection.cs new file mode 100644 index 000000000..b75c4ac53 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/AnchorDirection.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 锚点方向 +/// +public enum AnchorDirection { + /// + /// horizontal, 水平方向 + /// + [Description( "horizontal" )] + Horizontal, + /// + /// vertical, 垂直方向 + /// + [Description( "vertical" )] + Vertical +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/AntDesignColor.cs b/src/Util.Ui.NgZorro/Enums/AntDesignColor.cs new file mode 100644 index 000000000..04ecfcf8a --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/AntDesignColor.cs @@ -0,0 +1,77 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// Ant Design颜色 +/// +public enum AntDesignColor { + /// + /// pink, 粉红色 + /// + [Description( "pink" )] + Pink, + /// + /// red, 薄暮 - 红色 + /// + [Description( "red" )] + Red, + /// + /// magenta, 洋红 + /// + [Description( "magenta" )] + Magenta, + /// + /// volcano, 火山 + /// + [Description( "volcano" )] + Volcano, + /// + /// orange, 日暮 - 橘黄色 + /// + [Description( "orange" )] + Orange, + /// + /// gold, 金盏花 - 黄金色 + /// + [Description( "gold" )] + Gold, + /// + /// lime, 青柠 + /// + [Description( "lime" )] + Lime, + /// + /// green, 极光绿 + /// + [Description( "green" )] + Green, + /// + /// cyan, 明青 + /// + [Description( "cyan" )] + Cyan, + /// + /// blue, 拂晓蓝 + /// + [Description( "blue" )] + Blue, + /// + /// geekblue, 极客蓝 + /// + [Description( "geekblue" )] + GeekBlue, + /// + /// purple, 酱紫 + /// + [Description( "purple" )] + Purple, + /// + /// yellow, 黄色 + /// + [Description( "yellow" )] + Yellow, + /// + /// gray,灰色 + /// + [Description( "gray" )] + Gray +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/AntDesignIcon.cs b/src/Util.Ui.NgZorro/Enums/AntDesignIcon.cs new file mode 100644 index 000000000..617e4ab9b --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/AntDesignIcon.cs @@ -0,0 +1,2112 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// Ant Design图标 +/// +public enum AntDesignIcon { + /// + ///step-backward + /// + [Description( "step-backward" )] + StepBackward, + /// + ///step-forward + /// + [Description( "step-forward" )] + StepForward, + /// + ///fast-backward + /// + [Description( "fast-backward" )] + FastBackward, + /// + ///fast-forward + /// + [Description( "fast-forward" )] + FastForward, + /// + ///shrink + /// + [Description( "shrink" )] + Shrink, + /// + ///arrows-alt + /// + [Description( "arrows-alt" )] + ArrowsAlt, + /// + ///down + /// + [Description( "down" )] + Down, + /// + ///up + /// + [Description( "up" )] + Up, + /// + ///left + /// + [Description( "left" )] + Left, + /// + ///right + /// + [Description( "right" )] + Right, + /// + ///caret-up + /// + [Description( "caret-up" )] + CaretUp, + /// + ///caret-down + /// + [Description( "caret-down" )] + CaretDown, + /// + ///caret-left + /// + [Description( "caret-left" )] + CaretLeft, + /// + ///caret-right + /// + [Description( "caret-right" )] + CaretRight, + /// + ///up-circle + /// + [Description( "up-circle" )] + UpCircle, + /// + ///down-circle + /// + [Description( "down-circle" )] + DownCircle, + /// + ///left-circle + /// + [Description( "left-circle" )] + LeftCircle, + /// + ///right-circle + /// + [Description( "right-circle" )] + RightCircle, + /// + ///double-right + /// + [Description( "double-right" )] + DoubleRight, + /// + ///double-left + /// + [Description( "double-left" )] + DoubleLeft, + /// + ///vertical-left + /// + [Description( "vertical-left" )] + VerticalLeft, + /// + ///vertical-right + /// + [Description( "vertical-right" )] + VerticalRight, + /// + ///vertical-align-top + /// + [Description( "vertical-align-top" )] + VerticalAlignTop, + /// + ///vertical-align-middle + /// + [Description( "vertical-align-middle" )] + VerticalAlignMiddle, + /// + ///vertical-align-bottom + /// + [Description( "vertical-align-bottom" )] + VerticalAlignBottom, + /// + ///forward + /// + [Description( "forward" )] + Forward, + /// + ///backward + /// + [Description( "backward" )] + Backward, + /// + ///rollback + /// + [Description( "rollback" )] + Rollback, + /// + ///enter + /// + [Description( "enter" )] + Enter, + /// + ///retweet + /// + [Description( "retweet" )] + Retweet, + /// + ///swap + /// + [Description( "swap" )] + Swap, + /// + ///swap-left + /// + [Description( "swap-left" )] + SwapLeft, + /// + ///swap-right + /// + [Description( "swap-right" )] + SwapRight, + /// + ///arrow-up + /// + [Description( "arrow-up" )] + ArrowUp, + /// + ///arrow-down + /// + [Description( "arrow-down" )] + ArrowDown, + /// + ///arrow-left + /// + [Description( "arrow-left" )] + ArrowLeft, + /// + ///arrow-right + /// + [Description( "arrow-right" )] + ArrowRight, + /// + ///play-circle + /// + [Description( "play-circle" )] + PlayCircle, + /// + ///up-square + /// + [Description( "up-square" )] + UpSquare, + /// + ///down-square + /// + [Description( "down-square" )] + DownSquare, + /// + ///left-square + /// + [Description( "left-square" )] + LeftSquare, + /// + ///right-square + /// + [Description( "right-square" )] + RightSquare, + /// + ///login + /// + [Description( "login" )] + Login, + /// + ///logout + /// + [Description( "logout" )] + Logout, + /// + ///menu-fold + /// + [Description( "menu-fold" )] + MenuFold, + /// + ///menu-unfold + /// + [Description( "menu-unfold" )] + MenuUnfold, + /// + ///border-bottom + /// + [Description( "border-bottom" )] + BorderBottom, + /// + ///border-horizontal + /// + [Description( "border-horizontal" )] + BorderHorizontal, + /// + ///border-inner + /// + [Description( "border-inner" )] + BorderInner, + /// + ///border-outer + /// + [Description( "border-outer" )] + BorderOuter, + /// + ///border-left + /// + [Description( "border-left" )] + BorderLeft, + /// + ///border-right + /// + [Description( "border-right" )] + BorderRight, + /// + ///border-top + /// + [Description( "border-top" )] + BorderTop, + /// + ///border-verticle + /// + [Description( "border-verticle" )] + BorderVerticle, + /// + ///pic-center + /// + [Description( "pic-center" )] + PicCenter, + /// + ///pic-left + /// + [Description( "pic-left" )] + PicLeft, + /// + ///pic-right + /// + [Description( "pic-right" )] + PicRight, + /// + ///radius-bottomleft + /// + [Description( "radius-bottomleft" )] + RadiusBottomleft, + /// + ///radius-bottomright + /// + [Description( "radius-bottomright" )] + RadiusBottomright, + /// + ///radius-upleft + /// + [Description( "radius-upleft" )] + RadiusUpleft, + /// + ///radius-upright + /// + [Description( "radius-upright" )] + RadiusUpright, + /// + ///fullscreen + /// + [Description( "fullscreen" )] + Fullscreen, + /// + ///fullscreen-exit + /// + [Description( "fullscreen-exit" )] + FullscreenExit, + /// + ///question + /// + [Description( "question" )] + Question, + /// + ///question-circle + /// + [Description( "question-circle" )] + QuestionCircle, + /// + ///plus + /// + [Description( "plus" )] + Plus, + /// + ///plus-circle + /// + [Description( "plus-circle" )] + PlusCircle, + /// + ///pause + /// + [Description( "pause" )] + Pause, + /// + ///pause-circle + /// + [Description( "pause-circle" )] + PauseCircle, + /// + ///minus + /// + [Description( "minus" )] + Minus, + /// + ///minus-circle + /// + [Description( "minus-circle" )] + MinusCircle, + /// + ///plus-square + /// + [Description( "plus-square" )] + PlusSquare, + /// + ///minus-square + /// + [Description( "minus-square" )] + MinusSquare, + /// + ///info + /// + [Description( "info" )] + Info, + /// + ///info-circle + /// + [Description( "info-circle" )] + InfoCircle, + /// + ///exclamation + /// + [Description( "exclamation" )] + Exclamation, + /// + ///exclamation-circle + /// + [Description( "exclamation-circle" )] + ExclamationCircle, + /// + ///close + /// + [Description( "close" )] + Close, + /// + ///close-circle + /// + [Description( "close-circle" )] + CloseCircle, + /// + ///close-square + /// + [Description( "close-square" )] + CloseSquare, + /// + ///check + /// + [Description( "check" )] + Check, + /// + ///check-circle + /// + [Description( "check-circle" )] + CheckCircle, + /// + ///check-square + /// + [Description( "check-square" )] + CheckSquare, + /// + ///clock-circle + /// + [Description( "clock-circle" )] + ClockCircle, + /// + ///warning + /// + [Description( "warning" )] + Warning, + /// + ///issues-close + /// + [Description( "issues-close" )] + IssuesClose, + /// + ///stop + /// + [Description( "stop" )] + Stop, + /// + ///edit + /// + [Description( "edit" )] + Edit, + /// + ///form + /// + [Description( "form" )] + Form, + /// + ///copy + /// + [Description( "copy" )] + Copy, + /// + ///scissor + /// + [Description( "scissor" )] + Scissor, + /// + ///delete + /// + [Description( "delete" )] + Delete, + /// + ///snippets + /// + [Description( "snippets" )] + Snippets, + /// + ///diff + /// + [Description( "diff" )] + Diff, + /// + ///highlight + /// + [Description( "highlight" )] + Highlight, + /// + ///align-center + /// + [Description( "align-center" )] + AlignCenter, + /// + ///align-left + /// + [Description( "align-left" )] + AlignLeft, + /// + ///align-right + /// + [Description( "align-right" )] + AlignRight, + /// + ///bg-colors + /// + [Description( "bg-colors" )] + BgColors, + /// + ///bold + /// + [Description( "bold" )] + Bold, + /// + ///italic + /// + [Description( "italic" )] + Italic, + /// + ///underline + /// + [Description( "underline" )] + Underline, + /// + ///strikethrough + /// + [Description( "strikethrough" )] + Strikethrough, + /// + ///redo + /// + [Description( "redo" )] + Redo, + /// + ///undo + /// + [Description( "undo" )] + Undo, + /// + ///zoom-in + /// + [Description( "zoom-in" )] + ZoomIn, + /// + ///zoom-out + /// + [Description( "zoom-out" )] + ZoomOut, + /// + ///font-colors + /// + [Description( "font-colors" )] + FontColors, + /// + ///font-size + /// + [Description( "font-size" )] + FontSize, + /// + ///line-height + /// + [Description( "line-height" )] + LineHeight, + /// + ///dash + /// + [Description( "dash" )] + Dash, + /// + ///small-dash + /// + [Description( "small-dash" )] + SmallDash, + /// + ///sort-ascending + /// + [Description( "sort-ascending" )] + SortAscending, + /// + ///sort-descending + /// + [Description( "sort-descending" )] + SortDescending, + /// + ///drag + /// + [Description( "drag" )] + Drag, + /// + ///ordered-list + /// + [Description( "ordered-list" )] + OrderedList, + /// + ///unordered-list + /// + [Description( "unordered-list" )] + UnorderedList, + /// + ///radius-setting + /// + [Description( "radius-setting" )] + RadiusSetting, + /// + ///column-width + /// + [Description( "column-width" )] + ColumnWidth, + /// + ///column-height + /// + [Description( "column-height" )] + ColumnHeight, + /// + ///area-chart + /// + [Description( "area-chart" )] + AreaChart, + /// + ///pie-chart + /// + [Description( "pie-chart" )] + PieChart, + /// + ///bar-chart + /// + [Description( "bar-chart" )] + BarChart, + /// + ///dot-chart + /// + [Description( "dot-chart" )] + DotChart, + /// + ///line-chart + /// + [Description( "line-chart" )] + LineChart, + /// + ///radar-chart + /// + [Description( "radar-chart" )] + RadarChart, + /// + ///heat-map + /// + [Description( "heat-map" )] + HeatMap, + /// + ///fall + /// + [Description( "fall" )] + Fall, + /// + ///rise + /// + [Description( "rise" )] + Rise, + /// + ///stock + /// + [Description( "stock" )] + Stock, + /// + ///box-plot + /// + [Description( "box-plot" )] + BoxPlot, + /// + ///fund + /// + [Description( "fund" )] + Fund, + /// + ///sliders + /// + [Description( "sliders" )] + Sliders, + /// + ///android + /// + [Description( "android" )] + Android, + /// + ///apple + /// + [Description( "apple" )] + Apple, + /// + ///windows + /// + [Description( "windows" )] + Windows, + /// + ///ie + /// + [Description( "ie" )] + Ie, + /// + ///chrome + /// + [Description( "chrome" )] + Chrome, + /// + ///github + /// + [Description( "github" )] + Github, + /// + ///aliwangwang + /// + [Description( "aliwangwang" )] + Aliwangwang, + /// + ///dingding + /// + [Description( "dingding" )] + Dingding, + /// + ///weibo-square + /// + [Description( "weibo-square" )] + WeiboSquare, + /// + ///weibo-circle + /// + [Description( "weibo-circle" )] + WeiboCircle, + /// + ///taobao-circle + /// + [Description( "taobao-circle" )] + TaobaoCircle, + /// + ///html5 + /// + [Description( "html5" )] + Html5, + /// + ///weibo + /// + [Description( "weibo" )] + Weibo, + /// + ///twitter + /// + [Description( "twitter" )] + Twitter, + /// + ///wechat + /// + [Description( "wechat" )] + Wechat, + /// + ///youtube + /// + [Description( "youtube" )] + Youtube, + /// + ///alipay-circle + /// + [Description( "alipay-circle" )] + AlipayCircle, + /// + ///taobao + /// + [Description( "taobao" )] + Taobao, + /// + ///skype + /// + [Description( "skype" )] + Skype, + /// + ///qq + /// + [Description( "qq" )] + Qq, + /// + ///medium-workmark + /// + [Description( "medium-workmark" )] + MediumWorkmark, + /// + ///gitlab + /// + [Description( "gitlab" )] + Gitlab, + /// + ///medium + /// + [Description( "medium" )] + Medium, + /// + ///linkedin + /// + [Description( "linkedin" )] + Linkedin, + /// + ///google-plus + /// + [Description( "google-plus" )] + GooglePlus, + /// + ///dropbox + /// + [Description( "dropbox" )] + Dropbox, + /// + ///facebook + /// + [Description( "facebook" )] + Facebook, + /// + ///codepen + /// + [Description( "codepen" )] + Codepen, + /// + ///code-sandbox + /// + [Description( "code-sandbox" )] + CodeSandbox, + /// + ///amazon + /// + [Description( "amazon" )] + Amazon, + /// + ///google + /// + [Description( "google" )] + Google, + /// + ///codepen-circle + /// + [Description( "codepen-circle" )] + CodepenCircle, + /// + ///alipay + /// + [Description( "alipay" )] + Alipay, + /// + ///ant-design + /// + [Description( "ant-design" )] + AntDesign, + /// + ///ant-cloud + /// + [Description( "ant-cloud" )] + AntCloud, + /// + ///aliyun + /// + [Description( "aliyun" )] + Aliyun, + /// + ///zhihu + /// + [Description( "zhihu" )] + Zhihu, + /// + ///slack + /// + [Description( "slack" )] + Slack, + /// + ///slack-square + /// + [Description( "slack-square" )] + SlackSquare, + /// + ///behance + /// + [Description( "behance" )] + Behance, + /// + ///behance-square + /// + [Description( "behance-square" )] + BehanceSquare, + /// + ///dribbble + /// + [Description( "dribbble" )] + Dribbble, + /// + ///dribbble-square + /// + [Description( "dribbble-square" )] + DribbbleSquare, + /// + ///instagram + /// + [Description( "instagram" )] + Instagram, + /// + ///yuque + /// + [Description( "yuque" )] + Yuque, + /// + ///alibaba + /// + [Description( "alibaba" )] + Alibaba, + /// + ///yahoo + /// + [Description( "yahoo" )] + Yahoo, + /// + ///reddit + /// + [Description( "reddit" )] + Reddit, + /// + ///sketch + /// + [Description( "sketch" )] + Sketch, + /// + ///alert + /// + [Description( "alert" )] + Alert, + /// + ///aim + /// + [Description( "aim" )] + Aim, + /// + ///account-book + /// + [Description( "account-book" )] + AccountBook, + /// + ///apartment + /// + [Description( "apartment" )] + Apartment, + /// + ///api + /// + [Description( "api" )] + Api, + /// + ///appstore + /// + [Description( "appstore" )] + Appstore, + /// + ///appstore-add + /// + [Description( "appstore-add" )] + AppstoreAdd, + /// + ///barcode + /// + [Description( "barcode" )] + Barcode, + /// + ///bank + /// + [Description( "bank" )] + Bank, + /// + ///bars + /// + [Description( "bars" )] + Bars, + /// + ///bell + /// + [Description( "bell" )] + Bell, + /// + ///audio + /// + [Description( "audio" )] + Audio, + /// + ///block + /// + [Description( "block" )] + Block, + /// + ///border + /// + [Description( "border" )] + Border, + /// + ///borderless-table + /// + [Description( "borderless-table" )] + BorderlessTable, + /// + ///book + /// + [Description( "book" )] + Book, + /// + ///build + /// + [Description( "build" )] + Build, + /// + ///bug + /// + [Description( "bug" )] + Bug, + /// + ///bulb + /// + [Description( "bulb" )] + Bulb, + /// + ///branches + /// + [Description( "branches" )] + Branches, + /// + ///calendar + /// + [Description( "calendar" )] + Calendar, + /// + ///camera + /// + [Description( "camera" )] + Camera, + /// + ///car + /// + [Description( "car" )] + Car, + /// + ///calculator + /// + [Description( "calculator" )] + Calculator, + /// + ///carry-out + /// + [Description( "carry-out" )] + CarryOut, + /// + ///ci + /// + [Description( "ci" )] + Ci, + /// + ///clear + /// + [Description( "clear" )] + Clear, + /// + ///ci-circle + /// + [Description( "ci-circle" )] + CiCircle, + /// + ///cloud-server + /// + [Description( "cloud-server" )] + CloudServer, + /// + ///cloud + /// + [Description( "cloud" )] + Cloud, + /// + ///cloud-sync + /// + [Description( "cloud-sync" )] + CloudSync, + /// + ///cloud-download + /// + [Description( "cloud-download" )] + CloudDownload, + /// + ///code + /// + [Description( "code" )] + Code, + /// + ///cloud-upload + /// + [Description( "cloud-upload" )] + CloudUpload, + /// + ///coffee + /// + [Description( "coffee" )] + Coffee, + /// + ///comment + /// + [Description( "comment" )] + Comment, + /// + ///compress + /// + [Description( "compress" )] + Compress, + /// + ///console-sql + /// + [Description( "console-sql" )] + ConsoleSql, + /// + ///compass + /// + [Description( "compass" )] + Compass, + /// + ///container + /// + [Description( "container" )] + Container, + /// + ///cluster + /// + [Description( "cluster" )] + Cluster, + /// + ///control + /// + [Description( "control" )] + Control, + /// + ///contacts + /// + [Description( "contacts" )] + Contacts, + /// + ///copyright-circle + /// + [Description( "copyright-circle" )] + CopyrightCircle, + /// + ///copyright + /// + [Description( "copyright" )] + Copyright, + /// + ///customer-service + /// + [Description( "customer-service" )] + CustomerService, + /// + ///crown + /// + [Description( "crown" )] + Crown, + /// + ///database + /// + [Description( "database" )] + Database, + /// + ///credit-card + /// + [Description( "credit-card" )] + CreditCard, + /// + ///delete-column + /// + [Description( "delete-column" )] + DeleteColumn, + /// + ///dashboard + /// + [Description( "dashboard" )] + Dashboard, + /// + ///delivered-procedure + /// + [Description( "delivered-procedure" )] + DeliveredProcedure, + /// + ///desktop + /// + [Description( "desktop" )] + Desktop, + /// + ///disconnect + /// + [Description( "disconnect" )] + Disconnect, + /// + ///delete-row + /// + [Description( "delete-row" )] + DeleteRow, + /// + ///dislike + /// + [Description( "dislike" )] + Dislike, + /// + ///dollar-circle + /// + [Description( "dollar-circle" )] + DollarCircle, + /// + ///dingtalk + /// + [Description( "dingtalk" )] + Dingtalk, + /// + ///download + /// + [Description( "download" )] + Download, + /// + ///euro + /// + [Description( "euro" )] + Euro, + /// + ///exception + /// + [Description( "exception" )] + Exception, + /// + ///expand-alt + /// + [Description( "expand-alt" )] + ExpandAlt, + /// + ///experiment + /// + [Description( "experiment" )] + Experiment, + /// + ///expand + /// + [Description( "expand" )] + Expand, + /// + ///export + /// + [Description( "export" )] + Export, + /// + ///eye + /// + [Description( "eye" )] + Eye, + /// + ///environment + /// + [Description( "environment" )] + Environment, + /// + ///deployment-unit + /// + [Description( "deployment-unit" )] + DeploymentUnit, + /// + ///eye-invisible + /// + [Description( "eye-invisible" )] + EyeInvisible, + /// + ///field-time + /// + [Description( "field-time" )] + FieldTime, + /// + ///field-binary + /// + [Description( "field-binary" )] + FieldBinary, + /// + ///euro-circle + /// + [Description( "euro-circle" )] + EuroCircle, + /// + ///field-string + /// + [Description( "field-string" )] + FieldString, + /// + ///file-done + /// + [Description( "file-done" )] + FileDone, + /// + ///ellipsis + /// + [Description( "ellipsis" )] + Ellipsis, + /// + ///file-excel + /// + [Description( "file-excel" )] + FileExcel, + /// + ///file-add + /// + [Description( "file-add" )] + FileAdd, + /// + ///audit + /// + [Description( "audit" )] + Audit, + /// + ///file-gif + /// + [Description( "file-gif" )] + FileGif, + /// + ///file + /// + [Description( "file" )] + File, + /// + ///file-exclamation + /// + [Description( "file-exclamation" )] + FileExclamation, + /// + ///field-number + /// + [Description( "field-number" )] + FieldNumber, + /// + ///file-search + /// + [Description( "file-search" )] + FileSearch, + /// + ///file-sync + /// + [Description( "file-sync" )] + FileSync, + /// + ///file-text + /// + [Description( "file-text" )] + FileText, + /// + ///file-image + /// + [Description( "file-image" )] + FileImage, + /// + ///file-unknown + /// + [Description( "file-unknown" )] + FileUnknown, + /// + ///file-pdf + /// + [Description( "file-pdf" )] + FilePdf, + /// + ///file-jpg + /// + [Description( "file-jpg" )] + FileJpg, + /// + ///file-markdown + /// + [Description( "file-markdown" )] + FileMarkdown, + /// + ///file-ppt + /// + [Description( "file-ppt" )] + FilePpt, + /// + ///filter + /// + [Description( "filter" )] + Filter, + /// + ///file-word + /// + [Description( "file-word" )] + FileWord, + /// + ///flag + /// + [Description( "flag" )] + Flag, + /// + ///dollar + /// + [Description( "dollar" )] + Dollar, + /// + ///file-protect + /// + [Description( "file-protect" )] + FileProtect, + /// + ///folder + /// + [Description( "folder" )] + Folder, + /// + ///folder-open + /// + [Description( "folder-open" )] + FolderOpen, + /// + ///fork + /// + [Description( "fork" )] + Fork, + /// + ///folder-add + /// + [Description( "folder-add" )] + FolderAdd, + /// + ///frown + /// + [Description( "frown" )] + Frown, + /// + ///function + /// + [Description( "function" )] + Function, + /// + ///gateway + /// + [Description( "gateway" )] + Gateway, + /// + ///gif + /// + [Description( "gif" )] + Gif, + /// + ///gift + /// + [Description( "gift" )] + Gift, + /// + ///funnel-plot + /// + [Description( "funnel-plot" )] + FunnelPlot, + /// + ///fund-view + /// + [Description( "fund-view" )] + FundView, + /// + ///folder-view + /// + [Description( "folder-view" )] + FolderView, + /// + ///fund-projection-screen + /// + [Description( "fund-projection-screen" )] + FundProjectionScreen, + /// + ///gold + /// + [Description( "gold" )] + Gold, + /// + ///global + /// + [Description( "global" )] + Global, + /// + ///group + /// + [Description( "group" )] + Group, + /// + ///heart + /// + [Description( "heart" )] + Heart, + /// + ///audio-muted + /// + [Description( "audio-muted" )] + AudioMuted, + /// + ///format-painter + /// + [Description( "format-painter" )] + FormatPainter, + /// + ///history + /// + [Description( "history" )] + History, + /// + ///hdd + /// + [Description( "hdd" )] + Hdd, + /// + ///home + /// + [Description( "home" )] + Home, + /// + ///holder + /// + [Description( "holder" )] + Holder, + /// + ///idcard + /// + [Description( "idcard" )] + Idcard, + /// + ///import + /// + [Description( "import" )] + Import, + /// + ///hourglass + /// + [Description( "hourglass" )] + Hourglass, + /// + ///insert-row-below + /// + [Description( "insert-row-below" )] + InsertRowBelow, + /// + ///insert-row-left + /// + [Description( "insert-row-left" )] + InsertRowLeft, + /// + ///insert-row-right + /// + [Description( "insert-row-right" )] + InsertRowRight, + /// + ///interaction + /// + [Description( "interaction" )] + Interaction, + /// + ///insurance + /// + [Description( "insurance" )] + Insurance, + /// + ///inbox + /// + [Description( "inbox" )] + Inbox, + /// + ///key + /// + [Description( "key" )] + Key, + /// + ///laptop + /// + [Description( "laptop" )] + Laptop, + /// + ///link + /// + [Description( "link" )] + Link, + /// + ///line + /// + [Description( "line" )] + Line, + /// + ///like + /// + [Description( "like" )] + Like, + /// + ///loading-3-quarters + /// + [Description( "loading-3-quarters" )] + Loading3Quarters, + /// + ///file-zip + /// + [Description( "file-zip" )] + FileZip, + /// + ///layout + /// + [Description( "layout" )] + Layout, + /// + ///insert-row-above + /// + [Description( "insert-row-above" )] + InsertRowAbove, + /// + ///mac-command + /// + [Description( "mac-command" )] + MacCommand, + /// + ///man + /// + [Description( "man" )] + Man, + /// + ///medicine-box + /// + [Description( "medicine-box" )] + MedicineBox, + /// + ///lock + /// + [Description( "lock" )] + Lock, + /// + ///mail + /// + [Description( "mail" )] + Mail, + /// + ///merge-cells + /// + [Description( "merge-cells" )] + MergeCells, + /// + ///message + /// + [Description( "message" )] + Message, + /// + ///meh + /// + [Description( "meh" )] + Meh, + /// + ///money-collect + /// + [Description( "money-collect" )] + MoneyCollect, + /// + ///mobile + /// + [Description( "mobile" )] + Mobile, + /// + ///node-expand + /// + [Description( "node-expand" )] + NodeExpand, + /// + ///menu + /// + [Description( "menu" )] + Menu, + /// + ///node-index + /// + [Description( "node-index" )] + NodeIndex, + /// + ///node-collapse + /// + [Description( "node-collapse" )] + NodeCollapse, + /// + ///notification + /// + [Description( "notification" )] + Notification, + /// + ///paper-clip + /// + [Description( "paper-clip" )] + PaperClip, + /// + ///partition + /// + [Description( "partition" )] + Partition, + /// + ///loading + /// + [Description( "loading" )] + Loading, + /// + ///one-to-one + /// + [Description( "one-to-one" )] + OneToOne, + /// + ///pay-circle + /// + [Description( "pay-circle" )] + PayCircle, + /// + ///number + /// + [Description( "number" )] + Number, + /// + ///more + /// + [Description( "more" )] + More, + /// + ///picture + /// + [Description( "picture" )] + Picture, + /// + ///monitor + /// + [Description( "monitor" )] + Monitor, + /// + ///percentage + /// + [Description( "percentage" )] + Percentage, + /// + ///phone + /// + [Description( "phone" )] + Phone, + /// + ///play-square + /// + [Description( "play-square" )] + PlaySquare, + /// + ///pound-circle + /// + [Description( "pound-circle" )] + PoundCircle, + /// + ///pound + /// + [Description( "pound" )] + Pound, + /// + ///profile + /// + [Description( "profile" )] + Profile, + /// + ///property-safety + /// + [Description( "property-safety" )] + PropertySafety, + /// + ///printer + /// + [Description( "printer" )] + Printer, + /// + ///pushpin + /// + [Description( "pushpin" )] + Pushpin, + /// + ///poweroff + /// + [Description( "poweroff" )] + Poweroff, + /// + ///qrcode + /// + [Description( "qrcode" )] + Qrcode, + /// + ///pull-request + /// + [Description( "pull-request" )] + PullRequest, + /// + ///project + /// + [Description( "project" )] + Project, + /// + ///reconciliation + /// + [Description( "reconciliation" )] + Reconciliation, + /// + ///reload + /// + [Description( "reload" )] + Reload, + /// + ///rest + /// + [Description( "rest" )] + Rest, + /// + ///fire + /// + [Description( "fire" )] + Fire, + /// + ///read + /// + [Description( "read" )] + Read, + /// + ///red-envelope + /// + [Description( "red-envelope" )] + RedEnvelope, + /// + ///robot + /// + [Description( "robot" )] + Robot, + /// + ///rotate-right + /// + [Description( "rotate-right" )] + RotateRight, + /// + ///safety-certificate + /// + [Description( "safety-certificate" )] + SafetyCertificate, + /// + ///safety + /// + [Description( "safety" )] + Safety, + /// + ///rocket + /// + [Description( "rocket" )] + Rocket, + /// + ///rotate-left + /// + [Description( "rotate-left" )] + RotateLeft, + /// + ///select + /// + [Description( "select" )] + Select, + /// + ///save + /// + [Description( "save" )] + Save, + /// + ///schedule + /// + [Description( "schedule" )] + Schedule, + /// + ///security-scan + /// + [Description( "security-scan" )] + SecurityScan, + /// + ///search + /// + [Description( "search" )] + Search, + /// + ///shopping-cart + /// + [Description( "shopping-cart" )] + ShoppingCart, + /// + ///send + /// + [Description( "send" )] + Send, + /// + ///scan + /// + [Description( "scan" )] + Scan, + /// + ///shopping + /// + [Description( "shopping" )] + Shopping, + /// + ///shake + /// + [Description( "shake" )] + Shake, + /// + ///share-alt + /// + [Description( "share-alt" )] + ShareAlt, + /// + ///skin + /// + [Description( "skin" )] + Skin, + /// + ///sisternode + /// + [Description( "sisternode" )] + Sisternode, + /// + ///setting + /// + [Description( "setting" )] + Setting, + /// + ///shop + /// + [Description( "shop" )] + Shop, + /// + ///solution + /// + [Description( "solution" )] + Solution, + /// + ///smile + /// + [Description( "smile" )] + Smile, + /// + ///sound + /// + [Description( "sound" )] + Sound, + /// + ///star + /// + [Description( "star" )] + Star, + /// + ///split-cells + /// + [Description( "split-cells" )] + SplitCells, + /// + ///tablet + /// + [Description( "tablet" )] + Tablet, + /// + ///switcher + /// + [Description( "switcher" )] + Switcher, + /// + ///tag + /// + [Description( "tag" )] + Tag, + /// + ///tags + /// + [Description( "tags" )] + Tags, + /// + ///subnode + /// + [Description( "subnode" )] + Subnode, + /// + ///sync + /// + [Description( "sync" )] + Sync, + /// + ///team + /// + [Description( "team" )] + Team, + /// + ///table + /// + [Description( "table" )] + Table, + /// + ///thunderbolt + /// + [Description( "thunderbolt" )] + Thunderbolt, + /// + ///trademark-circle + /// + [Description( "trademark-circle" )] + TrademarkCircle, + /// + ///trademark + /// + [Description( "trademark" )] + Trademark, + /// + ///transaction + /// + [Description( "transaction" )] + Transaction, + /// + ///trophy + /// + [Description( "trophy" )] + Trophy, + /// + ///tool + /// + [Description( "tool" )] + Tool, + /// + ///ungroup + /// + [Description( "ungroup" )] + Ungroup, + /// + ///unlock + /// + [Description( "unlock" )] + Unlock, + /// + ///upload + /// + [Description( "upload" )] + Upload, + /// + ///user-delete + /// + [Description( "user-delete" )] + UserDelete, + /// + ///to-top + /// + [Description( "to-top" )] + ToTop, + /// + ///usb + /// + [Description( "usb" )] + Usb, + /// + ///user + /// + [Description( "user" )] + User, + /// + ///usergroup-add + /// + [Description( "usergroup-add" )] + UsergroupAdd, + /// + ///user-switch + /// + [Description( "user-switch" )] + UserSwitch, + /// + ///verified + /// + [Description( "verified" )] + Verified, + /// + ///user-add + /// + [Description( "user-add" )] + UserAdd, + /// + ///usergroup-delete + /// + [Description( "usergroup-delete" )] + UsergroupDelete, + /// + ///video-camera-add + /// + [Description( "video-camera-add" )] + VideoCameraAdd, + /// + ///wallet + /// + [Description( "wallet" )] + Wallet, + /// + ///video-camera + /// + [Description( "video-camera" )] + VideoCamera, + /// + ///woman + /// + [Description( "woman" )] + Woman, + /// + ///whats-app + /// + [Description( "whats-app" )] + WhatsApp, + /// + ///wifi + /// + [Description( "wifi" )] + Wifi, + /// + ///translation + /// + [Description( "translation" )] + Translation +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/AvatarShape.cs b/src/Util.Ui.NgZorro/Enums/AvatarShape.cs new file mode 100644 index 000000000..e2255a375 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/AvatarShape.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 头像形状 +/// +public enum AvatarShape { + /// + /// 圆形 + /// + [Description( "circle" )] + Circle, + /// + /// 方形 + /// + [Description( "square" )] + Square +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/AvatarSize.cs b/src/Util.Ui.NgZorro/Enums/AvatarSize.cs new file mode 100644 index 000000000..b6dadfdbb --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/AvatarSize.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 头像大小 +/// +public enum AvatarSize { + /// + /// 默认 + /// + [Description( "default" )] + Default, + /// + /// 小尺寸 + /// + [Description( "small" )] + Small, + /// + /// 大尺寸 + /// + [Description( "large" )] + Large +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/BadgeSize.cs b/src/Util.Ui.NgZorro/Enums/BadgeSize.cs new file mode 100644 index 000000000..c3a0da5d8 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/BadgeSize.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 徽标尺寸 +/// +public enum BadgeSize { + /// + /// default,默认尺寸 + /// + [Description( "default" )] + Default, + /// + /// small,小尺寸 + /// + [Description( "small" )] + Small +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/BadgeStatus.cs b/src/Util.Ui.NgZorro/Enums/BadgeStatus.cs new file mode 100644 index 000000000..f765500ef --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/BadgeStatus.cs @@ -0,0 +1,32 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 徽标状态 +/// +public enum BadgeStatus { + /// + /// 默认 + /// + [Description( "default" )] + Default, + /// + /// 成功 + /// + [Description( "success" )] + Success, + /// + /// 处理中 + /// + [Description( "processing" )] + Processing, + /// + /// 警告 + /// + [Description( "warning" )] + Warning, + /// + /// 错误 + /// + [Description( "error" )] + Error +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/ButtonShape.cs b/src/Util.Ui.NgZorro/Enums/ButtonShape.cs new file mode 100644 index 000000000..537abfbea --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/ButtonShape.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 按钮形状 +/// +public enum ButtonShape { + /// + /// 默认 + /// + [Description( "" )] + Default, + /// + /// 圆形 + /// + [Description( "circle" )] + Circle, + /// + /// 圆角 + /// + [Description( "round" )] + Round +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/ButtonSize.cs b/src/Util.Ui.NgZorro/Enums/ButtonSize.cs new file mode 100644 index 000000000..1159a9c85 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/ButtonSize.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 按钮大小 +/// +public enum ButtonSize { + /// + /// 默认 + /// + [Description( "default" )] + Default, + /// + /// 小尺寸 + /// + [Description( "small" )] + Small, + /// + /// 大尺寸 + /// + [Description( "large" )] + Large +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/ButtonType.cs b/src/Util.Ui.NgZorro/Enums/ButtonType.cs new file mode 100644 index 000000000..317c81394 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/ButtonType.cs @@ -0,0 +1,32 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 按钮类型 +/// +public enum ButtonType { + /// + /// 默认按钮 + /// + [Description( "default" )] + Default, + /// + /// 主按钮,深蓝色,在同一个操作区域最多出现一次 + /// + [Description( "primary" )] + Primary, + /// + /// 虚线按钮 + /// + [Description( "dashed" )] + Dashed, + /// + /// 文本按钮 + /// + [Description( "text" )] + Text, + /// + /// 链接按钮 + /// + [Description( "link" )] + Link +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/CalendarMode.cs b/src/Util.Ui.NgZorro/Enums/CalendarMode.cs new file mode 100644 index 000000000..275b74112 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/CalendarMode.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 日历显示模式 +/// +public enum CalendarMode { + /// + /// 月 + /// + [Description( "month" )] + Month, + /// + /// 年 + /// + [Description( "year" )] + Year +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/CardSize.cs b/src/Util.Ui.NgZorro/Enums/CardSize.cs new file mode 100644 index 000000000..597de8331 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/CardSize.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 卡片大小 +/// +public enum CardSize { + /// + /// 默认尺寸 + /// + [Description( "default" )] + Default, + /// + /// 小尺寸 + /// + [Description( "small" )] + Small +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/CardType.cs b/src/Util.Ui.NgZorro/Enums/CardType.cs new file mode 100644 index 000000000..2e2ac537b --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/CardType.cs @@ -0,0 +1,15 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 卡片类型 +/// +public enum CardType { + /// + /// 默认 + /// + Default, + /// + /// 内部卡片 + /// + Inner +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/CarouselDotPosition.cs b/src/Util.Ui.NgZorro/Enums/CarouselDotPosition.cs new file mode 100644 index 000000000..060655acc --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/CarouselDotPosition.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 走马灯指示点位置 +/// +public enum CarouselDotPosition { + /// + /// 上方 + /// + [Description( "top" )] + Top, + /// + /// 下方 + /// + [Description( "bottom" )] + Bottom, + /// + /// 左方 + /// + [Description( "left" )] + Left, + /// + /// 右方 + /// + [Description( "right" )] + Right +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/CarouselEffect.cs b/src/Util.Ui.NgZorro/Enums/CarouselEffect.cs new file mode 100644 index 000000000..24a98e568 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/CarouselEffect.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 走马灯动画效果 +/// +public enum CarouselEffect { + /// + /// X轴滚动 + /// + [Description( "scrollx" )] + ScrollX, + /// + /// 渐显 + /// + [Description( "fade" )] + Fade +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/CascaderExpandTrigger.cs b/src/Util.Ui.NgZorro/Enums/CascaderExpandTrigger.cs new file mode 100644 index 000000000..d2aa03984 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/CascaderExpandTrigger.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 级联选择展开方式 +/// +public enum CascaderExpandTrigger { + /// + /// 点击展开 + /// + [Description( "click" )] + Click, + /// + /// 鼠标悬停展开 + /// + [Description( "hover" )] + Hover +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/CascaderTriggerAction.cs b/src/Util.Ui.NgZorro/Enums/CascaderTriggerAction.cs new file mode 100644 index 000000000..f0bc9f013 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/CascaderTriggerAction.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 级联选择触发操作 +/// +public enum CascaderTriggerAction { + /// + /// 点击触发 + /// + [Description( "click" )] + Click, + /// + /// 移入触发 + /// + [Description( "hover" )] + Hover +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/CollapseIconPosition.cs b/src/Util.Ui.NgZorro/Enums/CollapseIconPosition.cs new file mode 100644 index 000000000..a7842f9b1 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/CollapseIconPosition.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 折叠图标位置 +/// +public enum CollapseIconPosition { + /// + /// 左方 + /// + [Description( "left" )] + Left, + /// + /// 右方 + /// + [Description( "right" )] + Right +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/ColorPickerFormat.cs b/src/Util.Ui.NgZorro/Enums/ColorPickerFormat.cs new file mode 100644 index 000000000..42155afb6 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/ColorPickerFormat.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 颜色选择器格式 +/// +public enum ColorPickerFormat { + /// + /// rgb + /// + [Description( "rgb" )] + Rgb, + /// + /// hex + /// + [Description( "hex" )] + Hex, + /// + /// hsb + /// + [Description( "hsb" )] + Hsb +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/ColorPickerTrigger.cs b/src/Util.Ui.NgZorro/Enums/ColorPickerTrigger.cs new file mode 100644 index 000000000..ad45141d8 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/ColorPickerTrigger.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 颜色选择器触发行为 +/// +public enum ColorPickerTrigger { + /// + /// 点击触发 + /// + [Description( "click" )] + Click, + /// + /// 移入触发 + /// + [Description( "hover" )] + Hover +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/CursorType.cs b/src/Util.Ui.NgZorro/Enums/CursorType.cs new file mode 100644 index 000000000..82ae742c1 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/CursorType.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 光标类型 +/// +public enum CursorType { + /// + /// window + /// + [Description( "window" )] + Window, + /// + /// grid + /// + [Description( "grid" )] + Grid +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/DataType.cs b/src/Util.Ui.NgZorro/Enums/DataType.cs new file mode 100644 index 000000000..26e1ce317 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/DataType.cs @@ -0,0 +1,23 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 数据类型 +/// +public enum DataType { + /// + /// 布尔 + /// + Bool, + /// + /// 日期 + /// + Date, + /// + /// 数值 + /// + Number, + /// + /// 枚举 + /// + Enum +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/DatePickerMode.cs b/src/Util.Ui.NgZorro/Enums/DatePickerMode.cs new file mode 100644 index 000000000..bf743b808 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/DatePickerMode.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 日期选择器模式 +/// +public enum DatePickerMode { + /// + /// 选择日期 + /// + [Description( "date" )] + Date, + /// + /// 选择一周 + /// + [Description( "week" )] + Week, + /// + /// 选择年和月份 + /// + [Description( "month" )] + Month, + /// + /// 选择年份 + /// + [Description( "year" )] + Year +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/DatePickerPlacement.cs b/src/Util.Ui.NgZorro/Enums/DatePickerPlacement.cs new file mode 100644 index 000000000..a2b1cd5ca --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/DatePickerPlacement.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 日期选择框弹出位置 +/// +public enum DatePickerPlacement { + /// + /// 左下方 + /// + [Description( "bottomLeft" )] + BottomLeft, + /// + /// 右下方 + /// + [Description( "bottomRight" )] + BottomRight, + /// + /// 左上方 + /// + [Description( "topLeft" )] + TopLeft, + /// + /// 右上方 + /// + [Description( "topRight" )] + TopRight +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/DescriptionDataType.cs b/src/Util.Ui.NgZorro/Enums/DescriptionDataType.cs new file mode 100644 index 000000000..233f26505 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/DescriptionDataType.cs @@ -0,0 +1,19 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 描述列表数据类型 +/// +public enum DescriptionDataType { + /// + /// 布尔 + /// + Bool, + /// + /// 日期 + /// + Date, + /// + /// 数值 + /// + Number +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/DescriptionLayout.cs b/src/Util.Ui.NgZorro/Enums/DescriptionLayout.cs new file mode 100644 index 000000000..ab17ef1b4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/DescriptionLayout.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 描述列表布局方式 +/// +public enum DescriptionLayout { + /// + /// horizontal, 水平排列 + /// + [Description( "horizontal" )] + Horizontal, + /// + /// vertical, 垂直排列 + /// + [Description( "vertical" )] + Vertical +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/DescriptionSize.cs b/src/Util.Ui.NgZorro/Enums/DescriptionSize.cs new file mode 100644 index 000000000..23395d2fc --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/DescriptionSize.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 描述列表大小 +/// +public enum DescriptionSize { + /// + /// 默认 + /// + [Description( "default" )] + Default, + /// + /// 中 + /// + [Description( "middle" )] + Middle, + /// + /// 小 + /// + [Description( "small" )] + Small +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/Direction.cs b/src/Util.Ui.NgZorro/Enums/Direction.cs new file mode 100644 index 000000000..1fbaef1cd --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/Direction.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 方向 +/// +public enum Direction { + /// + /// 从左到右 + /// + [Description( "ltr" )] + Ltr, + /// + /// 从右到左 + /// + [Description( "rtl" )] + Rtl +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/DividerOrientation.cs b/src/Util.Ui.NgZorro/Enums/DividerOrientation.cs new file mode 100644 index 000000000..f2d2d5c46 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/DividerOrientation.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 分隔线中间文字方向 +/// +public enum DividerOrientation { + /// + /// 中 + /// + [Description( "center" )] + Center, + /// + /// 左 + /// + [Description( "left" )] + Left, + /// + /// 右 + /// + [Description( "right" )] + Right +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/DividerType.cs b/src/Util.Ui.NgZorro/Enums/DividerType.cs new file mode 100644 index 000000000..39d393ee2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/DividerType.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 分隔线类型 +/// +public enum DividerType { + /// + /// 水平 + /// + [Description( "horizontal" )] + Horizontal, + /// + /// 垂直 + /// + [Description( "vertical" )] + Vertical +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/DrawerPlacement.cs b/src/Util.Ui.NgZorro/Enums/DrawerPlacement.cs new file mode 100644 index 000000000..46a56d27d --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/DrawerPlacement.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 抽屉方向 +/// +public enum DrawerPlacement { + /// + /// 上方 + /// + [Description( "top" )] + Top, + /// + /// 下方 + /// + [Description( "bottom" )] + Bottom, + /// + /// 左方 + /// + [Description( "left" )] + Left, + /// + /// 右方 + /// + [Description( "right" )] + Right +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/DrawerSize.cs b/src/Util.Ui.NgZorro/Enums/DrawerSize.cs new file mode 100644 index 000000000..d3c3b453d --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/DrawerSize.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 抽屉尺寸 +/// +public enum DrawerSize { + /// + /// 默认 + /// + [Description( "default" )] + Default, + /// + /// 大尺寸 + /// + [Description( "large" )] + Large +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/DropdownMenuPlacement.cs b/src/Util.Ui.NgZorro/Enums/DropdownMenuPlacement.cs new file mode 100644 index 000000000..3c15b713b --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/DropdownMenuPlacement.cs @@ -0,0 +1,37 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 下拉菜单弹出位置 +/// +public enum DropdownMenuPlacement { + /// + /// 左下方 + /// + [Description( "bottomLeft" )] + BottomLeft, + /// + /// 中下方 + /// + [Description( "bottomCenter" )] + BottomCenter, + /// + /// 右下方 + /// + [Description( "bottomRight" )] + BottomRight, + /// + /// 左上方 + /// + [Description( "topLeft" )] + TopLeft, + /// + /// 中上方 + /// + [Description( "topCenter" )] + TopCenter, + /// + /// 右上方 + /// + [Description( "topRight" )] + TopRight +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/DropdownMenuTrigger.cs b/src/Util.Ui.NgZorro/Enums/DropdownMenuTrigger.cs new file mode 100644 index 000000000..cd592330c --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/DropdownMenuTrigger.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 下拉菜单触发方式 +/// +public enum DropdownMenuTrigger { + /// + /// 移入触发 + /// + [Description( "hover" )] + Hover, + /// + /// 点击触发 + /// + [Description( "click" )] + Click +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/FlexAlign.cs b/src/Util.Ui.NgZorro/Enums/FlexAlign.cs new file mode 100644 index 000000000..281c313a9 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/FlexAlign.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// Flex垂直对齐方式 +/// +public enum FlexAlign { + /// + /// flex-start + /// + [Description( "flex-start" )] + FlexStart, + /// + /// center + /// + [Description( "center" )] + Center, + /// + /// flex-end + /// + [Description( "flex-end" )] + FlexEnd +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/FlexGap.cs b/src/Util.Ui.NgZorro/Enums/FlexGap.cs new file mode 100644 index 000000000..edd5710f9 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/FlexGap.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// Flex间距 +/// +public enum FlexGap { + /// + /// small + /// + [Description( "small" )] + Small, + /// + /// middle + /// + [Description( "middle" )] + Middle, + /// + /// large + /// + [Description( "large" )] + Large +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/FlexJustify.cs b/src/Util.Ui.NgZorro/Enums/FlexJustify.cs new file mode 100644 index 000000000..d2cccf307 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/FlexJustify.cs @@ -0,0 +1,37 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// Flex对齐方式 +/// +public enum FlexJustify { + /// + /// flex-start + /// + [Description( "flex-start" )] + FlexStart, + /// + /// center + /// + [Description( "center" )] + Center, + /// + /// flex-end + /// + [Description( "flex-end" )] + FlexEnd, + /// + /// space-between + /// + [Description( "space-between" )] + SpaceBetween, + /// + /// space-around + /// + [Description( "space-around" )] + SpaceAround, + /// + /// space-evenly + /// + [Description( "space-evenly" )] + SpaceEvenly +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/FlexWrap.cs b/src/Util.Ui.NgZorro/Enums/FlexWrap.cs new file mode 100644 index 000000000..c49848510 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/FlexWrap.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// Flex自动换行 +/// +public enum FlexWrap { + /// + /// 自动换行 + /// + [Description( "wrap" )] + Wrap, + /// + /// 反向自动换行 + /// + [Description( "wrap-reverse" )] + WrapReverse, + /// + /// 不自动换行 + /// + [Description( "nowrap" )] + Nowrap +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/FormControlStatus.cs b/src/Util.Ui.NgZorro/Enums/FormControlStatus.cs new file mode 100644 index 000000000..ccf2ce0ad --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/FormControlStatus.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 表单控件状态 +/// +public enum FormControlStatus { + /// + /// error + /// + [Description( "error" )] + Error, + /// + /// warning + /// + [Description( "warning" )] + Warning +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/FormLayout.cs b/src/Util.Ui.NgZorro/Enums/FormLayout.cs new file mode 100644 index 000000000..472d1ac82 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/FormLayout.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 表单布局 +/// +public enum FormLayout { + /// + /// 标签和表单控件水平排列 + /// + [Description( "horizontal" )] + Horizontal, + /// + /// 标签和表单控件上下垂直排列 + /// + [Description( "vertical" )] + Vertical, + /// + /// 表单项水平行内排列 + /// + [Description( "inline" )] + Inline +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/GridSize.cs b/src/Util.Ui.NgZorro/Enums/GridSize.cs new file mode 100644 index 000000000..259766d5a --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/GridSize.cs @@ -0,0 +1,37 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 栅格大小 +/// +public enum GridSize { + /// + /// 超窄尺寸 + /// + [Description( "xs" )] + Xs, + /// + /// 窄尺寸 + /// + [Description( "sm" )] + Sm, + /// + /// 中尺寸 + /// + [Description( "md" )] + Md, + /// + /// 宽尺寸 + /// + [Description( "lg" )] + Lg, + /// + /// 超宽尺寸 + /// + [Description( "xl" )] + Xl, + /// + /// 极宽尺寸 + /// + [Description( "xxl" )] + Xxl +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/HashCodeMode.cs b/src/Util.Ui.NgZorro/Enums/HashCodeMode.cs new file mode 100644 index 000000000..fd839d876 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/HashCodeMode.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 哈希码展示模式 +/// +public enum HashCodeMode { + /// + /// 单行模式 + /// + [Description( "single" )] + Single, + /// + /// 双行模式 + /// + [Description( "double" )] + Double, + /// + /// 长条模式 + /// + [Description( "strip" )] + Strip, + /// + /// 矩形模式 + /// + [Description( "rect" )] + Rect +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/HashCodeType.cs b/src/Util.Ui.NgZorro/Enums/HashCodeType.cs new file mode 100644 index 000000000..f35611888 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/HashCodeType.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 哈希码样式 +/// +public enum HashCodeType { + /// + /// 默认样式 + /// + [Description( "defalut " )] + Defalut, + /// + /// 主样式 + /// + [Description( "primary" )] + Primary +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/IconTheme.cs b/src/Util.Ui.NgZorro/Enums/IconTheme.cs new file mode 100644 index 000000000..0cfdbbf91 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/IconTheme.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 图标主题风格 +/// +public enum IconTheme { + /// + /// 实心主题 + /// + [Description( "fill" )] + Fill, + /// + /// 描线主题 + /// + [Description( "outline" )] + Outline, + /// + /// 双色主题 + /// + [Description( "twotone" )] + Twotone +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/InputMode.cs b/src/Util.Ui.NgZorro/Enums/InputMode.cs new file mode 100644 index 000000000..de3dbea66 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/InputMode.cs @@ -0,0 +1,47 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 输入模式 +/// +public enum InputMode { + /// + /// 无虚拟键盘,在需要实现自己的键盘输入控件时很有用 + /// + [Description( "none" )] + None, + /// + /// 使用用户本地区域设置的标准文本输入键盘 + /// + [Description( "text" )] + Text, + /// + /// 小数输入键盘,包含数字和分隔符,通常是“ . ”或者“ , ”。 + /// + [Description( "decimal" )] + Decimal, + /// + /// 数字输入键盘,0到9的数字 + /// + [Description( "numeric" )] + Numeric, + /// + /// 电话输入键盘,包含0到9的数字、星号(*)和井号(#)键 + /// + [Description( "tel" )] + Tel, + /// + /// 为搜索输入优化的虚拟键盘,比如,返回键可能被重新标记为“搜索” + /// + [Description( "search" )] + Search, + /// + /// 为邮件地址输入优化的虚拟键盘,通常包含"@"符号和其他优化 + /// + [Description( "email" )] + Email, + /// + /// 为网址输入优化的虚拟键盘,比如,“/”键会更加明显、历史记录访问 + /// + [Description( "url" )] + Url, +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/InputNumberPrecisionMode.cs b/src/Util.Ui.NgZorro/Enums/InputNumberPrecisionMode.cs new file mode 100644 index 000000000..e8bbdff66 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/InputNumberPrecisionMode.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 数字输入精度模式 +/// +public enum InputNumberPrecisionMode { + /// + /// 多余的位数直接截断 + /// + [Description( "cut" )] + Cut, + /// + /// 四舍五入 + /// + [Description( "toFixed" )] + ToFixed +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/InputSize.cs b/src/Util.Ui.NgZorro/Enums/InputSize.cs new file mode 100644 index 000000000..95e73fbfe --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/InputSize.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 输入框尺寸 +/// +public enum InputSize { + /// + /// 默认尺寸 + /// + [Description( "default" )] + Default, + /// + /// 小尺寸 + /// + [Description( "small" )] + Small, + /// + /// 大尺寸 + /// + [Description( "large" )] + Large +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/InputType.cs b/src/Util.Ui.NgZorro/Enums/InputType.cs new file mode 100644 index 000000000..c582a1cf8 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/InputType.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 输入框类型 +/// +public enum InputType { + /// + /// 文本输入框 + /// + [Description( "text" )] + Text, + /// + /// 密码输入框 + /// + [Description( "password" )] + Password, + /// + /// 数字输入框 + /// + [Description( "number" )] + Number, + /// + /// 电子邮件输入框 + /// + [Description( "email" )] + Email +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/Justify.cs b/src/Util.Ui.NgZorro/Enums/Justify.cs new file mode 100644 index 000000000..94646619b --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/Justify.cs @@ -0,0 +1,37 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 水平排列方式 +/// +public enum Justify { + /// + /// start,居左排列 + /// + [Description( "start" )] + Start, + /// + /// center,居中排列 + /// + [Description( "center" )] + Center, + /// + /// end, 居右排列 + /// + [Description( "end" )] + End, + /// + /// space-around, 等宽排列,每个项两侧的间隔相等 + /// + [Description( "space-around" )] + SpaceAround, + /// + /// space-between, 分散排列,两端对齐,项之间的间隔都相等 + /// + [Description( "space-between" )] + SpaceBetween, + /// + /// space-evenly, 均匀排列 + /// + [Description( "space-evenly" )] + SpaceEvenly +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/LabelAlign.cs b/src/Util.Ui.NgZorro/Enums/LabelAlign.cs new file mode 100644 index 000000000..55ab0ec7c --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/LabelAlign.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 标签文本对齐方式 +/// +public enum LabelAlign { + /// + /// left,左侧显示 + /// + [Description( "left" )] + Left, + /// + /// right,右侧显示 + /// + [Description( "right" )] + Right +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/ListItemLayout.cs b/src/Util.Ui.NgZorro/Enums/ListItemLayout.cs new file mode 100644 index 000000000..f715bcedb --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/ListItemLayout.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 列表项布局方式 +/// +public enum ListItemLayout { + /// + /// horizontal, 水平排列 + /// + [Description( "horizontal" )] + Horizontal, + /// + /// vertical, 垂直排列 + /// + [Description( "vertical" )] + Vertical +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/ListSize.cs b/src/Util.Ui.NgZorro/Enums/ListSize.cs new file mode 100644 index 000000000..a62dd8a67 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/ListSize.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 列表尺寸 +/// +public enum ListSize { + /// + /// 默认 + /// + [Description( "default" )] + Default, + /// + /// 小尺寸 + /// + [Description( "small" )] + Small, + /// + /// 大尺寸 + /// + [Description( "large" )] + Large +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/MentionPlacement.cs b/src/Util.Ui.NgZorro/Enums/MentionPlacement.cs new file mode 100644 index 000000000..f103fff03 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/MentionPlacement.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 提及建议框位置 +/// +public enum MentionPlacement { + /// + /// 向上展开建议框 + /// + [Description( "top" )] + Top, + /// + /// 向下展开建议框 + /// + [Description( "bottom" )] + Bottom +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/MenuMode.cs b/src/Util.Ui.NgZorro/Enums/MenuMode.cs new file mode 100644 index 000000000..cc72d3402 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/MenuMode.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 菜单模式 +/// +public enum MenuMode { + /// + /// 垂直 + /// + [Description( "vertical" )] + Vertical, + /// + /// 水平 + /// + [Description( "horizontal" )] + Horizontal, + /// + /// 内嵌 + /// + [Description( "inline" )] + Inline +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/MenuTheme.cs b/src/Util.Ui.NgZorro/Enums/MenuTheme.cs new file mode 100644 index 000000000..65313a2e5 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/MenuTheme.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 菜单主题 +/// +public enum MenuTheme { + /// + /// 浅色 + /// + [Description( "light" )] + Light, + /// + /// 深色 + /// + [Description( "dark" )] + Dark +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/ModalAutofocus.cs b/src/Util.Ui.NgZorro/Enums/ModalAutofocus.cs new file mode 100644 index 000000000..9a96788c7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/ModalAutofocus.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 对话框自动聚焦 +/// +public enum ModalAutofocus { + /// + /// 聚集到确认按钮 + /// + [Description( "ok" )] + Ok, + /// + /// 聚集到取消按钮 + /// + [Description( "cancel" )] + Cancel, + /// + /// 自动聚焦 + /// + [Description( "auto" )] + Auto +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/PaginationSize.cs b/src/Util.Ui.NgZorro/Enums/PaginationSize.cs new file mode 100644 index 000000000..b9a3805c7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/PaginationSize.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 分页大小 +/// +public enum PaginationSize { + /// + /// 默认尺寸 + /// + [Description( "default" )] + Default, + /// + /// 小尺寸 + /// + [Description( "small" )] + Small +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/PopconfirmAutoFocus.cs b/src/Util.Ui.NgZorro/Enums/PopconfirmAutoFocus.cs new file mode 100644 index 000000000..8e934cc3f --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/PopconfirmAutoFocus.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 气泡确认框自动聚焦 +/// +public enum PopconfirmAutoFocus { + /// + /// ok, 确认按钮 + /// + [Description( "ok" )] + Ok, + /// + /// cancel,取消按钮 + /// + [Description( "cancel" )] + Cancel +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/PopconfirmPlacement.cs b/src/Util.Ui.NgZorro/Enums/PopconfirmPlacement.cs new file mode 100644 index 000000000..5ff16ccbd --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/PopconfirmPlacement.cs @@ -0,0 +1,67 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 气泡确认框位置 +/// +public enum PopconfirmPlacement { + /// + /// 上方 + /// + [Description( "top" )] + Top, + /// + /// 左方 + /// + [Description( "left" )] + Left, + /// + /// 右方 + /// + [Description( "right" )] + Right, + /// + /// 下方 + /// + [Description( "bottom" )] + Bottom, + /// + /// 左上方 + /// + [Description( "topLeft" )] + TopLeft, + /// + /// 右上方 + /// + [Description( "topRight" )] + TopRight, + /// + /// 左下方 + /// + [Description( "bottomLeft" )] + BottomLeft, + /// + /// 右下方 + /// + [Description( "bottomRight" )] + BottomRight, + /// + /// 左上方 + /// + [Description( "leftTop" )] + LeftTop, + /// + /// 左下方 + /// + [Description( "leftBottom" )] + LeftBottom, + /// + /// 右上方 + /// + [Description( "rightTop" )] + RightTop, + /// + /// 右下方 + /// + [Description( "rightBottom" )] + RightBottom +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/PopconfirmTrigger.cs b/src/Util.Ui.NgZorro/Enums/PopconfirmTrigger.cs new file mode 100644 index 000000000..0f7da0741 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/PopconfirmTrigger.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 气泡确认框触发行为 +/// +public enum PopconfirmTrigger { + /// + /// 点击触发 + /// + [Description( "click" )] + Click, + /// + /// 获得焦点触发 + /// + [Description( "focus" )] + Focus, + /// + /// 移入触发 + /// + [Description( "hover" )] + Hover +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/PopoverPlacement.cs b/src/Util.Ui.NgZorro/Enums/PopoverPlacement.cs new file mode 100644 index 000000000..7cf929362 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/PopoverPlacement.cs @@ -0,0 +1,67 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 气泡框弹出位置 +/// +public enum PopoverPlacement { + /// + /// 上方 + /// + [Description( "top" )] + Top, + /// + /// 左方 + /// + [Description( "left" )] + Left, + /// + /// 右方 + /// + [Description( "right" )] + Right, + /// + /// 下方 + /// + [Description( "bottom" )] + Bottom, + /// + /// 左上方 + /// + [Description( "topLeft" )] + TopLeft, + /// + /// 右上方 + /// + [Description( "topRight" )] + TopRight, + /// + /// 左下方 + /// + [Description( "bottomLeft" )] + BottomLeft, + /// + /// 右下方 + /// + [Description( "bottomRight" )] + BottomRight, + /// + /// 左上方 + /// + [Description( "leftTop" )] + LeftTop, + /// + /// 左下方 + /// + [Description( "leftBottom" )] + LeftBottom, + /// + /// 右上方 + /// + [Description( "rightTop" )] + RightTop, + /// + /// 右下方 + /// + [Description( "rightBottom" )] + RightBottom +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/PopoverTrigger.cs b/src/Util.Ui.NgZorro/Enums/PopoverTrigger.cs new file mode 100644 index 000000000..f6b6f6108 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/PopoverTrigger.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 气泡框触发行为 +/// +public enum PopoverTrigger { + /// + /// 点击触发 + /// + [Description( "click" )] + Click, + /// + /// 获得焦点触发 + /// + [Description( "focus" )] + Focus, + /// + /// 移入触发 + /// + [Description( "hover" )] + Hover +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/ProgressGapPosition.cs b/src/Util.Ui.NgZorro/Enums/ProgressGapPosition.cs new file mode 100644 index 000000000..667173563 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/ProgressGapPosition.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 进度条缺口位置 +/// +public enum ProgressGapPosition { + /// + /// 上方 + /// + [Description( "top" )] + Top, + /// + /// 左方 + /// + [Description( "left" )] + Left, + /// + /// 右方 + /// + [Description( "right" )] + Right, + /// + /// 下方 + /// + [Description( "bottom" )] + Bottom +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/ProgressSize.cs b/src/Util.Ui.NgZorro/Enums/ProgressSize.cs new file mode 100644 index 000000000..26c2c747d --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/ProgressSize.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 进度条尺寸 +/// +public enum ProgressSize { + /// + /// default, 默认 + /// + [Description( "default" )] + Default, + /// + /// small, 小尺寸 + /// + [Description( "small" )] + Small +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/ProgressStatus.cs b/src/Util.Ui.NgZorro/Enums/ProgressStatus.cs new file mode 100644 index 000000000..64e434c56 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/ProgressStatus.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 进度条状态 +/// +public enum ProgressStatus { + /// + /// 成功 + /// + [Description( "success" )] + Success, + /// + /// 异常 + /// + [Description( "exception" )] + Exception, + /// + /// 活动 + /// + [Description( "active" )] + Active, + /// + /// 常规 + /// + [Description( "normal" )] + Normal +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/ProgressStrokeLinecap.cs b/src/Util.Ui.NgZorro/Enums/ProgressStrokeLinecap.cs new file mode 100644 index 000000000..a0f60dda6 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/ProgressStrokeLinecap.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 进度条端点形状 +/// +public enum ProgressStrokeLinecap { + /// + /// 圆角边缘 + /// + [Description( "round" )] + Round, + /// + /// 方角边缘 + /// + [Description( "square" )] + Square +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/ProgressType.cs b/src/Util.Ui.NgZorro/Enums/ProgressType.cs new file mode 100644 index 000000000..468c6bcf5 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/ProgressType.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 进度条类型 +/// +public enum ProgressType { + /// + /// 进度条 + /// + [Description( "line" )] + Line, + /// + /// 进度圈 + /// + [Description( "circle" )] + Circle, + /// + /// 仪表盘 + /// + [Description( "dashboard" )] + Dashboard +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/QrCodeCorrectionLevel.cs b/src/Util.Ui.NgZorro/Enums/QrCodeCorrectionLevel.cs new file mode 100644 index 000000000..8c58bfcd2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/QrCodeCorrectionLevel.cs @@ -0,0 +1,23 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 二维码容错级别 +/// +public enum QrCodeCorrectionLevel { + /// + /// 可以纠正最大7%的错误 + /// + L, + /// + /// 可以纠正最大15%的错误 + /// + M, + /// + /// 可以纠正最大25%的错误 + /// + Q, + /// + /// 可以纠正最大30%的错误 + /// + H +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/QrCodeStatus.cs b/src/Util.Ui.NgZorro/Enums/QrCodeStatus.cs new file mode 100644 index 000000000..302c80e6c --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/QrCodeStatus.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 二维码状态 +/// +public enum QrCodeStatus { + /// + /// active, 正常 + /// + [Description( "active" )] + Active, + /// + /// expired,已过期 + /// + [Description( "expired" )] + Expired, + /// + /// loading, 加载中 + /// + [Description( "loading" )] + Loading +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/RadioStyle.cs b/src/Util.Ui.NgZorro/Enums/RadioStyle.cs new file mode 100644 index 000000000..6e6cc9891 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/RadioStyle.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 单选框风格 +/// +public enum RadioStyle { + /// + /// 描边风格 + /// + [Description( "outline" )] + Outline, + /// + /// 填色风格 + /// + [Description( "solid" )] + Solid +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/ResizeDirection.cs b/src/Util.Ui.NgZorro/Enums/ResizeDirection.cs new file mode 100644 index 000000000..c4fb13510 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/ResizeDirection.cs @@ -0,0 +1,47 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 调整尺寸手柄方向 +/// +public enum ResizeDirection { + /// + /// 上方 + /// + [Description( "top" )] + Top, + /// + /// 右方 + /// + [Description( "right" )] + Right, + /// + /// 下方 + /// + [Description( "bottom" )] + Bottom, + /// + /// 左方 + /// + [Description( "left" )] + Left, + /// + /// 右上方 + /// + [Description( "topRight" )] + TopRight, + /// + /// 右下方 + /// + [Description( "bottomRight" )] + BottomRight, + /// + /// 左下方 + /// + [Description( "bottomLeft" )] + BottomLeft, + /// + /// 左上方 + /// + [Description( "topLeft" )] + TopLeft +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/ResultStatus.cs b/src/Util.Ui.NgZorro/Enums/ResultStatus.cs new file mode 100644 index 000000000..27586ed07 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/ResultStatus.cs @@ -0,0 +1,42 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 警告提示类型 +/// +public enum ResultStatus { + /// + /// 成功 + /// + [Description( "success" )] + Success, + /// + /// 信息 + /// + [Description( "info" )] + Info, + /// + /// 警告 + /// + [Description( "warning" )] + Warning, + /// + /// 错误 + /// + [Description( "error" )] + Error, + /// + /// 403 + /// + [Description( "403" )] + StatusCode403, + /// + /// 404 + /// + [Description( "404" )] + StatusCode404, + /// + /// 500 + /// + [Description( "500" )] + StatusCode500 +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/RibbonPlacement.cs b/src/Util.Ui.NgZorro/Enums/RibbonPlacement.cs new file mode 100644 index 000000000..c3796f7ff --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/RibbonPlacement.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 缎带徽标位置 +/// +public enum RibbonPlacement { + /// + /// 起始位置 + /// + [Description( "start" )] + Start, + /// + /// 结束位置 + /// + [Description( "end" )] + End +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/SelectMode.cs b/src/Util.Ui.NgZorro/Enums/SelectMode.cs new file mode 100644 index 000000000..6b6d00286 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/SelectMode.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 选择模式 +/// +public enum SelectMode { + /// + /// 默认 + /// + [Description( "default" )] + Default, + /// + /// 多选 + /// + [Description( "multiple" )] + Multiple, + /// + /// 标签 + /// + [Description( "tags" )] + Tags +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/SiderTheme.cs b/src/Util.Ui.NgZorro/Enums/SiderTheme.cs new file mode 100644 index 000000000..1f934b5ef --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/SiderTheme.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 侧边栏主题颜色 +/// +public enum SiderTheme { + /// + /// 浅色 + /// + [Description( "light" )] + Light, + /// + /// 深色 + /// + [Description( "dark" )] + Dark +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/SkeletonElementShape.cs b/src/Util.Ui.NgZorro/Enums/SkeletonElementShape.cs new file mode 100644 index 000000000..ab93462eb --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/SkeletonElementShape.cs @@ -0,0 +1,43 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 骨架屏按钮形状 +/// +public enum SkeletonButtonShape { + /// + /// default, 默认 + /// + [Description( "default" )] + Default, + /// + /// circle, 圆形 + /// + [Description( "circle" )] + Circle, + /// + /// square, 方形 + /// + [Description( "square" )] + Square, + /// + /// round, 圆角 + /// + [Description( "round" )] + Round +} + +/// +/// 骨架屏头像形状 +/// +public enum SkeletonAvatarShape { + /// + /// circle, 圆形 + /// + [Description( "circle" )] + Circle, + /// + /// square,方形 + /// + [Description( "square" )] + Square +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/SkeletonElementSize.cs b/src/Util.Ui.NgZorro/Enums/SkeletonElementSize.cs new file mode 100644 index 000000000..2acb4ff1e --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/SkeletonElementSize.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 骨架屏元素大小 +/// +public enum SkeletonElementSize { + /// + /// 默认 + /// + [Description( "default" )] + Default, + /// + /// 小尺寸 + /// + [Description( "small" )] + Small, + /// + /// 大尺寸 + /// + [Description( "large" )] + Large +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/SkeletonElementType.cs b/src/Util.Ui.NgZorro/Enums/SkeletonElementType.cs new file mode 100644 index 000000000..0e52e0850 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/SkeletonElementType.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 骨架屏元素类型 +/// +public enum SkeletonElementType { + /// + /// 按钮 + /// + [Description( "button" )] + Button, + /// + /// 头像 + /// + [Description( "avatar" )] + Avatar, + /// + /// 输入框 + /// + [Description( "input" )] + Input, + /// + /// 图片 + /// + [Description( "image" )] + Image +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/SliderTooltipVisible.cs b/src/Util.Ui.NgZorro/Enums/SliderTooltipVisible.cs new file mode 100644 index 000000000..4a26dcebe --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/SliderTooltipVisible.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 滑动输入条提示可见性 +/// +public enum SliderTooltipVisible { + /// + /// 默认 + /// + [Description( "default" )] + Default, + /// + /// 总是可见 + /// + [Description( "always" )] + Always, + /// + /// 不可见 + /// + [Description( "never" )] + Never +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/SpaceAlign.cs b/src/Util.Ui.NgZorro/Enums/SpaceAlign.cs new file mode 100644 index 000000000..77113ef9c --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/SpaceAlign.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 间距对齐方式 +/// +public enum SpaceAlign { + /// + /// 顶部对齐 + /// + [Description( "start" )] + Start, + /// + /// 底部对齐 + /// + [Description( "end" )] + End, + /// + /// 居中对齐 + /// + [Description( "center" )] + Center, + /// + /// 居中对齐 + /// + [Description( "baseline" )] + Baseline +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/SpaceDirection.cs b/src/Util.Ui.NgZorro/Enums/SpaceDirection.cs new file mode 100644 index 000000000..678ab19eb --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/SpaceDirection.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 间距方向 +/// +public enum SpaceDirection { + /// + /// 水平方向 + /// + [Description( "horizontal" )] + Horizontal, + /// + /// 垂直方向 + /// + [Description( "vertical" )] + Vertical +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/SpaceSize.cs b/src/Util.Ui.NgZorro/Enums/SpaceSize.cs new file mode 100644 index 000000000..721652bc4 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/SpaceSize.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 间距大小 +/// +public enum SpaceSize { + /// + /// 小 + /// + [Description( "small" )] + Small, + /// + /// 中 + /// + [Description( "middle" )] + Middle, + /// + /// 大 + /// + [Description( "large" )] + Large +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/SpanChildTag.cs b/src/Util.Ui.NgZorro/Enums/SpanChildTag.cs new file mode 100644 index 000000000..16ce0d525 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/SpanChildTag.cs @@ -0,0 +1,37 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// Span子标签 +/// +public enum SpanChildTag { + /// + /// <mark></mark> + /// + [Description( "mark" )] + Mark, + /// + /// <strong></strong> + /// + [Description( "strong" )] + Strong, + /// + /// <code></code> + /// + [Description( "code" )] + Code, + /// + /// <kbd></kbd> + /// + [Description( "kbd" )] + Keyboard, + /// + /// <u></u> ,下划线 + /// + [Description( "u" )] + Underline, + /// + /// <del></del> ,删除线 + /// + [Description( "del" )] + Delete +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/SpinSize.cs b/src/Util.Ui.NgZorro/Enums/SpinSize.cs new file mode 100644 index 000000000..204a56f25 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/SpinSize.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 加载中组件大小 +/// +public enum SpinSize { + /// + /// 默认 + /// + [Description( "default" )] + Default, + /// + /// 小尺寸 + /// + [Description( "small" )] + Small, + /// + /// 大尺寸 + /// + [Description( "large" )] + Large +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/StepStatus.cs b/src/Util.Ui.NgZorro/Enums/StepStatus.cs new file mode 100644 index 000000000..016c0770d --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/StepStatus.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 步骤状态 +/// +public enum StepStatus { + /// + /// 等待 + /// + [Description( "wait" )] + Wait, + /// + /// 处理 + /// + [Description( "process" )] + Process, + /// + /// 完成 + /// + [Description( "finish" )] + Finish, + /// + /// 错误 + /// + [Description( "error" )] + Error +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/StepsDirection.cs b/src/Util.Ui.NgZorro/Enums/StepsDirection.cs new file mode 100644 index 000000000..323f2228d --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/StepsDirection.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 步骤条方向 +/// +public enum StepsDirection { + /// + /// 水平方向 + /// + [Description( "horizontal" )] + Horizontal, + /// + /// 垂直方向 + /// + [Description( "vertical" )] + Vertical +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/StepsLabelPlacement.cs b/src/Util.Ui.NgZorro/Enums/StepsLabelPlacement.cs new file mode 100644 index 000000000..55da18cf7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/StepsLabelPlacement.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 步骤条标签位置 +/// +public enum StepsLabelPlacement { + /// + /// 水平放图标右侧 + /// + [Description( "horizontal" )] + Horizontal, + /// + /// 垂直放图标下方 + /// + [Description( "vertical" )] + Vertical +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/StepsSize.cs b/src/Util.Ui.NgZorro/Enums/StepsSize.cs new file mode 100644 index 000000000..9e22df306 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/StepsSize.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 步骤条大小 +/// +public enum StepsSize { + /// + /// 默认尺寸 + /// + [Description( "default" )] + Default, + /// + /// 小尺寸 + /// + [Description( "small" )] + Small +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/StepsType.cs b/src/Util.Ui.NgZorro/Enums/StepsType.cs new file mode 100644 index 000000000..ee134067c --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/StepsType.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 步骤条类型 +/// +public enum StepsType { + /// + /// 默认类型 + /// + [Description( "default" )] + Default, + /// + /// 导航类型 + /// + [Description( "navigation" )] + Navigation +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/SwitchSize.cs b/src/Util.Ui.NgZorro/Enums/SwitchSize.cs new file mode 100644 index 000000000..f9fa2b0c0 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/SwitchSize.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 开关大小 +/// +public enum SwitchSize { + /// + /// 默认尺寸 + /// + [Description( "default" )] + Default, + /// + /// 小尺寸 + /// + [Description( "small" )] + Small +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/TabPosition.cs b/src/Util.Ui.NgZorro/Enums/TabPosition.cs new file mode 100644 index 000000000..b183d25ea --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/TabPosition.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 标签位置 +/// +public enum TabPosition { + /// + /// 上方 + /// + [Description( "top" )] + Top, + /// + /// 左方 + /// + [Description( "left" )] + Left, + /// + /// 右方 + /// + [Description( "right" )] + Right, + /// + /// 下方 + /// + [Description( "bottom" )] + Bottom +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/TabSize.cs b/src/Util.Ui.NgZorro/Enums/TabSize.cs new file mode 100644 index 000000000..b6dcfa3a2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/TabSize.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 标签大小 +/// +public enum TabSize { + /// + /// 默认 + /// + [Description( "default" )] + Default, + /// + /// 小尺寸 + /// + [Description( "small" )] + Small, + /// + /// 大尺寸 + /// + [Description( "large" )] + Large +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/TabType.cs b/src/Util.Ui.NgZorro/Enums/TabType.cs new file mode 100644 index 000000000..2e9049e61 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/TabType.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 标签类型 +/// +public enum TabType { + /// + /// 常规标签 + /// + [Description( "line" )] + Line, + /// + /// 卡片式标签 + /// + [Description( "card" )] + Card, + /// + /// 可关闭的卡片式标签 + /// + [Description( "editable-card" )] + EditableCard +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/TableColumnType.cs b/src/Util.Ui.NgZorro/Enums/TableColumnType.cs new file mode 100644 index 000000000..322851f1b --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/TableColumnType.cs @@ -0,0 +1,19 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 表格列类型 +/// +public enum TableColumnType { + /// + /// 布尔 + /// + Bool, + /// + /// 日期 + /// + Date, + /// + /// 枚举 + /// + Enum +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/TableHeadColumnAlign.cs b/src/Util.Ui.NgZorro/Enums/TableHeadColumnAlign.cs new file mode 100644 index 000000000..232defb6c --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/TableHeadColumnAlign.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 表头单元格对齐方式 +/// +public enum TableHeadColumnAlign { + /// + /// 左对齐 + /// + [Description( "left" )] + Left, + /// + /// 右对齐 + /// + [Description( "right" )] + Right, + /// + /// 居中对齐 + /// + [Description( "center" )] + Center +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/TableLayout.cs b/src/Util.Ui.NgZorro/Enums/TableLayout.cs new file mode 100644 index 000000000..7529ead5d --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/TableLayout.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 表格布局 +/// +public enum TableLayout { + /// + /// 自动布局 + /// + [Description( "auto" )] + Auto, + /// + /// 固定布局 + /// + [Description( "fixed" )] + Fixed +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/TablePaginationPosition.cs b/src/Util.Ui.NgZorro/Enums/TablePaginationPosition.cs new file mode 100644 index 000000000..bb3e39cd9 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/TablePaginationPosition.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 表格分页位置 +/// +public enum TablePaginationPosition { + /// + /// 上方 + /// + [Description( "top" )] + Top, + /// + /// 下方 + /// + [Description( "bottom" )] + Bottom, + /// + /// 上方和下方 + /// + [Description( "both" )] + Both +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/TablePaginationSize.cs b/src/Util.Ui.NgZorro/Enums/TablePaginationSize.cs new file mode 100644 index 000000000..b6a345e61 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/TablePaginationSize.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 表格分页大小 +/// +public enum TablePaginationSize { + /// + /// 默认尺寸 + /// + [Description( "default" )] + Default, + /// + /// 小尺寸 + /// + [Description( "small" )] + Small +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/TableSize.cs b/src/Util.Ui.NgZorro/Enums/TableSize.cs new file mode 100644 index 000000000..67132bd56 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/TableSize.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 表格大小 +/// +public enum TableSize { + /// + /// 默认尺寸 + /// + [Description( "default" )] + Default, + /// + /// 中等尺寸 + /// + [Description( "middle" )] + Middle, + /// + /// 小尺寸 + /// + [Description( "small" )] + Small +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/TagColor.cs b/src/Util.Ui.NgZorro/Enums/TagColor.cs new file mode 100644 index 000000000..030e52734 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/TagColor.cs @@ -0,0 +1,102 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 标签颜色 +/// +public enum TagColor { + /// + /// default,默认 + /// + [Description( "default" )] + Default, + /// + /// success,成功 + /// + [Description( "success" )] + Success, + /// + /// processing,处理中 + /// + [Description( "processing" )] + Processing, + /// + /// warning,警告 + /// + [Description( "warning" )] + Warning, + /// + /// error, 错误 + /// + [Description( "error" )] + Error, + /// + /// pink, 粉红色 + /// + [Description( "pink" )] + Pink, + /// + /// red, 薄暮 - 红色 + /// + [Description( "red" )] + Red, + /// + /// magenta, 洋红 + /// + [Description( "magenta" )] + Magenta, + /// + /// volcano, 火山 + /// + [Description( "volcano" )] + Volcano, + /// + /// orange, 日暮 - 橘黄色 + /// + [Description( "orange" )] + Orange, + /// + /// gold, 金盏花 - 黄金色 + /// + [Description( "gold" )] + Gold, + /// + /// lime, 青柠 + /// + [Description( "lime" )] + Lime, + /// + /// green, 极光绿 + /// + [Description( "green" )] + Green, + /// + /// cyan, 明青 + /// + [Description( "cyan" )] + Cyan, + /// + /// blue, 拂晓蓝 + /// + [Description( "blue" )] + Blue, + /// + /// geekblue, 极客蓝 + /// + [Description( "geekblue" )] + GeekBlue, + /// + /// purple, 酱紫 + /// + [Description( "purple" )] + Purple, + /// + /// yellow, 黄色 + /// + [Description( "yellow" )] + Yellow, + /// + /// gray,灰色 + /// + [Description( "gray" )] + Gray +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/TagMode.cs b/src/Util.Ui.NgZorro/Enums/TagMode.cs new file mode 100644 index 000000000..8557d88a3 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/TagMode.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 标签模式 +/// +public enum TagMode { + /// + /// 默认 + /// + [Description( "default" )] + Default, + /// + /// 可关闭 + /// + [Description( "closeable" )] + Closeable, + /// + /// 可选择 + /// + [Description( "checkable" )] + Checkable +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/TextType.cs b/src/Util.Ui.NgZorro/Enums/TextType.cs new file mode 100644 index 000000000..fb5c0e037 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/TextType.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 文本类型 +/// +public enum TextType { + /// + /// 次要 + /// + [Description( "secondary" )] + Secondary, + /// + /// 警告 + /// + [Description( "warning" )] + Warning, + /// + /// 危险 + /// + [Description( "danger" )] + Danger, + /// + /// 成功 + /// + [Description( "success" )] + Success +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/TimelineItemPosition.cs b/src/Util.Ui.NgZorro/Enums/TimelineItemPosition.cs new file mode 100644 index 000000000..b9e91dfb7 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/TimelineItemPosition.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 时间轴节点位置 +/// +public enum TimelineItemPosition { + /// + /// 左侧显示 + /// + [Description( "left" )] + Left, + /// + /// 右侧显示 + /// + [Description( "right" )] + Right +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/TimelineMode.cs b/src/Util.Ui.NgZorro/Enums/TimelineMode.cs new file mode 100644 index 000000000..4c5bce229 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/TimelineMode.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 时间轴模式 +/// +public enum TimelineMode { + /// + /// 左侧显示 + /// + [Description( "left" )] + Left, + /// + /// 右侧显示 + /// + [Description( "right" )] + Right, + /// + /// 交替显示 + /// + [Description( "alternate" )] + Alternate, + /// + /// 自定义显示 + /// + [Description( "custom" )] + Custom +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/TooltipPlacement.cs b/src/Util.Ui.NgZorro/Enums/TooltipPlacement.cs new file mode 100644 index 000000000..ce1bb590b --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/TooltipPlacement.cs @@ -0,0 +1,67 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 提示框位置 +/// +public enum TooltipPlacement { + /// + /// 上方 + /// + [Description( "top" )] + Top, + /// + /// 左方 + /// + [Description( "left" )] + Left, + /// + /// 右方 + /// + [Description( "right" )] + Right, + /// + /// 下方 + /// + [Description( "bottom" )] + Bottom, + /// + /// 左上方 + /// + [Description( "topLeft" )] + TopLeft, + /// + /// 右上方 + /// + [Description( "topRight" )] + TopRight, + /// + /// 左下方 + /// + [Description( "bottomLeft" )] + BottomLeft, + /// + /// 右下方 + /// + [Description( "bottomRight" )] + BottomRight, + /// + /// 左上方 + /// + [Description( "leftTop" )] + LeftTop, + /// + /// 左下方 + /// + [Description( "leftBottom" )] + LeftBottom, + /// + /// 右上方 + /// + [Description( "rightTop" )] + RightTop, + /// + /// 右下方 + /// + [Description( "rightBottom" )] + RightBottom +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/TooltipTrigger.cs b/src/Util.Ui.NgZorro/Enums/TooltipTrigger.cs new file mode 100644 index 000000000..cf01f3996 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/TooltipTrigger.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 文字提示触发行为 +/// +public enum TooltipTrigger { + /// + /// 点击触发 + /// + [Description( "click" )] + Click, + /// + /// 获得焦点触发 + /// + [Description( "focus" )] + Focus, + /// + /// 移入触发 + /// + [Description( "hover" )] + Hover +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/TreeSelectPlacement.cs b/src/Util.Ui.NgZorro/Enums/TreeSelectPlacement.cs new file mode 100644 index 000000000..ad807314f --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/TreeSelectPlacement.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 树形选择弹出位置 +/// +public enum TreeSelectPlacement { + /// + /// 左下方 + /// + [Description( "bottomLeft" )] + BottomLeft, + /// + /// 右下方 + /// + [Description( "bottomRight" )] + BottomRight, + /// + /// 左上方 + /// + [Description( "topLeft" )] + TopLeft, + /// + /// 右上方 + /// + [Description( "topRight" )] + TopRight +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/UploadListType.cs b/src/Util.Ui.NgZorro/Enums/UploadListType.cs new file mode 100644 index 000000000..61ec0e4af --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/UploadListType.cs @@ -0,0 +1,22 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 上传列表类型 +/// +public enum UploadListType { + /// + /// 文本列表 + /// + [Description( "text" )] + Text, + /// + /// 图片列表 + /// + [Description( "picture" )] + Picture, + /// + /// 照片墙 + /// + [Description( "picture-card" )] + PictureCard +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Enums/ValidateStatus.cs b/src/Util.Ui.NgZorro/Enums/ValidateStatus.cs new file mode 100644 index 000000000..b05003c51 --- /dev/null +++ b/src/Util.Ui.NgZorro/Enums/ValidateStatus.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.NgZorro.Enums; + +/// +/// 校验状态 +/// +public enum ValidateStatus { + /// + /// 成功 + /// + [Description( "success" )] + Success, + /// + /// 警告 + /// + [Description( "warning" )] + Warning, + /// + /// 错误 + /// + [Description( "error" )] + Error, + /// + /// 校验中 + /// + [Description( "validating" )] + Validating +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Expressions/NgZorroExpressionLoaderBase.cs b/src/Util.Ui.NgZorro/Expressions/NgZorroExpressionLoaderBase.cs new file mode 100644 index 000000000..b95ef0091 --- /dev/null +++ b/src/Util.Ui.NgZorro/Expressions/NgZorroExpressionLoaderBase.cs @@ -0,0 +1,35 @@ +using Util.Ui.Expressions; +using Util.Ui.NgZorro.Components.Tables.Configs; + +namespace Util.Ui.NgZorro.Expressions; + +/// +/// NgZorro表达式加载器 +/// +public abstract class NgZorroExpressionLoaderBase : ExpressionLoaderBase { + /// + /// 创建模型表达式解析器 + /// + /// 配置 + protected override IExpressionResolver CreateExpressionResolver( Config config ) { + return new NgZorroExpressionResolver( config ); + } + + /// + /// 获取下划线分隔的小写属性名 + /// + protected virtual string GeKebaberizePropertyName( Config config, ModelExpressionInfo info ) { + var columnConfig = GetTableColumnShareConfig( config ); + var name = info.LastPropertyName.Kebaberize().Replace("-", "_"); + if ( columnConfig == null || columnConfig.IsEnableEdit == false ) + return name; + return $"control_{name}"; + } + + /// + /// 获取表格列共享配置 + /// + public TableColumnShareConfig GetTableColumnShareConfig( Config config ) { + return config.GetValueFromItems(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Expressions/NgZorroExpressionResolver.cs b/src/Util.Ui.NgZorro/Expressions/NgZorroExpressionResolver.cs new file mode 100644 index 000000000..6557c685f --- /dev/null +++ b/src/Util.Ui.NgZorro/Expressions/NgZorroExpressionResolver.cs @@ -0,0 +1,40 @@ +using Util.Ui.Configs; +using Util.Ui.Expressions; +using Util.Ui.NgZorro.Components.Tables.Configs; + +namespace Util.Ui.NgZorro.Expressions; + +/// +/// NgZorro模型表达式解析器 +/// +public class NgZorroExpressionResolver : ExpressionResolver { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化NgZorro模型表达式解析器 + /// + /// 配置 + public NgZorroExpressionResolver( Config config ) { + _config = config; + } + + /// + /// 获取默认模型名称 + /// + protected override string GetDefaultModel() { + var config = GetTableColumnShareConfig(); + if ( config == null ) + return "model"; + return "row"; + } + + /// + /// 获取表格列共享配置 + /// + public TableColumnShareConfig GetTableColumnShareConfig() { + return _config.GetValueFromItems(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Extensions/TagBuilderExtensions.cs b/src/Util.Ui.NgZorro/Extensions/TagBuilderExtensions.cs new file mode 100644 index 000000000..5b20ed25a --- /dev/null +++ b/src/Util.Ui.NgZorro/Extensions/TagBuilderExtensions.cs @@ -0,0 +1,86 @@ +using Util.Ui.Builders; + +namespace Util.Ui.NgZorro.Extensions; + +/// +/// ng-zorro扩展 +/// +public static class TagBuilderExtensions { + /// + /// *nzSpaceItem,间距项 + /// + /// 生成器类型 + /// 生成器实例 + /// 值 + public static TBuilder SpaceItem( this TBuilder builder, bool? value ) where TBuilder : TagBuilder { + if( value == true ) + builder.Attribute( "*nzSpaceItem" ); + return builder; + } + + /// + /// *nzSpaceItem,间距项 + /// + /// 生成器类型 + /// 生成器实例 + /// 配置 + public static TBuilder SpaceItem( this TBuilder builder, Config config ) where TBuilder : TagBuilder { + builder.SpaceItem( config.GetValue( UiConst.SpaceItem ) ); + return builder; + } + + /// + /// 宽度 + /// + /// 生成器类型 + /// 生成器实例 + /// 值 + /// 属性名 + public static TBuilder Width( this TBuilder builder, string value,string attributeName = "nzWidth" ) where TBuilder : TagBuilder { + builder.AttributeIfNotEmpty( attributeName, GetPixelValue( value ) ); + return builder; + } + + /// + /// 获取像素值,如果传入数字,后加px,否则按原样返回 + /// + /// 值 + private static string GetPixelValue( string value ) { + if ( value.IsEmpty() ) + return null; + if ( Util.Helpers.Validation.IsNumber( value ) ) + return $"{value}px"; + return value; + } + + /// + /// 添加到内容,支持i18n管道 + /// + /// 生成器类型 + /// 生成器实例 + /// 值 + public static TBuilder AppendContentByI18n( this TBuilder builder, string value ) where TBuilder : TagBuilder { + if ( value.IsEmpty() ) + return builder; + var result = new StringBuilder(); + result.Append( "{{" ); + result.Append( $"'{value}'|i18n" ); + result.Append( "}}" ); + builder.AppendContent( result.ToString() ); + return builder; + } + + /// + /// 添加属性,支持i18n管道 + /// + /// 生成器类型 + /// 生成器实例 + /// 属性名 + /// 值 + public static TBuilder AttributeByI18n( this TBuilder builder, string name, string value ) where TBuilder : TagBuilder { + if ( value.IsEmpty() ) + return builder; + builder.Attribute( name, $"'{value}'|i18n" ); + return builder; + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/NgZorroOptions.cs b/src/Util.Ui.NgZorro/NgZorroOptions.cs new file mode 100644 index 000000000..82d564e41 --- /dev/null +++ b/src/Util.Ui.NgZorro/NgZorroOptions.cs @@ -0,0 +1,74 @@ +using Util.Ui.NgZorro.Enums; +using Util.Ui.Razor; + +namespace Util.Ui.NgZorro; + +/// +/// NgZorro配置 +/// +public class NgZorroOptions : RazorOptions { + /// + /// 初始化NgZorro配置 + /// + public NgZorroOptions() { + if ( Util.Helpers.Environment.IsTest ) { + EnableDefaultOptionText = false; + EnableI18n = false; + EnableAllowClear = false; + EnableWatchRazor = false; + } + } + + /// + /// 是否启用默认项文本,默认值: true + /// + public bool EnableDefaultOptionText { get; set; } = true; + /// + /// 是否启用多语言,默认值: true + /// + public bool EnableI18n { get; set; } = true; + /// + /// 是否启用关闭浏览器自动完成功能, 设置 autocomplete="off",默认值: false + /// + public bool EnableAutocompleteOff { get; set; } + /// + /// 是否启用允许清除输入框,默认值: true + /// + public bool EnableAllowClear { get; set; } = true; + /// + /// 是否启用自动设置表单标签 nzFor,默认值: null + /// + public bool? EnableAutoNzFor { get; set; } = null; + /// + /// 是否启用勾选表格行复选框时添加样式类 table-row-checked ,默认值: false + /// + public bool EnableTableRowCheckedClass { get; set; } + /// + /// 获取表格布尔列内容操作 + /// + public Func GetTableColumnBoolContentAction { get; set; } + /// + /// 表格设置组件标签名,默认值:x-table-settings + /// + public string TableSettingsTag { get; set; } = "x-table-settings"; + /// + /// 表格复选框对齐方式,默认值:左对齐 + /// + public TableHeadColumnAlign TableCheckboxAlign { get; set; } = TableHeadColumnAlign.Left; + /// + /// 表格单选按钮对齐方式,默认值:左对齐 + /// + public TableHeadColumnAlign TableRadioAlign { get; set; } = TableHeadColumnAlign.Left; + /// + /// 表格序号对齐方式,默认值:左对齐 + /// + public TableHeadColumnAlign TableLineNumberAlign { get; set; } = TableHeadColumnAlign.Left; + /// + /// 对话框内容容器 <util-dialog-container> 的最大高度,默认值: 500px + /// + public string DialogContainerMaxHeight { get; set; } = "500px"; + /// + /// 启用表格排序,注意: 仅在使用 for 表达式时生效,且对树形表格无效, 默认值: false + /// + public bool EnableTableSort { get; set; } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Services/NgZorroTreeQueryAction.cs b/src/Util.Ui.NgZorro/Services/NgZorroTreeQueryAction.cs new file mode 100644 index 000000000..dcfc94a56 --- /dev/null +++ b/src/Util.Ui.NgZorro/Services/NgZorroTreeQueryAction.cs @@ -0,0 +1,61 @@ +using Util.Applications.Trees; +using Util.Data; +using Util.Data.Trees; + +namespace Util.Ui.NgZorro.Services; + +/// +/// 树形查询操作 +/// +/// 参数类型 +/// 查询参数类型 +public class NgZorroTreeQueryAction : TreeQueryActionBase + where TDto : TreeDtoBase, new() + where TQuery : class, ITreeQueryParameter { + /// + /// 查询服务 + /// + private readonly ITreeQueryService _service; + /// + /// 最大分页大小 + /// + private readonly int _maxPageSize; + /// + /// 初始化树形查询操作 + /// + /// 查询服务 + /// 加载模式 + /// 加载操作 + /// 最大分页大小 + /// 是否首次加载 + /// 是否展开所有节点 + /// 根节点异步加载模式是否展开子节点列表 + /// 需要加载的标识列表 + /// 查询前操作 + /// 数据处理操作 + public NgZorroTreeQueryAction( ITreeQueryService service, LoadMode loadMode, LoadOperation loadOperation, + int maxPageSize, bool isFirstLoad, bool isExpandAll, bool isExpandForRoot, string loadKeys, + Action queryBefore, Action, TQuery> processData ) + : base( service, loadMode, loadOperation, maxPageSize, isFirstLoad, isExpandAll, isExpandForRoot, loadKeys, queryBefore, processData ) { + _service = service; + _maxPageSize = maxPageSize; + } + + /// + /// 转换为树形结果 + /// + /// 数据列表 + /// 是否异步加载 + /// 所有节点是否全部展开 + protected override dynamic ToResult( PageList data, bool async = false, bool allExpand = false ) { + return data.Data.ToTreeResult( async, allExpand ); + } + + /// + /// 异步查询 + /// + protected override async Task> AsyncQuery( TQuery query ) { + query.PageSize = _maxPageSize; + return await _service.PageQueryAsync( query ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/TreeResultExtensions.cs b/src/Util.Ui.NgZorro/TreeResultExtensions.cs new file mode 100644 index 000000000..5d07d3969 --- /dev/null +++ b/src/Util.Ui.NgZorro/TreeResultExtensions.cs @@ -0,0 +1,19 @@ +using Util.Applications.Trees; +using Util.Ui.NgZorro.Data; + +namespace Util.Ui.NgZorro; + +/// +/// NgZorro树形结果扩展 +/// +public static class TreeResultExtensions { + /// + /// 转换为NgZorro树形结果 + /// + /// 树形参数列表 + /// 是否异步加载 + /// 所有节点是否全部展开 + public static NgZorroTreeResult ToTreeResult( this IEnumerable nodes, bool async = false, bool allExpand = false ) where TNode : TreeDtoBase { + return new TreeResult( nodes, async, allExpand ).GetResult(); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/Usings.cs b/src/Util.Ui.NgZorro/Usings.cs new file mode 100644 index 000000000..b03f3c3a2 --- /dev/null +++ b/src/Util.Ui.NgZorro/Usings.cs @@ -0,0 +1,19 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using System.Text; +global using System.IO; +global using System.ComponentModel; +global using System.Text.Json; +global using System.Text.Encodings.Web; +global using System.Text.Json.Serialization; +global using System.Text.Unicode; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.Logging; +global using Microsoft.AspNetCore.Html; +global using Microsoft.AspNetCore.Mvc; +global using Humanizer; +global using Util.Ui.Configs; diff --git a/src/Util.Ui.NgZorro/WatchHostedService.cs b/src/Util.Ui.NgZorro/WatchHostedService.cs new file mode 100644 index 000000000..f5133b8bb --- /dev/null +++ b/src/Util.Ui.NgZorro/WatchHostedService.cs @@ -0,0 +1,35 @@ +using Util.Ui.Razor; + +namespace Util.Ui.NgZorro; + +/// +/// Razor页面监视服务 +/// +public class WatchHostedService : IHostedService { + /// + /// Razor页面监听服务 + /// + private readonly IRazorWatchService _service; + + /// + /// 初始化Razor页面监视服务 + /// + /// Razor页面监听服务 + public WatchHostedService( IRazorWatchService service ) { + _service = service; + } + + /// + /// 启动服务 + /// + public async Task StartAsync( CancellationToken cancellationToken ) { + await _service.StartAsync( cancellationToken ); + } + + /// + /// 停止服务 + /// + public async Task StopAsync( CancellationToken cancellationToken ) { + await _service.StopAsync( cancellationToken ); + } +} \ No newline at end of file diff --git a/src/Util.Ui.NgZorro/WebApplicationExtensions.cs b/src/Util.Ui.NgZorro/WebApplicationExtensions.cs new file mode 100644 index 000000000..c85bc33f1 --- /dev/null +++ b/src/Util.Ui.NgZorro/WebApplicationExtensions.cs @@ -0,0 +1,121 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Options; +using Util.Ui.Sources.Spa.AngularCli; +using Util.Ui.Sources.Spa; +using Util.Ui.Razor; + +namespace Util.Ui.NgZorro; + +/// +/// NgZorro配置扩展 +/// +public static class WebApplicationExtensions { + /// + /// ClientApp + /// + private const string SourcePath = "ClientApp"; + + /// + /// 配置NgZorro应用 + /// + /// Web应用 + public static WebApplication UseNgZorro( this WebApplication app ) { + app.CheckNull( nameof( app ) ); + AddEndpoints( app ); + app.UseAngular( spa => { + var options = app.Services.GetService>(); + spa.Options.StartupTimeout = options.Value.StartupTimeout; + spa.Options.SourcePath = SourcePath; + if ( app.Environment.IsDevelopment() ) + spa.UseAngularCliServer( "start" ); + } ); + return app; + } + + /// + /// 添加路由端点 + /// + private static void AddEndpoints( WebApplication app ) { + app.CheckNull( nameof( app ) ); + app.UseStaticFiles(); + app.UseSpaStaticFiles(); + app.UseRouting(); +#pragma warning disable ASP0014 + app.UseEndpoints( builder => { + builder.MapRazorPages(); + builder.MapControllers(); + } ); +#pragma warning restore ASP0014 + } + + /// + /// 配置NgZorro应用 + /// + /// Web应用 + /// 配置Spa操作 + public static WebApplication UseNgZorro( this WebApplication app, Action action ) { + app.CheckNull( nameof( app ) ); + AddEndpoints( app ); + if ( app.Environment.IsDevelopment() == false ) + return app; + if ( action == null ) + return app; + app.UseSpa( spa => { + if ( app.Environment.IsDevelopment() ) + action( spa ); + }); + return app; + } + + /// + /// 配置NgZorro应用 + /// + /// Web应用 + /// 开发服务器基地址,范例: http://localhost:5000 + /// 是否自动启动Angular服务器,默认值: true + public static WebApplication UseNgZorro( this WebApplication app, string developmentServerBaseUri, bool isAutoStartAngularServer = true ) { + app.CheckNull( nameof( app ) ); + var port = new Uri( developmentServerBaseUri ).Port; + return app.UseNgZorro( port, isAutoStartAngularServer ); + } + + /// + /// 配置NgZorro应用 + /// + /// Web应用 + /// 开发服务器端口号 + /// 是否自动启动Angular服务器,默认值: true + public static WebApplication UseNgZorro( this WebApplication app, int port, bool isAutoStartAngularServer = true ) { + app.CheckNull( nameof( app ) ); + return isAutoStartAngularServer ? UseCustomSpa( app, port ) : UseSpa( app, $"http://localhost:{port}" ); + } + + /// + /// 使用官方SPA实现 + /// + private static WebApplication UseSpa( WebApplication app, string developmentServerBaseUri ) { + app.CheckNull( nameof( app ) ); + AddEndpoints( app ); + app.UseSpa( spa => { + spa.Options.SourcePath = SourcePath; + if ( app.Environment.IsDevelopment() ) + spa.UseProxyToSpaDevelopmentServer( developmentServerBaseUri ); + } ); + return app; + } + + /// + /// 使用SPA扩展实现 + /// + private static WebApplication UseCustomSpa( WebApplication app, int port ) { + app.CheckNull( nameof( app ) ); + AddEndpoints( app ); + app.UseAngular( spa => { + spa.Options.SourcePath = SourcePath; + spa.Options.DevServerPort = port; + if ( app.Environment.IsDevelopment() ) + spa.UseAngularCliServer( "start" ); + } ); + return app; + } +} \ No newline at end of file diff --git a/src/Util.Ui/01-Util.Ui.csproj b/src/Util.Ui/01-Util.Ui.csproj new file mode 100644 index 000000000..aac261033 --- /dev/null +++ b/src/Util.Ui/01-Util.Ui.csproj @@ -0,0 +1,43 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Ui是Util应用框架的Ui封装基类库 + Util.Ui + Util.Ui + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Ui.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Ui.xml + + + + + True + False + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Util.Ui/Builders/CodeBuilder.cs b/src/Util.Ui/Builders/CodeBuilder.cs new file mode 100644 index 000000000..eb12e9719 --- /dev/null +++ b/src/Util.Ui/Builders/CodeBuilder.cs @@ -0,0 +1,12 @@ +namespace Util.Ui.Builders; + +/// +/// code标签生成器 +/// +public class CodeBuilder : TagBuilder { + /// + /// 初始化code标签生成器 + /// + public CodeBuilder() : base( "code" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui/Builders/DelBuilder.cs b/src/Util.Ui/Builders/DelBuilder.cs new file mode 100644 index 000000000..f9b48fadd --- /dev/null +++ b/src/Util.Ui/Builders/DelBuilder.cs @@ -0,0 +1,12 @@ +namespace Util.Ui.Builders; + +/// +/// del标签生成器 +/// +public class DelBuilder : TagBuilder { + /// + /// 初始化del标签生成器 + /// + public DelBuilder() : base( "del" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui/Builders/EmptyContainerTagBuilder.cs b/src/Util.Ui/Builders/EmptyContainerTagBuilder.cs new file mode 100644 index 000000000..348d0ede6 --- /dev/null +++ b/src/Util.Ui/Builders/EmptyContainerTagBuilder.cs @@ -0,0 +1,136 @@ +namespace Util.Ui.Builders; + +/// +/// 空容器标签生成器 +/// +public class EmptyContainerTagBuilder : TagBuilder { + /// + /// html内容生成器 + /// + private readonly HtmlContentBuilder _builder; + + /// + /// 初始化空容器标签生成器 + /// + public EmptyContainerTagBuilder() : base( "i" ) { + _builder = new HtmlContentBuilder(); + } + + /// + /// Html内容 + /// + public override IHtmlContentBuilder InnerHtml => null; + + /// + /// 是否包含Html内容 + /// + public override bool HasInnerHtml => _builder.Count > 0; + + /// + /// 添加class属性 + /// + /// class属性值 + public override TagBuilder Class( string @class ) { + return this; + } + + /// + /// 添加属性,当属性名已存在则忽略,也可进行替换 + /// + /// 属性名 + /// 属性值 + /// 是否替换已存在的属性 + /// 相同名称的属性值是否累加 + public override TagBuilder Attribute( string name, string value = "", bool replaceExisting = false, bool append = false ) { + return this; + } + + /// + /// 添加属性,当值为空时忽略 + /// + /// 属性名 + /// 属性值 + /// 是否替换已存在的属性 + /// 相同名称的属性值是否累加 + public override TagBuilder AttributeIfNotEmpty( string name, string value, bool replaceExisting = false, bool append = false ) { + return this; + } + + /// + /// 添加内容 + /// + /// 内容 + public override TagBuilder AppendContent( string content ) { + _builder.AppendHtml( content ); + return this; + } + + /// + /// 添加内容 + /// + /// 内容 + public override TagBuilder AppendContent( IHtmlContent content ) { + _builder.AppendHtml( content ); + return this; + } + + /// + /// 添加内容 + /// + /// 标签生成器 + public override TagBuilder AppendContent( TagBuilder tagBuilder ) { + AppendContent( (IHtmlContent)tagBuilder ); + return this; + } + + /// + /// 设置内容 + /// + /// 内容 + public override TagBuilder SetContent( string content ) { + _builder.SetHtmlContent( content ); + return this; + } + + /// + /// 设置内容 + /// + /// 内容 + public override TagBuilder SetContent( IHtmlContent content ) { + _builder.SetHtmlContent( content ); + return this; + } + + /// + /// 设置内容 + /// + /// 标签生成器 + public override TagBuilder SetContent( TagBuilder tagBuilder ) { + SetContent( (IHtmlContent)tagBuilder ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + } + + /// + /// 写入文本流 + /// + /// 流写入器 + /// 编码 + public override void WriteTo( TextWriter writer, HtmlEncoder encoder ) { + _builder.WriteTo( writer, NullHtmlEncoder.Default ); + } + + /// + /// 获取Html结果 + /// + public override string ToString() { + using var writer = new StringWriter(); + _builder.WriteTo( writer, NullHtmlEncoder.Default ); + return writer.ToString(); + } +} \ No newline at end of file diff --git a/src/Util.Ui/Builders/EmptyTagBuilder.cs b/src/Util.Ui/Builders/EmptyTagBuilder.cs new file mode 100644 index 000000000..0fd8bb0ef --- /dev/null +++ b/src/Util.Ui/Builders/EmptyTagBuilder.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.Builders; + +/// +/// 空标签生成器 +/// +public class EmptyTagBuilder : TagBuilder { + /// + /// 初始化空标签生成器 + /// + public EmptyTagBuilder() : base( "i" ) { + } + + /// + /// 写入流 + /// + /// 流写入器 + /// Html编码 + public override void WriteTo( TextWriter writer, HtmlEncoder encoder ) { + } + + /// + /// 输出 + /// + public override string ToString() { + return string.Empty; + } +} \ No newline at end of file diff --git a/src/Util.Ui/Builders/ItalicBuilder.cs b/src/Util.Ui/Builders/ItalicBuilder.cs new file mode 100644 index 000000000..dd96a0b65 --- /dev/null +++ b/src/Util.Ui/Builders/ItalicBuilder.cs @@ -0,0 +1,12 @@ +namespace Util.Ui.Builders; + +/// +/// i标签生成器 +/// +public class ItalicBuilder : TagBuilder { + /// + /// 初始化i标签生成器 + /// + public ItalicBuilder() : base( "i" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui/Builders/KbdBuilder.cs b/src/Util.Ui/Builders/KbdBuilder.cs new file mode 100644 index 000000000..9350aa98e --- /dev/null +++ b/src/Util.Ui/Builders/KbdBuilder.cs @@ -0,0 +1,12 @@ +namespace Util.Ui.Builders; + +/// +/// kbd标签生成器 +/// +public class KbdBuilder : TagBuilder { + /// + /// 初始化kbd标签生成器 + /// + public KbdBuilder() : base( "kbd" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui/Builders/LiBuilder.cs b/src/Util.Ui/Builders/LiBuilder.cs new file mode 100644 index 000000000..c9448cde6 --- /dev/null +++ b/src/Util.Ui/Builders/LiBuilder.cs @@ -0,0 +1,12 @@ +namespace Util.Ui.Builders; + +/// +/// li标签生成器 +/// +public class LiBuilder : TagBuilder { + /// + /// 初始化li标签生成器 + /// + public LiBuilder() : base( "li" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui/Builders/MarkBuilder.cs b/src/Util.Ui/Builders/MarkBuilder.cs new file mode 100644 index 000000000..899cf2682 --- /dev/null +++ b/src/Util.Ui/Builders/MarkBuilder.cs @@ -0,0 +1,12 @@ +namespace Util.Ui.Builders; + +/// +/// mark标签生成器 +/// +public class MarkBuilder : TagBuilder { + /// + /// 初始化mark标签生成器 + /// + public MarkBuilder() : base( "mark" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui/Builders/StrongBuilder.cs b/src/Util.Ui/Builders/StrongBuilder.cs new file mode 100644 index 000000000..4dbe8e268 --- /dev/null +++ b/src/Util.Ui/Builders/StrongBuilder.cs @@ -0,0 +1,12 @@ +namespace Util.Ui.Builders; + +/// +/// strong标签生成器 +/// +public class StrongBuilder : TagBuilder { + /// + /// 初始化strong标签生成器 + /// + public StrongBuilder() : base( "strong" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui/Builders/TagBuilder.cs b/src/Util.Ui/Builders/TagBuilder.cs new file mode 100644 index 000000000..2c66f814d --- /dev/null +++ b/src/Util.Ui/Builders/TagBuilder.cs @@ -0,0 +1,257 @@ +using Microsoft.AspNetCore.Mvc.Rendering; +using Util.Ui.Configs; +using Util.Ui.Extensions; +using Util.Ui.Helpers; + +namespace Util.Ui.Builders; + +/// +/// 标签生成器 +/// +public class TagBuilder : IHtmlContent { + /// + /// 标签生成器 + /// + private readonly Microsoft.AspNetCore.Mvc.Rendering.TagBuilder _tagBuilder; + + /// + /// 初始化标签生成器 + /// + /// 标签名称,范例:div + /// 渲染模式 + public TagBuilder( string tagName, TagRenderMode renderMode = TagRenderMode.Normal ) { + _tagBuilder = new Microsoft.AspNetCore.Mvc.Rendering.TagBuilder( tagName ) { TagRenderMode = renderMode }; + } + + /// + /// 空标签生成器 + /// + public static readonly TagBuilder Null = new EmptyTagBuilder(); + + /// + /// Html内容 + /// + public virtual IHtmlContentBuilder InnerHtml => _tagBuilder.InnerHtml; + + /// + /// 是否包含Html内容 + /// + public virtual bool HasInnerHtml => _tagBuilder.HasInnerHtml; + + /// + /// 前一个标签生成器 + /// + public TagBuilder PreBuilder { get; set; } + + /// + /// 后一个标签生成器 + /// + public TagBuilder PostBuilder { get; set; } + + /// + /// 添加class属性 + /// + /// class属性值 + public virtual TagBuilder Class( string @class ) { + if ( string.IsNullOrWhiteSpace( @class ) == false ) + _tagBuilder.AddCssClass( @class ); + return this; + } + + /// + /// 添加属性,当属性名已存在则忽略,也可进行替换 + /// + /// 属性名 + /// 属性值 + /// 是否替换已存在的属性 + /// 相同名称的属性值是否累加 + public virtual TagBuilder Attribute( string name, string value = "", bool replaceExisting = false, bool append = false ) { + if ( _tagBuilder.Attributes.ContainsKey( name ) == false ) { + _tagBuilder.MergeAttribute( name, value ); + return this; + } + if ( replaceExisting ) { + _tagBuilder.MergeAttribute( name, value, true ); + return this; + } + if ( append == false ) + return this; + var newValue = $"{_tagBuilder.Attributes[name]};{value}"; + _tagBuilder.MergeAttribute( name, newValue, true ); + return this; + } + + /// + /// 添加属性,当条件为true时添加 + /// + /// 属性名 + /// 该值为true时添加,否则忽略 + public virtual TagBuilder AttributeIf( string name, bool condition ) { + if ( condition == false ) + return this; + Attribute( name ); + return this; + } + + /// + /// 添加属性,当条件为true时添加 + /// + /// 属性名 + /// 属性值 + /// 该值为true时添加,否则忽略 + /// 是否替换已存在的属性 + /// 相同名称的属性值是否累加 + public virtual TagBuilder AttributeIf( string name, string value, bool condition, bool replaceExisting = false, bool append = false ) { + if ( condition == false ) + return this; + Attribute( name, value, replaceExisting, append ); + return this; + } + + /// + /// 添加属性,当值为空时忽略 + /// + /// 属性名 + /// 属性值 + /// 是否替换已存在的属性 + /// 相同名称的属性值是否累加 + public virtual TagBuilder AttributeIfNotEmpty( string name, string value, bool replaceExisting = false, bool append = false ) { + AttributeIf( name, value, !string.IsNullOrWhiteSpace( value ), replaceExisting, append ); + return this; + } + + /// + /// 添加内容 + /// + /// 内容 + public virtual TagBuilder AppendContent( string content ) { + _tagBuilder.InnerHtml.AppendHtml( content ); + return this; + } + + /// + /// 添加内容 + /// + /// 内容 + public virtual TagBuilder AppendContent( IHtmlContent content ) { + _tagBuilder.InnerHtml.AppendHtml( content ); + return this; + } + + /// + /// 添加内容 + /// + /// 标签生成器 + public virtual TagBuilder AppendContent( TagBuilder tagBuilder ) { + AppendContent( (IHtmlContent)tagBuilder ); + return this; + } + + /// + /// 设置内容 + /// + /// 内容 + public virtual TagBuilder SetContent( string content ) { + _tagBuilder.InnerHtml.SetHtmlContent( content ); + return this; + } + + /// + /// 设置内容 + /// + /// 内容 + public virtual TagBuilder SetContent( IHtmlContent content ) { + _tagBuilder.InnerHtml.SetHtmlContent( content ); + return this; + } + + /// + /// 设置内容 + /// + /// 标签生成器 + public virtual TagBuilder SetContent( TagBuilder tagBuilder ) { + SetContent( (IHtmlContent)tagBuilder ); + return this; + } + + /// + /// 写入文本流 + /// + /// 流写入器 + /// 编码 + public virtual void WriteTo( TextWriter writer, HtmlEncoder encoder ) { + if ( PreBuilder != null ) + PreBuilder.WriteTo( writer, NullHtmlEncoder.Default ); + _tagBuilder.WriteTo( writer, NullHtmlEncoder.Default ); + if ( PostBuilder != null ) + PostBuilder.WriteTo( writer, NullHtmlEncoder.Default ); + } + + /// + /// 获取Html结果 + /// + public override string ToString() { + using var writer = new StringWriter(); + _tagBuilder.WriteTo( writer, NullHtmlEncoder.Default ); + return writer.ToString(); + } + + /// + /// 配置 + /// + public virtual void Config() { + } + + /// + /// 基础配置 + /// + /// 配置 + public virtual void ConfigBase( Config config ) { + ConfigStyle( config ); + ConfigClass( config ); + ConfigHidden( config ); + ConfigOutputAttributes( config ); + } + + /// + /// 配置样式 + /// + /// 配置 + protected virtual void ConfigStyle( Config config ) { + this.Style( config ); + } + + /// + /// 配置样式类 + /// + /// 配置 + protected virtual void ConfigClass( Config config ) { + this.Class( config ); + } + + /// + /// 配置隐藏 + /// + /// 配置 + protected virtual void ConfigHidden( Config config ) { + this.Hidden( config ); + } + + /// + /// 配置输出属性 + /// + /// 配置 + protected virtual void ConfigOutputAttributes( Config config ) { + this.Attributes( config.OutputAttributes ); + } + + /// + /// 添加style样式 + /// + /// 配置 + /// 样式名称 + /// 样式值 + protected virtual void Style( Config config, string name, string value ) { + config.SetAttribute( UiConst.Style, $"{name}:{SizeHelper.GetValue( value )}" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui/Builders/UBuilder.cs b/src/Util.Ui/Builders/UBuilder.cs new file mode 100644 index 000000000..46e761a0a --- /dev/null +++ b/src/Util.Ui/Builders/UBuilder.cs @@ -0,0 +1,12 @@ +namespace Util.Ui.Builders; + +/// +/// u标签生成器 +/// +public class UBuilder : TagBuilder { + /// + /// 初始化u标签生成器 + /// + public UBuilder() : base( "u" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui/Builders/UlBuilder.cs b/src/Util.Ui/Builders/UlBuilder.cs new file mode 100644 index 000000000..f7c858c7d --- /dev/null +++ b/src/Util.Ui/Builders/UlBuilder.cs @@ -0,0 +1,12 @@ +namespace Util.Ui.Builders; + +/// +/// ul标签生成器 +/// +public class UlBuilder : TagBuilder { + /// + /// 初始化ul标签生成器 + /// + public UlBuilder() : base( "ul" ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui/Configs/Config.cs b/src/Util.Ui/Configs/Config.cs new file mode 100644 index 000000000..950ee2792 --- /dev/null +++ b/src/Util.Ui/Configs/Config.cs @@ -0,0 +1,171 @@ +using Util.Ui.Extensions; + +namespace Util.Ui.Configs; + +/// +/// 配置 +/// +public class Config { + /// + /// 初始化配置 + /// + public Config() : this( null, null ) { + } + + /// + /// 初始化配置 + /// + /// 上下文 + /// 输出 + public Config( TagHelperContext context, TagHelperOutput output ) : this( context, output, null ) { + } + + /// + /// 初始化配置 + /// + /// 上下文 + /// 输出 + /// Html内容 + public Config( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + Id = context?.UniqueId; + context ??= new TagHelperContext( new TagHelperAttributeList(), new Dictionary(), Guid.NewGuid().ToString() ); + Output = output ?? new TagHelperOutput( "", new TagHelperAttributeList(), ( useCachedResult, encoder ) => Task.FromResult( content ) ); + Content = content; + AllAttributes = new TagHelperAttributeList( context.AllAttributes ); + OutputAttributes = new TagHelperAttributeList( Output.Attributes ); + Context = new TagHelperContext( AllAttributes, context.Items, context.UniqueId ); + } + + /// + /// 标识 + /// + public string Id { get; set; } + + /// + /// 上下文 + /// + protected TagHelperContext Context { get; set; } + + /// + /// 输出 + /// + protected TagHelperOutput Output { get; set; } + + /// + /// Html内容 + /// + public TagHelperContent Content { get; set; } + + /// + /// 全部属性集合 + /// + public TagHelperAttributeList AllAttributes { get; set; } + + /// + /// 输出属性集合,TagHelper中未明确定义的属性从该集合获取 + /// + public TagHelperAttributeList OutputAttributes { get; set; } + + /// + /// 属性集合是否包含指定属性 + /// + /// 属性名 + public bool Contains( string name ) { + return AllAttributes.ContainsName( name ); + } + + /// + /// 获取属性值 + /// + /// 属性名 + public string GetValue( string name ) { + return Contains( name ) ? AllAttributes[name].Value.SafeString() : string.Empty; + } + + /// + /// 获取属性值 + /// + /// 目标类型 + /// 属性名 + public T GetValue( string name ) { + return Contains( name ) ? Util.Helpers.Convert.To( AllAttributes[name].Value ) : default; + } + + /// + /// 获取布尔属性值 + /// + /// 属性名 + public string GetBoolValue( string name ) { + return Util.Helpers.String.FirstLowerCase( GetValue( name ) ); + } + + /// + /// 设置属性 + /// + /// 属性名 + /// 值 + /// 是否替换已存在的属性 + public void SetAttribute( string name, object value, bool replaceExisting = true ) { + if ( replaceExisting == false && Contains( name ) ) + return; + AllAttributes.SetAttribute( new TagHelperAttribute( name, value ) ); + } + + /// + /// 移除属性 + /// + /// 属性名 + public void RemoveAttribute( string name ) { + AllAttributes.RemoveAll( name ); + } + + /// + /// 从TagHelperContext Items里获取值 + /// + /// 返回类型 + /// 键 + public T GetValueFromItems( object key = null ) { + return Context.GetValueFromItems( key ); + } + + /// + /// 设置TagHelperContext Items值 + /// + /// 值 + public void SetValueToItems( T value ) { + Context.SetValueToItems( value ); + } + + /// + /// 从TagHelperContext AllAttributes里获取值 + /// + /// 返回类型 + /// 键 + public T GetValueFromAttributes( string key ) { + if ( Context == null ) + return default( T ); + return Context.GetValueFromAttributes( key ); + } + + /// + /// 从TagHelperContext AllAttributes里获取值 + /// + /// 键 + public string GetValueFromAttributes( string key ) { + return GetValueFromAttributes( key ); + } + + /// + /// 复制配置 + /// + public Config Copy() { + var content = new DefaultTagHelperContent(); + Content?.CopyTo( content ); + return new Config { + Context = new TagHelperContext( AllAttributes, Context.Items, Util.Helpers.Id.Create() ), + AllAttributes = new TagHelperAttributeList( AllAttributes ), + OutputAttributes = new TagHelperAttributeList( OutputAttributes ), + Content = content + }; + } +} \ No newline at end of file diff --git a/src/Util.Ui/Configs/UiConst.cs b/src/Util.Ui/Configs/UiConst.cs new file mode 100644 index 000000000..7a793c7be --- /dev/null +++ b/src/Util.Ui/Configs/UiConst.cs @@ -0,0 +1,3983 @@ +namespace Util.Ui.Configs; + +/// +/// Ui常量 +/// +public static class UiConst { + /// + /// 名称 + /// + public const string Name = "name"; + /// + /// 类名 + /// + public const string Class = "class"; + /// + /// 类名 + /// + public const string ClassName = "class-name"; + /// + /// 样式 + /// + public const string Style = "style"; + /// + /// 颜色 + /// + public const string Color = "color"; + /// + /// 填充 + /// + public const string Padding = "padding"; + /// + /// 递归 + /// + public const string Recursive = "recursive"; + /// + /// 跟踪 + /// + public const string TrackBy = "track-by"; + /// + /// 提及触发器 + /// + public const string MentionTrigger = "mention-trigger"; + /// + /// 旋转图标 + /// + public const string RotateIcon = "rotate-icon"; + /// + /// 激活图标 + /// + public const string ActiveIcon = "active-icon"; + /// + /// 标签模板 + /// + public const string LabelTemplate = "label-template"; + /// + /// 标签换行 + /// + public const string LabelWrap = "label-wrap"; + /// + /// 标签对齐方式 + /// + public const string LabelAlign = "label-align"; + /// + /// 缩放步数 + /// + public const string ScaleStep = "scale-step"; + /// + /// NzId + /// + public const string NzId = "nz-id"; + /// + /// NzFor + /// + public const string NzFor = "nz-for"; + /// + /// 循环 + /// + public const string Loop = "loop"; + /// + /// 触发器 + /// + public const string FlipFlop = "flip-flop"; + /// + /// 显示周数 + /// + public const string ShowWeekNumber = "show-week-number"; + /// + /// 背景 + /// + public const string Backdrop = "backdrop"; + /// + /// 自定义头部 + /// + public const string CustomHeader = "custom-header"; + /// + /// 百分比 + /// + public const string Percentage = "percentage"; + /// + /// 单位 + /// + public const string Unit = "unit"; + /// + /// 左内边距 + /// + public const string PaddingLeft = "padding-left"; + /// + /// 触发操作 + /// + public const string TriggerAction = "trigger-action"; + /// + /// 不验证表单 + /// + public const string Novalidate = "novalidate"; + /// + /// 选项卡链接 + /// + public const string TabLink = "tab-link"; + /// + /// 可选 + /// + public const string Optional = "optional"; + /// + /// 可选帮助 + /// + public const string OptionalHelp = "optional-help"; + /// + /// 渲染项 + /// + public const string RenderItem = "render-item"; + /// + /// 当前锚点 + /// + public const string CurrentAnchor = "current-anchor"; + /// + /// 确认气泡框确认前操作 + /// + public const string PopconfirmBeforeConfirm = "popconfirm-before-confirm"; + /// + /// 确认气泡框确认按钮是否危险按钮 + /// + public const string PopconfirmOkDanger = "popconfirm-ok-danger"; + /// + /// 确认气泡框是否禁用确认按钮 + /// + public const string PopconfirmOkDisabled = "popconfirm-ok-disabled"; + /// + /// 确认气泡框箭头是否指向锚点的中心 + /// + public const string PopconfirmArrowPointAtCenter = "popconfirm-arrow-point-at-center"; + /// + /// 目标偏移量 + /// + public const string TargetOffset = "target-offset"; + /// + /// 可选帮助颜色 + /// + public const string OptionalHelpColor = "optional-help-color"; + /// + /// 提示箭头指向锚点中心 + /// + public const string TooltipArrowPointAtCenter = "tooltip-arrow-point-at-center"; + /// + /// 气泡箭头指向锚点中心 + /// + public const string PopoverArrowPointAtCenter = "popover-arrow-point-at-center"; + /// + /// 提示文字上下文 + /// + public const string TooltipTitleContext = "tooltip-title-context"; + /// + /// 隐藏标签 + /// + public const string HideLabel = "hide-label"; + /// + /// 全屏外层样式类 + /// + public const string FullscreenWrapClass = "fullscreen-wrap-class"; + /// + /// 等级 + /// + public const string Level = "level"; + /// + /// 默认 + /// + public const string Default = "default"; + /// + /// 图片 + /// + public const string Image = "image"; + /// + /// 持续时间 + /// + public const string Duration = "duration"; + /// + /// 单元格控件 + /// + public const string CellControl = "cell-control"; + /// + /// 点击选中 + /// + public const string CheckOnClick = "check-on-click"; + /// + /// 值支持多语言 + /// + public const string ValueI18n = "value-i18n"; + /// + /// 可见高度 + /// + public const string VisibilityHeight = "visibility-height"; + /// + /// 启用拖动调整尺寸 + /// + public const string EnableResizable = "enable-resizable"; + /// + /// 启用表格设置 + /// + public const string EnableTableSettings = "enable-table-settings"; + /// + /// 启用固定列 + /// + public const string EnableFixedColumn = "enable-fixed-column"; + /// + /// 格式变化事件 + /// + public const string OnFormatChange = "on-format-change"; + /// + /// 显示文本 + /// + public const string ShowText = "show-text"; + /// + /// 默认值 + /// + public const string DefaultValue = "default-value"; + /// + /// 起始日期 + /// + public const string BeginDate = "begin-date"; + /// + /// 结束日期 + /// + public const string EndDate = "end-date"; + /// + /// 值变更事件 + /// + public const string OnValueChange = "on-value-change"; + /// + /// 起始 + /// + public const string ForBegin = "for-begin"; + /// + /// 结束 + /// + public const string ForEnd = "for-end"; + /// + /// 最小宽度 + /// + public const string MinWidth = "min-width"; + /// + /// 最大宽度 + /// + public const string MaxWidth = "max-width"; + /// + /// 是否启用自定义列 + /// + public const string EnableCustomColumn = "enable-custom-column"; + /// + /// 背景色 + /// + public const string BackgroundColor = "background-color"; + /// + /// 背景色 + /// + public const string BgColor = "bg-color"; + /// + /// 粘贴图片 + /// + public const string PasteDataImages = "paste-data-images"; + /// + /// 选中文本变更 + /// + public const string OnSelectedTextChange = "on-selected-text-change"; + /// + /// 选中值变更 + /// + public const string OnSelectedValueChange = "on-selected-value-change"; + /// + /// 剪贴板 + /// + public const string Clipboard = "clipboard"; + /// + /// 属性名 + /// + public const string PropertyName = "property-name"; + /// + /// Html + /// + public const string Html = "html"; + /// + /// 工具栏模式 + /// + public const string ToolbarMode = "toolbar-mode"; + /// + /// 全部选中 + /// + public const string AllSelected = "all-selected"; + /// + /// 选中文本 + /// + public const string SelectedText = "selected-text"; + /// + /// 选中值 + /// + public const string SelectedValue = "selected-value"; + /// + /// 下拉渲染 + /// + public const string DropdownRender = "dropdown-render"; + /// + /// 仅显示日期 + /// + public const string ShowDateOnly = "show-date-only"; + /// + /// 行数 + /// + public const string Lines = "lines"; + /// + /// 访问控制 + /// + public const string Acl = "acl"; + /// + /// 访问控制 + /// + public const string AclElse = "acl-else"; + /// + /// 访问控制模板标识 + /// + public const string AclElseTemplateId = "acl-else-template-id"; + /// + /// 尾巴 + /// + public const string Tail = "tail"; + /// + /// 类型 + /// + public const string Type = "type"; + /// + /// 数据类型 + /// + public const string DataType = "data-type"; + /// + /// 排版 + /// + public const string Typography = "typography"; + /// + /// 参数 + /// + public const string Param = "param"; + /// + /// 插件列表 + /// + public const string Plugins = "plugins"; + /// + /// 工具栏 + /// + public const string Toolbar = "toolbar"; + /// + /// 标识 + /// + public const string Id = "id"; + /// + /// 菜单栏 + /// + public const string Menubar = "menubar"; + /// + /// 文本 + /// + public const string Text = "text"; + /// + /// upload文本 + /// + public const string TextUpload = "text-upload"; + /// + /// download文本 + /// + public const string TextDownload = "text-download"; + /// + /// ok文本 + /// + public const string TextOk = "text-ok"; + /// + /// cancel文本 + /// + public const string TextCancel = "text-cancel"; + /// + /// create文本 + /// + public const string TextCreate = "text-create"; + /// + /// update文本 + /// + public const string TextUpdate = "text-update"; + /// + /// delete文本 + /// + public const string TextDelete = "text-delete"; + /// + /// detail文本 + /// + public const string TextDetail = "text-detail"; + /// + /// query文本 + /// + public const string TextQuery = "text-query"; + /// + /// refresh文本 + /// + public const string TextRefresh = "text-refresh"; + /// + /// save文本 + /// + public const string TextSave = "text-save"; + /// + /// enabled文本 + /// + public const string TextEnabled = "text-enabled"; + /// + /// not-enabled文本 + /// + public const string TextNotEnabled = "text-not-enabled"; + /// + /// enable文本 + /// + public const string TextEnable = "text-enable"; + /// + /// disable文本 + /// + public const string TextDisable = "text-disable"; + /// + /// select-all文本 + /// + public const string TextSelectAll = "text-select-all"; + /// + /// deselect-all文本 + /// + public const string TextDeselectAll = "text-deselect-all"; + /// + /// publish文本 + /// + public const string TextPublish = "text-publish"; + /// + /// run文本 + /// + public const string TextRun = "text-run"; + /// + /// start文本 + /// + public const string TextStart = "text-start"; + /// + /// stop文本 + /// + public const string TextStop = "text-stop"; + /// + /// add文本 + /// + public const string TextAdd = "text-add"; + /// + /// remove文本 + /// + public const string TextRemove = "text-remove"; + /// + /// open文本 + /// + public const string TextOpen = "text-open"; + /// + /// close文本 + /// + public const string TextClose = "text-close"; + /// + /// send文本 + /// + public const string TextSend = "text-send"; + /// + /// clear文本 + /// + public const string TextClear = "text-clear"; + /// + /// import文本 + /// + public const string TextImport = "text-import"; + /// + /// export文本 + /// + public const string TextExport = "text-export"; + /// + /// reset文本 + /// + public const string TextReset = "text-reset"; + /// + /// unedit文本 + /// + public const string TextUnedit = "text-unedit"; + /// + /// 加载标识列表 + /// + public const string LoadKeys = "load-keys"; + /// + /// 配置 + /// + public const string Config = "config"; + /// + /// 品牌 + /// + public const string Branding = "branding"; + /// + /// 是否提交 + /// + public const string IsSubmit = "is-submit"; + /// + /// 是否搜索 + /// + public const string IsSearch = "is-search"; + /// + /// 显示表格设置 + /// + public const string ShowTableSettings = "show-table-settings"; + /// + /// 显示数量 + /// + public const string ShowNumber = "show-number"; + /// + /// 列数 + /// + public const string ColumnsNumber = "columns-number"; + /// + /// ok + /// + public const string Ok = "ok"; + /// + /// 取消 + /// + public const string Cancel = "cancel"; + /// + /// 内容 + /// + public const string Content = "content"; + /// + /// 首页 + /// + public const string Home = "home"; + /// + /// 首页链接 + /// + public const string HomeLink = "home-link"; + /// + /// 首页国际化 + /// + public const string HomeI18n = "home-i18n"; + /// + /// 面包屑 + /// + public const string Breadcrumb = "breadcrumb"; + /// + /// 自动面包屑 + /// + public const string AutoBreadcrumb = "auto-breadcrumb"; + /// + /// 递归面包屑 + /// + public const string RecursiveBreadcrumb = "recursive-breadcrumb"; + /// + /// 定宽 + /// + public const string Wide = "wide"; + /// + /// 固定 + /// + public const string Fixed = "fixed"; + /// + /// 固定偏移顶部 + /// + public const string FixedOffsetTop = "fixed-offset-top"; + /// + /// 加载模式 + /// + public const string LoadMode = "load-mode"; + /// + /// Logo + /// + public const string Logo = "logo"; + /// + /// 标签 + /// + public const string Tab = "tab"; + /// + /// 禁用 + /// + public const string Disabled = "disabled"; + /// + /// 禁用透明度 + /// + public const string DisabledAlpha = "disabled-alpha"; + /// + /// 禁用日期 + /// + public const string DisabledDate = "disabled-date"; + /// + /// 只读 + /// + public const string Readonly = "readonly"; + /// + /// 只读 + /// + public const string InputReadonly = "input-readonly"; + /// + /// 单击事件 + /// + public const string OnClick = "on-click"; + /// + /// 变化事件 + /// + public const string OnChange = "on-change"; + /// + /// 刷新事件 + /// + public const string OnRefresh = "on-refresh"; + /// + /// 获得焦点事件 + /// + public const string OnFocus = "on-focus"; + /// + /// 失去焦点事件 + /// + public const string OnBlur = "on-blur"; + /// + /// 复制事件 + /// + public const string OnCopy = "on-copy"; + /// + /// 键盘按键事件 + /// + public const string OnKeyup = "on-keyup"; + /// + /// 键盘按下事件 + /// + public const string OnKeydown = "on-keydown"; + /// + /// 提交事件 + /// + public const string OnSubmit = "on-submit"; + /// + /// 打开事件 + /// + public const string OnOpen = "on-open"; + /// + /// 关闭事件 + /// + public const string OnClose = "on-close"; + /// + /// 搜索事件 + /// + public const string OnSearch = "on-search"; + /// + /// 滚动到底部事件 + /// + public const string OnScrollToBottom = "on-scroll-to-bottom"; + /// + /// 单击选中行 + /// + public const string SelectOnClick = "select-on-click"; + /// + /// 单击仅选中行 + /// + public const string SelectOnlyOnClick = "select-only-on-click"; + /// + /// 单击选中行 + /// + public const string SelectOnClickRow = "select-on-click-row"; + /// + /// 单击仅选中行 + /// + public const string SelectOnlyOnClickRow = "select-only-on-click-row"; + /// + /// 图标 + /// + public const string Icon = "icon"; + /// + /// 图标尺寸 + /// + public const string IconSize = "icon-size"; + /// + /// 复制到剪贴板 + /// + public const string CopyToClipboard = "copy-to-clipboard"; + /// + /// 字体 + /// + public const string Font = "font"; + /// + /// 字体颜色 + /// + public const string FontColor = "font-color"; + /// + /// 字体颜色 + /// + public const string FontColorType = "font-color-type"; + /// + /// 字体大小 + /// + public const string FontSize = "font-size"; + /// + /// 字体粗细 + /// + public const string FontWeight = "font-weight"; + /// + /// 字体类型 + /// + public const string FontFamily = "font-family"; + /// + /// 字体样式 + /// + public const string FontStyle = "font-style"; + /// + /// Font Awesome图标 + /// + public const string FontAwesomeIcon = "font-awesome-icon"; + /// + /// Material图标 + /// + public const string MaterialIcon = "material-icon"; + /// + /// 大小 + /// + public const string Size = "size"; + /// + /// 持续旋转 + /// + public const string Spin = "spin"; + /// + /// 旋转 + /// + public const string Rotate = "rotate"; + /// + /// 子组件 + /// + public const string Child = "child"; + /// + /// 子组件class + /// + public const string ChildClass = "child-class"; + /// + /// 搜索关键字 + /// + public const string SearchKeyword = "search-keyword"; + /// + /// 搜索延迟 + /// + public const string SearchDelay = "search-delay"; + /// + /// Url + /// + public const string Url = "url"; + /// + /// 保存Url + /// + public const string SaveUrl = "save-url"; + /// + /// 数据源 + /// + public const string Datasource = "datasource"; + /// + /// 占位提示 + /// + public const string Placeholder = "placeholder"; + /// + /// 多选 + /// + public const string Multiple = "multiple"; + /// + /// 最大多选数量 + /// + public const string MaxMultipleCount = "max-multiple-count"; + /// + /// 模型绑定 + /// + public const string Model = "model"; + /// + /// 必填项 + /// + public const string Required = "required"; + /// + /// 必填项验证消息 + /// + public const string RequiredMessage = "required-message"; + /// + /// 最小长度 + /// + public const string MinLength = "min-length"; + /// + /// 最小长度验证消息 + /// + public const string MinLengthMessage = "min-length-message"; + /// + /// 长度 + /// + public const string Length = "length"; + /// + /// 最大长度 + /// + public const string MaxLength = "max-length"; + /// + /// 最大长度验证消息 + /// + public const string MaxLengthMessage = "max-length-message"; + /// + /// 电子邮件错误消息 + /// + public const string EmailMessage = "email-message"; + /// + /// 模板 + /// + public const string Template = "template"; + /// + /// 前缀 + /// + public const string Prefix = "prefix"; + /// + /// 宽度 + /// + public const string Width = "width"; + /// + /// 高度 + /// + public const string Height = "height"; + /// + /// 提示 + /// + public const string Tooltip = "tooltip"; + /// + /// 全宽识别 + /// + public const string FullWidthRecognition = "full-width-recognition"; + /// + /// 链接 + /// + public const string Link = "link"; + /// + /// 标签 + /// + public const string Label = "label"; + /// + /// 标签宽度 + /// + public const string LabelWidth = "label-width"; + /// + /// 标签文本 + /// + public const string LabelText = "label-text"; + /// + /// 位置 + /// + public const string Position = "position"; + /// + /// 不确定 + /// + public const string Indeterminate = "indeterminate"; + /// + /// 垂直 + /// + public const string Vertical = "vertical"; + /// + /// for + /// + public const string For = "for"; + /// + /// X轴位置 + /// + public const string XPosition = "x-position"; + /// + /// Y轴位置 + /// + public const string YPosition = "y-position"; + /// + /// 重叠 + /// + public const string Overlap = "overlap"; + /// + /// 拉伸 + /// + public const string Stretch = "stretch"; + /// + /// 选中索引 + /// + public const string SelectedIndex = "selected-index"; + /// + /// 延迟加载 + /// + public const string LazyLoad = "lazy-load"; + /// + /// 全屏 + /// + public const string Fullscreen = "fullscreen"; + /// + /// 全屏包装 + /// + public const string FullscreenPack = "fullscreen-pack"; + /// + /// 全屏标题 + /// + public const string FullscreenTitle = "fullscreen-title"; + /// + /// 打开 + /// + public const string Opened = "opened"; + /// + /// 模式 + /// + public const string Mode = "mode"; + /// + /// 自动调整大小 + /// + public const string AutoSize = "auto-size"; + /// + /// 对齐 + /// + public const string Align = "align"; + /// + /// 源路径 + /// + public const string Src = "src"; + /// + /// 展开 + /// + public const string Expanded = "expanded"; + /// + /// 枚举内容 + /// + public const string EnumContent = "enum-content"; + /// + /// 列 + /// + public const string Column = "column"; + /// + /// 列 + /// + public const string XsColumn = "xs-column"; + /// + /// 列 + /// + public const string SmColumn = "sm-column"; + /// + /// 列 + /// + public const string MdColumn = "md-column"; + /// + /// 列 + /// + public const string LgColumn = "lg-column"; + /// + /// 列 + /// + public const string XlColumn = "xl-column"; + /// + /// 列 + /// + public const string XxlColumn = "xxl-column"; + /// + /// 列 + /// + public const string Columns = "columns"; + /// + /// 行高 + /// + public const string RowHeight = "row-height"; + /// + /// 合并列 + /// + public const string Colspan = "colspan"; + /// + /// 合并行 + /// + public const string Rowspan = "rowspan"; + /// + /// 左边合并列 + /// + public const string BeforeColspan = "before-colspan"; + /// + /// 右边合并列 + /// + public const string AfterColspan = "after-colspan"; + /// + /// 紧凑 + /// + public const string Dense = "dense"; + /// + /// 值 + /// + public const string Value = "value"; + /// + /// 选中 + /// + public const string Selected = "selected"; + /// + /// 选中 + /// + public const string Selection = "selection"; + /// + /// 标题 + /// + public const string Title = "title"; + /// + /// 标题对齐 + /// + public const string TitleAlign = "title-align"; + /// + /// 错误收集 + /// + public const string ErrorCollect = "error-collect"; + /// + /// operation标题 + /// + public const string TitleOperation = "title-operation"; + /// + /// 创建标题 + /// + public const string TitleCreate = "title-create"; + /// + /// 修改标题 + /// + public const string TitleUpdate = "title-update"; + /// + /// 副标题 + /// + public const string Subtitle = "subtitle"; + /// + /// 自动设置标题 + /// + public const string AutoTitle = "auto-title"; + /// + /// 同步标题 + /// + public const string SyncTitle = "sync-title"; + /// + /// 查询参数 + /// + public const string QueryParam = "query-param"; + /// + /// 查询参数 + /// + public const string QueryParams = "query-params"; + /// + /// 查询参数处理方式 + /// + public const string QueryParamsHandling = "query-params-handling"; + /// + /// 删除地址 + /// + public const string DeleteUrl = "delete-url"; + /// + /// 加载地址 + /// + public const string LoadUrl = "load-url"; + /// + /// 查询地址 + /// + public const string QueryUrl = "query-url"; + /// + /// 加载子节点地址 + /// + public const string LoadChildrenUrl = "load-children-url"; + /// + /// 子节点加载前事件 + /// + public const string OnLoadChildrenBefore = "on-load-children-before"; + /// + /// 子节点加载完成事件 + /// + public const string OnLoadChildren = "on-load-children"; + /// + /// 排序 + /// + public const string Sort = "sort"; + /// + /// 排序方向 + /// + public const string SortDirection = "sort-direction"; + /// + /// 最大高度 + /// + public const string MaxHeight = "max-height"; + /// + /// 最小高度 + /// + public const string MinHeight = "min-height"; + /// + /// 自动加载 + /// + public const string AutoLoad = "auto-load"; + /// + /// 分页长度列表 + /// + public const string PageSizeOptions = "page-size-options"; + /// + /// 方向 + /// + public const string Direction = "direction"; + /// + /// X轴水平对齐方式 + /// + public const string XAlign = "x-align"; + /// + /// Y轴垂直对齐方式 + /// + public const string YAlign = "y-align"; + /// + /// 间隙 + /// + public const string Gap = "gap"; + /// + /// 弹性 + /// + public const string Flex = "flex"; + /// + /// 排序 + /// + public const string Order = "order"; + /// + /// 偏移 + /// + public const string Offset = "offset"; + /// + /// 填充 + /// + public const string Fill = "fill"; + /// + /// 换行 + /// + public const string Wrap = "wrap"; + /// + /// 格式化 + /// + public const string Format = "format"; + /// + /// 日期格式化 + /// + public const string DateFormat = "date-format"; + /// + /// if + /// + public const string If = "if"; + /// + /// 还原查询参数 + /// + public const string OnQueryRestore = "on-query-restore"; + /// + /// 显示复选框 + /// + public const string ShowCheckbox = "show-checkbox"; + /// + /// 显示单选框 + /// + public const string ShowRadio = "show-radio"; + /// + /// 复选框 + /// + public const string Checkbox = "checkbox"; + /// + /// 固定复选框到左侧 + /// + public const string CheckboxLeft = "checkbox-left"; + /// + /// 单选框 + /// + public const string Radio = "radio"; + /// + /// 固定单选框到左侧 + /// + public const string RadioLeft = "radio-left"; + /// + /// 选中 + /// + public const string Checked = "checked"; + /// + /// 选择模式 + /// + public const string SelectionMode = "selection-mode"; + /// + /// 等待文本 + /// + public const string WaitingText = "waiting-text"; + /// + /// 等待图标 + /// + public const string WaitingIcon = "waiting-icon"; + /// + /// 内联 + /// + public const string Inline = "inline"; + /// + /// 独立 + /// + public const string Standalone = "standalone"; + /// + /// 上传地址 + /// + public const string UploadUrl = "upload-url"; + /// + /// 标识 + /// + public const string Key = "key"; + /// + /// 加载事件 + /// + public const string OnLoad = "on-load"; + /// + /// 选中事件 + /// + public const string OnCheck = "on-check"; + /// + /// 单击行事件 + /// + public const string OnClickRow = "on-click-row"; + /// + /// 单击行时选中 + /// + public const string CheckOnClickRow = "check-on-click-row"; + /// + /// 活动 + /// + public const string Active = "active"; + /// + /// 禁用过滤 + /// + public const string DisableFilter = "disable-filter"; + /// + /// 精确匹配 + /// + public const string Exact = "exact"; + /// + /// 冻结头部 + /// + public const string StickyHeader = "sticky-header"; + /// + /// 正则表达式 + /// + public const string Regex = "regex"; + /// + /// 模式 + /// + public const string Pattern = "pattern"; + /// + /// 模式错误消息 + /// + public const string PatternMessage = "pattern-message"; + /// + /// 最小值 + /// + public const string Min = "min"; + /// + /// 最小值错误消息 + /// + public const string MinMessage = "min-message"; + /// + /// 最大值 + /// + public const string Max = "max"; + /// + /// 最大值错误消息 + /// + public const string MaxMessage = "max-message"; + /// + /// 默认项文本 + /// + public const string DefaultOptionText = "default-option-text"; + /// + /// 是否显示默认项 + /// + public const string ShowDefaultOption = "show-default-option"; + /// + /// 最小行数 + /// + public const string MinRows = "min-rows"; + /// + /// 最大行数 + /// + public const string MaxRows = "max-rows"; + /// + /// 主题 + /// + public const string Theme = "theme"; + /// + /// 形状 + /// + public const string Shape = "shape"; + /// + /// 头像形状 + /// + public const string AvatarShape = "avatar-shape"; + /// + /// 按钮形状 + /// + public const string ButtonShape = "button-shape"; + /// + /// 加载状态 + /// + public const string Loading = "loading"; + /// + /// 块 + /// + public const string Block = "block"; + /// + /// 幽灵 + /// + public const string Ghost = "ghost"; + /// + /// 排序变化事件 + /// + public const string OnSortChange = "on-sort-change"; + /// + /// 前端分页 + /// + public const string FrontPage = "front-page"; + /// + /// 是否显示大小选择 + /// + public const string ShowSizeChanger = "show-size-changer"; + /// + /// 分页大小变化事件 + /// + public const string OnPageSizeChange = "on-page-size-change"; + /// + /// 页索引变化事件 + /// + public const string OnPageIndexChange = "on-page-index-change"; + /// + /// 截断 + /// + public const string Truncate = "truncate"; + /// + /// 按钮文本 + /// + public const string ButtonText = "button-text"; + /// + /// 按钮图标 + /// + public const string ButtonIcon = "button-icon"; + /// + /// 验证表单 + /// + public const string ValidateForm = "validate-form"; + /// + /// 接受 + /// + public const string Accept = "accept"; + /// + /// 文件类型 + /// + public const string FileType = "file-type"; + /// + /// 接受图片 + /// + public const string AcceptImage = "accept-image"; + /// + /// 接受文档 + /// + public const string AcceptDocument = "accept-document"; + /// + /// 图片类型 + /// + public const string ImageTypes = "image-types"; + /// + /// 文档类型 + /// + public const string DocumentTypes = "document-types"; + /// + /// 目录 + /// + public const string Directory = "directory"; + /// + /// 数据 + /// + public const string Data = "data"; + /// + /// 上传前操作 + /// + public const string BeforeUpload = "before-upload"; + /// + /// 上传前事件 + /// + public const string OnBeforeUpload = "on-before-upload"; + /// + /// 过滤器 + /// + public const string Filter = "filter"; + /// + /// 限制 + /// + public const string Limit = "limit"; + /// + /// 总量限制 + /// + public const string TotalLimit = "total-limit"; + /// + /// 文件列表 + /// + public const string FileList = "file-list"; + /// + /// 显示时间 + /// + public const string ShowTime = "show-time"; + /// + /// 显示今天 + /// + public const string ShowToday = "show-today"; + /// + /// 节点占一行 + /// + public const string BlockNode = "block-node"; + /// + /// 显示展开 + /// + public const string ShowExpand = "show-expand"; + /// + /// 显示线 + /// + public const string ShowLine = "show-line"; + /// + /// 显示序号 + /// + public const string ShowLineNumber = "show-line-number"; + /// + /// 固定序号到左侧 + /// + public const string LineNumberLeft = "line-number-left"; + /// + /// 显示标签 + /// + public const string ShowLabel = "show-label"; + /// + /// 展开所有 + /// + public const string ExpandAll = "expand-all"; + /// + /// 根节点异步展开 + /// + public const string ExpandForRootAsync = "expand-for-root-async"; + /// + /// 显示图标 + /// + public const string ShowIcon = "show-icon"; + /// + /// 双击事件 + /// + public const string OnDblClick = "on-dbl-click"; + /// + /// 展开事件 + /// + public const string OnExpand = "on-expand"; + /// + /// 折叠事件 + /// + public const string OnCollapse = "on-collapse"; + /// + /// 显示按钮 + /// + public const string ShowButton = "show-button"; + /// + /// 显示边框 + /// + public const string ShowBorder = "show-border"; + /// + /// 加载后事件 + /// + public const string OnLoadAfter = "on-load-after"; + /// + /// 显示跳转 + /// + public const string ShowJumper = "show-jumper"; + /// + /// 显示总量 + /// + public const string ShowTotal = "show-total"; + /// + /// 总量模板 + /// + public const string TotalTemplate = "total-template"; + /// + /// 显示清除 + /// + public const string ShowClear = "show-clear"; + /// + /// 显示搜索 + /// + public const string ShowSearch = "show-search"; + /// + /// 显示箭头 + /// + public const string ShowArrow = "show-arrow"; + /// + /// 自动获取焦点 + /// + public const string AutoFocus = "auto-focus"; + /// + /// 日期渲染器 + /// + public const string DateRender = "date-render"; + /// + /// 本地化 + /// + public const string Locale = "locale"; + /// + /// 是否打开 + /// + public const string IsOpen = "is-open"; + /// + /// 打开 + /// + public const string Open = "open"; + /// + /// 打开变化事件 + /// + public const string OnOpenChange = "on-open-change"; + /// + /// 禁用今天之前的日期 + /// + public const string DisabledBeforeToday = "disabled-before-today"; + /// + /// 禁用明天之前的日期 + /// + public const string DisabledBeforeTomorrow = "disabled-before-tomorrow"; + /// + /// 显示分页 + /// + public const string ShowPagination = "show-pagination"; + /// + /// 滚动高度 + /// + public const string ScrollHeight = "scroll-height"; + /// + /// 滚动宽度 + /// + public const string ScrollWidth = "scroll-width"; + /// + /// 服务端搜索 + /// + public const string ServerSearch = "server-search"; + /// + /// 滚动加载 + /// + public const string ScrollLoad = "scroll-load"; + /// + /// 精度 + /// + public const string Precision = "precision"; + /// + /// 步 + /// + public const string Step = "step"; + /// + /// 只能选择叶节点 + /// + public const string CheckLeafOnly = "check-leaf-only"; + /// + /// 列表类型 + /// + public const string ListType = "list-type"; + /// + /// 自定义请求 + /// + public const string CustomRequest = "custom-request"; + /// + /// 头部 + /// + public const string Headers = "headers"; + /// + /// 显示上传列表 + /// + public const string ShowUploadList = "show-upload-list"; + /// + /// 携带凭据 + /// + public const string WithCredentials = "with-credentials"; + /// + /// 预览 + /// + public const string Preview = "preview"; + /// + /// 移除 + /// + public const string Remove = "remove"; + /// + /// 是否虚线 + /// + public const string Dashed = "dashed"; + /// + /// 方向 + /// + public const string Orientation = "orientation"; + /// + /// 跨度 + /// + public const string Span = "span"; + /// + /// 布局 + /// + public const string Layout = "layout"; + /// + /// 自定义列 + /// + public const string CustomColumn = "custom-column"; + /// + /// 显示冒号 + /// + public const string ShowColon = "show-colon"; + /// + /// 不显示冒号 + /// + public const string NoColon = "no-colon"; + /// + /// 自动设置标签for属性 + /// + public const string AutoNzFor = "auto-nz-for"; + /// + /// 间隔 + /// + public const string Gutter = "gutter"; + /// + /// 是否浮动 + /// + public const string IsFlex = "is-flex"; + /// + /// 水平排列 + /// + public const string Justify = "justify"; + /// + /// 消息 + /// + public const string Message = "message"; + /// + /// 操作 + /// + public const string Actions = "actions"; + /// + /// 复选框变化事件 + /// + public const string OnCheckboxChange = "on-checkbox-change"; + /// + /// 允许选中 + /// + public const string Selectable = "selectable"; + /// + /// 内嵌折叠 + /// + public const string InlineCollapsed = "inline-collapsed"; + /// + /// 内嵌缩进 + /// + public const string InlineIndent = "inline-indent"; + /// + /// 勾选的键列表 + /// + public const string CheckedKeys = "checked-keys"; + /// + /// 展开的键列表 + /// + public const string ExpandedKeys = "expanded-keys"; + /// + /// 选中的键列表 + /// + public const string SelectedKeys = "selected-keys"; + /// + /// 是否按钮 + /// + public const string IsButton = "is-button"; + /// + /// 选中索引变化事件 + /// + public const string OnSelectedIndexChange = "on-selected-index-change"; + /// + /// 延迟加载 + /// + public const string Lazy = "lazy"; + /// + /// 双击启动编辑 + /// + public const string DoubleClickStartEdit = "double-click-start-edit"; + /// + /// 是否编辑 + /// + public const string IsEdit = "is-edit"; + /// + /// 是否批量编辑 + /// + public const string IsBatchEdit = "is-batch-edit"; + /// + /// 标签 + /// + public const string Tags = "tags"; + /// + /// 颜色类型 + /// + public const string ColorType = "color-type"; + /// + /// 关闭后事件 + /// + public const string OnAfterClose = "on-after-close"; + /// + /// 选中变化事件 + /// + public const string OnCheckedChange = "on-checked-change"; + /// + /// 按钮样式 + /// + public const string ButtonStyle = "button-style"; + /// + /// 表单项浮动 + /// + public const string FormItemFlex = "form-item-flex"; + /// + /// 拉 + /// + public const string Pull = "pull"; + /// + /// 推 + /// + public const string Push = "push"; + /// + /// 列跨度 + /// + public const string ColumnSpan = "column-span"; + /// + /// 启用自动创建 + /// + public const string EnableAutoCreate = "enable-auto-create"; + /// + /// 启用扩展 + /// + public const string EnableExtend = "enable-extend"; + /// + /// 选中行背景色 + /// + public const string SelectedRowBackgroundColor = "selected-row-background-color"; + /// + /// 全部选中变化事件 + /// + public const string OnAllSelectedChange = "on-all-selected-change"; + /// + /// 危险 + /// + public const string Danger = "danger"; + /// + /// 子标签 + /// + public const string ChildTag = "child-tag"; + /// + /// 可编辑 + /// + public const string Editable = "editable"; + /// + /// 编辑图标 + /// + public const string EditIcon = "edit-icon"; + /// + /// 编辑提示 + /// + public const string EditTooltip = "edit-tooltip"; + /// + /// 可复制 + /// + public const string Copyable = "copyable"; + /// + /// 复制文本 + /// + public const string CopyText = "copy-text"; + /// + /// 复制图标 + /// + public const string CopyIcon = "copy-icon"; + /// + /// 复制图标列表 + /// + public const string CopyIcons = "copy-icons"; + /// + /// 复制提示 + /// + public const string CopyTooltips = "copy-tooltips"; + /// + /// 省略 + /// + public const string Ellipsis = "ellipsis"; + /// + /// 可展开 + /// + public const string Expandable = "expandable"; + /// + /// 省略行数 + /// + public const string EllipsisRows = "ellipsis-rows"; + /// + /// 内容更改事件 + /// + public const string OnContentChange = "on-content-change"; + /// + /// 展开更改事件 + /// + public const string OnExpandChange = "on-expand-change"; + /// + /// 省略事件 + /// + public const string OnEllipsis = "on-ellipsis"; + /// + /// 后缀 + /// + public const string Suffix = "suffix"; + /// + /// 平淡 + /// + public const string Plain = "plain"; + /// + /// 超窄尺寸 + /// + public const string Xs = "xs"; + /// + /// 超窄尺寸跨度 + /// + public const string XsSpan = "xs-span"; + /// + /// 超窄尺寸偏移量 + /// + public const string XsOffset = "xs-offset"; + /// + /// 超窄尺寸顺序 + /// + public const string XsOrder = "xs-order"; + /// + /// 超窄尺寸左移 + /// + public const string XsPull = "xs-pull"; + /// + /// 超窄尺寸右移 + /// + public const string XsPush = "xs-push"; + /// + /// 窄尺寸 + /// + public const string Sm = "sm"; + /// + /// 窄尺寸跨度 + /// + public const string SmSpan = "sm-span"; + /// + /// 窄尺寸偏移量 + /// + public const string SmOffset = "sm-offset"; + /// + /// 窄尺寸顺序 + /// + public const string SmOrder = "sm-order"; + /// + /// 窄尺寸左移 + /// + public const string SmPull = "sm-pull"; + /// + /// 窄尺寸右移 + /// + public const string SmPush = "sm-push"; + /// + /// 中尺寸 + /// + public const string Md = "md"; + /// + /// 中尺寸跨度 + /// + public const string MdSpan = "md-span"; + /// + /// 中尺寸偏移量 + /// + public const string MdOffset = "md-offset"; + /// + /// 中尺寸顺序 + /// + public const string MdOrder = "md-order"; + /// + /// 中尺寸左移 + /// + public const string MdPull = "md-pull"; + /// + /// 中尺寸右移 + /// + public const string MdPush = "md-push"; + /// + /// 宽尺寸 + /// + public const string Lg = "lg"; + /// + /// 宽尺寸跨度 + /// + public const string LgSpan = "lg-span"; + /// + /// 宽尺寸偏移量 + /// + public const string LgOffset = "lg-offset"; + /// + /// 宽尺寸顺序 + /// + public const string LgOrder = "lg-order"; + /// + /// 宽尺寸左移 + /// + public const string LgPull = "lg-pull"; + /// + /// 宽尺寸右移 + /// + public const string LgPush = "lg-push"; + /// + /// 超宽尺寸 + /// + public const string Xl = "xl"; + /// + /// 超宽尺寸跨度 + /// + public const string XlSpan = "xl-span"; + /// + /// 超宽尺寸偏移量 + /// + public const string XlOffset = "xl-offset"; + /// + /// 超宽尺寸顺序 + /// + public const string XlOrder = "xl-order"; + /// + /// 超宽尺寸左移 + /// + public const string XlPull = "xl-pull"; + /// + /// 超宽尺寸右移 + /// + public const string XlPush = "xl-push"; + /// + /// 极宽尺寸 + /// + public const string Xxl = "xxl"; + /// + /// 极宽尺寸跨度 + /// + public const string XxlSpan = "xxl-span"; + /// + /// 极宽尺寸偏移量 + /// + public const string XxlOffset = "xxl-offset"; + /// + /// 极宽尺寸顺序 + /// + public const string XxlOrder = "xxl-order"; + /// + /// 极宽尺寸左移 + /// + public const string XxlPull = "xxl-pull"; + /// + /// 极宽尺寸右移 + /// + public const string XxlPush = "xxl-push"; + /// + /// 断点 + /// + public const string Breakpoint = "breakpoint"; + /// + /// 收缩宽度 + /// + public const string CollapsedWidth = "collapsed-width"; + /// + /// 可收缩 + /// + public const string Collapsible = "collapsible"; + /// + /// 收缩 + /// + public const string Collapsed = "collapsed"; + /// + /// 翻转箭头 + /// + public const string ReverseArrow = "reverse-arrow"; + /// + /// 触发器 + /// + public const string Trigger = "trigger"; + /// + /// 展开事件 + /// + public const string OnCollapsedChange = "on-collapsed-change"; + /// + /// 顶部偏移量 + /// + public const string OffsetTop = "offset-top"; + /// + /// 底部偏移量 + /// + public const string OffsetBottom = "offset-bottom"; + /// + /// 目标 + /// + public const string Target = "target"; + /// + /// 分隔符 + /// + public const string Separator = "separator"; + /// + /// 自动生成 + /// + public const string AutoGenerate = "auto-generate"; + /// + /// 弹出层 + /// + public const string Overlay = "overlay"; + /// + /// 下拉菜单 + /// + public const string DropdownMenu = "dropdown-menu"; + /// + /// 不创建ul + /// + public const string NotCreateUl = "not-create-ul"; + /// + /// 显示状态改变事件 + /// + public const string OnVisibleChange = "on-visible-change"; + /// + /// 位置 + /// + public const string Placement = "placement"; + /// + /// 下拉菜单位置 + /// + public const string DropdownMenuPlacement = "dropdown-menu-placement"; + /// + /// 下拉菜单触发方式 + /// + public const string DropdownMenuTrigger = "dropdown-menu-trigger"; + /// + /// 点击隐藏下拉菜单 + /// + public const string DropdownMenuClickHide = "dropdown-menu-click-hide"; + /// + /// 下拉菜单可见性 + /// + public const string DropdownMenuVisible = "dropdown-menu-visible"; + /// + /// 下拉菜单根元素类名 + /// + public const string DropdownMenuOverlayClassName = "dropdown-menu-overlay-class-name"; + /// + /// 下拉菜单根元素样式 + /// + public const string DropdownMenuOverlayStyle = "dropdown-menu-overlay-style"; + /// + /// href + /// + public const string Href = "href"; + /// + /// rel + /// + public const string Rel = "rel"; + /// + /// 匹配路由 + /// + public const string MatchRouter = "match-router"; + /// + /// 精确匹配路由 + /// + public const string MatchRouterExact = "match-router-exact"; + /// + /// 菜单类名 + /// + public const string MenuClassName = "menu-class-name"; + /// + /// 显示返回 + /// + public const string ShowBack = "show-back"; + /// + /// 返回图标 + /// + public const string BackIcon = "back-icon"; + /// + /// 返回事件 + /// + public const string OnBack = "on-back"; + /// + /// 总数 + /// + public const string Total = "total"; + /// + /// 当前页 + /// + public const string PageIndex = "page-index"; + /// + /// 每页大小 + /// + public const string PageSize = "page-size"; + /// + /// 显示快速跳转 + /// + public const string ShowQuickJumper = "show-quick-jumper"; + /// + /// 简单 + /// + public const string Simple = "simple"; + /// + /// 响应式 + /// + public const string Responsive = "responsive"; + /// + /// 项渲染 + /// + public const string ItemRender = "item-render"; + /// + /// 一页隐藏 + /// + public const string HideOnSinglePage = "hide-on-single-page"; + /// + /// 当前 + /// + public const string Current = "current"; + /// + /// 描述 + /// + public const string Description = "description"; + /// + /// 起始索引 + /// + public const string StartIndex = "start-index"; + /// + /// 状态 + /// + public const string Status = "status"; + /// + /// 点状进度 + /// + public const string ProgressDot = "progress-dot"; + /// + /// 索引变化事件 + /// + public const string OnIndexChange = "on-index-change"; + /// + /// 标签位置 + /// + public const string LabelPlacement = "label-placement"; + /// + /// 前置添加 + /// + public const string AddOnBefore = "add-on-before"; + /// + /// 后置添加 + /// + public const string AddOnAfter = "add-on-after"; + /// + /// 添加前置图标 + /// + public const string AddOnBeforeIcon = "add-on-before-icon"; + /// + /// 添加后置图标 + /// + public const string AddOnAfterIcon = "add-on-after-icon"; + /// + /// 搜索 + /// + public const string Search = "search"; + /// + /// 自适应大小 + /// + public const string Autosize = "autosize"; + /// + /// 允许清除 + /// + public const string AllowClear = "allow-clear"; + /// + /// 自动提示 + /// + public const string AutoTips = "auto-tips"; + /// + /// 禁用自动提示 + /// + public const string DisableAutoTips = "disable-auto-tips"; + /// + /// 提示图标 + /// + public const string TooltipIcon = "tooltip-icon"; + /// + /// 提示信息 + /// + public const string TooltipTitle = "tooltip-title"; + /// + /// update提示信息 + /// + public const string TooltipTitleUpdate = "tooltip-title-update"; + /// + /// delete提示信息 + /// + public const string TooltipTitleDelete = "tooltip-title-delete"; + /// + /// detail提示信息 + /// + public const string TooltipTitleDetail = "tooltip-title-detail"; + /// + /// 校验状态 + /// + public const string ValidateStatus = "validate-status"; + /// + /// 反馈 + /// + public const string HasFeedback = "has-feedback"; + /// + /// 额外 + /// + public const string Extra = "extra"; + /// + /// 成功提示 + /// + public const string SuccessTip = "success-tip"; + /// + /// 警告提示 + /// + public const string WarningTip = "warning-tip"; + /// + /// 错误提示 + /// + public const string ErrorTip = "error-tip"; + /// + /// 校验中提示 + /// + public const string ValidatingTip = "validating-tip"; + /// + /// 表单控件 + /// + public const string FormControl = "form-control"; + /// + /// 表单组 + /// + public const string FormGroup = "form-group"; + /// + /// 前缀图标 + /// + public const string PrefixIcon = "prefix-icon"; + /// + /// 后缀图标 + /// + public const string SuffixIcon = "suffix-icon"; + /// + /// 表单控件名 + /// + public const string FormControlName = "form-control-name"; + /// + /// 模型变化事件 + /// + public const string OnModelChange = "on-model-change"; + /// + /// 回车事件 + /// + public const string OnEnter = "on-enter"; + /// + /// 回车事件 + /// + public const string OnKeyupEnter = "on-keyup-enter"; + /// + /// 回车事件 + /// + public const string OnKeydownEnter = "on-keydown-enter"; + /// + /// 隐藏 + /// + public const string Hidden = "hidden"; + /// + /// 标签跨度 + /// + public const string LabelSpan = "label-span"; + /// + /// 标签偏移量 + /// + public const string LabelOffset = "label-offset"; + /// + /// 标签顺序 + /// + public const string LabelOrder = "label-order"; + /// + /// 标签左移 + /// + public const string LabelPull = "label-pull"; + /// + /// 标签右移 + /// + public const string LabelPush = "label-push"; + /// + /// 标签Flex布局 + /// + public const string LabelFlex = "label-flex"; + /// + /// 标签超窄尺寸 + /// + public const string LabelXs = "label-xs"; + /// + /// 标签超窄尺寸跨度 + /// + public const string LabelXsSpan = "label-xs-span"; + /// + /// 标签超窄尺寸偏移量 + /// + public const string LabelXsOffset = "label-xs-offset"; + /// + /// 标签超窄尺寸顺序 + /// + public const string LabelXsOrder = "label-xs-order"; + /// + /// 标签超窄尺寸左移 + /// + public const string LabelXsPull = "label-xs-pull"; + /// + /// 标签超窄尺寸右移 + /// + public const string LabelXsPush = "label-xs-push"; + /// + /// 标签窄尺寸 + /// + public const string LabelSm = "label-sm"; + /// + /// 标签窄尺寸跨度 + /// + public const string LabelSmSpan = "label-sm-span"; + /// + /// 标签窄尺寸偏移量 + /// + public const string LabelSmOffset = "label-sm-offset"; + /// + /// 标签窄尺寸顺序 + /// + public const string LabelSmOrder = "label-sm-order"; + /// + /// 标签窄尺寸左移 + /// + public const string LabelSmPull = "label-sm-pull"; + /// + /// 标签窄尺寸右移 + /// + public const string LabelSmPush = "label-sm-push"; + /// + /// 标签中尺寸 + /// + public const string LabelMd = "label-md"; + /// + /// 标签中尺寸跨度 + /// + public const string LabelMdSpan = "label-md-span"; + /// + /// 标签中尺寸偏移量 + /// + public const string LabelMdOffset = "label-md-offset"; + /// + /// 标签中尺寸顺序 + /// + public const string LabelMdOrder = "label-md-order"; + /// + /// 标签中尺寸左移 + /// + public const string LabelMdPull = "label-md-pull"; + /// + /// 标签中尺寸右移 + /// + public const string LabelMdPush = "label-md-push"; + /// + /// 标签宽尺寸 + /// + public const string LabelLg = "label-lg"; + /// + /// 标签宽尺寸跨度 + /// + public const string LabelLgSpan = "label-lg-span"; + /// + /// 标签宽尺寸偏移量 + /// + public const string LabelLgOffset = "label-lg-offset"; + /// + /// 标签宽尺寸顺序 + /// + public const string LabelLgOrder = "label-lg-order"; + /// + /// 标签宽尺寸左移 + /// + public const string LabelLgPull = "label-lg-pull"; + /// + /// 标签宽尺寸右移 + /// + public const string LabelLgPush = "label-lg-push"; + /// + /// 标签超宽尺寸 + /// + public const string LabelXl = "label-xl"; + /// + /// 标签超宽尺寸跨度 + /// + public const string LabelXlSpan = "label-xl-span"; + /// + /// 标签超宽尺寸偏移量 + /// + public const string LabelXlOffset = "label-xl-offset"; + /// + /// 标签超宽尺寸顺序 + /// + public const string LabelXlOrder = "label-xl-order"; + /// + /// 标签超宽尺寸左移 + /// + public const string LabelXlPull = "label-xl-pull"; + /// + /// 标签超宽尺寸右移 + /// + public const string LabelXlPush = "label-xl-push"; + /// + /// 标签极宽尺寸 + /// + public const string LabelXxl = "label-xxl"; + /// + /// 标签极宽尺寸跨度 + /// + public const string LabelXxlSpan = "label-xxl-span"; + /// + /// 标签极宽尺寸偏移量 + /// + public const string LabelXxlOffset = "label-xxl-offset"; + /// + /// 标签极宽尺寸顺序 + /// + public const string LabelXxlOrder = "label-xxl-order"; + /// + /// 标签极宽尺寸左移 + /// + public const string LabelXxlPull = "label-xxl-pull"; + /// + /// 标签极宽尺寸右移 + /// + public const string LabelXxlPush = "label-xxl-push"; + /// + /// 控件跨度 + /// + public const string ControlSpan = "control-span"; + /// + /// 控件偏移量 + /// + public const string ControlOffset = "control-offset"; + /// + /// 控件顺序 + /// + public const string ControlOrder = "control-order"; + /// + /// 控件左移 + /// + public const string ControlPull = "control-pull"; + /// + /// 控件右移 + /// + public const string ControlPush = "control-push"; + /// + /// 控件Flex布局 + /// + public const string ControlFlex = "control-flex"; + /// + /// 控件超窄尺寸 + /// + public const string ControlXs = "control-xs"; + /// + /// 控件超窄尺寸跨度 + /// + public const string ControlXsSpan = "control-xs-span"; + /// + /// 控件超窄尺寸偏移量 + /// + public const string ControlXsOffset = "control-xs-offset"; + /// + /// 控件超窄尺寸顺序 + /// + public const string ControlXsOrder = "control-xs-order"; + /// + /// 控件超窄尺寸左移 + /// + public const string ControlXsPull = "control-xs-pull"; + /// + /// 控件超窄尺寸右移 + /// + public const string ControlXsPush = "control-xs-push"; + /// + /// 控件窄尺寸 + /// + public const string ControlSm = "control-sm"; + /// + /// 控件窄尺寸跨度 + /// + public const string ControlSmSpan = "control-sm-span"; + /// + /// 控件窄尺寸偏移量 + /// + public const string ControlSmOffset = "control-sm-offset"; + /// + /// 控件窄尺寸顺序 + /// + public const string ControlSmOrder = "control-sm-order"; + /// + /// 控件窄尺寸左移 + /// + public const string ControlSmPull = "control-sm-pull"; + /// + /// 控件窄尺寸右移 + /// + public const string ControlSmPush = "control-sm-push"; + /// + /// 控件中尺寸 + /// + public const string ControlMd = "control-md"; + /// + /// 控件中尺寸跨度 + /// + public const string ControlMdSpan = "control-md-span"; + /// + /// 控件中尺寸偏移量 + /// + public const string ControlMdOffset = "control-md-offset"; + /// + /// 控件中尺寸顺序 + /// + public const string ControlMdOrder = "control-md-order"; + /// + /// 控件中尺寸左移 + /// + public const string ControlMdPull = "control-md-pull"; + /// + /// 控件中尺寸右移 + /// + public const string ControlMdPush = "control-md-push"; + /// + /// 控件宽尺寸 + /// + public const string ControlLg = "control-lg"; + /// + /// 控件宽尺寸跨度 + /// + public const string ControlLgSpan = "control-lg-span"; + /// + /// 控件宽尺寸偏移量 + /// + public const string ControlLgOffset = "control-lg-offset"; + /// + /// 控件宽尺寸顺序 + /// + public const string ControlLgOrder = "control-lg-order"; + /// + /// 控件宽尺寸左移 + /// + public const string ControlLgPull = "control-lg-pull"; + /// + /// 控件宽尺寸右移 + /// + public const string ControlLgPush = "control-lg-push"; + /// + /// 控件超宽尺寸 + /// + public const string ControlXl = "control-xl"; + /// + /// 控件超宽尺寸跨度 + /// + public const string ControlXlSpan = "control-xl-span"; + /// + /// 控件超宽尺寸偏移量 + /// + public const string ControlXlOffset = "control-xl-offset"; + /// + /// 控件超宽尺寸顺序 + /// + public const string ControlXlOrder = "control-xl-order"; + /// + /// 控件超宽尺寸左移 + /// + public const string ControlXlPull = "control-xl-pull"; + /// + /// 控件超宽尺寸右移 + /// + public const string ControlXlPush = "control-xl-push"; + /// + /// 控件极宽尺寸 + /// + public const string ControlXxl = "control-xxl"; + /// + /// 控件极宽尺寸跨度 + /// + public const string ControlXxlSpan = "control-xxl-span"; + /// + /// 控件极宽尺寸偏移量 + /// + public const string ControlXxlOffset = "control-xxl-offset"; + /// + /// 控件极宽尺寸顺序 + /// + public const string ControlXxlOrder = "control-xxl-order"; + /// + /// 控件极宽尺寸左移 + /// + public const string ControlXxlPull = "control-xxl-pull"; + /// + /// 控件极宽尺寸右移 + /// + public const string ControlXxlPush = "control-xxl-push"; + /// + /// 最大字符数 + /// + public const string MaxCharacterCount = "max-character-count"; + /// + /// 字符数计算函数 + /// + public const string ComputeCharacterCount = "compute-character-count"; + /// + /// 行数 + /// + public const string Rows = "rows"; + /// + /// 紧凑 + /// + public const string Compact = "compact"; + /// + /// 输入事件 + /// + public const string OnInput = "on-input"; + /// + /// 自动完成 + /// + public const string Autocomplete = "autocomplete"; + /// + /// 自动完成搜索关键字 + /// + public const string AutocompleteSearchKeyword = "autocomplete-search-keyword"; + /// + /// 回填 + /// + public const string Backfill = "backfill"; + /// + /// 比较 + /// + public const string CompareWith = "compare-with"; + /// + /// 高亮第一项 + /// + public const string DefaultActiveFirstOption = "default-active-first-option"; + /// + /// 根元素类名 + /// + public const string OverlayClassName = "overlay-class-name"; + /// + /// 根元素样式 + /// + public const string OverlayStyle = "overlay-style"; + /// + /// 变化函数 + /// + public const string ChangeOn = "change-on"; + /// + /// 选择变化 + /// + public const string ChangeOnSelect = "change-on-select"; + /// + /// 列类名 + /// + public const string ColumnClassName = "column-class-name"; + /// + /// 展开图标 + /// + public const string ExpandIcon = "expand-icon"; + /// + /// 展开方式 + /// + public const string ExpandTrigger = "expand-trigger"; + /// + /// 标签属性 + /// + public const string LabelProperty = "label-property"; + /// + /// 标签渲染模板 + /// + public const string LabelRender = "label-render"; + /// + /// 加载数据 + /// + public const string LoadData = "load-data"; + /// + /// 菜单样式 + /// + public const string MenuStyle = "menu-style"; + /// + /// 空内容 + /// + public const string NotFoundContent = "not-found-content"; + /// + /// 选项渲染模板 + /// + public const string OptionRender = "option-render"; + /// + /// 选项 + /// + public const string Options = "options"; + /// + /// 显示输入框 + /// + public const string ShowInput = "show-input"; + /// + /// 值属性 + /// + public const string ValueProperty = "value-property"; + /// + /// 清除事件 + /// + public const string OnClear = "on-clear"; + /// + /// 选中状态改变事件 + /// + public const string OnSelectionChange = "on-selection-change"; + /// + /// 默认选中值 + /// + public const string DefaultPickerValue = "default-picker-value"; + /// + /// 下拉样式类名 + /// + public const string DropdownClassName = "dropdown-class-name"; + /// + /// 弹出样式 + /// + public const string PopupStyle = "popup-style"; + /// + /// 额外页脚 + /// + public const string RenderExtraFooter = "render-extra-footer"; + /// + /// 无边框 + /// + public const string Borderless = "borderless"; + /// + /// 输入框标识 + /// + public const string InputId = "input-id"; + /// + /// 禁用时间 + /// + public const string DisabledTime = "disabled-time"; + /// + /// 显示当前 + /// + public const string ShowNow = "show-now"; + /// + /// 确定事件 + /// + public const string OnOk = "on-ok"; + /// + /// 范围 + /// + public const string Ranges = "ranges"; + /// + /// 日历改变事件 + /// + public const string OnCalendarChange = "on-calendar-change"; + /// + /// 格式化器 + /// + public const string Formatter = "formatter"; + /// + /// 转换器 + /// + public const string Parser = "parser"; + /// + /// 精度模式 + /// + public const string PrecisionMode = "precision-mode"; + /// + /// 输入模式 + /// + public const string InputMode = "input-mode"; + /// + /// 建议 + /// + public const string Suggestions = "suggestions"; + /// + /// 选择事件 + /// + public const string OnSelect = "on-select"; + /// + /// 搜索改变事件 + /// + public const string OnSearchChange = "on-search-change"; + /// + /// 取值 + /// + public const string ValueWith = "value-with"; + /// + /// 提及建议 + /// + public const string MentionSuggestion = "mention-suggestion"; + /// + /// 允许半选 + /// + public const string AllowHalf = "allow-half"; + /// + /// 字符 + /// + public const string Character = "character"; + /// + /// 数量 + /// + public const string Count = "count"; + /// + /// 提示信息 + /// + public const string Tooltips = "tooltips"; + /// + /// 滑动变化事件 + /// + public const string OnHoverChange = "on-hover-change"; + /// + /// 自动清空搜索值 + /// + public const string AutoClearSearchValue = "auto-clear-search-value"; + /// + /// 下拉菜单匹配选择宽度 + /// + public const string DropdownMatchSelectWidth = "dropdown-match-select-width"; + /// + /// 下拉样式 + /// + public const string DropdownStyle = "dropdown-style"; + /// + /// 自定义模板 + /// + public const string CustomTemplate = "custom-template"; + /// + /// 过滤项 + /// + public const string FilterOption = "filter-option"; + /// + /// 移除图标 + /// + public const string RemoveIcon = "remove-icon"; + /// + /// 清空图标 + /// + public const string ClearIcon = "clear-icon"; + /// + /// 清空文件 + /// + public const string ClearFiles = "clear-files"; + /// + /// 选中项图标 + /// + public const string MenuItemSelectedIcon = "menu-item-selected-icon"; + /// + /// 分隔符 + /// + public const string TokenSeparators = "token-separators"; + /// + /// 模型数据转换为上传文件列表的延迟时间 + /// + public const string ModelToFilesDebounceTime = "model-to-files-debounce-time"; + /// + /// 最大标签数量 + /// + public const string MaxTagCount = "max-tag-count"; + /// + /// 最大标签占位符 + /// + public const string MaxTagPlaceholder = "max-tag-placeholder"; + /// + /// 选项高度 + /// + public const string OptionHeightPx = "option-height-px"; + /// + /// 选项溢出数量 + /// + public const string OptionOverflowSize = "option-overflow-size"; + /// + /// 上传完成事件 + /// + public const string OnUploadComplete = "on-upload-complete"; + /// + /// 隐藏 + /// + public const string Hide = "hide"; + /// + /// 自定义内容 + /// + public const string CustomContent = "custom-content"; + /// + /// 点点 + /// + public const string Dots = "dots"; + /// + /// 包含 + /// + public const string Included = "included"; + /// + /// 标记 + /// + public const string Marks = "marks"; + /// + /// 范围 + /// + public const string Range = "range"; + /// + /// 提示格式化 + /// + public const string TipFormatter = "tip-formatter"; + /// + /// 反向 + /// + public const string Reverse = "reverse"; + /// + /// 提示可见性 + /// + public const string TooltipVisible = "tooltip-visible"; + /// + /// 提示位置 + /// + public const string TooltipPlacement = "tooltip-placement"; + /// + /// 变化后事件 + /// + public const string OnAfterChange = "on-after-change"; + /// + /// 选中时内容 + /// + public const string CheckedChildren = "checked-children"; + /// + /// 未选中时内容 + /// + public const string UncheckedChildren = "unchecked-children"; + /// + /// 控制 + /// + public const string Control = "control"; + /// + /// 附加组件 + /// + public const string AddOn = "add-on"; + /// + /// 允许清空 + /// + public const string AllowEmpty = "allow-empty"; + /// + /// 清空文本 + /// + public const string ClearText = "clear-text"; + /// + /// 默认值 + /// + public const string DefaultOpenValue = "default-open-value"; + /// + /// 禁用小时 + /// + public const string DisabledHours = "disabled-hours"; + /// + /// 禁用分 + /// + public const string DisabledMinutes = "disabled-minutes"; + /// + /// 禁用秒 + /// + public const string DisabledSeconds = "disabled-seconds"; + /// + /// 隐藏禁用选项 + /// + public const string HideDisabledOptions = "hide-disabled-options"; + /// + /// 小时间隔 + /// + public const string HourStep = "hour-step"; + /// + /// 分钟间隔 + /// + public const string MinuteStep = "minute-step"; + /// + /// 秒间隔 + /// + public const string SecondStep = "second-step"; + /// + /// 弹出层样式类名 + /// + public const string PopupClassName = "popup-class-name"; + /// + /// 12小时制 + /// + public const string Use12Hours = "use12-hours"; + /// + /// 标题集合 + /// + public const string Titles = "titles"; + /// + /// 操作集合 + /// + public const string Operations = "operations"; + /// + /// 列表样式 + /// + public const string ListStyle = "list-style"; + /// + /// 项目单位 + /// + public const string ItemUnit = "item-unit"; + /// + /// 项目列表单位 + /// + public const string ItemsUnit = "items-unit"; + /// + /// 渲染列表 + /// + public const string RenderList = "render-list"; + /// + /// 渲染 + /// + public const string Render = "render"; + /// + /// 页脚 + /// + public const string Footer = "footer"; + /// + /// 搜索占位提示 + /// + public const string SearchPlaceholder = "search-placeholder"; + /// + /// 可移动 + /// + public const string CanMove = "can-move"; + /// + /// 目标项键列表 + /// + public const string TargetKeys = "target-keys"; + /// + /// 选中状态改变事件 + /// + public const string OnSelectChange = "on-select-change"; + /// + /// 隐藏未匹配 + /// + public const string HideUnmatched = "hide-unmatched"; + /// + /// 可勾选 + /// + public const string Checkable = "checkable"; + /// + /// 严格勾选 + /// + public const string CheckStrictly = "check-strictly"; + /// + /// 异步数据 + /// + public const string AsyncData = "async-data"; + /// + /// 节点列表 + /// + public const string Nodes = "nodes"; + /// + /// 默认展开所有节点 + /// + public const string DefaultExpandAll = "default-expand-all"; + /// + /// 显示 + /// + public const string DisplayWith = "display-with"; + /// + /// 树模板 + /// + public const string TreeTemplate = "tree-template"; + /// + /// 虚拟高度 + /// + public const string VirtualHeight = "virtual-height"; + /// + /// 虚拟项大小 + /// + public const string VirtualItemSize = "virtual-item-size"; + /// + /// 虚拟缓冲区最大高度 + /// + public const string VirtualMaxBufferPx = "virtual-max-buffer-px"; + /// + /// 虚拟缓冲区最小高度 + /// + public const string VirtualMinBufferPx = "virtual-min-buffer-px"; + /// + /// 操作 + /// + public const string Action = "action"; + /// + /// 点击打开文件对话框 + /// + public const string OpenFileDialogOnClick = "open-file-dialog-on-click"; + /// + /// 预览文件 + /// + public const string PreviewFile = "preview-file"; + /// + /// 预览是否图片 + /// + public const string PreviewIsImage = "preview-is-image"; + /// + /// 下载 + /// + public const string Download = "download"; + /// + /// 转换文件 + /// + public const string TransformFile = "transform-file"; + /// + /// 图标渲染模板 + /// + public const string IconRender = "icon-render"; + /// + /// 文件列表渲染模板 + /// + public const string FileListRender = "file-list-render"; + /// + /// 源路径 + /// + public const string SrcSet = "src-set"; + /// + /// alt + /// + public const string Alt = "alt"; + /// + /// 错误事件 + /// + public const string OnError = "on-error"; + /// + /// 点 + /// + public const string Dot = "dot"; + /// + /// 显示点 + /// + public const string ShowDot = "show-dot"; + /// + /// 溢出数量 + /// + public const string OverflowCount = "overflow-count"; + /// + /// 显示0 + /// + public const string ShowZero = "show-zero"; + /// + /// 日期单元格 + /// + public const string DateCell = "date-cell"; + /// + /// 日期单元格 + /// + public const string DateFullCell = "date-full-cell"; + /// + /// 月份单元格 + /// + public const string MonthCell = "month-cell"; + /// + /// 月份单元格 + /// + public const string MonthFullCell = "month-full-cell"; + /// + /// 面板变化事件 + /// + public const string OnPanelChange = "on-panel-change"; + /// + /// 主体样式 + /// + public const string BodyStyle = "body-style"; + /// + /// 封面 + /// + public const string Cover = "cover"; + /// + /// 可悬停 + /// + public const string Hoverable = "hoverable"; + /// + /// 头像 + /// + public const string Avatar = "avatar"; + /// + /// 自动播放 + /// + public const string AutoPlay = "auto-play"; + /// + /// 自动播放速度 + /// + public const string AutoPlaySpeed = "auto-play-speed"; + /// + /// 指示点渲染模板 + /// + public const string DotRender = "dot-render"; + /// + /// 指示点位置 + /// + public const string DotPosition = "dot-position"; + /// + /// 效果 + /// + public const string Effect = "effect"; + /// + /// 启用划动 + /// + public const string EnableSwipe = "enable-swipe"; + /// + /// 变化前事件 + /// + public const string OnBeforeChange = "on-before-change"; + /// + /// 手风琴 + /// + public const string Accordion = "accordion"; + /// + /// 有边框 + /// + public const string Bordered = "bordered"; + /// + /// 扩展图标位置 + /// + public const string ExpandIconPosition = "expand-icon-position"; + /// + /// 头 + /// + public const string Header = "header"; + /// + /// 扩展图标 + /// + public const string ExpandedIcon = "expanded-icon"; + /// + /// 激活变化事件 + /// + public const string OnActiveChange = "on-active-change"; + /// + /// 作者 + /// + public const string Author = "author"; + /// + /// 日期 + /// + public const string Datetime = "datetime"; + /// + /// 冒号 + /// + public const string Colon = "colon"; + /// + /// 未找到图片 + /// + public const string NotFoundImage = "not-found-image"; + /// + /// 未找到页脚 + /// + public const string NotFoundFooter = "not-found-footer"; + /// + /// 回退 + /// + public const string Fallback = "fallback"; + /// + /// 禁止预览 + /// + public const string DisablePreview = "disable-preview"; + /// + /// 导航时关闭 + /// + public const string CloseOnNavigation = "close-on-navigation"; + /// + /// 项布局 + /// + public const string ItemLayout = "item-layout"; + /// + /// 分割 + /// + public const string Split = "split"; + /// + /// 网格 + /// + public const string Grid = "grid"; + /// + /// 无结果 + /// + public const string NoResult = "no-result"; + /// + /// 非flex布局 + /// + public const string NoFlex = "no-flex"; + /// + /// 间距项 + /// + public const string SpaceItem = "space-item"; + /// + /// 气泡框标题 + /// + public const string PopoverTitle = "popover-title"; + /// + /// 气泡框内容 + /// + public const string PopoverContent = "popover-content"; + /// + /// 气泡框触发器 + /// + public const string PopoverTrigger = "popover-trigger"; + /// + /// 气泡框位置 + /// + public const string PopoverPlacement = "popover-placement"; + /// + /// 气泡框源元素 + /// + public const string PopoverOrigin = "popover-origin"; + /// + /// 气泡框可见性 + /// + public const string PopoverVisible = "popover-visible"; + /// + /// 气泡框移入延时 + /// + public const string PopoverMouseEnterDelay = "popover-mouse-enter-delay"; + /// + /// 气泡框移出延时 + /// + public const string PopoverMouseLeaveDelay = "popover-mouse-leave-delay"; + /// + /// 气泡框类名 + /// + public const string PopoverOverlayClassName = "popover-overlay-class-name"; + /// + /// 气泡框样式 + /// + public const string PopoverOverlayStyle = "popover-overlay-style"; + /// + /// 气泡框背景 + /// + public const string PopoverBackdrop = "popover-backdrop"; + /// + /// 气泡框可见性变化事件 + /// + public const string OnPopoverVisibleChange = "on-popover-visible-change"; + /// + /// 值样式 + /// + public const string ValueStyle = "value-style"; + /// + /// 值模板 + /// + public const string ValueTemplate = "value-template"; + /// + /// 倒计时完成事件 + /// + public const string OnCountdownFinish = "on-countdown-finish"; + /// + /// 前端分页 + /// + public const string FrontPagination = "front-pagination"; + /// + /// 分页位置 + /// + public const string PaginationPosition = "pagination-position"; + /// + /// 分页类型 + /// + public const string PaginationType = "pagination-type"; + /// + /// 外边框 + /// + public const string OuterBordered = "outer-bordered"; + /// + /// 宽度配置 + /// + public const string WidthConfig = "width-config"; + /// + /// 加载指示符 + /// + public const string LoadingIndicator = "loading-indicator"; + /// + /// 延迟加载 + /// + public const string LoadingDelay = "loading-delay"; + /// + /// 滚动 + /// + public const string Scroll = "scroll"; + /// + /// 模板模式 + /// + public const string TemplateMode = "template-mode"; + /// + /// 虚拟跟踪 + /// + public const string VirtualForTrackBy = "virtual-for-track-by"; + /// + /// 当前页数据变化事件 + /// + public const string OnCurrentPageDataChange = "on-current-page-data-change"; + /// + /// 查询参数变化事件 + /// + public const string OnQueryParams = "on-query-params"; + /// + /// 展开 + /// + public const string Expand = "expand"; + /// + /// 显示行选择 + /// + public const string ShowRowSelection = "show-row-selection"; + /// + /// 选择 + /// + public const string Selections = "selections"; + /// + /// 显示排序 + /// + public const string ShowSort = "show-sort"; + /// + /// 排序函数 + /// + public const string SortFn = "sort-fn"; + /// + /// 排序方向 + /// + public const string SortDirections = "sort-directions"; + /// + /// 排序方向 + /// + public const string SortOrder = "sort-order"; + /// + /// 排序方向变化事件 + /// + public const string OnSortOrderChange = "on-sort-order-change"; + /// + /// 显示过滤 + /// + public const string ShowFilter = "show-filter"; + /// + /// 过滤函数 + /// + public const string FilterFn = "filter-fn"; + /// + /// 过滤器 + /// + public const string Filters = "filters"; + /// + /// 多选过滤器 + /// + public const string FilterMultiple = "filter-multiple"; + /// + /// 过滤条件变化事件 + /// + public const string OnFilterChange = "on-filter-change"; + /// + /// 左 + /// + public const string Left = "left"; + /// + /// 右 + /// + public const string Right = "right"; + /// + /// 折行 + /// + public const string BreakWord = "break-word"; + /// + /// 列标识 + /// + public const string ColumnKey = "column-key"; + /// + /// 缩进大小 + /// + public const string IndentSize = "indent-size"; + /// + /// 可见 + /// + public const string Visible = "visible"; + /// + /// 有背景 + /// + public const string HasBackdrop = "has-backdrop"; + /// + /// 数据 + /// + public const string LetData = "let-data"; + /// + /// 索引 + /// + public const string LetIndex = "let-index"; + /// + /// 项大小 + /// + public const string ItemSize = "item-size"; + /// + /// 虚拟 + /// + public const string VirtualFor = "virtual-for"; + /// + /// 动画 + /// + public const string Animated = "animated"; + /// + /// 标签栏附加内容 + /// + public const string TabBarExtraContent = "tab-bar-extra-content"; + /// + /// 标签栏样式 + /// + public const string TabBarStyle = "tab-bar-style"; + /// + /// 标签位置 + /// + public const string TabPosition = "tab-position"; + /// + /// 标签间隙 + /// + public const string TabBarGutter = "tab-bar-gutter"; + /// + /// 隐藏全部 + /// + public const string HideAll = "hide-all"; + /// + /// 链接路由 + /// + public const string LinkRouter = "link-router"; + /// + /// 严格模式匹配路由 + /// + public const string LinkExact = "link-exact"; + /// + /// 可停用 + /// + public const string CanDeactivate = "can-deactivate"; + /// + /// 居中 + /// + public const string Centered = "centered"; + /// + /// 隐藏添加 + /// + public const string HideAdd = "hide-add"; + /// + /// 添加图标 + /// + public const string AddIcon = "add-icon"; + /// + /// 添加事件 + /// + public const string OnAdd = "on-add"; + /// + /// 强制渲染 + /// + public const string ForceRender = "force-render"; + /// + /// 上下文菜单事件 + /// + public const string OnContextmenu = "on-contextmenu"; + /// + /// 取消选中事件 + /// + public const string OnDeselect = "on-deselect"; + /// + /// 可关闭 + /// + public const string Closable = "closable"; + /// + /// 关闭图标 + /// + public const string CloseIcon = "close-icon"; + /// + /// 待定 + /// + public const string Pending = "pending"; + /// + /// 待定点 + /// + public const string PendingDot = "pending-dot"; + /// + /// 提示触发行为 + /// + public const string TooltipTrigger = "tooltip-trigger"; + /// + /// 提示颜色 + /// + public const string TooltipColor = "tooltip-color"; + /// + /// 提示颜色类型 + /// + public const string TooltipColorType = "tooltip-color-type"; + /// + /// 提示源元素 + /// + public const string TooltipOrigin = "tooltip-origin"; + /// + /// 提示移入延时 + /// + public const string TooltipMouseEnterDelay = "tooltip-mouse-enter-delay"; + /// + /// 提示移出延时 + /// + public const string TooltipMouseLeaveDelay = "tooltip-mouse-leave-delay"; + /// + /// 提示类名 + /// + public const string TooltipOverlayClassName = "tooltip-overlay-class-name"; + /// + /// 提示样式 + /// + public const string TooltipOverlayStyle = "tooltip-overlay-style"; + /// + /// 提示框显示状态变化事件 + /// + public const string OnTooltipVisibleChange = "on-tooltip-visible-change"; + /// + /// 可拖拽 + /// + public const string Draggable = "draggable"; + /// + /// 搜索值 + /// + public const string SearchValue = "search-value"; + /// + /// 搜索方法 + /// + public const string SearchFunc = "search-func"; + /// + /// 拖拽前 + /// + public const string BeforeDrop = "before-drop"; + /// + /// 搜索值变化事件 + /// + public const string OnSearchValueChange = "on-search-value-change"; + /// + /// 开始拖拽事件 + /// + public const string OnDragStart = "on-drag-start"; + /// + /// dragenter事件 + /// + public const string OnDragEnter = "on-drag-enter"; + /// + /// dragover事件 + /// + public const string OnDragOver = "on-drag-over"; + /// + /// dragleave事件 + /// + public const string OnDragLeave = "on-drag-leave"; + /// + /// drop事件 + /// + public const string OnDrop = "on-drop"; + /// + /// dragend事件 + /// + public const string OnDragEnd = "on-drag-end"; + /// + /// 树控制器 + /// + public const string TreeControl = "tree-control"; + /// + /// 目录树 + /// + public const string DirectoryTree = "directory-tree"; + /// + /// 最大缓冲区 + /// + public const string MaxBufferPx = "max-buffer-px"; + /// + /// 最小缓冲区 + /// + public const string MinBufferPx = "min-buffer-px"; + /// + /// 树节点定义 + /// + public const string TreeNodeDef = "tree-node-def"; + /// + /// 树节点内边距 + /// + public const string TreeNodePadding = "tree-node-padding"; + /// + /// 树节点缩进线 + /// + public const string TreeNodeIndentLine = "tree-node-indent-line"; + /// + /// 树节点空操作切换 + /// + public const string TreeNodeNoopToggle = "tree-node-noop-toggle"; + /// + /// 横幅 + /// + public const string Banner = "banner"; + /// + /// 可关闭 + /// + public const string Closeable = "closeable"; + /// + /// 关闭文本 + /// + public const string CloseText = "close-text"; + /// + /// 图标类型 + /// + public const string IconType = "icon-type"; + /// + /// 遮罩可关闭 + /// + public const string MaskClosable = "mask-closable"; + /// + /// 遮罩 + /// + public const string Mask = "mask"; + /// + /// 遮罩样式 + /// + public const string MaskStyle = "mask-style"; + /// + /// 键盘 + /// + public const string Keyboard = "keyboard"; + /// + /// X坐标偏移量 + /// + public const string OffsetX = "offset-x"; + /// + /// Y坐标偏移量 + /// + public const string OffsetY = "offset-y"; + /// + /// 包装类名 + /// + public const string WrapClassName = "wrap-class-name"; + /// + /// z-index + /// + public const string ZIndex = "z-index"; + /// + /// 确定加载 + /// + public const string OkLoading = "ok-loading"; + /// + /// 禁用确定 + /// + public const string OkDisabled = "ok-disabled"; + /// + /// 取消加载 + /// + public const string CancelLoading = "cancel-loading"; + /// + /// 禁用取消 + /// + public const string CancelDisabled = "cancel-disabled"; + /// + /// 组件参数 + /// + public const string ComponentParams = "component-params"; + /// + /// 样式 + /// + public const string NzStyle = "nz-style"; + /// + /// 确认文字 + /// + public const string OkText = "ok-text"; + /// + /// 取消文字 + /// + public const string CancelText = "cancel-text"; + /// + /// 确认类型 + /// + public const string OkType = "ok-type"; + /// + /// 确认危险 + /// + public const string OkDanger = "ok-danger"; + /// + /// 自动聚焦 + /// + public const string Autofocus = "auto-focus"; + /// + /// 取消事件 + /// + public const string OnCancel = "on-cancel"; + /// + /// 打开后事件 + /// + public const string OnAfterOpen = "on-after-open"; + /// + /// 弹出确认标题 + /// + public const string PopconfirmTitle = "popconfirm-title"; + /// + /// 弹出确认触发行为 + /// + public const string PopconfirmTrigger = "popconfirm-trigger"; + /// + /// 弹出确认位置 + /// + public const string PopconfirmPlacement = "popconfirm-placement"; + /// + /// 弹出确认源元素 + /// + public const string PopconfirmOrigin = "popconfirm-origin"; + /// + /// 弹出确认是否可见 + /// + public const string PopconfirmVisible = "popconfirm-visible"; + /// + /// 弹出确认显示箭头 + /// + public const string PopconfirmShowArrow = "popconfirm-show-arrow"; + /// + /// 弹出确认显示状态变化事件 + /// + public const string OnPopconfirmVisibleChange = "on-popconfirm-visible-change"; + /// + /// 弹出确认移入延时 + /// + public const string PopconfirmMouseEnterDelay = "popconfirm-mouse-enter-delay"; + /// + /// 弹出确认移出延时 + /// + public const string PopconfirmMouseLeaveDelay = "popconfirm-mouse-leave-delay"; + /// + /// 弹出确认样式类名 + /// + public const string PopconfirmOverlayClassName = "popconfirm-overlay-class-name"; + /// + /// 弹出确认样式 + /// + public const string PopconfirmOverlayStyle = "popconfirm-overlay-style"; + /// + /// 弹出确认背景 + /// + public const string PopconfirmBackdrop = "popconfirm-backdrop"; + /// + /// 弹出确认取消文字 + /// + public const string PopconfirmCancelText = "popconfirm-cancel-text"; + /// + /// 弹出确认确定文字 + /// + public const string PopconfirmOkText = "popconfirm-ok-text"; + /// + /// 弹出确认确定类型 + /// + public const string PopconfirmOkType = "popconfirm-ok-type"; + /// + /// 弹出确认自动聚焦 + /// + public const string PopconfirmAutoFocus = "popconfirm-auto-focus"; + /// + /// 弹出确认条件 + /// + public const string PopconfirmCondition = "popconfirm-condition"; + /// + /// 弹出确认图标 + /// + public const string PopconfirmIcon = "popconfirm-icon"; + /// + /// 弹出确认取消事件 + /// + public const string OnPopconfirmCancel = "on-popconfirm-cancel"; + /// + /// 弹出确认确认事件 + /// + public const string OnPopconfirmConfirm = "on-popconfirm-confirm"; + /// + /// 百分比 + /// + public const string Percent = "percent"; + /// + /// 显示信息 + /// + public const string ShowInfo = "show-info"; + /// + /// 端点形状 + /// + public const string StrokeLinecap = "stroke-linecap"; + /// + /// 线条颜色 + /// + public const string StrokeColor = "stroke-color"; + /// + /// 线条颜色 + /// + public const string StrokeColorType = "stroke-color-type"; + /// + /// 成功百分比 + /// + public const string SuccessPercent = "success-percent"; + /// + /// 线条宽度 + /// + public const string StrokeWidth = "stroke-width"; + /// + /// 步数 + /// + public const string Steps = "steps"; + /// + /// 缺口角度 + /// + public const string GapDegree = "gap-degree"; + /// + /// 缺口位置 + /// + public const string GapPosition = "gap-position"; + /// + /// 段落 + /// + public const string Paragraph = "paragraph"; + /// + /// 圆形 + /// + public const string Round = "round"; + /// + /// 延迟 + /// + public const string Delay = "delay"; + /// + /// 指示符 + /// + public const string Indicator = "indicator"; + /// + /// 旋转 + /// + public const string Spinning = "spinning"; + /// + /// 提示 + /// + public const string Tip = "tip"; + /// + /// 固定 + /// + public const string Affix = "affix"; + /// + /// 边界 + /// + public const string Bounds = "bounds"; + /// + /// 固定显示圆点 + /// + public const string ShowInkInFixed = "show-ink-in-fixed"; + /// + /// 容器 + /// + public const string Container = "container"; + /// + /// 滚动事件 + /// + public const string OnScroll = "on-scroll"; + /// + /// 是否无效手机号 + /// + public const string IsInvalidPhone = "is-invalid-phone"; + /// + /// 是否无效身份证 + /// + public const string IsInvalidIdCard = "is-invalid-id-card"; + /// + /// 双色图标颜色 + /// + public const string TwotoneColor = "twotone-color"; + /// + /// 双色图标颜色类型 + /// + public const string TwotoneColorType = "twotone-color-type"; + /// + /// Iconfont图标 + /// + public const string IconFont = "icon-font"; + /// + /// 值为0时的触发器 + /// + public const string ZeroTrigger = "zero-trigger"; + /// + /// 路由标签 + /// + public const string RouteLabel = "route-label"; + /// + /// 路由标签函数 + /// + public const string RouteLabelFn = "route-label-fn"; +} \ No newline at end of file diff --git a/src/Util.Ui/Enums/ATarget.cs b/src/Util.Ui/Enums/ATarget.cs new file mode 100644 index 000000000..eaf5626f0 --- /dev/null +++ b/src/Util.Ui/Enums/ATarget.cs @@ -0,0 +1,27 @@ +namespace Util.Ui.Enums; + +/// +/// 链接打开目标 +/// +public enum ATarget { + /// + /// _self,在相同框架中打开 + /// + [Description( "_self" )] + Self, + /// + /// _blank,在新窗口中打开 + /// + [Description( "_blank" )] + Blank, + /// + /// _parent,在父框架中打开 + /// + [Description( "_parent" )] + Parent, + /// + /// _top,在窗口主体中打开 + /// + [Description( "_top" )] + Top +} \ No newline at end of file diff --git a/src/Util.Ui/Expressions/ExpressionLoader.cs b/src/Util.Ui/Expressions/ExpressionLoader.cs new file mode 100644 index 000000000..89aeb6e5a --- /dev/null +++ b/src/Util.Ui/Expressions/ExpressionLoader.cs @@ -0,0 +1,43 @@ +using Util.Ui.Configs; + +namespace Util.Ui.Expressions; + +/// +/// 表达式加载器 +/// +public class ExpressionLoader : ExpressionLoaderBase { + /// + /// 加载模型信息 + /// + /// 配置 + /// 模型表达式信息 + protected override void Load( Config config, ModelExpressionInfo info ) { + LoadName( config, info ); + LoadDisplayName( config, info ); + LoadRequired( config, info ); + } + + /// + /// 加载属性名 + /// + protected virtual void LoadName( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.Name, info.PropertyName, false ); + } + + /// + /// 加载显示名称 + /// + protected virtual void LoadDisplayName( Config config, ModelExpressionInfo info ) { + config.SetAttribute( UiConst.Title, info.DisplayName, false ); + } + + /// + /// 加载必填项验证 + /// + protected virtual void LoadRequired( Config config, ModelExpressionInfo info ) { + if ( info.IsRequired == false ) + return; + config.SetAttribute( UiConst.Required, "true", false ); + config.SetAttribute( UiConst.RequiredMessage, info.RequiredMessage, false ); + } +} \ No newline at end of file diff --git a/src/Util.Ui/Expressions/ExpressionLoaderBase.cs b/src/Util.Ui/Expressions/ExpressionLoaderBase.cs new file mode 100644 index 000000000..e55ec1394 --- /dev/null +++ b/src/Util.Ui/Expressions/ExpressionLoaderBase.cs @@ -0,0 +1,44 @@ +using Util.Ui.Configs; + +namespace Util.Ui.Expressions; + +/// +/// 表达式加载器 +/// +public abstract class ExpressionLoaderBase : IExpressionLoader { + /// + public virtual void Load( Config config, string expressionPropertyName = "for" ) { + if ( config == null ) + return; + if ( config.Contains( expressionPropertyName ) == false ) + return; + var info = ResolveModelExpression( config, expressionPropertyName ); + info.ExpressionPropertyName = expressionPropertyName; + Load( config, info ); + } + + /// + /// 解析模型表达式 + /// + protected virtual ModelExpressionInfo ResolveModelExpression( Config config, string expressionPropertyName ) { + var expression = config.GetValue( expressionPropertyName ); + var resolver = CreateExpressionResolver( config ); + return resolver.Resolve( expression ); + } + + /// + /// 创建模型表达式解析器 + /// + /// 配置 + protected virtual IExpressionResolver CreateExpressionResolver( Config config ) { + return new ExpressionResolver(); + } + + /// + /// 加载模型信息 + /// + /// 配置 + /// 模型表达式信息 + protected virtual void Load( Config config, ModelExpressionInfo info ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui/Expressions/ExpressionResolver.cs b/src/Util.Ui/Expressions/ExpressionResolver.cs new file mode 100644 index 000000000..4dc098b67 --- /dev/null +++ b/src/Util.Ui/Expressions/ExpressionResolver.cs @@ -0,0 +1,284 @@ +namespace Util.Ui.Expressions; + +/// +/// 模型表达式解析器 +/// +public class ExpressionResolver : IExpressionResolver { + /// + public ModelExpressionInfo Resolve( ModelExpression expression ) { + var result = new ModelExpressionInfo(); + if ( expression == null ) + return result; + result.ModelType = expression.Metadata.ModelType; + result.OriginalPropertyName = expression.Name; + result.PropertyName = GetPropertyName( expression.Name ); + result.ModelName = GetModelName( expression.ModelExplorer ); + var property = GetProperty( expression.Metadata ); + if ( property == null ) + return result; + result.Property = property; + result.LastPropertyName = GetLastPropertyName( property ); + result.DisplayName = GetDisplayName( property ); + result.IsPassword = GetIsPassword( property ); + result.IsBool = GetIsBool( property ); + result.IsEnum = GetIsEnum( property ); + result.IsDate = GetIsDate( property ); + result.IsInt = GetIsInt( property ); + result.IsNumber = GetIsNumber( property ); + ValidateRequired( result, property ); + ValidateLength( result, property ); + ValidateRange( result, property ); + ValidateEmail( result, property ); + ValidatePhone( result, property ); + ValidateIdCard( result, property ); + ValidateUrl( result, property ); + ValidateRegularExpression( result, property ); + return result; + } + + /// + /// 获取属性信息 + /// + protected virtual MemberInfo GetProperty( ModelMetadata metadata ) { + if ( metadata == null ) + return null; + if ( metadata.ContainerType == null ) + return null; + var members = metadata.ContainerType.GetMember( metadata.PropertyName! ); + return members.Length == 0 ? null : members[0]; + } + + /// + /// 获取属性名 + /// + protected virtual string GetPropertyName( string originalPropertyName ) { + if ( originalPropertyName.IsEmpty() ) + return null; + var array = originalPropertyName.Split( '.' ); + return array.Select( ModelExpressionHelper.ConvertName ).Join( separator: "." ); + } + + /// + /// 获取模型名称 + /// + protected virtual string GetModelName( ModelExplorer explorer ) { + var result = GetDefaultModel(); + if ( explorer == null ) + return result; + if ( explorer.Container == null ) + return result; + if ( explorer.Container.ModelType == null ) + return result; + var modelAttribute = explorer.Container.ModelType.GetCustomAttribute(); + if ( modelAttribute != null ) + return modelAttribute.Model; + var modelName = ModelName.Get( explorer.Container.ModelType ); + if( modelName.IsEmpty() ) + return result; + return modelName; + } + + /// + /// 获取默认模型名称 + /// + protected virtual string GetDefaultModel() { + return "model"; + } + + /// + /// 获取最后一级属性名 + /// + protected virtual string GetLastPropertyName( MemberInfo property ) { + return ModelExpressionHelper.ConvertName( property.Name ); + } + + /// + /// 获取显示名称 + /// + protected virtual string GetDisplayName( MemberInfo property ) { + return Util.Helpers.Reflection.GetDisplayNameOrDescription( property ); + } + + /// + /// 获取是否密码类型 + /// + protected virtual bool GetIsPassword( MemberInfo property ) { + var attribute = property.GetCustomAttribute(); + if ( attribute == null ) + return false; + if ( attribute.DataType == DataType.Password ) + return true; + return false; + } + + /// + /// 获取是否布尔类型 + /// + protected virtual bool GetIsBool( MemberInfo property ) { + return Util.Helpers.Reflection.IsBool( property ); + } + + /// + /// 获取是否枚举类型 + /// + protected virtual bool GetIsEnum( MemberInfo property ) { + return Util.Helpers.Reflection.IsEnum( property ); + } + + /// + /// 获取是否日期类型 + /// + protected virtual bool GetIsDate( MemberInfo property ) { + return Util.Helpers.Reflection.IsDate( property ); + } + + /// + /// 获取是否整型 + /// + protected virtual bool GetIsInt( MemberInfo property ) { + return Util.Helpers.Reflection.IsInt( property ); + } + + /// + /// 获取是否数值类型 + /// + protected virtual bool GetIsNumber( MemberInfo property ) { + return Util.Helpers.Reflection.IsNumber( property ); + } + + /// + /// 验证必填项 + /// + protected virtual void ValidateRequired( ModelExpressionInfo result, MemberInfo property ) { + var attribute = property.GetCustomAttribute(); + if ( attribute == null ) + return; + result.IsRequired = true; + result.RequiredMessage = attribute.ErrorMessage; + } + + /// + /// 验证长度 + /// + protected virtual void ValidateLength( ModelExpressionInfo result, MemberInfo property ) { + ValidateStringLength( result, property ); + ValidateMaxLength( result, property ); + ValidateMinLength( result, property ); + } + + /// + /// 验证StringLengthAttribute特性 + /// + protected virtual void ValidateStringLength( ModelExpressionInfo result, MemberInfo property ) { + var attribute = property.GetCustomAttribute(); + if ( attribute == null ) + return; + if ( attribute.MaximumLength > 0 ) { + result.MaxLength = attribute.MaximumLength; + result.MaxLengthMessage = attribute.ErrorMessage; + } + if ( attribute.MinimumLength > 0 ) { + result.MinLength = attribute.MinimumLength; + result.MinLengthMessage = attribute.ErrorMessage; + } + } + + /// + /// 验证MaxLengthAttribute特性 + /// + protected virtual void ValidateMaxLength( ModelExpressionInfo result, MemberInfo property ) { + var attribute = property.GetCustomAttribute(); + if ( attribute == null ) + return; + result.MaxLength = attribute.Length; + result.MaxLengthMessage = attribute.ErrorMessage; + } + + /// + /// 验证MinLengthAttribute特性 + /// + protected virtual void ValidateMinLength( ModelExpressionInfo result, MemberInfo property ) { + var attribute = property.GetCustomAttribute(); + if ( attribute == null ) + return; + result.MinLength = attribute.Length; + result.MinLengthMessage = attribute.ErrorMessage; + } + + /// + /// 验证RangeAttribute特性 + /// + protected virtual void ValidateRange( ModelExpressionInfo result, MemberInfo property ) { + var attribute = property.GetCustomAttribute(); + if ( attribute == null ) + return; + result.Min = attribute.Minimum; + result.Max = attribute.Maximum; + result.MinMessage = attribute.ErrorMessage; + result.MaxMessage = attribute.ErrorMessage; + } + + /// + /// 验证EmailAddressAttribute特性 + /// + protected virtual void ValidateEmail( ModelExpressionInfo result, MemberInfo property ) { + var attribute = property.GetCustomAttribute(); + if ( attribute == null ) + return; + result.IsEmail = true; + if ( attribute.ErrorMessage != null && attribute.ErrorMessage.Contains( "field is not a valid e-mail address" ) ) + return; + result.EmailMessage = attribute.ErrorMessage; + } + + /// + /// 验证PhoneAttribute特性 + /// + protected virtual void ValidatePhone( ModelExpressionInfo result, MemberInfo property ) { + var attribute = property.GetCustomAttribute(); + if ( attribute == null ) + return; + result.IsPhone = true; + if ( attribute.ErrorMessage != null && attribute.ErrorMessage.Contains( "field is not a valid phone number" ) ) + return; + result.PhoneMessage = attribute.ErrorMessage; + } + + /// + /// 验证IdCardAttribute特性 + /// + protected virtual void ValidateIdCard( ModelExpressionInfo result, MemberInfo property ) { + var attribute = property.GetCustomAttribute(); + if ( attribute == null ) + return; + result.IsIdCard = true; + if ( attribute.ErrorMessage == R.InvalidIdCard ) + return; + result.IdCardMessage = attribute.ErrorMessage; + } + + /// + /// 验证UrlAttribute特性 + /// + protected virtual void ValidateUrl( ModelExpressionInfo result, MemberInfo property ) { + var attribute = property.GetCustomAttribute(); + if ( attribute == null ) + return; + result.IsUrl = true; + if ( attribute.ErrorMessage != null && attribute.ErrorMessage.Contains( "field is not a valid fully-qualified http, https, or ftp URL" ) ) + return; + result.UrlMessage = attribute.ErrorMessage; + } + + /// + /// 验证RegularExpressionAttribute特性 + /// + protected virtual void ValidateRegularExpression( ModelExpressionInfo result, MemberInfo property ) { + var attribute = property.GetCustomAttribute(); + if ( attribute == null ) + return; + result.IsRegularExpression = true; + result.Pattern = attribute.Pattern; + result.RegularExpressionMessage = attribute.ErrorMessage; + } +} \ No newline at end of file diff --git a/src/Util.Ui/Expressions/IExpressionLoader.cs b/src/Util.Ui/Expressions/IExpressionLoader.cs new file mode 100644 index 000000000..738e08d2c --- /dev/null +++ b/src/Util.Ui/Expressions/IExpressionLoader.cs @@ -0,0 +1,15 @@ +using Util.Ui.Configs; + +namespace Util.Ui.Expressions; + +/// +/// 表达式加载器 +/// +public interface IExpressionLoader { + /// + /// 加载模型表达式配置 + /// + /// 配置 + /// 模型表达式属性名,默认值为for + void Load( Config config, string expressionPropertyName = "for" ); +} \ No newline at end of file diff --git a/src/Util.Ui/Expressions/IExpressionResolver.cs b/src/Util.Ui/Expressions/IExpressionResolver.cs new file mode 100644 index 000000000..b4a7f33d0 --- /dev/null +++ b/src/Util.Ui/Expressions/IExpressionResolver.cs @@ -0,0 +1,12 @@ +namespace Util.Ui.Expressions; + +/// +/// 模型表达式解析器 +/// +public interface IExpressionResolver { + /// + /// 解析 + /// + /// 模型表达式 + ModelExpressionInfo Resolve( ModelExpression expression ); +} \ No newline at end of file diff --git a/src/Util.Ui/Expressions/ModelExpressionHelper.cs b/src/Util.Ui/Expressions/ModelExpressionHelper.cs new file mode 100644 index 000000000..cb6622bd4 --- /dev/null +++ b/src/Util.Ui/Expressions/ModelExpressionHelper.cs @@ -0,0 +1,94 @@ +namespace Util.Ui.Expressions; + +/// +/// 模型表达式辅助操作 +/// +public static class ModelExpressionHelper { + /// + /// 创建模型表达式 + /// + /// 模型类型 + /// 属性类型 + /// 属性名 + /// 属性表达式 + public static ModelExpression Create( string name, Expression> expression ) { + var property = Util.Helpers.Lambda.GetMember( expression ) as PropertyInfo; + property!.CheckNull( nameof(property) ); + var provider = CreateModelMetadataProvider(); + var metadata = provider.GetMetadataForProperty( property, typeof( TProperty ) ); + var modelExplorer = new ModelExplorer( provider, metadata, null ); + return new ModelExpression( name, modelExplorer ); + } + + /// + /// 创建模型表达式 + /// + /// 模型类型 + /// 属性名 + public static ModelExpression Create( string name ) { + return Create( name, typeof( TModel ), typeof( TModel ) ); + } + + /// + /// 获取模型元数据提供程序 + /// + private static ModelMetadataProvider CreateModelMetadataProvider() { + return new EmptyModelMetadataProvider(); + } + + /// + /// 创建模型表达式 + /// + /// 模型类型 + /// 容器模型类型 + /// 属性名 + public static ModelExpression Create( string name ) { + return Create( name, typeof( TModel ), typeof( TContainer ) ); + } + + /// + /// 创建模型表达式 + /// + /// 属性名 + /// 模型类型 + /// 容器模型类型 + private static ModelExpression Create( string name, Type modelType, Type containerType ) { + var provider = CreateModelMetadataProvider(); + var containerModelExplorer = provider.GetModelExplorerForType( containerType, null ); + var metadata = provider.GetMetadataForType( modelType ); + var modelExplorer = new ModelExplorer( provider, containerModelExplorer, metadata, null ); + return new ModelExpression( name, modelExplorer ); + } + + /// + /// 按System.Text.Json转换命名 + /// + /// 属性名 + public static string ConvertName( string name ) { + if ( string.IsNullOrEmpty( name ) || !char.IsUpper( name[0] ) ) { + return name; + } + return string.Create( name.Length, name, ( chars, name2 ) => { + name2.AsSpan().CopyTo( chars ); + FixCasing( chars ); + } ); + } + + /// + /// 修复小写 + /// + private static void FixCasing( Span chars ) { + for ( int i = 0; i < chars.Length; i++ ) { + if ( i == 1 && !char.IsUpper( chars[i] ) ) + break; + bool hasNext = ( i + 1 < chars.Length ); + if ( i > 0 && hasNext && !char.IsUpper( chars[i + 1] ) ) { + if ( chars[i + 1] == ' ' ) { + chars[i] = char.ToLowerInvariant( chars[i] ); + } + break; + } + chars[i] = char.ToLowerInvariant( chars[i] ); + } + } +} \ No newline at end of file diff --git a/src/Util.Ui/Expressions/ModelExpressionInfo.cs b/src/Util.Ui/Expressions/ModelExpressionInfo.cs new file mode 100644 index 000000000..e5bcede2f --- /dev/null +++ b/src/Util.Ui/Expressions/ModelExpressionInfo.cs @@ -0,0 +1,168 @@ +namespace Util.Ui.Expressions; + +/// +/// 模型表达式信息 +/// +public class ModelExpressionInfo { + /// + /// 表达式属性名 + /// + public string ExpressionPropertyName { get; set; } + /// + /// 模型类型 + /// + public Type ModelType { get; set; } + /// + /// 属性 + /// + public MemberInfo Property { get; set; } + /// + /// 原始属性名 + /// + public string OriginalPropertyName { get; set; } + /// + /// 属性名,修正为驼峰形式,范例: 原始属性名为 Customer.Name,则该值为 customer.name + /// + public string PropertyName { get; set; } + /// + /// 安全属性名,范例: 原始属性名为 Customer.Name,则该值为 model.customer&&model.customer.name + /// + public string SafePropertyName => GetSafePropertyName( ModelName ); + /// + /// 最后一级属性名,范例: 原始属性名为 Customer.Name,则该值为 name + /// + public string LastPropertyName { get; set; } + /// + /// 显示名称 + /// + public string DisplayName { get; set; } + /// + /// 模型名称,默认值: model + /// + public string ModelName { get; set; } = "model"; + /// + /// 是否密码类型 + /// + public bool IsPassword { get; set; } + /// + /// 是否布尔类型 + /// + public bool IsBool { get; set; } + /// + /// 是否枚举类型 + /// + public bool IsEnum { get; set; } + /// + /// 是否日期类型 + /// + public bool IsDate { get; set; } + /// + /// 是否整型 + /// + public bool IsInt { get; set; } + /// + /// 是否数值类型 + /// + public bool IsNumber { get; set; } + /// + /// 是否必填项 + /// + public bool IsRequired { get; set; } + /// + /// 必填项验证消息 + /// + public string RequiredMessage { get; set; } + /// + /// 最小长度 + /// + public int? MinLength { get; set; } + /// + /// 最小长度验证消息 + /// + public string MinLengthMessage { get; set; } + /// + /// 最大长度 + /// + public int? MaxLength { get; set; } + /// + /// 最大长度验证消息 + /// + public string MaxLengthMessage { get; set; } + /// + /// 最小值 + /// + public object Min { get; set; } + /// + /// 最小值验证消息 + /// + public string MinMessage { get; set; } + /// + /// 最大值 + /// + public object Max { get; set; } + /// + /// 最大值验证消息 + /// + public string MaxMessage { get; set; } + /// + /// 是否电子邮件 + /// + public bool IsEmail { get; set; } + /// + /// 电子邮件验证消息 + /// + public string EmailMessage { get; set; } + /// + /// 是否手机号 + /// + public bool IsPhone { get; set; } + /// + /// 手机号验证消息 + /// + public string PhoneMessage { get; set; } + /// + /// 是否身份证 + /// + public bool IsIdCard { get; set; } + /// + /// 身份证验证消息 + /// + public string IdCardMessage { get; set; } + /// + /// 是否网址 + /// + public bool IsUrl { get; set; } + /// + /// 网址验证消息 + /// + public string UrlMessage { get; set; } + /// + /// 是否正则表达式 + /// + public bool IsRegularExpression { get; set; } + /// + /// 正则表达式模式 + /// + public string Pattern { get; set; } + /// + /// 正则表达式验证消息 + /// + public string RegularExpressionMessage { get; set; } + + /// + /// 获取安全属性名,范例: 原始属性名为 Customer.Name,则该值为 model.customer&&model.customer.name + /// + /// 模型名称 + public string GetSafePropertyName( string model = "model" ) { + if ( OriginalPropertyName.IsEmpty() ) + return null; + var result = new List(); + var temp = new List { model }; + var array = OriginalPropertyName.Split( '.' ); + foreach ( var item in array ) { + temp.Add( ModelExpressionHelper.ConvertName( item ) ); + result.Add( temp.Join( separator: "." ) ); + } + return result.Join( separator: "&&" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui/Extensions/TagBuilderExtensions.cs b/src/Util.Ui/Extensions/TagBuilderExtensions.cs new file mode 100644 index 000000000..add954006 --- /dev/null +++ b/src/Util.Ui/Extensions/TagBuilderExtensions.cs @@ -0,0 +1,49 @@ +using Util.Ui.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.Extensions; + +/// +/// 标签生成器扩展 +/// +public static class TagBuilderExtensions { + /// + /// 添加属性列表 + /// + /// 标签生成器 + /// 属性列表 + public static TagBuilder Attributes( this TagBuilder builder, TagHelperAttributeList attributes ) { + foreach ( var attribute in attributes ) + builder.Attribute( attribute.Name, attribute.Value.SafeString() ); + return builder; + } + + /// + /// 添加样式 + /// + /// 标签生成器 + /// 配置 + public static TagBuilder Style( this TagBuilder builder, Config config ) { + builder.AttributeIfNotEmpty( UiConst.Style, config.GetValue( UiConst.Style ) ); + return builder; + } + + /// + /// 添加样式类 + /// + /// 标签生成器 + /// 配置 + public static TagBuilder Class( this TagBuilder builder, Config config ) { + return builder.Class( config.GetValue( UiConst.Class ) ); + } + + /// + /// 隐藏 + /// + /// 标签生成器 + /// 配置 + public static TagBuilder Hidden( this TagBuilder builder, Config config ) { + builder.AttributeIfNotEmpty( "[hidden]", config.GetValue( UiConst.Hidden ) ); + return builder; + } +} \ No newline at end of file diff --git a/src/Util.Ui/Extensions/TagHelperExtensions.cs b/src/Util.Ui/Extensions/TagHelperExtensions.cs new file mode 100644 index 000000000..fdebe7f92 --- /dev/null +++ b/src/Util.Ui/Extensions/TagHelperExtensions.cs @@ -0,0 +1,107 @@ +using Util.Ui.Builders; + +namespace Util.Ui.Extensions; + +/// +/// TagHelper扩展 +/// +public static class TagHelperExtensions { + /// + /// 内容是否为空 + /// + /// 内容 + public static bool IsEmpty( this TagHelperContent content ) { + return content == null || content.IsEmptyOrWhiteSpace; + } + + /// + /// 将内容添加到标签生成器 + /// + /// 内容 + /// 标签生成器 + public static void AppendTo( this TagHelperContent content,TagBuilder builder ) { + if( content.IsEmpty() ) + return; + builder.AppendContent( content ); + } + + /// + /// 将内容设置到标签生成器 + /// + /// 内容 + /// 标签生成器 + public static void SetTo( this TagHelperContent content, TagBuilder builder ) { + if ( content.IsEmpty() ) + return; + builder.SetContent( content ); + } + + /// + /// 从TagHelperContext Items里获取值 + /// + /// 返回类型 + /// 上下文 + /// 键 + public static T GetValueFromItems( this TagHelperContext context, object key = null ) { + key ??= typeof(T); + var exists = context.Items.TryGetValue( key, out var value ); + if( exists == false ) + return default; + if( !( value is TagHelperAttribute tagHelperAttribute ) ) + return Util.Helpers.Convert.To( value ); + return Util.Helpers.Convert.To( tagHelperAttribute.Value ); + } + + /// + /// 设置TagHelperContext Items值 + /// + /// 上下文 + /// 值 + public static void SetValueToItems( this TagHelperContext context, T value ) { + var key = typeof( T ); + SetValueToItems( context, key, value ); + } + + /// + /// 设置TagHelperContext Items值 + /// + /// 上下文 + /// 键 + /// 值 + public static void SetValueToItems( this TagHelperContext context, object key, object value ) { + if ( key == null ) + return; + context.Items[key] = value; + } + + /// + /// 从TagHelperContext AllAttributes里获取值 + /// + /// 上下文 + /// 键 + public static string GetValueFromAttributes( this TagHelperContext context, string key ) { + return context.GetValueFromAttributes( key ); + } + + /// + /// 判断TagHelperContext AllAttributes是否存在指定项 + /// + /// 上下文 + /// 键 + public static bool ContainsByAttributes( this TagHelperContext context, string key ) { + return context.AllAttributes.ContainsName( key ); + } + + /// + /// 从TagHelperContext AllAttributes里获取值 + /// + /// 返回类型 + /// 上下文 + /// 键 + public static T GetValueFromAttributes( this TagHelperContext context, string key ) { + var exists = context.AllAttributes.TryGetAttribute( key, out var value ); + if( exists == false ) + return default; + return value is not { } tagHelperAttribute ? default : Util.Helpers.Convert.To( tagHelperAttribute?.Value ); + } +} \ No newline at end of file diff --git a/src/Util.Ui/Helpers/SizeHelper.cs b/src/Util.Ui/Helpers/SizeHelper.cs new file mode 100644 index 000000000..99887d5c0 --- /dev/null +++ b/src/Util.Ui/Helpers/SizeHelper.cs @@ -0,0 +1,15 @@ +namespace Util.Ui.Helpers; + +/// +/// 尺寸辅助操作 +/// +public class SizeHelper { + /// + /// 获取尺寸,如果值为数值,则添加px + /// + public static string GetValue( string value ) { + if ( value.IsEmpty() ) + return null; + return Util.Helpers.Validation.IsNumber( value ) ? $"{value}px" : value; + } +} \ No newline at end of file diff --git a/src/Util.Ui/ModelName.cs b/src/Util.Ui/ModelName.cs new file mode 100644 index 000000000..56f7d3559 --- /dev/null +++ b/src/Util.Ui/ModelName.cs @@ -0,0 +1,37 @@ +namespace Util.Ui; + +/// +/// 模型名称 +/// +public static class ModelName { + /// + /// 模型名称映射 + /// + private static readonly AsyncLocal> _modelNames = new(); + + /// + /// 添加模型名称映射 + /// + /// 模型类型 + /// 模型名称 + public static void Add( string name ) { + _modelNames.Value ??= new Dictionary(); + _modelNames.Value.Add( typeof( T ), name ); + } + + /// + /// 获取模型名称 + /// + /// 模型类型 + public static string Get() { + return Get( typeof( T ) ); + } + + /// + /// 获取模型名称 + /// + /// 模型类型 + public static string Get(Type type) { + return _modelNames.Value?.GetValue( type ); + } +} \ No newline at end of file diff --git a/src/Util.Ui/Razor/GenerateHtmlFilter.cs b/src/Util.Ui/Razor/GenerateHtmlFilter.cs new file mode 100644 index 000000000..222c64940 --- /dev/null +++ b/src/Util.Ui/Razor/GenerateHtmlFilter.cs @@ -0,0 +1,125 @@ +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Util.Helpers; +using File = System.IO.File; + +namespace Util.Ui.Razor; + +/// +/// 生成Html静态页面过滤器 +/// +public class GenerateHtmlFilter : IAsyncPageFilter { + /// + /// 执行 + /// + public async Task OnPageHandlerExecutionAsync( PageHandlerExecutingContext context, PageHandlerExecutionDelegate next ) { + await next(); + } + + /// + /// 选择操作 + /// + public async Task OnPageHandlerSelectionAsync( PageHandlerSelectedContext context ) { + var options = context.HttpContext.RequestServices.GetService>()?.Value ?? new RazorOptions(); + if ( options.IsGenerateHtml == false ) + return; + if ( context.ActionDescriptor.ViewEnginePath == "/Error" ) + return; + var path = CreatePath( context, options ); + if ( string.IsNullOrWhiteSpace( path ) ) + return; + if ( options.EnableOverrideHtml == false ) { + var filePath = Util.Helpers.Web.GetPhysicalPath( path ); + if ( File.Exists( filePath ) ) + return; + } + var log = GetLogger( context ); + try { + var html = await GetHtml( context ); + await WriteFile( path, html ); + log.LogDebug( $"Razor生成Html成功: Razor Path: {context.ActionDescriptor.ViewEnginePath}, Html Path: {path}" ); + } + catch ( Exception exception ) { + log.LogError( exception, $"Razor页面生成 html 失败: razor path: {context.ActionDescriptor.ViewEnginePath}" ); + throw; + } + } + + /// + /// 获取日志记录器 + /// + protected virtual ILogger GetLogger( PageHandlerSelectedContext context ) { + return context?.HttpContext?.RequestServices?.GetService>() ?? NullLogger.Instance; + } + + /// + /// 创建Html文件路径 + /// + private string CreatePath( PageHandlerSelectedContext context, RazorOptions options ) { + var attribute = context?.ActionDescriptor?.ModelTypeInfo?.GetCustomAttribute(); + if ( attribute == null ) + return GetPath( context?.ActionDescriptor.ViewEnginePath, options ); + if ( attribute.Ignore ) + return string.Empty; + return string.IsNullOrWhiteSpace( attribute.Path ) ? string.Empty : attribute.Path; + } + + /// + /// 获取Html文件路径 + /// + /// 路径 + /// 配置 + public static string GetPath( string path, RazorOptions options ) { + if ( string.IsNullOrWhiteSpace( path ) ) + return string.Empty; + if ( path.Contains( "/" ) == false ) + return Util.Helpers.Url.JoinPath( options.RazorRootDirectory, options.GenerateHtmlFolder, $"{path}.html" ); + var lastIndex = path.LastIndexOf( "/", StringComparison.Ordinal ); + return Util.Helpers.Url.JoinPath( options.RazorRootDirectory, path.Substring( 0, lastIndex ), options.GenerateHtmlFolder, $"{path.Substring( lastIndex + 1 )}.html" ); + } + + /// + /// 获取页面Html + /// + private async Task GetHtml( PageHandlerSelectedContext context ) { + var serviceProvider = context.HttpContext.RequestServices; + var engine = serviceProvider.GetService(); + var activator = serviceProvider.GetService(); + dynamic model = System.Convert.ChangeType( context.HandlerInstance, context.HandlerInstance.GetType(), CultureInfo.InvariantCulture ); + var actionContext = new ActionContext( context.HttpContext, model.RouteData, context.ActionDescriptor ); + var page = FindPage( engine, context.ActionDescriptor.RelativePath ); + await using var stringWriter = new StringWriter(); + var view = new RazorView( engine, activator, new List(), page, HtmlEncoder.Default, new DiagnosticListener( "ViewRenderService" ) ); + var viewContext = new ViewContext( actionContext, view, Reflection.GetPropertyValue( model, "ViewData" ), Reflection.GetPropertyValue( model, "TempData" ), stringWriter, new HtmlHelperOptions() ) { + ExecutingFilePath = context.ActionDescriptor.RelativePath + }; + var razorPage = (Page)page; + razorPage.PageContext = model.PageContext; + razorPage.ViewContext = viewContext; + activator.Activate( razorPage, viewContext ); + await page.ExecuteAsync(); + return stringWriter.ToString(); + } + + /// + /// 查找Razor页面 + /// + private IRazorPage FindPage( IRazorViewEngine razorViewEngine, string pageName ) { + var result = razorViewEngine.GetPage( null!, pageName ); + return result.Page; + } + + /// + /// 写入文件 + /// + private async Task WriteFile( string path, string html ) { + path = Util.Helpers.Web.GetPhysicalPath( path ); + var directory = Path.GetDirectoryName( path ); + if ( string.IsNullOrWhiteSpace( directory ) ) + return; + if ( Directory.Exists( directory ) == false ) + Directory.CreateDirectory( directory ); + await File.WriteAllTextAsync( path, html ); + } +} \ No newline at end of file diff --git a/src/Util.Ui/Razor/HtmlGenerator.cs b/src/Util.Ui/Razor/HtmlGenerator.cs new file mode 100644 index 000000000..de0695da3 --- /dev/null +++ b/src/Util.Ui/Razor/HtmlGenerator.cs @@ -0,0 +1,46 @@ +using Util.Helpers; + +namespace Util.Ui.Razor; + +/// +/// Html生成器 +/// +public static class HtmlGenerator { + /// + /// 生成Html + /// + public static async Task> GenerateAsync( CancellationToken cancellationToken = default ) { + EnableGenerateHtml(); + var result = new List(); + var descriptors = GetPageActionDescriptors(); + foreach ( var descriptor in descriptors ) { + var path = $"{GetHost()}/view{descriptor.ViewEnginePath}"; + result.Add( path ); + await Web.Client.Get( path ).GetResultAsync( cancellationToken ); + } + return result.Distinct().ToList(); + } + + /// + /// 启用Html自动生成 + /// + private static void EnableGenerateHtml( bool isGenerateHtml = true ) { + var options = Ioc.Create>(); + options.Value.IsGenerateHtml = isGenerateHtml; + } + + /// + /// 获取页面操作描述符列表 + /// + private static List GetPageActionDescriptors() { + var provider = Ioc.Create(); + return provider.ActionDescriptors.Items.OfType().ToList(); + } + + /// + /// 获取请求主机 + /// + private static string GetHost() { + return $"{Web.Request.Scheme}://{Web.Request.Host}"; + } +} \ No newline at end of file diff --git a/src/Util.Ui/Razor/IRazorWatchService.cs b/src/Util.Ui/Razor/IRazorWatchService.cs new file mode 100644 index 000000000..642ad5c9f --- /dev/null +++ b/src/Util.Ui/Razor/IRazorWatchService.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.Razor; + +/// +/// Razor页面监听服务,当保存Razor页面时生成该页面的html文件 +/// +public interface IRazorWatchService { + /// + /// 启动监听 + /// + /// 取消令牌 + Task StartAsync( CancellationToken cancellationToken ); + /// + /// 停止监听 + /// + /// 取消令牌 + Task StopAsync( CancellationToken cancellationToken ); +} \ No newline at end of file diff --git a/src/Util.Ui/Razor/Internal/IPartViewPathFinder.cs b/src/Util.Ui/Razor/Internal/IPartViewPathFinder.cs new file mode 100644 index 000000000..309cb683c --- /dev/null +++ b/src/Util.Ui/Razor/Internal/IPartViewPathFinder.cs @@ -0,0 +1,13 @@ +namespace Util.Ui.Razor.Internal; + +/// +/// 分部视图路径查找器 +/// +public interface IPartViewPathFinder { + /// + /// 查找分部视图的路径 + /// + /// 主视图路径 + /// 分部视图名称, partial 标签的 name 属性 + string Find( string viewPath, string partViewName ); +} \ No newline at end of file diff --git a/src/Util.Ui/Razor/Internal/IPartViewPathResolver.cs b/src/Util.Ui/Razor/Internal/IPartViewPathResolver.cs new file mode 100644 index 000000000..754a6246e --- /dev/null +++ b/src/Util.Ui/Razor/Internal/IPartViewPathResolver.cs @@ -0,0 +1,13 @@ +namespace Util.Ui.Razor.Internal; + +/// +/// 分部视图路径解析器 +/// +public interface IPartViewPathResolver { + /// + /// 从视图文件内容解析分部视图路径 + /// + /// 视图路径 + /// 视图文件内容 + List Resolve( string path, string content ); +} \ No newline at end of file diff --git a/src/Util.Ui/Razor/Internal/IViewContentResolver.cs b/src/Util.Ui/Razor/Internal/IViewContentResolver.cs new file mode 100644 index 000000000..946745202 --- /dev/null +++ b/src/Util.Ui/Razor/Internal/IViewContentResolver.cs @@ -0,0 +1,12 @@ +namespace Util.Ui.Razor.Internal; + +/// +/// 视图内容解析器 +/// +public interface IViewContentResolver { + /// + /// 解析视图文件内容 + /// + /// 视图路径 + string Resolve( string path ); +} \ No newline at end of file diff --git a/src/Util.Ui/Razor/Internal/MainView.cs b/src/Util.Ui/Razor/Internal/MainView.cs new file mode 100644 index 000000000..e2192a3be --- /dev/null +++ b/src/Util.Ui/Razor/Internal/MainView.cs @@ -0,0 +1,12 @@ +namespace Util.Ui.Razor.Internal; + +/// +/// 主视图 +/// +public class MainView : RazorView { + /// + /// 初始化主视图 + /// + public MainView( string path ) : base( path ) { + } +} \ No newline at end of file diff --git a/src/Util.Ui/Razor/Internal/PartView.cs b/src/Util.Ui/Razor/Internal/PartView.cs new file mode 100644 index 000000000..515d34a04 --- /dev/null +++ b/src/Util.Ui/Razor/Internal/PartView.cs @@ -0,0 +1,38 @@ +namespace Util.Ui.Razor.Internal; + +/// +/// 分部视图 +/// +public class PartView : RazorView { + /// + /// 初始化分部视图 + /// + public PartView( string path ) : base( path ) { + MainViews = []; + } + + /// + /// 引用了当前分部视图的视图集合 + /// + public List MainViews { get; set; } + + /// + /// 添加主视图 + /// + /// 主视图 + public void AddMainView( RazorView mainView ) { + if ( mainView == null ) + return; + if ( MainViews.Any( t => t.Path == mainView.Path ) ) + return; + MainViews.Add( mainView ); + } + + /// + public override List GetMainViewPaths() { + var result = new List(); + foreach (var view in MainViews ) + result.AddRange( view.GetMainViewPaths() ); + return result; + } +} \ No newline at end of file diff --git a/src/Util.Ui/Razor/Internal/PartViewPathFinder.cs b/src/Util.Ui/Razor/Internal/PartViewPathFinder.cs new file mode 100644 index 000000000..73ba55610 --- /dev/null +++ b/src/Util.Ui/Razor/Internal/PartViewPathFinder.cs @@ -0,0 +1,71 @@ +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewEngines; +using Microsoft.AspNetCore.Routing; + +namespace Util.Ui.Razor.Internal; + +/// +/// 分部视图路径查找器 +/// +public class PartViewPathFinder : IPartViewPathFinder { + /// + /// 视图引擎 + /// + private readonly ICompositeViewEngine _viewEngine; + /// + /// Razor配置 + /// + private readonly RazorOptions _options; + + /// + /// 初始化分部视图路径查找器 + /// + /// 视图引擎 + /// Razor配置 + public PartViewPathFinder( ICompositeViewEngine viewEngine, IOptions options ) { + _viewEngine = viewEngine ?? throw new ArgumentNullException( nameof( viewEngine ) ); + _options = options.Value; + } + + /// + public string Find( string viewPath, string partViewName ) { + if ( viewPath.IsEmpty() ) + return null; + if ( partViewName.IsEmpty() ) + return null; + var result = _viewEngine.GetView( GetRouteValue( viewPath ), partViewName, false ); + if ( result.Success ) + return result.View.Path; + var view = _viewEngine.FindView( CreateViewContext( viewPath ), GetPartViewName( partViewName ), isMainPage: false ); + return view.Success ? view.View.Path : null; + } + + /// + /// 获取路由值 + /// + private string GetRouteValue( string viewPath ) { + return viewPath.RemoveStart( _options.RazorRootDirectory ).RemoveEnd( ".cshtml" ); + } + + /// + /// 创建视图上下文 + /// + private ViewContext CreateViewContext( string viewPath ) { + return new ViewContext { + ExecutingFilePath = viewPath, + RouteData = new RouteData { + Values = { { "page", GetRouteValue( viewPath ) } } + }, + ActionDescriptor = new PageActionDescriptor { + RouteValues = { { "page", GetRouteValue( viewPath ) } } + } + }; + } + + /// + /// 获取分部视图路径 + /// + private string GetPartViewName( string partViewName ) { + return partViewName.RemoveEnd( ".cshtml" ); + } +} \ No newline at end of file diff --git a/src/Util.Ui/Razor/Internal/PartViewPathResolver.cs b/src/Util.Ui/Razor/Internal/PartViewPathResolver.cs new file mode 100644 index 000000000..93d6e351b --- /dev/null +++ b/src/Util.Ui/Razor/Internal/PartViewPathResolver.cs @@ -0,0 +1,39 @@ +using System.Text.RegularExpressions; +using Regex = Util.Helpers.Regex; + +namespace Util.Ui.Razor.Internal; + +/// +/// 分部视图路径解析器 +/// +public class PartViewPathResolver : IPartViewPathResolver { + /// + /// 分部视图路径查找器 + /// + private readonly IPartViewPathFinder _pathFinder; + + /// + /// 初始化分部视图路径解析器 + /// + /// 分部视图路径查找器 + public PartViewPathResolver( IPartViewPathFinder pathFinder ) { + _pathFinder = pathFinder ?? throw new ArgumentNullException( nameof( pathFinder ) ); + } + + /// + public List Resolve( string path, string content ) { + var result = new List(); + if ( path.IsEmpty() ) + return result; + if ( content.IsEmpty() ) + return result; + var matches = Regex.Matches( content, "<[ ]*partial[ ]+name[ ]*=[ ]*\"(.+?)\"[ ]*/[ ]*>", RegexOptions.IgnoreCase ); + foreach ( Match match in matches ) { + if ( match.Success == false ) + continue; + var partPath = _pathFinder.Find( path, match.Groups[1].Value ); + result.Add( partPath ); + } + return result; + } +} \ No newline at end of file diff --git a/src/Util.Ui/Razor/Internal/RazorView.cs b/src/Util.Ui/Razor/Internal/RazorView.cs new file mode 100644 index 000000000..4344439ca --- /dev/null +++ b/src/Util.Ui/Razor/Internal/RazorView.cs @@ -0,0 +1,47 @@ +namespace Util.Ui.Razor.Internal; + +/// +/// Razor视图 +/// +public class RazorView { + /// + /// 初始化主视图 + /// + /// 视图路径 + protected RazorView( string path ) { + if ( path.IsEmpty() ) + throw new ArgumentNullException( nameof( path ) ); + Path = path; + PartViews = []; + } + + /// + /// 视图路径 + /// + public string Path { get; } + + /// + /// 包含的分部视图集合 + /// + public List PartViews { get; set; } + + /// + /// 添加分部视图 + /// + /// 分部视图 + public virtual void AddPartView( PartView partView ) { + if ( partView == null ) + return; + if ( PartViews.Any( t => t.Path == partView.Path ) ) + return; + partView.AddMainView( this ); + PartViews.Add( partView ); + } + + /// + /// 获取主视图路径集合 + /// + public virtual List GetMainViewPaths() { + return [Path]; + } +} \ No newline at end of file diff --git a/src/Util.Ui/Razor/Internal/RazorViewContainer.cs b/src/Util.Ui/Razor/Internal/RazorViewContainer.cs new file mode 100644 index 000000000..a2bea9b00 --- /dev/null +++ b/src/Util.Ui/Razor/Internal/RazorViewContainer.cs @@ -0,0 +1,148 @@ +namespace Util.Ui.Razor.Internal; + +/// +/// Razor视图容器 +/// +public class RazorViewContainer { + /// + /// Razor视图集合 + /// + private readonly Dictionary _views; + /// + /// 分部视图路径解析器 + /// + private readonly IPartViewPathResolver _partViewPathResolver; + /// + /// 视图内容解析器 + /// + private readonly IViewContentResolver _contentResolver; + + /// + /// 初始化Razor视图容器 + /// + /// 分部视图路径解析器 + /// 视图内容解析器 + public RazorViewContainer( IPartViewPathResolver partViewPathResolver, IViewContentResolver contentResolver = null ) { + _partViewPathResolver = partViewPathResolver ?? throw new ArgumentNullException( nameof( partViewPathResolver ) ); + _contentResolver = contentResolver ?? new ViewContentResolver(); + _views = []; + } + + /// + /// 获取全部视图 + /// + public List GetAllViews() { + return _views.Values.ToList(); + } + + /// + /// 获取全部主视图 + /// + public List GetMainViews() { + return _views.Values.Where( view => view is MainView ).ToList(); + } + + /// + /// 获取全部主视图路径 + /// + public List GetMainViewPaths() { + return GetMainViews().Select( t => t.Path ).ToList(); + } + + /// + /// 获取随机视图路径 + /// + public List GetRandomPaths() { + return Util.Helpers.Random.GetValues( _views.Values.Where( view => view is MainView ).Select( t => t.Path ), 3 ); + } + + /// + /// 查找视图 + /// + /// 视图路径 + public RazorView FindView( string path ) { + if ( path.IsEmpty() ) + return null; + return _views.TryGetValue( path.SafeString().ToLower(), out var view ) ? view : null; + } + + /// + /// 添加视图到容器 + /// + /// 视图 + public void AddView( RazorView view ) { + if ( view == null ) + return; + _views.TryAdd( view.Path.SafeString().ToLower(), view ); + } + + /// + /// 初始化视图容器 + /// + /// 视图路径列表 + public void Init( List paths ) { + if ( paths == null ) + return; + foreach ( var path in paths ) + AddView( path ); + } + + /// + /// 添加视图 + /// + /// 视图路径 + public RazorView AddView( string path ) { + if ( path.IsEmpty() ) + return null; + var content = GetContent( path ); + if ( content.IsEmpty() ) + return null; + RazorView view = FindOrCreateView( path, content ); + var partViewPaths = _partViewPathResolver.Resolve( path, content ) ?? []; + foreach ( var partViewPath in partViewPaths ) { + var partView = AddView( partViewPath ); + if ( partView != null ) + view.AddPartView( (PartView)partView ); + } + AddView( view ); + return view; + } + + /// + /// 获取Razor文件内容 + /// + protected string GetContent( string relativePath ) { + return _contentResolver.Resolve( relativePath ); + } + + /// + /// 查找或创建视图 + /// + protected RazorView FindOrCreateView( string path, string content ) { + var result = FindView( path ); + if ( result != null ) + return result; + return IsPartView( content ) ? new PartView( path ) : new MainView( path ); + } + + /// + /// 是否分部视图 + /// + /// 视图内容 + protected bool IsPartView( string content ) { + return content.SafeString().StartsWith( "@page" ) == false; + } + + /// + /// 获取关联的视图路径列表,如果是主视图,获取它本身的路径,如果是分部视图,获取引用它的主视图路径集合 + /// + /// 视图路径 + public List GetViewPaths( string path ) { + if ( path.IsEmpty() ) + return new List(); + var view = FindView( path ); + if ( view == null ) + return new List(); + return view.GetMainViewPaths(); + } +} \ No newline at end of file diff --git a/src/Util.Ui/Razor/Internal/ViewContentResolver.cs b/src/Util.Ui/Razor/Internal/ViewContentResolver.cs new file mode 100644 index 000000000..253a02cf5 --- /dev/null +++ b/src/Util.Ui/Razor/Internal/ViewContentResolver.cs @@ -0,0 +1,18 @@ +using Util.Helpers; +using File = Util.Helpers.File; + +namespace Util.Ui.Razor.Internal; + +/// +/// 视图内容解析器 +/// +public class ViewContentResolver : IViewContentResolver { + /// + public string Resolve( string path ) { + if ( path.IsEmpty() ) + return null; + if ( File.FileExists( path ) == false ) + path = Url.JoinPath( Util.Helpers.Common.GetCurrentDirectory(), path ); + return File.ReadToString( path ); + } +} \ No newline at end of file diff --git a/src/Util.Ui/Razor/PageRouteConvention.cs b/src/Util.Ui/Razor/PageRouteConvention.cs new file mode 100644 index 000000000..98bc70d8f --- /dev/null +++ b/src/Util.Ui/Razor/PageRouteConvention.cs @@ -0,0 +1,20 @@ +namespace Util.Ui.Razor; + +/// +/// 页面默认路由添加view前缀 +/// +public class PageRouteConvention : IPageRouteModelConvention { + /// + /// 修改默认路由 + /// + public void Apply( PageRouteModel model ) { + foreach( var selector in model.Selectors.ToList() ) { + var template = selector.AttributeRouteModel?.Template; + if( string.IsNullOrWhiteSpace( template ) ) + continue; + if( template == "Error" ) + continue; + selector.AttributeRouteModel.Template = $"view/{template}"; + } + } +} \ No newline at end of file diff --git a/src/Util.Ui/Razor/RazorOptions.cs b/src/Util.Ui/Razor/RazorOptions.cs new file mode 100644 index 000000000..bd12fd7a4 --- /dev/null +++ b/src/Util.Ui/Razor/RazorOptions.cs @@ -0,0 +1,51 @@ +namespace Util.Ui.Razor; + +/// +/// Razor配置 +/// +public class RazorOptions { + /// + /// Razor文件根路径,默认值: /ClientApp + /// + public string RazorRootDirectory { get; set; } = "/ClientApp"; + /// + /// Spa静态文件根路径,默认值: ClientApp + /// + public string RootPath { get; set; } = "ClientApp"; + /// + /// 是否在Razor页面运行时自动生成html文件 + /// + public bool IsGenerateHtml { get; set; } + /// + /// Razor生成Html文件的目录名称,默认值:html + /// + public string GenerateHtmlFolder { get; set; } = "html"; + /// + /// 是否启用Razor监视服务,默认值: true + /// + public bool EnableWatchRazor { get; set; } = true; + /// + /// Razor监听服务启动初始化的延迟时间,单位: 毫秒, 默认值:1000, 注意: 需要等待Web服务启动完成才能开始初始化 + /// + public int StartInitDelay { get; set; } = 1000; + /// + /// 修改Razor页面生成Html文件的延迟时间,单位: 毫秒, 默认值:300 ,注意: 延迟太短可能导致生成异常 + /// + public int HtmlRenderDelayOnRazorChange { get; set; } = 300; + /// + /// 启动Razor监视服务时是否预热,默认值: true + /// + public bool EnablePreheat { get; set; } = true; + /// + /// 启用Razor生成覆盖已存在的html文件,默认值: true + /// + public bool EnableOverrideHtml { get; set; } = true; + /// + /// 启动Razor监视服务时, 是否重新生成全部html文件,默认值: false + /// + public bool EnableGenerateAllHtml { get; set; } + /// + /// 启动超时间隔,默认值: 300 秒 + /// + public TimeSpan StartupTimeout { get; set; } = TimeSpan.FromSeconds( 300 ); +} \ No newline at end of file diff --git a/src/Util.Ui/Razor/RazorPageExtensions.cs b/src/Util.Ui/Razor/RazorPageExtensions.cs new file mode 100644 index 000000000..7618d00d9 --- /dev/null +++ b/src/Util.Ui/Razor/RazorPageExtensions.cs @@ -0,0 +1,17 @@ +namespace Util.Ui.Razor; + +/// +/// Razor页面扩展 +/// +public static class RazorPageExtensions { + /// + /// 添加约定 + /// + /// mvc生成器 + public static IMvcBuilder AddConventions( this IMvcBuilder builder ) { + return builder.AddRazorPagesOptions( options => { + options.Conventions.Add( new PageRouteConvention() ); + options.Conventions.AddFolderApplicationModelConvention( "/", model => model.Filters.Add( new GenerateHtmlFilter() ) ); + } ); + } +} \ No newline at end of file diff --git a/src/Util.Ui/Razor/RazorWatchService.cs b/src/Util.Ui/Razor/RazorWatchService.cs new file mode 100644 index 000000000..d81040714 --- /dev/null +++ b/src/Util.Ui/Razor/RazorWatchService.cs @@ -0,0 +1,328 @@ +using System.Reactive.Linq; +using System.Reactive.Subjects; +using Microsoft.Extensions.Configuration; +using Util.Helpers; +using Util.Ui.Razor.Internal; + +namespace Util.Ui.Razor; + +/// +/// Razor页面监视服务 +/// +public class RazorWatchService : IRazorWatchService { + /// + /// 是否启动完成 + /// + public static bool IsStartComplete { get; set; } + /// + /// 文件监视器 + /// + private readonly FileWatcher _watcher; + /// + /// Razor视图容器 + /// + private readonly RazorViewContainer _container; + /// + /// Http客户端 + /// + private readonly HttpClient _client; + /// + /// 操作描述符集合提供器 + /// + private readonly IActionDescriptorCollectionProvider _provider; + /// + /// Razor配置 + /// + private readonly RazorOptions _options; + /// + /// 是否生成缺失的html + /// + private bool _isGenerateMissingHtml; + /// + /// 新增视图事件 + /// + private readonly SequentialSimpleAsyncSubject _addViewSubject; + + /// + /// 初始化Razor页面监视服务 + /// + public RazorWatchService( IServiceProvider serviceProvider, IPartViewPathResolver resolver, HttpClient client, + IActionDescriptorCollectionProvider provider, IOptions options ) { + Ioc.SetServiceProviderAction( () => serviceProvider ); + _watcher = new FileWatcher(); + _client = client; + _provider = provider; + _options = options.Value; + _container = new RazorViewContainer( resolver ); + _addViewSubject = new SequentialSimpleAsyncSubject(); + client.Timeout = options.Value.StartupTimeout; + InitAddViewSubject(); + } + + /// + /// 初始化添加视图事件 + /// + private void InitAddViewSubject() { + _addViewSubject.Where( path => path.IsEmpty() == false ) + .Delay( TimeSpan.FromSeconds( 1 ) ) + .SubscribeAsync( async path => { + WriteLog( $"发现新增文件: {path}" ); + _container.AddView( path ); + EnableOverrideHtml( false ); + await Request( path ); + EnableOverrideHtml(); + await Task.Delay( 200 ); + } ); + } + + /// + /// 启用Html覆盖 + /// + protected void EnableOverrideHtml( bool isOverrideHtml = true ) { + _options.EnableOverrideHtml = isOverrideHtml; + } + + /// + /// 发送请求 + /// + /// 视图路径 + /// 是否写入日志 + /// 请求次数 + public async Task Request( string path, bool isWrite = true, int times = 0 ) { + try { + times++; + if ( times > 3 ) + return; + await Task.Delay( TimeSpan.FromMilliseconds( _options.HtmlRenderDelayOnRazorChange ) ); + var requestPath = Url.JoinPath( GetApplicationUrl(), "view", path.RemoveStart( _options.RazorRootDirectory ).RemoveEnd( ".cshtml" ) ); + WriteLog( $"发送请求: {requestPath}", isWrite ); + var response = await _client.GetAsync( requestPath ); + if ( response.IsSuccessStatusCode ) { + WriteLog( $"请求成功: {requestPath}", isWrite ); + return; + } + await Task.Delay( 2000 ); + if ( response.StatusCode == HttpStatusCode.NotFound ) { + await AddView( path ); + return; + } + await Request( path, isWrite, times ); + } + catch ( InvalidOperationException ) { + await Task.Delay( 2000 ); + await Request( path, isWrite, times ); + } + } + + /// + /// 获取应用地址 + /// + protected string GetApplicationUrl() { + var path = GetProjectPath( "Properties" ); + var builder = new ConfigurationBuilder() + .SetBasePath( path ) + .AddJsonFile( "launchSettings.json", true, false ); + var config = builder.Build(); + foreach ( var child in config.AsEnumerable() ) { + if ( child.Key.Contains( "applicationUrl", StringComparison.OrdinalIgnoreCase ) ) + return child.Value; + } + return null; + } + + /// + /// 添加视图 + /// + protected async Task AddView( string path ) { + await _addViewSubject.OnNextAsync( path ); + } + + /// + public virtual async Task StartAsync( CancellationToken cancellationToken ) { + WriteLog( "启动中..." ); + WriteLog( "准备初始化..." ); + InitRazorViewContainer(); + await Task.Factory.StartNew( async () => { + await Task.Delay( _options.StartInitDelay, cancellationToken ); + try { + await GenerateAllHtml(); + await GenerateMissingHtml(); + await Preheat(); + IsStartComplete = true; + WriteLog( "初始化完成." ); + await StartWatch(); + } + catch ( Exception exception ) { + WriteLog( exception.Message ); + throw; + } + + }, cancellationToken ); + } + + /// + /// 写入控制台日志 + /// + private void WriteLog( string content, bool isWrite = true ) { + if ( isWrite == false ) + return; + Console.WriteLine( $"dbug: {DateTime.Now.ToMillisecondString()} - Util应用框架 - Razor监听服务 - {content}" ); + } + + /// + /// 初始化Razor视图容器 + /// + protected virtual void InitRazorViewContainer() { + _container.Init( GetAllViewPaths() ); + } + + /// + /// 获取所有主视图路径列表 + /// + protected List GetAllViewPaths() { + var result = new List(); + var descriptors = GetPageActionDescriptors(); + foreach ( var descriptor in descriptors ) + result.Add( descriptor.RelativePath ); + return result.Distinct().ToList(); + } + + /// + /// 获取页面操作描述符列表 + /// + protected List GetPageActionDescriptors() { + return _provider.ActionDescriptors.Items.OfType().ToList(); + } + + /// + /// 重新生成全部html + /// + protected async Task GenerateAllHtml() { + if ( _options.EnableGenerateAllHtml == false ) + return; + WriteLog( "准备重新生成全部html..." ); + _isGenerateMissingHtml = true; + EnableGenerateHtml(); + var requestPath = Url.JoinPath( GetApplicationUrl(), "api/html" ); + var response = await _client.GetAsync( requestPath ); + if ( response.IsSuccessStatusCode ) { + WriteLog( "重新生成全部html完成..." ); + return; + } + var result = await response.Content.ReadAsStringAsync(); + WriteLog( "重新生成全部html失败..." ); + WriteLog( result ); + } + + /// + /// 生成缺失的html + /// + protected async Task GenerateMissingHtml() { + if ( _options.EnableGenerateAllHtml ) + return; + WriteLog( "准备生成缺失的html..." ); + EnableGenerateHtml(); + EnableOverrideHtml( false ); + var fileBasePath = GetProjectPath( _options.RazorRootDirectory ); + var files = Util.Helpers.File.GetAllFiles( fileBasePath, "*.html" ); + foreach ( var path in _container.GetMainViewPaths() ) { + if ( Exists( files.Select( t => t.FullName ).ToList(), path ) ) + continue; + _isGenerateMissingHtml = true; + await Request( path ); + } + EnableOverrideHtml(); + WriteLog( "生成缺失的html完成..." ); + } + + /// + /// 启用Html生成 + /// + protected void EnableGenerateHtml( bool isGenerateHtml = true ) { + _options.IsGenerateHtml = isGenerateHtml; + } + + /// + /// 获取项目路径 + /// + protected string GetProjectPath( string relativePath ) { + return Url.JoinPath( Common.GetCurrentDirectory(), relativePath ); + } + + /// + /// html是否存在 + /// + protected bool Exists( List htmlPaths, string razorPath ) { + if ( string.Equals( razorPath, $"{_options.RazorRootDirectory}/Error.cshtml", StringComparison.OrdinalIgnoreCase ) ) + return true; + var path = GenerateHtmlFilter.GetPath( razorPath.RemoveStart( _options.RazorRootDirectory ).RemoveEnd( ".cshtml" ), _options ); + return htmlPaths.Any( t => t.Replace( "\\", "/" ).EndsWith( path, StringComparison.OrdinalIgnoreCase ) ); + } + + /// + /// Razor页面预热 + /// + protected async Task Preheat() { + if ( _isGenerateMissingHtml ) + return; + if ( _options.EnablePreheat == false ) + return; + WriteLog( "Razor页面准备预热..." ); + EnableGenerateHtml( false ); + var paths = _container.GetRandomPaths(); + foreach ( var path in paths ) + await Request( path, false ); + WriteLog( "Razor页面预热完成..." ); + } + + /// + /// 启动监听器 + /// + protected virtual Task StartWatch() { + WriteLog( "开始监听..." ); + var path = GetProjectPath( _options.RazorRootDirectory.RemoveStart( "/" ) ); + _watcher.Path( path ) + .Filter( "*.cshtml" ) + .OnChangedAsync( async ( _, e ) => await GenerateAsync( e.FullPath ) ) + .OnRenamedAsync( async ( _, e ) => await GenerateAsync( e.FullPath ) ) + .Start(); + return Task.CompletedTask; + } + + /// + /// 生成Html + /// + private async Task GenerateAsync( string fullPath ) { + if ( IsIgnore( fullPath ) ) + return; + EnableGenerateHtml(); + var file = new FileInfo( fullPath ); + var path = file.FullName.Replace( Common.GetCurrentDirectory(), "" ).Replace( "\\", "/" ); + WriteLog( $"发现修改: {file.FullName.Replace( "\\", "/" )}" ); + var viewPaths = _container.GetViewPaths( path ); + if ( viewPaths == null || viewPaths.Count == 0 ) { + await AddView( path ); + return; + } + foreach ( var viewPath in viewPaths ) + await Request( viewPath ); + } + + /// + /// 是否忽略 + /// + protected bool IsIgnore( string path ) { + if ( path.EndsWith( ".cshtml", StringComparison.OrdinalIgnoreCase ) == false ) + return true; + if ( path.Contains( "_ViewImports", StringComparison.OrdinalIgnoreCase ) ) + return true; + return false; + } + + /// + public virtual Task StopAsync( CancellationToken cancellationToken ) { + WriteLog( "准备关闭服务..." ); + _watcher.Dispose(); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/Util.Ui/Renders/IRender.cs b/src/Util.Ui/Renders/IRender.cs new file mode 100644 index 000000000..7408afbcb --- /dev/null +++ b/src/Util.Ui/Renders/IRender.cs @@ -0,0 +1,11 @@ +namespace Util.Ui.Renders; + +/// +/// 渲染器 +/// +public interface IRender : IHtmlContent { + /// + /// 复制副本 + /// + IHtmlContent Clone(); +} \ No newline at end of file diff --git a/src/Util.Ui/Renders/RenderBase.cs b/src/Util.Ui/Renders/RenderBase.cs new file mode 100644 index 000000000..2b652a932 --- /dev/null +++ b/src/Util.Ui/Renders/RenderBase.cs @@ -0,0 +1,41 @@ +using Util.Ui.Builders; + +namespace Util.Ui.Renders; + +/// +/// 渲染器 +/// +public abstract class RenderBase : IRender { + /// + /// 标签生成器 + /// + private TagBuilder _builder; + + /// + /// 初始化渲染器 + /// + protected RenderBase() { + } + + /// + /// 标签生成器 + /// + protected TagBuilder Builder => _builder ??= GetTagBuilder(); + + /// + /// 获取标签生成器 + /// + protected abstract TagBuilder GetTagBuilder(); + + /// + /// 渲染 + /// + /// 流写入器 + /// 编码 + public virtual void WriteTo( TextWriter writer, HtmlEncoder encoder ) { + Builder.WriteTo( writer, encoder ); + } + + /// + public abstract IHtmlContent Clone(); +} \ No newline at end of file diff --git a/src/Util.Ui/Sources/Spa/AngularCli/AngularCliBuilder.cs b/src/Util.Ui/Sources/Spa/AngularCli/AngularCliBuilder.cs new file mode 100644 index 000000000..8e6586f56 --- /dev/null +++ b/src/Util.Ui/Sources/Spa/AngularCli/AngularCliBuilder.cs @@ -0,0 +1,79 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.Hosting; +using Util.Ui.Sources.Spa.Npm; +using Util.Ui.Sources.Spa.Prerendering; +using Util.Ui.Sources.Spa.Util; + +namespace Util.Ui.Sources.Spa.AngularCli; + +/// +/// Provides an implementation of that can build +/// an Angular application by invoking the Angular CLI. +/// +public class AngularCliBuilder : ISpaPrerendererBuilder +{ + private static readonly TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine + + private readonly string _scriptName; + + /// + /// Constructs an instance of . + /// + /// The name of the script in your package.json file that builds the server-side bundle for your Angular application. + public AngularCliBuilder(string npmScript) + { + if (string.IsNullOrEmpty(npmScript)) + { + throw new ArgumentException("Cannot be null or empty.", nameof(npmScript)); + } + + _scriptName = npmScript; + } + + /// + public async Task Build(ISpaBuilder spaBuilder) + { + var pkgManagerCommand = spaBuilder.Options.PackageManagerCommand; + var sourcePath = spaBuilder.Options.SourcePath; + if (string.IsNullOrEmpty(sourcePath)) + { + throw new InvalidOperationException($"To use {nameof(AngularCliBuilder)}, you must supply a non-empty value for the {nameof(SpaOptions.SourcePath)} property of {nameof(SpaOptions)} ."); + } + + var appBuilder = spaBuilder.ApplicationBuilder; + var applicationStoppingToken = appBuilder.ApplicationServices.GetRequiredService().ApplicationStopping; + var logger = LoggerFinder.GetOrCreateLogger( + appBuilder, + nameof(AngularCliBuilder)); + var diagnosticSource = appBuilder.ApplicationServices.GetRequiredService(); + var scriptRunner = new NodeScriptRunner( + sourcePath, + _scriptName, + "--watch", + null, + pkgManagerCommand, + diagnosticSource, + applicationStoppingToken); + scriptRunner.AttachToLogger(logger); + + using var stdOutReader = new EventedStreamStringReader(scriptRunner.StdOut); + using var stdErrReader = new EventedStreamStringReader( scriptRunner.StdErr ); + try { + await scriptRunner.StdOut.WaitForMatch( ["Date"] ); + } + catch ( EndOfStreamException ex ) { + throw new InvalidOperationException( + $"The {pkgManagerCommand} script '{_scriptName}' exited without indicating success.\n" + + $"Output was: {stdOutReader.ReadAsString()}\n" + + $"Error output was: {stdErrReader.ReadAsString()}", ex ); + } + catch ( OperationCanceledException ex ) { + throw new InvalidOperationException( + $"The {pkgManagerCommand} script '{_scriptName}' timed out without indicating success. " + + $"Output was: {stdOutReader.ReadAsString()}\n" + + $"Error output was: {stdErrReader.ReadAsString()}", ex ); + } + } +} diff --git a/src/Util.Ui/Sources/Spa/AngularCli/AngularCliMiddleware.cs b/src/Util.Ui/Sources/Spa/AngularCli/AngularCliMiddleware.cs new file mode 100644 index 000000000..7b0eb42d2 --- /dev/null +++ b/src/Util.Ui/Sources/Spa/AngularCli/AngularCliMiddleware.cs @@ -0,0 +1,107 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Util.Ui.Razor; +using Util.Ui.Sources.Spa.Npm; +using Util.Ui.Sources.Spa.Proxying; +using Util.Ui.Sources.Spa.Util; + +namespace Util.Ui.Sources.Spa.AngularCli; + +internal static class AngularCliMiddleware { + private const string LogCategoryName = "Microsoft.AspNetCore.SpaServices"; + + public static void Attach( ISpaBuilder spaBuilder, string scriptName ) { + var pkgManagerCommand = spaBuilder.Options.PackageManagerCommand; + var sourcePath = spaBuilder.Options.SourcePath; + var devServerPort = spaBuilder.Options.DevServerPort; + if ( string.IsNullOrEmpty( sourcePath ) ) { + throw new ArgumentException( "Property 'SourcePath' cannot be null or empty", nameof( spaBuilder ) ); + } + + if ( string.IsNullOrEmpty( scriptName ) ) { + throw new ArgumentException( "Cannot be null or empty", nameof( scriptName ) ); + } + + if ( devServerPort == default ) { + devServerPort = TcpPortFinder.FindAvailablePort(); + } + + // Start Angular CLI and attach to middleware pipeline + var appBuilder = spaBuilder.ApplicationBuilder; + var applicationStoppingToken = appBuilder.ApplicationServices.GetRequiredService().ApplicationStopping; + var logger = LoggerFinder.GetOrCreateLogger( appBuilder, LogCategoryName ); + var diagnosticSource = appBuilder.ApplicationServices.GetRequiredService(); + var angularCliServerInfoTask = GetUrl( devServerPort, applicationStoppingToken ); + spaBuilder.UseProxyToSpaDevelopmentServer( () => { + var timeout = spaBuilder.Options.StartupTimeout; + return angularCliServerInfoTask.WithTimeout( timeout, + $"The Angular CLI process did not start listening for requests " + + $"within the timeout period of {timeout.TotalSeconds} seconds. " + + $"Check the log output for error information." ); + } ); + Task.Factory.StartNew( async () => { + while ( true ) { + if ( RazorWatchService.IsStartComplete == false ) { + await Task.Delay( 500, applicationStoppingToken ); + continue; + } + await StartAngularCliServerAsync( sourcePath, scriptName, pkgManagerCommand, devServerPort, logger, diagnosticSource, applicationStoppingToken ); + return; + } + }, applicationStoppingToken ); + } + + private static async Task GetUrl( int portNumber, CancellationToken applicationStoppingToken ) { + var uri = new Uri( $"http://localhost:{portNumber}" ); + await WaitForAngularCliServerToAcceptRequests( uri, applicationStoppingToken ); + return uri; + } + + private static async Task StartAngularCliServerAsync( string sourcePath, string scriptName, string pkgManagerCommand, int portNumber, ILogger logger, DiagnosticSource diagnosticSource, CancellationToken applicationStoppingToken ) { + Console.WriteLine($"dbug: ׼Angular http://localhost:{portNumber}"); + if ( logger.IsEnabled( LogLevel.Information ) ) { + logger.LogInformation( $"Starting @angular/cli on port {portNumber}..." ); + } + + var scriptRunner = new NodeScriptRunner( + sourcePath, scriptName, $"--port {portNumber}", null, pkgManagerCommand, diagnosticSource, applicationStoppingToken ); + scriptRunner.AttachToLogger( logger ); + + bool openBrowserLine; + using var stdErrReader = new EventedStreamStringReader( scriptRunner.StdErr ); + try { + openBrowserLine = await scriptRunner.StdOut.WaitForMatch( ["Watch mode enabled", "Angular Live Development"] ); + } + catch ( EndOfStreamException ex ) { + throw new InvalidOperationException( + $"The {pkgManagerCommand} script '{scriptName}' exited without indicating that the " + + $"Angular CLI was listening for requests. The error output was: " + + $"{stdErrReader.ReadAsString()}", ex ); + } + } + + private static async Task WaitForAngularCliServerToAcceptRequests( Uri cliServerUri, CancellationToken applicationStoppingToken ) { + using var client = new HttpClient(); + var i = 0; + while ( true ) { + try { + i++; + var response = await client.SendAsync( new HttpRequestMessage( HttpMethod.Get, cliServerUri ), applicationStoppingToken ); + var content = await response.Content.ReadAsStringAsync( applicationStoppingToken ); + if ( content.IsEmpty() == false ) { + return; + } + await Task.Delay( 300, applicationStoppingToken ); + if ( i > 1000 ) { + return; + } + } + catch ( Exception ) { + // ignored + } + } + } +} diff --git a/src/Util.Ui/Sources/Spa/AngularCli/AngularCliMiddlewareExtensions.cs b/src/Util.Ui/Sources/Spa/AngularCli/AngularCliMiddlewareExtensions.cs new file mode 100644 index 000000000..402ddaa0a --- /dev/null +++ b/src/Util.Ui/Sources/Spa/AngularCli/AngularCliMiddlewareExtensions.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Util.Ui.Sources.Spa.AngularCli; + +/// +/// Extension methods for enabling Angular CLI middleware support. +/// +public static class AngularCliMiddlewareExtensions { + /// + /// ʹAngular + /// + public static void UseAngularCliServer( + this ISpaBuilder spaBuilder, + string npmScript ) { + ArgumentNullException.ThrowIfNull( spaBuilder ); + var spaOptions = spaBuilder.Options; + if ( string.IsNullOrEmpty( spaOptions.SourcePath ) ) { + throw new InvalidOperationException( $"To use {nameof( UseAngularCliServer )}, you must supply a non-empty value for the {nameof( SpaOptions.SourcePath )} property of {nameof( SpaOptions )} ." ); + } + AngularCliMiddleware.Attach( spaBuilder, npmScript ); + } +} diff --git a/src/Util.Ui/Sources/Spa/DefaultSpaBuilder.cs b/src/Util.Ui/Sources/Spa/DefaultSpaBuilder.cs new file mode 100644 index 000000000..522001091 --- /dev/null +++ b/src/Util.Ui/Sources/Spa/DefaultSpaBuilder.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Util.Ui.Sources.Spa; + +internal sealed class DefaultSpaBuilder : ISpaBuilder +{ + public IApplicationBuilder ApplicationBuilder { get; } + + public SpaOptions Options { get; } + + public DefaultSpaBuilder(IApplicationBuilder applicationBuilder, SpaOptions options) + { + ApplicationBuilder = applicationBuilder + ?? throw new ArgumentNullException(nameof(applicationBuilder)); + + Options = options + ?? throw new ArgumentNullException(nameof(options)); + } +} diff --git a/src/Util.Ui/Sources/Spa/ISpaBuilder.cs b/src/Util.Ui/Sources/Spa/ISpaBuilder.cs new file mode 100644 index 000000000..aeb5df0d9 --- /dev/null +++ b/src/Util.Ui/Sources/Spa/ISpaBuilder.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Util.Ui.Sources.Spa; + +/// +/// Defines a class that provides mechanisms for configuring the hosting +/// of a Single Page Application (SPA) and attaching middleware. +/// +public interface ISpaBuilder +{ + /// + /// The representing the middleware pipeline + /// in which the SPA is being hosted. + /// + IApplicationBuilder ApplicationBuilder { get; } + + /// + /// Describes configuration options for hosting a SPA. + /// + SpaOptions Options { get; } +} diff --git a/src/Util.Ui/Sources/Spa/Npm/NodeScriptRunner.cs b/src/Util.Ui/Sources/Spa/Npm/NodeScriptRunner.cs new file mode 100644 index 000000000..cd330459a --- /dev/null +++ b/src/Util.Ui/Sources/Spa/Npm/NodeScriptRunner.cs @@ -0,0 +1,163 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; +using Microsoft.Extensions.Logging; +using Util.Ui.Sources.Spa.Util; + +// This is under the NodeServices namespace because post 2.1 it will be moved to that package +namespace Util.Ui.Sources.Spa.Npm; + +/// +/// Executes the script entries defined in a package.json file, +/// capturing any output written to stdio. +/// +internal sealed class NodeScriptRunner : IDisposable +{ + private Process _npmProcess; + public EventedStreamReader StdOut { get; } + public EventedStreamReader StdErr { get; } + + private static readonly Regex AnsiColorRegex = new Regex("\x001b\\[[0-9;]*m", RegexOptions.None, TimeSpan.FromSeconds(1)); + + public NodeScriptRunner(string workingDirectory, string scriptName, string arguments, IDictionary envVars, string pkgManagerCommand, DiagnosticSource diagnosticSource, CancellationToken applicationStoppingToken) + { + if (string.IsNullOrEmpty(workingDirectory)) + { + throw new ArgumentException("Cannot be null or empty.", nameof(workingDirectory)); + } + + if (string.IsNullOrEmpty(scriptName)) + { + throw new ArgumentException("Cannot be null or empty.", nameof(scriptName)); + } + + if (string.IsNullOrEmpty(pkgManagerCommand)) + { + throw new ArgumentException("Cannot be null or empty.", nameof(pkgManagerCommand)); + } + + var exeToRun = pkgManagerCommand; + var completeArguments = $"run {scriptName} -- {arguments ?? string.Empty}"; + if (OperatingSystem.IsWindows()) + { + // On Windows, the node executable is a .cmd file, so it can't be executed + // directly (except with UseShellExecute=true, but that's no good, because + // it prevents capturing stdio). So we need to invoke it via "cmd /c". + exeToRun = "cmd"; + completeArguments = $"/c {pkgManagerCommand} {completeArguments}"; + } + + var processStartInfo = new ProcessStartInfo(exeToRun) + { + Arguments = completeArguments, + UseShellExecute = false, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + WorkingDirectory = workingDirectory + }; + + if (envVars != null) + { + foreach (var keyValuePair in envVars) + { + processStartInfo.Environment[keyValuePair.Key] = keyValuePair.Value; + } + } + + _npmProcess = LaunchNodeProcess(processStartInfo, pkgManagerCommand); + StdOut = new EventedStreamReader(_npmProcess.StandardOutput); + StdErr = new EventedStreamReader(_npmProcess.StandardError); + + applicationStoppingToken.Register(((IDisposable)this).Dispose); + + if (diagnosticSource.IsEnabled("Microsoft.AspNetCore.NodeServices.Npm.NpmStarted")) + { + WriteDiagnosticEvent( + diagnosticSource, + "Microsoft.AspNetCore.NodeServices.Npm.NpmStarted", + new + { + processStartInfo = processStartInfo, + process = _npmProcess + }); + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026", + Justification = "The values being passed into Write have the commonly used properties being preserved with DynamicDependency.")] + static void WriteDiagnosticEvent<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TValue>(DiagnosticSource diagnosticSource, string name, TValue value) + => diagnosticSource.Write(name, value); + } + + public void AttachToLogger(ILogger logger) + { + // When the node task emits complete lines, pass them through to the real logger + StdOut.OnReceivedLine += line => + { + if (!string.IsNullOrWhiteSpace(line) && logger.IsEnabled(LogLevel.Information)) + { + // Node tasks commonly emit ANSI colors, but it wouldn't make sense to forward + // those to loggers (because a logger isn't necessarily any kind of terminal) + logger.LogInformation(StripAnsiColors(line)); + } + }; + + StdErr.OnReceivedLine += line => + { + if (!string.IsNullOrWhiteSpace(line)) + { + logger.LogWarning(StripAnsiColors(line)); + } + }; + + // But when it emits incomplete lines, assume this is progress information and + // hence just pass it through to StdOut regardless of logger config. + StdErr.OnReceivedChunk += chunk => + { + Debug.Assert(chunk.Array != null); + + var containsNewline = Array.IndexOf( + chunk.Array, '\n', chunk.Offset, chunk.Count) >= 0; + if (!containsNewline) + { + Console.Write(chunk.Array, chunk.Offset, chunk.Count); + } + }; + } + + private static string StripAnsiColors(string line) + => AnsiColorRegex.Replace(line, string.Empty); + + private static Process LaunchNodeProcess(ProcessStartInfo startInfo, string commandName) + { + try + { + var process = Process.Start(startInfo)!; + + // See equivalent comment in OutOfProcessNodeInstance.cs for why + process.EnableRaisingEvents = true; + + return process; + } + catch (Exception ex) + { + var message = $"Failed to start '{commandName}'. To resolve this:.\n\n" + + $"[1] Ensure that '{commandName}' is installed and can be found in one of the PATH directories.\n" + + $" Current PATH enviroment variable is: { Environment.GetEnvironmentVariable("PATH") }\n" + + " Make sure the executable is in one of those directories, or update your PATH.\n\n" + + "[2] See the InnerException for further details of the cause."; + throw new InvalidOperationException(message, ex); + } + } + + void IDisposable.Dispose() + { + if (_npmProcess != null && !_npmProcess.HasExited) + { + _npmProcess.Kill(entireProcessTree: true); + _npmProcess = null; + } + } +} diff --git a/src/Util.Ui/Sources/Spa/Prerendering/ISpaPrerendererBuilder.cs b/src/Util.Ui/Sources/Spa/Prerendering/ISpaPrerendererBuilder.cs new file mode 100644 index 000000000..cbbdb7790 --- /dev/null +++ b/src/Util.Ui/Sources/Spa/Prerendering/ISpaPrerendererBuilder.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Util.Ui.Sources.Spa.Prerendering; + +/// +/// Represents the ability to build a Single Page Application (SPA) on demand +/// so that it can be prerendered. This is only intended to be used at development +/// time. In production, a SPA should already have been built during publishing. +/// +public interface ISpaPrerendererBuilder +{ + /// + /// Builds the Single Page Application so that a JavaScript entrypoint file + /// exists on disk. Prerendering middleware can then execute that file in + /// a Node environment. + /// + /// The . + /// A representing completion of the build process. + Task Build(ISpaBuilder spaBuilder); +} diff --git a/src/Util.Ui/Sources/Spa/Proxying/ConditionalProxyMiddleware.cs b/src/Util.Ui/Sources/Spa/Proxying/ConditionalProxyMiddleware.cs new file mode 100644 index 000000000..825d2988c --- /dev/null +++ b/src/Util.Ui/Sources/Spa/Proxying/ConditionalProxyMiddleware.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.Hosting; + +namespace Util.Ui.Sources.Spa.Proxying; + +// This duplicates and updates the proxying logic in SpaServices so that we can update +// the project templates without waiting for 2.1 to ship. When 2.1 is ready to ship, +// merge the additional proxying features (e.g., proxying websocket connections) back +// into the SpaServices proxying code. It's all internal. +internal sealed class ConditionalProxyMiddleware +{ + private readonly RequestDelegate _next; + private readonly Task _baseUriTask; + private readonly string _pathPrefix; + private readonly bool _pathPrefixIsRoot; + private readonly HttpClient _httpClient; + private readonly CancellationToken _applicationStoppingToken; + + public ConditionalProxyMiddleware( + RequestDelegate next, + string pathPrefix, + TimeSpan requestTimeout, + Task baseUriTask, + IHostApplicationLifetime applicationLifetime) + { + if (!pathPrefix.StartsWith('/')) + { + pathPrefix = "/" + pathPrefix; + } + + _next = next; + _pathPrefix = pathPrefix; + _pathPrefixIsRoot = string.Equals(_pathPrefix, "/", StringComparison.Ordinal); + _baseUriTask = baseUriTask; + _httpClient = SpaProxy.CreateHttpClientForProxy(requestTimeout); + _applicationStoppingToken = applicationLifetime.ApplicationStopping; + } + + public Task Invoke(HttpContext context) + { + if (context.Request.Path.StartsWithSegments(_pathPrefix) || _pathPrefixIsRoot) + { + return InvokeCore(context); + } + return _next.Invoke(context); + } + + private async Task InvokeCore(HttpContext context) + { + var didProxyRequest = await SpaProxy.PerformProxyRequest( + context, _httpClient, _baseUriTask, _applicationStoppingToken, proxy404s: false); + if (didProxyRequest) + { + return; + } + + // Not a request we can proxy + await _next.Invoke(context); + } +} diff --git a/src/Util.Ui/Sources/Spa/Proxying/SpaProxy.cs b/src/Util.Ui/Sources/Spa/Proxying/SpaProxy.cs new file mode 100644 index 000000000..349fe890d --- /dev/null +++ b/src/Util.Ui/Sources/Spa/Proxying/SpaProxy.cs @@ -0,0 +1,304 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Net.WebSockets; + +namespace Util.Ui.Sources.Spa.Proxying; + +// This duplicates and updates the proxying logic in SpaServices so that we can update +// the project templates without waiting for 2.1 to ship. When 2.1 is ready to ship, +// remove the old ConditionalProxy.cs from SpaServices and replace its usages with this. +// Doesn't affect public API surface - it's all internal. +internal static class SpaProxy +{ + private const int DefaultWebSocketBufferSize = 4096; + private const int StreamCopyBufferSize = 81920; + + // https://github.com/dotnet/aspnetcore/issues/16797 + private static readonly HashSet NotForwardedHttpHeaders = new HashSet( + new[] { "Connection" }, + StringComparer.OrdinalIgnoreCase + ); + + // Don't forward User-Agent/Accept because of https://github.com/aspnet/JavaScriptServices/issues/1469 + // Others just aren't applicable in proxy scenarios + private static readonly HashSet NotForwardedWebSocketHeaders = new ( + ["Accept", "Connection", "Host", "User-Agent", "Upgrade", "Sec-WebSocket-Key", "Sec-WebSocket-Protocol", "Sec-WebSocket-Version"], + StringComparer.OrdinalIgnoreCase + ); + + // In case the connection to the client is HTTP/2 or HTTP/3 and to the server HTTP/1.1 or less, let's get rid of the HTTP/1.1 only headers + private static readonly HashSet InvalidH2H3Headers = new( + ["Connection", "Transfer-Encoding", "Keep-Alive", "Upgrade", "Proxy-Connection"], + StringComparer.OrdinalIgnoreCase + ); + + public static HttpClient CreateHttpClientForProxy(TimeSpan requestTimeout) + { + var handler = new HttpClientHandler + { + AllowAutoRedirect = false, + UseCookies = false, + }; + + return new HttpClient(handler) + { + Timeout = requestTimeout + }; + } + + public static async Task PerformProxyRequest( + HttpContext context, + HttpClient httpClient, + Task baseUriTask, + CancellationToken applicationStoppingToken, + bool proxy404s) + { + // Stop proxying if either the server or client wants to disconnect + var proxyCancellationToken = CancellationTokenSource.CreateLinkedTokenSource( + context.RequestAborted, + applicationStoppingToken).Token; + + // We allow for the case where the target isn't known ahead of time, and want to + // delay proxied requests until the target becomes known. This is useful, for example, + // when proxying to Angular CLI middleware: we won't know what port it's listening + // on until it finishes starting up. + var baseUri = await baseUriTask; + var baseUriAsString = baseUri.ToString(); + var targetUri = new Uri((baseUriAsString.EndsWith("/", StringComparison.OrdinalIgnoreCase) ? baseUriAsString[..^1] : baseUriAsString) + + context.Request.Path + + context.Request.QueryString); + + try + { + if (context.WebSockets.IsWebSocketRequest) + { + await AcceptProxyWebSocketRequest(context, ToWebSocketScheme(targetUri), proxyCancellationToken); + return true; + } + else { + using var requestMessage = CreateProxyHttpRequest(context, targetUri); + using var responseMessage = await httpClient.SendAsync( + requestMessage, + HttpCompletionOption.ResponseHeadersRead, + proxyCancellationToken); + if (!proxy404s) + { + if (responseMessage.StatusCode == HttpStatusCode.NotFound) + { + // We're not proxying 404s, i.e., we want to resume the middleware pipeline + // and let some other middleware handle this. + return false; + } + } + + await CopyProxyHttpResponse(context, responseMessage, proxyCancellationToken); + return true; + } + } + catch (OperationCanceledException) + { + // If we're aborting because either the client disconnected, or the server + // is shutting down, don't treat this as an error. + return true; + } + catch (IOException) + { + // This kind of exception can also occur if a proxy read/write gets interrupted + // due to the process shutting down. + return true; + } + catch (HttpRequestException ex) + { + throw new HttpRequestException( + $"Failed to proxy the request to {targetUri.ToString()}, because the request to " + + $"the proxy target failed. Check that the proxy target server is running and " + + $"accepting requests to {baseUri.ToString()}.\n\n" + + $"The underlying exception message was '{ex.Message}'." + + $"Check the InnerException for more details.", ex); + } + } + + private static HttpRequestMessage CreateProxyHttpRequest(HttpContext context, Uri uri) + { + var request = context.Request; + + var requestMessage = new HttpRequestMessage(); + var requestMethod = request.Method; + if (!HttpMethods.IsGet(requestMethod) && + !HttpMethods.IsHead(requestMethod) && + !HttpMethods.IsDelete(requestMethod) && + !HttpMethods.IsTrace(requestMethod)) + { + var streamContent = new StreamContent(request.Body); + requestMessage.Content = streamContent; + } + + // Copy the request headers + foreach (var header in request.Headers) + { + if (NotForwardedHttpHeaders.Contains(header.Key)) + { + continue; + } + + if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()) && requestMessage.Content != null) + { + requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()); + } + } + + requestMessage.Headers.Host = uri.Authority; + requestMessage.RequestUri = uri; + requestMessage.Method = new HttpMethod(request.Method); + + return requestMessage; + } + + private static async Task CopyProxyHttpResponse(HttpContext context, HttpResponseMessage responseMessage, CancellationToken cancellationToken) + { + context.Response.StatusCode = (int)responseMessage.StatusCode; + foreach (var header in responseMessage.Headers) + { + if ((HttpProtocol.IsHttp2(context.Request.Protocol) || HttpProtocol.IsHttp3(context.Request.Protocol)) + && InvalidH2H3Headers.Contains(header.Key)) + { + continue; + } + context.Response.Headers[header.Key] = header.Value.ToArray(); + } + + foreach (var header in responseMessage.Content.Headers) + { + context.Response.Headers[header.Key] = header.Value.ToArray(); + } + + // SendAsync removes chunking from the response. This removes the header so it doesn't expect a chunked response. + context.Response.Headers.Remove("transfer-encoding"); + + using (var responseStream = await responseMessage.Content.ReadAsStreamAsync(cancellationToken)) + { + await responseStream.CopyToAsync(context.Response.Body, StreamCopyBufferSize, cancellationToken); + } + } + + private static Uri ToWebSocketScheme(Uri uri) + { + ArgumentNullException.ThrowIfNull(uri); + + var uriBuilder = new UriBuilder(uri); + if (string.Equals(uriBuilder.Scheme, "https", StringComparison.OrdinalIgnoreCase)) + { + uriBuilder.Scheme = "wss"; + } + else if (string.Equals(uriBuilder.Scheme, "http", StringComparison.OrdinalIgnoreCase)) + { + uriBuilder.Scheme = "ws"; + } + + return uriBuilder.Uri; + } + + private static async Task AcceptProxyWebSocketRequest(HttpContext context, Uri destinationUri, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(destinationUri); + + using (var client = new ClientWebSocket()) + { + foreach (var protocol in context.WebSockets.WebSocketRequestedProtocols) + { + client.Options.AddSubProtocol(protocol); + } + foreach (var headerEntry in context.Request.Headers) + { + if (!NotForwardedWebSocketHeaders.Contains(headerEntry.Key)) + { + try + { + client.Options.SetRequestHeader(headerEntry.Key, headerEntry.Value); + } + catch (ArgumentException) + { + // On net462, certain header names are reserved and can't be set. + // We filter out the known ones via the test above, but there could + // be others arbitrarily set by the client. It's not helpful to + // consider it an error, so just skip non-forwardable headers. + // The perf implications of handling this via a catch aren't an + // issue since this is a dev-time only feature. + } + } + } + + try + { + // Note that this is not really good enough to make Websockets work with + // Angular CLI middleware. For some reason, ConnectAsync takes over 1 second, + // on Windows, by which time the logic in SockJS has already timed out and made + // it fall back on some other transport (xhr_streaming, usually). It's fine + // on Linux though, completing almost instantly. + // + // The slowness on Windows does not cause a problem though, because the transport + // fallback logic works correctly and doesn't surface any errors, but it would be + // better if ConnectAsync was fast enough and the initial Websocket transport + // could actually be used. + await client.ConnectAsync(destinationUri, cancellationToken); + } + catch (WebSocketException) + { + context.Response.StatusCode = 400; + return false; + } + + using var server = await context.WebSockets.AcceptWebSocketAsync(client.SubProtocol); + var bufferSize = DefaultWebSocketBufferSize; + await Task.WhenAll( + PumpWebSocket(client, server, bufferSize, cancellationToken), + PumpWebSocket(server, client, bufferSize, cancellationToken)); + + return true; + } + } + + private static async Task PumpWebSocket(WebSocket source, WebSocket destination, int bufferSize, CancellationToken cancellationToken) + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(bufferSize); + + var buffer = new byte[bufferSize]; + + while (true) + { + // Because WebSocket.ReceiveAsync doesn't work well with CancellationToken (it doesn't + // actually exit when the token notifies, at least not in the 'server' case), use + // polling. The perf might not be ideal, but this is a dev-time feature only. + var resultTask = source.ReceiveAsync(new ArraySegment(buffer), cancellationToken); + while (true) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + if (resultTask.IsCompleted) + { + break; + } + + await Task.Delay(100, cancellationToken); + } + + var result = resultTask.Result; // We know it's completed already + if (result.MessageType == WebSocketMessageType.Close) + { + if (destination.State == WebSocketState.Open || destination.State == WebSocketState.CloseReceived) + { + await destination.CloseOutputAsync(source.CloseStatus!.Value, source.CloseStatusDescription, cancellationToken); + } + + return; + } + + await destination.SendAsync(new ArraySegment(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, cancellationToken); + } + } +} diff --git a/src/Util.Ui/Sources/Spa/Proxying/SpaProxyingExtensions.cs b/src/Util.Ui/Sources/Spa/Proxying/SpaProxyingExtensions.cs new file mode 100644 index 000000000..9b29e846f --- /dev/null +++ b/src/Util.Ui/Sources/Spa/Proxying/SpaProxyingExtensions.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.Extensions.Hosting; + +namespace Util.Ui.Sources.Spa.Proxying; + +/// +/// Extension methods for proxying requests to a local SPA development server during +/// development. Not for use in production applications. +/// +public static class SpaProxyingExtensions { + /// + /// Configures the application to forward incoming requests to a local Single Page + /// Application (SPA) development server. This is only intended to be used during + /// development. Do not enable this middleware in production applications. + /// + /// The . + /// The target base URI to which requests should be proxied. + public static void UseProxyToSpaDevelopmentServer( + this ISpaBuilder spaBuilder, + string baseUri ) { + UseProxyToSpaDevelopmentServer( + spaBuilder, + new Uri( baseUri ) ); + } + + /// + /// Configures the application to forward incoming requests to a local Single Page + /// Application (SPA) development server. This is only intended to be used during + /// development. Do not enable this middleware in production applications. + /// + /// The . + /// The target base URI to which requests should be proxied. + public static void UseProxyToSpaDevelopmentServer( + this ISpaBuilder spaBuilder, + Uri baseUri ) { + UseProxyToSpaDevelopmentServer( + spaBuilder, + () => Task.FromResult( baseUri ) ); + } + + /// + /// Configures the application to forward incoming requests to a local Single Page + /// Application (SPA) development server. This is only intended to be used during + /// development. Do not enable this middleware in production applications. + /// + /// The . + /// A callback that will be invoked on each request to supply a that resolves with the target base URI to which requests should be proxied. + public static void UseProxyToSpaDevelopmentServer( + this ISpaBuilder spaBuilder, + Func> baseUriTaskFactory ) { + var applicationBuilder = spaBuilder.ApplicationBuilder; + var applicationStoppingToken = GetStoppingToken( applicationBuilder ); + + // Since we might want to proxy WebSockets requests (e.g., by default, AngularCliMiddleware + // requires it), enable it for the app + applicationBuilder.UseWebSockets(); + + // It's important not to time out the requests, as some of them might be to + // server-sent event endpoints or similar, where it's expected that the response + // takes an unlimited time and never actually completes + var neverTimeOutHttpClient = + SpaProxy.CreateHttpClientForProxy( Timeout.InfiniteTimeSpan ); + + applicationBuilder.Use(async(context, next) => { + if ( context.Request.Path.Value != null && context.Request.Path.Value.StartsWith( "/view/" ) ) { + await next(); + return; + } + if ( context.Request.Path.Value != null && context.Request.Path.Value.StartsWith( "/api/" ) ) { + await next(); + return; + } + await SpaProxy.PerformProxyRequest( + context, neverTimeOutHttpClient, baseUriTaskFactory(), applicationStoppingToken, + proxy404s: true ); + } ); + } + + private static CancellationToken GetStoppingToken( IApplicationBuilder appBuilder ) { + var applicationLifetime = appBuilder + .ApplicationServices + .GetRequiredService(); + return applicationLifetime.ApplicationStopping; + } +} diff --git a/src/Util.Ui/Sources/Spa/SpaApplicationBuilderExtensions.cs b/src/Util.Ui/Sources/Spa/SpaApplicationBuilderExtensions.cs new file mode 100644 index 000000000..554d2f35f --- /dev/null +++ b/src/Util.Ui/Sources/Spa/SpaApplicationBuilderExtensions.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Util.Ui.Sources.Spa; + +/// +/// Provides extension methods used for configuring an application to +/// host a client-side Single Page Application (SPA). +/// +public static class SpaApplicationBuilderExtensions { + /// + /// Handles all requests from this point in the middleware chain by returning + /// the default page for the Single Page Application (SPA). + /// + /// This middleware should be placed late in the chain, so that other middleware + /// for serving static files, MVC actions, etc., takes precedence. + /// + /// The . + /// + /// This callback will be invoked so that additional middleware can be registered within + /// the context of this SPA. + /// + public static void UseAngular( this IApplicationBuilder app, Action configuration ) { + ArgumentNullException.ThrowIfNull( configuration ); + var optionsProvider = app.ApplicationServices.GetService>()!; + var options = new SpaOptions( optionsProvider.Value ); + var spaBuilder = new DefaultSpaBuilder( app, options ); + configuration.Invoke( spaBuilder ); + SpaDefaultPageMiddleware.Attach( spaBuilder ); + } +} diff --git a/src/Util.Ui/Sources/Spa/SpaDefaultPageMiddleware.cs b/src/Util.Ui/Sources/Spa/SpaDefaultPageMiddleware.cs new file mode 100644 index 000000000..32ba3f035 --- /dev/null +++ b/src/Util.Ui/Sources/Spa/SpaDefaultPageMiddleware.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Util.Ui.Sources.Spa.StaticFiles; + +namespace Util.Ui.Sources.Spa; + +internal sealed class SpaDefaultPageMiddleware +{ + public static void Attach(ISpaBuilder spaBuilder) + { + ArgumentNullException.ThrowIfNull(spaBuilder); + + var app = spaBuilder.ApplicationBuilder; + var options = spaBuilder.Options; + + // Rewrite all requests to the default page + app.Use((context, next) => + { + // If we have an Endpoint, then this is a deferred match - just noop. + if (context.GetEndpoint() != null) + { + return next(context); + } + + context.Request.Path = options.DefaultPage; + return next(context); + }); + + // Serve it as a static file + // Developers who need to host more than one SPA with distinct default pages can + // override the file provider + app.UseSpaStaticFilesInternal( + options.DefaultPageStaticFileOptions ?? new StaticFileOptions(), + allowFallbackOnServingWebRootFiles: true); + } +} diff --git a/src/Util.Ui/Sources/Spa/SpaOptions.cs b/src/Util.Ui/Sources/Spa/SpaOptions.cs new file mode 100644 index 000000000..17ca754b0 --- /dev/null +++ b/src/Util.Ui/Sources/Spa/SpaOptions.cs @@ -0,0 +1,99 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Util.Ui.Sources.Spa; + +/// +/// Describes options for hosting a Single Page Application (SPA). +/// +public class SpaOptions +{ + private PathString _defaultPage = "/index.html"; + private string _packageManagerCommand = "npm"; + + /// + /// Constructs a new instance of . + /// + public SpaOptions() + { + } + + /// + /// Constructs a new instance of . + /// + /// An instance of from which values should be copied. + internal SpaOptions(SpaOptions copyFromOptions) + { + _defaultPage = copyFromOptions.DefaultPage; + _packageManagerCommand = copyFromOptions.PackageManagerCommand; + DefaultPageStaticFileOptions = copyFromOptions.DefaultPageStaticFileOptions; + SourcePath = copyFromOptions.SourcePath; + DevServerPort = copyFromOptions.DevServerPort; + } + + /// + /// Gets or sets the URL of the default page that hosts your SPA user interface. + /// The default value is "/index.html". + /// + public PathString DefaultPage + { + get => _defaultPage; + set + { + if (string.IsNullOrEmpty(value.Value)) + { + throw new ArgumentException($"The value for {nameof(DefaultPage)} cannot be null or empty."); + } + + _defaultPage = value; + } + } + + /// + /// Gets or sets the that supplies content + /// for serving the SPA's default page. + /// + /// If not set, a default file provider will read files from the + /// , which by default is + /// the wwwroot directory. + /// + public StaticFileOptions DefaultPageStaticFileOptions { get; set; } + + /// + /// Gets or sets the path, relative to the application working directory, + /// of the directory that contains the SPA source files during + /// development. The directory may not exist in published applications. + /// + public string SourcePath { get; set; } + + /// + /// Controls whether the development server should be used with a dynamic or fixed port. + /// + public int DevServerPort { get; set; } + + /// + /// Gets or sets the name of the package manager executable, (e.g npm, + /// yarn) to run the SPA. + /// + /// The default value is 'npm'. + /// + public string PackageManagerCommand + { + get => _packageManagerCommand; + set + { + if (string.IsNullOrEmpty(value)) + { + throw new ArgumentException($"The value for {nameof(PackageManagerCommand)} cannot be null or empty."); + } + + _packageManagerCommand = value; + } + } + + /// + /// Gets or sets the maximum duration that a request will wait for the SPA + /// to become ready to serve to the client. + /// + public TimeSpan StartupTimeout { get; set; } = TimeSpan.FromSeconds(120); +} diff --git a/src/Util.Ui/Sources/Spa/StaticFiles/DefaultSpaStaticFileProvider.cs b/src/Util.Ui/Sources/Spa/StaticFiles/DefaultSpaStaticFileProvider.cs new file mode 100644 index 000000000..278bf54e2 --- /dev/null +++ b/src/Util.Ui/Sources/Spa/StaticFiles/DefaultSpaStaticFileProvider.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.FileProviders; + +namespace Util.Ui.Sources.Spa.StaticFiles; + +/// +/// Provides an implementation of that supplies +/// physical files at a location configured using . +/// +internal sealed class DefaultSpaStaticFileProvider : ISpaStaticFileProvider +{ + private readonly IFileProvider _fileProvider; + + public DefaultSpaStaticFileProvider( + IServiceProvider serviceProvider, + SpaStaticFilesOptions options) + { + ArgumentNullException.ThrowIfNull(options); + + if (string.IsNullOrEmpty(options.RootPath)) + { + throw new ArgumentException($"The {nameof(options.RootPath)} property " + + $"of {nameof(options)} cannot be null or empty."); + } + + var env = serviceProvider.GetRequiredService(); + var absoluteRootPath = Path.Combine( + env.ContentRootPath, + options.RootPath); + + // PhysicalFileProvider will throw if you pass a non-existent path, + // but we don't want that scenario to be an error because for SPA + // scenarios, it's better if non-existing directory just means we + // don't serve any static files. + if (Directory.Exists(absoluteRootPath)) + { + _fileProvider = new PhysicalFileProvider(absoluteRootPath); + } + } + + public IFileProvider FileProvider => _fileProvider; +} diff --git a/src/Util.Ui/Sources/Spa/StaticFiles/ISpaStaticFileProvider.cs b/src/Util.Ui/Sources/Spa/StaticFiles/ISpaStaticFileProvider.cs new file mode 100644 index 000000000..bd5bb72c4 --- /dev/null +++ b/src/Util.Ui/Sources/Spa/StaticFiles/ISpaStaticFileProvider.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.FileProviders; + +namespace Util.Ui.Sources.Spa.StaticFiles; + +/// +/// Represents a service that can provide static files to be served for a Single Page +/// Application (SPA). +/// +public interface ISpaStaticFileProvider +{ + /// + /// Gets the file provider, if available, that supplies the static files for the SPA. + /// The value is null if no file provider is available. + /// + IFileProvider FileProvider { get; } +} diff --git a/src/Util.Ui/Sources/Spa/StaticFiles/SpaStaticFilesExtensions.cs b/src/Util.Ui/Sources/Spa/StaticFiles/SpaStaticFilesExtensions.cs new file mode 100644 index 000000000..3037390ad --- /dev/null +++ b/src/Util.Ui/Sources/Spa/StaticFiles/SpaStaticFilesExtensions.cs @@ -0,0 +1,131 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.FileProviders; + +namespace Util.Ui.Sources.Spa.StaticFiles; + +/// +/// Extension methods for configuring an application to serve static files for a +/// Single Page Application (SPA). +/// +public static class SpaStaticFilesExtensions +{ + /// + /// Registers an service that can provide static + /// files to be served for a Single Page Application (SPA). + /// + /// The . + /// If specified, this callback will be invoked to set additional configuration options. + public static void AddSpaStaticFiles( + this IServiceCollection services, + Action configuration = null) + { + services.AddSingleton(serviceProvider => + { + // Use the options configured in DI (or blank if none was configured) + var optionsProvider = serviceProvider.GetService>()!; + var options = optionsProvider.Value; + + // Allow the developer to perform further configuration + configuration?.Invoke(options); + + if (string.IsNullOrEmpty(options.RootPath)) + { + throw new InvalidOperationException($"No {nameof(SpaStaticFilesOptions.RootPath)} " + + $"was set on the {nameof(SpaStaticFilesOptions)}."); + } + + return new DefaultSpaStaticFileProvider(serviceProvider, options); + }); + } + + /// + /// Configures the application to serve static files for a Single Page Application (SPA). + /// The files will be located using the registered service. + /// + /// The . + public static void UseSpaStaticFiles(this IApplicationBuilder applicationBuilder) + { + UseSpaStaticFiles(applicationBuilder, new StaticFileOptions()); + } + + /// + /// Configures the application to serve static files for a Single Page Application (SPA). + /// The files will be located using the registered service. + /// + /// The . + /// Specifies options for serving the static files. + public static void UseSpaStaticFiles(this IApplicationBuilder applicationBuilder, StaticFileOptions options) + { + ArgumentNullException.ThrowIfNull(applicationBuilder); + ArgumentNullException.ThrowIfNull(options); + + UseSpaStaticFilesInternal(applicationBuilder, + staticFileOptions: options, + allowFallbackOnServingWebRootFiles: false); + } + + internal static void UseSpaStaticFilesInternal( + this IApplicationBuilder app, + StaticFileOptions staticFileOptions, + bool allowFallbackOnServingWebRootFiles) + { + ArgumentNullException.ThrowIfNull(staticFileOptions); + + // If the file provider was explicitly supplied, that takes precedence over any other + // configured file provider. This is most useful if the application hosts multiple SPAs + // (via multiple calls to UseSpa()), so each needs to serve its own separate static files + // instead of using AddSpaStaticFiles/UseSpaStaticFiles. + // But if no file provider was specified, try to get one from the DI config. + if (staticFileOptions.FileProvider == null) + { + var shouldServeStaticFiles = ShouldServeStaticFiles( + app, + allowFallbackOnServingWebRootFiles, + out var fileProviderOrDefault); + if (shouldServeStaticFiles) + { + staticFileOptions.FileProvider = fileProviderOrDefault; + } + else + { + // The registered ISpaStaticFileProvider says we shouldn't + // serve static files + return; + } + } + + app.UseStaticFiles(staticFileOptions); + } + + private static bool ShouldServeStaticFiles( + IApplicationBuilder app, + bool allowFallbackOnServingWebRootFiles, + out IFileProvider fileProviderOrDefault) + { + var spaStaticFilesService = app.ApplicationServices.GetService(); + if (spaStaticFilesService != null) + { + // If an ISpaStaticFileProvider was configured but it says no IFileProvider is available + // (i.e., it supplies 'null'), this implies we should not serve any static files. This + // is typically the case in development when SPA static files are being served from a + // SPA development server (e.g., Angular CLI or create-react-app), in which case no + // directory of prebuilt files will exist on disk. + fileProviderOrDefault = spaStaticFilesService.FileProvider; + return fileProviderOrDefault != null; + } + else if (!allowFallbackOnServingWebRootFiles) + { + throw new InvalidOperationException($"To use {nameof(UseSpaStaticFiles)}, you must " + + $"first register an {nameof(ISpaStaticFileProvider)} in the service provider, typically " + + $"by calling services.{nameof(AddSpaStaticFiles)}."); + } + else + { + // Fall back on serving wwwroot + fileProviderOrDefault = null; + return true; + } + } +} diff --git a/src/Util.Ui/Sources/Spa/StaticFiles/SpaStaticFilesOptions.cs b/src/Util.Ui/Sources/Spa/StaticFiles/SpaStaticFilesOptions.cs new file mode 100644 index 000000000..3f2152f81 --- /dev/null +++ b/src/Util.Ui/Sources/Spa/StaticFiles/SpaStaticFilesOptions.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Util.Ui.Sources.Spa.StaticFiles; + +/// +/// Represents options for serving static files for a Single Page Application (SPA). +/// +public class SpaStaticFilesOptions +{ + /// + /// Gets or sets the path, relative to the application root, of the directory in which + /// the physical files are located. + /// + /// If the specified directory does not exist, then the + /// + /// middleware will not serve any static files. + /// + public string RootPath { get; set; } = default!; +} diff --git a/src/Util.Ui/Sources/Spa/Util/EventedStreamReader.cs b/src/Util.Ui/Sources/Spa/Util/EventedStreamReader.cs new file mode 100644 index 000000000..2a7cabbbb --- /dev/null +++ b/src/Util.Ui/Sources/Spa/Util/EventedStreamReader.cs @@ -0,0 +1,130 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text.RegularExpressions; + +namespace Util.Ui.Sources.Spa.Util; + +/// +/// Wraps a to expose an evented API, issuing notifications +/// when the stream emits partial lines, completed lines, or finally closes. +/// +internal sealed class EventedStreamReader +{ + public delegate void OnReceivedChunkHandler(ArraySegment chunk); + public delegate void OnReceivedLineHandler(string line); + public delegate void OnStreamClosedHandler(); + + public event OnReceivedChunkHandler OnReceivedChunk; + public event OnReceivedLineHandler OnReceivedLine; + public event OnStreamClosedHandler OnStreamClosed; + + private readonly StreamReader _streamReader; + private readonly StringBuilder _linesBuffer; + + public EventedStreamReader(StreamReader streamReader) + { + _streamReader = streamReader ?? throw new ArgumentNullException(nameof(streamReader)); + _linesBuffer = new StringBuilder(); + Task.Factory.StartNew(Run, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); + } + + public Task WaitForMatch(string[] contents) + { + var tcs = new TaskCompletionSource(); + var completionLock = new object(); + + OnReceivedLineHandler onReceivedLineHandler = null; + OnStreamClosedHandler onStreamClosedHandler = null; + + void ResolveIfStillPending(Action applyResolution) + { + lock (completionLock) + { + if (!tcs.Task.IsCompleted) + { + OnReceivedLine -= onReceivedLineHandler; + OnStreamClosed -= onStreamClosedHandler; + applyResolution(); + } + } + } + + onReceivedLineHandler = line => { + + if ( contents.Any( content => line.Contains( content,StringComparison.OrdinalIgnoreCase ) ) ) { + Console.WriteLine( $"dbug: {line}" ); + ResolveIfStillPending( () => tcs.SetResult( true ) ); + } + }; + + onStreamClosedHandler = () => + { + ResolveIfStillPending(() => tcs.SetException(new EndOfStreamException())); + }; + + OnReceivedLine += onReceivedLineHandler; + OnStreamClosed += onStreamClosedHandler; + + return tcs.Task; + } + + private async Task Run() + { + var buf = new char[8 * 1024]; + while (true) + { + var chunkLength = await _streamReader.ReadAsync(buf, 0, buf.Length); + if (chunkLength == 0) + { + if (_linesBuffer.Length > 0) + { + OnCompleteLine(_linesBuffer.ToString()); + _linesBuffer.Clear(); + } + + OnClosed(); + break; + } + + OnChunk(new ArraySegment(buf, 0, chunkLength)); + + int lineBreakPos; + var startPos = 0; + + // get all the newlines + while ((lineBreakPos = Array.IndexOf(buf, '\n', startPos, chunkLength - startPos)) >= 0 && startPos < chunkLength) + { + var length = (lineBreakPos + 1) - startPos; + _linesBuffer.Append(buf, startPos, length); + OnCompleteLine(_linesBuffer.ToString()); + _linesBuffer.Clear(); + startPos = lineBreakPos + 1; + } + + // get the rest + if (lineBreakPos < 0 && startPos < chunkLength) + { + _linesBuffer.Append(buf, startPos, chunkLength - startPos); + } + } + } + + private void OnChunk(ArraySegment chunk) + { + var dlg = OnReceivedChunk; + dlg?.Invoke(chunk); + } + + private void OnCompleteLine(string line) + { + var dlg = OnReceivedLine; + dlg?.Invoke(line); + } + + private void OnClosed() + { + var dlg = OnStreamClosed; + dlg?.Invoke(); + } +} diff --git a/src/Util.Ui/Sources/Spa/Util/EventedStreamStringReader.cs b/src/Util.Ui/Sources/Spa/Util/EventedStreamStringReader.cs new file mode 100644 index 000000000..ad5b7c5c2 --- /dev/null +++ b/src/Util.Ui/Sources/Spa/Util/EventedStreamStringReader.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Util.Ui.Sources.Spa.Util; + +/// +/// Captures the completed-line notifications from a , +/// combining the data into a single . +/// +internal sealed class EventedStreamStringReader : IDisposable +{ + private readonly EventedStreamReader _eventedStreamReader; + private bool _isDisposed; + private readonly StringBuilder _stringBuilder = new StringBuilder(); + + public EventedStreamStringReader(EventedStreamReader eventedStreamReader) + { + _eventedStreamReader = eventedStreamReader + ?? throw new ArgumentNullException(nameof(eventedStreamReader)); + _eventedStreamReader.OnReceivedLine += OnReceivedLine; + } + + public string ReadAsString() => _stringBuilder.ToString(); + + private void OnReceivedLine(string line) => _stringBuilder.AppendLine(line); + + public void Dispose() + { + if (!_isDisposed) + { + _eventedStreamReader.OnReceivedLine -= OnReceivedLine; + _isDisposed = true; + } + } +} diff --git a/src/Util.Ui/Sources/Spa/Util/LoggerFinder.cs b/src/Util.Ui/Sources/Spa/Util/LoggerFinder.cs new file mode 100644 index 000000000..10009b39a --- /dev/null +++ b/src/Util.Ui/Sources/Spa/Util/LoggerFinder.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + +namespace Util.Ui.Sources.Spa.Util; + +internal static class LoggerFinder +{ + public static ILogger GetOrCreateLogger( + IApplicationBuilder appBuilder, + string logCategoryName) + { + // If the DI system gives us a logger, use it. Otherwise, set up a default one + var loggerFactory = appBuilder.ApplicationServices.GetService(); + var logger = loggerFactory != null + ? loggerFactory.CreateLogger(logCategoryName) + : NullLogger.Instance; + return logger; + } +} diff --git a/src/Util.Ui/Sources/Spa/Util/TaskTimeoutExtensions.cs b/src/Util.Ui/Sources/Spa/Util/TaskTimeoutExtensions.cs new file mode 100644 index 000000000..5de1b2585 --- /dev/null +++ b/src/Util.Ui/Sources/Spa/Util/TaskTimeoutExtensions.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Util.Ui.Sources.Spa.Util; + +internal static class TaskTimeoutExtensions +{ + public static async Task WithTimeout(this Task task, TimeSpan timeoutDelay, string message) + { + if (task == await Task.WhenAny(task, Task.Delay(timeoutDelay))) + { + task.Wait(); // Allow any errors to propagate + } + else + { + throw new TimeoutException(message); + } + } + + public static async Task WithTimeout(this Task task, TimeSpan timeoutDelay, string message) + { + if (task == await Task.WhenAny(task, Task.Delay(timeoutDelay))) + { + return task.Result; + } + else + { + throw new TimeoutException(message); + } + } +} diff --git a/src/Util.Ui/Sources/Spa/Util/TcpPortFinder.cs b/src/Util.Ui/Sources/Spa/Util/TcpPortFinder.cs new file mode 100644 index 000000000..ab9fc4c0a --- /dev/null +++ b/src/Util.Ui/Sources/Spa/Util/TcpPortFinder.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Util.Ui.Sources.Spa.Util; + +internal static class TcpPortFinder +{ + public static int FindAvailablePort() + { + var listener = new TcpListener(IPAddress.Loopback, 0); + listener.Start(); + try + { + return ((IPEndPoint)listener.LocalEndpoint).Port; + } + finally + { + listener.Stop(); + } + } +} diff --git a/src/Util.Ui/TagHelpers/TagHelperBase.cs b/src/Util.Ui/TagHelpers/TagHelperBase.cs new file mode 100644 index 000000000..bd4bb66f9 --- /dev/null +++ b/src/Util.Ui/TagHelpers/TagHelperBase.cs @@ -0,0 +1,66 @@ +using Util.Ui.Renders; + +namespace Util.Ui.TagHelpers; + +/// +/// TagHelper基类 +/// +public abstract class TagHelperBase : TagHelper { + /// + /// 标识 + /// + public string Id { get; set; } + /// + /// style,样式 + /// + public string Style { get; set; } + /// + /// class,样式类 + /// + public string Class { get; set; } + /// + /// [hidden],是否隐藏,类型: boolean, 默认值: false + /// + public string Hidden { get; set; } + + /// + /// 转换为包装器 + /// + public TagHelperWrapper ToWrapper() { + return new TagHelperWrapper( this ); + } + + /// + /// 转换为包装器 + /// + public TagHelperWrapper ToWrapper() { + return new TagHelperWrapper( this ); + } + + /// + /// 渲染 + /// + public override async Task ProcessAsync( TagHelperContext context, TagHelperOutput output ) { + ProcessBefore( context, output ); + var content = await output.GetChildContentAsync(); + var render = GetRender( context, output, content ); + output.SuppressOutput(); + output.PostElement.SetHtmlContent( render ); + } + + /// + /// 渲染前操作 + /// + /// 上下文 + /// 输出 + protected virtual void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + } + + /// + /// 获取渲染器 + /// + /// 上下文 + /// 输出 + /// 内容 + protected abstract IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ); +} \ No newline at end of file diff --git a/src/Util.Ui/TagHelpers/TagHelperWrapper.cs b/src/Util.Ui/TagHelpers/TagHelperWrapper.cs new file mode 100644 index 000000000..bdaad88cf --- /dev/null +++ b/src/Util.Ui/TagHelpers/TagHelperWrapper.cs @@ -0,0 +1,214 @@ +using Util.Ui.Configs; +using Util.Ui.Expressions; + +namespace Util.Ui.TagHelpers; + +/// +/// TagHelper包装器 +/// +/// 模型类型 +public class TagHelperWrapper : TagHelperWrapper { + /// + /// 初始化TagHelper包装器 + /// + public TagHelperWrapper( ITagHelper tagHelper ) : base( tagHelper ) { + } + + /// + /// 设置属性表达式 + /// + /// 属性类型 + /// 属性表达式 + public TagHelperWrapper SetExpression( Expression> propertyExpression ) { + return SetExpression( UiConst.For, propertyExpression ); + } + + /// + /// 设置属性表达式 + /// + /// 属性类型 + /// 属性名 + /// 属性表达式 + public TagHelperWrapper SetExpression( string name, Expression> propertyExpression ) { + var modelExpression = ModelExpressionHelper.Create( Util.Helpers.Lambda.GetName( propertyExpression ), propertyExpression ); + SetContextAttribute( name, modelExpression ); + return this; + } +} + +/// +/// TagHelper包装器 +/// +public class TagHelperWrapper { + /// + /// 上下文属性集合 + /// + private readonly TagHelperAttributeList _contextAttributes; + /// + /// 输出属性集合 + /// + private readonly TagHelperAttributeList _outputAttributes; + /// + /// 共享项集合 + /// + private readonly IDictionary _items; + /// + /// 组件 + /// + private readonly ITagHelper _component; + /// + /// 内容 + /// + private readonly TagHelperContent _content; + /// + /// 子组件 + /// + private readonly List _children; + + /// + /// 初始化TagHelper包装器 + /// + public TagHelperWrapper( ITagHelper tagHelper ) { + _component = tagHelper ?? throw new ArgumentNullException( nameof( tagHelper ) ); + _contextAttributes = new TagHelperAttributeList(); + _outputAttributes = new TagHelperAttributeList(); + _items = new Dictionary(); + _content = new DefaultTagHelperContent(); + _children = new List(); + } + + /// + /// 获取TagHelper + /// + public ITagHelper GetTagHelper() { + return _component; + } + + /// + /// 设置上下文属性 + /// + /// 属性名 + /// 值 + /// 是否替换已存在的属性 + public TagHelperWrapper SetContextAttribute( string name, object value, bool replaceExisting = true ) { + if ( replaceExisting == false && _contextAttributes.ContainsName( name ) ) + return this; + _contextAttributes.SetAttribute( new TagHelperAttribute( name, value ) ); + return this; + } + + /// + /// 设置输出属性 + /// + /// 属性名 + /// 值 + /// 是否替换已存在的属性 + public TagHelperWrapper SetOutputAttribute( string name, object value, bool replaceExisting = true ) { + if ( replaceExisting == false && _outputAttributes.ContainsName( name ) ) + return this; + _outputAttributes.SetAttribute( new TagHelperAttribute( name, value ) ); + return this; + } + + /// + /// 设置共享项 + /// + /// 键 + /// 值 + public TagHelperWrapper SetItem( object key, object value ) { + if ( key != null ) + _items[key] = value; + return this; + } + + /// + /// 设置共享项 + /// + /// 值 + public TagHelperWrapper SetItem( T value ) { + var key = typeof( T ); + return SetItem( key, value ); + } + + /// + /// 添加内容 + /// + /// 值 + public TagHelperWrapper AppendContent( string value ) { + if ( string.IsNullOrWhiteSpace( value ) ) + return this; + _content.AppendHtml( value ); + return this; + } + + /// + /// 添加内容 + /// + /// 值 + public TagHelperWrapper AppendContent( IHtmlContent value ) { + if ( value == null ) + return this; + _content.AppendHtml( value ); + return this; + } + + /// + /// 添加内容 + /// + /// 子组件 + public TagHelperWrapper AppendContent( ITagHelper childComponent ) { + if ( childComponent == null ) + return this; + _children.Add( new TagHelperWrapper( childComponent ) ); + return this; + } + + /// + /// 添加内容 + /// + /// 子组件 + public TagHelperWrapper AppendContent( TagHelperWrapper childComponent ) { + if ( childComponent == null ) + return this; + _children.Add( childComponent ); + return this; + } + + /// + /// 获取结果 + /// + public string GetResult() { + TagHelperContent content = new DefaultTagHelperContent(); + _content.CopyTo( content ); + var context = new TagHelperContext( _contextAttributes, _items, Guid.NewGuid().ToString() ); + var output = new TagHelperOutput( "", _outputAttributes, ( useCachedResult, encoder ) => { + foreach ( var child in _children ) + content.AppendHtml( child.GetContent( _items ) ); + return Task.FromResult( content ); + } ); + _component.ProcessAsync( context, output ).GetAwaiter().GetResult(); + var writer = new StringWriter(); + output.WriteTo( writer, HtmlEncoder.Default ); + return writer.ToString(); + } + + /// + /// 获取内容 + /// + /// 上下文 + public TagHelperContent GetContent( IDictionary items ) { + TagHelperContent content = new DefaultTagHelperContent(); + _content.CopyTo( content ); + IDictionary newItems = items.ToDictionary( t => t.Key, t => t.Value ); + var context = new TagHelperContext( _contextAttributes, newItems, Guid.NewGuid().ToString() ); + var output = new TagHelperOutput( "", _outputAttributes, ( useCachedResult, encoder ) => { + foreach ( var child in _children ) + content.AppendHtml( child.GetContent( newItems ) ); + return Task.FromResult( content ); + } ); + _component.ProcessAsync( context, output ); + var result = new DefaultTagHelperContent(); + result.AppendHtml( output ); + return result; + } +} \ No newline at end of file diff --git a/src/Util.Ui/Usings.cs b/src/Util.Ui/Usings.cs new file mode 100644 index 000000000..297c58d34 --- /dev/null +++ b/src/Util.Ui/Usings.cs @@ -0,0 +1,34 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Diagnostics; +global using System.IO; +global using System.Reflection; +global using System.ComponentModel; +global using System.Linq.Expressions; +global using System.ComponentModel.DataAnnotations; +global using System.Text.Encodings.Web; +global using System.Threading; +global using System.Text; +global using System.Globalization; +global using System.Net.Sockets; +global using System.Net; +global using System.Net.Http; +global using Microsoft.AspNetCore.Hosting; +global using Microsoft.AspNetCore.Http; +global using Microsoft.AspNetCore.Builder; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.AspNetCore.Html; +global using Microsoft.AspNetCore.Razor.TagHelpers; +global using Microsoft.AspNetCore.Mvc.ViewFeatures; +global using Microsoft.AspNetCore.Mvc.ModelBinding; +global using Microsoft.AspNetCore.Mvc; +global using Microsoft.AspNetCore.Mvc.Filters; +global using Microsoft.AspNetCore.Mvc.Razor; +global using Microsoft.AspNetCore.Mvc.RazorPages; +global using Microsoft.Extensions.Options; +global using Microsoft.AspNetCore.Mvc.Infrastructure; +global using Microsoft.AspNetCore.Mvc.ApplicationModels; +global using Humanizer; +global using Util.Properties; \ No newline at end of file diff --git a/src/Util.Validation/04-Util.Validation.csproj b/src/Util.Validation/04-Util.Validation.csproj new file mode 100644 index 000000000..4dc9d8d72 --- /dev/null +++ b/src/Util.Validation/04-Util.Validation.csproj @@ -0,0 +1,34 @@ + + + + $(NetTargetFramework) + icon.jpg + Util.Validation + Util.Validation + Util.Validation是Util应用框架的验证库 + + + + + .\obj\Debug\$(NetTargetFramework)\Util.Validation.xml + + + + + .\obj\Release\$(NetTargetFramework)\Util.Validation.xml + + + + + True + False + + + + + + + + + + diff --git a/src/Util.Validation/DataAnnotationValidation.cs b/src/Util.Validation/DataAnnotationValidation.cs new file mode 100644 index 000000000..2966909fb --- /dev/null +++ b/src/Util.Validation/DataAnnotationValidation.cs @@ -0,0 +1,22 @@ +namespace Util.Validation; + +/// +/// 数据注解验证操作 +/// +public static class DataAnnotationValidation { + /// + /// 验证 + /// + /// 验证目标 + public static ValidationResultCollection Validate( object target ) { + if( target == null ) + throw new ArgumentNullException( nameof( target ) ); + var result = new ValidationResultCollection(); + var validationResults = new List(); + var context = new ValidationContext( target, null, null ); + var isValid = Validator.TryValidateObject( target, context, validationResults, true ); + if ( !isValid ) + result.AddList( validationResults ); + return result; + } +} \ No newline at end of file diff --git a/src/Util.Validation/IValidation.cs b/src/Util.Validation/IValidation.cs new file mode 100644 index 000000000..dca9a8752 --- /dev/null +++ b/src/Util.Validation/IValidation.cs @@ -0,0 +1,11 @@ +namespace Util.Validation; + +/// +/// 验证操作 +/// +public interface IValidation { + /// + /// 验证 + /// + ValidationResultCollection Validate(); +} \ No newline at end of file diff --git a/src/Util.Validation/IValidationHandler.cs b/src/Util.Validation/IValidationHandler.cs new file mode 100644 index 000000000..ea27950b5 --- /dev/null +++ b/src/Util.Validation/IValidationHandler.cs @@ -0,0 +1,12 @@ +namespace Util.Validation; + +/// +/// 验证处理器 +/// +public interface IValidationHandler { + /// + /// 处理验证错误 + /// + /// 验证结果集合 + void Handle( ValidationResultCollection results ); +} \ No newline at end of file diff --git a/src/Util.Validation/IValidationRule.cs b/src/Util.Validation/IValidationRule.cs new file mode 100644 index 000000000..bfa0cee4e --- /dev/null +++ b/src/Util.Validation/IValidationRule.cs @@ -0,0 +1,11 @@ +namespace Util.Validation; + +/// +/// 验证规则 +/// +public interface IValidationRule { + /// + /// 验证 + /// + ValidationResult Validate(); +} \ No newline at end of file diff --git a/src/Util.Validation/NothingHandler.cs b/src/Util.Validation/NothingHandler.cs new file mode 100644 index 000000000..76910fd04 --- /dev/null +++ b/src/Util.Validation/NothingHandler.cs @@ -0,0 +1,13 @@ +namespace Util.Validation; + +/// +/// 验证失败,不做任何处理 +/// +public class NothingHandler : IValidationHandler { + /// + /// 处理验证错误 + /// + /// 验证结果集合 + public void Handle( ValidationResultCollection results ) { + } +} \ No newline at end of file diff --git a/src/Util.Validation/ThrowHandler.cs b/src/Util.Validation/ThrowHandler.cs new file mode 100644 index 000000000..952ec2bc4 --- /dev/null +++ b/src/Util.Validation/ThrowHandler.cs @@ -0,0 +1,18 @@ +using Util.Exceptions; + +namespace Util.Validation; + +/// +/// 验证失败,抛出异常 +/// +public class ThrowHandler : IValidationHandler{ + /// + /// 处理验证错误 + /// + /// 验证结果集合 + public void Handle( ValidationResultCollection results ) { + if ( results.IsValid ) + return; + throw new Warning( results.First().ErrorMessage ); + } +} \ No newline at end of file diff --git a/src/Util.Validation/Usings.cs b/src/Util.Validation/Usings.cs new file mode 100644 index 000000000..d2c333131 --- /dev/null +++ b/src/Util.Validation/Usings.cs @@ -0,0 +1,6 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Globalization; +global using System.ComponentModel.DataAnnotations; \ No newline at end of file diff --git a/src/Util.Validation/ValidAttribute.cs b/src/Util.Validation/ValidAttribute.cs new file mode 100644 index 000000000..eb16c2700 --- /dev/null +++ b/src/Util.Validation/ValidAttribute.cs @@ -0,0 +1,40 @@ +using AspectCore.DynamicProxy.Parameters; +using Util.Aop; +using Util.Helpers; + +namespace Util.Validation; + +/// +/// 验证拦截器 +/// +public class ValidAttribute : ParameterInterceptorBase { + /// + /// 执行 + /// + public override async Task Invoke( ParameterAspectContext context, ParameterAspectDelegate next ) { + Validate( context.Parameter ); + await next( context ); + } + + /// + /// 验证 + /// + private void Validate( Parameter parameter ) { + if ( Reflection.IsGenericCollection( parameter.RawType ) ) { + ValidateCollection( parameter ); + return; + } + IValidation validation = parameter.Value as IValidation; + validation?.Validate(); + } + + /// + /// 验证集合 + /// + private void ValidateCollection( Parameter parameter ) { + if ( !( parameter.Value is IEnumerable validations ) ) + return; + foreach ( var validation in validations ) + validation.Validate(); + } +} \ No newline at end of file diff --git a/src/Util.Validation/ValidationResultCollection.cs b/src/Util.Validation/ValidationResultCollection.cs new file mode 100644 index 000000000..0361afd5a --- /dev/null +++ b/src/Util.Validation/ValidationResultCollection.cs @@ -0,0 +1,52 @@ +namespace Util.Validation; + +/// +/// 验证结果集合 +/// +public class ValidationResultCollection : List { + /// + /// 初始化验证结果集合 + /// + public ValidationResultCollection() : this( "" ) { + } + + /// + /// 初始化验证结果集合 + /// + /// 验证结果 + public ValidationResultCollection( string result ) { + if( string.IsNullOrWhiteSpace( result ) ) + return; + Add( new ValidationResult( result ) ); + } + + /// + /// 成功验证结果集合 + /// + public static readonly ValidationResultCollection Success = new(); + + /// + /// 是否有效 + /// + public bool IsValid => Count == 0; + + /// + /// 添加验证结果集合 + /// + /// 验证结果集合 + public void AddList( IEnumerable results ) { + if( results == null ) + return; + foreach( var result in results ) + Add( result ); + } + + /// + /// 输出验证消息 + /// + public override string ToString() { + if( IsValid ) + return string.Empty; + return this.First().ErrorMessage; + } +} \ No newline at end of file diff --git a/src/Util.Validation/Validators/IdCardAttribute.cs b/src/Util.Validation/Validators/IdCardAttribute.cs new file mode 100644 index 000000000..e1632fa2a --- /dev/null +++ b/src/Util.Validation/Validators/IdCardAttribute.cs @@ -0,0 +1,31 @@ +using Util; +using Util.Properties; +using Util.Validation.Validators; + +namespace System.ComponentModel.DataAnnotations; + +/// +/// 身份证验证 +/// +[AttributeUsage( AttributeTargets.Property )] +public class IdCardAttribute : ValidationAttribute { + /// + /// 格式化错误消息 + /// + public override string FormatErrorMessage( string name ) { + if( ErrorMessage == null && ErrorMessageResourceName == null ) + ErrorMessage = R.InvalidIdCard; + return string.Format( CultureInfo.CurrentCulture, ErrorMessageString ); + } + + /// + /// 是否验证通过 + /// + protected override ValidationResult IsValid( object value, ValidationContext validationContext ) { + if( value.SafeString().IsEmpty() ) + return ValidationResult.Success; + if( Util.Helpers.Regex.IsMatch( value.SafeString(), ValidatePattern.IdCardPattern ) ) + return ValidationResult.Success; + return new ValidationResult( FormatErrorMessage( string.Empty ) ); + } +} \ No newline at end of file diff --git a/src/Util.Validation/Validators/ValidatePattern.cs b/src/Util.Validation/Validators/ValidatePattern.cs new file mode 100644 index 000000000..a1cbd929e --- /dev/null +++ b/src/Util.Validation/Validators/ValidatePattern.cs @@ -0,0 +1,15 @@ +namespace Util.Validation.Validators; + +/// +/// 验证模式 +/// +public static class ValidatePattern { + /// + /// 手机号验证正则表达式 + /// + public static string MobilePhonePattern = "^1[0-9]{10}$"; + /// + /// 身份证验证正则表达式 + /// + public static string IdCardPattern = @"(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)"; +} \ No newline at end of file diff --git a/src/Util.Webs/07-Util.Webs.csproj b/src/Util.Webs/07-Util.Webs.csproj deleted file mode 100644 index 1b6283881..000000000 --- a/src/Util.Webs/07-Util.Webs.csproj +++ /dev/null @@ -1,46 +0,0 @@ - - - - netstandard2.0 - Util.Webs - Util.Webs - ..\..\output\release\ - ..\..\output\release\netstandard2.0\Util.Webs.xml - 0.2.2-preview - 0.2.2.0 - 0.2.2.0 - 何镇汐 - 何镇汐 - Util.Webs是Util应用框架的Asp.Net Core操作类库。 -Util是一个.net core平台下的应用框架,旨在提升小型团队的开发输出能力,由常用公共操作类(工具类)、分层架构基类、Ui组件,第三方组件封装,第三方业务接口封装,配套代码生成模板,权限等组成。 - 何镇汐 - https://github.com/dotnetcore/Util/blob/master/LICENSE.txt - https://github.com/dotnetcore/Util - https://avatars2.githubusercontent.com/u/19421630 - https://github.com/dotnetcore/Util - git - util;applicationframework;dotnetcore;aspnetcore; - Util应用框架以MIT开源发布,可随意使用 - false - - - - ..\..\output\release\ - ..\..\output\release\netstandard2.0\Util.Webs.xml - - - - - - - - - - - - - - - - - diff --git a/src/Util.Webs/Commons/Result.cs b/src/Util.Webs/Commons/Result.cs deleted file mode 100644 index d931770d6..000000000 --- a/src/Util.Webs/Commons/Result.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; - -namespace Util.Webs.Commons { - /// - /// 返回结果 - /// - public class Result : JsonResult { - /// - /// 状态码 - /// - private readonly StateCode _code; - /// - /// 消息 - /// - private readonly string _message; - /// - /// 数据 - /// - private readonly dynamic _data; - - /// - /// 初始化返回结果 - /// - /// 状态码 - /// 消息 - /// 数据 - public Result( StateCode code, string message, dynamic data = null ) : base( null ) { - _code = code; - _message = message; - _data = data; - } - - /// - /// 执行结果 - /// - public override Task ExecuteResultAsync( ActionContext context ) { - this.Value = new { - Code = _code.Value(), - Message = _message, - Data = _data - }; - return base.ExecuteResultAsync( context ); - } - } -} diff --git a/src/Util.Webs/Commons/StateCode.cs b/src/Util.Webs/Commons/StateCode.cs deleted file mode 100644 index a73ee6ef3..000000000 --- a/src/Util.Webs/Commons/StateCode.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Util.Webs.Commons { - /// - /// 状态码 - /// - public enum StateCode { - /// - /// 成功 - /// - Ok = 1, - /// - /// 失败 - /// - Fail = 2 - } -} diff --git a/src/Util.Webs/Controllers/CrudControllerBase.cs b/src/Util.Webs/Controllers/CrudControllerBase.cs deleted file mode 100644 index 10ac50b10..000000000 --- a/src/Util.Webs/Controllers/CrudControllerBase.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Util.Applications; -using Util.Applications.Dtos; -using Util.Datas.Queries; -using Util.Exceptions; - -namespace Util.Webs.Controllers { - /// - /// Crud控制器 - /// - /// 数据传输对象类型 - /// 查询参数类型 - public abstract class CrudControllerBase : QueryControllerBase - where TQuery : IQueryParameter - where TDto : DtoBase, new() { - /// - /// Crud服务 - /// - private readonly ICrudService _service; - - /// - /// 初始化Crud控制器 - /// - /// Crud服务 - protected CrudControllerBase( ICrudService service ) - : base( service ) { - _service = service; - } - - /// - /// 创建,调用范例:POST URL(/api/customers) BODY({name:'a',age:2}) - /// - /// 数据传输对象 - [HttpPost] - public virtual async Task CreateAsync( [FromBody] TDto dto ) { - if ( dto == null ) - return Fail( "请求参数不能为空" ); - CreateBefore( dto ); - await _service.CreateAsync( dto ); - var result = await _service.GetByIdAsync( dto.Id ); - return Success( result ); - } - - /// - /// 创建前操作 - /// - /// 数据传输对象 - protected virtual void CreateBefore( TDto dto ) { - } - - /// - /// 修改,调用范例:PUT URL(/api/customers/1 或 /api/customers) BODY({id:1,name:'a'}) - /// - /// 标识 - /// 数据传输对象 - [HttpPut("{id?}")] - public virtual async Task UpdateAsync( string id, [FromBody] TDto dto ) { - if( dto == null ) - return Fail( "请求参数不能为空" ); - if( id.IsEmpty() && dto.Id.IsEmpty() ) - throw new Warning( "Id不能为空" ); - if ( dto.Id.IsEmpty() ) - dto.Id = id; - UpdateBefore( dto ); - await _service.UpdateAsync( dto ); - var result = await _service.GetByIdAsync( dto.Id ); - return Success( result ); - } - - /// - /// 修改前操作 - /// - /// 数据传输对象 - protected virtual void UpdateBefore( TDto dto ) { - } - - /// - /// 保存,调用范例:POST URL(/api/customers/save) BODY({name:'a',age:2}) - /// - /// 数据传输对象 - [HttpPost("save")] - public virtual async Task SaveAsync( [FromBody] TDto dto ) { - if( dto == null ) - return Fail( "请求参数不能为空" ); - SaveBefore( dto ); - await _service.SaveAsync( dto ); - var result = await _service.GetByIdAsync( dto.Id ); - return Success( result ); - } - - /// - /// 保存前操作 - /// - /// 数据传输对象 - protected virtual void SaveBefore( TDto dto ) { - } - - /// - /// 删除,调用范例:DELETE URL(/api/customers/1), - /// 注意:该方法用于删除单个实体,批量删除请使用POST提交,否则可能失败 - /// - /// 标识 - [HttpDelete( "{id}" )] - public virtual async Task DeleteAsync( string id ) { - await _service.DeleteAsync( id ); - return Success(); - } - - /// - /// 批量删除,调用范例:POST URL(/api/customers/delete) BODY("'1,2,3'"), - /// 注意:body参数需要添加引号,"'1,2,3'"而不是"1,2,3" - /// - /// 标识列表,多个Id用逗号分隔,范例:1,2,3 - [HttpPost( "delete" )] - public virtual async Task BatchDeleteAsync( [FromBody] string ids ) { - await _service.DeleteAsync( ids ); - return Success(); - } - } -} diff --git a/src/Util.Webs/Controllers/QueryControllerBase.cs b/src/Util.Webs/Controllers/QueryControllerBase.cs deleted file mode 100644 index 997dc80cb..000000000 --- a/src/Util.Webs/Controllers/QueryControllerBase.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Util.Applications; -using Util.Applications.Dtos; -using Util.Datas.Queries; - -namespace Util.Webs.Controllers { - /// - /// 查询控制器 - /// - /// 数据传输对象类型 - /// 查询参数类型 - public abstract class QueryControllerBase : WebApiControllerBase - where TQuery : IQueryParameter - where TDto : class, IDto, new() { - /// - /// Crud服务 - /// - private readonly ICrudService _service; - - /// - /// 初始化查询控制器 - /// - /// Crud服务 - protected QueryControllerBase( ICrudService service ) { - _service = service; - } - - /// - /// 获取单个实例,调用范例:GET URL(/api/customers/1) - /// - /// 标识 - [HttpGet( "{id}" )] - public virtual async Task GetAsync( string id ) { - var result = await _service.GetByIdAsync( id ); - return Success( result ); - } - - /// - /// 查询,调用范例:GET URL(/api/customers/query?name=a) - /// - /// 查询参数 - [HttpGet( "Query" )] - public virtual async Task QueryAsync( TQuery query ) { - var result = await _service.QueryAsync( query ); - return Success( result ); - } - - /// - /// 分页查询,调用范例:GET URL(/api/customers?name=a) - /// - /// 查询参数 - [HttpGet] - public virtual async Task PagerQueryAsync( TQuery query ) { - var result = await _service.PagerQueryAsync( query ); - return Success( result ); - } - } -} diff --git a/src/Util.Webs/Controllers/WebApiControllerBase.cs b/src/Util.Webs/Controllers/WebApiControllerBase.cs deleted file mode 100644 index 6e40616e5..000000000 --- a/src/Util.Webs/Controllers/WebApiControllerBase.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Util.Properties; -using Util.Webs.Commons; -using Util.Webs.Filters; - -namespace Util.Webs.Controllers { - /// - /// WebApi控制器 - /// - [Route( "api/[controller]" )] - [ExceptionHandler] - public class WebApiControllerBase : Controller { - /// - /// 返回成功消息 - /// - /// 数据 - /// 消息 - protected virtual IActionResult Success( dynamic data = null, string message = null ) { - if ( message == null ) - message = R.Success; - return new Result( StateCode.Ok, message, data ); - } - - /// - /// 返回失败消息 - /// - /// 消息 - protected IActionResult Fail( string message ) { - return new Result( StateCode.Fail, message ); - } - } -} diff --git a/src/Util.Webs/Extensions/Extensions.Middleware.cs b/src/Util.Webs/Extensions/Extensions.Middleware.cs deleted file mode 100644 index 1cc4dc9be..000000000 --- a/src/Util.Webs/Extensions/Extensions.Middleware.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Util.Webs.Middlewares; - -namespace Util.Webs.Extensions { - /// - /// 中间件扩展 - /// - public static partial class Extensions { - /// - /// 注册错误日志管道 - /// - /// 应用程序生成器 - public static IApplicationBuilder UseErrorLog( this IApplicationBuilder builder ) { - return builder.UseMiddleware(); - } - } -} diff --git a/src/Util.Webs/Filters/ExceptionHandlerAttribute.cs b/src/Util.Webs/Filters/ExceptionHandlerAttribute.cs deleted file mode 100644 index 5a7f22307..000000000 --- a/src/Util.Webs/Filters/ExceptionHandlerAttribute.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Exceptionless.Extensions; -using Microsoft.AspNetCore.Mvc.Filters; -using Util.Webs.Commons; - -namespace Util.Webs.Filters { - /// - /// 异常处理过滤器 - /// - public class ExceptionHandlerAttribute : ExceptionFilterAttribute { - /// - /// 异常处理 - /// - public override void OnException( ExceptionContext context ) { - context.ExceptionHandled = true; - context.HttpContext.Response.StatusCode = 200; - context.Result = new Result( StateCode.Fail, context.Exception.GetMessage() ); - } - } -} diff --git a/src/Util.Webs/HtmlEscape.cs b/src/Util.Webs/HtmlEscape.cs deleted file mode 100644 index 073fa3f1f..000000000 --- a/src/Util.Webs/HtmlEscape.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Util.Webs { - /// - /// Html转义字符 - /// - public static class HtmlEscape { - /// - /// 双引号,值为 &quot; - /// - public const string Quote = """; - /// - /// 双引号,值为 &#34; - /// - public const string Quote34 = """; - /// - /// 双引号,值为 &#x22; - /// - public const string Quote22 = """; - /// - /// 单引号,值为 &apos; - /// - public const string SingleQuote = "'"; - /// - /// 单引号,值为 &#x27; - /// - public const string SingleQuote27 = "'"; - /// - /// 单引号,值为 &#39; - /// - public const string SingleQuote39 = "'"; - } -} \ No newline at end of file diff --git a/src/Util.Webs/Middlewares/ErrorLogMiddleware.cs b/src/Util.Webs/Middlewares/ErrorLogMiddleware.cs deleted file mode 100644 index fa554a541..000000000 --- a/src/Util.Webs/Middlewares/ErrorLogMiddleware.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Util.Logs; -using Util.Logs.Extensions; - -namespace Util.Webs.Middlewares { - /// - /// 错误日志中间件 - /// - public class ErrorLogMiddleware { - /// - /// 方法 - /// - private readonly RequestDelegate _next; - - /// - /// 初始化错误日志中间件 - /// - /// 方法 - public ErrorLogMiddleware( RequestDelegate next ) { - _next = next; - } - - /// - /// 执行方法 - /// - /// Http上下文 - public async Task Invoke( HttpContext context ) { - try { - await _next( context ); - } - catch( Exception ex ) { - WriteLog( context, ex ); - throw; - } - } - - /// - /// 记录错误日志 - /// - private void WriteLog( HttpContext context, Exception ex ) { - if( context == null ) - return; - var log = Log.GetLog().Caption( "全局异常捕获" ).Content( $"状态码:{context.Response.StatusCode}" ); - ex.Log( log ); - } - } -} diff --git a/src/Util/01-Util.csproj b/src/Util/01-Util.csproj deleted file mode 100644 index 40d0f8ff7..000000000 --- a/src/Util/01-Util.csproj +++ /dev/null @@ -1,83 +0,0 @@ - - - netstandard2.0 - Util - util;applicationframework;dotnetcore;aspnetcore; - Util - 何镇汐 - Util.Core是Util应用框架的核心类库。 -Util是一个.net core平台下的应用框架,旨在提升小型团队的开发输出能力,由常用公共操作类(工具类)、分层架构基类、Ui组件,第三方组件封装,第三方业务接口封装,配套代码生成模板,权限等组成。 - - Util - false - 何镇汐 - 何镇汐 - 1.0.7.6 - 1.0.7.6 - 1.0.7-preview6 - https://avatars2.githubusercontent.com/u/19421630 - Util.Core - https://github.com/dotnetcore/Util/blob/master/LICENSE.txt - https://github.com/dotnetcore/Util - https://github.com/dotnetcore/Util - git - Util应用框架以MIT开源发布,可随意使用 - - - - ..\..\output\release\ - ..\..\output\release\netstandard2.0\Util.xml - - - - ..\..\output\release\ - ..\..\output\release\netstandard2.0\Util.xml - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - LibraryResource.resx - - - True - True - R.resx - - - - - - PublicResXFileCodeGenerator - LibraryResource.Designer.cs - - - PublicResXFileCodeGenerator - R.Designer.cs - - - - \ No newline at end of file diff --git a/src/Util/Applications/Commands/ICommand.cs b/src/Util/Applications/Commands/ICommand.cs deleted file mode 100644 index 86cbba08b..000000000 --- a/src/Util/Applications/Commands/ICommand.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Threading.Tasks; -using Util.Applications.Dtos; - -namespace Util.Applications.Commands { - /// - /// 命令 - /// - /// 请求类型 - /// 响应类型 - public interface ICommand where TRequest : IRequest where TResponse : IResponse { - /// - /// 执行 - /// - /// 请求参数 - TResponse Execute( TRequest request ); - /// - /// 执行 - /// - /// 请求参数 - Task ExecuteAsync( TRequest request ); - } -} diff --git a/src/Util/Applications/Commands/ICommandFactory.cs b/src/Util/Applications/Commands/ICommandFactory.cs deleted file mode 100644 index 321206aee..000000000 --- a/src/Util/Applications/Commands/ICommandFactory.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Util.Applications.Dtos; - -namespace Util.Applications.Commands { - /// - /// 命令工厂 - /// - public interface ICommandFactory { - /// - /// 创建命令 - /// - /// 请求类型 - /// 响应类型 - /// 命令名称 - ICommand Create( string name ) where TRequest : IRequest where TResponse : IResponse; - } -} diff --git a/src/Util/Applications/Dtos/DtoBase.cs b/src/Util/Applications/Dtos/DtoBase.cs deleted file mode 100644 index a729b112e..000000000 --- a/src/Util/Applications/Dtos/DtoBase.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Runtime.Serialization; - -namespace Util.Applications.Dtos { - /// - /// 数据传输对象 - /// - [DataContract] - public abstract class DtoBase : RequestBase, IDto { - /// - /// 标识 - /// - [DataMember] - public string Id { get; set; } - } -} diff --git a/src/Util/Applications/Dtos/IDto.cs b/src/Util/Applications/Dtos/IDto.cs deleted file mode 100644 index 2a31279be..000000000 --- a/src/Util/Applications/Dtos/IDto.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Applications.Dtos { - /// - /// 数据传输对象 - /// - public interface IDto : IKey, IRequest, IResponse { - } -} diff --git a/src/Util/Applications/Dtos/IKey.cs b/src/Util/Applications/Dtos/IKey.cs deleted file mode 100644 index 1bdadf163..000000000 --- a/src/Util/Applications/Dtos/IKey.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Util.Applications.Dtos { - /// - /// 标识 - /// - public interface IKey { - /// - /// 标识 - /// - string Id { get; set; } - } -} diff --git a/src/Util/Applications/Dtos/IRequest.cs b/src/Util/Applications/Dtos/IRequest.cs deleted file mode 100644 index 7ee63db94..000000000 --- a/src/Util/Applications/Dtos/IRequest.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Util.Validations; - -namespace Util.Applications.Dtos { - /// - /// 请求对象 - /// - public interface IRequest : IValidation { - } -} diff --git a/src/Util/Applications/Dtos/IResponse.cs b/src/Util/Applications/Dtos/IResponse.cs deleted file mode 100644 index e3480aae9..000000000 --- a/src/Util/Applications/Dtos/IResponse.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Applications.Dtos { - /// - /// 响应对象 - /// - public interface IResponse { - } -} diff --git a/src/Util/Applications/Dtos/RequestBase.cs b/src/Util/Applications/Dtos/RequestBase.cs deleted file mode 100644 index 97241103b..000000000 --- a/src/Util/Applications/Dtos/RequestBase.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Linq; -using System.Runtime.Serialization; -using Util.Exceptions; -using Util.Validations; - -namespace Util.Applications.Dtos { - /// - /// 请求对象 - /// - [DataContract] - public abstract class RequestBase : IRequest { - /// - /// 验证 - /// - public virtual ValidationResultCollection Validate() { - var result = DataAnnotationValidation.Validate( this ); - if( result.IsValid ) - return ValidationResultCollection.Success; - throw new Warning( result.First().ErrorMessage ); - } - } -} diff --git a/src/Util/Applications/IQueryService.cs b/src/Util/Applications/IQueryService.cs deleted file mode 100644 index 3112adc41..000000000 --- a/src/Util/Applications/IQueryService.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Util.Applications.Dtos; -using Util.Datas.Queries; -using Util.Dependency; -using Util.Domains.Repositories; - -namespace Util.Applications { - /// - /// 查询服务 - /// - /// 数据传输对象类型 - /// 查询参数类型 - public interface IQueryService : IScopeDependency - where TDto : IDto, new() - where TQueryParameter : IQueryParameter { - /// - /// 获取全部 - /// - List GetAll(); - /// - /// 获取全部 - /// - Task> GetAllAsync(); - /// - /// 通过编号获取 - /// - /// 实体编号 - TDto GetById( object id ); - /// - /// 通过编号获取 - /// - /// 实体编号 - Task GetByIdAsync( object id ); - /// - /// 通过编号列表获取 - /// - /// 用逗号分隔的Id列表,范例:"1,2" - List GetByIds( string ids ); - /// - /// 通过编号列表获取 - /// - /// 用逗号分隔的Id列表,范例:"1,2" - Task> GetByIdsAsync( string ids ); - /// - /// 查询 - /// - /// 查询参数 - List Query( TQueryParameter parameter ); - /// - /// 查询 - /// - /// 查询参数 - Task> QueryAsync( TQueryParameter parameter ); - /// - /// 分页查询 - /// - /// 查询参数 - PagerList PagerQuery( TQueryParameter parameter ); - /// - /// 分页查询 - /// - /// 查询参数 - Task> PagerQueryAsync( TQueryParameter parameter ); - } -} \ No newline at end of file diff --git a/src/Util/Aspects/Base/InterceptorBase.cs b/src/Util/Aspects/Base/InterceptorBase.cs deleted file mode 100644 index f1eb0af51..000000000 --- a/src/Util/Aspects/Base/InterceptorBase.cs +++ /dev/null @@ -1,9 +0,0 @@ -using AspectCore.DynamicProxy; - -namespace Util.Aspects.Base { - /// - /// 拦截器 - /// - public abstract class InterceptorBase : AbstractInterceptorAttribute { - } -} diff --git a/src/Util/Aspects/Base/ParameterInterceptorBase.cs b/src/Util/Aspects/Base/ParameterInterceptorBase.cs deleted file mode 100644 index a8d94c6d4..000000000 --- a/src/Util/Aspects/Base/ParameterInterceptorBase.cs +++ /dev/null @@ -1,9 +0,0 @@ -using AspectCore.DynamicProxy.Parameters; - -namespace Util.Aspects.Base { - /// - /// 参数拦截器 - /// - public abstract class ParameterInterceptorBase : ParameterInterceptorAttribute { - } -} diff --git a/src/Util/Aspects/IgnoreAttribute.cs b/src/Util/Aspects/IgnoreAttribute.cs deleted file mode 100644 index aa8f681db..000000000 --- a/src/Util/Aspects/IgnoreAttribute.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Aspects { - /// - /// 忽略拦截 - /// - public class IgnoreAttribute : AspectCore.DynamicProxy.NonAspectAttribute { - } -} diff --git a/src/Util/Aspects/NotEmptyAttribute.cs b/src/Util/Aspects/NotEmptyAttribute.cs deleted file mode 100644 index aa1bb6d4c..000000000 --- a/src/Util/Aspects/NotEmptyAttribute.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Threading.Tasks; -using AspectCore.DynamicProxy.Parameters; -using Util.Aspects.Base; - -namespace Util.Aspects { - /// - /// 验证不能为空 - /// - public class NotEmptyAttribute : ParameterInterceptorBase { - /// - /// 执行 - /// - public override Task Invoke( ParameterAspectContext context, ParameterAspectDelegate next ) { - if( string.IsNullOrWhiteSpace( context.Parameter.Value.SafeString() ) ) - throw new ArgumentNullException( context.Parameter.Name ); - return next( context ); - } - } -} diff --git a/src/Util/Aspects/NotNullAttribute.cs b/src/Util/Aspects/NotNullAttribute.cs deleted file mode 100644 index e7a64e170..000000000 --- a/src/Util/Aspects/NotNullAttribute.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Threading.Tasks; -using AspectCore.DynamicProxy.Parameters; -using Util.Aspects.Base; - -namespace Util.Aspects { - /// - /// 验证不能为null - /// - [AttributeUsage( AttributeTargets.Parameter )] - public class NotNullAttribute : ParameterInterceptorBase { - /// - /// 执行 - /// - public override Task Invoke( ParameterAspectContext context, ParameterAspectDelegate next ) { - if( context.Parameter.Value == null ) - throw new ArgumentNullException( context.Parameter.Name ); - return next( context ); - } - } -} diff --git a/src/Util/Contexts/IContext.cs b/src/Util/Contexts/IContext.cs deleted file mode 100644 index c80ed14b9..000000000 --- a/src/Util/Contexts/IContext.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Util.Contexts { - /// - /// 上下文 - /// - public interface IContext { - /// - /// 跟踪号 - /// - string TraceId { get; } - /// - /// 添加对象 - /// - /// 对象类型 - /// 键名 - /// 对象 - void Add( string key, T value ); - /// - /// 获取对象 - /// - /// 对象类型 - /// 键名 - T Get( string key ); - /// - /// 移除对象 - /// - /// 键名 - void Remove( string key ); - } -} diff --git a/src/Util/Contexts/WebContext.cs b/src/Util/Contexts/WebContext.cs deleted file mode 100644 index a8200fc2a..000000000 --- a/src/Util/Contexts/WebContext.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Microsoft.AspNetCore.Http; - -namespace Util.Contexts { - /// - /// Web上下文 - /// - public class WebContext : IContext { - /// - /// 初始化Web上下文 - /// - /// Http上下文访问器 - public WebContext( IHttpContextAccessor accessor ) { - HttpContextAccessor = accessor; - } - - /// - /// Http上下文访问器 - /// - public IHttpContextAccessor HttpContextAccessor { get; set; } - - /// - /// 跟踪号 - /// - public string TraceId => HttpContextAccessor?.HttpContext?.TraceIdentifier; - - /// - /// 添加对象 - /// - /// 对象类型 - /// 键名 - /// 对象 - public void Add( string key, T value ) { - if( HttpContextAccessor?.HttpContext == null ) - return; - HttpContextAccessor.HttpContext.Items[key] = value; - } - - /// - /// 获取对象 - /// - /// 对象类型 - /// 键名 - public T Get( string key ) { - if( HttpContextAccessor?.HttpContext == null ) - return default( T ); - return Util.Helpers.Convert.To( HttpContextAccessor.HttpContext.Items[key] ); - } - - /// - /// 移除对象 - /// - /// 键名 - public void Remove( string key ) { - HttpContextAccessor?.HttpContext?.Items.Remove( key ); - } - } -} diff --git a/src/Util/Datas/Persistence/IPersistentObject.cs b/src/Util/Datas/Persistence/IPersistentObject.cs deleted file mode 100644 index 74521d4d0..000000000 --- a/src/Util/Datas/Persistence/IPersistentObject.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Util.Dependency; -using Util.Domains; - -namespace Util.Datas.Persistence { - /// - /// 持久化对象 - /// - /// 标识类型 - public interface IPersistentObject : IKey, IScopeDependency, IVersion { - } -} diff --git a/src/Util/Datas/Persistence/IPersistentStore.cs b/src/Util/Datas/Persistence/IPersistentStore.cs deleted file mode 100644 index 019bb3480..000000000 --- a/src/Util/Datas/Persistence/IPersistentStore.cs +++ /dev/null @@ -1,196 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using Util.Domains.Repositories; - -namespace Util.Datas.Persistence { - /// - /// 持久化存储 - /// - /// 持久化对象类型 - public interface IPersistentStore : IPersistentStore where TPo : class, IPersistentObject { - } - - /// - /// 持久化存储 - /// - /// 持久化对象类型 - /// 持久化对象标识类型 - public interface IPersistentStore where TPo : class, IPersistentObject { - /// - /// 获取未跟踪的持久化对象集合 - /// - IQueryable FindAsNoTracking(); - /// - /// 查找持久化对象集合 - /// - IQueryable Find(); - /// - /// 查找持久化对象集合 - /// - /// 条件 - IQueryable Find( ICriteria criteria ); - /// - /// 查找持久化对象 - /// - /// 持久化对象标识 - TPo Find( object id ); - /// - /// 查找持久化对象 - /// - /// 持久化对象标识 - Task FindAsync( object id ); - /// - /// 查找持久化对象集合 - /// - /// 持久化对象标识集合 - List FindByIds( params TKey[] ids ); - /// - /// 查找持久化对象集合 - /// - /// 持久化对象标识集合 - List FindByIds( IEnumerable ids ); - /// - /// 查找持久化对象集合 - /// - /// 持久化对象标识集合 - Task> FindByIdsAsync( params TKey[] ids ); - /// - /// 查找持久化对象集合 - /// - /// 持久化对象标识集合 - Task> FindByIdsAsync( IEnumerable ids ); - /// - /// 获取单个持久化对象 - /// - /// 查询条件 - TPo Single( Expression> predicate ); - /// - /// 获取单个持久化对象 - /// - /// 查询条件 - Task SingleAsync( Expression> predicate ); - /// - /// 判断持久化对象是否存在 - /// - /// 持久化对象标识集合 - bool Exists( params TKey[] ids ); - /// - /// 判断持久化对象是否存在 - /// - /// 持久化对象标识集合 - Task ExistsAsync( params TKey[] ids ); - /// - /// 查询 - /// - /// 查询对象 - List Query( IQueryBase query ); - /// - /// 查询 - /// - /// 查询对象 - Task> QueryAsync( IQueryBase query ); - /// - /// 查询 - 返回未跟踪的持久化对象 - /// - /// 查询对象 - List QueryAsNoTracking( IQueryBase query ); - /// - /// 查询 - 返回未跟踪的持久化对象 - /// - /// 查询对象 - Task> QueryAsNoTrackingAsync( IQueryBase query ); - /// - /// 分页查询 - /// - /// 查询对象 - PagerList PagerQuery( IQueryBase query ); - /// - /// 分页查询 - /// - /// 查询对象 - Task> PagerQueryAsync( IQueryBase query ); - /// - /// 分页查询 - /// - /// 查询对象 - PagerList PagerQueryAsNoTracking( IQueryBase query ); - /// - /// 分页查询 - /// - /// 查询对象 - Task> PagerQueryAsNoTrackingAsync( IQueryBase query ); - /// - /// 添加持久化对象 - /// - /// 持久化对象 - void Add( TPo po ); - /// - /// 添加持久化对象集合 - /// - /// 持久化对象集合 - void Add( IEnumerable pos ); - /// - /// 添加持久化对象 - /// - /// 持久化对象 - Task AddAsync( TPo po ); - /// - /// 添加持久化对象集合 - /// - /// 持久化对象集合 - Task AddAsync( IEnumerable pos ); - /// - /// 修改持久化对象 - /// - /// 持久化对象 - void Update( TPo po ); - /// - /// 修改持久化对象 - /// - /// 持久化对象 - Task UpdateAsync( TPo po ); - /// - /// 移除持久化对象 - /// - /// 持久化对象标识 - void Remove( TKey id ); - /// - /// 移除持久化对象 - /// - /// 持久化对象标识 - Task RemoveAsync( TKey id ); - /// - /// 移除持久化对象 - /// - /// 持久化对象 - void Remove( TPo po ); - /// - /// 移除持久化对象 - /// - /// 持久化对象 - Task RemoveAsync( TPo po ); - /// - /// 移除持久化对象集合 - /// - /// 持久化对象编号集合 - void Remove( IEnumerable ids ); - /// - /// 移除持久化对象集合 - /// - /// 持久化对象编号集合 - Task RemoveAsync( IEnumerable ids ); - /// - /// 移除持久化对象集合 - /// - /// 持久化对象集合 - void Remove( IEnumerable pos ); - /// - /// 移除持久化对象集合 - /// - /// 持久化对象集合 - Task RemoveAsync( IEnumerable pos ); - } -} diff --git a/src/Util/Datas/Persistence/PersistentObjectBase.cs b/src/Util/Datas/Persistence/PersistentObjectBase.cs deleted file mode 100644 index 3560f716f..000000000 --- a/src/Util/Datas/Persistence/PersistentObjectBase.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Util.Datas.Persistence { - /// - /// 持久化对象 - /// - /// 标识类型 - public abstract class PersistentObjectBase : IPersistentObject { - /// - /// 标识 - /// - [Key] - public TKey Id { get; set; } - - /// - /// 相等运算 - /// - public override bool Equals( object other ) { - return this == (PersistentObjectBase)other; - } - - /// - /// 获取哈希 - /// - public override int GetHashCode() { - return ReferenceEquals( Id, null ) ? 0 : Id.GetHashCode(); - } - - /// - /// 相等比较 - /// - public static bool operator ==( PersistentObjectBase left, PersistentObjectBase right ) { - if( (object)left == null && (object)right == null ) - return true; - if( (object)left == null || (object)right == null ) - return false; - if ( left.GetType() != right.GetType() ) - return false; - if( Equals( left.Id, null ) ) - return false; - if( left.Id.Equals( default( TKey ) ) ) - return false; - return left.Id.Equals( right.Id ); - } - - /// - /// 不相等比较 - /// - public static bool operator !=( PersistentObjectBase left, PersistentObjectBase right ) { - return !( left == right ); - } - - /// - /// 版本号(乐观锁) - /// - public byte[] Version { get; set; } - } -} diff --git a/src/Util/Datas/Queries/Boundary.cs b/src/Util/Datas/Queries/Boundary.cs deleted file mode 100644 index f89cebb73..000000000 --- a/src/Util/Datas/Queries/Boundary.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Util.Datas.Queries { - /// - /// 查询边界 - /// - public enum Boundary { - /// - /// 包含左边 - /// - Left, - /// - /// 包含右边 - /// - Right, - /// - /// 包含两边 - /// - Both, - /// - /// 不包含 - /// - Neither - } -} diff --git a/src/Util/Datas/Queries/Criterias/AndCriteria.cs b/src/Util/Datas/Queries/Criterias/AndCriteria.cs deleted file mode 100644 index 73ff54836..000000000 --- a/src/Util/Datas/Queries/Criterias/AndCriteria.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Linq.Expressions; -using Util.Domains; -using Util.Domains.Repositories; - -namespace Util.Datas.Queries.Criterias { - /// - /// 与查询条件 - /// - /// 实体类型 - public class AndCriteria : ICriteria where TEntity : class { - /// - /// 初始化查询条件 - /// - /// 查询条件1 - /// 查询条件2 - public AndCriteria( Expression> left, Expression> right ) { - Predicate = left.And( right ); - } - - /// - /// 查询条件 - /// - protected Expression> Predicate { get; set; } - - /// - /// 获取查询条件 - /// - public virtual Expression> GetPredicate() { - return Predicate; - } - } -} diff --git a/src/Util/Datas/Queries/Criterias/DateSegmentCriteria.cs b/src/Util/Datas/Queries/Criterias/DateSegmentCriteria.cs deleted file mode 100644 index 309b21e5b..000000000 --- a/src/Util/Datas/Queries/Criterias/DateSegmentCriteria.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Linq.Expressions; -using Util.Domains; - -namespace Util.Datas.Queries.Criterias { - /// - /// 日期范围过滤条件 - 不包含时间 - /// - /// 实体类型 - /// 属性类型 - public class DateSegmentCriteria : SegmentCriteriaBase where TEntity : class { - /// - /// 初始化日期范围过滤条件 - 不包含时间 - /// - /// 属性表达式 - /// 最小值 - /// 最大值 - /// 包含边界 - public DateSegmentCriteria( Expression> propertyExpression, DateTime? min, DateTime? max, Boundary boundary = Boundary.Left ) - : base( propertyExpression, min, max, boundary ) { - } - - /// - /// 最小值是否大于最大值 - /// - protected override bool IsMinGreaterMax( DateTime? min, DateTime? max ) { - return min > max; - } - - /// - /// 获取最小值 - /// - protected override DateTime? GetMinValue() { - return base.GetMinValue().SafeValue().Date; - } - - /// - /// 获取最大值 - /// - protected override DateTime? GetMaxValue() { - return base.GetMaxValue().SafeValue().Date.AddDays( 1 ); - } - } -} diff --git a/src/Util/Datas/Queries/Criterias/DateTimeSegmentCriteria.cs b/src/Util/Datas/Queries/Criterias/DateTimeSegmentCriteria.cs deleted file mode 100644 index c311a599a..000000000 --- a/src/Util/Datas/Queries/Criterias/DateTimeSegmentCriteria.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Linq.Expressions; -using Util.Domains; - -namespace Util.Datas.Queries.Criterias { - /// - /// 日期范围过滤条件 - 包含时间 - /// - /// 实体类型 - /// 属性类型 - public class DateTimeSegmentCriteria : SegmentCriteriaBase where TEntity : class { - /// - /// 初始化日期范围过滤条件 - 包含时间 - /// - /// 属性表达式 - /// 最小值 - /// 最大值 - /// 包含边界 - public DateTimeSegmentCriteria( Expression> propertyExpression, DateTime? min, DateTime? max, Boundary boundary = Boundary.Both ) - : base( propertyExpression, min, max, boundary ) { - } - - /// - /// 最小值是否大于最大值 - /// - protected override bool IsMinGreaterMax( DateTime? min, DateTime? max ) { - return min > max; - } - } -} diff --git a/src/Util/Datas/Queries/Criterias/DecimalSegmentCriteria.cs b/src/Util/Datas/Queries/Criterias/DecimalSegmentCriteria.cs deleted file mode 100644 index 83e2e4615..000000000 --- a/src/Util/Datas/Queries/Criterias/DecimalSegmentCriteria.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Linq.Expressions; -using Util.Domains; - -namespace Util.Datas.Queries.Criterias { - /// - /// decimal范围过滤条件 - /// - /// 实体类型 - /// 属性类型 - public class DecimalSegmentCriteria : SegmentCriteriaBase where TEntity : class { - /// - /// 初始化decimal范围过滤条件 - /// - /// 属性表达式 - /// 最小值 - /// 最大值 - /// 包含边界 - public DecimalSegmentCriteria( Expression> propertyExpression, decimal? min, decimal? max, Boundary boundary = Boundary.Both ) - : base( propertyExpression, min, max, boundary ) { - } - - /// - /// 最小值是否大于最大值 - /// - protected override bool IsMinGreaterMax( decimal? min, decimal? max ) { - return min > max; - } - } -} diff --git a/src/Util/Datas/Queries/Criterias/DefaultCriteria.cs b/src/Util/Datas/Queries/Criterias/DefaultCriteria.cs deleted file mode 100644 index 1f513b894..000000000 --- a/src/Util/Datas/Queries/Criterias/DefaultCriteria.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Linq.Expressions; -using Util.Domains; -using Util.Domains.Repositories; - -namespace Util.Datas.Queries.Criterias { - /// - /// 默认查询条件 - /// - /// 实体类型 - public class DefaultCriteria : ICriteria where TEntity : class { - /// - /// 初始化查询条件 - /// - /// 查询条件 - public DefaultCriteria( Expression> predicate ) { - Predicate = predicate; - } - - /// - /// 查询条件 - /// - protected Expression> Predicate { get; set; } - - /// - /// 获取查询条件 - /// - public virtual Expression> GetPredicate() { - return Predicate; - } - } -} diff --git a/src/Util/Datas/Queries/Criterias/DoubleSegmentCriteria.cs b/src/Util/Datas/Queries/Criterias/DoubleSegmentCriteria.cs deleted file mode 100644 index 5f993abe0..000000000 --- a/src/Util/Datas/Queries/Criterias/DoubleSegmentCriteria.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Linq.Expressions; -using Util.Domains; - -namespace Util.Datas.Queries.Criterias { - /// - /// double范围过滤条件 - /// - /// 实体类型 - /// 属性类型 - public class DoubleSegmentCriteria : SegmentCriteriaBase where TEntity : class { - /// - /// 初始化double范围过滤条件 - /// - /// 属性表达式 - /// 最小值 - /// 最大值 - /// 包含边界 - public DoubleSegmentCriteria( Expression> propertyExpression, double? min, double? max, Boundary boundary = Boundary.Both ) - : base( propertyExpression, min, max, boundary ) { - } - - /// - /// 最小值是否大于最大值 - /// - protected override bool IsMinGreaterMax( double? min, double? max ) { - return min > max; - } - } -} diff --git a/src/Util/Datas/Queries/Criterias/IntSegmentCriteria.cs b/src/Util/Datas/Queries/Criterias/IntSegmentCriteria.cs deleted file mode 100644 index 01229c6ad..000000000 --- a/src/Util/Datas/Queries/Criterias/IntSegmentCriteria.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Linq.Expressions; -using Util.Domains; - -namespace Util.Datas.Queries.Criterias { - /// - /// 整数范围过滤条件 - /// - /// 实体类型 - /// 属性类型 - public class IntSegmentCriteria : SegmentCriteriaBase where TEntity : class { - /// - /// 初始化整数范围过滤条件 - /// - /// 属性表达式 - /// 最小值 - /// 最大值 - /// 包含边界 - public IntSegmentCriteria( Expression> propertyExpression, int? min, int? max, Boundary boundary = Boundary.Both ) - : base( propertyExpression,min,max, boundary ) { - } - - /// - /// 最小值是否大于最大值 - /// - protected override bool IsMinGreaterMax( int? min, int? max ) { - return min > max; - } - } -} diff --git a/src/Util/Datas/Queries/Criterias/OrCriteria.cs b/src/Util/Datas/Queries/Criterias/OrCriteria.cs deleted file mode 100644 index d9cc8e4a5..000000000 --- a/src/Util/Datas/Queries/Criterias/OrCriteria.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Linq.Expressions; -using Util.Domains; -using Util.Domains.Repositories; - -namespace Util.Datas.Queries.Criterias { - /// - /// 或查询条件 - /// - /// 实体类型 - public class OrCriteria : ICriteria where TEntity : class { - /// - /// 初始化查询条件 - /// - /// 查询条件1 - /// 查询条件2 - public OrCriteria( Expression> left, Expression> right ) { - Predicate = left.Or( right ); - } - - /// - /// 查询条件 - /// - protected Expression> Predicate { get; set; } - - /// - /// 获取查询条件 - /// - public virtual Expression> GetPredicate() { - return Predicate; - } - } -} diff --git a/src/Util/Datas/Queries/Criterias/SegmentCriteriaBase.cs b/src/Util/Datas/Queries/Criterias/SegmentCriteriaBase.cs deleted file mode 100644 index c3e4f1390..000000000 --- a/src/Util/Datas/Queries/Criterias/SegmentCriteriaBase.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.Linq.Expressions; -using Util.Domains; -using Util.Domains.Repositories; -using Util.Expressions; - -namespace Util.Datas.Queries.Criterias { - /// - /// 范围过滤条件 - /// - /// 实体类型 - /// 属性类型 - /// 值类型 - public abstract class SegmentCriteriaBase : ICriteria - where TEntity : class - where TValue : struct { - /// - /// 属性表达式 - /// - private readonly Expression> _propertyExpression; - /// - /// 表达式生成器 - /// - private readonly PredicateExpressionBuilder _builder; - /// - /// 最小值 - /// - private TValue? _min; - /// - /// 最大值 - /// - private TValue? _max; - /// - /// 包含边界 - /// - private readonly Boundary _boundary; - - /// - /// 初始化范围过滤条件 - /// - /// 属性表达式 - /// 最小值 - /// 最大值 - /// 包含边界 - protected SegmentCriteriaBase( Expression> propertyExpression, TValue? min, TValue? max, Boundary boundary ) { - _builder = new PredicateExpressionBuilder(); - _propertyExpression = propertyExpression; - _min = min; - _max = max; - _boundary = boundary; - } - - /// - /// 获取查询条件 - /// - public Expression> GetPredicate() { - Adjust( _min, _max ); - CreateLeftExpression(); - CreateRightExpression(); - return _builder.ToLambda(); - } - - /// - /// 当最小值大于最大值时进行校正 - /// - private void Adjust( TValue? min, TValue? max ) { - if( IsMinGreaterMax( min, max ) == false ) - return; - _min = max; - _max = min; - } - - /// - /// 最小值是否大于最大值 - /// - /// 最小值 - /// 最大值 - protected abstract bool IsMinGreaterMax( TValue? min, TValue? max ); - - /// - /// 创建左操作数,即 t => t.Property >= Min - /// - private void CreateLeftExpression() { - if( _min == null ) - return; - _builder.Append( _propertyExpression, CreateLeftOperator( _boundary ), GetMinValue() ); - } - - /// - /// 创建左操作符 - /// - protected virtual Operator CreateLeftOperator( Boundary? boundary ) { - switch( boundary ) { - case Boundary.Left: - return Operator.GreaterEqual; - case Boundary.Both: - return Operator.GreaterEqual; - default: - return Operator.Greater; - } - } - - /// - /// 获取最小值 - /// - protected virtual TValue? GetMinValue() { - return _min; - } - - /// - /// 创建右操作数,即 t => t.Property <= Max - /// - private void CreateRightExpression() { - if( _max == null ) - return; - _builder.Append( _propertyExpression, CreateRightOperator( _boundary ), GetMaxValue() ); - } - - /// - /// 创建右操作符 - /// - protected virtual Operator CreateRightOperator( Boundary? boundary ) { - switch( boundary ) { - case Boundary.Right: - return Operator.LessEqual; - case Boundary.Both: - return Operator.LessEqual; - default: - return Operator.Less; - } - } - - /// - /// 获取最大值 - /// - protected virtual TValue? GetMaxValue() { - return _max; - } - } -} diff --git a/src/Util/Datas/Queries/Extensions.Queryable.cs b/src/Util/Datas/Queries/Extensions.Queryable.cs deleted file mode 100644 index 5bd6f5ecb..000000000 --- a/src/Util/Datas/Queries/Extensions.Queryable.cs +++ /dev/null @@ -1,160 +0,0 @@ -using System; -using System.Linq; -using System.Linq.Expressions; -using Util.Datas.Queries.Criterias; -using Util.Datas.Queries.Internal; -using Util.Domains.Repositories; - -namespace Util.Datas.Queries { - /// - /// 查询扩展 - /// - public static class Extensions { - /// - /// 添加查询条件 - /// - /// 实体类型 - /// 数据源 - /// 查询条件对象 - public static IQueryable Where( this IQueryable source, ICriteria criteria ) where TEntity : class { - if( source == null ) - throw new ArgumentNullException( nameof( source ) ); - if( criteria == null ) - throw new ArgumentNullException( nameof( criteria ) ); - var predicate = criteria.GetPredicate(); - if( predicate == null ) - return source; - return source.Where( predicate ); - } - - /// - /// 添加查询条件 - /// - /// 实体类型 - /// 数据源 - /// 查询条件 - /// 该值为true时添加查询条件,否则忽略 - public static IQueryable WhereIf( this IQueryable source, Expression> predicate, bool condition ) where TEntity : class { - if( source == null ) - throw new ArgumentNullException( nameof( source ) ); - if( condition == false ) - return source; - return source.Where( predicate ); - } - - /// - /// 添加查询条件 - /// - /// 实体类型 - /// 数据源 - /// 查询条件,如果参数值为空,则忽略该查询条件,范例:t => t.Name == "",该查询条件被忽略。 - /// 注意:一次仅能添加一个条件,范例:t => t.Name == "a" && t.Mobile == "123",不支持,将抛出异常 - public static IQueryable WhereIfNotEmpty( this IQueryable source, Expression> predicate ) where TEntity : class { - if( source == null ) - throw new ArgumentNullException( nameof( source ) ); - predicate = Helper.GetWhereIfNotEmptyExpression( predicate ); - if( predicate == null ) - return source; - return source.Where( predicate ); - } - - /// - /// 添加范围查询条件 - /// - /// 实体类型 - /// 数据源 - /// 属性类型 - /// 属性表达式,范例:t => t.Age - /// 最小值 - /// 最大值 - /// 包含边界 - public static IQueryable Between( this IQueryable source, Expression> propertyExpression, int? min, int? max, Boundary boundary = Boundary.Both ) where TEntity : class { - if( source == null ) - throw new ArgumentNullException( nameof( source ) ); - return source.Where( new IntSegmentCriteria( propertyExpression, min, max, boundary ) ); - } - - /// - /// 添加范围查询条件 - /// - /// 实体类型 - /// 数据源 - /// 属性类型 - /// 属性表达式,范例:t => t.Age - /// 最小值 - /// 最大值 - /// 包含边界 - public static IQueryable Between( this IQueryable source, Expression> propertyExpression, double? min, double? max, Boundary boundary = Boundary.Both ) where TEntity : class { - if( source == null ) - throw new ArgumentNullException( nameof( source ) ); - return source.Where( new DoubleSegmentCriteria( propertyExpression, min, max, boundary ) ); - } - - /// - /// 添加范围查询条件 - /// - /// 实体类型 - /// 数据源 - /// 属性类型 - /// 属性表达式,范例:t => t.Age - /// 最小值 - /// 最大值 - /// 包含边界 - public static IQueryable Between( this IQueryable source, Expression> propertyExpression, decimal? min, decimal? max, Boundary boundary = Boundary.Both ) where TEntity : class { - if( source == null ) - throw new ArgumentNullException( nameof( source ) ); - return source.Where( new DecimalSegmentCriteria( propertyExpression, min, max, boundary ) ); - } - - /// - /// 添加范围查询条件 - /// - /// 实体类型 - /// 数据源 - /// 属性类型 - /// 属性表达式,范例:t => t.Time - /// 最小值 - /// 最大值 - /// 是否包含时间 - /// 包含边界 - public static IQueryable Between( this IQueryable source, Expression> propertyExpression, DateTime? min, DateTime? max, bool includeTime = true, Boundary? boundary = null ) where TEntity : class { - if( source == null ) - throw new ArgumentNullException( nameof( source ) ); - if( includeTime ) - return source.Where( new DateTimeSegmentCriteria( propertyExpression, min, max, boundary ?? Boundary.Both ) ); - return source.Where( new DateSegmentCriteria( propertyExpression, min, max, boundary ?? Boundary.Left ) ); - } - - /// - /// 分页 - /// - /// 实体类型 - /// 数据源 - /// 分页对象 - public static IQueryable Pager( this IQueryable source, IPager pager ) { - if( source == null ) - throw new ArgumentNullException( nameof( source ) ); - if( pager == null ) - throw new ArgumentNullException( nameof( pager ) ); - if( pager.TotalCount <= 0 ) - pager.TotalCount = source.Count(); - return source.Skip( pager.GetSkipCount() ).Take( pager.PageSize ); - } - - /// - /// 转换为分页列表 - /// - /// 实体类型 - /// 数据源 - /// 分页对象 - public static PagerList ToPagerList( this IQueryable source, IPager pager ) { - if( source == null ) - throw new ArgumentNullException( nameof( source ) ); - if( pager == null ) - throw new ArgumentNullException( nameof( pager ) ); - var result = new PagerList( pager ); - result.AddRange( source.ToList() ); - return result; - } - } -} diff --git a/src/Util/Datas/Queries/IQuery.cs b/src/Util/Datas/Queries/IQuery.cs deleted file mode 100644 index 9084faf5e..000000000 --- a/src/Util/Datas/Queries/IQuery.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System; -using System.Linq.Expressions; -using Util.Domains.Repositories; - -namespace Util.Datas.Queries { - /// - /// 查询对象 - /// - /// 实体类型 - public interface IQuery : IQuery where TEntity : class { - } - - /// - /// 查询对象 - /// - /// 实体类型 - /// 实体标识类型 - public interface IQuery : IQueryBase where TEntity : class { - /// - /// 添加查询条件 - /// - /// 查询条件 - IQuery Where( Expression> predicate ); - /// - /// 添加查询条件 - /// - /// 查询条件 - IQuery Where( ICriteria criteria ); - /// - /// 添加查询条件 - /// - /// 查询条件 - /// 该值为true时添加查询条件,否则忽略 - IQuery WhereIf( Expression> predicate, bool condition ); - /// - /// 添加查询条件 - /// - /// 查询条件,如果参数值为空,则忽略该查询条件,范例:t => t.Name == "",该查询条件被忽略。 - /// 注意:一次仅能添加一个条件,范例:t => t.Name == "a" && t.Mobile == "123",不支持,将抛出异常 - IQuery WhereIfNotEmpty( Expression> predicate ); - /// - /// 添加范围查询条件 - /// - /// 属性类型 - /// 属性表达式,范例:t => t.Age - /// 最小值 - /// 最大值 - /// 包含边界 - IQuery Between( Expression> propertyExpression, int? min, int? max, Boundary boundary = Boundary.Both ); - /// - /// 添加范围查询条件 - /// - /// 属性类型 - /// 属性表达式,范例:t => t.Age - /// 最小值 - /// 最大值 - /// 包含边界 - IQuery Between( Expression> propertyExpression, double? min, double? max, Boundary boundary = Boundary.Both ); - /// - /// 添加范围查询条件 - /// - /// 属性类型 - /// 属性表达式,范例:t => t.Age - /// 最小值 - /// 最大值 - /// 包含边界 - IQuery Between( Expression> propertyExpression, decimal? min, decimal? max, Boundary boundary = Boundary.Both ); - /// - /// 添加范围查询条件 - /// - /// 属性类型 - /// 属性表达式,范例:t => t.Time - /// 最小值 - /// 最大值 - /// 是否包含时间 - /// 包含边界 - IQuery Between( Expression> propertyExpression, DateTime? min, DateTime? max,bool includeTime = true, Boundary? boundary = null ); - /// - /// 添加排序 - /// - /// 属性类型 - /// 属性表达式 - /// 是否降序 - IQuery OrderBy( Expression> expression, bool desc = false ); - /// - /// 添加排序 - /// - /// 排序属性 - /// 是否降序 - IQuery OrderBy( string propertyName, bool desc = false ); - /// - /// 与连接 - /// - /// 查询条件 - IQuery And( Expression> predicate ); - /// - /// 与连接 - /// - /// 查询对象 - IQuery And( IQuery query ); - /// - /// 或连接 - /// - /// 查询条件 - IQuery Or( params Expression>[] predicates ); - /// - /// 或连接 - /// - /// 查询对象 - IQuery Or( IQuery query ); - } -} diff --git a/src/Util/Datas/Queries/IQueryParameter.cs b/src/Util/Datas/Queries/IQueryParameter.cs deleted file mode 100644 index e8ed4dea8..000000000 --- a/src/Util/Datas/Queries/IQueryParameter.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Util.Domains.Repositories; - -namespace Util.Datas.Queries { - /// - /// 查询参数 - /// - public interface IQueryParameter : IPager { - /// - /// 搜索关键字 - /// - string Keyword { get; set; } - } -} diff --git a/src/Util/Datas/Queries/Internal/Helper.cs b/src/Util/Datas/Queries/Internal/Helper.cs deleted file mode 100644 index 53b514b69..000000000 --- a/src/Util/Datas/Queries/Internal/Helper.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Linq.Expressions; -using Util.Helpers; -using Util.Properties; - -namespace Util.Datas.Queries.Internal { - /// - /// 查询工具类 - /// - internal static class Helper { - /// - /// 获取查询条件表达式 - /// - /// 查询条件,如果参数值为空,则忽略该查询条件,范例:t => t.Name == "",该查询条件被忽略。 - /// 注意:一次仅能添加一个条件,范例:t => t.Name == "a" && t.Mobile == "123",不支持,将抛出异常 - public static Expression> GetWhereIfNotEmptyExpression( Expression> predicate ) where TEntity : class { - if ( predicate == null ) - return null; - if( Lambda.GetConditionCount( predicate ) > 1 ) - throw new InvalidOperationException( string.Format( LibraryResource.OnlyOnePredicate, predicate ) ); - var value = predicate.Value(); - if( string.IsNullOrWhiteSpace( value.SafeString() ) ) - return null; - return predicate; - } - } -} diff --git a/src/Util/Datas/Queries/Operator.cs b/src/Util/Datas/Queries/Operator.cs deleted file mode 100644 index 2e466bbd0..000000000 --- a/src/Util/Datas/Queries/Operator.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.ComponentModel; - -namespace Util.Datas.Queries { - /// - /// 查询操作符 - /// - public enum Operator { - /// - /// 等于 - /// - [Description( "等于" )] - Equal, - /// - /// 不等于 - /// - [Description( "不等于" )] - NotEqual, - /// - /// 大于 - /// - [Description( "大于" )] - Greater, - /// - /// 大于等于 - /// - [Description( "大于等于" )] - GreaterEqual, - /// - /// 小于 - /// - [Description( "小于" )] - Less, - /// - /// 小于等于 - /// - [Description( "小于等于" )] - LessEqual, - /// - /// 头匹配 - /// - [Description( "头匹配" )] - Starts, - /// - /// 尾匹配 - /// - [Description( "尾匹配" )] - Ends, - /// - /// 模糊匹配 - /// - [Description( "模糊匹配" )] - Contains - } -} diff --git a/src/Util/Datas/Queries/OrderByBuilder.cs b/src/Util/Datas/Queries/OrderByBuilder.cs deleted file mode 100644 index c257efc2c..000000000 --- a/src/Util/Datas/Queries/OrderByBuilder.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace Util.Datas.Queries { - /// - /// 排序生成器 - /// - public class OrderByBuilder { - /// - /// 排序项列表 - /// - private readonly List _items; - - /// - /// 初始化排序生成器 - /// - public OrderByBuilder() { - _items = new List(); - } - - /// - /// 添加排序 - /// - /// 排序属性 - /// 是否降序 - public void Add( string name, bool desc = false ) { - if( string.IsNullOrWhiteSpace( name ) ) - return; - _items.Add( new OrderByItem( name, desc ) ); - } - - /// - /// 生成排序字符串 - /// - public string Generate() { - return _items.Select( t => t.Generate() ).ToList().Join(); - } - } -} diff --git a/src/Util/Datas/Queries/OrderByItem.cs b/src/Util/Datas/Queries/OrderByItem.cs deleted file mode 100644 index 59589978a..000000000 --- a/src/Util/Datas/Queries/OrderByItem.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace Util.Datas.Queries { - /// - /// 排序项 - /// - public class OrderByItem { - /// - /// 初始化排序项 - /// - /// 排序属性 - /// 是否降序 - public OrderByItem( string name, bool desc ) { - Name = name; - Desc = desc; - } - - /// - /// 排序属性 - /// - public string Name { get; } - - /// - /// 是否降序 - /// - public bool Desc { get; } - - /// - /// 创建排序字符串 - /// - public string Generate() { - if( Desc ) - return $"{Name} desc"; - return Name; - } - } -} diff --git a/src/Util/Datas/Queries/Query.cs b/src/Util/Datas/Queries/Query.cs deleted file mode 100644 index 72b93bcbe..000000000 --- a/src/Util/Datas/Queries/Query.cs +++ /dev/null @@ -1,240 +0,0 @@ -using System; -using System.Linq.Expressions; -using Util.Datas.Queries.Criterias; -using Util.Datas.Queries.Internal; -using Util.Domains.Repositories; -using Util.Helpers; -using Util.Properties; - -namespace Util.Datas.Queries { - /// - /// 查询对象 - /// - /// 实体类型 - public class Query : Query, IQuery where TEntity : class { - /// - /// 初始化查询对象 - /// - public Query() { - } - - /// - /// 初始化查询对象 - /// - /// 查询参数 - public Query( IQueryParameter queryParam ) : base( queryParam ) { - } - } - - /// - /// 查询对象 - /// - /// 实体类型 - /// 实体标识类型 - public class Query : IQuery where TEntity : class { - /// - /// 查询参数 - /// - private readonly IQueryParameter _parameter; - /// - /// 查询条件 - /// - private Expression> _predicate; - /// - /// 排序生成器 - /// - private readonly OrderByBuilder _orderByBuilder; - - /// - /// 初始化查询对象 - /// - public Query() : this( new QueryParameter() ) { - } - - /// - /// 初始化查询对象 - /// - /// 查询参数 - public Query( IQueryParameter parameter ) { - _parameter = parameter; - _orderByBuilder = new OrderByBuilder(); - OrderBy( parameter.Order ); - } - - /// - /// 获取查询条件 - /// - public Expression> GetPredicate() { - return _predicate; - } - - /// - /// 获取排序条件 - /// - public string GetOrder() { - return _orderByBuilder.Generate(); - } - - /// - /// 获取分页 - /// - public IPager GetPager() { - return new Pager( _parameter.Page, _parameter.PageSize, _parameter.TotalCount, GetOrder() ); - } - - /// - /// 添加查询条件 - /// - /// 查询条件 - public IQuery Where( Expression> predicate ) { - return And( predicate ); - } - - /// - /// 添加查询条件 - /// - /// 查询条件 - public IQuery Where( ICriteria criteria ) { - return And( criteria.GetPredicate() ); - } - - /// - /// 添加查询条件 - /// - /// 查询条件 - /// 该值为true时添加查询条件,否则忽略 - public IQuery WhereIf( Expression> predicate, bool condition ) { - if( condition == false ) - return this; - return Where( predicate ); - } - - /// - /// 添加查询条件 - /// - /// 查询条件,如果参数值为空,则忽略该查询条件,范例:t => t.Name == "",该查询条件被忽略。 - /// 注意:一次仅能添加一个条件,范例:t => t.Name == "a" && t.Mobile == "123",不支持,将抛出异常 - public IQuery WhereIfNotEmpty( Expression> predicate ) { - predicate = Helper.GetWhereIfNotEmptyExpression( predicate ); - if( predicate == null ) - return this; - return And( predicate ); - } - - /// - /// 添加范围查询条件 - /// - /// 属性类型 - /// 属性表达式,范例:t => t.Age - /// 最小值 - /// 最大值 - /// 包含边界 - public IQuery Between( Expression> propertyExpression, int? min, int? max, Boundary boundary = Boundary.Both ) { - return Where( new IntSegmentCriteria( propertyExpression, min, max, boundary ) ); - } - - /// - /// 添加范围查询条件 - /// - /// 属性类型 - /// 属性表达式,范例:t => t.Age - /// 最小值 - /// 最大值 - /// 包含边界 - public IQuery Between( Expression> propertyExpression, double? min, double? max, Boundary boundary = Boundary.Both ) { - return Where( new DoubleSegmentCriteria( propertyExpression, min, max, boundary ) ); - } - - /// - /// 添加范围查询条件 - /// - /// 属性类型 - /// 属性表达式,范例:t => t.Age - /// 最小值 - /// 最大值 - /// 包含边界 - public IQuery Between( Expression> propertyExpression, decimal? min, decimal? max, Boundary boundary = Boundary.Both ) { - return Where( new DecimalSegmentCriteria( propertyExpression, min, max, boundary ) ); - } - - /// - /// 添加范围查询条件 - /// - /// 属性类型 - /// 属性表达式,范例:t => t.Time - /// 最小值 - /// 最大值 - /// 是否包含时间 - /// 包含边界 - public IQuery Between( Expression> propertyExpression, DateTime? min, DateTime? max, bool includeTime = true, Boundary? boundary = null ) { - if( includeTime ) - return Where( new DateTimeSegmentCriteria( propertyExpression, min, max, boundary ?? Boundary.Both ) ); - return Where( new DateSegmentCriteria( propertyExpression, min, max, boundary ?? Boundary.Left ) ); - } - - /// - /// 添加排序 - /// - /// 属性类型 - /// 属性表达式 - /// 是否降序 - public IQuery OrderBy( Expression> expression, bool desc = false ) { - return OrderBy( Lambda.GetName( expression ), desc ); - } - - /// - /// 添加排序 - /// - /// 排序属性 - /// 是否降序 - public IQuery OrderBy( string propertyName, bool desc = false ) { - _orderByBuilder.Add( propertyName, desc ); - return this; - } - - /// - /// 与连接 - /// - /// 查询条件 - public IQuery And( Expression> predicate ) { - _predicate = _predicate.And( predicate ); - return this; - } - - /// - /// 与连接 - /// - /// 查询对象 - public IQuery And( IQuery query ) { - And( query.GetPredicate() ); - OrderBy( query.GetOrder() ); - return this; - } - - /// - /// 或连接 - /// - /// 查询条件 - public IQuery Or( params Expression>[] predicates ) { - if ( predicates == null ) - return this; - foreach ( var item in predicates ) { - var predicate = Helper.GetWhereIfNotEmptyExpression( item ); - if( predicate == null ) - continue; - _predicate = _predicate.Or( predicate ); - } - return this; - } - - /// - /// 或连接 - /// - /// 查询对象 - public IQuery Or( IQuery query ) { - _predicate = _predicate.Or( query.GetPredicate() ); - OrderBy( query.GetOrder() ); - return this; - } - } -} diff --git a/src/Util/Datas/Queries/QueryParameter.cs b/src/Util/Datas/Queries/QueryParameter.cs deleted file mode 100644 index 4093c81ad..000000000 --- a/src/Util/Datas/Queries/QueryParameter.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Util.Domains.Repositories; - -namespace Util.Datas.Queries { - /// - /// 查询参数 - /// - public class QueryParameter : Pager, IQueryParameter { - /// - /// 搜索关键字 - /// - public string Keyword { get; set; } - - /// - /// 添加描述 - /// - protected override void AddDescriptions() { - base.AddDescriptions(); - AddDescription( "Keyword", Keyword ); - } - } -} diff --git a/src/Util/Datas/Queries/Trees/ITreeQueryParameter.cs b/src/Util/Datas/Queries/Trees/ITreeQueryParameter.cs deleted file mode 100644 index 448067f3c..000000000 --- a/src/Util/Datas/Queries/Trees/ITreeQueryParameter.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; - -namespace Util.Datas.Queries.Trees { - /// - /// 树型查询参数 - /// - /// 父编号类型 - public interface ITreeQueryParameter : IQueryParameter { - /// - /// 父编号 - /// - TParentId ParentId { get; set; } - /// - /// 级数 - /// - int? Level { get; set; } - /// - /// 路径 - /// - string Path { get; set; } - /// - /// 启用 - /// - [Display( Name = "启用" )] - bool? Enabled { get; set; } - } - - /// - /// 树型查询参数 - /// - public interface ITreeQueryParameter : ITreeQueryParameter { - } -} diff --git a/src/Util/Datas/Queries/Trees/TreeCriteria.cs b/src/Util/Datas/Queries/Trees/TreeCriteria.cs deleted file mode 100644 index d24377b2a..000000000 --- a/src/Util/Datas/Queries/Trees/TreeCriteria.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Linq.Expressions; -using Util.Domains.Repositories; -using Util.Domains.Trees; - -namespace Util.Datas.Queries.Trees { - /// - /// 树型查询条件 - /// - public class TreeCriteria : TreeCriteria where TEntity : ITreeEntity { - /// - /// 初始化树型查询条件 - /// - /// 查询参数 - public TreeCriteria( ITreeQueryParameter parameter ) : base( parameter ) { - if( parameter.ParentId != null ) - Predicate = Predicate.And( t => t.ParentId == parameter.ParentId ); - } - } - - /// - /// 树型查询条件 - /// - public class TreeCriteria : ICriteria where TEntity : ITreeEntity { - /// - /// 初始化树型查询条件 - /// - /// 查询参数 - public TreeCriteria( ITreeQueryParameter parameter ) { - if( parameter.Level != null ) - Predicate = Predicate.And( t => t.Level == parameter.Level ); - if( !string.IsNullOrWhiteSpace( parameter.Path ) ) - Predicate = Predicate.And( t => t.Path.StartsWith( parameter.Path ) ); - if( parameter.Enabled != null ) - Predicate = Predicate.And( t => t.Enabled == parameter.Enabled ); - } - - /// - /// 查询条件 - /// - protected Expression> Predicate { get; set; } - - /// - /// 获取查询条件 - /// - public Expression> GetPredicate() { - return Predicate; - } - } -} diff --git a/src/Util/Datas/Queries/Trees/TreeQueryParameter.cs b/src/Util/Datas/Queries/Trees/TreeQueryParameter.cs deleted file mode 100644 index d7a8c3be4..000000000 --- a/src/Util/Datas/Queries/Trees/TreeQueryParameter.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; - -namespace Util.Datas.Queries.Trees { - /// - /// 树型查询参数 - /// - public class TreeQueryParameter : QueryParameter, ITreeQueryParameter { - /// - /// 初始化树型查询参数 - /// - protected TreeQueryParameter() { - Order = "SortId"; - } - - /// - /// 父编号 - /// - public TParentId ParentId { get; set; } - - /// - /// 级数 - /// - public int? Level { get; set; } - - private string _path = string.Empty; - /// - /// 路径 - /// - public string Path { - get => _path == null ? string.Empty : _path.Trim(); - set => _path = value; - } - - /// - /// 启用 - /// - [Display( Name = "启用" )] - public bool? Enabled { get; set; } - - /// - /// 添加描述 - /// - protected override void AddDescriptions() { - base.AddDescriptions(); - AddDescription( "ParentId", ParentId ); - AddDescription( "Level", Level ); - AddDescription( "Path", Path ); - AddDescription( "Enabled", Enabled ); - } - } - - /// - /// 树型查询参数 - /// - public class TreeQueryParameter : TreeQueryParameter, ITreeQueryParameter { - } -} diff --git a/src/Util/Datas/UnitOfWorks/IUnitOfWork.cs b/src/Util/Datas/UnitOfWorks/IUnitOfWork.cs deleted file mode 100644 index 0c31883df..000000000 --- a/src/Util/Datas/UnitOfWorks/IUnitOfWork.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Threading.Tasks; -using Util.Aspects; - -namespace Util.Datas.UnitOfWorks { - /// - /// 工作单元 - /// - [Ignore] - public interface IUnitOfWork : IDisposable { - /// - /// 提交,返回影响的行数 - /// - int Commit(); - /// - /// 提交,返回影响的行数 - /// - Task CommitAsync(); - } -} diff --git a/src/Util/Datas/UnitOfWorks/IUnitOfWorkManager.cs b/src/Util/Datas/UnitOfWorks/IUnitOfWorkManager.cs deleted file mode 100644 index f35240100..000000000 --- a/src/Util/Datas/UnitOfWorks/IUnitOfWorkManager.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Threading.Tasks; -using Util.Dependency; - -namespace Util.Datas.UnitOfWorks { - /// - /// 工作单元服务 - /// - public interface IUnitOfWorkManager : IScopeDependency { - /// - /// 提交 - /// - void Commit(); - /// - /// 提交 - /// - Task CommitAsync(); - /// - /// 注册工作单元 - /// - /// 工作单元 - void Register( IUnitOfWork unitOfWork ); - } -} diff --git a/src/Util/Datas/UnitOfWorks/UnitOfWorkManager.cs b/src/Util/Datas/UnitOfWorks/UnitOfWorkManager.cs deleted file mode 100644 index d7a92be6a..000000000 --- a/src/Util/Datas/UnitOfWorks/UnitOfWorkManager.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Util.Datas.UnitOfWorks { - /// - /// 工作单元服务 - /// - public class UnitOfWorkManager : IUnitOfWorkManager { - /// - /// 工作单元集合 - /// - private readonly List _unitOfWorks; - - /// - /// 初始化工作单元服务 - /// - public UnitOfWorkManager() { - _unitOfWorks = new List(); - } - - /// - /// 提交 - /// - public void Commit() { - foreach( var unitOfWork in _unitOfWorks ) - unitOfWork.Commit(); - } - - /// - /// 提交 - /// - public async Task CommitAsync() { - foreach ( var unitOfWork in _unitOfWorks ) - await unitOfWork.CommitAsync(); - } - - /// - /// 注册工作单元 - /// - /// 工作单元 - public void Register( IUnitOfWork unitOfWork ) { - if( unitOfWork == null ) - throw new ArgumentNullException( nameof( unitOfWork ) ); - if( _unitOfWorks.Contains( unitOfWork ) == false ) - _unitOfWorks.Add( unitOfWork ); - } - } -} diff --git a/src/Util/Dependency/ConfigBase.cs b/src/Util/Dependency/ConfigBase.cs deleted file mode 100644 index 73e8eacf2..000000000 --- a/src/Util/Dependency/ConfigBase.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Autofac; - -namespace Util.Dependency { - /// - /// 配置 - /// - public abstract class ConfigBase : Module, IConfig { - } -} \ No newline at end of file diff --git a/src/Util/Dependency/Container.cs b/src/Util/Dependency/Container.cs deleted file mode 100644 index 39bed75f0..000000000 --- a/src/Util/Dependency/Container.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Autofac; -using Autofac.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection; -using Util.Helpers; - -namespace Util.Dependency { - /// - /// Autofac对象容器 - /// - internal class Container : IContainer { - /// - /// 容器 - /// - private Autofac.IContainer _container; - - /// - /// 创建集合 - /// - /// 对象类型 - /// 服务名称 - public List CreateList( string name = null ) { - var result = CreateList( typeof( T ), name ); - if( result == null ) - return new List(); - return ( (IEnumerable)result ).ToList(); - } - - /// - /// 创建集合 - /// - /// 对象类型 - /// 服务名称 - public object CreateList( Type type, string name = null ) { - Type serviceType = typeof( IEnumerable<> ).MakeGenericType( type ); - return Create( serviceType, name ); - } - - /// - /// 创建对象 - /// - /// 对象类型 - /// 服务名称 - public T Create( string name = null ) { - return (T)Create( typeof( T ), name ); - } - - /// - /// 创建对象 - /// - /// 对象类型 - /// 服务名称 - public object Create( Type type, string name = null ) { - return Web.HttpContext?.RequestServices != null ? GetServiceFromHttpContext( type, name ) : GetService( type, name ); - } - - /// - /// 从HttpContext获取服务 - /// - private object GetServiceFromHttpContext( Type type, string name ) { - var serviceProvider = Web.HttpContext.RequestServices; - if( name == null ) - return serviceProvider.GetService( type ); - var context = serviceProvider.GetService(); - return context.ResolveNamed( name, type ); - } - - /// - /// 获取服务 - /// - private object GetService( Type type, string name ) { - if( name == null ) - return _container.Resolve( type ); - return _container.ResolveNamed( name, type ); - } - - /// - /// 作用域开始 - /// - public IScope BeginScope() { - return new Scope( _container.BeginLifetimeScope() ); - } - - /// - /// 注册依赖 - /// - /// 依赖配置 - public void Register( params IConfig[] configs ) { - Register( null, null, configs ); - } - - /// - /// 注册依赖 - /// - /// 服务集合 - /// 依赖配置 - public IServiceProvider Register( IServiceCollection services, params IConfig[] configs ) { - return Register( services, null, configs ); - } - - /// - /// 注册依赖 - /// - /// 服务集合 - /// 注册前操作 - /// 依赖配置 - public IServiceProvider Register( IServiceCollection services, Action actionBefore, params IConfig[] configs ) { - var builder = CreateBuilder( services, actionBefore, configs ); - _container = builder.Build(); - return new AutofacServiceProvider( _container ); - } - - /// - /// 创建容器生成器 - /// - /// 服务集合 - /// 注册前执行的操作 - /// 依赖配置 - public ContainerBuilder CreateBuilder( IServiceCollection services, Action actionBefore, params IConfig[] configs ) { - var builder = new ContainerBuilder(); - actionBefore?.Invoke( builder ); - foreach( var config in configs ) - builder.RegisterModule( config ); - if( services != null ) - builder.Populate( services ); - return builder; - } - - /// - /// 释放容器 - /// - public void Dispose() { - _container.Dispose(); - } - } -} \ No newline at end of file diff --git a/src/Util/Dependency/DependencyConfiguration.cs b/src/Util/Dependency/DependencyConfiguration.cs deleted file mode 100644 index 085eb3424..000000000 --- a/src/Util/Dependency/DependencyConfiguration.cs +++ /dev/null @@ -1,163 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Autofac; -using Microsoft.Extensions.DependencyInjection; -using Util.Contexts; -using Util.Events.Handlers; -using Util.Helpers; -using Util.Reflections; - -namespace Util.Dependency { - /// - /// 依赖配置 - /// - public class DependencyConfiguration { - /// - /// 服务集合 - /// - private readonly IServiceCollection _services; - /// - /// 依赖配置 - /// - private readonly IConfig[] _configs; - /// - /// 容器生成器 - /// - private ContainerBuilder _builder; - /// - /// 类型查找器 - /// - private IFind _finder; - /// - /// 程序集列表 - /// - private List _assemblies; - - /// - /// 初始化依赖配置 - /// - /// 服务集合 - /// 依赖配置 - public DependencyConfiguration( IServiceCollection services, IConfig[] configs ) { - _services = services; - _configs = configs; - } - - /// - /// 配置依赖 - /// - public IServiceProvider Config() { - return Ioc.DefaultContainer.Register( _services, RegistServices, _configs ); - } - - /// - /// 注册服务集合 - /// - private void RegistServices( ContainerBuilder builder ) { - _builder = builder; - _finder = new WebFinder(); - _assemblies = _finder.GetAssemblies(); - RegistInfrastracture(); - RegistEventHandlers(); - RegistDependency(); - } - - /// - /// 注册基础设施 - /// - private void RegistInfrastracture() { - EnableAop(); - RegistFinder(); - RegistContext(); - } - - /// - /// 启用Aop - /// - private void EnableAop() { - _builder.EnableAop(); - } - - /// - /// 注册类型查找器 - /// - private void RegistFinder() { - _builder.AddSingleton( _finder ); - } - - /// - /// 注册上下文 - /// - private void RegistContext() { - _builder.AddSingleton(); - } - - /// - /// 注册事件处理器 - /// - private void RegistEventHandlers() { - var handlerTypes = GetTypes( typeof( IEventHandler<> ) ); - foreach( var handler in handlerTypes ) { - _builder.RegisterType( handler ).As( handler.FindInterfaces( - ( filter, criteria ) => filter.IsGenericType && ( (Type)criteria ).IsAssignableFrom( filter.GetGenericTypeDefinition() ) - , typeof( IEventHandler<> ) - ) ).InstancePerLifetimeScope(); - } - } - - /// - /// 查找并注册依赖 - /// - private void RegistDependency() { - RegistSingletonDependency(); - RegistScopeDependency(); - RegistTransientDependency(); - ResolveDependencyRegistrar(); - } - - /// - /// 注册单例依赖 - /// - private void RegistSingletonDependency() { - _builder.RegisterTypes( GetTypes() ).AsImplementedInterfaces().PropertiesAutowired().SingleInstance(); - } - - /// - /// 注册作用域依赖 - /// - private void RegistScopeDependency() { - _builder.RegisterTypes( GetTypes() ).AsImplementedInterfaces().PropertiesAutowired().InstancePerLifetimeScope(); - } - - /// - /// 注册瞬态依赖 - /// - private void RegistTransientDependency() { - _builder.RegisterTypes( GetTypes() ).AsImplementedInterfaces().PropertiesAutowired().InstancePerDependency(); - } - - /// - /// 解析依赖注册器 - /// - private void ResolveDependencyRegistrar() { - var types = GetTypes(); - types.Select( type => Reflection.CreateInstance( type ) ).ToList().ForEach( t => t.Regist( _services ) ); - } - - /// - /// 获取类型集合 - /// - private Type[] GetTypes() { - return _finder.Find( _assemblies ).ToArray(); - } - - /// - /// 获取类型集合 - /// - private Type[] GetTypes( Type type ) { - return _finder.Find( type, _assemblies ).ToArray(); - } - } -} diff --git a/src/Util/Dependency/Extensions.Aop.cs b/src/Util/Dependency/Extensions.Aop.cs deleted file mode 100644 index f6a65c37b..000000000 --- a/src/Util/Dependency/Extensions.Aop.cs +++ /dev/null @@ -1,29 +0,0 @@ -using AspectCore.DynamicProxy; -using AspectCore.DynamicProxy.Parameters; -using AspectCore.Extensions.AspectScope; -using AspectCore.Extensions.Autofac; -using Autofac; - -namespace Util.Dependency { - /// - /// AspectCore扩展 - /// - public static partial class Extensions { - /// - /// 启用Aop - /// - public static void EnableAop( this ContainerBuilder builder ) { - builder.RegisterDynamicProxy( config => config.EnableParameterAspect() ); - builder.EnableAspectScoped(); - } - - /// - /// 启用Aop作用域 - /// - public static void EnableAspectScoped( this ContainerBuilder builder ) { - builder.AddScoped(); - builder.AddScoped(); - builder.AddScoped(); - } - } -} diff --git a/src/Util/Dependency/Extensions.Ioc.cs b/src/Util/Dependency/Extensions.Ioc.cs deleted file mode 100644 index ed14d75a1..000000000 --- a/src/Util/Dependency/Extensions.Ioc.cs +++ /dev/null @@ -1,72 +0,0 @@ -using Autofac; -using Autofac.Builder; - -namespace Util.Dependency { - /// - /// Autofac扩展 - /// - public static partial class Extensions { - /// - /// 注册服务,生命周期为 InstancePerDependency(每次创建一个新实例) - /// - /// 接口类型 - /// 实现类型 - /// 容器生成器 - /// 服务名称 - public static IRegistrationBuilder - AddTransient( this ContainerBuilder builder, string name = null ) where TService : class where TImplementation : class, TService { - if( name == null ) - return builder.RegisterType().As().InstancePerDependency(); - return builder.RegisterType().Named( name ).InstancePerDependency(); - } - - /// - /// 注册服务,生命周期为 InstancePerLifetimeScope(每个请求一个实例) - /// - /// 接口类型 - /// 实现类型 - /// 容器生成器 - /// 服务名称 - public static IRegistrationBuilder - AddScoped( this ContainerBuilder builder, string name = null ) where TService : class where TImplementation : class, TService { - if( name == null ) - return builder.RegisterType().As().InstancePerLifetimeScope(); - return builder.RegisterType().Named( name ).InstancePerLifetimeScope(); - } - - /// - /// 注册服务,生命周期为 InstancePerLifetimeScope(每个请求一个实例) - /// - /// 实现类型 - /// 容器生成器 - public static IRegistrationBuilder - AddScoped( this ContainerBuilder builder ) where TImplementation : class { - return builder.RegisterType().InstancePerLifetimeScope(); - } - - /// - /// 注册服务,生命周期为 SingleInstance(单例) - /// - /// 接口类型 - /// 实现类型 - /// 容器生成器 - /// 服务名称 - public static IRegistrationBuilder - AddSingleton( this ContainerBuilder builder, string name = null ) where TService : class where TImplementation : class, TService { - if( name == null ) - return builder.RegisterType().As().SingleInstance(); - return builder.RegisterType().Named( name ).SingleInstance(); - } - - /// - /// 注册服务,生命周期为 SingleInstance(单例) - /// - /// 接口类型 - /// 容器生成器 - /// 服务实例 - public static IRegistrationBuilder - AddSingleton( this ContainerBuilder builder, TService instance ) where TService : class { - return builder.RegisterInstance( instance ).As().SingleInstance(); - } - } -} diff --git a/src/Util/Dependency/IConfig.cs b/src/Util/Dependency/IConfig.cs deleted file mode 100644 index b5d89ff5c..000000000 --- a/src/Util/Dependency/IConfig.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Autofac.Core; - -namespace Util.Dependency { - /// - /// 依赖配置 - /// - public interface IConfig : IModule { - } -} diff --git a/src/Util/Dependency/IContainer.cs b/src/Util/Dependency/IContainer.cs deleted file mode 100644 index 13c1b7fb6..000000000 --- a/src/Util/Dependency/IContainer.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.Extensions.DependencyInjection; - -namespace Util.Dependency { - /// - /// 容器 - /// - public interface IContainer : IDisposable { - /// - /// 创建集合 - /// - /// 对象类型 - /// 服务名称 - List CreateList( string name = null ); - - /// - /// 创建集合 - /// - /// 对象类型 - /// 服务名称 - object CreateList( Type type, string name = null ); - - /// - /// 创建实例 - /// - /// 实例类型 - /// 服务名称 - T Create( string name = null ); - - /// - /// 创建对象 - /// - /// 对象类型 - /// 服务名称 - object Create( Type type, string name = null ); - - /// - /// 作用域开始 - /// - IScope BeginScope(); - - /// - /// 注册依赖 - /// - /// 依赖配置 - void Register( params IConfig[] configs ); - - /// - /// 注册依赖 - /// - /// 服务集合 - /// 依赖配置 - IServiceProvider Register( IServiceCollection services, params IConfig[] configs ); - } -} diff --git a/src/Util/Dependency/IDependencyRegistrar.cs b/src/Util/Dependency/IDependencyRegistrar.cs deleted file mode 100644 index 130a47dfe..000000000 --- a/src/Util/Dependency/IDependencyRegistrar.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace Util.Dependency { - /// - /// 依赖注册器 - /// - public interface IDependencyRegistrar { - /// - /// 注册依赖 - /// - /// 服务集合 - void Regist( IServiceCollection services ); - } -} diff --git a/src/Util/Dependency/IScope.cs b/src/Util/Dependency/IScope.cs deleted file mode 100644 index 4562c552d..000000000 --- a/src/Util/Dependency/IScope.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace Util.Dependency { - /// - /// 作用域 - /// - public interface IScope : IDisposable { - /// - /// 创建实例 - /// - /// 实例类型 - T Create(); - - /// - /// 创建对象 - /// - /// 对象类型 - object Create( Type type ); - } -} diff --git a/src/Util/Dependency/IScopeDependency.cs b/src/Util/Dependency/IScopeDependency.cs deleted file mode 100644 index 12b629cad..000000000 --- a/src/Util/Dependency/IScopeDependency.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Dependency { - /// - /// 实现该接口将自动注册到Ioc容器,生命周期为每次请求创建一个实例 - /// - public interface IScopeDependency { - } -} diff --git a/src/Util/Dependency/ISingletonDependency.cs b/src/Util/Dependency/ISingletonDependency.cs deleted file mode 100644 index 7c23e1a77..000000000 --- a/src/Util/Dependency/ISingletonDependency.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Dependency { - /// - /// 实现该接口将自动注册到Ioc容器,生命周期为单例 - /// - public interface ISingletonDependency { - } -} diff --git a/src/Util/Dependency/ITransientDependency.cs b/src/Util/Dependency/ITransientDependency.cs deleted file mode 100644 index fafbbd2c4..000000000 --- a/src/Util/Dependency/ITransientDependency.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Dependency { - /// - /// 实现该接口将自动注册到Ioc容器,生命周期为每次创建一个新实例 - /// - public interface ITransientDependency { - } -} diff --git a/src/Util/Dependency/Scope.cs b/src/Util/Dependency/Scope.cs deleted file mode 100644 index 7448103cf..000000000 --- a/src/Util/Dependency/Scope.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using Autofac; - -namespace Util.Dependency { - /// - /// 作用域 - /// - internal class Scope : IScope { - /// - /// autofac作用域 - /// - private readonly ILifetimeScope _scope; - - /// - /// 初始化作用域 - /// - /// autofac作用域 - public Scope( ILifetimeScope scope ) { - _scope = scope; - } - - /// - /// 创建实例 - /// - /// 实例类型 - public T Create() { - return _scope.Resolve(); - } - - /// - /// 创建对象 - /// - /// 对象类型 - public object Create( Type type ) { - return _scope.Resolve( type ); - } - - /// - /// 释放对象 - /// - public void Dispose() { - _scope.Dispose(); - } - } -} diff --git a/src/Util/Domains/AggregateRoot.cs b/src/Util/Domains/AggregateRoot.cs deleted file mode 100644 index 4484eb552..000000000 --- a/src/Util/Domains/AggregateRoot.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using Util.Events; -using Util.Helpers; - -namespace Util.Domains { - /// - /// 聚合根 - /// - /// 实体类型 - /// 标识类型 - public abstract class AggregateRoot : EntityBase, IAggregateRoot where TEntity : IAggregateRoot { - /// - /// 初始化聚合根 - /// - /// 标识 - protected AggregateRoot( TKey id ) - : base( id ) { - } - - /// - /// 版本号(乐观锁) - /// - public byte[] Version { get; set; } - } - - /// - /// 聚合根 - /// - /// 实体类型 - public abstract class AggregateRoot : AggregateRoot where TEntity : IAggregateRoot { - /// - /// 初始化聚合根 - /// - /// 标识 - protected AggregateRoot( Guid id ) - : base( id ) { - } - } -} - - diff --git a/src/Util/Domains/Auditing/CreationAuditedInitializer.cs b/src/Util/Domains/Auditing/CreationAuditedInitializer.cs deleted file mode 100644 index 5c0b88a3e..000000000 --- a/src/Util/Domains/Auditing/CreationAuditedInitializer.cs +++ /dev/null @@ -1,134 +0,0 @@ -using System; -using Util.Domains.Sessions; - -namespace Util.Domains.Auditing { - /// - /// 创建操作审计初始化器 - /// - public class CreationAuditedInitializer { - /// - /// 初始化创建操作审计初始化器 - /// - /// 实体 - /// 用户上下文 - private CreationAuditedInitializer( object entity, ISession session ) { - _entity = entity; - _session = session; - } - - /// - /// 实体 - /// - private readonly object _entity; - /// - /// 用户会话 - /// - private readonly ISession _session; - - /// - /// 初始化 - /// - /// 实体 - /// 用户上下文 - public static void Init( object entity, ISession session ) { - new CreationAuditedInitializer( entity, session ).Init(); - } - - /// - /// 初始化 - /// - public void Init() { - if( _entity is ICreationAudited) { - InitGuid(); - return; - } - if ( _entity is ICreationAudited ) { - InitNullableGuid(); - return; - } - if ( _entity is ICreationAudited ) { - InitInt(); - return; - } - if ( _entity is ICreationAudited ) { - InitNullableInt(); - return; - } - if ( _entity is ICreationAudited ) { - InitString(); - return; - } - if ( _entity is ICreationAudited ) { - InitLong(); - return; - } - if ( _entity is ICreationAudited ) { - InitNullableLong(); - return; - } - } - - /// - /// 初始化Guid - /// - private void InitGuid() { - var result = (ICreationAudited)_entity; - result.CreationTime = DateTime.Now; - result.CreatorId = _session.UserId.ToGuid(); - } - - /// - /// 初始化可空Guid - /// - private void InitNullableGuid() { - var result = (ICreationAudited)_entity; - result.CreationTime = DateTime.Now; - result.CreatorId = _session.UserId.ToGuidOrNull(); - } - - /// - /// 初始化int - /// - private void InitInt() { - var result = (ICreationAudited)_entity; - result.CreationTime = DateTime.Now; - result.CreatorId = _session.UserId.ToInt(); - } - - /// - /// 初始化可空int - /// - private void InitNullableInt() { - var result = (ICreationAudited)_entity; - result.CreationTime = DateTime.Now; - result.CreatorId = _session.UserId.ToIntOrNull(); - } - - /// - /// 初始化字符串 - /// - private void InitString() { - var result = (ICreationAudited)_entity; - result.CreationTime = DateTime.Now; - result.CreatorId = _session.UserId.SafeString(); - } - - /// - /// 初始化Long - /// - private void InitLong() { - var result = (ICreationAudited)_entity; - result.CreationTime = DateTime.Now; - result.CreatorId = _session.UserId.ToLong(); - } - - /// - /// 初始化可空Long - /// - private void InitNullableLong() { - var result = (ICreationAudited)_entity; - result.CreationTime = DateTime.Now; - result.CreatorId = _session.UserId.ToLongOrNull(); - } - } -} diff --git a/src/Util/Domains/Auditing/IAudited.cs b/src/Util/Domains/Auditing/IAudited.cs deleted file mode 100644 index a6526466b..000000000 --- a/src/Util/Domains/Auditing/IAudited.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Util.Domains.Auditing { - /// - /// 操作审计 - /// - public interface IAudited : ICreationAudited, IModificationAudited { - } - - /// - /// 操作审计 - /// - /// 操作人编号类型 - public interface IAudited : ICreationAudited, IModificationAudited { - } -} diff --git a/src/Util/Domains/Auditing/ICreationAudited.cs b/src/Util/Domains/Auditing/ICreationAudited.cs deleted file mode 100644 index 1bfce3472..000000000 --- a/src/Util/Domains/Auditing/ICreationAudited.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace Util.Domains.Auditing { - /// - /// 创建操作审计 - /// - public interface ICreationAudited : ICreationAudited { - } - - /// - /// 创建操作审计 - /// - /// 创建人编号类型 - public interface ICreationAudited { - /// - /// 创建时间 - /// - DateTime? CreationTime { get; set; } - /// - /// 创建人编号 - /// - TKey CreatorId { get; set; } - } -} diff --git a/src/Util/Domains/Auditing/IModificationAudited.cs b/src/Util/Domains/Auditing/IModificationAudited.cs deleted file mode 100644 index 50e9ffe93..000000000 --- a/src/Util/Domains/Auditing/IModificationAudited.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace Util.Domains.Auditing { - /// - /// 修改操作审计 - /// - public interface IModificationAudited : IModificationAudited { - } - - /// - /// 修改操作审计 - /// - /// 最后修改人编号类型 - public interface IModificationAudited { - /// - /// 最后修改时间 - /// - DateTime? LastModificationTime { get; set; } - /// - /// 最后修改人编号 - /// - TKey LastModifierId { get; set; } - } -} diff --git a/src/Util/Domains/Auditing/ModificationAuditedInitializer.cs b/src/Util/Domains/Auditing/ModificationAuditedInitializer.cs deleted file mode 100644 index 1dbe3d21b..000000000 --- a/src/Util/Domains/Auditing/ModificationAuditedInitializer.cs +++ /dev/null @@ -1,134 +0,0 @@ -using System; -using Util.Domains.Sessions; - -namespace Util.Domains.Auditing { - /// - /// 修改操作审计初始化器 - /// - public class ModificationAuditedInitializer { - /// - /// 初始化修改操作审计初始化器 - /// - /// 实体 - /// 用户上下文 - private ModificationAuditedInitializer( object entity, ISession session ) { - _entity = entity; - _session = session; - } - - /// - /// 实体 - /// - private readonly object _entity; - /// - /// 用户会话 - /// - private readonly ISession _session; - - /// - /// 初始化 - /// - /// 实体 - /// 用户上下文 - public static void Init( object entity, ISession session ) { - new ModificationAuditedInitializer( entity, session ).Init(); - } - - /// - /// 初始化 - /// - public void Init() { - if( _entity is IModificationAudited) { - InitGuid(); - return; - } - if ( _entity is IModificationAudited ) { - InitNullableGuid(); - return; - } - if ( _entity is IModificationAudited ) { - InitInt(); - return; - } - if ( _entity is IModificationAudited ) { - InitNullableInt(); - return; - } - if ( _entity is IModificationAudited ) { - InitString(); - return; - } - if ( _entity is IModificationAudited ) { - InitLong(); - return; - } - if ( _entity is IModificationAudited ) { - InitNullableLong(); - return; - } - } - - /// - /// 初始化Guid - /// - private void InitGuid() { - var result = (IModificationAudited)_entity; - result.LastModificationTime = DateTime.Now; - result.LastModifierId = _session.UserId.ToGuid(); - } - - /// - /// 初始化可空Guid - /// - private void InitNullableGuid() { - var result = (IModificationAudited)_entity; - result.LastModificationTime = DateTime.Now; - result.LastModifierId = _session.UserId.ToGuidOrNull(); - } - - /// - /// 初始化int - /// - private void InitInt() { - var result = (IModificationAudited)_entity; - result.LastModificationTime = DateTime.Now; - result.LastModifierId = _session.UserId.ToInt(); - } - - /// - /// 初始化可空int - /// - private void InitNullableInt() { - var result = (IModificationAudited)_entity; - result.LastModificationTime = DateTime.Now; - result.LastModifierId = _session.UserId.ToIntOrNull(); - } - - /// - /// 初始化字符串 - /// - private void InitString() { - var result = (IModificationAudited)_entity; - result.LastModificationTime = DateTime.Now; - result.LastModifierId = _session.UserId.SafeString(); - } - - /// - /// 初始化Long - /// - private void InitLong() { - var result = (IModificationAudited)_entity; - result.LastModificationTime = DateTime.Now; - result.LastModifierId = _session.UserId.ToLong(); - } - - /// - /// 初始化可空Long - /// - private void InitNullableLong() { - var result = (IModificationAudited)_entity; - result.LastModificationTime = DateTime.Now; - result.LastModifierId = _session.UserId.ToLongOrNull(); - } - } -} diff --git a/src/Util/Domains/ChangeValue.cs b/src/Util/Domains/ChangeValue.cs deleted file mode 100644 index bbab9b954..000000000 --- a/src/Util/Domains/ChangeValue.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Text; - -namespace Util.Domains { - /// - /// 变更值 - /// - public class ChangeValue { - /// - /// 初始化变更值 - /// - /// 属性名 - /// 描述 - /// 旧值 - /// 新值 - public ChangeValue( string propertyName, string description, string oldValue, string newValue ) { - PropertyName = propertyName; - Description = description; - OldValue = oldValue; - NewValue = newValue; - } - - /// - /// 属性名 - /// - public string PropertyName { get; } - /// - /// 描述 - /// - public string Description { get; } - /// - /// 旧值 - /// - public string OldValue { get; } - /// - /// 新值 - /// - public string NewValue { get; } - - /// - /// 输出变更信息 - /// - public override string ToString() { - var result = new StringBuilder(); - result.AppendFormat( "{0}({1}),", PropertyName, Description ); - result.AppendFormat( "旧值:{0},新值:{1}", OldValue, NewValue ); - return result.ToString(); - } - } -} diff --git a/src/Util/Domains/ChangeValueCollection.cs b/src/Util/Domains/ChangeValueCollection.cs deleted file mode 100644 index 46c137d0c..000000000 --- a/src/Util/Domains/ChangeValueCollection.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Util.Domains { - /// - /// 变更值集合 - /// - public class ChangeValueCollection : List { - /// - /// 添加 - /// - /// 属性名 - /// 描述 - /// 旧值 - /// 新值 - public void Add( string propertyName, string description, string oldValue, string newValue ) { - if( string.IsNullOrWhiteSpace( propertyName ) ) - return; - Add( new ChangeValue( propertyName, description, oldValue, newValue ) ); - } - - /// - /// 输出变更信息 - /// - public override string ToString() { - var result = new StringBuilder(); - foreach( var item in this ) - result.AppendLine( item.ToString() ); - return result.ToString(); - } - } -} diff --git a/src/Util/Domains/DomainBase.cs b/src/Util/Domains/DomainBase.cs deleted file mode 100644 index 0aae916ea..000000000 --- a/src/Util/Domains/DomainBase.cs +++ /dev/null @@ -1,258 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Text; -using Util.Validations; - -namespace Util.Domains { - /// - /// 领域层顶级基类 - /// - public abstract class DomainBase : IDomainObject, ICompareChange where T : IDomainObject { - - #region 字段 - - /// - /// 描述 - /// - private StringBuilder _description; - /// - /// 验证规则集合 - /// - private readonly List _rules; - /// - /// 验证处理器 - /// - private IValidationHandler _handler; - /// - /// 变更值集合 - /// - private ChangeValueCollection _changeValues; - - #endregion - - #region 构造方法 - - /// - /// 初始化领域层顶级基类 - /// - protected DomainBase() { - _rules = new List(); - _handler = new ThrowHandler(); - } - - #endregion - - #region SetValidationHandler(设置验证处理器) - - /// - /// 设置验证处理器 - /// - /// 验证处理器 - public void SetValidationHandler( IValidationHandler handler ) { - if( handler == null ) - return; - _handler = handler; - } - - #endregion - - #region AddValidationRules(添加验证规则列表) - - /// - /// 添加验证规则列表 - /// - /// 验证规则列表 - public void AddValidationRules( IEnumerable rules ) { - if( rules == null ) - return; - foreach( var rule in rules ) - AddValidationRule( rule ); - } - - #endregion - - #region AddValidationRule(添加验证规则) - - /// - /// 添加验证规则 - /// - /// 验证规则 - public void AddValidationRule( IValidationRule rule ) { - if( rule == null ) - return; - _rules.Add( rule ); - } - - #endregion - - #region Validate(验证) - - /// - /// 验证 - /// - public virtual ValidationResultCollection Validate() { - var result = GetValidationResults(); - HandleValidationResults( result ); - return result; - } - - /// - /// 获取验证结果 - /// - private ValidationResultCollection GetValidationResults() { - var result = DataAnnotationValidation.Validate( this ); - Validate( result ); - foreach( var rule in _rules ) - result.Add( rule.Validate() ); - return result; - } - - /// - /// 验证并添加到验证结果集合 - /// - /// 验证结果集合 - protected virtual void Validate( ValidationResultCollection results ) { - } - - /// - /// 处理验证结果 - /// - private void HandleValidationResults( ValidationResultCollection results ) { - if( results.IsValid ) - return; - _handler.Handle( results ); - } - - #endregion - - #region GetChanges(获取变更属性) - - /// - /// 获取变更属性 - /// - /// 新对象 - public ChangeValueCollection GetChanges( T newEntity ) { - _changeValues = new ChangeValueCollection(); - if( Equals( newEntity, null ) ) - return _changeValues; - AddChanges( newEntity ); - return _changeValues; - } - - /// - /// 添加变更列表 - /// - /// 新对象 - protected virtual void AddChanges( T newEntity ) { - } - - /// - /// 添加变更 - /// - /// 属性表达式,范例:t => t.Name - /// 新值,范例:newEntity.Name - protected void AddChange( Expression> expression, TValue newValue ) { - var name = Util.Helpers.Lambda.GetName( expression ); - var description = Util.Helpers.Reflection.GetDisplayNameOrDescription( (PropertyInfo)Util.Helpers.Lambda.GetMember( expression ) ); - var value = Util.Helpers.Lambda.GetValue( expression ); - AddChange( name, description, Util.Helpers.Convert.To( value ), newValue ); - } - - /// - /// 添加变更 - /// - /// 属性名 - /// 描述 - /// 旧值,范例:this.Name - /// 新值,范例:newEntity.Name - protected void AddChange( string propertyName, string description, TValue oldValue, TValue newValue ) { - if( Equals( oldValue, newValue ) ) - return; - string oldValueString = oldValue.SafeString().ToLower().Trim(); - string newValueString = newValue.SafeString().ToLower().Trim(); - if( oldValueString == newValueString ) - return; - _changeValues.Add( propertyName, description, oldValueString, newValueString ); - } - - /// - /// 添加变更 - /// - /// 旧对象 - /// 新对象 - protected void AddChange( ICompareChange oldObject, TDomainObject newObject ) where TDomainObject : IDomainObject { - if( Equals( oldObject, null ) ) - return; - if( Equals( newObject, null ) ) - return; - _changeValues.AddRange( oldObject.GetChanges( newObject ) ); - } - - /// - /// 添加变更 - /// - /// 旧对象列表 - /// 新对象列表 - protected void AddChange( IEnumerable> oldObjects, IEnumerable newObjects ) where TDomainObject : IDomainObject { - if( Equals( oldObjects, null ) ) - return; - if( Equals( newObjects, null ) ) - return; - var oldList = oldObjects.ToList(); - var newList = newObjects.ToList(); - for( int i = 0; i < oldList.Count; i++ ) { - if( newList.Count <= i ) - return; - AddChange( oldList[i], newList[i] ); - } - } - - #endregion - - #region AddDescriptions(添加描述) - - /// - /// 添加描述 - /// - protected virtual void AddDescriptions() { - } - - /// - /// 添加描述 - /// - protected void AddDescription( string description ) { - if( string.IsNullOrWhiteSpace( description ) ) - return; - _description.Append( description ); - } - - /// - /// 添加描述 - /// - protected void AddDescription( string name, TValue value ) { - if( value == null ) - return; - if( string.IsNullOrWhiteSpace( value.ToString() ) ) - return; - _description.AppendFormat( "{0}:{1},", name, value ); - } - - #endregion - - #region ToString(输出对象状态) - - /// - /// 输出对象状态 - /// - public override string ToString() { - _description = new StringBuilder(); - AddDescriptions(); - return _description.ToString().TrimEnd().TrimEnd( ',' ); - } - - #endregion - } -} diff --git a/src/Util/Domains/EntityBase.cs b/src/Util/Domains/EntityBase.cs deleted file mode 100644 index 2feff6518..000000000 --- a/src/Util/Domains/EntityBase.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using Util.Validations; - -namespace Util.Domains { - /// - /// 领域实体 - /// - /// 实体类型 - public abstract class EntityBase : EntityBase where TEntity : IEntity { - /// - /// 初始化领域实体 - /// - /// 标识 - protected EntityBase( Guid id ) : base( id ){ - } - } - - /// - /// 领域实体 - /// - /// 实体类型 - /// 标识类型 - public abstract class EntityBase : DomainBase, IEntity where TEntity : IEntity { - /// - /// 初始化领域实体 - /// - /// 标识 - protected EntityBase( TKey id ) { - Id = id; - } - - /// - /// 标识 - /// - [Required(ErrorMessage = "Id不能为空")] - [Key] - public TKey Id { get; private set; } - - /// - /// 相等运算 - /// - public override bool Equals( object other ) { - return this == ( other as EntityBase ); - } - - /// - /// 获取哈希 - /// - public override int GetHashCode() { - return ReferenceEquals( Id, null ) ? 0 : Id.GetHashCode(); - } - - /// - /// 相等比较 - /// - public static bool operator ==( EntityBase left, EntityBase right ) { - if( (object)left == null && (object)right == null ) - return true; - if( !( left is TEntity ) || !( right is TEntity ) ) - return false; - if( Equals( left.Id, null ) ) - return false; - if( left.Id.Equals( default( TKey ) ) ) - return false; - return left.Id.Equals( right.Id ); - } - - /// - /// 不相等比较 - /// - public static bool operator !=( EntityBase left, EntityBase right ) { - return !( left == right ); - } - - /// - /// 初始化 - /// - public virtual void Init() { - if( string.IsNullOrWhiteSpace( Id.SafeString() ) || Id.Equals( default( TKey ) ) ) - Id = CreateId(); - } - - /// - /// 创建标识 - /// - protected virtual TKey CreateId() { - return Util.Helpers.Convert.To( Guid.NewGuid() ); - } - - /// - /// 验证 - /// - protected override void Validate( ValidationResultCollection results ) { - if( Id == null || Id.Equals( default( TKey ) ) ) - results.Add( new ValidationResult( "Id不能为空" ) ); - } - } -} diff --git a/src/Util/Domains/IAggregateRoot.cs b/src/Util/Domains/IAggregateRoot.cs deleted file mode 100644 index bf1e69001..000000000 --- a/src/Util/Domains/IAggregateRoot.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Util.Domains { - /// - /// 聚合根 - /// - public interface IAggregateRoot : IEntity, IVersion { - } - - /// - /// 聚合根 - /// - /// 标识类型 - public interface IAggregateRoot : IEntity, IAggregateRoot { - } - - /// - /// 聚合根 - /// - /// 实体类型 - /// 标识类型 - public interface IAggregateRoot : IEntity, IAggregateRoot where TEntity : IAggregateRoot { - } -} diff --git a/src/Util/Domains/ICompareChange.cs b/src/Util/Domains/ICompareChange.cs deleted file mode 100644 index e943eebd0..000000000 --- a/src/Util/Domains/ICompareChange.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Util.Domains { - /// - /// 通过对象比较获取变更属性集 - /// - /// 领域对象类型 - public interface ICompareChange where T : IDomainObject { - /// - /// 获取变更属性 - /// - /// 其它领域对象 - ChangeValueCollection GetChanges( T other ); - } -} diff --git a/src/Util/Domains/IDelete.cs b/src/Util/Domains/IDelete.cs deleted file mode 100644 index 57f4a8ea4..000000000 --- a/src/Util/Domains/IDelete.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Util.Domains { - /// - /// 逻辑删除 - /// - public interface IDelete { - /// - /// 是否删除 - /// - bool IsDeleted { get; set; } - } -} diff --git a/src/Util/Domains/IDomainObject.cs b/src/Util/Domains/IDomainObject.cs deleted file mode 100644 index 20e01267e..000000000 --- a/src/Util/Domains/IDomainObject.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Util.Validations; - -namespace Util.Domains { - /// - /// 领域对象 - /// - public interface IDomainObject : IValidation { - } -} diff --git a/src/Util/Domains/IEntity.cs b/src/Util/Domains/IEntity.cs deleted file mode 100644 index ae60489d9..000000000 --- a/src/Util/Domains/IEntity.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Util.Domains { - /// - /// 实体 - /// - public interface IEntity : IDomainObject { - /// - /// 初始化 - /// - void Init(); - } - - /// - /// 实体 - /// - /// 标识类型 - public interface IEntity : IKey, IEntity { - } - - /// - /// 实体 - /// - /// 实体类型 - /// 标识类型 - public interface IEntity : ICompareChange, IEntity where TEntity : IEntity { - } -} diff --git a/src/Util/Domains/IKey.cs b/src/Util/Domains/IKey.cs deleted file mode 100644 index f751be979..000000000 --- a/src/Util/Domains/IKey.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Util.Domains { - /// - /// 标识 - /// - /// 标识类型 - public interface IKey { - /// - /// 标识 - /// - TKey Id { get; } - } -} diff --git a/src/Util/Domains/IVersion.cs b/src/Util/Domains/IVersion.cs deleted file mode 100644 index c66d447bc..000000000 --- a/src/Util/Domains/IVersion.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Util.Domains { - /// - /// 乐观锁 - /// - public interface IVersion { - /// - /// 版本号 - /// - byte[] Version { get; set; } - } -} diff --git a/src/Util/Domains/Repositories/ICompactRepository.cs b/src/Util/Domains/Repositories/ICompactRepository.cs deleted file mode 100644 index 9c9e0e8be..000000000 --- a/src/Util/Domains/Repositories/ICompactRepository.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Util.Validations.Aspects; - -namespace Util.Domains.Repositories { - /// - /// 精简仓储 - 配合Po使用 - /// - /// 实体类型 - public interface ICompactRepository : ICompactRepository - where TEntity : class, IAggregateRoot { - } - - /// - /// 精简仓储 - 配合Po使用 - /// - /// 实体类型 - /// 实体标识类型 - public interface ICompactRepository where TEntity : class, IAggregateRoot { - /// - /// 查找实体 - /// - /// 实体标识 - TEntity Find( object id ); - /// - /// 查找实体 - /// - /// 实体标识 - Task FindAsync( object id ); - /// - /// 查找实体列表 - /// - /// 实体标识列表 - List FindByIds( params TKey[] ids ); - /// - /// 查找实体列表 - /// - /// 实体标识列表 - List FindByIds( IEnumerable ids ); - /// - /// 查找实体集合 - /// - /// 实体标识集合 - Task> FindByIdsAsync( params TKey[] ids ); - /// - /// 查找实体集合 - /// - /// 实体标识集合 - Task> FindByIdsAsync( IEnumerable ids ); - /// - /// 判断实体是否存在 - /// - /// 实体标识集合 - bool Exists( params TKey[] ids ); - /// - /// 判断实体是否存在 - /// - /// 实体标识集合 - Task ExistsAsync( params TKey[] ids ); - /// - /// 添加实体 - /// - /// 实体 - void Add( [Valid] TEntity entity ); - /// - /// 添加实体集合 - /// - /// 实体集合 - void Add( [Valid] IEnumerable entities ); - /// - /// 添加实体 - /// - /// 实体 - Task AddAsync( [Valid] TEntity entity ); - /// - /// 添加实体集合 - /// - /// 实体集合 - Task AddAsync( [Valid] IEnumerable entities ); - /// - /// 修改实体 - /// - /// 实体 - void Update( [Valid] TEntity entity ); - /// - /// 修改实体 - /// - /// 实体 - Task UpdateAsync( [Valid] TEntity entity ); - /// - /// 移除实体 - /// - /// 实体标识 - void Remove( TKey id ); - /// - /// 移除实体 - /// - /// 实体标识 - Task RemoveAsync( TKey id ); - /// - /// 移除实体 - /// - /// 实体 - void Remove( TEntity entity ); - /// - /// 移除实体 - /// - /// 实体 - Task RemoveAsync( TEntity entity ); - /// - /// 移除实体集合 - /// - /// 实体编号集合 - void Remove( IEnumerable ids ); - /// - /// 移除实体集合 - /// - /// 实体编号集合 - Task RemoveAsync( IEnumerable ids ); - /// - /// 移除实体集合 - /// - /// 实体集合 - void Remove( IEnumerable entities ); - /// - /// 移除实体集合 - /// - /// 实体集合 - Task RemoveAsync( IEnumerable entities ); - } -} diff --git a/src/Util/Domains/Repositories/ICriteria.cs b/src/Util/Domains/Repositories/ICriteria.cs deleted file mode 100644 index 8b62ea400..000000000 --- a/src/Util/Domains/Repositories/ICriteria.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Linq.Expressions; - -namespace Util.Domains.Repositories { - /// - /// 查询条件 - /// - /// 实体类型 - public interface ICriteria { - /// - /// 获取查询条件 - /// - Expression> GetPredicate(); - } -} diff --git a/src/Util/Domains/Repositories/IPager.cs b/src/Util/Domains/Repositories/IPager.cs deleted file mode 100644 index 4d2e89136..000000000 --- a/src/Util/Domains/Repositories/IPager.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Util.Domains.Repositories { - /// - /// 分页 - /// - public interface IPager : IPagerBase { - /// - /// 获取总页数 - /// - int GetPageCount(); - /// - /// 获取跳过的行数 - /// - int GetSkipCount(); - /// - /// 排序条件 - /// - string Order { get; set; } - /// - /// 获取起始行数 - /// - int GetStartNumber(); - /// - /// 获取结束行数 - /// - int GetEndNumber(); - } -} diff --git a/src/Util/Domains/Repositories/IPagerBase.cs b/src/Util/Domains/Repositories/IPagerBase.cs deleted file mode 100644 index 1262cfc80..000000000 --- a/src/Util/Domains/Repositories/IPagerBase.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Util.Domains.Repositories { - /// - /// 分页 - /// - public interface IPagerBase { - /// - /// 页数,即第几页,从1开始 - /// - int Page { get; set; } - /// - /// 每页显示行数 - /// - int PageSize { get; set; } - /// - /// 总行数 - /// - int TotalCount { get; set; } - } -} diff --git a/src/Util/Domains/Repositories/IQueryBase.cs b/src/Util/Domains/Repositories/IQueryBase.cs deleted file mode 100644 index c61228eca..000000000 --- a/src/Util/Domains/Repositories/IQueryBase.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Util.Domains.Repositories { - /// - /// 查询对象 - /// - /// 实体类型 - public interface IQueryBase : ICriteria where TEntity : class { - /// - /// 获取排序条件 - /// - string GetOrder(); - /// - /// 获取分页参数 - /// - IPager GetPager(); - } -} diff --git a/src/Util/Domains/Repositories/IReadableRepository.cs b/src/Util/Domains/Repositories/IReadableRepository.cs deleted file mode 100644 index 8de770f70..000000000 --- a/src/Util/Domains/Repositories/IReadableRepository.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using Util.Dependency; - -namespace Util.Domains.Repositories { - /// - /// 仓储 - 可读 - /// - /// 实体类型 - public interface IReadableRepository : IReadableRepository where TEntity : class, IAggregateRoot { - } - - /// - /// 仓储 - 可读 - /// - /// 实体类型 - /// 实体标识类型 - public interface IReadableRepository : IScopeDependency where TEntity : class, IAggregateRoot { - /// - /// 获取未跟踪的实体集 - /// - IQueryable FindAsNoTracking(); - /// - /// 查找实体集合 - /// - IQueryable Find(); - /// - /// 查找实体集合 - /// - /// 查询条件对象 - IQueryable Find( ICriteria criteria ); - /// - /// 查找实体集合 - /// - /// 查询条件 - IQueryable Find( Expression> predicate ); - /// - /// 查找实体 - /// - /// 实体标识 - TEntity Find( object id ); - /// - /// 查找实体 - /// - /// 实体标识 - Task FindAsync( object id ); - /// - /// 查找实体集合 - /// - /// 实体标识集合 - List FindByIds( params TKey[] ids ); - /// - /// 查找实体集合 - /// - /// 实体标识集合 - List FindByIds( IEnumerable ids ); - /// - /// 查找实体集合 - /// - /// 逗号分隔的Id列表,范例:"1,2" - List FindByIds( string ids ); - /// - /// 查找实体集合 - /// - /// 实体标识集合 - Task> FindByIdsAsync( params TKey[] ids ); - /// - /// 查找实体集合 - /// - /// 实体标识集合 - Task> FindByIdsAsync( IEnumerable ids ); - /// - /// 查找实体集合 - /// - /// 逗号分隔的Id列表,范例:"1,2" - Task> FindByIdsAsync( string ids ); - /// - /// 获取单个实体 - /// - /// 查询条件 - TEntity Single( Expression> predicate ); - /// - /// 获取单个实体 - /// - /// 查询条件 - Task SingleAsync( Expression> predicate ); - /// - /// 查找实体集合 - /// - /// 查询条件 - List FindAll( Expression> predicate = null ); - /// - /// 查找实体集合 - /// - /// 查询条件 - Task> FindAllAsync( Expression> predicate = null ); - /// - /// 判断实体是否存在 - /// - /// 查询条件 - bool Exists( Expression> predicate ); - /// - /// 判断实体是否存在 - /// - /// 实体标识集合 - bool Exists( params TKey[] ids ); - /// - /// 判断实体是否存在 - /// - /// 查询条件 - Task ExistsAsync( Expression> predicate ); - /// - /// 判断实体是否存在 - /// - /// 实体标识集合 - Task ExistsAsync( params TKey[] ids ); - /// - /// 获取实体个数 - /// - /// 查询条件 - int Count( Expression> predicate = null ); - /// - /// 获取实体个数 - /// - /// 查询条件 - Task CountAsync( Expression> predicate = null ); - /// - /// 查询 - /// - /// 查询对象 - List Query( IQueryBase query ); - /// - /// 查询 - /// - /// 查询对象 - Task> QueryAsync( IQueryBase query ); - /// - /// 查询 - 返回未跟踪的实体 - /// - /// 查询对象 - List QueryAsNoTracking( IQueryBase query ); - /// - /// 查询 - 返回未跟踪的实体 - /// - /// 查询对象 - Task> QueryAsNoTrackingAsync( IQueryBase query ); - /// - /// 分页查询 - /// - /// 查询对象 - PagerList PagerQuery( IQueryBase query ); - /// - /// 分页查询 - /// - /// 查询对象 - Task> PagerQueryAsync( IQueryBase query ); - /// - /// 分页查询 - /// - /// 查询对象 - PagerList PagerQueryAsNoTracking( IQueryBase query ); - /// - /// 分页查询 - /// - /// 查询对象 - Task> PagerQueryAsNoTrackingAsync( IQueryBase query ); - } -} diff --git a/src/Util/Domains/Repositories/IRepository.cs b/src/Util/Domains/Repositories/IRepository.cs deleted file mode 100644 index 56ef2d511..000000000 --- a/src/Util/Domains/Repositories/IRepository.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Util.Validations.Aspects; - -namespace Util.Domains.Repositories { - /// - /// 仓储 - /// - /// 实体类型 - public interface IRepository : IRepository, IReadableRepository where TEntity : class, IAggregateRoot { - } - - /// - /// 仓储 - /// - /// 实体类型 - /// 实体标识类型 - public interface IRepository : IReadableRepository where TEntity : class, IAggregateRoot { - /// - /// 添加实体 - /// - /// 实体 - void Add( [Valid] TEntity entity ); - /// - /// 添加实体集合 - /// - /// 实体集合 - void Add( [Valid] IEnumerable entities ); - /// - /// 添加实体 - /// - /// 实体 - Task AddAsync( [Valid] TEntity entity ); - /// - /// 添加实体集合 - /// - /// 实体集合 - Task AddAsync( [Valid] IEnumerable entities ); - /// - /// 修改实体 - /// - /// 实体 - void Update( [Valid] TEntity entity ); - /// - /// 修改实体 - /// - /// 实体 - Task UpdateAsync( [Valid] TEntity entity ); - /// - /// 移除实体 - /// - /// 实体标识 - void Remove( TKey id ); - /// - /// 移除实体 - /// - /// 实体标识 - Task RemoveAsync( TKey id ); - /// - /// 移除实体 - /// - /// 实体 - void Remove( TEntity entity ); - /// - /// 移除实体 - /// - /// 实体 - Task RemoveAsync( TEntity entity ); - /// - /// 移除实体集合 - /// - /// 实体编号集合 - void Remove( IEnumerable ids ); - /// - /// 移除实体集合 - /// - /// 实体编号集合 - Task RemoveAsync( IEnumerable ids ); - /// - /// 移除实体集合 - /// - /// 实体集合 - void Remove( IEnumerable entities ); - /// - /// 移除实体集合 - /// - /// 实体集合 - Task RemoveAsync( IEnumerable entities ); - } -} diff --git a/src/Util/Domains/Repositories/Pager.cs b/src/Util/Domains/Repositories/Pager.cs deleted file mode 100644 index 278747b1a..000000000 --- a/src/Util/Domains/Repositories/Pager.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System.Text; - -namespace Util.Domains.Repositories { - /// - /// 分页参数 - /// - public class Pager : IPager { - /// - /// 初始化分页参数 - /// - public Pager() - : this( 1 ) { - } - - /// - /// 初始化分页参数 - /// - /// 页索引 - /// 每页显示行数,默认20 - /// 排序条件 - public Pager( int page, int pageSize, string order ) - : this( page, pageSize, 0, order ) { - } - - /// - /// 初始化分页参数 - /// - /// 页索引 - /// 每页显示行数,默认20 - /// 总行数 - /// 排序条件 - public Pager( int page, int pageSize = 20, int totalCount = 0, string order = "" ) { - Page = page; - PageSize = pageSize; - TotalCount = totalCount; - Order = order; - } - - private int _pageIndex; - /// - /// 页索引,即第几页,从1开始 - /// - public int Page { - get { - if( _pageIndex <= 0 ) - _pageIndex = 1; - return _pageIndex; - } - set => _pageIndex = value; - } - - /// - /// 每页显示行数 - /// - public int PageSize { get; set; } - - /// - /// 总行数 - /// - public int TotalCount { get; set; } - - /// - /// 获取总页数 - /// - public int GetPageCount() { - if ( ( TotalCount % PageSize ) == 0 ) - return TotalCount / PageSize; - return ( TotalCount / PageSize ) + 1; - } - - /// - /// 获取跳过的行数 - /// - public int GetSkipCount() { - if ( Page > GetPageCount() ) - Page = GetPageCount(); - return PageSize * ( Page - 1 ); - } - - /// - /// 排序条件 - /// - public string Order { get; set; } - - /// - /// 起始行数 - /// - public int GetStartNumber() { - return ( Page - 1 ) * PageSize + 1; - } - /// - /// 结束行数 - /// - public int GetEndNumber() { - return Page * PageSize; - } - - /// - /// 描述 - /// - private StringBuilder _description; - - /// - /// 输出对象状态 - /// - public override string ToString() { - _description = new StringBuilder(); - AddDescriptions(); - return _description.ToString().TrimEnd().TrimEnd( ',' ); - } - - /// - /// 添加描述 - /// - protected virtual void AddDescriptions() { - AddDescription( "Page", Page ); - AddDescription( "PageSize", PageSize ); - AddDescription( "Order", Order ); - } - - /// - /// 添加描述 - /// - protected void AddDescription( string description ) { - if( string.IsNullOrWhiteSpace( description ) ) - return; - _description.Append( description ); - } - - /// - /// 添加描述 - /// - protected void AddDescription( string name, TValue value ) { - if( string.IsNullOrWhiteSpace( value.SafeString() ) ) - return; - _description.AppendFormat( "{0}:{1},", name, value ); - } - } -} diff --git a/src/Util/Domains/Repositories/PagerList.cs b/src/Util/Domains/Repositories/PagerList.cs deleted file mode 100644 index f1e43846e..000000000 --- a/src/Util/Domains/Repositories/PagerList.cs +++ /dev/null @@ -1,134 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Util.Domains.Repositories { - /// - /// 分页集合 - /// - /// 元素类型 - [Serializable] - public class PagerList : IPagerBase { - /// - /// 初始化分页集合 - /// - public PagerList() : this( 0 ) { - } - - /// - /// 初始化分页集合 - /// - /// 总行数 - public PagerList( int totalCount ) - : this( 1, 20, totalCount ) { - } - - /// - /// 初始化分页集合 - /// - /// 页索引 - /// 每页显示行数 - /// 总行数 - public PagerList( int page, int pageSize, int totalCount ) - : this( page, pageSize, totalCount, "" ) { - } - - /// - /// 初始化分页集合 - /// - /// 页索引 - /// 每页显示行数 - /// 总行数 - /// 排序条件 - public PagerList( int page, int pageSize, int totalCount, string order ) { - Data = new List(); - var pager = new Pager( page, pageSize, totalCount ); - TotalCount = pager.TotalCount; - PageCount = pager.GetPageCount(); - Page = pager.Page; - PageSize = pager.PageSize; - Order = order; - } - - /// - /// 初始化分页集合 - /// - /// 查询对象 - public PagerList( IPager pager ) - : this( pager.Page, pager.PageSize, pager.TotalCount, pager.Order ) { - } - - /// - /// 页索引,即第几页,从1开始 - /// - public int Page { get; set; } - - /// - /// 每页显示行数 - /// - public int PageSize { get; set; } - - /// - /// 总行数 - /// - public int TotalCount { get; set; } - - /// - /// 总页数 - /// - public int PageCount { get; set; } - - /// - /// 排序条件 - /// - public string Order { get; set; } - - /// - /// 内容 - /// - public List Data { get; } - - /// - /// 索引器 - /// - /// 索引 - public T this[ int index ] { - get => Data[index]; - set => Data[index] = value; - } - - /// - /// 添加元素 - /// - /// 元素 - public void Add( T item ) { - Data.Add( item ); - } - - /// - /// 添加元素集合 - /// - /// 元素集合 - public void AddRange( IEnumerable collection ) { - Data.AddRange( collection ); - } - - /// - /// 清空 - /// - public void Clear() { - Data.Clear(); - } - - /// - /// 转换分页集合的元素类型 - /// - /// 目标元素类型 - /// 转换方法 - public PagerList Convert( Func converter ) { - var result = new PagerList( Page, PageSize, TotalCount, Order ); - result.AddRange( this.Data.Select( converter ) ); - return result; - } - } -} diff --git a/src/Util/Domains/Sessions/ISession.cs b/src/Util/Domains/Sessions/ISession.cs deleted file mode 100644 index 7d619edce..000000000 --- a/src/Util/Domains/Sessions/ISession.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Util.Domains.Sessions { - /// - /// 用户会话 - /// - public interface ISession { - /// - /// 用户编号 - /// - string UserId { get; } - } -} diff --git a/src/Util/Domains/Sessions/NullSession.cs b/src/Util/Domains/Sessions/NullSession.cs deleted file mode 100644 index 980a4d0ac..000000000 --- a/src/Util/Domains/Sessions/NullSession.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Util.Domains.Sessions { - /// - /// 空用户会话 - /// - public class NullSession : ISession { - /// - /// 用户编号 - /// - public string UserId => string.Empty; - } -} diff --git a/src/Util/Domains/Sessions/Session.cs b/src/Util/Domains/Sessions/Session.cs deleted file mode 100644 index 1ba9bb1b9..000000000 --- a/src/Util/Domains/Sessions/Session.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Util.Domains.Sessions { - /// - /// 用户会话 - /// - public class Session : ISession { - /// - /// 初始化用户会话 - /// - /// 用户编号 - public Session( string userId ) { - UserId = userId; - } - - /// - /// 空用户会话 - /// - public static readonly ISession Null = new NullSession(); - - /// - /// 用户编号 - /// - public string UserId { get; } - } -} diff --git a/src/Util/Domains/Tenants/ITenant.cs b/src/Util/Domains/Tenants/ITenant.cs deleted file mode 100644 index 58422d914..000000000 --- a/src/Util/Domains/Tenants/ITenant.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Util.Domains.Tenants { - /// - /// 租户 - /// - public interface ITenant { - /// - /// 租户编号 - /// - string TenantId { get; set; } - } -} diff --git a/src/Util/Domains/Trees/ITreeEntity.cs b/src/Util/Domains/Trees/ITreeEntity.cs deleted file mode 100644 index 7859742d1..000000000 --- a/src/Util/Domains/Trees/ITreeEntity.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Collections.Generic; - -namespace Util.Domains.Trees { - /// - /// 树型实体 - /// - /// 实体类型 - /// 实体标识类型 - /// 父编号类型 - public interface ITreeEntity : IAggregateRoot where TEntity : ITreeEntity { - /// - /// 父对象 - /// - TEntity Parent { get; set; } - /// - /// 父编号 - /// - TParentId ParentId { get; set; } - /// - /// 级数 - /// - int Level { get; } - /// - /// 路径 - /// - string Path { get; } - /// - /// 排序号 - /// - int? SortId { get; set; } - /// - /// 启用 - /// - bool Enabled { get; set; } - /// - /// 初始化路径 - /// - void InitPath(); - /// - /// 初始化路径 - /// - /// 父节点 - void InitPath( TEntity parent ); - /// - /// 从路径中获取所有上级节点编号 - /// - /// 是否排除当前节点,默认排除自身 - List GetParentIdsFromPath( bool excludeSelf = true ); - } -} diff --git a/src/Util/Domains/Trees/TreeEntityBase.cs b/src/Util/Domains/Trees/TreeEntityBase.cs deleted file mode 100644 index 887c56220..000000000 --- a/src/Util/Domains/Trees/TreeEntityBase.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; - -namespace Util.Domains.Trees { - /// - /// 树型实体 - /// - /// 树型实体类型 - /// 标识类型 - /// 父编号类型 - public abstract class TreeEntityBase : AggregateRoot, ITreeEntity where TEntity : ITreeEntity { - /// - /// 父对象 - /// - private TEntity _parent; - - /// - /// 初始化树型实体 - /// - /// 标识 - /// 路径 - /// 级数 - protected TreeEntityBase( TKey id, string path, int level ) - : base( id ) { - Path = path; - Level = level; - } - - /// - /// 父对象 - /// - public virtual TEntity Parent { - get => _parent; - set { - _parent = value; - InitPath(); - } - } - - /// - /// 父编号 - /// - public TParentId ParentId { get; set; } - - /// - /// 路径 - /// - [Required] - public virtual string Path { get;private set; } - - /// - /// 级数 - /// - public int Level { get; private set; } - - /// - /// 启用 - /// - public bool Enabled { get; set; } - - /// - /// 排序号 - /// - public int? SortId { get; set; } - - /// - /// 初始化路径 - /// - public virtual void InitPath() { - InitPath( Parent ); - } - - /// - /// 初始化路径 - /// - /// 父节点 - public void InitPath( TEntity parent ) { - if( Equals( parent, null ) ) { - Level = 1; - Path = $"{Id},"; - return; - } - Level = parent.Level + 1; - Path = $"{parent.Path}{Id},"; - } - - /// - /// 从路径中获取所有上级节点编号 - /// - /// 是否排除当前节点,默认排除自身 - public List GetParentIdsFromPath( bool excludeSelf = true ) { - if( string.IsNullOrWhiteSpace( Path ) ) - return new List(); - var result = Path.Split( ',' ).Where( id => !string.IsNullOrWhiteSpace( id ) && id != "," ).ToList(); - if( excludeSelf ) - result = result.Where( id => id.SafeString().ToLower() != Id.SafeString().ToLower() ).ToList(); - return result.Select( Util.Helpers.Convert.To ).ToList(); - } - } - - /// - /// 树型实体 - /// - /// 树型实体类型 - public abstract class TreeEntityBase : TreeEntityBase where TEntity : ITreeEntity { - /// - /// 初始化树型实体 - /// - /// 标识 - /// 路径 - /// 级数 - protected TreeEntityBase( Guid id, string path, int level ) - : base( id, path, level ) { - } - } -} diff --git a/src/Util/Domains/ValueObjectBase.cs b/src/Util/Domains/ValueObjectBase.cs deleted file mode 100644 index acc96066a..000000000 --- a/src/Util/Domains/ValueObjectBase.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Linq; -using System.Reflection; - -namespace Util.Domains { - /// - /// 值对象 - /// - /// 值对象类型 - public abstract class ValueObjectBase : DomainBase, IEquatable where TValueObject : ValueObjectBase { - /// - /// 相等性比较 - /// - public bool Equals( TValueObject other ) { - return this == other; - } - - /// - /// 相等性比较 - /// - public override bool Equals( object other ) { - return Equals( other as TValueObject ); - } - - /// - /// 相等性比较 - /// - public static bool operator ==( ValueObjectBase left, ValueObjectBase right ) { - if( (object)left == null && (object)right == null ) - return true; - if( !( left is TValueObject ) || !( right is TValueObject ) ) - return false; - var properties = left.GetType().GetTypeInfo().GetProperties(); - return properties.All( property => property.GetValue( left ) == property.GetValue( right ) ); - } - - /// - /// 不相等比较 - /// - public static bool operator !=( ValueObjectBase left, ValueObjectBase right ) { - return !( left == right ); - } - - /// - /// 获取哈希 - /// - public override int GetHashCode() { - var properties = GetType().GetTypeInfo().GetProperties(); - return properties.Select( property => property.GetValue( this ) ) - .Where( value => value != null ) - .Aggregate( 0, ( current, value ) => current ^ value.GetHashCode() ); - } - - /// - /// 克隆副本 - /// - public virtual TValueObject Clone() { - return Util.Helpers.Convert.To( MemberwiseClone() ); - } - } -} diff --git a/src/Util/Events/Event.cs b/src/Util/Events/Event.cs deleted file mode 100644 index 37040a777..000000000 --- a/src/Util/Events/Event.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Text; - -namespace Util.Events { - /// - /// 事件 - /// - public class Event : IEvent { - /// - /// 初始化事件 - /// - public Event() { - Id = Guid.NewGuid().ToString(); - Time = DateTime.Now; - } - - /// - /// 事件标识 - /// - public string Id { get; set; } - - /// - /// 事件时间 - /// - public DateTime Time { get; } - - /// - /// 输出日志 - /// - public override string ToString() { - StringBuilder result = new StringBuilder(); - result.AppendLine( $"事件标识: {Id}" ); - result.AppendLine( $"事件时间: {Time.ToMillisecondString()}" ); - result.Append( $"事件数据:{Util.Helpers.Json.ToJson( this )}" ); - return result.ToString(); - } - } -} diff --git a/src/Util/Events/Handlers/IEventHandler.cs b/src/Util/Events/Handlers/IEventHandler.cs deleted file mode 100644 index 46ca7c0a4..000000000 --- a/src/Util/Events/Handlers/IEventHandler.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Util.Events.Handlers { - /// - /// 事件处理器 - /// - public interface IEventHandler { - } - - /// - /// 事件处理器 - /// - /// 事件类型 - public interface IEventHandler : IEventHandler where TEvent : IEvent { - /// - /// 处理事件 - /// - /// 事件 - void Handle( TEvent @event ) ; - } -} diff --git a/src/Util/Events/Handlers/IEventHandlerManager.cs b/src/Util/Events/Handlers/IEventHandlerManager.cs deleted file mode 100644 index a9b6fb5ed..000000000 --- a/src/Util/Events/Handlers/IEventHandlerManager.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; - -namespace Util.Events.Handlers { - /// - /// 事件处理器服务 - /// - public interface IEventHandlerManager { - /// - /// 获取事件处理器列表 - /// - /// 事件类型 - List> GetHandlers() where TEvent : IEvent; - } -} diff --git a/src/Util/Events/IEvent.cs b/src/Util/Events/IEvent.cs deleted file mode 100644 index cb7c02a0b..000000000 --- a/src/Util/Events/IEvent.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace Util.Events { - /// - /// 事件 - /// - public interface IEvent { - /// - /// 事件标识 - /// - string Id { get; set; } - - /// - /// 事件时间 - /// - DateTime Time { get; } - } -} diff --git a/src/Util/Events/IEventBus.cs b/src/Util/Events/IEventBus.cs deleted file mode 100644 index b77feee32..000000000 --- a/src/Util/Events/IEventBus.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Util.Events { - /// - /// 事件总线 - /// - public interface IEventBus { - /// - /// 发布事件 - /// - /// 事件类型 - /// 事件 - void Publish( TEvent @event ) where TEvent : IEvent; - } -} diff --git a/src/Util/Events/Messages/IMessageEvent.cs b/src/Util/Events/Messages/IMessageEvent.cs deleted file mode 100644 index e6f6d09c5..000000000 --- a/src/Util/Events/Messages/IMessageEvent.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Util.Events.Messages { - /// - /// 消息事件 - /// - public interface IMessageEvent : IEvent { - /// - /// 事件数据 - /// - object Data { get; set; } - - /// - /// 发送目标 - /// - string Target { get; set; } - - /// - /// 回调 - /// - string Callback { get; set; } - } -} diff --git a/src/Util/Events/Messages/IMessageEventBus.cs b/src/Util/Events/Messages/IMessageEventBus.cs deleted file mode 100644 index 2fda9a41a..000000000 --- a/src/Util/Events/Messages/IMessageEventBus.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Util.Events.Messages { - /// - /// 消息事件总线 - /// - public interface IMessageEventBus { - /// - /// 发布事件 - /// - /// 事件类型 - /// 事件 - void Publish( TEvent @event ) where TEvent : IMessageEvent; - } -} diff --git a/src/Util/Events/Messages/MessageEvent.cs b/src/Util/Events/Messages/MessageEvent.cs deleted file mode 100644 index 6087666d2..000000000 --- a/src/Util/Events/Messages/MessageEvent.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Text; - -namespace Util.Events.Messages { - /// - /// 消息事件 - /// - public class MessageEvent : Event, IMessageEvent { - /// - /// 事件数据 - /// - public object Data { get; set; } - - /// - /// 发送目标 - /// - public string Target { get; set; } - - /// - /// 回调 - /// - public string Callback { get; set; } - - /// - /// 输出日志 - /// - public override string ToString() { - StringBuilder result = new StringBuilder(); - result.AppendLine( $"事件标识: {Id}" ); - result.AppendLine( $"事件时间:{Time.ToMillisecondString()}" ); - if( string.IsNullOrWhiteSpace( Target ) == false ) - result.AppendLine( $"发送目标:{Target}" ); - if( string.IsNullOrWhiteSpace( Callback ) == false ) - result.AppendLine( $"回调:{Callback}" ); - result.Append( $"事件数据:{Util.Helpers.Json.ToJson( Data )}" ); - return result.ToString(); - } - } -} diff --git a/src/Util/Exceptions/ConcurrencyException.cs b/src/Util/Exceptions/ConcurrencyException.cs deleted file mode 100644 index dea0bea12..000000000 --- a/src/Util/Exceptions/ConcurrencyException.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using Util.Properties; - -namespace Util.Exceptions { - /// - /// 并发异常 - /// - public class ConcurrencyException : Warning { - /// - /// 初始化并发异常 - /// - public ConcurrencyException() - : this( "" ) { - } - - /// - /// 初始化并发异常 - /// - /// 错误消息 - public ConcurrencyException( string message ) - : this( message, null ) { - } - - /// - /// 初始化并发异常 - /// - /// 异常 - public ConcurrencyException( Exception exception ) - : this( "" , exception ) { - } - - /// - /// 初始化并发异常 - /// - /// 错误消息 - /// 异常 - public ConcurrencyException( string message, Exception exception ) - : this( message, exception, "" ) { - } - - /// - /// 初始化并发异常 - /// - /// 错误消息 - /// 异常 - /// 错误码 - public ConcurrencyException( string message, Exception exception, string code ) - : base( "并发异常:" + LibraryResource.ConcurrencyExceptionMessage + Environment.NewLine + message, code, exception ) { - } - } -} diff --git a/src/Util/Exceptions/Warning.cs b/src/Util/Exceptions/Warning.cs deleted file mode 100644 index 73ae21f9d..000000000 --- a/src/Util/Exceptions/Warning.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System; -using System.Collections; -using System.Text; -using Microsoft.Extensions.Logging; -using Util.Properties; - -namespace Util.Exceptions { - /// - /// 应用程序异常 - /// - public class Warning : Exception { - /// - /// 错误消息 - /// - private readonly string _message; - - /// - /// 初始化应用程序异常 - /// - /// 错误消息 - public Warning( string message ) - : this( message, "" ) { - } - - /// - /// 初始化应用程序异常 - /// - /// 异常 - public Warning( Exception exception ) - : this( "", "", exception ) { - } - - /// - /// 初始化应用程序异常 - /// - /// 错误消息 - /// 错误码 - public Warning( string message, string code ) - : this( message, code, null ) { - } - - /// - /// 初始化应用程序异常 - /// - /// 错误消息 - /// 错误码 - /// 异常 - public Warning( string message, string code, Exception exception ) - : base( message ?? "", exception ) { - Code = code; - _message = GetMessage(); - } - - /// - /// 获取错误消息 - /// - private string GetMessage() { - var result = new StringBuilder(); - AppendSelfMessage( result ); - AppendInnerMessage( result, InnerException ); - return result.ToString().TrimEnd( Environment.NewLine.ToCharArray() ); - } - - /// - /// 添加外层异常消息 - /// - private void AppendSelfMessage( StringBuilder result ) { - if( string.IsNullOrWhiteSpace( base.Message ) ) - return; - result.AppendLine( base.Message ); - } - - /// - /// 添加内部异常消息 - /// - private void AppendInnerMessage( StringBuilder result, Exception exception ) { - if( exception == null ) - return; - if( exception is Warning ) { - result.AppendLine( exception.Message ); - return; - } - result.AppendLine( exception.Message ); - AppendData( result, exception ); - AppendInnerMessage( result, exception.InnerException ); - } - - /// - /// 添加额外数据 - /// - private void AppendData( StringBuilder result, Exception ex ) { - foreach( DictionaryEntry data in ex.Data ) - result.AppendFormat( "{0}:{1}{2}", data.Key, data.Value, Environment.NewLine ); - } - - /// - /// 错误消息 - /// - public override string Message { - get { - if( Data.Count == 0 ) - return _message; - StringBuilder result = new StringBuilder(); - result.AppendLine( _message ); - AppendData( result, this ); - return result.ToString(); - } - } - - /// - /// 错误码 - /// - public string Code { get; set; } - - /// - /// 堆栈跟踪 - /// - public override string StackTrace { - get { - if( !string.IsNullOrWhiteSpace( base.StackTrace ) ) - return base.StackTrace; - if( base.InnerException == null ) - return string.Empty; - return base.InnerException.StackTrace; - } - } - - /// - /// 获取友情提示 - /// - /// 日志级别 - public string GetPrompt( LogLevel level ) { - if( level == LogLevel.Error ) - return R.SystemError; - return Message; - } - } -} diff --git a/src/Util/Expressions/ParameterRebinder.cs b/src/Util/Expressions/ParameterRebinder.cs deleted file mode 100644 index 67b6b99cc..000000000 --- a/src/Util/Expressions/ParameterRebinder.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Collections.Generic; -using System.Linq.Expressions; - -namespace Util.Expressions { - /// - /// 参数重绑定操作 - /// - public class ParameterRebinder : ExpressionVisitor { - /// - /// 参数字典 - /// - private readonly Dictionary _map; - - /// - /// 初始化参数重绑定操作 - /// - /// 参数字典 - public ParameterRebinder( Dictionary map ) { - _map = map ?? new Dictionary(); - } - - /// - /// 替换参数 - /// - /// 参数字典 - /// 表达式 - public static Expression ReplaceParameters( Dictionary map, Expression exp ) { - return new ParameterRebinder( map ).Visit( exp ); - } - - /// - /// 访问参数 - /// - /// 参数 - protected override Expression VisitParameter( ParameterExpression parameterExpression ) { - ParameterExpression replacement; - if( _map.TryGetValue( parameterExpression, out replacement ) ) - parameterExpression = replacement; - return base.VisitParameter( parameterExpression ); - } - } -} diff --git a/src/Util/Expressions/PredicateExpressionBuilder.cs b/src/Util/Expressions/PredicateExpressionBuilder.cs deleted file mode 100644 index ee6a2e601..000000000 --- a/src/Util/Expressions/PredicateExpressionBuilder.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Linq.Expressions; -using Util.Datas.Queries; -using Util.Helpers; - -namespace Util.Expressions { - /// - /// 谓词表达式生成器 - /// - public class PredicateExpressionBuilder { - /// - /// 初始化谓词表达式生成器 - /// - public PredicateExpressionBuilder() { - _parameter = Expression.Parameter( typeof(TEntity), "t" ); - } - - /// - /// 参数 - /// - private readonly ParameterExpression _parameter; - /// - /// 结果表达式 - /// - private Expression _result; - - /// - /// 获取参数 - /// - public ParameterExpression GetParameter() { - return _parameter; - } - - /// - /// 添加表达式 - /// - /// 属性表达式 - /// 运算符 - /// 值 - public void Append( Expression> property, Operator @operator, object value ) { - _result = _result.And( _parameter.Property( Lambda.GetMember( property ) ).Operation( @operator, value ) ); - } - - /// - /// 添加表达式 - /// - /// 属性名 - /// 运算符 - /// 值 - public void Append( string property, Operator @operator, object value ) { - _result = _result.And( _parameter.Property( property ).Operation( @operator, value ) ); - } - - /// - /// 转换为Lambda表达式 - /// - public Expression> ToLambda() { - return _result.ToLambda>( _parameter ); - } - } -} diff --git a/src/Util/Extensions.Common.cs b/src/Util/Extensions.Common.cs deleted file mode 100644 index 8dd2e8849..000000000 --- a/src/Util/Extensions.Common.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Collections.Generic; - -namespace Util { - /// - /// 系统扩展 - 公共 - /// - public static partial class Extensions { - /// - /// 安全获取值,当值为null时,不会抛出异常 - /// - /// 可空值 - public static T SafeValue( this T? value ) where T : struct { - return value ?? default( T ); - } - - /// - /// 获取枚举值 - /// - /// 枚举实例 - public static int Value( this System.Enum instance ) { - return Util.Helpers.Enum.GetValue( instance.GetType(), instance ); - } - - /// - /// 获取枚举值 - /// - /// 返回值类型 - /// 枚举实例 - public static TResult Value( this System.Enum instance ) { - return Util.Helpers.Convert.To( Value( instance ) ); - } - - /// - /// 获取枚举描述,使用System.ComponentModel.Description特性设置描述 - /// - /// 枚举实例 - public static string Description( this System.Enum instance ) { - return Util.Helpers.Enum.GetDescription( instance.GetType(), instance ); - } - - /// - /// 转换为用分隔符连接的字符串 - /// - /// 集合元素类型 - /// 集合 - /// 引号,默认不带引号,范例:单引号 "'" - /// 分隔符,默认使用逗号分隔 - public static string Join( this IEnumerable list, string quotes = "", string separator = "," ) { - return Util.Helpers.String.Join( list, quotes, separator ); - } - } -} diff --git a/src/Util/Extensions.Convert.cs b/src/Util/Extensions.Convert.cs deleted file mode 100644 index b9aeca1db..000000000 --- a/src/Util/Extensions.Convert.cs +++ /dev/null @@ -1,148 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Util { - /// - /// 系统扩展 - 类型转换 - /// - public static partial class Extensions { - /// - /// 安全转换为字符串,去除两端空格,当值为null时返回"" - /// - /// 输入值 - public static string SafeString( this object input ) { - return input == null ? string.Empty : input.ToString().Trim(); - } - - /// - /// 转换为bool - /// - /// 数据 - public static bool ToBool( this string obj ) { - return Util.Helpers.Convert.ToBool( obj ); - } - - /// - /// 转换为可空bool - /// - /// 数据 - public static bool? ToBoolOrNull( this string obj ) { - return Util.Helpers.Convert.ToBoolOrNull( obj ); - } - - /// - /// 转换为int - /// - /// 数据 - public static int ToInt( this string obj ) { - return Util.Helpers.Convert.ToInt( obj ); - } - - /// - /// 转换为可空int - /// - /// 数据 - public static int? ToIntOrNull( this string obj ) { - return Util.Helpers.Convert.ToIntOrNull( obj ); - } - - /// - /// 转换为long - /// - /// 数据 - public static long ToLong( this string obj ) { - return Util.Helpers.Convert.ToLong( obj ); - } - - /// - /// 转换为可空long - /// - /// 数据 - public static long? ToLongOrNull( this string obj ) { - return Util.Helpers.Convert.ToLongOrNull( obj ); - } - - /// - /// 转换为double - /// - /// 数据 - public static double ToDouble( this string obj ) { - return Util.Helpers.Convert.ToDouble( obj ); - } - - /// - /// 转换为可空double - /// - /// 数据 - public static double? ToDoubleOrNull( this string obj ) { - return Util.Helpers.Convert.ToDoubleOrNull( obj ); - } - - /// - /// 转换为decimal - /// - /// 数据 - public static decimal ToDecimal( this string obj ) { - return Util.Helpers.Convert.ToDecimal( obj ); - } - - /// - /// 转换为可空decimal - /// - /// 数据 - public static decimal? ToDecimalOrNull( this string obj ) { - return Util.Helpers.Convert.ToDecimalOrNull( obj ); - } - - /// - /// 转换为日期 - /// - /// 数据 - public static DateTime ToDate( this string obj ) { - return Util.Helpers.Convert.ToDate( obj ); - } - - /// - /// 转换为可空日期 - /// - /// 数据 - public static DateTime? ToDateOrNull( this string obj ) { - return Util.Helpers.Convert.ToDateOrNull( obj ); - } - - /// - /// 转换为Guid - /// - /// 数据 - public static Guid ToGuid( this string obj ) { - return Util.Helpers.Convert.ToGuid( obj ); - } - - /// - /// 转换为可空Guid - /// - /// 数据 - public static Guid? ToGuidOrNull( this string obj ) { - return Util.Helpers.Convert.ToGuidOrNull( obj ); - } - - /// - /// 转换为Guid集合 - /// - /// 数据,范例: "83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A" - public static List ToGuidList( this string obj ) { - return Util.Helpers.Convert.ToGuidList( obj ); - } - - /// - /// 转换为Guid集合 - /// - /// 字符串集合 - public static List ToGuidList( this IList obj ) { - if( obj == null ) - return new List(); - return obj.Select( t => t.ToGuid() ).ToList(); - } - } -} diff --git a/src/Util/Extensions.DateTime.cs b/src/Util/Extensions.DateTime.cs deleted file mode 100644 index d8113f2c0..000000000 --- a/src/Util/Extensions.DateTime.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System; -using System.Text; - -namespace Util { - /// - /// 系统扩展 - 日期 - /// - public static partial class Extensions { - /// - /// 获取格式化字符串,带时分秒,格式:"yyyy-MM-dd HH:mm:ss" - /// - /// 日期 - /// 是否移除秒 - public static string ToDateTimeString( this DateTime dateTime, bool removeSecond = false ) { - if( removeSecond ) - return dateTime.ToString( "yyyy-MM-dd HH:mm" ); - return dateTime.ToString( "yyyy-MM-dd HH:mm:ss" ); - } - - /// - /// 获取格式化字符串,带时分秒,格式:"yyyy-MM-dd HH:mm:ss" - /// - /// 日期 - /// 是否移除秒 - public static string ToDateTimeString( this DateTime? dateTime, bool removeSecond = false ) { - if( dateTime == null ) - return string.Empty; - return ToDateTimeString( dateTime.Value, removeSecond ); - } - - /// - /// 获取格式化字符串,不带时分秒,格式:"yyyy-MM-dd" - /// - /// 日期 - public static string ToDateString( this DateTime dateTime ) { - return dateTime.ToString( "yyyy-MM-dd" ); - } - - /// - /// 获取格式化字符串,不带时分秒,格式:"yyyy-MM-dd" - /// - /// 日期 - public static string ToDateString( this DateTime? dateTime ) { - if( dateTime == null ) - return string.Empty; - return ToDateString( dateTime.Value ); - } - - /// - /// 获取格式化字符串,不带年月日,格式:"HH:mm:ss" - /// - /// 日期 - public static string ToTimeString( this DateTime dateTime ) { - return dateTime.ToString( "HH:mm:ss" ); - } - - /// - /// 获取格式化字符串,不带年月日,格式:"HH:mm:ss" - /// - /// 日期 - public static string ToTimeString( this DateTime? dateTime ) { - if( dateTime == null ) - return string.Empty; - return ToTimeString( dateTime.Value ); - } - - /// - /// 获取格式化字符串,带毫秒,格式:"yyyy-MM-dd HH:mm:ss.fff" - /// - /// 日期 - public static string ToMillisecondString( this DateTime dateTime ) { - return dateTime.ToString( "yyyy-MM-dd HH:mm:ss.fff" ); - } - - /// - /// 获取格式化字符串,带毫秒,格式:"yyyy-MM-dd HH:mm:ss.fff" - /// - /// 日期 - public static string ToMillisecondString( this DateTime? dateTime ) { - if( dateTime == null ) - return string.Empty; - return ToMillisecondString( dateTime.Value ); - } - - /// - /// 获取格式化字符串,不带时分秒,格式:"yyyy年MM月dd日" - /// - /// 日期 - public static string ToChineseDateString( this DateTime dateTime ) { - return string.Format( "{0}年{1}月{2}日", dateTime.Year, dateTime.Month, dateTime.Day ); - } - - /// - /// 获取格式化字符串,不带时分秒,格式:"yyyy年MM月dd日" - /// - /// 日期 - public static string ToChineseDateString( this DateTime? dateTime ) { - if( dateTime == null ) - return string.Empty; - return ToChineseDateString( dateTime.SafeValue() ); - } - - /// - /// 获取格式化字符串,带时分秒,格式:"yyyy年MM月dd日 HH时mm分" - /// - /// 日期 - /// 是否移除秒 - public static string ToChineseDateTimeString( this DateTime dateTime, bool removeSecond = false ) { - StringBuilder result = new StringBuilder(); - result.AppendFormat( "{0}年{1}月{2}日", dateTime.Year, dateTime.Month, dateTime.Day ); - result.AppendFormat( " {0}时{1}分", dateTime.Hour, dateTime.Minute ); - if( removeSecond == false ) - result.AppendFormat( "{0}秒", dateTime.Second ); - return result.ToString(); - } - - /// - /// 获取格式化字符串,带时分秒,格式:"yyyy年MM月dd日 HH时mm分" - /// - /// 日期 - /// 是否移除秒 - public static string ToChineseDateTimeString( this DateTime? dateTime, bool removeSecond = false ) { - if( dateTime == null ) - return string.Empty; - return ToChineseDateTimeString( dateTime.Value, removeSecond ); - } - - /// - /// 获取描述 - /// - /// 时间间隔 - public static string Description( this TimeSpan span ) { - StringBuilder result = new StringBuilder(); - if( span.Days > 0 ) - result.AppendFormat( "{0}天", span.Days ); - if( span.Hours > 0 ) - result.AppendFormat( "{0}小时", span.Hours ); - if( span.Minutes > 0 ) - result.AppendFormat( "{0}分", span.Minutes ); - if( span.Seconds > 0 ) - result.AppendFormat( "{0}秒", span.Seconds ); - if( span.Milliseconds > 0 ) - result.AppendFormat( "{0}毫秒", span.Milliseconds ); - if ( result.Length > 0 ) - return result.ToString(); - return $"{span.TotalSeconds * 1000}毫秒"; - } - } -} diff --git a/src/Util/Extensions.Format.cs b/src/Util/Extensions.Format.cs deleted file mode 100644 index 234d34fe1..000000000 --- a/src/Util/Extensions.Format.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Util { - /// - /// 系统扩展 - 格式化 - /// - public static partial class Extensions { - /// - /// 获取描述 - /// - /// 布尔值 - public static string Description( this bool value ) { - return value ? "是" : "否"; - } - - /// - /// 获取描述 - /// - /// 布尔值 - public static string Description( this bool? value ) { - return value == null ? "" : Description( value.Value ); - } - } -} diff --git a/src/Util/Extensions.Infrastructure.cs b/src/Util/Extensions.Infrastructure.cs deleted file mode 100644 index 80f8a0628..000000000 --- a/src/Util/Extensions.Infrastructure.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using Util.Dependency; - -namespace Util { - /// - /// 系统扩展 - 基础设施 - /// - public static partial class Extensions { - /// - /// 注册Util基础设施服务 - /// - /// 服务集合 - /// 依赖配置 - public static IServiceProvider AddUtil( this IServiceCollection services, params IConfig[] configs ) { - return new DependencyConfiguration( services, configs ).Config(); - } - } -} diff --git a/src/Util/Extensions.Lambda.cs b/src/Util/Extensions.Lambda.cs deleted file mode 100644 index 21be3f8a6..000000000 --- a/src/Util/Extensions.Lambda.cs +++ /dev/null @@ -1,400 +0,0 @@ -using System; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using Util.Datas.Queries; -using Util.Expressions; -using Util.Helpers; - -namespace Util { - /// - /// 系统扩展 - Lambda表达式 - /// - public static partial class Extensions { - - #region Property(属性表达式) - - /// - /// 创建属性表达式 - /// - /// 表达式 - /// 属性名,支持多级属性名,与句点分隔,范例:Customer.Name - public static Expression Property( this Expression expression, string propertyName ) { - if( propertyName.All( t => t != '.' ) ) - return Expression.Property( expression, propertyName ); - var propertyNameList = propertyName.Split( '.' ); - Expression result = null; - for( int i = 0; i < propertyNameList.Length; i++ ) { - if( i == 0 ) { - result = Expression.Property( expression, propertyNameList[0] ); - continue; - } - result = result.Property( propertyNameList[i] ); - } - return result; - } - - /// - /// 创建属性表达式 - /// - /// 表达式 - /// 属性 - public static Expression Property( this Expression expression, MemberInfo member ) { - return Expression.MakeMemberAccess( expression, member ); - } - - #endregion - - #region And(与表达式) - - /// - /// 与操作表达式 - /// - /// 左操作数 - /// 右操作数 - public static Expression And( this Expression left, Expression right ) { - if( left == null ) - return right; - if( right == null ) - return left; - return Expression.AndAlso( left, right ); - } - - /// - /// 与操作表达式 - /// - /// 对象类型 - /// 左操作数 - /// 右操作数 - public static Expression> And( this Expression> left, Expression> right ) { - if( left == null ) - return right; - if( right == null ) - return left; - return left.Compose( right, Expression.AndAlso ); - } - - #endregion - - #region Or(或表达式) - - /// - /// 或操作表达式 - /// - /// 左操作数 - /// 右操作数 - public static Expression Or( this Expression left, Expression right ) { - if( left == null ) - return right; - if( right == null ) - return left; - return Expression.OrElse( left, right ); - } - - /// - /// 或操作表达式 - /// - /// 对象类型 - /// 左操作数 - /// 右操作数 - public static Expression> Or( this Expression> left, Expression> right ) { - if( left == null ) - return right; - if( right == null ) - return left; - return left.Compose( right, Expression.OrElse ); - } - - #endregion - - #region Value(获取lambda表达式的值) - - /// - /// 获取lambda表达式的值 - /// - /// 对象类型 - public static object Value( this Expression> expression ) { - return Lambda.GetValue( expression ); - } - - #endregion - - #region Equal(等于表达式) - - /// - /// 创建等于运算表达式 - /// - /// 左操作数 - /// 右操作数 - public static Expression Equal( this Expression left, Expression right ) { - return Expression.Equal( left, right ); - } - - /// - /// 创建等于运算表达式 - /// - /// 左操作数 - /// 值 - public static Expression Equal( this Expression left, object value ) { - return left.Equal( Lambda.Constant( left, value ) ); - } - - #endregion - - #region NotEqual(不等于表达式) - - /// - /// 创建不等于运算表达式 - /// - /// 左操作数 - /// 右操作数 - public static Expression NotEqual( this Expression left, Expression right ) { - return Expression.NotEqual( left, right ); - } - - /// - /// 创建不等于运算表达式 - /// - /// 左操作数 - /// 值 - public static Expression NotEqual( this Expression left, object value ) { - return left.NotEqual( Lambda.Constant( left, value ) ); - } - - #endregion - - #region Greater(大于表达式) - - /// - /// 创建大于运算表达式 - /// - /// 左操作数 - /// 右操作数 - public static Expression Greater( this Expression left, Expression right ) { - return Expression.GreaterThan( left, right ); - } - - /// - /// 创建大于运算表达式 - /// - /// 左操作数 - /// 值 - public static Expression Greater( this Expression left, object value ) { - return left.Greater( Lambda.Constant( left, value ) ); - } - - #endregion - - #region GreaterEqual(大于等于表达式) - - /// - /// 创建大于等于运算表达式 - /// - /// 左操作数 - /// 右操作数 - public static Expression GreaterEqual( this Expression left, Expression right ) { - return Expression.GreaterThanOrEqual( left, right ); - } - - /// - /// 创建大于等于运算表达式 - /// - /// 左操作数 - /// 值 - public static Expression GreaterEqual( this Expression left, object value ) { - return left.GreaterEqual( Lambda.Constant( left, value ) ); - } - - #endregion - - #region Less(小于表达式) - - /// - /// 创建小于运算表达式 - /// - /// 左操作数 - /// 右操作数 - public static Expression Less( this Expression left, Expression right ) { - return Expression.LessThan( left, right ); - } - - /// - /// 创建小于运算表达式 - /// - /// 左操作数 - /// 值 - public static Expression Less( this Expression left, object value ) { - return left.Less( Lambda.Constant( left, value ) ); - } - - #endregion - - #region LessEqual(小于等于表达式) - - /// - /// 创建小于等于运算表达式 - /// - /// 左操作数 - /// 右操作数 - public static Expression LessEqual( this Expression left, Expression right ) { - return Expression.LessThanOrEqual( left, right ); - } - - /// - /// 创建小于等于运算表达式 - /// - /// 左操作数 - /// 值 - public static Expression LessEqual( this Expression left, object value ) { - return left.LessEqual( Lambda.Constant( left, value ) ); - } - - #endregion - - #region StartsWith(头匹配) - - /// - /// 头匹配 - /// - /// 左操作数 - /// 值 - public static Expression StartsWith( this Expression left, object value ) { - return left.Call( "StartsWith", new[] { typeof( string ) }, value ); - } - - #endregion - - #region EndsWith(尾匹配) - - /// - /// 尾匹配 - /// - /// 左操作数 - /// 值 - public static Expression EndsWith( this Expression left, object value ) { - return left.Call( "EndsWith", new[] { typeof( string ) }, value ); - } - - #endregion - - #region Contains(模糊匹配) - - /// - /// 模糊匹配 - /// - /// 左操作数 - /// 值 - public static Expression Contains( this Expression left, object value ) { - return left.Call( "Contains", new[] { typeof( string ) }, value ); - } - - #endregion - - #region Operation(操作) - - /// - /// 操作 - /// - /// 左操作数 - /// 运算符 - /// 值 - public static Expression Operation( this Expression left, Operator @operator, object value ) { - switch( @operator ) { - case Operator.Equal: - return left.Equal( value ); - case Operator.NotEqual: - return left.NotEqual( value ); - case Operator.Greater: - return left.Greater( value ); - case Operator.GreaterEqual: - return left.GreaterEqual( value ); - case Operator.Less: - return left.Less( value ); - case Operator.LessEqual: - return left.LessEqual( value ); - case Operator.Starts: - return left.StartsWith( value ); - case Operator.Ends: - return left.EndsWith( value ); - case Operator.Contains: - return left.Contains( value ); - } - throw new NotImplementedException(); - } - - #endregion - - #region Call(调用方法表达式) - - /// - /// 创建调用方法表达式 - /// - /// 调用的实例 - /// 方法名 - /// 参数值列表 - public static Expression Call( this Expression instance, string methodName, params Expression[] values ) { - return Expression.Call( instance, instance.Type.GetTypeInfo().GetMethod( methodName ), values ); - } - - /// - /// 创建调用方法表达式 - /// - /// 调用的实例 - /// 方法名 - /// 参数值列表 - public static Expression Call( this Expression instance, string methodName, params object[] values ) { - if( values == null || values.Length == 0 ) - return Expression.Call( instance, instance.Type.GetTypeInfo().GetMethod( methodName ) ); - return Expression.Call( instance, instance.Type.GetTypeInfo().GetMethod( methodName ), values.Select( Expression.Constant ) ); - } - - /// - /// 创建调用方法表达式 - /// - /// 调用的实例 - /// 方法名 - /// 参数类型列表 - /// 参数值列表 - public static Expression Call( this Expression instance, string methodName, Type[] paramTypes, params object[] values ) { - if( values == null || values.Length == 0 ) - return Expression.Call( instance, instance.Type.GetTypeInfo().GetMethod( methodName, paramTypes ) ); - return Expression.Call( instance, instance.Type.GetTypeInfo().GetMethod( methodName, paramTypes ), values.Select( Expression.Constant ) ); - } - - #endregion - - #region Compose(组合表达式) - - /// - /// 组合表达式 - /// - /// 对象类型 - /// 左操作数 - /// 右操作数 - /// 合并操作 - internal static Expression Compose( this Expression first, Expression second, - Func merge ) { - var map = first.Parameters.Select( ( f, i ) => new { f, s = second.Parameters[i] } ).ToDictionary( p => p.s, p => p.f ); - var secondBody = ParameterRebinder.ReplaceParameters( map, second.Body ); - return Expression.Lambda( merge( first.Body, secondBody ), first.Parameters ); - } - - #endregion - - #region ToLambda(创建Lambda表达式) - - /// - /// 创建Lambda表达式 - /// - /// 委托类型 - /// 表达式 - /// 参数列表 - public static Expression ToLambda( this Expression body, params ParameterExpression[] parameters ) { - if ( body == null ) - return null; - return Expression.Lambda( body, parameters ); - } - - #endregion - } -} diff --git a/src/Util/Extensions.Validate.cs b/src/Util/Extensions.Validate.cs deleted file mode 100644 index 00a915036..000000000 --- a/src/Util/Extensions.Validate.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; - -namespace Util { - /// - /// 系统扩展 - 验证 - /// - public static partial class Extensions { - /// - /// 检测对象是否为null,为null则抛出异常 - /// - /// 对象 - /// 参数名 - public static void CheckNull( this object obj, string parameterName ) { - if( obj == null ) - throw new ArgumentNullException( parameterName ); - } - - /// - /// 是否为空 - /// - /// 值 - public static bool IsEmpty( this string value ) { - return string.IsNullOrWhiteSpace( value ); - } - - /// - /// 是否为空 - /// - /// 值 - public static bool IsEmpty( this Guid value ) { - return value == Guid.Empty; - } - - /// - /// 是否为空 - /// - /// 值 - public static bool IsEmpty( this Guid? value ) { - if ( value == null ) - return true; - return value == Guid.Empty; - } - } -} diff --git a/src/Util/Files/DefaultFileStore.cs b/src/Util/Files/DefaultFileStore.cs deleted file mode 100644 index 40717e4d8..000000000 --- a/src/Util/Files/DefaultFileStore.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.IO; -using Util.Files.Paths; - -namespace Util.Files { - /// - /// 本地文件存储服务 - /// - public class DefaultFileStore : IFileStore { - /// - /// 路径生成器 - /// - private readonly IPathGenerator _generator; - - /// - /// 初始化本地文件存储服务 - /// - /// 路径生成器 - public DefaultFileStore( IPathGenerator pathGenerator ) { - _generator = pathGenerator; - } - - /// - /// 保存文件,返回完整文件路径 - /// - /// 文件流 - /// 文件名,包含扩展名 - public string Save( byte[] stream, string fileName ) { - var path = _generator.Generate( fileName ); - using ( new FileStream( path, FileMode.Create ) ) { - } - return path; - } - } -} diff --git a/src/Util/Files/FileInfo.cs b/src/Util/Files/FileInfo.cs deleted file mode 100644 index 5cd320c3f..000000000 --- a/src/Util/Files/FileInfo.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace Util.Files { - /// - /// 文件信息 - /// - public class FileInfo { - /// - /// 初始化文件信息 - /// - /// 文件路径 - /// 文件大小 - /// 文件名 - /// 文件标识 - public FileInfo( string path, long? size, string fileName = null, string id = null ) { - Path = path; - Size = new FileSize( size.SafeValue() ); - Extension = System.IO.Path.GetExtension( path ).TrimStart( '.' ); - FileName = string.IsNullOrWhiteSpace( fileName ) ? System.IO.Path.GetFileName( path ) : fileName; - Id = id; - } - - /// - /// 文件标识 - /// - public string Id { get; } - /// - /// 文件路径 - /// - public string Path { get; } - /// - /// 文件名 - /// - public string FileName { get; } - /// - /// 扩展名 - /// - public string Extension { get; } - /// - /// 文件大小 - /// - public FileSize Size { get; } - } -} diff --git a/src/Util/Files/FileSize.cs b/src/Util/Files/FileSize.cs deleted file mode 100644 index 6e0903d21..000000000 --- a/src/Util/Files/FileSize.cs +++ /dev/null @@ -1,78 +0,0 @@ -namespace Util.Files { - /// - /// 文件大小 - /// - public struct FileSize { - /// - /// 初始化文件大小 - /// - /// 文件大小 - /// 文件大小单位 - public FileSize( long size, FileSizeUnit unit = FileSizeUnit.Byte ) { - _size = GetSize( size, unit ); - } - - /// - /// 获取文件大小 - /// - private static long GetSize( long size, FileSizeUnit unit ) { - switch( unit ) { - case FileSizeUnit.K: - return size * 1024; - case FileSizeUnit.M: - return size * 1024 * 1024; - case FileSizeUnit.G: - return size * 1024 * 1024 * 1024; - default: - return size; - } - } - - private readonly long _size; - /// - /// 文件字节长度 - /// - public long Size => _size; - - /// - /// 获取文件大小,单位:字节 - /// - public int GetSize() { - return (int)Size; - } - - /// - /// 获取文件大小,单位:K - /// - public double GetSizeByK() { - return Util.Helpers.Convert.ToDouble( _size / 1024.0, 2 ); - } - - /// - /// 获取文件大小,单位:M - /// - public double GetSizeByM() { - return Util.Helpers.Convert.ToDouble( _size / 1024.0 / 1024.0, 2 ); - } - - /// - /// 获取文件大小,单位:G - /// - public double GetSizeByG() { - return Util.Helpers.Convert.ToDouble( _size / 1024.0 / 1024.0 / 1024.0, 2 ); - } - - /// - /// 输出描述 - /// - public override string ToString() { - if( _size >= 1024 * 1024 * 1024 ) - return $"{GetSizeByG()} {FileSizeUnit.G.Description()}"; - if( _size >= 1024 * 1024 ) - return $"{GetSizeByM()} {FileSizeUnit.M.Description()}"; - if( _size >= 1024 ) - return $"{GetSizeByK()} {FileSizeUnit.K.Description()}"; - return $"{_size} {FileSizeUnit.Byte.Description()}"; - } - } -} \ No newline at end of file diff --git a/src/Util/Files/FileSizeUnit.cs b/src/Util/Files/FileSizeUnit.cs deleted file mode 100644 index 18cdcfe21..000000000 --- a/src/Util/Files/FileSizeUnit.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.ComponentModel; - -namespace Util.Files { - /// - /// 文件大小单位 - /// - public enum FileSizeUnit { - /// - /// 字节 - /// - [Description( "B" )] - Byte, - /// - /// K字节 - /// - [Description( "KB" )] - K, - /// - /// M字节 - /// - [Description( "MB" )] - M, - /// - /// G字节 - /// - [Description( "GB" )] - G - } - - /// - /// 文件大小单位枚举扩展 - /// - public static class FileSizeUnitExtensions { - /// - /// 获取描述 - /// - public static string Description( this FileSizeUnit? unit ) { - return unit == null ? string.Empty : unit.Value.Description(); - } - - /// - /// 获取值 - /// - public static int? Value( this FileSizeUnit? unit ) { - return unit?.Value(); - } - } -} \ No newline at end of file diff --git a/src/Util/Files/IFileStore.cs b/src/Util/Files/IFileStore.cs deleted file mode 100644 index 04303fbea..000000000 --- a/src/Util/Files/IFileStore.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Util.Files { - /// - /// 文件存储服务 - /// - public interface IFileStore { - /// - /// 保存文件,返回完整文件路径 - /// - /// 文件流 - /// 文件名,必须包含扩展名,如果仅传入扩展名则生成随机文件名 - string Save( byte[] stream, string fileName ); - } -} diff --git a/src/Util/Files/Paths/DefaultBasePath.cs b/src/Util/Files/Paths/DefaultBasePath.cs deleted file mode 100644 index 2c34c2eef..000000000 --- a/src/Util/Files/Paths/DefaultBasePath.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Util.Files.Paths { - /// - /// 基路径 - /// - public class DefaultBasePath : IBasePath { - /// - /// 基路径 - /// - private readonly string _path; - - /// - /// 初始化基路径 - /// - /// 基路径 - public DefaultBasePath( string path ) { - _path = path; - } - - /// - /// 获取基路径 - /// - public string GetPath() { - return _path; - } - } -} diff --git a/src/Util/Files/Paths/DefaultPathGenerator.cs b/src/Util/Files/Paths/DefaultPathGenerator.cs deleted file mode 100644 index c7186c767..000000000 --- a/src/Util/Files/Paths/DefaultPathGenerator.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.IO; -using Util.Helpers; -using Util.Randoms; - -namespace Util.Files.Paths { - /// - /// 路径生成器 - /// - public class DefaultPathGenerator : PathGeneratorBase { - /// - /// 基路径 - /// - private readonly IBasePath _basePath; - - /// - /// 初始化路径生成器 - /// - /// 基路径 - /// 随机数生成器 - public DefaultPathGenerator( IBasePath basePath, IRandomGenerator randomGenerator = null ) : base( randomGenerator ) { - _basePath = basePath; - } - - /// - /// 创建完整路径 - /// - /// 被处理过的安全有效的文件名 - protected override string GeneratePath( string fileName ) { - return Common.GetPhysicalPath( Path.Combine( _basePath.GetPath(), fileName ) ); - } - } -} diff --git a/src/Util/Files/Paths/IBasePath.cs b/src/Util/Files/Paths/IBasePath.cs deleted file mode 100644 index 8a65dd334..000000000 --- a/src/Util/Files/Paths/IBasePath.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Util.Files.Paths { - /// - /// 基路径 - /// - public interface IBasePath { - /// - /// 获取基路径 - /// - string GetPath(); - } -} diff --git a/src/Util/Files/Paths/IPathGenerator.cs b/src/Util/Files/Paths/IPathGenerator.cs deleted file mode 100644 index a4f3f426c..000000000 --- a/src/Util/Files/Paths/IPathGenerator.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Util.Files.Paths { - /// - /// 路径生成器 - /// - public interface IPathGenerator { - /// - /// 生成完整路径 - /// - /// 文件名,必须包含扩展名,如果仅传入扩展名则生成随机文件名 - string Generate( string fileName ); - } -} diff --git a/src/Util/Files/Paths/PathGeneratorBase.cs b/src/Util/Files/Paths/PathGeneratorBase.cs deleted file mode 100644 index 369d91033..000000000 --- a/src/Util/Files/Paths/PathGeneratorBase.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.IO; -using Util.Helpers; -using Util.Randoms; - -namespace Util.Files.Paths { - /// - /// 路径生成器 - /// - public abstract class PathGeneratorBase : IPathGenerator { - /// - /// 随机数生成器 - /// - private readonly IRandomGenerator _randomGenerator; - - /// - /// 初始化路径生成器 - /// - /// 随机数生成器 - protected PathGeneratorBase( IRandomGenerator randomGenerator ) { - _randomGenerator = randomGenerator ?? GuidRandomGenerator.Instance; - } - - /// - /// 生成完整路径 - /// - /// 文件名,必须包含扩展名,如果仅传入扩展名则生成随机文件名 - public string Generate( string fileName ) { - if( string.IsNullOrWhiteSpace( fileName ) ) - throw new ArgumentNullException( nameof( fileName ) ); - return GeneratePath( GetFileName( fileName ) ); - } - - /// - /// 创建完整路径 - /// - /// 被处理过的安全有效的文件名 - protected abstract string GeneratePath( string fileName ); - - /// - /// 获取文件名 - /// - private string GetFileName( string fileName ) { - var name = Path.GetFileNameWithoutExtension( fileName ); - var extension = Path.GetExtension( fileName ).TrimStart( '.' ); - if( string.IsNullOrWhiteSpace( extension ) ) { - extension = fileName; - name = string.Empty; - } - if( string.IsNullOrWhiteSpace( name ) ) - name = _randomGenerator.Generate(); - name = FilterFileName( name ); - return $"{name}-{Time.GetDateTime():HHmmss}.{extension}"; - } - - /// - /// 过滤文件名 - /// - private static string FilterFileName( string fileName ) { - fileName = Regex.Replace( fileName, "\\W", "" ); - return Util.Helpers.String.PinYin( fileName ); - } - } -} diff --git a/src/Util/Helpers/Async.cs b/src/Util/Helpers/Async.cs deleted file mode 100644 index 971ada57f..000000000 --- a/src/Util/Helpers/Async.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Threading.Tasks; -using Nito.AsyncEx; - -namespace Util.Helpers { - /// - /// 异步操作 - /// - public static class Async { - /// - /// 执行 - /// - /// 操作,范例:Async.Run( async () => await SendAsync() ); - public static void Run( Action action ) { - AsyncContext.Run( action ); - } - - /// - /// 执行 - /// - /// 操作,范例:Async.Run( async () => await SendAsync() ); - public static void Run( Func action ) { - AsyncContext.Run( action ); - } - - /// - /// 执行 - /// - /// 返回类型 - /// 操作,范例:Async.Run( async () => await SendAsync() ); - public static TResult Run( Func action ) { - return AsyncContext.Run( action ); - } - - /// - /// 执行 - /// - /// 返回类型 - /// 操作,范例:Async.Run( async () => await SendAsync() ); - public static TResult Run( Func> action ) { - return AsyncContext.Run( action ); - } - } -} diff --git a/src/Util/Helpers/Common.cs b/src/Util/Helpers/Common.cs deleted file mode 100644 index ffcef3307..000000000 --- a/src/Util/Helpers/Common.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.IO; - -namespace Util.Helpers { - /// - /// 常用公共操作 - /// - public static class Common { - /// - /// 获取类型 - /// - /// 类型 - public static Type GetType() { - var type = typeof( T ); - return Nullable.GetUnderlyingType( type ) ?? type; - } - - /// - /// 换行符 - /// - public static string Line => Environment.NewLine; - - /// - /// 获取物理路径 - /// - /// 相对路径 - public static string GetPhysicalPath( string relativePath ) { - if( string.IsNullOrWhiteSpace( relativePath ) ) - return string.Empty; - var rootPath = Web.WebRootPath; - if( string.IsNullOrWhiteSpace( rootPath ) ) - return Path.GetFullPath( relativePath ); - return $"{Web.RootPath}\\{relativePath.Replace( "/", "\\" ).TrimStart( '\\' )}"; - } - } -} diff --git a/src/Util/Helpers/Const.cs b/src/Util/Helpers/Const.cs deleted file mode 100644 index 945a9e118..000000000 --- a/src/Util/Helpers/Const.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Util.Helpers { - /// - /// 常量 - /// - public static class Const { - /// - /// 汉字及拼音首字母 - /// - public const string ChinesePinYin = "丂k丄s丅x丆m丏m丒c丗s丢d丠q両l丣y并b丩j丮j丯j丱g丳c丵z丷b丼j乀f乁y讈l乆j乑y乕h乗c乚h乛w乢g乣j乤h乧d乨s乪n乫g乬g乭d乮m乯o乲c乴x乵y乶p乷s乸n乹q乺s乻e乼z乽z乿z亀g亁q乱l亃l亄y亅j亇m亊s亐y亖s亗s亘g亜y亝q亚y亣d亪y亯x亰j亱y亴y亶d亷l亸d亹m亼j亽j亾w仈b仌b仏f仐j仒e仚x仛t仜h仢b仦c仧c仩c仭r仮f仯c仱q仴w仸y仹f仺c仼w仾d伀z伂p伃y伄d伅d伆w伇y伈x汲j伌a伒j伓p伔d伖t伜c伝y伡c伣q伨x伩x伬c伭x伮n伱n伳x伵x伷z伹q伻b伾p伿z佀s佁a佄h佅m伫z布b佉q佊b佋z佌c佒y占z佖b佡x佢q佦s佨b徊h佫h佭x佮g佱f佲m佷h佸h佺q佽c侁s侂t侅g来l侇y侊g侌y侎m侐x侒a侓l侕e仑l侘c侙c侚x侜z侞r侟c価s侢d侤t侫n侭j侰j侱c侲z侳z侴h侣l局j侸s侹t侺s侻t侾x俀t俣y系j俆x俇g俈k俉w俋y俌f俍l俒h俓j俔q俕s俖p俙x俛f侠x俢x俤d俥c俧z俫l俬s俰h俲x俴j俵b俶c俷f俹y俻b俼y俽x俿h伥c倁z倂b倃j倅c俩l倇w倈l仓c倊z个g倎t倐s们m倓t倕c幸x倗p倛q倝g倞j倠s倢j仿f値z倧z伦l倯s倰l倱h倲d倳z倴b倵w倶j倷n倸c倹j倻y倽s倿n偀y偁c偂q偄r偅z偆c伟w偊y偋b偍t偐y偑f偒t偓w偔e偖c偗s偘k偙d偛c偝b偞x偠y偡z偢c偣a偦x偧z偨c偩f偪b偫z偭m偮j偯y偰x偱x偲c偳d侧c侦z偸t偹b咱z偼j伪w傁s傂z傃s傄x傆y傇r傉n傊y傋j傌m傎d傏t傐h杰j傒x傓s傔q傕j伧c傗c伞s备b傚x傛r傜y傝t傞s傟y傠f傡b家j傤z傦g傪c傫l佣y傮z偬z傰p傱s传c伛y债z伤s傹j傼h傽z倾q傿y僀d僁x偻l僃b僄b仅j僆l戮l僈m佥q僊x僋t僌y働d僎z僐s侨q僒j僓t僔z仆p僗l僘c僙g僛q僜d僝c僞w僟j僠b僡h僢c僣t僤d侥j偾f僩x僪y僫e僯l僰b雇g僲x僴x僶m僷y僸j价j僺q僼f僽z僾a僿s仪y儁j侬n儃c亿y当d侩k俭j儊c儌j儍s儎z儏c傧b儑a儓t俦c侪c儖l儗y尽j儙q儚m儛w儜n儝q儞n偿c儠l儢l儣k儤b儥y儦b儧z儨z儩s优y儫h儬q儭c儮l儯t儰w儱l储c儳c儴x儵s儶h俪l罗l儹z傩n傥t俨y儽l儾n兂z凶x兊d兑d兎t兏c児e儿e兖y兘s兟s兡b兤h兦w内n两l兪y兯h兲t兾j兿y冃m冄r円y冇m册c冋j冎g冏j冐m胄z冓g冔x冘y冚k冝y冞s冟s冡m冣z冦k冧l冨f冩x幂m冭t冮g冴y冸p冹f冺m冾q冿j凁s凂m凃t凅g净j凊q冻d凎g凐y凒a凓l凔c凕m凖z凗c凘s凙d凚j凛l凞x凟d凢f凣f凥j処c凧y凨f凩k凪n凬f凮f凯k凲g凴p凷k凾h刄r刅c刉j刋q刌c刏j刐d刓w刔j刕l刜f刞q刟d刡m刢l刣z别b刦j刧j删s刬c刯g刱c刲k刴d刵e刼j刾c刭j剅d剆l则z剈y剉c克k刹c剒c剓l剕f剗c剘q剙c剚z刚g剥b剟d剠q剢d剣j剤j剦y剨h剫d剬z剭w剐g剰s剱j剳d剀k创c剶c铲c剸t剹l剺l剻p剼s剾k劀g划h劄z劅z劆l剧j刘l刽g劋j刿g剑j劎j劏t剂j劒j劔j劕z劖c劗z劘m劙l劚z劜y劤j劥k劦x劧z劮y劯z劰m労l劵j劶k劷y劸w効x劺m匡k劼j劽l勀k劲j勂g勄m勅c勆l勈y勊k勌j勍q勎l勏b勑c勓k勔m动d勖x务w勚y勋x勜w胜s劳l勠l勡p势s积j勥j剿c勧q勨x勪q勫f勬j勭t勮j勯d劢m勲x勳x勴l励l勶c勷x劝q匀y勼j勽b匁m匂x匃g匄g匇y匉p匊j匋t匌g匎e匑g匒d匓j匔g匘n匛j匜y匞j匟k匢h匤q匥f匧q匨z匩k匫h匬y匦g汇h匰d匮k匲l匳l匴s匵d匶j匷j匸x匼k匽y区o卂x卄n卆z卋s卌x卍w卐w协x単d卙j卛s卝g卥x卨x卪j卬a卭q卲s卶c恤x却q卼w卽j卾e厀x厁s厃w厇z厈h厊y厎d厏z厐p厑y厒q厓y厔z厖m厗t厍s厛t厜z厞f厡y厤l厧d厪j厫a厬g厌y厯l厰c厱q厉l厳y厣y厵y厷g厸l厹r厺q厼k厽l厾d叀z参c叄c叅c叆a叇d収s叏g叐b叒r叓l叕z叚j叜s叝j叞w叡r丛c叧g叴q叺c叾d叿h吀m吂m吅x吇z寸c吔y吘o吙h吚y吜c吢q吤j吥b吪e吰h吴w呐n吷x吺d吽h吿g呁j吕l呄g呅w呇q呉w呌j呍h尺c呏s呑t呚h呝e呞s呟j呠p呡w呣m呥r呧d呪z呫t呬x呭y呮q呯p呰z呴g呹y呺x呾d呿q咁x咃t咅p咇b咈f咉y咊h咍h咑d咓w咗z咘b咜t咞x咟h咠q咡e咢e咥d咮z咰s咲x咵k咶g咷t咹e咺x呙g咾l哃t哅x哊y哋d哖n哘x哛p哠h员y哢l哣p哤m哫z哬h哯x哰l哱b哴l哵b哶m哸s哹f哻h哾c唀y唂g唃g呗b含h唈y唊j唋t唌d唍w唎l唒q唓c唕z唖y唗d唘q唙d唚q唜m唝g唞d唟g唡l唥l唦s唨z唩w唫j唭q唲e唴q唵a唶j念n唹y唺t唻l唽x啀a啂g啇d啈h啋c啌q啍z啎w问w啑s啒g啓q啔q啗d啘w啚b啝h哑y启q啠z啢l衔x啨q啩g啫z啯g啰l啱y啲d啳q啴c啹j啺t啽a啿d喅y喆z喌z喍c喎w喐h喒z喓y喕m喖h喗y唤h喛h喞j喠z喡w喢s喣x喤h喥d喦y喨l喩y丧s吃c乔q喭y单c喯p喰c哟y喴w営y喸p喺x喼j喿z嗀h嗁t嗂y嗃h呛q啬s嗈y嗊g嗋x吗m嗏c嗐h嗕r嗗w嗙p呜w嗛q嗞z嗠l嗢w嗧j唢s嗭z嗮s嗰g嗱n嗴q哔b嗸a嗹l嗺z嗻z嗼m嗿t嘂j嘃c嘄j嘅k叹t嘇s嘊a嘋x喽l嘐x嘑h嘒h呕o嘕x啧z尝c嘙p嘚d唛m嘝h嘠g嘡t嘢y嘥s嘦j嘨x哗h嘪m嘫r唠l啸x叽j嘳k哓x嘷h呒m嘺q嘼c嘽t嘾d噀x恶e噂z噃f噄c噅h噆z噇c噈c噉d噊j噋t噏x噐q噑h噒l嘘s噕h噖y噚x噛n噝s噞y噟y哒d噡z噣z哝n哕y噧x噭j噮y嗳a噰y哙k噳y喷p噷x吨d噺h噽p噾y噿z咛n嚁d嚂l嚃t嚄h吓h嚈y嚉d嚊x嚋z哜j嚑x嚔t噜l嚖h嚗b嚘y啮n嚚y嚛h嚜m嚝h嚞z嚟l嚠l嚡x嚢n嚤m咽y呖l嚧l咙l嚩p嚪d嚫c嚬p嚭p向x嚰m嚱x嚲d喾k严y嚵c嘤y嚸d嚹l嚺t嚻x嚽c嚾h嚿h啭z嗫n嚣a囃z冁c囆c囇l呓y囋z苏s囍x囎z囏j囐y嘱z囒l囓n囕r囖l囘h囙y囜n団t囥k囦y囧j囨p囩y囱c囬h囮e囯g囲w図t囶g囷q囸r囼t圀g圁y圂h圅h囵l国g圌c围w圎y圏q圐k圑p园y圆y圔y圕t图t圗t团t圙l圚h圛y圝l圞l圠y圡t圢t圤p圥l圦k圧y圫y圱q圲q圴z圵d圶q圷x圸s圼n圽m圿j坁z坃x坄y坅q坆m坈r坉t坋b坒b坓j坔d坕j坖j坘d坙j坢b坣t坥q坧z坬g坮t垧s坱y坲f坴l坵q坸g坹x坽l坾z坿f垁z垇a垈d垉p垊m垍j垎h垏l垐c垑c垔y垕h垖d垗z垘f垙g垚y垜d垝g垞c垟y垥x垨s垪b垬h垯d垰k垱d垳h垵a垶x垷x垹b垺p垻b垼y垽y垾h垿x埀c埁c埄b埅d埆q埇y埈j埉x埊d埌l埍j埐q埑z埓l埖h埗b埛j埜y埞d垭y埢q埣s埥q埦w埧j埨l埩z埪k埫c埬d埮t埰c埲b埳x埵d执z埻z埼q埾j埿n堁k堃k堄n坚j堈g堉y垩e堌g堎l堏f堐y堒k堓a堔s堖n堗t堘c堚h堛b堜l埚g堟z堢b堣y堥m堦j堧r堨a堩g堫z堬y堭h堮e尧y报b堲j堳m场c堶t堷y堸f堹z堺j堻j堼f堽g堾c碱j塀p塁l塂j塃h塅d塆w塇x塉j块k茔y塎y垲k塐s埘s塓m塕w塖c涂t塙q塚z塛l塜p塝b塟z塠d塡t坞w塣z埙x塦z塧a塨g塩y塪x塭w塮x塯l塰h塱l塲c塳p塴b尘c塶l塷l塸o堑q塺m塻m塼z塽s塿l墂b墄q墆z墇z墈k垫d墋c墌z墍x墎g墏q墐j墒s墔c墕y墖t増z墘q墛w坠z墝q墠s墡s墢f墣p墤k墥t墦f墧q墪d墫c墬d墭s堕d墯d墰t墱d墲w墴h墵t墶d墷y墸z墹j墺a墙q垦k墿y壀p壂d壃j壄y壆x坛t壈l壉j壊h壋d壌r壍q壏x壐x壒a压y壖r壗j垒l圹k垆l壛y壜t壝w坏h垄l壠l壡r坜l壣l壥c壦x壧y壨l坝b壪w壭s壮z壱y売m壴z壵z壷h壸k壶h壻x壼k寿s壾m壿d夀s夁y夃g夅j夆f夈z変b夊s夋q夌l夎c夐x夑x夒n夓x夗y夘w夛d夝q夞y够g夡q梦m夣m夦c夨c夬g夰g夲b夳t夵y夶b夻h夽y夹g夿b奀e奃d奅p奆j奊x奌d奍q奂h奒k奓z奙b奛h奜f奝d奞x奟b奡a奣w奤h奦w奥a奨j奁l夺d奫y奬j奭s奋f奯h奰b奱l奲d奵d奷q奺j奻n奼c奾x奿f妀j妅h妉d妋f妎h妏w妐z妑p妔k妕z妘y妚f妜y妆z妟y妠n妡x妦f妧w妬d妰z妱z你n妴y妵t妶x妷z妸e妺m妼b妽s妿e姀h姁x姂f姄m姅b姇f姈l姉z姌r姗s姎y姏g姕z姖j姙r姛d姞j姟g姠x姡h姢j姤g奸j姧j姩n侄z姫z姭x姮h姯g姰j姱h姲y姳m姴l姵p姶y姷y姸y姺x姼s姽g姾q娀s娂h娋s娍c娎x娏m娐f娒m娔k娕c娖c娗t娙x娚n娱y娝b娞n娡z娢h娤z娦p娧t娨m娪w娫y娬w娭a娮y娯y娰s娳l娵j娷s娸q娹x娺z娻d娽l娾a娿e娄l婃c婄p婅j婇c婈l婋x婌s婍q婎h婏f婐w婑w婒t婓f婔f婖t婗n婘j婙j婛j婜q婝d婞x婟h婠w婡l婣y婤z婥c妇f婨l婩a婫h婬y娅y婮j婯l婰d婱x婳h婸d婹y婻n婼c婽j婾t媀y媁w媂d媃r媄m媅d媆r媇q媈h媉w媊q媋c媌m媍f媎j媏d媐y媓h媔m媕a媖y媗x媘j媙w媜z媞t媟x媠d媢m媣r媤s媥p媦w娲w媨c媩h媫j媬b媭x媮t妫g媰c媱y媴y媶r媷r媹l媺m媻p媪a妈m媿k嫀q嫃z嫄y嫅j嫆r嫇m嫈y嫊s嫋n嫍t嫎p嫏l嫐n嫑b嫓p嫕y妪y嫙x嫚m嫛y嫝k嫞y嫟n嫢g嫤j嫥z嫧z嫨h嫪l嫬z嫭h嫮h嫯a嫰n嫲m嫳p嫴g妩w嫶q嫷t嫸z嫹m嫺x娴x嫼m嫽l嫾l嬀g嬁d嬂z嬄y嬅h嬆x嬇k娆r嬊y婵c娇j嬍m嬎f嬏f嬐x嬑y嬒h嬔f嬕s嬘s嫱q嬚l嬛h袅n嬞d嬠c嫒a嬢n嬣n嬷m嬥t嬦c嬧j嬨c嬩y嫔p嬫r嬬r嬭n嬮y嬯t婴y嬱q嬳y嬵m嬶k婶s嬹x嬺n嬻d嬼l嬽y嬾l嬿y孁l孂j娘n孄l孅x孆y孇s孈x孉h孊m孋l娈l孍y孎z孏l孖m孞x孠s孡t孧y孙s孭m孮c孯q孲y孴n孷l学x孹b孻n孼n孾y孪l宂r宆q宊t宍r宎y宐y宑j宒z宔z宖h実s宧y宨t宩s宬c宭q宫g宯x宱z宲b宷c宺h宻m宼k寀c寁z寃y寈q寉h寊z寋j寍n寎b寏h寑q寔s寕n寖j寗n寘z寙y寚b寛k寜n寠j寝q寣h实s宁n审s寪w写x宽k寭h寯j寱y寲y寳b寴q宠c宝b寷f寽l対d尀p専z尃f将j专z寻x尌s对d导d尐j尒e尓e尗s尙s尛m尞l尟x尠x尡h尣w尦l尨m尩w尪w尫w尭y尮d尯k尰z尲g尳g尵t尶g尴g屃x届j屇t屌d屍s屒z屓x屔n屖x屘m屚l屛p屉t扉f屟x屡l层c屧x屦j屩j屪l屫j属s屭x屰n屲w屳x屴l屵a屶h屷h屸h屻r屼w屽h屾s岀c岃y岄y岆y岇a岉w岊j岋e岏w岒q岓q岕j岝z岞z岠j冈g岤x岥p岦l迢t岨j岮t岯p岰a岲k岴q岶p岹t岺l岻d岼p峀x峂t峃x峅b峆h峇b峈l峉e峌d峍l峎e峏e峐g峑q峓y峔m峕s峖a峗w峘h峚m峛l峜f峝t峞w峟y峠q峢l峧j峩e峫x峬b峯f峱n峲l峳y岘x峵r岛d峷s峸c峹t峺g峼g峡x峿w崀l崁k崄x崅q崈c崉t崊l崋h崌j崃l崏m崐k崑k崒z崓g崕y岗g崘l崚l崜d崝z崟y岽d崡h峥z崣w崥p崨j崪z崫j崬d崯y崰z崱z崲h嵛y崵y崶f崷q崸y崹t崺y崻z崼s崿e嵀z嵁k嵂l嵃y嵄m嵅h嵆j嵈h嵉t嵍w嵎y嵏z岚l嵑k岩y嵓y嵔w嵕z嵖c嵗s嵙k嵚q嵜q嵞t嵟c嵠x嵡w嵢c嵣d嵤r嵥j嵦k嵧l嵨w嵪k嵭b嵮d嵰q嵱y嵲n嵳c嵵s嵶r嵷s嵸z嵹j嵺l嵻k嵼y嵾c嵿d嶀t嵝l嶃z崭z嶅a嶆c岖q嶉w嶊z嶋d嶌d嶍x嶎y嶏p嶐l嶑x嶒c嶓b嶔q嶕j嶖y崂l嶘z嶚l嶛l嶜q嶞t嶟z峤j嶡j嶢y嶣j嶤y嶥j嶦z峄y嶨x嶩n嶫y嶬y嶭n嶮x嶯j嶰x嶱k嶲g嶳d嶴a嶵z嶶w嵘r嶹d岭l嶻j屿y岳y嶾y嶿r巀j巁l巂x巃l巄l巇x巈j巉c巊y岿k巌y巎n巐c漓l峦l巓d巅d巕n巗y巘y巙k巚y巜k巟h巠j巣c巤l巪g巬p巭b巯q巵z巶z巸y卺j巺x巻j巼p巿f帀z帄d帇n帊p帋z帍h帎d帒d帓m帗f帞m帟y帠y帡p帢q帣j帤r帅s帨s帩q帪z师s帬q帯d帰g帲p帐z帴j帵w带d帹s帺q帾z帿h帧z幁z帏w幆y幇b幈p幉d幊g幋p幍t幎m幏j幐t幑h幒z幓s幖b帼g帻z幙m幚b幜j幝c帜z幠h币b幤b幦m幧q幨c蒙m帮b帱c幭m幮c幰x幱l幵j幷b干g几j庁t仄z広g庈q庉d庌y庍b庎j庒z庘y庛c庝t庡y庢z庣t庤z庨x庩t庪g库k庬m庮y庯b庰b庱c庲l庴j庻s庼q庽y庿m廀s厕c厢x廃f厩j廅e廆h廇l厦s廋s廌z廍b廎q廏j廔l廕y廗d廘l廙y厨c廜t厮s廞x庙m厂a庑w废f广a廤k廥k廦b廧q廪l廫l庐l廭j廮y廯x廰t痈y廲l厅t廵x廸d廹p廻h廼n廽h弆j弇y弉z弌y弍e弎s弑s吊d弖h弙w弚t弜j弝b弡j弢t弣f弤d弨c弫z弬y弮j弰s弲x弪j弴d张z弶j强j弸p弻b弽s弾d弿j彁g彂f彃b彄k彅j彇x弹d彉g彋h弥m彍g弯w彏j彑j彔l彚h彛y彜y彞y彟h彠h彣w彦y彧y彨c雕d彮y彯p彲c彴z彵t彶j彸z彺w彽d彾l佛f徃w徆x徍w徎c徏z径j従c徔c徖c徝z从c徟z徕l徢x徣j徤j徥s徧b复f旁p徯x徰z徱p徲t徳d徶b彻c徺j徻h徾m徿l忀x忁b忂q忈r忊d忋g忎r忓h忔y忕s忚x忛f応y忞m忟m忢w忣j忥x忦j忨w忩c忬y忯q忰c忲t忳d忴q忶h忷x忹w忺x忼k怇j怈y怉b怋m怌p怐j怑b怗t怘h怚c怞y怟d怢t怣y怤f怬x怮y怰x怲b怳h怴x怶b怷s怸x怹t怺y怽m恀s恄x恅l恒h恇k恈m恉z恊x恌y恏h恑g恓x恔x恖s恗h恘q恛h恞y恟x恠g恡l耻c恦s恮z恱y恲p恴d恵h恷q恾m悀y悁j悂p悦y悆y悇t悈j悊z悋l悎h悏q悐t悑b悓q悕x悗m悘y悙h悜c悞w悡l悢l悤c悥y悧l悩n悪e悮w悰c悳d怅c闷m悷l悹g悺g凄q悾k悿t惀l惁x惂k惃g惄n惇d惈g惉z惌y惍j惎j惏l惐y惒h惓q惔t惖t惗n啜c惛h惞x惢s惣z惤j惥y惪d恼n恽y惵d惷c惸q恻c惼b惽m惾z惿t愃x愄w愅g愇w愊b愋x愌h愐m愑y愒q愓d愔y愖c愗m愘k愙k爱a惬q愝y愞n愡c愢s愥y悫q愩g愬s愭q愮y愯s愰h愱j愳j怆c愵n愶x恺k愸z愹y愺c愻x愼s愽b忾k慀x慁h慂y栗l慅s慆t殷y慉x态t愠y慏m慐g慒c慓p慔m慖g慗c惨c惭c慛c慜m慞z恸t慠a慡s惯g慤q慥z慦j慩l怄o怂s慬q慭y虑l慯s慱t慲m悭q慴s庆q慸d慹z慺l慻j戚q慽q欲y慿p憀l憁c忧y憃c憄z憅t憆c憇q憈q惫b憌q憍j憏c怜l凭p愦k憓h憕c憖y憗y憘x憙x惮d憛t憞d憟s憠j憢x憣f愤f憥l憦l憪x悯m憭l怃w憯c憰j憱c宪x憳t憴s憵p忆y憸x憹n憺d憻t憼j憽s懀w懁x懃q懄q懅j懆s恳k应y怿y檩l懎s懐h懓a懕y懖g懗x懘c懙y懚y懛d懜m懝a怼d懠q懢l懑m懤c懥z懧n恹y懩y懪b懫z懬k懭k懮y懯f懰l懱m惩c懳h懴c懒l怀h悬x懹r忏c懻j惧j懽h慑s恋l戁n戂m戃t戄j戅g戆g钺y戓g戋j戙d戜d戝z戞j戠z戣k戦z戗q戨g戬j戫y戭y戯x战z戱x戏h户h戸h戹e戺s戻t戼m扂d扄s扅y扆y扊y扏q仂l払f扖h扗z扙z扚d扜y扝y扞h扟s叉c扡t扦q扤w扥d扨s扱c扲q扴j扵y扷b扸x抵d扻z扽d抁y抂k拚p抅j擦c抇h抈y抋q抌d抍z殒y抏w抐n抔p抙p抜b抝a択z抣y抦b抧z抩n抪p抭y抮z抰y抲h抳n曳y抶c抷p抸j抺m抾q拀z拁j拃z抛p拏n拑q拕t拝b拞d拠j拡k拤q拪q拫h拰n拵c拸y拹x拺c拻h挀b挃z挄k挅d挆d挊n挋z挍j挏d挐n挒l挓z挔l挕d挗j挘l挙j挜y挦x挧y挩t挬b挭g挮t挰c挱s挳k挴m挵n挷b挸j挻s挼r挟x挿c捀p捁j捄j捇h捈t捊p捑z捒s捓y捔j捖w捗b捘z捙y捚z捛l捜s捝t捠b捤w捥w捦q舍s捪m扪m捬f捯d捰w卷j捳y捴z捵c捸t捹b捼r捽z捾w捿q掁c扫s抡l掅q掆g掋d掍h掑q掓s掔q掕l掗y挣z掚l挂g掜y掝h掞y掟z采c掦t掫z掯k掱p掲j掵m掶j掹m掻s掽p掿n拣j揁z揂j揃j揅y揇n揈h揊p揋w揌z揑n揓s揔z揕z揗x揘h揙b扬y换h揜y揝z揟x揢k揤j揥d揦l揧l揨c揫j揬t挥h揯g揰c揱x揳x揵q揷c背b揺y揻w揼b揾w搃z搄g构g搇q搈r搉q搊c损s搎s搑r搒b搕k摇y捣d搘z搙n搚l搝q擀g搢j搣m搤e搥c搧s搨t搩j搫p搯t搰h搱z搲w搳h搵w抢q搷t搸z搹e搻n搼q搾z摂s摃g摉s摋s摌c摍s摎n摏c摐c掴g摓f摕d摗s摙l摚c摛c掼g摝l搂l摠z摡g摢h摣z摤q摥t摦h摨z摪j摫g摬y摮a挚z摰n摱m摲c抠k摴c摵s抟t摷j掺c摼k摽b摿y撀g撁q撃j撆p捞l撉d撊x撋r撌g撍z撎y撏x撑c挠n撔h撗h搭d捻n撛l撜z撝h挢j撠j撡c掸d拨b撦c撨x撪b抚f撯z撱w扑p揿q撴d撶h撹j挞t撽j挝w捡j拥y擃n掳l擆z择z擈p擉c击j挡d擌s擏q擑j擓k担d擕x擖y擙a据j擛y擜e擝m挤j擡t擥l擧j擨y擩r擪y擫y拟n擭h擮j摈b拧n搁g掷z擳z扩k擵m擶j撷x擸l擹t摆b擞s撸l擽l扰r攁y攃c摅s攅z撵n攇x攈j攊l攋l攌h攍y攎l隆l攐q攑q攓q拦l攕x撄y攗m搀c撺c携x摄s攞l攟j攠m攡c攒c挛l摊t攦l攧d攨w攩d搅j揽l攭l攰g攱g攲q攳x考k攼g攽b敀p敁d敂k敃m敄w敆h敇c敊c敋g敍x敎j敐c敒s敓d敔y败b叙x敚d敜n敟d敠d敡y敤k敥y敧q敨t敩x敪d敭y敮x敯m敱a敳a敌d敶c数s敹l敺q敻x敼y敽j敾s敿j斀z斁d敛l毙b斄l斅x斆x斈x斉q斊q斍j斎z斏l斒b斔y斓l斖w斘s斚j斝j斞y斠j斢t斣d斦y斨q斪q斩z斱z斲z斴l斵z斶c断d斸z斺c斻h斾p斿l旀m旗q旇p旈l旉f旊f旍j旐z旑y旓s旔j旕e旘z旙f旚p旛f旜z旝k旞s旟y旡j旣j旪x旫t旲t旳d旴x旵c旸y旹s旻m旼m旽t旾c旿w昁b昄b昅j升s昈h昉f昋g昍x昑q昒h昖y昗z昘f昚s昛j昜y昞b昡x昢p昣z昤l昦h昩m昪b昫x昬h昮z昰s昲f昳d昷w昸d昹a昺b昻a昽l昿k晀t时s晄h晅x晆k晈j晋j晊z晍t晎h晐g晑x晘h晙j晛x晜k昼z曦x晠s晢z晣z晥w晧h晩w晪t晫z晭z晱s晳x晵q晸z晹y晻a晼w晽l晿c暀w暁x暃f暅g暆y晕y晖h暊x暋m暍y暏s暐w暒q暓m暕j阳y暙c暚y暛s暜p暞j暟k暠g暡w畅c暣q暤h暥y暦l暩j暪m暂z暬x暯m暰c昵n暲z暳h暵h暶x暷c了l暺t暻j暼p暽l暿x曀y曁j曂h曃d晔y曅y历l昙t曈t晓x曊f曋s曍h曎y曏x曐x曑s曒j曓b曔j曕y暧a曘r曚m曞l曟c旷k曡d曣y曤h曥l曧r曨l曪l曫l晒s曭t曮y曯z曱y曵y曶h书s曺c曽z朁c朂x会g朄y朅q朇b朎l朏k朒g朓t朖l朘j朙m朚h朜t朞j朠y朡z朢w朣t朤l朥l胧l术s朰t朲r朳b朶d朷d朸l朹q朻j朼b朾c朿c杁r杄q杅y圬w杊x杋f杍z杒r杔t杕d杗m杘c杙y杚g杛g杝t杢j杣m杤w杦j杧m杫s杬y杮f东d杴x杶c杸s杹h杺x杻c杽c枀s枂w枃j枅j枆m枊a枍y枎f枏n枑h枒y枓d枔x枖y栀z枛z枟y枠h枡s枤d枦l枩s枬z枮x枱s枲x拐g枹b枺m枻y枼y枽y枾s枿n柀b柂d柅n柆l柇h柈b柉f柊z柋d柌c柍y柎f柕m柖s柗s柟n柡y柣z柤z柦d柧g柪a柫f柭b柮w柲b栅s柶s柷z柹s柺g査c柼y柾j栁l栂m栃l栄r栆z栍s栐y栒x栔q栕c栘y栙x栚z栛l栜s栞k栟b栠r栢b栣r栤b栥z栦c栧y栨c栫j栬z栮e栯y栰f栱g栴z栵l栶y栺z栻s栿f桇r桋y桍k桏q桒s桖x桘c桙y桚z桛k桝j桞l桟z桪x桬s桭c桮b桯t桰g桱j桲b桳b桵r桸x桹l桺l桻f桼q桽w桾j杆g梀s梂q梄y梇l梈p梉z梊d梋x梌t梎a梐b梑d梒h梕r梖b梘j梙h梚w梛n梜j条t梞j枭x梠l梡k梣c梥s里l梪d梬y梮j梱k梲z梴c梶w梷j梸l梹b梺x梻x梼d梽z梾l梿l棁z弃q棅b棆l棈q棊q棌c棎c棏d棐f棑p棓b棔h枨c枣z棙l棛y棜y棝g棞j栋d棡g棢w棤q棥f棦c栈z棨q棩y棪y棫y棬j棭y棯s栖q棳z棴f棶l棷z棸z棻f棽c棾q棿n椀w椂l椄j椆c椇j椈j椉c椊z椌k桠y椑b椓z椔z椕b椖p椗d椙c椚m椛h検j椝g椞x椡d椢g椣d椥z椦q椧m椨f椩g椪p椫s椬y椮s椯d椱f椲h椳w椵j椶z椷j椸y椺x椻y椼y椾j楀y楁h楃w楄p楅b楆y楇g楈x楉r杨y楋l楌y楍b楎h楏k楐j楑k楒s枫f楘m楙m楛h楜h楟t楢y楤c楥x楧y桢z楩p楪d楬j业y楯d楰y楲w楳m楴d极j楶j楺r楻h楽l楾h楿k榁m榃t榅w榊s榋c榌b榎j榏y榑b榒n榓m榖g榗j榚y榝s榞y榟z榠m榡s榢j榣y榤j榥h榩q杩m榬y荣r榯s榰z榲w榳t榵r榶t榸z榹s榺s榼k榽x榾g桤q槂s盘p槄t槅g槆c槇d槈n槉j枪q槏q槑m槒x杠g槕z槖t様y槙d槚j槜z槝d槞l槡s槢x槣j槤l槥h槦r椠q椁g槩g槪g槫t槬h槮s槯c槰p槱y桨j槴h槵h槶g槷n槹g槺k槻g槼g槾m樀d桩z樃l樄c枞c樆l樇x樈q樉s樋t樌g樍z樎s樏l梁l樒m楼l樔c樕s樖k标b樚l樛l樜z樝z枢s樠m樢n样y樤t樥p樦z樧s権q樫j樬c樭j樮y樰x樲e樳x樴z樶z樷c朴p树s桦h樻g橀x橁c橂d橃f橅m橆w桡r橉l橊l桥q橌x橍r橎f橏j橑l橒y橓s橔t橕c橖t橗m橚s橜j橝t橞h机j橠n椭t橣n橤r橦t橧z橪y横h橬q橭g橮l橯l橰g橲x橳s橴z橵z橶j橷d橸j橺x橻c橽t橾s橿j檃y檅h檆s檇z檈x柽c檊g檋j檌z檍y檏p檒f檓h档d檕j檖s檘p檙c檚c檛z桧g檝j檞j檟j檡z检j樯q檤d檥y檦b檧s檨s檭y檮c台t檰m檱q檲t槟b檴h檵j檶q檷n柠n檹y檺g槛j檼y檽n檿y櫀q櫁m櫂z柜g櫄c櫅j櫆k櫇p櫉c櫊g櫋m櫌y櫍z櫎h櫏q櫑l櫒s橹l櫔l櫕c櫖l櫗m櫘h櫙o榈l栉z櫜g椟d橼y栎l櫠f櫡z櫣l櫤j橱c櫦q槠z栌l櫩y枥l櫫z榇c櫭j櫮e櫯s櫰h櫱n櫲y栊l櫵q櫶x櫷g榉j櫹x棂l樱y櫼j櫽y櫾y欀x欁n栏l欅j欆s欇s欈w欉c权q欋q欌z欍j欎y椤l欐l欑c栾l欓d欔q欕e榄l欗l欘z欙l欚l欛b欜n欝y欥y欨x欩c欪c欫q欬k欭y欮j欯x欰x欱h欳k欴l欵k欶s欸a欻c欼c钦q欿k歀k歁k歂c歄g歅y歈y歊x歋y歍w歏j欧o歑h歒t歓h歔x歕p歖x歗x歘x歚s歛l歜c歝y欤y歠c欢h歨b歩b歫j歭z歮s歯c歰s歱z岁s歳s歴l歵z歶y归g歺d歽z歾m殁m殀y殅s殈x殌j殎q殐s殑j殔y殕f殗y残c殙h殜d殝z殟w殠c殢t殣j殇s殥y殦c殧j殨k殩c殚d殬d殓l殡b殰d殱j歼j殶z殸q殹y杀s殻k壳k殽x殾x毁h毃q毄j殴o毇h毈d毉y毊x毋w毎m毐a毑j毘p毚c毞b毟m毠j毢s毣m毤t毦e毧r毨x毩j球q毭d毰p毱j毲d毴b毶s毷m毸s毺s毻t毼h毾t毵s氀l氁m氂m氃t氄r毡z氉s氊z氋m氇l氎d氒j気q氜y氝n氞n氠s气q氥x氢q氩y氭d氱y氲y氶z氷b氹d氺s氻l氼n泛f氿g汃p汄z汅m汈d汋z汌c丸w汏d汑t汒m汓q汖p汘q污w汚w汢t汣j汥z汧q汫j汬j汭r汮j汯h汱t汳b汵g汷z汸f决j汼n汿x沀x沄y沇y沊d沋y沍h沎h沑n没m冲c沗h沘b沚z沜p沝z沞z沠p沢z沨f沬m沯z沰t沴l沵m沶y沷f沺t泀s况k泂j泃j泆y泇j泋h泍b泎z泏c泑y泒g泙p泚c泜z泝s泟c泤s泦j泧y泩s泬j泭f泲j泴g泹d洀p洃h洅z洆c洈w洉h洊j洍s洏e洐x洑f洓s洔z洕y洖w洘k洝a洟y洠s洡l洢y洣m洤q洦p洨x泄x洬s洭k洯q洰j洴p汹x洷z洸g洺m洿w浀q浂y浄j浉s浌p浐c浕j浖l浗q浘w浛h浝m浟y浡b浢d浤h浥y浧y浨l浫h浭g浱c浳y浵t浶l浃j浺c浻j浽s浾c浿p涀x涁s涃k涄p涆h泾j涊n涋t涍x涏t涐e涒t涗s涘s涙l涚s涢y涥h涬x涭s涰c涱z涳n涴w涷d涹w涺j涻s凉l涽h涾t淁q淂d淈g淉g淊y淍z淎p淏h淐c淓f淔z淕l淗j泪l淛z淜p淟t淢y淣n渌l淧m淩l沦l淭q淯y淰n淲b淴h渊y涞l淸q浅j淽z淾y淿b渀b渁y渂w渃r渄f渆y渇k済j渉s渋s渏y渒p渓x渕y渘r涣h减j渞q渟t渢f涡g渧t渨w渪r测c渮h渰y渱h渳m渵m渶y渷y渹q渻s渼m渽z浑h渿n湀g湁c湂e湅l湆q湇q湈m湉t凑c湋w湌c湏h湐p湑x湒j湗f湙y湚y湜s湝j浈z湠t湡y湢b湣m湤s湥t湦s涌c湨j湩d湪t湬q湭q汤s湰l湱h湳n湴b湵y湶q湷z湸l湹c湺x湻c湼n湽z満m溁y溂l溄f溇l沩w溊b溋y溌h溍j溎y溑s溒y溓l溔y溕m准z溗c溙t溚t溛w沟g溞s溠z溡s溣l溤m溦w溨z溩w温w溬q溭z溮s溰a溵y溸s溹s湿s溾a溿p滀c滃w沧c灭m滆g滈h滉h滊q涤d滍z荥x滐j滒g滘j滙h滛y滝l滣c滧y滪y滫x沪h滭b滮b滞z滰j滱k渗s滳s滶a卤l浒h滺y滻c滼f滽y滚g满m漀q渔y漃j漄y漅c漇x漈j漊l漋l漌j漍g漎c漑g漒q漖j漗c漘c漙t沤o漛t漜y漝x漞m漟t漡s汉h涟l漥w漦c漧g漨f渍z漮k漰p涨z漴c漵x漷h渐j漺s漻l漼c漽t浆j潀c潂h潃x潄s潅g潈z潉k潊x潌z潏y潐j泼p潒d潓h洁j潕w潖p潗j潙w潚s潜q潝x泻x潠s潡d润r潥s潧z潨c潩y潪z潫w潬t浔x溃k潱y潳t潵s潶h滗b潹c潻s潽p潾l涠w涩s澁s澂c澃j澅h浇a涝l澊c澋j澏h澐y澑l澒h澓f澔h澕h澖x涧j澘s澙x澛l澝n澞y澟l渑m澢d澣h泽d澥x澦y澨s泶x澪l澫w澬z澭y浍k澯c澰l淀d澲y澴h澷m澸g澺y澻s澼p澽j澾t澿q浊z浓n濄g濅j濆p濇s濈j濊h濋c濌t濍s濎d濏s濐z濓l濔m濖s濗m泞n濙y濚y濜j濝q济j濢c濣w涛t濥y濦y濧d濨c濩h濪q滥l濬j濭a潍w滨b濲g濳q濴y濶k濷f濹b溅j濻w泺p濽z滤l濿l瀁y瀂l瀃s瀄z滢y渎d瀈h瀊p渖s瀌b瀍c瀎m浏l瀒s瀓c瀔g濒b瀖h瀗x泸l瀙q瀜r沥l瀞j潇x潆y瀡s瀢w瀤h瀥x瀦z泷l濑l瀩d瀪f瀫h瀬l瀭s瀮l瀯y瀱j潋l瀳j瀴y瀶l瀷y瀸j瀺c瀻d瀼r瀽j澜l瀿f灀s灁y灂z沣f滠s灅l灆l灇c灈q灉y灊q灋f灍j灎y灐y洒s灒z灓l灔y灖m灗s滩t灙d灚j灛c灏h灟z灠l灡l灢n湾w滦l灥x灦x灧y灨g灪y灮g灱x灲x灳h灴h灷z灹z灺x灻c灾z炁q炂z炄n炆w炇p炈y炋p炌k炐p炑m炘x炚g炛g炞b炟d炠x炡z炢z炣k炤z炥f炦b炧x炨x炩l炪z炰p炴y炵t炶s为w炿z烄j烅x烆h烇q烉h烋x烌x烍x烎y乌w烐z烑y烒s烓w烔t烕m烖z烗k烚x烜x烝z烠h烡g烢c烣h烥c烰f烱j烲x烳p烃t烵z烶t烸h烺l烻y烼x烾c烿r焀h焁x焂s焃h焄x焅k焆j焇x焈x焋z焌j焍d焎x焏j焒l焔y焗j焛l焜k焝h焞t焟x焠c无m焢h焣j焤f焥w焧z焨f焩p焪q焫r焬x焮x焳j焴y焵g焸x焹g焻c焼s焽x焾n焿g煀q煁c煂h煃k煄z煆x煇h煈f炼l煋x煍j煏b煐y煑z炜w煓t煔s煕x煖n煗n煘c烟y煚j煛j煝m煟w煠y煡j茕q煣r焕h烦f煪q煫s炀y煭l煯j煰z煱g煴y煵n煶s煷h煹g煻t煼c煾e煿b熀h熁x熂x熃w熅y熆h熇h熈x熉y熋x熌s熍q熎y熐m熑l荧y熓w熕g熖y炝q熚b熛b熜c熝l熞j熡l熢p熣s熤y熥t熦j熧z熩h熪y熫z熭w熮l熯h熰o热r熲j熴k熶c熷z熸j熺x熻x熼y熽x炽c熿h燀c烨y燂t焰y燅x燆q燇j灯d炖d燊s燋q燍s燏y磷l燑t烧s燖x燗l燘m烫t燚y燛j焖m燝z营y燡y燣l燤t灿c燨x燩q燪z燫l烛z燯l燱y燲x燳z烩h燵t燶n燷l燸x燺h燻x烬j燽c焘d燿y爀h爂b爃r爄l爅m爇r爈l爉l爊a爋x爌k烁s爎l爏l炉l爑j爒l爓y爔x爕x爖l爘c爚y烂l爜c爞c爟g爡c爢m爣t爤l爦l爧l爩y爫z争z爮p爯c爲w爳h爴j爷y爼z尔e牀c牁k牂z牃d牄q牅y牉p牊c牋j牎c牏y牐z牑b牓b牕c牗y牍d牚c牜n牞j它t牣r牤m牥f牨g瘪b牫g牬b牭s牰y牱k牳m牶q牷q牸z牻m牼k牵q犂l犃p犅g犆z犇b犈q犉c犌j犎f犐k犑j犓c犔x犕b荦l犗j犘m犙s犚w犁l犜d犝t犞q犠x犡l犊d犣l犤b犥p犦b牺x犨c犩w犪k犫c犮q犱j犲c犳z犵g犺k犻p犼h犽y犾y犿h状z狅k狆z狇m狉p狊j狋y狌s狏t狑l狓p狕y狖y狘x狛b狜k狝x狟h狢h狣z狤j狥x狦s狧t狪t狫l狵m狶x狭x狈b狾z狿y猀s猂h猄j猅p猆f猇x猈b猉q猋b猌y猐q猑k猒y猔z猘z狰z猚y猟l猠c猣z猤g猦f猧w猨y猭c猯t猰y猲h猳j猵p犹y猺y狲s猼b獀s獁m獂h獃d狱y狮s獆h獇q獈y獊c獋h獌m奖j獏m獑c獓a獔h獕s獘b獙b獚h獛p獜l獝x獞t獟y獡s獢x獣s獤d獥j獦g獧j独d獩h狯k猃x猕m狞n獱b获h獳r獴m猎l獶n犷g兽s獭t献x獽r獿n猡l玁x玂q玃j玅m兹c玈l玊s玌q玍g玏l玐b玒h玓d玔c玕g玗y玘q玙y玚c玜h玝w玞f玠j玡y玣b玤b玥y玦j玧m珏j玪j玬d玭p玱q玴y玵a玶p玸f玹x玼c玽g玾j玿s珁c珃r珄s珅s珆y珇z珋l珌b珎z珒j珓j珔j珕l珖g珗x珘z珚y珛x珜y珝x珟s珡q珢y珣x珤b珦x珨x珪g珫c珬x佩p珯l珰d珱y珳w珴e珵c珶d珷w珸w珹c珺j珻m珼b珽t现x珿c琀h琁x琂y琄x琇x琈f琋x琌l琍l琎j琑s琒f琓w琔d琕p琖z琗c琘m琙y琜l琝m琞s琟w琠t琡s琣b琤c琧e琩c琫b琭l琯g琱d琲b琷q琸z琹q珐f琻j琽d琾j珲h瑀y瑂m瑃c瑄x瑅t瑆x瑇d瑈r瑉m玮w瑌r瑍h瑎x瑏c瑐j瑑z瑒y瑓l瑔q瑖d瑝h瑠l瑡s瑢r琐s瑶y瑥w瑧z瑨j莹y玛m瑫t瑬l瑮l琅l瑱t瑲q瑳c瑴j瑵z瑹t瑺c瑻k瑼z瑽c瑿y璂q璄j璅s璆q璈a琏l璌y璍y璏z璑w璒d璓x璔z璕x璖q璗d璘l璙l璚j璛s璝g璟j璡j璢l玑j璤h璥j瑷a璪z璫d璬j璭g璮t璯h环h璱s璲s璳t璴c璵y璶j璷l璸b璹s璻z璼l玺x璾j璿x瓀r瓁w瓂g瓃l瓄d瓅l瓆z瓇r瓈l瓉z琼q瓋t瓌g瓍s瓎l珑l瓐l瓑l瓓l璎y瓕m镶x瓗q瓘g瓙d瓒z瓛h瓝b瓟b瓡z瓥l瓨x瓩q瓪b瓫p瓬f瓭d瓳h瓵y瓸b瓹j瓺c瓻c瓽d瓾w甀z甁p甂b甃z甅l甆c甈q甉x甊l甋d瓯o甎z甐l甒w甔d瓮w甖y甗y甛t甝h甞c甠q甡s产c産c甤r甧s甪l甮f甴y甶f甹p甼t甽z甿m畁b畂m畃x畆m畇y畉f畊g畍j畐f畑t畒m畓d畕j畖w畗d畘n亩m畟j畠t畡g毕b畣d畤z畨f畩y画h畲s畭s畮m畯j异y畱l畳d畵h畷z畺j畻c畽t畾l疀c疁l疂d疄l疅j畴c疈p疉d叠d疌j疍d疎s疐z疓n疕b疘g疛z疞x疢c疦j疧q疨y疩c疭z疶x疷z疺f疻z痱f痀g痁d痆n痋t痌t痎j痏w痐h痑t痓z痗m痉j痚x痜t痝m酸s痡f痥d痩s痬y痮z痯g麻m痵j痶t痷a痸c痹b痻m痽d痾k瘂y瘄c瘆s瘇z瘈z瘉y疯f疡y瘎c瘏t瘑g瘒w痪h瘔k瘖y瘚j瘜x瘝g瘗y疮c瘣h疟n瘨d瘬z瘮s瘯c瘱y瘲z瘶s瘷s瘘l瘻l瘽q疗l癄q癅l痨l痫x癈f瘅d癋h癎x癏g癐g癑n癒y癓w癕y癗l疠l癙s癚d癛l癝l癠j痴c痒y疖j症z癧l癨h癞l癪j癣x瘿y瘾y瘫t癫d癳l癴l癵l癶b癹b発f发f癿q皀b皁z皃m皅p皉c皊l皌m皍j皏p皐g皒e皔h皕b皗c皘q皑a皛x皜h皝h皞h皟z皠c皡h皢x皣y皥h皦j皧a皨x皩h皪l皫p皬h皭j皯g疱p皳q皵q皶z皷g皲j皹j皱z隳h皼g皽z皾d盀q盁y盃b啊a阿a埃a挨a哎a哀a癌a蔼a矮a艾a碍a隘a鞍a氨a安a俺a按a暗a岸a胺a案a肮a昂a盎a凹a敖a熬a翱a袄a傲a懊a澳a芭b捌b扒b叭b吧b笆b八b疤b巴b拔b跋b靶b把b耙b霸b罢b爸b白b柏b百b佰b拜b稗b斑b班b搬b扳b般b颁b板b版b扮b拌b伴b瓣b半b办b绊b邦b梆b榜b膀b绑b棒b磅b蚌b镑b傍b谤b苞b胞b包b褒b盄d盇h盉h盋b盌w盓y盕f盙f盚q盗d盝l盏z盠l盢x监j盦a卢l盨x盩z荡d盫a盬g盭l盰g盳m盵q盷x盺x盻x盽f盿m眀m眂s眃y眅p眆f眊m県x眎s眏y眐z眑y眒s眓h眔d眕z眖k眗j眘s眛m眜m眝z眞z眡s眤n眦z眧c眪b眫p眬l眮t眰d眱d眲n眳m眴s眹z眻y眽m众z眿m睂m睄s睅h睆h睈c睉c睊j睋e睌m睍x睎x困k睒s睓t睔g睕w睖l睗s睘q睙l睁z薄b雹b保b堡b饱b抱b暴b豹b鲍b爆b杯b碑b悲b卑b北b辈b贝b钡b倍b焙b被b奔b本b笨b崩b绷b甭b泵b蹦b迸b逼b鼻b比b鄙b笔b彼b碧b蓖b蔽b毖b庇b闭b敝b弊b必b辟b壁b臂b避b陛b鞭b边b编b贬b扁b便b变b卞b辨b辩b辫b遍b彪b膘b表b鳖b憋b彬b斌b宾b兵b冰b柄b丙b秉b饼b炳b睝l睐l睟s睠j睤b睧h睩l睾g睭z睮y睯h睰m睱x睲x睳h睴h睵z睶c睷j睸m睺h睻x睼t瞁x瞂f瞃w瞆g眯m瞉k瞊d瞋c瞐m瞕z瞖y瞗d瞘k瞙m瞚s瞛c瞜l瞝c瞒m瞡g瞣w瞤s瞦x瞨p瞫s瞮c瞯x瞱y瞲x瞴w瞶g瞷j瞸y瞹a瞺h睑j瞾z矀w矁c矂s矃n矄x矅y矆h矈m矉p矋l矌k矎x矏m矐h矑l矒m矔g矕m矖x矘t矙k瞩z矝j矞y矟s矠z矡j矤s病b玻b菠b播b钵b波b博b勃b搏b铂b箔b伯b帛b舶b脖b膊b渤b泊b驳b捕b卜b哺b补b埠b不b步b簿b部b怖b猜c裁c材c才c财c睬c踩c彩c菜c蔡c餐c蚕c苍c舱c藏c操c糙c槽c曹c草c策c蹭c插c茬c茶c查c碴c搽c察c岔c差c诧c拆c柴c豺c蝉c馋c谗c缠c阐c颤c昌c猖c矦h矨y矪z矫j矰z矱y矴d矵q矷z矹w矺z矻k矼g砃d砄j砅l砆f砇m砈e砊k砋z砎j砏p砐e砓z砕s砙w砛j砞m砠j砡y砢l砤t砨e砪m砫z砮n砯p砱l炮b砳l砵b砶p砽y砿k硁k硂q朱z硄k硆e硈q硉l硊w硋a硍k硏y硑p硓l硔h硘q硙w硚q硛c硜k硞q硟c硠l硣x硖x硥m硦l硧y砗c硩c砚y硰s硱k硲y硳z硴h硵l硶c硹s硺z硻k硽y硾z硿k碀c碁q碂z碃q常c长c肠c敞c唱c倡c超c抄c钞c朝c嘲c潮c巢c吵c炒c车c扯c撤c掣c澈c郴c臣c辰c晨c忱c沉c陈c趁c衬c称c城c橙c成c呈c乘c程c澄c诚c承c逞c骋c秤c持c匙c池c迟c弛c驰c齿c侈c赤c翅c斥c充c虫c崇c抽c酬c踌c稠c愁c筹c仇c绸c瞅c丑c臭c初c出c躇c锄c雏c滁c除c楚c碄l碅j碆b碈m碊j碋h碏x碒y碔w碕q碖l碙n碝r碞y碠d碢t碤y碦k碨w硕s碪z碫d碬x砀d碮t碯n碵t碶q碷d碸f确q码m碿s磀e磃s磄t磆h磇p磈k磌t磍x磎x磏l磑w磒y磓d磖l磗z磘y砖z磛c磜q磝a磞p磟l磠l磡k磢c碜c磤y磥l磦b碛q磩z磪c磫z磮l矶j磱l磳z磵j磶x磸d磹d磻b磼j硗q磾d磿l礀j礂x礄q礆j礇y礈z礉h礊k礋z礌l础c矗c搐c触c处c揣c川c穿c椽c船c喘c串c窗c幢c床c闯c吹c炊c捶c锤c垂c春c椿c醇c唇c淳c纯c蠢c戳c绰c疵c茨c磁c雌c辞c慈c瓷c词c此c刺c赐c次c聪c葱c匆c粗c醋c簇c促c蹿c篡c窜c摧c崔c催c脆c瘁c粹c淬c翠c村c存c磋c撮c搓c措c挫c错c达d答d瘩d打d大d呆a歹d傣d戴d殆d代d贷d袋d待d逮d礍j礏y礐q礑d礒y礔p礕p礖y礗p礘e礚k礛j礜y礝r礟p礠c礡b礢y礣m礥x矿k礧l礨l礩z砺l砾l矾f礭q礮p礯y礰l砻l礲l礳m礵s礷j礸c礹y礽r礿y祂t祃m祄x祆x只z祊b祋d祌z祎y祏s佑y祑z祒t祔f秘b祙m祡c祣l祤y祦w祩z祪g祫x祬z祮g祰g祲j祳s祴g祵k祹t祻g祼g祽z祾l禄l禂d禃z禆b禇c禉y禋y禌z祸h禐y禑x禒x怠d耽d丹d郸d胆d旦d氮d但d淡d诞d蛋d党d刀d蹈d倒d祷d到d稻d悼d道d德d得d的d蹬d登d等d瞪d凳d邓d堤d低d滴d迪d笛d狄d翟d嫡d底d地d蒂d第d帝d弟d递d缔d颠d掂d滇d碘d点d典d靛d电d佃d甸d店d惦d奠d殿d碉d叼d凋d刁d掉d钓d调d跌d爹d碟d蝶d迭d谍d禓s禔z禕y禖m禗s禘d禙b禛z禜y禝j禞g禟t禠s禡m禢t禣f禤x禥q御y禨j禩s禅c禫d禬g禭s礼l禯n祢m禲l禴y禵t禶z禷l禸r禼x秃t秂r秄z秅c秇y籼x秊n秌q秏h秐y秓z秔j秖z秗y秙k秚b秛p秜n秝l秞y秠p秡b秥n秨z秪d秬j秮h秱t秲s秳h秴h秵y秶z秷z秹r秺d秼z秾n秿f稁g稄x税s稇k秆g稉j稊t稌s稏y稐l稑l稒g稓z稕z稖b稘j稙z稛k棱l丁d盯d叮d钉d顶d鼎d锭d定d订d冬d董d懂d侗d恫d洞d兜d抖d陡d豆d逗d痘d都d督d毒d读d堵d睹d赌d杜d镀d肚d度d渡d妒d端d短d锻d段d缎d堆d队d墩d蹲c敦d顿d囤d钝d盾d遁d掇d哆d多d垛d躲d朵d跺d舵d剁d惰d蛾e峨e鹅e俄e额e讹e娥e厄e扼e遏e鄂e饿e恩e而e耳e饵e洱e二e稝p禀b稡z稢y稤l稦y稧x稨b稩j稪f稫p稭j种c稯z稰x稲d稴x稵z稶y稺z稾g谷g穁r穂s穃r穄j穅k穇c穈m穊j穋l稣s颖y穏w穐q穒k穓y穔h穕q穖j穘x穚j穛z穜t穝z穞l穟s穑s秽h穣r穤n穥y穦p穧j穨t稳w穪c穬k穭l穮b穯s穱z穲l穳c穵w穻y穼s穽j穾y窂l窅y窇b窉b窊w窋z窌j窎d窏w窐w窓c窔y窙x窚c窛k窞d窡z窢h贰e罚f筏f伐f乏f阀f法f藩f帆f番f翻f樊f钒f繁f凡f反f返f范f贩f犯f饭f芳f方f肪f房f防f妨f访f纺f放f菲f非f啡f飞f肥f匪f诽f吠f肺f沸f费f芬f酚f吩f氛f分f纷f焚f汾f粉f份f忿f粪f丰f封f蜂f峰f锋f风f烽f逢f冯f缝f讽f奉f凤f否f敷f肤f孵f扶f拂b辐f幅f氟f符f伏f俘f服f窣s窤g窝w洼w窫y穷q窑y窰y窱t窲c窴t窵d窭j窷l窸x窹w窥k窻c窼c窾k竀c竁c竂l竃z窍q竆q窦d竈z竉l窃q竍s竎f竏q竐c竑h竒q竓h竔s竗m竘q竚z竛l竜l竝b竡b竢s竤h竧j竩y竫j竬q竮p竰l竱z竲c竳d竴c竵w竞j竷k竻l竼p竾c笀m笁z笂w笅j笇s笉q笌y笍z笎y笐h笒c笓b笖y笗d笘s笚d笜z笝n笟g笡q笢m笣b笧c笩f笭l浮f涪f福f袱f弗f甫f辅f俯f釜f斧f脯f腑f府f腐f赴f副f覆f赋f傅f付f阜f父f腹f负f富f讣f附f缚f咐f噶g嘎g该g改g概g钙g盖g溉g甘g柑g竿g肝g赶g感g敢g赣g钢g缸g肛g纲g港g篙g皋g高g膏g羔g糕g搞g镐g稿g哥g歌g戈g鸽g胳g疙g割g革g葛g格g蛤g阁g隔g铬g各g给g根g跟g耕g更g庚g羹g笯n笰f笲f笴g笵f笶s笷m笹t筇q笽m笿l筀g筁q筂c筃y筄y筈k筊j笋s筎r筓j筕h筗z筙l筜d筞c筟f筡t筣l筤l筥j筦g笕j筨h筩t筪x筫z筬c筭s筯z筰z筶g筸g筺k筼y筽o筿x箁p箂l箃z箅b箆b箉g箊y笺j箌z箎c筝z箑s箒z箓l箖l箘q箙f箚z箛g箞q箟j箠c箣c箤z箥p箮x箯b箰s箲x箳p箵x箶h箷s箹y箺c箻l箼w箽d箾s箿j节j篂x篃m埂g耿g梗g工g攻g功g恭g龚g供g躬g公g弓g巩g汞g拱g贡g共g钩g勾g苟g狗g垢g购g辜g菇g咕g箍g估g沽g孤g姑g鼓g古g蛊g骨g股g故g顾g固g刮g瓜g寡g褂g乖g怪g棺g关g官g冠g观g管g馆g罐g灌g贯g光g逛g瑰g规g圭g硅g闺g轨g鬼g诡g癸g桂g跪g贵g辊g棍g锅g郭g果g裹g过g哈h篅c篈f筑z篊h箧q篍q篎m篏q篐g篔y篕h篖t篗y篘c篛r篜z篞n篟q筱x篢l篣p笃d篧z篨c筛s篫z篬q篭l篯j篰b篲h筚b篴z篵c篶y篸c篹s篺p篽y篿t箦z簁s簂g簃y簄h簅c簆k簈p簉z簊j篓l簎c簐n蓑s簒c簓s簔s簕l簗z簘x簙b簚m簛s簜d簝l箪d簠f简j簢m篑k簤d簥j簨z簩l箫x簬l簭s簮z簯q簰p簱q簲p簳g簴j簵l簶l檐y簹d簺s簻z簼g签q帘l籂s骸h孩h海h氦h亥h害h骇h酣h憨h邯h韩h涵h寒h函h喊h罕h翰h撼h捍h旱h憾h悍h焊h汗h夯b杭h航h壕h嚎h豪h毫b郝h好h耗h号h浩h呵h喝h荷h菏h核h禾h和h何h合g盒h貉h阂h河h涸h赫h褐h鹤h贺h嘿h黑h痕h很h狠h恨h亨h衡h轰h哄h烘h虹h鸿h洪h宏h弘h红g喉h侯h猴h吼h厚h候h后h呼h乎h忽h瑚h葫h胡h蝴h狐h糊h湖h篮l籄k籅y籆y籇h籈z籉t籊t籎y籏q藤t籑z籒z籓f籔s籕z籖q籗z籘t籙l籚l籛j箨t籝y籞y籁l笼l籡s籢l籣l龠y仠g籦z籧j籨l笾b簖d籫z篱l籭s箩l籯y籰y籱z吁x籵f籶s籷z籸s籹n籺h籾n籿c粀z粁q粂z粃b粄b粅w粆s粇k粈r粊b粋c粌y粍z佂z粏t粐h佹g侀x粔j粖y粙z粚c粛s粠h侼b侽n粣c粦l粨b粩l粫e粬q粭h粯x粰f粴l粤y粶l粷j粸q粺b粻z弧h虎h唬h护h互h花h华h猾h滑h化h话h槐h淮h桓h还h缓h患h豢h宦h幻h荒h慌h黄h磺h蝗h簧h皇h凰h惶h煌h晃h幌h恍h谎h灰h辉h徽h恢h蛔h回h悔h慧h卉h惠h晦h贿h讳h诲h绘h荤h昏h婚h魂h混h豁h活h伙h火h或h惑h霍h货h圾j基j畸j稽j箕j粿g糀h倄y糃t糄b糆m糉z糋j糎l糏x糐f糑n糒b糓g糔x糘j糚z糁s糡j馍m糣s糤s糥n糦x粮l糩k糪b糫h糬s糭z糮x糱n粝l糳z籴d糵n粜t糷l糹s糺j糼g糽z纠j纪j紁c纣z紃x约y纡y纥g纨w纫r纹w紌q纳n紎z紏t纽n紑f紒j纾s纰p紖z纱s紘h纸z级j纭y紝r紞d紟j紣c紥z紦h紨f紩z紪q紬c紭h扎z细x绂f绁x绅s紴b紵z紶q偟h肌j饥j迹j激j讥j鸡j姬j绩j缉j吉j棘j辑j籍j集j及j急j疾j即j嫉j脊j己j蓟j技j偤y冀j季j伎j祭j悸j寄j寂j计j记j既j忌j际j妓j继j嘉j枷j佳j加j荚j颊j贾g甲j钾j假j稼j架j驾j嫁j尖j间j煎j兼j肩j艰j缄j茧j柬j硷j剪j荐j鉴j践j贱j见j键j箭j件j紷l紸z绍s绀g紻y绋f紽t紾z绐d绌c絁s终z弦x组z絅j絇q絈m絉s絊z絋k経j絍r绗h絏x傶q结j絑z絒c傸c絓g絔b绝j絖k絗h絘c絙h絚g绦t絜j絝k绞j絟q絠g络l绚x絣p絤x絥f絧t绒r絩t絪y絫l絬x絭j絯g絰d统t丝s絴x絵h絶j絸j绢j絺c絻w絼z絽l絿q綀s綂t绡x綄h綅q绠g綇x绨t綉x綊x綋h綌x綍f綎t绥s綐d捆k綒f经j綔h綕z綖y綗j綘f健j舰j饯j建j僵j姜j江j疆j蒋j讲j匠j酱j降j蕉j椒j礁j焦j胶j交j郊j骄j嚼j铰j脚j狡j角j饺j缴j教j酵j轿j较j叫j窖j揭j接j皆j秸j街j阶j截j劫j桔j捷j睫j竭j姐j戒j藉j芥g界j借j介j疥j诫j巾j筋j斤j金j兠d今j津j襟j紧j锦j谨j进j靳j禁j近j浸j継j続x综z綝c缍d綟l绿l綡j绻q綤s綧z綨j綩w綪q綫x绶s维w綯t绾w网w綳b缀z綶g綷c纶g绺l绮q绽z綼b绫l绵m緀q緁j緂t緃z绲g緅z緆x缁z緈x緉l绯f緌r緍m緎y総z緐f緑l绪x緓y緔s緕z緖x缃x缂k线x緜m緟c缗m緢m缘y緤x緥b缌s緧q緪g緫z缅m緭w緮f纬w緰x缑g缈m緳x练l緵z缏b緷g緸y缇t緺g荆j兢j茎j睛j晶j鲸j京j惊j精j粳j井j警j景j颈g静j境j敬j镜j靖j竟j炯j窘j揪j究j玖j韭j久j灸j九j酒j救j旧j臼j舅j咎j就j疚j鞠j拘j狙j疽j居j驹j菊j咀j矩j举j沮j聚j拒j巨j具j距j剏c踞j锯j俱j句g炬j捐j鹃j娟j倦j眷j撅j攫j抉j掘j倔j爵j觉j诀j均j菌j钧j军j君j峻j致z緼w緽c緾c緿d縀x縁y縂z縃x縄s縅o縆g縇s萦y缙j缢y缒z縌n縍b縎g縏p绉z缣j縒c縓q縔s縕y縖x縗c縘x縙r縜y缜z缟g缛r縠h縡z縢t县x縤s縥z縦z縧t縨h縩c縪b縬c缡l缩s演y縰x纵z缧l纤q缦m絷z缕l縸m缥p縺l縼x总z縿s繀s繂l繄y缫s缪m襁q繉s繊x繋j繌z繍x繎r繐s缯z繓z织z缮s繖s繗l繘j繙f缭l繛c繜z勩y俊j竣j浚j郡j骏j喀k咖g卡k咯g开k揩k楷j慨k刊k堪k勘k坎k砍k看k康k慷k糠k扛g抗k亢g炕k拷k烤k靠k坷k苛k柯k棵k磕k颗k科k咳h可k渴k刻k客k课k肯k啃k坑k吭h空k恐k孔k控k口k扣k寇k枯k哭k窟k苦k酷k裤k夸k垮k挎k跨k胯k筷k快k款k筐k狂k框k眶k亏k盔k葵k奎k魁k傀g绕r繟c繠r绣x缋h繣h繤z繥x繦q繧w繨d绳s繬s繮j缳h缲q繱c繲x繴b繵d繶y繷n繸s绎y繺s繻x缤b缱q繿l纀p纁x纃z纅y纆m纇l缬x纉z纩k续x累l纎x纐k纑l纒c缨y厠c纗z缵z纙l缆l纝l纞l纮h纴r纻z纼z绖d绤x绬y绹t缊w缐x缞c缷x缹f缻f缼q缾p罀z罁g罃y罆g罇z罉c罊q罋w罂y罍l罎t罏l罒w罓w馈k愧k坤k昆k括g廓k阔k垃l拉l喇l蜡l腊l辣l啦l莱l赖l蓝l婪l阑l兰l谰l览l榔l狼l廊l郎l朗l浪l牢l老l佬l姥l酪l烙l勒l雷l镭l蕾l磊l儡l肋l类l楞l冷l厘l梨l黎l狸l离l理l李l鲤l莉l荔l吏l丽l利l傈l例l俐l罙s罛g罜z罝j罞m罠m罣g罤t罥j罦f罧s罫g罬z罭y罯a罳s骂m罶l罸f罺c呩s罻w罼b罽j罿c羀l羂j羃m羄z罴p羇j羁j羉l羍d羏y羐l羑y羓b羕y羖g羗q羙m羠y羢r羟q羡x义y羬x羱y羴s羺n羾h翀c翃h翆c翇f翈x翉b翋l翍p翏l翐z翑q习x翓x翖x翗k翙h翚h翛x翜s翝h翞j翢d翣s哷l痢l立l粒l隶l力l璃l哩l联l莲l连l镰l廉l脸l链i啅z粱l良l辆l量l晾l亮l谅l撩l聊l僚l燎l寥l辽l潦l撂l镣l廖l料l列l烈l劣l琳l林l霖l临l邻l鳞l淋l赁l啙z啛c吝l拎l玲l菱l零l龄l铃l伶l羚l凌l灵l陵l领l另l令l溜l琉l榴l硫l馏l留l瘤l流l柳l六l龙l聋l窿l翤c翧x翨c翪z翫w翬h翯h翲p翴l翵h翶a翷l翸p翘q翽h翾x翿d耂l耇g耈g耉g耊d耎n耏n耓t耚p嗘j耝q耞j耟j耡c耣l耤j耫z耧l耭j耮l耯h耰y耲h耴y耹q耺y耼d耾h聀z聄z聅c聇z聈y聉w聏e聐y聑t聓x聕h圣s聗l聙j聛b聜d聝g闻w聟x聠p聡c聢x聣n聤t聥j聦c聧k聨l聫l聬w聭k聮l声s耸s聴t聩k聂n职z聸d聍n聺q聻n嘓g聼t听t拢l陇l漏l陋l芦l颅l虏l鲁l麓l碌l露l路l赂l鹿l潞l录l陆l驴l铝l旅l履l氯l律l率l卵l掠l略l轮l噵d论l萝l螺l逻l锣l骡l裸g落l洛l嚍j骆l嚒m蚂m马m嘛m埋m买m麦m卖m迈m脉m馒m蛮m蔓m曼m慢m漫m肁z肂s肃s肈z肊y肍q肎k肏c肐g肑d囄l肔c肕r肗r肙y肞c肣q肦b肧p肨p肬y肰r肳w肵q肶p肹x肻k胅d胇b胈b胉b胊q坺f垀h胏z胐f胒n胓p胔z胕f胘x胟m胠q胢q胣c胦y胮p胵c胷x胹e埱c埶y胾z胿g脀z脁t脃c脄m胁x脇x脋x脌n脕w脗w脙x胫j脜r脝h脟l脠s脡t脢m脤s脥j塈x睃s脨c修x脪x脱t脭c脮n脰d脳n脴p脵g脷l胀z脼l脽s脿b谩m芒m墽q茫m盲m氓m壔d忙m莽m猫m茅m锚m毛m矛m铆m卯m茂m冒m帽m貌m贸m么m玫m枚m梅m酶m霉m煤m眉m媒m镁m每m美m昧m寐m妹m媚m门m萌m檬m盟m锰m猛m孟m醚m靡m糜m迷m谜m米m觅m泌b蜜m密m棉m眠m冕m免m勉m娩m面m苗m妌j描m瞄m藐m秒m妛c妭b渺m妙m蔑m民m抿m皿m敏m姃z闽m明m螟m鸣m铭m名m命m谬m摸m腀l腁p腂g腃j腄c腅d腇n腉n腍r肾s腏z腒j腖d腗p腘g腛w腜m腝r腞z娊x脶l腢o腣d腤a脑n腨s腪y肿z腬r腯t腲w腵j婂m腶d腷b膁q腽w膄s膅t膆s膇z膉y膋l膌j膍p膎x膐l膒o膓c膔l膕g膖p膗c膙j膞z膟l膡y膢l膤x膥c膧t腻n膫l膬c膭k膮x媑z膯t膰f膱z膲j膴h媝q媡l膵c膶r膷x膸s膼z脍k脓n臄j臅c臇j臈l臋t脐q臎c膑b臐x臑n嫿h嬃x嬓j臓z摹m蘑m模m嬜x膜m磨m摩m嬟y魔m抹m末m莫m墨m默m沫m漠m寞m陌m谋m牟m某m拇m牡m姆m母m墓m暮m幕m募m慕m木m目m孨z睦m牧m穆m拿n哪n孶z钠n那n娜n氖n乃n奶n耐n奈n南n男n难n囊n闹n淖n呢n馁n嫩n能n妮n霓n倪n泥n尼n匿n逆n溺n蔫n拈n年n碾n酿n鸟d尿n捏n孽n镊n镍n涅n您n凝n臔x臕b臖x臗k臙y胪l臛h臜z臝l臞q脏z脔l臡n臢z卧w臦g臩g臫j臮j臯g臰c岅b臲n臵g臶j臷d臸z臹x岟y臽x臿c舃x与y兴x岪f岾z峊f舎s舏j舑t峣y舓s舕t铺p舗p峮q舘g舙h舚t舝x舠d舤f舥p舦t舧f舩c舮l舲l舼q峾y舽p艀f艁z艂f艃l艅y艆l艊b艌n艍j艎h艐k艑b艒m艓d艔d嵽d嶈q艖c艗y艛l艜d艝s艞y艠d艡d艢q舣y艥j艧h艩q牛m扭n钮n嶪y农n弄l奴n努n怒n女n暖n虐n挪n懦n糯n诺n哦e巆y鸥o藕o偶o巏q啪p趴p爬p帕p怕p琶p拍p排p牌p徘p湃p派p攀p潘f磐p盼p畔p判p叛p乓p庞p耪p胖p咆p刨b袍p跑p泡p呸p胚p培p裴p赔p陪p配p沛b盆p砰p抨p烹p澎p彭p蓬p棚p硼p篷p膨p朋p鹏p捧p碰p坯p砒p霹p批p披p劈p琵p毗p艪l舻l艬c艭s艵p艶y艳y艹a艻l艼d幯j庅m庺s芃p芅y芆c芇m廐j芉g芌y苄b芓z芔h芕s弐e芚t芛w芞q芠w芢r芣f芧x芲l弞s芵j芶g芺a刍c芼m芿r苀h苂c苃y苅y苆s苉p苐d苖d苙l苝b苢y苎z苨n苩p苪b苬x苭y苮x苰h徛j苲z徦j苳d苵d苶n苸h苺m苼s苽g苾b苿w徴z茀f徸z茊z茋z茐c茒y茓x茖g茘l忇l茙r茝c茞c怓n茠h茡z茢l茣w茤j茥g茦c茩h茪g茮j怱c茷f茻m茽z啤p脾p疲p皮p匹p恎d痞p僻p屁p譬p篇p偏p片p骗p恜c飘p瓢p票p撇p瞥p拼p频p贫p品p聘p乒p坪p苹p萍p平p瓶p评p屏b坡p颇p婆p破p魄b迫p粕p剖p莆p葡p菩p蒲p埔b圃b普p浦p谱p曝b瀑b期j欺q妻q七q漆q柒q沏q其j棋q奇j歧q畦q崎q愂b齐j祈q祁q起q岂q乞q企q契q器q迄q汽q泣q愪y讫q掐q茾q茿z荁h荂f荄g荅d愲g荈c荋e荌a荍q荎c荓p荕j荖l荗s荘z荙d荝c荢z慃y荰d荱w荲l荳d荴f荵r荶y荹b荺y荾s荿c莀c莁w莂b莃x莄g莇z莈m庄z莋z莌t莏s莐c莑p莔m莕x憉p莗c莙j莚y莝c莟h莡c莣w莤s莥n莦s憜d苋x莬w莭j莮n憡c莯m莵t莻n莾m莿c菂d菃q菄d菆c菈l菉l菋w菍n菎k菐p菑z菒g菓g菕l菗c憿j菙c菚z菛m菞l菢b菣q菤j菦q菧d菨j菫j懏j菬q菭t恰q洽q钎q铅q千q懡m迁q仟q谦q乾q黔q钱q钳q前q遣q谴q嵌k欠q歉q腔q羌q蔷q橇q锹q敲q悄q瞧q巧q鞘q撬q峭q俏q切q茄j且j怯q戵q侵q亲q秦q琴q勤q芹q擒q禽q沁q青q轻q卿q清q擎q晴q氰q情q顷q请q秋q丘q邱q求q囚q抯z酋q泅q趋q蛆q曲q躯q屈q驱q渠q菮g菳q庵a菵w拲g菶b菷z菺j挌g菼t菾t菿d萀h萅c苌c萈h萉f萐s萒y萓y萔t萕q萖w萗c萙k萚t萛j萞b萟y萠p萡b萢p萣d萩q萪k萫x万m萭y萮y萯f萰l萲x萳n萴c掤b莴w萶c萷s萹b萺m萻a萾y葀k葁j葂m葃z葄z葅z葇r葈x叶x葊a葋q葌j葍f葎l葏j葐p荭h葓h搮l摀w葕x葖t葘z葝q葞m葟h葠s葢g葤z摖q葥q苇w葧b葨w葪j葮d药y葰j葲q葴z摾j葹s葻l葼z取q娶q龋q趣q去q圈j颧q醛q泉q全q痊q拳q犬q券q撧j缺q炔g瘸q鹊q榷q裙q群q然r燃r冉r染r瓤r壤r攘r嚷r让r饶r惹r壬r擟m仁r人r忍r韧r任r认r刃r妊r扔r仍r日r戎r茸r擿t攂l蓉r融r熔r溶r攚w容r冗r揉r柔r肉r茹r蠕r儒r孺r如r辱r乳r汝r攺y入r褥r软r阮r蕊r瑞r锐r闰r若r弱r撒s萨s腮s鳃s塞s赛s三s叁s葽y葾y葿m蒀y蒁s蒃z蒄g蒅s蒆x蒍w蒏y蒐s蒑y蒒s蒓c莳s蒕y蒖z蒘r蒚l蒛q蒝y莅l蒟j蒠x蒢c蒣x蒤t蒥l蒦h蒧d蒨q蒩z蒪p蒫c蒬y蒭c蒮y蒰p蒱p蒳n斮z蒵x斳q蒷y蒻r蒾m荪s蓂m蓃s蓅l蓆x蓇g蓈l蓌c蓎t蓏l蓒x蓔y蓕g蓗z蓘g蓙g蓚t蓛c蓜p蓞d蓡s旤h蓢l蓤l蓧d蓨t蓩m蓪t蓫z蓭a苁c蓱p蓲q蓳j蓴c蓵j蓶w蓷t蓸c蓹y蓺y蓻z荜b蓾l蔀b蔁z蔂l散s桑s嗓s搔s骚s嫂s瑟s色s森s僧s莎s砂s沙s傻s啥s晇x煞s珊s苫s杉s山s煽s衫s闪s陕s擅s赡s膳s善s汕s晬z扇s商s赏s晌s上s尚s裳c梢s捎s稍s芍s勺s韶s少s哨s邵s奢s赊s蛇s舌s赦s射s涉s社s设s砷s申s呻s伸s身s深s娠s神s沈s暎y暔n甚s慎s生s甥s牲s蔃q暭h蔄m蔅y蔇j蔈b蔉g蔊h蔋d蔍l蔎s蔏s蔐d蔒h蔕d蔖c蔘s蔙x蔛h蔜a曌z曗y曻s蒌l蔠z蔢p蔤m茑n蔧h蔩y蔪j荫y蔮g蔯c蔰h蔱s蔲k蔳q枈p蔵z蔶z蔾l蔿w蕀j荨q蕂s蕄m蕅o蒇c蕇d蕋r蕌l蕍y荞q蕏z蕐h蕑j芸y蕔b莸y蕗l柛s荛r蕚e蕛t蕜f蕝j蕟f蕠r柨b蒉k蕣s蕥y蕦x蕧f柸b芜w蕫d蕬s萧x栭e蕯s蕰y蕱s蕳j蕵s蕶l蓣y蕸x桗d蕼s蕽n蕿x薀y薁y桜y省s盛c剩s失s施s诗s尸s虱s十s石d拾s什s梍z食s蚀s识s史s矢s使s屎s驶s始s式s示s士s世s梫q柿s事s拭s誓s逝s嗜s噬s适s棃l仕s侍s释s棇c饰s氏s市f恃s室s视s试s收s手s首s守s授s售s受s椃h瘦s蔬s梳s殊s抒s输s叔s舒s淑s椘c疏s赎s孰s熟s薯s暑s曙s署s蜀s黍s鼠s述s束s戍s竖s墅s庶s漱s薂x薃h楕t楖j楡y荟h薉h薋c芗x薍w薎m薐l薒c薓s薕l薖k榐z薗y薘d薙t薚t薝z薞s槀g薠f薡d薢x薣g薥s槁g薫x薬y薭b薱d槗q薲p薳y薴n薵c薶m薸p荠j薻z薼c薽z薾e薿n藀y藂c藃x藄q藅f藆j藇x藈k藊b藋d藌m荩j槸y藑q藒q藔l藖x藗s樐l藘l藙y藚x藛x艺y藞l藟l藠j藡d藢z藣b藦m藧h藨b薮s藫t藬t藭q藮q藯w藰l樼z樿s藲o藳g藴y藵b苈l藸z橩q恕s刷s耍s摔s衰c甩s栓s拴s霜s双s爽s谁s水s睡s吮s瞬s顺s舜s说s朔s斯s撕s嘶s思s私s司s死s肆s寺s嗣s四s似s饲s巳s松s颂s送s宋s讼s诵s搜s艘s嗽s酥s俗s素s檪l速s粟s僳s塑s溯s宿s诉s蒜s算s檾q櫈d櫐l虽s隋s随s髓s碎s穗s遂s隧s祟s櫢s梭s櫴l唆s索s锁s所s櫿y塌t他t她t欃c塔d蔺l欟g欦q藽q藾l歞e蘁w蘂r歬q殏q蕲q蘈t蘉m蕴y蘌y蘍x蘎j蘏j蘐x殭j毥x蘔j蘕p蘖n蘘r蘙y藓x蘛y蘜j蘝l蔹l蘟y蘠q蘡y茏l蘣t蘤h蘥y蘦l蘨y蘪m蘫l蘬g蘮j蘯d蘰k蘱l蘲l蘳h蘴f蘵z蘶w蘷k汦z汻h沕m蓠l蘻j蘽l蘾h虀j虁k泈z泘h虃j泿y虄s虅t洜l虇q虈x虉y虊l虋m虌b虒s虓x虖h虗x虘c虙f虚x虝h虠j浰l虡j虣b浲f虤y虥z虦z涖l涶t虩x虪s蹋t踏t胎t淃j抬t泰t酞t太t汰t坍t贪t檀t痰t潭t谭t谈t坦t毯t袒t碳t探t炭t渜n塘t搪t堂t棠t膛t唐t糖t倘c躺t淌t趟t掏t滔t萄t桃t逃t淘t陶t讨t套t特t腾t疼t誊t梯t湕j剔t踢t锑t提d题t蹄t啼t体t替t嚏t惕t涕t剃t湲y天t添t填t田t甜t恬t舔t腆t挑t眺t跳t贴t铁f帖t虭d虯q虰d虳j虴z虵s溳y滖s滜z滵m虸z蚃x蚄f漐z蚆b蚇c蚈q蚉w蚎y蚏y蚐j蚑q蚒t漹y潎p蚖y潣m蚗j蚘h蚙q蚚q蚛z蚞m蚟w蚡f蚢h蚥f蚦r蚫b澚a蚮d蚲p蚳c蚷j蚸l蚹f蚻z蚼g蚽p蚾p蛁d蛂b蛃b蛈t蛌t蛍y蛒g蛓c蛕h蛖m蛗f蛚l蛜y蛝x蛠l濵b蛡y蛢p蛣j濸c蛥s蛦y蛧w蛨m蛪q蛫g蛬q瀀y瀇w瀐j蛵x蛶j蛷q蛱j蜕t蛼c蛽b蛿h蜁x蜄s蜅f蚬x蜋l蜌b蜎y蜏y蜐j蜑d蜔d蜖h汀t廷t停t亭t庭t挺t艇t通t桐t酮t瞳t同t铜t彤t童t桶t灜y捅t筒t痛t偷t投t头t灩y透t凸t突t徒t途t屠t土t吐t兔t湍t推t颓t腿t褪t退t吞t屯t臀t拖t托t炏y鸵t陀t驮d炓l炗g驼t妥t拓t唾t挖w哇w蛙w娃w瓦w袜w炲t歪w外w豌w玩w顽w烷w完w碗w挽w晚w皖w惋w宛w烞p婉w腕w汪w王w烮l亡w枉w往w旺w望w忘w妄w威w蜙z蜛j蜝q蜟y蜠j蜤s蜦l蜧l蜨d蜪t蜫k蜬h蜭h蜯b蜰f蜲w蜳d蜵y蜶s蜸q蜹r蜺n蜼w蜽l蝀d蝁e蝂b蝃d蝄w蝅c焭q蝆y焲y蝋l蝍j蝏t焷p蝐m蝑x蝒m蝔j蝘y蝚r蝛w蝜f蝝y蝞m蝟w蝡r蝢x虾h蝧y蝨s蝩c蝪t蝫z蝬z蝭d蝯y蝱m蝲l蝳d蝵q蝷l蜗w蝹y蝺q蝿y螀j螁b蛳s螆c螇x螉w螊l螌b螎r螏j螑x螒h螔y螕b螖h螘y螙d螛h螜h螝g螠y螡w萤y螣t螤z巍w微w危w韦w违w桅w唯w惟w萎w委w尾w未w蔚w味w畏w胃w喂w魏w位w渭w谓w尉w慰w卫w瘟w蚊w文w燰w吻w紊w嗡w翁w爁l我w斡w握w沃w巫w钨w诬w屋w爗y爙r梧w吾w武w爠q五w捂w午w爥z舞w伍w侮w戊w雾w晤w物w勿w悟w误w昔x熙x析x西x硒x矽x晰x嘻x吸x牔b锡x螥c螦s螧q螩d螪s螮d螰l螱w螲d螴c螶q螷p螸y螹c蝼l螼q螾y螿j蟁w蟂x蟃w蛰z蟅z蝈g蟉l蟌c蟍l蟎m蟏x蟐c蟔m蟕z蟖s蟗q蟘t蟙z蟚p蟜j蟝q蟞b蟟l蟡g蟢x虮j蟤z蟦f蟧l蟨j蟩j蟫y狔n狚d蛲n猍l蟰x猏j蟱w蟳x蟴s蟵c蛏c蟷d蟸l蟺s蚁y蟼j蟽d蟿q蠀c蠁x蠂s蠄q蝇y虿c蠇l蠈z蠉x蠋z猽m蠌z蠍x蠎m蠏x蛴q蝾r蠒j蚝h獉z蠗z蠘j蠙b蠚h蠜f蠝l蠞j蠠m蛎l稀x息x希x悉x膝x夕x惜x熄x烯x溪x汐x犀x檄x袭x席x媳x喜x铣x洗x獹l隙x瞎x匣x霞x辖x暇x下x夏x掀x锨x先x仙x鲜x咸x贤x舷x闲x涎x嫌x显x险x腺x馅x陷x限x相x香x箱x襄x湘x乡x翔x祥x详x想x响x享x项x巷h橡x像x象x硝x霄x削x哮x销x消x宵x淆x蠤q蠥n蠦l蠧d蠨x蠩z蠪l蠫l蠬l蠭f蠮y蠯p蠰n蠳y蠴s蠵x瑊j瑘y蠸q瑦w瑸b璊m蠽j蠾z蠿z璠f衃p衆z衉k衋x衎k衏y衐q衑l衒x衕t衘x衦g衧y衪y甇y衱j衳z衴d衵r衶z衸j衺x衻r衼z袀j袃c袆h袇r畄l袉t袊l袌p袎y袏z袐b袑s袓j袔h袕x袗z袘y袙p畞m袚f畧l袝f袟z袠z袡r袣y袥t袦n袧g袨x袩z袪q小x孝x校j肖x疜x笑x效x楔x些x歇x疪b蝎x鞋x邪x斜x谐x械x卸x蟹x懈x痟x谢x屑x薪x痭b芯x锌x欣x辛x新x忻x心x信x衅x星x腥x猩x惺x刑x型x形x邢x行h醒x杏x性x姓x兄x胸x瘹d匈x雄x癁f熊x休x羞x朽x嗅x癊y锈x秀x袖x墟x戌q需x须x徐x许x蓄x酗x癦m旭x序x畜c絮n婿x轩x喧x宣x旋x玄x袬y袮m袯b袲c袳c袵r袶p袸j袹b袺j袻e袽r袿g裀y裃k裄h裇x裈k裋s裌j裍k裐j裑s裓g裖z裗l裚j裛y装z裞s裠q裦b裧c裩k裪t裫y裬l裭c裮c裯c裲l裵p裶f裷y裺y裻d制z裿y褀q褁g褃k褄q褅t褆t褈c褉x褋d褌k褍d褎x褏x褑y褔f褕y褖t褗y褘h褜p褝d褞y褟t褠g褢h褣r褤y褦n褧j褨s褩b褬s褭n褮y褯j褱h裢l褵l褷s选x眩x靴x薛x穴x雪x血x熏x循x旬x询x驯x巡x殉x汛x训x讯x逊x迅x押y鸦y鸭y丫y芽y牙y蚜y崖y衙y涯y雅y盶y讶y焉y阉y淹y盐y研y蜒y延y言y颜y阎y炎y沿y奄y掩y眼y衍y堰y燕y雁y唁y宴y谚y验y殃y央y鸯y秧y佯y羊y洋y氧y仰y养y漾y邀y腰y妖y褛l褹y褺d亵x褼x褽w褾b褿c襀j襃b襅q襆f襇j襈z襉j襊c襋j襌d襍z襎f襏b襑x襒b襓r襔m襕l襗z襘g襙c襚s襛n襜c裣l裆d襡s襢z襣b褴l襥f襧z襨t襩s襫s襭x襮b襰l襱l襳x襴l襵z襶d襷t襸z襹s襺j襼y襽l襾y覀y覂f覄f覅f覇b覉j瞈w覊j瞏q覌g覍b覎y瞔z覐j覑p覒m覔m覕p覗s觇c覙z覚j覛m覜t覝l覞y覟z覠j觋x遥y谣y姚y咬y舀y要y矊m耀y椰y噎y耶y野y冶y也y页y掖y腋y夜y液y一y壹y医y揖y铱y依y伊y衣y颐y夷y遗w移y胰y疑y沂y宜y姨y彝y椅y倚y已y乙y矣y以y抑y易y邑y屹g役y臆y逸y肄y疫y亦y裔y意y毅y益y溢y诣y议y谊y译y翼y翌y茵y因y音y阴y姻y吟y银y淫y寅y饮y尹y引y隐y覢s覣w覤x覥t觎y覧l矲b覨e覩d觊j覭m覮y觏g覰q覱z觐j観g覴d覵j覶l觑q覸j硡h硢y覻q覼l覾s觌d觃y觍t觕c觗z觘c觙j觛d觝d觟h觠q觡g觢s觤g觧j觨h觩q觪x觬n觭j觮j觰z觱b觲x觞s觵g觯z觷x觹x觺y觻l觼j觽x觾y觿x訁y訄q訅q訆j訉f訋d讧h訍c訏x讦j訑y訒r訔y讪s訙x訚y印y英y鹰y迎y赢y盈y影y硬y映y臃y庸y雍y踊y蛹y咏y碐l泳y永y恿y勇y用y幽y悠y尤y由y邮y铀y油y游y酉y碻q有y碽g友y磂l右y釉y诱y又y幼y迂y淤y于y盂y榆y虞y愚y舆y余y俞y逾y鱼y愉y渝y磭c隅y予y磰s雨y禹y宇y语y羽y玉y域y礃z芋y郁y遇y喻y峪y愈y育y誉y訞y訠s訡y讷n訦c訧y訨z訩x訫x訬c訮y訯s訰z訲y訳y訵c诃h訷s訸h訹x诊z注z证z訽g訿z詀z诂g詂f詃j詄d詅l诋d詇y詊p詋z詌g詍y讵j詏y诈z詑t诒y詓q诏z詖b礶g祍r祘s诎q詙b詚d诅z詜t詝z詟z诩x祱s詤h詥h祶d詧c詨x詪h诟g禈h诠q詯h诘j祯z詴w诜s詶c詷t詸m詻l诙h詽y詾x诖g誀e浴y寓y裕y预y豫y驭y鸳y冤y元y垣y袁y原y援y辕y猿y源y远y苑y愿y怨y院y曰y越y跃y钥y月y阅y耘y云y郧y陨y允y运y酝y韵y孕y匝z砸z杂z栽z哉z宰z载z再z在z赞z赃z葬z遭z糟z凿z藻z早z澡z蚤z躁z噪z造z皂z灶z燥z责z贼z怎z增z憎z曾c赠z喳c秢l渣z札z轧g誁b誂t誃y诔l诛z诓k誈w誋j志z誏l誐e诳k诶a誔t誖b誗c誙k诮q誛q誜s誝a誟x誢x诰g誧b誩j誫z説s読d誮h誯c誱j誳q誴c誵x谇s誷w誸x誺c誻t誾y諀p諁z谄c諃c谆z諅j諆j諈z诿w諊j諌d诤z諎z诹z諐q诼z諓j諔c諕x谂s諘b諙h諚b谀y諝x稬n谝p諟s諠x諡s诨h諣h稸x穉z铡z闸z眨z榨z咋z穙p乍z炸z摘z斋z穠n宅z窄z寨z瞻z詹z粘n沾z辗z展z蘸z站z湛z樟z章z彰z漳z掌z杖z丈z账z仗z瘴z障z招z昭z找z沼z赵z照z罩z兆z肇z召s遮z折s哲z辙z者z锗z蔗z这z浙z珍z斟z真z甄z砧z臻z贞z针z枕z疹z震z振z镇z阵z蒸z征z怔z整z拯z正z政z谔e諥z谛d諨f諩p諪t谏j諬q谕y谘z諯z諰x諲y谙a諴x諵n谌c诸z諹y諻h谖x窧z窽k谒y竌c竨d竪s诌z謆s謈b謉k謋h謌g謍y謏x竸j谧m謑x謒q謓c谑x謕t谡s謘c谥s謜y謞h謡y謢l謣y謤b謥c謧l谟m謩m謪s谪z謭j謮z謯j謰l謱l讴o謴g謵x謶z謷a謸a謺z謻y謼h謽j謿c譀h譂c譃x譄z譅s郑z芝z枝z支z吱z蜘z知z肢z脂z汁z之z直z植z殖s值z址z指z止z趾z旨z至z置z峙s智z秩z稚z质z炙z痔z治z窒z中z盅z筳t筴c忠z钟z衷z重c仲z舟z箈t周z州z洲z粥y轴z肘z帚z咒z宙z骤z珠z株z蛛z猪z逐z竹z煮z拄z主z著z柱z助z蛀z贮z铸z譆x譇z譈d譊n譋l譌e谲j譐z譑j譒b譓h譔z譕w谮z譗z谯q譛j譝s譞x譠t譡d譢s譣x譤j譥j譧z譨n譩y谵z譭h譮h譱s譲r譳r篒y譶t譸z譹h譺a譻y譼j譾j譿h讁z讂j讃z讄l讅s讆w讇c讉y讋z篻p讌y讍e雠c讏w讐c讑y讔y谶c讗x讘n讙h讛y谠d讝z谳y讟d讬t讱r讻x诇x诐b诪z谉s谞x住z祝z驻z抓z爪z拽z转z撰z赚z篆z撞z椎c锥z追z赘z捉z拙z卓z桌z琢z茁z酌z啄z着z灼z咨z资z姿z滋z淄z孜z紫z仔z籽z滓z自z字z鬃z棕z踪z宗z邹z走z奏z揍z租z足z卒c族z祖z阻z钻z纂z嘴z醉z最z罪z尊z遵z昨z左z佐z柞z做z作z坐z座z谸q谹h谺x谻j谼h谽h谾h谿x豀x豂l豃h豄d豅l豋d豍b豏x豑z豒z豓y籋n豖c豗h豘t豙y豛y豜j豝b豞h豟e豠c豣j豤k豥g豦j豧f豨x豩b豭j豮f豯x豰h豱w豲h豴d豵z豷y豻a豼p豽n豾p豿g貀n貁y貃m貄s貆h貇k貈h貋a貎n貏b貐y貑j貒t貕x貖y貗j貙c貚t貛h貜j粎m粓g貟y粡t貣d貤y貦w貭z亍c丌q兀w丐g廿n卅s丕p丞c鬲g孬n噩e丨g禺o丿p匕b乇z夭a爻y卮z氐d囟x胤y馗k毓y鼗t丶z亟j鼐n乜m亓q孛b嘏g厝c厥j靥y赝y匚f叵p糂s匾b赜z卦g卣y刂d刈y刎w刳k剌l糛t剞j剡s剜w蒯k剽p劂j劁q劐h劓y冂j罔w亻r仃d仉z仨s糿g仫m仞r仳p伢y佤w仵w伉k紤j佞n佧k攸y佚y佝g貮e贳s貱b赀z貵p貹s贶k絾c綛k綥q緛r繏x繑q繝j纄p纋y纕x纚l缿x翄c翭h翺a耛s聎t肒h肸x胋t胑z胻h脦t脺c脻j腟c臒w臤q臱m舋x舿k艕b芀t芁q芖z苚y茟y茰y莍q菻l萂h萿k葔h蔝m蔨j蔴m蕮x薆a莶x藱h藼x蘀t蘃r蘒q蘓s蘹h虂l虆l虨b虶y虷h蚅e蚔q蚭n蚿x蛯e蝖x螐w螚n蟭j蠷q蠺c衂n衭f袛d袾z襂s襐x覫p覹w觓q訤x詉n詗x詺m誎c誽n諽g諿x謃x謟t謲c譍y譪a譵d貥h乩j仡g斛h颢h銮l乊h乥h夫f兓j兙s兛q兝g兞h兣g兺b囻g坟f妢f婲h孒j尅k屗u岎f帉f幥z幩f弅f徚u怭b怾g慙c昐f晲n曢u朆f朌f朑u朩p枌f梤f乐l橨f檂n欂b毜c毮s洂y涜d颍y澵z炃f炍p炾h烪u焑y焺s燌f燓f燞u燢x獖f瓧s瓰f瓱m瓲t瓼l榛z彳c夔k璞p丟d並b丬p乂y乄s亙g亞y亠t亳b仝t伋j伕f伲n伽g佇z佈b佔z佗t佘s佟t佪h佴e併b佶j佻t佼j佾y侃k來l侉k侏z侑y侔m侖l侶l侷j俅q俎z俑y俚l俜p俟q俠x俳p俸f俾b倆l倉c個g倌g倏s們m倖x倜t倣f倥k倨j倩q倫l倬z倭w倮l偃y偈j偉w偌r偎w偕x側c偵z偺z偽w傖c傘s備b傢j傭y傳c傴y債z傷s傺c傾q僂l僅j僇l僉q僑q僕p僖x僥j僦j僬j僭j僮t僱g價j儀y儂n億y儅d儆j儇x儉j儋d儐b儔c儕c儘j優y儲c儷l儸l儺n儻t儼y兌d兒e兕s兗y內n兩l兮x冊c冑z冖m冢z冥m冪m冫b冱h冼x冽l凇s凈j凍d凫f凱k凵k凼d刖y別b刪s剄j則z剋k剎c剛g剝b剮g剴k創c剷c劃h劇j劉l劊g劌g劍j劑j劬q劭s劻k劾h勁j勐m動d勗x務w勛x勝s勞l勢s勣j勦j勰x勱m勵l勹b勻y匍p匏p匐f匭g匯h匱k區q協x卟b卩j卹x卻q厙s厭y厲l厴y厶s參c叟s叢c叨d叩k叱c叻l吆y吋c吒z吖y吡b吣q吲y吳w吶n呂l呃e呋f呎c呔d呤l呦y呱g呲c呶n呷g咂z咄d咔k咚d咝s咣g咤c咦y咧l咩m咪m咫z咭j咻x咼w咿y哂s哌p哏g哐k哚d哞m員y哧c哳z哽g哿g唄b唅h唏x唑z唔m唣z唧j唪f唰s唳l唷y唸n唼s唿h啁z啉l問w啐c啕t啖d啞y啟q啣x啵b啶d啷l啻c啾j喁y喃n喈j喋z喏n喑y喔o喙h喚h喟k喪s喫c喬q單d喱l喵m嗄a嗆q嗇s嗉s嗌y嗍s嗎m嗑k嗒d嗔c嗖s嗚w嗝g嗟j嗤c嗥h嗦s嗨h嗪q嗬h嗲d嗵t嗶b嗷a嗾s嘀d嘁q嘈c嘌p嘍l嘔o嘖z嘜m嘞l嘟d嘣b嘧m嘩h嘬c嘭p嘮l嘯x嘰j嘵x嘸f嘹l噁e噌c噍j噓x噔d噗p噘j噙q噠d噢o噤j噥n噦y噫a噯a噱j噲k噴p噸d噹d噻s噼p嚀n嚅r嚆h嚇h嚌j嚐c嚓c嚙n嚥y嚦l嚨l嚮x嚯h嚳k嚴y囀z囁n囂x囅c囈y囉l囌s囑z囔n囗w囝j囡n囪c囫h囹l囿y圄y圇l圉y圊q國g圍w園y圓y圖t圜h圩w圪g圮p圯y圳z圻q坂b坌b坨t坩g坫d坭n坰j坳a坶m坻c坼c垅l垌d垓g垠y垡f垤d垴n埏s埒l埕c埝n埡y埤p埭d埯a埴z執z埸y埽s堀k堅j堇j堊e堋b堍t堙y堞d堠h報b場c堿j塄l塊k塋y塍c塒s塗t塢w塤x塥g塬y塵c塹q塾s墀c墁m墉y墊d墑d墚l墜z墮d墻q壅y壇t壑h壓y壙k壚l壞h壟l壢l壩b壯z壺h壽s夂z夙s夠g夢m夤y夥h夼k夾j奐h奕y奘z奚x奧a奪d妁s妃f妍y妗j妝z妞n妣b妤y妯z妲d妳n妾q姊z姍s姒s姘p姝s姣j姦j姪z姹c娉p娌l娑s娓w娛y娣d娼c婀e婊b婕j婢b婦f婧j婭y婷t婺w媛y媧w媯g媲p媸c媼a媽m媾g嫖p嫗y嫘l嫜z嫠l嫣y嫦c嫫m嫵w嫻x嬈r嬉x嬌j嬖b嬗s嬙q嬝n嬡a嬤m嬰y嬲n嬴y嬸s孀s孃n孑j孓j孚f孢b孥n孫s孱c孳z學x孿l宀m宄g宓m宕d宥y宮g宸c寢q寤w實s寧n審s寫x寬k寮l寰h寵c寶b將j專z尋x對d尕g尜g尢w尥l尬g尷g尻k屄b屆j屐j屙e屜t屣x層c屬s屮c屺q岈y岌j岍q岐q岑c岙a岜b岡g岢k岣g岧t岫x岬j岱d岵h岷m峁m峋x峒d峴x島d峽x崆k崍l崗g崙l崛j崞g崠d崢z崤x崦y崧s崮g崴w崽z崾y嵇j嵊s嵋m嵒y嵩s嵫z嵬w嵯c嵴j嶁l嶂z嶄z嶇q嶗l嶙l嶷n嶸r嶺l嶼y嶽y巋k巑c巔d巖y巛c巰q巹j巽x帑t帔p帙z帥s帳z帶d帷w幃w幄w幔m幘z幛z幞f幟z幡f幣b幪m幫b幬c幹g幺y幾j庀p庂z庋g庖p庠x庥x庫k庳b庹t庾y廁c廂x廄j廈s廑q廒a廚c廛c廝s廡w廢f廣g廨x廩l廬l廱y廳t廴y廾g弁b弈y弋y弒s弔d弩n弭m弳j張z強q弼b彀g彆b彈d彊q彌m彎w彐x彖t彗h彘z彙h彡s彥y彫d彷f彿f徂c徇x徉y後h徑j徙x徜c從c徠l徨h復f徫w徬p徭y徵z徹c徼j忉d忐t忑t忒t忖c忝t忡c忤w忪s忭b忮z忸n怊c怍z怏y怙h怛d怡y怦p怩n怫f怵c恁n恂x恆h恙y恚h恝j恣z恥c恧n恪k悃k悅y悌t悒y悖b悚s悛q悝k悱f悴c悵c悶m悻x悽q惆c惘w惙c惝t惡e惱n惲y惴z惻c愀q愆q愍m愎b愕e愛a愜q愣l愨q愫s愴c愾k慄l慇y慊q態t慍y慘c慚c慝t慟t慣g慫s慮l慵y慶q慼q慾y憂y憊b憐l憑p憔q憚d憝d憧c憩q憫m憬j憲x憶y憷c懇k應y懋m懌y懍l懔l懞m懟d懣m懨y懲c懵m懶l懷h懺c懼j懾s懿y戀l戇g戉y戔j戕q戛j戟j戡k戢j戤g戥d戧q戩j戰z戲x戶h戽h戾l扃j扈h扌s扐c扠c扢g扺z抃b抆w抎y抴y抻c拊f拋p拗a拮j拶z挈q挲s挶j挹y挾x捃j捋l捨s捩l捫m捭b捱a捺n掃s掄l掊p掎j掙z掛g採c掬j掭t掮q掰b掾y揀j揄y揆k揎x揚y換h揞a揠y揲d揶y揸z揹b搆g搋c搌z損s搖y搗d搛j搟x搠s搡s搦n搴q搶q搿g摁e摑g摒b摜g摞l摟l摭z摯z摳k摶t摺z摻c撈l撓n撙z撚n撟j撣d撥b撫f撲p撻t撾z撿j擁y擄l擇z擊j擐h擔d擗p擘b據j擠j擢z擣d擤x擬n擯b擰n擱g擲z擴k擷x擺b擻s擼l擾r攄s攆n攉h攏l攔l攖y攙c攛c攜x攝s攢z攣l攤t攥z攬l攮n攴p攷k敉m敕c敗b敘x敫j敵d斂l斃b斐f斕l斫z斬z斷d於w旂q旃z旄m旆p旌j旎n旒l旖y旮g旯l旰g昀y昃z昇s昊h昕x昝z昱y昴m昶c晁c時s晉j晏y晗h晝z晞x晟c晡b晷g暄x暈y暉h暌k暘y暝m暨j暫z暱n暸l暹x暾t曄y曆l曇t曉x曖a曛x曜y曠k曩n曬s曷h書s會h朊r朐q朕z朦m朧l朮s杇w杈c杌w杓b杞q杪m東d杲g杳y杵c杷b杼z枇p枋f枘r枙e枰p枳z枵x枸g柁d柃l柘z柚y柝t柢d柩j柰n柵s栝g栩x栲k栳l桀j桁h桄g桉a桊j桎z桕j桫s桴f桷j桿g梃t梏g梓z梔z條t梟x梩q梵f棄q棖c棗z棟d棣d棧z棰c棲q棹z棼f椋l椏y椐j椴d椹s楂c楊y楓f楗j楝l楠n楣m楦x楨z楫j業y楮c楱c極j楸q楹y榀p榍x榕r榘j榙t榧f榪m榫s榭x榮r榱c榻t榿q槃p槊s槌c槍q槎c槓g槧q槨g槭q槲h槳j槿j樁z樂l樅c樑l樓l樗c樘t標b樞s樨x樵q樸p樹s樺h樽z樾y橄g橈r橋q橐t橘j橛j機j橢t橫h檁l檉c檎q檔d檗b檜g檠q檢j檫c檯t檳b檸n檻j櫓l櫚l櫛z櫝d櫟l櫥c櫧z櫨l櫪l櫬c櫳l櫸j櫺l櫻y欄l權q欏l欒l欖l欞l欷x欹q歃s歆x歎t歐o歙s歟y歡h歲s歷l歿m殂c殄t殍p殘c殞y殤s殪y殫d殮l殯b殲j殳s殺s殼k毀h毂g毆o毌g毪m毬q毳c毹s毽j毿s氅c氆p氈z氌l氍q氕p氘d氙x氚c氡d氣q氤y氪k氫q氬y氳y氵s氽c氾f汆c汊c汍w汎f汔q汜s汨m汩g汴b汶w決j沅y沆h沌d沐m沒m沓d沔m沖c沭s沱t沲t泐l泓h泔g泖m泗s泠l泫x泮p泯m泱y洄h洇y洌l洎j洙z洚j洧w洩x洫x洮t洳r洵x洶x洹h浜b浞z浠x浣h浬h浯w浹j浼m涇j涑s涓j涔c涫g涮s涼l涿z淅x淇q淒q淙c淚l淝f淞s淠p淥l淦g淨j淪l淵y淶l淼m渙h渚z減j渥w渦w渫x測c渲x渾h湄m湊c湎m湓p湔j湞z湟h湧y湫j湮y湯t溆x溏t準z溘k溝g溟m溥p溧l溫w溱q溲s溴x溷h溻t溼s溽r滂p滄c滅m滎x滏f滕t滟y滬h滯z滲s滷l滸h滹h滾g滿m漉l漕c漚o漢h漣l漤l漩x漪y漬z漭m漯l漲z漶h漸j漿j潁y潑p潔j潛q潟x潢h潤r潯x潰k潲s潴z潷b潸s潺c潼t澀s澆j澇l澉g澌s澍s澗j澠m澤d澧l澩x澮k澱d澶c澹d濂l濃n濉s濕s濘n濛m濞b濟j濠h濡r濤t濫l濮p濯z濰w濱b濺j濼l瀅y瀆d瀉x瀋s瀏l瀕b瀘l瀚h瀛y瀝l瀟x瀠y瀣x瀧l瀨l瀰m瀲l瀵f瀹y灃f灄s灑s灕l灘t灝h灞b灣w灤l灬h災z炅g炫x炱t為w炻s烀h烊y烏w焐w焓h無w焯c焱y煅d煉l煊x煒w煙y煜y煢q煦x煨w煩f煬y煲b煳h煸b煺t熒y熗q熘l熠y熨y熱r熳m熵s熹x熾c燁y燄y燉d燐l燒s燔f燙t營y燠y燦c燧s燬h燭z燮x燴h燹x燼j燾d爐l爛l爝j爨c爭z爰y爺y爾e爿p牆q牒d牖y牘d牝p牠t牦m牪y牮j牯g牴d牽q牿g犄j犍j犏p犒k犛l犟j犧x犭q犰q犴a犸m狀z狁y狃n狍p狎x狒f狨r狩s狳y狴b狷j狹x狺y狻s狽b猁l猊n猓g猗y猙z猝c猞s猢h猥w猬w猱n猶y猷y猸m猹c猻s獄y獅s獍j獐z獒a獗j獠l獨d獪k獫x獬x獯x獰n獲h獵l獷g獸s獺t獻x獼m獾h玀l玆z玎d玟m玢b玨j玳d玷d珀p珂k珈j珉m珙g珞l珥e珧y珩h珮p現x琊y琚j琛c琥h琦q琨k琪q琬w琮c琰y琺f琿h瑁m瑕x瑗y瑙n瑛y瑜y瑣s瑤y瑩y瑪m瑭t瑯l瑾j璀c璇x璉l璋z璐l璜h璣j璦a璧b璨c璩q璺w璽x瓊q瓏l瓖x瓚z瓞d瓠h瓴l瓿b甍m甏b甑z甓p甙d產c甦s甬y町d甾z畀b畈f畋t畎q畚b畛z畝m畫h畬s異y畹w畼c畿j疃t疊d疋y疒n疔d疝s疣y疬l疰z疳g疴k疸d疿f痂j痃x痄z痍y痖y痙j痣z痤c痦w痧s痲m痳m痼g痿w瘀y瘃z瘊h瘌l瘍y瘐y瘓h瘕j瘙s瘛c瘞y瘠j瘡c瘢b瘥c瘭b瘰l瘳c瘺l瘼m癀h療l癃l癆l癇x癍b癔y癖p癜d癟b癡c癢y癥z癩l癬x癭y癮y癯q癰y癱t癲d發f皈b皎j皓h皙x皚a皤f皰p皴c皸j皺z皻z盍h盜d盞z盡j監j盤p盥g盧l盪d盱x盹d眄m眇m眈d眍k眚s眢y眣d眥z眭s眵c眸m睇d睏k睚y睜z睞l睢s睥b睨n睪y睽k睿r瞀m瞌k瞍s瞑m瞞m瞟p瞠c瞢m瞭l瞰k瞵l瞼j瞽g瞿j矇m矍j矓l矚z矜j矧s矬c矯j矸g砉h砑y砘d砜f砝f砟z砣t砥d砩f砬l砭b砲p砹a砼t硃z硇n硌g硎x硐d硤x硨c硪w硭m硯y碇d碓d碚b碣j碥b碩s碲d碹x碼m磉s磔z磙g磚z磣c磧q磬q磯j磲q磴d礅d礎c礓j礙a礞m礤c礦k礪l礫l礱l礴b礻s祀s祅y祇q祉z祐y祓f祕m祗q祚z祛q祜h祠c祧t祺q祿l禊x禍h禎z禚z禦y禧x禮l禰m禱d禳r禿t秈x秕b秣m秫s秭z稂l稃f稅s稆l稈g稔r稜l稞k稟b種z稱c稷j稹z穀g穌s積j穢h穩w穫h穰r穸x穹q窀z窆b窈y窕t窠k窨x窩w窪w窬y窮q窯y窳y窶j窺k竄c竅q竊q竦s競j竺d竽y笄j笈j笊z笏h笙s笞c笠l笤t笥s笪d笫z笮z笱g笳j笸p笻q筆b筌q筍s筘k筠j筢p筮s筲s筵y筻g箄b箇g箋j箏z箐q箜k箝q箬r箴z箸z節j篁h範f築z篋q篌h篚f篝g篠x篤d篥l篦b篩s篪c篳b篼d篾m簀z簋g簌s簍l簏l簑s簟d簡j簣k簦d簪z簫x簷y簸b簽q簾l籀z籃l籌c籐t籜t籟l籠l籤q籥y籩b籪d籬l籮l籲y粑b粞x粢z粧z粲c粵y粼l粽z糅r糇h糈x糌z糍c糗q糝s糢m糧l糨j糰t糲l糴d糶t糸m糾j紀j紂z約y紅h紇h紈w紉r紋w納n紐n紓s紕p紗s紙z級j紜y紡f紮z細x紱f紲x紳s紹s紺g紼f紿d絀c終z絃x組z絆b絎h結j絛t絞j絡l絢x給g絨r統t絲s絳j絹j綁b綃x綆g綈t綏s綑k經j綜z綞d綠l綢c綣q綦q綬s維w綮q綰w綱g網w綴z綵c綸l綹l綺q綻z綾l綿m緇z緊j緋f緒x緗x緘j緙k線x緝j緞d締d緡m緣y緦s編b緩h緬m緯w緱g緲m練l緶b緹t緻z縈y縉j縊y縐z縑j縚t縛f縝z縞g縟r縣x縫f縭l縮s縯y縱z縳z縴q縵m縶z縷l縹p縻m總z績j繅s繆m繇y繈q繒z織z繕s繚l繞r繡x繢h繩s繪h繫x繭j繯h繰q繳j繹y繼j繾q纈x纊k續x纍l纏c纓y纔c纖x纘z纛d纜l纟s绔k绛j绱s缰j缶f缽b罄q罅x罈t罌y罘f罟g罡g罨y罰f罱l罵m罹l罾z羅l羆p羈j羋m羝d羧s義y羯j羰t羲x羸l羼c羿y翊y翎l習x翕x翡f翥z翦j翩p翮h翳y翹q耄m耆q耋d耑d耒l耔z耖c耜s耠h耢l耥t耦o耨n耩j耬l耱m耵d耷d聃d聆l聒g聞w聯l聰c聱a聲s聳s聵k聶n職z聹n聽t聾l聿y肅s肓h肜c肟w肫z肭n肱g肴y肷q肼j肽t胂s胍g胗z胛j胝z胥x胨d胩k胬n胭y胱g胲h胴d胼p脅x脈m脎s脒m脘w脛j脞c脣c脧j脩x脫t脬p脲n脹z腈j腌a腎s腓f腙z腚d腠c腡l腦n腧s腩n腫z腭e腱j腴y腸c腼m膂l膃w膈g膚f膠j膦l膪c膺y膻s膽d膾k膿n臁l臉l臊s臌g臍q臏b臚l臟z臠l臧z臨l臬n臾y舁y舂c舄x與y興x舉j舊j舐s舖p舛c舡c舢s舨b舫f舭b舯z舳z舴z舸g舺x舾x艄s艉w艋m艏s艚c艟c艣l艦j艨m艫l艮g艱j艴f艷y艸c艽j艿n芄w芊q芎x芏d芑q芘b芙f芟s芡q芤k芨j芩q芪q芫y芮r芰j芴w芷z芻c芾f苊e苒r苓l苕s苘q苜m苠m苡y苣j苤p苧n苴j苷g苻f茆m茇b茈c茉m茌c茍j茗m茚y茛g茜q茭j茯f茱z茲z茳j茴h茺c茼t荀x荃q荇x荊j荏r荑t荬m荸b荻d荼a荽s莊z莒j莓m莘s莛t莞g莠y莢j莧x莨l莩f莪e莰k莺y莼c菀w菁j菅j菔f菖c菘s菝b菟t菡h菥x華h菰g菸y菹z菽s萁q萃c萆b萊l萋q萏d萑h萘n萜t萬w萱x萵w萸y萼e葆b葉y葑f葙x葚r葜q葦w葩p葭j葯y葳w葶t葷h葸x葺q蒈k蒎p蒔s蒗l蒞l蒡b蒴s蒺j蒼c蒽e蒿h蓁z蓊w蓋g蓍s蓐r蓓b蓥y蓦m蓯c蓰x蓼l蓽b蔌s蔔b蔟c蔣j蔥c蔦n蔭y蔸d蔻k蕁q蕃f蕈x蕎q蕓y蕕y蕖q蕙h蕞z蕢k蕤r蕨j蕩d蕪w蕭x蕷y蕹w蕺j蕻h薅h薇w薈h薊j薌x薏y薑j薔q薜b薟x薤x薦j薧h薨h薩s薰x薷r薹t薺j藁g藍l藎j藜l藝y藥y藪s藶l藷s藹a藺l藿h蘄q蘅h蘆l蘇s蘊y蘋p蘗b蘚x蘢l蘧q蘩f蘭l蘺l蘼m蘿l虍h虔q處c虛x虜l號h虢g虧k虬q虺h虻m蚋r蚍p蚓y蚣g蚧j蚨f蚩c蚪d蚯q蚰y蚱z蚴y蚵k蚶h蚺r蛄g蛅z蛉l蛐q蛑m蛘y蛞k蛟j蛩q蛭z蛸s蛺j蛻t蜃s蜆x蜇z蜈w蜉f蜊l蜍c蜓t蜚f蜞q蜢m蜣q蜥x蜩t蜮y蜱p蜷q蜻q蜾g蜿w蝌k蝓y蝕s蝙b蝠f蝣y蝤q蝥m蝦x蝮f蝰k蝸w蝻n蝽c螂l螃p螅x螋s螓q螗t螞m螢y螨m螫s螬c螭c螯a螳t螵p螻l螽z蟀s蟄z蟆m蟈g蟊m蟋x蟑z蟒m蟓x蟠p蟥h蟪h蟬c蟮s蟯n蟲c蟶c蟻y蟾c蠃l蠅y蠆c蠊l蠐q蠑r蠓m蠔h蠖h蠛m蠟l蠡l蠣l蠱g蠲j蠹d蠼q衄n術s衛w衝c衢q衤y衩c衲n衹z衽r衾q衿j袂m袈j袞g袢p袤m袷q裊n裎c裏l裒p裘q補b裝z裟s裡l裥j裨b裰d裱b裼t製z裾j複f褊b褓b褙b褚c褡d褫c褰q褲k褳l褶z褻x襝l襞b襠d襤l襦r襪w襬b襯c襻p覃q見j規g覓m視s覘c覡x覦y親q覬j覯g覲j覷q覺j覽l覿d觖j觚g觜z觥g觫s觳h觴s觶z觸c訂d訃f訇h訊x訌h討t訐j訓x訕s訖q託t記j訛e訝y訢x訣j訪f設s許x訴s診z註z証z訾z詁g詆d詈l詎j詐z詒y詔z評p詘q詛z詞c詠y詡x詢x詣y詩s詫c詭g詮q話h該g詵s詼h誅z誆k誇k誌z認r誑k誕d誘y誚q語y誠c誡j誣w誤w誥g誨h誰s課k誶s誼y調d諂c諄z談t諉w請q諍z諏z諑z諒l論l諗s諛y諜d諞p諢h諤e諧x諫j諮z諱h諳a諶c諷f諸z諺y諾n謀m謁y謂w謅z謇j謊h謎m謐m謔x謗b謙q謚s講j謝x謠y謦q謨m謫z謬m謳o謹j譁h證z譎j譖z識s譙q譚t譜p譟z譫z譯y議y譴q護h譽y讀d讎c讒c讓r讕l讖c讚z谫j豇j豈q豉c豎s豐f豔y豕s豚t豬z豳b豸z貂d貅x貊m貍l貓m貔p貘m貝b負f財c貢g貧p貨h販f貪t貫g責z貯z貰s貲z貳e貴g貶b貸d貺k貼t貾c貿m賁b賂l賃l賄h賅g賆p資z賈j賉x賊z賋j賌g賎j賏y賐j賑z賒s賓b賔b賕q賖s賗c賘z賙z賛z賜c賝c賞s賟t賠p賡g賢x賣m賤j賥s賧d賨c賩c質z賬z賭d賮j賯x賰c賳z賴l賵f賶c賷j賸s賹y賺z賻f購g賽s賾z賿l贀y贂c贃w贄z贅z贆b贇y贈z贉d贊z贋y贌p贍s贎w贏y贐j贑g贒x贓z贔b贕d贗y贘s贙x贚l贛g贜z贠y贲b贻y贽z赅g赆j赇q赈z赉l赍j赑b赒z赓g赕d赗f赙f赟y赥x赧n赨t赩x赪c赬c赭z赮x赯t赱z赲l赳j赸s赹q赺y赻x赼z赽j赿c趂c趃d趄j趆d趇x趈z趉j趌j趍q趎c趏g趐x趑z趔l趖s趗c趘x趚s趜j趝j趞q趠c趡c趤d趥q趧t趨q趩c趪h趫q趭j趮z趯t趰e趱z趲z趵b趷k趹j趺f趻c趼j趽f趿t跁b跂q跄q跅t跆t跇y跈j跉l跍k跎t跏j跐c跒q跓z跔j跕d跖z跗f跘p跙j跚s跛b跜n跞l跠y跡j跢d跣x跤j跦z跧q跩z跫q跬k跭x跰p跱z跲j跴c跶d跷q跸b跹x跻j跼j跽j跾s跿t踀c踁j踂n踃x踄b踅x踆c踈s踉l踍q踎m踑j踒w踓w踔c踕j踗n踘j踙n踚l踛l踝h踟c踡j踣b踥q踦q踧c踫p踬z踭z踮d踯z踰y踱d踲d踳c踴y踵z踶d踷z踸c踹c踺j踻g踽j踾f踿c蹀d蹁p蹂r蹃n蹇j蹉c蹊q蹍n蹐j蹑n蹒p蹓l蹔z蹕b蹖c蹗l蹘l蹙c蹚t蹛d蹜s蹝x蹞k蹟j蹠z蹡q蹢d蹣p蹤z蹥l蹧z蹨n蹩b蹪t蹫j蹮x蹯f蹰c蹱z蹳b蹴c蹵c蹶g蹷j蹸l蹹t蹺q蹻q蹼p蹽l蹾d躀g躂d躃b躄b躅z躆j躈q躉d躊c躋j躍y躎n躏l躐l躑z躒l躓z躔c躕c躖d躗w躘l躙l躚x躛w躜z躝l躞x躟r躠s躡n躢t躣q躥c躦z躧x躨k躩j躭d躮s躰t躱d躵h躶l躷a躸j躹j躻w躽y躾x躿k軀q軁l軃d軄z軆t軇d軉y車c軋y軌g軎w軏y軐x軑d軒x軓f軔r軕s軖k軗s軘t軙c軚d軜n軝q軟r軠k軡q転z軣h軤h軥q軦k軧d軨l軩d軪a軫z軬f軮y軰b軱g軲g軴z軵r軶e軷b軹z軺y軻k軼y軽q軾s軿p輀e輁g輂j較j輄g輅l輆k輈z載z輋s輌l輍y輎s輏y輐w輓w輔f輕q輖z輗n輘l輙z輚z輛l輝h輞w輟c輡k輢y輣p輤q輥g輦n輨g輩b輫p輬l輭r輮r輰y輱x輲c輴c輵g輶y輷h輸s輹f輺z輻f輽f輾z輿y轀w轁t轂g轃z轄x轅y轆l轇j轈c轉z轊w轍z轎j轏z轐b轑l轔l轖s轗k轘h俁y係x轛d轝y轞j轟h轠l轡p轢l倀c轤l轥l轪d轫r轭e轱g轲k轳l轵z轶y轷h轸z轹l轺y轼s轾z辁q辂l辄z辇n辋w辌l辍c辏c辒w辔p辘l傑j辚l辝c辠z辡b辢l辥x辦b辪x辬b辮b辯b農n辳n辵c辶c辷y辸r辻s込y辿c迃y迆y迉q迊b迋w迌t迍z迏d儈k迕w迖d迗e迚d迤y迥j迨d迩e迬z迮z迯t迱y迲k迳j迴h迵d迺n迻y凜l迾l逄p逅h逇d逈j逋b逍x逎q逑q逓d逕j逖t這z逜w逡q連l逥h逦l逧g逨l逪c逬b逭h逯l週z進j逳y逴c逵k逶w逷t逹d逺y勸q遀s遄c遅c遈s遊y運y遌e過g遐x遑h遒q遖n遘g遙y遛l遜x遝t遞d遟c遠y遡s遢t遤h遦g遨a遪c遫c遯d遲c咴h遴l遶r遷q選x遹y遺y遻e遼l遽j遾s邁m邂x邃s還h邅z邆t邇e邈m邉b邊b邋l邌l邏l邒t邔q邕y邖s邘y邙m邛q邜x邝k邟k邠b邡f邥s邧y邬w邭j邰t邲b邳p邴b邶b邷w邸d邺y邼k邾z邿s喹k郀k郂g郃h郄q郅z郆j郇x郈h郉x郍n嗩s郏j郐k郒l郓y郔y郕c郖d郗x郘l郙f嘆t郚w郛f郜g郟j郠g嘗c郢y郤x郥b郦l郪q郫p郮z郯t郱p郳n郵y郶b郷x郹j郻q郾y郿m鄀r鄁b鄃s鄄j鄆y鄇h鄈k鄉x嚕l鄊x鄌t鄎x鄏r鄐c鄑z鄓y嚶y鄔w鄕x鄗h鄚m鄛c鄜f鄝l鄞y鄟z鄠h鄡q鄢y鄣z鄥q鄦x鄨b鄫z鄬w鄭z鄮m鄯s鄰l鄱p鄲d鄴y鄵c鄶k鄷f鄸m團t鄺k鄻l鄼z鄽c鄾y酀y酁c酂z酃l酄h酆f酇z酈l酊d酎z酏y酐g酑y酓y酔z酕m酖d酘d酙z酛k酜f酟t酠q酡t酢c酤g酦f垸y酧c酩m酫c酭y酯z酰x酲c酴t酹l酺p酻z堝g酾s醀w醁l醂l醃y醄t醅p醊z醌k醍t醎x醏d醐h醑x醕c醖y醗p醘k醙s醜c醝c醞y醟y醠a醡z醢h醣t醤j醥p醧y醨l醩z醪l醫y醬j醭b醮j醯x醰t醱p醲n醳y醴l醵j醶y醷y醸n醹r醺x醻c醼y醽l醾m醿m墼j釁x釃s釄m釅y壎x釆b壘l釐l釓g釖d奩l釘d釚q釛h針z釞z釟b釡f釣d釦k釧c釨z釩f釬h釭g釮q釰r釱d釲s釳x釴y釵c釶s釷t釸x釹n釺q釻q釼r釽p釾y鈀b鈁f鈂c鈅y鈆q嬋c鈉n鈊x鈋e鈌j鈍d嬪p孌l鈐q鈒s鈓r鈔c鈕n鈗y鈘y鈛g鈜h鈞j導d鈡z鈢x鈣g鈤r鈥h鈦t鈧k屝f鈫q鈬d鈭z鈮n鈯t鈰s鈲g鈳k鈴l屨j鈶s鈷g鈸b鈹p鈺y鈻s鈼z鈽b鉁z鉂s鉃s鉄t鉆z鉈t鉉x鉊z鉋b鉌h鉍b鉎s鉏c鉐s鉑b鉓c鉕p鉖t鉘f鉙z鉚m鉛q鉜f鉝l鉟p鉠y鉣j鉤g鉦z崳y鉨x鉩x鉪d鉫j鉭t鉮s嵐l鉯y鉰s鉱k鉶x鉷h鉸j鉹c鉺e鉻g鉼b嶝d鉿h嶧y銀y銂z巒l銄x銆m銇l銈j銉y銊x銋r銌z師s銎q銏s銒x銓q銔p銖z銙k銚d銛x銜x銝x銞j銟c銢p銥y銦y銧g銩d銪y銭q銯k銲h銳r銴s廟m銵k廠c銶q銷x銸z銺z銻t鋀t鋁l鋂m鋄w鋅x鋆y鋇b鋈w鋉s鋋c鋌t鋎h鋑j鋕z鋖s鋗x鋘h鋙w鋚t鋛k忄x鋞x鋟q鋠s鋡h鋢l鋣y鋤c鋥z鋦j鋧x鋨e鋩m鋪p鋬p鋭r鋮c鋯g鋰l鋳z鋴z鋵t惚h鋷z鋸j鋹c鋺y愷k鋼g鋽d鋿s錀l慪o錁k錂l錃p慳q憒k錅l憮w錇p錉m錊z錋p錌a錍p錎x錏y懸x錑l錒a錓k錕k錗n錘c錙z錚z錜n錞c錟t錠d錡q錢q錣z錤j錥y錦j錧g錨m錩c錪t捲j錬l錭d錮g錯c錱z録l錳m錴l錵h錷g錸l錹k錺k錻b揮h錼n錽w錾z錿h鍀d鍁x鍄l鍆m鍇k鍈y鍉c鍊l鍋g鍌x鍍d鍏w鍐z鍑f鍒r鍓j鍔e鍕j鍖c鍗t撐c鍘z鍙h鍚y撖h鍛d撘d撢d撳q擋d鍞k鍟s鍠h鍡w鍢f鍣z鍤c鍥q鍦s鍧h鍨k鍩n鍪m鍫q鍬q鍭h鍯c鍱y鍲m鍳j鍵j鍶s鍷k鍸h鍹x攪j鍺z鍻j鍼z鍽b鍾z鍿z鎀x鎁y鎂m鎃p鎄a鎅j鎆q鎇m鎈c鎉d鎊b攵p鎋x鎌l鎍s數s鎏l鎐y鎑y鎒n鎓w鎔r鎕t鎖s鎘g鎙s鎛b鎜p鎝d鎞b鎟s鎠g鎡z鎤h暢c鎦l鎧k鎨s鎩s鎪s鎫w鎬g鎭z鎮z鎯l鎰y鎱y鎲t鎳n鎴x鎵j鎶g鎷m鎸j鎹k鎺h鎻s鎼x鎽f鎾w鎿n鏀l鏁s鏂o鏃z鏄t鏅x鏆g鏇x鏈l鏉s鏊a鏋m鏌m鏍l鏎b鏏w鏐l鏑d鏒s鏔y鏕l鏖a鏗k鏘q鏙c鏚q鏛s鏝m鏞y鏟c鏠f鏡j鏢b鏣s鏤l鏥x枧j枴g柙x鏧l鏨z鏩j鏪c鏫l鏬x鏭x鏮k鏯s鏰b鏱z鏲q鏳z鏴l鏵h鏶j鏸h鏹q鏺p鏻l鏼s鏽x鏾s鏿c鐀g鐁s鐂l鐃n鐄h鐅p鐆s鐇f鐈q鐉q鐊x鐋t鐌x鐎j鐏z鐐l鐑q鐒l鐓d鐔x鐕z鐖j鐗j鐘z鐙d鐚y鐛y鐜d鐝j鐞n鐟z鐠p鐡t鐣c鐤d鐥s鐦k鐧j鐨f鐩s鐪l鐫j鐬h鐭y鐮l鐯z鐰q鐱j鐲z鐴b鐵t榦g鐷y鐸d鐹g鐻j鐽d鐾b鐿y鑀a構g鑁z鑂x槔g鑄z鑅h鑆z鑇j鑈n鑉h鑊h鑋q鑌b樣y鑎g鑏n鑐x鑑j鑒j鑓y鑔c鑕z鑖m鑗l鑘l檑l鑜s鑝p檣q鑞l鑟d鑠s鑡c鑢l鑣b鑤b鑥l鑦x鑧k鑨l鑩e櫃g鑪l鑫x鑬j櫞y鑮b鑯j鑰y鑱c鑲x鑳j鑴x鑵g鑷n鑸l鑹c鑺q鑻p鑼l鑽z鑾l鑿z钀n钁j欽q钃z钄l钅j钆g钇y钊z钋p钌l钍t钏c钐s钑s钔m钕n钖y钗c钚b钛t钜j钣b钤q钪k钫f钬h钭t钯b钰y钲z钴g钶k钷p钸b钹b钼m钽t铄s铈s铉x铊s铋b铌n铍p铎d铏x铐k铑l铒e铓m铔y铕y铖c铗j铘y铙n铚z铛c铞d铟y铠k铢z汙w铤d铥d铦x铧h铨q铩s铪h铫d铮z铯s铳c铴t铵a铷r铹l铻w铼l铽t铿k锂l锃z锆g锇e锉c锊l锍l锎k锏j況k锒l锓q锔j锕a锖q锘n锛b锜q锝d锞k锟k锠c锢g锧z锩j锪h锫p锬t锱z锲q锴k锵q锶s锷e锸c锺z锼s锽h锾h锿a镂l镃z镄f镅m镆m镈b镉g镋t镌j镎n镏l镒y镓j镔b镕r镖b镗t镘m镙l镚b镛y镝d镞z镟x镠l镡t镢j镤p淺q镥l镦d镧l镨p镩c镫d镬h镯z镱y镲c镳b镴l镵c長c镸c镹j溈w镻d镼j镽l門m閂s閃s閄h滌d閆y閇b閈h閉b閊c開k閌k閍b閎h閏r閐s閑x閒x漁y閔m閕x閖l閗d閘z閙n閚z閛p閜x閝l閞b閠r閡h関g閣g閤g閦c閧h閨g閩m閪s閫k閬l閭l閮t閰j閱y閲y潿w閳c閴q閶c閷s閸k閹y閺w閻y閼e閽h閾y閿w闀h闁b濁z闄y闅w闆b闈w闉y闊k闋q闌l闍d闎q闏p闐t濾l闓k瀾l闕q闖c闗g闘d闙q闚k闛t關g闝p闞k闟x闠h闡c闤h闥t闦w闩s闫y闬h闱w闳h闵m闶k闼t闾l闿k阃k阄j阆l阇d阈y阊c阋x阌w阍h阏e阒q阓h阕q阖h阗t阘t阙j阚k阛h阝f阞l阠x烴t阡q阢w阣y阤t阥y阦y阧d阨a阩s阪b阫p阬k阭y阯z阰p阱j阷c阸e阹q阺d阼z阽d阾l陁t陂b陃b陉x陊d煥h陎s陏d陑e陒g陓y陔g陖j陗q陘x陙c陚w陜x陝s陞s陟z陠p陣z陦d陧n陫f燈d燜m陭q陮d爍s陰y陱j陲c陳c陴p陸l陹s険x陻y陼z陽y陾r陿x隀c隁y隂y隃y隄d隇w隈w隉n隊d隌a牾w隍h階j隑g隒y隓h犋j隕y隖w隗k隚t際j犖l隝d隞a犢d隟x隠y隡s隢r隣l隤t隥d隦p隨s隩a險x隬n隭e隮j隯d隰x隱y隲z隴l隵x隷l隸l隹z隺h隻z隼s隽j隿y雂q雃j雈h雉z雊g雋j雎j雐h雑z雒l雓y雔c雖s雗h雘h雙s雚g雛c雜z雝y雞j雟g雡l離l難n雤y雥z雦c雧j雩y雫n雬f獎j雭s雮m雯w雰f雱p雲y雳l雴c雵y雸a獮x電d雼d雽h雿d霁j霂m霃c霅z霆t霈p霊l霋q霌z霎s霏f霐h霑z霒y霔s霕t霗l霘d霙y霚w霛l霝l霟h霠y霡m霢m霤l霥m霦b霧w霨w霩k霪y霫x霬y霭a霮d霯t霰s霱y霳l霴d霵j霶p霷y霺w霼x瑋w霽j霾m霿m靀m靁l靂l靃h靄a靅f靆d靇l靈l靉a靊f靋l靌b靍h靎h靏h靐b璁c靑q靓j靔t靕z靗c靘q靚j靜j靝t靟f靣m靤p靦m靧h靨y靪d靫c靬j靭r靮d靯d靰w靱r靲q靵n靷y環h靸s靹n靺m靻z靼d靽b靾x靿y鞀t鞁b瓔y鞂j鞃h鞄p鞅y鞆b鞇y鞈g鞉t鞊j鞌a鞎h鞏g鞐k鞑d鞒q鞓t甌o鞔m鞕b鞖s鞗t鞙x鞚k甕w鞛b鞜t鞝s鞞b鞟k鞡l鞢x鞣r鞤b鞥e鞦q鞧q鞨h鞩q鞪m鞫j鞬j鞮d鞯j鞰w鞱t鞲g鞳t鞴b鞵x畢b鞶p鞷g鞸b鞹k鞺t鞻l鞼g當d鞽q鞾x鞿j韀j疇c韂c韃d韅x韆q韇d韈w韉j韊l痠s韋w韌r韍f韎m韏q韐g韑w韒q韓h韔c韕k痺b韖r韗y韘s韙w韚g韛b韜t韝g瘋f韞y韟g韠b韡w韢s韣d韤w韥d韨f韪w韫y韬t韮j韯x韰x韱x韲j韴z韷l韸p韹h韺y韻y韼p韽a韾y響x頀h頁y頂d頃q項x癉d頇h須x頉y癘l頊x頋e頌s頍k頎q頏h癤j預y頑w頒b頓d頔d頕d頖p頗p領l頙c頚j頛l頜h頝q頞e頟e頠w頡j頢k頣s頤y頥y頦k頧d頨y頩p頪l頫f頬j頭t頮h頯k頰j頱l頲t頳c頴y頵j頶h頷h頸j頹t頺t頻b頼l頽t頾z頿z顀c顁d顂l顃t顄h顅q顆k顇c顈j顉q顊y顋s題t額e顎e顏y眙y顐w顒y顓z顔y顕x顖x顗y願y顙s顚d顛d顜j眾z顝k類l顟l顠p顡w顢m顣c顤y顥h顦q顧g顨x顩y顪h顫c顬r顭m顮b顯x顰p瞇m顱l顲l顳n顴q顸h顼x颀q颉j颋t颌g颎j颏k颒h颔h颕y颙y颚e颛z颞n颟m颡s颣l颥r颦p風f颩d颪g颫f颬x颭z颮b颯s颰b颱t颲l颳g颴x颵x颶j颷b颸s颹w颺y颻y颼s颽k颾s颿f飀l飁x飂l飃p飄p飅l飆b飇b飈b飉l飊b飋s飌f飍x飏y飐z飑b飒s飓j飔s飕s飖y砦z飗l飙b飚b飛f飜f飝f飡c飢j飣d飤s飥t飦z飧s飨x飩t飪r飫y飬y飭c飮y飯f飰f飱s碭d飲y飳z飴y飵z飶b飷j確q飸t飹b飺c飻t飼s飽b飾s飿d餀h餁r餂t餃j餄h餅b餆y餇t餈c餉x養y餋j餌e磽q餍y餎l餏x餑b餒n餓e餔b餕j餖d餗s餘y餙s餚y餛h餜g餝s餞j餟c餠b餡x餢b餣y餤d餥f餦z餧w館g餩e餪n餫y餬h餭h餮t餯h餰j餱h餲a餳x礬f餵w餶g餷c餸s餹t餺b餻g餼x餽k餾l餿s饀t饂w饃m饄t饅m饆b饇y饈x饉j饊s饋k饌z饍s饎x饏d饐y饑j饒r饔y饕t饖w饗x饘z饚h饛m饜y饝m饞c饟x饠l饡z饢n饣s饤d饦t饧x饨t禪s饩x饪r饫y饬c饳d饴y饷x饸h饹g饻x饽b饾d馀y馂j馃g馄h馇c馉g馊s馌y馎b馐x馑j馓s馔z馕n馘g馛b馜n馝b馞b馟t馠h馡f馢j馣a馤a馥f馦x馧y馨x穎y穡s馪p馫x馬m馭y馮f馯h馰d馱t馲t馳c馴x馵z馶z馷p馸x馹r馺s馻y馼w馽z馾d馿l駀y竇d駂b駃j筅x駅y駆q駇w駈q駉j駊p駋z駌y筧j駍p駎z駏j駐z駑n駒j駓p駔z駕j駖l駗z駘t駙f駚y駛s駜b駝t駞t駟s箢y駠l駡m駢p駣t駤z駥r駦t駧d駨x駩q駪s駫j駬e駭h駮b簞d駰y駱l駲z駳d駴h駵l駶j駷s駸q駹m駺l駻h駼t駽x駾t駿j騀e騁c騂x騃a騄l騅z騆z騇s騈p騉k騊t騋l騌z騍k騎q騏q騐y騑f騒s験y騔g騕y騖w騗p騙p騚q騛f紆y騜h騝q騞h騟y騠t騡q騢x騣z純c騥r騦s騧g騨t絕j騩g騪s騬c騭z騮l騯p騰t騱x騲c騳d騴y綽c緄g騶z騷s騸s騹q騺z騻s騼l騽x騾l騿z縋z驁a驂c驃p驄c驅q縲l驆b驇z驈y驉x驊h驋b驌s驍x驎l驏z驐d繃b驑l驒t驓c驔d驕j驖t驗y驘l驙z驚j驛y驜y繽b驝t驞p驟z驠y驡l驢l驣t驤x驥j驦s驧j驨x驩h驪l驫b驲r驵z驷s驸f驺z驽n驿y骀d骁x骃y骅h骈p骉b骊l骍x骎q骐q骒k骓z骔z骕s骖c骘z骙k罷b骛w骜a骝l骞q骟s骠b骢c骣c骥j骦s骧x骩w骪w骫w骬y骭g骮y骯a骰t骱j骲b骳b骴c骵t羨x骶d骷k骹q骺h骻k骼g骽t骾g骿p羻q髀b髁k髂q髃y髄s髅l髆b髇x髈b髉b髋k髌b髎l髏l聖s髐x髑d髒z體t髕b髖k髗l髙g髚q髛k髜q髝l髞s髟b髠k髡k髢d肀y髣f髤x髥r髦m髧d髩b髪f髫t髬p髭z髮f髯r髰t胙z髱b髲b髳m髴f髷q髸g髹x髺k髻j髽z髾s髿s鬀t鬁l膣z鬄t鬅p鬆s鬇z鬈q鬉z鬊s鬋j鬌d鬍h鬎l鬏j鬐q鬑l鬒z鬓b鬔p鬕m鬖s鬗m鬘m鬙s鬚x鬛l鬜q鬝q鬞n鬟h鬠k臘l鬡n鬢b鬣l鬤r鬥d臥w鬧n鬨h鬩x鬪d鬬d鬭d鬮j鬯c鬰y艙c鬳y艤y鬴f鬵z鬶g鬷z鬸l鬹g鬺s鬻y鬽m鬾j鬿q魀g魃b魅m魆x魇y芐h魉l魊y魋t魍w魎l魐g魑c魒p魕j魖x魗c魘y魙z魚y荮z莖j魜r魝j魞a魟h魠t魡d魢j魣x魤e魥j魦s魧h菪d菴a魩m萇c魪j魫s魬b魭y魮p魯l魰w葒h魲l魳z魴f魶n魷y魸n魹m魺h魻x魼q魽h魾p魿l鮀t蒹j蓀s鮂q鮃p鮄f鮅b鮆c鮇w鮈j鮉d蓮l鮊b鮋y鮌g鮍p鮎n鮏x鮐t鮑b蔆l鮒f鮓z鮔j鮕g鮖s蔞l蕆c蕘r鮘c鮙t鮚j鮛s鮜h鮝x鮞e鮟a鮠w鮡z鮢z鮣y鮤l鮥l鮦t鮧y鮨y鮩b鮪w鮫j鮬k鮭g鮮x鮯g鮰h鮱l鮲m鮳k鮴x鮵t鮶j鮷t鮸m鮹s鮺z鮻s鮼q鮽y鮾n鯀g鯁g鯂s鯃w鯄q鯅s鯆p鯇h鯈t蘞l鯊s鯋s鯌k鯍m鯎c鯏l虼g鯐s鯑k鯒y鯓s鯔z鯕q鯖q鯗x鯘n鯚j鯛d鯜q鯝g鯞z鯟d鯠l鯡f鯢n鯣y鯤k蜴y鯦j鯧c鯨j鯩l鯪l螄s螈y鯬l鯭m鯮z鯯z鯰n鯱x鯲d鯳s鯴s鯵s鯶h鯷t鯹x鯺z鯻l鯼z鯽j鯾b鯿b鰀h蟛p鰁q鰂z鰃w蟣j鰄w鰅y鰆c鰇r鰈d鰉h鰊l鰋y鰌q鰍q鰎j鰏b蠶c鰑y蠻m衊m鰓s鰔j鰕x鰖t鰗h鰘s鰙r鰚h鰛w鰜j鰝h鰞w鰟p鰠s鰡l鰢m鰣s鰤s鰥g鰦z鰧t鰨t鰩y鰪e鰫y鰬q鰭q鰮w鰯r鰰h鰱l鰲a袼g裉k鰴h鰵m鰶j鰷t鰸q鰹j鰺s鰻m鰼x鰽q鰿j鱀j鱁z鱂j鱃x鱄z鱅y鱆z鱇k鱈x襖a襲x鱊y鱋q鱌x鱍b鱎j鱏x鱐s鱑h鱒z覈h觀g觔j鱔s鱕f鱖g鱗l鱘x鱙y鱚x鱛a鱜k鱞g鱟h鱠k鱡z鱢s鱣z鱤g鱥g計j鱦y鱧l鱨c鱩h鱪s鱫a訟s訥n訶h鱭j鱮x鱯h鱰s鱱l鱲l鱳l鱴m鱵z鱶x鱷e鱸l鱹g鱺l鱻x鱽d鱾j試s鱿y鲀t鲂f詬g鲃b鲄h鲅b詰j鲆p鲇n詳x鲈l鲉y鲊z鲋f詿g鲌b鲎h誄l鲏p鲐t鲑g鲒j鲓k鲔w鲕e鲖t鲗z鲘h鲙k鲚j鲛j鲝z鲞x鲟x誦s鲠g鲡l說s鲢l鲣j鲥s鲦t誹f鲧g鲨s鲩h鲪j鲫j鲬y鲭q鲮l鲯q鲰z鲱f鲲k鲳c鲴g鲵n鲶n鲷d鲹s鲺s諦d鲻z鲼f諭y鲽d鲾b鲿c鳀t諼x謄t鳁w鳂w鳄e謖s鳅q鳆f謾m譏j鳇h鳈q鳉j鳊b變b鳌a鳍q鳎t鳏g鳐y鳑p讞y鳓l鳔b鳕x鳗m鳘m鳙y鳚w鳜g鳝s鳟z鳠h鳡g鳢l鳣z鳤g鳥n鳦y鳧f豊l鳨l鳪b鳫y鳬f鳭d鳮j鳯f鳰n鳱g鳲s鳳f鳴m鳵b鳶y貞z買m費f貽y鳸h賀h鳹q鳺f鳻b鳼w鳽j鳾s鳿y鴀f鴁y鴂j鴃j鴄p賍z鴅h鴆z鴇b鴈y鴉y鴊z鴋f鴌f賚l賦f賫l鴎o賱y鴏d賲b鴐j鴑r鴒l鴓m鴔f鴕t鴖m鴗l鴘b鴙z鴚g鴛y鴜c鴝q鴞x鴟c鴠d鴡j鴣g鴤z鴥y鴦y鴧y鴨y鴩d鴪y贖s鴬y鴭d赾q鴰g鴱a鴲z鴳y鴴h鴵x鴶j鴷l鴸z鴹y鴺y鴻h鴼l鴽r趒t鴾m鴿g趕g鵁j鵂x鵃z鵄c鵅l鵆q鵇t鵈e趛y鵊j鵋j趢l鵌t鵍h鵎t趦z趬q趶k鵐w鵒y鵓b鵔j鵕j跊m鵖b鵗x鵘j鵙j鵚t鵛j鵜t鵝e鵞e鵟k鵠h鵢s鵣l鵤z跮d鵦l鵧p鵨s鵩f鵪a鵫z鵬p踇m鵭q鵮q鵯b踋j鵰d鵱l鵲q踐j鵳j鵴j鵵t鵶y鵷y踖j鵸q鵹l鵺y鵻z鵼k鵽d踜l鵾k鵿s鶀q踠w鶁j鶂y踤z踨z踼t鶄j鶅z鶆l鶇d蹅c蹆t蹌q蹎d蹏t鶉c鶊g鶋j鶌q鶍y鶎k鶏j鶐s鶑y鶒c鶓m躌w躤j鶖q躪l鶗t鶘h鶙t鶚e躳g躴l躼l軂l軅y軈y軍j鶜m鶝f鶞c鶟t鶠y鶡h鶢y鶣p鶤k鶥m鶦h軛e鶧y軞m軭k軯p軳p鶩w鶪j軸z輇q鶬c鶭f鶮h鶯y鶰y鶱x鶲w輑y鶴h鶵c輜z鶶t鶷x鶸r輠g鶹l鶺j鶻g鶼j輧p輪l輯j輳c輼w鶾h鶿c鷀c鷁y鷂y轋h轌x轓f鷄j鷅l鷆t鷇k鷈t鷉t鷊y鷋t鷌m鷍x鷎g鷏t鷐c鷑j鷒t鷓z轙y鷔a轚j鷕y鷖y轜e鷗o鷘c鷙z鷚l鷛y轣l鷝b鷞s鷟z鷠y鷡w鷢j鷣y鷤t鷥s鷦j鷨h鷩b鷪y鷫s鷬h辀z鷮j辎z鷯l辤c鷱g辧b鷲j鷳x鷴x鷵t鷶m鷷z鷸y鷹y鷺l鷻t鷼x鷽x辭c鷿p鸀z鸁l鸂x鸃y鸄j鸅z鸆y鸇z辴z鸈y鸊p鸋n鸌h辺d鸍m鸎y鸏m鸐d鸑y迀g鸒y鸓l鸔b鸕l鸖h鸗l鸘s鸙y鸚y鸛g鸜q鸝l鸞l鸠j鸢y鸤s鸧c鸨b鸩z鸪g鸫d鸬l鸮x鸰l鸱c鸲q鸴x鸶s鸷z鸸e鸹g鸺x鸻h鸼z鸾l鹀w鹁b鹂l鹄g鹆y鹇x迒h鹈t迓y鹉w鹋m鹌a鹍k迠c鹎b迡c鹐q鹑c迣z迦j迧c鹓y鹔s迶y鹖h鹗e迼j鹘g迿x鹙q鹚c鹛m逌y逘y鹜w鹝y鹞y鹟w鹠l鹡j鹢y鹣j鹥y鹦y鹨l鹩l鹪j逤s鹫j逩b逫z逰y鹬y鹭l鹮h鹯z鹱h鹲m鹳g鹵l鹷l鹸j鹹x鹻j鹼j鹽y鹾c逽n逿d遃y遆t遉z麁c麂j麃b麄c麅p麆z麇j麈z麉j麊m麋b麌y麍l麎c麏j達d麐l違w麑n麒q麔j遚c麕j麖j麗l麘x麙x麚j麛m麜l麝s麞z麟l麠j麢l麣y麤c麥m麧h麨c麩f麪m麫m麬f麭p麮q麯q麰m麱f麲x麳l麴q麵m麶c麷f麸f麹q麺m適s麼m麽m遬s麾h麿m遰d黀z遱l黁n遳c黃h黅j黆g黇t黈t黉h黊h黋k黌h黏n黐c黒h黓y黕d黖x黗t黙m黚q黛d黜c黝y點d黟y黠x黡y黢q黣m黤y黥q黦y黧l黨d黩d黪c黫y邍y黬y邎y黭y黮d邐l黯a黰z黱d黲c黳y黴m黵d黶y黷d黸l黹z黻f黼f黽m邗h黾m黿y鼀c鼁q鼂c鼃w鼄z鼅z鼆m鼇a邞f鼊b鼋y邨c鼌c邩h鼍t邫b邽g鼑d鼒z鼔g鼕d鼘y鼙p鼚c鼛g鼜c鼝y鼞t鼟t鼡n鼢f鼣f鼤w鼥b鼦d鼧t郋x鼨z郌g鼩q鼪s鼫s鼬y鼭s鼮t鼯w鼰j鼱j鼲h鼳j鼴y鼵t鼶s鼷x鼸x郞l鼹y鼺l鼼y鼽q鼾h鼿w齀w齁h齂x齃e齄z齅x郣b郩x齆w齇z齈n齉n齊q郰z郲l齌j齍z齎j齏j齑j齒c齓c齔c齕h齖y齗y齘x郼y齚z齛x齜z齝c齞y齟j齠t齡l齢l齣c齤q齥x鄅y鄋s齧n齨j鄍m齪c齫y齬y齭c鄒z齯n鄖y鄘y鄤m齱z齲q齳y齴y鄧d鄩x鄪b齶e齷w齸y齹c齺z齻d齼c齾y龀c龁h龂y鄳m鄹z龃j龅b龆t龇z龈y龉y龊c龌w龍l龏g鄿q酅x龑y龒l龓l龔g龕k龗l龘d龛k龜g龝q酨z龞b龢h龣j龤x龥y鳩j鳷z鴍w鴢y鴫t鴮w鴯e鵀r鵉l鵏b鵑j鵡w鵥k鶃y鶛j鶨c鶫d鶳s鶽s鷃y鷜l鷧y鷭f鷰y鷾y鸉y鹒g鹕h鹶j鹺c麀y麡q鼈b鼉t鼏m齋z齙b齦y齮y齰z齵y齽j龎p龖d龡c癷b唉a苯b瞓f硸n秎f斗d稥x坊f竕f告g龟g哼h解j擂l罖r芈m羒f羘z羛y羜z羣q羦h羪y羫q羭y羮g羳f羵f羶s羷l翂f裂l聁u膹f艈u漂p骑q砌q雀q蒊h蒶f蕒m蕡f是s伺c毝c苔t虲u蚠f蝊u蟇m衁h衇m衈e衖x衚h衜d衞w衟d衠z衯f衮g袰b袴k呀y訜f子z豶f亂l傯z僨f償c兇x喲Y嗯E堯Y塏K墳F墾K鶕A鶔r鶈q鱬r鱝f魵f髍m馩f馚f饙f餴f颃h霻f霣y雺w隫f闧u闇a閥f閟b間j镪q钿d鐼f鐢f鏦c鏜t鍂p奮f婁l媵y屢l嶠j幀z幗g憤f錖d錆q銽x橥z歸g殛j炷z甯n瘧n瘵z碡z糞f紛f羥q腳j膩n臺t莜y蓿x褸l誒e讜d讠y贁b趀c趓d趙z趸d跀y跥d輊z輒z轒f轕g遧z邚r邤x郬q郺y酳y酼h酽y醆z醈t醓t醔q醦c釀n釂j釈s釋s釒j釔y釕l釗z釙p釠l釢n釤s釥q鉀j釪h釫h釯m釿j鈃x鈄d鈇f鈈b鈎g鈏y鈑b鈖f鈙q鈚p鈝y鈟d鈠y鈨y鈩h鈱m鈵b鈾y鈿d鉅j鉇s鉒z鉔z鉗q鉞y鉡b鉢b鉥s鉧m鉬m鉲k鉳b鉵t鉽s鉾m銁j銃c銅t銍z銐c銑x銕t銗h銘m銠l銡j銣r銤m銨a銫s銬k銰a銱d銹x銼c銾h銿z鋃l鋊y鋍b鋏j鋐h鋒f鋓c鋔w鋜z鋝l鋫l鋱t鋲b鋶l鋻j鋾t錄l錈j錐z錔t錛b錝c錫x錰s錶b鍃h鍅f鍎t鍜x鍝y鍮t鍰h鍴d鎎k鎗q鎚c鎢w鎣y鎥t鏓c鏷p鐍j鐳l鐶h鐺d鑃d鑍y鑙j鑚z鑛k鑭l鑶c钂t钘x铇b锳y镮h镺a镾m閁m閅m閯s閵l闂h闃g闑n闒t闔h闢p闣d陥x陬z陯l韁j韄h頄q順s顑k飠s饁y饓c駁b駄t駯z騘c騤k騫q騵y鰐e鰒f鰳l鰾p鱉b鱓s鳋s鳒j鳛x鹧z鹴s黂f黺f鼖f齩y驀m髊c髨k髵e髶e髼p鬂b鬦d鬫k鬱y魈x魌q魓b魛d魨t魱h鮁b鮗d鮿z鯙c鯉l鯥l鯫z鯸h龐p"; - /// - /// 常用简体汉字 - /// - public const string SimplifiedChinese = "一乙二十丁厂七卜人入八九几儿了力乃刀又三于干亏士工土才寸下大丈与万上小口巾山千乞川亿个勺久凡及夕丸么广亡门义之尸弓己已子卫也女飞刃习叉马乡丰王井开夫天无元专云扎艺木五支厅不太犬区历尤友匹车巨牙屯比互切瓦止少日中冈贝内水见午牛手毛气升长仁什片仆化仇币仍仅斤爪反介父从今凶分乏公仓月氏勿欠风丹匀乌凤勾文六方火为斗忆订计户认心尺引丑巴孔队办以允予劝双书幻玉刊示末未击打巧正扑扒功扔去甘世古节本术可丙左厉右石布龙平灭轧东卡北占业旧帅归且旦目叶甲申叮电号田由史只央兄叼叫另叨叹四生失禾丘付仗代仙们仪白仔他斥瓜乎丛令用甩印乐句匆册犯外处冬鸟务包饥主市立闪兰半汁汇头汉宁穴它讨写让礼训必议讯记永司尼民出辽奶奴加召皮边发孕圣对台矛纠母幼丝式刑动扛寺吉扣考托老执巩圾扩扫地扬场耳共芒亚芝朽朴机权过臣再协西压厌在有百存而页匠夸夺灰达列死成夹轨邪划迈毕至此贞师尘尖劣光当早吐吓虫曲团同吊吃因吸吗屿帆岁回岂刚则肉网年朱先丢舌竹迁乔伟传乒乓休伍伏优伐延件任伤价份华仰仿伙伪自血向似后行舟全会杀合兆企众爷伞创肌朵杂危旬旨负各名多争色壮冲冰庄庆亦刘齐交次衣产决充妄闭问闯羊并关米灯州汗污江池汤忙兴宇守宅字安讲军许论农讽设访寻那迅尽导异孙阵阳收阶阴防奸如妇好她妈戏羽观欢买红纤级约纪驰巡寿弄麦形进戒吞远违运扶抚坛技坏扰拒找批扯址走抄坝贡攻赤折抓扮抢孝均抛投坟抗坑坊抖护壳志扭块声把报却劫芽花芹芬苍芳严芦劳克苏杆杠杜材村杏极李杨求更束豆两丽医辰励否还歼来连步坚旱盯呈时吴助县里呆园旷围呀吨足邮男困吵串员听吩吹呜吧吼别岗帐财针钉告我乱利秃秀私每兵估体何但伸作伯伶佣低你住位伴身皂佛近彻役返余希坐谷妥含邻岔肝肚肠龟免狂犹角删条卵岛迎饭饮系言冻状亩况床库疗应冷这序辛弃冶忘闲间闷判灶灿弟汪沙汽沃泛沟没沈沉怀忧快完宋宏牢究穷灾良证启评补初社识诉诊词译君灵即层尿尾迟局改张忌际陆阿陈阻附妙妖妨努忍劲鸡驱纯纱纳纲驳纵纷纸纹纺驴纽奉玩环武青责现表规抹拢拔拣担坦押抽拐拖拍者顶拆拥抵拘势抱垃拉拦拌幸招坡披拨择抬其取苦若茂苹苗英范直茄茎茅林枝杯柜析板松枪构杰述枕丧或画卧事刺枣雨卖矿码厕奔奇奋态欧垄妻顷转斩轮软到非叔肯齿些虎虏肾贤尚旺具果味昆国昌畅明易昂典固忠咐呼鸣咏呢岸岩帖罗帜岭凯败贩购图钓制知垂牧物乖刮秆和季委佳侍供使例版侄侦侧凭侨佩货依的迫质欣征往爬彼径所舍金命斧爸采受乳贪念贫肤肺肢肿胀朋股肥服胁周昏鱼兔狐忽狗备饰饱饲变京享店夜庙府底剂郊废净盲放刻育闸闹郑券卷单炒炊炕炎炉沫浅法泄河沾泪油泊沿泡注泻泳泥沸波泼泽治怖性怕怜怪学宝宗定宜审宙官空帘实试郎诗肩房诚衬衫视话诞询该详建肃录隶居届刷屈弦承孟孤陕降限妹姑姐姓始驾参艰线练组细驶织终驻驼绍经贯奏春帮珍玻毒型挂封持项垮挎城挠政赴赵挡挺括拴拾挑指垫挣挤拼挖按挥挪某甚革荐巷带草茧茶荒茫荡荣故胡南药标枯柄栋相查柏柳柱柿栏树要咸威歪研砖厘厚砌砍面耐耍牵残殃轻鸦皆背战点临览竖省削尝是盼眨哄显哑冒映星昨畏趴胃贵界虹虾蚁思蚂虽品咽骂哗咱响哈咬咳哪炭峡罚贱贴骨钞钟钢钥钩卸缸拜看矩怎牲选适秒香种秋科重复竿段便俩贷顺修保促侮俭俗俘信皇泉鬼侵追俊盾待律很须叙剑逃食盆胆胜胞胖脉勉狭狮独狡狱狠贸怨急饶蚀饺饼弯将奖哀亭亮度迹庭疮疯疫疤姿亲音帝施闻阀阁差养美姜叛送类迷前首逆总炼炸炮烂剃洁洪洒浇浊洞测洗活派洽染济洋洲浑浓津恒恢恰恼恨举觉宣室宫宪突穿窃客冠语扁袄祖神祝误诱说诵垦退既屋昼费陡眉孩除险院娃姥姨姻娇怒架贺盈勇怠柔垒绑绒结绕骄绘给络骆绝绞统耕耗艳泰珠班素蚕顽盏匪捞栽捕振载赶起盐捎捏埋捉捆捐损都哲逝捡换挽热恐壶挨耻耽恭莲莫荷获晋恶真框桂档桐株桥桃格校核样根索哥速逗栗配翅辱唇夏础破原套逐烈殊顾轿较顿毙致柴桌虑监紧党晒眠晓鸭晃晌晕蚊哨哭恩唤啊唉罢峰圆贼贿钱钳钻铁铃铅缺氧特牺造乘敌秤租积秧秩称秘透笔笑笋债借值倚倾倒倘俱倡候俯倍倦健臭射躬息徒徐舰舱般航途拿爹爱颂翁脆脂胸胳脏胶脑狸狼逢留皱饿恋桨浆衰高席准座脊症病疾疼疲效离唐资凉站剖竞部旁旅畜阅羞瓶拳粉料益兼烤烘烦烧烛烟递涛浙涝酒涉消浩海涂浴浮流润浪浸涨烫涌悟悄悔悦害宽家宵宴宾窄容宰案请朗诸读扇袜袖袍被祥课谁调冤谅谈谊剥恳展剧屑弱陵陶陷陪娱娘通能难预桑绢绣验继球理捧堵描域掩捷排掉堆推掀授教掏掠培接控探据掘职基著勒黄萌萝菌菜萄菊萍菠营械梦梢梅检梳梯桶救副票戚爽聋袭盛雪辅辆虚雀堂常匙晨睁眯眼悬野啦晚啄距跃略蛇累唱患唯崖崭崇圈铜铲银甜梨犁移笨笼笛符第敏做袋悠偿偶偷您售停偏假得衔盘船斜盒鸽悉欲彩领脚脖脸脱象够猜猪猎猫猛馅馆凑减毫麻痒痕廊康庸鹿盗章竟商族旋望率着盖粘粗粒断剪兽清添淋淹渠渐混渔淘液淡深婆梁渗情惜惭悼惧惕惊惨惯寇寄宿窑密谋谎祸谜逮敢屠弹随蛋隆隐婚婶颈绩绪续骑绳维绵绸绿琴斑替款堪搭塔越趁趋超提堤博揭喜插揪搜煮援裁搁搂搅握揉斯期欺联散惹葬葛董葡敬葱落朝辜葵棒棋植森椅椒棵棍棉棚棕惠惑逼厨厦硬确雁殖裂雄暂雅辈悲紫辉敞赏掌晴暑最量喷晶喇遇喊景践跌跑遗蛙蛛蜓喝喂喘喉幅帽赌赔黑铸铺链销锁锄锅锈锋锐短智毯鹅剩稍程稀税筐等筑策筛筒答筋筝傲傅牌堡集焦傍储奥街惩御循艇舒番释禽腊脾腔鲁猾猴然馋装蛮就痛童阔善羡普粪尊道曾焰港湖渣湿温渴滑湾渡游滋溉愤慌惰愧愉慨割寒富窜窝窗遍裕裤裙谢谣谦属屡强粥疏隔隙絮嫂登缎缓编骗缘瑞魂肆摄摸填搏塌鼓摆携搬摇搞塘摊蒜勤鹊蓝墓幕蓬蓄蒙蒸献禁楚想槐榆楼概赖酬感碍碑碎碰碗碌雷零雾雹输督龄鉴睛睡睬鄙愚暖盟歇暗照跨跳跪路跟遣蛾蜂嗓置罪罩错锡锣锤锦键锯矮辞稠愁筹签简毁舅鼠催傻像躲微愈遥腰腥腹腾腿触解酱痰廉新韵意粮数煎塑慈煤煌满漠源滤滥滔溪溜滚滨粱滩慎誉塞谨福群殿辟障嫌嫁叠缝缠静碧璃墙撇嘉摧截誓境摘摔聚蔽慕暮蔑模榴榜榨歌遭酷酿酸磁愿需弊裳颗嗽蜻蜡蝇蜘赚锹锻舞稳算箩管僚鼻魄貌膜膊膀鲜疑馒裹敲豪膏遮腐瘦辣竭端旗精歉熄熔漆漂漫滴演漏慢寨赛察蜜谱嫩翠熊凳骡缩慧撕撒趣趟撑播撞撤增聪鞋蕉蔬横槽樱橡飘醋醉震霉瞒题暴瞎影踢踏踩踪蝶蝴嘱墨镇靠稻黎稿稼箱箭篇僵躺僻德艘膝膛熟摩颜毅糊遵潜潮懂额慰劈操燕薯薪薄颠橘整融醒餐嘴蹄器赠默镜赞篮邀衡膨雕磨凝辨辩糖糕燃澡激懒壁避缴戴擦鞠藏霜霞瞧蹈螺穗繁辫赢糟糠燥臂翼骤鞭覆蹦镰翻鹰警攀蹲颤瓣爆疆壤耀躁嚼嚷籍魔灌蠢霸露囊罐"; - /// - /// 英文字母 - /// - public const string Letters = "abcdefghijklmnopqrstuvwxyz"; - /// - /// 数字 - /// - public const string Numbers = "0123456789"; - } -} \ No newline at end of file diff --git a/src/Util/Helpers/Convert.cs b/src/Util/Helpers/Convert.cs deleted file mode 100644 index 15a106651..000000000 --- a/src/Util/Helpers/Convert.cs +++ /dev/null @@ -1,279 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Util.Helpers { - /// - /// 类型转换 - /// - public static class Convert { - /// - /// 转换为32位整型 - /// - /// 输入值 - public static int ToInt( object input ) { - return ToIntOrNull( input ) ?? 0; - } - - /// - /// 转换为32位可空整型 - /// - /// 输入值 - public static int? ToIntOrNull( object input ) { - var success = int.TryParse( input.SafeString(), out var result ); - if( success ) - return result; - try { - var temp = ToDoubleOrNull( input, 0 ); - if( temp == null ) - return null; - return System.Convert.ToInt32( temp ); - } - catch { - return null; - } - } - - /// - /// 转换为64位整型 - /// - /// 输入值 - public static long ToLong( object input ) { - return ToLongOrNull( input ) ?? 0; - } - - /// - /// 转换为64位可空整型 - /// - /// 输入值 - public static long? ToLongOrNull( object input ) { - var success = long.TryParse( input.SafeString(), out var result ); - if( success ) - return result; - try { - var temp = ToDecimalOrNull( input, 0 ); - if( temp == null ) - return null; - return System.Convert.ToInt64( temp ); - } - catch { - return null; - } - } - - /// - /// 转换为32位浮点型,并按指定小数位舍入 - /// - /// 输入值 - /// 小数位数 - public static float ToFloat( object input, int? digits = null ) { - return ToFloatOrNull( input, digits ) ?? 0; - } - - /// - /// 转换为32位可空浮点型,并按指定小数位舍入 - /// - /// 输入值 - /// 小数位数 - public static float? ToFloatOrNull( object input, int? digits = null ) { - var success = float.TryParse( input.SafeString(), out var result ); - if( !success ) - return null; - if( digits == null ) - return result; - return (float)Math.Round( result, digits.Value ); - } - - /// - /// 转换为64位浮点型,并按指定小数位舍入,温馨提示:4舍6入5成双 - /// - /// 输入值 - /// 小数位数 - public static double ToDouble( object input, int? digits = null ) { - return ToDoubleOrNull( input, digits ) ?? 0; - } - - /// - /// 转换为64位可空浮点型,并按指定小数位舍入,温馨提示:4舍6入5成双 - /// - /// 输入值 - /// 小数位数 - public static double? ToDoubleOrNull( object input, int? digits = null ) { - var success = double.TryParse( input.SafeString(), out var result ); - if( !success ) - return null; - if( digits == null ) - return result; - return Math.Round( result, digits.Value ); - } - - /// - /// 转换为128位浮点型,并按指定小数位舍入,温馨提示:4舍6入5成双 - /// - /// 输入值 - /// 小数位数 - public static decimal ToDecimal( object input, int? digits = null ) { - return ToDecimalOrNull( input, digits ) ?? 0; - } - - /// - /// 转换为128位可空浮点型,并按指定小数位舍入,温馨提示:4舍6入5成双 - /// - /// 输入值 - /// 小数位数 - public static decimal? ToDecimalOrNull( object input, int? digits = null ) { - var success = decimal.TryParse( input.SafeString(), out var result ); - if( !success ) - return null; - if( digits == null ) - return result; - return Math.Round( result, digits.Value ); - } - - /// - /// 转换为布尔值 - /// - /// 输入值 - public static bool ToBool( object input ) { - return ToBoolOrNull( input ) ?? false; - } - - /// - /// 转换为可空布尔值 - /// - /// 输入值 - public static bool? ToBoolOrNull( object input ) { - bool? value = GetBool( input ); - if( value != null ) - return value.Value; - return bool.TryParse( input.SafeString(), out var result ) ? (bool?)result : null; - } - - /// - /// 获取布尔值 - /// - private static bool? GetBool( object input ) { - switch( input.SafeString().ToLower() ) { - case "0": - return false; - case "否": - return false; - case "不": - return false; - case "no": - return false; - case "fail": - return false; - case "1": - return true; - case "是": - return true; - case "ok": - return true; - case "yes": - return true; - default: - return null; - } - } - - /// - /// 转换为日期 - /// - /// 输入值 - public static DateTime ToDate( object input ) { - return ToDateOrNull(input) ?? DateTime.MinValue; - } - - /// - /// 转换为可空日期 - /// - /// 输入值 - public static DateTime? ToDateOrNull( object input ) { - return DateTime.TryParse( input.SafeString(), out var result ) ? (DateTime?)result : null; - } - - /// - /// 转换为Guid - /// - /// 输入值 - public static Guid ToGuid( object input ) { - return ToGuidOrNull( input ) ?? Guid.Empty; - } - - /// - /// 转换为可空Guid - /// - /// 输入值 - public static Guid? ToGuidOrNull( object input ) { - return Guid.TryParse( input.SafeString(), out var result ) ? (Guid?)result : null; - } - - /// - /// 转换为Guid集合 - /// - /// 以逗号分隔的Guid集合字符串,范例:83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A - public static List ToGuidList( string input ) { - return ToList( input ); - } - - /// - /// 泛型集合转换 - /// - /// 目标元素类型 - /// 以逗号分隔的元素集合字符串,范例:83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A - public static List ToList( string input ) { - var result = new List(); - if( string.IsNullOrWhiteSpace( input ) ) - return result; - var array = input.Split( ',' ); - result.AddRange( from each in array where !string.IsNullOrWhiteSpace( each ) select To( each ) ); - return result; - } - - /// - /// 通用泛型转换 - /// - /// 目标类型 - /// 输入值 - public static T To( object input ) { - if( input == null ) - return default( T ); - if( input is string && string.IsNullOrWhiteSpace( input.ToString() ) ) - return default( T ); - Type type = Common.GetType(); - try { - if( type.Name.ToLower() == "string" ) - return (T)(object)input.ToString(); - if( type.Name.ToLower() == "guid" ) - return (T)(object)new Guid( input.ToString() ); - if ( type.IsEnum ) - return Enum.Parse( input ); - if( input is IConvertible ) - return (T)System.Convert.ChangeType( input, type ); - return (T)input; - } - catch { - return default( T ); - } - } - - /// - /// 转换为字节数组 - /// - /// 输入值 - public static byte[] ToBytes( string input ) { - return ToBytes( input, Encoding.UTF8 ); - } - - /// - /// 转换为字节数组 - /// - /// 输入值 - /// 字符编码 - public static byte[] ToBytes( string input, Encoding encoding ) { - return string.IsNullOrWhiteSpace( input ) ? new byte[] { } : encoding.GetBytes( input ); - } - } -} diff --git a/src/Util/Helpers/Encrypt.cs b/src/Util/Helpers/Encrypt.cs deleted file mode 100644 index a05962281..000000000 --- a/src/Util/Helpers/Encrypt.cs +++ /dev/null @@ -1,334 +0,0 @@ -using System; -using System.Security.Cryptography; -using System.Text; -using Util.Helpers.Internal; - -namespace Util.Helpers { - /// - /// 加密操作 - /// 说明: - /// 1. AES加密整理自支付宝SDK - /// 2. RSA加密采用 https://github.com/stulzq/DotnetCore.RSA/blob/master/DotnetCore.RSA/RSAHelper.cs - /// - public static class Encrypt { - - #region Md5加密 - - /// - /// Md5加密,返回16位结果 - /// - /// 值 - public static string Md5By16( string value ) { - return Md5By16( value, Encoding.UTF8 ); - } - - /// - /// Md5加密,返回16位结果 - /// - /// 值 - /// 字符编码 - public static string Md5By16( string value, Encoding encoding ) { - return Md5( value, encoding, 4, 8 ); - } - - /// - /// Md5加密 - /// - private static string Md5( string value, Encoding encoding, int? startIndex, int? length ) { - if( string.IsNullOrWhiteSpace( value ) ) - return string.Empty; - var md5 = new MD5CryptoServiceProvider(); - string result; - try { - var hash = md5.ComputeHash( encoding.GetBytes( value ) ); - result = startIndex == null ? BitConverter.ToString( hash ) : BitConverter.ToString( hash, startIndex.SafeValue(), length.SafeValue() ); - } - finally { - md5.Clear(); - } - return result.Replace( "-", "" ); - } - - /// - /// Md5加密,返回32位结果 - /// - /// 值 - public static string Md5By32( string value ) { - return Md5By32( value, Encoding.UTF8 ); - } - - /// - /// Md5加密,返回32位结果 - /// - /// 值 - /// 字符编码 - public static string Md5By32( string value, Encoding encoding ) { - return Md5( value, encoding, null, null ); - } - - #endregion - - #region DES加密 - - /// - /// DES密钥,24位字符串 - /// - public static string DesKey = "#s^un2ye21fcv%|f0XpR,+vh"; - - /// - /// DES加密 - /// - /// 待加密的值 - public static string DesEncrypt( object value ) { - return DesEncrypt( value, DesKey ); - } - - /// - /// DES加密 - /// - /// 待加密的值 - /// 密钥,24位 - public static string DesEncrypt( object value, string key ) { - return DesEncrypt( value, key, Encoding.UTF8 ); - } - - /// - /// DES加密 - /// - /// 待加密的值 - /// 密钥,24位 - /// 编码 - public static string DesEncrypt( object value, string key, Encoding encoding ) { - string text = value.SafeString(); - if( ValidateDes( text, key ) == false ) - return string.Empty; - using( var transform = CreateDesProvider( key ).CreateEncryptor() ) { - return GetEncryptResult( text, encoding, transform ); - } - } - - /// - /// 验证Des加密参数 - /// - private static bool ValidateDes( string text, string key ) { - if( string.IsNullOrWhiteSpace( text ) || string.IsNullOrWhiteSpace( key ) ) - return false; - return key.Length == 24; - } - - /// - /// 创建Des加密服务提供程序 - /// - private static TripleDESCryptoServiceProvider CreateDesProvider( string key ) { - return new TripleDESCryptoServiceProvider { Key = Encoding.ASCII.GetBytes( key ), Mode = CipherMode.ECB, Padding = PaddingMode.PKCS7 }; - } - - /// - /// 获取加密结果 - /// - private static string GetEncryptResult( string value, Encoding encoding, ICryptoTransform transform ) { - var bytes = encoding.GetBytes( value ); - var result = transform.TransformFinalBlock( bytes, 0, bytes.Length ); - return System.Convert.ToBase64String( result ); - } - - /// - /// DES解密 - /// - /// 加密后的值 - public static string DesDecrypt( object value ) { - return DesDecrypt( value, DesKey ); - } - - /// - /// DES解密 - /// - /// 加密后的值 - /// 密钥,24位 - public static string DesDecrypt( object value, string key ) { - return DesDecrypt( value, key, Encoding.UTF8 ); - } - - /// - /// DES解密 - /// - /// 加密后的值 - /// 密钥,24位 - /// 编码 - public static string DesDecrypt( object value, string key, Encoding encoding ) { - string text = value.SafeString(); - if( !ValidateDes( text, key ) ) - return string.Empty; - using( var transform = CreateDesProvider( key ).CreateDecryptor() ) { - return GetDecryptResult( text, encoding, transform ); - } - } - - /// - /// 获取解密结果 - /// - private static string GetDecryptResult( string value, Encoding encoding, ICryptoTransform transform ) { - var bytes = System.Convert.FromBase64String( value ); - var result = transform.TransformFinalBlock( bytes, 0, bytes.Length ); - return encoding.GetString( result ); - } - - #endregion - - #region AES加密 - - /// - /// 128位0向量 - /// - private static byte[] _iv; - /// - /// 128位0向量 - /// - private static byte[] Iv { - get { - if ( _iv == null ) { - var size = 16; - _iv = new byte[size]; - for ( int i = 0; i < size; i++ ) - _iv[i] = 0; - } - return _iv; - } - } - - /// - /// AES密钥 - /// - public static string AesKey = "QaP1AF8utIarcBqdhYTZpVGbiNQ9M6IL"; - - /// - /// AES加密 - /// - /// 待加密的值 - public static string AesEncrypt( string value ) { - return AesEncrypt( value, AesKey ); - } - - /// - /// AES加密 - /// - /// 待加密的值 - /// 密钥 - public static string AesEncrypt( string value, string key ) { - return AesEncrypt( value, key, Encoding.UTF8 ); - } - - /// - /// AES加密 - /// - /// 待加密的值 - /// 密钥 - /// 编码 - public static string AesEncrypt( string value, string key, Encoding encoding ) { - if( string.IsNullOrWhiteSpace( value ) || string.IsNullOrWhiteSpace( key ) ) - return string.Empty; - var rijndaelManaged = CreateRijndaelManaged( key ); - using ( var transform = rijndaelManaged.CreateEncryptor( rijndaelManaged.Key, rijndaelManaged.IV ) ) { - return GetEncryptResult( value, encoding, transform ); - } - } - - /// - /// 创建RijndaelManaged - /// - private static RijndaelManaged CreateRijndaelManaged( string key ) { - return new RijndaelManaged { - Key = System.Convert.FromBase64String( key ), - Mode = CipherMode.CBC, - Padding = PaddingMode.PKCS7, - IV = Iv - }; - } - - /// - /// AES解密 - /// - /// 加密后的值 - public static string AesDecrypt( string value ) { - return AesDecrypt( value, AesKey ); - } - - /// - /// AES解密 - /// - /// 加密后的值 - /// 密钥 - public static string AesDecrypt( string value, string key ) { - return AesDecrypt( value, key, Encoding.UTF8 ); - } - - /// - /// AES解密 - /// - /// 加密后的值 - /// 密钥 - /// 编码 - public static string AesDecrypt( string value, string key, Encoding encoding ) { - if( string.IsNullOrWhiteSpace( value ) || string.IsNullOrWhiteSpace( key ) ) - return string.Empty; - var rijndaelManaged = CreateRijndaelManaged( key ); - using( var transform = rijndaelManaged.CreateDecryptor( rijndaelManaged.Key, rijndaelManaged.IV ) ) { - return GetDecryptResult( value, encoding, transform ); - } - } - - #endregion - - #region RSA签名 - - /// - /// RSA加密,采用 SHA1 算法 - /// - /// 待加密的值 - /// 密钥 - public static string RsaSign( string value, string key ) { - return RsaSign( value, key, Encoding.UTF8 ); - } - - /// - /// RSA加密,采用 SHA1 算法 - /// - /// 待加密的值 - /// 密钥 - /// 编码 - public static string RsaSign( string value, string key, Encoding encoding ) { - return RsaSign( value, key, encoding, RSAType.RSA ); - } - - /// - /// RSA加密,采用 SHA256 算法 - /// - /// 待加密的值 - /// 密钥 - public static string Rsa2Sign( string value, string key ) { - return Rsa2Sign( value, key, Encoding.UTF8 ); - } - - /// - /// RSA加密,采用 SHA256 算法 - /// - /// 待加密的值 - /// 密钥 - /// 编码 - public static string Rsa2Sign( string value, string key, Encoding encoding ) { - return RsaSign( value, key, encoding, RSAType.RSA2 ); - } - - /// - /// Rsa加密 - /// - private static string RsaSign( string value, string key, Encoding encoding, RSAType type ) { - if( string.IsNullOrWhiteSpace( value ) || string.IsNullOrWhiteSpace( key ) ) - return string.Empty; - var rsa = new RsaHelper( type, encoding, key ); - return rsa.Sign( value ); - } - - #endregion - } -} diff --git a/src/Util/Helpers/Enum.cs b/src/Util/Helpers/Enum.cs deleted file mode 100644 index 0375f5cae..000000000 --- a/src/Util/Helpers/Enum.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace Util.Helpers { - /// - /// 枚举操作 - /// - public static class Enum { - /// - /// 获取实例 - /// - /// 枚举类型 - /// 成员名或值,范例:Enum1枚举有成员A=0,则传入"A"或"0"获取 Enum1.A - public static TEnum Parse( object member ) { - string value = member.SafeString(); - if( string.IsNullOrWhiteSpace( value ) ) { - if( typeof( TEnum ).IsGenericType ) - return default( TEnum ); - throw new ArgumentNullException( nameof( member ) ); - } - return (TEnum)System.Enum.Parse( Common.GetType(), value, true ); - } - - /// - /// 获取成员名 - /// - /// 枚举类型 - /// 成员名、值、实例均可,范例:Enum1枚举有成员A=0,则传入Enum1.A或0,获取成员名"A" - public static string GetName( object member ) { - return GetName( Common.GetType(), member ); - } - - /// - /// 获取成员名 - /// - /// 枚举类型 - /// 成员名、值、实例均可 - public static string GetName( Type type, object member ) { - if( type == null ) - return string.Empty; - if( member == null ) - return string.Empty; - if( member is string ) - return member.ToString(); - if( type.GetTypeInfo().IsEnum == false ) - return string.Empty; - return System.Enum.GetName( type, member ); - } - - /// - /// 获取成员值 - /// - /// 枚举类型 - /// 成员名、值、实例均可,范例:Enum1枚举有成员A=0,可传入"A"、0、Enum1.A,获取值0 - public static int GetValue( object member ) { - return GetValue( Common.GetType(), member ); - } - - /// - /// 获取成员值 - /// - /// 枚举类型 - /// 成员名、值、实例均可 - public static int GetValue( Type type, object member ) { - string value = member.SafeString(); - if( string.IsNullOrWhiteSpace( value ) ) - throw new ArgumentNullException( nameof(member) ); - return (int)System.Enum.Parse( type, member.ToString(), true ); - } - - /// - /// 获取描述,使用System.ComponentModel.Description特性设置描述 - /// - /// 枚举类型 - /// 成员名、值、实例均可 - public static string GetDescription( object member ) { - return Reflection.GetDescription( GetName( member ) ); - } - - /// - /// 获取描述,使用System.ComponentModel.Description特性设置描述 - /// - /// 枚举类型 - /// 成员名、值、实例均可 - public static string GetDescription( Type type, object member ) { - return Reflection.GetDescription( type, GetName( type, member ) ); - } - - /// - /// 获取项集合,文本设置为Description,值为Value - /// - /// 枚举类型 - public static List GetItems() { - return GetItems( Common.GetType() ); - } - - /// - /// 获取项集合,文本设置为Description,值为Value - /// - /// 枚举类型 - public static List GetItems( Type type ) { - TypeInfo enumType = type.GetTypeInfo(); - if( enumType.IsEnum == false ) - throw new InvalidOperationException( $"类型 {type} 不是枚举" ); - var result = new List(); - foreach( var field in enumType.GetFields() ) - AddItem( type, result, field ); - return result.OrderBy( t => t.SortId ).ToList(); - } - - /// - /// 添加描述项 - /// - private static void AddItem( Type type, ICollection result, FieldInfo field ) { - if( !field.FieldType.GetTypeInfo().IsEnum ) - return; - var value = GetValue( type, field.Name ); - var description = Reflection.GetDescription( field ); - result.Add( new Item( description, value, value ) ); - } - } -} diff --git a/src/Util/Helpers/Id.cs b/src/Util/Helpers/Id.cs deleted file mode 100644 index 95ca6ea01..000000000 --- a/src/Util/Helpers/Id.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace Util.Helpers { - /// - /// Id生成器 - /// - public static class Id { - /// - /// Id - /// - private static string _id; - - /// - /// 设置Id - /// - /// Id - public static void SetId( string id ) { - _id = id; - } - - /// - /// 重置Id - /// - public static void Reset() { - _id = null; - } - - /// - /// 创建Id - /// - public static string ObjectId() { - return string.IsNullOrWhiteSpace( _id ) ? Util.Helpers.Internal.ObjectId.GenerateNewStringId() : _id; - } - - /// - /// 用Guid创建Id,去掉分隔符 - /// - public static string Guid() { - return string.IsNullOrWhiteSpace( _id ) ? System.Guid.NewGuid().ToString( "N" ) : _id; - } - } -} diff --git a/src/Util/Helpers/Internal/ObjectId.cs b/src/Util/Helpers/Internal/ObjectId.cs deleted file mode 100644 index ac5d1a876..000000000 --- a/src/Util/Helpers/Internal/ObjectId.cs +++ /dev/null @@ -1,478 +0,0 @@ -using System; -using System.Diagnostics; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Security.Cryptography; -using System.Text; -using System.Threading; - -namespace Util.Helpers.Internal { - /// - /// Id生成器,代码出自:https://github.com/tangxuehua/ecommon/blob/master/src/ECommon/Utilities/ObjectId.cs - /// - internal struct ObjectId : IComparable, IEquatable{ - // private static fields - private static readonly DateTime __unixEpoch; - private static readonly long __dateTimeMaxValueMillisecondsSinceEpoch; - private static readonly long __dateTimeMinValueMillisecondsSinceEpoch; - private static ObjectId __emptyInstance = default( ObjectId ); - private static int __staticMachine; - private static short __staticPid; - private static int __staticIncrement; // high byte will be masked out when generating new ObjectId - private static uint[] _lookup32 = Enumerable.Range( 0, 256 ).Select( i => { - string s = i.ToString( "x2" ); - return ( (uint)s[0] ) + ( (uint)s[1] << 16 ); - } ).ToArray(); - - // we're using 14 bytes instead of 12 to hold the ObjectId in memory but unlike a byte[] there is no additional object on the heap - // the extra two bytes are not visible to anyone outside of this class and they buy us considerable simplification - // an additional advantage of this representation is that it will serialize to JSON without any 64 bit overflow problems - private int _timestamp; - private int _machine; - private short _pid; - private int _increment; - - // static constructor - static ObjectId() { - __unixEpoch = new DateTime( 1970, 1, 1, 0, 0, 0, DateTimeKind.Utc ); - __dateTimeMaxValueMillisecondsSinceEpoch = ( DateTime.MaxValue - __unixEpoch ).Ticks / 10000; - __dateTimeMinValueMillisecondsSinceEpoch = ( DateTime.MinValue - __unixEpoch ).Ticks / 10000; - __staticMachine = GetMachineHash(); - __staticIncrement = ( new System.Random() ).Next(); - __staticPid = (short)GetCurrentProcessId(); - } - - // constructors - /// - /// Initializes a new instance of the ObjectId class. - /// - /// The bytes. - public ObjectId( byte[] bytes ) { - if( bytes == null ) { - throw new ArgumentNullException( "bytes" ); - } - Unpack( bytes, out _timestamp, out _machine, out _pid, out _increment ); - } - - /// - /// Initializes a new instance of the ObjectId class. - /// - /// The timestamp (expressed as a DateTime). - /// The machine hash. - /// The PID. - /// The increment. - public ObjectId( DateTime timestamp, int machine, short pid, int increment ) - : this( GetTimestampFromDateTime( timestamp ), machine, pid, increment ) { - } - - /// - /// Initializes a new instance of the ObjectId class. - /// - /// The timestamp. - /// The machine hash. - /// The PID. - /// The increment. - public ObjectId( int timestamp, int machine, short pid, int increment ) { - if( ( machine & 0xff000000 ) != 0 ) { - throw new ArgumentOutOfRangeException( "machine", "The machine value must be between 0 and 16777215 (it must fit in 3 bytes)." ); - } - if( ( increment & 0xff000000 ) != 0 ) { - throw new ArgumentOutOfRangeException( "increment", "The increment value must be between 0 and 16777215 (it must fit in 3 bytes)." ); - } - - _timestamp = timestamp; - _machine = machine; - _pid = pid; - _increment = increment; - } - - /// - /// Initializes a new instance of the ObjectId class. - /// - /// The value. - public ObjectId( string value ) { - if( value == null ) { - throw new ArgumentNullException( "value" ); - } - Unpack( ParseHexString( value ), out _timestamp, out _machine, out _pid, out _increment ); - } - - // public static properties - /// - /// Gets an instance of ObjectId where the value is empty. - /// - public static ObjectId Empty { - get { return __emptyInstance; } - } - - // public properties - /// - /// Gets the timestamp. - /// - public int Timestamp { - get { return _timestamp; } - } - - /// - /// Gets the machine. - /// - public int Machine { - get { return _machine; } - } - - /// - /// Gets the PID. - /// - public short Pid { - get { return _pid; } - } - - /// - /// Gets the increment. - /// - public int Increment { - get { return _increment; } - } - - /// - /// Gets the creation time (derived from the timestamp). - /// - public DateTime CreationTime { - get { return __unixEpoch.AddSeconds( _timestamp ); } - } - - // public operators - /// - /// Compares two ObjectIds. - /// - /// The first ObjectId. - /// The other ObjectId - /// True if the first ObjectId is less than the second ObjectId. - public static bool operator <( ObjectId lhs, ObjectId rhs ) { - return lhs.CompareTo( rhs ) < 0; - } - - /// - /// Compares two ObjectIds. - /// - /// The first ObjectId. - /// The other ObjectId - /// True if the first ObjectId is less than or equal to the second ObjectId. - public static bool operator <=( ObjectId lhs, ObjectId rhs ) { - return lhs.CompareTo( rhs ) <= 0; - } - - /// - /// Compares two ObjectIds. - /// - /// The first ObjectId. - /// The other ObjectId. - /// True if the two ObjectIds are equal. - public static bool operator ==( ObjectId lhs, ObjectId rhs ) { - return lhs.Equals( rhs ); - } - - /// - /// Compares two ObjectIds. - /// - /// The first ObjectId. - /// The other ObjectId. - /// True if the two ObjectIds are not equal. - public static bool operator !=( ObjectId lhs, ObjectId rhs ) { - return !( lhs == rhs ); - } - - /// - /// Compares two ObjectIds. - /// - /// The first ObjectId. - /// The other ObjectId - /// True if the first ObjectId is greather than or equal to the second ObjectId. - public static bool operator >=( ObjectId lhs, ObjectId rhs ) { - return lhs.CompareTo( rhs ) >= 0; - } - - /// - /// Compares two ObjectIds. - /// - /// The first ObjectId. - /// The other ObjectId - /// True if the first ObjectId is greather than the second ObjectId. - public static bool operator >( ObjectId lhs, ObjectId rhs ) { - return lhs.CompareTo( rhs ) > 0; - } - - // public static methods - /// - /// Generates a new ObjectId with a unique value. - /// - /// An ObjectId. - public static ObjectId GenerateNewId() { - return GenerateNewId( GetTimestampFromDateTime( DateTime.UtcNow ) ); - } - - /// - /// Generates a new ObjectId with a unique value (with the timestamp component based on a given DateTime). - /// - /// The timestamp component (expressed as a DateTime). - /// An ObjectId. - public static ObjectId GenerateNewId( DateTime timestamp ) { - return GenerateNewId( GetTimestampFromDateTime( timestamp ) ); - } - - /// - /// Generates a new ObjectId with a unique value (with the given timestamp). - /// - /// The timestamp component. - /// An ObjectId. - public static ObjectId GenerateNewId( int timestamp ) { - int increment = Interlocked.Increment( ref __staticIncrement ) & 0x00ffffff; // only use low order 3 bytes - return new ObjectId( timestamp, __staticMachine, __staticPid, increment ); - } - - /// - /// Generates a new ObjectId string with a unique value. - /// - /// The string value of the new generated ObjectId. - public static string GenerateNewStringId() { - return GenerateNewId().ToString(); - } - - /// - /// Packs the components of an ObjectId into a byte array. - /// - /// The timestamp. - /// The machine hash. - /// The PID. - /// The increment. - /// A byte array. - public static byte[] Pack( int timestamp, int machine, short pid, int increment ) { - if( ( machine & 0xff000000 ) != 0 ) { - throw new ArgumentOutOfRangeException( "machine", "The machine value must be between 0 and 16777215 (it must fit in 3 bytes)." ); - } - if( ( increment & 0xff000000 ) != 0 ) { - throw new ArgumentOutOfRangeException( "increment", "The increment value must be between 0 and 16777215 (it must fit in 3 bytes)." ); - } - - byte[] bytes = new byte[12]; - bytes[0] = (byte)( timestamp >> 24 ); - bytes[1] = (byte)( timestamp >> 16 ); - bytes[2] = (byte)( timestamp >> 8 ); - bytes[3] = (byte)( timestamp ); - bytes[4] = (byte)( machine >> 16 ); - bytes[5] = (byte)( machine >> 8 ); - bytes[6] = (byte)( machine ); - bytes[7] = (byte)( pid >> 8 ); - bytes[8] = (byte)( pid ); - bytes[9] = (byte)( increment >> 16 ); - bytes[10] = (byte)( increment >> 8 ); - bytes[11] = (byte)( increment ); - return bytes; - } - - /// - /// Parses a string and creates a new ObjectId. - /// - /// The string value. - /// A ObjectId. - public static ObjectId Parse( string s ) { - if( s == null ) { - throw new ArgumentNullException( "s" ); - } - if( s.Length != 24 ) { - throw new ArgumentOutOfRangeException( "s", "ObjectId string value must be 24 characters." ); - } - return new ObjectId( ParseHexString( s ) ); - } - - /// - /// Unpacks a byte array into the components of an ObjectId. - /// - /// A byte array. - /// The timestamp. - /// The machine hash. - /// The PID. - /// The increment. - public static void Unpack( byte[] bytes, out int timestamp, out int machine, out short pid, out int increment ) { - if( bytes == null ) { - throw new ArgumentNullException( "bytes" ); - } - if( bytes.Length != 12 ) { - throw new ArgumentOutOfRangeException( "bytes", "Byte array must be 12 bytes long." ); - } - timestamp = ( bytes[0] << 24 ) + ( bytes[1] << 16 ) + ( bytes[2] << 8 ) + bytes[3]; - machine = ( bytes[4] << 16 ) + ( bytes[5] << 8 ) + bytes[6]; - pid = (short)( ( bytes[7] << 8 ) + bytes[8] ); - increment = ( bytes[9] << 16 ) + ( bytes[10] << 8 ) + bytes[11]; - } - - // private static methods - /// - /// Gets the current process id. This method exists because of how CAS operates on the call stack, checking - /// for permissions before executing the method. Hence, if we inlined this call, the calling method would not execute - /// before throwing an exception requiring the try/catch at an even higher level that we don't necessarily control. - /// - [MethodImpl( MethodImplOptions.NoInlining )] - private static int GetCurrentProcessId() { - return Process.GetCurrentProcess().Id; - } - - private static int GetMachineHash() { - var hostName = Environment.MachineName; // use instead of Dns.HostName so it will work offline - var md5 = MD5.Create(); - var hash = md5.ComputeHash( Encoding.UTF8.GetBytes( hostName ) ); - return ( hash[0] << 16 ) + ( hash[1] << 8 ) + hash[2]; // use first 3 bytes of hash - } - - private static int GetTimestampFromDateTime( DateTime timestamp ) { - return (int)Math.Floor( ( ToUniversalTime( timestamp ) - __unixEpoch ).TotalSeconds ); - } - - // public methods - /// - /// Compares this ObjectId to another ObjectId. - /// - /// The other ObjectId. - /// A 32-bit signed integer that indicates whether this ObjectId is less than, equal to, or greather than the other. - public int CompareTo( ObjectId other ) { - int r = _timestamp.CompareTo( other._timestamp ); - if( r != 0 ) { return r; } - r = _machine.CompareTo( other._machine ); - if( r != 0 ) { return r; } - r = _pid.CompareTo( other._pid ); - if( r != 0 ) { return r; } - return _increment.CompareTo( other._increment ); - } - - /// - /// Compares this ObjectId to another ObjectId. - /// - /// The other ObjectId. - /// True if the two ObjectIds are equal. - public bool Equals( ObjectId rhs ) { - return - _timestamp == rhs._timestamp && - _machine == rhs._machine && - _pid == rhs._pid && - _increment == rhs._increment; - } - - /// - /// Compares this ObjectId to another object. - /// - /// The other object. - /// True if the other object is an ObjectId and equal to this one. - public override bool Equals( object obj ) { - if( obj is ObjectId ) { - return Equals( (ObjectId)obj ); - } - else { - return false; - } - } - - /// - /// Gets the hash code. - /// - /// The hash code. - public override int GetHashCode() { - int hash = 17; - hash = 37 * hash + _timestamp.GetHashCode(); - hash = 37 * hash + _machine.GetHashCode(); - hash = 37 * hash + _pid.GetHashCode(); - hash = 37 * hash + _increment.GetHashCode(); - return hash; - } - - /// - /// Converts the ObjectId to a byte array. - /// - /// A byte array. - public byte[] ToByteArray() { - return Pack( _timestamp, _machine, _pid, _increment ); - } - - /// - /// Returns a string representation of the value. - /// - /// A string representation of the value. - public override string ToString() { - return ToHexString( ToByteArray() ); - } - - /// - /// Parses a hex string into its equivalent byte array. - /// - /// The hex string to parse. - /// The byte equivalent of the hex string. - public static byte[] ParseHexString( string s ) { - if( s == null ) { - throw new ArgumentNullException( "s" ); - } - - if( s.Length % 2 == 1 ) { - throw new Exception( "The binary key cannot have an odd number of digits" ); - } - - byte[] arr = new byte[s.Length >> 1]; - - for( int i = 0; i < s.Length >> 1; ++i ) { - arr[i] = (byte)( ( GetHexVal( s[i << 1] ) << 4 ) + ( GetHexVal( s[( i << 1 ) + 1] ) ) ); - } - - return arr; - } - /// - /// Converts a byte array to a hex string. - /// - /// The byte array. - /// A hex string. - public static string ToHexString( byte[] bytes ) { - if( bytes == null ) { - throw new ArgumentNullException( "bytes" ); - } - var result = new char[bytes.Length * 2]; - for( int i = 0; i < bytes.Length; i++ ) { - var val = _lookup32[bytes[i]]; - result[2 * i] = (char)val; - result[2 * i + 1] = (char)( val >> 16 ); - } - return new string( result ); - } - /// - /// Converts a DateTime to number of milliseconds since Unix epoch. - /// - /// A DateTime. - /// Number of seconds since Unix epoch. - public static long ToMillisecondsSinceEpoch( DateTime dateTime ) { - var utcDateTime = ToUniversalTime( dateTime ); - return ( utcDateTime - __unixEpoch ).Ticks / 10000; - } - /// - /// Converts a DateTime to UTC (with special handling for MinValue and MaxValue). - /// - /// A DateTime. - /// The DateTime in UTC. - public static DateTime ToUniversalTime( DateTime dateTime ) { - if( dateTime == DateTime.MinValue ) { - return DateTime.SpecifyKind( DateTime.MinValue, DateTimeKind.Utc ); - } - else if( dateTime == DateTime.MaxValue ) { - return DateTime.SpecifyKind( DateTime.MaxValue, DateTimeKind.Utc ); - } - else { - return dateTime.ToUniversalTime(); - } - } - - private static int GetHexVal( char hex ) { - int val = (int)hex; - //For uppercase A-F letters: - //return val - (val < 58 ? 48 : 55); - //For lowercase a-f letters: - //return val - (val < 58 ? 48 : 87); - //Or the two combined, but a bit slower: - return val - ( val < 58 ? 48 : ( val < 97 ? 55 : 87 ) ); - } - } -} diff --git a/src/Util/Helpers/Internal/RSAHelper.cs b/src/Util/Helpers/Internal/RSAHelper.cs deleted file mode 100644 index 4bc7a724a..000000000 --- a/src/Util/Helpers/Internal/RSAHelper.cs +++ /dev/null @@ -1,295 +0,0 @@ -using System; -using System.IO; -using System.Security.Cryptography; -using System.Text; - -namespace Util.Helpers.Internal { - /// - /// RSA加解密 使用OpenSSL的公钥加密/私钥解密 - /// - /// 公私钥请使用openssl生成 ssh-keygen -t rsa 命令生成的公钥私钥是不行的 - /// - /// 作者:李志强 - /// 时间:2017年10月30日15:50:14 - /// QQ:501232752 - /// - internal class RsaHelper { - private readonly RSA _privateKeyRsaProvider; - private readonly RSA _publicKeyRsaProvider; - private readonly HashAlgorithmName _hashAlgorithmName; - private readonly Encoding _encoding; - - /// - /// 实例化RSAHelper - /// - /// 加密算法类型 RSA SHA1;RSA2 SHA256 密钥长度至少为2048 - /// 编码类型 - /// 私钥 - /// 公钥 - public RsaHelper( RSAType rsaType, Encoding encoding, string privateKey, string publicKey = null ) { - _encoding = encoding; - if( !string.IsNullOrEmpty( privateKey ) ) { - _privateKeyRsaProvider = CreateRsaProviderFromPrivateKey( privateKey ); - } - - if( !string.IsNullOrEmpty( publicKey ) ) { - _publicKeyRsaProvider = CreateRsaProviderFromPublicKey( publicKey ); - } - - _hashAlgorithmName = rsaType == RSAType.RSA ? HashAlgorithmName.SHA1 : HashAlgorithmName.SHA256; - } - - #region 使用私钥签名 - - /// - /// 使用私钥签名 - /// - /// 原始数据 - /// - public string Sign( string data ) { - byte[] dataBytes = _encoding.GetBytes( data ); - - var signatureBytes = _privateKeyRsaProvider.SignData( dataBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1 ); - - return System.Convert.ToBase64String( signatureBytes ); - } - - #endregion - - #region 使用公钥验证签名 - - /// - /// 使用公钥验证签名 - /// - /// 原始数据 - /// 签名 - /// - public bool Verify( string data, string sign ) { - byte[] dataBytes = _encoding.GetBytes( data ); - byte[] signBytes = System.Convert.FromBase64String( sign ); - - var verify = _publicKeyRsaProvider.VerifyData( dataBytes, signBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1 ); - - return verify; - } - - #endregion - - #region 解密 - - public string Decrypt( string cipherText ) { - if( _privateKeyRsaProvider == null ) { - throw new Exception( "_privateKeyRsaProvider is null" ); - } - return Encoding.UTF8.GetString( _privateKeyRsaProvider.Decrypt( System.Convert.FromBase64String( cipherText ), RSAEncryptionPadding.Pkcs1 ) ); - } - - #endregion - - #region 加密 - - public string Encrypt( string text ) { - if( _publicKeyRsaProvider == null ) { - throw new Exception( "_publicKeyRsaProvider is null" ); - } - return System.Convert.ToBase64String( _publicKeyRsaProvider.Encrypt( Encoding.UTF8.GetBytes( text ), RSAEncryptionPadding.Pkcs1 ) ); - } - - #endregion - - #region 使用私钥创建RSA实例 - - public RSA CreateRsaProviderFromPrivateKey( string privateKey ) { - var privateKeyBits = System.Convert.FromBase64String( privateKey ); - - var rsa = RSA.Create(); - var rsaParameters = new RSAParameters(); - - using( BinaryReader binr = new BinaryReader( new MemoryStream( privateKeyBits ) ) ) { - byte bt = 0; - ushort twobytes = 0; - twobytes = binr.ReadUInt16(); - if( twobytes == 0x8130 ) - binr.ReadByte(); - else if( twobytes == 0x8230 ) - binr.ReadInt16(); - else - throw new Exception( "Unexpected value read binr.ReadUInt16()" ); - - twobytes = binr.ReadUInt16(); - if( twobytes != 0x0102 ) - throw new Exception( "Unexpected version" ); - - bt = binr.ReadByte(); - if( bt != 0x00 ) - throw new Exception( "Unexpected value read binr.ReadByte()" ); - - rsaParameters.Modulus = binr.ReadBytes( GetIntegerSize( binr ) ); - rsaParameters.Exponent = binr.ReadBytes( GetIntegerSize( binr ) ); - rsaParameters.D = binr.ReadBytes( GetIntegerSize( binr ) ); - rsaParameters.P = binr.ReadBytes( GetIntegerSize( binr ) ); - rsaParameters.Q = binr.ReadBytes( GetIntegerSize( binr ) ); - rsaParameters.DP = binr.ReadBytes( GetIntegerSize( binr ) ); - rsaParameters.DQ = binr.ReadBytes( GetIntegerSize( binr ) ); - rsaParameters.InverseQ = binr.ReadBytes( GetIntegerSize( binr ) ); - } - - rsa.ImportParameters( rsaParameters ); - return rsa; - } - - #endregion - - #region 使用公钥创建RSA实例 - - public RSA CreateRsaProviderFromPublicKey( string publicKeyString ) { - // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1" - byte[] seqOid = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; - byte[] seq = new byte[15]; - - var x509Key = System.Convert.FromBase64String( publicKeyString ); - - // --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------ - using( MemoryStream mem = new MemoryStream( x509Key ) ) { - using( BinaryReader binr = new BinaryReader( mem ) ) //wrap Memory Stream with BinaryReader for easy reading - { - byte bt = 0; - ushort twobytes = 0; - - twobytes = binr.ReadUInt16(); - if( twobytes == 0x8130 ) //data read as little endian order (actual data order for Sequence is 30 81) - binr.ReadByte(); //advance 1 byte - else if( twobytes == 0x8230 ) - binr.ReadInt16(); //advance 2 bytes - else - return null; - - seq = binr.ReadBytes( 15 ); //read the Sequence OID - if( !CompareBytearrays( seq, seqOid ) ) //make sure Sequence for OID is correct - return null; - - twobytes = binr.ReadUInt16(); - if( twobytes == 0x8103 ) //data read as little endian order (actual data order for Bit String is 03 81) - binr.ReadByte(); //advance 1 byte - else if( twobytes == 0x8203 ) - binr.ReadInt16(); //advance 2 bytes - else - return null; - - bt = binr.ReadByte(); - if( bt != 0x00 ) //expect null byte next - return null; - - twobytes = binr.ReadUInt16(); - if( twobytes == 0x8130 ) //data read as little endian order (actual data order for Sequence is 30 81) - binr.ReadByte(); //advance 1 byte - else if( twobytes == 0x8230 ) - binr.ReadInt16(); //advance 2 bytes - else - return null; - - twobytes = binr.ReadUInt16(); - byte lowbyte = 0x00; - byte highbyte = 0x00; - - if( twobytes == 0x8102 ) //data read as little endian order (actual data order for Integer is 02 81) - lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus - else if( twobytes == 0x8202 ) { - highbyte = binr.ReadByte(); //advance 2 bytes - lowbyte = binr.ReadByte(); - } - else - return null; - byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order - int modsize = BitConverter.ToInt32( modint, 0 ); - - int firstbyte = binr.PeekChar(); - if( firstbyte == 0x00 ) { //if first byte (highest order) of modulus is zero, don't include it - binr.ReadByte(); //skip this null byte - modsize -= 1; //reduce modulus buffer size by 1 - } - - byte[] modulus = binr.ReadBytes( modsize ); //read the modulus bytes - - if( binr.ReadByte() != 0x02 ) //expect an Integer for the exponent data - return null; - int expbytes = (int)binr.ReadByte(); // should only need one byte for actual exponent data (for all useful values) - byte[] exponent = binr.ReadBytes( expbytes ); - - // ------- create RSACryptoServiceProvider instance and initialize with public key ----- - var rsa = RSA.Create(); - RSAParameters rsaKeyInfo = new RSAParameters { - Modulus = modulus, - Exponent = exponent - }; - rsa.ImportParameters( rsaKeyInfo ); - - return rsa; - } - - } - } - - #endregion - - #region 导入密钥算法 - - private int GetIntegerSize( BinaryReader binr ) { - byte bt = 0; - int count = 0; - bt = binr.ReadByte(); - if( bt != 0x02 ) - return 0; - bt = binr.ReadByte(); - - if( bt == 0x81 ) - count = binr.ReadByte(); - else - if( bt == 0x82 ) { - var highbyte = binr.ReadByte(); - var lowbyte = binr.ReadByte(); - byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; - count = BitConverter.ToInt32( modint, 0 ); - } - else { - count = bt; - } - - while( binr.ReadByte() == 0x00 ) { - count -= 1; - } - binr.BaseStream.Seek( -1, SeekOrigin.Current ); - return count; - } - - private bool CompareBytearrays( byte[] a, byte[] b ) { - if( a.Length != b.Length ) - return false; - int i = 0; - foreach( byte c in a ) { - if( c != b[i] ) - return false; - i++; - } - return true; - } - - #endregion - - } - - /// - /// RSA算法类型 - /// - public enum RSAType { - /// - /// SHA1 - /// - RSA = 0, - /// - /// RSA2 密钥长度至少为2048 - /// SHA256 - /// - RSA2 - } -} \ No newline at end of file diff --git a/src/Util/Helpers/Ioc.cs b/src/Util/Helpers/Ioc.cs deleted file mode 100644 index 9c8f100ee..000000000 --- a/src/Util/Helpers/Ioc.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Extensions.DependencyInjection; -using Util.Dependency; - -namespace Util.Helpers { - /// - /// 容器 - /// - public static class Ioc { - /// - /// 默认容器 - /// - internal static readonly Container DefaultContainer = new Container(); - - /// - /// 创建容器 - /// - /// 依赖配置 - public static IContainer CreateContainer( params IConfig[] configs ) { - var container = new Container(); - container.Register( null, builder => builder.EnableAop(), configs ); - return container; - } - - /// - /// 创建集合 - /// - /// 对象类型 - /// 服务名称 - public static List CreateList( string name = null ) { - return DefaultContainer.CreateList( name ); - } - - /// - /// 创建集合 - /// - /// 返回类型 - /// 对象类型 - /// 服务名称 - public static List CreateList( Type type, string name = null ) { - return ( (IEnumerable)DefaultContainer.CreateList( type, name ) ).ToList(); - } - - /// - /// 创建实例 - /// - /// 实例类型 - /// 服务名称 - public static T Create( string name = null ) { - return DefaultContainer.Create( name ); - } - - /// - /// 创建对象 - /// - /// 返回类型 - /// 对象类型 - /// 服务名称 - public static TResult Create( Type type, string name = null ) { - return (TResult)DefaultContainer.Create( type, name ); - } - - /// - /// 作用域开始 - /// - public static IScope BeginScope() { - return DefaultContainer.BeginScope(); - } - - /// - /// 注册依赖 - /// - /// 依赖配置 - public static void Register( params IConfig[] configs ) { - DefaultContainer.Register( null, builder=> builder.EnableAop(), configs ); - } - - /// - /// 注册依赖 - /// - /// 服务集合 - /// 依赖配置 - public static IServiceProvider Register( IServiceCollection services, params IConfig[] configs ) { - return DefaultContainer.Register( services, builder => builder.EnableAop(), configs ); - } - - /// - /// 释放容器 - /// - public static void Dispose() { - DefaultContainer.Dispose(); - } - } -} diff --git a/src/Util/Helpers/Json.cs b/src/Util/Helpers/Json.cs deleted file mode 100644 index 94bfef2ed..000000000 --- a/src/Util/Helpers/Json.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Newtonsoft.Json; - -namespace Util.Helpers { - /// - /// Json操作 - /// - public static class Json { - /// - /// 将Json字符串转换为对象 - /// - /// Json字符串 - public static T ToObject( string json ) { - if ( string.IsNullOrWhiteSpace( json ) ) - return default(T); - return JsonConvert.DeserializeObject( json ); - } - - /// - /// 将对象转换为Json字符串 - /// - /// 目标对象 - /// 是否将双引号转成单引号 - public static string ToJson( object target,bool isConvertToSingleQuotes = false ) { - if ( target == null ) - return "{}"; - var result = JsonConvert.SerializeObject( target ); - if ( isConvertToSingleQuotes ) - result = result.Replace( "\"", "'" ); - return result; - } - } -} diff --git a/src/Util/Helpers/Lambda.cs b/src/Util/Helpers/Lambda.cs deleted file mode 100644 index 163ed2ef8..000000000 --- a/src/Util/Helpers/Lambda.cs +++ /dev/null @@ -1,465 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using Util.Datas.Queries; - -namespace Util.Helpers { - /// - /// Lambda表达式操作 - /// - public static class Lambda { - - #region GetMember(获取成员) - - /// - /// 获取成员 - /// - /// 表达式,范例:t => t.Name - public static MemberInfo GetMember( Expression expression ) { - var memberExpression = GetMemberExpression( expression ); - return memberExpression?.Member; - } - - /// - /// 获取成员表达式 - /// - /// 表达式 - public static MemberExpression GetMemberExpression( Expression expression ) { - if( expression == null ) - return null; - switch( expression.NodeType ) { - case ExpressionType.Lambda: - return GetMemberExpression( ( (LambdaExpression)expression ).Body ); - case ExpressionType.Convert: - return GetMemberExpression( ( (UnaryExpression)expression ).Operand ); - case ExpressionType.MemberAccess: - return (MemberExpression)expression; - } - return null; - } - - #endregion - - #region GetName(获取成员名称) - - /// - /// 获取成员名称,范例:t => t.Name,返回 Name - /// - /// 表达式,范例:t => t.Name - public static string GetName( Expression expression ) { - var memberExpression = GetMemberExpression( expression ); - return GetMemberName( memberExpression ); - } - - /// - /// 获取成员名称 - /// - private static string GetMemberName( MemberExpression memberExpression ) { - if( memberExpression == null ) - return string.Empty; - string result = memberExpression.ToString(); - return result.Substring( result.IndexOf( "." ) + 1 ); - } - - #endregion - - #region GetNames(获取名称列表) - - /// - /// 获取名称列表 - /// - /// 实体类型 - /// 属性集合表达式,范例:t => new object[]{t.A,t.B} - public static List GetNames( Expression> expression ) { - var result = new List(); - if( expression == null ) - return result; - var arrayExpression = expression.Body as NewArrayExpression; - if( arrayExpression == null ) - return result; - foreach( var each in arrayExpression.Expressions ) - AddName( result, each ); - return result; - } - - /// - /// 添加名称 - /// - private static void AddName( List result, Expression expression ) { - var name = GetName( expression ); - if( string.IsNullOrWhiteSpace( name ) ) - return; - result.Add( name ); - } - - #endregion - - #region GetValue(获取值) - - /// - /// 获取值,范例:t => t.Name == "A",返回 A - /// - /// 表达式,范例:t => t.Name == "A" - public static object GetValue( Expression expression ) { - if( expression == null ) - return null; - switch( expression.NodeType ) { - case ExpressionType.Lambda: - return GetValue( ( (LambdaExpression)expression ).Body ); - case ExpressionType.Convert: - return GetValue( ( (UnaryExpression)expression ).Operand ); - case ExpressionType.Equal: - case ExpressionType.NotEqual: - case ExpressionType.GreaterThan: - case ExpressionType.LessThan: - case ExpressionType.GreaterThanOrEqual: - case ExpressionType.LessThanOrEqual: - return GetValue( ( (BinaryExpression)expression ).Right ); - case ExpressionType.Call: - return GetMethodCallExpressionValue( expression ); - case ExpressionType.MemberAccess: - return GetMemberValue( (MemberExpression)expression ); - case ExpressionType.Constant: - return GetConstantExpressionValue( expression ); - } - return null; - } - - /// - /// 获取方法调用表达式的值 - /// - private static object GetMethodCallExpressionValue( Expression expression ) { - var methodCallExpression = (MethodCallExpression)expression; - var value = GetValue( methodCallExpression.Arguments.FirstOrDefault() ); - if( value != null ) - return value; - return GetValue( methodCallExpression.Object ); - } - - /// - /// 获取属性表达式的值 - /// - private static object GetMemberValue( MemberExpression expression ) { - if( expression == null ) - return null; - var field = expression.Member as FieldInfo; - if( field != null ) { - var constValue = GetConstantExpressionValue( expression.Expression ); - return field.GetValue( constValue ); - } - var property = expression.Member as PropertyInfo; - if( property == null ) - return null; - if( expression.Expression == null ) - return property.GetValue( null ); - var value = GetMemberValue( expression.Expression as MemberExpression ); - if( value == null ) - return null; - return property.GetValue( value ); - } - - /// - /// 获取常量表达式的值 - /// - private static object GetConstantExpressionValue( Expression expression ) { - var constantExpression = (ConstantExpression)expression; - return constantExpression.Value; - } - - #endregion - - #region GetParameter(获取参数) - - /// - /// 获取参数,范例:t.Name,返回 t - /// - /// 表达式,范例:t.Name - public static ParameterExpression GetParameter( Expression expression ) { - if( expression == null ) - return null; - switch( expression.NodeType ) { - case ExpressionType.Lambda: - return GetParameter( ( (LambdaExpression)expression ).Body ); - case ExpressionType.Convert: - return GetParameter( ( (UnaryExpression)expression ).Operand ); - case ExpressionType.Equal: - case ExpressionType.NotEqual: - case ExpressionType.GreaterThan: - case ExpressionType.LessThan: - case ExpressionType.GreaterThanOrEqual: - case ExpressionType.LessThanOrEqual: - return GetParameter( ( (BinaryExpression)expression ).Left ); - case ExpressionType.MemberAccess: - return GetParameter( ( (MemberExpression)expression ).Expression ); - case ExpressionType.Call: - return GetParameter( ( (MethodCallExpression)expression ).Object ); - case ExpressionType.Parameter: - return (ParameterExpression)expression; - } - return null; - } - - #endregion - - #region GetConditionCount(获取查询条件个数) - - /// - /// 获取查询条件个数 - /// - /// 谓词表达式,范例1:t => t.Name == "A" ,结果1。 - /// 范例2:t => t.Name == "A" && t.Age =1 ,结果2。 - public static int GetConditionCount( LambdaExpression expression ) { - if( expression == null ) - return 0; - var result = expression.ToString().Replace( "AndAlso", "|" ).Replace( "OrElse", "|" ); - return result.Split( '|' ).Count(); - } - - #endregion - - #region GetAttribute(获取特性) - - /// - /// 获取特性 - /// - /// 特性类型 - /// 属性表达式 - public static TAttribute GetAttribute( Expression expression ) where TAttribute : Attribute { - var memberInfo = GetMember( expression ); - return memberInfo.GetCustomAttribute(); - } - - /// - /// 获取特性 - /// - /// 实体类型 - /// 属性类型 - /// 特性类型 - /// 属性表达式 - public static TAttribute GetAttribute( Expression> propertyExpression ) where TAttribute : Attribute { - return GetAttribute( propertyExpression ); - } - - /// - /// 获取特性 - /// - /// 属性类型 - /// 特性类型 - /// 属性表达式 - public static TAttribute GetAttribute( Expression> propertyExpression ) where TAttribute : Attribute { - return GetAttribute( propertyExpression ); - } - - #endregion - - #region GetAttributes(获取特性列表) - - /// - /// 获取特性列表 - /// - /// 实体类型 - /// 属性类型 - /// 特性类型 - /// 属性表达式 - public static IEnumerable GetAttributes( Expression> propertyExpression ) where TAttribute : Attribute { - var memberInfo = GetMember( propertyExpression ); - return memberInfo.GetCustomAttributes(); - } - - #endregion - - #region Constant(获取常量) - - /// - /// 获取常量表达式 - /// - /// 表达式 - /// 值 - public static ConstantExpression Constant( Expression expression, object value ) { - var memberExpression = expression as MemberExpression; - if( memberExpression == null ) - return Expression.Constant( value ); - return Expression.Constant( value, memberExpression.Type ); - } - - #endregion - - #region Equal(等于表达式) - - /// - /// 创建等于运算lambda表达式 - /// - /// 对象类型 - /// 属性名 - /// 值 - public static Expression> Equal( string propertyName, object value ) { - var parameter = CreateParameter(); - return parameter.Property( propertyName ) - .Equal( value ) - .ToLambda>( parameter ); - } - - /// - /// 创建参数 - /// - private static ParameterExpression CreateParameter() { - return Expression.Parameter( typeof( T ), "t" ); - } - - #endregion - - #region NotEqual(不等于表达式) - - /// - /// 创建不等于运算lambda表达式 - /// - /// 对象类型 - /// 属性名 - /// 值 - public static Expression> NotEqual( string propertyName, object value ) { - var parameter = CreateParameter(); - return parameter.Property( propertyName ) - .NotEqual( value ) - .ToLambda>( parameter ); - } - - #endregion - - #region Greater(大于表达式) - - /// - /// 创建大于运算lambda表达式 - /// - /// 对象类型 - /// 属性名 - /// 值 - public static Expression> Greater( string propertyName, object value ) { - var parameter = CreateParameter(); - return parameter.Property( propertyName ) - .Greater( value ) - .ToLambda>( parameter ); - } - - #endregion - - #region GreaterEqual(大于等于表达式) - - /// - /// 创建大于等于运算lambda表达式 - /// - /// 对象类型 - /// 属性名 - /// 值 - public static Expression> GreaterEqual( string propertyName, object value ) { - var parameter = CreateParameter(); - return parameter.Property( propertyName ) - .GreaterEqual( value ) - .ToLambda>( parameter ); - } - - #endregion - - #region Less(小于表达式) - - /// - /// 创建小于运算lambda表达式 - /// - /// 对象类型 - /// 属性名 - /// 值 - public static Expression> Less( string propertyName, object value ) { - var parameter = CreateParameter(); - return parameter.Property( propertyName ) - .Less( value ) - .ToLambda>( parameter ); - } - - #endregion - - #region LessEqual(小于等于表达式) - - /// - /// 创建小于等于运算lambda表达式 - /// - /// 对象类型 - /// 属性名 - /// 值 - public static Expression> LessEqual( string propertyName, object value ) { - var parameter = CreateParameter(); - return parameter.Property( propertyName ) - .LessEqual( value ) - .ToLambda>( parameter ); - } - - #endregion - - #region Starts(调用StartsWith方法) - - /// - /// 调用StartsWith方法 - /// - /// 对象类型 - /// 属性名 - /// 值 - public static Expression> Starts( string propertyName, string value ) { - var parameter = CreateParameter(); - return parameter.Property( propertyName ) - .StartsWith( value ) - .ToLambda>( parameter ); - } - - #endregion - - #region Ends(调用EndsWith方法) - - /// - /// 调用EndsWith方法 - /// - /// 对象类型 - /// 属性名 - /// 值 - public static Expression> Ends( string propertyName, string value ) { - var parameter = CreateParameter(); - return parameter.Property( propertyName ) - .EndsWith( value ) - .ToLambda>( parameter ); - } - - #endregion - - #region Contains(调用Contains方法) - - /// - /// 调用Contains方法 - /// - /// 对象类型 - /// 属性名 - /// 值 - public static Expression> Contains( string propertyName, object value ) { - var parameter = CreateParameter(); - return parameter.Property( propertyName ) - .Contains( value ) - .ToLambda>( parameter ); - } - - #endregion - - #region ParsePredicate(解析为谓词表达式) - - /// - /// 解析为谓词表达式 - /// - /// 实体类型 - /// 属性名 - /// 值 - /// 运算符 - public static Expression> ParsePredicate( string propertyName, object value, Operator @operator ) { - var parameter = Expression.Parameter( typeof( T ), "t" ); - return parameter.Property( propertyName ).Operation( @operator, value ).ToLambda>( parameter ); - } - - #endregion - } -} diff --git a/src/Util/Helpers/Random.cs b/src/Util/Helpers/Random.cs deleted file mode 100644 index 137b014cb..000000000 --- a/src/Util/Helpers/Random.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace Util.Helpers { - /// - /// 随机数操作 - /// - public class Random { - /// - /// 随机数 - /// - private readonly System.Random _random; - - /// - /// 初始化随机数 - /// - public Random() { - _random = new System.Random(); - } - - /// - /// 获取指定范围的随机整数 - /// - /// 最大值 - public int Next( int max ) { - return _random.Next( max ); - } - - /// - /// 获取指定范围的随机整数,该范围包括最小值,但不包括最大值 - /// - /// 最小值 - /// 最大值 - public int Next( int min, int max ) { - return _random.Next( min, max ); - } - - /// - /// 对集合随机排序 - /// - /// 集合元素类型 - /// 集合 - public static List Sort( IEnumerable array ) { - if( array == null ) - return null; - var random = new System.Random(); - var list = array.ToList(); - for( int i = 0; i < list.Count; i++ ) { - int index1 = random.Next( 0, list.Count ); - int index2 = random.Next( 0, list.Count ); - T temp = list[index1]; - list[index1] = list[index2]; - list[index2] = temp; - } - return list; - } - } -} diff --git a/src/Util/Helpers/Reflection.cs b/src/Util/Helpers/Reflection.cs deleted file mode 100644 index 442ed3250..000000000 --- a/src/Util/Helpers/Reflection.cs +++ /dev/null @@ -1,297 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.ComponentModel; -using System.IO; -using System.Reflection; -using System.ComponentModel.DataAnnotations; - -namespace Util.Helpers { - /// - /// 反射操作 - /// - public static class Reflection { - /// - /// 获取类型描述,使用DescriptionAttribute设置描述 - /// - /// 类型 - public static string GetDescription() { - return GetDescription( Common.GetType() ); - } - - /// - /// 获取类型成员描述,使用DescriptionAttribute设置描述 - /// - /// 类型 - /// 成员名称 - public static string GetDescription( string memberName ) { - return GetDescription( Common.GetType(), memberName ); - } - - /// - /// 获取类型成员描述,使用DescriptionAttribute设置描述 - /// - /// 类型 - /// 成员名称 - public static string GetDescription( Type type, string memberName ) { - if( type == null ) - return string.Empty; - if( string.IsNullOrWhiteSpace( memberName ) ) - return string.Empty; - return GetDescription( type.GetTypeInfo().GetMember( memberName ).FirstOrDefault() ); - } - - /// - /// 获取类型成员描述,使用DescriptionAttribute设置描述 - /// - /// 成员 - public static string GetDescription( MemberInfo member ) { - if( member == null ) - return string.Empty; - return member.GetCustomAttribute() is DescriptionAttribute attribute ? attribute.Description : member.Name; - } - - /// - /// 获取显示名称,使用DisplayNameAttribute设置显示名称 - /// - /// 类型 - public static string GetDisplayName() { - return GetDisplayName( Common.GetType() ); - } - - /// - /// 获取显示名称,使用DisplayAttribute或DisplayNameAttribute设置显示名称 - /// - public static string GetDisplayName( MemberInfo member ) { - if( member == null ) - return string.Empty; - if( member.GetCustomAttribute() is DisplayAttribute displayAttribute ) - return displayAttribute.Name; - if( member.GetCustomAttribute() is DisplayNameAttribute displayNameAttribute ) - return displayNameAttribute.DisplayName; - return string.Empty; - } - - /// - /// 获取显示名称或描述,使用DisplayNameAttribute设置显示名称,使用DescriptionAttribute设置描述 - /// - /// 类型 - public static string GetDisplayNameOrDescription() { - return GetDisplayNameOrDescription( Common.GetType() ); - } - - /// - /// 获取属性显示名称或描述,使用DisplayAttribute或DisplayNameAttribute设置显示名称,使用DescriptionAttribute设置描述 - /// - public static string GetDisplayNameOrDescription( MemberInfo member ) { - var result = GetDisplayName( member ); - return string.IsNullOrWhiteSpace( result ) ? GetDescription( member ) : result; - } - - /// - /// 获取实现了接口的所有具体类型 - /// - /// 接口类型 - /// 在该程序集中查找 - public static List GetTypesByInterface( Assembly assembly ) { - var typeInterface = typeof( TInterface ); - return assembly.GetTypes() - .Where( t => typeInterface.GetTypeInfo().IsAssignableFrom( t ) && t != typeInterface && t.GetTypeInfo().IsAbstract == false ) - .Select( t => CreateInstance( t ) ).ToList(); - } - - /// - /// 动态创建实例 - /// - /// 目标类型 - /// 类型 - /// 传递给构造函数的参数 - public static T CreateInstance( Type type, params object[] parameters ) { - return Util.Helpers.Convert.To( Activator.CreateInstance( type, parameters ) ); - } - - /// - /// 获取程序集 - /// - /// 程序集名称 - public static Assembly GetAssembly( string assemblyName ) { - return Assembly.Load( new AssemblyName( assemblyName ) ); - } - - /// - /// 是否布尔类型 - /// - /// 成员 - public static bool IsBool( MemberInfo member ) { - if( member == null ) - return false; - switch( member.MemberType ) { - case MemberTypes.TypeInfo: - return member.ToString() == "System.Boolean"; - case MemberTypes.Property: - return IsBool( (PropertyInfo)member ); - } - return false; - } - - /// - /// 是否布尔类型 - /// - private static bool IsBool( PropertyInfo property ) { - return property.PropertyType == typeof( bool ) || property.PropertyType == typeof( bool? ); - } - - /// - /// 是否枚举类型 - /// - /// 成员 - public static bool IsEnum( MemberInfo member ) { - if( member == null ) - return false; - switch( member.MemberType ) { - case MemberTypes.TypeInfo: - return ( (TypeInfo)member ).IsEnum; - case MemberTypes.Property: - return IsEnum( (PropertyInfo)member ); - } - return false; - } - - /// - /// 是否枚举类型 - /// - private static bool IsEnum( PropertyInfo property ) { - if( property.PropertyType.GetTypeInfo().IsEnum ) - return true; - var value = Nullable.GetUnderlyingType( property.PropertyType ); - if( value == null ) - return false; - return value.GetTypeInfo().IsEnum; - } - - /// - /// 是否日期类型 - /// - /// 成员 - public static bool IsDate( MemberInfo member ) { - if( member == null ) - return false; - switch( member.MemberType ) { - case MemberTypes.TypeInfo: - return member.ToString() == "System.DateTime"; - case MemberTypes.Property: - return IsDate( (PropertyInfo)member ); - } - return false; - } - - /// - /// 是否日期类型 - /// - private static bool IsDate( PropertyInfo property ) { - if( property.PropertyType == typeof( DateTime ) ) - return true; - if( property.PropertyType == typeof( DateTime? ) ) - return true; - return false; - } - - /// - /// 是否整型 - /// - /// 成员 - public static bool IsInt( MemberInfo member ) { - if( member == null ) - return false; - switch( member.MemberType ) { - case MemberTypes.TypeInfo: - return member.ToString() == "System.Int32" || member.ToString() == "System.Int16" || member.ToString() == "System.Int64"; - case MemberTypes.Property: - return IsInt( (PropertyInfo)member ); - } - return false; - } - - /// - /// 是否整型 - /// - private static bool IsInt( PropertyInfo property ) { - if( property.PropertyType == typeof( int ) ) - return true; - if( property.PropertyType == typeof( int? ) ) - return true; - if( property.PropertyType == typeof( short ) ) - return true; - if( property.PropertyType == typeof( short? ) ) - return true; - if( property.PropertyType == typeof( long ) ) - return true; - if( property.PropertyType == typeof( long? ) ) - return true; - return false; - } - - /// - /// 是否数值类型 - /// - /// 成员 - public static bool IsNumber( MemberInfo member ) { - if( member == null ) - return false; - if ( IsInt( member ) ) - return true; - switch( member.MemberType ) { - case MemberTypes.TypeInfo: - return member.ToString() == "System.Double" || member.ToString() == "System.Decimal" || member.ToString() == "System.Single"; - case MemberTypes.Property: - return IsNumber( (PropertyInfo)member ); - } - return false; - } - - /// - /// 是否数值类型 - /// - private static bool IsNumber( PropertyInfo property ) { - if( property.PropertyType == typeof( double ) ) - return true; - if( property.PropertyType == typeof( double? ) ) - return true; - if( property.PropertyType == typeof( decimal ) ) - return true; - if( property.PropertyType == typeof( decimal? ) ) - return true; - if( property.PropertyType == typeof( float ) ) - return true; - if( property.PropertyType == typeof( float? ) ) - return true; - return false; - } - - /// - /// 是否泛型集合 - /// - /// 类型 - public static bool IsGenericCollection( Type type ) { - if ( !type.IsGenericType ) - return false; - var typeDefinition = type.GetGenericTypeDefinition(); - return typeDefinition == typeof( IEnumerable<> ) - || typeDefinition == typeof( IReadOnlyCollection<> ) - || typeDefinition == typeof( IReadOnlyList<> ) - || typeDefinition == typeof( ICollection<> ) - || typeDefinition == typeof( IList<> ) - || typeDefinition == typeof( List<> ); - } - - /// - /// 从目录中获取所有程序集 - /// - /// 目录绝对路径 - public static List GetAssemblies( string directoryPath ) { - return Directory.GetFiles( directoryPath, "*.*", SearchOption.AllDirectories ).ToList() - .Where( t => t.EndsWith( ".exe" ) || t.EndsWith( ".dll" ) ) - .Select( path => Assembly.Load( new AssemblyName( path ) ) ).ToList(); - } - } -} diff --git a/src/Util/Helpers/Regex.cs b/src/Util/Helpers/Regex.cs deleted file mode 100644 index 274140f6a..000000000 --- a/src/Util/Helpers/Regex.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Collections.Generic; -using System.Text.RegularExpressions; - -namespace Util.Helpers { - /// - /// 正则操作 - /// - public static class Regex { - /// - /// 获取匹配值集合 - /// - /// 输入字符串 - /// 模式字符串 - /// 结果模式字符串数组,范例:new[]{"$1","$2"} - /// 选项 - public static Dictionary GetValues( string input, string pattern, string[] resultPatterns, RegexOptions options = RegexOptions.IgnoreCase ) { - var result = new Dictionary(); - if( string.IsNullOrWhiteSpace( input ) ) - return result; - var match = System.Text.RegularExpressions.Regex.Match( input, pattern, options ); - if( match.Success == false ) - return result; - AddResults( result, match, resultPatterns ); - return result; - } - - /// - /// 添加匹配结果 - /// - private static void AddResults( Dictionary result, Match match, string[] resultPatterns ) { - if( resultPatterns == null ) { - result.Add( string.Empty, match.Value ); - return; - } - foreach( var resultPattern in resultPatterns ) - result.Add( resultPattern, match.Result( resultPattern ) ); - } - - /// - /// 获取匹配值 - /// - /// 输入字符串 - /// 模式字符串 - /// 结果模式字符串,范例:"$1"用来获取第一个()内的值 - /// 选项 - public static string GetValue( string input, string pattern, string resultPattern = "", RegexOptions options = RegexOptions.IgnoreCase ) { - if( string.IsNullOrWhiteSpace( input ) ) - return string.Empty; - var match = System.Text.RegularExpressions.Regex.Match( input, pattern, options ); - if( match.Success == false ) - return string.Empty; - return string.IsNullOrWhiteSpace( resultPattern ) ? match.Value : match.Result( resultPattern ); - } - - /// - /// 分割成字符串数组 - /// - /// 输入字符串 - /// 模式字符串 - /// 选项 - public static string[] Split( string input, string pattern, RegexOptions options = RegexOptions.IgnoreCase ) { - if( string.IsNullOrWhiteSpace( input ) ) - return new string[]{}; - return System.Text.RegularExpressions.Regex.Split( input, pattern, options ); - } - - /// - /// 替换 - /// - /// 输入字符串 - /// 模式字符串 - /// 替换字符串 - /// 选项 - public static string Replace( string input, string pattern,string replacement, RegexOptions options = RegexOptions.IgnoreCase ) { - if( string.IsNullOrWhiteSpace( input ) ) - return string.Empty; - return System.Text.RegularExpressions.Regex.Replace( input, pattern, replacement, options ); - } - - /// - /// 验证输入与模式是否匹配 - /// - /// 输入字符串 - /// 模式字符串 - public static bool IsMatch( string input, string pattern ) { - return IsMatch( input, pattern, RegexOptions.IgnoreCase ); - } - - /// - /// 验证输入与模式是否匹配 - /// - /// 输入的字符串 - /// 模式字符串 - /// 选项 - public static bool IsMatch( string input, string pattern, RegexOptions options ) { - return System.Text.RegularExpressions.Regex.IsMatch( input, pattern, options ); - } - } -} diff --git a/src/Util/Helpers/String.Builder.cs b/src/Util/Helpers/String.Builder.cs deleted file mode 100644 index 7941f486e..000000000 --- a/src/Util/Helpers/String.Builder.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System.Text; - -namespace Util.Helpers { - /// - /// 字符串操作 - 字符串生成器 - /// - public partial class String { - /// - /// 初始化字符串操作 - /// - public String() { - Builder = new StringBuilder(); - } - - /// - /// 字符串生成器 - /// - private StringBuilder Builder { get; set; } - - /// - /// 追加内容 - /// - /// 值的类型 - /// 值 - public String Append( T value ) { - Builder.Append( value ); - return this; - } - - /// - /// 追加内容 - /// - /// 值 - /// 参数 - public String Append( string value, params object[] args ) { - if( args == null ) - args = new object[] { string.Empty }; - if( args.Length == 0 ) - Builder.Append( value ); - else - Builder.AppendFormat( value, args ); - return this; - } - - /// - /// 追加内容并换行 - /// - public String AppendLine() { - Builder.AppendLine(); - return this; - } - - /// - /// 追加内容并换行 - /// - /// 值的类型 - /// 值 - public String AppendLine( T value ) { - Append( value ); - Builder.AppendLine(); - return this; - } - - /// - /// 追加内容并换行 - /// - /// 值 - /// 参数 - public String AppendLine( string value, params object[] args ) { - Append( value, args ); - Builder.AppendLine(); - return this; - } - - /// - /// 替换内容 - /// - /// 值 - public String Replace( string value ) { - Builder.Clear(); - Builder.Append( value ); - return this; - } - - /// - /// 移除末尾字符串 - /// - /// 末尾字符串 - public String RemoveEnd( string end ) { - string result = Builder.ToString(); - if( !result.EndsWith( end ) ) - return this; - Builder = new StringBuilder( result.TrimEnd( end.ToCharArray() ) ); - return this; - } - - /// - /// 清空字符串 - /// - public String Clear() { - Builder = Builder.Clear(); - return this; - } - - /// - /// 字符串长度 - /// - public int Length => Builder.Length; - - /// - /// 空字符串 - /// - public string Empty => string.Empty; - - /// - /// 转换为字符串 - /// - public override string ToString() { - return Builder.ToString(); - } - } -} diff --git a/src/Util/Helpers/String.Tools.cs b/src/Util/Helpers/String.Tools.cs deleted file mode 100644 index a25c17bba..000000000 --- a/src/Util/Helpers/String.Tools.cs +++ /dev/null @@ -1,141 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Util.Helpers { - /// - /// 字符串操作 - 工具 - /// - public partial class String { - - #region Join(将集合连接为带分隔符的字符串) - - /// - /// 将集合连接为带分隔符的字符串 - /// - /// 集合元素类型 - /// 集合 - /// 引号,默认不带引号,范例:单引号 "'" - /// 分隔符,默认使用逗号分隔 - public static string Join( IEnumerable list, string quotes = "", string separator = "," ) { - if( list == null ) - return string.Empty; - var result = new StringBuilder(); - foreach( var each in list ) - result.AppendFormat( "{0}{1}{0}{2}", quotes, each, separator ); - if( separator == "" ) - return result.ToString(); - return result.ToString().TrimEnd( separator.ToCharArray() ); - } - - #endregion - - #region PinYin(获取汉字的拼音简码) - - /// - /// 获取汉字的拼音简码,即首字母缩写,范例:中国,返回zg - /// - /// 汉字文本,范例: 中国 - public static string PinYin( string chineseText ) { - if( string.IsNullOrWhiteSpace( chineseText ) ) - return string.Empty; - var result = new StringBuilder(); - foreach( char text in chineseText ) - result.AppendFormat( "{0}", ResolvePinYin( text ) ); - return result.ToString().ToLower(); - } - - /// - /// 解析单个汉字的拼音简码 - /// - private static string ResolvePinYin( char text ) { - byte[] charBytes = Encoding.UTF8.GetBytes( text.ToString() ); - if( charBytes[0] <= 127 ) - return text.ToString(); - var unicode = (ushort)( charBytes[0] * 256 + charBytes[1] ); - string pinYin = ResolveByCode( unicode ); - if( !string.IsNullOrWhiteSpace( pinYin ) ) - return pinYin; - return ResolveByConst( text.ToString() ); - } - - /// - /// 使用字符编码方式获取拼音简码 - /// - private static string ResolveByCode( ushort unicode ) { - if( unicode >= '\uB0A1' && unicode <= '\uB0C4' ) - return "A"; - if( unicode >= '\uB0C5' && unicode <= '\uB2C0' && unicode != 45464 ) - return "B"; - if( unicode >= '\uB2C1' && unicode <= '\uB4ED' ) - return "C"; - if( unicode >= '\uB4EE' && unicode <= '\uB6E9' ) - return "D"; - if( unicode >= '\uB6EA' && unicode <= '\uB7A1' ) - return "E"; - if( unicode >= '\uB7A2' && unicode <= '\uB8C0' ) - return "F"; - if( unicode >= '\uB8C1' && unicode <= '\uB9FD' ) - return "G"; - if( unicode >= '\uB9FE' && unicode <= '\uBBF6' ) - return "H"; - if( unicode >= '\uBBF7' && unicode <= '\uBFA5' ) - return "J"; - if( unicode >= '\uBFA6' && unicode <= '\uC0AB' ) - return "K"; - if( unicode >= '\uC0AC' && unicode <= '\uC2E7' ) - return "L"; - if( unicode >= '\uC2E8' && unicode <= '\uC4C2' ) - return "M"; - if( unicode >= '\uC4C3' && unicode <= '\uC5B5' ) - return "N"; - if( unicode >= '\uC5B6' && unicode <= '\uC5BD' ) - return "O"; - if( unicode >= '\uC5BE' && unicode <= '\uC6D9' ) - return "P"; - if( unicode >= '\uC6DA' && unicode <= '\uC8BA' ) - return "Q"; - if( unicode >= '\uC8BB' && unicode <= '\uC8F5' ) - return "R"; - if( unicode >= '\uC8F6' && unicode <= '\uCBF9' ) - return "S"; - if( unicode >= '\uCBFA' && unicode <= '\uCDD9' ) - return "T"; - if( unicode >= '\uCDDA' && unicode <= '\uCEF3' ) - return "W"; - if( unicode >= '\uCEF4' && unicode <= '\uD188' ) - return "X"; - if( unicode >= '\uD1B9' && unicode <= '\uD4D0' ) - return "Y"; - if( unicode >= '\uD4D1' && unicode <= '\uD7F9' ) - return "Z"; - return string.Empty; - } - - /// - /// 通过拼音简码常量获取 - /// - private static string ResolveByConst( string text ) { - int index = Const.ChinesePinYin.IndexOf( text, StringComparison.Ordinal ); - if( index < 0 ) - return string.Empty; - return Const.ChinesePinYin.Substring( index + 1, 1 ); - } - - #endregion - - #region FirstLowerCase(首字母小写) - - /// - /// 首字母小写 - /// - /// 值 - public static string FirstLowerCase( string value ) { - if ( string.IsNullOrWhiteSpace( value ) ) - return string.Empty; - return $"{value.Substring( 0, 1 ).ToLower()}{value.Substring( 1 )}"; - } - - #endregion - } -} diff --git a/src/Util/Helpers/Thread.cs b/src/Util/Helpers/Thread.cs deleted file mode 100644 index b3c9ed3af..000000000 --- a/src/Util/Helpers/Thread.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Util.Helpers { - /// - /// 线程操作 - /// - public static class Thread { - /// - /// 执行多个操作,多个操作将同时执行 - /// - /// 操作集合 - public static void WaitAll( params Action[] actions ) { - if ( actions == null ) - return; - List tasks = new List(); - foreach ( var action in actions ) - tasks.Add( Task.Factory.StartNew( action, TaskCreationOptions.None ) ); - Task.WaitAll( tasks.ToArray() ); - } - } -} diff --git a/src/Util/Helpers/Time.cs b/src/Util/Helpers/Time.cs deleted file mode 100644 index e6c9fc83b..000000000 --- a/src/Util/Helpers/Time.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; - -namespace Util.Helpers { - /// - /// 时间操作 - /// - public static class Time { - /// - /// 日期 - /// - private static DateTime? _dateTime; - - /// - /// 设置时间 - /// - /// 时间 - public static void SetTime( DateTime? dateTime ) { - _dateTime = dateTime; - } - - /// - /// 设置时间 - /// - /// 时间 - public static void SetTime( string dateTime ) { - _dateTime = Util.Helpers.Convert.ToDateOrNull( dateTime ); - } - - /// - /// 重置时间 - /// - public static void Reset() { - _dateTime = null; - } - - /// - /// 获取当前日期时间 - /// - public static DateTime GetDateTime() { - if( _dateTime == null ) - return DateTime.Now; - return _dateTime.Value; - } - - /// - /// 获取当前日期,不带时间 - /// - public static DateTime GetDate() { - return GetDateTime().Date; - } - - /// - /// 获取Unix时间戳 - /// - public static long GetUnixTimestamp() { - return GetUnixTimestamp( DateTime.Now ); - } - - /// - /// 获取Unix时间戳 - /// - /// 时间 - public static long GetUnixTimestamp( DateTime time ) { - var start = TimeZoneInfo.ConvertTime( new DateTime( 1970, 1, 1 ), TimeZoneInfo.Local ); - long ticks = ( time - start.Add( new TimeSpan( 8, 0, 0 ) ) ).Ticks; - return Util.Helpers.Convert.ToLong( ticks / TimeSpan.TicksPerSecond ); - } - - /// - /// 从Unix时间戳获取时间 - /// - /// Unix时间戳 - public static DateTime GetTimeFromUnixTimestamp( long timestamp ) { - var start = TimeZoneInfo.ConvertTime( new DateTime( 1970, 1, 1 ), TimeZoneInfo.Local ); - TimeSpan span = new TimeSpan( long.Parse( timestamp + "0000000" ) ); - return start.Add( span ).Add( new TimeSpan( 8, 0, 0 ) ); - } - } -} \ No newline at end of file diff --git a/src/Util/Helpers/Validation.cs b/src/Util/Helpers/Validation.cs deleted file mode 100644 index 87bff2814..000000000 --- a/src/Util/Helpers/Validation.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Util.Helpers { - /// - /// 验证操作 - /// - public class Validation { - /// - /// 是否数字 - /// - /// 输入值 - public static bool IsNumber( string input ) { - if( input.IsEmpty() ) - return false; - const string pattern = @"^(-?\d*)(\.\d+)?$"; - return Regex.IsMatch( input, pattern ); - } - } -} diff --git a/src/Util/Helpers/Web.cs b/src/Util/Helpers/Web.cs deleted file mode 100644 index 4c44e0737..000000000 --- a/src/Util/Helpers/Web.cs +++ /dev/null @@ -1,190 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Extensions; - -namespace Util.Helpers { - /// - /// Web操作 - /// - public static class Web { - - #region 静态构造方法 - - /// - /// 初始化Web操作 - /// - static Web() { - try { - HttpContextAccessor = Ioc.Create(); - Environment = Ioc.Create(); - } - catch{ - } - } - - #endregion - - #region 属性 - - /// - /// Http上下文访问器 - /// - public static IHttpContextAccessor HttpContextAccessor { get; set; } - - /// - /// 当前Http上下文 - /// - public static HttpContext HttpContext => HttpContextAccessor?.HttpContext; - - /// - /// 宿主环境 - /// - public static IHostingEnvironment Environment { get; set; } - - #endregion - - #region Client( Web客户端 ) - - /// - /// Web客户端,用于发送Http请求 - /// - public static Util.Webs.Clients.WebClient Client() { - return new Util.Webs.Clients.WebClient(); - } - - /// - /// Web客户端,用于发送Http请求 - /// - /// 返回结果类型 - public static Util.Webs.Clients.WebClient Client() where TResult : class { - return new Util.Webs.Clients.WebClient(); - } - - #endregion - - #region Url(请求地址) - - /// - /// 请求地址 - /// - public static string Url => HttpContext?.Request?.GetDisplayUrl(); - - #endregion - - #region Ip(客户端Ip地址) - - /// - /// 客户端Ip地址 - /// - public static string Ip { - get { - var list = new[] { "127.0.0.1", "::1" }; - var result = HttpContext?.Connection?.RemoteIpAddress.SafeString(); - if( string.IsNullOrWhiteSpace( result ) || list.Contains( result ) ) - result = GetLanIp(); - return result; - } - } - - /// - /// 获取局域网IP - /// - private static string GetLanIp() { - foreach( var hostAddress in Dns.GetHostAddresses( Dns.GetHostName() ) ) { - if( hostAddress.AddressFamily == AddressFamily.InterNetwork ) - return hostAddress.ToString(); - } - return string.Empty; - } - - #endregion - - #region Host(主机) - - /// - /// 主机 - /// - public static string Host => HttpContext == null ? Dns.GetHostName() : GetClientHostName(); - - /// - /// 获取Web客户端主机名 - /// - private static string GetClientHostName() { - var address = GetRemoteAddress(); - if( string.IsNullOrWhiteSpace( address ) ) - return Dns.GetHostName(); - var result = Dns.GetHostEntry( IPAddress.Parse( address ) ).HostName; - if( result == "localhost.localdomain" ) - result = Dns.GetHostName(); - return result; - } - - /// - /// 获取远程地址 - /// - private static string GetRemoteAddress() { - return HttpContext?.Request?.Headers["HTTP_X_FORWARDED_FOR"] ?? HttpContext?.Request?.Headers["REMOTE_ADDR"]; - } - - #endregion - - #region Browser(浏览器) - - /// - /// 浏览器 - /// - public static string Browser => HttpContext?.Request?.Headers["User-Agent"]; - - #endregion - - #region RootPath(根路径) - - /// - /// 根路径 - /// - public static string RootPath => Environment?.ContentRootPath; - - #endregion - - #region WebRootPath(Web根路径) - - /// - /// Web根路径,即wwwroot - /// - public static string WebRootPath => Environment?.WebRootPath; - - #endregion - - #region GetFiles(获取客户端文件集合) - - /// - /// 获取客户端文件集合 - /// - public static List GetFiles() { - var result = new List(); - var files = HttpContext.Request.Form.Files; - if( files == null || files.Count == 0 ) - return result; - result.AddRange( files.Where( file => file?.Length > 0 ) ); - return result; - } - - #endregion - - #region GetFile(获取客户端文件) - - /// - /// 获取客户端文件 - /// - public static IFormFile GetFile() { - var files = GetFiles(); - return files.Count == 0 ? null : files[0]; - } - - #endregion - } -} diff --git a/src/Util/Item.cs b/src/Util/Item.cs deleted file mode 100644 index a52efb844..000000000 --- a/src/Util/Item.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using Newtonsoft.Json; - -namespace Util { - /// - /// 列表项 - /// - public class Item : IComparable { - /// - /// 初始化 - /// - /// 文本 - /// 值 - /// 排序号 - /// 组 - /// 禁用 - public Item( string text, object value, int? sortId = null, string group = null, bool? disabled = null ) { - Text = text; - Value = value; - SortId = sortId; - Group = group; - Disabled = disabled; - } - - /// - /// 文本 - /// - [JsonProperty( "text", NullValueHandling = NullValueHandling.Ignore )] - public string Text { get; } - - /// - /// 值 - /// - [JsonProperty( "value", NullValueHandling = NullValueHandling.Ignore )] - public object Value { get; } - - /// - /// 排序号 - /// - [JsonProperty( "sortId", NullValueHandling = NullValueHandling.Ignore )] - public int? SortId { get; } - - /// - /// 组 - /// - [JsonProperty( "group", NullValueHandling = NullValueHandling.Ignore )] - public string Group { get; } - - /// - /// 禁用 - /// - [JsonProperty( "disabled", NullValueHandling = NullValueHandling.Ignore )] - public bool? Disabled { get; } - - /// - /// 比较 - /// - /// 其它列表项 - public int CompareTo( Item other ) { - return string.Compare( Text, other.Text, StringComparison.CurrentCulture ); - } - } -} diff --git a/src/Util/Logs/Abstractions/ILogContent.cs b/src/Util/Logs/Abstractions/ILogContent.cs deleted file mode 100644 index b347fa4bd..000000000 --- a/src/Util/Logs/Abstractions/ILogContent.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Text; -using Util.Exceptions; - -namespace Util.Logs.Abstractions { - /// - /// 日志内容 - /// - public interface ILogContent { - /// - /// 日志名称 - /// - string LogName { get; set; } - /// - /// 日志级别 - /// - string Level { get; set; } - /// - /// 跟踪号 - /// - string TraceId { get; set; } - /// - /// 操作时间 - /// - string OperationTime { get; set; } - /// - /// 持续时间 - /// - string Duration { get; set; } - /// - /// IP - /// - string Ip { get; set; } - /// - /// 主机 - /// - string Host { get; set; } - /// - /// 线程号 - /// - string ThreadId { get; set; } - /// - /// 浏览器 - /// - string Browser { get; set; } - /// - /// 请求地址 - /// - string Url { get; set; } - /// - /// 操作人编号 - /// - string UserId { get; set; } - /// - /// 内容 - /// - StringBuilder Content { get; set; } - /// - /// 异常消息 - /// - Warning Exception { get; set; } - } -} diff --git a/src/Util/Logs/Abstractions/ILogContext.cs b/src/Util/Logs/Abstractions/ILogContext.cs deleted file mode 100644 index f9a2748d6..000000000 --- a/src/Util/Logs/Abstractions/ILogContext.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Diagnostics; - -namespace Util.Logs.Abstractions { - /// - /// 日志上下文 - /// - public interface ILogContext { - /// - /// 跟踪号 - /// - string TraceId { get; } - /// - /// 计时器 - /// - Stopwatch Stopwatch { get; } - /// - /// IP - /// - string Ip { get; } - /// - /// 主机 - /// - string Host { get; } - /// - /// 浏览器 - /// - string Browser { get; } - /// - /// 请求地址 - /// - string Url { get; } - } -} diff --git a/src/Util/Logs/Abstractions/ILogFormat.cs b/src/Util/Logs/Abstractions/ILogFormat.cs deleted file mode 100644 index 0aecccd31..000000000 --- a/src/Util/Logs/Abstractions/ILogFormat.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Util.Logs.Abstractions { - /// - /// 日志格式器 - /// - public interface ILogFormat { - /// - /// 格式化 - /// - /// 日志内容 - string Format( ILogContent content ); - } -} diff --git a/src/Util/Logs/Abstractions/ILogProvider.cs b/src/Util/Logs/Abstractions/ILogProvider.cs deleted file mode 100644 index 00cff81b8..000000000 --- a/src/Util/Logs/Abstractions/ILogProvider.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.Extensions.Logging; - -namespace Util.Logs.Abstractions { - /// - /// 日志提供程序 - /// - public interface ILogProvider { - /// - /// 日志名称 - /// - string LogName { get; } - /// - /// 调试级别是否启用 - /// - bool IsDebugEnabled { get; } - /// - /// 跟踪级别是否启用 - /// - bool IsTraceEnabled { get; } - /// - /// 写日志 - /// - /// 日志等级 - /// 日志内容 - void WriteLog( LogLevel level, ILogContent content ); - } -} diff --git a/src/Util/Logs/Abstractions/ILogProviderFactory.cs b/src/Util/Logs/Abstractions/ILogProviderFactory.cs deleted file mode 100644 index 46f5ab49d..000000000 --- a/src/Util/Logs/Abstractions/ILogProviderFactory.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Util.Logs.Abstractions { - /// - /// 日志提供程序工厂 - /// - public interface ILogProviderFactory { - /// - /// 创建日志提供程序 - /// - /// 日志名称 - /// 日志格式化器 - ILogProvider Create( string logName, ILogFormat format = null ); - } -} diff --git a/src/Util/Logs/Core/LogBase.cs b/src/Util/Logs/Core/LogBase.cs deleted file mode 100644 index a602ffe26..000000000 --- a/src/Util/Logs/Core/LogBase.cs +++ /dev/null @@ -1,222 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; -using Util.Domains.Sessions; -using Util.Logs.Abstractions; -using Util.Logs.Extensions; - -namespace Util.Logs.Core { - /// - /// 日志操作 - /// - /// 日志内容类型 - public abstract class LogBase : ILog where TContent : class, ILogContent { - /// - /// 日志内容 - /// - private TContent _content; - /// - /// 日志内容 - /// - private TContent LogContent => _content ?? ( _content = GetContent() ); - - /// - /// 初始化日志操作 - /// - /// 日志提供程序 - /// 日志上下文 - /// 用户会话 - protected LogBase( ILogProvider provider, ILogContext context, ISession session ) { - Provider = provider; - Context = context; - Session = session ?? Util.Domains.Sessions.Session.Null; - } - - /// - /// 日志提供程序 - /// - public ILogProvider Provider { get; } - - /// - /// 日志上下文 - /// - public ILogContext Context { get; } - - /// - /// 用户会话 - /// - public ISession Session { get; set; } - - /// - /// 获取日志内容 - /// - protected abstract TContent GetContent(); - - /// - /// 设置内容 - /// - /// 设置内容操作 - public ILog Set( Action action ) where T : ILogContent { - if( action == null ) - throw new ArgumentNullException( nameof( action ) ); - ILogContent content = LogContent; - action( (T)content ); - return this; - } - - /// - /// 初始化 - /// - /// 日志内容 - protected virtual void Init( TContent content ) { - content.LogName = Provider.LogName; - content.TraceId = Context.TraceId; - content.OperationTime = DateTime.Now.ToMillisecondString(); - content.Duration = Context.Stopwatch.Elapsed.Description(); - content.Ip = Context.Ip; - content.Host = Context.Host; - content.ThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId.ToString(); - content.Browser = Context.Browser; - content.Url = Context.Url; - content.UserId = Session.UserId; - } - - /// - /// 调试级别是否启用 - /// - public bool IsDebugEnabled => Provider.IsDebugEnabled; - - /// - /// 跟踪级别是否启用 - /// - public bool IsTraceEnabled => Provider.IsTraceEnabled; - - /// - /// 跟踪 - /// - public virtual void Trace() { - _content = LogContent; - Execute( LogLevel.Trace, ref _content ); - } - - /// - /// 执行 - /// - private void Execute( LogLevel level, ref TContent content ) { - if ( content == null ) - return; - if ( Enabled( level ) == false ) - return; - try { - content.Level = Util.Helpers.Enum.GetName( level ); - Init( content ); - Provider.WriteLog( level, content ); - } - finally { - content = null; - } - } - - /// - /// 是否启用 - /// - private bool Enabled( LogLevel level ) { - if ( level > LogLevel.Debug ) - return true; - return IsDebugEnabled || IsTraceEnabled && level == LogLevel.Trace; - } - - /// - /// 跟踪 - /// - /// 日志消息 - public virtual void Trace( string message ) { - LogContent.Content( message ); - Trace(); - } - - /// - /// 调试 - /// - public virtual void Debug() { - _content = LogContent; - Execute( LogLevel.Debug, ref _content ); - } - - /// - /// 调试 - /// - /// 日志消息 - public virtual void Debug( string message ) { - LogContent.Content( message ); - Debug(); - } - - /// - /// 信息 - /// - public virtual void Info() { - _content = LogContent; - Execute( LogLevel.Information, ref _content ); - } - - /// - /// 信息 - /// - /// 日志消息 - public virtual void Info( string message ) { - LogContent.Content( message ); - Info(); - } - - /// - /// 警告 - /// - public virtual void Warn() { - _content = LogContent; - Execute( LogLevel.Warning, ref _content ); - } - - /// - /// 警告 - /// - /// 日志消息 - public virtual void Warn( string message ) { - LogContent.Content( message ); - Warn(); - } - - /// - /// 错误 - /// - public virtual void Error() { - _content = LogContent; - Execute( LogLevel.Error, ref _content ); - } - - /// - /// 错误 - /// - /// 日志消息 - public virtual void Error( string message ) { - LogContent.Content( message ); - Error(); - } - - /// - /// 致命错误 - /// - public virtual void Fatal() { - _content = LogContent; - Execute( LogLevel.Critical, ref _content ); - } - - /// - /// 致命错误 - /// - /// 日志消息 - public virtual void Fatal( string message ) { - LogContent.Content( message ); - Fatal(); - } - } -} diff --git a/src/Util/Logs/Core/LogContext.cs b/src/Util/Logs/Core/LogContext.cs deleted file mode 100644 index d26382bb4..000000000 --- a/src/Util/Logs/Core/LogContext.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Diagnostics; -using Util.Contexts; -using Util.Helpers; -using Util.Logs.Abstractions; -using Util.Logs.Internal; - -namespace Util.Logs.Core { - /// - /// 日志上下文 - /// - public class LogContext : ILogContext { - /// - /// 日志上下文信息 - /// - private LogContextInfo _info; - - /// - /// 初始化日志上下文 - /// - /// 上下文 - public LogContext( IContext context ) { - Context = context; - } - - /// - /// 上下文 - /// - public IContext Context { get; set; } - - /// - /// 获取日志上下文信息 - /// - private LogContextInfo GetInfo() { - if ( _info != null ) - return _info; - var key = "Util.Logs.LogContext"; - _info = Context.Get( key ); - if( _info != null ) - return _info; - _info = CreateInfo(); - Context.Add( key, _info ); - return _info; - } - - /// - /// 创建日志上下文信息 - /// - protected virtual LogContextInfo CreateInfo() { - return new LogContextInfo { - TraceId = GetTraceId(), - Stopwatch = GetStopwatch(), - Ip = Web.Ip, - Host = Web.Host, - Browser = Web.Browser, - Url = Web.Url - }; - } - - /// - /// 获取跟踪号 - /// - protected string GetTraceId() { - var traceId = Context.TraceId; - return string.IsNullOrWhiteSpace( traceId ) ? Guid.NewGuid().ToString() : traceId; - } - - /// - /// 获取计时器 - /// - protected Stopwatch GetStopwatch() { - Stopwatch stopwatch = new Stopwatch(); - stopwatch.Start(); - return stopwatch; - } - - /// - /// 跟踪号 - /// - public string TraceId => GetInfo().TraceId; - - /// - /// 计时器 - /// - public Stopwatch Stopwatch => GetInfo().Stopwatch; - - /// - /// IP - /// - public string Ip => GetInfo().Ip; - /// - /// 主机 - /// - public string Host => GetInfo().Host; - /// - /// 浏览器 - /// - public string Browser => GetInfo().Browser; - /// - /// 请求地址 - /// - public string Url => GetInfo().Url; - } -} diff --git a/src/Util/Logs/Core/NullLog.cs b/src/Util/Logs/Core/NullLog.cs deleted file mode 100644 index 979541db6..000000000 --- a/src/Util/Logs/Core/NullLog.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System; -using Util.Logs.Abstractions; - -namespace Util.Logs.Core { - /// - /// 空日志操作 - /// - public class NullLog : ILog { - /// - /// 空日志操作实例 - /// - public static readonly ILog Instance = new NullLog(); - - /// - /// 设置内容 - /// - /// 日志内容类型 - /// 设置内容操作 - public ILog Set( Action action ) where TContent : ILogContent { - return this; - } - - /// - /// 调试级别是否启用 - /// - public bool IsDebugEnabled => false; - - /// - /// 跟踪级别是否启用 - /// - public bool IsTraceEnabled => false; - - /// - /// 跟踪 - /// - public void Trace() { - } - - /// - /// 跟踪 - /// - /// 日志消息 - public void Trace( string message ) { - } - - /// - /// 调试 - /// - public void Debug() { - } - - /// - /// 调试 - /// - /// 日志消息 - public void Debug( string message ) { - } - - /// - /// 信息 - /// - public void Info() { - } - - /// - /// 信息 - /// - /// 日志消息 - public void Info( string message ) { - } - - /// - /// 警告 - /// - public void Warn() { - } - - /// - /// 警告 - /// - /// 日志消息 - public void Warn( string message ) { - } - - /// - /// 错误 - /// - public void Error() { - } - - /// - /// 错误 - /// - /// 日志消息 - public void Error( string message ) { - } - - /// - /// 致命错误 - /// - public void Fatal() { - } - - /// - /// 致命错误 - /// - /// 日志消息 - public void Fatal( string message ) { - } - } -} diff --git a/src/Util/Logs/Core/NullLogContext.cs b/src/Util/Logs/Core/NullLogContext.cs deleted file mode 100644 index de48c4472..000000000 --- a/src/Util/Logs/Core/NullLogContext.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Diagnostics; -using Util.Logs.Abstractions; - -namespace Util.Logs.Core { - /// - /// 空日志上下文 - /// - public class NullLogContext : ILogContext { - /// - /// 空日志上下文实例 - /// - public static readonly ILogContext Instance = new NullLogContext(); - /// - /// 跟踪号 - /// - public string TraceId => string.Empty; - /// - /// 计时器 - /// - public Stopwatch Stopwatch => new Stopwatch(); - /// - /// IP - /// - public string Ip => string.Empty; - /// - /// 主机 - /// - public string Host => string.Empty; - /// - /// 浏览器 - /// - public string Browser => string.Empty; - /// - /// 请求地址 - /// - public string Url => string.Empty; - } -} diff --git a/src/Util/Logs/Core/NullLogFormat.cs b/src/Util/Logs/Core/NullLogFormat.cs deleted file mode 100644 index 9ec825642..000000000 --- a/src/Util/Logs/Core/NullLogFormat.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Util.Logs.Abstractions; - -namespace Util.Logs.Core { - /// - /// 空日志格式器 - /// - public class NullLogFormat : ILogFormat { - /// - /// 空日志格式器实例 - /// - public static readonly ILogFormat Instance = new NullLogFormat(); - - /// - /// 格式化 - /// - /// 日志内容 - public string Format( ILogContent content ) { - return ""; - } - } -} diff --git a/src/Util/Logs/Extensions/Extensions.Log.cs b/src/Util/Logs/Extensions/Extensions.Log.cs deleted file mode 100644 index c8b47c78a..000000000 --- a/src/Util/Logs/Extensions/Extensions.Log.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; -using Util.Logs.Abstractions; - -namespace Util.Logs.Extensions { - /// - /// 日志扩展 - /// - public static partial class Extensions { - /// - /// 设置内容 - /// - /// 日志操作 - public static ILog Content( this ILog log ) { - return log.Set( content => content.Content( "" ) ); - } - - /// - /// 设置内容 - /// - /// 日志操作 - /// 值 - public static ILog Content( this ILog log, string value ) { - return log.Set( content => content.Content( value ) ); - } - - /// - /// 设置内容 - /// - /// 日志操作 - /// 字典 - public static ILog Content( this ILog log, IDictionary dictionary ) { - if ( dictionary == null ) - return log; - foreach ( var keyValue in dictionary ) - log.Set( content => content.Content( $"{keyValue.Key} : {keyValue.Value}" ) ); - return log; - } - } -} diff --git a/src/Util/Logs/Extensions/Extensions.LogContent.cs b/src/Util/Logs/Extensions/Extensions.LogContent.cs deleted file mode 100644 index 57976a9ab..000000000 --- a/src/Util/Logs/Extensions/Extensions.LogContent.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Text; -using Util.Logs.Abstractions; - -namespace Util.Logs.Extensions { - /// - /// 日志扩展 - /// - public static partial class Extensions { - /// - /// 追加内容 - /// - public static void Append( this ILogContent content, StringBuilder result, string value ) { - if( string.IsNullOrWhiteSpace( value ) ) - return; - result.Append( " " ); - result.Append( value ); - } - - /// - /// 追加内容并换行 - /// - public static void AppendLine( this ILogContent content, StringBuilder result, string value ) { - content.Append( result, value ); - result.AppendLine(); - } - - /// - /// 设置内容并换行 - /// - /// 日志内容 - /// 值 - public static void Content( this ILogContent content, string value ) { - content.AppendLine( content.Content, value ); - } - } -} diff --git a/src/Util/Logs/ILog.cs b/src/Util/Logs/ILog.cs deleted file mode 100644 index 9ee15fd25..000000000 --- a/src/Util/Logs/ILog.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using Util.Logs.Abstractions; - -namespace Util.Logs { - /// - /// 日志操作 - /// - public interface ILog { - /// - /// 设置内容 - /// - /// 日志内容类型 - /// 设置内容操作 - ILog Set( Action action ) where TContent : ILogContent; - /// - /// 调试级别是否启用 - /// - bool IsDebugEnabled { get; } - /// - /// 跟踪级别是否启用 - /// - bool IsTraceEnabled { get; } - /// - /// 跟踪 - /// - void Trace(); - /// - /// 跟踪 - /// - /// 日志消息 - void Trace( string message ); - /// - /// 调试 - /// - void Debug(); - /// - /// 调试 - /// - /// 日志消息 - void Debug( string message ); - /// - /// 信息 - /// - void Info(); - /// - /// 信息 - /// - /// 日志消息 - void Info( string message ); - /// - /// 警告 - /// - void Warn(); - /// - /// 警告 - /// - /// 日志消息 - void Warn( string message ); - /// - /// 错误 - /// - void Error(); - /// - /// 错误 - /// - /// 日志消息 - void Error( string message ); - /// - /// 致命错误 - /// - void Fatal(); - /// - /// 致命错误 - /// - /// 日志消息 - void Fatal( string message ); - } -} diff --git a/src/Util/Logs/Internal/LogContextInfo.cs b/src/Util/Logs/Internal/LogContextInfo.cs deleted file mode 100644 index 96c385c73..000000000 --- a/src/Util/Logs/Internal/LogContextInfo.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Diagnostics; - -namespace Util.Logs.Internal { - /// - /// 日志上下文信息 - /// - public class LogContextInfo { - /// - /// 跟踪号 - /// - public string TraceId { get; set; } - - /// - /// 计时器 - /// - public Stopwatch Stopwatch { get; set; } - - /// - /// IP - /// - public string Ip { get; set; } - - /// - /// 主机 - /// - public string Host { get; set; } - - /// - /// 浏览器 - /// - public string Browser { get; set; } - - /// - /// 请求地址 - /// - public string Url { get; set; } - } -} diff --git a/src/Util/Maps/Extensions.Mapper.cs b/src/Util/Maps/Extensions.Mapper.cs deleted file mode 100644 index 8c98c8943..000000000 --- a/src/Util/Maps/Extensions.Mapper.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System; -using AutoMapper; -using System.Collections.Generic; -using System.Globalization; -using System.Reflection; - -namespace Util.Maps { - /// - /// 对象映射 - /// - public static class Extensions { - /// - /// 同步锁 - /// - private static readonly object Sync = new object(); - - /// - /// 将源对象映射到目标对象 - /// - /// 源类型 - /// 目标类型 - /// 源对象 - /// 目标对象 - public static TDestination MapTo( this TSource source, TDestination destination ) { - return MapTo( source, destination ); - } - - /// - /// 将源对象映射到目标对象 - /// - /// 目标类型 - /// 源对象 - public static TDestination MapTo( this object source ) where TDestination : new() { - return MapTo( source, new TDestination() ); - } - - /// - /// 将源对象映射到目标对象 - /// - private static TDestination MapTo( object source, TDestination destination ) { - if( source == null ) - throw new ArgumentNullException( nameof( source ) ); - if( destination == null ) - throw new ArgumentNullException( nameof( destination ) ); - var sourceType = GetType( source ); - var destinationType = GetType( destination ); - var map = GetMap( sourceType, destinationType ); - if( map != null ) - return Mapper.Map( source, destination ); - lock( Sync ) { - map = GetMap( sourceType, destinationType ); - if( map != null ) - return Mapper.Map( source, destination ); - InitMaps( sourceType, destinationType ); - } - return Mapper.Map( source, destination ); - } - - /// - /// 获取类型 - /// - private static Type GetType( object obj ) { - var type = obj.GetType(); - if( ( obj is System.Collections.IEnumerable ) == false ) - return type; - if( type.IsArray ) - return type.GetElementType(); - var genericArgumentsTypes = type.GetTypeInfo().GetGenericArguments(); - if( genericArgumentsTypes == null || genericArgumentsTypes.Length == 0 ) - throw new ArgumentException( "泛型类型参数不能为空" ); - return genericArgumentsTypes[0]; - } - - /// - /// 获取映射配置 - /// - private static TypeMap GetMap( Type sourceType, Type destinationType ) { - try { - return Mapper.Configuration.FindTypeMapFor( sourceType, destinationType ); - } - catch( InvalidOperationException ) { - lock( Sync ) { - try { - return Mapper.Configuration.FindTypeMapFor( sourceType, destinationType ); - } - catch( InvalidOperationException ) { - InitMaps( sourceType, destinationType ); - } - return Mapper.Configuration.FindTypeMapFor( sourceType, destinationType ); - } - } - } - - /// - /// 初始化映射配置 - /// - private static void InitMaps( Type sourceType, Type destinationType ) { - try { - var maps = Mapper.Configuration.GetAllTypeMaps(); - Mapper.Initialize( config => { - ClearConfig(); - foreach( var item in maps ) - config.CreateMap( item.SourceType, item.DestinationType ); - config.CreateMap( sourceType, destinationType ); - } ); - } - catch( InvalidOperationException ) { - Mapper.Initialize( config => { - config.CreateMap( sourceType, destinationType ); - } ); - } - } - - /// - /// 清空配置 - /// - private static void ClearConfig() { - var typeMapper = typeof( Mapper ).GetTypeInfo(); - var configuration = typeMapper.GetDeclaredField( "_configuration" ); - configuration.SetValue( null, null, BindingFlags.Static, null, CultureInfo.CurrentCulture ); - } - - /// - /// 将源集合映射到目标集合 - /// - /// 目标元素类型,范例:Sample,不要加List - /// 源集合 - public static List MapToList( this System.Collections.IEnumerable source ) { - return MapTo>( source ); - } - } -} diff --git a/src/Util/Parameters/Formats/IParameterFormat.cs b/src/Util/Parameters/Formats/IParameterFormat.cs deleted file mode 100644 index 30b207c48..000000000 --- a/src/Util/Parameters/Formats/IParameterFormat.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Util.Parameters.Formats { - /// - /// 参数格式化器 - /// - public interface IParameterFormat { - /// - /// 格式化 - /// - /// 键 - /// 值 - string Format( string key, string value ); - /// - /// 连接参数 - /// - /// 左操作数 - /// 右操作数 - string Join( string left, string right ); - } -} diff --git a/src/Util/Parameters/Formats/ParameterFormatBase.cs b/src/Util/Parameters/Formats/ParameterFormatBase.cs deleted file mode 100644 index 5c279195e..000000000 --- a/src/Util/Parameters/Formats/ParameterFormatBase.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Util.Parameters.Formats { - /// - /// 参数格式化器 - /// - public abstract class ParameterFormatBase : IParameterFormat { - /// - /// 格式化 - /// - /// 键 - /// 值 - public string Format( string key, string value ) { - return $"{key}{FormatSeparator}{value}"; - } - - /// - /// 格式化分割符 - /// - protected abstract string FormatSeparator { get; } - - /// - /// 连接参数 - /// - /// 左操作数 - /// 右操作数 - public string Join( string left, string right ) { - if ( string.IsNullOrWhiteSpace( left ) ) - return right; - if ( string.IsNullOrWhiteSpace( right ) ) - return left; - return $"{left}{JoinSeparator}{right}"; - } - - /// - /// 连接符 - /// - protected abstract string JoinSeparator { get; } - } -} diff --git a/src/Util/Parameters/Formats/UrlParameterFormat.cs b/src/Util/Parameters/Formats/UrlParameterFormat.cs deleted file mode 100644 index d115b4077..000000000 --- a/src/Util/Parameters/Formats/UrlParameterFormat.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Util.Parameters.Formats { - /// - /// Url参数格式化器 - /// - public class UrlParameterFormat : ParameterFormatBase { - /// - /// Url参数格式化器实例 - /// - public static readonly IParameterFormat Instance = new UrlParameterFormat(); - - /// - /// 格式化分割符 - /// - protected override string FormatSeparator => "="; - - /// - /// 连接符 - /// - protected override string JoinSeparator => "&"; - } -} diff --git a/src/Util/Parameters/ParameterBuilder.cs b/src/Util/Parameters/ParameterBuilder.cs deleted file mode 100644 index 0059545dd..000000000 --- a/src/Util/Parameters/ParameterBuilder.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Collections.Generic; -using Util.Helpers; -using Util.Parameters.Formats; - -namespace Util.Parameters { - /// - /// 参数生成器 - /// - public class ParameterBuilder { - /// - /// 参数集合 - /// - private readonly IDictionary _params; - - /// - /// 初始化参数生成器 - /// - public ParameterBuilder() { - _params = new Dictionary(); - } - - /// - /// 初始化参数生成器 - /// - /// 参数生成器 - public ParameterBuilder( ParameterBuilder builder ) : this( builder.GetDictionary() ) { - } - - /// - /// 初始化参数生成器 - /// - /// 字典 - public ParameterBuilder( IDictionary dictionary ) { - _params = dictionary == null ? new Dictionary() : new Dictionary( dictionary ); - } - - /// - /// 添加参数,如果参数已存在则替换 - /// - /// 键 - /// 值 - public ParameterBuilder Add( string key, object value ) { - if( string.IsNullOrWhiteSpace( key ) ) - return this; - string strValue = GetValue( value ).Trim(); - if( string.IsNullOrWhiteSpace( strValue ) ) - return this; - key = key.Trim(); - if( _params.ContainsKey( key ) ) - _params[key] = strValue; - else - _params.Add( key, strValue ); - return this; - } - - /// - /// 获取值 - /// - private string GetValue( object value ) { - if( value is DateTime dateTime ) - return dateTime.ToString( "yyyy-MM-dd HH:mm:ss" ); - if( value is bool boolValue ) - return boolValue.ToString().ToLower(); - return value.SafeString(); - } - - /// - /// 获取字典 - /// - public IDictionary GetDictionary() { - return _params; - } - - /// - /// 获取键值对集合 - /// - public IEnumerable> GetKeyValuePairs() { - return _params; - } - - /// - /// 清空 - /// - public void Clear() { - _params.Clear(); - } - - /// - /// 移除参数 - /// - /// 键 - public bool Remove( string key ) { - return _params.Remove( key ); - } - - /// - /// 转换为Json - /// - public string ToJson() { - return Json.ToJson( _params ); - } - - /// - /// 获取结果 - /// - /// 参数格式化器 - /// 是否按参数名排序 - public string Result( IParameterFormat format, bool isSort = false ) { - if( format == null ) - throw new ArgumentNullException( nameof( format ) ); - var result = string.Empty; - foreach( var param in GetDictionary( isSort ) ) - result = format.Join( result, format.Format( param.Key, param.Value ) ); - return result; - } - - /// - /// 获取字典 - /// - private IDictionary GetDictionary( bool isSort ) { - if( isSort == false ) - return _params; - return new SortedDictionary( _params ); - } - } -} \ No newline at end of file diff --git a/src/Util/Parameters/UrlParameterBuilder.cs b/src/Util/Parameters/UrlParameterBuilder.cs deleted file mode 100644 index 267076ad9..000000000 --- a/src/Util/Parameters/UrlParameterBuilder.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Web; -using Util.Parameters.Formats; - -namespace Util.Parameters { - /// - /// Url参数生成器 - /// - public class UrlParameterBuilder { - /// - /// 参数生成器 - /// - private ParameterBuilder ParameterBuilder { get; } - - /// - /// 初始化Url参数生成器 - /// - public UrlParameterBuilder() : this( "" ) { - } - - /// - /// 初始化Url参数生成器 - /// - /// Url参数生成器 - public UrlParameterBuilder( UrlParameterBuilder builder ) : this( "", builder ) { - } - - /// - /// 初始化Url参数生成器 - /// - /// Url - /// Url参数生成器 - public UrlParameterBuilder( string url, UrlParameterBuilder builder = null ) { - ParameterBuilder = builder == null ? new ParameterBuilder() : new ParameterBuilder( builder.ParameterBuilder ); - LoadUrl( url ); - } - - /// - /// 加载Url - /// - public void LoadUrl( string url ) { - if( string.IsNullOrWhiteSpace( url ) ) - return; - if( url.Contains( "?" ) ) - url = url.Substring( url.IndexOf( "?", StringComparison.Ordinal ) + 1 ); - var parameters = HttpUtility.ParseQueryString( url ); - foreach( var key in parameters.AllKeys ) - Add( key, parameters.Get( key ) ); - } - - /// - /// 添加参数 - /// - /// 键 - /// 值 - public UrlParameterBuilder Add( string key, object value ) { - ParameterBuilder.Add( key, value ); - return this; - } - - /// - /// 获取字典 - /// - public IDictionary GetDictionary() { - return ParameterBuilder.GetDictionary(); - } - - /// - /// 获取键值对集合 - /// - public IEnumerable> GetKeyValuePairs() { - return ParameterBuilder.GetKeyValuePairs(); - } - - /// - /// 获取结果,格式:参数名=参数值&参数名=参数值 - /// - /// 是否按参数名排序 - public string Result( bool isSort = false ) { - return ParameterBuilder.Result( UrlParameterFormat.Instance, isSort ); - } - - /// - /// 连接Url - /// - /// 地址 - public string JoinUrl( string url ) { - return $"{GetUrl( url )}{Result()}"; - } - - /// - /// 获取Url - /// - private string GetUrl( string url ) { - if( !url.Contains( "?" ) ) - return $"{url}?"; - if( url.EndsWith( "?" ) ) - return url; - if( url.EndsWith( "&" ) ) - return url; - return $"{url}&"; - } - - /// - /// 清空 - /// - public void Clear() { - ParameterBuilder.Clear(); - } - - /// - /// 移除参数 - /// - /// 键 - public bool Remove( string key ) { - return ParameterBuilder.Remove( key ); - } - } -} diff --git a/src/Util/Properties/LibraryResource.Designer.cs b/src/Util/Properties/LibraryResource.Designer.cs deleted file mode 100644 index 02780f291..000000000 --- a/src/Util/Properties/LibraryResource.Designer.cs +++ /dev/null @@ -1,81 +0,0 @@ -//------------------------------------------------------------------------------ -// -// 此代码由工具生成。 -// 运行时版本:4.0.30319.42000 -// -// 对此文件的更改可能会导致不正确的行为,并且如果 -// 重新生成代码,这些更改将会丢失。 -// -//------------------------------------------------------------------------------ - -namespace Util.Properties { - using System; - - - /// - /// 一个强类型的资源类,用于查找本地化的字符串等。 - /// - // 此类是由 StronglyTypedResourceBuilder - // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 - // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen - // (以 /str 作为命令选项),或重新生成 VS 项目。 - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class LibraryResource { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal LibraryResource() { - } - - /// - /// 返回此类使用的缓存的 ResourceManager 实例。 - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Util.Properties.LibraryResource", typeof(LibraryResource).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// 使用此强类型资源类,为所有资源查找 - /// 重写当前线程的 CurrentUICulture 属性。 - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// 查找类似 当前操作的数据已被其他人修改,请刷新后重试 的本地化字符串。 - /// - public static string ConcurrencyExceptionMessage { - get { - return ResourceManager.GetString("ConcurrencyExceptionMessage", resourceCulture); - } - } - - /// - /// 查找类似 仅允许添加一个条件,条件:{0} 的本地化字符串。 - /// - public static string OnlyOnePredicate { - get { - return ResourceManager.GetString("OnlyOnePredicate", resourceCulture); - } - } - } -} diff --git a/src/Util/Properties/LibraryResource.resx b/src/Util/Properties/LibraryResource.resx deleted file mode 100644 index 3cb9f8912..000000000 --- a/src/Util/Properties/LibraryResource.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 当前操作的数据已被其他人修改,请刷新后重试 - - - 仅允许添加一个条件,条件:{0} - - \ No newline at end of file diff --git a/src/Util/Properties/R.Designer.cs b/src/Util/Properties/R.Designer.cs deleted file mode 100644 index 72bee6385..000000000 --- a/src/Util/Properties/R.Designer.cs +++ /dev/null @@ -1,90 +0,0 @@ -//------------------------------------------------------------------------------ -// -// 此代码由工具生成。 -// 运行时版本:4.0.30319.42000 -// -// 对此文件的更改可能会导致不正确的行为,并且如果 -// 重新生成代码,这些更改将会丢失。 -// -//------------------------------------------------------------------------------ - -namespace Util.Properties { - using System; - - - /// - /// 一个强类型的资源类,用于查找本地化的字符串等。 - /// - // 此类是由 StronglyTypedResourceBuilder - // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 - // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen - // (以 /str 作为命令选项),或重新生成 VS 项目。 - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class R { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal R() { - } - - /// - /// 返回此类使用的缓存的 ResourceManager 实例。 - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Util.Properties.R", typeof(R).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// 使用此强类型资源类,为所有资源查找 - /// 重写当前线程的 CurrentUICulture 属性。 - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// 查找类似 删除成功 的本地化字符串。 - /// - public static string DeleteSuccess { - get { - return ResourceManager.GetString("DeleteSuccess", resourceCulture); - } - } - - /// - /// 查找类似 操作成功 的本地化字符串。 - /// - public static string Success { - get { - return ResourceManager.GetString("Success", resourceCulture); - } - } - - /// - /// 查找类似 系统忙,请稍后再试 的本地化字符串。 - /// - public static string SystemError { - get { - return ResourceManager.GetString("SystemError", resourceCulture); - } - } - } -} diff --git a/src/Util/Properties/R.resx b/src/Util/Properties/R.resx deleted file mode 100644 index 485b9656c..000000000 --- a/src/Util/Properties/R.resx +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 删除成功 - - - 操作成功 - - - 系统忙,请稍后再试 - 抛出系统异常时显示该消息 - - \ No newline at end of file diff --git a/src/Util/Randoms/GuidRandomGenerator.cs b/src/Util/Randoms/GuidRandomGenerator.cs deleted file mode 100644 index cb4329375..000000000 --- a/src/Util/Randoms/GuidRandomGenerator.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Util.Helpers; - -namespace Util.Randoms { - /// - /// Guid随机数生成器,每次创建一个新的Guid字符串,去掉了Guid的分隔符 - /// - public class GuidRandomGenerator : IRandomGenerator { - /// - /// 生成随机数 - /// - public string Generate() { - return Id.Guid(); - } - - /// - /// Guid随机数生成器实例 - /// - public static readonly IRandomGenerator Instance = new GuidRandomGenerator(); - } -} diff --git a/src/Util/Randoms/IRandomGenerator.cs b/src/Util/Randoms/IRandomGenerator.cs deleted file mode 100644 index 4a1bc6029..000000000 --- a/src/Util/Randoms/IRandomGenerator.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Util.Randoms { - /// - /// 随机数生成器 - /// - public interface IRandomGenerator { - /// - /// 生成随机数 - /// - string Generate(); - } -} diff --git a/src/Util/Randoms/StubRandomGenerator.cs b/src/Util/Randoms/StubRandomGenerator.cs deleted file mode 100644 index 4605214c4..000000000 --- a/src/Util/Randoms/StubRandomGenerator.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Util.Randoms { - /// - /// 伪随机数生成器,用于单元测试,固定返回字符串"random" - /// - public class StubRandomGenerator : IRandomGenerator { - /// - /// 生成随机数 - /// - public string Generate() { - return "random"; - } - } -} diff --git a/src/Util/Reflections/Finder.cs b/src/Util/Reflections/Finder.cs deleted file mode 100644 index 8bf4d0262..000000000 --- a/src/Util/Reflections/Finder.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text.RegularExpressions; - -namespace Util.Reflections { - /// - /// 类型查找器 - /// - public class Finder : IFind { - /// - /// 跳过的程序集 - /// - private const string SkipAssemblies = "^System|^Mscorlib|^Netstandard|^Microsoft|^Autofac|^AutoMapper|^EntityFramework|^Newtonsoft|^Castle|^NLog|^Pomelo|^AspectCore|^Xunit|^Nito|^Npgsql|^Exceptionless|^MySqlConnector|^Anonymously Hosted"; - - /// - /// 获取程序集列表 - /// - public virtual List GetAssemblies() { - return GetAssembliesFromCurrentAppDomain(); - } - - /// - /// 从当前应用程序域获取程序集列表 - /// - private List GetAssembliesFromCurrentAppDomain() { - var result = new List(); - foreach( var assembly in AppDomain.CurrentDomain.GetAssemblies() ) { - if( Match( assembly ) ) - result.Add( assembly ); - } - return result.Distinct().ToList(); - } - - /// - /// 程序集是否匹配 - /// - private bool Match( Assembly assembly ) { - return !Regex.IsMatch( assembly.FullName, SkipAssemblies, RegexOptions.IgnoreCase | RegexOptions.Compiled ); - } - - /// - /// 加载程序集到当前应用程序域 - /// - /// 目录绝对路径 - protected void LoadAssemblies( string path ) { - foreach( string file in Directory.GetFiles( path, "*.dll" ) ) { - var assemblyName = AssemblyName.GetAssemblyName( file ); - if( Match( assemblyName ) ) - AppDomain.CurrentDomain.Load( assemblyName ); - } - } - - /// - /// 程序集是否匹配 - /// - private bool Match( AssemblyName assemblyName ) { - return !Regex.IsMatch( assemblyName.FullName, SkipAssemblies, RegexOptions.IgnoreCase | RegexOptions.Compiled ); - } - - /// - /// 查找类型列表 - /// - /// 查找类型 - /// 在指定的程序集列表中查找 - public List Find( List assemblies = null ) { - return Find( typeof( T ), assemblies ); - } - - /// - /// 查找类型列表 - /// - /// 查找类型 - /// 在指定的程序集列表中查找 - public List Find( Type findType, List assemblies = null ) { - assemblies = assemblies ?? GetAssemblies(); - var result = new List(); - foreach( var assembly in assemblies ) - result.AddRange( GetTypes( findType, assembly ) ); - return result.Distinct().ToList(); - } - - /// - /// 获取类型列表 - /// - private List GetTypes( Type findType, Assembly assembly ) { - var result = new List(); - Type[] types; - try { - types = assembly.GetTypes(); - } - catch( ReflectionTypeLoadException ) { - return result; - } - if( types == null ) - return result; - foreach( var type in types ) - AddType( result, findType, type ); - return result; - } - - /// - /// 添加类型 - /// - private void AddType( List result, Type findType, Type type ) { - if( type.IsInterface || type.IsAbstract ) - return; - if ( findType.IsAssignableFrom( type ) == false && MatchGeneric( findType, type ) == false ) - return; - result.Add( type ); - } - - /// - /// 泛型匹配 - /// - private bool MatchGeneric( Type findType, Type type ) { - if ( findType.IsGenericTypeDefinition == false ) - return false; - var definition = findType.GetGenericTypeDefinition(); - foreach ( var implementedInterface in type.FindInterfaces( ( filter, criteria ) => true, null ) ) { - if( implementedInterface.IsGenericType == false ) - continue; - return definition.IsAssignableFrom( implementedInterface.GetGenericTypeDefinition() ); - } - return false; - } - } -} diff --git a/src/Util/Reflections/IFind.cs b/src/Util/Reflections/IFind.cs deleted file mode 100644 index a3d7b5133..000000000 --- a/src/Util/Reflections/IFind.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; - -namespace Util.Reflections { - /// - /// 类型查找器 - /// - public interface IFind { - /// - /// 获取程序集列表 - /// - List GetAssemblies(); - /// - /// 查找类型列表 - /// - /// 查找类型 - /// 在指定的程序集列表中查找 - List Find( List assemblies = null ); - /// - /// 查找类型列表 - /// - /// 查找类型 - /// 在指定的程序集列表中查找 - List Find( Type findType, List assemblies = null ); - } -} diff --git a/src/Util/Reflections/WebFinder.cs b/src/Util/Reflections/WebFinder.cs deleted file mode 100644 index e9be15931..000000000 --- a/src/Util/Reflections/WebFinder.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Generic; -using System.Reflection; -using Microsoft.Extensions.PlatformAbstractions; - -namespace Util.Reflections { - /// - /// Web类型查找器 - /// - public class WebFinder : Finder { - /// - /// 获取程序集列表 - /// - public override List GetAssemblies() { - LoadAssemblies( PlatformServices.Default.Application.ApplicationBasePath ); - return base.GetAssemblies(); - } - } -} diff --git a/src/Util/Signatures/ISignKey.cs b/src/Util/Signatures/ISignKey.cs deleted file mode 100644 index 00d277323..000000000 --- a/src/Util/Signatures/ISignKey.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Util.Signatures { - /// - /// 签名密钥 - /// - public interface ISignKey { - /// - /// 获取签名密钥 - /// - string GetKey(); - } -} diff --git a/src/Util/Signatures/ISignManager.cs b/src/Util/Signatures/ISignManager.cs deleted file mode 100644 index c9d327efb..000000000 --- a/src/Util/Signatures/ISignManager.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Util.Signatures { - /// - /// 签名服务 - /// - public interface ISignManager { - /// - /// 添加参数 - /// - /// 键 - /// 值 - ISignManager Add( string key, object value ); - /// - /// 签名 - /// - string Sign(); - } -} diff --git a/src/Util/Signatures/SignKey.cs b/src/Util/Signatures/SignKey.cs deleted file mode 100644 index 1aae081f6..000000000 --- a/src/Util/Signatures/SignKey.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Util.Signatures { - /// - /// 签名密钥 - /// - public class SignKey : ISignKey { - /// - /// 密钥 - /// - private readonly string _key; - - /// - /// 初始化签名密钥 - /// - /// 密钥 - public SignKey( string key ) { - _key = key; - } - - /// - /// 获取签名密钥 - /// - public string GetKey() { - return _key; - } - } -} diff --git a/src/Util/Signatures/SignManager.cs b/src/Util/Signatures/SignManager.cs deleted file mode 100644 index 6cd5a37c6..000000000 --- a/src/Util/Signatures/SignManager.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Util.Helpers; -using Util.Parameters; - -namespace Util.Signatures { - /// - /// 签名服务 - /// - public class SignManager : ISignManager { - /// - /// 签名密钥 - /// - private readonly string _key; - /// - /// Url参数生成器 - /// - private readonly UrlParameterBuilder _builder; - - /// - /// 初始化签名服务 - /// - /// 签名密钥 - /// Url参数生成器 - public SignManager( ISignKey key, UrlParameterBuilder builder = null ) { - key.CheckNull( nameof( key ) ); - _key = key.GetKey(); - _builder = builder == null ? new UrlParameterBuilder() : new UrlParameterBuilder( builder ); - } - - /// - /// 添加参数 - /// - /// 键 - /// 值 - public ISignManager Add( string key, object value ) { - _builder.Add( key, value ); - return this; - } - - /// - /// 签名 - /// - public string Sign() { - return Encrypt.Rsa2Sign( _builder.Result( true ), _key ); - } - } -} diff --git a/src/Util/Validations/Aspects/ValidAttribute.cs b/src/Util/Validations/Aspects/ValidAttribute.cs deleted file mode 100644 index a7594901f..000000000 --- a/src/Util/Validations/Aspects/ValidAttribute.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using AspectCore.DynamicProxy.Parameters; -using Util.Aspects.Base; -using Util.Helpers; - -namespace Util.Validations.Aspects { - /// - /// 验证拦截器 - /// - public class ValidAttribute : ParameterInterceptorBase { - /// - /// 执行 - /// - public override async Task Invoke( ParameterAspectContext context, ParameterAspectDelegate next ) { - Validate( context.Parameter ); - await next( context ); - } - - /// - /// 验证 - /// - private void Validate( Parameter parameter ) { - if ( Reflection.IsGenericCollection( parameter.RawType ) ) { - ValidateCollection( parameter ); - return; - } - IValidation validation = parameter.Value as IValidation; - validation?.Validate(); - } - - /// - /// 验证集合 - /// - private void ValidateCollection( Parameter parameter ) { - if ( !( parameter.Value is IEnumerable validations ) ) - return; - foreach ( var validation in validations ) - validation.Validate(); - } - } -} diff --git a/src/Util/Validations/DataAnnotationValidation.cs b/src/Util/Validations/DataAnnotationValidation.cs deleted file mode 100644 index 0fe0f4ebd..000000000 --- a/src/Util/Validations/DataAnnotationValidation.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; - -namespace Util.Validations { - /// - /// DataAnnotations验证操作 - /// - public static class DataAnnotationValidation { - /// - /// 验证 - /// - /// 验证目标 - public static ValidationResultCollection Validate( object target ) { - if( target == null ) - throw new ArgumentNullException( nameof( target ) ); - var result = new ValidationResultCollection(); - var validationResults = new List(); - var context = new ValidationContext( target, null, null ); - var isValid = Validator.TryValidateObject( target, context, validationResults, true ); - if ( !isValid ) - result.AddList( validationResults ); - return result; - } - } -} diff --git a/src/Util/Validations/IValidation.cs b/src/Util/Validations/IValidation.cs deleted file mode 100644 index ed5343ba2..000000000 --- a/src/Util/Validations/IValidation.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Util.Validations { - /// - /// 验证操作 - /// - public interface IValidation { - /// - /// 验证 - /// - ValidationResultCollection Validate(); - } -} diff --git a/src/Util/Validations/IValidationHandler.cs b/src/Util/Validations/IValidationHandler.cs deleted file mode 100644 index 7cc41174e..000000000 --- a/src/Util/Validations/IValidationHandler.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Util.Validations { - /// - /// 验证处理器 - /// - public interface IValidationHandler { - /// - /// 处理验证错误 - /// - /// 验证结果集合 - void Handle( ValidationResultCollection results ); - } -} diff --git a/src/Util/Validations/IValidationRule.cs b/src/Util/Validations/IValidationRule.cs deleted file mode 100644 index 652fd70e0..000000000 --- a/src/Util/Validations/IValidationRule.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Util.Validations { - /// - /// 验证规则 - /// - public interface IValidationRule { - /// - /// 验证 - /// - ValidationResult Validate(); - } -} diff --git a/src/Util/Validations/NothingHandler.cs b/src/Util/Validations/NothingHandler.cs deleted file mode 100644 index 7f15bb1c0..000000000 --- a/src/Util/Validations/NothingHandler.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Util.Validations { - /// - /// 验证失败,不做任何处理 - /// - public class NothingHandler : IValidationHandler { - /// - /// 处理验证错误 - /// - /// 验证结果集合 - public void Handle( ValidationResultCollection results ) { - } - } -} diff --git a/src/Util/Validations/ThrowHandler.cs b/src/Util/Validations/ThrowHandler.cs deleted file mode 100644 index a33a0020e..000000000 --- a/src/Util/Validations/ThrowHandler.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Linq; -using Util.Exceptions; - -namespace Util.Validations { - /// - /// 验证失败,抛出异常 - 默认验证处理器 - /// - public class ThrowHandler : IValidationHandler{ - /// - /// 处理验证错误 - /// - /// 验证结果集合 - public void Handle( ValidationResultCollection results ) { - if ( results.IsValid ) - return; - throw new Warning( results.First().ErrorMessage ); - } - } -} diff --git a/src/Util/Validations/ValidationResultCollection.cs b/src/Util/Validations/ValidationResultCollection.cs deleted file mode 100644 index d25db52bc..000000000 --- a/src/Util/Validations/ValidationResultCollection.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; - -namespace Util.Validations { - /// - /// 验证结果集合 - /// - public class ValidationResultCollection : IEnumerable { - /// - /// 初始化验证结果集合 - /// - public ValidationResultCollection() { - _results = new List(); - } - /// - /// 验证结果 - /// - private readonly List _results; - - /// - /// 成功验证结果集合 - /// - public static readonly ValidationResultCollection Success = new ValidationResultCollection(); - - /// - /// 是否有效 - /// - public bool IsValid => _results.Count == 0; - - /// - /// 验证结果个数 - /// - public int Count => _results.Count; - - /// - /// 添加验证结果 - /// - /// 验证结果 - public void Add( ValidationResult result ) { - if ( result == null ) - return; - _results.Add( result ); - } - - /// - /// 添加验证结果集合 - /// - /// 验证结果集合 - public void AddList( IEnumerable results ) { - if ( results == null ) - return; - foreach( var result in results ) - Add( result ); - } - - /// - /// 获取迭代器 - /// - IEnumerator IEnumerable.GetEnumerator() { - return _results.GetEnumerator(); - } - - /// - /// 获取迭代器 - /// - IEnumerator IEnumerable.GetEnumerator() { - return _results.GetEnumerator(); - } - } -} diff --git a/src/Util/Webs/Clients/HttpContentType.cs b/src/Util/Webs/Clients/HttpContentType.cs deleted file mode 100644 index 3d33b7117..000000000 --- a/src/Util/Webs/Clients/HttpContentType.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.ComponentModel; - -namespace Util.Webs.Clients { - /// - /// 内容类型 - /// - public enum HttpContentType { - /// - /// application/x-www-form-urlencoded - /// - [Description( "application/x-www-form-urlencoded" )] - FormUrlEncoded, - /// - /// application/json - /// - [Description( "application/json" )] - Json - } -} diff --git a/src/Util/Webs/Clients/HttpRequest.cs b/src/Util/Webs/Clients/HttpRequest.cs deleted file mode 100644 index 792b3ef3a..000000000 --- a/src/Util/Webs/Clients/HttpRequest.cs +++ /dev/null @@ -1,153 +0,0 @@ -using System; -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using Util.Helpers; - -namespace Util.Webs.Clients { - /// - /// Http请求 - /// - public class HttpRequest : HttpRequestBase, IHttpRequest { - /// - /// 执行成功的回调函数 - /// - private Action _successAction; - /// - /// 执行成功的回调函数 - /// - private Action _successStatusCodeAction; - - /// - /// 初始化Http请求 - /// - /// Http动词 - /// 地址 - public HttpRequest( HttpMethod httpMethod, string url ) : base( httpMethod, url ) { - } - - /// - /// 请求成功回调函数 - /// - /// 执行成功的回调函数,参数为响应结果 - public IHttpRequest OnSuccess( Action action ) { - _successAction = action; - return this; - } - - /// - /// 请求成功回调函数 - /// - /// 执行成功的回调函数,第一个参数为响应结果,第二个参数为状态码 - public IHttpRequest OnSuccess( Action action ) { - _successStatusCodeAction = action; - return this; - } - - /// - /// 成功处理操作 - /// - protected override void SuccessHandler( string result, HttpStatusCode statusCode, string contentType ) { - _successAction?.Invoke( result ); - _successStatusCodeAction?.Invoke( result, statusCode ); - } - - /// - /// 获取Json结果 - /// - public T ResultFromJson() { - return Util.Helpers.Json.ToObject( Result() ); - } - - /// - /// 获取Json结果 - /// - public async Task ResultFromJsonAsync() { - return Util.Helpers.Json.ToObject( await ResultAsync() ); - } - } - - /// - /// Http请求 - /// - public class HttpRequest : HttpRequestBase>, IHttpRequest where TResult : class { - /// - /// 执行成功的回调函数 - /// - private Action _successAction; - /// - /// 执行成功的回调函数 - /// - private Action _successStatusCodeAction; - /// - /// 执行成功的转换函数 - /// - private Func _convertAction; - - /// - /// 初始化Http请求 - /// - /// Http动词 - /// 地址 - public HttpRequest( HttpMethod httpMethod, string url ) : base( httpMethod,url ){ - } - - /// - /// 请求成功回调函数 - /// - /// 执行成功的回调函数,参数为响应结果 - /// 将结果字符串转换为指定类型,当默认转换实现无法转换时使用 - public IHttpRequest OnSuccess( Action action, Func convertAction = null ) { - _successAction = action; - _convertAction = convertAction; - return this; - } - - /// - /// 请求成功回调函数 - /// - /// 执行成功的回调函数,第一个参数为响应结果,第二个参数为状态码 - /// 将结果字符串转换为指定类型,当默认转换实现无法转换时使用 - public IHttpRequest OnSuccess( Action action, Func convertAction = null ) { - _successStatusCodeAction = action; - _convertAction = convertAction; - return this; - } - - /// - /// 成功处理操作 - /// - protected override void SuccessHandler( string result, HttpStatusCode statusCode, string contentType ) { - TResult successResult = ConvertTo( result, contentType ); - _successAction?.Invoke( successResult ); - _successStatusCodeAction?.Invoke( successResult, statusCode ); - } - - /// - /// 将结果字符串转换为指定类型 - /// - private TResult ConvertTo( string result, string contentType ) { - if( typeof( TResult ) == typeof( string ) ) - return Util.Helpers.Convert.To( result ); - if( _convertAction != null ) - return _convertAction( result ); - if( contentType.SafeString().ToLower() == "application/json" ) - return Json.ToObject( result ); - return null; - } - - /// - /// 获取Json结果 - /// - public TResult ResultFromJson() { - return Util.Helpers.Json.ToObject( Result() ); - } - - /// - /// 获取Json结果 - /// - public async Task ResultFromJsonAsync() { - return Util.Helpers.Json.ToObject( await ResultAsync() ); - } - } -} \ No newline at end of file diff --git a/src/Util/Webs/Clients/HttpRequestBase.cs b/src/Util/Webs/Clients/HttpRequestBase.cs deleted file mode 100644 index 303373249..000000000 --- a/src/Util/Webs/Clients/HttpRequestBase.cs +++ /dev/null @@ -1,335 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; -using Util.Helpers; - -namespace Util.Webs.Clients { - /// - /// Http请求 - /// - public abstract class HttpRequestBase where TRequest : IRequest { - /// - /// 地址 - /// - private readonly string _url; - /// - /// Http动词 - /// - private readonly HttpMethod _httpMethod; - /// - /// 参数集合 - /// - private IDictionary _params; - /// - /// Json参数 - /// - private string _json; - /// - /// 内容类型 - /// - private string _contentType; - /// - /// Cookie容器 - /// - private readonly CookieContainer _cookieContainer; - /// - /// 超时时间 - /// - private TimeSpan _timeout; - /// - /// 请求头集合 - /// - private readonly Dictionary _headers; - /// - /// 执行失败的回调函数 - /// - private Action _failAction; - /// - /// 执行失败的回调函数 - /// - private Action _failStatusCodeAction; - - /// - /// 初始化Http请求 - /// - /// Http动词 - /// 地址 - protected HttpRequestBase( HttpMethod httpMethod, string url ) { - if( string.IsNullOrWhiteSpace( url ) ) - throw new ArgumentNullException( nameof( url ) ); - _url = url; - _httpMethod = httpMethod; - _params = new Dictionary(); - _contentType = HttpContentType.FormUrlEncoded.Description(); - _cookieContainer = new CookieContainer(); - _timeout = new TimeSpan( 0, 0, 30 ); - _headers = new Dictionary(); - Encoding.RegisterProvider( CodePagesEncodingProvider.Instance ); - } - - /// - /// 设置内容类型 - /// - /// 内容类型 - public TRequest ContentType( HttpContentType contentType ) { - return ContentType( contentType.Description() ); - } - - /// - /// 设置内容类型 - /// - /// 内容类型 - public TRequest ContentType( string contentType ) { - _contentType = contentType; - return This(); - } - - /// - /// 返回自身 - /// - private TRequest This() { - return (TRequest)(object)this; - } - - /// - /// 设置Cookie - /// - /// 名称 - /// 值 - /// 有效时间,单位:天 - public TRequest Cookie( string name, string value, double expiresDate ) { - return Cookie( name, value, null, null, DateTime.Now.AddDays( expiresDate ) ); - } - - /// - /// 设置Cookie - /// - /// 名称 - /// 值 - /// 到期时间 - public TRequest Cookie( string name, string value, DateTime expiresDate ) { - return Cookie( name, value, null, null, expiresDate ); - } - - /// - /// 设置Cookie - /// - /// 名称 - /// 值 - /// 源服务器URL子集 - /// 所属域 - /// 到期时间 - public TRequest Cookie( string name, string value, string path = "/", string domain = null, DateTime? expiresDate = null ) { - return Cookie( new Cookie( name, value, path, domain ) { - Expires = expiresDate ?? DateTime.Now.AddYears( 1 ) - } ); - } - - /// - /// 设置Cookie - /// - /// cookie - public TRequest Cookie( Cookie cookie ) { - _cookieContainer.Add( new Uri( _url ), cookie ); - return This(); - } - - /// - /// 超时时间 - /// - /// 超时时间 - public TRequest Timeout( int timeout ) { - _timeout = new TimeSpan( 0, 0, timeout ); - return This(); - } - - /// - /// 请求头 - /// - /// 键 - /// 值 - public TRequest Header( string key, T value ) { - _headers.Add( key, value.SafeString() ); - return This(); - } - - /// - /// 添加参数字典 - /// - /// 参数字典 - public TRequest Data( IDictionary parameters ) { - _params = parameters ?? throw new ArgumentNullException( nameof( parameters ) ); - return This(); - } - - /// - /// 添加参数 - /// - /// 键 - /// 值 - public TRequest Data( string key, T value ) { - if( string.IsNullOrWhiteSpace( key ) ) - throw new ArgumentNullException( nameof( key ) ); - var data = value.SafeString(); - if( string.IsNullOrWhiteSpace( data ) ) - return This(); - _params.Add( key, data ); - return This(); - } - - /// - /// 添加Json参数 - /// - /// 值 - public TRequest JsonData( T value ) { - ContentType( HttpContentType.Json ); - _json = Util.Helpers.Json.ToJson( value ); - return This(); - } - - /// - /// 请求失败回调函数 - /// - /// 执行失败的回调函数,参数为响应结果 - public TRequest OnFail( Action action ) { - _failAction = action; - return This(); - } - - /// - /// 请求失败回调函数 - /// - /// 执行失败的回调函数,第一个参数为响应结果,第二个参数为状态码 - public TRequest OnFail( Action action ) { - _failStatusCodeAction = action; - return This(); - } - - /// - /// 获取结果 - /// - public string Result() { - return Async.Run( async () => { - SendBefore(); - var response = await SendAsync(); - var result = await response.Content.ReadAsStringAsync(); - SendAfter( result, response.StatusCode, GetContentType( response ) ); - return result; - } ); - } - - /// - /// 获取内容类型 - /// - private string GetContentType( HttpResponseMessage response ) { - return response?.Content?.Headers?.ContentType == null ? string.Empty : response.Content.Headers.ContentType.MediaType; - } - - /// - /// 发送前操作 - /// - protected virtual void SendBefore() { - } - - /// - /// 发送请求 - /// - private async Task SendAsync() { - HttpClient client = CreateHttpClient(); - return await client.SendAsync( CreateRequestMessage() ); - } - - /// - /// 发送后操作 - /// - protected virtual void SendAfter( string result, HttpStatusCode statusCode, string contentType ) { - if( IsSuccess( statusCode ) ) { - SuccessHandler( result, statusCode, contentType ); - return; - } - FailHandler( result, statusCode, contentType ); - } - - /// - /// 发送请求是否成功 - /// - /// 状态码 - protected virtual bool IsSuccess( HttpStatusCode statusCode ) { - return statusCode.Value() < 400; - } - - /// - /// 成功处理操作 - /// - protected virtual void SuccessHandler( string result, HttpStatusCode statusCode, string contentType ) { - } - - /// - /// 失败处理操作 - /// - protected virtual void FailHandler( string result, HttpStatusCode statusCode, string contentType ) { - _failAction?.Invoke( result ); - _failStatusCodeAction?.Invoke( result, statusCode ); - } - - /// - /// 获取结果 - /// - public async Task ResultAsync() { - SendBefore(); - var response = await SendAsync(); - return await response.Content.ReadAsStringAsync().ContinueWith( ( task, state ) => { - var result = task.Result; - SendAfter( result, response.StatusCode, state.SafeString() ); - return result; - }, GetContentType( response ) ); - } - - /// - /// 创建请求客户端 - /// - private HttpClient CreateHttpClient() { - return new HttpClient( new HttpClientHandler { CookieContainer = _cookieContainer } ) { Timeout = _timeout }; - } - - /// - /// 创建请求消息 - /// - private HttpRequestMessage CreateRequestMessage() { - var message = new HttpRequestMessage { - Method = _httpMethod, - RequestUri = new Uri( _url ), - Content = CreateHttpContent() - }; - foreach( var header in _headers ) - message.Headers.Add( header.Key, header.Value ); - return message; - } - - /// - /// 创建请求内容 - /// - private HttpContent CreateHttpContent() { - var contentType = _contentType.SafeString().ToLower(); - switch( contentType ) { - case "application/x-www-form-urlencoded": - return new FormUrlEncodedContent( _params ); - case "application/json": - return CreateJsonContent(); - } - throw new NotImplementedException( "未实现该ContentType" ); - } - - /// - /// 创建json内容类型 - /// - private HttpContent CreateJsonContent() { - if( string.IsNullOrWhiteSpace( _json ) ) - _json = Util.Helpers.Json.ToJson( _params ); - return new StringContent( _json, Encoding.UTF8, "application/json" ); - } - } -} diff --git a/src/Util/Webs/Clients/IHttpRequest.cs b/src/Util/Webs/Clients/IHttpRequest.cs deleted file mode 100644 index 134caff23..000000000 --- a/src/Util/Webs/Clients/IHttpRequest.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Net; -using System.Threading.Tasks; - -namespace Util.Webs.Clients { - /// - /// Http请求 - /// - public interface IHttpRequest : IRequest { - /// - /// 请求成功回调函数 - /// - /// 执行成功的回调函数,参数为响应结果 - IHttpRequest OnSuccess( Action action ); - /// - /// 请求成功回调函数 - /// - /// 执行成功的回调函数,第一个参数为响应结果,第二个参数为状态码 - IHttpRequest OnSuccess( Action action ); - /// - /// 获取Json结果 - /// - T ResultFromJson(); - /// - /// 获取Json结果 - /// - Task ResultFromJsonAsync(); - } - - /// - /// Http请求 - /// - /// 结果类型 - public interface IHttpRequest : IRequest> where TResult : class { - /// - /// 请求成功回调函数 - /// - /// 执行成功的回调函数,参数为响应结果 - /// 将结果字符串转换为指定类型,当默认转换实现无法转换时使用 - IHttpRequest OnSuccess( Action action, Func convertAction = null ); - /// - /// 请求成功回调函数 - /// - /// 执行成功的回调函数,第一个参数为响应结果,第二个参数为状态码 - /// 将结果字符串转换为指定类型,当默认转换实现无法转换时使用 - IHttpRequest OnSuccess( Action action, Func convertAction = null ); - /// - /// 获取Json结果 - /// - TResult ResultFromJson(); - /// - /// 获取Json结果 - /// - Task ResultFromJsonAsync(); - } -} diff --git a/src/Util/Webs/Clients/IRequest.cs b/src/Util/Webs/Clients/IRequest.cs deleted file mode 100644 index 425a66bd1..000000000 --- a/src/Util/Webs/Clients/IRequest.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Threading.Tasks; - -namespace Util.Webs.Clients { - /// - /// Http请求 - /// - public interface IRequest where TRequest : IRequest { - /// - /// 设置内容类型 - /// - /// 内容类型 - TRequest ContentType( HttpContentType contentType ); - /// - /// 设置内容类型 - /// - /// 内容类型 - TRequest ContentType( string contentType ); - /// - /// 设置Cookie - /// - /// 名称 - /// 值 - /// 有效时间,单位:天 - TRequest Cookie( string name, string value, double expiresDate ); - /// - /// 设置Cookie - /// - /// 名称 - /// 值 - /// 到期时间 - TRequest Cookie( string name, string value, DateTime expiresDate ); - /// - /// 设置Cookie - /// - /// 名称 - /// 值 - /// 源服务器URL子集 - /// 所属域 - /// 到期时间 - TRequest Cookie( string name, string value, string path = "/", string domain = null, DateTime? expiresDate = null ); - /// - /// 设置Cookie - /// - /// cookie - TRequest Cookie( Cookie cookie ); - /// - /// 超时时间 - /// - /// 超时时间,单位:秒 - TRequest Timeout( int timeout ); - /// - /// 请求头 - /// - /// 键 - /// 值 - TRequest Header( string key, T value ); - /// - /// 添加参数字典 - /// - /// 参数字典 - TRequest Data( IDictionary parameters ); - /// - /// 添加参数 - /// - /// 键 - /// 值 - TRequest Data( string key, T value ); - /// - /// 添加Json参数 - /// - /// 值 - TRequest JsonData( T value ); - /// - /// 请求失败回调函数 - /// - /// 执行失败的回调函数,参数为响应结果 - TRequest OnFail( Action action ); - /// - /// 请求失败回调函数 - /// - /// 执行失败的回调函数,第一个参数为响应结果,第二个参数为状态码 - TRequest OnFail( Action action ); - /// - /// 获取结果 - /// - string Result(); - /// - /// 获取结果 - /// - Task ResultAsync(); - } -} diff --git a/src/Util/Webs/Clients/WebClient.cs b/src/Util/Webs/Clients/WebClient.cs deleted file mode 100644 index 466ea3c97..000000000 --- a/src/Util/Webs/Clients/WebClient.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.Net.Http; - -namespace Util.Webs.Clients { - /// - /// Web客户端 - /// - public class WebClient { - /// - /// Get请求 - /// - /// 地址 - public IHttpRequest Get( string url ) { - return new HttpRequest( HttpMethod.Get, url ); - } - - /// - /// Post请求 - /// - /// 地址 - public IHttpRequest Post( string url ) { - return new HttpRequest( HttpMethod.Post, url ); - } - - /// - /// Put请求 - /// - /// 地址 - public IHttpRequest Put( string url ) { - return new HttpRequest( HttpMethod.Put, url ); - } - - /// - /// Delete请求 - /// - /// 地址 - public IHttpRequest Delete( string url ) { - return new HttpRequest( HttpMethod.Delete, url ); - } - } - - /// - /// Web客户端 - /// - /// 返回的结果类型 - public class WebClient where TResult : class { - /// - /// Get请求 - /// - /// 地址 - public IHttpRequest Get( string url ) { - return new HttpRequest( HttpMethod.Get, url ); - } - - /// - /// Post请求 - /// - /// 地址 - public IHttpRequest Post( string url ) { - return new HttpRequest( HttpMethod.Post, url ); - } - - /// - /// Put请求 - /// - /// 地址 - public IHttpRequest Put( string url ) { - return new HttpRequest( HttpMethod.Put, url ); - } - - /// - /// Delete请求 - /// - /// 地址 - public IHttpRequest Delete( string url ) { - return new HttpRequest( HttpMethod.Delete, url ); - } - } -} \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator.sln b/template/CodeSmith/CodeGenerator.sln deleted file mode 100644 index 89f26eb8d..000000000 --- a/template/CodeSmith/CodeGenerator.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeGenerator", "CodeGenerator\CodeGenerator.csproj", "{B58D16AD-9987-4BB8-8BB2-2A0CE8BC9829}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B58D16AD-9987-4BB8-8BB2-2A0CE8BC9829}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B58D16AD-9987-4BB8-8BB2-2A0CE8BC9829}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B58D16AD-9987-4BB8-8BB2-2A0CE8BC9829}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B58D16AD-9987-4BB8-8BB2-2A0CE8BC9829}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/template/CodeSmith/CodeGenerator.v11.suo b/template/CodeSmith/CodeGenerator.v11.suo deleted file mode 100644 index ff2331a4f..000000000 Binary files a/template/CodeSmith/CodeGenerator.v11.suo and /dev/null differ diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Common/Extensions.cs b/template/CodeSmith/CodeGenerator/01-Templates/Common/Extensions.cs deleted file mode 100644 index e0ef01cf3..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Common/Extensions.cs +++ /dev/null @@ -1,195 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using CodeSmith.Engine; - -namespace SchemaMapper -{ - public enum CodeLanguage - { - CSharp, - VisualBasic - } - - public static class Extensions - { - private static readonly HashSet _csharpKeywords; - private static readonly HashSet _visualBasicKeywords; - private static readonly Dictionary _csharpTypeAlias; - - static Extensions() - { - _csharpKeywords = new HashSet(StringComparer.Ordinal) - { - "as", "do", "if", "in", "is", - "for", "int", "new", "out", "ref", "try", - "base", "bool", "byte", "case", "char", "else", "enum", "goto", "lock", "long", "null", "this", "true", "uint", "void", - "break", "catch", "class", "const", "event", "false", "fixed", "float", "sbyte", "short", "throw", "ulong", "using", "while", - "double", "extern", "object", "params", "public", "return", "sealed", "sizeof", "static", "string", "struct", "switch", "typeof", "unsafe", "ushort", - "checked", "decimal", "default", "finally", "foreach", "private", "virtual", - "abstract", "continue", "delegate", "explicit", "implicit", "internal", "operator", "override", "readonly", "volatile", - "__arglist", "__makeref", "__reftype", "interface", "namespace", "protected", "unchecked", - "__refvalue", "stackalloc" - }; - - _visualBasicKeywords = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "as", "do", "if", "in", "is", "me", "of", "on", "or", "to", - "and", "dim", "end", "for", "get", "let", "lib", "mod", "new", "not", "rem", "set", "sub", "try", "xor", - "ansi", "auto", "byte", "call", "case", "cdbl", "cdec", "char", "cint", "clng", "cobj", "csng", "cstr", "date", "each", "else", - "enum", "exit", "goto", "like", "long", "loop", "next", "step", "stop", "then", "true", "wend", "when", "with", - "alias", "byref", "byval", "catch", "cbool", "cbyte", "cchar", "cdate", "class", "const", "ctype", "cuint", "culng", "endif", "erase", "error", - "event", "false", "gosub", "isnot", "redim", "sbyte", "short", "throw", "ulong", "until", "using", "while", - "csbyte", "cshort", "double", "elseif", "friend", "global", "module", "mybase", "object", "option", "orelse", "public", "resume", "return", "select", "shared", - "single", "static", "string", "typeof", "ushort", - "andalso", "boolean", "cushort", "decimal", "declare", "default", "finally", "gettype", "handles", "imports", "integer", "myclass", "nothing", "partial", "private", "shadows", - "trycast", "unicode", "variant", - "assembly", "continue", "delegate", "function", "inherits", "operator", "optional", "preserve", "property", "readonly", "synclock", "uinteger", "widening", - "addressof", "interface", "namespace", "narrowing", "overloads", "overrides", "protected", "structure", "writeonly", - "addhandler", "directcast", "implements", "paramarray", "raiseevent", "withevents", - "mustinherit", "overridable", - "mustoverride", - "removehandler", - "class_finalize", "notinheritable", "notoverridable", - "class_initialize" - }; - - _csharpTypeAlias = new Dictionary(16) - { - {"System.Int16", "short"}, - {"System.Int32", "int"}, - {"System.Int64", "long"}, - {"System.String", "string"}, - {"System.Object", "object"}, - {"System.Boolean", "bool"}, - {"System.Void", "void"}, - {"System.Char", "char"}, - {"System.Byte", "byte"}, - {"System.UInt16", "ushort"}, - {"System.UInt32", "uint"}, - {"System.UInt64", "ulong"}, - {"System.SByte", "sbyte"}, - {"System.Single", "float"}, - {"System.Double", "double"}, - {"System.Decimal", "decimal"} - }; - } - - public static string ToCamelCase(this string name) - { - return StringUtil.ToCamelCase(name); - } - - public static string ToPascalCase(this string name) - { - return StringUtil.ToPascalCase(name); - } - - public static string ToPlural( this string name ) { - return StringUtil.ToPlural( name ); - } - - public static string ToSingular(this string name) { - return StringUtil.ToSingular( name ); - } - - public static string ToFieldName(this string name) - { - return "_" + StringUtil.ToCamelCase(name); - } - - public static string MakeUnique(this string name, Func exists) - { - string uniqueName = name; - int count = 1; - - while (exists(uniqueName)) - uniqueName = string.Concat(name, count++); - - return uniqueName; - } - - public static bool IsKeyword(this string text, CodeLanguage language = CodeLanguage.CSharp) - { - return language == CodeLanguage.VisualBasic - ? _visualBasicKeywords.Contains(text) - : _csharpKeywords.Contains(text); - } - - public static string ToSafeName(this string name, CodeLanguage language = CodeLanguage.CSharp) - { - if (!name.IsKeyword(language)) - return name; - - return language == CodeLanguage.VisualBasic - ? string.Format("[{0}]", name) - : "@" + name; - } - - public static string ToType(this Type type, CodeLanguage language = CodeLanguage.CSharp) - { - return ToType(type.FullName, language); - } - - public static string ToType(this string type, CodeLanguage language = CodeLanguage.CSharp) - { - if (type == "System.Xml.XmlDocument") - type = "System.String"; - - string t; - if (language == CodeLanguage.CSharp && _csharpTypeAlias.TryGetValue(type, out t)) - return t; - - - return type; - } - - public static string ToNullableType(this Type type, bool isNullable = false, CodeLanguage language = CodeLanguage.CSharp) - { - return ToNullableType(type.FullName, isNullable, language); - } - - public static string ToNullableType(this string type, bool isNullable = false, CodeLanguage language = CodeLanguage.CSharp) - { - bool isValueType = type.IsValueType(); - - type = type.ToType(language); - type = type.Replace("System.", ""); - - if (!isValueType || !isNullable) - return type; - - return language == CodeLanguage.VisualBasic - ? string.Format("Nullable(Of {0})", type) - : type + "?"; - } - - public static bool IsValueType(this string type) - { - if (!type.StartsWith("System.")) - return false; - - var t = Type.GetType(type, false); - return t != null && t.IsValueType; - } - - public static string ToDelimitedString(this IEnumerable values, string delimiter, string format = null) - { - var sb = new StringBuilder(); - foreach (var i in values) - { - if (sb.Length > 0) - sb.Append(delimiter); - - if (string.IsNullOrEmpty(format)) - sb.Append(i); - else - sb.AppendFormat(format, i); - } - - return sb.ToString(); - } - - } -} diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Common/Generator.cs b/template/CodeSmith/CodeGenerator/01-Templates/Common/Generator.cs deleted file mode 100644 index 5ff41e3fd..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Common/Generator.cs +++ /dev/null @@ -1,787 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text.RegularExpressions; -using CodeSmith.Engine; -using SchemaExplorer; - -namespace SchemaMapper -{ - public enum TableNaming - { - Mixed = 0, - Plural = 1, - Singular = 2 - } - - public enum EntityNaming - { - Preserve = 0, - Plural = 1, - Singular = 2 - } - - public enum RelationshipNaming - { - None = 0, - Plural = 1, - ListSuffix = 2 - } - - public enum ContextNaming - { - Preserve = 0, - Plural = 1, - TableSuffix = 2 - } - - public class GeneratorSettings - { - public GeneratorSettings() - { - RelationshipNaming = RelationshipNaming.ListSuffix; - EntityNaming = EntityNaming.Singular; - TableNaming = TableNaming.Singular; - CleanExpressions = new List { @"^\d+" }; - IgnoreExpressions = new List(); - } - - public TableNaming TableNaming { get; set; } - - public EntityNaming EntityNaming { get; set; } - - public RelationshipNaming RelationshipNaming { get; set; } - - public ContextNaming ContextNaming { get; set; } - - public List IgnoreExpressions { get; set; } - - public List CleanExpressions { get; set; } - - public bool InclusionMode { get; set; } - - public bool IsIgnored(string name) - { - if (IgnoreExpressions.Count == 0) - return false; - - bool isMatch = IgnoreExpressions.Any(regex => Regex.IsMatch(name, regex)); - - return isMatch && !InclusionMode; - } - - public string CleanName(string name) - { - if (CleanExpressions.Count == 0) - return name; - - foreach (var regex in CleanExpressions.Where(r => !string.IsNullOrEmpty(r))) - if (Regex.IsMatch(name, regex)) - return Regex.Replace(name, regex, ""); - - return name; - } - - public string RelationshipName(string name) - { - if (RelationshipNaming == RelationshipNaming.None) - return name; - - if (RelationshipNaming == RelationshipNaming.ListSuffix) - return name + "List"; - - return StringUtil.ToPlural(name); - } - - public string ContextName(string name) - { - if (ContextNaming == ContextNaming.Preserve) - return name; - - if (ContextNaming == ContextNaming.TableSuffix) - return name + "Table"; - - return StringUtil.ToPlural(name); - } - - public string EntityName(string name) - { - if (TableNaming != TableNaming.Plural && EntityNaming == EntityNaming.Plural) - name = StringUtil.ToPlural(name); - else if (TableNaming != TableNaming.Singular && EntityNaming == EntityNaming.Singular) - name = StringUtil.ToSingular(name); - - return name; - } - } - - public class SchemaItemProcessedEventArgs : EventArgs - { - public SchemaItemProcessedEventArgs(string name) - { - _name = name; - } - - private readonly string _name; - public string Name - { - get { return _name; } - } - } - - public class UniqueNamer - { - private readonly ConcurrentDictionary> _names; - - public UniqueNamer() - { - _names = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); - Comparer = StringComparer.CurrentCulture; - - // add existing - UniqueContextName("ChangeTracker"); - UniqueContextName("Configuration"); - UniqueContextName("Database"); - UniqueContextName("InternalContext"); - } - - public IEqualityComparer Comparer { get; set; } - - public string UniqueName(string bucketName, string name) - { - var hashSet = _names.GetOrAdd(bucketName, k => new HashSet(Comparer)); - string result = name.MakeUnique(hashSet.Contains); - hashSet.Add(result); - - return result; - } - - public string UniqueClassName(string className) - { - const string globalClassName = "global::ClassName"; - return UniqueName(globalClassName, className); - } - - public string UniqueContextName(string name) - { - const string globalContextname = "global::ContextName"; - return UniqueName(globalContextname, name); - } - - public string UniqueRelationshipName(string name) - { - const string globalContextname = "global::RelationshipName"; - return UniqueName(globalContextname, name); - } - - } - - public class Generator - { - private readonly UniqueNamer _namer; - - public Generator() - { - _settings = new GeneratorSettings(); - _namer = new UniqueNamer(); - } - - public event EventHandler SchemaItemProcessed; - protected void OnSchemaItemProcessed(string name) - { - var handler = SchemaItemProcessed; - if (handler == null) - return; - - handler(this, new SchemaItemProcessedEventArgs(name)); - } - - private readonly GeneratorSettings _settings; - public GeneratorSettings Settings - { - get { return _settings; } - } - - public EntityContext Generate(DatabaseSchema databaseSchema) - { - // only DeepLoad when in ignore mode - databaseSchema.DeepLoad = !Settings.InclusionMode; - - var entityContext = new EntityContext(); - entityContext.DatabaseName = databaseSchema.Name; - - string dataContextName = StringUtil.ToPascalCase(databaseSchema.Name) + "Context"; - dataContextName = _namer.UniqueClassName(dataContextName); - - entityContext.ClassName = dataContextName; - - foreach (TableSchema t in databaseSchema.Tables) - { - if (Settings.IsIgnored(t.FullName)) - { - Debug.WriteLine("Skipping Table: " + t.FullName); - } - else if (IsManyToMany(t)) - { - CreateManyToMany(entityContext, t); - } - else - { - Debug.WriteLine("Getting Table Schema: " + t.FullName); - GetEntity(entityContext, t); - } - - OnSchemaItemProcessed(t.FullName); - } - - return entityContext; - } - - - private Entity GetEntity(EntityContext entityContext, TableSchema tableSchema, bool processRelationships = true, bool processMethods = true) - { - string key = tableSchema.FullName; - - Entity entity = entityContext.Entities.ByTable(key) - ?? CreateEntity(entityContext, tableSchema); - - if (!entity.Properties.IsProcessed) - CreateProperties(entity, tableSchema); - - if (processRelationships && !entity.Relationships.IsProcessed) - CreateRelationships(entityContext, entity, tableSchema); - - if (processMethods && !entity.Methods.IsProcessed) - CreateMethods(entity, tableSchema); - - entity.IsProcessed = true; - return entity; - } - - private Entity CreateEntity(EntityContext entityContext, TableSchema tableSchema) - { - var entity = new Entity - { - FullName = tableSchema.FullName, - TableName = tableSchema.Name, - TableSchema = tableSchema.Owner, - Description = tableSchema.Description, - Context = entityContext - }; - - string className = ToClassName(tableSchema.Name); - className = _namer.UniqueClassName(className); - - string mappingName = className + "Map"; - mappingName = _namer.UniqueClassName(mappingName); - - string contextName = Settings.ContextName(className); - contextName = ToPropertyName(entityContext.ClassName, contextName); - contextName = _namer.UniqueContextName(contextName); - - entity.ClassName = className; - entity.ContextName = contextName; - entity.MappingName = mappingName; - - entityContext.Entities.Add(entity); - - return entity; - } - - - private void CreateProperties(Entity entity, TableSchema tableSchema) - { - foreach (ColumnSchema columnSchema in tableSchema.Columns) - { - // skip unsupported type - if (columnSchema.NativeType.Equals("hierarchyid", StringComparison.OrdinalIgnoreCase) - || columnSchema.NativeType.Equals("sql_variant", StringComparison.OrdinalIgnoreCase)) - { - Debug.WriteLine(string.Format("Skipping column '{0}' because it has an unsupported db type '{1}'.", - columnSchema.Name, columnSchema.NativeType)); - continue; - } - - Property property = entity.Properties.ByColumn(columnSchema.Name); - - if (property == null) - { - property = new Property { ColumnName = columnSchema.Name }; - entity.Properties.Add(property); - } - - string propertyName = ToPropertyName(entity.ClassName, columnSchema.Name); - propertyName = _namer.UniqueName(entity.ClassName, propertyName); - - property.PropertyName = propertyName; - property.Description = columnSchema.Description; - - property.DataType = columnSchema.DataType; - property.SystemType = columnSchema.SystemType; - property.NativeType = columnSchema.NativeType; - - property.IsPrimaryKey = columnSchema.IsPrimaryKeyMember; - property.IsForeignKey = columnSchema.IsForeignKeyMember; - property.IsNullable = columnSchema.AllowDBNull; - - property.IsIdentity = IsIdentity(columnSchema); - property.IsRowVersion = IsRowVersion(columnSchema); - property.IsAutoGenerated = IsDbGenerated(columnSchema); - - if (columnSchema.IsUnique) - property.IsUnique = columnSchema.IsUnique; - - if (property.SystemType == typeof(string) - || property.SystemType == typeof(byte[])) - { - property.MaxLength = columnSchema.Size; - } - - if (property.SystemType == typeof(float) - || property.SystemType == typeof(double) - || property.SystemType == typeof(decimal)) - { - property.Precision = columnSchema.Precision; - property.Scale = columnSchema.Scale; - } - - property.IsProcessed = true; - } - - entity.Properties.IsProcessed = true; - } - - - private void CreateRelationships(EntityContext entityContext, Entity entity, TableSchema tableSchema) - { - foreach (TableKeySchema tableKey in tableSchema.ForeignKeys) - { - if (Settings.IsIgnored(tableKey.ForeignKeyTable.FullName) - || Settings.IsIgnored(tableKey.PrimaryKeyTable.FullName)) - continue; - - CreateRelationship(entityContext, entity, tableKey); - } - - entity.Relationships.IsProcessed = true; - } - - private void CreateRelationship(EntityContext entityContext, Entity foreignEntity, TableKeySchema tableKeySchema) - { - Entity primaryEntity = GetEntity(entityContext, tableKeySchema.PrimaryKeyTable, false, false); - - string primaryName = primaryEntity.ClassName; - string foreignName = foreignEntity.ClassName; - - string relationshipName = tableKeySchema.Name; - relationshipName = _namer.UniqueRelationshipName(relationshipName); - - bool foreignMembersRequired; - bool primaryMembersRequired; - - var foreignMembers = GetKeyMembers(foreignEntity, tableKeySchema.ForeignKeyMemberColumns, tableKeySchema.Name, out foreignMembersRequired); - var primaryMembers = GetKeyMembers(primaryEntity, tableKeySchema.PrimaryKeyMemberColumns, tableKeySchema.Name, out primaryMembersRequired); - - Relationship foreignRelationship = foreignEntity.Relationships - .FirstOrDefault(r => r.RelationshipName == relationshipName && r.IsForeignKey); - - if (foreignRelationship == null) - { - foreignRelationship = new Relationship { RelationshipName = relationshipName }; - foreignEntity.Relationships.Add(foreignRelationship); - } - foreignRelationship.IsMapped = true; - foreignRelationship.IsForeignKey = true; - foreignRelationship.ThisCardinality = foreignMembersRequired ? Cardinality.One : Cardinality.ZeroOrOne; - foreignRelationship.ThisEntity = foreignName; - foreignRelationship.ThisProperties = new List(foreignMembers); - foreignRelationship.OtherEntity = primaryName; - foreignRelationship.OtherProperties = new List(primaryMembers); - - string prefix = GetMemberPrefix(foreignRelationship, primaryName, foreignName); - - string foreignPropertyName = ToPropertyName(foreignEntity.ClassName, prefix + primaryName); - foreignPropertyName = _namer.UniqueName(foreignEntity.ClassName, foreignPropertyName); - foreignRelationship.ThisPropertyName = foreignPropertyName; - - // add reverse - Relationship primaryRelationship = primaryEntity.Relationships - .FirstOrDefault(r => r.RelationshipName == relationshipName && r.IsForeignKey == false); - - if (primaryRelationship == null) - { - primaryRelationship = new Relationship { RelationshipName = relationshipName }; - primaryEntity.Relationships.Add(primaryRelationship); - } - - primaryRelationship.IsMapped = false; - primaryRelationship.IsForeignKey = false; - primaryRelationship.ThisEntity = primaryName; - primaryRelationship.ThisProperties = new List(primaryMembers); - primaryRelationship.OtherEntity = foreignName; - primaryRelationship.OtherProperties = new List(foreignMembers); - - bool isOneToOne = IsOneToOne(tableKeySchema, foreignRelationship); - - if (isOneToOne) - primaryRelationship.ThisCardinality = primaryMembersRequired ? Cardinality.One : Cardinality.ZeroOrOne; - else - primaryRelationship.ThisCardinality = Cardinality.Many; - - string primaryPropertyName = prefix + foreignName; - if (!isOneToOne) - primaryPropertyName = Settings.RelationshipName(primaryPropertyName); - - primaryPropertyName = ToPropertyName(primaryEntity.ClassName, primaryPropertyName); - primaryPropertyName = _namer.UniqueName(primaryEntity.ClassName, primaryPropertyName); - - primaryRelationship.ThisPropertyName = primaryPropertyName; - - foreignRelationship.OtherPropertyName = primaryRelationship.ThisPropertyName; - foreignRelationship.OtherCardinality = primaryRelationship.ThisCardinality; - - primaryRelationship.OtherPropertyName = foreignRelationship.ThisPropertyName; - primaryRelationship.OtherCardinality = foreignRelationship.ThisCardinality; - - foreignRelationship.IsProcessed = true; - primaryRelationship.IsProcessed = true; - } - - private void CreateManyToMany(EntityContext entityContext, TableSchema joinTable) - { - if (joinTable.ForeignKeys.Count != 2) - return; - - var joinTableName = joinTable.Name; - var joinSchemaName = joinTable.Owner; - - // first fkey is always left, second fkey is right - var leftForeignKey = joinTable.ForeignKeys[0]; - var leftTable = leftForeignKey.PrimaryKeyTable; - var joinLeftColumn = leftForeignKey.ForeignKeyMemberColumns.Select(c => c.Name).ToList(); - var leftEntity = GetEntity(entityContext, leftTable, false, false); - - var rightForeignKey = joinTable.ForeignKeys[1]; - var rightTable = rightForeignKey.PrimaryKeyTable; - var joinRightColumn = rightForeignKey.ForeignKeyMemberColumns.Select(c => c.Name).ToList(); - var rightEntity = GetEntity(entityContext, rightTable, false, false); - - string leftPropertyName = Settings.RelationshipName(rightEntity.ClassName); - leftPropertyName = _namer.UniqueName(leftEntity.ClassName, leftPropertyName); - - string rightPropertyName = Settings.RelationshipName(leftEntity.ClassName); - rightPropertyName = _namer.UniqueName(rightEntity.ClassName, rightPropertyName); - - string relationshipName = string.Format("{0}|{1}", - leftForeignKey.Name, - rightForeignKey.Name); - - relationshipName = _namer.UniqueRelationshipName(relationshipName); - - var left = new Relationship { RelationshipName = relationshipName }; - left.IsForeignKey = false; - left.IsMapped = true; - - left.ThisCardinality = Cardinality.Many; - left.ThisEntity = leftEntity.ClassName; - left.ThisPropertyName = leftPropertyName; - - left.OtherCardinality = Cardinality.Many; - left.OtherEntity = rightEntity.ClassName; - left.OtherPropertyName = rightPropertyName; - - left.JoinTable = joinTableName; - left.JoinSchema = joinSchemaName; - left.JoinThisColumn = new List(joinLeftColumn); - left.JoinOtherColumn = new List(joinRightColumn); - - leftEntity.Relationships.Add(left); - - var right = new Relationship { RelationshipName = relationshipName }; - right.IsForeignKey = false; - right.IsMapped = false; - - right.ThisCardinality = Cardinality.Many; - right.ThisEntity = rightEntity.ClassName; - right.ThisPropertyName = rightPropertyName; - - right.OtherCardinality = Cardinality.Many; - right.OtherEntity = leftEntity.ClassName; - right.OtherPropertyName = leftPropertyName; - - right.JoinTable = joinTableName; - right.JoinSchema = joinSchemaName; - right.JoinThisColumn = new List(joinRightColumn); - right.JoinOtherColumn = new List(joinLeftColumn); - - rightEntity.Relationships.Add(right); - } - - - private void CreateMethods(Entity entity, TableSchema tableSchema) - { - if (tableSchema.HasPrimaryKey) - { - var method = GetMethodFromColumns(entity, tableSchema.PrimaryKey.MemberColumns); - if (method != null) - { - method.IsKey = true; - method.SourceName = tableSchema.PrimaryKey.FullName; - - if (!entity.Methods.Any(m=> m.NameSuffix == method.NameSuffix)) - entity.Methods.Add(method); - } - } - - GetIndexMethods(entity, tableSchema); - GetForeignKeyMethods(entity, tableSchema); - - entity.Methods.IsProcessed = true; - } - - private static void GetForeignKeyMethods(Entity entity, TableSchema table) - { - var columns = new List(); - - foreach (ColumnSchema column in table.ForeignKeyColumns) - { - columns.Add(column); - - Method method = GetMethodFromColumns(entity, columns); - if (method != null && !entity.Methods.Any(m => m.NameSuffix == method.NameSuffix)) - entity.Methods.Add(method); - - columns.Clear(); - } - } - - private static void GetIndexMethods(Entity entity, TableSchema table) - { - foreach (IndexSchema index in table.Indexes) - { - Method method = GetMethodFromColumns(entity, index.MemberColumns); - if (method == null) - continue; - - method.SourceName = index.FullName; - method.IsUnique = index.IsUnique; - method.IsIndex = true; - - if (!entity.Methods.Any(m => m.NameSuffix == method.NameSuffix)) - entity.Methods.Add(method); - } - } - - private static Method GetMethodFromColumns(Entity entity, IEnumerable columns) - { - var method = new Method(); - string methodName = string.Empty; - - foreach (var column in columns) - { - var property = entity.Properties.ByColumn(column.Name); - if (property == null) - continue; - - method.Properties.Add(property); - methodName += property.PropertyName; - } - - if (method.Properties.Count == 0) - return null; - - method.NameSuffix = methodName; - return method; - } - - - private static List GetKeyMembers(Entity entity, IEnumerable members, string name, out bool isRequired) - { - var keyMembers = new List(); - isRequired = false; - - foreach (var member in members) - { - var property = entity.Properties.ByColumn(member.Name); - - if (property == null) - throw new InvalidOperationException(string.Format( - "Could not find column {0} for relationship {1}.", - member.Name, - name)); - - if (!isRequired) - isRequired = property.IsRequired; - - keyMembers.Add(property.PropertyName); - } - - return keyMembers; - } - - private static string GetMemberPrefix(Relationship relationship, string primaryClass, string foreignClass) - { - string thisKey = relationship.ThisProperties.FirstOrDefault() ?? string.Empty; - string otherKey = relationship.OtherProperties.FirstOrDefault() ?? string.Empty; - - bool isSameName = thisKey.Equals(otherKey, StringComparison.OrdinalIgnoreCase); - isSameName = (isSameName || thisKey.Equals(primaryClass + otherKey, StringComparison.OrdinalIgnoreCase)); - - string prefix = string.Empty; - if (isSameName) - return prefix; - - prefix = thisKey.Replace(otherKey, ""); - prefix = prefix.Replace(primaryClass, ""); - prefix = prefix.Replace(foreignClass, ""); - prefix = Regex.Replace(prefix, @"(_ID|_id|_Id|\.ID|\.id|\.Id|ID|Id)$", ""); - prefix = Regex.Replace(prefix, @"^\d", ""); - - return prefix; - } - - private static bool IsOneToOne(TableKeySchema tableKeySchema, Relationship foreignRelationship) - { - bool isFkeyPkey = tableKeySchema.ForeignKeyTable.HasPrimaryKey - && tableKeySchema.ForeignKeyTable.PrimaryKey != null - && tableKeySchema.ForeignKeyTable.PrimaryKey.MemberColumns.Count == 1 - && tableKeySchema.ForeignKeyTable.PrimaryKey.MemberColumns.Contains( - foreignRelationship.ThisProperties.FirstOrDefault()); - - if (isFkeyPkey) - return true; - - // if f.key is unique - return tableKeySchema.ForeignKeyMemberColumns.All(column => column.IsUnique); - } - - private static bool IsManyToMany(TableSchema tableSchema) - { - // 1) Table must have Two ForeignKeys. - // 2) All columns must be either... - // a) Member of a Foreign Key. - // b) DbGenerated - - if (tableSchema.Columns.Count < 2) - return false; - - if (tableSchema.ForeignKeyColumns.Count != 2) - return false; - - // all columns are fkeys - if (tableSchema.Columns.Count == 2 && - tableSchema.ForeignKeyColumns.Count == 2) - return true; - - // check all non fkey columns to make sure db gen'd - return tableSchema.NonForeignKeyColumns.All(c => - IsDbGenerated(c) || HasDefaultValue(c)); - } - - #region Name Helpers - private string ToClassName(string name) - { - name = Settings.EntityName(name); - string legalName = ToLegalName(name); - - return legalName; - } - - private string ToPropertyName(string className, string name) - { - string propertyName = ToLegalName(name); - if (className.Equals(propertyName, StringComparison.OrdinalIgnoreCase)) - propertyName += "Member"; - - return propertyName; - } - - private string ToLegalName(string name) - { - string legalName = Settings.CleanName(name); - legalName = StringUtil.ToPascalCase(legalName); - - return legalName; - } - #endregion - - #region Column Flag Helpers - private static bool IsRowVersion(DataObjectBase column) - { - bool isTimeStamp = column.NativeType.Equals( - "timestamp", StringComparison.OrdinalIgnoreCase); - bool isRowVersion = column.NativeType.Equals( - "rowversion", StringComparison.OrdinalIgnoreCase); - - return (isTimeStamp || isRowVersion); - } - - private static bool IsDbGenerated(DataObjectBase column) - { - if (IsRowVersion(column)) - return true; - - if (IsIdentity(column)) - return true; - - bool isComputed = false; - string value; - try - { - if (column.ExtendedProperties.Contains(ExtendedPropertyNames.IsComputed)) - { - value = column.ExtendedProperties[ExtendedPropertyNames.IsComputed].Value.ToString(); - bool.TryParse(value, out isComputed); - } - } - catch (Exception ex) - { - Debug.WriteLine("Error: " + ex.Message); - } - - return isComputed; - } - - private static bool IsIdentity(DataObjectBase column) - { - string temp; - bool isIdentity = false; - try - { - if (column.ExtendedProperties.Contains(ExtendedPropertyNames.IsIdentity)) - { - temp = column.ExtendedProperties[ExtendedPropertyNames.IsIdentity].Value.ToString(); - bool.TryParse(temp, out isIdentity); - } - } - catch (Exception ex) - { - Debug.WriteLine("Error: " + ex.Message); - } - - return isIdentity; - } - - private static bool HasDefaultValue(DataObjectBase column) - { - try - { - if (!column.ExtendedProperties.Contains(ExtendedPropertyNames.DefaultValue)) - return false; - - string value = column.ExtendedProperties[ExtendedPropertyNames.DefaultValue].Value.ToString(); - return !string.IsNullOrEmpty(value); - } - catch (Exception ex) - { - Debug.WriteLine("Error: " + ex.Message); - } - - return false; - } - #endregion - } -} diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Common/Model.cs b/template/CodeSmith/CodeGenerator/01-Templates/Common/Model.cs deleted file mode 100644 index 03bff4639..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Common/Model.cs +++ /dev/null @@ -1,938 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Data; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Xml.Serialization; -using CodeSmith.Core.Extensions; - -namespace SchemaMapper -{ - /// - /// 系统操作 - /// - public class Sys - { - /// - /// 调试 - /// - public static void Debug() - { - System.Diagnostics.Debugger.Launch(); - System.Diagnostics.Debugger.Break(); - } - } - - /// - /// 扩展 - 可空类型 - /// - public static class NullExtensions - { - /// - /// 安全返回值 - /// - /// 可空值 - public static T SafeValue(this T? value) where T : struct - { - return value ?? default(T); - } - } - - #region Base - public enum Cardinality - { - ZeroOrOne, - One, - Many - } - - public class EntityBase - { - public bool IsProcessed { get; set; } - } - #endregion - - #region Model - [DebuggerDisplay("Context: {ContextName}, Database: {DatabaseName}")] - public class EntityContext : EntityBase - { - public EntityContext() - { - Entities = new EntityCollection(); - } - - public string ClassName { get; set; } - public string DatabaseName { get; set; } - - public EntityCollection Entities { get; set; } - - public void RenameEntity(string originalName, string newName) - { - if (originalName == newName) - return; - - Debug.WriteLine("Rename Entity '{0}' to '{1}'.", originalName, newName); - foreach (var entity in Entities) - { - if (entity.ClassName == originalName) - entity.ClassName = newName; - - foreach (var relationship in entity.Relationships) - { - if (relationship.ThisEntity == originalName) - relationship.ThisEntity = newName; - if (relationship.OtherEntity == originalName) - relationship.OtherEntity = newName; - } - } - } - - public void RenameProperty(string entityName, string originalName, string newName) - { - if (originalName == newName) - return; - - Debug.WriteLine("Rename Property '{0}' to '{1}' in Entity '{2}'.", originalName, newName, entityName); - foreach (var entity in Entities) - { - if (entity.ClassName == entityName) - { - var property = entity.Properties.ByProperty(originalName); - if (property != null) - property.PropertyName = newName; - } - - foreach (var relationship in entity.Relationships) - { - if (relationship.ThisEntity == entityName) - for (int i = 0; i < relationship.ThisProperties.Count; i++) - if (relationship.ThisProperties[i] == originalName) - relationship.ThisProperties[i] = newName; - - if (relationship.OtherEntity == entityName) - for (int i = 0; i < relationship.OtherProperties.Count; i++) - if (relationship.OtherProperties[i] == originalName) - relationship.OtherProperties[i] = newName; - } - } - - } - } - - [DebuggerDisplay("Class: {ClassName}, Table: {FullName}, Context: {ContextName}")] - public class Entity : EntityBase - { - public Entity() - { - Properties = new PropertyCollection(); - Relationships = new RelationshipCollection(); - Methods = new MethodCollection(); - } - - public EntityContext Context { get; set; } - public string ContextName { get; set; } - public string ClassName { get; set; } - public string MappingName { get; set; } - - public string TableSchema { get; set; } - public string TableName { get; set; } - public string FullName { get; set; } - /// - /// 描述 - /// - public string Description { get; set; } - - public PropertyCollection Properties { get; set; } - public RelationshipCollection Relationships { get; set; } - public MethodCollection Methods { get; set; } - - /// - /// 获取主键 - /// - public Property GetId() - { - foreach (var property in Properties) - { - if (property.IsPrimaryKey.SafeValue()) - return property; - } - return new Property(); - } - - /// - /// 获取导航属性描述 - /// - public string GetOtherDescription(string name) - { - return Context.Entities.ByClass(name).Description; - } - - /// - /// 获取属性集合,不包含Version属性 - /// - public List GetProperties(bool isExcludeVersion = true) - { - if (isExcludeVersion) - return Properties.Where(t => (t.ColumnName == "Version" && t.DataType == DbType.Binary) == false).ToList(); - return Properties.ToList(); - } - - /// - /// 获取第一个不是Id的属性 - /// - public Property GetFirstProperty() - { - return GetProperties().FirstOrDefault(t => t.IsPrimaryKey != true) ?? new Property(); - } - - /// - /// 是否最后一个属性 - /// - public bool IsLast(Property property, bool isExcludeVersion = true) - { - return GetProperties(isExcludeVersion).Last() == property; - } - - /// - /// 获取逗号 - /// - /// 属性 - /// 是否排除版本号 - public string GetComma(Property property, bool isExcludeVersion = true) - { - return IsLast(property, isExcludeVersion) ? "" : ","; - } - - /// - /// 获取主键 - /// - public Property GetKey() - { - var key = Properties.FirstOrDefault(t => t.IsPrimaryKey.SafeValue()); - if (key == null) - throw new Exception("没有设置主键"); - return key; - } - - /// - /// 获取主键类型 - /// - public Type GetKeyType() - { - var key = GetKey(); - return key.SystemType; - } - - /// - /// 获取主键类型 - /// - public string GetKeyTypeString() - { - return GetKeyType().ToNullableType(); - } - - /// - /// 获取聚合根 - /// - public string GetAggregateRoot() - { - if (GetKeyType() == typeof(string)) - return string.Format("AggregateRoot<{0},string>", ClassName); - if (GetKeyType() == typeof(int)) - return string.Format("AggregateRoot<{0},int>", ClassName); - if (GetKeyType() == typeof(long)) - return string.Format("AggregateRoot<{0},long>", ClassName); - return string.Format("AggregateRoot<{0}>", ClassName); - } - - /// - /// 获取树型实体和需要实现的接口 - /// - public string GetTreeEntityAndInterfaces() - { - StringBuilder result = new StringBuilder(); - result.Append(GetTreeEntity()); - result.Append(GetInterfaces()); - return result.ToString(); - } - - /// - /// 获取聚合根 - /// - public string GetTreeEntity() - { - if (GetKeyType() == typeof(string)) - return string.Format("TreeEntityBase<{0},string,string>", ClassName); - if (GetKeyType() == typeof(int)) - return string.Format("TreeEntityBase<{0},int,int?>", ClassName); - return string.Format("TreeEntityBase<{0}>", ClassName); - } - - /// - /// 获取聚合根和需要实现的接口 - /// - public string GetAggregateRootAndInterfaces() - { - StringBuilder result = new StringBuilder(); - result.Append(GetAggregateRoot()); - result.Append(GetInterfaces()); - return result.ToString(); - } - - /// - /// 获取需要实现的接口 - /// - public string GetInterfaces() { - StringBuilder result = new StringBuilder(); - result.Append(GetITenant()); - result.Append(GetISoftDelete()); - result.Append(GetIAudited()); - return result.ToString(); - } - - /// - /// 获取租户接口 - /// - private string GetITenant() - { - if (Properties.Any(t => t.ColumnName == "TenantId")) - return ",ITenant"; - return string.Empty; - } - - /// - /// 获取逻辑删除接口 - /// - private string GetISoftDelete() - { - if (Properties.Any(t => t.ColumnName == "IsDeleted")) - return ",IDelete"; - return string.Empty; - } - - /// - /// 获取审计接口 - /// - private string GetIAudited() - { - if (IsCreationAudited() && IsModificationAudited() ) - return ",IAudited"; - if (IsCreationAudited()) - return ",ICreationAudited"; - if (IsModificationAudited()) - return ",IModificationAudited"; - return string.Empty; - } - - /// - /// 是否创建审计 - /// - private bool IsCreationAudited() { - return Properties.Any( t => t.ColumnName == "CreationTime" )&&Properties.Any( t => t.ColumnName == "CreatorId" ); - } - - /// - /// 是否修改审计 - /// - private bool IsModificationAudited() - { - return Properties.Any(t => t.ColumnName == "LastModificationTime") && Properties.Any(t => t.ColumnName == "LastModifierId"); - } - - /// - /// 获取主键默认值 - /// - public string GetKeyDefault() - { - if (GetKeyType() == typeof(string)) - return "string.Empty"; - if (GetKeyType() == typeof(int)) - return "0"; - if (GetKeyType() == typeof(long)) - return "0"; - return "Guid.Empty"; - } - - /// - /// 获取键类型,但不包括Guid - /// - /// 是否在前面添加逗号 - public string GetKeyTypeNoContainsGuid(bool isAddCommon = true) - { - if (GetKeyType() == typeof(Guid)) - return ""; - string comma = isAddCommon ? "," : ""; - return comma + GetKeyTypeString(); - } - - /// - /// 获取转换为实体 - /// - public string GetToEntityConvert() - { - if (GetKeyType() == typeof(Guid)) - return ".ToGuid()"; - if (GetKeyType() == typeof(int)) - return ".ToInt()"; - return ""; - } - - /// - /// 获取转换为Dto - /// - public string GetToDtoConvert() - { - if (GetKeyType() == typeof(Guid)) - return ".ToString()"; - return ""; - } - - /// - /// 获取新的主键值 - /// - public string GetNewKey() - { - if (GetKeyType() == typeof(Guid)) - return "Guid.NewGuid()"; - if (GetKeyType() == typeof(string)) - return "Guid.NewGuid().ToString()"; - return ""; - } - - /// - /// 获取排序字段 - /// - public string GetOrderBy() - { - var exists = Properties.Any(t => t.PropertyName == "CreationTime" && t.SystemType == typeof(DateTime)); - if (exists) - return "CreationTime"; - return "Id"; - } - - /// - /// 获取名称空间 - /// - /// 基名称空间 - /// 层 - /// 分类 - public string GetNamespace(string baseNamespace, string layer, string category = "") - { - if (string.IsNullOrWhiteSpace(TableSchema) || TableSchema.ToLower().Trim() == "dbo") - return string.Format("{0}.{1}{2}", baseNamespace, layer, GetCategory(category)); - return string.Format("{0}.{1}.{2}{3}", baseNamespace, TableSchema, layer, GetCategory(category)); - } - - /// - /// 获取分类 - /// - private string GetCategory(string category) - { - if (string.IsNullOrWhiteSpace(category)) - return string.Empty; - return string.Format(".{0}", category); - } - - /// - /// 查询实体属性过滤 - /// - /// 属性 - public bool IsFilterByQueryObject(Property property) { - if (property.ColumnName == "Version" && property.DataType == DbType.Binary) - return true; - if (property.ColumnName == "IsDeleted" && property.DataType == DbType.Boolean) - return true; - return false; - } - - /// - /// 是否表单隐藏属性 - /// - /// 属性 - public bool IsHidden(Property property) { - if (property.ColumnName == "Version" && property.DataType == DbType.Binary) - return true; - if (property.ColumnName == "IsDeleted" && property.DataType == DbType.Boolean) - return true; - if (property.ColumnName == "CreationTime" && property.DataType == DbType.DateTime) - return true; - if (property.ColumnName == "CreatorId" && property.DataType == DbType.Guid) - return true; - if (property.ColumnName == "LastModificationTime" && property.DataType == DbType.DateTime) - return true; - if (property.ColumnName == "LastModifierId" && property.DataType == DbType.Guid ) - return true; - return false; - } - - /// - /// 获取格式化 - /// - /// 属性 - public string GetFormat(Property property) - { - if (property.DataType == DbType.Boolean) - return ".Description()"; - if (property.DataType == DbType.DateTime) - return ".ToDateTimeString( true )"; - return string.Empty; - } - - /// - /// 获取Grid显示的属性集合 - /// - public List GetGridProperties() - { - return Properties.Where(t => - ( t.ColumnName == "Version" && t.DataType == DbType.Binary ) == false && - ( t.ColumnName == "IsDeleted" && t.DataType == DbType.Boolean ) == false && - ( t.ColumnName == "CreatorId" && t.DataType == DbType.Guid ) == false && - ( t.ColumnName == "LastModificationTime" && t.DataType == DbType.DateTime ) == false && - (t.ColumnName == "LastModifierId" && t.DataType == DbType.Guid) == false && - (t.ColumnName == "TenantId" && t.DataType == DbType.String) == false - ) .ToList(); - } - - /// - /// 获取Grid项的逗号 - /// - /// 属性 - public string GetGridComma(Property property) - { - return GetGridProperties().Last() == property ? "" : ","; - } - - /// - /// 是否树型实体 - /// - public bool IsTreeEntity() { - if (Properties.Any(t => t.ColumnName == "ParentId") - && Properties.Any(t => t.ColumnName == "Path") - && Properties.Any(t => t.ColumnName == "Level")) { - return true; - } - return false; - } - - /// - /// 忽略树型实体属性 - /// - public bool IgnoreTreeEntityProperty(Property property) { - if (property.ColumnName == "ParentId") - return true; - if (property.ColumnName == "Path" && property.DataType == DbType.String) - return true; - if (property.ColumnName == "Level" && property.DataType == DbType.Int32) - return true; - if (property.ColumnName == "Enabled" && property.DataType == DbType.Boolean) - return true; - if (property.ColumnName == "SortId" && property.DataType == DbType.Int32) - return true; - return false; - } - } - - [DebuggerDisplay("Property: {PropertyName}, Column: {ColumnName}, Type: {NativeType}")] - public class Property : EntityBase - { - /// - /// 属性名 - /// - public string PropertyName { get; set; } - /// - /// 列名 - /// - public string ColumnName { get; set; } - /// - /// 描述 - /// - public string Description { get; set; } - - public DbType DataType { get; set; } - public string NativeType { get; set; } - - [XmlIgnore] - public Type SystemType { get; set; } - - public int? Order { get; set; } - public bool OrderSpecified - { - get { return Order.HasValue; } - } - - public bool? IsNullable { get; set; } - public bool IsNullableSpecified - { - get { return IsNullable.HasValue; } - } - - public bool IsRequired - { - get { return IsNullable == false; } - set { IsNullable = !value; } - } - public bool IsOptional - { - get { return IsNullable == true; } - set { IsNullable = value; } - } - - public bool? IsPrimaryKey { get; set; } - public bool IsPrimaryKeySpecified - { - get { return IsPrimaryKey.HasValue; } - } - public bool? IsForeignKey { get; set; } - public bool IsForeignKeySpecified - { - get { return IsForeignKey.HasValue; } - } - - public bool? IsAutoGenerated { get; set; } - public bool IsAutoGeneratedSpecified - { - get { return IsAutoGenerated.HasValue; } - } - public bool? IsReadOnly { get; set; } - public bool IsReadOnlySpecified - { - get { return IsReadOnly.HasValue; } - } - public bool? IsRowVersion { get; set; } - public bool IsRowVersionSpecified - { - get { return IsRowVersion.HasValue; } - } - public bool? IsIdentity { get; set; } - public bool IsIdentitySpecified - { - get { return IsIdentity.HasValue; } - } - public bool? IsUnique { get; set; } - public bool IsUniqueSpecified - { - get { return IsUnique.HasValue; } - } - - public bool? IsUnicode { get; set; } - public bool IsUnicodeSpecified - { - get { return IsUnicode.HasValue; } - } - public bool? IsFixedLength { get; set; } - public bool IsFixedLengthSpecified - { - get { return IsFixedLength.HasValue; } - } - - public int? MaxLength { get; set; } - public bool MaxLengthSpecified - { - get { return MaxLength.HasValue; } - } - - public byte? Precision { get; set; } - public bool PrecisionSpecified - { - get { return Precision.HasValue; } - } - public int? Scale { get; set; } - public bool ScaleSpecified - { - get { return Scale.HasValue; } - } - - /// - /// 是否需要验证 - /// - public bool HasValidate() - { - var result = GetValidations(); - if (result.Count > 0) - return true; - return false; - } - - /// - /// 获取验证列表 - /// - private List GetValidations() - { - List result = new List(); - ValidateRequired(result); - ValidateStringLength(result); - return result; - } - - /// - /// 验证是否必填项 - /// - private void ValidateRequired(List result) - { - if (IsRequired == false) - return; - result.Add(string.Format("[Required(ErrorMessage = \"{0}不能为空\")]", Description)); - } - - /// - /// 验证字符串长度 - /// - private void ValidateStringLength(List result) - { - if (SystemType != typeof(String)) - return; - if (MaxLength == -1) - return; - result.Add(string.Format("[StringLength( {0}, ErrorMessage = \"{1}输入过长,不能超过{0}位\" )]", MaxLength, Description)); - } - - /// - /// 验证 - /// - public string Validate() - { - StringBuilder result = new StringBuilder(); - List validations = GetValidations(); - if (validations.Count == 1) - result.Append(validations[0]); - else - AddValidations(result, validations); - return result.ToString(); - } - - /// - /// 添加验证列表 - /// - private void AddValidations(StringBuilder result, List validations) - { - for (int i = 0; i < validations.Count; i++) - { - if (i == 0) - { - result.AppendFormat("{0}\r\n", validations[i]); - continue; - } - if (i == validations.Count - 1) - result.AppendFormat(" {0}", validations[i]); - else - { - result.AppendFormat(" {0}\r\n", validations[i]); - } - } - } - - /// - /// 设置Description方法 - /// - public string ShowDescription() - { - if (DataType == DbType.Boolean) - return ".Description()"; - return string.Empty; - } - - /// - /// 类型名称 - /// - public string TypeName - { - get { return SystemType.ToNullableType(IsNullable == true); } - } - - /// - /// 是否标识 - /// - public bool IsKey - { - get { return IsPrimaryKey.SafeValue(); } - } - - /// - /// 格式化表格列 - /// - public string FormatGridColumn() - { - if (SystemType == typeof(DateTime)) - return ".FormatDate()"; - if (SystemType == typeof(bool)) - return ".FormatBool()"; - return string.Empty; - } - - /// - /// 获取表格列宽 - /// - public string GetGridColumnWidth() - { - if (SystemType == typeof(DateTime)) - return "120"; - if (SystemType == typeof(bool)) - return "60"; - return "100"; - } - - /// - /// 获取显示名称 - /// - public string GetDisplayName() - { - if (!string.IsNullOrWhiteSpace(Description)) - return Description; - return PropertyName; - } - } - - [DebuggerDisplay("Other: {OtherEntity}, Property: {OtherPropertyName}, Relationship: {RelationshipName}")] - public class Relationship : EntityBase - { - public Relationship() - { - OtherProperties = new List(); - ThisProperties = new List(); - } - - public string RelationshipName { get; set; } - - public string ThisEntity { get; set; } - public string ThisPropertyName { get; set; } - public Cardinality ThisCardinality { get; set; } - public List ThisProperties { get; set; } - - public string OtherEntity { get; set; } - public string OtherPropertyName { get; set; } - public Cardinality OtherCardinality { get; set; } - public List OtherProperties { get; set; } - - public bool? DeleteOnNull { get; set; } - public bool IsForeignKey { get; set; } - public bool IsMapped { get; set; } - - public bool IsManyToMany - { - get - { - return ThisCardinality == Cardinality.Many - && OtherCardinality == Cardinality.Many; - } - } - - public bool IsOneToOne - { - get - { - return ThisCardinality != Cardinality.Many - && OtherCardinality != Cardinality.Many; - } - } - - public string JoinTable { get; set; } - public string JoinSchema { get; set; } - public List JoinThisColumn { get; set; } - public List JoinOtherColumn { get; set; } - - } - - [DebuggerDisplay("Suffix: {NameSuffix}, IsKey: {IsKey}, IsUnique: {IsUnique}")] - public class Method : EntityBase - { - public Method() - { - Properties = new List(); - } - - public string NameSuffix { get; set; } - public string SourceName { get; set; } - - public bool IsKey { get; set; } - public bool IsUnique { get; set; } - public bool IsIndex { get; set; } - - public List Properties { get; set; } - } - #endregion - - #region Collections - public class EntityCollection - : ObservableCollection - { - public bool IsProcessed { get; set; } - - public Entity ByTable(string fullName) - { - return this.FirstOrDefault(x => x.FullName == fullName); - } - - public Entity ByTable(string tableName, string tableSchema) - { - return this.FirstOrDefault(x => x.TableName == tableName && x.TableSchema == tableSchema); - } - - public Entity ByClass(string className) - { - return this.FirstOrDefault(x => x.ClassName == className); - } - - public Entity ByContext(string contextName) - { - return this.FirstOrDefault(x => x.ContextName == contextName); - } - } - - public class PropertyCollection - : ObservableCollection - { - public bool IsProcessed { get; set; } - - public IEnumerable PrimaryKeys - { - get { return this.Where(p => p.IsPrimaryKey == true); } - } - - public IEnumerable ForeignKeys - { - get { return this.Where(p => p.IsForeignKey == true); } - } - - public Property ByColumn(string columnName) - { - return this.FirstOrDefault(x => x.ColumnName == columnName); - } - - public Property ByProperty(string propertyName) - { - return this.FirstOrDefault(x => x.PropertyName == propertyName); - } - } - - public class RelationshipCollection - : ObservableCollection - { - public bool IsProcessed { get; set; } - - public Relationship ByName(string name) - { - return this.FirstOrDefault(x => x.RelationshipName == name); - } - - public Relationship ByProperty(string propertyName) - { - return this.FirstOrDefault(x => x.ThisPropertyName == propertyName); - } - - public Relationship ByOther(string name) - { - return this.FirstOrDefault(x => x.OtherEntity == name); - } - } - - public class MethodCollection - : ObservableCollection - { - public bool IsProcessed { get; set; } - } - - #endregion -} - diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Common/Parser.cs b/template/CodeSmith/CodeGenerator/01-Templates/Common/Parser.cs deleted file mode 100644 index 95766be9a..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Common/Parser.cs +++ /dev/null @@ -1,639 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using ICSharpCode.NRefactory.CSharp; - -namespace SchemaMapper -{ - #region Mapping Parser - [DebuggerDisplay("Table: {TableName}, Entity: {EntityClass}, Mapping: {MappingClass}")] - public class ParsedEntity - { - public ParsedEntity() - { - Properties = new List(); - Relationships = new List(); - } - - public string EntityClass { get; set; } - public string MappingClass { get; set; } - - public string TableName { get; set; } - public string TableSchema { get; set; } - - public List Properties { get; private set; } - public List Relationships { get; private set; } - } - - [DebuggerDisplay("Column: {ColumnName}, Property: {PropertyName}")] - public class ParsedProperty - { - public string ColumnName { get; set; } - public string PropertyName { get; set; } - } - - [DebuggerDisplay("This: {ThisPropertyName}, Other: {OtherPropertyName}")] - public class ParsedRelationship - { - public ParsedRelationship() - { - ThisProperties = new List(); - JoinThisColumn = new List(); - JoinOtherColumn = new List(); - } - - public string ThisPropertyName { get; set; } - public List ThisProperties { get; private set; } - - public string OtherPropertyName { get; set; } - - public string JoinTable { get; set; } - public string JoinSchema { get; set; } - public List JoinThisColumn { get; private set; } - public List JoinOtherColumn { get; private set; } - } - - public class MappingVisitor : DepthFirstAstVisitor - { - public MappingVisitor() - { - MappingBaseType = "EntityTypeConfiguration"; - } - - public string MappingBaseType { get; set; } - public ParsedEntity ParsedEntity { get; set; } - - public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) - { - var baseType = typeDeclaration.BaseTypes.OfType().FirstOrDefault(); - if (baseType == null || baseType.MemberName != MappingBaseType) - return base.VisitTypeDeclaration(typeDeclaration, data); - - var entity = baseType.TypeArguments.OfType().FirstOrDefault(); - if (entity == null) - return base.VisitTypeDeclaration(typeDeclaration, data); - - if (ParsedEntity == null) - ParsedEntity = new ParsedEntity(); - - ParsedEntity.EntityClass = entity.MemberName; - ParsedEntity.MappingClass = typeDeclaration.Name; - - return base.VisitTypeDeclaration(typeDeclaration, ParsedEntity); - } - - public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data) - { - if (data == null) - return base.VisitInvocationExpression(invocationExpression, null); - - // visit all the methods - var identifier = invocationExpression.Target.Children.OfType().FirstOrDefault(); - string methodName = identifier == null ? string.Empty : identifier.Name; - - // the different types of incoming data, helps us know what we're parsing - var parsedEntity = data as ParsedEntity; - var parsedProperty = data as ParsedProperty; - var parsedRelationship = data as ParsedRelationship; - - switch (methodName) - { - case "ToTable": - var tableNameExpression = invocationExpression.Arguments - .OfType() - .ToArray(); - - string tableName = null; - string tableSchema = null; - - if (tableNameExpression.Length >= 1) - tableName = tableNameExpression[0].Value.ToString(); - if (tableNameExpression.Length >= 2) - tableSchema = tableNameExpression[1].Value.ToString(); - - // ToTable is either Entity -> Table map or Many to Many map - if (parsedEntity != null) - { - // when data is ParsedEntity, entity map - parsedEntity.TableName = tableName; - parsedEntity.TableSchema = tableSchema; - } - else if (parsedRelationship != null) - { - // when data is ParsedRelationship, many to many map - parsedRelationship.JoinTable = tableName; - parsedRelationship.JoinSchema = tableSchema; - } - break; - case "HasColumnName": - var columnNameExpression = invocationExpression.Arguments - .OfType() - .FirstOrDefault(); - - if (columnNameExpression == null) - break; - - // property to column map start. - string columnName = columnNameExpression.Value.ToString(); - var property = new ParsedProperty { ColumnName = columnName }; - ParsedEntity.Properties.Add(property); - - //only have column info at this point. pass data to get property name. - return base.VisitInvocationExpression(invocationExpression, property); - case "Property": - var propertyExpression = invocationExpression.Arguments - .OfType() - .FirstOrDefault(); - - if (parsedProperty == null || propertyExpression == null) - break; - - // ParsedProperty is passed in as data from HasColumnName, add property name - var propertyBodyExpression = propertyExpression.Body as MemberReferenceExpression; - if (propertyBodyExpression != null) - parsedProperty.PropertyName = propertyBodyExpression.MemberName; - - break; - case "Map": - // start a new Many to Many relationship - var mapRelation = new ParsedRelationship(); - ParsedEntity.Relationships.Add(mapRelation); - // pass to child nodes to fill in data - return base.VisitInvocationExpression(invocationExpression, mapRelation); - case "HasForeignKey": - var foreignExpression = invocationExpression.Arguments - .OfType() - .FirstOrDefault(); - - if (foreignExpression == null) - break; - - // when only 1 fkey, body will be member ref - if (foreignExpression.Body is MemberReferenceExpression) - { - var foreignBodyExpression = foreignExpression.Body as MemberReferenceExpression; - // start a new relationship - var foreignRelation = new ParsedRelationship(); - ParsedEntity.Relationships.Add(foreignRelation); - - foreignRelation.ThisProperties.Add(foreignBodyExpression.MemberName); - // pass as data for child nodes to fill in data - return base.VisitInvocationExpression(invocationExpression, foreignRelation); - } - // when more then 1 fkey, body will be an anonymous type - if (foreignExpression.Body is AnonymousTypeCreateExpression) - { - var foreignBodyExpression = foreignExpression.Body as AnonymousTypeCreateExpression; - var foreignRelation = new ParsedRelationship(); - ParsedEntity.Relationships.Add(foreignRelation); - - var properties = foreignBodyExpression.Children - .OfType() - .Select(m => m.MemberName); - - foreignRelation.ThisProperties.AddRange(properties); - - return base.VisitInvocationExpression(invocationExpression, foreignRelation); - } - break; - case "HasMany": - var hasManyExpression = invocationExpression.Arguments - .OfType() - .FirstOrDefault(); - - if (parsedRelationship == null || hasManyExpression == null) - break; - - // filling existing relationship data - var hasManyBodyExpression = hasManyExpression.Body as MemberReferenceExpression; - if (hasManyBodyExpression != null) - parsedRelationship.ThisPropertyName = hasManyBodyExpression.MemberName; - - break; - case "WithMany": - var withManyExpression = invocationExpression.Arguments - .OfType() - .FirstOrDefault(); - - if (parsedRelationship == null || withManyExpression == null) - break; - - // filling existing relationship data - var withManyBodyExpression = withManyExpression.Body as MemberReferenceExpression; - if (withManyBodyExpression != null) - parsedRelationship.OtherPropertyName = withManyBodyExpression.MemberName; - - break; - case "HasRequired": - case "HasOptional": - var hasExpression = invocationExpression.Arguments - .OfType() - .FirstOrDefault(); - - if (parsedRelationship == null || hasExpression == null) - break; - - // filling existing relationship data - var hasBodyExpression = hasExpression.Body as MemberReferenceExpression; - if (hasBodyExpression != null) - parsedRelationship.ThisPropertyName = hasBodyExpression.MemberName; - - break; - case "MapLeftKey": - if (parsedRelationship == null) - break; - - var leftKeyExpression = invocationExpression.Arguments - .OfType() - .Select(e => e.Value.ToString()); - - parsedRelationship.JoinThisColumn.AddRange(leftKeyExpression); - break; - case "MapRightKey": - if (parsedRelationship == null) - break; - - var rightKeyExpression = invocationExpression.Arguments - .OfType() - .Select(e => e.Value.ToString()); - - parsedRelationship.JoinOtherColumn.AddRange(rightKeyExpression); - break; - } - - return base.VisitInvocationExpression(invocationExpression, data); - } - } - - public static class MappingParser - { - public static ParsedEntity Parse(string mappingFile) - { - if (string.IsNullOrEmpty(mappingFile) || !File.Exists(mappingFile)) - return null; - - var parser = new CSharpParser(); - CompilationUnit compilationUnit; - - using (var stream = File.OpenText(mappingFile)) - compilationUnit = parser.Parse(stream, mappingFile); - - var visitor = new MappingVisitor(); - - visitor.VisitCompilationUnit(compilationUnit, null); - var parsedEntity = visitor.ParsedEntity; - - if (parsedEntity != null) - Debug.WriteLine("Parsed Mapping File: '{0}'; Properties: {1}; Relationships: {2}", - Path.GetFileName(mappingFile), - parsedEntity.Properties.Count, - parsedEntity.Relationships.Count); - - return parsedEntity; - } - } - #endregion - - #region Context Parser - [DebuggerDisplay("Context: {ContextClass}")] - public class ParsedContext - { - public ParsedContext() - { - Properties = new List(); - } - - public string ContextClass { get; set; } - - public List Properties { get; private set; } - } - - [DebuggerDisplay("Entity: {EntityClass}, Property: {ContextProperty}")] - public class ParsedEntitySet - { - public string EntityClass { get; set; } - public string ContextProperty { get; set; } - } - - public class ContextVisitor : DepthFirstAstVisitor - { - public ContextVisitor() - { - ContextBaseType = "DbContext"; - DataSetType = "DbSet"; - } - - public string ContextBaseType { get; set; } - public string DataSetType { get; set; } - - public ParsedContext ParsedContext { get; set; } - - public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) - { - var baseType = typeDeclaration.BaseTypes - .OfType() - .FirstOrDefault(); - - // warning: if inherited from custom base type, this will break - // anyway to improve this? - if (baseType == null || baseType.MemberName != ContextBaseType) - return base.VisitTypeDeclaration(typeDeclaration, data); - - if (ParsedContext == null) - ParsedContext = new ParsedContext(); - - ParsedContext.ContextClass = typeDeclaration.Name; - - return base.VisitTypeDeclaration(typeDeclaration, ParsedContext); - } - - public override object VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, object data) - { - if (data == null) - return base.VisitPropertyDeclaration(propertyDeclaration, null); - - // look for property to return generic DbSet type - var memberType = propertyDeclaration.ReturnType as MemberType; - if (memberType == null || memberType.MemberName != DataSetType) - return base.VisitPropertyDeclaration(propertyDeclaration, data); - - // get the first generic type - var entityType = memberType.TypeArguments - .OfType() - .FirstOrDefault(); - - if (entityType == null) - return base.VisitPropertyDeclaration(propertyDeclaration, data); - - var entitySet = new ParsedEntitySet - { - EntityClass = entityType.MemberName, - ContextProperty = propertyDeclaration.Name - }; - - ParsedContext.Properties.Add(entitySet); - - return base.VisitPropertyDeclaration(propertyDeclaration, data); - } - } - - public static class ContextParser - { - public static ParsedContext Parse(string contextFile) - { - if (string.IsNullOrEmpty(contextFile) || !File.Exists(contextFile)) - return null; - - var parser = new CSharpParser(); - CompilationUnit compilationUnit; - - using (var stream = File.OpenText(contextFile)) - compilationUnit = parser.Parse(stream, contextFile); - - var visitor = new ContextVisitor(); - - visitor.VisitCompilationUnit(compilationUnit, null); - var parsedContext = visitor.ParsedContext; - - if (parsedContext != null) - Debug.WriteLine("Parsed Context File: '{0}'; Entities: {1}", - Path.GetFileName(contextFile), - parsedContext.Properties.Count); - - return parsedContext; - } - } - #endregion - - public static class Synchronizer - { - public static bool UpdateFromSource(EntityContext generatedContext, string contextDirectory, string mappingDirectory) - { - if (generatedContext == null) - return false; - - // make sure to update the entities before the context - UpdateFromMapping(generatedContext, mappingDirectory); - UpdateFromContext(generatedContext, contextDirectory); - return true; - } - - private static void UpdateFromContext(EntityContext generatedContext, string contextDirectory) - { - if (generatedContext == null - || contextDirectory == null - || !Directory.Exists(contextDirectory)) - return; - - // parse context - ParsedContext parsedContext = null; - var files = Directory.EnumerateFiles(contextDirectory, "*.Generated.cs").GetEnumerator(); - while (files.MoveNext() && parsedContext == null) - parsedContext = ContextParser.Parse(files.Current); - - if (parsedContext == null) - return; - - if (generatedContext.ClassName != parsedContext.ContextClass) - { - Debug.WriteLine("Rename Context Class'{0}' to '{1}'.", - generatedContext.ClassName, - parsedContext.ContextClass); - - generatedContext.ClassName = parsedContext.ContextClass; - } - - foreach (var parsedProperty in parsedContext.Properties) - { - var entity = generatedContext.Entities.ByClass(parsedProperty.EntityClass); - if (entity == null) - continue; - - - if (entity.ContextName == parsedProperty.ContextProperty) - continue; - - Debug.WriteLine("Rename Context Property'{0}' to '{1}'.", - entity.ContextName, - parsedProperty.ContextProperty); - - entity.ContextName = parsedProperty.ContextProperty; - } - } - - private static void UpdateFromMapping(EntityContext generatedContext, string mappingDirectory) - { - if (generatedContext == null - || mappingDirectory == null - || !Directory.Exists(mappingDirectory)) - return; - - // parse all mapping files - var mappingFiles = Directory.EnumerateFiles(mappingDirectory, "*.Generated.cs"); - var parsedEntities = mappingFiles - .Select(MappingParser.Parse) - .Where(parsedEntity => parsedEntity != null) - .ToList(); - - var relationshipQueue = new List>(); - - // update all entity and property names first because relationships are linked by property names - foreach (var parsedEntity in parsedEntities) - { - // find entity by table name to support renaming entity - var entity = generatedContext.Entities - .ByTable(parsedEntity.TableName, parsedEntity.TableSchema); - - if (entity == null) - continue; - - // sync names - if (entity.MappingName != parsedEntity.MappingClass) - { - Debug.WriteLine("Rename Mapping Class'{0}' to '{1}'.", - entity.MappingName, - parsedEntity.MappingClass); - - entity.MappingName = parsedEntity.MappingClass; - } - - // use rename api to make sure all instances are renamed - generatedContext.RenameEntity(entity.ClassName, parsedEntity.EntityClass); - - // sync properties - foreach (var parsedProperty in parsedEntity.Properties) - { - // find property by column name to support property name rename - var property = entity.Properties.ByColumn(parsedProperty.ColumnName); - if (property == null) - continue; - - // use rename api to make sure all instances are renamed - generatedContext.RenameProperty( - entity.ClassName, - property.PropertyName, - parsedProperty.PropertyName); - } - - // save relationship for later processing - var item = new Tuple(entity, parsedEntity); - relationshipQueue.Add(item); - } - - // update relationships last - foreach (var tuple in relationshipQueue) - UpdateRelationships(generatedContext, tuple.Item1, tuple.Item2); - } - - private static void UpdateRelationships(EntityContext generatedContext, Entity entity, ParsedEntity parsedEntity) - { - // sync relationships - foreach (var parsedRelationship in parsedEntity.Relationships.Where(r => r.JoinTable == null)) - { - var parsedProperties = parsedRelationship.ThisProperties; - var relationship = entity.Relationships - .Where(r => !r.IsManyToMany) - .FirstOrDefault(r => r.ThisProperties.Except(parsedProperties).Count() == 0); - - if (relationship == null) - continue; - - bool isThisSame = relationship.ThisPropertyName == parsedRelationship.ThisPropertyName; - bool isOtherSame = relationship.OtherPropertyName == parsedRelationship.OtherPropertyName; - - if (isThisSame && isOtherSame) - continue; - - if (!isThisSame) - { - Debug.WriteLine("Rename Relationship Property '{0}.{1}' to '{0}.{2}'.", - relationship.ThisEntity, - relationship.ThisPropertyName, - parsedRelationship.ThisPropertyName); - - relationship.ThisPropertyName = parsedRelationship.ThisPropertyName; - } - if (!isOtherSame) - { - Debug.WriteLine("Rename Relationship Property '{0}.{1}' to '{0}.{2}'.", - relationship.OtherEntity, - relationship.OtherPropertyName, - parsedRelationship.OtherPropertyName); - - relationship.OtherPropertyName = parsedRelationship.OtherPropertyName; - } - - // sync other relationship - var otherEntity = generatedContext.Entities.ByClass(relationship.OtherEntity); - if (otherEntity == null) - continue; - - var otherRelationship = otherEntity.Relationships.ByName(relationship.RelationshipName); - if (otherRelationship == null) - continue; - - otherRelationship.ThisPropertyName = relationship.OtherPropertyName; - otherRelationship.OtherPropertyName = relationship.ThisPropertyName; - } - - // sync many to many - foreach (var parsedRelationship in parsedEntity.Relationships.Where(r => r.JoinTable != null)) - { - var joinThisColumn = parsedRelationship.JoinThisColumn; - var joinOtherColumn = parsedRelationship.JoinOtherColumn; - - var relationship = entity.Relationships - .Where(r => r.IsManyToMany) - .FirstOrDefault(r => - r.JoinThisColumn.Except(joinThisColumn).Count() == 0 && - r.JoinOtherColumn.Except(joinOtherColumn).Count() == 0 && - r.JoinTable == parsedRelationship.JoinTable && - r.JoinSchema == parsedRelationship.JoinSchema); - - if (relationship == null) - continue; - - - bool isThisSame = relationship.ThisPropertyName == parsedRelationship.ThisPropertyName; - bool isOtherSame = relationship.OtherPropertyName == parsedRelationship.OtherPropertyName; - - if (isThisSame && isOtherSame) - continue; - - if (!isThisSame) - { - Debug.WriteLine("Rename Relationship Property '{0}.{1}' to '{0}.{2}'.", - relationship.ThisEntity, - relationship.ThisPropertyName, - parsedRelationship.ThisPropertyName); - - relationship.ThisPropertyName = parsedRelationship.ThisPropertyName; - } - if (!isOtherSame) - { - Debug.WriteLine("Rename Relationship Property '{0}.{1}' to '{0}.{2}'.", - relationship.OtherEntity, - relationship.OtherPropertyName, - parsedRelationship.OtherPropertyName); - - relationship.OtherPropertyName = parsedRelationship.OtherPropertyName; - } - - // sync other relationship - var otherEntity = generatedContext.Entities.ByClass(relationship.OtherEntity); - if (otherEntity == null) - continue; - - var otherRelationship = otherEntity.Relationships.ByName(relationship.RelationshipName); - if (otherRelationship == null) - continue; - - otherRelationship.ThisPropertyName = relationship.OtherPropertyName; - otherRelationship.OtherPropertyName = relationship.ThisPropertyName; - } - } - } -} diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates.cst deleted file mode 100644 index 772341575..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates.cst +++ /dev/null @@ -1,659 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="Text" Debug="True" OutputType="None" %> - -<%@ Assembly Name="SchemaExplorer" %> -<%@ Assembly Name="CodeSmith.CustomProperties" %> -<%@ Assembly Name="Mono.Cecil" Path="..\Libraries" %> -<%@ Assembly Name="ICSharpCode.NRefactory" Path="..\Libraries" %> -<%@ Assembly Name="ICSharpCode.NRefactory.CSharp" Path="..\Libraries" %> -<%@ Assembly Src="Common\Model.cs" %> -<%@ Assembly Src="Common\Extensions.cs" %> -<%@ Assembly Src="Common\Generator.cs" %> -<%@ Assembly Src="Common\Parser.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.IO" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Database" - Type="SchemaExplorer.DatabaseSchema" - Category="1.数据库" - OnChanged="OnSourceDatabaseChanged" - Description="数据库" %> -<%@ Property Name="Namespace" - Type="System.String" - Category="2.1 命名空间" - Description="命名空间"%> -<%@ Property Name="DbName" - Type="System.String" - Category="2.2 数据库名" - Description="数据库名称,用于创建工作单元及其它类型名称,设置.将自动获取数据库名"%> - -<%@ Property Name="TableNaming" - Type="SchemaMapper.TableNaming" - Category="3.Hints" - Default="Singular" - Description="Provide generator a hint as to how the tables are named." %> -<%@ Property Name="EntityNaming" - Type="SchemaMapper.EntityNaming" - Category="3.Hints" - Default="Singular" - Description="Tell generator how the entities are to be named." %> -<%@ Property Name="RelationshipNaming" - Type="SchemaMapper.RelationshipNaming" - Category="3.Hints" - Default="Plural" - Description="Tell generator how the relationships are to be named." %> -<%@ Property Name="ContextNaming" - Type="SchemaMapper.ContextNaming" - Category="3.Hints" - Default="Plural" - Description="Tell generator how the context properties are to be named." %> - -<%@ Property Name="IgnoreList" - Type="CodeSmith.CustomProperties.StringCollection" - Category="4.Customization" - Default="sysdiagrams$" - Optional="True" - Description="List of regular expressions to ignore tables, views and commands when generating mapping." %> -<%@ Property Name="InclusionMode" - Type="Boolean" - Category="4.Customization" - Default="False" - Optional="True" - Description="Change the IgnoreList to be a list of table to include instead of ignore." %> -<%@ Property Name="CleanExpressions" - Type="CodeSmith.CustomProperties.StringCollection" - Category="4.Customization" - Default="^(sp|tbl|udf|vw)_" - Optional="True" - Description="List of regular expressions to clean table, view and column names." %> - -<%@ Register Name="EntityBaseClass" - Template="Templates\Domains\Entity.Base.cst" - MergeProperties="False" %> -<%@ Register Name="EntityClass" - Template="Templates\Domains\Entity.cst" - MergeProperties="False" %> -<%@ Register Name="FactoryClass" - Template="Templates\Domains\Factory.cst" - MergeProperties="False" %> -<%@ Register Name="IRepositoryClass" - Template="Templates\Domains\IRepository.cst" - MergeProperties="False" %> -<%@ Register Name="RepositoryClass" - Template="Templates\Datas\Repository.cst" - MergeProperties="False" %> -<%@ Register Name="PoClass" - Template="Templates\PersistObjects\Datas\Po.cst" - MergeProperties="False" %> -<%@ Register Name="PoExtensionClass" - Template="Templates\PersistObjects\Datas\PoExtension.cst" - MergeProperties="False" %> -<%@ Register Name="SqlServerPoMappingClass" - Template="Templates\PersistObjects\Datas\SqlServer\PoMapping.cst" - MergeProperties="False" %> -<%@ Register Name="PoIRepositoryClass" - Template="Templates\PersistObjects\Domains\IRepository.cst" - MergeProperties="False" %> -<%@ Register Name="PoRepositoryClass" - Template="Templates\PersistObjects\Datas\Repository.cst" - MergeProperties="False" %> -<%@ Register Name="IStoreClass" - Template="Templates\PersistObjects\Datas\IStore.cst" - MergeProperties="False" %> -<%@ Register Name="StoreClass" - Template="Templates\PersistObjects\Datas\Store.cst" - MergeProperties="False" %> -<%@ Register Name="IUnitOfWorkClass" - Template="Templates\Datas\IUnitOfWork.cst" - MergeProperties="False" %> -<%@ Register Name="SqlServerUnitOfWorkClass" - Template="Templates\Datas\SqlServer\UnitOfWork.cst" - MergeProperties="False" %> -<%@ Register Name="SqlServerMappingClass" - Template="Templates\Datas\SqlServer\Mapping.cst" - MergeProperties="False" %> -<%@ Register Name="IocConfigClass" - Template="Templates\Services\IocConfig.cst" - MergeProperties="False" %> -<%@ Register Name="DtoClass" - Template="Templates\Services\Dto.cst" - MergeProperties="False" %> -<%@ Register Name="DtoExtensionClass" - Template="Templates\Services\DtoExtension.cst" - MergeProperties="False" %> -<%@ Register Name="QueryClass" - Template="Templates\Services\Query.cst" - MergeProperties="False" %> -<%@ Register Name="IServiceClass" - Template="Templates\Services\IService.cst" - MergeProperties="False" %> -<%@ Register Name="ServiceClass" - Template="Templates\Services\Service.cst" - MergeProperties="False" %> -<%@ Register Name="ItemGroupClass" - Template="Templates\Domains\ItemGroup.cst" - MergeProperties="False" %> -<%@ Register Name="EntityTestClass" - Template="Templates\Tests\EntityTest.cst" - MergeProperties="False" %> -<%@ Register Name="EntityTestDataClass" - Template="Templates\Tests\EntityTest.Data.cst" - MergeProperties="False" %> -<%@ Register Name="DtoTestClass" - Template="Templates\Tests\DtoTest.cst" - MergeProperties="False" %> -<%@ Register Name="ServiceTestClass" - Template="Templates\Tests\ServiceTest.cst" - MergeProperties="False" %> -<% Generate(); %> - - \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Datas/IUnitOfWork.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Datas/IUnitOfWork.cst deleted file mode 100644 index 34317a2b6..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Datas/IUnitOfWork.cst +++ /dev/null @@ -1,23 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> - -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Namespace" Type="System.String" %> -<%@ Property Name="UnitOfWork" Type="System.String" %> -using Util.Datas.UnitOfWorks; - -namespace <%= Namespace %>.Data { - /// - /// 工作单元 - /// - public interface <%= UnitOfWork %> : IUnitOfWork { - } -} \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Datas/Repository.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Datas/Repository.cst deleted file mode 100644 index 8e9d84b3b..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Datas/Repository.cst +++ /dev/null @@ -1,36 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%@ Property Name="UnitOfWork" Type="System.String" %> -<%@ Property Name="IUnitOfWorkNamespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using <%= Entity.GetNamespace(Namespace,"Domain","Models") %>; -using <%= Entity.GetNamespace(Namespace,"Domain","Repositories") %>; -using Util.Datas.Ef.Core; - -namespace <%= string.Format("{0}.Data.Repositories.{1}",Namespace,Entity.TableSchema) %> { - /// - /// <%= Description %>仓储 - /// - public class <%= SafeClassName %>Repository : RepositoryBase<<%= SafeClassName %><%= Entity.GetKeyTypeNoContainsGuid() %>>, I<%= SafeClassName %>Repository { - /// - /// 初始化<%= Description %>仓储 - /// - /// 工作单元 - public <%= SafeClassName %>Repository( I<%= UnitOfWork %> unitOfWork ) : base( unitOfWork ) { - } - } -} diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Datas/SqlServer/Mapping.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Datas/SqlServer/Mapping.cst deleted file mode 100644 index cf6645909..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Datas/SqlServer/Mapping.cst +++ /dev/null @@ -1,266 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> - -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using <%= Entity.GetNamespace(Namespace,"Domain","Models") %>; - -namespace <%= string.Format("{0}.Data.SqlServer.Mappings.{1}",Namespace,Entity.TableSchema) %> { - /// - /// <%= Description %>映射配置 - /// - public class <%= SafeClassName %>Map : Util.Datas.Ef.SqlServer.AggregateRootMap<<%= SafeClassName %>> { - /// - /// 映射表 - /// - protected override void MapTable( EntityTypeBuilder<<%= SafeClassName %>> builder ) { - builder.ToTable( "<%= Entity.TableName %>", "<%= Entity.TableSchema %>" ); - } - - /// - /// 映射属性 - /// - protected override void MapProperties( EntityTypeBuilder<<%= SafeClassName %>> builder ) { - <% - foreach(var p in Entity.Properties) - { - Response.Write(PropertyExpression(p)); - } - %> - } - - /// - /// 映射导航属性 - /// - protected override void MapAssociations( EntityTypeBuilder<<%= SafeClassName %>> builder ) { - <% - foreach(var r in Entity.Relationships.Where(e => e.IsMapped)) - { - if (r.IsManyToMany) - Response.Write(ManyToManyExpression(r)); - else - Response.Write(RelationshipExpression(r)); - } - %> - } - } -} - \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Datas/SqlServer/UnitOfWork.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Datas/SqlServer/UnitOfWork.cst deleted file mode 100644 index f8eaece75..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Datas/SqlServer/UnitOfWork.cst +++ /dev/null @@ -1,33 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="EntityContext" Type="SchemaMapper.EntityContext" %> -<%@ Property Name="DbName" Type="System.String" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%@ Property Name="UnitOfWork" Type="System.String" %> -<%@ Property Name="IUnitOfWorkNamespace" Type="System.String" %> -using Microsoft.EntityFrameworkCore; -using Util.Datas.UnitOfWorks; - -namespace <%= Namespace %>.Data.SqlServer { - /// - /// 工作单元 - /// - public class <%= UnitOfWork %> : Util.Datas.Ef.SqlServer.UnitOfWork,I<%= UnitOfWork %> { - /// - /// 初始化工作单元 - /// - /// 配置项 - /// 工作单元服务 - public <%= UnitOfWork %>( DbContextOptions options, IUnitOfWorkManager unitOfWorkManager ) : base( options, unitOfWorkManager ) { - } - } -} \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Domains/Entity.Base.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Domains/Entity.Base.cst deleted file mode 100644 index fde571a02..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Domains/Entity.Base.cst +++ /dev/null @@ -1,116 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Register Name="TreeEntity" Template="TreeEntity.cst" MergeProperties="False" ExcludeProperties="" %> - -<%@ Assembly Src="..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -<%-- 如果是树型实体,则调用子模板 --%> -<% -if( Entity.IsTreeEntity() ){ - CodeTemplate template =new TreeEntity(); - template.SetProperty("Namespace",Namespace); - template.SetProperty("Entity",Entity); - template.Render(this.Response); - return; -} -%> -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using Util; -using Util.Domains; -using Util.Domains.Auditing; -using Util.Domains.Tenants; - -namespace <%= Entity.GetNamespace(Namespace,"Domain","Models") %> { - /// - /// <%= Description %> - /// - [DisplayName( "<%= Description %>" )] - public partial class <%= SafeClassName %> : <%= Entity.GetAggregateRootAndInterfaces() %> { - /// - /// 初始化<%= Description %> - /// - public <%= SafeClassName %>() : this( <%= Entity.GetKeyDefault() %> ) { - } - - /// - /// 初始化<%= Description %> - /// - /// <%= Description %>标识 - public <%= SafeClassName %>( <%= Entity.GetKeyTypeString() %> id ) : base( id ) { - <% foreach(var r in Entity.Relationships.Where(e => e.ThisCardinality == Cardinality.Many)) { %> - <%= r.ThisPropertyName.ToSafeName() %> = new List<<%= r.OtherEntity.ToSafeName() %>>(); - <% } %> - } - - <% foreach(Property p in Entity.Properties) { %> - <% if( p.IsPrimaryKey.SafeValue() ) continue; %> - <% if( p.ColumnName == "Version" && p.DataType == DbType.Binary ) continue;%> - /// - /// <%= p.Description %> - /// - [DisplayName( "<%= p.Description %>" )] - <% if( p.HasValidate() ) {%> - <%= p.Validate() %> - <%}%> - public <%= p.TypeName %> <%= p.PropertyName.ToSafeName() %> { get; set; } - <% }%> - <% foreach(var r in Entity.Relationships) { %> - - <% if(r.ThisCardinality == Cardinality.Many) { %> - /// - /// <%= Entity.GetOtherDescription( r.OtherEntity ) %>列表 - /// - public ICollection<<%= r.OtherEntity.ToSafeName() %>> <%= r.ThisPropertyName.ToSafeName() %> { get; set; } - <% } else { %> - /// - /// <%= Entity.GetOtherDescription( r.OtherEntity ) %> - /// - public <%= r.OtherEntity.ToSafeName() %> <%= r.ThisPropertyName.ToSafeName() %> { get; set; } - <% } %> - <% }%> - - /// - /// 添加描述 - /// - protected override void AddDescriptions() { - <% foreach(Property p in Entity.Properties) { %> - <% if( p.ColumnName == "Version" && p.DataType == DbType.Binary ) continue;%> - <% if( p.ColumnName == "IsDeleted" && p.DataType == DbType.Boolean ) continue;%> - <% if( p.IsPrimaryKey.Value ){ %> - AddDescription( "<%=p.Description %>", Id ); - <% continue;} %> - AddDescription( "<%=p.Description %>", <%= p.PropertyName.ToSafeName() %><%= p.ShowDescription() %> ); - <% } %> - } - - /// - /// 添加变更列表 - /// - protected override void AddChanges( <%= SafeClassName %> other ) { - <% foreach(Property p in Entity.Properties) { %> - <% if( p.ColumnName == "Version" && p.DataType == DbType.Binary ) continue;%> - <% if( p.ColumnName == "IsDeleted" && p.DataType == DbType.Boolean ) continue;%> - <% if( p.IsPrimaryKey.Value ){ %> - AddChange( "Id", "<%=p.Description %>", Id, other.Id ); - <%continue; } %> - AddChange( "<%=p.ColumnName %>", "<%=p.Description %>", <%= p.PropertyName.ToSafeName() %>, other.<%= p.PropertyName.ToSafeName() %> ); - <% } %> - } - } -} \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Domains/Entity.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Domains/Entity.cst deleted file mode 100644 index 2efd6705f..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Domains/Entity.cst +++ /dev/null @@ -1,20 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" Description="实体命名空间" %> -namespace <%= Entity.GetNamespace(Namespace,"Domain","Models") %> { - /// - /// <%= Entity.Description %> - /// - public partial class <%= Entity.ClassName.ToSafeName() %> { - } -} \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Domains/Factory.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Domains/Factory.cst deleted file mode 100644 index 0ee0f3b09..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Domains/Factory.cst +++ /dev/null @@ -1,47 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using System; -using <%= Entity.GetNamespace(Namespace,"Domains","Models") %>; - -namespace <%= Entity.GetNamespace(Namespace,"Domains","Factories") %> { - /// - /// <%= Description %>工厂 - /// - public static class <%= SafeClassName %>Factory { - /// - /// 创建<%= Description %> - /// - <% foreach(Property p in Entity.Properties) { %> - /// <%= p.Description %> - <%} %> - public static <%= SafeClassName %> Create( - <% foreach(Property p in Entity.Properties) { %> - <%= p.TypeName %> <%= p.PropertyName.ToSafeName().ToCamelCase() %><%= Entity.GetComma(p,false) %> - <%} %> - ) { - <%= SafeClassName %> result; - <% foreach(Property p in Entity.Properties) { %> - <% if( p.IsPrimaryKey.SafeValue() ) {%> - result = new <%= SafeClassName %>( <%= p.PropertyName.ToSafeName().ToCamelCase() %> ); - <% continue;} %> - result.<%= p.PropertyName.ToSafeName() %> = <%= p.PropertyName.ToSafeName().ToCamelCase() %>; - <%} %> - return result; - } - } -} \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Domains/IRepository.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Domains/IRepository.cst deleted file mode 100644 index cf70a47f8..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Domains/IRepository.cst +++ /dev/null @@ -1,27 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using Util.Domains.Repositories; -using <%= Entity.GetNamespace(Namespace,"Domain","Models") %>; - -namespace <%= Entity.GetNamespace(Namespace,"Domain","Repositories") %> { - /// - /// <%= Description %>仓储 - /// - public interface I<%= SafeClassName %>Repository : IRepository<<%= SafeClassName %><%= Entity.GetKeyTypeNoContainsGuid() %>> { - } -} \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Domains/ItemGroup.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Domains/ItemGroup.cst deleted file mode 100644 index bc21138f8..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Domains/ItemGroup.cst +++ /dev/null @@ -1,19 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entities" Type="List" %> - -<% foreach( Entity entity in Entities ) {%> - - <%= entity.ClassName %>.cs - -<% }%> - \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Domains/TreeEntity.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Domains/TreeEntity.cst deleted file mode 100644 index 060edf987..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Domains/TreeEntity.cst +++ /dev/null @@ -1,109 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using Util; -using Util.Domains; -using Util.Domains.Trees; -using Util.Domains.Auditing; -using Util.Domains.Tenants; - -namespace <%= Entity.GetNamespace(Namespace,"Domains","Models") %> { - /// - /// <%= Description %> - /// - [Description( "<%= Description %>" )] - public partial class <%= SafeClassName %> : <%= Entity.GetTreeEntityAndInterfaces() %> { - /// - /// 初始化<%= Description %> - /// - public <%= SafeClassName %>() - : this( <%= Entity.GetKeyDefault() %>, "", 0 ) { - } - - /// - /// 初始化<%= Description %> - /// - /// <%= Description %>标识 - /// 路径 - /// 级数 - public <%= SafeClassName %>( <%= Entity.GetKeyTypeString() %> id, string path, int level ) - : base( id, path, level ) { - <% foreach(var r in Entity.Relationships.Where(e => e.ThisCardinality == Cardinality.Many)) { %> - <%= r.ThisPropertyName.ToSafeName() %> = new List<<%= r.OtherEntity.ToSafeName() %>>(); - <% } %> - } - - <% foreach(Property p in Entity.Properties) { %> - <% if( p.IsPrimaryKey.SafeValue() ) continue; %> - <% if( p.ColumnName == "Version" && p.DataType == DbType.Binary ) continue;%> - <% if( Entity.IgnoreTreeEntityProperty(p) ) continue;%> - /// - /// <%= p.Description %> - /// - <% if( p.HasValidate() ) {%> - <%= p.Validate() %> - <%}%> - public <%= p.TypeName %> <%= p.PropertyName.ToSafeName() %> { get; set; } - <% }%> - <% foreach(var r in Entity.Relationships) { %> - - <% if(r.ThisCardinality == Cardinality.Many) { %> - /// - /// <%= Entity.GetOtherDescription( r.OtherEntity ) %>列表 - /// - public ICollection<<%= r.OtherEntity.ToSafeName() %>> <%= r.ThisPropertyName.ToSafeName() %> { get; set; } - <% } else { %> - /// - /// <%= Entity.GetOtherDescription( r.OtherEntity ) %> - /// - public virtual <%= r.OtherEntity.ToSafeName() %> <%= r.ThisPropertyName.ToSafeName() %> { get; set; } - <% } %> - <% }%> - - /// - /// 添加描述 - /// - protected override void AddDescriptions() { - <% foreach(Property p in Entity.Properties) { %> - <% if( p.ColumnName == "Version" && p.DataType == DbType.Binary ) continue;%> - <% if( p.ColumnName == "IsDeleted" && p.DataType == DbType.Boolean ) continue;%> - <% if( p.IsPrimaryKey.Value ){ %> - AddDescription( "<%=p.Description %>", Id ); - <% continue;} %> - AddDescription( "<%=p.Description %>", <%= p.PropertyName.ToSafeName() %><%= p.ShowDescription() %> ); - <% } %> - } - - /// - /// 添加变更列表 - /// - protected override void AddChanges( <%= SafeClassName %> newEntity ) { - <% foreach(Property p in Entity.Properties) { %> - <% if( p.ColumnName == "Version" && p.DataType == DbType.Binary ) continue;%> - <% if( p.ColumnName == "IsDeleted" && p.DataType == DbType.Boolean ) continue;%> - <% if( p.IsPrimaryKey.Value ){ %> - AddChange( "Id", "<%=p.Description %>", Id, newEntity.Id ); - <%continue; } %> - AddChange( "<%=p.ColumnName %>", "<%=p.Description %>", <%= p.PropertyName.ToSafeName() %>, newEntity.<%= p.PropertyName.ToSafeName() %> ); - <% } %> - } - } -} \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Datas/IStore.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Datas/IStore.cst deleted file mode 100644 index b064669a3..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Datas/IStore.cst +++ /dev/null @@ -1,29 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%@ Property Name="UnitOfWork" Type="System.String" %> -<%@ Property Name="IUnitOfWorkNamespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using <%= Entity.GetNamespace(Namespace,"Datas","Pos") %>; -using Util.Datas.Persistence; - -namespace <%= Entity.GetNamespace(Namespace,"Datas","Stores") %> { - /// - /// <%= Description %>持久化存储 - /// - public interface I<%= SafeClassName %>PoStore : IPersistentStore<<%= SafeClassName %>Po<%= Entity.GetKeyTypeNoContainsGuid() %>> { - } -} diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Datas/Po.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Datas/Po.cst deleted file mode 100644 index 785cd29a7..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Datas/Po.cst +++ /dev/null @@ -1,35 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using System; -using Util.Datas.Persistence; - -namespace <%= string.Format("{0}.Datas.{1}.Pos",Namespace,Entity.TableSchema) %> { - /// - /// <%= Description %>持久化对象 - /// - public class <%= SafeClassName %>Po : PersistentObjectBase<<%= Entity.GetKeyTypeString() %>><%=Entity.GetInterfaces()%>{ - <% foreach(Property p in Entity.Properties) { %> - <% if( p.IsPrimaryKey.SafeValue() ) continue; %> - <% if( p.ColumnName == "Version" && p.DataType == DbType.Binary ) continue;%> - /// - /// <%= p.Description %> - /// - public <%= p.TypeName %> <%= p.PropertyName.ToSafeName() %> { get; set; } - <% }%> - } -} \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Datas/PoExtension.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Datas/PoExtension.cst deleted file mode 100644 index 8dc8008d9..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Datas/PoExtension.cst +++ /dev/null @@ -1,79 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using Util; -using <%= Entity.GetNamespace(Namespace,"Domains","Models") %>; -using Util.Maps; - -namespace <%= string.Format("{0}.Datas.{1}.Pos",Namespace,Entity.TableSchema) %> { - /// - /// <%= Description %>持久化对象扩展 - /// - public static partial class Extension { - /// - /// 转换为<%= Description %>实体 - /// - /// <%= Description %>持久化对象 - public static <%= SafeClassName %> ToEntity( this <%= SafeClassName %>Po po ) { - if ( po == null ) - return null; - return po.MapTo( new <%= SafeClassName %>( po.Id ) ); - } - - /// - /// 转换为<%= Description %>实体 - /// - /// <%= Description %>持久化对象 - private static <%= SafeClassName %> ToEntity2( this <%= SafeClassName %>Po po ) { - if( po == null ) - return null; - return new <%= SafeClassName %>( po.Id ) { - <% foreach(Property p in Entity.Properties) { %> - <% if( p.IsPrimaryKey.SafeValue() ) continue; %> - <%= p.PropertyName.ToSafeName() %> = po.<%= p.PropertyName.ToSafeName() %>, - <%}%> - }; - } - - /// - /// 转换为<%= Description %>持久化对象 - /// - /// <%= Description %>实体 - public static <%= SafeClassName %>Po ToPo(this <%= SafeClassName %> entity) { - if( entity == null ) - return null; - return entity.MapTo<<%= SafeClassName %>Po>(); - } - - /// - /// 转换为<%= Description %>持久化对象 - /// - /// <%= Description %>实体 - private static <%= SafeClassName %>Po ToPo2( this <%= SafeClassName %> entity ) { - if( entity == null ) - return null; - return new <%= SafeClassName %>Po { - <% foreach(Property p in Entity.Properties) { %> - <% if( p.IsPrimaryKey.SafeValue() ) {%> - Id = entity.Id, - <%continue;}%> - <%= p.PropertyName.ToSafeName() %> = entity.<%= p.PropertyName.ToSafeName() %>, - <%}%> - }; - } - } -} diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Datas/Repository.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Datas/Repository.cst deleted file mode 100644 index a55c976e7..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Datas/Repository.cst +++ /dev/null @@ -1,54 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%@ Property Name="UnitOfWork" Type="System.String" %> -<%@ Property Name="IUnitOfWorkNamespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using <%= Entity.GetNamespace(Namespace,"Domains","Models") %>; -using <%= Entity.GetNamespace(Namespace,"Domains","Repositories") %>; -using <%= Entity.GetNamespace(Namespace,"Datas","Pos") %>; -using <%= Entity.GetNamespace(Namespace,"Datas","Stores") %>; -using Util.Datas.Ef.Core; - -namespace <%= string.Format("{0}.Datas.{1}.Repositories",Namespace,Entity.TableSchema) %> { - /// - /// <%= Description %>仓储 - /// - public class <%= SafeClassName %>Repository : PersistentRepositoryBase<<%= SafeClassName %>,<%= SafeClassName %>Po<%= Entity.GetKeyTypeNoContainsGuid() %>>, I<%= SafeClassName %>Repository { - /// - /// 初始化<%= Description %>仓储 - /// - /// <%= Description %>持久化存储 - public <%= SafeClassName %>Repository( I<%= SafeClassName %>PoStore store ) : base( store ) { - } - - /// - /// 将持久化对象转成实体 - /// - /// 持久化对象 - protected override <%= SafeClassName %> ToEntity( <%= SafeClassName %>Po po ) { - return po.ToEntity(); - } - - /// - /// 将实体转成持久化对象 - /// - /// 实体 - protected override <%= SafeClassName %>Po ToPo( <%= SafeClassName %> entity ) { - return entity.ToPo(); - } - } -} diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Datas/SqlServer/PoMapping.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Datas/SqlServer/PoMapping.cst deleted file mode 100644 index b605137b6..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Datas/SqlServer/PoMapping.cst +++ /dev/null @@ -1,267 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> - -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using <%= Entity.GetNamespace(Namespace,"Datas","Pos") %>; - -namespace <%= Entity.GetNamespace(Namespace,"Datas","Mappings.SqlServer") %> { - /// - /// <%= Description %>持久化对象映射配置 - /// - public class <%= SafeClassName %>PoMap : Util.Datas.Ef.SqlServer.AggregateRootMap<<%= SafeClassName %>Po> { - /// - /// 映射表 - /// - protected override void MapTable( EntityTypeBuilder<<%= SafeClassName %>Po> builder ) { - builder.ToTable( "<%= Entity.TableName %>", "<%= Entity.TableSchema %>" ); - } - - /// - /// 映射属性 - /// - protected override void MapProperties( EntityTypeBuilder<<%= SafeClassName %>Po> builder ) { - base.MapProperties( builder ); - <% - foreach(var p in Entity.Properties) - { - Response.Write(PropertyExpression(p)); - } - %> - } - - /// - /// 映射导航属性 - /// - protected override void MapAssociations( EntityTypeBuilder<<%= SafeClassName %>Po> builder ) { - <% - foreach(var r in Entity.Relationships.Where(e => e.IsMapped)) - { - if (r.IsManyToMany) - Response.Write(ManyToManyExpression(r)); - else - Response.Write(RelationshipExpression(r)); - } - %> - } - } -} - \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Datas/Store.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Datas/Store.cst deleted file mode 100644 index 24002f3df..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Datas/Store.cst +++ /dev/null @@ -1,35 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%@ Property Name="UnitOfWork" Type="System.String" %> -<%@ Property Name="IUnitOfWorkNamespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using <%= Entity.GetNamespace(Namespace,"Datas","Pos") %>; -using Util.Datas.Ef.Core; - -namespace <%= Entity.GetNamespace(Namespace,"Datas","Stores") %> { - /// - /// <%= Description %>持久化存储 - /// - public class <%= SafeClassName %>PoStore : PersistentStoreBase<<%= SafeClassName %>Po<%= Entity.GetKeyTypeNoContainsGuid() %>>, I<%= SafeClassName %>PoStore { - /// - /// 初始化<%= Description %>持久化存储 - /// - /// 工作单元 - public <%= SafeClassName %>PoStore( I<%= UnitOfWork %> unitOfWork ) : base( unitOfWork ) { - } - } -} diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Domains/IRepository.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Domains/IRepository.cst deleted file mode 100644 index f61b31a24..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/PersistObjects/Domains/IRepository.cst +++ /dev/null @@ -1,27 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using Util.Domains.Repositories; -using <%= Entity.GetNamespace(Namespace,"Domains","Models") %>; - -namespace <%= Entity.GetNamespace(Namespace,"Domains","Repositories") %> { - /// - /// <%= Description %>仓储 - /// - public interface I<%= SafeClassName %>Repository : ICompactRepository<<%= SafeClassName %><%= Entity.GetKeyTypeNoContainsGuid() %>> { - } -} diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Services/Dto.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Services/Dto.cst deleted file mode 100644 index 7e5de2844..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Services/Dto.cst +++ /dev/null @@ -1,42 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using System; -using System.ComponentModel.DataAnnotations; -using System.Runtime.Serialization; -using Util.Applications.Dtos; - -namespace <%= string.Format("{0}.Service.Dtos.{1}",Namespace,Entity.TableSchema) %> { - /// - /// <%= Description %>数据传输对象 - /// - [DataContract] - public class <%= SafeClassName %>Dto : DtoBase { - <% foreach(Property p in Entity.Properties) { %> - <% if( p.IsPrimaryKey.SafeValue() ) continue; %> - /// - /// <%= p.Description %> - /// - <% if( p.HasValidate() ) {%> - <%= p.Validate() %> - <%}%> - [Display( Name = "<%= p.Description %>" )] - [DataMember] - public <%= p.TypeName %> <%= p.PropertyName.ToSafeName() %> { get; set; } - <% }%> - } -} diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Services/DtoExtension.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Services/DtoExtension.cst deleted file mode 100644 index a52c3574f..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Services/DtoExtension.cst +++ /dev/null @@ -1,96 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using Util; -using Util.Maps; -using <%= Entity.GetNamespace(Namespace,"Domain","Models") %>; - -namespace <%= string.Format("{0}.Service.Dtos.{1}",Namespace,Entity.TableSchema) %> { - /// - /// <%= Description %>数据传输对象扩展 - /// - public static class <%= SafeClassName %>DtoExtension { - /// - /// 转换为<%= Description %>实体 - /// - /// <%= Description %>数据传输对象 - public static <%= SafeClassName %> ToEntity( this <%= SafeClassName %>Dto dto ) { - if ( dto == null ) - return new <%= SafeClassName %>(); - return dto.MapTo( new <%= SafeClassName %>( dto.Id<%=Entity.GetToEntityConvert() %> ) ); - } - - /// - /// 转换为<%= Description %>实体 - /// - /// <%= Description %>数据传输对象 - public static <%= SafeClassName %> ToEntity2( this <%= SafeClassName %>Dto dto ) { - if( dto == null ) - return new <%= SafeClassName %>(); - return new <%= SafeClassName %>( dto.Id<%=Entity.GetToEntityConvert() %> ) { - <% foreach(Property p in Entity.Properties) { %> - <% if( p.IsPrimaryKey.SafeValue() ) continue; %> - <%= p.PropertyName.ToSafeName() %> = dto.<%= p.PropertyName.ToSafeName() %>, - <%}%> - }; - } - - /// - /// 转换为<%= Description %>实体 - /// - /// <%= Description %>数据传输对象 - public static <%= SafeClassName %> ToEntity3( this <%= SafeClassName %>Dto dto ) { - if( dto == null ) - return new <%= SafeClassName %>(); - return <%= SafeClassName %>Factory.Create( - <% foreach(Property p in Entity.Properties) { %> - <% if( p.IsPrimaryKey.SafeValue() ) {%> - <%= p.PropertyName.ToSafeName().ToCamelCase() %> : dto.Id<%=Entity.GetToEntityConvert() %>, - <%continue;}%> - <%= p.PropertyName.ToSafeName().ToCamelCase() %> : dto.<%= p.PropertyName.ToSafeName() %><%= Entity.GetComma(p,false) %> - <%}%> - ); - } - - /// - /// 转换为<%= Description %>数据传输对象 - /// - /// <%= Description %>实体 - public static <%= SafeClassName %>Dto ToDto(this <%= SafeClassName %> entity) { - if( entity == null ) - return new <%= SafeClassName %>Dto(); - return entity.MapTo<<%= SafeClassName %>Dto>(); - } - - /// - /// 转换为<%= Description %>数据传输对象 - /// - /// <%= Description %>实体 - public static <%= SafeClassName %>Dto ToDto2( this <%= SafeClassName %> entity ) { - if( entity == null ) - return new <%= SafeClassName %>Dto(); - return new <%= SafeClassName %>Dto { - <% foreach(Property p in Entity.Properties) { %> - <% if( p.IsPrimaryKey.SafeValue() ) {%> - Id = entity.Id<%=Entity.GetToDtoConvert() %>, - <%continue;}%> - <%= p.PropertyName.ToSafeName() %> = entity.<%= p.PropertyName.ToSafeName() %>, - <%}%> - }; - } - } -} \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Services/IService.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Services/IService.cst deleted file mode 100644 index 0d91ec579..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Services/IService.cst +++ /dev/null @@ -1,28 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using Util.Applications; -using <%= string.Format("{0}.Service.Dtos.{1}",Namespace,Entity.TableSchema) %>; -using <%= string.Format("{0}.Service.Queries.{1}",Namespace,Entity.TableSchema) %>; - -namespace <%= string.Format("{0}.Service.Abstractions.{1}",Namespace,Entity.TableSchema) %> { - /// - /// <%= Description %>服务 - /// - public interface I<%= SafeClassName %>Service : ICrudService<<%= SafeClassName %>Dto, <%= SafeClassName %>Query> { - } -} \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Services/IocConfig.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Services/IocConfig.cst deleted file mode 100644 index e17f6103b..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Services/IocConfig.cst +++ /dev/null @@ -1,65 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="EntityContext" Type="SchemaMapper.EntityContext" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%@ Property Name="DbName" Type="System.String" %> -using Autofac; -using Util.Dependency; -using <%= Namespace %>.Data; - -namespace <%= Namespace %>.Service.Configs { - /// - /// 依赖注入配置 - /// - public class IocConfig : ConfigBase { - /// - /// 加载配置 - /// - protected override void Load( ContainerBuilder builder ) { - LoadInfrastructure( builder ); - LoadDomainServices( builder ); - LoadRepositories( builder ); - LoadApplicationServices( builder ); - } - - /// - /// 加载基础设施 - /// - private void LoadInfrastructure( ContainerBuilder builder ) { - builder.AddScopedUnitOfWork, <%= DbName %>UnitOfWork>(); - } - - /// - /// 加载领域服务 - /// - private void LoadDomainServices( ContainerBuilder builder ) { - } - - /// - /// 加载仓储 - /// - private void LoadRepositories( ContainerBuilder builder ) { - <% foreach(Entity entity in EntityContext.Entities) {%> - builder.AddScopedRepository,<%= entity.ClassName.ToSafeName() %>Repository>(); - <%} %> - } - - /// - /// 加载应用服务 - /// - private void LoadApplicationServices( ContainerBuilder builder ) { - <% foreach(Entity entity in EntityContext.Entities) {%> - builder.AddScopedService,<%= entity.ClassName.ToSafeName() %>Service>().PropertiesAutowired(); - <%} %> - } - } -} \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Services/Query.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Services/Query.cst deleted file mode 100644 index cc270ec4c..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Services/Query.cst +++ /dev/null @@ -1,76 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using System; -using System.ComponentModel.DataAnnotations; -using Util; -using Util.Datas.Queries; - -namespace <%= string.Format("{0}.Service.Queries.{1}",Namespace,Entity.TableSchema) %> { - /// - /// <%= Description %>查询实体 - /// - public class <%= SafeClassName %>Query : QueryParameter { - <% foreach(Property p in Entity.Properties) { %> - <% if( Entity.IsFilterByQueryObject(p) ) continue;%> - <% if( p.SystemType == typeof(DateTime) ) {%> - /// - /// 起始<%=p.Description %> - /// - [Display( Name = "起始<%=p.Description %>" )] - public DateTime? Begin<%= p.PropertyName.ToSafeName() %> { get; set; } - /// - /// 结束<%=p.Description %> - /// - [Display( Name = "结束<%=p.Description %>" )] - public DateTime? End<%= p.PropertyName.ToSafeName() %> { get; set; } - <%continue;}%> - <% if( p.SystemType == typeof(string) ) {%> - - private <%= p.SystemType.ToNullableType(true) %> <%= p.PropertyName.ToFieldName() %> = string.Empty; - /// - /// <%=p.Description %> - /// - [Display(Name="<%=p.Description %>")] - public <%= p.SystemType.ToNullableType(true) %> <%= p.PropertyName.ToSafeName() %> { - get => <%= p.PropertyName.ToFieldName() %> == null ? string.Empty : <%= p.PropertyName.ToFieldName() %>.Trim(); - set => <%= p.PropertyName.ToFieldName() %>=value; - } - <%continue;} %> - /// - /// <%=p.Description %> - /// - [Display(Name="<%=p.Description %>")] - public <%= p.SystemType.ToNullableType(true) %> <%= p.PropertyName.ToSafeName() %> { get; set; } - <% } %> - - /// - /// 添加描述 - /// - protected override void AddDescriptions() { - base.AddDescriptions(); - <% foreach(Property p in Entity.Properties) { %> - <% if( Entity.IsFilterByQueryObject(p) ) continue;%> - <% if( p.DataType == DbType.DateTime ) {%> - AddDescription( "起始<%=p.Description %>", Begin<%= p.PropertyName.ToSafeName() %> ); - AddDescription( "结束<%=p.Description %>", End<%= p.PropertyName.ToSafeName() %> ); - <%continue; } %> - AddDescription( "<%=p.Description %>", <%= p.PropertyName.ToSafeName() %><%= p.ShowDescription() %> ); - <% } %> - } - } -} \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Services/Service.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Services/Service.cst deleted file mode 100644 index ee909ff01..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Services/Service.cst +++ /dev/null @@ -1,76 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Namespace" Type="System.String" %> -<%@ Property Name="UnitOfWork" Type="System.String" %> -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="IUnitOfWorkNamespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using Util; -using Util.Maps; -using Util.Domains.Repositories; -using Util.Datas.Queries; -using Util.Applications; -using <%= string.Format("{0}.Data",Namespace) %>; -using <%= Entity.GetNamespace(Namespace,"Domain","Models") %>; -using <%= Entity.GetNamespace(Namespace,"Domain","Repositories") %>; -using <%= string.Format("{0}.Service.Dtos.{1}",Namespace,Entity.TableSchema) %>; -using <%= string.Format("{0}.Service.Queries.{1}",Namespace,Entity.TableSchema) %>; -using <%= string.Format("{0}.Service.Abstractions.{1}",Namespace,Entity.TableSchema) %>; - -namespace <%= string.Format("{0}.Service.Implements.{1}",Namespace,Entity.TableSchema) %> { - /// - /// <%= Description %>服务 - /// - public class <%= SafeClassName %>Service : CrudServiceBase<<%= SafeClassName %>, <%= SafeClassName %>Dto, <%= SafeClassName %>Query<%= Entity.GetKeyTypeNoContainsGuid() %>>, I<%= SafeClassName %>Service { - /// - /// 初始化<%= Description %>服务 - /// - /// 工作单元 - /// <%= Description %>仓储 - public <%= SafeClassName %>Service( I<%= UnitOfWork %> unitOfWork, I<%= SafeClassName %>Repository <%= SafeClassName.ToCamelCase() %>Repository ) - : base( unitOfWork, <%= SafeClassName.ToCamelCase() %>Repository ) { - <%= SafeClassName %>Repository = <%= SafeClassName.ToCamelCase() %>Repository; - } - - /// - /// <%= Description %>仓储 - /// - public I<%= SafeClassName %>Repository <%= SafeClassName %>Repository { get; set; } - - /// - /// 转换为数据传输对象 - /// - /// 实体 - protected override <%= SafeClassName %>Dto ToDto( <%= SafeClassName %> entity ) { - return entity.MapTo<<%= SafeClassName %>Dto>(); - } - - /// - /// 转换为实体 - /// - /// 数据传输对象 - protected override <%= SafeClassName %> ToEntity( <%= SafeClassName %>Dto dto ) { - return dto.MapTo( new <%= SafeClassName %>( dto.Id<%=Entity.GetToEntityConvert() %> ) ); - } - - /// - /// 创建查询对象 - /// - /// <%= Description %>查询实体 - protected override IQueryBase<<%= SafeClassName %>> CreateQuery( <%= SafeClassName %>Query param ) { - return new Query<<%= SafeClassName %><%= Entity.GetKeyTypeNoContainsGuid() %>>( param ); - } - } -} \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Tests/DtoTest.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Tests/DtoTest.cst deleted file mode 100644 index 84c96f442..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Tests/DtoTest.cst +++ /dev/null @@ -1,82 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using System; -using System.Collections.Generic; -using Xunit; -using <%= string.Format("{0}.Service.Dtos.{1}",Namespace,Entity.TableSchema) %>; -using <%= string.Format("{0}.Test.Models.{1}",Namespace,Entity.TableSchema) %>; - -namespace <%= string.Format("{0}.Test.Integration.Dtos.{1}",Namespace,Entity.TableSchema) %> { - /// - /// <%= Description %>数据传输对象测试 - /// - public class <%= SafeClassName %>DtoTest { - /// - /// 创建<%= Description %>数据传输对象 - /// - public static <%= SafeClassName %>Dto Create(string id = "") { - return <%= SafeClassName %>Test.Create(id).ToDto(); - } - - /// - /// 创建<%= Description %>数据传输对象2 - /// - /// <%= Description %>编号 - public static <%= SafeClassName %>Dto Create2( string id = "" ) { - return <%= SafeClassName %>Test.Create2( id ).ToDto(); - } - - /// - /// 创建<%= Description %>数据传输对象列表 - /// - public static List<<%= SafeClassName %>Dto> CreateList() { - return new List<<%= SafeClassName %>Dto>() { - Create(), - Create2() - }; - } - - /// - /// 转换为数据传输对象 - /// - [Fact] - public void TestToDto() { - var dto = Create(); - <% foreach(Property p in Entity.Properties) { %> - <% if( p.IsPrimaryKey.SafeValue() ) { %> - Assert.Equal( <%= SafeClassName %>Test.Id<%= Entity.GetToDtoConvert() %>,dto.Id ); - <% continue;}%> - Assert.Equal( <%= SafeClassName %>Test.<%= p.ColumnName %>,dto.<%= p.ColumnName %> ); - <% }%> - } - - /// - /// 转换为实体 - /// - [Fact] - public void TestToEntity() { - var entity = Create().ToEntity(); - <% foreach(Property p in Entity.Properties) { %> - <% if( p.IsPrimaryKey.SafeValue() ) { %> - Assert.Equal( <%= SafeClassName %>Test.Id,entity.Id ); - <% continue;}%> - Assert.Equal( <%= SafeClassName %>Test.<%= p.ColumnName %>,entity.<%= p.ColumnName %> ); - <% }%> - } - } -} \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Tests/EntityTest.Data.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Tests/EntityTest.Data.cst deleted file mode 100644 index ebb34668b..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Tests/EntityTest.Data.cst +++ /dev/null @@ -1,136 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using System; -using System.Collections.Generic; -using Util; -using <%= Entity.GetNamespace(Namespace,"Domain","Models") %>; - -namespace <%= string.Format("{0}.Test.Models.{1}",Namespace,Entity.TableSchema) %> { - /// - /// <%= Description %>测试数据 - /// - public partial class <%= SafeClassName %>Test { - - #region 测试数据1 - - <% foreach(Property p in Entity.Properties) { %> - /// - /// <%= p.Description %> - /// - <% if( p.IsPrimaryKey.SafeValue() ){ %> - public static readonly <%= p.TypeName %> Id = "<%= Guid.NewGuid() %>"<%=Entity.GetToEntityConvert() %>; - <%continue;} %> - <% if( p.DataType == DbType.Guid ){ %> - public static readonly <%= p.TypeName %> <%= p.PropertyName.ToSafeName() %> = "<%= Guid.NewGuid() %>".ToGuid(); - <%continue;} %> - <% if( p.DataType == DbType.Int32 ){ %> - public static readonly <%= p.TypeName %> <%= p.PropertyName.ToSafeName() %> = 1; - <%continue;} %> - <% if( p.DataType == DbType.Decimal ){ %> - public static readonly <%= p.TypeName %> <%= p.PropertyName.ToSafeName() %> = 1; - <%continue;} %> - <% if( p.DataType == DbType.DateTime ){ %> - public static readonly <%= p.TypeName %> <%= p.PropertyName.ToSafeName() %> = "<%= DateTime.Now %>".ToDate(); - <%continue;} %> - <% if( p.DataType == DbType.Binary ){ %> - public static readonly <%= p.TypeName %> <%= p.PropertyName.ToSafeName() %> = Util.Helpers.Convert.ToBytes( "1" ); - <%continue;} %> - <% if( p.DataType == DbType.Boolean ){ %> - public static readonly <%= p.TypeName %> <%= p.PropertyName.ToSafeName() %> = true; - <%continue;} %> - public static readonly <%= p.TypeName %> <%= p.PropertyName.ToSafeName() %> = "<%= p.PropertyName.ToSafeName() %>"; - <% }%> - - #endregion - - #region 测试数据2 - - <% foreach(Property p in Entity.Properties) { %> - /// - /// <%= p.Description %> - /// - <% if( p.IsPrimaryKey.SafeValue() ){ %> - public static readonly <%= p.TypeName %> Id2 = "<%= Guid.NewGuid() %>"<%=Entity.GetToEntityConvert() %>; - <%continue;} %> - <% if( p.DataType == DbType.Guid ){ %> - public static readonly <%= p.TypeName %> <%= p.PropertyName.ToSafeName() %>2 = "<%= Guid.NewGuid() %>".ToGuid(); - <%continue;} %> - <% if( p.DataType == DbType.Int32 ){ %> - public static readonly <%= p.TypeName %> <%= p.PropertyName.ToSafeName() %>2 = 2; - <%continue;} %> - <% if( p.DataType == DbType.Decimal ){ %> - public static readonly <%= p.TypeName %> <%= p.PropertyName.ToSafeName() %>2 = 2; - <%continue;} %> - <% if( p.DataType == DbType.DateTime ){ %> - public static readonly <%= p.TypeName %> <%= p.PropertyName.ToSafeName() %>2 = "<%= DateTime.Now.AddDays(1) %>".ToDate(); - <%continue;} %> - <% if( p.DataType == DbType.Binary ){ %> - public static readonly <%= p.TypeName %> <%= p.PropertyName.ToSafeName() %>2 = Util.Helpers.Convert.ToBytes( "2" ); - <%continue;} %> - <% if( p.DataType == DbType.Boolean ){ %> - public static readonly <%= p.TypeName %> <%= p.PropertyName.ToSafeName() %>2 = true; - <%continue;} %> - public static readonly <%= p.TypeName %> <%= p.PropertyName.ToSafeName() %>2 = "<%= p.PropertyName.ToSafeName() %>2"; - <% }%> - - #endregion - - #region Create(创建实体) - - /// - /// 创建<%= Description %>实体 - /// - public static <%= SafeClassName %> Create(string id = "") { - return new <%= SafeClassName %>( id<%=Entity.GetToEntityConvert() %> ) { - <% foreach(Property p in Entity.Properties) { %> - <% if( p.IsPrimaryKey.SafeValue() ) continue; %> - <%= p.PropertyName.ToSafeName() %> = <%= p.PropertyName.ToSafeName() %>, - <% }%> - }; - } - - /// - /// 创建<%= Description %>实体2 - /// - /// <%= Description %>编号 - public static <%= SafeClassName %> Create2( string id = "" ) { - return new <%= SafeClassName %>( id<%=Entity.GetToEntityConvert() %> ) { - <% foreach(Property p in Entity.Properties) { %> - <% if( p.IsPrimaryKey.SafeValue() ) continue; %> - <%= p.PropertyName.ToSafeName() %> = <%= p.PropertyName.ToSafeName() %>2, - <% }%> - }; - } - - #endregion - - #region CreateList(创建列表) - - /// - /// 创建列表 - /// - public static List<<%= SafeClassName %>> CreateList() { - return new List<<%= SafeClassName %>>() { - Create(), - Create2() - }; - } - - #endregion - } -} \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Tests/EntityTest.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Tests/EntityTest.cst deleted file mode 100644 index dd43e2fe7..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Tests/EntityTest.cst +++ /dev/null @@ -1,38 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using Xunit; -using <%= Entity.GetNamespace(Namespace,"Domain","Models") %>; - -namespace <%= string.Format("{0}.Test.Models.{1}",Namespace,Entity.TableSchema) %> { - /// - /// <%= Description %>测试 - /// - public partial class <%= SafeClassName %>Test { - /// - /// <%= Description %> - /// - private <%= SafeClassName %> <%= SafeClassName.ToFieldName() %>; - - /// - /// 测试初始化 - /// - public <%= SafeClassName %>Test() { - <%= SafeClassName.ToFieldName() %> = Create(); - } - } -} \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Tests/ServiceTest.cst b/template/CodeSmith/CodeGenerator/01-Templates/Templates/Tests/ServiceTest.cst deleted file mode 100644 index 3fab8972a..000000000 --- a/template/CodeSmith/CodeGenerator/01-Templates/Templates/Tests/ServiceTest.cst +++ /dev/null @@ -1,80 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="..\..\Common\Model.cs" %> -<%@ Assembly Src="..\..\Common\Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="Namespace" Type="System.String" %> -<%-- Safe类名 --%> -<% string SafeClassName = Entity.ClassName.ToSafeName(); %> -<%-- 描述 --%> -<% string Description = Entity.Description; %> -using System; -using System.Collections.Generic; -using Xunit; -using Util; -using Util.Helpers; -using Util.Dependency; -using <%= Entity.GetNamespace(Namespace,"Domain","Models") %>; -using <%= Entity.GetNamespace(Namespace,"Domain","Repositories") %>; -using <%= string.Format("{0}.Service.Abstractions.{1}",Namespace,Entity.TableSchema) %>; -using <%= string.Format("{0}.Service.Dtos.{1}",Namespace,Entity.TableSchema) %>; -using <%= string.Format("{0}.Test.Integration.Dtos.{1}",Namespace,Entity.TableSchema) %>; - -namespace <%= string.Format("{0}.Test.Integration.Services.{1}",Namespace,Entity.TableSchema) %> { - /// - /// <%= Description %>服务测试 - /// - [Collection( "GlobalConfig" )] - public class <%= SafeClassName %>ServiceTest : IDisposable { - /// - /// 容器作用域 - /// - private readonly IScope _scope; - /// - /// <%= Description %>服务 - /// - private readonly I<%= SafeClassName %>Service <%= SafeClassName.ToFieldName() %>Service; - /// - /// <%= Description %>仓储 - /// - private readonly I<%= SafeClassName %>Repository <%= SafeClassName.ToFieldName() %>Repository; - /// - /// <%= Description %>Dto - /// - private readonly <%= SafeClassName %>Dto <%= SafeClassName.ToFieldName() %>Dto; - - /// - /// 测试初始化 - /// - public <%= SafeClassName %>ServiceTest() { - _scope = Ioc.BeginScope(); - <%= SafeClassName.ToFieldName() %>Repository = _scope.CreateRepository>(); - <%= SafeClassName.ToFieldName() %>Service = _scope.CreateService>(); - <%= SafeClassName.ToFieldName() %>Dto = <%= SafeClassName %>DtoTest.Create(); - } - - /// - /// 测试清理 - /// - public void Dispose() { - _scope.Dispose(); - } - - /// - /// 测试 - /// - [Fact] - public void Test() { - var count = <%= SafeClassName.ToFieldName() %>Repository.Count(); - <%= SafeClassName.ToFieldName() %>Service.Save( <%= SafeClassName.ToFieldName() %>Dto ); - Assert.Equal( count + 1, <%= SafeClassName.ToFieldName() %>Repository.Count() ); - } - } -} \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/CodeGenerator.csproj b/template/CodeSmith/CodeGenerator/CodeGenerator.csproj deleted file mode 100644 index 46a0792b1..000000000 --- a/template/CodeSmith/CodeGenerator/CodeGenerator.csproj +++ /dev/null @@ -1,115 +0,0 @@ - - - - - Debug - AnyCPU - {B58D16AD-9987-4BB8-8BB2-2A0CE8BC9829} - Library - Properties - CodeGenerator - CodeGenerator - v4.5 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - none - true - bin\Release\ - TRACE - prompt - 4 - - - - False - Libraries\CodeSmith.Core.dll - - - False - Libraries\CodeSmith.Engine.dll - - - False - Libraries\EntityFramework.dll - - - False - Libraries\ICSharpCode.NRefactory.dll - - - False - Libraries\ICSharpCode.NRefactory.CSharp.dll - - - False - Libraries\Mono.Cecil.dll - - - False - Libraries\SchemaExplorer.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/CodeGenerator.csproj.user b/template/CodeSmith/CodeGenerator/CodeGenerator.csproj.user deleted file mode 100644 index 07dbd5b56..000000000 --- a/template/CodeSmith/CodeGenerator/CodeGenerator.csproj.user +++ /dev/null @@ -1,6 +0,0 @@ - - - - ProjectFiles - - \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/Libraries/CodeSmith.Core.dll b/template/CodeSmith/CodeGenerator/Libraries/CodeSmith.Core.dll deleted file mode 100644 index 1414f987d..000000000 Binary files a/template/CodeSmith/CodeGenerator/Libraries/CodeSmith.Core.dll and /dev/null differ diff --git a/template/CodeSmith/CodeGenerator/Libraries/CodeSmith.Core.xml b/template/CodeSmith/CodeGenerator/Libraries/CodeSmith.Core.xml deleted file mode 100644 index e7c953cd0..000000000 --- a/template/CodeSmith/CodeGenerator/Libraries/CodeSmith.Core.xml +++ /dev/null @@ -1,15828 +0,0 @@ - - - - CodeSmith.Core - - - - - A class defining a business day. - - - - - Initializes a new instance of the class. - - The day of week this business day represents. - - - - Initializes a new instance of the class. - - The day of week this business day represents. - The start time of the business day. - The end time of the business day. - - - - Determines whether the specified date falls in the business day. - - The date to check. - - true if the specified date falls in the business day; otherwise, false. - - - - - Gets the day of week this business day represents.. - - The day of week. - - - - Gets the start time of the business day. - - The start time of the business day. - - - - Gets the end time of the business day. - - The end time of the business day. - - - - A class representing a business week. - - - - - Initializes a new instance of the class. - - - - - Determines whether the specified date falls on a business day. - - The date to check. - - true if the specified date falls on a business day; otherwise, false. - - - - - Gets the business time between the start date and end date. - - The start date. - The end date. - - A TimeSpan of the amount of business time between the start and end date. - - - Business time is calculated by adding only the time that falls inside the business day range. - If all the time between the start and end date fall outside the business day, the time will be zero. - - - - - Gets the business end date using the specified time. - - The start date. - The business time. - - - - - Validates the business week. - - if set to true throw excption if invalid. - true if valid; otherwise false. - - - - Gets the business days for the week. - - The business days for the week. - - - - Gets the default BusinessWeek. - - - - - Nested class to lazy-load singleton. - - - - - Initializes the Nested class. - - - Explicit static constructor to tell C# compiler not to mark type as beforefieldinit. - - - - - Current singleton instance. - - - - - Represents a thread-safe collection of keys and values. - - The type of the keys in the dictionary. - The type of the values in the dictionary. - - All public and protected members of are thread-safe and may be used - concurrently from multiple threads. - - - - - Raises the PropertyChanged event with the provided arguments. - - The instance containing the event data. - - - - Raises the PropertyChanged event with the provided arguments. - - Name of the property. - - - - Raises the CollectionChanged event with the provided arguments. - - - - - Initializes a new instance of the - class that is empty, has the default concurrency level, has the default initial capacity, and - uses the default comparer for the key type. - - - - - Initializes a new instance of the - class that is empty, has the specified concurrency level and capacity, and uses the default - comparer for the key type. - - The estimated number of threads that will update the - concurrently. - The initial number of elements that the - can contain. - is - less than 1. - is less than - 0. - - - - Initializes a new instance of the - class that contains elements copied from the specified , has the default concurrency - level, has the default initial capacity, and uses the default comparer for the key type. - - The whose elements are copied to - the new - . - is a null reference - (Nothing in Visual Basic). - contains one or more - duplicate keys. - - - - Initializes a new instance of the - class that is empty, has the specified concurrency level and capacity, and uses the specified - . - - The - implementation to use when comparing keys. - is a null reference - (Nothing in Visual Basic). - - - - Initializes a new instance of the - class that contains elements copied from the specified , has the default concurrency level, has the default - initial capacity, and uses the specified - . - - The whose elements are copied to - the new - . - The - implementation to use when comparing keys. - is a null reference - (Nothing in Visual Basic). -or- - is a null reference (Nothing in Visual Basic). - - - - - Initializes a new instance of the - class that contains elements copied from the specified , - has the specified concurrency level, has the specified initial capacity, and uses the specified - . - - The estimated number of threads that will update the - concurrently. - The whose elements are copied to the new - . - The implementation to use - when comparing keys. - - is a null reference (Nothing in Visual Basic). - -or- - is a null reference (Nothing in Visual Basic). - - - is less than 1. - - contains one or more duplicate keys. - - - - Initializes a new instance of the - class that is empty, has the specified concurrency level, has the specified initial capacity, and - uses the specified . - - The estimated number of threads that will update the - concurrently. - The initial number of elements that the - can contain. - The - implementation to use when comparing keys. - - is less than 1. -or- - is less than 0. - - is a null reference - (Nothing in Visual Basic). - - - - Attempts to add the specified key and value to the . - - The key of the element to add. - The value of the element to add. The value can be a null reference (Nothing - in Visual Basic) for reference types. - true if the key/value pair was added to the - successfully; otherwise, false. - is null reference - (Nothing in Visual Basic). - The - contains too many elements. - - - - Determines whether the contains the specified - key. - - The key to locate in the . - true if the contains an element with - the specified key; otherwise, false. - is a null reference - (Nothing in Visual Basic). - - - - Attempts to remove and return the the value with the specified key from the - . - - The key of the element to remove and return. - When this method returns, contains the object removed from the - or the default value of - if the operation failed. - true if an object was removed successfully; otherwise, false. - is a null reference - (Nothing in Visual Basic). - - - - Removes the specified key from the dictionary if it exists and returns its associated value. - If matchValue flag is set, the key will be removed only if is associated with a particular - value. - - The key to search for and remove if it exists. - The variable into which the removed value, if found, is stored. - Whether removal of the key is conditional on its value. - The conditional value to compare against if is true - - - - - Attempts to get the value associated with the specified key from the . - - The key of the value to get. - When this method returns, contains the object from - the - with the spedified key or the default value of - , if the operation failed. - true if the key was found in the ; - otherwise, false. - is a null reference - (Nothing in Visual Basic). - - - - Compares the existing value for the specified key with a specified value, and if they’re equal, - updates the key with a third value. - - The key whose value is compared with and - possibly replaced. - The value that replaces the value of the element with if the comparison results in equality. - The value that is compared to the value of the element with - . - true if the value with was equal to and replaced with ; otherwise, - false. - is a null - reference. - - - - Removes all keys and values from the . - - - - - Copies the elements of the to an array of - type , starting at the - specified array index. - - The one-dimensional array of type - that is the destination of the elements copied from the . The array must have zero-based indexing. - The zero-based index in at which copying - begins. - is a null reference - (Nothing in Visual Basic). - is less than - 0. - is equal to or greater than - the length of the . -or- The number of elements in the source - is greater than the available space from to the end of the destination - . - - - - Copies the key and value pairs stored in the to a - new array. - - A new array containing a snapshot of key and value pairs copied from the . - - - - Copy dictionary contents to an array - shared implementation between ToArray and CopyTo. - - Important: the caller must hold all locks in m_locks before calling CopyToPairs. - - - - - Copy dictionary contents to an array - shared implementation between ToArray and CopyTo. - - Important: the caller must hold all locks in m_locks before calling CopyToEntries. - - - - - Copy dictionary contents to an array - shared implementation between ToArray and CopyTo. - - Important: the caller must hold all locks in m_locks before calling CopyToObjects. - - - - Returns an enumerator that iterates through the . - An enumerator for the . - - The enumerator returned from the dictionary is safe to use concurrently with - reads and writes to the dictionary, however it does not represent a moment-in-time snapshot - of the dictionary. The contents exposed through the enumerator may contain modifications - made to the dictionary after was called. - - - - - Shared internal implementation for inserts and updates. - If key exists, we always return false; and if updateIfExists == true we force update with value; - If key doesn't exist, we always add value and return true; - - - - - Adds a key/value pair to the - if the key does not already exist. - - The key of the element to add. - The function used to generate a value for the key - is a null reference - (Nothing in Visual Basic). - is a null reference - (Nothing in Visual Basic). - The dictionary contains too many - elements. - The value for the key. This will be either the existing value for the key if the - key is already in the dictionary, or the new value for the key as returned by valueFactory - if the key was not in the dictionary. - - - - Adds a key/value pair to the - if the key does not already exist. - - The key of the element to add. - the value to be added, if the key does not already exist - is a null reference - (Nothing in Visual Basic). - The dictionary contains too many - elements. - The value for the key. This will be either the existing value for the key if the - key is already in the dictionary, or the new value if the key was not in the dictionary. - - - - Adds a key/value pair to the if the key does not already - exist, or updates a key/value pair in the if the key - already exists. - - The key to be added or whose value should be updated - The function used to generate a value for an absent key - The function used to generate a new value for an existing key - based on the key's existing value - is a null reference - (Nothing in Visual Basic). - is a null reference - (Nothing in Visual Basic). - is a null reference - (Nothing in Visual Basic). - The dictionary contains too many - elements. - The new value for the key. This will be either be the result of addValueFactory (if the key was - absent) or the result of updateValueFactory (if the key was present). - - - - Adds a key/value pair to the if the key does not already - exist, or updates a key/value pair in the if the key - already exists. - - The key to be added or whose value should be updated - The value to be added for an absent key - The function used to generate a new value for an existing key based on - the key's existing value - is a null reference - (Nothing in Visual Basic). - is a null reference - (Nothing in Visual Basic). - The dictionary contains too many - elements. - The new value for the key. This will be either be the result of addValueFactory (if the key was - absent) or the result of updateValueFactory (if the key was present). - - - - Adds the specified key and value to the . - - The object to use as the key of the element to add. - The object to use as the value of the element to add. - is a null reference - (Nothing in Visual Basic). - The dictionary contains too many - elements. - - An element with the same key already exists in the . - - - - Removes the element with the specified key from the . - - The key of the element to remove. - true if the element is successfully remove; otherwise false. This method also returns - false if - was not found in the original . - - is a null reference - (Nothing in Visual Basic). - - - - Adds the specified value to the - with the specified key. - - The - structure representing the key and value to add to the . - The of is null. - The - contains too many elements. - An element with the same key already exists in the - - - - - Determines whether the - contains a specific key and value. - - The - structure to locate in the . - true if the is found in the ; otherwise, false. - - - - Removes a key and value from the dictionary. - - The - structure representing the key and value to remove from the . - true if the key and value represented by is successfully - found and removed; otherwise, false. - The Key property of is a null reference (Nothing in Visual Basic). - - - Returns an enumerator that iterates through the . - An enumerator for the . - - The enumerator returned from the dictionary is safe to use concurrently with - reads and writes to the dictionary, however it does not represent a moment-in-time snapshot - of the dictionary. The contents exposed through the enumerator may contain modifications - made to the dictionary after was called. - - - - - Adds the specified key and value to the dictionary. - - The object to use as the key. - The object to use as the value. - is a null reference - (Nothing in Visual Basic). - The dictionary contains too many - elements. - - is of a type that is not assignable to the key type of the . -or- - is of a type that is not assignable to , - the type of values in the . - -or- A value with the same key already exists in the . - - - - - Gets whether the contains an - element with the specified key. - - The key to locate in the . - true if the contains - an element with the specified key; otherwise, false. - is a null reference - (Nothing in Visual Basic). - - - Provides an for the - . - An for the . - - - - Removes the element with the specified key from the . - - The key of the element to remove. - is a null reference - (Nothing in Visual Basic). - - - - Copies the elements of the to an array, starting - at the specified array index. - - The one-dimensional array that is the destination of the elements copied from - the . The array must have zero-based - indexing. - The zero-based index in at which copying - begins. - is a null reference - (Nothing in Visual Basic). - is less than - 0. - is equal to or greater than - the length of the . -or- The number of elements in the source - is greater than the available space from to the end of the destination - . - - - - Replaces the internal table with a larger one. To prevent multiple threads from resizing the - table as a result of ----s, the table of buckets that was deemed too small is passed in as - an argument to GrowTable(). GrowTable() obtains a lock, and then checks whether the bucket - table has been replaced in the meantime or not. - - Reference to the bucket table that was deemed too small. - - - - Computes the bucket and lock number for a particular key. - - - - - Acquires all locks for this hash table, and increments locksAcquired by the number - of locks that were successfully acquired. The locks are acquired in an increasing - order. - - - - - Acquires a contiguous range of locks for this hash table, and increments locksAcquired - by the number of locks that were successfully acquired. The locks are acquired in an - increasing order. - - - - - Releases a contiguous range of locks. - - - - - Gets a collection containing the keys in the dictionary. - - - - - Gets a collection containing the values in the dictionary. - - - - - A helper method for asserts. - - - - - A helper function to obtain the string for a particular resource key. - - - - - - - Get the data array to be serialized - - - - - Construct the dictionary from a previously seiralized one - - - - - Occurs when a property value changed. - - - - - Occurs when the collection changed. - - - - - Gets or sets the value associated with the specified key. - - The key of the value to get or set. - The value associated with the specified key. If the specified key is not found, a get - operation throws a - , and a set operation creates a new - element with the specified key. - is a null reference - (Nothing in Visual Basic). - The property is retrieved and - - does not exist in the collection. - - - - Gets the number of key/value pairs contained in the . - - The dictionary contains too many - elements. - The number of key/value paris contained in the . - Count has snapshot semantics and represents the number of items in the - at the moment when Count was accessed. - - - - Gets a value that indicates whether the is empty. - - true if the is empty; otherwise, - false. - - - - Gets a collection containing the keys in the . - - An containing the keys in the - . - - - - Gets a collection containing the values in the . - - An containing the values in - the - . - - - - Gets a value indicating whether the dictionary is read-only. - - true if the is - read-only; otherwise, false. For , this property always returns - false. - - - - Gets a value indicating whether the has a fixed size. - - true if the has a - fixed size; otherwise, false. For , this property always - returns false. - - - - Gets a value indicating whether the is read-only. - - true if the is - read-only; otherwise, false. For , this property always - returns false. - - - - Gets an containing the keys of the . - - An containing the keys of the . - - - - Gets an containing the values in the . - - An containing the values in the . - - - - Gets or sets the value associated with the specified key. - - The key of the value to get or set. - The value associated with the specified key, or a null reference (Nothing in Visual Basic) - if is not in the dictionary or is of a type that is - not assignable to the key type of the . - is a null reference - (Nothing in Visual Basic). - - A value is being assigned, and is of a type that is not assignable to the - key type of the . -or- A value is being - assigned, and is of a type that is not assignable to the value type - of the - - - - - Gets a value indicating whether access to the is - synchronized with the SyncRoot. - - true if access to the is synchronized - (thread safe); otherwise, false. For , this property always - returns false. - - - - Gets an object that can be used to synchronize access to the . This property is not supported. - - The SyncRoot property is not supported. - - - - The number of concurrent writes for which to optimize by default. - - - - - A node in a singly-linked list representing a particular hash table bucket. - - - - - A private class to represent enumeration over the dictionary that implements the - IDictionaryEnumerator interface. - - - - - IndexedEnumerable makes enumerating over collections much easier by implementing properties like: IsEven, IsOdd, IsLast. - - - - - Returns an IndexedEnumerable from any collection implementing IEnumerable<T> - - Type of enumerable - Source enumerable - A new IndexedEnumerable<T>. - - - - Returns an IndexedEnumerable from any collection implementing IEnumerable<T> - - Type of enumerable - Source enumerable - A new IndexedEnumerable<T>. - - - - IndexedEnumerable makes enumerating over collections much easier by implementing properties like: IsEven, IsOdd, IsLast. - - Type to iterate over - - - - Default constructor. - - - - - Constructor that takes an IEnumerable<T> - - The collection to enumerate. - - - - Returns an enumeration of Entry objects. - - - - - Non-generic form of GetEnumerator. - - - - - Represents each entry returned within a collection, - containing the _value and whether it is the first and/or - the last entry in the collection's. enumeration - - - - - The Entry Value. - - - - - Returns true if it is the first item in the collection. - - - - - Returns true if it is the last item in the collection. - - - - - The index of the current item in the collection. - - - - - Returns true if the current item has an even index - - - - - Returns true if the current item has an odd index - - - - - Provides a base for objects with names to derive from. - - - - - The name of the object. - - - - - Returns the name of the table. - - - - - - The name of the object. - - - - - Determines whether an element is in the collection with the specified name. - - The name of the item to locate in the collection. - - true if item is found in the collection; otherwise, false. - - - - - Determines the index of a specific item in the list. - - The name of the item to locate in the list. - The index of item if found in the list; otherwise, -1. - - - - Gets the item with the specified name. - - - The item with the specified name. - - - - - Implements a strongly typed collection of elements. - - - SchemaObjectBaseCollection provides an - that is strongly typed for elements. - - - - - Represents a strongly typed list of objects that provides notifications when items get added, removed, or when the whole list is refreshed. - - The type of elements in the list. - - - - Initializes a new instance of the class that is empty and has the default initial capacity. - - - - - 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. - - - is less than 0. - - - - - 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. - - - is null. - - - - - Searches for the specified object and returns the zero-based index of the first occurrence within the entire . - - - The zero-based index of the first occurrence of within the entire , if found; otherwise, –1. - - - The object to locate in the . The value can be null for reference types. - - - - - Inserts an element into the at the specified index. - - - The zero-based index at which should be inserted. - - - The object to insert. The value can be null for reference types. - - - is less than 0. - - -or- - is greater than . - - - - - Removes the element at the specified index of the . - - - The zero-based index of the element to remove. - - - is less than 0. - - -or- - is equal to or greater than . - - - - - Adds an object to the end of the . - - - The object to be added to the end of the . The value can be null for reference types. - - - - - Removes all elements from the . - - - - - Determines whether an element is in the . - - true if is found in the ; otherwise, false. - - - The object to locate in the . The value can be null for reference types. - - - - - Copies the entire to a compatible one-dimensional array, starting at the specified index of the target array. - - - The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. - - - is null. - - - is less than 0. - - - is equal to or greater than the length of . - - -or- - - The number of elements in the source is greater than the available space from to the end of the destination . - - - - - Copies the entire to a compatible one-dimensional array, starting at the specified index of the target array. - - - The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. - - - The zero-based index in at which copying begins. - - - is null. - - - is less than 0. - - - is equal to or greater than the length of . - - -or- - - The number of elements in the source is greater than the available space from to the end of the destination . - - - - - Removes the first occurrence of a specific object from the . - - true if is successfully removed; otherwise, false. This method also returns false if was not found in the . - - - The object to remove from the . The value can be null for reference types. - - - - - Returns an enumerator that iterates through the . - - - A for the . - - - - - Returns an enumerator that iterates through a collection. - - - An object that can be used to iterate through the collection. - - - - - Adds an item to the . - - The object to add to the . - - The position into which the new element was inserted, or -1 to indicate that the item was not inserted into the collection, - - - The is read-only.-or- The has a fixed size. - - - - - Determines whether the contains a specific value. - - The object to locate in the . - - true if the is found in the ; otherwise, false. - - - - - Determines the index of a specific item in the . - - The object to locate in the . - - The index of if found in the list; otherwise, -1. - - - - - Inserts an item to the at the specified index. - - The zero-based index at which should be inserted. - The object to insert into the . - - is not a valid index in the . - - - The is read-only.-or- The has a fixed size. - - - is null reference in the . - - - - - Removes the first occurrence of a specific object from the . - - The object to remove from the . - - The is read-only.-or- The has a fixed size. - - - - - Copies the elements of the to an , starting at a particular index. - - The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. - The zero-based index in at which copying begins. - - is null. - - - is less than zero. - - - is multidimensional.-or- The number of elements in the source is greater than the available space from to the end of the destination . - - - The type of the source cannot be cast automatically to the type of the destination . - - - - - Adds the elements of the specified collection to the end of the . - - - The collection whose elements should be added to the end of the . The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type. - - - is null. - - - - - Returns a read-only wrapper for the current collection. - - - A that acts as a read-only wrapper around the current . - - - - - Searches a range of elements in the sorted for an element using the specified comparer and returns the zero-based index of the element. - - - The zero-based index of in the sorted , if is found; otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than or, if there is no larger element, the bitwise complement of . - - - The zero-based starting index of the range to search. - - - The length of the range to search. - - - The object to locate. The value can be null for reference types. - - - The implementation to use when comparing elements, or null to use the default comparer . - - - is less than 0. - - -or- - is less than 0. - - - and do not denote a valid range in the . - - - is null, and the default comparer cannot find an implementation of the generic interface or the interface for type T. - - - - - Searches the entire sorted for an element using the default comparer and returns the zero-based index of the element. - - - The zero-based index of in the sorted , if is found; otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than or, if there is no larger element, the bitwise complement of . - - - The object to locate. The value can be null for reference types. - - - The default comparer cannot find an implementation of the generic interface or the interface for type T. - - - - - Searches the entire sorted for an element using the specified comparer and returns the zero-based index of the element. - - - The zero-based index of in the sorted , if is found; otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than or, if there is no larger element, the bitwise complement of . - - - The object to locate. The value can be null for reference types. - - - The implementation to use when comparing elements. - - -or- - null to use the default comparer . - - - is null, and the default comparer cannot find an implementation of the generic interface or the interface for type T. - - - - - Converts the elements in the current to another type, and returns a list containing the converted elements. - - - A of the target type containing the converted elements from the current . - - - A delegate that converts each element from one type to another type. - - - The type of the elements of the target array. - - - is null. - - - - - Determines whether the contains elements that match the conditions defined by the specified predicate. - - true if the contains one or more elements that match the conditions defined by the specified predicate; otherwise, false. - - - The delegate that defines the conditions of the elements to search for. - - - is null. - - - - - Searches for an element that matches the conditions defined by the specified predicate, and returns the first occurrence within the entire . - - - The first element that matches the conditions defined by the specified predicate, if found; otherwise, the default value for type T. - - - The delegate that defines the conditions of the element to search for. - - - is null. - - - - - Retrieves all the elements that match the conditions defined by the specified predicate. - - - A containing all the elements that match the conditions defined by the specified predicate, if found; otherwise, an empty . - - - The delegate that defines the conditions of the elements to search for. - - - is null. - - - - - Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the entire . - - - The zero-based index of the first occurrence of an element that matches the conditions defined by , if found; otherwise, –1. - - - The delegate that defines the conditions of the element to search for. - - - is null. - - - - - Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the range of elements in the that extends from the specified index to the last element. - - - The zero-based index of the first occurrence of an element that matches the conditions defined by , if found; otherwise, –1. - - - The zero-based starting index of the search. - - - The delegate that defines the conditions of the element to search for. - - - is null. - - - is outside the range of valid indexes for the . - - - - - Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the range of elements in the that starts at the specified index and contains the specified number of elements. - - - The zero-based index of the first occurrence of an element that matches the conditions defined by , if found; otherwise, –1. - - - The zero-based starting index of the search. - - - The number of elements in the section to search. - - - The delegate that defines the conditions of the element to search for. - - - is null. - - - is outside the range of valid indexes for the . - - -or- - is less than 0. - - -or- - and do not specify a valid section in the . - - - - - Searches for an element that matches the conditions defined by the specified predicate, and returns the last occurrence within the entire . - - - The last element that matches the conditions defined by the specified predicate, if found; otherwise, the default value for type T. - - - The delegate that defines the conditions of the element to search for. - - - is null. - - - - - Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the last occurrence within the entire . - - - The zero-based index of the last occurrence of an element that matches the conditions defined by , if found; otherwise, –1. - - - The delegate that defines the conditions of the element to search for. - - - is null. - - - - - Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the last occurrence within the range of elements in the that extends from the first element to the specified index. - - - The zero-based index of the last occurrence of an element that matches the conditions defined by , if found; otherwise, –1. - - - The zero-based starting index of the backward search. - - - The delegate that defines the conditions of the element to search for. - - - is null. - - - is outside the range of valid indexes for the . - - - - - Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the last occurrence within the range of elements in the that contains the specified number of elements and ends at the specified index. - - - The zero-based index of the last occurrence of an element that matches the conditions defined by , if found; otherwise, –1. - - - The zero-based starting index of the backward search. - - - The number of elements in the section to search. - - - The delegate that defines the conditions of the element to search for. - - - is null. - - - is outside the range of valid indexes for the . - - -or- - is less than 0. - - -or- - and do not specify a valid section in the . - - - - - Performs the specified action on each element of the . - - - The delegate to perform on each element of the . - - - is null. - - - - - Creates a shallow copy of a range of elements in the source . - - - A shallow copy of a range of elements in the source . - - - The zero-based index at which the range starts. - - - The number of elements in the range. - - - is less than 0. - - -or- - is less than 0. - - - and do not denote a valid range of elements in the . - - - - - Searches for the specified object and returns the zero-based index of the first occurrence within the range of elements in the that extends from the specified index to the last element. - - - The zero-based index of the first occurrence of within the range of elements in the that extends from to the last element, if found; otherwise, –1. - - - The object to locate in the . The value can be null for reference types. - - - The zero-based starting index of the search. - - - is outside the range of valid indexes for the . - - - - - Searches for the specified object and returns the zero-based index of the first occurrence within the range of elements in the that starts at the specified index and contains the specified number of elements. - - - The zero-based index of the first occurrence of within the range of elements in the that starts at and contains number of elements, if found; otherwise, –1. - - - The object to locate in the . The value can be null for reference types. - - - The zero-based starting index of the search. - - - The number of elements in the section to search. - - - is outside the range of valid indexes for the . - - -or- - is less than 0. - - -or- - and do not specify a valid section in the . - - - - - Inserts the elements of a collection into the at the specified index. - - - The zero-based index at which the new elements should be inserted. - - - The collection whose elements should be inserted into the . The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type. - - - is null. - - - is less than 0. - - -or- - is greater than . - - - - - Searches for the specified object and returns the zero-based index of the last occurrence within the entire . - - - The zero-based index of the last occurrence of within the entire the , if found; otherwise, –1. - - - The object to locate in the . The value can be null for reference types. - - - - - Searches for the specified object and returns the zero-based index of the last occurrence within the range of elements in the that extends from the first element to the specified index. - - - The zero-based index of the last occurrence of within the range of elements in the that extends from the first element to , if found; otherwise, –1. - - - The object to locate in the . The value can be null for reference types. - - - The zero-based starting index of the backward search. - - - is outside the range of valid indexes for the . - - - - - Searches for the specified object and returns the zero-based index of the last occurrence within the range of elements in the that contains the specified number of elements and ends at the specified index. - - - The zero-based index of the last occurrence of within the range of elements in the that contains number of elements and ends at , if found; otherwise, –1. - - - The object to locate in the . The value can be null for reference types. - - - The zero-based starting index of the backward search. - - - The number of elements in the section to search. - - - is outside the range of valid indexes for the . - - -or- - is less than 0. - - -or- - and do not specify a valid section in the . - - - - - Removes the all the elements that match the conditions defined by the specified predicate. - - - The number of elements removed from the . - - - The delegate that defines the conditions of the elements to remove. - - - is null. - - - - - Removes a range of elements from the . - - - The zero-based starting index of the range of elements to remove. - - - The number of elements to remove. - - - is less than 0. - - -or- - is less than 0. - - - and do not denote a valid range of elements in the . - - - - - Determines whether every element in the matches the conditions defined by the specified predicate. - - true if every element in the matches the conditions defined by the specified predicate; otherwise, false. If the list has no elements, the return value is true. - - - The delegate that defines the conditions to check against the elements. - - - is null. - - - - - Copies the elements of the to a new array. - - - An array containing copies of the elements of the . - - - - - Sets the capacity to the actual number of elements in the , if that number is less than a threshold value. - - - - - Reverses the order of the elements in the entire . - - - - - Reverses the order of the elements in the specified range. - - - The zero-based starting index of the range to reverse. - - - The number of elements in the range to reverse. - - - is less than 0. - - -or- - is less than 0. - - - and do not denote a valid range of elements in the . - - - - - Sorts the elements in the entire using the default comparer. - - - The default comparer cannot find an implementation of the generic interface or the interface for type T. - - - - - Sorts the elements in the entire using the specified comparer. - - - The implementation to use when comparing elements. - - - is null. - - - The implementation of caused an error during the sort. For example, might not return 0 when comparing an item with itself. - - - - - Sorts the elements in the entire using the specified comparer. - - - The implementation to use when comparing elements, or null to use the default comparer . - - - is null, and the default comparer cannot find implementation of the generic interface or the interface for type T. - - - The implementation of caused an error during the sort. For example, might not return 0 when comparing an item with itself. - - - - - Sorts the elements in a range of elements in using the specified comparer. - - - The zero-based starting index of the range to sort. - - - The length of the range to sort. - - - The implementation to use when comparing elements, or null to use the default comparer . - - - is less than 0. - - -or- - is less than 0. - - - and do not specify a valid range in the . - - -or- - - The implementation of caused an error during the sort. For example, might not return 0 when comparing an item with itself. - - - is null, and the default comparer cannot find implementation of the generic interface or the interface for type T. - - - - - Sorts the elements in the entire using the specified . - - - The to use when comparing elements. - - - is null. - - - The implementation of caused an error during the sort. For example, might not return 0 when comparing an item with itself. - - - - - Raises the event with the provided arguments. - - The start index. - The new items. - - - - Raises the event with the provided arguments. - - The start index. - The old items. - - - - Raises the event with the provided arguments. - - The index. - The old item. - The new item. - - - - Raises the event for the Count property. - - - - - Raises the event for the Item[] property. - - - - - Raises the event with the provided arguments. - - Arguments of the event being raised. - - - - Gets a wrapper around the . - - - A wrapper around the . - - - - - Gets or sets the element at the specified index. - - - The element at the specified index. - - - The zero-based index of the element to get or set. - - - is less than 0. - - -or- - is equal to or greater than . - - - - - Gets the number of elements actually contained in the . - - - The number of elements actually contained in the . - - - - - Gets a value indicating whether the is read-only. - - true if the is read-only; otherwise, false. - - - - - Gets or sets the element at the specified index. - - - The element at the specified index. - - - is not a valid index in the . - - - The property is set and the is read-only. - - - - - Gets a value indicating whether the is read-only. - - - true if the is read-only; otherwise, false. - - - - - Gets a value indicating whether the has a fixed size. - - - true if the has a fixed size; otherwise, false. - - - - - Gets an object that can be used to synchronize access to the . - - - An object that can be used to synchronize access to the . - - - - - Gets a value indicating whether access to the is synchronized (thread safe). - - - true if access to the is synchronized (thread safe); otherwise, false. - - - - - Occurs when an item is added, removed, changed, moved, or the entire list is refreshed. - - - - - Occurs when a property value changes. - - - - - Gets the item with the specified index. - - - The item with the specified index. - - - - - How many items are in the collection. - - - - - Determines whether an element is in the collection with the specified name. - - The name of the item to locate in the collection. - - true if item is found in the collection; otherwise, false. - - - - - Determines the index of a specific item in the list. - - The name of the item to locate in the list. - The index of item if found in the list; otherwise, -1. - - - - Gets the item with the specified name. - - - The item with the specified name. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - Determines whether an element is in the collection with the specified name. - - The name of the item to locate in the collection. - - true if item is found in the collection; otherwise, false. - - - - - Determines the index of a specific item in the list. - - The name of the item to locate in the list. - The index of item if found in the list; otherwise, -1. - - - - Gets the item with the specified name. - - - The item with the specified name. - - - - - Represents an observable set of values. - - The type of elements in the hash set. - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The collection whose elements are copied to the new set. - - - - Initializes a new instance of the class. - - The IEqualityComparer<T> implementation to use when comparing values in the set, or null to use the default EqualityComparer<T> implementation for the set type. - - - - Initializes a new instance of the class. - - The collection whose elements are copied to the new set. - The IEqualityComparer<T> implementation to use when comparing values in the set, or null to use the default EqualityComparer<T> implementation for the set type. - - - - Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - - - - - Adds the specified element to a set. - - The element to add to the set. - true if the element is added to the object; false if the element is already present. - - - - Adds an item to the . - - The object to add to the . - - The is read-only. - - - - - Removes all elements from a object. - - - - - Determines whether a object contains the specified element. - - The element to locate in the object. - true if the object contains the specified element; otherwise, false. - - - - Copies the elements of a collection to an array. - - The one-dimensional array that is the destination of the elements copied from the object. The array must have zero-based indexing. - - - - Copies the elements of a collection to an array. - - The one-dimensional array that is the destination of the elements copied from the object. The array must have zero-based indexing. - The zero-based index in array at which copying begins. - - - - Copies the elements of a collection to an array. - - The one-dimensional array that is the destination of the elements copied from the object. The array must have zero-based indexing. - The zero-based index in array at which copying begins. - The number of elements to copy to array. - - - - Removes all elements in the specified collection from the current object. - - The collection of items to remove from the object. - - - - Returns an enumerator that iterates through a . - - A .Enumerator object for the object. - - - - Modifies the current object to contain only elements that are present in that object and in the specified collection. - - The collection to compare to the current object. - - - - Determines whether a object is a proper subset of the specified collection. - - The collection to compare to the current object. - true if the object is a proper subset of other; otherwise, false. - - - - Determines whether a object is a proper subset of the specified collection. - - The collection to compare to the current object. - true if the object is a proper superset of other; otherwise, false. - - - - Determines whether a object is a subset of the specified collection. - - The collection to compare to the current object. - true if the object is a subset of other; otherwise, false. - - - - Determines whether a object is a superset of the specified collection. - - The collection to compare to the current object. - true if the object is a superset of other; otherwise, false. - - - - Determines whether the current object and a specified collection share common elements. - - The collection to compare to the current object. - true if the object and other share at least one common element; otherwise, false. - - - - Removes the specified element from a object. - - The element to remove. - true if the element is successfully found and removed; otherwise, false. This method returns false if item is not found in the object. - - - - Determines whether a object and the specified collection contain the same elements. - - The collection to compare to the current object. - true if the object is equal to other; otherwise, false. - - - - Modifies the current object to contain only elements that are present either in that object or in the specified collection, but not both. - - The collection to compare to the current object. - - - - Sets the capacity of a object to the actual number of elements it contains, rounded up to a nearby, implementation-specific value. - - - - - Modifies the current object to contain all elements that are present in itself, the specified collection, or both. - - The collection to compare to the current object. - - - - Returns an enumerator that iterates through a collection. - - - An object that can be used to iterate through the collection. - - - - - Gets the IEqualityComparer<T> object that is used to determine equality for the values in the set. - - - - - Gets the number of elements contained in the . - - - The number of elements contained in the . - - - - - Gets a value indicating whether the is read-only. - - true if the is read-only; otherwise, false. - - - - - Raised when the collection changes. - - - - - Raised when a property value changes. - - - - - The property names used with INotifyPropertyChanged. - - - - - Initializes a new instance of the class. - - The items from which the elements are copied. - - - - Initializes a new instance of the class. - - The items from which the elements are copied. - - - - Determines whether an element is in the collection with the specified name. - - The name of the item to locate in the collection. - - true if item is found in the collection; otherwise, false. - - - - - Determines the index of a specific item in the list. - - The name of the item to locate in the list. - The index of item if found in the list; otherwise, -1. - - - - Gets the item with the specified name. - - - The item with the specified name. - - - - - Searches for the specified object and returns the zero-based index of the first occurrence within the entire . - - - The zero-based index of the first occurrence of within the entire , if found; otherwise, –1. - - - The object to locate in the . The value can be null for reference types. - - - - - Inserts an element into the at the specified index. - - - The zero-based index at which should be inserted. - - - The object to insert. The value can be null for reference types. - - - is less than 0. - - -or- - is greater than . - - - - - Removes the element at the specified index of the . - - - The zero-based index of the element to remove. - - - is less than 0. - - -or- - is equal to or greater than . - - - - - Adds an object to the end of the . - - - The object to be added to the end of the . The value can be null for reference types. - - - - - Removes all elements from the . - - - - - Determines whether an element is in the . - - true if is found in the ; otherwise, false. - - - The object to locate in the . The value can be null for reference types. - - - - - Copies the entire to a compatible one-dimensional array, starting at the specified index of the target array. - - - The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. - - - The zero-based index in at which copying begins. - - - is null. - - - is less than 0. - - - is equal to or greater than the length of . - - -or- - - The number of elements in the source is greater than the available space from to the end of the destination . - - - - - Removes the first occurrence of a specific object from the . - - true if is successfully removed; otherwise, false. This method also returns false if was not found in the . - - - The object to remove from the . The value can be null for reference types. - - - - - Returns an enumerator that iterates through the . - - - A for the . - - - - - Returns an enumerator that iterates through a collection. - - - An object that can be used to iterate through the collection. - - - - - Adds an item to the . - - The object to add to the . - - The position into which the new element was inserted, or -1 to indicate that the item was not inserted into the collection, - - - The is read-only.-or- The has a fixed size. - - - - - Determines whether the contains a specific value. - - The object to locate in the . - - true if the is found in the ; otherwise, false. - - - - - Determines the index of a specific item in the . - - The object to locate in the . - - The index of if found in the list; otherwise, -1. - - - - - Inserts an item to the at the specified index. - - The zero-based index at which should be inserted. - The object to insert into the . - - is not a valid index in the . - - - The is read-only.-or- The has a fixed size. - - - is null reference in the . - - - - - Removes the first occurrence of a specific object from the . - - The object to remove from the . - - The is read-only.-or- The has a fixed size. - - - - - Copies the elements of the to an , starting at a particular index. - - The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. - The zero-based index in at which copying begins. - - is null. - - - is less than zero. - - - is multidimensional.-or- The number of elements in the source is greater than the available space from to the end of the destination . - - - The type of the source cannot be cast automatically to the type of the destination . - - - - - Adds the elements of the specified collection to the end of the . - - - The collection whose elements should be added to the end of the . The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type. - - - is null. - - - - - Returns a read-only wrapper for the current collection. - - - A that acts as a read-only wrapper around the current . - - - - - Searches a range of elements in the sorted for an element using the specified comparer and returns the zero-based index of the element. - - - The zero-based index of in the sorted , if is found; otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than or, if there is no larger element, the bitwise complement of . - - - The zero-based starting index of the range to search. - - - The length of the range to search. - - - The object to locate. The value can be null for reference types. - - - The implementation to use when comparing elements, or null to use the default comparer . - - - is less than 0. - - -or- - is less than 0. - - - and do not denote a valid range in the . - - - is null, and the default comparer cannot find an implementation of the generic interface or the interface for type T. - - - - - Searches the entire sorted for an element using the default comparer and returns the zero-based index of the element. - - - The zero-based index of in the sorted , if is found; otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than or, if there is no larger element, the bitwise complement of . - - - The object to locate. The value can be null for reference types. - - - The default comparer cannot find an implementation of the generic interface or the interface for type T. - - - - - Searches the entire sorted for an element using the specified comparer and returns the zero-based index of the element. - - - The zero-based index of in the sorted , if is found; otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than or, if there is no larger element, the bitwise complement of . - - - The object to locate. The value can be null for reference types. - - - The implementation to use when comparing elements. - - -or- - null to use the default comparer . - - - is null, and the default comparer cannot find an implementation of the generic interface or the interface for type T. - - - - - Converts the elements in the current to another type, and returns a list containing the converted elements. - - - A of the target type containing the converted elements from the current . - - - A delegate that converts each element from one type to another type. - - - The type of the elements of the target array. - - - is null. - - - - - Determines whether the contains elements that match the conditions defined by the specified predicate. - - true if the contains one or more elements that match the conditions defined by the specified predicate; otherwise, false. - - - The delegate that defines the conditions of the elements to search for. - - - is null. - - - - - Searches for an element that matches the conditions defined by the specified predicate, and returns the first occurrence within the entire . - - - The first element that matches the conditions defined by the specified predicate, if found; otherwise, the default value for type T. - - - The delegate that defines the conditions of the element to search for. - - - is null. - - - - - Retrieves all the elements that match the conditions defined by the specified predicate. - - - A containing all the elements that match the conditions defined by the specified predicate, if found; otherwise, an empty . - - - The delegate that defines the conditions of the elements to search for. - - - is null. - - - - - Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the entire . - - - The zero-based index of the first occurrence of an element that matches the conditions defined by , if found; otherwise, –1. - - - The delegate that defines the conditions of the element to search for. - - - is null. - - - - - Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the range of elements in the that extends from the specified index to the last element. - - - The zero-based index of the first occurrence of an element that matches the conditions defined by , if found; otherwise, –1. - - - The zero-based starting index of the search. - - - The delegate that defines the conditions of the element to search for. - - - is null. - - - is outside the range of valid indexes for the . - - - - - Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the range of elements in the that starts at the specified index and contains the specified number of elements. - - - The zero-based index of the first occurrence of an element that matches the conditions defined by , if found; otherwise, –1. - - - The zero-based starting index of the search. - - - The number of elements in the section to search. - - - The delegate that defines the conditions of the element to search for. - - - is null. - - - is outside the range of valid indexes for the . - - -or- - is less than 0. - - -or- - and do not specify a valid section in the . - - - - - Searches for an element that matches the conditions defined by the specified predicate, and returns the last occurrence within the entire . - - - The last element that matches the conditions defined by the specified predicate, if found; otherwise, the default value for type T. - - - The delegate that defines the conditions of the element to search for. - - - is null. - - - - - Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the last occurrence within the entire . - - - The zero-based index of the last occurrence of an element that matches the conditions defined by , if found; otherwise, –1. - - - The delegate that defines the conditions of the element to search for. - - - is null. - - - - - Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the last occurrence within the range of elements in the that extends from the first element to the specified index. - - - The zero-based index of the last occurrence of an element that matches the conditions defined by , if found; otherwise, –1. - - - The zero-based starting index of the backward search. - - - The delegate that defines the conditions of the element to search for. - - - is null. - - - is outside the range of valid indexes for the . - - - - - Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the last occurrence within the range of elements in the that contains the specified number of elements and ends at the specified index. - - - The zero-based index of the last occurrence of an element that matches the conditions defined by , if found; otherwise, –1. - - - The zero-based starting index of the backward search. - - - The number of elements in the section to search. - - - The delegate that defines the conditions of the element to search for. - - - is null. - - - is outside the range of valid indexes for the . - - -or- - is less than 0. - - -or- - and do not specify a valid section in the . - - - - - Performs the specified action on each element of the . - - - The delegate to perform on each element of the . - - - is null. - - - - - Creates a shallow copy of a range of elements in the source . - - - A shallow copy of a range of elements in the source . - - - The zero-based index at which the range starts. - - - The number of elements in the range. - - - is less than 0. - - -or- - is less than 0. - - - and do not denote a valid range of elements in the . - - - - - Searches for the specified object and returns the zero-based index of the first occurrence within the range of elements in the that extends from the specified index to the last element. - - - The zero-based index of the first occurrence of within the range of elements in the that extends from to the last element, if found; otherwise, –1. - - - The object to locate in the . The value can be null for reference types. - - - The zero-based starting index of the search. - - - is outside the range of valid indexes for the . - - - - - Searches for the specified object and returns the zero-based index of the first occurrence within the range of elements in the that starts at the specified index and contains the specified number of elements. - - - The zero-based index of the first occurrence of within the range of elements in the that starts at and contains number of elements, if found; otherwise, –1. - - - The object to locate in the . The value can be null for reference types. - - - The zero-based starting index of the search. - - - The number of elements in the section to search. - - - is outside the range of valid indexes for the . - - -or- - is less than 0. - - -or- - and do not specify a valid section in the . - - - - - Inserts the elements of a collection into the at the specified index. - - - The zero-based index at which the new elements should be inserted. - - - The collection whose elements should be inserted into the . The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type. - - - is null. - - - is less than 0. - - -or- - is greater than . - - - - - Searches for the specified object and returns the zero-based index of the last occurrence within the entire . - - - The zero-based index of the last occurrence of within the entire the , if found; otherwise, –1. - - - The object to locate in the . The value can be null for reference types. - - - - - Searches for the specified object and returns the zero-based index of the last occurrence within the range of elements in the that extends from the first element to the specified index. - - - The zero-based index of the last occurrence of within the range of elements in the that extends from the first element to , if found; otherwise, –1. - - - The object to locate in the . The value can be null for reference types. - - - The zero-based starting index of the backward search. - - - is outside the range of valid indexes for the . - - - - - Searches for the specified object and returns the zero-based index of the last occurrence within the range of elements in the that contains the specified number of elements and ends at the specified index. - - - The zero-based index of the last occurrence of within the range of elements in the that contains number of elements and ends at , if found; otherwise, –1. - - - The object to locate in the . The value can be null for reference types. - - - The zero-based starting index of the backward search. - - - The number of elements in the section to search. - - - is outside the range of valid indexes for the . - - -or- - is less than 0. - - -or- - and do not specify a valid section in the . - - - - - Removes the all the elements that match the conditions defined by the specified predicate. - - - The number of elements removed from the . - - - The delegate that defines the conditions of the elements to remove. - - - is null. - - - - - Removes a range of elements from the . - - - The zero-based starting index of the range of elements to remove. - - - The number of elements to remove. - - - is less than 0. - - -or- - is less than 0. - - - and do not denote a valid range of elements in the . - - - - - Determines whether every element in the matches the conditions defined by the specified predicate. - - true if every element in the matches the conditions defined by the specified predicate; otherwise, false. If the list has no elements, the return value is true. - - - The delegate that defines the conditions to check against the elements. - - - is null. - - - - - Copies the elements of the to a new array. - - - An array containing copies of the elements of the . - - - - - Sets the capacity to the actual number of elements in the , if that number is less than a threshold value. - - - - - Reverses the order of the elements in the entire . - - - - - Reverses the order of the elements in the specified range. - - - The zero-based starting index of the range to reverse. - - - The number of elements in the range to reverse. - - - is less than 0. - - -or- - is less than 0. - - - and do not denote a valid range of elements in the . - - - - - Sorts the elements in the entire using the default comparer. - - - The default comparer cannot find an implementation of the generic interface or the interface for type T. - - - - - Sorts the elements in the entire using the specified comparer. - - - The implementation to use when comparing elements. - - - is null. - - - The implementation of caused an error during the sort. For example, might not return 0 when comparing an item with itself. - - - - - Sorts the elements in the entire using the specified comparer. - - - The implementation to use when comparing elements, or null to use the default comparer . - - - is null, and the default comparer cannot find implementation of the generic interface or the interface for type T. - - - The implementation of caused an error during the sort. For example, might not return 0 when comparing an item with itself. - - - - - Sorts the elements in a range of elements in using the specified comparer. - - - The zero-based starting index of the range to sort. - - - The length of the range to sort. - - - The implementation to use when comparing elements, or null to use the default comparer . - - - is less than 0. - - -or- - is less than 0. - - - and do not specify a valid range in the . - - -or- - - The implementation of caused an error during the sort. For example, might not return 0 when comparing an item with itself. - - - is null, and the default comparer cannot find implementation of the generic interface or the interface for type T. - - - - - Sorts the elements in the entire using the specified . - - - The to use when comparing elements. - - - is null. - - - The implementation of caused an error during the sort. For example, might not return 0 when comparing an item with itself. - - - - - Gets or sets the element at the specified index. - - - The element at the specified index. - - - The zero-based index of the element to get or set. - - - is less than 0. - - -or- - is equal to or greater than . - - - - - Gets the number of elements actually contained in the . - - - The number of elements actually contained in the . - - - - - Gets a value indicating whether the is read-only. - - true if the is read-only; otherwise, false. - - - - - Gets or sets the element at the specified index. - - - The element at the specified index. - - - is not a valid index in the . - - - The property is set and the is read-only. - - - - - Gets a value indicating whether the is read-only. - - - true if the is read-only; otherwise, false. - - - - - Gets a value indicating whether the has a fixed size. - - - true if the has a fixed size; otherwise, false. - - - - - Gets an object that can be used to synchronize access to the . - - - An object that can be used to synchronize access to the . - - - - - Gets a value indicating whether access to the is synchronized (thread safe). - - - true if access to the is synchronized (thread safe); otherwise, false. - - - - - Occurs when an item is added, removed, changed, moved, or the entire list is refreshed. - - - - - Occurs when a property value changes. - - - - - A collection that provides notifications when items get added, removed, or when the whole list is refreshed. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The collection. - - - - Initializes a new instance of the class. - - The delimited values. - - - - Disallows reentrant attempts to change this collection. - - An IDisposable object that can be used to dispose of the object. - - - - Checks for reentrant attempts to change this collection. - - - - - Removes all elements from the . - - - - - Inserts an element into the at the specified index. - - The zero-based index at which should be inserted. - The object to insert. The value can be null for reference types. - - is less than zero. - -or- - is greater than . - - - - - Moves the item at the specified index to a new location in the collection. - - The zero-based index specifying the location of the item to be moved. - The zero-based index specifying the new location of the item. - - - - Moves the item at the specified index to a new location in the collection. - - The zero-based index specifying the location of the item to be moved. - The zero-based index specifying the new location of the item. - - - - Raises the CollectionChanged event with the provided arguments. - - - - - Raises the PropertyChanged event with the provided arguments. - - The instance containing the event data. - - - - Raises the PropertyChanged event with the provided arguments. - - Name of the property. - - - - Removes the element at the specified index of the . - - The zero-based index of the element to remove. - - is less than zero. - -or- - is equal to or greater than . - - - - - Replaces the element at the specified index. - - The zero-based index of the element to replace. - The new value for the element at the specified index. The value can be null for reference types. - - is less than zero. - -or- - is greater than . - - - - - Returns a that represents the current . - - - A that represents the current . - - - - - Returns a that represents the values. - - The values. - The delimiter. - - A that represents the current . - - - - - Returns a that represents the values. - - The values. - - A that represents the current . - - - - - Returns an array of strings that represents the current collection. - - An array of strings that represents the current collection. - - - - Adds the split values from to the collection. - The string is split using the property. - - The delimited values. - The number of values added. - Thrown when is null. - - - - Parses the specified delimited values. - - The delimited values. - The list delimiters. - - - - - Gets or sets the string delimiter. - - The string delimiter. - - - - Occurs when a property value changed. - - - - - Occurs when the collection changed. - - - - - A type converter for the . - - - - - Returns whether this converter can convert an object of the given type to the type of this converter, using the specified context. - - An that provides a format context. - A that represents the type you want to convert from. - - true if this converter can perform the conversion; otherwise, false. - - - - - Returns whether this converter can convert the object to the specified type, using the specified context. - - An that provides a format context. - A that represents the type you want to convert to. - - true if this converter can perform the conversion; otherwise, false. - - - - - Converts the given object to the type of this converter, using the specified context and culture information. - - An that provides a format context. - The to use as the current culture. - The to convert. - - An that represents the converted value. - - - The conversion cannot be performed. - - - - - Converts the given value object to the specified type, using the specified context and culture information. - - An that provides a format context. - A . If null is passed, the current culture is assumed. - The to convert. - The to convert the parameter to. - - An that represents the converted value. - - - The parameter is null. - - - The conversion cannot be performed. - - - - - A collection that provides notifications when items get added, removed, or when the whole list is refreshed. - - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The collection. - - - - Disallows reentrant attempts to change this collection. - - An IDisposable object that can be used to dispose of the object. - - - - Checks for reentrant attempts to change this collection. - - - - - Removes all elements from the . - - - - - Inserts an element into the at the specified index. - - The zero-based index at which should be inserted. - The object to insert. The value can be null for reference types. - - is less than zero. - -or- - is greater than . - - - - - Moves the item at the specified index to a new location in the collection. - - The zero-based index specifying the location of the item to be moved. - The zero-based index specifying the new location of the item. - - - - Moves the item at the specified index to a new location in the collection. - - The zero-based index specifying the location of the item to be moved. - The zero-based index specifying the new location of the item. - - - - Raises the CollectionChanged event with the provided arguments. - - - - - Raises the PropertyChanged event with the provided arguments. - - The instance containing the event data. - - - - Raises the PropertyChanged event with the provided arguments. - - Name of the property. - - - - Removes the element at the specified index of the . - - The zero-based index of the element to remove. - - is less than zero. - -or- - is equal to or greater than . - - - - - Replaces the element at the specified index. - - The zero-based index of the element to replace. - The new value for the element at the specified index. The value can be null for reference types. - - is less than zero. - -or- - is greater than . - - - - - Occurs when a property value changed. - - - - - Occurs when the collection changed. - - - - - Raises the PropertyChanged event with the provided arguments. - - The instance containing the event data. - - - - Raises the PropertyChanged event with the provided arguments. - - Name of the property. - - - - Raises the CollectionChanged event with the provided arguments. - - - - - Initializes a new instance of the class. - - - - Initializes a new instance of the class that contains elements copied from the specified and uses the default equality comparer for the key type. - The whose elements are copied to the new . - dictionary contains one or more duplicate keys. - dictionary is null. - - - Initializes a new instance of the class that is empty, has the default initial capacity, and uses the specified . - The implementation to use when comparing keys, or null to use the default for the type of the key. - - - Initializes a new instance of the class that is empty, has the specified initial capacity, and uses the default equality comparer for the key type. - The initial number of elements that the can contain. - capacity is less than 0. - - - Initializes a new instance of the class that contains elements copied from the specified and uses the specified . - The whose elements are copied to the new . - The implementation to use when comparing keys, or null to use the default for the type of the key. - dictionary contains one or more duplicate keys. - dictionary is null. - - - Initializes a new instance of the class that is empty, has the specified initial capacity, and uses the specified . - The initial number of elements that the can contain. - The implementation to use when comparing keys, or null to use the default for the type of the key. - capacity is less than 0. - - - Implements the interface and raises the deserialization event when the deserialization is complete. - The source of the deserialization event. - The object associated with the current instance is invalid. - - - Adds the specified key and value to the dictionary. - The value of the element to add. The value can be null for reference types. - The key of the element to add. - An element with the same key already exists in the . - key is null. - - - Removes all keys and values from the . - - - Determines whether the contains the specified key. - true if the contains an element with the specified key; otherwise, false. - The key to locate in the . - key is null. - - - Removes the value with the specified key from the . - true if the element is successfully found and removed; otherwise, false. This method returns false if key is not found in the . - The key of the element to remove. - key is null. - - - - Gets the value associated with the specified key. - - The key whose value to get. - When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. - - true if the object that implements contains an element with the specified key; otherwise, false. - - is null. - - - Implements the interface and returns the data needed to serialize the instance. - A structure that contains the source and destination of the serialized stream associated with the instance. - A object that contains the information required to serialize the instance. - info is null. - - - Determines whether the contains a specific value. - true if the contains an element with the specified value; otherwise, false. - The value to locate in the . The value can be null for reference types. - - - Returns an enumerator that iterates through the . - A structure for the . - - - - Enters the read lock. - - - - - - Enters the upgradeable read lock. - - - - - - Enters the write lock. - - - - - - Occurs when a property value changed. - - - - - Occurs when the collection changed. - - - - Gets the that is used to determine equality of keys for the dictionary. - The generic interface implementation that is used to determine equality of keys for the current and to provide hash values for the keys. - - - Gets a collection containing the keys in the . - A containing the keys in the . - - - Gets a collection containing the values in the . - A containing the values in the . - - - Gets the number of key/value pairs contained in the . - The number of key/value pairs contained in the . - - - Gets or sets the value associated with the specified key. - The value associated with the specified key. If the specified key is not found, a get operation throws a , and a set operation creates a new element with the specified key. - The key of the value to get or set. - key is null. - The property is retrieved and key does not exist in the collection. - - - - Adds an object to the end of the . - - - The object to be added to the end of the . The value can be null for reference types. - - - - - Removes all elements from the . - - - - - Determines whether an element is in the . - - true if is found in the ; otherwise, false. - - - The object to locate in the . The value can be null for reference types. - - - - - Copies the entire to a compatible one-dimensional array, starting at the specified index of the target array. - - - The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. - - - The zero-based index in at which copying begins. - - - is null. - - - is less than 0. - - - is equal to or greater than the length of . - - -or- - - The number of elements in the source is greater than the available space from to the end of the destination . - - - - - Removes the first occurrence of a specific object from the . - - true if is successfully removed; otherwise, false. This method also returns false if was not found in the . - - - The object to remove from the . The value can be null for reference types. - - - - - Returns an enumerator that iterates through the . - - - A for the . - - - - - Returns an enumerator that iterates through a collection. - - - An object that can be used to iterate through the collection. - - - - - Gets the number of elements actually contained in the . - - - The number of elements actually contained in the . - - - - - Gets a value indicating whether the is read-only. - - true if the is read-only; otherwise, false. - - - - - Occurs when an item is added, removed, changed, moved, or the entire list is refreshed. - - - - - Occurs when a property value changes. - - - - - Allows control of command line parsing. - Attach this attribute to instance fields of types used - as the destination of command line argument parsing. - - - - - Allows control of command line parsing. - - Specifies the error checking to be done on the argument. - - - - The error checking to be done on the argument. - - - - - Returns true if the argument did not have an explicit short name specified. - - - - - The short name of the argument. - Set to null means use the default short name if it does not - conflict with any other parameter name. - Set to String.Empty for no short name. - This property should not be set for DefaultArgumentAttributes. - - - - - Returns true if the argument did not have an explicit long name specified. - - - - - The long name of the argument. - Set to null means use the default long name. - The long name for every argument must be unique. - It is an error to specify a long name of String.Empty. - - - - - The default value of the argument. - - - - - Returns true if the argument has a default value. - - - - - Returns true if the argument has help text specified. - - - - - The help text for the argument. - - - - - Show optional boolean toggle syntax options for the arguments. - - - - - Used to control parsing of command line arguments. - - - - - Indicates that this field is required. An error will be displayed - if it is not present when parsing arguments. - - - - - Only valid in conjunction with Multiple. - Duplicate values will result in an error. - - - - - Indicates that the argument may be specified more than once. - Only valid if the argument is a collection - - - - - The default type for non-collection arguments. - The argument is not required, but an error will be reported if it is specified more than once. - - - - - For non-collection arguments, when the argument is specified more than - once no error is reported and the value of the argument is the last - value which occurs in the argument list. - - - - - The default type for collection arguments. - The argument is permitted to occur multiple times, but duplicate - values will cause an error to be reported. - - - - - Indicates that this argument is the default argument. - '/' or '-' prefix only the argument value is specified. - The ShortName property should not be set for DefaultArgumentAttribute - instances. The LongName property is used for usage text only and - does not affect the usage of the argument. - - - - - Indicates that this argument is the default argument. - - Specifies the error checking to be done on the argument. - - - - A delegate used in error reporting. - - - - - Parser for command line arguments. - - The parser specification is inferred from the instance fields of the object - specified as the destination of the parse. - Valid argument types are: int, uint, string, bool, enums - Also argument types of Array of the above types are also valid. - - Error checking options can be controlled by adding a ArgumentAttribute - to the instance fields of the destination object. - - At most one field may be marked with the DefaultArgumentAttribute - indicating that arguments without a '-' or '/' prefix will be parsed as that argument. - - If not specified then the parser will infer default options for parsing each - instance field. The default long name of the argument is the field name. The - default short name is the first character of the long name. Long names and explicitly - specified short names must be unique. Default short names will be used provided that - the default short name does not conflict with a long name or an explicitly - specified short name. - - Arguments which are array types are collection arguments. Collection - arguments can be specified multiple times. - - - - - The System Defined new line string. - - - - - Don't ever call this. - - - - - Parses Command Line Arguments. Displays usage message to Console.Out - if /?, /help or invalid arguments are encountered. - Errors are output on Console.Error. - Use ArgumentAttributes to control parsing behavior. - - The actual arguments. - The resulting parsed arguments. - true if no errors were detected. - - - - Parses Command Line Arguments. - Errors are output on Console.Error. - Use ArgumentAttributes to control parsing behavior. - - The actual arguments. - The resulting parsed arguments. - true if no errors were detected. - - - - Parses Command Line Arguments. - Use ArgumentAttributes to control parsing behavior. - - The actual arguments. - The resulting parsed arguments. - The destination for parse errors. - true if no errors were detected. - - - - Checks if a set of arguments asks for help. - - Args to check for help. - Returns true if args contains /? or /help. - - - - Returns a Usage string for command line argument parsing. - Use ArgumentAttributes to control parsing behavior. - Formats the output to the width of the current console window. - - The type of the arguments to display usage for. - Printable string containing a user friendly description of command line arguments. - - - - Returns a Usage string for command line argument parsing. - Use ArgumentAttributes to control parsing behavior. - - The type of the arguments to display usage for. - The number of columns to format the output to. - Printable string containing a user friendly description of command line arguments. - - - - Searches a StringBuilder for a character - - The text to search. - The character value to search for. - The index to stat searching at. - The index of the first occurrence of value or -1 if it is not found. - - - - Searches a StringBuilder for a character in reverse - - The text to search. - The character to search for. - The index to start the search at. - The index of the last occurrence of value in text or -1 if it is not found. - - - - Creates a new command line argument parser. - - The type of object to parse. - The destination for parse errors. - - - - Parses an argument list into an object - - - - true if an error occurred - - - - Parses an argument list. - - The arguments to parse. - The destination of the parsed arguments. - true if no parse errors were encountered. - - - - A user firendly usage string describing the command line argument syntax. - - - - - Does this parser have a default argument. - - Does this parser have a default argument. - - - - The interface for pipeline context data - - - - - Gets or sets a value indicating whether this pipeline is cancelled. - - - true if this pipeline is cancelled; otherwise, false. - - - - - Gets or sets a value indicating whether this pipeline context is processed. - - - true if this pipeline context is processed; otherwise, false. - - - - - The base class for pipeline context data - - - - - Gets or sets a value indicating whether this pipeline is cancelled. - - - true if this pipeline is cancelled; otherwise, false. - - - - - Gets or sets a value indicating whether this pipeline context is processed. - - - true if this pipeline context is processed; otherwise, false. - - - - - Processes this module using the specified pipeline context. - - The pipeline context. - - - - Gets the priority of this pipeline module. - - The pipeline module priority. - - - - The base class for pipeline modules - - The type of the pipeline context. - - - - Processes this module using the specified pipeline context. - - The pipeline context. - - - - Gets the priority of this pipeline module. - - The pipeline module priority. - - - - The base class for a pipeline service. - - The type used as the context for the pipeline. - The base type of the pipeline module to run in this pipeline. - - The pipeline works by collection modules (classes) that have a common base class to run in a series. - To setup a pipeline, you have to have a context class that will hold all the common data for the pipeline. - You also have to have a common base class that inherits for all your modules. - The pipeline looks for all types that inherit that common base class to run. - - - - - Runs all the modules of pipeline with the specified context list. - - The context list to run the modules with. - - - - Runs all the modules of pipeline with the specified context. - - The context to run the modules with. - - - - Runs all the specified modules of pipeline with the specified context. - - The context to run the modules with. - The list modules to run. - - - - Called before any pipeline modules are run. - - The context the modules will run with. - - - - Called after all pipeline modules have run. - - The context the modules ran with. - - - - Gets the modules that are subclasses of . - - An enumerable list of modules to run for the pipeline. - - - - Provides a global point of access to a single instance of a given class. - - The type to provide a singleton instance for. - - - The singleton instance can be accessed through a static property, - where the type of the singleton is passed as a generic type parameter. - Subsequent requests for the instance will yield the same class instance. - - - The singleton is thread-safe and lazy (i.e. it is created when the instance is first requested). - - - - The following example gets a singleton instance of a Foo class: - - Foo singleInstance = Singleton.Current; - - - - - - Gets the singleton instance. - - - - - A class representing a singleton pattern. - - The type of the singleton - - - - Initializes a new instance of the class. - - - - - Gets the current instance of the singleton. - - - - - Represents a weak reference, which references an object while still allowing - that object to be reclaimed by garbage collection. - - The type of the object that is referenced. - - - - Initializes a new instance of the Minimal.WeakReference{T} class, referencing - the specified object. - - The object to reference. - - - - Initializes a new instance of the WeakReference{T} class, referencing - the specified object and using the specified resurrection tracking. - - An object to track. - Indicates when to stop tracking the object. If true, the object is tracked - after finalization; if false, the object is only tracked until finalization. - - - - Casts an object of the type T to a weak reference - of T. - - - - - Casts a weak reference to an object of the type the - reference represents. - - - - - Gets or sets the object (the target) referenced by the current WeakReference{T} - object. - - - - - Gets the content type based on file extension . - - The file extension. - The content type. - - - - Human-readable text and source code content types - - - - RichText; text/enriched - - - Html; text/html - - - Plain Text; text/plain - - - RichText; text/richtext - - - Sgml; text/sgml - - - JavaScript; text/javascript - - - Cascading Style Sheets; text/css - - - Xml; text/xml - - - - A base class that implements - - - - - Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - - - - - Releases unmanaged and - optionally - managed resources - - true to release both managed and unmanaged resources; false to release only unmanaged resources. - - - - Disposes the managed resources. - - - - - Disposes the unmanaged resources. - - - - - Releases unmanaged resources and performs other cleanup operations before the - is reclaimed by garbage collection. - - - - - Validates a credit card number using the standard Luhn/mod10 validation algorithm. - - Card number, with or without punctuation - - true if card number appears valid; otherwise, false. - - - - - Cleans the credit card number. - - The card number. - The card number with only the valid digits. - - - - Determines whether the credit card is exired. - - The year. - The month. - - true if credit card is exired; otherwise, false. - - - - - The AdoHelper class is intended to encapsulate high performance, scalable best practices for - common data access uses. - - The type of the factory. - - - - Get an IDataParameter for use in a SQL command - - The name of the parameter to create - The value of the specified parameter - An IDataParameter object - - - - Get an IDataParameter for use in a SQL command - - The name of the parameter to create - The System.Data.DbType of the parameter - The size of the parameter - The System.Data.ParameterDirection of the parameter - An IDataParameter object - - - - Get an IDataParameter for use in a SQL command - - The name of the parameter to create - The System.Data.DbType of the parameter - The size of the parameter - The source column of the parameter - The System.Data.DataRowVersion of the parameter - An IDataParameter object - - - - This method is used to attach array of IDataParameters to an IDbCommand. - - This method will assign a value of DbNull to any parameter with a direction of - InputOutput and a value of null. - - This behavior will prevent default values from being used, but - this will be the less common case than an intended pure output parameter (derived as InputOutput) - where the user provided no input value. - - The command to which the parameters will be added - An array of IDataParameterParameters to be added to command - Thrown if command is null. - - - - This method assigns dataRow column values to an IDataParameterCollection - - The IDataParameterCollection to be assigned values - The dataRow used to hold the stored procedure's parameter values - Thrown if any of the parameter names are invalid. - - - - This method assigns dataRow column values to an array of IDataParameters - - Array of IDataParameters to be assigned values - The dataRow used to hold the stored procedure's parameter values - Thrown if any of the parameter names are invalid. - - - - This method assigns an array of values to an array of IDataParameters - - Array of IDataParameters to be assigned values - Array of objects holding the values to be assigned - Thrown if an incorrect number of parameters are passed. - - - - This method cleans up the parameter syntax for the provider - - The IDbCommand containing the parameters to clean up. - - - - This method opens (if necessary) and assigns a connection, transaction, command type and parameters - to the provided command - - The IDbCommand to be prepared - A valid IDbConnection, on which to execute this command - A valid IDbTransaction, or 'null' - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters to be associated with the command or 'null' if no parameters are required - true if the connection was opened by the method, otherwose is false. - Thrown if command is null. - Thrown if commandText is null. - - - - This method clears (if necessary) the connection, transaction, command type and parameters - from the provided command - - - Not implemented here because the behavior of this method differs on each data provider. - - The IDbCommand to be cleared - - - - Execute an IDbCommand (that returns a resultset) against the database specified in - the connection string. - - The IDbCommand object to use - A DataSet containing the resultset generated by the command - Thrown if command is null. - - - - Execute an IDbCommand (that returns a resultset and takes no parameters) against the database specified in - the connection string. - - - - DataSet ds = helper.ExecuteDataset(connString, CommandType.StoredProcedure, "GetOrders"); - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A DataSet containing the resultset generated by the command - Thrown if connectionString is null - Thrown if commandText is null - A DataSet containing the resultset generated by the command - - - - Execute an IDbCommand (that returns a resultset) against the database specified in the connection string - using the provided parameters. - - - - DataSet ds = helper.ExecuteDataset(connString, CommandType.StoredProcedure, "GetOrders", new IDbParameter("@prodid", 24)); - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDbParamters used to execute the command - A DataSet containing the resultset generated by the command - Thrown if connectionString is null - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the database specified in - the connection string using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - DataSet ds = helper.ExecuteDataset(connString, "GetOrders", 24, 36); - - A valid connection string for an IDbConnection - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - A DataSet containing the resultset generated by the command - Thrown if connectionString is null - Thrown if spName is null - - - - Execute an IDbCommand (that returns a resultset and takes no parameters) against the provided IDbConnection. - - - - DataSet ds = helper.ExecuteDataset(conn, CommandType.StoredProcedure, "GetOrders"); - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A DataSet containing the resultset generated by the command - Thrown if commandText is null - Thrown if connection is null - - - - Execute an IDbCommand (that returns a resultset) against the specified IDbConnection - using the provided parameters. - - - - DataSet ds = helper.ExecuteDataset(conn, CommandType.StoredProcedure, "GetOrders", new IDataParameter("@prodid", 24)); - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - A DataSet containing the resultset generated by the command - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - Thrown if connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified IDbConnection - using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - DataSet ds = helper.ExecuteDataset(conn, "GetOrders", 24, 36); - - A valid IDbConnection - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - A DataSet containing the resultset generated by the command - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - Thrown if connection is null - - - - Execute an IDbCommand (that returns a resultset and takes no parameters) against the provided IDbTransaction. - - - DataSet ds = helper.ExecuteDataset(trans, CommandType.StoredProcedure, "GetOrders"); - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A DataSet containing the resultset generated by the command - Thrown if commandText is null - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute an IDbCommand (that returns a resultset) against the specified IDbTransaction - using the provided parameters. - - - - DataSet ds = helper.ExecuteDataset(trans, CommandType.StoredProcedure, "GetOrders", new IDataParameter("@prodid", 24)); - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - A DataSet containing the resultset generated by the command - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified - IDbTransaction using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - DataSet ds = helper.ExecuteDataset(tran, "GetOrders", 24, 36); - - A valid IDbTransaction - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - A DataSet containing the resultset generated by the command - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute an IDbCommand (that returns no resultset) against the database - - The IDbCommand to execute - An int representing the number of rows affected by the command - Thrown if command is null. - - - - Execute an IDbCommand (that returns no resultset and takes no parameters) against the database specified in - the connection string - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An int representing the number of rows affected by the command - Thrown if connectionString is null - Thrown if commandText is null - - - - Execute an IDbCommand (that returns no resultset) against the database specified in the connection string - using the provided parameters - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - An int representing the number of rows affected by the command - Thrown if connectionString is null - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - - - - Execute a stored procedure via an IDbCommand (that returns no resultset) against the database specified in - the connection string using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - A valid connection string for an IDbConnection - The name of the stored prcedure - An array of objects to be assigned as the input values of the stored procedure - An int representing the number of rows affected by the command - Thrown if connectionString is null - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - - - - Execute an IDbCommand (that returns no resultset and takes no parameters) against the provided IDbConnection. - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An int representing the number of rows affected by the command - Thrown if commandText is null - Thrown if connection is null - - - - Execute an IDbCommand (that returns no resultset) against the specified IDbConnection - using the provided parameters. - - - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDbParamters used to execute the command - An int representing the number of rows affected by the command - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - Thrown if connection is null - - - - Execute a stored procedure via an IDbCommand (that returns no resultset) against the specified IDbConnection - using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - A valid IDbConnection - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - An int representing the number of rows affected by the command - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - Thrown if connection is null - - - - Execute an IDbCommand (that returns no resultset and takes no parameters) against the provided IDbTransaction. - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An int representing the number of rows affected by the command - Thrown if commandText is null - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute an IDbCommand (that returns no resultset) against the specified IDbTransaction - using the provided parameters. - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - An int representing the number of rows affected by the command - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute a stored procedure via an IDbCommand (that returns no resultset) against the specified - IDbTransaction using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - A valid IDbTransaction - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - An int representing the number of rows affected by the command - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute an IDbCommand (that returns a resultset) against the database specified in - the connection string. - - The IDbCommand object to use - A IDataReader containing the resultset generated by the command - Thrown if command is null. - - - - Execute an IDbCommand (that returns a resultset) against the database specified in - the connection string. - - The IDbCommand object to use - Enum indicating whether the connection was created internally or externally. - A IDataReader containing the resultset generated by the command - Thrown if command is null. - - - - Create and prepare an IDbCommand, and call ExecuteReader with the appropriate CommandBehavior. - - - If we created and opened the connection, we want the connection to be closed when the DataReader is closed. - - If the caller provided the connection, we want to leave it to them to manage. - - A valid IDbConnection, on which to execute this command - A valid IDbTransaction, or 'null' - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters to be associated with the command or 'null' if no parameters are required - Indicates whether the connection parameter was provided by the caller, or created by AdoHelper - IDataReader containing the results of the command - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - Thrown if connection is null - - - - Execute an IDbCommand (that returns a resultset and takes no parameters) against the database specified in - the connection string. - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A IDataReader containing the resultset generated by the command - Thrown if connectionString is null - Thrown if commandText is null - - - - Execute an IDbCommand (that returns a resultset) against the database specified in the connection string - using the provided parameters. - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - A IDataReader containing the resultset generated by the command - Thrown if connectionString is null - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the database specified in - the connection string using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - IDataReader dr = helper.ExecuteReader(connString, "GetOrders", 24, 36); - - A valid connection string for an IDbConnection - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - an IDataReader containing the resultset generated by the command - Thrown if connectionString is null - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - - - - Execute an IDbCommand (that returns a resultset and takes no parameters) against the provided IDbConnection. - - - - IDataReader dr = helper.ExecuteReader(conn, CommandType.StoredProcedure, "GetOrders"); - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - an IDataReader containing the resultset generated by the command - Thrown if commandText is null - - - - Execute an IDbCommand (that returns a resultset) against the specified IDbConnection - using the provided parameters. - - - - IDataReader dr = helper.ExecuteReader(conn, CommandType.StoredProcedure, "GetOrders", new IDataParameter("@prodid", 24)); - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - an IDataReader containing the resultset generated by the command - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - Thrown if connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified IDbConnection - using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - IDataReader dr = helper.ExecuteReader(conn, "GetOrders", 24, 36); - - A valid IDbConnection - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - an IDataReader containing the resultset generated by the command - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - Thrown if connection is null - - - - Execute an IDbCommand (that returns a resultset and takes no parameters) against the provided IDbTransaction. - - - IDataReader dr = helper.ExecuteReader(trans, CommandType.StoredProcedure, "GetOrders"); - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A IDataReader containing the resultset generated by the command - Thrown if commandText is null - - - - Execute an IDbCommand (that returns a resultset) against the specified IDbTransaction - using the provided parameters. - - - - IDataReader dr = helper.ExecuteReader(trans, CommandType.StoredProcedure, "GetOrders", new IDataParameter("@prodid", 24)); - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - A IDataReader containing the resultset generated by the command - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified - IDbTransaction using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - IDataReader dr = helper.ExecuteReader(tran, "GetOrders", 24, 36); - - A valid IDbTransaction - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - an IDataReader containing the resultset generated by the command - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute an IDbCommand (that returns a 1x1 resultset) against the database specified in - the connection string. - - The IDbCommand to execute - An object containing the value in the 1x1 resultset generated by the command - Thrown if command is null. - - - - Execute an IDbCommand (that returns a 1x1 resultset and takes no parameters) against the database specified in - the connection string. - - - - int orderCount = (int)helper.ExecuteScalar(connString, CommandType.StoredProcedure, "GetOrderCount"); - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An object containing the value in the 1x1 resultset generated by the command - Thrown if connectionString is null - Thrown if commandText is null - - - - Execute an IDbCommand (that returns a 1x1 resultset) against the database specified in the connection string - using the provided parameters. - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - An object containing the value in the 1x1 resultset generated by the command - Thrown if connectionString is null - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - - - - Execute a stored procedure via an IDbCommand (that returns a 1x1 resultset) against the database specified in - the connection string using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - int orderCount = (int)helper.ExecuteScalar(connString, "GetOrderCount", 24, 36); - - A valid connection string for an IDbConnection - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - An object containing the value in the 1x1 resultset generated by the command - Thrown if connectionString is null - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - - - - Execute an IDbCommand (that returns a 1x1 resultset and takes no parameters) against the provided IDbConnection. - - - - int orderCount = (int)helper.ExecuteScalar(conn, CommandType.StoredProcedure, "GetOrderCount"); - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An object containing the value in the 1x1 resultset generated by the command - Thrown if commandText is null - - - - Execute an IDbCommand (that returns a 1x1 resultset) against the specified IDbConnection - using the provided parameters. - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - An object containing the value in the 1x1 resultset generated by the command - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - Thrown if connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a 1x1 resultset) against the specified IDbConnection - using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - int orderCount = (int)helper.ExecuteScalar(conn, "GetOrderCount", 24, 36); - - A valid IDbConnection - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - An object containing the value in the 1x1 resultset generated by the command - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - Thrown if connection is null - - - - Execute an IDbCommand (that returns a 1x1 resultset and takes no parameters) against the provided IDbTransaction. - - - - int orderCount = (int)helper.ExecuteScalar(tran, CommandType.StoredProcedure, "GetOrderCount"); - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An object containing the value in the 1x1 resultset generated by the command - Thrown if commandText is null - - - - Execute an IDbCommand (that returns a 1x1 resultset) against the specified IDbTransaction - using the provided parameters. - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDbParamters used to execute the command - An object containing the value in the 1x1 resultset generated by the command - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a 1x1 resultset) against the specified - IDbTransaction using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - int orderCount = (int)helper.ExecuteScalar(tran, "GetOrderCount", 24, 36); - - A valid IDbTransaction - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - An object containing the value in the 1x1 resultset generated by the command - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - Thrown if transaction is null - Thrown if transaction.Connection is null - Thrown if the transaction is rolled back or commmitted - - - - Execute an IDbCommand (that returns a resultset) against the database specified in - the connection string. - - The IDbCommand to execute - A DataSet wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - Thrown if command is null. - - - - Execute an IDbCommand (that returns a resultset and takes no parameters) against the database specified in - the connection string. - - - - helper.FillDataset(connString, CommandType.StoredProcedure, "GetOrders", ds, new string[] {"orders"}); - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A DataSet wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - Thrown if connectionString is null - Thrown if commandText is null - - - - Execute an IDbCommand (that returns a resultset) against the database specified in the connection string - using the provided parameters. - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - A DataSet wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - - Thrown if connectionString is null - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the database specified in - the connection string using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - helper.FillDataset(connString, CommandType.StoredProcedure, "GetOrders", ds, new string[] {"orders"}, 24); - - A valid connection string for an IDbConnection - The name of the stored procedure - A dataset wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - - An array of objects to be assigned as the input values of the stored procedure - Thrown if connectionString is null - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - - - - Execute an IDbCommand (that returns a resultset and takes no parameters) against the provided IDbConnection. - - - - helper.FillDataset(conn, CommandType.StoredProcedure, "GetOrders", ds, new string[] {"orders"}); - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A dataset wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - - Thrown if commandText is null - Thrown if connection is null - - - - Execute an IDbCommand (that returns a resultset) against the specified IDbConnection - using the provided parameters. - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A DataSet wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - - An array of IDataParameters used to execute the command - Thrown if commandText is null - Thrown if connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified IDbConnection - using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - helper.FillDataset(conn, "GetOrders", ds, new string[] {"orders"}, 24, 36); - - A valid IDbConnection - The name of the stored procedure - A dataset wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - - An array of objects to be assigned as the input values of the stored procedure - Thrown if spName is null - Thrown if connection is null - - - - Execute an IDbCommand (that returns a resultset and takes no parameters) against the provided IDbTransaction. - - - - helper.FillDataset(tran, CommandType.StoredProcedure, "GetOrders", ds, new string[] {"orders"}); - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A dataset wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - - Thrown if commandText is null - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute an IDbCommand (that returns a resultset) against the specified IDbTransaction - using the provided parameters. - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A DataSet wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - - An array of IDataParameters used to execute the command - Thrown if commandText is null - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified - IDbTransaction using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - helper.FillDataset(tran, "GetOrders", ds, new string[] {"orders"}, 24, 36); - - A valid IDbTransaction - The name of the stored procedure - A dataset wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - - An array of objects to be assigned as the input values of the stored procedure - Thrown if spName is null - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Private helper method that execute an IDbCommand (that returns a resultset) against the specified IDbTransaction and IDbConnection - using the provided parameters. - - A valid IDbConnection - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A DataSet wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - - An array of IDataParameters used to execute the command - - - - This method consumes the RowUpdatingEvent and passes it on to the consumer specifed in the call to UpdateDataset - - The object that generated the event - The System.Data.Common.RowUpdatingEventArgs - - - - This method consumes the RowUpdatedEvent and passes it on to the consumer specifed in the call to UpdateDataset - - The object that generated the event - The System.Data.Common.RowUpdatingEventArgs - - - - Set up a command for updating a DataSet. - - command object to prepare - output parameter specifying whether the connection used should be closed by the DAAB - An IDbCommand object - - - - Executes the respective command for each inserted, updated, or deleted row in the DataSet. - - - - helper.UpdateDataset(conn, insertCommand, deleteCommand, updateCommand, dataSet, "Order"); - - A valid SQL statement or stored procedure to insert new records into the data source - A valid SQL statement or stored procedure to delete records from the data source - A valid SQL statement or stored procedure used to update records in the data source - The DataSet used to update the data source - The DataTable used to update the data source. - - - - Executes the IDbCommand for each inserted, updated, or deleted row in the DataSet also implementing RowUpdating and RowUpdated Event Handlers - - - - RowUpdatingEventHandler rowUpdatingHandler = new RowUpdatingEventHandler( OnRowUpdating ); - RowUpdatedEventHandler rowUpdatedHandler = new RowUpdatedEventHandler( OnRowUpdated ); - helper.UpdateDataSet(sqlInsertCommand, sqlDeleteCommand, sqlUpdateCommand, dataSet, "Order", rowUpdatingHandler, rowUpdatedHandler); - - A valid SQL statement or stored procedure to insert new records into the data source - A valid SQL statement or stored procedure to delete records from the data source - A valid SQL statement or stored procedure used to update records in the data source - The DataSet used to update the data source - The DataTable used to update the data source. - RowUpdatingEventHandler - RowUpdatedEventHandler - - - - Simplify the creation of an IDbCommand object by allowing - a stored procedure and optional parameters to be provided - - - - IDbCommand command = helper.CreateCommand(conn, "AddCustomer", "CustomerID", "CustomerName"); - - A valid connection string for an IDbConnection - The name of the stored procedure - An array of string to be assigned as the source columns of the stored procedure parameters - A valid IDbCommand object - Thrown if connectionString is null - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - - - - Simplify the creation of an IDbCommand object by allowing - a stored procedure and optional parameters to be provided - - - - IDbCommand command = helper.CreateCommand(conn, "AddCustomer", "CustomerID", "CustomerName"); - - A valid IDbConnection object - The name of the stored procedure - An array of string to be assigned as the source columns of the stored procedure parameters - A valid IDbCommand object - Thrown if spName is null - Thrown if connection is null - - - - Simplify the creation of an IDbCommand object by allowing - a stored procedure and optional parameters to be provided - - A valid connection string for an IDbConnection - A valid SQL statement - A System.Data.CommandType - The parameters for the SQL statement - A valid IDbCommand object - - - - Simplify the creation of an IDbCommand object by allowing - a stored procedure and optional parameters to be provided - - - IDbCommand command = helper.CreateCommand(conn, "AddCustomer", "CustomerID", "CustomerName"); - - A valid IDbConnection object - A valid SQL statement - A System.Data.CommandType - The parameters for the SQL statement - A valid IDbCommand object - - - - Execute a stored procedure via an IDbCommand (that returns no resultset) - against the database specified in the connection string using the - dataRow column values as the stored procedure's parameters values. - This method will assign the parameter values based on row values. - - The IDbCommand to execute - The dataRow used to hold the stored procedure's parameter values. - An int representing the number of rows affected by the command - Thrown if command is null. - - - - Execute a stored procedure via an IDbCommand (that returns no resultset) against the database specified in - the connection string using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on row values. - - A valid connection string for an IDbConnection - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - An int representing the number of rows affected by the command - Thrown if connectionString is null - Thrown if spName is null - - - - Execute a stored procedure via an IDbCommand (that returns no resultset) against the specified IDbConnection - using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on row values. - - A valid IDbConnection object - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - An int representing the number of rows affected by the command - Thrown if spName is null - Thrown if connection is null - - - - Execute a stored procedure via an IDbCommand (that returns no resultset) against the specified - IDbTransaction using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on row values. - - A valid IDbTransaction object - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - An int representing the number of rows affected by the command - Thrown if spName is null - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the database specified in - the connection string using the dataRow column values as the stored procedure's parameters values. - This method will assign the paraemter values based on row values. - - The IDbCommand to execute - The dataRow used to hold the stored procedure's parameter values. - A DataSet containing the resultset generated by the command - Thrown if command is null. - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the database specified in - the connection string using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on row values. - - A valid connection string for an IDbConnection - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - A DataSet containing the resultset generated by the command - Thrown if connectionString is null - Thrown if spName is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified IDbConnection - using the dataRow column values as the store procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on row values. - - A valid IDbConnection object - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - A DataSet containing the resultset generated by the command - Thrown if spName is null - Thrown if connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified IDbTransaction - using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on row values. - - A valid IDbTransaction object - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - A DataSet containing the resultset generated by the command - Thrown if spName is null - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the database specified in - the connection string using the dataRow column values as the stored procedure's parameters values. - This method will assign the parameter values based on parameter order. - - The IDbCommand to execute - The dataRow used to hold the stored procedure's parameter values. - A IDataReader containing the resultset generated by the command - Thrown if command is null. - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the database specified in - the connection string using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - A valid connection string for an IDbConnection - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - A IDataReader containing the resultset generated by the command - Thrown if connectionString is null - Thrown if spName is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified IDbConnection - using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - A valid IDbConnection object - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - A IDataReader containing the resultset generated by the command - Thrown if spName is null - Thrown if connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified IDbTransaction - using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - A valid IDbTransaction object - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - A IDataReader containing the resultset generated by the command - Thrown if spName is null - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a 1x1 resultset) against the database specified in - the connection string using the dataRow column values as the stored procedure's parameters values. - This method will assign the parameter values based on parameter order. - - The IDbCommand to execute - The dataRow used to hold the stored procedure's parameter values. - An object containing the value in the 1x1 resultset generated by the command - Thrown if command is null. - - - - Execute a stored procedure via an IDbCommand (that returns a 1x1 resultset) against the database specified in - the connection string using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - A valid connection string for an IDbConnection - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - An object containing the value in the 1x1 resultset generated by the command - Thrown if connectionString is null - Thrown if spName is null - - - - Execute a stored procedure via an IDbCommand (that returns a 1x1 resultset) against the specified IDbConnection - using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - A valid IDbConnection object - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - An object containing the value in the 1x1 resultset generated by the command - Thrown if spName is null - Thrown if connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a 1x1 resultset) against the specified IDbTransaction - using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - A valid IDbTransaction object - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - An object containing the value in the 1x1 resultset generated by the command - Thrown if spName is null - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Checks for the existence of a return value parameter in the parametervalues - - An array of objects to be assigned as the input values of the stored procedure - true if the parameterValues contains a return value parameter, false otherwise - - - - Check to see if the parameter values passed to the helper are, in fact, IDataParameters. - - Array of parameter values passed to helper - new array of IDataParameters built from parameter values - True if the parameter values are IDataParameters - - - - Retrieves the set of IDataParameters appropriate for the stored procedure - - - This method will query the database for this information, and then store it in a cache for future requests. - - A valid connection string for an IDbConnection - The name of the stored procedure - An array of IDataParameterParameters - Thrown if connectionString is null - Thrown if spName is null - - - - Retrieves the set of IDataParameters appropriate for the stored procedure - - - This method will query the database for this information, and then store it in a cache for future requests. - - A valid connection string for an IDbConnection - The name of the stored procedure - A bool value indicating whether the return value parameter should be included in the results - An array of IDataParameters - Thrown if connectionString is null - Thrown if spName is null - - - - Retrieves the set of IDataParameters appropriate for the stored procedure - - - This method will query the database for this information, and then store it in a cache for future requests. - - A valid IDataConnection object - The name of the stored procedure - An array of IDataParameters - Thrown if spName is null - Thrown if connection is null - - - - Retrieves the set of IDataParameters appropriate for the stored procedure - - - This method will query the database for this information, and then store it in a cache for future requests. - - A valid IDbConnection object - The name of the stored procedure - A bool value indicating whether the return value parameter should be included in the results - An array of IDataParameters - Thrown if spName is null - Thrown if connection is null - - - - Retrieves the set of IDataParameters appropriate for the stored procedure - - A valid IDbConnection object - The name of the stored procedure - A bool value indicating whether the return value parameter should be included in the results - An array of IDataParameters - Thrown if spName is null - Thrown if connection is null - - - - Retrieve a parameter array from the cache - - A valid connection string for an IDbConnection - The stored procedure name or SQL command - An array of IDataParameters - Thrown if connectionString is null - Thrown if commandText is null - - - - Retrieve a parameter array from the cache - - A valid IDbConnection object - The stored procedure name or SQL command - An array of IDataParameters - Thrown if commandText is null - Thrown if connection is null - - - - Retrieve a parameter array from the cache - - A valid IDbConnection object - The stored procedure name or SQL command - An array of IDataParameters - - - - Add parameter array to the cache - - A valid connection string for an IDbConnection - The stored procedure name or SQL command - An array of IDataParameters to be cached - - - - Add parameter array to the cache - - A valid IDbConnection - The stored procedure name or SQL command - An array of IDataParameters to be cached - - - - Add parameter array to the cache - - A valid IDbConnection - The stored procedure name or SQL command - An array of IDataParameters to be cached - - - - Resolve at run time the appropriate set of IDataParameters for a stored procedure - - A valid IDbConnection object - The name of the stored procedure - Whether or not to include their return value parameter - The parameter array discovered. - Thrown if spName is null - Thrown if connection is null - - - - Internal handler used for bubbling up the event to the user - - - - - Internal handler used for bubbling up the event to the user - - - - - Returns an IDbConnection object for the given connection string - - The connection string to be used to create the connection - An IDbConnection object - Thrown if connectionString is null - - - - Returns an IDbDataAdapter object - - The IDbDataAdapter - - - - Calls the CommandBuilder.DeriveParameters method for the specified provider, doing any setup and cleanup necessary - - The IDbCommand referencing the stored procedure from which the parameter information is to be derived. The derived parameters are added to the Parameters collection of the IDbCommand. - - - - Returns an IDataParameter object - - The IDataParameter object - - - - Provider specific code to set up the updating/ed event handlers used by UpdateDataset - - DataAdapter to attach the event handlers to - The handler to be called when a row is updating - The handler to be called when a row is updated - - - - Returns an array of IDataParameters of the specified size - - size of the array - The array of IDataParameters - - - - Handle any provider-specific issues with BLOBs here by "washing" the IDataParameter and returning a new one that is set up appropriately for the provider. - - The IDbConnection to use in cleansing the parameter - The parameter before cleansing - The parameter after it's been cleansed. - - - - Delegate for creating a RowUpdatedEvent handler - - The object that published the event - The RowUpdatedEventArgs for the event - - - - Delegate for creating a RowUpdatingEvent handler - - The object that published the event - The RowUpdatingEventArgs for the event - - - - This enum is used to indicate whether the connection was provided by the caller, or created by AdoHelper, so that - we can set the appropriate CommandBehavior when calling ExecuteReader() - - - - Connection is owned and managed by ADOHelper - - - Connection is owned and managed by the caller - - - - ADOHelperParameterCache provides functions to leverage a static cache of procedure parameters, and the - ability to discover parameters for stored procedures at run-time. - - - - - Deep copy of cached IDataParameter array - - - - - - - Add parameter array to the cache - - A valid connection string for an IDbConnection - The stored procedure name or SQL command - An array of IDataParameters to be cached - Thrown if commandText is null - Thrown if connectionString is null - - - - Retrieve a parameter array from the cache - - A valid connection string for an IDbConnection - The stored procedure name or SQL command - An array of IDataParameters - Thrown if commandText is null - Thrown if connectionString is null - - - - This is a DataReader that 'fixes' any null values before - they are returned to our business code. - - - - - Initializes the SafeDataReader object to use data from - the provided DataReader object. - - The source DataReader object containing the data. - - - - Gets a string value from the datareader. - - - Returns empty string for null. - - Ordinal column position of the value. - - - - Gets a value of type from the datareader. - - Ordinal column position of the value. - - - - Gets an integer from the datareader. - - - Returns 0 for null. - - Ordinal column position of the value. - - - - Gets a double from the datareader. - - - Returns 0 for null. - - Ordinal column position of the value. - - - - Gets a Guid value from the datareader. - - - Returns Guid.Empty for null. - - Ordinal column position of the value. - - - - Reads the next row of data from the datareader. - - - - - Moves to the next result set in the datareader. - - - - - Closes the datareader. - - - - - Gets a boolean value from the datareader. - - - Returns for null. - - Ordinal column position of the value. - - - - Gets a byte value from the datareader. - - - Returns 0 for null. - - Ordinal column position of the value. - - - - Invokes the GetBytes method of the underlying datareader. - - - Returns 0 for null. - - Ordinal column position of the value. - Array containing the data. - Offset position within the buffer. - Offset position within the field. - Length of data to read. - - - - Gets a char value from the datareader. - - - Returns Char.MinValue for null. - - Ordinal column position of the value. - - - - Invokes the GetChars method of the underlying datareader. - - - Returns 0 for null. - - Ordinal column position of the value. - Array containing the data. - Offset position within the buffer. - Offset position within the field. - Length of data to read. - - - - Invokes the GetData method of the underlying datareader. - - Ordinal column position of the value. - - - - Invokes the GetDataTypeName method of the underlying datareader. - - Ordinal column position of the value. - - - - Gets a date value from the datareader. - - - Returns DateTime.MinValue for null. - - Ordinal column position of the value. - - - - Gets a decimal value from the datareader. - - - Returns 0 for null. - - Ordinal column position of the value. - - - - Invokes the GetFieldType method of the underlying datareader. - - Ordinal column position of the value. - - - - Gets a Single value from the datareader. - - - Returns 0 for null. - - Ordinal column position of the value. - - - - Gets a Short value from the datareader. - - - Returns 0 for null. - - Ordinal column position of the value. - - - - Gets a Long value from the datareader. - - - Returns 0 for null. - - Ordinal column position of the value. - - - - Invokes the GetName method of the underlying datareader. - - Ordinal column position of the value. - - - - Gets an ordinal value from the datareader. - - Name of the column containing the value. - - - - Invokes the GetSchemaTable method of the underlying datareader. - - - - - Invokes the GetValues method of the underlying datareader. - - An array of System.Object to - copy the values into. - - - - Invokes the IsDBNull method of the underlying datareader. - - Ordinal column position of the value. - - - - Disposes the object. - - - - - Disposes the object. - - True if called by - the public Dispose method. - - - - Object finalizer. - - - - - Gets a string value from the datareader. - - - Returns empty string for null. - - Name of the column containing the value. - - - - Gets a value of type from the datareader. - - Name of the column containing the value. - - - - Gets an integer from the datareader. - - - Returns 0 for null. - - Name of the column containing the value. - - - - Gets a double from the datareader. - - - Returns 0 for null. - - Name of the column containing the value. - - - - Gets a Guid value from the datareader. - - - Returns Guid.Empty for null. - - Name of the column containing the value. - - - - Gets a boolean value from the datareader. - - - Returns for null. - - Name of the column containing the value. - - - - Gets a byte value from the datareader. - - - Returns 0 for null. - - Name of the column containing the value. - - - - Invokes the GetBytes method of the underlying datareader. - - - Returns 0 for null. - - Name of the column containing the value. - Array containing the data. - Offset position within the buffer. - Offset position within the field. - Length of data to read. - - - - Gets a char value from the datareader. - - - Returns Char.MinValue for null. - - Name of the column containing the value. - - - - Invokes the GetChars method of the underlying datareader. - - - Returns 0 for null. - - Name of the column containing the value. - Array containing the data. - Offset position within the buffer. - Offset position within the field. - Length of data to read. - - - - Invokes the GetData method of the underlying datareader. - - Name of the column containing the value. - - - - Invokes the GetDataTypeName method of the underlying datareader. - - Name of the column containing the value. - - - - Gets a date value from the datareader. - - - Returns DateTime.MinValue for null. - - Name of the column containing the value. - - - - Gets a decimal value from the datareader. - - - Returns 0 for null. - - Name of the column containing the value. - - - - Invokes the GetFieldType method of the underlying datareader. - - Name of the column containing the value. - - - - Gets a Single value from the datareader. - - - Returns 0 for null. - - Name of the column containing the value. - - - - Gets a Short value from the datareader. - - - Returns 0 for null. - - Name of the column containing the value. - - - - Gets a Long value from the datareader. - - - Returns 0 for null. - - Name of the column containing the value. - - - - Invokes the IsDBNull method of the underlying datareader. - - Name of the column containing the value. - - - - Get a reference to the underlying data reader - object that actually contains the data from - the data source. - - - - - Returns the depth property value from the datareader. - - - - - Returns the FieldCount property from the datareader. - - - - - Returns the IsClosed property value from the datareader. - - - - - Returns a value from the datareader. - - Name of the column containing the value. - - - - Returns a value from the datareader. - - Ordinal column position of the value. - - - - Returns the RecordsAffected property value from the underlying datareader. - - - - - Writes tracing or debugging output to a text file. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - Name of the file. - - - - Initializes a new instance of the class. - - Name of the file. - The name. - - - - When overridden in a derived class, flushes the output buffer. - - - - - Writes the specified value. - - The value. - - - - Writes the line. - - The value. - - - - When overridden in a derived class, closes the output stream so it no longer receives tracing or debugging output. - - - - - Releases the unmanaged resources used by the and optionally releases the managed resources. - - true to release both managed and unmanaged resources; false to release only unmanaged resources. - - - - Delegate of an unsubscribe delegate - - - - - A handler for an event that doesn't store a reference to the source - handler must be a instance method - - type of calling object - type of event args - type of event handler - - - - Performs an implicit conversion from to . - - The weh. - The result of the conversion. - - - - Casts the delegate. - - The source. - - - - - Gets the handler. - - - - - An interface for a weak event handler - - - - - - A handler for an event that doesn't store a reference to the source - handler must be a instance method - - - - - - - An interface for a weak event handler - - - - - - A handler for an event that doesn't store a reference to the source - handler must be a instance method - - - - - - - Utilities for the weak event method - - - - - Makes a property change handler weak - - - The event handler. - The unregister. - - - - - Makes a generic handler weak - - - The event handler. - The unregister. - - - - - http://msdn.microsoft.com/en-us/library/dd997369.aspx - http://kozmic.pl/2010/08/06/concurrentdictionary-in-net-4-not-what-you-would-expect/ - http://codereview.stackexchange.com/questions/2025/extension-methods-to-make-concurrentdictionary-getoradd-and-addorupdate-thread-sa - - - - - Adds the elements of the specified collection to the end of the list. - - The type of elements in the list. - The list to which the items should be added. - The collection whose elements should be added to the end of the list. The collection itself cannot be null, but it can contain elements that are null, if type is a reference type. - - - - Inserts the elements of a collection into the list at the specified index. - - The type of elements in the list. - The list to which the items should be inserted. - The zero-based index at which the new elements should be inserted.. - The collection whose elements should be inserted into the list. The collection itself cannot be null, but it can contain elements that are null, if type is a reference type. - - - - Removes all the elements that match the conditions defined by the specified predicate. - - The type of elements in the list. - The list from with the items should be removed. - The delegate that defines the conditions of the elements to remove. - The number of elements removed from the . - - - - Helper class to print out performance related data like number of runs, elapsed time and frequency - - - - - Execute the given function n-times and print the timing values (number of runs, elapsed time, call frequency) - to the console window. - - Function to call in a for loop. - Number of iterations. - Format string which can contain {runs} or {0},{time} or {1} and {frequency} or {2}. - - - - Execute the given function n-times and print the timing values (number of runs, elapsed time, call frequency) - to the console window. - - Function to call in a for loop. - Number of iterations. - Format string which can contain {runs} or {0},{time} or {1} and {frequency} or {2}. - - - - Call a function in a for loop n-times. The first function call will be measured independently to measure - first call effects. - - Function to call in a loop. - Number of iterations. - Format string for first function call performance. - - The format string can contain {runs} or {0},{time} or {1} and {frequency} or {2}. - - - - - Call a function in a for loop n-times. The first function call will be measured independently to measure - first call effects. - - Function to call in a loop. - Number of iterations. - Format string for first function call performance. - - The format string can contain {runs} or {0},{time} or {1} and {frequency} or {2}. - - - - - Adjust the DateTime so the time is 1 millisecond before the next day. - - The DateTime to adjust. - A DateTime that is 1 millisecond before the next day. - - - - Serializes the current DateTime object to a 64-bit binary value that subsequently can be used to recreate the DateTime object. - - The DateTime to serialize. - A 64-bit signed integer that encodes the Kind and Ticks properties. - - This method exists to add missing funtionality in Silverlight. - - - - - A class to help with Enum Flags. - - - - - Determines whether any flag is on for the specified mask. - - The flag type. - The mask to check if the flag is on. - The flag to check for in the mask. - - true if any flag is on for the specified mask; otherwise, false. - - - - - Determines whether the flag is on for the specified mask. - - The flag type. - The mask to check if the flag is on. - The flag to check for in the mask. - - true if the flag is on for the specified mask; otherwise, false. - - - - - Sets the flag on in the specified mask. - - The flag type. - The mask to set flag on. - The flag to set. - The mask with the flag set to on. - - - - Sets the flag off in the specified mask. - - The flag type. - The mask to set flag off. - The flag to set. - The mask with the flag set to off. - - - - Toggles the flag in the specified mask. - - The flag type. - The mask to toggle the flag against. - The flag to toggle. - The mask with the flag set in the opposite position then it was. - - - - Gets the string hex of the enum. - - The enum type. - The enum to get the string hex from. - - - - - Tries to get an enum from a string. - - The enum type. - The enum value. - The input string. - The return enum value. - - true if the string was able to be parsed to an enum; otherwise, false. - - - - - Will try and parse an enum and it's default type. - - - - True if the enum value is defined. - - - - Gets the default defined value of an enum. - - The enum. - If the value cannot be determined, 0 will be returned. - - - - Attempts to get the default value of an enum. - - The System Type. - - - - - - - Retrieve the description on the enum, e.g. - [Description("Bright Pink")] - BrightPink = 2, - Then when you pass in the enum, it will retrieve the description - - The Enumeration - A string representing the friendly name - - - - Gets the exception that is wrapped by an AggregateException. - - - - - - - Formats an exception with the stack trace included. - - - - - - - Hash Extension methods - - - - Compute hash on input string - The string to compute hash on. - The hash as a hexadecimal string. - - - Compute hash on input stream - The stream to compute hash on. - The hash as a hexadecimal string. - - - - Compute hash on byte array - - The byte array to get hash from. - The hash as a hexadecimal string. - - - Compute hash on input string - The file to get hash from. - The hash as a hexadecimal string. - - - Compute hash on input string - The string to compute hash on. - The hash as a hexadecimal string. - - - Compute SHA1 hash on input string - The string to compute hash on. - The hash as a hexadecimal string. - - - Compute SHA1 hash on input string - The string to compute hash on. - The hash as a hexadecimal string. - - - - Compute SHA1 hash on input string - - The buffer. - The hash as a hexadecimal string. - - - Compute SHA1 hash on input string - The file to get hash from. - The hash as a hexadecimal string. - - - Compute SHA1 hash on input string - The string to compute hash on. - The hash as a hexadecimal string. - - - Compute SHA256 hash on input string - The string to compute hash on. - The hash as a hexadecimal string. - - - Compute SHA256 hash on input string - The string to compute hash on. - The hash as a hexadecimal string. - - - - Compute SHA256 hash on input string - - The buffer. - The hash as a hexadecimal string. - - - Compute SHA256 hash on input string - The file to get hash from. - The hash as a hexadecimal string. - - - Compute SHA256 hash on input string - The string to compute hash on. - The hash as a hexadecimal string. - - - Compute SHA512 hash on input string - The string to compute hash on. - The hash as a hexadecimal string. - - - Compute SHA512 hash on input string - The string to compute hash on. - The hash as a hexadecimal string. - - - - Compute SHA512 hash on input string - - The buffer. - The hash as a hexadecimal string. - - - Compute SHA512 hash on input string - The file to get hash from. - The hash as a hexadecimal string. - - - Compute SHA512 hash on input string - The string to compute hash on. - The hash as a hexadecimal string. - - - Compute CRC32 hash on input string - The string to compute hash on. - The hash as a hexadecimal string. - - - Compute CRC32 hash on input string - The string to compute hash on. - The hash as a hexadecimal string. - - - - Compute CRC32 hash on input string - - The buffer. - The hash as a hexadecimal string. - - - Compute CRC32 hash on input string - The file to get hash from. - The hash as a hexadecimal string. - - - Compute CRC32 hash on input string - The string to compute hash on. - The hash as a hexadecimal string. - - - Compute MD5 hash on input string - The string to compute hash on. - The hash as a hexadecimal string. - - - Compute MD5 hash on input string - The string to compute hash on. - The hash as a hexadecimal string. - - - - Compute MD5 hash on input string - - The buffer. - The hash as a hexadecimal string. - - - Compute MD5 hash on input string - The file to get hash from. - The hash as a hexadecimal string. - - - Compute MD5 hash on input string - The string to compute hash on. - The hash as a hexadecimal string. - - - - Converts a byte array to Hexadecimal. - - The bytes to convert. - Hexadecimal string of the byte array. - - - - Converts a hexadecimal string into a byte array. - - The hex string. - A byte array. - - - - Sets the property value if the value is different fromt he existing value. - The PropertyChanging and PropertyChanged events are raised if the value is updated. - - The type of the property - The handler for the PropertyChanged event. - The handler for the PropertyChanging event. - The new value. - The old value expression. - The setter delegate. - The new value. - The following is an example of a Name property. - - Name, v => _name = v); } - } - ]]> - - - - - - Sets the property value if the value is different fromt he existing value. - The PropertyChanging and PropertyChanged events are raised if the value is updated. - - The type of the property - The handler for the PropertyChanged event. - The handler for the PropertyChanging event. - The new value. - The old value expression. - The setter delegate. - The new value. - - - - Truncates the specified text. - - The text to truncate. - The number of characters to keep. - A truncate string. - - - - Calculates a hashcode for the string that is guaranteed to be stable across .NET versions. - - The string to hash. - The hashcode - - - - Indicates whether the specified String object is null or an empty string - - A String reference - - true if is null or empty; otherwise, false. - - - - - Indicates whether a specified string is null, empty, or consists only of white-space characters - - A String reference - - true if is null or empty; otherwise, false. - - - - - Determines if the string looks like JSON content. - - - - - - - Formats a string without throwing a FormatException. - - A String reference - Object parameters that should be formatted - Formatted string if no error is thrown, else reutrns the format parameter. - - - - Uses the string as a format - - A String reference - Object parameters that should be formatted - Formatted string - - - - Applies a format to the item - - Item to format - Format string - Formatted string - - - - Uses the string as a format. - - A String reference - Object that should be formatted - Formatted string - - - - Applies a format to the item - - Item to format - Format string - Formatted string - - - - Converts an IEnumerable of values to a delimited string. - - - The type of objects to delimit. - - - The IEnumerable string values to convert. - - - The delimiter. - - - A delimited string of the values. - - - - - Converts an IEnumerable of values to a delimited string. - - The IEnumerable string values to convert. - A delimited string of the values. - - - - Converts an IEnumerable of values to a delimited string. - - The IEnumerable string values to convert. - The delimiter. - A delimited string of the values. - - - - Converts a string to use camelCase. - - The value. - The to camel case. - - - - Converts a string to use PascalCase. - - Text to convert - The string - - - - Converts a string to use PascalCase. - - Text to convert - The string - - - - Converts a string to an C# escaped literal string. - - Text to escape - The escaped string - - - - Converts a string to a valid C# identifier string. - - Text to convert. - The valid identifier - - - - Converts a string to an VB escaped literal string. - - Text to escape - The escaped string - - - - Converts a string to a valid C# identifier string. - - Text to convert. - The valid identifier - - - - Converts a string to a valid .NET identifier string. - - Text to convert. - The valid identifier - - - - Checks to see if a string is a valid .NET identifier string. - - String identifier to check. - Returns true if value is a valid identifier - - - - Checks to see if a string is a valid .NET namespace. - - String identifier to check. - Returns true if value is a valid namespace. - - - - Replicates the given string. - - Text to replicate - Times to replicate - The replicated string - - - - Converts a string to use PascalCase. - - Text to convert - Regular Expression to split words on. - The string - - - - Takes a NameIdentifier and spaces it out into words "Name Identifier". - - The value to convert. - The string - - - - Takes a NameIdentifier and spaces it out into words "Name Identifier". - - The value to convert. - The string - - - - Removes all whitespace from a string. - - Initial string. - String with no whitespace. - - - - Returns a copy of this string converted to HTML markup. - - - - - Returns a copy of this string converted to HTML markup. - - If specified, links will have the rel attribute set to this value - attribute - - - - Encodes a single paragraph to HTML. - - Text to encode - StringBuilder to write results - If specified, links will have the rel attribute set to this value - attribute - - - - Encodes [[URL]] and [[Text][URL]] links to HTML. - - Text to encode - StringBuilder to write results - If specified, links will have the rel attribute set to this value - attribute - - - - Strips NewLines and Tabs - - The string to strip. - Stripped string. - - - - Returns true if s contains substring value. - - Initial value - Substring value - Boolean - - - - Returns true if s contains substring value. - - Initial value - Substring value - StringComparison options. - Boolean - - - - Indicates whether a string contains x occurrences of a string. - - The string to search. - The string to search for. - - true if the string contains at least two occurrences of {value}; otherwise, false. - - - - - Indicates whether a string contains x occurrences of a string. - - The string to search. - The string to search for. - The number of occurrences to search for. - - true if the string contains at least {count} occurrences of {value}; otherwise, false. - - - - - Convert UTF8 string to ASCII. - - The UTF8 string. - The ASCII string. - - - - Do any of the strings contain both uppercase and lowercase characters? - - String values. - True if any contain mixed cases. - - - - Is the string all lower case characters? - - The value. - True if all lower case. - - - - Is the string all upper case characters? - - The value. - True if all upper case. - - - - Does string contain uppercase characters? - - The value. - True if contain upper case. - - - - Does string contain lowercase characters? - - The value. - True if contain lower case. - - - - Does string contain both uppercase and lowercase characters? - - The value. - True if contain mixed case. - - - - Parses a person's full name from a single string. - - The person's full name. - - - - Writes the object to the Debug console, prefixed with an identifier for debugView filtering - - Object to write - - - - Writes the object on a new line to the Debug console, prefixed with an identifier for debugView filtering. - - Object to write - - - - Implements a for writing information to the debugger log. - - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class with the specified level and category. - - A description of the importance of the messages. - The category of the messages. - - - - Initializes a new instance of the class with the specified level, category and format provider. - - A description of the importance of the messages. - The category of the messages. - An object that controls formatting. - - - - A class with file helper methods - - - - - Gets the content type based on file extension . - - The file extension. - The content type. - - - - Captures the Desktop in a screenshot. - - Screenshot of the Desktop. - - - - Captures the Foreground window in a screenshot. - - Screenshot of the current Foreground window. - - - - Captures a screenshot of the window associated with the handle argument. - - Used to determine which window to provide a screenshot for. - Screenshot of the window corresponding to the handle argument. - - - - Saves the encoded image to a file - - The image to save - The quality desired (a value between 1 and 100). - The to save the image as. - The file path to save the image to. - - - - Saves the encoded image to a stream. - - The image to save - The quality desired (a value between 1 and 100). - The to save the image as. - - - - - Ensures that the derived classes always have a string parameter to pass in a path. - - - - - - Acquires a lock while waiting with the default timeout value. - - - - - Acquires a lock in a specific amount of time. - - The time to wait for when trying to acquire a lock. - - - - Acquires a lock while waiting with the default timeout value. - - The path to acquire a lock on. - A lock instance. - - - - Acquires a lock in a specific amount of time. - - The path to acquire a lock on. - The time to wait for when trying to acquire a lock. - A lock instance. - - - - Creates a lock file. - - The place to create the lock file. - The amount of time to wait before a TimeoutException is thrown. - - - - Releases the lock. - - - - - Releases the lock. - - If set to true, the lock will be removed forcefully. - - - - Releases the lock. - - The path to the lock file. - - - - Releases the lock. - - - - - The default time to wait when trying to acquire a lock. - - - - - The name of the lock file. - - - - - Acquires a lock in a specific amount of time. - - The time to wait for when trying to acquire a lock. - - - - Releases the lock. - - If set to true, the lock will be removed forcefully. - - - - The directory that is being locked. - - The directory. - - - - Initializes a new instance of the class. - - The file. - - - - Acquires a lock in a specific amount of time. - - The time to wait for when trying to acquire a lock. - - - - Releases the lock. - - If set to true, the lock will be removed forcefully. - - - - The file that is being locked. - - - - - A class with various path helper methods - - - - - Creates a unique filename based on an existing filename - - A string containing the fully qualified path that will contain the new file - A string that contains the fully qualified path of the unique file name - - - - Removes illegal characters from a file path - - The file path - - A string that contains the cleaned file path - - - - - Removes illegal characters from a file name - - The file name - The maximum length for the returned file name - - A string that contains the cleaned file name - - - - - Creates a relative path from one file or folder to another. - - Contains the directory that defines the start of the relative path. - Contains the path that defines the endpoint of the relative path. - The relative path from the start directory to the end path. - - - - - Expand the filename of the data source, resolving the |DataDirectory| macro as appropriate. - - The database filename to expand - The expanded path and filename of the filename - - - - Gets the data directory for the |DataDirectory| macro. - - The DataDirectory path. - - - - Creates the directory if it does not exist. - - The directory path to create. - - - - Deletes the directory, subdirectories, and files in path if it exists. - - The directory path to delete. - - - - Implements a TextWriter with Encoding for writing information to a string. The information is stored in an underlying StringBuilder. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - An object that controls formatting. - - - - Initializes a new instance of the class. - - The StringBuilder to write to. - - - - Initializes a new instance of the class. - - The StringBuilder to write to. - An object that controls formatting. - - is null. - - - - - Initializes a new instance of the class. - - The in which the output is written. - - - - Initializes a new instance of the class. - - The in which the output is written. - An object that controls formatting. - - - - Initializes a new instance of the class. - - The in which the output is written. - The StringBuilder to write to. - - - - Initializes a new instance of the class. - - The in which the output is written. - The StringBuilder to write to. - An object that controls formatting. - - - - Gets the in which the output is written. - - - - The Encoding in which the output is written. - - - - Base stream class that implements all of Stream's abstract members. - - - Flushes the contents of the stream to the underlying storage. - - - - Reads a sequence of bytes from the current - stream and advances the position within the stream by the number of bytes read. - - - An array of bytes. When Read returns, the buffer contains the specified - byte array with the values between offset and (offset + count - 1) replaced - by the bytes read from the current source. - - - The zero-based byte offset in buffer at which to begin storing the data read - from the current stream. - - The maximum number of bytes to be read from the current stream. - - The total number of bytes read into the buffer. This can be less than the - number of bytes requested if that many bytes are not currently available, - or zero (0) if the end of the stream has been reached. - - - - Sets the position within the current stream. - A byte offset relative to the origin parameter. - - A value of type System.IO.SeekOrigin indicating the reference point used - to obtain the new position. - - The new position within the current stream. - - - Sets the length of the current stream. - The desired length of the current stream in bytes. - - - Writes a sequence of bytes to the stream. - An array of bytes. Write copies count bytes from buffer to the stream. - The zero-based byte offset in buffer at which to begin copying bytes to the stream. - The number of bytes to be written to the current stream. - - - Determines whether data can be read from the stream. - - - Determines whether data can be written to the stream. - - - Determines whether the stream can be seeked. - - - Gets the length of the stream. - - - Gets or sets the current position of the stream. - - - Runs an action when the CountdownEvent reaches zero. - - - Initializes the ActionCountdownEvent. - The number of signals required to set the CountdownEvent. - The delegate to be invoked when the count reaches zero. - - - Increments the current count by one. - - - Registers a signal with the event, decrementing its count. - - - Releases all resources used by the current instance. - - - Releases all resources used by the current instance. - - true if called because the object is being disposed; otherwise, false. - - - - Provides an asynchronous barrier. - - - The number of participants in the barrier. - - - The task used to signal completion of the current round. - - - The number of participants remaining to arrive for this round. - - - Initializes the BarrierAsync with the specified number of participants. - The number of participants in the barrier. - - - Signals that a participant has arrived. - A Task that will be signaled when the current round completes. - - - Gets the participant count. - - - Gets the number of participants still not yet arrived in this round. - - - Debugger type proxy for AsyncCache. - Specifies the type of the cache's keys. - Specifies the type of the cache's values. - - - Caches asynchronously retrieved data. - Specifies the type of the cache's keys. - Specifies the type of the cache's values. - - - The factory to use to create tasks. - - - The dictionary to store all of the tasks. - - - Initializes the cache. - A factory for producing the cache's values. - - - Gets a Task to retrieve the value for the specified key. - The key whose value should be retrieved. - A Task for the value of the specified key. - - - Sets the value for the specified key. - The key whose value should be set. - The value to which the key should be set. - - - Sets the value for the specified key. - The key whose value should be set. - The value to which the key should be set. - - - Empties the cache. - - - Gets an enumerator for the contents of the cache. - An enumerator for the contents of the cache. - - - Gets an enumerator for the contents of the cache. - An enumerator for the contents of the cache. - - - Adds or overwrites the specified entry in the cache. - The item to be added. - - - Determines whether the cache contains the specified key. - The item contained the key to be searched for. - True if the cache contains the key; otherwise, false. - - - - Copies the elements of the System.Collections.Generic.ICollection{T} to an - System.Array, starting at a particular System.Array index. - - - The one-dimensional System.Array that is the destination of the elements - copied from System.Collections.Generic.ICollection{T}. The System.Array must - have zero-based indexing. - - The zero-based index in array at which copying begins. - - - Removes the specified key from the cache. - The item containing the key to be removed. - True if the item could be removed; otherwise, false. - - - Gets a Task to retrieve the value for the specified key. - The key whose value should be retrieved. - A Task for the value of the specified key. - - - Gets the number of items in the cache. - - - Gets whether the cache is read-only. - - - An asynchronous cache for downloaded HTML. - - - Initializes the HtmlCache. - - - Asynchronously invokes a handler for every posted item. - Specifies the type of data processed by the instance. - - - - A queue that stores the posted data. Also serves as the syncObj for protected instance state. - A ConcurrentQueue is used to enable lock-free dequeues while running with a single consumer task. - - - - The delegate to invoke for every element. - - - The maximum number of items that should be processed by an individual task. - - - The TaskFactory to use to launch new tasks. - - - The options to use for parallel processing of data. - - - Whether a processing task has been scheduled. - - - Initializes the AsyncCall with an action to execute for each element. - The action to run for every posted item. - The maximum degree of parallelism to use. If not specified, 1 is used for serial execution. - The scheduler to use. If null, the default scheduler is used. - The maximum number of items to be processed per task. If not specified, Int32.MaxValue is used. - - - - Initializes the AsyncCall with a function to execute for each element. The function returns an Task - that represents the asynchronous completion of that element's processing. - - The function to run for every posted item. - The maximum degree of parallelism to use. If not specified, 1 is used for serial execution. - The scheduler to use. If null, the default scheduler is used. - - - General initialization of the AsyncCall. Another constructor must initialize the delegate. - The maximum degree of parallelism to use. If not specified, 1 is used for serial execution. - The maximum number of items to be processed per task. If not specified, Int32.MaxValue is used. - The scheduler to use. If null, the default scheduler is used. - - - Post an item for processing. - The item to be processed. - - - Gets an enumerable that yields the items to be processed at this time. - An enumerable of items. - - - Used as the body of an action task to process items in the queue. - - - Used as the body of a function task to process items in the queue. - - - Provides static factory methods for creating AsyncCall(Of T) instances. - - - Initializes the AsyncCall with an action to execute for each element. - The action to run for every posted item. - The maximum degree of parallelism to use. If not specified, 1 is used for serial execution. - The scheduler to use. If null, the default scheduler is used. - The maximum number of items to be processed per task. If not specified, Int32.MaxValue is used. - - - - Initializes the AsyncCall with a function to execute for each element. The function returns an Task - that represents the asynchronous completion of that element's processing. - - The function to run for every posted item. - The maximum degree of parallelism to use. If not specified, 1 is used for serial execution. - The scheduler to use. If null, the default scheduler is used. - - - Initializes the AsyncCall in the specified AppDomain with an action to execute for each element. - - The action to run for every posted item. - The maximum degree of parallelism to use. If not specified, 1 is used for serial execution. - The maximum number of items to be processed per task. If not specified, Int32.MaxValue is used. - - - - Initializes the AsyncCall in the specified AppDomain with a function to execute for each element. - The function returns an Task that represents the asynchronous completion of that element's processing. - - - The action to run for every posted item. - The maximum degree of parallelism to use. If not specified, 1 is used for serial execution. - - - Provides an asynchronous producer/consumer collection. - - - Asynchronous semaphore used to keep track of asynchronous work. - - - The data stored in the collection. - - - Initializes the asynchronous producer/consumer collection to store data in a first-in-first-out (FIFO) order. - - - Initializes the asynchronous producer/consumer collection. - The underlying collection to use to store data. - - - Adds an element to the collection. - The item to be added. - - - Takes an element from the collection asynchronously. - A Task that represents the element removed from the collection. - - - Disposes of the collection. - - - Gets the number of elements in the collection. - - - Provides for asynchronous exclusive and concurrent execution support. - - - The lock that protects all shared state in this instance. - - - The queue of concurrent readers waiting to execute. - - - The queue of exclusive writers waiting to execute. - - - The number of concurrent readers currently executing. - - - The number of exclusive writers currently executing. - - - The non-generic factory to use for task creation. - - - Initializes the ReaderWriterAsync. - - - Initializes the ReaderWriterAsync with the specified TaskFactory for us in creating all tasks. - The TaskFactory to use to create all tasks. - - - Queues an exclusive writer action to the ReaderWriterAsync. - The action to be executed exclusively. - A Task that represents the execution of the provided action. - - - Queues an exclusive writer function to the ReaderWriterAsync. - The function to be executed exclusively. - A Task that represents the execution of the provided function. - - - Queues a concurrent reader action to the ReaderWriterAsync. - The action to be executed concurrently. - A Task that represents the execution of the provided action. - - - Queues a concurrent reader function to the ReaderWriterAsync. - The function to be executed concurrently. - A Task that represents the execution of the provided function. - - - Starts the specified exclusive task. - The exclusive task to be started. - This must only be executed while holding the instance's lock. - - - Starts the specified concurrent task. - The exclusive task to be started. - This must only be executed while holding the instance's lock. - - - Starts all queued concurrent tasks. - This must only be executed while holding the instance's lock. - - - Completes the processing of a concurrent reader. - - - Completes the processing of an exclusive writer. - - - Gets the number of exclusive operations currently queued. - - - Gets the number of concurrent operations currently queued. - - - Gets the number of concurrent operations currently executing. - - - Gets whether an exclusive operation is currently executing. - - - Provides an asynchronous semaphore. - - - The current count. - - - The maximum count. If _maxCount isn't positive, the instance has been disposed. - - - Tasks waiting to be completed when the semaphore has count available. - - - Initializes the SemaphoreAsync with a count of zero and a maximum count of Int32.MaxValue. - - - Initializes the SemaphoreAsync with the specified count and a maximum count of Int32.MaxValue. - The initial count to use as the current count. - - - Initializes the SemaphoreAsync with the specified counts. - The initial count to use as the current count. - The maximum count allowed. - - - Waits for a unit to be available in the semaphore. - A Task that will be completed when a unit is available and this Wait operation succeeds. - - - - Queues an action that will be executed when space is available - in the semaphore. - - The action to be executed. - - A Task that represents the execution of the action. - - - Release does not need to be called for this action, as it will be handled implicitly - by the Queue method. - - - - - Queues a function that will be executed when space is available - in the semaphore. - - The function to be executed. - - A Task that represents the execution of the function. - - - Release does not need to be called for this function, as it will be handled implicitly - by the Queue method. - - - - Releases a unit of work to the semaphore. - - - Releases the resources used by the semaphore. - - - Gets the current count. - - - Gets the maximum count. - - - Gets the number of operations currently waiting on the semaphore. - - - Provides a thread-safe priority queue data structure. - Specifies the type of keys used to prioritize values. - Specifies the type of elements in the queue. - - - Initializes a new instance of the ConcurrentPriorityQueue class. - - - Initializes a new instance of the ConcurrentPriorityQueue class that contains elements copied from the specified collection. - The collection whose elements are copied to the new ConcurrentPriorityQueue. - - - Adds the key/value pair to the priority queue. - The priority of the item to be added. - The item to be added. - - - Adds the key/value pair to the priority queue. - The key/value pair to be added to the queue. - - - Attempts to remove and return the next prioritized item in the queue. - - When this method returns, if the operation was successful, result contains the object removed. If - no object was available to be removed, the value is unspecified. - - - true if an element was removed and returned from the queue succesfully; otherwise, false. - - - - Attempts to return the next prioritized item in the queue. - - When this method returns, if the operation was successful, result contains the object. - The queue was not modified by the operation. - - - true if an element was returned from the queue succesfully; otherwise, false. - - - - Empties the queue. - - - Copies the elements of the collection to an array, starting at a particular array index. - - The one-dimensional array that is the destination of the elements copied from the queue. - - - The zero-based index in array at which copying begins. - - The elements will not be copied to the array in any guaranteed order. - - - Copies the elements stored in the queue to a new array. - A new array containing a snapshot of elements copied from the queue. - - - Attempts to add an item in the queue. - The key/value pair to be added. - - true if the pair was added; otherwise, false. - - - - Attempts to remove and return the next prioritized item in the queue. - - When this method returns, if the operation was successful, result contains the object removed. If - no object was available to be removed, the value is unspecified. - - - true if an element was removed and returned from the queue succesfully; otherwise, false. - - - - Returns an enumerator that iterates through the collection. - An enumerator for the contents of the queue. - - The enumeration represents a moment-in-time snapshot of the contents of the queue. It does not - reflect any updates to the collection after GetEnumerator was called. The enumerator is safe to - use concurrently with reads from and writes to the queue. - - - - Returns an enumerator that iterates through a collection. - An IEnumerator that can be used to iterate through the collection. - - - Copies the elements of the collection to an array, starting at a particular array index. - - The one-dimensional array that is the destination of the elements copied from the queue. - - - The zero-based index in array at which copying begins. - - - - Gets whether the queue is empty. - - - Gets the number of elements contained in the queue. - - - - Gets a value indicating whether access to the ICollection is synchronized with the SyncRoot. - - - - - Gets an object that can be used to synchronize access to the collection. - - - - Implements a binary heap that prioritizes smaller values. - - - Initializes an empty heap. - - - Initializes a heap as a copy of another heap instance. - The heap to copy. - Key/Value values are not deep cloned. - - - Empties the heap. - - - Adds an item to the heap. - - - Adds an item to the heap. - - - Returns the entry at the top of the heap. - - - Removes the entry at the top of the heap. - - - Gets the number of objects stored in the heap. - - - Provides a thread-safe object pool. - Specifies the type of the elements stored in the pool. - - - - Provides a base implementation for producer-consumer collections that wrap other - producer-consumer collections. - - Specifies the type of elements in the collection. - - - Initializes the ProducerConsumerCollectionBase instance. - The collection to be wrapped by this instance. - - - Attempts to add the specified value to the end of the deque. - The item to add. - true if the item could be added; otherwise, false. - - - Attempts to remove and return an item from the collection. - - When this method returns, if the operation was successful, item contains the item removed. If - no item was available to be removed, the value is unspecified. - - - true if an element was removed and returned from the collection; otherwise, false. - - - - Attempts to add the specified value to the end of the deque. - The item to add. - true if the item could be added; otherwise, false. - - - Attempts to remove and return an item from the collection. - - When this method returns, if the operation was successful, item contains the item removed. If - no item was available to be removed, the value is unspecified. - - - true if an element was removed and returned from the collection; otherwise, false. - - - - Creates an array containing the contents of the collection. - The array. - - - Copies the contents of the collection to an array. - The array to which the data should be copied. - The starting index at which data should be copied. - - - Copies the contents of the collection to an array. - The array to which the data should be copied. - The starting index at which data should be copied. - - - Gets an enumerator for the collection. - An enumerator. - - - Gets an enumerator for the collection. - An enumerator. - - - Gets the contained collection. - - - Gets the number of elements contained in the collection. - - - Gets whether the collection is synchronized. - - - Gets the synchronization root object for the collection. - - - Initializes an instance of the ObjectPool class. - The function used to create items when no items exist in the pool. - - - Initializes an instance of the ObjectPool class. - The function used to create items when no items exist in the pool. - The collection used to store the elements of the pool. - - - Adds the provided item into the pool. - The item to be added. - - - Gets an item from the pool. - The removed or created item. - If the pool is empty, a new item will be created and returned. - - - Clears the object pool, returning all of the data that was in the pool. - An array containing all of the elements in the pool. - - - - Provides a thread-safe, concurrent collection for use with data binding. - - Specifies the type of the elements in this collection. - - - - Initializes an instance of the ObservableConcurrentCollection class with an underlying - queue data structure. - - - - - Initializes an instance of the ObservableConcurrentCollection class with the specified - collection as the underlying data structure. - - - - - Notifies observers of CollectionChanged or PropertyChanged of an update to the dictionary. - - - - Event raised when the collection changes. - - - Event raised when a property on the collection changes. - - - - Provides a thread-safe dictionary for use with data binding. - - Specifies the type of the keys in this collection. - Specifies the type of the values in this collection. - - - - Initializes an instance of the ObservableConcurrentDictionary class. - - - - - Initializes an instance of the ObservableConcurrentDictionary class. - - - - - Notifies observers of CollectionChanged or PropertyChanged of an update to the dictionary. - - - - Attempts to add an item to the dictionary, notifying observers of any changes. - The item to be added. - Whether the add was successful. - - - Attempts to add an item to the dictionary, notifying observers of any changes. - The key of the item to be added. - The value of the item to be added. - Whether the add was successful. - - - Attempts to remove an item from the dictionary, notifying observers of any changes. - The key of the item to be removed. - The value of the item removed. - Whether the removal was successful. - - - Attempts to add or update an item in the dictionary, notifying observers of any changes. - The key of the item to be updated. - The new value to set for the item. - Whether the update was successful. - - - Event raised when the collection changes. - - - Event raised when a property on the collection changes. - - - Provides support for pipelined data processing. - - - Creates a new pipeline, with the specified function as the sole stage. - Specifies the type of the input data to the pipeline. - Specifies the type of the output data from this stage of the pipeline. - The function used to process input data into output data. - A pipeline for converting from input data to output data. - - - Creates a new pipeline, with the specified function as the sole stage. - Specifies the type of the input data to the pipeline. - Specifies the type of the output data from this stage of the pipeline. - The function used to process input data into output data. - The concurrency level for this stage of the pipeline. - A pipeline for converting from input data to output data. - - - Provides support for pipelined data processing. - Specifies the type of the input data to the pipeline. - Specifies the type of the output data from this stage of the pipeline. - - - Creates a new pipeline that combines the current pipeline with a new stage. - Specifies the new output type of the pipeline. - - The function used to convert the output of the current pipeline into the new - output of the new pipeline. - - A new pipeline that combines the current pipeline with the new stage. - This overload creates a parallel pipeline stage. - - - Creates a new pipeline that combines the current pipeline with a new stage. - Specifies the new output type of the pipeline. - - The function used to convert the output of the current pipeline into the new - output of the new pipeline. - - The concurrency level for this stage of the pipeline. - A new pipeline that combines the current pipeline with the new stage. - - - Runs the pipeline and returns an enumerable over the results. - The source data to be processed by the pipeline. - An enumerable of the results of the pipeline. - - - Runs the pipeline and returns an enumerable over the results. - The source data to be processed by the pipeline. - The cancellation token used to signal cancellation of the pipelining. - An enumerable of the results of the pipeline. - - - Runs the pipeline and returns an enumerable over the results. - The source data to be processed by the pipeline. - The cancellation token used to signal cancellation of the pipelining. - An enumerable of the results of the pipeline. - - - Implements the core processing for a pipeline stage. - The source data to be processed by the pipeline. - The cancellation token used to signal cancellation of the pipelining. - The collection into which to put the output. - - - Helper used to add a new stage to a pipeline. - Specifies the type of the output for the new pipeline. - - - Implements the core processing for a pipeline stage. - The source data to be processed by the pipeline. - The cancellation token used to signal cancellation of the pipelining. - The collection into which to put the output. - - - Debug view for the IProducerConsumerCollection. - Specifies the type of the data being aggregated. - - - Provides a reduction variable for aggregating data across multiple threads involved in a computation. - Specifies the type of the data being aggregated. - - - The factory used to initialize a value on a thread. - - - Thread-local storage for each thread's value. - - - The list of all thread-local values for later enumeration. - - - Initializes the instances. - - - Initializes the instances. - - The function invoked to provide the initial value for a thread. - If null, the default value of T will be used as the seed. - - - - Creates a value for the current thread and stores it in the central list of values. - The boxed value. - - - Applies an accumulator function over the values in this variable. - An accumulator function to be invoked on each value. - The accumulated value. - - - - Applies an accumulator function over the values in this variable. - The specified seed is used as the initial accumulator value. - - - An accumulator function to be invoked on each value. - The accumulated value. - - - Gets or sets the value for the current thread. - - - Gets the values for all of the threads that have used this instance. - - - Debug view for the reductino variable - Specifies the type of the data being aggregated. - - - Represents a queue of tasks to be started and executed serially. - - - The ordered queue of tasks to be executed. Also serves as a lock protecting all shared state. - - - The task currently executing, or null if there is none. - - - Enqueues the task to be processed serially and in order. - The function that generates a non-started task. - - - Enqueues the non-started task to be processed serially and in order. - The task. - - - Gets a Task that represents the completion of all previously queued tasks. - - - Enqueues the task to be processed serially and in order. - The task or functino that generates a task. - The task must not be started and must only be started by this instance. - - - Called when a Task completes to potentially start the next in the queue. - The task that completed. - - - Starts the provided task (or function that returns a task). - The next task or function that returns a task. - - - Provides a simple, reference type wrapper for SpinLock. - - - Initializes an instance of the SpinLockClass class. - - - Initializes an instance of the SpinLockClass class. - - Controls whether the SpinLockClass should track - thread-ownership fo the lock. - - - - Runs the specified delegate under the lock. - The delegate to be executed while holding the lock. - - - Enters the lock. - - Upon exit of the Enter method, specifies whether the lock was acquired. - The variable passed by reference must be initialized to false. - - - - Exits the SpinLock. - - - Exits the SpinLock. - - A Boolean value that indicates whether a memory fence should be issued in - order to immediately publish the exit operation to other threads. - - - - - Represents a thread-safe, pseudo-random number generator. - - - - Seed provider. - - - The underlyin provider of randomness, one instance per thread, initialized with _global. - - - Returns a nonnegative random number. - A 32-bit signed integer greater than or equal to zero and less than MaxValue. - - - Returns a nonnegative random number less than the specified maximum. - - The exclusive upper bound of the random number to be generated. maxValue must be greater than or equal to zero. - - - A 32-bit signed integer greater than or equal to zero, and less than maxValue; - that is, the range of return values ordinarily includes zero but not maxValue. However, - if maxValue equals zero, maxValue is returned. - - - - Returns a random number within a specified range. - The inclusive lower bound of the random number returned. - The exclusive upper bound of the random number returned. maxValue must be greater than or equal to minValue. - - A 32-bit signed integer greater than or equal to minValue and less than maxValue; - that is, the range of return values includes minValue but not maxValue. - If minValue equals maxValue, minValue is returned. - - - - Returns a random number between 0.0 and 1.0. - A double-precision floating point number greater than or equal to 0.0, and less than 1.0. - - - Fills the elements of a specified array of bytes with random numbers. - An array of bytes to contain random numbers. - - - Writeable stream for using a separate thread in a producer/consumer scenario. - - - The underlying stream to target. - - - The collection of chunks to be written. - - - The Task to use for background writing. - - - Initializes a new instance of the TransferStream. - The underlying stream to which to write. - - - Writes a sequence of bytes to the stream. - An array of bytes. Write copies count bytes from buffer to the stream. - The zero-based byte offset in buffer at which to begin copying bytes to the stream. - The number of bytes to be written to the current stream. - - - Closes the stream and releases all resources associated with it. - - - Determines whether data can be written to the stream. - - - Extension methods for AggregateException. - - - Invokes a handler on each Exception contained by this AggregateException. - The AggregateException. - - The predicate to execute for each exception. The predicate accepts as an argument the Exception - to be processed and returns a Boolean to indicate whether the exception was handled. - - - Whether the rethrown AggregateException should maintain the same hierarchy as the original. - - - - Provides asynchronous counterparts to members of the File class. - - - Opens an existing file for asynchronous reading. - The path to the file to be opened for reading. - A read-only FileStream on the specified path. - - - Opens an existing file for asynchronous writing. - The path to the file to be opened for writing. - An unshared FileStream on the specified path with access for writing. - - - - Opens a binary file for asynchronosu operation, reads the contents of the file into a byte array, and then closes the file. - - The path to the file to be read. - A task that will contain the contents of the file. - - - - Opens a binary file for asynchronous operation, writes the contents of the byte array into the file, and then closes the file. - - The path to the file to be written. - - A task that will signal the completion of the operation. - - - - Opens a text file for asynchronosu operation, reads the contents of the file into a string, and then closes the file. - - The path to the file to be read. - A task that will contain the contents of the file. - - - - Opens a text file for asynchronous operation, writes a string into the file, and then closes the file. - - The path to the file to be written. - - A task that will signal the completion of the operation. - - - Extension methods for asynchronously working with streams. - - - Read from a stream asynchronously. - The stream. - An array of bytes to be filled by the read operation. - The offset at which data should be stored. - The number of bytes to be read. - A Task containing the number of bytes read. - - - Write to a stream asynchronously. - The stream. - An array of bytes to be written. - The offset from which data should be read to be written. - The number of bytes to be written. - A Task representing the completion of the asynchronous operation. - - - Reads the contents of the stream asynchronously. - The stream. - A Task representing the contents of the file in bytes. - - - Read the content of the stream, yielding its data in buffers to the provided delegate. - The stream. - The size of the buffers to use. - The delegate to be called when a new buffer is available. - A Task that represents the completion of the asynchronous operation. - - - - Creates an enumerable to be used with TaskFactoryExtensions.Iterate that reads data - from an input stream and passes it to a user-provided delegate. - - The source stream. - The size of the buffers to be used. - - A delegate to be invoked when a buffer is available (provided the - buffer and the number of bytes in the buffer starting at offset 0. - - An enumerable containing yielded tasks from the operation. - - - Copies the contents of a stream to a file, asynchronously. - The source stream. - The path to the destination file. - A Task that represents the asynchronous operation. - - - Copies the contents of one stream to another, asynchronously. - The source stream. - The destination stream. - A Task that represents the completion of the asynchronous operation. - - - - Creates an enumerable to be used with TaskFactoryExtensions.Iterate that copies data from one - stream to another. - - The input stream. - The output stream. - An enumerable containing yielded tasks from the copy operation. - - - Extension methods for working with WebRequest asynchronously. - - - Creates a Task that represents an asynchronous request to GetResponse. - The WebRequest. - A Task containing the retrieved WebResponse. - - - Creates a Task that represents an asynchronous request to GetRequestStream. - The WebRequest. - A Task containing the retrieved Stream. - - - Creates a Task that respresents downloading all of the data from a WebRequest. - The WebRequest. - A Task containing the downloaded content. - - - Extension methods for BlockingCollection. - - - - Gets a partitioner for a BlockingCollection that consumes and yields the contents of the BlockingCollection. - Specifies the type of data in the collection. - The collection for which to create a partitioner. - A partitioner that completely consumes and enumerates the contents of the collection. - - Using this partitioner with a Parallel.ForEach loop or with PLINQ eliminates the need for those - constructs to do any additional locking. The only synchronization in place is that used by the - BlockingCollection internally. - - - - Adds the contents of an enumerable to the BlockingCollection. - Specifies the type of the elements in the collection. - The target BlockingCollection to be augmented. - The source enumerable containing the data to be added. - - Whether to mark the target BlockingCollection as complete for adding when - all elements of the source enumerable have been transfered. - - - - Adds the contents of an observable to the BlockingCollection. - Specifies the type of the elements in the collection. - The target BlockingCollection to be augmented. - The source observable containing the data to be added. - - Whether to mark the target BlockingCollection as complete for adding when - all elements of the source observable have been transfered. - - An IDisposable that may be used to cancel the transfer. - - - Creates an IProducerConsumerCollection-facade for a BlockingCollection instance. - Specifies the type of the elements in the collection. - The BlockingCollection. - - An IProducerConsumerCollection that wraps the provided BlockingCollection. - - - - Creates an IProducerConsumerCollection-facade for a BlockingCollection instance. - Specifies the type of the elements in the collection. - The BlockingCollection. - -1 for infinite blocking add and take operations. 0 for non-blocking, 1 or greater for blocking with timeout. - An IProducerConsumerCollection that wraps the provided BlockingCollection. - - - Creates an IProducerConsumerCollection-facade for a BlockingCollection instance. - Specifies the type of the elements in the collection. - The BlockingCollection. - -1 for infinite blocking add and take operations. 0 for non-blocking, 1 or greater for blocking with timeout. - The CancellationToken to use for any blocking operations. - An IProducerConsumerCollection that wraps the provided BlockingCollection. - - - Provides a partitioner that consumes a blocking collection and yields its contents. - Specifies the type of data in the collection. - - - The target collection. - - - Initializes the partitioner. - The collection to be enumerated and consumed. - - - Partitions the underlying collection into the given number of partitions. - The number of partitions to create. - A list containing partitionCount enumerators. - - - - Creates an object that can partition the underlying collection into a variable number of partitions. - - An object that can create partitions over the underlying data source. - - - Gets whether additional partitions can be created dynamically. - - - Provides a producer-consumer collection facade for a BlockingCollection. - Specifies the type of the elements in the collection. - - - Extension methods for CancellationToken. - - - Cancels a CancellationTokenSource and throws a corresponding OperationCanceledException. - The source to be canceled. - - - - Creates a CancellationTokenSource that will be canceled when the specified token has cancellation requested. - - The token. - The created CancellationTokenSource. - - - Provides access to an already completed task. - A completed task can be useful for using ContinueWith overloads where there aren't StartNew equivalents. - - - Gets a completed Task. - - - Provides access to an already completed task. - A completed task can be useful for using ContinueWith overloads where there aren't StartNew equivalents. - - - Initializes a Task. - - - Gets a completed Task. - - - Parallel extensions for the Delegate class. - - - Dynamically invokes (late-bound) in parallel the methods represented by the delegate. - The delegate to be invoked. - An array of objects that are the arguments to pass to the delegates. - The return value of one of the delegate invocations. - - - - Provides a delegate that runs the specified action and fails fast if the action throws an exception. - - The action to invoke. - The wrapper delegate. - - - - Provides a delegate that runs the specified function and fails fast if the function throws an exception. - - The function to invoke. - The wrapper delegate. - - - Extension methods for working with Ping asynchronously. - - - - Asynchronously attempts to send an Internet Control Message Protocol (ICMP) echo message. - - The Ping. - An IPAddress that identifies the computer that is the destination for the ICMP echo message. - A user-defined object stored in the resulting Task. - A task that represents the asynchronous operation. - - - - Asynchronously attempts to send an Internet Control Message Protocol (ICMP) echo message. - - The Ping. - - A String that identifies the computer that is the destination for the ICMP echo message. - The value specified for this parameter can be a host name or a string representation of an IP address. - - A user-defined object stored in the resulting Task. - A task that represents the asynchronous operation. - - - - Asynchronously attempts to send an Internet Control Message Protocol (ICMP) echo message. - - The Ping. - An IPAddress that identifies the computer that is the destination for the ICMP echo message. - - An Int32 value that specifies the maximum number of milliseconds (after sending the echo message) - to wait for the ICMP echo reply message. - - A user-defined object stored in the resulting Task. - A task that represents the asynchronous operation. - - - - Asynchronously attempts to send an Internet Control Message Protocol (ICMP) echo message. - - The Ping. - - A String that identifies the computer that is the destination for the ICMP echo message. - The value specified for this parameter can be a host name or a string representation of an IP address. - - - An Int32 value that specifies the maximum number of milliseconds (after sending the echo message) - to wait for the ICMP echo reply message. - - A user-defined object stored in the resulting Task. - A task that represents the asynchronous operation. - - - - Asynchronously attempts to send an Internet Control Message Protocol (ICMP) echo message. - - The Ping. - An IPAddress that identifies the computer that is the destination for the ICMP echo message. - - An Int32 value that specifies the maximum number of milliseconds (after sending the echo message) - to wait for the ICMP echo reply message. - - - A Byte array that contains data to be sent with the ICMP echo message and returned - in the ICMP echo reply message. The array cannot contain more than 65,500 bytes. - - A user-defined object stored in the resulting Task. - A task that represents the asynchronous operation. - - - - Asynchronously attempts to send an Internet Control Message Protocol (ICMP) echo message. - - The Ping. - - A String that identifies the computer that is the destination for the ICMP echo message. - The value specified for this parameter can be a host name or a string representation of an IP address. - - - An Int32 value that specifies the maximum number of milliseconds (after sending the echo message) - to wait for the ICMP echo reply message. - - - A Byte array that contains data to be sent with the ICMP echo message and returned - in the ICMP echo reply message. The array cannot contain more than 65,500 bytes. - - A user-defined object stored in the resulting Task. - A task that represents the asynchronous operation. - - - - Asynchronously attempts to send an Internet Control Message Protocol (ICMP) echo message. - - The Ping. - An IPAddress that identifies the computer that is the destination for the ICMP echo message. - - An Int32 value that specifies the maximum number of milliseconds (after sending the echo message) - to wait for the ICMP echo reply message. - - - A Byte array that contains data to be sent with the ICMP echo message and returned - in the ICMP echo reply message. The array cannot contain more than 65,500 bytes. - - A PingOptions object used to control fragmentation and Time-to-Live values for the ICMP echo message packet. - A user-defined object stored in the resulting Task. - A task that represents the asynchronous operation. - - - - Asynchronously attempts to send an Internet Control Message Protocol (ICMP) echo message. - - The Ping. - - A String that identifies the computer that is the destination for the ICMP echo message. - The value specified for this parameter can be a host name or a string representation of an IP address. - - - An Int32 value that specifies the maximum number of milliseconds (after sending the echo message) - to wait for the ICMP echo reply message. - - - A Byte array that contains data to be sent with the ICMP echo message and returned - in the ICMP echo reply message. The array cannot contain more than 65,500 bytes. - - A PingOptions object used to control fragmentation and Time-to-Live values for the ICMP echo message packet. - A user-defined object stored in the resulting Task. - A task that represents the asynchronous operation. - - - The core implementation of SendTask. - The Ping. - A user-defined object stored in the resulting Task. - - A delegate that initiates the asynchronous send. - The provided TaskCompletionSource must be passed as the user-supplied state to the actual Ping.SendAsync method. - - - - - Extension methods for working with SmtpClient asynchronously. - - - Sends an e-mail message asynchronously. - The client. - A MailMessage that contains the message to send. - A user-defined object stored in the resulting Task. - A Task that represents the asynchronous send. - - - Sends an e-mail message asynchronously. - The client. - A String that contains the address information of the message sender. - A String that contains the address that the message is sent to. - A String that contains the subject line for the message. - A String that contains the message body. - A user-defined object stored in the resulting Task. - A Task that represents the asynchronous send. - - - The core implementation of SendTask. - The client. - The user-supplied state. - - A delegate that initiates the asynchronous send. - The provided TaskCompletionSource must be passed as the user-supplied state to the actual SmtpClient.SendAsync method. - - - - - Extension methods for working with WebClient asynchronously. - - - Downloads the resource with the specified URI as a byte array, asynchronously. - The WebClient. - The URI from which to download data. - A Task that contains the downloaded data. - - - Downloads the resource with the specified URI as a byte array, asynchronously. - The WebClient. - The URI from which to download data. - A Task that contains the downloaded data. - - - Downloads the resource with the specified URI to a local file, asynchronously. - The WebClient. - The URI from which to download data. - The name of the local file that is to receive the data. - A Task that contains the downloaded data. - - - Downloads the resource with the specified URI to a local file, asynchronously. - The WebClient. - The URI from which to download data. - The name of the local file that is to receive the data. - A Task that contains the downloaded data. - - - Downloads the resource with the specified URI as a string, asynchronously. - The WebClient. - The URI from which to download data. - A Task that contains the downloaded string. - - - Downloads the resource with the specified URI as a string, asynchronously. - The WebClient. - The URI from which to download data. - A Task that contains the downloaded string. - - - Opens a readable stream for the data downloaded from a resource, asynchronously. - The WebClient. - The URI for which the stream should be opened. - A Task that contains the opened stream. - - - Opens a readable stream for the data downloaded from a resource, asynchronously. - The WebClient. - The URI for which the stream should be opened. - A Task that contains the opened stream. - - - Opens a writeable stream for uploading data to a resource, asynchronously. - The WebClient. - The URI for which the stream should be opened. - The HTTP method that should be used to open the stream. - A Task that contains the opened stream. - - - Opens a writeable stream for uploading data to a resource, asynchronously. - The WebClient. - The URI for which the stream should be opened. - The HTTP method that should be used to open the stream. - A Task that contains the opened stream. - - - Uploads data to the specified resource, asynchronously. - The WebClient. - The URI to which the data should be uploaded. - The HTTP method that should be used to upload the data. - The data to upload. - A Task containing the data in the response from the upload. - - - Uploads data to the specified resource, asynchronously. - The WebClient. - The URI to which the data should be uploaded. - The HTTP method that should be used to upload the data. - The data to upload. - A Task containing the data in the response from the upload. - - - Uploads a file to the specified resource, asynchronously. - The WebClient. - The URI to which the file should be uploaded. - The HTTP method that should be used to upload the file. - A path to the file to upload. - A Task containing the data in the response from the upload. - - - Uploads a file to the specified resource, asynchronously. - The WebClient. - The URI to which the file should be uploaded. - The HTTP method that should be used to upload the file. - A path to the file to upload. - A Task containing the data in the response from the upload. - - - Uploads data in a string to the specified resource, asynchronously. - The WebClient. - The URI to which the data should be uploaded. - The HTTP method that should be used to upload the data. - The data to upload. - A Task containing the data in the response from the upload. - - - Uploads data in a string to the specified resource, asynchronously. - The WebClient. - The URI to which the data should be uploaded. - The HTTP method that should be used to upload the data. - The data to upload. - A Task containing the data in the response from the upload. - - - Extension methods for IProducerConsumerCollection. - - - Clears the collection by repeatedly taking elements until it's empty. - Specifies the type of the elements in the collection. - The collection to be cleared. - - - Creates an enumerable which will consume and return elements from the collection. - Specifies the type of the elements in the collection. - The collection to be consumed. - An enumerable that consumes elements from the collection and returns them. - - - Adds the contents of an enumerable to the collection. - Specifies the type of the elements in the collection. - The target collection to be augmented. - The source enumerable containing the data to be added. - - - Adds the contents of an observable to the collection. - Specifies the type of the elements in the collection. - The target collection to be augmented. - The source observable containing the data to be added. - An IDisposable that may be used to cancel the transfer. - - - Creates an add-only facade for the collection. - Specifies the type of the elements in the collection. - The collection to be wrapped. - - An IProducerConsumerCollection that wraps the target collection and supports only add - functionality, not take. - - - - Creates a take-only facade for the collection. - Specifies the type of the elements in the collection. - The collection to be wrapped. - - An IProducerConsumerCollection that wraps the target collection and supports only take - functionality, not add. - - - - Extension methods for Lazy. - - - Forces value creation of a Lazy instance. - Specifies the type of the value being lazily initialized. - The Lazy instance. - The initialized Lazy instance. - - - Retrieves the value of a Lazy asynchronously. - Specifies the type of the value being lazily initialized. - The Lazy instance. - A Task representing the Lazy's value. - - - Creates a Lazy that's already been initialized to a specified value. - The type of the data to be initialized. - The value with which to initialize the Lazy instance. - The initialized Lazy. - - - - Provides LINQ support for Tasks by implementing the primary standard query operators. - - - - Represents a grouping of one element. - The type of the key for the element. - The type of the element. - - - Provides a grouping for common Parallel LINQ options. - - - Gets or sets the execution mode. - - - Gets or sets the merge options. - - - Gets or sets whether the query should retain ordering. - - - Extension methods for ParallelOptions. - - - Copies a ParallelOptions instance to a shallow clone. - The options to be cloned. - The shallow clone. - - - Extension methods for Parallel LINQ. - - - Takes the top elements as if they were sorted. - Specifies the type of the elements. - Specifies the type of the keys used to compare elements. - The source elements. - A function used to extract a key from each element. - The number of elements to take. - - - - Implements a map-reduce operation. - Specifies the type of the source elements. - Specifies the type of the mapped elements. - Specifies the type of the element keys. - Specifies the type of the results. - The source elements. - A function used to get the target data from a source element. - A function used to get a key from the target data. - A function used to reduce a group of elements. - The result elements of the reductions. - - - Implements a map-reduce operation. - Specifies the type of the source elements. - Specifies the type of the mapped elements. - Specifies the type of the element keys. - Specifies the type of the results. - The source elements. - A function used to get an enumerable of target data from a source element. - A function used to get a key from target data. - A function used to reduce a group of elements to an enumerable of results. - The result elements of the reductions. - - - Runs the query and outputs its results into the target collection. - Specifies the type of elements output from the query. - The source query. - The target collection. - - - This is the method to opt into Parallel LINQ. - Specifies the type of elements provided to the query. - The source query. - The options to use for query processing. - The source as a ParallelQuery to bind to ParallelEnumerable extension methods. - - - A comparer that comparers using the inverse of the default comparer. - Specifies the type being compared. - - - Extension methods for TaskCompletionSource. - - - Transfers the result of a Task to the TaskCompletionSource. - Specifies the type of the result. - The TaskCompletionSource. - The task whose completion results should be transfered. - - - Transfers the result of a Task to the TaskCompletionSource. - Specifies the type of the result. - The TaskCompletionSource. - The task whose completion results should be transfered. - - - Attempts to transfer the result of a Task to the TaskCompletionSource. - Specifies the type of the result. - The TaskCompletionSource. - The task whose completion results should be transfered. - Whether the transfer could be completed. - - - Attempts to transfer the result of a Task to the TaskCompletionSource. - Specifies the type of the result. - The TaskCompletionSource. - The task whose completion results should be transfered. - Whether the transfer could be completed. - - - Extensions methods for Task. - - - Creates a continuation task using the specified TaskFactory. - The antecedent Task. - The continuation action. - The TaskFactory. - A continuation task. - - - Creates a continuation task using the specified TaskFactory. - The antecedent Task. - The continuation function. - The TaskFactory. - A continuation task. - - - Creates a continuation task using the specified TaskFactory. - The antecedent Task. - The continuation action. - The TaskFactory. - A continuation task. - - - Creates a continuation task using the specified TaskFactory. - The antecedent Task. - The continuation function. - The TaskFactory. - A continuation task. - - - - Creates a Task that represents the completion of another Task, and - that schedules an AsyncCallback to run upon completion. - - The antecedent Task. - The AsyncCallback to run. - The object state to use with the AsyncCallback. - The new task. - - - - Creates a Task that represents the completion of another Task, and - that schedules an AsyncCallback to run upon completion. - - The antecedent Task. - The AsyncCallback to run. - The object state to use with the AsyncCallback. - The new task. - - - Suppresses default exception handling of a Task that would otherwise reraise the exception on the finalizer thread. - The Task to be monitored. - The original Task. - - - Suppresses default exception handling of a Task that would otherwise reraise the exception on the finalizer thread. - The Task to be monitored. - The original Task. - - - Fails immediately when an exception is encountered. - The Task to be monitored. - The original Task. - - - Fails immediately when an exception is encountered. - The Task to be monitored. - The original Task. - - - Propagates any exceptions that occurred on the specified task. - The Task whose exceptions are to be propagated. - - - Propagates any exceptions that occurred on the specified tasks. - The Task whose exceptions are to be propagated. - - - Creates an IObservable that represents the completion of a Task. - Specifies the type of data returned by the Task. - The Task to be represented as an IObservable. - An IObservable that represents the completion of the Task. - - - Creates a new Task that mirrors the supplied task but that will be canceled after the specified timeout. - The task. - The timeout. - The new Task that may time out. - - - Creates a new Task that mirrors the supplied task but that will be canceled after the specified timeout. - Specifies the type of data contained in the task. - The task. - The timeout. - The new Task that may time out. - - - - Ensures that a parent task can't transition into a completed state - until the specified task has also completed, even if it's not - already a child task. - - The task to attach to the current task as a child. - - - Waits for the task to complete execution, pumping in the meantime. - The task for which to wait. - This method is intended for usage with Windows Presentation Foundation. - - - Waits for the task to complete execution, pumping in the meantime. - The task for which to wait. - This method is intended for usage with Windows Presentation Foundation. - - - Waits for the task to complete execution, returning the task's final status. - The task for which to wait. - The completion status of the task. - Unlike Wait, this method will not throw an exception if the task ends in the Faulted or Canceled state. - - - Creates a task that represents the completion of a follow-up action when a task completes. - The task. - The action to run when the task completes. - The task that represents the completion of both the task and the action. - - - Creates a task that represents the completion of a follow-up function when a task completes. - The task. - The function to run when the task completes. - The task that represents the completion of both the task and the function. - - - Creates a task that represents the completion of a follow-up action when a task completes. - The task. - The action to run when the task completes. - The task that represents the completion of both the task and the action. - - - Creates a task that represents the completion of a follow-up function when a task completes. - The task. - The function to run when the task completes. - The task that represents the completion of both the task and the function. - - - Creates a task that represents the completion of a second task when a first task completes. - The first task. - The function that produces the second task. - The task that represents the completion of both the first and second task. - - - Creates a task that represents the completion of a second task when a first task completes. - The first task. - The function that produces the second task based on the result of the first task. - The task that represents the completion of both the first and second task. - - - Creates a task that represents the completion of a second task when a first task completes. - The first task. - The function that produces the second task. - The task that represents the completion of both the first and second task. - - - Creates a task that represents the completion of a second task when a first task completes. - The first task. - The function that produces the second task based on the result of the first. - The task that represents the completion of both the first and second task. - - - An implementation of IObservable that wraps a Task. - The type of data returned by the task. - - - Translate a call to IDisposable.Dispose to a CancellationTokenSource.Cancel. - - - Extensions for TaskFactory. - Extensions for TaskFactory. - Extensions for TaskFactory. - - - Creates a generic TaskFactory from a non-generic one. - Specifies the type of Task results for the Tasks created by the new TaskFactory. - The TaskFactory to serve as a template. - The created TaskFactory. - - - Creates a generic TaskFactory from a non-generic one. - Specifies the type of Task results for the Tasks created by the new TaskFactory. - The TaskFactory to serve as a template. - The created TaskFactory. - - - Gets the TaskScheduler instance that should be used to schedule tasks. - - - Gets the TaskScheduler instance that should be used to schedule tasks. - - - Converts TaskCreationOptions into TaskContinuationOptions. - - - - - - Creates a continuation Task that will compplete upon - the completion of a set of provided Tasks. - - The TaskFactory to use to create the continuation task. - The array of tasks from which to continue. - A task that, when completed, will return the array of completed tasks. - - - - Creates a continuation Task that will compplete upon - the completion of a set of provided Tasks. - - The TaskFactory to use to create the continuation task. - The array of tasks from which to continue. - A task that, when completed, will return the array of completed tasks. - - - - Creates a continuation Task that will complete upon - the completion of any one of a set of provided Tasks. - - The TaskFactory to use to create the continuation task. - The array of tasks from which to continue. - A task that, when completed, will return the completed task. - - - - Creates a continuation Task that will complete upon - the completion of any one of a set of provided Tasks. - - The TaskFactory to use to create the continuation task. - The array of tasks from which to continue. - A task that, when completed, will return the completed task. - - - Creates a Task using the TaskFactory. - The factory to use. - The delegate for the task. - The created task. The task has not been scheduled. - - - Creates a Task using the TaskFactory. - The factory to use. - The delegate for the task. - Options that control the task's behavior. - The created task. The task has not been scheduled. - - - Creates a Task using the TaskFactory. - The factory to use. - The delegate for the task. - An object provided to the delegate. - The created task. The task has not been scheduled. - - - Creates a Task using the TaskFactory. - The factory to use. - The delegate for the task. - An object provided to the delegate. - Options that control the task's behavior. - The created task. The task has not been scheduled. - - - Creates a Task using the TaskFactory. - The factory to use. - The delegate for the task. - The created task. The task has not been scheduled. - - - Creates a Task using the TaskFactory. - The factory to use. - The delegate for the task. - Options that control the task's behavior. - The created task. The task has not been scheduled. - - - Creates a Task using the TaskFactory. - The factory to use. - The delegate for the task. - An object provided to the delegate. - The created task. The task has not been scheduled. - - - Creates a Task using the TaskFactory. - The factory to use. - The delegate for the task. - An object provided to the delegate. - Options that control the task's behavior. - The created task. The task has not been scheduled. - - - Creates a Task using the TaskFactory. - The factory to use. - The delegate for the task. - The created task. The task has not been scheduled. - - - Creates a Task using the TaskFactory. - The factory to use. - The delegate for the task. - Options that control the task's behavior. - The created task. The task has not been scheduled. - - - Creates a Task using the TaskFactory. - The factory to use. - The delegate for the task. - An object provided to the delegate. - The created task. The task has not been scheduled. - - - Creates a Task using the TaskFactory. - The factory to use. - The delegate for the task. - An object provided to the delegate. - Options that control the task's behavior. - The created task. The task has not been scheduled. - - - Creates a Task that will complete after the specified delay. - The TaskFactory. - The delay after which the Task should transition to RanToCompletion. - A Task that will be completed after the specified duration. - - - Creates a Task that will complete after the specified delay. - The TaskFactory. - The delay after which the Task should transition to RanToCompletion. - The cancellation token that can be used to cancel the timed task. - A Task that will be completed after the specified duration and that's cancelable with the specified token. - - - Creates and schedules a task for execution after the specified time delay. - The factory to use to create the task. - The delay after which the task will be scheduled. - The delegate executed by the task. - The created Task. - - - Creates and schedules a task for execution after the specified time delay. - The factory to use to create the task. - The delay after which the task will be scheduled. - The delegate executed by the task. - Options that control the task's behavior. - The created Task. - - - Creates and schedules a task for execution after the specified time delay. - The factory to use to create the task. - The delay after which the task will be scheduled. - The delegate executed by the task. - The cancellation token to assign to the created Task. - The created Task. - - - Creates and schedules a task for execution after the specified time delay. - The factory to use to create the task. - The delay after which the task will be scheduled. - The delegate executed by the task. - The cancellation token to assign to the created Task. - Options that control the task's behavior. - The scheduler to which the Task will be scheduled. - The created Task. - - - Creates and schedules a task for execution after the specified time delay. - The factory to use to create the task. - The delay after which the task will be scheduled. - The delegate executed by the task. - An object provided to the delegate. - The created Task. - - - Creates and schedules a task for execution after the specified time delay. - The factory to use to create the task. - The delay after which the task will be scheduled. - The delegate executed by the task. - An object provided to the delegate. - Options that control the task's behavior. - The created Task. - - - Creates and schedules a task for execution after the specified time delay. - The factory to use to create the task. - The delay after which the task will be scheduled. - The delegate executed by the task. - An object provided to the delegate. - The cancellation token to assign to the created Task. - The created Task. - - - Creates and schedules a task for execution after the specified time delay. - The factory to use to create the task. - The delay after which the task will be scheduled. - The delegate executed by the task. - An object provided to the delegate. - The cancellation token to assign to the created Task. - Options that control the task's behavior. - The scheduler to which the Task will be scheduled. - The created Task. - - - Creates and schedules a task for execution after the specified time delay. - The factory to use to create the task. - The delay after which the task will be scheduled. - The delegate executed by the task. - The created Task. - - - Creates and schedules a task for execution after the specified time delay. - The factory to use to create the task. - The delay after which the task will be scheduled. - The delegate executed by the task. - Options that control the task's behavior. - The created Task. - - - Creates and schedules a task for execution after the specified time delay. - The factory to use to create the task. - The delay after which the task will be scheduled. - The delegate executed by the task. - The CancellationToken to assign to the Task. - The created Task. - - - Creates and schedules a task for execution after the specified time delay. - The factory to use to create the task. - The delay after which the task will be scheduled. - The delegate executed by the task. - The CancellationToken to assign to the Task. - Options that control the task's behavior. - The scheduler to which the Task will be scheduled. - The created Task. - - - Creates and schedules a task for execution after the specified time delay. - The factory to use to create the task. - The delay after which the task will be scheduled. - The delegate executed by the task. - An object provided to the delegate. - The created Task. - - - Creates and schedules a task for execution after the specified time delay. - The factory to use to create the task. - The delay after which the task will be scheduled. - The delegate executed by the task. - An object provided to the delegate. - The CancellationToken to assign to the Task. - The created Task. - - - Creates and schedules a task for execution after the specified time delay. - The factory to use to create the task. - The delay after which the task will be scheduled. - The delegate executed by the task. - An object provided to the delegate. - Options that control the task's behavior. - The created Task. - - - Creates and schedules a task for execution after the specified time delay. - The factory to use to create the task. - The delay after which the task will be scheduled. - The delegate executed by the task. - An object provided to the delegate. - The CancellationToken to assign to the Task. - Options that control the task's behavior. - The scheduler to which the Task will be scheduled. - The created Task. - - - Creates a Task that has completed in the Faulted state with the specified exception. - The target TaskFactory. - The exception with which the Task should fault. - The completed Task. - - - Creates a Task that has completed in the Faulted state with the specified exception. - Specifies the type of payload for the new Task. - The target TaskFactory. - The exception with which the Task should fault. - The completed Task. - - - Creates a Task that has completed in the RanToCompletion state with the specified result. - Specifies the type of payload for the new Task. - The target TaskFactory. - The result with which the Task should complete. - The completed Task. - - - Creates a Task that has completed in the Canceled state with the specified CancellationToken. - The target TaskFactory. - The CancellationToken with which the Task should complete. - The completed Task. - - - Creates a Task that has completed in the Canceled state with the specified CancellationToken. - Specifies the type of payload for the new Task. - The target TaskFactory. - The CancellationToken with which the Task should complete. - The completed Task. - - - Creates a Task that has completed in the Faulted state with the specified exception. - The target TaskFactory. - The exception with which the Task should fault. - The completed Task. - - - Creates a Task that has completed in the RanToCompletion state with the specified result. - Specifies the type of payload for the new Task. - The target TaskFactory. - The result with which the Task should complete. - The completed Task. - - - Creates a Task that has completed in the Canceled state with the specified CancellationToken. - Specifies the type of payload for the new Task. - The target TaskFactory. - The CancellationToken with which the Task should complete. - The completed Task. - - - Creates a Task that will be completed when the specified WaitHandle is signaled. - The target factory. - The WaitHandle. - The created Task. - - - Asynchronously iterates through an enumerable of tasks. - The target factory. - The enumerable containing the tasks to be iterated through. - A Task that represents the complete asynchronous operation. - - - Asynchronously iterates through an enumerable of tasks. - The target factory. - The enumerable containing the tasks to be iterated through. - The cancellation token used to cancel the iteration. - A Task that represents the complete asynchronous operation. - - - Asynchronously iterates through an enumerable of tasks. - The target factory. - The enumerable containing the tasks to be iterated through. - Options that control the task's behavior. - A Task that represents the complete asynchronous operation. - - - Asynchronously iterates through an enumerable of tasks. - The target factory. - The enumerable containing the tasks to be iterated through. - The scheduler to which tasks will be scheduled. - A Task that represents the complete asynchronous operation. - - - Asynchronously iterates through an enumerable of tasks. - The target factory. - The enumerable containing the tasks to be iterated through. - The cancellation token used to cancel the iteration. - Options that control the task's behavior. - The scheduler to which tasks will be scheduled. - A Task that represents the complete asynchronous operation. - - - Asynchronously iterates through an enumerable of tasks. - The target factory. - The enumerable containing the tasks to be iterated through. - The asynchronous state for the returned Task. - A Task that represents the complete asynchronous operation. - - - Asynchronously iterates through an enumerable of tasks. - The target factory. - The enumerable containing the tasks to be iterated through. - The asynchronous state for the returned Task. - The cancellation token used to cancel the iteration. - A Task that represents the complete asynchronous operation. - - - Asynchronously iterates through an enumerable of tasks. - The target factory. - The enumerable containing the tasks to be iterated through. - The asynchronous state for the returned Task. - Options that control the task's behavior. - A Task that represents the complete asynchronous operation. - - - Asynchronously iterates through an enumerable of tasks. - The target factory. - The enumerable containing the tasks to be iterated through. - The asynchronous state for the returned Task. - The scheduler to which tasks will be scheduled. - A Task that represents the complete asynchronous operation. - - - Asynchronously iterates through an enumerable of tasks. - The target factory. - The enumerable containing the tasks to be iterated through. - The asynchronous state for the returned Task. - The cancellation token used to cancel the iteration. - Options that control the task's behavior. - The scheduler to which tasks will be scheduled. - A Task that represents the complete asynchronous operation. - - - Asynchronously executes a sequence of tasks, maintaining a list of all tasks processed. - The TaskFactory to use to create the task. - - The functions that generate the tasks through which to iterate sequentially. - Iteration will cease if a task faults. - - A Task that will return the list of tracked tasks iterated. - - - Creates the enumerable to iterate through with Iterate. - - The functions that generate the tasks through which to iterate sequentially. - Iteration will cease if a task faults. - - The TaskCompletionSource to resolve with the asynchronous results. - The enumerable through which to iterate. - - - A cache of delegates. - The result type. - - - Function that returns default(TResult). - - - Extension methods for TaskScheduler. - - - Gets a SynchronizationContext that targets this TaskScheduler. - The target scheduler. - A SynchronizationContext that targets this scheduler. - - - Provides a SynchronizationContext wrapper for a TaskScheduler. - - - The scheduler. - - - Initializes the context with the specified scheduler. - The scheduler to target. - - - Dispatches an asynchronous message to the synchronization context. - The System.Threading.SendOrPostCallback delegate to call. - The object passed to the delegate. - - - Dispatches a synchronous message to the synchronization context. - The System.Threading.SendOrPostCallback delegate to call. - The object passed to the delegate. - - - - Provides parallelized algorithms for common operations. - - - - Filters an input list, running a predicate over each element of the input. - Specifies the type of data in the list. - The list to be filtered. - The predicate to use to determine which elements pass. - A new list containing all those elements from the input that passed the filter. - - - Filters an input list, running a predicate over each element of the input. - Specifies the type of data in the list. - The list to be filtered. - Options to use for the execution of this filter. - The predicate to use to determine which elements pass. - A new list containing all those elements from the input that passed the filter. - - - Executes a for loop in which iterations may run in parallel. - The start index, inclusive. - The end index, exclusive. - The delegate that is invoked once per iteration. - - - Executes a for loop in which iterations may run in parallel. - The start index, inclusive. - The end index, exclusive. - A System.Threading.Tasks.ParallelOptions instance that configures the behavior of this operation. - The delegate that is invoked once per iteration. - - - Creates an enumerable that iterates the range [fromInclusive, toExclusive). - The lower bound, inclusive. - The upper bound, exclusive. - The enumerable of the range. - - - Executes a for loop over ranges in which iterations may run in parallel. - The start index, inclusive. - The end index, exclusive. - The delegate that is invoked once per range. - A ParallelLoopResult structure that contains information on what portion of the loop completed. - - - Executes a for loop over ranges in which iterations may run in parallel. - The start index, inclusive. - The end index, exclusive. - The delegate that is invoked once per range. - A ParallelLoopResult structure that contains information on what portion of the loop completed. - - - Executes a for loop over ranges in which iterations may run in parallel. - The start index, inclusive. - The end index, exclusive. - The function delegate that returns the initial state of the local data for each thread. - The delegate that is invoked once per range. - The delegate that performs a final action on the local state of each thread. - A ParallelLoopResult structure that contains information on what portion of the loop completed. - - - Executes a for loop over ranges in which iterations may run in parallel. - The start index, inclusive. - The end index, exclusive. - The delegate that is invoked once per range. - A ParallelLoopResult structure that contains information on what portion of the loop completed. - - - Executes a for loop over ranges in which iterations may run in parallel. - The start index, inclusive. - The end index, exclusive. - The delegate that is invoked once per range. - A ParallelLoopResult structure that contains information on what portion of the loop completed. - - - Executes a for loop over ranges in which iterations may run in parallel. - The start index, inclusive. - The end index, exclusive. - The function delegate that returns the initial state of the local data for each thread. - The delegate that is invoked once per range. - The delegate that performs a final action on the local state of each thread. - A ParallelLoopResult structure that contains information on what portion of the loop completed. - - - Executes a for loop over ranges in which iterations may run in parallel. - The start index, inclusive. - The end index, exclusive. - A ParallelOptions instance that configures the behavior of this operation. - The delegate that is invoked once per range. - A ParallelLoopResult structure that contains information on what portion of the loop completed. - - - Executes a for loop over ranges in which iterations may run in parallel. - The start index, inclusive. - The end index, exclusive. - A ParallelOptions instance that configures the behavior of this operation. - The delegate that is invoked once per range. - A ParallelLoopResult structure that contains information on what portion of the loop completed. - - - Executes a for loop over ranges in which iterations may run in parallel. - The start index, inclusive. - The end index, exclusive. - - The function delegate that returns the initial state of the local data for each thread. - The delegate that is invoked once per range. - The delegate that performs a final action on the local state of each thread. - A ParallelLoopResult structure that contains information on what portion of the loop completed. - - - Executes a for loop over ranges in which iterations may run in parallel. - The start index, inclusive. - The end index, exclusive. - A ParallelOptions instance that configures the behavior of this operation. - The delegate that is invoked once per range. - A ParallelLoopResult structure that contains information on what portion of the loop completed. - - - Executes a for loop over ranges in which iterations may run in parallel. - The start index, inclusive. - The end index, exclusive. - A ParallelOptions instance that configures the behavior of this operation. - The delegate that is invoked once per range. - A ParallelLoopResult structure that contains information on what portion of the loop completed. - - - Executes a for loop over ranges in which iterations may run in parallel. - The start index, inclusive. - The end index, exclusive. - - The function delegate that returns the initial state of the local data for each thread. - The delegate that is invoked once per range. - The delegate that performs a final action on the local state of each thread. - A ParallelLoopResult structure that contains information on what portion of the loop completed. - - - Executes a map operation, converting an input list into an output list, in parallel. - Specifies the type of the input data. - Specifies the type of the output data. - The input list to be mapped used the transform function. - The transform function to use to map the input data to the output data. - The output data, transformed using the transform function. - - - Executes a map operation, converting an input list into an output list, in parallel. - Specifies the type of the input data. - Specifies the type of the output data. - The input list to be mapped used the transform function. - A ParallelOptions instance that configures the behavior of this operation. - The transform function to use to map the input data to the output data. - The output data, transformed using the transform function. - - - Reduces the input data using the specified aggregation operation. - Specifies the type of data being aggregated. - The input data to be reduced. - The seed to use to initialize the operation; this seed may be used multiple times. - The reduction operation. - The reduced value. - - - Reduces the input data using the specified aggregation operation. - Specifies the type of data being aggregated. - The input data to be reduced. - A ParallelOptions instance that configures the behavior of this operation. - The seed to use to initialize the operation; this seed may be used multiple times. - The reduction operation. - The reduced value. - - - Reduces the input range using the specified aggregation operation. - Specifies the type of data being aggregated. - The start index, inclusive. - The end index, exclusive. - The function used to retrieve the data to be reduced for a given index. - The seed to use to initialize the operation; this seed may be used multiple times. - The reduction operation. - The reduced value. - - - Reduces the input range using the specified aggregation operation. - Specifies the type of data being aggregated. - The start index, inclusive. - The end index, exclusive. - A ParallelOptions instance that configures the behavior of this operation. - The function used to retrieve the data to be reduced for a given index. - The seed to use to initialize the operation; this seed may be used multiple times. - The reduction operation. - The reduced value. - - - Computes a parallel prefix scan over the source enumerable using the specified function. - The type of the data in the source. - The source data over which a prefix scan should be computed. - The function to use for the scan. - The results of the scan operation. - - For very small functions, such as additions, an implementation targeted - at the relevant type and operation will perform significantly better than - this generalized implementation. - - - - Computes a parallel prefix scan over the source enumerable using the specified function. - The type of the data in the source. - The source data over which a prefix scan should be computed. - The function to use for the scan. - Whether to load-balance during process. - The results of the scan operation. - - For very small functions, such as additions, an implementation targeted - at the relevant type and operation will perform significantly better than - this generalized implementation. - - - - Computes a parallel prefix scan in-place on an array using the specified function. - The type of the data in the source. - The data over which a prefix scan should be computed. Upon exit, stores the results. - The function to use for the scan. - The results of the scan operation. - - For very small functions, such as additions, an implementation targeted - at the relevant type and operation will perform significantly better than - this generalized implementation. - - - - Computes a parallel prefix scan in-place on an array using the specified function. - The type of the data in the source. - The data over which a prefix scan should be computed. Upon exit, stores the results. - The function to use for the scan. - Whether to load-balance during process. - The results of the scan operation. - - For very small functions, such as additions, an implementation targeted - at the relevant type and operation will perform significantly better than - this generalized implementation. - - - - Computes a sequential prefix scan over the array using the specified function. - The type of the data in the array. - The data, which will be overwritten with the computed prefix scan. - The function to use for the scan. - The start of the data in arr over which the scan is being computed. - The length of the data in arr over which the scan is being computed. - The inclusive distance between elements over which the scan is being computed. - No parameter validation is performed. - - - Computes a sequential exclusive prefix scan over the array using the specified function. - The data, which will be overwritten with the computed prefix scan. - The function to use for the scan. - The inclusive lower bound of the array at which to start the scan. - The exclusive upper bound of the array at which to end the scan. - - - Computes a parallel prefix scan over the array using the specified function. - The type of the data in the array. - The data, which will be overwritten with the computed prefix scan. - The function to use for the scan. - The start of the data in arr over which the scan is being computed. - The length of the data in arr over which the scan is being computed. - The inclusive distance between elements over which the scan is being computed. - No parameter validation is performed. - - - Computes a parallel inclusive prefix scan over the array using the specified function. - - - Sorts an array in parallel. - Specifies the type of data in the array. - The array to be sorted. - - - Sorts an array in parallel. - Specifies the type of data in the array. - The array to be sorted. - The comparer used to compare two elements during the sort operation. - - - Sorts an array in parallel. - Specifies the type of data in the array. - The array to be sorted. - The index at which to start the sort, inclusive. - The number of elements to be sorted, starting at the start index. - - - Sorts an array in parallel. - Specifies the type of data in the array. - The array to be sorted. - The index at which to start the sort, inclusive. - The number of elements to be sorted, starting at the start index. - The comparer used to compare two elements during the sort operation. - - - Sorts key/value arrays in parallel. - Specifies the type of the data in the keys array. - Specifies the type of the data in the items array. - The keys to be sorted. - The items to be sorted based on the corresponding keys. - - - Sorts key/value arrays in parallel. - Specifies the type of the data in the keys array. - Specifies the type of the data in the items array. - The keys to be sorted. - The items to be sorted based on the corresponding keys. - The comparer used to compare two elements during the sort operation. - - - Sorts key/value arrays in parallel. - Specifies the type of the data in the keys array. - Specifies the type of the data in the items array. - The keys to be sorted. - The items to be sorted based on the corresponding keys. - The index at which to start the sort, inclusive. - The number of elements to be sorted, starting at the start index. - - - Sorts key/value arrays in parallel. - Specifies the type of the data in the keys array. - Specifies the type of the data in the items array. - The keys to be sorted. - The items to be sorted based on the corresponding keys. - The index at which to start the sort, inclusive. - The number of elements to be sorted, starting at the start index. - The comparer used to compare two elements during the sort operation. - - - Executes a function for each value in a range, returning the first result achieved and ceasing execution. - The type of the data returned. - The start of the range, inclusive. - The end of the range, exclusive. - The function to execute for each element. - The result computed. - - - Executes a function for each value in a range, returning the first result achieved and ceasing execution. - The type of the data returned. - The start of the range, inclusive. - The end of the range, exclusive. - The options to use for processing the loop. - The function to execute for each element. - The result computed. - - - Executes a function for each element in a source, returning the first result achieved and ceasing execution. - The type of the data in the source. - The type of the data returned. - The input elements to be processed. - The function to execute for each element. - The result computed. - - - Executes a function for each element in a source, returning the first result achieved and ceasing execution. - The type of the data in the source. - The type of the data returned. - The input elements to be processed. - The options to use for processing the loop. - The function to execute for each element. - The result computed. - - - Invokes the specified functions, potentially in parallel, canceling outstanding invocations once one completes. - Specifies the type of data returned by the functions. - The functions to be executed. - A result from executing one of the functions. - - - Invokes the specified functions, potentially in parallel, canceling outstanding invocations once one completes. - Specifies the type of data returned by the functions. - The options to use for the execution. - The functions to be executed. - A result from executing one of the functions. - - - Process in parallel a matrix where every cell has a dependency on the cell above it and to its left. - The number of rows in the matrix. - The number of columns in the matrix. - Partition the matrix into this number of blocks along the rows. - Partition the matrix into this number of blocks along the columns. - The action to invoke for every block, supplied with the start and end indices of the rows and columns. - - - Process in parallel a matrix where every cell has a dependency on the cell above it and to its left. - The number of rows in the matrix. - The number of columns in the matrix. - The action to invoke for every cell, supplied with the row and column indices. - - - Repeatedly executes an operation in parallel while the specified condition evaluates to true. - The condition to evaluate. - The loop body. - - - Repeatedly executes an operation in parallel while the specified condition evaluates to true. - A ParallelOptions instance that configures the behavior of this operation. - The condition to evaluate. - The loop body. - - - Processes data in parallel, allowing the processing function to add more data to be processed. - Specifies the type of data being processed. - The initial set of data to be processed. - The operation to execute for each value. - - - Processes data in parallel, allowing the processing function to add more data to be processed. - Specifies the type of data being processed. - A ParallelOptions instance that configures the behavior of this operation. - The initial set of data to be processed. - The operation to execute for each value. - - - - Partitions an enumerable into chunks based on user-supplied criteria. - - - - Creates a partitioner that chooses the next chunk size based on a user-supplied function. - The type of the data being partitioned. - The data being partitioned. - A function that determines the next chunk size based on the - previous chunk size. - A partitioner. - - - Creates a partitioner that always uses a user-specified chunk size. - The type of the data being partitioned. - The data being partitioned. - The chunk size to be used. - A partitioner. - - - Creates a partitioner that chooses chunk sizes between the user-specified min and max. - The type of the data being partitioned. - The data being partitioned. - The minimum chunk size to use. - The maximum chunk size to use. - A partitioner. - - - - Partitions an enumerable into chunks based on user-supplied criteria. - - - - - Partitions the underlying collection into the specified number of orderable partitions. - - The number of partitions to create. - An object that can create partitions over the underlying data source. - - - - Creates an object that can partition the underlying collection into a variable number of - partitions. - - - An object that can create partitions over the underlying data source. - - - - Gets whether additional partitions can be created dynamically. - - - Partitions a data source one item at a time. - - - Creates a partitioner for an enumerable that partitions it one item at a time. - Specifies the type of data contained in the enumerable. - The source enumerable to be partitioned. - The partitioner. - - - Partitions an enumerable one item at a time. - Specifies the type of data contained in the list. - - - The enumerable to be partitioned. - - - Initializes the partitioner. - The enumerable to be partitioned. - - - Gets a list of the specified static number of partitions. - The list of created partitions ready to be iterated. - - - Gets whether this partitioner supports dynamic partitioning (it does). - - - Dynamically generates a partitions on a shared enumerator. - - - The source enumerator shared amongst all partitions. - - - The next available position to be yielded. - - - The number of partitions remaining to be disposed, potentially including this dynamic generator. - - - Whether this dynamic partitioner has been disposed. - - - Initializes the dynamic generator. - The enumerator shared by all partitions. - Whether this generator will be disposed. - - - Closes the shared enumerator if all other partitions have completed. - - - Increments the number of partitions in use and returns a new partition. - The new partition. - - - Creates a partition. - The new partition. - - - Partitions a list one item at a time. - Specifies the type of data contained in the list. - - - The list to be partitioned. - - - Initializes the partitioner. - The list to be partitioned. - - - Gets a list of the specified static number of partitions. - The static number of partitions to create. - The list of created partitions ready to be iterated. - - - Creates a dynamic partitioner for creating a dynamic number of partitions. - The dynamic partitioner. - - - An enumerable that creates individual enumerators that all work together to partition the list. - The list being partitioned. - An integer shared between partitions denoting the next available index in the source. - An enumerable that generates enumerators which participate in partitioning the list. - - - Gets whether this partitioner supports dynamic partitioning (it does). - - - Provides concurrent and exclusive task schedulers that coordinate. - - - Synchronizes all activity in this type and its generated schedulers. - - - The parallel options used by the asynchronous task and parallel loops. - - - The scheduler used to queue and execute "reader" tasks that may run concurrently with other readers. - - - The scheduler used to queue and execute "writer" tasks that must run exclusively while no other tasks for this interleave are running. - - - Whether this interleave has queued its processing task. - - - Whether the exclusive processing of a task should include all of its children as well. - - - Initialies the ConcurrentExclusiveInterleave. - - - Initialies the ConcurrentExclusiveInterleave. - Whether the exclusive processing of a task should include all of its children as well. - - - Initialies the ConcurrentExclusiveInterleave. - The target scheduler on which this interleave should execute. - - - Initialies the ConcurrentExclusiveInterleave. - The target scheduler on which this interleave should execute. - Whether the exclusive processing of a task should include all of its children as well. - - - Notifies the interleave that new work has arrived to be processed. - Must only be called while holding the lock. - - - The body of the async processor to be run in a Task. Only one should be running at a time. - This has been separated out into its own method to improve the Parallel Tasks window experience. - - - Runs a concurrent task. - The task to execute. - This has been separated out into its own method to improve the Parallel Tasks window experience. - - - - Gets an enumerable that yields waiting concurrent tasks one at a time until - either there are no more concurrent tasks or there are any exclusive tasks. - - - - - Gets an enumerable that yields all of the exclusive tasks one at a time. - - - - - Gets a TaskScheduler that can be used to schedule tasks to this interleave - that may run concurrently with other tasks on this interleave. - - - - - Gets a TaskScheduler that can be used to schedule tasks to this interleave - that must run exclusively with regards to other tasks on this interleave. - - - - Gets the number of tasks waiting to run exclusively. - - - Gets the number of tasks waiting to run concurrently. - - - Provides a debug view for ConcurrentExclusiveInterleave. - - - The interleave being debugged. - - - Initializes the debug view. - The interleave being debugged. - - - Gets the number of tasks waiting to run concurrently. - - - Gets a description of the processing task for debugging purposes. - - - - A scheduler shim used to queue tasks to the interleave and execute those tasks on request of the interleave. - - - - The parent interleave. - - - The maximum concurrency level for the scheduler. - - - Whether a Task is currently being processed on this thread. - - - Initializes the scheduler. - The parent interleave. - The queue to store queued tasks into. - - - - Queues a task to the scheduler. - The task to be queued. - - - Executes a task on this scheduler. - The task to be executed. - - - Tries to execute the task synchronously on this scheduler. - The task to execute. - Whether the task was previously queued to the scheduler. - true if the task could be executed; otherwise, false. - - - Gets for debugging purposes the tasks scheduled to this scheduler. - An enumerable of the tasks queued. - - - Gets the maximum concurrency level this scheduler is able to support. - - - Gets the queue of tasks for this scheduler. - - - Provides a task scheduler that runs tasks on the current thread. - - - Runs the provided Task synchronously on the current thread. - The task to be executed. - - - Runs the provided Task synchronously on the current thread. - The task to be executed. - Whether the Task was previously queued to the scheduler. - True if the Task was successfully executed; otherwise, false. - - - Gets the Tasks currently scheduled to this scheduler. - An empty enumerable, as Tasks are never queued, only executed. - - - Gets the maximum degree of parallelism for this scheduler. - - - Provides a TaskScheduler that uses an I/O completion port for concurrency control. - - - The queue of tasks to be scheduled. - - - The I/O completion port to use for concurrency control. - - - Whether the current thread is a scheduler thread. - - - Event used to wait for all threads to shutdown. - - - Initializes the IOCompletionPortTaskScheduler. - The maximum number of threads in the scheduler to be executing concurrently. - The number of threads to have available in the scheduler for executing tasks. - - - Dispose of the scheduler. - - - Gets a list of all tasks scheduled to this scheduler. - An enumerable of all scheduled tasks. - - - Queues a task to this scheduler for execution. - The task to be executed. - - - Try to execute a task on the current thread. - The task to execute. - Whether the task was previously queued to this scheduler. - Whether the task was executed. - - - Provides a simple managed wrapper for an I/O completion port. - - - Infinite timeout value to use for GetQueuedCompletedStatus. - - - An invalid file handle value. - - - An invalid I/O completion port handle value. - - - The I/O completion porth handle. - - - Initializes the I/O completion port. - The maximum concurrency level allowed by the I/O completion port. - - - Clean up. - - - Notify that I/O completion port that new work is available. - - - Waits for an item on the I/O completion port. - true if an item was available; false if the completion port closed before an item could be retrieved. - - - - Creates an input/output (I/O) completion port and associates it with a specified file handle, - or creates an I/O completion port that is not yet associated with a file handle, allowing association at a later time. - - An open file handle or INVALID_HANDLE_VALUE. - A handle to an existing I/O completion port or NULL. - The per-handle user-defined completion key that is included in every I/O completion packet for the specified file handle. - The maximum number of threads that the operating system can allow to concurrently process I/O completion packets for the I/O completion port. - If the function succeeds, the return value is the handle to an I/O completion port. If the function fails, the return value is NULL. - - - Attempts to dequeue an I/O completion packet from the specified I/O completion port. - A handle to the completion port. - A pointer to a variable that receives the number of bytes transferred during an I/O operation that has completed. - A pointer to a variable that receives the completion key value associated with the file handle whose I/O operation has completed. - A pointer to a variable that receives the address of the OVERLAPPED structure that was specified when the completed I/O operation was started. - The number of milliseconds that the caller is willing to wait for a completion packet to appear at the completion port. - Returns nonzero (TRUE) if successful or zero (FALSE) otherwise. - - - Posts an I/O completion packet to an I/O completion port. - A handle to the completion port. - The value to be returned through the lpNumberOfBytesTransferred parameter of the GetQueuedCompletionStatus function. - The value to be returned through the lpCompletionKey parameter of the GetQueuedCompletionStatus function. - The value to be returned through the lpOverlapped parameter of the GetQueuedCompletionStatus function. - If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. - - - - Provides a task scheduler that ensures a maximum concurrency level while - running on top of the ThreadPool. - - - - Whether the current thread is processing work items. - - - The list of tasks to be executed. - - - The maximum concurrency level allowed by this scheduler. - - - Whether the scheduler is currently processing work items. - - - - Initializes an instance of the LimitedConcurrencyLevelTaskScheduler class with the - specified degree of parallelism. - - The maximum degree of parallelism provided by this scheduler. - - - Queues a task to the scheduler. - The task to be queued. - - - - Informs the ThreadPool that there's work to be executed for this scheduler. - - - - Attempts to execute the specified task on the current thread. - The task to be executed. - - Whether the task could be executed on the current thread. - - - Attempts to remove a previously scheduled task from the scheduler. - The task to be removed. - Whether the task could be found and removed. - - - Gets an enumerable of the tasks currently scheduled on this scheduler. - An enumerable of the tasks currently scheduled. - - - Gets the maximum concurrency level supported by this scheduler. - - - - Provides a task scheduler that ensures only one task is executing at a time, and that tasks - execute in the order that they were queued. - - - - Initializes an instance of the OrderedTaskScheduler class. - - - - Provides a TaskScheduler that provides control over priorities, fairness, and the underlying threads utilized. - - - - - A sorted list of round-robin queue lists. Tasks with the smallest priority value - are preferred. Priority groups are round-robin'd through in order of priority. - - - - Cancellation token used for disposal. - - - - The maximum allowed concurrency level of this scheduler. If custom threads are - used, this represents the number of created threads. - - - - Whether we're processing tasks on the current thread. - - - The scheduler onto which actual work is scheduled. - - - The queue of tasks to process when using an underlying target scheduler. - - - The number of Tasks that have been queued or that are running whiel using an underlying scheduler. - - - The threads used by the scheduler to process work. - - - The collection of tasks to be executed on our custom threads. - - - Initializes the scheduler. - - - Initializes the scheduler. - The target underlying scheduler onto which this sceduler's work is queued. - - - Initializes the scheduler. - The target underlying scheduler onto which this sceduler's work is queued. - The maximum degree of concurrency allowed for this scheduler's work. - - - Initializes the scheduler. - The number of threads to create and use for processing work items. - - - Initializes the scheduler. - The number of threads to create and use for processing work items. - The name to use for each of the created threads. - A Boolean value that indicates whether to use foreground threads instead of background. - The priority to assign to each thread. - The apartment state to use for each thread. - The stack size to use for each thread. - An initialization routine to run on each thread. - A finalization routine to run on each thread. - - - The dispatch loop run by all threads in this scheduler. - An initialization routine to run when the thread begins. - A finalization routine to run before the thread ends. - - - Find the next task that should be executed, based on priorities and fairness and the like. - The found task, or null if none was found. - - The scheduler associated with the found task. Due to security checks inside of TPL, - this scheduler needs to be used to execute that task. - - - - Queues a task to the scheduler. - The task to be queued. - - - - Process tasks one at a time in the best order. - This should be run in a Task generated by QueueTask. - It's been separated out into its own method to show up better in Parallel Tasks. - - - - Notifies the pool that there's a new item to be executed in one of the round-robin queues. - - - Tries to execute a task synchronously on the current thread. - The task to execute. - Whether the task was previously queued. - true if the task was executed; otherwise, false. - - - Gets the tasks scheduled to this scheduler. - An enumerable of all tasks queued to this scheduler. - This does not include the tasks on sub-schedulers. Those will be retrieved by the debugger separately. - - - Initiates shutdown of the scheduler. - - - Creates and activates a new scheduling queue for this scheduler. - The newly created and activated queue at priority 0. - - - Creates and activates a new scheduling queue for this scheduler. - The priority level for the new queue. - The newly created and activated queue at the specified priority. - - - Removes a scheduler from the group. - The scheduler to be removed. - - - Gets the number of queues currently activated. - - - Gets the number of tasks currently scheduled. - - - Gets the maximum concurrency level to use when processing tasks. - - - Debug view for the QueuedTaskScheduler. - - - The scheduler. - - - Initializes the debug view. - The scheduler. - - - Gets all of the Tasks queued to the scheduler directly. - - - Gets the prioritized and fair queues. - - - A group of queues a the same priority level. - - - The starting index for the next round-robin traversal. - - - Creates a search order through this group. - An enumerable of indices for this group. - - - Provides a scheduling queue associatd with a QueuedTaskScheduler. - - - The scheduler with which this pool is associated. - - - The work items stored in this queue. - - - Whether this queue has been disposed. - - - Gets the priority for this queue. - - - Initializes the queue. - The priority associated with this queue. - The scheduler with which this queue is associated. - - - Gets the tasks scheduled to this scheduler. - An enumerable of all tasks queued to this scheduler. - - - Queues a task to the scheduler. - The task to be queued. - - - Tries to execute a task synchronously on the current thread. - The task to execute. - Whether the task was previously queued. - true if the task was executed; otherwise, false. - - - Runs the specified ask. - The task to execute. - - - Signals that the queue should be removed from the scheduler as soon as the queue is empty. - - - Gets the number of tasks waiting in this scheduler. - - - Gets the maximum concurrency level to use when processing tasks. - - - A debug view for the queue. - - - The queue. - - - Initializes the debug view. - The queue to be debugged. - - - Gets the priority of this queue in its associated scheduler. - - - Gets the ID of this scheduler. - - - Gets all of the tasks scheduled to this queue. - - - Gets the QueuedTaskScheduler with which this queue is associated. - - - Provides a task scheduler that supports reprioritizing previously queued tasks. - - - Queues a task to the scheduler. - The task to be queued. - - - Reprioritizes a previously queued task to the front of the queue. - The task to be reprioritized. - Whether the task could be found and moved to the front of the queue. - - - Reprioritizes a previously queued task to the back of the queue. - The task to be reprioritized. - Whether the task could be found and moved to the back of the queue. - - - Removes a previously queued item from the scheduler. - The task to be removed. - Whether the task could be removed from the scheduler. - - - Picks up and executes the next item in the queue. - Ignored. - - - Executes the specified task inline. - The task to be executed. - Whether the task was previously queued. - Whether the task could be executed inline. - - - Gets all of the tasks currently queued to the scheduler. - An enumerable of the tasks currently queued to the scheduler. - - - Enables the creation of a group of schedulers that support round-robin scheduling for fairness. - - - Creates a new scheduler as part of this group. - The new scheduler. - - - Removes a scheduler from the group. - The scheduler to be removed. - - - Notifies the ThreadPool that there's a new item to be executed. - - - Gets a collection of all schedulers in this group. - - - A scheduler that participates in round-robin scheduling. - - - Provides a scheduler that uses STA threads. - - - Stores the queued tasks to be executed by our pool of STA threads. - - - The STA threads used by the scheduler. - - - Initializes a new instance of the StaTaskScheduler class with the specified concurrency level. - The number of threads that should be created and used by this scheduler. - - - Queues a Task to be executed by this scheduler. - The task to be executed. - - - Provides a list of the scheduled tasks for the debugger to consume. - An enumerable of all tasks currently scheduled. - - - Determines whether a Task may be inlined. - The task to be executed. - Whether the task was previously queued. - true if the task was successfully inlined; otherwise, false. - - - - Cleans up the scheduler by indicating that no more tasks will be queued. - This method blocks until all threads successfully shutdown. - - - - Gets the maximum concurrency level supported by this scheduler. - - - Provides a task scheduler that targets a specific SynchronizationContext. - - - The queue of tasks to execute, maintained for debugging purposes. - - - The target context under which to execute the queued tasks. - - - Initializes an instance of the SynchronizationContextTaskScheduler class. - - - - Initializes an instance of the SynchronizationContextTaskScheduler class - with the specified SynchronizationContext. - - The SynchronizationContext under which to execute tasks. - - - Queues a task to the scheduler for execution on the I/O ThreadPool. - The Task to queue. - - - Tries to execute a task on the current thread. - The task to be executed. - Ignored. - Whether the task could be executed. - - - Gets an enumerable of tasks queued to the scheduler. - An enumerable of tasks queued to the scheduler. - - - Gets the maximum concurrency level supported by this scheduler. - - - Provides a task scheduler that dedicates a thread per task. - - - Gets the tasks currently scheduled to this scheduler. - This will always return an empty enumerable, as tasks are launched as soon as they're queued. - - - Starts a new thread to process the provided task. - The task to be executed. - - - Runs the provided task on the current thread. - The task to be executed. - Ignored. - Whether the task could be executed on the current thread. - - - Provides a work-stealing scheduler. - - - Initializes a new instance of the WorkStealingTaskScheduler class. - This constructors defaults to using twice as many threads as there are processors. - - - Initializes a new instance of the WorkStealingTaskScheduler class. - The number of threads to use in the scheduler. - - - Queues a task to the scheduler. - The task to be scheduled. - - - Executes a task on the current thread. - The task to be executed. - Ignored. - Whether the task could be executed. - - - Gets all of the tasks currently scheduled to this scheduler. - An enumerable containing all of the scheduled tasks. - - - Adds a work-stealing queue to the set of queues. - The queue to be added. - - - Remove a work-stealing queue from the set of queues. - The work-stealing queue to remove. - - - - The dispatch loop run by each thread in the scheduler. - - - - Signal the scheduler to shutdown and wait for all threads to finish. - - - Gets the maximum concurrency level supported by this scheduler. - - - A work-stealing queue. - Specifies the type of data stored in the queue. - - - - This exception is returned from the - CallMethod method in the server-side DataPortal - and contains the exception thrown by the - underlying business object method that was - being invoked. - - - - - Creates an instance of the object. - - Message text describing the exception. - Inner exception object. - - - - Creates an instance of the object for deserialization. - - Serialization info. - Serialiation context. - - - - Serializes the object. - - Serialization info. - Serialization context. - - - - Get the stack trace from the original - exception. - - - - - - - - Delegate for a dynamic constructor method. - - - - - Delegate for a dynamic method. - - - Object containg method to invoke. - - - Parameters passed to method. - - - - - Delegate for getting a value. - - Target object. - The value from the target. - - - - Delegate for setting a value. - - Target object. - Argument value. - - - - An accessor class for . - - - - - A base class for member accessors. - - - - - An interface for member accessors. - - - - - Returns the value of the member. - - The object whose member value will be returned. - The member value for the instance parameter. - - - - Sets the value of the member. - - The object whose member value will be set. - The new value for this member. - - - - Gets the type of the member. - - The type of the member. - - - - Gets the member info. - - The member info. - - - - Gets the name of the member. - - The name of the member. - - - - Gets a value indicating whether this member has getter. - - - true if this member has getter; otherwise, false. - - - - - Gets a value indicating whether this member has setter. - - - true if this member has setter; otherwise, false. - - - - - Returns the value of the member. - - The object whose member value will be returned. - - The member value for the instance parameter. - - - - - Sets the value of the member. - - The object whose member value will be set. - The new value for this member. - - - - Determines whether the specified is equal to this instance. - - The to compare with this instance. - - true if the specified is equal to this instance; otherwise, false. - - - - - Determines whether the specified is equal to this instance. - - The to compare with this instance. - - true if the specified is equal to this instance; otherwise, false. - - - - - Returns a hash code for this instance. - - - A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - - - - - Gets the type of the member. - - The type of the member. - - - - Gets the member info. - - The member info. - - - - Gets the name of the member. - - The name of the member. - - - - Gets a value indicating whether this member has getter. - - true if this member has getter; otherwise, false. - - - - Gets a value indicating whether this member has setter. - - true if this member has setter; otherwise, false. - - - - Initializes a new instance of the class. - - The instance to use for this accessor. - - - - Returns the value of the member. - - The object whose member value will be returned. - - The member value for the instance parameter. - - - - - Sets the value of the member. - - The object whose member value will be set. - The new value for this member. - - - - Gets the type of the member. - - The type of the member. - - - - Gets the member info. - - The member info. - - - - Gets the name of the member. - - The name of the member. - - - - Gets a value indicating whether this member has getter. - - true if this member has getter; otherwise, false. - - - - Gets a value indicating whether this member has setter. - - true if this member has setter; otherwise, false. - - - - A class for late bound operations on a type. - - - - - Searches for the public property with the specified name. - - The to search for the property in. - The name of the property to find. - - An instance for the property if found; otherwise null. - - - - - Searches for the specified property, using the specified binding constraints. - - The to search for the property in. - The name of the property to find. - A bitmask comprised of one or more that specify how the search is conducted. - - An instance for the property if found; otherwise null. - - - - - Searches for the field with the specified name. - - The to search for the field in. - The name of the field to find. - - An instance for the field if found; otherwise null. - - - - - Searches for the field, using the specified binding constraints. - - The to search for the field in. - The name of the field to find. - A bitmask comprised of one or more that specify how the search is conducted. - - An instance for the field if found; otherwise null. - - - - - Sets the property value with the specified name. - - The object whose property value will be set. - The name of the property to set. - The new value to be set. - - - - Sets the field value with the specified name. - - The object whose field value will be set. - The name of the field to set. - The new value to be set. - - - - Returns the value of the property with the specified name. - - The object whose property value will be returned. - The name of the property to read. - The value of the property. - - - - Returns the value of the field with the specified name. - - The object whose field value will be returned. - The name of the field to read. - The value of the field. - - - - Creates an instance of the specified type. - - The type to create. - A new instance of the specified type. - - - - Provides methods to dynamically find and call methods. - - - - - Uses reflection to create an object using its - default constructor. - - Type of object to create. - - - - Invokes a property getter using dynamic - method invocation. - - Target object. - Property to invoke. - - - - - Invokes a property setter using dynamic - method invocation. - - Target object. - Property to invoke. - New value for property. - - - - Returns an array of Type objects corresponding - to the type of parameters provided. - - - Parameter values. - - - - - Uses reflection to dynamically invoke a method - if that method is implemented on the target object. - - - Object containing method. - - - Name of the method. - - - Parameters to pass to method. - - - - - Uses reflection to dynamically invoke a method, - throwing an exception if it is not - implemented on the target object. - - - Object containing method. - - - Name of the method. - - - Parameters to pass to method. - - - - - Uses reflection to dynamically invoke a method, - throwing an exception if it is not - implemented on the target object. - - - Object containing method. - - - MethodInfo for the method. - - - Parameters to pass to method. - - - - - Uses reflection to dynamically invoke a method, - throwing an exception if it is not implemented - on the target object. - - - Object containing method. - - - MethodHandle for the method. - - - Parameters to pass to method. - - - - - Uses reflection to locate a matching method - on the target object. - - - Type of object containing method. - - - Name of the method. - - - Parameters to pass to method. - - - - - Returns information about the specified - method, even if the parameter types are - generic and are located in an abstract - generic base class. - - - Type of object containing method. - - - Name of the method. - - - Parameter types to pass to method. - - - - - Returns information about the specified - method, finding the method based purely - on the method name and number of parameters. - - - Type of object containing method. - - - Name of the method. - - - Number of parameters to pass to method. - - - - - Finds a by name ignoring case. - - The type to search. - Name of the property. - A matching the property name. - - FindProperty will first try to get a property matching the name and case of the - property name specified. If a property cannot be found, all the properties will - be searched ignoring the case of the name. - - - - - Settings class for the . - - - - - Initializes a new instance of the class. - - - - - Gets or sets a value indicating whether the will suppress exceptions when copying. - - true to suppress exceptions; otherwise, false. - - - - Gets or sets the list of property names to ignore when is copying properties. - - The ignore list. - - - - Gets or sets a value indicating whether will use dynamic cache. - - true to use dynamic cache; otherwise, false. - - - - An accessor class for . - - - - - Initializes a new instance of the class. - - The instance to use for this accessor. - - - - Returns the value of the member. - - The object whose member value will be returned. - - The member value for the instance parameter. - - - - - Sets the value of the member. - - The object whose member value will be set. - The new value for this member. - - - - Gets the type of the member. - - The type of the member. - - - - Gets the member info. - - The member info. - - - - Gets the name of the member. - - The name of the member. - - - - Gets a value indicating whether this member has getter. - - true if this member has getter; otherwise, false. - - - - Gets a value indicating whether this member has setter. - - true if this member has setter; otherwise, false. - - - - A class holding all the accessors for a . - - - - - Initializes a new instance of the class. - - The this accessor is for. - - - - Creates a new instance of accessors type. - - A new instance of accessors type. - - - - Searches for the public property with the specified name. - - The name of the property to find. - An instance for the property if found; otherwise null. - - - - Searches for the specified property, using the specified binding constraints. - - The name of the property to find. - A bitmask comprised of one or more that specify how the search is conducted. - - An instance for the property if found; otherwise null. - - - - - Searches for the specified field with the specified name. - - The name of the field to find. - - An instance for the field if found; otherwise null. - - - - - Searches for the specified field, using the specified binding constraints. - - The name of the field to find. - A bitmask comprised of one or more that specify how the search is conducted. - - An instance for the field if found; otherwise null. - - - - - Gets the this accessor is for. - - The this accessor is for. - - - - A class to parse an assembly-qualified name of a Type into its parts. - - - - - Parses an assembly-qualified name of a Type into its parts. - - The assembly-qualified name of the Type. - - - - - The rule operator for criteria. - - - - - A comparison for equality. - - - - - A comparison for greater than. - - - - - A comparison for greater than or equal to. - - - - - A comparison for less than. - - - - - A comparison for less than or equal to. - - - - - A comparison for inequality. - - - - - A comparison for partial match - - - - - A default lock provider - - - - - A base class for JobLockProviders. - - - - - Acquires a lock on specified job name. - - Name of the lock, usually the job name. - A object that will release the lock when disposed. - - - - Releases the specified job lock. - - The job lock. - - - - Acquires a lock on specified job name. - - Name of the lock, usually the job name. - A object that will release the lock when disposed. - - - - Releases the specified job lock. - - The job lock. - - - - Interface for a scheduled job. - - - - - Runs this job. - - The job context. - - A instance indicating the results of the job. - - - - - Cancels this job. - - - - - Gets or sets the name of the job. - - The name of the job. - - - - Gets or sets the description for the job. - - The description for the job. - - - - Gets or sets the group for the job. - - The group for the job. - - - - Gets or sets the timer interval. - - - - - Gets or sets a value indicating whether Interval is a time of day. - - - true if Interval is time of day; otherwise, false. - - - - - Gets or sets the assembly type that contains the job to run. - - - - - Gets or sets a value indicating whether to keep alive the instance between job runs. - - true to keep alive instance; otherwise, false. - - Setting this to true, the default value, will keep the instance alive between runs. - - - - - Gets or sets the name of the provider that is used to lock the job when running. - - The type to use to lock the job. - - - - Gets or sets the job history provider. - - The job history provider. - - - - Gets the arguments. - - The arguments. - - - - A class representing a scheduled job. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - - - - Starts this job timer. - - - - - Stops this job timer. - - - - - Stops this job timer. - - if set to true cancel running job. - - - - Runs this job asynchronous. - - Can be used to speed up the job when an event occurs. - - - - Runs this job at the specified time. - - The run time. - Can be used to speed up the job when an event occurs. - - - - Runs this job. - - - - - Gets the name. - - The name. - - - - Gets the description. - - The description. - - - - Gets the group name. - - The group name. - - - - Gets the arguments. - - The arguments. - - - - Gets the job interval. - - The job interval. - - - - Gets a value indicating whether Interval is a time of day. - - - true if Interval is time of day; otherwise, false. - - - - - Gets a value indicating whether to keep alive the job instance. - - true to keep alive; otherwise, false. - - - - Gets the last run start time. - - The last run start time. - This property is thread safe. - - - - Gets the last run finish time. - - The last run finish time. - This property is thread safe. - - - - Gets the last run duration. - - The last run duration. - This property is thread safe. - - - - Gets the next run time. - - The next run time. - This property is thread safe. - - - - Gets the status. - - The status. - This property is thread safe. - - - - Gets the last status. - - The last status. - This property is thread safe. - - - - Gets the last result. - - The last result. - This property is thread safe. - - - - Gets a value indicating whether this job is busy. - - true if this job is busy; otherwise, false. - This property is thread safe. - - - - A base class for jobs - - - - - Runs this job. - - The job context. - - A instance indicating the results of the job. - - - - - Cancels this job. - - - - - Gets a value indicating whether a cancel request is pending. - - true if cancel is pending; otherwise, false. - - - - A keyed collection of Jobs. - - - - - When implemented in a derived class, extracts the key from the specified element. - - The element from which to extract the key. - The key for the specified element. - - - - A class holding job configuration. - - - - - Initializes a new instance of the class. - - - - - Gets or sets the name of the job. - - The name of the job. - - - - Gets or sets the description for the job. - - The description for the job. - - - - Gets or sets the group for the job. - - The group for the job. - - - - Gets or sets the timer interval. - - - - - Gets or sets a value indicating whether Interval is a time of day. - - true if Interval is time of day; otherwise, false. - - - - Gets or sets the assembly type that contains the job to run. - - - - - Gets or sets a value indicating whether to keep alive the instance between job runs. - - true to keep alive instance; otherwise, false. - - Setting this to true, the default value, will keep the instance alive between runs. - - - - - Gets or sets the name of the provider that is used to lock the job when running. - - The type to use to lock the job. - - - - Gets or sets the job history provider. - - The job history provider. - - - - Gets the arguments. - - The arguments. - - - - Initializes a new instance of the class. - - - - - Updates the status. - - The message. - - - - Gets the name of the job. - - The name of the job. - - - - Gets the description for the job. - - The description for the job. - - - - Gets the arguments. - - The arguments. - - - - Gets the last status. - - The last status. - - - - Gets the last run time. - - The last run time. - - - - Job event arguments. - - - - - Initializes a new instance of the class. - - Name of the job. - The action. - - - - Initializes a new instance of the class. - - Name of the job. - The action. - The job id. - - - - Returns a that represents this instance. - - - A that represents this instance. - - - - - Gets or sets the name of the job. - - The name of the job. - - - - Gets or sets the action. - - The action. - - - - Gets or sets the job id. - - The job id. - - - - A provider to save and restore the history for a job. - - - - - Restores the latest job history from the provider. - - The job to restore the history to. - - - - Saves the history to the provider. - - The job to save the history on. - - - - A collection. - - - - - Adds a provider to the collection. - - The provider to be added. - The collection is read-only. - - is null. - The of is null.- or -The length of the of is less than 1. - - - - - - - Gets the with the specified name. - - An instance of . - - - - A base class for job providers. - - - - - Gets an list jobs. - - An list jobs. - - - - Determines whether a job reload is required. - - The last load. - - true if a job reload is required; otherwise, false. - - - If true is return, the will call Reload that will - in turn call on this provider. - - - - - A configuration element collection class for . - - - - - When overridden in a derived class, creates a new . - - - A new . - - - - - Gets the element key for a specified configuration element when overridden in a derived class. - - The to return the key for. - - An that acts as the key for the specified . - - - - - Returns an enumerator that iterates through the collection. - - - A that can be used to iterate through the collection. - - 1 - - - - A Http module class to start the . - - - - - Disposes of the resources (other than memory) used by the module that implements . - - - - - Initializes a module and prepares it to handle requests. - - An that provides access to the methods, properties, and events common to all application objects within an ASP.NET application - - - - A base class representing a job lock. - - - - - Initializes a new instance of the class. - - The lock provider to call when disposing. - Name of the lock. - if set to true lock was acquired. - - - - Disposes the unmanaged resources. - - - - - Sets the lock to released. - - - - - Returns a that represents the current . - - - A that represents the current . - - - - - Gets the name of the lock. - - The name of the lock. - - - - Gets a value indicating whether the lock was acquired successfully. - - true if the was lock acquired; otherwise, false. - - - - A class to manage the jobs for the Scheduler. - - - - - Raises the event. - - The instance containing the event data. - - - - Raises the event. - - The instance containing the event data. - - - - Raises the event. - - The instance containing the event data. - - - - Raises the event. - - The instance containing the event data. - - - - Raises the event. - - The instance containing the event data. - - - - Raises the event. - - The instance containing the event data. - - - - Initializes a new instance of the class. - - - - - Initializes the jobs for this manager. - - - - - Starts all jobs in this manager. - - - - - Stops all jobs in this manager. - - - - - Reload by stopping all jobs and reloading configuration. All the jobs will be restarted after reload. - - - - - Reload by stopping all jobs and reloading configuration. - - if set to true start the jobs after reload. - - - - Occurs when the JobManager starts. - - - - - - Occurs when the JobManager stops. - - - - - - Occurs when the Job is starting. - - - - - - Occurs when the Job is stopping. - - - - - - Occurs when the Job is running. - - - - - - Occurs when the Job run is completed. - - - - - - Gets the number of active jobs. - - The number of active jobs. - - - - Gets the collection of jobs. - - The collection of jobs. - - - - Gets the current instance of . - - The current instance. - - - - The job configuration element - - - - - Initializes a new instance of the class. - - - - - Gets a value indicating whether an unknown attribute is encountered during deserialization. - - The name of the unrecognized attribute. - The value of the unrecognized attribute. - - true when an unknown attribute is encountered while deserializing; otherwise, false. - - - - - Gets or sets the name of the job. - - The name of the job. - - - - Gets or sets the description for the job. - - The description for the job. - - - - Gets or sets the group for the job. - - The group for the job. - - - - Gets or sets the timer interval. - - - - - Gets or sets a value indicating whether Interval is a time of day. - - true if Interval is time of day; otherwise, false. - - - - Gets or sets the assembly type that contains the job to run. - - - - - Gets or sets a value indicating whether to keep alive the instance between job runs. - - true to keep alive instance; otherwise, false. - - Setting this to true, the default value, will keep the instance alive between runs. - - - - - Gets or sets the name of the provider that is used to lock the job when running. - - The type to use to lock the job. - - - - Gets or sets the job history provider. - - The job history provider. - - - - Gets the arguments. - - The arguments. - - - - Initializes a new instance of the class. - - - - - Gets or sets the poll interval for calling .IsReloadRequired. - - Set to .Zero to disable reload checking. - - - - Gets the providers to configure jobs. - - The job configuration providers. - - - - Gets the job lock providers. - - The job lock providers. - - - - The jobs to schedule. - - - - - A class representing the results from a job. - - - - - Gets or sets a value indicating whether this is cancelled. - - true if cancelled; otherwise, false. - - - - Gets or sets the error for the job. - - The error. - - - - Gets or sets the result of the job. - - The result of the job. - - - - Job run completed event arguments. - - - - - Initializes a new instance of the class. - - Name of the job. - The action. - The time the job run started. - The time the job run ended. - The result of the job run. - The status of the job run. - - - - Initializes a new instance of the class. - - Name of the job. - The action. - The job id. - The time the job run started. - The time the job run ended. - The result of the job run. - The status of the job run. - - - - Returns a that represents this instance. - - - A that represents this instance. - - - - - Gets or sets the time that the job run started. - - The time the job run started. - - - - Gets or sets the time that the job run finished. - - The time the job run finished. - - - - Gets or sets the result of the job run. - - The result of the job run. - - - - Gets or sets the status of the job run. - - The status of the job run. - - - - Gets the duration of the job run. - - The duration of the job run. - - - - The status state for a job - - - - The default job status. - - - The job is stopped. - - - The job is running. - - - The job is waiting for the next run time. - - - The job result was in error. - - - The job result was completed. - - - The job result was canceled. - - - - Copy data from a source into a target object by copying public property values. - - - - - - Copies values from the source into the properties of the target. - - An object containing the source values. - An object with properties to be set from the source. - - The property names and types of the source object must match the property names and types - on the target object. Source properties may not be indexed. - Target properties may not be readonly or indexed. - - - - - Copies values from the source into the properties of the target. - - An object containing the source values. - An object with properties to be set from the source. - A list of property names to ignore. - These properties will not be set on the target object. - - The property names and types of the source object must match the property names and types - on the target object. Source properties may not be indexed. - Target properties may not be readonly or indexed. - - - - - Copies values from the source into the properties of the target. - - An object containing the source values. - An object with properties to be set from the source. - A list of property names to ignore. - These properties will not be set on the target object. - If , any exceptions will be suppressed. - - - The property names and types of the source object must match the property names and types - on the target object. Source properties may not be indexed. - Target properties may not be readonly or indexed. - - Properties to copy are determined based on the source object. Any properties - on the source object marked with the equal - to false are ignored. - - - - - - Copies values from the source into the properties of the target. - - An object containing the source values. - An object with properties to be set from the source. - The settings to use when copying properties. - - - The property names and types of the source object must match the property names and types - on the target object. Source properties may not be indexed. - Target properties may not be readonly or indexed. - - Properties to copy are determined based on the source object. Any properties - on the source object marked with the equal - to false are ignored. - - - - - - Copies values from the source into the target . - - The source object. - The target . - - - - Copies values from the source into the target . - - The source object. - The target . - A list of property names to ignore. - These properties will not be added to the targeted . - - - - Copies values from the source into the target . - - The source object. - The target . - A list of property names to ignore. - These properties will not be added to the targeted . - If , any exceptions will be suppressed. - - - - Copies values from the source into the target . - - The source object. - The target . - The settings to use when copying properties. - - - - Copies values from the into the properties of the target. - - The source. - The target object. - - - - Copies values from the into the properties of the target. - - The source. - The target object. - A list of property names to ignore. - These properties will not be set on the target object. - - - - Copies values from the into the properties of the target. - - The source. - The target object. - A list of property names to ignore. - These properties will not be set on the target object. - If , any exceptions will be suppressed. - - - - Copies values from the into the properties of the target. - - The source. - The target object. - The settings to use when copying properties. - - - - Copies values from the into the properties of the target. - - The source. - The target object. - - - - Copies values from the into the properties of the target. - - The source. - The target object. - A list of property names to ignore. - These properties will not be set on the target object. - - - - Copies values from the into the properties of the target. - - The source. - The target object. - A list of property names to ignore. - These properties will not be set on the target object. - If , any exceptions will be suppressed. - - - - Copies values from the into the properties of the target. - - The source. - The target object. - The settings to use when copying properties. - - - - Uses BinaryFormatter.Serialize to Clone an object. - - The source object. - A cloned copy of the object. - - - - Sets an object's property with the specified value, - converting that value to the appropriate type if possible. - - Object containing the property to set. - Name of the property to set. - Value to set into the property. - - - - Sets an object's property with the specified value, - converting that value to the appropriate type if possible. - - Object containing the property to set. - Name of the property to set. - Value to set into the property. - if set to true use dynamic cache. - - - - Gets an object's property value by name. - - Object containing the property to get. - Name of the property. - The value of the property. - - - - Gets an object's property value by name. - - Object containing the property to get. - Name of the property. - if set to true use dynamic cache. - The value of the property. - - - - Gets the underlying type dealing with . - - The type. - Returns a type dealing with . - - - - Attempts to coerce a value of one type into - a value of a different type. - - - Type to which the value should be coerced. - - - Original type of the value. - - - The value to coerce. - - - - If the desired type is a primitive type or Decimal, - empty string and null values will result in a 0 - or equivalent. - - - If the desired type is a type, empty string - and null values will result in a null result. - - - If the desired type is an enum the value's ToString() - result is parsed to convert into the enum value. - - - - - - Finds a by name ignoring case. - - The type to search. - Name of the property. - A matching the property name. - - FindProperty will first try to get a property matching the name and case of the - property name specified. If a property cannot be found, all the properties will - be searched ignoring the case of the name. - - - - - Writes the object's public fields and properties to Console.Out. - - The object that will be dumped. - - - - Writes the object's public fields and properties to Console.Out, excludes fields/properties found in exclusions. - - The object that will be dumped. - List of excluded fields/properties. - - - - A lock provider that only allows one job per . - - - - - Acquires a lock on specified job name. - - Name of the lock, usually the job name. - An object that will release the lock when disposed. - - - - Releases the specified job lock. - - The job lock. - - - - Represents a globally unique identifier (GUID) with a shorter string value. - - - - - A read-only instance of the ShortGuid class whose value - is guaranteed to be all zeroes. - - - - - Creates a ShortGuid from a base64 encoded string - - The encoded guid as a - base64 string - - - - Creates a ShortGuid from a Guid - - The Guid to encode - - - - Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object. - - - A 32-bit signed integer that indicates the relative order of the objects being compared. - - - An object to compare with this instance. - - - - - Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object. - - - A 32-bit signed integer that indicates the relative order of the objects being compared. - - - An object to compare with this instance. - - - - - Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object. - - - A 32-bit signed integer that indicates the relative order of the objects being compared. - - - An object to compare with this instance. - - - - - Returns the base64 encoded guid as a string - - - - - - Returns a value indicating whether this instance and a - specified Object represent the same type and value. - - The object to compare - - true if the specified is equal to this instance; otherwise, false. - - - - - Returns a value indicating whether this instance and a - specified Object represent the same type and value. - - The object to compare - - true if the specified is equal to this instance; otherwise, false. - - - - - Returns a value indicating whether this instance and a - specified Object represent the same type and value. - - The object to compare - - true if the specified is equal to this instance; otherwise, false. - - - - - Returns the HashCode for underlying Guid. - - - - - - Initialises a new instance of the ShortGuid class - - - - - - Creates a new instance of a Guid using the string value, - then returns the base64 encoded version of the Guid. - - An actual Guid string (i.e. not a ShortGuid) - - - - - Encodes the given Guid as a base64 string that is 22 - characters long. - - The Guid to encode - - - - - Decodes the given base64 string - - The base64 encoded string of a Guid - A new Guid - - - - Determines if both ShortGuids have the same underlying - Guid value. - - - - - - - - Determines if both ShortGuids do not have the - same underlying Guid value. - - - - - - - - Implicitly converts the ShortGuid to it's string equivilent - - - - - - - Implicitly converts the ShortGuid to it's Guid equivilent - - - - - - - Implicitly converts the string to a ShortGuid - - - - - - - Implicitly converts the Guid to a ShortGuid - - - - - - - Gets/sets the underlying Guid - - - - - Gets/sets the underlying base64 encoded string - - - - - Named string formatter. - - - - - Formats the specified input string. - - The input format string. - The source to replace the formant names with. - A formatted string. - - - - This class stores the caller call context in order to restore - it when the work item is executed in the thread pool environment. - - - - - Constructor - - - - - Captures the current thread context - - - - - - Applies the thread context stored earlier - - - - - - Represents an exception in case IWorkItemResult.GetResult has been canceled - - - - - Represents an exception in case IWorkItemResult.GetResult has been timed out - - - - - Represents an exception in case IWorkItemResult.GetResult has been timed out - - - - - A delegate that represents the method to run as the work item - - A state object for the method to run - - - - A delegate to call after the WorkItemCallback completed - - The work item result object - - - - A delegate to call when a WorkItemsGroup becomes idle - - A reference to the WorkItemsGroup that became idle - - - - IWorkItemsGroup interface - - - - - Get/Set the name of the WorkItemsGroup - - - - - IWorkItemResult interface - - - - - Get the result of the work item. - If the work item didn't run yet then the caller waits. - - The result of the work item - - - - Get the result of the work item. - If the work item didn't run yet then the caller waits until timeout. - - The result of the work item - On timeout throws WorkItemTimeoutException - - - - Get the result of the work item. - If the work item didn't run yet then the caller waits until timeout. - - The result of the work item - On timeout throws WorkItemTimeoutException - - - - Get the result of the work item. - If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - - Timeout in milliseconds, or -1 for infinite - - true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - - A cancel wait handle to interrupt the blocking if needed - The result of the work item - On timeout throws WorkItemTimeoutException - On cancel throws WorkItemCancelException - - - - Get the result of the work item. - If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - - The result of the work item - On timeout throws WorkItemTimeoutException - On cancel throws WorkItemCancelException - - - - Get the result of the work item. - If the work item didn't run yet then the caller waits. - - Filled with the exception if one was thrown - The result of the work item - - - - Get the result of the work item. - If the work item didn't run yet then the caller waits until timeout. - - The milliseconds timeout. - if set to true [exit context]. - Filled with the exception if one was thrown - The result of the work item - On timeout throws WorkItemTimeoutException - - - - Get the result of the work item. - If the work item didn't run yet then the caller waits until timeout. - - The timeout. - if set to true [exit context]. - Filled with the exception if one was thrown - The result of the work item - On timeout throws WorkItemTimeoutException - - - - Get the result of the work item. - If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - - Timeout in milliseconds, or -1 for infinite - - true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - - A cancel wait handle to interrupt the blocking if needed - Filled with the exception if one was thrown - The result of the work item - On timeout throws WorkItemTimeoutException - On cancel throws WorkItemCancelException - - - - Get the result of the work item. - If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - - The timeout. - if set to true [exit context]. - The cancel wait handle. - Filled with the exception if one was thrown - The result of the work item - On timeout throws WorkItemTimeoutException - On cancel throws WorkItemCancelException - - - - Cancel the work item if it didn't start running yet. - - Returns true on success or false if the work item is in progress or already completed - - - - Gets an indication whether the asynchronous operation has completed. - - - - - Gets an indication whether the asynchronous operation has been canceled. - - - - - Gets a user-defined object that qualifies or contains information about an asynchronous operation. - - - - - Get the work item's priority - - - - - Return the result, same as GetResult() - - - - - Returns the exception if occured otherwise returns null. - - - - - Specifies how a Lazy instance synchronizes access among multiple threads. - - - - - - - - - - - - - - - - - Provides support for lazy initialization. - - Specifies the type of element being laziliy initialized. - - - By default, all public and protected members of are thread-safe and may be used - concurrently from multiple threads. These thread-safety guarantees may be removed optionally and per instance - using parameters to the type's constructors. - - - - - - Initializes a new instance of the class that - uses 's default constructor for lazy initialization. - - - An instance created with this constructor may be used concurrently from multiple threads. - - - - - Initializes a new instance of the class that uses a - specified initialization function. - - - The invoked to produce the lazily-initialized value when it is - needed. - - is a null - reference (Nothing in Visual Basic). - - An instance created with this constructor may be used concurrently from multiple threads. - - - - - Initializes a new instance of the - class that uses 's default constructor and a specified thread-safety mode. - - true if this instance should be usable by multiple threads concurrently; false if the instance will only be used by one thread at a time. - - - - - Initializes a new instance of the - class that uses 's default constructor and a specified thread-safety mode. - - The lazy thread-safety mode mode - mode contains an invalid valuee - - - - Initializes a new instance of the class - that uses a specified initialization function and a specified thread-safety mode. - - - The invoked to produce the lazily-initialized value when it is needed. - - true if this instance should be usable by multiple threads concurrently; false if the instance will only be used by one thread at a time. - - is - a null reference (Nothing in Visual Basic). - - - - Initializes a new instance of the class - that uses a specified initialization function and a specified thread-safety mode. - - - The invoked to produce the lazily-initialized value when it is needed. - - The lazy thread-safety mode. - is - a null reference (Nothing in Visual Basic). - mode contains an invalid value. - - - - Static helper function that returns an object based on the given mode. it also throws an exception if the mode is invalid - - - - Forces initialization during serialization. - The StreamingContext for the serialization operation. - - - Creates and returns a string representation of this instance. - The result of calling on the . - - The is null. - - - - - local helper method to initialize the value - - The inititialized T value - - - Creates an instance of T using m_valueFactory in case its not null or use reflection to create a new T() - An instance of Boxed. - - - Gets the value of the Lazy<T> for debugging display purposes. - - - - Gets a value indicating whether this instance may be used concurrently from multiple threads. - - - - - Gets whether the value creation is faulted or not - - - - Gets a value indicating whether the has been initialized. - - true if the instance has been initialized; - otherwise, false. - - The initialization of a instance may result in either - a value being produced or an exception being thrown. If an exception goes unhandled during initialization, - the instance is still considered initialized, and that exception - will be thrown on subsequent accesses to . In such cases, - will return true. - - - - Gets the lazily initialized value of the current . - The lazily initialized value of the current . - - The was initialized to use the default constructor - of the type being lazily initialized, and that type does not have a public, parameterless constructor. - - - The was initialized to use the default constructor - of the type being lazily initialized, and permissions to access the constructor were missing. - - - The was constructed with the or - and the initialization function attempted to access on this instance. - - - If is false, accessing will force initialization. - Please for more information on how will behave if an exception is thrown - from initialization delegate. - - - - - wrapper class to box the initialized value, this is mainly created to avoid boxing/unboxing the value each time the value is called in case T is - a value type - - - - - Wrapper class to wrap the excpetion thrown by the value factory - - - - A debugger view of the Lazy<T> to surface additional debugging properties and - to ensure that the Lazy<T> does not become initialized if it was not already. - - - Constructs a new debugger view object for the provided Lazy object. - A Lazy object to browse in the debugger. - - - Returns whether the Lazy object is initialized or not. - - - Returns the value of the Lazy object. - - - Returns the execution mode of the Lazy object - - - Returns the execution mode of the Lazy object - - - - PriorityQueue class - This class is not thread safe because we use external lock - - - - - The number of queues, there is one for each type of priority - - - - - Work items queues. There is one for each type of priority - - - - - The total number of work items within the queues - - - - - Use with IEnumerable interface - - - - - Enqueue a work item. - - A work item - - - - Dequeque a work item. - - Returns the next work item - - - - Find the next non empty queue starting at queue queueIndex+1 - - The index-1 to start from - - The index of the next non empty queue or -1 if all the queues are empty - - - - - Clear all the work items - - - - - Returns an enumerator to iterate over the work items - - Returns an enumerator - - - - The number of work items - - - - - The class the implements the enumerator - - - - - Class that implements a wrapper for a delegate to support - fire and forget asynchronous invoke of a delegate. - - - - - Invoke the specified delegate with the specified arguments - asynchronously on a thread pool thread. EndInvoke is automatically - called to prevent resource leaks. - - - - - Smart thread pool class. - - - - - Default minimum number of threads the thread pool contains. (0) - - - - - Default maximum number of threads the thread pool contains. (25) - - - - - Default idle timeout in milliseconds. (One minute) - - - - - Indicate to copy the security context of the caller and then use it in the call. (false) - - - - - Indicate to copy the HTTP context of the caller and then use it in the call. (false) - - - - - Indicate to dispose of the state objects if they support the IDispose interface. (false) - - - - - The default option to run the post execute - - - - - The default work item priority - - - - - The default is to work on work items as soon as they arrive - and not to wait for the start. - - - - - The default thread priority - - - - - The default post execute method to run. - When null it means not to call it. - - - - - The default is not to use the performance counters - - - - - Contains the name of this instance of SmartThreadPool. - Can be changed by the user. - - - - - Hashtable of all the threads in the thread pool. - - - - - Queue of work items. - - - - - Count the work items handled. - Used by the performance counter. - - - - - Number of threads that currently work (not idle). - - - - - Start information to use. - It is simpler than providing many constructors. - - - - - Total number of work items that are stored in the work items queue - plus the work items that the threads in the pool are working on. - - - - - Signaled when the thread pool is idle, i.e. no thread is busy - and the work items queue is empty - - - - - An event to signal all the threads to quit immediately. - - - - - A flag to indicate the threads to quit. - - - - - Counts the threads created in the pool. - It is used to name the threads. - - - - - Indicate that the SmartThreadPool has been disposed - - - - - Holds all the WorkItemsGroup instances that have at least one - work item int the SmartThreadPool - This variable is used in case of Shutdown - - - - - A reference from each thread in the thread pool to its SmartThreadPool - object container. - With this variable a thread can know whatever it belongs to a - SmartThreadPool. - - - - - A reference to the current work item a thread from the thread pool - is executing. - - - - - STP performance counters - - - - - Constructor - - - - - Constructor - - Idle timeout in milliseconds - - - - Constructor - - Idle timeout in milliseconds - Upper limit of threads in the pool - - - - Constructor - - Idle timeout in milliseconds - Upper limit of threads in the pool - Lower limit of threads in the pool - - - - Constructor - - - - - Waits on the queue for a work item, shutdown, or timeout. - - - Returns the WaitingCallback or null in case of timeout or shutdown. - - - - - Put a new work item in the queue - - A work item to queue - - - - Put a new work item in the queue - - A work item to queue - if set to true increment work items. - - - - Inform that the current thread is about to quit or quiting. - The same thread may call this method more than once. - - - - - Starts new threads - - The number of threads to start - - - - A worker thread method that processes work items from the work items queue. - - - - - Queue a work item - - A callback to execute - Returns a work item result - - - - Queue a work item - - A callback to execute - The priority of the work item - Returns a work item result - - - - Queue a work item - - Work item info - A callback to execute - Returns a work item result - - - - Queue a work item - - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - Returns a work item result - - - - Queue a work item - - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - The work item priority - Returns a work item result - - - - Queue a work item - - Work item information - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - Returns a work item result - - - - Queue a work item - - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - - A delegate to call after the callback completion - - Returns a work item result - - - - Queue a work item - - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - - A delegate to call after the callback completion - - The work item priority - Returns a work item result - - - - Queue a work item - - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - - A delegate to call after the callback completion - - Indicates on which cases to call to the post execute callback - Returns a work item result - - - - Queue a work item - - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - - A delegate to call after the callback completion - - Indicates on which cases to call to the post execute callback - The work item priority - Returns a work item result - - - - Wait for the thread pool to be idle - - - - - Wait for the thread pool to be idle - - - - - Wait for the thread pool to be idle - - - - - Force the SmartThreadPool to shutdown - - - - - Empties the queue of work items and abort the threads in the pool. - - - - - Wait for all work items to complete - - Array of work item result objects - - true when every work item in workItemResults has completed; otherwise false. - - - - - Wait for all work items to complete - - Array of work item result objects - The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. - - true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - - - true when every work item in workItemResults has completed; otherwise false. - - - - - Wait for all work items to complete - - Array of work item result objects - The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. - - true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - - A cancel wait handle to interrupt the wait if needed - - true when every work item in workItemResults has completed; otherwise false. - - - - - Wait for all work items to complete - - Array of work item result objects - The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - - true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - - - true when every work item in workItemResults has completed; otherwise false. - - - - - Wait for all work items to complete - - Array of work item result objects - The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - - true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - - A cancel wait handle to interrupt the wait if needed - - true when every work item in workItemResults has completed; otherwise false. - - - - - Waits for any of the work items in the specified array to complete, cancel, or timeout - - Array of work item result objects - - The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled. - - - - - Waits for any of the work items in the specified array to complete, cancel, or timeout - - Array of work item result objects - The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. - - true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - - - The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. - - - - - Waits for any of the work items in the specified array to complete, cancel, or timeout - - Array of work item result objects - The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. - - true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - - A cancel wait handle to interrupt the wait if needed - - The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. - - - - - Waits for any of the work items in the specified array to complete, cancel, or timeout - - Array of work item result objects - The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - - true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - - - The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. - - - - - Waits for any of the work items in the specified array to complete, cancel, or timeout - - Array of work item result objects - The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - - true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - - A cancel wait handle to interrupt the wait if needed - - The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. - - - - - Event to send that the thread pool is idle - - - - - Get/Set the name of the SmartThreadPool instance - - - - - Get the lower limit of threads in the pool. - - - - - Get the upper limit of threads in the pool. - - - - - Get the number of threads in the thread pool. - Should be between the lower and the upper limits. - - - - - Get the number of busy (not idle) threads in the thread pool. - - - - - Get the number of work items in the queue. - - - - - Summary description for STPPerformanceCounter. - - - - - Summary description for STPStartInfo. - - - - - Summary description for WIGStartInfo. - - - - - Use the caller's security context - - - - - Use the caller's HTTP context - - - - - Dispose of the state object of a work item - - - - - The option to run the post execute - - - - - A post execute callback to call when none is provided in - the QueueWorkItem method. - - - - - Indicate the WorkItemsGroup to suspend the handling of the work items - until the Start() method is called. - - - - - Idle timeout in milliseconds. - If a thread is idle for _idleTimeout milliseconds then - it may quit. - - - - - The lower limit of threads in the pool. - - - - - The upper limit of threads in the pool. - - - - - The priority of the threads in the pool - - - - - If this field is not null then the performance counters are enabled - and use the string as the name of the instance. - - - - - Synchronized access wrapper class - - The type that has its access synchronized. - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The initial value. - - - - Initializes a new instance of the class. - - The initial value. - The shared lock. - - - - Performs an implicit conversion from to {T}. - - The value. - The result of the conversion. - - - - Gets or sets the value. - - The value. - - - - An internal delegate to call when the WorkItem starts or completes - - - - - Holds a callback delegate and the state for that delegate. - - - - - Callback delegate for the callback. - - - - - State with which to call the callback delegate. - - - - - Stores the caller's context - - - - - Holds the result of the mehtod - - - - - Hold the exception if the method threw it - - - - - Hold the state of the work item - - - - - A ManualResetEvent to indicate that the result is ready - - - - - A reference count to the _workItemCompleted. - When it reaches to zero _workItemCompleted is Closed - - - - - Represents the result state of the work item - - - - - Work item info - - - - - A reference to an object that indicates whatever the - WorkItemsGroup has been canceled - - - - - The work item group this work item belong to. - - - - - - The time when the work items is queued. - Used with the performance counter. - - - - - The time when the work items starts its execution. - Used with the performance counter. - - - - - The time when the work items ends its execution. - Used with the performance counter. - - - - - Initialize the callback holding object. - - - - Callback delegate for the callback. - State with which to call the callback delegate. - - We assume that the WorkItem object is created within the thread - that meant to run the callback - - - - Change the state of the work item to in progress if it wasn't canceled. - - - Return true on success or false in case the work item was canceled. - If the work item needs to run a post execute then the method will return true. - - - - - Execute the work item and the post execute - - - - - Execute the work item - - - - - Runs the post execute callback - - - - - Set the result of the work item to return - - The result of the work item - The exception. - - - - Returns the work item result - - The work item result - - - - Wait for all work items to complete - - Array of work item result objects - The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - - true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - - A cancel wait handle to interrupt the wait if needed - - true when every work item in workItemResults has completed; otherwise false. - - - - - Waits for any of the work items in the specified array to complete, cancel, or timeout - - Array of work item result objects - The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - - true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - - A cancel wait handle to interrupt the wait if needed - - The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. - - - - - Fill an array of wait handles with the work items wait handles. - - An array of work item results - An array of wait handles to fill - - - - Release the work items' wait handles - - An array of work item results - - - - Sets the work item's state - - The state to set the work item to - - - - Signals that work item has been completed or canceled - - Indicates that the work item has been canceled - - - - Cancel the work item if it didn't start running yet. - - Returns true on success or false if the work item is in progress or already completed - - - - Get the result of the work item. - If the work item didn't run yet then the caller waits for the result, timeout, or cancel. - In case of error the method throws and exception - - The result of the work item - - - - Get the result of the work item. - If the work item didn't run yet then the caller waits for the result, timeout, or cancel. - In case of error the e argument is filled with the exception - - The result of the work item - - - - A wait handle to wait for completion, cancel, or timeout - - - - - Called when the WorkItem starts - - - - - Called when the WorkItem completes - - - - - Returns true when the work item has completed or canceled - - - - - Returns true when the work item has canceled - - - - - Returns the priority of the work item - - - - - Indicates the state of the work item in the thread pool - - - - - A back reference to the work item - - - - - Return the result, same as GetResult() - - - - - Returns the exception if occured otherwise returns null. - This value is valid only after the work item completed, - before that it is always null. - - - - - Create a new work item - - The work items group. - Work item group start information - A callback to execute - Returns a work item - - - - Create a new work item - - - Work item group start information - A callback to execute - The priority of the work item - Returns a work item - - - - Create a new work item - - - Work item group start information - Work item info - A callback to execute - Returns a work item - - - - Create a new work item - - - Work item group start information - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - Returns a work item - - - - Create a new work item - - - Work item group start information - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - The work item priority - Returns a work item - - - - Create a new work item - - - Work item group start information - Work item information - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - Returns a work item - - - - Create a new work item - - - Work item group start information - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - - A delegate to call after the callback completion - - Returns a work item - - - - Create a new work item - - - Work item group start information - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - - A delegate to call after the callback completion - - The work item priority - Returns a work item - - - - Create a new work item - - - Work item group start information - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - - A delegate to call after the callback completion - - Indicates on which cases to call to the post execute callback - Returns a work item - - - - Create a new work item - - - Work item group start information - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - - A delegate to call after the callback completion - - Indicates on which cases to call to the post execute callback - The work item priority - Returns a work item - - - - Summary description for WorkItemInfo. - - - - - Use the caller's security context - - - - - Use the caller's security context - - - - - Dispose of the state object of a work item - - - - - The option to run the post execute - - - - - A post execute callback to call when none is provided in - the QueueWorkItem method. - - - - - The priority of the work item - - - - - Summary description for WorkItemsGroup. - - - - - Contains the name of this instance of SmartThreadPool. - Can be changed by the user. - - - - - A reference to the SmartThreadPool instance that created this - WorkItemsGroup. - - - - - Defines how many work items of this WorkItemsGroup can run at once. - - - - - Priority queue to hold work items before they are passed - to the SmartThreadPool. - - - - - Indicate how many work items are waiting in the SmartThreadPool - queue. - This value is used to apply the concurrency. - - - - - Indicate how many work items are currently running in the SmartThreadPool. - This value is used with the Cancel, to calculate if we can send new - work items to the STP. - - - - - WorkItemsGroup start information - - - - - Signaled when all of the WorkItemsGroup's work item completed. - - - - - A common object for all the work items that this work items group - generate so we can mark them to cancel in O(1) - - - - - Queue a work item - - A callback to execute - Returns a work item result - - - - Queue a work item - - A callback to execute - The priority of the work item - Returns a work item result - - - - Queue a work item - - Work item info - A callback to execute - Returns a work item result - - - - Queue a work item - - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - Returns a work item result - - - - Queue a work item - - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - The work item priority - Returns a work item result - - - - Queue a work item - - Work item information - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - Returns a work item result - - - - Queue a work item - - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - - A delegate to call after the callback completion - - Returns a work item result - - - - Queue a work item - - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - - A delegate to call after the callback completion - - The work item priority - Returns a work item result - - - - Queue a work item - - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - - A delegate to call after the callback completion - - Indicates on which cases to call to the post execute callback - Returns a work item result - - - - Queue a work item - - A callback to execute - - The context object of the work item. Used for passing arguments to the work item. - - - A delegate to call after the callback completion - - Indicates on which cases to call to the post execute callback - The work item priority - Returns a work item result - - - - Wait for the thread pool to be idle - - - - - Wait for the thread pool to be idle - - - - - Wait for the thread pool to be idle - - - - - The OnIdle event - - - - - Get/Set the name of the SmartThreadPool instance - - - - - WorkItemsQueue class. - - - - - Waiters queue (implemented as stack). - - - - - Waiters count - - - - - Work items queue - - - - - Indicate that work items are allowed to be queued - - - - - Each thread in the thread pool keeps its own waiter entry. - - - - - A flag that indicates if the WorkItemsQueue has been disposed. - - - - - Enqueue a work item to the queue. - - - - - Waits for a work item or exits on timeout or cancel - - Timeout in milliseconds - Cancel wait handle - Returns true if the resource was granted - - - - Cleanup the work items queue, hence no more work - items are allowed to be queue - - - - - Returns the WaiterEntry of the current thread - - - In order to avoid creation and destuction of WaiterEntry - objects each thread has its own WaiterEntry object. - - - - Push a new waiter into the waiter's stack - - A waiter to put in the stack - - - - Pop a waiter from the waiter's stack - - Returns the first waiter in the stack - - - - Remove a waiter from the stack - - A waiter entry to remove - If true the waiter count is always decremented - - - - Returns the current number of work items in the queue - - - - - Returns the current number of waiters - - - - - Event to signal the waiter that it got the work item. - - - - - Flag to know if this waiter already quited from the queue - because of a timeout. - - - - - Flag to know if the waiter was signaled and got a work item. - - - - - A work item that passed directly to the waiter withou going - through the queue - - - - - Signal the waiter that it got a work item. - - Return true on success - The method fails if Timeout() preceded its call - - - - Mark the wait entry that it has been timed out - - Return true on success - The method fails if Signal() preceded its call - - - - Reset the wait entry so it can be used again - - - - - Free resources - - - - - Determines if the visitor should call the visitors of any registered delegates that are base types of the being visited. - - - - - Determines if the visiting should stop after one successful visit. - - - - - A collection class for attachments - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The list that is wrapped by the new collection. - - is null. - - - - - Defines a set of utility methods for extracting icons for files and file extensions. - - - - - Retrieve the handle to the icon that represents the file and the index of the icon within the system image list. The handle is copied to the hIcon member of the structure specified by psfi, and the index is copied to the iIcon member. - - - - - Modify SHGFI_ICON, causing the function to retrieve the file's large icon. The SHGFI_ICON flag must also be set. - - - - - Modify SHGFI_ICON, causing the function to retrieve the file's small icon. Also used to modify SHGFI_SYSICONINDEX, causing the function to return the handle to the system image list that contains small icon images. The SHGFI_ICON and/or SHGFI_SYSICONINDEX flag must also be set. - - - - - Retrieves information about an object in the file system, such as a file, folder, directory, or drive root. - - A pointer to a null-terminated string of maximum length MAX_PATH that contains the path and file name. Both absolute and relative paths are valid. - A combination of one or more file attribute flags (FILE_ATTRIBUTE_ values as defined in Winnt.h). - The address of a SHFILEINFO structure to receive the file information. - The size, in bytes, of the SHFILEINFO structure pointed to by the psfi parameter. - The flags that specify the file information to retrieve. - Nonzero if successful, or zero otherwise. - - - - Creates an array of handles to large or small icons extracted from the specified executable file, DLL, or icon file. - - The name of an executable file, DLL, or icon file from which icons will be extracted. - The zero-based index of the first icon to extract. If this value is a negative number and either phiconLarge or phiconSmall is not NULL, the function begins by extracting the icon whose resource identifier is equal to the absolute value of nIconIndex. For example, use -3 to extract the icon whose resource identifier is 3. - An array of icon handles that receives handles to the large icons extracted from the file. If this parameter is NULL, no large icons are extracted from the file. - An array of icon handles that receives handles to the small icons extracted from the file. If this parameter is NULL, no small icons are extracted from the file. - The number of icons to be extracted from the file. - If the nIconIndex parameter is -1, the phiconLarge parameter is NULL, and the phiconSmall parameter is NULL, then the return value is the number of icons contained in the specified file. Otherwise, the return value is the number of icons successfully extracted from the file. - - - - Returns an icon representation of the specified file. - - The path to the file. - The desired size of the icon. - An icon that represents the file. - - - - Returns the default icon representation for files with the specified extension. - - File extension (including the leading period). - The desired size of the icon. - The default icon for files with the specified extension. - - - - Contains information about a file object. - - - - - A handle to the icon that represents the file. - - - - - The index of the icon image within the system image list. - - - - - An array of values that indicates the attributes of the file object. - - - - - A string that contains the name of the file as it appears in the Windows Shell, or the path and file name of the file that contains the icon representing the file. - - - - - A string that describes the type of file. - - - - - Represents the different icon sizes that can be extracted using the ExtractAssociatedIcon method. - - - - - Specifies a small (16x16) icon. - - - - - Specifies a large (32x32) icon. - - - - - Represents an email message to be sent through MAPI. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class with the specified subject. - - The subject. - - - - Initializes a new instance of the class with the specified subject and body. - - The subject. - The body. - - - - Called when send mail is complete. - - The error code. - - - - Displays the mail message dialog asynchronously. - - - - - Displays the mail message dialog. The call is block until the dialog is closed. - - The error code from the mapi call. - - - - Occurs when the MAPI send mail call is complete. - - - - - Gets or sets the subject of this mail message. - - - - - Gets or sets the body of this mail message. - - - - - Gets the recipient list for this mail message. - - - - - Gets the attachment list for this mail message. - - - - - Specifies the valid RecipientTypes for a Recipient. - - - - - Recipient will be in the TO list. - - - - - Recipient will be in the CC list. - - - - - Recipient will be in the BCC list. - - - - - Internal class for calling MAPI APIs - - - - - Gets the error message from the specified error code. - - The error code. - The error message from the error code. - - - - Represents a Recipient for a . - - - - - Initializes a new instance of the class. - - The email address. - - - - Initializes a new instance of the class. - - The email address. - The display name. - - - - Initializes a new instance of the class. - - The email address. - Type of the recipient. - - - - Initializes a new instance of the class. - - The email address. - The display name. - Type of the recipient. - - - - Gets or sets the email address. - - The email address. - - - - Gets or sets the display name. - - The display name. - - - - Gets or sets the type of the recipient. - - The type of the recipient. - - - - A collection of recipients for a mail message. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The list that is wrapped by the new collection. - - is null. - - - - - Adds a new recipient with the specified email address to this collection. - - The email address. - - - - Adds a new recipient with the specified email address and display name to this collection. - - The email address. - The display name. - - - - Adds a new recipient with the specified email address and recipient type to this collection. - - The email address. - Type of the recipient. - - - - Adds a new recipient with the specified email address, display name and recipient type to this collection. - - The email address. - The display name. - Type of the recipient. - - - - A class representing the SendMailComplate event. - - - - - Initializes a new instance of the class. - - The error code. - - - - Gets the error code. - - The error code. - - - - Gets the error message. - - The error message. - - - - Activates and displays the window. If the window is minimized or - maximized, the system restores it to its original size and position. - An application should specify this flag when restoring a minimized window. - - - - - A class to parse all the xml namespaces from an XPathNavigator. - - - - - Initializes a new instance of the class. - - - - - Parses an XML document for its namespaces. - - The navigator. - - - - Gets or sets the namespaces collection. - - The namespaces collection. - - - - Gets or sets a value indicating whether to parse child nodes to find namespaces. - - true if parse child nodes; otherwise, to only parse root element, false. - - - - Gets or sets the scheme to use when generating prefix values for default namespaces. - - The default scheme. - - - - Gets or sets the default prefix. - - The default prefix. - - - diff --git a/template/CodeSmith/CodeGenerator/Libraries/CodeSmith.Engine.dll b/template/CodeSmith/CodeGenerator/Libraries/CodeSmith.Engine.dll deleted file mode 100644 index b5b75a7c6..000000000 Binary files a/template/CodeSmith/CodeGenerator/Libraries/CodeSmith.Engine.dll and /dev/null differ diff --git a/template/CodeSmith/CodeGenerator/Libraries/CodeSmith.Engine.xml b/template/CodeSmith/CodeGenerator/Libraries/CodeSmith.Engine.xml deleted file mode 100644 index 0b4d176ae..000000000 --- a/template/CodeSmith/CodeGenerator/Libraries/CodeSmith.Engine.xml +++ /dev/null @@ -1,6168 +0,0 @@ - - - - CodeSmith.Engine - - - - - The file path that is being modified. - - - - - The content that the template generated. - - - - - The previous content waiting to be merged. - - - - - Generates the snippet. - - The raw line. - The target extension. - The snippet output. - - - - - Outputs the usage. - - The raw line. - The target extension. - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The MSBuild task item to get the properties from. - - - - Gets or sets the original name used to request the assembly resolve. - - - The original name. - - - - - Gets or sets the assembly name. - - - The assembly name. - - - - - Gets or sets the full assembly name. - - - The full assembly name. - - - - - Gets or sets the full path to the assembly. - - - The path to the assembly. - - - - - Gets or sets a value indicating the assembly should be copied local. - - - true if copy local; otherwise, false. - - - - - Gets a value indicating whether the assembly was loaded from the global assembly cache. - - - true if the assembly was loaded from the global assembly cache; otherwise, false. - - - - - Gets or sets the location where the assembly was resolved from. - - - The location where the assembly was resolved from. - - - - - Gets or sets the full path to the assemblies new local path. - - - The path to the assembly local path. - - - - - Initializes a new instance of the class. - - - - - Gets the current instance of . - - The current instance. - - - - This class can be used to compile templates. - - - - - Creates a new instance. - - Path. - - - - Creates a new instance. - - Code template info. - - - - Compiles the specified template and outputs an assembly to the output path. - - - - - Compiles the specified template and outputs an assembly to the output path. - - Force recompile. - - - - Compiles the specified template and outputs an assembly to the output path. - - Output path. - - - - Compiles the specified template and outputs an assembly to the output path. - - Output path. - Force recompile. - - - - Compiles the specified template and outputs an assembly to the output path. - - Output path. - Force recompile. - - - - - Creates an instance of the compiled template. You must compile the template first. - - - - - - Gets the code template information. - - - - - - Gets or sets a value indicating whether the template should be compiled in debug mode or not. - - - true if debug; otherwise, false. - - - - - Gets or sets a value indicating whether line pragmas should be output in the compiled source code. - - - true if line pragmas should be generated; otherwise, false. - - - - - Gets or sets a value indicating whether the temp files should be kept. - - - true if you want to keep the temp files; otherwise, false. - - - - - Namespace that the compiled template should be a member of. - - - - - - Gets or sets the type name of the compiled template. - - - - - - Provides access to the compiled assembly. - - - - - - Provides access to the generated source code. - - - - - - Gets the errors. - - - - - - Gets or sets the compiler output writer. - - - - - - Gets the hash code based on the template dependencies. - - - - - - Gets the full path to the template. - - - - - - Gets the name of the template file. - - - - - - Gets the name of the directory the template is located in. - - - - - - Gets the template language. - - - - - - Gets the target language. - - - - - - Gets the response encoding. - - - - - - Gets the description. - - - - - - Gets the date the template was created. - - - - - - Gets the date the template was modified. - - - - - - Gets the hash code based on the template content and all template Dependencies. - - - - - - Gets the type of the output for this template. - - The type of the output. - - - - Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - - - - - Releases unmanaged and - optionally - managed resources - - true to release both managed and unmanaged resources; false to release only unmanaged resources. - - - - Disposes the managed resources. - - - - - Disposes the unmanaged resources. - - - - - Releases unmanaged resources and performs other cleanup operations before the - is reclaimed by garbage collection. - - - - - Adds a instance to the current thread's . - - The currently executing CodeTemplate - An instance of the current TemplateContext. - - - - Cleans up all resources associated with the . - - - - - Returns the data associated with the current executing . - - - - - Returns the that is being rendered in the current executing . - - - - - Returns the Parent that is being rendered in the current executing . - - - - - Returns the Root that is being rendered in the current executing . - - - - - Creates a new Template Error while setting the SourceFilePath property. - - - - - - - - Gets the item type for the file type. - - The item. - - - - - Gets the CodeSmith safe import. - - The CodeSmith safe import. - - - - This is the channel from the client (child) process to the host (parent) process. - - - - - This is called by the client when it is ready to receive messages. - - A pointer to the client channel - - - - The process id of the host (parent) process. - - - - - This is the channel from the host (parent) proccess to the client (child) process. - - - - - A class to aid in remoting with a host process. - - - There can only be one HostProcess for the current process. - Initialize must be called to wire up the host process remoting. - When the client is fully started and ready to receive message, - ClientReady must be called to alert the host process that messages - can be sent. - - - - - Initializes a new instance of the class. - - - - - Raises the event. - - The remote message. - - - - Raises the event. - - The remote message. - - - - Raises the message error. - - The error that occurred. - - - - Initializes by getting the from - the specified host ipc URI. Must be called before calling other methods. - - The host ipc URI. - - Initialize must be called before any other method in the class. - Initialize can only be called once, additional calls will be ignored - - - Thrown when host remoting channel can not be loaded from . - - - - - Alerts the host process that the client is ready to receive messages. Must be called after Initialize. - - - - - Alerts the host process that the client is ready to receive messages. Must be called after Initialize. - - - - - Sends message to host async. Initialize must be called before this. - - The message to send. - - - - Shutdown was initiated in the client. - - - - - Occurs when a message is received from the remote process. - - - - - Occurs when a message is sent to the host process. - - - - - Raised when an error is thrown by . - - - - - Interface that all property serializers must implement. - - - - - This method will be used to save the property value when a template is being compiled. - - Context information of the target property. - The property to be saved. - An object that will be stored in a Hashtable during template compilation. - - - - This method will be used to restore the property value after a template has been compiled. - - Context information of the target property. - The property to be loaded. - The value to be assigned to the template property after it has been compiled. - - - - This method will be used when serializing the property value to an XML property set. - - Context information of the target property. - The XML writer that the property value will be written to. - The property to be serialized. - - - - This method will be used when deserializing the property from an XML property set. - - Context information of the target property. - The XML node to read the property value from. - The value to be assigned to the template property. - - - - This method will be used to parse a default value for a property when a template is being instantiated. - - Context information of the target property. - The default value. - An object that will be assigned to the template property. - - - - Begin generating code asynchronous - - The project file to generate - - - - Generates a CodeSmith Project file based on the file path to the csp. - - File path to the csp file. - true if the csp was generated. - - - - Access's the Registry and gets the string value of the specific SubKey. - - The SubKey. - The name of the key. - The value returned from the registry. - Returns true if the object is not null. - - - - Attempts to update configuration from a configuration file. The version of CodeSmith must be older than CodeSmith 5.1. - - Type of the configuration class. - The configuration filename. - Updated Configuration file if a configuration file was found. - - - - Finds all configuration files with a specified name. - - Configuration file name. - A List of configuration files with the specified file name. - - - - This method is here purely to make sure that we don't make a double call to the Initialize method while initializing configuration. - - - - - Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - - 2 - - - - Finds the specified alias. - - The alias. - The target extension. - The instance that match closest to the alias, extension pair. - - - - Finds the specified alias. - - The alias. - The target extension. - If true, the target extension must match. - The instance that matches the passed in alias and targetExtension - - - - Finds the instance that match closest to the alias. - - The partial alias. - The target extension. - - The instance that match closest to the alias. - - - - - A collection of Active Snippet maps. - - - The key for the collection is the alias of the template map. - - - - - Gets the current singleton instance of Configuration. - - The current singleton instance. - - An instance of Configuration wont be created until the very first - call to the sealed class. This is a CLR optimization that - provides a properly lazy-loading singleton. - - - - - Nested class to lazy-load singleton. - - - - - Initializes the Nested class. - - - Explicit static constructor to tell C# compiler not to mark type as beforefieldinit. - - - - - Current singleton instance. - - - - - Interface defining an indexed object. - - - - - Gets or sets the index. - - The index. - - - - Gets or sets the alias. - - The alias. - - - - Gets or sets the target language. - - The target language. - - - - Gets or sets the template file. - - The template file. - - - - Returns the original template file name. - - - - - Gets or sets the name of the template. - - The name of the template. - - - - Gets the name of the display. - - The name of the display. - - - - Gets or sets the arguments. - - The arguments. - - - - Gets or sets the target file extensions. - - The target extensions. - - - - Gets or sets the property set. - - The property set. - - - - A base Collection used to load data on depand from database. - - - - - - Copies the elements of the to an , starting at a particular index. - - The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. - The zero-based index in at which copying begins. - - is null. - - - is less than zero. - - - is multidimensional. - -or- - is equal to or greater than the length of . - -or- - The number of elements in the source is greater than the available space from to the end of the destination . - - - The type of the source cannot be cast automatically to the type of the destination . - - - - - Adds an item to the . - - The to add to the . - - The position into which the new element was inserted. - - - The is read-only. - -or- - The has a fixed size. - - - - - Determines whether the contains a specific value. - - The to locate in the . - - true if the is found in the ; otherwise, false. - - - - - Determines the index of a specific item in the . - - The to locate in the . - - The index of if found in the list; otherwise, -1. - - - - - Inserts an item to the at the specified index. - - The zero-based index at which should be inserted. - The to insert into the . - - is not a valid index in the . - - - The is read-only. - -or- - The has a fixed size. - - - is null reference in the . - - - - - Removes the first occurrence of a specific object from the . - - The to remove from the . - - The is read-only. - -or- - The has a fixed size. - - - - - Adds an item to the . - - The object to add to the . - - The is read-only. - - - - - Removes all items from the . - - - The is read-only. - - - - - Copies the elements of the to an , starting at a particular index. - - The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. - The zero-based index in at which copying begins. - - - - Determines whether the contains a specific value. - - The object to locate in the . - - true if is found in the ; otherwise, false. - - - - - Returns an enumerator that iterates through the collection. - - - A that can be used to iterate through the collection. - - - - - Determines the index of a specific item in the . - - The object to locate in the . - - The index of if found in the list; otherwise, -1. - - - - - Inserts an item to the at the specified index. - - The zero-based index at which should be inserted. - The object to insert into the . - - is not a valid index in the . - - - The is read-only. - - - - - Removes the first occurrence of a specific object from the . - - The object to remove from the . - - true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . - - - The is read-only. - - - - - Removes the item at the specified index. - - The zero-based index of the item to remove. - - is not a valid index in the . - - - The is read-only. - - - - - Returns an enumerator that iterates through a collection. - - - An object that can be used to iterate through the collection. - - - - - Gets the count. - - - - - - Gets the index. - - The item. - - - - - Gets the item. - - The index. - - - - - Gets the items. - - - - - - Clears the items. - - - - - Adds the item. - - The item. - - - - - Inserts an element into the Collection at the specified index. - - The zero-based index at which item should be inserted. - The object to insert. The value can be null (Nothing in Visual Basic) for reference types. - - - - Removes the element at the specified index of the Collection. - - The zero-based index of the element to remove. - - - - Replaces the element at the specified index. - - The zero-based index of the element to replace. - The new value for the element at the specified index. The value can be null (Nothing in Visual Basic) for reference types. - - - - Determines whether the specified value is compatible with Collection. - - The value to check. - - true if is a compatible object; otherwise, false. - - - - - Verifies the type of the value. - - The value to check. - - - - Gets a value indicating whether access to the is synchronized (thread safe). - - - true if access to the is synchronized (thread safe); otherwise, false. - - - - - Gets an object that can be used to synchronize access to the . - - - - An object that can be used to synchronize access to the . - - - - - Gets or sets the at the specified index. - - - - - - Gets a value indicating whether the is read-only. - - - true if the is read-only; otherwise, false. - - - - - Gets a value indicating whether the has a fixed size. - - - true if the has a fixed size; otherwise, false. - - - - - Gets the number of elements contained in the . - - - - The number of elements contained in the . - - - - - Gets or sets the type T at the specified index. - - - - - - Gets a value indicating whether the is read-only. - - - true if the is read-only; otherwise, false. - - - - - Value to be cached. - - - - - Initializes a new instance of the class. - - - - - This method will safely upgrade the default values for collections and overwrite any directory settings. - - - - - The following method will check the registry and see if an upgrade is needed. - - Returns true if the registry is up to date; returns false if there is no previous registry version. - - - - Updates Lines Generated based off of the passed in string containing source code. - - Source code. - - - - Returns the numbers of hours saved by using CodeSmith. - - Returns the numbers of hours saved by using CodeSmith. - - - - Returns the amount of money saved by using CodeSmith. - - Returns the amount of money saved by using CodeSmith. - - - - Returns the path to the icon resource dll. - - Returns the path to the icon resource dll. - - - - Extracts an embedded license if it was found. - - The full path to store the license file. - The assembly that contains the license file. - - - - Checks an assembly for an embedded license. - - The assembly with the embedded license. - - - - Gets or sets a value indicating whether to track lines generated. - - true to track lines generated; otherwise, false. - - - - Gets or sets the number of lines generated. - - The number of lines generated. - - - - Gets or sets the average lines per hour. - - The average lines per hour. - - - - Gets or sets the average cost per hour. - - The average cost per hour. - - - - Gets or sets the application directory. - - The application directory. - - - - Gets the merge strategy aliases. - - The merge strategy aliases. - - - - Gets the language region definitions. - - The language region definitions. - - - - Gets the property designer maps. - - The property designer maps. - - - - Gets the conditional compilation statements for the compiler. - - The conditional compilation. - - - - Gets or sets the base CodeSmith sample directory. - - The CodeSmith sample directory. - - - - Gets or sets the CodeSmith sample directory. - - The CodeSmith sample directory. - - - - Gets or sets the CodeSmith maps directory. - - The CodeSmith maps directory. - - - - Gets or sets the CodeSmith templates directory. - - The CodeSmith templates directory. - - - - Gets or sets the CodeSmith data directory. - - The CodeSmith data directory. - - - - Gets or sets the template cache directory. - - The template cache directory. - - - - Gets or sets the property set cache directory. - - The property set cache directory. - - - - Gets or sets the plural overrides map file. - - The plural overrides map file. - - - - Controls whether the Customer Experience Improvement Program is enabled. - - - - - Gets or sets the semicolon delimited list of assembly probing paths. The path is relative to ApplicationDirectory. - - The semicolon delimited list of assembly probing paths. - - - - Returns true if CodeSmith Explorer, CodeSmith Studio, or Console was not the entry assembly. - - - - - Returns the name of the current Entry Assembly. - - - - - Gets the current singleton instance of Configuration. - - The current singleton instance. - - An instance of Configuration wont be created until the very first - call to the sealed class. This is a CLR optimization that - provides a properly lazy-loading singleton. - - - - - Nested class to lazy-load singleton. - - - - - Initializes the Nested class. - - - Explicit static constructor to tell C# compiler not to mark type as beforefieldinit. - - - - - Current singleton instance. - - - - - Loads this instance. - - - - - Saves this instance. - - - - - Gets the singleton instance. - - The singleton instance. - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The index. - - - - Gets or sets the index. - - The index. - - - - Occurs when a property value changes. - - - - - IndexedEnumerable makes enumerating over collections much easier by implementing properties like: IsEven, IsOdd, IsLast. - - - - - Returns an IndexedEnumerable from any collection implementing IEnumerable<T> - - Type of enumerable - Source enumerable - A new IndexedEnumerable<T>. - - - - Returns an IndexedEnumerable from any collection implementing IEnumerable<T> - - Type of enumerable - Source enumerable - A new IndexedEnumerable<T>. - - - - IndexedEnumerable makes enumerating over collections much easier by implementing properties like: IsEven, IsOdd, IsLast. - - Type to iterate over - - - - Default constructor. - - - - - Constructor that takes an IEnumerable<T> - - The collection to enumerate. - - - - Returns an enumeration of Entry objects. - - - - - Non-generic form of GetEnumerator. - - - - - Represents each entry returned within a collection, - containing the _value and whether it is the first and/or - the last entry in the collection's. enumeration - - - - - The Entry Value. - - - - - Returns true if it is the first item in the collection. - - - - - Returns true if it is the last item in the collection. - - - - - The index of the current item in the collection. - - - - - Returns true if the current item has an even index - - - - - Returns true if the current item has an odd index - - - - - Raises the event. - - The remote message. - - - - Sends a message to the remote process - - - - - - Occurs when the remote process is available. - - - - - Occurs when the remote process is starting. - - - - - Occurs when a message is received from the remote process. - - - - - Gets or sets the executable path for the remote process. - - - The executable path for the remote process. - - - - - Gets the current singleton instance of RemoteProjectProcess. - - The current singleton instance. - - An instance of RemoteProjectProcess wont be created until the very first - call to the sealed class. This is a CLR optimization that - provides a properly lazy-loading singleton. - - - - - Comprises utility methods for constructing a command line. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The command line to start with. - - - - Appends the command line with file name represented by the parameter, inserting quotation marks if necessary. - - Name of the file. - - - - Appends the command line with a list of file names, inserting quotation marks if necessary. - - The file names to append. If the array is null reference, then this method has no effect. - The delimiter to put between file names in the command line. - - - - Appends the command line with a file name, and surrounds the file name with quotation marks as necessary. - - Name of the file. - - - - Appends the space if command line is not empty. - - - - - Appends the command line with the specified switch. - - Name of the switch. - - - - Appends the switch if true. - - Name of the switch. - if set to true [condition]. - - - - Appends the switch if not null. - - Name of the switch. - The parameter. - - - - Appends the switch if not default. - - Type of the parameter - Name of the switch. - The parameter. - - - - Appends the switch if not default. - - Type of the parameter - Name of the switch. - The parameter. - The default value. - - - - Appends the switch if not null. - - Name of the switch. - The parameters. - The delimiter. - - - - Appends the switch unquoted if not null. - - Name of the switch. - The parameter. - - - - Appends the switch unquoted if not null. - - Name of the switch. - The parameters. - The delimiter. - - - - Appends the text unquoted. - - The text to append. - - - - Appends the text with quoting. - - The text to append. - - - - Determines whether [is quoting required] [the specified parameter]. - - The parameter. - - true if [is quoting required] [the specified parameter]; otherwise, false. - - - - - Returns a that represents this instance. - - - A that represents this instance. - - - - - Verifies the throw no embedded double quotes. - - Name of the switch. - The parameter. - - - - Verifies the throw argument null. - - The parameter. - Name of the parameter. - - - - Gets the command line buffer. - - The command line. - - - - A collection that provides notifications when items get added, removed, or when the whole list is refreshed. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The collection. - - - - Initializes a new instance of the class. - - The delimited values. - - - - Disallows reentrant attempts to change this collection. - - An IDisposable object that can be used to dispose of the object. - - - - Checks for reentrant attempts to change this collection. - - - - - Removes all elements from the . - - - - - Inserts an element into the at the specified index. - - The zero-based index at which should be inserted. - The object to insert. The value can be null for reference types. - - is less than zero. - -or- - is greater than . - - - - - Moves the item at the specified index to a new location in the collection. - - The zero-based index specifying the location of the item to be moved. - The zero-based index specifying the new location of the item. - - - - Moves the item at the specified index to a new location in the collection. - - The zero-based index specifying the location of the item to be moved. - The zero-based index specifying the new location of the item. - - - - Raises the CollectionChanged event with the provided arguments. - - - - - Raises the PropertyChanged event with the provided arguments. - - The instance containing the event data. - - - - Raises the PropertyChanged event with the provided arguments. - - Name of the property. - - - - Removes the element at the specified index of the . - - The zero-based index of the element to remove. - - is less than zero. - -or- - is equal to or greater than . - - - - - Replaces the element at the specified index. - - The zero-based index of the element to replace. - The new value for the element at the specified index. The value can be null for reference types. - - is less than zero. - -or- - is greater than . - - - - - Returns a that represents the current . - - - A that represents the current . - - - - - Returns a that represents the values. - - The values. - The delimiter. - - A that represents the current . - - - - - Returns a that represents the values. - - The values. - - A that represents the current . - - - - - Returns an array of strings that represents the current collection. - - An array of strings that represents the current collection. - - - - Adds the split values from to the collection. - The string is split using the property. - - The delimited values. - The number of values added. - Thrown when is null. - - - - Gets or sets the string delimiter. - - The string delimiter. - - - - Occurs when a property value changed. - - - - - Occurs when the collection changed. - - - - - A type converter for the . - - - - - Returns whether this converter can convert an object of the given type to the type of this converter, using the specified context. - - An that provides a format context. - A that represents the type you want to convert from. - - true if this converter can perform the conversion; otherwise, false. - - - - - Returns whether this converter can convert the object to the specified type, using the specified context. - - An that provides a format context. - A that represents the type you want to convert to. - - true if this converter can perform the conversion; otherwise, false. - - - - - Converts the given object to the type of this converter, using the specified context and culture information. - - An that provides a format context. - The to use as the current culture. - The to convert. - - An that represents the converted value. - - - The conversion cannot be performed. - - - - - Converts the given value object to the specified type, using the specified context and culture information. - - An that provides a format context. - A . If null is passed, the current culture is assumed. - The to convert. - The to convert the parameter to. - - An that represents the converted value. - - - The parameter is null. - - - The conversion cannot be performed. - - - - - Loads the default settings. - - - This method is called when existing config file is not - found. Use this method to populate the configuration - class with default values. - - - - - Upgrades the file. - - The file to upgrade. - - This method is called just before loading a previous config - file. Use this method to modify the file before it is loaded. - - - - - Upgrades the settings. - - - This method is called after loading a previous config file. - Use this method to change any existing configurations to - work with the latest version. - - - - - Gets a value indicating whether this instance can watch file. - - - true if this instance can watch file; otherwise, false. - - - - - Gets the configuration file. - - The configuration file. - - - - Gets a value indicating whether this configuration is backward compatible. - - - true if this configuration is backward compatible; otherwise, false. - - - - - Gets or sets the configuration version. - - The configuration version. - - - - Gets the list of language keys for the region defintion. - - The language keys. - - - - Gets or sets the region start regex. - - The region start regex. - - - - Gets or sets the region end regex. - - The region end regex. - - - - Gets or sets the name of the merge strategy alias. - - The merge strategy alias name. - - - - Gets or sets the fully qualified type name for the merge strategy. - - The name of the type. - - - - Gets the list of data types the designer should be used for. - - The data types. - - - - Gets or sets the type of the editor. - - The type of the editor. - - - - Gets or sets the type of the serializer. - - The type of the serializer. - - - - All templates must derive from this class. - - - - - This method can be overridden to perform logic when the template instance is created. - - - - - This method is overridden in templates to set default values in a template instance. - - - - - This method is overridden in templates to set unknown directive attributes in the ContextData. - - - - - This method can be overridden to perform logic before the template is rendered. - - - - - This method can be overridden to perform logic when a template exception occurs. - - - - - This method can be overridden to perform logic when a property changes. - - - - - This method can be overridden to perform logic when properties are loaded. - - - - - This method can be overridden to perform logic when properties are saved. - - - - - Renders to the template output to the specified writer. - - Writer. - - - - This method can be overridden to perform logic after the template is rendered. - - The template output. - - - - Renders the template output to a string. - - - - - - Renders the template output to a string. - - - - - - Renders the template output to a file. - - The output file path. - if set to true overwrite file. - - - - Renders the template output to a file. - - The output file path. - The file that the output is dependent upon. - if set to true overwrite file. - - - - Renders the template output to a file. - - The output file. - if set to true overwrite file. - - - - Renders the template output to a file using a merge strategy. - - The output path. - The merge strategy to use. - - - - Renders the template output to a file using a merge strategy. - - The output path. - The merge strategy to use. - The source file to base the merge on. - - - - Renders the template output to a file using a merge strategy. - - The output file. - The merge strategy to use. - The source file to base the merge on. - - - - Validates this template instance. - - - - - Allows the developer to add custom ValidationErrors to the validation collection. - - - - - Gets a default name for the template output. This can be overridden in your template to provide a suitable name for the output of the template based on the property values. - - A file name (E.G., output.txt). - - - - Set the context data object for the template. - Any items in the current ContextData object will be added to the new one. - This method should only be called while a template is initializing. - - The context data object. - - - - Parses the default value for properties. This can be overridden to provide custom default parsing logic. - - Target property. - Default value. - - - - Gets the property attribute. Property attributes can be used to store various information at the property level. - - Name of the property. - Name of the attribute. - The value of the attribute - - - - Sets the property attribute. Property attributes can be used to store various information at the property level. - - Name of the property. - Name of the attribute. - The attribute value. - - - - Gets the template attribute. Template attributes can be used to store various information at the template level. - - Name of the attribute. - The value of the attribute - - - - Sets the template attribute. Template attributes can be used to store various information at the template level. - - Name of the attribute. - The attribute value. - - - - Gets information on all of the properties on this template. - - - - - Gets information on all of the properties on this template. - - Whether non-browsable properties should be included. - - - - Gets information on all of the required properties for this template. - - - - - Saves the properties to a Hashtable. - - - - - - Saves the properties to a Hashtable. - - - Whether non-browsable properties should be included. - - - - Saves the properties to a Hashtable. - - - Whether non-browsable properties should be included. - Ignores the specified list of property names when copying properties to the template. - - - - Restores the properties from a Hashtable. - - Properties. - - - - Saves the properties to an XML file. - - XML file path. - - - - Copies any matching properties to another CodeTemplate instance. - - Template. - - - - Copies any matching properties to another CodeTemplate instance. - - Template. - Ignores the specified list of property names when copying properties to the template. - - - - Copies any matching properties to another CodeTemplate instance. - - Template. - Whether non-browsable properties should be included. - - - - Copies any matching properties to another CodeTemplate instance. - - Template. - Whether non-browsable properties should be included. - Ignores the specified list of property names when copying properties to the template. - - - - Restores the properties from XML file. - - XML file path. - - - - Saves the properties to XML. - - XmlWriter. - - - - Saves the properties to XML. - - - - - - Restores the properties from XML. - - The XML fragment. - The base directory to resolve file references. - - - - Sets the value for the specified property. - - Property name. - Value to assign. - - - - Sets the value for the specified property. - - Information about the target property. - Value to assign. - - - - Gets the value for the specified property. - - Property name. - - - - - Gets an instance of the specified template. - - Path to the template. - - - - - Gets an instance of the specified template. - - Path to the template. - Forces the template to be recompiled instead of using the cache. - The CodeTemplate instance. - - - - Gets an instance of the specified template. - - Path to the template. - Forces the template to be recompiled instead of using the cache. - Share template context data with the child template instance. - The CodeTemplate instance. - - - - This is the method that is responsible for notifying - receivers that the event occurred - - - - - This is the method that is responsible for notifying - receivers that the event occurred - - - - - This is the method that is responsible for notifying - receivers that the event occurred - - - - - Raises the event. - - The instance containing the event data. - - - - Registers the reference. When generating from within Visual Studio, these references will be added to the project. - - Name of the reference. - - - - Gets an instance of the specified template type. Should be used with the Register directive. - - The CodeTemplate instance. - - - - Gets an instance of the specified template type. Should be used with the Register directive. - - Share template context data with the child template instance. - The CodeTemplate instance. - - - - Response stream for the template output. You can write to the stream programmatically using this property. - - - - - - Provides a way to report execution progress for the template. - - - - - - State of this template instance. - - - - - - Gets the information for the current template. - - - - - - Gets the validation errors for this template. - - - - - - Contextual data items can be stored in this object and shared with sub-templates. This data will not be persisted with the template property set. - - - - - Raised when an output file is registered - - - - - Raised prior to an output being written to disk - - - - - Occurs when a reference is registered. - - - - - Increments the IndentLevel by one. - - - - - The number of tabs or spaces the generated output should indent. - - - - - This value is outputted once for each Indent Level. IndentValue defaults to one tab. - - - - - Excludes a property from being required. - - - - - Marks a property as optional. - - - - - A class representing a template output file. - - - - - Initializes a new instance of the class. - - The output file. - - - - Initializes a new instance of the class. - - The output file. - The metadata. - - - - Initializes a new instance of the class. - - The output file. - The file that output is dependent upon. - - - - Initializes a new instance of the class. - - The output file. - The file that output is dependent upon. - The metadata. - - - - Performs an implicit conversion from to . - - The output file. - The result of the conversion. - - - - Performs an implicit conversion from to . - - The output file. - The result of the conversion. - - - - Gets the name of the output file. - - The name of the output file. - - - - Gets or sets the dependent upon file. - - The dependent upon file. - - - - Gets the metadata. - - The metadata. - - - - - - - - - Initializes a new instance of the class. - - Name of the item. - - - - Gets or sets the name of the item. - - The name of the item. - - - - - - - - - EventArgs derived type which holds the custom event fields - - - - - Initializes a new instance of the class. - - The output file. - - - - Initializes a new instance of the class. - - The output file. - - - - Gets or sets the output file. - - The output file. - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - - - - - Initializes a new instance of the class. - - The output file. - - - - Gets or sets a value indicating whether this output is canceled - - true if cancel; otherwise, false. - - - - This represents the delegate method prototype that - event receivers must implement - - - - - This represents the delegate method prototype that - event receivers must implement - - - - - Progress information. - - - - - Performs a progress step. - - - - - Increments the current progress value by the specified amount. - - Value. - - - - Occurs when the progress value has been changed. - - - - - Occurs when the progress value has been changed. - - - - - Current status for the operation. - - - - - Minimum progress value. - - - - - Maximum progress value. - - - - - Gets or sets the current progress value. - - - - - - Gets or sets the step increment value. - - - - - - Provides information about a template validation error. - - - - - The Validation Message - - - - - The name of the Property. - - - - - EventArgs derived type which holds the custom event fields - - - - - Initializes a new instance of the class. - - if set to true generation was successful. - The registered outputs. - - - - Initializes a new instance of the class. - - if set to true [result]. - The registered outputs. - The registered references. - - - - Initializes a new instance of the class. - - if set to true [result]. - The registered outputs. - The registered references. - The file path of the CodeSmith project file. - - - - Initializes a new instance of the class. - - The error. - - - - This represents the delegate method prototype that - event receivers must implement - - - - - EventArgs derived type which holds the custom event fields - - - - - Use this constructor to initialize the event arguments - object with the custom event fields - - - - - This represents the delegate method prototype that - event receivers must implement - - - - - This represents the delegate method prototype that - event receivers must implement - - - - - EventArgs derived type which holds the custom event fields - - - - - Use this constructor to initialize the event arguments - object with the custom event fields - - - - - This represents the delegate method prototype that - event receivers must implement - - - - - EventArgs derived type which holds the custom event fields - - - - - This ensures that if the licensing is reset during runtime, that this is only ever incremented once. - - - - - This method checks to see if you are using invalid features of CodeSmith. - E.G. You are using CodeSmith Professional and register a CodeSmith Standard Key. - This will cause CodeSmith Generator Studio to close. - - - - - Clean up any resources being used. - - true if managed resources should be disposed; otherwise, false. - - - - Gets the map files. - - - - - - Loads the specified name. - - The path. - - - - - Loads the map with the specified name by trying to resolve the path. - - The name of the map. - - - - - Tries the resolve path. - - Name of the map. - The base directory. - The full path. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - Whether map lookups should be case sensitive or not. - - - - Initializes a new instance of the class. - - Name of the map file. - - - - Initializes a new instance of the class. - - Name of the map file. - if set to true the key and value will be reversed. - - - - Initializes a new instance of the class. - - Name of the map file. - if set to true the key and value will be reversed. - The default value. - - - - Saves the specified map file name. - - Name of the map file. - - - - Loads the specified map file name. - - Name of the map file. - - - - - Loads the specified map file name. - - Name of the map file. - if set to true [reverse]. - - - - - Returns a that represents the current . - - - A that represents the current . - - - - - Adds an element with the provided key and value to the . - - The object to use as the key of the element to add. - The object to use as the value of the element to add. - The is read-only. - An element with the same key already exists in the . - key is null. - - - - Determines whether the contains an element with the specified key. - - The key to locate in the . - - true if the contains an element with the key; otherwise, false. - - key is null. - - - - Determines whether the specified value contains value. - - The value. - - true if the specified value contains value; otherwise, false. - - - - - Gets the enumerator. - - - - - - Removes the element with the specified key from the . - - The key of the element to remove. - - true if the element is successfully removed; otherwise, false. This method also returns false if key was not found in the original . - - The is read-only. - key is null. - - - - Tries the get value. - - The key. - The value. - - - - - Returns an enumerator that iterates through a collection. - - - An object that can be used to iterate through the collection. - - - - - Copies the elements of the to an , starting at a particular index. - - The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. - The zero-based index in array at which copying begins. - array is null. - index is less than zero. - array is multidimensional.-or- index is equal to or greater than the length of array.-or- The number of elements in the source is greater than the available space from index to the end of the destination array. - The type of the source cannot be cast automatically to the type of the destination array. - - - - Adds an element with the provided key and value to the object. - - The to use as the key of the element to add. - The to use as the value of the element to add. - An element with the same key already exists in the object. - key is null. - The is read-only.-or- The has a fixed size. - - - - Removes all items from the . - - The is read-only. - - - - Determines whether the object contains an element with the specified key. - - The key to locate in the object. - - true if the contains an element with the key; otherwise, false. - - key is null. - - - - Returns an object for the object. - - - An object for the object. - - - - - Removes the element with the specified key from the object. - - The key of the element to remove. - The object is read-only.-or- The has a fixed size. - key is null. - - - - Runs when the entire object graph has been deserialized. - - The object that initiated the callback. The functionality for this parameter is not currently implemented. - - - - Populates a with the data needed to serialize the target object. - - The to populate with data. - The destination (see ) for this serialization. - The caller does not have the required permission. - - - - Adds an item to the . - - The object to add to the . - The is read-only. - - - - Determines whether the contains a specific value. - - The object to locate in the . - - true if item is found in the ; otherwise, false. - - - - - Copies the elements of the to an , starting at a particular index. - - The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. - The zero-based index in array at which copying begins. - arrayIndex is less than 0. - array is null. - array is multidimensional.-or-arrayIndex is equal to or greater than the length of array.-or-The number of elements in the source is greater than the available space from arrayIndex to the end of the destination array.-or-Type T cannot be cast automatically to the type of the destination array. - - - - Removes the first occurrence of a specific object from the . - - The object to remove from the . - - true if item was successfully removed from the ; otherwise, false. This method also returns false if item is not found in the original . - - The is read-only. - - - - Returns an enumerator that iterates through the collection. - - - A that can be used to iterate through the collection. - - - - - Gets the name of the file. - - The name of the file. - - - - Whether the map lookups should be case Sensitive. - - - - - Gets or sets the description. - - The description. - - - - Gets a value indicating whether this key value pair is reversed. - - true if reversed; otherwise, false. - - - - Gets or sets a value indicating whether to return the key when its not found in the collection. - - true to return the key; otherwise, false. - - - - Gets or sets the default value for when the key is not found. - - The default value. - - - - Gets an containing the keys of the . - - - An containing the keys of the object that implements . - - - - Gets an containing the values in the . - - - An containing the values in the object that implements . - - - - Gets or sets the with the specified key. - - - - - - Gets the with the specified key. - - - - - - Gets the number of elements contained in the . - - - The number of elements contained in the . - - - - Gets a value indicating whether access to the is synchronized (thread safe). - - - true if access to the is synchronized (thread safe); otherwise, false. - - - - Gets an object that can be used to synchronize access to the . - - - An object that can be used to synchronize access to the . - - - - Gets a value indicating whether the object has a fixed size. - - - true if the object has a fixed size; otherwise, false. - - - - Gets a value indicating whether the is read-only. - - - true if the is read-only; otherwise, false. - - - - Gets an containing the keys of the . - - - An containing the keys of the object that implements . - - - - Gets an containing the values in the . - - - An containing the values in the object that implements . - - - - Gets or sets the with the specified key. - - - - - - Gets a value indicating whether the is read-only. - - - true if the is read-only; otherwise, false. - - - - This interface can be implemented to create a merge strategy. - - - - - Initialize the merge strategy by passing in an initialization string. - - Initialization string. - - - - Merge the template output with the existing content. - - A reference to the MergeContext. - - - - - This merge strategy can be used to insert the template output into a named region inside an existing file - and preserve the rest of the content in the file. The initialization string should be of the format: - "RegionName=<RegionName>;Language=<Language>" - - - - - This merge strategy can be used to preserve designated custom regions inside an existing file and - regenerate the rest of the content in the file. The initialization string should be of the format: - "RegionNameRegex=<RegexExpression>;Language=<Language>" - - - - - A strongly-typed resource class, for looking up localized strings, etc. - - - - - Returns the cached ResourceManager instance used by this class. - - - - - Overrides the current thread's CurrentUICulture property for all - resource lookups using this strongly typed resource class. - - - - - Looks up a localized string similar to The trial key is invalid. Please try again or contact customer service for assistance.. - - - - - Looks up a localized string similar to The key you entered appears to be a full license key. Please use the Register button to register your license.. - - - - - Looks up a localized string similar to Request Trial Key. - - - - - Looks up a localized string similar to Trial Key. - - - - - Looks up a localized string similar to Please enter your {0} trial key and click Try. If you do not have a trial key, please click the Request Trial Key link on the left. If you already have a license, click the Register button below.. - - - - - Looks up a localized string similar to Enter Trial Key. - - - - - Interface that all property refreshers must implement. - - - - - Context information passed into the IPropertyRefresher methods. - - - - - The property that the context points to. - - - - - The CodeTemplate instance that the context points to. - - - - - The working directory for resolving and creating relative paths. - - - - - Saves the project to a file. - - Name of the file. - - - - Saves the project to a file. - - Name of the file. - if set to true make paths relative. - - - - Saves project to string. - - An XML string of the project. - - - - Saves project to string. - - if set to true apply variables. - An XML string of the project. - - - - Makes the paths relative to the directory of . - - - - - Inserts variables into the property set where the value matches. - - The original project. - A new instance of CodeSmithProject. - - - - Replaces variables in the property set with the variables value. - - The original project. - A new instance of CodeSmithProject. - - - - Returns a that represents the current . - - - A that represents the current . - - - - - Loads the project from a file. - - Name of the file. - - - - - Loads the project from a file. - - Name of the file. - if set to true replace variables. - - - - - Loads the project from a string. - - The XML fragment. - - - - - Loads from string. - - The XML fragment. - if set to true replace variables. - - - - - Creates a new object that is a copy of the current instance. - - - A new object that is a copy of this instance. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Context information passed into the IPropertySerializer methods. - - - - - Initializes a new instance of the class. - - The property that the context points to. - The CodeTemplate instance that the context points to. - - - - Initializes a new instance of the class. - - The property info. - The instance. - The working directory. - - - - The property that the context points to. - - - - - The CodeTemplate instance that the context points to. - - - - - The working directory for resolving and creating relative paths. - - - - - This class can be used to convert to and from an XmlNode for a class that supports XML serialization. - - - - - This type editor can be used on a class that supports XML serialization to allow the user to pick an XML file and have that XML file deserialized into the target class. - - - - - This class can be used to convert to and from an XmlNode for a class that supports XML serialization. - - - - - Allows you to browse for an XSD Schema. - - - - - The AdoHelper class is intended to encapsulate high performance, scalable best practices for - common data access uses. - - The type of the factory. - - - - Get an IDataParameter for use in a SQL command - - The name of the parameter to create - The value of the specified parameter - An IDataParameter object - - - - Get an IDataParameter for use in a SQL command - - The name of the parameter to create - The System.Data.DbType of the parameter - The size of the parameter - The System.Data.ParameterDirection of the parameter - An IDataParameter object - - - - Get an IDataParameter for use in a SQL command - - The name of the parameter to create - The System.Data.DbType of the parameter - The size of the parameter - The source column of the parameter - The System.Data.DataRowVersion of the parameter - An IDataParameter object - - - - This method is used to attach array of IDataParameters to an IDbCommand. - - This method will assign a value of DbNull to any parameter with a direction of - InputOutput and a value of null. - - This behavior will prevent default values from being used, but - this will be the less common case than an intended pure output parameter (derived as InputOutput) - where the user provided no input value. - - The command to which the parameters will be added - An array of IDataParameterParameters to be added to command - Thrown if command is null. - - - - This method assigns dataRow column values to an IDataParameterCollection - - The IDataParameterCollection to be assigned values - The dataRow used to hold the stored procedure's parameter values - Thrown if any of the parameter names are invalid. - - - - This method assigns dataRow column values to an array of IDataParameters - - Array of IDataParameters to be assigned values - The dataRow used to hold the stored procedure's parameter values - Thrown if any of the parameter names are invalid. - - - - This method assigns an array of values to an array of IDataParameters - - Array of IDataParameters to be assigned values - Array of objects holding the values to be assigned - Thrown if an incorrect number of parameters are passed. - - - - This method cleans up the parameter syntax for the provider - - The IDbCommand containing the parameters to clean up. - - - - This method opens (if necessary) and assigns a connection, transaction, command type and parameters - to the provided command - - The IDbCommand to be prepared - A valid IDbConnection, on which to execute this command - A valid IDbTransaction, or 'null' - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters to be associated with the command or 'null' if no parameters are required - true if the connection was opened by the method, otherwose is false. - Thrown if command is null. - Thrown if commandText is null. - - - - This method clears (if necessary) the connection, transaction, command type and parameters - from the provided command - - - Not implemented here because the behavior of this method differs on each data provider. - - The IDbCommand to be cleared - - - - Execute an IDbCommand (that returns a resultset) against the database specified in - the connection string. - - The IDbCommand object to use - A DataSet containing the resultset generated by the command - Thrown if command is null. - - - - Execute an IDbCommand (that returns a resultset and takes no parameters) against the database specified in - the connection string. - - - - DataSet ds = helper.ExecuteDataset(connString, CommandType.StoredProcedure, "GetOrders"); - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A DataSet containing the resultset generated by the command - Thrown if connectionString is null - Thrown if commandText is null - A DataSet containing the resultset generated by the command - - - - Execute an IDbCommand (that returns a resultset) against the database specified in the connection string - using the provided parameters. - - - - DataSet ds = helper.ExecuteDataset(connString, CommandType.StoredProcedure, "GetOrders", new IDbParameter("@prodid", 24)); - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDbParamters used to execute the command - A DataSet containing the resultset generated by the command - Thrown if connectionString is null - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the database specified in - the connection string using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - DataSet ds = helper.ExecuteDataset(connString, "GetOrders", 24, 36); - - A valid connection string for an IDbConnection - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - A DataSet containing the resultset generated by the command - Thrown if connectionString is null - Thrown if spName is null - - - - Execute an IDbCommand (that returns a resultset and takes no parameters) against the provided IDbConnection. - - - - DataSet ds = helper.ExecuteDataset(conn, CommandType.StoredProcedure, "GetOrders"); - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A DataSet containing the resultset generated by the command - Thrown if commandText is null - Thrown if connection is null - - - - Execute an IDbCommand (that returns a resultset) against the specified IDbConnection - using the provided parameters. - - - - DataSet ds = helper.ExecuteDataset(conn, CommandType.StoredProcedure, "GetOrders", new IDataParameter("@prodid", 24)); - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - A DataSet containing the resultset generated by the command - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - Thrown if connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified IDbConnection - using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - DataSet ds = helper.ExecuteDataset(conn, "GetOrders", 24, 36); - - A valid IDbConnection - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - A DataSet containing the resultset generated by the command - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - Thrown if connection is null - - - - Execute an IDbCommand (that returns a resultset and takes no parameters) against the provided IDbTransaction. - - - DataSet ds = helper.ExecuteDataset(trans, CommandType.StoredProcedure, "GetOrders"); - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A DataSet containing the resultset generated by the command - Thrown if commandText is null - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute an IDbCommand (that returns a resultset) against the specified IDbTransaction - using the provided parameters. - - - - DataSet ds = helper.ExecuteDataset(trans, CommandType.StoredProcedure, "GetOrders", new IDataParameter("@prodid", 24)); - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - A DataSet containing the resultset generated by the command - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified - IDbTransaction using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - DataSet ds = helper.ExecuteDataset(tran, "GetOrders", 24, 36); - - A valid IDbTransaction - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - A DataSet containing the resultset generated by the command - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute an IDbCommand (that returns no resultset) against the database - - The IDbCommand to execute - An int representing the number of rows affected by the command - Thrown if command is null. - - - - Execute an IDbCommand (that returns no resultset and takes no parameters) against the database specified in - the connection string - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An int representing the number of rows affected by the command - Thrown if connectionString is null - Thrown if commandText is null - - - - Execute an IDbCommand (that returns no resultset) against the database specified in the connection string - using the provided parameters - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - An int representing the number of rows affected by the command - Thrown if connectionString is null - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - - - - Execute a stored procedure via an IDbCommand (that returns no resultset) against the database specified in - the connection string using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - A valid connection string for an IDbConnection - The name of the stored prcedure - An array of objects to be assigned as the input values of the stored procedure - An int representing the number of rows affected by the command - Thrown if connectionString is null - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - - - - Execute an IDbCommand (that returns no resultset and takes no parameters) against the provided IDbConnection. - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An int representing the number of rows affected by the command - Thrown if commandText is null - Thrown if connection is null - - - - Execute an IDbCommand (that returns no resultset) against the specified IDbConnection - using the provided parameters. - - - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDbParamters used to execute the command - An int representing the number of rows affected by the command - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - Thrown if connection is null - - - - Execute a stored procedure via an IDbCommand (that returns no resultset) against the specified IDbConnection - using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - A valid IDbConnection - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - An int representing the number of rows affected by the command - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - Thrown if connection is null - - - - Execute an IDbCommand (that returns no resultset and takes no parameters) against the provided IDbTransaction. - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An int representing the number of rows affected by the command - Thrown if commandText is null - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute an IDbCommand (that returns no resultset) against the specified IDbTransaction - using the provided parameters. - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - An int representing the number of rows affected by the command - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute a stored procedure via an IDbCommand (that returns no resultset) against the specified - IDbTransaction using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - A valid IDbTransaction - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - An int representing the number of rows affected by the command - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute an IDbCommand (that returns a resultset) against the database specified in - the connection string. - - The IDbCommand object to use - A IDataReader containing the resultset generated by the command - Thrown if command is null. - - - - Execute an IDbCommand (that returns a resultset) against the database specified in - the connection string. - - The IDbCommand object to use - Enum indicating whether the connection was created internally or externally. - A IDataReader containing the resultset generated by the command - Thrown if command is null. - - - - Create and prepare an IDbCommand, and call ExecuteReader with the appropriate CommandBehavior. - - - If we created and opened the connection, we want the connection to be closed when the DataReader is closed. - - If the caller provided the connection, we want to leave it to them to manage. - - A valid IDbConnection, on which to execute this command - A valid IDbTransaction, or 'null' - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters to be associated with the command or 'null' if no parameters are required - Indicates whether the connection parameter was provided by the caller, or created by AdoHelper - IDataReader containing the results of the command - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - Thrown if connection is null - - - - Execute an IDbCommand (that returns a resultset and takes no parameters) against the database specified in - the connection string. - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A IDataReader containing the resultset generated by the command - Thrown if connectionString is null - Thrown if commandText is null - - - - Execute an IDbCommand (that returns a resultset) against the database specified in the connection string - using the provided parameters. - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - A IDataReader containing the resultset generated by the command - Thrown if connectionString is null - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the database specified in - the connection string using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - IDataReader dr = helper.ExecuteReader(connString, "GetOrders", 24, 36); - - A valid connection string for an IDbConnection - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - an IDataReader containing the resultset generated by the command - Thrown if connectionString is null - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - - - - Execute an IDbCommand (that returns a resultset and takes no parameters) against the provided IDbConnection. - - - - IDataReader dr = helper.ExecuteReader(conn, CommandType.StoredProcedure, "GetOrders"); - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - an IDataReader containing the resultset generated by the command - Thrown if commandText is null - - - - Execute an IDbCommand (that returns a resultset) against the specified IDbConnection - using the provided parameters. - - - - IDataReader dr = helper.ExecuteReader(conn, CommandType.StoredProcedure, "GetOrders", new IDataParameter("@prodid", 24)); - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - an IDataReader containing the resultset generated by the command - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - Thrown if connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified IDbConnection - using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - IDataReader dr = helper.ExecuteReader(conn, "GetOrders", 24, 36); - - A valid IDbConnection - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - an IDataReader containing the resultset generated by the command - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - Thrown if connection is null - - - - Execute an IDbCommand (that returns a resultset and takes no parameters) against the provided IDbTransaction. - - - IDataReader dr = helper.ExecuteReader(trans, CommandType.StoredProcedure, "GetOrders"); - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A IDataReader containing the resultset generated by the command - Thrown if commandText is null - - - - Execute an IDbCommand (that returns a resultset) against the specified IDbTransaction - using the provided parameters. - - - - IDataReader dr = helper.ExecuteReader(trans, CommandType.StoredProcedure, "GetOrders", new IDataParameter("@prodid", 24)); - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - A IDataReader containing the resultset generated by the command - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified - IDbTransaction using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - IDataReader dr = helper.ExecuteReader(tran, "GetOrders", 24, 36); - - A valid IDbTransaction - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - an IDataReader containing the resultset generated by the command - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute an IDbCommand (that returns a 1x1 resultset) against the database specified in - the connection string. - - The IDbCommand to execute - An object containing the value in the 1x1 resultset generated by the command - Thrown if command is null. - - - - Execute an IDbCommand (that returns a 1x1 resultset and takes no parameters) against the database specified in - the connection string. - - - - int orderCount = (int)helper.ExecuteScalar(connString, CommandType.StoredProcedure, "GetOrderCount"); - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An object containing the value in the 1x1 resultset generated by the command - Thrown if connectionString is null - Thrown if commandText is null - - - - Execute an IDbCommand (that returns a 1x1 resultset) against the database specified in the connection string - using the provided parameters. - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - An object containing the value in the 1x1 resultset generated by the command - Thrown if connectionString is null - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - - - - Execute a stored procedure via an IDbCommand (that returns a 1x1 resultset) against the database specified in - the connection string using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - int orderCount = (int)helper.ExecuteScalar(connString, "GetOrderCount", 24, 36); - - A valid connection string for an IDbConnection - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - An object containing the value in the 1x1 resultset generated by the command - Thrown if connectionString is null - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - - - - Execute an IDbCommand (that returns a 1x1 resultset and takes no parameters) against the provided IDbConnection. - - - - int orderCount = (int)helper.ExecuteScalar(conn, CommandType.StoredProcedure, "GetOrderCount"); - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An object containing the value in the 1x1 resultset generated by the command - Thrown if commandText is null - - - - Execute an IDbCommand (that returns a 1x1 resultset) against the specified IDbConnection - using the provided parameters. - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - An object containing the value in the 1x1 resultset generated by the command - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - Thrown if connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a 1x1 resultset) against the specified IDbConnection - using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - int orderCount = (int)helper.ExecuteScalar(conn, "GetOrderCount", 24, 36); - - A valid IDbConnection - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - An object containing the value in the 1x1 resultset generated by the command - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - Thrown if connection is null - - - - Execute an IDbCommand (that returns a 1x1 resultset and takes no parameters) against the provided IDbTransaction. - - - - int orderCount = (int)helper.ExecuteScalar(tran, CommandType.StoredProcedure, "GetOrderCount"); - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An object containing the value in the 1x1 resultset generated by the command - Thrown if commandText is null - - - - Execute an IDbCommand (that returns a 1x1 resultset) against the specified IDbTransaction - using the provided parameters. - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDbParamters used to execute the command - An object containing the value in the 1x1 resultset generated by the command - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a 1x1 resultset) against the specified - IDbTransaction using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - int orderCount = (int)helper.ExecuteScalar(tran, "GetOrderCount", 24, 36); - - A valid IDbTransaction - The name of the stored procedure - An array of objects to be assigned as the input values of the stored procedure - An object containing the value in the 1x1 resultset generated by the command - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - Thrown if transaction is null - Thrown if transaction.Connection is null - Thrown if the transaction is rolled back or commmitted - - - - Execute an IDbCommand (that returns a resultset) against the database specified in - the connection string. - - The IDbCommand to execute - A DataSet wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - Thrown if command is null. - - - - Execute an IDbCommand (that returns a resultset and takes no parameters) against the database specified in - the connection string. - - - - helper.FillDataset(connString, CommandType.StoredProcedure, "GetOrders", ds, new string[] {"orders"}); - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A DataSet wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - Thrown if connectionString is null - Thrown if commandText is null - - - - Execute an IDbCommand (that returns a resultset) against the database specified in the connection string - using the provided parameters. - - A valid connection string for an IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - An array of IDataParameters used to execute the command - A DataSet wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - - Thrown if connectionString is null - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if commandText is null - Thrown if the parameter count does not match the number of values supplied - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the database specified in - the connection string using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - helper.FillDataset(connString, CommandType.StoredProcedure, "GetOrders", ds, new string[] {"orders"}, 24); - - A valid connection string for an IDbConnection - The name of the stored procedure - A dataset wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - - An array of objects to be assigned as the input values of the stored procedure - Thrown if connectionString is null - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - - - - Execute an IDbCommand (that returns a resultset and takes no parameters) against the provided IDbConnection. - - - - helper.FillDataset(conn, CommandType.StoredProcedure, "GetOrders", ds, new string[] {"orders"}); - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A dataset wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - - Thrown if commandText is null - Thrown if connection is null - - - - Execute an IDbCommand (that returns a resultset) against the specified IDbConnection - using the provided parameters. - - A valid IDbConnection - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A DataSet wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - - An array of IDataParameters used to execute the command - Thrown if commandText is null - Thrown if connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified IDbConnection - using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - helper.FillDataset(conn, "GetOrders", ds, new string[] {"orders"}, 24, 36); - - A valid IDbConnection - The name of the stored procedure - A dataset wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - - An array of objects to be assigned as the input values of the stored procedure - Thrown if spName is null - Thrown if connection is null - - - - Execute an IDbCommand (that returns a resultset and takes no parameters) against the provided IDbTransaction. - - - - helper.FillDataset(tran, CommandType.StoredProcedure, "GetOrders", ds, new string[] {"orders"}); - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A dataset wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - - Thrown if commandText is null - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute an IDbCommand (that returns a resultset) against the specified IDbTransaction - using the provided parameters. - - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A DataSet wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - - An array of IDataParameters used to execute the command - Thrown if commandText is null - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified - IDbTransaction using the provided parameter values. This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - - This method provides no access to output parameters or the stored procedure's return value parameter. - - - - helper.FillDataset(tran, "GetOrders", ds, new string[] {"orders"}, 24, 36); - - A valid IDbTransaction - The name of the stored procedure - A dataset wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - - An array of objects to be assigned as the input values of the stored procedure - Thrown if spName is null - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Private helper method that execute an IDbCommand (that returns a resultset) against the specified IDbTransaction and IDbConnection - using the provided parameters. - - A valid IDbConnection - A valid IDbTransaction - The CommandType (stored procedure, text, etc.) - The stored procedure name or SQL command - A DataSet wich will contain the resultset generated by the command - This array will be used to create table mappings allowing the DataTables to be referenced - by a user defined name (probably the actual table name) - - An array of IDataParameters used to execute the command - - - - This method consumes the RowUpdatingEvent and passes it on to the consumer specifed in the call to UpdateDataset - - The object that generated the event - The System.Data.Common.RowUpdatingEventArgs - - - - This method consumes the RowUpdatedEvent and passes it on to the consumer specifed in the call to UpdateDataset - - The object that generated the event - The System.Data.Common.RowUpdatingEventArgs - - - - Set up a command for updating a DataSet. - - command object to prepare - output parameter specifying whether the connection used should be closed by the DAAB - An IDbCommand object - - - - Executes the respective command for each inserted, updated, or deleted row in the DataSet. - - - - helper.UpdateDataset(conn, insertCommand, deleteCommand, updateCommand, dataSet, "Order"); - - A valid SQL statement or stored procedure to insert new records into the data source - A valid SQL statement or stored procedure to delete records from the data source - A valid SQL statement or stored procedure used to update records in the data source - The DataSet used to update the data source - The DataTable used to update the data source. - - - - Executes the IDbCommand for each inserted, updated, or deleted row in the DataSet also implementing RowUpdating and RowUpdated Event Handlers - - - - RowUpdatingEventHandler rowUpdatingHandler = new RowUpdatingEventHandler( OnRowUpdating ); - RowUpdatedEventHandler rowUpdatedHandler = new RowUpdatedEventHandler( OnRowUpdated ); - helper.UpdateDataSet(sqlInsertCommand, sqlDeleteCommand, sqlUpdateCommand, dataSet, "Order", rowUpdatingHandler, rowUpdatedHandler); - - A valid SQL statement or stored procedure to insert new records into the data source - A valid SQL statement or stored procedure to delete records from the data source - A valid SQL statement or stored procedure used to update records in the data source - The DataSet used to update the data source - The DataTable used to update the data source. - RowUpdatingEventHandler - RowUpdatedEventHandler - - - - Simplify the creation of an IDbCommand object by allowing - a stored procedure and optional parameters to be provided - - - - IDbCommand command = helper.CreateCommand(conn, "AddCustomer", "CustomerID", "CustomerName"); - - A valid connection string for an IDbConnection - The name of the stored procedure - An array of string to be assigned as the source columns of the stored procedure parameters - A valid IDbCommand object - Thrown if connectionString is null - Thrown if any of the IDataParameters.ParameterNames are null, or if the parameter count does not match the number of values supplied - Thrown if spName is null - Thrown if the parameter count does not match the number of values supplied - - - - Simplify the creation of an IDbCommand object by allowing - a stored procedure and optional parameters to be provided - - - - IDbCommand command = helper.CreateCommand(conn, "AddCustomer", "CustomerID", "CustomerName"); - - A valid IDbConnection object - The name of the stored procedure - An array of string to be assigned as the source columns of the stored procedure parameters - A valid IDbCommand object - Thrown if spName is null - Thrown if connection is null - - - - Simplify the creation of an IDbCommand object by allowing - a stored procedure and optional parameters to be provided - - A valid connection string for an IDbConnection - A valid SQL statement - A System.Data.CommandType - The parameters for the SQL statement - A valid IDbCommand object - - - - Simplify the creation of an IDbCommand object by allowing - a stored procedure and optional parameters to be provided - - - IDbCommand command = helper.CreateCommand(conn, "AddCustomer", "CustomerID", "CustomerName"); - - A valid IDbConnection object - A valid SQL statement - A System.Data.CommandType - The parameters for the SQL statement - A valid IDbCommand object - - - - Execute a stored procedure via an IDbCommand (that returns no resultset) - against the database specified in the connection string using the - dataRow column values as the stored procedure's parameters values. - This method will assign the parameter values based on row values. - - The IDbCommand to execute - The dataRow used to hold the stored procedure's parameter values. - An int representing the number of rows affected by the command - Thrown if command is null. - - - - Execute a stored procedure via an IDbCommand (that returns no resultset) against the database specified in - the connection string using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on row values. - - A valid connection string for an IDbConnection - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - An int representing the number of rows affected by the command - Thrown if connectionString is null - Thrown if spName is null - - - - Execute a stored procedure via an IDbCommand (that returns no resultset) against the specified IDbConnection - using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on row values. - - A valid IDbConnection object - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - An int representing the number of rows affected by the command - Thrown if spName is null - Thrown if connection is null - - - - Execute a stored procedure via an IDbCommand (that returns no resultset) against the specified - IDbTransaction using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on row values. - - A valid IDbTransaction object - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - An int representing the number of rows affected by the command - Thrown if spName is null - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the database specified in - the connection string using the dataRow column values as the stored procedure's parameters values. - This method will assign the paraemter values based on row values. - - The IDbCommand to execute - The dataRow used to hold the stored procedure's parameter values. - A DataSet containing the resultset generated by the command - Thrown if command is null. - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the database specified in - the connection string using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on row values. - - A valid connection string for an IDbConnection - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - A DataSet containing the resultset generated by the command - Thrown if connectionString is null - Thrown if spName is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified IDbConnection - using the dataRow column values as the store procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on row values. - - A valid IDbConnection object - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - A DataSet containing the resultset generated by the command - Thrown if spName is null - Thrown if connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified IDbTransaction - using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on row values. - - A valid IDbTransaction object - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - A DataSet containing the resultset generated by the command - Thrown if spName is null - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the database specified in - the connection string using the dataRow column values as the stored procedure's parameters values. - This method will assign the parameter values based on parameter order. - - The IDbCommand to execute - The dataRow used to hold the stored procedure's parameter values. - A IDataReader containing the resultset generated by the command - Thrown if command is null. - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the database specified in - the connection string using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - A valid connection string for an IDbConnection - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - A IDataReader containing the resultset generated by the command - Thrown if connectionString is null - Thrown if spName is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified IDbConnection - using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - A valid IDbConnection object - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - A IDataReader containing the resultset generated by the command - Thrown if spName is null - Thrown if connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a resultset) against the specified IDbTransaction - using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - A valid IDbTransaction object - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - A IDataReader containing the resultset generated by the command - Thrown if spName is null - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a 1x1 resultset) against the database specified in - the connection string using the dataRow column values as the stored procedure's parameters values. - This method will assign the parameter values based on parameter order. - - The IDbCommand to execute - The dataRow used to hold the stored procedure's parameter values. - An object containing the value in the 1x1 resultset generated by the command - Thrown if command is null. - - - - Execute a stored procedure via an IDbCommand (that returns a 1x1 resultset) against the database specified in - the connection string using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - A valid connection string for an IDbConnection - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - An object containing the value in the 1x1 resultset generated by the command - Thrown if connectionString is null - Thrown if spName is null - - - - Execute a stored procedure via an IDbCommand (that returns a 1x1 resultset) against the specified IDbConnection - using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - A valid IDbConnection object - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - An object containing the value in the 1x1 resultset generated by the command - Thrown if spName is null - Thrown if connection is null - - - - Execute a stored procedure via an IDbCommand (that returns a 1x1 resultset) against the specified IDbTransaction - using the dataRow column values as the stored procedure's parameters values. - This method will query the database to discover the parameters for the - stored procedure (the first time each stored procedure is called), and assign the values based on parameter order. - - A valid IDbTransaction object - The name of the stored procedure - The dataRow used to hold the stored procedure's parameter values. - An object containing the value in the 1x1 resultset generated by the command - Thrown if spName is null - Thrown if transaction is null - Thrown if transaction.Connection is null - - - - Checks for the existence of a return value parameter in the parametervalues - - An array of objects to be assigned as the input values of the stored procedure - true if the parameterValues contains a return value parameter, false otherwise - - - - Check to see if the parameter values passed to the helper are, in fact, IDataParameters. - - Array of parameter values passed to helper - new array of IDataParameters built from parameter values - True if the parameter values are IDataParameters - - - - Retrieves the set of IDataParameters appropriate for the stored procedure - - - This method will query the database for this information, and then store it in a cache for future requests. - - A valid connection string for an IDbConnection - The name of the stored procedure - An array of IDataParameterParameters - Thrown if connectionString is null - Thrown if spName is null - - - - Retrieves the set of IDataParameters appropriate for the stored procedure - - - This method will query the database for this information, and then store it in a cache for future requests. - - A valid connection string for an IDbConnection - The name of the stored procedure - A bool value indicating whether the return value parameter should be included in the results - An array of IDataParameters - Thrown if connectionString is null - Thrown if spName is null - - - - Retrieves the set of IDataParameters appropriate for the stored procedure - - - This method will query the database for this information, and then store it in a cache for future requests. - - A valid IDataConnection object - The name of the stored procedure - An array of IDataParameters - Thrown if spName is null - Thrown if connection is null - - - - Retrieves the set of IDataParameters appropriate for the stored procedure - - - This method will query the database for this information, and then store it in a cache for future requests. - - A valid IDbConnection object - The name of the stored procedure - A bool value indicating whether the return value parameter should be included in the results - An array of IDataParameters - Thrown if spName is null - Thrown if connection is null - - - - Retrieves the set of IDataParameters appropriate for the stored procedure - - A valid IDbConnection object - The name of the stored procedure - A bool value indicating whether the return value parameter should be included in the results - An array of IDataParameters - Thrown if spName is null - Thrown if connection is null - - - - Retrieve a parameter array from the cache - - A valid connection string for an IDbConnection - The stored procedure name or SQL command - An array of IDataParameters - Thrown if connectionString is null - Thrown if commandText is null - - - - Retrieve a parameter array from the cache - - A valid IDbConnection object - The stored procedure name or SQL command - An array of IDataParameters - Thrown if commandText is null - Thrown if connection is null - - - - Retrieve a parameter array from the cache - - A valid IDbConnection object - The stored procedure name or SQL command - An array of IDataParameters - - - - Add parameter array to the cache - - A valid connection string for an IDbConnection - The stored procedure name or SQL command - An array of IDataParameters to be cached - - - - Add parameter array to the cache - - A valid IDbConnection - The stored procedure name or SQL command - An array of IDataParameters to be cached - - - - Add parameter array to the cache - - A valid IDbConnection - The stored procedure name or SQL command - An array of IDataParameters to be cached - - - - Resolve at run time the appropriate set of IDataParameters for a stored procedure - - A valid IDbConnection object - The name of the stored procedure - Whether or not to include their return value parameter - The parameter array discovered. - Thrown if spName is null - Thrown if connection is null - - - - Internal handler used for bubbling up the event to the user - - - - - Internal handler used for bubbling up the event to the user - - - - - Returns an IDbConnection object for the given connection string - - The connection string to be used to create the connection - An IDbConnection object - Thrown if connectionString is null - - - - Returns an IDbDataAdapter object - - The IDbDataAdapter - - - - Calls the CommandBuilder.DeriveParameters method for the specified provider, doing any setup and cleanup necessary - - The IDbCommand referencing the stored procedure from which the parameter information is to be derived. The derived parameters are added to the Parameters collection of the IDbCommand. - - - - Returns an IDataParameter object - - The IDataParameter object - - - - Provider specific code to set up the updating/ed event handlers used by UpdateDataset - - DataAdapter to attach the event handlers to - The handler to be called when a row is updating - The handler to be called when a row is updated - - - - Returns an array of IDataParameters of the specified size - - size of the array - The array of IDataParameters - - - - Handle any provider-specific issues with BLOBs here by "washing" the IDataParameter and returning a new one that is set up appropriately for the provider. - - The IDbConnection to use in cleansing the parameter - The parameter before cleansing - The parameter after it's been cleansed. - - - - Delegate for creating a RowUpdatedEvent handler - - The object that published the event - The RowUpdatedEventArgs for the event - - - - Delegate for creating a RowUpdatingEvent handler - - The object that published the event - The RowUpdatingEventArgs for the event - - - - This enum is used to indicate whether the connection was provided by the caller, or created by AdoHelper, so that - we can set the appropriate CommandBehavior when calling ExecuteReader() - - - - Connection is owned and managed by ADOHelper - - - Connection is owned and managed by the caller - - - - ADOHelperParameterCache provides functions to leverage a static cache of procedure parameters, and the - ability to discover parameters for stored procedures at run-time. - - - - - Deep copy of cached IDataParameter array - - - - - - - Add parameter array to the cache - - A valid connection string for an IDbConnection - The stored procedure name or SQL command - An array of IDataParameters to be cached - Thrown if commandText is null - Thrown if connectionString is null - - - - Retrieve a parameter array from the cache - - A valid connection string for an IDbConnection - The stored procedure name or SQL command - An array of IDataParameters - Thrown if commandText is null - Thrown if connectionString is null - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - Name of the friendly. - - - - Releases unmanaged resources and performs other cleanup operations before the - is reclaimed by garbage collection. - - - - - Creates an instance in the . - - - - - - Creates an instance in the . - - The constructor args. - - - - - Performs application-defined tasks associated with freeing, - releasing, or resetting unmanaged resources. - - - - - Need a . - - true if a is needed. - - - - Gets the CodeSmith . - - The CodeSmith . - - - - Checks an exception for a licensing related error. - - The exception - Returns true if a licensing exception is found. - - - - Gets the current logger instance or falls back to a default logger. - - - - - Creates a relative path from one file or folder to another. - - Contains the directory that defines the start of the relative path. - Contains the path that defines the endpoint of the relative path. - The relative path from the start directory to the end path. - - - - - Provides enumerated values that indicate the current status of a process. - - - - Process is ready to run. - - - Process is currently running. - - - Process is complete. - - - Process ran with an error. - - - Process timed out. - - - - A base class to run a process. - - - - - Initializes a new instance of the class. - - - - - Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - - - - - Deletes the specified temporary file. - - Name of the file. - - - - Runs the exectuable file with the specified task parameters. - - true if the task runs successfully; otherwise, false. - - - - Runs the executable file. - - The path to tool. - The command line arguments. - The returned exit code of the executable file. - - - - Generates the command line arguments. - - Returns a string value containing the command line arguments to pass directly to the executable file. - - - - Generates the full path to tool. - - Returns the fully qualified path to the executable file. - - - - Gets the working directory. - - Returns the directory in which to run the executable file. - - - - Indicates whether task execution should be skipped. - - true to skip task execution; otherwise, false. - - - - Indicates whether all task parameters are valid. - - true if all task parameters are valid; otherwise, falsec>. - - - - Releases unmanaged and - optionally - managed resources - - true to release both managed and unmanaged resources; false to release only unmanaged resources. - - - - Appends to the standard error writer. - - The message to write. - - - - Appends to the standard error writer. - - A composite format string. - An System.Object array containing zero or more objects to format. - - - - Gets the override value of the PATH environment variable. - - The override value of the PATH environment variable. - - - - Gets the returned exit code of the executable file. - - The returned exit code of the executable file. - - - - Gets the status of the process. - - The status of the process. - - - - Gets the Encoding of the standard error stream of the task. - - The Encoding of the standard error stream of the task. - - - - Gets the Encoding of the standard output stream of the task. - - The Encoding of the standard output stream of the task. - - - - Gets or sets the amount of time after which the task executable is terminated. - - The amount of time after which the task executable is terminated. - - - - Gets the name of the executable file to run. - - The name of the executable file to run. - - - - Gets or sets the path of the executable file to run. - - The path of the executable file to run. - - - - Gets the standard output stream. - - The standard output. - - - - Gets the standard error stream. - - The standard error. - - - - Gets or sets the standard error writer. - - The standard error writer. - - - - Gets or sets the standard output writer. - - The standard output writer. - - - - Utility class for working with the registry. - NOTE: At some time it would be nice to add automatic detection to see if values exist in the Wow6432Node keys. - - - - - Retrieves the specified value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Gets the value for the specified key. - Returns null if an error occurred, else returns the string value for the specified key. - - - - Retrieves the specified value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Gets the value for the specified key. - The value to return if the key does not exist. - Returns null if an error occurred, else returns the string value for the specified key. - - - - Retrieves the specified DWORD value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Gets the value for the specified key. - Returns 0 if an error occurred, else returns the int value for the specified key. - - - - Retrieves the specified DWORD value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Gets the value for the specified key. - The value to return if the key does not exist. - Returns 0 if an error occurred, else returns the int value for the specified key. - - - - Retrieves the specified QWORD value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Gets the value for the specified key. - Returns 0 if an error occurred, else returns the long value for the specified key. - - - - Retrieves the specified QWORD value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Gets the value for the specified key. - The value to return if the key does not exist. - Returns 0 if an error occurred, else returns the long value for the specified key. - - - - Retrieves the specified DWORD value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Gets the value for the specified key. - Returns false if an error occurred, else returns the value for the specified key. - - - - Retrieves the specified DWORD value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Gets the value for the specified key. - The value to return if the key does not exist. - Returns false if an error occurred, else returns the value for the specified key. - - - - Retrieves the specified Binary value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Gets the value for the specified key. - Returns null if an error occurred, else returns the byte[] value for the specified key. - - - - Retrieves the specified Binary value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Gets the value for the specified key. - The value to return if the key does not exist. - Returns null if an error occurred, else returns the byte[] value for the specified key. - - - - Retrieves the specified DateTime value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Gets the value for the specified key. - Returns DateTime.Min if an error occurred, else returns the value for the specified key. - - - - Retrieves the specified DateTime value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Gets the value for the specified key. - The value to return if the key does not exist. - Returns DateTime.Min if an error occurred, else returns the value for the specified key. - - - - Retrieves the type of the specified Registry value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Gets the value type for the specified key name. - - - - Sets or creates the specified String value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Sets the value for the specified key name. - Sets this value to the key name. - - - - Sets or creates the specified DWORD value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Sets the value for the specified key name. - Sets this value to the key name. - - - - Sets or creates the specified QWORD value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Sets the value for the specified key name. - Sets this value to the key name. - - - - Sets or creates the specified Boolean value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Sets the value for the specified key name. - Sets this value to the key name. - - - - Sets or creates the specified Binary value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Sets the value for the specified key name. - Sets this value to the key name. - - - - Sets or creates the specified DateTime value. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Sets the value for the specified key name. - Sets this value to the key name. - - - - Creates a new subkey or opens an existing subkey. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Returns the opened subkey if it could be opened, else returns null. - - - - Deletes a subkey and any child subkeys recursively. - - The RegistryKey that the sub key exists in. - Deletes this key under the supplied RegistryKey. - - - - Deletes the specified value from this (current) key. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Sets the value for the specified key name. - - - - Renames a registry key. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Then name of the new SubKey. - The new SubKey. - - - - Copies a registry key. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Then name of the destination SubKey. - The newly copied SubKey. - - - - Copies a HiveKey Recursively. - - The RegistryKey that the sub key exists in. - Then name of the destination HiveKey. - - - - Retrieves the number of values in the specified Registry hive. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Returns the number of sub keys, else returns 0. - - - - Retrieves the number of values in the specified Registry hive. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - - Returns the number of sub keys, else returns 0. - - - - Checks to see if a subkey exists. - - The RegistryKey that the sub key exists in. - Returns false if the key wasn't found. - - - - Checks to see if a subkey exists. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Returns false if the key wasn't found. - - - - Checks to see if a subkey exists. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - If the name is specified then it will check to see if this value exists. - Returns false if the key wasn't found. - - - - Finds a specific key one level deep from the hivekey. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - Gets the value for the specified key. - Value to check. - The SubKey name if found; returns string.Empty if not found. - - - - Retrieves all key names under a given hive key. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - /// Retrieves all key names under a given hive key. - - - - Retrieves all subkeys under a given hive key. - - The RegistryKey that the sub key exists in. - Opens this key under the supplied RegistryKey. - /// Retrieves all subkeys under a given hive key. - - - - Returns the next available index in a list of registry sub keys. - - The hive key. - Returns the next number in an indexed list. - - - - create the root CodeSmith RegistryKey. - - - - - This is the Error message, it will be null unless an error occurred. - - - - - Returns the root CodeSmith RegistryKey (E.G Software\CodeSmith). - - - - - Returns the CodeSmith RegistryKey (E.G Software\CodeSmith\v6.x). - - - - - This is a DataReader that 'fixes' any null values before - they are returned to our business code. - - - - - Initializes the SafeDataReader object to use data from - the provided DataReader object. - - The source DataReader object containing the data. - - - - Gets a string value from the datareader. - - - Returns empty string for null. - - Ordinal column position of the value. - - - - Gets a value of type from the datareader. - - Ordinal column position of the value. - - - - Gets an integer from the datareader. - - - Returns 0 for null. - - Ordinal column position of the value. - - - - Gets a double from the datareader. - - - Returns 0 for null. - - Ordinal column position of the value. - - - - Gets a Guid value from the datareader. - - - Returns Guid.Empty for null. - - Ordinal column position of the value. - - - - Reads the next row of data from the datareader. - - - - - Moves to the next result set in the datareader. - - - - - Closes the datareader. - - - - - Gets a boolean value from the datareader. - - - Returns for null. - - Ordinal column position of the value. - - - - Gets a byte value from the datareader. - - - Returns 0 for null. - - Ordinal column position of the value. - - - - Invokes the GetBytes method of the underlying datareader. - - - Returns 0 for null. - - Ordinal column position of the value. - Array containing the data. - Offset position within the buffer. - Offset position within the field. - Length of data to read. - - - - Gets a char value from the datareader. - - - Returns Char.MinValue for null. - - Ordinal column position of the value. - - - - Invokes the GetChars method of the underlying datareader. - - - Returns 0 for null. - - Ordinal column position of the value. - Array containing the data. - Offset position within the buffer. - Offset position within the field. - Length of data to read. - - - - Invokes the GetData method of the underlying datareader. - - Ordinal column position of the value. - - - - Invokes the GetDataTypeName method of the underlying datareader. - - Ordinal column position of the value. - - - - Gets a date value from the datareader. - - - Returns DateTime.MinValue for null. - - Ordinal column position of the value. - - - - Gets a decimal value from the datareader. - - - Returns 0 for null. - - Ordinal column position of the value. - - - - Invokes the GetFieldType method of the underlying datareader. - - Ordinal column position of the value. - - - - Gets a Single value from the datareader. - - - Returns 0 for null. - - Ordinal column position of the value. - - - - Gets a Short value from the datareader. - - - Returns 0 for null. - - Ordinal column position of the value. - - - - Gets a Long value from the datareader. - - - Returns 0 for null. - - Ordinal column position of the value. - - - - Invokes the GetName method of the underlying datareader. - - Ordinal column position of the value. - - - - Gets an ordinal value from the datareader. - - Name of the column containing the value. - - - - Invokes the GetSchemaTable method of the underlying datareader. - - - - - Invokes the GetValues method of the underlying datareader. - - An array of System.Object to - copy the values into. - - - - Invokes the IsDBNull method of the underlying datareader. - - Ordinal column position of the value. - - - - Disposes the object. - - - - - Disposes the object. - - True if called by - the public Dispose method. - - - - Object finalizer. - - - - - Gets a string value from the datareader. - - - Returns empty string for null. - - Name of the column containing the value. - - - - Gets a value of type from the datareader. - - Name of the column containing the value. - - - - Gets an integer from the datareader. - - - Returns 0 for null. - - Name of the column containing the value. - - - - Gets a double from the datareader. - - - Returns 0 for null. - - Name of the column containing the value. - - - - Gets a Guid value from the datareader. - - - Returns Guid.Empty for null. - - Name of the column containing the value. - - - - Gets a boolean value from the datareader. - - - Returns for null. - - Name of the column containing the value. - - - - Gets a byte value from the datareader. - - - Returns 0 for null. - - Name of the column containing the value. - - - - Invokes the GetBytes method of the underlying datareader. - - - Returns 0 for null. - - Name of the column containing the value. - Array containing the data. - Offset position within the buffer. - Offset position within the field. - Length of data to read. - - - - Gets a char value from the datareader. - - - Returns Char.MinValue for null. - - Name of the column containing the value. - - - - Invokes the GetChars method of the underlying datareader. - - - Returns 0 for null. - - Name of the column containing the value. - Array containing the data. - Offset position within the buffer. - Offset position within the field. - Length of data to read. - - - - Invokes the GetData method of the underlying datareader. - - Name of the column containing the value. - - - - Invokes the GetDataTypeName method of the underlying datareader. - - Name of the column containing the value. - - - - Gets a date value from the datareader. - - - Returns DateTime.MinValue for null. - - Name of the column containing the value. - - - - Gets a decimal value from the datareader. - - - Returns 0 for null. - - Name of the column containing the value. - - - - Invokes the GetFieldType method of the underlying datareader. - - Name of the column containing the value. - - - - Gets a Single value from the datareader. - - - Returns 0 for null. - - Name of the column containing the value. - - - - Gets a Short value from the datareader. - - - Returns 0 for null. - - Name of the column containing the value. - - - - Gets a Long value from the datareader. - - - Returns 0 for null. - - Name of the column containing the value. - - - - Invokes the IsDBNull method of the underlying datareader. - - Name of the column containing the value. - - - - Get a reference to the underlying data reader - object that actually contains the data from - the data source. - - - - - Returns the depth property value from the datareader. - - - - - Returns the FieldCount property from the datareader. - - - - - Returns the IsClosed property value from the datareader. - - - - - Returns a value from the datareader. - - Name of the column containing the value. - - - - Returns a value from the datareader. - - Ordinal column position of the value. - - - - Returns the RecordsAffected property value from the underlying datareader. - - - - - Represents a xml serializable collection of keys and values. - - The type of the keys in the dictionary. - The type of the values in the dictionary. - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The dictionary. - - - - Initializes a new instance of the class. - - The comparer. - - - - Initializes a new instance of the class. - - The capacity. - - - - Initializes a new instance of the class. - - The dictionary. - The comparer. - - - - Initializes a new instance of the class. - - The capacity. - The comparer. - - - - Initializes a new instance of the class. - - A object containing the information required to serialize the . - A structure containing the source and destination of the serialized stream associated with the . - - - - This property is reserved, apply the - to the class instead. - - - An - that describes the XML representation of the object that - is produced by the - method and consumed by the - method. - - - - - Generates an object from its XML representation. - - The - stream from which the object is deserialized. - - - - Converts an object into its XML representation. - - The - stream to which the object is serialized. - - - - Determines if a string is in plural form based on some simple rules. - - - - - - - Determines if a string is in singular form based on some simple rules. - - - - - - - Converts a string to plural based on some simple rules. - - The value to convert. - - - - - Converts a string to plural based on some simple rules. - - The value to convert. - The plural overrides map file. - - - - - Converts a string to plural based on some simple rules. - - The value to convert. - The plural overrides map. - - - - - Converts a string to singular based on some simple rules. - - - - - - - Converts a string to singular based on some simple rules. - - The value to convert. - The plural overrides map file. - - - - - Converts a string to singular based on some simple rules. - - The value to convert. - The plural overrides map. - - - - - Converts a string to use camelCase. - - - - - - - Converts a string to use PascalCase. - - Text to convert - - - - - Takes a NameIdentifier and spaces it out into words "Name Identifier". - - - - - - - Determines if a string value contains a space. - - - - - - - Determines if a string value contains any of the strings passed in. - - - - - - - - Determines if a string is a numeric value. - - - - - - - Takes a number and appends the ordinal suffix. ie. 1 = 1st, 13 = 13th. - - - - - - - Takes a number and appends the ordinal suffix. ie. 1 = 1st, 13 = 13th. - - - - - - - Safely returns a value if GetValue succeeded, otherwise it returns null. - - The Source Property - The object whose property value will be returned. - Optional index values for indexed properties. This value should be null for non-indexed properties. - - - - Gets the latest released version number of CodeSmith that has been released. - - - If an internet connection was detected it will return the version information of the latest released version of CodeSmith. - If an internet connection could not be found then it returns null. - - - - - Returns the Assembly file version information for the CodeSmith.Engine assembly. - - Returns the Assembly file version information for the CodeSmith.Engine assembly. - - - - Returns the current version information in the following format: 5.2.0000 (Major.Minor.Revision) - - Returns the current version information in the following format: 5.2.0000 (Major.Minor.Revision) - - - - Returns the current version information in the following format: 5.2.0000 (Major.Minor.Build) - - Returns the current version information in the following format: 5.2.0000 (Major.Minor.Build) - - - - Returns the current version information in the following format: 5.2 (Major.Minor) - - Returns the current version information in the following format: 5.2 (Major.Minor) - - - diff --git a/template/CodeSmith/CodeGenerator/Libraries/EntityFramework.dll b/template/CodeSmith/CodeGenerator/Libraries/EntityFramework.dll deleted file mode 100644 index 8548a4f2e..000000000 Binary files a/template/CodeSmith/CodeGenerator/Libraries/EntityFramework.dll and /dev/null differ diff --git a/template/CodeSmith/CodeGenerator/Libraries/ICSharpCode.NRefactory.CSharp.dll b/template/CodeSmith/CodeGenerator/Libraries/ICSharpCode.NRefactory.CSharp.dll deleted file mode 100644 index 4962b6e12..000000000 Binary files a/template/CodeSmith/CodeGenerator/Libraries/ICSharpCode.NRefactory.CSharp.dll and /dev/null differ diff --git a/template/CodeSmith/CodeGenerator/Libraries/ICSharpCode.NRefactory.dll b/template/CodeSmith/CodeGenerator/Libraries/ICSharpCode.NRefactory.dll deleted file mode 100644 index 17bdf839f..000000000 Binary files a/template/CodeSmith/CodeGenerator/Libraries/ICSharpCode.NRefactory.dll and /dev/null differ diff --git a/template/CodeSmith/CodeGenerator/Libraries/Mono.Cecil.dll b/template/CodeSmith/CodeGenerator/Libraries/Mono.Cecil.dll deleted file mode 100644 index d22da172a..000000000 Binary files a/template/CodeSmith/CodeGenerator/Libraries/Mono.Cecil.dll and /dev/null differ diff --git a/template/CodeSmith/CodeGenerator/Libraries/SchemaExplorer.dll b/template/CodeSmith/CodeGenerator/Libraries/SchemaExplorer.dll deleted file mode 100644 index cd2e3fbd7..000000000 Binary files a/template/CodeSmith/CodeGenerator/Libraries/SchemaExplorer.dll and /dev/null differ diff --git a/template/CodeSmith/CodeGenerator/Libraries/SchemaExplorer.xml b/template/CodeSmith/CodeGenerator/Libraries/SchemaExplorer.xml deleted file mode 100644 index fecf9e7a3..000000000 --- a/template/CodeSmith/CodeGenerator/Libraries/SchemaExplorer.xml +++ /dev/null @@ -1,3748 +0,0 @@ - - - - SchemaExplorer - - - - - Provides a base for objects with names to derive from. - - - - - The name of the object. - - - - - Returns the name of the table. - - - - - - The name of the object. - - - - - Determines whether an element is in the collection with the specified name. - - The name of the item to locate in the collection. - - true if item is found in the collection; otherwise, false. - - - - - Determines the index of a specific item in the list. - - The name of the item to locate in the list. - The index of item if found in the list; otherwise, -1. - - - - Gets the item with the specified name. - - - The item with the specified name. - - - - - Implements a strongly typed collection of elements. - - - SchemaObjectBaseCollection provides an - that is strongly typed for elements. - - - - - Gets the item with the specified index. - - - The item with the specified index. - - - - - How many items are in the collection. - - - - - Determines whether an element is in the collection with the specified name. - - The name of the item to locate in the collection. - - true if item is found in the collection; otherwise, false. - - - - - Determines the index of a specific item in the list. - - The name of the item to locate in the list. - The index of item if found in the list; otherwise, -1. - - - - Gets the item with the specified name. - - - The item with the specified name. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - Determines whether an element is in the collection with the specified name. - - The name of the item to locate in the collection. - - true if item is found in the collection; otherwise, false. - - - - - Determines the index of a specific item in the list. - - The name of the item to locate in the list. - The index of item if found in the list; otherwise, -1. - - - - Gets the item with the specified name. - - - The item with the specified name. - - - - - Initializes a new instance of the class. - - The items from which the elements are copied. - - - - Initializes a new instance of the class. - - The items from which the elements are copied. - - - - Determines whether an element is in the collection with the specified name. - - The name of the item to locate in the collection. - - true if item is found in the collection; otherwise, false. - - - - - Determines the index of a specific item in the list. - - The name of the item to locate in the list. - The index of item if found in the list; otherwise, -1. - - - - Gets the item with the specified name. - - - The item with the specified name. - - - - - Determines whether an element is in the collection with the specified owner and name. - - The name of the item to locate in the collection. - - true if item is found in the collection; otherwise, false. - - - - - Determines the index of an item with the specified owner and name. - - The name of the item to locate in the collection. - The index of item if found in the list; otherwise, -1. - - - - Gets the with the specified owner and name. - - - The with the specified owner and name. - - - - - Refreshes the schema object. - - - - - Determines whether an element is in the collection with the specified name. - - The name of the item to locate in the collection. - - true if item is found in the collection; otherwise, false. - - - - - Determines the index of a specific item in the list. - - The name of the item to locate in the list. - The index of item if found in the list; otherwise, -1. - - - - Gets the item with the specified name. - - - The item with the specified name. - - - - - Determines whether an element is in the collection with the specified owner and name. - - The owner of the item to locate in the collection. - The name of the item to locate in the collection. - - true if item is found in the collection; otherwise, false. - - - - - Determines the index of an item with the specified owner and name. - - The owner of the item to locate in the collection. - The name of the item to locate in the collection. - The index of item if found in the list; otherwise, -1. - - - - Gets the with the specified owner and name. - - - The with the specified owner and name. - - - - - Determines whether an element is in the collection with the specified owner and name. - - The owner of the item to locate in the collection. - The name of the item to locate in the collection. - - true if item is found in the collection; otherwise, false. - - - - - Determines the index of an item with the specified owner and name. - - The owner of the item to locate in the collection. - The name of the item to locate in the collection. - The index of item if found in the list; otherwise, -1. - - - - Gets the with the specified owner and name. - - - The with the specified owner and name. - - - - - Implements a strongly typed collection of elements. - - - SchemaObjectBaseCollection provides an - that is strongly typed for elements. - - - - - Refreshes the schema object. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - Provides a base for all schema objects to derive from. - - - - - Provides a base for all schema objects to derive from. - - - - - Allows the object to be refreshed. - - - - - Returns the date this table was created. - - - - - The description of the object. - - - - - Used to store any additional information about the schema object. - - - - - Allows access to the DatabaseSchema object that contains this schema object. - - - - - Gets or sets a value indicating this schema object and all its children will be loaded. - - true if deep load; otherwise, false. - - - - Gets or sets a value indicating this schema object and all its children will be loaded. - - true if functions should be included; otherwise, false. - - - - Returns the owner and name of the table. - - - - - - Gets the name used to sort in 'Table (Owner)' format. - - The name used to sort. - - - - Allows the object to be refreshed. - - - - - Gets the existing ExtendedProperties without causing a lazy load. - - - - - Formats the full name. - - The owner. - The name. - - - - - Returns a HashCode based on the database and name of the table. - - - - - - Determines if another table is the same as this table. - - The table to check against. - true/false - - - - Returns the date this table was created. - - - - - The description of the object. - - - - - Used to store any additional information about the schema object. - - - - - Allows access to the DatabaseSchema object that contains this schema object. - - - - - Gets or sets a value indicating this schema object and all its children will be loaded. - - true if deep load; otherwise, false. - - - - Gets or sets a value indicating this schema object and all its children will be loaded. - - true if functions should be included; otherwise, false. - - - - Returns the owner and name of the table. - - - - - - Gets the name used to sort in 'Table (Owner)' format. - - The name used to sort. - - - - Returns the owner of the table. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Returns the name of the table. - - - - - - Returns a HashCode based on the database and name of the table. - - - - - - Determines if another table is the same as this table. - - The table to check against. - true/false - - - - Returns the owner of the table. - - - - - - Returns the owner and name of the table. - - - - - - Gets the name used to sort in 'Table (Owner)' format. - - The name used to sort. - - - - Clean up any resources being used. - - true if managed resources should be disposed; otherwise, false. - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Clean up any resources being used. - - true if managed resources should be disposed; otherwise, false. - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Gets or sets a value indicating this schema object and all its children will be loaded. - - true if functions should be included; otherwise, false. - - - - Clean up any resources being used. - - true if managed resources should be disposed; otherwise, false. - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Clean up any resources being used. - - true if managed resources should be disposed; otherwise, false. - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Clean up any resources being used. - - true if managed resources should be disposed; otherwise, false. - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Required designer variable. - - - - - Clean up any resources being used. - - true if managed resources should be disposed; otherwise, false. - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Clean up any resources being used. - - true if managed resources should be disposed; otherwise, false. - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Clean up any resources being used. - - true if managed resources should be disposed; otherwise, false. - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Clean up any resources being used. - - true if managed resources should be disposed; otherwise, false. - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Clean up any resources being used. - - true if managed resources should be disposed; otherwise, false. - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Determines whether an element is in the collection with the specified owner and name. - - The owner of the item to locate in the collection. - The name of the item to locate in the collection. - - true if item is found in the collection; otherwise, false. - - - - - Determines the index of an item with the specified owner and name. - - The owner of the item to locate in the collection. - The name of the item to locate in the collection. - The index of item if found in the list; otherwise, -1. - - - - Gets the with the specified owner and name. - - - The with the specified owner and name. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - Determines whether an element is in the collection with the specified owner and name. - - The owner of the item to locate in the collection. - The name of the item to locate in the collection. - - true if item is found in the collection; otherwise, false. - - - - - Determines the index of an item with the specified owner and name. - - The owner of the item to locate in the collection. - The name of the item to locate in the collection. - The index of item if found in the list; otherwise, -1. - - - - Gets the with the specified owner and name. - - - The with the specified owner and name. - - - - - Initializes a new instance of the class. - - The assembly-qualified name of the Type, which includes the name of the assembly from which the Type was loaded. - - - - Initializes a new instance of the class. - - The assembly-qualified name of the Type, which includes the name of the assembly from which the Type was loaded. - The name used in displaying this DataSourceProvider. - - - - Gets the for the provider specified in . - - - The for the provider specified in . - If the Type can not be resolved, null is returned. - - - - - Creates an instance of for the type specified in . - - An instance of , null if the type can not be resolved. - - - - Creates an instance of for the type specified in . - - if set to true, the an instance will be cached and return on future calls. - - An instance of , null if the type can not be resolved. - - - - - Creates an instance of for the type specified in . - - An instance of , null if the type can not be resolved. - - - - Creates an instance of for the type specified in . - - if set to true, the an instance will be cached and return on future calls. - An instance of , null if the type can not be resolved. - - - - Performs an implicit conversion from to . - - Name of the assembly qualified. - - The result of the conversion. - - - - - Performs an implicit conversion from to . - - The data source provider. - - The result of the conversion. - - - - - Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object. - - - A 32-bit signed integer that indicates the relative order of the objects being compared. - - An object to compare with this instance. - - - is not the same type as this instance. - - - - - Compares the current object with another object of the same type. - - - A 32-bit signed integer that indicates the relative order of the objects being compared. - - An object to compare with this object. - - - - Compares the current object with another object of the same type. - - - A 32-bit signed integer that indicates the relative order of the objects being compared. - - An object to compare with this object. - - - - Indicates whether the current object is equal to another object of the same type. - - - true if the current object is equal to the parameter; otherwise, false. - - An object to compare with this object. - - - - Indicates whether the current object is equal to another object of the same type. - - - true if the current object is equal to the parameter; otherwise, false. - - An object to compare with this object. - - - - Determines whether the specified is equal to this instance. - - The to compare with this instance. - - true if the specified is equal to this instance; otherwise, false. - - - - - Returns a hash code for this instance. - - - A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - - - - - Returns a that represents this instance. - - - A that represents this instance. - - - - - Gets the display name. - - - - - Gets the assembly-qualified name of the Type, which includes the name of the assembly from which the Type was loaded. - - - - - This interface is used to implement a custom schema provider for - SchemaExplorer. To create your own schema provider simply create a - new assembly which includes a public class that implements this interface. - Also, the assembly name must end with SchemaProvider.dll - (ie. MySampleSchemaProvider.dll). Once you have done this, just - put the assembly in the same directory as CodeSmith and that's it. - - - - - Gets the name of the database. - - The connection string used to connect to the target database. - The name of the database - - - - Gets the extended property collection for a given schema object. - - The connection string used to connect to the target database. - Any type that derives from SchemaObjectBase. (E.G. DatabaseSchema, TableSchema, ColumnSchema, ViewSchema, ViewColumnSchema, IndexSchema, CommandSchema, ParameterSchema, PrimaryKeySchema, TableKeySchema) - An array of ExtendedProperties for a specific SchemaObjectBase. - - - - Sets the extended properties. - - The connection string used to connect to the target database. - Any type that derives from SchemaObjectBase. (E.G. DatabaseSchema, TableSchema, ColumnSchema, ViewSchema, ViewColumnSchema, IndexSchema, CommandSchema, ParameterSchema, PrimaryKeySchema, TableKeySchema) - - - - Gets all of the tables available in the database. - - The connection string used to connect to the target database. - The database schema. - An array of tables for a specific database. - - - - Gets all columns for a given table. - - The connection string used to connect to the target database. - The table schema. - An array of view columns for a specific table. - - - - Gets all the views available for a given database. - - The connection string used to connect to the target database. - The database schema. - An array of views for a specific database. - - - - Gets the columns for a given view. - - The connection string used to connect to the target database. - The view schema. - An array of view columns for a specific view. - - - - Gets the definition for a given view. - - The connection string used to connect to the target database. - The view schema. - The definition of a view. - - - - Gets the primary key for a given table. - - The connection string used to connect to the target database. - The table schema. - An the primary key for a specific table. - - - - Gets all of the table keys for a given table. - - The connection string used to connect to the target database. - The table schema. - An array of keys for a specific table. - - - - Gats all of the indexes for a given table. - - The connection string used to connect to the target database. - The table schema. - An array of indexes for a specific table. - - - - Gets the data from the given table. - - The connection string used to connect to the target database. - The table schema. - A DataTable containing the data of the specific table. - - - - Gets the data from a given view. - - The connection string used to connect to the target database. - The view schema. - A DataTable containing the data of the specific view. - - - - Gets all commands for the given database. - - The connection string used to connect to the target database. - The database schema. - An array of commands. - - - - Gets the parameters for a given command. - - The connection string used to connect to the target database. - The command schema. - An array of parameters. - - - - Gets the definition for a given command. - - The connection string used to connect to the target database. - The command schema. - The definition of a command. - - - - Gets schema information about the results of a given command. - - The connection string used to connect to the target database. - The command schema. - An array of command results. - - - - Gets the description for the schema provider (E.G. SQL Server Schema Provider).. - - - - - The data type of a (column) parameter is specific to the .NET Framework data provider. - Specifying the type converts the value of the Parameter to the data provider - Type before passing the value to the data source. - - seealso: http://msdn.microsoft.com/en-us/library/system.data.dbtype.aspx - example: DbType.AnsiString - - - - - The System Type that is resolved from . - - example: typeof(System.String) - - - - - This is the native data type from the database. - - example: nvarchar - - - - - The size of the data object. - - - - - The precision of the data object. - - - - - The scale of the data object. - - - - - Whether or not the data object allows null values. - - - - - The Parent Schema Object. - - - - - The table that contains this column. - - - - - Whether or not the column is a primary key member. - - - - - Whether or not the column is a foreign key member. - - - - - Whether or not this column is unique. - - - - - Provides information about a table column. - - - - - Provides a common base for all schema objects that describe a data field. - - - - - Creates a new instance of the ViewColumnSchema object. - - The object that the column belongs to. - The name of the column. - The columns data type. - The columns native type. - The size of the column. - The precision of the column. - The scale of the column. - - - - Creates a new instance of the ViewColumnSchema object. - - The object that the column belongs to. - The name of the column. - The columns data type. - The columns native type. - The size of the column. - The precision of the column. - The scale of the column. - Whether the column allows null values. - - - - Creates a new instance of the ViewColumnSchema object. - - The object that the column belongs to. - The name of the column. - The columns data type. - The columns native type. - The size of the column. - The precision of the column. - The scale of the column. - The array of extended properties for this column. - - - - Creates a new instance of the ViewColumnSchema object. - - The object that the column belongs to. - The name of the column. - The columns data type. - The columns native type. - The size of the column. - The precision of the column. - The scale of the column. - Whether the column allows null values. - The array of extended properties for this column. - - - - Returns the name of the column. - - - - - - Used to refresh this instance. - - - - - Determines if another view column is the same as this view column. - - The view column to check against. - true/false - - - - Returns a hashcode based on the database, view and name of the view column. - - - - - - The data type of a (column) parameter is specific to the .NET Framework data provider. - Specifying the type converts the value of the Parameter to the data provider - Type before passing the value to the data source. - - see also: http://msdn.microsoft.com/en-us/library/system.data.dbtype.aspx - example: DbType.AnsiString - - - - - The System Type that is resolved from . - - example: typeof(System.String) - - - - - The Parent Schema Object. - - - - - This is the native data type from the database. - - example: nvarchar - - - - - The size of the data object. - - - - - The precision of the data object. - - - - - The scale of the data object. - - - - - Whether or not the data object allows null values. - - - - - Creates a new instance of the ColumnSchema object. - - The table that the column belongs to. - The name of the column. - The columns data type. - The columns native type. - The size of the column. - The precision of the column. - The scale of the column. - Whether the column allows null values. - - - - Creates a new instance of the ColumnSchema object. - - The table that the column belongs to. - The name of the column. - The columns data type. - The columns native type. - The size of the column. - The precision of the column. - The scale of the column. - Whether the column allows null values. - The array of extended properties for the column. - - - - The table that contains this column. - - - - - Whether or not the column is a primary key member. - - - - - Whether or not the column is a foreign key member. - - - - - Whether or not this column is unique. - - - - - Implements a strongly typed collection of elements. - - - ColumnSchemaCollection provides an - that is strongly typed for elements. - - - - - Implements a strongly typed collection of elements. - - - DataObjectBaseCollection provides an - that is strongly typed for elements. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - Provides a common base for all tabular schema objects. - - - - - Gets the data contained within the schema object. - - - - - - Index to allow easy access to the columns in this schema object. - - - - - Provides information about the schema objects columns. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Gets the data contained within the table. - - - - - - Index to allow easy access to the columns in this schema object. - - - - - Provides information about the schema objects columns. - - - - - Implements a strongly typed collection of elements. - - - TabularObjectBaseCollection provides an - that is strongly typed for elements. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - The command that contains this column. - - - - - Provides information about a command result column. - - - - - Creates a new instance of the CommandResultColumnSchema object. - - The command that the column belongs to. - The name of the column. - The columns data type. - The columns native type. - The size of the column. - The precision of the column. - The scale of the column. - Whether the column allows null values. - - - - Creates a new instance of the CommandResultColumnSchema object. - - The command that the column belongs to. - The name of the column. - The columns data type. - The columns native type. - The size of the column. - The precision of the column. - The scale of the column. - Whether the column allows null values. - The array of extended properties for the column. - - - - The command that contains this column. - - - - - Implements a strongly typed collection of elements. - - - CommandResultColumnSchemaCollection provides an - that is strongly typed for elements. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - Provides access to the command that this command result schema belongs to. - - - - - Provides information about the command result columns. - - - - - Summary description for CommandResultSchema. - - - - - - - The command that the command result schema belongs to. - The name of this command result schema. - The columns that make up this command result schema. - - - - - - The command that the command result schema belongs to. - The name of this command result schema. - The columns that make up this command result schema. - The array of extended properties for the column. - - - - Used to refresh this instance. - - - - - Provides access to the command that this command result schema belongs to. - - - - - Provides information about the command result columns. - - - - - Implements a strongly typed collection of elements. - - - CommandResultSchemaCollection provides an - that is strongly typed for elements. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - Parameters for this command. - - - - - Provides access to information about the results and column schema information returned when executing this command. - - - - - Input parameters for this command. - - - - - Output parameters for this command. - - - - - InputOutput parameters for this command. - - - - - Input and InputOutput parameters for this command. - - - - - Output and InputOutput parameters for this command. - - - - - All parameters except the return value parameter. - - - - - The return value parameter for this command. - - - - - Gets the command text for this command. - - - - - Provides information about a command. - - - - - Creates a new instance of the CommandSchema object. - - The database that the command belongs to. - The name of the command. - The owner of the command. - The date this command was created. - - - - Creates a new instance of the CommandSchema object. - - The database that the command belongs to. - The name of the command. - The owner of the command. - The date this command was created. - The array of extended properties for the command. - - - - Refresh this instance. - - - - - Parameters for this command. - - - - - Provides access to information about the results and column schema information returned when executing this command. - - - - - Input parameters for this command. - - - - - Output parameters for this command. - - - - - InputOutput parameters for this command. - - - - - Input and InputOutput parameters for this command. - - - - - Output and InputOutput parameters for this command. - - - - - All parameters except the return value parameter. - - - - - The return value parameter for this command. - - - - - Gets the command text for this command. - - - - - Implements a strongly typed collection of elements. - - - CommandSchemaCollection provides an - that is strongly typed for elements. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - Initializes a new instance of the class. - - - - - Gets the current singleton instance of Configuration. - - The current singleton instance. - - An instance of Configuration wont be created until the very first - call to the sealed class. This is a CLR optimization that - provides a properly lazy-loading singleton. - - - - - Nested class to lazy-load singleton. - - - - - Initializes the Nested class. - - - Explicit static constructor to tell C# compiler not to mark type as beforefieldinit. - - - - - Current singleton instance. - - - - - A class used to import old schema explorer configuration files. - - - - - Required designer variable. - - - - - Clean up any resources being used. - - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Required designer variable. - - - - - Clean up any resources being used. - - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Required designer variable. - - - - - Clean up any resources being used. - - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Required designer variable. - - - - - Clean up any resources being used. - - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Required designer variable. - - - - - Clean up any resources being used. - - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - This function checks if the OS is 64 bits. - - - - - Get ValueNames from registry for WoW64 machine. Corresponding to Microsoft.Win32.RegistryKey.GetValueNames(). - - Registry key string value - Access key value options - - - - - Required designer variable. - - - - - Clean up any resources being used. - - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Gets the registered OLE DB providers as an array of ProgIDs. - - - - - Required designer variable. - - - - - Clean up any resources being used. - - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Required designer variable. - - - - - Clean up any resources being used. - - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Required designer variable. - - - - - Clean up any resources being used. - - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - Required designer variable. - - - - - Clean up any resources being used. - - - - - Required method for Designer support - do not modify - the contents of this method with the code editor. - - - - - A strongly-typed resource class, for looking up localized strings, etc. - - - - - Returns the cached ResourceManager instance used by this class. - - - - - Overrides the current thread's CurrentUICulture property for all - resource lookups using this strongly typed resource class. - - - - - Looks up a localized string similar to mdb. - - - - - Looks up a localized string similar to Microsoft Access Databases (*.mdb;*.accdb)|*.mdb;*.accdb|All Files (*.*)|*.*. - - - - - Looks up a localized string similar to Select Microsoft Access Database File. - - - - - Looks up a localized string similar to The connection properties object must be of type OleDBAccessConnectionProperties.. - - - - - Looks up a localized string similar to No connection properties have been set.. - - - - - Looks up a localized string similar to &Add.... - - - - - Looks up a localized string similar to &Description. - - - - - Looks up a localized string similar to Remo&ve. - - - - - Looks up a localized string similar to &Reset. - - - - - Looks up a localized string similar to The selected data provider cannot be changed when there is only one data provider available.. - - - - - Looks up a localized string similar to The selected data source cannot be changed when there is only one data source available.. - - - - - Looks up a localized string similar to The data connection dialog state cannot be programmatically modified when the dialog is visible.. - - - - - Looks up a localized string similar to Change Data Source. - - - - - Looks up a localized string similar to The selected data source has no association with this data provider.. - - - - - Looks up a localized string similar to The data source was not found.. - - - - - Looks up a localized string similar to {0} ({1}). - - - - - Looks up a localized string similar to No data provider is currently selected.. - - - - - Looks up a localized string similar to The '{0}' data source has no association with any data providers.. - - - - - Looks up a localized string similar to No data sources are available.. - - - - - Looks up a localized string similar to No data source is currently selected.. - - - - - Looks up a localized string similar to No help is available in this context.. - - - - - Looks up a localized string similar to You cannot use the Form.ShowDialog() method to show the data connection dialog. Use DataConnectionDialog.Show() instead.. - - - - - Looks up a localized string similar to Test connection succeeded.. - - - - - Looks up a localized string similar to Test results. - - - - - Looks up a localized string similar to .NET Framework Data Provider for ODBC. - - - - - Looks up a localized string similar to Use this selection to specify an ODBC user or system data source name to connect to an ODBC driver through the .NET Framework Data Provider for ODBC.. - - - - - Looks up a localized string similar to Use this data provider to connect to ODBC data sources.. - - - - - Looks up a localized string similar to ODBC. - - - - - Looks up a localized string similar to .NET Framework Data Provider for OLE DB. - - - - - Looks up a localized string similar to Use this selection to connect to a Microsoft Access database file through the .NET Framework Data Provider for OLE DB.. - - - - - Looks up a localized string similar to Use this data provider to connect to OLE DB data sources.. - - - - - Looks up a localized string similar to Use this selection to connect to Oracle 7.3, 8i, 9i or 10g using the native MSDAORA provider through the .NET Framework Data Provider for OLE DB.. - - - - - Looks up a localized string similar to OLE DB. - - - - - Looks up a localized string similar to Use this selection to connect to Microsoft SQL Server 2000 or above using the .NET framework data provider for OLEDB.. - - - - - Looks up a localized string similar to .NET Framework Data Provider for Oracle. - - - - - Looks up a localized string similar to Use this selection to connect to Oracle 7.3, 8i, 9i or 10g using the .NET Framework Data Provider for Oracle. - - Using the .NET Framework Data Provider for Oracle is not recommended because it is being deprecated.. - - - - - Looks up a localized string similar to Use this data provider to connect to Oracle 7.3, 8i, 9i or 10g. - - Using the .NET Framework Data Provider for Oracle is not recommended because it is being deprecated.. - - - - - Looks up a localized string similar to OracleClient. - - - - - Looks up a localized string similar to .NET Framework Data Provider for SQL Server. - - - - - Looks up a localized string similar to Use this selection to connect to Microsoft SQL Server 2005 or above using the .NET Framework Data Provider for SQL Server.. - - - - - Looks up a localized string similar to Use this data provider to connect to Microsoft SQL Server 2005 or above.. - - - - - Looks up a localized string similar to Use this selection to attach a database file to a local Microsoft SQL Server instance (including Microsoft SQL Express) using the .NET Framework Data Provider for SQL Server.. - - - - - Looks up a localized string similar to SqlClient. - - - - - Looks up a localized string similar to The default data provider cannot be changed when there is only one data provider available.. - - - - - Looks up a localized string similar to The data provider was not found.. - - - - - Looks up a localized string similar to Microsoft Access Database File. - - - - - Looks up a localized string similar to Microsoft ODBC Data Source. - - - - - Looks up a localized string similar to Microsoft SQL Server. - - - - - Looks up a localized string similar to Microsoft SQL Server Database File. - - - - - Looks up a localized string similar to Oracle Database. - - - - - Looks up a localized string similar to <other>. - - - - - Looks up a localized string similar to The connection properties object must be of type OdbcConnectionProperties.. - - - - - Looks up a localized string similar to Failed to allocate ODBC connection handle.. - - - - - Looks up a localized string similar to Failed to allocate ODBC environment handle.. - - - - - Looks up a localized string similar to Failed to call the ODBC driver connection utility.. - - - - - Looks up a localized string similar to This connection cannot be tested because no database file has been specified.. - - - - - Looks up a localized string similar to The connection properties object must be of type OleDBConnectionProperties.. - - - - - Looks up a localized string similar to The connection properties object must be of type OracleConnectionProperties or OleDBOracleConnectionProperties.. - - - - - Looks up a localized string similar to This connection cannot be tested because the specified database does not exist or is not visible to the specified user.. - - - - - Looks up a localized string similar to This connection cannot be tested because no server name has been specified.. - - - - - Looks up a localized string similar to This server version is not supported. You must have Microsoft SQL Server 2005 or later.. - - - - - Looks up a localized string similar to mdf. - - - - - Looks up a localized string similar to Microsoft SQL Server Databases (*.mdf)|*.mdf|All Files (*.*)|*.*. - - - - - Looks up a localized string similar to Select SQL Server Database File. - - - - - Looks up a localized string similar to The connection properties object must be of type SqlConnectionProperties or OleDBSqlConnectionProperties.. - - - - - Looks up a localized string similar to This connection cannot be tested because the specified database file does not exist.. - - - - - Looks up a localized string similar to No database file was specified.. - - - - - Looks up a localized string similar to Common reasons for this problem include that the server is not installed, the service is not started or the server is upgrading your database. If an upgrade is in process, you will be able to connect to the database when the upgrade is complete.. - - - - - Looks up a localized string similar to The connection properties object must be of type SqlFileConnectionProperties.. - - - - - Gets or sets the current provider for this instance. - - - - - Gets or sets the current connection string for this instance. - - - - - Provides access to the tables contained in the database. - - - - - Provides access to the views contained in the database. - - - - - Provides access to the commands contained in the database. - - - - - This class allows programmatic discovery of a database schema. - - - - - This is the default constructor. - - - - - This constructor allows you to specify your own schema provider and set the connection string. - - The provider to use for this instance. - The connection string to use for this instance. - - - - Returns a collection of available providers. - - IDbSchemaProviderCollection - - - - Returns the name of the database. - - - - - - Gets a provider by name. - - The name of the provider. - IDbSchemaProvider - - - - Allows for programmatic refreshing of the database schema. - - - - - Determines if another database is the same as this database. - - The database to check against. - true/false - - - - Returns a hashcode based on the database. - - - - - - Gets or sets the current provider for this instance. - - - - - Gets or sets the current cached provider for this instance. - - - - - Gets or sets the current connection string for this instance. - - - - - The name of the database. - - - - - Provides access to the tables contained in the database. - - - - - Provides access to the views contained in the database. - - - - - Provides access to the commands contained in the database. - - - - - Implements a strongly typed collection of elements. - - - DataObjectBaseCollection provides an - that is strongly typed for elements. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - Used to store information about a data source. - - - - - Implements a strongly typed collection of elements. - - - DataSourceCollection provides an - that is strongly typed for elements. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - Determines whether an element is in the collection with the specified name. - - The name of the item to locate in the collection. - - true if item is found in the collection; otherwise, false. - - - - - Determines the index of a specific item in the list. - - The name of the item to locate in the list. - The index of item if found in the list; otherwise, -1. - - - - Gets the with the specified name. - - - The with the specified name. - - - - - The value of this extended property. - - - - - The data type of this extended property. - - - - - The name of this extended property. - - - - - The value of this extended property. - - - - - The data type of this extended property. - - - - - Implements a strongly typed collection of elements. - - - ExtendedPropertyCollection provides an - that is strongly typed for elements. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - Implements a strongly typed collection of elements. - - - IDbSchemaProviderCollection provides an - that is strongly typed for elements. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - Determines whether an element is in the collection with the specified name. - - The name of the item to locate in the collection. - - true if item is found in the collection; otherwise, false. - - - - - Determines the index of a specific item in the list. - - The name of the item to locate in the list. - The index of item if found in the list; otherwise, -1. - - - - Gets the with the specified name. - - - The with the specified name. - - - - - The table that contains this index. - - - - - Whether this is the index for the primary key or not. - - - - - Whether this is a unique index. - - - - - Whether this is a clustered index. - - - - - The columns that make up this index. - - - - - Provides information about an index. - - - - - Formats the full name. - - The owner. - Name of the table. - Name of the index. - - - - - Determines if another index is the same as this index. - - The index to check against. - true/false - - - - Returns a hashcode based on the database, table and name of the index. - - - - - - The table that contains this index. - - - - - Whether this is the index for the primary key or not. - - - - - Whether this is a unique index. - - - - - Whether this is a clustered index. - - - - - The columns that make up this index. - - - - - Implements a strongly typed collection of elements. - - - IndexSchemaCollection provides an - that is strongly typed for elements. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - The column that this member column object wraps. - - - - - The underlying column's extended properties. - - - - - Wraps a ColumnSchema object and allows for storing extended properties specific to this instance of the member column. - - - - - Creates a new instance of the MemberColumnSchema object. - - The column that is member column refers to. - - - - Creates a new instance of the MemberColumnSchema object. - - The column that is member column refers to. - The array of extended properties for the member column. - - - - Used to refresh this instance. - - - - - Determines if another column is the same as this column. - - The column to check against. - true/false - - - - Returns a HashCode based on the database, table and name of the column. - - - - - - The column that this member column object wraps. - - - - - The underlying column's extended properties. - - - - - Used to store any additional information about the schema object. - - - - - The table that contains this column. - - - - - The Parent Schema Object. - - - - - Whether or not the column is a primary key member. - - - - - Whether or not the column is a foreign key member. - - - - - Whether or not this column is unique. - - - - - The data type for this data object. - - - - - The system type that for this data object. - - - - - The native data type for this data object. - - - - - The size of the data object. - - - - - The precision of the data object. - - - - - The scale of the data object. - - - - - Whether or not the data object allows null values. - - - - - The name of the object. - - - - - The description of the object. - - - - - Allows access to the DatabaseSchema object that contains this schema object. - - - - - Implements a strongly typed collection of elements. - - - MemberColumnSchemaCollection provides an - that is strongly typed for elements. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - Determines whether the - contains a element that points to the specified . - - The object - to locate in the . - This argument can be a null reference. - - true if is found in the - ; otherwise, false. - Please refer to for details. - - - - Returns the zero-based index of the first occurrence of the specified - in the that points to the specified .. - - The object - to locate in the . - This argument can be a null reference. - - - The zero-based index of the first occurrence of - in the , if found; otherwise, -1. - - Please refer to for details. - - - - The command that contains this parameter. - - - - - The parameter direction. - - - - - Provides information about a parameter. - - - - - The command that contains this parameter. - - - - - The parameter direction. - - - - - Implements a strongly typed collection of elements. - - - ParameterSchemaCollection provides an - that is strongly typed for elements. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - The table that contains this primary key. - - - - - The primary key's member columns. - - - - - Provides information about a table primary key. - - - - - Determines if another primary key is the same as this primary key. - - The primary key to check against. - true/false - - - - Returns a hashcode based on the database, table and name of the primary key. - - - - - - The table that contains this primary key. - - - - - The primary key's member columns. - - - - - CLR Procedure or Function. - - - - - Inline-Table-Valued Function. - - - - - Multi-Statement-Table-Valued Function - - - - - Function. - - - - - Table-Valued Function - - - - - User Defined Table Type. - - - - - Major Version. - - - - - Version. - - - - - The collation setting - - - - - A comment. - - - - - The statement that defines the computed field - - - - - The default value - - - - - Identity Column - - - - - Identity Increment - - - - - Identity Seed - - - - - Computed Column or Index - - - - - Column is Deterministic - - - - - The Column is a Row Guid - - - - - - - - - - - - - - - - - - - - - - - - - The index is used to maintain a declarative referential constraint. - - - - - - The index implements a SQL Server PRIMARY KEY constraint. A value is returned only. - - - - - - The index implements a UNIQUE constraint on a table not constrained by primary key. The index is a candidate key. - - - - - - Optimizes index creation when an existing index is rebuilt. - - - - - - The StatisticsIndex property directs Index object property evaluation when the object is used to create a Microsoft SQL Server index. - - - - - - - - - - - The index is clustered. SQL Server supports a single clustered index on any table. - - - - - - - - - - - The IsFullTextKey property identifies the index used by Microsoft Search to support row identification. - - - - - - Controls error generation when an INSERT or UPDATE operation could cause a constraint violation and the index implements a PRIMARY KEY or UNIQUE constraint. - - - - - - Redirects index creation, mapping Index object manipulation to CREATE STATISTICS and DROP STATISTICS statements. - - - - - - The StatisticsIndex property directs Index object property evaluation when the object is used to create a Microsoft SQL Server index. - - - - - - - - - - - Creates index with statistics computation off. - - - - - - The FillFactor property exposes the percent of each page used to store index data when the index is created. - - - - - - Index nodes are padded using a fill factor. - - - - - - - - - - - - - - - - - - - - - - - - - - The Description - - - - - The FileGroup property identifies the file group used to store Microsoft SQL Server table or index data. - - - - - - - - - - - - - - - - - - - - - The System Type (E.G., System.String) - - - - - - - - - - Summary description for SchemaUtility. - - - - - Sorts TableSchema objects based on their dependencies. - - - - - The foreign key table. - - - - - The foreign key table columns that make up this key. - - - - - The primary key table. - - - - - The primary key table columns that make up this key. - - - - - The primary key for the table that this key refers to. - - - - - Provides information about a table key. - - - - - Refresh this object. - - - - - Formats the full name. - - The owner. - Name of the table. - Name of the key. - - - - - Determines if another table key is the same as this table key. - - The table key to check against. - true/false - - - - Returns a hashcode based on the database, table and name of the table key. - - - - - - The foreign key table. - - - - - The foreign key table columns that make up this key. - - - - - The primary key table. - - - - - The primary key table columns that make up this key. - - - - - The primary key for the table that this key refers to. - - - - - Implements a strongly typed collection of elements. - - - TableKeySchemaCollection provides an - that is strongly typed for elements. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - Gets the data contained within the table. - - - - - - Provides information about the primary key of the table. - - - - - Provides information about the primary key of the table. - - - - - Provides information about the keys contained in the table. - - - - - Provides information about the foreign keys (keys where the current table is the foreign table) contained in the table. - - - - - Provides information about the primary keys (keys where the current table is the primary table) contained in the table. - - - - - Provides information about the indexes contained in the table. - - - - - Provides information about the table columns. - - - - - Provides information about all non-primary key table columns. - - - - - Provides information about all non-key columns. - - - - - Provides information about all columns who are not members of a foreign key. - - - - - Provides information about all columns who are members of a foreign key. - - - - - Provides information about a database table. - - - - - - - - - - - - - - - - - - - - - - - - Allows you to refresh the information for this table. - - - - - Determines whether the current table is a dependant of the supplied table. - - The table to check against. - true/false - - - - Gets the data contained within the table. - - - - - - Provides information about the primary key of the table. - - - - - Provides information about the primary key of the table. - - - - - Provides information about the table columns. - - - - - Provides information about all non-primary key table columns. - - - - - Provides information about all non-key columns. - - - - - Provides information about all columns who are not members of a foreign key. - - - - - Provides information about all columns who are members of a foreign key. - - - - - Provides information about the keys contained in the table. - - - - - Provides information about the foreign keys (keys where the current table is the foreign table) contained in the table. - - - - - Provides information about the primary keys (keys where the current table is the primary table) contained in the table. - - - - - Provides information about the indexes contained in the table. - - - - - Implements a strongly typed collection of elements. - - - TableSchemaCollection provides an - that is strongly typed for elements. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - The view that contains this column. - - - - - Provides information about a view column. - - - - - Creates a new instance of the ViewColumnSchema object. - - The view that the column belongs to. - The name of the column. - The columns data type. - The columns native type. - The size of the column. - The precision of the column. - The scale of the column. - - - - Creates a new instance of the ViewColumnSchema object. - - The view that the column belongs to. - The name of the column. - The columns data type. - The columns native type. - The size of the column. - The precision of the column. - The scale of the column. - Whether the column allows null values. - - - - Creates a new instance of the ViewColumnSchema object. - - The view that the column belongs to. - The name of the column. - The columns data type. - The columns native type. - The size of the column. - The precision of the column. - The scale of the column. - The array of extended properties for this column. - - - - Creates a new instance of the ViewColumnSchema object. - - The view that the column belongs to. - The name of the column. - The columns data type. - The columns native type. - The size of the column. - The precision of the column. - The scale of the column. - Whether the column allows null values. - The array of extended properties for this column. - - - - The view that contains this column. - - - - - Implements a strongly typed collection of elements. - - - ViewColumnSchemaCollection provides an - that is strongly typed for elements. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - - Gets the data contained within the table. - - - - - - Provides information about the view columns. - - - - - Gets the view text for this view. - - - - - Provides information about a database table. - - - - - - - - - - - - - - - - - - - - - - - - Allows you to refresh the information for this table. - - - - - Gets the data contained within the table. - - - - - - Provides information about the view columns. - - - - - Gets the view text for this view. - - - - - Implements a strongly typed collection of elements. - - - ViewSchemaCollection provides an - that is strongly typed for elements. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - The number of elements that the new list can initially store. - - - - Initializes a new instance of the class. - - The collection from which the elements are copied. - - - diff --git a/template/CodeSmith/CodeGenerator/Project.csp b/template/CodeSmith/CodeGenerator/Project.csp deleted file mode 100644 index 6320fa990..000000000 --- a/template/CodeSmith/CodeGenerator/Project.csp +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - Singular - Singular - Plural - Plural - - - sysdiagrams$ - - - False - - - ^(sp|tbl|udf|vw)_ - - - - $(ConnectionString2) - SchemaExplorer.SqlSchemaProvider,SchemaExplorer.SqlSchemaProvider - - GreatWall - . - - - \ No newline at end of file diff --git a/template/CodeSmith/CodeGenerator/Properties/AssemblyInfo.cs b/template/CodeSmith/CodeGenerator/Properties/AssemblyInfo.cs deleted file mode 100644 index cf45a860c..000000000 --- a/template/CodeSmith/CodeGenerator/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// 有关程序集的常规信息通过以下 -// 特性集控制。更改这些特性值可修改 -// 与程序集关联的信息。 -[assembly: AssemblyTitle("CodeGenerator")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany( "河西软件科技" )] -[assembly: AssemblyProduct("CodeGenerator")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2014")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// 将 ComVisible 设置为 false 使此程序集中的类型 -// 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型, -// 则将该类型上的 ComVisible 特性设置为 true。 -[assembly: ComVisible(false)] - -// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID -[assembly: Guid("aa2d87ce-657e-4282-a436-fdb9c995fd2e")] - -// 程序集的版本信息由下面四个值组成: -// -// 主版本 -// 次版本 -// 生成号 -// 修订号 -// -// 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, -// 方法是按如下所示使用“*”: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/template/Pd/Resource Files/DBMS/pgsql9.xdb b/template/Pd/Resource Files/DBMS/pgsql9.xdb deleted file mode 100644 index 34d5429a3..000000000 --- a/template/Pd/Resource Files/DBMS/pgsql9.xdb +++ /dev/null @@ -1,4418 +0,0 @@ - - - - - - - -83063C86-4E0A-4954-BF00-620C0D0F02D8 -PostgreSQL 9.x -PGSQL9 -0 - -1341906213 -mledier -ANSI -'****************************************************************************** -'* Purpose: This VB-Script holds global definitions shared by all the custom- -'* checks scripts of the model extension. -'****************************************************************************** - -Option Explicit ' This is to ensure all used variables are defined - - - - -F1A020C1-477C-4FE6-B187-7FC7D46FE484 -General -0 - -0 - -Target DBMS identification - - -EA3C706C-F552-4CC5-8A17-AB0A2D0F4BDE -SqlSupport -0 - -0 - -SQL syntax allowed. This does not impact the script generation, but it impacts the SQL Preview -1 - - -7868A7BA-E474-4E27-BDF3-1104AFABF7C0 -EnableCheck -0 - -0 - -Determines if the generation of check parameters is authorized or not -1 - - -714677D5-B508-4B87-AB65-AF6E66C5C151 -Enableconstname -0 - -0 - -Determines if constraint names are used during the generation -1 - - -AB1F411F-11C4-4EDD-8A76-7A79B81D042D -UniqConstName -0 - -0 - -Determines if unique constraint names for objects are authorized or not - - -68C5494A-C8AB-4FC7-A431-3EAF5B4C44D6 -EnableIntegrity -0 - -0 - -Allows integrity constraints in the DBMS. This controls whether generation parameters for primary, foreign, and alternate keys are grayed or available -1 - - -09CC7C62-0030-4BD4-BB4D-24373F684A76 -EnableMultiCheck -0 - -0 - -Determines if the generation of multiple check parameters is authorized or not -1 - - - - -CC013A88-7C22-4315-9C88-0B17EC54F8D0 -Script -0 - -0 - -DBMS characteristics, command definition, and data type translations for the Script generation and reverse engineering - - -5FD38E36-8DC0-4299-974F-F28B32CCF6C1 -Sql -0 - -0 - -Contains sub-categories Syntax, Format, File and Keywords. Each sub-category contains entries whose values define general syntax for the database - - -5C4F8EE0-FFDE-4901-A42E-08330F43BE4E -Syntax -0 - -0 - -Contains general parameters for SQL syntax - - -1207496B-6D26-4E86-B310-FC0B82E8E494 -Terminator -0 - -0 - -End of command character -; - - -30DA977E-0C12-41F8-A871-734B9C92752B -BlockTerminator -0 - -0 - -End of block character - - -E5E60CF7-3FDB-4F4A-ACFA-E61497BB94F8 -Delimiter -0 - -0 - -Field separation character. Example: col1, col2, col3 -, - - -02FCF451-A3F8-4443-854F-39B46B4E90D0 -Quote -0 - -0 - -Character used to enclose string values -' - - -53DCF79F-F968-4CFB-8081-100F8BE9598E -SqlContinue -0 - -0 - -Continuation character - - -941E4818-62E6-468B-AF8F-682C2B802D75 -LineComment -0 - -0 - -Characters used to enclose a single line comment --- - - -EE966B5D-7C62-4EAA-837C-BEEC61F23DD8 -BlockComment -0 - -0 - -Characters used to enclose a multi-line comment -/* */ - - -7A611A4A-F0EF-4AF8-9A14-CACDA6F94A83 -UseBlockTerm -0 - -0 - -Use end of block character by default - - - - -B9BD47AF-E067-4888-91D0-369F4FC676FD -Format -0 - -0 - -Contains entries that define script formatting - - -BE0A4657-577C-40DA-823D-DFE9D65EA0FE -IllegalChar -0 - -0 - -Invalid characters for names -" +-*/!=<>'"()[]". - - -884832B1-A23F-4D99-9D32-3300C16BEFCD -UpperCaseOnly -0 - -0 - -Uppercase only - - -410153CC-039A-4BCC-A9DA-73A14758BB96 -LowerCaseOnly -0 - -0 - -Lowercase only - - -270DB985-97B4-4796-B3AC-8E008529FBC5 -EnableOwnerPrefix -0 - -0 - -Object codes can have a prefix made of the object owner code -1 - - - - -3025ED8F-2ED3-44A9-824D-5BCD6D6FD1B9 -File -0 - -0 - -Contains header, footer and usage text entries used during the generation - - -F615AB51-5D07-436F-8C4D-F43C8B7C5FDD -Header -0 - -0 - -Header text for a database generation script - - -71AC941F-F3B7-4278-BC51-DF34D6EF42CF -Footer -0 - -0 - -Footer text for a database generation script - - -F43D615E-3EBD-4C14-9199-5914CBEAA5DD -EnableMultiFile -0 - -0 - -Multi-script allowed -1 - - -FA22FD3F-AB4C-4DC2-86E9-10ED4592C386 -ScriptExt -0 - -0 - -Main script extension in database generation -sql - - -E532FCE4-F3B1-4CD0-BBD8-6006C09F4ACB -TableExt -0 - -0 - -Other scripts extension in database generation -tab - - -7AE42F8F-8544-4AB1-B396-D843BE10CE7E -StartCommand -0 - -0 - -Command for executing a script - - -8391BA96-63E7-43A6-B37C-7C65B9525CC9 -Usage1 - (1) Go to the directory %PATHSCRIPT% - (2) Start the SQL interpreter - (3) Run the database creation script: - %NAMESCRIPT% -0 - -0 - -Usage for a single script in database generation - - -29E46CF1-DB88-4860-ABD2-09D9933D3FC6 -Usage2 - (1) Go to the directory %PATHSCRIPT% - (2) Start the SQL interpreter - (3) Run the database creation scripts -0 - -0 - -Usage for multiple scripts in database generation - - - - -CF7B9B8A-86B0-42F1-883E-BB792C02DFE1 -Keywords -0 - -0 - -Contains the list of reserved words and functions available in SQL - - -C65EFCC0-604A-4E47-B059-EA77B400FCDE -ReservedDefault -CURRENT_USER -SESSION_USER -USER -CURRENT_DATE -CURRENT_TIME -CURRENT_TIMESTAMP -NULL -0 - -0 - -Reserved default values - - -B8C734A3-0407-4828-A3AF-9EC38A4A7084 -GroupFunc -avg() -count() -max() -min() -sum() -0 - -0 - -List of SQL functions to use with group keywords. - - -1997444A-6E38-441F-BB1A-01CFFAC96EC4 -NumberFunc -abs() -degrees() -exp() -ln() -log() -pi() -pow() -radians() -round() -sqrt() -cbrt() -trunc() -float() -float4() -integer() - -0 - -0 - -List of SQL functions used on numbers - - -08A5913D-0845-4676-B284-5E02B34DFC49 -CharFunc -char() -char_length() -character_length() -initcap() -lower() -lpad() -ltrim() -octet_length() -position() -rpad() -rtrim() -substr() -substring() -text() -textpos() -translate() -trim() -upper() -varchar() - -0 - -0 - -List of SQL functions for characters and strings - - -4E9B8DD2-C3C8-431A-83B1-84AEDB98B887 -DateFunc -abstime() -age() -date_part() -date_trunc() -interval() -isfinite() -reltime() -timestamp() - -0 - -0 - -List of SQL functions for dates - - -50363314-58C8-4960-BA9D-4F69F83815FB -ConvertFunc -to_char() -to_date() -to_timestamp() -to_number() -0 - -0 - -List of SQL functions used to convert values between hex and integer and handling strings - - -E0B49A16-9386-4F77-ABD8-B00A609C8F41 -OtherFunc -coalesce() -nullif() -area() -box() -center() -circle() -diameter() -height() -isclosed() -isoldpath() -isopen() -length() -lseg() -npoint() -path() -pclose() -point() -polygon() -popen() -radius() -revertpoly() -upgradepath() -upgradepoly() -width() -broadcast() -host() -masklen() -netmask() - -0 - -0 - -List of other SQL functions - - -8763140B-F7FB-4D02-9792-2F532B3C42DE -ListOperators -- -! -!! -# -% -& -* -*= -/ -@ -^ -| -|/ -|| -||/ -~ -+ -< -< all -< any -<< -<= -<= all -<= any -<> -<> all -<> any -= -= all -= any -=* -> -> all -> any ->= ->= all ->= any ->> -and -between -exists -in -is -is not -like -not -not between -not exists -not in -not like -or -0 - -0 - -List of operators for comparing values, boolean, and various semantic operators - - -6B334218-ED13-4F55-93CC-9B462934CDCD -Commit -commit -0 - -0 - -Command for validating the transaction by OBDC - - -F9D5F8F9-A693-4FD9-897D-A61D7150C44E -ReservedWord -ALL -ANALYSE -ANALYZE -AND -ANY -AS -ASC -AUTHORIZATION -BETWEEN -BIGINT -BINARY -BIT -BOOLEAN -BOTH -CASE -CAST -CHAR -CHARACTER -CHECK -COALESCE -COLLATE -COLUMN -CONSTRAINT -CONVERT -CREATE -CROSS -CURRENT_DATE -CURRENT_TIME -CURRENT_TIMESTAMP -CURRENT_USER -DEC -DECIMAL -DEFAULT -DEFERRABLE -DESC -DISTINCT -DO -ELSE -END -EXCEPT -EXISTS -EXTRACT -FALSE -FLOAT -FOR -FOREIGN -FREEZE -FROM -FULL -GRANT -GROUP -HAVING -ILIKE -IN -INITIALLY -INNER -INT -INTEGER -INTERSECT -INTERVAL -INTO -IS -ISNULL -JOIN -LEADING -LEFT -LIKE -LIMIT -LOCALTIME -LOCALTIMESTAMP -NATURAL -NCHAR -NEW -NONE -NOT -NOTNULL -NULL -NULLIF -NUMERIC -OFF -OFFSET -OLD -ON -ONLY -OR -ORDER -OUTER -OVERLAPS -OVERLAY -PLACING -POSITION -PRIMARY -REAL -REFERENCES -RIGHT -ROW -SELECT -SESSION_USER -SETOF -SIMILAR -SMALLINT -SOME -SUBSTRING -TABLE -THEN -TIME -TIMESTAMP -TO -TRAILING -TREAT -TRIM -TRUE -UNION -UNIQUE -USER -USING -VARCHAR -VERBOSE -WHEN -WHERE -0 - -0 - -Reserved words - - - - - - -E16BAC33-A1FB-4D41-A754-3AB61E287A6A -Objects -0 - -0 - -Contains sub-categories for each type of object in the database, for example: Table, or Reference. Each sub-category contains entries whose values define database commands and object-related characteristics - - -5787687A-ED79-4140-AFDB-1BC438B4E1B2 -Table -TABL -0 - -0 - -The following system variables are available: - "TABLE" // generated code of the table - "TNAME" // name of the table - "TCODE" // code of the table - "TLABL" // comment of the table - "PKEYCOLUMNS" // list of primary key columns. Eg: A, B - "TABLDEFN" // complete body of the table definition. Contains definition of columns, checks and keys - - - -D25D7DD8-E18F-4E6C-8EDF-9ECED15275D7 -Maxlen -0 - -0 - -Maximum object code length -31 - - -4B9BC540-BD54-44B9-99E6-999BD9EBF5E3 -ConstName -CKT_%.U26:TABLE% -0 - -0 - -Constraint name template for check of table - - -F5049524-C037-4724-9519-F2C985F2BFD3 -Create -create [%Temporary% ]table "[%OWNER%]"."%TABLE%" ( - %TABLDEFN% -) -[%OPTIONS%] -0 - -1514553435 -Administrator -Command for creating a table. Example: create table %TABLE% - - -D3C15873-790A-4ACD-BC92-859B7C7C1F0E -Options -inherits : composite=yes, parenthesis=yes, separator=yes -{ - <parent_table> %s : multiple=yes -} -<special_columns> %s : list=with oids|without oids -on commit %s : list=preserve rows|delete rows|drop -tablespace %s : category=tablespace -0 - -0 - -Available options for creating a table - - -BCC29DEE-6F1F-45EE-B38B-41EFD1FFF636 -TableComment -comment on table "[%OWNER%]"."%TABLE%" is -%.q:COMMENT% -0 - -1514553663 -Administrator -Command for adding a table comment - - -71513402-ADFB-4CE2-A73C-56D3D8E26CC8 -Drop -drop table [%QUALIFIER%]%TABLE% -0 - -0 - -Command for dropping a table. Example: drop table %TABLE% - - -5A2D175A-FB8B-4182-A24B-3A863BEB2A49 -Rename -alter table [%QUALIFIER%]%OLDTABL% - rename to %NEWTABL% -0 - -1244729550 -mledier -Command for renaming a table - - -9E059BBF-C0DD-4A20-85E0-FEBA2D8A9435 -AlterTableHeader -0 - -0 - -Alter table header - - -080E3432-3F41-470A-B7A0-C83594DE3E52 -AlterTableFooter -0 - -0 - -Alter table footer - - -7524AF71-E973-4D57-98BF-86C82A19CF11 -DefineTableCheck -[constraint %CONSTNAME% ]check (%.A:CONSTRAINT%) -0 - -0 - -Allows to customize the script of table check constraints - - -DC6A6F24-AE59-494D-986E-7727698D9C48 -Enable -0 - -0 - -Table allowed -1 - - -8B726C93-8587-4D14-89DD-AB4744CF7677 -AddTableCheck -alter table [%QUALIFIER%]%TABLE% - add [constraint %CONSTNAME% ]check (%.A:CONSTRAINT%) -0 - -1244729550 -mledier -Allows to customize the script for modifying table constraints within an alter table statement - - -BA26AAF4-1E2B-40AF-BE30-F3B8F1BD6E95 -DropTableCheck -alter table [%QUALIFIER%]%TABLE% - drop constraint %CONSTNAME% -0 - -1244729550 -mledier -Command for dropping a table check in an alter table statement - - -B99A5700-27CC-45D8-B3C9-04218133C0D9 -SqlListQuery -{OWNER, TABLE, TABLE_TYPE, COMMENT} - -select - pg_get_userbyid(t.relowner), - t.relname, - case substring(t.relname from 1 for 3) - when 'pg_' then 'SYSTEM TABLE' else 'TABLE' - end::varchar, - obj_description(t.oid, 'pg_class') -from - pg_class t -where - t.relkind = 'r' -[ and pg_get_userbyid(t.relowner) = %.q:SCHEMA%] -order by 1, 2 -0 - -1341908013 -mledier -SQL query to list objects - - -81F8F744-2727-402B-BC1A-5B056B1E571E -AfterCreate -[%OWNER%?%SetOwnership%] -0 - -1257761737 -mledier -Commands executed after create statement - - -D46478F3-F5A2-45CA-811E-BCE8CF3780F0 -SqlChckQuery -{OWNER, TABLE, CONSTNAME, CONSTRAINT} - -select - pg_get_userbyid(t.relowner), - t.relname, - k.conname, - k.consrc -from - pg_constraint k - join pg_class t on (t.oid = k.conrelid) -where - k.contype = 'c' - and k.conrelid != 0 -[ and pg_get_userbyid(t.relowner) = %.q:OWNER%] -[ and t.relname = %.q:TABLE%] - and array_dims(k.conkey) != '[1:1]' -- a table constraint applies on more than one column ! - -0 - -1341908522 -mledier -SQL query to reverse object check constraints - - -3B8C614F-A6CB-47E9-A1C9-4AD0464951C1 -SetOwnership -0 - -1514545636 -Administrator -1 - - -070CDD2B-026F-46B2-B53A-F06EB48A65C4 -ReversedStatements -SetOwnership -0 - -0 - -Additional statements to be reversed by script - - -DCBEC306-5F49-4294-B8B8-CFA0610D68B7 -SqlOptsQuery -{OWNER, TABLE, OPTIONS} - -select - pg_get_userbyid(t.relowner), - t.relname, - case (t.relhasoids) when '1' then 'with oids' else 'without oids' end - ||case when (t.reltablespace = 0) then '' else ' tablespace ' || (select s.spcname from pg_tablespace s where s.oid = t.reltablespace) end as coln -from - pg_class t -where - t.relkind = 'r' -[ and pg_get_userbyid(t.relowner) = %.q:SCHEMA%] -order by 1, 2 -0 - -1341908522 -mledier -SQL query to reverse object physical options - - - - -D469C1EC-3699-4486-B3C1-22A547A4D775 -Index -INDX -0 - -0 - -The following system variables are available: -(parent table items are also available for indexes) - "INDEX" // generated code of the index - "INDEXNAME" // index name - "INDEXCODE" // index code - "UNIQUE" // keyword "unique" when the index is unique - "INDEXTYPE" // index type (available only for a few DBMS) - "INDEXKEY" // keywords "primary", "unique" or "foreign" depending on the index origin - "CIDXLIST" // list of index columns. Eg: A asc, B desc, C asc - "CLUSTER" // keyword "cluster" when the index is cluster -For index columns, the following system variables are available: - "ASC" // keywords "ASC" or "DESC" depending on sort order - "ISASC" // TRUE if the index column sort is ascending - - - -6A266B2C-8C89-4333-B581-01FE4683BD49 -Maxlen -0 - -0 - -Maximum object code length -31 - - -158351C4-310E-4E60-AD98-A8AE9C963ABF -MaxColIndex -0 - -0 - -Maximum number of columns in an index -16 - - -A6311922-FFDD-48A8-BFF1-99B7FA752D4D -Enable -0 - -0 - -Index allowed -1 - - -63D26606-B484-4A2C-AC86-C366BF8E2124 -EnableAscDesc -0 - -0 - -ASC, DESC keywords allowed - - -E67A82C2-24E0-4C5E-AB7C-CAEE31E7DDCF -UniqName -0 - -0 - -Unique index name in the database -1 - - -C26CDC7E-654B-40BD-AB1E-A55CD2DF68D3 -Create -create [%UNIQUE%] index %INDEX% on %TABLE%[ using %INDEXTYPE%] ( -%CIDXLIST% -) -[%OPTIONS%] -0 - -0 - -Command for creating an index. Example: create index %INDEX% - - -AA8102D0-D4B0-4812-B4F4-25318309F35B -AddColIndex -%COLUMN% -0 - -0 - -Command for defining an index column - - -A496C837-C471-4255-A9A1-E7ADA47099A2 -Options -tablespace %s : category=tablespace -where %s -0 - -0 - -Default options for creating an index - - -89E10A37-4899-40B7-BDEF-EF62CC4EEA5A -Drop -drop index %INDEX% -0 - -0 - -Command for dropping an index. Example: drop index %INDEX% - - -A3892D84-13EC-4B8D-88D6-C7F04A4F2FD7 -IndexType -BTREE -RTREE -HASH -0 - -0 - -List of types available for an index - - -19D48614-2538-48BD-80E4-E9E932CEEDE5 -SqlListQuery -{OWNER, TABLE, INDEX, UNIQUE, INDEXTYPE, CIDXLIST} - -select - pg_get_userbyid(t.relowner), - t.relname, - i.relname, - case when (x.indisunique) then 'unique' else '' end, - (select c.amname from pg_am c where c.oid = i.relam), - pg_get_indexdef(x.indexrelid, 1, True) - || case when (x.indnatts > 1) then ', ' || pg_get_indexdef(x.indexrelid, 2, True) else '' end - || case when (x.indnatts > 2) then ', ' || pg_get_indexdef(x.indexrelid, 3, True) else '' end - || case when (x.indnatts > 3) then ', ' || pg_get_indexdef(x.indexrelid, 4, True) else '' end - || case when (x.indnatts > 4) then ', ' || pg_get_indexdef(x.indexrelid, 5, True) else '' end - || case when (x.indnatts > 5) then ', ' || pg_get_indexdef(x.indexrelid, 6, True) else '' end - || case when (x.indnatts > 6) then ', ' || pg_get_indexdef(x.indexrelid, 7, True) else '' end - || case when (x.indnatts > 7) then ', ' || pg_get_indexdef(x.indexrelid, 8, True) else '' end as coln -from - pg_class t - join pg_index x on (x.indrelid = t.oid and x.indisprimary is false) - join pg_class i on (i.oid = x.indexrelid) -where 1=1 -[ and pg_get_userbyid(t.relowner)=%.q:OWNER%] -[ and t.relname=%.q:TABLE%] -order by 1, 2, 3 - -0 - -1341908522 -mledier -SQL query to list objects - - -690BA164-502C-4CD7-87D2-9D103EC03A9D -EnableFunction -0 - -0 - -Function-based indexes allowed -1 - - -F6066718-C8EF-4569-9A40-03BC0A51237D -SqlOptsQuery -{OWNER, TABLE, INDEX, OPTIONS} - -select - pg_get_userbyid(t.relowner), - t.relname, - i.relname, - case when (i.reltablespace = 0) then '' else 'tablespace ' || (select s.spcname from pg_tablespace s where s.oid = i.reltablespace) end - ||case when (x.indpred is null) then '' else ' where ' || pg_get_expr(x.indpred, t.oid, false) end as coln -from - pg_class t - join pg_index x on (x.indrelid = t.oid and x.indisprimary is false) - join pg_class i on (i.oid = x.indexrelid) -where (i.reltablespace != 0) or (x.indpred is not null) -[ and pg_get_userbyid(t.relowner) = %.q:OWNER%] -[ and t.relname = %.q:TABLE%] -[ and i.relname = %.q:INDEX%] -0 - -1341908522 -mledier -SQL query to reverse object physical options - - - - -12961F66-AA6A-46EF-B73E-0429D5753729 -Sequence -SQNC -0 - -0 - -The following system variables are available: - "SQNC" // generated code of the sequence - - - -2590B828-ED12-472D-BD57-6C94ACB0018D -Enable -0 - -0 - -Sequence allowed -1 - - -E8D096B7-6265-4F0A-827E-AA7D09CD2D75 -Create -create sequence %SQNC% -[%OPTIONS%] -0 - -0 - -Command to create a sequence - - -04BD7462-001C-4E2A-9549-28DB02D36F04 -Drop -drop sequence %SQNC% -0 - -0 - -Command to drop a sequence - - -15DD78FF-1E58-44E3-8F0F-3B3B308D4E0F -Options -increment %d -minvalue %d -maxvalue %d -start %d -cache %d -cycle - -0 - -0 - -Options for creating a sequence - - -E063B299-96AF-4761-8BAF-F0C68AEBF420 -DefOptions -0 - -0 - -Default values for sequence options - - -1BE49214-419D-45F0-A7C4-5317B5191708 -SqlListQuery -{SQNC} - -select - s.relname -from - pg_class s -where - s.relkind = 'S' -order by 1 -0 - -0 - -SQL query to list objects - - -089823CE-5C3C-4E6C-8ADC-853E7EFEB8A5 -SqlAttrQuery -{SQNC, OPTIONS} - -select - s.sequence_name, - ('increment ' || (increment_by::varchar))::varchar -from - [%QUALIFIER%]%SQNC% s - -0 - -0 - -SQL query to reverse object attributes - - -780D038C-ECD5-4745-8A4A-101DB3C07F68 -Rename -rename %OLDNAME% to %NEWNAME% -0 - -0 - -Command for renaming a sequence - - - - -FF3844ED-6B0B-40E6-937B-917D5CB424A8 -Column -COLN -0 - -0 - -The following system variables are available: -(parent table items are also available for columns) - "COLUMN" // generated code of the column - "COLNNO" // position of the column in the list of columns of the table - "COLNNAME" // name of the column - "COLNCODE" // code of the column - "PRIMARY" // keyword "primary" if the column is primary - "ISPKEY" // TRUE if the column is part of the primary key - "FOREIGN" // TRUE if the column is part of one foreign key - - - -828D9749-94BF-484F-A06C-5C7B1F960903 -Maxlen -0 - -0 - -Maximum object code length -31 - - -CB82A9E9-FDD3-49DA-BA2F-8DFF3D31F8B8 -EnableDefault -0 - -0 - -Default values allowed -1 - - -09695073-87B8-48F8-98FF-93E405631B92 -ConstName -CKC_%.U17:COLUMN%_%.U8:TABLE% -0 - -0 - -Constraint name template for a column check parameter - - -8AC0DE6C-5EC7-4F0D-A25F-9446CC0E9101 -Add -"%COLUMN%" [%IDENTITY%?SERIAL:%20:DATATYPE%][.Z:[ %NULL%][ %NOTNULL%][ default %DEFAULT%] - [%CONSTDEFN%]] -0 - -1514554417 -Administrator -Command for defining a table column - - -1A0A97C8-7902-4E80-85DE-03EB9F1C78B9 -ColumnComment -comment on column "[%OWNER%]"."%TABLE%"."%COLUMN%" is -%.q:COMMENT% - -0 - -1514553663 -Administrator -Command for adding a column comment - - -2F9C3E97-9D17-4AA8-A23F-B766F9C7EE8E -Rename -alter table %TABLE% - rename %OLDCOLN% to %NEWCOLN% -0 - -0 - -Command for renaming a column - - -6484FA2C-0C2D-4297-B18B-C4CE8E6E33DB -Create -alter table %TABLE% - add[ column] %COLUMN% %DATATYPE% -0 - -0 - -Command for adding a column - - -69CDADE9-BDB7-45A9-9992-0DA3A03A3A37 -Drop -0 - -0 - -Command for dropping a column -Not yet supported. Should be -alter table %TABLE% drop[ column] %COLUMN% [restrict | cascade] - - - -827206FF-8FAC-4D38-A0A3-BEA2E85EBDCC -Enable -0 - -0 - -Column allowed -1 - - -A0B859F4-3181-4865-9A5B-80B41DA085CA -ModifyColnDflt -alter table %TABLE% - alter[ column] %COLUMN% [%DEFAULT%?set default %DEFAULT%:drop default] -0 - -0 - -Command for modifying a column default in an alter table statement - - -B18B751A-E445-443A-B4C6-99B933BFD22A -EnableIdentity -0 - -0 - -Identity keyword entry support. Identity columns are serial counters maintains by the database -1 - - -73CB8FE3-BCD6-4A0A-9A77-3370E9690128 -SqlListQuery -{OWNER, TABLE, COLUMN, DTTPCODE, LENGTH, PREC, ISMAND, DEFAULT, COMMENT} - -select - pg_get_userbyid(t.relowner), - t.relname, - c.attname, - case(c.attndims) when 0 then d.typname else (select x.typname from pg_type x where x.oid = d.typelem) || '[]' end, - case(c.atttypmod >> 16) when 0 then (c.atttypmod - ((c.atttypmod >> 16)<<16) - 4) else (c.atttypmod >> 16) end, - case(c.atttypmod >> 16) when 0 then 0 else (c.atttypmod - 65536 * (c.atttypmod / 65536) - 4) end, - c.attnotnull, - case(c.atthasdef) - when true then (select adsrc::varchar from pg_attrdef f where f.adrelid = t.oid and f.adnum = c.attnum)::varchar - else '' end::varchar as coln, - col_description(t.oid, c.attnum) -from - pg_class t - join pg_attribute c on (c.attrelid = t.oid and c.attnum > 0 and c.attisdropped is false) - join pg_type d on (d.oid = c.atttypid) -where t.relkind = 'r' -[ and pg_get_userbyid(t.relowner) = %.q:OWNER%] -[ and t.relname = %.q:TABLE%] -order by 1, 2, c.attnum -0 - -1341908522 -mledier -SQL query to list objects - - -32224D8A-DF82-45F5-B446-39F2272E16EA -SqlChckQuery -{OWNER, TABLE, COLUMN, CONSTNAME, CONSTRAINT} - -select - pg_get_userbyid(t.relowner), t.relname, c.attname, k.conname, k.consrc -from - pg_constraint k - join pg_class t on (t.oid = k.conrelid) - join pg_attribute c on (c.attrelid = t.oid and c.attnum > 0 and c.attnum = k.conkey[1]) -where - k.contype = 'c' - and k.conrelid != 0 - and array_dims(k.conkey) = '[1:1]' -- a column constraint applies on only one column ! -[ and pg_get_userbyid(t.relowner) = %.q:OWNER%] -[ and t.relname = %.q:TABLE%] -[ and c.attname = %.q:COLUMN%] -0 - -1341909104 -mledier -SQL query to reverse object check constraints - - -A7FBC232-1BEB-4789-8E22-0148B2ABA21E -DefineColnChck -[%R%?[[foreign key (%S%) ]references %S%[(%S%)][ with no index]][%ISPKEY%?[constraint %PKNAME% ]primary key ][%ISAKEY%?[constraint %AKNAME% ]unique ]][[constraint %CONSTNAME%] check (%.A:CONSTRAINT%)] - -0 - -0 - -Allows to customize the script of column check constraints - - - - -4F5CF591-46F1-489C-BB52-8456FC8F6387 -MaxConstLen -0 - -0 - -Maximum constraint name length -31 - - -162F26E6-8FF1-42AD-B4E8-F671074E05DF -EnableOption -0 - -0 - -Physical options allowed YES/NO -1 - - -00D9D8AE-E5D1-456C-9E00-6413C2F27450 -Reference -REFR -0 - -0 - -The following system variables are available: - "REFR" // generated code of the reference - "REFNAME" // reference name - "PARENT" // generated code of the parent table - "PNAME" // name of the parent table - "PCODE" // code of the parent table - "CHILD" // generated code of the reference - "CNAME" // name of the child table - "CCODE" // code of the child table - "PQUALIFIER" // qualifier of the parent table. See QUALIFIER - "CQUALIFIER" // qualifier of the child table. See QUALIFIER - "REFRNAME" // Reference name - "REFRCODE" // Reference code - "FKCONSTRAINT" // Reference constraint name (foreign key) - "PKCONSTRAINT" // constraint name of the parent key used to reference object - "CKEYCOLUMNS" // list of parent key columns. Eg: C1, C2, C3 - "FKEYCOLUMNS" // list of child foreign key columns. Eg: - "UPDCONST" // Update declarative constraint. Keywords "restrict", "cascade", "set null" or "set default" - "DELCONST" // Delete declarative constraint. Keywords "restrict", "cascade", "set null" or "set default" - "MINCARD" // Min cardinality - "MAXCARD" // Max cardinality - "POWNER" // Parent table owner - "COWNER" // child table owner - "CHCKONCMMT" // TRUE when check on commit is selected on the reference (ASA 6.0 specific) - -For reference joins (couple of column in a reference), -the following system variables are available: - "CKEYCOLUMN" // generated code of the parent table column (primary key) - "FKEYCOLUMN" // generated code of the child table column (foreign key) - "PK" // primary key column generated code - "PKNAME" // primary key column name - "FK" // foreign key column generated code - "FKNAME" // foreign key column name - "AK" // alternate key column code (same as PK) - "AKNAME" // alternate key column name (same as PKNAME) - "COLTYPE" // primary column column datatype - "DEFAULT" // foreign key column default value - - - -EB1D6CAE-4F39-40A9-A085-7E520D794CC3 -Enable -0 - -0 - -Foreign key allowed -1 - - -9CFA076E-382C-4E94-BD87-C8B9A0279178 -FKAutoIndex -0 - -0 - -Foreign key is auto-indexed - - -2C3A75D2-AF8F-4B73-8811-088384CF5768 -ConstName -FK_%.U8:CHILD%_%.U9:REFR%_%.U8:PARENT% -0 - -0 - -Constraint name template for foreign keys - - -EC2AEA60-0853-4A0D-A257-CF973D2C1422 -CheckOnCommit -0 - -0 - -Referential integrity test differed after the COMMIT - - -D2D10DAE-E158-45A1-A6C6-494BE005B975 -Add -0 - -1514555514 -Administrator -Command for defining a foreign key - - -78ED96D5-7585-4E89-91E2-BAA53B8B5A28 -DclUpdIntegrity -RESTRICT -CASCADE -SET NULL -SET DEFAULT -NONE -0 - -0 - -Declarative referential integrity constraint allowed for update - - -317E3894-CA93-4320-8BDD-0B71E3C76394 -DclDelIntegrity -RESTRICT -CASCADE -SET NULL -SET DEFAULT -NONE -0 - -0 - -Declarative referential integrity constraint allowed for delete - - -D60B0ACA-00FB-4AB4-809C-38AD4FEADB2D -Create -0 - -1514555488 -Administrator -Command for adding a foreign key - - -CCBCF292-6A8A-496A-AB10-81DC9B1D60A0 -SqlListQuery -{POWNER, PARENT, COWNER, CHILD, CONSTNAME, Deferrable, ForeignKeyConstraintDeferred, UPDCONST, DELCONST, FKEYCOLUMNS, CKEYCOLUMNS} - -select - pg_get_userbyid(p.relowner), - p.relname, - pg_get_userbyid(c.relowner), - c.relname, - k.conname, - case when (k.condeferrable) then 'true' else 'false' end, - case when (k.condeferred) then 'true' else 'false' end, - case(k.confupdtype) when 'r' then 'restrict' when 'c' then 'cascade' when 'n' then 'set null' when 'd' then 'set default' else 'no action' end::varchar as colnA, - case(k.confdeltype) when 'r' then 'restrict' when 'c' then 'cascade' when 'n' then 'set null' when 'd' then 'set default' else 'no action' end::varchar as colnB, - substring(substring(pg_get_constraintdef(k.oid) from 'FOREIGN KEY (.*) REFERENCES') from 2 for char_length(substring(pg_get_constraintdef(k.oid) from 'FOREIGN KEY (.*) REFERENCES')) - 2) as colnC, - substring(substring(pg_get_constraintdef(k.oid) from 'REFERENCES '||p.relname||'(.*)') from 2 for position(')' in substring(pg_get_constraintdef(k.oid) from 'REFERENCES '||p.relname||'(.*)') ) - 2) as colnD -from - pg_constraint k - join pg_class p on (p.oid = k.confrelid) - join pg_class c on (c.oid = k.conrelid) -where - k.contype = 'f' -[ and pg_get_userbyid(c.relowner) = %.q:OWNER%] -[ and c.relname=%.q:TABLE%] -order by 1, 2, 3, 4, 5 -0 - -1341908522 -mledier -SQL query to list objects - - - - -394D6E77-60DA-41C7-89D4-FB9783581615 -PKey -PKEY -0 - -0 - -The following system variables are available: -(parent table items are also available for keys) - "PKEYCOLUMNS" // list of primary key columns. Eg: A, B - "ISPKEY" // TRUE when the key is the primary key of the table - "KEY" // constraint name - "PKEY" // constraint name for primary key - "AKEY" // constraint name for alternate key - "ISMULTICOLN" // TRUE if key has more than one column - "CLUSTER" // keyword cluster - - -FD4344E9-E9CC-4B7C-9BB0-4E4EED0CEE72 -Enable -0 - -0 - -Primary key allowed -1 - - -35F0CA00-8A94-4D42-A911-F9BCFF8C206D -PkAutoIndex -0 - -0 - -Primary key is auto-indexed - - -9E64641C-FC08-4E73-9433-D69D614EC61D -ConstName -PK_%.U27:TABLE% -0 - -0 - -Constraint name template for primary keys - - -7F2E72C1-2E4C-4159-8942-AAFA742A4131 -Add -[constraint %CONSTNAME% ]primary key ("%PKEYCOLUMNS%") -0 - -1514557752 -Administrator -Command for defining a primary key - - -303ED984-E4BB-4FAA-BF86-95DBBBCCF697 -Create -alter table "[%OWNER%]"."%TABLE%" - add [constraint %CONSTNAME% ]primary key ("%PKEYCOLUMNS%") - -0 - -1514553853 -Administrator -Command for adding a primary key - - -C02EC114-6CBB-45B1-B4B5-6B7E3CEBED89 -Options -using index tablespace %s -0 - -0 - -Available options for creating a primary key - - -6CBAA878-3E66-4E06-B35F-9BCA1250C13A -Drop -alter table %TABLE% - drop constraint %CONSTNAME% -0 - -0 - -Command for dropping a primary key - - - - -5CA1DDBD-771A-4DEE-99A2-325F82A15288 -Key -KEY -0 - -0 - -The following system variables are available: -(parent table items are also available for keys) - "COLUMNS" // List of columns of the key. Eg: "A, B, C" - "ISPKEY" // TRUE when the key is the primary key of the table - "KEY" // constraint name - "PKEY" // constraint name for primary key - "AKEY" // constraint name for alternate key - "ISMULTICOLN" // TRUE if key has more than one column - "CLUSTER" // keyword cluster - - - -B6AA8161-11E3-42C4-AD8C-5B8E0804508A -Enable -0 - -0 - -UNIQUE constraint allowed for tables -1 - - -29F6C46C-6412-4DE9-97FC-6BF9ED12C4F6 -UniqConstAutoIndex -0 - -0 - -UNIQUE constraint is auto-indexed -1 - - -FE710D18-9664-4853-860B-089C2AC56D43 -ConstName -AK_%.U18:AKEY%_%.U8:TABLE% -0 - -0 - -Constraint name template for alternate keys - - -3E8D0AD3-B254-4556-9885-B6D603C45759 -Add -[constraint %CONSTNAME% ]unique (%COLUMNS%) -0 - -0 - -Command for defining an alternate key - - -B946017B-7EB2-4E25-8864-27BFB6774ED0 -Create -alter table %TABLE% - add [constraint %CONSTNAME% ]unique (%COLUMNS%) -0 - -0 - -Command for adding an alternate key - - -BDE692C1-B51D-48EB-81FB-0BFCF3B49572 -Options -using index tablespace %s - -0 - -0 - -Available options for an alternate key - - -619ECBA7-A1D2-4EE3-B8C9-F811E7CC0290 -SqlListQuery -{OWNER ID, TABLE ID, CONSTNAME ID, ISPKEY ID, COLUMNS ...} - -select - pg_get_userbyid(t.relowner), - t.relname, - i.relname, - x.indisprimary, - a.attname || ','::varchar -from - pg_index x - join pg_class t on (t.oid = x.indrelid and t.relkind = 'r') - join pg_class i on (i.oid = x.indexrelid) - join pg_attribute a on (a.attrelid = i.oid) -where x.indisprimary is true -[ and pg_get_userbyid(t.relowner)=%.q:OWNER%] -[ and t.relname=%.q:TABLE%] -order by 1, 2, 3, a.attnum - -0 - -1341908522 -mledier -SQL query to list objects - - -C32D12FB-648F-4281-B1D7-0BE9540242C9 -Drop -alter table %TABLE% - drop constraint %CONSTNAME% -0 - -0 - -Command for dropping an alternate key - - - - -5C93D4F0-AD9E-4ACC-90D2-F4A90002BE81 -Database -DTBS -0 - -0 - -The following system variables are available: - "DATABASE" // generated code of the database - - - -D00C6FC9-2B52-4462-B5B2-C46605197C05 -Create -create database "%DATABASE%" -[ [%R%?with ][.Z:[ template[ =] %Template%][ encoding[ =] %.q:Encoding%][ tablespace[ =] %OPTIONS%]]] -0 - -1514552500 -Administrator -Command for creating a database. Example: create database %DATABASE% - - -1347A7DC-CC8D-4365-B8B7-AC815C936A87 -OpenDatabase -0 - -0 - -Command for opening a database. Example: open database %DATABASE% - - -883CDA80-551C-43E7-A0FA-19468A55E254 -CloseDatabase -0 - -0 - -Command for closing a database. Example: close database - - -527489B6-A8CA-491E-82A9-FB300D10904D -Drop -drop database %DATABASE% -0 - -0 - -Command for dropping a database. Example: drop database %DATABASE% - - -6271B61C-5571-4923-AD35-50AE6044E279 -Enable -0 - -0 - -Database allowed -1 - - -7B94D113-E137-4FF2-B383-A8FB0E129E1D -EnableManyDatabases -0 - -0 - -Many databases allowed -1 - - -43028201-52E6-4DC1-B48D-EBECCDC7EE94 -Options -<tablespace> %s : category=TABLESPACE -0 - -0 - -Available options for creating a database - - -E616EC6E-2767-4410-BC15-764AA6B67647 -SqlListQuery -{DATABASE, COMMENT, Encoding, OPTIONS} - -select - d.datname, - obj_description(d.oid, 'pg_database'), - pg_encoding_to_char(d.encoding), - (select t.spcname from pg_tablespace t where t.oid = d.dattablespace) -from - pg_database d -order by 1 -0 - -0 - -SQL query to list objects - - - - -6D6DC36E-2660-47B5-9731-E97BB1AFC19C -View -VIEW -0 - -0 - -The following system variables are available: - "VIEW" // generated code of the view - "VIEWNAME" // view name - "VIEWCODE" // view code - "VIEWCOLN" // List of columns of the view. Eg: "A, B, C" - "SQL" // SQL text of the view. Eg: Select * from T1 - "VIEWCHECK" // Keyword "with check option" if selected on the view - "SCRIPT" // complete view creation order. Eg: create view V1 as select * from T1 - - - -60C4C986-06CB-4FB0-80C1-7444D0E2DD0F -Create -create[ or replace] view %VIEW%[ (%VIEWCOLN%)] as -%SQL% -0 - -0 - -Command for creating a view. Example: create view %VIEW% - - -D7F80660-A729-463E-8715-6602F92DAA4A -Drop -drop view %VIEW% -0 - -0 - -Command for dropping a view. Example: drop view %VIEW% - - -A2DF1F7F-1FE8-4053-898C-741F30BD0E32 -ViewComment -comment on view %VIEW% is -%.q:COMMENT% - -0 - -0 - -Command for adding a view comment. - - -A65C3C8D-4FF8-4BAC-B525-7B7C0A85BA0E -SqlListQuery -{OWNER, VIEW} - -select - pg_get_userbyid(t.relowner), - t.relname -from - pg_class t -where t.relkind = 'v' -[ and pg_get_userbyid(t.relowner) = %.q:SCHEMA%] -order by 1, 2 -0 - -1341908522 -mledier -SQL query to list objects - - -9E32900C-BF73-416C-8A2B-EF1770CB4D13 -Enable -0 - -0 - -View allowed -1 - - -FAED4860-650D-4088-B0E6-574FD05B32A5 -SqlAttrQuery -{OWNER, VIEW, SQL} - -select - pg_get_userbyid(v.relowner), - v.relname, - pg_get_viewdef(v.oid) -from - pg_class v -where v.relkind = 'v' -[ and pg_get_userbyid(v.relowner) = %.q:OWNER%] -[ and v.relname = %.q:VIEW%] - -0 - -1341908522 -mledier -SQL query to reverse object attributes - - -7B673B74-71BD-423E-80F6-670FFCDA0C25 -SetOwnership -[-- set view ownership -]alter table %VIEW% owner to %OWNER% -; -0 - -0 - -1 - - -4AD2B5CD-4B14-41DE-AA40-2A9977165E28 -AfterCreate -[%OWNER%?%SetOwnership%] -0 - -0 - -Commands executed after create statement - - -B5AABC8F-D6D9-49DE-8200-FC6813E9259F -ReversedStatements -SetOwnership -0 - -0 - -Additional statements to be reversed by script - - - - -315292D9-F98A-4EE5-B761-6515CB98B37C -Domain -PDMDOMN -0 - -0 - -The following system variables are available: - "DOMAIN" // generated code of the domain (also available for columns) -SQL Server specific domain system variables: - "RULENAME" // name of the rule object associated with the domain - "DEFAULTNAME" // name of the default object associated with the domain - - - -609E7A72-CC2A-4D04-A138-92795CBEACC9 -Create -[%Stereotype%=CompositeType?create type [%QUALIFIER%]%DOMAIN% as (%CompositeDefinition%):[%Stereotype%=BaseType?create type [%QUALIFIER%]%DOMAIN% ([.Z:[ - input=%ExtTypeInput%,][ - output=%ExtTypeOutput%,][ - internallength=%ExtTypeLength%][, - default=%DEFAULT%][, - element=%ExtTypeElement%][, - delimiter=%.q:ExtTypeDelimiter%][, - send=%ExtTypeSend%][, - receive=%ExtTypeReceive%][%ExtTypePassedByValue%?, passedbyvalue]] -):create domain [%QUALIFIER%]%DOMAIN%[ as] %DATATYPE%]] - -0 - -0 - -Command for creating a user defined data type - - -C4EF9C30-D684-4430-AE35-A4EE0A89648B -Maxlen -0 - -0 - -Maximum object code length -30 - - -EB4A3477-D194-4ADC-8684-4E398F454C2E -Enable -0 - -0 - -User defined data types allowed -1 - - -46CB428B-DC23-4DBE-98CB-EA7BD9DA1E64 -Drop -[%Stereotype%=BaseType?drop type %DOMAIN%:drop domain %DOMAIN%] -0 - -0 - -Command for dropping a user defined data type - - -F776AAE4-1B7D-4417-ADA0-925918FA8C46 -SqlListQuery -{OWNER, DOMAIN, DTTPCODE, Stereotype} - -select - pg_get_userbyid(d.typowner), - d.typname, - case (d.typtype) when 'd' then (select t.typname from pg_type t where t.oid = d.typBaseType) else '' end, - case (d.typtype) when 'b' then 'BaseType' when 'c' then 'CompositeType' else '' end -from - pg_type d - left outer join pg_class r on (r.oid = d.typrelid) -where - (d.typtype in ('b', 'd') or (d.typtype = 'c' and r.relkind = 'c')) -[ and pg_get_userbyid(d.typowner) = %.q:SCHEMA%] -order by 1, 2 -0 - -1341908522 -mledier -SQL query to list objects - - -AFA53FAE-E05C-4C4B-BB73-A8EC404CFFE6 -EnableOwner -0 - -0 - -Owner allowed -1 - - -E2BAA09E-4664-4153-A28C-A206743C8BD5 -EnableCheck -0 - -0 - -Allows the creation of checks on the domain -1 - - -DA4BAC34-7D11-4D74-9A58-5142566F9D88 -SqlCompositeTypeColnList -{OWNER ID, DOMAIN ID, CompositeDefinition ...} - -select - pg_get_userbyid(r.relowner), - r.relname, - c.attname || ' ' || t.typname || ', ' -from - pg_class r - join pg_attribute c on (c.attrelid = r.oid) - join pg_type t on (t.oid = c.atttypid) -where - r.relkind = 'c' -[ and r.relname = %.q:DOMAIN%] -[ and pg_get_userbyid(r.relowner) = %.q:OWNER%] -order by 1, 2, c.attnum -0 - -0 - -1 - - -F67508D3-3C78-4C24-BC4B-55D90C64689A -ReversedQueries -SqlCompositeTypeColnList -0 - -0 - -Additional attributes queries to be called by ODBC - - -330B7CDB-EF27-4ADE-BBFB-56329E4AE80A -UddtComment -comment on [%Stereotype%=BaseType?type:[%Stereotype%=CompositeType?type:domain]] %DOMAIN% is -%.q:COMMENT% -0 - -0 - -command for adding a user-defined data type comment - - - - -2D803C02-6E8E-42E3-884C-99D8D9D11B0A -Procedure -PROC -0 - -0 - -The following system variables are available: - "PROC" // generated code of the procedure (also available for trigger when the trigger is implemented with a procedure) - "FUNC" // generated code of the procedure if the procedure is a function (with return value) - "PROCPRMS" // list of parameters of the procedure (also available for function) - - - -4975CC75-CAB7-4FAC-9989-EC181498F4EB -Enable -0 - -0 - -Procedure allowed -1 - - -B227AD93-0969-43CE-B311-735CF6FA3BA7 -EnableFunc -0 - -0 - -Function allowed -1 - - -FDA1A9E3-3694-48FD-A788-228D53B19829 -Maxlen -0 - -0 - -Maximum object code length -31 - - -567B59B0-D391-4E8B-B6EB-F9280D8458C8 -MaxFuncLen -0 - -0 - -Function name length -31 - - -93FB86F2-AFA3-40FB-A122-704F47E14567 -DropFunc -drop function %FUNC% ( <type> ) -0 - -0 - -Command for dropping a function. Example: drop function %FUNC% - - -68DA6237-E801-4609-8DF3-9913541D763C -CustomFunc -CREATE FUNCTION %FUNC% ( <type> ) - RETURNS <type> - AS '%SCRIPT%' - LANGUAGE '%ProcLanguage%' -; -0 - -0 - -Command for creating a function - - -251982F3-DE0C-4FAA-A131-7DBFE0759575 -SqlListQuery -{OWNER, PROC, FUNC} - -select - pg_get_userbyid(p.proowner), - p.proname, - case(pronargs) when 0 then '' else p.proname end -from - pg_proc p - join pg_language l on (l.oid = p.prolang and l.lanname <> 'internal') -[where pg_get_userbyid(p.proowner) = %.q:SCHEMA%] -order by 1, 2, 3 -0 - -0 - -SQL query to list objects - - -192AC9A1-15B6-4DF7-BC4C-B516BDAF88FB -SqlAttrQuery -{OWNER, PROC, ProcLanguage, TRGDEFN} - -select - pg_get_userbyid(p.proowner), - p.proname, - l.lanname, - p.prosrc::varchar -from - pg_proc p - join pg_language l on (l.oid = p.prolang and l.lanname <> 'internal') -where 1=1 -[ and pg_get_userbyid(p.proowner) = %.q:OWNER%] -[ and p.proname = %.q:PROC%] -0 - -0 - -SQL query to reverse object attributes - - -40FF2B3A-67ED-44EF-A265-8A9E228E7687 -Create -create procedure %PROC%[(%PROCPRMS%)] -as %.q:TRGDEFN% -0 - -0 - -Command for creating a procedure. Example: create procedure %PROC% %TRGDEFN% - - -AAEB8CBB-C876-4B6D-BE50-E6E73BC964D8 -CreateFunc -create[ or replace] function %FUNC%[(%PROCPRMS%)] -%TRGDEFN% -0 - -0 - -Command for creating a function. Example: create function %FUNC% %TRGDEFN% - - - - -8FF2CB5D-C3CB-4D91-BE5B-64EE3E717750 -User -USER -0 - -0 - -The following system variables are available: - "USER" // generated code of the user - - - -E6F5A60D-CA9F-44E1-8E1C-126237EE8264 -Maxlen -0 - -0 - -Maximum object code length -31 - - -D9017676-BDD5-453E-924C-0D0D36CC2448 -Enable -0 - -0 - -User allowed -1 - - -F8617015-368F-4D56-9D87-9FE3DADBC19A -SqlListQuery -{USER, Schema, SysId, CreateDB, CreateUser, Validity, Owner} - -select - usename, 'false', - usesysid, - case (usecreatedb) when true then 'true' else 'false' end, - case (usesuper) when true then 'true' else 'false' end, - to_char(valuntil, 'DD/MM/YYYY HH24:MI:SS'), - '' -from - pg_user -[where - UPPER(usename) = %.qU:SCHEMA%] -union select - nspname, 'true', - 0, - '', - '', - '', - pg_get_userbyid(nspowner) -from - pg_namespace -[where UPPER(nspname) = %.qU:SCHEMA%] -order by 1 -0 - -0 - -SQL query to list objects - - -AE3804C2-2F0E-498E-AE13-BFE9F75A1DB7 -Create -create schema "%USER%" -0 - -1514552569 -Administrator -Command for creating a user - - -0C08C855-C137-4B0C-91DD-5C84925F17C8 -Drop -[%CanCreate%?drop [%Schema%?schema:user] %USER%] -0 - -1246367139 -mledier -Command for dropping a user - - - - -3F101A42-CA85-4BEA-A518-927051455323 -Trigger -TRGR -0 - -0 - -The following system variables are available: -(parent table items are also available for indexes) - trigger object specific items - "ORDER" // order number of the trigger (to sort the triggers when the DBMS supports more than one trigger of one type) - "TRIGGER" // generated code of trigger - "TRGTYPE" // trigger type (keywords "beforeinsert", "afterupdate", ...) - "TRGEVENT" // trigger event (keywords "insert", "update", "delete") - "TRGTIME" // trigger time (keywords NULL, "before", "after") - inside the body of the trigger, macros redefine variables on objects - "REFNO" // reference position in the list of references of the table - customized error messages management - "ERRNO" // error number for standard error - "ERRMSG" // error message for standard error - "MSGTAB" // name of the table containing user-defined error messages - "MSGNO" // in the user-defined error table, name of the column containing the error numbers - "MSGTXT" // in the user-defined error table, name of the column containing the error messages - "SCRIPT" // body of trigger or procedure - "TRGDEFN" // complete body of the trigger definition. Contains variable declarations and body of trigger or procedure - - - -E19DFCA9-7B9C-4C66-94E1-6219E6D60F2A -EnableMultiEvent -0 - -0 - -Multiple event allowed -1 - - -7F592BF2-1A0B-45C8-85FC-65AB6BF27D7A -Drop -drop trigger %TRIGGER% on [%QUALIFIER%]%TABLE% -0 - -0 - -Command for dropping a trigger. Example: drop trigger %TRIGGER% - - -6D51FD27-F0DA-4172-B79C-881FD8691D28 -SqlListQuery -{OWNER, TABLE, TRIGGER, TRGEVENT, TRGTIME} - -select - pg_get_userbyid(t.relowner), - t.relname, - g.tgname, - case (g.tgtype & 28) - when 4 then 'insert' when 8 then 'delete' when 16 then 'update' - when 12 then 'insert or delete' when 20 then 'insert or update' when 24 then 'delete or update' - when 28 then 'insert or delete or update' else '' end::varchar as coln, - case (g.tgtype & 2) - when 2 then 'before' when 0 then 'after' else '' end::varchar -from - pg_trigger g - join pg_class t on (t.oid = g.tgrelid and t.relkind = 'r') -where g.tgconstraint = 0 -[ and pg_get_userbyid(t.relowner) = %.q:SCHEMA%] -order by 1, 2 -0 - -1341907281 -mledier -SQL query to list objects - - -1A74C1FB-8DBF-49E1-8CC1-C218CBB058A6 -SqlAttrQuery -{OWNER, TABLE, TRIGGER, SCRIPT} - -select - pg_get_userbyid(t.relowner), - t.relname, - g.tgname, - pg_get_triggerdef(g.oid) -from - pg_trigger g - join pg_class t on (t.oid = g.tgrelid and t.relkind = 'r') -where g.tgconstraint = 0 -[ and pg_get_userbyid(t.relowner) = %.q:SCHEMA%] -[ and t.relname = %.q:TABLE%] -[ and g.tgname = %.q:TRIGGER%] -order by 1, 2 -0 - -1341909260 -mledier -SQL query to reverse object attributes - - -CE4F0440-B491-4676-9435-73737B55A8FF -UseErrorMsgTable -let errno = %ERRNO%; -select %MSGTXT% -into errmsg -from %MSGTAB% -where %MSGNO% = %ERRNO%; -raise exception -746, 0, errmsg; -0 - -0 - -Errors handling using an error messages table - - -5989F402-A130-4D39-AA84-E13023A49577 -UseErrorMsgText -let errno = %ERRNO%; -let errmsg = "%ERRMSG%"; -raise exception -746, 0, errmsg; -0 - -0 - -Errors handling using fixed error messages - - -00CC31F8-67B8-419E-82C9-CB8BDBE3C7ED -Create -create trigger %TRIGGER% %TRGTIME% %TRGEVENT% on %TABLE% -%TRGDEFN% -0 - -0 - -Command for creating a trigger. Example: create trigger on [%QUALIFIER%]%TABLE% %TRGDEFN% - - -BCE86A5F-400D-4EF5-AFEA-7A66DE9E65D1 -Maxlen -0 - -0 - -Maximum object code length -30 - - -5D052D24-6CFE-4A8F-8662-E5599DBEB469 -DefaultTriggerName -%TEMPLATE%_%.L:TABLE% -0 - -0 - -Default trigger name - - -802E3B4B-7A6E-451C-B065-37C52B07E693 -Enable -0 - -0 - -Trigger allowed -1 - - -34C945EE-91C3-44A0-990B-52391394D184 -EventDelimiter -0 - -0 - -Events separation character. -or - - -DE6F260F-ADD8-4CE8-8FC3-58D9D68CE6C0 -Time -before -after -0 - -0 - -Extended trigger times list. - - -A19C9B0A-3FF2-43AE-8340-BF77D513A2E9 -Event -insert -delete -update -0 - -0 - -Extended trigger events list. - - - - -7D1D2CDF-E58B-42B2-9016-5108F01CE18C -Tablespace -TSPC -0 - -0 - -The following system variables are available: - "TABLESPACE" // generated code of the tablespace - - - -0000D97B-6EF3-4C81-AC59-0AC769EF1889 -Enable -0 - -0 - -Tablespace allowed -1 - - -2BE7A654-B74D-4D5B-AA49-D6D2A546B6FE -Create -create tablespace %TABLESPACE% -[ owner %TbspOwner% -] location %.q:TbspLocation% -0 - -0 - -Command for creating a tablespace. Example: create tablespace %TABLESPACE% - - -0E7909E4-9E5E-4A10-80C0-CC8E1F8DD69C -Drop -drop tablespace %TABLESPACE% -0 - -0 - -Command for dropping a tablespace. Example: drop tablespace %TABLESPACE% - - -CD05E302-8958-41BF-9042-81185E8C6753 -SqlListQuery -{TABLESPACE, TbspOwner, TbspLocation} - -select - t.spcname, - pg_get_userbyid(t.spcowner), - t.spclocation -from - pg_tablespace t -order by 1 -0 - -0 - -SQL query to list objects - - - - -7837BA9D-D967-437A-95C3-C44DA43469A8 -Group -GRPE -0 - -0 - -Manages the group object. - - -B2F69FF7-ECB8-4F16-82E5-E8BE6E333B20 -Enable -0 - -0 - -Group allowed -1 - - -92FE7ABE-3D9C-4D59-BBB1-0AE7F543E719 -Create -create group %GROUP% -[ [%R%?with ] sysid %SysId%] -0 - -0 - -Command for creating a group. - - -6B19B14B-B135-4310-9025-3783B7F72E25 -Drop -drop group %GROUP% -0 - -0 - -Command for dropping a group. - - -BCE6A1C0-AD38-4AF6-8918-112A9109D421 -SqlListQuery -{GROUP} - -select - groname -from - pg_group -order by 1 -0 - -0 - -SQL query to list objects - - -44322879-5000-42F5-AB39-4DE022512CA5 -Bind -alter group %GROUP% add user %USER% -0 - -0 - -Command for adding a member to a group - - -880E598B-C369-4F64-8BEF-85689387E12A -Unbind -alter group %GROUP% drop user %USER% -0 - -0 - -Command for removing a member from a group - - -B29585AC-4B80-456A-97F0-5FBCD48641A5 -SqlListChildrenQuery -{GROUP, MEMBER} -select - g.groname, - u.usename -from - pg_group g - join pg_user u on (u.usesysid = any(g.grolist)) -[where g.groname = %.q:GROUP%] - -0 - -0 - -SQL query to list members of the group - - - - -EDE2756E-AD00-4A2F-B677-CF1436597E81 -GenerationOrder -<Order> -<Metaclass Name="Storage" /> -<Metaclass Name="Tablespace" /> -<Metaclass Name="Database"> - <Metaclass Name="Database::Permission" /> -</Metaclass> -<Metaclass Name="Role" /> -<Metaclass Name="Group" /> -<Metaclass Name="User" /> -<Metaclass Name="BusinessRule" /> -<Metaclass Name="Sequence"> - <Metaclass Name="Sequence::Permission" /> -</Metaclass> -<Metaclass Name="AbstractDataType"> - <Metaclass Name="AbstractDataType::Permission" /> -</Metaclass> -<Metaclass Name="PhysicalDefault" /> -<Metaclass Name="PhysicalDomain" /> -<Metaclass Name="Table"> - <Metaclass Name="Table::Key" /> - <Metaclass Name="Table::Index" /> - <Metaclass Name="Table::Permission" /> -</Metaclass> -<Metaclass Name="View"> - <Metaclass Name="View::ViewIndex" /> - <Metaclass Name="View::Permission" /> -</Metaclass> -<Metaclass Name="Reference" /> -<Metaclass Name="JoinIndex" /> -<Metaclass Name="Synonym"> - <Metaclass Name="Synonym::Permission" /> -</Metaclass> -<Metaclass Name="Dimension" /> -<Metaclass Name="Procedure"> - <Metaclass Name="Procedure::Permission" /> -</Metaclass> -<Metaclass Name="DatabasePackage"> - <Metaclass Name="DatabasePackage::Permission" /> -</Metaclass> -<Metaclass Name="Table::Trigger" /> -<Metaclass Name="View::Trigger" /> -<Metaclass Name="WebService" /> -</Order> -0 - -1263300423 -mledier -This list defines the objects generation order. - - - - -1C9264F0-50A8-45F8-BAB8-A6810D27D506 -DataType -0 - -0 - -Contains data type translation entries. These entries list the correspondence between internal data types and the target database data types - - -0BBE373C-5AAD-4A7A-853A-7BE50EA05FC0 -AmcdDataType -0 - -1514588659 -Administrator -Data types translation table from internal data types to target database data types. - %n is the length of the data type - %s is the size of the data type - %p is the precision of the data type -<UNDEF>=<Undefined> -A=CHAR(1) -A%n=CHAR(%n) -VA%n=VARCHAR(%n) -LA=VARCHAR(1) -LA%n=VARCHAR(%n) -LVA=VARCHAR(1) -LVA%n=VARCHAR(%n) -BT=INT2 -BT%n=INT2 -SI=INT2 -I=INT4 -LI=INT8 -N=NUMERIC -N%n=NUMERIC(%n) -N%s,%p=NUMERIC(%s,%p) -DC=DECIMAL -DC%n=DECIMAL(%n) -DC%s,%p=DECIMAL(%s,%p) -SF=FLOAT4 -F=FLOAT8 -F%n=FLOAT8 -LF=FLOAT8 -MN=MONEY -MN%n=MONEY -MN%s,%p=MONEY -D=DATE -T=TIME -DT=TIMESTAMP -TS=BYTEA -BL=BOOL -NO=SERIAL -NO%n=SERIAL -BIN%n=CHAR(%n) -LBIN=CHAR -LBIN%n=CHAR(%n) -TXT=TEXT -TXT%n=TEXT -MBT=CHAR -MBT%n=CHAR(%n) -VMBT=VARCHAR(254) -VMBT%n=VARCHAR(%n) -PIC=CHAR(254) -PIC%n=CHAR(%n) -BMP=CHAR(254) -BMP%n=CHAR(%n) -OLE=CHAR(254) -OLE%n=CHAR(%n) -*=CHAR(10) -POINT=POINT -LINE=LINE -SEGMENT=LSEG -RECTANGLE=BOX -POLYGON=POLYGON -CIRCLE=CIRCLE -A36=UUID -<UNDEF> -A -A%n -VA%n -LA -LA%n -LVA -LVA%n -BT -BT%n -SI -I -LI -N -N%n -N%s,%p -DC -DC%n -DC%s,%p -SF -F -F%n -LF -MN -MN%n -MN%s,%p -D -T -DT -TS -BL -NO -NO%n -BIN%n -LBIN -LBIN%n -TXT -TXT%n -MBT -MBT%n -VMBT -VMBT%n -PIC -PIC%n -BMP -BMP%n -OLE -OLE%n -POINT -LINE -SEGMENT -RECTANGLE -POLYGON -CIRCLE -A36 - - - -8152888C-EC21-4CE9-BE44-BB0185B56336 -PhysDataType -0 - -1514588528 -Administrator -Data types translation table from target database data types to internal data types. -<Undefined>=<UNDEF> -DECIMAL=DC -DECIMAL(%n)=DC%n -DECIMAL(%s,%p)=DC%s,%p -FLOAT4=F7 -FLOAT8=F16 -INT2=SI -INT4=I -INT8=LI -NUMERIC=N -NUMERIC(%n)=N%n -NUMERIC(%s,%p)=N%s,%p -SERIAL=NO -MONEY=MN -CHAR=A1 -CHAR(%n)=A%n -TEXT=TXT -VARCHAR(%n)=VA%n -TIMESTAMP=TS -TIMESTAMP WITH TIME ZONE=TS -INTERVAL=DT -DATE=D -TIME=T -TIME WITH TIME ZONE=T -BOOL=BL -POINT=POINT -LINE=LINE -LSEG=SEGMENT -BOX=RECTANGLE -PATH=A248 -POLYGON=POLYGON -CIRCLE=CIRCLE -CIDR=A10 -INET=A10 -*=A10 -<Undefined> -DECIMAL -DECIMAL(%n) -DECIMAL(%s,%p) -FLOAT4 -FLOAT8 -INT2 -INT4 -INT8 -NUMERIC -NUMERIC(%n) -NUMERIC(%s,%p) -SERIAL -MONEY -CHAR -CHAR(%n) -TEXT -VARCHAR(%n) -TIMESTAMP -TIMESTAMP WITH TIME ZONE -INTERVAL -DATE -TIME -TIME WITH TIME ZONE -BOOL -POINT -LINE -LSEG -BOX -PATH -POLYGON -CIRCLE -CIDR -INET - - - -7743713B-3320-4EE4-9E87-3AFB729355D7 -PhysDttpSize -0 - -0 - -Table of storage sizes of target database data type - - -87D4DC23-7078-4D7D-94A4-B3210DA8CC3E -OdbcPhysDataType -0 - -0 - -Data types translation table from ODBC data types to target database data types. -bpchar(%n)=char(%n) -bpchar(%n) - - - - - - - -CDC2B06C-5AC1-4D4C-88AF-2C88898BDB26 -Profile -0 - -0 - -1 - - -815871BB-A068-41E2-BA52-205B50A0AD52 -Shared -Shared -0 - -0 - -1 - - -ExtendedAttributeTypeTargetItem -8995054D-72B6-4201-B86A-98DB680FB1BD -Extended Attribute Types -0 - -0 - -1 - - -94C33C46-EC2F-4743-8D37-18C5B6666938 -ProcLanguageList -0 - -0 - -Predefined list of language that the function is implemented in. (See also Extended Attribute ProcLanguage.) -1 -sql -sql -c -internal - - -9984F847-6977-4648-826F-78BC26C6C4CD -TemporaryState -0 - -0 - -This list stores all possible states for Temporary attribute. -Optionally, GLOBAL or LOCAL can be written before TEMPORARY or TEMP. This makes no difference in PostgreSQL. -1 -temp -temporary -local temp -local temporary -global temp -global temporary - - - - - - -1C98415B-16D6-425F-8303-C2FF48130BBE -PhysicalDomain -0 - -0 - -1 - - -StereotypeTargetItem -ADAC425E-EB22-4640-8A4F-D9CD71687A79 -Stereotypes -0 - -0 - -1 - - -0F50EE1C-651C-4508-B05E-AA4FB19D3130 -BaseType -0 - -0 - -1 - - -ExtendedAttributeTargetItem -7C56205A-F82D-4439-9C47-AA7E39B8C1B9 -Extended Attributes -0 - -0 - -1 - - -3954A652-B44B-4642-88E7-CE8099392324 -ExtTypeInput -0 - -0 - -The name of a function, created by CREATE FUNCTION, which converts data from its external form to the type's internal form. -1 -12 -0 - - -D596DD04-D85D-4A04-A7EC-349BC723BD2F -ExtTypeOutput -0 - -0 - -The name of a function, created by CREATE FUNCTION, which converts data from its internal form to a form suitable for display. -1 -12 -0 - - -B5F0DA75-D422-4AE5-97E2-7379ACA74F9D -ExtTypeLength -0 - -0 - -A literal value, which specifies the internal length of the new type. -1 -10 -0 - - -BE7B22DF-64C1-4C0B-B2EA-AF35795600FA -ExtTypeElement -0 - -0 - -The type being created is an array; this specifies the type of the array elements. -1 -12 -0 - - -DE189C84-B4B4-44F5-80D7-794AE7E5FAA8 -ExtTypeDelimiter -0 - -0 - -The delimiter character for the array. -1 -12 -0 - - -00878568-A073-4D54-A848-DD34908584D5 -ExtTypeSend -0 - -0 - -The name of a function, created by CREATE FUNCTION, which converts data of this type into a form suitable for transmission to another machine. -1 -12 -0 - - -A557019E-E970-424C-8772-69236830584E -ExtTypeReceive -0 - -0 - -The name of a function, created by CREATE FUNCTION, which converts data of this type from a form suitable for transmission from another machine to internal form. -1 -12 -0 - - -DA5B44D4-E590-4A53-828F-EC8963669BB2 -ExtTypePassedByValue -0 - -0 - -indicates that operators and functions which use this data type should be passed an argument by value rather than by reference. -1 -FALSE -0 - - - - -FormTargetItem -EBF9D66B-A877-4182-902E-FDD810E82BF1 -Forms -0 - -0 - -1 - - -1402B726-1615-42A2-9D4D-DFD6811689A2 -PostgreSQL -<Form > - <ExtendedAttribute Attribute="ExtTypeLength" AttributeID="{B5F0DA75-D422-4AE5-97E2-7379ACA74F9D}" Name="ExtTypeLength" Caption="Length" GrayHiddenAttributes="No" /> - <ExtendedAttribute Attribute="ExtTypeElement" AttributeID="{BE7B22DF-64C1-4C0B-B2EA-AF35795600FA}" Name="ExtTypeElement" Caption="Array Element type" GrayHiddenAttributes="No" /> - <ExtendedAttribute Attribute="ExtTypeDelimiter" AttributeID="{DE189C84-B4B4-44F5-80D7-794AE7E5FAA8}" Name="ExtTypeDelimiter" Caption="Array delimiter" GrayHiddenAttributes="No" /> - <ExtendedAttribute Attribute="ExtTypePassedByValue" AttributeID="{DA5B44D4-E590-4A53-828F-EC8963669BB2}" Name="ExtTypePassedByValue" Caption="By Value" GrayHiddenAttributes="No" /> - <Separator Name="Separator1" /> - <ExtendedAttribute Attribute="ExtTypeInput" AttributeID="{3954A652-B44B-4642-88E7-CE8099392324}" Name="ExtTypeInput" Caption="Input function" GrayHiddenAttributes="No" /> - <ExtendedAttribute Attribute="ExtTypeOutput" AttributeID="{D596DD04-D85D-4A04-A7EC-349BC723BD2F}" Name="ExtTypeOutput" Caption="Output function" GrayHiddenAttributes="No" /> - <ExtendedAttribute Attribute="ExtTypeSend" AttributeID="{00878568-A073-4D54-A848-DD34908584D5}" Name="ExtTypeSend" Caption="Send function" GrayHiddenAttributes="No" /> - <ExtendedAttribute Attribute="ExtTypeReceive" AttributeID="{A557019E-E970-424C-8772-69236830584E}" Name="ExtTypeReceive" Caption="Receive function" GrayHiddenAttributes="No" /> -</Form> - -0 - -1276524800 -obalen -1 -24912 - - - - - - -6DAAC25D-4FAB-425C-A545-5C5DD08DCFFF -CompositeType -0 - -0 - -1 - - -ExtendedAttributeTargetItem -4F0F83D1-D45D-4A84-9904-35BB9E96F981 -Extended Attributes -0 - -0 - -1 - - -F09B1253-9E0E-48DD-94C5-7EB645E424C9 -CompositeDefinition -0 - -0 - -The composite type is specified by a list of attribute names and data types. This is essentially the same as the row type of a table, but using CREATE TYPE avoids the need to create an actual table when all that is wanted is to define a type. A stand-alone composite type is useful as the argument or return type of a function. -1 -12 -0 - - - - -FormTargetItem -EF4CD3F3-26DA-4F9D-8318-D1BBA1AFE4EB -Forms -0 - -0 - -1 - - -166747AD-2DF7-46FB-A33D-1DF60DFCFADB -PostgreSQL -<Form > - <ExtendedAttribute Attribute="CompositeDefinition" AttributeID="{F09B1253-9E0E-48DD-94C5-7EB645E424C9}" Name="Definition" Caption="Definition" GrayHiddenAttributes="No" /> -</Form> - -0 - -1276524800 -obalen -1 -24912 - - - - - - - - - - -CCF5FE55-2BCB-4591-A512-B232444428CF -Procedure -0 - -0 - -1 - - -ExtendedAttributeTargetItem -08020C9D-AE04-4063-ADDF-3BB6FB1FF63D -Extended Attributes -0 - -0 - -1 - - -09E77E7F-0834-4FFF-A0EB-3FE1A3FA16B1 -ProcLanguage -0 - -0 - -The name of the language that the function is implemented in. May be SQL, C, internal, or the -name of a user-defined procedural language. (See also extended attribute type ProcLanguageList.) -1 --1 -sql -0 - - - - - - - -FormTargetItem -0BA45BFD-1CF5-4D99-B6FB-AB6E8B510D0F -Forms -0 - -0 - -1 - - -632C0A96-5390-4ABF-B891-B723954AFA3D -PostgreSQL -<Form > - <GroupBox Name="GroupBox1" Caption="Language" ControlAsLabel="No" > - <StaticText Name="StaticText1" Caption="Choose here the name of the language that the function is implemented in." /> - <StaticText Name="StaticText3" Caption="May be SQL, C, internal, or the name of a user-defined procedural language." /> - <ExtendedAttribute Name="ProcLanguage" Caption="Language" Attribute="ProcLanguage" AttributeID="{09E77E7F-0834-4FFF-A0EB-3FE1A3FA16B1}" GrayHiddenAttributes="Yes" /> - </GroupBox> -</Form> - -0 - -1276524800 -obalen -1 -24912 - - - - - - -D6A943D2-2621-4BAD-8B30-F90E7FC2FB95 -Tablespace -0 - -0 - -1 - - -ExtendedAttributeTargetItem -8BA82F30-4EA2-4A6D-B7B9-D5EDE8FBA4C6 -Extended Attributes -0 - -0 - -1 - - -6A4623D4-04FD-49E4-BBB2-26B246E58EAF -TbspLocation -0 - -0 - -The directory that will be used for the tablespace. The directory must be specified by an absolute path name. -1 -12 -0 - - -8B7D44B4-EC82-41B9-9443-CC961DEE9E81 -TbspOwner -0 - -0 - -The name of the user who will own the tablespace. If omitted, defaults to the user executing the command. Only superusers may create tablespaces, but they can assign ownership of tablespaces to non-superusers. -1 -12 -0 - - - - -FormTargetItem -8C308551-D020-4376-A081-F598F6B78A23 -Forms -0 - -0 - -1 - - -83A3A92E-36CE-46F3-A9F6-F04DBFBAC1C4 -PostgreSQL -<Form > - <ExtendedAttribute Attribute="TbspLocation" AttributeID="{6A4623D4-04FD-49E4-BBB2-26B246E58EAF}" Name="TbspLocation" Caption="Location" GrayHiddenAttributes="No" /> - <ExtendedAttribute Attribute="TbspOwner" AttributeID="{8B7D44B4-EC82-41B9-9443-CC961DEE9E81}" Name="TbspOwner" Caption="Owner" GrayHiddenAttributes="No" /> -</Form> - -0 - -1276524800 -obalen -1 -24912 - - - - -CustomCheckTargetItem -A643B629-0C06-4935-97B2-BECCC51FA9D4 -Custom Checks -0 - -0 - -1 - - -4447EEA6-F62F-416E-9A45-E707E0A8A3EF -Mandatory Tablespace Location -0 - -0 - -1 -This check ensures that the Location extended attribute is set for the tablespace -The following tablespaces have no location defined -Function %Check%(obj) - ' Check that the extended attribute "TbspLocation" is filled. - ' The contents of the attribute is not controlled. - %Check% = (obj.GetExtendedAttribute("TbspLocation") <> "") -End Function -Function %Fix%(obj, outmsg) - ' Implement your automatic correction on <obj> here - ' filling <outmsg> as you wish - ' and return True if successful. - - outmsg = "Automatic correction not implemented" - - %Fix% = False -End Function - - - - - - -C5C915C9-3817-4DDB-95E2-5CCBD01C091C -IndexColumn -0 - -0 - -1 - - -CustomCheckTargetItem -6B0A0403-6F86-4876-9CB0-9AED66E3CA2E -Custom Checks -0 - -0 - -1 - - -8422D3FA-4033-45AA-95A4-F0DDA1A40A64 -Index expression enclosed in parenthesis -0 - -0 - -1 -This check ensures that index column expression are always enclosed in parenthesis -The following indexes column have an invalid expression: -Function %Check%(obj) - if (obj.Expression <> "") then - %Check% = (left(obj.Expression, 1) & right(obj.Expression, 1) = "()") - else - %Check% = True - end if -End Function -Function %Fix%(obj, outmsg) - obj.Expression = "( " & obj.Expression & " )" - outmsg = "Parenthesis have been added around column index expression" - - %Fix% = True -End Function -1 -1 - - - - - - -ACD56323-30AB-4823-B61B-03AD25AF177B -Table -0 - -0 - -1 - - -ExtendedAttributeTargetItem -7FB938DF-E6B5-4DFA-A865-A7175805E4D6 -Extended Attributes -0 - -0 - -1 - - -04B573C8-8062-4FFC-8C07-AA34F76050C2 -Temporary -0 - -0 - -If specified, the table is created as a temporary table. Temporary tables are automatically dropped at the end of a session, or optionally at the end of the current transaction -1 --1 -0 - - - - - - - -FormTargetItem -033A27B6-2884-45DD-83BC-65E79E967C47 -Forms -0 - -0 - -1 - - -4E49596A-0533-4E72-8AE6-1315208D0BBF -PostgreSQL -<Form > - <ExtendedAttribute Name="Temporary" Caption="Temporary state" Attribute="Temporary" AttributeID="{04B573C8-8062-4FFC-8C07-AA34F76050C2}" GrayHiddenAttributes="Yes" /> -</Form> - -0 - -1276524800 -obalen -1 -24912 - - - - -MethodTargetItem -B74022BD-ECA7-4557-B8D0-8713FD87874B -Methods -0 - -0 - -1 - - -3C7A869B-607A-433B-859A-748E99F6EAEE -SetPublicSchema -0 - -1244729286 -mledier -1 -Sub %Method%(obj) - Dim pub - for each pub in obj.Model.Users - if (LCase(pub.Code) = "public") then Exit For - next - if Not(isObject(pub)) then - Set pub = obj.Model.Users.CreateNew() - pub.SetExtendedAttribute "%CurrentTargetCode%.Schema", True - pub.SetNameAndCode "public", "public", False - end if - Set obj.Owner = pub -End Sub - - - - - - -269A9BBC-165B-467F-ABC9-DD53C67FC88F -Reference -0 - -0 - -1 - - -ExtendedAttributeTargetItem -FB33CA15-0879-43BA-8B1F-545AD3EAFDFA -Extended Attributes -0 - -0 - -1 - - -6D3919F1-F3EA-49D8-8E50-7AC8380DC462 -Deferrable -0 - -0 - -This controls whether the constraint can be deferred. A constraint that is not deferrable will be checked immediately after every command. Checking of constraints that are deferrable may be postponed until the end of the transaction. -Only foreign key constraints currently accept this clause. All other constraint types are not deferrable. -1 -false -0 - - -E93DB726-E4A4-46A4-9219-548CAA66F2A0 -ForeignKeyConstraintDeferred -0 - -0 - -If a constraint is deferrable, this clause specifies the default time to check the constraint. -False means the constraint is INITIALLY IMMEDIATE, it is checked after each statement. This is the default. -True means the constraint is INITIALLY DEFERRED, it is checked only at the end of the transaction. -1 -false -0 - - - - -FormTargetItem -E4F188B9-21A4-4DC5-B9D0-1298AAC67E48 -Forms -0 - -0 - -1 - - -86AF89FA-E313-4725-AE1E-08C0AAFEE0BB -PostgreSQL -<Form > - <ExtendedAttribute Name="Deferrable" Caption="Deferrable" Attribute="Deferrable" AttributeID="{6D3919F1-F3EA-49D8-8E50-7AC8380DC462}" GrayHiddenAttributes="Yes" /> - <ExtendedAttribute Name="ForeignKeyConstraintDeferred" Caption="Foreign key constraint deferred" Attribute="ForeignKeyConstraintDeferred" AttributeID="{E93DB726-E4A4-46A4-9219-548CAA66F2A0}" GrayHiddenAttributes="Yes" /> -</Form> - -0 - -1276524800 -obalen -1 -24912 - - - - - - -7F47B72E-AE21-435D-83AC-1FCFD007BBEB -Database -0 - -0 - -1 - - -ExtendedAttributeTargetItem -8AB877B5-6E4E-424A-A234-6DD9DC4D6EB4 -Extended Attributes -0 - -0 - -1 - - -406ADDC6-D29B-4555-A539-15BC5B712A26 -Template -0 - -0 - -The name of the template from which to create the new database, or DEFAULT to use the default template -1 -12 -0 - - -5B507946-57A3-42B3-BEBF-00E549563D32 -Encoding -0 - -0 - -Character set encoding to use in the new database. Specify a string constant (e.g., 'SQL_ASCII'), or an integer encoding number, or DEFAULT to use the default encoding -1 -12 -0 - - - - -FormTargetItem -9EAAD069-88CB-4CAC-8765-F86B5891DCBA -Forms -0 - -0 - -1 - - -81D28ABD-0696-418A-9A37-43CC32CDE7DC -PostgreSQL -<Form > - <ExtendedAttribute Name="Template" Caption="Template" Attribute="Template" AttributeID="{406ADDC6-D29B-4555-A539-15BC5B712A26}" GrayHiddenAttributes="Yes" /> - <ExtendedAttribute Name="Encoding" Caption="Encoding" Attribute="Encoding" AttributeID="{5B507946-57A3-42B3-BEBF-00E549563D32}" GrayHiddenAttributes="Yes" /> -</Form> - -0 - -1276524800 -obalen -1 -24912 - - - - - - -16AB5309-2509-4C3E-AE19-BB18CC382404 -Group -0 - -0 - -1 - - -ExtendedAttributeTargetItem -96DD5873-228F-460D-9602-0A3CF94FABF1 -Extended Attributes -0 - -0 - -1 - - -E6FC2DB5-17D5-4FD1-B8DE-A0ED011588DE -SysId -0 - -0 - -The SYSID clause can be used to choose the PostgreSQL group ID of the new group. This is normally not necessary, but may be useful if you need to recreate a group referenced in the permissions of some object. -1 -10 -0 - - - - -FormTargetItem -8BB37684-F2F8-4866-8F43-5ABC9B681689 -Forms -0 - -0 - -1 - - -FE43BCEE-E961-4DFE-B597-7C5B3D57F55A -PostgreSQL -<Form > - <ExtendedAttribute Attribute="SysId" AttributeID="{E6FC2DB5-17D5-4FD1-B8DE-A0ED011588DE}" Name="SysId" Caption="Group identifier (id)" GrayHiddenAttributes="Yes" /> -</Form> - -0 - -1276524800 -obalen -1 -24912 - - - - - - -EEA405CC-F187-4546-909F-60D33599A4DF -User -0 - -0 - -1 - - -ExtendedAttributeTargetItem -CC851CCE-CF77-4A16-9215-E9408BE04646 -Extended Attributes -0 - -1246366865 -mledier -1 - - -947DD0F5-B5A2-4BF9-BA50-736D57126F7B -Schema -0 - -0 - -Indicates if selected user is a schema or not. -1 -false -Is schema -0 - - -FAFA5191-4B2E-4087-BCE3-6EF3058B19BA -CanCreate -1246366826 -mledier -1246367214 -mledier -If user can be created in database. -1 -1 -RO -Function %Get%(obj) - %Get% = (LCase(obj.Code) <> "public") -End Function -Can create -0 - - - - -FormTargetItem -A68579A4-53A6-4AEE-81DB-6DF3BC0C6E9C -Forms -0 - -1246366881 -mledier -1 - - -A1FB5438-65BB-48B7-A086-FF0BDB9FF81D -General -GENERAL -<Form > - <StandardNameAndCode Attribute="NameAndCode" SizeInChars="1" HorizontalResize="true" /> - <StandardAttribute Attribute="Comment" Caption="Comment:" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" LeftText="false" /> - <StandardAttribute Attribute="Stereotype" Caption="Stereotype:" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" LeftText="false" /> - <StandardAttribute Attribute="Password" Caption="Password:" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" LeftText="false" /> - <HorizontalLayout Name="HorizontalLayout1" > - <ExtendedAttribute Attribute="Schema" Caption="Is schema" AttributeID="{947DD0F5-B5A2-4BF9-BA50-736D57126F7B}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" Display="Checkbox" LeftText="Yes" /> - <ExtendedAttribute Attribute="Owner" Caption="Owner" AttributeID="{3358B5FA-08C0-4DFB-869C-FEEFEB1C49BB}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - </HorizontalLayout> <StandardAttribute Attribute="KeywordList" Caption="Keywords" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> -</Form> - -0 - -1276524800 -obalen -1 -GENERAL -24912 - - -8E605CC9-9153-4D30-96C1-0B0D713729C0 -Hidden -<Form > - <ExtendedAttribute Attribute="CanCreate" Caption="Can create" AttributeID="{FAFA5191-4B2E-4087-BCE3-6EF3058B19BA}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> -</Form> - -1246366865 -mledier -1257761859 -mledier -1 -Dialog - - - - -CriterionTargetItem -B186F72A-EBB8-4762-AEE6-A159D0BD2065 -Criteria -0 - -0 - -1 - - -066ABFE6-7605-44F9-9247-10E90A158571 -isSchema -(%Schema%) -0 - -0 - -1 - - -ExtendedAttributeTargetItem -F855AC43-4986-451F-AD18-2B3151BF9597 -Extended Attributes -0 - -0 - -1 - - -3358B5FA-08C0-4DFB-869C-FEEFEB1C49BB -Owner -0 - -0 - -Owner of the schema -1 -15 -4BA9F642-DAB1-11D1-9944-006097355D9B -Owner -0 - - - - - - - -ExtendedCollectionTargetItem -AC0619DB-1C44-4A62-9746-D9EE1B446AD7 -Extended Collections -0 - -0 - -1 - - -4E7637E8-52C0-48F5-9218-E17C22025290 -Owner -0 - -0 - -1 -Schemas -4BA9F642-DAB1-11D1-9944-006097355D9B - - - - - - -60BBFAB7-C4BD-449F-AA31-C2ABF4D22667 -isUser -(%Schema%==false) -0 - -0 - -1 - - -FormTargetItem -D36A8DBE-B11A-4D49-B6AB-1081192A5722 -Forms -0 - -0 - -1 - - -002FACA9-83F6-4E6C-B3A7-5E79A15E210F -PostgreSQL -<Form > - <ExtendedAttribute Attribute="SysId" Caption="User identifier (id)" AttributeID="{8F48507A-7C0D-4822-A566-2D7767715205}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" LeftText="false" /> - <GroupBox Name="GroupBox2" Caption="Special permission" ControlAsLabel="No" > - <HorizontalLayout Name="HorizontalLayout1" Indentation="0" > - <ExtendedAttribute Attribute="CreateDB" Caption="Create database" AttributeID="{8A4EBE2D-8EDB-438E-8160-E66B06BDED64}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="CreateUser" Caption="Create user" AttributeID="{664FD4CF-0D90-4BA0-B7EF-A21C2C16B995}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" LeftText="false" /> - </HorizontalLayout> - </GroupBox> - <GroupBox Name="GroupBox1" Caption="Security" ControlAsLabel="No" > - <ExtendedAttribute Attribute="Validity" Caption="Validity" AttributeID="{E21C45EE-516B-4540-9EC1-B88EFE86A7C9}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="EncryptedPassword" Caption="Encrypted password" AttributeID="{89E80C1A-9E8C-4BD5-BCA4-8FCE7EB7D6D7}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" LeftText="false" /> - </GroupBox> -</Form> - -0 - -0 - -1 - - - - -ExtendedAttributeTargetItem -5D39E586-E369-4646-BA41-950BF52CEE33 -Extended Attributes -0 - -0 - -1 - - -8A4EBE2D-8EDB-438E-8160-E66B06BDED64 -CreateDB -0 - -0 - -Defines a user's ability to create databases. -If TRUE, the user is allowed to create databases. -1 -false -0 - - -664FD4CF-0D90-4BA0-B7EF-A21C2C16B995 -CreateUser -0 - -0 - -If TRUE, the user is allowed to create new users. -This option also turns the user into a superuser who can override all access restrictions. -1 -false -0 - - -89E80C1A-9E8C-4BD5-BCA4-8FCE7EB7D6D7 -EncryptedPassword -0 - -0 - -Control whether the password is stored encrypted in the system catalogs. -1 -true -0 - - -8F48507A-7C0D-4822-A566-2D7767715205 -SysId -0 - -0 - -The SYSID clause can be used to choose the PostgreSQL user ID of the new user. This is normally not necessary, but may be useful if you need to recreate the owner of an orphaned object. -1 -10 -0 - - -E21C45EE-516B-4540-9EC1-B88EFE86A7C9 -Validity -0 - -0 - -This clause sets an absolute time after which the user's password is no longer valid. If this clause is omitted the password will be valid for all time. -1 -2 -0 - - - - - - - - - - -62B3BA47-41C9-42A1-B298-23FF4E128490 -Model -0 - -0 - -1 - - -EventHandlerTargetItem -128243FB-D24B-4635-B002-E7705C4500CF -Event Handlers -0 - -0 - -1 - - -CEE1267A-8981-4459-9BA1-2D512C7DB04D -AfterDatabaseReverseEngineer -0 - -0 - -1 -Function %AfterDatabaseReverseEngineer%(model) - Dim tab - For each tab in model.Tables - if (tab.Owner is Nothing) then - tab.ExecuteCustomMethod("%CurrentTargetCode%.SetPublicSchema") - end if - Next - %AfterDatabaseReverseEngineer% = True -End Function - - - - - - - - -F45EAD0C-A5E6-4DA5-BF2C-D37E197C36B6 -Odbc -0 - -0 - -DBMS characteristics, command definition, and data type translations for the ODBC generation and reverse engineering - - -93397FEC-B239-43E0-808E-6C37FE1D55D9 -Objects -0 - -0 - -Contains sub-categories for each type of object in the database, for example: Table, or Reference. Each sub-category contains entries whose values define database commands and object-related characteristics - - -B561F06F-8EA2-4923-9026-BA6D50FCC935 -Qualifier -0 - -0 - -Manages the use of qualifier in ODBC reverse engineering - - -D3ECF29D-3212-414B-B983-755D3A28E865 -Enable -0 - -0 - -Allows using the qualifier combo box during ODBC reverse engineering - - -6DBE6644-748C-411E-8D24-C9762EF859BA -SqlListQuery -0 - -0 - -Query to retrieve qualifier during ODBC reverse engineering - - -69A5288F-87C6-4041-A68A-E9DDCC5629A4 -Label -0 - -0 - -Label for <All> in qualifier selection list -All catalogs - - - - - - - - - - - \ No newline at end of file diff --git a/template/Pd/Resource Files/DBMS/sqlsv2012.xdb b/template/Pd/Resource Files/DBMS/sqlsv2012.xdb deleted file mode 100644 index 565795d74..000000000 --- a/template/Pd/Resource Files/DBMS/sqlsv2012.xdb +++ /dev/null @@ -1,28154 +0,0 @@ - - - - - - - -11D14009-FEF7-43A3-BC76-BE5B34F11D08 -Microsoft SQL Server 2012 -MSSQLSRV2012 -0 - -1341932191 -mledier -SQL SERVER;MICROSOFT -'****************************************************************************** -'* Purpose: This VB-Script holds global definitions shared by all the custom- -'* checks scripts of the model extension. -'****************************************************************************** - -Option Explicit ' This is to ensure all used variables are defined - -Const ADODotNetTarget = "%CurrentTargetCode%" -function GetDatatypeName(sDatatype) - if (instr(sDatatype, "(") > 0) then - GetDatatypeName = lcase(rtrim(left(sDatatype, instr(sDatatype, "(") - 1))) - else - GetDatatypeName = lCase(sDatatype) - end if -end function - -Function CanSetNameCode(pObj, sName, sCode) - CanSetNameCode = (pObj.CanSetAttribute("Name", sName, "") and pObj.CanSetAttribute("Code", sCode, "")) -End Function - -Function C2Bool(v) - if (IsObject(v)) then - C2Bool = not(v is Nothing) - elseif (CStr(v) = "") then - C2Bool = False - else - C2Bool = CBool(v) - end if -end Function - -Function FindExtObject(pModel, sCode, sStereo) - dim pObj - for each pObj in pModel.ExtendedObjects - if (LCase(pObj.Stereotype) = LCase(sStereo)) then - if (pObj.Code = sCode) then - Set FindExtObject = pObj - Exit Function - end if - if (pObj.Name = sCode) then - Set FindExtObject = pObj - Exit Function - end if - end if - next - Set FindExtObject = Nothing - -End Function - - - -41C1218E-699D-4200-9F45-3FAA15814312 -Assembly -1152536409 -xwang -1152536415 -xwang -A95D024F-FCA6-4E17-AEAA-BD9E6855BCEE -4 -D:\PDDEV\icons\PD Icons\Icons\Assembly.ico -0D0000001000000010000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00FC0000FFF8000001F8000001E0000001C00000018000 -0001800000018000000180000001800000018000000180000001800000018000 -000180000001800000018000000180000001C0000001E0000001FC000000FC00 -0000FE000000FF000000FF000000FF000000FF000001FF000003FF000007FF00 -000FFF00001FFFFFF03F424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000DE993D01DE993D03 -DE993D06DE993D09DE993D0ADE993D0ADE993D0ADE993D0ADE993D0ADE993D0A -DE993D0ADE993D0ADE993D0ADE993D0ADE993D08DE993D05DE993D02DE993D01 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000DE993D01DE993D03DE993D0B -DE993DF0DE993DFFDE993DFFDE993DFFDE993DFFDE993DFFDE993DFFDE993DFF -DE993DFFDE993DFFDE993DFFDE993DFFDE993DFFDE993DFFDE993DFFDE993DFF -DE993DFFDE993DFFDE993DFFDE993DFFDE993DFFDE993DFFDE993DEF00000000 -0000000000000000000000000000000000000000DE993D01DE993D06DE993D16 -DE993DFFF5D5A2FFF3D19BFFF3D19AFFF3D19AFFF3D19AFFF3D19AFFF3D19AFF -F3D19AFFF3D19BFFF3D19BFFF3D29CFFF4D6A2FFF8DEAEFFFBE6BEFFFDEECAFF -FEF1CFFFFFF2D1FFFFF3D2FFFFF3D2FFFFF3D3FFFFF4D3FFDE993DFF00000000 -000000000000000000000000936C6F5F936C6FCF9A7375FF9A7375FF9A7375FF -9A7375FF9A7375FF9A7375FF9A7375FF9A7375FF9A7375FF9A7375FF9A7375FF -9A7375FF9A7375FF9A7375FF9A7375FFA47B73FFCFA484FFF7D0A1FFFBD9B1FF -FDDFBCFFFEE3C1FFFFE5C3FFFFE5C4FFFFE6C5FFFFF3D3FFDE993DFF00000000 -0000000000000000936C6F9F9D7477FFB68687FFB78484FFB48181FFB48181FF -B48181FFB48181FFB48181FFB48181FFB48181FFB48181FFB48181FFB48181FF -B48181FFB48181FFB48181FFB78484FFB68687FF9D7477FFB68D7BFFF7CFA0FF -FBD9B1FFFDE0BCFFFEE3C2FFFFE4C3FFFFE5C4FFFFF3D2FFDE993DFF00000000 -00000000936C6F5F9E7577FFBA8888FFA97676FFA26F6FFFA26F6FFFA26F6FFF -A26F6FFFA26F6FFFA26F6FFFA26F6FFFA26F6FFFA26F6FFFA26F6FFFA26F6FFF -A26F6FFFA26F6FFFA26F6FFFA26F6FFFA97676FFBA8888FF9E7577FFCFA483FF -F8D1A3FFFCDCB6FFFEE0BEFFFFE3C1FFFFE4C3FFFFF2D2FFDE993DFF00000000 -00000000936C6FCFB88989FFAC7979FFA57272FFA57272FFA57272FFA57272FF -A57272FFA57272FFA57272FFA57272FFA57272FFA57272FFA57272FFA57272FF -A57272FFA57272FFA57272FFA57272FFA57272FFAC7979FFB88989FFA47B73FF -F5CA97FFFBD7AEFFFEDFBCFFFFE1C0FFFFE3C2FFFFF1D1FFDE993DFF00000000 -000000009A7375FFBD8B8BFFA97676FFA97676FFA97676FFA97676FFA97676FF -A97676FFA97676FFA97676FFA97676FFA97676FFA97676FFA97676FFA97676FF -A97676FFA97676FFA97676FFA97676FFA97676FFA97676FFBD8B8BFF9A7375FF -F3C590FFFAD4AAFFFEDDBAFFFFE0BFFFFFE2C1FFFFF1D0FFDE993DFF00000000 -000000009A7375FFBE8B8BFFAC7979FFAC7979FFAC7979FFAC7979FFAC7979FF -AC7979FFAC7979FFAC7979FFAC7979FFAC7979FFAC7979FFAC7979FFAC7979FF -AC7979FFAC7979FFAC7979FFAC7979FFAC7979FFAC7979FFBE8B8BFF9A7375FF -F3C48FFFFAD3A8FFFDDCB9FFFFDFBEFFFFE1C0FFFFF0D0FFDE993DFF00000000 -000000009A7375FFC08D8DFFAF7C7CFFAF7C7CFFAF7C7CFFAF7C7CFFAF7C7CFF -AF7C7CFFAF7C7CFFAC7979FFAC7979FFAC7979FFAC7979FFAC7979FFAC7979FF -AC7979FFAF7C7CFFAF7C7CFFAF7C7CFFAF7C7CFFAF7C7CFFC08D8DFF9A7375FF -F3C590FFFAD3A8FFFDDBB8FFFFDEBDFFFFE0BFFFFFF0CFFFDE993DFF00000000 -000000009A7375FFC28F8FFFB27F7FFFB27F7FFFB27F7FFFB27F7FFFB27F7FFF -B27F7FFFB27F7FFFB27F7FFFB27F7FFFB27F7FFFB27F7FFFB27F7FFFB27F7FFF -B27F7FFFB27F7FFFB27F7FFFB27F7FFFB27F7FFFB27F7FFFC28F8FFF9A7375FF -F3C792FFFAD3A9FFFDDBB9FFFFDDBCFFFFDFBEFFFFEFCFFFDE993DFF00000000 -000000009A7375FFC59292FFB58282FFB58282FFB58282FFB58282FFB58282FF -B58282FFB58282FFB58282FFB58282FFB58282FFB58282FFB58282FFB58282FF -B58282FFB58282FFB58282FFB58282FFB58282FFB58282FFC59292FF9A7375FF -F3C793FFFAD4ABFFFDDCBBFFFFDDBDFFFFDEBDFFFFEFCEFFDE993DFF00000000 -000000009A7375FFC89494FFBC8C8CFFBE9090FFBF9191FFBF9191FFBF9191FF -BF9191FFBF9191FFBF9191FFBF9191FFBF9191FFBF9191FFBF9191FFBF9191FF -BF9191FFBF9191FFBF9191FFBF9191FFBF9191FFBF9191FFC89494FF9A7375FF -F3C894FFFAD5ADFFFDDDBCFFFFDEBFFFFFDEBEFFFFEFCEFFDE993DFF00000000 -000000009A7375FFCF9F9FFFC59A9AFFC59A9AFFC59A9AFFC59A9AFFC59A9AFF -C59A9AFFC59A9AFFC59A9AFFC59A9AFFC59A9AFFC59A9AFFC59A9AFFC59A9AFF -C59A9AFFC59A9AFFC59A9AFFC59A9AFFC59A9AFFC39696FFCB9898FF9A7375FF -F3C895FFFAD6AEFFFDDEBDFFFFDFC0FFFFDFC0FFFFEFCEFFDE993DFF00000000 -000000009A7375FFD3A4A4FFCDA3A3FFCDA3A3FFCDA3A3FFCDA3A3FFCDA3A3FF -CDA3A3FFCDA3A3FFCDA3A3FFCDA3A3FFCDA3A3FFCDA3A3FFCDA3A3FFCDA3A3FF -CDA3A3FFCDA3A3FFCDA3A3FFCDA3A3FFCDA3A3FFCDA3A3FFD3A4A4FF9A7375FF -F3C997FFFAD7B1FFFDDFBFFFFFE0C2FFFFE0C1FFFFEFCFFFDE993DFF00000000 -000000009A7375FFD4A4A4FFD3ACACFFD3ACACFFD3ACACFFD3ACACFFD3ACACFF -D3ACACFFD3ACACFFD3ACACFFD3ACACFFD3ACACFFD3ACACFFD3ACACFFD3ACACFF -D3ACACFFD3ACACFFD3ACACFFD3ACACFFD3ACACFFD3ACACFFD4A6A6FF9A7375FF -F4CB9AFFFAD8B3FFFEE0C1FFFFE1C4FFFFE1C3FFFFEFD0FFDE993DFF00000000 -00000000936C6FCFC79797FFD9B3B3FFD8B4B4FFD8B4B4FFD8B4B4FFD8B4B4FF -D8B4B4FFD8B4B4FFD8B4B4FFD8B4B4FFD8B4B4FFD8B4B4FFD8B4B4FFD8B4B4FF -D8B4B4FFD8B4B4FFD8B4B4FFD8B4B4FFD8B4B4FFD9B3B3FFC79797FFA47C75FF -F6D0A4FFFBDBB9FFFEE1C4FFFFE2C5FFFFE1C4FFFFF0D1FFDE993DFF00000000 -00000000936C6F5FA1787AFFD8A8A8FFDDBABAFFDDBCBCFFDDBCBCFFDDBCBCFF -DDBCBCFFDDBCBCFFDDBCBCFFDDBCBCFFDDBCBCFFDDBCBCFFDDBCBCFFDDBCBCFF -DDBCBCFFDDBCBCFFDDBCBCFFDDBCBCFFDEBCBCFFD8A8A8FFA1787AFFD0A88CFF -F9D8B2FFFDE0C2FFFEE3C9FFFFE3C8FFFFE2C7FFFFF0D1FFDE993DFF00000000 -0000000000000000936C6F9FA1787BFFCD9D9EFFDFB3B3FFE0B6B6FFE0B6B6FF -E0B6B6FFE0B6B6FFE0B6B6FFE0B6B6FFE0B6B6FFE0B6B6FFE0B6B6FFE0B6B6FF -E0B6B6FFE0B6B6FFE0B6B6FFE0B5B5FFCD9D9EFFA1787BFFB68F80FFF8D6AFFF -FCDEBFFFFEE4C9FFFFE4CAFFFFE4C9FFFFE3C8FFFFF1D2FFDE993DFF00000000 -000000000000000000000000936C6F5F936C6FCF9A7375FF9A7375FF9A7375FF -9A7375FF9A7375FF9A7375FF9A7375FF9A7375FF9A7375FF9A7375FF9A7375FF -9A7375FF9A7375FF9A7375FF9A7375FFA47D76FFCFA88CFFF6D4AAFFF9DCBAFF -FCE2C5FFFDE3C9FFFEE4C9FFFEE2C8FFFEE2C7FFFFF0D1FFDE993DFF00000000 -000000000000000000000000000000000000000000000000DE993D01DE993D07 -DE993DFFF9E8C4FFF7DDBDFFF6DBBAFFF6DBB9FFF6DBB8FFF6DAB8FFF6D9B7FF -F6D9B6FFF6D9B5FFF6D8B3FFF5D7B1FFF6D6AFFFF6D5AEFFF7D6B0FFF9DAB6FF -F9DCBAFFFBDDBDFFFBDEBFFFFCDFBFFFFDDFC1FFFEEECEFFDE993DFFDE993D01 -000000000000000000000000000000000000000000000000DE993D01DE993D03 -DE993DFFFDF3D7FFFCECDAFFFCEBD7FFFCEAD7FFFCEAD6FFFCE9D5FFFCE8D4FF -FCE8D2FFFCE7D1FFFCE6CFFFFBE4CCFFFAE1C4FFF7D9B6FFF5D2A8FFF3CFA3FF -F4D0A3FFF5D1A6FFF6D3A9FFF7D5ACFFF9D7B1FFFBE8C3FFDE993DFFDE993D03 -00000000000000000000000000000000000000000000000000000000DE993D01 -DE993DFFFEF8E2FFFEF3E8FFFEF3E7FFFEF2E6FFFEF2E5FFFEF1E4FFFEF0E3FF -FEF0E1FFFEEFDFFFFEEEDDFFFDECD9FFFBE6D0FFF7DCBAFFF3CEA1FFF1C894FF -F0C591FFF1C592FFF1C794FFF2C996FFF5CE9FFFF8E0B5FFDE993DFFDE993D05 -0000000000000000000000000000000000000000000000000000000000000000 -DE993DFFFFFBE5FFFFF6EDFFFFF6EDFFFFF5ECFFFFF5EBFFFFF4E9FFFFF3E7FF -FFF2E6FFFFF2E4FFFFF1E2FFFEEFDEFFFDE9D4FFF9DDBDFFF3CEA1FFFFFFDEFF -FFFFDEFFFFFFDEFFFFFFDEFFFFFFDEFFFFFFDEFFFFFFDEFFDE993DFFDE993D05 -0000000000000000000000000000000000000000000000000000000000000000 -DE993DFFFFFCE7FFFFF7F1FFFFF7F0FFFFF7EFFFFFF6EDFFFFF5ECFFFFF4EAFF -FFF3E8FFFFF2E6FFFFF1E4FFFFEFE1FFFDEBD8FFF9E0C3FFF3D0A5FFFFFFDEFF -F0CE93FFF6DBABFFF6D6A5FFF6D0A0FFF4C996FFE09D45FFDE993D95DE993D03 -0000000000000000000000000000000000000000000000000000000000000000 -DE993DFFFFFCE8FFFFF9F3FFFFF8F2FFFFF8F1FFFFF7EFFFFFF6EEFFFFF5ECFF -FFF4EAFFFFF3E8FFFFF2E6FFFFF0E2FFFEEDDBFFFAE2C8FFF4D2A9FFFFFFDEFF -F6DBABFFFFEBCAFFFFE3C2FFFCDBB6FFE4A655FFDE993DA6DE993D06DE993D01 -0000000000000000000000000000000000000000000000000000000000000000 -DE993DFFFFFDE9FFFFFAF5FFFFF9F4FFFFF9F3FFFFF8F1FFFFF7EFFFFFF6EDFF -FFF5EBFFFFF3E9FFFFF2E6FFFFF1E3FFFEEDDDFFFBE4CBFFF5D5ADFFFFFFDEFF -F6D6A5FFFFE3C2FFFCDBB6FFE4A655FFDE993DA6DE993D06DE993D0200000000 -0000000000000000000000000000000000000000000000000000000000000000 -DE993DFFFFFDEBFFFFFBF8FFFFFAF6FFFFF9F5FFFFF8F3FFFFF7F0FFFFF6EEFF -FFF5ECFFFFF4EAFFFFF3E7FFFFF1E4FFFEEEDEFFFBE6CFFFF6D8B3FFFFFFDEFF -F6D0A0FFF8D2A6FFE09D45FFDE993DA6DE993D06DE993D020000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -DE993DFFFFFEECFFFFFCFAFFFFFBF8FFFFFAF6FFFFF9F3FFFFF8F1FFFFF7EFFF -FFF5ECFFFFF4EAFFFFF3E8FFFFF2E4FFFEEEDFFFFCE8D2FFF8DCBBFFFFFFDEFF -F0BF85FFE09D45FFDE993DA6DE993D06DE993D02000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -DE993DFFFFFFEDFFFFFEECFFFFFEEBFFFFFDEAFFFFFCE9FFFFFCE7FFFFFBE6FF -FFFAE5FFFFFAE4FFFFF9E2FFFFF9E1FFFEF7DEFFFDF2D6FFFAEAC8FFFFFFDEFF -DE993DFFDE993DA6DE993D06DE993D0200000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -DE993DEFDE993DFFDE993DFFDE993DFFDE993DFFDE993DFFDE993DFFDE993DFF -DE993DFFDE993DFFDE993DFFDE993DFFDE993DFFDE993DFFDE993DFFDE993DFF -DE993DA4DE993D06DE993D020000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000DE993D01DE993D02DE993D04DE993D05 -DE993D03DE993D01000000000000000000000000000000000000000000000000 - - - -63164DED-EFFA-40D3-B387-F2802CF8D676 -SymetricKey -1152536483 -xwang -1152536491 -xwang -54C3E4A5-1AE3-4728-AF6F-569AF6A54914 -4 -D:\PDDEV\icons\PD Icons\Icons\SymetricKey.ico -0D0000001000000010000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00FFFFFFFFFFFE0FFFFFF8007FFFF800FFFFF8007FFFF8 -007FFFF0007FFFF0007FFFF0007FFFF0007FFFF0007FFFF0007FFFF0007FFFF0 -007FFE00007FFC00007FFC00007FFC00007FF800001FF800000FF800001FF800 -000FF800000FF800000FF000001FF000000FF000000FFC00000FFFC0001FFFF0 -003FFFFE03FFFFFFFFFF424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000003F464A09 -252C3020242D3121212C300D4A64700200000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000696B6D04080808312E2E2E7D -535353BF262626B00303048302141D8C164E6ACF16526ADD051922A500020440 -264A5A0300000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000626262286F6F6FC3B3B3B3F3 -DBDBDBFEB5B5B5F814425AE52B779CEE6BD6EEFE7EE0EAFD23728FE807304AB6 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000090909079D2D2D2FFF4F4F4FF -D8D8D8FFD4D4D4FF3C8FBEFFC2E0EFFF99E4FCFF6BCBFCFF67CBDFFD216A8FFD -172E381600000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000005151521CA6A6A6F3EEEEEEFF -B8B8B8FFCFCFCFFF66879AFD76B3D0FDBCE7FAFF3D9DF9FF78D9E4FE20698FFE -172E391600000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000969797094F4F4F518F8F8FE4D2D2D2FF -ADADADFFBDC3C7FF45728FFD3E91BFFC84D3F4FF3B9BF6FF66C7E0FF1F678CFF -172E391600000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000009292920E888888B3DDDDDDFFDBDBDBFF -A3A3A3FFB2B7BBFF56AAD7FFB8ECF8FF5BC2F3FF399BF3FF69C8DEFF1E658BFF -162E391600000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000A1A2A2017D7E7E59B7B7B7FFE7E7E7FF -9D9D9DFFD2D2D2FF3E6F8FFD91C9DFFD54BBEEFF3899F1FF65C6DEFF1D6389FF -17303D1600000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000008A8B8B085C5C5D60989898FFD2D2D2FF -969696FFBCBEC1FF295D7FFD4DA6D6FD4FB5EAFF3698EEFF62C1DCFF1C6188FF -17303E1600000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000868788248D8D8DCEDDDDDDFFC9C9C9FF -939393FF9BABB6FF56A6CCFD9DE7FAFF48AEE4FF3497ECFF5EBCDCFF1B6086FF -152E3D1600000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000626669075B5B5B88CBCBCBFFC7C7C7FF -949494FFB0B0B2FF316C8FF88FDBEBFE42A8E0FF3396EAFF52B1D8FF1B5E85FF -132D3C1600000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000024242440555555B1989898FFB5B5B5FF -8F8F90FF7C8F9BFF1B689BF94FADE6FD3BA1DBFF3194E7FF52AFD9FF1A5C83FF -122C3C1600000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000868686B7BFBFBFFC9D9D9DFF9B9B9BFF -7C868CFF61AACEFF68C4E9FE3BA2FDFF369BD6FF3094E5FF4AA7D6FF195B82FF -112B3B1600000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000059616607 -00000023000000321B20231C52626B0C939393DBCECECEFF9B9B9BFF909090FF -737E87FF57AAD0FF8BEAFFFF3DA2FFFF2F94D1FF2D92E2FF409DD0FF185981FF -102A3B1600000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000065686A0B27282845 -4A4A4AAD2F2F2FAC0000007B0000005A8F8F8FEF819FB7FF6C7379FF7B8287FF -4E718DFF55ADD3FF81E5FFFF3FA6FFFF298FCCFF2C91E0FF3D97CEFF17577FFF -112B3D1600000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000004C4C4C73A4A4A4ED -BCBCBCFFB1B1B1FF9A9A9AF7505F6BEB4CA2D6FF4AB7FBFF40A7EAFF3994C7FF -2975A4FF3EA0D8FF75DAFFFF4DB3FDFF2387C7FF2A90DDFF3490CFFF17567EFF -122C3E1600000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000A4A4A4DACFCFCFFF -B7B7B7FFC2C2C2FFBFC7CDFF549BCEFF6AD2FEFF54BBFEFF5BC2FEFF7BE2FFFF -80E5F7FF6ED4F7FF67CDFEFF52B8FEFF1D82C3FF298FDBFF2F87C5FF16547CFF -132D3E1600000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000079797903B2B2B2FFD7D7D7FF -AFAFAFFFB3B3B3FFA7B5C0FF58ABDEFF6ED5FEFF4EB4FFFF4FB5FFFF6FD4FFFF -91F7FFFF96FBFFFF76DCFFFF5EC4FFFF2A90D8FF2E94EBFF3A96E0FF457DA8FE -415D73CD1C28316B2860890C0000000000000000000000000000000000000000 -000000000000000000000000000000000000000070707022B9B9B9FFDBDBDBFF -A6A6A6FFA9A9A9FFA1B0BDFF54ADE1FF76DDFFFF4AB0FFFF42A8FFFF62C7FFFF -8AEFFFFF97FDFFFF80E8FFFF67CEFFFF49AFFEFF369CFEFF3EA5FAFF44A9EEFE -54A9D9FF4F83AAFF122E442E3478AB0100000000000000000000000000000000 -00000000000000000000000000000000000000008E8E8E3DC8C8C8FFE0E0E0FF -A7A7A7FFA0A0A0FF97A8B8FF65BAE4FF77DDFFFF45ABFFFF3AA0FFFF50B6FDFF -6ECEDDFF92E8EFFFA0F2F9FF89E0FCFF69C6FFFF47A8FFFF399FFFFF4DB3FFFF -62C9FDFE5792B7FE112B3F280000000000000000000000000000000000000000 -00000000000000000000000000000000000000009696965ACFCFCFFFE4E4E4FF -A5A5A5FF9B9B9BFF90A3B4FF60B8E7FF7BE0FEFF49AFFFFF359BFFFF3AA0ECFF -197FB4FF2480ABFF449BBAFF80C6D8FF9AD3E8FFA7D9F6FF7BBEFFFF48ACFFFF -59C0FDFE528DB6FE122C4132367DB40200000000000000000000000000000000 -000000000000000000000000000000000000000099999978DEDEDEFFE7E7E7FF -ABABABFF9D9D9DFF879CAFFF68C5ECFF80E6FFFF44AAFFFF3298FEFF379CF4FF -0F75BDFF0F75B6FF076AA0FF06689BFF13719DFF398BB4FF3091E8FF379EFCFF -51B9FEFF538FB8FF08131B2D2D65910200000000000000000000000000000000 -00000000000000000000000000000000000000009A9A9A8CEDEDEDFFE6E6E6FF -AFAFAFFFA2A2A2FF8299AEFF74D1F1FF80E6FEFF41A7FFFF3197FEFF3E9EEFFF -2B8CCBFF1C81C6FF137AC0FF177EBFFF0A6EA5FF1876A0FF1B83D2FF369CFBFF -48AFFEFF4F8BB7FF153145363E87BD0100000000000000000000000000000000 -00000000000000000000000000000000B1B1B103979797ADF6F6F6FFECECECFF -BABABAFFA8A8A8FF829BB2FF71D0F4FF81E9FFFF48A9FFFF3097FFFF2E96FEFF -43A7F8FF57B6EBFF45A8DCFF3094CBFF1077C0FF0E71ADFF1F84C6FF3097F3FF -40A7FDFF4D88B6FF1731442F0000000000000000000000000000000000000000 -00000000000000000000000000000000A6A6A612969696BCFBFBFBFFF7F7F7FF -E5E5E5FFD0D0D0FF92ADC5FF8DDCF8FFCAF8FFFFB8E0FFFF85C5FFFF6AB8FFFF -41A5FFFF4DB6FDFF67C9F9FF6CCBF2FF4AAAD9FF2283C7FF228AD3FF39A0F8FF -39A1FFFF4C87B8FF1731442C428FC70100000000000000000000000000000000 -00000000000000000000000000000000B4B4B40E9E9E9E80B2B4B5B3C3C3C3EB -DBDBDBFFE9E9E9FFBCD9F3FF89CBFDFFCDE9FEFFE0F2FEFFE8FCFEFFE3FBFEFF -C7F1FFFFB6EBFFFF87DAFFFF76D2FEFF69C7F9FF71D0F8FF55ACDCFF46A6EFFF -339AFEFF4984B7FF193A523A428FC60200000000000000000000000000000000 -000000000000000000000000000000000000000000000000AFB7BE09A6B2B941 -969696939E9E9EF79FA8B1FF81B0DBFF6CB6F5FF70BAFAFF8FCFFEFF95D2FDFF -BBEBFFFFC7F6FEFFC5FCFEFFB8F6FEFFA6EDFEFF87DEFDFF7FD8FBFF68C9FCFF -3CA4FFFF4783B8FF1732461E418EC60100000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000008892994389949D9C7E7E7ED19098A0F879A9CEFF6CA6DCFF -78B7F4FF65B7FAFF78C8F6FF8ED8FDFFB8F1FDFFC7FDFEFFB4F8FBFF9AF4FEFF -58BDF2EC4789BABA2753700E0000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000869FB227727B8159717171BA8B8D8FFF -8A9BABFF7D98B0FF4B94D6FF59AFF8FF72C0FCFF87D3FEFF5CB9F2F33DA6EFB4 -2E87D4524B98CB10000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000006E879715 -8A9AA4AA8A9BA5AA8B9BA6AAA8ADB9AAACAEBBAA7C96A3AA0000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 - - - -C40FC010-C64C-48A2-A188-E5954BEB4B8F -AsymetricKey -1152536552 -xwang -1152536554 -xwang -9D2984CF-0649-417E-A331-D1B4208BA52D -4 -D:\PDDEV\icons\PD Icons\Icons\AsymetricKey.ico -0D0000001000000010000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00FFC01FFFFFC01F03FFC01F01FFC01E00FFC01C00FFC0 -1C00FFC01C00FF801800FF801801FF801801FF800003F0000003E0000007E000 -0007C000000FC000000FC000001FC000001FC000000FC000000FC0000007C000 -000F8000000F8000000FC000001FC000001FC000003FC000003FC000003FE000 -007FF800007FFE007FFF424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000004000000233E3E3E8D6B6B6BD64B4B4BD0111111A8 -0000006A0000001F000000020000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000007272721E8A8A8AB9C1C1C1FBE9E9E9FFE9E9E9FF696969EA -000000940000003A000000080000000000000000000000000000000000000000 -0000000100000004000000070000000C0000000B000000040000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000084848440B2B2B2FBFFFFFFFFE0E0E0FFD9D9D9FFC0C0C0FE -1C1C1CB20000004A0000000D0000000000000000000000000000000000000000 -0000000900000023000000390000004800000042000000200000000600000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000A767676B1DFDFDFFFD2D2D2FFB3B3B3FFD4D4D4FF -353535C3000000460000000B0000000000000000000000000000000000000001 -03354F2C001E2D77012F46B6002E45C0000102980000005F0000001700000001 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000072727245858585C4C6C6C6FFC2C2C2FFB6B6B6FFCECECEFF -262626B60000003C000000080000000000000000000000000000000100000004 -1270A3CF75B3D3FF6BCCE7FF65CCE0FF0F5472E4000000880000002500000001 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000007F7F7F84CBCBCBFFEDEDEDFFABABABFFBFBFBFFFB4B4B4FF -1B1B1BA900000031000000040000000000000000000000000000000300000017 -196D9BC5E4F4FAFF97EEFFFF86EEFFFF4AAEC7FD0000008F0000002800000001 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000005B5B5B23919191E4F2F2F2FFACACACFFC1C1C1FFAAAAAAFE -0B0B0B980000002A00000001000000000000000000000000084C77130147726C -025B8EDBB6DFEDFF80CAFFFF50B8FFFF60C3D5FC000000850000002400000001 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000166666641818181E8CECECEFFA0A0A0FFC8C8C8FF949494FD -03030388000000250000000100000000000000000000000102598E3A52A0C8FA -90D4EEFF75D3FBFF3C9DF6FF76DDFFFF3C9DB9F8000000660000001800000001 -0000000000000000000000000000000000000000000000000000000000000000 -0000000042424202808080AED4D4D4FFD4D4D4FF949494FFC7C7C7FF7C7C7CF3 -0202027E0000002100000001000000000000000000000006022F4C3A4D9AC2F0 -C7FBFFFF47B3F7FF3EA5F2FF79DFF4FF095279D30000003B0000000900000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000064949494BA5A5A5F5DEDEDEFF8F8F8FFFC3C3C3FF6F6F6FEF -010101730000001C0000000100000000000000000C57882A0B5A8D9D358DBEF8 -B2EEFCFF3398E8FF67CDFAFF42A6CBFC001A2A7F0000001C0000000100000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000404040205D5D5D9A838383F7BEBEBEFF8C8C8CFFC1C1C1FF595959E2 -00000069000000190000000100000001000000090B60997171C0DDFF7ED5FBFF -47B0F3FF3298E2FF70D6F5FF0F5981D6000000430000000C0000000000000000 -0000000000000000000000000000000000000001000000060000000F0000000E -0000000676767684C2C2C2FFADADADFF9E9E9EFF858585FFBEBEBEFF4E4C4CDF -0000006A0000001C00000003000000080000002D032A468269BBD7FD9EEFFFFF -248CDAFF53BAEFFF48ACD6FC022D489300000021000000030000000000000000 -000000000000000000000000000000020000000D0000002C0000004D0000004C -00000033727272ABD6D6D6FFABABABFF949494FF8B8885FFBEB7B1FF373A3CD8 -0000008B00000045000000160C456D38267AABC8267CB3EC4FAEE6FF6EC8F0FF -258BD3FF65CBF7FF126090DE000000460000000D000000000000000000000000 -0000000000000000000000000000000B1B1B1B415D5D5DB5474747C50D0D0DA8 -0000008F666666D4D2D2D2FFAFAEADFF8B8D8DFF66829AFF619DCBFF2B74ABF5 -041F34C00000008E0000005612649FAC8DEEF5FF61C3FFFF3CA3FFFF2288D3FF -48AEE8FF48AEE3FF042D48960000002300000003000000000000000000000000 -0000000000000000000000016C6C6C449B9B9BE5BEBEBEFFB3B3B3FFA8A8A8FC -676767E6707070F6B9B9B9FFB6B0ADFF6C95B6FF49B6F7FF63CDFFFF53BDFFFF -3799DAFB0B3759D0020D16A7409ECDF293F8FFFF44AAFFFF2D93EDFF1C82C2FF -5DC3F8FF1A6DA2E40105074D0000000F00000000000000000000000000000000 -000000000000000000000001959595B9D5D5D5FFBEBEBEFFBCBCBCFFE4E4E4FF -F1F1F1FFCCCCCCFFAFAFAFFFB0B2B4FF52A8E3FF72DAFFFF5DC3FFFF4CB2FFFF -76DCFFFF5EC2E8FE1970B2F268CEF1FF74DAFFFF389EFFFF157BC7FF369CD7FF -4AAFE9FE073758A4000000260000000400000000000000000000000000000000 -000000000000000000000002A0A0A0D7DDDDDDFFB8B8B8FFACACACFFCECECEFF -F5F5F5FFFFFFFFFFE5E0DCFF89B1D4FF5FC9FBFF78DDFFFF43A9FFFF4BB1FFFF -75DAFFFF99FFFFFF7BE2F8FF6BD1FDFF5AC0FFFF3096F0FF0E74B4FF4FB5F7FF -2077B2EB00030551000000100000000000000000000000000000000000000000 -00000000000000006A6A6A11A7A7A7E1E2E2E2FFB2B2B2FFA1A1A1FFC5C5C5FF -EBEBEBFFFFFFFFFFE1E5EAFF62B4EDFF7BE4FFFF65CBFFFF349AFFFF4DB3FFFF -74D9FFFF95FBFFFF92F8FFFF70D6FFFF55BBFFFF177DBFFF2389CEFF42A8F2FF -0B3F65AE0000002B000000040000000000000000000000000000000000000000 -000000000000000071717124B3B3B3EAE7E7E7FFB5B5B5FF9A9A9AFFB6B6B6FF -BBBBBBFFE1DCD8FF9DC4EBFF61CCFBFF84EAFFFF47ADFFFF3399FFFF4FB5FFFF -82E6FEFF9CFEFFFF8FF7FFFF73D9FFFF50B6FAFF157BBCFF389EF1FF2279BAEF -0105087D0000002B000000070000000100000000000000000000000000000000 -00000000000000007A7A7A3EBCBCBCF4ECECECFFB5B5B5FF999999FFA7A7A7FF -727272FF75767AFF56ACE9FF87EFFFFF73D9FFFF349AFFFF379DFFFF39A1EDFF -3299BAFF95EDF4FFA1FDFFFF6FD8FFFF53BAFFFF399FFDFF379DF6FF125383E0 -000000A000000064000000270000000700000000000000000000000000000000 -00000000000000007B7B7B55CBCBCBFCF0F0F0FFB9B9B9FF9D9D9DFF919294FF -A59B8EFFA5C6E3FF69D4FDFF92F8FFFF4EB4FFFF2E94FFFF369FFFFF467FA7FF -606D76FF37738DFF88CFDEFFA4EEFFFF57BCFFFF349DFFFF3FA5FFFF42A6ECFD -165686E0020D15A00000004F0000001000000001000000000000000000000000 -00000000000000007979796ADBDBDBFFEEEEEEFFBBBBBBFFA3A3A3FF959493FF -7C7F83FF61B3ECFF91FBFFFF7FE7FFFF2D96FFFF319AFFFF2E92EFFF5D6D7BFF -CBA8A3FFAE9492FF4B6775FF6DACC1FFB6E8FEFF5BB0FFFF3BA2FFFF5BC1FFFF -57BDF6FF155788D30000004C0000000E00000000000000000000000000000000 -00000000000000017D7D7D86E6E6E6FFF4F4F4FFC1C1C1FFA4A4A4FF9F9994FF -7195BBFF78CAFFFFF4FFFFFFEAF9FFFF73BBFFFF2E96FFFF329AFDFF4894CAFF -6E7A82FFBC9C9BFFBF9A98FF3E5A68FA4385A4F5A1D0F8FF44A7FFFF58BEFFFF -56BBF4FE0F456FB40000002F0000000500000000000000000000000000000000 -00000000000000018F8F8F9FF2F3F3FFFFFFFFFFEEEEEEFFD1D1D1FFB2B1B0FF -99A0A7FF74A6DBFF8CC6FFFFECFAFFFFF5FFFFFF9CD5FFFF3CA0FFFF49B5FFFF -66C6E8FF6E878AFFBD9998FF9E8380FD225C85F31886E7FF3FA5FFFF59BFFFFF -318DCEF404121D6E000000190000000100000000000000000000000000000000 -00000000000000008484849AA3A3A2FFC4C5C7FFEAEAEAFFFDFDFDFFF8F9F9FF -E9E8E6FFDBD7D3FFA3C3E3FF77BBF9FFC3EBFFFFF0FFFFFFBAEBFFFF6BC4FFFF -70DCFFFF8BEBF0FF887E7DFFD09E9CFF4C6E83FF218EECFF42A8FFFF4EB4FBFF -185F97D20000003F0000000A0000000000000000000000000000000000000000 -00000000000000006B6C6E61AD967DFFA9947EFF6D6D6BF39B9B9BF1BCBCBCFF -DCDCDCFFEDEDECFFF1EDE9FFD7DEE7FF7EBBF4FF96D6FFFFDAFFFFFFCFFEFFFF -98EBFFFF8EF6FCFF837D7CFFDAA29FFF5F7586FF2E9CFAFF43A9FFFF399FEFFF -0A28428F00000022000000020000000000000000000000000000000000000000 -0000000000000000595C5E5EEBC39AFFDBBB9AFF323232D10809098C34343469 -5F5F5F94797979CB969797E8C0BEBBFFD1CFCEFFA3C5E7FF84CCFFFFBCF9FFFF -D0FFFFFF8EBEBFFFA38B8AFFC19592FF5C87A8FF39A4FFFF3CA2FDFF2477BCE6 -0000005200000010000000000000000000000000000000000000000000000000 -00000000000000005D5E603BB59D86F9F6D1AAFF686360EE0C0C0CB30000008C -00000064000000461C1C1C4C333333774D4C4C9F736E6CCD718091F5528FC2FF -87B1BAFF938886FFD2A6A5FF887D7FFF50A9E8FF39A1FFFF3399F7FF134876B4 -0000003100000006000000000000000000000000000000000000000000000000 -0000000000000000646565016B6866A5DCBE9EFFE8CBADFF7C7772F43E3E3DD4 -1B1B1CB9131313AA0E0E0EA00A0A0A980A0A0A9B191919AE484542DA6E6C6EFC -A9979AFFCFABABFF968A87FF95D2CDFF7EE3FFFF40A5FFFF2684DDF10920356A -0000001900000001000000000000000000000000000000000000000000000000 -0000000000000000000000005D5E5F166C6968B1B5A492FFE8D0B9FFD8C8B9FF -B4ADA5FFA09E9BFA9D9D9CF89C9B9BF8A29E9EFBB2AAAAFFC9BABAFFD2BCBCFF -AE9B9BFF6C6567D4498EC3C384E5FFFF83E3FCFF4CB1F8FF237BCEAB0000001D -0000000600000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000005C5E5F4A6E6C6ABD8F8A84E5 -B0A9A2F8C4C0BAFFD0CFCDFFCDCBCCFFBDB7B7FFA7A0A0F9858080E5696767BD -4E50506021212215174B7D082E90ED472A87E0532A7EC5311A568D1100000003 -0000000100000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000086868618 -8585853B8383836182828272808080737B7B7B5B6868682B0000000700000001 -0000000100000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 - - - -B80D585E-9984-4969-A676-B4E65BB37671 -XML -1152537167 -xwang -1152537174 -xwang -3D1EF3E5-5B5C-4B74-A97C-135376BD8D46 -4 -D:\PDDEV\icons\PD Icons\Icons\XML.ico -0D0000001000000010000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00FFFFFFFFF000001FF000001FF000001FF000001FF000 -001FF000001FF000001FF000001FF000001FF000001FF000001FF000001FF000 -001FF000001FF000001FF000001FF000001FF000001FF000001FF000001FF000 -001FF000001FF000001FF000001FF000001FF000001FF000003FF000007FF000 -00FFF00001FFFFFFFFFF424D3608000000000000360400002800000020000000 -20000000010008000000000000040000000000000000000000000000F5000000 -0000000080202000B45C5C00DE993D00E09D4500E09F4600E4A65500E8B87000 -EEBB7D00EEBB7E00EEBC7E00EEBC7F00EEBC8000EEBD8000EEBD8100EEBD8200 -EEBE8200EEBE8300EEBE8400EEBF8400EEBF8500EEBF8600EEC08600EEC08700 -EEC08800F0BF8500F0C59100F0CE9300F1C59200F1C79400F1C89400F2C99600 -F2D49D00F3CEA100F3CFA200F3D0A500F4C99600F4D0A300F4D2A900F5CE9F00 -F5D1A600F5D5AD00F6D0A000F6D3A900F6D4AC00F6D6A500F6D8B300F6DBAB00 -F7D5AC00F8D2A600F8DCBB00F8DDBC00F8E0B500F9D7B100F9DDBE00F9E0C300 -FADDBC00FADEBF00FAE2C800FAEAC800FBDDBE00FBDEBF00FBE4CB00FBE6CF00 -FBE8C300FCDBB600FCDFBF00FCE8D300FCF7D800FDDFC100FDE6CE00FDE8D200 -FDE9D500FDEBD800FDF2D600FEE2C700FEE2C800FEE4C900FEE4CA00FEE5CC00 -FEE8D300FEEDDB00FEEDDC00FEEDDD00FEEECE00FEEEDD00FEEEDE00FEEEDF00 -FEEFDF00FEF7DE00FFDEBD00FFDEBE00FFDFBE00FFDFBF00FFDFC000FFE0BF00 -FFE0C100FFE0C200FFE1C000FFE1C300FFE1C400FFE2C100FFE2C500FFE2C700 -FFE3C200FFE3C800FFE3C900FFE4C300FFE4C900FFE4CA00FFE4CB00FFE5C400 -FFE5CA00FFE5CB00FFE5CC00FFE5CD00FFE6C500FFE6CD00FFE6CE00FFE6CF00 -FFE7D000FFE7D100FFE8C800FFE8C900FFE8D200FFE8D300FFE9C800FFE9C900 -FFE9CA00FFE9CB00FFE9D300FFE9D500FFEACC00FFEACD00FFEAD500FFEAD600 -FFEAD700FFEBCA00FFEBCD00FFEBCE00FFEBCF00FFEBD600FFEBD800FFEBD900 -FFECCF00FFECD000FFECD100FFECDA00FFEDD100FFEDD200FFEDDC00FFEDDD00 -FFEED300FFEED400FFEEDD00FFEEDE00FFEEDF00FFEFCE00FFEFCF00FFEFD000 -FFEFD500FFEFD600FFEFDF00FFEFE000FFEFE100FFF0CF00FFF0D000FFF0D100 -FFF0D700FFF0D800FFF0D900FFF0E100FFF0E200FFF0E300FFF1D000FFF1D100 -FFF1D200FFF1DA00FFF1E300FFF1E400FFF1E500FFF2D100FFF2D200FFF2DB00 -FFF2E400FFF2E500FFF2E600FFF2E700FFF3D200FFF3D300FFF3E700FFF3E800 -FFF3E900FFF4D300FFF4E900FFF4EA00FFF5D600FFF5D700FFF5D800FFF5EB00 -FFF5EC00FFF5ED00FFF6D900FFF6DA00FFF6ED00FFF6EE00FFF7DB00FFF7DC00 -FFF7EF00FFF7F000FFF8DC00FFF8DE00FFF8F100FFF8F200FFF8F300FFF9DE00 -FFF9E000FFF9E100FFF9E200FFF9F100FFF9F300FFF9F400FFF9F500FFFAE000 -FFFAE100FFFAE400FFFAF500FFFAF600FFFBE200FFFBE300FFFBE500FFFBE600 -FFFBF800FFFCE400FFFCE500FFFCE700FFFCE900FFFCFA00FFFDE900FFFDEA00 -FFFDEB00FFFEEB00FFFEEC00FFFFDE00FFFFED00000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000003030303030303030303030303030303030303030303030000000000 -00000000039E9E9E9E9EA5A5A5A6A6A6AEAEAFB5B5B6BCBCBDC1030000000000 -00000000039D5A5A5A5A5A5C5C5C5F5F62626568686B6F6F74BD030000000000 -00000000039E5D5D5B5B5A5A5A5A5A5C5F5F626265686B6B6FBC030000000000 -0000000003A66060605E5E5D5D5B5A5A5A5C5C5F626565686BB6030000000000 -0000000003B0646363636161605E5D5D5B5A5A5C5F5F626568AF030000000000 -0000000003207067678A8A85848481807F7B7A7A7A7E7F6265AE030000000000 -000000000305C46A6A0F0F0E0E0D0C0C0B0A09080808085F62A6030000000000 -000000000303C572721111100F0F0E0E0C0B0B090808085C5FA5030000000000 -000000000303C67676757372716D6C6A67666461605D5B5A5C9E030000000000 -000000000303CA797899999898959491908C8B8584807F5B5A9D030000000000 -000000000303CB7D7D1716151414131211110F0E0C0C0B5E5B9D030000000000 -000000000303CE8786181717171515141312110F0E0D0C605E9D030000000000 -000000000303CF8E8E88878683827C797876736E6C696663609E030000000000 -000000000303D29393B7B1B1AAA9A8A1A0999895928C8B66639F030000000000 -000000000303D3979601A39B0196018F8D0150010101016764A7030000000000 -000000000303D7A29CA301019A96018F8D0150017775716A67A7030000000000 -000000000303D8ACABA302029A96018F8D0150017775716C69B0030000000000 -000000000303DFB3B3AD0202A29B0102020146014F4E4D4C4BA7030000000000 -000000000303E0BBBAB90101ABA3010101013901383C3D424554030000000000 -000000000303E4C2BF01BAB401AC015247012C0125282B303540030000000000 -000000000303E5C7C3C2BFBBB9B2AB554333221E1A1C1D1F2734030000000000 -000000000303E9CDC9C8C3BFBBB9B258483621F3F3F3F3F3F3F3030000000000 -000000000307EAD1D0CCC8C3BFBAB3A4493723F31B2F2D2A2404030000000000 -000000000344DBD5D4D0CDC8C3BFBAAC513A26F32F8968410603030000000000 -0000000003EEE2DDDCD4D0CCC7C0BAB2533E29F32D6841060303030000000000 -0000000003F0E8E3DED6D1CDC8C3BEB3563F2EF32A3104030303000000000000 -0000000003F2EDE8E3DCD4D0C8C3BFB8574332F3190403030300000000000000 -0000000003F4F2F1EFECEBE7E6E1DAD9594A3BF3030303030000000000000000 -0000000003030303030303030303030303030303030303000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 - - - -DE1FB8AA-9460-4B03-B729-CCE11EC90039 -FullTextCatalog -1152537537 -xwang -1152537539 -xwang -E96559E0-7B88-425D-89B3-86F3E82FE473 -4 -D:\PDDEV\icons\PD Icons\Icons\FullTextCatalog.ico -0D0000001000000010000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00E1FFFFFF803FFFFF8007FFFF0000FFFF00001FFF0000 -01FF0000003F0000001F0000000F000000070000000700000007000000038000 -0003800000038000000380000001800000018000000180000001800000018000 -00018000000180000001800000018000000380000007E000000FFF800E1FFFFC -0FFFFFFE0FFFFFFFFFFF424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -000000000000000000000000FFFFFF02FFFFFF02FFFFFF02FFFFFF0200000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000098CD4F099CCFB7079ACDC018A1D17647B4DA2AFFFFFF08FFFFFF04 -FFFFFF02FFFFFF01000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000098CDFF069ACFFF4AC2E4FF54BDDFFF2BA9D6FF24A6D4D525A7D57A -42B3DA29FFFFFF06FFFFFF03FFFFFF02FFFFFF01000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -1E9AF4010398CEFF2EADDCFFAEE9F7FFC0F2FDFF89E2F5FF6ACFEAFF46B8DDFF -26A8D5FF1EA4D3D421A6D37942B3DA29FFFFFF06FFFFFF03FFFFFF02FFFFFF01 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -1E9AF4030198CFFF3AB2E3FF99DBF0FFC5F3FFFFA1ECFEFF99ECFEFF92EBFDFF -82E0F6FF62CDEAFF42B6DDFF26A8D5FF1DA4D2D321A6D37942B3DA29FFFFFF06 -FFFFFF03FFFFFF02FFFFFF010000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -1E9AF4070398D1FF64C3F0FF5EC0E1FFC0F0FFFFA9EDFEFF8EE8FEFF90EAFEFF -91EAFEFF91EAFEFF8DE9FDFF7DDFF6FF60CCEAFF41B5DDFF26A8D5FF1FA4D3C5 -26A8D46A69C3E11AFFFFFF06FFFFFF03FFFFFF02FFFFFF01FFFFFF0100000000 -0000000000000000000000000000000000000000000000000000000000000000 -1E9AF4090498D2FF7BCDFBFF28A7D4FFB3EAFBFFB3EEFEFF81E4FFFF84E6FEFF -87E6FEFF89E7FEFF8BE8FEFF8CE9FEFF8DE8FEFF88E7FCFF79DDF5FF5DCAEAFF -41B5DDFF25A8D5FF21A5D3B729A9D46A74C8E41CFFFFFF09FFFFFF05FFFFFF03 -FFFFFF01FFFFFF01000000000000000000000000000000000000000000000000 -1E9AF4080498D2FF7CCDFBFF20A6DCFFA2E1F5FFB8F0FFFF83E4FFFF7CE2FFFF -7EE4FFFF7FE3FEFF7FE4FEFF82E5FEFF84E5FEFF86E6FDFF87E6FDFF88E7FDFF -83E5FCFF75DBF5FF5BCBEBFF41B6DEFF2AAAD6FF2CA9D6B933ADD76E79CAE51D -FFFFFF09FFFFFF05FFFFFF020000000000000000000000000000000000000000 -1E9AF4040298D0FF84D2FCFF2DADE6FF6DC9E7FFB9F0FFFF94E8FFFF7AE2FFFF -7BE2FFFF7BE1FFFF7AE1FFFF7BE2FFFF79E1FEFF7AE2FEFF7CE3FEFF7EE3FDFF -7FE4FDFF81E5FDFF82E5FDFF80E5FDFF75DBF6FF5ECBEBFF44B7DFFF2DABD6FF -30ABD6BA39AFD85EFFFFFF09FFFFFF0200000000000000000000000000000000 -1E9AF4020298CFFF89D4FCFF41B6F2FF33ADD7FFB8EEFFFFA7ECFFFF79E2FFFF -79E2FFFF79E1FFFF79E1FFFF79E0FFFF78E0FEFF77DFFEFF75E0FEFF73DFFEFF -72DFFEFF74DFFDFF77E0FDFF79E2FDFF78E2FDFF7AE3FDFF7AE3FDFF76DBF7FF -64CBECFF38AFD9FF36AED86EFFFFFF07FFFFFF01000000000000000000000000 -1E9AF4010198CFFF8AD5FCFF4FBDFBFF19A3D5FFACE7FAFFB7EFFFFF7BE2FFFF -79E2FFFF79E1FFFF78E1FFFF77E0FFFF76DFFEFF76DFFEFF76DEFEFF74DDFEFF -71DDFEFF6DDCFEFF6ADBFEFF6ADBFDFF69DCFDFF6BDBFDFF71DEFDFF7CE2FDFF -83E4FDFF6CD0F0FF2BA9D4E4FFFFFF0BFFFFFF02000000000000000000000000 -1E9AF4010198CEFF8DD7FDFF50BEFBFF1DA5DEFF87D5EFFFB7EFFFFF8EE6FFFF -79E2FFFF79E1FFFF78E1FFFF77E0FEFF76DFFEFF76DEFEFF75DDFEFF74DDFEFF -74DCFEFF72DBFEFF6DDAFEFF67D9FEFF63D8FEFF60D6FDFF62D9FDFF69DAFDFF -74DDFDFF6ED3F4FF2AA9D6FF4BB6DC2BFFFFFF02000000000000000000000000 -1E9AF4010098CDFF93DAFDFF55C2FCFF32AFE9FF46B7DEFFA4E4C8FF8CDEBBFF -52CC96FF4FCC95FF4FCB96FF4ECB95FF4DCA95FF4FCB9BFF75DDFEFF74DDFEFF -73DCFEFF72DCFEFF4AC599FF46C191FF42BE8DFF3FBC8AFF3DB987FF42BB89FF -4EBD90FF5CC199FF36AFC2FF129E5DF0FFFFFF04FFFFFF010000000000000000 -000000000098CDFF98DEFDFF58C4FCFF4BBBF8FF17A1D1FFA3E4C7FFA2E5CBFF -69D8B4FF66DCBDFF5EDAB8FF55D1A4FF4ECA96FF4ECB9BFF74DDFEFF74DDFEFF -73DCFEFF72DBFEFF4BC59AFF49C598FF4DCBA4FF51CFB0FF50CEB0FF4FCCAFFF -5BCEB9FF6DCDB7FF47B7BDFF18A293F0FFFFFF07FFFFFF010000000000000000 -000000000098CDFF9DE1FDFF5CC9FCFF51BFFBFF109FD5FF95DCF4FFB4ECF7FF -7CD9B7FF6ED7B2FF58D4AAFF51CB9EFF6DDAEAFF74DEFEFF73DDFEFF73DCFEFF -72DBFEFF71DBFEFF71DAFEFF5CCFCAFF49C79AFF50D0ADFF4FCEAEFF4FCDB0FF -5ECBB0FF50BEA5FF53BDD6EB21A6D4D4FFFFFF09FFFFFF010000000000000000 -000000000098CDFFA1E4FDFF62CEFDFF55C3FBFF22A9E0FF58BEE2FFB5ECFFFF -B4ECFBFFA9E5D2FF9EE5CDFF87DBC0FF8EE3FEFF7EDEFEFF72DBFEFF72DBFEFF -72DBFEFF71DAFEFF70DAFEFF54CBB5FF4BCAA0FF4FCFA9FF4ECEAAFF57D0B2FF -53C3A1FF5CC7E7FF6ECCF2CC1FA5D3FF69C2E11AFFFFFF020000000000000000 -000000000098CDFFA6E8FEFF6AD5FDFF5ECBFCFF4DBEF8FF26A9DAFF49B7DEFF -77CDEAFF9CDDDDFFA7E5D5FF9FE1C2FFADE8EFFFB3EBFFFFB2ECFFFFA0E6FEFF -8FE2FEFF80DDFEFF6ED7F0FF47C395FF4CCEA2FF4CCEA4FF4CCEA6FF5ECFB2FF -55C7B4FF61CCF4FF6CCEF2CD36AFDBE42DAAD65BFFFFFF04FFFFFF0100000000 -000000000098CDFFAAEBFEFF73DBFDFF68D4FDFF5CCAFBFF54C3FAFF3DB6EFFF -28ACE4FF14A1D8FF1FA4D3FF3CB1D3FF65C3DEFF93D9F2FFA7E4F9FFB1E9FEFF -B0E9FFFFAFEAFFFFA4E6E3FF76D7B5FF4ACEA0FF4ACDA0FF52CFA9FF58C9A9FF -60CFDDFF5ECCF4FF69CEF2CE4EBCE3D22DABD68DF2FFFF08FFFFFF0100000000 -000000000098CDFFADEFFEFF79E2FEFF73DDFDFF6CD7FDFF65D1FCFF5FCCFCFF -58C7FBFF53C2FAFF2CAC6BFF2AAD54FF00995AFF01987FFF109EB5FF30ACD7FF -5ABFD2FF7ED2C8FF9AE0C7FF95E3CAFF49CD9FFF49CD9FFF58CFADFF51C7AAFF -6CD5FEFF5DCCF4FF68CFF2CF6CCCEEBD27A9D5C8F5FFFF0AFFFFFF0100000000 -000000000098CDFFB1F2FFFF7EE7FFFF7CE4FEFF78E1FDFF73DEFDFF6FD9FDFF -69D4FDFF64D0FCFF48BFBCFF30AD32FF18AC20FF009800FF009800FF01991AFF -049B5DFF0A9FD0FF86D9CEFF9BE5CEFF61D3ABFF4ACCA0FF5ACCACFF58CBCAFF -6BD5FEFF5CCCF4FF68D0F2CF8CDEFAA823A8D5FFE5FFFF0CCCFFFF0200000000 -000000000098CDFFB4F4FFFF84ECFFFF82EAFFFF7FE7FFFF7CE5FEFF79E2FEFF -76DFFDFF73DCFDFF70DAFDFF21A833FF2BAE2FFF0C9E1EFF5DC9FCFF56C4FCFF -50BFFAFF13A298FF56BFD5FFA1E5D0FF88DEC1FF64D2AFFF4EC29DFF64D1F0FF -6AD4FEFF5BCBF4FF69D0F2CF95E3FEA533B0DAEB2FAED74CDDFFFF0300000000 -000000000098CDFFB7F7FFFF89F1FFFF86EEFFFF84ECFFFF82EAFFFF80E8FFFF -7DE6FEFF7BE3FEFF78E0FEFF4CC5A1FF49B94CFF17A319FF50C5BCFF64D0FDFF -48BDCCFF099D24FF18A3D2FF9FE1D6FFA5E5D2FFACE5D8FFA1E0D8FF9BE1FEFF -8ADCFEFF6FD1F5FF6CD0F2CD96E2FEA346B9E1D922A7D48AE6FFFF0400000000 -000000000098CDFFB9FAFFFF8DF5FFFF8BF3FFFF89F1FFFF87EFFFFF84ECFFFF -82EAFFFF80E8FFFF7EE6FEFF74DFEEFF33AE3BFF48BA4EFF2AB15EFF6ED8FDFF -24AC5DFF28B031FF2BB585FF32ACD5FF60C2D7FF86D2D7FF9CDBE8FFAAE3FDFF -AEE6FFFFA5E1F9FFB3E6F8E4C8EFFEC365C7EAC41BA3D3B6EBFFFF0500000000 -000000000098CDFFBCFDFFFF92FAFFFF90F7FFFF8EF5FFFF8BF3FFFF89F1FFFF -87EFFFFF85EDFFFF82EAFFFF80E8FFFF36B865FF61C265FF109E10FF65D5DDFF -0D9E15FF4DC860FF50CB65FF45C472FF39B499FF0C9D95FF24A8DBFF27A7D4FF -4BB7DDFF71C8E6FF99D8EFF1A7DEF3DB46B9E6B60A9CCFEFFFFFFF0300000000 -000000000098CDFFBEFEFFFFA7FDFFFF9FFCFFFF92FAFFFF90F8FFFF8EF5FFFF -8CF3FFFF89F1FFFF87EFFFFF85EDFFFF72E1DFFF3DB13EFF43B647FF19A62EFF -29AD2FFF5CD073FF5BCF72FF5ED074FF5DC262FF32B18FFF52C1FBFF50BFFBFF -41B8F2FF22A8E1FF0F9DD1C625A7D4C2079ACEC10B9DD088FFFFFF0100000000 -000000000098CDEF6FD8ECFFAFFEFFFFB6FEFFFFBDFDFFFFB5FDFFFFACFBFFFF -A2F9FFFF99F6FFFF8EF4FFFF8AF1FFFF87EFFFFF38B553FF6FC974FF15A015FF -55C565FF67D47FFF66D37EFF76D688FF32AC32FF80D6ECFF7AD5FCFF6DCFFCFF -62C9FBFF2FAFE8FF0798D48D3AA7F5088ECDFA02FFFFFF010000000000000000 -000000000098CD4F0098CDDF0098CDFF1BAAD6FF38BDDFFF5ED1E9FF82E4F2FF -AEFDFFFFB4FCFFFFBAFBFFFFB3F9FFFFAAF6FFFF6CD09CFF58BD5BFF66C76EFF -71D78AFF72D78AFF73D78BFF77CF80FF1BA55CFF50C1E8FF6DD0F1FF87DBFAFF -8EDDFDFF57BFE9FF0398D0861E9AF4031E9AF401000000000000000000000000 -0000000000000000000000000098CD0F0098CD3F0098CD6F0098CD9F0098CDCF -0098CDFF1BAAD6FF2DB6DCFF53C9E6FF72DBEFFF2EAED8FF28A841FF8ADA99FF -7BDA94FF7BDB95FF85DD9CFF4DB84FFF0098718F0098CDAF0098CDBF0198CEFF -0198CEFF0198CEFF0098CD511E9AF40100000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000098CD3F0098CD6F0098CD8F0098CDBF0098CD6F0098007F7ED085FF -8BDFA2FF85DE9EFF8FDB9EFF21A521DF0000000000000000000000001E9AF401 -0198CF100098CE201E9AF4010000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000098001F3DB03DFF -9BE2ADFF9AE4B0FF71C976FF0098006F00000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000014A0148F -44B344FF44B344FF22A622EF0098000F00000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 - - - -AF74B44F-DF3D-4A38-9F6F-E1513DCA9CB3 -Aggregate -1152538145 -xwang -1152538147 -xwang -5246507C-DA84-4907-A3F3-CCF6A9F2775E -4 -D:\PDDEV\icons\PD Icons\Icons\Aggregate.ico -0D0000001000000010000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00FFFFC3E1FF8001F1FC000030F8000000F0000000F000 -0008F0000008F0000008F0000008F0000008F0000008F0000000F0000000F000 -0001F0000001F000000FF000000FF000000FF000000FF000000FF000000FF000 -000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF800 -001FFE00007FFFFFFFFF424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000CC65003FCD6A08FFCC6500FFCC65001F0000000000000000 -000000000000000000000000CC65002FCC6500FFCD6A08FFCC65001F00000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000098CD2F0098CD7F0098CDAF0098CDCF0098CDFF0098CDFF0098CDFF -0098CDFF0098CDFF9A773CFFD98B3BFF987234EB0098CD7F0098CD3F00000000 -00000000000000000000000000000000CC6500CFE09849FFCE6D0D9F00000000 -0000000000000000000000000000000000000000000000000098CD0F0098CD7F -0098CDDF0D9FD3FF2BAFE2FF41BCEBFF52C7F1FF73DCFEFF78E0FEFF7DE5FEFF -82EAFFFF8FDEE0FFD78531FFD57713FF86AD94FF40C2E2FF12A3D3FF0098CDEF -0098CD7F0098CD0F0000000000000000CC65006FDA8323FFD47E2AFFCC65000F -00000000000000000000000000000000000000000098CD2F0098CDDF1FABD9FF -57CDF1FF6AD4FEFF63CDFEFF68D2FEFF6DD7FEFF73DCFEFF78E0FEFF7DE5FEFF -82EAFFFFA5B390FFE9A756FFCC6500FFBE8B40FFCD6907FFCC6500FFA5915BFF -1FABD9FF9B7131FBCC6500FFCF6E0FFFCC6500DFD2710CFFE49C47FFCC65005F -000000000000000000000000000000000098CD2F0098CDEF4CCAE9FF81E9FFFF -76DFFEFF6AD4FEFF63CDFEFF68D2FEFF6DD7FEFF73DCFEFF78E0FEFF7DE5FEFF -82EAFFFFBB8840FFE99F43FFC57820FF9DEBE0FFD2771EFFF5B95FFFCF6A05FF -AA9E70FFD2700BFFF5B85CFFD2771EFFCC65001FCC6500FFEEAA50FFCC65009F -000000000000000000000000000000000098CDAF48C8E6FF8AF2FFFF81E9FFFF -76DFFEFF6AD4FEFF63CDFEFF68D2FEFF6DD7FEFF73DCFEFF78E0FEFF7DE5FEFF -82EAFFFFCE8232FFED9B2DFFBD8A40FF96FDFFFFAFB080FFE39C4AFFE58F23FF -CC6500FFEB992BFFE39B49FF707C5CE700000000CC6500BFEA9C37FFD07216BF -000000000000000000000000000000000098CDFF89F3FBFF8AF2FFFF81E9FFFF -76DFFEFF6AD4FEFF63CDFEFF68D2FEFF6DD7FEFF73DCFEFF78E0FEFF7DE5FEFF -82EAFFFFD37A22FFE88D18FFB69D60FF96FDFFFF96F1F0FFC57B27FFF3AA44FF -E28210FFF3AC49FFC17827FF0C95C1FF00000000CC6500BFEF951CFFD37921FF -000000000000000000000000000000000098CDFF92F9FFFF8AF2FFFF81E9FFFF -76DFFEFF6AD4FEFF63CDFEFF68D2FEFF6DD7FEFF73DCFEFF78E0FEFF7DE5FEFF -82EAFFFFD37A22FFE5850EFFAEAF80FF96FDFFFF93FAFFFFA8B690FFD57A1EFF -FF9F11FFD37718FF90AC9FFF0098CDFF00000000CC6500BFF29413FFD37921FF -000000000000000000000000000000000098CDFF92F9FFFF8BF2FFFF82EAFFFF -79E1FEFF6DD7FEFF63CDFEFF68D2FEFF6DD7FEFF73DCFEFF78E0FEFF7DE5FEFF -82EAFFFFD37A22FFE88406FFB69D60FF96FDFFFF93FAFFFFBD8D46FFED9525FF -F29313FFE6912BFFAE8850FF0098CDFF00000000CC6500BFEF8B08FFD37921FF -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF7DE6FEFF78E0FEFF74DDFEFF71DAFEFF73DCFEFF78E0FEFF7DE5FEFF -82EAFFFFCE8232FFED8A09FFBD8A40FF96FDFFFFACB990FFD98225FFF79A18FF -D47F2BFFF29313FFD67F25FF328B9AFF00000000CC6500BFE98811FFD37921BF -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF7DE6FEFF79E2FEFF7BE3FEFF7DE5FEFF7CE4FEFF79E1FEFF7DE5FEFF -82EAFFFFBB8840FFEE982AFFC57820FF99F4F0FFCC7A22FFF79A18FFD98225FF -BE7D30FFE18C2BFFEE941EFFB36F20FF00000000CC6500FFEE982AFFCC6500AF -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF7DE6FEFF79E2FEFF7BE3FEFF80E9FEFF82EAFEFF525454FF434242FF -272727FF683503FFE7922AFFCC6500FFBE8B40FFCE6C0CFFD17418FFC47A26FF -7FE7FEFFC27826FFD17418FFCD6806FFCC65009FD26B00FFE48E2AFFCC65005F -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF89F1FFFF -7AE7F9FF69DCF3FF61D7F0FF56D1ECFF55D0ECFF48C7E7FF5F6162FF6A6969FF -424242FF1C1004FFD7822AFFD87100FF87AD94FF6BE0F0FF71E3F4FF7DEAFAFF -82EAFEFF76DFFEFF6DD6FEFF0098CDFFCC65008FD87100FFD47E2AFFCC65000F -000000000000000000000000000000000098CDFF8AF5FBFF68DEF0FF50CDE9FF -4BC9E8FF54CDEDFF5BD0F1FF6BDAF7FF6DDBF7FF77E2FAFF5A5B5CFF908F8FFF -616060FF292A2AFFBB8D47FFDB852AFFB5914EFF6EE2F2FF5DD6EDFF4DCBE9FF -4DCBE9FF5FD5F0FF6EDAFBFF0098CDFFCC6500DFDE882AFFCE6D0D9F00000000 -000000000000000000000000000000000098CDFF4ECDE7FF61D8EFFF7AE7FAFF -82EAFEFF7DE6FEFF79E2FEFF7BE3FEFF7DE5FEFF7FE7FEFF575859FFA9A9A8FF -818080FF464747FF9CD1C0FFD17419FFCD6A08FF9AE8E0FF8DF5FFFF86EEFFFF -73DFFAFF53CBEEFF44C2E7FF268FA7FFCD6A08FFD17418FFCC65001F00000000 -000000000000000000000000000000000098CDFF77E7F5FF8DF5FFFF87EFFFFF -82EAFEFF7DE6FEFF79E2FEFF7BE3FEFF7BE1FAFF71CBDFFF59595AFFBFBFBFFF -9E9D9DFF434343FF79CDD5FF90F7FDFF96FDFFFF93FAFFFF8DF5FFFF86EEFFFF -7FE7FEFF76DFFEFF5CCDF6FF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF7DE6FEFF79E2FEFF79DFFAFF6FC8DEFF565756FF9D9D9DFFD3D3D2FF -AEAEAEFF727272FF1A1B1BFF7BCDD2FF95FBFDFF93FAFFFF8DF5FFFF86EEFFFF -7FE7FEFF76DFFEFF6DD6FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF7DE6FEFF77DEFAFF6DC7DEFF6A6969FFB8B8B8FFE4E4E4FFDCDCDBFF -BEBEBDFF949493FF5B5B5BFF191A1AFF7ED1D2FF92F8FDFF8DF5FFFF86EEFFFF -7FE7FEFF76DFFEFF6DD6FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF7BE2FAFF6CC6DEFF6F706EFFC3C2C2FFEAEAEAFFFBFBFBFFEEEFEDFF -D6D6D5FFBDBDBDFF9B9B9BFF595A5AFF171818FF7BCED2FF8CF3FDFF86EEFFFF -7FE7FEFF76DFFEFF6DD6FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF70CADFFF828080FFD2D2D2FFEDEDEDFFFDFDFDFFFFFFFFFFFAFAF9FF -E7E7E6FFD6D6D5FFB8B8B8FF939393FF535353FF181818FF77CBD3FF86EEFFFF -7FE7FEFF76DFFEFF6DD6FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF7E7E7DFFD0D0CFFFEEEDEDFFFBFBFBFFFAFAFAFFF5F5F5FFEDEDEDFF -E5E5E4FFD0D0CFFFBFBFBFFF999999FF646464FF555555FF181919FF86EEFFFF -7FE7FEFF76DFFEFF6DD6FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFFB6B7B7FFC4C4C4FFD3D3D3FFCFCECEFFC0C0C0FFBBBBBBFFB0AFAFFF -A6A5A4FFA19F9FFF9B9A9AFF8E8E8EFF777878FF646363FF494949FF86EEFFFF -7FE7FEFF76DFFEFF6DD6FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF7DE6FEFF79E2FEFF7BE3FEFF7DE5FEFF7FE7FEFF81E9FEFF84ECFEFF -87EFFFFF8BF3FFFF8EF6FFFF92FAFFFF96FDFFFF93FAFFFF8DF5FFFF86EEFFFF -7FE7FEFF76DFFEFF6DD6FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF7DE6FEFF79E2FEFF7BE3FEFF7DE5FEFF7FE7FEFF81E9FEFF84ECFEFF -87EFFFFF8BF3FFFF8EF6FFFF92FAFFFF96FDFFFF93FBFFFF8DF5FFFF86EEFFFF -7FE7FEFF76DFFEFF6DD6FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -87EFFEFF86EFFEFF8AF2FEFF8CF3FEFF92F9FFFF92FAFFFF92F9FFFF93FAFFFF -92FAFFFF93FAFFFF93FAFFFF93FBFFFF94FBFFFF93FBFFFF91F8FFFF8AF1FFFF -80E7FEFF76DFFEFF6DD6FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF92F9FFFF94FBFFFF -94FBFFFF93FAFFFF92F9FFFF90F7FFFF8DF4FFFF8AF2FFFF87EFFFFF85EDFFFF -82EAFFFF7FE7FFFF7CE4FEFF7BE3FEFF7DE5FEFF7EE7FEFF81EAFEFF87EFFEFF -8EF5FFFF84ECFEFF72DBFEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF96FDFFFF95FCFFFF94FBFFFF -92F9FFFF91F8FFFF8FF6FFFF8CF4FFFF8AF2FFFF88EFFFFF85EDFFFF82EAFFFF -7FE7FFFF7CE5FEFF79E2FEFF76DFFEFF73DCFEFF6FD8FEFF6CD6FEFF68D2FEFF -6AD3FEFF77DFFEFF89F0FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF8AF5FBFF93FAFFFF92FAFFFF -91F8FFFF8FF6FFFF8DF4FFFF8AF2FFFF88EFFFFF85EDFFFF82EAFFFF7FE8FFFF -7CE5FEFF79E2FEFF76DFFEFF73DCFEFF70D9FEFF6CD6FEFF69D2FEFF65CFFEFF -62CCFEFF62CCFEFF61CDFBFF0098CDFF00000000000000000000000000000000 -0000000000000000000000000000000011A3D2D81AAAD6FF64DBEFFF91F8FFFF -8FF6FFFF8DF4FFFF8AF2FFFF88F0FFFF85EDFFFF83EBFFFF80E8FFFF7DE5FEFF -7AE2FEFF76DFFEFF73DCFEFF70D9FEFF6CD6FEFF69D3FEFF65CFFEFF62CCFEFF -62CCFEFF43BBEEFF12A1D6FF11A3D2D800000000000000000000000000000000 -00000000000000000000000000000000000000000098CD5F0098CDCF0098CDFF -22AFD9FF45C5E6FF55CFECFF64D8F2FF6ADBF5FF80E8FFFF7DE5FFFF7AE2FEFF -77DFFEFF73DCFEFF70D9FEFF58CBF4FF4FC3F1FF3FBAEBFF31B2E5FF17A4D9FF -0098CDFF0098CDCF0098CD5F0000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000098CD1F -0098CD4F0098CD7F0098CDAF0098CDBF0098CDDF0098CDFF0098CDFF0098CDFF -0098CDFF0098CDFF0098CDFF0098CDDF0098CDBF0098CDAF0098CD7F0098CD4F -0098CD1F00000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 - - - -4FEE3858-A7E4-406C-A214-EB12048A3C52 -Certificate -1152539281 -xwang -1152539282 -xwang -2C0AF450-B1DB-4F8E-BD07-02F73824B289 -4 -D:\PDDEV\icons\PD Icons\Icons\Certificate.ico -0D0000001000000010000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00FFFFFFFFFFFFFFFFFFFFE007FFFFF00F000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000FFFFFFFFFFFFFFFF424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000E1A048FFF0BF7CFFF6D69FFFFDE8BCFFF3CA89FF -EEB260FFF5CB8EFFF7C58AFFE8A959FFC1912AFF000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000D8983CFFF6DAA5FFEFDCB4FFA1CFC7FF -52AFC5FFCED3BCFFE6AA59FFBB9026FF00000000000000000000000000000000 -C8D6DCEF99B4C2EF6A93A8EF8CAABBEFB2C3CEFE9EB4C2FE6D8FA4FE7999ABFE -A9BCC8FEA2B6C3FE6D8FA4EF7898ABEFA9BCC8EFA2B6C3EF6F8EA3EF6E8EA2EF -B0C2CDFEA0B5C2FE7191A5FE7C98A9FEB1C2CDFFDCAE71FF37A8CBFF14ABE0FF -29BEEBFF23A6D3FFA79078FF7898ABFFA8BBC7FEA3B7C3FE7890A2EF3B5C79EF -9EB9C6FF99B6C4FF94B2C2FF9BB9C7FFA0BCC9FF9AB7C5FF91ADBDFF94AFBFFF -9CB7C5FF99B4C2FF91ADBDFF94AFBFFF9CB7C5FF99B4C2FF8FAABAFF90ABBAFF -A0BCC9FF9AB7C5FF91ADBDFF94AFBFFFA0BCC9FF736865FF067BA8FF10CDFCFF -12D2FFFF13C1EFFF7E9FA5FF94AFBFFF9CB7C5FF99B4C2FF8098AAFF71899DFF -6F99ADFF9DBAC8FFCCDBE2FFFAF9F7FF85AFC0FF99BBCAFFC1D4DDFFB1C9D4FF -87ADBEFF91B3C3FFC1D4DDFFB1C9D4FF87ADBEFF91B3C3FFBBCFD9FFB4CBD5FF -85AFC0FF99BBCAFFC1D4DDFFB1C9D4FF85AFC0FF38373EFF1482A1FF34DDFFFF -2DDBFFFF14D3FFFF7BB4C1FFB1C9D4FF87ADBEFF91B3C3FF8DA6B6FFAFBDC8FF -8EADBDFFA0BECBFFB3CED9FFFAF9F7FFF1E6E0FFEEE0D9FFEDDDD5FFE9D4CAFF -E9D4CAFFE9D4CAFFE9D4CAFFE9D4CAFFE9D4CAFFE9D4CAFFE9D4CAFFE9D4CAFF -E9D4CAFFE9D4CAFFE9D4CAFFE9D4CAFFE9D4CAFF131520FF0B0D17FF58D1E2FF -5AEAFFFF3ADFFFFF52B3CEFFE9D4CAFFE9D4CAFF8DABBBFF8BA4B5FF8A9FAFFF -BECED7FFA3C2CEFF89B5C5FFFAF9F7FFF1E6E0FFF1E6E0FFEDDDD5FFECDBD3FF -EBDAD1FFEBD9D0FFEBD8CFFFEAD8CFFFEAD7CEFFEAD7CEFFEAD7CDFFEAD6CDFF -EAD6CDFFEAD6CDFFEAD6CCFFEAD6CCFFEAD6CCFF2E2F39FF23252FFF203842FF -69D7E2FF51E7FFFF285E73FFEAD6CCFFE9D4CAFFC3D3DCFF869FB0FF4E6E87FF -A7C0CCFFA2C1CEFF9EC2CFFFFAF9F7FFF4ECE8FFF1E6E0FFEFE2DCFFEEE0D9FF -EDDED6FFEDDDD5FFECDCD4FFECDBD3FFECDAD2FFEBDAD1FFEBD9D0FFEBD9D0FF -EBD8D0FFEBD8CFFFEBD8CFFFEBD8CFFFEBD8CFFF6B6C71FF464850FF22242EFF -0D0F19FF0B0D17FF474246FFEAD6CCFFE9D4CAFFA9C0CCFF8AA3B3FF6C879CFF -79A3B6FFA1BFCCFFC9DBE3FFFAF9F7FFF5EFEBFFF1E6E0FFF1E6E0FFF0E3DDFF -EFE1DAFFEDDDD5FFECDCD4FFECDBD3FFECDAD2FFECDCD4FFECDBD3FFECDBD3FF -ECDAD2FFECDAD1FFECDAD1FFECDAD1FFECDAD1FFECDAD1FF7A7A80FF514E52FF -515056FF656165FFEBD8CFFFEAD6CDFFE9D4CAFF7599ADFF90A9B9FFA9B8C4FF -8BAEBFFFA2C0CDFFB9D3DCFFFAF9F7FFF5EFEBFFF3EAE5FFD1C5BCFFD0C2B8FF -D0BFB4FFCFBCB0FFCFB9ACFFCFB6A8FFCEB3A4FFCEB0A0FFCEAD9CFFCDAA98FF -CDA794FFCDA490FFCCA18CFFCC9E88FFCC9C84FFEDDCD4FFEDDCD4FFEDDCD4FF -EDDCD4FFECDAD2FFEBD8D0FFEAD6CDFFE9D4CAFF89A8B9FF8EA7B8FF93A7B6FF -B9CCD6FFA4C3D0FF90BBCAFFFAF9F7FFF6F1EEFFF4ECE8FFF2E9E3FFF1E6E0FF -F0E4DEFFEFE2DCFFEFE1DAFFEEE0D9FFEEDFD7FFEDDED6FFEDDDD6FFEDDDD5FF -EDDCD4FFEDDCD4FFEDDCD4FFEDDCD4FFEDDCD4FFEDDCD4FFEDDCD4FFEDDCD4FF -EDDDD5FFECDBD3FFEBD9D0FFEAD6CDFFE9D4CAFFBDCFD8FF89A2B3FF587790FF -AFC6D1FFA5C3D0FF9AC1CFFFFAF9F7FFF7F2EFFFF5EEEAFFF3EBE6FFF2E8E3FF -F1E6E0FFF0E4DEFFF0E3DCFFEFE2DBFFEFE1DAFFEEE0D9FFEEDFD8FFEEDED7FF -EEDED7FFEEDED7FFEEDED7FFEEDED7FFEEDED7FFEEDED7FFEEDED7FFEEDED7FF -EDDDD6FFECDBD3FFEBD9D0FFEAD7CDFFE9D4CAFFB1C7D2FF8BA4B5FF67849AFF -84ABBDFFA4C2CFFFC4D9E1FFFAF9F7FFF7F3F0FFF5EFEBFFD1C5BCFFD0C2B9FF -D0C0B6FFD0BEB3FFCFBCB0FFCFBAADFFCFB8AAFFCFB5A7FFCEB3A4FFCEB1A1FF -CEAF9EFFCEAD9BFFCDAB98FFCDA895FFCDA692FFCDA48FFFCCA28CFFCCA089FF -CC9E86FFCC9C84FFEBDAD1FFEAD7CEFFE9D4CAFF7EA2B4FF91ACBBFFA3B4C1FF -8AAFC0FFA4C2CFFFBFD6DFFFFAF9F7FFF7F4F1FFF5EFEBFFF4ECE8FFF3EAE5FF -F2E8E2FFF2E7E2FFF1E6E0FFF0E5DFFFF0E4DDFFF0E3DCFFF0E3DCFFF0E3DCFF -F0E3DCFFF0E3DCFFF0E3DCFFF0E3DCFFF0E3DCFFF0E3DCFFEFE1DAFFEEE0D9FF -EDDED6FFEDDDD5FFECDAD2FFEAD7CEFFE9D4CAFF85A7B8FF91ACBBFF9DB0BDFF -B6CBD6FFA6C5D1FF96BFCDFFFAF9F7FFF7F4F1FFF6F0EDFFF4EDE9FFF3EBE6FF -F3EAE5FFF2E9E3FFF2E7E2FFF1E6E0FFF1E5DFFFF1E5DFFFF1E5DFFFF1E5DFFF -F1E5DFFFF1E5DFFFF1E5DFFFF1E5DFFFF1E5DFFFF0E4DDFFEFE2DCFFEFE1DAFF -EEDFD7FFEDDED6FFECDAD2FFEAD7CEFFE9D4CAFFB7CCD6FF8CA6B6FF648399FF -B7CDD7FFA6C6D2FF96BFCDFFFAF9F7FFF8F4F2FFF6F1EEFFD1C5BCFFD0C2B9FF -D0C0B6FFD0BEB3FFCFBCB0FFCFBAADFFCFB8AAFFCFB5A7FFCEB3A4FFCEB1A1FF -CEAF9EFFCEAD9BFFCDAB98FFCDA895FFCDA692FFCDA48FFFCCA28CFFCCA089FF -CC9E86FFCC9C84FFECDBD3FFEAD8CFFFE9D4CAFFB8CDD7FF8DA7B7FF658499FF -8CB3C3FFA5C5D1FFBFD6DFFFFAF9F7FFF8F4F2FFF6F1EEFFF5EFEBFFF5EEEAFF -F4ECE8FFF3EBE6FFF3E9E4FFF3E9E4FFF3E9E4FFF3E9E4FFF3E9E4FFF3E9E4FF -F3E9E4FFF3E9E4FFF3E9E4FFF2E8E3FFF2E7E2FFF1E6E0FFF0E5DEFFF0E4DEFF -EFE1DAFFEEDFD7FFECDCD4FFEBD8CFFFE9D4CAFF87AABBFF93AEBDFF9FB2BEFF -86B0C1FFA5C5D1FFC4D9E1FFFAF9F7FFF8F5F2FFF6F2EEFFF6F0ECFFF5EFEBFF -F4EDE8FFF4EBE7FFF4EBE7FFF4EBE7FFF4EBE7FFF4EBE7FFF4EBE7FFF4EBE7FF -F4EBE7FFF4EBE7FFF3EBE6FFF3EAE5FFF2E9E3FFF2E7E2FFF2E8E2FFF0E4DEFF -EFE2DCFFEEE0D9FFEDDDD5FFEBD9D0FFE9D4CAFF81A6B8FF95B0BEFFA7B9C4FF -B2CAD5FFA6C6D2FF9BC2CFFFFAF9F7FFF8F5F2FFF7F3F0FFD1C5BCFFD0C2B9FF -D0C0B6FFD0BEB3FFCFBCB0FFCFBAADFFCFB8AAFFCFB5A7FFCEB3A4FFCEB1A1FF -CEAF9EFFCEAD9BFFCDAB98FFCDA895FFCDA692FFCDA48FFFCCA28CFFCCA089FF -CC9E86FFCC9C84FFEDDED6FFEBDAD1FFE9D4CAFFB2C9D4FF8FAABAFF6F8DA1FF -BDD2DBFFA7C7D3FF90BCCBFFFAF9F7FFF8F6F3FFF7F3F0FFF6F1EEFFF5EFEBFF -F6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFF -F5EFEBFFF5EFEBFFF5EEEAFFF4EDE9FFF4ECE8FFF3EBE6FFF2E8E2FFF2E8E3FF -F1E6E0FFF0E3DDFFEEE0D9FFECDBD3FFE9D4CAFFBFD2DBFF8EA9B9FF618399FF -94BAC9FFA7C7D3FFBAD3DDFFFAF9F7FFF8F6F3FFF7F4F1FFF6F2EEFFF6F0ECFF -F6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFF -F5EFEBFFF6F1EDFFF6F0ECFFF5EFEBFFF5EEEAFFF3EBE6FFF3EAE5FFF2E8E3FF -F2E9E3FFF1E6E0FFEFE2DCFFEDDDD5FFE9D4CAFF90B2C2FF95B0BFFF9AAFBDFF -85B1C2FFA7C7D3FFCADCE4FFFAF9F7FFF8F6F4FFF7F4F1FFD5C7BEFFD6C8BFFF -F7F2EFFFD7C9BFFFD8C9C0FFF7F2EFFFDACAC1FFDACBC1FFF7F2EFFFDCCCC2FF -DDCCC3FFF6F1EDFFDECDC3FFDFCDC4FFF5EEEAFFE0CEC5FFE1CFC5FFF3EBE6FF -E3D0C6FFE3D0C6FFF1E6E0FFEDDDD5FFE9D4CAFF7EA6B8FF98B3C2FFB0C0CBFF -AFC9D5FFA7C7D3FFA0C5D2FFFAF9F7FFF8F6F4FFF8F4F1FFD5C7BEFFD6C8BFFF -F8F4F1FFD7C9BFFFD8C9C0FFF8F4F1FFDACAC1FFDACBC1FFF7F4F1FFDCCCC2FF -DDCCC3FFF7F3F0FFDECDC3FFDFCDC4FFF6F1EEFFE0CEC5FFE1CFC5FFF5EEEAFF -E3D0C6FFE3D0C6FFF1E6E0FFEEE0D9FFE9D4CAFFAEC6D2FF93AEBDFF7997AAFF -C4D6DEFFA7C7D4FF8BB9C9FFFAF9F7FFF9F6F4FFF9F6F4FFF9F6F4FFF9F6F4FF -F9F6F4FFF9F6F4FFF9F6F4FFF9F6F4FFF9F6F4FFF8F6F4FFF8F6F4FFF8F6F3FF -F8F6F3FFFAF9F7FFF8F5F2FFF8F5F2FFF8F4F2FFF7F4F1FFF7F3F0FFF7F2EFFF -F6F1EEFFF5EFEBFFF4ECE8FFF1E6E0FFE9D4CAFFC6D7DFFF90ACBBFF5E8399FF -9CC0CEFFA8C8D4FFB5D0DBFFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FF -FAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FF -FAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FF -FAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FF98B8C7FF97B2C1FF96ADBBFF -83B2C4FFA9C8D5FFCFDFE6FFAFCDD8FF8AB8C8FFA5C7D4FFCADCE3FFB3CFDAFF -8FBACAFFA1C5D2FFC5D9E1FFB7D1DBFF93BCCBFF9CC1CFFFC0D6DEFFBAD2DCFF -95BCCBFF94BCCBFFBAD2DBFFBED4DCFF97BCCAFF8DB5C5FFB4CDD7FFC2D5DEFF -99BBCAFF85ADC0FFADC8D3FFC6D7DFFF9BBAC9FF7AA4B7FF9AB6C4FFB9C7D1FF -ABC8D4FFA7C7D3FFA4C5D2FFA7C7D3FFABC9D5FFA7C7D3FFA2C3CFFFA5C5D1FF -AAC8D4FFA6C5D2FFA0C0CDFFA2C1CEFFA8C6D2FFA5C4D0FF9EBDCBFF9EBDCBFF -A5C2CFFFA4C2CEFF9CBAC8FF9AB9C7FFA1BECBFFA2BFCCFF9AB7C6FF97B4C3FF -9EBAC8FFA1BDCAFF98B4C3FF92AFBFFF99B5C4FF9DB9C7FF90ACBBFF84A0B1FF -CEDCE3EFA8C7D3EF83B2C3EF9FC1CEEFC4D6DEEFABC7D3EF84B0C0EF97BBC9EF -BDD2DBEFADC7D2EF84ADBEEF8DB3C3EFB6CCD6EFAFC7D2EF84ABBCEF84AABBEF -AEC5D0EFB3C7D1EF86A9B9EF7AA0B3EFA6BECBEFB6C8D3EF88A7B8EF7096AAEF -9DB7C4EFB9CBD3EF89A7B7EF648BA2EF93AEBDEFBCCCD5EF8BA6B6EF567F96EF -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 - - - -1D7E17B2-B25A-4C83-A4ED-096CB3B2FEEA -PartitionScheme -1152540388 -xwang -1152540389 -xwang -1524DE65-25B1-4C87-B44C-A4B82F767B04 -4 -D:\PDDEV\icons\PD Icons\Icons\PartitionScheme.ico -0D0000001000000010000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00FFFFFFFFFFFFFFFFFFFF8000FFFF8000FFFF8000FFFF -8000FFFF8000FFFF8000FFFF0000FFFE00000002800000028000000280000002 -8001000280FF0000FFFF0000FFFF000280000002800000028000000280000002 -8000FFFE0000FFFF0000FFFF8000FFFF8000FFFF8000FFFF8000FFFF8001FFFF -80FFFFFFFFFFFFFFFFFF424D3608000000000000360400002800000020000000 -200000000100080000000000000400000000000000000000000000007A000000 -000000000098CD0005700A0006710B000EA2D10010A1D300118A1E0011B42A00 -128D2100128D22001290210013A2D30013B5290013B52D0013B62E0014B63200 -15B6300016B735001791240017AA370017B7370018A52E0018AC390018B83900 -199A32001AB63D001B952B001C9026001CB941001CBA42001DBA43001F902900 -1F9A30001FBC3E00209C320020B9490020BB4A0021B93300229E340022BC4E00 -22BE420023912C00239C340023A4390023BA3900248D2D0024BD530026932F00 -28AA3F0029AC41002AC84D002BAD43002BC44D002CA33B002CAE44002CAF4500 -2CB146002FB54A0030B54B0030B74B0030B84C0032A5400033CB58003BB6DC00 -3EC24B0040C3500042DC6A0043DD6B0043DE6C0045E06F0046DF6F0046E06F00 -46E2700047BCDF0047D46C0050C6600054CA65005DCC670060CA6E0062CCFE00 -63CCFE0065CDFE0065CF730066CDFE0067D1FE0069D1FE006ACEFE006CCFFE00 -6CD6FE006DCFFE006DD6FE006FD0FE006FD8FE0070D0FE0070D1FE0071CF7C00 -71DBFE0074D5FE0076D2FE0077D4820078D3FE0079D5EF007CD7FE007DD6FE00 -7DE6FE007FD5FE0081D6FE0082D58A0085DAFE0089DA91008BDBFE008DE3FE00 -8EDEFE0095E0FE0098E3FE0098FFFF009CE6F800A2E7FE00A3E8FE00A4E9FE00 -B0EFFE00BFF8FF00000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000010101010101010101010101010101 -0000000000000000000000000000000000015C58585858585858585858586001 -000000000000000000000000000000000001544F4F4F4F4F4F4F4F4F4F4F5801 -000000000000000000000000000000000001554F4F4F4F4F4F4F4F4F4F4F5801 -000000000000000000000000000000000001615D5D595756534F4F4F4F4F5801 -00000000000000000000000000000000000166646464646464625B514F4F5801 -0000000000000000000000000000000002016C6A6A6A6A6A6A6A6A695E505A01 -000000000000000000000000000000020201706E6E6E6E6E6E6E6E6E6E675C01 -0202020202030202020202020202000200017271717171717171717171716F01 -021616161306392A2B2D161616020002000177757575760B0101010101010101 -02141D23093B432B18271E170E02000200017478787865056868686868680473 -021119083C442B182E241C140D0200020001497979793F010101010101010100 -020F0A314530222020202A2A0702000200010101010101010000000000000000 -0210151A48322128343E4A2A0C02020200000000000000000000000000000000 -022C12384237262020202A2A2502020200000000000000000000000000000000 -02414B1B3C4733354C4C4C4C4002000200010101010101010101010101010101 -0241524E1F3B46333D5252524002000200015C58585858585858585858586001 -024D63635F293946362D63634D0200020001544F4F4F4F4F4F4F4F4F4F4F5801 -024D6D6D6D6B2F3A2A2D6D6D4D0200020001554F4F4F4F4F4F4F4F4F4F4F5801 -020202020202020202020202020200020001615D5D595756534F4F4F4F4F5801 -00000000000000000000000000000002020166646464646464625B514F4F5801 -0000000000000000000000000000000002016C6A6A6A6A6A6A6A6A695E505A01 -000000000000000000000000000000000001706E6E6E6E6E6E6E6E6E6E675C01 -0000000000000000000000000000000000017271717171717171717171716F01 -00000000000000000000000000000000000177757575760B0101010101010101 -0000000000000000000000000000000000017478787865056868686868680473 -000000000000000000000000000000000001497979793F010101010101010100 -0000000000000000000000000000000000010101010101010000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 - - - -8A310636-1783-43AE-A76D-75CF4A8270E0 -PartitionFunction -1152541084 -xwang -1152541085 -xwang -ADD555C9-7EA1-4532-B85C-437B77250D17 -4 -D:\PDDEV\icons\PD Icons\Icons\PartitionFunction.ico -0D0000001000000010000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00FFFFFFFFFFFFFFFFFFFF8000FFFF8000FFFF8000FFFF -8000FFFF8000FFFF8000FFFF0000000200000002800000028000000280000002 -8001000280FF0000FFFF0000FFFF000280000002800000028000000280000002 -800000020000FFFF0000FFFF8000FFFF8000FFFF8000FFFF8000FFFF8001FFFF -80FFFFFFFFFFFFFFFFFF424D3608000000000000360400002800000020000000 -2000000001000800000000000004000000000000000000000000000081000000 -000000000098CD000EA2D10010A1D30013A2D3003BB6DC0047BCDF0062CCFE00 -63CCFE0065CDFE0066CDFE0067D1FE0069D1FE006ACEFE006CCFFE006CD6FE00 -6DCFFE006DD6FE006FD0FE006FD8FE0070D0FE0070D1FE0071DBFE0074D5FE00 -76D2FE0078D3FE0079D5EF007CD7FE007DD6FE007DE6FE007FD5FE0081D6FE00 -85DAFE008BDBFE008DE3FE008EDEFE0095E0FE0098E3FE0098FFFF009CE6F800 -9D4E0000A2E7FE00A3E8FE00A4E9FE00B0EFFE00BFF8FF00C7772900CA833D00 -CC650000CE935900E1C2A500F0953C00F0994300F1984100F1994100F19A4500 -F19B4600F1A85F00F29D4900F29E4A00F29E4B00F2AC6600F39F4D00F3A04E00 -F3A04F00F3A14F00F3A15000F3A25200F3A35300F3AD6900F3AE6A00F4A35400 -F4A45400F4A45500F4A55700F4A65800F4AF6C00F4B06E00F4B16F00F5A85C00 -F5AB6200F5B27100F5B37300F5B47500F5B57600F5B67800F6AA5F00F6AA6000 -F6AD6400F6AD6500F6B06A00F6B77900F6B97C00F7B06900F7BB8000F8BF8700 -F8BF8800F8C08800F8C08900F8C28D00F9C38E00F9C79400F9C89500FAC59100 -FACB9C00FACC9D00FAD0A400FAD0A500FBCEA100FBD2A800FBD4AC00FBD8B400 -FCD8B300FCDCBB00FCDCBC00FCDFC100FDE0C200FDE2C800FDE3CA00FDE4CC00 -FDE5CE00FEE7D000FEE7D200FEE8D300FEEAD700FEEAD800FEEDDC00FEEFE100 -FEF2E50000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000010101010101010101010101010101 -000000000000000000000000000000000001130F0F0F0F0F0F0F0F0F0F0F1601 -0000000000000000000000000000000000010B07070707070707070707070F01 -0000000000000000000000000000000000010C07070707070707070707070F01 -000000000000000000000000000000000001171414100E0D0A07070707070F01 -0000000000000000000000000000000000011B19191919191918120907070F01 -000000000000000000000000000000002801201F1F1F1F1F1F1F1F1E15081101 -28282828282828282828282828280028280123212121212121212121211C1301 -282F5E5C5B54524E4C453D392E28002800012524242424242424242424242201 -285F3030433E3A37303330343928002800012B2929292A040101010101010101 -2864563030433F30303530303D2800280001272C2C2C1A031D1D1D1D1D1D0226 -28675857304A44303B383630462800280001062D2D2D05010101010101010100 -286C5D59304F4A30403B38304D28002800010101010101010000000000000000 -28776D66305A4F3047413C305128282800000000000000000000000000000000 -287A7030303063304B4842305328282800000000000000000000000000000000 -287C7170306A6830304F30305528002800010101010101010101010101010101 -287E7471306E6A68306030495C2800280001130F0F0F0F0F0F0F0F0F0F0F1601 -287F767430306E6A686560505E28002800010B07070707070707070707070F01 -288079767430306E6B6965616228002800010C07070707070707070707070F01 -2832807F7E7D7B787573726F312800280001171414100E0D0A07070707070F01 -2828282828282828282828282828002828011B19191919191918120907070F01 -000000000000000000000000000000002801201F1F1F1F1F1F1F1F1E15081101 -00000000000000000000000000000000000123212121212121212121211C1301 -0000000000000000000000000000000000012524242424242424242424242201 -0000000000000000000000000000000000012B2929292A040101010101010101 -000000000000000000000000000000000001272C2C2C1A031D1D1D1D1D1D0226 -000000000000000000000000000000000001062D2D2D05010101010101010100 -0000000000000000000000000000000000010101010101010000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 - - - - - -894C2961-F591-4512-A522-CFB5B8BB46EF -General -0 - -1277121181 -psitja -Target DBMS identification - - -8DE52C31-4FDF-42A6-9113-DAD8414D29BB -SqlSupport -0 - -0 - -SQL syntax allowed. This does not impact the script generation, but it impacts the SQL Preview -1 - - -554059F4-FB7F-48D4-959E-0ABD0C6BE5BD -EnableMultiCheck -0 - -0 - -Determines if the generation of multiple check parameters is authorized or not -1 - - -F7AF6D31-12BB-4EED-BBBB-0AFA7612E88C -EnableCheck -0 - -0 - -Determines if the generation of check parameters is authorized or not -1 - - -DC9B6DDC-44B7-488C-BA1E-2C965A5560BE -SchemaStereotype -1271174385 -psitja -1278512516 -psitja -Specifies the user stereotype to be used to indicate a schema (object owner) -Schema - - -A7D17634-D620-447E-9C44-9F515BAFB781 -Enableconstname -0 - -0 - -Determines if constraint names are used during the generation -1 - - -BD47D27A-9116-4B70-AC97-F270FC8E99DE -UniqConstName -0 - -0 - -Determines if unique constraint names for objects are authorized or not -1 - - -CC60BA3A-7C9F-4631-BCA8-A2406802026C -UserStereotype -1271174385 -psitja -1278512516 -psitja -Specifies the user stereotype to be used to indicate a user (permissions grantee) -User - - - - -929B15D3-4308-4979-BD1F-476C85F6ABCA -Script -0 - -0 - -DBMS characteristics, command definition, and data type translations for the Script generation and reverse engineering - - -64EB33A8-8E73-489B-9162-76B91482A740 -Sql -0 - -1334855171 -mledier -Contains sub-categories Syntax, Format, File and Keywords. Each sub-category contains entries whose values define general syntax for the database - - -613CEA53-F59B-493B-9C4B-2354C23E6C29 -Syntax -0 - -0 - -Contains general parameters for SQL syntax - - -08830285-01A3-4F3B-81E1-33E2F4466521 -Terminator -0 - -0 - -End of command character -; - - -BCBFFF82-1936-485D-91EA-321B4DB75998 -BlockTerminator -0 - -0 - -End of block character -go - - -F91659BC-EB1B-4947-8BEC-99AFD1B5D71F -Delimiter -0 - -0 - -Field separation character. Example: col1, col2, col3 -, - - -3DA2FF92-5AFF-4C53-958B-3719481170ED -Quote -0 - -0 - -Character used to enclose string values -' - - -372AE37B-E2E3-4292-B8E2-76B6CBAE6F31 -SqlContinue -0 - -0 - -Continuation character - - -BE595A22-E066-4E9B-B7EE-F6695AA35DD4 -BlockComment -0 - -0 - -Characters used to enclose a multi-line comment -/* */ - - -E7E76C1F-F56C-4894-B61B-733110F862B9 -LineComment -0 - -0 - -Characters used to enclose a single line comment --- - - -226186CD-83F5-4678-9454-EB2F20CB4019 -IdentifierDelimiter -0 - -0 - -Define the identifier delimiter. Check the value to use as default for database generation. -When begin and end delimiter are different, they must be separated by a space character. -" -" -[ ] - - -08BBE218-2864-468B-A9E9-E998A93EE74F -UsingBlockTerm -create trigger, %TERMINATOR%, 1 -{ - begin, end, 3 - begin transaction,,4 - begin tran,,4 - case, end, 4 -} -create procedure,%TERMINATOR%, 1 -{ - begin, end, 3 - begin transaction,,4 - begin tran,,4 - case, end, 4 -} -create function,%TERMINATOR%, 1 -{ - begin, end, 3 - begin transaction,,4 - begin tran,,4 - case, end, 4 -} -0 - -0 - -List of objects using block definition during reverse. - - -C4134A06-8D4D-4755-B716-3EA885E44315 -UseBlockTerm -0 - -0 - -Use end of block character by default -1 - - - - -CBF825DE-B69B-4CD9-9060-26A7F081ACF0 -Format -0 - -0 - -Contains entries that define script formatting - - -1CE3E558-1C31-4E94-BEA3-66A26CB7A9AE -IllegalChar -0 - -0 - -Invalid characters for names -" +-*/\!=%<>'"()". - - -04A55FD8-E45A-4CE4-813B-DBEE49366CA6 -UpperCaseOnly -0 - -0 - -Uppercase only - - -04E51BAC-7FF4-48E1-B55C-600739E5ABF2 -LowerCaseOnly -0 - -0 - -Lowercase only - - -63335789-080B-4A8B-AA1B-0ECA9C389D82 -EnableOwnerPrefix -0 - -0 - -Object codes can have a prefix made of the object owner code -1 - - - - -D6EE988A-03EC-422B-9E0D-152541840783 -File -0 - -0 - -Contains header, footer and usage text entries used during the generation - - -ADE29E68-3C97-4D95-8EC0-ABA6B58B6D9A -Header -0 - -0 - -Header text for a database generation script - - -DB426071-2FCA-4B5B-9F43-C718AC13C9B5 -Footer -0 - -0 - -Footer text for a database generation script - - -89DC8A7B-9A04-4812-98FF-B27E8BDA2790 -EnableMultiFile -0 - -0 - -Multi-script allowed -1 - - -43C8F8EB-188A-4BA2-94F3-CCCC7FD1E44D -ScriptExt -0 - -0 - -Main script extension in database generation -sql - - -7410692A-6FA3-423A-B1E1-6A141413FFB3 -TableExt -0 - -0 - -Other scripts extension in database generation -tab - - -B53BADE5-E464-4288-AB40-63BB9A776E51 -StartCommand -0 - -0 - -Command for executing a script -isql %NAMESCRIPT% - - -DE2613C0-1C1F-425D-984E-8970DF9AF178 -Usage1 - (1) Go to the directory %PATHSCRIPT% - (2) Run the database creation script: - isql -U<username> -P<password> < %NAMESCRIPT% -0 - -0 - -Usage for a single script in database generation - - -A94DFD76-D13C-4315-B43F-D4003FC7D642 -Usage2 - (1) Go to the directory %PATHSCRIPT% - (2) Run the database creation scripts one by one: - isql -U<username> -P<password> < <script_name> -0 - -0 - -Usage for multiple scripts in database generation - - -7AB5DD52-012F-499E-86DB-AF7657041BAF -TriggerExt -0 - -0 - -Script file extension in triggers and procedures generation -trg - - -5695BE66-1770-4549-BFF9-A6506D5EB6F3 -TrgUsage1 - (1) Go to the directory %PATHSCRIPT% - (2) Run the trigger creation script: - isql -U<username> -P<password> < %NAMESCRIPT% -0 - -0 - -Usage for a single script in triggers and procedures generation - - -ABADF591-D64C-44D0-878C-1414BC8A505F -TrgUsage2 - (1) Go to the directory %PATHSCRIPT% - (2) Run the trigger creation scripts: - isql -U<username> -P<password> < <script_name> -0 - -0 - -Usage for multiple scripts in triggers and procedures generation - - - - -86073EE7-3280-4236-A851-F172BC636E9A -Keywords -0 - -1334855171 -mledier -Contains the list of reserved words and functions available in SQL - - -A9A3C1EB-D579-4134-B39C-E0D9A0793366 -ReservedDefault -user -null -current_user -session_user -system_user -current_timestamp -getdate() -user_id() -user_name() -db_id() -db_name() -host_id() -host_name() -newid() -suser_id() -suser_name() -suser_sname() -0 - -0 - -Reserved default values - - -A6FB1E8A-D482-45CF-9833-2594B6FB1D33 -GroupFunc -avg() -checksum_agg() -count() -count_big() -grouping() -grouping_id() -max() -min() -rowcount_big() -stdev() -stdevp() -sum() -var() -varp() - -0 - -1299595242 -Marc -List of SQL functions to use with group keywords. - - -070D5137-F3EF-41EF-B9FA-4CDC2D32ABD5 -ConvertAnyToString -convert(varchar, %@1%) -1334854692 -mledier -1334855171 -mledier -Returns the conversion function to get the a string from any other type. Input is GTL-like entry. -1 - - -83211462-9403-4B2D-B26F-C63708ED3FBF -ConvertDateToMonth -datepart(month, %@1%) -1334852700 -mledier -1334855171 -mledier -Returns the conversion function to get the month number from a date. Input is GTL-like entry. -1 - - -6D92AA17-AC71-4AB2-AC29-18B50017E321 -ConvertDateToQuarter -datepart(quarter, %@1%) -1334852700 -mledier -1334855171 -mledier -Returns the conversion function to get the quarter number from a date. Input is GTL-like entry. -1 - - -477E5373-06FA-4140-97BC-FA5F032A6F15 -ConvertDateToYear -datepart(year, %@1%) -1334852093 -mledier -1334855171 -mledier -Returns the conversion function to get the year number from a date. Input is GTL-like entry. -1 - - -C7DA05D0-A7C6-4CD1-B5E5-08B13956308E -NumberFunc -abs() -acos() -asin() -atan() -atn2() -ceiling() -cos() -cot() -degrees() -exp() -floor() -log() -log10() -pi() -power() -radians() -rand() -round() -sign() -sin() -sqrt() -square() -tan() - -0 - -0 - -List of SQL functions used on numbers - - -D86F282A-C148-4A16-974E-DCACC0E6CE28 -CharFunc -ascii() -char() -charindex() -difference() -left() -len() -lower() -ltrim() -nchar() -patindex() -quotename() -readtext() -replace() -replicate() -reverse() -right() -rtrim() -soundex() -space() -str() -stuff() -substring() -textptr() -textvalid() -unicode() -updatetext() -upper() -writetext() - -0 - -1299595242 -Marc -List of SQL functions for characters and strings - - -29574A7B-F7C0-4FED-9E05-2D11C6E638E8 -DateFunc -current_timestamp() -dateadd() -datediff() -datename() -datepart() -day() -getdate() -getutcdate() -isdate() -month() -switchoffset() -sysdatetime() -sysdatetimeoffset() -sysutcdatetime() -todatetimeoffset() -year() - -0 - -0 - -List of SQL functions for dates - - -AE06DA05-89D5-4FEE-82D9-EA7A4B618094 -ConvertFunc -cast() -convert() -0 - -0 - -List of SQL functions used to convert values between hex and integer and handling strings - - -82B7824E-8D55-4C6C-AAFA-289A56D4D759 -OtherFunc -app_name() -assemblyproperty() -asymkey_id() -asymkeyproperty() -binary_checksum() -cert_id() -certproperty() -checksum() -coalesce() -col_length() -col_name() -collationproperty() -columnproperty() -columns_updated() -connectionproperty() -containstable() -context_info() -current_request_id() -current_timestamp() -current_user() -cursor_status() -database_principal_id() -databaseproperty() -databasepropertyex() -datalength() -db_id() -db_name() -decryptbyasmkey() -decryptbycert() -decryptbykey() -decryptbykeyautocert() -decryptbypassphrase() -dense_rank() -encryptbyasmkey() -encryptbycert() -encryptbykey() -encryptbypassphrase() -error_line() -error_message() -error_number() -error_procedure() -error_severity() -error_state() -eventdata() -file_id() -file_idex() -file_name() -filegroup_id() -filegroup_name() -filegroupproperty() -fileproperty() -fn_helpcollations() -fn_listextendedproperty() -fn_servershareddrives() -fn_virtualfilestats() -formatmessage() -freetexttable() -fulltextcatalogproperty() -fulltextserviceproperty() -get_filestream_transaction_context() -getansinull() -has_perms_by_name() -hashbytes() -host_id() -host_name() -ident_current() -ident_incr() -ident_seed() -identity() -index_col() -indexkey_property() -indexproperty() -is_member() -is_srvrolemember() -isdate() -isnull() -isnumeric() -key_guid() -key_id() -key_name() -min_active_rowversion() -newid() -newsequentialid() -ntile() -nullif() -object_definition() -object_id() -object_name() -object_schema_name() -objectproperty() -objectpropertyex() -opendatasource() -openquery() -openrowset() -openxml() -original_login() -parsename() -pathname() -permissions() -publishingservername() -pwdcompare() -pwdencrypt() -rank() -row_number() -rowcount_big() -schema_id() -schema_name() -scope_identity() -serverproperty() -session_user() -sessionproperty() -setuser() -signbyasymkey() -signbycert() -sql_variant_property() -stats_date() -suser_id() -suser_name() -suser_sid() -suser_sname() -symkeyproperty() -sys.dm_db_index_physical_stats() -sys.fn_builtin_permissions() -sys.fn_my_permissions() -system_user() -tertiary_weights() -trigger_nestlevel() -type_id() -type_name() -typeproperty() -update() -user_id() -user_name() -verifysignedbyasmkey() -verifysignedbycert() -xact_state() - -0 - -1299595242 -Marc -List of other SQL functions - - -C9A4C290-E53B-4436-B2F4-E73B622DFD8B -ListOperators -= -!= -<> -> -!> -< -!< ->= -<= -*= -=* -not -in -not in -between -not between -exists -not exists -like -not like -is -is not -= any -<> any -> any -< any ->= any -<= any -= all -<> all -> all -< all ->= all -<= all -0 - -0 - -List of operators for comparing values, boolean, and various semantic operators - - -B8067E03-BEA6-4542-B9DB-AA3B3B758A8A -Commit -commit -0 - -0 - -Command for validating the transaction by OBDC - - -F3DA0F90-5FD6-4826-AB1B-51D77014CF4D -ReservedWord -ADD -ALL -ALTER -AND -ANY -AS -ASC -AUTHORIZATION -BACKUP -BEGIN -BETWEEN -BREAK -BROWSE -BULK -BY -CASCADE -CASE -CHECK -CHECKPOINT -CLOSE -CLUSTERED -COALESCE -COLLATE -COLUMN -COMMIT -COMPUTE -CONSTRAINT -CONTAINS -CONTAINSTABLE -CONTINUE -CONVERT -CREATE -CROSS -CURRENT -CURRENT_DATE -CURRENT_TIME -CURRENT_TIMESTAMP -CURRENT_USER -CURSOR -DATABASE -DBCC -DEALLOCATE -DECLARE -DEFAULT -DELETE -DENY -DESC -DISK -DISTINCT -DISTRIBUTED -DOUBLE -DROP -DUMP -ELSE -END -ERRLVL -ESCAPE -EXCEPT -EXEC -EXECUTE -EXISTS -EXIT -EXTERNAL -FETCH -FILE -FILLFACTOR -FOR -FOREIGN -FREETEXT -FREETEXTTABLE -FROM -FULL -FUNCTION -GOTO -GRANT -GROUP -HAVING -HOLDLOCK -IDENTITY -IDENTITYCOL -IDENTITY_INSERT -IF -IN -INDEX -INNER -INSERT -INTERSECT -INTO -IS -JOIN -KEY -KILL -LEFT -LIKE -LINENO -LOAD -MERGE -NATIONAL -NOCHECK -NONCLUSTERED -NOT -NULL -NULLIF -OF -OFF -OFFSETS -ON -OPEN -OPENDATASOURCE -OPENQUERY -OPENROWSET -OPENXML -OPTION -OR -ORDER -OUTER -OVER -PERCENT -PIVOT -PLAN -PRECISION -PRIMARY -PRINT -PROC -PROCEDURE -PUBLIC -RAISERROR -READ -READTEXT -RECONFIGURE -REFERENCES -REPLICATION -RESTORE -RESTRICT -RETURN -REVERT -REVOKE -RIGHT -ROLLBACK -ROWCOUNT -ROWGUIDCOL -RULE -SAVE -SCHEMA -SECURITYAUDIT -SELECT -SESSION_USER -SET -SETUSER -SHUTDOWN -SOME -STATISTICS -SYSTEM_USER -TABLE -TABLESAMPLE -TEXTSIZE -THEN -TO -TOP -TRAN -TRANSACTION -TRIGGER -TRUNCATE -TSEQUAL -UNION -UNIQUE -UNPIVOT -UPDATE -UPDATETEXT -USE -USER -VALUES -VARYING -VIEW -WAITFOR -WHEN -WHERE -WHILE -WITH -WRITETEXT -0 - -1299595242 -Marc -Reserved words - - -C5786CE8-F848-4534-9092-C2F44B30DC71 -StringConcatenationOperator -+ -1334854662 -mledier -1334855171 -mledier -Which operator is used to concatenate two strings -1 - - - - - - -4A3AED4A-4BC5-4706-AAF6-6E7314AD06DD -Objects -0 - -1277121181 -psitja -Contains sub-categories for each type of object in the database, for example: Table, or Reference. Each sub-category contains entries whose values define database commands and object-related characteristics - - -FB7D3682-781D-4DA6-BCA9-8389E22517CE -Abstract Data Type -ADT -0 - -0 - -The following system variables are available: - "ADT" // generated code of the abstract data type - "TYPE" // ADT type - "SIZE" // ADT size - "FILE" // ADT: Java file - "ISARRAY" // ADT: is an array? - "ISLIST" // ADT: is a list? - "ISSTRUCT" // ADT: is a structure? - "ISOBJECT" // ADT: is an object? - "ISJAVA" // ADT: is a JAVA class? - "ADTDEF" // ADT definition - - - -27C6AA5E-EA1E-4D76-8AA2-5E130FF09187 -Enable -0 - -0 - -Abstract Data Types allowed -1 - - -A0CDBC46-FE8C-452E-977A-0C94EF5390F5 -Create -create type [%QUALIFIER%]%ADT%[%TYPE%=CLR? - external name [%Assembly.GeneratedName%[.%Class%]]: - as table ( - %ADTDEF% - )] - -0 - -0 - -Command for adding an abstract data type - - -25A700EC-35E5-4A29-837D-E4143DBD14E4 -Drop -drop type [%QUALIFIER%]%ADT% -0 - -0 - -Command for dropping an abstract data type - - -C5475A30-2D15-40FB-96FA-EFC6C72678A2 -SqlListQuery -{ OWNER, ADT, DTTPCODE, LENGTH, PREC, Type, Mandatory } - -select - u.name, - t.name, - s.name, - t.precision, - t.scale, - 'CLR', - ~ t.is_nullable -from - [%CATALOG%.]sys.types t - join [%CATALOG%.]sys.schemas u on (u.schema_id = t.schema_id) - join [%CATALOG%.]sys.types s on (s.system_type_id = t.system_type_id and ((s.system_type_id = s.user_type_id) or (t.is_assembly_type=1 and t.name=s.name))) -where - t.is_assembly_type = 1 -[ and u.name=%.q:SCHEMA%] -order by 1, 2 -0 - -0 - -SQL query to list objects - - -CC97A775-904F-40AC-9AD5-94E47DAF4B5F -EnableAdtOnColn -0 - -0 - -Abstract Data Types allowed on column -1 - - -2FE49E1D-8964-462A-B486-274248A445BB -AllowedADT -CLR -Table - -0 - -0 - -List of ADTs which can be used as the datatype of an ADT type - - - - -CA45C83A-A866-4E3E-8C5C-6A0550424268 -Table -TABL -0 - -1327683960 -mledier -The following system variables are available: - "TABLE" // generated code of the table - "TNAME" // name of the table - "TCODE" // code of the table - "TLABL" // comment of the table - "PKEYCOLUMNS" // list of primary key columns. Eg: A, B - "TABLDEFN" // complete body of the table definition. Contains definition of columns, checks and keys - - - -7301398C-DA91-44D4-810D-D0E518446B12 -Enable -0 - -0 - -Table allowed -1 - - -C14AE06F-B8C9-4627-A393-6E2D87CF3FD4 -Maxlen -0 - -0 - -Maximum object code length -128 - - -A693AA94-0998-433B-88BF-14E2C6220EF8 -ConstName -CKT_%.U26:TABLE% -0 - -0 - -Constraint name template for check of table - - -7275A2D5-88C5-41DC-9B52-DBA2DF8105E1 -Create -create table [%QUALIFIER%]%TABLE% ( - %TABLDEFN% -) -[%OPTIONS%] -0 - -0 - -Command for creating a table. Example: create table %TABLE% - - -7AEE9A5E-E6D8-48CD-90F8-EB55E5CD1EFD -BeforeDropEx -[%OLD.dropColumnDefaultConstraint%\n]\ -1268403311 -mledier -1269267132 -mledier - - -CB93D113-8E9A-4015-9F6A-CCF11D124F20 -Options -on : composite=yes, chldmand=yes -{ - <partition scheme> %s : composite=yes, parenthesis=yes, chldmand=yes - { - <column> %s - } - <filegroup> %s : category=storage, default="DEFAULT" -} -textimage_on %s : category=storage, default="DEFAULT" -filestream_on : composite=yes, chldmand=yes -{ - <partition scheme> %s : composite=yes, parenthesis=yes, chldmand=yes - { - <column> %s - } - <filegroup> %s : category=storage, default="DEFAULT" -} -with : composite=yes, separator=yes, parenthesis=yes -{ - data_compression = %s : list=none|row|page, composite=yes, multiple=yes - { - on partitions : composite=yes, parenthesis=yes, separator=yes - { - <expression_or_range> %s : multiple=yes - } - } -} - - - -0 - -0 - -Available options for creating a table - - -1C8505EB-CA23-474B-B331-CD263A87860C -DefOptions -0 - -0 - -Default values for table options - - -3B7A88BA-EAA9-4532-91ED-CE35D74818A4 -TableComment -[if exists (select 1 from sys.extended_properties - where major_id = object_id('[%QUALIFIER%]%TABLE%') and minor_id = 0) -begin - [%OWNER%?[.O:[execute ][exec ]]sp_dropextendedproperty [%R%?[N]]'MS_Description', - [%R%?[N]]'schema', [%R%?[N]]%.q:OWNER%, [%R%?[N]]'table', [%R%?[N]]%.q:TABLE% -:declare @CurrentUser sysname -select @CurrentUser = user_name() -[.O:[execute ][exec ]]sp_dropextendedproperty [%R%?[N]]'MS_Description', - [%R%?[N]]'schema', [%R%?[N]]@CurrentUser, [%R%?[N]]'table', [%R%?[N]]%.q:TABLE% -] -end - - -][%OWNER%?[.O:[execute ][exec ]]sp_addextendedproperty [%R%?[N]]'MS_Description', - [%R%?[N]]%.q:COMMENT%, - [%R%?[N]]'schema', [%R%?[N]]%.q:OWNER%, [%R%?[N]]'table', [%R%?[N]]%.q:TABLE% -:select @CurrentUser = user_name() -[.O:[execute ][exec ]]sp_addextendedproperty [%R%?[N]]'MS_Description', - [%R%?[N]]%.q:COMMENT%, - [%R%?[N]]'schema', [%R%?[N]]@CurrentUser, [%R%?[N]]'table', [%R%?[N]]%.q:TABLE% -] -0 - -1411699000 -Administrator -Command for adding a table comment using an extended property of MS SQL Server 2000 to store object description: "MS_Description". - - -68902B08-5E21-4A7B-AA68-320BA6DF87BA -Drop -if exists (select 1 - from sysobjects - where id = object_id('[%QUALIFIER%]%TABLE%') - and type = 'U') - drop table [%QUALIFIER%]%TABLE% -0 - -0 - -Command for dropping a table. Example: drop table %TABLE% - - -5E6BEE0F-2F40-463A-886B-6BC8F1B5F64D -Rename -[%R%?[exec ]][execute ]sp_rename %OLDTABL%, %NEWTABL% -0 - -0 - -Command for renaming a table - - -E66DF94F-56EA-4094-866B-D999E5F67D3C -AlterTableHeader -0 - -0 - -Alter table header - - -E638220D-5FC4-4678-8667-4171A36745A2 -AlterTableFooter -0 - -0 - -Alter table footer - - -7BF7DDC7-D9D5-4954-8B36-5C8894183B7C -DropTableCheck -alter table [%QUALIFIER%]%TABLE% - drop constraint %CONSTNAME% -0 - -0 - -Command for dropping a table check in an alter table statement - - -7E55B1B8-1138-4C9A-9B8A-6CD70BF6723F -Permission -0 - -1277123454 -psitja -Available permission for a table. -The first column is the SQL name of permission (eg: SELECT) -The second column is a shortname to display in a grid -SELECT=Select -INSERT=Insert -DELETE=Delete -UPDATE=Update -REFERENCES=Refr. -SELECT -INSERT -DELETE -UPDATE -REFERENCES - - - -BEA490D6-8735-4D80-982B-AC4328B38B2F -DefineTableCheck -[[constraint %CONSTNAME% ]check [%ExtCktNotForReplication%?not for replication ](%.A:CONSTRAINT%)] -0 - -0 - -Allows to customize the script of table check constraints - - -CCE73521-F511-4AC9-9B2B-425DFA312D04 -AddTableCheck -alter table [%QUALIFIER%]%TABLE%[%R%?[ with nocheck][ with check]] - add [constraint %CONSTNAME% ]check [%ExtCktNotForReplication%?not for replication ](%.A:CONSTRAINT%) -0 - -0 - -Allows to customize the script for modifying table constraints within an alter table statement - - -531D36AB-E6D9-4DD8-A217-38801D3874D2 -SqlOptsQuery -{OWNER ID, TABLE ID, OPTIONS EX ...} - -select - u.name as A, - t.name as B, - case(i.data_space_id & 65536) - when 0 then - (select 'on ' + f.name from [%CATALOG%.]sys.filegroups f where f.data_space_id = i.data_space_id) - when 65536 then - (select 'on ' + p.name + ' (%SqlPartitionCols.' + convert(varchar(30), t.object_id) + '.' + convert(varchar(30), i.index_id) + '%)' - from [%CATALOG%.]sys.partition_schemes p where p.data_space_id = i.data_space_id) - else '' - end - + case(t.filestream_data_space_id & 65536) - when 0 then (select ' filestream_on ' + f.name from [%CATALOG%.]sys.filegroups f where f.data_space_id = t.filestream_data_space_id) - when 65536 then (select ' filestream_on ' + p.name from [%CATALOG%.]sys.partition_schemes p where p.data_space_id = t.filestream_data_space_id) - else '' - end - collate database_default as C, - i.index_id as D -from - [%CATALOG%.]sys.tables t - join [%CATALOG%.]sys.schemas u on (u.schema_id = t.schema_id) - join [%CATALOG%.]sys.indexes i on (i.object_id = t.object_id and i.index_id in (0, 1, 255)) -where 1=1 -[ and u.name = %.q:OWNER%] -[ and t.name = %.q:TABLE%] -order by 1, 2, 4 - -0 - -1328262548 -mledier -SQL query to reverse object physical options - - -83C71C81-13A3-4C8C-BD02-3D09EE4B7E9E -SqlChckQuery -{OWNER, TABLE, CONSTNAME, ExtCktNotForReplication, CONSTRAINT} - -select - u.name, - t.name, - o.name, - case (k.status & 0x00200000) when 0x00200000 then 'true' else 'false' end, - m.text -from - [%CATALOG%.]sys.sysconstraints k - join [%CATALOG%.]sys.sysobjects t on (t.id = k.id) - join [%CATALOG%.]sys.schemas u on (u.schema_id = t.uid) - join [%CATALOG%.]sys.sysobjects o on (o.id = k.constid and o.xtype = 'C') - join [%CATALOG%.]sys.syscomments m on (m.id = o.id) -where k.colid=0 -[ and t.name = %.q:TABLE%] -[ and u.name = %.q:OWNER%] - -0 - -0 - -SQL query to reverse object check constraints - - -6FB61D12-870E-4D6E-8F74-ED4D2C8C1CFD -SqlPermQuery -{GRANTEE ID, PERMISSION ...} - -select - v.name, - case (s.action) when 193 then 'SELECT' when 195 then 'INSERT' when 196 then 'DELETE' - when 197 then 'UPDATE' when 26 then 'REFERENCES' end + - case (s.protecttype) when 204 then '+' when 205 then '' when 206 then '-' end + ',' as coln -from - [%CATALOG%.]sys.sysprotects s - join [%CATALOG%.]sys.schemas v on (v.schema_id = s.uid) - join [%CATALOG%.]sys.sysobjects o on (o.id = s.id) - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.uid) -where - (s.columns IS NULL or s.columns = 1) -[ and o.name = %.q:TABLE%] -[ and u.name = %.q:OWNER%] -order by 1 - -0 - -1285255467 -psitja -SQL query to reverse object permissions - - -9DFD061A-4B20-468B-972D-775FDCB0512F -InsertIdentityOn -set identity_insert [%QUALIFIER%]%@OBJTCODE% on -0 - -0 - -Command for enabling insertion of data into table containing identity column - - -7C35B8A9-27A8-479F-8CF3-D3E651B08804 -InsertIdentityOff -set identity_insert [%QUALIFIER%]%@OBJTCODE% off -0 - -0 - -Command for disabling insertion of data into table containing identity column - - -5EEC67E4-A356-4078-8EF0-F21B628568AB -SqlAttrQuery -0 - -1328262548 -mledier -{OWNER, TABLE, OPTIONS} - -select - u.name as A, - t.name as B, - case(i.data_space_id & 65536) - when 0 then - (select 'on ' + f.name from [%CATALOG%.]sys.filegroups f where f.data_space_id = i.data_space_id) - when 65536 then - (select 'on ' + p.name + ' (A)' - from [%CATALOG%.]sys.partition_schemes p where p.data_space_id = i.data_space_id) - else '' - end - + case(t.filestream_data_space_id & 65536) - when 0 then (select ' filestream_on ' + f.name from [%CATALOG%.]sys.filegroups f where f.data_space_id = t.filestream_data_space_id) - when 65536 then (select ' filestream_on ' + p.name from [%CATALOG%.]sys.partition_schemes p where p.data_space_id = t.filestream_data_space_id) - else '' - end - collate database_default as C, - i.index_id as D -from - [%CATALOG%.]sys.tables t - join [%CATALOG%.]sys.schemas u on (u.schema_id = t.schema_id) - join [%CATALOG%.]sys.indexes i on (i.object_id = t.object_id and i.index_id in (0, 1, 255)) -where 1=1 -[ and u.name = %.q:OWNER%] -[ and t.name = %.q:TABLE%] -order by 1, 2, 4 - - - -020067B2-C10D-42F8-9D5F-A8E991737254 -SqlPartitionCols -{VAR ID, VAL ...} - -select - convert(varchar(30), o.object_id) + '.' + convert(varchar(30), i.index_id), - c.name + ',' -from - [%CATALOG%.]sys.objects o - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.schema_id) - join [%CATALOG%.]sys.indexes i on (i.object_id = o.object_id and i.index_id in (0, 1, 255)) - join [%CATALOG%.]sys.index_columns x on (x.object_id = o.object_id and x.index_id = i.index_id and x.partition_ordinal > 0) - join [%CATALOG%.]sys.columns c on (c.object_id = x.object_id and c.column_id = x.column_id) -where 1=1 -[ and u.name = %.q:OWNER%] -[ and o.name = %.q:TABLE%] -order by 1, 2 - -1327577850 -mledier -1328262548 -mledier -{OWNER ID, TABLE ID, PartitionSchemeColumn ...} - -select - u.name, - o.name, - c.name + ',' -from - [%CATALOG%.]sys.objects o - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.schema_id) - join [%CATALOG%.]sys.indexes i on (i.object_id = o.object_id and i.index_id in (0, 1, 255)) - join [%CATALOG%.]sys.index_columns x on (x.object_id = o.object_id and x.index_id = i.index_id and x.partition_ordinal > 0) - join [%CATALOG%.]sys.columns c on (c.object_id = x.object_id and c.column_id = x.column_id) -where 1=1 -[ and u.name = %.q:OWNER%] -[ and o.name = %.q:TABLE%] -1 - - -A5C94304-1394-4108-AAAF-8FE92106D8F3 -SqlListQuery -{OWNER, TABLE, TABLE_TYPE, COMMENT} - -select - u.name, - o.name, - case (o.type) when 'S' then 'SYSTEM TABLE' else 'TABLE' end, - convert(varchar(8000), p.value) -from - [%CATALOG%.]sys.sysobjects o - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.uid) -left outer join [%CATALOG%.]sys.extended_properties p on (p.major_id = o.id and p.minor_id = 0 and p.name = 'MS_Description') -where - o.type in ('U', 'S') -[ and u.name = %.q:OWNER%] -order by 1, 2 - - -0 - -1327671821 -mledier -SQL query to list objects - - -2BD9D8A2-B12B-4A28-B29F-8A1326F60F60 -Alter -alter table [%QUALIFIER%]%TABLE%[%R%? with nocheck] add %TABLDEFN% -0 - -0 - -Command for modifying a table. - - -9D9C0EE2-D479-4FE3-9677-7D33A89596CD -ReversedQueries -SqlPartitionCols -1327683697 -mledier -1327683960 -mledier -Additional attributes queries to be called by ODBC - - - - -1E22D3D3-6BD6-46E7-98DA-7E61934B782E -Index -INDX -0 - -1328003496 -mledier -The following system variables are available: -(parent table items are also available for indexes) - "INDEX" // generated code of the index - "INDEXNAME" // index name - "INDEXCODE" // index code - "UNIQUE" // keyword "unique" when the index is unique - "INDEXTYPE" // index type (available only for a few DBMS) - "INDEXKEY" // keywords "primary", "unique" or "foreign" depending on the index origin - "CIDXLIST" // list of index columns. Eg: A asc, B desc, C asc - "CLUSTER" // keyword "cluster" when the index is cluster -For index columns, the following system variables are available: - "ASC" // keywords "ASC" or "DESC" depending on sort order - "ISASC" // TRUE if the index column sort is ascending - - - -F2CC63A9-7042-4DCD-9EA6-34426804F47B -Maxlen -0 - -0 - -Maximum object code length -128 - - -9EEE50F2-49D3-44FE-8E90-5D7ABEE6276E -MaxColIndex -0 - -0 - -Maximum number of columns in an index -16 - - -8A75CB3F-59D4-434A-B473-538CCB3B419C -Enable -0 - -0 - -Index allowed -1 - - -B5D4B611-A0B9-4D00-A4B3-30B8CDB786BD -EnableAscDesc -0 - -0 - -ASC, DESC keywords allowed -1 - - -567A16AB-2B1D-448F-99CC-8AF88798C811 -UniqName -0 - -0 - -Unique index name in the database - - -91F4F6B0-F37D-4F9F-97E4-BD003D600FA3 -Create -[%Type%=SPATIAL?create spatial index %INDEX% on [%TABLQUALIFIER%]%TABLE% %.A:SpatialIndexDefn%:[%Type%=FULLTEXT?create fulltext index on [%TABLQUALIFIER%]%TABLE% (%CIDXLIST%) - key index %INDEX%[%FullTextCatalog%? - on %FullTextCatalog.GeneratedName%][%ChangeTracking%? - with change_tracking = %ChangeTracking%]:create [%Type%=XML?[%XMLPrimary%?primary ]xml :[%UNIQUE% ][%CLUSTER%?clustered :[%R%?[nonclustered ]]]]index %INDEX% on [%TABLQUALIFIER%]%TABLE% ( -%CIDXLIST% -) -[%Type%=XML?[%XMLPrimary%?:[%PrimaryXMLIndex%?using xml index %PrimaryXMLIndex.GeneratedName%[ for %SecondaryXMLIndexType%] -]]:[%CLUSTER%?:[%IncludedColumns%?include (%IncludedColumns%) -]]][%OPTIONS%]]] - -0 - -1299595242 -Marc -Command for creating an index. Example: create index %INDEX% - - -6734D968-5B18-4895-8018-D2CBDDD616CF -Cluster -0 - -0 - -Cluster index keyword (default value: cluster) -clustered - - -3AAC2320-2D8F-4198-98BB-FA8B3D9969ED -AddColIndex -%COLUMN%[%Index.Type%=FULLTEXT?[%TypeColumn%? type column %TypeColumn.GeneratedName%][%Language%? language %Language%]:[%Index.Type%=XML?:[ %ASC%]]] -0 - -0 - -Command for defining an index column - - -515DA7A9-B441-4230-97EB-B737683FF77C -Options -with : composite=yes, separator=yes, parenthesis=yes, chldmand=yes -{ -pad_index=%s : list=on|off -fillfactor=%d : default=10 -sort_in_tempdb=%s : list=on|off -ignore_dup_key=%s : list=on|off -statistics_norecompute=%s : list=on|off -drop_existing=%s : list=on|off -online=%s : list=on|off -allow_row_locks=%s : list=on|off -allow_page_locks=%s : list=on|off -maxdop=%s -<compression> : composite=yes, separator=yes -{ - data_compression = %s : list=none|row|page, composite=yes, multiple=yes - { - on partitions : composite=yes, parenthesis=yes, separator=yes - { - <expression_or_range> %s : multiple=yes - } - } -} -} -on : composite=yes -{ - <partition scheme> %s : composite=yes, parenthesis=yes, chldmand=yes - { - <column> %s - } - <filegroup> %s : category=storage, default="DEFAULT" -} -filestream_on : composite=yes, chldmand=yes -{ - <partition scheme> %s : composite=yes, parenthesis=yes, chldmand=yes - { - <column> %s - } - <filegroup> %s : category=storage, default="DEFAULT" -} -0 - -0 - -Default options for creating an index - - -690CDB12-04B2-4B65-A58F-EBC195657AFE -DefOptions -0 - -0 - -Default values for index options - - -A0E7A393-7177-4DB6-A963-E31542A07EED -Drop -if exists (select 1 - from sysindexes - where id = object_id('[%QUALIFIER%]%TABLE%') - and name = %.q:INDEX% - and indid > 0 - and indid < 255) - drop index [%QUALIFIER%]%TABLE%.%INDEX% -0 - -0 - -Command for dropping an index. Example: drop index %INDEX% - - -B2E973F6-7460-43B5-8B52-9D1AE0406128 -SqlOptsQuery -{OWNER, TABLE, INDEX, NULL} - -select 1 - -0 - -1327939207 -mledier -SQL query to reverse object physical options - - -F7B3BBA8-45CA-4145-86EC-E2719EC852B1 -SqlSysIndexQuery -{OWNER, TABLE, INDEX} - -select - u.name, - o.name, - i.name -from - sysindexes i, - sysobjects o, - sysusers u -where - i.id = o.id and u.uid = o.uid - and o.type = 'U' - and (i.status & 6144) >0 - [and o.name = %.q:TABLE%] - [and u.name=%.q:OWNER%] - -0 - -1327938937 -mledier -SQL query to list system indexes created by the database - - -4FFACDE7-C097-4AE0-9EE8-21F433D19BF9 -EnableCluster -0 - -0 - -Cluster option available for indexes -1 -1 - - -765D521B-5D4E-45C8-A98D-7E8F6A5B07D8 -SqlListQuery -{OWNER ID, TABLE ID, INDEX ID, CLUSTER ID, UNIQUE ID, Type ID, XMLPrimary ID, CIDXLIST ..., Include ...} - -select - u.name, - o.name, - i.name, - case(i.type) when 1 then 'clustered' else '' end, - case(i.is_unique) when 1 then 'unique' else '' end, - case(i.type) when 3 then 'XML' when 4 then 'SPATIAL' else '' end, - case(i.type) when 3 then case when ((select p.using_xml_index_id from [%CATALOG%.]sys.xml_indexes p where p.object_id = o.object_id and p.index_id = i.index_id) is null) then 'true' else 'false' end else '' end as colnA, - case(x.is_included_column) when 0 then case(x.index_column_id) when 1 then '' else ', ' end + c.name + case(x.is_descending_key) when 1 then ' desc' else '' end else '' end as colnB, - case(x.is_included_column) when 1 then case(x.index_column_id) when 1 then '' else ', ' end + c.name else '' end as colnC -from - [%CATALOG%.]sys.objects o - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.schema_id) - join [%CATALOG%.]sys.indexes i on (i.object_id = o.object_id) - join [%CATALOG%.]sys.index_columns x on (x.object_id = o.object_id and x.index_id = i.index_id and x.partition_ordinal = 0) - join [%CATALOG%.]sys.columns c on (c.object_id = o.object_id and c.column_id = x.column_id) -where i.index_id > 0 -[ and o.name = %.q:TABLE%] -[ and u.name = %.q:SCHEMA%] -order by - 1, 2, 3, x.index_column_id - -0 - -1328260113 -mledier -SQL query to list objects - - -7CFF1EF0-12EE-4F70-9C3C-00BD38104199 -IndexType -XML -FULLTEXT -SPATIAL -0 - -1299595242 -Marc -List of types available for an index - - -758E4A06-3886-4987-A730-1CEF7E84AAE6 -SqlAttrQuery -{OWNER ID, TABLE ID, INDEX ID, PrimaryXMLIndex, SecondaryXMLIndexType} - -select - u.name, - o.name, - i.name, - pi.name, - case(xi.secondary_type) when 'P' then 'path ' when 'V' then 'value' when 'R' then 'property' else '' end -from - [%CATALOG%.]sys.objects o - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.schema_id) - join [%CATALOG%.]sys.indexes i on (i.object_id = o.object_id) - join [%CATALOG%.]sys.xml_indexes xi on (xi.index_id = i.index_id and xi.object_id = i.object_id) - join [%CATALOG%.]sys.xml_indexes pi on (pi.index_id = xi.using_xml_index_id and pi.object_id = i.object_id) - -where i.index_id > 0 and i.type = 3 -[ and o.name = %.q:TABLE%] -[ and u.name = %.q:SCHEMA%] -[ and i.name = %.q:INDEX%] -order by - 1, 2, 3 - -0 - -0 - -SQL query to reverse object attributes - - -D8EA55B7-C57F-478A-B9C8-EF3CE5E5496A -ReversedQueries -SqlPartOptions -SqlPartitionCols -SqlSpatialOptions -SqlCompressionQuery -1299602736 -Marc -1328003558 -mledier -Additional attributes queries to be called by ODBC - - -37018C16-A905-4BE6-9386-778C10F3764D -SqlSpatialOptions -{OWNER, TABLE, INDEX, TesselationType, BBoxXMin, BBoxXMax, BBoxYMin, BBoxYMax, Level1Density, Level2Density, Level3Density, Level4Density, CellsPerObject, FillFactor, PadIndex, AllowRowLocks, AllowPageLocks} - -select - u.name, - o.name, - i.name, - lower(s.tessellation_scheme), - case when (s.tessellation_scheme = 'GEOMETRY_GRID') then s.bounding_box_xmin else '' end, - case when (s.tessellation_scheme = 'GEOMETRY_GRID') then s.bounding_box_xmax else '' end, - case when (s.tessellation_scheme = 'GEOMETRY_GRID') then s.bounding_box_ymin else '' end, - case when (s.tessellation_scheme = 'GEOMETRY_GRID') then s.bounding_box_ymax else '' end, - lower(level_1_grid_desc), - lower(level_2_grid_desc), - lower(level_3_grid_desc), - lower(level_4_grid_desc), - cells_per_object, - i.fill_factor, - case (i.is_padded) when 'TRUE' then 'true' else 'false' end, - case (i.allow_row_locks) when 'TRUE' then 'true' else 'false' end, - case (i.allow_page_locks) when 'TRUE' then 'true' else 'false' end -from - [%CATALOG%.]sys.objects o - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.schema_id) - join [%CATALOG%.]sys.indexes i on (i.object_id = o.object_id) - join [%CATALOG%.]sys.spatial_index_tessellations s on (s.object_id = o.object_id and s.index_id = i.index_id) -where i.index_id > 0 and i.type = 4 -[ and o.name = %.q:TABLE%] -[ and u.name = %.q:SCHEMA%] - - - -1299602736 -Marc -1303200053 -Marc -1 - - -66C6EFBF-AD85-48C9-9800-00D84B4B13FD -SqlPartOptions -{OWNER, TABLE, INDEX, OPTIONS} - -select - u.name, - o.name, - i.name, - replace('with (' - + case when (i.allow_row_locks != 0) then '' else 'allow_row_locks=off' end - + case when (i.allow_page_locks != 0) then '' else 'allow_page_locks=off' end - + case when (i.is_padded != 0) then 'pad_index=on' else '' end - + case when (i.fill_factor != 0) then 'fillfactor=' + convert(varchar, i.fill_factor) else '' end - + case when (i.ignore_dup_key != 0) then 'ignore_dup_key=on' else '' end - + ')', 'with ()', '') - + case(i.data_space_id & 65536) - when 0 then - (select ' on ' + f.name from [%CATALOG%.]sys.filegroups f where f.data_space_id = i.data_space_id) - when 65536 then - (select ' on ' + p.name + ' (x)' - from [%CATALOG%.]sys.partition_schemes p where p.data_space_id = i.data_space_id) - else '' - end -from - [%CATALOG%.]sys.objects o - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.schema_id) - join [%CATALOG%.]sys.indexes i on (i.object_id = o.object_id and i.index_id > 0 and i.type != 4) -where - o.type = 'U' -[ and u.name=%.q:OWNER%] -[ and o.name=%.q:TABLE%] -[ and i.name=%.q:INDEX%] - -1327934655 -mledier -1327939089 -mledier -1 - - -A7AF3FDE-3C0C-43B9-BE12-84A7C9181218 -SqlPartitionCols -{OWNER ID, TABLE ID, INDEX ID, PartitionSchemeColumn ...} - -select - u.name, - o.name, - i.name, - c.name + ',' -from - [%CATALOG%.]sys.objects o - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.schema_id) - join [%CATALOG%.]sys.indexes i on (i.object_id = o.object_id and i.index_id > 0 and i.type != 4) - join [%CATALOG%.]sys.index_columns x on (x.object_id = o.object_id and x.index_id = i.index_id and x.partition_ordinal > 0) - join [%CATALOG%.]sys.columns c on (c.object_id = x.object_id and c.column_id = x.column_id) -where - o.type = 'U' -[ and u.name = %.q:OWNER%] -[ and o.name = %.q:TABLE%] -[ and i.name=%.q:INDEX%] - -1327577850 -mledier -1328022965 -mledier -1 - - -8FE48E2A-FF39-4604-9C5E-475F43B2E6EF -SqlCompressionQuery -{OWNER ID, TABLE ID, INDEX ID, WithCompression ...} - -select - u.name, - o.name, - i.name, - case when(p.data_compression > 0) - then 'data_compression = ' - + lower(p.data_compression_desc) - + ' on partitions (' - + convert(varchar, p.partition_number) - + '), ' - else '' - end -from - [%CATALOG%.]sys.objects o - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.schema_id) - join [%CATALOG%.]sys.indexes i on (i.object_id = o.object_id and i.index_id > 0 and i.type != 4) - join [%CATALOG%.]sys.partitions p on (p.object_id = o.object_id and p.index_id = i.index_id) -where - o.type = 'U' -[ and u.name=%.q:OWNER%] -[ and o.name=%.q:TABLE%] -[ and i.name=%.q:INDEX%] -order by 1, 2, 3, p.partition_number -1328001249 -mledier -1328003712 -mledier -1 - - - - -14D02ECD-9628-48BE-A8D7-9F640EF7B376 -Default -DFLT -0 - -0 - -The following system variables are available: - "DEFAULT" // generated code of the default - "VALUE" // value of the default object - "BOUND_OBJECT" // the generated code (including qualifier and parent) of the object which is bound to the default - "SCRIPT" // the create default body - - - -7B11A84C-5FA8-47C3-A02C-19B94070B4A7 -Enable -0 - -0 - -Defaults allowed -1 - - -5AA7A161-2E36-4C3B-B210-2545F92F0194 -Create -create default [%QUALIFIER%]%DEFAULT% - as %VALUE% - -0 - -0 - -Command for creating a default. -Example: create default %DEFAULT% as %VALUE% - - -D74F113F-0F2D-47D9-9B07-07A0ABE63D2A -Bind -[%R%?[exec ]][execute ]sp_bindefault [%QUALIFIER%]%DEFAULT%, %.q:BOUND_OBJECT% - -0 - -0 - -Command for binding a default object to a domain or a column - - -7174CA49-F956-4986-AE9E-C20DBE2390C3 -SqlListQuery -{OWNER, DEFAULT, SCRIPT} - -select - u.name, - o.name, - t.text -from - [%CATALOG%.]sys.sysobjects o - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.uid) - join [%CATALOG%.]sys.syscomments t on (t.id = o.id) -where - o.type = 'D' - and (o.parent_obj is null or o.parent_obj = 0) -[ and u.name = %.q:SCHEMA%] -order by - 1, 2 -0 - -0 - -SQL query to list objects - - -027910D7-77A5-42B0-915B-F97405774F99 -Drop -if exists (select 1 - from sysobjects where type = 'D' - and name = %.q:DEFAULT%[ and user_name(uid) = %.q:OWNER%] - ) - drop default [%QUALIFIER%]%DEFAULT% -0 - -0 - -Command for dropping a default - - -F358011A-6CF8-49CE-ABDF-2D2D1763B9C2 -EnableOwner -0 - -0 - -Owner allowed -1 -1 - - -3372477A-EF18-4B03-B0FE-83D20814EA55 -SqlAttrQuery -{OWNER, DEFAULT, BOUND_OBJECT} - -select - u.name, - o.name, - d.name -from - [%CATALOG%.]sys.sysobjects o - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.uid) - join [%CATALOG%.]sys.systypes d on (d.tdefault = o.id) -where - o.type = 'D' -[ and o.name = %.q:DEFAULT%] -[ and u.name = %.q:OWNER%] -union select - u.name, - o.name, - v.name + '.' + t.name + '.' + c.name -from - [%CATALOG%.]sys.sysobjects o - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.uid) - join [%CATALOG%.]sys.syscolumns c on (c.cdefault = o.id) - join [%CATALOG%.]sys.sysobjects t on (t.id = c.id) - join [%CATALOG%.]sys.schemas v on (v.schema_id = t.uid) -where - o.type = 'D' -[ and o.name = %.q:DEFAULT%] -[ and u.name = %.q:OWNER%] -order by - 1, 2 -0 - -0 - -SQL query to reverse object attributes - - -4B7C7B89-8744-46A5-9396-09C8B1B58A0E -Unbind -[%R%?[exec ]][execute ]sp_unbindefault %.q:BOUND_OBJECT% -0 - -0 - -Command for unbinding a default value from an object (domain or column) - - -11E92182-5435-41B1-BB8D-17D147F652EE -Maxlen -0 - -0 - -Maximum object code length -128 - - - - -3C7CC1A9-0A0F-4878-ACF5-19E78C5F64F0 -DBMS Trigger -DBMSTRGR -0 - -1269267132 -mledier - - -CBBED234-17E1-4F57-B39C-63BFB910704E -EventDelimiter -0 - -1269267132 -mledier -Events separation character. -, - - -7A9270AF-9480-4D8B-AB2D-C0268E550106 -Scope -0 - -1269267132 -mledier -Supported Scopes -Database=Events_Database -All Server=Events_Server -Database -All Server - - - -4EDB1FBC-6891-468B-B5F0-BE0D55F39459 -Time -for -after -0 - -1269267132 -mledier -Extended trigger times list. - - -410F7A6F-09E1-4AC7-ADB2-A42786200D0F -Create -create trigger %TRIGGER% on %TRGSCOPE%[ with %Option%] %TRGTIME% %TRGEVENT% -as [%ImplementationType%=ClassMethod?external name %Assembly.GeneratedName%.%MethodName%:%TRGDEFN%] -0 - -1269267132 -mledier -Command for creating a DBMS trigger. - - -B543C788-1ECE-4D18-801B-34C282CDFB7B -Drop -if exists (select 1 - from [%.U:TRGSCOPE%=DATABASE?sys.triggers:sys.server_triggers] - where name = %.q:TRIGGER%) - drop trigger %TRIGGER% on %TRGSCOPE% -0 - -1269267132 -mledier -Command for dropping a DBMS trigger. - - -7C5059D6-D303-4DC3-8430-02D0C01354EE -Events_Server -DDL_EVENTS -.DDL_SERVER_LEVEL_EVENTS -..ALTER_DATABASE -..CREATE_DATABASE -..DROP_DATABASE -..ALTER_INSTANCE -..DDL_LINKED_SERVER_EVENTS -...ALTER_LINKED_SERVER -...CREATE_LINKED_SERVER -...DROP_LINKED_SERVER -...DDL_LINKED_SERVER_LOGIN_EVENTS -....ALTER_LINKED_SERVER_LOGIN -....CREATE_LINKED_SERVER_LOGIN -....DROP_LINKED_SERVER_LOGIN -...DDL_REMOTE_SERVER_EVENTS -....ALTER_REMOTE_SERVER -....CREATE_REMOTE_SERVER -....DROP_REMOTE_SERVER -...DDL_EXTENDED_PROCEDURE_EVENTS -....ALTER_EXTENDED_PROCEDURE -....CREATE_EXTENDED_PROCEDURE -....DROP_EXTENDED_PROCEDURE -....DDL_MESSAGE_EVENTS -.....ALTER_MESSAGE -.....CREATE_MESSAGE -.....DROP_MESSAGE -..DDL_ENDPOINT_EVENTS -...ALTER_ENDPOINT -...CREATE_ENDPOINT -...DROP_ENDPOINT -..DDL_SERVER_SECURITY_EVENTS -...ADD_ROLE_MEMBER -...ADD_SERVER_ROLE_MEMBER -...DROP_ROLE_MEMBER -...DROP_SERVER_ROLE_MEMBER -...DDL_LOGIN_EVENTS -....ALTER_LOGIN -....CREATE_LOGIN -....DROP_LOGIN -...DDL_GDR_SERVER_EVENTS -....DENY_SERVER -....GRANT_SERVER -....REVOKE_SERVER -...DDL_AUTHORIZATION_SERVER_EVENTS -....ALTER_AUTHORIZATION_SERVER -.DDL_DATABASE_LEVEL_EVENTS -..DDL_FULLTEXT_CATALOG_EVENTS -...ALTER_FULLTEXT_CATALOG -...CREATE_FULLTEXT_CATALOG -...DROP_FULLTEXT_CATALOG -..DDL_DEFAULT_EVENTS -...CREATE_DEFAULT -...BIND_DEFAULT -...DROP_DEFAULT -..DDL_EXTENDED_PROPERTY_EVENTS -...CREATE_EXTENDED_PROPERTY -...DROP_EXTENDED_PROPERTY -..DDL_PLAN_GUIDE_EVENTS -...ALTER_PLAN_GUIDE -...CREATE_PLAN_GUIDE -...DROP_PLAN_GUIDE -..DDL_RULE_EVENTS -...CREATE_RULE -...BIND_RULE -...DROP_RULE -..DDL_TABLE_VIEW_EVENTS -...DDL_TABLE_EVENTS -....ALTER_TABLE -....CREATE_TABLE -....DROP_TABLE -...DDL_VIEW_EVENTS -....ALTER_VIEW -....CREATE_VIEW -....DROP_VIEW -...DDL_INDEX_EVENTS -....ALTER_INDEX -....CREATE_INDEX -....CREATE_XML_INDEX -....DROP_INDEX -....ALTER_FULLTEXT_INDEX -....CREATE_FULLTEXT_INDEX -....DROP_FULLTEXT_INDEX -...DDL_STATISTICS_EVENTS -....CREATE_STATISTICS -....DROP_STATISTICS -....UPDATE_STATISTICS -..DDL_SYNONYM_EVENTS -...CREATE_SYNONYM -...DROP_SYNONYM -..DDL_FUNCTION_EVENTS -...ALTER_FUNCTION -...CREATE_FUNCTION -...DROP_FUNCTION -..DDL_PROCEDURE_EVENTS -...ALTER_PROCEDURE -...CREATE_PROCEDURE -...DROP_PROCEDURE -..DDL_TRIGGER_EVENTS -...ALTER_TRIGGER -...CREATE_TRIGGER -...DROP_TRIGGER -..DDL_EVENT_NOTIFICATION_EVENTS -...CREATE_EVENT_NOTIFICATION -...DROP_EVENT_NOTIFICATION -..DDL_ASSEMBLY_EVENTS -...ALTER_ASSEMBLY -...CREATE_ASSEMBLY -...DROP_ASSEMBLY -..DDL_TYPE_EVENTS -...CREATE_TYPE -...DROP_TYPE -..DDL_DATABASE_SECURITY_EVENTS -...DDL_CERTIFICATE_EVENTS -....ALTER_CERTIFICATE -....CREATE_CERTIFICATE -....DROP_CERTIFICATE -...DDL_USER_EVENTS -....CREATE_USER -....ALTER_USER -....DROP_USER -...DDL_ROLE_EVENTS -....ALTER_ROLE -....CREATE_ROLE -....DROP_ROLE -...DDL_APPLICATION_ROLE_EVENTS -....ALTER_APPLICATION_ROLE -....CREATE_APPLICATION_ROLE -....DROP_APPLICATION_ROLE -...DDL_SCHEMA_EVENTS -....ALTER_SCHEMA -....CREATE_SCHEMA -....DROP_SCHEMA -...DDL_GDR_DATABASE_EVENTS -....DENY_DATABASE -....GRANT_DATABASE -....REVOKE_DATABASE -...DDL_AUTHORIZATION_DATABASE_EVENTS -....ALTER_AUTHORIZATION_DATABASE -..DDL_SSB_EVENTS -...DDL_MESSAGE_TYPE_EVENTS -....ALTER_MSGTYPE -....CREATE_MSGTYPE -....DROP_MSGTYPE -...DDL_CONTRACT_EVENTS -....CREATE_CONTRACT -....DROP_CONTRACT -...DDL_QUEUE_EVENTS -....ALTER_QUEUE -....CREATE_QUEUE -....DROP_QUEUE -...DDL_SERVICE_EVENTS -....ALTER_SERVICE -....CREATE_SERVICE -....DROP_SERVICE -...DDL_ROUTE_EVENTS -....ALTER_ROUTE -....CREATE_ROUTE -....DROP_ROUTE -...DDL_REMOTE_SERVICE_BINDING_EVENTS -....ALTER_REMOTE_SERVICE_BINDING -....CREATE_REMOTE_SERVICE_BINDING -....DROP_REMOTE_SERVICE_BINDING -..DDL_XML_SCHEMA_COLLECTION_EVENTS -...ALTER_XML_SCHEMA_COLLECTION -...CREATE_XML_SCHEMA_COLLECTION -...DROP_XML_SCHEMA_COLLECTION -..DDL_PARTITION_EVENTS -...DDL_PARTITION_FUNCTION_EVENTS -....ALTER_PARTITION_FUNCTION -....CREATE_PARTITION_FUNCTION -....DROP_PARTITION_FUNCTION -...DDL_PARTITION_SCHEME_EVENTS -....ALTER_PARTITION_SCHEME -....CREATE_PARTITION_SCHEME -....DROP_PARTITION_SCHEME -LOGON -0 - -1269267132 -mledier -1 - - -FB368008-DDA5-4558-861A-B42D7715DF6E -Events_Database -DDL_EVENTS -.DDL_DATABASE_LEVEL_EVENTS -..DDL_FULLTEXT_CATALOG_EVENTS -...ALTER_FULLTEXT_CATALOG -...CREATE_FULLTEXT_CATALOG -...DROP_FULLTEXT_CATALOG -..DDL_DEFAULT_EVENTS -...CREATE_DEFAULT -...BIND_DEFAULT -...DROP_DEFAULT -..DDL_EXTENDED_PROPERTY_EVENTS -...CREATE_EXTENDED_PROPERTY -...DROP_EXTENDED_PROPERTY -..DDL_PLAN_GUIDE_EVENTS -...ALTER_PLAN_GUIDE -...CREATE_PLAN_GUIDE -...DROP_PLAN_GUIDE -..DDL_RULE_EVENTS -...CREATE_RULE -...BIND_RULE -...DROP_RULE -..DDL_TABLE_VIEW_EVENTS -...DDL_TABLE_EVENTS -....ALTER_TABLE -....CREATE_TABLE -....DROP_TABLE -...DDL_VIEW_EVENTS -....ALTER_VIEW -....CREATE_VIEW -....DROP_VIEW -...DDL_INDEX_EVENTS -....ALTER_INDEX -....CREATE_INDEX -....CREATE_XML_INDEX -....DROP_INDEX -....ALTER_FULLTEXT_INDEX -....CREATE_FULLTEXT_INDEX -....DROP_FULLTEXT_INDEX -...DDL_STATISTICS_EVENTS -....CREATE_STATISTICS -....DROP_STATISTICS -....UPDATE_STATISTICS -..DDL_SYNONYM_EVENTS -...CREATE_SYNONYM -...DROP_SYNONYM -..DDL_FUNCTION_EVENTS -...ALTER_FUNCTION -...CREATE_FUNCTION -...DROP_FUNCTION -..DDL_PROCEDURE_EVENTS -...ALTER_PROCEDURE -...CREATE_PROCEDURE -...DROP_PROCEDURE -..DDL_TRIGGER_EVENTS -...ALTER_TRIGGER -...CREATE_TRIGGER -...DROP_TRIGGER -..DDL_EVENT_NOTIFICATION_EVENTS -...CREATE_EVENT_NOTIFICATION -...DROP_EVENT_NOTIFICATION -..DDL_ASSEMBLY_EVENTS -...ALTER_ASSEMBLY -...CREATE_ASSEMBLY -...DROP_ASSEMBLY -..DDL_TYPE_EVENTS -...CREATE_TYPE -...DROP_TYPE -..DDL_DATABASE_SECURITY_EVENTS -...DDL_CERTIFICATE_EVENTS -....ALTER_CERTIFICATE -....CREATE_CERTIFICATE -....DROP_CERTIFICATE -...DDL_USER_EVENTS -....CREATE_USER -....ALTER_USER -....DROP_USER -...DDL_ROLE_EVENTS -....ALTER_ROLE -....CREATE_ROLE -....DROP_ROLE -...DDL_APPLICATION_ROLE_EVENTS -....ALTER_APPLICATION_ROLE -....CREATE_APPLICATION_ROLE -....DROP_APPLICATION_ROLE -...DDL_SCHEMA_EVENTS -....ALTER_SCHEMA -....CREATE_SCHEMA -....DROP_SCHEMA -...DDL_GDR_DATABASE_EVENTS -....DENY_DATABASE -....GRANT_DATABASE -....REVOKE_DATABASE -...DDL_AUTHORIZATION_DATABASE_EVENTS -....ALTER_AUTHORIZATION_DATABASE -..DDL_SSB_EVENTS -...DDL_MESSAGE_TYPE_EVENTS -....ALTER_MSGTYPE -....CREATE_MSGTYPE -....DROP_MSGTYPE -...DDL_CONTRACT_EVENTS -....CREATE_CONTRACT -....DROP_CONTRACT -...DDL_QUEUE_EVENTS -....ALTER_QUEUE -....CREATE_QUEUE -....DROP_QUEUE -...DDL_SERVICE_EVENTS -....ALTER_SERVICE -....CREATE_SERVICE -....DROP_SERVICE -...DDL_ROUTE_EVENTS -....ALTER_ROUTE -....CREATE_ROUTE -....DROP_ROUTE -...DDL_REMOTE_SERVICE_BINDING_EVENTS -....ALTER_REMOTE_SERVICE_BINDING -....CREATE_REMOTE_SERVICE_BINDING -....DROP_REMOTE_SERVICE_BINDING -..DDL_XML_SCHEMA_COLLECTION_EVENTS -...ALTER_XML_SCHEMA_COLLECTION -...CREATE_XML_SCHEMA_COLLECTION -...DROP_XML_SCHEMA_COLLECTION -..DDL_PARTITION_EVENTS -...DDL_PARTITION_FUNCTION_EVENTS -....ALTER_PARTITION_FUNCTION -....CREATE_PARTITION_FUNCTION -....DROP_PARTITION_FUNCTION -...DDL_PARTITION_SCHEME_EVENTS -....ALTER_PARTITION_SCHEME -....CREATE_PARTITION_SCHEME -....DROP_PARTITION_SCHEME -0 - -1269267132 -mledier -1 - - -92ACFC22-AC99-475C-83F3-5CC677FD102E -Enable -0 - -1269267132 -mledier -DBMS trigger allowed -1 - - -04BF51B5-7993-4FBA-A8AC-541EC45A76DC -EnableMultiEvent -0 - -1269267132 -mledier -Multiple event allowed -1 - - -7B0CAF6C-C4E1-4042-A166-B26F55BE39DB -EnableOwner -0 - -1269267132 -mledier -Owner allowed - - -1EA9C2D8-38E8-4D4C-AC30-5B8E67BBFCCF -SqlAttrQuery -{TRIGGER, SCRIPT} - -select - tr.name, - m.definition -from - [%CATALOG%.]sys.triggers tr - join [%CATALOG%.]sys.sql_modules m on (m.object_id = tr.object_id) -where - tr.type = 'TR' - and tr.parent_class_desc = 'DATABASE' -[ and tr.name = %.q:TRIGGER%] -union -select - tr.name, - m.definition -from - [%CATALOG%.]sys.server_triggers tr - join [%CATALOG%.]sys.sql_modules m on (m.object_id = tr.object_id) -where - tr.type = 'TR' - and tr.parent_class_desc = 'SERVER' -[ and tr.name = %.q:TRIGGER%] - - -0 - -1269267132 -mledier -SQL query to reverse object attributes - - -CE8443D6-380D-491C-8ED4-C26A3FD08008 -SqlListQuery -{TRIGGER ID, ImplementationType ID, Assembly ID, MethodName ID, TRGTIME ID, TRGSCOPE, TRGEVENT ...} - -select - tr.name, - case(tr.type) when 'TA' then 'ClassMethod' else '' end, - a.name, - s.assembly_class + '.' + s.assembly_method, - 'after', - 'Database', - te.type_desc + ',' -from - [%CATALOG%.]sys.triggers tr - join [%CATALOG%.]sys.trigger_events te on (te.object_id = tr.object_id) - left outer join [%CATALOG%.]sys.assembly_modules s on (s.object_id = tr.object_id and tr.type = 'TA') - left outer join [%CATALOG%.]sys.assemblies a on (a.assembly_id = s.assembly_id and tr.type = 'TA') -where - tr.parent_class_desc = 'DATABASE' -union -select - tr.name, - case(tr.type) when 'TA' then 'ClassMethod' else '' end, - a.name, - s.assembly_class + '.' + s.assembly_method, - 'after', - 'All Server', - te.type_desc + ',' -from - [%CATALOG%.]sys.server_triggers tr - join [%CATALOG%.]sys.server_trigger_events te on (te.object_id = tr.object_id) - left outer join [%CATALOG%.]sys.server_assembly_modules s on (s.object_id = tr.object_id and tr.type = 'TA') - left outer join [%CATALOG%.]sys.assemblies a on (a.assembly_id = s.assembly_id and tr.type = 'TA') -where - tr.parent_class_desc = 'SERVER' -order by - 1 -0 - -1269267132 -mledier -SQL query to list objects - - - - -72340800-1072-4E83-AFB5-7E2FB1DD6E56 -Column -COLN -0 - -0 - -The following system variables are available: -(parent table items are also available for columns) - "COLUMN" // generated code of the column - "COLNNO" // position of the column in the list of columns of the table - "COLNNAME" // name of the column - "COLNCODE" // code of the column - "PRIMARY" // keyword "primary" if the column is primary - "ISPKEY" // TRUE if the column is part of the primary key - "FOREIGN" // TRUE if the column is part of one foreign key - - - -B6FBB172-EE65-462B-B7B8-4022476FCCA8 -Enable -0 - -0 - -Column allowed -1 - - -0617A037-CBA0-4D97-8F44-10826D23FDCF -Maxlen -0 - -0 - -Maximum object code length -128 - - -BD6DCC43-BC73-471E-A05E-DD2749C28373 -EnableIdentity -0 - -0 - -Identity keyword entry support. Identity columns are serial counters maintains by the database -1 - - -7624484B-9307-4284-A738-56C2844F8DF5 -AlterTableAddDefault -[[ constraint %ExtDeftConstName%] default [%R%?[(]]%DEFAULT%[%R%?[)]] ]for %COLUMN% -0 - -1328805983 -mledier -Command for defining the default value of a column in an alter statement - - -CC3BBE53-BED1-404E-9050-9E4A23AB854C -EnableDefault -0 - -0 - -Default values allowed -1 - - -A116B757-06F3-46F1-9C1B-424E667AF345 -ConstName -CKC_%.U17:COLUMN%_%.U8:TABLE% -0 - -0 - -Constraint name template for a column check parameter - - -27E4D79C-AFC7-4400-923E-A4B5ED8AF466 -Add -%20:COLUMN% [%COMPUTE%?AS [(]%COMPUTE%[)]:[%.L:DATATYPE%=xml?xml[%XMLSchemaCollection%?([%ContentType% ]%XMLSchemaCollection.GeneratedName%): ]:%20:DATATYPE%][.Z:[ collate %ExtCollation%][%allowFilestream%?[%FileStream%? filestream]][%ExtRowGuidCol%? RowGuidCol][%Mandatory%?:[%Sparse%? sparse]][%IDENTITY%? %IDENTITY%[[(%ExtIdentitySeedInc%)][%ExtIdtNotForReplication%? not for replication]]:[%ExtNullConstName%? constraint %ExtNullConstName%][ %NULL%][ %NOTNULL%]][[%ExtDeftConstName%? constraint %ExtDeftConstName%] default [%R%?[(]]%DEFAULT%[%R%?[)]]]] - [%CONSTDEFN%]] - -0 - -1328546106 -mledier -Command for defining a table column - - -56F6468D-706A-4166-8355-DCA0D508012A -DefineColnChck -[[constraint %CONSTNAME% ]check [%ExtCkcNotForReplication%?not for replication ](%.A:CONSTRAINT%)] -0 - -0 - -Allows to customize the script of column check constraints - - -618AD7E1-0F02-47CB-AB2D-5BFEAABD287B -Rename -[%R%?[exec ]][execute ]sp_rename "%TABLE%.%OLDCOLN%", %NEWCOLN%, 'COLUMN' -0 - -0 - -Command for renaming a column - - -BB7B349D-FDBD-4475-8C2C-DD98EC703B8B -Create -alter table [%QUALIFIER%]%TABLE% - add %COLUMN% [%COMPUTE%?AS [(]%COMPUTE%[)]:[%.L:DATATYPE%=xml?xml[%XMLSchemaCollection%?([%ContentType% ]%XMLSchemaCollection.GeneratedName%): ]:%20:DATATYPE%][.Z:[ collate %ExtCollation%][%.L:DataType%="varbinary(max)"?[%FileStream%? filestream]][%ExtRowGuidCol%? RowGuidCol][%Mandatory%?:[%Sparse%? sparse]][%IDENTITY%? %IDENTITY%[[(%ExtIdentitySeedInc%)][%ExtIdtNotForReplication%? not for replication]]:[%ExtNullConstName%? constraint %ExtNullConstName%][ %NULL%][ %NOTNULL%]][[%ExtDeftConstName%? constraint %ExtDeftConstName%] default [%R%?[(]]%DEFAULT%[%R%?[)]]]] - [%CONSTDEFN%]] - -0 - -1328805983 -mledier -Command for adding a column - - -37B7B2E6-6D15-470E-B0AF-DFD024925CF3 -ModifyColumn -alter table [%QUALIFIER%]%TABLE% - alter column %COLUMN% %DATATYPE% %NOTNULL% -0 - -0 - -Command for modifying a column - - -5E792889-C8C3-4086-9525-12A500E832BA -DropDefaultConstraint -[%ExtDeftConstName%?alter table [%QUALIFIER%]%TABLE% - drop constraint %ExtDeftConstName%] -0 - -0 - -Command for dropping constraint linked to a column defined with default value. - - -E20949D5-977E-441D-B417-DC40B7A5E41C -Drop -alter table [%QUALIFIER%]%TABLE% - drop column %COLUMN% -0 - -0 - -Command for dropping a column - - -3EDAFEB3-A3B8-44C7-AD11-F3365993A19D -ModifyColnDflt -0 - -0 - -Command for modifying a column default in an alter table statement - - -2A576541-1585-42E2-9591-466640752A08 -ModifyColnChck -0 - -0 - -Command for modifying a column check in an alter table statement - - -410C689C-4BD5-4E77-959F-50A1B80D3DA2 -DropColnChck -alter table [%QUALIFIER%]%TABLE% - drop constraint %CONSTNAME% -0 - -0 - -Command for dropping a column check in an alter table statement - - -AD07A8A1-A638-4283-845B-14E8151ED1C3 -AltEnableAddColnChk -0 - -0 - -Column check constraint allowed in an alter table statement -1 - - -4BBA9DCD-F71B-4D52-BBA6-3F3117B637CE -SqlChckQuery -{OWNER, TABLE, COLUMN, CONSTNAME, ExtCkcNotForReplication, CONSTRAINT} - -select - u.name, - t.name, - c.name, - o.name, - case (k.status & 0x00200000) when 0x00200000 then 'true' else 'false' end, - replace(m.text, '[' + c.name + ']', c.name) -from - [%CATALOG%.]sys.sysconstraints k - join [%CATALOG%.]sys.syscolumns c on (c.id = k.id and c.colid = k.colid) - join [%CATALOG%.]sys.sysobjects t on (t.id = c.id) - join [%CATALOG%.]sys.schemas u on (u.schema_id = t.uid) - join [%CATALOG%.]sys.sysobjects o on (o.id = k.constid and o.xtype = 'C') - join [%CATALOG%.]sys.syscomments m on (m.id = o.id) -where k.colid > 0 -[ and u.name = %.q:OWNER%] -[ and t.name = %.q:TABLE%] -[ and c.name = %.q:COLUMN%] -order by u.name, t.name, c.name, o.name -0 - -0 - -SQL query to reverse object check constraints - - -6C49C318-5B80-456C-A4ED-639277DF8159 -Permission -0 - -1328805983 -mledier -Available permission for a column. -The first column is the SQL name of permission (eg: SELECT) -The second column is a shortname to display in a grid -SELECT=Select -UPDATE=Update -REFERENCES=Refr. -SELECT -UPDATE -REFERENCES - - - -F751BB20-4F1B-4DD9-B12E-B839949246B5 -EnableBindRule -0 - -0 - -Allows the binding of business rule to a column for check parameters - - -7E9A9288-45BA-4872-98CC-D7D2638FFF19 -Bind -[%R%?[exec ]][execute ]sp_bindrule %RULE%, "%TABLE%.%COLUMN%" -0 - -0 - -Command for binding a rule to a column - - -D90885DD-BCAF-49F6-9F2B-AEADD7519CDE -Unbind -[%R%?[exec ]][execute ]sp_unbindrule %TABLE%.%COLUMN% -0 - -0 - -Command for unbinding a rule from a column - - -D4065C97-07F1-4A82-9ECB-7F909B9F6503 -EnableComputedColn -0 - -0 - -Computed columns allowed -1 - - -499ABF7A-219D-48A7-B4F0-53AE09985862 -SqlPermQuery -{GRANTEE ID, BITMAPCOLN ID, COLNPERMISSION ...} - -select - v.name, - convert(integer, s.columns), - case (s.action) when 193 then 'SELECT' when 197 then 'UPDATE' when 26 then 'REFERENCES' end + - case (s.protecttype) when 204 then '+' else '' end + ',' as coln -from - [%CATALOG%.]sys.sysprotects s - join [%CATALOG%.]sys.sysusers v on (v.uid = s.uid) - join [%CATALOG%.]sys.sysobjects o on (o.id = s.id) - join [%CATALOG%.]sys.sysusers u on (u.uid = o.uid) -where - s.columns > 1 -[ and o.name = %.q:TABLE%] -[ and u.name = %.q:OWNER%] -order by 1 - -0 - -0 - -SQL query to reverse object permissions - - -03C76496-12AC-4696-95F2-F696E3A8B1A8 -SqlListQuery -{OWNER, TABLE, S, COLUMN, DTTPCODE, LENGTH, SIZE, PREC, COMPUTE, NOTNULL, IDENTITY, DOMAIN, DEFAULT, ExtIdentitySeedInc, COMMENT, ExtCollation, ExtIdtNotForReplication, ExtDeftConstName, Sparse, FileStream, ExtRowGuidCol} - -select - u.name, - o.name, - c.column_id, - c.name, - case when c.system_type_id in (165, 167, 231) and c.max_length = -1 then t.name + '(Max)' else t.name end, - c.precision, - case (c.max_length) when -1 then 0 else case when c.system_type_id in (99, 231, 239) then (c.max_length/2) else (c.max_length) end end as colnA, - c.scale, - case(c.is_computed) when 1 then convert(varchar(8000), (select z.definition from [%CATALOG%.]sys.computed_columns z where z.object_id = c.object_id and z.column_id = c.column_id)) else '' end as colnB, - case(c.is_nullable) when 1 then 'NULL' else 'NOTNULL' end, - case(c.is_identity) when 1 then 'identity' else '' end, - case when(c.user_type_id <> c.system_type_id) then (select d.name from [%CATALOG%.]sys.types d where d.user_type_id = c.user_type_id) else '' end as colnC, - case when (d.definition = '('''')') then '~''''~' else convert(varchar(8000), d.definition) end, - case (c.is_identity) when 1 then convert(varchar, i.seed_value) + ', ' + convert(varchar, i.increment_value) else '' end as colnD, - convert(varchar(8000), p.value), - c.collation_name, - case (i.is_not_for_replication) when 1 then 'true' else 'false' end, - d.name, - case(c.is_sparse) when 1 then 'true' else 'false' end, - case(c.is_filestream) when 1 then 'true' else 'false' end, - case(c.is_rowguidcol) when 1 then 'true' else 'false' end -from - [%CATALOG%.]sys.columns c - join [%CATALOG%.]sys.objects o on (o.object_id = c.object_id) - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.schema_id) - left outer join [%CATALOG%.]sys.types t on (t.system_type_id = c.system_type_id and t.user_type_id = c.user_type_id) - left outer join [%CATALOG%.]sys.identity_columns i on (i.object_id = c.object_id and i.column_id = c.column_id) - left outer join [%CATALOG%.]sys.default_constraints d on (d.object_id = c.default_object_id) - left outer join [%CATALOG%.]sys.extended_properties p on (p.major_id = o.object_id and p.minor_id = c.column_id and p.class = 1 and p.name = 'MS_Description') -where - o.type in ('U', 'S', 'V') -[ and u.name = %.q:OWNER%] -[ and o.name=%.q:TABLE%] -order by 1, 2, 3 - -0 - -1328017395 -mledier -SQL query to list objects - - -293EBA10-601E-4EA6-B4FA-16DBFE042D92 -ColumnComment -[if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('[%QUALIFIER%]%TABLE%') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = %.q:COLUMN%) -) -begin - [%OWNER%?[.O:[execute ][exec ]]sp_dropextendedproperty [%R%?[N]]'MS_Description', - [%R%?[N]]'schema', [%R%?[N]]%.q:OWNER%, [%R%?[N]]'table', [%R%?[N]]%.q:TABLE%, [%R%?[N]]'column', [%R%?[N]]%.q:COLUMN% -:declare @CurrentUser sysname -select @CurrentUser = user_name() -[.O:[execute ][exec ]]sp_dropextendedproperty [%R%?[N]]'MS_Description', - [%R%?[N]]'schema', [%R%?[N]]@CurrentUser, [%R%?[N]]'table', [%R%?[N]]%.q:TABLE%, [%R%?[N]]'column', [%R%?[N]]%.q:COLUMN% -] -end - - -][%OWNER%?[.O:[execute ][exec ]]sp_addextendedproperty [%R%?[N]]'MS_Description', - [%R%?[N]]%.q:COMMENT%, - [%R%?[N]]'schema', [%R%?[N]]%.q:OWNER%, [%R%?[N]]'table', [%R%?[N]]%.q:TABLE%, [%R%?[N]]'column', [%R%?[N]]%.q:COLUMN% -:select @CurrentUser = user_name() -[.O:[execute ][exec ]]sp_addextendedproperty [%R%?[N]]'MS_Description', - [%R%?[N]]%.q:COMMENT%, - [%R%?[N]]'schema', [%R%?[N]]@CurrentUser, [%R%?[N]]'table', [%R%?[N]]%.q:TABLE%, [%R%?[N]]'column', [%R%?[N]]%.q:COLUMN% -] - - -0 - -1411699225 -Administrator -Command for adding a column comment using an extended property of MS SQL Server 2000 to store object description: "MS_Description". - - -020174CE-8FFF-4AE4-B81C-6643DFEBD13F -AddColnChck -alter table [%QUALIFIER%]%TABLE% - add [constraint %CONSTNAME% ]check [%ExtCkcNotForReplication%?not for replication ](%.A:CONSTRAINT%) -0 - -0 - -Allows to customize the script for modifying column constraints within an alter table statement - - -2E6E69A9-C5BA-48ED-AA52-88052DAC947E -SqlStatistics -[%ISLONGDTTP%?{ AverageLength } -select [%ISLONGDTTP%?[%ISSTRDTTP%?avg(LEN(%COLUMN%)):avg(datalength(%COLUMN%))]:null] as average_length -from [%QUALIFIER%]%TABLE% -:{ NullValuesRate, DistinctValues, AverageLength } -select -[%COLTYPE%="uniqueidentifier"?null:[%COLTYPE%="xml"?null:[%ISMAND%?null:(count(*) - count(%COLUMN%)) * 100 / count(*)]]] as average_length, -[%COLTYPE%="uniqueidentifier"?null:[%COLTYPE%="xml"?null:count(distinct %COLUMN%)]] as distinct_values, -[%ISVARDTTP%?[%ISSTRDTTP%?avg(LEN(%COLUMN%)):avg(datalength(%COLUMN%))]:null] as average_length -from [%QUALIFIER%]%TABLE%] -0 - -0 - -SQL query for retrieving statistics of column - - -2266FD7A-793A-4C01-93CC-0E4AA845AA64 -ReversedStatements -ModifyColumn -0 - -0 - -Additional statements to be reversed by script - - - - -16BC4D0F-514F-42B7-8482-99F2C069118A -MaxConstLen -0 - -0 - -Maximum constraint name length -128 - - -F84F6C77-5665-4C2E-9B74-536D688AC5F9 -User -USER -0 - -1277121181 -psitja -The following system variables are available: - "USER" // generated code of the user - - - -A5D8CC25-0E73-4359-A313-BBA1801C3519 -Enable -0 - -1285930321 -psitja -User allowed - - -14D9897E-8F0C-4056-9E42-F581DCD4FD46 -Maxlen -0 - -0 - -Maximum object code length -128 - - -A2D2EA0C-199F-4DDA-B673-B78C6BC77E8F -SqlListQuery -0 - -1277121202 -psitja -SQL query to list objects - - -94BE4103-C2AE-4641-87D4-A39D6947EE1A -SqlAttrQuery -0 - -0 - -SQL query to reverse object attributes - - -DF74001B-4D4C-4487-8A09-1D6C1BB48FE3 -Create -0 - -1277121181 -psitja -Command for creating a user - - -1CDE27F3-6656-499D-8B00-2FD87DBFE35E -Drop -0 - -1277121181 -psitja -Command for dropping a user - - - - -0D484CC9-6666-43BE-B514-04880BFA435A -EnableOption -0 - -0 - -Physical options allowed YES/NO -1 - - -FF2287A9-8788-4F7F-8DF6-410ED5104BDF -Reference -REFR -0 - -0 - -The following system variables are available: - "REFR" // generated code of the reference - "REFNAME" // reference name - "PARENT" // generated code of the parent table - "PNAME" // name of the parent table - "PCODE" // code of the parent table - "CHILD" // generated code of the reference - "CNAME" // name of the child table - "CCODE" // code of the child table - "PQUALIFIER" // qualifier of the parent table. See QUALIFIER - "CQUALIFIER" // qualifier of the child table. See QUALIFIER - "REFRNAME" // Reference name - "REFRCODE" // Reference code - "FKCONSTRAINT" // Reference constraint name (foreign key) - "PKCONSTRAINT" // constraint name of the parent key used to reference object - "CKEYCOLUMNS" // list of parent key columns. Eg: C1, C2, C3 - "FKEYCOLUMNS" // list of child foreign key columns. Eg: - "UPDCONST" // Update declarative constraint. Keywords "restrict", "cascade", "set null" or "set default" - "DELCONST" // Delete declarative constraint. Keywords "restrict", "cascade", "set null" or "set default" - "MINCARD" // Min cardinality - "MAXCARD" // Max cardinality - "POWNER" // Parent table owner - "COWNER" // child table owner - "CHCKONCMMT" // TRUE when check on commit is selected on the reference (ASA 6.0 specific) - -For reference joins (couple of column in a reference), -the following system variables are available: - "CKEYCOLUMN" // generated code of the parent table column (primary key) - "FKEYCOLUMN" // generated code of the child table column (foreign key) - "PK" // primary key column generated code - "PKNAME" // primary key column name - "FK" // foreign key column generated code - "FKNAME" // foreign key column name - "AK" // alternate key column code (same as PK) - "AKNAME" // alternate key column name (same as PKNAME) - "COLTYPE" // primary column column datatype - "DEFAULT" // foreign key column default value - - - -6DD68604-54D9-43A9-9448-24A350BAFA10 -Enable -0 - -0 - -Foreign key allowed -1 - - -559B6024-BA73-4285-B281-9DDD51237890 -FKAutoIndex -0 - -0 - -Foreign key is auto-indexed - - -722C0ACA-5530-458C-9E30-431A54D58F04 -ConstName -FK_%.U8:CHILD%_%.U9:REFR%_%.U8:PARENT% -0 - -0 - -Constraint name template for foreign keys - - -FB17E6F7-D563-498F-A08C-E55114AC2E4A -CheckOnCommit -0 - -0 - -Referential integrity test differed after the COMMIT - - -1FC604B4-A50B-4C73-B070-E203AE618043 -Drop -if exists (select 1 - from [%CATALOG%.]sys.sysreferences r join [%CATALOG%.]sys.sysobjects o on (o.id = r.constid and o.type = 'F') - where r.fkeyid = object_id('[%QUALIFIER%]%TABLE%') and o.name = %.q:CONSTNAME%) -alter table [%QUALIFIER%]%TABLE% - drop constraint %CONSTNAME% -0 - -0 - -Command for dropping a foreign key - - -E4C87346-0188-47CE-9E96-8303CD536BF9 -Add -[constraint %CONSTNAME% ]foreign key (%FKEYCOLUMNS%) - references [%PQUALIFIER%]%PARENT%[ (%CKEYCOLUMNS%)][.Z: - [%R%?[on update [%UPDCONST%?%UPDCONST%:no action ]]:[%UPDCONST%?[%UPDCONST%=RESTRICT?:on update %UPDCONST% ]]][%R%?[on delete [%DELCONST%?%DELCONST%:no action ]]:[%DELCONST%?[%DELCONST%=RESTRICT?:on delete %DELCONST% ]]] - [%ExtFkNotForReplication%?not for replication]] -0 - -0 - -Command for defining a foreign key - - -E7DA87FB-8128-4757-9901-4444EA15AD42 -Create -alter table [%QUALIFIER%]%TABLE%[%R%?[ with nocheck][ with check]] - add [constraint %CONSTNAME% ]foreign key (%FKEYCOLUMNS%) - references [%PQUALIFIER%]%PARENT%[ (%CKEYCOLUMNS%)][.Z: - [%R%?[on update [%UPDCONST%?%UPDCONST%:no action ]]:[%UPDCONST%?[%UPDCONST%=RESTRICT?:on update %UPDCONST% ]]][%R%?[on delete [%DELCONST%?%DELCONST%:no action ]]:[%DELCONST%?[%DELCONST%=RESTRICT?:on delete %DELCONST% ]]] - [%ExtFkNotForReplication%?not for replication]] -0 - -0 - -Command for adding a foreign key - - -51A1CAD4-6349-444A-B70C-7E04C7E6114A -DclUpdIntegrity -NO ACTION -CASCADE -SET NULL -SET DEFAULT -0 - -0 - -Declarative referential integrity constraint allowed for update - - -6094047D-927F-4E52-B33B-C69D9E8CFDD0 -DclDelIntegrity -NO ACTION -CASCADE -SET NULL -SET DEFAULT -0 - -0 - -Declarative referential integrity constraint allowed for delete - - -D6285EC6-796A-4D05-A974-523F772364EB -SqlListQuery -{COWNER, CHILD, FKCONSTRAINT, ExtFkNotForReplication, UPDCONST, DELCONST, POWNER, PARENT} - -select - u.name, - c.name, - o.name, - case (r.is_not_for_replication) when 1 then 'true' else 'false' end, - case(r.update_referential_action ) when 1 then 'cascade' when 2 then 'set null' when 3 then 'set default' else '' end, - case(r.delete_referential_action ) when 1 then 'cascade' when 2 then 'set null' when 3 then 'set default' else '' end, - v.name, - p.name -from - [%CATALOG%.]sys.foreign_keys r - join [%CATALOG%.]sys.objects o on (o.object_id = r.object_id and o.type = 'F') - join [%CATALOG%.]sys.objects c on (c.object_id = r.parent_object_id) - join [%CATALOG%.]sys.schemas u on (u.schema_id = c.schema_id) - join [%CATALOG%.]sys.objects p on (p.object_id = r.referenced_object_id) - join [%CATALOG%.]sys.schemas v on (v.schema_id = p.schema_id) -where 1 = 1 -[ and u.name = %.q:SCHEMA%] -[ and c.name = %.q:TABLE%] -0 - -0 - -SQL query to list objects - - -964D435A-44A6-46F8-9236-69DDE53AD78B -SqlListChildrenQuery -{COWNER, CHILD, FKCONSTRAINT, FKEYCOLUMN, CKEYCOLUMN} - -select - u.name, - t.name, - o.name, - c.name, - p.name -from - [%CATALOG%.]sys.foreign_key_columns j - join [%CATALOG%.]sys.objects o on (o.object_id = j.constraint_object_id and o.type = 'F') - join [%CATALOG%.]sys.columns p on (p.object_id = j.referenced_object_id and p.column_id = j.referenced_column_id) - join [%CATALOG%.]sys.columns c on (c.object_id = j.parent_object_id and c.column_id = j.parent_column_id) - join [%CATALOG%.]sys.objects t on (t.object_id = j.parent_object_id) - join [%CATALOG%.]sys.schemas u on (u.schema_id = t.schema_id) -where 1 = 1 -[ and o.name = %.q:FKCONSTRAINT%] -[ and u.name = %.q:COWNER%] -[ and t.name = %.q:TABLE%] -order by 1, 2, 3, j.constraint_column_id -0 - -0 - -SQL query to list reference joins - - - - -FD41D614-2DFE-4C25-8269-33810BD7C3A7 -Permission -PERM -0 - -0 - -Manages the permissions on database objects - - -1CFFA389-457F-4C94-A2A4-757C592DBC0C -Create -grant %PERMLIST% on [%QUALIFIER%]%OBJECT% to %GRANTEE% [%GRANTOPTION%] -0 - -0 - -Command for granting permissions. - - -3E1AD819-A1A1-4734-BB27-74FB47227E50 -Drop -revoke %REVPERMLIST% on [%QUALIFIER%]%OBJECT% from %GRANTEE% -0 - -0 - -Command for revoking permissions. - - -7416F676-1412-4A27-A651-9D4771EE04DB -GrantOption -with grant option -0 - -0 - -Option for the grant statement. - - -F3F9CBCE-7888-4146-8562-8C4B2171DA1D -RevokeInherited -0 - -0 - -Allow to revoke an inherited permission from a group or a role. -1 - - - - -C10A3CA2-1D1B-4E5C-B443-EACC2D62479E -PKey -PKEY -0 - -0 - -The following system variables are available: -(parent table items are also available for keys) - "PKEYCOLUMNS" // list of primary key columns. Eg: A, B - "ISPKEY" // TRUE when the key is the primary key of the table - "KEY" // constraint name - "PKEY" // constraint name for primary key - "AKEY" // constraint name for alternate key - "ISMULTICOLN" // TRUE if key has more than one column - "CLUSTER" // keyword cluster - - -249A5D36-C782-4596-B6AD-C6EDAA992D71 -Enable -0 - -0 - -Primary key allowed -1 - - -3EC46083-4745-4C22-B118-3A84A3315225 -PkAutoIndex -0 - -0 - -Primary key is auto-indexed -1 - - -B4F7F67A-8223-4C1D-9CBD-74B4377AB6F2 -ConstName -PK_%.U27:TABLE% -0 - -0 - -Constraint name template for primary keys - - -D34DC4F6-3175-4576-AAA9-F8697EA181FE -Options -with : composite=yes, separator=yes, parenthesis=yes, chldmand=yes -{ -pad_index=%s : list=on|off -fillfactor=%d : default=10 -sort_in_tempdb=%s : list=on|off -ignore_dup_key=%s : list=on|off -statistics_norecompute=%s : list=on|off -drop_existing=%s : list=on|off -online=%s : list=on|off -allow_row_locks=%s : list=on|off -allow_page_locks=%s : list=on|off -maxdop=%s -<compression> : composite=yes, separator=yes -{ - data_compression = %s : list=none|row|page, composite=yes, multiple=yes - { - on partitions : composite=yes, parenthesis=yes, separator=yes - { - <expression_or_range> %s : multiple=yes - } - } -} -} -on : composite=yes -{ - <partition scheme> %s : composite=yes, parenthesis=yes, chldmand=yes - { - <column> %s - } - <filegroup> %s : category=storage, default="DEFAULT" -} -filestream_on : composite=yes, chldmand=yes -{ - <partition scheme> %s : composite=yes, parenthesis=yes, chldmand=yes - { - <column> %s - } - <filegroup> %s : category=storage, default="DEFAULT" -} -0 - -1328011320 -mledier -Available options for creating a primary key - - -0FD91CA1-86B3-4340-B3B9-653460F55CEC -DefOptions -0 - -0 - -Default values for primary key options - - -BA6602D2-3A36-4C9D-8969-2C50FFCBB520 -Drop -alter table [%QUALIFIER%]%TABLE% - drop constraint %CONSTNAME% -0 - -0 - -Command for dropping a primary key - - -4346E5CE-2E92-4F03-8DA7-4B9FC10B1F7F -Add -[constraint %CONSTNAME% ]primary key [%CLUSTER%?[%R%?[clustered ]]:nonclustered ](%PKEYCOLUMNS%) - [%OPTIONS%] -0 - -0 - -Command for defining a primary key - - -FEF2C6FE-4851-4AB5-8466-135D51C81B21 -Create -alter table [%QUALIFIER%]%TABLE%[%R%?[ with nocheck][ with check]] - add [constraint %CONSTNAME% ]primary key [%CLUSTER%?[%R%?[clustered ]]:nonclustered ](%PKEYCOLUMNS%) - [%OPTIONS%] -0 - -0 - -Command for adding a primary key - - -F3A4CC2B-5F55-4961-9F96-F914C2F4D460 -EnableCluster -0 - -0 - -Cluster option available for primary keys -1 - - - - -DF0C644D-734E-4DB0-9948-0B897F727A96 -Privilege -0 - -0 - -Manages the system privileges for a user, role or group - - -29EE4AB4-D34C-42E0-B0C2-0CC6A6884C98 -Create -grant %PRIVLIST% to %USER% -0 - -0 - -Command for granting privileges. - - -59F3E26C-832C-49D8-8784-4FAB2DEE3CB3 -Drop -revoke %REVPRIVLIST% from %USER% -0 - -0 - -Command for revoking privileges. - - -7D162333-2AEF-467E-86BA-01FF4613FC05 -System -BACKUP DATABASE -BACKUP LOG -CREATE DATABASE -CREATE DEFAULT -CREATE FUNCTION -CREATE PROCEDURE -CREATE RULE -CREATE TABLE -CREATE VIEW -0 - -0 - -List of system privileges - - -E31E0BFD-8DED-46BC-B503-D06005156384 -RevokeInherited -0 - -0 - -Allows to revoke an inherited privilege from a group or a role. -1 - - - - -1AA527B0-0ACC-46B3-B856-51151F62F293 -Key -KEY -0 - -1328015677 -mledier -The following system variables are available: -(parent table items are also available for keys) - "COLUMNS" // List of columns of the key. Eg: "A, B, C" - "ISPKEY" // TRUE when the key is the primary key of the table - "KEY" // constraint name - "PKEY" // constraint name for primary key - "AKEY" // constraint name for alternate key - "ISMULTICOLN" // TRUE if key has more than one column - "CLUSTER" // keyword cluster - - - -8DF94FAF-8239-49AB-B33B-7BC972FD6770 -Enable -0 - -0 - -UNIQUE constraint allowed for tables -1 - - -B9B326A9-870B-4D88-8ABE-FE362675C553 -UniqConstAutoIndex -0 - -0 - -UNIQUE constraint is auto-indexed -1 - - -484D1C02-A346-41E9-AB3D-30E671AE908A -ConstName -AK_%.U18:AKEY%_%.U8:TABLE% -0 - -0 - -Constraint name template for alternate keys - - -E2E6BA20-FB69-4389-B880-C0E31DA5DBB0 -Options -with : composite=yes, separator=yes, parenthesis=yes, chldmand=yes -{ -pad_index=%s : list=on|off -fillfactor=%d : default=10 -sort_in_tempdb=%s : list=on|off -ignore_dup_key=%s : list=on|off -statistics_norecompute=%s : list=on|off -drop_existing=%s : list=on|off -online=%s : list=on|off -allow_row_locks=%s : list=on|off -allow_page_locks=%s : list=on|off -maxdop=%s -<compression> : composite=yes, separator=yes -{ - data_compression = %s : list=none|row|page, composite=yes, multiple=yes - { - on partitions : composite=yes, parenthesis=yes, separator=yes - { - <expression_or_range> %s : multiple=yes - } - } -} -} -on : composite=yes -{ - <partition scheme> %s : composite=yes, parenthesis=yes, chldmand=yes - { - <column> %s - } - <filegroup> %s : category=storage, default="DEFAULT" -} -filestream_on : composite=yes, chldmand=yes -{ - <partition scheme> %s : composite=yes, parenthesis=yes, chldmand=yes - { - <column> %s - } - <filegroup> %s : category=storage, default="DEFAULT" -} -0 - -1328011320 -mledier -Available options for an alternate key - - -B60CBB50-2B8D-4FCF-949F-F1E525E7A774 -DefOptions -0 - -0 - -Default values for alternate key options - - -82C7CF5E-A0B7-4DAA-A021-FCD5F0DE9157 -SqlAkeyIndex -{INDEX} - -select - o.name -from - [%CATALOG%.]sys.sysobjects o - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.uid) - join [%CATALOG%.]sys.sysindexes i on (i.id = o.parent_obj and i.name = o.name) -where o.xtype='UQ' and o.type = 'U' - [and o.name = %.q:TABLE%] - [and u.name=%.q:OWNER%] -0 - -0 - -SQL query to obtain the alternate key indexes of a table by ODBC - - -5552593F-9444-4F6B-AD8E-15B2E5A0DAE3 -Drop -alter table [%QUALIFIER%]%TABLE% - drop constraint %CONSTNAME% -0 - -0 - -Command for dropping an alternate key - - -9CB7FCBB-1EEE-44E7-870B-2761AE0C1E5F -Add -[constraint %CONSTNAME% ]unique [%CLUSTER%?clustered :[%R%?[nonclustered ]]](%COLUMNS%) - [%OPTIONS%] -0 - -0 - -Command for defining an alternate key - - -45092803-49C9-450D-A66F-27D4911573CA -Create -alter table [%QUALIFIER%]%TABLE%[%R%?[ with nocheck][ with check]] - add [constraint %CONSTNAME% ]unique [%CLUSTER%?clustered :[%R%?[nonclustered ]]](%COLUMNS%) - [%OPTIONS%] -0 - -0 - -Command for adding an alternate key - - -B5439FA8-191F-4B54-80E1-38C0DD39B0DC -SqlListQuery -{OWNER ID, TABLE ID, CONSTNAME ID, ISPKEY ID, CLUSTER ID, COLUMNS ...} - -select - u.name, - o.name, - k.name, - case (k.xtype) when 'PK' then 1 else 0 end, - case (i.indid) when 1 then 'YES' else 'NO' end, - c.name + ', ' -from - [%CATALOG%.]sys.sysobjects k - join [%CATALOG%.]sys.sysobjects o on (o.type = 'U' and o.id = k.parent_obj) - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.uid) - join [%CATALOG%.]sys.sysindexes i on (i.id = o.id and i.name = k.name) - join [%CATALOG%.]sys.sysindexkeys x on (x.id = o.id and x.indid = i.indid) - join [%CATALOG%.]sys.syscolumns c on (c.id=x.id and c.colid=x.colid) -where k.type = 'K' -[ and u.name=%.q:OWNER%] -[ and o.name=%.q:TABLE%] -order by u.name, o.name, x.indid, x.keyno -0 - -0 - -SQL query to list objects - - -0E4E85FB-5336-4A8E-8E0C-B634F990F858 -SqlOptsQuery -0 - -1327685723 -mledier -SQL query to reverse object physical options - - -AF44D42C-8827-4C2B-94C5-23820C5D806B -SqlAttrQuery -0 - -1327934697 -mledier -SQL query to reverse object attributes - - -A3FFE65F-544E-4AD4-B524-2C5CA48549D5 -SqlPartitionCols -{OWNER ID, TABLE ID, CONSTNAME ID, PartitionSchemeColumn ...} - -select - u.name, - o.name, - k.name, - c.name + ',' -from - [%CATALOG%.]sys.objects k - join [%CATALOG%.]sys.objects o on (o.type = 'U' and o.object_id = k.parent_object_id) - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.schema_id) - join [%CATALOG%.]sys.indexes i on (i.object_id = o.object_id and i.name = k.name) - join [%CATALOG%.]sys.index_columns x on (x.object_id = o.object_id and x.index_id = i.index_id and x.partition_ordinal > 0) - join [%CATALOG%.]sys.columns c on (c.object_id = x.object_id and c.column_id = x.column_id) -where 1=1 -[ and u.name = %.q:OWNER%] -[ and o.name = %.q:TABLE%] -[ and k.name=%.q:CONSTNAME%] - -1327577850 -mledier -1328022965 -mledier -1 - - -8718F0EC-30FC-43BB-B659-1DAF0B46EFB9 -EnableCluster -0 - -0 - -Cluster option available for alternate keys -1 - - -2381747B-76E7-4D61-A05B-FF224CA3FD4E -ReversedQueries -SqlPartOptions -SqlPartitionCols -SqlCompressionQuery -1327683294 -mledier -1328015677 -mledier -Additional attributes queries to be called by ODBC - - -C1C57B56-C1F8-488F-9A12-186282CDD323 -SqlPartOptions -{OWNER, TABLE, CONSTNAME, OPTIONS} - -select - u.name, - o.name, - k.name, - replace('with (' - + case when (i.allow_row_locks != 0) then '' else 'allow_row_locks=off' end - + case when (i.allow_page_locks != 0) then '' else 'allow_page_locks=off' end - + case when (i.is_padded != 0) then 'pad_index=on' else '' end - + case when (i.fill_factor != 0) then 'fillfactor=' + convert(varchar, i.fill_factor) else '' end - + case when (i.ignore_dup_key != 0) then 'ignore_dup_key=on' else '' end - + ')', 'with ()', '') - + case(i.data_space_id & 65536) - when 0 then - (select ' on ' + f.name from [%CATALOG%.]sys.filegroups f where f.data_space_id = i.data_space_id) - when 65536 then - (select ' on ' + p.name + ' (x)' - from [%CATALOG%.]sys.partition_schemes p where p.data_space_id = i.data_space_id) - else '' - end -from [%CATALOG%.]sys.objects k - join [%CATALOG%.]sys.objects o on (o.type = 'U' and o.object_id = k.parent_object_id) - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.schema_id) - join [%CATALOG%.]sys.indexes i on (i.object_id = o.object_id and i.name = k.name) -where k.type in ('PK', 'UQ') -[ and u.name=%.q:OWNER%] -[ and o.name=%.q:TABLE%] -[ and k.name=%.q:CONSTNAME%] - -1327934655 -mledier -1327934697 -mledier -1 - - -56FAF59C-6DB9-4434-82EE-C1667B03EA24 -SqlCompressionQuery -{OWNER ID, TABLE ID, CONSTNAME ID, WithCompression ...} - -select - u.name, - o.name, - k.name, - case when(p.data_compression > 0) - then 'data_compression = ' - + lower(p.data_compression_desc) - + ' on partitions (' - + convert(varchar, p.partition_number) - + '), ' - else '' - end -from - [%CATALOG%.]sys.objects k - join [%CATALOG%.]sys.objects o on (o.type = 'U' and o.object_id = k.parent_object_id) - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.schema_id) - join [%CATALOG%.]sys.indexes i on (i.object_id = o.object_id and i.name = k.name) - join [%CATALOG%.]sys.partitions p on (p.object_id = o.object_id and p.index_id = i.index_id) -where - o.type = 'U' -[ and u.name=%.q:OWNER%] -[ and o.name=%.q:TABLE%] -[ and k.name=%.q:CONSTNAME%] -order by 1, 2, 3, p.partition_number -1328001249 -mledier -1328016123 -mledier -1 - - - - -77E46E6A-0379-4FA3-A342-64732BC5D5E4 -Domain -PDMDOMN -0 - -0 - -The following system variables are available: - "DOMAIN" // generated code of the domain (also available for columns) -SQL Server specific domain system variables: - "RULENAME" // name of the rule object associated with the domain - "DEFAULTNAME" // name of the default object associated with the domain - - - -F379A151-1956-4EE1-A621-B891629FA68D -Enable -0 - -0 - -User defined data types allowed -1 - - -E9556D8D-BDED-4D57-A113-09B35064726A -Maxlen -0 - -0 - -Maximum object code length -128 - - -0E6FC3C4-585D-4F37-A4FF-E0C08785B0CD -Create -[%R%?[%R%?[exec ]][execute ]sp_addtype %DOMAIN%, %.q:DATATYPE%[%NOTNULL%?, 'not null':[%R%?, 'null']]:create type [%QUALIFIER%]%DOMAIN% - from %DATATYPE% [%NOTNULL%]] - -0 - -0 - -Command for creating a user defined data type - - -E23D09D1-C451-45E5-89AA-D1A61F410EC7 -Drop -if exists(select 1 from systypes where name=%.q:DOMAIN%) - drop type [%QUALIFIER%]%DOMAIN% -0 - -0 - -Command for dropping a user defined data type - - -54234650-737B-48B0-96D2-560105B388EF -SqlListQuery -{OWNER, DOMAIN, DTTPCODE, LENGTH, SIZE, PREC, NOTNULL} - -select - u.name, - d.name, - case when d.system_type_id in (165, 167, 231) and d.max_length = -1 then t.name + '(Max)' else t.name end as colnA, - d.precision, - case (d.max_length) when -1 then 0 else case when d.system_type_id in (99, 231, 239) then (d.max_length/2) else (d.max_length) end end as colnB, - d.scale, - case(d.is_nullable) when 1 then 'NULL' else 'NOTNULL' end -from - [%CATALOG%.]sys.types d - join [%CATALOG%.]sys.schemas u on (u.schema_id = d.schema_id) - left outer join [%CATALOG%.]sys.types t on (t.user_type_id = d.system_type_id and t.user_type_id = t.system_type_id) -where - d.is_user_defined = 1 and d.is_assembly_type = 0 -[ and u.name = %.q:SCHEMA%] -order by 1, 2 - -0 - -0 - -SQL query to list objects - - -7D2E2BE7-11B8-4DD6-8483-6826DFAF3834 -EnableBindRule -0 - -0 - -Allows the binding of a business rule to a domain -1 - - -6CAA7E14-FAFE-42F1-9FB2-C8902517A5D2 -Bind -[%R%?[exec ]][execute ]sp_bindrule %RULE%, %DOMAIN% -0 - -0 - -Command for binding a rule to a domain - - -69D8A08D-342A-4FEE-86EF-91B5A72830F2 -Unbind -if exists(select 1 from systypes where name=%.q:DOMAIN%) - [%R%?[exec ]][execute ]sp_unbindrule %DOMAIN% -0 - -0 - -Command for unbinding a rule from a domain - - -B5647589-1520-4D3D-ACCC-9A064EEC1801 -EnableOwner -0 - -0 - -Owner allowed -1 - - -7EBD26D9-92CC-4A13-A3D8-43254FB5876A -SqlAttrQuery -{OWNER, DOMAIN, RULENAME} - -select - u.name, - d.name, - convert(char(20), d.domain) -from - [%CATALOG%.]sys.systypes d - join [%CATALOG%.]sys.schemas u on (u.schema_id = d.uid) -where - d.domain <> 0 - and d.usertype >= 100 -[ and u.name = %.q:OWNER%] -[ and d.name = %.q:DOMAIN%] -order by 1 - -0 - -0 - -SQL query to reverse object attributes - - - - -49600175-0522-4601-BB67-4FBC89DACBCA -Rule -RULE -0 - -0 - -The following system variables are available: - "RULE" // generated code of the rule - "RULENAME" // rule name - "RULECODE" // rule code - "RULECEXPR" // rule client expression - "RULESEXPR" // rule server expression - - - -71DCE33C-43DF-40CF-882C-69D60F018A4B -MaxDefaultLen -0 - -0 - -Length of name of default -128 - - -00390F17-4D0F-4493-BE25-C992CEF1B838 -UddtDefaultName -0 - -0 - -Name of default for domain -D_%.28:DOMAIN% - - -25830A1D-80B2-48EE-A7E6-F31CF1BBA407 -ColnDefaultName -0 - -0 - -Name of default for column -D_%.19:COLUMN%_%.8:TABLE% - - -30CEEA2E-A2AF-4308-BE5C-C23C2B9E0A4D -Maxlen -0 - -0 - -Maximum object code length -128 - - -9980E0D9-6A61-4765-8235-A0F5A3B02B0B -UddtRuleName -0 - -0 - -Name of rules for domains -R_%.28:DOMAIN% - - -A0E2B0F4-802D-404B-B188-68552B2B034F -ColnRuleName -0 - -0 - -Name of rules for columns -R_%.19:COLUMN%_%.8:TABLE% - - -22B9213A-5486-4519-A953-BE3E00618691 -Enable -0 - -0 - -Rule allowed -1 - - -DDDA409C-458E-4B33-AB26-F48E0734C064 -Create -create rule [%S%.]%RULE% as - %RULESEXPR% - -0 - -0 - -Command for adding a rule - - -7BC4F98C-637F-4DA0-A5E0-83C842BCE8A3 -Drop -if exists (select 1 from sysobjects where id=object_id(%.q:RULE%) and type='R') - drop rule %RULE% - -0 - -0 - -Command for dropping a rule - - -FC4E86EF-B458-4138-A8AD-EC3C2173C8B8 -SqlListQuery -{OWNER ID, @OBJTNAME ID, @OBJTCODE ID, SCRIPT ...} - -select - u.name, - r.name, - convert(char(20), r.id), - ltrim(substring(t.text, (charindex('as', lower(t.text), charindex(lower(r.name), lower(t.text)) + len(r.name))+ 2), len(t.text) - 2)) as coln -from - [%CATALOG%.]sys.sysobjects r - join [%CATALOG%.]sys.schemas u on (u.schema_id = r.uid) - join [%CATALOG%.]sys.syscomments t on (t.id=r.id and t.texttype in (0, 2)) -where - r.type='R' -[ and u.name = %.q:SCHEMA%] -order by - 1, 2, t.number, t.colid -0 - -0 - -SQL query to list objects - - - - -DDEC83F4-DF08-480C-8B1C-D65E9DA4E48F -Database -DTBS -0 - -0 - -The following system variables are available: - "DATABASE" // generated code of the database - - - -66E204DA-68D3-4AF8-A8F1-9C6FCD29CB38 -AfterCreate -[%AlterDBSetMirroring% -go -] -0 - -0 - -Commands executed after create statement - - -279C4B3F-B63D-4600-B801-54F0E6438DF2 -AlterDBSetMirroring -[%EnableMirroring%?alter database %DATABASE% - set [%Partner%?partner :witness ][%MirrorOptions%=server?= %.q:MirrorServer%:%MirrorOptions%[%MirrorOptions%=timeout? %TimeOut%]]] - -0 - -0 - -1 - - -1CCDA9A2-8EC5-40B4-B9B8-4B0FB12950A9 -Enable -0 - -0 - -Database allowed -1 - - -4547ED05-1FCC-48D6-B3FD-971E5DBE6774 -Create -create database %DATABASE% -[%OPTIONS%] -0 - -0 - -Command for creating a database. Example: create database %DATABASE% - - -FBD310D0-47C2-49D8-BD6A-98B351FA2FE5 -Options -on : composite=yes -{ -primary -<filelist> : composite=yes, separator=yes -{ -<filespec> : composite=yes, multiple=yes, parenthesis=yes, separator=yes -{ -name= %s -filename= %s : squoted=yes -size= %s -maxsize= %s -filegrowth= %s -} -filegroup %s : composite=yes, multiple=yes, parenthesis=yes, separator=yes -{ -name= %s -filename= %s : squoted=yes -size= %s -maxsize= %s -filegrowth= %s -} -} -} -log on : composite=yes, separator=yes -{ -<filespec> : composite=yes, multiple=yes, parenthesis=yes, separator=yes -{ -name= %s -filename= %s : squoted=yes -size= %s -maxsize= %s -filegrowth= %s -} -} -collate %s -with : composite=yes, chldmand=yes, separator=yes -{ -db_chaining %s : list=ON|OFF, default=OFF -trustworthy %s : list=ON|OFF, default=OFF -} -for : composite=yes, chldmand=yes -{ -attach : composite=yes -{ -with %s : list=enable_broker|new_broker|error_broker_conversations -} -attach_rebuild_log -} -as snapshot of %s -0 - -0 - -Available options for creating a database - - -5BAF27BE-5C92-49C4-9F0B-60729D778B5D -DefOptions -0 - -0 - -Default values for database options - - -07EA6CB1-75EC-4561-8630-0244BDA64D59 -OpenDatabase -use %DATABASE% -0 - -0 - -Command for opening a database. Example: open database %DATABASE% - - -6D3BFE63-49FC-4F5B-96DF-54E275CAE7D5 -CloseDatabase -0 - -0 - -Command for closing a database. Example: close database - - -5218938B-B90E-4EB9-B365-3A4001871214 -Drop -drop database %DATABASE% -0 - -0 - -Command for dropping a database. Example: drop database %DATABASE% - - -112737FC-D785-4421-B51C-D2BB62088E98 -ReversedStatements -AlterDBSetMirroring -0 - -0 - -Additional statements to be reversed by script - - - - -C8E963AF-0DFC-4D2B-95C5-D2D5C1812742 -Tablespace -TSPC -0 - -0 - -The following system variables are available: - "TABLESPACE" // generated code of the tablespace - - - -3ADE7744-56A8-4F07-9401-2976B8166C74 -Create -0 - -0 - -Command for creating a tablespace. Example: create tablespace %TABLESPACE% - - -1CADF607-6ACA-4D6B-9652-02191D7D21D4 -Drop -0 - -0 - -Command for dropping a tablespace. Example: drop tablespace %TABLESPACE% - - -871921E2-6474-4431-A681-B23E3A67B199 -Options -0 - -0 - -Default options for creating a tablespace. - - -20244AED-92F3-4762-A191-DB3DAD8C9CAA -DefOptions -0 - -0 - -Default values for tablespace options. - - -AACD64E9-F875-4F47-B4C9-ECFC2D96CAC8 -Enable -0 - -0 - -Tablespace allowed - - - - -D416DB29-947F-4816-913F-A36849BC2214 -Storage -STOR -0 - -0 - -The following system variables are available: - "STORAGE" // generated code of the storage - - - -AD5BC4A3-63AC-4393-894C-6E4914E42DBC -AfterCreate -[%usedb(Database)%] - -0 - -0 - -Commands executed after create statement - - -A8A07CDE-9ACC-4F25-B328-7C70CE673E40 -AfterDrop -%usedb(Database)% - -0 - -0 - -Commands executed after drop statement - - -9451DE7B-1451-4BC5-996B-E034BC86C611 -BeforeCreate -[%usedb(master)%] - -alter database %DATABASE% add filegroup %STORAGE%[%FileStream%? contains filestream] -go -0 - -0 - -Commands executed before create statement - - -FDE833A2-5685-43EA-9D13-8016C3642C2D -BeforeDrop -%usedb(master)% -0 - -0 - -Commands executed before drop statement - - -3E8DBB54-331A-4AA7-A608-B53D8877343A -Create -[%OPTIONS%?alter database %DATABASE% add - %OPTIONS% - to filegroup %STORAGE% -] -0 - -0 - -Command for creating a storage. Example: create storage %STORAGE% - - -D90BC271-98B0-4E52-910E-2C4AFA71C6AE -Drop -alter database %DATABASE% - remove filegroup %STORAGE% -0 - -0 - -Command for dropping a storage. Example: drop storage %STORAGE% - - -DEE0B7D2-395F-4BB9-8D54-C70A8D7D71D4 -Options -file : composite=yes, separator=yes -{ - <definition> : composite=yes, parenthesis=yes, separator=yes, multiple=yes - { - name = %s - filename = %s : squoted=yes - size = %s - maxsize = %s - filegrowth = %s - } -} - - -0 - -0 - -Default options for creating a storage - - -7F03B9C4-4E04-4FA3-B43E-00D5FCAB3050 -DefOptions -0 - -0 - -Default values for storage options - - -8566D151-9715-4D3F-BE82-94E6FC002F01 -Enable -0 - -0 - -Storage allowed -1 - - -F3A2511A-1E6E-4947-8683-42F887B642CF -SqlListQuery -{DATABASE ID, STORAGE ID, OPTIONS ...} - -select - db_name(), - g.groupname, - case when (f.fileid=(select min(x.fileid) from sysfiles x where x.groupid = g.groupid)) - then 'file (' - else ', (' - end - + ' name = ' + rtrim(f.name) - + ', filename = ''' + rtrim(f.filename) + '''' - + ', size = ' + rtrim(f.size/128) - + ', maxsize = ' + case when (f.maxsize = -1) then 'unlimited' else rtrim(f.maxsize/128) end - + ', filegrowth = ' + rtrim(f.growth) - + ')' -from - sysfilegroups g - join sysfiles f on (f.groupid = g.groupid) -order by 1, 2, f.fileid -0 - -0 - -SQL query to list objects - - -E64F6262-7F4B-4A69-9587-AEA299D9979B -ReversedStatements -AfterCreate -0 - -0 - -Additional statements to be reversed by script - - - - -F377755C-9389-4327-9868-11DE89BD896E -View -VIEW -0 - -0 - -The following system variables are available: - "VIEW" // generated code of the view - "VIEWNAME" // view name - "VIEWCODE" // view code - "VIEWCOLN" // List of columns of the view. Eg: "A, B, C" - "SQL" // SQL text of the view. Eg: Select * from T1 - "VIEWCHECK" // Keyword "with check option" if selected on the view - "SCRIPT" // complete view creation order. Eg: create view V1 as select * from T1 - - - -9CBA2B77-2494-49F9-8C43-424F4B71A89F -Enable -0 - -0 - -View allowed -1 - - -61F2E17D-30FF-4FBA-8C3B-60024F64A840 -Create -create view [%QUALIFIER%]%VIEW%[ (%VIEWCOLN%)][%VIEWSTYLE%=WithOptions? %OPTIONS%] as -%SQL% -[%VIEWCHECK%] - -0 - -0 - -Command for creating a view. Example: create view %VIEW% - - -666CDA10-9B01-42C2-8DA3-A127C89A5AB9 -Drop -if exists (select 1 - from sysobjects - where id = object_id('[%QUALIFIER%]%VIEW%') - and type = 'V') - drop view [%QUALIFIER%]%VIEW% -0 - -0 - -Command for dropping a view. Example: drop view %VIEW% - - -EC7F99E6-210C-411E-9E17-D9FBB59F39C8 -Permission -0 - -0 - -Available permission for a view. -The first column is the SQL name of permission (eg: SELECT) -The second column is a shortname to display in a grid -SELECT=Select -INSERT=Insert -DELETE=Delete -UPDATE=Update -SELECT -INSERT -DELETE -UPDATE - - - -B512481E-830A-41EC-9EC2-AC606F016914 -ViewComment -[if exists (select 1 from sys.extended_properties - where major_id = object_id('[%QUALIFIER%]%VIEW%') and minor_id = 0) -begin - [%OWNER%?[.O:[execute ][exec ]]sp_dropextendedproperty [%R%?[N]]'MS_Description', - [%R%?[N]]'user', [%R%?[N]]%.q:OWNER%, [%R%?[N]]'view', [%R%?[N]]%.q:VIEW% -:declare @CurrentUser sysname -select @CurrentUser = user_name() -[.O:[execute ][exec ]]sp_dropextendedproperty [%R%?[N]]'MS_Description', - [%R%?[N]]'user', [%R%?[N]]@CurrentUser, [%R%?[N]]'view', [%R%?[N]]%.q:VIEW% -] -end - - -][%OWNER%?[.O:[execute ][exec ]]sp_addextendedproperty [%R%?[N]]'MS_Description', - [%R%?[N]]%.q:COMMENT%, - [%R%?[N]]'user', [%R%?[N]]%.q:OWNER%, [%R%?[N]]'view', [%R%?[N]]%.q:VIEW% -:select @CurrentUser = user_name() -[.O:[execute ][exec ]]sp_addextendedproperty [%R%?[N]]'MS_Description', - [%R%?[N]]%.q:COMMENT%, - [%R%?[N]]'user', [%R%?[N]]@CurrentUser, [%R%?[N]]'view', [%R%?[N]]%.q:VIEW% -] - -0 - -1322756312 -mledier -Command for adding a view comment. - - -21FB42A9-7F59-4AA9-9560-7B822DCB04D0 -ViewCheck -with check option -0 - -0 - -Option for checking a view - - -4D7A50D4-AC5C-445C-A615-FD244576E066 -SqlListQuery -{OWNER, VIEW, COMMENT} - -select - u.name, - o.name, - convert(varchar(8000), p.value) -from - [%CATALOG%.]sys.sysobjects o - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.uid) - left outer join [%CATALOG%.]sys.extended_properties p on (p.major_id = o.id and p.minor_id = 0 and p.name = 'MS_Description') -where o.type = 'V' -[ and u.name = %.q:SCHEMA%] -order by 1, 2 -0 - -1322756386 -mledier -SQL query to list objects - - -443641CA-6E1E-4956-B892-97E7E8C134CD -SqlAttrQuery -{OWNER ID, VIEW ID, SCRIPT ...} - -select - u.name, - v.name, - case when (t.encrypted = 1) then '' else t.text end -from - [%CATALOG%.]sys.views v - join [%CATALOG%.]sys.schemas u on (u.schema_id = v.schema_id) - join [%CATALOG%.]sys.syscomments t on (t.id = v.object_id) -where 1 = 1 -[ and u.name=%.q:OWNER%] -[ and v.name=%.q:VIEW%] -order by t.id, t.number, t.colid - -0 - -0 - -SQL query to reverse object attributes - - -147480FF-B2B0-46CD-8BC7-316B22977CA1 -SqlPermQuery -{GRANTEE ID, PERMISSION ...} - -select - v.name, - case (s.action) when 193 then 'SELECT' when 195 then 'INSERT' when 196 then 'DELETE' when 197 then 'UPDATE' end + - case (s.protecttype) when 204 then '+' when 205 then '' when 206 then '-' end + ',' as coln -from - [%CATALOG%.]sys.sysprotects s - join [%CATALOG%.]sys.sysusers v on (v.uid = s.uid) - join [%CATALOG%.]sys.sysobjects o on (o.id = s.id) - join [%CATALOG%.]sys.sysusers u on (u.uid = o.uid) -where 1 = 1 -[ and o.name = %.q:VIEW%] -[ and u.name = %.q:OWNER%] -order by 1 -0 - -0 - -SQL query to reverse object permissions - - -702E51B1-3522-4ACB-947B-CD802E1D5626 -SqlColnListQuery -{OWNER ID, VIEW ID, VIEWCOLN ...} - -select - u.name, - o.name, - c.name + ',' -from - [%CATALOG%.]sys.sysobjects o - join [%CATALOG%.]sys.schemas u on (u.schema_id = o.uid) - join [%CATALOG%.]sys.syscolumns c on (c.id = o.id) -where - o.type = 'V' -[ and u.name = %.q:OWNER%] -[ and o.name = %.q:VIEW%] -order by 1, 2, c.colid - -0 - -0 - -SQL query to list view columns -1 - - -552EAB58-7666-413D-81C4-F91E83BC131E -ReversedQueries -SqlColnListQuery -0 - -0 - -Additional attributes queries to be called by ODBC - - -0B4007F8-C33B-4F57-9490-FD86DACAFF29 -EnableIndex -withoptions -0 - -0 - -List of types for which view index is available. - - -BA8BD8A6-2D4D-44CE-BDAF-494E771E99F6 -Options -with : composite = yes, separator=yes -{ - encryption - schemabinding - view_metadata -} -0 - -0 - -Available options for creating a snapshot (or materialized view) - - -E4DF8A09-3427-41B1-80FA-456F4B25C306 -ViewStyle -WithOptions -0 - -0 - -Command for defining a specific view - - -F3751416-3DD7-45E5-9100-BB24148CFE9A -SqlOptsQuery -{OWNER, VIEW, VIEWSTYLE, OPTIONS} - -select - u.name, - v.name, - 'WithOptions', - 'with ' - + case when (t.encrypted = 1) then 'encryption, ' else '' end - + case when (v.has_opaque_metadata = 1) then 'view_metadata, ' else '' end - + case when (d.referenced_minor_id = 0) then 'schemabinding' else '' end as coln -from - [%CATALOG%.]sys.views v - join [%CATALOG%.]sys.schemas u on (u.schema_id = v.schema_id) - left outer join [%CATALOG%.]sys.syscomments t on (t.id = v.object_id and t.encrypted = 1) - left outer join [%CATALOG%.]sys.sql_dependencies d on (d.object_id = v.object_id and d.referenced_minor_id = 0) -where (v.has_opaque_metadata = 1) or (d.referenced_minor_id = 0) or (t.encrypted = 1) -[ and u.name=%.q:OWNER%] -[ and v.name=%.q:VIEW%] -order by t.id, t.number, t.colid - -0 - -0 - -SQL query to reverse object physical options - - - - -7B5BBECE-678B-425F-87E0-49A7D91C2D34 -Trigger -TRGR -0 - -1269267132 -mledier -The following system variables are available: -(parent table items are also available for indexes) - trigger object specific items - "ORDER" // order number of the trigger (to sort the triggers when the DBMS supports more than one trigger of one type) - "TRIGGER" // generated code of trigger - "TRGTYPE" // trigger type (keywords "beforeinsert", "afterupdate", ...) - "TRGEVENT" // trigger event (keywords "insert", "update", "delete") - "TRGTIME" // trigger time (keywords NULL, "before", "after") - inside the body of the trigger, macros redefine variables on objects - "REFNO" // reference position in the list of references of the table - customized error messages management - "ERRNO" // error number for standard error - "ERRMSG" // error message for standard error - "MSGTAB" // name of the table containing user-defined error messages - "MSGNO" // in the user-defined error table, name of the column containing the error numbers - "MSGTXT" // in the user-defined error table, name of the column containing the error messages - "SCRIPT" // body of trigger or procedure - "TRGDEFN" // complete body of the trigger definition. Contains variable declarations and body of trigger or procedure - - - -CA8EEC1C-65EB-48D9-A849-922FACF2EFF4 -Enable -0 - -0 - -Trigger allowed -1 - - -4A30F916-27F2-4634-8836-5145DA1ABF5A -EnableMultiEvent -0 - -1269267132 -mledier -Multiple event allowed -1 - - -11455CAF-89F8-4555-A073-43D681ADAC6F -DefaultTriggerName -%TEMPLATE%_%.L:TABLE% -0 - -0 - -Default trigger name - - -E7784532-3BB0-46A0-B235-2D0B008ED832 -Maxlen -0 - -0 - -Maximum object code length -128 - - -D35C9129-7305-4719-916D-219B650D5A21 -Drop -if exists (select 1 - from sysobjects - where id = object_id('[%QUALIFIER%]%TRIGGER%') - and type = 'TR') - drop trigger [%QUALIFIER%]%TRIGGER% -0 - -0 - -Command for dropping a trigger. Example: drop trigger %TRIGGER% - - -248D4DC0-FF55-4E5D-B971-1AE6CC7883DC -EnableOwner -0 - -0 - -Owner allowed -1 - - -D28BC213-0468-4FFD-A1DA-9BCA83EEB5A3 -SqlListQuery -{TABLOWNER ID, TABLE ID, OWNER ID, TRIGGER ID, ImplementationType ID, Assembly ID, MethodName ID, TRGTIME ID, TRGEVENT ...} - -select - schema_name(tb.schema_id), - tb.name, - schema_name(tb.schema_id), - tr.name, - case(tr.type) when 'TA' then 'ClassMethod' else '' end, - a.name, - s.assembly_class + '.' + s.assembly_method, - case(tr.is_instead_of_trigger) when 1 then 'instead of' else 'after' end, - te.type_desc + ',' -from - [%CATALOG%.]sys.objects tb - join [%CATALOG%.]sys.triggers tr on (tr.parent_id = tb.object_id) - join [%CATALOG%.]sys.trigger_events te on (te.object_id = tr.object_id) - left outer join [%CATALOG%.]sys.assembly_modules s on (s.object_id = tr.object_id and tr.type = 'TA') - left outer join [%CATALOG%.]sys.assemblies a on (a.assembly_id = s.assembly_id and tr.type = 'TA') -where 1=1 -[ and schema_name(tb.schema_id) =%.q:SCHEMA%] -[ and tb.name = %.q:TABLE%] -order by - 1, 2, 3, 4 -0 - -0 - -SQL query to list objects - - -77CB8E98-25C4-4E43-A52F-628070645EAF -ImplementationType -0 - -0 - -Defines list of available types for a trigger template. -Name is the displayed value. -Value is the stored value in the object. -SQL= -CLR=ClassMethod -SQL -CLR - - - -2400E9BF-A267-4A55-8821-0B0C6DCE69F0 -SqlAttrQuery -{TABLOWNER, TABLE, OWNER, TRIGGER, SCRIPT} - -select - schema_name(tb.schema_id), - tb.name, - schema_name(tb.schema_id), - tr.name, - m.definition -from - [%CATALOG%.]sys.objects tb - join [%CATALOG%.]sys.triggers tr on (tr.parent_id = tb.object_id) - join [%CATALOG%.]sys.sql_modules m on (m.object_id = tr.object_id) -where - tr.type = 'TR' -[ and schema_name(tb.schema_id) =%.q:SCHEMA%] -[ and tb.name = %.q:TABLE%] -[ and tr.name = %.q:TRIGGER%] - -0 - -0 - -SQL query to reverse object attributes - - -D4666601-A62F-402C-B247-D4A349056FE3 -UseErrorMsgTable -begin - select @errno = %ERRNO%, - @errmsg = %MSGTXT% - from %MSGTAB% - where %MSGNO% = %ERRNO% - goto error -end -0 - -0 - -Errors handling using an error messages table - - -029901EB-FA99-4A24-B852-10B97A90A233 -UseErrorMsgText -begin - select @errno = %ERRNO%, - @errmsg = %.q:ERRMSG% - goto error -end -0 - -0 - -Errors handling using fixed error messages - - -7A4BF9BD-B80C-4AAB-9B90-C16F6E4D8D44 -Create -create trigger [%QUALIFIER%]%TRIGGER% on [%TABLQUALIFIER%]%TABLE%[ with %Option%] %TRGTIME% %TRGEVENT% -[%ImplementationType%=ClassMethod?as external name %Assembly.GeneratedName%.%MethodName%:%TRGDEFN%] -0 - -1269267132 -mledier -Command for creating a trigger. Example: create trigger on [%QUALIFIER%]%TABLE% %TRGDEFN% - - -25331050-9C48-4718-9D59-9220E6668380 -EventDelimiter -0 - -0 - -Events separation character. -, - - -6B4B0BCC-8DC0-4A70-AAAF-9922D982610C -ViewTime -instead of -0 - -0 - -Time list available from trigger on view. - - -F72D323F-5303-4718-B298-031767400206 -Time -for -after -instead of -0 - -0 - -Extended trigger times list. - - -B62A79E8-1D52-4EF6-B4A6-F4FF6740B89F -UniqName -0 - -0 - -1 - - - - -BD3624FE-C05A-4B4E-B631-556B1C41D337 -Procedure -PROC -0 - -0 - -The following system variables are available: - "PROC" // generated code of the procedure (also available for trigger when the trigger is implemented with a procedure) - "FUNC" // generated code of the procedure if the procedure is a function (with return value) - "PROCPRMS" // list of parameters of the procedure (also available for function) - - - -D48B4FCF-39DE-41C7-BF57-9E972D9FCC99 -Enable -0 - -0 - -Procedure allowed -1 - - -F898EE26-7D3A-465C-8844-07D550ACFCED -EnableFunc -0 - -0 - -Function allowed -1 - - -76BF109E-BD07-4FB9-B95E-F05EC88E795A -Maxlen -0 - -0 - -Maximum object code length -128 - - -5EA031B4-9026-4AB3-AA6C-8D4B07952077 -MaxFuncLen -0 - -0 - -Function name length -128 - - -BE8D32B8-3360-4E37-89B5-719FE3126111 -Drop -if exists (select 1 - from sysobjects - where id = object_id('[%QUALIFIER%]%PROC%') - and type in ('P','PC')) - drop procedure [%QUALIFIER%]%PROC% -0 - -0 - -Command for dropping a procedure. Example: drop procedure %PROC% - - -F8C583AC-3C7F-4825-88C7-7C987F9F50C5 -DropFunc -if exists (select 1 - from sysobjects - where id = object_id('[%QUALIFIER%]%FUNC%') - and type in ('IF', 'FN', 'TF')) - drop function [%QUALIFIER%]%FUNC% - -0 - -0 - -Command for dropping a function. Example: drop function %FUNC% - - -9FF2AFD9-925F-4151-927E-2A405E796FC3 -CustomProc -create procedure [%QUALIFIER%]%PROC% <@arg> <type> as -declare <@var> <type> -begin - -end - -0 - -0 - -Command for creating a stored procedure - - -1B42FA7D-AF76-4BBC-A423-CAB792E38F95 -CustomFunc -create function [%QUALIFIER%]%FUNC% (<arg> <type>) -RETURNS <type> -begin - -end - -0 - -0 - -Command for creating a function - - -1AC8FFE5-02F7-40B2-BA57-B828DAA0B821 -EnableOwner -0 - -0 - -Owner allowed -1 - - -371C1ABE-41CA-4219-9BF2-54F5074FDCDB -SqlListQuery -{OWNER, PROC, FUNC, ImplementationType, Assembly, MethodName} - -select - u.name, - case(p.type) - when 'P' then p.name - when 'PC' then p.name - else null - end, - case(p.type) - when 'P' then null - when 'PC' then null - else p.name - end, - case(p.type) - when 'PC' then 'ClassMethod' - when 'FS' then 'ClassMethod' - when 'FT' then 'ClassMethod' - else null - end as coln, - a.name, - s.assembly_class + '.' + s.assembly_method -from - [%CATALOG%.]sys.sysobjects p - join [%CATALOG%.]sys.schemas u on (u.schema_id = p.uid) - left outer join [%CATALOG%.]sys.assembly_modules s on (s.object_id = p.id) - left outer join [%CATALOG%.]sys.assemblies a on (a.assembly_id = s.assembly_id) -where - p.type in ('P','PC','IF', 'FN', 'TF', 'FS','FT') -[ and u.name=%.q:SCHEMA%] -order by - 1,2,3 - -0 - -0 - -SQL query to list objects - - -91213262-C995-41CF-AE78-105D7E1E8612 -ImplementationType -0 - -0 - -Defines list of available types for a procedure template. -Name is the displayed value. -Value is the stored value in the object. -SQL= -CLR=ClassMethod -Trigger=DDLTrigger -CLRTrigger=DDLTriggerClassMethod -SQL -CLR -Trigger -CLRTrigger - - - -2CE35497-B72D-4A89-84CE-B70E160EA1F0 -SqlAttrQuery -{OWNER ID, PROC ID, SCRIPT ...} - -select - u.name, - p.name, - t.text -from - [%CATALOG%.]sys.sysobjects p - join [%CATALOG%.]sys.schemas u on (u.schema_id = p.uid) - join [%CATALOG%.]sys.syscomments t on (t.id = p.id) -where - p.type in ('P', 'IF', 'FN', 'TF') -[ and u.name=%.q:OWNER%] -[ and p.name=%.q:PROC%] -order by - t.id, t.number, t.colid - -0 - -0 - -SQL query to reverse object attributes - - -0977B112-5982-46CA-B13F-F658D63EA4E9 -Permission -0 - -0 - -Available permission for a procedure. -The first column is the SQL name of permission (eg: EXECUTE) -The second column is a shortname to display in a grid -EXECUTE=Execute -EXECUTE - - - -5A5507A1-728C-4FD5-A537-E110A81128B9 -Create -create [.O:[procedure ][proc ]][%QUALIFIER%]%PROC%[ %PROCPRMS%] as -[%ImplementationType%=ClassMethod?external name %Assembly.GeneratedName%.%MethodName%:%TRGDEFN%] -0 - -0 - -Command for creating a procedure. Example: create procedure %PROC% %TRGDEFN% - - -EF7F7317-2C3E-4FE7-B60C-A8A9C244E9AF -CreateFunc -create function [%QUALIFIER%]%FUNC% [([%PROCPRMS%])] -[%ImplementationType%=ClassMethod?external name %Assembly.GeneratedName%.%MethodName%:%TRGDEFN%] -0 - -0 - -Command for creating a function. Example: create function %FUNC% %TRGDEFN% - - -A9E54A85-F6C2-48E9-8B62-CC97C77C1F39 -SqlPermQuery -{GRANTEE ID, PERMISSION ...} - -select - v.name, - case (s.action) when 224 then 'EXECUTE' end + - case (s.protecttype) when 204 then '+' when 205 then '' when 206 then '-' end + ',' as coln -from - [%CATALOG%.]sys.sysprotects s - join [%CATALOG%.]sys.sysusers v on (v.uid = s.uid) - join [%CATALOG%.]sys.sysobjects o on (o.id = s.id) - join [%CATALOG%.]sys.sysusers u on (u.uid = o.uid) -where 1 = 1 -[ and o.name = %.q:PROC%] -[ and u.name = %.q:OWNER%] -order by 1 -0 - -0 - -SQL query to reverse object permissions - - -307CC712-6607-4717-9DFA-88177D47F51E -EnableSynonym -0 - -0 - -Synonym supported -1 - - - - -396D0A74-CFCC-48F0-BC89-C41F53DF2D5C -Role -ROLE -0 - -0 - -Manages the role object. - - -F005CEF6-1070-4A01-B847-6A92E412AACD -Create -[%R%?[exec ]][execute ]sp_addrole %ROLE% -0 - -0 - -Command for creating a role. - - -AD8E1760-1922-46AE-96A7-7CC933BF9709 -Drop -[%R%?[exec ]][execute ]sp_droprole %ROLE% -0 - -0 - -Command for dropping a role. - - -FAC4D142-D5F7-451E-874F-BD93FC6B6137 -Enable -0 - -0 - -Role allowed -1 - - -56ED7FBA-32E4-4E2F-B74A-2F6EC26E810D -Bind -[%R%?[exec ]][execute ]sp_addrolemember %ROLE%, %USER% -0 - -0 - -Command for assigning a role to a user or another role - - -035F14B0-FCDF-47CE-AC5B-5C8ADADD9BB3 -Unbind -[%R%?[exec ]][execute ]sp_droprolemember %ROLE%, %USER% -0 - -0 - -Command for unassiging a role from a user or another role - - -C357B56D-1537-4121-A08B-41FD4B790023 -SqlListQuery -{ ROLE } - -select name from [%CATALOG%.]sys.sysusers where issqlrole = 1 -order by 1 - -0 - -0 - -SQL query to list objects - - -2C2E341A-A6D7-4CA5-AC7B-BC72D8D394E0 -SqlPermQuery -{ROLE ID, PRIVILEGE ...} - -select - u.name, - case (s.action) when 178 then 'CREATE FUNCTION' when 198 then 'CREATE TABLE' when 203 then 'CREATE DATABASE' - when 207 then 'CREATE VIEW' when 222 then 'CREATE PROCEDURE' when 228 then 'BACKUP DATABASE' - when 233 then 'CREATE DEFAULT' when 235 then 'BACKUP LOG' when 236 then 'CREATE RULE' end + - case (s.protecttype) when 204 then '+' when 205 then '' when 206 then '-' end + ',' as coln -from - [%CATALOG%.]sys.sysprotects s - join [%CATALOG%.]sys.sysusers u on (s.uid = u.uid and u.issqlrole = 1) -where - s.id = 0 -order by 1 - -0 - -0 - -Query to reverse privileges - - -AF593D81-E8CD-49AD-BE9E-94E28988984C -SqlListChildrenQuery -{ROLE, MEMBER} - -select - r.name, - m.name -from - [%CATALOG%.]sys.sysmembers x - join [%CATALOG%.]sys.sysusers r on (r.uid = x.groupuid) - join [%CATALOG%.]sys.sysusers m on (m.uid = x.memberuid) - -0 - -0 - -SQL query to list assigned ids to the role. - - - - -5831CCFA-189E-4899-AD22-3EFE130386EE -Synonym -SYNM -0 - -0 - -Manages the synonym object. - - -5A12242D-073C-48B8-B110-0332844BC97A -Create -create synonym [%QUALIFIER%]%SYNONYM% for [%BASEQUALIFIER%]%BASEOBJECT% -0 - -0 - -Command for creating a synonym. - - -7FB75DE8-076E-4B76-8315-FDD789D54D59 -Drop -drop synonym [%QUALIFIER%]%SYNONYM% -0 - -0 - -Command for dropping a synonym. - - -62E68AC9-F395-48A6-B9EF-0E998A849E99 -Enable -0 - -0 - -Synonym allowed -1 - - -AC1D3499-C522-4770-9D4F-FE73B95B8162 -SqlListQuery -{OWNER, SYNONYM, BASEOWNER, BASEOBJECT} - -select - u.name, - s.name, - isnull(parsename(s.base_object_name, 2), u.name), - parsename(s.base_object_name, 1) -from - [%CATALOG%.]sys.synonyms s - join [%CATALOG%.]sys.schemas u on (u.schema_id = s.schema_id) -[where u.name = %.q:SCHEMA%] -order by 1, 2 -0 - -0 - -SQL query to list objects - - -F61BC147-9679-4370-9B7C-90A80EC6AE59 -EnableSynonym -0 - -0 - -Synonym supported -1 - - - - -2108DF55-9174-45AF-8E20-44F15F5C7485 -Extended Object <<PartitionFunction>> -FRMEOBJ <<PartitionFunction>> -0 - -0 - - - -CF638EC9-1C2D-4802-8395-34FD220041F8 -Drop -drop partition function %PARTITIONFUNCTION% -0 - -0 - -Command for dropping a partition function Example: drop partition function %PARTITIONFUNCTION% - - -E8994466-0E40-404A-87CE-4461D1676541 -Create -create partition function %PARTITIONFUNCTION% (%DisplayType%) -as range %IntervalSide% -for values (%BoundaryValues%) - -0 - -0 - -Command for creating a partition function. Example: create partition function %PARTITIONFUNCTION% - - -4F509169-3B10-4DF9-8B46-8DBF11583610 -SqlAttrQuery -{PARTITIONFUNCTION ID, BoundaryValues ...} - -select - pf.name, - case when pp.system_type_id in (35,61,99,175,189,231,239,241) then ''''+ RTRIM(convert(varchar, prv.value)) + '''' else RTRIM(convert(varchar, prv.value)) end +',' as coln - from - [%CATALOG%.]sys.partition_functions pf - join [%CATALOG%.]sys.partition_parameters pp on (pp.function_id = pf.function_id) - join [%CATALOG%.]sys.partition_range_values prv on (prv.function_id = pf.function_id and prv.parameter_id = pp.parameter_id) -[where pf.name = %.q:PARTITIONFUNCTION%] -order by pf.name, pp.parameter_id, prv.boundary_id - -0 - -0 - -SQL query to reverse object attributes - - -2637E228-4A1D-4842-93F8-FEF1F5299319 -SqlListQuery -{PARTITIONFUNCTION, IntervalSide, InputParameterType} - -select - pf.name, - case(pf.boundary_value_on_right) when 1 then 'RIGHT' else 'LEFT' end, - t.Name + - case when (pp.max_length <> t.max_length) then '(' + LTRIM(STR(case when pp.system_type_id in (99, 231, 239) then (pp.max_length/2) else pp.max_length end)) + ')' - when (pp.scale <> t.scale) then (LTRIM(STR(pp.precision)) + ',' + LTRIM(STR(pp.scale))) - when (pp.precision <> t.precision) then '(' + STR(pp.precision) + ')' else '' end as "InputParameterType" - from - [%CATALOG%.]sys.partition_functions pf, - [%CATALOG%.]sys.partition_parameters pp, - [%CATALOG%.]sys.types t -where - pp.function_id = pf.function_id and - pp.system_type_id = t.user_type_id -order by 1 -0 - -0 - -SQL query to list objects - - - - -45F6FC7F-DC63-4E34-87FE-86B940A142A8 -Extended Object <<PartitionScheme>> -FRMEOBJ <<PartitionScheme>> -0 - -0 - - - -DC0A45D3-6E06-40BD-A2CE-C6092F0A41AA -Drop -drop partition scheme %PARTITIONSCHEME% -0 - -0 - -Command for dropping a partition scheme. Example: drop partition scheme %PARTITIONSCHEME% - - -36272265-A783-4B05-ABA2-E131197A8F1E -Create -create partition scheme %PARTITIONSCHEME% -as partition %PartitionFunction.GeneratedName% -[%AllPartitions% ? ALL ] to (%FileGroups%) - -0 - -0 - -Command for creating a partition scheme. Example: create partition scheme %PARTITIONSCHEME% - - -A5949F4B-4084-40CD-86B1-FDAA7CAC6778 -SqlAttrQuery -{PARTITIONSCHEME ID, PartitionFunction, FileGroups ...} - -select - ps.name, - pf.name, - '\[' + fg.name + '\],' -from - [%CATALOG%.]sys.partition_schemes ps - join [%CATALOG%.]sys.partition_functions pf on (pf.function_id = ps.function_id) - join [%CATALOG%.]sys.destination_data_spaces dds on (dds.partition_scheme_id = ps.data_space_id) - join [%CATALOG%.]sys.filegroups fg on (fg.data_space_id = dds.data_space_id) -[where ps.name = %.q:PARTITIONSCHEME%] -order by ps.name, dds.destination_id - -0 - -0 - -SQL query to reverse object attributes - - -8399C663-A4C4-4603-8B43-B1D34481579E -SqlListQuery -{PARTITIONSCHEME} -select - ps.name -from - [%CATALOG%.]sys.partition_schemes ps -order by 1 -0 - -0 - -SQL query to list objects - - - - -8AA040B3-12DC-4B15-A67B-30BEF58CBFB2 -Extended Object <<Assembly>> -FRMEOBJ <<Assembly>> -0 - -0 - - - -54B78534-73BC-4DE9-AB29-2ACEE3C4DE92 -SqlListQuery -{ ASSEMBLY} -select - asm.name as ASSEMBLY -from - [%CATALOG%.]sys.assemblies asm -order by 1 - -0 - -0 - -SQL query to list objects - - -5285E17F-5F2D-4389-AB69-A43D0F548983 -AlterStatementList -0 - -0 - -List of text items representing statements modifying the corresponding attributes: -Example: -Rename = Code -AlterFileName=FileName -AlterOptions=PermissionSet, Visibility, UncheckedData -AlterFileName -AlterOptions - - - -44F414DC-1A55-4B3F-ACDF-A499293EAD29 -Drop -drop assembly %ASSEMBLY%[ -with no dependents] - -0 - -0 - -Command for dropping an assembly. Example: drop assembly %ASSEMBLY% - - -2B853D02-6242-45ED-BBC4-8FBBA61AE7F3 -Create -create assembly %ASSEMBLY%[%Authorization%? -authorization %Authorization.GeneratedName%] -from %.q:FileName%[%PermissionSet%? -with permission_set = %PermissionSet%] - -0 - -0 - -Command for creating an assembly. Example: create assembly %ASSEMBLY% - - -B994262A-739C-410A-86BC-7C509268D9A2 -AfterCreate -.if ((%Visibility% != "") and (%UncheckedData% == true) ) -alter assembly %ASSEMBLY% - with visibility = %Visibility%, unchecked data -.elsif ((%Visibility% != "") or (%UncheckedData% == true) ) -alter assembly %ASSEMBLY% - with[ visibility = %Visibility%][%UncheckedData%? unchecked data] -.endif("\ngo") - - -0 - -0 - -Commands executed after create statement - - -43D7BD06-27E3-4794-BCF7-5646E7426A7D -AlterAssembly -alter assembly %ASSEMBLY%[ - from %.q:FileName%][ - with[%PermissionSet%? permission_set = %PermissionSet%][%Visibility%?[,] visibility = %Visibility%][%UncheckedData% ?[,] unchecked data]] - -0 - -0 - -Updates an assembly by modifying the SQL Server catalog properties of an assembly -1 - - -DB0237D0-3379-4F0F-ABBA-3EDE21B9BD63 -ReversedStatements -AlterAssembly -0 - -0 - -Additional statements to be reversed by script - - -A8816FD7-687C-4007-A9AF-1C36E3CD64E4 -AlterFileName -.if (%FileName%) -alter assembly %ASSEMBLY% - from %.q:FileName% -.endif - -0 - -0 - -1 - - -7AA0D73C-5BCF-48C4-A026-DF2A9F6FE248 -AlterOptions -.replace(",,","") -alter assembly %ASSEMBLY%[ - with,[, permission_set = %PermissionSet%][, visibility = %Visibility%][%UncheckedData% ?, unchecked data]] -.endreplace -0 - -0 - -1 - - -EC1A87D9-D333-437C-BF72-335929655073 -SqlAttrQuery -{ ASSEMBLY, PermissionSet, Visibility, FileName} -select - asm.name as ASSEMBLY, - case (asm.permission_set) when 1 then 'SAFE' when 2 then 'EXTERNAL_ACCESS' when 3 then 'UNSAFE' else '' end as PermissionSet, - case (asm.is_visible) when 1 then 'on' when 0 then 'off' else '' end as Visibility, - asf.name as FileName -from - [%CATALOG%.]sys.assemblies asm, - [%CATALOG%.]sys.assembly_files asf -where - asm.assembly_id = asf.assembly_id and - asf.file_id = 1 -[and asm.name = %.q:ASSEMBLY%] -order by 1 - -0 - -0 - -SQL query to reverse object attributes - - - - -63A7CFA0-A18F-41D1-A125-44D3570D5B64 -Extended Object <<Aggregate>> -FRMEOBJ <<Aggregate>> -0 - -0 - - - -316E171D-97B9-49F1-93A4-80423203B80F -SqlListQuery -{AGGREGATE, OWNER} -select - so.name as "AGGREGATE", - schema_name(so.schema_id) as "OWNER" -from - [%CATALOG%.]sys.objects so -where - so.type = 'AF' -[and schema_name(so.schema_id) = %.q:SCHEMA%] -order by so.name -0 - -0 - -SQL query to list objects - - -9FC71034-41AF-4982-A765-6B76CB960670 -Drop -drop aggregate [%QUALIFIER%]%AGGREGATE% -0 - -0 - -Command for dropping an aggregate. Example: drop aggregate %AGGREGATE% - - -5DA70A0E-AB7D-4EF4-9CEC-BA1D9A41E91D -Create -create aggregate [%QUALIFIER%]%AGGREGATE% - (%Parameters% ) -returns [%R%?%ReturnType%(%ReturnTypeLength%[,%ReturnTypePrec%]):%DisplayType%] -external name %Assembly.GeneratedName%[.%Class%] - -0 - -0 - -Command for creating an aggregate. Example: create aggregate %AGGREGATE% - - -CF800E49-D81C-4893-A90A-0DFE2689892E -EnableSynonym -0 - -0 - -Synonym supported -1 - - -5735B322-26A3-4A4B-979A-521D1F05798F -SqlAttrQuery -{AGGREGATE, OWNER, Assembly, ReturnType } -select - so.name as "AGGREGATE", - schema_name(so.schema_id) as "OWNER", - object_name(so.parent_object_id) as "Assembly", - rt.name + - case when (rp.max_length <> rt.max_length) then '(' + LTRIM(STR(case when rp.system_type_id in (99, 231, 239) then (rp.max_length/2) else rp.max_length end)) + ')' - when (rp.scale <> rt.scale) then (LTRIM(STR(rp.precision)) + ',' + LTRIM(STR(rp.scale))) - when (rp.precision <> rt.precision) then '(' + STR(rp.precision) + ')' else '' end as "ReturnType" -from - [%CATALOG%.]sys.objects so, - [%CATALOG%.]sys.parameters rp, - [%CATALOG%.]sys.types it, - [%CATALOG%.]sys.types rt -where - so.type = 'AF' and - rp.object_id = so.object_id and - rp.parameter_id = 0 and - rp.system_type_id = rt.system_type_id -[and schema_name(so.schema_id) = %.q:SCHEMA%] -[and so.name = %.q:AGGREGATE%] -order by so.name -0 - -0 - -SQL query to reverse object attributes - - - - -7307C3F5-75AC-42DA-BE9A-C6C204F19B49 -Extended Object <<Certificate>> -FRMEOBJ <<Certificate>> -0 - -0 - - - -22A68396-9653-4124-B8AF-ED727BCDAF68 -AlterStatementList -0 - -0 - -List of text items representing statements modifying the corresponding attributes: -Example: -Rename = Code -AlterPrivateKeyFile=PrivateKeyFile, PrivateKeyDecryptionPassword, PrivateKeyEncryptionPassword -AlterPrivateKeyDecryptionPassword=PrivateKeyDecryptionPassword -AlterPrivateKeyEncryptionPassword=PrivateKeyEncryptionPassword -AlterActiveForBeginDialog=ActiveForBeginDialog -AlterPrivateKeyFile -AlterPrivateKeyDecryptionPassword -AlterPrivateKeyEncryptionPassword -AlterActiveForBeginDialog - - - -81B4EC52-C0DF-46AA-A234-86F4E09E5359 -SqlListQuery -{CERTIFICATE, Authorization, Subject, StartDate, ExpiryDate, ActiveForBeginDialog} -select - c.name as "CERTIFICATE", - dp.name as "Authorization", - c.subject as "Subject", - cast(c.start_date as varchar(30)) as "StartDate", - cast(c.expiry_date as varchar(30)) as "ExpiryDate", - case (c.is_active_for_begin_dialog) when 1 then 'true' else 'false' end as "ActiveForBeginDialog" -from - [%CATALOG%.]sys.certificates c, - [%CATALOG%.]sys.database_principals dp -where - c.principal_id = dp.principal_id -order by c.name - -0 - -0 - -SQL query to list objects - - -B9E267E0-6064-4E2A-9348-5157703EDE46 -Drop -drop certificate %CERTIFICATE% -0 - -0 - -Command for dropping a certificate. Example: drop certificate %CERTIFICATE% - - -B5EED7D8-FFBB-4D95-8C8D-966881B94F8C -Create -create certificate %CERTIFICATE%[%Authorization%? authorization %Authorization.GeneratedName%] [%Assembly%? from assembly %Assembly.GeneratedName%:[%AssemblyFile%? - from[%Executable%? executable] file = %.q:AssemblyFile%[%PrivateKeyFile% ? - with private key (file = %.q:PrivateKeyFile%[%PrivateKeyDecryptionPassword% ?, - decryption by password = %.q:PrivateKeyDecryptionPassword%][%PrivateKeyEncryptionPassword% ?, - encryption by password = %.q:PrivateKeyEncryptionPassword%])] : [%Subject%?[%EncryptionPassword%? - encryption by password = %.q:EncryptionPassword%] - with subject = %.q:Subject%[%StartDate%?, - start_date = %.q:StartDate%][%ExpiryDate%?, - expiry_date = %.q:ExpiryDate%]]]][%ActiveForBeginDialog%?: - active for begin_dialog = [%ActiveForBeginDialog%?'on':'off']] - -0 - -0 - -Command for creating a certificate. Example: create certificate %CERTIFICATE% - - -52025F1A-EC91-44A7-BA2F-DC08F22BAAC4 -AlterPrivateKey -alter certificate %CERTIFICATE%[%PrivateKeyFile%? - with private key (file = %.q:PrivateKeyFile%[%PrivateKeyDecryptionPassword%?, - decryption by password =%.q:PrivateKeyDecryptionPassword%][%PrivateKeyEncryptionPassword%?, - encryption by password =%.q:PrivateKeyEncryptionPassword%]) : - remove private key] -0 - -0 - -Modify private key of a certificate -1 - - -BC7DEC5C-72B4-4445-BCE4-C620DD19E41C -AlterActiveForBeginDialog -alter certificate %CERTIFICATE% - with active for begin_dialog = [%ActiveForBeginDialog%?'on':'off'] - -0 - -0 - -Modify active for begin_dialog option of a certificate -1 - - -7AD0FD5A-3C19-47DE-99CA-BCEA02A3DD95 -AlterPrivateKeyDecryptionPassword -alter certificate %CERTIFICATE% - with private key (decryption by password =%.q:PrivateKeyDecryptionPassword%][%PrivateKeyEncryptionPassword%?, - encryption by password =%.q:PrivateKeyEncryptionPassword%]) - - -0 - -0 - -Modify private key decryption password -1 - - -89EA1981-90BD-461F-A011-7CFE2D998928 -AlterPrivateKeyEncryptionPassword -alter certificate %CERTIFICATE% - with private key (encryption by password =%.q:PrivateKeyEncryptionPassword%) - - -0 - -0 - -Modify private key encryption password -1 - - - - -BAD6DFE3-733C-43BE-BB1C-2E1577F616B9 -Extended Object <<AsymmetricKey>> -FRMEOBJ <<AsymmetricKey>> -0 - -0 - - - -3DCEBABC-FAB3-4464-AC43-536943E38844 -SqlListQuery -{ASYMMETRICKEY, Authorization, Algorithm} -select - ask.name as "ASYMMETRICKEY", - dp.name as "Authorization", - ask.algorithm_desc as "Algorithm" -from - [%CATALOG%.]sys.asymmetric_keys ask, - [%CATALOG%.]sys.database_principals dp -where - ask.principal_id = dp.principal_id -order by ask.name - -0 - -0 - -SQL query to list objects - - -26B06DD2-BA91-424F-9DB7-347529A9275B -Drop -drop asymmetric key %ASYMMETRICKEY% -0 - -0 - -Command for dropping an asymmetric key. Example: drop asymmetric key %ASYMMETRICKEY% - - -39BE09DD-5485-4E87-9A2E-40603025DAFA -Create -create asymmetric key %ASYMMETRICKEY%[%Authorization%? - authorization %Authorization.GeneratedName% ] - [%Source%=Provider?from provider %Provider% - with [creation_disposition = %CreateDisposition%, ]provider_key_name = %.q:ProviderKeyName%:[%Source%=Assembly?from assembly %Assembly.GeneratedName%:from [%.Q:Source%="Executable file"?executable ]file = %.q:AssemblyFile%][%Algorithm%? - with algorithm = %Algorithm%]][%EncryptionPassword%? - encryption by password = %.q:EncryptionPassword%] - -0 - -0 - -Command for creating an asymmetric key. Example: create asymmetric key %ASYMMETRICKEY % - - - - -7DC7B53C-696D-44DD-B087-E372384DCF32 -Extended Object <<SymmetricKey>> -FRMEOBJ <<SymmetricKey>> -0 - -0 - - - -28256A5C-4A7D-46AE-8692-8C61847A56A5 -SqlListQuery -{SYMMETRICKEY, Authorization, Algorithm, } -select - sk.name as "SYMMETRICKEY", - dp.name as "Authorization", - sk.algorithm_desc as "Algorithm" -from - [%CATALOG%.]sys.symmetric_keys sk, - [%CATALOG%.]sys.database_principals dp -where - sk.principal_id = dp.principal_id -order by 1 -0 - -0 - -SQL query to list objects - - -17C101EF-E2D1-4A30-8E4F-70E289429109 -Drop -drop symmetric key %SYMMETRICKEY% -0 - -0 - -Command for dropping a symmetric key. Example: drop symmetric key %SYMMETRICKEY% - - -9250616C-DE73-4C92-8C95-D00A64556D17 -Create -[%R%? -create symmetric key %SYMMETRICKEY%[%Authorization%? - authorization %Authorization%] - with [key_source = '%KeySource%'[, algorithm = %Algorithm%][, identity_value = '%IdentityValue%']] - [key_source = '%KeySource%'[, identity_value = '%IdentityValue%'][, algorithm = %Algorithm%]] - [algorithm = %Algorithm%[, key_source = '%KeySource%'][, identity_value = '%IdentityValue%']] - [algorithm = %Algorithm%[, identity_value = '%IdentityValue%'][, key_source = '%KeySource%']] - [identity_value = '%IdentityValue%'[, key_source = '%KeySource%'][, algorithm = %Algorithm%]] - [identity_value = '%IdentityValue%'[, algorithm = %Algorithm%][, key_source = '%KeySource%']] - encryption by [certificate %BaseCertificate%[, password = '%Password%'][, symmetric key %BaseSymmetricKey%][, asymmetric key %BaseAsymmetricKey%]] - [certificate %BaseCertificate%[, password = '%Password%'][, asymmetric key %BaseAsymmetricKey%][, symmetric key %BaseSymmetricKey%]] - [certificate %BaseCertificate%[, symmetric key %BaseSymmetricKey%][, password = '%Password%'][, asymmetric key %BaseAsymmetricKey%]] - [certificate %BaseCertificate%[, symmetric key %BaseSymmetricKey%][, asymmetric key %BaseAsymmetricKey%][, password = '%Password%']] - [certificate %BaseCertificate%[, asymmetric key %BaseAsymmetricKey%][, password = '%Password%'][, symmetric key %BaseSymmetricKey%]] - [certificate %BaseCertificate%[, asymmetric key %BaseAsymmetricKey%][, symmetric key %BaseSymmetricKey%][, password = '%Password%']] - [password = '%Password%'[, certificate %BaseCertificate%][, symmetric key %BaseSymmetricKey%][, asymmetric key %BaseAsymmetricKey%]] - [password = '%Password%'[, certificate %BaseCertificate%][, asymmetric key %BaseAsymmetricKey%][, symmetric key %BaseSymmetricKey%]] - [password = '%Password%'[, symmetric key %BaseSymmetricKey%][, certificate %BaseCertificate%][, asymmetric key %BaseAsymmetricKey%]] - [password = '%Password%'[, symmetric key %BaseSymmetricKey%][, asymmetric key %BaseAsymmetricKey%][, certificate %BaseCertificate%]] - [password = '%Password%'[, asymmetric key %BaseAsymmetricKey%][, certificate %BaseCertificate%][, symmetric key %BaseSymmetricKey%]] - [password = '%Password%'[, asymmetric key %BaseAsymmetricKey%][, symmetric key %BaseSymmetricKey%][, certificate %BaseCertificate%]] - [symmetric key %BaseSymmetricKey%[, password = '%Password%'][, certificate %BaseCertificate%][, asymmetric key %BaseAsymmetricKey%]] - [symmetric key %BaseSymmetricKey%[, password = '%Password%'][, asymmetric key %BaseAsymmetricKey%][, certificate %BaseCertificate%]] - [symmetric key %BaseSymmetricKey%[, certificate %BaseCertificate%][, password = '%Password%'][, asymmetric key %BaseAsymmetricKey%]] - [symmetric key %BaseSymmetricKey%[, certificate %BaseCertificate%][, asymmetric key %BaseAsymmetricKey%][, password = '%Password%']] - [symmetric key %BaseSymmetricKey%[, asymmetric key %BaseAsymmetricKey%][, password = '%Password%'][, certificate %BaseCertificate%]] - [symmetric key %BaseSymmetricKey%[, asymmetric key %BaseAsymmetricKey%][, certificate %BaseCertificate%][, password = '%Password%']] - [asymmetric key %BaseAsymmetricKey%[, password = '%Password%'][, symmetric key %BaseSymmetricKey%][, certificate %BaseCertificate%]] - [asymmetric key %BaseAsymmetricKey%[, password = '%Password%'][, certificate %BaseCertificate%][, symmetric key %BaseSymmetricKey%]] - [asymmetric key %BaseAsymmetricKey%[, symmetric key %BaseSymmetricKey%][, password = '%Password%'][, certificate %BaseCertificate%]] - [asymmetric key %BaseAsymmetricKey%[, symmetric key %BaseSymmetricKey%][, certificate %BaseCertificate%][, password = '%Password%']] - [asymmetric key %BaseAsymmetricKey%[, certificate %BaseCertificate%][, password = '%Password%'][, symmetric key %BaseSymmetricKey%]] - [asymmetric key %BaseAsymmetricKey%[, certificate %BaseCertificate%][, symmetric key %BaseSymmetricKey%][, password = '%Password%']] -:create symmetric key %SYMMETRICKEY%[%Authorization%? - authorization %Authorization.GeneratedName%] - with[%KeySource%? key_source = %.q:KeySource%[%Algorithm%?, - algorithm = %Algorithm%][%IdentityValue%?, - identity_value = %.q:IdentityValue%]:[%Algorithm%? algorithm = %Algorithm%[%IdentityValue%?, - identity_value = %.q:IdentityValue%]:[%IdentityValue%? identity_value = %.q:IdentityValue%]]] - encryption by[%BaseCertificate%? certificate %BaseCertificate.GeneratedName%[%Password%?, - password = %.q:Password%][%BaseSymmetricKey%?, - symmetric key %BaseSymmetricKey.GeneratedName%][%BaseAsymmetricKey%?, - asymmetric key %BaseAsymmetricKey.GeneratedName%]:[%Password%? password = %.q:Password%[%BaseSymmetricKey%?, - symmetric key %BaseSymmetricKey.GeneratedName%][%BaseAsymmetricKey%?, - asymmetric key %BaseAsymmetricKey.GeneratedName%]:[%BaseSymmetricKey%? symmetric key %BaseSymmetricKey.GeneratedName%[%BaseAsymmetricKey%?, - asymmetric key %BaseAsymmetricKey.GeneratedName%]:[%BaseAsymmetricKey%? asymmetric key %BaseAsymmetricKey.GeneratedName%]]]]] - -0 - -0 - -Command for creating a symmetric key. Example: create symmetric key %SYMMETRICKEY% - - - - -3A2DACA7-54E4-4A89-BE81-6596375EA61F -CreateSymmetricKey -create symmetric key %SYMMETRICKEY%[%Authorization%? - authorization %Authorization%] - with [key_source=%.q:KeySource%[, algorithm = %Algorithm%][, identity_value = %.q:IdentityValue%]] - [key_source=%.q:KeySource%[, identity_value = %.q:IdentityValue%][, algorithm = %Algorithm%]] - [algorithm = %Algorithm%[, key_source=%.q:KeySource%][, identity_value = %.q:IdentityValue%]] - [algorithm = %Algorithm%[, identity_value = %.q:IdentityValue%][, key_source=%.q:KeySource%]] - [identity_value = %.q:IdentityValue%[, key_source=%.q:KeySource%][, algorithm = %Algorithm%]] - [identity_value = %.q:IdentityValue%[, algorithm = %Algorithm%][, key_source=%.q:KeySource%]] - encryption by [certificate %BaseCertificate.GeneratedName%[, password = %.q:Password%][, symmetric key = %BaseSymmetricKey.GeneratedName%][, asymmetric key = %BaseAsymmetricKey.GeneratedName%]] - [certificate %BaseCertificate.GeneratedName%[, password = %.q:Password%][, asymmetric key = %BaseAsymmetricKey.GeneratedName%][, symmetric key = %BaseSymmetricKey.GeneratedName%]] - [certificate %BaseCertificate.GeneratedName%[, symmetric key = %BaseSymmetricKey.GeneratedName%][, password = %.q:Password%][, asymmetric key = %BaseAsymmetricKey.GeneratedName%]] - [certificate %BaseCertificate.GeneratedName%[, symmetric key = %BaseSymmetricKey.GeneratedName%][, asymmetric key = %BaseAsymmetricKey.GeneratedName%][, password = %.q:Password%]] - [certificate %BaseCertificate.GeneratedName%[, asymmetric key = %BaseAsymmetricKey.GeneratedName%][, password = %.q:Password%][, symmetric key = %BaseSymmetricKey.GeneratedName%]] - [certificate %BaseCertificate.GeneratedName%[, asymmetric key = %BaseAsymmetricKey.GeneratedName%][, symmetric key = %BaseSymmetricKey.GeneratedName%][, password = %.q:Password%]] - [password = %.q:Password%[, certificate %BaseCertificate.GeneratedName%][, symmetric key = %BaseSymmetricKey.GeneratedName%][, asymmetric key = %BaseAsymmetricKey.GeneratedName%]] - [password = %.q:Password%[, certificate %BaseCertificate.GeneratedName%][, asymmetric key = %BaseAsymmetricKey.GeneratedName%][, symmetric key = %BaseSymmetricKey.GeneratedName%]] - [password = %.q:Password%[, symmetric key = %BaseSymmetricKey.GeneratedName%][, certificate %BaseCertificate.GeneratedName%][, asymmetric key = %BaseAsymmetricKey.GeneratedName%]] - [password = %.q:Password%[, symmetric key = %BaseSymmetricKey.GeneratedName%][, asymmetric key = %BaseAsymmetricKey.GeneratedName%][, certificate %BaseCertificate.GeneratedName%]] - [password = %.q:Password%[, asymmetric key = %BaseAsymmetricKey.GeneratedName%][, certificate %BaseCertificate.GeneratedName%][, symmetric key = %BaseSymmetricKey.GeneratedName%]] - [password = %.q:Password%[, asymmetric key = %BaseAsymmetricKey.GeneratedName%][, symmetric key = %BaseSymmetricKey.GeneratedName%][, certificate %BaseCertificate.GeneratedName%]] - [symmetric key = %BaseSymmetricKey.GeneratedName%[, password = %.q:Password%][, certificate %BaseCertificate.GeneratedName%][, asymmetric key = %BaseAsymmetricKey.GeneratedName%]] - [symmetric key = %BaseSymmetricKey.GeneratedName%[, password = %.q:Password%][, asymmetric key = %BaseAsymmetricKey.GeneratedName%][, certificate %BaseCertificate.GeneratedName%]] - [symmetric key = %BaseSymmetricKey.GeneratedName%[, certificate %BaseCertificate.GeneratedName%][, password = %.q:Password%][, asymmetric key = %BaseAsymmetricKey.GeneratedName%]] - [symmetric key = %BaseSymmetricKey.GeneratedName%[, certificate %BaseCertificate.GeneratedName%][, asymmetric key = %BaseAsymmetricKey.GeneratedName%][, password = %.q:Password%]] - [symmetric key = %BaseSymmetricKey.GeneratedName%[, asymmetric key = %BaseAsymmetricKey.GeneratedName%][, password = %.q:Password%][, certificate %BaseCertificate.GeneratedName%]] - [symmetric key = %BaseSymmetricKey.GeneratedName%[, asymmetric key = %BaseAsymmetricKey.GeneratedName%][, certificate %BaseCertificate.GeneratedName%][, password = %.q:Password%]] - [asymmetric key = %BaseAsymmetricKey.GeneratedName%[, password = %.q:Password%][, symmetric key = %BaseSymmetricKey.GeneratedName%][, certificate %BaseCertificate.GeneratedName%]] - [asymmetric key = %BaseAsymmetricKey.GeneratedName%[, password = %.q:Password%][, certificate %BaseCertificate.GeneratedName%][, symmetric key = %BaseSymmetricKey.GeneratedName%]] - [asymmetric key = %BaseAsymmetricKey.GeneratedName%[, symmetric key = %BaseSymmetricKey.GeneratedName%][, password = %.q:Password%][, certificate %BaseCertificate.GeneratedName%]] - [asymmetric key = %BaseAsymmetricKey.GeneratedName%[, symmetric key = %BaseSymmetricKey.GeneratedName%][, certificate %BaseCertificate.GeneratedName%][, password = %.q:Password%]] - [asymmetric key = %BaseAsymmetricKey.GeneratedName%[, certificate %BaseCertificate.GeneratedName%][, password = %.q:Password%][, symmetric key = %BaseSymmetricKey.GeneratedName%]] - [asymmetric key = %BaseAsymmetricKey.GeneratedName%[, certificate %BaseCertificate.GeneratedName%][, symmetric key = %BaseSymmetricKey.GeneratedName%][, password = %.q:Password%]] - -0 - -0 - -1 - - -BEBDD1AA-1FE2-4093-9325-6E128B362489 -SqlAttrQuery -{SYMMETRICKEY ID, BaseSymmetricKey, BaseCertificate, BaseAsymmetricKey} -select - sk.name, - case (ke.crypt_type) when 'ESKS' then (select sk2.name from [%CATALOG%.]sys.symmetric_keys sk2 where sk2.key_guid = ke.thumbprint) else null end as "BaseSymmetricKey", - case (ke.crypt_type) when 'EPUC' then (select c.name from [%CATALOG%.]sys.certificates c where c.thumbprint = ke.thumbprint) else null end as "BaseCertificate", - case (ke.crypt_type) when 'EPUA' then (select ak.name from [%CATALOG%.]sys.asymmetric_keys ak where ak.thumbprint = ke.thumbprint) else null end as "BaseAsymmetricKey" -from - [%CATALOG%.]sys.symmetric_keys sk, - [%CATALOG%.]sys.key_encryptions ke -where - sk.symmetric_key_id = ke.key_id -0 - -0 - -SQL query to reverse object attributes - - - - -784A6F70-9017-4169-8D75-9E97D13554DC -Extended Object <<FullTextCatalog>> -FRMEOBJ <<FullTextCatalog>> -0 - -0 - - - -3A5C1CBF-7A7C-4CF1-B4B1-B93F523271D9 -SqlListQuery -{FULLTEXTCATALOG} -select - ftc.name as "FULLTEXTCATALOG" -from - [%CATALOG%.]sys.fulltext_catalogs ftc -order by ftc.name - - -0 - -0 - -SQL query to list objects - - -34B20B6C-BF3C-4FD3-980D-6D3C4274364C -Drop -drop fulltext catalog %FULLTEXTCATALOG% -0 - -0 - -Command for dropping a full text catalog. Example: drop fulltext catalog %FULLTEXTCATALOG% - - -36247D18-3694-4DE8-8E49-EDD7694DEE92 -Create -create fulltext catalog %FULLTEXTCATALOG%[%FileGroup%? - on filegroup %.q:FileGroup%][%Path%? - in path %.q:Path%][%AccentSensitivity%? - with accent_sensitivity = %AccentSensitivity%][%Default%? - as default][%Authorization%? - authorization %Authorization.GeneratedName%] -0 - -0 - -Command for creating a full text catalog. Example: create fulltext catalog %FULLTEXTCATALOG% - - -6412B256-A895-43EA-80C4-E6A7F60140C6 -AlterAccentSensitivity -[%AccentSensitivity%?alter fulltext catalog %FULLTEXTCATALOG% - rebuild with accent_sensitivity = %AccentSensitivity%] - -0 - -0 - -Modify the case sensitivity of the full text catalog -1 - - -42D88325-5190-4C21-B693-C4FCEAE12080 -AlterStatementList -0 - -0 - -List of text items representing statements modifying the corresponding attributes: -Example: -Rename = Code -AlterAccentSensitivity=AccentSensitivity -AlterDefault=Default -AlterAccentSensitivity -AlterDefault - - - -3E28802F-4566-4C1B-9708-455DE386088C -AlterDefault -[%Default%?alter fulltext catalog %FULLTEXTCATALOG% - as default] - -0 - -0 - -Sets the full text catalog as default -1 - - -A3ABC2D6-0552-4E3D-ADEC-C6AF2C1F9E65 -SqlAttrQuery -{FULLTEXTCATALOG, Path, Default, FileGroup, Authorization} -select - ftc.name as "FULLTEXTCATALOG", - ftc.path as "Path", - case (ftc.is_default) when 1 then 'true' else 'false' end as "Default", - case (ftc.is_accent_sensitivity_on) when 1 then 'on' else 'off' end as "AccentSensitivity", - fg.name as "FileGroup", - dp.name as "Authorization" -from - [%CATALOG%.]sys.fulltext_catalogs ftc, - [%CATALOG%.]sys.filegroups fg, - [%CATALOG%.]sys.database_principals dp -where - ftc.data_space_id = fg.data_space_id and - ftc.principal_id = dp.principal_id -[and ftc.name = %.q:FULLTEXTCATALOG%] -order by ftc.name - -0 - -0 - -SQL query to reverse object attributes - - - - -588B3C54-38C0-4AD9-A32B-38809CF1CB97 -Extended Object <<XMLSchemaCollection>> -FRMEOBJ <<XMLSchemaCollection>> -0 - -0 - - - -E95FE1B2-6EF9-4327-86D7-F82B881A9C52 -SqlListQuery -{XMLSCHEMACOLLECTION, OWNER} - -select - xsc.name as "XMLSCHEMACOLLECTION", - SCHEMA_NAME(xsc.schema_id) -from - [%CATALOG%.]sys.xml_schema_collections xsc -order by 1 -0 - -0 - -SQL query to list objects - - -3FE94599-29A4-477A-B3B2-C6B3F33BC4C0 -Drop -drop xml schema collection [%QUALIFIER%]%XMLSCHEMACOLLECTION% -0 - -0 - -Command for dropping an extended object. Example: drop extended object %EXTENDEDOBJECT% - - -84627AD9-8203-4F0E-A31A-29A64C525B7E -Create -create xml schema collection [%QUALIFIER%]%XMLSCHEMACOLLECTION% as -%.qE:Content% -0 - -0 - -Command for creating an xml schema collection. Example: create xml schema collection %XMLSCHEMACOLLECTION% - - - - -089489CC-7460-4A02-8E6F-A0FFAF8C8438 -GenerationOrder -<Order> -<Metaclass Name="Database"> - <Metaclass Name="Database::Permission" /> -</Metaclass> -<Metaclass Name="Storage" /> -<Metaclass Name="Tablespace" /> -<Metaclass Name="BusinessRule" /> -<Metaclass Name="Sequence"> - <Metaclass Name="Sequence::Permission" /> -</Metaclass> -<Metaclass Name="ExtendedObject" Stereotype="Assembly" /> -<Metaclass Name="AbstractDataType"> - <Metaclass Name="AbstractDataType::Permission" /> -</Metaclass> -<Metaclass Name="PhysicalDefault" /> -<Metaclass Name="User" Stereotype="User" /> -<Metaclass Name="User" Stereotype="Schema" /> -<Metaclass Name="Group" /> -<Metaclass Name="Role" /> -<Metaclass Name="PhysicalDomain" /> -<Metaclass Name="ExtendedObject" Stereotype="Aggregate" /> -<Metaclass Name="ExtendedObject" Stereotype="Certificate" /> -<Metaclass Name="ExtendedObject" Stereotype="AsymmetricKey" /> -<Metaclass Name="ExtendedObject" Stereotype="SymmetricKey" /> -<Metaclass Name="ExtendedObject" Stereotype="PartitionFunction" /> -<Metaclass Name="ExtendedObject" Stereotype="PartitionScheme" /> -<Metaclass Name="ExtendedObject" Stereotype="FullTextCatalog" /> -<Metaclass Name="ExtendedObject" Stereotype="XMLSchemaCollection" /> -<Metaclass Name="Table"> - <Metaclass Name="Table::Key" /> - <Metaclass Name="Table::Index" /> - <Metaclass Name="Table::Permission" /> -</Metaclass> -<Metaclass Name="View"> - <Metaclass Name="View::ViewIndex" /> - <Metaclass Name="View::Permission" /> -</Metaclass> -<Metaclass Name="Reference" /> -<Metaclass Name="JoinIndex" /> -<Metaclass Name="Synonym"> - <Metaclass Name="Synonym::Permission" /> -</Metaclass> -<Metaclass Name="Dimension" /> -<Metaclass Name="Procedure"> - <Metaclass Name="Procedure::Permission" /> -</Metaclass> -<Metaclass Name="DatabasePackage"> - <Metaclass Name="DatabasePackage::Permission" /> -</Metaclass> -<Metaclass Name="Table::Trigger" /> -<Metaclass Name="View::Trigger" /> -<Metaclass Name="WebService" /> -<Metaclass Name="ExtendedObject" Stereotype="MessageType" /> -<Metaclass Name="ExtendedObject" Stereotype="Contract" /> -<Metaclass Name="ExtendedObject" Stereotype="Queue" /> -<Metaclass Name="ExtendedObject" Stereotype="Service" /> -<Metaclass Name="ExtendedObject" Stereotype="Route" /> -<Metaclass Name="ExtendedObject" Stereotype="EndPoint" /> -<Metaclass Name="ExtendedObject" Stereotype="EventNotification" /> -<Metaclass Name="ExtendedObject" Stereotype="RemoteServiceBinding" /> -</Order> -0 - -1327671821 -mledier -This list defines the objects generation order. - - -A9AD0519-5BD1-48DE-8107-1CF0E671205E -Extended Object <<EndPoint>> -FRMEOBJ <<EndPoint>> -0 - -0 - - - -8B04EB83-F7A0-47F7-A138-38620B4DA6B5 -Create -create endpoint %ENDPOINT%[ authorization %Owner.GeneratedName%] [ - state = %State%] - as %Protocol% ( - %ProtocolArgument%) - for %Language% ( - %LanguageArgument%) - -0 - -1286984656 -psitja -Command for creating an extended object. Example: create extended object %EXTENDEDOBJECT% - - -2DFB5F7F-FCEF-4EA0-B41E-4B201748A67C -Drop -drop endpoint %ENDPOINT% -0 - -0 - -Command for dropping an extended object. Example: drop extended object %EXTENDEDOBJECT% - - -5FCDAE7F-EB96-4981-9D72-D89044A39A89 -SqlListQuery -{Owner.GeneratedName, ENDPOINT} - -select - coalesce(p.name, s.name), - e.name -from - [%CATALOG%.]sys.endpoints e - left outer join [%CATALOG%.]sys.database_principals p on (p.principal_id = e.principal_id) - left outer join [%CATALOG%.]sys.server_principals s on (s.principal_id = e.principal_id) -[where p.name = %.q:SCHEMA%] -0 - -1286984656 -psitja -SQL query to list objects - - -470E4118-DCAA-4BB6-921D-08D2D8F98A4A -SqlAttrQuery -{Owner.GeneratedName, ENDPOINT, State, Protocol, ProtocolArgument, Language, LanguageArgument} - -select - coalesce(p.name, s.name), - e.name, - e.state_desc, - e.protocol_desc, - case(e.protocol_desc) - when 'TCP' then - (select - case(x.is_dynamic_port) when 1 then '' else 'listener_port =' + convert(varchar, x.port) + ', ' end - + 'listener_ip =' + case(isnull(x.ip_address, '')) when '' then 'all' else convert(varchar, x.ip_address) end - from sys.tcp_endpoints x where x.endpoint_id = e.endpoint_id) - when 'HTTP' then - (select 'path = ' + x.url_path - + replace(replace(', authentication = (' - + case(x.is_anonymous_enabled) when 1 then 'anonymous, ' end - + case(x.is_basic_auth_enabled) when 1 then 'basic, ' end - + case(x.is_digest_auth_enabled) when 1 then 'digest, ' end - + case(x.is_kerberos_auth_enabled) when 1 then 'kerberos, ' end - + case(x.is_ntlm_auth_enabled) when 1 then 'ntlm, ' end - + case(x.is_integrated_auth_enabled) when 1 then 'integrated, ' end - + ')', ', )', ')'), ', authentication = ()', '') - + replace(replace(', port = (' - + case(x.is_clear_port_enabled) when 1 then 'clear, ' end - + case(x.is_ssl_port_enabled ) when 1 then 'ssl, ' end - + ')', ', )', ')'), ', port = ()', '') - + ', site = ' + x.site - + case(x.is_clear_port_enabled) when 1 then ', clear_port = ' + convert(varchar, x.clear_port) end - + case(x.is_ssl_port_enabled) when 1 then ', ssl_port = ' + convert(varchar, x.ssl_port) end - + case(x.is_digest_auth_enabled) when 1 then ', auth_realm = ' + coalesce(x.authorization_realm collate Latin1_general_CI_AS, 'NONE') end - + case(x.is_basic_auth_enabled) when 1 then ', default_logon_domain = ' + coalesce(x.default_logon_domain collate Latin1_general_CI_AS, 'NONE') end - + case(x.is_compression_enabled) when 1 then ', compression = enabled' else ', compression = disabled' end - from sys.http_endpoints x where x.endpoint_id = e.endpoint_id) - else '' - end, - e.type_desc, - case - when (e.type = 4) then (select - ' role = ' + m.role_desc - + ', AUTHENTICATION = ' - + case - when(m.connection_auth <= 3) then 'WINDOWS ' + m.connection_auth_desc - when(m.connection_auth >= 8) then replace(m.connection_auth_desc, ', ', ' ' + c.name collate SQL_Latin1_General_CP1_CI_AS) - else replace(m.connection_auth_desc, ',', '') + 'CERTIFICATE ' + c.name collate SQL_Latin1_General_CP1_CI_AS - end - + ', ENCRYPTION = ' - + case(m.is_encryption_enabled) when 1 then case(m.encryption_algorithm) - when 0 then 'SUPPORTED' - when 1 then 'REQUIRED ALGORITHM RC4' - when 2 then 'REQUIRED ALGORITHM AES' - when 3 then 'SUPPORTED ALGORITHM RC4' - when 4 then 'SUPPORTED ALGORITHM AES' - when 5 then 'REQUIRED ALGORITHM RC4 AES' - when 6 then 'REQUIRED ALGORITHM AES RC4' - when 7 then 'SUPPORTED ALGORITHM RC4 AES' - when 8 then 'SUPPORTED ALGORITHM AES RC4' - else '' end - else 'DISABLED' end - from [%CATALOG%.]sys.database_mirroring_endpoints m left outer join [%CATALOG%.]sys.certificates c on (c.certificate_id = m.certificate_id) - where m.endpoint_id = e.endpoint_id) - when (e.type = 3) then (select - ' AUTHENTICATION = ' - + case - when(b.connection_auth <= 3) then 'WINDOWS ' + b.connection_auth_desc - when(b.connection_auth >= 8) then replace(b.connection_auth_desc, ', ', ' ' + c.name collate SQL_Latin1_General_CP1_CI_AS) - else replace(b.connection_auth_desc, ',', '') + 'CERTIFICATE ' + c.name collate SQL_Latin1_General_CP1_CI_AS - end - + ', ENCRYPTION = ' - + case(b.encryption_algorithm) - when 0 then 'DISABLED' - when 1 then 'REQUIRED ALGORITHM RC4' - when 2 then 'REQUIRED ALGORITHM AES' - when 3 then 'SUPPORTED ALGORITHM RC4' - when 4 then 'SUPPORTED ALGORITHM AES' - when 5 then 'REQUIRED ALGORITHM RC4 AES' - when 6 then 'REQUIRED ALGORITHM AES RC4' - when 7 then 'SUPPORTED ALGORITHM RC4 AES' - when 8 then 'SUPPORTED ALGORITHM AES RC4' - else 'DISABLED' end - + case (b.is_message_forwarding_enabled) when 1 then - ', MESSAGE_FORWARDING = ENABLED , MESSAGE_FORWARD_SIZE = ' + convert(varchar, b.message_forwarding_size) - else '' end - from [%CATALOG%.]sys.service_broker_endpoints b left outer join [%CATALOG%.]sys.certificates c on (c.certificate_id = b.certificate_id) - where b.endpoint_id = e.endpoint_id) - end -from - [%CATALOG%.]sys.endpoints e - left outer join [%CATALOG%.]sys.database_principals p on (p.principal_id = e.principal_id) - left outer join [%CATALOG%.]sys.server_principals s on (s.principal_id = e.principal_id) -where 1=1 -[and p.name = %.q:SCHEMA%] -[and e.name = %.q:ENDPOINT%] -0 - -1286984656 -psitja -SQL query to reverse object attributes - - - - -9DF5AB22-792F-43FB-A57D-2FE2D6F00B63 -Extended Object <<Queue>> -FRMEOBJ <<Queue>> -0 - -0 - - - -19DCCC3B-C3EE-4060-906D-787EE5DE30D6 -Create -create queue [%QUALIFIER%]%QUEUE% -[ with - [status = [%Status%?on:off]][[,] - retention = [%Retention%?on:off]][%Activation%?, - activation ( - [status = [%ActivationStatus%?on:off],] - procedure_name = %ActivationProcedureName%, - max_queue_readers = %ActivationMaxQueueReaders%, - execute as %ActivationExecuteAs% - )] -] -[on %FileGroup.GeneratedName%] -0 - -0 - -Command for creating an extended object. Example: create extended object %EXTENDEDOBJECT% - - -DBA5C2E4-5BCF-4EB7-AB62-CA9C2BDF1E84 -Drop -if exists (select 1 from sys.service_queues where object_id = object_id('[%QUALIFIER%]%QUEUE%')) - drop queue [%QUALIFIER%]%QUEUE% -0 - -0 - -Command for dropping an extended object. Example: drop extended object %EXTENDEDOBJECT% - - -C1919134-3F0A-49D9-9216-3BBDC9BE71CF -SqlListQuery -{OWNER, QUEUE} - -select - u.name, - q.name -from - [%CATALOG%.]sys.service_queues q - join [%CATALOG%.]sys.schemas u on (u.schema_id = q.schema_id) -[where u.name = %.q:SCHEMA%] -0 - -0 - -SQL query to list objects - - -07D3A6B9-1FB2-44CF-97C6-F5B171616809 -SqlAttrQuery -{OWNER, QUEUE, Status, Retention, Activation, ActivationStatus, ActivationProcedureName, ActivationExecuteAs, ActivationMaxQueueReaders} - -select - u.name, - q.name, - q.is_enqueue_enabled, - q.is_retention_enabled, - case(isnull(q.activation_procedure, '')) when '' then 'False' else 'True' end, - q.is_activation_enabled, - isnull(q.activation_procedure, ''), - case(isnull(q.execute_as_principal_id, -9)) - when -9 then '' - when -2 then 'owner' - when user_id() then 'self' - else (select p.name from [%CATALOG%.]sys.database_principals p where p.principal_id = q.execute_as_principal_id) - end as coln, - q.max_readers -from - [%CATALOG%.]sys.service_queues q - join [%CATALOG%.]sys.schemas u on (u.schema_id = q.schema_id) -where 1=1 -[and u.name = %.q:SCHEMA%] -[and q.name = %.q:QUEUE%] -0 - -0 - -SQL query to reverse object attributes - - - - -5C0CD743-F47A-4771-8793-693DC5D7AE50 -Extended Object <<Contract>> -FRMEOBJ <<Contract>> -0 - -0 - - - -48F0FF12-979D-4B1F-B9D9-75059794DAC2 -Create -create contract %CONTRACT%[ authorization %Owner.GeneratedName%] -( -[%R%?%ReversedMessageContracts%:%messageContractList%] -) -0 - -1286984656 -psitja -Command for creating an extended object. Example: create extended object %EXTENDEDOBJECT% - - -AEF96E9E-73A4-495D-981D-CE8B34B65D1C -Drop -if exists (select 1 from sys.service_contracts where name = '%CONTRACT%') - drop contract %CONTRACT% -0 - -0 - -Command for dropping an extended object. Example: drop extended object %EXTENDEDOBJECT% - - -588554BA-F156-4F03-B03F-57841BB18FB8 -SqlListQuery -{Owner.GeneratedName, CONTRACT} - -select - u.name, - c.name -from - [%CATALOG%.]sys.service_contracts c - join [%CATALOG%.]sys.database_principals u on (u.principal_id = c.principal_id) -[where u.name = %.q:SCHEMA%] -order by 1, 2 -0 - -1286984656 -psitja -SQL query to list objects - - -BEE9C17E-381A-4376-8CA3-D9C8CD1C9602 -SqlAttrQuery -{Owner.GeneratedName ID, CONTRACT ID, ReversedMessageContracts ...} - -select - u.name, - c.name, - m.name - + ' sent by ' - + case((x.is_sent_by_target & 1) + 2*(x.is_sent_by_initiator & 1)) when 1 then 'target' when 2 then 'initiator' when 3 then 'any' end - + ',' as coln -from - [%CATALOG%.]sys.service_contracts c - join [%CATALOG%.]sys.service_contract_message_usages x on (x.service_contract_id = c.service_contract_id) - join [%CATALOG%.]sys.service_message_types m on (m.message_type_id = x.message_type_id) - join [%CATALOG%.]sys.database_principals u on (u.principal_id = c.principal_id) -where 1=1 -[and u.name = %.q:OWNER%] -[and c.name = %.q:CONTRACT%] -order by 1, 2 -0 - -1286984656 -psitja -SQL query to reverse object attributes - - - - -E4D3BC9B-B3B0-40F9-B741-F20F1AAF75FA -Extended Object <<MessageType>> -FRMEOBJ <<MessageType>> -0 - -0 - - - -09BDE53E-7039-49E8-855A-F19E73988638 -Create -create message type %MESSAGETYPE%[ authorization %Owner.GeneratedName%] - [ validation = %Validation%[%R%?%SchemaCollectionName%:[%.L9:Validation%=valid_xml? %SchemaCollectionName.GeneratedName%]]] - -0 - -1286984656 -psitja -Command for creating an extended object. Example: create extended object %EXTENDEDOBJECT% - - -5E9F1BE9-5B85-405A-91C0-1DF5D106B9C9 -Drop -if exists (select 1 from sys.service_message_types where name = '%MESSAGETYPE%') - drop message type %MESSAGETYPE% - -0 - -0 - -Command for dropping an extended object. Example: drop extended object %EXTENDEDOBJECT% - - -9A118BD8-6C02-4CC0-B9FC-627C721F69D8 -SqlListQuery -{Owner.GeneratedName, MESSAGETYPE} - -select - u.name, - s.name -from - [%CATALOG%.]sys.service_message_types s - join [%CATALOG%.]sys.database_principals u on (u.principal_id = s.principal_id) -[where u.name = %.q:SCHEMA%] -order by 1, 2 -0 - -1286984656 -psitja -SQL query to list objects - - -C0785EEA-802E-4299-B3E7-FD826F0E6987 -SqlAttrQuery -{Owner.GeneratedName, MESSAGETYPE, Validation, SchemaCollectionName} - -select - u.name, - s.name, - case (s.validation) - when 'E' then 'empty' - when 'N' then 'none' - when 'X' then case(isnull(s.xml_collection_id, 0)) - when 0 then 'well_formed_xml' - else 'valid_xml with schema collection' - end - end as colnA, - case(isnull(s.xml_collection_id, 0)) when 0 then '' else (select x.name from [%CATALOG%.]sys.xml_schema_collections x where x.xml_collection_id = s.xml_collection_id) end as colnB -from - [%CATALOG%.]sys.service_message_types s - join [%CATALOG%.]sys.database_principals u on (u.principal_id = s.principal_id) -where 1=1 -[and u.name = %.q:SCHEMA%] -[and s.name = %.q:MESSAGETYPE%] -order by 1, 2 -0 - -1286984656 -psitja -SQL query to reverse object attributes - - - - -03D130E7-76F4-434D-A476-1EF02B935932 -Extended Object <<EventNotification>> -FRMEOBJ <<EventNotification>> -0 - -0 - - - -35E2ADE7-0DD5-4151-A2BD-741F9DFB700C -Create -create event notification %EVENTNOTIFICATION% on %AppliesOn%[%.L:AppliesOn%=queue? [%Queue.Owner%?%Queue.Owner.GeneratedName%.]%Queue.GeneratedName%][%WithFanIn%? with fan in] - for %Events% - to service %.q:Service.GeneratedName% , %.q:Instance% -0 - -0 - -Command for creating an extended object. Example: create extended object %EXTENDEDOBJECT% - - -62BEEE54-50C6-4860-A328-EBA4DECD651A -Drop -drop event notification %EVENTNOTIFICATION% on %AppliesOn%[%.L:AppliesOn%=queue? [%Queue.Owner%?%Queue.Owner.GeneratedName%.]%Queue.GeneratedName%] - -0 - -0 - -Command for dropping an extended object. Example: drop extended object %EXTENDEDOBJECT% - - -D319AD70-3F37-4CE5-B2E1-09CC1E3B7757 -SqlListQuery -{EVENTNOTIFICATION} - -select - s.name as "Event" -from - [%CATALOG%.]sys.server_event_notifications s -union -select - s.name as "Event" -from - [%CATALOG%.]sys.event_notifications s -order by 1 -0 - -0 - -SQL query to list objects - - -87CBD623-F9F3-4609-82E6-CE9E3B010DE8 -SqlAttrQuery -{EVENTNOTIFICATION, AppliesOn, Events, Service, Instance, Queue} - -select - s.name as "Event", - s.parent_class_desc as "On", - t.type_desc as "For", - s.service_name as "service", - case(d.name) when db_name() then 'current_database' else s.broker_instance end as "broker_instance", - '' as "queue" -from - [%CATALOG%.]sys.server_event_notifications s - join [%CATALOG%.]sys.server_events t on (t.object_id = s.object_id) - left outer join [%CATALOG%.]sys.databases d on (d.service_broker_guid = s.broker_instance) -union -select - s.name as "Event", - case(s.parent_class) when 0 then s.parent_class_desc else - case(q.type) when 'SQ' then 'queue' else '' end end as "On", - t.type_desc as "For", - s.service_name as "service", - case(d.name) when db_name() then 'current database' else s.broker_instance end as "broker_instance", - q.name as "queue" -from - [%CATALOG%.]sys.event_notifications s - join [%CATALOG%.]sys.events t on (t.object_id = s.object_id) - left outer join [%CATALOG%.]sys.databases d on (d.service_broker_guid = s.broker_instance) - left outer join [%CATALOG%.]sys.service_queues q on (q.object_id = s.parent_id) -[where s.name = %.q:EVENTNOTIFICATION%] -order by 1, 2 -0 - -0 - -SQL query to reverse object attributes - - - - -342F5151-AF59-4258-91B8-59995AADD1D4 -Extended Object <<Route>> -FRMEOBJ <<Route>> -0 - -0 - - - -857B4521-C3FB-4706-A65F-71A53613EC52 -Create -create route %ROUTE%[ authorization %Owner.GeneratedName%] -with[ - service_name = %.q:Service%,][ - broker_instance = %.q:BrokerInstance%,][ - lifetime = %Lifetime%,] - address = %.q:Address%[, - mirror_address = %.q:MirrorAddress%] - -0 - -1286984656 -psitja -Command for creating an extended object. Example: create extended object %EXTENDEDOBJECT% - - -64910496-D8C6-4BBF-8BB6-93971F22C4F0 -Drop -if exists (select 1 from sys.routes where name = '%ROUTE%') - drop route %ROUTE% -0 - -0 - -Command for dropping an extended object. Example: drop extended object %EXTENDEDOBJECT% - - -0CBA6831-E2BC-44DC-9D28-AD7B978311BC -SqlListQuery -{Owner.GeneratedName, ROUTE} - -select - u.name, - r.name -from - [%CATALOG%.]sys.routes r - join [%CATALOG%.]sys.database_principals u on (u.principal_id = r.principal_id) -[where u.name = %.q:SCHEMA%] -0 - -1286984656 -psitja -SQL query to list objects - - -4BABA7AF-91C7-49C6-9581-1EAA1A556E78 -SqlAttrQuery -{Owner.GeneratedName, ROUTE, Service, BrokerInstance, Lifetime, Address, MirrorAddress} - -select - u.name, - r.name, - r.remote_service_name, - r.broker_instance, - datediff(s, getdate(), r.lifetime), - r.address, - r.mirror_address -from - [%CATALOG%.]sys.routes r - join [%CATALOG%.]sys.database_principals u on (u.principal_id = r.principal_id) -where 1=1 -[and u.name = %.q:SCHEMA%] -[and r.name = %.q:ROUTE%] -0 - -1286984656 -psitja -SQL query to reverse object attributes - - - - -7925C6D3-AB5B-4B08-9EE0-5CEC27BE6136 -Extended Object <<Service>> -FRMEOBJ <<Service>> -0 - -0 - - - -AB9E901C-2106-4404-822C-AC8FCF79D41B -Create -create service %SERVICE%[ authorization %Owner.GeneratedName%] - on queue [%Queue.Owner%?%Queue.Owner.GeneratedName%.]%Queue.GeneratedName% -[%R%?(%ReversedContracts%):[(%contractList%)]] - -0 - -1286984656 -psitja -Command for creating an extended object. Example: create extended object %EXTENDEDOBJECT% - - -28B17989-D235-4D32-BB74-9286D429CE6B -Drop -if exists (select 1 from sys.services where name = '%SERVICE%') - drop service %SERVICE% -0 - -0 - -Command for dropping an extended object. Example: drop extended object %EXTENDEDOBJECT% - - -0E85657F-D453-4F12-875A-58F82B32473C -SqlListQuery -{Owner.GeneratedName, SERVICE} - -select - u.name, - s.name -from - [%CATALOG%.]sys.services s - join [%CATALOG%.]sys.database_principals u on (u.principal_id = s.principal_id) -[where u.name = %.q:SCHEMA%] -0 - -1286984656 -psitja -SQL query to list objects - - -C25E1373-A845-450F-A745-F4207C153131 -SqlAttrQuery -{Owner.GeneratedName, SERVICE, Queue} - -select - u.name, - s.name, - q.name -from - [%CATALOG%.]sys.services s - join [%CATALOG%.]sys.service_queues q on (q.object_id = s.service_queue_id) - join [%CATALOG%.]sys.database_principals u on (u.principal_id = s.principal_id) -where 1=1 -[and u.name = %.q:OWNER%] -[and s.name = %.q:SERVICE%] - -0 - -1286984656 -psitja -SQL query to reverse object attributes - - - - -845D8A49-4772-45FA-998D-8022A0572BCC -Extended Object <<RemoteServiceBinding>> -FRMEOBJ <<RemoteServiceBinding>> -0 - -0 - - - -FDCB6683-F7F7-4B08-BCFD-1ACEB343A7F8 -Create -create remote service binding %REMOTESERVICEBINDING%[ authorization %Owner.GeneratedName%] - to service %.q:RemoteService% - with user = %User.GeneratedName% [%Anonymous%?, anonymous = on:[, anonymous = off]] -0 - -1286984656 -psitja -Command for creating an extended object. Example: create extended object %EXTENDEDOBJECT% - - -2250B3F4-63B4-4BEA-AF34-0F16C1884676 -Drop -if exists (select 1 from sys.remote_service_bindings where name = '%REMOTESERVICEBINDING%') - drop remote service binding %REMOTESERVICEBINDING% -0 - -0 - -Command for dropping an extended object. Example: drop extended object %EXTENDEDOBJECT% - - -B8B9F0C6-D2BF-409C-93D2-BAF6A9D36CD2 -SqlListQuery -{Owner.GeneratedName, REMOTESERVICEBINDING} - -select - u.name, - r.name -from - [%CATALOG%.]sys.remote_service_bindings r - join [%CATALOG%.]sys.database_principals u on (u.principal_id = r.principal_id) -[where u.name = %.q:SCHEMA%] - -0 - -1286984656 -psitja -SQL query to list objects - - -44FADBA3-8657-47BE-860D-8FF09D398667 -SqlAttrQuery -{Owner.GeneratedName, REMOTESERVICEBINDING, RemoteService, User, Anonymous} - -select - u.name, - r.name, - r.remote_service_name, - user_name(r.remote_principal_id), - r.is_anonymous_on -from - [%CATALOG%.]sys.remote_service_bindings r - join [%CATALOG%.]sys.database_principals u on (u.principal_id = r.principal_id) -where 1=1 -[and u.name = %.q:SCHEMA%] -[and r.name = %.q:REMOTESERVICEBINDING%] - -0 - -1286984656 -psitja -SQL query to reverse object attributes - - - - -72007E7D-3A2D-442B-9454-871B428C7844 -Extended Sub-Object <<AggregateParameter>> -FRMESOB <<AggregateParameter>> -0 - -0 - -The following system variables are available: - "EXTENDEDSUBOBJECT" // generated code of the extended sub-object - "SUBOBJECTPARENT" // generated code of the parent object - "EXTOBJECTDEFN" // complete body of the extended sub-object definition. Contains definition of extended composition listed in the "DefinitionContent" item - - - -5A8EB36B-1FFB-47A6-AB87-6FAD09BF687C -Add -@%EXTENDEDSUBOBJECT% %DisplayType% -0 - -0 - -Command for defining a sub-object. Example: add %EXTENDEDSUBOBJECT% on %SUBOBJECTPARENT% - - -A229E7E3-4112-4860-82C3-6CE3B700AB41 -SqlListQuery -{EXTSUBOBJTPARENTOWNER, EXTSUBOBJTPARENT, EXTENDEDSUBOBJECT, InputParameterType} - -select - schema_name(so.schema_id), - so.name, - rp.name, - rt.name + - case when (rp.max_length <> rt.max_length) then '(' + LTRIM(STR(case when rp.system_type_id in (99, 231, 239) then (rp.max_length/2) else rp.max_length end)) + ')' - when (rp.scale <> rt.scale) then (LTRIM(STR(rp.precision)) + ',' + LTRIM(STR(rp.scale))) - when (rp.precision <> rt.precision) then '(' + STR(rp.precision) + ')' else '' end as "ParameterType" -from - [%CATALOG%.]sys.objects so - join [%CATALOG%.]sys.parameters rp on (rp.object_id = so.object_id and rp.is_output = 0) - join [%CATALOG%.]sys.types rt on (rt.system_type_id = rp.system_type_id) -where - so.type = 'AF' -order by so.name -0 - -0 - -SQL query to list objects - - - - -2388A1A6-C0EF-4374-B667-3181C00E7872 -Abstract Data Type Attribute -ATTR -0 - -0 - -The following system variables are available: - "ADTATTR" // generated code of the abstract data type attribute - - - -A806FD1D-3862-4439-AB6F-3EC4EFB3AA9A -AllowedADT -CLR -Table - -0 - -0 - -List of ADTs which can be used as the datatype of an ADT attribute - - -E85B6B35-E2FC-457B-AF53-C42D8C05C078 -Add -%ADTATTR%[%Computed%? as %Expression%[%Persisted%? persisted][%NOTNULL%? not null]: %DATATYPE%[.Z:[ collate %Collate%][ %NOTNULL%][%Identity%? identity[(%Seed%, %Increment%)]:[ default %Default%]][%RowGuidCol%? rowguidcol]]] -0 - -0 - -Command for defining an abstract data type attribute - - - - -DCA599AF-B17A-4FA5-8B5B-C4FB94C93B30 -Abstract Data Type Procedure -PDMADPR -0 - -0 - -Manages the abstract data type package procedure - - -DEDCFE4E-36F8-49AC-93A0-A1BA6D402526 -Extended Object <<ResourcePool>> -FRMEOBJ <<ResourcePool>> -0 - -0 - -The following system variables are available: - "EXTENDEDOBJECT" // generated code of the extended object - "EXTOBJECTDEFN" // complete body of the extended object definition. Contains definition of extended composition listed in the "DefinitionContent" item - - - -9BC6347F-D7FB-4624-8F22-9BB28B5D3D09 -Create -create resource pool %RESOURCEPOOL% -[%OPTIONS%] -0 - -0 - -Command for creating an extended object. Example: create extended object %EXTENDEDOBJECT% - - -27796C16-06ED-4EE0-A887-1ED8A9BB1FFE -Options -with : composite=yes, parenthesis=yes, separator=yes -{ - min_cpu_percent = %s - max_cpu_percent = %s - min_memory_percent = %s - max_memory_percent = %s -} -0 - -0 - -Available options for creating a user - - -5283CCE8-06B5-4C55-BA7E-F572D8989675 -SqlListQuery -{RESOURCEPOOL, MinCpuPercent, MaxCpuPercent, MinMemoryPercent, MaxMemoryPercent} - -select - name, - min_cpu_percent, - max_cpu_percent, - min_memory_percent, - max_memory_percent -from - [%CATALOG%.]sys.resource_governor_resource_pools -0 - -0 - -SQL query to list objects - - -3B901486-3294-4BA5-9EB5-A755B19EE057 -Drop -drop resource pool %RESOURCEPOOL% -0 - -0 - -Drops a user-defined Resource Governor resource pool. - - -BF489689-7A43-440D-BA7F-D7ECDDA79D1E -AlterStatementList -0 - -0 - -List of text items representing statements modifying the corresponding attributes: -Example: -Rename = Code -Alter=PhysicalOptions -Alter - - - -A9CE02DF-80B3-46EE-94B9-51740966EA2E -Alter -alter resource pool %RESOURCEPOOL% -[%OPTIONS%] -0 - -0 - -Changes an existing Resource Governor workload group configuration and optionally, assigns it to a to a Resource Governor resource pool. -1 - - - - -2D6D019E-BAE7-4EAE-BF34-2EACADB1645E -User <<Schema>> -USER <<Schema>> -1271341631 -psitja -1277123527 -psitja -The following system variables are available: - "USER" // generated code of the user - - - -EF01DCA1-5478-41FB-A82E-538BF3CD1C2C -Drop -drop schema %USER% -1271341631 -psitja -1278597469 -psitja -Command for dropping a user - - -28EC4693-595D-418F-A3EB-A735A3029B8A -Create -create schema [%R%?[.O:[ authorization %SchemaOwner.GeneratedName%][%USER%[ authorization %SchemaOwner.GeneratedName%]]]:%USER%[ authorization %SchemaOwner.GeneratedName%]][ -%SCHEMADEFN%] -1271341631 -psitja -1277219988 -psitja -Command for creating a user - - -1F72DBA0-4AA9-420F-8672-AED8CD1E1F9E -UserComment -Add comment on schema %USER% -%.q:COMMENT% -1274780343 -psitja -1277121181 -psitja -Command for adding an user comment - - -43B78082-EFCD-4621-84DA-B360FEBB88EE -SqlListQuery -{USER} - -select - s.name -from - [%CATALOG%.]sys.schemas s -[where UPPER(s.name) = %.qU:SCHEMA%] -order by 1 - -1275912061 -psitja -1277121181 -psitja -SQL query to list objects - - -840BF7D7-164B-4F38-BF70-6916F5AFC3CE -Enable -1275913538 -psitja -1277121181 -psitja -User allowed -1 - - -E054AA18-2FD0-41FE-A805-FF5E12DF350A -SqlAttrQuery -{USER, SchemaOwner} - -select - s.name, - u.name -from - [%CATALOG%.]sys.schemas s - join [%CATALOG%.]sys.database_principals u on (u.principal_id = s.principal_id and (u.type in ('U', 'S') or (u.type = 'R' and s.schema_id <> s.principal_id))) -order by 1 - -1276595008 -psitja -1277307491 -psitja -SQL query to reverse object attributes - - - - -3B2B865A-918D-4FB6-B7C2-555D10D0F274 -User <<User>> -USER <<User>> -1271341631 -psitja -1285249153 -psitja -The following system variables are available: - "USER" // generated code of the user - - - -22CCAB00-513F-4779-BC34-D61C77493838 -Create -[%ImplicitSchema%?[%R%?[exec ]][execute ]sp_grantdbaccess %USER%:create user %USER%[ with default_schema = %DefaultSchema.GeneratedName%] -] -1273143108 -psitja -1277219988 -psitja -Command for creating a user - - -DB0AAEA7-8C1D-4F12-AD95-D4C3D23F7191 -Drop -drop user %USER% -1274263512 -psitja -1278597469 -psitja -Command for dropping a user - - -8D0492F2-0A78-49FD-9DFB-DD269DAD146F -UserComment -Add comment on user %USER% -%.q:COMMENT% -1274780343 -psitja -1277121181 -psitja -Command for adding an user comment - - -B948A287-D1B5-433B-8637-FA23C5E004FC -SqlListQuery -{USER} - -select - u.name -from - [%CATALOG%.]sys.database_principals u -where - u.type in ('U', 'S') -[ and UPPER(u.name) = %.qU:SCHEMA%] -order by 1 -1275912061 -psitja -1277121181 -psitja -SQL query to list objects - - -1E147739-1020-4AEC-99E2-F7B13280FA3E -Enable -1275913538 -psitja -1277121181 -psitja -User allowed -1 - - -66C46963-EF8B-4908-B15C-0D72BAC8FE16 -SqlPermQuery -{USER ID, PRIVILEGE ...} - -select - u.name, - case (s.action) when 178 then 'CREATE FUNCTION' when 198 then 'CREATE TABLE' when 203 then 'CREATE DATABASE' - when 207 then 'CREATE VIEW' when 222 then 'CREATE PROCEDURE' when 228 then 'BACKUP DATABASE' - when 233 then 'CREATE DEFAULT' when 235 then 'BACKUP LOG' when 236 then 'CREATE RULE' end + - case (s.protecttype) when 204 then '+' when 205 then '' when 206 then '-' end + ',' as coln -from - [%CATALOG%.]sys.sysprotects s - join [%CATALOG%.]sys.sysusers u on (s.uid = u.uid and u.issqlrole <> 1) -where - s.id = 0 -order by 1 - -0 - -1277121181 -psitja -Query to reverse privileges - - -D5BCCD37-D4BF-4179-8755-3281D48A8BAF -SqlAttrQuery -{USER, DefaultSchema} - -select - u.name, - u.default_schema_name -from - [%CATALOG%.]sys.database_principals u -where - u.type in ('U', 'S') -order by 1 -1276595055 -psitja -1277307491 -psitja -SQL query to reverse object attributes - - -CE8AE9B9-89B5-4675-BC15-E16A0E690931 -ReversedQueries -SqlPermQuery -1285249049 -psitja -1327671821 -mledier -Additional attributes queries to be called by ODBC - - - - -26760C95-846A-40A9-98A6-3CD96A9A53AB -Extended Object <<WorkloadGroup>> -FRMEOBJ <<WorkloadGroup>> -0 - -0 - -The following system variables are available: - "EXTENDEDOBJECT" // generated code of the extended object - "EXTOBJECTDEFN" // complete body of the extended object definition. Contains definition of extended composition listed in the "DefinitionContent" item - - - -25BFAC10-D085-4F76-89F5-24FF294702E8 -Options -with : composite=yes, parenthesis=yes, separator=yes -{ - importance = %s : list=low|medium|high, default=medium - request_max_memory_grant_percent = %s - request_max_cpu_time_sec = %s - request_memory_grant_timeout_sec = %s - max_dop = %s - group_max_requests = %s -} -0 - -0 - -Available options for creating a user - - -38734361-717A-48D5-B6AD-4AE35330E4AD -Create -create workload group %WORKLOADGROUP% -[%OPTIONS%] -[using [%ResourcePool%?%ResourcePool.GeneratedName%:"default"]] -0 - -0 - -Command for creating an extended object. Example: create extended object %EXTENDEDOBJECT% - - -57B60E30-52AA-413C-8F1C-668B198485AA -SqlListQuery -{WORKLOADGROUP, Importance, RequestMaxMemoryGrantPercent, RequestMaxCpuTimeSec, RequestMemoryGrantTimeoutSec, MaxDop, GroupMaxRequests, ResourcePool} - -select - w.name, - w.importance, - w.request_max_memory_grant_percent, - w.request_max_cpu_time_sec, - w.request_memory_grant_timeout_sec, - w.max_dop, - w.group_max_requests, - r.name -from - [%CATALOG%.]sys.resource_governor_workload_groups w - join [%CATALOG%.]sys.resource_governor_resource_pools r on (r.pool_id = w.pool_id) -0 - -0 - -SQL query to list objects - - -5E8FE6F1-C6B8-4DF4-9361-BBDADA5B5F40 -Drop -drop workload group %WORKLOADGROUP% - -0 - -0 - -Drops an existing user-defined Resource Governor workload group. - - -2DAF01E3-6AD5-4022-9B49-2C763AA697A2 -AlterStatementList -0 - -0 - -List of text items representing statements modifying the corresponding attributes: -Example: -Rename = Code -Alter=PhysicalOptions -Alter - - - -1F3A0EC1-63C3-44DE-AB59-B82CBB4709F1 -Alter -alter workload group %WORKLOADGROUP% -[%OPTIONS%] -[using [%ResourcePool%?%ResourcePool.GeneratedName%:"default"]] -0 - -0 - -Changes an existing Resource Governor workload group configuration and optionally, assigns it to a to a Resource Governor resource pool. -1 - - - - - - -D990E41C-F4D1-43FA-9F28-8665D5B1922E -DataType -0 - -0 - -Contains data type translation entries. These entries list the correspondence between internal data types and the target database data types - - -00DE4DFC-D35B-49C9-A0D8-6D723A09D69D -AmcdDataType -0 - -1514556467 -Administrator -Data types translation table from internal data types to target database data types. - %n is the length of the data type - %s is the size of the data type - %p is the precision of the data type -<UNDEF>=<Undefined> -A=char(1) -A%n=char(%n) -VA%n=varchar(%n) -LA=varchar(1) -LA%n=varchar(%n) -LVA=varchar(1) -LVA%n=varchar(%n) -BT=tinyint -BT%n=tinyint -SI=smallint -I=int -LI=bigint -N=numeric -N%n=numeric(%n) -N%s,%p=numeric(%s,%p) -DC=decimal -DC%n=decimal(%n) -DC%s,%p=decimal(%s,%p) -SF=real -F=float -F%n=float(%n) -LF=double precision -MN=money -MN%n=money -MN%s,%p=money -D=datetime -T=datetime -DT=datetime -TS=timestamp -BL=bit -NO=numeric -NO%n=numeric(%n) -BIN%n=binary(%n) -LBIN=binary(1) -LBIN%n=binary(%n) -TXT=text -TXT%n=text -MBT=nchar(1) -MBT%n=nchar(%n) -VMBT=nvarchar(255) -VMBT%n=nvarchar(%n) -PIC=image -PIC%n=image -BMP=image -BMP%n=image -OLE=image -OLE%n=image -VBIN%n=varbinary(%n) -VBINMAX=varbinary(Max) -VAMAX=varchar(Max) -VMBTMAX=nvarchar(Max) -CURSOR=cursor -VARIANT=sql_variant -TABLE=table -XML=xml -SPATIAL=geometry -*=char(10) -<UNDEF> -A -A%n -VA%n -LA -LA%n -LVA -LVA%n -BT -BT%n -SI -I -LI -N -N%n -N%s,%p -DC -DC%n -DC%s,%p -SF -F -F%n -LF -MN -MN%n -MN%s,%p -D -T -DT -TS -BL -NO -NO%n -BIN%n -LBIN -LBIN%n -TXT -TXT%n -MBT -MBT%n -VMBT -VMBT%n -PIC -PIC%n -BMP -BMP%n -OLE -OLE%n -VBIN%n -VBINMAX -VAMAX -VMBTMAX -CURSOR -VARIANT -TABLE -XML -SPATIAL - - - -9152290A-C1D9-4437-B8D2-593A07DE0976 -LogPhysADTType -0 - -1514556467 -Administrator -Abstract Data types translation table from internal abstract data types to target database abstract data types. -CLR=CLR -Alias=Alias -Table=Structured -CLR -Alias -Table - - - -B2100B44-6D0A-4600-BF9A-068973476D60 -PhysDataType -0 - -1514556467 -Administrator -Data types translation table from target database data types to internal data types. -<Undefined>=<UNDEF> -char(%n)=A%n -character(%n)=A%n -varchar(%n)=VA%n -varchar(Max)=VAMAX -char varying(%n)=VA%n -character varying(%n)=VA%n -sysname=VMBT128 -uniqueidentifier=A36 -tinyint=BT -smallint=SI -int=I -integer=I -bigint=LI -numeric=N -numeric(%n)=N%n -numeric(%s,%p)=N%s,%p -decimal=DC -decimal(%n)=DC%n -decimal(%s,%p)=DC%s,%p -dec=DC -dec(%n)=DC%n -dec(%s,%p)=DC%s,%p -real=SF -float=F -float(%n)=F%n -double precision=LF -money=MN -smallmoney=MN -datetime=DT -smalldatetime=DT -date=D -time=T -time(%n)=T -datetime2=DT -datetime2(%n)=DT -datetimeoffset=DT -datetimeoffset(%n)=DT -timestamp=TS -bit=BL -binary(%n)=BIN%n -ntext=LBIN -national text=LBIN -varbinary(%n)=VBIN%n -varbinary(Max)=VBINMAX -binary varying(%n)=VBIN%n -binary varying(Max)=VBINMAX -text=TXT -national char(%n)=MBT%n -national character(%n)=MBT%n -nchar(%n)=MBT%n -nvarchar(%n)=VMBT%n -nvarchar(Max)=VMBTMAX -national char varying(%n)=VMBT%n -national char varying(Max)=VMBTMAX -national character varying(%n)=VMBT%n -national character varying(Max)=VMBTMAX -image=PIC -xml=XML -cursor=CURSOR -sql_variant=VARIANT -table=TABLE -geometry=SPATIAL -geography=SPATIAL -*=A10 -<Undefined> -char(%n) -character(%n) -varchar(%n) -varchar(Max) -char varying(%n) -character varying(%n) -sysname -uniqueidentifier -tinyint -smallint -int -integer -bigint -numeric -numeric(%n) -numeric(%s,%p) -decimal -decimal(%n) -decimal(%s,%p) -dec -dec(%n) -dec(%s,%p) -real -float -float(%n) -double precision -money -smallmoney -datetime -smalldatetime -date -time -time(%n) -datetime2 -datetime2(%n) -datetimeoffset -datetimeoffset(%n) -timestamp -bit -binary(%n) -ntext -national text -varbinary(%n) -varbinary(Max) -binary varying(%n) -binary varying(Max) -text -national char(%n) -national character(%n) -nchar(%n) -nvarchar(%n) -nvarchar(Max) -national char varying(%n) -national char varying(Max) -national character varying(%n) -national character varying(Max) -image -xml -cursor -sql_variant -table -geometry -geography - - - -F36D8AD6-58FC-47BD-8A52-2318AAD025EE -PhysDttpSize -0 - -1514556467 -Administrator -Table of storage sizes of target database data type -smallmoney=4 -datetime=8 -smalldatetime=4 -timestamp=8 -uniqueidentifier=16 -smallmoney -datetime -smalldatetime -timestamp -uniqueidentifier - - - -942DEF77-58F5-463D-B022-6CF3EC12F6CB -OdbcPhysDataType -0 - -1514556467 -Administrator -Data types translation table from ODBC data types to target database data types. -nvarchar(128)=sysname -numeric(18)=numeric -decimal(18)=decimal -float(24)=real -float(53)=float -sql_variant(8016)=sql_variant -nvarchar(128) -numeric(18) -decimal(18) -float(24) -float(53) -sql_variant(8016) - - - -64F7B426-6074-4C40-B2C3-AEFE27C0E321 -PhysLogADTType -0 - -1514556467 -Administrator -Abstract Data types translation table from target database abstract data types to internal abstract data types. -CLR=CLR -Table=Structured -CLR -Table - - - -1C035C03-0ED0-48E5-A4E8-91CD055B4347 -PhysOdbcDataType -0 - -1514556467 -Administrator -Data types translation table from target database data types to ODBC data types. -int=integer -character varying=varchar -character varying(%n)=varchar(%n) -dec=decimal -dec(%n)=decimal(%n) -dec(%s,%p)=decimal(%s,%p) -char varying=varchar -char varying(%n)=varchar(%n) -int -character varying -character varying(%n) -dec -dec(%n) -dec(%s,%p) -char varying -char varying(%n) - - - -2B4F8BAD-729E-4BB7-8D57-E747F9A92365 -AllowedADT -CLR -PMVarray -PMList -PMTable -PMObject -0 - -0 - -The list of ADT which can be used as the datatype of a domain or column - - - - - - -9841CC5C-8250-45FC-B284-F0082FD1653C -Profile -0 - -1339509359 -psitja -1 - - -AD41106B-A4CB-4374-9B72-A210848CC8AB -AbstractDataType -0 - -0 - -1 - - -FormTargetItem -79E65441-35FA-4E4D-8BCB-7B2D05DE86B9 -Forms -0 - -0 - -1 - - -CA6E52BE-F4FA-456E-BF10-75484650F376 -Microsoft -<Form > - <ExtendedAttribute Attribute="Assembly" Caption="Assembly:" AttributeID="{F783ADE5-F0B7-4612-814D-F091FD7AEA47}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> -</Form> - -0 - -1276605429 -obalen -1 -24917 - - - - -EventHandlerTargetItem -6D3E58ED-531B-4452-A458-47F2BB234E4A -Event Handlers -0 - -0 - -1 - - -CB72FA46-8D75-4EA0-A6A2-A81AAB4C357D -Initialize -0 - -0 - -1 -Function %Initialize%(obj) - - obj.Type = "CLR" - %Initialize% = True -End Function - - - - -CriterionTargetItem -94525A76-3EB3-4B4F-A8E8-965A475E0E49 -Criteria -0 - -0 - -1 - - -339A8B43-196E-464F-974A-52106503BAC7 -CLR -%Type%=="CLR" -0 - -0 - -1 - - -ExtendedAttributeTargetItem -DC40308E-9BE1-465B-A4AB-606426C5CB01 -Extended Attributes -0 - -0 - -1 - - -F783ADE5-F0B7-4612-814D-F091FD7AEA47 -Assembly -0 - -0 - -Specifies the assembly to bind with the abstract data type. -1 -15 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Assembly -0 - - - - - - - -ExtendedCollectionTargetItem -89A865F0-D250-4CA0-8402-1BFEB0C4E376 -Extended Collections -0 - -0 - -1 - - -F806D16C-4483-485A-92F1-82873FB174B0 -Assembly -0 - -0 - -1 -Abstract Data Types -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Assembly - - - - - - -D866F835-8448-4A6D-92A4-191B046C8950 -TABLE -%Type%=="TABLE" -0 - -0 - -1 - - - - - - -AB3B55F9-7776-46AF-8EC9-DAAFF6D17632 -Shared -Shared -0 - -0 - -1 - - -ExtendedAttributeTypeTargetItem -DF292A24-772A-4F0C-AFE1-3BACF551559E -Extended Attribute Types -0 - -0 - -1 - - -134E0EA7-8A8B-4B35-8885-843D1C5082E9 -AggregateFunctionTypes -0 - -0 - -The AggregateFunction types specifies the list of possible aggregate functions. -1 -Sum -Sum -Count -Min -Max -DistinctCount -None -ByAccount -AverageOfChildren -FirstChild -LastChild -FirstNonEmpty -LastNonEmpty - - -BA9680A7-CEDB-4373-B398-670A273CF030 -WithOptionList -0 - -0 - -This type contains the list of available parameters for the view extended attribute: WithOption -1 -encryption -schemabinding -view_metadata - - -66826DEE-A03D-4F03-9877-2E63F36AD065 -DimensionOptionTypes -0 - -0 - -The UNIQUE_NAME options indicates that member names are unique within the dimension. - -The UNIQUE_KEY option indicates that member keys are unique within the dimension. - -The NOTRELATEDTOFACTTABLE option indicates that non-leaf members cannot be associated to fact table data. - -The ALLOWSIBLINGSWITHSAMENAME option of the dimension determines whether children of a single member in a hierarchy can have identical names. -1 -UNIQUE_NAME -UNIQUE_KEY -NOTRELATEDTOFACTTABLE -ALLOWSIBLINGSWITHSAMENAME - - -D005EFF6-54BA-4BD2-96CB-76C0EABBA29E -KeyUsageTypes -0 - -0 - -Specifies the Key usage type. -1 -Parent -Key -Regular - - -B2536904-EE3B-4275-843F-490A59F688C1 -LevelHidesValues -0 - -0 - -The option BLANK_NAME hides a level member having an empty name. - -The option PARENT_NAME hides a level member when the member name is identical to that of its parent. - -The option ONLY_CHILD_AND_BLANK_NAME hides a level member when it is the only child of its parent and its name is null or an empty string. - -The option ONLY_CHILD_AND_PARENT_NAME hides a level member when it is the only child of its parent and its name is the same as its parent's name. -1 -ONLY_CHILD_AND_BLANK_NAME -ONLY_CHILD_AND_PARENT_NAME -BLANK_NAME -PARENT_NAME - - -C7CF65CE-BE7B-4F71-8926-8BD305B8A08C -LevelOptions -0 - -0 - -The UNIQUE option indicates that the members of a level are unique. - -The UNIQUE_NAME option indicates that the members of a level are uniquely identified by their member name column. - -The UNIQUE_KEY option indicates that the members of a level are uniquely identified by their member key column. - -The NOTRELATEDTOFACTTABLE option indicates that level members cannot be associated to fact table data. - -The SORTBYNAME option indicates that level members are ordered by their names. - -The SORTBYKEY option indicates that level members are ordered by their keys. - -The SORTBYPROPERTY options indicates that members are ordered by their property <property names>. - -1 -UNIQUE -UNIQUE_NAME -UNIQUE_KEY -NOTRELATEDTOFACTTABLE -SORTBYNAME -SORTBYKEY -SORTBYPROPERTY <property name> - - -3550FC29-4692-4494-8A76-9F7CDA832D72 -LevelRootValues -0 - -0 - -ROOT_IF_PARENT_IS_BLANK: -Only members with a null, a zero, or an empty string in their parent key column are treated as root members. - -ROOT_IF_PARENT_IS_MISSING: -Only members with parents that cannot be found are treated as root members. - -ROOT_IF_PARENT_IS_SELF: -Only members with themselves as parents are treated as root members. - -ROOT_IF_PARENT_IS_BLANK_OR_SELF_OR_MISSING: -Members are treated as root members if they meet one or more of the conditions specified by ROOT_IF_PARENT_IS_BLANK, ROOT_IF_PARENT_IS_SELF, or ROOT_IF_PARENT_IS_MISSING. -1 -ROOT_IF_PARENT_IS_BLANK -ROOT_IF_PARENT_IS_MISSING -ROOT_IF_PARENT_IS_SELF -ROOT_IF_PARENT_IS_BLANK_OR_SELF_OR_MISSING - - -A935A29D-BF98-439B-9784-D3CAD22E62A4 -LevelTypes -0 - -0 - -- ALL indicates the top (All) level of a dimension (the one that precalculates all the members of all lower levels). -- YEAR indicates that a level refers to years. It must be used in a dimension whose type is Time dimension. -- QUARTER indicates that a level refers to (calendar) quarters. It must be used in a dimension whose type is Time dimension. -- MONTH indicates that a level refers to months. Must be used in a dimension whose type is Time dimension. -- WEEK indicates that a level refers to weeks. It must be used in a dimension whose type is Time dimension. -- DAY indicates that a level refers to days. It must be used in a dimension whose type is Time dimension. -- DAYOFWEEK indicates that a level refers to days of the week. It must be used in a dimension whose type is Time dimension. -- DATE indicates that a level refers to dates. It must be used in a dimension whose type is Time dimension. -- HOUR indicates that a level refers to hours. It must be used in a dimension whose type is Time dimension. -- MINUTE indicates that a level refers to minutes. It must be used in a dimension whose type is Time dimension. -- SECOND indicates that a level refers to seconds. It must be used in a dimension whose type is Time dimension. - - - - -1 -ALL -YEAR -QUARTER -MONTH -WEEK -DAY -DAYOFWEEK -DATE -HOUR -MINUTE -SECOND - - -5DC933C1-B703-46BC-8AAA-98A21FEABC52 -MeasureFunction -0 - -0 - -SUM uses the Sum function for aggregation -MIN uses the Min function for aggregation -MAX uses the Max function for aggregation -COUNT uses the Count function for aggregation - -1 -SUM -SUM -MIN -MAX -COUNT - - -6B857E91-BAB2-4D8F-957C-164C14B025AB -OLEDBTypes -0 - -0 - -Predefined OLE DB data types. -1 -DBTYPE_I1 -DBTYPE_I2 -DBTYPE_I4 -DBTYPE_I8 -DBTYPE_UI1 -DBTYPE_UI2 -DBTYPE_UI4 -DBTYPE_UI8 -DBTYPE_R4 -DBTYPE_R8 -DBTYPE_CY -DBTYPE_DECIMAL -DBTYPE_NUMERIC -DBTYPE_DATE - - -AF095A82-12D2-4109-8372-452052FCFCAD -SubTypes -0 - -0 - -PARENT_CHILD indicates that the level is a parent-child level. -1 -PARENT_CHILD - - -E85FFCA4-F19F-4234-A253-B68729F15903 -TimeTypes -0 - -0 - -TIME indicates that a dimension refers to time (year, month, week, day, and so on). The only valid levels in a time dimension are those defined in the LevelTypes enumeration. -1 -TIME - - -525FC791-2997-4BC3-831B-0F2B8112879C -MembersWithDataTypes -0 - -0 - -The different types in MembersWithData. -1 -NonLeafDataVisible -NonLeafDataHidden -NonLeafDataVisible - - -98B0F1E4-A66F-4301-8B95-F8462088BCA2 -CubeOptions -0 - -0 - -The DEFER_DATA option causes the query to be parsed locally and executed only when necessary to retrieve data to satisfy a user request. DEFER_DATA is used to specify that a local cube be defined in the ROLAP storage mode. - -The ATTEMPT_DEFER option causes PivotTable Service to attempt to parse the query and defer data loading if successful, or, if the query cannot be parsed, to process the specified query immediately as if the PASSTHROUGH had been specified. - -The PASSTHROUGH option causes the SELECT clause to be passed directly to the source database without modification by PivotTable Service. If PASSTHROUGH is not specified, PivotTable Service parses the query and formulates a set of queries equivalent to the original that is optimized for the source database and index structures. This set of queries is often more efficient than the specified query. - -The ATTEMPT_ANALYSIS option causes PivotTable Service to attempt to parse the query and formulate an optimized set of queries. If the query cannot be parsed, PivotTable Services processes the query immediately as if the PASSTHROUGH had been specified. - - - -1 -DEFER_DATA -ATTEMPT_DEFER -PASSTHROUGH -ATTEMPT_ANALYSIS - - -175774AD-943A-428A-958E-7C5896E8787C -ProcessingModeTypes -0 - -0 - -Specifies the processing mode type. -1 -Regular -Regular - - -CD9EDE6E-8488-4417-AB1B-20E62B8BECD8 -StorageModeTypes -0 - -0 - -Specifies the Storage Mode -1 -Molap -Molap -Rolap -Holap - - -DB400F0F-5DCA-401E-9E57-52182B6CEAA0 -DataProviderList -0 - -1327671821 -mledier -Specifies a data provider list. -1 -Microsoft OLE DB Provider For Oracle -Microsoft OLE DB Provider For SQL Server -Sybase Adaptive Server Anywhere Provider 7.0 -Sybase Adaptive Server Anywhere Provider 8.0 -Sybase Adaptive Server Anywhere Provider 9.0 -Sybase ASE OLE DB Provider - - -19825013-11A2-4883-8645-F2B38919F1BB -DataProviderType -0 - -1327671821 -mledier -Specifies the data Provider type. -1 -Native Data Provider for OLE DB -.NET Framework Data Provider for Microsoft SQL Server -.NET Framework Data Provider for Oracle -Native Data Provider for OLE DB - - -70F229DB-E21D-4E8B-A027-3990C9AD0A6A -AuthenticationTypeType -0 - -0 - -Specifies the SQL Server authentication type. -1 -Use SQL Server Authentication -Use Windows Authentication -Use SQL Server Authentication - - -C62D4E73-82AE-4295-9E67-C5EBBE0F45A2 -FormatStringTypes -0 - -0 - -Specify the Format of a Fact Measure -1 -Percent -Short Time -Short Date -Currency -Standard -#,#.0 -#,#.00 -#,# -# - - -AA2EC094-0239-4016-96A0-E51BB69B73BA -DimensionTypes -0 - -0 - -Specifies the dimension type. -1 -Regular -Regular -Time -Geography -Organization -BillOfMaterials -Accounts -Customers -Products -Scenario -Quantitative -Utility -Currency -Rates -Channel -Promotion - - -7E98A3E4-535F-4BA1-B1CB-BF41F2E936BA -DimensionAttributeTypes -0 - -0 - -Specifies the dimension attribute type. -1 -Regular -Account -AccountName -AccountNumber -AccountType -Address -AddressBuilding -AddressCity -AddressCountry -AddressFax -AddressFloor -AddressHouse -AddressPhone -AddressQuarter -AddressRoom -AddressStateOrProvince -AddressStreet -AddressZip -BomResource -Caption -CaptionAbbreviation -CaptionDescription -Channel -City -Company -Continent -Country -County -CurrencyDestination -CurrencyIsoCode -CurrencyName -CurrencySource -CustomerGroup -CustomerHousehold -Customers -Date -DateCanceled -DateDuration -DateEnded -DateModified -DateStart -DayOfHalfYears -DayOfMonth -DayOfQuarter -DayOfTenDays -DayOfTrimester -DayOfWeek -DayOfYear -Days -DeletedFlag -FiscalDate -FiscalDayOfHalfYears -FiscalDayOfMonth -FiscalDayOfQuarter -FiscalDayOfTrimester -FiscalDayOfWeek -FiscalDayOfYear -FiscalHalfYears -FiscalHalfYearOfYear -FiscalMonths -FiscalMonthOfHalfYear -FiscalMonthOfQuarter -FiscalMonthOfTrimester -FiscalMonthOfYear -FiscalQuarters -FiscalQuarterOfHalfYear -FiscalQuarterOfYear -FiscalTrimesters -FiscalTrimesterOfYear -FiscalWeeks -FiscalWeekOfHalfYear -FiscalWeekOfMonth -FiscalWeekOfQuarter -FiscalWeekOfTrimester -FiscalWeekOfYear -FiscalYears -FormattingColor -FormattingFont -FormattingFontEffects -FormattingFontSize -FormattingOrder -FormattingSubtotal -GeoBoundaryBottom -GeoBoundaryFront -GeoBoundaryLeft -GeoBoundaryPolygon -GeoBoundaryRear -GeoBoundaryRight -GeoBoundaryTop -GeoCentroidX -GeoCentroidY -GeoCentroidZ -HalfYears -HalfYearOfYear -Hours -ID -Image -ImageBmp -ImageGif -ImageJpg -ImagePng -ImageTiff -IsHoliday -ISO8601Date -ISO8601DayOfWeek -ISO8601DayOfYear -ISO8601Weeks -ISO8601WeekOfYear -ISO8601Years -IsPeakDay -IsWeekDay -IsWorkingDay -ManufacturingDate -ManufacturingDayOfHalfYear -ManufacturingDayOfMonth -ManufacturingDayOfQuarter -ManufacturingDayOfWeek -ManufacturingDayOfYear -ManufacturingHalfYears -ManufacturingHalfYearOfYear -ManufacturingMonths -ManufacturingMonthOfHalfYear -ManufacturingMonthOfQuarter -ManufacturingMonthOfYear -ManufacturingQuarters -ManufacturingQuarterOfHalfYear -ManufacturingQuarterOfYear -ManufacturingWeeks -ManufacturingWeekOfHalfYear -ManufacturingWeekOfMonth -ManufacturingWeekOfQuarter -ManufacturingWeekOfYear -ManufacturingYears -Minutes -Months -MonthOfHalfYear -MonthOfQuarter -MonthOfTrimester -MonthOfYear -OrganizationalUnit -OrgTitle -PercentOwnership -PercentVoteRight -Person -PersonContact -PersonDemographic -PersonFirstName -PersonFullName -PersonLastName -PersonMiddleName -PhysicalColor -PhysicalDensity -PhysicalDepth -PhysicalHeight -PhysicalSize -PhysicalVolume -PhysicalWeight -PhysicalWidth -Point -PostalCode -Product -ProductBrand -ProductCategory -ProductGroup -ProductSKU -ProjectCode -Projectcompletion -ProjectEnddate -ProjectName -ProjectStartDate -Promotion -QtyRangeHigh -QtyRangeLow -Quantitative -QuarterOfHalfYear -QuarterOfYear -Quarters -Rate -RateType -Region -Regular -RelationToParent -ReportingDate -ReportingDayOfHalfYear -ReportingDayOfMonth -ReportingDayOfQuarter -ReportingDayOfTrimester -ReportingDayOfWeek -ReportingDayOfYear -ReportingHalfYears -ReportingHalfYearOfYear -ReportingMonths -ReportingMonthOfHalfYear -ReportingMonthOfQuarter -ReportingMonthOfTrimester -ReportingMonthOfYear -ReportingQuarters -ReportingQuarterOfHalfYear -ReportingQuarterOfYear -ReportingTrimesters -ReportingTrimesterOfYear -ReportingWeeks -ReportingWeekOfHalfYear -ReportingWeekOfMonth -ReportingWeekOfQuarter -ReportingWeekOfTrimester -ReportingWeekOfYear -ReportingYears -Representative -ScdEndDate -ScdOriginalID -ScdStartDate -ScdStatus -Scenario -Seconds -Sequence -ShortCaption -StateOrProvince -TenDays -TenDayOfHalfYear -TenDayOfQuarter -TenDayOfTrimester -TenDayOfYear -TenDayOfMonth -Trimesters -TrimesterOfYear -UndefinedTime -Utility -Version -WebHtml -WebMailAlias -WebUrl -WebXmlOrXsl -WeekOfYear -Weeks -WinterSummerSeason -Years - - -2A3F8E04-C259-4EE2-8DFD-6570673D5E50 -UsageTypes -0 - -0 - -Specifies the usage type. -1 -Regular -Parent -Key -Regular - - -9C00D00E-36E7-4036-A510-2350CAE85949 -RootMemberTypes -0 - -0 - -Specifies the rootMember type. -1 -ParentIsBlankSelfOrMissing -ParentIsBlank -ParentIsBlankSelfOrMissing -ParentIsSelf -ParentIsMissing - - -D3C45057-6E44-4E68-80D6-443513C77AA5 -OrderByTypes -0 - -0 - -Specifies the OrderBy element type. -1 -Name -Name -Key -AttributeKey -AttributeName - - -5E4B75AC-E6D2-4CA2-9002-3A7C49F3D0DB -DimensionStorageModeTypes -0 - -0 - -Specifies the dimension's storage mode. -1 -Molap -Molap -Rolap - - -6F97EEA4-CDFA-4D9F-B02D-F8F6BAA3E53D -BindingTypes -0 - -0 - -Specifies the Binding type. -1 -Column Binding -Row Binding -Column Binding - - - - -TemplateTargetItem -BC3CEAF7-21F3-4148-B480-47C697531DA9 -Templates -0 - -0 - -1 - - -DA8F23F1-AA4F-49BD-92B2-515CCB73D30B -usedb -.if(%.L:@1%==master) -use master -go -.elsif(%*@1%) -use %*@1.Code% -go -.endif("\n") -0 - -0 - -1 - - - - - - -30AE15E6-19AF-4271-8CBE-7C54B0F5059C -Table -0 - -0 - -1 -1 - - -CustomCheckTargetItem -66D9933F-E8D7-4A06-ABD1-9B5D8D2B45DF -Custom Checks -0 - -0 - -1 - - -32413ECA-2A63-489A-976E-4C0315586D45 -Table Timestamp Uniqueness -0 - -0 - -1 -This check ensures that a table has no more than one timestamp column. -The following tables have more than one timestamp column: -Function %Check%(obj) - dim pCol, bTSFound - bTSFound = false - for each pCol in obj.Columns - if GetDatatypeName(pCol.datatype) = "timestamp" then - if (bTSFound) then - %Check% = false - exit function - end if - bTSFound = true - end if - next - %Check% = True -End Function -Function %Fix%(obj, outmsg) - ' Implement your automatic correction on <obj> here - ' filling <outmsg> as you wish - ' and return True if successful. - - outmsg = "Automatic correction not implemented" - - %Fix% = False -End Function - - - - -ExtendedAttributeTargetItem -CD730944-2C23-494C-BA3E-1443EA79EF6D -Extended Attributes -0 - -0 - -1 - - -4E4DBB32-1050-467A-BF37-AAB7473F37B4 -ExtCktNotForReplication -0 - -0 - -Specifies that "NOT FOR REPLICATION" keywords are used to prevent the TABLE CHECK constraint from being enforced during the distribution process used by replication. -1 -false -0 - - -694ED839-DC5E-42AF-AA59-566C9D8EB8F5 -PartitionedTable -0 - -0 - -Set table partitioned state. -1 -1 -Function %Get%(obj) - %Get% = (obj.GetPhysicalOptionSql("on/<filegroup>") = "") -End Function -Sub %Set%(obj, value) - if (value) then - obj.DeletePhysicalOption "on/<filegroup>" - obj.DeletePhysicalOption "filestream_on/<filegroup>" - obj.SetPhysicalOptionSql "on/<partition scheme>", "? (?)" - else - obj.DeletePhysicalOption "on/<partition scheme>" - obj.SetPhysicalOptionSql "on/<filegroup>", "'default'" - end if - -End Sub -0 - - - - -FormTargetItem -BAC683C2-C45E-4E00-B917-FD2E9F4D1E5F -Forms -0 - -0 - -1 - - -65AD4373-6E72-4111-88A3-D83E7245FC83 -Microsoft -<Form > - <ExtendedAttribute Attribute="ExtCktNotForReplication" Caption="Do not validate check constraints during replication" AttributeID="{4E4DBB32-1050-467A-BF37-AAB7473F37B4}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="PartitionedTable" Caption="Table is partitioned" AttributeID="{694ED839-DC5E-42AF-AA59-566C9D8EB8F5}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <GroupBox Name="GroupBox1" Caption="File groups" ControlAsLabel="No" > - <HorizontalLayout Name="HorizontalLayout1" > - <ExtendedAttribute Attribute="PartitionScheme" Caption="Partition scheme:" AttributeID="{8CD50E8C-B529-4E35-9B5B-9599C3DBEE67}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="PartitionSchemeColumn" Caption="Column:" AttributeID="{0F51F0F4-7057-431A-B185-8B8E735E10A8}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - </HorizontalLayout> - <HorizontalLayout Name="HorizontalLayout2" > - <ExtendedAttribute Attribute="FilestreamPartitionScheme" Caption="Filestream partition scheme" AttributeID="{75545940-79FE-405E-9A5B-264CC265A9E3}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="FilestreamPartitionSchemeColumn" Caption="Column" AttributeID="{FAC5F627-E394-4C12-B156-A7751A2DD5CE}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - </HorizontalLayout> - <ExtendedAttribute Attribute="FileGroup" Caption="Filegroup:" AttributeID="{E5A5D032-309F-449E-9D28-F98688FC0036}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="TextImageOn" Caption="Text/Image" AttributeID="{A32B80C1-C595-4DD4-A3B7-F753AF5517C0}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="FilestreamOnFilegroup" Caption="Filestream" AttributeID="{6CA0347D-424F-49E9-9383-F2F84823ABE1}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" LeftText="No" /> - </GroupBox> - <ExtendedAttribute Attribute="DataCompression" Caption="Compression" AttributeID="{0F6DF697-C183-4C09-BD02-64907C8BFF74}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" LeftText="No" /> - <ExtendedAttribute Attribute="TableCompression" Caption="Compression" AttributeID="{3F028C9E-269E-464F-82CD-D4FBE0A5DEA3}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> -</Form> - -0 - -1276605429 -obalen -1 -24917 - - - - -TemplateTargetItem -9D61F1C3-9CE8-49D5-84F4-361921820EC9 -Templates -0 - -1269267132 -mledier -1 - - -2EFCDC10-647F-4134-AEE6-5DB6B5339473 -dropColumnDefaultConstraint -.foreach_item(Columns) -%dropConstraintEx% -.next("\n") -1268403311 -mledier -1269267132 -mledier -1 - - -272A4748-6B5A-40A6-BECC-6FD89C4C77C2 -partitionSchemeColumnList -List -.foreach_item(Columns) -%ShortGeneratedName% -.next(\n) -0 - -0 - -1 - - -6EF9D975-B288-4B96-AFE6-285888B58E7C -partitionSchemeList -List -.foreach_item(Package.ExtendedObjects,,,%Stereotype%==PartitionScheme) -%PARTITIONSCHEME% -.next(\n) -0 - -0 - -1 - - -3FDD86F7-7B46-424F-B35A-2239B39643AA -fileGroupList -List -DEFAULT\n -.foreach_item(Model.Storages) -%Code% \t %GeneratedName% -.next(\n) -0 - -0 - -Enumerates filegroups -1 - - - - -EventHandlerTargetItem -015E472A-F532-4627-A18C-C0D98B108C44 -Event Handlers -0 - -0 - -1 - - -B205332C-C685-4949-915D-4A0E4F7E6A7A -Validate -0 - -0 - -1 -Function %Validate%(obj, ByRef message) - - Dim sFileGroup - Dim sPartitionScheme - Dim bValidate - - bValidate = True - - sFileGroup = obj.GetExtendedAttribute("FileGroup") - sPartitionScheme = obj.GetExtendedAttribute("PartitionScheme") - If ((sFileGroup <> "") And (sPartitionScheme <> "")) Then - message = "You cannot specify a file group and and a partition scheme simultaneously!" - bValidate = False - Else - bValidate = True - End If - - If (bValidate = True) Then - - - Dim depd, depdObj - - ' Compute new dependent object list - Dim DependentObjects - set DependentObjects = CreateObject("Scripting.Dictionary") - - If (sPartitionScheme <> "") Then - Dim pExtendedObject - For each pExtendedObject in obj.Package.ExtendedObjects - If (pExtendedObject.Stereotype = "PartitionScheme") Then - Dim sGeneratedName - sGeneratedName = pExtendedObject.EvaluateText("%PARTITIONSCHEME%") - If (sGeneratedName = sPartitionScheme) Then - DependentObjects.Add pExtendedObject.ObjectID, pExtendedObject - End If - End If - Next - End If - - ' Remove old dependencies - Dim DependenciesToDelete - set DependenciesToDelete = CreateObject("Scripting.Dictionary") - - For each depd in obj.ExtendedDependencies - If (depd.Stereotype = "DBCreateAfter (Computed)") Then - If (DependentObjects.Exists(depd.InfluentObject.ObjectID)) Then - DependentObjects.Remove depd.InfluentObject.ObjectID - Else - DependenciesToDelete.Add depd.ObjectID, depd - End If - End If - Next - - Dim i, col - col = DependenciesToDelete.Items - For i = 0 To DependenciesToDelete.Count -1 'Iterate the array - set depd = col(i) - if not (depd is nothing) then depd.Delete - Next - - - ' Add new dependencies - col = DependentObjects.Items - For i = 0 To DependentObjects.Count -1 'Iterate the array - set depdObj = col(i) - set depd = obj.Package.ChildExtendedDependencies.CreateNew - If not (depd is nothing) Then - set depd.DependentObject = obj - set depd.InfluentObject = depdObj - depd.Stereotype = "DBCreateAfter (Computed)" - End If - Next - - End If - - - %Validate% = bValidate - -End Function - - - - -CriterionTargetItem -F9653B01-4095-4F78-9A18-A322B0B99AC5 -Criteria -0 - -0 - -1 - - -41FD51D7-BBD3-48FE-9021-063773F91BB9 -PartitionedTable -%PartitionedTable%==true -0 - -0 - -Partitioned table. -1 - - -ExtendedAttributeTargetItem -1BBE4383-55E1-4A35-BD6D-0B4F16BC951F -Extended Attributes -0 - -0 - -1 - - -8CD50E8C-B529-4E35-9B5B-9599C3DBEE67 -PartitionScheme -0 - -0 - -Name of the partition scheme -1 -12 -%partitionSchemeList% -1 -Function %Get%(obj) - %Get% = obj.GetPhysicalOptionValue("on/<partition scheme>") -End Function -Sub %Set%(obj, value) - If ((value <> "") and (obj.HasPhysicalOption("on/<partition scheme>/<column>") = false)) Then - obj.SetPhysicalOptionValue "on/<partition scheme>/<column>", "?" - End If - obj.SetPhysicalOptionValue "on/<partition scheme>", value - if (value <> "") then - obj.DeletePhysicalOption "on/<filegroup>" - end if -End Sub -0 - - -0F51F0F4-7057-431A-B185-8B8E735E10A8 -PartitionSchemeColumn -0 - -0 - -Partitioned column -1 -12 -%partitionSchemeColumnList% -1 -Function %Get%(obj) - %Get% = obj.GetPhysicalOptionValue("on/<partition scheme>/<column>") -End Function -Sub %Set%(obj, value) - obj.SetPhysicalOptionValue "on/<partition scheme>/<column>", value -End Sub -0 - - -0F6DF697-C183-4C09-BD02-64907C8BFF74 -DataCompression -0 - -0 - -List the partitions that use specified compression -1 -100 -1 -with/data_compression = -0 - - -75545940-79FE-405E-9A5B-264CC265A9E3 -FilestreamPartitionScheme -0 - -0 - -Name of the partition scheme -1 -12 -%partitionSchemeList% -1 -Function %Get%(obj) - %Get% = obj.GetPhysicalOptionValue("filestream_on/<partition scheme>") -End Function -Sub %Set%(obj, value) - If ((value <> "") and (obj.HasPhysicalOption("filestream_on/<partition scheme>/<column>") = false)) Then - obj.SetPhysicalOptionValue "filestream_on/<partition scheme>/<column>", "?" - End If - obj.SetPhysicalOptionValue "filestream_on/<partition scheme>", value - if (value <> "") then - obj.DeletePhysicalOption "filestream_on/<filegroup>" - end if -End Sub -0 - - -FAC5F627-E394-4C12-B156-A7751A2DD5CE -FilestreamPartitionSchemeColumn -0 - -0 - -Partitioned column -1 -12 -%partitionSchemeColumnList% -1 -Function %Get%(obj) - %Get% = obj.GetPhysicalOptionValue("filestream_on/<partition scheme>/<column>") -End Function -Sub %Set%(obj, value) - obj.SetPhysicalOptionValue "filestream_on/<partition scheme>/<column>", value -End Sub -0 - - - - - - -21E0A2B0-EC9D-4A84-B600-F7517E8B7D8B -NotPartitionedTable -%PartitionedTable%==false -0 - -0 - -Table is not partitioned -1 - - -ExtendedAttributeTargetItem -66AEB531-0781-4AA2-9EA0-9DAD7D250C7E -Extended Attributes -0 - -0 - -1 - - -E5A5D032-309F-449E-9D28-F98688FC0036 -FileGroup -0 - -0 - -Name of the filegroup -1 -12 -%fileGroupList% -1 -Function %Get%(obj) - %Get% = obj.GetPhysicalOptionValue("on/<filegroup>") -End Function -Sub %Set%(obj, value) - obj.SetPhysicalOptionValue "on/<filegroup>", value - if (value <> "") then - obj.DeletePhysicalOption "on/<partition scheme>" - end if -End Sub -0 - - -6CA0347D-424F-49E9-9383-F2F84823ABE1 -FilestreamOnFilegroup -0 - -0 - -Name of the filegroup used for filestream -1 -100 -1 -filestream_on/<filegroup> -0 - - -A32B80C1-C595-4DD4-A3B7-F753AF5517C0 -TextImageOn -0 - -0 - -Name of the filegroup where text and image are stored -1 -12 -%fileGroupList% -1 -Function %Get%(obj) -%Get% = obj.GetPhysicalOptionValue("textimage_on") -End Function -Sub %Set%(obj, value) -obj.SetPhysicalOptionValue "textimage_on", value -End Sub -0 - - -3F028C9E-269E-464F-82CD-D4FBE0A5DEA3 -TableCompression -0 - -0 - -The compression type of the table (none, row or page) -1 -12 -none;row;page -1 -Function %Get%(obj) - %Get% = obj.GetPhysicalOptionValue("with/data_compression =") -End Function -Sub %Set%(obj, value) - obj.SetPhysicalOptionSql "with/data_compression =", value -End Sub -1 -0 - - - - - - - - - - -4CB68E78-F32F-41F7-8FB0-A8A5FB2B0780 -BaseTrigger -0 - -1269267132 -mledier -1 - - -CriterionTargetItem -878F6741-9134-4D11-83DC-38000EE9F44E -Criteria -0 - -1269267132 -mledier -1 - - -562B2E17-0F6B-4B38-852B-BA08567A3829 -isCLR -%ImplementationType% == "ClassMethod" -0 - -1269267132 -mledier -1 - - -ExtendedAttributeTargetItem -AE0BD8EC-B530-42B5-973C-D48AE7BC552F -Extended Attributes -0 - -1269267132 -mledier -1 - - -1FF6DC9F-B368-4758-B90F-9A59B91DF129 -Assembly -0 - -1269267132 -mledier -Specifies the assembly to bind with the trigger. -1 -15 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Assembly -0 - - - - - - - -ExtendedCollectionTargetItem -6D3585B9-A048-4673-A7A0-369CD018C8BF -Extended Collections -0 - -1269267132 -mledier -1 - - -09014470-F6A9-49E6-879D-DFBE01E51164 -Assembly -0 - -1269267132 -mledier -1 -Triggers -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Assembly - - - - - - - - - - - -ExtendedAttributeTargetItem -3C8C1FBA-9C11-487E-BBA1-B969685B0062 -Extended Attributes -0 - -1269267132 -mledier -1 - - -75BF97EE-5760-4AB9-B5A7-76F2EA30A2B1 -Option -0 - -1269267132 -mledier -Trigger option is the concatenation of "ENCRYPTION" and "EXECUTE AS" options. - -Encryption allows to obfuscate the text of the CREATE TRIGGER statement. -Using WITH ENCRYPTION prevents the trigger from being published. -Note that WITH ENCRYPTION cannot be specified for CLR triggers. - -EXECUTE AS specifies the security context under which the trigger is executed. -1 -14 -0 - - - - -FormTargetItem -74B77ADE-08DC-4A1F-8D26-9A6DF3B7F62C -Forms -0 - -1269267132 -mledier -1 - - -C167DD23-5D2C-430A-AB8D-BB830ABEFEFB -Microsoft -<Form > - <ExtendedAttribute Attribute="Option" Caption="Option" AttributeID="{75BF97EE-5760-4AB9-B5A7-76F2EA30A2B1}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" /> - <ExtendedAttribute Attribute="Assembly" Caption="Assembly" AttributeID="{1FF6DC9F-B368-4758-B90F-9A59B91DF129}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> -</Form> - -0 - -1276605429 -obalen -1 -24917 - - - - - - -BE559A7F-12B4-4F40-9685-8D44C2BC8BF2 -Column -0 - -1252675865 -mledier -1 - - -CustomCheckTargetItem -96A31915-97E8-4A37-B797-15662EC6AD0D -Custom Checks -0 - -0 - -1 - - -6C9F00DF-08DF-4128-9BDB-808CE0370770 -Identity Seed and Increment validity -0 - -0 - -1 -This check ensures that the values given to the extended attribute containing Seed and Increment values of identity column are correct. -The following identity columns have inconsistent Seed or Increment value: -Function %Check%(obj) - if (obj.Identity) then - dim SeedInc - SeedInc = CStr(obj.GetExtendedAttribute("ExtIdentitySeedInc")) - if (SeedInc <> "") then - Dim Pos - Pos = InStr(SeedInc, ",") - if (Pos = 0) then ' need both increment and seed - %Check% = False - exit function - end if - if not IsNumeric(left(SeedInc, Pos - 1)) then ' Seed value is not numeric - %Check% = False - exit function - end if - if not IsNumeric(mid(SeedInc, Pos + 1)) then ' Increment value is not numeric - %Check% = False - exit function - end if - end if - end if - %Check% = True -End Function -Function %Fix%(obj, outmsg) - ' Implement your automatic correction on <obj> here - ' filling <outmsg> as you wish - ' and return True if successful. - - outmsg = "Automatic correction not implemented" - - %Fix% = False -End Function - - - - -ExtendedAttributeTargetItem -668286ED-96A8-4E0D-A441-DCA4F810EFBB -Extended Attributes -0 - -0 - -1 - - -8A61EC6A-CBA0-4D6B-BAB7-0A9B8AB1D692 -ExtIdentitySeedInc -0 - -0 - -This attribute is composed of two integer parts, separated with a comma. -First part is the value to be assigned to the first row in the table (Seed). -Second part is the increment to add to the seed value for successive rows in the table. - -1 -12 -0 - - -83084FA2-787D-4128-8BE1-F8DCAB2BA108 -ExtDeftConstName -0 - -0 - -Contains the name of the constraint that is used to apply a default value to the column. If empty, the "constraint" keyword is not generated. -1 -12 -0 - - -CD30938A-589C-477E-9305-F22BB4148A80 -ExtCkcNotForReplication -0 - -0 - -Specifies that "NOT FOR REPLICATION" keywords are used to prevent the CHECK constraint from being enforced during the distribution process used by replication. -1 -false -0 - - -96271E6C-7734-4312-890E-03C690A55C56 -ExtIdtNotForReplication -0 - -0 - -Indicates that the IDENTITY property should not be enforced when a replication login inserts data into the table. -1 -false -0 - - -988BDD6B-9B51-4662-8D05-EA39DF924C90 -ExtNullConstName -0 - -0 - -Contains the name of the constraint that is used to apply a mandatory property of the column. If empty, the "constraint" keyword is not generated. -1 -12 -0 - - -391A2D11-C8B9-409A-B2A2-CCB677DED33D -ExtCollation -0 - -0 - -A single string that specifies the collation name for a SQL collation. -1 -12 -0 - - -9A56C910-ECA6-4A55-ACCF-61570BB75A6E -ExtRowGuidCol -0 - -0 - -Indicates that the new column is a row global unique identifier column. Only one unique identifier column per table can be designated as the ROWGUIDCOL column. -1 -false -0 - - - - -FormTargetItem -886675BD-FDB2-444D-9982-63E4B5DE4F02 -Forms -0 - -0 - -1 - - -C71B1BCC-E20E-4025-B225-976C96FC1AD3 -Microsoft -<Form > - <HorizontalLayout Name="HorizontalLayout1" > - <ExtendedAttribute Attribute="ExtRowGuidCol" Caption="Row global unique identifier" AttributeID="{9A56C910-ECA6-4A55-ACCF-61570BB75A6E}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="Sparse" Caption="Sparse" AttributeID="{67D402C1-C90F-48BB-9C42-940AC5B6776D}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="FileStream" Caption="Filestream" AttributeID="{97818B1A-41D0-4490-9F59-7237523CB6CD}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" Display="Checkbox" LeftText="No" /> - </HorizontalLayout> - <ExtendedAttribute Attribute="ExtCkcNotForReplication" Caption="Do not validate check constraints during replication" AttributeID="{CD30938A-589C-477E-9305-F22BB4148A80}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="ExtDeftConstName" Caption="Default constraint name" AttributeID="{83084FA2-787D-4128-8BE1-F8DCAB2BA108}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="ExtNullConstName" Caption="Not null constraint name" AttributeID="{988BDD6B-9B51-4662-8D05-EA39DF924C90}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="ExtCollation" Caption="Collation name" AttributeID="{391A2D11-C8B9-409A-B2A2-CCB677DED33D}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <GroupBox Name="Identity" Caption="Identity column" ControlAsLabel="No" > - <ExtendedAttribute Attribute="ExtIdentitySeedInc" Caption="Identity seed and increment" AttributeID="{8A61EC6A-CBA0-4D6B-BAB7-0A9B8AB1D692}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="ExtIdtNotForReplication" Caption="Identity value not replicated" AttributeID="{96271E6C-7734-4312-890E-03C690A55C56}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" Display="Checkbox" LeftText="No" /> - </GroupBox> - <ExtendedAttribute Attribute="XMLSchemaCollection" Caption="XML schema collection:" AttributeID="{2B232CDD-2A2A-46A6-BBB1-3E19B8D30CC1}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="ContentType" Caption="Content type:" AttributeID="{51D65BC0-8625-4E8F-9073-314BFFAE7E1F}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" Display="Combobox" LeftText="false" /> -</Form> - -0 - -1276605429 -obalen -1 -24917 - - - - -CriterionTargetItem -BA4495AD-65DB-4254-AC0E-B273E213755B -Criteria -0 - -0 - -1 - - -FEBCCF10-3EF4-424B-B69B-2EA6902A4D84 -HasXMLDatatype -%.U:DataType% == XML -0 - -0 - -1 - - -ExtendedAttributeTargetItem -069B9439-251B-4C8C-8C96-C79EE2C2E2F9 -Extended Attributes -0 - -0 - -1 - - -2B232CDD-2A2A-46A6-BBB1-3E19B8D30CC1 -XMLSchemaCollection -0 - -0 - -Applies only to the xml data type for associating an XML schema collection with the type -1 -15 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -XMLSchemaCollection -0 - - - - - - - -ExtendedCollectionTargetItem -CBC1FB6B-970E-43D3-B49D-88033F37D362 -Extended Collections -0 - -0 - -1 - - -E8DCECA9-7BA6-4C01-AE6E-481FC35034F8 -XMLSchemaCollection -0 - -0 - -1 -XMLColumns -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -XMLSchemaCollection - - - - -TemplateTargetItem -65C8C1FD-680A-43ED-B6A6-01CC59324CF2 -Templates -0 - -0 - -1 - - -67C0C3B6-B706-4285-A29E-59E6AB5E4B51 -hasXMLSchemaCollection -.bool(%XMLSchemaCollection%) -0 - -0 - -1 - - - - -CriterionTargetItem -A06B0DC8-8862-4297-BB60-BAA82F30AAAA -Criteria -0 - -0 - -1 - - -6E9E7AF8-09F8-4CEB-8B6F-6CBDAD285CEC -HasXMLSchemaCollection -%hasXMLSchemaCollection% -0 - -0 - -1 - - - - - -ExtendedAttributeTargetItem -08C78732-8F85-49C8-98E2-57F01854E3EE -Extended Attributes -0 - -0 - -1 - - -51D65BC0-8625-4E8F-9073-314BFFAE7E1F -ContentType -0 - -0 - -- CONTENT: -Specifies that each instance of the xml data type in column_name can contain multiple top-level elements. CONTENT applies only to the xml data type and can be specified only if xml_schema_collection is also specified. If not specified, CONTENT is the default behavior. - -- DOCUMENT: -Specifies that each instance of the xml data type in column_name can contain only one top-level element. DOCUMENT applies only to the xml data type and can be specified only if xml_schema_collection is also specified. - -1 -12 -CONTENT;DOCUMENT -1 -0 - - - - - - - - - - -7E74AD78-26F9-46D3-A065-B084A609A168 -Mandatory -(%Mandatory%==false) -0 - -0 - -1 - - -ExtendedAttributeTargetItem -1BB590AF-DB61-4237-BA79-9B90B4FC663F -Extended Attributes -0 - -0 - -1 - - -67D402C1-C90F-48BB-9C42-940AC5B6776D -Sparse -0 - -0 - -Indicates that the column is a sparse column. The storage of sparse columns is optimized for null values. Sparse columns cannot be designated as NOT NULL. -1 -Sparse -0 - - - - - - -DA7C11BB-DDA3-4E93-AE1E-CC8A7E6D6B90 -AllowFilestream -%allowFilestream% -0 - -1252676200 -mledier -1 - - -ExtendedAttributeTargetItem -C244F39F-2A7D-421A-95C4-21FBA30DA078 -Extended Attributes -0 - -0 - -1 - - -97818B1A-41D0-4490-9F59-7237523CB6CD -FileStream -0 - -0 - -When the FILESTREAM storage attribute is specified for a column, all values for that column are stored in a FILESTREAM data container on the file system. -1 -Filestream -0 - - - - - - - - -TemplateTargetItem -BC94A661-7E17-4638-ABCB-60E4F00014EC -Templates -1252675687 -mledier -1269267132 -mledier -1 - - -6DB9773F-CAF8-45D9-9843-503FF410FF1E -dropConstraintEx -.if(%ExtNullConstName%) -alter table [%QUALIFIER%]%TABLE% - drop constraint %ExtNullConstName% -.endif("\ngo\n") -.if(%ExtDeftConstName%) -alter table [%QUALIFIER%]%TABLE% - drop constraint %ExtDeftConstName% -.endif("\ngo\n") - -1269266918 -mledier -1269267132 -mledier -1 - - -F111C284-14F1-46BC-8EEC-344DDC04B2A0 -allowFilestream -.if(%.L:DataType%=="varbinary(max)") -true -.elsif(%.L:DataType%=="binary varying(max)") -true -.else -false -.endif -1252675687 -mledier -1252676262 -mledier -1 - - - - - - -4A064CCC-2DF5-4362-8680-806E5EC0BC24 -CubeDimensionAssociation -0 - -0 - -1 - - -TemplateTargetItem -4AC34048-8A3A-4529-A893-E713A33A5872 -Templates -0 - -0 - -1 - - -B355276A-6CD2-42CF-A624-517C81C03836 -CubeDimensionAssocationDef -%Dimension.DimensionDef%,\n -.if (%Hierarchy%) - %Hierarchy.HierarchyAllLevelDef% -.elsif (%Dimension.DimensionHierarchies.IsEmpty% == false) - .foreach_item(Dimension.DimensionHierarchies,, \n) - %HierarchyDef%, - %HierarchyAllLevelDef% - .next(",\n") -.else - %Dimension.AllLevelDef% -.endif -0 - -0 - -1 - - - - - - -254D0EA4-556C-402F-80E3-2E5C80727634 -DBMSTrigger -0 - -1269267132 -mledier -1 - - -CustomCheckTargetItem -05FF709E-F9B9-406D-9E29-0A53D6C64AD2 -Custom Checks -0 - -1269267132 -mledier -1 - - -70B91744-234D-44B2-AC1C-EEC6D8AAF820 -Logon must be unique trigger event -0 - -1269267132 -mledier -Triggers associated with Logon event have not to be associated to any other event -1 -This check ensures that DBMS triggers that fire on a "Logon" event are not associated with any other event. -The following "Logon" triggers are associated with other events: -Function %Check%(obj) - %Check% = True - dim s - s = UCase(obj.Scope) - if (s = "ALL SERVER") then - dim e - e = Trim(UCase(obj.Event)) - if ((not (e = "LOGON")) and (InStr(e, "LOGON") > 0)) then - %Check% = False - end if - end if -End Function -Function %Fix%(obj, outmsg) - ' Implement your automatic correction on <obj> here - ' filling <outmsg> as you wish - ' and return True if successful. - - outmsg = "Automatic correction not implemented" - - %Fix% = False -End Function - - - - - - -5108B737-C289-4609-8B40-AED8BFCC3DC2 -FactMeasure -0 - -0 - -1 - - -ExtendedAttributeTargetItem -2D2E49D6-E3A6-4746-9BDC-62477D94C296 -Extended Attributes -0 - -0 - -1 - - -34366820-B9A1-4D92-AB92-F9B7F8CCC6A4 -AggregateFunction -0 - -0 - -Defines the common prefix to be used for aggregation names throughout the associated parent element. -1 --1 -Sum -1 -0 - - - - - -7D765419-F606-4375-9AE2-80318DA89433 -FormatString -0 - -0 - -Describes the display format for a CalculationProperty or a Measure element. -1 --1 -0 - - - - - -8C2D0A37-6114-4316-84B7-7CA8B5CEC575 -Visible -0 - -0 - -Determines the visibility of the Fact Measure. -1 -true -0 - - -CCB2B980-2593-4F1E-8646-6FECF340B691 -BindingType -0 - -0 - -Defines the binding type for the measure. -1 --1 -Column Binding -0 - - - - - - - -TemplateTargetItem -063D4277-31B2-458A-A288-B009BFCEAFEA -Templates -0 - -0 - -1 - - -63C78BC2-D3AE-4C9A-A4DC-C0D61CB6F0B5 -MeasureDef -MEASURE %GeneratedName% -0 - -0 - -1 - - -4C8E87A7-0276-428F-84CD-95D0BFBA36F7 -CommandDef -COMMAND ( CREATE MEMBER \[%CubeName%\].\[Measures\].\[%GeneratedName%\] as %.q:formula% -.if (%IsHidden%) -, VISIBLE = 1 -.endif -.if (%Format%) -, FORMAT_STRING = %Format% -.endif -.if (%SolveOrder%) -, SOLVEORDER = %SolveOrder% -.endif -) -0 - -0 - -1 - - -80B38408-2EDC-4821-8C1F-161275064FB3 -formula -%FormulaExpression% -0 - -0 - -1 - - - - -FormTargetItem -87EBF6BD-9C20-4BD5-A3E1-E0FC38102EE7 -Forms -0 - -0 - -1 - - -C0CDEA80-0EC5-46A5-BDDD-2104637F7898 -Microsoft -<Form > - <ExtendedAttribute Attribute="AggregateFunction" Caption="AggregateFunction" AttributeID="{34366820-B9A1-4D92-AB92-F9B7F8CCC6A4}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="BindingType" Caption="BindingType" AttributeID="{CCB2B980-2593-4F1E-8646-6FECF340B691}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="Visible" Caption="Visible" AttributeID="{8C2D0A37-6114-4316-84B7-7CA8B5CEC575}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="FormatString" Caption="FormatString" AttributeID="{7D765419-F606-4375-9AE2-80318DA89433}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> -</Form> - -0 - -1276605429 -obalen -1 -24917 - - - - - - -BF11CAF2-A541-445B-AF1E-1FDEC14F4A06 -Model -0 - -1285594912 -psitja -1 - - -CustomCheckTargetItem -7769257F-13AD-4290-BCE3-A51AB35A7D45 -Custom Checks -0 - -0 - -1 - - -FA2A91F9-4397-4E3F-BED5-D95FE678534F -Storage requires a database. -0 - -0 - -This check ensures that the model has a defined database in order to create storages. -1 -This custom check ensures that a database is defined in the model if it contains a storages. -Create a database for the model in the model property sheet. -Function %Check%(obj) - if (obj.Storages.Count = 0) then - %Check% = True ' No storage, no need of checking database existence. - else - %Check% = Not(obj.Database is Nothing) - end if -End Function -Function %Fix%(obj, outmsg) - ' Implement your automatic correction on <obj> here - ' filling <outmsg> as you wish - ' and return True if successful. - - outmsg = "Automatic correction not implemented" - - %Fix% = False -End Function - - - - -EventHandlerTargetItem -56EBD9AD-6375-4453-B455-C14821951096 -Event Handlers -0 - -1285588953 -psitja -1 - - -DFEAB344-7900-496B-8689-FD5E9B92B86C -AfterDatabaseReverseEngineer -0 - -1285234677 -psitja -1 -Function %AfterDatabaseReverseEngineer%(model) - - Dim obj, Msg - For Each obj in model.ExtendedObjects - Select Case(obj.Stereotype) - Case "PartitionScheme", "FullTextCatalog" : - obj.TriggerValidateEvent Msg - Case "Contract" : - obj.ExecuteCustomMethod("%CurrentTargetCode%.ParseReversedMessageContracts") - Case "Service" : - obj.ExecuteCustomMethod("%CurrentTargetCode%.ParseReversedContracts") - End Select - Next - - For Each obj in model.Users - if obj.Stereotype = "User" then - if obj.GetExtendedAttribute("%CurrentTargetCode%.ImplicitSchema") then - dim userCode - 'set userCode = obj.Code - userCode = obj.Code - dim schema - set schema = model.FindChildByCode(userCode, PdPDM.cls_User, "Schema") - if (schema is Nothing) then - dim usercoll - set usercoll = obj.Model.GetCollectionByName("<<Schema>>") - Set schema = usercoll.CreateNew() - schema.SetNameAndCode obj.Name, userCode - end if - obj.SetExtendedAttribute "%CurrentTargetCode%.DefaultSchema", schema - obj.SetExtendedAttribute "%CurrentTargetCode%.ImplicitSchema", True - end if - End if - Next - - %AfterDatabaseReverseEngineer% = True -End Function - - - -CFD6F77A-6B33-4298-9B96-7B1109ED9856 -OnModelOpen -1277132210 -psitja -1285576966 -psitja -1 -Function %OnModelOpen%(model) - - %OnModelOpen% = True - model.ExecuteCustomMethod("%CurrentTargetCode%.CreateSchemas") - -End Function - - -D6A35700-CA07-4FDF-9329-1AE961F5AE29 -OnLanguageChanged -1285574882 -psitja -1285592392 -psitja -1 -Function %OnLanguageChanged%(model, oldLanguage) - - %OnLanguageChanged% = True - if (oldLanguage.GetExtendedMetaclassByName("Schema") is Nothing) then - model.ExecuteCustomMethod("%CurrentTargetCode%.CreateSchemas") - end if - -End Function - - -02E3732B-783D-44D6-A9F1-AC095ECF8D7A -OnLanguageChangeRequest -1285587892 -psitja -1285592751 -psitja -1 -Function %OnLanguageChangeRequest%(model, newLanguage) - - %OnLanguageChangeRequest% = True - if (newLanguage.GetExtendedMetaclassByName("Schema") is Nothing) then - model.ExecuteCustomMethod("%CurrentTargetCode%.MergeUsers") - end if - -End Function - - - - -MethodTargetItem -2C63A5B2-87C3-4D5D-8BBB-9878D8596EC5 -Methods -1285575458 -psitja -1285589375 -psitja -1 - - -27EEC2C5-A2D3-496B-821C-B86DD137BC8B -CreateSchemas -1285575458 -psitja -1341502043 -mledier -1 -Sub %Method%(model) - - dim usr, sch - for each usr in model.Users - if not(usr.IsShortcut) then - if (usr.stereotype = "User") then 'Already fixed - Exit Sub - elseif (usr.stereotype <> "Schema") then - usr.stereotype = "User" - dim obj - set sch = usr.GetExtendedAttribute("%CurrentTargetCode%.DefaultSchema") - if not(sch is Nothing) then - for each obj in usr.OwnedObjects - obj.Owner = sch - next - end if - end if - end if - next - - dim schemacoll, usercoll - set schemacoll = model.GetCollectionByName("<<Schema>>") - set usercoll = model.GetCollectionByName("<<User>>") - if not (schemacoll is Nothing) and not (usercoll is Nothing) then - for each usr in schemacoll - dim ownerStr - ownerStr = usr.GetExtendedAttribute("SchemaOwner", 2) - if (ownerStr <> "") then - dim owner - set owner = model.FindChildByCode(ownerStr, PdPDM.cls_User, "User") - if (owner is Nothing) then - Set owner = usercoll.CreateNew() - owner.Name = ownerStr - dim msg - if not owner.CanSetAttribute("Code", ownerStr, msg) then - dim modlopts - set modlopts = owner.GetModelOptions - dim codeconv - for each codeconv in modlopts.CodeNamingConventions - if (codeconv.Code = "DefaultClass") then - codeconv.AllCharactersValid = true - output "The naming conventions for ""Other objects"" have been modified to allow the creation of " & owner - end if - next - end if - owner.Code = ownerStr - output "Create " & owner & " as owner of " & usr - dim dftsch - owner.SetExtendedAttribute "%CurrentTargetCode%.ImplicitSchema", False - set dftsch = owner.GetExtendedAttribute("%CurrentTargetCode%.DefaultSchema") - if not(dftsch is Nothing) then - dftsch.Delete - end if - set dftsch = model.FindChildByCode(ownerStr, PdPDM.cls_User, "Schema") - if not(dftsch is Nothing) then - owner.SetExtendedAttribute "%CurrentTargetCode%.DefaultSchema", dftsch - owner.SetExtendedAttribute "%CurrentTargetCode%.ImplicitSchema", True - end if - end if - if not(owner is Nothing) then - usr.SetExtendedAttribute "%CurrentTargetCode%.SchemaOwner", owner - end if - end if - next - end if - - for each obj in model.ExtendedObjects - if (obj.HasExtendedAttribute("%CurrentTargetCode%.Owner")) then - set usr = obj.GetExtendedAttribute("%CurrentTargetCode%.Owner") - if not (usr is nothing) then - if (obj.Stereotype = "Aggregate") or (obj.Stereotype = "Queue") or (obj.Stereotype = "XMLSchemaCollection") then - if (usr.stereotype = "User") then - set sch = usr.GetExtendedAttribute("%CurrentTargetCode%.DefaultSchema") - obj.SetExtendedAttribute "%CurrentTargetCode%.Owner", sch - end if - else - if (usr.stereotype = "Schema") then - set sch = usr.GetExtendedAttribute("%CurrentTargetCode%.SchemaOwner") - obj.SetExtendedAttribute "%CurrentTargetCode%.Owner", sch - end if - end if - end if - end if - next - -End Sub - - -1AB03713-0DE8-4514-A16A-4F2BBB752513 -MergeUsers -1285589312 -psitja -1286896341 -psitja -1 -Sub %Method%(model) - - dim usr, sch - for each usr in model.GetCollectionByName("<<User>>") - set sch = usr.GetExtendedAttribute("%CurrentTargetCode%.DefaultSchema") - if not (sch is Nothing) then - if (usr.Code = sch.Code) then - Dim obj, xcol - for each obj in sch.OwnedObjects - obj.Owner = usr - next - for each obj in sch.ExtendedInverseCollections - set xcol = obj.TargetItemDefinition - if (xcol.Name = "Owner") then - xcol.Content.Remove sch - xcol.Content.Add usr - end if - next - sch.Delete - end if - end if - usr.Stereotype = "" - next - -End Sub - - - - -TransformationTargetItem -54DF6562-8A41-4724-A850-9D487F773F17 -Transformations -1285594636 -psitja -1285594912 -psitja -1 - - -D4644028-6ADA-49C3-9641-72F83471944C -trfCreateSchemas -1285594636 -psitja -1286895916 -psitja -1 -Sub %Transformation%(model, trfm) - - dim srce - set srce = trfm.GetSource(model) - if not (srce is nothing) then - dim dbms - set dbms = srce.dbms - if (dbms.isShortcut) then - set dbms = dbms.targetObject - end if - if (dbms.GetExtendedMetaclassByName("Schema") is Nothing) then - model.ExecuteCustomMethod("%CurrentTargetCode%.CreateSchemas") - end if - end if - -End Sub - - -008D0442-13E5-41E1-B499-38AFB245BAF0 -trfMergeUsers -1285594636 -psitja -1286896341 -psitja -1 -Sub %Transformation%(model, trfm) - - dim trgt - set trgt = trfm.GenerationTargetModel - if not (trgt is nothing) then - dim dbms - set dbms = trgt.dbms - if (dbms.isShortcut) then - set dbms = dbms.targetObject - end if - if (dbms.GetExtendedMetaclassByName("Schema") is Nothing) then - model.ExecuteCustomMethod("%CurrentTargetCode%.MergeUsers") - end if - end if - - -End Sub - - - - - - -2F393D0D-DBDD-40DB-B84D-09EE57D5C46E -PhysicalDomain -0 - -0 - -1 - - -CustomCheckTargetItem -F0D6B879-B01C-4D36-A261-2E73712C6EB7 -Custom Checks -0 - -0 - -1 - - -6557F16F-0F0A-46F3-A9E8-376978D43B54 -Domain missing default object -0 - -0 - -In the SQL SERVER family, you set a default value to a domain using a default object. This check ensures that all domains with a default use a default object. -1 -A domain cannot have a default value without being attached to a default object. You can either create a default object for the value and run the autofix on the domain or use the Rebuild Default feature. -The following domain must be linked to a default object: -Function %Check%(obj) - if (obj.default is Nothing) then ' not an object - if CStr(obj.defaultvalue) <> "" then ' but not empty !! - %Check% = false - exit function - end if - end if - %Check% = True -End Function -Function %Fix%(obj, outmsg) - if (obj.default is Nothing) then ' not an object - if CStr(obj.defaultvalue) <> "" then ' but not empty !! - dim pDefault, sDefault - set pDefault = ActiveModel.CreateObject(cls_PhysicalDefault) - sDefault = replace(Cstr(obj.DefaultValue), "'", "") - If (CanSetNameCode(pDefault, "D_" & sDefault, "D_" & sDefault)) Then - pDefault.Name = "D_" & sDefault - pDefault.Code = "D_" & sDefault - ElseIf (CanSetNameCode(pDefault, "D_" & Cstr(obj.Name), "D_" & Cstr(obj.Code))) Then - pDefault.Name = "D_" & Cstr(obj.Name) - pDefault.Code = "D_" & Cstr(obj.Code) - Else - Dim i - i = 1 - While not (CanSetNameCode(pDefault, "D_" & Cstr(obj.Name) & "_" & CStr(i), "D_" & Cstr(obj.Code) & "_" & CStr(i))) - i = i + 1 - Wend - pDefault.Name = "D_" & Cstr(obj.Name) & "_" & CStr(i) - pDefault.Code = "D_" & Cstr(obj.Code) & "_" & CStr(i) - End If - pDefault.Value = obj.DefaultValue - Set obj.Default = pDefault - %Fix% = True - outmsg = "Default " & pDefault.Name & " created and attached to domain " & obj.Name - Exit Function - end if - end if - outmsg = "Unknown Error occurs, autofix failed" - %Fix% = False -End Function - -1 - - - - - - -EAC300F6-2738-4420-BCEC-C20075D378C3 -Reference -0 - -0 - -1 - - -ExtendedAttributeTargetItem -E0A1E22C-8474-4BE8-A472-B38ED82780C0 -Extended Attributes -0 - -0 - -1 - - -991CC8BA-EA68-4621-BB69-97AF282F0DAC -ExtFkNotForReplication -0 - -0 - -Specifies that "NOT FOR REPLICATION" keywords are used to prevent the FOREIGN KEY constraint from being enforced during the distribution process used by replication. -1 -false -0 - - - - -FormTargetItem -816D4DE3-96B2-4D99-8415-51EFE794B9CD -Forms -0 - -0 - -1 - - -1BE7CA52-5963-4427-A05D-A00E9C2AA9CB -Microsoft -<Form > - <ExtendedAttribute Attribute="ExtFkNotForReplication" Caption="Do not validate foreign key constraint during replication" AttributeID="{991CC8BA-EA68-4621-BB69-97AF282F0DAC}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> -</Form> - -0 - -1276759563 -obalen -1 -24917 - - - - - - -DD0442F9-1DCE-4B89-BEC4-62ED6D5A0A56 -Dimension -0 - -0 - -1 - - -ExtendedAttributeTargetItem -ACB21F8C-C155-47B9-B744-F3563EC49906 -Extended Attributes -0 - -0 - -1 - - -915F7F06-4541-4571-B067-515C1C5279D1 -AttributeAllMemberName -0 - -0 - -Contains the caption, in the default language, for the All member of the dimension. -1 -12 -0 - - -90AA142D-D502-432C-AA1B-44AEBBF5E551 -StorageMode -0 - -0 - -Determines the storage mode for the parent element. -1 --1 -Molap -1 -0 - - - - - -E13D58B1-5B08-4C63-BADD-9F8788706562 -Type -0 - -0 - -Provides information about the contents of the dimension. -1 --1 -Regular -1 -0 - - - - - -9AFD384E-15D8-4558-B17D-3F45C2499288 -WriteEnabled -0 - -0 - -Indicates whether dimension writebacks are available (subject to security permissions). -1 -false -0 - - - - -TemplateTargetItem -64440AAA-2026-448F-A62B-61D473595016 -Templates -0 - -0 - -1 - - -135E0460-EE49-4E8E-982B-16B739D00FA8 -DimensionDef -DIMENSION %GeneratedName% -0 - -0 - -1 - - -FBAFCF4D-823F-4C31-9BDC-5BB115FADB9E -HiddenDef -.if (%IsHidden%) -HIDDEN -.endif -0 - -0 - -1 - - -0B2A9424-E23E-4546-BBDA-BA633851EDE3 -AllLevelDef -.foreach_item(Attributes,,\n) -%LevelDef% -.next(",\n") - -0 - -0 - -1 - - - - -FormTargetItem -A3911522-2F28-4204-BA48-FD53EE5091AB -Forms -0 - -0 - -1 - - -C9290D5B-8935-47F6-8296-71D26E159AEE -Microsoft -<Form > - <ExtendedAttribute Attribute="Type" Caption="Type" AttributeID="{E13D58B1-5B08-4C63-BADD-9F8788706562}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="StorageMode" Caption="Storage mode" AttributeID="{90AA142D-D502-432C-AA1B-44AEBBF5E551}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="AttributeAllMemberName" Caption="AttributeAllMemberName" AttributeID="{915F7F06-4541-4571-B067-515C1C5279D1}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="WriteEnabled" Caption="WriteEnabled" AttributeID="{9AFD384E-15D8-4558-B17D-3F45C2499288}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> -</Form> - -0 - -1276605429 -obalen -1 -24917 - - - - - - -4752AC7C-B90D-4E80-87BB-CA1A614A0369 -DimensionAttribute -0 - -0 - -1 - - -ExtendedAttributeTargetItem -281C49E6-7A16-487E-A63D-3F0EC7BCBB71 -Extended Attributes -0 - -0 - -1 - - -E7F019F3-4E07-41B2-9C5B-B803C3E9715C -AttributeHierarchyEnabled -0 - -0 - -Determines whether an attribute hierarchy is enabled for the attribute. -1 -true -0 - - -F247818B-2393-4C90-AC7E-DF8E878FE155 -AttributeHierarchyVisible -0 - -0 - -Determines whether the attribute hierarchy is visible to client applications. -1 -true -0 - - -5E55B2A9-F386-431E-B1E9-A29D24DD55CB -IsAggregatable -0 - -0 - -Specifies whether the values of the DimensionAttribute element can be aggregated. -1 -true -0 - - -9FE117D7-9227-4A92-A8CD-0250CBCD0185 -MemberNamesUnique -0 - -0 - -Determines whether member names under the parent element must be unique. -1 -false -0 - - -9FC6654C-E26C-42DC-B294-1B1461EAF4C8 -MembersWithData -0 - -0 - -Determines whether to display data members for non-leaf members in the parent attribute. -1 --1 -NonLeafDataVisible -1 -0 - - - - - -983E1893-7746-4E96-811D-47152510594F -OrderBy -0 - -0 - -Describes how to order the members contained in the attribute -1 --1 -Name -1 -0 - - - - - -BD811B60-D709-488C-85ED-97DB5F167AA5 -Type -0 - -0 - -Identifies the specific type of level -1 --1 -Regular -1 -0 - - - - - - - -TemplateTargetItem -55449EE0-8B3F-47D9-B5F3-B4B5B38FF8CA -Templates -0 - -0 - -1 - - -32B8964A-1B07-4C76-900F-96401DF0356A -HoleDef -[HIDE_MEMBER_IF %HideValues%] - -0 - -0 - -1 - - -CCE8CBF4-BD0C-41CB-8522-55C6F87B8697 -LevelDef -LEVEL %GeneratedName%[ TYPE %Type%] -0 - -0 - -1 - - - - -FormTargetItem -B49CE779-3273-4BC1-8475-574F2284F74A -Forms -0 - -0 - -1 - - -877867D2-6DA7-4008-B987-E544B309774F -Microsoft -<Form > - <GroupBox Name="GroupBox1" Caption="Member attributes" ControlAsLabel="No" > - </GroupBox> - <ExtendedAttribute Attribute="Type" Caption="Type" AttributeID="{BD811B60-D709-488C-85ED-97DB5F167AA5}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="MembersWithData" Caption="MembersWithData" AttributeID="{9FC6654C-E26C-42DC-B294-1B1461EAF4C8}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="OrderBy" Caption="OrderBy" AttributeID="{983E1893-7746-4E96-811D-47152510594F}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="MemberNamesUnique" Caption="MemberNamesUnique" AttributeID="{9FE117D7-9227-4A92-A8CD-0250CBCD0185}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="IsAggregatable" Caption="IsAggregatable" AttributeID="{5E55B2A9-F386-431E-B1E9-A29D24DD55CB}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="AttributeHierarchyEnabled" Caption="AttributeHierarchyEnabled" AttributeID="{E7F019F3-4E07-41B2-9C5B-B803C3E9715C}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="AttributeHierarchyVisible" Caption="AttributeHierarchyVisible" AttributeID="{F247818B-2393-4C90-AC7E-DF8E878FE155}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> -</Form> - -0 - -1276605429 -obalen -1 -24917 - - - - - - -341B691A-A772-4112-B75D-BD1CF9D6DEBD -DimensionHierarchy -0 - -0 - -1 - - -ExtendedAttributeTargetItem -945EBF34-3C0A-4456-8863-5B7E5685984D -Extended Attributes -0 - -0 - -1 - - -ADAAEEDD-A287-406F-B2D4-DF3EA3A1367F -AllMemberName -0 - -0 - -Contains the caption in the default language for the All member of a Hierarchy element. -1 -12 -0 - - -1B9DE899-B24A-4805-BA10-A490CAA6B8C4 -AllowDuplicateNames -0 - -0 - -Determines whether duplicate names are allowed in a Hierarchy element. -1 -true -0 - - -664728D2-977C-4FAF-B3CC-203FFBACF12B -MemberNamesUnique -0 - -0 - -Determines whether member names under the parent element must be unique. -1 -false -0 - - - - -TemplateTargetItem -2B287079-3F13-4D50-83C1-7D0F2271DCEF -Templates -0 - -0 - -1 - - -0126AD82-10C1-4996-B6E6-28F2759C9729 -HierarchyAllLevelDef -.foreach_item(Attributes,,\n) -%LevelDef% -.next(",\n") - -0 - -0 - -1 - - -F31A0BF7-A1CC-4C75-BA66-454AD5C6F2A9 -HierarchyDef -HIERARCHY %GeneratedName% - -0 - -0 - -1 - - - - -FormTargetItem -757F08DB-20CD-4C82-8F20-536DF1D9555F -Forms -0 - -0 - -1 - - -1466AF56-FF81-450D-8E56-BFC50A292CA1 -Microsoft -<Form > - <ExtendedAttribute Attribute="AllMemberName" Caption="AllMemberName" AttributeID="{ADAAEEDD-A287-406F-B2D4-DF3EA3A1367F}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="MemberNamesUnique" Caption="MemberNamesUnique" AttributeID="{664728D2-977C-4FAF-B3CC-203FFBACF12B}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="AllowDuplicateNames" Caption="AllowDuplicateNames" AttributeID="{1B9DE899-B24A-4805-BA10-A490CAA6B8C4}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> -</Form> - -0 - -1276605429 -obalen -1 -24917 - - - - - - -596FEF9D-5ECD-4CAB-B4E5-E098CCF07EF4 -User -0 - -1277121181 -psitja -1 -1 - - -StereotypeTargetItem -09BCE122-D199-4B25-BE3E-BD57D4E6461D -Stereotypes -0 - -1277121181 -psitja -1 - - -BAB8D764-7BE0-4A5D-912B-5C8E355A2687 -Schema -0 - -1286890165 -psitja -The User with this stereotype is a Schema. - -1 -Schema -Schemas -0D0000000800000008000000424D7E000000000000003E000000280000001000 -0000100000000100010000000000400000000000000000000000000000000000 -000000000000FFFFFF00FFFF0000F8030000E0030000E0030000E0030000E003 -0000E0070000F80F0000F01F0000F01F0000F01F0000F01F0000F01F0000F01F -0000F81F0000FFFF0000424D3605000000000000360400002800000010000000 -1000000001000800000000000001000000000000000000000000000073000000 -0000000005000000131313001313160014141400151516001515180015192200 -17171800191918002525240029292A002C2F3400303030003822220038383800 -3D3C3C003E3E3F00410708004140410043000000444648004560920046000000 -47000000480000004B1515004B535F004C0101004D0404004D1212004D4D4D00 -4E0404004E454500501C1C0053111100541A1A0056606F005725250058191900 -591D1E00592626005A2728005C2525005C2727005C2828005E2526005E2F2F00 -602A2A00602B2B00602B2C00602C2C00602D2C00612D2D006187CE0062262600 -6230300063292A00632F2F006391DF00642C2D00642F2F0064343500662E2E00 -6631310066353600673B3C0068303000683435006836370069333400693A3A00 -6A82A4006B3333006B3738006C9AEB00703D3D00713E3F007144430073646400 -74484900758292007699DE0078A6F5007DA5E8007E9EDE007FA2E20082A6E800 -82A7E60082B0FA00897C8A008A6969008CB7FA008DB2EE008EBBFF0090818100 -93BEFF0094BFFF0099807F0099C1FC009DB4D9009FAEBF009FC0F200A0A09F00 -A5B9D500A7CDFF00A8CDFF00AACEFD00ADCCF500B2B2C300C2DFFE00CBCED800 -CBE4FF00CCE4FF00FE0000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000001E28444A463C2717140000 -0000001A3A3331434C4D49371C1800000000002D452F0E423F44403920190000 -000000293847215F503D352E1D170000000000222A414F67623E2C2B23140000 -0000000126345A646F5B323024000000000000000055563B586D4E1200000000 -00000000525C5E594B57720000000000000000005D6361605336000000000000 -00000000666B696A5416030000000000000000006C6E71704807050000000000 -000000001B516568250904000000000000000000131F150C0D0F080000000000 -000000000011100A020B06000000000000000000000000000000000000000000 -0D0000001000000010000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00FFFFFFFFFFFFFFFFFFFC007FFFC0001FFF80000FFE00 -000FFE00000FFE00000FFE00000FFE00001FFE00001FFE00001FFF00003FFF80 -007FFFE001FFFFC007FFFFC00FFFFF800FFFFF800FFFFF8007FFFF8007FFFF80 -03FFFF8003FFFF8003FFFF8003FFFF0003FFFF0007FFFF8007FFFFC00FFFFFFC -3FFFFFFFFFFFFFFFFFFF424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000001D0000053903030F -450D0C1A4A1313244C1313294C1313294A121226460D0D1D3E0606143400000A -2300000200000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000060000014005051A4A0B0B545F28298C673334B26A3637CA -6C3939DA6D3A3BE56D3839E86A3435E9672F30E6652D2EDE622829D24D0A0AC0 -4500009A4300005B3D0000170000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000004812121C5C27287A5A2122D45C2121FA5F1F20FF6C3535FF754344FF -764445FF754445FF744243FF713D3DFF6B3232FF6B3233FF6D3637FF581313FF -4B0000FF4A0000FC470000C23F00002400000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000035010106 -551F1F57643132CE6D3A3BFD5D2829FF64393AFF6B3838FF5D1E1FFF602222FF -6D393AFF714041FF714040FF714040FF6A3233FF662C2CFF703E3FFF602222FF -4C0000FF4C0000FF4A0000F24000003900000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000541E1E60 -673535F46E3C3DFF6A3636FF5A2829FF2E1A1AFF543434FF724445FF662E2FFF -5C1C1CFF652D2DFF6D3C3CFF6D3C3DFF6B3738FF602324FF6B3839FF63292AFF -4C0101FF4C0000FF490000E53E00002500000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000005C27278F -693738FF6A393AFF652F30FF5F3030FF1C1212FF110C0CFF654445FF744748FF -6D3C3DFF5F2222FF602526FF693738FF6A3839FF612727FF652F30FF622A2BFF -4D0202FF4C0000FF480000D13B00001300000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000057232382 -653334FF663435FF622B2CFF6F4142FF473A3AFF0A0B0BFF8A8383FF8F6B6BFF -6D4041FF6D4041FF632A2BFF5D2323FF653434FF622E2EFF612B2CFF602728FF -4D0202FF4C0000FF460000B02D00000500000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000521D1D68 -602F2FFF623131FF602A2AFF714243FF918081FF323333FFA3A4A4FFD7CBCCFF -754B4CFF693C3DFF6B3E3FFF642D2EFF5D2323FF612F30FF612E2FFF5B2122FF -4C0101FF4B0000FF4400007E0000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000004B15163C -5B2929F15E2C2DFF5F2929FF67393AFF977F80FF636464FF6A6A6AFFDEDEDEFF -967F80FF66393AFF66393AFF683C3CFF5F2626FF5C2828FF5E2D2EFF581B1BFF -540E0EFF4D0707EA400000370000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000003C04040E -532020B95A2929FF5D2728FF643637FF785757FF676565FF444443FFD9D9D9FF -C9C3C4FF7B5859FF623536FF633233FF592121FF592728FF5A2424FF602525FF -5C1E1FF74C0E0F7C220000040000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -4C1718365B2A2AC3612F30F9653333FF7A5454FFBBBABEFF9CA9BEFFE1E4EAFF -E9E9E8FFCCC3C3FF7A5657FF5F2B2CFF592121FF5C2425FF673333FF60292AE2 -4F15156934000008000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000004C17181459262750592221977B504EE2ADB9D8FE75A0E8FF8BADE9FF -C7D6EEFFE6E8E9FFBDB3B2FF734D4DFF6A3A3AFF632D2EF3602E2EA54B141432 -0100000100000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000006E92D83B7486BCB06D8FCFFA4D7ACAFF5582D1FF -739DE7FFAAC2EDFFD9DDE2FFB7A5A5FF6B3C3DDB4F1819563201020700000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000006E93DB1E84AFF5D08BB9FFFF88B5FCFF74A3F1FF5B89DBFF -5986D5FF6E9BE8FFADCAF5FFBCB1B4D65C2D2D38000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000007EA6EB688DB9FCFE8DBAFFFF8DBAFFFF8BB8FEFF76A5F5FF -6290E4FF6895E2FF85ADF1F2787DA24B00000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000004C69B60689B1F3B091BDFFFF8DBAFFFF8DBAFFFF8DBAFFFF88B5FDFF -6F9EF2FF6997EAFF80ABF3EA7096E02C00000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000007296DC1E94BBF8DD96C0FFFF91BDFFFF91BDFFFF91BDFFFF8FBCFFFF -7CAAF7FF6796EDFF78A4F1F6759BE44200000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000082A6E63EA1C7FBF49DC5FFFF98C1FFFF98C1FFFF98C1FFFF96C0FFFF -89B6FCFF6997EDFF628CDAFF3144668800000004000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000008EB1EB5EACCFFDFEA3CAFFFF9EC7FFFFA0C8FFFFA2C9FFFF9EC7FFFF -90BCFEFF709DF0FF6691E2FF2C3B56F105030151000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000098BAEF75B6D7FEFFABD0FFFFABCFFFFFB0D2FFFFB3D4FFFFACD0FFFF -94BEFBFF5D7FB8FF648CD8FF384B70FF121110BA0000000C0000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000009FBEEF77C0DDFEFFB8D8FFFFBAD9FFFFC0DCFFFFC5DFFFFFBDDAFFFF -95BEF8FF3C4A5EFF1B2334FF121721FF191919E6060608270000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000A2BFED5DCDE5FDFDC9E3FFFFCCE3FFFFD8E9FFFFDEEDFFFFCCE3FFFF -8FB5ECFF454C58FF060504FF040404FF1C1C1DED0B0C0D300000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000748BB338A9BFDAEFBBD3EDFFD1E6FCFFEAF6FFFFEEF8FFFFC4E0FFFF -7D9AC4FF3D3E41FF101010FF0B0B0BFF1A1A1AE6080809270000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0B0A0C06191A1B91282C32FD323943FF4F5C6CFF7F92AAFF96AFD2FF7792B9FF -5B626CFF3F3E3EFF252525FF191919FF161616CA000001110000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -131315184C4C4DC8656564FF5B5B5AFF474747FF3B3C3EFF3A3E43FF1B1D1FFF -2F2F2FFF575757FF444444FF2C2C2CFF13131482000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000003333352649494A86525252D74F4F50FA434343FF2E2E2DFF131313FF -0B0B0BFF323232FE484849EC2F2E2FA50E0D1019000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000012020221B353536523939398D333333B7252526CF -141414C509090A8618181A301010120A00000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000050508070D0E1212 -0C0C110D03030A01000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 - -1 -Schema - - -ExtendedAttributeTargetItem -1462A958-A341-4BE1-A38C-08D6C65F82C9 -Extended Attributes -0 - -1314867569 -mledier -1 - - -3D80D8B1-8604-4F07-80DD-4C3B5BC0FA17 -SchemaOwner -0 - -1277210314 -psitja -Specifies the name of the database-level principal user that will own the schema. This principal may own other schemas, and may not use the current schema as its default schema. -1 -15 -4BA9F642-DAB1-11D1-9944-006097355D9B -User -0 - - - - - - - -ExtendedCollectionTargetItem -A0660DA8-0862-499E-BDC3-727DFF0061BA -Extended Collections -1268666928 -mledier -1277121181 -psitja -1 - - -A3BEFD1E-A3C5-49FA-9610-FCDDF1AFB008 -SchemaOwner -1268665783 -mledier -1277210314 -psitja -1 -4BA9F642-DAB1-11D1-9944-006097355D9B -User -NAME 80 CODE 80 - - - - - - - -FormTargetItem -30BEB833-D8FB-40C1-B0A6-D5D99DAE4466 -Forms -0 - -1314867569 -mledier -1 - - -A2ED98B7-4BF0-4B44-A50A-DDC66E66ADD8 -General -GENERAL -<Form > - <StandardNameAndCode Attribute="NameAndCode" SizeInChars="1" HorizontalResize="true" /> - <StandardAttribute Attribute="Comment" Caption="Comment:" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="SchemaOwner" Caption="Owner" AttributeID="{3D80D8B1-8604-4F07-80DD-4C3B5BC0FA17}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> <StandardAttribute Attribute="KeywordList" Caption="Keywords" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> -</Form> - -1268672349 -mledier -1287129849 -obalen -1 -GENERAL -24917 - - - - - - -57A02979-7341-4901-8D35-F83FAD3CCD6E -User -1268666517 -mledier -1286890165 -psitja -The User with this stereotype is a User. - -1 -User -Users -1 - - -ExtendedAttributeTargetItem -726DD602-6AC5-42C5-9B80-B1577915BAB0 -Extended Attributes -1268666928 -mledier -1277475121 -psitja -1 - - -E41FA7D5-DB57-4F7D-91F9-A3448292B062 -ImplicitSchema -1268671332 -mledier -1279274213 -psitja -If true, the database generation will use the stored procedure sp_grantdbaccess instead of create user statement. -1 -false -Implicit schema -0 - - - - -ExtendedCollectionTargetItem -43328ABE-315F-438D-B75B-938C9972D2C1 -Extended Collections -1268671423 -mledier -1277121181 -psitja -1 - - -F26BBD1B-6203-432B-8FAF-33D750132006 -DefaultSchema -1268665736 -mledier -1277121181 -psitja -1 -Users -4BA9F642-DAB1-11D1-9944-006097355D9B -Schema -NAME 80 CODE 80 - - - - - - - -FormTargetItem -D1401C9C-6CFF-4975-9E4C-7C424492BE69 -Forms -1268672349 -mledier -1277121181 -psitja -1 - - -9A2CD7D9-F1B0-4EE4-830D-9729BB79A3F0 -General -GENERAL -<Form > - <StandardNameAndCode Attribute="NameAndCode" Caption1="Name" Caption2="Code" SizeInChars="1" HorizontalResize="Yes" /> - <StandardAttribute Attribute="Comment" Caption="Comment:" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" LeftText="false" /> - <StandardAttribute Attribute="Password" Caption="Password:" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="ImplicitSchema" Caption="Implicit schema" AttributeID="{E41FA7D5-DB57-4F7D-91F9-A3448292B062}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="Yes" /> - <ExtendedAttribute Attribute="DefaultSchema" Caption="Default schema" AttributeID="{160E7624-1BCB-4D5A-B428-D163233678A9}" GrayHiddenAttributes="No" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="DefaultSchema" Caption="Default schema" AttributeID="{ADC0C186-CDEF-4689-8448-0DB60D7CAD78}" GrayHiddenAttributes="No" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> <StandardAttribute Attribute="KeywordList" Caption="Keywords" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> -</Form> - -1268672349 -mledier -1287129914 -obalen -1 -GENERAL -24917 - - - - -EventHandlerTargetItem -ED34EA19-2A5C-41A0-8E42-318C7E0E4F7F -Event Handlers -1276160957 -psitja -1277121181 -psitja -1 - - -92C8379A-955D-44E2-849E-DDE8D8ECA112 -Initialize -1276160957 -psitja -1286280209 -psitja -1 -Function %Initialize%(obj) - dim defSchema - set defSchema = obj.GetExtendedAttribute("%CurrentTargetCode%.DefaultSchema") - if (defSchema is Nothing) then - dim usercoll - set usercoll = obj.Model.GetCollectionByName("<<Schema>>") - if not (usercoll is Nothing) then - Set defSchema = usercoll.CreateNew() - defSchema.SetNameAndCode obj.Name, obj.Code - obj.SetExtendedAttribute "%CurrentTargetCode%.DefaultSchema", defSchema - obj.SetExtendedAttribute "%CurrentTargetCode%.ImplicitSchema", True - end if - end if - %Initialize% = True -End Function - - -7A2E6595-AD2F-43C7-A958-E0C1E51A5966 -Validate -1276163354 -psitja -1287071554 -psitja -1 -Function %Validate%(obj, ByRef message) - - if (obj.GetExtendedAttribute("%CurrentTargetCode%.ImplicitSchema")) then - dim defSchema - set defSchema = obj.GetExtendedAttribute("%CurrentTargetCode%.DefaultSchema") - if not (defSchema is Nothing) then - if (defSchema.code <> obj.code) then - dim deftSchema2 - set deftSchema2 = obj.model.FindChildByCode(obj.code, PdPDM.cls_User, "Schema") - if (deftSchema2 is Nothing) then - defSchema.SetNameAndCode obj.Name, obj.Code - else - dim schcoll - set schcoll = obj.GetExtendedCollection("%CurrentTargetCode%.DefaultSchema") - if not (schcoll is Nothing ) then - schcoll.Clear - schcoll.Add deftSchema2 - end if - end if - end if - end if - end if - - %Validate% = True -End Function - - - - -CriterionTargetItem -4EBCE88F-781E-4943-A520-7045827E2432 -Criteria -1277473817 -psitja -1277475121 -psitja -1 - - -6A917CA0-6652-44D9-B7F8-15FF6E4308C9 -Implicit Schema -%ImplicitSchema% -1277473817 -psitja -1277475121 -psitja -1 - - -ExtendedAttributeTargetItem -5AA22590-2BC7-4B33-B4B0-CBD8F755362E -Extended Attributes -1277473817 -psitja -1277474761 -psitja -1 - - -160E7624-1BCB-4D5A-B428-D163233678A9 -DefaultSchema -1277473817 -psitja -1287071087 -psitja -Specifies the first schema searched to resolve the names of objects for this user. -1 -15 -1 -RO -Function %Get%(obj) - if obj.GetExtendedAttribute("%CurrentTargetCode%.ImplicitSchema") then - dim schcoll - set schcoll = obj.GetExtendedCollection("%CurrentTargetCode%.DefaultSchema") - if not (schcoll is Nothing) then - dim deftSchema - if (schcoll.Count = 0) then - set deftSchema = obj.model.FindChildByCode(obj.code, PdPDM.cls_User, "Schema") - if (deftSchema is Nothing) then - dim schemacoll - set schemacoll = obj.model.GetCollectionByName("<<Schema>>") - Set deftSchema = schemacoll.CreateNew() - deftSchema.SetNameAndCode obj.Name, obj.Code - end if - if not (deftSchema is Nothing) then - schcoll.add deftSchema - end if - else - set deftSchema = schcoll.Item(0) - end if - set %Get% = deftSchema - end if - else - set %Get% = Nothing - end if -End Function -4BA9F642-DAB1-11D1-9944-006097355D9B -Schema -Default schema -0 - - - - - - -CEEA6558-D3AD-49AF-8BC0-9B0E57F1BBAD -Not Implicit Schema -%ImplicitSchema% == false -1277475011 -psitja -1277475121 -psitja -1 - - -ExtendedAttributeTargetItem -3E76A025-33DD-4CA4-8D53-CD5A1A610F76 -Extended Attributes -1277475011 -psitja -1277475121 -psitja -1 - - -ADC0C186-CDEF-4689-8448-0DB60D7CAD78 -DefaultSchema -1268665736 -mledier -1285605818 -psitja -Specifies the first schema searched to resolve the names of objects for this user. -1 -15 -4BA9F642-DAB1-11D1-9944-006097355D9B -Schema -Default schema -0 - - - - - - - - - - - - - - - - - -69720C6E-BB36-468D-A43C-38101AA9CC69 -Procedure -0 - -0 - -1 - - -CriterionTargetItem -A098763D-C4EB-49A4-951C-0BA758C05D36 -Criteria -0 - -0 - -1 - - -6FF53FA4-0FCC-4277-B3BD-837980A4CE06 -isCLR -%ImplementationType% == "ClassMethod" -0 - -0 - -1 - - -ExtendedAttributeTargetItem -49C43664-5E57-4DDE-A090-A320447876A5 -Extended Attributes -0 - -0 - -1 - - -E21F6FDD-BF10-44CE-8799-7CF95370FB97 -Assembly -0 - -0 - -Specifies the assembly to bind with the procedure. -1 -15 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Assembly -0 - - - - - - - -ExtendedCollectionTargetItem -E5A8B60A-F149-410E-B26C-4B673A6E3E69 -Extended Collections -0 - -0 - -1 - - -5832F810-ED5D-4E9A-9BCE-4F7C90F4D098 -Assembly -0 - -0 - -1 -Procedures -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Assembly - - - - -FormTargetItem -D10343BE-1DEE-4AA3-A161-D7F9DC83679D -Forms -0 - -0 - -1 - - -15DFAECC-0F2A-480A-AF0E-5FDBAF830549 -Microsoft -<Form > - <ExtendedAttribute Attribute="Assembly" Caption="Assembly:" AttributeID="{E21F6FDD-BF10-44CE-8799-7CF95370FB97}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> -</Form> - -0 - -1276605429 -obalen -1 -24917 - - - - - - - - -TemplateTargetItem -359ACDBD-0F9A-4780-9219-7F37815B45B0 -Templates -0 - -0 - -1 - - -084344D7-30EA-4EF6-8B66-981FB760CB8D -canDefineInsertProcedure -.set_value(_res, "false", new) -.if (%Table%) - .foreach_item(Table.Columns) - .if (%Computed% == false) and (%SerialDataType% == false) -.set_value(_res, "true", update) - .break - .endif - .next() -.endif -%_res% -0 - -0 - -1 - - -0C508B79-9B82-4656-B295-8A64B3A76CFC -canDefineUpdateProcedure -.set_value(_res, "false", new) -.if (%Table%) - .foreach_item(Table.Columns) - .if (%Computed% == false) -.set_value(_res, "true", update) - .break - .endif - .next() -.endif -%_res% -0 - -0 - -1 - - -471A89E2-8843-414B-B069-5A5B3611E58F -canDefineDeleteProcedure -.set_value(_res, "false", new) -.if (%Table%) - .foreach_item(Table.Columns) - .if (%Primary%) -.set_value(_res, "true", update) - .break - .endif - .next() -.endif -%_res% -0 - -0 - -1 - - -500C4B87-3F89-423C-93C2-47C19B72DDF1 -canDefineSelectProcedure -.set_value(_res, "false", new) -.if (%Table%) - .foreach_item(Table.Columns) - .if (%Primary%) -.set_value(_res, "true", update) - .break - .endif - .next() -.endif -%_res% -0 - -0 - -1 - - - - - - -4A535FA4-39C9-4E7F-95C7-F12C1E374045 -BaseIndex -0 - -1328012388 -mledier -1 - - -CustomCheckTargetItem -8B42CB85-19ED-49BA-A4A6-E700ACA29A16 -Custom Checks -0 - -0 - -1 - - -998BBA17-DCFC-486F-B147-A256C3FAC838 -XML index specific checks -0 - -0 - -1 -This check ensures that XML index respects following rules: -- XML index needs a clustered key or index on the table -- XML Index can only have one column and this column must have xml datatype -- A primary (clustered) XML index on an xml column must exist before a secondary XML index can be created on the column. -- You cannot create a primary XML index on a computed xml column. - -The following XML indexes are not correctly defined: -Function %Check%(obj) - dim bPrimary - bPrimary = False - if (obj.Type = "XML") then - bPrimary = C2Bool(obj.GetExtendedAttribute("XMLPrimary")) - - ' XML index needs a clustered key or index on the table - if (obj.Table.ClusterObject is Nothing) then - output "XML Index " & obj.Table.Name & "." & obj.Name & " needs a clustered index on the table." - %Check% = False - Exit Function - end if - ' XML Index can only have one column - if (obj.IndexColumns.Count <> 1) then - output "XML Index " & obj.Table.Name & "." & obj.Name & " can only have one column" - %Check% = False - Exit Function - end if - - ' and this column must have xml datatype - dim pCol : set pCol = obj.IndexColumns.Item(0).Column - if (lCase(left(pCol.Datatype, 3)) <> "xml") then - output "XML Index " & obj.Table.Name & "." & obj.Name & " column must have xml datatype" - %Check% = False - Exit Function - end if - - ' A primary XML index on an xml column must exist before a secondary XML index can be created on the column. - ' Also control that a xml column do not have two primary indexes. - if not(bPrimary) then - dim pIdxCol, bFound - bFound = False - for each pIdxCol in pCol.IndexColumns - if ((pIdxCol.Index.Type = "XML") and C2Bool(pIdxCol.Index.GetExtendedAttribute("XMLPrimary"))) then - if (bFound) then - output "Column " & pCol.Table.Name & "." & pCol.Name & " cannot have two PRIMARY XML indexes" - %Check% = False - Exit Function - else - bFound = True - end if - end if - next - if not(bFound) then - output "A primary XML index on the xml column must exist before secondary XML index " & obj.Table.Name & "." & obj.Name & " can be created." - %Check% = False - Exit Function - end if - end if - - ' You cannot create a primary XML index on a computed xml column. - if (pCol.Computed and bPrimary) then - output "You cannot create primary XML Index " & obj.Table.Name & "." & obj.Name & " on a computed xml column." - %Check% = False - Exit Function - end if - %Check% = True - else - %Check% = not bPrimary - end if - -End Function - -Function %Fix%(obj, outmsg) - ' Implement your automatic correction on <obj> here - ' filling <outmsg> as you wish - ' and return True if successful. - - outmsg = "Automatic correction not implemented" - - %Fix% = False -End Function - - - - -TemplateTargetItem -78FB4E2D-F26B-47DE-AB37-6BE2F9CA62E8 -Templates -0 - -1328013296 -mledier -1 - - -0C5E4431-77E2-4061-A9C9-A434C0E66718 -isXML -.bool(%Type% == XML) -0 - -0 - -1 - - -F1D3A019-D278-47DD-898C-247D383D345A -isRelational -.bool(%Type% == "") -0 - -0 - -Returns if an index is a relational index -1 - - -B13B1681-73AB-4FFB-A155-C882D4112CF2 -isFullText -false -0 - -0 - -1 - - -7475DAEA-9F1C-4440-8449-D55F600B186B -primaryXMLIndexList -List -.foreach_item(Table.Indexes,,, %isXML%) - .if (%isPrimaryXMLIndex%) -%ObjectID% \t %DisplayName% - .endif -.next(\n) -0 - -1328004989 -mledier -Enumerates primary indexes of the table -1 - - -469A54A3-2753-47FA-A337-9C24A922A644 -fillFactorList -.vbscript -dim obj -for each obj in ActiveSelection -scriptresult = obj.GetPhysicalOptionValues("with/fillfactor=") -next -.endvbscript -1328013275 -mledier -1328013296 -mledier -List of values for the fill factor -1 - - - - -CriterionTargetItem -6B8183FC-4305-4719-B32C-37E31273E90D -Criteria -0 - -0 - -1 - - -0D2B60F5-379F-46CC-A7DA-2055916E16B0 -IsXMLIndex -%isXML% -0 - -1328004199 -mledier -1 - - -ExtendedAttributeTargetItem -A1360CBE-9A4A-46BD-A291-4A3012FD4BC7 -Extended Attributes -0 - -0 - -1 - - -6EF9AF42-2538-4ACD-82BC-4C2CB1793B06 -XMLPrimary -0 - -0 - -True when xml index is the primary xml index -1 -0 - - - - -TemplateTargetItem -623BBE34-2342-4B5A-9B37-E7C7D706603D -Templates -0 - -1328004989 -mledier -1 - - -C61DE486-CBEF-4A0B-B3B4-4082FA221A2C -isPrimaryXMLIndex -.bool(%XMLPrimary%) -0 - -0 - -Returns if the XML index is primary -1 - - - - -CriterionTargetItem -311B56C5-0F8E-49AD-BECB-8BBCEB36F387 -Criteria -0 - -0 - -1 - - -845B3CC2-85B0-4734-A6B9-64FE109EF5F8 -IsSecondaryXMLIndex -%isPrimaryXMLIndex% == false -0 - -0 - -1 - - - - - -ExtendedAttributeTargetItem -7A815490-5CF1-487C-8198-CAB278200974 -Extended Attributes -0 - -0 - -1 - - -D22BD232-16D8-449A-9B51-3AD790203842 -PrimaryXMLIndex -0 - -0 - -Specifies the primary XML index to use in creating a secondary XML index. -1 -15 -%primaryXMLIndexList% -380BAA42-0042-11D2-BD28-00A02478ECC9 -0 - - - - - -16EB6B6B-0343-4386-A0F2-C27070424242 -SecondaryXMLIndexType -0 - -0 - -Specifies the type of secondary XML index -1 -12 -VALUE ; PATH ; PROPERTY -1 -0 - - - - -ExtendedCollectionTargetItem -380FA059-C582-4EF8-B32F-8AA8521FFBAC -Extended Collections -0 - -0 - -1 - - -E44F8BB8-F75F-4AE6-99BE-50E38503B276 -PrimaryXMLIndex -0 - -0 - -1 -380BAA42-0042-11D2-BD28-00A02478ECC9 - - - - - - - - - - -48D7081F-6251-44A8-B07A-303FC2D3EF79 -IsRelationalIndex -%isRelational% -0 - -1328004199 -mledier -1 - - -ExtendedAttributeTargetItem -3A0EBE06-A6B7-4AE5-A157-D90A45EA8D2C -Extended Attributes -0 - -1328011320 -mledier -1 - - -02E101A1-E371-4C70-A731-30FB0D454389 -FileGroup -0 - -0 - -Name of the filegroup -1 -12 -%fileGroupList% -1 -Function %Get%(obj) - %Get% = obj.GetPhysicalOptionValue("on/<filegroup>") -End Function -Sub %Set%(obj, value) - obj.SetPhysicalOptionValue "on/<filegroup>", value - if (value <> "") then - obj.DeletePhysicalOption "on/<partition scheme>" - end if -End Sub -0 - - -7A7F0817-CFC2-49B5-93AC-B7B079EA3560 -PartitionScheme -0 - -0 - -Name of the partition scheme -1 -12 -%partitionSchemeList% -1 -Function %Get%(obj) - %Get% = obj.GetPhysicalOptionValue("on/<partition scheme>") -End Function -Sub %Set%(obj, value) - if ((value <> "") and (obj.HasPhysicalOption("on/<partition scheme>/<column>") = False)) then - obj.SetPhysicalOptionValue "on/<partition scheme>/<column>", "?" - end if - obj.SetPhysicalOptionValue "on/<partition scheme>", value - if (value <> "") then - obj.DeletePhysicalOption "on/<filegroup>" - end if -End Sub -0 - - -A433BE24-5AA2-4ECD-93C0-A9643589A155 -PartitionSchemeColumn -0 - -0 - -Partitioned column -1 -12 -%partitionSchemeColumnList% -1 -Function %Get%(obj) - %Get% = obj.GetPhysicalOptionValue("on/<partition scheme>/<column>") -End Function -Sub %Set%(obj, value) - obj.SetPhysicalOptionValue "on/<partition scheme>/<column>", value -End Sub -0 - - -D1066375-249D-42B3-B622-039C2C7C1A19 -Online -1327657771 -mledier -1328011320 -mledier -Specifies whether underlying tables and associated indexes are available for queries and data modification during the index operation. -1 -1 -Function %Get%(obj) - ' Default is off, check "on" value - %Get% = (LCase(obj.GetPhysicalOptionValue("with/online=")) = "on") -End Function -Sub %Set%(obj, value) - ' default is off - if(value) then - obj.SetPhysicalOptionValue "with/online=", "on" - else - obj.DeletePhysicalOption "with/online=" - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - end if -End Sub -0 - - -AE99C299-70AD-414F-AECE-4A3A3B36D42C -IgnoreDupKey -1327657771 -mledier -1328011320 -mledier -Specifies the error response to duplicate key values in a multiple-row INSERT transaction on a unique clustered or unique nonclustered index. When ON and a row violates the unique index, a warning message is issued and only the rows violating the UNIQUE index fail. -1 -1 -Function %Get%(obj) - ' Default is off, check "on" value - %Get% = (LCase(obj.GetPhysicalOptionValue("with/ignore_dup_key=")) = "on") - -End Function -Sub %Set%(obj, value) - ' default is off - if(value) then - obj.SetPhysicalOptionValue "with/ignore_dup_key=", "on" - else - obj.DeletePhysicalOption "with/ignore_dup_key=" - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - end if -End Sub -0 - - - - -TemplateTargetItem -9C01E28F-DB4D-4818-91C3-A78AE0679B58 -Templates -0 - -0 - -1 - - -61544C8F-0A60-4338-9924-41E68747B2E0 -fileGroupList -List -DEFAULT\n -.foreach_item(Model.Storages) -%Code% \t %GeneratedName% -.next(\n) -0 - -0 - -Lists filegroups (Storages) of the model -1 - - -BCC4A205-7CB2-40D0-9C5C-A8C529871AD9 -partitionSchemeColumnList -List -.foreach_item(Parent.Columns) -%ShortGeneratedName% -.next(\n) -0 - -0 - -Lists columns of the table -1 - - -A2F3C213-7213-48BF-90D6-9314399BB3C2 -partitionSchemeList -List -.foreach_item(Package.ExtendedObjects,,,%Stereotype%==PartitionScheme) -%PARTITIONSCHEME% -.next(\n) -0 - -0 - -Lists partition schemes of the model -1 - - - - -CriterionTargetItem -22F1A0D7-F8A0-407B-9AB7-4999F1FB4890 -Criteria -0 - -0 - -1 - - -4D6DF339-0025-4270-912A-C20F54BCD706 -IsNotClusteredIndex -%Clustered% == false -0 - -0 - -1 - - - - - -ExtendedCollectionTargetItem -2971EBB3-B73B-4154-A832-EC08A461050F -Extended Collections -0 - -0 - -1 - - -6A8BBFE9-3E70-4828-996A-CB2395BA150A -Include -0 - -0 - -Specifies the non key columns to be added to the leaf level of the nonclustered index. -1 -Including Indexes -380BAA41-0042-11D2-BD28-00A02478ECC9 - - - - -ExtendedAttributeTargetItem -E70545DE-1209-4E6C-BF27-C3F4233D5F86 -Extended Attributes -0 - -0 - -1 - - -49285D91-ABC7-4051-988A-FA0348157A84 -IncludedColumns -0 - -0 - -List of columns included in the non clustered index -1 -14 -1 -Function %Get%(obj) - Dim includedColumns, sIncludedColumns - set includedColumns = obj.GetExtendedCollection("Include", False) - - ' Build string with the list of included column - Dim column - sIncludedColumns = "" - If not (includedColumns is nothing) Then - For Each column in includedColumns - If (sIncludedColumns <> "") Then - sIncludedColumns = sIncludedColumns & "," - End If - sIncludedColumns = sIncludedColumns & column.ShortGeneratedName - Next - End If - - %Get% = sIncludedColumns -End Function -Sub %Set%(obj, value) - Dim includedColumns - - set includedColumns = obj.GetExtendedCollection("Include", (value <> "")) - - If Not (includedColumns is Nothing) Then - - ' Clear the list of included columns - includedColumns.Clear - - ' Build a map of table column - Dim mapTableColumns - Set mapTableColumns = CreateObject("Scripting.Dictionary") - If Not (mapTableColumns is Nothing) Then - Dim column - For each column in obj.Table.Columns - mapTableColumns.Add column.ShortGeneratedName, column - Next - - Dim aIncludedColumns - aIncludedColumns = Split(value,",") - - Dim sIncludedColumn, i - For i = LBound(aIncludedColumns) to UBound(aIncludedColumns) - sIncludedColumn = aIncludedColumns(i) - sIncludedColumn = Trim(sIncludedColumn) - If (mapTableColumns.Exists(sIncludedColumn)) Then - includedColumns.Add mapTableColumns.Item(sIncludedColumn) - End If - Next - End If - End If -End Sub -0 - - - - -FormTargetItem -6772922A-EF9A-45F2-9C48-AA5D393497A9 -Forms -0 - -0 - -1 - - -27A0C991-1B57-4753-873F-5B6ADAE6B5C6 -IncludedColumns -<Form > - <ExtendedAttribute Attribute="IncludedColumns" Caption="Included columns:" AttributeID="{49285D91-ABC7-4051-988A-FA0348157A84}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" /> -</Form> - -0 - -0 - -Dialog box to avoid display of included columns extended attribute in list of extended attributes -1 -Dialog - - - - - - - - - - - - -ExtendedAttributeTargetItem -ADF2DB68-BCF9-41E7-853E-8C3C97FC9E9B -Extended Attributes -0 - -1328015988 -mledier -1 - - -7B83F55C-3ED5-4C6D-A483-14A88519354C -DropExisting -0 - -1303198737 -Marc -Specifies that the named, preexisting clustered, nonclustered, or XML index is dropped and rebuilt. -1 -1 -Function %Get%(obj) - Dim bDropExisting - bDropExisting = False - If (obj.GetPhysicalOptionValue("with/drop_existing=") = "on") Then - bDropExisting = True - Else - bDropExisting = False - End If - %Get% = bDropExisting -End Function -Sub %Set%(obj, value) - If (value = True) Then - obj.SetPhysicalOptionValue "with/drop_existing=","on" - Else - obj.DeletePhysicalOption "with/drop_existing=" - End If -End Sub -Drop if exist -0 - - -5E1796FF-7302-4B94-B715-8EF9CB0D3D01 -AllowPageLocks -1327657414 -mledier -1328011320 -mledier -When ON, page locks are allowed when you access the index. The Database Engine determines when page locks are used. When OFF, page locks are not used. -1 -1 -Function %Get%(obj) - 'default is on. Check "off" value - %Get% = (LCase(obj.GetPhysicalOptionValue("with/allow_page_locks=")) <> "off") -End Function -Sub %Set%(obj, value) - ' default is on - if(value) then - obj.DeletePhysicalOption "with/allow_page_locks=" - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - else - obj.SetPhysicalOptionValue "with/allow_page_locks=", "off" - end if -End Sub -0 - - -371C7CA9-6F27-4C54-A04A-DB0C420EE222 -AllowRowLocks -1327657706 -mledier -1328011320 -mledier -When ON, row locks are allowed when you access the index. The Database Engine determines when row locks are used. When OFF, row locks are not used. -1 -1 -Function %Get%(obj) - 'default is on. Check "off" value - %Get% = (LCase(obj.GetPhysicalOptionValue("with/allow_row_locks=")) <> "off") - -End Function -Sub %Set%(obj, value) - ' default is on - if(value) then - obj.DeletePhysicalOption "with/allow_row_locks=" - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - else - obj.SetPhysicalOptionValue "with/allow_row_locks=", "off" - end if -End Sub -0 - - -8DDC82D2-F6F5-46E5-901F-2BE84224E9E2 -FillFactor -1327673589 -mledier -1328011320 -mledier -Specifies how full SQL Server should make each index page used to store the index data. -1 -12 -%fillFactorList% -1 -Function %Get%(obj) - dim s - s = obj.GetPhysicalOptionValue("with/fillfactor=") - if(s = "") then - s = obj.GetPhysicalOptionValue("with fillfactor=") - end if - %Get% = s -End Function -Sub %Set%(obj, value) - ' By default, set the one in parenthesis - obj.DeletePhysicalOption "with fillfactor=" - if(value <> "") then - obj.SetPhysicalOptionValue "with/fillfactor=", value - else - obj.DeletePhysicalOption "with/fillfactor=" - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - end if -End Sub -0 - - -E42130AB-7C5A-4F0E-9AC2-F9A3332A5C5A -MaxDOP -1327674734 -mledier -1328011320 -mledier -Overrides the max degree of parallelism configuration option for the duration of the index operation. For more information, see max degree of parallelism Option. Use MAXDOP to limit the number of processors used in a parallel plan execution. -1 -12 -1 -Function %Get%(obj) - - %Get% = obj.GetPhysicalOptionValue("with/maxdop=") - -End Function -Sub %Set%(obj, value) - if(value <> "") then - obj.SetPhysicalOptionValue "with/maxdop=", value - else - obj.DeletePhysicalOption "with/maxdop=" - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - end if -End Sub -Max degree of parallelism -0 - - -1058C51C-CEA6-4F4B-9EF9-5C31524ACD88 -PadIndex -1327657771 -mledier -1328011320 -mledier -When ON, the percentage of free space specified by FILLFACTOR is applied to the intermediate level pages of the index. When OFF or a FILLFACTOR value it not specified, the intermediate level pages are filled to near capacity leaving enough space for at least one row of the maximum size the index can have, considering the set of keys on the intermediate pages. -1 -1 -Function %Get%(obj) - ' Default is off, check "on" value - %Get% = (LCase(obj.GetPhysicalOptionValue("with/pad_index=")) = "on") -End Function -Sub %Set%(obj, value) - ' default is off - if(value) then - obj.SetPhysicalOptionValue "with/pad_index=", "on" - else - obj.DeletePhysicalOption "with/pad_index=" - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - end if -End Sub -Index padding -0 - - -5E89B64D-BD63-4BE6-B33F-792F70B0E700 -SortInTempDB -1327657771 -mledier -1328011320 -mledier -Specifies whether to store sort results in tempdb. -1 -1 -Function %Get%(obj) - ' Default is off, check "on" value - %Get% = (LCase(obj.GetPhysicalOptionValue("with/sort_in_tempdb=")) = "on") -End Function -Sub %Set%(obj, value) - ' default is off - if(value) then - obj.SetPhysicalOptionValue "with/sort_in_tempdb=", "on" - else - obj.DeletePhysicalOption "with/sort_in_tempdb=" - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - end if -End Sub -0 - - -34B6D976-1F0B-4740-83A4-608C05AB4108 -StatisticsNorecompute -1327657771 -mledier -1328011320 -mledier -When ON, out-of-date index statistics are not automatically recomputed. When OFF, automatic statistics updating are enabled. -1 -1 -Function %Get%(obj) - ' Default is off, check "on" value - %Get% = (LCase(obj.GetPhysicalOptionValue("with/statistics_norecompute=")) = "on") - -End Function -Sub %Set%(obj, value) - ' default is off - if(value) then - obj.SetPhysicalOptionValue "with/statistics_norecompute=", "on" - else - obj.DeletePhysicalOption "with/statistics_norecompute=" - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - end if -End Sub -0 - - -2CB5D983-75E8-4525-9883-FF8E93A0864C -WithCompression -1328015867 -mledier -1328015988 -mledier -Compression option -1 -14 -1 -Function %Get%(obj) - %Get% = obj.GetPhysicalOptionSql("with/<compression>/") -End Function -Sub %Set%(obj, value) - - dim rxp, mch, partlist, str - - partlist = array("", "") - set rxp = new regexp - rxp.global = true - rxp.IgnoreCase = true - str = replace(replace(replace(value, vbTab, " "), vbLf, " "), vbCr, " ") - - rxp.Pattern = "data_compression\s*=\s*(none|row|page)\s+on\s+partitions\s*\(([^)]+)\)" - - for each mch in rxp.Execute(str) - select case(LCase(rxp.Replace(mch, "$1"))) - case "row" : partlist(0) = partlist(0) & ", " & rxp.Replace(mch, "$2") - case "page" : partlist(1) = partlist(1) & ", " & rxp.Replace(mch, "$2") - end select - next - dim result - result = "" - if(partlist(0) <> "") then - result = result & ", data_compression = row on partitions (" & Mid(partlist(0), 3) & ")" - end if - if(partlist(1) <> "") then - result = result & ", data_compression = page on partitions (" & Mid(partlist(1), 3) & ")" - end if - if(result <> "") then - obj.SetPhysicalOptionSql "with/<compression>", mid(result, 3) - else - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - end if -End Sub -Compression -0 - - - - -FormTargetItem -36B0CD63-A4B5-438B-A94A-56CA9A471729 -Forms -1328003936 -mledier -1328004199 -mledier -1 - - -CBCFD46D-B029-4627-B575-B8128844ACE0 -Microsoft -<Form > - <ExtendedAttribute Attribute="FileGroup" Caption="Filegroup:" AttributeID="{02E101A1-E371-4C70-A731-30FB0D454389}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="XMLPrimary" Caption="Primary" AttributeID="{6EF9AF42-2538-4ACD-82BC-4C2CB1793B06}" GrayHiddenAttributes="No" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="PrimaryXMLIndex" Caption="Primary index:" AttributeID="{D22BD232-16D8-449A-9B51-3AD790203842}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="SecondaryXMLIndexType" Caption="Secondary XML index type:" AttributeID="{16EB6B6B-0343-4386-A0F2-C27070424242}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <HorizontalLayout Name="PartitionSchemeLayout" > - <ExtendedAttribute Attribute="PartitionScheme" Caption="Partition scheme:" AttributeID="{7A7F0817-CFC2-49B5-93AC-B7B079EA3560}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="PartitionSchemeColumn" Caption="Column:" AttributeID="{A433BE24-5AA2-4ECD-93C0-A9643589A155}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - </HorizontalLayout> - <Separator Name="Separator1" /> - <HorizontalLayout Name="ColumnLayout" > - <Space Name="Space1" Width="80" Height="6" HorizontalResize="No" VerticalResize="No" /> - <VerticalLayout Name="RowLayout1" > - <ExtendedAttribute Attribute="AllowPageLocks" Caption="Allow page locks" AttributeID="{5E1796FF-7302-4B94-B715-8EF9CB0D3D01}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="AllowRowLocks" Caption="Allow row locks" AttributeID="{371C7CA9-6F27-4C54-A04A-DB0C420EE222}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="SortInTempDB" Caption="Sort in tempdb" AttributeID="{5E89B64D-BD63-4BE6-B33F-792F70B0E700}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - </VerticalLayout> - <VerticalLayout Name="RowLayout2" > - <ExtendedAttribute Attribute="IgnoreDupKey" Caption="Ignore dup key" AttributeID="{AE99C299-70AD-414F-AECE-4A3A3B36D42C}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="PadIndex" Caption="Index padding" AttributeID="{1058C51C-CEA6-4F4B-9EF9-5C31524ACD88}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="StatisticsNorecompute" Caption="Statistics no recompute" AttributeID="{34B6D976-1F0B-4740-83A4-608C05AB4108}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - </VerticalLayout> - <VerticalLayout Name="VerticalLayout1" > - <ExtendedAttribute Attribute="Online" Caption="Online" AttributeID="{D1066375-249D-42B3-B622-039C2C7C1A19}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="DropExisting" Caption="Drop existing" AttributeID="{7B83F55C-3ED5-4C6D-A483-14A88519354C}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - </VerticalLayout> - </HorizontalLayout> - <HorizontalLayout Name="HorizontalLayout1" > - <ExtendedAttribute Attribute="FillFactor" Caption="Fill factor" AttributeID="{8DDC82D2-F6F5-46E5-901F-2BE84224E9E2}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="MaxDOP" Caption="Max degree of parallelism" AttributeID="{E42130AB-7C5A-4F0E-9AC2-F9A3332A5C5A}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - </HorizontalLayout> - <ExtendedAttribute Attribute="IncludedColumns" Caption="Included columns" AttributeID="{49285D91-ABC7-4051-988A-FA0348157A84}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="1" HorizontalResize="Yes" VerticalResize="No" /> - <Separator Name="Separator" /> - <ExtendedAttribute Attribute="WithCompression" Caption="Compression" AttributeID="{2CB5D983-75E8-4525-9883-FF8E93A0864C}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" /> -</Form> - -0 - -1328865858 -mledier -Specific page for Microsoft SQL Server -1 -24917 - - - - -MethodTargetItem -FE6A3191-7FAE-4110-9D2F-D2EE3AA67140 -Methods -1328012204 -mledier -1328012388 -mledier -1 - - -30F0E2EA-21DF-4CAE-8A79-FAD8CD21E908 -CleanWithOption -1327658775 -mledier -1328012388 -mledier -1 -Sub %Method%(obj) - - Dim s - s = obj.GetPhysicalOptionSql("with") - if ((s = "with") or (s = "")) then - obj.DeletePhysicalOption("with") - end if - -End Sub - - - - - - -B7E0E742-8299-49A5-A1BA-988CE66E69B3 -ExtendedObject -0 - -0 - -1 -1 - - -StereotypeTargetItem -8F4846AB-06C5-420E-ACA2-93F4EBD82E70 -Stereotypes -0 - -1299595242 -Marc -1 - - -A3E74DEC-29E2-4CA2-8485-4E769088B8BE -PartitionFunction -0 - -0 - -Defines a partition function -1 -Partition Function -Partition Functions -0D0000000800000008000000424D7E000000000000003E000000280000001000 -0000100000000100010000000000400000000000000000000000000000000000 -000000000000FFFFFF00FFFF0000FF000000FF000000FF000000010000000100 -000001000000000F000000FF0000010000000100000001000000FF000000FF00 -0000FF0F0000FFFF0000424D3605000000000000360400002800000010000000 -100000000100080000000000000100000000000000000000000000004D000000 -000000000E9FD0001CA8D90023AAD90029B4DA0033A5CC0036B7E50037B7E500 -37B8E5003BA7CA003CA4C4003EBFE5003FA3BE003FA7C6003FBAE50040A4C100 -41B9DE00449FB50047A1B7004AA0B3004ABCE5004FC0E4005FC8E60062CCFE00 -63CCFE0065CDFE0065CEFE0066CDFE0067D1FE0068CDFE0069D2FE006BD0FE00 -6DCFFE0071D0FE0073D1FE0075D2FE0076D3FE0078D2F3007CD4FE007ED6FE00 -85D8FE0086D8FE0087DAFE008CDCFE0090DDFE0091DFFE009BE3FE009CE4FE00 -9D4E0000A0743C00A37A4600A4E9FC00A9EBFE00BD712600C0824300CC650000 -F0963C00F0A05000F19B4500F19B4600F2A25400F3A04F00F3A15000F3A65C00 -F4A55600F5AA5F00F5AE6800F5B06B00F6AF6900F8BD8200F9C59200FACEA300 -FBD8B300FCDEC000FCDFC200FDE4CC00FDEAD800000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000506060606060602 -00000000000000000A1A17171717170600000000000000000C242221201B1706 -3530303030303100302A2929292824073043373F3C393000112F2E2514141403 -304140373A3830000D3334150B0B0B04304544373D3B30300910160100000000 -30483737373E30300000000000000000304A483746423000131E1C1C1C1C1C08 -304C4B4937473000121F1D1B1817170636303030303032003027262626231906 -00000000000000000F2D2C2C2C2C2B0E00000000000000000D3334150B0B0B04 -0000000000000000091016010000000000000000000000000000000000000000 -0E0000000100000020000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00FFFFFFFFFFFFFFFFFFFF8000FFFF8000FFFF8000FFFF -8000FFFF8000FFFF8000FFFF0000000200000002800000028000000280000002 -8001000280FF0000FFFF0000FFFF000280000002800000028000000280000002 -800000020000FFFF0000FFFF8000FFFF8000FFFF8000FFFF8000FFFF8001FFFF -80FFFFFFFFFFFFFFFFFF424D3608000000000000360400002800000020000000 -2000000001000800000000000004000000000000000000000000000081000000 -000000000098CD000EA2D10010A1D30013A2D3003BB6DC0047BCDF0062CCFE00 -63CCFE0065CDFE0066CDFE0067D1FE0069D1FE006ACEFE006CCFFE006CD6FE00 -6DCFFE006DD6FE006FD0FE006FD8FE0070D0FE0070D1FE0071DBFE0074D5FE00 -76D2FE0078D3FE0079D5EF007CD7FE007DD6FE007DE6FE007FD5FE0081D6FE00 -85DAFE008BDBFE008DE3FE008EDEFE0095E0FE0098E3FE0098FFFF009CE6F800 -9D4E0000A2E7FE00A3E8FE00A4E9FE00B0EFFE00BFF8FF00C7772900CA833D00 -CC650000CE935900E1C2A500F0953C00F0994300F1984100F1994100F19A4500 -F19B4600F1A85F00F29D4900F29E4A00F29E4B00F2AC6600F39F4D00F3A04E00 -F3A04F00F3A14F00F3A15000F3A25200F3A35300F3AD6900F3AE6A00F4A35400 -F4A45400F4A45500F4A55700F4A65800F4AF6C00F4B06E00F4B16F00F5A85C00 -F5AB6200F5B27100F5B37300F5B47500F5B57600F5B67800F6AA5F00F6AA6000 -F6AD6400F6AD6500F6B06A00F6B77900F6B97C00F7B06900F7BB8000F8BF8700 -F8BF8800F8C08800F8C08900F8C28D00F9C38E00F9C79400F9C89500FAC59100 -FACB9C00FACC9D00FAD0A400FAD0A500FBCEA100FBD2A800FBD4AC00FBD8B400 -FCD8B300FCDCBB00FCDCBC00FCDFC100FDE0C200FDE2C800FDE3CA00FDE4CC00 -FDE5CE00FEE7D000FEE7D200FEE8D300FEEAD700FEEAD800FEEDDC00FEEFE100 -FEF2E50000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000010101010101010101010101010101 -000000000000000000000000000000000001130F0F0F0F0F0F0F0F0F0F0F1601 -0000000000000000000000000000000000010B07070707070707070707070F01 -0000000000000000000000000000000000010C07070707070707070707070F01 -000000000000000000000000000000000001171414100E0D0A07070707070F01 -0000000000000000000000000000000000011B19191919191918120907070F01 -000000000000000000000000000000002801201F1F1F1F1F1F1F1F1E15081101 -28282828282828282828282828280028280123212121212121212121211C1301 -282F5E5C5B54524E4C453D392E28002800012524242424242424242424242201 -285F3030433E3A37303330343928002800012B2929292A040101010101010101 -2864563030433F30303530303D2800280001272C2C2C1A031D1D1D1D1D1D0226 -28675857304A44303B383630462800280001062D2D2D05010101010101010100 -286C5D59304F4A30403B38304D28002800010101010101010000000000000000 -28776D66305A4F3047413C305128282800000000000000000000000000000000 -287A7030303063304B4842305328282800000000000000000000000000000000 -287C7170306A6830304F30305528002800010101010101010101010101010101 -287E7471306E6A68306030495C2800280001130F0F0F0F0F0F0F0F0F0F0F1601 -287F767430306E6A686560505E28002800010B07070707070707070707070F01 -288079767430306E6B6965616228002800010C07070707070707070707070F01 -2832807F7E7D7B787573726F312800280001171414100E0D0A07070707070F01 -2828282828282828282828282828002828011B19191919191918120907070F01 -000000000000000000000000000000002801201F1F1F1F1F1F1F1F1E15081101 -00000000000000000000000000000000000123212121212121212121211C1301 -0000000000000000000000000000000000012524242424242424242424242201 -0000000000000000000000000000000000012B2929292A040101010101010101 -000000000000000000000000000000000001272C2C2C1A031D1D1D1D1D1D0226 -000000000000000000000000000000000001062D2D2D05010101010101010100 -0000000000000000000000000000000000010101010101010000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 - -1 -PartitionFunction -1 - - -ExtendedAttributeTargetItem -93541185-F4D5-42D9-B9D3-EA3CF6746051 -Extended Attributes -0 - -0 - -1 - - -22EEF2DB-CAAE-43CD-B961-CD7F19CEF421 -InputParameterType -0 - -0 - -Data type of the column used for partitioning. All data types are valid for use as partitioning columns, except text, ntext, image, xml, timestamp, varchar(max), nvarchar(max), varbinary(max), alias data types, or CLR user-defined data types. -1 -12 -%getTypeList% -0 - - -BB0D1645-D1EE-41A4-8624-33C298E8D929 -IntervalSide -0 - -0 - -Specifies to which side of each boundary value interval, left or right, the boundary_value [ ,...n ] belongs, when interval values are sorted by the Database Engine in ascending order from left to right. If not specified, LEFT is the default. -1 -12 -LEFT -LEFT;RIGHT -1 -0 - - -028D4CD4-C5A3-4A02-BB04-C8C8B884954A -BoundaryValues -0 - -0 - -Specifies the boundary values for each partition of a partitioned table or index. All values must be separated by a comma. -We must provide a default value equal to (xxx, xxx, xxx) in comment to describe what kind of value must be typed. - -1 -12 -0 - - -EDAA1D9A-227F-4820-8E91-E062E590172B -DisplayType -0 - -0 - -Complete datatype with length and precision (for script generation and reverse engineering purpose) -1 -12 -1 -Function %Get%(obj) - Dim sGet - sGet = obj.GetExtendedAttribute("%CurrentTargetCode%.InputParameterType") - if (sGet = "") then - %Get% = "" - else - Dim sLP - if CBool(obj.EvaluateTemplateFor("needLength")) then - sLP = obj.GetExtendedAttribute("%CurrentTargetCode%.InputParameterLength") - if (sLP = "") then - %Get% = sGet - else - sGet = sGet & "(" & sLP - if CBool(obj.EvaluateTemplateFor("needPrec")) then - sLP = obj.GetExtendedAttribute("%CurrentTargetCode%.InputParameterPrec") - if (sLP <> "") then - sGet = sGet & ", " & sLP - end if - end if - %Get% = sGet & ")" - end if - else - %Get% = sGet - end if - end if -End Function -Sub %Set%(obj, value) - Dim pos, sType, sLP - ' reset attributes (dec supports length & precision) - obj.SetExtendedAttribute "%CurrentTargetCode%.InputParameterType", "dec" - obj.SetExtendedAttribute "%CurrentTargetCode%.InputParameterLength", "" - obj.SetExtendedAttribute "%CurrentTargetCode%.InputParameterPrec", "" - - pos = InStr(value, "(") - if (pos = 0) then - sType = value - sLP = "" - else - sType = Left(value, pos - 1) - sLP = replace(Mid(value, pos + 1), ")", "") - end if - obj.SetExtendedAttribute "%CurrentTargetCode%.InputParameterType", sType - - if (sLP <> "") then - if CBool(obj.EvaluateTemplateFor("needLength")) then - Dim LPArray - LPArray = Split(sLP, ",") - obj.SetExtendedAttribute "%CurrentTargetCode%.InputParameterLength", Trim(LPArray(0)) - if ((UBound(LPArray) > 0) and CBool(obj.EvaluateTemplateFor("needPrec"))) then - obj.SetExtendedAttribute "%CurrentTargetCode%.InputParameterPrec", Trim(LPArray(1)) - end if - end if - end if - -End Sub -0 - - - - -FormTargetItem -11623100-99AF-4EF0-B9FF-92C8D5DD4CB2 -Forms -0 - -0 - -1 - - -EDD69252-F9DE-446F-BB20-5B92C19E557A -Microsoft -<Form > - <ExtendedAttribute Attribute="InputParameterType" Caption="&amp;Input parameter type:" AttributeID="{22EEF2DB-CAAE-43CD-B961-CD7F19CEF421}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <HorizontalLayout Name="HorizontalLayout1" > - <ExtendedAttribute Attribute="InputParameterLength" Caption="Length" AttributeID="{29194D77-9165-47F9-930D-A102DFF999EF}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="InputParameterPrec" Caption="Precision" AttributeID="{C2CA28D0-4676-4EED-B548-A3D2F26543F4}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - </HorizontalLayout> - <ExtendedAttribute Attribute="IntervalSide" Caption="Interval side:" AttributeID="{BB0D1645-D1EE-41A4-8624-33C298E8D929}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" Display="Combobox" LeftText="false" /> - <ExtendedAttribute Attribute="BoundaryValues" Caption="Boundary values:" AttributeID="{028D4CD4-C5A3-4A02-BB04-C8C8B884954A}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> -</Form> - -0 - -1276605960 -obalen -1 -24959 - - -054976B0-AE57-4740-9405-418126A28193 -Hidden -<Form > - <ExtendedAttribute Attribute="DisplayType" Caption="Hidden" AttributeID="{EDAA1D9A-227F-4820-8E91-E062E590172B}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> -</Form> - -0 - -0 - -1 -Dialog - - - - -D25709A2-33B0-4C4A-9241-1F6B0675351D -Custom Symbol -0 - -1152541085 - -1 -0 -(2400, 2400) -128 -14737632 -STRN 0 Arial,8,N -DISPNAME 0 Arial,8,N -LABL 0 Arial,8,N -4 -1 -ADD555C9-7EA1-4532-B85C-437B77250D17 -1 -1 - - -CriterionTargetItem -48666D08-C42F-480C-831B-46433C135A08 -Criteria -0 - -0 - -1 - - -03486A68-2445-4A9E-8524-91DDCE001C5F -NeedLength -%needLength%==True -0 - -0 - -1 - - -ExtendedAttributeTargetItem -1271747B-1E3D-44CA-93EC-29CDC2C95243 -Extended Attributes -0 - -0 - -1 - - -29194D77-9165-47F9-930D-A102DFF999EF -InputParameterLength -0 - -0 - -Length of input parameter data type -1 -10 -Length -0 - - - - -CriterionTargetItem -91671A5F-D0ED-45D1-BA9D-07FC14A09E65 -Criteria -0 - -0 - -1 - - -98088B6A-74A5-4EE5-95F1-9E39359D928F -NeedPrec -%needPrec%==True -0 - -0 - -1 - - - - - -ExtendedAttributeTargetItem -4B77524D-6557-494F-AFDB-C6DD13C7D02C -Extended Attributes -0 - -0 - -1 - - -C2CA28D0-4676-4EED-B548-A3D2F26543F4 -InputParameterPrec -0 - -0 - -Precision of input parameter data type -1 -10 -Precision -0 - - - - - - - - -TemplateTargetItem -E426ED9C-70B8-4A5A-8FBB-CB4ACD7E04E8 -Templates -0 - -0 - -1 - - -F35ABE1E-D572-4D52-AAB2-521EEEF14B23 -needPrec -.if(%.L:InputParameterType%=="numeric") -True -.elsif(%.L:InputParameterType%=="decimal") -True -.elsif(%.L:InputParameterType%=="dec") -True -.else -False -.endif -0 - -0 - -1 - - - - - - - - -TemplateTargetItem -3384DA68-4C2B-43C7-AB16-83A33483FFFE -Templates -0 - -0 - -1 - - -6E6DEAB5-4DB1-481B-82CE-A1F2C025234A -needLength -.if(%.L:InputParameterType%=="char") -True -.elsif(%.L:InputParameterType%=="character") -True -.elsif(%.L:InputParameterType%=="numeric") -True -.elsif(%.L:InputParameterType%=="decimal") -True -.elsif(%.L:InputParameterType%=="dec") -True -.elsif(%.L:InputParameterType%=="float") -True -.elsif(%.L:InputParameterType%=="binary") -True -.elsif(%.L:InputParameterType%=="nvarchar") -True -.else -False -.endif -0 - -0 - -1 - - - - - - -FB5634EC-508A-4993-AA6C-5893129655A3 -PartitionScheme -0 - -0 - -Defines a partition scheme -1 -Partition Scheme -Partition Schemes -0D0000000800000008000000424D7E000000000000003E000000280000001000 -0000100000000100010000000000400000000000000000000000000000000000 -000000000000FFFFFF00FFFF0000FF000000FF000000FF000000FF0000000100 -000001000000000F000000FF00000100000001000000FF000000FF000000FF00 -0000FF0F0000FFFF0000424D3605000000000000360400002800000010000000 -100000000100080000000000000100000000000000000000000000005D000000 -0000000002846B00097F15000B9219000C851A000C921B000C931D000D932000 -0E8E21000E9FD000117E250014821D0014841E00178C2400188A23001A962400 -1AAD3A001AB83D001B8721001B9727001B9D2E001CA8D9001FA4B0001FA4B300 -1FA4B6001FB3470020AC380021A5B50023A7C10023AAD90025A7C00026A23800 -26A8C80027B0410028AAD40028AF4300299B3100299B320029A9C60029B4DA00 -2AA9CC002BA63C002CAA42002DB6490031B1DA0036B7E50037B7E50037B8E500 -37CD5A0038A9470039AB490039B24A0039C85A003BB34C003EBFE5003FBAE500 -41B9DE0045A34B0047A54D004ABCE5004FC0E4005FC8E60062CCFE0063CCFE00 -65CDFE0065CEFE0066CDFE0067D1FE0068CDFE0069D2FE006BD0FE006CD07900 -6DCFFE006ED17A0071D0FE0073D1FE0075D2FE0076D3FE0078D2F3007CD4FE00 -7ED6FE0085D8FE0086D8FE0087DAFE008CDCFE0090DDFE0091DFFE0094E2F500 -9BE3FE009CE4FE00A4E9FC00A9EBFE00B7F3FE00000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000222D2D2D2D2D2D15 -000000000000000020413E3E3E3E3E2D00000000000000001C4D4B4A48423E2D -00000000000000000153525252514D2E0208040D0C0802001859584E3B3B3B1D -07102B231911050017575C2C363636270614301A212A030A28383D0900000000 -1329341F33350F0A00000000000000002547313432492400164543434343432F -123A390E0B3A12001B4644423F3E3E2D000000000000000001504F4F4F4C402D -00000000000000001E565555555554370000000000000000265A5B3C36363627 -000000000000000028383D090000000000000000000000000000000000000000 -0E0000000100000020000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00FFFFFFFFFFFFFFFFFFFF8000FFFF8000FFFF8000FFFF -8000FFFF8000FFFF8000FFFF0000FFFE00000002800000028000000280000002 -8001000280FF0000FFFF0000FFFF000280000002800000028000000280000002 -8000FFFE0000FFFF0000FFFF8000FFFF8000FFFF8000FFFF8000FFFF8001FFFF -80FFFFFFFFFFFFFFFFFF424D3608000000000000360400002800000020000000 -200000000100080000000000000400000000000000000000000000007A000000 -000000000098CD0005700A0006710B000EA2D10010A1D300118A1E0011B42A00 -128D2100128D22001290210013A2D30013B5290013B52D0013B62E0014B63200 -15B6300016B735001791240017AA370017B7370018A52E0018AC390018B83900 -199A32001AB63D001B952B001C9026001CB941001CBA42001DBA43001F902900 -1F9A30001FBC3E00209C320020B9490020BB4A0021B93300229E340022BC4E00 -22BE420023912C00239C340023A4390023BA3900248D2D0024BD530026932F00 -28AA3F0029AC41002AC84D002BAD43002BC44D002CA33B002CAE44002CAF4500 -2CB146002FB54A0030B54B0030B74B0030B84C0032A5400033CB58003BB6DC00 -3EC24B0040C3500042DC6A0043DD6B0043DE6C0045E06F0046DF6F0046E06F00 -46E2700047BCDF0047D46C0050C6600054CA65005DCC670060CA6E0062CCFE00 -63CCFE0065CDFE0065CF730066CDFE0067D1FE0069D1FE006ACEFE006CCFFE00 -6CD6FE006DCFFE006DD6FE006FD0FE006FD8FE0070D0FE0070D1FE0071CF7C00 -71DBFE0074D5FE0076D2FE0077D4820078D3FE0079D5EF007CD7FE007DD6FE00 -7DE6FE007FD5FE0081D6FE0082D58A0085DAFE0089DA91008BDBFE008DE3FE00 -8EDEFE0095E0FE0098E3FE0098FFFF009CE6F800A2E7FE00A3E8FE00A4E9FE00 -B0EFFE00BFF8FF00000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000010101010101010101010101010101 -0000000000000000000000000000000000015C58585858585858585858586001 -000000000000000000000000000000000001544F4F4F4F4F4F4F4F4F4F4F5801 -000000000000000000000000000000000001554F4F4F4F4F4F4F4F4F4F4F5801 -000000000000000000000000000000000001615D5D595756534F4F4F4F4F5801 -00000000000000000000000000000000000166646464646464625B514F4F5801 -0000000000000000000000000000000002016C6A6A6A6A6A6A6A6A695E505A01 -000000000000000000000000000000020201706E6E6E6E6E6E6E6E6E6E675C01 -0202020202030202020202020202000200017271717171717171717171716F01 -021616161306392A2B2D161616020002000177757575760B0101010101010101 -02141D23093B432B18271E170E02000200017478787865056868686868680473 -021119083C442B182E241C140D0200020001497979793F010101010101010100 -020F0A314530222020202A2A0702000200010101010101010000000000000000 -0210151A48322128343E4A2A0C02020200000000000000000000000000000000 -022C12384237262020202A2A2502020200000000000000000000000000000000 -02414B1B3C4733354C4C4C4C4002000200010101010101010101010101010101 -0241524E1F3B46333D5252524002000200015C58585858585858585858586001 -024D63635F293946362D63634D0200020001544F4F4F4F4F4F4F4F4F4F4F5801 -024D6D6D6D6B2F3A2A2D6D6D4D0200020001554F4F4F4F4F4F4F4F4F4F4F5801 -020202020202020202020202020200020001615D5D595756534F4F4F4F4F5801 -00000000000000000000000000000002020166646464646464625B514F4F5801 -0000000000000000000000000000000002016C6A6A6A6A6A6A6A6A695E505A01 -000000000000000000000000000000000001706E6E6E6E6E6E6E6E6E6E675C01 -0000000000000000000000000000000000017271717171717171717171716F01 -00000000000000000000000000000000000177757575760B0101010101010101 -0000000000000000000000000000000000017478787865056868686868680473 -000000000000000000000000000000000001497979793F010101010101010100 -0000000000000000000000000000000000010101010101010000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 - -1 -PartitionScheme -1 - - -ExtendedAttributeTargetItem -07C1B158-F66C-4D3E-AF8F-7F0D8AE21D68 -Extended Attributes -0 - -0 - -1 - - -59F467AD-2659-4444-B120-7925565FE595 -PartitionFunction -0 - -0 - -Partition function using the partition scheme. Partitions created by the partition function are mapped to the filegroups specified in the partition scheme. -1 -15 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -PartitionFunction -0 - - - - - -64962812-FE54-44E8-A5B0-E04E3B982323 -AllPartitions -0 - -0 - -Specifies that all partitions map to the filegroup provided in file_group_name, or to the primary filegroup if [PRIMARY] is specified. If ALL is specified, only one file_group_name can be specified. -1 -0 - - -1E56E5EA-8200-49D1-8974-0F82DB07FB13 -FileGroups -0 - -0 - -Specifies the names of the filegroups to hold the partitions specified by Partition Function. If [PRIMARY] is specified, the partition is stored on the primary filegroup. If ALL is specified, only one file_group_name can be specified. This field must be hidden. -1 -14 -0 - - - - - - - -ExtendedCollectionTargetItem -9CB6F71D-B169-425E-BFCD-E6F29B4F90EC -Extended Collections -0 - -0 - -1 - - -E54B019A-8114-4B9B-B783-619F928D3E12 -PartitionFunction -0 - -0 - -1 -Partition Schemes -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -PartitionFunction - - - - -TemplateTargetItem -DD611F78-936E-4DA3-A119-3EBB22B3823D -Templates -0 - -0 - -1 - - -97E45B06-9A87-4DB2-90D1-033528D5AC53 -partitionFunctionList -List -.foreach_item(Package.ExtendedObjects,,,%Stereotype% == "PartitionFunction", %Item1.PARTITIONFUNCTION% > %Item2.PARTITIONFUNCTION%) -.if (%IsShortcut%) -%Shortcut.ObjectID% \t %PARTITIONFUNCTION% -.else -%ObjectID% \t %PARTITIONFUNCTION% -.endif -.next -0 - -0 - -1 - - - - -FormTargetItem -43BDDC88-6B12-43FC-B452-82D423BB503E -Forms -0 - -0 - -1 - - -E93BDA85-5FB8-4D13-BABC-F5E6A2A60B47 -Microsoft -<Form > - <ExtendedAttribute Attribute="PartitionFunction" Caption="Partition function:" AttributeID="{59F467AD-2659-4444-B120-7925565FE595}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="AllPartitions" Caption="All partitions" AttributeID="{64962812-FE54-44E8-A5B0-E04E3B982323}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="FileGroups" Caption="File groups:" AttributeID="{1E56E5EA-8200-49D1-8974-0F82DB07FB13}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" /> -</Form> - -0 - -1276605960 -obalen -1 -24959 - - - - -4F09C7B4-9EA1-4A29-8E88-D0D276B02C6A -Custom Symbol -0 - -1152540389 - -1 -0 -(2400, 2400) -128 -14737632 -STRN 0 Arial,8,N -DISPNAME 0 Arial,8,N -LABL 0 Arial,8,N -4 -1 -1524DE65-25B1-4C87-B44C-A4B82F767B04 -1 -1 - - -EventHandlerTargetItem -5334CEB7-26CC-4A3D-86CD-FDC155C0BA9A -Event Handlers -0 - -0 - -1 - - -14335684-98AB-4963-B20E-9347FE10710F -Validate -0 - -0 - -Add dependencies to partition functions and filegroups (storages) -1 -Function %Validate%(obj, ByRef message) - - Dim depd, depdObj - - - ' Compute new dependent object list - Dim DependentObjects - set DependentObjects = CreateObject("Scripting.Dictionary") - - ' Add the partition function to the dependent object list - Dim pPartitionFunction - set pPartitionFunction = obj.GetExtendedAttribute("PartitionFunction") - If Not (pPartitionFunction is nothing) Then - DependentObjects.Add pPartitionFunction.ObjectID, pPartitionFunction - End If - ' Add file groups to the dependent object list - Dim sFileGroups - sFileGroups = obj.GetExtendedAttribute("FileGroups") - - ' Loop on all files group defined in the text - Dim arrayFileGroups, sFileGroup, fileGroup, storage, sStorage - arrayFileGroups = Split(sFileGroups, ",") - for each sFileGroup in arrayFileGroups - sFileGroup = Trim(sFileGroup) - ' search for an existing storage - for each fileGroup in obj.Model.Storages - sStorage = fileGroup.EvaluateText("%GeneratedName%") - if (sStorage = sFileGroup) then - if not (DependentObjects.Exists(fileGroup.ObjectID)) then - DependentObjects.Add fileGroup.ObjectID, fileGroup - end if - Exit For - end if - next - next - - ' Remove old dependencies - Dim DependenciesToDelete - set DependenciesToDelete = CreateObject("Scripting.Dictionary") - - For each depd in obj.ExtendedDependencies - If (depd.Stereotype = "DBCreateAfter (Computed)") Then - If (DependentObjects.Exists(depd.InfluentObject.ObjectID)) Then - DependentObjects.Remove depd.InfluentObject.ObjectID - Else - DependenciesToDelete.Add depd.ObjectID, depd - End If - End If - Next - - Dim i, col - col = DependenciesToDelete.Items - For i = 0 To DependenciesToDelete.Count -1 'Iterate the array - set depd = col(i) - if not (depd is nothing) then depd.Delete - Next - - - ' Add new dependencies - col = DependentObjects.Items - For i = 0 To DependentObjects.Count -1 'Iterate the array - set depdObj = col(i) - set depd = obj.Package.ChildExtendedDependencies.CreateNew - If not (depd is nothing) Then - set depd.DependentObject = obj - set depd.InfluentObject = depdObj - depd.Stereotype = "DBCreateAfter (Computed)" - End If - Next - - %Validate% = True -End Function - - - - -MethodTargetItem -BCEEA786-A4BF-4809-AF9D-42312F046B06 -Methods -0 - -0 - -1 - - -9200822C-CE92-473B-975E-3CFA1F6D1918 -FileGroupsEdit -Edit -0 - -0 - -1 -Sub %Method%(obj) - - Dim Mdl - Set Mdl = obj.Model - - ' Create a selection for the file groups - Dim Sel - Set Sel = Mdl.CreateSelection - If not (Sel is nothing) Then - Dim fileGroup - ' Add filegroups to the selection - For Each fileGroup in Mdl.Storages - Sel.Objects.Add fileGroup - Next - ' Display the object picker on the selectipn - Dim selObj - set selObj = Sel.ShowObjectPicker - If Not (selObj is Nothing) Then - Dim sFileGroups - - sFileGroups = obj.GetExtendedAttribute("FileGroups") - - sFileGroups = Trim(sFileGroups) - If (sFileGroups <> "") Then - sFileGroups = sFileGroups + "," - End If - - sFileGroups = sFileGroups + selObj.EvaluateText("%GeneratedName%") - - ' Set the authorization from the selected object - obj.SetExtendedAttribute "FileGroups", sFileGroups - End If - - Sel.Delete - End If - -End Sub - - - - - - - -5586EA89-A595-4A64-A7C7-346C66F161C7 -Assembly -0 - -0 - -Represents a Microsoft .NET Framework assembly -1 -Assembly -Assemblies -0D0000000800000008000000424D7E000000000000003E000000280000001000 -0000100000000100010000000000400000000000000000000000000000000000 -000000000000FFFFFF00FFFF0000E00100000001000000010000000100000001 -000000010000000000000000000000000000E0000000E0000000E0000000E000 -0000E0010000E0030000424D3605000000000000360400002800000010000000 -1000000001000800000000000001000000000000000000000000000054000000 -00000000936C6F009C6F70009F7875009F797700A07A7900A07C7B00A4717100 -AB787800AD808100B27F7F00B9868600C18E8E00C9959500DE993D00E09D4500 -E1B99400E2A34F00E6AB5E00EAB46E00EAD7BD00EEBF8100F0C49000F0C59100 -F2C89500F2CC9C00F2CC9D00F3CC9C00F3D0A500F5CE9F00F5D7B100F6D7A800 -F6D8B400F7DBB800F8DDB000F8DEC000F9D8B300F9E3CA00F9E6C000FAD8B400 -FAEFC500FAF0C700FBE7C100FBE7D000FBEFD000FBF0C900FBF2CC00FBF3CD00 -FBF3CE00FCE9D500FCECDB00FCF6D200FDDFC100FDF0E100FEDEBF00FEEDCB00 -FEEFE000FEF1E500FEF3EA00FEF6EE00FEF7DF00FFDEBD00FFDFBE00FFDFC000 -FFE0BF00FFE0C300FFE2C100FFEFCD00FFEFCF00FFF0D000FFF1D000FFF1D100 -FFF2D100FFF2D200FFF2E500FFF5EA00FFF7EF00FFF9E200FFF9F400FFFAE500 -FFFAE600FFFBE700FFFDEA00FFFEEC0000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000E0E0E0E0E0E0E0E0E0E0E0E00 -01010101010101010106144648490E0001020707070707070702054042470E00 -01080808080808080808013D3E450E00010A0A0A0A0A0A0A0A0A013F3D440E00 -010B0B0B0B0B0B0B0B0B01413F430E00010C0C0C0C0C0C0C0C0C013436370E0E -01090D0D0D0D0D0D0D090424272A0E0E01010101010101010103101B1D220E0E -0000000E4D4A382B20191716181F0E0E0000000E4F4B3931211A2F28292D0E0E -0000000E514C3A32231C2E11150F0E0E0000000E524E3B35251E3012130E0E0E -0000000E5352503C2C26330E0E0E0E000000000E0E0E0E0E0E0E0E0E0E0E0000 -0E0000000100000005000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00FE000001FE000001E0000001C0000001800000018000 -0001800000018000000180000001800000018000000180000001800000018000 -0001800000018000000180000001E0000001FE000001FE000001FE000001FE00 -0001FE000001FE000001FE000001FE000003FE000007FE00000FFE00001FFE00 -003FFE00007FFFFFFFFF424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000885E2800 -EBBE7B00EBBD7A00EBBD7A00EBBD7A00EBBD7A00EBBD7A00EBBD7A00EBBD7A00 -EBBD7A00EBBE7B00EBBE7B00ECC28100EEC48600EEC48700EEC48700EEC48700 -EEC48700EEC48700EEC48700EEC48700EEC48700EEC48700724F210000000000 -00000000000000000000000003020200040303000403030004030300885D2700 -EFCE9D00EEC48E00EEC48E00EEC48E00EEC48E00EEC48E00EEC48E00EEC48E00 -EEC48E00EEC48E00F0C59100F6D1A700FADDB500FCE0BD00FEE1C100FEE1C100 -FEE1C100FEE1C100FEE1C100FEE1C100FDE5C500FEEFCE00724E1F0000000000 -0000000000000000685C5D0083656700A5797B00A7797A00A7797A00A7797A00 -A7797A00A7797A00A7797A00A7797A00A7797A00A7797A00A7797A00A7797A00 -A7797A00A7797A00A67A7700B2887A00E2BD9C00FADDB500FDDEBE00FEE1C100 -FEE1C100FEE1C100FEE1C100FEE1C100FEE1C100FEEFCE00724E1F0000000000 -0000000062555600976F7000B4818100A8757600A26F6F00A26F6F00A26F6F00 -A26F6F00A26F6F00A26F6F00A26F6F00A26F6F00A26F6F00A26F6F00A26F6F00 -A26F6F00A26F6F00AC797900B17F7F00A1787400ECC49E00FBDAB700F9E2B800 -FCE0BF00FEE1C100FEE1C100FEE1C100FEE1C100FEEFCE00724E1F0000000000 -0000000085686900B5818100A4767700A3767700A3767700A3767700A3767700 -A3767700A3767700A3767700A3767700A3767700A3767700A3767700A3767700 -A3767700A3767700A3767700A7787900AD7D7D00BD947E00F7D2A700FBDEB900 -FBE2BB00FEE1C100FEE1C100FEE1C100FEE1C100FEEFCE00724E1F0000000000 -00000000A87D7F00AF7B7B00A2757600A2757600A2757600A2757600A2757600 -A2757600A2757600A2757600A2757600A2757600A2757600A2757600A2757600 -A2757600A2757600A2757600A2757600B17F7F009B727200F4CB9900FDDCBA00 -FBDFBA00FCE1BE00FCE0BF00FEE1C100FEE1C100FEEFCE00724E1F0000000000 -00000000AB7D7E00AA777700A9767600A9767600A9767600A9767600A9767600 -A9767600A9767600A9767600A9767600A9767600A9767600A9767600A9767600 -A9767600A9767600A9767600A9767600B17F7F009B717400F4CB9900FDDCBA00 -FEDEBD00FBDEBA00F9E2B900FDE0BF00FEE1C100FEEFCE00724E1F0000000000 -00000000AC7E7F00AA777700AA777700AA777700AA777700AA777700AA777700 -AA777700AA777700AA777700AA777700AA777700AA777700AA777700AA777700 -AA777700AA777700AA777700AA777700B27F7F009B717400F4CC9A00FDDCBA00 -FEDEBD00FEDEBD00FBDEBA00F9E3B800FCE0BF00FEEFCE00724E1F0000000000 -00000000AE808100B07E7E00B07E7E00B07E7E00B07E7E00B07E7E00B07E7E00 -B07E7E00B07E7E00B07E7E00B07E7E00B07E7E00B07E7E00B07E7E00B07E7E00 -B07E7E00B07E7E00B07E7E00B07E7E00B78585009C727500F4CC9A00FDDCBA00 -FEDEBD00FEDEBD00FEDEBD00FBDFBA00FBE0BA00FEEFCD00724E1F0000000000 -00000000AE808100B17F7F00B2808000B2808000B2808000B8858500B8858500 -B8858500B8858500B8858500B8858500B8858500B8858500B8858500B4818100 -B2808000B2808000B17F7F00B2808000B88686009C727500F4CC9A00FDDCBA00 -FEDEBF00FDDDBC00FEDEBD00FEDEBD00FEDEBD00FEEFCE00724E1F0000000000 -00000000B0828300B7858500BF8D8D00C08D8D00C08D8D00C08D8D00C08D8D00 -C08D8D00C08D8D00C08D8D00C08D8D00C08D8D00C08D8D00C08D8D00C08D8D00 -C08D8D00C08D8D00BE8C8C00B7878700BD8989009D727500F4CC9A00FDDCBA00 -FDDFBF00FDDFBF00FDDDBD00FEDEBD00FEDEBD00FDEDCD00724E1F0000000000 -00000000B1848600C69C9C00C69C9C00C69C9C00C69C9C00C69C9C00C69C9C00 -C69C9C00C69C9C00C69C9C00C69C9C00C69C9C00C69C9C00C69C9C00C69C9C00 -C69C9C00C69C9C00C69C9C00C69C9C00C79696009D737600F3CC9A00FDDCBA00 -FEE1C100FEE1C100FDDFC000FDDDBC00FEDEBD00FDEDCD00724E1F0000000000 -00000000B1848600CBA1A100CDA5A500CDA5A500CDA5A500CDA5A500CDA5A500 -CDA5A500CDA5A500CDA5A500CDA5A500CDA5A500CDA5A500CDA5A500CDA5A500 -CDA5A500CDA5A500CDA5A500CDA5A500CEA1A1009D737600F4CE9C00FDDEBE00 -FEE1C100FEE1C100FEE1C100FDDFBF00FDDDBD00FDEDCD00724E1F0000000000 -00000000B1868800D0A6A600D0A9A900D0A9A900D0A9A900D0A9A900D0A9A900 -D0A9A900D0A9A900D0A9A900D0A9A900D0A9A900D0A9A900D0A9A900D0A9A900 -D0A9A900D0A9A900D0A9A900D0A9A900C99D9D00A2797500F7D2A600FDDFBF00 -FEE2C800FDE0C100FEE1C100FEE1C100FDDFBE00FDEDCD00724E1F0000000000 -00000000674E4F00CD9C9C00D9B0B000D9B5B500D9B5B500D9B5B500D9B5B500 -D9B5B500D9B5B500D9B5B500D9B5B500D9B5B500D9B5B500D9B5B500D9B5B500 -D9B5B500D9B5B500D9B5B500D8ADAD00BD8E8E00C79F8500F9D7B200FDE0C400 -FEE2C800FDE0C500FEE1C100FEE1C100FEE1C100FDEDCE00724E1F0000000000 -000000004A414200A2807F00C7989900D7B0B000DCB8B800DCB8B800DCB8B800 -DCB8B800DCB8B800DCB8B800DCB8B800DCB8B800DCB8B800DCB8B800DCB8B800 -DCB8B800DDB8B800D4AAAA00C2919300AD867A00F0CDAA00FDDFC100FEE2C800 -FEE2C800FEE2C800FDE1C400FDE0C000FEE1C100FEEFCE00724E1F0000000000 -00000000000000002B26260074606100A47C7F00A3787A00A3787A00A3787A00 -A3787A00A3787A00A3787A00A3787A00A3787A00A3787A00A3787A00A3787A00 -A3787A00A3787A00A77E7900C69D8300EDCCA800FCDFC200FDE4CA00FEE2C800 -FEE2C800FEE2C800FEE2C800FDE0C500FEE1C100FEEFCE00724E1F0000000000 -00000000000000000000000000000000000000000000000000000000885D2500 -F9E4BD00F6D7AE00F5D7AC00F5D7AA00F5D7A900F5D7A900F5D7A900F5D3A900 -F6D2A900F6D2A900F5D3A900F7D7B000FDE0C200FDE7CE00FFEFD000FDE9CF00 -FDE1C800FEE2C800FEE2C800FEE2C800FDE0C100FEEFCE00724E1F0000000000 -00000000000000000000000000000000000000000000000000000000885D2500 -FDF2D800FDEBDA00FDEBDA00FDEBD900FDEBD800FDEBD800FDEBD800FDEBD800 -FDEBD500FEEACB00FDEBCC00FDEBCC00FEECCE00FFEFD000FFEFD000FFEFD000 -FDE4CC00FEE2C800FEE2C800FEE2C800FDE0C400FEEFCE00724E1F0000000000 -00000000000000000000000000000000000000000000000000000000885D2500 -FEF7E000FEF0E400FEF0E400FFF1E500FFF1E500FFF1E500FEEFE100FEEFE100 -FEEEE000FEEEDF00FFEFE000FFEFE000FEEEDA00FEEBCF00FDE6C800FDE0C400 -FDE0C400FDE0C400FDE0C400FDE0C400FDE0C200FEEFCE00724E1F0000000000 -00000000000000000000000000000000000000000000000000000000885D2500 -FEF7E000FFF3E800FEF1E600FEF1E600FEF0E600FFF1E500FFF1E500FFF1E500 -FEEFE300FEEFE000FFEFE000FFEFE000FEEEDA00FEE3C900F7D8B300F4D5A700 -F6D5AF00F7D7B000FBDBB600FDDDBB00FDDDBC00FDEDCD00724E1F0000000000 -00000000000000000000000000000000000000000000000000000000885D2500 -FEF7E000FFF3E800FFF3E800FFF3E800FFF3E800FEF1E500FEF0E400FFF1E500 -FFF1E500FFF1E500FEEFE100FEEEDF00FEEEDA00FEE2C200F4D0A400F1C58F00 -F0C58E00F2C89500F2C99700F2CA9A00F3CF9E00F6DEB100724E1F0000000000 -00000000000000000000000000000000000000000000000000000000885D2500 -FEF7E000FFF3E800FFF3E800FFF3E800FFF3E800FFF3E800FEF2E600FEF0E400 -FFF1E500FFF1E500FEF0E300FEEEDF00FEEEDE00FEE2C900F4CFA200F0C28C00 -F0C38D00EDBF8600EEBF8600EDBF8600F0C28E00F3D4A000724E1F0000000000 -00000000000000000000000000000000000000000000000000000000885D2500 -FEF7E100FEF6EF00FEF3EA00FEF2E800FFF3E800FFF3E800FFF3E800FFF3E800 -FEF0E500FFF1E500FFF1E500FEEFE200FFEFE000FEE3CB00F6D3AA00FDF8D500 -FEFBDB00FEF8D700FDF5D400FDF2D100FBECC800F1D79F0048371F0000000000 -00000000000000000000000000000000000000000000000000000000885D2500 -FEF8E800FFF8F100FEF7F000FEF6EF00FEF5EC00FFF3E800FFF3E800FFF3E800 -FEF2E700FFF1E500FFF1E500FFF1E500FEEEE000FEECD100F8D6AF00FDF8D500 -FEEFCE00FDEDCE00FDE0C000FDDDBC00E9B46F001B1207000000000000000000 -00000000000000000000000000000000000000000000000000000000885D2500 -FEF9E900FFF8F100FFF8F100FFF8F100FEF7F000FEF4EB00FFF3E800FFF3E800 -FFF3E800FEF0E500FFF1E500FFF1E500FEEFE100FEEED700F9D9B400FDF8D500 -FDECCA00FDE1C000FAD9B500E9B3700046301300000000000000000000000000 -00000000000000000000000000000000000000000000000000000000885D2500 -FEF9E900FFF8F100FFF8F100FFF8F100FFF8F100FEF7EF00FFF3E800FFF3E800 -FFF3E800FEF0E500FFF1E500FFF1E500FEF0E300FFEFE000F9DCBD00FDF9D600 -FDE8C800FDDDBC00E9B46F001B12070000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000885D2500 -FEF8EC00FEFAF500FEFAF600FFF8F100FFF8F100FFF8F100FEF5ED00FFF3E800 -FFF3E800FFF3E800FFF1E500FFF1E500FEF0E300FFEFE000FADCBE00FDF9D700 -FADEBB00E9B36F00463013000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000885D2500 -FEF7F000FEFAF700FEFAF700FEF7F200FFF8F100FFF8F100FEF5ED00FFF3E800 -FFF3E800FFF3E800FFF1E500FFF1E500FEF0E300FEEEDF00FDE7C800FDFAD800 -E9B671001B120700000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000885E2800 -EEC99800EECA9400EECA9400EECA9400EECA9400EECA9400EEC99000EEC99000 -EEC99000EEC99000EEC99000EEC99000EECA9000EEC98E00EEC48700EECB8E00 -48381F0000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 - -1 -Assembly -1 - - -ExtendedAttributeTargetItem -6C171D5A-D53C-457F-887E-B74C2A389171 -Extended Attributes -0 - -0 - -1 - - -8A90C9C1-B9DF-4761-B679-9D361A79A00A -Authorization -0 - -1285836027 -psitja -Specifies the name of a user or role as owner of the assembly -1 -15 -%authorizationList% -4BA9F642-DAB1-11D1-9944-006097355D9B -User -0 - - - - - - - - -9BC4FD43-C331-48FB-9C7A-8D85C4EA1EE4 -FileName -0 - -0 - -Specifies the local path or network location where the assembly that is being uploaded is located, and also the manifest file name that corresponds to the assembly. It can be expressed as a fixed string or an expression evaluating to a fixed string, with variables. -1 -3 -0 - - -BFE2118A-8530-4ED9-84BD-69CD594E9E94 -PermissionSet -0 - -0 - -Specifies a set of code access permissions that are granted to the assembly when it is accessed by SQL Server. If not specified, SAFE is applied as the default. -1 -12 -SAFE ; UNSAFE ; EXTERNAL_ACCESS -1 -0 - - -E08E117F-5F21-410A-B797-493DF9F7911F -Visibility -0 - -0 - -Indicates whether the assembly is visible for creating common language runtime (CLR) functions, stored procedures, triggers, user-defined types, and user-defined aggregate functions against it. -1 -12 -on;off -1 -0 - - -86AC9238-AC76-4FA8-BA8B-3AFD6EBFC2A1 -UncheckedData -0 - -0 - -By default, ALTER ASSEMBLY fails if it must verify the consistency of individual table rows. This option allows postponing the checks until a later time by using DBCC CHECKTABLE. -1 -false -0 - - - - -ExtendedCollectionTargetItem -751BCA08-24A2-44AB-A837-8DF1A0EF3E8F -Extended Collections -0 - -0 - -1 - - -C211D269-92AB-442B-ADFE-C90E87863C02 -Authorization -0 - -1285836027 -psitja -1 -Assemblies -4BA9F642-DAB1-11D1-9944-006097355D9B -User -NAME 80 CODE 80 - - - - - - - -FormTargetItem -4D316688-B802-49AD-B58E-494BF25C0C70 -Forms -0 - -0 - -1 - - -5289970A-17B8-431E-82B0-5CB7C5DB9C71 -Microsoft -<Form > - <ExtendedAttribute Attribute="Authorization" Caption="Authorization:" AttributeID="{8A90C9C1-B9DF-4761-B679-9D361A79A00A}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="FileName" Caption="File name:" AttributeID="{9BC4FD43-C331-48FB-9C7A-8D85C4EA1EE4}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="PermissionSet" Caption="Permission set:" AttributeID="{BFE2118A-8530-4ED9-84BD-69CD594E9E94}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="Visibility" Caption="Visibility:" AttributeID="{E08E117F-5F21-410A-B797-493DF9F7911F}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="UncheckedData" Caption="Unchecked data" AttributeID="{86AC9238-AC76-4FA8-BA8B-3AFD6EBFC2A1}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> -</Form> - -0 - -1276605960 -obalen -1 -24955 - - - - -FF7819D4-5B0E-4048-9400-B6EF3BEAE991 -Custom Symbol -0 - -1152537225 - -1 -0 -(2400, 2400) -128 -14737632 -STRN 0 Arial,8,N -DISPNAME 0 Arial,8,N -LABL 0 Arial,8,N -4 -1 -A95D024F-FCA6-4E17-AEAA-BD9E6855BCEE -1 -1 - - - - -F2F47B08-0996-47C4-8C82-FA8AEE26C71B -Aggregate -0 - -0 - -Defines an aggregation function which allows users to perform aggregation over data. -1 -Aggregate -Aggregates -0D0000000800000008000000424D7E000000000000003E000000280000001000 -0000100000000100010000000000400000000000000000000000000000000000 -000000000000FFFFFF00F0080000C0000000C0000000C0000000C0000000C000 -0000C0000000C0000000C0030000C0030000C0030000C0030000C0030000C003 -0000C0030000E0070000424D3605000000000000360400002800000010000000 -10000000010008000000000000010000000000000000000000000000A6000000 -00000000079CD00012A4D3001FA9D80025AFD90029ADE1002E93A9002FB5E000 -30AED50031ADD40031B6E00031B9DD0033B6E30034B8E10035BAE00036B7E500 -36B8E40036BAE10037B8E50037BBE10038ABCB003A3734003ABBE4003DA9C500 -3DC0E2003F54550040BEE70041A6BD004257580043BCEE0045C2E90046C7E500 -47C7E50048C8E50049C8E60049C9E6004DC2F2004ECBE9005794950058C9F600 -5B8F98005FD4F100608C7E0060D5F10060D6F00060D7EF006297A40065656500 -65CFFE0066DBF10068D2FE0069D2FE0069D5FA0069DAF5006A6969006AD6FB00 -6C6D6D006D8A77006DD7FE006EDCF9006FD8FE0070D9FE0071DAFE0072E2F700 -73735A0073E4F50075D9F40076DFFE0076E3F9007990950079E2FE007ADEF500 -7AE2FE007AE3FE007BE0F5007BE8F9007CE4FE007CE6FC007D9980007DE5FE00 -7EE6FE007EE7FE007FE8FE0082E9FE0082EAFE0082EAFF0083E6F30085C0B100 -85EDFF008686860087EFFE0087EFFF0088F0FE0088F0FF0089F1FF008AF2FF00 -8BA2A7008C7540008CF3FF008CF4FF008DF4FE008EC2CC008ED9DB008EEFF200 -8EF5FE008EF6FF008F90900090F7FF0090F8FF0093FAFF0094986F0094FBFF00 -95F9FB009AEBE3009C9C9C009DC2AA009E9A6F00A0A0A000A3B79C00A4D2B900 -A9B49400AAB29000AD8F5600ADC39F00B89A5F00B9813500B9843900BC7D2E00 -BC995700BFBEBD00C17C2A00C17D2E00C3741F00C3751E00C3761E00C5701400 -C6914700CC832900CC974100CCCCCB00CD954600CF771700D16F0B00D1731400 -D1812600D28D2B00D3700A00D3D3D300D4791C00D4D4D300D5D5D400D68F3400 -D8822600D9811D00D9821E00DA7C1500DD872700DE8F2A00DF811600E1861C00 -E1E0E000E3933400E5E5E500E78D1F00E9912000F8F8F8000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000001A0814171B84614E007E8F8500001A010C24344866986E2A06828E94 -000002443D303D4876977BA18988879C00001F583D303D48798A708CA4267F9F -0000225D463A3E487891719DA3397D9E0000235F5248512E4099809A7C8D869B -000020412C292B38159057313510928300000B4B524847593673775E3B097A74 -0000235F5242458B721C675E490F00000000235F4A60A2A5956A1956490F0000 -0000235F6596A09381752F28490F00000000235F524850545E6C6F5E490F0000 -00002369646869625E5A5C5C531200000000216D6B635B554C433C3233160000 -000004182D3F4D4F4337271D05030000000000250E0E1113110D0A071E000000 -0E0000000100000020000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00FFFFC3E1FF8001F1FC000030F8000000F0000000F000 -0008F0000008F0000008F0000008F0000008F0000008F0000000F0000000F000 -0001F0000001F000000FF000000FF000000FF000000FF000000FF000000FF000 -000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF800 -001FFE00007FFFFFFFFF424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000CC65003FCD6A08FFCC6500FFCC65001F0000000000000000 -000000000000000000000000CC65002FCC6500FFCD6A08FFCC65001F00000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000098CD2F0098CD7F0098CDAF0098CDCF0098CDFF0098CDFF0098CDFF -0098CDFF0098CDFF9A773CFFD98B3BFF987234EB0098CD7F0098CD3F00000000 -00000000000000000000000000000000CC6500CFE09849FFCE6D0D9F00000000 -0000000000000000000000000000000000000000000000000098CD0F0098CD7F -0098CDDF0D9FD3FF2BAFE2FF41BCEBFF52C7F1FF73DCFEFF78E0FEFF7DE5FEFF -82EAFFFF8FDEE0FFD78531FFD57713FF86AD94FF40C2E2FF12A3D3FF0098CDEF -0098CD7F0098CD0F0000000000000000CC65006FDA8323FFD47E2AFFCC65000F -00000000000000000000000000000000000000000098CD2F0098CDDF1FABD9FF -57CDF1FF6AD4FEFF63CDFEFF68D2FEFF6DD7FEFF73DCFEFF78E0FEFF7DE5FEFF -82EAFFFFA5B390FFE9A756FFCC6500FFBE8B40FFCD6907FFCC6500FFA5915BFF -1FABD9FF9B7131FBCC6500FFCF6E0FFFCC6500DFD2710CFFE49C47FFCC65005F -000000000000000000000000000000000098CD2F0098CDEF4CCAE9FF81E9FFFF -76DFFEFF6AD4FEFF63CDFEFF68D2FEFF6DD7FEFF73DCFEFF78E0FEFF7DE5FEFF -82EAFFFFBB8840FFE99F43FFC57820FF9DEBE0FFD2771EFFF5B95FFFCF6A05FF -AA9E70FFD2700BFFF5B85CFFD2771EFFCC65001FCC6500FFEEAA50FFCC65009F -000000000000000000000000000000000098CDAF48C8E6FF8AF2FFFF81E9FFFF -76DFFEFF6AD4FEFF63CDFEFF68D2FEFF6DD7FEFF73DCFEFF78E0FEFF7DE5FEFF -82EAFFFFCE8232FFED9B2DFFBD8A40FF96FDFFFFAFB080FFE39C4AFFE58F23FF -CC6500FFEB992BFFE39B49FF707C5CE700000000CC6500BFEA9C37FFD07216BF -000000000000000000000000000000000098CDFF89F3FBFF8AF2FFFF81E9FFFF -76DFFEFF6AD4FEFF63CDFEFF68D2FEFF6DD7FEFF73DCFEFF78E0FEFF7DE5FEFF -82EAFFFFD37A22FFE88D18FFB69D60FF96FDFFFF96F1F0FFC57B27FFF3AA44FF -E28210FFF3AC49FFC17827FF0C95C1FF00000000CC6500BFEF951CFFD37921FF -000000000000000000000000000000000098CDFF92F9FFFF8AF2FFFF81E9FFFF -76DFFEFF6AD4FEFF63CDFEFF68D2FEFF6DD7FEFF73DCFEFF78E0FEFF7DE5FEFF -82EAFFFFD37A22FFE5850EFFAEAF80FF96FDFFFF93FAFFFFA8B690FFD57A1EFF -FF9F11FFD37718FF90AC9FFF0098CDFF00000000CC6500BFF29413FFD37921FF -000000000000000000000000000000000098CDFF92F9FFFF8BF2FFFF82EAFFFF -79E1FEFF6DD7FEFF63CDFEFF68D2FEFF6DD7FEFF73DCFEFF78E0FEFF7DE5FEFF -82EAFFFFD37A22FFE88406FFB69D60FF96FDFFFF93FAFFFFBD8D46FFED9525FF -F29313FFE6912BFFAE8850FF0098CDFF00000000CC6500BFEF8B08FFD37921FF -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF7DE6FEFF78E0FEFF74DDFEFF71DAFEFF73DCFEFF78E0FEFF7DE5FEFF -82EAFFFFCE8232FFED8A09FFBD8A40FF96FDFFFFACB990FFD98225FFF79A18FF -D47F2BFFF29313FFD67F25FF328B9AFF00000000CC6500BFE98811FFD37921BF -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF7DE6FEFF79E2FEFF7BE3FEFF7DE5FEFF7CE4FEFF79E1FEFF7DE5FEFF -82EAFFFFBB8840FFEE982AFFC57820FF99F4F0FFCC7A22FFF79A18FFD98225FF -BE7D30FFE18C2BFFEE941EFFB36F20FF00000000CC6500FFEE982AFFCC6500AF -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF7DE6FEFF79E2FEFF7BE3FEFF80E9FEFF82EAFEFF525454FF434242FF -272727FF683503FFE7922AFFCC6500FFBE8B40FFCE6C0CFFD17418FFC47A26FF -7FE7FEFFC27826FFD17418FFCD6806FFCC65009FD26B00FFE48E2AFFCC65005F -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF89F1FFFF -7AE7F9FF69DCF3FF61D7F0FF56D1ECFF55D0ECFF48C7E7FF5F6162FF6A6969FF -424242FF1C1004FFD7822AFFD87100FF87AD94FF6BE0F0FF71E3F4FF7DEAFAFF -82EAFEFF76DFFEFF6DD6FEFF0098CDFFCC65008FD87100FFD47E2AFFCC65000F -000000000000000000000000000000000098CDFF8AF5FBFF68DEF0FF50CDE9FF -4BC9E8FF54CDEDFF5BD0F1FF6BDAF7FF6DDBF7FF77E2FAFF5A5B5CFF908F8FFF -616060FF292A2AFFBB8D47FFDB852AFFB5914EFF6EE2F2FF5DD6EDFF4DCBE9FF -4DCBE9FF5FD5F0FF6EDAFBFF0098CDFFCC6500DFDE882AFFCE6D0D9F00000000 -000000000000000000000000000000000098CDFF4ECDE7FF61D8EFFF7AE7FAFF -82EAFEFF7DE6FEFF79E2FEFF7BE3FEFF7DE5FEFF7FE7FEFF575859FFA9A9A8FF -818080FF464747FF9CD1C0FFD17419FFCD6A08FF9AE8E0FF8DF5FFFF86EEFFFF -73DFFAFF53CBEEFF44C2E7FF268FA7FFCD6A08FFD17418FFCC65001F00000000 -000000000000000000000000000000000098CDFF77E7F5FF8DF5FFFF87EFFFFF -82EAFEFF7DE6FEFF79E2FEFF7BE3FEFF7BE1FAFF71CBDFFF59595AFFBFBFBFFF -9E9D9DFF434343FF79CDD5FF90F7FDFF96FDFFFF93FAFFFF8DF5FFFF86EEFFFF -7FE7FEFF76DFFEFF5CCDF6FF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF7DE6FEFF79E2FEFF79DFFAFF6FC8DEFF565756FF9D9D9DFFD3D3D2FF -AEAEAEFF727272FF1A1B1BFF7BCDD2FF95FBFDFF93FAFFFF8DF5FFFF86EEFFFF -7FE7FEFF76DFFEFF6DD6FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF7DE6FEFF77DEFAFF6DC7DEFF6A6969FFB8B8B8FFE4E4E4FFDCDCDBFF -BEBEBDFF949493FF5B5B5BFF191A1AFF7ED1D2FF92F8FDFF8DF5FFFF86EEFFFF -7FE7FEFF76DFFEFF6DD6FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF7BE2FAFF6CC6DEFF6F706EFFC3C2C2FFEAEAEAFFFBFBFBFFEEEFEDFF -D6D6D5FFBDBDBDFF9B9B9BFF595A5AFF171818FF7BCED2FF8CF3FDFF86EEFFFF -7FE7FEFF76DFFEFF6DD6FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF70CADFFF828080FFD2D2D2FFEDEDEDFFFDFDFDFFFFFFFFFFFAFAF9FF -E7E7E6FFD6D6D5FFB8B8B8FF939393FF535353FF181818FF77CBD3FF86EEFFFF -7FE7FEFF76DFFEFF6DD6FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF7E7E7DFFD0D0CFFFEEEDEDFFFBFBFBFFFAFAFAFFF5F5F5FFEDEDEDFF -E5E5E4FFD0D0CFFFBFBFBFFF999999FF646464FF555555FF181919FF86EEFFFF -7FE7FEFF76DFFEFF6DD6FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFFB6B7B7FFC4C4C4FFD3D3D3FFCFCECEFFC0C0C0FFBBBBBBFFB0AFAFFF -A6A5A4FFA19F9FFF9B9A9AFF8E8E8EFF777878FF646363FF494949FF86EEFFFF -7FE7FEFF76DFFEFF6DD6FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF7DE6FEFF79E2FEFF7BE3FEFF7DE5FEFF7FE7FEFF81E9FEFF84ECFEFF -87EFFFFF8BF3FFFF8EF6FFFF92FAFFFF96FDFFFF93FAFFFF8DF5FFFF86EEFFFF -7FE7FEFF76DFFEFF6DD6FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -82EAFEFF7DE6FEFF79E2FEFF7BE3FEFF7DE5FEFF7FE7FEFF81E9FEFF84ECFEFF -87EFFFFF8BF3FFFF8EF6FFFF92FAFFFF96FDFFFF93FBFFFF8DF5FFFF86EEFFFF -7FE7FEFF76DFFEFF6DD6FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF8DF5FFFF87EFFFFF -87EFFEFF86EFFEFF8AF2FEFF8CF3FEFF92F9FFFF92FAFFFF92F9FFFF93FAFFFF -92FAFFFF93FAFFFF93FAFFFF93FBFFFF94FBFFFF93FBFFFF91F8FFFF8AF1FFFF -80E7FEFF76DFFEFF6DD6FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF93FAFFFF92F9FFFF94FBFFFF -94FBFFFF93FAFFFF92F9FFFF90F7FFFF8DF4FFFF8AF2FFFF87EFFFFF85EDFFFF -82EAFFFF7FE7FFFF7CE4FEFF7BE3FEFF7DE5FEFF7EE7FEFF81EAFEFF87EFFEFF -8EF5FFFF84ECFEFF72DBFEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF96FDFFFF95FCFFFF94FBFFFF -92F9FFFF91F8FFFF8FF6FFFF8CF4FFFF8AF2FFFF88EFFFFF85EDFFFF82EAFFFF -7FE7FFFF7CE5FEFF79E2FEFF76DFFEFF73DCFEFF6FD8FEFF6CD6FEFF68D2FEFF -6AD3FEFF77DFFEFF89F0FEFF0098CDFF00000000000000000000000000000000 -000000000000000000000000000000000098CDFF8AF5FBFF93FAFFFF92FAFFFF -91F8FFFF8FF6FFFF8DF4FFFF8AF2FFFF88EFFFFF85EDFFFF82EAFFFF7FE8FFFF -7CE5FEFF79E2FEFF76DFFEFF73DCFEFF70D9FEFF6CD6FEFF69D2FEFF65CFFEFF -62CCFEFF62CCFEFF61CDFBFF0098CDFF00000000000000000000000000000000 -0000000000000000000000000000000011A3D2D81AAAD6FF64DBEFFF91F8FFFF -8FF6FFFF8DF4FFFF8AF2FFFF88F0FFFF85EDFFFF83EBFFFF80E8FFFF7DE5FEFF -7AE2FEFF76DFFEFF73DCFEFF70D9FEFF6CD6FEFF69D3FEFF65CFFEFF62CCFEFF -62CCFEFF43BBEEFF12A1D6FF11A3D2D800000000000000000000000000000000 -00000000000000000000000000000000000000000098CD5F0098CDCF0098CDFF -22AFD9FF45C5E6FF55CFECFF64D8F2FF6ADBF5FF80E8FFFF7DE5FFFF7AE2FEFF -77DFFEFF73DCFEFF70D9FEFF58CBF4FF4FC3F1FF3FBAEBFF31B2E5FF17A4D9FF -0098CDFF0098CDCF0098CD5F0000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000098CD1F -0098CD4F0098CD7F0098CDAF0098CDBF0098CDDF0098CDFF0098CDFF0098CDFF -0098CDFF0098CDFF0098CDFF0098CDDF0098CDBF0098CDAF0098CD7F0098CD4F -0098CD1F00000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 - -1 -Aggregate -1 - - -ExtendedAttributeTargetItem -63886F85-A5E5-4568-83CF-CC5ABF2D461C -Extended Attributes -0 - -0 - -1 - - -58E811EE-09D5-462C-8D99-04D8B1734A39 -ReturnType -0 - -0 - -Specifies the return type of the aggregate function. All scalar data type or CLR user-defined types can be used as return type, except text, ntext, and image. Non scalar types, such as cursor and table, cannot be specified. -1 -12 -integer -%getTypeList% -0 - - -86046A95-6A5E-4CAD-B838-3E9B810F1DCD -Assembly -0 - -0 - -Specifies the assembly to bind with the user-defined aggregate function. -1 -15 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Assembly -0 - - - - - -ACC7A7D0-6DE4-41D8-B54C-1B3C992C6362 -Class -0 - -0 - -Specifies the name of the class in the assembly that implements the user-defined aggregate. -If the class name is not specified, SQL Server assumes it is the same as aggregate name. -1 -12 -0 - - -B8644BBE-9AEB-4DDF-AF2C-6F3F2AC81B03 -Owner -0 - -1277294329 -psitja -Is the name of the schema to which the user-defined aggregate function belongs. - - -1 -15 -4BA9F642-DAB1-11D1-9944-006097355D9B -Schema -0 - - - - - -4F33725E-3BDA-4DFF-92D3-F95A714B343D -DisplayType -0 - -0 - -Complete datatype with length and precision (for script generation and reverse engineering purpose) -1 -12 -1 -Function %Get%(obj) - Dim sGet - sGet = obj.GetExtendedAttribute("%CurrentTargetCode%.ReturnType") - if (sGet = "") then - %Get% = "" - else - Dim sLP - if CBool(obj.EvaluateTemplateFor("needLength")) then - sLP = obj.GetExtendedAttribute("%CurrentTargetCode%.ReturnTypeLength") - if (sLP = "") then - %Get% = sGet - else - sGet = sGet & "(" & sLP - if CBool(obj.EvaluateTemplateFor("needPrec")) then - sLP = obj.GetExtendedAttribute("%CurrentTargetCode%.ReturnTypePrec") - if (sLP <> "") then - sGet = sGet & ", " & sLP - end if - end if - %Get% = sGet & ")" - end if - else - %Get% = sGet - end if - end if -End Function -Sub %Set%(obj, value) - Dim pos, sType, sLP - ' reset attributes (dec supports length & precision) - obj.SetExtendedAttribute "%CurrentTargetCode%.ReturnType", "dec" - obj.SetExtendedAttribute "%CurrentTargetCode%.ReturnTypeLength", "" - obj.SetExtendedAttribute "%CurrentTargetCode%.ReturnTypePrec", "" - - pos = InStr(value, "(") - if (pos = 0) then - sType = value - sLP = "" - else - sType = Left(value, pos - 1) - sLP = replace(Mid(value, pos + 1), ")", "") - end if - obj.SetExtendedAttribute "%CurrentTargetCode%.ReturnType", sType - - if (sLP <> "") then - if CBool(obj.EvaluateTemplateFor("needLength")) then - Dim LPArray - LPArray = Split(sLP, ",") - obj.SetExtendedAttribute "%CurrentTargetCode%.ReturnTypeLength", Trim(LPArray(0)) - if ((UBound(LPArray) > 0) and CBool(obj.EvaluateTemplateFor("needPrec"))) then - obj.SetExtendedAttribute "%CurrentTargetCode%.ReturnTypePrec", Trim(LPArray(1)) - end if - end if - end if - -End Sub -0 - - - - -ExtendedCollectionTargetItem -9E50D6A0-9F20-44FF-B6AF-C1893D929C58 -Extended Collections -0 - -0 - -1 - - -334E6FE1-DD22-4150-9FEB-9BB66E7023FE -Assembly -0 - -0 - -1 -Aggregates -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Assembly - - -A9328E28-4D8B-474C-8792-26AC40346600 -Owner -0 - -1277294329 -psitja -1 -Aggregates -4BA9F642-DAB1-11D1-9944-006097355D9B -Schema - - - - - - - -FormTargetItem -B56C8715-6EB1-4675-B9E3-D0439A38243B -Forms -0 - -0 - -1 - - -D97553A1-449D-486A-89BF-13C6E2D24494 -Microsoft -<Form > - <ExtendedAttribute Attribute="Owner" Caption="Schema:" AttributeID="{B8644BBE-9AEB-4DDF-AF2C-6F3F2AC81B03}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <Separator Name="Separator2" /> - <ExtendedAttribute Attribute="Assembly" Caption="Assembly:" AttributeID="{86046A95-6A5E-4CAD-B838-3E9B810F1DCD}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="Class" Caption="Class name:" AttributeID="{ACC7A7D0-6DE4-41D8-B54C-1B3C992C6362}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <Separator Name="Separator1" /> - <HorizontalLayout Name="HorizontalLayout1" > - <ExtendedAttribute Attribute="InputParameterType" Caption="Type:" AttributeID="{385B7A48-909B-4C46-975F-DE81E0737499}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - </HorizontalLayout> - <ExtendedAttribute Attribute="ReturnType" Caption="Return type:" AttributeID="{58E811EE-09D5-462C-8D99-04D8B1734A39}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <HorizontalLayout Name="HorizontalLayout2" > - <ExtendedAttribute Attribute="ReturnTypeLength" Caption="Length" AttributeID="{1E007685-A7BF-462A-951B-CC848D7D3BD2}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="ReturnTypePrec" Caption="Precision" AttributeID="{2409BEB9-A7DF-4967-AA35-6BE633F3F042}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - </HorizontalLayout> -</Form> - -0 - -1276605960 -obalen -1 -24955 - - -D808B271-A13B-4E79-910B-2BF9AE998134 -Hidden -<Form > - <ExtendedAttribute Attribute="DisplayType" Caption="Hidden" AttributeID="{4F33725E-3BDA-4DFF-92D3-F95A714B343D}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> -</Form> - -0 - -0 - -1 -Dialog - - - - -DCE645DC-0C18-4A3E-85B0-869755BEA7FD -Custom Symbol -0 - -1152538147 - -1 -0 -(2400, 2400) -128 -14737632 -STRN 0 Arial,8,N -DISPNAME 0 Arial,8,N -LABL 0 Arial,8,N -4 -1 -5246507C-DA84-4907-A3F3-CCF6A9F2775E -1 -1 - - -ExtendedCompositionTargetItem -1501A556-54D7-4655-85EF-39635DC3D110 -Extended Compositions -0 - -0 - -1 - - -88759F14-0B46-4ED6-B426-C29E32EC644A -Parameters -0 - -0 - -List of parameters for aggregate -1 -E66E5B1D-A046-4482-ADC2-5D6CCF8CBB85 -AggregateParameter - - - - - - - -TemplateTargetItem -A8EEA622-CC6F-49D4-B975-53D2A9A988D6 -Templates -0 - -0 - -1 - - -E0981AE1-53CF-4912-BADC-D81C71D347E0 -needLength -.if(%.L:ReturnType%=="char") -True -.elsif(%.L:ReturnType%=="character") -True -.elsif(%.L:ReturnType%=="numeric") -True -.elsif(%.L:ReturnType%=="decimal") -True -.elsif(%.L:ReturnType%=="dec") -True -.elsif(%.L:ReturnType%=="float") -True -.elsif(%.L:ReturnType%=="binary") -True -.elsif(%.L:ReturnType%=="nvarchar") -True -.else -False -.endif -0 - -0 - -1 - - - - -CriterionTargetItem -ED32805A-7CA6-447F-A156-98474604C0B8 -Criteria -0 - -0 - -1 - - -B2FBFC80-F105-4980-847F-24F27EFA87F2 -NeedLength -%needLength%==True -0 - -0 - -1 - - -ExtendedAttributeTargetItem -47547353-A64C-45E0-9D70-C88AAB0E8040 -Extended Attributes -0 - -0 - -1 - - -1E007685-A7BF-462A-951B-CC848D7D3BD2 -ReturnTypeLength -0 - -0 - -Length of return data type -1 -10 -Length -0 - - - - -CriterionTargetItem -B3ABE5CB-A4EB-4F55-922B-A61EF8C44B23 -Criteria -0 - -0 - -1 - - -51D5BB3D-FCDE-4C01-8542-37BCC823A0E8 -NeedPrec -%needPrec%==True -0 - -0 - -1 - - - - - -ExtendedAttributeTargetItem -900C63C0-836B-4C9A-A313-358756C0CAB5 -Extended Attributes -0 - -0 - -1 - - -2409BEB9-A7DF-4967-AA35-6BE633F3F042 -ReturnTypePrec -0 - -0 - -Precision of return data type -1 -10 -Precision -0 - - - - - - - - -TemplateTargetItem -839176AF-B678-43D8-9029-B76ECA620319 -Templates -0 - -0 - -1 - - -9E7523F2-24B0-4355-8E51-3CFE2B85BD11 -needPrec -.if(%.L:ReturnType%=="numeric") -True -.elsif(%.L:ReturnType%=="decimal") -True -.elsif(%.L:ReturnType%=="dec") -True -.else -False -.endif -0 - -0 - -1 - - - - - - - - - - -7D49C656-DF19-4B8D-8E34-923A720C94F5 -EndPoint -0 - -0 - -Endpoint objects are used to specify the database mirroring source and target. -1 -End Point -End Points -0D0000000800000008000000424D7E000000000000003E000000280000001000 -0000100000000100010000000000400000000000000000000000000000000000 -000000000000FFFFFF0000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000424D3605000000000000360400002800000010000000 -1000000001000800000000000001000000000000000000000000000061000000 -013C680001406A0001446E0001457300014A7200014C7C00018FCE0001A4E900 -01A4EA0001A5EC0001A6EC0001A7EC0001A8EC0001AAED0001FFFF00034E7700 -03ABEC0004ABE90004ACEA0004ADEE0005ABEA0005ACEC0006ABEB00135B8100 -25587700266A8B002BC9F8002DC3F40032C5F70032C5F90032C8F80035CCFB00 -36A0C50036C6F80036CAF7003E7089004177890044CDF900457C9A004BC8F000 -4DC9F2004EC7F2004FCAF10051CAEF0051CCF00052D8FE0054CDF2005A809300 -5D8797006998AB006A9CAF006E90A8006FA3B2007092A800719EB1007599AC00 -769FB300789CB00078A5B7007BA2B6007CA6B90081AFC40084A2B00084E2F900 -86ACBC0088D0E4008FB6C60092DEEB0094C6D7009AE9F6009BE9F5009BE9F600 -9CEBF500A1ECF400A2EBF400A2EBF600A2ECF700A3EAF400A3EBF400A3EBF700 -A3F1FD00A4ECF500A4EDF300A5E9F800A8F1FC00AEF0F800AFF2FC00B4F6FC00 -B5D3DD00C4F7FB00C5D6E000C5F8FD00C8FCFD00CDFFFF00E4F3F400F9FBFF00 -FFFFFF0000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -606060606060605A025A60606060606002600202020202020E02020202026002 -3D603D3D3D3D3D022D023D3D3D3D603D6060606060606060025A606060606060 -606060605F3D302F2319265A60606060606060603E2043542D11061860606060 -60606060331F504E2E130B006060606060606060351A495328150C0160606060 -60606060371E484A2B120C026060606060606060391D4C4B2A14090360606060 -606060603821454D2C160A0460606060606060603B1C4652270D080F60606060 -606060603C1B4751291007056060606060606060402256554F3F251760606060 -6060606042415D595B5C572460606060606060605E44343631323A5860606060 -0E0000000200000030000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF0000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00 -FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00EEF3F600D2DFE70092B0C200 -33698B002D6487008CABBE00D0DDE500ECF1F400FFFFFF00FFFFFF00FFFFFF00 -FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00 -84A4B800BECFD900FAFBFB00C6D4DE008AA9BC0083A3B70083A3B70083A3B700 -83A3B70083A3B70083A3B70083A3B70083A3B7007A9DB3006B93AB004B91AA00 -1999B000169AB0004792AB006A92AA00799CB20083A3B70083A3B70083A3B700 -83A3B70083A3B70083A3B7008AA9BC00C6D4DE00FAFBFB00BECFD90083A3B700 -094A72007FA1B500F6F8F9008EABBE0017547A00084971000849710008497100 -0849710008497100084971000849710008497100074871000648700004749300 -01CAD60001D0DA00047B99000647700007487100084971000849710008497100 -08497100084971000849710017547A008EABBE00F6F8F9007FA1B50008497100 -3A74940098B6C700F7F9FA00A5BFCD00467C9A00397393003973930039739300 -3973930039739300397393003973930039739300296588000D4D7600096E9200 -1BC0D8001CC6DC000A7497000A4C740027638700397393003973930039739300 -397393003973930039739300467C9A00A5BFCD00F7F9FA0098B6C70039739300 -78A8BE00B8D1DD00F9FAFB00C1D6E00080ACC10077A7BD0077A7BD0077A7BD00 -77A7BD0077A7BD0077A7BD0077A7BD0077A7BD00558AA6001B597F0013699200 -38B2D8003AB7DC00156E970016557B005186A30077A7BD0077A7BD0077A7BD00 -77A7BD0077A7BD0077A7BD0080ACC100C1D6E000F9FAFB00B8D1DD0077A7BD00 -B5D0DC00D8E6EC00FBFCFC00DCE9EE00B9D2DE00B4CFDB00B4CFDB00B4CFDB00 -B4CFDB00B4CFDB00B4CFDB00B4CFDB00B4CFDB009FBDCD007B9FB5005B93AF00 -3E97BA003793B8004787A600648FA80095B5C700B4CFDB00B4CFDB00B4CFDB00 -B4CFDB00B4CFDB00B4CFDB00B9D2DE00DCE9EE00FBFCFC00D8E6EC00B4CFDB00 -F2F7F900F7FAFB00FDFDFD00F8FAFB00F2F6F800F1F6F800F1F6F800F1F6F800 -F1F6F800F1F6F800F1F6F800F1F6F800F1F6F800EEF3F500E8EEF100ACC4D200 -407998002D6B8F007FA4B900BDD0DA00DCE7EC00F1F6F800F1F6F800F1F6F800 -F1F6F800F1F6F800F1F6F800F2F6F800F8FAFB00FDFDFD00F7FAFB00F1F6F800 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FDFDFE00 -FBFCFE00EBF2F500D4E3EB00C8DAE100C1D2D800BFCED500BECDD40093ADBC00 -42738E00306787006691A8008FB1C200A6C1CE00BDD1DB00D6E1E800EAF0F300 -F5F7F900FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FCFDFE00 -F8FAFE00D3E3EB00A1C2D30085ADBD00769BAA007093A2006F91A0005E849900 -43738B00366B880038738F00407A98005085A0006D98B000A4BECD00D1DEE600 -EAF0F300FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00ECF0F300 -D3DEE500A9C8D60076B0C80068A7BD006CA4B60070A5B40073A6B60067A1B600 -5099B2003A8FB0002585AC001C7FAB002581AB003A88AE00699AB40099B2C200 -D2DCE300FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00D6DFE400 -A2B9C40078AABE004FA4C20058B0CB007AC5D7008DD2E00096DAE70086D7EA00 -61CCEA003DBDE6001CAADD00099AD4000A91CB001285BA002B769C00567D9500 -B4C5D000FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00CCD7DF00 -8BA7B60062A1BA0040AACF0054BEDD0083D8EA009AE6F200A2EBF7008FE6F900 -67DAF9003FCAF50019B6EE0003A6E500019CDC00038AC6000F67950035648200 -A5BAC800FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00C7D4DD00 -82A0B3005BA3C2003DBBE40056D0F2008AE4F6009FECF700A2EBF5008DE3F400 -67D5F40040C6F10019B5EE0003A9EA0001A4E6000091D1000462950026587C00 -9EB5C500FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00C6D3DD00 -7F9DB20057A4C4003AC1EC0054D6F9008BE7F800A1EDF700A2EAF5008DE1F300 -66D3F2003FC3F00019B4ED0003AAEC0001A8EB000095D6000061960020547A00 -9CB3C400FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00C6D3DD00 -809EB20057A4C30037BFEA0051D4F70089E5F500A1EBF500A2E9F5008DDFF500 -64D1F2003DC1F00019B4ED0003AAEB0001A8EB000095D6000063960020567B00 -9CB3C400FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00C7D4DD00 -82A0B20058A5C30037BFEA0050D3F60088E3F4009FEAF300A2E9F5008CDFF400 -63D1F2003CC1EF0019B4EC0003AAEA0001A8EA000096D7000064970020587C00 -9CB4C500FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00C8D6DD00 -83A3B4005AA7C40039BFEA0052D2F60086E3F4009EEAF300A0E9F3008CE1F300 -64D1F0003DC1ED0019B4EB0003AAE90001A8EA000096D7000065980020597E00 -9CB5C500FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00C9D7DE00 -85A5B5005CA8C5003BBEEA0053D1F60087E3F5009DEAF300A0EAF3008CE2F200 -64D1F0003EC2ED0019B3EB0003A9E90001A7EA000095D70000669900205B8000 -9CB6C600FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00C9D7DF00 -87A6B7005DA8C7003BBEEB0054D1F60089E3F6009FEAF500A0EAF4008CE2F300 -63D1F0003EC2ED0019B3EB0003A8E90001A6EA000095D70000669B00205B8200 -9CB6C700FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00C9D8E000 -87A7B9005EA9C9003CBEEB0055D1F7008AE3F600A0EAF500A0E9F4008CE1F300 -63D1F1003EC2EE0019B3EB0004A8E90001A6EA000095D80000669C00205C8300 -9CB6C800FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00C9D8E000 -86A9BA005EABC9003EBEEB0055D1F70087E1F6009DE8F400A0E9F3008CE1F200 -65D2F1003EC2EE001AB3EB0004A8E90001A6EA000095D80000689C00205E8300 -9CB7C800FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00C9D9E100 -86AABB005FACCA003FBFEB0054D0F60085E1F5009CE8F300A0E9F3008CE1F100 -65D2F0003EC2EE001AB3EB0003A8EA0000A6EA000095D70000699C0020608300 -9CB8C800FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00CAD9E100 -89ABBC005FACCB003DBFEB0053D0F50085E1F4009CE8F300A0EAF3008CE1F100 -62D1F0003BC1EE0017B2EC0001A8EB0000A5EA000095D700006A9D0021618500 -9CB9C900FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00CBDAE200 -8BADBE005FADCC003BBEEB0051CFF40084E0F4009DE9F300A1EAF3008CE2F100 -61D0F0003AC0EE0015B2ED0000A8EB0000A5E9000095D600006B9F0021628700 -9CB9CA00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00CBDBE200 -8BAEBF005FAECC003ABDEA0050CFF40084E0F4009DE9F400A1EAF3008CE2F200 -62CFF1003BC0EF0016B2ED0001A8EB0000A5E9000095D600006BA00020628A00 -9CB9CB00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00CBDCE300 -8BB0C1005FB0CD0039BDE90050CFF40085E0F5009EE9F500A2EAF4008FE3F300 -69D1F20045C3F00023B6ED000DADEC0008AAEA000598D700036DA10021638C00 -9CB9CC00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00CDDDE300 -90B2C20064B2CE003EC1EB0056D3F6008FE5F700A6EDF700A7ECF5009EE8F500 -88DFF40073D6F4005BCFF20045C6F20033BFF10022A9DE001478A60029698E00 -A0BCCC00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00CFDEE400 -94B5C3006BB5CF0046C4EC0060D6F70099EAF900AFF0F900AEEFF700ACEEF700 -A6EBF7009DE8F7008FE5F7007BDEF8005ED4F80042BBE4002884AB0032709000 -A4BFCD00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00D1E0E600 -98B9C7007CB9CF0068C7E40080DAF100ACF0F900BCF6FB00BAF3F900B8F2F900 -B5F1F900B2F0F900ABF0F900A0ECFA008DE6F90073CEE6004994AE00457C9300 -ACC5CF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00D4E2E800 -9EBFCD008DBFCF0089CBDD009DDCEA00BCF2F600C6F9FA00C2F5F800C0F3F800 -C1F3F800C2F4F900C3F6F900C0F7F900B6F4F8009FDFE70069A3B1005B899900 -B6CBD200FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00E4EEF100 -C2D9E000AAD0DC0094CCDC0092CBDA0098CDD7009ACCD50099C9D40097C7D300 -95C5D10095C6D20096C8D30096C9D40094CAD50090C5D10083B2BF008CB0BC00 -CCDCE100FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00F3F9F900 -E5F2F300C6E1E8009FCCDB0087BACA0075A9B8006EA0B100709EB0006E9CAF00 -6A98AB006898AB00699AAD006C9CB00073A1B30081ABBC009DC1CD00BCD6DF00 -E1ECF000FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 - -1 -EndPoint -1 - - -ExtendedAttributeTargetItem -15E93B7D-4895-425C-8828-1B901F87ABAE -Extended Attributes -0 - -0 - -1 - - -B1454388-BB04-4B2E-97FA-7CF8B27F4947 -Owner -0 - -1286890165 -psitja -Specifies a valid SQL Server or Windows login that is assigned ownership of the newly created endpoint object. -1 -15 -4BA9F642-DAB1-11D1-9944-006097355D9B -User -0 - - - - - -D3612940-5657-46E5-8BD0-8150DA7459D6 -State -0 - -0 - -Specifies the state of the endpoint when it is created. -1 -12 -stopped -started;stopped;disabled -1 -0 - - -EA7D7F8A-C7AE-4DDE-81C7-DF2258CC4D50 -Protocol -0 - -0 - -Specifies the transport protocol to use. -1 -12 -http -http;tcp -1 -0 - - -B18404BB-08BE-49A2-B8BC-44F46BBD1BD8 -ProtocolArgument -0 - -0 - -Specifies a list of options for the selected protocol. -1 -14 -0 - - -21EE4514-720A-4D60-931A-7B23F59286D8 -Language -0 - -0 - -Specifies the payload type. -1 -12 -soap -soap;tsql;service_broker;database_mirroring -1 -0 - - -06D11BB9-8B60-40FC-98E2-C9AB42DB0826 -LanguageArgument -0 - -0 - -Specifies a list of options for the selected language. -1 -14 -0 - - - - -ExtendedCollectionTargetItem -9E7AC04D-C381-4727-AC0F-82A5969E5235 -Extended Collections -0 - -0 - -1 - - -34356F8E-124D-415C-896E-1136502F855A -Owner -0 - -1286890165 -psitja -1 -4BA9F642-DAB1-11D1-9944-006097355D9B -User - - - - - - - -FormTargetItem -D2F9DE69-F23C-4509-98E5-674697D130C8 -Forms -0 - -0 - -1 - - -549145CB-B619-49C5-8AEB-99C656844930 -Microsoft -<Form > - <ExtendedAttribute Attribute="Owner" Caption="Owner" AttributeID="{B8644BBE-9AEB-4DDF-AF2C-6F3F2AC81B03}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="State" Caption="State" AttributeID="{D3612940-5657-46E5-8BD0-8150DA7459D6}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <GroupBox Name="GroupBox1" Caption="Protocol" ControlAsLabel="No" > - <ExtendedAttribute Attribute="Protocol" Caption="Name" AttributeID="{EA7D7F8A-C7AE-4DDE-81C7-DF2258CC4D50}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="ProtocolArgument" Caption="Argument" AttributeID="{B18404BB-08BE-49A2-B8BC-44F46BBD1BD8}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="2" HorizontalResize="Yes" VerticalResize="Yes" /> - </GroupBox> - <GroupBox Name="GroupBox2" Caption="Language" ControlAsLabel="No" > - <ExtendedAttribute Attribute="Language" Caption="Name" AttributeID="{21EE4514-720A-4D60-931A-7B23F59286D8}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="LanguageArgument" Caption="Argument" AttributeID="{06D11BB9-8B60-40FC-98E2-C9AB42DB0826}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="2" HorizontalResize="Yes" VerticalResize="Yes" /> - </GroupBox> -</Form> - -0 - -1276605960 -obalen -1 -24957 - - - - - - -CDE9B67D-6B8B-4878-97D8-2953A6EC5803 -Certificate -0 - -0 - -Defines a certificate for data encryption -1 -Certificate -Certificates -0D0000000800000008000000424D7E000000000000003E000000280000001000 -0000100000000100010000000000400000000000000000000000000000000000 -000000000000FFFFFF00FFFF0000FFC300000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000FFFF0000424D3605000000000000360400002800000010000000 -10000000010008000000000000010000000000000000000000000000A3000000 -000000000076B3000087D30000B3F80005709F0013B7E5001BC0F10023B9E800 -34A4CA003B5C79003F627E004366820046D3F000486D87004C738C0051799100 -52789100567F9600577F97005C859D005C869E00608AA100618DA400628CA300 -638FA5006793A9006794A9006794AA006B99AD006B99AE006B9AAF006C9BB000 -6E90A5006F5A54006F9DB200709EB300719FB40072A2B60074A3B80074A5B900 -78A9BB0078A9BC0078AABD0079AABD007BAEC0007CAEC0007CAEC1007EB1C300 -7FB1C3007FB2C40088CEE200987E75009FD7E600A8B7C200ABABAC00B4A7A900 -B65B3800B7A79B00BAC7CF00BBC7D000BCC8D100BDB8B600BDCAD200C0CCD400 -C0CED500C1CDD600C2CFD700C3CFD700C3D1D900C4D1D900C6D3DB00C6D4DB00 -C8D5DD00C8D6DC00CAD7DE00CBD3DC00CBD8DF00CBD9E000CC9D8C00CCC1B900 -CCDBE200CDDAE100CDDBE200CEC1B800CECCCA00CEDCE300CFC2B900D0C4BB00 -D1BCB200D1C5BC00D2C8C000D6C6BD00D7754C00D7CCC400DA9D8500DAE6EB00 -DCD2CA00DFD9D500DFDBDB00E1D4CD00E1D6D100E5D9D100E8DFD900E8DFDB00 -E8E0DC00EDE6E300F3D8CB00F3ECE800F3ECEA00F4DCD000F5DCD000F5DDD100 -F5DFD400F5E9E400F6E0D500F6E0D600F6E1D600F6E1D700F6E1D800F6E2D900 -F7E3DA00F7E4DB00F7E4DC00F7E5DD00F7E6DE00F7F2F000F8E6DE00F8E7DF00 -F8E7E000F8E8E000F8F6F600F9EAE200F9EAE300F9EBE500F9ECE500F9F7F500 -FA723D00FAEDE700FAEEE800FAEFEA00FAF0EA00FBF1EC00FBF2ED00FBF3EF00 -FCF5F100FCF5F200FCF6F300FCF7F400FDF8F600FDF9F700FDFAF800FEFAF800 -FEFBFA00FEFCFB00FEFCFC00FEFEFD00FF783E00FFF3EE00FFFDFD00FFFEFD00 -FFFEFE00FFFFFE00FFFFFF000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000004E9B33540000 -491646144512421040379C885C203A09195F2A5F275F235F1C4B5E382135143B -4A2D8E8A847E78726E340702043D5F0A1E5F928F8B858079743206030136153C -4C2F976B67645B5871760C0508625F0B245F9E9894908D89837C77706D6A183E -5131A27D6966635B589D7F78736F5F0D275FA2A2A19A96938F8C86817A751B3F -5231A287594F68538261575A567B5F0E2B5FA265A25DA29F6C6091398A841D41 -5531A2A2A2A2A2A2A09995928F8B5F0F2E5F315F315F305F2C5F295F265F2243 -552D552850254D1F4A1A48174713441100000000000000000000000000000000 -0E0000000100000020000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00FFFFFFFFFFFFFFFFFFFFE007FFFFF00F000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000FFFFFFFFFFFFFFFF424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000E1A048FFF0BF7CFFF6D69FFFFDE8BCFFF3CA89FF -EEB260FFF5CB8EFFF7C58AFFE8A959FFC1912AFF000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000D8983CFFF6DAA5FFEFDCB4FFA1CFC7FF -52AFC5FFCED3BCFFE6AA59FFBB9026FF00000000000000000000000000000000 -C8D6DCEF99B4C2EF6A93A8EF8CAABBEFB2C3CEFE9EB4C2FE6D8FA4FE7999ABFE -A9BCC8FEA2B6C3FE6D8FA4EF7898ABEFA9BCC8EFA2B6C3EF6F8EA3EF6E8EA2EF -B0C2CDFEA0B5C2FE7191A5FE7C98A9FEB1C2CDFFDCAE71FF37A8CBFF14ABE0FF -29BEEBFF23A6D3FFA79078FF7898ABFFA8BBC7FEA3B7C3FE7890A2EF3B5C79EF -9EB9C6FF99B6C4FF94B2C2FF9BB9C7FFA0BCC9FF9AB7C5FF91ADBDFF94AFBFFF -9CB7C5FF99B4C2FF91ADBDFF94AFBFFF9CB7C5FF99B4C2FF8FAABAFF90ABBAFF -A0BCC9FF9AB7C5FF91ADBDFF94AFBFFFA0BCC9FF736865FF067BA8FF10CDFCFF -12D2FFFF13C1EFFF7E9FA5FF94AFBFFF9CB7C5FF99B4C2FF8098AAFF71899DFF -6F99ADFF9DBAC8FFCCDBE2FFFAF9F7FF85AFC0FF99BBCAFFC1D4DDFFB1C9D4FF -87ADBEFF91B3C3FFC1D4DDFFB1C9D4FF87ADBEFF91B3C3FFBBCFD9FFB4CBD5FF -85AFC0FF99BBCAFFC1D4DDFFB1C9D4FF85AFC0FF38373EFF1482A1FF34DDFFFF -2DDBFFFF14D3FFFF7BB4C1FFB1C9D4FF87ADBEFF91B3C3FF8DA6B6FFAFBDC8FF -8EADBDFFA0BECBFFB3CED9FFFAF9F7FFF1E6E0FFEEE0D9FFEDDDD5FFE9D4CAFF -E9D4CAFFE9D4CAFFE9D4CAFFE9D4CAFFE9D4CAFFE9D4CAFFE9D4CAFFE9D4CAFF -E9D4CAFFE9D4CAFFE9D4CAFFE9D4CAFFE9D4CAFF131520FF0B0D17FF58D1E2FF -5AEAFFFF3ADFFFFF52B3CEFFE9D4CAFFE9D4CAFF8DABBBFF8BA4B5FF8A9FAFFF -BECED7FFA3C2CEFF89B5C5FFFAF9F7FFF1E6E0FFF1E6E0FFEDDDD5FFECDBD3FF -EBDAD1FFEBD9D0FFEBD8CFFFEAD8CFFFEAD7CEFFEAD7CEFFEAD7CDFFEAD6CDFF -EAD6CDFFEAD6CDFFEAD6CCFFEAD6CCFFEAD6CCFF2E2F39FF23252FFF203842FF -69D7E2FF51E7FFFF285E73FFEAD6CCFFE9D4CAFFC3D3DCFF869FB0FF4E6E87FF -A7C0CCFFA2C1CEFF9EC2CFFFFAF9F7FFF4ECE8FFF1E6E0FFEFE2DCFFEEE0D9FF -EDDED6FFEDDDD5FFECDCD4FFECDBD3FFECDAD2FFEBDAD1FFEBD9D0FFEBD9D0FF -EBD8D0FFEBD8CFFFEBD8CFFFEBD8CFFFEBD8CFFF6B6C71FF464850FF22242EFF -0D0F19FF0B0D17FF474246FFEAD6CCFFE9D4CAFFA9C0CCFF8AA3B3FF6C879CFF -79A3B6FFA1BFCCFFC9DBE3FFFAF9F7FFF5EFEBFFF1E6E0FFF1E6E0FFF0E3DDFF -EFE1DAFFEDDDD5FFECDCD4FFECDBD3FFECDAD2FFECDCD4FFECDBD3FFECDBD3FF -ECDAD2FFECDAD1FFECDAD1FFECDAD1FFECDAD1FFECDAD1FF7A7A80FF514E52FF -515056FF656165FFEBD8CFFFEAD6CDFFE9D4CAFF7599ADFF90A9B9FFA9B8C4FF -8BAEBFFFA2C0CDFFB9D3DCFFFAF9F7FFF5EFEBFFF3EAE5FFD1C5BCFFD0C2B8FF -D0BFB4FFCFBCB0FFCFB9ACFFCFB6A8FFCEB3A4FFCEB0A0FFCEAD9CFFCDAA98FF -CDA794FFCDA490FFCCA18CFFCC9E88FFCC9C84FFEDDCD4FFEDDCD4FFEDDCD4FF -EDDCD4FFECDAD2FFEBD8D0FFEAD6CDFFE9D4CAFF89A8B9FF8EA7B8FF93A7B6FF -B9CCD6FFA4C3D0FF90BBCAFFFAF9F7FFF6F1EEFFF4ECE8FFF2E9E3FFF1E6E0FF -F0E4DEFFEFE2DCFFEFE1DAFFEEE0D9FFEEDFD7FFEDDED6FFEDDDD6FFEDDDD5FF -EDDCD4FFEDDCD4FFEDDCD4FFEDDCD4FFEDDCD4FFEDDCD4FFEDDCD4FFEDDCD4FF -EDDDD5FFECDBD3FFEBD9D0FFEAD6CDFFE9D4CAFFBDCFD8FF89A2B3FF587790FF -AFC6D1FFA5C3D0FF9AC1CFFFFAF9F7FFF7F2EFFFF5EEEAFFF3EBE6FFF2E8E3FF -F1E6E0FFF0E4DEFFF0E3DCFFEFE2DBFFEFE1DAFFEEE0D9FFEEDFD8FFEEDED7FF -EEDED7FFEEDED7FFEEDED7FFEEDED7FFEEDED7FFEEDED7FFEEDED7FFEEDED7FF -EDDDD6FFECDBD3FFEBD9D0FFEAD7CDFFE9D4CAFFB1C7D2FF8BA4B5FF67849AFF -84ABBDFFA4C2CFFFC4D9E1FFFAF9F7FFF7F3F0FFF5EFEBFFD1C5BCFFD0C2B9FF -D0C0B6FFD0BEB3FFCFBCB0FFCFBAADFFCFB8AAFFCFB5A7FFCEB3A4FFCEB1A1FF -CEAF9EFFCEAD9BFFCDAB98FFCDA895FFCDA692FFCDA48FFFCCA28CFFCCA089FF -CC9E86FFCC9C84FFEBDAD1FFEAD7CEFFE9D4CAFF7EA2B4FF91ACBBFFA3B4C1FF -8AAFC0FFA4C2CFFFBFD6DFFFFAF9F7FFF7F4F1FFF5EFEBFFF4ECE8FFF3EAE5FF -F2E8E2FFF2E7E2FFF1E6E0FFF0E5DFFFF0E4DDFFF0E3DCFFF0E3DCFFF0E3DCFF -F0E3DCFFF0E3DCFFF0E3DCFFF0E3DCFFF0E3DCFFF0E3DCFFEFE1DAFFEEE0D9FF -EDDED6FFEDDDD5FFECDAD2FFEAD7CEFFE9D4CAFF85A7B8FF91ACBBFF9DB0BDFF -B6CBD6FFA6C5D1FF96BFCDFFFAF9F7FFF7F4F1FFF6F0EDFFF4EDE9FFF3EBE6FF -F3EAE5FFF2E9E3FFF2E7E2FFF1E6E0FFF1E5DFFFF1E5DFFFF1E5DFFFF1E5DFFF -F1E5DFFFF1E5DFFFF1E5DFFFF1E5DFFFF1E5DFFFF0E4DDFFEFE2DCFFEFE1DAFF -EEDFD7FFEDDED6FFECDAD2FFEAD7CEFFE9D4CAFFB7CCD6FF8CA6B6FF648399FF -B7CDD7FFA6C6D2FF96BFCDFFFAF9F7FFF8F4F2FFF6F1EEFFD1C5BCFFD0C2B9FF -D0C0B6FFD0BEB3FFCFBCB0FFCFBAADFFCFB8AAFFCFB5A7FFCEB3A4FFCEB1A1FF -CEAF9EFFCEAD9BFFCDAB98FFCDA895FFCDA692FFCDA48FFFCCA28CFFCCA089FF -CC9E86FFCC9C84FFECDBD3FFEAD8CFFFE9D4CAFFB8CDD7FF8DA7B7FF658499FF -8CB3C3FFA5C5D1FFBFD6DFFFFAF9F7FFF8F4F2FFF6F1EEFFF5EFEBFFF5EEEAFF -F4ECE8FFF3EBE6FFF3E9E4FFF3E9E4FFF3E9E4FFF3E9E4FFF3E9E4FFF3E9E4FF -F3E9E4FFF3E9E4FFF3E9E4FFF2E8E3FFF2E7E2FFF1E6E0FFF0E5DEFFF0E4DEFF -EFE1DAFFEEDFD7FFECDCD4FFEBD8CFFFE9D4CAFF87AABBFF93AEBDFF9FB2BEFF -86B0C1FFA5C5D1FFC4D9E1FFFAF9F7FFF8F5F2FFF6F2EEFFF6F0ECFFF5EFEBFF -F4EDE8FFF4EBE7FFF4EBE7FFF4EBE7FFF4EBE7FFF4EBE7FFF4EBE7FFF4EBE7FF -F4EBE7FFF4EBE7FFF3EBE6FFF3EAE5FFF2E9E3FFF2E7E2FFF2E8E2FFF0E4DEFF -EFE2DCFFEEE0D9FFEDDDD5FFEBD9D0FFE9D4CAFF81A6B8FF95B0BEFFA7B9C4FF -B2CAD5FFA6C6D2FF9BC2CFFFFAF9F7FFF8F5F2FFF7F3F0FFD1C5BCFFD0C2B9FF -D0C0B6FFD0BEB3FFCFBCB0FFCFBAADFFCFB8AAFFCFB5A7FFCEB3A4FFCEB1A1FF -CEAF9EFFCEAD9BFFCDAB98FFCDA895FFCDA692FFCDA48FFFCCA28CFFCCA089FF -CC9E86FFCC9C84FFEDDED6FFEBDAD1FFE9D4CAFFB2C9D4FF8FAABAFF6F8DA1FF -BDD2DBFFA7C7D3FF90BCCBFFFAF9F7FFF8F6F3FFF7F3F0FFF6F1EEFFF5EFEBFF -F6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFF -F5EFEBFFF5EFEBFFF5EEEAFFF4EDE9FFF4ECE8FFF3EBE6FFF2E8E2FFF2E8E3FF -F1E6E0FFF0E3DDFFEEE0D9FFECDBD3FFE9D4CAFFBFD2DBFF8EA9B9FF618399FF -94BAC9FFA7C7D3FFBAD3DDFFFAF9F7FFF8F6F3FFF7F4F1FFF6F2EEFFF6F0ECFF -F6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFFF6F0ECFF -F5EFEBFFF6F1EDFFF6F0ECFFF5EFEBFFF5EEEAFFF3EBE6FFF3EAE5FFF2E8E3FF -F2E9E3FFF1E6E0FFEFE2DCFFEDDDD5FFE9D4CAFF90B2C2FF95B0BFFF9AAFBDFF -85B1C2FFA7C7D3FFCADCE4FFFAF9F7FFF8F6F4FFF7F4F1FFD5C7BEFFD6C8BFFF -F7F2EFFFD7C9BFFFD8C9C0FFF7F2EFFFDACAC1FFDACBC1FFF7F2EFFFDCCCC2FF -DDCCC3FFF6F1EDFFDECDC3FFDFCDC4FFF5EEEAFFE0CEC5FFE1CFC5FFF3EBE6FF -E3D0C6FFE3D0C6FFF1E6E0FFEDDDD5FFE9D4CAFF7EA6B8FF98B3C2FFB0C0CBFF -AFC9D5FFA7C7D3FFA0C5D2FFFAF9F7FFF8F6F4FFF8F4F1FFD5C7BEFFD6C8BFFF -F8F4F1FFD7C9BFFFD8C9C0FFF8F4F1FFDACAC1FFDACBC1FFF7F4F1FFDCCCC2FF -DDCCC3FFF7F3F0FFDECDC3FFDFCDC4FFF6F1EEFFE0CEC5FFE1CFC5FFF5EEEAFF -E3D0C6FFE3D0C6FFF1E6E0FFEEE0D9FFE9D4CAFFAEC6D2FF93AEBDFF7997AAFF -C4D6DEFFA7C7D4FF8BB9C9FFFAF9F7FFF9F6F4FFF9F6F4FFF9F6F4FFF9F6F4FF -F9F6F4FFF9F6F4FFF9F6F4FFF9F6F4FFF9F6F4FFF8F6F4FFF8F6F4FFF8F6F3FF -F8F6F3FFFAF9F7FFF8F5F2FFF8F5F2FFF8F4F2FFF7F4F1FFF7F3F0FFF7F2EFFF -F6F1EEFFF5EFEBFFF4ECE8FFF1E6E0FFE9D4CAFFC6D7DFFF90ACBBFF5E8399FF -9CC0CEFFA8C8D4FFB5D0DBFFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FF -FAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FF -FAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FF -FAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FFFAF9F7FF98B8C7FF97B2C1FF96ADBBFF -83B2C4FFA9C8D5FFCFDFE6FFAFCDD8FF8AB8C8FFA5C7D4FFCADCE3FFB3CFDAFF -8FBACAFFA1C5D2FFC5D9E1FFB7D1DBFF93BCCBFF9CC1CFFFC0D6DEFFBAD2DCFF -95BCCBFF94BCCBFFBAD2DBFFBED4DCFF97BCCAFF8DB5C5FFB4CDD7FFC2D5DEFF -99BBCAFF85ADC0FFADC8D3FFC6D7DFFF9BBAC9FF7AA4B7FF9AB6C4FFB9C7D1FF -ABC8D4FFA7C7D3FFA4C5D2FFA7C7D3FFABC9D5FFA7C7D3FFA2C3CFFFA5C5D1FF -AAC8D4FFA6C5D2FFA0C0CDFFA2C1CEFFA8C6D2FFA5C4D0FF9EBDCBFF9EBDCBFF -A5C2CFFFA4C2CEFF9CBAC8FF9AB9C7FFA1BECBFFA2BFCCFF9AB7C6FF97B4C3FF -9EBAC8FFA1BDCAFF98B4C3FF92AFBFFF99B5C4FF9DB9C7FF90ACBBFF84A0B1FF -CEDCE3EFA8C7D3EF83B2C3EF9FC1CEEFC4D6DEEFABC7D3EF84B0C0EF97BBC9EF -BDD2DBEFADC7D2EF84ADBEEF8DB3C3EFB6CCD6EFAFC7D2EF84ABBCEF84AABBEF -AEC5D0EFB3C7D1EF86A9B9EF7AA0B3EFA6BECBEFB6C8D3EF88A7B8EF7096AAEF -9DB7C4EFB9CBD3EF89A7B7EF648BA2EF93AEBDEFBCCCD5EF8BA6B6EF567F96EF -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 - -1 -Certificate -1 - - -ExtendedAttributeTargetItem -99EE99A5-E36A-402B-BFCF-E7BA46F6529E -Extended Attributes -0 - -0 - -1 - - -2B62B4F3-76B6-4B0E-8DE7-7A9D0FFD4ACE -Authorization -0 - -1285836574 -psitja -Is the name of the user that will own this certificate. -1 -15 -4BA9F642-DAB1-11D1-9944-006097355D9B -User -0 - - - - - -1C1A626F-33C7-4C9A-94ED-79F421680440 -Assembly -0 - -0 - -Specifies a signed assembly that has already been loaded into the database. -1 -15 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Assembly -0 - - - - - -5F4EFC83-3EC4-43B0-BE30-5EBC9760FB3C -AssemblyFile -0 - -0 - -Specifies the complete path, including file name, to a DER encoded file that contains the certificate. The path name can be a local path or a UNC path to a network location. The file will be accessed in the security context of the SQL Server service account. This account must have the required file system permissions. -1 -3 -0 - - -D28A9FE4-0337-42B6-AF43-A1C01DD9575B -Subject -0 - -0 - -The term subject refers to a field in the metadata of the certificate as defined in the X.509 standard. -1 -12 -0 - - -A9955921-B956-4B3E-A3F2-D438330E8919 -ActiveForBeginDialog -0 - -0 - -Makes the certificate available to the initiator of a Service Broker dialog conversation. -1 -true -0 - - - - -ExtendedCollectionTargetItem -70BB315E-2DE6-402F-B57F-08DD7EE40B87 -Extended Collections -0 - -0 - -1 - - -F6122A8C-09C4-42C7-90D1-B417CE3D3622 -Authorization -0 - -1285836574 -psitja -1 -Certificates -4BA9F642-DAB1-11D1-9944-006097355D9B -User - - - - - -53D57C2D-8238-476C-AFD3-485B98EF3A52 -Assembly -0 - -0 - -1 -Certificates -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Assembly - - - - -FormTargetItem -09132F99-6183-4167-A58D-D2097CED272F -Forms -0 - -0 - -1 - - -28E1A122-EFCD-47B3-8911-01DD4EC14CBE -Microsoft -<Form > - <ExtendedAttribute Attribute="Authorization" Caption="Authorization:" AttributeID="{8A90C9C1-B9DF-4761-B679-9D361A79A00A}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <GroupBox Name="ExistingKey" Caption="Existing Key" ControlAsLabel="No" > - <ExtendedAttribute Attribute="Assembly" Caption="Assembly:" AttributeID="{86046A95-6A5E-4CAD-B838-3E9B810F1DCD}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <HorizontalLayout Name="HorizontalLayout1" > - <ExtendedAttribute Attribute="AssemblyFile" Caption="Assembly file:" AttributeID="{676F268E-8327-40DE-8AF1-06CD3FA20A5D}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - </HorizontalLayout> - <GroupBox Name="GroupBoxPrivateKey" Caption="Private Key" ControlAsLabel="No" > - <ExtendedAttribute Attribute="PrivateKeyFile" Caption="File:" AttributeID="{2DD91865-4718-435B-A571-94FF65927147}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="PrivateKeyEncryptionPassword" Caption="Encryption password:" AttributeID="{806CB29F-6F2B-42F9-8472-F48290458C78}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="PrivateKeyDecryptionPassword" Caption="Decryption password:" AttributeID="{6E1BFB12-5FCD-40AE-AA68-0E9DE4D09181}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - </GroupBox> - </GroupBox> - <GroupBox Name="GroupBoxGenerateNewKey" Caption="Generate new Key" ControlAsLabel="No" > - <ExtendedAttribute Attribute="Subject" Caption="Subject:" AttributeID="{D28A9FE4-0337-42B6-AF43-A1C01DD9575B}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="EncryptionPassword" Caption="Encryption password:" AttributeID="{EA04F4EA-ED59-4AC9-A10F-81D0A4096B34}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="StartDate" Caption="Start date:" AttributeID="{9AD49E98-95CA-427B-BB19-79547A0366F8}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="ExpiryDate" Caption="Expiry date:" AttributeID="{2A387087-B851-432D-9A77-D66963A191E1}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - </GroupBox> - <ExtendedAttribute Attribute="ActiveForBeginDialog" Caption="Active for begin dialog" AttributeID="{A9955921-B956-4B3E-A3F2-D438330E8919}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> -</Form> - -0 - -1276605960 -obalen -1 -24956 - - - - -CriterionTargetItem -1D7BFA3D-0CC2-46F4-9122-02BDC8563018 -Criteria -0 - -0 - -1 - - -360D69FE-F9E9-4D59-881A-C61955E8D069 -HasAssemblyFile -%AssemblyFile% -0 - -0 - -1 - - -ExtendedAttributeTargetItem -5D73DC14-28AE-4255-9403-F1E9FEE659F8 -Extended Attributes -0 - -0 - -1 - - -2DD91865-4718-435B-A571-94FF65927147 -PrivateKeyFile -0 - -0 - -Specifies the complete path, including file name, to the private key. The private key path name can be a local path or a UNC path to a network location. The file will be accessed in the security context of the SQL Server service account. This account must have the necessary file system permissions. -1 -3 -0 - - -76E9AA22-B8B4-4AED-BE6B-7F74F5FC4AA9 -Executable -0 - -0 - -If the EXECUTABLE option is used, the file is a DLL that has been signed by the certificate. -1 -0 - - - - -CriterionTargetItem -BA592699-6BF9-48E2-8544-4C3A362D720E -Criteria -0 - -0 - -1 - - -046FF4E2-016F-4B53-8692-3FDC24C1E40A -HasPrivateKey -%PrivateKeyFile% -0 - -0 - -1 - - - - - -ExtendedAttributeTargetItem -CCEA8E9E-58E3-45C1-AFD6-D63A1C6F0B4D -Extended Attributes -0 - -0 - -1 - - -806CB29F-6F2B-42F9-8472-F48290458C78 -PrivateKeyEncryptionPassword -0 - -0 - -Specifies the password that will be used to encrypt the private key. -1 -12 -0 - - -6E1BFB12-5FCD-40AE-AA68-0E9DE4D09181 -PrivateKeyDecryptionPassword -0 - -0 - -Specifies the password required to decrypt a private key that is retrieved from a file. -1 -12 -0 - - - - - - - - - - -4EF99BFE-E58F-431C-96F8-4695A802091B -HasSubject -%Subject% -0 - -0 - -1 - - -ExtendedAttributeTargetItem -A4BADE30-DD25-4CB8-B38C-90521AF2F97D -Extended Attributes -0 - -0 - -1 - - -74133662-5446-4740-B00F-AE3B4E72B3D6 -EncryptionPassword -0 - -0 - -Use this option only if you want to encrypt the certificate with a password. -1 -12 -0 - - -9AD49E98-95CA-427B-BB19-79547A0366F8 -StartDate -0 - -0 - -Is the date on which the certificate becomes valid. If not specified, StartDate will be set equal to the current date. - - -1 -2 -0 - - -2A387087-B851-432D-9A77-D66963A191E1 -ExpiryDate -0 - -0 - -Is the date on which the certificate expires. If not specified, ExpiryDate will be set to a date one year after StartDate. -1 -2 -0 - - - - - - - - -732D17A6-FEBC-484A-93E1-F63463B319CE -Custom Symbol -0 - -1152539282 - -1 -0 -(2400, 2400) -128 -14737632 -STRN 0 Arial,8,N -DISPNAME 0 Arial,8,N -LABL 0 Arial,8,N -4 -1 -2C0AF450-B1DB-4F8E-BD07-02F73824B289 -1 -1 - - - - -1A708B71-B442-4237-B3B3-AA3B003FD607 -SymmetricKey -0 - -0 - -Defines a symmetric key for data encryption -1 -Symmetric Key -Symmetric Keys -0D0000000800000008000000424D7E000000000000003E000000280000001000 -0000100000000100010000000000400000000000000000000000000000000000 -000000000000FFFFFF00FE3F0000FC0F0000FC070000FC070000FC070000FC07 -0000FC070000E0070000E0070000C0030000C0030000C0030000C0030000C003 -0000F8030000FE1F0000424D3605000000000000360400002800000010000000 -1000000001000800000000000001000000000000000000000000000085000000 -000000000C2F40000E73AF0011344600197EC1001B2023001B78A6001E1E1E00 -2053750020547400224C6C002285C3002354790024577B002470A30025597700 -258AD000285F88002A5A71002B5E79002B79A7002B91E1002C5A70002C608500 -2D6389002D92D8002E94EC003483AD003499DF00369AF800369CF0003A8BB000 -3CA1E6003E84B3003EA4FA00406C8A004091DB004093B3004098DC004397B500 -439FDA004496DA0044A8EE004595AB004699D20047585F00489EB8004A636D00 -4AAEF8004FA0DB004FADCB00518FB10052AFFF0052B4E40053AAFE0054B8EC00 -55B3F40055B4DB0055B9F30056565600585C5F0059AADA0059BEFF005B92AE00 -5FC5FE0060C6FE0061BCF10061C7FE00626D720062C6FF0064A6C8006AB9F000 -6DB5D0006FD5FF00707070007475760076AAC6007893AA0079A8D20079B3CF00 -7A93A1007AD3F6007B7C7D007BB0CE007BCDE2007CC7F7007DAFCF007F97A800 -7FCDFB008080800081828200818C940081D8FB00828282008484840085888B00 -85BDD90088AFC90089AFC6008A8A8A008DADC00091B3C90092F8FF0093A8B700 -93DBFF009999990099BFDF009CA0A4009CA4AA009EE0FD009EE7FE00A1A1A100 -A2A2A200A2D3FB00A6A6A600AAAAAA00ACACAC00B0B1B200B0B3B400B3B8BC00 -B4B4B400BABABA00BCE6FE00BEBEBE00C0EFFE00C2C2C200C6C6C600C7C7C700 -CDCDCD00CECECE00D7D7D700E6E6E600F5F5F500000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000442D2F000000000000000000000000523B5D032B0100000000 -0000000000005E82804C582E16000000000000000000597E7646382712000000 -0000000000005A7D75332A25130000000000000000004B816B48201F0F000000 -0000000000004A7050281C1B090000000000003C07056C5F3F45191408000000 -0000006979573A373940100E0D0000000000637F7456403E66491E2623110000 -0000708270534122325455343D0C000000007383724F431D0402061A310A0000 -00006F847E60683630350B1529170000000078777B6A717A7C6E5C4224180000 -0000000000675B4D4E476D512C21000000000000000000626465610000000000 -0E0000000100000005000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00FFFFFFFFFFFFFFFFFFFE00FFFFFC00FFFFF800FFFFFC -00FFFFF800FFFFF800FFFFF800FFFFF800FFFFF800FFFFF800FFFFF800FFFFF0 -00FFFFF000FFFF0000FFFC0000FFFC0000FFFC00003FFC00003FFC00003FF800 -003FF800003FF800003FF800003FF800003FF800003FFF80003FFFE0003FFFFE -00FFFFFF03FFFFFFFFFF424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000004040400 -7777770070707000020202000102030046617200315162003A3C3D0000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000062626200B2B2B200 -D2D3D300A6A7A700305468003C82A30062CBE60072CFDD00377B97001B384B00 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000065656500D0D2D200EEF0F000 -E0E0E000D5D5D5002B8CC300D8EDF6008FE1FC007EE3FC0061C4DA00185C8400 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000A4A5A500EDF2F200 -BABABA00D4D4D40067879B0089C1DB00B9E7F9002E94FB0074D4E100175B8400 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000A0A0A008D8D8D00D5D5D500 -AEAEAE00D5D7D80049687D00247FB50090D7F3002D93F90063C6DE00175A8300 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000063636300D8D8D800D5D5D500 -A4A4A400B0B8BD004DA3D300B7E9FA005BC0F3002C92F90064C3DA00165A8200 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000069696900B5B5B500E7E7E700 -9B9B9B00D9D9D9003F729300B8E2EE0056BCEF002A90F70063C3DC0016598200 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000004646460091919100D5D5D500 -92929200BDC0C2002A5C7C0058B0DD004EB5E9002990F60060BCD90016598200 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000001C1C1C0095959500DDDDDD00C7C7C700 -8F8F8F0091A8B90055A6CD0095E1FB0046AEE3002F97F5005CB8D80015578000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000006B6B6B00C5C5C500C4C4C400 -8C8C8C00ACADAE00336C8C009BE9F30040A6DE002E96F4004FAFD50015578000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000040404006464640090909000B9B9B900 -8A8A8A007D899100196092005FBAE9003CA1DA002C95F20050AED50015578000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000007A7A7A00C6C6C6009B9B9B0099999900 -7C8489005BADD3006CCAEA003AA3FB003397D6002B94F20048A3D30014567F00 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000009A9A9A00CECECE009A9A9A008C8C8C00 -727B820056B1D7008EECFB003199FB002E95D3002C95F000409ECC0014567F00 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -5F5F5F0050505000464646001B1B1B00898989007395B2005F697200797F8300 -4A6E8C0059B7E30081E6FD003AA4FC002C90D000349CED003F98CC0014557E00 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000038383800A9A9A900 -BEBEBE00ACACAC00989898005A6770004794C70045B0FC003DA2EF003089BF00 -266C9A0040A5DD0076DCFC0046AEFC002389CC003199EC00358FC40013547D00 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000A9A9A900CFCFCF00 -B4B4B400C1C1C100CBCFD3004D95D1006FD5FC0050B5FB0059BFFC007BE3FD00 -88EBF90072D9F80067CEFC004EB6FC001D84C4003199EB002D83C20013537D00 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000A3A3A300D4D4D400 -AEAEAE00B1B1B100BAC1C60057A9DD006CD3FC004EB2FB004CB2FB0072D5FA00 -90F7FD0095FBFD0074D9FC005CC3FD00278ED4003098F4003A90DF004579A100 -3B566B0017222A00000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000A9A9A900DCDCDC00 -A7A7A700A5A5A500B0B8BE0053ACDD007AE2FC0044ADFC003DA6FC0063C8FC00 -87EDFD0097FBFD0081E9FD0064CAFC004BB2FA002B95FC003DA5FA0049ADF000 -4EADE6004F7A9C00000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000BEBEBE00DFDFDF00 -ABABAB009D9D9D00A7AFB70065B9E5007AE2FD003DA6FD00329DFD0053BAFC00 -6BCCDC0091EAF000A1F1F70083DBF90068C4FB003D9FFD00339CFD0050B6FB00 -61C7FC00527E9D00000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000005A5A5A00C9C9C900E4E4E400 -A7A7A7009A9A9A009DA5AE005EB5E9007DE3FD0044ADFB002993FC00339AE400 -1176B300136F9E002985AB007CC1D30099D1E100B8E0EF008DC9FA0047ABFA00 -54BBFC00517D9D00000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000061616100D6D6D600E7E7E700 -ABABAB009C9C9C00929CA6006BC6ED0083EAFD003CA5FD002E99FF00349DF500 -1076BC001076B500096BA300036698000E6D9E004092B8002F8FEA0037A0FA00 -50B7FB00527E9E00000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000059595900EFEFEF00E7E7E700 -B0B0B000A3A3A3008E99A30076D2F00086ECFD003BA5FD002E99FF003CA2ED00 -3093D000157AC1000F76BF001D83C7000B70A9001C779D001B81CD0038A0FA00 -44A9FA00507C9E00000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000057575700F2F6F600EAECEC00 -BDBDBD00A6A6A6008E9AA50075D4F50084EEFC0048A8FB002791FD002993FD00 -3FA7F70059B6E70045AAE0003396CD000F75BB001072AF002083C5002D97F900 -40A7F6004F7C9C00000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000063636300F5FEFF00F3FBFC00 -EFF6F700DDDDDD00A6B3BE0094DAF600DCFCFD00CAE7FC00A7D5FC0084C8FD00 -3EA7FB004BB2FC0063C4F40065C4F30064BEDF001F83C6002286CF0038A1F800 -329CFC004F7B9E00000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000031313100676767009E9E9E00 -CFD4D400E4E4E400D8E6F30084C7FB00BAE0FE00D1EEFE00E5F9FE00E1FAFD00 -CEF2FC00B9ECFE008FDDFB0079D5FC0069C9F8006FD2FB0055ADDD0045A6F500 -2E99FF004E7A9E00000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000005050500 -1A1A1A008D8D8D009192930093B0CE0069B2EF0059AEF9007AC3FC0079C3FC00 -A4E0FD00B8F5FC00C7F9FE00CCF2FE00BCEFFD008BE1FD0087DEFD006BD0FB00 -3CA6FD004E7A9E00000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000C0C0C004C4C4C005656560091989F007CA3C0006FA1CF00 -7AB7F00062B5F9006DBFF30088D5F800B1E6FD00CFF3FD00AEF5FA0091EFFD00 -64BEF200304B5F00000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000040404002222220089898900 -888A8B00878A8C00488FCD0055A9F50069B5FC006AC4F60046AAE9000B1B2500 -0305070000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -46464600464646004846460064565600655555003D4343000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 - -1 -SymmetricKey -1 - - -ExtendedAttributeTargetItem -073FC381-B55D-44FA-8BD0-7F8A47A81688 -Extended Attributes -0 - -0 - -1 - - -EC04B1A2-A7C8-4792-BF23-7776CF85262B -Authorization -0 - -1285836574 -psitja -Specifies the name of the database user or application role that will own this key. -1 -15 -4BA9F642-DAB1-11D1-9944-006097355D9B -User -0 - - - - - -E111BEB0-3A9C-44AB-8E77-4A10F7328732 -BaseCertificate -0 - -0 - -Specifies the name of the certificate that will be used to encrypt the symmetric key. -1 -15 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Certificate -0 - - - - - -9994E9EA-54E8-41BC-A6D4-D20AA94D733B -Password -0 - -0 - -Specifies a password from which to derive a TRIPLE_DES key with which to secure the symmetric key. Password complexity will be checked. You should always use strong passwords. - - -1 -12 -0 - - -062176EB-EEC8-4831-A112-C8C5AAB6A255 -BaseSymmetricKey -0 - -0 - -Specifies a symmetric key to be used to encrypt the key that is being created. -1 -15 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -SymmetricKey -0 - - - - - -3BDE0B12-CF1D-4AE3-9347-0BB655259773 -BaseAsymmetricKey -0 - -0 - -Specifies an asymmetric key to be used to encrypt the key that is being created. - - -1 -15 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -AsymmetricKey -0 - - - - - -7352FC16-A0A7-4FD4-A237-6FFADF842660 -KeySource -0 - -0 - -Specifies a pass phrase from which to derive the key. - -1 -14 -0 - - -E738CB61-D749-4EE8-9534-BF47074E5D23 -Algorithm -0 - -0 - -Specifies the algorithm used to encrypt the key -1 -12 -TRIPLE_DES; RC2; RC4; RC4_128; DESX; AES_128; AES_192; AES_256 -1 -0 - - -8C7BA2E3-2201-4359-AB25-82914E6ABE0C -IdentityValue -0 - -0 - -Specifies an identity phrase from which to generate a GUID for tagging data that is encrypted with a temporary key. -1 -12 -0 - - - - -ExtendedCollectionTargetItem -1BF82D25-4D0E-4056-9A42-5B5EC5E0DF53 -Extended Collections -0 - -0 - -1 - - -C26A451C-F878-463B-9847-D61C7AA5772E -Authorization -0 - -1285836574 -psitja -1 -SymmetricKeys -4BA9F642-DAB1-11D1-9944-006097355D9B -User - - - - - -37C25AB8-762F-4D01-832C-187535B1678F -BaseCertificate -0 - -0 - -1 -Symmetric Keys -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Certificate - - -00C2EC70-8B9E-426F-85FD-9D57B6641F87 -BaseSymmetricKey -0 - -0 - -1 -SymmetricKeys -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -SymmetricKey - - -EE8F3B0D-2AEB-4492-B92B-3AE7609E802A -BaseAsymmetricKey -0 - -0 - -1 -SymmetricKey -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -AsymmetricKey - - - - -FormTargetItem -777D159B-8FA7-4477-9B94-11F22AB315C7 -Forms -0 - -0 - -1 - - -0218C0AD-E2B9-4463-BB7E-2A66843B5011 -Microsoft -<Form > - <ExtendedAttribute Attribute="Authorization" Caption="Authorization:" AttributeID="{8A90C9C1-B9DF-4761-B679-9D361A79A00A}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <GroupBox Name="GroupBoxEncryptingMechanism" Caption="Encrypting Mechanism" ControlAsLabel="No" > - <ExtendedAttribute Attribute="Certificate" Caption="Certificate:" AttributeID="{E111BEB0-3A9C-44AB-8E77-4A10F7328732}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" LeftText="No" /> - <ExtendedAttribute Attribute="Password" Caption="Password:" AttributeID="{9994E9EA-54E8-41BC-A6D4-D20AA94D733B}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="BaseSymmetricKey" Caption="Symmetric key:" AttributeID="{062176EB-EEC8-4831-A112-C8C5AAB6A255}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="BaseAsymmetricKey" Caption="Asymmetric key:" AttributeID="{3BDE0B12-CF1D-4AE3-9347-0BB655259773}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - </GroupBox> - <GroupBox Name="GroupBoxKeyOptions" Caption="Key Options" ControlAsLabel="No" > - <ExtendedAttribute Attribute="KeySource" Caption="Key source:" AttributeID="{7352FC16-A0A7-4FD4-A237-6FFADF842660}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" /> - <ExtendedAttribute Attribute="Algorithm" Caption="Algorithm:" AttributeID="{0A8EECA4-8381-4C29-8873-0FB6F03AEF10}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="IdentityValue" Caption="Identity value:" AttributeID="{8C7BA2E3-2201-4359-AB25-82914E6ABE0C}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - </GroupBox> -</Form> - -0 - -1276605960 -obalen -1 -24956 - - - - -67B227C6-314B-4A3F-BBF8-DD155FA39C4C -Custom Symbol -0 - -1152537225 - -1 -0 -(2400, 2400) -128 -14737632 -STRN 0 Arial,8,N -DISPNAME 0 Arial,8,N -LABL 0 Arial,8,N -4 -1 -54C3E4A5-1AE3-4728-AF6F-569AF6A54914 -1 -1 - - - - -C32D6154-9D16-41D6-86A0-325C513C8C89 -AsymmetricKey -0 - -0 - -Defines an asymmetric key for data encryption -1 -Asymmetric Key -Asymmetric Keys -0D0000000800000008000000424D7E000000000000003E000000280000001000 -0000100000000100010000000000400000000000000000000000000000000000 -000000000000FFFFFF00FCFF0000F8FF0000F0F30000F0E30000F0C30000F0C3 -0000F0870000C0070000800F0000000F00000007000000070000000F0000000F -0000001F0000803F0000424D3605000000000000360400002800000010000000 -100000000100080000000000000100000000000000000000000000008A000000 -000000002A91DB002B80BE002D3A44003031340031313100315C7300316C9400 -3185C700328BD500339AE300347B9F003680BF00378CDB0039789D003B484F00 -3B87CC003C87B7003D3D3D003E3E3E00408AB00040A2D60043ACFF0046454500 -4662780046A8E90047B1FF0049AFFF0049B7FF004B93D4004CB2EE004D4D4D00 -4DABFF004E4E4E004F4F4F00504E4E005050500051B5F9005353530053C2FF00 -54545400545B620054BAFF0055A7D3005788B60059595900595B5C005A6D7A00 -5C7F96005CA6BB005CAED5005D5D5D005DC2FD0061CCFF006262620066B3DE00 -67B1E60069A5B0006DACCC0070C5E900717F880071B0CD007284920072CAFF00 -72D8F6007373730073CAFD0076A0BF00778A920077C1D4007EC4EB007F797600 -80E9FF0089898900899095008990960089E2F5008CF5FF008E8E8E0093D1FC00 -969492009B8583009B9997009BC2D9009BE1E6009FCDE800A0837E00A09B9700 -A2A2A200A3A9A500A6A4A200A6A6A600A7D6F500A8C2D400A8C9E400AAA9A900 -AB8B8600ABA0A200AE9C8800AEA9A900AFAAA400B0ADAA00B1A4A200B1B1B100 -B1F5FF00B2AFB000B4B4B400B5B4B300B6ABA100B7B7B700B7F1FE00BADAFB00 -BBBBBB00BCBAB800BCBABE00BCC0C200BCCBD900BFBFBF00C1BBB500C1C1C100 -C6C5C500C6EBFF00C7C7C700C9C7C500CAB09500CBCBCB00CCCCCC00D2D0CF00 -D2D2D200D4D4D400D6D6D600D8D8D800DADADA00DFDFDF00E0E0E000E1DFDE00 -E2DCDA00E9E9EA00000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000004E4E000000000000000000000000004E26050000000000000000 -0000000081847712000000000F330000000000007E7D6A170000003D31240000 -000000007A7A582100005346324100000000000070754E3600003A420B410000 -000000005B5A472D005D3B1E060000000000281F714A30042F2B19153C000000 -006D705F693727111434010E00000000857E67885535164D480A022900000000 -8283524B401C0D394C1B080349000000808957436820105144382A0749000000 -788687746F793F45560C1A180000000076622E65735E6E54601D094900000000 -7F7C502213233E6159252C0000000000007B6C646B6366725C4F000000000000 -0E0000000100000005000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00FFF8FFFFFFF07FFFFFE07FFFFFE07FFFFFE07F03FFE0 -7F03FFE07E03FFE07E03FFE07E03FFE07E07FFE07807FFC0780FFF80F00FFF80 -201FF000001FF000003FE000003FE000007FE000007FE000007FE000003FE000 -003FE000003FE000003FE000003FE000007FE000007FE1E000FFE00000FFF000 -01FFF80023FFFF03FFFF424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000003F404100828485006161610051555900 -2A2F330000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000005D5D5D00C3C3C300EAEAEA00EEEEEE0079797900 -2C2C2C0000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000019191900ABABAB00F8F8F800E1E1E100C6C6C600B7B7B700 -3030300000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000063636300CFCFCF00CFCFCF00A9A9A900C6C6C600 -3737370000000000000000000000000000000000000000000000000000000000 -262C2F00303C44002346580023414F0021222300000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000D0D0D0086868600C2C2C200C2C2C200A7A7A700C0C0C000 -3131310000000000000000000000000000000000000000000000000000000000 -4999C40097C3E1006ACEE90065C3DD002A566B00010101000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000013131300BFBFBE00EAEAEA00AFAEAE00B3B3B300A6A6A600 -2D2D2D000000000000000000000000000000000000000000000000000B0B0B00 -639FC900EBF6FA0075D9FD008AEFFE0037869F00000203000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000062626200E6E6E600ADADAD00B6B6B6009F9F9F00 -2424240000000000000000000000000000000000000000000C1F290041729300 -327FAD00D4F4F90059B1FB0075DAFE004694A900000203000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000151515008C8C8C00BCBCBC009A9A9A00BDBDBD0084848400 -4242420000000000000000000000000000000000000000001533430094C5E200 -A0E0FE006AD0FB003BA1F2008AF0FD00306D8D00000102000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000016161600C7C7C700D1D1D10092929200BEBEBE0078787800 -4A4A4A0000000000000000000000000000000000000000001C313E0069ADD200 -A7E9FE003BA1EA0056BBF50056B9DD0012314200000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000001A1A1A00A5A5A400DBDBDB008E8E8E00BEBEBE006E6E6E00 -535C6300000000000000000000000000000000000E181D003E7AA6003F9DCE00 -95DAF5002F90E2006DD2F3000F68960033393D00000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000606060008E8E8E00B1B1B10086868600C0C0C00062626200 -5D606300000000000000000000000000000000006C98B5007EC5E30065C5FB00 -3FA5E7004CB0EC0055B7DF00546E7F0000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000006B6B6B00C9C9C900ACACAC00A0A0A00080808000BEBEBE0058585800 -000000000000000000000000000000002936400060869F0067BDDD0079D3FA00 -268DD6006BD1F9003683AC000205070000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000064646400D0D0D000ABABAB009797970082828200B6B6B60047515900 -33363800111315000000000021526B0058ABCE003C99D7004CAFF4003B9CDA00 -399DDD0052B8E6001E455D000202020000000000000000000000000000000000 -00000000000000000000000000000000262626008B8B8B00787676004D4D4D00 -4B4B4B0079797900C6C6C600AEAEAE008D939800568BAF004EA2D6002F95DA00 -2A506C00545E660013191B002479B00091F2FD003FA5FD0039A0F5001D81C600 -56BCF000217DB90032373B000000000000000000000000000000000000000000 -00000000000000000000000000000000AEAEAE00C2C2C200B4B4B400C7C7C700 -B3B1B10085858500B1B1B000B1B1B1006F96B0005CC1F60065CAFD0052B9FD00 -53B5EA00266893003451650060C3E50080E5FD00379DFD00258ADE00288ED300 -4CB2EB0054758D00000000000000000000000000000000000000000000000000 -00000000000000000000000057575700D3D3D300C2C2C200AFAFAF00D0D0D000 -F2F2F200EFEFEF00BDBDBD00B0B8BD0043A9E70071D6FD0058BEFD004DB3FE00 -72D7FE0079DFF5003CA1DE0071D7F8005EC4FE003399FA00157BBE004FB5F200 -398BBF0003090C00000000000000000000000000000000000000000000000000 -0000000000000000000000005A5A5A00D9D9D900C0C0BF00A3A3A300C3C3C300 -E6E6E600FAFAFA00E4E4E40079B3DD006DD2FB0073D8FD003CA2FD004DB3FE00 -72D7FE0095FAFE0085EBFB006AD1FD0051B7FE002388D3001B82C3004AB0F300 -194F730001020200000000000000000000000000000000000000000000000000 -0000000000000000000000005E5E5E00DEDEDE00C0C0C0009A9A9A00B9B9B900 -D8D8D800F1F1F100DCE7F00068C2F5007EE2FE0059BFFD00359BFE004DB3FE00 -74D9FE0095FAFE008EF3FE006ED4FE004BB2F6000C6EA4003298E4002888CD00 -262D320000000000000000000000000000000000000000000000000000000000 -000000000000000004040400A2A2A200E3E3E300C2C2C2009A9A9A00ACACAC00 -92929200ABABAA0090BEE00070D5FA007BE2FE0042A7FD00359BFE004DB3FE00 -77D9EB009AFCFD008EF3FE006ED4FE004EB5FB002287D400369CF30027587C00 -3737370000000000000000000000000000000000000000000000000000000000 -000000000000000005050500BBBBBB00E6E6E600C3C3C3009E9E9E00A3A3A200 -7776750085888B0048A7EC0086EBFE006DD3FE00349AFE00359BFE00368FD000 -297296006EC1D6009BF0F80075D6FE0050B7FE00359BFE00379EF30030658B00 -3E48500028282800000000000000000000000000000000000000000000000000 -000000000000000005050500C5C5C400EAEAEA00C3C3C300A3A3A3009B9B9B00 -7B7875008AB5DF007EE5FD008CF1FE0040A6FE003399FF003296F60052708800 -A0939400616E77005390AD00B4EBF80067BFFE00359BFE0040A7FD0053B8F800 -3589BD0035404800000000000000000000000000000000000000000000000000 -000000000000000004040400CBCBCB00EFEFEF00C8C8C800AAAAAA009A9A9A00 -7C838B0064B0E400B1FAFE00A5EEFE00409FFC003399FF003194F20054789300 -A7959400C2A9A7007B777C003B789A009DD0E50077BFFC0041A8FE0059BEFD00 -50B5EE0047687F00000000000000000000000000000000000000000000000000 -000000000000000004040400D3D3D300F7F7F700D7D7D700B0B0B0009D9D9D00 -959EA3005DADF100DCF0FE00F1FEFE00CEEAFB0051A8FA00349AFD004CADF500 -5994B2007E797A00CBA2A2006E6B6E002B638C003D9BED0041A8FE0059BEFD00 -3D93C80010151900000000000000000000000000000000000000000000000000 -000000000000000004040400C2C2C200EBEBEB00FAFAFA00F1F1F100D8D8D800 -BEBEBE00ADBECB0075B2EE00A8D7FA00E3FDFE00D2F4FE007BC5FB0053B6FE00 -71D5FC0080B5BB009A878600A38D86003C739D002A90EB0041A8FE0054B9FA00 -3F7DAA00080A0A00000000000000000000000000000000000000000000000000 -000000000000000004040400908985008882800091919100D8D8D800F1F1F100 -EBEBEB00E7E7E700DFE1E200B3D3E90069B8FD00CEF5FE00D7FAFE00ABE5FE00 -77D8FE0094F8FC0082757500C59B9A004E6F87002E94F50041A8FE00399EE900 -243F510000000000000000000000000000000000000000000000000000000000 -000000000000000004040400BFAB9500F4CDA7006F6663008585850051515100 -B6B6B600B7B7B700C4C4C400DADADA00C2D1E00086C1EC00A0E0FC00C9F9FE00 -B8FAFE0096DEE0008C7E7D00C09B9800587E9C00349AFC003EA4FB002B7EBD00 -2828280000000000000000000000000000000000000000000000000000000000 -000000000000000004040400B6A49300F4CDA700726764003030300000000000 -0B0B0B0008080800212121008585850084848400B7B9BB0096BFE60076C3FA00 -BAEFF00079828200C59D9D008D7F7D004C9BD600359BFE00369CF400506E8300 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000004E494800E7C39F00C5AB98005D5959004A4A4A00 -17171700191919002C2C2C002C2C2C002C2C2C005B5B5B005E5E5E0055687800 -888C9400C0A5A400A994920081A8B0005AC0FE00359BFE003087D200050D1200 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000202020077757300DCBDA000F1DFCB00A7978E00 -6262620059595900575757005757570059595900636363008D878700CAB1AE00 -D6B8B800938584007299A400A1F6F90094F8FE0043AAFE00195A920000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000404040042404000A49B9300B6AAA100 -CDC1B400CFC8C300D6D6D600D6D5D500CBC5C500C4BABA00B3AAA900A69C9A00 -413E3D000C0C0C000C192500326582002C6081001E537C000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -8B8B8B008B8B8B008B8B8B008B8B8B008B8B8B008B8B8B000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 - -1 -AsymmetricKey -1 - - -ExtendedAttributeTargetItem -B7DDCFF0-9BBC-4EB6-91CA-84A542FF9272 -Extended Attributes -0 - -0 - -1 - - -29A3C50E-8D1B-4110-AEF9-11A178B21088 -Authorization -0 - -1285836595 -psitja -Specifies the owner of the asymmetric key. The owner cannot be a role or a group. -1 -15 -4BA9F642-DAB1-11D1-9944-006097355D9B -User -0 - - - - - -EA04F4EA-ED59-4AC9-A10F-81D0A4096B34 -EncryptionPassword -0 - -0 - -Specifies the password with which to encrypt the private key. If this clause is not present, the private key will be encrypted with the database master key. -1 -12 -0 - - -F28367B3-92B4-4BB2-B1B7-03F8C7A88FF0 -Source -0 - -0 - -Type of source (File, Executable file, Assembly or Provider) -1 -12 -File -File;Executable file;Assembly;Provider -1 -0 - - -99DEBC28-EA24-4533-A2AD-9FFA9AE8EF18 -KeyOption -0 - -0 - -Option type -1 -12 -Algorithm;Provider -Option type -0 - - - - -ExtendedCollectionTargetItem -081411C4-29FF-41E5-898A-F810EB3514D9 -Extended Collections -0 - -0 - -1 - - -A576EAEF-7C61-4F71-B041-A4618A35C1CC -Authorization -0 - -1285836595 -psitja -1 -Symmetric Keys -4BA9F642-DAB1-11D1-9944-006097355D9B -User - - - - - -D8584ACC-F536-4134-B473-BC22C3117A24 -Assembly -0 - -1327671821 -mledier -1 -Certificates -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Assembly - - - - -CriterionTargetItem -E5F5E526-B046-41A2-9C4A-E64C1E2D9090 -Criteria -0 - -0 - -1 - - -68696B86-DE56-4687-9C95-86284984E4FD -HasProvider -(%Source%=="Provider") -0 - -0 - -1 - - -ExtendedAttributeTargetItem -EDE27CB4-F837-48F6-B491-A029F60488A0 -Extended Attributes -0 - -0 - -1 - - -E2D5DB0B-7786-410D-8C90-795C94B348CB -Provider -0 - -0 - -Extensible Key Management provider name. -1 -12 -Provider -0 - - -27C70FA5-37A5-4B53-B08B-E52E01AD4458 -CreateDisposition -0 - -0 - -Creates a new key or use an existing one. -1 -12 -Create_New -Create_New;Open_Existing -1 -Create disposition -0 - - -17A37392-3408-4A32-870A-0856B1B0B8C9 -ProviderKeyName -0 - -0 - -Specifies the key name from the external provider. -1 -12 -Provider key name -0 - - - - - - -7698041E-B1B5-40EC-B35D-336F891E4A37 -HasNoProvider -(%Source%!="Provider") -0 - -0 - -1 - - -CriterionTargetItem -06EB27FC-278C-40D4-B948-EDF7BCDF75DC -Criteria -0 - -0 - -1 - - -C045B4AD-0B46-4E44-A6E6-BC677BB25817 -HasAssemblyFile -(%Source%=="File") || (%Source%=="Executable file") -0 - -0 - -1 - - - - - -ExtendedAttributeTargetItem -7BF9591A-AE75-45DD-9EA9-5194AC5BB3D9 -Extended Attributes -0 - -0 - -1 - - -676F268E-8327-40DE-8AF1-06CD3FA20A5D -AssemblyFile -0 - -0 - -Specifies the path of a file from which to load the key. -1 -3 -0 - - - - - - -787754D5-40A6-40A1-9078-818D9D0F270D -HasAssembly -(%Source%=="Assembly") -0 - -0 - -1 - - - - - -ExtendedAttributeTargetItem -FD0C57EA-DD98-4AE4-903C-3A4574BE5975 -Extended Attributes -0 - -0 - -1 - - -474958A4-B7C6-4F17-8F29-C3313B6A696F -Assembly -0 - -0 - -Specifies the name of an assembly from which to load the public key. -1 -15 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Assembly -Assembly -0 - - - - - - - - - - - -ExtendedAttributeTargetItem -D73F0C06-BAE9-4169-9DF4-8315FB83B62B -Extended Attributes -0 - -0 - -1 - - -0A8EECA4-8381-4C29-8873-0FB6F03AEF10 -Algorithm -0 - -0 - -Specifies the algorithm used to encrypt the key -1 -12 -RSA_512; RSA_1024; RSA_2048 -1 -0 - - - - - - - - -FormTargetItem -9590AE23-06DC-4968-919C-E57DF7A633C3 -Forms -0 - -0 - -1 - - -3B3323D5-351C-41C7-B1A5-5CD5628AC560 -Microsoft -<Form CaptionWidth="80" > - <ExtendedAttribute Attribute="Authorization" Caption="Authorization:" AttributeID="{8A90C9C1-B9DF-4761-B679-9D361A79A00A}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <GroupBox Name="GroupBoxExistingKey" Caption="Asymmetric key source" CaptionWidth="117" ControlAsLabel="No" > - <ExtendedAttribute Attribute="Source" Caption="Source type" AttributeID="{F28367B3-92B4-4BB2-B1B7-03F8C7A88FF0}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="Assembly" Caption="Assembly" AttributeID="{474958A4-B7C6-4F17-8F29-C3313B6A696F}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="AssemblyFile" Caption="Assembly file" AttributeID="{676F268E-8327-40DE-8AF1-06CD3FA20A5D}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="Provider" Caption="Provider" AttributeID="{E2D5DB0B-7786-410D-8C90-795C94B348CB}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="Algorithm" Caption="Algorithm:" AttributeID="{0A8EECA4-8381-4C29-8873-0FB6F03AEF10}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <HorizontalLayout Name="HorizontalLayout1" > - <ExtendedAttribute Attribute="CreateDisposition" Caption="Create disposition" AttributeID="{27C70FA5-37A5-4B53-B08B-E52E01AD4458}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="ProviderKeyName" Caption="Provider key name" AttributeID="{17A37392-3408-4A32-870A-0856B1B0B8C9}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - </HorizontalLayout> - </GroupBox> - <ExtendedAttribute Attribute="EncryptionPassword" Caption="Encryption password:" AttributeID="{EA04F4EA-ED59-4AC9-A10F-81D0A4096B34}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> -</Form> - -0 - -1276605960 -obalen -1 -24956 - - - - -5A001415-E94C-4922-8211-0D9B4BA0DC7B -Custom Symbol -0 - -1152538379 - -1 -0 -(2400, 2400) -128 -14737632 -STRN 0 Arial,8,N -DISPNAME 0 Arial,8,N -LABL 0 Arial,8,N -4 -1 -9D2984CF-0649-417E-A331-D1B4208BA52D -1 - - - - -EDA15103-515A-4DF3-ACC4-471F9113860C -FullTextCatalog -0 - -0 - -Defines a full text catalog allowing to group full text indexes. -1 -Full Text Catalog -Full Text Catalogs -0D0000000800000008000000424D7E000000000000003E000000280000001000 -0000100000000100010000000000400000000000000000000000000000000000 -000000000000FFFFFF00007F0000000F00000003000000010000000000000000 -0000000000000000000000000000000000000000000080000000800000008000 -0000FC010000FFC30000424D3605000000000000360400002800000010000000 -10000000010008000000000000010000000000000000000000000000A7000000 -000000000098CD000398D0000699D2000999D200099B7000099FA8000C9CD000 -0EA428000FA1500012A33F0013AB2A0014A0D10014A1B20014AA3C00159FD500 -16A5500017A1D10017A3B80019A1D40019A63B001AA4BE001AA643001AA73A00 -1AB342001CB43B001CB43F001DA4D2001DAF3F001E9AF4001EA4D2001EA93F00 -21A4D70022A9400023A6D40023AA4B0024AC540025A6D40025A7D40026A9DA00 -26BF4F0027A7D40027ACDF0027C04F0027C05100299FF50029A8D5002BA9D500 -2DADD7002EAAD5002FABD6002FAED80030AF5C0032ABDC0032ACD60032AF4C00 -33ACD70033ADD70033B38C0034ADD70034ADD80035A4F50035AEDE0036ADD700 -36AED80036AED90036B1580037AED70038B0DA0038B1E60038BD740039B0DB00 -3AA7F5003AB68D003EB1D9003FA9F6003FB2D60040B5640040BA8E0042B0DE00 -42B0DF0042B1E00042B2DD0042B49C0042BDE30043BBC20043BFE50044B4DB00 -45BA930046B3DC0046B88E0047ADF60047B4DA0047B4DB0047B5DB0047B75F00 -47B874004BC2A5004CB7DE004DAFF6004DB6DC004EBDF2004FB7DD004FCCEF00 -51BFBE0051C1E60056B3F7005ABCD5005ABDE3005ACFF2005DCAFC005DD3DD00 -61B8F70061BFE00061C7F40061D9FA0063C9F50065D2FC0066C5EF0066CFFD00 -67C48D0067CEFC0067D0FE0069C3E30069D0FE0069D6FD006BCDE6006CD3FD00 -6CD8FD006DCAF2006DD2FE006EC5D1006FD4FD006FDAFD0071C99E0072D6FD00 -72DDFD0076CA9F0079DCB8007ACAE6007BDEFD007BE5FE007CC4F9007ECCCE00 -81CEB80082CDE60082CFB80083C8F90085CFD20085EDFE0089D4F7008AD5DD00 -8BDCFD008DCDF9008ECDFA008EDEEF008EEEFE008EF4FF0096D8ED0096DDFA00 -A0ECF700A3F9FF00B1DDFB00C4E4FB00E6F3FD00E8F4FD00FFFFFF0000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -6A8EA2A4A6A6A6A6A60000000000000063072E2238A6A6A6A6A6A6A600000000 -4B139E6933252F4AA6A6A6A6A6A600003D357B98856D5630293671A6A6A6A600 -2D51579F84878073675440315CA6A6A61D4F3E921F4E827F7D6F5F5339A6A6A6 -1D4F45834D24777A7C58375A3F91A6A61D50656286603416141721684164A6A6 -1D52792A436B8F9078422374443BA6A61D598C75550512274C8949724726A5A6 -1D5D9C8D880A106E159497966C1EA399005EA19D95460861090D3C668B0C939A -00327E9BA08A0B1C0E3A8176200F709A00010101111B18191A060203045B6A1D -000000000000282C2B1D1D1D481D1D00000000000000000000001D1D1D1D0000 -0E0000000100000020000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00E1FFFFFF803FFFFF8007FFFF0000FFFF00001FFF0000 -01FF0000003F0000001F0000000F000000070000000700000007000000038000 -0003800000038000000380000001800000018000000180000001800000018000 -00018000000180000001800000018000000380000007E000000FFF800E1FFFFC -0FFFFFFE0FFFFFFFFFFF424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -000000000000000000000000FFFFFF02FFFFFF02FFFFFF02FFFFFF0200000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000098CD4F099CCFB7079ACDC018A1D17647B4DA2AFFFFFF08FFFFFF04 -FFFFFF02FFFFFF01000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000098CDFF069ACFFF4AC2E4FF54BDDFFF2BA9D6FF24A6D4D525A7D57A -42B3DA29FFFFFF06FFFFFF03FFFFFF02FFFFFF01000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -1E9AF4010398CEFF2EADDCFFAEE9F7FFC0F2FDFF89E2F5FF6ACFEAFF46B8DDFF -26A8D5FF1EA4D3D421A6D37942B3DA29FFFFFF06FFFFFF03FFFFFF02FFFFFF01 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -1E9AF4030198CFFF3AB2E3FF99DBF0FFC5F3FFFFA1ECFEFF99ECFEFF92EBFDFF -82E0F6FF62CDEAFF42B6DDFF26A8D5FF1DA4D2D321A6D37942B3DA29FFFFFF06 -FFFFFF03FFFFFF02FFFFFF010000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -1E9AF4070398D1FF64C3F0FF5EC0E1FFC0F0FFFFA9EDFEFF8EE8FEFF90EAFEFF -91EAFEFF91EAFEFF8DE9FDFF7DDFF6FF60CCEAFF41B5DDFF26A8D5FF1FA4D3C5 -26A8D46A69C3E11AFFFFFF06FFFFFF03FFFFFF02FFFFFF01FFFFFF0100000000 -0000000000000000000000000000000000000000000000000000000000000000 -1E9AF4090498D2FF7BCDFBFF28A7D4FFB3EAFBFFB3EEFEFF81E4FFFF84E6FEFF -87E6FEFF89E7FEFF8BE8FEFF8CE9FEFF8DE8FEFF88E7FCFF79DDF5FF5DCAEAFF -41B5DDFF25A8D5FF21A5D3B729A9D46A74C8E41CFFFFFF09FFFFFF05FFFFFF03 -FFFFFF01FFFFFF01000000000000000000000000000000000000000000000000 -1E9AF4080498D2FF7CCDFBFF20A6DCFFA2E1F5FFB8F0FFFF83E4FFFF7CE2FFFF -7EE4FFFF7FE3FEFF7FE4FEFF82E5FEFF84E5FEFF86E6FDFF87E6FDFF88E7FDFF -83E5FCFF75DBF5FF5BCBEBFF41B6DEFF2AAAD6FF2CA9D6B933ADD76E79CAE51D -FFFFFF09FFFFFF05FFFFFF020000000000000000000000000000000000000000 -1E9AF4040298D0FF84D2FCFF2DADE6FF6DC9E7FFB9F0FFFF94E8FFFF7AE2FFFF -7BE2FFFF7BE1FFFF7AE1FFFF7BE2FFFF79E1FEFF7AE2FEFF7CE3FEFF7EE3FDFF -7FE4FDFF81E5FDFF82E5FDFF80E5FDFF75DBF6FF5ECBEBFF44B7DFFF2DABD6FF -30ABD6BA39AFD85EFFFFFF09FFFFFF0200000000000000000000000000000000 -1E9AF4020298CFFF89D4FCFF41B6F2FF33ADD7FFB8EEFFFFA7ECFFFF79E2FFFF -79E2FFFF79E1FFFF79E1FFFF79E0FFFF78E0FEFF77DFFEFF75E0FEFF73DFFEFF -72DFFEFF74DFFDFF77E0FDFF79E2FDFF78E2FDFF7AE3FDFF7AE3FDFF76DBF7FF -64CBECFF38AFD9FF36AED86EFFFFFF07FFFFFF01000000000000000000000000 -1E9AF4010198CFFF8AD5FCFF4FBDFBFF19A3D5FFACE7FAFFB7EFFFFF7BE2FFFF -79E2FFFF79E1FFFF78E1FFFF77E0FFFF76DFFEFF76DFFEFF76DEFEFF74DDFEFF -71DDFEFF6DDCFEFF6ADBFEFF6ADBFDFF69DCFDFF6BDBFDFF71DEFDFF7CE2FDFF -83E4FDFF6CD0F0FF2BA9D4E4FFFFFF0BFFFFFF02000000000000000000000000 -1E9AF4010198CEFF8DD7FDFF50BEFBFF1DA5DEFF87D5EFFFB7EFFFFF8EE6FFFF -79E2FFFF79E1FFFF78E1FFFF77E0FEFF76DFFEFF76DEFEFF75DDFEFF74DDFEFF -74DCFEFF72DBFEFF6DDAFEFF67D9FEFF63D8FEFF60D6FDFF62D9FDFF69DAFDFF -74DDFDFF6ED3F4FF2AA9D6FF4BB6DC2BFFFFFF02000000000000000000000000 -1E9AF4010098CDFF93DAFDFF55C2FCFF32AFE9FF46B7DEFFA4E4C8FF8CDEBBFF -52CC96FF4FCC95FF4FCB96FF4ECB95FF4DCA95FF4FCB9BFF75DDFEFF74DDFEFF -73DCFEFF72DCFEFF4AC599FF46C191FF42BE8DFF3FBC8AFF3DB987FF42BB89FF -4EBD90FF5CC199FF36AFC2FF129E5DF0FFFFFF04FFFFFF010000000000000000 -000000000098CDFF98DEFDFF58C4FCFF4BBBF8FF17A1D1FFA3E4C7FFA2E5CBFF -69D8B4FF66DCBDFF5EDAB8FF55D1A4FF4ECA96FF4ECB9BFF74DDFEFF74DDFEFF -73DCFEFF72DBFEFF4BC59AFF49C598FF4DCBA4FF51CFB0FF50CEB0FF4FCCAFFF -5BCEB9FF6DCDB7FF47B7BDFF18A293F0FFFFFF07FFFFFF010000000000000000 -000000000098CDFF9DE1FDFF5CC9FCFF51BFFBFF109FD5FF95DCF4FFB4ECF7FF -7CD9B7FF6ED7B2FF58D4AAFF51CB9EFF6DDAEAFF74DEFEFF73DDFEFF73DCFEFF -72DBFEFF71DBFEFF71DAFEFF5CCFCAFF49C79AFF50D0ADFF4FCEAEFF4FCDB0FF -5ECBB0FF50BEA5FF53BDD6EB21A6D4D4FFFFFF09FFFFFF010000000000000000 -000000000098CDFFA1E4FDFF62CEFDFF55C3FBFF22A9E0FF58BEE2FFB5ECFFFF -B4ECFBFFA9E5D2FF9EE5CDFF87DBC0FF8EE3FEFF7EDEFEFF72DBFEFF72DBFEFF -72DBFEFF71DAFEFF70DAFEFF54CBB5FF4BCAA0FF4FCFA9FF4ECEAAFF57D0B2FF -53C3A1FF5CC7E7FF6ECCF2CC1FA5D3FF69C2E11AFFFFFF020000000000000000 -000000000098CDFFA6E8FEFF6AD5FDFF5ECBFCFF4DBEF8FF26A9DAFF49B7DEFF -77CDEAFF9CDDDDFFA7E5D5FF9FE1C2FFADE8EFFFB3EBFFFFB2ECFFFFA0E6FEFF -8FE2FEFF80DDFEFF6ED7F0FF47C395FF4CCEA2FF4CCEA4FF4CCEA6FF5ECFB2FF -55C7B4FF61CCF4FF6CCEF2CD36AFDBE42DAAD65BFFFFFF04FFFFFF0100000000 -000000000098CDFFAAEBFEFF73DBFDFF68D4FDFF5CCAFBFF54C3FAFF3DB6EFFF -28ACE4FF14A1D8FF1FA4D3FF3CB1D3FF65C3DEFF93D9F2FFA7E4F9FFB1E9FEFF -B0E9FFFFAFEAFFFFA4E6E3FF76D7B5FF4ACEA0FF4ACDA0FF52CFA9FF58C9A9FF -60CFDDFF5ECCF4FF69CEF2CE4EBCE3D22DABD68DF2FFFF08FFFFFF0100000000 -000000000098CDFFADEFFEFF79E2FEFF73DDFDFF6CD7FDFF65D1FCFF5FCCFCFF -58C7FBFF53C2FAFF2CAC6BFF2AAD54FF00995AFF01987FFF109EB5FF30ACD7FF -5ABFD2FF7ED2C8FF9AE0C7FF95E3CAFF49CD9FFF49CD9FFF58CFADFF51C7AAFF -6CD5FEFF5DCCF4FF68CFF2CF6CCCEEBD27A9D5C8F5FFFF0AFFFFFF0100000000 -000000000098CDFFB1F2FFFF7EE7FFFF7CE4FEFF78E1FDFF73DEFDFF6FD9FDFF -69D4FDFF64D0FCFF48BFBCFF30AD32FF18AC20FF009800FF009800FF01991AFF -049B5DFF0A9FD0FF86D9CEFF9BE5CEFF61D3ABFF4ACCA0FF5ACCACFF58CBCAFF -6BD5FEFF5CCCF4FF68D0F2CF8CDEFAA823A8D5FFE5FFFF0CCCFFFF0200000000 -000000000098CDFFB4F4FFFF84ECFFFF82EAFFFF7FE7FFFF7CE5FEFF79E2FEFF -76DFFDFF73DCFDFF70DAFDFF21A833FF2BAE2FFF0C9E1EFF5DC9FCFF56C4FCFF -50BFFAFF13A298FF56BFD5FFA1E5D0FF88DEC1FF64D2AFFF4EC29DFF64D1F0FF -6AD4FEFF5BCBF4FF69D0F2CF95E3FEA533B0DAEB2FAED74CDDFFFF0300000000 -000000000098CDFFB7F7FFFF89F1FFFF86EEFFFF84ECFFFF82EAFFFF80E8FFFF -7DE6FEFF7BE3FEFF78E0FEFF4CC5A1FF49B94CFF17A319FF50C5BCFF64D0FDFF -48BDCCFF099D24FF18A3D2FF9FE1D6FFA5E5D2FFACE5D8FFA1E0D8FF9BE1FEFF -8ADCFEFF6FD1F5FF6CD0F2CD96E2FEA346B9E1D922A7D48AE6FFFF0400000000 -000000000098CDFFB9FAFFFF8DF5FFFF8BF3FFFF89F1FFFF87EFFFFF84ECFFFF -82EAFFFF80E8FFFF7EE6FEFF74DFEEFF33AE3BFF48BA4EFF2AB15EFF6ED8FDFF -24AC5DFF28B031FF2BB585FF32ACD5FF60C2D7FF86D2D7FF9CDBE8FFAAE3FDFF -AEE6FFFFA5E1F9FFB3E6F8E4C8EFFEC365C7EAC41BA3D3B6EBFFFF0500000000 -000000000098CDFFBCFDFFFF92FAFFFF90F7FFFF8EF5FFFF8BF3FFFF89F1FFFF -87EFFFFF85EDFFFF82EAFFFF80E8FFFF36B865FF61C265FF109E10FF65D5DDFF -0D9E15FF4DC860FF50CB65FF45C472FF39B499FF0C9D95FF24A8DBFF27A7D4FF -4BB7DDFF71C8E6FF99D8EFF1A7DEF3DB46B9E6B60A9CCFEFFFFFFF0300000000 -000000000098CDFFBEFEFFFFA7FDFFFF9FFCFFFF92FAFFFF90F8FFFF8EF5FFFF -8CF3FFFF89F1FFFF87EFFFFF85EDFFFF72E1DFFF3DB13EFF43B647FF19A62EFF -29AD2FFF5CD073FF5BCF72FF5ED074FF5DC262FF32B18FFF52C1FBFF50BFFBFF -41B8F2FF22A8E1FF0F9DD1C625A7D4C2079ACEC10B9DD088FFFFFF0100000000 -000000000098CDEF6FD8ECFFAFFEFFFFB6FEFFFFBDFDFFFFB5FDFFFFACFBFFFF -A2F9FFFF99F6FFFF8EF4FFFF8AF1FFFF87EFFFFF38B553FF6FC974FF15A015FF -55C565FF67D47FFF66D37EFF76D688FF32AC32FF80D6ECFF7AD5FCFF6DCFFCFF -62C9FBFF2FAFE8FF0798D48D3AA7F5088ECDFA02FFFFFF010000000000000000 -000000000098CD4F0098CDDF0098CDFF1BAAD6FF38BDDFFF5ED1E9FF82E4F2FF -AEFDFFFFB4FCFFFFBAFBFFFFB3F9FFFFAAF6FFFF6CD09CFF58BD5BFF66C76EFF -71D78AFF72D78AFF73D78BFF77CF80FF1BA55CFF50C1E8FF6DD0F1FF87DBFAFF -8EDDFDFF57BFE9FF0398D0861E9AF4031E9AF401000000000000000000000000 -0000000000000000000000000098CD0F0098CD3F0098CD6F0098CD9F0098CDCF -0098CDFF1BAAD6FF2DB6DCFF53C9E6FF72DBEFFF2EAED8FF28A841FF8ADA99FF -7BDA94FF7BDB95FF85DD9CFF4DB84FFF0098718F0098CDAF0098CDBF0198CEFF -0198CEFF0198CEFF0098CD511E9AF40100000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -000000000098CD3F0098CD6F0098CD8F0098CDBF0098CD6F0098007F7ED085FF -8BDFA2FF85DE9EFF8FDB9EFF21A521DF0000000000000000000000001E9AF401 -0198CF100098CE201E9AF4010000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000098001F3DB03DFF -9BE2ADFF9AE4B0FF71C976FF0098006F00000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000014A0148F -44B344FF44B344FF22A622EF0098000F00000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 - -1 -FullTextCatalog -1 - - -ExtendedAttributeTargetItem -7513FE3A-A85E-4A1D-A837-3D50579ADBC5 -Extended Attributes -0 - -0 - -1 - - -9860EA2C-CD55-469B-B2B2-018714A345EB -Authorization -0 - -1285836609 -psitja -Specifies the owner of the full text catalog to the name of a database user or role. -1 -15 -%authorizationList% -4BA9F642-DAB1-11D1-9944-006097355D9B -User -0 - - - - - - - - -E68B2274-736A-449C-91D0-23E1535A03DD -FileGroup -0 - -0 - -Name of the SQL Server filegroup of which the new catalog will be part. -1 -12 -%fileGroupList% -0 - - -AF87A929-9CB5-402A-A476-17A2B9AD609C -Path -0 - -0 - -Root directory for the catalog -1 -11 -0 - - -D97D8985-E86C-4E16-B7B2-94F6448DE723 -AccentSensitivity -0 - -0 - -Specifies that the catalog is accent sensitive or accent insensitive for full text indexing -1 -12 -on;off -0 - - -244AA8A8-8A07-4984-8CD8-1A0F7730A73A -Default -0 - -0 - -Specifies that the catalog is the default catalog. -1 -false -0 - - - - -ExtendedCollectionTargetItem -47B8E9F3-90C1-444E-BAF4-B99C598D0825 -Extended Collections -0 - -0 - -1 - - -7DBF60D4-B5A3-421E-A23B-3DF86626DAAC -Authorization -0 - -1285836609 -psitja -1 -Full Text Catalogs -4BA9F642-DAB1-11D1-9944-006097355D9B -User - - - - - - - -TemplateTargetItem -39E9B588-5E83-4BC0-8601-1849358F3897 -Templates -0 - -0 - -1 - - -C7051309-651E-46FA-8117-66984FB7A7B1 -fileGroupList -list -.foreach_item(Model.Storages,,,,%Item1.Code% > %Item2.Code%) -%Code% -.next(\n) -0 - -0 - -Enumerates filegroups (storages) of the model -1 - - - - -FormTargetItem -56DA90E8-6961-4BA0-A629-597DD2A91E6E -Forms -0 - -0 - -1 - - -5B41314F-CE98-4109-9BC9-C146B61EB225 -Microsoft -<Form > - <ExtendedAttribute Attribute="Authorization" Caption="Authorization:" AttributeID="{8A90C9C1-B9DF-4761-B679-9D361A79A00A}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="FileGroup" Caption="File group:" AttributeID="{E68B2274-736A-449C-91D0-23E1535A03DD}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="Path" Caption="Path:" AttributeID="{AF87A929-9CB5-402A-A476-17A2B9AD609C}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="AccentSensitivity" Caption="Accent sensitivity" AttributeID="{D97D8985-E86C-4E16-B7B2-94F6448DE723}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="Default" Caption="Default" AttributeID="{244AA8A8-8A07-4984-8CD8-1A0F7730A73A}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> -</Form> - -0 - -1276605960 -obalen -1 -24958 - - - - -EventHandlerTargetItem -2FD06146-72CA-457A-B3BC-0A0264E05A70 -Event Handlers -0 - -0 - -1 - - -5F43C52E-7797-473F-BEC1-8F378DF9AC65 -Validate -0 - -0 - -1 -Function %Validate%(obj, ByRef message) - - Dim depd, depdObj - - - ' Compute new dependent object list - Dim DependentObjects - set DependentObjects = CreateObject("Scripting.Dictionary") - - ' Add the file groups to the dependent object list - Dim sFileGroup - sFileGroup = obj.GetExtendedAttribute("FileGroup") - if (sFileGroup <> "") Then - ' search for an existing storage - Dim fileGroup - for each fileGroup in obj.Model.Storages - if (fileGroup.Code = sFileGroup) then - if not (DependentObjects.Exists(fileGroup.ObjectID)) then - DependentObjects.Add fileGroup.ObjectID, fileGroup - end if - Exit For - end if - next - end if - - ' Remove old dependencies - Dim DependenciesToDelete - set DependenciesToDelete = CreateObject("Scripting.Dictionary") - - For each depd in obj.ExtendedDependencies - If (depd.Stereotype = "DBCreateAfter (Computed)") Then - If (DependentObjects.Exists(depd.InfluentObject.ObjectID)) Then - DependentObjects.Remove depd.InfluentObject.ObjectID - Else - DependenciesToDelete.Add depd.ObjectID, depd - End If - End If - Next - - Dim i, col - col = DependenciesToDelete.Items - For i = 0 To DependenciesToDelete.Count -1 'Iterate the array - set depd = col(i) - if not (depd is nothing) then depd.Delete - Next - - - ' Add new dependencies - col = DependentObjects.Items - For i = 0 To DependentObjects.Count -1 'Iterate the array - set depdObj = col(i) - set depd = obj.Package.ChildExtendedDependencies.CreateNew - If not (depd is nothing) Then - set depd.DependentObject = obj - set depd.InfluentObject = depdObj - depd.Stereotype = "DBCreateAfter (Computed)" - End If - Next - - ' Update Default of other full text catalog - Dim bDefault - bDefault = obj.GetExtendedAttribute("Default") - If (bDefault = True) Then - Dim ftc - For Each ftc in obj.Package.ExtendedObjects - If Not (ftc.IsShortcut()) Then - If ((ftc <> obj) and (ftc.stereotype = "FullTextCatalog")) Then - ftc.SetExtendedAttribute "Default", False - End If - End If - Next - End If - - %Validate% = True -End Function - - - - -F1604C45-AC4E-4824-89CC-BB9CEB6AA530 -Custom Symbol -0 - -1152538147 - -1 -0 -(2400, 2400) -128 -14737632 -STRN 0 Arial,8,N -DISPNAME 0 Arial,8,N -LABL 0 Arial,8,N -4 -1 -E96559E0-7B88-425D-89B3-86F3E82FE473 -1 -1 - - - - -04D73B04-611A-4748-B45D-D5A9D2FE7693 -XMLSchemaCollection -0 - -0 - -Defines an XML schema collection identifying an XML schema -1 -XML Schema Collection -XML Schema Collections -0D0000000800000008000000424D7E000000000000003E000000280000001000 -0000100000000100010000000000400000000000000000000000000000000000 -000000000000FFFFFF0000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000424D3605000000000000360400002800000010000000 -1000000001000800000000000001000000000000000000000000000068000000 -63493500644A36006C553E0080202000833B1500863C1300B7A29300B9A49500 -BBA69600BC998600BC9B8900BEA89900C0AB9C00C0AC9C00C0C0C000C2A58E00 -C4AF9F00C8B2A200CAB4A500CBB5A600CBB6A600CDB6A800CEB9A900CFB9AA00 -D1BAAB00D1BCAC00D2BDAD00D4BEAE00D4C5BA00D6C0B000D6C1B000D7C1B100 -D8C2B200DDBAA100DDBBA400DEB89D00DEB99F00DFB89E00DFBBA000E0B69900 -E0BCA200E1BDA300E1BEA400E2B19100E2B39500E2B49500E2BFA500E2BFA600 -E3B18F00E3C0A700E3C1A800E4AE8A00E4AF8C00E4C2A900E4C3AA00E5AD8800 -E5C4AC00E6AB8400E6AC8500E7A77F00E7A78000E8A57A00E9A47B00EBA37700 -F8DDCC00F9E0D000FAE2D200FAE4D600FAE5D700FAE5D800FBE2D300FBE4D600 -FBE6D900FBE7DA00FBE8DC00FBEBE100FCECE200FCEDE500FCEFE700FCEFE800 -FCF3EC00FDF0E800FDF0E900FDF4ED00FDF4EF00FDF5EF00FDF5F000FDF8F400 -FEF7F200FEF7F300FEF7F400FEF8F500FEFAF600FEFAF800FEFBF800FEFCFA00 -FEFCFB00FFFCFA00FFFDFC00FFFDFD00FFFEFD00FFFEFE00FFFFFE00FFFFFF00 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0E00000000000000000000000000000E0E06422525272D2B3437393B3D3F000E -0E0762094948454746414009623E000E0E080A0432312E29282624040A3C000E -0E0B056552514E4D4B4A4465053A000E0E0D0A05383635312F2A28050A33000E -0E10650A5B5B5856504F4C0A6530000E0E1103430343034303430303032C000E -0E13030303430343034303434327000E0E1643034343030F034303434323000E -0E19030303430303034303434321000E0E1B034303430343034303434322010E -0E1E6565666563605E5755060101010E0E206566666562615D5A54071C01020E -0E2065676564625F5C59530C01020E0E0E201F1F1D1B1A1817151412020E0E0E -0E0000000100000020000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF00FFFFFFFFF000001FF000001FF000001FF000001FF000 -001FF000001FF000001FF000001FF000001FF000001FF000001FF000001FF000 -001FF000001FF000001FF000001FF000001FF000001FF000001FF000001FF000 -001FF000001FF000001FF000001FF000001FF000001FF000003FF000007FF000 -00FFF00001FFFFFFFFFF424D3608000000000000360400002800000020000000 -20000000010008000000000000040000000000000000000000000000F5000000 -0000000080202000B45C5C00DE993D00E09D4500E09F4600E4A65500E8B87000 -EEBB7D00EEBB7E00EEBC7E00EEBC7F00EEBC8000EEBD8000EEBD8100EEBD8200 -EEBE8200EEBE8300EEBE8400EEBF8400EEBF8500EEBF8600EEC08600EEC08700 -EEC08800F0BF8500F0C59100F0CE9300F1C59200F1C79400F1C89400F2C99600 -F2D49D00F3CEA100F3CFA200F3D0A500F4C99600F4D0A300F4D2A900F5CE9F00 -F5D1A600F5D5AD00F6D0A000F6D3A900F6D4AC00F6D6A500F6D8B300F6DBAB00 -F7D5AC00F8D2A600F8DCBB00F8DDBC00F8E0B500F9D7B100F9DDBE00F9E0C300 -FADDBC00FADEBF00FAE2C800FAEAC800FBDDBE00FBDEBF00FBE4CB00FBE6CF00 -FBE8C300FCDBB600FCDFBF00FCE8D300FCF7D800FDDFC100FDE6CE00FDE8D200 -FDE9D500FDEBD800FDF2D600FEE2C700FEE2C800FEE4C900FEE4CA00FEE5CC00 -FEE8D300FEEDDB00FEEDDC00FEEDDD00FEEECE00FEEEDD00FEEEDE00FEEEDF00 -FEEFDF00FEF7DE00FFDEBD00FFDEBE00FFDFBE00FFDFBF00FFDFC000FFE0BF00 -FFE0C100FFE0C200FFE1C000FFE1C300FFE1C400FFE2C100FFE2C500FFE2C700 -FFE3C200FFE3C800FFE3C900FFE4C300FFE4C900FFE4CA00FFE4CB00FFE5C400 -FFE5CA00FFE5CB00FFE5CC00FFE5CD00FFE6C500FFE6CD00FFE6CE00FFE6CF00 -FFE7D000FFE7D100FFE8C800FFE8C900FFE8D200FFE8D300FFE9C800FFE9C900 -FFE9CA00FFE9CB00FFE9D300FFE9D500FFEACC00FFEACD00FFEAD500FFEAD600 -FFEAD700FFEBCA00FFEBCD00FFEBCE00FFEBCF00FFEBD600FFEBD800FFEBD900 -FFECCF00FFECD000FFECD100FFECDA00FFEDD100FFEDD200FFEDDC00FFEDDD00 -FFEED300FFEED400FFEEDD00FFEEDE00FFEEDF00FFEFCE00FFEFCF00FFEFD000 -FFEFD500FFEFD600FFEFDF00FFEFE000FFEFE100FFF0CF00FFF0D000FFF0D100 -FFF0D700FFF0D800FFF0D900FFF0E100FFF0E200FFF0E300FFF1D000FFF1D100 -FFF1D200FFF1DA00FFF1E300FFF1E400FFF1E500FFF2D100FFF2D200FFF2DB00 -FFF2E400FFF2E500FFF2E600FFF2E700FFF3D200FFF3D300FFF3E700FFF3E800 -FFF3E900FFF4D300FFF4E900FFF4EA00FFF5D600FFF5D700FFF5D800FFF5EB00 -FFF5EC00FFF5ED00FFF6D900FFF6DA00FFF6ED00FFF6EE00FFF7DB00FFF7DC00 -FFF7EF00FFF7F000FFF8DC00FFF8DE00FFF8F100FFF8F200FFF8F300FFF9DE00 -FFF9E000FFF9E100FFF9E200FFF9F100FFF9F300FFF9F400FFF9F500FFFAE000 -FFFAE100FFFAE400FFFAF500FFFAF600FFFBE200FFFBE300FFFBE500FFFBE600 -FFFBF800FFFCE400FFFCE500FFFCE700FFFCE900FFFCFA00FFFDE900FFFDEA00 -FFFDEB00FFFEEB00FFFEEC00FFFFDE00FFFFED00000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000003030303030303030303030303030303030303030303030000000000 -00000000039E9E9E9E9EA5A5A5A6A6A6AEAEAFB5B5B6BCBCBDC1030000000000 -00000000039D5A5A5A5A5A5C5C5C5F5F62626568686B6F6F74BD030000000000 -00000000039E5D5D5B5B5A5A5A5A5A5C5F5F626265686B6B6FBC030000000000 -0000000003A66060605E5E5D5D5B5A5A5A5C5C5F626565686BB6030000000000 -0000000003B0646363636161605E5D5D5B5A5A5C5F5F626568AF030000000000 -0000000003207067678A8A85848481807F7B7A7A7A7E7F6265AE030000000000 -000000000305C46A6A0F0F0E0E0D0C0C0B0A09080808085F62A6030000000000 -000000000303C572721111100F0F0E0E0C0B0B090808085C5FA5030000000000 -000000000303C67676757372716D6C6A67666461605D5B5A5C9E030000000000 -000000000303CA797899999898959491908C8B8584807F5B5A9D030000000000 -000000000303CB7D7D1716151414131211110F0E0C0C0B5E5B9D030000000000 -000000000303CE8786181717171515141312110F0E0D0C605E9D030000000000 -000000000303CF8E8E88878683827C797876736E6C696663609E030000000000 -000000000303D29393B7B1B1AAA9A8A1A0999895928C8B66639F030000000000 -000000000303D3979601A39B0196018F8D0150010101016764A7030000000000 -000000000303D7A29CA301019A96018F8D0150017775716A67A7030000000000 -000000000303D8ACABA302029A96018F8D0150017775716C69B0030000000000 -000000000303DFB3B3AD0202A29B0102020146014F4E4D4C4BA7030000000000 -000000000303E0BBBAB90101ABA3010101013901383C3D424554030000000000 -000000000303E4C2BF01BAB401AC015247012C0125282B303540030000000000 -000000000303E5C7C3C2BFBBB9B2AB554333221E1A1C1D1F2734030000000000 -000000000303E9CDC9C8C3BFBBB9B258483621F3F3F3F3F3F3F3030000000000 -000000000307EAD1D0CCC8C3BFBAB3A4493723F31B2F2D2A2404030000000000 -000000000344DBD5D4D0CDC8C3BFBAAC513A26F32F8968410603030000000000 -0000000003EEE2DDDCD4D0CCC7C0BAB2533E29F32D6841060303030000000000 -0000000003F0E8E3DED6D1CDC8C3BEB3563F2EF32A3104030303000000000000 -0000000003F2EDE8E3DCD4D0C8C3BFB8574332F3190403030300000000000000 -0000000003F4F2F1EFECEBE7E6E1DAD9594A3BF3030303030000000000000000 -0000000003030303030303030303030303030303030303000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 - -1 -XMLSchemaCollection -1 - - -ExtendedAttributeTargetItem -CEFBDD12-0FB3-47E3-B577-548AC5D500DA -Extended Attributes -0 - -0 - -1 - - -DDBCDA07-7A0E-4FAD-BA92-74E85DBCEC88 -Owner -0 - -1277294329 -psitja -Identifies the relational schema name. If not specified, default relational schema is assumed. -1 -15 -4BA9F642-DAB1-11D1-9944-006097355D9B -Schema -0 - - - - - -14331240-EE0D-47C6-8814-C55CAFC416DB -XMLModel -0 - -0 - -Specifies an XML Model corresponding to the schema -1 -15 -029EAF0C-06EE-4282-8CB2-D70732085DEF -0 - - - - - -D79BAE15-0F1E-4080-9BA3-09017FCBF679 -Content -0 - -0 - -Content of the xml schema. -1 -14 -%xmlModelContent% -1 -0 - - - - -ExtendedCollectionTargetItem -1566F50E-D33C-4900-8907-B9BEE6A8AF1C -Extended Collections -0 - -0 - -1 - - -7341A24B-9E3D-4753-991F-5F7F0B484344 -Owner -0 - -1277294329 -psitja -1 -XML Schema Collections -4BA9F642-DAB1-11D1-9944-006097355D9B -Schema - - - - - -45994CC7-DAA2-423B-BA0D-3C815D7570CC -XMLModel -0 - -0 - -1 -XML Schema Collections -029EAF0C-06EE-4282-8CB2-D70732085DEF - - - - -TemplateTargetItem -5650062A-3DF6-4D9B-99EE-A18B7045FAD5 -Templates -0 - -0 - -1 - - -3FDBCE64-D627-49B5-8EC6-9CABA240FFC6 -xmlModelContent -[%XMLModel%?%XMLModel.Preview%] - -0 - -0 - -Retrieve content of an xml model as an xml schema -1 - - - - -FormTargetItem -99622685-E7D2-43DC-B547-330F66715F03 -Forms -0 - -0 - -1 - - -C5999C0E-F67D-4582-A766-0FC0A65B18C2 -Microsoft -<Form > - <ExtendedAttribute Attribute="Owner" Caption="Owner:" AttributeID="{B8644BBE-9AEB-4DDF-AF2C-6F3F2AC81B03}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="XMLModel" Caption="XML model:" AttributeID="{14331240-EE0D-47C6-8814-C55CAFC416DB}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="Content" Caption="Content:" AttributeID="{D79BAE15-0F1E-4080-9BA3-09017FCBF679}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" /> -</Form> - -0 - -1276605960 -obalen -1 -24960 - - - - -26ADB218-195A-4142-ACE1-F9BC52F61EF8 -Custom Symbol -0 - -1152537225 - -1 -0 -(2400, 2400) -128 -14737632 -STRN 0 Arial,8,N -DISPNAME 0 Arial,8,N -LABL 0 Arial,8,N -4 -1 -3D1EF3E5-5B5C-4B74-A97C-135376BD8D46 -1 -1 - - - - -253813BE-4B5A-45FA-A146-EA029829C7CF -Service Broker -0 - -0 - -Queuing and asynchronous messaging are needed for many database applications today. -Service Broker provides a new, queue-based durable messaging framework to address these needs. Using the Transact-SQL API provided by Service Broker, you can easily develop services to handle application requirements for queuing or asynchronous communications. - -1 - - -4E495B03-5581-4AF5-A36A-D64695038A3E -Service -0 - -0 - -A Service Broker service is a name for a specific task or set of tasks. The Service Broker uses the name of the service to route and deliver messages to the correct queue within the database, and to enforce the contract for a conversation. - -1 -Service -Services -0D0000000800000008000000424D7E000000000000003E000000280000001000 -0000100000000100010000000000400000000000000000000000000000000000 -000000000000FFFFFF0000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000424D3605000000000000360400002800000010000000 -100000000100080000000000000100000000000000000000000000009E000000 -5B2526005B4325005D2627005D452600713333007157330077373700775C3700 -79353600795C35007C4C4E007C664C007D5F3A00834C4C00836D4C00853B3D00 -85653B0089515400896F51008A3A3A008A4649008A4948008A693A008A6B4600 -8A7048008D4748008D6F470090595C0090775900915256009173520093494900 -93754900945C5B00947E5B009672720096877200A49B9B00A4A09B00A5434700 -A5794300A8535200A8855200AB6C6D00AB8F6C00B0868800B09C8600B14B4B00 -B1864B00B18D6000B7936400B84B4C00B8894B00BB5A5A00BB925A00BE5E5E00 -BE6D6F00BE955E00BE9A6D00BF6B6C00BF9A6B00C09CA000C0AD9C00C1727200 -C1A07200C26D6B00C2818400C29F6B00C2A38100C36B6A00C39E6A00C55B5D00 -C5965B00C5B7A400C7767800C7A37600C86B6F00C89D6B00C9818100C9A67700 -C9AB8100CA6A6800CA727300CAA36800CAA47200CC737200CC797900CCA77200 -CCA97900CD7D7A00CDAD7A00CE605C00CEA25C00CEA3A500CEBAA300D2878900 -D2B18700D35E5D00D3A25D00D4595900D47C7D00D4A05900D4AE7C00D68D8D00 -D6AD7600D6B78D00D6BE9600D6BFBE00D6CDBE00D7B57E00D7CFC700D97D8100 -D9AE7D00D9C0C200D9CDC000DA848600DAB0B000DAB48400DAC8B000DB838100 -DB848500DBB68400DBB78100DF767700DFB27600E0AEB000E0C3C600E0C9AE00 -E0D1C300E2CFD000E2D9CF00E36A6900E3888C00E3B16900E3B98800E58E9000 -E5BF8E00E5BFC000E5D4BF00E68F8D00E6C38D00E6D6D900E6DCD600EB918F00 -EBC78F00EDDCDD00EDE5DC00EDE7DB00EE989800EECA9800F19B9D00F1CB9B00 -F6D0D200F6E4D000F7F3ED00FDB9B800FDE1B800FFFFFF000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -9D6E6A9D2001269D2E929D9D9D9D9D9D9331320C852A030934249D9D9D9D9D9D -494F6D687C856265160B9D9D9D9D9D9D9A5E4D431C22365C186C9D9D9D9D9D9D -3A5854059D8E124830289D9D9D9D9D9D708C901A72820E5339109D9D9D9D9D9D -9D99795A170746571E761F00259D2D919D7A869588504B663C2C832902083323 -9D9C979D69753E80447F7B836163130A9D9D9D9D60408A5D4C411B21355B156B -9D9D9D9D9D9D385652049D8D11472F279D9D9D9D9D9D6F8B8F1971810D51370F -9D9D9D9D9D9D9D987859140645551D749D9D9D9D9D9D9D778494874E4A643B2B -9D9D9D9D9D9D9D9B969D67733D7E427D9D9D9D9D9D9D9D9D9D9D5F3F899D9D9D -0E0000000200000030000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF0000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -FFFFFF00EBE7E300D8D0C800D6C7B000D6BF9900E7D9C200FBF8F400D5C9B800 -A08660007F633C00644B2A0072604B00958D8400BEBBB800EAE9E800EAE5DF00 -C4B5A500BDAC9900DBCFC300F0EAE200F9F6F300FFFFFF00FFFFFF00FFFFFF00 -FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00 -F6F3ED00DDD1C200C5B09700C4AB8900C6A87E00C3AB8B00BFAF9B00BCA48300 -B9946000A4804D0088693E007F674600806F590092837200AFA18F00BAA88F00 -B5987500B69A7600BDAB9500CEC4B800EAE6E200FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -EDE7DC00CFBCA100B3916600B4906300B7936400A07E550084674200A4804E00 -D3A46100CA9E5E00AD8852008D6F43006B512F00674D2D00755A36008B6B3F00 -A77D4700AF895500A0886800AC9F8E00DCD7D100FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -DAD1C200CAB59600BB986C00BF9C6B00C3A06E00B6936400A6845600BB935D00 -D8AA6A00D5A76700C69B5E00B18A51009A75430096703F009D764300A1794300 -A27942009C79490090775600A0917E00D8D2CA00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -C7BAA700C6AE8E00C6A37600CDAA7700D2B07A00D1AD7700CFA87200D5AA7200 -DCB07300DDAF6F00DEAD6900D7A76100CE9E5A00CA9A5700CC9A5600BC8E4E00 -9C764100876A3E008168470096846F00D4CCC400FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -D9D0C200D1BEA500CAAD8A00CCAC7F00CFAA7600CEA87300CDA67000C6A16D00 -BF9A6900BD986600C09A6300C39A5F00C69A5B00CA9B5A00CE9E5900BD925200 -9B7845008D734C0099856800B3A59400E0DAD300FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -F2EDE500DFD2C200CDB79F00CAAC8700C9A06F00C69E6B00C39F6B00B1926400 -9C805C00977D5B009A815B00A5875A00B58F5900C0965900C99D5A00BB935500 -9B7B4A0097815D00B8A99100D6CDC100EEEAE500FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -E0D0BB00D6C1A600CCB29200CAA98100C89F6F00B8926200A5845700AA926E00 -B7A58F00B7A79300B4A38C00AF987900A8895F00B08A5600C1965900BC925600 -A5814C00A1835700B69C7800CDBBA300EAE3D900FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -C5A57D00C7A67D00CBA97D00CAA67700C8A27000A886590082653F00A7957D00 -E0DAD200E7E2DB00DDD3CB00C1B1A0009D8467009E7C5300B88F5800BE915600 -B1874D00AB824C00AA814F00BB9A7300E3D6C600FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -C7A17200CDA87800D4B07F00D4B07D00D4AF7B00AE8D5F0082664000A7957C00 -E1DAD100ECE5DF00E5DDD500C5B7A80097826800987A5300B7905B00C1955B00 -B88E5400AE844D00A1774400AC8C6400DDD0C000FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -D4AA7A00DAB38100E0BC8800E3BE8900E3C08900BD9C6B0090724900A8937600 -D2C6B800E0D5CA00E1D7CD00C3B6A50094816500967B5300B8945F00C49B6200 -BE955C00AF87530098734400A0846100D7CDBF00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -E5C8A600E7C9A300EACCA200E8C69700E4C18C00C8A67400A7885B00AB936F00 -BAA78F00BFAD9900BEAF9B00B19F86009E856300A5875B00BE9A6500C29C6600 -B5905D00AB885A00A4855D00AB907300C7AEA300D8C1C100CFBBBB00CCBCBC00 -D7CECE00E3E0E000F1F0F000FCFBFB00F0E8E900E5D7D800EEE5E500F8F2F300 -F7EFE500F4E5D300F2DDC300E8CCA600DDBA8900D0AD7C00C2A27100B0936800 -9D815E00947959008C755500987D5900AD8D6000BB996700C5A16C00BD996800 -A5845A00A3866200BAA28300C1A68F00B18279009F66670089575800815A5B00 -9E888800BBB5B500DEDCDC00F9F7F700DBC8C900BF9EA000D8BFC000EFE1E200 -FFFFFF00F5EADB00EED7BA00E5C8A000DDB88700D9B58300D6B48200C1A17200 -AA895E009C7C540091744E009E7F5600B9956500C6A16E00CBA57100C19C6D00 -A9885F00A78A6700BEA58700C4A38D00B4726900A04C4C00843C3C00743A3A00 -825C5C00957F8000B8A3A400D4BEBE00C2989900B2767800C3989900D2BCBC00 -FFFFFF00EFDFC900E1C29500E0BC8C00E0B88700E1BB8900E4C18F00DCB88800 -D2AE7E00C6A47700B99B7100BA9A7000C19F7100C8A37300CEA87600C9A47300 -BA976A00B3936900B4977400B9907500C7746900C65D5D00AA504F0090464600 -7C424200704344008452540098606100A65C5D00B4585A00AF707000A8888900 -FFFFFF00F0E0C700E3C39100E4BE8C00E5BC8C00E9C79B00EFD3AE00EACBA300 -E2BF9100D9B68800CFAE8200CAA98000C5A67E00C9A98200D2B18900D0AF8500 -C4A17700BC9C7200B99C7800BF937900D37B7000DA686800C55E5E00AE535200 -914240007C34350087383A00913D3E009E414200AA4647009C5757008E676700 -FFFFFF00F8E9D500F3D6AC00EFCD9F00EDC69500F1D6B500F8ECDC00ECDAC100 -DCBF9600D7B58800D4B18300CDAD8700C4AA8D00C8B19900D5C0A800D5BDA200 -C6A98800C4A58100CCB19200D3AB9300DA887F00DD706F00D7686700CE605F00 -BF565500B44E4E00B84E4E00B84D4D00A5454500933E3E008A494A0081545600 -FFFFFF00FDF2E300FCE7C900F7DFBC00F3D6B100F7E5D100FDFAF700EDE1D100 -D9BD9800D4B28500D3AF8000CEB08A00C9B39B00CCB9A900D6C1B500D5B9AA00 -C8A18C00C79C8300D1AA9400D5A59400CF847E00CC6D6D00CF686800CF656300 -CD605E00CC5B5B00CF5A5A00CC575600A9494900883E3D008B5152008F646600 -FFFFFF00FDF9F400FDF5EA00FBF2E500FAEFE100FBF4ED00FDFCFB00EDE0D000 -D7BB9600CEAD8100C8A67800CCB08A00D5C0A700D6C0B100D3B4AE00CEA19D00 -C8888200C57C7500C8837A00C27F7800B16C6B00A6606100A85F5F00AC5E5D00 -B85C5B00C15A5900C95B5A00C95A5800A84F4D008A464600A2717000BA9C9C00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00F0E5D800 -DDC7A900D4BA9700CDB38E00D0B59700D9BBA700D7B4A800CFA29F00CB8D8F00 -C9777A00C1686B00B7646300AE656400A8707100A3767900A3747400A46F6F00 -AB616200B2585700BF5A5900C75C5A00AB525000924B4A00AE787700CBA6A600 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00F8F4EF00 -F1E8DC00EDE2D400EAE0D100DFC4B800D09C9500CA8B8700CA878600CB7F8000 -C9747600BA6566009C515100995E5E00C09D9E00D7C3C400D0B6B800C4A2A300 -AB7578009B535400B3575800C45A5A00B3525200A44A4A00AB5A5B00B36A6C00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00EACFCF00CD8A8C00C5727300CB787900CF7A7900 -CE777800B96768008E4848008E5A5A00CDB6B600F4EEEE00EBDEE000D8C2C500 -AB8285008C505200AB565800C35B5C00BA545500B14C4C00A84648009E404400 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00EED2D200D7909300D27A7C00D9828200DC868400 -DD858400C87574009D535300955C5D00C5A4A600E5D4D500E3D2D300D5BFC100 -A87F81008A4F4F00AB595900C6636200BF5C5C00B7555500A3494A008F3C3F00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00F2D9DA00E2A2A500E08F9100E5949400E7949200 -E78F8F00D4807E00AD6060009E606100BA8F9100CFB1B200D1B6B700C9ACAD00 -A678780090515100AE5E5D00C8696800BF616200B55A5B00A25153008E494A00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00F8EDEE00F1D5D600EFC5C600EFBABB00EAAAAA00 -E4939400D5818000C0706F00B0696900AB707100A6737500A2727300A06D6E00 -A5656400AC5F5E00BC676600C76D6B00B4606200A1575900AD6C6D00B8818200 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FBEEEE00F4D3D400ECB6B700 -E0949600D8828300D27F7E00C3747300A55C5D008E494B008542430086424200 -A8595800C36B6A00C76E6F00C8707000AD60630096565900B57E8000D4A8A800 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00F7E4E300EAB6B600E39B9B00 -E08C8E00DE878800DE898900D9858500C8787900BA6C6E00AE656600A9626200 -B86A6A00C5707100CB737400CD757500BB6A6B00AA606300B4737500BD878800 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00F4DBDB00E3A09E00DD868500 -E1868900E48D9000EA979800EC9A9A00E6939300DF8B8B00D2858600C87F7F00 -C67A7B00C6777A00CE7B7D00D27E7E00C7747500BD6B6C00B56C6E00AD6F7000 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00F9E3E200F0B5B300EB9C9B00 -EA949600EC9FA100F2BDBD00F1C2C200E4A2A200DA8B8B00D5868700CF838300 -C7868800C48B8F00CF979A00D89FA000CB8A8C00BF767800C3828400C78F9000 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEEAEA00FCC9C800F9B2B200 -F3A3A500F3B3B400FAE1E100F5E7E700E1B0B000D58A8A00D7868700D4868700 -C9929500C39FA400D2B3B600DEC0C200D0A1A300C3848700D19A9C00DFAFB100 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEF4F400FDE3E300FBD8D800 -F8D0D100F8D8D900FCEFEF00F5E7E700E0AEAF00D2868700CE7F8000CC808100 -CF999B00D4B1B400E2CBCC00EEDFE000E7CFD000E0C1C200E7CCCD00EED6D700 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFDFD00FDFDFD00FDFDFD00 -FDFDFD00FDFDFD00FDFDFD00F5E7E700E0ADAE00CF838400C6787900C57B7B00 -D59FA000E5C3C400F2E2E200FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00 - -1 -Service -1 - - -ExtendedAttributeTargetItem -E2745DD0-A0E6-491D-8201-6939BE113F2B -Extended Attributes -0 - -0 - -1 - - -E4389705-BD07-4188-A6C8-BF51F34B39B5 -Queue -0 - -0 - -Specifies the queue that receives messages for the service. The queue must exist in the same database as the service. -1 -15 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Queue -0 - - - - - -4DC338A0-1020-4CFD-A281-561EDFBFC4D6 -Owner -0 - -1286890165 -psitja -Sets the owner of the service to the specified database user or role. -1 -15 -4BA9F642-DAB1-11D1-9944-006097355D9B -User -0 - - - - - -D9855B90-0FF9-454B-9D64-BE52177AD906 -ReversedContracts -0 - -0 - -A temporary attribute used to store all service contracts during reverse by script. -1 -14 -0 - - - - -ExtendedCollectionTargetItem -B562925C-52FA-4BCC-A449-0EB7EE94CA39 -Extended Collections -0 - -0 - -1 - - -FD7EA529-BF07-41A3-9D55-B1113B8FD9DB -Queue -0 - -1285836027 -psitja -Specifies the queue that receives messages for the service. -1 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Queue -NAME 80 CODE 80 - - -F3ACCD56-D3C7-4706-8753-E68A877AB154 -Contracts -0 - -1285836027 -psitja -1 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Contract -NAME 80 CODE 80 - - -CC33504B-83F6-4CD6-A182-BE5CA07D6525 -Owner -0 - -1286890165 -psitja -Sets the owner of the service to the specified database user or role. -1 -4BA9F642-DAB1-11D1-9944-006097355D9B -User - - - - - - - -MethodTargetItem -B8F61980-A050-4DC3-AD6C-59F4F45EA347 -Methods -0 - -0 - -1 - - -9C98E2EF-99F1-4EBE-AE92-9C3F3B2BABA3 -ParseReversedContracts -0 - -0 - -1 -Sub %Method%(obj) - dim pos, CurCct, AllCct, CctObj - AllCct = obj.GetExtendedAttribute("%CurrentTargetCode%.ReversedContracts") - AllCct = Replace(AllCct, vbTab, " ") - AllCct = Replace(AllCct, vbCrLf, " ") - AllCct = replace(AllCct, " ", " ") - - while (AllCct <> "") - pos = InStr(AllCct, ",") - if (pos = 0) then - CurCct = Trim(AllCct) - AllCct = "" - else - CurCct = Trim(Left(AllCct, pos - 1)) - AllCct = Trim(Mid(AllCct, pos + 1)) - end if - set CctObj = FindExtObject(obj.Model, CurCct, "Contract") - if (CctObj is Nothing) then - Set CctObj = obj.Model.ExtendedObjects.CreateNew() - CctObj.Stereotype = "Contract" - CctObj.SetNameAndCode CurCct, CurCct, True - end if - obj.GetExtendedCollection("Contracts").Add CctObj - wend - obj.SetExtendedAttribute "%CurrentTargetCode%.ReversedContracts", "" - -End Sub - - - - - -TemplateTargetItem -006BD798-9B5C-4F26-9C14-6CCF485D9A7D -Templates -0 - -0 - -1 - - -5C632527-9C67-4D58-B0FE-CF4A8D0D5635 -contractList -.foreach_item(Contracts) - %GeneratedName% -.next(,\n) - -0 - -0 - -1 - - - - -FormTargetItem -A67D9A99-5F99-4B52-AC24-17A890C5F2F4 -Forms -0 - -0 - -1 - - -61E66CFE-BEBC-4122-9AB8-F983182BDE51 -Microsoft -<Form > - <ExtendedAttribute Attribute="Owner" Caption="Authorization" AttributeID="{B8644BBE-9AEB-4DDF-AF2C-6F3F2AC81B03}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="Queue" Caption="Queue" AttributeID="{6A36E37D-544E-4CEA-BE2F-87D33111737E}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> -</Form> - -0 - -1276605960 -obalen -1 -24954 - - -66FA3477-99AD-44AA-996F-2D031E24893F -HiddenForm -<Form > - <ExtendedAttribute Attribute="ReversedContracts" Caption="ReversedContracts:" AttributeID="{D9855B90-0FF9-454B-9D64-BE52177AD906}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" /> -</Form> - -0 - -0 - -1 -Dialog - - - - - - -ED5E4446-14A3-46F5-9D04-44B93C5FFB8C -Queue -0 - -0 - -When a message arrives for a service, Service Broker places the message on the queue associated with the service. -1 -Queue -Queues -0D0000000800000008000000424D7E000000000000003E000000280000001000 -0000100000000100010000000000400000000000000000000000000000000000 -000000000000FFFFFF0000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000424D3605000000000000360400002800000010000000 -1000000001000800000000000001000000000000000000000000000062000000 -0C1115000C1215000C1219000E1115000F5B760010171E00101A210011202B00 -1220270012648400127699001385AC0014799D0015688200162A3500178AB200 -182C370018324200188AB7001983A9001B8AB4001B8EB5001C8AB3001C8DB200 -1C91B9001D3945001D93B50020445700214D63002491B800255062002591B500 -2697BC002699BF002794B700279AC0002DADD20030ACCF00369CBE0042A9C500 -48B0C50049C6E3004BC9EA0050C7E80052B4C80052C7E30053CCEB0057CDEA00 -58CAE30058CCE7005DD7F20060DCF40063D6EB0063D7EE0065D9ED00698FA100 -69D9EE006A91A8006CE0F7006F98AC006FA1B400719CAD0072A1B40074A0B900 -74E7FD0075A8B80076EBFD0078A8BB0078ECFF007AABBB007AB3C7007AECFF00 -7BADC1007BAEBF007CB2C5007DEDFF007EB5CD007EECFF007FB4C90081B9CC00 -925037009E624900A24B2600A94E2900A9664900B4572F00BA623900C0653D00 -D5815C00D6815C00E28B6000E37C4D00E3825600EB9C7B00EFA07F00F5A67F00 -F5A78400FFFFFF00000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -616161616161371E1C1B1910070503006161616161613921282C272622130401 -6161576161613B38152D322A25090C02615D5E5761613C4736230B0A0D201606 -5D605F5C5761371E1C1B1910070503005D565A5553613921282C272622130401 -615D595461613B38152D322A25090C02615D595161613C4736230B0A0D201606 -615D58506161371E1C1B191007050300615D585461613921282C272622130401 -615D585161613B38152D322A25090C02615D585061613C4736230B0A0D201606 -615D59546161414D31142F2E12241D08615D5951616148301734423A2B0F1F0E -615B52506161461A35444B40332918116161616161614F4C4E4A4945433F3E3D -0E0000000200000030000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF0000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00 -FFFFFF00FFFFFF00FFFFFF00CEDAE00086A4B300557C8E00345E7000234F6200 -224D6200204A600020465A001F4153001D3C4A001C36420019303B00172A3500 -13242F00101F2900101A23000F161D000E1319000D1115000C1115000C111500 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00CDDAE00085A4B40054869D003378920028748E00 -2F799000337B9000357A8D0035778B00307185002C6C7F0028667A0024607500 -1E5B71001A556C001650660012485E00103E50000D3241000C212A000B101400 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00CDDAE10085A4B7005591AC003394B5002D9BBB00 -3DA6BF0047ACC1004CAEC2004CADC30044A8C1003DA2BE00379CBA003396B700 -2A92B400248DB0001D86A900167BA000126988000D556E000C3341000B101400 -FFFFFF00FEFEFE00FEFEFE00F0DED600E3BCAC00EDD5CA00FBF7F500FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00CEDBE20087A7B9005F9CB5004CADC9003EAFCC00 -37A5C30039A5C20048B3CC0050BBD4004EBCD6004BBAD70043B4D2003CADCE00 -31A4C6002899BD001E86A80014729600126D8E00106583000D3B4C000B111500 -FFFFFF00FEFEFE00FEFEFE00E2BCAB00C6775400DBA99400F8EFEC00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00CEDCE20089AABA006AA7BD0066C5DB0050C2DB00 -2FA1C300289ABF0041B5D40052C7E30056CEE90058D1ED004FCAE80045C2E200 -39B3D5002BA2C5001E82A20011658700126E9000127395000E4256000B111700 -FFFFFF00F9E9E300F6D5C800E5AC9400D5825E00D9977A00E2B7A400EDD6CC00 -FAF6F300FEFEFE00FEFEFE00CFDDE4008AADBD006EAEC2006FD0E40060D2E900 -47B9D5003AADCD003DB1D1003EB3D3003DB3D3003BB1D10036AACB0030A2C300 -2996B700228CAC001E82A20018789B00177CA000167BA10011485E000C141B00 -FFFFFF00F4D2C400ECA78B00EA9F8000E99A7700D9886600C9765300DAA89200 -F6ECE700FEFEFE00FEFEFE00CFDEE5008AB1C10070B4C70075D8EB0070E2F700 -63D6EB0051C5DE0038ACCC00269ABF001E91B6001889AE001782A600167A9C00 -1573910017708D001E82A2002290B4001E8BB2001A83AB00144D65000E181F00 -F7D8CB00F2BCA500EEA18000EEA07D00F0A17D00E18C6600D0744B00D68F6F00 -E2B7A500EED7CD00F9F4F100CEDDE30088ACBB00659FB1005CA9BC0054ABBE00 -4DA4B9004198AF002E849F0020749200196C890014638000145D780012566F00 -12506600134D6200185971001B637D00195F780015577200113646000D151B00 -EDA88C00EFA58600F2A48100F2A38000F3A47D00EA936B00DF815600D57B5200 -CA785500DAA79100F4E8E200CDDADF0085A5B400578395003B6D7F002D617400 -2A5F7400265A6F00225367001E4B60001C455700193F4E001739470015334000 -122D3A0010273400112733001026310010232C000E1F27000D181E000B111500 -EB9C7B00E4947100DF8E6800E5936D00EC9A7100E2896000D4764B00C7693F00 -BA5E3700D0957C00F1E3DC00CDDAE00085A4B4005483990033718800266A8200 -2B6D84002E6E83002F6C7F002F687C002B637500275D6F002457690020516400 -1B4C5F0017465A0014415400113B4D000F3241000D2935000C1D24000B101400 -EB9C7B00D6845F00C36D4500D27B5200E28C6100D3794F00BE613900B5583000 -AE522C00C98E7600F0E1DB00CDDAE10085A4B700558EA800338CAC002C91AF00 -399AB300429FB40046A0B400469EB4003F99B1003993AE00338DA9002F87A600 -2783A200217E9E001B779700156E8F00125D79000D4C62000C2F3B000B101400 -F1BBA500DC987A00C9765000D27C5400DC865C00CA744C00B35F3A00B76B4A00 -C1806600D8B0A000F4EAE600CEDAE20087A6B8005D99B30045A6C40039AAC800 -39A6C3003DA8C30049B3CA004FB8D0004BB7D10047B4D10040AECD003AA7C900 -2FA0C2002797BA001E87AA0015769A00126C8D000F617E000D3949000B111500 -FBEBE500EDBEAA00E1927100DC896500D8835C00C4745100AE654400C3917A00 -E4CCC200F3E7E300FBF8F700CEDCE20089A9BA0067A4BB005FBFD6004BBDD700 -31A2C3002C9DC00043B5D20052C4DF0054C9E40054CBE7004CC4E20043BCDD00 -37AFD1002AA0C3001E83A40012698B00126E9000126F90000E4053000B111700 -FFFFFF00F4CEBE00EB9E7F00E08F6C00D7825D00C0735300A9664900C69F8E00 -F2EAE700FEFEFE00FEFEFE00CFDDE3008AADBD006DACC1006DCDE2005CCEE600 -3FB2D00033A7C9003EB3D20044B9D80045BCDA0044BBDB003EB5D40037ADCD00 -2EA0C1002593B4001E82A2001672940016789B0015799E0010465C000C131A00 -FFFFFF00F4CEBE00EB9E7F00E08F6C00D7825D00BE735300A4644900C39E8E00 -F2EAE700FEFEFE00FEFEFE00CFDEE5008AB0C0006FB3C50074D6E9006CDEF300 -5BCEE5004BBEDA003AAECE002DA0C400269ABE002193B8001F8DB0001D85A700 -1B7C9B001A7795001E82A2001F89AE001C87AD001981A900134C63000E171E00 -FFFFFF00F4CEBE00EB9E7F00E08F6C00D6825D00BC7051009F5F4400C09B8B00 -F1E9E600FEFEFE00FEFEFE00CEDDE40088AEBD0069A7B90065B9CC005FBED200 -56B7CB0048A8C0003191AE00207FA00019769600136D8D001367850012607B00 -1259720013576E001A6781001E7491001B6F8D0017668600123E51000D161C00 -FFFFFF00F4CEBE00EB9E7F00E08F6C00D6825D00B96D4D0099583D00BC958500 -F0E8E500FEFEFE00FEFEFE00CDDBE00086A7B6005B8A9C00447D8F0037758800 -337186002D6B8000256076001F566D001B4F640018495B0016435400143C4D00 -1236460011314000133444001336450012334000102E3B000E2029000C121700 -FFFFFF00F4CEBE00EB9E7F00E08F6C00D6825D00BA6D4D009B583C00BE958500 -F0E8E500FEFEFE00FEFEFE00CDDADF0085A4B3005480950033697F0025607600 -2761770029617600295E7100285A6D0026546500234F5E00204958001C435200 -183D4E001437470013334200102D3B000F2732000D2029000C181E000B101400 -FFFFFF00F4CEBE00EB9E7F00E08F6C00D6825D00BE715000A5624400C49C8A00 -F2EAE600FEFEFE00FEFEFE00CDDAE10085A4B600558BA4003385A2002A86A300 -358EA6003D92A7004092A6004090A5003A8BA10034859D002F7F98002B799400 -247490001E6F8C001969850014607D0011526A000D4356000C2A35000B101400 -FFFFFF00F4CEBE00EB9E7F00E08F6C00D6825D00C0735300AA674900C79F8E00 -F2EAE700FEFEFE00FEFEFE00CEDAE20086A6B8005A96B1003E9FBF0035A5C400 -3BA7C30042ABC3004BB2C8004EB5CC0049B3CC0044AECB003DA8C70037A2C300 -2D9CBE002694B8001E88AB0015799E00126C8C000E5D79000D3747000B111400 -FFFFFF00F4CEBE00EB9E7F00E08F6C00D6825D00BE735300A5654900C49E8E00 -F2EAE700FEFEFE00FEFEFE00CEDCE20088A9BA0064A1B90058B8D10047B8D300 -33A3C30031A0C00045B4D00051C1DB0052C5DF0051C5E10049BEDC0040B7D700 -35ABCD00299DC1001E84A500126C8F00126E8F00116B8B000E3E51000B111600 -FFFFFF00F4CEBE00EB9E7F00E08F6C00D6825D00BC715100A0614600C09C8C00 -F1E9E600FEFEFE00FEFEFE00CFDDE3008AACBC006CABC0006CCBE00058CAE200 -38AACA002DA0C4003FB4D3004BC0DE004DC5E2004EC6E40046BFDE003EB7D800 -33AACB00289ABC001E82A200146B8D001474960014779B000F445A000B121900 -FFFFFF00F4CEBE00EB9E7F00E08F6C00D6825D00B96D4E009A593E00BD968600 -F0E8E500FEFEFE00FEFEFE00CFDEE4008AAFBF006FB1C40072D3E70068DAF000 -54C7E00044B8D5003BAFCF0033A7C9002EA3C5002A9EC1002798B9002490B100 -2086A5001D7F9D001E82A2001D83A7001B83A800187FA600124A61000D161D00 -FFFFFF00F4CEBE00EB9E7F00E08F6C00D6825D00B96C4C0099563B00BC948400 -F0E8E500FEFEFE00FEFEFE00CFDFE6008AB2C20071B6C80077DAED0073E5F900 -67DAEF0054C8E00036A9CA002295BB001D90B600198AB0001985A800187D9F00 -15739200156F8D001E85A7002597BC002090B7001B86AE00154F67000F192000 -FFFFFF00F4CEBE00EB9E7F00E08F6C00D6825D00BE705000A3604200C39A8900 -F1E9E600FEFEFE00FEFEFE00D0E0E6008DB5C30074B9C90079DAED0073E4F800 -64D6ED004EC1DD0030A2C5002395BC0031A5C8003AAECF0039ACCC0034A3C400 -238DB100177FA5002192B70028A1C6002497BD001F89B10018536A00101C2300 -FFFFFF00F4CEBE00EB9E7F00E08F6C00D7825D00C1735300AB674900C89F8E00 -F2EAE700FEFEFE00FEFEFE00D1E2E7008FB8C50076BACA0078D8EC006EDEF400 -5BCDE70047B9D7002F9FC4002B9CC20046BADA0057CDEA0055CDEB004DC4E400 -32A7CE001D91BC00249DC50029A6CC00269BC000228DB2001A566D00111F2700 -FFFFFF00F4CEBE00EB9E7F00E08F6C00D7825D00BF735300A6654900C49E8E00 -F2EAE700FEFEFE00FEFEFE00D2E2E80091BAC90072B6CA006ACDE2005ACAE300 -41B3D10038AAC9003FB0CF004ABCD9005BD0E90066DCF30062D8F2005BD1EC00 -47BCDE0035AAD00029A0C6001F97BD002193B900228DB1001B586F0013242E00 -FFFFFF00F3CCBC00EA9C7C00DE8C6800D37E5900BB705000A0624700C19D8D00 -F1E9E600FEFEFE00FEFEFE00D3E3EA0093BCCC006EB3C9005CC0D70046B7D300 -2C9EC0002EA0C10050C1DB0066DAEE006EE4F60074E9FB006EE3F80068DCF400 -5AD0ED004BC1E20030A5CA00198DB3001F8EB400228DB0001B5B720015293400 -FFFFFF00F1C5B100E68D6600D1775100BB653F00AA5D3E0099583D00BE978700 -F0E8E500FEFEFE00FEFEFE00D3E4EB0093BECE0066ADC50045ABC70039ADCB00 -3EB1CF004BBED90061D3EA006FE2F60074E8FA0077EAFC0072E5FA006CE0F700 -60D7F20054CDEA0041BADA0030A8CA00279BC0001E8DB2001A5D7600162D3B00 -FFFFFF00F0C0AA00E3835700C6694100A7532D009E51330095543A00BD968600 -F0E8E500FEFEFE00FEFEFE00D3E5EC0093C1D1005FA9C1003399B80030A4C300 -51C3DD0067D9EF0071E3F70078E9FC007AEAFC007AE8FA0076E6FA0071E3F800 -66DDF5005DD7EF0053CCE70046C0DD0031A7CA001D8DB4001B607C0019344400 -FFFFFF00F7DFD400F0C0AA00E2B39F00D2A89500CEA79800C9A99C00DDCAC200 -F7F3F100FEFEFE00FEFEFE00D4E6EC0096C3D3006FB0C60058A7C20056ACC700 -67BBD30072C6DB0076CADE0079CCDF007ACCDE007ACADC0077C8DA0075C6D900 -6FC2D7006ABED40064B7D0005CAFCA0051A3BF004796B300467E960044677800 -FFFFFF00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00 -FDFDFD00FEFEFE00FEFEFE00D4E6EC0098C5D4007EB6CA007DB4CB007CB3CA00 -7DB3C9007CB3C7007AB2C5007AB0C2007AAEC0007AACBE0078ABBB0078A9BA00 -77A8BA0076A6B90074A2B900729FB80071A0B500709EB300709CAF006F9AAB00 - -1 -Queue -1 - - -ExtendedAttributeTargetItem -A5EAC00E-7818-4BB5-858F-6C121B4E4E8B -Extended Attributes -0 - -0 - -1 - - -9C18C036-12F0-4482-AF5B-83251D95FBC3 -Status -0 - -0 - -Specifies whether the queue is available (True) or unavailable (False). -When the queue is unavailable, no messages can be added to the queue or removed from the queue. You can create the queue in an unavailable state in order to keep messages from arriving on the queue until the queue is made available with an ALTER QUEUE statement. -If this clause is omitted, the default is ON, and the queue is available. - - -1 -true -0 - - -9D6BFE47-3584-4F0E-BC5A-97CCAE59A5AB -Retention -0 - -0 - -Specifies the retention setting for the queue. -If RETENTION = True, all messages sent or received on conversations using this queue are retained in the queue until the conversations have ended. This allows you to retain messages for auditing purposes, or to perform compensating transactions if an error occurs. -If this clause is not specified, the retention setting defaults to False. -1 -false -0 - - -3204C3C0-92F6-483C-BC57-A5FAC9017A91 -Activation -0 - -0 - -Specifies information about the stored procedure to activate to process messages in this queue. -1 -0 - - -BA78E3C8-DB1E-4E54-B79D-10650BBD19D6 -FileGroup -0 - -0 - -Specifies the SQL Server filegroup on which to create this queue -1 -15 -4BA9F644-DAB1-11D1-9944-006097355D9B -0 - - - - - -086BA728-AE47-421B-9321-DBCECA3D1D3C -Owner -0 - -1277294329 -psitja -Specifies the owner of the queue -1 -15 -4BA9F642-DAB1-11D1-9944-006097355D9B -Schema -0 - - - - - - - -CriterionTargetItem -FD1AB46E-67E7-4A7A-A158-988BB69F9AE9 -Criteria -0 - -0 - -1 - - -1E0739A1-5505-425A-BC9F-03F6EF1B54CB -ActivationInformation -%Activation% -0 - -0 - -1 - - -ExtendedAttributeTargetItem -D6E17525-37B4-453F-A075-81E39FA2AA05 -Extended Attributes -0 - -0 - -1 - - -D4CD95AF-B316-41DC-B6D8-AC1A0E6815A9 -ActivationStatus -0 - -0 - -Specifies whether or not Service Broker activates the stored procedure. -When STATUS = True, the queue starts the stored procedure specified with PROCEDURE_NAME when the number of procedures currently running is less than MAX_QUEUE_READERS and when messages arrive on the queue faster than the stored procedures receive messages. -When STATUS = False, the queue does not activate the stored procedure. -If this clause is not specified, the default is ON. -1 -true -0 - - -ADA2B6EB-F9DB-4FCB-A8F9-1EBC2FB40F21 -ActivationProcedureName -0 - -0 - -Specifies the name of the stored procedure to activate to process messages in this queue. -1 -12 -0 - - -8345B653-34CD-4EC6-B3F0-C55F5943AA9C -ActivationMaxQueueReaders -0 - -0 - -Specifies the maximum number of instances of the activation stored procedure that the queue starts at the same time. The value of max_readers must be a number between 0 and 32767. -1 -10 -0 - - -CC496B1B-0CC3-4754-A29B-C49E74A85532 -ActivationExecuteAs -0 - -0 - -Specifies the SQL Server database user account under which the activation stored procedure runs. -SQL Server must be able to check the permissions for this user at the time that the queue activates the stored procedure. - -SELF: Specifies that the stored procedure executes as the current user. (The database principal executing this CREATE QUEUE statement.) - -OWNER: Specifies that the stored procedure executes as the owner of the queue. - -'user_name': Is the name of the user that the stored procedure executes as. The user_name parameter must be a valid SQL Server user specified as a SQL Server identifier. The current user must have IMPERSONATE permission for the user_name specified. -1 -12 -self;owner -0 - - - - - - - - -ExtendedCollectionTargetItem -198BF966-4172-4ABC-B243-ECFF822E538C -Extended Collections -0 - -0 - -1 - - -5438AE10-9E52-47EB-80E3-57ADF8415781 -Owner -0 - -1277294329 -psitja -1 -4BA9F642-DAB1-11D1-9944-006097355D9B -Schema - - - - - -4E5FF597-18A9-44CC-9AD8-D3A816940073 -FileGroup -0 - -0 - -1 -4BA9F644-DAB1-11D1-9944-006097355D9B - - - - -FormTargetItem -3B3B1E12-4B29-4CF2-B8E3-3A7D5F5EB0BC -Forms -0 - -0 - -1 - - -7CAC9966-392C-45D6-914D-E1D543C8F2DE -Microsoft -<Form > - <ExtendedAttribute Name="Owner" Caption="Owner" Attribute="Owner" AttributeID="{086BA728-AE47-421B-9321-DBCECA3D1D3C}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Name="Status" Caption="Status" Attribute="Status" AttributeID="{9C18C036-12F0-4482-AF5B-83251D95FBC3}" GrayHiddenAttributes="Yes" /> - <ExtendedAttribute Name="Retention" Caption="Retention" Attribute="Retention" AttributeID="{9D6BFE47-3584-4F0E-BC5A-97CCAE59A5AB}" GrayHiddenAttributes="Yes" /> - <GroupBox Name="GroupBox1" Caption="Label" ControlAsLabel="Yes" > - <ExtendedAttribute Name="Activation" Caption="Activation" Attribute="Activation" AttributeID="{3204C3C0-92F6-483C-BC57-A5FAC9017A91}" GrayHiddenAttributes="Yes" /> - <ExtendedAttribute Name="ActivationStatus" Caption="Status" Attribute="ActivationStatus" AttributeID="{D4CD95AF-B316-41DC-B6D8-AC1A0E6815A9}" GrayHiddenAttributes="Yes" /> - <ExtendedAttribute Name="ActivationProcedureName" Caption="Procedure" Attribute="ActivationProcedureName" AttributeID="{ADA2B6EB-F9DB-4FCB-A8F9-1EBC2FB40F21}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Name="ActivationMaxQueueReaders" Caption="MaxQueueReaders" Attribute="ActivationMaxQueueReaders" AttributeID="{8345B653-34CD-4EC6-B3F0-C55F5943AA9C}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Name="ActivationExecuteAs" Caption="Execute as" Attribute="ActivationExecuteAs" AttributeID="{CC496B1B-0CC3-4754-A29B-C49E74A85532}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - </GroupBox> - <ExtendedAttribute Name="FileGroup" Caption="File group" Attribute="FileGroup" AttributeID="{BA78E3C8-DB1E-4E54-B79D-10650BBD19D6}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> -</Form> - -0 - -1276605960 -obalen -1 -24954 - - - - -CustomCheckTargetItem -75400294-CC68-4F6A-8D98-B96AAD0A9A7D -Custom Checks -0 - -0 - -1 - - -2CCCED91-DCFB-4722-AD54-29AE388DB448 -Valid maximum number of readers. -0 - -0 - -1 -This check ensures that the maximum number of readers falls within prescribed limits. -The following queues do not have valid parameter(s): -Function %Check%(obj) - if CBool(obj.GetExtendedAttribute("Activation")) then - Dim v : v = obj.GetExtendedAttribute("ActivationMaxQueueReaders") - if (IsNumeric(v)) then - %Check% = ((v >= 0) and (v <= 32767)) - else - %Check% = False - end if - else - %Check% = True - end if -End Function -Function %Fix%(obj, outmsg) - ' Implement your automatic correction on <obj> here - ' filling <outmsg> as you wish - ' and return True if successful. - - outmsg = "Automatic correction not implemented" - - %Fix% = False -End Function - - - - - - -F1EBB0B2-D01C-4825-8679-84440CCB02B6 -EventNotification -0 - -0 - -Sends information about a database or server event to a service broker service. Event notifications are created only by using Transact-SQL statements. -1 -Event Notification -Event Notifications -1 -EventNotification -1 - - -ExtendedAttributeTargetItem -CE44CBA2-F9D5-487D-A18F-5F3B1042BE35 -Extended Attributes -0 - -0 - -1 - - -5C551772-4DCA-4EE9-B062-01A677618853 -AppliesOn -0 - -0 - -SERVER -Applies the scope of the event notification to the current instance of SQL Server. If specified, the notification fires whenever the specified event in the FOR clause occurs anywhere in the instance of SQL Server. - -DATABASE -Applies the scope of the event notification to the current database. If specified, the notification fires whenever the specified event in the FOR clause occurs in the current database. - -QUEUE -Applies the scope of the notification to a specific queue in the current database. QUEUE can be specified only if FOR QUEUE_ACTIVATION or FOR BROKER_QUEUE_DISABLED is also specified. - -1 -12 -database;server;queue -1 -0 - - -FC5427D8-655D-4ACD-93B8-F5EC1367E2E7 -Service -0 - -0 - -Specifies the target service that receives the event instance data. SQL Server opens one or more conversations to the target service for the event notification. This service must honor the same SQL Server Events message type and contract that is used to send the message. For more information, see Creating Service Broker Services. -1 -15 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Service -0 - - - - - -A10E95BD-C8C0-41FC-B0EF-29E4F260ED81 -WithFanIn -0 - -0 - -Instructs SQL Server to send only one message per event to any specified service for all event notifications that: -- Are created on the same event. -- Are created by the same principal (as identified by the same SID). -- Specify the same service and broker_instance_specifier. -- Specify WITH FAN_IN. -1 -0 - - -406FA124-2D7A-4C47-B3D3-571FDF66DE32 -Events -0 - -0 - -Specifies the name of the event type that causes the event notification to execute. Can be a Transact-SQL DDL, SQL Trace, or Service Broker event type. -1 -14 -0 - - -CAA1ABCC-96D5-4500-9A3F-32487C7C5FA2 -Instance -0 - -0 - -Specifies a service broker instance against which broker_service is resolved. Use 'current database' to specify the service broker instance in the current database. -1 -12 -current database -0 - - - - -ExtendedCollectionTargetItem -4CD40068-A510-4328-ADE0-4C69527753D7 -Extended Collections -0 - -0 - -1 - - -C490755D-94C6-4E03-8E4C-56D6AF79C296 -Queue -0 - -0 - -1 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Queue - - -5F8865C3-C569-46F7-B369-C8F5EA63A254 -Service -0 - -0 - -1 -Service -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Service - - - - -FormTargetItem -4E8BB37B-642F-41F5-A4EE-A6C8EA9D8344 -Forms -0 - -0 - -1 - - -5DCEBDC2-129E-490B-A241-89D0ADBF86C2 -Microsoft -<Form > - <ExtendedAttribute Name="AppliesOn" Caption="Applies on" Attribute="AppliesOn" AttributeID="{5C551772-4DCA-4EE9-B062-01A677618853}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" /> - <ExtendedAttribute Name="Queue" Caption="Queue" Attribute="Queue" AttributeID="{6A36E37D-544E-4CEA-BE2F-87D33111737E}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" /> - <ExtendedAttribute Name="WithFanIn" Caption="With fan in" Attribute="WithFanIn" AttributeID="{A10E95BD-C8C0-41FC-B0EF-29E4F260ED81}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" /> - <ExtendedAttribute Name="Events" Caption="Events" Attribute="Events" AttributeID="{406FA124-2D7A-4C47-B3D3-571FDF66DE32}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" /> - <ExtendedAttribute Name="Service" Caption="Service" Attribute="Service" AttributeID="{FC5427D8-655D-4ACD-93B8-F5EC1367E2E7}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" /> - <ExtendedAttribute Name="Instance" Caption="Instance" Attribute="Instance" AttributeID="{CAA1ABCC-96D5-4500-9A3F-32487C7C5FA2}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> -</Form> - -0 - -1276605960 -obalen -1 -24954 - - - - -CriterionTargetItem -ECEEB418-CD7B-4A1C-9FAA-449FB82689FC -Criteria -0 - -0 - -1 - - -AC3FB50B-F807-47DA-B711-2BEE708B289F -AppliesOnQueue -%.L:AppliesOn% == queue -0 - -0 - -1 - - -ExtendedAttributeTargetItem -656EE036-1EAC-4966-87E2-09D4257D49E2 -Extended Attributes -0 - -0 - -1 - - -6A36E37D-544E-4CEA-BE2F-87D33111737E -Queue -0 - -0 - -Specifies the queue to which the event notification applies. Can be specified only if QUEUE is specified in "Applies on" property. -1 -15 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -Queue -0 - - - - - - - - - - - - - -B8B71484-0BA2-4E55-905A-12CA942E1CF8 -Contract -0 - -0 - -A contract defines the message types used in a Service Broker conversation and which side of the conversation can send messages of that type. Each conversation conforms to a contract. The initiating service specifies the contract for the conversation when the conversation begins. The target service specifies the contracts for which it accepts conversations. -1 -Contract -Contracts -0D0000000800000008000000424D7E000000000000003E000000280000001000 -0000100000000100010000000000400000000000000000000000000000000000 -000000000000FFFFFF0000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000424D3605000000000000360400002800000010000000 -1000000001000800000000000001000000000000000000000000000049000000 -010101004F3826004F3827004F39250050382600503925005138280051392700 -513A260052362800523825005238260088595200A6918100AA928200AB918100 -AB968500AC928100AC928200AD908300AD927F00AE928100B49A8900C1A49900 -C69C8A00C79D8C00CDB1A300CFA69700D0B5A300D2A89600D3B3A300D4B4A400 -D5507F00D5B8AC00D9BFB100DDC6B900E0CABE00E3CEC100E3D0C800E8D6CC00 -EAD5CA00EBDBD200ECDDD600ECDFDA00ECE2DA00F0E7E300F2EAE600F3E3DA00 -F3EDEA00F4EAE600F9EAE000F9F0E700F9F9F900FBE3DB00FBF4F100FCF1E600 -FDEEEA00FDFEFF00FDFFFF00FEF3EE00FEF8F400FEFBF600FEFCFB00FEFDF800 -FEFDFF00FEFEFE00FEFFFD00FFF3F100FFFDFC00FFFEFE00FFFEFF00FFFFFE00 -FFFFFF0000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -48484848484848484848484848484848484848480B09030704040B0B02044848 -4848480C15140C130E0C11150C014848484848113A4834332C2724220E024848 -4848480D473945362E2A26230F0748484848480C48204520191919250C054848 -48484810423C203732352B280E04484848484816462045202E2A2D290E044848 -4848480C40463F3C4338302F0C054848484848164021211F1B191931120A4848 -4848481640404041443D3E3B0E0848484848480C4021211E1D18193B0C064848 -48484816404040473A483E3B0E0048484848480C4040404848483E3B0E004848 -484848480C1C0C1C1A0C170E0C48484848484848484848484848484848484848 -0E0000000200000030000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF0000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00 -FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00 -FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00 -FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00DDD8D500 -B4A9A200AA9D9500AA9D9500A99D9500A89D9400A99E9500A99E9500A99E9500 -A99D9500A99D9500A99D9500A99D9500A99D9500AA9D9500AA9D9500AA9D9500 -A99D9500A89D9500A99D9500AEA49B00D7D2CE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00BDB3AC00 -6B554700563D2C00563C2D00553C2C00533D2B00543E2C00543E2C00553E2C00 -543D2C00543D2C00543D2C00543D2C00553D2C00563D2C00563D2C00563D2C00 -543D2C00533D2C00543D2C005F4A3900B1A69F00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00E7DEDD00CDBAB700AA928900 -846A5A007A5F4E007A5F4E00745647006A4A3C006D4D3F0074594A00795F4E00 -785F4F00745A49006C4D3F006B4A3C00735747007A5F4E007A5F4E00785C4B00 -6F4F400065443700593D2E0059443300AEA49C00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00D0BDBA0098716C0098726700 -A3867600A78B7A00A78B78009A796B00896056008D645C009D7E7100A5897B00 -A48B7B009B7F70008C655B008960560099796C00A68B7A00A78B7A00A2847400 -916A5E007D534A006444360059443300AEA49C00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00D3C3BE009F7D7300AB8E8300 -C7B4AA00CEBEB400CEBEB300C6B2AA00BAA19C00BCA39D00C6B1A700C9B5AA00 -C6B3A700BFAA9E00B5978D00B2918800BA9F9200C0A79800BFA59600B99D8E00 -A58477008D6A5F006B4F400059443300AEA49C00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00DACEC800AF968700C4B3A900 -EAE6E200F5F3F100F5F3F100F3EFEE00EEEAE900EEE7E400EFE6DE00ECE2D900 -E8DBD300E2D6CC00DFCDC400DDC8BD00DAC5B900D8C1B500D5BDAF00CDB3A500 -B89D8E009C837300725A4A0059443300AEA49C00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00DAD0CA00B09A8C00C8BAB100 -F2F0ED00FDFEFD00FDFDFE00FCFCFC00FAFAFA00F9F6F500F9F2ED00F5EDE600 -F0E7E000ECE1DA00E9DAD200E7D5CB00E2CFC500DEC9BE00DBC4B800D3BAAC00 -BCA39400A0887700745D4C0059443400AEA49C00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00D9D0CA00AD9A8C00C7BAB100 -F2F0ED00FDFEFD00FDFDFE00FCFCFC00FDFCFC00FCF9F800FAF4F100F7F0EB00 -F2EAE600EFE5E000ECDED700E8D9D000E4D2CA00E0CCC200DDC7BC00D5BDB000 -BCA39500A0887700755D4C005A443400AEA49C00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00D4C7C200A3887D00C0ADA600 -F1EDEB00F8E8ED00F0C9D800F3D3DE00FBF0F400F9E9ED00F0C7D300EABEC700 -E4C8C500E1CBC300DFC7BD00DCC4B800DABFB400D8BEB200DBC3B700D6BEB100 -B6998C0095756800705444005A453300AFA49B00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00CEBCB90096706800B79D9800 -EFEAE800F1CAD800E083A400E69AB400F7E0E800F3D2DD00E18AA900D7799300 -D0989600CCA79900CCA69700CBA69600CBA49500CDAA9B00D8BDAF00D6BEB100 -AD8A8000865D530069493A005A453200AFA49B00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00D1C1BC009C797000BBA39D00 -F0EBE900F3D3DD00E599B200E699B300EDB8CA00ECB1C400E491AB00DE8FA100 -D9A7A400D6B4A500D6B3A500D5B2A400D3B1A400D4B6A900DDC4B700D9C2B600 -B19186008A655A006B4D3D005A443200AFA49B00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00D8CDC600AB948700C5B5AC00 -F2EFEC00FAEFF100F6DCE100EDB5C500E184A400E38CA700EEBBC400F2D3D000 -EFD7CF00EED9CD00EFD6CC00EDD4CA00E7D2C900E4D1C900E5D2C800DEC8BD00 -BDA39600997E6F00725847005A443200AFA49B00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00DCD2CB00B4A09000CBBDB200 -F3F0ED00F8E9ED00F3CFD700EBB0C100E38FAB00E593AD00ECB3BF00F2CBCD00 -F4DEDA00F5E7DF00F6E2DB00F3E0D900EFE0DA00EBDFD800E9DAD100E0CCC100 -C2AB9D00A0887800755D4C005A443300AFA49C00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00DED3CC00B8A29200CEBFB400 -F4F0EE00F2CCDA00E28BA800E594AF00F0C2D200EDB9CA00E189A600E391A900 -EDCACF00F1E6E100EFE0D900EEDED700EEE2DC00EDE2DC00EBDDD500E0CEC400 -C2AC9F00A0887800755D4C005A443300AFA49C00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00DACDC700AF938600C9B5AC00 -F3EDED00F2CEDC00E491AE00E8A5BC00F7E3E900F4D7DF00E597B100E497B000 -EFCED400F3E8E500F1E2DD00EFE1DB00EFE5E000EFE5E100EDE0D900E1D0C600 -BEA49900987A6C00715746005A443200AFA49B00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00D1BFBB009C776E00BBA19C00 -EFE9E900F9ECF200F4D6E200F6DEE500FBF3F200FAEDEE00F4D6DD00F4D5DB00 -F8E6E600FAEEEA00F8EAE700F6E9E400F3E9E600F0E8E400F0E4DC00E3D0C600 -B5958C00896258006A4B3C005A443200AFA49B00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00D1BFBB009B756D00BBA09B00 -EFE8E900F9F7F700F6EEEE00F3EDE800F3ECE600F3EAE400F3E8E100F3E5E000 -F3E3DD00F2DFD900F1DCD600EEDAD300EAD9D300E9DBD500EEE0D900E5D2CA00 -B5958D00886056006A4B3B005A443200AFA49B00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00D9CCC600AE918400C8B3AB00 -F2ECEC00F2EBE900E5D2CD00DFCBC200DFCBC100DFC9BF00DFC7BB00DEC3B700 -DCBEB200DAB9AD00D8B5A900D5B3A600D3B3A600D7BBB000E7D4CC00E8D9D300 -C0A79E0098796B00725645005B443200AFA49B00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00DFD4CD00BAA39300D0BFB500 -F4EFEF00F0E8E600E1CDC600DBC4BB00DBC4BB00DBC3B900DBC2B500DABEB000 -D8B9AB00D5B4A600D3B0A200D0AE9F00D0AEA000D6B7AC00E8D4CD00EBDED900 -C7B3A800A1887800765D4B005B443200AFA49B00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00DFD4CD00BAA39300D0BFB500 -F4EFEF00F8F4F400F2E9E700EFE5E300EFE5E300EFE5E200EFE5E000EFE3DD00 -EEE1DB00EDDED800ECDDD500EBDBD300EBDCD400EDDFD900F4E8E300EFE2DC00 -C9B5AA00A0887800765D4B005B453200AFA49B00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00DCCFC900B3998B00CBB8AF00 -F3EDED00FAF8F900F7F2F200F6F0F000F6F0F000F6F0F000F6F1EE00F6F0EC00 -F6EEEB00F6EDE900F5ECE500F3EAE400F3EBE600F5EDE900F9EFEB00F1E3DE00 -C6B1A6009B7F7100735948005B453300AFA49C00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00D3C2BE00A07C7300BEA59F00 -F0E9EA00F3ECEB00E7D6D100E2CFC800E2CFC800E2CEC700E1CDC300E1CABF00 -E0C7BB00DFC3B700DBBFB200D8BCB000D8BDB100DEC6BC00F0DFD800EFE0DB00 -BCA098008C675C006C4D3F005B443400AFA49C00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00CFBCB800976F6800B89C9800 -EEE8E800EFE7E400DFC9C100D9BFB500D9BFB500D9BEB300D8BCAF00D7B9AA00 -D6B4A500D3B0A000CFAA9A00CCA79800CCA89800D4B4A600EBD7CF00EEDED900 -B7989100845A520063433800513D3000AAA09A00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00D8C9C400AA8C8000C5AFA800 -F1EBEC00F7F2F200EFE5E300EDE1DD00EDE1DD00EDE0DD00EDE0DA00ECDFD800 -EBDDD600EADBD400E8D8D100E7D7D000E7D6CF00EADBD300F5E7E200F0E2DC00 -C1A99F008F716500534037002D241E0098949100FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00DDD1CA00B69D8E00CDBBB100 -F3EEEE00FDFCFE00FDFCFE00FDFCFE00FDFCFE00FDFCFE00FEFEFD00FEFEFD00 -FCFEFE00FCFEFE00FDFEFE00FEFDFD00FDFCFB00FDFAF800FDF5F200F2E5DF00 -CAB7AB0099837500473D3600101010008B8B8B00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00D4C4BF00A2807600C0A7A100 -F0EAEA00FDFCFE00FDFCFE00FDFCFE00FDFCFE00FDFCFE00FEFEFD00FEFEFD00 -FDFEFE00FDFEFE00FDFEFE00FEFDFD00FDFCFB00FDFAF800FDF5F200F2E5DF00 -CAB7AB0099837500473D3600101010008B8B8B00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00D0BDBA0098716C00B6999600 -E8DFDF00F7F3F400F9F6F700F8F5F600F6F2F300F6F3F400F9F7F600FAF9F800 -FAF9F800F9F7F600F7F5F400F7F3F200F7F4F300F8F4F100F8EFEB00EDDFD800 -C6B2A600988173004F443E001F1F1F0092929200FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00E7DEDD00CDBAB700C3ACA900 -BDA4A100CAB4AE00DDCCC400D6C4BD00C4ABA700C7B0AC00DACAC100E3D4CB00 -E3D3CB00D9C8C100C7B1AC00C2AAA600D1BEB800D9C8C100D3C2B800C7B3A800 -AC8E840095746A008F807B0092929200CACACA00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00D1C0BD00 -99716B00A27D7100C4A79800B9998B00986E65009F776D00C0A29300CFB5A400 -CEB3A400BEA093009E766D00956A6400AF8E8400BEA29700B39B8C00A78D7F00 -97726800956C6600CDBAB700FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00E7DFDD00 -CBB7B400D0BDB700E1D2CB00DBCBC400CBB6B100CEBAB500DFD0C800E6D9D100 -E6D8D100DECFC800CEBAB500C9B4B000D6C6C100DED0CA00D8CCC500D2C5BE00 -CAB8B300C9B5B200E5DCDA00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FDFDFD00 -FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00 -FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00FDFDFD00 -FDFDFD00FDFDFD00FDFDFD00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 - -1 -Contract -1 - - -ExtendedAttributeTargetItem -0726A61C-D4BE-4B40-BCE1-5A70A5532293 -Extended Attributes -0 - -0 - -1 - - -0C5FB62A-C1D0-4E92-87B2-6F2A596C9696 -Owner -0 - -1286890165 -psitja -Sets the owner of the contract to the specified database user or role. When the current user is dbo or sa, owner_name may be the name of any valid user or role. Otherwise, owner_name must be the name of the current user, the name of a user that the current user has impersonate permissions for, or the name of a role to which the current user belongs. When this clause is omitted, the contract belongs to the current user. -1 -15 -4BA9F642-DAB1-11D1-9944-006097355D9B -User -0 - - - - - -F7F8A228-1F18-4101-9FCC-847401C86B83 -ReversedMessageContracts -0 - -0 - -A temporary attribute used to store all message contracts during reverse by script. -1 -14 -0 - - - - -ExtendedCollectionTargetItem -B50935AB-1873-47C4-809A-776F7BED5E3A -Extended Collections -0 - -0 - -1 - - -38C2FE5B-1455-4C91-8F0A-119BBCB14842 -Owner -0 - -1286890165 -psitja -Sets the owner of the contract to the specified database user or role. When the current user is dbo or sa, owner_name may be the name of any valid user or role. Otherwise, owner_name must be the name of the current user, the name of a user that the current user has impersonate permissions for, or the name of a role to which the current user belongs. When this clause is omitted, the contract belongs to the current user. -1 -Contract -4BA9F642-DAB1-11D1-9944-006097355D9B -User - - - - - -69642C11-CCED-4EDA-997C-5C40FEBBFCA1 -MessageTypes -0 - -0 - -1 -Contract -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -MessageContract - - - - -MethodTargetItem -E2E747E9-D590-4FBF-8E6C-C4BBF2D8E3F2 -Methods -0 - -0 - -1 - - -49045DBF-14BE-4850-A7E0-63FC45243726 -ParseReversedMessageContracts -0 - -0 - -1 -Sub %Method%(obj) - dim pos, CurMsg, AllMsg, MsgObj, MsgCct, Sender - AllMsg = obj.GetExtendedAttribute("%CurrentTargetCode%.ReversedMessageContracts") - AllMsg = Replace(AllMsg, vbTab, " ") - AllMsg = Replace(AllMsg, vbCrLf, " ") - AllMsg = replace(AllMsg, " ", " ") - while (AllMsg <> "") - pos = InStr(AllMsg, ",") - if (pos = 0) then - CurMsg = Trim(AllMsg) - AllMsg = "" - else - CurMsg = Trim(Left(AllMsg, pos - 1)) - AllMsg = Trim(Mid(AllMsg, pos + 1)) - end if - - pos = InStr(LCase(CurMsg), "sent by") - if (pos > 0) then ' sender is mandatory - set MsgObj = FindExtObject(obj.Model, Trim(Left(CurMsg, pos - 1)), "MessageType") - if (MsgObj is Nothing) then - Set MsgObj = obj.Model.ExtendedObjects.CreateNew() - MsgObj.Stereotype = "MessageType" - MsgObj.SetNameAndCode Trim(Left(CurMsg, pos - 1)), Trim(Left(CurMsg, pos - 1)), True - end if - Set MsgCct = obj.Model.ExtendedObjects.CreateNew() - MsgCct.Stereotype = "MessageContract" - ' MsgCct.SetExtendedAttribute "messageType", MsgObj - MsgCct.GetExtendedCollection("MessageType").Add MsgObj - Sender = Trim(Mid(CurMsg, pos + Len("sent by"))) - MsgCct.SetExtendedAttribute "Sender", Sender - - MsgCct.SetNameAndCode MsgObj.Name & Sender & obj.Name, MsgObj.Code & UCase(Sender) & obj.Code, False - obj.GetExtendedCollection("MessageTypes").Add MsgCct - end if - wend - obj.SetExtendedAttribute "%CurrentTargetCode%.ReversedMessageContracts", "" - -End Sub - - - - -TemplateTargetItem -F06E94B2-0C54-487C-9AF7-8A118819BE73 -Templates -0 - -0 - -1 - - -6743CFE0-8ED0-47EB-8F2B-FFB723AA490F -messageContractList -.foreach_Item(MessageTypes) -%MessageType.GeneratedName% sent by %Sender% -.next(,\n) -0 - -0 - -1 - - - - -FormTargetItem -A4FEB729-6564-469C-9EDC-55C0572F021C -Forms -0 - -0 - -1 - - -E786FAE3-7C4C-4CB8-9168-3EB05DF81D81 -Microsoft -<Form > - <ExtendedAttribute Name="Owner" Caption="Authorization" Attribute="Owner" AttributeID="{0C5FB62A-C1D0-4E92-87B2-6F2A596C9696}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> -</Form> - -0 - -1276605960 -obalen -1 -24954 - - -C175928D-C147-4F01-B876-D2B7268717D8 -HiddenForm -<Form > - <ExtendedAttribute Attribute="ReversedMessageContracts" Caption="Hidden" AttributeID="{F7F8A228-1F18-4101-9FCC-847401C86B83}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" /> -</Form> - -0 - -1276605960 -obalen -1 -Dialog - - - - - - -46EE4EE0-6C8F-4420-AAB7-E458917B4EC5 -MessageType -0 - -0 - -A message type defines the name of a message and the validation that the Service Broker performs on messages with that name. Both sides of a conversation must define the same message types. -1 -Message Type -Message Types -0E0000000100000018000000424D7E000000000000003E000000280000001000 -0000100000000100010000000000400000000000000000000000000000000000 -000000000000FFFFFF0000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000424D3605000000000000360400002800000010000000 -10000000010008000000000000010000000000000000000000000000EF000000 -00172500001D2600002A4500002C4E00002D430006464A00174148001A616400 -215E620023839300248C99003474790037747800387278003996A50042A7BD00 -4F8DB30050A8B600529DBD0057939F005A776E005A8171005B8679005CC4E100 -5D7979005DB7C2005EA8CC0060BDCC0061C0DA0061CCE10063A6A90066B2C400 -67867F0067B0C4006AC3D7006BA1A8006BBEC6006BCEEA006C897A006C8F9C00 -6F887E006FBCD60072D0E30074BDD100757D73007595900076D8F60077D8EC00 -7A929E007A9CAC007BA3B5007C9D99007CCCD3007DD6EB007E99AD007F899300 -7FBAC40080B3B60081D8F20082A8BA0087B3BA0087BEC70087C7CC0088A2B300 -88D0D80088E2E9008B9A9D008CBED0008CC5D4008CD2D9008D8A93008EF8FF00 -91C1D30092C9D20093E4F3009486910095AABF0095ACA80096C0BF0096C9C500 -96CDD20096F0FF0097C0B10098DCE10099D5E10099DAD8009BDEDF009CEBF400 -9FDBDB009FEAF300A0DBD700A0FFFF00A3E1E100A4B7BE00A4D5D300A5C6CF00 -A5D0D900A5E7EC00A6EAF100A8C9D900A8E2E800AAD4E100AAFAFF00ABD8E300 -ABE2E700ACEAF400ADD5D000B0DCED00B0E6ED00B1D2D500B2D0E100B4D6E600 -B5D4CB00B5DFEB00B6E3F000B6E5E300B7EBF100B7EDE800B8D9EC00B8DEE000 -BBE5EA00BBEAF800BBF4FD00BCDEDD00BDCAE000BDF2F500BDFDFF00BED1E600 -C0E6E600C1D7DD00C1E3E900C1F1FD00C6DADF00C6E8EE00C7D7DE00C7E7EC00 -C9FAFF00CAF4F900CBF5E400CDD3D800CDE0E500CDFFFF00CEE1E600CFE4E200 -CFFFFF00D0FAFF00D2FFFF00D3D4DE00D4FFFF00D5F5EA00D6FCFE00D6FFFF00 -D8DAE400D8FFFF00D9FCFF00DAEDF400DCF3FF00DCFEFF00DDFFFF00DEFBFF00 -DFFFFF00E1F6F700E3FFFC00E3FFFF00E4EDEA00E4F7FF00E4FFF900E4FFFF00 -E5F6FF00E6FFFA00E7E7ED00E7FFFD00E8FFFF00E9FFFF00ECFFFE00EDFFEE00 -EDFFF700EDFFFC00EDFFFE00EEFFFA00EFFFF500EFFFFC00F1F8F500F1FDFF00 -F1FEFF00F1FFF800F1FFFF00F3FAFD00F3FFFF00F4FAFF00F4FEF100F4FFFD00 -F4FFFF00F5F7EB00F5FAF900F6EEF800F6FCFF00F6FDFA00F6FFFE00F7FAFE00 -F7FBF600F7FFFF00F8F3F000F8FCFF00F8FFF500F8FFF700F9F6F800FAF1F400 -FAFAFF00FAFCFF00FAFFFF00FBF0EC00FBFFF900FCF5F800FCFEFE00FDFCFF00 -FDFFFC00FDFFFE00FEFAFF00FEFCFC00FFF4FB00FFF5FF00FFF8F500FFF8F600 -FFF8FF00FFF9FF00FFFAFD00FFFBFF00FFFCFF00FFFDFC00FFFDFF00FFFEFC00 -FFFEFF00FFFFF600FFFFF800FFFFF900FFFFFB00FFFFFC00FFFFFE0000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -7C4C3B32363F312730374BDCE8CAE5DF7F6E6B79766365715F5D46D5C2D8D3EB -6F72546988837A64397742BFB2B3D4E9678D6245493D3E1E5C5E18AEA2A6B9CD -609061536C747E5655753370168AB0AF8278506891897D585A4F2DA95215B4CC -8681859DA59B9A99736A4DB59526BEC1AA8B8C8E9398846D807B8FB114282CEA -E4E6C4BDE3C3BAA79EA0ADABA320A4EDE2E6D2C7E3E3B88792979E96944EA1C0 -DBD6C0BBE4D7AC3C0C0B0D0807050601D9C5BCB7DAD19F234034595741241300 -D8B6BCC0E4CBA0386619110E0A092104EDC8C6C9E3D0A0444A1B2A2F1D0F2902 -ECEDEECEE1DD9C431F22515B47251203DEE7EECFE0E0A8482B353A1C172E1A10 -0E0000000200000030000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF0000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -BDCAE000A9BAD00096ABC0008CA9BC0083A8BA007FA5B7007BA3B5007C9FB100 -7D9AAE00819CAF0086A0B20083A0B0007C9DAD007598A7006F919F006F8F9C00 -76919D007B8F9B007D8B9600838892008D869100A597A200D8CCD500FFF5FB00 -FFFAFD00FEFCFD00FAF7F600F8F3F000FBF8F600FFFCFB00FFFAF800FFF8F600 -BDCDE200B0C4D800A3BBCE009CBDCF0097BFD10098C2D20098C4D30098BED000 -99B8CB0098B6C80096B4C50094B4C40091B5C4008FB5C4008EB4C1008EB2BD00 -8DACB8008DA7B1008FA1AA009099A2008F8E9800A198A100D6CCD300FCF4F900 -FAF8FA00F9FAFA00F9F8F700F9F7F400FBF6F400FCF5F200FDF8F500FEFAF600 -BDD0E500B7CEE100B1CDDE00AED3E400ADD8E900B2DFEE00B6E5F200B6DFF000 -B6D7EA00AFD1E200A7C9D900A5CAD900A7CEDD00AAD3E100AFD8E400ADD5E000 -A6C9D300A1C0C800A1B8C0009EABB30092979F009E99A100D4CDD300FAF4F800 -F7F7F800F5F9F800F8FAF900FBFCF900FBF6F200FBF0EB00FCF7F200FEFDF700 -B9D3E600B6D4E500B3D7E600ACD8E600A5D7E600AADFEC00B1E7F300B7E7F400 -BDE7F400BAE2EF00B3DCEA00B1DCE900B1DFEC00AFE0EB00AFDFE900A7D6DE00 -9AC5CB0098BFC400A4C4C900A5BCC20095A2A8009CA1A600D1D3D600F6F9F900 -F3FAFA00F0FBFA00F3FCFA00F5FDF900F7F9F500FBF6F000FCFAF300FEFEF600 -B4D5E600B4DAE900B4DFEE00A8DBE7009BD5E100A1DDE800AAE7F100B6EEF600 -C4F5FB00C3F3FB00BFEFFA00BCEEF900B9EFFA00B3ECF400ACE4EB009ED5DA00 -8BBEC2008DBCBE00A6CFD000ADCDD00098ADB0009BA8AA00CED9D900F2FDFB00 -EFFDFC00ECFDFC00EDFDFA00EEFDF800F4FDF800FBFDF700FCFDF500FEFEF500 -B0D6E400B6E1EC00BEECF400AFE6EE009FDDE7009DDDE6009DDEE700A4E0E900 -AFE4EA00ADE1E800A9DCE600A6DCE500A4DEE7009CD9DF0090CDD2008BC7CB00 -8CC5C70095CACA00A5D3D300A3CACA0088A4A5008A9E9E00C5D4D400EEFEFC00 -EBFEFC00E7FEFC00E8FEFA00E9FEF800F0FEF800F6FEF700F8FEF600FBFEF500 -ABD7E300BAE8EF00C8F9FB00B8F1F500A6E7EF009ADEE50090D4DC0090D0D900 -96CED60092C8D1008DC3CC008BC4CC008BC8CE0080C1C60070B1B50077B8BA00 -90CECF009FDAD900A3D7D50097C3C2007597970076908F00BBCFCE00EAFEFC00 -E6FEFC00E2FEFB00E3FEF900E4FEF800EBFEF700F1FEF700F3FEF600F7FEF500 -A8D4DF00BAE8EE00CBFCFD00BAF3F600A7E9EF009BE0E50091D6DC0094D3DB00 -9BD4DB009AD1D90099CFD60098D2D8009AD8DC0090D1D5007FC1C40082C4C500 -94D3D400A0DDDC00A6DDDA009DCCC9007CA19F0079969400AFC8C400D0E9E500 -BDDBD500B1D3CC00C7E8DF00DAFAF000E5FCF300EEFEF600F0FEF400F2FEF200 -A5D1DA00B9E6EB00CCFCFD00BAF3F500A7E8EC009DE1E60096DAE0009EDCE200 -A8E0E700ACE2E800AFE4EA00B0E9EF00B4F1F400AAECEE0099DCDE0094D6D700 -97D8D7009FDDDB00ABE5E000A7D9D5008AB1AD0082A29D00A3C0BA00B0CFC700 -8AAFA400759E9100A4CDBE00CFF6E600DDF9EF00ECFEF600ECFEF100EDFEEE00 -AED6DE00BAE4EA00C6F3F500B4E9EC00A1DEE3009EDDE2009DDDE200A9E2E700 -B6EAF000BAECF200BCEDF300BBF1F500BCF5F800B2EFF100A2E2E3009BDBDA00 -9BDAD8009DDAD700A5DDD900A0D2CD0088AFAA0085A5A000AAC7C100BAD8D000 -90B6AA00749E8F008CB6A600A6CEBD00CAE6DB00ECFEF500EEFEF200EFFEEF00 -BCDFE600BCE3E900BDE7EC00ABDDE1009AD1D7009ED7DB00A5DEE300B4E8ED00 -C5F3F800C8F4F900C6F2F700C1F2F500BEF2F500B3EBED00A4DFE0009EDAD900 -9EDAD7009CD7D3009BD1CD0093C3BE0081A6A20086A5A000B8D3CF00D2EEE700 -ABCEC30089B2A20079A1910073998900B2CDC100EDFEF400F1FEF300F5FEF200 -C2DFE500BEDEE400BBDFE400B1DADF00A6D5DB00ADDDE100B7E8EC00C4EFF300 -D2F6FB00D3F5FB00CFF1F700CAF2F600C7F3F700BFEFF100B5E8E900AEE3E200 -A8DFDC00A3D9D5009FD0CC0096C2BD0088AAA60090ACA600C2DAD600E0F8F200 -C2E1D700A2C8BA007EA29400688A7A00ADC5B900EFFDF200F2FCF100F6FBF000 -C5D9E000C1D8DE00BED8DE00BDDCE200BCE1E700C4EAEE00CFF5F800D6F6FB00 -DDF6FD00DCF3FA00D6EEF500D4F1F600D3F6FA00D1F7FA00CEF7F800C4F2F100 -B7E6E500AEDEDA00A9D6D100A2C9C40095B4B0009DB5B000CADFDB00E7FBF700 -D6F1E900BEE0D30090AFA200708E7F00B2C7BA00F2FCF000F3FAEE00F4F7EB00 -D0DBE200C9D8DD00C3D5DA00C4DBE100C7E3E900CDEBEF00D5F3F600D9F1F500 -DDEDF400DCEAF200D9E6EF00D5EAF000D3EFF300D0F0F300CDF0F200C6EEEE00 -BCE7E600B6E1DE00B2DAD600ADD1CD00A8C3C000B0C6C200D3E6E300E5F8F400 -C7E1D900A9C8BD0088A59900728D7F00A1B3A700CFD8CC00E3E8DD00F6F8ED00 -DFE3E900D4DBE000CAD3D800C9D9DE00CADFE500CDE3E700CFE6EA00D2E2E700 -D4DCE500D6DBE500D7DCE600D2DEE500CBE0E500C4DEE200BCDCDF00B9DDDF00 -BBE2E200BBE3E100B8DDDB00B9DAD800BDD7D600C8DDD900DCEFED00DDF0EC00 -A3BBB50074918800708B82006F897D0081918600949C9100C7CCC200FBFCF400 -EDECF100E3E4E900D9DDE100D7E1E500D6E5E900D7E6EA00D6E6EB00D9E2E800 -DCDEE600DEDDE600DFDDE700D9DFE700D2E1E600CAE0E400C0DEE000BFE0E200 -C3E7E800C5EAE900C4E6E600C7E6E500CFE8E700D8EDEB00E2F6F400DCF1ED00 -A7BFBB007A979000728D86006E887E0080908800949C9300C8CCC500FEFEF800 -F8F6FA00F4F3F600F1F1F400EEF1F500EBF3F700E9F3F700E9F2F700ECF1F600 -F1F0F500F0ECF300EEE9F200EAECF300E6F2F500E1F3F400D9F2F300D5F3F400 -D4F6F600D4F7F700D5F5F500D7F5F500DDF6F600E2F8F600E4FBFA00E2F9F700 -CEE8E600B6D2D0008CA9A4006E8A82009BADA700C8D1CC00E3E7E200FEFEF900 -FFFBFE00FEFBFD00FEFCFE00FAFBFE00F6FBFE00F4FAFE00F3F9FD00F7FAFD00 -FDFAFE00FBF6FB00F8F1F900F4F4FA00F1FBFD00EAFCFC00E1F9FA00DBF9FA00 -D9FCFC00DAFEFE00DBFEFE00DDFEFE00E2FEFE00E5FDFD00E2FDFD00E2FDFC00 -E0FDFE00D3F3F3009EBFBC0077989200AEC3C100E3EFEC00EFF6F300FBFEFB00 -FFFAFD00FEFBFD00FEFCFE00FAFCFE00F8FDFE00F6FBFE00F5F9FD00F8FAFD00 -FDFAFE00FDF8FD00FBF6FC00F7F8FC00F2FBFD00E6F9FA00D6F0F300CFF1F400 -D2F9FA00D5FEFE00D6FEFE00D8FEFE00DDFEFE00DEFDFD00DBFCFD00D9FBFC00 -D7FDFE00CFF6F600A8D0CE008DB2AF00B8D2D200E1F3F300EBF8F700F6FEFD00 -FEFAFC00FDFBFC00FDFCFD00FAFDFE00F8FEFE00F6FCFE00F6F9FC00F8FAFD00 -FDFAFE00FDFAFE00FDFAFE00F8FBFE00F1FCFE00DFF4F600C5E3E700B8DDE200 -B4E1E300B3E3E300B4E3E300B6E3E400BAE2E400BAE1E200B5DEE000B1DCDE00 -AFDEDF00A9DADA008FBFBF007DA9A9009BBEC000B8D2D400BED2D300C4D2D400 -FEFBFC00FCFBFC00FBFCFD00F8FDFE00F5FEFE00F3FCFE00F3F9FC00F6FAFD00 -FDFAFE00FDFAFE00FDFAFE00F6FBFE00EEFCFE00D3ECEE00ACCFD4008EB9BF00 -74A9AD00679FA200669FA200679FA3006A9FA300699C9F006095980059909300 -56929400518F9100417C7E00356D6F004673780057797F00526F74004E646B00 -FDFCFC00FBFCFB00FAFCFC00F7FDFD00F3FEFE00F2FCFE00F1F9FC00F5FAFD00 -FCFAFE00FCFAFE00FCFBFE00F4FBFE00EAFCFE00C8E6E90098C0C50072A2A900 -528E9300418186003F8185003F82870044838A00448187003A797F0032737800 -2D747800287176001E63670015575C001B5158001F4A53000F323B00001B2400 -FDFDFD00FAFCFB00F7FCFA00F4FDFC00F2FEFE00F2FDFE00F0FAFD00F5FAFD00 -FCF9FE00FBFAFE00FBFBFE00F1FAFE00E5FBFE00C1E2E6008CB9BF0072A8AF00 -6CACB30067AEB40063ACB20065AFB50071B8C00077BDC40072BAC1006DB7BD00 -65B5BA005DB0B600519FA5004590970042818B003D717C001E44500000182400 -FDFFFD00F8FCFA00F5FBF800F2FCFB00F2FEFD00F2FEFE00F0FCFE00F5FBFE00 -FCF9FE00FBFAFE00F9FBFE00EFFAFE00E2FAFE00BCE1E60086B6BD0074AFB600 -82C7CF0086D2D9007CCBD3007CCDD4008AD8E10093E0E90090DFE9008BDEE600 -82D9E10077D3DB006CC2CB0060B3BD005BA3AF0055919E002A55640000182700 -FDFFFC00F7FBF900F2F8F600F1FBFA00F2FEFD00F2FEFE00F1FDFE00F6FCFE00 -FCFAFE00FBFAFE00F8FBFE00EFFBFE00E2FBFE00BFE6E9008DC0C70080BEC500 -91DAE1008FDFE50076CAD30069BEC8006CBFCB006BBECA0063B9C6005BB4C000 -52B0BA0048AAB50043A1AD003F99A600509EAE005D9FB0002E61730000233600 -FDFFFC00F6FAF800F1F7F500F1FBF800F2FEFC00F3FEFD00F3FEFE00F7FDFE00 -FDFAFE00FBFAFE00F8FAFE00EFFBFE00E3FDFE00C2EAED0094C8D10089CAD200 -9CE7EE0095E8EF0071C8D3005AB4C00056AEBC004FA8B60045A0AF003A9AA800 -3195A200288F9E00278A9A0028889800489DAF0063ACC000316C8000002B4200 -FEFFFC00F8FBF800F4F8F500F3FBF800F3FEFC00F4FEFD00F4FEFE00F8FDFE00 -FDFAFE00FBF9FE00F9F9FE00EFFBFE00E3FDFE00C4ECEF0099CDD7008ACBD700 -96E0EC008DE0EC006FC8D4005FBAC80061BCCC0062BDCE005EBCCD0058BACB00 -4FB4C50044ADC0003BA1B4003799AC0051A8BD0067B1C900336E8500002A4300 -FFFFFB00FAFCF900F7FAF600F6FCF800F5FEFC00F5FDFC00F6FDFD00F9FCFD00 -FDF9FE00FCF8FE00FAF8FE00F0FAFE00E2FCFE00C5EDF1009DD0DC008BCADA00 -8ED8E70083D7E7006DC7D50063C0CF006CCADB0074D2E40076D6E90075D8EA00 -6BD1E5005FCADE0050B7CD0046AABF0059B2CA0069B5CF00346F890000294400 -FFFFFB00FCFDF900FAFCF800FAFDFA00F9FEFC00F8FCFB00F7F9FA00F9F9FB00 -FDF8FE00FDF7FE00FCF6FE00F1F7FE00E2F6FE00C4E7F1009DCCDA0085C2D200 -7EC6D60073C5D6006AC1D2006AC6D7007AD5E60085E2F10089E8F4008AEBF400 -80E6F20075DFED0065CCE00058BAD3005AB2CD005BA6C3002D688600002A4800 -FFFEFA00FEFDF900FEFDFA00FEFEFC00FEFEFD00FCFAF900F8F5F600FAF5F900 -FEF6FD00FEF6FE00FEF5FE00F2F4FE00E2F2FE00C4E3F1009DC9D90081BACC00 -6FB5C60065B4C70068BDD00071CBDE0086DFF00095F1FD009AF7FD009BFBFC00 -91F7FC0088F1FB0078DEF10069CAE6005CB3D0004F99B80028638400022E5000 -FFFBF700FEFBF800FEFDFA00FEFEFC00FEFEFD00FCF9F900F8F3F500FAF3F800 -FEF6FD00FEF6FE00FEF6FE00F3F5FE00E5F4FE00C8E4F100A0CADA0084BCCE00 -75B9CB006DBBCE0070C5D90076D0E40082DBEF0088E2F50082DFEF007DDDEA00 -77DCEC0073DBEE0071D6EE006ECFEC0062B8D700569FC1003E7EA100285D8100 -FFF8F500FEFAF700FEFDFA00FEFEFC00FEFEFD00FCF9F900F8F1F400FAF2F700 -FEF6FD00FEF6FE00FEF6FE00F4F6FE00E8F5FE00CBE4F100A2CBDB0087BED000 -7ABCD00074C1D50078CCE1007BD4EA007FD7EE007BD3EE006BC7E1005FC0D900 -5DC2DD005EC5E2006ACFEC0073D3F20067BCDD005CA5C9005498BD004D8BB100 - -1 -MessageType -1 - - -ExtendedAttributeTargetItem -98C49BF3-6D04-4F46-87B6-A55FCF30F921 -Extended Attributes -0 - -0 - -1 - - -63991A53-F028-4963-8EAE-A5C7445128E7 -Owner -0 - -1286890165 -psitja -Sets the owner of the message type to the specified database user or role. When the current user is dbo or sa, owner_name may be the name of any valid user or role. Otherwise, owner_name must be the name of the current user, the name of a user that the current user has IMPERSONATE permission for, or the name of a role to which the current user belongs. When this clause is omitted, the message type belongs to the current user. -1 -15 -4BA9F642-DAB1-11D1-9944-006097355D9B -User -0 - - - - - -1DB0BDC6-145A-4490-AB25-94F0A3B6C476 -Validation -0 - -0 - -Specifies how the Service Broker validates the message body for messages of this type. When this clause is not specified, validation defaults to NONE. -1 -12 -none -none;empty;well_formed_xml;valid_xml with schema collection -1 -0 - - - - -ExtendedCollectionTargetItem -E3ABAB6B-922F-414C-9E1D-325ED8697A1E -Extended Collections -0 - -0 - -1 - - -D756DA5E-3215-4007-A823-515F765A665D -Owner -0 - -1286890165 -psitja -1 -4BA9F642-DAB1-11D1-9944-006097355D9B -User - - - - - - - -CriterionTargetItem -8D0E7C15-6645-47DB-B762-FF8DCB3498DF -Criteria -0 - -0 - -1 - - -5A0DB295-1259-4D49-846E-A6BF99653545 -WithSchemaCollection -%.L9:Validation% == valid_xml -0 - -0 - -The message body XML must conform to a schema in the specified schema collection. -1 - - -ExtendedAttributeTargetItem -37A272F9-D07D-461C-BF04-ED02BFAB07B1 -Extended Attributes -0 - -0 - -1 - - -6E373F8D-02CF-4083-ADDC-5D9D9C5220C8 -SchemaCollectionName -0 - -0 - -Specifies the name of an existing XML schema collection. -1 -15 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -XMLSchemaCollection -0 - - - - - - - -ExtendedCollectionTargetItem -C9215CB8-3C1A-41BC-BC65-FE2BCB4DC377 -Extended Collections -0 - -0 - -1 - - -3AAD8D1C-B40C-436F-AF79-B062D2713B0B -SchemaCollectionName -0 - -0 - -1 -MessageType -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -XMLSchemaCollection - - - - - - - - -FormTargetItem -ACD6A28C-8A49-4CD6-B893-34B9ECD9BA9C -Forms -0 - -0 - -1 - - -D1F3D624-3684-40E6-90F3-F20436E7546A -Microsoft -<Form > - <ExtendedAttribute Attribute="Owner" Caption="Authorization" AttributeID="{B8644BBE-9AEB-4DDF-AF2C-6F3F2AC81B03}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="Validation" Caption="Validation" AttributeID="{1DB0BDC6-145A-4490-AB25-94F0A3B6C476}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="SchemaCollectionName" Caption="Schema" AttributeID="{6E373F8D-02CF-4083-ADDC-5D9D9C5220C8}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> -</Form> - -0 - -1276605960 -obalen -1 -24954 - - - - - - -398CE67B-DB06-42DC-8D96-C0FAB09BACA9 -Route -0 - -0 - -For outgoing messages, Service Broker determines routing by checking the routing table in the local database. For messages in conversations that originate in another instance, including messages to be forwarded, Service Broker checks the routes in msdb. -1 -Route -Routes -0D0000000800000008000000424D7E000000000000003E000000280000001000 -0000100000000100010000000000400000000000000000000000000000000000 -000000000000FFFFFF0000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000424D3605000000000000360400002800000010000000 -100000000100080000000000000100000000000000000000000000004B000000 -222E2300253327002D4D3300394D3B003A8C4C004158450042684A00435A4B00 -43784F0045A0600046734F00475F4F00479E5C004B6352004B664F004C635200 -4C645300506956005CB375005CBB770065C8810066C0820067B07F006E363700 -713D3D0072D79000744242007D887F00814747008458580085EFA9008E575800 -90504F00926162009553530095595800965D5C00985756009AB3A1009DF7BC00 -A0E3BA00A66E6A00AA6C6F00B1575700B1636700B9707100BB625F00BBF6D000 -BD737400BDF8D100C06F6B00C0767800C4646300C4969600C4DCC900C4DDCA00 -C6898800CD6E6E00CE7F7B00D0827F00D3878600D67E7800DA717000DA919300 -DA9F9E00DC878600DD898600DD908E00E18D8D00E3A7A800E5A4A600EEA8A800 -F1A8A800F6B8B800FFFFFF000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A -4A4A4A4A4A4A4A4A24234A3E224A4A4A4A4A4A4A4A4A4A4A202C1822184A4A4A -4A4A4A4A4A4A251C322E2E3E3429174A4A4A4A4A4A4A4133411F3538342B2B4A -4A4A4A4A014A4A3C441A4A35392B4A4A4A4A4A4A01014346482A1D21322B174A -4A3700020312013F4947423D3233304A370004091215160145403B3A2D4A4A4A -1B0C1414191E272811404A3A304A4A4A0413040806272F0F4A4A4A4A4A4A4A4A -0A0837360D31104A4A4A4A4A4A4A4A4A03374A4A05074A4A4A4A4A4A4A4A4A4A -0E4A4A4A0B4A4A4A4A4A4A4A4A4A4A4A264A4A4A4A4A4A4A4A4A4A4A4A4A4A4A -0E0000000200000030000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF0000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00 -FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00 -FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00 -FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00F1EBEB00 -DAC7C700CEB4B300CEB3B300D7C1C100EEE5E500FBF3F300F3D4D400E9BCBB00 -DAB5B500D2B7B700E9DDDD00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00E4D6D600 -B58C8C009B6664009A646200AE807F00DECBCB00F8E8E800E7A7A700D2757400 -B4676700A46C6C00D3B9B900FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00E2D2D200 -AD818000955857009B5B5A00A66B6C00B7929300C1A0A000BE7D7D00B45F5F00 -9A525200905A5A00CAB1B100FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00E2D1D100 -AB7C7B0095535400A45C5E00A35C60008D545600815050009152520095515100 -8247470081505000C3ACAC00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00F1E9E800DDC9C900D3BBBB00CFB7B700C29F9E00 -AC727000A55C5B00AE606000AB5C5E0099515100914D4C00A3565500AA5A5A00 -9C52520097575600BB919000DAC4C300CFB9B900C7B2B300E2D8D800FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00E1CECE00B58887009E676700946060009A605F00 -AE676400B8676400B8636100B7605E00B35D5B00B55F5C00C4656400CC696900 -C1636200B8616000B4706F00AD7C79009664630084555500C1A9A900FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00E4CDCC00BE848300A76262009B5A5A00A35E5E00 -BC6E6C00C2706D00B2646200AD615F00B66C6A00C0747200CA767500CF757400 -C96B6A00C1636200B4636200A66260009550500089474700C3A2A200FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00EED4D400D9969600C8797900BA717200BD727300 -CE7D7B00C8787600A66463009C616100B27B7B00C28B8A00C5878600C77F7E00 -C5706E00C1626100B75D5C00AD595800A9545400A8565600D2AAAA00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00E2E4E300 -C0C4C100D0D3D100F2F3F200F5E3E300EBBCBC00DC9E9E00CC858600CB7D7E00 -D6848300C97B7A009D5F5F0094636300BB959500D2B0AF00CA9B9A00C4878600 -C5757400C4646400B95C5C00B1595900BD737300CB909000E4C7C700FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00BABEBB00 -646E65008C938D00E0E3E100FBF6F600F8EBEB00EBCAC900D7999800D2858400 -DA888800C87B7A00965858008F656500CAB4B400EADBDB00D4B1B100C48D8D00 -C77A7A00C8696900BB5F5F00B45E5E00D2A0A000EFDEDE00F6EEEE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00AAAFAB00 -404C41005A655C00A5ABA600D0CBC800E9D8D700ECC7C600DDA0A000DA908F00 -E0929200CE8384009F61610093666700BDA3A400D3C1C100C19F9F00B6828200 -C0767600C66B6A00BA605F00B25D5D00C4929200D5C5C600E9E1E100FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00AAAFAB00 -404C4100354237004D584E0083777000C89A9700E2A5A400E09EA000E39D9E00 -E99F9F00DA929300B47374009D6769009D7374009E7979009D727200A16C6C00 -B36C6B00C06B6800B7605E00AE5656009D5C5D0091676800C7B2B200FEFEFE00 -FFFFFF00F6F9F700EFF5F000DBE0DC00C7CBC800C6CBC700C7CFC9008A958C00 -3D4C3E002C413000304E37005A5E4C0095706700BB827F00D5949500E5A1A300 -EDA7A700E4A0A100CA888900B4767700A46A6A009B6363009F666500A6686600 -B36A6800BE6C6800B8646200B05C5D0098505100854B4C00C1A4A400FEFEFE00 -FFFFFF00E8F1EA00D3E5D8009CAA9E00616B62005D6C60006177650050655300 -384C3B003B5F4400498559004E7A5500515746007B605900BC848300E19C9E00 -EDABAB00EEACAC00E29FA000D7929200CB848300C47A7700C3777400C2747000 -C0706C00BE6E6A00BC6D6C00BA6C6D00B0676800AA676800D4B2B300FEFEFE00 -F1F7F300C8D6CB00A1B6A60068816C002D4933002A4E3200305B3A0037604000 -3D6245004A80580058A86F004F9463003A624500575B4C0093706A00C0898800 -E0A4A400EEAFAF00EAA9A900E59F9D00DD908E00D8848100D4807C00D07B7500 -C5737000BF726F00C6848300CD939500CB939300CC959600E5C9CA00FEFEFE00 -D5E6D90094A597005464560042674900346E4000367947003C845000448C5800 -4E92600057A26C005FB579005DAB75005594690054785B00575B4C007D655F00 -C2929100E5A8A900E0A3A300DC9B9900D68D8A00D2827E00CF7F7C00CC7B7700 -C2747200BF787800D6AAAA00EBD6D600EBD5D600EBD6D700F4EAEA00FEFEFE00 -B6CCBB00718975002C47310033693F0040905300449D5C0049A5630052AE6C00 -5CB6760063BE7E0068C586006DC488006FBE8900619D7500476B52005F665800 -A2878100C99A9800D29C9B00D89D9B00D89B9800D6959100D0878400CA7C7900 -C0757400BF7D7E00E0C2C200FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -93A39700688E70003D7A4B0046935A0054B06C0057B6710059B974005FC07B00 -68C883006FD18D0077DB98007FDE9F0087DFA40085CF9E007BB48F00789D8300 -7C87760092837900BE938F00DDA9A700E7C3C200E9C7C500D89C9A00CA7C7900 -C1767500C17E7F00E1C2C200FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -728876005D946A004AA0600051AE6A005BBC75005DBC77005EBA770062BE7C00 -68C2820074D3900082E8A40090F0B1009BF5BA009CE9B70094D6AC0087B99A00 -75937E00858A7D00BDA09A00E4BDBC00F3E3E300F7ECEB00E3B8B600D3908E00 -CB8C8B00CB939400E6CCCC00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -518A5E00519C640053AE6C004EA8660048A15E004A9B5F004D945F004F916000 -518F610069B27E0088E2A6009CF3BA00A9F5C3009BD7AF0078A688007F9E8900 -A9B9AE00CACCC700E2D6D400F3E2E200F9F2F200FBF6F600F2E0DF00EBCFCE00 -E8CECD00E8D0D100F4E9E900FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -3B884C00489C5D0056AF6F0052A467004B975C004E8E5C0051855D004C785600 -446A4C006399720090DEAA00A4F0BE00A9E5BE0096C2A400728E7B008A988D00 -D5DBD700FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -417C4D00458654004B905D0069A4780089B994008FB9980090B5980074957B00 -506E57006C9778009EDEB4009DD9B10081AD8F0088A59200A9B8AE00CAD1CC00 -ECEEEC00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -446F4D00497752004D80590085AA8E00C1D8C600C8DFCE00C8DECD0098AD9C00 -59715F006E8F78009FD1B00095C2A3006C8974008A998E00D9DEDA00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -3E5C430063856A0089AE9200B4CDBA00DFEBE200E5EFE700E5EFE700A7B6AA00 -586E5D0055715D006D90790089A69200A6B6AB00C8CFCA00EDEFED00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -3A4E3C007D928100C1D9C700E0EDE200FAFCFB00FEFEFE00FEFEFE00B5BEB600 -586C5C00445B4A004C61520087958B00DBE0DD00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -425A45008FA19200DDE9E000EFF5F000FCFDFC00FEFEFE00FEFEFE00B6BFB800 -5A6F6000667A6C0098A59C00C4CBC600EDEFEE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -4D685100A2B0A400F8F9F800FEFEFE00FEFEFE00FEFEFE00FEFEFE00B9C1BC00 -627668008A9A8F00E0E4E100FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -738D7900B6C3B900F9FAF900FEFEFE00FEFEFE00FEFEFE00FEFEFE00DBDFDD00 -B0BAB300C4CCC600EFF1EF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -99B2A000C9D6CD00F9FAFA00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FDFDFD00 -FDFDFD00FDFDFD00FDFDFD00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 - -1 -Route -1 - - -ExtendedAttributeTargetItem -193A7C33-ADA8-4AEA-96E0-F7455A41929F -Extended Attributes -0 - -0 - -1 - - -8F27AB4E-1BBD-4AAB-99C9-DCB4F16451F3 -Owner -0 - -1286890165 -psitja -Sets the owner of the route to the specified database user or role. -1 -15 -4BA9F642-DAB1-11D1-9944-006097355D9B -User -0 - - - - - -51B42EA1-8B94-432D-BEDD-037B752933E6 -Service -0 - -0 - -Specifies the name of the remote service that this route points to. -1 -12 -0 - - -7DA4290E-EC42-4F7F-84C8-FE31C22623A4 -BrokerInstance -0 - -0 - -Specifies the database that hosts the target service. -1 -12 -0 - - -E0131330-7CFF-4213-9D49-22F94EC42AD7 -Lifetime -0 - -0 - -Specifies the amount of time, in seconds, that SQL Server retains the route in the routing table. -1 -10 -0 - - -5780B1B4-7665-4229-A7CD-DB58254631CB -Address -0 - -0 - -Specifies the network address for this route. The next_hop_address specifies a TCP/IP address in the following format: TCP://{ dns_name | netbios_name | ip_address } : port_number - - -1 -12 -0 - - -B1DCEA4B-47AB-4932-8148-21C26B626A88 -MirrorAddress -0 - -0 - -Specifies the network address for a mirrored database with one mirrored database hosted at the next_hop_address. The next_hop_mirror_address specifies a TCP/IP address in the following format: -TCP://{ dns_name | netbios_name | ip_address } : port_number - - -1 -12 -0 - - - - -ExtendedCollectionTargetItem -854D36C8-30FC-4CFF-9901-43F1245BA86A -Extended Collections -0 - -0 - -1 - - -C0E3E66A-828A-4A1B-8A72-91A90587696C -Owner -0 - -1286890165 -psitja -Sets the owner of the route to the specified database user or role. -1 -4BA9F642-DAB1-11D1-9944-006097355D9B -User - - - - - - - -FormTargetItem -786945BA-FE9D-4CEB-8A1A-CD965EC2305B -Forms -0 - -0 - -1 - - -B37C21B9-51F9-4EFC-A11A-B2A74CBD4F7F -Microsoft -<Form > - <ExtendedAttribute Attribute="Owner" Caption="Owner" AttributeID="{B8644BBE-9AEB-4DDF-AF2C-6F3F2AC81B03}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="Service" Caption="Remote service" AttributeID="{FC5427D8-655D-4ACD-93B8-F5EC1367E2E7}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="BrokerInstance" Caption="Broker instance" AttributeID="{7DA4290E-EC42-4F7F-84C8-FE31C22623A4}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="Lifetime" Caption="Lifetime" AttributeID="{E0131330-7CFF-4213-9D49-22F94EC42AD7}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="Address" Caption="Address" AttributeID="{5780B1B4-7665-4229-A7CD-DB58254631CB}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="MirrorAddress" Caption="Mirror address" AttributeID="{B1DCEA4B-47AB-4932-8148-21C26B626A88}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> -</Form> - -0 - -1276605960 -obalen -1 -24954 - - - - - - -B9F2FEEB-A308-4022-B7C0-55B4538CAE7B -MessageContract -0 - -0 - -Contract sub object that lists its message types. -1 -Message Contract -Message Contracts -1 -MessageContract -1 - - -ExtendedAttributeTargetItem -4AE082BE-1F84-43B2-AAFC-664D66F9DEBB -Extended Attributes -0 - -0 - -1 - - -45483C32-CB51-4BB0-9B38-A77FA8EBBE64 -Sender -0 - -0 - -Specifies which endpoint can send a message of the indicated message type. Contracts document the messages that services can use to have specific conversations. Each conversation has two endpoints: the initiator endpoint, the service that started the conversation, and the target endpoint, the service that the initiator is contacting. -1 -12 -any -initiator;target;any -1 -0 - - -F4F2138A-9766-4C4F-ABD5-D428074163B8 -MessageType -0 - -0 - -The message type of the contract. -1 -15 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -MessageType -0 - - - - - - - -ExtendedCollectionTargetItem -ECDEB7A2-85C9-45C1-8400-0A815E0BC16F -Extended Collections -0 - -0 - -1 - - -6481CD14-5E69-488C-8910-3549C87E3A6A -MessageType -0 - -0 - -1 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -MessageType - - - - -FormTargetItem -2D307C20-4314-402A-B92C-AF4DBB2566A0 -Forms -0 - -0 - -1 - - -79AB26F2-9CA7-46E6-8BB9-7B4E86076762 -Microsoft -<Form > - <ExtendedAttribute Attribute="Sender" Caption="Sent by" AttributeID="{45483C32-CB51-4BB0-9B38-A77FA8EBBE64}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="MessageType" Caption="Message type" AttributeID="{F4F2138A-9766-4C4F-ABD5-D428074163B8}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> -</Form> - -0 - -1276605960 -obalen -1 -24954 - - - - - - -21FF47EE-2E71-44E0-BCD1-752682607EDE -RemoteServiceBinding -0 - -0 - -A binding that defines the security credentials to use to initiate a conversation with a remote service. -1 -Remote Service Binding -Remote Service Bindings -0D0000000800000008000000424D7E000000000000003E000000280000001000 -0000100000000100010000000000400000000000000000000000000000000000 -000000000000FFFFFF0000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000424D3605000000000000360400002800000010000000 -1000000001000800000000000001000000000000000000000000000054000000 -01446E0001F2F5000E4D75001150770011618A003C40530047DDFE006FA0B800 -7D42430081474700824D4D0085505000884343009256550094504C0095616100 -974F4E009A595A009DB8C9009F666600A35E5F00A65E5C00A95D5C00AA646200 -B1575700B35C5C00B36A6900B5606000B5616000B5626300B6878800B7777A00 -B9707100BBA1A100BC727300BF979700BF9B9A00C06D6E00C0797400C09D9D00 -C26D6E00C4646300C4A9A900C5858600C58D8C00C5D6E000C6676600C67B7C00 -C8848400CA6D6E00CA8E8E00CB787500CCD9E100CE7F7B00CEB0B000D27A7900 -D38A8700D47E7800D5BBB900D78E8E00D7B8B800D8C9C900DA9F9E00DB888500 -DD888800DD9C9B00DD9F9E00DE949300DE9FA100DED2D200E2A0A200E3A6A900 -E5949400E5EEF300E6B2B300E7AEAE00E8A9A900EBA6A600EFF4F600F1ABAB00 -F3B5B600F5E1E000FEFEFE00FFFFFF0000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -535353535353532D002D53535353535303530303030303020102030303035303 -0753070707070700060007070707530749534949494949340412494949495349 -5353535353535353004E53535353535353535353535353530053535353535353 -5353535353532724004C2353535353535353535353530D14051C095353535353 -53535353362A151D0E1B103A21535353535353531A113316263729170C535353 -535353534B2F400B3C2C2E183253535353535353513B480A451E31183D535353 -5353535343464F1F0F13281908535353535353534A44504D3F3925302B535353 -535353535353473E383520535353535353535353535341425235225353535353 -0E0000000200000033000000424DBE000000000000003E000000280000002000 -0000200000000100010000000000800000000000000000000000000000000000 -000000000000FFFFFF0000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000424D3610000000000000360000002800000020000000 -2000000001002000000000000010000000000000000000000000000000000000 -FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00 -FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00EEF3F600D2DFE70092B0C200 -33698B002D6487008CABBE00D0DDE500ECF1F400FFFFFF00FFFFFF00FFFFFF00 -FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00 -8CAABD00C2D2DC00FAFBFB00CAD7E00092AEC0008BA9BC008BA9BC008BA9BC00 -8BA9BC008BA9BC008BA9BC008BA9BC008BA9BC0082A3B7007298AF005093AC00 -1B96AD001896AD004C94AC007197AE0080A2B7008BA9BC008BA9BC008BA9BC00 -8BA9BC008BA9BC008BA9BC0092AEC000CAD7E000FAFBFB00C2D2DC008BA9BC00 -19567C0087A7BA00F6F8F90096B1C200275F830018557B0018557B0018557B00 -18557B0018557B0018557B0018557B0018557B0017547A00145278000E789600 -04C4D10004C9D4000D7E9B001451780016537A0018557B0018557B0018557B00 -18557B0018557B0018557B00275F830096B1C200F6F8F90087A7BA0018557B00 -2E698C0092B1C200F7F9FA009FBAC9003A7292002D688B002D688B002D688B00 -2D688B002D688B002D688B002D688B002D688B00205D82000A4B7400076F9200 -15C3D80016C9DC0008759700084A72001E5C81002D688B002D688B002D688B00 -2D688B002D688B002D688B003A7292009FBAC900F7F9FA0092B1C2002D688B00 -689AB300B0CAD700F9FAFB00B9D0DB00709FB7006799B2006799B2006799B200 -6799B2006799B2006799B2006799B2006799B20049809E0017567D00106A9200 -30B6D80032BBDC001270970013537900467D9B006799B2006799B2006799B200 -6799B2006799B2006799B200709FB700B9D0DB00F9FAFB00B0CAD7006799B200 -A1C3D300CDDFE700FBFBFC00D3E3E900A6C6D500A0C2D200A0C2D200A0C2D200 -A0C2D200A0C2D200A0C2D200A0C2D200A0C2D20085ABBF005785A1004083A400 -3DA1C5003AA0C600347DA000477997007DA5BA00A0C2D200A0C2D200A0C2D200 -A0C2D200A0C2D200A0C2D200A6C6D500D3E3E900FBFBFC00CDDFE700A0C2D200 -DAE7ED00EBF2F500FCFDFD00EDF3F600DBE8EE00D9E6EC00D9E6EC00D9E6EC00 -D9E6EC00D9E6EC00D9E6EC00D9E6EC00D9E6EC00CEDDE500BCCED9008CB0C400 -3F85A600317B9F006998B10099B6C600C0D3DD00D9E6EC00D9E6EC00D9E6EC00 -D9E6EC00D9E6EC00D9E6EC00DBE8EE00EDF3F600FCFDFD00EBF2F500D9E6EC00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00BDCED900 -417392002E65880091AEC000D6E2E900EEF3F500FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00BDCED900 -4173920033698B00A4BBCA00EBF1F400F6F8FA00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00BDCED900 -41739200396D8E00B4C7D300FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00BDCED900 -41739200396D8E00B4C7D300FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00BDCED900 -41739200396D8E00B4C7D300FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00EFE7E700DAC7C700D1B9B900D1B8B8009B9AA500 -35607F00355F8000A99DA800E8C0BF00DABABA00D9C1C100EEE4E400FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00E0CFCF00B38B8B00A2706F00A16E6D0078626D00 -294D6C00325172009D6F7B00D17D7D00B4707000B1808000DCC8C800FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00DBC7C700A7777700965858009A5A5A007E545C00 -43475C0047495F00945B6200B76160009B5353009C686800D4BEBE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00DAC5C500A573720098555600A35C5E0094565B00 -6B464E0067434B008C5052009B535300864848008E606000CEBABA00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00F4EFEF00 -E9DCDC00E4D6D600E2D5D400CAABAB00A76E6D00A1585800AC5F6000A55A5B00 -8D4C4B0089494800995251009D5454008D4B4B00935D5D00C5A6A600E8DBDB00 -E2D5D500E0D4D500F0EAEB00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00E2D1D100 -C19A9900B2888800AB838200AA777700AD696700B2626000B4626000B15E5D00 -A85A5500AB5A5600B9625F00BD636200B05B5B00AC616100B9828100BC969500 -AA848400A5828300D4C4C500FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00D9BFBF00 -AB706F00975756008C4E4E009A585700B6696700BE6B6800B7636100B6625F00 -BB686200C36C6700CF706E00D1706F00C9686700BE636300B0676500A0636100 -884C4B00824B4C00C4AAAB00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00E5C8C700 -C6838200B56C6B00A9636400B2696A00C6767500C2727000AA626100A8646300 -B7777400C37F7D00C97E7D00CA797800C56B6B00BF626200B4606000A85C5B00 -9D5150009D595900D1B1B100FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00F1D1D000 -DF989700D1828200C4787A00C87A7C00D6828300C47777009D6061009A666700 -B6878700C4949400C48D8C00C4818000C36F6E00C0616100B75B5B00B0565600 -B1595900B96A6A00DEB9B900FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00F7E6E500 -EDC7C700DFA8A800CE878800CE808100D9868600C277770094595A00996B6B00 -C6A8A800D9BABA00CC9F9E00C4878600C5747300C3656500B85C5C00B4606000 -C7878700D9AFAF00ECD9D900FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FDFBFA00 -FBF7F700ECCDCD00D7979600D5878600DC8A8900C07777008C52530097707000 -D7C9C900EEE0E000D3B1B100C48D8D00C8797900C6696900B95E5E00B9696900 -DCB6B600F9F4F400FBF9F900FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00F8EDEC00 -F2D8D700E8BBBB00DC9C9C00DC939200E3959500CA8283009C6061009A6C6D00 -BBA1A200C7AFAF00B9929200B47D7D00C0737300C3696A00B85E5F00B3606100 -BD8D8D00CBB7B700E6DDDD00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00F3D9D900 -E4ACAB00E19F9F00E19F9F00E5A0A000EBA1A200D7909100B17173009E676900 -986E6F00976F7000986B6B00A0696A00B56A6B00BF696A00B65E5F00AA555600 -9555550090656600CAB6B700FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00F1D5D500 -E1A2A100DF989800E09C9D00E6A2A300EEA8A900E29E9E00C8868800B4767700 -A46B6B009E666500A2676600A9696800B86A6B00BE6A6B00B8626300AE5A5A00 -964F50008F575800CAB0B100FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00F2D9DA00 -E3ABAC00DF9B9D00DC969800E39FA000EFADAD00EDAAAB00E19D9E00D5909000 -CA828100C57A7700C3767300C2747000C26E6E00BF6B6C00BC6A6B00B9686800 -AF656600AF707100D9BCBC00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00F5E2E300 -E9BFC000E4B0B200E0A7AA00E5AAAB00EEB1B100EFAFAF00EBA8A800E49D9C00 -DD8E8C00D8848100D57F7B00CF7A7500C6717000C0717200C6808100CB8C8C00 -C98D8D00CD979800E7CECE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FAF1F200 -F5E2E300F2DBDC00F1D7D900EEC8C900EAB4B600E6A8A900E2A3A300DC999800 -D68B8800D3827E00D17F7B00CB7A7600C2727100C27D7E00D7AAAB00E7CBCB00 -E6CCCC00E8D0D100F4E9E900FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00F4E0E100E7B6B800DFA3A400DB9F9F00D79A9800 -D5918F00D2898600CF827E00C97B7800BF747300C4868700E5CBCC00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00F3DFDF00E5B2B200DD9F9F00DB9E9E00DEA8A700 -E5BBBA00E2B5B300D5938F00C97B7800C0757400C5878800E6CBCC00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FFFFFF00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00F3DEDD00E3AEAD00DC9C9B00DB9E9D00E4B6B500 -F5E5E400F2E0DF00DBA3A000C97B7800C0757400C6888900E6CBCC00FEFEFE00 -FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00FEFEFE00 - -1 -RemoteServiceBinding -1 - - -ExtendedAttributeTargetItem -9D2DEC03-9527-4781-9CDA-FA2C9E13DE18 -Extended Attributes -0 - -0 - -1 - - -CE9FBD19-A297-4126-99B3-F510E78000D1 -Owner -0 - -1286890165 -psitja -Sets the owner of the binding to the specified database user or role -1 -15 -4BA9F642-DAB1-11D1-9944-006097355D9B -User -0 - - - - - -8D4E8F6E-9515-4E17-8DE9-671AA9C1A971 -User -0 - -1285835944 -psitja -Specifies the database principal that owns the certificate associated with the remote service identified by the TO SERVICE clause. -1 -15 -4BA9F642-DAB1-11D1-9944-006097355D9B -User -0 - - - - - -7B11A597-B14F-4284-A9D9-8A3AC4D83021 -Anonymous -0 - -0 - -Specifies whether anonymous authentication is used when communicating with the remote service. -1 -0 - - -3D6821C9-3144-4F94-91E0-9AD7F551BFE4 -RemoteService -0 - -0 - -Specifies the remote service to bind to the user identified in the WITH USER clause. -1 -12 -0 - - - - -ExtendedCollectionTargetItem -CC15078A-BA18-48EA-93D8-340431B8B1AD -Extended Collections -0 - -0 - -1 - - -91ACF1B7-36BB-4916-A0E1-DAA15C8ADE14 -Owner -0 - -1286890165 -psitja -Sets the owner of the binding to the specified database user or role. -1 -4BA9F642-DAB1-11D1-9944-006097355D9B -User - - - - - -D77B06B7-6611-4B5E-9DF1-009A254227BE -User -0 - -1285836027 -psitja -Specifies the database principal that owns the certificate associated with the remote service identified by the TO SERVICE clause. -1 -User -4BA9F642-DAB1-11D1-9944-006097355D9B -User -NAME 80 CODE 80 - - - - - - - -FormTargetItem -60DABEA4-D4D2-4985-9431-B19534D8C8B9 -Forms -0 - -0 - -1 - - -A54EC6CA-AFFB-43F1-92A4-B2F0B8E5F57A -Microsoft -<Form > - <ExtendedAttribute Attribute="Owner" Caption="Owner" AttributeID="{B8644BBE-9AEB-4DDF-AF2C-6F3F2AC81B03}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="RemoteService" Caption="Remote service" AttributeID="{3D6821C9-3144-4F94-91E0-9AD7F551BFE4}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <HorizontalLayout Name="HorizontalLayout1" > - <ExtendedAttribute Attribute="User" Caption="User" AttributeID="{8D4E8F6E-9515-4E17-8DE9-671AA9C1A971}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="Anonymous" Caption="Anonymous" AttributeID="{7B11A597-B14F-4284-A9D9-8A3AC4D83021}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - </HorizontalLayout> -</Form> - -0 - -1276605960 -obalen -1 -24954 - - - - - - - - -BA310A28-8238-4C78-A238-053EECC24CC7 -ResourceGovernor -0 - -0 - -1 - - -9C16BE81-2FDB-4665-9CB4-55419822AC5C -WorkloadGroup -0 - -0 - -A workload group serves as a container for session requests that are similar according to the classification criteria that are applied to each request. A workload group allows the aggregate monitoring of resource consumption and the application of a uniform policy to all the requests in the group. A group defines the policies for its members. -1 -Workload group -Workload groups -1 -WorkloadGroup -1 - - -ExtendedAttributeTargetItem -D4BCE60D-132F-4B25-ACE5-2533CE0F6124 -Extended Attributes -0 - -0 - -1 - - -D11B5FAB-5A7B-4A00-9587-898B83DE7E13 -PhysicalOptions -0 - -0 - -Physical options for workload group -1 -14 -Options -0 - - -FE017EB9-7E29-43BE-8A2A-AC6325BD803D -Importance -0 - -0 - -Specifies the relative importance of a request in the workload group. -1 -100 -1 -with/importance = -Importance -0 - - -D82463A7-6F1B-40EC-9BE3-9B4CC687BEFE -RequestMaxMemoryGrantPercent -0 - -0 - -Specifies the maximum amount of memory that a single request can take from the pool. -1 -100 -1 -with/request_max_memory_grant_percent = -Request maximum memory -0 - - -E92AE78E-8532-4505-9287-5CBC6C1EAFD3 -RequestMaxCpuTimeSec -0 - -0 - -Specifies the maximum amount of CPU time, in seconds, that a request can use. -1 -100 -1 -with/request_max_cpu_time_sec = -Request maximum CPU -0 - - -48C6F793-F0D0-4062-9403-97B8422960A7 -RequestMemoryGrantTimeoutSec -0 - -0 - -Specifies the maximum time, in seconds, that a query can wait for a memory grant (work buffer memory) to become available. -1 -100 -1 -with/request_memory_grant_timeout_sec = -Memory grant request timeout -0 - - -D29ABF19-C6F5-4D22-B38B-963FF0E53061 -MaxDop -0 - -0 - -Specifies the maximum degree of parallelism (DOP) for parallel requests. -1 -100 -1 -with/max_dop = -Maximum degree of parallelism -0 - - -9F2679D0-6B25-44D3-81AB-3E6F683D3838 -GroupMaxRequests -0 - -0 - -Specifies the maximum number of simultaneous requests that are allowed to execute in the workload group. -1 -100 -1 -with/group_max_requests = -Maximum requests -0 - - -85A027D3-E2A1-47C0-B125-5BB1860437D4 -ResourcePool -0 - -0 - -Associates the workload group with the user-defined resource pool. -1 -15 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -ResourcePool -Resource pool -0 - - - - - - - -ExtendedCollectionTargetItem -9A49656E-2C0E-4652-AFB7-6D1697E07EFB -Extended Collections -0 - -0 - -1 - - -6866D3E2-9616-44A9-98BB-A83C61715342 -ResourcePool -0 - -1285836027 -psitja -1 -WorkloadGroups -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -ResourcePool -NAME 80 CODE 80 - - - - - - - -FormTargetItem -B8426D67-415A-4E46-8461-12C1816FB7F2 -Forms -0 - -0 - -1 - - -5AC3A0EC-1C9B-4910-A09B-91DEFF0699CC -Hidden -<Form > - <ExtendedAttribute Attribute="PhysicalOptions" Caption="Options" AttributeID="{D11B5FAB-5A7B-4A00-9587-898B83DE7E13}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" /> -</Form> - -0 - -1276605960 -obalen -1 -Dialog -24953 - - -1B3973CA-275B-4797-9C26-4886BAEE6726 -Microsoft -<Form > - <GroupBox Name="GroupBox1" Caption="Options" ControlAsLabel="No" > - <ExtendedAttribute Attribute="Importance" Caption="Importance" AttributeID="{FE017EB9-7E29-43BE-8A2A-AC6325BD803D}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="No" /> - <ExtendedAttribute Attribute="RequestMaxMemoryGrantPercent" Caption="Request maximum memory" AttributeID="{D82463A7-6F1B-40EC-9BE3-9B4CC687BEFE}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="No" /> - <ExtendedAttribute Attribute="RequestMaxCpuTimeSec" Caption="Request maximum CPU" AttributeID="{E92AE78E-8532-4505-9287-5CBC6C1EAFD3}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="No" /> - <ExtendedAttribute Attribute="RequestMemoryGrantTimeoutSec" Caption="Memory grant request timeout" AttributeID="{48C6F793-F0D0-4062-9403-97B8422960A7}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="No" /> - <ExtendedAttribute Attribute="MaxDop" Caption="Maximum degree of parallelism" AttributeID="{D29ABF19-C6F5-4D22-B38B-963FF0E53061}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="No" /> - <ExtendedAttribute Attribute="GroupMaxRequests" Caption="Maximum requests" AttributeID="{9F2679D0-6B25-44D3-81AB-3E6F683D3838}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="No" /> - </GroupBox> - <ExtendedAttribute Attribute="ResourcePool" Caption="Resource pool" AttributeID="{85A027D3-E2A1-47C0-B125-5BB1860437D4}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> -</Form> - -0 - -1276605960 -obalen -1 -24953 - - - - - - -98C0E616-E10E-4DE3-8F68-480B6C9B24BE -ResourcePool -0 - -0 - -A resource pool, or pool, represents the physical resources of the server. You can think of a pool as a virtual SQL Server instance inside of a SQL Server instance. - - -1 -Resource pool -Resource pools -1 -ResourcePool -1 - - -ExtendedAttributeTargetItem -02A5BEA9-0EA2-433C-8930-6EBB508C2A72 -Extended Attributes -0 - -0 - -1 - - -6774B5E0-9C83-4552-BDA1-C5CC1E4355CE -PhysicalOptions -0 - -0 - -Physical options for resource pool -1 -14 -Options -0 - - -A1AC111B-0F05-4A1F-B34A-8EF0C1E3D032 -MinCpuPercent -0 - -0 - -Specifies the guaranteed average CPU bandwidth for all requests in the resource pool when there is CPU contention. value is an integer with a default setting of 0. -1 -100 -1 -with/min_cpu_percent = -Min CPU percent -0 - - -DA8058E0-6EE7-4EC9-A052-1B85961F447D -MaxCpuPercent -0 - -0 - -Specifies the maximum average CPU bandwidth that all requests in resource pool will receive when there is CPU contention. value is an integer with a default setting of 100. -1 -100 -1 -with/max_cpu_percent = -Max CPU percent -0 - - -CBE5AE52-C1BD-476A-AEBA-CAE2C3FB9D9D -MinMemoryPercent -0 - -0 - -Specifies the minimum amount of memory reserved for this resource pool that cannot be shared with other resource pools. value is an integer with a default setting of 0. -1 -100 -1 -with/min_memory_percent = -Min memory percent -0 - - -6A9ABF61-24CB-416C-9BD1-7938C34973AF -MaxMemoryPercent -0 - -0 - -Specifies the total server memory that can be used by requests in this resource pool. value is an integer with a default setting of 100. -1 -100 -1 -with/max_memory_percent = -Max memory percent -0 - - - - -FormTargetItem -43839F89-C8FA-47EE-B4BE-2AE810CD8E92 -Forms -0 - -0 - -1 - - -7191B9BD-6446-483D-AC0E-D6E5EECFEEA1 -Microsoft -<Form > - <HorizontalLayout Name="HorizontalLayout1" > - <ExtendedAttribute Attribute="MinCpuPercent" Caption="CPU percent: Min" AttributeID="{A1AC111B-0F05-4A1F-B34A-8EF0C1E3D032}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="No" /> - <ExtendedAttribute Attribute="MaxCpuPercent" Caption="Max" AttributeID="{DA8058E0-6EE7-4EC9-A052-1B85961F447D}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="No" /> - </HorizontalLayout> - <HorizontalLayout Name="HorizontalLayout2" > - <ExtendedAttribute Attribute="MinMemoryPercent" Caption="Memory percent: Min" AttributeID="{CBE5AE52-C1BD-476A-AEBA-CAE2C3FB9D9D}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="No" /> - <ExtendedAttribute Attribute="MaxMemoryPercent" Caption="Max" AttributeID="{6A9ABF61-24CB-416C-9BD1-7938C34973AF}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="No" /> - </HorizontalLayout> -</Form> - -0 - -1276605960 -obalen -1 -24953 - - -92AE170D-6BC5-4384-856F-3530B6D869AF -Hiddent -<Form > - <ExtendedAttribute Attribute="PhysicalOptions" Caption="Options" AttributeID="{6774B5E0-9C83-4552-BDA1-C5CC1E4355CE}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" /> -</Form> - -0 - -1276605960 -obalen -1 -Dialog -24953 - - - - - - - - - - -TemplateTargetItem -0655993C-A127-45FA-A32C-9E35C6948ACB -Templates -0 - -0 - -1 - - -18A702D1-C8CE-4798-B55B-C841570EEE90 -getTypeList -List -char -character -tinyint -smallint -int -integer -bigint -numeric -decimal -dec -real -float -double precision -money -smallmoney -datetime -smalldatetime -timestamp -bit -binary -nvarchar -0 - -0 - -1 - - -71BD0726-F305-462E-941A-FAB3181ADF77 -authorizationList -list -.foreach_item(Model.Users,,\n,,%authorizationListCompare%) -.if (%IsShortcut%) -%Shortcut.ObjectID% \t %Shortcut.DisplayName% -.else -%ObjectID% \t %DisplayName% -.endif -.next(\n) -.foreach_item(Model.Roles,,\n,,%authorizationListCompare%) -.if (%IsShortcut%) -%Shortcut.ObjectID% \t %Shortcut.DisplayName% -.else -%ObjectID% \t %DisplayName% -.endif -.next(\n) - -0 - -0 - -Users or roles which can own an assembly -1 - - -9A1B9798-A9EE-4A04-AF8C-F1483B2E07E2 -authorizationListCompare -.set_value(item1Name, [%Item1.IsShortcut% ? %Item1.Shortcut.DisplayName% : %Item1.DisplayName%]) -.set_value(item2Name, [%Item2.IsShortcut% ? %Item2.Shortcut.DisplayName% : %Item2.DisplayName%]) -.bool (%item1Name% > %item2Name%) -0 - -0 - -1 - - - - -MethodTargetItem -7BD3957B-EC41-4602-8F9A-C17AF9BF8A8A -Methods -0 - -0 - -1 - - -5C81D106-7E92-4EC6-A83A-FCDD20897256 -AuthorizationEdit -Edit -0 - -0 - -1 -Sub %Method%(obj) - - Dim Mdl - Set Mdl = obj.Model - - ' Create a selection for the authorization - Dim Sel - Set Sel = Mdl.CreateSelection - If not (Sel is nothing) Then - Dim user, role - ' Add users to the selection - For Each user in Mdl.Users - Sel.Objects.Add user - Next - ' Add roles to the selection - For Each role in Mdl.Roles - Sel.Objects.Add role - Next - ' Display the object picker on the selectipn - Dim selObj - set selObj = Sel.ShowObjectPicker - If Not (selObj is Nothing) Then - ' Set the authorization from the selected object - obj.SetExtendedAttribute "Authorization", selObj - End If - - Sel.Delete - End If - -End Sub - - - - - - -6C71005B-C644-492B-8834-4CB9F591A251 -Index -0 - -1299602506 -Marc -1 - - -CriterionTargetItem -59C23FF1-4BE9-4F8E-B31A-1D1AD84710DD -Criteria -0 - -1299597612 -Marc -1 - - -1EA32495-5E76-4D34-8625-0BC45E3250B6 -FullText Index -%isFullText% -0 - -1299597484 -Marc -1 - - -ExtendedAttributeTargetItem -683CC686-D9F6-48F9-8541-51759296F6C4 -Extended Attributes -0 - -1299597484 -Marc -1 - - -E130124A-DF17-456F-A780-106F47EF59E1 -FullTextCatalog -0 - -1299597484 -Marc -Defines the full text catalog where the full text index is defined. -1 -15 -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -FullTextCatalog -0 - - - - - -30B79F87-9E9D-4B9D-A849-BC4E651DDBB3 -ChangeTracking -0 - -1299597484 -Marc -Specifies whether or not SQL Server maintains a list of all changes to the indexed data -1 -12 -manual ; auto; off ; off , no population -0 - - - - -ExtendedCollectionTargetItem -615029C8-54EB-4F66-A1A9-FE70CAB16359 -Extended Collections -0 - -1299597484 -Marc -1 - - -0B46BB5A-3A26-466D-AF82-199C7756CD11 -FullTextCatalog -0 - -1299597484 -Marc -1 -Full Text Indexes -8AF1C3D1-6782-4EBD-A539-2A16C7F61969 -FullTextCatalog -NAME 80 CODE 80 - - - - - - - -FormTargetItem -48741DE8-2641-42BE-8878-D078C374D434 -Forms -0 - -1299597484 -Marc -1 - - -3A5C8C0D-83C4-4419-A223-8605EDCCF042 -Microsoft -<Form > - <ExtendedAttribute Attribute="FullTextCatalog" Caption="Catalog:" AttributeID="{E130124A-DF17-456F-A780-106F47EF59E1}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="ChangeTracking" Caption="Change tracking:" AttributeID="{30B79F87-9E9D-4B9D-A849-BC4E651DDBB3}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> -</Form> - -0 - -1302080716 -obalen -1 -24975 - - - - - - -30E1F53E-31FC-4752-B3A1-ACDF3332FE91 -Spatial Index -%Type%==SPATIAL -1299594206 -Marc -1299604840 -Marc -1 - - -ExtendedAttributeTargetItem -E6D356EC-2660-4669-ACA6-0D63F9B71C87 -Extended Attributes -1299593346 -Marc -1303198737 -Marc -1 - - -967FD69E-8699-444F-9169-5DDFA2C18628 -Table -1299593346 -Marc -1299595242 -Marc -Spatial index parent table -1 -15 -1 -RO -Function %Get%(obj) - - dim coln - Set coln = obj.GetExtendedAttribute("%CurrentTargetCode%.IndexedColumn") - if(coln is Nothing) then - Set %Get% = Nothing - elseif(coln.IsKindOf(PDPDM.cls_Column)) then - Set %Get% = coln.Table - else - Set %Get% = Nothing - end if -End Function -380BAA40-0042-11D2-BD28-00A02478ECC9 -Table -0 - - -ECF487B6-DE04-43A2-9AD9-E7853B7EB6F0 -Definitions -1299593346 -Marc -1299595242 -Marc -1 - - -4E11A296-E662-4BA7-BFCB-F05E0615D50E -GridsDefn -1299593346 -Marc -1299669388 -Marc -Defines the density of the grid at each level of a tessellation scheme. -1 -12 -1 -Function %Get%(obj) - - dim attr, defn - - defn = "" - attr = obj.GetExtendedAttributeText("%CurrentTargetCode%.Level1Density") - if (attr <> "") then - defn = defn & ", level_1 = " & attr - end if - attr = obj.GetExtendedAttributeText("%CurrentTargetCode%.Level2Density") - if (attr <> "") then - defn = defn & ", level_2 = " & attr - end if - attr = obj.GetExtendedAttributeText("%CurrentTargetCode%.Level3Density") - if (attr <> "") then - defn = defn & ", level_3 = " & attr - end if - attr = obj.GetExtendedAttributeText("%CurrentTargetCode%.Level4Density") - if (attr <> "") then - defn = defn & ", level_4 = " & attr - end if - - if(defn <> "") then - %Get% = "( " & Mid(defn, 3) & " )" - ' also reset the cells_per_object options (which is incompatible with grids) - obj.SetExtendedAttribute "%CurrentTargetCode%.CellsPerObject", "" - else - %Get% = "" - end if -End Function -Sub %Set%(obj, value) - - dim str, arr - str = LCase(replace(replace(value, ")", ""), "(", "")) ' remove parenthesis - if (InStr(str, "=") > 0) then - obj.SetExtendedAttributeText "%CurrentTargetCode%.Level1Density", "" - obj.SetExtendedAttributeText "%CurrentTargetCode%.Level2Density", "" - obj.SetExtendedAttributeText "%CurrentTargetCode%.Level3Density", "" - obj.SetExtendedAttributeText "%CurrentTargetCode%.Level4Density", "" - for each str in split(str, ",") - arr = split(str, "=") - Select case(Trim(arr(0))) - case "level_1" : obj.SetExtendedAttributeText "%CurrentTargetCode%.Level1Density", Trim(arr(1)) - case "level_2" : obj.SetExtendedAttributeText "%CurrentTargetCode%.Level2Density", Trim(arr(1)) - case "level_3" : obj.SetExtendedAttributeText "%CurrentTargetCode%.Level3Density", Trim(arr(1)) - case "level_4" : obj.SetExtendedAttributeText "%CurrentTargetCode%.Level4Density", Trim(arr(1)) - End Select - next - else - arr = split(str, ",") - if(UBound(arr) <> 3) then '4 values - output "error in Grid definition: ubound=" & UBound(arr) - else - obj.SetExtendedAttributeText "%CurrentTargetCode%.Level1Density", Trim(arr(0)) - obj.SetExtendedAttributeText "%CurrentTargetCode%.Level2Density", Trim(arr(1)) - obj.SetExtendedAttributeText "%CurrentTargetCode%.Level3Density", Trim(arr(2)) - obj.SetExtendedAttributeText "%CurrentTargetCode%.Level4Density", Trim(arr(3)) - end if - end if - -End Sub -Grids -0 - - - - - - - -D633CF5C-47BE-44CF-9B34-03B7F0CDD280 -CellsPerObject -1299593346 -Marc -1299604440 -Marc -Specifies the number of tessellation cells per object that can be used for a single spatial object in the index by the tessellation process. n can be any integer between 1 and 8192, inclusive -1 -10 -Cells per object -0 - - -84B71BA8-5410-4C5F-B456-F3697013007E -TesselationType -1299593346 -Marc -1299677325 -Marc -Indicates the tessellation scheme for the spatial index. -1 -12 -geography_grid -1 -RO -Function %Get%(obj) - - dim col - set col = obj.GetExtendedAttribute("%CurrentTargetCode%.IndexedColumn") - if not (col is Nothing) then - if(LCase(col.ConceptualDataType) = "spatial") then - %Get% = LCase(col.DataType) & "_grid" - else - %Get% = "" - end if - else - %Get% = "" - end if - -End Function -1 -Tessellation scheme -0 - - -4D5A231C-DF71-4DAF-9630-C1C0AF5DE5B7 -Level1Density -1299593346 -Marc -1299595242 -Marc -Specifies the first (top) level grid. -1 -12 -%densityList% -1 -Level 1 -0 - - -041EDB6F-DE6C-4EA3-A349-DEF74E1A3C58 -Level2Density -1299593346 -Marc -1299595242 -Marc -Specifies the second-level grid. -1 -12 -%densityList% -1 -Level 2 -0 - - -A9C3E9B2-C64C-456B-93FE-AC8B73882111 -Level3Density -1299593346 -Marc -1299595242 -Marc -Specifies the third-level grid. -1 -12 -%densityList% -1 -Level 3 -0 - - -F84611AA-C8F1-4BB2-86A2-5E58A9A6067D -Level4Density -1299593346 -Marc -1299595242 -Marc -Specifies the fourth-level grid. -1 -12 -%densityList% -1 -Level 4 -0 - - -48F3352B-5139-446D-AE8F-5F3F0D70B0A3 -IndexedColumn -1299593413 -Marc -1299674760 -Marc -Specifies the spatial column on which the index is based. -1 -15 -%indexedColumnList% -1 -Function %Get%(obj) - - if(obj.IndexColumns.Count = 0) then - set %Get% = Nothing - else - set %Get% = obj.IndexColumns.Item(0).Column - end if -End Function -Sub %Set%(obj, value) - - if(obj.IndexColumns.Count > 0) then - obj.IndexColumns.Clear(True) - end if - - Dim IdxCol - Set IdxCol = obj.IndexColumns.CreateNew() - Set IdxCol.Column = value - - -End Sub -380BAA41-0042-11D2-BD28-00A02478ECC9 -Indexed column -0 - - - - - - - -FormTargetItem -3494111B-B573-4A0D-BD11-AA534096B777 -Forms -1299593346 -Marc -1299597484 -Marc -1 - - -CA868068-1C1D-46EE-A1CB-4BB029C90D3F -Spatial options -COLN -<Form > - <ExtendedAttribute Attribute="IndexedColumn" Caption="Indexed column" AttributeID="{48F3352B-5139-446D-AE8F-5F3F0D70B0A3}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <GroupBox Name="GroupBox2" Caption="Spatial options" ControlAsLabel="No" > - <ExtendedAttribute Attribute="TesselationType" Caption="Tessellation scheme" AttributeID="{84B71BA8-5410-4C5F-B456-F3697013007E}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" Display="Combobox" LeftText="false" /> - <ExtendedAttribute Attribute="BoundingBoxDefn" Caption="Bounding box" AttributeID="{6F042DD2-E8B9-4B06-80AD-8FBA7C7372D6}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="CellsPerObject" Caption="Cells per object" AttributeID="{FAC72E12-C7B7-4F0D-9786-EFE5FDCD59EB}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="GridsDefn" Caption="Grids" AttributeID="{79DB95A1-B7CE-4A53-AA77-1E2D9C6D3072}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - </GroupBox> - <GroupBox Name="GroupBox1" Caption="Index options" ControlAsLabel="No" > - <VerticalLayout Name="VerticalLayout1" > - <HorizontalLayout Name="HorizontalLayout1" > - <ExtendedAttribute Attribute="FillFactor" Caption="Fill factor" AttributeID="{3A273717-E593-4F43-906D-EF077FEA046F}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="PadIndex" Caption="Index padding" AttributeID="{5EAD2467-9E98-48A0-94FC-D1874605876E}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - </HorizontalLayout> - <ExtendedAttribute Attribute="MaxDoP" Caption="Max degree of parallelism" AttributeID="{300850D2-0E49-41B1-AFED-1B7A853F2868}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - </VerticalLayout> - <HorizontalLayout Name="HorizontalLayout4" > - <Space Name="Space1" Width="60" Height="6" HorizontalResize="No" VerticalResize="No" /> - <VerticalLayout Name="VerticalLayout4" > - <HorizontalLayout Name="HorizontalLayout2" > - <ExtendedAttribute Attribute="AllowRowLocks" Caption="Allow row locks" AttributeID="{A858A7A8-0184-40C6-9102-7D6F0B22407C}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <Space Name="Space2" Width="24" Height="6" HorizontalResize="No" VerticalResize="No" /> - <ExtendedAttribute Attribute="AllowPageLocks" Caption="Allow page locks" AttributeID="{265DC722-8E5C-4550-AC3F-ED502D54DC1D}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - </HorizontalLayout> - <HorizontalLayout Name="HorizontalLayout3" > - <ExtendedAttribute Attribute="SortInTempDB" Caption="Store sort result" AttributeID="{5BBD7037-A3C3-40FB-AF53-E60CCB2D1F3A}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <Space Name="Space3" Width="24" Height="6" HorizontalResize="No" VerticalResize="No" /> - <ExtendedAttribute Attribute="StatisticsNoRecompute" Caption="Do not recompute statistics" AttributeID="{36087631-0279-40A6-A421-E3D7CB0DC376}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - </HorizontalLayout> - <ExtendedAttribute Attribute="DropExisting" Caption="Drop if exist" AttributeID="{E30BBAD4-6F19-4C36-8B3F-884A7DE15716}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" Display="Checkbox" LeftText="No" /> - </VerticalLayout> - </HorizontalLayout> - </GroupBox> -</Form> - -1299593346 -Marc -1305116654 -obalen -1 -Spatial options -COLN - - -89E1A96D-A514-4872-9089-E306A4F9FFDD -Physical Options -OPTIONS -<Form > -</Form> - -1299596050 -Marc -1305116654 -obalen -1 -Physical options -OPTIONS - - -27681CD0-21CE-43A5-A569-654B642E4B1E -GridsDialog -<Form > - <StaticText Name="StaticText1" Caption="The number of cells along the axes of a grid" /> - <StaticText Name="StaticText2" Caption="determines its density:" /> - <StaticText Name="StaticText3" Caption="the larger the number, the denser the grid." /> - <StaticText Name="StaticText4" Caption="For example, an 8x8 grid (which produces 64 cells)," /> - <StaticText Name="StaticText5" Caption="is denser than a 4x4 grid (which produces 16 cells)." /> - <StaticText Name="StaticText6" Caption="Grid density is defined on a per-level basis." /> - <Separator Name="Separator" /> - <ExtendedAttribute Attribute="Level1Density" Caption="Level 1" AttributeID="{9310651C-A64E-4DF1-A6BA-FEB22278B3A9}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" Display="Combobox" LeftText="false" /> - <ExtendedAttribute Attribute="Level2Density" Caption="Level 2" AttributeID="{BC9FAED5-F4E4-4295-A5BB-A4F448B9F1D7}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" Display="Combobox" LeftText="false" /> - <ExtendedAttribute Attribute="Level3Density" Caption="Level 3" AttributeID="{CF6A3552-0AA9-425C-BFF8-791AF30FDE40}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" Display="Combobox" LeftText="false" /> - <ExtendedAttribute Attribute="Level4Density" Caption="Level 4" AttributeID="{C20E8B69-7A4C-4232-AEE1-5B3121D667CE}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> -</Form> - -1299593346 -Marc -1299595242 -Marc -1 -Grids definition -Dialog - - - - -MethodTargetItem -6A0FB657-318C-4467-BD89-5FE8643DD528 -Methods -1299593413 -Marc -1299674760 -Marc -1 - - -E8DA40CA-A027-4FB3-A6F3-F09D75F677E4 -GridsDefnEdit -1299593413 -Marc -1299669327 -Marc -1 -Sub %Method%(obj) - - obj.CreateCustomDialog("%CurrentTargetCode%.GridsDialog").ShowDialog() - -End Sub - - -024A13A5-54C4-4C2C-A5F2-C41683585298 -IndexedColumnEdit -1299674541 -Marc -1299677927 -Marc -1 -Sub %Method%(obj) - - Dim Sel - Set Sel = obj.Model.CreateSelection() - - If not (Sel is nothing) Then - dim col - for each col in obj.Table.Columns - if(LCase(col.ConceptualDataType) = "spatial") then - Sel.Objects.Add(col) - end if - next - Dim selObj - set selObj = Sel.ShowObjectPicker() - If Not (selObj is Nothing) Then - obj.SetExtendedAttribute "%CurrentTargetCode%.IndexedColumn", selObj - End If - - Sel.Delete - End If - -End Sub - - - - -TemplateTargetItem -3AA8CFFC-C6BA-43CE-A230-1DC56DF2F465 -Templates -1299593413 -Marc -1299674524 -Marc -1 - - -E3C0DF74-7562-4456-8441-11E6E031BDE5 -densityList -low;medium;high -1299593413 -Marc -1299595242 -Marc -1 - - -6E4EDF73-3CD1-4E3D-B2FF-FC1B4A06CB99 -indexedColumnList -.// .collection(Table.Columns), filtered on geometry and geography data types -.foreach_item (Table.Columns) -.if(%.L:DataType%==geometry) -%ObjectID% -.elsif(%.L:DataType%==geography) -%ObjectID% -.endif -.next("\n") -1299674429 -Marc -1299675262 -Marc -1 - - - - -CriterionTargetItem -54DEDFB5-F6FB-4A09-B2AF-4ECE7478433F -Criteria -1299597576 -Marc -1299597612 -Marc -1 - - -A8DE42C1-E6FE-405A-BEA2-32B81E50D2D6 -Geometry -%TesselationType%==geometry_grid -1299593413 -Marc -1299597612 -Marc -1 - - - - - -ExtendedAttributeTargetItem -05331FDC-5BFD-4331-99C2-280314F9E82E -Extended Attributes -1299593413 -Marc -1299595242 -Marc -1 - - -48F43844-64B2-435D-A3CC-F7EB11B00E6E -BoundingBoxDefn -1299593413 -Marc -1303202537 -Marc -Specifies a numeric four-tuple that defines the four coordinates of the bounding box: the x-min and y-min coordinates of the lower, left corner, and the x-max and y-max coordinates of the upper right corner. -1 -12 -1 -Function %Get%(obj) - - dim attr, defn, bnul - - bnul = true - defn = "" - attr = obj.GetExtendedAttributeText("%CurrentTargetCode%.BBoxXMin") - if (attr <> "") then - defn = defn & ", xmin = " & attr - bnul = false - else - defn = defn & ", xmin = 0" - end if - attr = obj.GetExtendedAttributeText("%CurrentTargetCode%.BBoxXMax") - if (attr <> "") then - defn = defn & ", xmax = " & attr - bnul = false - else - defn = defn & ", xmax = 0" ' invalid... But what els - end if - attr = obj.GetExtendedAttributeText("%CurrentTargetCode%.BBoxYMin") - if (attr <> "") then - defn = defn & ", ymin = " & attr - bnul = false - else - defn = defn & ", ymin = 0" - end if - attr = obj.GetExtendedAttributeText("%CurrentTargetCode%.BBoxYMax") - if (attr <> "") then - defn = defn & ", ymax = " & attr - bnul = false - else - defn = defn & ", ymax = 0" - end if - - if (bnul) then - %Get% = "" - else - %Get% = "( " & Mid(defn, 3) & " )" - end if -End Function -Sub %Set%(obj, value) - - dim str, arr - str = replace(replace(value, ")", ""), "(", "") ' remove parenthesis - if (InStr(str, "=") > 0) then - for each str in split(str, ",") - arr = split(str, "=") - Select case(LCase(Trim(arr(0)))) - case "xmin" : obj.SetExtendedAttributeText "%CurrentTargetCode%.BBoxXMin", Trim(arr(1)) - case "xmax" : obj.SetExtendedAttributeText "%CurrentTargetCode%.BBoxXMax", Trim(arr(1)) - case "ymin" : obj.SetExtendedAttributeText "%CurrentTargetCode%.BBoxYMin", Trim(arr(1)) - case "ymax" : obj.SetExtendedAttributeText "%CurrentTargetCode%.BBoxYMax", Trim(arr(1)) - End Select - next - else - arr = split(str, ",") - if(UBound(arr) <> 3) then - output "error in BBox definition : There must be exactly 4 values" - else - obj.SetExtendedAttributeText "%CurrentTargetCode%.BBoxXMin", Trim(arr(0)) - obj.SetExtendedAttributeText "%CurrentTargetCode%.BBoxXMax", Trim(arr(1)) - obj.SetExtendedAttributeText "%CurrentTargetCode%.BBoxYMin", Trim(arr(2)) - obj.SetExtendedAttributeText "%CurrentTargetCode%.BBoxYMax", Trim(arr(3)) - end if - end if - -End Sub -Bounding box definition -0 - - - - - -02EDBD3A-A41A-4BBB-9EFD-ACEF6587AC75 -BBoxXMax -1299593413 -Marc -1299595242 -Marc -Specifies the property name and value for the x-coordinate of the upper-right corner of the bounding box. -1 -10 -X Max -0 - - -90249543-C1C3-4102-8C78-3787CC3EBAE8 -BBoxXMin -1299593413 -Marc -1299595242 -Marc -Specifies the property name and value for the x-coordinate of the lower-left corner of the bounding box. -1 -10 -X Min -0 - - -5B9441D8-797A-417B-B086-24508653F60F -BBoxYMax -1299593413 -Marc -1299595242 -Marc -Specifies the property name and value for the y-coordinate of upper-right corner of the bounding box. -1 -10 -Y Max -0 - - -F665EE08-B8F0-4A3F-90D0-D6D1076D5CAB -BBoxYMin -1299593413 -Marc -1299595242 -Marc -Specifies the property name and value for the y-coordinate of the lower-left corner of the bounding box. -1 -10 -Y Min -0 - - - - -MethodTargetItem -E530D05B-C203-40D6-BAF4-22E41698BD74 -Methods -1299593413 -Marc -1299595242 -Marc -1 - - -AAFD7470-63CD-45F2-8B8A-78A35C461C09 -BoundingBoxEdit -1299593413 -Marc -1299595242 -Marc -1 -Sub %Method%(obj) - - obj.CreateCustomDialog("%CurrentTargetCode%.BoundingBoxDialog").ShowDialog() - -End Sub - - - - -FormTargetItem -1A212A9F-4714-47AB-A605-CC945338D471 -Forms -1299593413 -Marc -1299595242 -Marc -1 - - -C80E6F11-0960-41D3-8659-83C9EA1CCCF1 -BoundingBoxDialog -<Form > - <HorizontalLayout Name="HorizontalLayout1" > - <ExtendedAttribute Attribute="BBoxXMin" Caption="X Min" AttributeID="{90249543-C1C3-4102-8C78-3787CC3EBAE8}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="BBoxXMax" Caption="X Max" AttributeID="{02EDBD3A-A41A-4BBB-9EFD-ACEF6587AC75}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - </HorizontalLayout> - <Space Name="Space1" Width="6" Height="6" HorizontalResize="No" VerticalResize="No" /> - <HorizontalLayout Name="HorizontalLayout2" > - <ExtendedAttribute Attribute="BBoxYMin" Caption="Y Min" AttributeID="{F665EE08-B8F0-4A3F-90D0-D6D1076D5CAB}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="BBoxYMax" Caption="Y Max" AttributeID="{5B9441D8-797A-417B-B086-24508653F60F}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - </HorizontalLayout> -</Form> - -1299593413 -Marc -1299595242 -Marc -1 -Bounding box -Dialog - - - - - - - - - - - - -TemplateTargetItem -7C9F1FD9-5DD1-42EC-9966-52051B5467C3 -Templates -0 - -0 - -1 - - -3DA461D0-CBEF-483C-AB86-730A3ABC919F -isFullText -.bool(%Type% == FULLTEXT) -0 - -0 - -Returns if an index is a full text index -1 - - - - -FormTargetItem -2394D4B9-B93B-45D6-8B17-FAB34C95437E -Forms -1299597615 -Marc -1299597730 -Marc -1 - - -1F9214C4-E78F-40B3-AC7B-CA7EF5CCDE14 -Hidden -<Form > - <ExtendedAttribute Attribute="AllowPageLocks" Caption="Hidden" AttributeID="{265DC722-8E5C-4550-AC3F-ED502D54DC1D}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - - <ExtendedAttribute Attribute="AllowRowLocks" Caption="Hidden" AttributeID="{A858A7A8-0184-40C6-9102-7D6F0B22407C}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - - <ExtendedAttribute Attribute="BBoxXMax" Caption="X Max" AttributeID="{02EDBD3A-A41A-4BBB-9EFD-ACEF6587AC75}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="BBoxXMin" Caption="X Min" AttributeID="{90249543-C1C3-4102-8C78-3787CC3EBAE8}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="BBoxYMax" Caption="Y Max" AttributeID="{5B9441D8-797A-417B-B086-24508653F60F}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="BBoxYMin" Caption="Y Min" AttributeID="{F665EE08-B8F0-4A3F-90D0-D6D1076D5CAB}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="BoundingBoxDefn" Caption="Bounding box definition" AttributeID="{48F43844-64B2-435D-A3CC-F7EB11B00E6E}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="CellsPerObject" Caption="Cells per object" AttributeID="{D633CF5C-47BE-44CF-9B34-03B7F0CDD280}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="ChangeTracking" Caption="Hidden" AttributeID="{30B79F87-9E9D-4B9D-A849-BC4E651DDBB3}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="DropExisting" Caption="Hidden" AttributeID="{7B83F55C-3ED5-4C6D-A483-14A88519354C}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - - <ExtendedAttribute Attribute="FileGroup" Caption="Hidden" AttributeID="{02E101A1-E371-4C70-A731-30FB0D454389}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="FillFactor" Caption="Hidden" AttributeID="{3A273717-E593-4F43-906D-EF077FEA046F}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - - <ExtendedAttribute Attribute="FullTextCatalog" Caption="Hidden" AttributeID="{E130124A-DF17-456F-A780-106F47EF59E1}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="GridsDefn" Caption="Grids" AttributeID="{4E11A296-E662-4BA7-BFCB-F05E0615D50E}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="IgnoreDupKey" Caption="Hidden" AttributeID="{59B75B03-38B4-4B44-890A-20BFE728CED4}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="IncludedColumns" Caption="Hidden" AttributeID="{49285D91-ABC7-4051-988A-FA0348157A84}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" /> - <ExtendedAttribute Attribute="IndexedColumn" Caption="Indexed column" AttributeID="{48F3352B-5139-446D-AE8F-5F3F0D70B0A3}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="Level1Density" Caption="Level 1" AttributeID="{4D5A231C-DF71-4DAF-9630-C1C0AF5DE5B7}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="Level2Density" Caption="Level 2" AttributeID="{041EDB6F-DE6C-4EA3-A349-DEF74E1A3C58}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="Level3Density" Caption="Level 3" AttributeID="{A9C3E9B2-C64C-456B-93FE-AC8B73882111}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="Level4Density" Caption="Level 4" AttributeID="{F84611AA-C8F1-4BB2-86A2-5E58A9A6067D}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="MaxDop" Caption="Hidden" AttributeID="{8619B81B-01D5-4A03-BE35-E18CECB684A5}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - - <ExtendedAttribute Attribute="Online" Caption="Hidden" AttributeID="{5708B3EE-694F-4037-91ED-57CC8F6F2D9A}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="PadIndex" Caption="Hidden" AttributeID="{5EAD2467-9E98-48A0-94FC-D1874605876E}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - - <ExtendedAttribute Attribute="PartitionScheme" Caption="Hidden" AttributeID="{7A7F0817-CFC2-49B5-93AC-B7B079EA3560}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="PartitionSchemeColumn" Caption="Hidden" AttributeID="{A433BE24-5AA2-4ECD-93C0-A9643589A155}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="PrimaryXMLIndex" Caption="Hidden" AttributeID="{D22BD232-16D8-449A-9B51-3AD790203842}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="SecondaryXMLIndexType" Caption="Hidden" AttributeID="{16EB6B6B-0343-4386-A0F2-C27070424242}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - - <ExtendedAttribute Attribute="SortInTempDB" Caption="Hidden" AttributeID="{5BBD7037-A3C3-40FB-AF53-E60CCB2D1F3A}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="SpatialIndexDefn" Caption="Definition of spatial index" AttributeID="{4CEEF976-E73C-4553-BB0E-ECF63E8393EC}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" /> - <ExtendedAttribute Attribute="StatisticsNoRecompute" Caption="Hidden" AttributeID="{36087631-0279-40A6-A421-E3D7CB0DC376}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - - <ExtendedAttribute Attribute="Table" Caption="Table" AttributeID="{967FD69E-8699-444F-9169-5DDFA2C18628}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="TesselationType" Caption="Tessellation scheme" AttributeID="{84B71BA8-5410-4C5F-B456-F3697013007E}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="XMLPrimary" Caption="Hidden" AttributeID="{6EF9AF42-2538-4ACD-82BC-4C2CB1793B06}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> -</Form> - -1299597615 -Marc -1303198737 -Marc -1 -Dialog - - - - -ExtendedAttributeTargetItem -B9E31B73-3C75-4C4C-816D-86D8346A5EC2 -Extended Attributes -1299602459 -Marc -1299602506 -Marc -1 - - -4CEEF976-E73C-4553-BB0E-ECF63E8393EC -SpatialIndexDefn -1299593346 -Marc -1303198223 -Marc -Definition of spatial index -1 -14 -1 -Function %Get%(obj) - - if (obj.Type <> "SPATIAL") then Exit Function - - dim nl - nl = "," & vbCrLf & " " - - dim coln, defn, wopt, attr - Set coln = obj.GetExtendedAttribute("%CurrentTargetCode%.IndexedColumn") - if(coln is Nothing) then - defn = "" - else - 'defn = "on " & coln.Table.QualifiedCode & " ( " & coln.Code & " ) " - defn = " ( " & coln.Code & " ) " - end if - wopt = "" - attr = LCase(obj.GetExtendedAttributeText("%CurrentTargetCode%.TesselationType")) - defn = defn & vbCrLf & " using " & attr - if (attr = "geometry_grid") then - attr = obj.GetExtendedAttributeText("%CurrentTargetCode%.BoundingBoxDefn") - if (attr <> "") then - wopt = wopt & nl & "bounding_box = " & attr ' which includes the parenthesis already - end if - end if - attr = obj.GetExtendedAttributeText("%CurrentTargetCode%.CellsPerObject") - if (attr <> "") then - wopt = wopt & nl & "cells_per_object = " & attr - else - attr = obj.GetExtendedAttributeText("%CurrentTargetCode%.GridsDefn") - if (attr <> "") then - wopt = wopt & nl & "grids = " & attr - end if - end if - - if(obj.GetExtendedAttribute("%CurrentTargetCode%.PadIndex")) then - wopt = wopt & nl & "pad_index = on" - end if - - attr = obj.GetExtendedAttributeText("%CurrentTargetCode%.FillFactor") - if(attr <> "") then - wopt = wopt & nl & "fillfactor = " & attr - end if - - if(obj.GetExtendedAttribute("%CurrentTargetCode%.SortInTempDB")) then - wopt = wopt & nl & "sort_in_temp = on" - end if - - if(obj.GetExtendedAttribute("%CurrentTargetCode%.StatisticsNoRecompute")) then - wopt = wopt & nl & "statistics_norecompute = on" - end if - - if(obj.GetExtendedAttribute("%CurrentTargetCode%.DropExisting")) then - wopt = wopt & nl & "drop_existing = on" - end if - - if(obj.GetExtendedAttribute("%CurrentTargetCode%.AllowRowLocks")) then - wopt = wopt & nl & "allow_row_locks = on" - else - wopt = wopt & nl & "allow_row_locks = off" - end if - - if (obj.GetExtendedAttribute("%CurrentTargetCode%.AllowPageLocks")) then - wopt = wopt & nl & "allow_page_locks = on" - else - wopt = wopt & nl & "allow_page_locks = off" - end if - - attr = obj.GetExtendedAttributeText("%CurrentTargetCode%.MaxDoP") - if(attr <> "") then - wopt = wopt & nl & "maxdop = " & attr - end if - - if(wopt <> "") then - defn = defn & vbCrLf & " with ( " & vbCrLf & " " & Mid(wopt, Len(nl) + 1) & vbCrLf & " )" - end if - - %Get% = defn - -End Function -Sub %Set%(obj, value) - - if (obj.Type <> "SPATIAL") then Exit Sub - - dim rxp, mch, str, arr, col - set rxp = new regexp - rxp.global = true - rxp.IgnoreCase = true - str = replace(replace(replace(value, vbTab, " "), vbLf, " "), vbCr, " ") - rxp.Pattern = "" - rxp.Pattern = rxp.Pattern & "(?:\(\s*([^\(^\)]+)\s*\)" - rxp.Pattern = rxp.Pattern & "|using\s+(geometry_grid|geography_grid)" - rxp.Pattern = rxp.Pattern & "|(\w+)\s*=\s*(\w+|\([^\(^\)]+\))" - rxp.Pattern = rxp.Pattern & "|on\s+(\w+))" - - for each mch in rxp.Execute(str) - arr = split(rxp.Replace(mch, "$1~$2~$3~$4~$5"), "~") - if(arr(0) <> "") then ' on table (column) - set col = obj.Table.FindChildByCode(arr(0), PDPDM.cls_Column, "", Nothing, False, True) - if(col is Nothing) then - output "Cannot find column " & arr(0) - else - obj.SetExtendedAttribute "%CurrentTargetCode%.IndexedColumn", col - end if -' elseif(arr(1) <> "") then ' using... is read-only, based on column dttp... -' if not(IsObject(col)) then ' Not yet initialized ? -' set col = obj.GetExtendedAttribute("%CurrentTargetCode%.IndexedColumn") -' end if -' if not (col is Nothing) then -' col.Datatype = Replace(LCase(arr(1)), "_grid", "") -' end if - elseif(arr(4) <> "") then - output "set filegroup = " & LCase(arr(4)) - else - select case(LCase(arr(2))) - case "bounding_box": - obj.SetExtendedAttribute "%CurrentTargetCode%.BoundingBoxDefn", arr(3) - - case "grids" : - obj.SetExtendedAttribute "%CurrentTargetCode%.GridsDefn", arr(3) - - case "cells_per_object" : - obj.SetExtendedAttribute "%CurrentTargetCode%.CellsPerObject", arr(3) - - case "pad_index" : - obj.SetExtendedAttribute "%CurrentTargetCode%.PadIndex", (LCase(arr(3)) = "on") - - case "fillfactor" : output "fillfactor=" & arr(3) - obj.SetExtendedAttribute "%CurrentTargetCode%.FillFactor", arr(3) - - case "sort_in_tempdb" : - obj.SetExtendedAttribute "%CurrentTargetCode%.SortInTempdb", (LCase(arr(3)) = "on") - - case "ignore_dup_key" : output "ignored option 'ignore_dup_key' = " & arr(3) - case "statistics_norecompute" : - obj.SetExtendedAttribute "%CurrentTargetCode%.StatisticsNoRecompute", (LCase(arr(3)) = "on") - - case "drop_existing" : output "drop_existing=" & arr(3) - obj.SetExtendedAttribute "%CurrentTargetCode%.DropExisting", (LCase(arr(3)) = "on") - - case "online" : output "ignored option 'online' = " & arr(3) - case "allow_row_locks" : - obj.SetExtendedAttribute "%CurrentTargetCode%.AllowRowLocks", (LCase(arr(3)) = "on") - - case "allow_page_locks" : output "allow_page_locks=" & arr(3) - obj.SetExtendedAttribute "%CurrentTargetCode%.AllowPageLocks", (LCase(arr(3)) = "on") - - case "maxdop" : output "maxdop=" & arr(3) - obj.SetExtendedAttribute "%CurrentTargetCode%.MaxDoP", arr(3) - - case else output "unknow option: " & arr(2) & "=" & arr(3) - end select - end if - next - -End Sub -Definition of spatial index -0 - - - - - - -BDBC0705-322F-4EBF-8027-AA721C792761 -IndexColumn -0 - -0 - -1 - - -CriterionTargetItem -BFFAC80A-2CAE-4C9E-B282-119E889CF364 -Criteria -0 - -0 - -1 - - -CE3B563A-3768-476D-AC65-CE2A6F59F0B3 -IsFullTextIndexColumn -%isFullText% -0 - -0 - -1 - - -ExtendedAttributeTargetItem -884E2FA3-E324-44F8-9AD2-452C101ACC56 -Extended Attributes -0 - -0 - -1 - - -97E10764-D2BC-4A7F-947F-02D15C08E618 -TypeColumn -0 - -0 - -Data type of the column -1 -15 -%typeColumnList% -380BAA41-0042-11D2-BD28-00A02478ECC9 -0 - - - - - -CDA23757-DFBE-4F3C-B48B-56D2B28A18F8 -Language -0 - -0 - -Is the language of the data stored in the column -1 -12 -0 - - - - -ExtendedCollectionTargetItem -B8A2E56C-B4E1-452B-B97D-92795C30F1BE -Extended Collections -0 - -0 - -1 - - -904D4D29-3995-467B-BACE-368A76FC0983 -TypeColumn -0 - -0 - -1 -TypeOfFullIndexColumns -380BAA41-0042-11D2-BD28-00A02478ECC9 - - - - -TemplateTargetItem -344F3AA5-9948-4E53-BC46-B31B08C12CD4 -Templates -0 - -0 - -1 - - -FA543CA8-428F-4DB8-920F-877AEF3CC405 -typeColumnList -list -.foreach_item(Index.Table.Columns) -%ObjectID% \t %DisplayName% -.next(\n) -0 - -0 - -Enumerates columns -1 - - - - - - - - -TemplateTargetItem -543C2930-8222-4E63-A56A-72A53C0C2470 -Templates -0 - -0 - -1 - - -426E1581-F81C-4378-BC41-B667D455BDFF -isFullText -%Index.isFullText% -0 - -0 - -1 - - - - - - -C3C6961B-5A88-4502-9123-C3FF96600C80 -Key -0 - -1328015623 -mledier -1 - - -EventHandlerTargetItem -62650D99-6F4A-4E28-B2F1-3D5515D0B1E3 -Event Handlers -1327652821 -mledier -1327671821 -mledier -1 - - -3704B472-CC6E-4146-B448-0D48C72AA402 -Validate -1327652821 -mledier -1327671821 -mledier -1 -Function %Validate%(obj, ByRef message) - - Dim sFileGroup - Dim sPartitionScheme - Dim bValidate - - bValidate = True - - sFileGroup = obj.GetExtendedAttribute("FileGroup") - sPartitionScheme = obj.GetExtendedAttribute("PartitionScheme") - If ((sFileGroup <> "") And (sPartitionScheme <> "")) Then - message = "You cannot specify a file group and and a partition scheme simultaneously!" - bValidate = False - Else - bValidate = True - End If - - If (bValidate = True) Then - - - Dim depd, depdObj - - ' Compute new dependent object list - Dim DependentObjects - set DependentObjects = CreateObject("Scripting.Dictionary") - - If (sPartitionScheme <> "") Then - Dim pExtendedObject - For each pExtendedObject in obj.Package.ExtendedObjects - If (pExtendedObject.Stereotype = "PartitionScheme") Then - Dim sGeneratedName - sGeneratedName = pExtendedObject.EvaluateText("%PARTITIONSCHEME%") - If (sGeneratedName = sPartitionScheme) Then - DependentObjects.Add pExtendedObject.ObjectID, pExtendedObject - End If - End If - Next - End If - - ' Remove old dependencies - Dim DependenciesToDelete - set DependenciesToDelete = CreateObject("Scripting.Dictionary") - - For each depd in obj.ExtendedDependencies - If (depd.Stereotype = "DBCreateAfter (Computed)") Then - If (DependentObjects.Exists(depd.InfluentObject.ObjectID)) Then - DependentObjects.Remove depd.InfluentObject.ObjectID - Else - DependenciesToDelete.Add depd.ObjectID, depd - End If - End If - Next - - Dim i, col - col = DependenciesToDelete.Items - For i = 0 To DependenciesToDelete.Count -1 'Iterate the array - set depd = col(i) - if not (depd is nothing) then depd.Delete - Next - - - ' Add new dependencies - col = DependentObjects.Items - For i = 0 To DependentObjects.Count -1 'Iterate the array - set depdObj = col(i) - set depd = obj.Package.ChildExtendedDependencies.CreateNew - If not (depd is nothing) Then - set depd.DependentObject = obj - set depd.InfluentObject = depdObj - depd.Stereotype = "DBCreateAfter (Computed)" - End If - Next - - End If - - - %Validate% = bValidate - -End Function - - - - -ExtendedAttributeTargetItem -74BB93E7-DCCC-4E1D-A3AE-A60AD71D0934 -Extended Attributes -0 - -1328015623 -mledier -1 - - -7A74E583-9AC9-488B-852E-A018A6474BDF -AllowRowLocks -1327657706 -mledier -1327671821 -mledier -When ON, row locks are allowed when you access the index. The Database Engine determines when row locks are used. When OFF, row locks are not used. -1 -1 -Function %Get%(obj) - 'default is on. Check "off" value - %Get% = (LCase(obj.GetPhysicalOptionValue("with/allow_row_locks=")) <> "off") - -End Function -Sub %Set%(obj, value) - ' default is on - if(value) then - obj.DeletePhysicalOption "with/allow_row_locks=" - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - else - obj.SetPhysicalOptionValue "with/allow_row_locks=", "off" - end if -End Sub -0 - - -0B419D8C-FFDC-4D69-9623-567742CC793F -FileGroup -1327652709 -mledier -1327671821 -mledier -Name of the filegroup -1 -12 -%fileGroupList% -1 -Function %Get%(obj) - %Get% = obj.GetPhysicalOptionValue("on/<filegroup>") -End Function -Sub %Set%(obj, value) - obj.SetPhysicalOptionValue "on/<filegroup>", value - if (value <> "") then - obj.DeletePhysicalOption "on/<partition scheme>" - end if -End Sub -0 - - -A53FF58E-EF64-40DD-B02A-1CE837B48F90 -IgnoreDupKey -1327657771 -mledier -1327671821 -mledier -Specifies the error response to duplicate key values in a multiple-row INSERT transaction on a unique clustered or unique nonclustered index. When ON and a row violates the unique index, a warning message is issued and only the rows violating the UNIQUE index fail. -1 -1 -Function %Get%(obj) - ' Default is off, check "on" value - %Get% = (LCase(obj.GetPhysicalOptionValue("with/ignore_dup_key=")) = "on") - -End Function -Sub %Set%(obj, value) - ' default is off - if(value) then - obj.SetPhysicalOptionValue "with/ignore_dup_key=", "on" - else - obj.DeletePhysicalOption "with/ignore_dup_key=" - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - end if -End Sub -0 - - -91C3095A-0D75-4172-8339-532179FB56E1 -Online -1327657771 -mledier -1327674734 -mledier -Specifies whether underlying tables and associated indexes are available for queries and data modification during the index operation. -1 -1 -Function %Get%(obj) - ' Default is off, check "on" value - %Get% = (LCase(obj.GetPhysicalOptionValue("with/online=")) = "on") -End Function -Sub %Set%(obj, value) - ' default is off - if(value) then - obj.SetPhysicalOptionValue "with/online=", "on" - else - obj.DeletePhysicalOption "with/online=" - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - end if -End Sub -0 - - -F73CA777-EA37-49A9-A7A1-35556FB5A931 -PartitionScheme -1327652437 -mledier -1327671821 -mledier -Name of the partition scheme -1 -12 -%partitionSchemeList% -1 -Function %Get%(obj) - %Get% = obj.GetPhysicalOptionValue("on/<partition scheme>") -End Function -Sub %Set%(obj, value) - If ((value <> "") and (obj.HasPhysicalOption("on/<partition scheme>/<column>") = false)) Then - obj.SetPhysicalOptionValue "on/<partition scheme>/<column>", "?" - End If - obj.SetPhysicalOptionValue "on/<partition scheme>", value - if (value <> "") then - obj.DeletePhysicalOption "on/<filegroup>" - end if -End Sub -0 - - -8673D96D-1277-40E0-8EF8-EFBA21C65700 -PartitionSchemeColumn -1327652437 -mledier -1327671821 -mledier -Partitioned column -1 -12 -%partitionSchemeColumnList% -1 -Function %Get%(obj) - %Get% = obj.GetPhysicalOptionValue("on/<partition scheme>/<column>") -End Function -Sub %Set%(obj, value) - obj.SetPhysicalOptionValue "on/<partition scheme>/<column>", value -End Sub -0 - - -1A76EC03-3AFC-43CA-B42E-7A2E96D597C2 -StatisticsNorecompute -1327657771 -mledier -1327671821 -mledier -When ON, out-of-date index statistics are not automatically recomputed. When OFF, automatic statistics updating are enabled. -1 -1 -Function %Get%(obj) - ' Default is off, check "on" value - %Get% = (LCase(obj.GetPhysicalOptionValue("with/statistics_norecompute=")) = "on") - -End Function -Sub %Set%(obj, value) - ' default is off - if(value) then - obj.SetPhysicalOptionValue "with/statistics_norecompute=", "on" - else - obj.DeletePhysicalOption "with/statistics_norecompute=" - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - end if -End Sub -0 - - -0C3B00E4-AAE3-4758-827D-2946C12A957D -AllowPageLocks -1327657414 -mledier -1327674734 -mledier -When ON, page locks are allowed when you access the index. The Database Engine determines when page locks are used. When OFF, page locks are not used. -1 -1 -Function %Get%(obj) - 'default is on. Check "off" value - %Get% = (LCase(obj.GetPhysicalOptionValue("with/allow_page_locks=")) <> "off") -End Function -Sub %Set%(obj, value) - ' default is on - if(value) then - obj.DeletePhysicalOption "with/allow_page_locks=" - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - else - obj.SetPhysicalOptionValue "with/allow_page_locks=", "off" - end if -End Sub -0 - - -3F0BC6CA-0C76-47AB-923B-6F82633AE328 -PadIndex -1327657771 -mledier -1327674734 -mledier -When ON, the percentage of free space specified by FILLFACTOR is applied to the intermediate level pages of the index. When OFF or a FILLFACTOR value it not specified, the intermediate level pages are filled to near capacity leaving enough space for at least one row of the maximum size the index can have, considering the set of keys on the intermediate pages. -1 -1 -Function %Get%(obj) - ' Default is off, check "on" value - %Get% = (LCase(obj.GetPhysicalOptionValue("with/pad_index=")) = "on") -End Function -Sub %Set%(obj, value) - ' default is off - if(value) then - obj.SetPhysicalOptionValue "with/pad_index=", "on" - else - obj.DeletePhysicalOption "with/pad_index=" - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - end if -End Sub -0 - - -5073A619-5D1F-4F5A-B392-9BA0F030E704 -SortInTempDB -1327657771 -mledier -1327674734 -mledier -Specifies whether to store sort results in tempdb. -1 -1 -Function %Get%(obj) - ' Default is off, check "on" value - %Get% = (LCase(obj.GetPhysicalOptionValue("with/sort_in_tempdb=")) = "on") -End Function -Sub %Set%(obj, value) - ' default is off - if(value) then - obj.SetPhysicalOptionValue "with/sort_in_tempdb=", "on" - else - obj.DeletePhysicalOption "with/sort_in_tempdb=" - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - end if -End Sub -0 - - -DB489B76-6A4D-4024-943D-60A8C15DF2C4 -FillFactor -1327673589 -mledier -1327675043 -mledier -Specifies how full SQL Server should make each index page used to store the index data. -1 -12 -%fillFactorList% -1 -Function %Get%(obj) - dim s - s = obj.GetPhysicalOptionValue("with/fillfactor=") - if(s = "") then - s = obj.GetPhysicalOptionValue("with fillfactor=") - end if - %Get% = s -End Function -Sub %Set%(obj, value) - ' By default, set the one in parenthesis - obj.DeletePhysicalOption "with fillfactor=" - if(value <> "") then - obj.SetPhysicalOptionValue "with/fillfactor=", value - else - obj.DeletePhysicalOption "with/fillfactor=" - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - end if -End Sub -0 - - -D11C4644-4E4C-4904-AE3B-D2208ADC3F67 -MaxDOP -1327674734 -mledier -1327675328 -mledier -Overrides the max degree of parallelism configuration option for the duration of the index operation. For more information, see max degree of parallelism Option. Use MAXDOP to limit the number of processors used in a parallel plan execution. -1 -12 -1 -Function %Get%(obj) - - %Get% = obj.GetPhysicalOptionValue("with/maxdop=") - -End Function -Sub %Set%(obj, value) - if(value <> "") then - obj.SetPhysicalOptionValue "with/maxdop=", value - else - obj.DeletePhysicalOption "with/maxdop=" - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - end if -End Sub -0 - - -10AFF327-349C-4B32-8BE7-82269FDA57DC -WithCompression -1328005995 -mledier -1328865467 -mledier -Compression option -1 -14 -1 -Function %Get%(obj) - %Get% = obj.GetPhysicalOptionSql("with/<compression>/") -End Function -Sub %Set%(obj, value) - - dim rxp, mch, partlist, str - - partlist = array("", "") - set rxp = new regexp - rxp.global = true - rxp.IgnoreCase = true - str = replace(replace(replace(value, vbTab, " "), vbLf, " "), vbCr, " ") - - rxp.Pattern = "data_compression\s*=\s*(none|row|page)\s+on\s+partitions\s*\(([^)]+)\)" - - for each mch in rxp.Execute(str) - select case(LCase(rxp.Replace(mch, "$1"))) - case "row" : partlist(0) = partlist(0) & ", " & rxp.Replace(mch, "$2") - case "page" : partlist(1) = partlist(1) & ", " & rxp.Replace(mch, "$2") - end select - next - dim result - result = "" - if(partlist(0) <> "") then - result = result & ", data_compression = row on partitions (" & Mid(partlist(0), 3) & ")" - end if - if(partlist(1) <> "") then - result = result & ", data_compression = page on partitions (" & Mid(partlist(1), 3) & ")" - end if - if(result <> "") then - obj.SetPhysicalOptionSql "with/<compression>", mid(result, 3) - else - obj.ExecuteCustomMethod "%CurrentTargetCode%.CleanWithOption" - end if -End Sub -Compression -0 - - - - -TemplateTargetItem -07434897-CD0B-4B7F-8A22-E32C7C821408 -Templates -0 - -1327675043 -mledier -1 - - -7E59A15A-8EC6-4480-8F28-A89F0BF88AC8 -fileGroupList -List -DEFAULT\n -.foreach_item(Model.Storages) -%Code% -.next(\n) -0 - -0 - -Lists filegroups (Storages) of the model -1 - - -5301F2F0-A0EE-4A8F-B35B-908633CFE038 -fillFactorList -.vbscript -dim obj -for each obj in ActiveSelection -scriptresult = obj.GetPhysicalOptionValues("with/fillfactor=") -next -.endvbscript -0 - -0 - -List of values for the fill factor -1 - - -4444D738-46B6-4B02-8FD0-A41575699D82 -partitionSchemeColumnList -List -.foreach_item(Columns) -%ShortGeneratedName% -.next(\n) -1327652437 -mledier -1327671821 -mledier -1 - - -026C1977-CE13-4834-8AB3-70514B55E372 -partitionSchemeList -List -.foreach_item(Package.ExtendedObjects,,,%Stereotype%==PartitionScheme) -%PARTITIONSCHEME% -.next(\n) -1327652437 -mledier -1327671821 -mledier -1 - - -A65F9144-D70B-49CC-8BF1-02C610EA8332 -sortedDataList -.vbscript -dim obj -for each obj in ActiveSelection -scriptresult = obj.GetPhysicalOptionValues("with/<sorted_data>") -next -.endvbscript -0 - -0 - -1 - - -22A3750E-0447-4267-9633-4840D90421F9 -dataCompressionList -.vbscript -dim obj -for each obj in ActiveSelection -scriptresult = obj.GetPhysicalOptionValues("with/data_compression=") -next -.endvbscript -1327674734 -mledier -1327675043 -mledier -1 - - - - -MethodTargetItem -FE602BC0-E7E0-4FDC-8BF7-AA0F50A94ECC -Methods -1327658775 -mledier -1327671821 -mledier -1 - - -6265D305-0A65-410E-8A66-8E2E5C39C304 -CleanWithOption -1327658775 -mledier -1327671821 -mledier -1 -Sub %Method%(obj) - - Dim s - s = obj.GetPhysicalOptionSql("with") - if ((s = "with") or (s = "")) then - obj.DeletePhysicalOption("with") - end if - -End Sub - - - - -FormTargetItem -0B8580C0-E600-4010-B62F-084F250C13AC -Forms -1328015283 -mledier -1328015623 -mledier -1 - - -1B09536A-CD35-4A9E-BCBE-156C69EA6125 -Microsoft -<Form > - <ExtendedAttribute Attribute="FileGroup" Caption="Filegroup:" AttributeID="{0B419D8C-FFDC-4D69-9623-567742CC793F}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - <HorizontalLayout Name="PartitionSchemeLayout" > - <ExtendedAttribute Attribute="PartitionScheme" Caption="Partition scheme:" AttributeID="{F73CA777-EA37-49A9-A7A1-35556FB5A931}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="PartitionSchemeColumn" Caption="Column:" AttributeID="{8673D96D-1277-40E0-8EF8-EFBA21C65700}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - </HorizontalLayout> - <Separator Name="Separator1" /> - <HorizontalLayout Name="ColumnLayout" > - <Space Name="Space1" Width="80" Height="6" HorizontalResize="No" VerticalResize="No" /> - <VerticalLayout Name="RowLayout1" > - <ExtendedAttribute Attribute="AllowPageLocks" Caption="Allow page lock" AttributeID="{0C3B00E4-AAE3-4758-827D-2946C12A957D}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="AllowRowLocks" Caption="Allow row lock" AttributeID="{7A74E583-9AC9-488B-852E-A018A6474BDF}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="SortInTempDB" Caption="Sort in tempdb" AttributeID="{5073A619-5D1F-4F5A-B392-9BA0F030E704}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - </VerticalLayout> - <VerticalLayout Name="RowLayout2" > - <ExtendedAttribute Attribute="IgnoreDupKey" Caption="Ignore dup key" AttributeID="{A53FF58E-EF64-40DD-B02A-1CE837B48F90}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="PadIndex" Caption="Index padding" AttributeID="{3F0BC6CA-0C76-47AB-923B-6F82633AE328}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="StatisticsNorecompute" Caption="Statistics no recompute" AttributeID="{1A76EC03-3AFC-43CA-B42E-7A2E96D597C2}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - </VerticalLayout> - <VerticalLayout Name="VerticalLayout1" > - <ExtendedAttribute Attribute="Online" Caption="Online" AttributeID="{91C3095A-0D75-4172-8339-532179FB56E1}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - </VerticalLayout> - </HorizontalLayout> - <HorizontalLayout Name="HorizontalLayout1" > - <ExtendedAttribute Attribute="FillFactor" Caption="Fill factor" AttributeID="{DB489B76-6A4D-4024-943D-60A8C15DF2C4}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="MaxDOP" Caption="Max degree of parallelism" AttributeID="{D11C4644-4E4C-4904-AE3B-D2208ADC3F67}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - </HorizontalLayout> - <ExtendedAttribute Attribute="IncludedColumns" Caption="Include" AttributeID="{49285D91-ABC7-4051-988A-FA0348157A84}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="1" HorizontalResize="Yes" VerticalResize="No" LeftText="No" /> - <Separator Name="Separator" /> - <ExtendedAttribute Attribute="WithCompression" Caption="Compression" AttributeID="{10AFF327-349C-4B32-8BE7-82269FDA57DC}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" /> -</Form> - -1328015283 -mledier -1328015623 -mledier -Specific page for Microsoft SQL Server -1 -24917 - - - - - - -468F7FBB-974E-480A-ADD4-F996883B77C1 -Database -0 - -0 - -1 - - -CriterionTargetItem -3017B6ED-850B-446A-80EE-4C7E6CEC571C -Criteria -0 - -0 - -1 - - -E40FFE85-8FB7-410B-9534-998B83902A55 -Mirroring -%EnableMirroring% -0 - -0 - -Controls database mirroring for a database. -Values specified with these options apply both to copies of the database and to the database mirroring session as a whole. - -1 - - -ExtendedAttributeTargetItem -325DEAB7-C332-4E5E-856C-1897B77EB8A8 -Extended Attributes -0 - -0 - -1 - - -37A3A005-E700-4FE6-B6F6-3DC23438D686 -Partner -0 - -0 - -When True, controls the database properties that define the failover partners of a database mirroring session and their behavior. -When False, controls the database properties that define a database mirroring witness. -1 -0 - - -DFD9304D-FE45-4589-96B3-2F2C58557247 -MirrorOptions -0 - -0 - -Controls mirroring session -1 -12 -%optionsList% -1 -0 - - -7EAFA431-0D79-46A2-9D5F-6910E9C22240 -Witness -0 - -0 - -Controls the database properties that define a database mirroring witness. A SET WITNESS clause affects both copies of the database, but can only be specified on the principal server. If a witness is set for a session, a quorum is required to serve the database, regardless of the SAFETY setting. -1 -1 -Function %Get%(obj) - %Get% = Not(CBool(obj.GetExtendedAttribute("Partner"))) -End Function -Sub %Set%(obj, value) - ' Not(CBool(obj.GetExtendedAttribute("Partner") - obj.SetExtendedAttribute "Partner", Not(value) - -End Sub -0 - - - - -TemplateTargetItem -A376052F-1D02-4F41-A6CE-573BBD00D317 -Templates -0 - -0 - -1 - - -12D3BC32-DD02-459F-BD1D-AFDAD81C5C31 -optionsList -server -off\n -.if(%Partner%) -failover -force_service_allow_data_loss -resume -safety full -safety off -suspend -timeout -.endif() -0 - -0 - -1 - - - - -CriterionTargetItem -FA425439-5BB9-40CA-88DE-B91443E3B5E1 -Criteria -0 - -0 - -1 - - -8B92B3AA-C60B-4223-9F0A-16CE76DB6F94 -TimeoutMirror -%MirrorOptions% == timeout -0 - -0 - -Available items when select option has the specified value. -1 - - - - - -ExtendedAttributeTargetItem -CAC4D1D3-3D39-42B6-AB16-B9499FD94A49 -Extended Attributes -0 - -0 - -1 - - -64932A2E-B4A6-45D1-AF9C-BFD85815F167 -TimeOut -0 - -0 - -Specifies the time-out period in seconds. The time-out period is the maximum time that a server instance waits to receive a PING message from another instance in the mirroring session before considering that other instance to be disconnected. -1 -10 -0 - - - - - - -04C02E6A-B272-4741-A864-1E83F12F901D -ServerMirror -%MirrorOptions% == server -0 - -0 - -1 - - - - - -ExtendedAttributeTargetItem -E915C96A-B920-4090-882C-67B350354D10 -Extended Attributes -0 - -0 - -1 - - -EF6719F7-8340-41ED-9805-92D1C2AA10FB -MirrorServer -0 - -0 - -For partner mirroring, specifies the server network address of an instance of SQL Server to act as a failover partner in a new database mirroring session. -For witness mirroring, specifies an instance of the Database Engine to act as the witness server for a database mirroring session -1 -12 -0 - - - - - - - - - - - - -ExtendedAttributeTargetItem -FB182C23-6134-45DB-816C-F9AC02158389 -Extended Attributes -0 - -0 - -1 - - -9A095731-B906-4D61-91C3-E2069C83A258 -Primary -0 - -0 - -Specifies that the associated file specification list defines the primary file -1 -1 -Function %Get%(obj) - %Get% = obj.HasPhysicalOption("on/primary") -End Function -Sub %Set%(obj, value) - If (value = True) Then - obj.AddPhysicalOption "on/primary" - Else - obj.DeletePhysicalOption "on/primary" - End If -End Sub -0 - - -5D4BC1AF-4140-4CFB-93A1-CBB72F7993D3 -ForAttach -0 - -0 - -Specifies that a database is attached from an existing set of operating system files. -1 -1 -Function %Get%(obj) -%Get% = obj.HasPhysicalOption("for/attach") -End Function -Sub %Set%(obj, value) - If (value = True) Then - obj.AddPhysicalOption "for/attach" - Else - obj.DeletePhysicalOption "for/attach" - End If -End Sub -0 - - -FF5A6B3F-2569-40AA-A0E1-74373FCCE815 -EnableMirroring -0 - -0 - -Enables database mirroring for a database. See Mirroring criteria. -1 -false -0 - - -CEDEDF1A-2957-4441-86C1-F659F3141FA2 -FilelistFilegroup -0 - -0 - -Gets or sets the first filegroup name -1 -12 -1 -Function %Get%(obj) -%Get% = obj.GetPhysicalOptionValue("on/<filelist>/filegroup") -End Function -Sub %Set%(obj, value) -obj.SetPhysicalOptionValue "on/<filelist>/filegroup", value -End Sub -0 - - -2DA17CE1-5AE0-4C25-AF6C-C2B10ABD06A3 -FileGroupFileSpec -0 - -0 - -Gets or sets the Filegroup specification -1 -14 -1 -Function %Get%(obj) - %Get% = obj.GetPhysicalOptionSql("on/<filelist>/filegroup") -End Function -Sub %Set%(obj, value) - obj.SetPhysicalOptionSql "on/<filelist>/filegroup", value -End Sub -0 - - - - - -99F40AAD-9A0A-4428-9CFE-65A135905171 -FileListFileSpec -0 - -0 - -Gets or sets the file specification -1 -14 -1 -Function %Get%(obj) - %Get% = obj.GetPhysicalOptionSql("on/<filelist>/<filespec>") -End Function -Sub %Set%(obj, value) - obj.SetPhysicalOptionSql "on/<filelist>/<filespec>", value -End Sub -0 - - - - - -313B7F66-5565-479D-BD3C-CA01A15304C7 -LogOnFileSpec -0 - -0 - -Gets or sets the log file specification -1 -14 -1 -Function %Get%(obj) - %Get% = obj.GetPhysicalOptionSql("log on/<filespec>") -End Function -Sub %Set%(obj, value) - obj.SetPhysicalOptionSql "log on/<filespec>", value -End Sub -0 - - - - - -A2C7D232-3CF5-4823-BB40-27D6AAE9D83E -WithDbChaining -0 - -0 - -When ON is specified, the database can be the source or target of a cross database ownership chain. -When OFF, the database cannot participate in cross database ownership chaining. The default is OFF. - - -1 -1 -Function %Get%(obj) - Dim value - value = obj.GetPhysicalOptionValue("with/db_chaining") - value = UCase(value) - If (value = "ON") Then - %Get% = True - Else - %Get% = False - End If -End Function -Sub %Set%(obj, value) - If (value = True) Then - obj.SetPhysicalOptionValue "with/db_chaining", "ON" - Else - obj.DeletePhysicalOption "with/db_chaining" - End If -End Sub -0 - - -EF8631AC-7A60-4799-9A45-3B11A5531067 -WithTrustworthy -0 - -0 - -When ON is specified, database modules (for example, views, user-defined functions, or stored procedures) that use an impersonation context can access resources outside the database. -When OFF, database modules in an impersonation context cannot access resources outside the database. The default is OFF. - -1 -1 -Function %Get%(obj) - Dim value - value = obj.GetPhysicalOptionValue("with/trustworthy") - value = UCase(value) - If (value = "ON") Then - %Get% = True - Else - %Get% = False - End If -End Function -Sub %Set%(obj, value) - If (value = True) Then - obj.SetPhysicalOptionValue "with/trustworthy", "ON" - Else - obj.DeletePhysicalOption "with/trustworthy" - End If -End Sub -0 - - -2CA9D2C1-C9DE-4078-8F4E-DEC04BC458A9 -ForAttachWith -0 - -0 - -Controls Service Broker options on the database. -Service Broker options can only be specified when the FOR ATTACH clause is used. - -ENABLE_BROKER: -Specifies that Service Broker is enabled for the specified database. - -NEW_BROKER: -Creates a new service_broker_guid value in both sys.databases and the restored database and ends all conversation endpoints with clean up. The broker is enabled, but no message is sent to the remote conversation endpoints. - -ERROR_BROKER_CONVERSATIONS: -Ends all conversations with an error stating that the database is attached or restored. The broker is disabled until this operation is completed and then enabled. - -1 -12 -%forAttachWithList% -1 -Function %Get%(obj) -%Get% = obj.GetPhysicalOptionValue("for/attach/with") -End Function -Sub %Set%(obj, value) -obj.SetPhysicalOptionValue "for/attach/with", value -End Sub -0 - - -286749C4-0CF8-4E60-AEF0-429DE623014E -ForAttachRebuildLog -0 - -0 - -Specifies that the database is created by attaching an existing set of operating system files. -1 -1 -Function %Get%(obj) -%Get% = obj.HasPhysicalOption("for/attach_rebuild_log") -End Function -Sub %Set%(obj, value) -obj.AddPhysicalOption "for/attach_rebuild_log" -End Sub -0 - - -4B749514-FA61-4C81-BC4B-22881A7C3DF9 -AsSnapshotOf -0 - -0 - -Is the name of the new database snapshot. -1 -12 -1 -Function %Get%(obj) -%Get% = obj.GetPhysicalOptionValue("as snapshot of") -End Function -Sub %Set%(obj, value) -obj.SetPhysicalOptionValue "as snapshot of", value -End Sub -0 - - -7A0D0924-9B10-43AD-B507-8FED0995D306 -Collate -0 - -0 - -Specifies the default collation for the database. Collation name can be either a Windows collation name or a SQL collation name. -1 -12 -1 -Function %Get%(obj) -%Get% = obj.GetPhysicalOptionValue("collate") -End Function -Sub %Set%(obj, value) -obj.SetPhysicalOptionValue "collate", value -End Sub -0 - - - - -MethodTargetItem -C76F189C-B041-4E03-A95B-19FF03E7828C -Methods -0 - -0 - -1 - - -0AA51AFD-0352-4A9B-B0DA-452522F9E65D -EditFileGroupFileSpec -0 - -0 - -1 -Sub %Method%(obj) - - Dim dlgFileSpec - - set dlgFileSpec = obj.CreateCustomDialog("Specification") - If Not (dlgFileSpec is Nothing) Then - - Dim sPath - sPath = "on/<filelist>/filegroup/" - ' Set file specifications - dlgFileSpec.SetValue "Name" , obj.GetPhysicalOptionValue(sPath+"name=") - dlgFileSpec.SetValue "FileName" , obj.GetPhysicalOptionValue(sPath+"filename=") - dlgFileSpec.SetValue "Size" , obj.GetPhysicalOptionValue(sPath+"size=") - dlgFileSpec.SetValue "MaxSize" , obj.GetPhysicalOptionValue(sPath+"maxsize=") - dlgFileSpec.SetValue "FileGrowth", obj.GetPhysicalOptionValue(sPath+"filegrowth=") - - ' Edit file specifications - If (dlgFileSpec.ShowDialog = True) Then - ' Save file specifications - obj.SetPhysicalOptionValue sPath+"name=" , dlgFileSpec.GetValue("Name") - obj.SetPhysicalOptionValue sPath+"filename=" , dlgFileSpec.GetValue("FileName") - obj.SetPhysicalOptionValue sPath+"size=" , dlgFileSpec.GetValue("Size") - obj.SetPhysicalOptionValue sPath+"maxsize=" , dlgFileSpec.GetValue("MaxSize") - obj.SetPhysicalOptionValue sPath+"filegrowth=", dlgFileSpec.GetValue("FileGrowth") - End If - - End If - -End Sub - - -6DA66E22-D083-4C9E-B402-199B6AC85E90 -EditFileListFileSpec -0 - -0 - -1 -Sub %Method%(obj) - - Dim dlgFileSpec - - set dlgFileSpec = obj.CreateCustomDialog("Specification") - If Not (dlgFileSpec is Nothing) Then - - Dim sPath - sPath = "on/<filelist>/<filespec>/" - ' Set file specifications - dlgFileSpec.SetValue "Name" , obj.GetPhysicalOptionValue(sPath+"name=") - dlgFileSpec.SetValue "FileName" , obj.GetPhysicalOptionValue(sPath+"filename=") - dlgFileSpec.SetValue "Size" , obj.GetPhysicalOptionValue(sPath+"size=") - dlgFileSpec.SetValue "MaxSize" , obj.GetPhysicalOptionValue(sPath+"maxsize=") - dlgFileSpec.SetValue "FileGrowth", obj.GetPhysicalOptionValue(sPath+"filegrowth=") - - ' Edit file specifications - If (dlgFileSpec.ShowDialog = True) Then - ' Save file specifications - obj.SetPhysicalOptionValue sPath+"name=" , dlgFileSpec.GetValue("Name") - obj.SetPhysicalOptionValue sPath+"filename=" , dlgFileSpec.GetValue("FileName") - obj.SetPhysicalOptionValue sPath+"size=" , dlgFileSpec.GetValue("Size") - obj.SetPhysicalOptionValue sPath+"maxsize=" , dlgFileSpec.GetValue("MaxSize") - obj.SetPhysicalOptionValue sPath+"filegrowth=", dlgFileSpec.GetValue("FileGrowth") - End If - - End If - -End Sub - - -5F212DE7-0413-473E-A9F8-F74F13DE7700 -EditLogOnFileSpec -0 - -0 - -1 -Sub %Method%(obj) - - Dim dlgFileSpec - - set dlgFileSpec = obj.CreateCustomDialog("Specification") - If Not (dlgFileSpec is Nothing) Then - - Dim sPath - sPath = "log on/<filespec>/" - ' Set file specifications - dlgFileSpec.SetValue "Name" , obj.GetPhysicalOptionValue(sPath+"name=") - dlgFileSpec.SetValue "FileName" , obj.GetPhysicalOptionValue(sPath+"filename=") - dlgFileSpec.SetValue "Size" , obj.GetPhysicalOptionValue(sPath+"size=") - dlgFileSpec.SetValue "MaxSize" , obj.GetPhysicalOptionValue(sPath+"maxsize=") - dlgFileSpec.SetValue "FileGrowth", obj.GetPhysicalOptionValue(sPath+"filegrowth=") - - ' Edit file specifications - If (dlgFileSpec.ShowDialog = True) Then - ' Save file specifications - obj.SetPhysicalOptionValue sPath+"name=" , dlgFileSpec.GetValue("Name") - obj.SetPhysicalOptionValue sPath+"filename=" , dlgFileSpec.GetValue("FileName") - obj.SetPhysicalOptionValue sPath+"size=" , dlgFileSpec.GetValue("Size") - obj.SetPhysicalOptionValue sPath+"maxsize=" , dlgFileSpec.GetValue("MaxSize") - obj.SetPhysicalOptionValue sPath+"filegrowth=", dlgFileSpec.GetValue("FileGrowth") - End If - - End If - -End Sub - - - - -TemplateTargetItem -06C210B7-6AC4-4955-8B80-739B1A568567 -Templates -0 - -0 - -1 - - -478A7951-D0C8-4336-99A9-564D3B801C7F -withDbChainingList -.vbscript -dim obj -for each obj in ActiveSelection -scriptresult = obj.GetPhysicalOptionValues("with/db_chaining") -next -.endvbscript -0 - -0 - -1 - - -2D6D1421-F837-45F5-9C7E-5523BDAF8D18 -withTrustworthyList -.vbscript -dim obj -for each obj in ActiveSelection -scriptresult = obj.GetPhysicalOptionValues("with/trustworthy") -next -.endvbscript -0 - -0 - -1 - - -850F64C0-453D-49D8-ABE1-0342444AE82A -forAttachWithList -.vbscript -dim obj -for each obj in ActiveSelection -scriptresult = obj.GetPhysicalOptionValues("for/attach/with") -next -.endvbscript -0 - -0 - -1 - - - - -FormTargetItem -DF83F2A0-628B-4135-87C3-2AA395539F2F -Forms -0 - -0 - -1 - - -8E6B9CA4-9BF8-432C-AE9A-667A2BC04544 -Specification -<Form > - <Edit Name="Name" Caption="Name:" SizeInChars="1" HorizontalResize="Yes" ReadOnly="No" /> - <Edit Name="FileName" Caption="File name:" SizeInChars="1" HorizontalResize="Yes" ReadOnly="No" /> - <Edit Name="Size" Caption="Size:" SizeInChars="1" HorizontalResize="Yes" ReadOnly="No" /> - <Edit Name="MaxSize" Caption="Maximum size:" SizeInChars="1" HorizontalResize="Yes" ReadOnly="No" /> - <Edit Name="FileGrowth" Caption="File growth:" SizeInChars="1" HorizontalResize="Yes" ReadOnly="No" /> -</Form> - -0 - -1276605429 -obalen -Specification of a file -1 -Dialog -24917 - - -0B57A613-561B-4D4F-B04C-2E9EC4CEDFE7 -Mirroring -<Form > - <GroupBox Name="GroupBox1" Caption="Mirroring" ControlAsLabel="Yes" > - <ExtendedAttribute Attribute="EnableMirroring" Caption="Enable mirroring" AttributeID="{FF5A6B3F-2569-40AA-A0E1-74373FCCE815}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <HorizontalLayout Name="HorizontalLayout1" > - <ExtendedAttribute Attribute="Partner" Caption="Partner" AttributeID="{37A3A005-E700-4FE6-B6F6-3DC23438D686}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="Witness" Caption="Witness" AttributeID="{7EAFA431-0D79-46A2-9D5F-6910E9C22240}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - </HorizontalLayout> - <HorizontalLayout Name="HorizontalLayout2" > - <ExtendedAttribute Attribute="MirrorOptions" Caption="Options" AttributeID="{DFD9304D-FE45-4589-96B3-2F2C58557247}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="MirrorServer" Caption="Server" AttributeID="{EF6719F7-8340-41ED-9805-92D1C2AA10FB}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="TimeOut" Caption="Time-out" AttributeID="{64932A2E-B4A6-45D1-AF9C-BFD85815F167}" GrayHiddenAttributes="No" SizeInChars="1" HorizontalResize="Yes" /> - </HorizontalLayout> - </GroupBox> -</Form> - -0 - -1276605429 -obalen -1 -24917 - - -BD863D2D-A0AB-4608-9C67-BDE76339B459 -Microsoft -<Form > - <ExtendedAttribute Attribute="Primary" Caption="Primary" AttributeID="{9A095731-B906-4D61-91C3-E2069C83A258}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="FileListFileSpec" Caption="File:" AttributeID="{99F40AAD-9A0A-4428-9CFE-65A135905171}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" LeftText="false" /> - <Separator Name="FileGroupSeparator" /> - <ExtendedAttribute Attribute="FilelistFilegroup" Caption="Filegroup:" AttributeID="{CEDEDF1A-2957-4441-86C1-F659F3141FA2}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="FileGroupFileSpec" Caption="File:" AttributeID="{2DA17CE1-5AE0-4C25-AF6C-C2B10ABD06A3}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" LeftText="false" /> - <Separator Name="LogOnSeparator" /> - <ExtendedAttribute Attribute="LogOnFileSpec" Caption="Log on:" AttributeID="{313B7F66-5565-479D-BD3C-CA01A15304C7}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" /> - <Separator Name="CollateSeparator" /> - <ExtendedAttribute Attribute="Collate" Caption="Collation name:" AttributeID="{7A0D0924-9B10-43AD-B507-8FED0995D306}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <Separator Name="ForSeparator" /> - <HorizontalLayout Name="ForHorizontalLayout" > - <StaticText Name="For" Caption="For:" /> - <Space Name="Space1" Width="36" Height="1" HorizontalResize="No" VerticalResize="No" /> - <ExtendedAttribute Attribute="ForAttach" Caption="Attach" AttributeID="{5D4BC1AF-4140-4CFB-93A1-CBB72F7993D3}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="ForAttachWith" Caption="with:" AttributeID="{2CA9D2C1-C9DE-4078-8F4E-DEC04BC458A9}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - </HorizontalLayout> - <ExtendedAttribute Attribute="ForAttachRebuildLog" Caption="Attach rebuild log" AttributeID="{286749C4-0CF8-4E60-AEF0-429DE623014E}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="WithDbChaining" Caption="Database chaining" AttributeID="{A2C7D232-3CF5-4823-BB40-27D6AAE9D83E}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="WithTrustworthy" Caption="Trust worthy" AttributeID="{EF8631AC-7A60-4799-9A45-3B11A5531067}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="AsSnapshotOf" Caption="Snapshot of:" AttributeID="{4B749514-FA61-4C81-BC4B-22881A7C3DF9}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> -</Form> - -0 - -1276605429 -obalen -Specific page for Microsoft SQL Server -1 -24917 - - - - - - -EFD472FA-F7CC-4ED9-A748-88517A38A316 -DataSource -0 - -0 - -1 - - -MethodTargetItem -779998FA-1963-40A4-A85D-148A1D4FA237 -Methods -0 - -0 - -1 - - -BD2B2044-E3FD-46B5-9E55-A7A6EA441B97 -Test Connection -0 - -0 - -1 -Sub %Method%(obj) - Dim myVar -On Error Resume Next - Dim CONNECTION - CONNECTION = obj.getExtendedAttribute(ADODotNetTarget + ".ConnectionString") - If CONNECTION="" then - MsgBox "No connection string defined.", vbExclamation, "PowerDesigner - Warning" - Exit Sub - end If - Dim cnx ' connection object - set cnx = CreateObject("ADODB.Connection") - cnx.Open CONNECTION -If Err.Number <> 0 Then - MsgBox "Connection failed.", vbExclamation, "PowerDesigner - Error" -On Error GoTo 0 -Else - MsgBox "Connection successful", vbInformation, "PowerDesigner" -On Error GoTo 0 -End If -End Sub - - -9BF72489-BC17-49A0-B463-E8D6CA468533 -Show detail setting -0 - -0 - -1 -Sub %Method%(obj) - 'ExecuteCustomMethod - 'InteractiveMode = im_Dialog - If obj.getExtendedAttribute(ADODotNetTarget + ".DataProvider") = "Native Data Provider for OLE DB" Then - obj.CreateCustomDialog(ADODotNetTarget + ".OLE DB Configuration").ShowDialog() - ElseIf obj.getExtendedAttribute(ADODotNetTarget + ".DataProvider") = ".NET Framework Data Provider for ODBC" Then - obj.CreateCustomDialog(ADODotNetTarget + ".ODBC Configuration").ShowDialog() - ElseIf obj.getExtendedAttribute(ADODotNetTarget + ".DataProvider") = ".NET Framework Data Provider for Oracle" Then - obj.CreateCustomDialog(ADODotNetTarget + ".Oracle Configuration").ShowDialog() - ElseIf obj.getExtendedAttribute(ADODotNetTarget + ".DataProvider") = ".NET Framework Data Provider for Microsoft SQL Server" Then - obj.CreateCustomDialog(ADODotNetTarget + ".Microsoft SQL Server Configuration").ShowDialog() - ElseIf obj.getExtendedAttribute(ADODotNetTarget + ".DataProvider") = "Microsoft SQL Server Mobile Edition" Then - obj.CreateCustomDialog(ADODotNetTarget + ".Microsoft SQL Server Mobile Configuration").ShowDialog() - ElseIf obj.getExtendedAttribute(ADODotNetTarget + ".DataProvider") = ".NET Framework Data Provider for Sybase SQL Anywhere" Then - obj.CreateCustomDialog(ADODotNetTarget + ".Sybase SQL Anywhere Configuration").ShowDialog() - Else - msgbox "Select a Data Provider." - End If -End Sub - - -4E790177-E3C4-4D02-A1BF-C38FDB601185 -Set Connection String -0 - -0 - -1 -Sub %Method%(obj) - - Dim ConnectionString - Dim ServerName - ServerName = obj.getExtendedAttribute(ADODotNetTarget + ".ServerName") - Dim Location - Location = obj.getExtendedAttribute(ADODotNetTarget + ".Location") - Dim UserName - UserName = obj.getExtendedAttribute(ADODotNetTarget + ".UserName") - Dim Password - Password = obj.getExtendedAttribute(ADODotNetTarget + ".Password") - Dim Allow - if obj.getExtendedAttribute(ADODotNetTarget + ".PersistSecurityInfo")=true then - Allow = "True" - else - Allow = "False" - end if - Dim InitialCatalog - InitialCatalog=obj.getExtendedAttribute(ADODotNetTarget + ".InitialCatalog") - If obj.getExtendedAttribute(ADODotNetTarget + ".DataProvider") = "Native Data Provider for OLE DB" Then - if obj.getExtendedAttribute(ADODotNetTarget + ".DataProviderList") = "Microsoft OLE DB Provider For Oracle" Then - ConnectionString= "Provider=MSDAORA.1;" + GetSubConfig("Data Source", ServerName) + GetSubConfig("PersistSecurityInfo", Allow) + GetSubConfig("User ID", UserName) + GetSubConfig("Password", Password) - elseif obj.getExtendedAttribute(ADODotNetTarget + ".DataProviderList") = "Microsoft OLE DB Provider For SQL Server" Then - ConnectionString="Provider=SQLOLEDB.1;" + GetSubConfig("Data Source", ServerName) + GetSubConfig("PersistSecurityInfo", Allow) + GetSubConfig("User ID", UserName) + GetSubConfig("Password", Password) + GetSubConfig("Initial Catalog", InitialCatalog) - elseif obj.getExtendedAttribute(ADODotNetTarget + ".DataProviderList") = "Sybase Adaptive Server Anywhere Provider 7.0" Then - ConnectionString="Provider=ASAProv.70;" + GetSubConfig("Data Source", ServerName) + GetSubConfig("Persist Security Info", Allow) - elseif obj.getExtendedAttribute(ADODotNetTarget + ".DataProviderList") = "Sybase Adaptive Server Anywhere Provider 8.0" Then - ConnectionString="Provider=ASAProv.80;" + GetSubConfig("Data Source", ServerName) + GetSubConfig("Persist Security Info", Allow) - elseif obj.getExtendedAttribute(ADODotNetTarget + ".DataProviderList") = "Sybase Adaptive Server Anywhere Provider 9.0" Then - ConnectionString="Provider=ASAProv.90;" + GetSubConfig("Data Source", ServerName) + GetSubConfig("Location", Location) + GetSubConfig("Persist Security Info", Allow) + GetSubConfig("User ID", UserName) + GetSubConfig("Password", Password) + GetSubConfig("Initial Catalog", InitialCatalog) - elseif obj.getExtendedAttribute(ADODotNetTarget + ".DataProviderList") = "Sybase ASE OLE DB Provider" Then - ConnectionString="Provider=Sybase.ASEOLEDBProvider.2;" + GetSubConfig("Data Source", ServerName) + GetSubConfig("Persist Security Info", Allow) + GetSubConfig("User ID", UserName) + GetSubConfig("Password", Password) + GetSubConfig("Initial Catalog", InitialCatalog) - end if - if obj.getExtendedAttribute(ADODotNetTarget + ".UseNTIntegratedSecurity")=true then - ConnectionString = ConnectionString + "Integrated Security=SSPI" - end if - ElseIf obj.getExtendedAttribute(ADODotNetTarget + ".DataProvider") = ".NET Framework Data Provider for Oracle" Then - 'Data Source=asdasd;User ID=11;Unicode=True - ConnectionString=GetSubConfig("Data Source", ServerName) + GetSubConfig("User ID", UserName) + GetSubConfig("Password", Password) + "Unicode=True" - ElseIf obj.getExtendedAttribute(ADODotNetTarget + ".DataProvider") = ".NET Framework Data Provider for Sybase SQL Anywhere" Then - ConnectionString=GetSubConfig("Data Source", ServerName) + GetSubConfig("UID", UserName) + GetSubConfig("PWD", Password) - ElseIf obj.getExtendedAttribute(ADODotNetTarget + ".DataProvider") = ".NET Framework Data Provider for Microsoft SQL Server" Then - Dim AuthType - AuthType = obj.getExtendedAttribute(ADODotNetTarget + ".AuthenticationType") - Dim AttachDbFilename - AttachDbFilename = obj.getExtendedAttribute(ADODotNetTarget + ".MSSQLDatabaseFile") - dim LogicalName - LogicalName = obj.getExtendedAttribute(ADODotNetTarget + ".LogicalName") - if LogicalName<>"" and AttachDbFilename<>"" AND AttachDbFilename<>"*.mdf" Then - InitialCatalog=LogicalName - end if - if AuthType="Use SQL Server Authentication" then - ConnectionString=GetSubConfig("Data Source", ServerName)+ GetSubConfig("User ID", UserName) + GetSubConfig("Password", Password) + "Connect Timeout=30;" + GetSubConfig("Initial Catalog", InitialCatalog) - else - ConnectionString=GetSubConfig("Data Source", ServerName) + "Connect Timeout=30; Integrated Security=True;" + GetSubConfig("Initial Catalog", InitialCatalog) - end if - if AttachDbFilename<>"" AND AttachDbFilename<>"*.mdf" Then - ConnectionString=ConnectionString + GetSubConfig("AttachDbFilename", AttachDbFilename) - end if - ElseIf obj.getExtendedAttribute(ADODotNetTarget + ".DataProvider") = "Microsoft SQL Server Mobile Edition" Then - Dim Filename - Filename = obj.getExtendedAttribute(ADODotNetTarget + ".MSSQLMobileDatabaseFile") - If(obj.getExtendedAttribute(ADODotNetTarget + ".MSMobileType")="My Computer") Then - ConnectionString=GetSubConfig("Data Source", Filename) + GetSubConfig("Password", Password) - else - ConnectionString=GetSubConfig("Data Source", "Mobile Device\" + Filename) + GetSubConfig("Password", Password) - end if - - 'Data Source ="asdasdsa"; - End If - - - 'msgbox ConnectionString - call obj.setExtendedAttribute(ADODotNetTarget + ".ConnectionString",ConnectionString) - -End Sub - -Function GetSubConfig(tag, value) - if (value = "") then - GetSubConfig = "" - else - GetSubConfig = tag + "=" + value + ";" - end if -End Function - - - - -ExtendedAttributeTargetItem -7A2B358D-DDBF-46B1-B9C6-7EA16521586A -Extended Attributes -0 - -0 - -1 - - -EC514B4D-2E58-4E53-833C-B175FA325F25 -ConnectionString -0 - -0 - -Specifies the connection string. -1 -12 -0 - - -90B72C92-BEA8-4BE1-90D7-713D098A6719 -DataProvider -0 - -0 - -Specifies the data provider. -1 --1 -Native Data Provider for OLE DB -0 - - - - - -BF5F5158-8EEE-4A20-83E9-A13D46028B40 -DataProviderList -0 - -0 - -Specifies a data provider list. -1 --1 -0 - - - - - -E76A7296-0FDE-4714-BE3E-885A2B1B5207 -InitialCatalog -0 - -0 - -Specifies the Initial catalog. -1 -12 -0 - - -C0E5A632-C2FD-4482-A647-6FADBC508924 -PersistSecurityInfo -0 - -0 - -Specifies that security information be persistent. -1 -true -0 - - -0BFD6B45-7690-44AF-A33D-6A05BEB6D31A -ServerName -0 - -0 - -Specifies the server name. -1 -12 -0 - - -9DACFD18-1FD4-4DD5-8CF8-7C607E4290E0 -UseNTIntegratedSecurity -0 - -0 - -Specifies whether to use windows NT Integrated Security or not. -1 -false -0 - - -CCE1C273-0F9D-4DCF-8FC6-BB8871D88D02 -MSSQL -0 - -0 - -1 - - -D0AC2DFB-5FD1-4977-9F85-AA964E74CB69 -AuthenticationType -0 - -0 - -Specifies the Windows Authentication and SQL Server Authentication types. -1 --1 -Use SQL Server Authentication -0 - - - - - -E5A1A814-D12B-4543-9867-3998CB2258BE -LogicalName -0 - -0 - -Specifies the logical name of the selected database file. -1 -12 -0 - - -603EEA91-6443-4643-B658-9D301F7606BF -MSSQLDatabaseFile -0 - -0 - -Specifies a Microsoft SQL Server database file if you select an MSSQL connection. -1 -3 -*.mdf -0 - - - - - - -FormTargetItem -3D771FB5-4748-49B6-A569-ACEE189D0F87 -Forms -0 - -0 - -1 - - -24FA18D0-A612-4909-97DB-9EAB24F60AFC -OLE DB Configuration -<Form > - <VerticalLayout Name="VerticalLayout1" > - <StaticText Name="Data Source" Caption="Data Source:" /> - <StaticText Name="Static Text1" Caption=" .NET Framework Data Provider for OLE DB" /> - <StaticText Name="OLD DB Provider" Caption="OLE DB Provider:" /> - <ExtendedAttribute Attribute="DataProviderList" Caption="Data providers" AttributeID="{BF5F5158-8EEE-4A20-83E9-A13D46028B40}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <GroupBox Name="Enter a server or file name" Caption="Enter a server or file name" ControlAsLabel="No" > - <ExtendedAttribute Attribute="ServerName" Caption="Server name" AttributeID="{0BFD6B45-7690-44AF-A33D-6A05BEB6D31A}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="Location" Caption="Location" AttributeID="{6CA15A08-8C99-4BBC-B153-3AEA98455DC2}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - </GroupBox> - <GroupBox Name="Log on to the server" Caption="Log on to the server" ControlAsLabel="No" > - <ExtendedAttribute Attribute="UserName" Caption="User name" AttributeID="{18C94AAC-8339-4BD2-B214-434E7ECE90B9}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="Password" Caption="Password" AttributeID="{F71985C6-2082-4AB6-B312-C1489D1BA37F}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <HorizontalLayout Name="HorizontalLayout1" > - <ExtendedAttribute Attribute="PersistSecurityInfo" Caption="Persist security info" AttributeID="{C0E5A632-C2FD-4482-A647-6FADBC508924}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="UseNTIntegratedSecurity" Caption="Use Windows NT Integrated Security" AttributeID="{9DACFD18-1FD4-4DD5-8CF8-7C607E4290E0}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - </HorizontalLayout> - </GroupBox> - <ExtendedAttribute Attribute="InitialCatalog" Caption="Initial catalog" AttributeID="{E76A7296-0FDE-4714-BE3E-885A2B1B5207}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <HorizontalLayout Name="HorizontalLayout2" > - <CustomMethod Method="Test Connection" Name="Test Connection" Caption="&amp;Test Connection" HorizontalResize="No" /> - <Space Name="Space1" Width="6" Height="6" HorizontalResize="No" VerticalResize="No" /> - <CustomMethod Method="Set Connection String" Name="Apply To Connection String" Caption="&amp;Apply To Connection String" HorizontalResize="No" /> - </HorizontalLayout> - </VerticalLayout> -</Form> - -0 - -1276605429 -obalen -1 -Dialog -24917 - - -A4DF69D1-6F22-4478-B5E3-55B8488BD09E -OLE DB -<Form > - <GroupBox Name="Database Connection" Caption="Database connection options" ControlAsLabel="No" > - <ExtendedAttribute Attribute="DataProvider" Caption="Data provider" AttributeID="{90B72C92-BEA8-4BE1-90D7-713D098A6719}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <HorizontalLayout Name="HorizontalLayout1" > - <ExtendedAttribute Attribute="ConnectionString" Caption="Connection string" AttributeID="{EC514B4D-2E58-4E53-833C-B175FA325F25}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <CustomMethod Method="Show detail setting" Name="Show detail setting" Caption="..." HorizontalResize="No" /> - </HorizontalLayout> - <VerticalLayout Name="VerticalLayout1" > - <ExtendedAttribute Attribute="EncryptConnectionString" Caption="Encrypt connection string:" AttributeID="{4FCF9445-D655-4800-9B12-24B6589D2456}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="Yes" LeftText="No" /> - </VerticalLayout> - </GroupBox> -</Form> - -0 - -1276605429 -obalen -1 -24917 - - -E5FACE5F-E564-4608-AFE1-6FB78D90ACD5 -Microsoft SQL Server Configuration -<Form > - <VerticalLayout Name="VerticalLayout1" > - </VerticalLayout> - <StaticText Name="Data source" Caption="Data source: " /> - <StaticText Name="StaticText2" Caption=" Microsoft SQL Server Database File (SqlClient)" /> - <ExtendedAttribute Attribute="ServerName" Caption="Server name:" AttributeID="{0BFD6B45-7690-44AF-A33D-6A05BEB6D31A}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <GroupBox Name="Login information" Caption="Log on to the server" ControlAsLabel="No" > - <ExtendedAttribute Attribute="AuthenticationType" Caption="Authentication" AttributeID="{D0AC2DFB-5FD1-4977-9F85-AA964E74CB69}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="UserName" Caption="User name" AttributeID="{18C94AAC-8339-4BD2-B214-434E7ECE90B9}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="Password" Caption="Password" AttributeID="{F71985C6-2082-4AB6-B312-C1489D1BA37F}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="InitialCatalog" Caption="Initial catalog" AttributeID="{E76A7296-0FDE-4714-BE3E-885A2B1B5207}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - </GroupBox> - <GroupBox Name="Attach a database file" Caption="Attach a database file" ControlAsLabel="No" > - <ExtendedAttribute Attribute="MSSQLDatabaseFile" Caption="Database File" AttributeID="{603EEA91-6443-4643-B658-9D301F7606BF}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - <ExtendedAttribute Attribute="LogicalName" Caption="Logical name" AttributeID="{E5A1A814-D12B-4543-9867-3998CB2258BE}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> - </GroupBox> - <HorizontalLayout Name="HorizontalLayout1" > - <CustomMethod Method="Set Connection String" Name="Apply To Connection String" Caption="Apply To Connection String" HorizontalResize="No" /> - </HorizontalLayout> -</Form> - -0 - -1276605429 -obalen -1 -Dialog -24917 - - -E79C7795-8E76-409F-94CA-4D88B0801B59 -Oracle Configuration -<Form > - <VerticalLayout Name="VerticalLayout1" > - </VerticalLayout> - <StaticText Name="Data source" Caption="Data source:" /> - <StaticText Name="StaticText2" Caption=" Oracle Database (OracleClient)" /> - <GroupBox Name="Data source specification" Caption="Data source specification" ControlAsLabel="No" > - <ExtendedAttribute Attribute="ServerName" Caption="Server name" AttributeID="{0BFD6B45-7690-44AF-A33D-6A05BEB6D31A}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - </GroupBox> - <GroupBox Name="Login information" Caption="Login information" ControlAsLabel="No" > - <ExtendedAttribute Attribute="UserName" Caption="User name:" AttributeID="{18C94AAC-8339-4BD2-B214-434E7ECE90B9}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="Password" Caption="Password" AttributeID="{F71985C6-2082-4AB6-B312-C1489D1BA37F}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - </GroupBox> - <HorizontalLayout Name="HorizontalLayout1" > - <CustomMethod Method="Set Connection String" Name="Apply To Connection String" Caption="Apply To Connection String" HorizontalResize="No" /> - </HorizontalLayout> -</Form> - -0 - -1276605429 -obalen -1 -Dialog -24917 - - - - -CriterionTargetItem -D0BF1B0A-6E8D-47DC-AA55-0D7921F2B1F7 -Criteria -0 - -0 - -1 - - -0878584A-1CA2-4FEC-B156-4F87DC02CB51 -ASA -(%.31:DataProviderList% == "Sybase Adaptive Server Anywhere") -0 - -0 - -Controls ASA specific extended attributes. -1 - - -ExtendedAttributeTargetItem -B1270AB5-C543-4045-9C37-30E798AAFD4A -Extended Attributes -0 - -0 - -1 - - -6CA15A08-8C99-4BBC-B153-3AEA98455DC2 -Location -0 - -0 - -Specifies the location for OLEDB -1 -12 -0 - - - - - - -E44B35EA-CD7A-4A03-8DFB-C2B929EC9D19 -NeedUserNamePassword -(%DataProvider% == ".NET Framework Data Provider for Oracle") or ((%DataProvider% == ".NET Framework Data Provider for Microsoft SQL Server") and (%AuthenticationType% == "Use SQL Server Authentication")) or ((%DataProvider% == "Native Data Provider for OLE DB") and (%UseNTIntegratedSecurity% == false)) -0 - -0 - -If do not use Windows NT integrated security, or Data Provider is from Oracle, the user name and password will be enabled. -1 - - -ExtendedAttributeTargetItem -C0DC950E-A90B-49FF-8AAA-6C868CFB89B3 -Extended Attributes -0 - -0 - -1 - - -F71985C6-2082-4AB6-B312-C1489D1BA37F -Password -0 - -0 - -Specifies the password -1 -12 -0 - - -18C94AAC-8339-4BD2-B214-434E7ECE90B9 -UserName -0 - -0 - -Specifies the User name. -1 -12 -0 - - - - - - - - - - -C0868091-43BF-4210-B103-A64CA2302A27 -Storage -0 - -0 - -1 - - -ExtendedAttributeTargetItem -AF941C50-A1ED-4BF2-B24C-404D2DAC3CAD -Extended Attributes -0 - -0 - -1 - - -3A78AA18-329F-4B25-8FE6-05D3CF2588C1 -FileStream -0 - -0 - -Specifies that the filegroup stores FILESTREAM binary large objects (BLOBs) in the file system. -1 -Contains filestream -0 - - - - -FormTargetItem -53ED5114-E46E-4A16-99AB-7413B9E9EA9E -Forms -0 - -0 - -1 - - -FE26AED7-F14B-4AC5-9B91-0E3A8B1F666A -Microsoft -<Form > - <ExtendedAttribute Attribute="FileStream" Caption="Contains filestream" AttributeID="{3A78AA18-329F-4B25-8FE6-05D3CF2588C1}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> -</Form> - -0 - -1276605429 -obalen -1 -24917 - - - - - - -FBD675CA-37CA-4116-9310-2F74BBFE44C8 -ExtendedSubObject -0 - -0 - -1 - - -StereotypeTargetItem -5A7077EC-8959-4EA5-B9FD-71F746F57162 -Stereotypes -0 - -1299595242 -Marc -1 - - -009B037D-1160-4ED2-B3AF-1587F682C255 -AggregateParameter -0 - -0 - -Parameters of aggregate object -1 -Parameter -Parameters -1 -Parameter - - -ExtendedAttributeTargetItem -E858C802-245B-42F8-8E64-EA826DCFEEAE -Extended Attributes -0 - -0 - -1 - - -385B7A48-909B-4C46-975F-DE81E0737499 -InputParameterType -0 - -0 - -Specifies the type of the input parameter. All scalar data type or CLR user-defined types can be used as a parameter for a user-defined aggregate, except text, ntext, and image. Non scalar types, such as cursor and table, cannot be specified. -1 -12 -integer -%inputParameterTypeList% -Data type -0 - - -1E349D9A-1DF5-4E2F-A96D-8AB01706C450 -DisplayType -0 - -0 - -Complete datatype with length and precision (for script generation and reverse engineering purpose) -1 -12 -1 -Function %Get%(obj) - Dim sGet - sGet = obj.GetExtendedAttribute("%CurrentTargetCode%.InputParameterType") - if (sGet = "") then - %Get% = "" - else - Dim sLP - if CBool(obj.EvaluateTemplateFor("needLength")) then - sLP = obj.GetExtendedAttribute("%CurrentTargetCode%.InputParameterLength") - if (sLP = "") then - %Get% = sGet - else - sGet = sGet & "(" & sLP - if CBool(obj.EvaluateTemplateFor("needPrec")) then - sLP = obj.GetExtendedAttribute("%CurrentTargetCode%.InputParameterPrec") - if (sLP <> "") then - sGet = sGet & ", " & sLP - end if - end if - %Get% = sGet & ")" - end if - else - %Get% = sGet - end if - end if -End Function -Sub %Set%(obj, value) - Dim pos, sType, sLP - ' reset attributes (dec supports length & precision) - obj.SetExtendedAttribute "%CurrentTargetCode%.InputParameterType", "dec" - obj.SetExtendedAttribute "%CurrentTargetCode%.InputParameterLength", "" - obj.SetExtendedAttribute "%CurrentTargetCode%.InputParameterPrec", "" - - pos = InStr(value, "(") - if (pos = 0) then - sType = value - sLP = "" - else - sType = Left(value, pos - 1) - sLP = replace(Mid(value, pos + 1), ")", "") - end if - obj.SetExtendedAttribute "%CurrentTargetCode%.InputParameterType", sType - - if (sLP <> "") then - if CBool(obj.EvaluateTemplateFor("needLength")) then - Dim LPArray - LPArray = Split(sLP, ",") - obj.SetExtendedAttribute "%CurrentTargetCode%.InputParameterLength", Trim(LPArray(0)) - if ((UBound(LPArray) > 0) and CBool(obj.EvaluateTemplateFor("needPrec"))) then - obj.SetExtendedAttribute "%CurrentTargetCode%.InputParameterPrec", Trim(LPArray(1)) - end if - end if - end if - -End Sub -0 - - - - -FormTargetItem -BA55C7EC-75A3-4210-9E4F-32111898DDA8 -Forms -0 - -0 - -1 - - -5D5B13F5-A512-4DF7-9759-E832F6A1B8F9 -General -GENERAL -<Form > - <StandardAttribute Attribute="ParentObject" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" LeftText="false" /> - <StandardNameAndCode Attribute="NameAndCode" SizeInChars="1" HorizontalResize="true" /> - <StandardAttribute Attribute="Comment" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="InputParameterType" Caption="Type" AttributeID="{385B7A48-909B-4C46-975F-DE81E0737499}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <HorizontalLayout Name="HorizontalLayout1" > - <ExtendedAttribute Attribute="InputParameterLength" Caption="Length" AttributeID="{4DFBFA62-8593-4840-B2A1-6A173BEBD413}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="InputParameterPrec" Caption="Precision" AttributeID="{FD12AAD6-F4B0-494D-9073-CA67C5F2E0D5}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - </HorizontalLayout> <StandardAttribute Attribute="KeywordList" Caption="Keywords" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> -</Form> - -0 - -1276605502 -obalen -1 -GENERAL -24955 - - -84899A10-B060-4583-9D34-2F32026C0810 -Hidden -<Form > - <ExtendedAttribute Attribute="DisplayType" Caption="Hidden" AttributeID="{1E349D9A-1DF5-4E2F-A96D-8AB01706C450}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" /> -</Form> - -0 - -0 - -1 -Dialog - - - - -CriterionTargetItem -74A46EC8-ED3F-4F1D-AC2E-42937235095F -Criteria -0 - -0 - -1 - - -A26797BE-880F-4E6D-B246-FD3BA26C14FF -NeedLength -%needLength%==True -0 - -0 - -1 - - -ExtendedAttributeTargetItem -0B70F995-BF90-4BC1-B346-F0F235C63EB6 -Extended Attributes -0 - -0 - -1 - - -4DFBFA62-8593-4840-B2A1-6A173BEBD413 -InputParameterLength -0 - -0 - -Length of input parameter data type -1 -10 -Length -0 - - - - -CriterionTargetItem -C505BA15-F6FD-4199-97E8-A592BF5E9F88 -Criteria -0 - -0 - -1 - - -231C978F-33D7-47FD-B50C-EE637A69734C -NeedPrec -%needPrec%==True -0 - -0 - -1 - - - - - -ExtendedAttributeTargetItem -587B004E-04BC-4FD3-802F-F313E9AE8483 -Extended Attributes -0 - -0 - -1 - - -FD12AAD6-F4B0-494D-9073-CA67C5F2E0D5 -InputParameterPrec -0 - -0 - -Precision of input parameter data type -1 -10 -Precision -0 - - - - - - - - -TemplateTargetItem -5B8B76ED-8BA0-4C38-8D1B-57C15AB15E6A -Templates -0 - -0 - -1 - - -5640BB36-9067-42DC-9CF6-C697C96DF528 -needPrec -.if(%.L:InputParameterType%=="numeric") -True -.elsif(%.L:InputParameterType%=="decimal") -True -.elsif(%.L:InputParameterType%=="dec") -True -.else -False -.endif -0 - -0 - -1 - - - - - - - - - - - - -TemplateTargetItem -6C52C01A-C813-42E0-B059-1928630D499E -Templates -0 - -0 - -1 - - -AC5D1AB9-D49E-4BCB-81D1-C926863783BC -inputParameterTypeList -List -char -character -tinyint -smallint -int -integer -bigint -numeric -decimal -dec -real -float -double precision -money -smallmoney -datetime -smalldatetime -timestamp -bit -binary -nvarchar -0 - -0 - -1 - - -9EE5E055-CF8A-45ED-AB4F-00E0F3FAD0FD -needLength -.if(%.L:InputParameterType%=="char") -True -.elsif(%.L:InputParameterType%=="character") -True -.elsif(%.L:InputParameterType%=="numeric") -True -.elsif(%.L:InputParameterType%=="decimal") -True -.elsif(%.L:InputParameterType%=="dec") -True -.elsif(%.L:InputParameterType%=="float") -True -.elsif(%.L:InputParameterType%=="binary") -True -.elsif(%.L:InputParameterType%=="nvarchar") -True -.else -False -.endif -0 - -0 - -1 - - - - - - -B78275A5-4ED3-49CE-8DE7-D34DAA4A1ED3 -AbstractDataTypeAttribute -0 - -0 - -1 - - -ExtendedAttributeTargetItem -6297A8CA-3F7D-44A2-827C-8CC440014EE2 -Extended Attributes -0 - -0 - -1 - - -F5D8BF71-6CF8-428F-9BDA-B9547E488011 -Computed -0 - -0 - -Indicate that the type column is computed. -1 -0 - - -B13C5932-A261-4638-B5AA-22580B4E7200 -Nullable -0 - -0 - -Indicate that the type column allows null value. -1 -true -1 -Function %Get%(obj) - %Get% = Not(obj.Mandatory) -End Function -Sub %Set%(obj, value) - obj.Mandatory = Not(value) -End Sub -Nullable -0 - - - - -CriterionTargetItem -376D50D3-ED38-4F8E-8070-19C671F91AF4 -Criteria -0 - -0 - -1 - - -C679C67C-2663-47D6-9702-769B74AFE589 -Computed -%Computed% -0 - -0 - -1 - - -ExtendedAttributeTargetItem -524E25FB-7FE3-4E96-A60A-B4E798013286 -Extended Attributes -0 - -0 - -1 - - -97CA728D-C96D-481E-8B7F-18685B550D16 -Expression -0 - -0 - -Is an expression that defines the value of a computed column. -1 -12 -0 - - -01AB3048-7A3D-43D2-B438-6163AE9A219F -Persisted -0 - -0 - -Specifies that the SQL Server Database Engine will physically store the computed values in the table, and update the values when any other columns on which the computed column depends are updated. -1 -0 - - - - - - -74A22AA3-2135-4FF9-916D-013460294ECE -NotComputed -%Computed%==false -0 - -0 - -1 - - -ExtendedAttributeTargetItem -94707602-1A76-4648-AA5F-49775BA9E8D4 -Extended Attributes -0 - -0 - -1 - - -7BFB73CB-58E0-4434-BF1F-FB4890E77C88 -Identity -0 - -0 - -Indicates that the new column is an identity column. -1 -false -Identity -0 - - -D459D4A2-A169-4546-8F60-CEB4DD0E667A -Collate -0 - -0 - -Specifies the collation for the column. -1 -12 -Collation -0 - - -27331E86-111A-47BB-9F7E-5CB958DC0A24 -RowGuidCol -0 - -0 - -Indicates that the new column is a row GUID column. -1 -Row GUID -0 - - - - -CriterionTargetItem -01129649-CA99-431B-982D-75A97E3DDFD6 -Criteria -0 - -0 - -1 - - -D653D6CC-7239-489E-B210-3309EF5F235C -Identity -%Identity% -0 - -0 - -1 - - - - - -ExtendedAttributeTargetItem -4552922E-AC40-4E4B-A9BB-F4640E3D8C05 -Extended Attributes -0 - -0 - -1 - - -035D51D4-00E4-455B-A595-1BED57C478D0 -Seed -0 - -0 - -Is the value used for the very first row loaded into the table. -1 -10 -1 -Seed -0 - - -3F6FE1E4-7854-4EA6-902A-2FA6D1384129 -Increment -0 - -0 - -Is the incremental value added to the identity value of the previous row loaded. -1 -10 -1 -Increment -0 - - - - - - -907C1A21-C729-4DB5-9D3D-F90CD3D8E331 -NotIdentity -%Identity%==false -0 - -0 - -1 - - - - - -ExtendedAttributeTargetItem -81A391E8-D192-4DDD-88A8-8E14AE099FB4 -Extended Attributes -0 - -0 - -1 - - -AA57596D-45BA-43B8-9653-72945070D693 -Default -0 - -0 - -Specifies the value provided for the column when a value is not explicitly supplied during an insert. -1 -12 -Default -0 - - - - - - - - - - - - -FormTargetItem -4ACC44D0-7C37-464D-8A1A-3FEBE4AB3840 -Forms -0 - -0 - -1 - - -B64FB9FA-9AD7-472E-98DA-942BA946A2E7 -Microsoft -<Form > - <VerticalLayout Name="VerticalLayout1" > - <HorizontalLayout Name="HorizontalLayout3" > - <ExtendedAttribute Attribute="Nullable" Caption="Nullable" AttributeID="{B13C5932-A261-4638-B5AA-22580B4E7200}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="Computed" Caption="Computed" AttributeID="{F5D8BF71-6CF8-428F-9BDA-B9547E488011}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> - <ExtendedAttribute Attribute="Identity" Caption="Identity" AttributeID="{7BFB73CB-58E0-4434-BF1F-FB4890E77C88}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" Display="Checkbox" LeftText="No" /> - </HorizontalLayout> - <HorizontalLayout Name="HorizontalLayout2" > - <ExtendedAttribute Attribute="Expression" Caption="Expression" AttributeID="{97CA728D-C96D-481E-8B7F-18685B550D16}" GrayHiddenAttributes="No" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="Persisted" Caption="Persisted" AttributeID="{01AB3048-7A3D-43D2-B438-6163AE9A219F}" GrayHiddenAttributes="No" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" Display="Checkbox" LeftText="No" /> - </HorizontalLayout> - <HorizontalLayout Name="HorizontalLayout1" > - <ExtendedAttribute Attribute="Seed" Caption="Seed" AttributeID="{035D51D4-00E4-455B-A595-1BED57C478D0}" GrayHiddenAttributes="No" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - <ExtendedAttribute Attribute="Increment" Caption="Increment" AttributeID="{3F6FE1E4-7854-4EA6-902A-2FA6D1384129}" GrayHiddenAttributes="No" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - </HorizontalLayout> - <ExtendedAttribute Attribute="Default" Caption="Default" AttributeID="{AA57596D-45BA-43B8-9653-72945070D693}" GrayHiddenAttributes="No" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> - </VerticalLayout> - <Separator Name="Separator" /> - <HorizontalLayout Name="HorizontalLayout4" > - <ExtendedAttribute Attribute="RowGuidCol" Caption="Row GUID" AttributeID="{27331E86-111A-47BB-9F7E-5CB958DC0A24}" GrayHiddenAttributes="Yes" SizeInChars="1" LineNumber="3" HorizontalResize="true" VerticalResize="true" Display="Checkbox" LeftText="No" /> - </HorizontalLayout> - <ExtendedAttribute Attribute="Collate" Caption="Collation" AttributeID="{D459D4A2-A169-4546-8F60-CEB4DD0E667A}" GrayHiddenAttributes="No" SizeInChars="1" LineNumber="3" HorizontalResize="Yes" VerticalResize="true" LeftText="false" /> -</Form> - -0 - -1276605429 -obalen -1 -24917 - - - - - - -986345D1-4DFB-4373-8F60-47A7AAB4403B -Fact -1339509313 -psitja -1339509359 -psitja -1 - - -ExtendedAttributeTargetItem -F427072F-2959-4945-A419-B0995AC43808 -Extended Attributes -0 - -1339509359 -psitja -1 - - -364AB9EF-2F09-4E1A-8796-88CDE05A02EA -StorageMode -0 - -0 - -Specifies the storage mode for the cube. -1 --1 -Molap -1 -0 - - - - - -85DF6303-64C4-4350-A102-D07CA30EE0B1 -Visible -0 - -0 - -Determines the visibility of the Cube. -1 -true -0 - - - - -FormTargetItem -B87C8F35-9BC7-46BE-91E4-CC3B9F6D986F -Forms -0 - -1339509359 -psitja -1 - - -2EDBA828-C168-4B5B-87B4-7DEC0E5688CF -Microsoft -<Form > - <ExtendedAttribute Attribute="StorageMode" Caption="Storage Mode" AttributeID="{364AB9EF-2F09-4E1A-8796-88CDE05A02EA}" GrayHiddenAttributes="Yes" SizeInChars="1" HorizontalResize="Yes" Display="Combobox" /> - <ExtendedAttribute Attribute="Visible" Caption="Visible " AttributeID="{85DF6303-64C4-4350-A102-D07CA30EE0B1}" GrayHiddenAttributes="Yes" Display="Checkbox" LeftText="No" /> -</Form> - -0 - -1276605429 -obalen -1 -24917 - - - - -GeneratedFileTargetItem -34B08A87-22CD-433E-AB0F-7152CBFA4514 -Generated Files -0 - -1339509359 -psitja -1 - - -6AA3EC1E-0B20-4CCB-8E30-3BED6EB81211 -Create -%CreateCube% -0 - -0 - -1 - - -339DB687-D96A-4F41-831A-892E4DF01969 -Populate -%InsertCube% -0 - -0 - -1 - - - - -TemplateTargetItem -2AAE7593-C673-4B1B-A18B-A1C4DFD3E21C -Templates -0 - -1339509359 -psitja -1 - - -0DA3178A-9CAF-4D94-8B2B-2AB260F0A2A6 -CreateCube -.set_value(CubeName, %GeneratedName%,new) -CREATE CUBE %GeneratedName% -(\n -.foreach_item(CubeDimensionAssociations,,) - %CubeDimensionAssocationDef% -.next(,\n) -.if (%Measures.IsEmpty% == false) - .foreach_item(Measures,",\n") - .if (%Formula% == false) - %MeasureDef% - .endif - .next(,\n) - .foreach_item(Measures,",\n") - .if (%Formula% == true) - %CommandDef% - .endif - .next(,\n) -.endif - -) -0 - -1339509402 -psitja -1 - - -16EE5B47-A106-425E-8302-D3BC0F42647F -InsertCube -.if (%DefaultMapping%) - .set_value(_columns,"",new) -INSERT INTO %GeneratedName% ( - .foreach_item(CubeDimensionAssociations,\n,) - .if (%Hierarchy%) - .foreach_item(Hierarchy.Attributes) - .ifnot (%SourceMaps.IsEmpty%) - %Dimension.GeneratedName%.%GeneratedName% - .if (%_columns%) - .set_value(_columns, "%_columns%,\n") - .set_value(_columns, %_columns% %SourceMaps.First.SourceFeature.FullCode%) - .else - .set_value(_columns, %SourceMaps.First.SourceFeature.FullCode%) - .endif - .endif - .next(",\n") - .elsif (%Dimension.DimensionHierarchies.IsEmpty% == false%) - .foreach_item(Dimension.DimensionHierarchies) - .foreach_item(Attributes) - .ifnot (%SourceMaps.IsEmpty%) - %Dimension.GeneratedName%.%Outer.GeneratedName%.%GeneratedName% - .if (%_columns%) - .set_value(_columns, "%_columns%,\n") - .endif - .set_value(_columns, %_columns% %SourceMaps.First.SourceFeature.FullCode%) - .endif - .next(",\n") - .next(",\n") - .else - .foreach_item(Dimension.Attributes) - .ifnot (%SourceMaps.IsEmpty%) - %Dimension.GeneratedName%.%GeneratedName% - .if (%_columns%) - .set_value(_columns, "%_columns%,\n") - .endif - .set_value(_columns, [%_columns%]%SourceMaps.First.SourceFeature.FullCode%) - .endif - .next(",\n") - .endif - .next(",\n") - .foreach_item(Measures,",\n") - %GeneratedName% - .next(",\n") - -) - -SELECT\n -.replace(\\n,"\n ") - %_columns% -.endreplace(\n) -FROM\n - .foreach_item(DefaultMapping.CubeSources, " ", \n) -.if (%Owner%) -%Owner.GeneratedName% -.endif(.) -%GeneratedName% - .next(", ") - .foreach_item(DefaultMapping.CubeSourceLinks, "WHERE\n") - .foreach_item(Joins) - %ParentTableColumn.FullCode% = %ChildTableColumn.FullCode% - .next(" AND \n") - .next(" AND \n") -.endif -0 - -1339509520 -psitja -1 - - - - - - - - -B2C7F90B-0848-4AEF-9C73-E53292FD1236 -Odbc -0 - -0 - -DBMS characteristics, command definition, and data type translations for the ODBC generation and reverse engineering - - -A868C170-2F81-4F13-B08E-FB4FB9DDE064 -Objects -0 - -0 - -Contains sub-categories for each type of object in the database, for example: Table, or Reference. Each sub-category contains entries whose values define database commands and object-related characteristics - - -E94BE1D2-71D6-40BB-8ADD-66C0914D4E26 -Qualifier -0 - -0 - -Manages the use of qualifier in ODBC reverse engineering - - -C179D4A9-AFD9-47CE-9062-9B89FD054B37 -Enable -0 - -0 - -Allows using the qualifier combo box during ODBC reverse engineering -1 - - -56625836-F4E8-4D37-9C57-46C132BE5CDF -SqlListQuery -select name from master.dbo.sysdatabases order by name -0 - -0 - -Query to retrieve qualifier during ODBC reverse engineering - - -0293BFEF-768D-4E1A-9B9A-47DEA3DD4722 -Label -0 - -0 - -Label for <All> in qualifier selection list -All databases - - - - - - - - -TransformationTargetItemProfile -B5599B87-BE46-4CD4-8DD9-78EA03262F6A -Transformation Profiles -1285594636 -psitja -1285594912 -psitja - - -296405CE-215D-4CD2-8065-8CE723F65E0C -Handle MS SQL Server User-Schema Separation -1285594636 -psitja -1286895846 -psitja -1 -B18A8271-9633-11D1-9914-006097355D9B -1 - - - - - - - - - - - - -88ACEC30-901E-4CE2-99C5-3C89EDE1553D -<Default CLR DDL Trigger> -<Default CLR DDL Trigger> -create procedure [%QUALIFIER%]%PROC% <@arg> <type> as -declare <@var> <type> -begin - -end - -1165412648 -mledier -1165412648 -mledier -Default CLR DDL Trigger -0 -%ProcedureTemplate.Name%_Trigger -DDLTriggerClassMethod - - -E2E1D9E0-900E-4D6E-93ED-3AAE49E22173 -<Default CLR Function> -<Default CLR Function> -create function [%QUALIFIER%]%FUNC% (<arg> <type>) -RETURNS <type> -external name %Assembly.GeneratedName%.%MethodName% -1146585127 -fleysour -1149003928 -fleysour -Default CLR function template -1 -0 -%Table.Name%_%ProcedureTemplate.Name% -ClassMethod - - -8EC63330-1FA3-42CD-A374-9DD3F457FAFC -<Default CLR Procedure> -<Default CLR Procedure> -create procedure [%QUALIFIER%]%PROC% <@arg> <type> as -external name %Assembly.GeneratedName%.%MethodName% -1146585127 -fleysour -1149003931 -fleysour -Default CLR Procedure template -0 -%Table.Name%_%ProcedureTemplate.Name% -ClassMethod - - -74062D1F-F93F-4AFE-A576-765E1A62C41F -<Default DDL Trigger> -DEFAULTDDLTRIGGER -create trigger [%QUALIFIER%]%PROC% on %ddlTriggerTarget% -[with %ddlTriggerOptions%] -%ddlTriggerTime% -begin - -end - -1165412648 -mledier -1165412648 -mledier -Default DDL Trigger template -0 -%ProcedureTemplate.Code% -DDLTrigger - - -3FB7BFD4-AED6-4BED-90FB-A600B8F3E7B4 -DeleteProcedure -DeleteProcedure -.if (%canDefineDeleteProcedure%) -create procedure [%QUALIFIER%]%PROC% - .foreach_item(Table.Columns," "," as\n", %Primary%) -@%Code% %DataType% - .next(",") -begin -delete from - .if(%Owner%) -%Owner.Code%.%Table.GeneratedName% - .else -%Table.GeneratedName% - .endif("\n") - .foreach_item(Table.Columns,"where ","\n", %Primary%) -%GeneratedName% = @%Code% - .next(" and ") -end -.endif -1138617083 -fhillard -1152531510 -fhillard -Procedure template that generates a SQL delete statement. -%Table.Name%_%ProcedureTemplate.Name% - - -5A98A522-6649-4E7F-81E1-8A7B20581FD2 -InsertProcedure -InsertProcedure -.if (%canDefineInsertProcedure%) -create procedure [%QUALIFIER%]%PROC% - .foreach_item(Table.Columns," "," as\n", %SerialDataType% == false) - .if (%Computed% == false) -@%Code% %DataType% - .endif - .next(",") -begin -insert into - .if(%Owner%) -%Owner.Code%.%Table.GeneratedName% - .else -%Table.GeneratedName% - .endif() - .foreach_item(Table.Columns," (",")\n", %SerialDataType% == false) - .if (%Computed% == false) -%GeneratedName% - .endif - .next(",") -values - .foreach_item(Table.Columns,"(",")\n", %SerialDataType% == false) - .if (%Computed% == false) -@%Code% - .endif - .next(",") -end -.endif - -1138617083 -fhillard -1152531523 -fhillard -Procedure template that generates a SQL insert statement. -%Table.Name%_%ProcedureTemplate.Name% - - -68294B9D-3A8E-409D-9D2A-F1E9C4549DDC -SelectProcedure -SelectProcedure -.if (%canDefineSelectProcedure%) -create procedure [%QUALIFIER%]%PROC% - .foreach_item(Table.Columns," "," as\n", %Primary%) -@%Code% %DataType% - .next(",") -begin -select * from - .if(%Owner%) -%Owner.Code%.%Table.GeneratedName% - .else -%Table.GeneratedName% - .endif("\n") - .foreach_item(Table.Columns,"where ","\n", %Primary%) -%GeneratedName% = @%Code% - .next(" and ") -end -.endif -1138617083 -fhillard -1152531535 -fhillard -Procedure template that generates a SQL select statement. -%Table.Name%_%ProcedureTemplate.Name% - - -C5C36618-0F67-4034-8AE1-E82AF999950F -UpdateProcedure -UpdateProcedure -.if (%canDefineUpdateProcedure%) -create procedure [%QUALIFIER%]%PROC% - .foreach_item(Table.Columns," "," as\n", %Computed% == false) -@%Code% %DataType% - .next(",") -begin -update - .if(%Owner%) -%Owner.Code%.%Table.GeneratedName% - .else -%Table.GeneratedName% - .endif("\n") - .foreach_item(Table.Columns,"set ","\n",%Primary% == false) - .if (%Computed% == false) -%GeneratedName% = @%Code% - .endif - .next(", ") - .foreach_item(Table.Columns,"where (",")\n", %Primary%) - .if (%Computed% == false) -%GeneratedName% = @%Code% - .endif - .next(" and ") -end -.endif -1138617083 -fhillard -1152531547 -fhillard -Procedure template that generates a SQL update statement. -%Table.Name%_%ProcedureTemplate.Name% - - - - -4FFA48EB-79E5-4A60-86AD-1763DC5E3FEE -InsertChildParentExist -InsertChildParentExist -.FOREACH_PARENT("FKNOTNULL") -/* Parent "[%PQUALIFIER%]%PARENT%" must exist when inserting a child in "[%CQUALIFIER%]%CHILD%" */ -if .JOIN("update(%FK%)", "", " or") -begin - if (select count(*) - from [%PQUALIFIER%]%PARENT% t1, inserted t2 - where .JOIN("t1.%PK% = t2.%FK%", "and ", "", ") != @numrows") - .ERROR(50002, "Parent does not exist in "[%PQUALIFIER%]%PARENT%". Cannot create child in "[%CQUALIFIER%]%CHILD%".") -end -.ENDFOR -.FOREACH_PARENT("FKNULL") -/* Parent "[%PQUALIFIER%]%PARENT%" must exist when inserting a child in "[%CQUALIFIER%]%CHILD%" */ -if .JOIN("update(%FK%)", "", " or") -begin - select @numnull = (select count(*) - from inserted - where .JOIN("%FK% is null", "and ", "", ")") - if @numnull != @numrows - if (select count(*) - from [%PQUALIFIER%]%PARENT% t1, inserted t2 - where .JOIN("t1.%PK% = t2.%FK%", "and ", "", ") != @numrows - @numnull") - .ERROR(50002, "Parent does not exist in "[%PQUALIFIER%]%PARENT%". Cannot create child in "[%CQUALIFIER%]%CHILD%".") -end -.ENDFOR -0 - -1139500716 -fhillard -Parent must exist when inserting child constraint -.FOREACH_PARENT() -.ENDFOR - - -D7EF5824-1CB4-470F-A0B5-43708975465C -InsertTooManyChildren -InsertTooManyChildren -.FOREACH_PARENT("FKMAXCARD") -/* The cardinality of Parent "[%PQUALIFIER%]%PARENT%" in child "[%CQUALIFIER%]%CHILD%" cannot exceed %MAXCARD% */ -if .JOIN("update(%FK%)", "", " or") -begin - select @maxcard = (select count(*) - from [%CQUALIFIER%]%CHILD% old - where .JOIN("ins.%FK% = old.%FK%", "and ", "", ")") - from inserted ins - where .JOIN("ins.%FK% is not null", "and ", "", "") - group by .JOIN("ins.%FK%", ", ", "", "") - order by 1 - if @maxcard > %MAXCARD% - .ERROR(50007, "The maximum cardinality of a child has been exceeded! Cannot create child in "[%CQUALIFIER%]%CHILD%".") -end -.ENDFOR -0 - -1139500716 -fhillard -Cannot exceed the maximum cardinality constraint when inserting the child -@maxcard int, -.FOREACH_PARENT("FKMAXCARD") -.ENDFOR - - -A77A318B-1301-4F6A-BBC5-C055091A06A8 -UpdateChangeColumn -UpdateChangeColumn -.FOREACH_COLUMN("NMFCOL") -/* Non modifiable column "%COLUMN%" cannot be modified */ -if update(%COLUMN%) - if exists (select 1 - from inserted i, deleted d - where i.%COLUMN% != d.%COLUMN%) - .ERROR(50001, "Non modifiable column "%COLUMN%" cannot be modified.") - -.ENDFOR -0 - -1139500716 -fhillard -Cannot modify non modifiable column constraint -.FOREACH_COLUMN("NMFCOL") -.ENDFOR - - -4EFE4FFE-202E-4C75-9721-E8B0899B4150 -UpdateChildParentExist -UpdateChildParentExist -.FOREACH_PARENT("FKNOTNULL") -/* Parent "[%PQUALIFIER%]%PARENT%" must exist when updating a child in "[%CQUALIFIER%]%CHILD%" */ -if .JOIN("update(%FK%)", "", " or") -begin - if (select count(*) - from [%PQUALIFIER%]%PARENT% t1, inserted t2 - where .JOIN("t1.%PK% = t2.%FK%", "and ", "", ") != @numrows") - .ERROR(50003, ""[%PQUALIFIER%]%PARENT%" does not exist. Cannot modify child in "[%CQUALIFIER%]%CHILD%".") -end -.ENDFOR -.FOREACH_PARENT("FKNULL") -/* Parent "[%PQUALIFIER%]%PARENT%" must exist when updating a child in "[%CQUALIFIER%]%CHILD%" */ -if .JOIN("update(%FK%)", "", " or") -begin - select @numnull = (select count(*) - from inserted - where .JOIN("%FK% is null", "and ", "", ")") - if @numnull != @numrows - if (select count(*) - from [%PQUALIFIER%]%PARENT% t1, inserted t2 - where .JOIN("t1.%PK% = t2.%FK%", "and ", "", ") != @numrows - @numnull") - .ERROR(50003, ""[%PQUALIFIER%]%PARENT%" does not exist. Cannot modify child in "[%CQUALIFIER%]%CHILD%".") -end -.ENDFOR -0 - -1139500716 -fhillard -Parent must exist when updating a child constraint -.FOREACH_PARENT() -.ENDFOR - - -492CB7F3-4FAD-408D-BCF8-3456D99A2956 -UpdateTooManyChildren -UpdateTooManyChildren -.FOREACH_PARENT("FKMAXCARD") -/* The cardinality of Parent "[%PQUALIFIER%]%PARENT%" in child "[%CQUALIFIER%]%CHILD%" cannot exceed %MAXCARD% */ -if .JOIN("update(%FK%)", "", " or") -begin - select @maxcard = (select count(*) - from [%CQUALIFIER%]%CHILD% old - where .JOIN("ins.%FK% = old.%FK%", "and ", "", ")") - from inserted ins - where .JOIN("ins.%FK% is not null", "and ", "", "") - group by .JOIN("ins.%FK%", ", ", "", "") - order by 1 - if @maxcard > %MAXCARD% - .ERROR(50007, "The maximum cardinality of a child has been exceeded! Cannot modify child in "[%CQUALIFIER%]%CHILD%".") -end -.ENDFOR -0 - -1139500716 -fhillard -Cannot exceed the maximum cardinality constraint when updating the child -@maxcard int, -.FOREACH_PARENT("FKMAXCARD") -.ENDFOR - - -71F081D5-423C-45FE-8E01-024EAE7EE809 -UpdateChildChangeParent -UpdateChildChangeParent -.FOREACH_PARENT("FKCANTCHG") -/* Cannot modify parent code of "[%PQUALIFIER%]%PARENT%" in child "[%CQUALIFIER%]%CHILD%" */ -if .JOIN("update(%FK%)", "", " or") - if exists (select 1 - from inserted i, deleted d - where .JOIN("i.%FK% != d.%FK%", "", " or", ")") - .ERROR(50004, "Cannot modify parent code of "[%PQUALIFIER%]%PARENT%" in child "[%CQUALIFIER%]%CHILD%".") - -.ENDFOR -0 - -1139500716 -fhillard -Cannot change parent constraint -.FOREACH_PARENT("FKCANTCHG") -.ENDFOR - - -1A4B830A-9252-4D38-9152-C28069BA7FA5 -UpdateParentRestrict -UpdateParentRestrict -.FOREACH_CHILD("UPDATE RESTRICT") -/* Cannot modify parent code in "[%PQUALIFIER%]%PARENT%" if children still exist in "[%CQUALIFIER%]%CHILD%" */ -if .PKCOLN("update(%COLUMN%)", "", " or") -begin - if exists (select 1 - from [%CQUALIFIER%]%CHILD% t2, inserted i1, deleted d1 - where .JOIN("t2.%FK% = d1.%PK%", "and ") - and (.JOIN("i1.%PK% != d1.%PK%", "or ", "", "))") - .ERROR(50005, "Children still exist in "[%CQUALIFIER%]%CHILD%". Cannot modify parent code in "[%PQUALIFIER%]%PARENT%".") -end - -.ENDFOR -0 - -1139500716 -fhillard -Update restrict constraint -.FOREACH_CHILD("UPDATE RESTRICT") -.ENDFOR - - -3F0CB4CA-E455-464B-8504-7DCD48F9EFDD -UpdateParentCascade -UpdateParentCascade -declare cIns cursor local for select - .PKCOLN("%COLUMN%", "", ",") - from inserted -declare cDel cursor local for select - .PKCOLN("%COLUMN%", "", ",") - from deleted - -.FOREACH_CHILD("UPDATE CASCADE") -/* Modify parent code of "[%PQUALIFIER%]%PARENT%" for all children in "[%CQUALIFIER%]%CHILD%" */ -if .PKCOLN("update(%COLUMN%)", "", " or") -begin - if @@rowcount = 1 - update [%CQUALIFIER%]%CHILD% - set .JOIN("%FK% = i1.%PK%", "", ",") - from [%CQUALIFIER%]%CHILD% t2, inserted i1, deleted d1 - where .JOIN("t2.%FK% = d1.%PK%", "and ") - and (.JOIN("i1.%PK% != d1.%PK%", "", " or", ")") - else - begin - open cIns - open cDel - fetch cIns into .PKCOLN("@ins_%COLUMN%", "", ",") - fetch cDel into .PKCOLN("@del_%COLUMN%", "", ",") - - while (@@fetch_status = 0) - begin - update [%CQUALIFIER%]%CHILD% - set .JOIN("%FK% = @ins_%PK%", "", ",") - where .JOIN("%FK% = @del_%PK%", "and ") - fetch cIns into .PKCOLN("@ins_%COLUMN%", "", ",") - fetch cDel into .PKCOLN("@del_%COLUMN%", "", ",") - end - close cIns - close cDel - end -end -.ENDFOR - -0 - -1081437635 -mledier -Update cascade constraint -.PKCOLN("@ins_%COLUMN% %DATATYPE%", "", ",", ",") -.PKCOLN("@del_%COLUMN% %DATATYPE%", "", ",", ",") - - -5A23EF6C-236F-474A-A819-4CC8B4DB4990 -UpdateParentSetNull -UpdateParentSetNull -.FOREACH_CHILD("UPDATE SETNULL") -/* Set parent code of "[%PQUALIFIER%]%PARENT%" to NULL in "[%CQUALIFIER%]%CHILD%" */ -if .PKCOLN("update(%COLUMN%)", "", " or") -begin - update [%CQUALIFIER%]%CHILD% - set .JOIN("%FK% = NULL", "", ",") - from [%CQUALIFIER%]%CHILD% t2, inserted i1, deleted d1 - where .JOIN("t2.%FK% = d1.%PK%", "and ") - and (.JOIN("i1.%PK% != d1.%PK%", "", " or", ")") -end - -.ENDFOR -0 - -933262535 -stevensj -Set parent code to null in child -.FOREACH_CHILD("UPDATE SETNULL") -.ENDFOR - - -D3182607-AA84-46E4-A759-6EEE8F3782EA -UpdateParentSetDefault -UpdateParentSetDefault -.DEFINE "_DEFAULT" "NULL" -.FOREACH_CHILD("UPDATE SETDEFAULT") -/* Set parent code of "[%PQUALIFIER%]%PARENT%" to default in "[%CQUALIFIER%]%CHILD%" */ -if .PKCOLN("update(%COLUMN%)", "", " or") -begin - update [%CQUALIFIER%]%CHILD% - set .JOIN("%FK% = %DEFAULT%", "", ",") - from [%CQUALIFIER%]%CHILD% t2, inserted i1, deleted d1 - where .JOIN("t2.%FK% = d1.%PK%", "and ") - and (.JOIN("i1.%PK% != d1.%PK%", "", " or", ")") -end - -.ENDFOR -0 - -933262535 -stevensj -Set parent code to default in child -.FOREACH_CHILD("UPDATE SETDEFAULT") -.ENDFOR - - -7D958953-EC47-4B3B-AD0E-FE4832E0E857 -DeleteParentRestrict -DeleteParentRestrict -.FOREACH_CHILD("DELETE RESTRICT") -/* Cannot delete parent "[%PQUALIFIER%]%PARENT%" if children still exist in "[%CQUALIFIER%]%CHILD%" */ -if exists (select 1 - from [%CQUALIFIER%]%CHILD% t2, deleted t1 - where .JOIN("t2.%FK% = t1.%PK%", "and ", "", ")") - .ERROR(50006, "Children still exist in "[%CQUALIFIER%]%CHILD%". Cannot delete parent "[%PQUALIFIER%]%PARENT%".") - -.ENDFOR -0 - -1139500716 -fhillard -Delete restrict constraint -.FOREACH_CHILD("DELETE RESTRICT") -.ENDFOR - - -E5D08B90-231B-4219-8904-B42A33496F7D -DeleteParentCascade -DeleteParentCascade -.FOREACH_CHILD("DELETE CASCADE") -/* Delete all children in "[%CQUALIFIER%]%CHILD%" */ -delete [%CQUALIFIER%]%CHILD% -from [%CQUALIFIER%]%CHILD% t2, deleted t1 -where .JOIN("t2.%FK% = t1.%PK%", "and ") - -.ENDFOR -0 - -933262501 -stevensj -Delete cascade constraint -.FOREACH_CHILD("DELETE CASCADE") -.ENDFOR - - -B947EC18-633C-42D0-94DA-B45B6960383C -DeleteParentSetNull -DeleteParentSetNull -.FOREACH_CHILD("DELETE SETNULL") -/* Set parent code of "[%PQUALIFIER%]%PARENT%" to NULL in child "[%CQUALIFIER%]%CHILD%" */ -update [%CQUALIFIER%]%CHILD% - set .JOIN("%FK% = NULL", "", ",") -from [%CQUALIFIER%]%CHILD% t2, deleted t1 -where .JOIN("t2.%FK% = t1.%PK%", "and ") - -.ENDFOR -0 - -933262501 -stevensj -Delete set null constraint -.FOREACH_CHILD("DELETE SETNULL") -.ENDFOR - - -93722373-2CF7-4A49-9139-0A8068020984 -DeleteParentSetDefault -DeleteParentSetDefault -.DEFINE "_DEFAULT" "NULL" -.FOREACH_CHILD("DELETE SETDEFAULT") -/* Set parent code of "[%PQUALIFIER%]%PARENT%" to default in "[%CQUALIFIER%]%CHILD%" */ -update [%CQUALIFIER%]%CHILD% - set .JOIN("%FK% = %DEFAULT%", "", ",") -from [%CQUALIFIER%]%CHILD% t2, deleted t1 -where .JOIN("t2.%FK% = t1.%PK%", "and ") - -.ENDFOR -0 - -933262501 -stevensj -Delete set default constraint -.FOREACH_CHILD("DELETE SETDEFAULT") -.ENDFOR - - - - -6BB6E65B-2094-4BBB-8241-C977B12C33C6 -CLR All Server Trigger -CLR All Server Trigger -create trigger %TRIGGER% on %TRGSCOPE%[ with %Option%] %TRGTIME% %TRGEVENT% as -external name %Assembly.GeneratedName%.%MethodName% -1238509340 -psitja -1244794557 -psitja -CLR trigger -for -DDL_SERVER_LEVEL_EVENTS -All Server -ClassMethod -Trigger_ -2 - - -54CE6464-2644-47DE-B11C-2F7E1637F678 -CLR Database Trigger -CLR Database Trigger -create trigger %TRIGGER% on %TRGSCOPE%[ with %Option%] %TRGTIME% %TRGEVENT% as -external name %Assembly.GeneratedName%.%MethodName% -1238510188 -psitja -1244794585 -psitja -CLR trigger -for -DDL_DATABASE_LEVEL_EVENTS -Database -ClassMethod -Trigger_ -2 - - -5118B0DA-19D9-44A1-B3AE-094E141D228A -CLR Logon Trigger -CLR Logon Trigger -create trigger %TRIGGER% on %TRGSCOPE%[ with %Option%] %TRGTIME% %TRGEVENT% as -external name %Assembly.GeneratedName%.%MethodName% -1238510371 -psitja -1244794592 -psitja -CLR trigger -for -LOGON -All Server -ClassMethod -Trigger_ -2 - - -6C19FD3B-1607-4CDB-9510-2A8BE2BE340B -CLR Trigger -CLR Trigger -create trigger [%QUALIFIER%]%TRIGGER% on [%TABLQUALIFIER%]%TABLE%[ with %Option%] %TRGTIME% %TRGEVENT% as -external name %Assembly.GeneratedName%.%MethodName% -1146585127 -fleysour -1244794654 -psitja -CLR trigger - -ClassMethod -%TEMPLATE%_%.L:TABLE% - - -A003D7D2-EF11-41FE-8272-54CFB4AA33BD -InsertTrigger -InsertTrigger -create trigger [%QUALIFIER%]%TRIGGER% on [%TABLQUALIFIER%]%TABLE% for insert as -begin - declare - .DeclInsertChildParentExist - .DeclInsertTooManyChildren - @numrows int, - @numnull int, - @errno int, - @errmsg varchar(255) - - select @numrows = @@rowcount - if @numrows = 0 - return - - .InsertChildParentExist - .InsertTooManyChildren - - return - -/* Errors handling */ -error: - raiserror @errno @errmsg - rollback transaction -end - -0 - -933337536 -stevensj -Insert trigger - -ti_%.L:TABLE% - - - - - - -65D2F287-4A0D-4B03-9CE5-2FCB342094FC -UpdateTrigger -UpdateTrigger -create trigger [%QUALIFIER%]%TRIGGER% on [%TABLQUALIFIER%]%TABLE% for update as -begin - declare - .DeclUpdateChangeColumn - .DeclUpdateChildParentExist - .DeclUpdateTooManyChildren - .DeclUpdateChildChangeParent - .DeclUpdateParentRestrict - .DeclUpdateParentCascade - .DeclUpdateParentSetNull - .DeclUpdateParentSetDefault - @numrows int, - @numnull int, - @errno int, - @errmsg varchar(255) - - select @numrows = @@rowcount - if @numrows = 0 - return - - .UpdateChangeColumn - .UpdateChildParentExist - .UpdateTooManyChildren - .UpdateChildChangeParent - .UpdateParentRestrict - .UpdateParentCascade - .UpdateParentSetNull - .UpdateParentSetDefault - - return - -/* Errors handling */ -error: - raiserror @errno @errmsg - rollback transaction -end - -0 - -933337536 -stevensj -Update trigger - -Update -tu_%.L:TABLE% - - - - - - - - - - - - -50C9E31A-6289-4A9F-BE7E-30E5CED413F3 -DeleteTrigger -DeleteTrigger -create trigger [%QUALIFIER%]%TRIGGER% on [%TABLQUALIFIER%]%TABLE% for delete as -begin - declare - .DeclDeleteParentRestrict - .DeclDeleteParentCascade - .DeclDeleteParentSetNull - .DeclDeleteParentSetDefault - @numrows int, - @errno int, - @errmsg varchar(255) - - select @numrows = @@rowcount - if @numrows = 0 - return - - .DeleteParentRestrict - .DeleteParentCascade - .DeleteParentSetNull - .DeleteParentSetDefault - - return - -/* Errors handling */ -error: - raiserror @errno @errmsg - rollback transaction -end - -0 - -933337536 -stevensj -Delete trigger - -Delete -td_%.L:TABLE% - - - - - - - - - -F57709C3-6901-4BE3-AA7A-34B1E1A45310 -InsteadOfInsertTrigger -InsteadOfInsertTrigger -/* -Specifies instruction set that will be executed instead of Insert event on view %TABLE% -*/ -create trigger %TRIGGER% on %TABLE% %Time% %Event% as -begin - return; -end - -1171291401 -mledier -1240990692 -mledier -Specifies instruction set that will be executed instead of Insert event on view %TABLE% - -instead of -%TEMPLATE%_%.L:TABLE% -1 - - -904D6542-116E-4B97-A75A-026BE6E45740 -InsteadOfUpdateTrigger -InsteadOfUpdateTrigger -/* -Specifies instruction set that will be executed instead of Update event on view %TABLE% -*/ -create trigger %TRIGGER% on %TABLE% %Time% %Event% as -begin - return; -end - -1171292115 -mledier -1240990692 -mledier -Specifies instruction set that will be executed instead of Update event on view %TABLE% - -instead of -update -%TEMPLATE%_%.L:TABLE% -1 - - -DD5699BE-A489-406F-9CFD-42D0C5A14882 -InsteadOfDeleteTrigger -InsteadOfDeleteTrigger -/* -Specifies instruction set that will be executed instead of Insert event on view %TABLE% -*/ -create trigger %TRIGGER% on %TABLE% %Time% %Event% as -begin - return; -end - -1171292149 -mledier -1240990692 -mledier -Specifies instruction set that will be executed instead of Delete event on view %TABLE% - -instead of -delete -%TEMPLATE%_%.L:TABLE% -1 - - -467C9CBD-E83B-4D69-BA2A-AF6942AF133E -Database Trigger -Database Trigger -create trigger %TRIGGER% on %TRGSCOPE%[ with %Option%] %TRGTIME% %TRGEVENT% -as -begin - -end -1243500113 -psitja -1243500389 -psitja -for -DDL_EVENTS -Database -Trigger_ -2 - - -21390317-8BD6-4DC7-8568-E50D7635945E -All Server Trigger -All Server Trigger -create trigger %TRIGGER% on %TRGSCOPE%[ with %Option%] %TRGTIME% %TRGEVENT% -as -begin - -end -1243500358 -psitja -1243500700 -psitja -for -DDL_SERVER_LEVEL_EVENTS -All Server -Trigger_ -2 - - - - - \ No newline at end of file diff --git a/template/T4/Icons/FontAwesomeIcon.tt b/template/T4/Icons/FontAwesomeIcon.tt deleted file mode 100644 index 55028d533..000000000 --- a/template/T4/Icons/FontAwesomeIcon.tt +++ /dev/null @@ -1,34 +0,0 @@ -<#@ template language="C#" hostSpecific="true" #> -<#@ assembly name="System.Core" #> -<#@ import namespace="System.Linq" #> -<#@ import namespace="System.Globalization" #> -<#@ import namespace="System.Collections.Generic" #> -<#@ import namespace="System.Text.RegularExpressions" #> -<# Process(); #> -<#+ - readonly Regex regex = new Regex(@"^\.fa-(.*)\:before \{$", RegexOptions.Compiled | RegexOptions.Multiline); - TextInfo textInfo = CultureInfo.InvariantCulture.TextInfo; - - public void Process() - { - WriteLine("using System.ComponentModel;"); - WriteLine(""); - WriteLine("namespace Util.Ui.Enums"); - WriteLine("{"); - WriteLine("\tpublic enum FontAwesomeIcon"); - WriteLine("\t{"); - string css = System.IO.File.ReadAllText(Host.ResolvePath("./node_modules/font-awesome/css/font-awesome.css")); - css = css.Replace("\r\n", "\n"); - foreach (Match match in regex.Matches(css)) - { - WriteLine("\t\t/// "); - WriteLine($"\t\t/// fa-{match.Groups[1].Value}"); - WriteLine("\t\t/// "); - WriteLine($"\t\t[Description(\"fa-{match.Groups[1].Value}\")]"); - WriteLine("\t\t" + String.Join(null, match.Groups[1].Value.Split(new char[]{'-'}, StringSplitOptions.RemoveEmptyEntries) - .Select(x => (char.IsDigit(x[0]) ? ("_" + x[0]) + x.Substring(1) : textInfo.ToTitleCase(x)) )) + ","); - } - WriteLine("\t}"); - WriteLine("}"); - } -#> \ No newline at end of file diff --git a/template/T4/Icons/MaterialIcon.tt b/template/T4/Icons/MaterialIcon.tt deleted file mode 100644 index a1ef61fe2..000000000 --- a/template/T4/Icons/MaterialIcon.tt +++ /dev/null @@ -1,31 +0,0 @@ -<#@ template language="C#" hostSpecific="true" #> -<#@ assembly name="System.Core" #> -<#@ import namespace="System.Linq" #> -<#@ import namespace="System.Globalization" #> -<#@ import namespace="System.Collections.Generic" #> -<#@ import namespace="System.Text.RegularExpressions" #> -<# Process(); #> -<#+ - TextInfo textInfo = CultureInfo.InvariantCulture.TextInfo; - - public void Process() - { - WriteLine("using System.ComponentModel;"); - WriteLine(""); - WriteLine("namespace Util.Ui.Enums"); - WriteLine("{"); - WriteLine("\tpublic enum MaterialIcon"); - WriteLine("\t{"); - foreach( var line in System.IO.File.ReadAllLines( Host.ResolvePath("./node_modules/material-design-icons/iconfont/codepoints")) ){ - var text = line.Split(' ')[0]; - WriteLine("\t\t/// "); - WriteLine($"\t\t/// {text}"); - WriteLine("\t\t/// "); - WriteLine($"\t\t[Description(\"{text}\")]"); - WriteLine("\t\t" + String.Join(null, text.Split(new char[]{'-'}, StringSplitOptions.RemoveEmptyEntries) - .Select(x => (char.IsDigit(x[0]) ? ("_" + x[0]) + x.Substring(1) : textInfo.ToTitleCase(x)) )) + ","); - } - WriteLine("\t}"); - WriteLine("}"); - } -#> \ No newline at end of file diff --git a/test/Util.Aop.AspectCore.Tests/NotEmptyAttributeTest.cs b/test/Util.Aop.AspectCore.Tests/NotEmptyAttributeTest.cs new file mode 100644 index 000000000..faa92befe --- /dev/null +++ b/test/Util.Aop.AspectCore.Tests/NotEmptyAttributeTest.cs @@ -0,0 +1,40 @@ +using System; +using Util.Aop.AspectCore.Tests.Samples; +using Xunit; + +namespace Util.Aop.AspectCore.Tests; + +/// +/// 测试NotEmptyAttribute拦截器 +/// +public class NotEmptyAttributeTest { + /// + /// 测试服务 + /// + private readonly ITestService _service; + + /// + /// 测试初始化 + /// + public NotEmptyAttributeTest( ITestService service ) { + _service = service; + } + + /// + /// 测试传入空字符串抛出异常 + /// + [Fact] + public void TestNotEmpty_1() { + Assert.Throws( () => { + _service.GetNotEmptyValue( "" ); + } ); + } + + /// + /// 测试传入值 + /// + [Fact] + public void TestNotEmpty_2() { + Assert.Equal( "a", _service.GetNotEmptyValue( "a" ) ); + } +} \ No newline at end of file diff --git a/test/Util.Aop.AspectCore.Tests/NotNullAttributeTest.cs b/test/Util.Aop.AspectCore.Tests/NotNullAttributeTest.cs new file mode 100644 index 000000000..026330328 --- /dev/null +++ b/test/Util.Aop.AspectCore.Tests/NotNullAttributeTest.cs @@ -0,0 +1,48 @@ +using System; +using Util.Aop.AspectCore.Tests.Samples; +using Xunit; + +namespace Util.Aop.AspectCore.Tests; + +/// +/// 测试NotNullAttribute拦截器 +/// +public class NotNullAttributeTest { + /// + /// 测试服务 + /// + private readonly ITestService _service; + + /// + /// 测试初始化 + /// + public NotNullAttributeTest( ITestService service ) { + _service = service; + } + + /// + /// 测试传入null抛出异常 + /// + [Fact] + public void TestNotNull_1() { + Assert.Throws( () => { + _service.GetNotNullValue( null ); + } ); + } + + /// + /// 测试传入空字符串 + /// + [Fact] + public void TestNotNull_2() { + Assert.Equal( "", _service.GetNotNullValue( "" ) ); + } + + /// + /// 测试传入值 + /// + [Fact] + public void TestNotNull_3() { + Assert.Equal( "a", _service.GetNotNullValue( "a" ) ); + } +} \ No newline at end of file diff --git a/test/Util.Aop.AspectCore.Tests/Samples/ITestService.cs b/test/Util.Aop.AspectCore.Tests/Samples/ITestService.cs new file mode 100644 index 000000000..692f1b1ef --- /dev/null +++ b/test/Util.Aop.AspectCore.Tests/Samples/ITestService.cs @@ -0,0 +1,19 @@ +using Util.Dependency; + +namespace Util.Aop.AspectCore.Tests.Samples; + +/// +/// 测试服务 +/// +public interface ITestService : ISingletonDependency{ + /// + /// 获取值,值不能为空 + /// + /// 参数 + string GetNotEmptyValue( [NotEmpty] string value ); + /// + /// 获取值,值不能为null + /// + /// 参数 + string GetNotNullValue( [NotNull] string value ); +} \ No newline at end of file diff --git a/test/Util.Aop.AspectCore.Tests/Samples/TestService.cs b/test/Util.Aop.AspectCore.Tests/Samples/TestService.cs new file mode 100644 index 000000000..5bf0a2318 --- /dev/null +++ b/test/Util.Aop.AspectCore.Tests/Samples/TestService.cs @@ -0,0 +1,22 @@ +namespace Util.Aop.AspectCore.Tests.Samples; + +/// +/// 测试服务 +/// +public class TestService : ITestService{ + /// + /// 获取值 + /// + /// 参数 + public string GetNotEmptyValue( string value ) { + return value; + } + + /// + /// 获取值,值不能为null + /// + /// 参数 + public string GetNotNullValue( [NotNull] string value ) { + return value; + } +} \ No newline at end of file diff --git a/test/Util.Aop.AspectCore.Tests/Startup.cs b/test/Util.Aop.AspectCore.Tests/Startup.cs new file mode 100644 index 000000000..bc31e51c3 --- /dev/null +++ b/test/Util.Aop.AspectCore.Tests/Startup.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit.DependencyInjection.Logging; + +namespace Util.Aop.AspectCore.Tests; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + hostBuilder.AsBuild() + .AddAop() + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + } +} \ No newline at end of file diff --git a/test/Util.Aop.AspectCore.Tests/Util.Aop.AspectCore.Tests.csproj b/test/Util.Aop.AspectCore.Tests/Util.Aop.AspectCore.Tests.csproj new file mode 100644 index 000000000..9b226ea90 --- /dev/null +++ b/test/Util.Aop.AspectCore.Tests/Util.Aop.AspectCore.Tests.csproj @@ -0,0 +1,28 @@ + + + + $(NetTargetFramework) + false + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/Startup.cs b/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/Startup.cs new file mode 100644 index 000000000..ae5c29952 --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/Startup.cs @@ -0,0 +1,46 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit.DependencyInjection.Logging; +using Util.Aop; +using Util.Data.EntityFrameworkCore; +using Util.Helpers; +using Util.Sessions; +using Util.Tests.Infrastructure; +using Util.Tests.UnitOfWorks; + +namespace Util.Applications { + /// + /// + /// + public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .AsBuild() + .AddAop() + .AddMySqlUnitOfWork( Config.GetConnectionString( "connection" ) ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddSingleton(); + InitDatabase( services ); + } + + /// + /// ʼݿ + /// + private void InitDatabase( IServiceCollection services ) { + var unitOfWork = services.BuildServiceProvider().GetService(); + unitOfWork.EnsureDeleted(); + unitOfWork.EnsureCreated(); + } + } +} \ No newline at end of file diff --git a/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/Tests/CustomerQueryServiceTest.cs b/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/Tests/CustomerQueryServiceTest.cs new file mode 100644 index 000000000..6b69296b8 --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/Tests/CustomerQueryServiceTest.cs @@ -0,0 +1,215 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Util.Data.EntityFrameworkCore; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Queries; +using Util.Tests.Repositories; +using Util.Tests.Services; +using Util.Tests.UnitOfWorks; +using Xunit; +using Xunit.Abstractions; +using Xunit.DependencyInjection; + +namespace Util.Applications.Tests { + /// + /// 客户查询服务测试 + /// 说明: + /// 1. 测试查询服务操作 + /// + public class CustomerQueryServiceTest : TestBase { + + #region 测试初始化 + + /// + /// 测试输出 + /// + private readonly ITestOutputHelper _testOutputHelper; + /// + /// 客户仓储 + /// + private readonly ICustomerRepository _repository; + /// + /// 客户查询服务 + /// + private readonly ICustomerQueryService _service; + + /// + /// 测试初始化 + /// + public CustomerQueryServiceTest( ITestOutputHelperAccessor testOutputHelperAccessor, ICustomerRepository repository, + ICustomerQueryService service, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _testOutputHelper = testOutputHelperAccessor.Output; + _repository = repository; + _service = service; + } + + #endregion + + #region 辅助方法 + + /// + /// 添加客户 + /// + public async Task AddCustomerAsync() { + return ( await AddCustomersAsync( 1 ) ).First(); + } + + /// + /// 添加客户列表 + /// + /// 添加数量 + /// 客户姓名 + public async Task> AddCustomersAsync( int count = 2, string name = null ) { + var customers = CustomerFakeService.GetCustomers( count ); + customers.ForEach( customer => { + if ( name.IsEmpty() == false ) + customer.Name = name; + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + return customers.OrderBy( t => t.Code ).ToList(); + } + + /// + /// 打印更改跟踪调试信息 + /// + private void WriteTrackerDebug() { + _testOutputHelper.WriteLine( UnitOfWork.GetChangeTrackerDebugView() ); + } + + #endregion + + #region GetAllAsync + + /// + /// 测试获取全部实体 + /// + [Fact] + public async Task TestGetAllAsync() { + var entities = await AddCustomersAsync(); + var dtos = await _service.GetAllAsync(); + Assert.True( dtos.Count >= entities.Count ); + Assert.Equal( entities[0].Name, dtos.Find( t => t.Id == entities[0].Id.ToString() )?.Name ); + } + + #endregion + + #region GetByIdAsync + + /// + /// 测试通过标识获取实体 + /// + [Fact] + public async Task TestGetByIdAsync() { + var entity = await AddCustomerAsync(); + var dto = await _service.GetByIdAsync( entity.Id ); + Assert.Equal( entity.Name, dto.Name ); + } + + #endregion + + #region GetByIdsAsync + + /// + /// 测试通过标识列表获取实体列表 + /// + [Fact] + public async Task TestGetByIdsAsync() { + var entities = await AddCustomersAsync(); + var dtos = await _service.GetByIdsAsync( entities.Select( t => t.Id ).Join() ); + Assert.Equal( entities.Count, dtos.Count ); + Assert.Equal( entities[0].Name, dtos.Find( t => t.Id == entities[0].Id.ToString() )?.Name ); + } + + #endregion + + #region QueryAsync + + /// + /// 测试查询 + /// + [Fact] + public async Task TestQueryAsync() { + //常量 + var name = "TestQueryAsync"; + + //添加客户实体列表 + await AddCustomersAsync(); + var entities = await AddCustomersAsync( 5, name ); + + //查询 + var query = new CustomerQuery { + Name = name, + Order = "Code" + }; + var dtos = await _service.QueryAsync( query ); + WriteTrackerDebug(); + Assert.True( dtos.Count == entities.Count ); + Assert.Equal( entities[0].Id.ToString(), dtos[0].Id ); + Assert.Equal( entities[4].Id.ToString(), dtos[4].Id ); + } + + #endregion + + #region PageQueryAsync + + /// + /// 测试分页查询 - 第1页 + /// + [Fact] + public async Task TestPageQueryAsync_1() { + //常量 + var name = "TestPageQueryAsync_1"; + + //添加客户实体列表 + await AddCustomersAsync(); + var entities = await AddCustomersAsync( 5, name ); + + //查询 + var query = new CustomerQuery { + PageSize = 3, + Page = 1, + Name = name, + Order = "Code" + }; + var dtos = await _service.PageQueryAsync( query ); + WriteTrackerDebug(); + Assert.Equal( 3, dtos.Data.Count ); + Assert.Equal( entities[0].Id.ToString(), dtos.Data[0].Id ); + Assert.Equal( entities[1].Id.ToString(), dtos.Data[1].Id ); + Assert.Equal( entities[2].Id.ToString(), dtos.Data[2].Id ); + } + + /// + /// 测试分页查询 - 第2页 + /// + [Fact] + public async Task TestPageQueryAsync_2() { + //常量 + var name = "TestPageQueryAsync_2"; + + //添加客户实体列表 + await AddCustomersAsync(); + var entities = await AddCustomersAsync( 5, name ); + + //查询 + var query = new CustomerQuery { + PageSize = 3, + Page = 2, + Name = name, + Order = "Code" + }; + var dtos = await _service.PageQueryAsync( query ); + WriteTrackerDebug(); + Assert.Equal( 2, dtos.Data.Count ); + Assert.Equal( entities[3].Id.ToString(), dtos.Data[0].Id ); + Assert.Equal( entities[4].Id.ToString(), dtos.Data[1].Id ); + } + + #endregion + } +} diff --git a/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/Tests/CustomerServiceTest.cs b/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/Tests/CustomerServiceTest.cs new file mode 100644 index 000000000..2025e37c3 --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/Tests/CustomerServiceTest.cs @@ -0,0 +1,40 @@ +using System.Threading.Tasks; +using Util.Tests.Fakes; +using Util.Tests.Services; +using Xunit; + +namespace Util.Applications.Tests { + /// + /// 客户服务测试 + /// + public class CustomerServiceTest { + /// + /// 客户服务 + /// + private readonly ICustomerService _service; + + /// + /// 测试初始化 + /// + public CustomerServiceTest( ICustomerService service ) { + _service = service; + } + + /// + /// 测试创建 + /// + [Fact] + public async Task TestCreateAsync() { + //创建 + var dto = CustomerFakeService.GetCustomerDto(); + var id = await _service.CreateAsync( dto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.NotNull( result ); + Assert.Equal( id, result.Id ); + Assert.Equal( dto.Code, result.Code ); + Assert.Equal( dto.Name, result.Name ); + } + } +} \ No newline at end of file diff --git a/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/Tests/ProductServiceTest.cs b/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/Tests/ProductServiceTest.cs new file mode 100644 index 000000000..660d9048c --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/Tests/ProductServiceTest.cs @@ -0,0 +1,217 @@ +using System.Threading.Tasks; +using Util.Tests.Dtos; +using Util.Tests.Fakes; +using Util.Tests.Models; +using Util.Tests.Services; +using Xunit; + +namespace Util.Applications.Tests { + /// + /// 产品服务测试 + /// + public class ProductServiceTest { + + #region 测试初始化 + + /// + /// 产品服务 + /// + private readonly IProductService _service; + + /// + /// 测试初始化 + /// + public ProductServiceTest( IProductService service ) { + _service = service; + } + + #endregion + + #region CreateAsync + + /// + /// 测试创建 + /// + [Fact] + public async Task TestCreateAsync() { + //创建 + var dto = ProductFakeService.GetProductDto(); + var id = await _service.CreateAsync( dto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.NotNull( result ); + Assert.Equal( id, result.Id ); + Assert.Equal( dto.Code, result.Code ); + Assert.Equal( dto.Name, result.Name ); + } + + #endregion + + #region UpdateAsync + + /// + /// 测试修改 + /// + [Fact] + public async Task TestUpdateAsync() { + //常量 + var code = "TestUpdateAsync"; + + //创建 + var dto = ProductFakeService.GetProductDto(); + var id = await _service.CreateAsync( dto ); + + //修改 + var updateDto = await _service.GetByIdAsync( id ); + updateDto.Code = code; + await _service.UpdateAsync( updateDto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( code, result.Code ); + } + + #endregion + + #region DeleteAsync + + /// + /// 测试删除 + /// + [Fact] + public async Task TestDeleteAsync() { + //创建 + var dto = ProductFakeService.GetProductDto(); + var id = await _service.CreateAsync( dto ); + + //删除 + await _service.DeleteAsync( id ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Null( result ); + } + + #endregion + + #region ExtraProperties + + /// + /// 测试扩展属性 - 添加简单扩展属性 + /// + [Fact] + public async Task TestExtraProperties_1() { + //常量 + var value = "TestExtraProperties_1"; + + //创建 + var dto = new ProductDto { TestProperty1 = value }; + var id = await _service.CreateAsync( dto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( value, result.TestProperty1 ); + } + + /// + /// 测试扩展属性 - 修改简单扩展属性 + /// + [Fact] + public async Task TestExtraProperties_2() { + //常量 + var oldValue = "TestExtraProperties_2_1"; + var newValue = "TestExtraProperties_2_2"; + + //添加实体 + var dto = new ProductDto { TestProperty1 = oldValue }; + var id = await _service.CreateAsync( dto ); + + //验证 + var oldDto = await _service.GetByIdAsync( id ); + Assert.Equal( oldValue, oldDto.TestProperty1 ); + + //修改实体 + oldDto.TestProperty1 = newValue; + await _service.UpdateAsync( oldDto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( newValue, result.TestProperty1 ); + } + + /// + /// 测试扩展属性 - 添加复杂扩展属性 + /// + [Fact] + public async Task TestExtraProperties_3() { + //常量 + var code = 2; + var name = "TestExtraProperties_3"; + + //添加实体 + var dto = new ProductDto { TestProperty2 = { Code = code, Name = name } }; + var id = await _service.CreateAsync( dto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( code, result.TestProperty2.Code ); + Assert.Equal( name, result.TestProperty2.Name ); + } + + /// + /// 测试扩展属性 - 修改复杂扩展属性 + /// + [Fact] + public async Task TestExtraProperties_4() { + //常量 + var code = 2; + var oldName = "TestExtraProperties_4_1"; + var newName = "TestExtraProperties_4_2"; + + //添加实体 + var dto = new ProductDto { TestProperty2 = { Code = code, Name = oldName } }; + var id = await _service.CreateAsync( dto ); + + //验证 + var oldDto = await _service.GetByIdAsync( id ); + Assert.Equal( code, oldDto.TestProperty2.Code ); + Assert.Equal( oldName, oldDto.TestProperty2.Name ); + + //修改实体 + oldDto.TestProperty2.Name = newName; + await _service.UpdateAsync( oldDto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( newName, result.TestProperty2.Name ); + } + + /// + /// 测试扩展属性 - 添加对象集合扩展属性 + /// + [Fact] + public async Task TestExtraProperties_5() { + //常量 + var code = 1; + var name1 = "TestExtraProperties_5_1"; + var name2 = "TestExtraProperties_5_2"; + + //添加实体 + var dto = new ProductDto(); + dto.TestProperties.Add( new ProductItem2 { Code = code, Name = name1 } ); + dto.TestProperties.Add( new ProductItem2 { Code = code, Name = name2 } ); + var id = await _service.CreateAsync( dto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( 2, result.TestProperties.Count ); + Assert.Equal( code, result.TestProperties[0].Code ); + Assert.Equal( name1, result.TestProperties[0].Name ); + Assert.Equal( code, result.TestProperties[1].Code ); + Assert.Equal( name2, result.TestProperties[1].Name ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/Util.Application.EntityFrameworkCore.MySql.Tests.Integration.csproj b/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/Util.Application.EntityFrameworkCore.MySql.Tests.Integration.csproj new file mode 100644 index 000000000..762690890 --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/Util.Application.EntityFrameworkCore.MySql.Tests.Integration.csproj @@ -0,0 +1,39 @@ + + + + $(NetTargetFramework) + false + Util.Applications + Util.Applications.Startup + + + + + Always + + + Always + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + \ No newline at end of file diff --git a/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/appsettings.Development.json b/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..3cab8ec33 --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Trace" + } + }, + "ConnectionStrings": { + "connection": "server=127.0.0.1;user=root;password=admin;database=Util.Application.EntityFrameworkCore.Test;port=3306;charset=utf8" + } +} \ No newline at end of file diff --git a/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/appsettings.json b/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/appsettings.json new file mode 100644 index 000000000..07941f8a3 --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.MySql.Tests.Integration/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + }, + "ConnectionStrings": { + "connection": "server=mysql.common;user=root;password=admin;database=Util.Application.EntityFrameworkCore.Test;port=3306;charset=utf8" + } +} \ No newline at end of file diff --git a/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/Startup.cs b/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/Startup.cs new file mode 100644 index 000000000..f0c6f4ca9 --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/Startup.cs @@ -0,0 +1,48 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit.DependencyInjection.Logging; +using Util.Aop; +using Util.Data.EntityFrameworkCore; +using Util.Dates; +using Util.Helpers; +using Util.Sessions; +using Util.Tests.Infrastructure; +using Util.Tests.UnitOfWorks; + +namespace Util.Applications { + /// + /// + /// + public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .AsBuild() + .AddUtc() + .AddAop() + .AddPgSqlUnitOfWork( Config.GetConnectionString( "connection" ) ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddSingleton(); + InitDatabase( services ); + } + + /// + /// ʼݿ + /// + private void InitDatabase( IServiceCollection services ) { + var unitOfWork = services.BuildServiceProvider().GetService(); + unitOfWork.EnsureDeleted(); + unitOfWork.EnsureCreated(); + } + } +} \ No newline at end of file diff --git a/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/CustomerQueryServiceTest.cs b/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/CustomerQueryServiceTest.cs new file mode 100644 index 000000000..6b69296b8 --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/CustomerQueryServiceTest.cs @@ -0,0 +1,215 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Util.Data.EntityFrameworkCore; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Queries; +using Util.Tests.Repositories; +using Util.Tests.Services; +using Util.Tests.UnitOfWorks; +using Xunit; +using Xunit.Abstractions; +using Xunit.DependencyInjection; + +namespace Util.Applications.Tests { + /// + /// 客户查询服务测试 + /// 说明: + /// 1. 测试查询服务操作 + /// + public class CustomerQueryServiceTest : TestBase { + + #region 测试初始化 + + /// + /// 测试输出 + /// + private readonly ITestOutputHelper _testOutputHelper; + /// + /// 客户仓储 + /// + private readonly ICustomerRepository _repository; + /// + /// 客户查询服务 + /// + private readonly ICustomerQueryService _service; + + /// + /// 测试初始化 + /// + public CustomerQueryServiceTest( ITestOutputHelperAccessor testOutputHelperAccessor, ICustomerRepository repository, + ICustomerQueryService service, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _testOutputHelper = testOutputHelperAccessor.Output; + _repository = repository; + _service = service; + } + + #endregion + + #region 辅助方法 + + /// + /// 添加客户 + /// + public async Task AddCustomerAsync() { + return ( await AddCustomersAsync( 1 ) ).First(); + } + + /// + /// 添加客户列表 + /// + /// 添加数量 + /// 客户姓名 + public async Task> AddCustomersAsync( int count = 2, string name = null ) { + var customers = CustomerFakeService.GetCustomers( count ); + customers.ForEach( customer => { + if ( name.IsEmpty() == false ) + customer.Name = name; + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + return customers.OrderBy( t => t.Code ).ToList(); + } + + /// + /// 打印更改跟踪调试信息 + /// + private void WriteTrackerDebug() { + _testOutputHelper.WriteLine( UnitOfWork.GetChangeTrackerDebugView() ); + } + + #endregion + + #region GetAllAsync + + /// + /// 测试获取全部实体 + /// + [Fact] + public async Task TestGetAllAsync() { + var entities = await AddCustomersAsync(); + var dtos = await _service.GetAllAsync(); + Assert.True( dtos.Count >= entities.Count ); + Assert.Equal( entities[0].Name, dtos.Find( t => t.Id == entities[0].Id.ToString() )?.Name ); + } + + #endregion + + #region GetByIdAsync + + /// + /// 测试通过标识获取实体 + /// + [Fact] + public async Task TestGetByIdAsync() { + var entity = await AddCustomerAsync(); + var dto = await _service.GetByIdAsync( entity.Id ); + Assert.Equal( entity.Name, dto.Name ); + } + + #endregion + + #region GetByIdsAsync + + /// + /// 测试通过标识列表获取实体列表 + /// + [Fact] + public async Task TestGetByIdsAsync() { + var entities = await AddCustomersAsync(); + var dtos = await _service.GetByIdsAsync( entities.Select( t => t.Id ).Join() ); + Assert.Equal( entities.Count, dtos.Count ); + Assert.Equal( entities[0].Name, dtos.Find( t => t.Id == entities[0].Id.ToString() )?.Name ); + } + + #endregion + + #region QueryAsync + + /// + /// 测试查询 + /// + [Fact] + public async Task TestQueryAsync() { + //常量 + var name = "TestQueryAsync"; + + //添加客户实体列表 + await AddCustomersAsync(); + var entities = await AddCustomersAsync( 5, name ); + + //查询 + var query = new CustomerQuery { + Name = name, + Order = "Code" + }; + var dtos = await _service.QueryAsync( query ); + WriteTrackerDebug(); + Assert.True( dtos.Count == entities.Count ); + Assert.Equal( entities[0].Id.ToString(), dtos[0].Id ); + Assert.Equal( entities[4].Id.ToString(), dtos[4].Id ); + } + + #endregion + + #region PageQueryAsync + + /// + /// 测试分页查询 - 第1页 + /// + [Fact] + public async Task TestPageQueryAsync_1() { + //常量 + var name = "TestPageQueryAsync_1"; + + //添加客户实体列表 + await AddCustomersAsync(); + var entities = await AddCustomersAsync( 5, name ); + + //查询 + var query = new CustomerQuery { + PageSize = 3, + Page = 1, + Name = name, + Order = "Code" + }; + var dtos = await _service.PageQueryAsync( query ); + WriteTrackerDebug(); + Assert.Equal( 3, dtos.Data.Count ); + Assert.Equal( entities[0].Id.ToString(), dtos.Data[0].Id ); + Assert.Equal( entities[1].Id.ToString(), dtos.Data[1].Id ); + Assert.Equal( entities[2].Id.ToString(), dtos.Data[2].Id ); + } + + /// + /// 测试分页查询 - 第2页 + /// + [Fact] + public async Task TestPageQueryAsync_2() { + //常量 + var name = "TestPageQueryAsync_2"; + + //添加客户实体列表 + await AddCustomersAsync(); + var entities = await AddCustomersAsync( 5, name ); + + //查询 + var query = new CustomerQuery { + PageSize = 3, + Page = 2, + Name = name, + Order = "Code" + }; + var dtos = await _service.PageQueryAsync( query ); + WriteTrackerDebug(); + Assert.Equal( 2, dtos.Data.Count ); + Assert.Equal( entities[3].Id.ToString(), dtos.Data[0].Id ); + Assert.Equal( entities[4].Id.ToString(), dtos.Data[1].Id ); + } + + #endregion + } +} diff --git a/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/CustomerServiceTest.cs b/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/CustomerServiceTest.cs new file mode 100644 index 000000000..2025e37c3 --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/CustomerServiceTest.cs @@ -0,0 +1,40 @@ +using System.Threading.Tasks; +using Util.Tests.Fakes; +using Util.Tests.Services; +using Xunit; + +namespace Util.Applications.Tests { + /// + /// 客户服务测试 + /// + public class CustomerServiceTest { + /// + /// 客户服务 + /// + private readonly ICustomerService _service; + + /// + /// 测试初始化 + /// + public CustomerServiceTest( ICustomerService service ) { + _service = service; + } + + /// + /// 测试创建 + /// + [Fact] + public async Task TestCreateAsync() { + //创建 + var dto = CustomerFakeService.GetCustomerDto(); + var id = await _service.CreateAsync( dto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.NotNull( result ); + Assert.Equal( id, result.Id ); + Assert.Equal( dto.Code, result.Code ); + Assert.Equal( dto.Name, result.Name ); + } + } +} \ No newline at end of file diff --git a/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/ProductServiceTest.cs b/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/ProductServiceTest.cs new file mode 100644 index 000000000..660d9048c --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/ProductServiceTest.cs @@ -0,0 +1,217 @@ +using System.Threading.Tasks; +using Util.Tests.Dtos; +using Util.Tests.Fakes; +using Util.Tests.Models; +using Util.Tests.Services; +using Xunit; + +namespace Util.Applications.Tests { + /// + /// 产品服务测试 + /// + public class ProductServiceTest { + + #region 测试初始化 + + /// + /// 产品服务 + /// + private readonly IProductService _service; + + /// + /// 测试初始化 + /// + public ProductServiceTest( IProductService service ) { + _service = service; + } + + #endregion + + #region CreateAsync + + /// + /// 测试创建 + /// + [Fact] + public async Task TestCreateAsync() { + //创建 + var dto = ProductFakeService.GetProductDto(); + var id = await _service.CreateAsync( dto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.NotNull( result ); + Assert.Equal( id, result.Id ); + Assert.Equal( dto.Code, result.Code ); + Assert.Equal( dto.Name, result.Name ); + } + + #endregion + + #region UpdateAsync + + /// + /// 测试修改 + /// + [Fact] + public async Task TestUpdateAsync() { + //常量 + var code = "TestUpdateAsync"; + + //创建 + var dto = ProductFakeService.GetProductDto(); + var id = await _service.CreateAsync( dto ); + + //修改 + var updateDto = await _service.GetByIdAsync( id ); + updateDto.Code = code; + await _service.UpdateAsync( updateDto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( code, result.Code ); + } + + #endregion + + #region DeleteAsync + + /// + /// 测试删除 + /// + [Fact] + public async Task TestDeleteAsync() { + //创建 + var dto = ProductFakeService.GetProductDto(); + var id = await _service.CreateAsync( dto ); + + //删除 + await _service.DeleteAsync( id ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Null( result ); + } + + #endregion + + #region ExtraProperties + + /// + /// 测试扩展属性 - 添加简单扩展属性 + /// + [Fact] + public async Task TestExtraProperties_1() { + //常量 + var value = "TestExtraProperties_1"; + + //创建 + var dto = new ProductDto { TestProperty1 = value }; + var id = await _service.CreateAsync( dto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( value, result.TestProperty1 ); + } + + /// + /// 测试扩展属性 - 修改简单扩展属性 + /// + [Fact] + public async Task TestExtraProperties_2() { + //常量 + var oldValue = "TestExtraProperties_2_1"; + var newValue = "TestExtraProperties_2_2"; + + //添加实体 + var dto = new ProductDto { TestProperty1 = oldValue }; + var id = await _service.CreateAsync( dto ); + + //验证 + var oldDto = await _service.GetByIdAsync( id ); + Assert.Equal( oldValue, oldDto.TestProperty1 ); + + //修改实体 + oldDto.TestProperty1 = newValue; + await _service.UpdateAsync( oldDto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( newValue, result.TestProperty1 ); + } + + /// + /// 测试扩展属性 - 添加复杂扩展属性 + /// + [Fact] + public async Task TestExtraProperties_3() { + //常量 + var code = 2; + var name = "TestExtraProperties_3"; + + //添加实体 + var dto = new ProductDto { TestProperty2 = { Code = code, Name = name } }; + var id = await _service.CreateAsync( dto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( code, result.TestProperty2.Code ); + Assert.Equal( name, result.TestProperty2.Name ); + } + + /// + /// 测试扩展属性 - 修改复杂扩展属性 + /// + [Fact] + public async Task TestExtraProperties_4() { + //常量 + var code = 2; + var oldName = "TestExtraProperties_4_1"; + var newName = "TestExtraProperties_4_2"; + + //添加实体 + var dto = new ProductDto { TestProperty2 = { Code = code, Name = oldName } }; + var id = await _service.CreateAsync( dto ); + + //验证 + var oldDto = await _service.GetByIdAsync( id ); + Assert.Equal( code, oldDto.TestProperty2.Code ); + Assert.Equal( oldName, oldDto.TestProperty2.Name ); + + //修改实体 + oldDto.TestProperty2.Name = newName; + await _service.UpdateAsync( oldDto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( newName, result.TestProperty2.Name ); + } + + /// + /// 测试扩展属性 - 添加对象集合扩展属性 + /// + [Fact] + public async Task TestExtraProperties_5() { + //常量 + var code = 1; + var name1 = "TestExtraProperties_5_1"; + var name2 = "TestExtraProperties_5_2"; + + //添加实体 + var dto = new ProductDto(); + dto.TestProperties.Add( new ProductItem2 { Code = code, Name = name1 } ); + dto.TestProperties.Add( new ProductItem2 { Code = code, Name = name2 } ); + var id = await _service.CreateAsync( dto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( 2, result.TestProperties.Count ); + Assert.Equal( code, result.TestProperties[0].Code ); + Assert.Equal( name1, result.TestProperties[0].Name ); + Assert.Equal( code, result.TestProperties[1].Code ); + Assert.Equal( name2, result.TestProperties[1].Name ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration.csproj b/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration.csproj new file mode 100644 index 000000000..8b054c459 --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration.csproj @@ -0,0 +1,39 @@ + + + + $(NetTargetFramework) + false + Util.Applications + Util.Applications.Startup + + + + + Always + + + Always + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + \ No newline at end of file diff --git a/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/appsettings.Development.json b/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..8584c1f9f --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Trace" + } + }, + "ConnectionStrings": { + "connection": "server=127.0.0.1:15432;User Id=admin;password=admin;database=Util.Application.EntityFrameworkCore.Test" + } +} \ No newline at end of file diff --git a/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/appsettings.json b/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/appsettings.json new file mode 100644 index 000000000..af8b6a225 --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.PostgreSql.Tests.Integration/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + }, + "ConnectionStrings": { + "connection": "server=postgresql.common;User Id=admin;password=admin;database=Util.Application.EntityFrameworkCore.Test" + } +} \ No newline at end of file diff --git a/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/Startup.cs b/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/Startup.cs new file mode 100644 index 000000000..900f3fdbf --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/Startup.cs @@ -0,0 +1,46 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit.DependencyInjection.Logging; +using Util.Aop; +using Util.Data.EntityFrameworkCore; +using Util.Helpers; +using Util.Sessions; +using Util.Tests.Infrastructure; +using Util.Tests.UnitOfWorks; + +namespace Util.Applications { + /// + /// + /// + public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .AsBuild() + .AddAop() + .AddSqlServerUnitOfWork( Config.GetConnectionString( "connection" ) ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddSingleton(); + InitDatabase( services ); + } + + /// + /// ʼݿ + /// + private void InitDatabase( IServiceCollection services ) { + var unitOfWork = services.BuildServiceProvider().GetService(); + unitOfWork.EnsureDeleted(); + unitOfWork.EnsureCreated(); + } + } +} \ No newline at end of file diff --git a/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/CustomerQueryServiceTest.cs b/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/CustomerQueryServiceTest.cs new file mode 100644 index 000000000..6b69296b8 --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/CustomerQueryServiceTest.cs @@ -0,0 +1,215 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Util.Data.EntityFrameworkCore; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Queries; +using Util.Tests.Repositories; +using Util.Tests.Services; +using Util.Tests.UnitOfWorks; +using Xunit; +using Xunit.Abstractions; +using Xunit.DependencyInjection; + +namespace Util.Applications.Tests { + /// + /// 客户查询服务测试 + /// 说明: + /// 1. 测试查询服务操作 + /// + public class CustomerQueryServiceTest : TestBase { + + #region 测试初始化 + + /// + /// 测试输出 + /// + private readonly ITestOutputHelper _testOutputHelper; + /// + /// 客户仓储 + /// + private readonly ICustomerRepository _repository; + /// + /// 客户查询服务 + /// + private readonly ICustomerQueryService _service; + + /// + /// 测试初始化 + /// + public CustomerQueryServiceTest( ITestOutputHelperAccessor testOutputHelperAccessor, ICustomerRepository repository, + ICustomerQueryService service, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _testOutputHelper = testOutputHelperAccessor.Output; + _repository = repository; + _service = service; + } + + #endregion + + #region 辅助方法 + + /// + /// 添加客户 + /// + public async Task AddCustomerAsync() { + return ( await AddCustomersAsync( 1 ) ).First(); + } + + /// + /// 添加客户列表 + /// + /// 添加数量 + /// 客户姓名 + public async Task> AddCustomersAsync( int count = 2, string name = null ) { + var customers = CustomerFakeService.GetCustomers( count ); + customers.ForEach( customer => { + if ( name.IsEmpty() == false ) + customer.Name = name; + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + return customers.OrderBy( t => t.Code ).ToList(); + } + + /// + /// 打印更改跟踪调试信息 + /// + private void WriteTrackerDebug() { + _testOutputHelper.WriteLine( UnitOfWork.GetChangeTrackerDebugView() ); + } + + #endregion + + #region GetAllAsync + + /// + /// 测试获取全部实体 + /// + [Fact] + public async Task TestGetAllAsync() { + var entities = await AddCustomersAsync(); + var dtos = await _service.GetAllAsync(); + Assert.True( dtos.Count >= entities.Count ); + Assert.Equal( entities[0].Name, dtos.Find( t => t.Id == entities[0].Id.ToString() )?.Name ); + } + + #endregion + + #region GetByIdAsync + + /// + /// 测试通过标识获取实体 + /// + [Fact] + public async Task TestGetByIdAsync() { + var entity = await AddCustomerAsync(); + var dto = await _service.GetByIdAsync( entity.Id ); + Assert.Equal( entity.Name, dto.Name ); + } + + #endregion + + #region GetByIdsAsync + + /// + /// 测试通过标识列表获取实体列表 + /// + [Fact] + public async Task TestGetByIdsAsync() { + var entities = await AddCustomersAsync(); + var dtos = await _service.GetByIdsAsync( entities.Select( t => t.Id ).Join() ); + Assert.Equal( entities.Count, dtos.Count ); + Assert.Equal( entities[0].Name, dtos.Find( t => t.Id == entities[0].Id.ToString() )?.Name ); + } + + #endregion + + #region QueryAsync + + /// + /// 测试查询 + /// + [Fact] + public async Task TestQueryAsync() { + //常量 + var name = "TestQueryAsync"; + + //添加客户实体列表 + await AddCustomersAsync(); + var entities = await AddCustomersAsync( 5, name ); + + //查询 + var query = new CustomerQuery { + Name = name, + Order = "Code" + }; + var dtos = await _service.QueryAsync( query ); + WriteTrackerDebug(); + Assert.True( dtos.Count == entities.Count ); + Assert.Equal( entities[0].Id.ToString(), dtos[0].Id ); + Assert.Equal( entities[4].Id.ToString(), dtos[4].Id ); + } + + #endregion + + #region PageQueryAsync + + /// + /// 测试分页查询 - 第1页 + /// + [Fact] + public async Task TestPageQueryAsync_1() { + //常量 + var name = "TestPageQueryAsync_1"; + + //添加客户实体列表 + await AddCustomersAsync(); + var entities = await AddCustomersAsync( 5, name ); + + //查询 + var query = new CustomerQuery { + PageSize = 3, + Page = 1, + Name = name, + Order = "Code" + }; + var dtos = await _service.PageQueryAsync( query ); + WriteTrackerDebug(); + Assert.Equal( 3, dtos.Data.Count ); + Assert.Equal( entities[0].Id.ToString(), dtos.Data[0].Id ); + Assert.Equal( entities[1].Id.ToString(), dtos.Data[1].Id ); + Assert.Equal( entities[2].Id.ToString(), dtos.Data[2].Id ); + } + + /// + /// 测试分页查询 - 第2页 + /// + [Fact] + public async Task TestPageQueryAsync_2() { + //常量 + var name = "TestPageQueryAsync_2"; + + //添加客户实体列表 + await AddCustomersAsync(); + var entities = await AddCustomersAsync( 5, name ); + + //查询 + var query = new CustomerQuery { + PageSize = 3, + Page = 2, + Name = name, + Order = "Code" + }; + var dtos = await _service.PageQueryAsync( query ); + WriteTrackerDebug(); + Assert.Equal( 2, dtos.Data.Count ); + Assert.Equal( entities[3].Id.ToString(), dtos.Data[0].Id ); + Assert.Equal( entities[4].Id.ToString(), dtos.Data[1].Id ); + } + + #endregion + } +} diff --git a/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/CustomerServiceTest.cs b/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/CustomerServiceTest.cs new file mode 100644 index 000000000..2025e37c3 --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/CustomerServiceTest.cs @@ -0,0 +1,40 @@ +using System.Threading.Tasks; +using Util.Tests.Fakes; +using Util.Tests.Services; +using Xunit; + +namespace Util.Applications.Tests { + /// + /// 客户服务测试 + /// + public class CustomerServiceTest { + /// + /// 客户服务 + /// + private readonly ICustomerService _service; + + /// + /// 测试初始化 + /// + public CustomerServiceTest( ICustomerService service ) { + _service = service; + } + + /// + /// 测试创建 + /// + [Fact] + public async Task TestCreateAsync() { + //创建 + var dto = CustomerFakeService.GetCustomerDto(); + var id = await _service.CreateAsync( dto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.NotNull( result ); + Assert.Equal( id, result.Id ); + Assert.Equal( dto.Code, result.Code ); + Assert.Equal( dto.Name, result.Name ); + } + } +} \ No newline at end of file diff --git a/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/ProductServiceTest.cs b/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/ProductServiceTest.cs new file mode 100644 index 000000000..660d9048c --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/ProductServiceTest.cs @@ -0,0 +1,217 @@ +using System.Threading.Tasks; +using Util.Tests.Dtos; +using Util.Tests.Fakes; +using Util.Tests.Models; +using Util.Tests.Services; +using Xunit; + +namespace Util.Applications.Tests { + /// + /// 产品服务测试 + /// + public class ProductServiceTest { + + #region 测试初始化 + + /// + /// 产品服务 + /// + private readonly IProductService _service; + + /// + /// 测试初始化 + /// + public ProductServiceTest( IProductService service ) { + _service = service; + } + + #endregion + + #region CreateAsync + + /// + /// 测试创建 + /// + [Fact] + public async Task TestCreateAsync() { + //创建 + var dto = ProductFakeService.GetProductDto(); + var id = await _service.CreateAsync( dto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.NotNull( result ); + Assert.Equal( id, result.Id ); + Assert.Equal( dto.Code, result.Code ); + Assert.Equal( dto.Name, result.Name ); + } + + #endregion + + #region UpdateAsync + + /// + /// 测试修改 + /// + [Fact] + public async Task TestUpdateAsync() { + //常量 + var code = "TestUpdateAsync"; + + //创建 + var dto = ProductFakeService.GetProductDto(); + var id = await _service.CreateAsync( dto ); + + //修改 + var updateDto = await _service.GetByIdAsync( id ); + updateDto.Code = code; + await _service.UpdateAsync( updateDto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( code, result.Code ); + } + + #endregion + + #region DeleteAsync + + /// + /// 测试删除 + /// + [Fact] + public async Task TestDeleteAsync() { + //创建 + var dto = ProductFakeService.GetProductDto(); + var id = await _service.CreateAsync( dto ); + + //删除 + await _service.DeleteAsync( id ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Null( result ); + } + + #endregion + + #region ExtraProperties + + /// + /// 测试扩展属性 - 添加简单扩展属性 + /// + [Fact] + public async Task TestExtraProperties_1() { + //常量 + var value = "TestExtraProperties_1"; + + //创建 + var dto = new ProductDto { TestProperty1 = value }; + var id = await _service.CreateAsync( dto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( value, result.TestProperty1 ); + } + + /// + /// 测试扩展属性 - 修改简单扩展属性 + /// + [Fact] + public async Task TestExtraProperties_2() { + //常量 + var oldValue = "TestExtraProperties_2_1"; + var newValue = "TestExtraProperties_2_2"; + + //添加实体 + var dto = new ProductDto { TestProperty1 = oldValue }; + var id = await _service.CreateAsync( dto ); + + //验证 + var oldDto = await _service.GetByIdAsync( id ); + Assert.Equal( oldValue, oldDto.TestProperty1 ); + + //修改实体 + oldDto.TestProperty1 = newValue; + await _service.UpdateAsync( oldDto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( newValue, result.TestProperty1 ); + } + + /// + /// 测试扩展属性 - 添加复杂扩展属性 + /// + [Fact] + public async Task TestExtraProperties_3() { + //常量 + var code = 2; + var name = "TestExtraProperties_3"; + + //添加实体 + var dto = new ProductDto { TestProperty2 = { Code = code, Name = name } }; + var id = await _service.CreateAsync( dto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( code, result.TestProperty2.Code ); + Assert.Equal( name, result.TestProperty2.Name ); + } + + /// + /// 测试扩展属性 - 修改复杂扩展属性 + /// + [Fact] + public async Task TestExtraProperties_4() { + //常量 + var code = 2; + var oldName = "TestExtraProperties_4_1"; + var newName = "TestExtraProperties_4_2"; + + //添加实体 + var dto = new ProductDto { TestProperty2 = { Code = code, Name = oldName } }; + var id = await _service.CreateAsync( dto ); + + //验证 + var oldDto = await _service.GetByIdAsync( id ); + Assert.Equal( code, oldDto.TestProperty2.Code ); + Assert.Equal( oldName, oldDto.TestProperty2.Name ); + + //修改实体 + oldDto.TestProperty2.Name = newName; + await _service.UpdateAsync( oldDto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( newName, result.TestProperty2.Name ); + } + + /// + /// 测试扩展属性 - 添加对象集合扩展属性 + /// + [Fact] + public async Task TestExtraProperties_5() { + //常量 + var code = 1; + var name1 = "TestExtraProperties_5_1"; + var name2 = "TestExtraProperties_5_2"; + + //添加实体 + var dto = new ProductDto(); + dto.TestProperties.Add( new ProductItem2 { Code = code, Name = name1 } ); + dto.TestProperties.Add( new ProductItem2 { Code = code, Name = name2 } ); + var id = await _service.CreateAsync( dto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( 2, result.TestProperties.Count ); + Assert.Equal( code, result.TestProperties[0].Code ); + Assert.Equal( name1, result.TestProperties[0].Name ); + Assert.Equal( code, result.TestProperties[1].Code ); + Assert.Equal( name2, result.TestProperties[1].Name ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/ResourceServiceTest.cs b/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/ResourceServiceTest.cs new file mode 100644 index 000000000..ade8264b3 --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/ResourceServiceTest.cs @@ -0,0 +1,223 @@ +using System.Threading.Tasks; +using Util.Tests.Fakes; +using Util.Tests.Services; +using Xunit; + +namespace Util.Applications.Tests { + /// + /// 资源服务测试 - 测试树形 + /// + public class ResourceServiceTest { + + #region 测试初始化 + + /// + /// 资源服务 + /// + private readonly IResourceService _service; + + /// + /// 测试初始化 + /// + /// 资源服务 + public ResourceServiceTest( IResourceService service ) { + _service = service; + } + + #endregion + + #region CreateAsync + + /// + /// 测试创建 - 添加根节点 + /// + [Fact] + public async Task TestCreateAsync_1() { + //添加资源 + var dto = ResourceDtoFakeService.GetResourceDto(); + dto.Name = "TestCreateAsync_1"; + var id = await _service.CreateAsync( dto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( $"{id},", result.Path ); + Assert.Equal( 1, result.Level ); + } + + /// + /// 测试创建 - 添加2个子节点 + /// + [Fact] + public async Task TestCreateAsync_2() { + //添加根节点 + var root = ResourceDtoFakeService.GetResourceDto(); + root.Name = "TestCreateAsync_2_Root"; + var rootId = await _service.CreateAsync( root ); + + //添加子节点1 + var dto = ResourceDtoFakeService.GetResourceDto(); + dto.Name = "TestCreateAsync_2_Child_1"; + dto.ParentId = rootId; + var childId = await _service.CreateAsync( dto ); + + //添加子节点2 + var dto2 = ResourceDtoFakeService.GetResourceDto(); + dto2.Name = "TestCreateAsync_2_Child_2"; + dto2.ParentId = rootId; + var childId2 = await _service.CreateAsync( dto2 ); + + //验证子节点1 + var result = await _service.GetByIdAsync( childId ); + Assert.Equal( $"{rootId},{childId},", result.Path ); + Assert.Equal( 2, result.Level ); + + //验证子节点2 + result = await _service.GetByIdAsync( childId2 ); + Assert.Equal( $"{rootId},{childId2},", result.Path ); + Assert.Equal( 2, result.Level ); + } + + #endregion + + #region UpdateAsync + + /// + /// 测试修改 - 1个节点 + /// + [Fact] + public async Task TestUpdateAsync_1() { + //定义变量 + var name = "TestUpdateAsync_1"; + + //添加资源 + var dto = ResourceDtoFakeService.GetResourceDto(); + var id = await _service.CreateAsync( dto ); + + //修改 + dto = await _service.GetByIdAsync( id ); + dto.Name = name; + await _service.UpdateAsync( dto ); + + //验证 + var result = await _service.GetByIdAsync( id ); + Assert.Equal( name, result.Name ); + Assert.Equal( $"{id},", result.Path ); + Assert.Equal( 1, result.Level ); + } + + /// + /// 测试修改 - 3级节点 + /// + [Fact] + public async Task TestUpdateAsync_2() { + //添加根节点 + var root = ResourceDtoFakeService.GetResourceDto(); + root.Name = "Root"; + var rootId = await _service.CreateAsync( root ); + + //添加2级节点A + var nodeA = ResourceDtoFakeService.GetResourceDto(); + nodeA.Name = "NodeA"; + nodeA.ParentId = rootId; + var aId = await _service.CreateAsync( nodeA ); + + //在A节点下添加A1节点 + var nodeA1 = ResourceDtoFakeService.GetResourceDto(); + nodeA1.Name = "NodeA1"; + nodeA1.ParentId = aId; + var a1Id = await _service.CreateAsync( nodeA1 ); + + //添加2级节点B + var nodeB = ResourceDtoFakeService.GetResourceDto(); + nodeB.Name = "NodeB"; + nodeB.ParentId = rootId; + var bId = await _service.CreateAsync( nodeB ); + + //在B节点下添加B1节点 + var nodeB1 = ResourceDtoFakeService.GetResourceDto(); + nodeB1.Name = "NodeB1"; + nodeB1.ParentId = bId; + var b1Id = await _service.CreateAsync( nodeB1 ); + + //将A节点移动到B节点下 + nodeA = await _service.GetByIdAsync( aId ); + nodeA.ParentId = bId; + await _service.UpdateAsync( nodeA ); + + //验证A节点 + nodeA = await _service.GetByIdAsync( aId ); + Assert.Equal( $"{rootId},{bId},{aId},", nodeA.Path ); + Assert.Equal( 3, nodeA.Level ); + + //验证A1节点 + nodeA1 = await _service.GetByIdAsync( a1Id ); + Assert.Equal( $"{rootId},{bId},{aId},{a1Id},", nodeA1.Path ); + Assert.Equal( 4, nodeA1.Level ); + + //验证B1节点 + nodeB1 = await _service.GetByIdAsync( b1Id ); + Assert.Equal( $"{rootId},{bId},{b1Id},", nodeB1.Path ); + Assert.Equal( 3, nodeB1.Level ); + } + + #endregion + + #region EnableAsync + + /// + /// 测试启用 + /// + [Fact] + public async Task TestEnableAsync_1() { + //添加资源1 + var dto = ResourceDtoFakeService.GetResourceDto(); + dto.Enabled = false; + var id = await _service.CreateAsync( dto ); + + //添加资源2 + var dto2 = ResourceDtoFakeService.GetResourceDto(); + dto2.Enabled = false; + var id2 = await _service.CreateAsync( dto2 ); + + //启用资源 + var ids = $"{id},{id2}"; + await _service.EnableAsync( ids ); + + //验证 + var result = await _service.GetByIdsAsync( ids ); + Assert.True( result[0].Enabled ); + Assert.True( result[1].Enabled ); + } + + #endregion + + #region DisableAsync + + /// + /// 测试禁用 + /// + [Fact] + public async Task TestDisableAsync_1() { + //添加资源1 + var dto = ResourceDtoFakeService.GetResourceDto(); + dto.Enabled = true; + var id = await _service.CreateAsync( dto ); + + //添加资源2 + var dto2 = ResourceDtoFakeService.GetResourceDto(); + dto2.Enabled = true; + var id2 = await _service.CreateAsync( dto2 ); + + //启用资源 + var ids = $"{id},{id2}"; + await _service.DisableAsync( ids ); + + //验证 + var result = await _service.GetByIdsAsync( ids ); + Assert.False( result[0].Enabled ); + Assert.False( result[1].Enabled ); + } + + #endregion + } +} diff --git a/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration.csproj b/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration.csproj new file mode 100644 index 000000000..d8f59f5ec --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration.csproj @@ -0,0 +1,39 @@ + + + + $(NetTargetFramework) + false + Util.Applications + Util.Applications.Startup + + + + + Always + + + Always + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/appsettings.Development.json b/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..45e8537c5 --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Trace" + } + }, + "ConnectionStrings": { + "connection": "Server=.;Database=Util.Application.EntityFrameworkCore.Test;uid=sa;pwd=Pass@word;MultipleActiveResultSets=true;TrustServerCertificate=true" + } +} diff --git a/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/appsettings.json b/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/appsettings.json new file mode 100644 index 000000000..9e90b8f71 --- /dev/null +++ b/test/Util.Application.EntityFrameworkCore.SqlServer.Tests.Integration/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + }, + "ConnectionStrings": { + "connection": "Server=192.168.31.123\\sql2019;Database=Util.Application.EntityFrameworkCore.Test;uid=sa;pwd=sa;MultipleActiveResultSets=true;TrustServerCertificate=true" + } +} \ No newline at end of file diff --git a/test/Util.Application.Tests/Dtos/CustomerDto.cs b/test/Util.Application.Tests/Dtos/CustomerDto.cs new file mode 100644 index 000000000..183d592f0 --- /dev/null +++ b/test/Util.Application.Tests/Dtos/CustomerDto.cs @@ -0,0 +1,87 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Domain.Biz.Enums; + +namespace Util.Applications.Dtos { + /// + /// 客户参数 + /// + public class CustomerDto : DtoBase { + /// + /// 编码 + /// + [Description( "编码" )] + [MaxLength( 100 )] + public string Code { get; set; } + /// + /// 姓名 + /// + [Description( "姓名" )] + [MaxLength( 200 )] + public string Name { get; set; } + /// + /// 昵称 + /// + [Description( "昵称" )] + [MaxLength( 50 )] + public string Nickname { get; set; } + /// + /// 性别 + /// + [Description( "性别" )] + public Gender? Gender { get; set; } + /// + /// 出生日期 + /// + [Description( "出生日期" )] + public DateTime? Birthday { get; set; } + /// + /// 民族 + /// + [Description( "民族" )] + public Nation? Nation { get; set; } + /// + /// 手机号 + /// + [Description( "手机号" )] + [MaxLength( 50 )] + public string Phone { get; set; } + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + [MaxLength( 500 )] + public string Email { get; set; } + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + /// + /// 版本号 + /// + [Description( "版本号" )] + public byte[] Version { get; set; } + } +} \ No newline at end of file diff --git a/test/Util.Application.Tests/Dtos/TreeDto.cs b/test/Util.Application.Tests/Dtos/TreeDto.cs new file mode 100644 index 000000000..36a7b3c4e --- /dev/null +++ b/test/Util.Application.Tests/Dtos/TreeDto.cs @@ -0,0 +1,15 @@ +using Util.Applications.Trees; + +namespace Util.Applications.Dtos { + /// + /// 树形参数 + /// + public class TreeDto : TreeDtoBase { + + public string Text { get; set; } + + public override string GetText() { + return Text; + } + } +} diff --git a/test/Util.Application.Tests/ExceptionExtensionsTest.cs b/test/Util.Application.Tests/ExceptionExtensionsTest.cs new file mode 100644 index 000000000..fcee5ea1e --- /dev/null +++ b/test/Util.Application.Tests/ExceptionExtensionsTest.cs @@ -0,0 +1,86 @@ +using System; +using AspectCore.DynamicProxy; +using Util.Aop; +using Util.Exceptions; +using Util.Properties; +using Xunit; + +namespace Util.Applications { + /// + /// 异常扩展测试 + /// + public class ExceptionExtensionsTest { + /// + /// 测试获取原始异常 + /// + [Fact] + public void TestGetRawException() { + var exception = new AspectInvocationException( null, new Exception( "a" ) ); + Assert.Equal( "a",exception.GetRawException().Message ); + } + + /// + /// 测试获取异常提示 - 验证空 + /// + [Fact] + public void TestGetPrompt_Null() { + Exception exception = null; + Assert.Null( exception.GetPrompt() ); + } + + /// + /// 测试获取异常提示 - 生产环境隐藏系统异常消息 + /// + [Fact] + public void TestGetPrompt_IsProduction_True() { + var exception = new Exception( "a" ); + Assert.Equal( R.SystemError, exception.GetPrompt( true ) ); + } + + /// + /// 测试获取异常提示 - 非生产环境显示系统异常消息 + /// + [Fact] + public void TestGetPrompt_IsDevelopment_False() { + var exception = new Exception( "a" ); + Assert.Equal( "a", exception.GetPrompt() ); + } + + /// + /// 测试获取异常提示 - Warning异常生产环境正常显示 + /// + [Fact] + public void TestGetPrompt_Warning_IsProduction_True() { + var exception = new Warning( "a" ); + Assert.Equal( "a", exception.GetPrompt( true ) ); + } + + /// + /// 测试获取异常提示 - 非生产环境显示Warning异常消息 + /// + [Fact] + public void TestGetPrompt_Warning_IsProduction_False() { + var exception = new Warning( "a" ); + Assert.Equal( "a", exception.GetPrompt() ); + } + + /// + /// 测试获取异常提示 - 显示Warning多层异常消息 + /// + [Fact] + public void TestGetPrompt_Warning_2Level() { + var rawException = new Exception( "a" ); + var exception = new Warning( rawException ); + Assert.Equal( "a", exception.GetPrompt() ); + } + + /// + /// 测试获取异常提示 - 显示原始异常消息 + /// + [Fact] + public void TestGetPrompt_AspectInvocationException() { + var exception = new AspectInvocationException( null, new Exception( "a" ) ); + Assert.Equal( "a", exception.GetPrompt() ); + } + } +} diff --git a/test/Util.Application.Tests/Fakes/CustomerFakeService.cs b/test/Util.Application.Tests/Fakes/CustomerFakeService.cs new file mode 100644 index 000000000..b1febff33 --- /dev/null +++ b/test/Util.Application.Tests/Fakes/CustomerFakeService.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using AutoBogus; +using Util.Applications.Models; + +namespace Util.Applications.Fakes { + /// + /// 客户模拟数据服务 + /// + public static class CustomerFakeService { + /// + /// 获取客户 + /// + public static Customer GetCustomer() { + return new AutoFaker().Generate(); + } + + /// + /// 获取客户列表 + /// + /// 行数 + public static List GetCustomers( int count ) { + return new AutoFaker().Generate( count ); + } + } +} \ No newline at end of file diff --git a/test/Util.Application.Tests/Models/Customer.cs b/test/Util.Application.Tests/Models/Customer.cs new file mode 100644 index 000000000..8afd762da --- /dev/null +++ b/test/Util.Application.Tests/Models/Customer.cs @@ -0,0 +1,116 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Domain; +using Util.Domain.Biz.Enums; +using Util.Domain.Entities; + +namespace Util.Applications.Models { + /// + /// 客户 + /// + [Description( "客户" )] + public class Customer : AggregateRoot,IDelete{ + /// + /// 初始化客户 + /// + public Customer() : this( 0 ) { + } + + /// + /// 初始化客户 + /// + /// 客户标识 + public Customer( int id ) : base( id ) { + } + + /// + /// 编码 + /// + [Description( "编码" )] + [MaxLength( 100 )] + public string Code { get; set; } + /// + /// 姓名 + /// + [Description( "姓名" )] + [MaxLength( 200 )] + public string Name { get; set; } + /// + /// 昵称 + /// + [Description( "昵称" )] + [MaxLength( 50 )] + public string Nickname { get; set; } + /// + /// 性别 + /// + [Description( "性别" )] + public Gender? Gender { get; set; } + /// + /// 出生日期 + /// + [Description( "出生日期" )] + public DateTime? Birthday { get; set; } + /// + /// 民族 + /// + [Description( "民族" )] + public Nation? Nation { get; set; } + /// + /// 手机号 + /// + [Description( "手机号" )] + [MaxLength( 50 )] + public string Phone { get; set; } + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + [MaxLength( 500 )] + public string Email { get; set; } + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + + /// + /// 添加变更列表 + /// + protected override void AddChanges( Customer other ) { + AddChange( t => t.Code, other.Code ); + AddChange( t => t.Name, other.Name ); + AddChange( t => t.Nickname, other.Nickname ); + AddChange( t => t.Gender, other.Gender ); + AddChange( t => t.Birthday, other.Birthday ); + AddChange( t => t.Nation, other.Nation ); + AddChange( t => t.Phone, other.Phone ); + AddChange( t => t.Email, other.Email ); + AddChange( t => t.CreationTime, other.CreationTime ); + AddChange( t => t.CreatorId, other.CreatorId ); + AddChange( t => t.LastModificationTime, other.LastModificationTime ); + AddChange( t => t.LastModifierId, other.LastModifierId ); + } + } +} \ No newline at end of file diff --git a/test/Util.Application.Tests/Queries/CustomerQuery.cs b/test/Util.Application.Tests/Queries/CustomerQuery.cs new file mode 100644 index 000000000..8ec3c12ef --- /dev/null +++ b/test/Util.Application.Tests/Queries/CustomerQuery.cs @@ -0,0 +1,121 @@ +using System; +using System.ComponentModel; +using Util.Data.Queries; +using Util.Domain.Biz.Enums; + +namespace Util.Applications.Queries { + /// + /// 客户查询参数 + /// + public class CustomerQuery : QueryParameter { + /// + /// 客户标识 + /// + [Description( "客户标识" )] + public int? CustomerId { get; set; } + /// + /// 编码 + /// + private string _code = string.Empty; + /// + /// 编码 + /// + [Description( "编码" )] + public string Code { + get => _code == null ? string.Empty : _code.Trim(); + set => _code = value; + } + /// + /// 姓名 + /// + private string _name = string.Empty; + /// + /// 姓名 + /// + [Description( "姓名" )] + public string Name { + get => _name == null ? string.Empty : _name.Trim(); + set => _name = value; + } + /// + /// 昵称 + /// + private string _nickname = string.Empty; + /// + /// 昵称 + /// + [Description( "昵称" )] + public string Nickname { + get => _nickname == null ? string.Empty : _nickname.Trim(); + set => _nickname = value; + } + /// + /// 性别 + /// + [Description( "性别" )] + public Gender? Gender { get; set; } + /// + /// 起始出生日期 + /// + public DateTime? BeginBirthday { get; set; } + /// + /// 结束出生日期 + /// + public DateTime? EndBirthday { get; set; } + /// + /// 民族 + /// + [Description( "民族" )] + public Nation? Nation { get; set; } + /// + /// 手机号 + /// + private string _phone = string.Empty; + /// + /// 手机号 + /// + [Description( "手机号" )] + public string Phone { + get => _phone == null ? string.Empty : _phone.Trim(); + set => _phone = value; + } + /// + /// 电子邮件 + /// + private string _email = string.Empty; + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + public string Email { + get => _email == null ? string.Empty : _email.Trim(); + set => _email = value; + } + /// + /// 起始创建时间 + /// + public DateTime? BeginCreationTime { get; set; } + /// + /// 结束创建时间 + /// + public DateTime? EndCreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 起始最后修改时间 + /// + public DateTime? BeginLastModificationTime { get; set; } + /// + /// 结束最后修改时间 + /// + public DateTime? EndLastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + } +} \ No newline at end of file diff --git a/test/Util.Application.Tests/Repositories/ICustomerRepository.cs b/test/Util.Application.Tests/Repositories/ICustomerRepository.cs new file mode 100644 index 000000000..f0a09586c --- /dev/null +++ b/test/Util.Application.Tests/Repositories/ICustomerRepository.cs @@ -0,0 +1,10 @@ +using Util.Applications.Models; +using Util.Domain.Repositories; + +namespace Util.Applications.Repositories { + /// + /// 客户仓储 + /// + public interface ICustomerRepository : IRepository { + } +} \ No newline at end of file diff --git a/test/Util.Application.Tests/Tests/TreeResultTest.cs b/test/Util.Application.Tests/Tests/TreeResultTest.cs new file mode 100644 index 000000000..9e03e96c4 --- /dev/null +++ b/test/Util.Application.Tests/Tests/TreeResultTest.cs @@ -0,0 +1,203 @@ +using System.Collections.Generic; +using Util.Applications.Dtos; +using Util.Applications.Trees; +using Xunit; + +namespace Util.Applications.Tests { + /// + /// 树形结果测试 + /// + public class TreeResultTest { + /// + /// 标识 + /// + public const string Id = "1"; + /// + /// 标识2 + /// + public const string Id2 = "2"; + /// + /// 标识3 + /// + public const string Id3 = "3"; + /// + /// 标识4 + /// + public const string Id4 = "4"; + /// + /// 标识5 + /// + public const string Id5 = "5"; + + /// + /// 获取根节点1 + /// + private TreeDto GetNode1() { + return new TreeDto { + Id = Id, + Path = $"{Id},", + Level = 1, + Expanded = true, + SortId = 1 + }; + } + + /// + /// 获取子节点2,父节点为根节点1 + /// + private TreeDto GetNode2() { + return new TreeDto { + Id = Id2, + ParentId = Id, + Path = $"{Id},{Id2},", + Level = 2, + Expanded = true, + SortId = 2 + }; + } + + /// + /// 获取根节点3 + /// + private TreeDto GetNode3() { + return new TreeDto { + Id = Id3, + Path = $"{Id3},", + Level = 1, + Expanded = true, + SortId = 2 + }; + } + + /// + /// 获取子节点4,父节点为根节点1 + /// + private TreeDto GetNode4() { + return new TreeDto { + Id = Id4, + ParentId = Id, + Level = 2, + Path = $"{Id},{Id4},", + SortId = 1, + Expanded = true + }; + } + + /// + /// 获取子节点5,父节点为节点4 + /// + private TreeDto GetNode5() { + return new TreeDto { + Id = Id5, + ParentId = Id4, + Path = $"{Id},{Id4},{Id5},", + }; + } + + /// + /// 测试获取结果 - 添加一个根节点 + /// + [Fact] + public void TestGetResult_1() { + //添加一个节点并获取结果 + var list = new List { GetNode1() }; + var result = new TreeResult( list ).GetResult(); + + //验证 + Assert.Single( result ); + var root = result[0]; + Assert.Equal( Id, root.Id ); + Assert.Empty( root.Children ); + Assert.True( root.Leaf ); + } + + /// + /// 测试获取结果 - 添加一个子节点 + /// + [Fact] + public void TestGetResult_2() { + //添加根节点和它的子节点 + var list = new List { GetNode1(), GetNode2() }; + var result = new TreeResult( list ).GetResult(); + + //验证 + Assert.Single( result ); + + var root = result[0]; + Assert.Equal( Id, root.Id ); + Assert.Single( root.Children ); + Assert.False( root.Leaf ); + + var child = root.Children[0]; + Assert.Equal( Id2, child.Id ); + Assert.True( child.Leaf ); + } + + /// + /// 测试获取结果 - 添加2个根节点,其中一个有子节点 + /// + [Fact] + public void TestGetResult_3() { + var list = new List { GetNode1(), GetNode2(), GetNode3() }; + var result = new TreeResult( list ).GetResult(); + Assert.Equal( 2, result.Count ); + + var root1 = result[0]; + Assert.Equal( Id, root1.Id ); + Assert.Single( root1.Children ); + Assert.Equal( Id2, root1.Children[0].Id ); + Assert.True( root1.Children[0].Leaf ); + + Assert.Equal( Id3, result[1].Id ); + Assert.Empty( result[1].Children ); + } + + /// + /// 测试获取结果 - 添加2个子节点 + /// + [Fact] + public void TestGetResult_4() { + var list = new List { GetNode1(), GetNode2(), GetNode3(), GetNode4() }; + var result = new TreeResult( list ).GetResult(); + Assert.Equal( 2, result.Count ); + + var root1 = result[0]; + Assert.Equal( Id, root1.Id ); + Assert.Equal( 2, root1.Children.Count ); + Assert.Equal( Id4, root1.Children[0].Id ); + Assert.True( root1.Children[0].Leaf ); + Assert.Equal( Id2, root1.Children[1].Id ); + Assert.True( root1.Children[1].Leaf ); + + Assert.Equal( Id3, result[1].Id ); + Assert.True( result[1].Leaf ); + } + + /// + /// 添加3级节点 + /// + [Fact] + public void TestGetResult_5() { + var list = new List { GetNode1(), GetNode2(), GetNode3(), GetNode4(), GetNode5() }; + var result = new TreeResult( list ).GetResult(); + Assert.Equal( 2, result.Count ); + + var root1 = result[0]; + Assert.Equal( Id, root1.Id ); + Assert.Equal( 2, root1.Children.Count ); + + Assert.Equal( Id4, root1.Children[0].Id ); + Assert.False( root1.Children[0].Leaf ); + + Assert.Equal( Id2, root1.Children[1].Id ); + Assert.True( root1.Children[1].Leaf ); + + Assert.Single( root1.Children[0].Children ); + Assert.Equal( Id5, root1.Children[0].Children[0].Id ); + Assert.True( root1.Children[0].Children[0].Leaf ); + + Assert.Equal( Id3, result[1].Id ); + } + } +} + diff --git a/test/Util.Application.Tests/Tests/TreeTableResultTest.cs b/test/Util.Application.Tests/Tests/TreeTableResultTest.cs new file mode 100644 index 000000000..c46ed2d57 --- /dev/null +++ b/test/Util.Application.Tests/Tests/TreeTableResultTest.cs @@ -0,0 +1,270 @@ +using System.Collections.Generic; +using Util.Applications.Dtos; +using Util.Applications.Trees; +using Xunit; + +namespace Util.Applications.Tests { + /// + /// 树形表格结果测试 + /// + public class TreeTableResultTest { + /// + /// 标识 + /// + public const string Id = "1"; + /// + /// 标识2 + /// + public const string Id2 = "2"; + /// + /// 标识3 + /// + public const string Id3 = "3"; + /// + /// 标识4 + /// + public const string Id4 = "4"; + /// + /// 标识5 + /// + public const string Id5 = "5"; + /// + /// 标识6 + /// + public const string Id6 = "6"; + /// + /// 标识7 + /// + public const string Id7 = "7"; + /// + /// 标识8 + /// + public const string Id8 = "8"; + + /// + /// 获取根节点1 + /// + private TreeDto GetNode1() { + return new TreeDto { + Id = Id, + Path = $"{Id},", + Level = 1, + Expanded = true, + SortId = 1 + }; + } + + /// + /// 获取子节点2,父节点为根节点1 + /// + private TreeDto GetNode2() { + return new TreeDto { + Id = Id2, + ParentId = Id, + Path = $"{Id},{Id2},", + Level = 2, + Expanded = true, + SortId = 2 + }; + } + + /// + /// 获取根节点3 + /// + private TreeDto GetNode3() { + return new TreeDto { + Id = Id3, + Path = $"{Id3},", + Level = 1, + Expanded = true, + SortId = 2 + }; + } + + /// + /// 获取子节点4,父节点为根节点1 + /// + private TreeDto GetNode4() { + return new TreeDto { + Id = Id4, + ParentId = Id, + Level = 2, + Path = $"{Id},{Id4},", + SortId = 1, + Expanded = true + }; + } + + /// + /// 获取子节点5,父节点为节点4 + /// + private TreeDto GetNode5() { + return new TreeDto { + Id = Id5, + ParentId = Id4, + Path = $"{Id},{Id4},{Id5},", + }; + } + + /// + /// 获取子节点6,父节点为根节点1 + /// + private TreeDto GetNode6() { + return new TreeDto { + Id = Id6, + ParentId = Id, + Path = $"{Id},{Id6},", + Level = 2, + Expanded = true, + SortId = 2 + }; + } + + /// + /// 获取子节点7,父节点为节点6 + /// + private TreeDto GetNode7() { + return new TreeDto { + Id = Id7, + ParentId = Id6, + Path = $"{Id},{Id6},{Id7},", + Level = 3, + Hide = true, + SortId = 3 + }; + } + + /// + /// 获取子节点8,父节点为节点6 + /// + private TreeDto GetNode8() { + return new TreeDto { + Id = Id8, + ParentId = Id6, + Path = $"{Id},{Id6},{Id8},", + Level = 3, + Hide = true, + SortId = 4 + }; + } + + /// + /// 测试获取结果 - 添加一个根节点 + /// + [Fact] + public void TestGetResult_1() { + //添加一个节点并获取结果 + var list = new List { GetNode1() }; + var result = new TestTreeTableResult( list ).GetResult(); + + //验证 + Assert.Single( result ); + var root = result[0]; + Assert.Equal( Id, root.Id ); + Assert.Empty( root.Children ); + Assert.True( root.Leaf ); + } + + /// + /// 测试获取结果 - 添加一个子节点 + /// + [Fact] + public void TestGetResult_2() { + //添加根节点和它的子节点 + var list = new List { GetNode1(), GetNode2() }; + var result = new TestTreeTableResult( list ).GetResult(); + + //验证 + Assert.Equal( 2,result.Count ); + + var root = result[0]; + Assert.Equal( Id, root.Id ); + Assert.Empty( root.Children ); + Assert.False( root.Leaf ); + + var child = result[1]; + Assert.Equal( Id2, child.Id ); + Assert.True( child.Leaf ); + } + + /// + /// 测试获取结果 - 添加2个根节点,其中一个有子节点 + /// + [Fact] + public void TestGetResult_3() { + var list = new List { GetNode1(), GetNode2(), GetNode3() }; + var result = new TestTreeTableResult( list ).GetResult(); + Assert.Equal( 3, result.Count ); + + var node1 = result[0]; + Assert.Equal( Id, node1.Id ); + + var node2 = result[1]; + Assert.Equal( Id2, node2.Id ); + + var node3 = result[2]; + Assert.Equal( Id3, node3.Id ); + } + + /// + /// 添加2个子节点 + /// + [Fact] + public void TestGetResult_4() { + var list = new List { GetNode1(), GetNode2(), GetNode3(), GetNode4() }; + var result = new TestTreeTableResult( list ).GetResult(); + Assert.Equal( 4, result.Count ); + + var node1 = result[0]; + Assert.Equal( Id, node1.Id ); + + var node4 = result[1]; + Assert.Equal( Id4, node4.Id ); + + var node2 = result[2]; + Assert.Equal( Id2, node2.Id ); + + var node3 = result[3]; + Assert.Equal( Id3, node3.Id ); + } + + /// + /// 添加3级节点 + /// + [Fact] + public void TestGetResult_5() { + var list = new List { GetNode1(), GetNode2(), GetNode3(), GetNode4(), GetNode5() }; + var result = new TestTreeTableResult( list ).GetResult(); + Assert.Equal( 5, result.Count ); + + var node1 = result[0]; + Assert.Equal( Id, node1.Id ); + + var node4 = result[1]; + Assert.Equal( Id4, node4.Id ); + + var node5 = result[2]; + Assert.Equal( Id5, node5.Id ); + + var node2 = result[3]; + Assert.Equal( Id2, node2.Id ); + + var node3 = result[4]; + Assert.Equal( Id3, node3.Id ); + } + + /// + /// 测试隐藏属性 - 下级节点全部隐藏时,应设置为叶节点 + /// + [Fact] + public void TestGetResult_6() { + var list = new List { GetNode1(), GetNode6(), GetNode7(), GetNode8() }; + var result = new TestTreeTableResult( list ).GetResult(); + + var node6 = result[1]; + Assert.Equal( Id6,node6.Id ); + Assert.True( node6.Leaf ); + } + } +} + diff --git a/test/Util.Application.Tests/TreeNodeExtensionsTest.cs b/test/Util.Application.Tests/TreeNodeExtensionsTest.cs new file mode 100644 index 000000000..cfd1dad1a --- /dev/null +++ b/test/Util.Application.Tests/TreeNodeExtensionsTest.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; +using Util.Applications.Trees; +using Xunit; + +namespace Util.Applications { + /// + /// 树形节点扩展测试 + /// + public class TreeNodeExtensionsTest { + /// + /// 测试获取缺失的父标识列表 - 1个根节点,不缺失 + /// + [Fact] + public void TestGetMissingParentIds_1() { + var nodes = new List { + new TreeNode1() + }; + var result = nodes.GetMissingParentIds(); + Assert.Empty( result ); + } + + /// + /// 测试获取缺失的父标识列表 - 1个2级节点,缺失根节点标识 + /// + [Fact] + public void TestGetMissingParentIds_2() { + var nodes = new List { + new TreeNode2() + }; + var result = nodes.GetMissingParentIds(); + Assert.Single( result ); + Assert.Equal( "1", result[0] ); + } + + /// + /// 测试获取缺失的父标识列表 - 2个节点,不缺失 + /// + [Fact] + public void TestGetMissingParentIds_3() { + var nodes = new List { + new TreeNode1(), + new TreeNode2() + }; + var result = nodes.GetMissingParentIds(); + Assert.Empty( result ); + } + + /// + /// 测试获取缺失的父标识列表 - 1个3级节点,缺失 + /// + [Fact] + public void TestGetMissingParentIds_4() { + var nodes = new List { + new TreeNode3() + }; + var result = nodes.GetMissingParentIds(); + Assert.Equal( 2,result.Count ); + Assert.Equal( "1", result[0] ); + Assert.Equal( "2", result[1] ); + } + + /// + /// 测试获取缺失的父标识列表 - 1个4级节点,缺失 + /// + [Fact] + public void TestGetMissingParentIds_5() { + var nodes = new List { + new TreeNode5() + }; + var result = nodes.GetMissingParentIds(); + Assert.Equal( 3, result.Count ); + Assert.Equal( "1", result[0] ); + Assert.Equal( "2", result[1] ); + Assert.Equal( "4", result[2] ); + } + } +} diff --git a/test/Util.Application.Tests/Trees/TestTreeTableResult.cs b/test/Util.Application.Tests/Trees/TestTreeTableResult.cs new file mode 100644 index 000000000..0388a97f1 --- /dev/null +++ b/test/Util.Application.Tests/Trees/TestTreeTableResult.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Util.Applications.Dtos; + +namespace Util.Applications.Trees { + /// + /// 树形表格结果 + /// + public class TestTreeTableResult : TreeTableResultBase> { + public TestTreeTableResult( IEnumerable data, bool async = false ) : base( data, async ) { + } + + protected override TreeDto ToDestinationNode( TreeDto node ) { + return new TreeDto { + Id = node.Id, + Leaf = node.Leaf + }; + } + + protected override List ToResult( List nodes ) { + return nodes; + } + } +} diff --git a/test/Util.Application.Tests/Trees/TreeNode.cs b/test/Util.Application.Tests/Trees/TreeNode.cs new file mode 100644 index 000000000..747a019f0 --- /dev/null +++ b/test/Util.Application.Tests/Trees/TreeNode.cs @@ -0,0 +1,60 @@ +namespace Util.Applications.Trees { + public class TreeNode1 : TreeDtoBase { + public string Text { get; set; } + public TreeNode1() { + Id = "1"; + Path = "1,"; + } + public override string GetText() { + return Text; + } + } + + public class TreeNode2 : TreeDtoBase { + public string Text { get; set; } + public TreeNode2() { + Id = "2"; + ParentId = "1"; + Path = "1,2,"; + } + public override string GetText() { + return Text; + } + } + + public class TreeNode3 : TreeDtoBase { + public string Text { get; set; } + public TreeNode3() { + Id = "3"; + ParentId = "2"; + Path = "1,2,3,"; + } + public override string GetText() { + return Text; + } + } + + public class TreeNode4 : TreeDtoBase { + public string Text { get; set; } + public TreeNode4() { + Id = "4"; + ParentId = "2"; + Path = "1,2,4,"; + } + public override string GetText() { + return Text; + } + } + + public class TreeNode5 : TreeDtoBase { + public string Text { get; set; } + public TreeNode5() { + Id = "5"; + ParentId = "4"; + Path = "1,2,4,5,"; + } + public override string GetText() { + return Text; + } + } +} diff --git a/test/Util.Application.Tests/Trees/TreeResult.cs b/test/Util.Application.Tests/Trees/TreeResult.cs new file mode 100644 index 000000000..875e5b6d6 --- /dev/null +++ b/test/Util.Application.Tests/Trees/TreeResult.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using Util.Applications.Dtos; + +namespace Util.Applications.Trees { + /// + /// 树形结果 + /// + public class TreeResult : TreeResultBase> { + public TreeResult( IEnumerable data, bool async = false ) : base( data, async ) { + } + + protected override TreeDto ToDestinationNode( TreeDto node ) { + return new TreeDto { + Id = node.Id, + Leaf = node.Leaf, + Children = node.Children + }; + } + + protected override List ToResult( List nodes ) { + return nodes; + } + } +} diff --git a/test/Util.Application.Tests/Util.Application.Tests.csproj b/test/Util.Application.Tests/Util.Application.Tests.csproj new file mode 100644 index 000000000..cbfe1bba0 --- /dev/null +++ b/test/Util.Application.Tests/Util.Application.Tests.csproj @@ -0,0 +1,36 @@ + + + + $(NetTargetFramework) + false + Util.Applications + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + diff --git a/test/Util.Application.WebApi.MySql.Tests.Integration/Properties/launchSettings.json b/test/Util.Application.WebApi.MySql.Tests.Integration/Properties/launchSettings.json new file mode 100644 index 000000000..e351472af --- /dev/null +++ b/test/Util.Application.WebApi.MySql.Tests.Integration/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Util.Application.WebApi.MySql.Tests.Integration": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:65194;http://localhost:65195" + } + } +} \ No newline at end of file diff --git a/test/Util.Application.WebApi.MySql.Tests.Integration/Startup.cs b/test/Util.Application.WebApi.MySql.Tests.Integration/Startup.cs new file mode 100644 index 000000000..451ab1287 --- /dev/null +++ b/test/Util.Application.WebApi.MySql.Tests.Integration/Startup.cs @@ -0,0 +1,62 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Aop; +using Util.Data.EntityFrameworkCore; +using Util.Helpers; +using Util.Http; +using Util.Tests.UnitOfWorks; +using Xunit.DependencyInjection.Logging; + +namespace Util.Applications { + /// + /// + /// + public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .ConfigureWebHostDefaults( webHostBuilder => { + webHostBuilder.UseTestServer() + .Configure( t => { + t.UseRouting(); + t.UseEndpoints( endpoints => { + endpoints.MapControllers(); + } ); + } ); + } ) + .AsBuild() + .AddAop() + .AddMySqlUnitOfWork( Config.GetConnectionString( "connection" ) ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddControllers(); + services.AddTransient( t => { + var client = new HttpClientService(); + client.SetHttpClient( t.GetService().GetTestClient() ); + return client; + } ); + InitDatabase( services ); + } + + /// + /// ʼݿ + /// + private void InitDatabase( IServiceCollection services ) { + var unitOfWork = services.BuildServiceProvider().GetService(); + unitOfWork.EnsureDeleted(); + unitOfWork.EnsureCreated(); + } + } +} diff --git a/test/Util.Application.WebApi.MySql.Tests.Integration/Tests/CustomerControllerTest.cs b/test/Util.Application.WebApi.MySql.Tests.Integration/Tests/CustomerControllerTest.cs new file mode 100644 index 000000000..9981b7fe7 --- /dev/null +++ b/test/Util.Application.WebApi.MySql.Tests.Integration/Tests/CustomerControllerTest.cs @@ -0,0 +1,115 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Util.Data; +using Util.Helpers; +using Util.Http; +using Util.Tests.Dtos; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Queries; +using Util.Tests.Services; +using Xunit; +using Xunit.Abstractions; +using Xunit.DependencyInjection; + +namespace Util.Applications.Tests { + /// + /// 客户控制器测试 + /// + public class CustomerControllerTest : ControllerTestBase { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _testOutputHelper; + /// + /// 客户服务 + /// + private readonly ICustomerService _service; + + /// + /// 测试初始化 + /// + public CustomerControllerTest( ITestOutputHelperAccessor testOutputHelperAccessor, ICustomerService service,IHttpClient client ) : base(client){ + _testOutputHelper = testOutputHelperAccessor.Output; + _service = service; + } + + /// + /// 测试获取单个实体 + /// + [Fact] + public async Task TestGetAsync() { + //创建实体 + var dto = CustomerFakeService.GetCustomerDto(); + var id = await _service.CreateAsync( dto ); + + //获取单个实体 + var url = $"/api/customer/{id}"; + var result = await GetAsync( url, dto ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.Equal( dto.Email, result.Data.Email ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } + + /// + /// 测试查询 + /// + [Fact] + public async Task TestQueryAsync() { + //常量 + var name = "TestQueryAsync"; + var url = "/api/customer/query"; + + //创建3个实体,前两个设置了名称 + var dtos = CustomerFakeService.GetCustomerDtos(3); + dtos[0].Name = name; + dtos[1].Name = name; + await _service.CreateAsync( dtos[0] ); + await _service.CreateAsync( dtos[1] ); + await _service.CreateAsync( dtos[2] ); + + //使用名称查询 + var query = new CustomerQuery { Name = name }; + var result = await GetAsync>( url, query ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.Equal( 2, result.Data.Count ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } + + /// + /// 测试分页查询 + /// + [Fact] + public async Task TestPageQueryAsync() { + //常量 + var name = "TestPageQueryAsync"; + var url = "/api/customer"; + + //创建5个实体,前4个设置了名称,并使用Id属性排序 + var dtos = CustomerFakeService.GetCustomerDtos( 5 ).OrderBy( t => t.Id ).ToList(); + for ( int i = 0; i < 4; i++ ) { + dtos[i].Name = name; + } + foreach ( var dto in dtos ) { + await _service.CreateAsync( dto ); + } + + //使用名称查询第二页,每页2行,并使用Id属性排序 + var query = new CustomerQuery { Page = 2, PageSize = 2, Name = name,Order = "Id" }; + var result = await GetAsync>( url, query ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.Equal( 2, result.Data.PageCount ); + Assert.Equal( 4, result.Data.Total ); + Assert.Equal( dtos[2].Email, result.Data.Data[0].Email ); + Assert.Equal( dtos[3].Email, result.Data.Data[1].Email ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } + } +} diff --git a/test/Util.Application.WebApi.MySql.Tests.Integration/Tests/ProductControllerTest.cs b/test/Util.Application.WebApi.MySql.Tests.Integration/Tests/ProductControllerTest.cs new file mode 100644 index 000000000..e2a3cc466 --- /dev/null +++ b/test/Util.Application.WebApi.MySql.Tests.Integration/Tests/ProductControllerTest.cs @@ -0,0 +1,132 @@ +using System.Threading.Tasks; +using Util.Helpers; +using Util.Http; +using Util.Tests.Dtos; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Services; +using Xunit; +using Xunit.Abstractions; +using Xunit.DependencyInjection; + +namespace Util.Applications.Tests { + /// + /// 产品控制器测试 + /// + public class ProductControllerTest : ControllerTestBase { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _testOutputHelper; + /// + /// 产品服务 + /// + private readonly IProductService _service; + + /// + /// 测试初始化 + /// + public ProductControllerTest( ITestOutputHelperAccessor testOutputHelperAccessor, IProductService service, IHttpClient client ) : base( client ) { + _testOutputHelper = testOutputHelperAccessor.Output; + _service = service; + } + + /// + /// 测试创建 + /// + [Fact] + public async Task TestCreateAsync() { + //服务地址 + var url = "/api/product"; + + //创建实体 + var dto = ProductFakeService.GetProductDto(); + var result = await PostAsync( url, dto ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.NotEmpty( result.Data.Id ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } + + /// + /// 测试修改 + /// + [Fact] + public async Task TestUpdateAsync() { + //常量 + var name = "TestUpdateAsync"; + + //创建实体 + var dto = ProductFakeService.GetProductDto(); + var id = await _service.CreateAsync( dto ); + + //服务地址 + var url = $"/api/product/{id}"; + + //修改实体 + dto = await _service.GetByIdAsync( id ); + dto.Name = name; + var result = await PutAsync( url, dto ); + + //重新获取实体 + dto = await _service.GetByIdAsync( id ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.Equal( name, dto.Name ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } + + /// + /// 测试删除 + /// + [Fact] + public async Task TestDeleteAsync() { + //创建实体 + var dto = ProductFakeService.GetProductDto(); + var id = await _service.CreateAsync( dto ); + + //服务地址 + var url = $"/api/product/{id}"; + + //删除实体 + var result = await DeleteAsync( url ); + + //重新获取实体 + dto = await _service.GetByIdAsync( id ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.Null( dto ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } + + /// + /// 测试批量删除 + /// + [Fact] + public async Task TestBatchDeleteAsync() { + //创建实体 + var dtos = ProductFakeService.GetProductDtos(2); + var id = await _service.CreateAsync( dtos[0] ); + var id2 = await _service.CreateAsync( dtos[1] ); + + //服务地址 + var url = "/api/product/delete"; + + //删除实体列表 + var result = await PostAsync( url,$"{id},{id2}" ); + + //重新获取实体 + var dto = await _service.GetByIdAsync( id ); + var dto2 = await _service.GetByIdAsync( id2 ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.Null( dto ); + Assert.Null( dto2 ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } + } +} \ No newline at end of file diff --git a/test/Util.Application.WebApi.MySql.Tests.Integration/Util.Application.WebApi.MySql.Tests.Integration.csproj b/test/Util.Application.WebApi.MySql.Tests.Integration/Util.Application.WebApi.MySql.Tests.Integration.csproj new file mode 100644 index 000000000..178568e0c --- /dev/null +++ b/test/Util.Application.WebApi.MySql.Tests.Integration/Util.Application.WebApi.MySql.Tests.Integration.csproj @@ -0,0 +1,41 @@ + + + + $(NetTargetFramework) + false + Util.Applications + Util.Applications.Startup + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + Always + + + Always + + + + \ No newline at end of file diff --git a/test/Util.Application.WebApi.MySql.Tests.Integration/appsettings.Development.json b/test/Util.Application.WebApi.MySql.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..d0fed6438 --- /dev/null +++ b/test/Util.Application.WebApi.MySql.Tests.Integration/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Trace" + } + }, + "ConnectionStrings": { + "connection": "server=127.0.0.1;user=root;password=admin;database=Util.WebApi.Test;port=3306;charset=utf8" + } +} \ No newline at end of file diff --git a/test/Util.Application.WebApi.MySql.Tests.Integration/appsettings.json b/test/Util.Application.WebApi.MySql.Tests.Integration/appsettings.json new file mode 100644 index 000000000..bc5e59b3f --- /dev/null +++ b/test/Util.Application.WebApi.MySql.Tests.Integration/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + }, + "ConnectionStrings": { + "connection": "server=mysql.common;user=root;password=admin;database=Util.WebApi.Test;port=3306;charset=utf8" + } +} \ No newline at end of file diff --git a/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Properties/launchSettings.json b/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Properties/launchSettings.json new file mode 100644 index 000000000..31c6eb2c6 --- /dev/null +++ b/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Util.Application.WebApi.PostgreSql.Tests.Integration": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:3603;http://localhost:3604" + } + } +} \ No newline at end of file diff --git a/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Startup.cs b/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Startup.cs new file mode 100644 index 000000000..7eab4fc0a --- /dev/null +++ b/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Startup.cs @@ -0,0 +1,64 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Aop; +using Util.Data.EntityFrameworkCore; +using Util.Dates; +using Util.Helpers; +using Util.Http; +using Util.Tests.UnitOfWorks; +using Xunit.DependencyInjection.Logging; + +namespace Util.Applications; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .ConfigureWebHostDefaults( webHostBuilder => { + webHostBuilder.UseTestServer() + .Configure( t => { + t.UseRouting(); + t.UseEndpoints( endpoints => { + endpoints.MapControllers(); + } ); + } ); + } ) + .AsBuild() + .AddUtc() + .AddAop() + .AddPgSqlUnitOfWork( Config.GetConnectionString( "connection" ) ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddControllers(); + services.AddTransient( t => { + var client = new HttpClientService(); + client.SetHttpClient( t.GetService().GetTestClient() ); + return client; + } ); + InitDatabase( services ); + } + + /// + /// ʼݿ + /// + private void InitDatabase( IServiceCollection services ) { + var unitOfWork = services.BuildServiceProvider().GetService(); + unitOfWork.EnsureDeleted(); + unitOfWork.EnsureCreated(); + } +} \ No newline at end of file diff --git a/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Tests/CustomerControllerTest.cs b/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Tests/CustomerControllerTest.cs new file mode 100644 index 000000000..2337a3cf6 --- /dev/null +++ b/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Tests/CustomerControllerTest.cs @@ -0,0 +1,115 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Util.Data; +using Util.Helpers; +using Util.Http; +using Util.Tests.Dtos; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Queries; +using Util.Tests.Services; +using Xunit; +using Xunit.Abstractions; +using Xunit.DependencyInjection; + +namespace Util.Applications.Tests; + +/// +/// 客户控制器测试 +/// +public class CustomerControllerTest : ControllerTestBase { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _testOutputHelper; + /// + /// 客户服务 + /// + private readonly ICustomerService _service; + + /// + /// 测试初始化 + /// + public CustomerControllerTest( ITestOutputHelperAccessor testOutputHelperAccessor, ICustomerService service,IHttpClient client ) : base(client){ + _testOutputHelper = testOutputHelperAccessor.Output; + _service = service; + } + + /// + /// 测试获取单个实体 + /// + [Fact] + public async Task TestGetAsync() { + //创建实体 + var dto = CustomerFakeService.GetCustomerDto(); + var id = await _service.CreateAsync( dto ); + + //获取单个实体 + var url = $"/api/customer/{id}"; + var result = await GetAsync( url, dto ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.Equal( dto.Email, result.Data.Email ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } + + /// + /// 测试查询 + /// + [Fact] + public async Task TestQueryAsync() { + //常量 + var name = "TestQueryAsync"; + var url = "/api/customer/query"; + + //创建3个实体,前两个设置了名称 + var dtos = CustomerFakeService.GetCustomerDtos(3); + dtos[0].Name = name; + dtos[1].Name = name; + await _service.CreateAsync( dtos[0] ); + await _service.CreateAsync( dtos[1] ); + await _service.CreateAsync( dtos[2] ); + + //使用名称查询 + var query = new CustomerQuery { Name = name }; + var result = await GetAsync>( url, query ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.Equal( 2, result.Data.Count ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } + + /// + /// 测试分页查询 + /// + [Fact] + public async Task TestPageQueryAsync() { + //常量 + var name = "TestPageQueryAsync"; + var url = "/api/customer"; + + //创建5个实体,前4个设置了名称,并使用Id属性排序 + var dtos = CustomerFakeService.GetCustomerDtos( 5 ).OrderBy( t => t.Id ).ToList(); + for ( int i = 0; i < 4; i++ ) { + dtos[i].Name = name; + } + foreach ( var dto in dtos ) { + await _service.CreateAsync( dto ); + } + + //使用名称查询第二页,每页2行,并使用Id属性排序 + var query = new CustomerQuery { Page = 2, PageSize = 2, Name = name,Order = "Id" }; + var result = await GetAsync>( url, query ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.Equal( 2, result.Data.PageCount ); + Assert.Equal( 4, result.Data.Total ); + Assert.Equal( dtos[2].Email, result.Data.Data[0].Email ); + Assert.Equal( dtos[3].Email, result.Data.Data[1].Email ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } +} \ No newline at end of file diff --git a/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Tests/ProductControllerTest.cs b/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Tests/ProductControllerTest.cs new file mode 100644 index 000000000..521f4a72b --- /dev/null +++ b/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Tests/ProductControllerTest.cs @@ -0,0 +1,132 @@ +using System.Threading.Tasks; +using Util.Helpers; +using Util.Http; +using Util.Tests.Dtos; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Services; +using Xunit; +using Xunit.Abstractions; +using Xunit.DependencyInjection; + +namespace Util.Applications.Tests; + +/// +/// 产品控制器测试 +/// +public class ProductControllerTest : ControllerTestBase { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _testOutputHelper; + /// + /// 产品服务 + /// + private readonly IProductService _service; + + /// + /// 测试初始化 + /// + public ProductControllerTest( ITestOutputHelperAccessor testOutputHelperAccessor, IProductService service, IHttpClient client ) : base( client ) { + _testOutputHelper = testOutputHelperAccessor.Output; + _service = service; + } + + /// + /// 测试创建 + /// + [Fact] + public async Task TestCreateAsync() { + //服务地址 + var url = "/api/product"; + + //创建实体 + var dto = ProductFakeService.GetProductDto(); + var result = await PostAsync( url, dto ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.NotEmpty( result.Data.Id ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } + + /// + /// 测试修改 + /// + [Fact] + public async Task TestUpdateAsync() { + //常量 + var name = "TestUpdateAsync"; + + //创建实体 + var dto = ProductFakeService.GetProductDto(); + var id = await _service.CreateAsync( dto ); + + //服务地址 + var url = $"/api/product/{id}"; + + //修改实体 + dto = await _service.GetByIdAsync( id ); + dto.Name = name; + var result = await PutAsync( url, dto ); + + //重新获取实体 + dto = await _service.GetByIdAsync( id ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.Equal( name, dto.Name ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } + + /// + /// 测试删除 + /// + [Fact] + public async Task TestDeleteAsync() { + //创建实体 + var dto = ProductFakeService.GetProductDto(); + var id = await _service.CreateAsync( dto ); + + //服务地址 + var url = $"/api/product/{id}"; + + //删除实体 + var result = await DeleteAsync( url ); + + //重新获取实体 + dto = await _service.GetByIdAsync( id ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.Null( dto ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } + + /// + /// 测试批量删除 + /// + [Fact] + public async Task TestBatchDeleteAsync() { + //创建实体 + var dtos = ProductFakeService.GetProductDtos(2); + var id = await _service.CreateAsync( dtos[0] ); + var id2 = await _service.CreateAsync( dtos[1] ); + + //服务地址 + var url = "/api/product/delete"; + + //删除实体列表 + var result = await PostAsync( url,$"{id},{id2}" ); + + //重新获取实体 + var dto = await _service.GetByIdAsync( id ); + var dto2 = await _service.GetByIdAsync( id2 ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.Null( dto ); + Assert.Null( dto2 ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } +} \ No newline at end of file diff --git a/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Util.Application.WebApi.PostgreSql.Tests.Integration.csproj b/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Util.Application.WebApi.PostgreSql.Tests.Integration.csproj new file mode 100644 index 000000000..1b2b3a806 --- /dev/null +++ b/test/Util.Application.WebApi.PostgreSql.Tests.Integration/Util.Application.WebApi.PostgreSql.Tests.Integration.csproj @@ -0,0 +1,41 @@ + + + + $(NetTargetFramework) + false + Util.Applications + Util.Applications.Startup + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + Always + + + Always + + + + \ No newline at end of file diff --git a/test/Util.Application.WebApi.PostgreSql.Tests.Integration/appsettings.Development.json b/test/Util.Application.WebApi.PostgreSql.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..ae905cf7b --- /dev/null +++ b/test/Util.Application.WebApi.PostgreSql.Tests.Integration/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Trace" + } + }, + "ConnectionStrings": { + "connection": "server=127.0.0.1:15432;User Id=admin;password=admin;database=Util.WebApi.Test" + } +} \ No newline at end of file diff --git a/test/Util.Application.WebApi.PostgreSql.Tests.Integration/appsettings.json b/test/Util.Application.WebApi.PostgreSql.Tests.Integration/appsettings.json new file mode 100644 index 000000000..833add564 --- /dev/null +++ b/test/Util.Application.WebApi.PostgreSql.Tests.Integration/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + }, + "ConnectionStrings": { + "connection": "server=postgresql.common;User Id=admin;password=admin;database=Util.WebApi.Test" + } +} \ No newline at end of file diff --git a/test/Util.Application.WebApi.SqlServer.Tests.Integration/Properties/launchSettings.json b/test/Util.Application.WebApi.SqlServer.Tests.Integration/Properties/launchSettings.json new file mode 100644 index 000000000..b83b4f7b7 --- /dev/null +++ b/test/Util.Application.WebApi.SqlServer.Tests.Integration/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:1047/", + "sslPort": 44370 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Util.Application.WebApi.Tests.Integration": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/test/Util.Application.WebApi.SqlServer.Tests.Integration/Startup.cs b/test/Util.Application.WebApi.SqlServer.Tests.Integration/Startup.cs new file mode 100644 index 000000000..862a1844d --- /dev/null +++ b/test/Util.Application.WebApi.SqlServer.Tests.Integration/Startup.cs @@ -0,0 +1,62 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Aop; +using Util.Data.EntityFrameworkCore; +using Util.Helpers; +using Util.Http; +using Util.Tests.UnitOfWorks; +using Xunit.DependencyInjection.Logging; + +namespace Util.Applications; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .ConfigureWebHostDefaults( webHostBuilder => { + webHostBuilder.UseTestServer() + .Configure( t => { + t.UseRouting(); + t.UseEndpoints( endpoints => { + endpoints.MapControllers(); + } ); + } ); + } ) + .AsBuild() + .AddAop() + .AddSqlServerUnitOfWork( Config.GetConnectionString( "connection" ) ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddControllers(); + services.AddTransient( t => { + var client = new HttpClientService(); + client.SetHttpClient( t.GetService().GetTestClient() ); + return client; + } ); + InitDatabase( services ); + } + + /// + /// ʼݿ + /// + private void InitDatabase( IServiceCollection services ) { + var unitOfWork = services.BuildServiceProvider().GetService(); + unitOfWork.EnsureDeleted(); + unitOfWork.EnsureCreated(); + } +} \ No newline at end of file diff --git a/test/Util.Application.WebApi.SqlServer.Tests.Integration/Tests/CustomerControllerTest.cs b/test/Util.Application.WebApi.SqlServer.Tests.Integration/Tests/CustomerControllerTest.cs new file mode 100644 index 000000000..2337a3cf6 --- /dev/null +++ b/test/Util.Application.WebApi.SqlServer.Tests.Integration/Tests/CustomerControllerTest.cs @@ -0,0 +1,115 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Util.Data; +using Util.Helpers; +using Util.Http; +using Util.Tests.Dtos; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Queries; +using Util.Tests.Services; +using Xunit; +using Xunit.Abstractions; +using Xunit.DependencyInjection; + +namespace Util.Applications.Tests; + +/// +/// 客户控制器测试 +/// +public class CustomerControllerTest : ControllerTestBase { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _testOutputHelper; + /// + /// 客户服务 + /// + private readonly ICustomerService _service; + + /// + /// 测试初始化 + /// + public CustomerControllerTest( ITestOutputHelperAccessor testOutputHelperAccessor, ICustomerService service,IHttpClient client ) : base(client){ + _testOutputHelper = testOutputHelperAccessor.Output; + _service = service; + } + + /// + /// 测试获取单个实体 + /// + [Fact] + public async Task TestGetAsync() { + //创建实体 + var dto = CustomerFakeService.GetCustomerDto(); + var id = await _service.CreateAsync( dto ); + + //获取单个实体 + var url = $"/api/customer/{id}"; + var result = await GetAsync( url, dto ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.Equal( dto.Email, result.Data.Email ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } + + /// + /// 测试查询 + /// + [Fact] + public async Task TestQueryAsync() { + //常量 + var name = "TestQueryAsync"; + var url = "/api/customer/query"; + + //创建3个实体,前两个设置了名称 + var dtos = CustomerFakeService.GetCustomerDtos(3); + dtos[0].Name = name; + dtos[1].Name = name; + await _service.CreateAsync( dtos[0] ); + await _service.CreateAsync( dtos[1] ); + await _service.CreateAsync( dtos[2] ); + + //使用名称查询 + var query = new CustomerQuery { Name = name }; + var result = await GetAsync>( url, query ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.Equal( 2, result.Data.Count ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } + + /// + /// 测试分页查询 + /// + [Fact] + public async Task TestPageQueryAsync() { + //常量 + var name = "TestPageQueryAsync"; + var url = "/api/customer"; + + //创建5个实体,前4个设置了名称,并使用Id属性排序 + var dtos = CustomerFakeService.GetCustomerDtos( 5 ).OrderBy( t => t.Id ).ToList(); + for ( int i = 0; i < 4; i++ ) { + dtos[i].Name = name; + } + foreach ( var dto in dtos ) { + await _service.CreateAsync( dto ); + } + + //使用名称查询第二页,每页2行,并使用Id属性排序 + var query = new CustomerQuery { Page = 2, PageSize = 2, Name = name,Order = "Id" }; + var result = await GetAsync>( url, query ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.Equal( 2, result.Data.PageCount ); + Assert.Equal( 4, result.Data.Total ); + Assert.Equal( dtos[2].Email, result.Data.Data[0].Email ); + Assert.Equal( dtos[3].Email, result.Data.Data[1].Email ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } +} \ No newline at end of file diff --git a/test/Util.Application.WebApi.SqlServer.Tests.Integration/Tests/ProductControllerTest.cs b/test/Util.Application.WebApi.SqlServer.Tests.Integration/Tests/ProductControllerTest.cs new file mode 100644 index 000000000..521f4a72b --- /dev/null +++ b/test/Util.Application.WebApi.SqlServer.Tests.Integration/Tests/ProductControllerTest.cs @@ -0,0 +1,132 @@ +using System.Threading.Tasks; +using Util.Helpers; +using Util.Http; +using Util.Tests.Dtos; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Services; +using Xunit; +using Xunit.Abstractions; +using Xunit.DependencyInjection; + +namespace Util.Applications.Tests; + +/// +/// 产品控制器测试 +/// +public class ProductControllerTest : ControllerTestBase { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _testOutputHelper; + /// + /// 产品服务 + /// + private readonly IProductService _service; + + /// + /// 测试初始化 + /// + public ProductControllerTest( ITestOutputHelperAccessor testOutputHelperAccessor, IProductService service, IHttpClient client ) : base( client ) { + _testOutputHelper = testOutputHelperAccessor.Output; + _service = service; + } + + /// + /// 测试创建 + /// + [Fact] + public async Task TestCreateAsync() { + //服务地址 + var url = "/api/product"; + + //创建实体 + var dto = ProductFakeService.GetProductDto(); + var result = await PostAsync( url, dto ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.NotEmpty( result.Data.Id ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } + + /// + /// 测试修改 + /// + [Fact] + public async Task TestUpdateAsync() { + //常量 + var name = "TestUpdateAsync"; + + //创建实体 + var dto = ProductFakeService.GetProductDto(); + var id = await _service.CreateAsync( dto ); + + //服务地址 + var url = $"/api/product/{id}"; + + //修改实体 + dto = await _service.GetByIdAsync( id ); + dto.Name = name; + var result = await PutAsync( url, dto ); + + //重新获取实体 + dto = await _service.GetByIdAsync( id ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.Equal( name, dto.Name ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } + + /// + /// 测试删除 + /// + [Fact] + public async Task TestDeleteAsync() { + //创建实体 + var dto = ProductFakeService.GetProductDto(); + var id = await _service.CreateAsync( dto ); + + //服务地址 + var url = $"/api/product/{id}"; + + //删除实体 + var result = await DeleteAsync( url ); + + //重新获取实体 + dto = await _service.GetByIdAsync( id ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.Null( dto ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } + + /// + /// 测试批量删除 + /// + [Fact] + public async Task TestBatchDeleteAsync() { + //创建实体 + var dtos = ProductFakeService.GetProductDtos(2); + var id = await _service.CreateAsync( dtos[0] ); + var id2 = await _service.CreateAsync( dtos[1] ); + + //服务地址 + var url = "/api/product/delete"; + + //删除实体列表 + var result = await PostAsync( url,$"{id},{id2}" ); + + //重新获取实体 + var dto = await _service.GetByIdAsync( id ); + var dto2 = await _service.GetByIdAsync( id2 ); + + //验证 + Assert.Equal( StateCode.Ok, result.Code ); + Assert.Null( dto ); + Assert.Null( dto2 ); + _testOutputHelper.WriteLine( Json.ToJson( result ) ); + } +} \ No newline at end of file diff --git a/test/Util.Application.WebApi.SqlServer.Tests.Integration/Util.Application.WebApi.SqlServer.Tests.Integration.csproj b/test/Util.Application.WebApi.SqlServer.Tests.Integration/Util.Application.WebApi.SqlServer.Tests.Integration.csproj new file mode 100644 index 000000000..e207c528f --- /dev/null +++ b/test/Util.Application.WebApi.SqlServer.Tests.Integration/Util.Application.WebApi.SqlServer.Tests.Integration.csproj @@ -0,0 +1,41 @@ + + + + $(NetTargetFramework) + false + Util.Applications + Util.Applications.Startup + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + Always + + + Always + + + + diff --git a/test/Util.Application.WebApi.SqlServer.Tests.Integration/appsettings.Development.json b/test/Util.Application.WebApi.SqlServer.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..fb96acf05 --- /dev/null +++ b/test/Util.Application.WebApi.SqlServer.Tests.Integration/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Trace" + } + }, + "ConnectionStrings": { + "connection": "Server=.;Database=Util.WebApi.Test;uid=sa;pwd=Pass@word;MultipleActiveResultSets=true;TrustServerCertificate=true" + } +} diff --git a/test/Util.Application.WebApi.SqlServer.Tests.Integration/appsettings.json b/test/Util.Application.WebApi.SqlServer.Tests.Integration/appsettings.json new file mode 100644 index 000000000..7583199ab --- /dev/null +++ b/test/Util.Application.WebApi.SqlServer.Tests.Integration/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + }, + "ConnectionStrings": { + "connection": "Server=192.168.31.123\\sql2019;Database=Util.WebApi.Test;uid=sa;pwd=sa;MultipleActiveResultSets=true;TrustServerCertificate=true" + } +} \ No newline at end of file diff --git a/test/Util.Application.WebApi.Tests.Integration/Locks/LockAttributeTest.cs b/test/Util.Application.WebApi.Tests.Integration/Locks/LockAttributeTest.cs new file mode 100644 index 000000000..eece8abb0 --- /dev/null +++ b/test/Util.Application.WebApi.Tests.Integration/Locks/LockAttributeTest.cs @@ -0,0 +1,70 @@ +using System.Collections.Concurrent; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Util.Helpers; +using Util.Http; +using Util.Tests.Infrastructure; +using Xunit; + +namespace Util.Applications.Locks { + /// + /// 请求锁过滤器测试 + /// + public class LockAttributeTest : ControllerTestBase { + /// + /// 测试初始化 + /// + public LockAttributeTest( IHttpClient client, IHttpContextAccessor accessor ) : base(client, accessor ) { + } + + /// + /// 测试未设置防重复过滤器 + /// + [Fact] + public async Task Test_1() { + var url = "/api/lock/nolock"; + var bag = new ConcurrentBag>(); + await Thread.ParallelForAsync( async (i) => { + await Task.Delay( i * 100 ); + var result = await GetAsync( url ); + bag.Add( result ); + }, 20 ); + Assert.Equal( 20, bag.ToList().FindAll( t => t.Data == "ok" ).Count ); + } + + /// + /// 测试设置防重复过滤器 - 全局锁 - 同一时刻只有一个用户成功执行 + /// + [Fact] + public async Task Test_2() { + var url = "/api/lock/GlobalLock"; + var bag = new ConcurrentBag>(); + await Thread.ParallelForAsync( async (i) => { + await Task.Delay( i * 10 ); + var result = await GetAsync( url ); + bag.Add( result ); + }, 20 ); + Assert.Single( bag.ToList().FindAll( t => t.Data == "ok" )); + } + + /// + /// 测试设置防重复过滤器 - 用户锁 - 模拟10个用户,每个用户发送5个请求,结果是每个用户只成功执行一次 + /// + [Fact] + public async Task Test_3() { + var url = "/api/lock/UserLock"; + var bag = new ConcurrentBag>(); + await Thread.ParallelForAsync( async (i) => { + await Task.Delay( i * 10 ); + var userId = Id.Create(); + await Thread.ParallelForAsync( async (j) => { + await Task.Delay( j * 100 ); + var result = await Client.Get>( url ).Header( "user-id",userId ).GetResultAsync(); + bag.Add( result ); + }, 5 ); + }, 10 ); + Assert.Equal( 10, bag.ToList().FindAll( t => t.Data == "ok" ).Count ); + } + } +} diff --git a/test/Util.Application.WebApi.Tests.Integration/Locks/LockTest.cs b/test/Util.Application.WebApi.Tests.Integration/Locks/LockTest.cs new file mode 100644 index 000000000..bfa0ca4e8 --- /dev/null +++ b/test/Util.Application.WebApi.Tests.Integration/Locks/LockTest.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Util.Caching; +using Util.Helpers; +using Xunit; + +namespace Util.Applications.Locks { + /// + /// 业务锁测试 + /// + public class LockTest : IDisposable { + /// + /// 业务锁测试服务 + /// + private readonly LockTestService _service; + /// + /// 缓存 + /// + private readonly ICache _cache; + + /// + /// 测试初始化 + /// + public LockTest( LockTestService service, ICache cache) { + _service = service; + _cache = cache; + } + + /// + /// 测试清理 + /// + public void Dispose() { + Time.Reset(); + _service.UnLockAsync().GetAwaiter().GetResult(); + } + + /// + /// 执行一个服务 + /// + [Fact] + public async Task Test_1() { + Assert.Equal( "ok",await _service.ExecuteAsync( "lock:Test_1" ) ); + } + + /// + /// 测试解锁 + /// + [Fact] + public async Task Test_2() { + var key = "lock:Test_2"; + await _cache.RemoveAsync( key ); + + //执行,被锁定 + await _service.ExecuteAsync( key ); + + //未解锁,无法执行 + var result = new List(); + await Util.Helpers.Thread.ParallelForAsync( async () => result.Add( await _service.ExecuteAsync( key ) ), 20 ); + Assert.Empty( result.FindAll( t => t == "ok" ) ); + + //解锁 + await _service.UnLockAsync(); + + //再次执行 + await Util.Helpers.Thread.ParallelForAsync( async () => result.Add( await _service.ExecuteAsync( key ) ), 20 ); + Assert.Single( result.FindAll( t => t == "ok" )); + } + + /// + /// 延迟1秒才允许执行 - 设置延迟时间后,UnLock无效 + /// + [Fact] + public async Task Test_3() { + var key = "lock:Test_3"; + Assert.Equal( "ok", await _service.ExecuteAsync( key, TimeSpan.FromSeconds( 1 ) ) ); + await _service.UnLockAsync(); + Assert.Equal( "fail",await _service.ExecuteAsync( key ) ); + await Task.Delay( 1500 ); + Assert.Equal( "ok",await _service.ExecuteAsync( key ) ); + } + } +} diff --git a/test/Util.Application.WebApi.Tests.Integration/Locks/LockTestService.cs b/test/Util.Application.WebApi.Tests.Integration/Locks/LockTestService.cs new file mode 100644 index 000000000..9b963ceda --- /dev/null +++ b/test/Util.Application.WebApi.Tests.Integration/Locks/LockTestService.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading.Tasks; + +namespace Util.Applications.Locks { + /// + /// 业务锁测试服务 + /// + public class LockTestService { + /// + /// 业务锁 + /// + private readonly ILock _lock; + + /// + /// 初始化业务锁测试服务 + /// + public LockTestService( ILock lLock ) { + _lock = lLock; + } + + /// + /// 执行 + /// + public async Task ExecuteAsync( string key, TimeSpan? expiration = null ) { + var result = await _lock.LockAsync( key, expiration ); + return result ? "ok" : "fail"; + } + + /// + /// 解锁 + /// + public async Task UnLockAsync() { + await _lock.UnLockAsync(); + } + } +} diff --git a/test/Util.Application.WebApi.Tests.Integration/Properties/launchSettings.json b/test/Util.Application.WebApi.Tests.Integration/Properties/launchSettings.json new file mode 100644 index 000000000..2526188f2 --- /dev/null +++ b/test/Util.Application.WebApi.Tests.Integration/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Util.Application.WebApi.Tests.Integration": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:17185;http://localhost:17186" + } + } +} \ No newline at end of file diff --git a/test/Util.Application.WebApi.Tests.Integration/Startup.cs b/test/Util.Application.WebApi.Tests.Integration/Startup.cs new file mode 100644 index 000000000..fbf836c8e --- /dev/null +++ b/test/Util.Application.WebApi.Tests.Integration/Startup.cs @@ -0,0 +1,71 @@ +using EasyCaching.Core.Configurations; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Applications.Locks; +using Util.Caching.EasyCaching; +using Util.Events.Infrastructure; +using Util.Helpers; +using Util.Http; +using Util.Infrastructure; +using Util.Sessions; +using Util.Tests.Middlewares; +using Xunit.DependencyInjection.Logging; + +namespace Util.Applications { + /// + /// + /// + public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Environment.SetDevelopment(); + ServiceRegistrarConfig.Instance.DisableDependencyServiceRegistrar(); + ServiceRegistrarConfig.Instance.DisableLocalEventBusServiceRegistrar(); + var builder = hostBuilder.ConfigureDefaults( null ) + .ConfigureWebHostDefaults( webHostBuilder => { + webHostBuilder.UseTestServer() + .Configure( t => { + t.UseMiddleware(); + t.UseRouting(); + t.UseEndpoints( endpoints => { + endpoints.MapControllers(); + } ); + } ); + } ) + .AsBuild() + .AddLock(); + if ( Environment.IsDevelopment() ) { + builder.AddMemoryCache( t => t.MaxRdSecond = 0 ); + } + else { + builder.AddRedisCache( t => { + t.MaxRdSecond = 0; + t.DBConfig.AllowAdmin = true; + t.DBConfig.KeyPrefix = "util.lock.test:"; + t.DBConfig.Endpoints.Add( new ServerEndPoint( "127.0.0.1", 6379 ) ); + } ); + } + builder.AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddControllers(); + services.AddTransient(); + services.AddSingleton(); + services.AddTransient( t => { + var client = new HttpClientService(); + client.SetHttpClient( t.GetService().GetTestClient() ); + return client; + } ); + } + } +} diff --git a/test/Util.Application.WebApi.Tests.Integration/Util.Application.WebApi.Tests.Integration.csproj b/test/Util.Application.WebApi.Tests.Integration/Util.Application.WebApi.Tests.Integration.csproj new file mode 100644 index 000000000..d534ecbad --- /dev/null +++ b/test/Util.Application.WebApi.Tests.Integration/Util.Application.WebApi.Tests.Integration.csproj @@ -0,0 +1,36 @@ + + + + $(NetTargetFramework) + false + Util.Applications + Util.Applications.Startup + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + diff --git a/test/Util.AspNetCore.Tests.Integration/Authorization/AclTest.cs b/test/Util.AspNetCore.Tests.Integration/Authorization/AclTest.cs new file mode 100644 index 000000000..3a891648f --- /dev/null +++ b/test/Util.AspNetCore.Tests.Integration/Authorization/AclTest.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; +using Util.Applications; +using Util.Http; +using Util.Microservices; +using Xunit; + +namespace Util.AspNetCore.Tests.Authorization; + +/// +/// Ȩʲ +/// +public class AclTest { + /// + /// Httpͻ + /// + private readonly IHttpClient _client; + + /// + /// Գʼ + /// + /// Httpͻ + public AclTest( IHttpClient client ) { + _client = client; + } + + /// + /// δȨ + /// + [Fact] + public async Task Test() { + var result = await _client.Get>( "/api/test5/1" ).GetResultAsync(); + Assert.Equal( StateCode.Unauthorized, result.Code ); + } +} \ No newline at end of file diff --git a/test/Util.AspNetCore.Tests.Integration/Controllers/Test1Controller.cs b/test/Util.AspNetCore.Tests.Integration/Controllers/Test1Controller.cs new file mode 100644 index 000000000..5be603835 --- /dev/null +++ b/test/Util.AspNetCore.Tests.Integration/Controllers/Test1Controller.cs @@ -0,0 +1,66 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using Util.AspNetCore.Tests.Samples; + +namespace Util.AspNetCore.Tests.Controllers; + +/// +/// 测试Api控制器1 +/// +[ApiController] +[Route( "api/[controller]" )] +public class Test1Controller : ControllerBase { + /// + /// 获取请求头操作 + /// + [HttpGet("header")] + public string GetHeader() { + var header = Request.Headers["Authorization"].FirstOrDefault(); + return $"ok:{header}"; + } + + /// + /// 默认Get操作 + /// + [HttpGet] + public string Get() { + return "ok"; + } + + /// + /// 通过id获取 + /// + [HttpGet( "{id}" )] + public string Get( string id ) { + return $"ok:{id}"; + } + + /// + /// 查询操作,通过query参数绑定 + /// + [HttpGet( "Query" )] + public string Query( [FromQuery] CustomerQuery query ) { + return $"code:{query.Code},name:{query.Name}"; + } + + /// + /// 查询操作,返回对象 + /// + [HttpGet( "List" )] + public ActionResult> List( [FromQuery] CustomerQuery query ) { + var result = new List { + new() {Code = query.Code}, + new() {Name = query.Name} + }; + return result; + } + + /// + /// 获取cookie值 + /// + [HttpGet( "cookie" )] + public string GetCookie() { + return $"code:{Request.Cookies["code"]},name:{Request.Cookies["name"]}"; + } +} \ No newline at end of file diff --git a/test/Util.AspNetCore.Tests.Integration/Controllers/Test2Controller.cs b/test/Util.AspNetCore.Tests.Integration/Controllers/Test2Controller.cs new file mode 100644 index 000000000..49d716459 --- /dev/null +++ b/test/Util.AspNetCore.Tests.Integration/Controllers/Test2Controller.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Mvc; +using Util.AspNetCore.Tests.Samples; + +namespace Util.AspNetCore.Tests.Controllers; + +/// +/// 测试Api控制器2 +/// +[ApiController] +[Route( "api/[controller]" )] +public class Test2Controller : ControllerBase { + /// + /// Post操作 + /// + [HttpPost("create")] + public string Create( CustomerDto dto ) { + return $"ok:{dto.Code}"; + } + + /// + /// Post操作 + /// + [HttpPost] + public IActionResult Create2( CustomerDto dto ) { + return new JsonResult( dto ); + } +} \ No newline at end of file diff --git a/test/Util.AspNetCore.Tests.Integration/Controllers/Test3Controller.cs b/test/Util.AspNetCore.Tests.Integration/Controllers/Test3Controller.cs new file mode 100644 index 000000000..1231d9176 --- /dev/null +++ b/test/Util.AspNetCore.Tests.Integration/Controllers/Test3Controller.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Mvc; +using Util.AspNetCore.Tests.Samples; + +namespace Util.AspNetCore.Tests.Controllers; + +/// +/// 测试Api控制器3 +/// +[ApiController] +[Route( "api/[controller]" )] +public class Test3Controller : ControllerBase { + /// + /// Put操作 + /// + [HttpPut("update")] + public string Update( CustomerDto dto ) { + return $"ok:{dto.Code}"; + } + + /// + /// Put操作 + /// + [HttpPut("{id}")] + public IActionResult Update2( string id, CustomerDto dto ) { + dto.Id = id; + return new JsonResult( dto ); + } +} \ No newline at end of file diff --git a/test/Util.AspNetCore.Tests.Integration/Controllers/Test4Controller.cs b/test/Util.AspNetCore.Tests.Integration/Controllers/Test4Controller.cs new file mode 100644 index 000000000..b32dd44c2 --- /dev/null +++ b/test/Util.AspNetCore.Tests.Integration/Controllers/Test4Controller.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Mvc; +using Util.AspNetCore.Tests.Samples; + +namespace Util.AspNetCore.Tests.Controllers; + +/// +/// 测试Api控制器4 +/// +[ApiController] +[Route( "api/[controller]" )] +public class Test4Controller : ControllerBase { + /// + /// Delete操作 + /// + [HttpDelete( "delete/{id}" )] + public string Delete( string id ) { + return $"ok:{id}"; + } + + /// + /// Delete操作 + /// + [HttpDelete( "{id}" )] + public CustomerDto Delete2( string id ) { + return new CustomerDto { Code = id }; + } +} \ No newline at end of file diff --git a/test/Util.AspNetCore.Tests.Integration/Controllers/Test5Controller.cs b/test/Util.AspNetCore.Tests.Integration/Controllers/Test5Controller.cs new file mode 100644 index 000000000..178134848 --- /dev/null +++ b/test/Util.AspNetCore.Tests.Integration/Controllers/Test5Controller.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Mvc; +using Util.Security; + +namespace Util.AspNetCore.Tests.Controllers; + +/// +/// 测试Api控制器5 - 用于测试授权 +/// +[ApiController] +[Route( "api/[controller]" )] +public class Test5Controller : ControllerBase { + /// + /// 操作1 + /// + [HttpGet( "1" )] + [Acl] + public string Get() { + return "ok"; + } +} \ No newline at end of file diff --git a/test/Util.AspNetCore.Tests.Integration/Controllers/Test6Controller.cs b/test/Util.AspNetCore.Tests.Integration/Controllers/Test6Controller.cs new file mode 100644 index 000000000..e04dafe86 --- /dev/null +++ b/test/Util.AspNetCore.Tests.Integration/Controllers/Test6Controller.cs @@ -0,0 +1,42 @@ +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace Util.AspNetCore.Tests.Controllers; + +/// +/// 测试Api控制器6 - 用于测试上传 +/// +[ApiController] +[Route( "api/[controller]" )] +public class Test6Controller : ControllerBase { + /// + /// 上传 + /// + [HttpPost] + public async Task Upload() { + var file = Util.Helpers.Web.GetFile(); + await using var stream = file.OpenReadStream(); + await Util.Helpers.File.WriteAsync( file.FileName, stream ); + var param = Util.Helpers.Web.GetParam( "util" ); + if( param.IsEmpty() ) + return $"ok:{file.Name}:{file.FileName}"; + return $"ok:{file.Name}:{file.FileName}:{param}"; + } + + /// + /// 多上传 + /// + [HttpPost("multi")] + public async Task MultiUpload() { + var files = Util.Helpers.Web.GetFiles(); + var result = new StringBuilder(); + result.Append( "ok" ); + foreach ( var file in files ) { + await using var stream = file.OpenReadStream(); + await Util.Helpers.File.WriteAsync( file.FileName, stream ); + result.Append( $":{file.Name}:{file.FileName}" ); + } + return result.ToString(); + } +} \ No newline at end of file diff --git a/test/Util.AspNetCore.Tests.Integration/Helpers/WebTest.cs b/test/Util.AspNetCore.Tests.Integration/Helpers/WebTest.cs new file mode 100644 index 000000000..b288379f6 --- /dev/null +++ b/test/Util.AspNetCore.Tests.Integration/Helpers/WebTest.cs @@ -0,0 +1,25 @@ +using Util.Helpers; +using Xunit; + +namespace Util.AspNetCore.Tests.Helpers; + +/// +/// Web测试 +/// +public class WebTest { + /// + /// Url编码 + /// + [Fact] + public void TestUrlEncode() { + Assert.Equal( "http%3a%2f%2fwww.a.com", Web.UrlEncode( @"http://www.a.com" ) ); + } + + /// + /// Url编码,转大写 + /// + [Fact] + public void TestUrlEncode_Upper() { + Assert.Equal( "http%3A%2F%2Fwww.a.com", Web.UrlEncode( @"http://www.a.com", true ) ); + } +} \ No newline at end of file diff --git a/test/Util.AspNetCore.Tests.Integration/Http/HttpClientServiceTest.Delete.cs b/test/Util.AspNetCore.Tests.Integration/Http/HttpClientServiceTest.Delete.cs new file mode 100644 index 000000000..1127308e4 --- /dev/null +++ b/test/Util.AspNetCore.Tests.Integration/Http/HttpClientServiceTest.Delete.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using Util.AspNetCore.Tests.Samples; +using Xunit; + +namespace Util.AspNetCore.Tests.Http; + +/// +/// Httpͻ˲ - Delete +/// +public partial class HttpClientServiceTest { + /// + /// ԵDelete - ַ + /// + [Fact] + public async Task TestDelete_1() { + var result = await _client.Delete( "/api/test4/delete/1" ).GetResultAsync(); + Assert.Equal( "ok:1", result ); + } + + /// + /// ԵPut - طͽ + /// + [Fact] + public async Task TestDelete_2() { + var result = await _client.Delete( "/api/test4/1" ).GetResultAsync(); + Assert.Equal( "1", result.Code ); + } +} \ No newline at end of file diff --git a/test/Util.AspNetCore.Tests.Integration/Http/HttpClientServiceTest.Get.cs b/test/Util.AspNetCore.Tests.Integration/Http/HttpClientServiceTest.Get.cs new file mode 100644 index 000000000..095f7e79c --- /dev/null +++ b/test/Util.AspNetCore.Tests.Integration/Http/HttpClientServiceTest.Get.cs @@ -0,0 +1,170 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Util.AspNetCore.Tests.Samples; +using Util.Http; +using Xunit; + +namespace Util.AspNetCore.Tests.Http; + +/// +/// Httpͻ˲ - Get +/// +public partial class HttpClientServiceTest { + + #region Գʼ + + /// + /// Httpͻ + /// + private readonly IHttpClient _client; + + /// + /// Գʼ + /// + /// Httpͻ + public HttpClientServiceTest( IHttpClient client ) { + _client = client; + } + + #endregion + + #region + + /// + /// ԵGet + /// + [Fact] + public async Task TestGet_1() { + var result = await _client.Get( "/api/test1" ).GetResultAsync(); + Assert.Equal( "ok", result ); + } + + /// + /// ԵGet - id + /// + [Fact] + public async Task TestGet_2() { + var result = await _client.Get( "/api/test1/2" ).GetResultAsync(); + Assert.Equal( "ok:2", result ); + } + + /// + /// ԵGet - ض + /// + [Fact] + public async Task TestGet_3() { + CustomerQuery query = new CustomerQuery { Code = "a", Name = "b" }; + var result = await _client.Get>( "/api/test1/list", query ).GetResultAsync(); + Assert.Equal( 2, result.Count ); + Assert.Equal( "a", result[0].Code ); + Assert.Equal( "b", result[1].Name ); + } + + #endregion + + #region Header + + /// + /// ԵGet - ͷ + /// + [Fact] + public async Task TestGet_Header_1() { + var result = await _client.Get( "/api/test1/header" ).Header( "Authorization", "abc" ).GetResultAsync(); + Assert.Equal( "ok:abc", result ); + } + + /// + /// ԵGet - ͷ - ֵ + /// + [Fact] + public async Task TestGet_Header_2() { + var headers = new Dictionary { { "Authorization", "abc" } }; + var result = await _client.Get( "/api/test1/header" ).Header( headers ).GetResultAsync(); + Assert.Equal( "ok:abc", result ); + } + + #endregion + + #region QueryString + + /// + /// ԵGet - query - Ӳ + /// + [Fact] + public async Task TestGet_QueryString_1() { + var result = await _client.Get( "/api/test1/query?code=9&name=a" ).GetResultAsync(); + Assert.Equal( "code:9,name:a", result ); + } + + /// + /// ԵGet - query + /// + [Fact] + public async Task TestGet_QueryString_2() { + var result = await _client.Get( "/api/test1/query" ).QueryString( "code", "9" ).QueryString( "name", "a" ).GetResultAsync(); + Assert.Equal( "code:9,name:a", result ); + } + + /// + /// ԵGet - query - Ӳ + /// + [Fact] + public async Task TestGet_QueryString_3() { + var result = await _client.Get( "/api/test1/query?code=9" ).QueryString( "name", "a" ).GetResultAsync(); + Assert.Equal( "code:9,name:a", result ); + } + + /// + /// ԵGet - query - ֵ + /// + [Fact] + public async Task TestGet_QueryString_4() { + var queryString = new Dictionary { { "code", "9" }, { "name", "a" } }; + var result = await _client.Get( "/api/test1/query" ).QueryString( queryString ).GetResultAsync(); + Assert.Equal( "code:9,name:a", result ); + } + + /// + /// ԵGet - query - + /// + [Fact] + public async Task TestGet_QueryString_5() { + var query = new CustomerQuery { Code = "9", Name = "a" }; + var result = await _client.Get( "/api/test1/query" ).QueryString( query ).GetResultAsync(); + Assert.Equal( "code:9,name:a", result ); + } + + /// + /// ԵGet - query - ʹGetط + /// + [Fact] + public async Task TestGet_QueryString_6() { + var query = new CustomerQuery { Code = "9", Name = "a" }; + var result = await _client.Get( "/api/test1/query", query ).GetResultAsync(); + Assert.Equal( "code:9,name:a", result ); + } + + #endregion + + #region Cookie + + /// + /// ԵGet - cookie - 1 + /// + [Fact] + public async Task TestGet_Cookie_1() { + var result = await _client.Get( "http://a.com/api/test1/cookie" ).Cookie( "code", "a" ).GetResultAsync(); + Assert.Equal( "code:a,name:", result ); + } + + /// + /// ԵGet - cookie - 2 + /// + [Fact] + public async Task TestGet_Cookie_2() { + var result = await _client.Get( "/api/test1/cookie" ).Cookie( "code", "a" ).Cookie( "name", "b" ).GetResultAsync(); + Assert.Equal( "code:a,name:b", result ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.AspNetCore.Tests.Integration/Http/HttpClientServiceTest.Post.cs b/test/Util.AspNetCore.Tests.Integration/Http/HttpClientServiceTest.Post.cs new file mode 100644 index 000000000..124e219ce --- /dev/null +++ b/test/Util.AspNetCore.Tests.Integration/Http/HttpClientServiceTest.Post.cs @@ -0,0 +1,108 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Util.AspNetCore.Tests.Samples; +using Xunit; + +namespace Util.AspNetCore.Tests.Http; + +/// +/// Httpͻ˲ - Post +/// +public partial class HttpClientServiceTest { + /// + /// Post - ַ + /// + [Fact] + public async Task TestPost_1() { + var dto = new CustomerDto { Code = "a" }; + var result = await _client.Post( "/api/test2/create", dto ).GetResultAsync(); + Assert.Equal( "ok:a", result ); + } + + /// + /// Post - طͽ + /// + [Fact] + public async Task TestPost_2() { + var dto = new CustomerDto { Code = "a" }; + var result = await _client.Post( "/api/test2", dto ).GetResultAsync(); + Assert.Equal( "a", result.Code ); + } + + /// + /// Post - ʹContentݲ - ֵ + /// + [Fact] + public async Task TestPost_Content_1() { + var result = await _client.Post( "/api/test2/create" ).Content( "code", "a" ).GetResultAsync(); + Assert.Equal( "ok:a", result ); + } + + /// + /// Post - ʹContentݲ - ֵ + /// + [Fact] + public async Task TestPost_Content_2() { + var dic = new Dictionary { { "code", "a" } }; + var result = await _client.Post( "/api/test2/create" ).Content( dic ).GetResultAsync(); + Assert.Equal( "ok:a", result ); + } + + /// + /// Post - ʹContentݲ - + /// + [Fact] + public async Task TestPost_Content_3() { + var dto = new CustomerDto { Code = "a" }; + var result = await _client.Post( "/api/test2/create" ).Content( dto ).GetResultAsync(); + Assert.Equal( "ok:a", result ); + } + + /// + /// Post - ϴļ - ļ· + /// + [Fact] + public async Task TestPost_FileContent_1() { + var path = Util.Helpers.Common.GetPhysicalPath( "Resources/a.png" ); + var result = await _client.Post( "/api/test6" ).FileContent( path, "file1" ).GetResultAsync(); + Assert.Equal( "ok:file1:a.png", result ); + } + + /// + /// Post - ϴļ - ļ + /// + [Fact] + public async Task TestPost_FileContent_2() { + var path = Util.Helpers.Common.GetPhysicalPath( "Resources/a.png" ); + var stream = await Util.Helpers.File.ReadToMemoryStreamAsync( path ); + var result = await _client.Post( "/api/test6" ).FileContent( stream, "abc.png", "file2" ).GetResultAsync(); + Assert.Equal( "ok:file2:abc.png", result ); + } + + /// + /// Post - ϴļ - ļϴ + /// + [Fact] + public async Task TestPost_FileContent_3() { + var path = Util.Helpers.Common.GetPhysicalPath( "Resources/a.png" ); + var stream = await Util.Helpers.File.ReadToMemoryStreamAsync( path ); + var result = await _client.Post( "/api/test6/multi" ) + .FileContent( path, "file1" ) + .FileContent( stream, "b.png", "file2" ) + .GetResultAsync(); + Assert.Equal( "ok:file1:a.png:file2:b.png", result ); + } + + /// + /// Post - ϴļ - Ͳ + /// + [Fact] + public async Task TestPost_FileContent_4() { + var path = Util.Helpers.Common.GetPhysicalPath( "Resources/a.png" ); + var result = await _client.Post( "/api/test6" ) + .FileContent( path, "file1" ) + .Content( "util","core" ) + .GetResultAsync(); + Assert.Equal( "ok:file1:a.png:core", result ); + } +} \ No newline at end of file diff --git a/test/Util.AspNetCore.Tests.Integration/Http/HttpClientServiceTest.Put.cs b/test/Util.AspNetCore.Tests.Integration/Http/HttpClientServiceTest.Put.cs new file mode 100644 index 000000000..4a3ed1652 --- /dev/null +++ b/test/Util.AspNetCore.Tests.Integration/Http/HttpClientServiceTest.Put.cs @@ -0,0 +1,33 @@ +using System.Threading.Tasks; +using Util.AspNetCore.Tests.Samples; +using Xunit; + +namespace Util.AspNetCore.Tests.Http; + +/// +/// Httpͻ˲ - Put +/// +public partial class HttpClientServiceTest { + /// + /// ԵPut - ַ + /// + [Fact] + public async Task TestPut_1() { + var dto = new CustomerDto { Code = "a" }; + var result = await _client.Put( "/api/test3/update", dto ).GetResultAsync(); + Assert.Equal( "ok:a", result ); + } + + /// + /// ԵPut - طͽ + /// + [Fact] + public async Task TestPut_2() { + var date = "2022-09-02 19:26:32"; + var dto = new CustomerDto { Code = "a",Birthday = date.ToDateTime() }; + var result = await _client.Put( "/api/test3/2", dto ).GetResultAsync(); + Assert.Equal( "2", result?.Id ); + Assert.Equal( "a", result?.Code ); + Assert.Equal( date, result?.Birthday.ToDateTimeString() ); + } +} \ No newline at end of file diff --git a/test/Util.AspNetCore.Tests.Integration/Resources/a.png b/test/Util.AspNetCore.Tests.Integration/Resources/a.png new file mode 100644 index 000000000..ace11c472 Binary files /dev/null and b/test/Util.AspNetCore.Tests.Integration/Resources/a.png differ diff --git a/test/Util.AspNetCore.Tests.Integration/Samples/Certificate/apiclient_cert.p12 b/test/Util.AspNetCore.Tests.Integration/Samples/Certificate/apiclient_cert.p12 new file mode 100644 index 000000000..3d03b770e Binary files /dev/null and b/test/Util.AspNetCore.Tests.Integration/Samples/Certificate/apiclient_cert.p12 differ diff --git a/test/Util.AspNetCore.Tests.Integration/Samples/Certificate/apiclient_cert.pem b/test/Util.AspNetCore.Tests.Integration/Samples/Certificate/apiclient_cert.pem new file mode 100644 index 000000000..7b80a20d9 --- /dev/null +++ b/test/Util.AspNetCore.Tests.Integration/Samples/Certificate/apiclient_cert.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID7DCCAtSgAwIBAgIULTQ1TmSotcbvSPsnVmPvfq6NqicwDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT +FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg +Q0EwHhcNMjExMDEzMDgyMDMxWhcNMjYxMDEyMDgyMDMxWjB+MRMwEQYDVQQDDAox +NjA5NjUyNzM1MRswGQYDVQQKDBLlvq7kv6HllYbmiLfns7vnu58xKjAoBgNVBAsM +IeWbm+W3neiFvumjnuaJrOenkeaKgOaciemZkOWFrOWPuDELMAkGA1UEBgwCQ04x +ETAPBgNVBAcMCFNoZW5aaGVuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAp8Qc11qNPnF/iO88GMHgjjq/BUZpXAeExpNFs3+D0dKkloD5ssV/kvVXZPIp +INRjyl0mCovjyAZ4J8VLg/XZbG9f5Od6p0eXWMULubooI7Ii+K8pLE6eowwrieuY +4AXRLi339q8kiQC1gKZc0jN3YOWpdtss3ioFIaxLpHPNzaNG2aqeiEjQtJmkV17P +XFaM8hkqY2Csewb/yFXtUBcyJhrKGDgverNNu0Pz4DhVmw1cyjYTUOOY5cBErxza +FdMDXyAL3MJ3yN2h+zOJWSRwtYUwzqurBGvEwFlv/DTSmOoszPbSbB9rkPRlv2Zg +lBI55udovKZDbN+dRGy+08WO2QIDAQABo4GBMH8wCQYDVR0TBAIwADALBgNVHQ8E +BAMCBPAwZQYDVR0fBF4wXDBaoFigVoZUaHR0cDovL2V2Y2EuaXRydXMuY29tLmNu +L3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJDMDRCMDZBRDM5NzU0OTg0 +NkMwMUMzRThFQkQyMA0GCSqGSIb3DQEBCwUAA4IBAQA8uLOYkgqJMfN6YdS5ZqBl +Q0BuaOADR8+GJAC828CGce4DAaeVLHo3lR/cuYeS4ursegw82tq1MW0kxTELhaCE +MeOVm2dui7ydQXuEx4GUVXK+VC5KN4HlnQe4roFqDXeVEcRrlkRQP+af9Tek9Vip +oywBdCLh3W58QlaPrNd565lqq+77HGn/7uj3aWjPVomr0M8Hc/LOiSyhefU0ot2i +PKBSpwM9j0l4Syk7bDk48BGWqq1UdggXZqLJ7HNIyX0lW3AaHGhIYopAK2b3d4X5 +DR6mDLekmLRc1QeUFUhw5dJ+pnLxvH6PUJeMq7RNf/k9EyGZJdTvBKsHpKdnWgfD +-----END CERTIFICATE----- diff --git a/test/Util.AspNetCore.Tests.Integration/Samples/Certificate/apiclient_key.pem b/test/Util.AspNetCore.Tests.Integration/Samples/Certificate/apiclient_key.pem new file mode 100644 index 000000000..e3170e3b8 --- /dev/null +++ b/test/Util.AspNetCore.Tests.Integration/Samples/Certificate/apiclient_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCnxBzXWo0+cX+I +7zwYweCOOr8FRmlcB4TGk0Wzf4PR0qSWgPmyxX+S9Vdk8ikg1GPKXSYKi+PIBngn +xUuD9dlsb1/k53qnR5dYxQu5uigjsiL4ryksTp6jDCuJ65jgBdEuLff2rySJALWA +plzSM3dg5al22yzeKgUhrEukc83No0bZqp6ISNC0maRXXs9cVozyGSpjYKx7Bv/I +Ve1QFzImGsoYOC96s027Q/PgOFWbDVzKNhNQ45jlwESvHNoV0wNfIAvcwnfI3aH7 +M4lZJHC1hTDOq6sEa8TAWW/8NNKY6izM9tJsH2uQ9GW/ZmCUEjnm52i8pkNs351E +bL7TxY7ZAgMBAAECggEATp0RPsDJ6vbYYlxodtly1nMky94AxQUo7LxaLxCTvqDM +S1JIr8LoUv7wYvcu2plr4Yngp5SBult9tnU3O7Dan3tZHjRyPyzG+Ovs4K0g3gk9 +Zfb+fiqhH5o2VLy3uFJBgTg9N7U+k4xngM90M3O2fwtfa7isv7ZFDVBlabxyNeeI +RYo7NYzkJCJjPq+LyW58F1y3BCs4s7ycN9PgxrkH52QFxq829p0zpXYcLhYygYH7 +EgUu+Cc/rFsuwp4DLslBVHmW0s1q40/8nRU3ytA1VWgTL7SRrWYGPm+3exgbFLh5 +RffTSHWFWqfx2W8VRN7qzZgvO5LeHXQAMzwCcYj4AQKBgQDdjfg7FM1vBHewqGCa +FGFrIeIZ4yOSBhMW3HvNmeswfSsrXn9szXTdZ+2n6kSgMbRJQKfrss91tNiInrF/ +tIo7av8ITKyIFFGR1Oauk3lxPxOINe925xImtyFMSoRZWzwd8MeckWC/GRNTUGe0 +1TNqYzcUuqPJunoF/UKmK10zgQKBgQDB2VJzdUgeEvogn2GesMoWtgQ6Y/o9sZQH +bXXJQwT0iROFoMnm/W9vliCmHy3OUnP/Nsr7pFy6rU4kWYll2ACsXRJ3DBL0YlAk +6gPsbXj6+yilg50ZPVAeAdV5g+lGIlMg5Uc5dAm7p7NaWHJE8iKvd2SpzfEfUJSo +vXOBmIYnWQKBgGj/hlvhoPZzJJIpShbbf8u49CGWnt3uWQ+lkFrBDlAdkd7s2vDi +cfcX8O56POm6hJmOtcYjKOS8vz4ID1DZbAV60MDLLZ0UHBBD2Mt1gsFjxkrwXLt6 +CQUmFvbD/fMnJNd/q+oMj9Ag/yKl4XA29jX+LTV6unK5GGlMPFDqDjKBAoGAEORu +Qe9zdGN09ojNCfYf9jCwV6JEuUGTQ75vKDW15dVYJSnozj9BibjMaY2IAVXAE/pj +X+Bn0ECv8Yz34y8ah+qoZEViFXfHwh++bcjwZPakCSBZ/UwRgjvorWkoV9s/4Tr5 +pPEOtBKeF35jnsx251GBLSbsHy13dujjeuSetCkCgYB7jzPMYLpQWbzYfqnQsUX1 +ZqKDWK+HOE3ZrxUZMBnbfs+0txnkgeF7yF1J52DLuAt1GlH+cYYA0oR6XajyOChd +BqKF1oi7Lsq/FqUi3eYaAlOPjVTZ+u4jqYrukzipWWbV9avfn02O/XLB8sm5ug2D +OYiAx6Hssmet8+KrcUA27A== +-----END PRIVATE KEY----- diff --git a/test/Util.AspNetCore.Tests.Integration/Samples/CustomerDto.cs b/test/Util.AspNetCore.Tests.Integration/Samples/CustomerDto.cs new file mode 100644 index 000000000..d8bd87a07 --- /dev/null +++ b/test/Util.AspNetCore.Tests.Integration/Samples/CustomerDto.cs @@ -0,0 +1,50 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace Util.AspNetCore.Tests.Samples; + +/// +/// 客户参数 +/// +public class CustomerDto { + /// + /// 标识 + /// + public string Id { get; set; } + /// + /// 编码 + /// + [Description( "编码" )] + [MaxLength( 100 )] + public string Code { get; set; } + /// + /// 姓名 + /// + [Description( "姓名" )] + [MaxLength( 200 )] + public string Name { get; set; } + /// + /// 昵称 + /// + [Description( "昵称" )] + [MaxLength( 50 )] + public string Nickname { get; set; } + /// + /// 出生日期 + /// + [Description( "出生日期" )] + public DateTime? Birthday { get; set; } + /// + /// 手机号 + /// + [Description( "手机号" )] + [MaxLength( 50 )] + public string Phone { get; set; } + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + [MaxLength( 500 )] + public string Email { get; set; } +} \ No newline at end of file diff --git a/test/Util.AspNetCore.Tests.Integration/Samples/CustomerQuery.cs b/test/Util.AspNetCore.Tests.Integration/Samples/CustomerQuery.cs new file mode 100644 index 000000000..5fde8dd5d --- /dev/null +++ b/test/Util.AspNetCore.Tests.Integration/Samples/CustomerQuery.cs @@ -0,0 +1,39 @@ +using System.ComponentModel; + +namespace Util.AspNetCore.Tests.Samples; + +/// +/// 客户查询参数 +/// +public class CustomerQuery { + /// + /// 客户标识 + /// + [Description( "客户标识" )] + public int? CustomerId { get; set; } + /// + /// 编码 + /// + [Description( "编码" )] + public string Code { get; set; } + /// + /// 姓名 + /// + [Description( "姓名" )] + public string Name { get; set; } + /// + /// 昵称 + /// + [Description( "昵称" )] + public string Nickname { get; set; } + /// + /// 手机号 + /// + [Description( "手机号" )] + public string Phone { get; set; } + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + public string Email { get; set; } +} \ No newline at end of file diff --git a/test/Util.AspNetCore.Tests.Integration/Startup.cs b/test/Util.AspNetCore.Tests.Integration/Startup.cs new file mode 100644 index 000000000..be370d2f1 --- /dev/null +++ b/test/Util.AspNetCore.Tests.Integration/Startup.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Http; +using Xunit.DependencyInjection.Logging; + +namespace Util.AspNetCore.Tests; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + hostBuilder.ConfigureWebHostDefaults( webHostBuilder => { + webHostBuilder.UseTestServer() + .Configure( t => { + t.UseRouting(); + t.UseAuthentication(); + t.UseAuthorization(); + t.UseEndpoints( endpoints => { + endpoints.MapControllers(); + } ); + } ); + } ) + .AsBuild() + .AddAcl() + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddControllers(); + services.AddTransient( t => { + var client = new HttpClientService(); + client.SetHttpClient( t.GetService().GetTestClient() ); + return client; + } ); + } +} \ No newline at end of file diff --git a/test/Util.AspNetCore.Tests.Integration/Util.AspNetCore.Tests.Integration.csproj b/test/Util.AspNetCore.Tests.Integration/Util.AspNetCore.Tests.Integration.csproj new file mode 100644 index 000000000..ba2fe8349 --- /dev/null +++ b/test/Util.AspNetCore.Tests.Integration/Util.AspNetCore.Tests.Integration.csproj @@ -0,0 +1,47 @@ + + + + $(NetTargetFramework) + false + Util.AspNetCore.Tests + Util.AspNetCore.Tests.Startup + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + Always + + + Always + + + Always + + + Always + + + + \ No newline at end of file diff --git a/test/Util.Biz.Tests.Integration/Payments/Alipay/AlipayBarcodePayServiceTest.cs b/test/Util.Biz.Tests.Integration/Payments/Alipay/AlipayBarcodePayServiceTest.cs deleted file mode 100644 index b41785f50..000000000 --- a/test/Util.Biz.Tests.Integration/Payments/Alipay/AlipayBarcodePayServiceTest.cs +++ /dev/null @@ -1,158 +0,0 @@ -using System; -using System.Threading.Tasks; -using Util.Biz.Payments; -using Util.Biz.Payments.Alipay.Results; -using Util.Biz.Payments.Alipay.Services; -using Util.Biz.Payments.Core; -using Util.Biz.Tests.Integration.Payments.Alipay.Configs; -using Util.Exceptions; -using Util.Helpers; -using Util.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; - -namespace Util.Biz.Tests.Integration.Payments.Alipay { - /// - /// 支付宝条码支付服务测试 - /// - public class AlipayBarcodePayServiceTest : IDisposable { - /// - /// 控制台输出 - /// - private readonly ITestOutputHelper _output; - /// - /// 支付宝条码支付服务 - /// - private AlipayBarcodePayService _service; - - /// - /// 测试初始化 - /// - public AlipayBarcodePayServiceTest( ITestOutputHelper output ) { - _output = output; - _service = new AlipayBarcodePayService( new TestConfigProvider() ); - } - - /// - /// 测试清理 - /// - public void Dispose() { - Time.Reset(); - } - - /// - /// 验证订单标题为空时,将订单号赋值给订单标题 - /// - [Fact] - public void TestPay_Validate_Subject() { - var param = new PayParam { - Money = 1, - OrderId = "a", - AuthCode = "1" - }; - _service.Pay( param ); - Assert.Equal( "a", param.Subject ); - } - - /// - /// 验证订单号为空 - /// - [Fact] - public void TestPay_Validate_OrderId() { - AssertHelper.Throws( () => { - _service.Pay( new PayParam { - Money = 1, - AuthCode = "1", - Subject = "a" - } ); - }, PayResource.OrderIdIsEmpty ); - } - - /// - /// 验证无效金额 - /// - [Fact] - public void TestPay_Validate_Money() { - AssertHelper.Throws( () => { - _service.Pay( new PayParam { - Money = 0, - OrderId = "a", - Subject = "a", - AuthCode = "1" - } ); - }, PayResource.InvalidMoney ); - } - - /// - /// 验证用户付款授权码为空 - /// - [Fact] - public void TestPay_Validate_AuthCode() { - AssertHelper.Throws( () => { - _service.Pay( new PayParam { - Money = 10, - OrderId = "a", - Subject = "test" - } ); - }, PayResource.AuthCodeIsEmpty ); - } - - /// - /// 测试支付 - /// - [Fact] - public void TestPay_1() { - var result = "app_id=2016090800463464&biz_content={\"out_trade_no\":\"59f7caeeab89e009e4a4e1fb\",\"subject\":\"test\",\"total_amount\":\"10\",\"timeout_express\":\"90m\",\"auth_code\":\"281023564031402341\",\"scene\":\"bar_code\"}&charset=utf-8&format=json&method=alipay.trade.pay&sign=TrzaFo0mYi5ECKxQqwVj8WeNivimDYdeZfRF9bGV4Hq2rAoaZLshvV6C/1oeGJq/jsfiFFvxi6RNSlJuo2+erZq81FYALKltt+8gL6XwgCf8KL64+nC3zpE1dAmkKJA3ft4xKoEG5uUSMQBKqx59E3DNApAeibrFboF5vP1MB/Dru1pfS7mijixWhPd1LSDMdH0tUCyWlkh1W1MiWnrzBCNNEaNn2slHvYQjyUHZyR577yuCdcst5/MjCwY+0l0Rt0QYaCPizQEU1n6h65gifq/sSyOlkLnaMeX2JZpRSD7yCVzSyMHqsYRAr5vzSBztfYIrMVdb74JkQQKa27QVng==&sign_type=RSA2×tamp=2017-10-31 08:59:26&version=1.0"; - Time.SetTime( "2017-10-31 08:59:26" ); - _service = new AlipayBarcodePayService( new TestConfigProvider() ) { IsSendRequest = false }; - _service.Pay( new PayParam { - Money = 10, - OrderId = "59f7caeeab89e009e4a4e1fb", - Subject = "test", - AuthCode = "281023564031402341" - } ); - _output.WriteLine( _service.ToString() ); - Assert.Equal( result, _service.ToString() ); - } - - /// - /// 测试支付 - /// - [Fact( Skip = "请下载沙箱支付宝,填写正确的付款码" )] - public void TestPay_2() { - var result = _service.Pay( new PayParam { - Money = 10, - OrderId = Id.Guid(), - AuthCode = "280299913207329986" - } ); - _output.WriteLine( result.Message ); - Assert.True( result.Success ); - } - - /// - /// 测试支付 - /// - [Fact( Skip = "请下载沙箱支付宝,填写正确的付款码" )] - public async Task TestPayAsync() { - var result = await _service.PayAsync( new PayParam { - Money = 10, - OrderId = Id.Guid(), - AuthCode = "281296574204429642" - } ); - _output.WriteLine( result.Message ); - Assert.True( result.Success ); - } - - /// - /// 测试Json转换 - /// - [Fact] - public void TestJson() { - const string json = "{\"alipay_trade_pay_response\":{\"code\":\"10000\",\"msg\":\"Success\",\"buyer_logon_id\":\"jeu*** @sandbox.com\",\"buyer_pay_amount\":\"10.00\",\"buyer_user_id\":\"2088102174804335\",\"fund_bill_list\":[{\"amount\":\"10.00\",\"fund_channel\":\"ALIPAYACCOUNT\"}],\"gmt_payment\":\"2017-10-31 13:23:12\",\"invoice_amount\":\"10.00\",\"open_id\":\"20881058260191225496241750118233\",\"out_trade_no\":\"06bfa9807832438dac6a2b785ad7addc\",\"point_amount\":\"0.00\",\"receipt_amount\":\"10.00\",\"total_amount\":\"10.00\",\"trade_no\":\"2017103121001004330200344580\"},\"sign\":\"CkMKbLdFmlhIz0Ymob7IjnGdjBDfAt5/aAZ7l0jMvwFJyBRf0TaRJiHfXTCI7srL68RQ5DnR6N89XSr1+MiclVbpbNa3joi4XDd1sdEkTKMcEmp28tvL9q3UAbMtwKgiS93CWjmj/D5xK7K+ZxwVPwF3JlkeCd2Qg5GAtHmNjAAt3tlEKVn+SmRQ0yKyk2PpvVSSBBYbFo+VircmOxHo/m/ji3sK68y0ikhQYhHRuNQXXTp3KellpIESaIUGHi8KdQa7lV2acnDnSDChWy/4PxIrEmm8Ki8PMKsqS8WiIwKiUTldeWGZ0D749oP4iq6n18iDtjmSDeEgOTkhErVKLg==\"}"; - var result = new AlipayResult( json ); - Assert.Equal( "10000", result.GetCode() ); - Assert.Equal( "2017103121001004330200344580", result.GetTradeNo() ); - Assert.True( result.HasKey( "sign" ) ); - } - } -} diff --git a/test/Util.Biz.Tests.Integration/Payments/Alipay/Configs/TestConfigProvider.cs b/test/Util.Biz.Tests.Integration/Payments/Alipay/Configs/TestConfigProvider.cs deleted file mode 100644 index 832e9e1eb..000000000 --- a/test/Util.Biz.Tests.Integration/Payments/Alipay/Configs/TestConfigProvider.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Util.Biz.Payments.Alipay.Configs; - -namespace Util.Biz.Tests.Integration.Payments.Alipay.Configs { - /// - /// 支付宝测试配置提供器 - /// - public class TestConfigProvider : IAlipayConfigProvider { - /// - /// 获取配置 - /// - public AlipayConfig GetConfig() { - return new AlipayConfig { - GatewayUrl = "https://openapi.alipaydev.com/gateway.do", - AppId = "2016090800463464", - AppPrivateKey = "MIIEowIBAAKCAQEA4/wh822ynz+AutSuBfVgK39y9kU/QjlSb7sAhiypOdomZT4p7fyaVmXLdg361E3BeykbTVVxaFNZjRZ2p7Mk5VPl6cFB+eU+yJ8tsi8a/6zmhjT3Wf4bMYE417Dld9UfAzsVNP/UyukqjNlx9QkOLzKqrgAT4xU0i7OiWHUDqt3j62NFLGy3ofZxDS/Bq0xbbO/nH5t0qhBZbD5u1AajxL7S2sdcVwG+w7f/g5DLDHVt21KSZi3ML5je1FzhQUz5Cgb8Z/7R2KNqDoG8CQqafrD6QRAk+U3Khx/OoNEADZyg27hRIEuLJNj7IiiK47QEQGCIdktynKzbdMqK0vnqWwIDAQABAoIBAHPehPLPYeUFxUsvJHLqzsHMuITplMj3kSowBIgs0qUQdksmWPEXXOlkOw/48u5LxnXt4m5fao/3LKBENnHs3mefSE6RZhK3rD0SiYrx3err2Q2EheI6/18dqeIVicppiqV9tb2F+IRYFMQZD43V2FusiIp+h5z1/gqydWQ3tHF19uh9WoKS6Lj8RVo+Z9iHovJEBFOqEgJWu8TON16Gk+aebj5diJd4Rz0JRMPFaJO5XT59A7r9YtrZLwEP17loKXCAHMlxgzye+Mwta3nDT4vBgPW7uqIV0lbRV9dTJC+W36eYjL2X4lvJ77deAix3MZS1NANIJ920VFw5ccbBcQECgYEA94nWk6ZfZdAAAbwZX9jmuY9SJJ4aVUHvgfTyao5jlGN5SeeZrmXvDYMPfzEtYwHsRwZj3x4CZoUWX4U1jVgL34tIZBE0n0hXKsBJBmJuykT4iPVNkb6egsLpw5MkTwmIGGjUAqmqfN5yzWMsT7Yj7rJ7+E6VMOcWtxom+btugjsCgYEA68cvZSB+6/fcj8sMV/3+vP9ZhTTAIjtkF4npg3t7/1KeryCGz/9kiolcxDnWoT7wMi1dHQn/c2Az5yx75ZBIq9vyS6cyYa1FmIeClqrz6e8hBqcvfvx05TqjEcf02ZOBVu7Y9aj7YVC6BQgkJ6rse6dbkKyNf13ATlhhe+CklmECgYEAs3jAlrSdnhe8qYVTcGa2gfsjt2DZ/s6bdEHAMPmjwUaN/cNpbUSCme1YIwPowEv4n1ZGutHrM6FyozbVqPEZSq6Vgdfnq0SrzlczmulrIkP3XnVcFDt8eZoQGrhoLtXeUrROYs/YyEgQPFGXIwQP8VR/BX0UNiyWD5Nqhs4aRVkCgYBr7r3ajFqtyMR2KtxWt5ZSjI49dV1zDj8Oq3pzTyb/RaIQx1IPi3lKFgGabmO7YuVwAuY6MeZ3uZVpASsidr9dtQ0g5sQghMJ0RaxZLp8D+wziT8xlVEpq2UHnFOMYavbthd9Z7eZNsTfGr+hlJFCndoBrJSKNKKys8LaHj9moYQKBgAk0H7IMMQgdaGQmczk9NenRI4uE5A2Mra8k8MeTJyXGkWG5Ew6eMr/pSTIrkN8iQkSswv8MGYkb0lyazvWlm5lLGF3x/Jmh+3XIsjmwHDSHiMJDBJVkpTbjGQ15KPouzCO3gDJZYYWOO0dDj9tTEvnN8ftjLGE2gSJdgKB5hyXa", - AlipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnmjf+QpGqixBIgk2FYNigemzhiagH0T0qIAN4sHofOacqFYknNG3vZbsoFy5SfBuYBnqBLxF8rekoeKx3RnZn1KUS9KD7VOAlVVBRpopTM7AzvzQrylICF1atRSuSqvw14vCXbAnzvagJR1OLwSE/2uFAqrRAXCampMU30D3OiLgoyJoIMTzg28LGMYS0cnTs44OkgleQtlGWMBr0V1bpOvYTNveETeASbAUMp5J7ofy+JRpX1YwcjY95ecpQWv9dp72XzjKN5q6l1FlLUeIewRde+Ry70M5/P90ofzCVmcaq30pbp0fQh3KyI4SJ6+NdqlF/GzLwO61gbF9xwIZ0QIDAQAB", - NotifyUrl = "test" - }; - } - } -} diff --git a/test/Util.Biz.Tests.Integration/Payments/Alipay/Parameters/AlipayContentBuilderTest.cs b/test/Util.Biz.Tests.Integration/Payments/Alipay/Parameters/AlipayContentBuilderTest.cs deleted file mode 100644 index 2d7acc272..000000000 --- a/test/Util.Biz.Tests.Integration/Payments/Alipay/Parameters/AlipayContentBuilderTest.cs +++ /dev/null @@ -1,85 +0,0 @@ -using Util.Biz.Payments.Alipay.Parameters; -using Util.Biz.Payments.Core; -using Xunit; - -namespace Util.Biz.Tests.Integration.Payments.Alipay.Parameters { - /// - /// 支付宝内容参数生成器测试 - /// - public class AlipayContentBuilderTest { - /// - /// 支付宝内容参数生成器 - /// - private readonly AlipayContentBuilder _builder; - - /// - /// 测试初始化 - /// - public AlipayContentBuilderTest() { - _builder = new AlipayContentBuilder(); - } - - /// - /// 加载参数 - /// - [Fact] - public void TestLoad() { - _builder.Load( new PayParam { OrderId = "a",AuthCode = "b",Subject = "c",Timeout = 2,Money = 88.88M} ); - Assert.Equal( "out_trade_no=a&subject=c&total_amount=88.88&timeout_express=2m", _builder.ToString() ); - } - - /// - /// 添加商户订单号 - /// - [Fact] - public void TestOutTradeNo() { - _builder.OutTradeNo( "a" ); - Assert.Equal( "out_trade_no=a", _builder.ToString() ); - } - - /// - /// 设置场景 - /// - [Fact] - public void TestScene() { - _builder.Scene( "a" ); - Assert.Equal( "scene=a", _builder.ToString() ); - } - - /// - /// 设置用户付款授权码 - /// - [Fact] - public void TestAuthCode() { - _builder.AuthCode( "a" ); - Assert.Equal( "auth_code=a", _builder.ToString() ); - } - - /// - /// 设置订单标题 - /// - [Fact] - public void TestSubject() { - _builder.Subject( "a" ); - Assert.Equal( "subject=a", _builder.ToString() ); - } - - /// - /// 设置支付超时 - /// - [Fact] - public void TestTimeoutExpress() { - _builder.TimeoutExpress( 2 ); - Assert.Equal( "timeout_express=2m", _builder.ToString() ); - } - - /// - /// 设置支付金额 - /// - [Fact] - public void TestTotalAmount() { - _builder.TotalAmount( 88.88M ); - Assert.Equal( "total_amount=88.88", _builder.ToString() ); - } - } -} diff --git a/test/Util.Biz.Tests.Integration/Payments/Alipay/Parameters/AlipayParameterBuilderTest.cs b/test/Util.Biz.Tests.Integration/Payments/Alipay/Parameters/AlipayParameterBuilderTest.cs deleted file mode 100644 index 5e3b98ad3..000000000 --- a/test/Util.Biz.Tests.Integration/Payments/Alipay/Parameters/AlipayParameterBuilderTest.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using Util.Biz.Payments.Alipay.Configs; -using Util.Biz.Payments.Alipay.Parameters; -using Util.Helpers; -using Xunit; -using Xunit.Abstractions; - -namespace Util.Biz.Tests.Integration.Payments.Alipay.Parameters { - /// - /// 支付宝参数生成器测试 - /// - public class AlipayParameterBuilderTest : IDisposable { - /// - /// 输出 - /// - private readonly ITestOutputHelper _output; - - /// - /// 支付宝参数生成器 - /// - private AlipayParameterBuilder _builder; - - /// - /// 测试初始化 - /// - public AlipayParameterBuilderTest( ITestOutputHelper output ) { - _output = output; - Time.SetTime( "2000-10-10 10:10:10" ); - _builder = new AlipayParameterBuilder( new AlipayConfig() ); - } - - /// - /// 测试清理 - /// - public void Dispose() { - Time.Reset(); - } - - /// - /// 添加设置配置 - /// - [Fact] - public void TestConfig() { - _builder = new AlipayParameterBuilder( new AlipayConfig { AppId = "a" } ); - _output.WriteLine( _builder.ToString() ); - Assert.Equal( "app_id=a&charset=utf-8&format=json&sign_type=RSA2×tamp=2000-10-10 10:10:10&version=1.0", _builder.ToString() ); - } - - /// - /// 添加应用Id - /// - [Fact] - public void TestAppId() { - _builder.AppId( "a" ); - Assert.Equal( "app_id=a&charset=utf-8&format=json&sign_type=RSA2×tamp=2000-10-10 10:10:10&version=1.0", _builder.ToString() ); - } - - /// - /// 添加请求方法 - /// - [Fact] - public void TestMethod() { - _builder.Method( "a" ); - Assert.Equal( "charset=utf-8&format=json&method=a&sign_type=RSA2×tamp=2000-10-10 10:10:10&version=1.0", _builder.ToString() ); - } - - /// - /// 添加内容 - /// - [Fact] - public void TestContent() { - var contentBuilder = new AlipayContentBuilder(); - contentBuilder.OutTradeNo( "a" ); - _builder.Content( contentBuilder ); - Assert.Equal( "biz_content={\"out_trade_no\":\"a\"}&charset=utf-8&format=json&sign_type=RSA2×tamp=2000-10-10 10:10:10&version=1.0", _builder.ToString() ); - } - } -} diff --git a/test/Util.Biz.Tests.Integration/Util.Biz.Tests.Integration.csproj b/test/Util.Biz.Tests.Integration/Util.Biz.Tests.Integration.csproj deleted file mode 100644 index b68520eee..000000000 --- a/test/Util.Biz.Tests.Integration/Util.Biz.Tests.Integration.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - - netcoreapp2.0 - - false - - - - ..\..\output\test - ..\..\output\test\netcoreapp2.0\Util.Biz.Tests.Integration.xml - - - - ..\..\output\test - ..\..\output\test\netcoreapp2.0\Util.Biz.Tests.Integration.xml - - - - - - - - - - - - - - - - - diff --git a/test/Util.Biz.Tests.Integration/XUnitHelpers/AssertHelper.cs b/test/Util.Biz.Tests.Integration/XUnitHelpers/AssertHelper.cs deleted file mode 100644 index de6a7a74a..000000000 --- a/test/Util.Biz.Tests.Integration/XUnitHelpers/AssertHelper.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using AspectCore.DynamicProxy; -using Xunit; - -namespace Util.Tests.XUnitHelpers { - /// - /// 断言操作 - /// - public class AssertHelper { - /// - /// 抛出异常,并从异常消息中搜索特定关键字 - /// - /// 异常类型 - /// 操作 - /// 关键字 - public static TException Throws( Action action,string keyword = "" ) where TException : Exception { - var exception = GetException( action ); - if( !string.IsNullOrWhiteSpace(keyword) ) - Assert.Contains( keyword, exception.Message ); - return exception; - } - - /// - /// 获取异常 - /// - private static TException GetException( Action action ) where TException : Exception { - try { - var exception = Assert.Throws( action ); - return (TException)exception.InnerException; - } - catch { - return Assert.Throws( action ); - } - } - } -} diff --git a/test/Util.Caching.EasyCaching.Tests.Integration/Samples/CacheService.cs b/test/Util.Caching.EasyCaching.Tests.Integration/Samples/CacheService.cs new file mode 100644 index 000000000..730c42aab --- /dev/null +++ b/test/Util.Caching.EasyCaching.Tests.Integration/Samples/CacheService.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Util.Helpers; + +namespace Util.Caching.EasyCaching.Samples; + +/// +/// 缓存测试服务 +/// +public class CacheService : ICacheService { + /// + /// 获取值 + /// + public string GetValue() { + return Id.Create(); + } + + /// + /// 获取值 - 带一个简单参数 + /// + public string GetValue( int id ) { + return $"{Id.Create()}:{id}"; + } + + /// + /// 异步获取值 + /// + public async Task GetValueAsync() { + await Task.Delay( 1 ); + return Id.Create(); + } + + /// + /// 获取对象集合 + /// + public List GetItems() { + return new List { new ( "a", Id.Create() ), new( "a", Id.Create() ) }; + } + + /// + /// 获取对象集合 + /// + public async Task> GetItemsAsync( int id, string name, Item item, List items ) { + await Task.Delay( 1 ); + var result = new List(); + items.ForEach( t => { + var value = $"{id}-{name}-{item.Text}-{item.Value}-{t.Text}-{t.Value}"; + result.Add( new Item( "",value ) ); + } ); + return result; + } +} \ No newline at end of file diff --git a/test/Util.Caching.EasyCaching.Tests.Integration/Samples/ICacheService.cs b/test/Util.Caching.EasyCaching.Tests.Integration/Samples/ICacheService.cs new file mode 100644 index 000000000..4fc8f9563 --- /dev/null +++ b/test/Util.Caching.EasyCaching.Tests.Integration/Samples/ICacheService.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Util.Dependency; + +namespace Util.Caching.EasyCaching.Samples; + +/// +/// 缓存测试服务 +/// +public interface ICacheService : ITransientDependency { + /// + /// 获取值 + /// + [LocalCache] + string GetValue(); + /// + /// 获取值 - 带一个简单参数 + /// + [LocalCache] + string GetValue( int id ); + /// + /// 异步获取值 + /// + [LocalCache] + Task GetValueAsync(); + /// + /// 获取对象集合 + /// + [LocalCache] + List GetItems(); + /// + /// 获取对象集合 + /// + [LocalCache] + Task> GetItemsAsync( int id, string name , Item item, List items ); +} \ No newline at end of file diff --git a/test/Util.Caching.EasyCaching.Tests.Integration/Startup.cs b/test/Util.Caching.EasyCaching.Tests.Integration/Startup.cs new file mode 100644 index 000000000..23f059843 --- /dev/null +++ b/test/Util.Caching.EasyCaching.Tests.Integration/Startup.cs @@ -0,0 +1,41 @@ +using EasyCaching.Core.Configurations; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Aop; +using Util.Helpers; +using Xunit.DependencyInjection.Logging; + +namespace Util.Caching.EasyCaching; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Util.Helpers.Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .AsBuild() + .AddAop() + .AddRedisCache( t => { + t.MaxRdSecond = 0; + t.DBConfig.AllowAdmin = true; + t.DBConfig.KeyPrefix = "test:"; + t.DBConfig.Endpoints.Add( new ServerEndPoint( Config.GetConnectionString( "Redis" ), 6379 ) ); + } ) + .AddMemoryCache( t => { + t.MaxRdSecond = 0; + t.CacheNulls = true; + } ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + } +} \ No newline at end of file diff --git a/test/Util.Caching.EasyCaching.Tests.Integration/Tests/CacheManagerTest.cs b/test/Util.Caching.EasyCaching.Tests.Integration/Tests/CacheManagerTest.cs new file mode 100644 index 000000000..e0112820b --- /dev/null +++ b/test/Util.Caching.EasyCaching.Tests.Integration/Tests/CacheManagerTest.cs @@ -0,0 +1,292 @@ +using System; +using System.Threading.Tasks; +using Xunit; + +namespace Util.Caching.EasyCaching.Tests; + +/// +/// EasyCaching +/// +public class CacheManagerTest { + + #region Գʼ + + /// + /// + /// + private readonly ICache _cache; + + /// + /// Գʼ + /// + /// + public CacheManagerTest( ICache cache ) { + _cache = cache; + } + + #endregion + + #region Get + + /// + /// Իȡ + /// + [Fact] + public void TestGet_1() { + // + var key = "c:TestGet_1"; + var value = 1; + + //ȡ,Ϊ + var result = _cache.Get( key ); + Assert.Null( result ); + + //û + _cache.Set( key, value ); + + //֤ + result = _cache.Get( key ); + Assert.Equal( value, result ); + } + + /// + /// Իȡ - û + /// + [Fact] + public void TestGet_2() { + // + var key = new CacheKey( "c:TestGet_2" ); + var value = 1; + + //ȡ,Ϊ + var result = _cache.Get( key ); + Assert.Null( result ); + + //û + _cache.Set( key, value ); + + //֤ + result = _cache.Get( key ); + Assert.Equal( value, result ); + } + + /// + /// Իȡ - ȡ + /// + [Fact] + public void TestGet_3() { + // + var key = "c:TestGet_31"; + var key2 = "c:TestGet_32"; + + //û + _cache.Set( key, 1 ); + _cache.Set( key2, 2 ); + + //֤ + var result = _cache.Get( new[] { key, key2 } ); + Assert.Equal( 1, result[0] ); + Assert.Equal( 2, result[1] ); + } + + /// + /// Իȡ - ȡ,û + /// + [Fact] + public void TestGet_4() { + // + var key = new CacheKey( "c:TestGet_41" ); + var key2 = new CacheKey( "c:TestGet_42" ); + + //û + _cache.Set( key, 1 ); + _cache.Set( key2, 2 ); + + //֤ + var result = _cache.Get( new[] { key, key2 } ); + Assert.Equal( 1, result[0] ); + Assert.Equal( 2, result[1] ); + } + + /// + /// Դӻлȡ - Ĭ8Сʱ + /// + [Fact] + public void TestGet_5() { + var result = 0; + var data = 0; + for ( int i = 0; i < 3; i++ ) { + result = _cache.Get( "TestGet_5", () => { + data++; + return data; + } ); + } + Assert.Equal( 1, result ); + } + + /// + /// Դӻлȡ - 1΢ + /// + [Fact] + public void TestGet_6() { + var result = 0; + var data = 0; + for ( int i = 0; i < 3; i++ ) { + result = _cache.Get( "c:TestGet_6", () => { + data++; + return data; + }, new CacheOptions { Expiration = TimeSpan.FromMicroseconds( 1 ) } ); + } + Assert.NotEqual( 1, result ); + } + + /// + /// Դӻлȡ - û + /// + [Fact] + public void TestGet_7() { + var result = 0; + var data = 0; + for ( int i = 0; i < 3; i++ ) { + result = _cache.Get( new CacheKey( "c:TestGet_7" ), () => { + data++; + return data; + } ); + } + Assert.Equal( 1, result ); + } + + #endregion + + #region GetAsync + + /// + /// Իȡ + /// + [Fact] + public async Task TestGetAsync_1() { + // + var key = "c:TestGetAsync_1"; + var value = 1; + + //ȡ,Ϊ + var result = await _cache.GetAsync( key ); + Assert.Null( result ); + + //û + await _cache.SetAsync( key, value ); + + //֤ + result = await _cache.GetAsync( key ); + Assert.Equal( value, result ); + } + + /// + /// Իȡ - û + /// + [Fact] + public async Task TestGetAsync_2() { + // + var key = new CacheKey( "c:TestGetAsync_2" ); + var value = 1; + + //ȡ,Ϊ + var result = await _cache.GetAsync( key ); + Assert.Null( result ); + + //û + await _cache.SetAsync( key, value ); + + //֤ + result = await _cache.GetAsync( key ); + Assert.Equal( value, result ); + } + + /// + /// Իȡ - ȡ + /// + [Fact] + public async Task TestGetAsync_3() { + // + var key = "c:TestGetAsync_31"; + var key2 = "c:TestGetAsync_32"; + + //û + await _cache.SetAsync( key, 1 ); + await _cache.SetAsync( key2, 2 ); + + //֤ + var result = await _cache.GetAsync( new[] { key, key2 } ); + Assert.Equal( 1, result[0] ); + Assert.Equal( 2, result[1] ); + } + + /// + /// Իȡ - ȡ,û + /// + [Fact] + public async Task TestGetAsync_4() { + // + var key = new CacheKey( "c:TestGetAsync_41" ); + var key2 = new CacheKey( "c:TestGetAsync_42" ); + + //û + await _cache.SetAsync( key, 1 ); + await _cache.SetAsync( key2, 2 ); + + //֤ + var result = await _cache.GetAsync( new[] { key, key2 } ); + Assert.Equal( 1, result[0] ); + Assert.Equal( 2, result[1] ); + } + + /// + /// Դӻлȡ - Ĭ8Сʱ + /// + [Fact] + public async Task TestGetAsync_5() { + var result = 0; + var data = 0; + for ( int i = 0; i < 3; i++ ) { + result = await _cache.GetAsync( "c:TestGetAsync_5", async () => { + data++; + return await Task.FromResult( data ); + } ); + } + Assert.Equal( 1, result ); + } + + /// + /// Դӻлȡ - 1΢ + /// + [Fact] + public async Task TestGetAsync_6() { + var result = 0; + var data = 0; + for ( int i = 0; i < 3; i++ ) { + result = await _cache.GetAsync( "c:TestGetAsync_6", async () => { + data++; + return await Task.FromResult( data ); + }, new CacheOptions { Expiration = TimeSpan.FromMicroseconds( 1 ) } ); + } + Assert.NotEqual( 1, result ); + } + + /// + /// Դӻлȡ - û + /// + [Fact] + public async Task TestGetAsync_7() { + var result = 0; + var data = 0; + for ( int i = 0; i < 3; i++ ) { + result = await _cache.GetAsync( new CacheKey( "c:TestGetAsync_7" ), async () => { + data++; + return await Task.FromResult( data ); + }, new CacheOptions { Expiration = TimeSpan.FromMicroseconds( 1 ) } ); + } + Assert.NotEqual( 1, result ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Caching.EasyCaching.Tests.Integration/Tests/LocalCacheAttributeTest.cs b/test/Util.Caching.EasyCaching.Tests.Integration/Tests/LocalCacheAttributeTest.cs new file mode 100644 index 000000000..fefe6262e --- /dev/null +++ b/test/Util.Caching.EasyCaching.Tests.Integration/Tests/LocalCacheAttributeTest.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Util.Caching.EasyCaching.Samples; +using Xunit; + +namespace Util.Caching.EasyCaching.Tests; + +/// +/// 本地缓存拦截器测试 +/// +public class LocalCacheAttributeTest { + /// + /// 缓存测试服务 + /// + private readonly ICacheService _cacheService; + + /// + /// 测试初始化 + /// + public LocalCacheAttributeTest( ICacheService cacheService ) { + _cacheService = cacheService; + } + + /// + /// 测试获取值 + /// + [Fact] + public void TestGetValue_1() { + var value = _cacheService.GetValue(); + var value2 = _cacheService.GetValue(); + Assert.Equal( value,value2 ); + } + + /// + /// 测试获取值 - 带一个简单参数 + /// + [Fact] + public void TestGetValue_2() { + var value1 = _cacheService.GetValue(1); + var value11 = _cacheService.GetValue( 1 ); + var value2 = _cacheService.GetValue(2); + Assert.Equal( value1, value11 ); + Assert.NotEqual( value1, value2 ); + } + + /// + /// 测试获取值 - 异步 + /// + [Fact] + public async Task TestGetValueAsync() { + var value = await _cacheService.GetValueAsync(); + var value2 = await _cacheService.GetValueAsync(); + Assert.Equal( value, value2 ); + } + + /// + /// 测试获取对象集合 + /// + [Fact] + public void TestGetItems() { + var value = _cacheService.GetItems(); + var value2 = _cacheService.GetItems(); + Assert.Equal( value[0].Value, value2[0].Value ); + Assert.Equal( value[1].Value, value2[1].Value ); + } + + /// + /// 测试获取对象集合 + /// + [Fact] + public async Task TestGetItemsAsync() { + var value1 = await _cacheService.GetItemsAsync(1,"a",new Item( "b",2 ),new List{ new ( "c", 3 ), new ( "d", 4 ) } ); + var value11 = await _cacheService.GetItemsAsync( 1, "a", new Item( "b", 2 ), new List { new ( "c", 3 ), new ( "d", 4 ) } ); + var value2 = await _cacheService.GetItemsAsync( 2, "a", new Item( "b", 2 ), new List { new ( "c", 3 ), new ( "d", 4 ) } ); + Assert.Equal( value1[0].Value, value11[0].Value ); + Assert.Equal( value1[1].Value, value11[1].Value ); + Assert.NotEqual( value1[0].Value, value2[0].Value ); + } +} \ No newline at end of file diff --git a/test/Util.Caching.EasyCaching.Tests.Integration/Tests/MemoryCacheManagerTest.cs b/test/Util.Caching.EasyCaching.Tests.Integration/Tests/MemoryCacheManagerTest.cs new file mode 100644 index 000000000..8c1668f83 --- /dev/null +++ b/test/Util.Caching.EasyCaching.Tests.Integration/Tests/MemoryCacheManagerTest.cs @@ -0,0 +1,1031 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Util.Caching.EasyCaching.Tests; + +/// +/// EasyCachingڴ滺 +/// +public class MemoryCacheManagerTest { + + #region Գʼ + + /// + /// + /// + private readonly ILocalCache _cache; + + /// + /// Գʼ + /// + /// + public MemoryCacheManagerTest( ILocalCache cache ) { + _cache = cache; + } + + #endregion + + #region Get + + /// + /// Իȡ + /// + [Fact] + public void TestGet_1() { + // + var key = "TestGet_1"; + var value = 1; + + //ȡ,Ϊ + var result = _cache.Get( key ); + Assert.Null( result ); + + //û + _cache.Set( key, value ); + + //֤ + result = _cache.Get( key ); + Assert.Equal( value, result ); + } + + /// + /// Իȡ - û + /// + [Fact] + public void TestGet_2() { + // + var key = new CacheKey( "TestGet_2" ); + var value = 1; + + //ȡ,Ϊ + _cache.Remove( key ); + var result = _cache.Get( key ); + Assert.Null( result ); + + //û + _cache.Set( key, value ); + + //֤ + result = _cache.Get( key ); + Assert.Equal( value, result ); + } + + /// + /// Իȡ - ȡ + /// + [Fact] + public void TestGet_3() { + // + var key = "TestGet_31"; + var key2 = "TestGet_32"; + + //û + _cache.Set( key, 1 ); + _cache.Set( key2, 2 ); + + //֤ + var result = _cache.Get( new []{ key , key2 } ); + Assert.Equal( 1,result[0] ); + Assert.Equal( 2, result[1] ); + } + + /// + /// Իȡ - ȡ,û + /// + [Fact] + public void TestGet_4() { + // + var key = new CacheKey( "TestGet_41" ); + var key2 = new CacheKey( "TestGet_42" ); + + //û + _cache.Set( key, 1 ); + _cache.Set( key2, 2 ); + + //֤ + var result = _cache.Get( new[] { key, key2 } ); + Assert.Equal( 1, result[0] ); + Assert.Equal( 2, result[1] ); + } + + /// + /// Դӻлȡ - Ĭ8Сʱ + /// + [Fact] + public void TestGet_5() { + var result = 0; + var data = 0; + for ( int i = 0; i < 3; i++ ) { + result = _cache.Get( "TestGet_5", () => { + data++; + return data; + } ); + } + Assert.Equal( 1, result ); + } + + /// + /// Դӻлȡ - 1΢ + /// + [Fact] + public void TestGet_6() { + var result = 0; + var data = 0; + for ( int i = 0; i < 3; i++ ) { + result = _cache.Get( "TestGet_6", () => { + data++; + return data; + }, new CacheOptions { Expiration = TimeSpan.FromMicroseconds( 1 ) } ); + } + Assert.NotEqual( 1, result ); + } + + /// + /// Դӻлȡ - û + /// + [Fact] + public void TestGet_7() { + var result = 0; + var data = 0; + for ( int i = 0; i < 3; i++ ) { + result = _cache.Get( new CacheKey( "TestGet_7" ), () => { + data++; + return data; + } ); + } + Assert.Equal( 1, result ); + } + + /// + /// Դӻлȡ - null ֵ - nullӦ + /// + [Fact] + public void TestGet_8() { + var index = 0; + for( int i = 0; i < 3; i++ ) { + _cache.Get( "TestGet_8", () => { + index++; + return null; + } ); + } + Assert.Equal( 1, index ); + } + + #endregion + + #region GetAsync + + /// + /// Իȡ + /// + [Fact] + public async Task TestGetAsync_1() { + // + var key = "TestGetAsync_1"; + var value = 1; + + //ȡ,Ϊ + var result = await _cache.GetAsync( key ); + Assert.Null( result ); + + //û + await _cache.SetAsync( key, value ); + + //֤ + result = await _cache.GetAsync( key ); + Assert.Equal( value, result ); + } + + /// + /// Իȡ - û + /// + [Fact] + public async Task TestGetAsync_2() { + // + var key = new CacheKey( "TestGetAsync_2" ); + var value = 1; + + //ȡ,Ϊ + await _cache.RemoveAsync( key ); + var result = await _cache.GetAsync( key ); + Assert.Null( result ); + + //û + await _cache.SetAsync( key, value ); + + //֤ + result = await _cache.GetAsync( key ); + Assert.Equal( value, result ); + } + + /// + /// Իȡ - ȡ + /// + [Fact] + public async Task TestGetAsync_3() { + // + var key = "TestGetAsync_31"; + var key2 = "TestGetAsync_32"; + + //û + await _cache.SetAsync( key, 1 ); + await _cache.SetAsync( key2, 2 ); + + //֤ + var result = await _cache.GetAsync( new[] { key, key2 } ); + Assert.Equal( 1, result[0] ); + Assert.Equal( 2, result[1] ); + } + + /// + /// Իȡ - ȡ,û + /// + [Fact] + public async Task TestGetAsync_4() { + // + var key = new CacheKey( "TestGetAsync_41" ); + var key2 = new CacheKey( "TestGetAsync_42" ); + + //û + await _cache.SetAsync( key, 1 ); + await _cache.SetAsync( key2, 2 ); + + //֤ + var result = await _cache.GetAsync( new[] { key, key2 } ); + Assert.Equal( 1, result[0] ); + Assert.Equal( 2, result[1] ); + } + + /// + /// Դӻлȡ - Ĭ8Сʱ + /// + [Fact] + public async Task TestGetAsync_5() { + var result = 0; + var data = 0; + for ( int i = 0; i < 3; i++ ) { + result = await _cache.GetAsync( "TestGetAsync_5", async () => { + data++; + return await Task.FromResult( data ); + } ); + } + Assert.Equal( 1, result ); + } + + /// + /// Դӻлȡ - 1΢ + /// + [Fact] + public async Task TestGetAsync_6() { + var result = 0; + var data = 0; + for ( int i = 0; i < 3; i++ ) { + result = await _cache.GetAsync( "TestGetAsync_6", async () => { + data++; + return await Task.FromResult( data ); + }, new CacheOptions { Expiration = TimeSpan.FromMicroseconds( 1 ) } ); + } + Assert.NotEqual( 1, result ); + } + + /// + /// Դӻлȡ - û + /// + [Fact] + public async Task TestGetAsync_7() { + var result = 0; + var data = 0; + for ( int i = 0; i < 3; i++ ) { + result = await _cache.GetAsync( new CacheKey( "TestGetAsync_7" ), async () => { + data++; + return await Task.FromResult( data ); + }, new CacheOptions { Expiration = TimeSpan.FromMicroseconds( 1 ) } ); + } + Assert.NotEqual( 1, result ); + } + + #endregion + + #region GetByPrefix + + /// + /// ͨǰ׺ȡ + /// + [Fact] + public void TestGetByPrefix_1() { + // + var key = "TestGetByPrefix_11"; + var key2 = "TestGetByPrefix_12"; + + //û + _cache.Set( key, 1 ); + _cache.Set( key2, 2 ); + + //֤ + var result = _cache.GetByPrefix( "TestGetByPrefix_1" ).OrderBy( t => t ).ToList(); + Assert.Equal( 1, result[0] ); + Assert.Equal( 2, result[1] ); + } + + #endregion + + #region GetByPrefixAsync + + /// + /// ͨǰ׺ȡ + /// + [Fact] + public async Task TestGetByPrefixAsync_1() { + // + var key = "abc:TestGetByPrefixAsync_11"; + var key2 = "abc:TestGetByPrefixAsync_12"; + + //û + await _cache.SetAsync( key, 1 ); + await _cache.SetAsync( key2, 2 ); + + //֤ + var result = (await _cache.GetByPrefixAsync( "abc:" )).OrderBy( t => t ).ToList(); + Assert.Equal( 1, result[0] ); + Assert.Equal( 2, result[1] ); + } + + #endregion + + #region TrySet + + /// + /// û + /// + [Fact] + public void TestTrySet_1() { + // + var key = "TestTrySet_1"; + var value = 1; + + //ȡ,Ϊ + var result = _cache.Get( key ); + Assert.Null( result ); + + //û1 + _cache.TrySet( key, value ); + result = _cache.Get( key ); + Assert.Equal( value, result ); + + //û2,Ч + _cache.TrySet( key, 2 ); + result = _cache.Get( key ); + Assert.Equal( value, result ); + } + + /// + /// û - û + /// + [Fact] + public void TestTrySet_2() { + // + var key = new CacheKey( "TestTrySet_2" ); + var value = 1; + + //ȡ,Ϊ + var result = _cache.Get( key ); + Assert.Null( result ); + + //û1 + _cache.TrySet( key, value ); + result = _cache.Get( key ); + Assert.Equal( value, result ); + + //û2,Ч + _cache.TrySet( key, 2 ); + result = _cache.Get( key ); + Assert.Equal( value, result ); + } + + #endregion + + #region TrySetAsync + + /// + /// û + /// + [Fact] + public async Task TestTrySetAsync_1() { + // + var key = "TestTrySetAsync_1"; + var value = 1; + + //ȡ,Ϊ + var result = await _cache.GetAsync( key ); + Assert.Null( result ); + + //û1 + await _cache.TrySetAsync( key, value ); + result = await _cache.GetAsync( key ); + Assert.Equal( value, result ); + + //û2,Ч + await _cache.TrySetAsync( key, 2 ); + result = await _cache.GetAsync( key ); + Assert.Equal( value, result ); + } + + /// + /// û - û + /// + [Fact] + public async Task TestTrySetAsync_2() { + // + var key = new CacheKey( "TestTrySetAsync_2" ); + var value = 1; + + //ȡ,Ϊ + var result = await _cache.GetAsync( key ); + Assert.Null( result ); + + //û1 + await _cache.TrySetAsync( key, value ); + result = await _cache.GetAsync( key ); + Assert.Equal( value, result ); + + //û2,Ч + await _cache.TrySetAsync( key, 2 ); + result = await _cache.GetAsync( key ); + Assert.Equal( value, result ); + } + + #endregion + + #region Set + + /// + /// û + /// + [Fact] + public void TestSet_1() { + // + var key = "TestSet_1"; + + //ȡ,Ϊ + var result = _cache.Get( key ); + Assert.Null( result ); + + //û1 + _cache.Set( key, 1 ); + result = _cache.Get( key ); + Assert.Equal( 1, result ); + + //û2, + _cache.Set( key, 2 ); + result = _cache.Get( key ); + Assert.Equal( 2, result ); + } + + /// + /// û- û + /// + [Fact] + public void TestSet_2() { + // + var key = new CacheKey( "TestSet_2" ); + + //ȡ,Ϊ + var result = _cache.Get( key ); + Assert.Null( result ); + + //û1 + _cache.Set( key, 1 ); + result = _cache.Get( key ); + Assert.Equal( 1, result ); + + //û2, + _cache.Set( key, 2 ); + result = _cache.Get( key ); + Assert.Equal( 2, result ); + } + + /// + /// û - ü + /// + [Fact] + public void TestSet_3() { + // + var key = "TestSet_31"; + var key2 = "TestSet_32"; + + //û + _cache.Set( new Dictionary{{key,1}, { key2, 2 } } ); + + //֤ + Assert.Equal( 1, _cache.Get( key ) ); + Assert.Equal( 2, _cache.Get( key2 ) ); + } + + /// + /// û - ü,û + /// + [Fact] + public void TestSet_4() { + // + var key = new CacheKey( "TestSet_41" ); + var key2 = new CacheKey( "TestSet_42" ); + + //û + _cache.Set( new Dictionary { { key, 1 }, { key2, 2 } } ); + + //֤ + Assert.Equal( 1, _cache.Get( key ) ); + Assert.Equal( 2, _cache.Get( key2 ) ); + } + + #endregion + + #region SetAsync + + /// + /// û + /// + [Fact] + public async Task TestSetAsync_1() { + // + var key = "TestSetAsync_1"; + + //ȡ,Ϊ + var result = await _cache.GetAsync( key ); + Assert.Null( result ); + + //û1 + await _cache.SetAsync( key, 1 ); + result = await _cache.GetAsync( key ); + Assert.Equal( 1, result ); + + //û2, + await _cache.SetAsync( key, 2 ); + result = await _cache.GetAsync( key ); + Assert.Equal( 2, result ); + } + + /// + /// û - û + /// + [Fact] + public async Task TestSetAsync_2() { + // + var key = new CacheKey( "TestSetAsync_2" ); + + //ȡ,Ϊ + var result = await _cache.GetAsync( key ); + Assert.Null( result ); + + //û1 + await _cache.SetAsync( key, 1 ); + result = await _cache.GetAsync( key ); + Assert.Equal( 1, result ); + + //û2, + await _cache.SetAsync( key, 2 ); + result = await _cache.GetAsync( key ); + Assert.Equal( 2, result ); + } + + /// + /// û - ü + /// + [Fact] + public async Task TestSetAsync_3() { + // + var key = "TestSetAsync_31"; + var key2 = "TestSetAsync_32"; + + //û + await _cache.SetAsync( new Dictionary { { key, 1 }, { key2, 2 } } ); + + //֤ + Assert.Equal( 1, await _cache.GetAsync( key ) ); + Assert.Equal( 2, await _cache.GetAsync( key2 ) ); + } + + /// + /// û - ü,û + /// + [Fact] + public async Task TestSetAsync_4() { + // + var key = new CacheKey( "TestSetAsync_41" ); + var key2 = new CacheKey( "TestSetAsync_42" ); + + //û + await _cache.SetAsync( new Dictionary { { key, 1 }, { key2, 2 } } ); + + //֤ + Assert.Equal( 1, await _cache.GetAsync( key ) ); + Assert.Equal( 2, await _cache.GetAsync( key2 ) ); + } + + #endregion + + #region Exists + + /// + /// ԻǷѴ + /// + [Fact] + public void TestExists_1() { + // + var key = "TestExists_1"; + + //治 + Assert.False( _cache.Exists( key ) ); + + //û + _cache.Set( key, 1 ); + + //֤ + Assert.True( _cache.Exists( key ) ); + } + + /// + /// ԻǷѴ- û + /// + [Fact] + public void TestExists_2() { + // + var key = new CacheKey( "TestExists_2" ); + + //治 + Assert.False( _cache.Exists( key ) ); + + //û + _cache.Set( key, 1 ); + + //֤ + Assert.True( _cache.Exists( key ) ); + } + + #endregion + + #region ExistsAsync + + /// + /// ԻǷѴ + /// + [Fact] + public async Task TestExistsAsync_1() { + // + var key = "TestExistsAsync_1"; + + //治 + Assert.False( await _cache.ExistsAsync( key ) ); + + //û + await _cache.SetAsync( key, 1 ); + + //֤ + Assert.True( await _cache.ExistsAsync( key ) ); + } + + /// + /// ԻǷѴ - û + /// + [Fact] + public async Task TestExistsAsync_2() { + // + var key = new CacheKey( "TestExistsAsync_2" ); + + //治 + Assert.False( await _cache.ExistsAsync( key ) ); + + //û + await _cache.SetAsync( key, 1 ); + + //֤ + Assert.True( await _cache.ExistsAsync( key ) ); + } + + #endregion + + #region Remove + + /// + /// Ƴ + /// + [Fact] + public void TestRemove_1() { + // + var key = "TestRemove_1"; + + //û + _cache.Set( key, 1 ); + Assert.True( _cache.Exists( key ) ); + + //Ƴ + _cache.Remove( key ); + + //֤ + Assert.False( _cache.Exists( key ) ); + } + + /// + /// Ƴ - û + /// + [Fact] + public void TestRemove_2() { + // + var key = new CacheKey( "TestRemove_2" ); + + //û + _cache.Set( key, 1 ); + Assert.True( _cache.Exists( key ) ); + + //Ƴ + _cache.Remove( key ); + + //֤ + Assert.False( _cache.Exists( key ) ); + } + + /// + /// Ƴ漯 + /// + [Fact] + public void TestRemove_3() { + // + var key = "TestRemove_31"; + var key2 = "TestRemove_32"; + + //û + _cache.Set( key, 1 ); + _cache.Set( key2, 2 ); + + //Ƴ + _cache.Remove( new[] { key, key2 } ); + + //֤ + Assert.False( _cache.Exists( key ) ); + Assert.False( _cache.Exists( key2 ) ); + } + + /// + /// Ƴ漯 - û + /// + [Fact] + public void TestRemove_4() { + // + var key = new CacheKey( "TestRemove_41" ); + var key2 = new CacheKey( "TestRemove_42" ); + + //û + _cache.Set( key, 1 ); + _cache.Set( key2, 2 ); + + //Ƴ + _cache.Remove( new[] { key, key2 } ); + + //֤ + Assert.False( _cache.Exists( key ) ); + Assert.False( _cache.Exists( key2 ) ); + } + + #endregion + + #region RemoveAsync + + /// + /// Ƴ + /// + [Fact] + public async Task TestRemoveAsync_1() { + // + var key = "TestRemoveAsync_1"; + + //û + await _cache.SetAsync( key, 1 ); + Assert.True( await _cache.ExistsAsync( key ) ); + + //Ƴ + await _cache.RemoveAsync( key ); + + //֤ + Assert.False( await _cache.ExistsAsync( key ) ); + } + + /// + /// Ƴ - û + /// + [Fact] + public async Task TestRemoveAsync_2() { + // + var key = new CacheKey( "TestRemoveAsync_2" ); + + //û + await _cache.SetAsync( key, 1 ); + Assert.True( await _cache.ExistsAsync( key ) ); + + //Ƴ + await _cache.RemoveAsync( key ); + + //֤ + Assert.False( await _cache.ExistsAsync( key ) ); + } + + /// + /// Ƴ漯 + /// + [Fact] + public async Task TestRemoveAsync_3() { + // + var key = "TestRemoveAsync_31"; + var key2 = "TestRemoveAsync_32"; + + //û + await _cache.SetAsync( key, 1 ); + await _cache.SetAsync( key2, 2 ); + + //Ƴ + await _cache.RemoveAsync( new[] { key, key2 } ); + + //֤ + Assert.False( await _cache.ExistsAsync( key ) ); + Assert.False( await _cache.ExistsAsync( key2 ) ); + } + + /// + /// Ƴ漯 - û + /// + [Fact] + public async Task TestRemoveAsync_4() { + // + var key = new CacheKey( "TestRemoveAsync_41" ); + var key2 = new CacheKey( "TestRemoveAsync_42" ); + + //û + await _cache.SetAsync( key, 1 ); + await _cache.SetAsync( key2, 2 ); + + //Ƴ + await _cache.RemoveAsync( new[] { key, key2 } ); + + //֤ + Assert.False( await _cache.ExistsAsync( key ) ); + Assert.False( await _cache.ExistsAsync( key2 ) ); + } + + #endregion + + #region RemoveByPrefix + + /// + /// ͨǰ׺Ƴ + /// + [Fact] + public void RemoveByPrefix_1() { + // + var key = "RemoveByPrefix_11"; + var key2 = "RemoveByPrefix_12"; + + //û + _cache.Set( key, 1 ); + _cache.Set( key2, 2 ); + + //֤ + Assert.True( _cache.Exists( key ) ); + Assert.True( _cache.Exists( key2 ) ); + + //Ƴ + _cache.RemoveByPrefix( "RemoveByPrefix" ); + + //֤ + Assert.False( _cache.Exists( key ) ); + Assert.False( _cache.Exists( key2 ) ); + } + + #endregion + + #region RemoveByPrefixAsync + + /// + /// ͨǰ׺Ƴ + /// + [Fact] + public async Task RemoveByPrefixAsync_1() { + // + var key = "RemoveByPrefixAsync_11"; + var key2 = "RemoveByPrefixAsync_12"; + + //û + await _cache.SetAsync( key, 1 ); + await _cache.SetAsync( key2, 2 ); + + //Ƴ + await _cache.RemoveByPrefixAsync( "RemoveByPrefix" ); + + //֤ + Assert.False( await _cache.ExistsAsync( key ) ); + Assert.False( await _cache.ExistsAsync( key2 ) ); + } + + #endregion + + #region RemoveByPattern + + /// + /// ͨģʽƳ + /// + [Fact] + public void RemoveByPattern() { + // + var key = "RemoveByPattern_1"; + var key2 = "RemoveByPattern_2"; + + //û + _cache.Set( key, 1 ); + _cache.Set( key2, 2 ); + + //Ƴ + _cache.RemoveByPattern( "*2" ); + + //֤ + Assert.True( _cache.Exists( key ) ); + Assert.False( _cache.Exists( key2 ) ); + } + + #endregion + + #region RemoveByPatternAsync + + /// + /// ͨģʽƳ + /// + [Fact] + public async Task RemoveByPatternAsync() { + // + var key = "RemoveByPatternAsync_1"; + var key2 = "RemoveByPatternAsync_2"; + + //û + await _cache.SetAsync( key, 1 ); + await _cache.SetAsync( key2, 2 ); + + //Ƴ + await _cache.RemoveByPatternAsync( "*2" ); + + //֤ + Assert.True( await _cache.ExistsAsync( key ) ); + Assert.False( await _cache.ExistsAsync( key2 ) ); + } + + #endregion + + #region Clear + + /// + /// ջ + /// + [Fact] + public void TestClear() { + // + var key = "TestClear_1"; + var key2 = "TestClear_2"; + + //û + _cache.Set( key, 1 ); + _cache.Set( key2, 2 ); + + //ջ + _cache.Clear(); + + //֤ + Assert.False( _cache.Exists( key ) ); + Assert.False( _cache.Exists( key2 ) ); + } + + #endregion + + #region ClearAsync + + /// + /// ջ + /// + [Fact] + public async Task TestClearAsync() { + // + var key = "TestClear_1"; + var key2 = "TestClear_2"; + + //û + await _cache.SetAsync( key, 1 ); + await _cache.SetAsync( key2, 2 ); + + //ջ + await _cache.ClearAsync(); + + //֤ + Assert.False( await _cache.ExistsAsync( key ) ); + Assert.False( await _cache.ExistsAsync( key2 ) ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Caching.EasyCaching.Tests.Integration/Tests/RedisCacheManagerTest.cs b/test/Util.Caching.EasyCaching.Tests.Integration/Tests/RedisCacheManagerTest.cs new file mode 100644 index 000000000..0991fe0dd --- /dev/null +++ b/test/Util.Caching.EasyCaching.Tests.Integration/Tests/RedisCacheManagerTest.cs @@ -0,0 +1,961 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Util.Caching.EasyCaching.Tests; + +/// +/// Redis +/// +public class RedisCacheManagerTest { + + #region Գʼ + + /// + /// + /// + private readonly IRedisCache _cache; + + /// + /// Գʼ + /// + /// + public RedisCacheManagerTest( IRedisCache cache ) { + _cache = cache; + } + + #endregion + + #region Get + + /// + /// Իȡ + /// + [Fact] + public void TestGet_1() { + // + var key = "r:TestGet_1"; + var value = 1; + + //ȡ,Ϊ + var result = _cache.Get( key ); + Assert.Null( result ); + + //û + _cache.Set( key, value ); + + //֤ + result = _cache.Get( key ); + Assert.Equal( value, result ); + } + + /// + /// Իȡ - û + /// + [Fact] + public void TestGet_2() { + // + var key = new CacheKey( "r:TestGet_2" ); + var value = 1; + + //ȡ,Ϊ + var result = _cache.Get( key ); + Assert.Null( result ); + + //û + _cache.Set( key, value ); + + //֤ + result = _cache.Get( key ); + Assert.Equal( value, result ); + } + + /// + /// Իȡ - ȡ + /// + [Fact] + public void TestGet_3() { + // + var key = "r:TestGet_31"; + var key2 = "r:TestGet_32"; + + //û + _cache.Set( key, 1 ); + _cache.Set( key2, 2 ); + + //֤ + var result = _cache.Get( new []{ key , key2 } ); + Assert.Equal( 1,result[0] ); + Assert.Equal( 2, result[1] ); + } + + /// + /// Իȡ - ȡ,û + /// + [Fact] + public void TestGet_4() { + // + var key = new CacheKey( "r:TestGet_41" ); + var key2 = new CacheKey( "r:TestGet_42" ); + + //û + _cache.Set( key, 1 ); + _cache.Set( key2, 2 ); + + //֤ + var result = _cache.Get( new[] { key, key2 } ); + Assert.Equal( 1, result[0] ); + Assert.Equal( 2, result[1] ); + } + + /// + /// Դӻлȡ - Ĭ8Сʱ + /// + [Fact] + public void TestGet_5() { + var result = 0; + var data = 0; + for ( int i = 0; i < 3; i++ ) { + result = _cache.Get( "r:TestGet_5", () => { + data++; + return data; + } ); + } + Assert.Equal( 1, result ); + } + + /// + /// Դӻлȡ - û + /// + [Fact] + public void TestGet_6() { + var result = 0; + var data = 0; + for ( int i = 0; i < 3; i++ ) { + result = _cache.Get( new CacheKey( "r:TestGet_6" ), () => { + data++; + return data; + } ); + } + Assert.Equal( 1, result ); + } + + #endregion + + #region GetAsync + + /// + /// Իȡ + /// + [Fact] + public async Task TestGetAsync_1() { + // + var key = "r:TestGetAsync_1"; + var value = 1; + + //ȡ,Ϊ + var result = await _cache.GetAsync( key ); + Assert.Null( result ); + + //û + await _cache.SetAsync( key, value ); + + //֤ + result = await _cache.GetAsync( key ); + Assert.Equal( value, result ); + } + + /// + /// Իȡ - û + /// + [Fact] + public async Task TestGetAsync_2() { + // + var key = new CacheKey( "r:TestGetAsync_2" ); + var value = 1; + + //ȡ,Ϊ + var result = await _cache.GetAsync( key ); + Assert.Null( result ); + + //û + await _cache.SetAsync( key, value ); + + //֤ + result = await _cache.GetAsync( key ); + Assert.Equal( value, result ); + } + + /// + /// Իȡ - ȡ + /// + [Fact] + public async Task TestGetAsync_3() { + // + var key = "r:TestGetAsync_31"; + var key2 = "r:TestGetAsync_32"; + + //û + await _cache.SetAsync( key, 1 ); + await _cache.SetAsync( key2, 2 ); + + //֤ + var result = await _cache.GetAsync( new[] { key, key2 } ); + Assert.Equal( 1, result[0] ); + Assert.Equal( 2, result[1] ); + } + + /// + /// Իȡ - ȡ,û + /// + [Fact] + public async Task TestGetAsync_4() { + // + var key = new CacheKey( "r:TestGetAsync_41" ); + var key2 = new CacheKey( "r:TestGetAsync_42" ); + + //û + await _cache.SetAsync( key, 1 ); + await _cache.SetAsync( key2, 2 ); + + //֤ + var result = await _cache.GetAsync( new[] { key, key2 } ); + Assert.Equal( 1, result[0] ); + Assert.Equal( 2, result[1] ); + } + + /// + /// Դӻлȡ - Ĭ8Сʱ + /// + [Fact] + public async Task TestGetAsync_5() { + var result = 0; + var data = 0; + for ( int i = 0; i < 3; i++ ) { + result = await _cache.GetAsync( "r:TestGetAsync_5", async () => { + data++; + return await Task.FromResult( data ); + } ); + } + Assert.Equal( 1, result ); + } + + #endregion + + #region GetByPrefix + + /// + /// ͨǰ׺ȡ + /// + [Fact] + public void TestGetByPrefix_1() { + // + var key = "jd:TestGetByPrefix_11"; + var key2 = "jd:TestGetByPrefix_12"; + + //û + _cache.Set( key, 1 ); + _cache.Set( key2, 2 ); + + //֤ + var result = _cache.GetByPrefix( "jd" ).OrderBy( t => t ).ToList(); + Assert.Equal( 1, result[0] ); + Assert.Equal( 2, result[1] ); + } + + #endregion + + #region GetByPrefixAsync + + /// + /// ͨǰ׺ȡ + /// + [Fact] + public async Task TestGetByPrefixAsync_1() { + // + var key = "ef:TestGetByPrefixAsync_11"; + var key2 = "ef:TestGetByPrefixAsync_12"; + + //û + await _cache.SetAsync( key, 1 ); + await _cache.SetAsync( key2, 2 ); + + //֤ + var result = (await _cache.GetByPrefixAsync( "ef" )).OrderBy( t => t ).ToList(); + Assert.Equal( 1, result[0] ); + Assert.Equal( 2, result[1] ); + } + + #endregion + + #region TrySet + + /// + /// û + /// + [Fact] + public void TestTrySet_1() { + // + var key = "TestTrySet_1"; + var value = 1; + + //ȡ,Ϊ + var result = _cache.Get( key ); + Assert.Null( result ); + + //û1 + _cache.TrySet( key, value ); + result = _cache.Get( key ); + Assert.Equal( value, result ); + + //û2,Ч + _cache.TrySet( key, 2 ); + result = _cache.Get( key ); + Assert.Equal( value, result ); + } + + /// + /// û - û + /// + [Fact] + public void TestTrySet_2() { + // + var key = new CacheKey( "TestTrySet_2" ); + var value = 1; + + //ȡ,Ϊ + var result = _cache.Get( key ); + Assert.Null( result ); + + //û1 + _cache.TrySet( key, value ); + result = _cache.Get( key ); + Assert.Equal( value, result ); + + //û2,Ч + _cache.TrySet( key, 2 ); + result = _cache.Get( key ); + Assert.Equal( value, result ); + } + + #endregion + + #region TrySetAsync + + /// + /// û + /// + [Fact] + public async Task TestTrySetAsync_1() { + // + var key = "TestTrySetAsync_1"; + var value = 1; + + //ȡ,Ϊ + var result = await _cache.GetAsync( key ); + Assert.Null( result ); + + //û1 + await _cache.TrySetAsync( key, value ); + result = await _cache.GetAsync( key ); + Assert.Equal( value, result ); + + //û2,Ч + await _cache.TrySetAsync( key, 2 ); + result = await _cache.GetAsync( key ); + Assert.Equal( value, result ); + } + + /// + /// û - û + /// + [Fact] + public async Task TestTrySetAsync_2() { + // + var key = new CacheKey( "TestTrySetAsync_2" ); + var value = 1; + + //ȡ,Ϊ + var result = await _cache.GetAsync( key ); + Assert.Null( result ); + + //û1 + await _cache.TrySetAsync( key, value ); + result = await _cache.GetAsync( key ); + Assert.Equal( value, result ); + + //û2,Ч + await _cache.TrySetAsync( key, 2 ); + result = await _cache.GetAsync( key ); + Assert.Equal( value, result ); + } + + #endregion + + #region Set + + /// + /// û + /// + [Fact] + public void TestSet_1() { + // + var key = "TestSet_1"; + + //ȡ,Ϊ + var result = _cache.Get( key ); + Assert.Null( result ); + + //û1 + _cache.Set( key, 1 ); + result = _cache.Get( key ); + Assert.Equal( 1, result ); + + //û2, + _cache.Set( key, 2 ); + result = _cache.Get( key ); + Assert.Equal( 2, result ); + } + + /// + /// û- û + /// + [Fact] + public void TestSet_2() { + // + var key = new CacheKey( "TestSet_2" ); + + //ȡ,Ϊ + var result = _cache.Get( key ); + Assert.Null( result ); + + //û1 + _cache.Set( key, 1 ); + result = _cache.Get( key ); + Assert.Equal( 1, result ); + + //û2, + _cache.Set( key, 2 ); + result = _cache.Get( key ); + Assert.Equal( 2, result ); + } + + /// + /// û - ü + /// + [Fact] + public void TestSet_3() { + // + var key = "TestSet_31"; + var key2 = "TestSet_32"; + + //û + _cache.Set( new Dictionary{{key,1}, { key2, 2 } } ); + + //֤ + Assert.Equal( 1, _cache.Get( key ) ); + Assert.Equal( 2, _cache.Get( key2 ) ); + } + + /// + /// û - ü,û + /// + [Fact] + public void TestSet_4() { + // + var key = new CacheKey( "TestSet_41" ); + var key2 = new CacheKey( "TestSet_42" ); + + //û + _cache.Set( new Dictionary { { key, 1 }, { key2, 2 } } ); + + //֤ + Assert.Equal( 1, _cache.Get( key ) ); + Assert.Equal( 2, _cache.Get( key2 ) ); + } + + #endregion + + #region SetAsync + + /// + /// û + /// + [Fact] + public async Task TestSetAsync_1() { + // + var key = "TestSetAsync_1"; + + //ȡ,Ϊ + var result = await _cache.GetAsync( key ); + Assert.Null( result ); + + //û1 + await _cache.SetAsync( key, 1 ); + result = await _cache.GetAsync( key ); + Assert.Equal( 1, result ); + + //û2, + await _cache.SetAsync( key, 2 ); + result = await _cache.GetAsync( key ); + Assert.Equal( 2, result ); + } + + /// + /// û - û + /// + [Fact] + public async Task TestSetAsync_2() { + // + var key = new CacheKey( "TestSetAsync_2" ); + + //ȡ,Ϊ + var result = await _cache.GetAsync( key ); + Assert.Null( result ); + + //û1 + await _cache.SetAsync( key, 1 ); + result = await _cache.GetAsync( key ); + Assert.Equal( 1, result ); + + //û2, + await _cache.SetAsync( key, 2 ); + result = await _cache.GetAsync( key ); + Assert.Equal( 2, result ); + } + + /// + /// û - ü + /// + [Fact] + public async Task TestSetAsync_3() { + // + var key = "TestSetAsync_31"; + var key2 = "TestSetAsync_32"; + + //û + await _cache.SetAsync( new Dictionary { { key, 1 }, { key2, 2 } } ); + + //֤ + Assert.Equal( 1, await _cache.GetAsync( key ) ); + Assert.Equal( 2, await _cache.GetAsync( key2 ) ); + } + + /// + /// û - ü,û + /// + [Fact] + public async Task TestSetAsync_4() { + // + var key = new CacheKey( "TestSetAsync_41" ); + var key2 = new CacheKey( "TestSetAsync_42" ); + + //û + await _cache.SetAsync( new Dictionary { { key, 1 }, { key2, 2 } } ); + + //֤ + Assert.Equal( 1, await _cache.GetAsync( key ) ); + Assert.Equal( 2, await _cache.GetAsync( key2 ) ); + } + + #endregion + + #region Exists + + /// + /// ԻǷѴ + /// + [Fact] + public void TestExists_1() { + // + var key = "TestExists_1"; + + //治 + Assert.False( _cache.Exists( key ) ); + + //û + _cache.Set( key, 1 ); + + //֤ + Assert.True( _cache.Exists( key ) ); + } + + /// + /// ԻǷѴ- û + /// + [Fact] + public void TestExists_2() { + // + var key = new CacheKey( "TestExists_2" ); + + //治 + Assert.False( _cache.Exists( key ) ); + + //û + _cache.Set( key, 1 ); + + //֤ + Assert.True( _cache.Exists( key ) ); + } + + #endregion + + #region ExistsAsync + + /// + /// ԻǷѴ + /// + [Fact] + public async Task TestExistsAsync_1() { + // + var key = "TestExistsAsync_1"; + + //治 + Assert.False( await _cache.ExistsAsync( key ) ); + + //û + await _cache.SetAsync( key, 1 ); + + //֤ + Assert.True( await _cache.ExistsAsync( key ) ); + } + + /// + /// ԻǷѴ - û + /// + [Fact] + public async Task TestExistsAsync_2() { + // + var key = new CacheKey( "TestExistsAsync_2" ); + + //治 + Assert.False( await _cache.ExistsAsync( key ) ); + + //û + await _cache.SetAsync( key, 1 ); + + //֤ + Assert.True( await _cache.ExistsAsync( key ) ); + } + + #endregion + + #region Remove + + /// + /// Ƴ + /// + [Fact] + public void TestRemove_1() { + // + var key = "TestRemove_1"; + + //û + _cache.Set( key, 1 ); + Assert.True( _cache.Exists( key ) ); + + //Ƴ + _cache.Remove( key ); + + //֤ + Assert.False( _cache.Exists( key ) ); + } + + /// + /// Ƴ - û + /// + [Fact] + public void TestRemove_2() { + // + var key = new CacheKey( "TestRemove_2" ); + + //û + _cache.Set( key, 1 ); + Assert.True( _cache.Exists( key ) ); + + //Ƴ + _cache.Remove( key ); + + //֤ + Assert.False( _cache.Exists( key ) ); + } + + /// + /// Ƴ漯 + /// + [Fact] + public void TestRemove_3() { + // + var key = "TestRemove_31"; + var key2 = "TestRemove_32"; + + //û + _cache.Set( key, 1 ); + _cache.Set( key2, 2 ); + + //Ƴ + _cache.Remove( new[] { key, key2 } ); + + //֤ + Assert.False( _cache.Exists( key ) ); + Assert.False( _cache.Exists( key2 ) ); + } + + /// + /// Ƴ漯 - û + /// + [Fact] + public void TestRemove_4() { + // + var key = new CacheKey( "TestRemove_41" ); + var key2 = new CacheKey( "TestRemove_42" ); + + //û + _cache.Set( key, 1 ); + _cache.Set( key2, 2 ); + + //Ƴ + _cache.Remove( new[] { key, key2 } ); + + //֤ + Assert.False( _cache.Exists( key ) ); + Assert.False( _cache.Exists( key2 ) ); + } + + #endregion + + #region RemoveAsync + + /// + /// Ƴ + /// + [Fact] + public async Task TestRemoveAsync_1() { + // + var key = "TestRemoveAsync_1"; + + //û + await _cache.SetAsync( key, 1 ); + Assert.True( await _cache.ExistsAsync( key ) ); + + //Ƴ + await _cache.RemoveAsync( key ); + + //֤ + Assert.False( await _cache.ExistsAsync( key ) ); + } + + /// + /// Ƴ - û + /// + [Fact] + public async Task TestRemoveAsync_2() { + // + var key = new CacheKey( "TestRemoveAsync_2" ); + + //û + await _cache.SetAsync( key, 1 ); + Assert.True( await _cache.ExistsAsync( key ) ); + + //Ƴ + await _cache.RemoveAsync( key ); + + //֤ + Assert.False( await _cache.ExistsAsync( key ) ); + } + + /// + /// Ƴ漯 + /// + [Fact] + public async Task TestRemoveAsync_3() { + // + var key = "TestRemoveAsync_31"; + var key2 = "TestRemoveAsync_32"; + + //û + await _cache.SetAsync( key, 1 ); + await _cache.SetAsync( key2, 2 ); + + //Ƴ + await _cache.RemoveAsync( new[] { key, key2 } ); + + //֤ + Assert.False( await _cache.ExistsAsync( key ) ); + Assert.False( await _cache.ExistsAsync( key2 ) ); + } + + /// + /// Ƴ漯 - û + /// + [Fact] + public async Task TestRemoveAsync_4() { + // + var key = new CacheKey( "TestRemoveAsync_41" ); + var key2 = new CacheKey( "TestRemoveAsync_42" ); + + //û + await _cache.SetAsync( key, 1 ); + await _cache.SetAsync( key2, 2 ); + + //Ƴ + await _cache.RemoveAsync( new[] { key, key2 } ); + + //֤ + Assert.False( await _cache.ExistsAsync( key ) ); + Assert.False( await _cache.ExistsAsync( key2 ) ); + } + + #endregion + + #region RemoveByPrefix + + /// + /// ͨǰ׺Ƴ + /// + [Fact] + public void RemoveByPrefix_1() { + // + var key = "RemoveByPrefix_11"; + var key2 = "RemoveByPrefix_12"; + + //û + _cache.Set( key, 1 ); + _cache.Set( key2, 2 ); + + //Ƴ + _cache.RemoveByPrefix( "RemoveByPrefix" ); + + //֤ + Assert.False( _cache.Exists( key ) ); + Assert.False( _cache.Exists( key2 ) ); + } + + #endregion + + #region RemoveByPrefixAsync + + /// + /// ͨǰ׺Ƴ + /// + [Fact] + public async Task RemoveByPrefixAsync_1() { + // + var key = "RemoveByPrefixAsync_11"; + var key2 = "RemoveByPrefixAsync_12"; + + //û + await _cache.SetAsync( key, 1 ); + await _cache.SetAsync( key2, 2 ); + + //Ƴ + await _cache.RemoveByPrefixAsync( "RemoveByPrefix" ); + + //֤ + Assert.False( await _cache.ExistsAsync( key ) ); + Assert.False( await _cache.ExistsAsync( key2 ) ); + } + + #endregion + + #region RemoveByPattern + + /// + /// ͨģʽƳ + /// + [Fact] + public void RemoveByPattern() { + // + var key = "RemoveByPattern_1"; + var key2 = "RemoveByPattern_2"; + + //û + _cache.Set( key, 1 ); + _cache.Set( key2, 2 ); + + //Ƴ + _cache.RemoveByPattern( "*2" ); + + //֤ + Assert.True( _cache.Exists( key ) ); + Assert.False( _cache.Exists( key2 ) ); + } + + #endregion + + #region RemoveByPatternAsync + + /// + /// ͨģʽƳ + /// + [Fact] + public async Task RemoveByPatternAsync() { + // + var key = "RemoveByPatternAsync_1"; + var key2 = "RemoveByPatternAsync_2"; + + //û + await _cache.SetAsync( key, 1 ); + await _cache.SetAsync( key2, 2 ); + + //Ƴ + await _cache.RemoveByPatternAsync( "*2" ); + + //֤ + Assert.True( await _cache.ExistsAsync( key ) ); + Assert.False( await _cache.ExistsAsync( key2 ) ); + } + + #endregion + + #region Clear + + /// + /// ջ + /// + [Fact] + public void TestClear() { + // + var key = "TestClear_1"; + var key2 = "TestClear_2"; + + //û + _cache.Set( key, 1 ); + _cache.Set( key2, 2 ); + + //ջ + _cache.Clear(); + + //֤ + Assert.False( _cache.Exists( key ) ); + Assert.False( _cache.Exists( key2 ) ); + } + + #endregion + + #region ClearAsync + + /// + /// ջ + /// + [Fact] + public async Task TestClearAsync() { + // + var key = "TestClear_1"; + var key2 = "TestClear_2"; + + //û + await _cache.SetAsync( key, 1 ); + await _cache.SetAsync( key2, 2 ); + + //ջ + await _cache.ClearAsync(); + + //֤ + Assert.False( await _cache.ExistsAsync( key ) ); + Assert.False( await _cache.ExistsAsync( key2 ) ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Caching.EasyCaching.Tests.Integration/Util.Caching.EasyCaching.Tests.Integration.csproj b/test/Util.Caching.EasyCaching.Tests.Integration/Util.Caching.EasyCaching.Tests.Integration.csproj new file mode 100644 index 000000000..d1b5d6be6 --- /dev/null +++ b/test/Util.Caching.EasyCaching.Tests.Integration/Util.Caching.EasyCaching.Tests.Integration.csproj @@ -0,0 +1,45 @@ + + + + $(NetTargetFramework) + false + Util.Caching.EasyCaching + Util.Caching.EasyCaching.Startup + + + + + + + + + + Always + + + Always + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/test/Util.Caching.EasyCaching.Tests.Integration/appsettings.Development.json b/test/Util.Caching.EasyCaching.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..5737eae92 --- /dev/null +++ b/test/Util.Caching.EasyCaching.Tests.Integration/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Information" + } + }, + "ConnectionStrings": { + "Redis": "127.0.0.1" + } +} \ No newline at end of file diff --git a/test/Util.Caching.EasyCaching.Tests.Integration/appsettings.json b/test/Util.Caching.EasyCaching.Tests.Integration/appsettings.json new file mode 100644 index 000000000..686f86bab --- /dev/null +++ b/test/Util.Caching.EasyCaching.Tests.Integration/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests.Integration/Helpers/CommandLineTest.cs b/test/Util.Core.Tests.Integration/Helpers/CommandLineTest.cs new file mode 100644 index 000000000..50fc28fdf --- /dev/null +++ b/test/Util.Core.Tests.Integration/Helpers/CommandLineTest.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using Util.Helpers; +using Xunit; + +namespace Util.Tests.Helpers { + /// + /// 命令行操作测试 + /// + public class CommandLineTest { + /// + /// 测试设置命令参数 - 1个参数 + /// + [Fact] + public void TestArguments_1() { + var command = new CommandLine().Command( "dapr" ).Arguments( "run" ); + Assert.Equal( "dapr run", command.GetDebugText()); + } + + /// + /// 测试设置命令参数 - 多个参数 + /// + [Fact] + public void TestArguments_2() { + var command = new CommandLine().Command( "dapr" ).Arguments( "run", "--app-id","80" ); + Assert.Equal( "dapr run --app-id 80", command.GetDebugText() ); + } + + /// + /// 测试设置命令参数 - 数组参数 + /// + [Fact] + public void TestArguments_3() { + var list = new List { + "run", + "--app-id", + "80" + }; + var command = new CommandLine().Command( "dapr" ).Arguments( list ); + Assert.Equal( "dapr run --app-id 80", command.GetDebugText() ); + } + + /// + /// 测试设置命令参数 - 多次调用Arguments + /// + [Fact] + public void TestArguments_4() { + var list = new List { + "--app-id", + "80" + }; + var command = new CommandLine().Command( "dapr" ).Arguments( "run" ).Arguments( list ); + Assert.Equal( "dapr run --app-id 80", command.GetDebugText() ); + } + + /// + /// 测试根据条件设置命令参数 + /// + [Fact] + public void TestArgumentsIf() { + var command = new CommandLine().Command( "dapr" ).ArgumentsIf( false,"run" ); + Assert.Equal( "dapr ", command.GetDebugText() ); + } + } +} diff --git a/test/Util.Core.Tests.Integration/Helpers/ConfigTest.cs b/test/Util.Core.Tests.Integration/Helpers/ConfigTest.cs new file mode 100644 index 000000000..476f05f62 --- /dev/null +++ b/test/Util.Core.Tests.Integration/Helpers/ConfigTest.cs @@ -0,0 +1,19 @@ +using Util.Tests.Samples; +using Xunit; + +namespace Util.Tests.Helpers; + +/// +/// 配置操作测试 +/// +public class ConfigTest { + /// + /// 测试从根目录的appsettings.json文件读取配置 + /// + [Fact] + public void TestGet() { + TestOptions options = Util.Helpers.Config.Get( "Test" ); + Assert.Equal( 1,options.Code ); + Assert.Equal( "a", options.Name ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests.Integration/Helpers/FileTest.cs b/test/Util.Core.Tests.Integration/Helpers/FileTest.cs new file mode 100644 index 000000000..7733584eb --- /dev/null +++ b/test/Util.Core.Tests.Integration/Helpers/FileTest.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using Util.Helpers; +using Xunit; + +namespace Util.Tests.Helpers; + +/// +/// 文件流操作 +/// +public class FileTest { + /// + /// 测试读取文件到字符串 + /// + [Fact] + public void TestReadToString() { + var filePath = Util.Helpers.Common.GetPhysicalPath( "/Samples/FileSample.txt" ); + Assert.Equal( "test", File.ReadToString( filePath ) ); + } + + /// + /// 测试读取文件到字符串 + /// + [Fact] + public async Task TestReadToStringAsync() { + var filePath = Util.Helpers.Common.GetPhysicalPath( "/Samples/FileSample.txt" ); + Assert.Equal( "test",await File.ReadToStringAsync( filePath ) ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests.Integration/Helpers/PlatformTest.cs b/test/Util.Core.Tests.Integration/Helpers/PlatformTest.cs new file mode 100644 index 000000000..ac95fb48f --- /dev/null +++ b/test/Util.Core.Tests.Integration/Helpers/PlatformTest.cs @@ -0,0 +1,48 @@ +using Xunit; + +namespace Util.Tests.Helpers; + +/// +/// 平台操作测试 +/// +public class PlatformTest { + /// + /// 测试获取物理路径 + /// + [Fact] + public void TestGetPhysicalPath_1() { + var path = Util.Helpers.Common.GetPhysicalPath( "a/b.txt" ); + var result = $"{System.AppContext.BaseDirectory}a/b.txt"; + Assert.Equal( result,path ); + } + + /// + /// 测试获取物理路径 - 以/开头 + /// + [Fact] + public void TestGetPhysicalPath_2() { + var path = Util.Helpers.Common.GetPhysicalPath( "/a/b.txt" ); + var result = $"{System.AppContext.BaseDirectory}a/b.txt"; + Assert.Equal( result, path ); + } + + /// + /// 测试获取物理路径 - 以\开头 + /// + [Fact] + public void TestGetPhysicalPath_3() { + var path = Util.Helpers.Common.GetPhysicalPath( "\\a\\b.txt" ); + var result = $"{System.AppContext.BaseDirectory}a\\b.txt"; + Assert.Equal( result, path ); + } + + /// + /// 测试获取物理路径 + /// + [Fact] + public void TestGetPhysicalPath_4() { + var path = Util.Helpers.Common.GetPhysicalPath( "~/a/b.txt" ); + var result = $"{System.AppContext.BaseDirectory}a/b.txt"; + Assert.Equal( result, path ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests.Integration/Helpers/ReflectionTest.cs b/test/Util.Core.Tests.Integration/Helpers/ReflectionTest.cs new file mode 100644 index 000000000..2878062a3 --- /dev/null +++ b/test/Util.Core.Tests.Integration/Helpers/ReflectionTest.cs @@ -0,0 +1,85 @@ +using Util.Helpers; +using Util.Tests.Samples; +using Xunit; + +namespace Util.Tests.Helpers; + +/// +/// 测试反射操作 +/// +public class ReflectionTest { + /// + /// 测试查找实现类型列表 - 测试类型中只有一个类实现IA接口 + /// + [Fact] + public void TestFindImplementTypes_1() { + var types = Reflection.FindImplementTypes( this.GetType().Assembly ); + Assert.Single( types ); + Assert.Equal( typeof( A ), types[0] ); + } + + /// + /// 测试查找实现类型列表 - 抽象类和接口被排除 + /// + [Fact] + public void TestFindImplementTypes_2() { + var types = Reflection.FindImplementTypes( this.GetType().Assembly ); + Assert.Equal( 2, types.Count ); + Assert.Equal( typeof( A ), types[0] ); + Assert.Equal( typeof( B ), types[1] ); + } + + /// + /// 测试查找实现类型列表 - 泛型实现 + /// + [Fact] + public void TestFindImplementTypes_3() { + var types = Reflection.FindImplementTypes( this.GetType().Assembly ); + Assert.Equal( 2, types.Count ); + Assert.Equal( typeof( B ), types[0] ); + Assert.Equal( typeof( D<> ), types[1] ); + } + + /// + /// 测试查找实现类型列表 - 泛型参数为E + /// + [Fact] + public void TestFindImplementTypes_4() { + var types = Reflection.FindImplementTypes>( this.GetType().Assembly ); + Assert.Single( types ); + Assert.Equal( typeof( E ), types[0] ); + } + + /// + /// 测试查找实现类型列表 - 泛型参数为空 + /// + [Fact] + public void TestFindImplementTypes_5() { + var types = Reflection.FindImplementTypes( typeof( IG<> ), this.GetType().Assembly ); + Assert.Equal( 2, types.Count ); + Assert.Equal( typeof( E ), types[0] ); + Assert.Equal( typeof( F<> ), types[1] ); + } + + /// + /// 测试查找实现类型列表 - 测试并发 + /// + [Fact] + public void TestFindImplementTypes_6() { + Util.Helpers.Thread.ParallelInvoke( () => { + var types = Reflection.FindImplementTypes( this.GetType().Assembly ); + Assert.Single( types ); + Assert.Equal( typeof( A ), types[0] ); + }, () => { + var types = Reflection.FindImplementTypes( this.GetType().Assembly ); + Assert.Equal( 2, types.Count ); + Assert.Equal( typeof( A ), types[0] ); + Assert.Equal( typeof( B ), types[1] ); + }, () => { + var types = Reflection.FindImplementTypes( this.GetType().Assembly ); + Assert.Equal( 2, types.Count ); + Assert.Equal( typeof( B ), types[0] ); + Assert.Equal( typeof( D<> ), types[1] ); + } ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests.Integration/Helpers/XmlTest.cs b/test/Util.Core.Tests.Integration/Helpers/XmlTest.cs new file mode 100644 index 000000000..dc237e2d9 --- /dev/null +++ b/test/Util.Core.Tests.Integration/Helpers/XmlTest.cs @@ -0,0 +1,207 @@ +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Util.Helpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Tests.Helpers; + +/// +/// Xml操作测试 +/// +public class XmlTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// Xml操作 + /// + private readonly Xml _xml; + + /// + /// 测试初始化 + /// + public XmlTest( ITestOutputHelper output ) { + _output = output; + _xml = new Xml(); + } + + /// + /// 测试添加节点 + /// + [Fact] + public void TestAddNode_1() { + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "1" ); + result.Append( "" ); + + //操作 + _xml.AddNode( "a", "1" ); + + //验证 + Assert.Equal( result.ToString(), _xml.ToString() ); + + //输出结果 + _output.WriteLine( _xml.ToString() ); + } + + /// + /// 测试添加节点 - 父子节点 + /// + [Fact] + public void TestAddNode_2() { + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "2" ); + result.Append( "" ); + result.Append( "" ); + + //操作 + var parent = _xml.AddNode( "a" ); + _xml.AddNode( "b", "2", parent ); + + //验证 + Assert.Equal( result.ToString(), _xml.ToString() ); + + //输出结果 + _output.WriteLine( _xml.ToString() ); + } + + /// + /// 测试添加注释类型节点 + /// + [Fact] + public void TestAddCDataNode_1() { + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //操作 + _xml.AddCDataNode( "1" ); + + //验证 + Assert.Equal( result.ToString(), _xml.ToString() ); + + //输出结果 + _output.WriteLine( _xml.ToString() ); + } + + /// + /// 测试添加注释类型节点 - 父节点 + /// + [Fact] + public void TestAddCDataNode_2() { + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //操作 + var parent = _xml.AddNode( "a" ); + _xml.AddCDataNode( "1", parent ); + + //验证 + Assert.Equal( result.ToString(), _xml.ToString() ); + + //输出结果 + _output.WriteLine( _xml.ToString() ); + } + + /// + /// 测试添加注释类型节点 - 父节点 - 通过设置字符串父节点名称 + /// + [Fact] + public void TestAddCDataNode_3() { + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //操作 + _xml.AddCDataNode( "1", "a" ); + + //验证 + Assert.Equal( result.ToString(), _xml.ToString() ); + + //输出结果 + _output.WriteLine( _xml.ToString() ); + } + + /// + /// xml字符串转换为XElement列表 + /// + [Fact] + public void TestToElements_1() { + //输入 + var input = new StringBuilder(); + input.Append( "" ); + input.Append( "1" ); + input.Append( "2" ); + input.Append( "" ); + + //操作 + var elements = Xml.ToElements( input.ToString() ); + var element = elements.FirstOrDefault( t => t.Name == "b"); + + //验证 + Assert.Equal( "2",element?.Value ); + } + + /// + /// xml字符串转换为XElement列表 + /// + [Fact] + public void TestToElements_2() { + //输入 + var input = new StringBuilder(); + input.Append( "" ); + input.Append( "1" ); + input.Append( "2" ); + input.Append( "" ); + input.Append( "" ); + input.Append( "" ); + input.Append( "" ); + + //操作 + var elements = Xml.ToElements( input.ToString() ); + var element = elements.FirstOrDefault( t => t?.Name == "c" ); + + //验证 + Assert.Equal( "3", element?.Value ); + } + + /// + /// 加载Xml文件到XDocument + /// + [Fact] + public async Task TestLoadFileToDocumentAsync() { + var file = Common.GetPhysicalPath( "./Samples/version.xml" ); + var document = await Util.Helpers.Xml.LoadFileToDocumentAsync( file ); + Assert.Equal( "123", document?.Root?.Value ); + } + + /// + /// 加载Xml文件到XElement列表 + /// + [Fact] + public async Task TestLoadFileToElementsAsync() { + var file = Common.GetPhysicalPath( "./Samples/version.xml" ); + var elements = await Util.Helpers.Xml.LoadFileToElementsAsync( file ); + var element = elements.FirstOrDefault( t => t?.Name == "VersionMinor" ); + Assert.Equal( "2", element?.Value ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests.Integration/Infrastructure/BootstrapperTest.cs b/test/Util.Core.Tests.Integration/Infrastructure/BootstrapperTest.cs new file mode 100644 index 000000000..c984e5fb8 --- /dev/null +++ b/test/Util.Core.Tests.Integration/Infrastructure/BootstrapperTest.cs @@ -0,0 +1,28 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Infrastructure; +using Util.Tests.Samples; +using Xunit; + +namespace Util.Tests.Infrastructure; + +/// +/// 启动器测试 +/// +public class BootstrapperTest { + /// + /// 测试启动时解析服务注册器 + /// 说明: + /// 1. 有两个测试服务注册器TestServiceRegistrar和TestServiceRegistrar2 + /// 2. TestServiceRegistrar的Id更小,所以先执行 + /// 3. TestServiceRegistrar2使用services.TryAddSingleton进行依赖注册,由于之前已经添加过依赖,所以不会再次添加,TestServiceRegistrar的依赖配置生效 + /// + [Fact] + public void TestStart_1() { + var builder = new HostBuilder(); + var bootstrapper = new Bootstrapper( builder ); + bootstrapper.Start(); + var host = builder.Build(); + Assert.Equal( "a", host.Services.GetService()?.Value ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests.Integration/Infrastructure/DependencyServiceRegistrarTest.cs b/test/Util.Core.Tests.Integration/Infrastructure/DependencyServiceRegistrarTest.cs new file mode 100644 index 000000000..dcb6a4dd2 --- /dev/null +++ b/test/Util.Core.Tests.Integration/Infrastructure/DependencyServiceRegistrarTest.cs @@ -0,0 +1,67 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Infrastructure; +using Util.Tests.Samples; +using Xunit; + +namespace Util.Tests.Infrastructure; + +/// +/// 依赖服务注册器测试 +/// +public class DependencyServiceRegistrarTest { + /// + /// 测试注册服务 + /// + [Fact] + public void TestRegisterDependency_1() { + var builder = new HostBuilder(); + var bootstrapper = new Bootstrapper( builder ); + bootstrapper.Start(); + var host = builder.Build(); + Assert.Equal( typeof( TestService4 ), host.Services.GetService()?.GetType() ); + Assert.NotEqual( typeof( TestService4 ), host.Services.GetService()?.GetType() ); + Assert.Equal( typeof( TestService4 ), host.Services.GetService()?.GetType() ); + Assert.Equal( typeof( TestService5 ), host.Services.GetService>()?.GetType() ); + } + + /// + /// 测试Ioc特性覆盖实现 + /// + [Fact] + public void TestRegisterDependency_2() { + var builder = new HostBuilder(); + var bootstrapper = new Bootstrapper( builder ); + bootstrapper.Start(); + var host = builder.Build(); + Assert.Equal( typeof( TestService8 ), host.Services.GetService()?.GetType() ); + Assert.Equal( typeof( TestService70 ), host.Services.GetService()?.GetType() ); + } + + /// + /// 测试间接继承ISingletonDependency接口进行依赖配置 + /// + [Fact] + public void TestRegisterDependency_4() { + var builder = new HostBuilder(); + var bootstrapper = new Bootstrapper( builder ); + bootstrapper.Start(); + var host = builder.Build(); + Assert.Equal( typeof( TestService12 ), host.Services.GetService()?.GetType() ); + Assert.Equal( typeof( TestService12 ), host.Services.GetService()?.GetType() ); + } + + /// + /// 测试禁用依赖服务注册器 + /// + [Fact] + public void TestDisableDependencyServiceRegistrar() { + ServiceRegistrarConfig.Instance.DisableDependencyServiceRegistrar(); + var builder = new HostBuilder(); + var bootstrapper = new Bootstrapper( builder ); + bootstrapper.Start(); + var host = builder.Build(); + Assert.NotEqual( typeof( TestService4 ), host.Services.GetService()?.GetType() ); + ServiceRegistrarConfig.Instance.EnableDependencyServiceRegistrar(); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests.Integration/Reflections/AppDomainAssemblyFinderTest.cs b/test/Util.Core.Tests.Integration/Reflections/AppDomainAssemblyFinderTest.cs new file mode 100644 index 000000000..24303c992 --- /dev/null +++ b/test/Util.Core.Tests.Integration/Reflections/AppDomainAssemblyFinderTest.cs @@ -0,0 +1,53 @@ +using System.Linq; +using Util.Reflections; +using Xunit; + +namespace Util.Tests.Reflections; + +/// +/// 应用程序域程序集查找器测试 +/// +public class AppDomainAssemblyFinderTest { + /// + /// 程序集查找器 + /// + private readonly AppDomainAssemblyFinder _finder; + + /// + /// 测试初始化 + /// + public AppDomainAssemblyFinderTest() { + _finder = new AppDomainAssemblyFinder(); + } + + /// + /// 测试查找程序集列表 - 当前测试项目程序集被自动加载到应用程序域 + /// + [Fact] + public void TestFind_1() { + var assemblies = _finder.Find(); + var names = assemblies.Select( t => t.FullName ).ToList(); + Assert.True( names.Exists( t => t.StartsWith( "Util.Core.Tests.Integration" ) ) ); + } + + /// + /// 测试查找程序集列表 - Util.Events程序集被引用但未被调用,未初始加载到应用程序域,需要在获取之前进行加载 + /// + [Fact] + public void TestFind_2() { + var assemblies = _finder.Find(); + var names = assemblies.Select( t => t.FullName ).ToList(); + Assert.True( names.Exists( t => t.StartsWith( "Util.Events" ) ) ); + } + + /// + /// 测试查找程序集列表 - 过滤Microsoft程序集 + /// + [Fact] + public void TestFind_3() { + _finder.AssemblySkipPattern = "^Microsoft"; + var assemblies = _finder.Find(); + var names = assemblies.Select( t => t.FullName ).ToList(); + Assert.False( names.Exists( t => t.StartsWith( "Microsoft" ) ) ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests.Integration/Reflections/AppDomainTypeFinderTest.cs b/test/Util.Core.Tests.Integration/Reflections/AppDomainTypeFinderTest.cs new file mode 100644 index 000000000..177d544da --- /dev/null +++ b/test/Util.Core.Tests.Integration/Reflections/AppDomainTypeFinderTest.cs @@ -0,0 +1,33 @@ +using Util.Reflections; +using Util.Tests.Samples; +using Xunit; + +namespace Util.Tests.Reflections; + +/// +/// 应用程序域类型查找器测试 +/// +public class AppDomainTypeFinderTest { + /// + /// 应用程序域类型查找器 + /// + private readonly AppDomainTypeFinder _finder; + + /// + /// 测试初始化 + /// + public AppDomainTypeFinderTest() { + var assemblyFinder = new AppDomainAssemblyFinder(); + _finder = new AppDomainTypeFinder( assemblyFinder ); + } + + /// + /// 测试查找实现类型列表 + /// + [Fact] + public void TestFind_1() { + var types = _finder.Find(); + Assert.Single( types ); + Assert.Equal( typeof( A ), types[0] ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests.Integration/Samples/FileSample.txt b/test/Util.Core.Tests.Integration/Samples/FileSample.txt new file mode 100644 index 000000000..b0540d229 --- /dev/null +++ b/test/Util.Core.Tests.Integration/Samples/FileSample.txt @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/test/Util.Core.Tests.Integration/Samples/TestOptions.cs b/test/Util.Core.Tests.Integration/Samples/TestOptions.cs new file mode 100644 index 000000000..62360b42f --- /dev/null +++ b/test/Util.Core.Tests.Integration/Samples/TestOptions.cs @@ -0,0 +1,6 @@ +namespace Util.Tests.Samples; + +public class TestOptions { + public int Code { get; set; } + public string Name { get; set; } +} \ No newline at end of file diff --git a/test/Util.Core.Tests.Integration/Samples/TestSample.cs b/test/Util.Core.Tests.Integration/Samples/TestSample.cs new file mode 100644 index 000000000..cf72799ce --- /dev/null +++ b/test/Util.Core.Tests.Integration/Samples/TestSample.cs @@ -0,0 +1,227 @@ +using Util.Dependency; + +namespace Util.Tests.Samples; + +/// +/// 测试查找接口 +/// +public interface IA : IB { + string Value { get; set; } +} + +/// +/// 测试查找接口 +/// +public interface IB { +} + +/// +/// 测试查找接口 +/// +public interface IC { +} + +/// +/// 测试查找接口 +/// +public interface IG { +} + +/// +/// 测试类型 +/// +public class A : IA, IB { + public string Value { get; set; } +} + +/// +/// 测试类型 +/// +public class B : IB, IC { +} + +/// +/// 测试类型 +/// +public abstract class C : IB, IC { +} + +/// +/// 测试类型 +/// +public class D : IC { +} + +/// +/// 测试类型 +/// +public class E : IG { +} + +/// +/// 测试类型 +/// +public class F : IG { +} + +/// +/// 测试服务 +/// +public class TestService : ITestService { + public string Value { get; set; } +} + +/// +/// 测试服务接口 +/// +public interface ITestService { + string Value { get; set; } +} + +/// +/// 测试服务 +/// +public class TestService2 : ITestService2 { + public string Value { get; set; } +} + +/// +/// 测试服务接口 +/// +public interface ITestService2 { + string Value { get; set; } +} + +/// +/// 测试服务 +/// +public class TestService3 : ITestService3 { + public string Value { get; set; } +} + +/// +/// 测试服务接口 +/// +public interface ITestService3 { + string Value { get; set; } +} + +/// +/// 测试服务 +/// +public class TestService4 : ITestService2,ITestService5 { + public string Value { get; set; } +} + +/// +/// 测试服务接口 +/// +public interface ITestService4: ITestService3, ISingletonDependency { +} + +/// +/// 测试服务接口 +/// +public interface ITestService5 : ITestService4 { +} + +/// +/// 测试服务接口 +/// +public interface ITestService6 : ISingletonDependency { +} + +/// +/// 测试服务 +/// +public class TestService5 : ITestService6 { +} + +/// +/// 测试服务接口 +/// +public interface ITestService7 : ISingletonDependency { +} + +/// +/// 测试服务 +/// +[Ioc( -2 )] +public class TestService7 : ITestService7 { +} + +/// +/// 测试服务 +/// +[Ioc( -1 )] +public class TestService8 : ITestService7 { +} + +/// +/// 测试服务接口 +/// +public interface ITestService70 : ISingletonDependency { +} + +/// +/// 测试服务 +/// +[Ioc( 2 )] +public class TestService70 : ITestService70 { +} + +/// +/// 测试服务 +/// +[Ioc( 1 )] +public class TestService80 : ITestService70 { +} + +/// +/// 测试服务 +/// +public class TestService90 : ITestService70 { +} + +/// +/// 测试服务接口 +/// +public interface ITestService8 : ISingletonDependency { +} + +/// +/// 测试服务 +/// +public class TestService9 : ITestService8 { +} + +/// +/// 测试服务 +/// +public class TestService10 : ITestService8 { +} + +/// +/// 测试服务 +/// +[Ioc( 2 )] +public class TestService11 : ITestService8 { +} + +/// +/// 测试服务接口 +/// +public interface ITestService10 : ISingletonDependency { +} + +/// +/// 测试服务接口 +/// +public interface ITestService12 : ITestService10 { +} + +/// +/// 测试服务 +/// +public class TestService12 : ITestService12 { +} \ No newline at end of file diff --git a/test/Util.Core.Tests.Integration/Samples/TestServiceRegistrar.cs b/test/Util.Core.Tests.Integration/Samples/TestServiceRegistrar.cs new file mode 100644 index 000000000..c133809a7 --- /dev/null +++ b/test/Util.Core.Tests.Integration/Samples/TestServiceRegistrar.cs @@ -0,0 +1,130 @@ +using System; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Util.Infrastructure; + +namespace Util.Tests.Samples; + +/// +/// 测试服务注册器 +/// +public class TestServiceRegistrar : IServiceRegistrar { + /// + /// 获取服务名 + /// + public static string ServiceName => "TestServiceRegistrar"; + + /// + /// 排序号 + /// + public int OrderId => 100; + + /// + /// 是否启用 + /// + public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName ); + + /// + /// 注册服务 + /// + public Action Register( ServiceContext serviceContext ) { + A a = new A { Value = "a" }; + serviceContext.HostBuilder.ConfigureServices( ( context, services ) => { + services.TryAddSingleton( a ); + } ); + return null; + } +} + +/// +/// 测试服务注册器2 +/// +public class TestServiceRegistrar2 : IServiceRegistrar { + /// + /// 获取服务名 + /// + public static string ServiceName => "TestServiceRegistrar2"; + + /// + /// 排序号 + /// + public int OrderId => 200; + + /// + /// 是否启用 + /// + public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName ); + + /// + /// 注册服务 + /// + public Action Register( ServiceContext serviceContext ) { + A a = new A { Value = "b" }; + serviceContext.HostBuilder.ConfigureServices( ( context, services ) => { + services.TryAddSingleton( a ); + } ); + return null; + } +} + +/// +/// 测试服务注册器 +/// +public class TestServiceRegistrar3 : IServiceRegistrar { + /// + /// 获取服务名 + /// + public static string ServiceName => "TestServiceRegistrar3"; + + /// + /// 排序号 + /// + public int OrderId => 300; + + /// + /// 是否启用 + /// + public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName ); + + /// + /// 注册服务 + /// + public Action Register( ServiceContext serviceContext ) { + var service = new TestService2 { Value = "a" }; + serviceContext.HostBuilder.ConfigureServices( ( context, services ) => { + services.TryAddSingleton( service ); + } ); + return null; + } +} + +/// +/// 测试服务注册器 +/// +public class TestServiceRegistrar4 : IServiceRegistrar { + /// + /// 获取服务名 + /// + public static string ServiceName => "TestServiceRegistrar3"; + + /// + /// 排序号 + /// + public int OrderId => 400; + + /// + /// 是否启用 + /// + public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName ); + + /// + /// 注册服务 + /// + public Action Register( ServiceContext serviceContext ) { + return () => { + var service = new TestService3 { Value = "a" }; + serviceContext.HostBuilder.ConfigureServices( ( context, services ) => { + services.TryAddSingleton( service ); + } ); + }; + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests.Integration/Samples/version.xml b/test/Util.Core.Tests.Integration/Samples/version.xml new file mode 100644 index 000000000..c49679958 --- /dev/null +++ b/test/Util.Core.Tests.Integration/Samples/version.xml @@ -0,0 +1,5 @@ + + 1 + 2 + 3 + \ No newline at end of file diff --git a/test/Util.Core.Tests.Integration/Util.Core.Tests.Integration.csproj b/test/Util.Core.Tests.Integration/Util.Core.Tests.Integration.csproj new file mode 100644 index 000000000..44b4eaff3 --- /dev/null +++ b/test/Util.Core.Tests.Integration/Util.Core.Tests.Integration.csproj @@ -0,0 +1,49 @@ + + + + $(NetTargetFramework) + false + Util.Tests + + + + + + + + + PreserveNewest + true + PreserveNewest + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + Always + + + Always + + + + diff --git a/test/Util.Core.Tests.Integration/appsettings.json b/test/Util.Core.Tests.Integration/appsettings.json new file mode 100644 index 000000000..3d6dde5ee --- /dev/null +++ b/test/Util.Core.Tests.Integration/appsettings.json @@ -0,0 +1,6 @@ +{ + "Test": { + "Code": 1, + "Name": "a" + } +} diff --git a/test/Util.Core.Tests/Dependency/ContainerTest.cs b/test/Util.Core.Tests/Dependency/ContainerTest.cs new file mode 100644 index 000000000..2404f0ce8 --- /dev/null +++ b/test/Util.Core.Tests/Dependency/ContainerTest.cs @@ -0,0 +1,53 @@ +using Microsoft.Extensions.DependencyInjection; +using Util.Dependency; +using Util.Tests.Samples; +using Xunit; + +namespace Util.Tests.Dependency; + +/// +/// 容器测试 +/// +public class ContainerTest { + /// + /// 容器 + /// + private readonly Container _container; + + /// + /// 测试初始化 + /// + public ContainerTest() { + _container = new Container(); + } + + /// + /// 测试获取服务集合 + /// + [Fact] + public void TestGetServices() { + Assert.NotNull( _container.GetServices() ); + } + + /// + /// 测试获取服务 + /// + [Fact] + public void TestGetService() { + var services = _container.GetServices(); + services.AddSingleton(); + var result = _container.GetService(); + Assert.Equal( typeof( Sample ), result.GetType() ); + } + + /// + /// 测试获取服务 - 传入类型参数 + /// + [Fact] + public void TestGetService_Type() { + var services = _container.GetServices(); + services.AddSingleton(); + var result = _container.GetService(typeof( ISample ) ); + Assert.Equal( typeof( Sample ), result.GetType() ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Exceptions/WarningTest.cs b/test/Util.Core.Tests/Exceptions/WarningTest.cs new file mode 100644 index 000000000..8089ab7d6 --- /dev/null +++ b/test/Util.Core.Tests/Exceptions/WarningTest.cs @@ -0,0 +1,97 @@ +using System; +using Util.Exceptions; +using Xunit; + +namespace Util.Tests.Exceptions; + +/// +/// 应用程序异常测试 +/// +public class WarningTest { + /// + /// 测试设置消息 + /// + [Fact] + public void TestMessage() { + Warning warning = new Warning( "A" ); + Assert.Equal( "A", warning.Message ); + } + + /// + /// 测试消息为空 + /// + [Fact] + public void TestMessage_Null() { + Warning warning = new Warning( "" ); + Assert.Equal( string.Empty, warning.Message ); + } + + /// + /// 测试设置错误码 + /// + [Fact] + public void TestCode() { + Warning warning = new Warning( "", code:"B" ); + Assert.Equal( "B", warning.Code ); + } + + /// + /// 测试设置异常 + /// + [Fact] + public void TestException() { + Warning warning = new Warning( new Exception( "A" ) ); + Assert.Empty( warning.Message ); + Assert.Equal( "A", warning.GetMessage() ); + } + + /// + /// 测试设置错误消息和异常 + /// + [Fact] + public void TestMessageAndException() { + Warning warning = new Warning( "A", new Exception( "C" ) ); + Assert.Equal( "A", warning.Message ); + Assert.Equal( $"A{Environment.NewLine}C", warning.GetMessage() ); + } + + /// + /// 测试设置2层异常 + /// + [Fact] + public void TestException_2Layer() { + Warning warning = new Warning( "A", new Exception( "C", new NotImplementedException( "D" ) ) ); + Assert.Equal( 3, warning.GetExceptions().Count ); + Assert.Equal( typeof( Warning ), warning.GetExceptions()[0].GetType() ); + Assert.Equal( typeof( Exception ), warning.GetExceptions()[1].GetType() ); + Assert.Equal( typeof( NotImplementedException ), warning.GetExceptions()[2].GetType() ); + Assert.Equal( "A", warning.GetExceptions()[0].Message ); + Assert.Equal( "C", warning.GetExceptions()[1].Message ); + Assert.Equal( "D", warning.GetExceptions()[2].Message ); + Assert.Equal( $"A{Environment.NewLine}C{Environment.NewLine}D", warning.GetMessage() ); + } + + /// + /// 测试获取异常列表 + /// + [Fact] + public void TestGetExceptions_1() { + var exception = new Exception( "A" ); + var list = Warning.GetExceptions( exception ); + Assert.Single( list ); + Assert.Equal( "A", list[0].Message ); + } + + /// + /// 测试获取异常列表 + /// + [Fact] + public void TestGetExceptions_2() { + var exceptionB = new Exception( "B" ); + var exceptionA = new Exception( "A", exceptionB ); + var list = Warning.GetExceptions( exceptionA ); + Assert.Equal( 2, list.Count ); + Assert.Equal( "A", list[0].Message ); + Assert.Equal( "B", list[1].Message ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Extensions/CommonExtensionsTest.cs b/test/Util.Core.Tests/Extensions/CommonExtensionsTest.cs new file mode 100644 index 000000000..7d57f3963 --- /dev/null +++ b/test/Util.Core.Tests/Extensions/CommonExtensionsTest.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using Util.Tests.Samples; +using Xunit; + +namespace Util.Tests.Extensions; + +/// +/// 公共扩展测试 +/// +public class CommonExtensionsTest { + /// + /// 测试安全获取值 + /// + [Fact] + public void TestSafeValue() { + int? value = null; + Assert.Equal( 0, value.SafeValue() ); + value = 1; + Assert.Equal( 1, value.SafeValue() ); + } + + /// + /// 测试获取枚举值 + /// + [Fact] + public void TestValue() { + Assert.Equal( 2, EnumSample.B.Value() ); + Assert.Equal( "2", EnumSample.B.Value() ); + } + + /// + /// 测试获取枚举描述 + /// + [Fact] + public void TestDescription() { + Assert.Equal( "B2", EnumSample.B.Description() ); + } + + /// + /// 转换为用分隔符连接的字符串 + /// + [Fact] + public void TestJoin() { + Assert.Equal( "1,2,3", new List { 1, 2, 3 }.Join() ); + Assert.Equal( "'1','2','3'", new List { 1, 2, 3 }.Join( "'" ) ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Extensions/ConvertExtensionsTest.cs b/test/Util.Core.Tests/Extensions/ConvertExtensionsTest.cs new file mode 100644 index 000000000..d203690fd --- /dev/null +++ b/test/Util.Core.Tests/Extensions/ConvertExtensionsTest.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using Xunit; + +namespace Util.Tests.Extensions; + +/// +/// 类型转换扩展测试 +/// +public class ConvertExtensionsTest { + /// + /// 安全转换为字符串 + /// + /// 输入值 + /// 结果 + [Theory] + [InlineData( null, "" )] + [InlineData( " ", "" )] + [InlineData( 1,"1" )] + [InlineData( " 1 ", "1" )] + public void TestSafeString( object input, string result ) { + Assert.Equal( result, input.SafeString() ); + } + + /// + /// 转换为整数 + /// + [Theory] + [InlineData( "", 0 )] + [InlineData( "1", 1 )] + public void TestToInt( string input, int result ) { + Assert.Equal( result, input.ToInt() ); + } + + /// + /// 转换为可空整数 + /// + [Theory] + [InlineData( "", null )] + [InlineData( "1", 1 )] + public void TestToIntOrNull( string input, int? result ) { + Assert.Equal( result, input.ToIntOrNull() ); + } + + /// + /// 转换为双精度浮点数 + /// + [Theory] + [InlineData( "", 0 )] + [InlineData( "1.2", 1.2 )] + public void TestToDouble( string input, double result ) { + Assert.Equal( result, input.ToDouble() ); + } + + /// + /// 转换为可空双精度浮点数 + /// + [Theory] + [InlineData( "", null )] + [InlineData( "1.2", 1.2 )] + public void TestToDoubleOrNull( string input, double? result ) { + Assert.Equal( result, input.ToDoubleOrNull() ); + } + + /// + /// 转换为高精度浮点数 + /// + [Fact] + public void TestToDecimal() { + Assert.Equal( 0, "".ToDecimal() ); + Assert.Equal( 1.2M, "1.2".ToDecimal() ); + } + + /// + /// 转换为可空高精度浮点数 + /// + [Fact] + public void TestToDecimalOrNull() { + Assert.Null( "".ToDecimalOrNull() ); + Assert.Equal( 1.2M, "1.2".ToDecimalOrNull() ); + } + + /// + /// 转换为日期 + /// + [Fact] + public void TestToDateTime() { + Assert.Equal( DateTime.MinValue, "".ToDateTime() ); + Assert.Equal( new DateTime( 2000, 1, 1 ), "2000-1-1".ToDateTime() ); + } + + /// + /// 转换为可空日期 + /// + [Fact] + public void TestToDateTimeOrNull() { + Assert.Null( "".ToDateTimeOrNull() ); + Assert.Equal( new DateTime( 2000, 1, 1 ), "2000-1-1".ToDateTimeOrNull() ); + } + + /// + /// 转换为Guid + /// + [Fact] + public void TestToGuid() { + Assert.Equal( Guid.Empty, "".ToGuid() ); + Assert.Equal( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ), "B9EB56E9-B720-40B4-9425-00483D311DDC".ToGuid() ); + } + + /// + /// 转换为可空Guid + /// + [Fact] + public void TestToGuidOrNull() { + Assert.Null( "".ToGuidOrNull() ); + Assert.Equal( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ), "B9EB56E9-B720-40B4-9425-00483D311DDC".ToGuidOrNull() ); + } + + /// + /// 转换为Guid集合,值为字符串 + /// + [Fact] + public void TestToGuidList_String() { + const string guid = "83B0233C-A24F-49FD-8083-1337209EBC9A,,EAB523C6-2FE7-47BE-89D5-C6D440C3033A,"; + Assert.Equal( 2, guid.ToGuidList().Count ); + Assert.Equal( new Guid( "83B0233C-A24F-49FD-8083-1337209EBC9A" ), guid.ToGuidList()[0] ); + Assert.Equal( new Guid( "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" ), guid.ToGuidList()[1] ); + } + + /// + /// 转换为Guid集合,值为字符串集合 + /// + [Fact] + public void TestToGuidList_StringList() { + var list = new List { "83B0233C-A24F-49FD-8083-1337209EBC9A", "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" }; + Assert.Equal( 2, list.ToGuidList().Count ); + Assert.Equal( new Guid( "83B0233C-A24F-49FD-8083-1337209EBC9A" ), list.ToGuidList()[0] ); + Assert.Equal( new Guid( "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" ), list.ToGuidList()[1] ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Extensions/DateTimeExtensionsTest.cs b/test/Util.Core.Tests/Extensions/DateTimeExtensionsTest.cs new file mode 100644 index 000000000..ad60dd4a5 --- /dev/null +++ b/test/Util.Core.Tests/Extensions/DateTimeExtensionsTest.cs @@ -0,0 +1,201 @@ +using System; +using Util.Helpers; +using Xunit; +using Convert = Util.Helpers.Convert; + +namespace Util.Tests.Extensions; + +/// +/// 日期扩展测试 +/// +public class DateTimeExtensionsTest : IDisposable { + + #region 测试初始化 + + /// + /// 日期字符串1,值为 2012-12-12 12:12:12 + /// + private string _dateString1 = "2012-12-12 12:12:12"; + /// + /// 日期字符串2,值为 2012-12-12 20:12:12 + /// + private string _dateString2 = "2012-12-12 20:12:12"; + /// + /// 日期字符串3,值为 2012-12-12 12:12 + /// + private string _dateString3 = "2012-12-12 12:12"; + /// + /// 日期字符串4,值为 2012-12-12 20:12 + /// + private string _dateString4 = "2012-12-12 20:12"; + /// + /// 日期1,值为 2012-12-12 12:12:12 + /// + private DateTime _date = new DateTime( 2012, 12, 12, 12, 12, 12 ); + /// + /// Utc日期,值为 2012-12-12 12:12:12 + /// + private DateTime _utcDate = new DateTime( 2012, 12, 12, 12, 12, 12,DateTimeKind.Utc ); + /// + /// 可空日期1,值为 2012-12-12 12:12:12 + /// + private DateTime? _nullabledate = new DateTime( 2012, 12, 12, 12, 12, 12 ); + /// + /// 可空Utc日期,值为 2012-12-12 12:12:12 + /// + private DateTime? _nullableUtcDate = new DateTime( 2012, 12, 12, 12, 12, 12, DateTimeKind.Utc ); + + #endregion + + #region 测试清理 + + /// + /// 测试清理 + /// + public void Dispose() { + Time.Reset(); + } + + #endregion + + #region ToDateTimeString + + /// + /// 测试获取格式化日期时间字符串 + /// + [Fact] + public void TestToDateTimeString() { + Assert.Equal( _dateString1, _date.ToDateTimeString() ); + Assert.Equal( _dateString3, _date.ToDateTimeString( true ) ); + Assert.Equal( "", Convert.ToDateTimeOrNull( "" ).ToDateTimeString() ); + Assert.Equal( _dateString1, _nullabledate.ToDateTimeString() ); + Assert.Equal( _dateString3, _nullabledate.ToDateTimeString( true ) ); + Assert.Equal( _dateString2, _utcDate.ToDateTimeString() ); + Assert.Equal( _dateString4, _utcDate.ToDateTimeString( true ) ); + Assert.Equal( _dateString2, _nullableUtcDate.ToDateTimeString() ); + Assert.Equal( _dateString4, _nullableUtcDate.ToDateTimeString( true ) ); + } + + /// + /// 测试获取格式化日期时间字符串 - 使用Utc + /// + [Fact] + public void TestToDateTimeString_Utc() { + Time.UseUtc(); + Assert.Equal( _dateString1, _date.ToDateTimeString() ); + Assert.Equal( _dateString3, _date.ToDateTimeString( true ) ); + Assert.Equal( "", Convert.ToDateTimeOrNull( "" ).ToDateTimeString() ); + Assert.Equal( _dateString1, _nullabledate.ToDateTimeString() ); + Assert.Equal( _dateString3, _nullabledate.ToDateTimeString( true ) ); + Assert.Equal( _dateString2, _utcDate.ToDateTimeString() ); + Assert.Equal( _dateString4, _utcDate.ToDateTimeString( true ) ); + Assert.Equal( _dateString2, _nullableUtcDate.ToDateTimeString() ); + Assert.Equal( _dateString4, _nullableUtcDate.ToDateTimeString( true ) ); + } + + #endregion + + #region ToDateString + + /// + /// 获取格式化日期字符串 + /// + [Fact] + public void TestToDateString() { + string date = "2012-01-02"; + Assert.Equal( date, Convert.ToDateTime( date ).ToDateString() ); + Assert.Equal( "", Convert.ToDateTimeOrNull( "" ).ToDateString() ); + Assert.Equal( date, Convert.ToDateTimeOrNull( date ).ToDateString() ); + } + + #endregion + + #region ToTimeString + + /// + /// 获取格式化时间字符串 + /// + [Fact] + public void TestToTimeString() { + string date = "2012-01-02 11:11:11"; + Assert.Equal( "11:11:11", Convert.ToDateTime( date ).ToTimeString() ); + Assert.Equal( "", Convert.ToDateTimeOrNull( "" ).ToTimeString() ); + Assert.Equal( "11:11:11", Convert.ToDateTimeOrNull( date ).ToTimeString() ); + } + + #endregion + + #region ToMillisecondString + + /// + /// 获取格式化毫秒字符串 + /// + [Fact] + public void TestToMillisecondString() { + string date = "2012-01-02 11:11:11.123"; + Assert.Equal( date, Convert.ToDateTime( date ).ToMillisecondString() ); + Assert.Equal( "", Convert.ToDateTimeOrNull( "" ).ToMillisecondString() ); + Assert.Equal( date, Convert.ToDateTimeOrNull( date ).ToMillisecondString() ); + } + + #endregion + + #region ToChineseDateString + + /// + /// 获取格式化中文日期字符串 + /// + [Fact] + public void TestToChineseDateString() { + string date = "2012-01-02"; + Assert.Equal( "2012年1月2日", Convert.ToDateTime( date ).ToChineseDateString() ); + Assert.Equal( "2012年12月12日", Convert.ToDateTime( "2012-12-12" ).ToChineseDateString() ); + Assert.Equal( "", Convert.ToDateTimeOrNull( "" ).ToChineseDateString() ); + Assert.Equal( "2012年1月2日", Convert.ToDateTimeOrNull( date ).ToChineseDateString() ); + } + + #endregion + + #region ToChineseDateTimeString + + /// + /// 获取格式化中文日期时间字符串 + /// + [Fact] + public void TestToChineseDateTimeString() { + string date = "2012-01-02 11:11:11"; + Assert.Equal( "2012年1月2日 11时11分11秒", Convert.ToDateTime( date ).ToChineseDateTimeString() ); + Assert.Equal( "2012年12月12日 11时11分11秒", Convert.ToDateTime( "2012-12-12 11:11:11" ).ToChineseDateTimeString() ); + Assert.Equal( "2012年1月2日 11时11分", Convert.ToDateTime( date ).ToChineseDateTimeString( true ) ); + Assert.Equal( "", Convert.ToDateTimeOrNull( "" ).ToChineseDateTimeString() ); + Assert.Equal( "2012年1月2日 11时11分11秒", Convert.ToDateTimeOrNull( date ).ToChineseDateTimeString() ); + Assert.Equal( "2012年1月2日 11时11分", Convert.ToDateTimeOrNull( date ).ToChineseDateTimeString( true ) ); + } + + #endregion + + #region TimeSpan_Description + + /// + /// 测试获取时间间隔描述 + /// + [Fact] + public void TestDescription_TimeSpan() { + TimeSpan timeSpan = new DateTime( 2000, 1, 1, 1, 0, 1 ) - new DateTime( 2000, 1, 1, 1, 0, 0 ); + Assert.Equal( "1秒", timeSpan.Description() ); + timeSpan = new DateTime( 2000, 1, 1, 1, 1, 0 ) - new DateTime( 2000, 1, 1, 1, 0, 0 ); + Assert.Equal( "1分", timeSpan.Description() ); + timeSpan = new DateTime( 2000, 1, 1, 1, 0, 0 ) - new DateTime( 2000, 1, 1, 0, 0, 0 ); + Assert.Equal( "1小时", timeSpan.Description() ); + timeSpan = new DateTime( 2000, 1, 2, 0, 0, 0 ) - new DateTime( 2000, 1, 1, 0, 0, 0 ); + Assert.Equal( "1天", timeSpan.Description() ); + timeSpan = new DateTime( 2000, 1, 2, 0, 2, 0 ) - new DateTime( 2000, 1, 1, 0, 0, 0 ); + Assert.Equal( "1天2分", timeSpan.Description() ); + timeSpan = "2000-1-1 06:10:10.123".ToDateTime() - "2000-1-1 06:10:10.122".ToDateTime(); + Assert.Equal( "1毫秒", timeSpan.Description() ); + timeSpan = "2000-1-1 06:10:10.1000001".ToDateTime() - "2000-1-1 06:10:10.1000000".ToDateTime(); + Assert.Equal( "0.0001毫秒", timeSpan.Description() ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Extensions/ExpressionExtensionsTest.cs b/test/Util.Core.Tests/Extensions/ExpressionExtensionsTest.cs new file mode 100644 index 000000000..cd21ff4d2 --- /dev/null +++ b/test/Util.Core.Tests/Extensions/ExpressionExtensionsTest.cs @@ -0,0 +1,232 @@ +using System; +using System.Linq.Expressions; +using Util.Tests.Samples; +using Xunit; + +namespace Util.Tests.Extensions; + +/// +/// 表达式扩展测试 +/// +public class ExpressionExtensionsTest { + /// + /// 参数表达式 + /// + private readonly ParameterExpression _parameterExpression; + /// + /// 表达式1 + /// + private Expression _expression1; + /// + /// 表达式2 + /// + private Expression _expression2; + + /// + /// 测试初始化 + /// + public ExpressionExtensionsTest() { + _parameterExpression = Expression.Parameter( typeof( Sample ), "t" ); + } + + /// + /// 测试创建属性表达式 - 一级属性 + /// + [Fact] + public void TestProperty_1() { + var propertyExpression = _parameterExpression.Property( "StringValue" ); + Assert.Equal( "t.StringValue", propertyExpression.ToString() ); + } + + /// + /// 测试创建属性表达式 - 二级属性 + /// + [Fact] + public void TestProperty_2() { + var propertyExpression = _parameterExpression.Property( "NullableDateValue.Value" ); + Assert.Equal( "t.NullableDateValue.Value", propertyExpression.ToString() ); + } + + /// + /// 测试与操作 + /// + [Fact] + public void TestAnd_1() { + _expression1 = _parameterExpression.Property( "StringValue" ).Contains( "A" ); + _expression2 = _parameterExpression.Property( "NullableDateValue.Value.Year" ).Greater( Expression.Constant( 2000 ) ); + var andExpression = _expression1.And( _expression2 ).ToLambda>( _parameterExpression ); + Expression> expected = t => t.StringValue.Contains( "A" ) && t.NullableDateValue.Value.Year > 2000; + Assert.Equal( expected.ToString(), andExpression.ToString() ); + } + + /// + /// 测试与操作 + /// + [Fact] + public void TestAnd_2() { + Expression> left = t => t.StringValue == "A"; + Expression> right = t => t.StringValue == "B"; + Expression> expected = t => t.StringValue == "A" && t.StringValue == "B"; + Assert.Equal( expected.ToString(), left.And( right ).ToString() ); + } + + /// + /// 测试Or方法 + /// + [Fact] + public void TestOr() { + _expression1 = _parameterExpression.Property( "StringValue" ).Contains( "A" ); + _expression2 = _parameterExpression.Property( "NullableDateValue.Value.Year" ).Greater( Expression.Constant( 2000 ) ); + var andExpression = _expression1.Or( _expression2 ).ToLambda>( _parameterExpression ); + Expression> expected = t => t.StringValue.Contains( "A" ) || t.NullableDateValue.Value.Year > 2000; + Assert.Equal( expected.ToString(), andExpression.ToString() ); + } + + /// + /// 测试Or方法 - 左表达式为空 + /// + [Fact] + public void TestOr_LeftIsNull() { + _expression1 = null; + _expression2 = _parameterExpression.Property( "NullableDateValue.Value.Year" ).Greater( Expression.Constant( 2000 ) ); + var andExpression = _expression1.Or( _expression2 ).ToLambda>( _parameterExpression ); + Expression> expected = t => t.NullableDateValue.Value.Year > 2000; + Assert.Equal( expected.ToString(), andExpression.ToString() ); + } + + /// + /// 测试Or方法 - 右表达式为空 + /// + [Fact] + public void TestOr_RightIsNull() { + _expression1 = _parameterExpression.Property( "StringValue" ).Contains( "A" ); + _expression2 = null; + var andExpression = _expression1.Or( _expression2 ).ToLambda>( _parameterExpression ); + Expression> expected = t => t.StringValue.Contains( "A" ); + Assert.Equal( expected.ToString(), andExpression.ToString() ); + } + + /// + /// 测试Or方法 + /// + [Fact] + public void TestOr_2() { + Expression> left = t => t.StringValue == "A"; + Expression> right = t => t.StringValue == "B"; + Expression> expected = t => t.StringValue == "A" || t.StringValue == "B"; + Assert.Equal( expected.ToString(), left.Or( right ).ToString() ); + } + + /// + /// 测试Or方法 - 左表达式为空 + /// + [Fact] + public void TestOr_2_LeftIsNull() { + Expression> left = null; + Expression> right = t => t.StringValue == "B"; + Expression> expected = t => t.StringValue == "B"; + Assert.Equal( expected.ToString(), left.Or( right ).ToString() ); + } + + /// + /// 测试Or方法 - 右表达式为空 + /// + [Fact] + public void TestOr_2_RightIsNull() { + Expression> left = t => t.StringValue == "A"; + Expression> right = null; + Expression> expected = t => t.StringValue == "A"; + Assert.Equal( expected.ToString(), left.Or( right ).ToString() ); + } + + /// + /// 获取lambda表达式成员值 + /// + [Fact] + public void TestValue_LambdaExpression() { + Expression> expression = test => test.StringValue == "A"; + Assert.Equal( "A", expression.Value() ); + } + + /// + /// 测试相等 + /// + [Fact] + public void TestEqual() { + _expression1 = _parameterExpression.Property( "IntValue" ).Equal( 1 ); + Assert.Equal( "t => (t.IntValue == 1)", _expression1.ToLambda>( _parameterExpression ).ToString() ); + } + + /// + /// 测试不相等 + /// + [Fact] + public void TestNotEqual() { + _expression1 = _parameterExpression.Property( "NullableIntValue" ).NotEqual( 1 ); + Assert.Equal( "t => (t.NullableIntValue != 1)", _expression1.ToLambda>( _parameterExpression ).ToString() ); + } + + /// + /// 测试大于 + /// + [Fact] + public void TestGreater() { + _expression1 = _parameterExpression.Property( "NullableIntValue" ).Greater( 1 ); + Assert.Equal( "t => (t.NullableIntValue > 1)", _expression1.ToLambda>( _parameterExpression ).ToString() ); + } + + /// + /// 测试大于等于 + /// + [Fact] + public void TestGreaterEqual_Nullable() { + _expression1 = _parameterExpression.Property( "NullableIntValue" ).GreaterEqual( 1 ); + Assert.Equal( "t => (t.NullableIntValue >= 1)", _expression1.ToLambda>( _parameterExpression ).ToString() ); + } + + /// + /// 测试小于 + /// + [Fact] + public void TestLess() { + _expression1 = _parameterExpression.Property( "NullableIntValue" ).Less( 1 ); + Assert.Equal( "t => (t.NullableIntValue < 1)", _expression1.ToLambda>( _parameterExpression ).ToString() ); + } + + /// + /// 测试小于等于 + /// + [Fact] + public void TestLessEqual() { + _expression1 = _parameterExpression.Property( "NullableIntValue" ).LessEqual( 1 ); + Assert.Equal( "t => (t.NullableIntValue <= 1)", _expression1.ToLambda>( _parameterExpression ).ToString() ); + } + + /// + /// 测试头匹配 + /// + [Fact] + public void TestStartsWith() { + _expression1 = _parameterExpression.Property( "StringValue" ).StartsWith( "a" ); + Assert.Equal( "t => t.StringValue.StartsWith(\"a\")", _expression1.ToLambda>( _parameterExpression ).ToString() ); + } + + /// + /// 测试尾匹配 + /// + [Fact] + public void TestEndsWith() { + _expression1 = _parameterExpression.Property( "StringValue" ).EndsWith( "a" ); + Assert.Equal( "t => t.StringValue.EndsWith(\"a\")", _expression1.ToLambda>( _parameterExpression ).ToString() ); + } + + /// + /// 测试模糊匹配 + /// + [Fact] + public void TestContains() { + _expression1 = _parameterExpression.Property( "StringValue" ).Contains( "a" ); + Assert.Equal( "t => t.StringValue.Contains(\"a\")", + _expression1.ToLambda>( _parameterExpression ).ToString() ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Extensions/ReflectionExtensionsTest.cs b/test/Util.Core.Tests/Extensions/ReflectionExtensionsTest.cs new file mode 100644 index 000000000..ab2f2dc76 --- /dev/null +++ b/test/Util.Core.Tests/Extensions/ReflectionExtensionsTest.cs @@ -0,0 +1,20 @@ +using System.Linq; +using Util.Tests.Samples; +using Xunit; + +namespace Util.Tests.Extensions; + +/// +/// 反射扩展测试 +/// +public class ReflectionExtensionsTest { + /// + /// 测试安全获取值 + /// + [Fact] + public void TestSafeValue() { + Sample sample = new Sample { StringValue = "A" }; + var member = sample.GetType().GetMember( "StringValue" ).First(); + Assert.Equal( "A", member.GetPropertyValue( sample ) ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Extensions/StringExtensionsTest.cs b/test/Util.Core.Tests/Extensions/StringExtensionsTest.cs new file mode 100644 index 000000000..c4ecb7622 --- /dev/null +++ b/test/Util.Core.Tests/Extensions/StringExtensionsTest.cs @@ -0,0 +1,64 @@ +using System.IO; +using System.Text; +using Xunit; + +namespace Util.Tests.Extensions; + +/// +/// 字符串扩展测试 +/// +public class StringExtensionsTest { + /// + /// 测试移除起始字符串 - 字符串 + /// + [Fact] + public void TestRemoveStart_String() { + Assert.Equal( "bc", "abc".RemoveStart( "a" ) ); + } + + /// + /// 测试移除末尾字符串 - 字符串 + /// + [Fact] + public void TestRemoveEnd_String() { + Assert.Equal( "ab", "abc".RemoveEnd( "c" ) ); + } + + /// + /// 测试移除起始字符串 - StringBuilder + /// + [Fact] + public void TestRemoveStart_StringBuilder() { + var builder = new StringBuilder( "abc" ); + Assert.Equal( "bc", builder.RemoveStart( "a" ).ToString() ); + } + + /// + /// 测试移除末尾字符串 - StringBuilder + /// + [Fact] + public void TestRemoveEnd_StringBuilder() { + var builder = new StringBuilder( "abc" ); + Assert.Equal( "ab", builder.RemoveEnd( "c" ).ToString() ); + } + + /// + /// 测试移除起始字符串 - StringWriter + /// + [Fact] + public void TestRemoveStart_StringWriter() { + var writer = new StringWriter(); + writer.Write( "abc" ); + Assert.Equal( "bc", writer.RemoveStart( "a" ).ToString() ); + } + + /// + /// 测试移除末尾字符串 - StringWriter + /// + [Fact] + public void TestRemoveEnd_StringWriter() { + var writer = new StringWriter(); + writer.Write( "abc" ); + Assert.Equal( "ab", writer.RemoveEnd( "c" ).ToString() ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Extensions/ValidationExtensionsTest.cs b/test/Util.Core.Tests/Extensions/ValidationExtensionsTest.cs new file mode 100644 index 000000000..655ff77d4 --- /dev/null +++ b/test/Util.Core.Tests/Extensions/ValidationExtensionsTest.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using Util.Tests.XUnitHelpers; +using Xunit; + +namespace Util.Tests.Extensions; + +/// +/// 验证扩展测试 +/// +public class ValidationExtensionsTest { + /// + /// 检查空值,不为空则正常执行 + /// + [Fact] + public void TestCheckNull() { + object test = new object(); + test.CheckNull( "test" ); + } + + /// + /// 检查空值,值为null则抛出异常 + /// + [Fact] + public void TestCheckNull_Null_Throw() { + AssertHelper.Throws( () => { + object test = new object(); + test = null; + test.CheckNull( "test" ); + }, "test" ); + } + + /// + /// 测试是否空值 - 字符串 + /// + [Theory] + [InlineData( null,true )] + [InlineData( "", true )] + [InlineData( " ", true )] + [InlineData( "a", false )] + public void TestIsEmpty_String( string value,bool result ) { + Assert.Equal( value.IsEmpty(),result ); + } + + /// + /// 测试是否空值 - Guid + /// + [Fact] + public void TestIsEmpty_Guid() { + Assert.True( Guid.Empty.IsEmpty() ); + Assert.False( Guid.NewGuid().IsEmpty() ); + } + + /// + /// 测试是否空值 - 可空Guid + /// + [Fact] + public void TestIsEmpty_Guid_Nullable() { + Guid? value = null; + Assert.True( value.IsEmpty() ); + value = Guid.Empty; + Assert.True( value.IsEmpty() ); + value = Guid.NewGuid(); + Assert.False( value.IsEmpty() ); + } + + /// + /// 测试是否空值 - 集合 + /// + [Fact] + public void TestIsEmpty_List() { + List list = null; + Assert.True( list.IsEmpty() ); + list = new List(); + Assert.True( list.IsEmpty() ); + list.Add( 1 ); + Assert.False( list.IsEmpty() ); + } + + /// + /// 测试是否默认值 + /// + [Fact] + public void TestIsDefault() { + Assert.True( Guid.Empty.IsDefault() ); + Assert.False( Guid.NewGuid().IsDefault() ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Helpers/ConvertTest.cs b/test/Util.Core.Tests/Helpers/ConvertTest.cs new file mode 100644 index 000000000..b0de10d2e --- /dev/null +++ b/test/Util.Core.Tests/Helpers/ConvertTest.cs @@ -0,0 +1,582 @@ +using System; +using System.Collections.Generic; +using Util.Helpers; +using Util.Tests.Samples; +using Xunit; +using Convert = Util.Helpers.Convert; + +namespace Util.Tests.Helpers; + +/// +/// 类型转换测试 +/// +public class ConvertTest { + + #region ToInt + + /// + /// 转换为整型 + /// + /// 输入值 + /// 结果 + [Theory] + [InlineData( null, 0 )] + [InlineData( "", 0 )] + [InlineData( "1A", 0 )] + [InlineData( "0", 0 )] + [InlineData( "1", 1 )] + [InlineData( "1778019.78", 1778020 )] + [InlineData( "1778019.7801684", 1778020 )] + public void TestToInt( object input, int result ) { + Assert.Equal( result, Convert.ToInt( input ) ); + } + + #endregion + + #region ToIntOrNull + + /// + /// 转换为可空整型 + /// + /// 输入值 + /// 结果 + [Theory] + [InlineData( null, null )] + [InlineData( "", null )] + [InlineData( "1A", null )] + [InlineData( "0", 0 )] + [InlineData( "1", 1 )] + [InlineData( "1778019.78", 1778020 )] + [InlineData( "1778019.7801684", 1778020 )] + public void TestToIntOrNull( object input, int? result ) { + Assert.Equal( result, Convert.ToIntOrNull( input ) ); + } + + #endregion + + #region ToLong + + /// + /// 转换为64位整型 + /// + /// 输入值 + /// 结果 + [Theory] + [InlineData( null, 0 )] + [InlineData( "", 0 )] + [InlineData( "1A", 0 )] + [InlineData( "0", 0 )] + [InlineData( "1", 1 )] + [InlineData( "1778019.7801684", 1778020 )] + [InlineData( "177801978016841234", 177801978016841234 )] + public void TestToLong( object input, long result ) { + Assert.Equal( result, Convert.ToLong( input ) ); + } + + #endregion + + #region ToLongOrNull + + /// + /// 转换为64位可空整型 + /// + /// 输入值 + /// 结果 + [Theory] + [InlineData( null, null )] + [InlineData( "", null )] + [InlineData( "1A", null )] + [InlineData( "0", 0L )] + [InlineData( "1", 1L )] + [InlineData( "1778019.7801684", 1778020L )] + [InlineData( "177801978016841234", 177801978016841234L )] + public void TestToLongOrNull( object input, long? result ) { + Assert.Equal( result, Convert.ToLongOrNull( input ) ); + } + + #endregion + + #region ToFloat + + /// + /// 转换为32位浮点型 + /// + /// 输入值 + /// 结果 + /// 小数位数 + [Theory] + [InlineData( null, 0, null )] + [InlineData( "", 0, null )] + [InlineData( "1A", 0, null )] + [InlineData( "0", 0, null )] + [InlineData( "1", 1, null )] + [InlineData( "1.2", 1.2, null )] + [InlineData( "12.346", 12.35, 2 )] + public void TestToFloat( object input, float result, int? digits ) { + Assert.Equal( result, Convert.ToFloat( input, digits ) ); + } + + #endregion + + #region ToFloatOrNull + + /// + /// 转换为32位可空浮点型 + /// + /// 输入值 + /// 结果 + /// 小数位数 + [Theory] + [InlineData( null, null, null )] + [InlineData( "", null, null )] + [InlineData( "1A", null, null )] + [InlineData( "0", 0f, null )] + [InlineData( "1", 1f, null )] + [InlineData( "1.2", 1.2f, null )] + [InlineData( "12.346", 12.35f, 2 )] + public void TestToFloatOrNull( object input, float? result, int? digits ) { + Assert.Equal( result, Convert.ToFloatOrNull( input, digits ) ); + } + + #endregion + + #region ToDouble + + /// + /// 转换为64位浮点型 + /// + /// 输入值 + /// 结果 + /// 小数位数 + [Theory] + [InlineData( null, 0, null )] + [InlineData( "", 0, null )] + [InlineData( "1A", 0, null )] + [InlineData( "0", 0, null )] + [InlineData( "1", 1, null )] + [InlineData( "1.2", 1.2, null )] + [InlineData( "12.235", 12.24, 2 )] + [InlineData( "12.345", 12.34, 2 )] + [InlineData( "12.3451", 12.35, 2 )] + [InlineData( "12.346", 12.35, 2 )] + public void TestToDouble( object input, double result, int? digits ) { + Assert.Equal( result, Convert.ToDouble( input, digits ) ); + } + + #endregion + + #region ToDoubleOrNull + + /// + /// 转换为64位可空浮点型 + /// + /// 输入值 + /// 结果 + /// 小数位数 + [Theory] + [InlineData( null, null, null )] + [InlineData( "", null, null )] + [InlineData( "1A", null, null )] + [InlineData( "0", 0d, null )] + [InlineData( "1", 1d, null )] + [InlineData( "1.2", 1.2, null )] + [InlineData( "12.355", 12.36, 2 )] + public void TestToDoubleOrNull( object input, double? result, int? digits ) { + Assert.Equal( result, Convert.ToDoubleOrNull( input, digits ) ); + } + + #endregion + + #region ToDecimal + + /// + /// 转换为128位浮点型 + /// + /// 输入值 + /// 结果 + /// 小数位数 + [Theory] + [InlineData( null, 0, null )] + [InlineData( "", 0, null )] + [InlineData( "1A", 0, null )] + [InlineData( "0", 0, null )] + [InlineData( "1", 1, null )] + [InlineData( "1.2", 1.2, null )] + [InlineData( "12.235", 12.24, 2 )] + [InlineData( "12.345", 12.34, 2 )] + [InlineData( "12.3451", 12.35, 2 )] + [InlineData( "12.346", 12.35, 2 )] + public void TestToDecimal( object input, decimal result, int? digits ) { + Assert.Equal( result, Convert.ToDecimal( input, digits ) ); + } + + #endregion + + #region ToDecimalOrNull + + /// + /// 转换为128位可空浮点型,验证 + /// + /// 输入值 + /// 结果 + /// 小数位数 + [Theory] + [InlineData( null, null, null )] + [InlineData( "", null, null )] + [InlineData( "1A", null, null )] + [InlineData( "1A", null, 2 )] + public void TestToDecimalOrNull_Validate( object input, decimal? result, int? digits ) { + Assert.Equal( result, Convert.ToDecimalOrNull( input, digits ) ); + } + + /// + /// 转换为128位可空浮点型,输入值为"0" + /// + [Fact] + public void TestToDecimalOrNull() { + Assert.Equal( 0M, Convert.ToDecimalOrNull( "0" ) ); + Assert.Equal( 1.2M, Convert.ToDecimalOrNull( "1.2" ) ); + Assert.Equal( 23.46M, Convert.ToDecimalOrNull( "23.456", 2 ) ); + } + + #endregion + + #region ToBool + + /// + /// 转换为布尔型 + /// + /// 输入值 + /// 结果 + [Theory] + [InlineData( null, null )] + [InlineData( "false", false )] + [InlineData( "true", true )] + [InlineData( 0, false )] + [InlineData( 1, true )] + [InlineData( "0", false )] + [InlineData( "1", true )] + public void TestToBool( object input, bool result ) { + Assert.Equal( result, Convert.ToBool( input ) ); + } + + #endregion + + #region ToBoolOrNull + + /// + /// 转换为可空布尔型 + /// + /// 输入值 + /// 结果 + [Theory] + [InlineData( null, null )] + [InlineData( "false", false )] + [InlineData( "true", true )] + [InlineData( 0, false )] + [InlineData( 1, true )] + [InlineData( "0", false )] + [InlineData( "1", true )] + public void TestToBoolOrNull( object input, bool? result ) { + Assert.Equal( result, Convert.ToBoolOrNull( input ) ); + } + + #endregion + + #region ToDateTime + + /// + /// 转换为日期,验证 + /// + [Fact] + public void TestToDateTime_Validate() { + Assert.Equal( DateTime.MinValue, Convert.ToDateTime( null ) ); + Assert.Equal( DateTime.MinValue, Convert.ToDateTime( "" ) ); + Assert.Equal( DateTime.MinValue, Convert.ToDateTime( "1A" ) ); + } + + /// + /// 转换为日期 + /// + [Fact] + public void TestToDateTime() { + Assert.Equal( new DateTime( 2000, 1, 1 ), Convert.ToDateTime( "2000-1-1" ) ); + } + + #endregion + + #region ToDateTimeOrNull + + /// + /// 转换为可空日期,验证 + /// + /// 输入值 + /// 结果 + [Theory] + [InlineData( null, null )] + [InlineData( "", null )] + [InlineData( "1A", null )] + public void TestToDateTimeOrNull_Validate( object input, DateTime? result ) { + Assert.Equal( result, Convert.ToDateTimeOrNull( input ) ); + } + + /// + /// 转换为可空日期 + /// + [Fact] + public void TestToDateTimeOrNull() { + Assert.Equal( new DateTime( 2000, 1, 1 ), Convert.ToDateTimeOrNull( "2000-1-1" ) ); + } + + #endregion + + #region ToGuid + + /// + /// 转换为Guid,验证 + /// + [Fact] + public void TestToGuid_Validate() { + Assert.Equal( Guid.Empty, Convert.ToGuid( null ) ); + Assert.Equal( Guid.Empty, Convert.ToGuid( "" ) ); + Assert.Equal( Guid.Empty, Convert.ToGuid( "1A" ) ); + } + + /// + /// 转换为Guid + /// + [Fact] + public void TestToGuid() { + Assert.Equal( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ), Convert.ToGuid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ) ); + } + + #endregion + + #region ToGuidOrNull + + /// + /// 转换为可空Guid,验证 + /// + /// 输入值 + /// 结果 + [Theory] + [InlineData( null, null )] + [InlineData( "", null )] + [InlineData( "1A", null )] + public void TestToGuidOrNull_Validate( object input, Guid? result ) { + Assert.Equal( result, Convert.ToGuidOrNull( input ) ); + } + + /// + /// 转换为可空Guid - 转换字符串 + /// + [Fact] + public void TestToGuidOrNull_1() { + Assert.Equal( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ), Convert.ToGuidOrNull( "B9EB56E9-B720-40B4-9425-00483D311DDC" ) ); + } + + /// + /// 转换为可空Guid - 转换字节数组 + /// + [Fact] + public void TestToGuidOrNull_2() { + var guid = Guid.NewGuid(); + var bytes = guid.ToByteArray(); + Assert.Equal( guid, Convert.ToGuidOrNull( bytes ) ); + } + + /// + /// 转换为可空Guid - 转换字节数组 - 空guid + /// + [Fact] + public void TestToGuidOrNull_3() { + var guid = Guid.Empty; + var bytes = guid.ToByteArray(); + Assert.Equal( guid, Convert.ToGuidOrNull( bytes ) ); + } + + #endregion + + #region ToGuidList + + /// + /// 转换为Guid集合 + /// + [Fact] + public void TestToGuidList() { + Assert.Empty( Convert.ToGuidList( null ) ); + Assert.Empty( Convert.ToGuidList( "" ) ); + + const string guid = "83B0233C-A24F-49FD-8083-1337209EBC9A"; + Assert.Single( Convert.ToGuidList( guid ) ); + Assert.Equal( new Guid( guid ), Convert.ToGuidList( guid )[0] ); + + const string guid2 = "83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A"; + Assert.Equal( 2, Convert.ToGuidList( guid2 ).Count ); + Assert.Equal( new Guid( "83B0233C-A24F-49FD-8083-1337209EBC9A" ), Convert.ToGuidList( guid2 )[0] ); + Assert.Equal( new Guid( "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" ), Convert.ToGuidList( guid2 )[1] ); + } + + /// + /// 转换为Guid集合 + /// + [Fact] + public void TestToGuidList_2() { + const string guid = "83B0233C-A24F-49FD-8083-1337209EBC9A,,EAB523C6-2FE7-47BE-89D5-C6D440C3033A,"; + Assert.Equal( 2, Convert.ToGuidList( guid ).Count ); + Assert.Equal( new Guid( "83B0233C-A24F-49FD-8083-1337209EBC9A" ), Convert.ToGuidList( guid )[0] ); + Assert.Equal( new Guid( "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" ), Convert.ToGuidList( guid )[1] ); + } + + #endregion + + #region ToList + + /// + /// 泛型集合转换 + /// + [Fact] + public void TestToList() { + Assert.Empty( Convert.ToList( null ) ); + Assert.Single( Convert.ToList( "1" ) ); + Assert.Equal( 2, Convert.ToList( "1,2" ).Count ); + Assert.Equal( 2, Convert.ToList( "1,2" )[1] ); + } + + #endregion + + #region To + + /// + /// 测试通用泛型转换 + /// + [Fact] + public void TestTo() { + Assert.Null( Convert.To( "" ) ); + Assert.Equal( "1A", Convert.To( "1A" ) ); + Assert.Equal( 0, Convert.To( null ) ); + Assert.Equal( 0, Convert.To( "" ) ); + Assert.Equal( 0, Convert.To( "2A" ) ); + Assert.Equal( 1, Convert.To( "1" ) ); + Assert.Null( Convert.To( null ) ); + Assert.Null( Convert.To( "" ) ); + Assert.Null( Convert.To( "3A" ) ); + Assert.Equal( Guid.Empty, Convert.To( "" ) ); + Assert.Equal( Guid.Empty, Convert.To( "4A" ) ); + Assert.Equal( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ), Convert.To( "B9EB56E9-B720-40B4-9425-00483D311DDC" ) ); + Assert.Equal( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ), Convert.To( "B9EB56E9-B720-40B4-9425-00483D311DDC" ) ); + Assert.Equal( 12.5, Convert.To( "12.5" ) ); + Assert.Equal( 12.5, Convert.To( "12.5" ) ); + Assert.Equal( 12.5M, Convert.To( "12.5" ) ); + Assert.True( Convert.To( "true" ) ); + Assert.Equal( new DateTime( 2000, 1, 1 ), Convert.To( "2000-1-1" ) ); + Assert.Equal( new DateTime( 2000, 1, 1 ), Convert.To( "2000-1-1" ) ); + var guid = Guid.NewGuid(); + Assert.Equal( guid.ToString(), Convert.To( guid ) ); + Assert.Equal( EnumSample.C, Convert.To( "c" ) ); + } + + /// + /// 测试通用泛型转换 - 转换对象副本 + /// + [Fact] + public void TestTo_2() { + Sample4 sample = new Sample4 { StringValue = "a" }; + var result = Convert.To( sample.GetClone() ); + Assert.Equal( "a", result?.StringValue ); + } + + /// + /// 测试通用泛型转换 - 转换Json元素 + /// + [Fact] + public void TestTo_3() { + //序列化再反序列化字典 + var dic = new Dictionary { + {"a", new Sample3 {StringValue = "a"}}, + {"b", new Sample3 {StringValue = "b"}} + }; + var json = Json.ToJson( dic ); + dic = Json.ToObject>( json ); + + //从字典中获取元素并转换 + var element = dic["b"]; + var result = Convert.To( element ); + + //验证 + Assert.Equal( "b", result?.StringValue ); + } + + #endregion + + #region ToDictionary + + /// + /// 测试对象转换为属性名值对 + /// + [Fact] + public void TestToDictionary_1() { + var sample = new Sample2 { + BoolValue = true, + Description = "Description", + StringValue = "StringValue", + IntValue = 2, + Display = "Display", + NullableBoolValue = true, + DisplayName = "DisplayName", + DisplayName2 = "DisplayName2", + Test3 = new Sample3 { StringValue = "a" }, + TestList = new List { new() { StringValue = "a" }, new() { StringValue = "b" } } + }; + var result = Convert.ToDictionary( sample ); + Assert.Equal( 10, result.Count ); + Assert.Equal( "Description", result["Description"] ); + Assert.Equal( "Display", result["Display"] ); + Assert.Equal( "DisplayName", result["DisplayName"] ); + Assert.Equal( "DisplayName2", result["DisplayName2"] ); + Assert.Equal( 2, result["IntValue"] ); + } + + /// + /// 测试对象转换为属性名值对 + /// + [Fact] + public void TestToDictionary_2() { + var sample = new Sample2 { + BoolValue = true, + Description = "Description", + StringValue = "StringValue", + IntValue = 2, + Display = "Display", + NullableBoolValue = true, + DisplayName = "DisplayName", + DisplayName2 = "DisplayName2", + Test3 = new Sample3 { StringValue = "a" }, + TestList = new List { new() { StringValue = "a" }, new() { StringValue = "b" } } + }; + var result = Convert.ToDictionary( sample,true ); + Assert.Equal( 10, result.Count ); + Assert.Equal( "Description", result["描述"] ); + Assert.Equal( "Display", result["Display"] ); + Assert.Equal( "DisplayName", result["显示名"] ); + Assert.Equal( "DisplayName2", result["DisplayName2"] ); + Assert.Equal( 2, result["IntValue"] ); + } + + /// + /// 测试对象转换为属性名值对 - 传入字典 + /// + [Fact] + public void TestToDictionary_3() { + var content = new Dictionary { + { "Code", "a" }, + { "Price", 0 } + }; + var result = Convert.ToDictionary( content ); + Assert.Equal( 2, result.Count ); + Assert.Equal( "a", result["Code"] ); + Assert.Equal( 0, result["Price"] ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Helpers/CultureTest.cs b/test/Util.Core.Tests/Helpers/CultureTest.cs new file mode 100644 index 000000000..ec8fa1065 --- /dev/null +++ b/test/Util.Core.Tests/Helpers/CultureTest.cs @@ -0,0 +1,22 @@ +using System.Globalization; +using Xunit; + +namespace Util.Tests.Helpers; + +/// +/// 区域文化测试 +/// +public class CultureTest { + /// + /// 测试获取区域文化信息列表 + /// + [Fact] + public void TestGetCultures() { + var culture = new CultureInfo( "zh-CN" ); + var cultures = Util.Helpers.Culture.GetCultures( culture ); + Assert.Equal( 3, cultures.Count ); + Assert.Equal( "zh-CN", cultures[0].Name ); + Assert.Equal( "zh-Hans", cultures[1].Name ); + Assert.Equal( "zh", cultures[2].Name ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Helpers/EnumTest.cs b/test/Util.Core.Tests/Helpers/EnumTest.cs new file mode 100644 index 000000000..03311b3d3 --- /dev/null +++ b/test/Util.Core.Tests/Helpers/EnumTest.cs @@ -0,0 +1,234 @@ +using System; +using System.Linq; +using Util.Tests.Samples; +using Util.Tests.XUnitHelpers; +using Xunit; + +namespace Util.Tests.Helpers; + +/// +/// 测试枚举操作 +/// +public class EnumTest { + /// + /// 测试获取枚举实例 + /// + [Theory] + [InlineData( "C", EnumSample.C )] + [InlineData( "3", EnumSample.C )] + public void TestParse( string memeber, EnumSample sample ) { + Assert.Equal( sample, Util.Helpers.Enum.Parse( memeber ) ); + } + + /// + /// 测试获取枚举实例 - 参数为空,抛出异常 + /// + [Theory] + [InlineData( null )] + [InlineData( "" )] + public void TestParse_MemberIsEmpty( string member ) { + AssertHelper.Throws( () => { Util.Helpers.Enum.Parse( member ); }, "member" ); + } + + /// + /// 测试获取枚举实例 - 可空枚举 + /// + [Theory] + [InlineData( null, null )] + [InlineData( "", null )] + [InlineData( " ", null )] + [InlineData( "C", EnumSample.C )] + [InlineData( "3", EnumSample.C )] + public void TestParse_Nullable( string memeber, EnumSample? sample ) { + Assert.Equal( sample, Util.Helpers.Enum.Parse( memeber ) ); + } + + /// + /// 测试获取枚举成员名 + /// + [Theory] + [InlineData( null, "" )] + [InlineData( "", "" )] + [InlineData( " ", " " )] + [InlineData( "C", "C" )] + [InlineData( 3, "C" )] + [InlineData( EnumSample.C, "C" )] + public void TestGetName( object member, string name ) { + Assert.Equal( name, Util.Helpers.Enum.GetName( member ) ); + } + + /// + /// 测试获取枚举成员名 - 验证传入的枚举参数并非枚举类型 + /// + [Fact] + public void TestGetName_Validate() { + Assert.Equal( string.Empty, Util.Helpers.Enum.GetName( typeof( Sample ), 3 ) ); + } + + /// + /// 测试获取枚举成员名 - 可空枚举 + /// + [Theory] + [InlineData( null, "" )] + [InlineData( "", "" )] + [InlineData( " ", " " )] + [InlineData( "C", "C" )] + [InlineData( 3, "C" )] + [InlineData( EnumSample.C, "C" )] + public void TestGetName_Nullable( object member, string name ) { + Assert.Equal( name, Util.Helpers.Enum.GetName( member ) ); + } + + /// + /// 测试获取枚举成员值 + /// + [Theory] + [InlineData( "C", 3 )] + [InlineData( 3, 3 )] + [InlineData( EnumSample.C, 3 )] + public void TestGetValue( object member, int value ) { + Assert.Equal( value, Util.Helpers.Enum.GetValue( member ) ); + } + + /// + /// 测试获取枚举成员值 - 可空枚举 + /// + [Theory] + [InlineData( "C", 3 )] + [InlineData( 3, 3 )] + [InlineData( EnumSample.C, 3 )] + public void TestGetValue_Nullable( object member, int value ) { + Assert.Equal( value, Util.Helpers.Enum.GetValue( member ) ); + } + + /// + /// 测试获取枚举成员值 - 验证参数为空 + /// + [Fact] + public void TestGetValue_Null() { + Assert.Null( Util.Helpers.Enum.GetValue( null ) ); + } + + /// + /// 测试获取枚举描述 + /// + [Theory] + [InlineData( null, "" )] + [InlineData( "", "" )] + [InlineData( "A", "A" )] + [InlineData( "B", "B2" )] + [InlineData( 2, "B2" )] + [InlineData( EnumSample.B, "B2" )] + public void TestGetDescription( object member, string description ) { + Assert.Equal( description, Util.Helpers.Enum.GetDescription( member ) ); + } + + /// + /// 测试获取枚举描述 - 可空枚举 + /// + [Theory] + [InlineData( null, "" )] + [InlineData( "", "" )] + [InlineData( "A", "A" )] + [InlineData( "B", "B2" )] + [InlineData( 2, "B2" )] + [InlineData( EnumSample.B, "B2" )] + public void TestGetDescription_Nullable( object member, string description ) { + Assert.Equal( description, Util.Helpers.Enum.GetDescription( member ) ); + } + + /// + /// 测试获取项集合 + /// + [Fact] + public void TestGetItems() { + var items = Util.Helpers.Enum.GetItems(); + Assert.Equal( 5, items.Count ); + Assert.Equal( "A", items[0].Text ); + Assert.Equal( 1, items[0].Value ); + Assert.Equal( "D4", items[3].Text ); + Assert.Equal( 4, items[3].Value ); + Assert.Equal( "E5", items[4].Text ); + Assert.Equal( 5, items[4].Value ); + } + + /// + /// 测试获取项集合 + /// + [Fact] + public void TestGetItems_Type() { + var items = Util.Helpers.Enum.GetItems( typeof( EnumSample ) ); + Assert.Equal( 5, items.Count ); + Assert.Equal( "A", items[0].Text ); + Assert.Equal( 1, items[0].Value ); + Assert.Equal( "D4", items[3].Text ); + Assert.Equal( 4, items[3].Value ); + Assert.Equal( "E5", items[4].Text ); + Assert.Equal( 5, items[4].Value ); + } + + /// + /// 测试获取项集合 - 可空枚举 + /// + [Fact] + public void TestGetItems_Nullable() { + var items = Util.Helpers.Enum.GetItems(); + Assert.Equal( 5, items.Count ); + Assert.Equal( "A", items[0].Text ); + Assert.Equal( 1, items[0].Value ); + Assert.Equal( "D4", items[3].Text ); + Assert.Equal( 4, items[3].Value ); + Assert.Equal( "E5", items[4].Text ); + Assert.Equal( 5, items[4].Value ); + } + + /// + /// 测试获取项集合 - 可空枚举 + /// + [Fact] + public void TestGetItems_Nullable_Type() { + var items = Util.Helpers.Enum.GetItems( typeof( EnumSample? ) ); + Assert.Equal( 5, items.Count ); + Assert.Equal( "A", items[0].Text ); + Assert.Equal( 1, items[0].Value ); + Assert.Equal( "D4", items[3].Text ); + Assert.Equal( 4, items[3].Value ); + Assert.Equal( "E5", items[4].Text ); + Assert.Equal( 5, items[4].Value ); + } + + /// + /// 测试获取项集合 - 基础类型为byte的枚举 + /// + [Fact] + public void TestGetItems_Byte() { + var items = Util.Helpers.Enum.GetItems( typeof( HttpMethod ) ); + Assert.Equal( 11, items.Count ); + Assert.Equal( "Get", items[0].Text ); + Assert.Equal( 0, items[0].Value ); + Assert.Equal( "Put", items[1].Text ); + Assert.Equal( 1, items[1].Value ); + } + + /// + /// 测试获取项集合 - 验证枚举类型 + /// + [Fact] + public void TestGetItems_Validate() { + AssertHelper.Throws( () => { + Util.Helpers.Enum.GetItems(); + }, "类型 Util.Tests.Samples.Sample 不是枚举" ); + } + + /// + /// 测试获取名称集合 + /// + [Fact] + public void TestGetNames() { + var names = Util.Helpers.Enum.GetNames().OrderBy( t => t ).ToList(); + Assert.Equal( 5, names.Count ); + Assert.Equal( "A", names[0] ); + Assert.Equal( "D", names[3] ); + Assert.Equal( "E", names[4] ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Helpers/JsonTest.cs b/test/Util.Core.Tests/Helpers/JsonTest.cs new file mode 100644 index 000000000..54f1bab0b --- /dev/null +++ b/test/Util.Core.Tests/Helpers/JsonTest.cs @@ -0,0 +1,236 @@ +using System.Text; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.Unicode; +using System.Threading.Tasks; +using Util.Helpers; +using Util.SystemTextJson; +using Util.Tests.Samples; +using Xunit; + +namespace Util.Tests.Helpers; + +/// +/// Json测试 +/// +public class JsonTest { + /// + /// 测试转成Json - 转换为本地日期 + /// + [Fact] + public void TestToJson_Local() { + Time.UseUtc( false ); + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"Name\":\"a\"," ); + result.Append( "\"nickname\":\"b\"," ); + result.Append( "\"firstName\":\"c\"," ); + result.Append( "\"Date\":\"2012-12-12 20:12:12\"," ); + result.Append( "\"UtcDate\":\"2012-12-12 20:12:12\"," ); + result.Append( "\"Age\":1," ); + result.Append( "\"IsShow\":true," ); + result.Append( "\"Enum\":0" ); + result.Append( "}" ); + var sample = JsonTestSample.Create(); + Assert.Equal( result.ToString(), Json.ToJson( sample ) ); + } + + /// + /// 测试转成Json,移除双引号 + /// + [Fact] + public void TestToJson_RemoveQuotationMarks() { + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "Name:a," ); + result.Append( "nickname:b," ); + result.Append( "firstName:c," ); + result.Append( "Date:2012-12-12 20:12:12," ); + result.Append( "UtcDate:2012-12-12 20:12:12," ); + result.Append( "Age:1," ); + result.Append( "IsShow:true," ); + result.Append( "Enum:0" ); + result.Append( "}" ); + var sample = JsonTestSample.Create(); + Assert.Equal( result.ToString(), Json.ToJson( sample, removeQuotationMarks: true ) ); + } + + /// + /// 测试转成Json,将双引号转成单引号 + /// + [Fact] + public void TestToJson_ToSingleQuotes() { + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "'Name':'a'," ); + result.Append( "'nickname':'b'," ); + result.Append( "'firstName':'c'," ); + result.Append( "'Date':'2012-12-12 20:12:12'," ); + result.Append( "'UtcDate':'2012-12-12 20:12:12'," ); + result.Append( "'Age':1," ); + result.Append( "'IsShow':true," ); + result.Append( "'Enum':0" ); + result.Append( "}" ); + var sample = JsonTestSample.Create(); + Assert.Equal( result.ToString(), Json.ToJson( sample, toSingleQuotes: true ) ); + } + + /// + /// 测试转成Json - 序列化接口 + /// + [Fact] + public void TestToJson_Interface() { + Time.UseUtc( false ); + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"Name\":\"a\"," ); + result.Append( "\"nickname\":\"b\"," ); + result.Append( "\"firstName\":\"c\"," ); + result.Append( "\"Date\":\"2012-12-12 20:12:12\"," ); + result.Append( "\"UtcDate\":\"2012-12-12 20:12:12\"," ); + result.Append( "\"Age\":1," ); + result.Append( "\"IsShow\":true," ); + result.Append( "\"Enum\":0" ); + result.Append( "}" ); + var sample = JsonTestSample.CreateToInterface(); + Assert.Equal( result.ToString(), Json.ToJson( sample ) ); + } + + /// + /// 测试转成Json - 异步 + /// + [Fact] + public async Task TestToJsonAsync() { + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"Name\":\"a\"," ); + result.Append( "\"nickname\":\"b\"," ); + result.Append( "\"firstName\":\"c\"," ); + result.Append( "\"Date\":\"2012-12-12 20:12:12\"," ); + result.Append( "\"UtcDate\":\"2012-12-12 20:12:12\"," ); + result.Append( "\"Age\":1," ); + result.Append( "\"IsShow\":true," ); + result.Append( "\"Enum\":0" ); + result.Append( "}" ); + var sample = JsonTestSample.Create(); + var json = await Json.ToJsonAsync( sample ); + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试转成对象 + /// + [Fact] + public void TestToObject() { + var sample = Json.ToObject( "{\"Name\":\"a\"}" ); + Assert.Equal( "a", sample.Name ); + } + + /// + /// 测试转成对象 - 异步 + /// + [Fact] + public async Task TestToObjectAsync() { + var sample = await Json.ToObjectAsync( "{\"Name\":\"a\"}" ); + Assert.Equal( "a", sample.Name ); + } + + /// + /// 测试转成Json - 不转义中文字符 + /// + [Fact] + public void TestToJson_Encoder() { + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"Name\":\"哈哈\"," ); + result.Append( "\"nickname\":\"b\"," ); + result.Append( "\"firstName\":\"c\"," ); + result.Append( "\"Date\":\"2012-12-12 12:12:12\"," ); + result.Append( "\"Age\":1," ); + result.Append( "\"IsShow\":true," ); + result.Append( "\"Enum\":0" ); + result.Append( "}" ); + var sample = new JsonTestSample() { + Name = "哈哈", + nickname = "b", + FirstName = "c", + Value = null, + Date = Util.Helpers.Convert.ToDateTime( "2012-12-12 12:12:12" ), + Age = 1, + IsShow = true + }; + Assert.Equal( result.ToString(), Json.ToJson( sample, new JsonSerializerOptions { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Encoder = JavaScriptEncoder.Create( UnicodeRanges.All ), + Converters = { new NullableDateTimeJsonConverter() } + } ) ); + } + + /// + /// 测试转成Json - 枚举转换器 + /// + [Fact] + public void Test_Enum() { + var sample = new JsonTestSample() { + Enum = TestEnum.Test2, + NullableEnum = TestEnum.Test2 + }; + var json = Json.ToJson( sample, + new JsonSerializerOptions { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, + Converters = { new EnumJsonConverterFactory() } + } ); + + var result = Json.ToObject( json, new JsonSerializerOptions() { + Converters = { new EnumJsonConverterFactory() } + } ); + Assert.True( result.Enum == TestEnum.Test2 ); + Assert.True( result.NullableEnum == TestEnum.Test2 ); + } + + /// + /// 测试转成Json - long类型 + /// + [Fact] + public void TestToJson_Long() { + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"Long\":\"123456789123456789\"" ); + result.Append( "}" ); + var sample = new JsonTestSample2 { Long = 123456789123456789 }; + var json = Json.ToJson( sample ); + Assert.Equal( result.ToString(), json ); + + var obj = Json.ToObject( json ); + Assert.Equal( 123456789123456789, obj.Long ); + } + + /// + /// 测试转成Json - long?类型 + /// + [Fact] + public void TestToJson_NullableLong() { + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"Long\":\"0\"," ); + result.Append( "\"NullableLong\":\"123456789123456789\"" ); + result.Append( "}" ); + var sample = new JsonTestSample2 { NullableLong = 123456789123456789 }; + var json = Json.ToJson( sample ); + Assert.Equal( result.ToString(), json ); + + var obj = Json.ToObject( json ); + Assert.Equal( 123456789123456789, obj.NullableLong ); + } + + /// + /// 测试转成Json - 忽略空字符串 + /// + [Fact] + public void TestToJson_IgnoreEmpty() { + var sample = new JsonTestSample3 { Name = "" }; + var json = Json.ToJson( sample, new JsonOptions { IgnoreEmptyString = true } ); + Assert.Equal( "{}", json ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Helpers/LambdaTest.cs b/test/Util.Core.Tests/Helpers/LambdaTest.cs new file mode 100644 index 000000000..b31606743 --- /dev/null +++ b/test/Util.Core.Tests/Helpers/LambdaTest.cs @@ -0,0 +1,973 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using Util.Data; +using Util.Helpers; +using Util.Tests.Samples; +using Xunit; + +namespace Util.Tests.Helpers; + +/// +/// 测试Lambda表达式操作 +/// +public class LambdaTest { + + #region GetMember(获取成员) + + /// + /// 测试获取成员 + /// + [Fact] + public void TestGetMember() { + Assert.Null( Lambda.GetMember( null ) ); + + Expression> expression = t => t.StringValue; + Assert.Equal( "StringValue", Lambda.GetMember( expression ).Name ); + + Expression> expression2 = t => t.Test2.StringValue; + Assert.Equal( "StringValue", ( (PropertyInfo)Lambda.GetMember( expression2 ) ).Name ); + } + + #endregion + + #region GetName(获取成员名称) + + /// + /// 测试获取成员名称 + /// + [Fact] + public void TestGetName() { + Assert.Equal( "", Lambda.GetName( null ) ); + + Expression> expression = t => t.StringValue; + Assert.Equal( "StringValue", Lambda.GetName( expression ) ); + + Expression> expression2 = t => t.Test2.StringValue; + Assert.Equal( "Test2.StringValue", Lambda.GetName( expression2 ) ); + + Expression> expression3 = t => t.Test2.Test3.StringValue; + Assert.Equal( "Test2.Test3.StringValue", Lambda.GetName( expression3 ) ); + + Expression> expression4 = t => t.NullableIntValue; + Assert.Equal( "NullableIntValue", Lambda.GetName( expression4 ) ); + + Expression> expression5 = t => t.IntValue; + Assert.Equal( "IntValue", Lambda.GetName( expression5 ) ); + + Expression> expression6 = t => t.BoolValue; + Assert.Equal( "BoolValue", Lambda.GetName( expression6 ) ); + + expression6 = t => !t.BoolValue; + Assert.Equal( "BoolValue", Lambda.GetName( expression6 ) ); + + expression6 = t => !t.Test2.BoolValue; + Assert.Equal( "Test2.BoolValue", Lambda.GetName( expression6 ) ); + } + + #endregion + + #region GetNames(获取成员名称列表) + + /// + /// 测试获取成员名称列表 + /// + [Fact] + public void TestGetNames() { + Expression> expression = ( t => new object[] { t.Test2.StringValue, t.IntValue } ); + Assert.Equal( 2, Lambda.GetNames( expression ).Count ); + Assert.Equal( "Test2.StringValue", Lambda.GetNames( expression )[0] ); + Assert.Equal( "IntValue", Lambda.GetNames( expression )[1] ); + } + + #endregion + + #region GetLastName(获取最后一级成员名称) + + /// + /// 测试获取成员名称 - 返回类型为Object + /// + [Fact] + public void TestGetLastName_Object() { + Expression> expression = t => t.StringValue == "A"; + Assert.Equal( "StringValue", Lambda.GetLastName( expression ) ); + } + + /// + /// 测试获取成员名称 - 返回类型为bool + /// + [Fact] + public void TestGetLastName_Bool() { + Assert.Empty( Lambda.GetLastName( null ) ); + + Expression> expression = t => t.StringValue == "A"; + Assert.Equal( "StringValue", Lambda.GetLastName( expression ) ); + + Expression> expression2 = t => t.Test2.IntValue == 1; + Assert.Equal( "IntValue", Lambda.GetLastName( expression2 ) ); + + Expression> expression3 = t => t.Test2.Test3.StringValue == "B"; + Assert.Equal( "StringValue", Lambda.GetLastName( expression3 ) ); + + expression3 = t => !t.Test2.BoolValue; + Assert.Equal( "BoolValue", Lambda.GetLastName( expression3 ) ); + + expression = t => t.StringValue == "A"; + Assert.Empty( Lambda.GetLastName( expression, true ) ); + + var value = "a"; + expression = t => t.StringValue == value; + Assert.Empty( Lambda.GetLastName( expression, true ) ); + + var sample = new Sample(); + expression = t => t.StringValue == sample.StringValue; + Assert.Empty( Lambda.GetLastName( expression, true ) ); + } + + /// + /// 测试获取成员名称 - 运算符 + /// + [Fact] + public void TestGetLastName_Operation() { + Expression> expression = t => t.Test2.IntValue != 1; + Assert.Equal( "IntValue", Lambda.GetLastName( expression ) ); + + expression = t => t.Test2.IntValue > 1; + Assert.Equal( "IntValue", Lambda.GetLastName( expression ) ); + + expression = t => t.Test2.IntValue < 1; + Assert.Equal( "IntValue", Lambda.GetLastName( expression ) ); + + expression = t => t.Test2.IntValue >= 1; + Assert.Equal( "IntValue", Lambda.GetLastName( expression ) ); + + expression = t => t.Test2.IntValue <= 1; + Assert.Equal( "IntValue", Lambda.GetLastName( expression ) ); + } + + /// + /// 测试获取成员名称 - 可空类型 + /// + [Fact] + public void TestGetLastName_Nullable() { + Expression> expression = t => t.NullableIntValue == 1; + Assert.Equal( "NullableIntValue", Lambda.GetLastName( expression ) ); + + expression = t => t.NullableDecimalValue == 1.5M; + Assert.Equal( "NullableDecimalValue", Lambda.GetLastName( expression ) ); + } + + /// + /// 测试获取成员名称 - 方法 + /// + [Fact] + public void TestGetLastName_Method() { + Expression> expression = t => t.StringValue.Contains( "A" ); + Assert.Equal( "StringValue", Lambda.GetLastName( expression ) ); + + expression = t => t.Test2.StringValue.Contains( "B" ); + Assert.Equal( "StringValue", Lambda.GetLastName( expression ) ); + + expression = t => t.Test2.Test3.StringValue.StartsWith( "C" ); + Assert.Equal( "StringValue", Lambda.GetLastName( expression ) ); + + var test = new Sample { Email = "a" }; + expression = t => t.StringValue.Contains( test.Email ); + Assert.Equal( "StringValue", Lambda.GetLastName( expression ) ); + } + + /// + /// 测试获取成员名称 - 实例 + /// + [Fact] + public void TestGetLastName_Instance() { + var test = new Sample() { StringValue = "a", Test2 = new Sample2() { StringValue = "b", Test3 = new Sample3() { StringValue = "c" } } }; + + Expression> expression = () => test.StringValue; + Assert.Empty( Lambda.GetLastName( expression ) ); + + Expression> expression2 = () => test.Test2.StringValue; + Assert.Empty( Lambda.GetLastName( expression2 ) ); + + Expression> expression3 = () => test.Test2.Test3.StringValue; + Assert.Empty( Lambda.GetLastName( expression3 ) ); + } + + /// + /// 测试获取成员名称 - 复杂类型 + /// + [Fact] + public void TestGetLastName_Complex() { + var test = new Sample() { StringValue = "a", Test2 = new Sample2() { StringValue = "b" } }; + + Expression> expression = t => t.StringValue == test.StringValue; + Assert.Equal( "StringValue", Lambda.GetLastName( expression ) ); + Expression> expression2 = t => t.StringValue == test.Test2.StringValue; + Assert.Equal( "StringValue", Lambda.GetLastName( expression2 ) ); + + Expression> expression3 = t => t.StringValue.Contains( test.StringValue ); + Assert.Equal( "StringValue", Lambda.GetLastName( expression3 ) ); + Expression> expression4 = t => t.StringValue.Contains( test.Test2.StringValue ); + Assert.Equal( "StringValue", Lambda.GetLastName( expression4 ) ); + } + + /// + /// 测试获取成员名称 - 枚举 + /// + [Fact] + public void TestGetLastName_Enum() { + var test1 = new Sample { NullableEnumValue = EnumSample.C }; + + Expression> expression = test => test.EnumValue == EnumSample.D; + Assert.Equal( "EnumValue", Lambda.GetLastName( expression ) ); + + expression = test => test.EnumValue == test1.NullableEnumValue; + Assert.Equal( "EnumValue", Lambda.GetLastName( expression ) ); + + expression = test => test.NullableEnumValue == EnumSample.E; + Assert.Equal( "NullableEnumValue", Lambda.GetLastName( expression ) ); + + expression = test => test.NullableEnumValue == test1.NullableEnumValue; + Assert.Equal( "NullableEnumValue", Lambda.GetLastName( expression ) ); + + test1.NullableEnumValue = null; + expression = test => test.NullableEnumValue == test1.NullableEnumValue; + Assert.Equal( "NullableEnumValue", Lambda.GetLastName( expression ) ); + } + + /// + /// 测试获取成员名称 - 集合 + /// + [Fact] + public void TestGetLastName_List() { + var list = new List { "a", "b" }; + Expression> expression = t => list.Contains( t.Test2.StringValue ); + Assert.Equal( "StringValue", Lambda.GetLastName( expression ) ); + } + + /// + /// 测试获取成员名称 - 静态成员 + /// + [Fact] + public void TestGetLastName_Static() { + Expression> expression = t => t.StringValue == Sample.StaticString; + Assert.Equal( "StringValue", Lambda.GetLastName( expression ) ); + + expression = t => t.StringValue == Sample.StaticSample.StringValue; + Assert.Equal( "StringValue", Lambda.GetLastName( expression ) ); + + expression = t => t.Test2.StringValue == Sample.StaticString; + Assert.Equal( "StringValue", Lambda.GetLastName( expression ) ); + + expression = t => t.Test2.StringValue == Sample.StaticSample.StringValue; + Assert.Equal( "StringValue", Lambda.GetLastName( expression ) ); + + expression = t => t.Test2.StringValue.Contains( Sample.StaticSample.StringValue ); + Assert.Equal( "StringValue", Lambda.GetLastName( expression ) ); + } + + /// + /// 测试获取成员名称 - 返回右侧表达式名称 + /// + [Fact] + public void TestGetLastName_Right() { + Expression> expression = ( l, r ) => l.DisplayValue == r.Test3.StringValue; + Assert.Equal( "DisplayValue", Lambda.GetLastName( expression ) ); + Assert.Equal( "StringValue", Lambda.GetLastName( expression, true ) ); + } + + #endregion + + #region GetLastNames(获取最后一级成员名称列表) + + /// + /// 获取最后一级成员名称列表 + /// + [Fact] + public void TestGetLastNames() { + Expression> expression = ( t => new object[] { t.Test2.StringValue, t.IntValue } ); + Assert.Equal( 2, Lambda.GetLastNames( expression ).Count ); + Assert.Equal( "StringValue", Lambda.GetLastNames( expression )[0] ); + Assert.Equal( "IntValue", Lambda.GetLastNames( expression )[1] ); + } + + #endregion + + #region GetValue(获取成员值) + + /// + /// 测试获取成员值 - 返回类型为Object + /// + [Fact] + public void TestGetValue_Object() { + Expression> expression = t => t.StringValue == "A"; + Assert.Equal( "A", Lambda.GetValue( expression ) ); + } + + /// + /// 测试获取成员值 + /// + [Fact] + public void TestGetValue() { + Assert.Null( Lambda.GetValue( null ) ); + + Expression> expression = t => t.StringValue == "A"; + Assert.Equal( "A", Lambda.GetValue( expression ) ); + + Expression> expression2 = t => t.Test2.IntValue == 1; + Assert.Equal( 1, Lambda.GetValue( expression2 ) ); + + Expression> expression3 = t => t.Test2.Test3.StringValue == "B"; + Assert.Equal( "B", Lambda.GetValue( expression3 ) ); + + var value = Guid.NewGuid(); + Expression> expression4 = t => t.GuidValue == value; + Assert.Equal( value, Lambda.GetValue( expression4 ) ); + + Expression> expression5 = t => 1 == t.Test2.IntValue; + Assert.Equal( 1, Lambda.GetValue( expression5 ) ); + } + + /// + /// 测试获取成员值 - 布尔属性 + /// + [Fact] + public void TestGetValue_Bool() { + Expression> expression = t => t.BoolValue; + Assert.Equal( "True", Lambda.GetValue( expression ).ToString() ); + + expression = t => !t.BoolValue; + Assert.Equal( "False", Lambda.GetValue( expression ).ToString() ); + + expression = t => t.Test2.BoolValue; + Assert.Equal( "True", Lambda.GetValue( expression ).ToString() ); + + expression = t => !t.Test2.BoolValue; + Assert.Equal( "False", Lambda.GetValue( expression ).ToString() ); + + expression = t => t.BoolValue == true; + Assert.Equal( "True", Lambda.GetValue( expression ).ToString() ); + + expression = t => t.BoolValue == false; + Assert.Equal( "False", Lambda.GetValue( expression ).ToString() ); + } + + /// + /// 测试获取成员值 - Guid.NewGuid + /// + [Fact] + public void TestGetValue_NewGuid() { + Expression> expression = t => t.GuidValue == Guid.NewGuid(); + var value = Lambda.GetValue( expression ); + Assert.NotEqual( Guid.Empty, Util.Helpers.Convert.ToGuid( value ) ); + } + + /// + /// 测试获取成员值 - DateTime.Now + /// + [Fact] + public void TestGetValue_DateTimeNow() { + Expression> expression = t => t.DateValue == DateTime.Now; + var value = Lambda.GetValue( expression ); + Assert.NotNull( Util.Helpers.Convert.ToDateTimeOrNull( value ) ); + } + + /// + /// 测试获取成员值 - 运算符 + /// + [Fact] + public void TestGetValue_Operation() { + Expression> expression = t => t.Test2.IntValue != 1; + Assert.Equal( 1, Lambda.GetValue( expression ) ); + + expression = t => t.Test2.IntValue > 1; + Assert.Equal( 1, Lambda.GetValue( expression ) ); + + expression = t => t.Test2.IntValue < 1; + Assert.Equal( 1, Lambda.GetValue( expression ) ); + + expression = t => t.Test2.IntValue >= 1; + Assert.Equal( 1, Lambda.GetValue( expression ) ); + + expression = t => t.Test2.IntValue <= 1; + Assert.Equal( 1, Lambda.GetValue( expression ) ); + } + + /// + /// 测试获取成员值 - 可空类型 + /// + [Fact] + public void TestGetValue_Nullable() { + Expression> expression = t => t.NullableIntValue == 1; + Assert.Equal( 1, Lambda.GetValue( expression ) ); + + expression = t => t.NullableDecimalValue == 1.5M; + Assert.Equal( 1.5M, Lambda.GetValue( expression ) ); + + var sample = new Sample(); + expression = t => t.BoolValue == sample.NullableBoolValue; + Assert.Null( Lambda.GetValue( expression ) ); + } + + /// + /// 测试获取成员值 - 方法 + /// + [Fact] + public void TestGetValue_Method() { + Expression> expression = t => t.StringValue.Contains( "A" ); + Assert.Equal( "A", Lambda.GetValue( expression ) ); + + expression = t => t.Test2.StringValue.Contains( "B" ); + Assert.Equal( "B", Lambda.GetValue( expression ) ); + + expression = t => t.Test2.Test3.StringValue.StartsWith( "C" ); + Assert.Equal( "C", Lambda.GetValue( expression ) ); + + var test = new Sample { Email = "a" }; + expression = t => t.StringValue.Contains( test.Email ); + Assert.Equal( "a", Lambda.GetValue( expression ) ); + } + + /// + /// 测试获取成员值 - 扩展方法 + /// + [Fact] + public void TestGetValue_ExtensionMethod() { + var test = new Sample { Email = "a" }; + Expression> expression = t => t.TestList.Any( a => a.StringValue == test.Email ); + Assert.Equal( "a", Lambda.GetValue( expression ) ); + } + + /// + /// 测试获取成员值 - 扩展方法 - 值为null + /// + [Fact] + public void TestGetValue_ExtensionMethod_Null() { + var test = new Sample { Email = null }; + Expression> expression = t => t.TestList.Any( a => a.StringValue == test.Email ); + Assert.Null( Lambda.GetValue( expression ) ); + } + + /// + /// 测试获取成员值 - 实例 + /// + [Fact] + public void TestGetValue_Instance() { + var test = new Sample() { StringValue = "a", BoolValue = true, Test2 = new Sample2() { StringValue = "b", Test3 = new Sample3() { StringValue = "c" } } }; + + Expression> expression = () => test.StringValue; + Assert.Equal( "a", Lambda.GetValue( expression ) ); + + Expression> expression2 = () => test.Test2.StringValue; + Assert.Equal( "b", Lambda.GetValue( expression2 ) ); + + Expression> expression3 = () => test.Test2.Test3.StringValue; + Assert.Equal( "c", Lambda.GetValue( expression3 ) ); + + Expression> expression4 = () => test.BoolValue; + Assert.True( Util.Helpers.Convert.ToBool( Lambda.GetValue( expression4 ) ) ); + } + + /// + /// 测试获取成员值 - 复杂类型 + /// + [Fact] + public void TestGetValue_Complex() { + var test = new Sample() { StringValue = "a", Test2 = new Sample2() { StringValue = "b" } }; + + Expression> expression = t => t.StringValue == test.StringValue; + Assert.Equal( "a", Lambda.GetValue( expression ) ); + Expression> expression2 = t => t.StringValue == test.Test2.StringValue; + Assert.Equal( "b", Lambda.GetValue( expression2 ) ); + + Expression> expression3 = t => t.StringValue.Contains( test.StringValue ); + Assert.Equal( "a", Lambda.GetValue( expression3 ) ); + Expression> expression4 = t => t.StringValue.Contains( test.Test2.StringValue ); + Assert.Equal( "b", Lambda.GetValue( expression4 ) ); + } + + /// + /// 测试获取成员值 - 枚举 + /// + [Fact] + public void TestGetValue_Enum() { + var test1 = new Sample { NullableEnumValue = EnumSample.C }; + + Expression> expression = test => test.EnumValue == EnumSample.D; + Assert.Equal( EnumSample.D.Value(), Util.Helpers.Enum.GetValue( Lambda.GetValue( expression ) ) ); + + expression = test => test.EnumValue == test1.NullableEnumValue; + Assert.Equal( EnumSample.C, Lambda.GetValue( expression ) ); + + expression = test => test.NullableEnumValue == EnumSample.E; + Assert.Equal( EnumSample.E, Lambda.GetValue( expression ) ); + + expression = test => test.NullableEnumValue == test1.NullableEnumValue; + Assert.Equal( EnumSample.C, Lambda.GetValue( expression ) ); + + test1.NullableEnumValue = null; + expression = test => test.NullableEnumValue == test1.NullableEnumValue; + Assert.Null( Lambda.GetValue( expression ) ); + } + + /// + /// 测试获取成员值 - 集合 + /// + [Fact] + public void TestGetValue_List() { + var list = new List { "a", "b" }; + Expression> expression = t => list.Contains( t.StringValue ); + Assert.Equal( list, Lambda.GetValue( expression ) ); + } + + /// + /// 测试获取成员值 - 静态成员 + /// + [Fact] + public void TestGetValue_Static() { + Expression> expression = t => t.StringValue == Sample.StaticString; + Assert.Equal( "TestStaticString", Lambda.GetValue( expression ) ); + + expression = t => t.StringValue == Sample.StaticSample.StringValue; + Assert.Equal( "TestStaticSample", Lambda.GetValue( expression ) ); + + expression = t => t.Test2.StringValue == Sample.StaticString; + Assert.Equal( "TestStaticString", Lambda.GetValue( expression ) ); + + expression = t => t.Test2.StringValue == Sample.StaticSample.StringValue; + Assert.Equal( "TestStaticSample", Lambda.GetValue( expression ) ); + + expression = t => t.Test2.StringValue.Contains( Sample.StaticSample.StringValue ); + Assert.Equal( "TestStaticSample", Lambda.GetValue( expression ) ); + } + + #endregion + + #region GetOperator(获取查询操作符) + + /// + /// 获取查询操作符 - 返回类型为Object + /// + [Fact] + public void TestGetOperator_Object() { + Expression> expression = t => t.StringValue == "A"; + Assert.Equal( Operator.Equal, Lambda.GetOperator( expression ) ); + } + + /// + /// 获取查询操作符 - 返回类型为bool + /// + [Fact] + public void TestGetOperator() { + Assert.Null( Lambda.GetOperator( null ) ); + + Expression> expression = t => t.StringValue == "A"; + Assert.Equal( Operator.Equal, Lambda.GetOperator( expression ) ); + + Expression> expression2 = t => t.Test2.IntValue == 1; + Assert.Equal( Operator.Equal, Lambda.GetOperator( expression2 ) ); + + Expression> expression3 = t => t.Test2.Test3.StringValue == "B"; + Assert.Equal( Operator.Equal, Lambda.GetOperator( expression3 ) ); + } + + /// + /// 获取查询操作符 - 运算符 + /// + [Fact] + public void TestGetOperator_Operation() { + Expression> expression = t => t.Test2.IntValue != 1; + Assert.Equal( Operator.NotEqual, Lambda.GetOperator( expression ) ); + + expression = t => t.Test2.IntValue > 1; + Assert.Equal( Operator.Greater, Lambda.GetOperator( expression ) ); + + expression = t => t.Test2.IntValue < 1; + Assert.Equal( Operator.Less, Lambda.GetOperator( expression ) ); + + expression = t => t.Test2.IntValue >= 1; + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( expression ) ); + + expression = t => t.Test2.IntValue <= 1; + Assert.Equal( Operator.LessEqual, Lambda.GetOperator( expression ) ); + } + + /// + /// 获取查询操作符 - 可空类型 + /// + [Fact] + public void TestGetOperator_Nullable() { + Expression> expression = t => t.NullableIntValue == 1; + Assert.Equal( Operator.Equal, Lambda.GetOperator( expression ) ); + + expression = t => t.NullableDecimalValue == 1.5M; + Assert.Equal( Operator.Equal, Lambda.GetOperator( expression ) ); + } + + /// + /// 获取查询操作符 - 方法 + /// + [Fact] + public void TestGetOperator_Method() { + Expression> expression = t => t.StringValue.Contains( "A" ); + Assert.Equal( Operator.Contains, Lambda.GetOperator( expression ) ); + + expression = t => t.Test2.StringValue.EndsWith( "B" ); + Assert.Equal( Operator.Ends, Lambda.GetOperator( expression ) ); + + expression = t => t.Test2.Test3.StringValue.StartsWith( "C" ); + Assert.Equal( Operator.Starts, Lambda.GetOperator( expression ) ); + } + + /// + /// 获取查询操作符 - 复杂类型 + /// + [Fact] + public void TestGetOperator_Complex() { + var test = new Sample() { StringValue = "a", Test2 = new Sample2() { StringValue = "b" } }; + + Expression> expression = t => t.StringValue == test.StringValue; + Assert.Equal( Operator.Equal, Lambda.GetOperator( expression ) ); + Expression> expression2 = t => t.StringValue != test.Test2.StringValue; + Assert.Equal( Operator.NotEqual, Lambda.GetOperator( expression2 ) ); + + Expression> expression3 = t => t.StringValue.Contains( test.StringValue ); + Assert.Equal( Operator.Contains, Lambda.GetOperator( expression3 ) ); + Expression> expression4 = t => t.StringValue.EndsWith( test.Test2.StringValue ); + Assert.Equal( Operator.Ends, Lambda.GetOperator( expression4 ) ); + } + + /// + /// 获取查询操作符 - 枚举 + /// + [Fact] + public void TestGetOperator_Enum() { + var test1 = new Sample { NullableEnumValue = EnumSample.C }; + + Expression> expression = test => test.EnumValue == EnumSample.D; + Assert.Equal( Operator.Equal, Lambda.GetOperator( expression ) ); + + expression = test => test.EnumValue == test1.NullableEnumValue; + Assert.Equal( Operator.Equal, Lambda.GetOperator( expression ) ); + + expression = test => test.NullableEnumValue == EnumSample.E; + Assert.Equal( Operator.Equal, Lambda.GetOperator( expression ) ); + + expression = test => test.NullableEnumValue == test1.NullableEnumValue; + Assert.Equal( Operator.Equal, Lambda.GetOperator( expression ) ); + } + + /// + /// 获取查询操作符 - 集合 + /// + [Fact] + public void TestGetOperator_List() { + var list = new List { "a", "b" }; + Expression> expression = t => list.Contains( t.StringValue ); + Assert.Equal( Operator.Contains, Lambda.GetOperator( expression ) ); + } + + /// + /// 获取查询操作符 - 静态成员 + /// + [Fact] + public void TestGetOperator_Static() { + Expression> expression = t => t.StringValue == Sample.StaticString; + Assert.Equal( Operator.Equal, Lambda.GetOperator( expression ) ); + + expression = t => t.StringValue == Sample.StaticSample.StringValue; + Assert.Equal( Operator.Equal, Lambda.GetOperator( expression ) ); + + expression = t => t.Test2.StringValue == Sample.StaticString; + Assert.Equal( Operator.Equal, Lambda.GetOperator( expression ) ); + + expression = t => t.Test2.StringValue == Sample.StaticSample.StringValue; + Assert.Equal( Operator.Equal, Lambda.GetOperator( expression ) ); + + expression = t => t.Test2.StringValue.Contains( Sample.StaticSample.StringValue ); + Assert.Equal( Operator.Contains, Lambda.GetOperator( expression ) ); + } + + #endregion + + #region GetParameter(获取参数) + + /// + /// 测试获取参数 + /// + [Fact] + public void TestGetParameter() { + Assert.Null( Lambda.GetParameter( null ) ); + + Expression> expression = test => test.StringValue == "A"; + Assert.Equal( "test", Lambda.GetParameter( expression ).ToString() ); + + expression = test => test.Test2.IntValue == 1; + Assert.Equal( "test", Lambda.GetParameter( expression ).ToString() ); + + expression = test => test.Test2.Test3.StringValue == "B"; + Assert.Equal( "test", Lambda.GetParameter( expression ).ToString() ); + + expression = test => test.Test2.IntValue; + Assert.Equal( "test", Lambda.GetParameter( expression ).ToString() ); + + expression = test => test.Test2.Test3.StringValue.Contains( "B" ); + Assert.Equal( "test", Lambda.GetParameter( expression ).ToString() ); + } + + #endregion + + #region GetCriteriaCount(获取查询条件个数) + + /// + /// 测试获取查询条件个数 + /// + [Fact] + public void TestGetConditionCount() { + Assert.Equal( 0, Lambda.GetConditionCount( null ) ); + + Expression> expression = test => test.StringValue == "A"; + Assert.Equal( 1, Lambda.GetConditionCount( expression ) ); + + expression = test => test.StringValue == "A" && test.StringValue == "B"; + Assert.Equal( 2, Lambda.GetConditionCount( expression ) ); + + expression = test => test.StringValue == "A" || test.StringValue == "B"; + Assert.Equal( 2, Lambda.GetConditionCount( expression ) ); + + expression = test => test.StringValue == "A" && test.StringValue == "B" || test.StringValue == "C"; + Assert.Equal( 3, Lambda.GetConditionCount( expression ) ); + + expression = test => test.Test2.StringValue == "A" && test.StringValue == "B" || test.StringValue == "C"; + Assert.Equal( 3, Lambda.GetConditionCount( expression ) ); + + expression = t => t.StringValue.Contains( "A" ); + Assert.Equal( 1, Lambda.GetConditionCount( expression ) ); + + expression = t => t.StringValue.Contains( "A" ) && t.StringValue == "A"; + Assert.Equal( 2, Lambda.GetConditionCount( expression ) ); + + expression = t => t.StringValue.Contains( "A" ) || t.Test2.StringValue == "A"; + Assert.Equal( 2, Lambda.GetConditionCount( expression ) ); + } + + #endregion + + #region GetAttribute(获取特性) + + /// + /// 测试获取特性 + /// + [Fact] + public void TestGetAttribute() { + DisplayAttribute attribute = Lambda.GetAttribute( t => t.StringValue ); + Assert.Equal( "字符串值", attribute.Name ); + } + + #endregion + + #region GetAttributes(获取特性列表) + + /// + /// 测试获取特性列表 + /// + [Fact] + public void TestGetAttributes() { + IEnumerable attributes = Lambda.GetAttributes( t => t.StringValue ); + Assert.Equal( 2, attributes.Count() ); + } + + #endregion + + #region Constant(获取常量表达式) + + /// + /// 测试获取常量表达式 + /// + [Fact] + public void TestConstant() { + var constantExpression = Lambda.Constant( 1 ); + Assert.Equal( typeof( int ), constantExpression.Type ); + } + + /// + /// 测试获取常量表达式 + /// + [Fact] + public void TestConstant_2() { + Expression> property = t => t.NullableIntValue; + var constantExpression = Lambda.Constant( 1 , property ); + Assert.Equal( typeof( int? ), constantExpression.Type ); + } + + #endregion + + #region Equal(创建等于表达式) + + /// + /// 测试创建等于表达式 + /// + [Fact] + public void TestEqual() { + Expression> expected = t => t.IntValue == 1; + Assert.Equal( expected.ToString(), Lambda.Equal( "IntValue", 1 ).ToString() ); + + Expression> expected2 = t => t.Test2.IntValue == 1; + Assert.Equal( expected2.ToString(), Lambda.Equal( "Test2.IntValue", 1 ).ToString() ); + } + + #endregion + + #region NotEqual(创建不等于表达式) + + /// + /// 测试创建不等于表达式 + /// + [Fact] + public void TestNotEqual() { + Expression> expected = t => t.IntValue != 1; + Assert.Equal( expected.ToString(), Lambda.NotEqual( "IntValue", 1 ).ToString() ); + + Expression> expected2 = t => t.Test2.IntValue != 1; + Assert.Equal( expected2.ToString(), Lambda.NotEqual( "Test2.IntValue", 1 ).ToString() ); + } + + #endregion + + #region Greater(创建大于表达式) + + /// + /// 测试创建大于表达式 + /// + [Fact] + public void TestGreater() { + Expression> expected = t => t.IntValue > 1; + Assert.Equal( expected.ToString(), Lambda.Greater( "IntValue", 1 ).ToString() ); + + Expression> expected2 = t => t.Test2.IntValue > 1; + Assert.Equal( expected2.ToString(), Lambda.Greater( "Test2.IntValue", 1 ).ToString() ); + } + + #endregion + + #region GreaterEqual(创建大于等于表达式) + + /// + /// 测试创建大于等于表达式 + /// + [Fact] + public void TestGreaterEqual() { + Expression> expected = t => t.IntValue >= 1; + Assert.Equal( expected.ToString(), Lambda.GreaterEqual( "IntValue", 1 ).ToString() ); + + Expression> expected2 = t => t.Test2.IntValue >= 1; + Assert.Equal( expected2.ToString(), Lambda.GreaterEqual( "Test2.IntValue", 1 ).ToString() ); + } + + #endregion + + #region Less(创建小于表达式) + + /// + /// 测试创建小于表达式 + /// + [Fact] + public void TestLess() { + Expression> expected = t => t.IntValue < 1; + Assert.Equal( expected.ToString(), Lambda.Less( "IntValue", 1 ).ToString() ); + + Expression> expected2 = t => t.Test2.IntValue < 1; + Assert.Equal( expected2.ToString(), Lambda.Less( "Test2.IntValue", 1 ).ToString() ); + } + + #endregion + + #region LessEqual(创建小于等于表达式) + + /// + /// 测试创建小于等于表达式 + /// + [Fact] + public void TestLessEqual() { + Expression> expected = t => t.IntValue <= 1; + Assert.Equal( expected.ToString(), Lambda.LessEqual( "IntValue", 1 ).ToString() ); + + Expression> expected2 = t => t.Test2.IntValue <= 1; + Assert.Equal( expected2.ToString(), Lambda.LessEqual( "Test2.IntValue", 1 ).ToString() ); + } + + #endregion + + #region Starts(调用StartsWith方法) + + /// + /// 测试调用StartsWith方法 + /// + [Fact] + public void TestStarts() { + Expression> expected = t => t.StringValue.StartsWith( "A" ); + Assert.Equal( expected.ToString(), Lambda.Starts( "StringValue", "A" ).ToString() ); + + Expression> expected2 = t => t.Test2.StringValue.StartsWith( "A" ); + Assert.Equal( expected2.ToString(), Lambda.Starts( "Test2.StringValue", "A" ).ToString() ); + + Expression> expected3 = t => t.Test2.Test3.StringValue.StartsWith( "A" ); + Assert.Equal( expected3.ToString(), Lambda.Starts( "Test2.Test3.StringValue", "A" ).ToString() ); + } + + #endregion + + #region Ends(调用EndsWith方法) + + /// + /// 测试调用EndsWith方法 + /// + [Fact] + public void TestEnds() { + Expression> expected = t => t.StringValue.EndsWith( "A" ); + Assert.Equal( expected.ToString(), Lambda.Ends( "StringValue", "A" ).ToString() ); + + Expression> expected2 = t => t.Test2.StringValue.EndsWith( "A" ); + Assert.Equal( expected2.ToString(), Lambda.Ends( "Test2.StringValue", "A" ).ToString() ); + + Expression> expected3 = t => t.Test2.Test3.StringValue.EndsWith( "A" ); + Assert.Equal( expected3.ToString(), Lambda.Ends( "Test2.Test3.StringValue", "A" ).ToString() ); + } + + #endregion + + #region Contains(调用Contains方法) + + /// + /// 测试调用Contains方法 + /// + [Fact] + public void TestContains() { + Expression> expected = t => t.StringValue.Contains( "A" ); + Assert.Equal( expected.ToString(), Lambda.Contains( "StringValue", "A" ).ToString() ); + + Expression> expected2 = t => t.Test2.StringValue.Contains( "A" ); + Assert.Equal( expected2.ToString(), Lambda.Contains( "Test2.StringValue", "A" ).ToString() ); + + Expression> expected3 = t => t.Test2.Test3.StringValue.Contains( "A" ); + Assert.Equal( expected3.ToString(), Lambda.Contains( "Test2.Test3.StringValue", "A" ).ToString() ); + } + + #endregion + + #region ParsePredicate(解析为谓词表达式) + + /// + /// 测试解析为谓词表达式 + /// + [Fact] + public void TestParsePredicate() { + Assert.Equal( "t => (t.StringValue == \"A\")", Lambda.ParsePredicate( "StringValue", "A", Operator.Equal ).SafeString() ); + Assert.Equal( "t => t.StringValue.Contains(\"A\")", Lambda.ParsePredicate( "StringValue", "A", Operator.Contains ).SafeString() ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Helpers/RandomTest.cs b/test/Util.Core.Tests/Helpers/RandomTest.cs new file mode 100644 index 000000000..3db8afeb7 --- /dev/null +++ b/test/Util.Core.Tests/Helpers/RandomTest.cs @@ -0,0 +1,81 @@ +using System.Collections.Generic; +using System.Linq; +using Util.Helpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Tests.Helpers; + +/// +/// 随机数操作测试 +/// +public class RandomTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _testOutputHelper; + + /// + /// 测试初始化 + /// + public RandomTest( ITestOutputHelper testOutputHelper ) { + _testOutputHelper = testOutputHelper; + } + + /// + /// 测试从集合中随机获取值 + /// + [Fact] + public void TestGetValue() { + var list = new List(); + for ( int i = 0; i < 10; i++ ) { + list.Add( i ); + } + for ( int i = 0; i < 10; i++ ) { + var a = Random.GetValue( list ); + var b = Random.GetValue( list ); + if ( a != b ) + return; + } + Assert.Fail( "fail" ); + } + + /// + /// 测试从集合中随机获取值列表 + /// + [Fact] + public void TestGetValues_1() { + var list = Enumerable.Range( 0, 10 ); + var result = Random.GetValues( list, 3 ); + Assert.Equal( 3, result.Count ); + Assert.True( result[0] >= 0 && result[0] < 10 ); + Assert.True( result[1] >= 0 && result[1] < 10 ); + Assert.True( result[2] >= 0 && result[2] < 10 ); + } + + /// + /// 测试从集合中随机获取值列表 - 获取数量大于集合数量 + /// + [Fact] + public void TestGetValues_2() { + var list = Enumerable.Range( 0, 3 ); + var result = Random.GetValues( list, 10 ); + Assert.Equal( 3, result.Count ); + Assert.True( result[0] >= 0 && result[0] < 3 ); + Assert.True( result[1] >= 0 && result[1] < 3 ); + Assert.True( result[2] >= 0 && result[2] < 3 ); + } + + /// + /// 获取随机排序的集合 + /// + [Fact] + public void TestSort() { + int[] input = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + var list = Util.Helpers.Random.Sort( input ); + var result = list.Join(); + Assert.Equal( 10, list.Distinct().Count() ); + Assert.NotEqual( "1,2,3,4,5,6,7,8,9,10", result ); + _testOutputHelper.WriteLine( result ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Helpers/ReflectionTest.cs b/test/Util.Core.Tests/Helpers/ReflectionTest.cs new file mode 100644 index 000000000..5f082fbd9 --- /dev/null +++ b/test/Util.Core.Tests/Helpers/ReflectionTest.cs @@ -0,0 +1,243 @@ +using System.Collections.Generic; +using System.Reflection; +using Util.Helpers; +using Util.Tests.Samples; +using Xunit; + +namespace Util.Tests.Helpers; + +/// +/// 测试反射操作 +/// +public class ReflectionTest { + /// + /// 测试样例 + /// + private readonly Sample _sample; + + /// + /// 测试初始化 + /// + public ReflectionTest() { + _sample = new Sample(); + } + + /// + /// 测试获取类型描述 + /// + [Fact] + public void TestGetDescription_1() { + Assert.Equal( "测试样例", Util.Helpers.Reflection.GetDescription() ); + Assert.Equal( "Sample2", Util.Helpers.Reflection.GetDescription() ); + } + + /// + /// 测试获取类型成员描述 + /// + [Fact] + public void TestGetDescription_2() { + Assert.Equal( "", Util.Helpers.Reflection.GetDescription( "X" ) ); + Assert.Equal( "A", Util.Helpers.Reflection.GetDescription( "A" ) ); + Assert.Equal( "B2", Util.Helpers.Reflection.GetDescription( "B" ) ); + Assert.Equal( "IntValue", Util.Helpers.Reflection.GetDescription( "IntValue" ) ); + } + + /// + /// 测试显示名 + /// + [Fact] + public void TestGetDisplayName() { + Assert.Equal( "", Util.Helpers.Reflection.GetDisplayName() ); + Assert.Equal( "测试样例2", Util.Helpers.Reflection.GetDisplayName() ); + } + + /// + /// 测试获取类描述或显示名 + /// + [Fact] + public void TestGetDescriptionOrDisplayName() { + Assert.Equal( "测试样例", Util.Helpers.Reflection.GetDisplayNameOrDescription() ); + Assert.Equal( "测试样例2", Util.Helpers.Reflection.GetDisplayNameOrDescription() ); + Assert.Equal( "测试样例", Util.Helpers.Reflection.GetDisplayNameOrDescription() ); + } + + /// + /// 测试是否布尔类型 + /// + [Fact] + public void TestIsBool() { + Assert.True( Util.Helpers.Reflection.IsBool( _sample.BoolValue.GetType().GetTypeInfo() ), "BoolValue GetType" ); + Assert.True( Util.Helpers.Reflection.IsBool( _sample.GetType().GetMember( "BoolValue" )[0] ), "BoolValue" ); + Assert.True( Util.Helpers.Reflection.IsBool( _sample.GetType().GetMember( "NullableBoolValue" )[0] ), "NullableBoolValue" ); + Assert.False( Util.Helpers.Reflection.IsBool( _sample.GetType().GetMember( "EnumValue" )[0] ), "EnumValue" ); + } + + /// + /// 测试是否枚举类型 + /// + [Fact] + public void TestIsEnum() { + Assert.True( Util.Helpers.Reflection.IsEnum( _sample.EnumValue.GetType().GetTypeInfo() ), "EnumValue GetType" ); + Assert.True( Util.Helpers.Reflection.IsEnum( _sample.GetType().GetMember( "EnumValue" )[0] ), "EnumValue" ); + Assert.True( Util.Helpers.Reflection.IsEnum( _sample.GetType().GetMember( "NullableEnumValue" )[0] ), "NullableEnumValue" ); + Assert.False( Util.Helpers.Reflection.IsEnum( _sample.GetType().GetMember( "BoolValue" )[0] ), "BoolValue" ); + Assert.False( Util.Helpers.Reflection.IsEnum( _sample.GetType().GetMember( "NullableBoolValue" )[0] ), "NullableBoolValue" ); + } + + /// + /// 测试是否日期类型 + /// + [Fact] + public void TestIsDate() { + Assert.True( Util.Helpers.Reflection.IsDate( _sample.DateValue.GetType().GetTypeInfo() ), "DateValue GetType" ); + Assert.True( Util.Helpers.Reflection.IsDate( _sample.GetType().GetMember( "DateValue" )[0] ), "DateValue" ); + Assert.True( Util.Helpers.Reflection.IsDate( _sample.GetType().GetMember( "NullableDateValue" )[0] ), "NullableDateValue" ); + Assert.False( Util.Helpers.Reflection.IsDate( _sample.GetType().GetMember( "EnumValue" )[0] ), "EnumValue" ); + } + + /// + /// 测试是否整型 + /// + [Fact] + public void TestIsInt() { + Assert.True( Util.Helpers.Reflection.IsInt( _sample.IntValue.GetType().GetTypeInfo() ), "IntValue GetType" ); + Assert.True( Util.Helpers.Reflection.IsInt( _sample.GetType().GetMember( "IntValue" )[0] ), "IntValue" ); + Assert.True( Util.Helpers.Reflection.IsInt( _sample.GetType().GetMember( "NullableIntValue" )[0] ), "NullableIntValue" ); + + Assert.True( Util.Helpers.Reflection.IsInt( _sample.ShortValue.GetType().GetTypeInfo() ), "ShortValue GetType" ); + Assert.True( Util.Helpers.Reflection.IsInt( _sample.GetType().GetMember( "ShortValue" )[0] ), "ShortValue" ); + Assert.True( Util.Helpers.Reflection.IsInt( _sample.GetType().GetMember( "NullableShortValue" )[0] ), "NullableShortValue" ); + + Assert.True( Util.Helpers.Reflection.IsInt( _sample.LongValue.GetType().GetTypeInfo() ), "LongValue GetType" ); + Assert.True( Util.Helpers.Reflection.IsInt( _sample.GetType().GetMember( "LongValue" )[0] ), "LongValue" ); + Assert.True( Util.Helpers.Reflection.IsInt( _sample.GetType().GetMember( "NullableLongValue" )[0] ), "NullableLongValue" ); + } + + /// + /// 测试是否数值类型 + /// + [Fact] + public void TestIsNumber() { + Assert.True( Util.Helpers.Reflection.IsNumber( _sample.DoubleValue.GetType().GetTypeInfo() ), "DoubleValue GetType" ); + Assert.True( Util.Helpers.Reflection.IsNumber( _sample.GetType().GetMember( "DoubleValue" )[0] ), "DoubleValue" ); + Assert.True( Util.Helpers.Reflection.IsNumber( _sample.GetType().GetMember( "NullableDoubleValue" )[0] ), "NullableDoubleValue" ); + + Assert.True( Util.Helpers.Reflection.IsNumber( _sample.DecimalValue.GetType().GetTypeInfo() ), "DecimalValue GetType" ); + Assert.True( Util.Helpers.Reflection.IsNumber( _sample.GetType().GetMember( "DecimalValue" )[0] ), "DecimalValue" ); + Assert.True( Util.Helpers.Reflection.IsNumber( _sample.GetType().GetMember( "NullableDecimalValue" )[0] ), "NullableDecimalValue" ); + + Assert.True( Util.Helpers.Reflection.IsNumber( _sample.FloatValue.GetType().GetTypeInfo() ), "FloatValue GetType" ); + Assert.True( Util.Helpers.Reflection.IsNumber( _sample.GetType().GetMember( "FloatValue" )[0] ), "FloatValue" ); + Assert.True( Util.Helpers.Reflection.IsNumber( _sample.GetType().GetMember( "NullableFloatValue" )[0] ), "NullableFloatValue" ); + + Assert.True( Util.Helpers.Reflection.IsNumber( _sample.IntValue.GetType().GetTypeInfo() ), "IntValue GetType" ); + Assert.True( Util.Helpers.Reflection.IsNumber( _sample.GetType().GetMember( "IntValue" )[0] ), "IntValue" ); + Assert.True( Util.Helpers.Reflection.IsNumber( _sample.GetType().GetMember( "NullableIntValue" )[0] ), "NullableIntValue" ); + } + + /// + /// 测试获取直接接口列表 + /// + [Fact] + public void TestGetDirectInterfaces() { + var interfaceTypes = Util.Helpers.Reflection.GetDirectInterfaceTypes(); + Assert.Equal( 2, interfaceTypes.Count ); + Assert.True( interfaceTypes.Exists( t => t.Name == "ITestSample" ) ); + Assert.True( interfaceTypes.Exists( t => t.Name == "ITestSample5" ) ); + } + + /// + /// 测试获取直接接口列表 - 一个基接口 + /// + [Fact] + public void TestGetDirectInterfaces_2() { + var interfaceTypes = Util.Helpers.Reflection.GetDirectInterfaceTypes( typeof( ITestSample3 ) ); + Assert.Single( interfaceTypes ); + Assert.True( interfaceTypes.Exists( t => t.Name == "ITestSample" ) ); + } + + /// + /// 测试获取直接接口列表 - 两个基接口 + /// + [Fact] + public void TestGetDirectInterfaces_3() { + var interfaceTypes = Util.Helpers.Reflection.GetDirectInterfaceTypes( typeof( ITestSample4 ), typeof( ITestSample3 ) ); + Assert.Equal( 2, interfaceTypes.Count ); + Assert.True( interfaceTypes.Exists( t => t.Name == "ITestSample" ) ); + Assert.True( interfaceTypes.Exists( t => t.Name == "ITestSample5" ) ); + } + + /// + /// 测试获取接口列表 + /// + [Fact] + public void TestGetInterfaces_1() { + var interfaceTypes = Util.Helpers.Reflection.GetInterfaceTypes(); + Assert.Equal( 5, interfaceTypes.Count ); + } + + /// + /// 测试获取接口列表 - 一个基接口 + /// + [Fact] + public void TestGetInterfaces_2() { + var interfaceTypes = Util.Helpers.Reflection.GetInterfaceTypes( typeof( ITestSample3 ) ); + Assert.Equal( 2,interfaceTypes.Count ); + Assert.True( interfaceTypes.Exists( t => t.Name == "ITestSample" ) ); + Assert.True( interfaceTypes.Exists( t => t.Name == "ITestSample2" ) ); + } + + /// + /// 测试获取接口列表 - 两个基接口 + /// + [Fact] + public void TestGetInterfaces_3() { + var interfaceTypes = Util.Helpers.Reflection.GetInterfaceTypes( typeof( ITestSample4 ), typeof( ITestSample3 ) ); + Assert.Equal( 3, interfaceTypes.Count ); + } + + /// + /// 测试是否集合 + /// + [Fact] + public void TestIsCollection() { + Assert.True( Util.Helpers.Reflection.IsCollection( _sample.StringArray.GetType() ) ); + } + + /// + /// 测试是否泛型集合 + /// + [Fact] + public void TestIsGenericCollection() { + Assert.True( Util.Helpers.Reflection.IsGenericCollection( _sample.StringList.GetType() ) ); + } + + /// + /// 测试获取元素类型 + /// + [Fact] + public void TestGetElementType_1() { + Sample sample = new Sample(); + Assert.Equal( typeof( Sample ), Reflection.GetElementType( sample.GetType() ) ); + } + + /// + /// 获取元素类型 - 数组 + /// + [Fact] + public void TestGetElementType_2() { + var list = new[] { new Sample() }; + var type = list.GetType(); + Assert.Equal( typeof( Sample ), Reflection.GetElementType( type ) ); + } + + /// + /// 获取元素类型 - 集合 + /// + [Fact] + public void TestGetElementType_3() { + var list = new List { new Sample() }; + var type = list.GetType(); + Assert.Equal( typeof( Sample ), Reflection.GetElementType( type ) ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Helpers/RegexTest.cs b/test/Util.Core.Tests/Helpers/RegexTest.cs new file mode 100644 index 000000000..62477042e --- /dev/null +++ b/test/Util.Core.Tests/Helpers/RegexTest.cs @@ -0,0 +1,39 @@ +using Xunit; + +namespace Util.Tests.Helpers; + +/// +/// 正则操作测试 +/// +public class RegexTest { + /// + /// 测试获取值 + /// + /// 输入字符串 + /// 模式字符串 + /// 结果模式字符串 + /// 结果 + [Theory] + [InlineData( "", "", "", "" )] + [InlineData( "123", "a", "", "" )] + [InlineData( "123", @"\d", "", "1" )] + [InlineData( "123abc456", @"\d+([a-z]+\d+)", "$1", "abc456" )] + [InlineData( "123abc456", @"\d+([a-z]\d+)", "$1", "" )] + public void TestGetValue( string input, string pattern,string resultPattern, string result ) { + Assert.Equal( result, Util.Helpers.Regex.GetValue( input, pattern, resultPattern ) ); + } + + /// + /// 测试获取值集合 + /// + [Fact] + public void TestGetValues() { + Assert.Empty( Util.Helpers.Regex.GetValues( "", "",null ) ); + Assert.Empty( Util.Helpers.Regex.GetValues( "123abc456", @"\d{5}",new []{"$1"} ) ); + Assert.Equal( "123", Util.Helpers.Regex.GetValues( "123abc456", @"(\d*)", new[] { "$1" } )["$1"] ); + Assert.Equal( "abc", Util.Helpers.Regex.GetValues( "123abc456", @"\d*([a-z]*)\d*", new[] { "$1" } )["$1"] ); + Assert.Equal( "123", Util.Helpers.Regex.GetValues( "123abc456", @"(\d*)([a-z]*)(\d*)", new[] { "$1", "$2", "$3" } )["$1"] ); + Assert.Equal( "abc", Util.Helpers.Regex.GetValues( "123abc456", @"(\d*)([a-z]*)(\d*)", new[] { "$1", "$2", "$3" } )["$2"] ); + Assert.Equal( "456", Util.Helpers.Regex.GetValues( "123abc456", @"(\d*)([a-z]*)(\d*)", new[] { "$1", "$2", "$3" } )["$3"] ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Helpers/StringTest.cs b/test/Util.Core.Tests/Helpers/StringTest.cs new file mode 100644 index 000000000..b2b43bf42 --- /dev/null +++ b/test/Util.Core.Tests/Helpers/StringTest.cs @@ -0,0 +1,379 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; +using String = Util.Helpers.String; + +namespace Util.Tests.Helpers; + +/// +/// 测试字符串操作 - 工具库 +/// +public class StringTest { + + #region Join + + /// + /// 测试将集合连接为带分隔符的字符串 + /// + [Fact] + public void TestJoin() { + Assert.Equal( "1,2,3", String.Join( new List { 1, 2, 3 } ) ); + Assert.Equal( "'1','2','3'", String.Join( new List { 1, 2, 3 }, "'" ) ); + Assert.Equal( "123", String.Join( new List { 1, 2, 3 }, "", "" ) ); + Assert.Equal( "\"1\",\"2\",\"3\"", String.Join( new List { 1, 2, 3 }, "\"" ) ); + Assert.Equal( "1 2 3", String.Join( new List { 1, 2, 3 }, "", " " ) ); + Assert.Equal( "1;2;3", String.Join( new List { 1, 2, 3 }, "", ";" ) ); + Assert.Equal( "1,2,3", String.Join( new List { "1", "2", "3" } ) ); + Assert.Equal( "'1','2','3'", String.Join( new List { "1", "2", "3" }, "'" ) ); + + var list = new List { + new( "83B0233C-A24F-49FD-8083-1337209EBC9A" ), + new( "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" ) + }; + Assert.Equal( "83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A".ToLower(), String.Join( list ) ); + Assert.Equal( "'83B0233C-A24F-49FD-8083-1337209EBC9A','EAB523C6-2FE7-47BE-89D5-C6D440C3033A'".ToLower(), String.Join( list, "'" ) ); + } + + #endregion + + #region FirstLowerCase + + /// + /// 首字母小写 + /// + [Theory] + [InlineData( null, "" )] + [InlineData( "", "" )] + [InlineData( " ", "" )] + [InlineData( "a", "a" )] + [InlineData( "A", "a" )] + [InlineData( "Ab", "ab" )] + [InlineData( "AB", "aB" )] + [InlineData( "Abc", "abc" )] + public void TestFirstLowerCase( string value, string result ) { + Assert.Equal( result, String.FirstLowerCase( value ) ); + } + + #endregion + + #region FirstUpperCase + + /// + /// 首字母大写 + /// + [Theory] + [InlineData( null, "" )] + [InlineData( "", "" )] + [InlineData( " ", "" )] + [InlineData( "a", "A" )] + [InlineData( "A", "A" )] + [InlineData( "Ab", "Ab" )] + [InlineData( "aB", "AB" )] + [InlineData( "abc", "Abc" )] + public void TestFirstUpperCase( string value, string result ) { + Assert.Equal( result, String.FirstUpperCase( value ) ); + } + + #endregion + + #region RemoveStart + + /// + /// 测试移除起始字符串 + /// + [Theory] + [InlineData( null, null, "" )] + [InlineData( null, "a", "" )] + [InlineData( "", "", "" )] + [InlineData( "a", "b", "a" )] + [InlineData( "ab", "b", "ab" )] + [InlineData( "ab", "a", "b" )] + [InlineData( "abc", "ab", "c" )] + [InlineData( "abc", "abc", "" )] + [InlineData( "ab", "abc", "ab" )] + [InlineData( "a.cs.cshtml", "a.cs", ".cshtml" )] + [InlineData( "\r\na", "\r\n", "a" )] + public void TestRemoveStart( string value, string removeValue, string result ) { + Assert.Equal( result, String.RemoveStart( value, removeValue ) ); + } + + /// + /// 测试移除起始字符串 - 大小写 + /// + [Fact] + public void TestRemoveStart_2() { + Assert.Equal( ".cs", String.RemoveStart( "ab.cs", "Ab" ) ); + Assert.Equal( "ab.cs", String.RemoveStart( "ab.cs", "Ab", false ) ); + } + + /// + /// 测试移除起始字符串 + /// + [Theory] + [InlineData( null, null, null )] + [InlineData( null, "a", null )] + public void TestRemoveStart_StringBuilder( StringBuilder value, string removeValue, string result ) { + Assert.Equal( result, Util.Helpers.String.RemoveStart( value, removeValue )?.ToString() ); + } + + /// + /// 测试移除起始字符串 + /// + [Theory] + [InlineData( null, null, null )] + [InlineData( null, "a", null )] + [InlineData( "", "", null )] + [InlineData( "a", "b", "a" )] + [InlineData( "ab", "b", "ab" )] + [InlineData( "ab", "a", "b" )] + [InlineData( "abc", "ab", "c" )] + [InlineData( "abc", "Ab", "abc" )] + [InlineData( "abc", "abc", "" )] + [InlineData( "ab", "abc", "ab" )] + [InlineData( "a.cs.cshtml", "a.cs", ".cshtml" )] + [InlineData( "\r\na", "\r\n", "a" )] + public void TestRemoveStart_StringBuilder_2( string value, string removeValue, string result ) { + var builder = new StringBuilder( value ); + Assert.Equal( result, Util.Helpers.String.RemoveStart( builder, removeValue )?.ToString() ); + } + + #endregion + + #region RemoveEnd + + /// + /// 测试移除末尾字符串 + /// + [Theory] + [InlineData( null, null, "" )] + [InlineData( null, "a", "" )] + [InlineData( "", "", "" )] + [InlineData( "a", "b", "a" )] + [InlineData( "ab", "a", "ab" )] + [InlineData( "ab", "b", "a" )] + [InlineData( "abc", "abc", "" )] + [InlineData( "bc", "abc", "bc" )] + [InlineData( "ab", "abc", "ab" )] + [InlineData( "a.cs.cshtml", ".cshtml", "a.cs" )] + [InlineData( "a\r\n", "\r\n", "a" )] + public void TestRemoveEnd( string value, string removeValue, string result ) { + Assert.Equal( result, String.RemoveEnd( value, removeValue, false ) ); + } + + /// + /// 测试移除末尾字符串 - 大小写 + /// + [Fact] + public void TestRemoveEnd_2() { + Assert.Equal( "ab", String.RemoveEnd( "ab.cs", ".Cs" ) ); + Assert.Equal( "ab.cs", String.RemoveEnd( "ab.cs", ".Cs", false ) ); + } + + /// + /// 测试移除末尾字符串 + /// + [Theory] + [InlineData( null, null, null )] + [InlineData( null, "a", null )] + public void TestRemoveEnd_StringBuilder( StringBuilder value, string removeValue, string result ) { + Assert.Equal( result, Util.Helpers.String.RemoveEnd( value, removeValue )?.ToString() ); + } + + /// + /// 测试移除末尾字符串 + /// + [Theory] + [InlineData( null, null, null )] + [InlineData( null, "a", null )] + [InlineData( "", "", null )] + [InlineData( "a", "b", "a" )] + [InlineData( "ab", "a", "ab" )] + [InlineData( "ab", "b", "a" )] + [InlineData( "abc", "abc", "" )] + [InlineData( "bc", "abc", "bc" )] + [InlineData( "ab", "abc", "ab" )] + [InlineData( "a.cs.cshtml", ".cshtml", "a.cs" )] + [InlineData( "a\r\n", "\r\n", "a" )] + public void TestRemoveEnd_StringBuilder_2( string value, string removeValue, string result ) { + var builder = new StringBuilder( value ); + Assert.Equal( result, Util.Helpers.String.RemoveEnd( builder, removeValue )?.ToString() ); + } + + #endregion + + #region PinYin + + /// + /// 测试获取拼音简码 + /// + [Theory] + [InlineData( null, "" )] + [InlineData( "", "" )] + [InlineData( "中国", "zg" )] + [InlineData( "a1宝藏b2", "a1bcb2" )] + [InlineData( "饕餮", "tt" )] + [InlineData( "爩", "y" )] + public void TestPinYin( string input, string result ) { + Assert.Equal( result, String.PinYin( input ) ); + } + + #endregion + + #region Extract + + /// + /// 测试提取字符串中的变量值 - 原始值为空 + /// + [Fact] + public void TestExtract_1() { + var result = String.Extract( "", "a" ); + Assert.Empty( result ); + } + + /// + /// 测试提取字符串中的变量值 - 格式字符串为空 + /// + [Fact] + public void TestExtract_2() { + var result = String.Extract( "a", "" ); + Assert.Empty( result ); + } + + /// + /// 测试提取字符串中的变量值 - 格式字符串未包含{} + /// + [Fact] + public void TestExtract_3() { + var result = String.Extract( "a", "a" ); + Assert.Empty( result ); + } + + /// + /// 测试提取字符串中的变量值 - 格式字符串未包含{} + /// + [Fact] + public void TestExtract_4() { + var result = String.Extract( "a", "{value}" ); + Assert.Single( result ); + Assert.Equal( "a", result["value"] ); + } + + /// + /// 测试提取字符串中的变量值 - 值前后有空格 + /// + [Fact] + public void TestExtract_5() { + var result = String.Extract( " a ", "{value}" ); + Assert.Single( result ); + Assert.Equal( "a", result["value"] ); + } + + /// + /// 测试提取字符串中的变量值 - 格式字符串前后有空格 + /// + [Fact] + public void TestExtract_6() { + var result = String.Extract( "a", " {value} " ); + Assert.Single( result ); + Assert.Equal( "a", result["value"] ); + } + + /// + /// 测试提取字符串中的变量值 - 格式字符串左侧有文本 - 忽略大小写 + /// + [Fact] + public void TestExtract_7() { + var result = String.Extract( "Hello,World", "hello,{value}" ); + Assert.Single( result ); + Assert.Equal( "World", result["value"] ); + } + + /// + /// 测试提取字符串中的变量值 - 格式字符串右侧有文本 - 忽略大小写 + /// + [Fact] + public void TestExtract_8() { + var result = String.Extract( "Hello,World", "{value},world" ); + Assert.Single( result ); + Assert.Equal( "Hello", result["value"] ); + } + + /// + /// 测试提取字符串中的变量值 - 格式字符串左右侧都有文本 + /// + [Fact] + public void TestExtract_9() { + var result = String.Extract( "Hello,Util,World", "Hello,{value},world" ); + Assert.Single( result ); + Assert.Equal( "Util", result["value"] ); + } + + /// + /// 测试提取字符串中的变量值 - 格式字符串包含两个变量 - 左侧文本 + /// + [Fact] + public void TestExtract_10() { + var result = String.Extract( "Hello,Util,World", "Hello,{a},{b}" ); + Assert.Equal( 2, result.Count ); + Assert.Equal( "Util", result["a"] ); + Assert.Equal( "World", result["b"] ); + } + + /// + /// 测试提取字符串中的变量值 - 格式字符串包含两个变量 - 右侧文本 + /// + [Fact] + public void TestExtract_11() { + var result = String.Extract( "Hello,Util,World", "{a},{b},World" ); + Assert.Equal( 2, result.Count ); + Assert.Equal( "Hello", result["a"] ); + Assert.Equal( "Util", result["b"] ); + } + + /// + /// 测试提取字符串中的变量值 - 格式字符串包含两个变量 - 重复文本 + /// + [Fact] + public void TestExtract_12() { + var result = String.Extract( "Hello,Util,Hello,Test,Hello,World", "Hello,{a},Hello,{b},Hello,World" ); + Assert.Equal( 2, result.Count ); + Assert.Equal( "Util", result["a"] ); + Assert.Equal( "Test", result["b"] ); + } + + /// + /// 测试提取字符串中的变量值 - 没有分隔符 + /// + [Fact] + public void TestExtract_13() { + var result = String.Extract( "acababcabcd", "a{b}c{d}" ); + Assert.Equal( 2, result.Count ); + Assert.Equal( "cabab", result["b"] ); + Assert.Equal( "abcd", result["d"] ); + } + + /// + /// 测试提取字符串中的变量值 - 未匹配到变量b + /// + [Fact] + public void TestExtract_14() { + var result = String.Extract( "Hello,Util,World", "Hello,{a},{b},World" ); + Assert.Equal( 2, result.Count ); + Assert.Equal( "Util", result["a"] ); + Assert.Equal( "", result["b"] ); + } + + /// + /// 测试提取字符串中的变量值 - 多个括号 + /// + [Fact] + public void TestExtract_15() { + var result = String.Extract( "Hello,Util,Hello,Test,Hello,World", "Hello,{{a}},Hello,{{{b}}},Hello,World" ); + Assert.Equal( 2, result.Count ); + Assert.Equal( "Util", result["a"] ); + Assert.Equal( "Test", result["b"] ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Helpers/TimeTest.cs b/test/Util.Core.Tests/Helpers/TimeTest.cs new file mode 100644 index 000000000..296ce7a3e --- /dev/null +++ b/test/Util.Core.Tests/Helpers/TimeTest.cs @@ -0,0 +1,239 @@ +using System; +using Util.Helpers; +using Xunit; + +namespace Util.Tests.Helpers; + +/// +/// 时间操作测试 +/// +public class TimeTest : IDisposable { + + #region 测试初始化 + + /// + /// 日期格式 + /// + private static readonly string _dateFormat = "yyyy-MM-dd HH:mm:ss"; + /// + /// 测试日期时间字符串,2012-12-12 12:12:12 + /// + private static readonly string _testDateString = "2012-12-12 12:12:12"; + /// + /// 测试日期时间,2012-12-12 12:12:12 + /// + private static readonly DateTime _testDate = DateTime.Parse( _testDateString ); + /// + /// 本地日期时间,2012-12-12 12:12:12 + /// + private static readonly DateTime _localDate = new( 2012, 12, 12, 12, 12, 12, DateTimeKind.Local ); + /// + /// 本地日期时间,2012-12-12 20:12:12 + /// + private static readonly DateTime _localDate2 = new( 2012, 12, 12, 20, 12, 12, DateTimeKind.Local ); + /// + /// utc日期时间,2012-12-12 4:12:12 + /// + private static readonly DateTime _utcDate = new( 2012, 12, 12, 4, 12, 12, DateTimeKind.Utc ); + /// + /// utc日期时间,2012-12-12 12:12:12 + /// + private static readonly DateTime _utcDate2 = new( 2012, 12, 12, 12, 12, 12, DateTimeKind.Utc ); + /// + /// 未指定日期时间,2012-12-12 12:12:12 + /// + private static readonly DateTime _unspecifiedDate = new( 2012, 12, 12, 12, 12, 12, DateTimeKind.Unspecified ); + + #endregion + + #region 测试清理 + + /// + /// 测试清理 + /// + public void Dispose() { + Time.Reset(); + } + + #endregion + + #region 辅助方法 + + /// + /// 相等断言 + /// + /// 预期日期字符串 + /// 实际日期 + private void AssertEqual( string expected,DateTime actual ) { + Assert.Equal( expected, actual.ToString( _dateFormat ) ); + } + + /// + /// 不相等断言 + /// + /// 预期日期字符串 + /// 实际日期 + private void AssertNotEqual( string expected, DateTime actual ) { + Assert.NotEqual( expected, actual.ToString( _dateFormat ) ); + } + + #endregion + + #region SetTime + + /// + /// 测试设置时间 + /// + [Fact] + public void TestSetTime() { + Time.SetTime( _testDate ); + AssertEqual( _testDateString, Time.Now ); + Time.Reset(); + AssertNotEqual( _testDateString, Time.Now ); + } + + #endregion + + #region UseUtc + + /// + /// 测试设置Utc日期 + /// + [Fact] + public void TestUseUtc() { + Time.UseUtc(); + Assert.Equal( DateTime.UtcNow.ToString( _dateFormat ), Time.Now.ToString( _dateFormat ) ); + Time.UseUtc( false ); + Assert.Equal( DateTime.Now.ToString( _dateFormat ), Time.Now.ToString( _dateFormat ) ); + } + + #endregion + + #region Normalize + + /// + /// 测试本地日期转换为本地日期 + /// + [Fact] + public void TestNormalize_1() { + //使用本地日期 + Time.UseUtc( false ); + + //转换本地日期为本地日期 + var result = Time.Normalize( _localDate ); + + //验证 + AssertEqual( _testDateString, result ); + Assert.Equal( DateTimeKind.Local, result.Kind ); + } + + /// + /// 测试Utc日期转换为本地日期 + /// + [Fact] + public void TestNormalize_2() { + //使用本地日期 + Time.UseUtc( false ); + + //转换Utc日期为本地日期 + var result = Time.Normalize( _utcDate ); + + //验证 + AssertEqual( _testDateString, result ); + Assert.Equal( DateTimeKind.Local, result.Kind ); + } + + /// + /// 测试本地日期转换为Utc日期 + /// + [Fact] + public void TestNormalize_3() { + //使用Utc日期 + Time.UseUtc(); + + //转换本地日期为Utc日期 + var result = Time.Normalize( _localDate2 ); + + //验证 + AssertEqual( _testDateString, result ); + Assert.Equal( DateTimeKind.Utc, result.Kind ); + } + + /// + /// 测试Utc日期转换为Utc日期 + /// + [Fact] + public void TestNormalize_4() { + //使用Utc日期 + Time.UseUtc(); + + //转换Utc日期为Utc日期 + var result = Time.Normalize( _utcDate2 ); + + //验证 + AssertEqual( _testDateString, result ); + Assert.Equal( DateTimeKind.Utc, result.Kind ); + } + + /// + /// 测试未指定日期转换为本地日期 + /// + [Fact] + public void TestNormalize_5() { + //使用本地日期 + Time.UseUtc( false ); + + //转换本地日期为本地日期 + var result = Time.Normalize( _unspecifiedDate ); + + //验证 + AssertEqual( _testDateString, result ); + Assert.Equal( DateTimeKind.Local, result.Kind ); + } + + /// + /// 测试未指定日期转换为Utc日期 + /// + [Fact] + public void TestNormalize_6() { + //使用Utc日期 + Time.UseUtc(); + + //转换本地日期为本地日期 + var result = Time.Normalize( _unspecifiedDate ); + + //验证 + Assert.Equal( _utcDate, result ); + Assert.Equal( DateTimeKind.Utc, result.Kind ); + } + + #endregion + + #region GetUnixTimestamp + + /// + /// 测试获取Unix时间戳 + /// + [Fact] + public void TestGetUnixTimestamp() { + Assert.Equal( 15132, Time.GetUnixTimestamp( new DateTime( 1970, 01, 01, 12, 12, 12 ) ) ); + Assert.Equal( 976594332, Time.GetUnixTimestamp( new DateTime( 2000, 12, 12, 12, 12, 12 ) ) ); + Assert.Equal( 1392668699, Time.GetUnixTimestamp( new DateTime( 2014, 02, 18, 04, 24, 59 ) ) ); + } + + #endregion + + #region GetTimeFromUnixTimestamp + + /// + /// 测试从Unix时间戳获取时间 + /// + [Fact] + public void TestGetTimeFromUnixTimestamp() { + Assert.Equal( new DateTime( 1970, 01, 01, 12, 12, 12 ), Time.GetTimeFromUnixTimestamp( 15132 ) ); + Assert.Equal( new DateTime( 2000, 12, 12, 12, 12, 12 ), Time.GetTimeFromUnixTimestamp( 976594332 ) ); + Assert.Equal( new DateTime( 2014, 02, 18, 04, 24, 59 ), Time.GetTimeFromUnixTimestamp( 1392668699 ) ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Helpers/UrlTest.cs b/test/Util.Core.Tests/Helpers/UrlTest.cs new file mode 100644 index 000000000..7aed059f1 --- /dev/null +++ b/test/Util.Core.Tests/Helpers/UrlTest.cs @@ -0,0 +1,84 @@ +using Util.Helpers; +using Xunit; + +namespace Util.Tests.Helpers; + +/// +/// Url测试 +/// +public class UrlTest { + /// + /// 测试连接路径 + /// + [Theory] + [InlineData( null, null, "" )] + [InlineData( "", "", "" )] + [InlineData( "a", "", "a" )] + [InlineData( "", "a", "a" )] + [InlineData( "a", "b", "a/b" )] + [InlineData( "a/", "b", "a/b" )] + [InlineData( "a", "/b", "a/b" )] + [InlineData( "a", "\\b", "a/b" )] + [InlineData( "/a", "b", "/a/b" )] + [InlineData( "a", "b/", "a/b/" )] + [InlineData( "/a/", "/b/", "/a/b/" )] + public void TestJoinPath( string path1, string path2, string result ) { + Assert.Equal( result, Url.JoinPath( path1, path2 ) ); + } + + /// + /// 测试连接路径 - 上级路径带./ + /// + [Fact] + public void TestJoinPath_2() { + Assert.Equal( "a/b", Url.JoinPath( "./a", "b" ) ); + } + + /// + /// 测试连接路径 - 下级路径带./ + /// + [Fact] + public void TestJoinPath_3() { + Assert.Equal( "a/b", Url.JoinPath( "a", "./b" ) ); + } + + /// + /// 测试连接路径 - 下级路径带../ + /// + [Fact] + public void TestJoinPath_4() { + Assert.Equal( "a/c", Url.JoinPath( "a/b", "../c" ) ); + } + + /// + /// 测试连接路径 - http打头 + /// + [Fact] + public void TestJoinPath_5() { + Assert.Equal( "http://a.com/a/c", Url.JoinPath( "http://a.com", "a/b", "../c" ) ); + } + + /// + /// 测试连接路径 - https打头 + /// + [Fact] + public void TestJoinPath_6() { + Assert.Equal( "https://a.com/a/c", Url.JoinPath( "https://a.com/", "a/b/", "../c" ) ); + } + + /// + /// 测试连接路径 - https打头,带..路径 + /// + [Fact] + public void TestJoinPath_7() { + Assert.Equal( "https://a.com/a/c/D/F/g", Url.JoinPath( "Https://a.com/a/b/", "../c/D/e/", "../F/g" ) ); + } + + /// + /// 测试连接路径 - https打头,带..路径 + /// + [Fact] + public void TestJoinPath_8() { + Assert.Equal( "https://a.com/view/a", Url.JoinPath( "https://a.com", "view", "a" ) ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Helpers/ValidationTest.cs b/test/Util.Core.Tests/Helpers/ValidationTest.cs new file mode 100644 index 000000000..3bf5610eb --- /dev/null +++ b/test/Util.Core.Tests/Helpers/ValidationTest.cs @@ -0,0 +1,21 @@ +using Util.Helpers; +using Xunit; + +namespace Util.Tests.Helpers; + +/// +/// 验证操作测试 +/// +public class ValidationTest { + /// + /// 是否数字 + /// + [Fact] + public void TestIsNumber() { + Assert.False( Validation.IsNumber( "" ) ); + Assert.True( Validation.IsNumber( "1" ) ); + Assert.False( Validation.IsNumber( "1a" ) ); + Assert.True( Validation.IsNumber( "100.04" ) ); + Assert.False( Validation.IsNumber( "100a.01" ) ); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Samples/EnumSample.cs b/test/Util.Core.Tests/Samples/EnumSample.cs new file mode 100644 index 000000000..f9f7c9380 --- /dev/null +++ b/test/Util.Core.Tests/Samples/EnumSample.cs @@ -0,0 +1,79 @@ +using System.ComponentModel; + +namespace Util.Tests.Samples; + +/// +/// 枚举测试样例 +/// +public enum EnumSample { + A = 1, + [Description( "B2" )] + B = 2, + [Description( "C3" )] + C = 3, + [Description( "D4" )] + D = 4, + [Description( "E5" )] + E = 5 +} + +/// +/// 枚举测试样例2 +/// +public enum HttpMethod : byte { + /// + /// This API supports framework infrastructure and is not intended to be used + /// directly from application code. + /// + Get = 0, + /// + /// This API supports framework infrastructure and is not intended to be used + /// directly from application code. + /// + Put = 1, + /// + /// This API supports framework infrastructure and is not intended to be used + /// directly from application code. + /// + Delete = 2, + /// + /// This API supports framework infrastructure and is not intended to be used + /// directly from application code. + /// + Post = 3, + /// + /// This API supports framework infrastructure and is not intended to be used + /// directly from application code. + /// + Head = 4, + /// + /// This API supports framework infrastructure and is not intended to be used + /// directly from application code. + /// + Trace = 5, + /// + /// This API supports framework infrastructure and is not intended to be used + /// directly from application code. + /// + Patch = 6, + /// + /// This API supports framework infrastructure and is not intended to be used + /// directly from application code. + /// + Connect = 7, + /// + /// This API supports framework infrastructure and is not intended to be used + /// directly from application code. + /// + Options = 8, + /// + /// This API supports framework infrastructure and is not intended to be used + /// directly from application code. + /// + Custom = 9, + /// + /// This API supports framework infrastructure and is not intended to be used + /// directly from application code. + /// + None = 255, // 0xFF +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Samples/JsonTestSample.cs b/test/Util.Core.Tests/Samples/JsonTestSample.cs new file mode 100644 index 000000000..ca0a9ea5c --- /dev/null +++ b/test/Util.Core.Tests/Samples/JsonTestSample.cs @@ -0,0 +1,133 @@ +using System; +using System.Text.Json.Serialization; + +namespace Util.Tests.Samples; + +public class A { + public string Name { get; set; } + public B B { get; set; } +} + +public class B { + public string Name { get; set; } + public C C { get; set; } +} + +public class C { + public string Name { get; set; } + public A A { get; set; } +} + +/// +/// Json测试样例 +/// +public class JsonTestSample : IJsonTestSample { + /// + /// 名称,测试公共属性,且首字母大写 + /// + public string Name { get; set; } + /// + /// 私有属性,应被忽略 + /// + private string A { get; set; } + /// + /// 昵称,用来测试小写 + /// + public string nickname { get; set; } + /// + /// 用于测试JsonPropertyName特性 + /// + [JsonPropertyName("firstName")] + public string FirstName { get; set; } + /// + /// 测试null + /// + public object Value { get; set; } + /// + /// 日期 + /// + public DateTime? Date { get; set; } + /// + /// Utc日期 + /// + public DateTime? UtcDate { get; set; } + /// + /// 测试整型,不添加引号 + /// + public int Age { get; set; } + /// + /// 测试布尔型 + /// + public bool IsShow { get; set; } + /// + /// 测试枚举 + /// + public TestEnum Enum { get; set; } + /// + /// 测试可空枚举 + /// + public TestEnum? NullableEnum { get; set; } + + /// + /// 创建样例 + /// + public static JsonTestSample Create() { + return new JsonTestSample() { + Name = "a", + A = "1", + nickname = "b", + FirstName = "c", + Value = null, + Date = new DateTime( 2012,12,12,20,12,12, DateTimeKind.Local ), + UtcDate = new DateTime( 2012, 12, 12, 12, 12, 12,DateTimeKind.Utc ), + Age = 1, + IsShow = true + }; + } + + /// + /// 创建样例 - 转成接口 + /// + public static IJsonTestSample CreateToInterface() { + return Create(); + } +} + +/// +/// Json测试样例 +/// +public class JsonTestSample2 { + /// + /// 测试long + /// + public long Long { get; set; } + /// + /// 测试long? + /// + public long? NullableLong { get; set; } +} + +/// +/// Json测试样例 +/// +public interface IJsonTestSample { + /// + /// 名称,测试公共属性,且首字母大写 + /// + public string Name { get; set; } +} + +/// +/// 测试枚举 +/// +public enum TestEnum { + Test1 = 1, + Test2 = 2 +} + +/// +/// Json测试样例 +/// +public class JsonTestSample3 { + public string Name { get; set; } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Samples/Sample.cs b/test/Util.Core.Tests/Samples/Sample.cs new file mode 100644 index 000000000..49916b021 --- /dev/null +++ b/test/Util.Core.Tests/Samples/Sample.cs @@ -0,0 +1,200 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace Util.Tests.Samples; + +public interface ISample { +} + +/// +/// 测试样例副本 +/// +public class SampleCopy : ISample { +} + +/// +/// 测试样例 +/// +[Description( "测试样例" )] +public class Sample : ISample { + /// + /// 初始化测试样例 + /// + public Sample() { + StringList = new List(); + StringArray = StringList.ToArray(); + } + + /// + /// 测试值 + /// + [Display( Name = "测试值" )] + [Required( ErrorMessage = "测试值不能为空" )] + [StringLength( 20, ErrorMessage = "测试值长度不能超过20" )] + [EmailAddress] + [Url] + public string TestValue { get; set; } + /// + /// 电子邮件验证 + /// + [EmailAddress( ErrorMessage = "电子邮件不正确" )] + public string Email { get; set; } + /// + /// 网址验证 + /// + [Url( ErrorMessage = "网址不正确" )] + public string Url { get; set; } + /// + /// 最大长度 + /// + [MaxLength( 2, ErrorMessage = "最大长度是2" )] + public string MaxLengthValue { get; set; } + /// + /// 长度验证 + /// + [StringLength( 3, MinimumLength = 2, ErrorMessage = "最大长度是3,最小长度是2" )] + public string StringLengthValue { get; set; } + /// + /// DisplayName + /// + [DisplayName( "DisplayNameValue值" )] + public string DisplayNameValue { get; set; } + /// + /// Display + /// + [Display( Name = "DisplayValue值" )] + public string DisplayValue { get; set; } + /// + /// string值 + /// + [Display( Name = "字符串值" )] + [StringLength( 20, ErrorMessage = "长度不能超过20" )] + [Required( ErrorMessage = "不能为空" )] + public string StringValue { get; set; } + /// + /// decimal值 + /// + public decimal DecimalValue { get; set; } + /// + /// 可空decimal值 + /// + public decimal? NullableDecimalValue { get; set; } + /// + /// float值 + /// + public float FloatValue { get; set; } + /// + /// 可空float值 + /// + public float? NullableFloatValue { get; set; } + /// + /// double值 + /// + public double DoubleValue { get; set; } + /// + /// 可空double值 + /// + public double? NullableDoubleValue { get; set; } + /// + /// bool值 + /// + public bool BoolValue { get; set; } + /// + /// 可空bool值 + /// + public bool? NullableBoolValue { get; set; } + /// + /// Enum值 + /// + public EnumSample EnumValue { get; set; } + /// + /// 可空Enum值 + /// + public EnumSample? NullableEnumValue { get; set; } + /// + /// DateTime值 + /// + public DateTime DateValue { get; set; } + /// + /// 可空DateTime值 + /// + public DateTime? NullableDateValue { get; set; } + /// + /// int值 + /// + [Description( "IntValue" )] + public int IntValue { get; set; } + /// + /// 可空int值 + /// + public int? NullableIntValue { get; set; } + /// + /// short值 + /// + public short ShortValue { get; set; } + /// + /// 可空short值 + /// + public short? NullableShortValue { get; set; } + /// + /// long值 + /// + public long LongValue { get; set; } + /// + /// 可空long值 + /// + public long? NullableLongValue { get; set; } + /// + /// 字符串列表 + /// + public List StringList { get; set; } + /// + /// 字符串数据 + /// + public string[] StringArray { get; set; } + /// + /// Guid值 + /// + public Guid GuidValue { get; set; } + + /// + /// 测试2 + /// + public Sample2 Test2 { get; set; } + + /// + /// 测试3 + /// + public Sample3Copy Test3 { get; set; } + + /// + /// 导航属性 + /// + public List TestList { get; set; } + + /// + /// 静态属性 + /// + public static string StaticString => "TestStaticString"; + + /// + /// 静态对象 + /// + public static Sample2 StaticSample => new Sample2 { StringValue = "TestStaticSample" }; + + /// + /// 创建测试实例1 + /// + public static Sample Create1() { + return new Sample { StringValue = "A" }; + } + + /// + /// 创建测试实例2 + /// + public static Sample Create2() { + return new Sample { StringValue = "B" }; + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Samples/Sample2.cs b/test/Util.Core.Tests/Samples/Sample2.cs new file mode 100644 index 000000000..0d6f62011 --- /dev/null +++ b/test/Util.Core.Tests/Samples/Sample2.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace Util.Tests.Samples; + +/// +/// 测试样例2 +/// +[DisplayName( "测试样例2" )] +public class Sample2 { + /// + /// 描述 + /// + [Description("描述")] + public string Description { get; set; } + /// + /// 显示名 + /// + [DisplayName( "显示名" )] + public string DisplayName { get; set; } + /// + /// 显示描述 + /// + [Display( Name = "显示名2" )] + public string DisplayName2 { get; set; } + /// + /// 显示描述 + /// + [Display( Description= "显示描述" )] + public string Display { get; set; } + /// + /// string值 + /// + public string StringValue { get; set; } + /// + /// int值 + /// + public int IntValue { get; set; } + /// + /// bool值 + /// + public bool BoolValue { get; set; } + /// + /// 可空bool值 + /// + public bool? NullableBoolValue { get; set; } + /// + /// 导航属性 + /// + public Sample3 Test3 { get; set; } + /// + /// 导航属性 + /// + public List TestList { get; set; } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Samples/Sample3.cs b/test/Util.Core.Tests/Samples/Sample3.cs new file mode 100644 index 000000000..de28879a7 --- /dev/null +++ b/test/Util.Core.Tests/Samples/Sample3.cs @@ -0,0 +1,24 @@ +using System.ComponentModel; + +namespace Util.Tests.Samples; + +/// +/// 测试样例3 +/// +[DisplayName( "测试样例3" )] +public class Sample3 { + /// + /// string值 + /// + public string StringValue { get; set; } +} + +/// +/// 测试样例3 +/// +public class Sample3Copy { + /// + /// string值 + /// + public string StringValue { get; set; } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Samples/Sample4.cs b/test/Util.Core.Tests/Samples/Sample4.cs new file mode 100644 index 000000000..56036b379 --- /dev/null +++ b/test/Util.Core.Tests/Samples/Sample4.cs @@ -0,0 +1,25 @@ +namespace Util.Tests.Samples; + +/// +/// 测试样例4 +/// +public class Sample4 { + /// + /// string值 + /// + public string StringValue { get; set; } + + /// + /// 输出空字符串 + /// + public override string ToString() { + return ""; + } + + /// + /// 获取副本 + /// + public object GetClone() { + return MemberwiseClone(); + } +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Samples/TestSample.cs b/test/Util.Core.Tests/Samples/TestSample.cs new file mode 100644 index 000000000..480f0a8fb --- /dev/null +++ b/test/Util.Core.Tests/Samples/TestSample.cs @@ -0,0 +1,37 @@ +namespace Util.Tests.Samples; + +/// +/// 测试服务接口 +/// +public interface ITestSample : ITestSample2 { +} + +/// +/// 测试服务接口 +/// +public interface ITestSample2 : ITestSample3 { +} + +/// +/// 测试服务接口 +/// +public interface ITestSample3 { +} + +/// +/// 测试服务接口 +/// +public interface ITestSample4 { +} + +/// +/// 测试服务接口 +/// +public interface ITestSample5 : ITestSample4 { +} + +/// +/// 测试服务 +/// +public class TestSample : ITestSample, ITestSample5 { +} \ No newline at end of file diff --git a/test/Util.Core.Tests/Util.Core.Tests.csproj b/test/Util.Core.Tests/Util.Core.Tests.csproj new file mode 100644 index 000000000..05734f2e4 --- /dev/null +++ b/test/Util.Core.Tests/Util.Core.Tests.csproj @@ -0,0 +1,27 @@ + + + + $(NetTargetFramework) + false + Util.Tests + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.Core.Tests/XUnitHelpers/AssertHelper.cs b/test/Util.Core.Tests/XUnitHelpers/AssertHelper.cs new file mode 100644 index 000000000..718c42d48 --- /dev/null +++ b/test/Util.Core.Tests/XUnitHelpers/AssertHelper.cs @@ -0,0 +1,50 @@ +using System; +using System.Threading.Tasks; +using Xunit; + +namespace Util.Tests.XUnitHelpers; + +/// +/// 断言操作 +/// +public class AssertHelper { + /// + /// 抛出异常,并从异常消息中搜索特定关键字 + /// + /// 异常类型 + /// 操作 + /// 关键字 + public static TException Throws( Action action,string keyword = "" ) where TException : Exception { + var exception = GetException( action ); + if( !string.IsNullOrWhiteSpace( keyword ) ) + Assert.Contains( keyword, exception.Message ); + return exception; + } + + /// + /// 获取异常 + /// + private static TException GetException( Action action ) where TException : Exception { + return Assert.Throws(action); + } + + /// + /// 抛出异常,并从异常消息中搜索特定关键字 + /// + /// 异常类型 + /// 操作 + /// 关键字 + public static async Task ThrowsAsync( Func action, string keyword = "" ) where TException : Exception { + var exception = await GetExceptionAsync( action ); + if( !string.IsNullOrWhiteSpace( keyword ) ) + Assert.Contains( keyword, exception.Message ); + return exception; + } + + /// + /// 获取异常 + /// + private static async Task GetExceptionAsync( Func action ) where TException : Exception { + return await Assert.ThrowsAsync(action); + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/Infrastructure/DatabaseScript.cs b/test/Util.Data.Dapper.MySql.Tests.Integration/Infrastructure/DatabaseScript.cs new file mode 100644 index 000000000..88472a00b --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/Infrastructure/DatabaseScript.cs @@ -0,0 +1,126 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Util.Data.Dapper.Tests.Infrastructure { + /// + /// 数据库初始化脚本 + /// + public class DatabaseScript { + /// + /// 初始化 + /// + public static void InitProcedures( DatabaseFacade database ) { + CreateGetProductCodeProcedure( database ); + CreateGetProductProcedure( database ); + CreateGetProductsProcedure( database ); + CreateGetOrderItemProcedure( database ); + CreateGetProductCodeOutputProcedure( database ); + CreateInsertProductProcedure( database ); + } + + /// + /// 创建Proc_GetProductCode存储过程 + /// + private static void CreateGetProductCodeProcedure( DatabaseFacade database ) { + var sql = @" + Create Procedure Proc_GetProductCode ( + id char(36) + ) + Begin + Select Code + From Product + Where ProductId = id; + End + "; + database.ExecuteSqlRaw( sql ); + } + + /// + /// 创建Proc_GetProduct存储过程 + /// + private static void CreateGetProductProcedure( DatabaseFacade database ) { + var sql = @" + Create Procedure Proc_GetProduct ( + id char(36) + ) + Begin + Select * + From Product + Where ProductId = id; + End + "; + database.ExecuteSqlRaw( sql ); + } + + /// + /// 创建Proc_GetProducts存储过程 + /// + private static void CreateGetProductsProcedure( DatabaseFacade database ) { + var sql = @" + Create Procedure Proc_GetProducts ( + id char(36), + id2 char(36) + ) + Begin + Select p.ProductId As Id,p.* + From Product As p + Where p.ProductId in ( id,id2 ); + End + "; + database.ExecuteSqlRaw( sql ); + } + + /// + /// 创建Proc_GetOrderItem存储过程 + /// + private static void CreateGetOrderItemProcedure( DatabaseFacade database ) { + var sql = @" + Create Procedure Proc_GetOrderItem ( + id char(36) + ) + Begin + Select i.OrderItemId As Id,i.*,o.OrderId As Id,o.* + From OrderItem As i + Left Join `Order` As o On o.OrderId=i.OrderId + Where i.OrderItemId=id; + End + "; + database.ExecuteSqlRaw( sql ); + } + + /// + /// 创建Proc_GetProductCode_Output存储过程 + /// + private static void CreateGetProductCodeOutputProcedure( DatabaseFacade database ) { + var sql = @" + Create Procedure Proc_GetProductCode_Output ( + id char(36), + out code_output varchar(50) + ) + Begin + Select `Code` into code_output + From `Product` + Where `ProductId` = id; + End + "; + database.ExecuteSqlRaw( sql ); + } + + /// + /// 创建Proc_InsertProduct存储过程 + /// + private static void CreateInsertProductProcedure( DatabaseFacade database ) { + var sql = @" + Create Procedure Proc_InsertProduct ( + id char(36), + codeValue varchar(50) + ) + Begin + Insert Into Product(ProductId,Code) + Values( id,codeValue ); + End + "; + database.ExecuteSqlRaw( sql ); + } + } +} diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/Metadata/MySqlMetadataServiceTest.cs b/test/Util.Data.Dapper.MySql.Tests.Integration/Metadata/MySqlMetadataServiceTest.cs new file mode 100644 index 000000000..dcd30605a --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/Metadata/MySqlMetadataServiceTest.cs @@ -0,0 +1,120 @@ +using System.Linq; +using System.Threading.Tasks; +using Util.Data.Metadata; +using Xunit; + +namespace Util.Data.Dapper.Tests.Metadata { + /// + /// MySql数据库元数据服务测试 + /// + public class MySqlMetadataServiceTest { + /// + /// 数据库元信息服务 + /// + private readonly IMetadataService _metadataService; + + /// + /// 测试初始化 + /// + public MySqlMetadataServiceTest( IMetadataService service ) { + _metadataService = service; + } + + /// + /// 测试获取数据库名称 + /// + [Fact] + public async Task TestDatabaseName() { + var info = await _metadataService.GetDatabaseInfoAsync(); + Assert.Equal( "Util.Data.Dapper.Test", info.Name ); + } + + /// + /// 测试获取表信息 + /// + [Fact] + public async Task TestTableInfo() { + var info = await _metadataService.GetDatabaseInfoAsync(); + var table = info.Tables.FirstOrDefault( t => t.Name == "Product" ); + Assert.NotNull( table ); + Assert.Null( table.Schema ); + Assert.Equal( "产品", table.Comment ); + } + + /// + /// 测试获取主键信息 + /// + [Fact] + public async Task TestPrimaryKey() { + var info = await _metadataService.GetDatabaseInfoAsync(); + var table = info.Tables.First( t => t.Name == "OrderItem" ); + var pkColumn = table.Columns.FirstOrDefault( t => t.Name == "OrderItemId" ); + Assert.NotNull( pkColumn ); + Assert.True( pkColumn.IsPrimaryKey ); + var fkColumn = table.Columns.FirstOrDefault( t => t.Name == "OrderId" ); + Assert.NotNull( fkColumn ); + Assert.False( fkColumn.IsPrimaryKey ); + } + + /// + /// 测试获取自增信息 + /// + [Fact] + public async Task TestAutoIncrement() { + var info = await _metadataService.GetDatabaseInfoAsync(); + var table = info.Tables.FirstOrDefault( t => t.Name == "Customer" ); + Assert.NotNull( table ); + var column = table.Columns.FirstOrDefault( t => t.Name == "CustomerId" ); + Assert.NotNull( column ); + Assert.True( column.IsAutoIncrement ); + Assert.Equal( "int", column.DataType ); + } + + /// + /// 测试获取数值精度信息 + /// + [Fact] + public async Task TestPrecision() { + var info = await _metadataService.GetDatabaseInfoAsync(); + var table = info.Tables.FirstOrDefault( t => t.Name == "Product" ); + Assert.NotNull( table ); + var column = table.Columns.FirstOrDefault( t => t.Name == "Price" ); + Assert.NotNull( column ); + Assert.Equal( 12, column.Precision ); + Assert.Equal( 2, column.Scale ); + } + + /// + /// 测试获取字符串列信息 + /// + [Fact] + public async Task TestStringInfo() { + var info = await _metadataService.GetDatabaseInfoAsync(); + var table = info.Tables.FirstOrDefault( t => t.Name == "Product" ); + Assert.NotNull( table ); + var count = table.Columns.Count( t => t.Name == "Code" ); + Assert.Equal( 1, count ); + var column = table.Columns.FirstOrDefault( t => t.Name == "Code" ); + Assert.NotNull( column ); + Assert.Equal( "产品编码", column.Comment ); + Assert.False( column.IsPrimaryKey ); + Assert.True( column.IsNullable ); + Assert.Equal( "varchar", column.DataType ); + Assert.Equal( 50, column.Length ); + } + + /// + /// 测试获取布尔列信息 + /// + [Fact] + public async Task TestBoolInfo() { + var info = await _metadataService.GetDatabaseInfoAsync(); + var table = info.Tables.FirstOrDefault( t => t.Name == "Product" ); + Assert.NotNull( table ); + var column = table.Columns.FirstOrDefault( t => t.Name == "IsDeleted" ); + Assert.NotNull( column ); + Assert.Equal( "tinyint", column.DataType ); + Assert.Equal( 1, column.Length ); + } + } +} diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/SqlBuilders/MySqlExistsSqlBuilderTest.cs b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlBuilders/MySqlExistsSqlBuilderTest.cs new file mode 100644 index 000000000..b427661bf --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlBuilders/MySqlExistsSqlBuilderTest.cs @@ -0,0 +1,48 @@ +using System.Text; +using Util.Data.Dapper.Sql.Builders; +using Util.Data.Sql; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Data.Dapper.Tests.SqlBuilders { + /// + /// 测试判断是否存在的Sql生成器 + /// + public class MySqlExistsSqlBuilderTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + + /// + /// 测试初始化 + /// + public MySqlExistsSqlBuilderTest( ITestOutputHelper output ) { + _output = output; + } + + /// + /// 测试生成的Sql + /// + [Fact] + public void TestToSql() { + var sql = new StringBuilder(); + sql.AppendLine( "Select Case" ); + sql.AppendLine( " When Exists (" ); + sql.AppendLine( "Select 1 " ); + sql.Append( "From `b`" ); + sql.AppendLine( ")" ); + sql.AppendLine( " Then true" ); + sql.AppendLine( " Else false " ); + sql.Append( "End" ); + + var subQuery = new MySqlBuilder() + .Select("a") + .From( "b" ); + var sqlBuilder = new MySqlExistsSqlBuilder( subQuery ); + var result = sqlBuilder.GetSql(); + _output.WriteLine( result ); + Assert.Equal( sql.ToString(),result ); + } + } +} diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.Execute.cs b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.Execute.cs new file mode 100644 index 000000000..c283a48c7 --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.Execute.cs @@ -0,0 +1,50 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Util.Tests.Models; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// MySql Sql执行器测试 - 执行Sql测试 + /// + public partial class MySqlExecutorTest { + /// + /// 测试执行Sql增删改操作 + /// + [Fact] + public async Task TestExecuteAsync() { + var id = Guid.NewGuid(); + await _sqlExecutor + .AppendLine( "Insert Product(ProductId,Code) " ) + .Append( "Values(@ProductId,@Code)" ) + .AddParam( "@ProductId", id ) + .AddParam( "Code", TestConfig.Value ) + .ExecuteAsync(); + var result = await _sqlExecutor.Select( "Code" ).From( "Product" ).Where( "ProductId", id ).ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + + /// + /// 测试执行Sql增删改操作 - 写入扩展属性 + /// + [Fact] + public async Task TestExecuteAsync_2() { + var id = Guid.NewGuid(); + var product = new Product( id ) { + Code = TestConfig.Value, + TestProperty1 = "a" + }; + await _sqlExecutor + .AppendLine( "Insert Product(ProductId,Code,ExtraProperties) " ) + .Append( "Values(@Id,@Code,@ExtraProperties)" ) + .AddDynamicParams( product ) + .AddParam( "ExtraProperties", product.GetExtraPropertiesJson() ) + .ExecuteAsync(); + var result = await _sqlExecutor.Select( "*" ).From( "Product" ).Where( "ProductId", id ).ExecuteSingleAsync(); + Assert.Equal( TestConfig.Value, result.Code ); + Assert.Equal( "a", result.TestProperty1 ); + } + } +} diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.Id.cs b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.Id.cs new file mode 100644 index 000000000..57dce2fc5 --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.Id.cs @@ -0,0 +1,67 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Helpers; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// MySql Sql执行器测试 - 标识列测试 + /// + public partial class MySqlExecutorTest { + /// + /// 测试插入Guid主键 + /// + [Fact] + public async Task TestInsert_Guid() { + var id = Guid.NewGuid(); + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + var result = await _sqlExecutor + .Select( "Code" ) + .From( "Product" ) + .Where( "ProductId", id ) + .ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + + /// + /// 测试插入int自增长主键 + /// + [Fact] + public async Task TestInsert_Int() { + var value = Id.Create(); + await _sqlExecutor + .Insert( "Code", "Customer" ) + .Values( value ) + .ExecuteAsync(); + var result = await _sqlExecutor + .Select( "CustomerId" ) + .From( "Customer" ) + .Where( "Code", value ) + .ToIntAsync(); + Assert.True( result > 0 ); + } + + /// + /// 测试插入string主键 + /// + [Fact] + public async Task TestInsert_String() { + var id = Id.Create(); + await _sqlExecutor + .Insert( "OrderId,CustomerName", "Order" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + var result = await _sqlExecutor + .Select( "CustomerName" ) + .From( "Order" ) + .Where( "OrderId", id ) + .ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + } +} diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.Insert.cs b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.Insert.cs new file mode 100644 index 000000000..dabe4a44a --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.Insert.cs @@ -0,0 +1,83 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// MySql Sql执行器测试 - 插入测试 + /// + public partial class MySqlExecutorTest { + /// + /// 测试使用Insert Values语句插入一条数据 + /// + [Fact] + public async Task TestInsert_1() { + //插入一条数据 + var id = Guid.NewGuid(); + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //获取值 + var result = await _sqlExecutor + .Select( "Code" ) + .From( "Product" ) + .Where( "ProductId", id ) + .ToStringAsync(); + + //验证 + Assert.Equal( TestConfig.Value, result ); + } + + /// + /// 测试使用Insert Values语句插入2条数据 + /// + [Fact] + public async Task TestInsert_2() { + //插入一条数据 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id, TestConfig.Value ) + .Values( id2, TestConfig.Value ) + .ExecuteAsync(); + + //获取值 + var result = await _sqlExecutor + .AppendSelect( "Count(*)" ) + .From( "Product" ) + .In( "ProductId", new[]{id,id2} ) + .ToIntAsync(); + + //验证 + Assert.Equal( 2, result ); + } + + /// + /// 测试使用Insert Select语句插入 + /// + [Fact] + public async Task TestInsert_3() { + //用Insert Select插入一条数据 + var id = Guid.NewGuid(); + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .AppendSelect( $"'{id}','{TestConfig.Value}'" ) + .ExecuteAsync(); + + //获取值 + var result = await _sqlExecutor + .Select( "Code" ) + .From( "Product" ) + .Where( "ProductId", id ) + .ToStringAsync(); + + //验证 + Assert.Equal( TestConfig.Value, result ); + } + } +} diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.Param.cs b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.Param.cs new file mode 100644 index 000000000..f6ca96a05 --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.Param.cs @@ -0,0 +1,81 @@ +using System; +using System.Data; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// MySql Sql执行器测试 - 执行存储过程参数测试 + /// + public partial class MySqlExecutorTest { + /// + /// 测试添加动态参数 + /// + [Fact] + public async Task TestAddParam_Dynamic() { + var id = Guid.NewGuid(); + await _sqlExecutor + .AppendLine( "Insert Product(ProductId,Code) " ) + .Append( "Values(@ProductId,@Code)" ) + .AddDynamicParams( new { ProductId=id, Code= TestConfig.Value } ) + .ExecuteAsync(); + var result = await _sqlExecutor.Select( "Code" ).From( "Product" ).Where( "ProductId", id ).ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + + /// + /// 测试添加和获取输出参数 + /// + [Fact] + public async Task TestAddParam_Output() { + //定义变量 + var id = Guid.NewGuid(); + + //插入数据 + await _sqlExecutor + .AppendLine( "Insert Product(ProductId,Code) " ) + .Append( "Values(@ProductId,@Code)" ) + .AddParam( "@ProductId", id ) + .AddParam( "Code", TestConfig.Value ) + .ExecuteAsync(); + + //执行存储过程 - 添加输出参数 + await _sqlExecutor + .AddParam( "id", id ) + .AddParam( "code_output", dbType: DbType.String,direction: ParameterDirection.Output,size:50 ) + .ExecuteProcedureAsync( "Proc_GetProductCode_Output" ); + + //验证 - 使用GetParam获取输出参数 + Assert.Equal( TestConfig.Value, _sqlExecutor.GetParam( "code_output" ) ); + } + + /// + /// 测试添加参数 - 在执行前添加并获取参数 + /// + [Fact] + public async Task TestAddParam_1() { + var id = Guid.NewGuid(); + + //添加第一个参数 + _sqlExecutor + .AppendLine( "Insert Product(ProductId,Code) " ) + .Append( "Values(@ProductId,@Code)" ) + .AddParam( "@ProductId", id ); + + //获取参数 + var param = _sqlExecutor.GetParam( "ProductId" ); + Assert.Equal( id, param ); + + //添加第二个参数并执行 + await _sqlExecutor + .AddParam( "Code", TestConfig.Value ) + .ExecuteAsync(); + + //验证 + var result = await _sqlExecutor.Select( "Code" ).From( "Product" ).Where( "ProductId", id ).ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + } +} diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.Procedure.cs b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.Procedure.cs new file mode 100644 index 000000000..69a32f940 --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.Procedure.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// MySql Sql执行器测试 - 执行存储过程测试 + /// + public partial class MySqlExecutorTest { + /// + /// 测试执行存储过程增删改操作 + /// + [Fact] + public async Task TestExecuteProcedureScalarAsync() { + //定义变量 + var id = Guid.NewGuid(); + + //插入数据,参数无论是否加@前缀均可 + await _sqlExecutor + .AddParam( "id", id ) + .AddParam( "codeValue", TestConfig.Value ) + .ExecuteProcedureAsync( "Proc_InsertProduct" ); + + //获取IntValue值 + var result = await _sqlExecutor + .AddParam( "id", id ) + .ExecuteProcedureScalarAsync( "Proc_GetProductCode" ); + + //验证 + Assert.Equal( TestConfig.Value, result ); + } + } +} diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.Transaction.cs b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.Transaction.cs new file mode 100644 index 000000000..770f2c22e --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.Transaction.cs @@ -0,0 +1,250 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// MySql Sql执行器测试 - 事务测试 + /// + public partial class MySqlExecutorTest { + /// + /// 测试事务操作 - 不使用事务,第一次操作成功,第二次失败 + /// + [Fact] + public async Task TestTransaction_1() { + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //Code2列名错误,插入Id2失败 + await _sqlExecutor + .Insert( "ProductId,Code,Code2", "Product" ) + .Values( id2, TestConfig.Value, TestConfig.Value ) + .ExecuteAsync(); + } + catch { + // ignored + } + + //断言 + var exists = await _sqlExecutor.From( "Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.True( exists ); + Assert.False( exists2 ); + } + + /// + /// 测试事务操作 - 调用BeginTransaction,CommitTransaction,RollbackTransaction + /// + [Fact] + public async Task TestTransaction_2() { + //开始事务 + _sqlExecutor.BeginTransaction(); + + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //Code2列名错误,插入Id2失败 + await _sqlExecutor + .Insert( "ProductId,Code,Code2", "Product" ) + .Values( id2, TestConfig.Value, TestConfig.Value ) + .ExecuteAsync(); + + //提交事务 + _sqlExecutor.CommitTransaction(); + } + catch { + //回滚事务 + _sqlExecutor.RollbackTransaction(); + } + + //断言 + var exists = await _sqlExecutor.From( "Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.False( exists ); + Assert.False( exists2 ); + } + + /// + /// 测试事务操作 - 使用using调用BeginTransaction + /// + [Fact] + public async Task TestTransaction_3() { + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + using var transaction = _sqlExecutor.BeginTransaction(); + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //Code2列名错误,插入Id2失败 + await _sqlExecutor + .Insert( "ProductId,Code,Code2", "Product" ) + .Values( id2, TestConfig.Value, TestConfig.Value ) + .ExecuteAsync(); + + //提交事务 + transaction.Commit(); + } + catch { + // ignored + } + + //断言 + var exists = await _sqlExecutor.From( "Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.False( exists ); + Assert.False( exists2 ); + } + + /// + /// 测试事务操作 - 外部传入事务 - 回滚 + /// + [Fact] + public async Task TestTransaction_4() { + //Sql执行器实例2开始事务 + _sqlExecutor2.BeginTransaction(); + var transaction = _sqlExecutor2.GetTransaction(); + + //设置Sql执行器实例1事务 + _sqlExecutor.SetTransaction( transaction ); + + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //Code2列名错误,插入Id2失败 + await _sqlExecutor2 + .Insert( "ProductId,Code,Code2", "Product" ) + .Values( id2, TestConfig.Value, TestConfig.Value ) + .ExecuteAsync(); + + //提交事务 + _sqlExecutor.CommitTransaction(); + } + catch { + //回滚事务 + _sqlExecutor.RollbackTransaction(); + } + + //断言 + var exists = await _sqlExecutor.From( "Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.False( exists ); + Assert.False( exists2 ); + } + + /// + /// 测试事务操作 - 外部传入事务 - 成功 + /// + [Fact] + public async Task TestTransaction_5() { + //Sql执行器实例2开始事务 + _sqlExecutor2.BeginTransaction(); + var transaction = _sqlExecutor2.GetTransaction(); + + //设置Sql执行器实例1事务 + _sqlExecutor.SetTransaction( transaction ); + + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //Code2列名错误,插入Id2失败 + await _sqlExecutor2 + .Insert( "ProductId,Code", "Product" ) + .Values( id2, TestConfig.Value ) + .ExecuteAsync(); + + //提交事务 + _sqlExecutor.CommitTransaction(); + } + catch { + //回滚事务 + _sqlExecutor.RollbackTransaction(); + } + + //断言 + var exists = await _sqlExecutor.From( "Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.True( exists ); + Assert.True( exists2 ); + } + + /// + /// 测试事务操作 - 成功提交 + /// + [Fact] + public async Task TestTransaction_6() { + //开始事务 + _sqlExecutor.BeginTransaction(); + + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //插入Id2成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id2, TestConfig.Value ) + .ExecuteAsync(); + + //提交事务 + _sqlExecutor.CommitTransaction(); + } + catch { + //回滚事务 + _sqlExecutor.RollbackTransaction(); + } + + //断言 + var exists = await _sqlExecutor.From( "Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.True( exists ); + Assert.True( exists2 ); + } + } +} diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.cs b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.cs new file mode 100644 index 000000000..21ed067c6 --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlExecutor/MySqlExecutorTest.cs @@ -0,0 +1,31 @@ +using Util.Data.Sql; +using Xunit.Abstractions; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// MySql Sql执行器测试 + /// + public partial class MySqlExecutorTest { + /// + /// 测试输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// Sql执行器 + /// + private readonly ISqlExecutor _sqlExecutor; + /// + /// Sql执行器2 + /// + private readonly ISqlExecutor _sqlExecutor2; + + /// + /// 测试初始化 + /// + public MySqlExecutorTest( ITestOutputHelper output, ISqlExecutor sqlExecutor, ISqlExecutor sqlExecutor2 ) { + _output = output; + _sqlExecutor = sqlExecutor; + _sqlExecutor2 = sqlExecutor2; + } + } +} diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/SqlQuery/MySqlQueryTest.Exists.cs b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlQuery/MySqlQueryTest.Exists.cs new file mode 100644 index 000000000..36fd48bd3 --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlQuery/MySqlQueryTest.Exists.cs @@ -0,0 +1,48 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlQuery { + /// + /// MySql Sqlѯ - жǷڲ + /// + public partial class MySqlQueryTest { + /// + /// жǷ - + /// + [Fact] + public void TestExecuteExists_1() { + var result = _sqlQuery.From( "Product" ).Where( "ProductId", TestConfig.Id ).ExecuteExists(); + Assert.True( result ); + } + + /// + /// жǷ - + /// + [Fact] + public void TestExecuteExists_2() { + var result = _sqlQuery.From( "Product" ).Where( "ProductId", Guid.NewGuid() ).ExecuteExists(); + Assert.False( result ); + } + + /// + /// жǷ - + /// + [Fact] + public async Task TestExecuteExistsAsync_1() { + var result = await _sqlQuery.From( "Product" ).Where( "ProductId", TestConfig.Id ).ExecuteExistsAsync(); + Assert.True( result ); + } + + /// + /// жǷ - + /// + [Fact] + public async Task TestExecuteExistsAsync_2() { + var result = await _sqlQuery.From( "Product" ).Where( "ProductId", Guid.NewGuid() ).ExecuteExistsAsync(); + Assert.False( result ); + } + } +} diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/SqlQuery/MySqlQueryTest.Procedure.cs b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlQuery/MySqlQueryTest.Procedure.cs new file mode 100644 index 000000000..6d6809b4d --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlQuery/MySqlQueryTest.Procedure.cs @@ -0,0 +1,301 @@ +using System; +using System.Collections; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Helpers; +using Util.Tests.Configs; +using Util.Tests.Models; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlQuery { + /// + /// MySql Sqlѯ - ִд洢̲ + /// + public partial class MySqlQueryTest { + + #region ExecuteProcedureScalar + + /// + /// ִд洢̻ȡֵ + /// + [Fact] + public void TestExecuteProcedureScalar_1() { + //ִд洢 + var result = _sqlQuery + .AddParam( "id", TestConfig.Id ) + .ExecuteProcedureScalar( "Proc_GetProductCode" ); + + // + Assert.Equal( TestConfig.Value, result ); + Assert.Equal( "`Proc_GetProductCode`", _sqlQuery.PreviousSql.GetSql() ); + } + + /// + /// ִд洢̻ȡֵ - ͷ + /// + [Fact] + public void TestExecuteProcedureScalar_2() { + //ִд洢 + var result = _sqlQuery + .AddParam( "id", TestConfig.Id ) + .ExecuteProcedureScalar( "Proc_GetProductCode" ); + + // + Assert.Equal( TestConfig.Value, result ); + Assert.Equal( "`Proc_GetProductCode`", _sqlQuery.PreviousSql.GetSql() ); + } + + #endregion + + #region ExecuteProcedureScalarAsync + + /// + /// ִд洢̻ȡֵ + /// + [Fact] + public async Task TestExecuteProcedureScalarAsync_1() { + //ִд洢 + var result = await _sqlQuery + .AddParam( "id", TestConfig.Id ) + .ExecuteProcedureScalarAsync( "Proc_GetProductCode" ); + + // + Assert.Equal( TestConfig.Value, result ); + Assert.Equal( "`Proc_GetProductCode`", _sqlQuery.PreviousSql.GetSql() ); + } + + /// + /// ִд洢̻ȡֵ - ͷ + /// + [Fact] + public async Task TestExecuteProcedureScalarAsync_2() { + //ִд洢 + var result = await _sqlQuery + .AddParam( "id", TestConfig.Id ) + .ExecuteProcedureScalarAsync( "Proc_GetProductCode" ); + + // + Assert.Equal( TestConfig.Value, result ); + Assert.Equal( "`Proc_GetProductCode`", _sqlQuery.PreviousSql.GetSql() ); + } + + #endregion + + #region ExecuteProcedureSingle + + /// + /// ִд洢̻ȡʵ + /// + [Fact] + public void TestExecuteProcedureSingle() { + //ִд洢 + var result = _sqlQuery + .AddParam( "id", TestConfig.Id ) + .ExecuteProcedureSingle( "Proc_GetProduct" ); + + // + Assert.Equal( TestConfig.Value, result.Code ); + Assert.Equal( "`Proc_GetProduct`", _sqlQuery.PreviousSql.GetSql() ); + } + + #endregion + + #region ExecuteProcedureSingleAsync + + /// + /// ִд洢̻ȡʵ + /// + [Fact] + public async Task TestExecuteProcedureSingleAsync() { + //ִд洢 + var result = await _sqlQuery + .AddParam( "id", TestConfig.Id ) + .ExecuteProcedureSingleAsync( "Proc_GetProduct" ); + + // + Assert.Equal( TestConfig.Value, result.Code ); + Assert.Equal( "`Proc_GetProduct`", _sqlQuery.PreviousSql.GetSql() ); + } + + #endregion + + #region ExecuteProcedureQuery + + /// + /// ִд洢̻ȡʵ弯 + /// + [Fact] + public async Task TestExecuteProcedureQuery_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, "a" ) + .Values( id2, "b" ) + .ExecuteAsync(); + + //ִд洢 + var result = _sqlQuery + .AddParam( "id", id ) + .AddParam( "id2", id2 ) + .ExecuteProcedureQuery( "Proc_GetProducts" ); + + // + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id && t.Code == "a" ); + Assert.Contains( result, t => t.Id == id2 && t.Code == "b" ); + Assert.Equal( "`Proc_GetProducts`", _sqlQuery.PreviousSql.GetSql() ); + } + + /// + /// Իȡʵ弯 - 1Ͳ + /// + [Fact] + public async Task TestExecuteProcedureQuery_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, "a" ) + .Values( id2, "b" ) + .ExecuteAsync(); + + //ִд洢 + var result = _sqlQuery + .AddParam( "id", id ) + .AddParam( "id2", id2 ) + .ExecuteProcedureQuery( "Proc_GetProducts" ); + + // + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id && t.Code == "a" ); + Assert.Contains( result, t => t.Id == id2 && t.Code == "b" ); + Assert.Equal( "`Proc_GetProducts`", _sqlQuery.PreviousSql.GetSql() ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestExecuteProcedureQuery_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "OrderItem" ) + .Values( orderItemId, orderId, TestConfig.DecimalValue ) + .ExecuteAsync(); + + //ȡ + var result = _sqlQuery + .AddParam( "id", orderItemId ) + .ExecuteProcedureQuery( "Proc_GetOrderItem", ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion + + #region ExecuteProcedureQueryAsync + + /// + /// ִд洢̻ȡʵ弯 + /// + [Fact] + public async Task TestExecuteProcedureQueryAsync_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, "a" ) + .Values( id2, "b" ) + .ExecuteAsync(); + + //ִд洢 + var result = await _sqlQuery + .AddDynamicParams( new{ id, id2 } ) + .ExecuteProcedureQueryAsync( "Proc_GetProducts" ); + + // + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id && t.Code == "a" ); + Assert.Contains( result, t => t.Id == id2 && t.Code == "b" ); + } + + /// + /// Իȡʵ弯 - 1Ͳ + /// + [Fact] + public async Task TestExecuteProcedureQueryAsync_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, "a" ) + .Values( id2, "b" ) + .ExecuteAsync(); + + //ִд洢 + var result = await _sqlQuery + .AddParam( "id", id ) + .AddParam( "id2", id2 ) + .ExecuteProcedureQueryAsync( "Proc_GetProducts" ); + + // + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id && t.Code == "a" ); + Assert.Contains( result, t => t.Id == id2 && t.Code == "b" ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestExecuteProcedureQueryAsync_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "OrderItem" ) + .Values( orderItemId, orderId, TestConfig.DecimalValue ) + .ExecuteAsync(); + + //ȡ + var result = await _sqlQuery + .AddParam( "id", orderItemId ) + .ExecuteProcedureQueryAsync( "Proc_GetOrderItem", ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion + } +} diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/SqlQuery/MySqlQueryTest.Query.cs b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlQuery/MySqlQueryTest.Query.cs new file mode 100644 index 000000000..41cfee0ec --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlQuery/MySqlQueryTest.Query.cs @@ -0,0 +1,388 @@ +using System; +using System.Collections; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Helpers; +using Util.Tests.Configs; +using Util.Tests.Models; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlQuery { + /// + /// MySql Sqlѯ - ѯ + /// + public partial class MySqlQueryTest { + + #region ExecuteQuery + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestExecuteQuery_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestExecuteQuery_1"; + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = _sqlQuery.Select().From( "Product" ).In( "ProductId", new[] { id, id2 } ).ExecuteQuery(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.ProductId == id ); + Assert.Contains( result, t => t.ProductId == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 1Ͳ + /// + [Fact] + public async Task TestExecuteQuery_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestExecuteQuery_2"; + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = _sqlQuery.Select("ProductId As Id,Code").From( "Product" ).In( "ProductId", new[] { id, id2 } ).ExecuteQuery(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id ); + Assert.Contains( result, t => t.Id == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestExecuteQuery_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "OrderItem" ) + .Values( orderItemId, orderId,TestConfig.DecimalValue ) + .ExecuteAsync(); + + ////ȡ + var result = _sqlQuery + .Select( "i.OrderItemId As Id,i.*" ) //OrderItemIdӳΪId,֮OrderItemص + .Select( "o.OrderId As Id,o.*" ) //OrderIdӳΪId,֮ǰOrderItemеϢ,֮Orderе + .From( "OrderItem As i" ) + .LeftJoin( "Order As o" ).On( "o.OrderId", "i.OrderId" ) + .Where( "i.OrderItemId", orderItemId ) + .ExecuteQuery( ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion + + #region ExecuteQueryAsync + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestExecuteQueryAsync_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestExecuteQueryAsync_1"; + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = await _sqlQuery.Select().From( "Product" ).In( "ProductId", new[] { id, id2 } ).ExecuteQueryAsync(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.ProductId == id ); + Assert.Contains( result, t => t.ProductId == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestExecuteQueryAsync_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestExecuteQueryAsync_2"; + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = await _sqlQuery.Select( "ProductId As Id,Code" ).From( "Product" ).In( "ProductId", new[] { id, id2 } ).ExecuteQueryAsync(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id ); + Assert.Contains( result, t => t.Id == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestExecuteQueryAsync_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "OrderItem" ) + .Values( orderItemId, orderId, TestConfig.DecimalValue ) + .ExecuteAsync(); + + ////ȡ + var result = await _sqlQuery + .Select( "i.OrderItemId As Id,i.*" ) //OrderItemIdӳΪId,֮OrderItemص + .Select( "o.OrderId As Id,o.*" ) //OrderIdӳΪId,֮ǰOrderItemеϢ,֮Orderе + .From( "OrderItem As i" ) + .LeftJoin( "Order As o" ).On( "o.OrderId", "i.OrderId" ) + .Where( "i.OrderItemId", orderItemId ) + .ExecuteQueryAsync( ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion + + #region ToList + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestToList_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestToList_1"; + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = _sqlQuery.Select().From( "Product" ).In( "ProductId", new[] { id, id2 } ).ToList(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.ProductId == id ); + Assert.Contains( result, t => t.ProductId == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 1Ͳ + /// + [Fact] + public async Task TestToList_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestToList_2"; + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = _sqlQuery.Select( "ProductId As Id,Code" ).From( "Product" ).In( "ProductId", new[] { id, id2 } ).ToList(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id ); + Assert.Contains( result, t => t.Id == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestToList_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "OrderItem" ) + .Values( orderItemId, orderId, TestConfig.DecimalValue ) + .ExecuteAsync(); + + ////ȡ + var result = _sqlQuery + .Select( "i.OrderItemId As Id,i.*" ) //OrderItemIdӳΪId,֮OrderItemص + .Select( "o.OrderId As Id,o.*" ) //OrderIdӳΪId,֮ǰOrderItemеϢ,֮Orderе + .From( "OrderItem As i" ) + .LeftJoin( "Order As o" ).On( "o.OrderId", "i.OrderId" ) + .Where( "i.OrderItemId", orderItemId ) + .ToList( ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion + + #region ToListAsync + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestToListAsync_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestToListAsync_1"; + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = await _sqlQuery.Select().From( "Product" ).In( "ProductId", new[] { id, id2 } ).ToListAsync(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.ProductId == id ); + Assert.Contains( result, t => t.ProductId == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestToListAsync_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestToListAsync_2"; + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = await _sqlQuery.Select( "ProductId As Id,Code" ).From( "Product" ).In( "ProductId", new[] { id, id2 } ).ToListAsync(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id ); + Assert.Contains( result, t => t.Id == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestToListAsync_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "OrderItem" ) + .Values( orderItemId, orderId, TestConfig.DecimalValue ) + .ExecuteAsync(); + + ////ȡ + var result = await _sqlQuery + .Select( "i.OrderItemId As Id,i.*" ) //OrderItemIdӳΪId,֮OrderItemص + .Select( "o.OrderId As Id,o.*" ) //OrderIdӳΪId,֮ǰOrderItemеϢ,֮Orderе + .From( "OrderItem As i" ) + .LeftJoin( "Order As o" ).On( "o.OrderId", "i.OrderId" ) + .Where( "i.OrderItemId", orderItemId ) + .ToListAsync( ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion + } +} diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/SqlQuery/MySqlQueryTest.Scalar.cs b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlQuery/MySqlQueryTest.Scalar.cs new file mode 100644 index 000000000..2ce877e04 --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlQuery/MySqlQueryTest.Scalar.cs @@ -0,0 +1,629 @@ +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlQuery { + /// + /// MySql Sqlѯ - ֵ + /// + public partial class MySqlQueryTest { + + #region ExecuteScalar + + /// + /// Իȡֵ + /// + [Fact] + public void TestExecuteScalar_1() { + var result = _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ExecuteScalar(); + Assert.Equal( TestConfig.Value, result.SafeString() ); + } + + /// + /// Իȡֵ - + /// + [Fact] + public void TestExecuteScalar_2() { + var result = _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ExecuteScalar(); + Assert.Equal( TestConfig.Value, result ); + } + + #endregion + + #region ExecuteScalarAsync + + /// + /// Իȡֵ + /// + [Fact] + public async Task TestExecuteScalarAsync_1() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ExecuteScalarAsync(); + Assert.Equal( TestConfig.Value, result.SafeString() ); + } + + /// + /// Իȡֵ - + /// + [Fact] + public async Task TestExecuteScalarAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ExecuteScalarAsync(); + Assert.Equal( TestConfig.Value, result ); + } + + #endregion + + #region ToStringAsync + + /// + /// Իȡֵַ + /// + [Fact] + public async Task TestToStringAsync() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + + #endregion + + #region ToInt + + /// + /// Իȡ32λֵ + /// + [Fact] + public void TestToInt() { + var result = _sqlQuery.Select( "IntPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToInt(); + Assert.Equal( TestConfig.IntValue, result ); + } + + #endregion + + #region ToIntAsync + + /// + /// Իȡ32λֵ + /// + [Fact] + public async Task TestToIntAsync() { + var result = await _sqlQuery.Select( "IntPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToIntAsync(); + Assert.Equal( TestConfig.IntValue, result ); + } + + #endregion + + #region ToIntOrNull + + /// + /// Իȡ32λֵ - ֵ + /// + [Fact] + public void TestToIntOrNull_1() { + var result = _sqlQuery.Select( "IntPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToIntOrNull(); + Assert.Equal( TestConfig.IntValue, result ); + } + + /// + /// Իȡ32λɿֵ - ֵ + /// + [Fact] + public void TestToIntOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToIntOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToIntOrNullAsync + + /// + /// Իȡ32λɿֵ - ֵ + /// + [Fact] + public async Task TestToIntOrNullAsync_1() { + var result = await _sqlQuery.Select( "IntPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToIntOrNullAsync(); + Assert.Equal( TestConfig.IntValue, result ); + } + + /// + /// Իȡ32λɿֵ - ֵ + /// + [Fact] + public async Task TestToIntOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToIntOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToLong + + /// + /// Իȡ64λֵ + /// + [Fact] + public void TestToLong() { + var result = _sqlQuery.Select( "LongPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToLong(); + Assert.Equal( TestConfig.LongValue, result ); + } + + #endregion + + #region ToLongAsync + + /// + /// Իȡ64λֵ + /// + [Fact] + public async Task TestToLongAsync() { + var result = await _sqlQuery.Select( "LongPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToLongAsync(); + Assert.Equal( TestConfig.LongValue, result ); + } + + #endregion + + #region ToLongOrNull + + /// + /// Իȡ64λɿֵ - ֵ + /// + [Fact] + public void TestToLongOrNull_1() { + var result = _sqlQuery.Select( "LongPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToLongOrNull(); + Assert.Equal( TestConfig.LongValue, result ); + } + + /// + /// Իȡ64λɿֵ - ֵ + /// + [Fact] + public void TestToLongOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToLongOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToLongOrNullAsync + + /// + /// Իȡ64λɿֵ - ֵ + /// + [Fact] + public async Task TestToLongOrNullAsync_1() { + var result = await _sqlQuery.Select( "LongPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToLongOrNullAsync(); + Assert.Equal( TestConfig.LongValue, result ); + } + + /// + /// Իȡ64λɿֵ - ֵ + /// + [Fact] + public async Task TestToLongOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToLongOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToGuid + + /// + /// ԻȡGuidֵ + /// + [Fact] + public void TestToGuid() { + var result = _sqlQuery.Select( "ProductId" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToGuid(); + Assert.Equal( TestConfig.Id, result ); + } + + #endregion + + #region ToGuidAsync + + /// + /// ԻȡGuidֵ + /// + [Fact] + public async Task TestToGuidAsync() { + var result = await _sqlQuery.Select( "ProductId" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToGuidAsync(); + Assert.Equal( TestConfig.Id, result ); + } + + #endregion + + #region ToGuidOrNull + + /// + /// ԻȡɿGuidֵ - ֵ + /// + [Fact] + public void TestToGuidOrNull_1() { + var result = _sqlQuery.Select( "ProductId" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToGuidOrNull(); + Assert.Equal( TestConfig.Id, result ); + } + + /// + /// ԻȡɿGuidֵ - ֵ + /// + [Fact] + public void TestToGuidOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToGuidOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToGuidOrNullAsync + + /// + /// ԻȡɿGuidֵ - ֵ + /// + [Fact] + public async Task TestToGuidOrNullAsync_1() { + var result = await _sqlQuery.Select( "ProductId" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToGuidOrNullAsync(); + Assert.Equal( TestConfig.Id, result ); + } + + /// + /// ԻȡɿGuidֵ - ֵ + /// + [Fact] + public async Task TestToGuidOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToGuidOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToBool + + /// + /// Իȡֵ + /// + [Fact] + public void TestToBool() { + var result = _sqlQuery.Select( "Enabled" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToBool(); + Assert.Equal( TestConfig.BoolValue, result ); + } + + #endregion + + #region ToBoolAsync + + /// + /// Իȡֵ + /// + [Fact] + public async Task TestToBoolAsync() { + var result = await _sqlQuery.Select( "Enabled" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToBoolAsync(); + Assert.Equal( TestConfig.BoolValue, result ); + } + + #endregion + + #region ToBoolOrNull + + /// + /// Իȡɿղֵ - ֵ + /// + [Fact] + public void TestToBoolOrNull_1() { + var result = _sqlQuery.Select( "Enabled" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToBoolOrNull(); + Assert.Equal( TestConfig.BoolValue, result ); + } + + /// + /// Իȡɿղֵ - ֵ + /// + [Fact] + public void TestToBoolOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToBoolOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToBoolOrNullAsync + + /// + /// Իȡɿղֵ - ֵ + /// + [Fact] + public async Task TestToBoolOrNullAsync_1() { + var result = await _sqlQuery.Select( "Enabled" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToBoolOrNullAsync(); + Assert.Equal( TestConfig.BoolValue, result ); + } + + /// + /// Իȡɿղֵ - ֵ + /// + [Fact] + public async Task TestToBoolOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToBoolOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToFloat + + /// + /// ԻȡFloatֵ + /// + [Fact] + public void TestToFloat() { + var result = _sqlQuery.Select( "FloatPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToFloat(); + Assert.Equal( TestConfig.FloatValue, result ); + } + + #endregion + + #region ToFloatAsync + + /// + /// ԻȡFloatֵ + /// + [Fact] + public async Task TestToFloatAsync() { + var result = await _sqlQuery.Select( "FloatPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToFloatAsync(); + Assert.Equal( TestConfig.FloatValue, result ); + } + + #endregion + + #region ToFloatOrNull + + /// + /// ԻȡɿFloatֵ - ֵ + /// + [Fact] + public void TestToFloatOrNull_1() { + var result = _sqlQuery.Select( "FloatPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToFloatOrNull(); + Assert.Equal( TestConfig.FloatValue, result ); + } + + /// + /// ԻȡɿFloatֵ - ֵ + /// + [Fact] + public void TestToFloatOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToFloatOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToFloatOrNullAsync + + /// + /// ԻȡɿFloatֵ - ֵ + /// + [Fact] + public async Task TestToFloatOrNullAsync_1() { + var result = await _sqlQuery.Select( "FloatPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToFloatOrNullAsync(); + Assert.Equal( TestConfig.FloatValue, result ); + } + + /// + /// ԻȡɿFloatֵ - ֵ + /// + [Fact] + public async Task TestToFloatOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToFloatOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToDouble + + /// + /// ԻȡDoubleֵ + /// + [Fact] + public void TestToDouble() { + var result = _sqlQuery.Select( "Price" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDouble(); + Assert.Equal( TestConfig.DoubleValue, result ); + } + + #endregion + + #region ToDoubleAsync + + /// + /// ԻȡDoubleֵ + /// + [Fact] + public async Task TestToDoubleAsync() { + var result = await _sqlQuery.Select( "Price" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleAsync(); + Assert.Equal( TestConfig.DoubleValue, result ); + } + + #endregion + + #region ToDoubleOrNull + + /// + /// ԻȡɿDoubleֵ - ֵ + /// + [Fact] + public void TestToDoubleOrNull_1() { + var result = _sqlQuery.Select( "Price" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleOrNull(); + Assert.Equal( TestConfig.DoubleValue, result ); + } + + /// + /// ԻȡɿDoubleֵ - ֵ + /// + [Fact] + public void TestToDoubleOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToDoubleOrNullAsync + + /// + /// ԻȡɿDoubleֵ - ֵ + /// + [Fact] + public async Task TestToDoubleOrNullAsync_1() { + var result = await _sqlQuery.Select( "Price" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleOrNullAsync(); + Assert.Equal( TestConfig.DoubleValue, result ); + } + + /// + /// ԻȡɿDoubleֵ - ֵ + /// + [Fact] + public async Task TestToDoubleOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToDecimal + + /// + /// ԻȡDecimalֵ + /// + [Fact] + public void TestToDecimal() { + var result = _sqlQuery.Select( "Price" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDecimal(); + Assert.Equal( TestConfig.DecimalValue, result ); + } + + #endregion + + #region ToDecimalAsync + + /// + /// ԻȡDecimalֵ + /// + [Fact] + public async Task TestToDecimalAsync() { + var result = await _sqlQuery.Select( "Price" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalAsync(); + Assert.Equal( TestConfig.DecimalValue, result ); + } + + #endregion + + #region ToDecimalOrNull + + /// + /// ԻȡDecimalֵ - ֵ + /// + [Fact] + public void TestToDecimalOrNull_1() { + var result = _sqlQuery.Select( "Price" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalOrNull(); + Assert.Equal( TestConfig.DecimalValue, result ); + } + + /// + /// ԻȡDecimalֵ - ֵ + /// + [Fact] + public void TestToDecimalOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToDecimalOrNullAsync + + /// + /// ԻȡɿDecimalֵ - ֵ + /// + [Fact] + public async Task TestToDecimalOrNullAsync_1() { + var result = await _sqlQuery.Select( "Price" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalOrNullAsync(); + Assert.Equal( TestConfig.DecimalValue, result ); + } + + /// + /// ԻȡɿDecimalֵ - ֵ + /// + [Fact] + public async Task TestToDecimalOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToDateTime + + /// + /// Իȡֵ + /// + [Fact] + public void TestToDateTime() { + var result = _sqlQuery.Select( "Birthday" ).From( "Customer" ).Where( "CustomerId", 1 ).ToDateTime(); + Assert.Equal( TestConfig.DateTimeValue, result ); + } + + #endregion + + #region ToDateTimeAsync + + /// + /// Իȡֵ + /// + [Fact] + public async Task TestToDateTimeAsync() { + var result = await _sqlQuery.Select( "Birthday" ).From( "Customer" ).Where( "CustomerId", 1 ).ToDateTimeAsync(); + Assert.Equal( TestConfig.DateTimeValue, result ); + } + + #endregion + + #region ToDateTimeOrNull + + /// + /// Իȡɿֵ - ֵ + /// + [Fact] + public void TestToDateTimeOrNull_1() { + var result = _sqlQuery.Select( "Birthday" ).From( "Customer" ).Where( "CustomerId", 1 ).ToDateTimeOrNull(); + Assert.Equal( TestConfig.DateTimeValue, result ); + } + + /// + /// Իȡɿֵ - ֵ + /// + [Fact] + public void TestToDateTimeOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Customer" ).Where( "CustomerId", 1 ).ToDateTimeOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToDateTimeOrNullAsync + + /// + /// Իȡɿֵ - ֵ + /// + [Fact] + public async Task TestToDateTimeOrNullAsync_1() { + var result = await _sqlQuery.Select( "Birthday" ).From( "Customer" ).Where( "CustomerId", 1 ).ToDateTimeOrNullAsync(); + Assert.Equal( TestConfig.DateTimeValue, result ); + } + + /// + /// Իȡɿֵ - ֵ + /// + [Fact] + public async Task TestToDateTimeOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Customer" ).Where( "CustomerId", 1 ).ToDateTimeOrNullAsync(); + Assert.Null( result ); + } + + #endregion + } +} diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/SqlQuery/MySqlQueryTest.Single.cs b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlQuery/MySqlQueryTest.Single.cs new file mode 100644 index 000000000..b6a86e926 --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlQuery/MySqlQueryTest.Single.cs @@ -0,0 +1,52 @@ +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Util.Tests.Models; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlQuery { + /// + /// MySql Sqlѯ - ȡ + /// + public partial class MySqlQueryTest { + /// + /// Իȡʵ + /// + [Fact] + public void TestExecuteSingle() { + var result = _sqlQuery.Select( "ProductId as Id,Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ExecuteSingle(); + Assert.Equal( TestConfig.Id, result.Id ); + Assert.Equal( TestConfig.Value, result.Code ); + } + + /// + /// Իȡʵ + /// + [Fact] + public async Task TestExecuteSingleAsync() { + var result = await _sqlQuery.Select( "ProductId as Id,Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ExecuteSingleAsync(); + Assert.Equal( TestConfig.Id, result.Id ); + Assert.Equal( TestConfig.Value, result.Code ); + } + + /// + /// Իȡʵ + /// + [Fact] + public void TestToEntity() { + var result = _sqlQuery.Select( "ProductId as Id,Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToEntity(); + Assert.Equal( TestConfig.Id, result.Id ); + Assert.Equal( TestConfig.Value, result.Code ); + } + + /// + /// Իȡʵ + /// + [Fact] + public async Task TestToEntityAsync() { + var result = await _sqlQuery.Select( "ProductId as Id,Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToEntityAsync(); + Assert.Equal( TestConfig.Id, result.Id ); + Assert.Equal( TestConfig.Value, result.Code ); + } + } +} diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/SqlQuery/MySqlQueryTest.cs b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlQuery/MySqlQueryTest.cs new file mode 100644 index 000000000..5963b44b9 --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/SqlQuery/MySqlQueryTest.cs @@ -0,0 +1,25 @@ +using Util.Data.Sql; + +namespace Util.Data.Dapper.Tests.SqlQuery { + /// + /// MySql Sqlѯ + /// + public partial class MySqlQueryTest { + /// + /// Sqlִ + /// + private readonly ISqlExecutor _sqlExecutor; + /// + /// Sqlѯ + /// + private readonly ISqlQuery _sqlQuery; + + /// + /// Գʼ + /// + public MySqlQueryTest( ISqlExecutor sqlExecutor, ISqlQuery sqlQuery ) { + _sqlExecutor = sqlExecutor; + _sqlQuery = sqlQuery; + } + } +} diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/Startup.cs b/test/Util.Data.Dapper.MySql.Tests.Integration/Startup.cs new file mode 100644 index 000000000..309564e42 --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/Startup.cs @@ -0,0 +1,54 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Aop; +using Util.Data.Dapper.Metadata; +using Util.Data.Dapper.Sql; +using Util.Data.Dapper.Tests.Infrastructure; +using Util.Data.EntityFrameworkCore; +using Util.Data.Metadata; +using Util.Helpers; +using Util.Sessions; +using Util.Tests.Infrastructure; +using Util.Tests.UnitOfWorks; +using Xunit.DependencyInjection.Logging; + +namespace Util.Data.Dapper.Tests { + /// + /// + /// + public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .AsBuild() + .AddAop() + .AddMySqlQuery( Config.GetConnectionString( "connection" ) ) + .AddMySqlExecutor( Config.GetConnectionString( "connection" ) ) + .AddMySqlUnitOfWork( Config.GetConnectionString( "connection" ) ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddTransient(); + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddSingleton(); + InitDatabase( services ); + } + + /// + /// ʼݿ + /// + private void InitDatabase( IServiceCollection services ) { + var unitOfWork = (MySqlUnitOfWork)services.BuildServiceProvider().GetService(); + unitOfWork.EnsureDeleted(); + unitOfWork.EnsureCreated(); + DatabaseScript.InitProcedures( unitOfWork?.Database ); + } + } +} diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/Util.Data.Dapper.MySql.Tests.Integration.csproj b/test/Util.Data.Dapper.MySql.Tests.Integration/Util.Data.Dapper.MySql.Tests.Integration.csproj new file mode 100644 index 000000000..2652ef18a --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/Util.Data.Dapper.MySql.Tests.Integration.csproj @@ -0,0 +1,43 @@ + + + + $(NetTargetFramework) + false + Util.Data.Dapper.Tests + Util.Data.Dapper.Tests.Startup + + + + + Always + + + Always + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/appsettings.Development.json b/test/Util.Data.Dapper.MySql.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..92f00844f --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Trace" + } + }, + "ConnectionStrings": { + "connection": "server=127.0.0.1;user=root;password=admin;database=Util.Data.Dapper.Test;port=3306;charset=utf8" + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.MySql.Tests.Integration/appsettings.json b/test/Util.Data.Dapper.MySql.Tests.Integration/appsettings.json new file mode 100644 index 000000000..e543ef8de --- /dev/null +++ b/test/Util.Data.Dapper.MySql.Tests.Integration/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + }, + "ConnectionStrings": { + "connection": "server=mysql.common;user=root;password=admin;database=Util.Data.Dapper.Test;port=3306;charset=utf8" + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlExecutor/OracleExecutorTest.Execute.cs b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlExecutor/OracleExecutorTest.Execute.cs new file mode 100644 index 000000000..1d5e0f8e1 --- /dev/null +++ b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlExecutor/OracleExecutorTest.Execute.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// Oracle Sql执行器测试 - 执行Sql测试 + /// + public partial class OracleExecutorTest { + /// + /// 测试执行Sql增删改操作 + /// + [Fact] + public async Task TestExecuteAsync() { + var id = Guid.NewGuid(); + await _sqlExecutor + .AppendLine( "Insert Into \"Product\"(\"ProductId\",\"Code\") " ) + .Append( "Values(:ProductId,:Code)" ) + .AddParam( ":ProductId", id ) + .AddParam( "Code", TestConfig.Value ) + .ExecuteAsync(); + var result = await _sqlExecutor.Select( "Code" ).From( "Product" ).Where( "ProductId", id ).ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + } +} diff --git a/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlExecutor/OracleExecutorTest.Id.cs b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlExecutor/OracleExecutorTest.Id.cs new file mode 100644 index 000000000..a1370e749 --- /dev/null +++ b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlExecutor/OracleExecutorTest.Id.cs @@ -0,0 +1,66 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Helpers; +using Util.Tests.Configs; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// Oracle Sql执行器测试 - 标识列测试 + /// + public partial class OracleExecutorTest { + /// + /// 测试插入Guid主键 + /// + [Fact] + public async Task TestInsert_Guid() { + var id = Guid.NewGuid(); + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + var result = await _sqlExecutor + .Select( "Code" ) + .From( "Product" ) + .Where( "ProductId", id ) + .ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + + /// + /// 测试插入int自增长主键 + /// + [Fact] + public async Task TestInsert_Int() { + var value = Id.Create(); + await _sqlExecutor + .Insert( "Code", "Customer" ) + .Values( value ) + .ExecuteAsync(); + var result = await _sqlExecutor + .Select( "CustomerId" ) + .From( "Customer" ) + .Where( "Code", value ) + .ToIntAsync(); + Assert.True( result > 0 ); + } + + /// + /// 测试插入string主键 + /// + [Fact] + public async Task TestInsert_String() { + var id = Id.Create(); + await _sqlExecutor + .Insert( "OrderId,CustomerName", "Order" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + var result = await _sqlExecutor + .Select( "CustomerName" ) + .From( "Order" ) + .Where( "OrderId", id ) + .ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + } +} diff --git a/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlExecutor/OracleExecutorTest.Insert.cs b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlExecutor/OracleExecutorTest.Insert.cs new file mode 100644 index 000000000..70cb65081 --- /dev/null +++ b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlExecutor/OracleExecutorTest.Insert.cs @@ -0,0 +1,60 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// Oracle Sql执行器测试 - 插入测试 + /// + public partial class OracleExecutorTest { + /// + /// 测试使用Insert Values语句插入一条数据 + /// + [Fact] + public async Task TestInsert_1() { + //插入一条数据 + var id = Guid.NewGuid(); + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //获取值 + var result = await _sqlExecutor + .Select( "Code" ) + .From( "Product" ) + .Where( "ProductId", id ) + .ToStringAsync(); + + //验证 + Assert.Equal( TestConfig.Value, result ); + } + + /// + /// 测试使用Insert Values语句插入2条数据 + /// + [Fact] + public async Task TestInsert_2() { + //插入一条数据 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id, TestConfig.Value ) + .Values( id2, TestConfig.Value ) + .ExecuteAsync(); + + //获取值 + var result = await _sqlExecutor + .AppendSelect( "Count(*)" ) + .From( "Product" ) + .In( "ProductId", new[]{id,id2} ) + .ToIntAsync(); + + //验证 + Assert.Equal( 2, result ); + } + } +} diff --git a/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlExecutor/OracleExecutorTest.Param.cs b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlExecutor/OracleExecutorTest.Param.cs new file mode 100644 index 000000000..42fe66d90 --- /dev/null +++ b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlExecutor/OracleExecutorTest.Param.cs @@ -0,0 +1,55 @@ +using System; +using System.Data; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// Oracle Sql执行器测试 - 执行存储过程参数测试 + /// + public partial class OracleExecutorTest { + /// + /// 测试添加动态参数 + /// + [Fact] + public async Task TestAddParam_Dynamic() { + var id = Guid.NewGuid(); + await _sqlExecutor + .AppendLine( "Insert Into \"Product\"(\"ProductId\",\"Code\") " ) + .Append( "Values(:ProductId,:Code)" ) + .AddDynamicParams( new { ProductId=id, Code= TestConfig.Value } ) + .ExecuteAsync(); + var result = await _sqlExecutor.Select( "Code" ).From( "Product" ).Where( "ProductId", id ).ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + + /// + /// 测试添加参数 - 在执行前添加并获取参数 + /// + [Fact] + public async Task TestAddParam_1() { + var id = Guid.NewGuid(); + + //添加第一个参数 + _sqlExecutor + .AppendLine( "Insert Into \"Product\"(\"ProductId\",\"Code\") " ) + .Append( "Values(:ProductId,:Code)" ) + .AddParam( "ProductId", id ); + + //获取参数 + var param = _sqlExecutor.GetParam( "ProductId" ); + Assert.Equal( id, param ); + + //添加第二个参数并执行 + await _sqlExecutor + .AddParam( "Code", TestConfig.Value ) + .ExecuteAsync(); + + //验证 + var result = await _sqlExecutor.Select( "Code" ).From( "Product" ).Where( "ProductId", id ).ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + } +} diff --git a/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlExecutor/OracleExecutorTest.Transaction.cs b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlExecutor/OracleExecutorTest.Transaction.cs new file mode 100644 index 000000000..d7b82d782 --- /dev/null +++ b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlExecutor/OracleExecutorTest.Transaction.cs @@ -0,0 +1,249 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// Oracle Sql执行器测试 - 事务测试 + /// + public partial class OracleExecutorTest { + /// + /// 测试事务操作 - 不使用事务,第一次操作成功,第二次失败 + /// + [Fact] + public async Task TestTransaction_1() { + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //Code2列名错误,插入Id2失败 + await _sqlExecutor + .Insert( "ProductId,Code,Code2", "Product" ) + .Values( id2, TestConfig.Value, TestConfig.Value ) + .ExecuteAsync(); + } + catch { + // ignored + } + + //断言 + var exists = await _sqlExecutor.From( "Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.True( exists ); + Assert.False( exists2 ); + } + + /// + /// 测试事务操作 - 调用BeginTransaction,CommitTransaction,RollbackTransaction + /// + [Fact] + public async Task TestTransaction_2() { + //开始事务 + _sqlExecutor.BeginTransaction(); + + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //Code2列名错误,插入Id2失败 + await _sqlExecutor + .Insert( "ProductId,Code,Code2", "Product" ) + .Values( id2, TestConfig.Value, TestConfig.Value ) + .ExecuteAsync(); + + //提交事务 + _sqlExecutor.CommitTransaction(); + } + catch { + //回滚事务 + _sqlExecutor.RollbackTransaction(); + } + + //断言 + var exists = await _sqlExecutor.From( "Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.False( exists ); + Assert.False( exists2 ); + } + + /// + /// 测试事务操作 - 使用using调用BeginTransaction + /// + [Fact] + public async Task TestTransaction_3() { + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + using var transaction = _sqlExecutor.BeginTransaction(); + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //Code2列名错误,插入Id2失败 + await _sqlExecutor + .Insert( "ProductId,Code,Code2", "Product" ) + .Values( id2, TestConfig.Value, TestConfig.Value ) + .ExecuteAsync(); + + //提交事务 + transaction.Commit(); + } + catch { + // ignored + } + + //断言 + var exists = await _sqlExecutor.From( "Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.False( exists ); + Assert.False( exists2 ); + } + + /// + /// 测试事务操作 - 外部传入事务 - 回滚 + /// + [Fact] + public async Task TestTransaction_4() { + //Sql执行器实例2开始事务 + _sqlExecutor2.BeginTransaction(); + var transaction = _sqlExecutor2.GetTransaction(); + + //设置Sql执行器实例1事务 + _sqlExecutor.SetTransaction( transaction ); + + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //Code2列名错误,插入Id2失败 + await _sqlExecutor2 + .Insert( "ProductId,Code,Code2", "Product" ) + .Values( id2, TestConfig.Value, TestConfig.Value ) + .ExecuteAsync(); + + //提交事务 + _sqlExecutor.CommitTransaction(); + } + catch { + //回滚事务 + _sqlExecutor.RollbackTransaction(); + } + + //断言 + var exists = await _sqlExecutor.From( "Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.False( exists ); + Assert.False( exists2 ); + } + + /// + /// 测试事务操作 - 外部传入事务 - 成功 + /// + [Fact] + public async Task TestTransaction_5() { + //Sql执行器实例2开始事务 + _sqlExecutor2.BeginTransaction(); + var transaction = _sqlExecutor2.GetTransaction(); + + //设置Sql执行器实例1事务 + _sqlExecutor.SetTransaction( transaction ); + + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //Code2列名错误,插入Id2失败 + await _sqlExecutor2 + .Insert( "ProductId,Code", "Product" ) + .Values( id2, TestConfig.Value ) + .ExecuteAsync(); + + //提交事务 + _sqlExecutor.CommitTransaction(); + } + catch { + //回滚事务 + _sqlExecutor.RollbackTransaction(); + } + + //断言 + var exists = await _sqlExecutor.From( "Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.True( exists ); + Assert.True( exists2 ); + } + + /// + /// 测试事务操作 - 成功提交 + /// + [Fact] + public async Task TestTransaction_6() { + //开始事务 + _sqlExecutor.BeginTransaction(); + + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //插入Id2成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Product" ) + .Values( id2, TestConfig.Value ) + .ExecuteAsync(); + + //提交事务 + _sqlExecutor.CommitTransaction(); + } + catch { + //回滚事务 + _sqlExecutor.RollbackTransaction(); + } + + //断言 + var exists = await _sqlExecutor.From( "Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.True( exists ); + Assert.True( exists2 ); + } + } +} diff --git a/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlExecutor/OracleExecutorTest.cs b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlExecutor/OracleExecutorTest.cs new file mode 100644 index 000000000..06bd8d80e --- /dev/null +++ b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlExecutor/OracleExecutorTest.cs @@ -0,0 +1,31 @@ +using Util.Data.Sql; +using Xunit.Abstractions; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// Oracle Sql执行器测试 + /// + public partial class OracleExecutorTest { + /// + /// 测试输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// Sql执行器 + /// + private readonly ISqlExecutor _sqlExecutor; + /// + /// Sql执行器2 + /// + private readonly ISqlExecutor _sqlExecutor2; + + /// + /// 测试初始化 + /// + public OracleExecutorTest( ITestOutputHelper output, ISqlExecutor sqlExecutor, ISqlExecutor sqlExecutor2 ) { + _output = output; + _sqlExecutor = sqlExecutor; + _sqlExecutor2 = sqlExecutor2; + } + } +} diff --git a/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlQuery/OracleQueryTest.Exists.cs b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlQuery/OracleQueryTest.Exists.cs new file mode 100644 index 000000000..7d7afd8c0 --- /dev/null +++ b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlQuery/OracleQueryTest.Exists.cs @@ -0,0 +1,47 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; + +namespace Util.Data.Dapper.Tests.SqlQuery { + /// + /// Oracle Sqlѯ - жǷڲ + /// + public partial class OracleQueryTest { + /// + /// жǷ - + /// + [Fact] + public void TestExecuteExists_1() { + var result = _sqlQuery.Select("*").From( "Product" ).Where( "ProductId", TestConfig.Id ).ExecuteExists(); + Assert.True( result ); + } + + /// + /// жǷ - + /// + [Fact] + public void TestExecuteExists_2() { + var result = _sqlQuery.From( "Product" ).Where( "ProductId", Guid.NewGuid() ).ExecuteExists(); + Assert.False( result ); + } + + /// + /// жǷ - + /// + [Fact] + public async Task TestExecuteExistsAsync_1() { + var result = await _sqlQuery.From( "Product" ).Where( "ProductId", TestConfig.Id ).ExecuteExistsAsync(); + Assert.True( result ); + } + + /// + /// жǷ - + /// + [Fact] + public async Task TestExecuteExistsAsync_2() { + var result = await _sqlQuery.From( "Product" ).Where( "ProductId", Guid.NewGuid() ).ExecuteExistsAsync(); + Assert.False( result ); + } + } +} diff --git a/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlQuery/OracleQueryTest.Query.cs b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlQuery/OracleQueryTest.Query.cs new file mode 100644 index 000000000..04b328ac0 --- /dev/null +++ b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlQuery/OracleQueryTest.Query.cs @@ -0,0 +1,387 @@ +using System; +using System.Collections; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Helpers; +using Util.Tests.Configs; +using Util.Tests.Models; + +namespace Util.Data.Dapper.Tests.SqlQuery { + /// + /// Oracle Sqlѯ - ѯ + /// + public partial class OracleQueryTest { + + #region ExecuteQuery + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestExecuteQuery_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestExecuteQuery_1"; + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = _sqlQuery.Select().From( "Product" ).In( "ProductId", new[] { id, id2 } ).ExecuteQuery(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => new Guid( t.ProductId ) == id ); + Assert.Contains( result, t => new Guid( t.ProductId ) == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 1Ͳ + /// + [Fact] + public async Task TestExecuteQuery_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestExecuteQuery_2"; + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = _sqlQuery.Select( "ProductId As Id,Code" ).From( "Product" ).In( "ProductId", new[] { id, id2 } ).ExecuteQuery(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id ); + Assert.Contains( result, t => t.Id == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestExecuteQuery_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "OrderItem" ) + .Values( orderItemId, orderId, TestConfig.DecimalValue ) + .ExecuteAsync(); + + ////ȡ + var result = _sqlQuery + .Select( "i.OrderItemId As Id,i.*" ) //OrderItemIdӳΪId,֮OrderItemص + .Select( "o.OrderId As Id,o.*" ) //OrderIdӳΪId,֮ǰOrderItemеϢ,֮Orderе + .From( "OrderItem As i" ) + .LeftJoin( "Order As o" ).On( "o.OrderId", "i.OrderId" ) + .Where( "i.OrderItemId", orderItemId ) + .ExecuteQuery( ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion + + #region ExecuteQueryAsync + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestExecuteQueryAsync_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestExecuteQueryAsync_1"; + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = await _sqlQuery.Select().From( "Product" ).In( "ProductId", new[] { id, id2 } ).ExecuteQueryAsync(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => new Guid( t.ProductId ) == id ); + Assert.Contains( result, t => new Guid( t.ProductId ) == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestExecuteQueryAsync_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestExecuteQueryAsync_2"; + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = await _sqlQuery.Select( "ProductId As Id,Code" ).From( "Product" ).In( "ProductId", new[] { id, id2 } ).ExecuteQueryAsync(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id ); + Assert.Contains( result, t => t.Id == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestExecuteQueryAsync_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "OrderItem" ) + .Values( orderItemId, orderId, TestConfig.DecimalValue ) + .ExecuteAsync(); + + ////ȡ + var result = await _sqlQuery + .Select( "i.OrderItemId As Id,i.*" ) //OrderItemIdӳΪId,֮OrderItemص + .Select( "o.OrderId As Id,o.*" ) //OrderIdӳΪId,֮ǰOrderItemеϢ,֮Orderе + .From( "OrderItem As i" ) + .LeftJoin( "Order As o" ).On( "o.OrderId", "i.OrderId" ) + .Where( "i.OrderItemId", orderItemId ) + .ExecuteQueryAsync( ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion + + #region ToList + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestToList_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestToList_1"; + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = _sqlQuery.Select().From( "Product" ).In( "ProductId", new[] { id, id2 } ).ToList(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => new Guid( t.ProductId ) == id ); + Assert.Contains( result, t => new Guid( t.ProductId ) == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 1Ͳ + /// + [Fact] + public async Task TestToList_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestToList_2"; + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = _sqlQuery.Select( "ProductId As Id,Code" ).From( "Product" ).In( "ProductId", new[] { id, id2 } ).ToList(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id ); + Assert.Contains( result, t => t.Id == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestToList_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "OrderItem" ) + .Values( orderItemId, orderId, TestConfig.DecimalValue ) + .ExecuteAsync(); + + ////ȡ + var result = _sqlQuery + .Select( "i.OrderItemId As Id,i.*" ) //OrderItemIdӳΪId,֮OrderItemص + .Select( "o.OrderId As Id,o.*" ) //OrderIdӳΪId,֮ǰOrderItemеϢ,֮Orderе + .From( "OrderItem As i" ) + .LeftJoin( "Order As o" ).On( "o.OrderId", "i.OrderId" ) + .Where( "i.OrderItemId", orderItemId ) + .ToList( ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion + + #region ToListAsync + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestToListAsync_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestToListAsync_1"; + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = await _sqlQuery.Select().From( "Product" ).In( "ProductId", new[] { id, id2 } ).ToListAsync(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => new Guid( t.ProductId ) == id ); + Assert.Contains( result, t => new Guid( t.ProductId ) == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestToListAsync_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestToListAsync_2"; + await _sqlExecutor.Insert( "ProductId,Code", "Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = await _sqlQuery.Select( "ProductId As Id,Code" ).From( "Product" ).In( "ProductId", new[] { id, id2 } ).ToListAsync(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id ); + Assert.Contains( result, t => t.Id == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestToListAsync_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "OrderItem" ) + .Values( orderItemId, orderId, TestConfig.DecimalValue ) + .ExecuteAsync(); + + ////ȡ + var result = await _sqlQuery + .Select( "i.OrderItemId As Id,i.*" ) //OrderItemIdӳΪId,֮OrderItemص + .Select( "o.OrderId As Id,o.*" ) //OrderIdӳΪId,֮ǰOrderItemеϢ,֮Orderе + .From( "OrderItem As i" ) + .LeftJoin( "Order As o" ).On( "o.OrderId", "i.OrderId" ) + .Where( "i.OrderItemId", orderItemId ) + .ToListAsync( ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion + } +} diff --git a/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlQuery/OracleQueryTest.Scalar.cs b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlQuery/OracleQueryTest.Scalar.cs new file mode 100644 index 000000000..519870bb6 --- /dev/null +++ b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlQuery/OracleQueryTest.Scalar.cs @@ -0,0 +1,628 @@ +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; + +namespace Util.Data.Dapper.Tests.SqlQuery { + /// + /// Oracle Sqlѯ - ֵ + /// + public partial class OracleQueryTest { + + #region ExecuteScalar + + /// + /// Իȡֵ + /// + [Fact] + public void TestExecuteScalar_1() { + var result = _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ExecuteScalar(); + Assert.Equal( TestConfig.Value, result.SafeString() ); + } + + /// + /// Իȡֵ - + /// + [Fact] + public void TestExecuteScalar_2() { + var result = _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ExecuteScalar(); + Assert.Equal( TestConfig.Value, result ); + } + + #endregion + + #region ExecuteScalarAsync + + /// + /// Իȡֵ + /// + [Fact] + public async Task TestExecuteScalarAsync_1() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ExecuteScalarAsync(); + Assert.Equal( TestConfig.Value, result.SafeString() ); + } + + /// + /// Իȡֵ - + /// + [Fact] + public async Task TestExecuteScalarAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ExecuteScalarAsync(); + Assert.Equal( TestConfig.Value, result ); + } + + #endregion + + #region ToStringAsync + + /// + /// Իȡֵַ + /// + [Fact] + public async Task TestToStringAsync() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + + #endregion + + #region ToInt + + /// + /// Իȡ32λֵ + /// + [Fact] + public void TestToInt() { + var result = _sqlQuery.Select( "IntPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToInt(); + Assert.Equal( TestConfig.IntValue, result ); + } + + #endregion + + #region ToIntAsync + + /// + /// Իȡ32λֵ + /// + [Fact] + public async Task TestToIntAsync() { + var result = await _sqlQuery.Select( "IntPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToIntAsync(); + Assert.Equal( TestConfig.IntValue, result ); + } + + #endregion + + #region ToIntOrNull + + /// + /// Իȡ32λֵ - ֵ + /// + [Fact] + public void TestToIntOrNull_1() { + var result = _sqlQuery.Select( "IntPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToIntOrNull(); + Assert.Equal( TestConfig.IntValue, result ); + } + + /// + /// Իȡ32λɿֵ - ֵ + /// + [Fact] + public void TestToIntOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToIntOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToIntOrNullAsync + + /// + /// Իȡ32λɿֵ - ֵ + /// + [Fact] + public async Task TestToIntOrNullAsync_1() { + var result = await _sqlQuery.Select( "IntPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToIntOrNullAsync(); + Assert.Equal( TestConfig.IntValue, result ); + } + + /// + /// Իȡ32λɿֵ - ֵ + /// + [Fact] + public async Task TestToIntOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToIntOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToLong + + /// + /// Իȡ64λֵ + /// + [Fact] + public void TestToLong() { + var result = _sqlQuery.Select( "LongPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToLong(); + Assert.Equal( TestConfig.LongValue, result ); + } + + #endregion + + #region ToLongAsync + + /// + /// Իȡ64λֵ + /// + [Fact] + public async Task TestToLongAsync() { + var result = await _sqlQuery.Select( "LongPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToLongAsync(); + Assert.Equal( TestConfig.LongValue, result ); + } + + #endregion + + #region ToLongOrNull + + /// + /// Իȡ64λɿֵ - ֵ + /// + [Fact] + public void TestToLongOrNull_1() { + var result = _sqlQuery.Select( "LongPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToLongOrNull(); + Assert.Equal( TestConfig.LongValue, result ); + } + + /// + /// Իȡ64λɿֵ - ֵ + /// + [Fact] + public void TestToLongOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToLongOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToLongOrNullAsync + + /// + /// Իȡ64λɿֵ - ֵ + /// + [Fact] + public async Task TestToLongOrNullAsync_1() { + var result = await _sqlQuery.Select( "LongPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToLongOrNullAsync(); + Assert.Equal( TestConfig.LongValue, result ); + } + + /// + /// Իȡ64λɿֵ - ֵ + /// + [Fact] + public async Task TestToLongOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToLongOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToGuid + + /// + /// ԻȡGuidֵ + /// + [Fact] + public void TestToGuid() { + var result = _sqlQuery.Select( "ProductId" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToGuid(); + Assert.Equal( TestConfig.Id, result ); + } + + #endregion + + #region ToGuidAsync + + /// + /// ԻȡGuidֵ + /// + [Fact] + public async Task TestToGuidAsync() { + var result = await _sqlQuery.Select( "ProductId" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToGuidAsync(); + Assert.Equal( TestConfig.Id, result ); + } + + #endregion + + #region ToGuidOrNull + + /// + /// ԻȡɿGuidֵ - ֵ + /// + [Fact] + public void TestToGuidOrNull_1() { + var result = _sqlQuery.Select( "ProductId" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToGuidOrNull(); + Assert.Equal( TestConfig.Id, result ); + } + + /// + /// ԻȡɿGuidֵ - ֵ + /// + [Fact] + public void TestToGuidOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToGuidOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToGuidOrNullAsync + + /// + /// ԻȡɿGuidֵ - ֵ + /// + [Fact] + public async Task TestToGuidOrNullAsync_1() { + var result = await _sqlQuery.Select( "ProductId" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToGuidOrNullAsync(); + Assert.Equal( TestConfig.Id, result ); + } + + /// + /// ԻȡɿGuidֵ - ֵ + /// + [Fact] + public async Task TestToGuidOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToGuidOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToBool + + /// + /// Իȡֵ + /// + [Fact] + public void TestToBool() { + var result = _sqlQuery.Select( "Enabled" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToBool(); + Assert.Equal( TestConfig.BoolValue, result ); + } + + #endregion + + #region ToBoolAsync + + /// + /// Իȡֵ + /// + [Fact] + public async Task TestToBoolAsync() { + var result = await _sqlQuery.Select( "Enabled" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToBoolAsync(); + Assert.Equal( TestConfig.BoolValue, result ); + } + + #endregion + + #region ToBoolOrNull + + /// + /// Իȡɿղֵ - ֵ + /// + [Fact] + public void TestToBoolOrNull_1() { + var result = _sqlQuery.Select( "Enabled" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToBoolOrNull(); + Assert.Equal( TestConfig.BoolValue, result ); + } + + /// + /// Իȡɿղֵ - ֵ + /// + [Fact] + public void TestToBoolOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToBoolOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToBoolOrNullAsync + + /// + /// Իȡɿղֵ - ֵ + /// + [Fact] + public async Task TestToBoolOrNullAsync_1() { + var result = await _sqlQuery.Select( "Enabled" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToBoolOrNullAsync(); + Assert.Equal( TestConfig.BoolValue, result ); + } + + /// + /// Իȡɿղֵ - ֵ + /// + [Fact] + public async Task TestToBoolOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToBoolOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToFloat + + /// + /// ԻȡFloatֵ + /// + [Fact] + public void TestToFloat() { + var result = _sqlQuery.Select( "FloatPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToFloat(); + Assert.Equal( TestConfig.FloatValue, result ); + } + + #endregion + + #region ToFloatAsync + + /// + /// ԻȡFloatֵ + /// + [Fact] + public async Task TestToFloatAsync() { + var result = await _sqlQuery.Select( "FloatPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToFloatAsync(); + Assert.Equal( TestConfig.FloatValue, result ); + } + + #endregion + + #region ToFloatOrNull + + /// + /// ԻȡɿFloatֵ - ֵ + /// + [Fact] + public void TestToFloatOrNull_1() { + var result = _sqlQuery.Select( "FloatPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToFloatOrNull(); + Assert.Equal( TestConfig.FloatValue, result ); + } + + /// + /// ԻȡɿFloatֵ - ֵ + /// + [Fact] + public void TestToFloatOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToFloatOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToFloatOrNullAsync + + /// + /// ԻȡɿFloatֵ - ֵ + /// + [Fact] + public async Task TestToFloatOrNullAsync_1() { + var result = await _sqlQuery.Select( "FloatPrice" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToFloatOrNullAsync(); + Assert.Equal( TestConfig.FloatValue, result ); + } + + /// + /// ԻȡɿFloatֵ - ֵ + /// + [Fact] + public async Task TestToFloatOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToFloatOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToDouble + + /// + /// ԻȡDoubleֵ + /// + [Fact] + public void TestToDouble() { + var result = _sqlQuery.Select( "Price" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDouble(); + Assert.Equal( TestConfig.DoubleValue, result ); + } + + #endregion + + #region ToDoubleAsync + + /// + /// ԻȡDoubleֵ + /// + [Fact] + public async Task TestToDoubleAsync() { + var result = await _sqlQuery.Select( "Price" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleAsync(); + Assert.Equal( TestConfig.DoubleValue, result ); + } + + #endregion + + #region ToDoubleOrNull + + /// + /// ԻȡɿDoubleֵ - ֵ + /// + [Fact] + public void TestToDoubleOrNull_1() { + var result = _sqlQuery.Select( "Price" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleOrNull(); + Assert.Equal( TestConfig.DoubleValue, result ); + } + + /// + /// ԻȡɿDoubleֵ - ֵ + /// + [Fact] + public void TestToDoubleOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToDoubleOrNullAsync + + /// + /// ԻȡɿDoubleֵ - ֵ + /// + [Fact] + public async Task TestToDoubleOrNullAsync_1() { + var result = await _sqlQuery.Select( "Price" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleOrNullAsync(); + Assert.Equal( TestConfig.DoubleValue, result ); + } + + /// + /// ԻȡɿDoubleֵ - ֵ + /// + [Fact] + public async Task TestToDoubleOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToDecimal + + /// + /// ԻȡDecimalֵ + /// + [Fact] + public void TestToDecimal() { + var result = _sqlQuery.Select( "Price" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDecimal(); + Assert.Equal( TestConfig.DecimalValue, result ); + } + + #endregion + + #region ToDecimalAsync + + /// + /// ԻȡDecimalֵ + /// + [Fact] + public async Task TestToDecimalAsync() { + var result = await _sqlQuery.Select( "Price" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalAsync(); + Assert.Equal( TestConfig.DecimalValue, result ); + } + + #endregion + + #region ToDecimalOrNull + + /// + /// ԻȡDecimalֵ - ֵ + /// + [Fact] + public void TestToDecimalOrNull_1() { + var result = _sqlQuery.Select( "Price" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalOrNull(); + Assert.Equal( TestConfig.DecimalValue, result ); + } + + /// + /// ԻȡDecimalֵ - ֵ + /// + [Fact] + public void TestToDecimalOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToDecimalOrNullAsync + + /// + /// ԻȡɿDecimalֵ - ֵ + /// + [Fact] + public async Task TestToDecimalOrNullAsync_1() { + var result = await _sqlQuery.Select( "Price" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalOrNullAsync(); + Assert.Equal( TestConfig.DecimalValue, result ); + } + + /// + /// ԻȡɿDecimalֵ - ֵ + /// + [Fact] + public async Task TestToDecimalOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToDateTime + + /// + /// Իȡֵ + /// + [Fact] + public void TestToDateTime() { + var result = _sqlQuery.Select( "Birthday" ).From( "Customer" ).Where( "CustomerId", 1 ).ToDateTime(); + Assert.Equal( TestConfig.DateTimeValue, result ); + } + + #endregion + + #region ToDateTimeAsync + + /// + /// Իȡֵ + /// + [Fact] + public async Task TestToDateTimeAsync() { + var result = await _sqlQuery.Select( "Birthday" ).From( "Customer" ).Where( "CustomerId", 1 ).ToDateTimeAsync(); + Assert.Equal( TestConfig.DateTimeValue, result ); + } + + #endregion + + #region ToDateTimeOrNull + + /// + /// Իȡɿֵ - ֵ + /// + [Fact] + public void TestToDateTimeOrNull_1() { + var result = _sqlQuery.Select( "Birthday" ).From( "Customer" ).Where( "CustomerId", 1 ).ToDateTimeOrNull(); + Assert.Equal( TestConfig.DateTimeValue, result ); + } + + /// + /// Իȡɿֵ - ֵ + /// + [Fact] + public void TestToDateTimeOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Customer" ).Where( "CustomerId", 1 ).ToDateTimeOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToDateTimeOrNullAsync + + /// + /// Իȡɿֵ - ֵ + /// + [Fact] + public async Task TestToDateTimeOrNullAsync_1() { + var result = await _sqlQuery.Select( "Birthday" ).From( "Customer" ).Where( "CustomerId", 1 ).ToDateTimeOrNullAsync(); + Assert.Equal( TestConfig.DateTimeValue, result ); + } + + /// + /// Իȡɿֵ - ֵ + /// + [Fact] + public async Task TestToDateTimeOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Customer" ).Where( "CustomerId", 1 ).ToDateTimeOrNullAsync(); + Assert.Null( result ); + } + + #endregion + } +} diff --git a/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlQuery/OracleQueryTest.Single.cs b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlQuery/OracleQueryTest.Single.cs new file mode 100644 index 000000000..e124adfe0 --- /dev/null +++ b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlQuery/OracleQueryTest.Single.cs @@ -0,0 +1,51 @@ +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Util.Tests.Models; + +namespace Util.Data.Dapper.Tests.SqlQuery { + /// + /// Oracle Sqlѯ - ȡ + /// + public partial class OracleQueryTest { + /// + /// Իȡʵ + /// + [Fact] + public void TestExecuteSingle() { + var result = _sqlQuery.Select( "ProductId as Id,Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ExecuteSingle(); + Assert.Equal( TestConfig.Id, result.Id ); + Assert.Equal( TestConfig.Value, result.Code ); + } + + /// + /// Իȡʵ + /// + [Fact] + public async Task TestExecuteSingleAsync() { + var result = await _sqlQuery.Select( "ProductId as Id,Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ExecuteSingleAsync(); + Assert.Equal( TestConfig.Id, result.Id ); + Assert.Equal( TestConfig.Value, result.Code ); + } + + /// + /// Իȡʵ + /// + [Fact] + public void TestToEntity() { + var result = _sqlQuery.Select( "ProductId as Id,Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToEntity(); + Assert.Equal( TestConfig.Id, result.Id ); + Assert.Equal( TestConfig.Value, result.Code ); + } + + /// + /// Իȡʵ + /// + [Fact] + public async Task TestToEntityAsync() { + var result = await _sqlQuery.Select( "ProductId as Id,Code" ).From( "Product" ).Where( "ProductId", TestConfig.Id ).ToEntityAsync(); + Assert.Equal( TestConfig.Id, result.Id ); + Assert.Equal( TestConfig.Value, result.Code ); + } + } +} diff --git a/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlQuery/OracleQueryTest.cs b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlQuery/OracleQueryTest.cs new file mode 100644 index 000000000..4a6bdaf58 --- /dev/null +++ b/test/Util.Data.Dapper.Oracle.Tests.Integration/SqlQuery/OracleQueryTest.cs @@ -0,0 +1,25 @@ +using Util.Data.Sql; + +namespace Util.Data.Dapper.Tests.SqlQuery { + /// + /// Oracle Sqlѯ + /// + public partial class OracleQueryTest { + /// + /// Sqlִ + /// + private readonly ISqlExecutor _sqlExecutor; + /// + /// Sqlѯ + /// + private readonly ISqlQuery _sqlQuery; + + /// + /// Գʼ + /// + public OracleQueryTest( ISqlExecutor sqlExecutor, ISqlQuery sqlQuery ) { + _sqlExecutor = sqlExecutor; + _sqlQuery = sqlQuery; + } + } +} diff --git a/test/Util.Data.Dapper.Oracle.Tests.Integration/Startup.cs b/test/Util.Data.Dapper.Oracle.Tests.Integration/Startup.cs new file mode 100644 index 000000000..66123b171 --- /dev/null +++ b/test/Util.Data.Dapper.Oracle.Tests.Integration/Startup.cs @@ -0,0 +1,51 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Aop; +using Util.Data.Dapper.Sql; +using Util.Data.EntityFrameworkCore; +using Util.Helpers; +using Util.Logging.Serilog; +using Util.Sessions; +using Util.Tests.Infrastructure; +using Util.Tests.UnitOfWorks; +using Xunit.DependencyInjection.Logging; + +namespace Util.Data.Dapper.Tests { + /// + /// + /// + public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .AsBuild() + .AddAop() + .AddSerilog() + .AddOracleSqlQuery( Config.GetConnectionString( "connection" ) ) + .AddOracleSqlExecutor( Config.GetConnectionString( "connection" ) ) + .AddOracleUnitOfWork( Config.GetConnectionString( "connection" ) ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddSingleton(); + InitDatabase( services ); + } + + /// + /// ʼݿ + /// + private void InitDatabase( IServiceCollection services ) { + var unitOfWork = (OracleUnitOfWork)services.BuildServiceProvider().GetService(); + unitOfWork.EnsureDeleted(); + unitOfWork.EnsureCreated(); + } + } +} diff --git a/test/Util.Data.Dapper.Oracle.Tests.Integration/Usings.cs b/test/Util.Data.Dapper.Oracle.Tests.Integration/Usings.cs new file mode 100644 index 000000000..8c927eb74 --- /dev/null +++ b/test/Util.Data.Dapper.Oracle.Tests.Integration/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/test/Util.Data.Dapper.Oracle.Tests.Integration/Util.Data.Dapper.Oracle.Tests.Integration.csproj b/test/Util.Data.Dapper.Oracle.Tests.Integration/Util.Data.Dapper.Oracle.Tests.Integration.csproj new file mode 100644 index 000000000..49e3d04df --- /dev/null +++ b/test/Util.Data.Dapper.Oracle.Tests.Integration/Util.Data.Dapper.Oracle.Tests.Integration.csproj @@ -0,0 +1,50 @@ + + + + $(NetTargetFramework) + false + true + Util.Data.Dapper.Tests + Util.Data.Dapper.Tests.Startup + + + + + + + + + + Always + + + Always + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + diff --git a/test/Util.Data.Dapper.Oracle.Tests.Integration/appsettings.Development.json b/test/Util.Data.Dapper.Oracle.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..1ba2c378d --- /dev/null +++ b/test/Util.Data.Dapper.Oracle.Tests.Integration/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Trace" + } + }, + "ConnectionStrings": { + "connection": "User Id=c##test;Password=test;Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1521)));" + } +} diff --git a/test/Util.Data.Dapper.Oracle.Tests.Integration/appsettings.json b/test/Util.Data.Dapper.Oracle.Tests.Integration/appsettings.json new file mode 100644 index 000000000..39722103e --- /dev/null +++ b/test/Util.Data.Dapper.Oracle.Tests.Integration/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + }, + "ConnectionStrings": { + "connection": "" + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/Infrastructure/DatabaseScript.cs b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/Infrastructure/DatabaseScript.cs new file mode 100644 index 000000000..0ed0c9cef --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/Infrastructure/DatabaseScript.cs @@ -0,0 +1,125 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Util.Data.Dapper.Tests.Infrastructure { + /// + /// 数据库初始化脚本 + /// + public class DatabaseScript { + /// + /// 初始化 + /// + public static void InitProcedures( DatabaseFacade database ) { + CreateGetProductCodeFunction( database ); + CreateGetProductFunction( database ); + CreateGetProductsFunction( database ); + CreateGetProductCodeOutputProcedure( database ); + CreateInsertProductProcedure( database ); + } + + /// + /// 创建Func_GetProductCode函数 + /// + private static void CreateGetProductCodeFunction( DatabaseFacade database ) { + var sql = @" + Create Or Replace Function ""Products"".""Func_GetProductCode""( + ""productId"" uuid + ) + Returns varchar + As $$ + Declare + code varchar; + Begin + Select ""Code"" Into code + From ""Products"".""Product"" + Where ""ProductId"" = ""productId""; + Return code; + End;$$ + Language plpgsql + "; + database.ExecuteSqlRaw( sql ); + } + + /// + /// 创建Func_GetProduct函数 + /// + private static void CreateGetProductFunction( DatabaseFacade database ) { + var sql = @" + Create Or Replace Function ""Products"".""Func_GetProduct""( + ""productId"" uuid + ) + Returns setof ""Products"".""Product"" + As $$ + Begin + Return Query + Select * + From ""Products"".""Product"" + Where ""ProductId"" = ""productId""; + End;$$ + Language plpgsql + "; + database.ExecuteSqlRaw( sql ); + } + + /// + /// 创建Func_GetProducts函数 + /// + private static void CreateGetProductsFunction( DatabaseFacade database ) { + var sql = @" + Create Or Replace Function ""Products"".""Func_GetProducts""( + ""productId"" uuid, + ""productId2"" uuid + ) + Returns Table( ""Id"" uuid,""Code"" varchar ) + As $$ + Begin + Return Query + Select p.""ProductId"" As ""Id"", p.""Code"" + From ""Products"".""Product"" As p + Where ""ProductId"" in ( ""productId"",""productId2""); + End;$$ + Language plpgsql + "; + database.ExecuteSqlRaw( sql ); + } + + /// + /// 创建Proc_GetProductCode_Output存储过程 + /// + private static void CreateGetProductCodeOutputProcedure( DatabaseFacade database ) { + var sql = @" + Create Or Replace Procedure ""Products"".""Proc_GetProductCode_Output""( + ""productId"" uuid, + ""code"" in out varchar + ) + As $$ + Begin + Select ""Code"" into code + From ""Products"".""Product"" + Where ""ProductId"" = ""productId""; + End;$$ + Language plpgsql + "; + database.ExecuteSqlRaw( sql ); + } + + /// + /// 创建Proc_InsertProduct存储过程 + /// + private static void CreateInsertProductProcedure( DatabaseFacade database ) { + var sql = @" + Create Or Replace Procedure ""Products"".""Proc_InsertProduct""( + ""productId"" uuid, + ""code"" varchar + ) + As $$ + Begin + Insert Into ""Products"".""Product""(""ProductId"",""Code"") + Values( ""productId"",code ); + End;$$ + Language plpgsql + "; + database.ExecuteSqlRaw( sql ); + } + } +} diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/Metadata/PostgreSqlMetadataServiceTest.cs b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/Metadata/PostgreSqlMetadataServiceTest.cs new file mode 100644 index 000000000..0127c19f2 --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/Metadata/PostgreSqlMetadataServiceTest.cs @@ -0,0 +1,106 @@ +using System.Linq; +using System.Threading.Tasks; +using Util.Data.Metadata; +using Xunit; + +namespace Util.Data.Dapper.Tests.Metadata { + /// + /// PostgreSql数据库元数据服务测试 + /// + public class PostgreSqlMetadataServiceTest { + /// + /// 数据库元信息服务 + /// + private readonly IMetadataService _metadataService; + + /// + /// 初始化 + /// + public PostgreSqlMetadataServiceTest( IMetadataService service ) { + _metadataService = service; + } + + /// + /// 测试获取数据库名称 + /// + [Fact] + public async Task TestDatabaseName() { + var info = await _metadataService.GetDatabaseInfoAsync(); + Assert.Equal( "Util.Data.Dapper.Test", info.Name ); + } + + /// + /// 测试获取表信息 + /// + [Fact] + public async Task TestTableInfo() { + var info = await _metadataService.GetDatabaseInfoAsync(); + var table = info.Tables.FirstOrDefault( t => t.Name == "Product" ); + Assert.NotNull( table ); + Assert.Equal( "Products", table.Schema ); + Assert.Equal( "产品", table.Comment ); + } + + /// + /// 测试获取主键信息 + /// + [Fact] + public async Task TestPrimaryKey() { + var info = await _metadataService.GetDatabaseInfoAsync(); + var table = info.Tables.First( t => t.Name == "OrderItem" ); + var pkColumn = table.Columns.FirstOrDefault( t => t.Name == "OrderItemId" ); + Assert.NotNull( pkColumn ); + Assert.True( pkColumn.IsPrimaryKey ); + var fkColumn = table.Columns.FirstOrDefault( t => t.Name == "OrderId" ); + Assert.NotNull( fkColumn ); + Assert.False( fkColumn.IsPrimaryKey ); + } + + /// + /// 测试获取自增信息 + /// + [Fact] + public async Task TestAutoIncrement() { + var info = await _metadataService.GetDatabaseInfoAsync(); + var table = info.Tables.FirstOrDefault( t => t.Name == "Customer" ); + Assert.NotNull( table ); + var column = table.Columns.FirstOrDefault( t => t.Name == "CustomerId" ); + Assert.NotNull( column ); + Assert.True( column.IsAutoIncrement ); + Assert.Equal( "int4", column.DataType ); + } + + /// + /// 测试获取数值精度信息 + /// + [Fact] + public async Task TestPrecision() { + var info = await _metadataService.GetDatabaseInfoAsync(); + var table = info.Tables.FirstOrDefault( t => t.Name == "Product" ); + Assert.NotNull( table ); + var column = table.Columns.FirstOrDefault( t => t.Name == "Price" ); + Assert.NotNull( column ); + Assert.Equal( 12, column.Precision ); + Assert.Equal( 2, column.Scale ); + } + + /// + /// 测试获取字符串列信息 + /// + [Fact] + public async Task TestStringInfo() { + var info = await _metadataService.GetDatabaseInfoAsync(); + var table = info.Tables.FirstOrDefault( t => t.Name == "Product" ); + Assert.NotNull( table ); + var count = table.Columns.Count( t => t.Name == "Code" ); + Assert.Equal( 1, count ); + var column = table.Columns.FirstOrDefault( t => t.Name == "Code" ); + Assert.NotNull( column ); + Assert.Equal( "产品编码", column.Comment ); + Assert.False( column.IsPrimaryKey ); + Assert.True( column.IsNullable ); + Assert.Equal( "varchar", column.DataType ); + Assert.Equal( 50, column.Length ); + } + } +} diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlBuilders/PostgreSqlExistsSqlBuilderTest.cs b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlBuilders/PostgreSqlExistsSqlBuilderTest.cs new file mode 100644 index 000000000..aa1265beb --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlBuilders/PostgreSqlExistsSqlBuilderTest.cs @@ -0,0 +1,48 @@ +using System.Text; +using Util.Data.Dapper.Sql.Builders; +using Util.Data.Sql; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Data.Dapper.Tests.SqlBuilders { + /// + /// 测试判断是否存在的Sql生成器 + /// + public class PostgreSqlExistsSqlBuilderTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + + /// + /// 测试初始化 + /// + public PostgreSqlExistsSqlBuilderTest( ITestOutputHelper output ) { + _output = output; + } + + /// + /// 测试生成的Sql + /// + [Fact] + public void TestToSql() { + var sql = new StringBuilder(); + sql.AppendLine( "Select Case" ); + sql.AppendLine( " When Exists (" ); + sql.AppendLine( "Select 1 " ); + sql.Append( "From \"b\"" ); + sql.AppendLine( ")" ); + sql.AppendLine( " Then Cast(1 As Bit)" ); + sql.AppendLine( " Else Cast(0 As Bit) " ); + sql.Append( "End" ); + + var subQuery = new PostgreSqlBuilder() + .Select("a") + .From( "b" ); + var sqlBuilder = new PostgreSqlExistsSqlBuilder( subQuery ); + var result = sqlBuilder.GetSql(); + _output.WriteLine( result ); + Assert.Equal( sql.ToString(),result ); + } + } +} diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/PostgreSqlExecutorTest.Execute.cs b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/PostgreSqlExecutorTest.Execute.cs new file mode 100644 index 000000000..294c20928 --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/PostgreSqlExecutorTest.Execute.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// PostgreSql Sql执行器测试 - 执行Sql测试 + /// + public partial class PostgreSqlExecutorTest { + /// + /// 测试执行Sql增删改操作 + /// + [Fact] + public async Task TestExecuteAsync() { + var id = Guid.NewGuid(); + await _sqlExecutor + .AppendLine( "Insert Into \"Products\".\"Product\"(\"ProductId\",\"Code\") " ) + .Append( "Values(@ProductId,@Code)" ) + .AddParam( "@ProductId", id ) + .AddParam( "Code", TestConfig.Value ) + .ExecuteAsync(); + var result = await _sqlExecutor.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", id ).ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + } +} diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/PostgreSqlExecutorTest.Id.cs b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/PostgreSqlExecutorTest.Id.cs new file mode 100644 index 000000000..fe80e50f0 --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/PostgreSqlExecutorTest.Id.cs @@ -0,0 +1,67 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Helpers; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// PostgreSql Sql执行器测试 - 标识列测试 + /// + public partial class PostgreSqlExecutorTest { + /// + /// 测试插入Guid主键 + /// + [Fact] + public async Task TestInsert_Guid() { + var id = Guid.NewGuid(); + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + var result = await _sqlExecutor + .Select( "Code" ) + .From( "Products.Product" ) + .Where( "ProductId", id ) + .ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + + /// + /// 测试插入int自增长主键 + /// + [Fact] + public async Task TestInsert_Int() { + var value = Id.Create(); + await _sqlExecutor + .Insert( "Code", "Customers.Customer" ) + .Values( value ) + .ExecuteAsync(); + var result = await _sqlExecutor + .Select( "CustomerId" ) + .From( "Customers.Customer" ) + .Where( "Code", value ) + .ToIntAsync(); + Assert.True( result > 0 ); + } + + /// + /// 测试插入string主键 + /// + [Fact] + public async Task TestInsert_String() { + var id = Id.Create(); + await _sqlExecutor + .Insert( "OrderId,CustomerName", "Sales.Order" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + var result = await _sqlExecutor + .Select( "CustomerName" ) + .From( "Sales.Order" ) + .Where( "OrderId", id ) + .ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + } +} diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/PostgreSqlExecutorTest.Insert.cs b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/PostgreSqlExecutorTest.Insert.cs new file mode 100644 index 000000000..f93130ca9 --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/PostgreSqlExecutorTest.Insert.cs @@ -0,0 +1,83 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// PostgreSql Sql执行器测试 - 插入测试 + /// + public partial class PostgreSqlExecutorTest { + /// + /// 测试使用Insert Values语句插入一条数据 + /// + [Fact] + public async Task TestInsert_1() { + //插入一条数据 + var id = Guid.NewGuid(); + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //获取值 + var result = await _sqlExecutor + .Select( "Code" ) + .From( "Products.Product" ) + .Where( "ProductId", id ) + .ToStringAsync(); + + //验证 + Assert.Equal( TestConfig.Value, result ); + } + + /// + /// 测试使用Insert Values语句插入2条数据 + /// + [Fact] + public async Task TestInsert_2() { + //插入一条数据 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .Values( id, TestConfig.Value ) + .Values( id2, TestConfig.Value ) + .ExecuteAsync(); + + //获取值 + var result = await _sqlExecutor + .AppendSelect( "Count(*)" ) + .From( "Products.Product" ) + .In( "ProductId", new[]{id,id2} ) + .ToIntAsync(); + + //验证 + Assert.Equal( 2, result ); + } + + /// + /// 测试使用Insert Select语句插入 + /// + [Fact] + public async Task TestInsert_3() { + //用Insert Select插入一条数据 + var id = Guid.NewGuid(); + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .AppendSelect( $"'{id}','{TestConfig.Value}'" ) + .ExecuteAsync(); + + //获取值 + var result = await _sqlExecutor + .Select( "Code" ) + .From( "Products.Product" ) + .Where( "ProductId", id ) + .ToStringAsync(); + + //验证 + Assert.Equal( TestConfig.Value, result ); + } + } +} diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/PostgreSqlExecutorTest.Param.cs b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/PostgreSqlExecutorTest.Param.cs new file mode 100644 index 000000000..ecea40b08 --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/PostgreSqlExecutorTest.Param.cs @@ -0,0 +1,86 @@ +using System; +using System.Data; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// PostgreSql Sql执行器测试 - 执行存储过程参数测试 + /// + public partial class PostgreSqlExecutorTest { + /// + /// 测试添加动态参数 + /// + [Fact] + public async Task TestAddParam_Dynamic() { + var id = Guid.NewGuid(); + await _sqlExecutor + .AppendLine( "Insert Into \"Products\".\"Product\"(\"ProductId\",\"Code\") " ) + .Append( "Values(@ProductId,@Code)" ) + .AddDynamicParams( new { ProductId=id, Code= TestConfig.Value } ) + .ExecuteAsync(); + var result = await _sqlExecutor.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", id ).ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + + /// + /// 测试添加和获取输出参数 + /// 说明 + /// 1. PostgreSql数据库的存储过程需要使用call进行调用 + /// 2. 存储过程名称如果包含大写,需要使用转义符"",也可以使用[],会自动转换为"" + /// 3. 调用存储过程时,输出参数值传入null + /// 4. 定义存储过程的输出参数需要使用in out参数 + /// + [Fact] + public async Task TestAddParam_Output() { + //定义变量 + var id = Guid.NewGuid(); + + //插入数据 + await _sqlExecutor + .AppendLine( "Insert Into \"Products\".\"Product\"(\"ProductId\",\"Code\") " ) + .Append( "Values(@ProductId,@Code)" ) + .AddParam( "@ProductId", id ) + .AddParam( "Code", TestConfig.Value ) + .ExecuteAsync(); + + //执行存储过程 - 添加输出参数 + await _sqlExecutor + .AddParam( "productId", id ) + .AddParam( "code",DbType.String,ParameterDirection.InputOutput ) + .ExecuteProcedureAsync( "call [Products].[Proc_GetProductCode_Output](@productId,null)" ); + + //验证 - 使用GetParam获取输出参数 + Assert.Equal( TestConfig.Value, _sqlExecutor.GetParam( "code" ) ); + } + + /// + /// 测试添加参数 - 在执行前添加并获取参数 + /// + [Fact] + public async Task TestAddParam_1() { + var id = Guid.NewGuid(); + + //添加第一个参数 + _sqlExecutor + .AppendLine( "Insert Into \"Products\".\"Product\"(\"ProductId\",\"Code\") " ) + .Append( "Values(@ProductId,@Code)" ) + .AddParam( "@ProductId", id ); + + //获取参数 + var param = _sqlExecutor.GetParam( "ProductId" ); + Assert.Equal( id, param ); + + //添加第二个参数并执行 + await _sqlExecutor + .AddParam( "Code", TestConfig.Value ) + .ExecuteAsync(); + + //验证 + var result = await _sqlExecutor.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", id ).ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + } +} diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/PostgreSqlExecutorTest.Procedure.cs b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/PostgreSqlExecutorTest.Procedure.cs new file mode 100644 index 000000000..80cd10e83 --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/PostgreSqlExecutorTest.Procedure.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// PostgreSql Sql执行器测试 - 执行存储过程测试 + /// + public partial class PostgreSqlExecutorTest { + /// + /// 测试执行存储过程增删改操作 + /// + [Fact] + public async Task TestExecuteProcedureScalarAsync() { + //定义变量 + var id = Guid.NewGuid(); + + //插入数据,参数无论是否加@前缀均可 + await _sqlExecutor + .AddParam( "@productId", id ) + .AddParam( "code", TestConfig.Value ) + .ExecuteProcedureAsync( "call [Products].[Proc_InsertProduct](@productId,@code)" ); + + //获取IntValue值 + var result = await _sqlExecutor + .AddParam( "productId", id ) + .ExecuteProcedureScalarAsync( "Products.Func_GetProductCode" ); + + //验证 + Assert.Equal( TestConfig.Value, result ); + } + } +} diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/PostgreSqlExecutorTest.cs b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/PostgreSqlExecutorTest.cs new file mode 100644 index 000000000..ba77b17ea --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/PostgreSqlExecutorTest.cs @@ -0,0 +1,31 @@ +using Util.Data.Sql; +using Xunit.Abstractions; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// PostgreSql Sql执行器测试 + /// + public partial class PostgreSqlExecutorTest { + /// + /// 测试输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// Sql执行器 + /// + private readonly ISqlExecutor _sqlExecutor; + /// + /// Sql执行器2 + /// + private readonly ISqlExecutor _sqlExecutor2; + + /// + /// 测试初始化 + /// + public PostgreSqlExecutorTest( ITestOutputHelper output, ISqlExecutor sqlExecutor, ISqlExecutor sqlExecutor2 ) { + _output = output; + _sqlExecutor = sqlExecutor; + _sqlExecutor2 = sqlExecutor2; + } + } +} diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Transaction.cs b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Transaction.cs new file mode 100644 index 000000000..79969b19d --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Transaction.cs @@ -0,0 +1,206 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor { + /// + /// PostgreSql Sql执行器测试 - 事务测试 + /// + public partial class PostgreSqlExecutorTest { + /// + /// 测试事务操作 - 不使用事务,第一次操作成功,第二次失败 + /// + [Fact] + public async Task TestTransaction_1() { + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //Code2列名错误,插入Id2失败 + await _sqlExecutor + .Insert( "ProductId,Code,Code2", "Products.Product" ) + .Values( id2, TestConfig.Value, TestConfig.Value ) + .ExecuteAsync(); + } + catch { + // ignored + } + + //断言 + var exists = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.True( exists ); + Assert.False( exists2 ); + } + + /// + /// 测试事务操作 - 调用BeginTransaction,CommitTransaction,RollbackTransaction + /// + [Fact] + public async Task TestTransaction_2() { + //开始事务 + _sqlExecutor.BeginTransaction(); + + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //Code2列名错误,插入Id2失败 + await _sqlExecutor + .Insert( "ProductId,Code,Code2", "Products.Product" ) + .Values( id2, TestConfig.Value, TestConfig.Value ) + .ExecuteAsync(); + + //提交事务 + _sqlExecutor.CommitTransaction(); + } + catch { + //回滚事务 + _sqlExecutor.RollbackTransaction(); + } + + //断言 + var exists = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.False( exists ); + Assert.False( exists2 ); + } + + /// + /// 测试事务操作 - 使用using调用BeginTransaction + /// + [Fact] + public async Task TestTransaction_3() { + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + using var transaction = _sqlExecutor.BeginTransaction(); + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //Code2列名错误,插入Id2失败 + await _sqlExecutor + .Insert( "ProductId,Code,Code2", "Products.Product" ) + .Values( id2, TestConfig.Value, TestConfig.Value ) + .ExecuteAsync(); + + //提交事务 + transaction.Commit(); + } + catch { + // ignored + } + + //断言 + var exists = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.False( exists ); + Assert.False( exists2 ); + } + + /// + /// 测试事务操作 - 外部传入事务 + /// + [Fact] + public async Task TestTransaction_4() { + //Sql执行器实例2开始事务 + _sqlExecutor2.BeginTransaction(); + var transaction = _sqlExecutor2.GetTransaction(); + + //设置Sql执行器实例1事务 + _sqlExecutor.SetTransaction( transaction ); + + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //Code2列名错误,插入Id2失败 + await _sqlExecutor2 + .Insert( "ProductId,Code,Code2", "Products.Product" ) + .Values( id2, TestConfig.Value, TestConfig.Value ) + .ExecuteAsync(); + + //提交事务 + _sqlExecutor.CommitTransaction(); + } + catch { + //回滚事务 + _sqlExecutor.RollbackTransaction(); + } + + //断言 + var exists = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.False( exists ); + Assert.False( exists2 ); + } + + /// + /// 测试事务操作 - 成功提交 + /// + [Fact] + public async Task TestTransaction_5() { + //开始事务 + _sqlExecutor.BeginTransaction(); + + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //插入Id2成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .Values( id2, TestConfig.Value ) + .ExecuteAsync(); + + //提交事务 + _sqlExecutor.CommitTransaction(); + } + catch { + //回滚事务 + _sqlExecutor.RollbackTransaction(); + } + + //断言 + var exists = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.True( exists ); + Assert.True( exists2 ); + } + } +} diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlQuery/PostgreSqlQueryTest.Exists.cs b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlQuery/PostgreSqlQueryTest.Exists.cs new file mode 100644 index 000000000..44b85caeb --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlQuery/PostgreSqlQueryTest.Exists.cs @@ -0,0 +1,48 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlQuery { + /// + /// PostgreSql Sqlѯ - жǷڲ + /// + public partial class PostgreSqlQueryTest { + /// + /// жǷ - + /// + [Fact] + public void TestExecuteExists_1() { + var result = _sqlQuery.From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ExecuteExists(); + Assert.True( result ); + } + + /// + /// жǷ - + /// + [Fact] + public void TestExecuteExists_2() { + var result = _sqlQuery.From( "Products.Product" ).Where( "ProductId", Guid.NewGuid() ).ExecuteExists(); + Assert.False( result ); + } + + /// + /// жǷ - + /// + [Fact] + public async Task TestExecuteExistsAsync_1() { + var result = await _sqlQuery.From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ExecuteExistsAsync(); + Assert.True( result ); + } + + /// + /// жǷ - + /// + [Fact] + public async Task TestExecuteExistsAsync_2() { + var result = await _sqlQuery.From( "Products.Product" ).Where( "ProductId", Guid.NewGuid() ).ExecuteExistsAsync(); + Assert.False( result ); + } + } +} diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlQuery/PostgreSqlQueryTest.Procedure.cs b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlQuery/PostgreSqlQueryTest.Procedure.cs new file mode 100644 index 000000000..fd9c28cd1 --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlQuery/PostgreSqlQueryTest.Procedure.cs @@ -0,0 +1,233 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Util.Tests.Models; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlQuery { + /// + /// PostgreSql Sqlѯ - ִд洢̲ + /// ˵: + /// 1. PostgreSqlĴ洢̲ܷ + /// 2. PostgreSqlĺԷ,ܹʹExecuteProcedureXXXXú + /// 3. ҪرעPostgreSqlʹ洢дСд + /// + public partial class PostgreSqlQueryTest { + + #region ExecuteProcedureScalar + + /// + /// ִд洢̻ȡֵ + /// + [Fact] + public void TestExecuteProcedureScalar_1() { + //ִд洢 + var result = _sqlQuery + .AddParam( "productId", TestConfig.Id ) + .ExecuteProcedureScalar( "Products.Func_GetProductCode" ); + + // + Assert.Equal( TestConfig.Value, result ); + Assert.Equal( "\"Products\".\"Func_GetProductCode\"", _sqlQuery.PreviousSql.GetSql() ); + } + + /// + /// ִд洢̻ȡֵ - ͷ + /// + [Fact] + public void TestExecuteProcedureScalar_2() { + //ִд洢 + var result = _sqlQuery + .AddParam( "productId", TestConfig.Id ) + .ExecuteProcedureScalar( "Products.Func_GetProductCode" ); + + // + Assert.Equal( TestConfig.Value, result ); + Assert.Equal( "\"Products\".\"Func_GetProductCode\"", _sqlQuery.PreviousSql.GetSql() ); + } + + #endregion + + #region ExecuteProcedureScalarAsync + + /// + /// ִд洢̻ȡֵ + /// + [Fact] + public async Task TestExecuteProcedureScalarAsync_1() { + //ִд洢 + var result = await _sqlQuery + .AddParam( "productId", TestConfig.Id ) + .ExecuteProcedureScalarAsync( "Products.Func_GetProductCode" ); + + // + Assert.Equal( TestConfig.Value, result ); + Assert.Equal( "\"Products\".\"Func_GetProductCode\"", _sqlQuery.PreviousSql.GetSql() ); + } + + /// + /// ִд洢̻ȡֵ - ͷ + /// + [Fact] + public async Task TestExecuteProcedureScalarAsync_2() { + //ִд洢 + var result = await _sqlQuery + .AddParam( "productId", TestConfig.Id ) + .ExecuteProcedureScalarAsync( "Products.Func_GetProductCode" ); + + // + Assert.Equal( TestConfig.Value, result ); + Assert.Equal( "\"Products\".\"Func_GetProductCode\"", _sqlQuery.PreviousSql.GetSql() ); + } + + #endregion + + #region ExecuteProcedureSingle + + /// + /// ִд洢̻ȡʵ + /// + [Fact] + public void TestExecuteProcedureSingle() { + //ִд洢 + var result = _sqlQuery + .AddParam( "productId", TestConfig.Id ) + .ExecuteProcedureSingle( "Products.Func_GetProduct" ); + + // + Assert.Equal( TestConfig.Value, result.Code ); + Assert.Equal( "\"Products\".\"Func_GetProduct\"", _sqlQuery.PreviousSql.GetSql() ); + } + + #endregion + + #region ExecuteProcedureSingleAsync + + /// + /// ִд洢̻ȡʵ + /// + [Fact] + public async Task TestExecuteProcedureSingleAsync() { + //ִд洢 + var result = await _sqlQuery + .AddParam( "productId", TestConfig.Id ) + .ExecuteProcedureSingleAsync( "Products.Func_GetProduct" ); + + // + Assert.Equal( TestConfig.Value, result.Code ); + Assert.Equal( "\"Products\".\"Func_GetProduct\"", _sqlQuery.PreviousSql.GetSql() ); + } + + #endregion + + #region ExecuteProcedureQuery + + /// + /// ִд洢̻ȡʵ弯 + /// + [Fact] + public async Task TestExecuteProcedureQuery_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, "a" ) + .Values( id2, "b" ) + .ExecuteAsync(); + + //ִд洢 + var result = _sqlQuery + .AddParam( "productId", id ) + .AddParam( "productId2", id2 ) + .ExecuteProcedureQuery( "Products.Func_GetProducts" ); + + // + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id && t.Code == "a" ); + Assert.Contains( result, t => t.Id == id2 && t.Code == "b" ); + Assert.Equal( "\"Products\".\"Func_GetProducts\"", _sqlQuery.PreviousSql.GetSql() ); + } + + /// + /// Իȡʵ弯 - 1Ͳ + /// + [Fact] + public async Task TestExecuteProcedureQuery_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, "a" ) + .Values( id2, "b" ) + .ExecuteAsync(); + + //ִд洢 + var result = _sqlQuery + .AddParam( "productId", id ) + .AddParam( "productId2", id2 ) + .ExecuteProcedureQuery( "Products.Func_GetProducts" ); + + // + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id && t.Code == "a" ); + Assert.Contains( result, t => t.Id == id2 && t.Code == "b" ); + Assert.Equal( "\"Products\".\"Func_GetProducts\"", _sqlQuery.PreviousSql.GetSql() ); + } + + #endregion + + #region ExecuteProcedureQueryAsync + + /// + /// ִд洢̻ȡʵ弯 + /// + [Fact] + public async Task TestExecuteProcedureQueryAsync_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, "a" ) + .Values( id2, "b" ) + .ExecuteAsync(); + + //ִд洢 + var result = await _sqlQuery + .AddDynamicParams( new{ productId=id, productId2=id2 } ) + .ExecuteProcedureQueryAsync( "Products.Func_GetProducts" ); + + // + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id && t.Code == "a" ); + Assert.Contains( result, t => t.Id == id2 && t.Code == "b" ); + } + + /// + /// Իȡʵ弯 - 1Ͳ + /// + [Fact] + public async Task TestExecuteProcedureQueryAsync_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, "a" ) + .Values( id2, "b" ) + .ExecuteAsync(); + + //ִд洢 + var result = await _sqlQuery + .AddParam( "productId", id ) + .AddParam( "productId2", id2 ) + .ExecuteProcedureQueryAsync( "Products.Func_GetProducts" ); + + // + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id && t.Code == "a" ); + Assert.Contains( result, t => t.Id == id2 && t.Code == "b" ); + } + + #endregion + } +} diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlQuery/PostgreSqlQueryTest.Query.cs b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlQuery/PostgreSqlQueryTest.Query.cs new file mode 100644 index 000000000..e072f291f --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlQuery/PostgreSqlQueryTest.Query.cs @@ -0,0 +1,388 @@ +using System; +using System.Collections; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Helpers; +using Util.Tests.Configs; +using Util.Tests.Models; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlQuery { + /// + /// PostgreSql Sqlѯ - ѯ + /// + public partial class PostgreSqlQueryTest { + + #region ExecuteQuery + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestExecuteQuery_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestExecuteQuery_1"; + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = _sqlQuery.Select().From( "Products.Product" ).In( "ProductId", new[] { id, id2 } ).ExecuteQuery(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.ProductId == id ); + Assert.Contains( result, t => t.ProductId == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 1Ͳ + /// + [Fact] + public async Task TestExecuteQuery_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestExecuteQuery_2"; + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = _sqlQuery.Select("ProductId As Id,Code").From( "Products.Product" ).In( "ProductId", new[] { id, id2 } ).ExecuteQuery(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id ); + Assert.Contains( result, t => t.Id == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestExecuteQuery_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Sales.Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "Sales.OrderItem" ) + .Values( orderItemId, orderId,TestConfig.DecimalValue ) + .ExecuteAsync(); + + ////ȡ + var result = _sqlQuery + .Select( "i.OrderItemId As Id,i.*" ) //OrderItemIdӳΪId,֮OrderItemص + .Select( "o.OrderId As Id,o.*" ) //OrderIdӳΪId,֮ǰOrderItemеϢ,֮Orderе + .From( "Sales.OrderItem As i" ) + .LeftJoin( "Sales.Order As o" ).On( "o.OrderId", "i.OrderId" ) + .Where( "i.OrderItemId", orderItemId ) + .ExecuteQuery( ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion + + #region ExecuteQueryAsync + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestExecuteQueryAsync_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestExecuteQueryAsync_1"; + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = await _sqlQuery.Select().From( "Products.Product" ).In( "ProductId", new[] { id, id2 } ).ExecuteQueryAsync(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.ProductId == id ); + Assert.Contains( result, t => t.ProductId == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestExecuteQueryAsync_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestExecuteQueryAsync_2"; + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = await _sqlQuery.Select( "ProductId As Id,Code" ).From( "Products.Product" ).In( "ProductId", new[] { id, id2 } ).ExecuteQueryAsync(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id ); + Assert.Contains( result, t => t.Id == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestExecuteQueryAsync_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Sales.Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "Sales.OrderItem" ) + .Values( orderItemId, orderId, TestConfig.DecimalValue ) + .ExecuteAsync(); + + ////ȡ + var result = await _sqlQuery + .Select( "i.OrderItemId As Id,i.*" ) //OrderItemIdӳΪId,֮OrderItemص + .Select( "o.OrderId As Id,o.*" ) //OrderIdӳΪId,֮ǰOrderItemеϢ,֮Orderе + .From( "Sales.OrderItem As i" ) + .LeftJoin( "Sales.Order As o" ).On( "o.OrderId", "i.OrderId" ) + .Where( "i.OrderItemId", orderItemId ) + .ExecuteQueryAsync( ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion + + #region ToList + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestToList_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestToList_1"; + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = _sqlQuery.Select().From( "Products.Product" ).In( "ProductId", new[] { id, id2 } ).ToList(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.ProductId == id ); + Assert.Contains( result, t => t.ProductId == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 1Ͳ + /// + [Fact] + public async Task TestToList_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestToList_2"; + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = _sqlQuery.Select( "ProductId As Id,Code" ).From( "Products.Product" ).In( "ProductId", new[] { id, id2 } ).ToList(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id ); + Assert.Contains( result, t => t.Id == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestToList_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Sales.Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "Sales.OrderItem" ) + .Values( orderItemId, orderId, TestConfig.DecimalValue ) + .ExecuteAsync(); + + ////ȡ + var result = _sqlQuery + .Select( "i.OrderItemId As Id,i.*" ) //OrderItemIdӳΪId,֮OrderItemص + .Select( "o.OrderId As Id,o.*" ) //OrderIdӳΪId,֮ǰOrderItemеϢ,֮Orderе + .From( "Sales.OrderItem As i" ) + .LeftJoin( "Sales.Order As o" ).On( "o.OrderId", "i.OrderId" ) + .Where( "i.OrderItemId", orderItemId ) + .ToList( ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion + + #region ToListAsync + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestToListAsync_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestToListAsync_1"; + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = await _sqlQuery.Select().From( "Products.Product" ).In( "ProductId", new[] { id, id2 } ).ToListAsync(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.ProductId == id ); + Assert.Contains( result, t => t.ProductId == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestToListAsync_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestToListAsync_2"; + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = await _sqlQuery.Select( "ProductId As Id,Code" ).From( "Products.Product" ).In( "ProductId", new[] { id, id2 } ).ToListAsync(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id ); + Assert.Contains( result, t => t.Id == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestToListAsync_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Sales.Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "Sales.OrderItem" ) + .Values( orderItemId, orderId, TestConfig.DecimalValue ) + .ExecuteAsync(); + + ////ȡ + var result = await _sqlQuery + .Select( "i.OrderItemId As Id,i.*" ) //OrderItemIdӳΪId,֮OrderItemص + .Select( "o.OrderId As Id,o.*" ) //OrderIdӳΪId,֮ǰOrderItemеϢ,֮Orderе + .From( "Sales.OrderItem As i" ) + .LeftJoin( "Sales.Order As o" ).On( "o.OrderId", "i.OrderId" ) + .Where( "i.OrderItemId", orderItemId ) + .ToListAsync( ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion + } +} diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlQuery/PostgreSqlQueryTest.Scalar.cs b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlQuery/PostgreSqlQueryTest.Scalar.cs new file mode 100644 index 000000000..ea4dbb549 --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlQuery/PostgreSqlQueryTest.Scalar.cs @@ -0,0 +1,629 @@ +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlQuery { + /// + /// PostgreSql Sqlѯ - ֵ + /// + public partial class PostgreSqlQueryTest { + + #region ExecuteScalar + + /// + /// Իȡֵ + /// + [Fact] + public void TestExecuteScalar_1() { + var result = _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ExecuteScalar(); + Assert.Equal( TestConfig.Value, result.SafeString() ); + } + + /// + /// Իȡֵ - + /// + [Fact] + public void TestExecuteScalar_2() { + var result = _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ExecuteScalar(); + Assert.Equal( TestConfig.Value, result ); + } + + #endregion + + #region ExecuteScalarAsync + + /// + /// Իȡֵ + /// + [Fact] + public async Task TestExecuteScalarAsync_1() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ExecuteScalarAsync(); + Assert.Equal( TestConfig.Value, result.SafeString() ); + } + + /// + /// Իȡֵ - + /// + [Fact] + public async Task TestExecuteScalarAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ExecuteScalarAsync(); + Assert.Equal( TestConfig.Value, result ); + } + + #endregion + + #region ToStringAsync + + /// + /// Իȡֵַ + /// + [Fact] + public async Task TestToStringAsync() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + + #endregion + + #region ToInt + + /// + /// Իȡ32λֵ + /// + [Fact] + public void TestToInt() { + var result = _sqlQuery.Select( "IntPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToInt(); + Assert.Equal( TestConfig.IntValue, result ); + } + + #endregion + + #region ToIntAsync + + /// + /// Իȡ32λֵ + /// + [Fact] + public async Task TestToIntAsync() { + var result = await _sqlQuery.Select( "IntPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToIntAsync(); + Assert.Equal( TestConfig.IntValue, result ); + } + + #endregion + + #region ToIntOrNull + + /// + /// Իȡ32λֵ - ֵ + /// + [Fact] + public void TestToIntOrNull_1() { + var result = _sqlQuery.Select( "IntPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToIntOrNull(); + Assert.Equal( TestConfig.IntValue, result ); + } + + /// + /// Իȡ32λɿֵ - ֵ + /// + [Fact] + public void TestToIntOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToIntOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToIntOrNullAsync + + /// + /// Իȡ32λɿֵ - ֵ + /// + [Fact] + public async Task TestToIntOrNullAsync_1() { + var result = await _sqlQuery.Select( "IntPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToIntOrNullAsync(); + Assert.Equal( TestConfig.IntValue, result ); + } + + /// + /// Իȡ32λɿֵ - ֵ + /// + [Fact] + public async Task TestToIntOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToIntOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToLong + + /// + /// Իȡ64λֵ + /// + [Fact] + public void TestToLong() { + var result = _sqlQuery.Select( "LongPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToLong(); + Assert.Equal( TestConfig.LongValue, result ); + } + + #endregion + + #region ToLongAsync + + /// + /// Իȡ64λֵ + /// + [Fact] + public async Task TestToLongAsync() { + var result = await _sqlQuery.Select( "LongPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToLongAsync(); + Assert.Equal( TestConfig.LongValue, result ); + } + + #endregion + + #region ToLongOrNull + + /// + /// Իȡ64λɿֵ - ֵ + /// + [Fact] + public void TestToLongOrNull_1() { + var result = _sqlQuery.Select( "LongPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToLongOrNull(); + Assert.Equal( TestConfig.LongValue, result ); + } + + /// + /// Իȡ64λɿֵ - ֵ + /// + [Fact] + public void TestToLongOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToLongOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToLongOrNullAsync + + /// + /// Իȡ64λɿֵ - ֵ + /// + [Fact] + public async Task TestToLongOrNullAsync_1() { + var result = await _sqlQuery.Select( "LongPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToLongOrNullAsync(); + Assert.Equal( TestConfig.LongValue, result ); + } + + /// + /// Իȡ64λɿֵ - ֵ + /// + [Fact] + public async Task TestToLongOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToLongOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToGuid + + /// + /// ԻȡGuidֵ + /// + [Fact] + public void TestToGuid() { + var result = _sqlQuery.Select( "ProductId" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToGuid(); + Assert.Equal( TestConfig.Id, result ); + } + + #endregion + + #region ToGuidAsync + + /// + /// ԻȡGuidֵ + /// + [Fact] + public async Task TestToGuidAsync() { + var result = await _sqlQuery.Select( "ProductId" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToGuidAsync(); + Assert.Equal( TestConfig.Id, result ); + } + + #endregion + + #region ToGuidOrNull + + /// + /// ԻȡɿGuidֵ - ֵ + /// + [Fact] + public void TestToGuidOrNull_1() { + var result = _sqlQuery.Select( "ProductId" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToGuidOrNull(); + Assert.Equal( TestConfig.Id, result ); + } + + /// + /// ԻȡɿGuidֵ - ֵ + /// + [Fact] + public void TestToGuidOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToGuidOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToGuidOrNullAsync + + /// + /// ԻȡɿGuidֵ - ֵ + /// + [Fact] + public async Task TestToGuidOrNullAsync_1() { + var result = await _sqlQuery.Select( "ProductId" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToGuidOrNullAsync(); + Assert.Equal( TestConfig.Id, result ); + } + + /// + /// ԻȡɿGuidֵ - ֵ + /// + [Fact] + public async Task TestToGuidOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToGuidOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToBool + + /// + /// Իȡֵ + /// + [Fact] + public void TestToBool() { + var result = _sqlQuery.Select( "Enabled" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToBool(); + Assert.Equal( TestConfig.BoolValue, result ); + } + + #endregion + + #region ToBoolAsync + + /// + /// Իȡֵ + /// + [Fact] + public async Task TestToBoolAsync() { + var result = await _sqlQuery.Select( "Enabled" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToBoolAsync(); + Assert.Equal( TestConfig.BoolValue, result ); + } + + #endregion + + #region ToBoolOrNull + + /// + /// Իȡɿղֵ - ֵ + /// + [Fact] + public void TestToBoolOrNull_1() { + var result = _sqlQuery.Select( "Enabled" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToBoolOrNull(); + Assert.Equal( TestConfig.BoolValue, result ); + } + + /// + /// Իȡɿղֵ - ֵ + /// + [Fact] + public void TestToBoolOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToBoolOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToBoolOrNullAsync + + /// + /// Իȡɿղֵ - ֵ + /// + [Fact] + public async Task TestToBoolOrNullAsync_1() { + var result = await _sqlQuery.Select( "Enabled" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToBoolOrNullAsync(); + Assert.Equal( TestConfig.BoolValue, result ); + } + + /// + /// Իȡɿղֵ - ֵ + /// + [Fact] + public async Task TestToBoolOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToBoolOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToFloat + + /// + /// ԻȡFloatֵ + /// + [Fact] + public void TestToFloat() { + var result = _sqlQuery.Select( "FloatPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToFloat(); + Assert.Equal( TestConfig.FloatValue, result ); + } + + #endregion + + #region ToFloatAsync + + /// + /// ԻȡFloatֵ + /// + [Fact] + public async Task TestToFloatAsync() { + var result = await _sqlQuery.Select( "FloatPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToFloatAsync(); + Assert.Equal( TestConfig.FloatValue, result ); + } + + #endregion + + #region ToFloatOrNull + + /// + /// ԻȡɿFloatֵ - ֵ + /// + [Fact] + public void TestToFloatOrNull_1() { + var result = _sqlQuery.Select( "FloatPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToFloatOrNull(); + Assert.Equal( TestConfig.FloatValue, result ); + } + + /// + /// ԻȡɿFloatֵ - ֵ + /// + [Fact] + public void TestToFloatOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToFloatOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToFloatOrNullAsync + + /// + /// ԻȡɿFloatֵ - ֵ + /// + [Fact] + public async Task TestToFloatOrNullAsync_1() { + var result = await _sqlQuery.Select( "FloatPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToFloatOrNullAsync(); + Assert.Equal( TestConfig.FloatValue, result ); + } + + /// + /// ԻȡɿFloatֵ - ֵ + /// + [Fact] + public async Task TestToFloatOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToFloatOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToDouble + + /// + /// ԻȡDoubleֵ + /// + [Fact] + public void TestToDouble() { + var result = _sqlQuery.Select( "Price" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDouble(); + Assert.Equal( TestConfig.DoubleValue, result ); + } + + #endregion + + #region ToDoubleAsync + + /// + /// ԻȡDoubleֵ + /// + [Fact] + public async Task TestToDoubleAsync() { + var result = await _sqlQuery.Select( "Price" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleAsync(); + Assert.Equal( TestConfig.DoubleValue, result ); + } + + #endregion + + #region ToDoubleOrNull + + /// + /// ԻȡɿDoubleֵ - ֵ + /// + [Fact] + public void TestToDoubleOrNull_1() { + var result = _sqlQuery.Select( "Price" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleOrNull(); + Assert.Equal( TestConfig.DoubleValue, result ); + } + + /// + /// ԻȡɿDoubleֵ - ֵ + /// + [Fact] + public void TestToDoubleOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToDoubleOrNullAsync + + /// + /// ԻȡɿDoubleֵ - ֵ + /// + [Fact] + public async Task TestToDoubleOrNullAsync_1() { + var result = await _sqlQuery.Select( "Price" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleOrNullAsync(); + Assert.Equal( TestConfig.DoubleValue, result ); + } + + /// + /// ԻȡɿDoubleֵ - ֵ + /// + [Fact] + public async Task TestToDoubleOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToDecimal + + /// + /// ԻȡDecimalֵ + /// + [Fact] + public void TestToDecimal() { + var result = _sqlQuery.Select( "Price" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDecimal(); + Assert.Equal( TestConfig.DecimalValue, result ); + } + + #endregion + + #region ToDecimalAsync + + /// + /// ԻȡDecimalֵ + /// + [Fact] + public async Task TestToDecimalAsync() { + var result = await _sqlQuery.Select( "Price" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalAsync(); + Assert.Equal( TestConfig.DecimalValue, result ); + } + + #endregion + + #region ToDecimalOrNull + + /// + /// ԻȡDecimalֵ - ֵ + /// + [Fact] + public void TestToDecimalOrNull_1() { + var result = _sqlQuery.Select( "Price" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalOrNull(); + Assert.Equal( TestConfig.DecimalValue, result ); + } + + /// + /// ԻȡDecimalֵ - ֵ + /// + [Fact] + public void TestToDecimalOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToDecimalOrNullAsync + + /// + /// ԻȡɿDecimalֵ - ֵ + /// + [Fact] + public async Task TestToDecimalOrNullAsync_1() { + var result = await _sqlQuery.Select( "Price" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalOrNullAsync(); + Assert.Equal( TestConfig.DecimalValue, result ); + } + + /// + /// ԻȡɿDecimalֵ - ֵ + /// + [Fact] + public async Task TestToDecimalOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToDateTime + + /// + /// Իȡֵ + /// + [Fact] + public void TestToDateTime() { + var result = _sqlQuery.Select( "Birthday" ).From( "Customers.Customer" ).Where( "CustomerId", 1 ).ToDateTime(); + Assert.Equal( TestConfig.DateTimeValue, result ); + } + + #endregion + + #region ToDateTimeAsync + + /// + /// Իȡֵ + /// + [Fact] + public async Task TestToDateTimeAsync() { + var result = await _sqlQuery.Select( "Birthday" ).From( "Customers.Customer" ).Where( "CustomerId", 1 ).ToDateTimeAsync(); + Assert.Equal( TestConfig.DateTimeValue, result ); + } + + #endregion + + #region ToDateTimeOrNull + + /// + /// Իȡɿֵ - ֵ + /// + [Fact] + public void TestToDateTimeOrNull_1() { + var result = _sqlQuery.Select( "Birthday" ).From( "Customers.Customer" ).Where( "CustomerId", 1 ).ToDateTimeOrNull(); + Assert.Equal( TestConfig.DateTimeValue, result ); + } + + /// + /// Իȡɿֵ - ֵ + /// + [Fact] + public void TestToDateTimeOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Customers.Customer" ).Where( "CustomerId", 1 ).ToDateTimeOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToDateTimeOrNullAsync + + /// + /// Իȡɿֵ - ֵ + /// + [Fact] + public async Task TestToDateTimeOrNullAsync_1() { + var result = await _sqlQuery.Select( "Birthday" ).From( "Customers.Customer" ).Where( "CustomerId", 1 ).ToDateTimeOrNullAsync(); + Assert.Equal( TestConfig.DateTimeValue, result ); + } + + /// + /// Իȡɿֵ - ֵ + /// + [Fact] + public async Task TestToDateTimeOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Customers.Customer" ).Where( "CustomerId", 1 ).ToDateTimeOrNullAsync(); + Assert.Null( result ); + } + + #endregion + } +} diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlQuery/PostgreSqlQueryTest.Single.cs b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlQuery/PostgreSqlQueryTest.Single.cs new file mode 100644 index 000000000..f75ce0d5a --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlQuery/PostgreSqlQueryTest.Single.cs @@ -0,0 +1,52 @@ +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Util.Tests.Models; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlQuery { + /// + /// PostgreSql Sqlѯ - ȡ + /// + public partial class PostgreSqlQueryTest { + /// + /// Իȡʵ + /// + [Fact] + public void TestExecuteSingle() { + var result = _sqlQuery.Select( "ProductId as Id,Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ExecuteSingle(); + Assert.Equal( TestConfig.Id, result.Id ); + Assert.Equal( TestConfig.Value, result.Code ); + } + + /// + /// Իȡʵ + /// + [Fact] + public async Task TestExecuteSingleAsync() { + var result = await _sqlQuery.Select( "ProductId as Id,Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ExecuteSingleAsync(); + Assert.Equal( TestConfig.Id, result.Id ); + Assert.Equal( TestConfig.Value, result.Code ); + } + + /// + /// Իȡʵ + /// + [Fact] + public void TestToEntity() { + var result = _sqlQuery.Select( "ProductId as Id,Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToEntity(); + Assert.Equal( TestConfig.Id, result.Id ); + Assert.Equal( TestConfig.Value, result.Code ); + } + + /// + /// Իȡʵ + /// + [Fact] + public async Task TestToEntityAsync() { + var result = await _sqlQuery.Select( "ProductId as Id,Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToEntityAsync(); + Assert.Equal( TestConfig.Id, result.Id ); + Assert.Equal( TestConfig.Value, result.Code ); + } + } +} diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlQuery/PostgreSqlQueryTest.cs b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlQuery/PostgreSqlQueryTest.cs new file mode 100644 index 000000000..9637c95c7 --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/SqlQuery/PostgreSqlQueryTest.cs @@ -0,0 +1,25 @@ +using Util.Data.Sql; + +namespace Util.Data.Dapper.Tests.SqlQuery { + /// + /// PostgreSql Sqlѯ + /// + public partial class PostgreSqlQueryTest { + /// + /// Sqlִ + /// + private readonly ISqlExecutor _sqlExecutor; + /// + /// Sqlѯ + /// + private readonly ISqlQuery _sqlQuery; + + /// + /// Գʼ + /// + public PostgreSqlQueryTest( ISqlExecutor sqlExecutor, ISqlQuery sqlQuery ) { + _sqlExecutor = sqlExecutor; + _sqlQuery = sqlQuery; + } + } +} diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/Startup.cs b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/Startup.cs new file mode 100644 index 000000000..6af464335 --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/Startup.cs @@ -0,0 +1,59 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Aop; +using Util.Data.Dapper.Metadata; +using Util.Data.Dapper.Sql; +using Util.Data.Dapper.Tests.Infrastructure; +using Util.Data.EntityFrameworkCore; +using Util.Data.Metadata; +using Util.Dates; +using Util.Helpers; +using Util.Sessions; +using Util.Tests.Infrastructure; +using Util.Tests.UnitOfWorks; +using Xunit.DependencyInjection.Logging; + +namespace Util.Data.Dapper.Tests { + /// + /// + /// + public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .ConfigureServices( ( context, services ) => { + services.AddTransient(); + } ) + .AsBuild() + .AddAop() + .AddUtc() + .AddPgSqlQuery( Config.GetConnectionString( "connection" ) ) + .AddPgSqlExecutor( Config.GetConnectionString( "connection" ) ) + .AddPgSqlUnitOfWork( Config.GetConnectionString( "connection" ) ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddSingleton(); + InitDatabase( services ); + } + + /// + /// ʼݿ + /// + private void InitDatabase( IServiceCollection services ) { + using var scope = services.BuildServiceProvider().CreateScope(); + var unitOfWork = (PgSqlUnitOfWork)scope.ServiceProvider.GetService(); + unitOfWork.EnsureDeleted(); + unitOfWork.EnsureCreated(); + DatabaseScript.InitProcedures( unitOfWork?.Database ); + } + } +} diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/Util.Data.Dapper.PostgreSql.Tests.Integration.csproj b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/Util.Data.Dapper.PostgreSql.Tests.Integration.csproj new file mode 100644 index 000000000..47879b872 --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/Util.Data.Dapper.PostgreSql.Tests.Integration.csproj @@ -0,0 +1,43 @@ + + + + $(NetTargetFramework) + false + Util.Data.Dapper.Tests + Util.Data.Dapper.Tests.Startup + + + + + Always + + + Always + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + \ No newline at end of file diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/appsettings.Development.json b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..29bdd0782 --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Trace" + } + }, + "ConnectionStrings": { + "connection": "server=127.0.0.1:15432;User Id=admin;password=admin;database=Util.Data.Dapper.Test" + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.PostgreSql.Tests.Integration/appsettings.json b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/appsettings.json new file mode 100644 index 000000000..0d52abec0 --- /dev/null +++ b/test/Util.Data.Dapper.PostgreSql.Tests.Integration/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + }, + "ConnectionStrings": { + "connection": "server=postgresql.common;User Id=admin;password=admin;database=Util.Data.Dapper.Test" + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/Infrastructure/DatabaseScript.cs b/test/Util.Data.Dapper.SqlServer.Tests.Integration/Infrastructure/DatabaseScript.cs new file mode 100644 index 000000000..f389f6a32 --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/Infrastructure/DatabaseScript.cs @@ -0,0 +1,138 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Util.Data.Dapper.Tests.Infrastructure; + +/// +/// 数据库初始化脚本 +/// +public class DatabaseScript { + /// + /// 初始化 + /// + public static void InitProcedures( DatabaseFacade database ) { + CreateGetProductCodeProcedure( database ); + CreateGetProductProcedure( database ); + CreateGetProductsProcedure( database ); + CreateGetOrderItemProcedure( database ); + CreateGetProductCodeOutputProcedure( database ); + CreateReturnProcedure( database ); + CreateInsertProductProcedure( database ); + } + + /// + /// 创建Proc_GetProductCode存储过程 + /// + private static void CreateGetProductCodeProcedure( DatabaseFacade database ) { + var sql = @" + Create Procedure [Products].[Proc_GetProductCode] ( + @productId uniqueidentifier + ) + As + Select [Code] + From [Products].[Product] + Where [ProductId] = @productId + "; + database.ExecuteSqlRaw( sql ); + } + + /// + /// 创建Proc_GetProduct存储过程 + /// + private static void CreateGetProductProcedure( DatabaseFacade database ) { + var sql = @" + Create Procedure [Products].[Proc_GetProduct] ( + @productId uniqueidentifier + ) + As + Select * + From [Products].[Product] + Where [ProductId] = @productId + "; + database.ExecuteSqlRaw( sql ); + } + + /// + /// 创建Proc_GetProducts存储过程 + /// + private static void CreateGetProductsProcedure( DatabaseFacade database ) { + var sql = @" + Create Procedure [Products].[Proc_GetProducts] ( + @productId uniqueidentifier, + @productId2 uniqueidentifier + ) + As + Select p.ProductId As Id,p.* + From [Products].[Product] As p + Where [ProductId] in ( @productId,@productId2 ) + "; + database.ExecuteSqlRaw( sql ); + } + + /// + /// 创建Proc_GetOrderItem存储过程 + /// + private static void CreateGetOrderItemProcedure( DatabaseFacade database ) { + var sql = @" + Create Procedure [Sales].[Proc_GetOrderItem] ( + @orderItemId uniqueidentifier + ) + As + Select i.OrderItemId As Id,i.*,o.OrderId As Id,o.* + From [Sales].[OrderItem] As i + Left Join [Sales].[Order] As o On o.OrderId=i.OrderId + Where i.OrderItemId=@orderItemId + "; + database.ExecuteSqlRaw( sql ); + } + + /// + /// 创建Proc_GetProductCode_Output存储过程 + /// + private static void CreateGetProductCodeOutputProcedure( DatabaseFacade database ) { + var sql = @" + Create Procedure [Products].[Proc_GetProductCode_Output] ( + @productId uniqueidentifier, + @code nvarchar(50) output + ) + As + Select @code=[Code] + From [Products].[Product] + Where [ProductId] = @productId + "; + database.ExecuteSqlRaw( sql ); + } + + /// + /// 创建Proc_Return存储过程 + /// + private static void CreateReturnProcedure( DatabaseFacade database ) { + var sql = @" + Create Procedure [Products].[Proc_Return] ( + @productId uniqueidentifier + ) + As + If Exists(Select * From [Products].[Product] Where [ProductId] = @productId) + Return 1; + Else + Return -1; + "; + database.ExecuteSqlRaw( sql ); + } + + /// + /// 创建Proc_InsertProduct存储过程 + /// + private static void CreateInsertProductProcedure( DatabaseFacade database ) { + var sql = @" + Create Procedure [Products].[Proc_InsertProduct] ( + @productId uniqueidentifier, + @code nvarchar(50) + ) + As + Insert [Products].[Product](ProductId,Code) + Values( @productId,@code ) + "; + database.ExecuteSqlRaw( sql ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/Metadata/SqlServerMetadataServiceTest.cs b/test/Util.Data.Dapper.SqlServer.Tests.Integration/Metadata/SqlServerMetadataServiceTest.cs new file mode 100644 index 000000000..49c889659 --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/Metadata/SqlServerMetadataServiceTest.cs @@ -0,0 +1,106 @@ +using System.Linq; +using System.Threading.Tasks; +using Util.Data.Metadata; +using Xunit; + +namespace Util.Data.Dapper.Tests.Metadata; + +/// +/// Sql Server数据库元数据服务测试 +/// +public class SqlServerMetadataServiceTest { + /// + /// 数据库元信息服务 + /// + private readonly IMetadataService _metadataService; + + /// + /// 初始化 + /// + public SqlServerMetadataServiceTest( IMetadataService service ) { + _metadataService = service; + } + + /// + /// 测试获取数据库名称 + /// + [Fact] + public async Task TestDatabaseName() { + var info = await _metadataService.GetDatabaseInfoAsync(); + Assert.Equal( "Util.Data.Dapper.Test", info.Name ); + } + + /// + /// 测试获取表信息 + /// + [Fact] + public async Task TestTableInfo() { + var info = await _metadataService.GetDatabaseInfoAsync(); + var table = info.Tables.FirstOrDefault( t => t.Name == "Product" ); + Assert.NotNull( table ); + Assert.Equal( "Products", table.Schema ); + Assert.Equal( "产品", table.Comment ); + } + + /// + /// 测试获取主键信息 + /// + [Fact] + public async Task TestPrimaryKey() { + var info = await _metadataService.GetDatabaseInfoAsync(); + var table = info.Tables.First( t => t.Name == "OrderItem" ); + var pkColumn = table.Columns.FirstOrDefault( t => t.Name == "OrderItemId" ); + Assert.NotNull( pkColumn ); + Assert.True( pkColumn.IsPrimaryKey ); + var fkColumn = table.Columns.FirstOrDefault( t => t.Name == "OrderId" ); + Assert.NotNull( fkColumn ); + Assert.False( fkColumn.IsPrimaryKey ); + } + + /// + /// 测试获取自增信息 + /// + [Fact] + public async Task TestAutoIncrement() { + var info = await _metadataService.GetDatabaseInfoAsync(); + var table = info.Tables.FirstOrDefault( t => t.Name == "Customer" ); + Assert.NotNull( table ); + var column = table.Columns.FirstOrDefault( t => t.Name == "CustomerId" ); + Assert.NotNull( column ); + Assert.True( column.IsAutoIncrement ); + Assert.Equal( "int", column.DataType ); + } + + /// + /// 测试获取数值精度信息 + /// + [Fact] + public async Task TestPrecision() { + var info = await _metadataService.GetDatabaseInfoAsync(); + var table = info.Tables.FirstOrDefault( t => t.Name == "Product" ); + Assert.NotNull( table ); + var column = table.Columns.FirstOrDefault( t => t.Name == "Price" ); + Assert.NotNull( column ); + Assert.Equal( 12, column.Precision ); + Assert.Equal( 2, column.Scale ); + } + + /// + /// 测试获取字符串列信息 + /// + [Fact] + public async Task TestStringInfo() { + var info = await _metadataService.GetDatabaseInfoAsync(); + var table = info.Tables.FirstOrDefault( t => t.Name == "Product" ); + Assert.NotNull( table ); + var count = table.Columns.Count( t => t.Name == "Code" ); + Assert.Equal( 1, count ); + var column = table.Columns.FirstOrDefault( t => t.Name == "Code" ); + Assert.NotNull( column ); + Assert.Equal( "产品编码", column.Comment ); + Assert.False( column.IsPrimaryKey ); + Assert.True( column.IsNullable ); + Assert.Equal( "nvarchar", column.DataType ); + Assert.Equal( 100, column.Length ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlBuilders/SqlServerExistsSqlBuilderTest.cs b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlBuilders/SqlServerExistsSqlBuilderTest.cs new file mode 100644 index 000000000..ea803a071 --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlBuilders/SqlServerExistsSqlBuilderTest.cs @@ -0,0 +1,49 @@ +using System.Text; +using Util.Data.Dapper.Sql.Builders; +using Util.Data.Sql; +using Util.Data.Sql.Builders; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Data.Dapper.Tests.SqlBuilders; + +/// +/// 测试判断是否存在的Sql生成器 +/// +public class SqlServerExistsSqlBuilderTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + + /// + /// 测试初始化 + /// + public SqlServerExistsSqlBuilderTest( ITestOutputHelper output ) { + _output = output; + } + + /// + /// 测试生成的Sql + /// + [Fact] + public void TestToSql() { + var sql = new StringBuilder(); + sql.AppendLine( "Select Case" ); + sql.AppendLine( " When Exists (" ); + sql.AppendLine( "Select 1 " ); + sql.Append( "From [b]" ); + sql.AppendLine( ")" ); + sql.AppendLine( " Then Cast(1 As Bit)" ); + sql.AppendLine( " Else Cast(0 As Bit) " ); + sql.Append( "End" ); + + var subQuery = new SqlServerBuilder() + .Select("a") + .From( "b" ); + var sqlBuilder = new SqlServerExistsSqlBuilder( subQuery ); + var result = sqlBuilder.GetSql(); + _output.WriteLine( result ); + Assert.Equal( sql.ToString(),result ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Execute.cs b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Execute.cs new file mode 100644 index 000000000..0c972f634 --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Execute.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor; + +/// +/// Sql Server Sql执行器测试 - 执行Sql测试 +/// +public partial class SqlServerSqlExecutorTest { + /// + /// 测试执行Sql增删改操作 + /// + [Fact] + public async Task TestExecuteAsync() { + var id = Guid.NewGuid(); + await _sqlExecutor + .AppendLine( "Insert Products.Product(ProductId,Code) " ) + .Append( "Values(@ProductId,@Code)" ) + .AddParam( "@ProductId", id ) + .AddParam( "Code", TestConfig.Value ) + .ExecuteAsync(); + var result = await _sqlExecutor.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", id ).ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Id.cs b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Id.cs new file mode 100644 index 000000000..01a450de0 --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Id.cs @@ -0,0 +1,67 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Helpers; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor; + +/// +/// Sql Server Sql执行器测试 - 标识列测试 +/// +public partial class SqlServerSqlExecutorTest { + /// + /// 测试插入Guid主键 + /// + [Fact] + public async Task TestInsert_Guid() { + var id = Guid.NewGuid(); + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + var result = await _sqlExecutor + .Select( "Code" ) + .From( "Products.Product" ) + .Where( "ProductId", id ) + .ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + + /// + /// 测试插入int自增长主键 + /// + [Fact] + public async Task TestInsert_Int() { + var value = Id.Create(); + await _sqlExecutor + .Insert( "Code", "Customers.Customer" ) + .Values( value ) + .ExecuteAsync(); + var result = await _sqlExecutor + .Select( "CustomerId" ) + .From( "Customers.Customer" ) + .Where( "Code", value ) + .ToIntAsync(); + Assert.True( result > 0 ); + } + + /// + /// 测试插入string主键 + /// + [Fact] + public async Task TestInsert_String() { + var id = Id.Create(); + await _sqlExecutor + .Insert( "OrderId,CustomerName", "Sales.Order" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + var result = await _sqlExecutor + .Select( "CustomerName" ) + .From( "Sales.Order" ) + .Where( "OrderId", id ) + .ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Insert.cs b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Insert.cs new file mode 100644 index 000000000..811951123 --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Insert.cs @@ -0,0 +1,83 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor; + +/// +/// Sql Server Sql执行器测试 - 插入测试 +/// +public partial class SqlServerSqlExecutorTest { + /// + /// 测试使用Insert Values语句插入一条数据 + /// + [Fact] + public async Task TestInsert_1() { + //插入一条数据 + var id = Guid.NewGuid(); + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //获取值 + var result = await _sqlExecutor + .Select( "Code" ) + .From( "Products.Product" ) + .Where( "ProductId", id ) + .ToStringAsync(); + + //验证 + Assert.Equal( TestConfig.Value, result ); + } + + /// + /// 测试使用Insert Values语句插入2条数据 + /// + [Fact] + public async Task TestInsert_2() { + //插入一条数据 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .Values( id, TestConfig.Value ) + .Values( id2, TestConfig.Value ) + .ExecuteAsync(); + + //获取值 + var result = await _sqlExecutor + .AppendSelect( "Count(*)" ) + .From( "Products.Product" ) + .In( "ProductId", new[]{id,id2} ) + .ToIntAsync(); + + //验证 + Assert.Equal( 2, result ); + } + + /// + /// 测试使用Insert Select语句插入 + /// + [Fact] + public async Task TestInsert_3() { + //用Insert Select插入一条数据 + var id = Guid.NewGuid(); + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .AppendSelect( $"'{id}','{TestConfig.Value}'" ) + .ExecuteAsync(); + + //获取值 + var result = await _sqlExecutor + .Select( "Code" ) + .From( "Products.Product" ) + .Where( "ProductId", id ) + .ToStringAsync(); + + //验证 + Assert.Equal( TestConfig.Value, result ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Param.cs b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Param.cs new file mode 100644 index 000000000..48983c695 --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Param.cs @@ -0,0 +1,102 @@ +using System; +using System.Data; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor; + +/// +/// Sql Server Sql执行器测试 - 执行存储过程参数测试 +/// +public partial class SqlServerSqlExecutorTest { + /// + /// 测试添加动态参数 + /// + [Fact] + public async Task TestAddParam_Dynamic() { + var id = Guid.NewGuid(); + await _sqlExecutor + .AppendLine( "Insert Products.Product(ProductId,Code) " ) + .Append( "Values(@ProductId,@Code)" ) + .AddDynamicParams( new { ProductId=id, Code= TestConfig.Value } ) + .ExecuteAsync(); + var result = await _sqlExecutor.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", id ).ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + + /// + /// 测试添加和获取输出参数 + /// + [Fact] + public async Task TestAddParam_Output() { + //定义变量 + var id = Guid.NewGuid(); + + //插入数据 + await _sqlExecutor + .AppendLine( "Insert Products.Product(ProductId,Code) " ) + .Append( "Values(@ProductId,@Code)" ) + .AddParam( "@ProductId", id ) + .AddParam( "Code", TestConfig.Value ) + .ExecuteAsync(); + + //执行存储过程 - 添加输出参数 + await _sqlExecutor + .AddParam( "productId", id ) + .AddParam( "code",dbType: DbType.String,direction: ParameterDirection.Output,size:50 ) + .ExecuteProcedureAsync( "Products.Proc_GetProductCode_Output" ); + + //验证 - 使用GetParam获取输出参数 + Assert.Equal( TestConfig.Value, _sqlExecutor.GetParam( "code" ) ); + } + + /// + /// 测试添加和获取返回参数 + /// + [Fact] + public async Task TestAddParam_Return() { + //执行存储过程 - 添加返回参数,注意:返回参数名任意设置均可 + await _sqlExecutor + .AddParam( "productId", TestConfig.Id ) + .AddParam( "value", DbType.Int32, ParameterDirection.ReturnValue ) + .ExecuteProcedureAsync( "Products.Proc_Return" ); + //使用GetParam获取返回参数,参数名与之前设置的保持一致 + Assert.Equal( 1, _sqlExecutor.GetParam( "value" ) ); + + //执行存储过程 - 传入无效Id + await _sqlExecutor + .AddParam( "productId", Guid.NewGuid() ) + .AddParam( "value", DbType.Int32, ParameterDirection.ReturnValue ) + .ExecuteProcedureAsync( "Products.Proc_Return" ); + Assert.Equal( -1, _sqlExecutor.GetParam( "value" ) ); + } + + /// + /// 测试添加参数 - 在执行前添加并获取参数 + /// + [Fact] + public async Task TestAddParam_1() { + var id = Guid.NewGuid(); + + //添加第一个参数 + _sqlExecutor + .AppendLine( "Insert Products.Product(ProductId,Code) " ) + .Append( "Values(@ProductId,@Code)" ) + .AddParam( "@ProductId", id ); + + //获取参数 + var param = _sqlExecutor.GetParam( "ProductId" ); + Assert.Equal( id, param ); + + //添加第二个参数并执行 + await _sqlExecutor + .AddParam( "Code", TestConfig.Value ) + .ExecuteAsync(); + + //验证 + var result = await _sqlExecutor.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", id ).ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Procedure.cs b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Procedure.cs new file mode 100644 index 000000000..b4c023667 --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Procedure.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor; + +/// +/// Sql Server Sql执行器测试 - 执行存储过程测试 +/// +public partial class SqlServerSqlExecutorTest { + /// + /// 测试执行存储过程增删改操作 + /// + [Fact] + public async Task TestExecuteProcedureScalarAsync() { + //定义变量 + var id = Guid.NewGuid(); + + //插入数据,参数无论是否加@前缀均可 + await _sqlExecutor + .AddParam( "@productId", id ) + .AddParam( "code", TestConfig.Value ) + .ExecuteProcedureAsync( "Products.Proc_InsertProduct" ); + + //获取IntValue值 + var result = await _sqlExecutor + .AddParam( "productId", id ) + .ExecuteProcedureScalarAsync( "Products.Proc_GetProductCode" ); + + //验证 + Assert.Equal( TestConfig.Value, result ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Transaction.cs b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Transaction.cs new file mode 100644 index 000000000..173532255 --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.Transaction.cs @@ -0,0 +1,206 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlExecutor; + +/// +/// Sql Server Sql执行器测试 - 事务测试 +/// +public partial class SqlServerSqlExecutorTest { + /// + /// 测试事务操作 - 不使用事务,第一次操作成功,第二次失败 + /// + [Fact] + public async Task TestTransaction_1() { + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //Code2列名错误,插入Id2失败 + await _sqlExecutor + .Insert( "ProductId,Code,Code2", "Products.Product" ) + .Values( id2, TestConfig.Value, TestConfig.Value ) + .ExecuteAsync(); + } + catch { + // ignored + } + + //断言 + var exists = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.True( exists ); + Assert.False( exists2 ); + } + + /// + /// 测试事务操作 - 调用BeginTransaction,CommitTransaction,RollbackTransaction + /// + [Fact] + public async Task TestTransaction_2() { + //开始事务 + _sqlExecutor.BeginTransaction(); + + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //Code2列名错误,插入Id2失败 + await _sqlExecutor + .Insert( "ProductId,Code,Code2", "Products.Product" ) + .Values( id2, TestConfig.Value, TestConfig.Value ) + .ExecuteAsync(); + + //提交事务 + _sqlExecutor.CommitTransaction(); + } + catch { + //回滚事务 + _sqlExecutor.RollbackTransaction(); + } + + //断言 + var exists = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.False( exists ); + Assert.False( exists2 ); + } + + /// + /// 测试事务操作 - 使用using调用BeginTransaction + /// + [Fact] + public async Task TestTransaction_3() { + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + using var transaction = _sqlExecutor.BeginTransaction(); + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //Code2列名错误,插入Id2失败 + await _sqlExecutor + .Insert( "ProductId,Code,Code2", "Products.Product" ) + .Values( id2, TestConfig.Value, TestConfig.Value ) + .ExecuteAsync(); + + //提交事务 + transaction.Commit(); + } + catch { + // ignored + } + + //断言 + var exists = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.False( exists ); + Assert.False( exists2 ); + } + + /// + /// 测试事务操作 - 外部传入事务 + /// + [Fact] + public async Task TestTransaction_4() { + //Sql执行器实例2开始事务 + _sqlExecutor2.BeginTransaction(); + var transaction = _sqlExecutor2.GetTransaction(); + + //设置Sql执行器实例1事务 + _sqlExecutor.SetTransaction( transaction ); + + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //Code2列名错误,插入Id2失败 + await _sqlExecutor2 + .Insert( "ProductId,Code,Code2", "Products.Product" ) + .Values( id2, TestConfig.Value, TestConfig.Value ) + .ExecuteAsync(); + + //提交事务 + _sqlExecutor.CommitTransaction(); + } + catch { + //回滚事务 + _sqlExecutor.RollbackTransaction(); + } + + //断言 + var exists = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.False( exists ); + Assert.False( exists2 ); + } + + /// + /// 测试事务操作 - 成功提交 + /// + [Fact] + public async Task TestTransaction_5() { + //开始事务 + _sqlExecutor.BeginTransaction(); + + //创建两个Id + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + + try { + //插入Id成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .Values( id, TestConfig.Value ) + .ExecuteAsync(); + + //插入Id2成功 + await _sqlExecutor + .Insert( "ProductId,Code", "Products.Product" ) + .Values( id2, TestConfig.Value ) + .ExecuteAsync(); + + //提交事务 + _sqlExecutor.CommitTransaction(); + } + catch { + //回滚事务 + _sqlExecutor.RollbackTransaction(); + } + + //断言 + var exists = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id ).ExecuteExistsAsync(); + var exists2 = await _sqlExecutor.From( "Products.Product" ).Where( "ProductId", id2 ).ExecuteExistsAsync(); + Assert.True( exists ); + Assert.True( exists2 ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.cs b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.cs new file mode 100644 index 000000000..3fcb15ccd --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlExecutor/SqlServerSqlExecutorTest.cs @@ -0,0 +1,31 @@ +using Util.Data.Sql; +using Xunit.Abstractions; + +namespace Util.Data.Dapper.Tests.SqlExecutor; + +/// +/// Sql Server Sql执行器测试 +/// +public partial class SqlServerSqlExecutorTest { + /// + /// 测试输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// Sql执行器 + /// + private readonly ISqlExecutor _sqlExecutor; + /// + /// Sql执行器2 + /// + private readonly ISqlExecutor _sqlExecutor2; + + /// + /// 测试初始化 + /// + public SqlServerSqlExecutorTest( ITestOutputHelper output, ISqlExecutor sqlExecutor, ISqlExecutor sqlExecutor2 ) { + _output = output; + _sqlExecutor = sqlExecutor; + _sqlExecutor2 = sqlExecutor2; + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlQuery/SqlServerSqlQueryTest.Exists.cs b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlQuery/SqlServerSqlQueryTest.Exists.cs new file mode 100644 index 000000000..d6e46eec9 --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlQuery/SqlServerSqlQueryTest.Exists.cs @@ -0,0 +1,48 @@ +using System; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlQuery; + +/// +/// Sql Server Sqlѯ - жǷڲ +/// +public partial class SqlServerSqlQueryTest { + /// + /// жǷ - + /// + [Fact] + public void TestExecuteExists_1() { + var result = _sqlQuery.From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ExecuteExists(); + Assert.True( result ); + } + + /// + /// жǷ - + /// + [Fact] + public void TestExecuteExists_2() { + var result = _sqlQuery.From( "Products.Product" ).Where( "ProductId", Guid.NewGuid() ).ExecuteExists(); + Assert.False( result ); + } + + /// + /// жǷ - + /// + [Fact] + public async Task TestExecuteExistsAsync_1() { + var result = await _sqlQuery.From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ExecuteExistsAsync(); + Assert.True( result ); + } + + /// + /// жǷ - + /// + [Fact] + public async Task TestExecuteExistsAsync_2() { + var result = await _sqlQuery.From( "Products.Product" ).Where( "ProductId", Guid.NewGuid() ).ExecuteExistsAsync(); + Assert.False( result ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlQuery/SqlServerSqlQueryTest.Procedure.cs b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlQuery/SqlServerSqlQueryTest.Procedure.cs new file mode 100644 index 000000000..57b621f73 --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlQuery/SqlServerSqlQueryTest.Procedure.cs @@ -0,0 +1,303 @@ +using System; +using System.Collections; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Helpers; +using Util.Tests.Configs; +using Util.Tests.Models; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlQuery; + +/// +/// Sql Server Sqlѯ - ִд洢̲ +/// +public partial class SqlServerSqlQueryTest { + + #region ExecuteProcedureScalar + + /// + /// ִд洢̻ȡֵ + /// + [Fact] + public void TestExecuteProcedureScalar_1() { + //ִд洢 + var result = _sqlQuery + .AddParam( "productId", TestConfig.Id ) + .ExecuteProcedureScalar( "Products.Proc_GetProductCode" ); + + // + Assert.Equal( TestConfig.Value, result ); + Assert.Equal( "[Products].[Proc_GetProductCode]", _sqlQuery.PreviousSql.GetSql() ); + } + + /// + /// ִд洢̻ȡֵ - ͷ + /// + [Fact] + public void TestExecuteProcedureScalar_2() { + //ִд洢 + var result = _sqlQuery + .AddParam( "productId", TestConfig.Id ) + .ExecuteProcedureScalar( "Products.Proc_GetProductCode" ); + + // + Assert.Equal( TestConfig.Value, result ); + Assert.Equal( "[Products].[Proc_GetProductCode]", _sqlQuery.PreviousSql.GetSql() ); + } + + #endregion + + #region ExecuteProcedureScalarAsync + + /// + /// ִд洢̻ȡֵ + /// + [Fact] + public async Task TestExecuteProcedureScalarAsync_1() { + //ִд洢 + var result = await _sqlQuery + .AddParam( "productId", TestConfig.Id ) + .ExecuteProcedureScalarAsync( "Products.Proc_GetProductCode" ); + + // + Assert.Equal( TestConfig.Value, result ); + Assert.Equal( "[Products].[Proc_GetProductCode]", _sqlQuery.PreviousSql.GetSql() ); + } + + /// + /// ִд洢̻ȡֵ - ͷ + /// + [Fact] + public async Task TestExecuteProcedureScalarAsync_2() { + //ִд洢 + var result = await _sqlQuery + .AddParam( "productId", TestConfig.Id ) + .ExecuteProcedureScalarAsync( "Products.Proc_GetProductCode" ); + + // + Assert.Equal( TestConfig.Value, result ); + Assert.Equal( "[Products].[Proc_GetProductCode]", _sqlQuery.PreviousSql.GetSql() ); + } + + #endregion + + #region ExecuteProcedureSingle + + /// + /// ִд洢̻ȡʵ + /// + [Fact(Skip = "չתʧ,δ")] + public void TestExecuteProcedureSingle() { + //ִд洢 + var result = _sqlQuery + .AddParam( "productId", TestConfig.Id ) + .ExecuteProcedureSingle( "Products.Proc_GetProduct" ); + + // + Assert.Equal( TestConfig.Value, result.Code ); + Assert.Equal( "[Products].[Proc_GetProduct]", _sqlQuery.PreviousSql.GetSql() ); + } + + #endregion + + #region ExecuteProcedureSingleAsync + + /// + /// ִд洢̻ȡʵ + /// + [Fact( Skip = "չתʧ,δ" )] + public async Task TestExecuteProcedureSingleAsync() { + //ִд洢 + var result = await _sqlQuery + .AddParam( "productId", TestConfig.Id ) + .ExecuteProcedureSingleAsync( "Products.Proc_GetProduct" ); + + // + Assert.Equal( TestConfig.Value, result.Code ); + Assert.Equal( "[Products].[Proc_GetProduct]", _sqlQuery.PreviousSql.GetSql() ); + } + + #endregion + + #region ExecuteProcedureQuery + + /// + /// ִд洢̻ȡʵ弯 + /// + [Fact] + public async Task TestExecuteProcedureQuery_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, "a" ) + .Values( id2, "b" ) + .ExecuteAsync(); + + //ִд洢 + var result = _sqlQuery + .AddParam( "productId", id ) + .AddParam( "productId2", id2 ) + .ExecuteProcedureQuery( "Products.Proc_GetProducts" ); + + // + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id && t.Code == "a" ); + Assert.Contains( result, t => t.Id == id2 && t.Code == "b" ); + Assert.Equal( "[Products].[Proc_GetProducts]", _sqlQuery.PreviousSql.GetSql() ); + } + + /// + /// Իȡʵ弯 - 1Ͳ + /// + [Fact] + public async Task TestExecuteProcedureQuery_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, "a" ) + .Values( id2, "b" ) + .ExecuteAsync(); + + //ִд洢 + var result = _sqlQuery + .AddParam( "productId", id ) + .AddParam( "productId2", id2 ) + .ExecuteProcedureQuery( "Products.Proc_GetProducts" ); + + // + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id && t.Code == "a" ); + Assert.Contains( result, t => t.Id == id2 && t.Code == "b" ); + Assert.Equal( "[Products].[Proc_GetProducts]", _sqlQuery.PreviousSql.GetSql() ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestExecuteProcedureQuery_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Sales.Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "Sales.OrderItem" ) + .Values( orderItemId, orderId, TestConfig.DecimalValue ) + .ExecuteAsync(); + + //ȡ + var result = _sqlQuery + .AddParam( "orderItemId", orderItemId ) + .ExecuteProcedureQuery( "Sales.Proc_GetOrderItem", ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion + + #region ExecuteProcedureQueryAsync + + /// + /// ִд洢̻ȡʵ弯 + /// + [Fact] + public async Task TestExecuteProcedureQueryAsync_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, "a" ) + .Values( id2, "b" ) + .ExecuteAsync(); + + //ִд洢 + var result = await _sqlQuery + .AddDynamicParams( new{ productId=id, productId2=id2 } ) + .ExecuteProcedureQueryAsync( "Products.Proc_GetProducts" ); + + // + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id && t.Code == "a" ); + Assert.Contains( result, t => t.Id == id2 && t.Code == "b" ); + Assert.Equal( "[Products].[Proc_GetProducts]", _sqlQuery.PreviousSql.GetSql() ); + } + + /// + /// Իȡʵ弯 - 1Ͳ + /// + [Fact] + public async Task TestExecuteProcedureQueryAsync_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, "a" ) + .Values( id2, "b" ) + .ExecuteAsync(); + + //ִд洢 + var result = await _sqlQuery + .AddParam( "productId", id ) + .AddParam( "productId2", id2 ) + .ExecuteProcedureQueryAsync( "Products.Proc_GetProducts" ); + + // + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id && t.Code == "a" ); + Assert.Contains( result, t => t.Id == id2 && t.Code == "b" ); + Assert.Equal( "[Products].[Proc_GetProducts]", _sqlQuery.PreviousSql.GetSql() ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestExecuteProcedureQueryAsync_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Sales.Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "Sales.OrderItem" ) + .Values( orderItemId, orderId, TestConfig.DecimalValue ) + .ExecuteAsync(); + + //ȡ + var result = await _sqlQuery + .AddParam( "orderItemId", orderItemId ) + .ExecuteProcedureQueryAsync( "Sales.Proc_GetOrderItem", ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlQuery/SqlServerSqlQueryTest.Query.cs b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlQuery/SqlServerSqlQueryTest.Query.cs new file mode 100644 index 000000000..36cadf31c --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlQuery/SqlServerSqlQueryTest.Query.cs @@ -0,0 +1,388 @@ +using System; +using System.Collections; +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Helpers; +using Util.Tests.Configs; +using Util.Tests.Models; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlQuery; + +/// +/// Sql Server Sqlѯ - ѯ +/// +public partial class SqlServerSqlQueryTest { + + #region ExecuteQuery + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestExecuteQuery_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestExecuteQuery_1"; + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = _sqlQuery.Select().From( "Products.Product" ).In( "ProductId", new[] { id, id2 } ).ExecuteQuery(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.ProductId == id ); + Assert.Contains( result, t => t.ProductId == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 1Ͳ + /// + [Fact] + public async Task TestExecuteQuery_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestExecuteQuery_2"; + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = _sqlQuery.Select("ProductId As Id,Code").From( "Products.Product" ).In( "ProductId", new[] { id, id2 } ).ExecuteQuery(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id ); + Assert.Contains( result, t => t.Id == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestExecuteQuery_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Sales.Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "Sales.OrderItem" ) + .Values( orderItemId, orderId,TestConfig.DecimalValue ) + .ExecuteAsync(); + + ////ȡ + var result = _sqlQuery + .Select( "i.OrderItemId As Id,i.*" ) //OrderItemIdӳΪId,֮OrderItemص + .Select( "o.OrderId As Id,o.*" ) //OrderIdӳΪId,֮ǰOrderItemеϢ,֮Orderе + .From( "Sales.OrderItem As i" ) + .LeftJoin( "Sales.Order As o" ).On( "o.OrderId", "i.OrderId" ) + .Where( "i.OrderItemId", orderItemId ) + .ExecuteQuery( ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion + + #region ExecuteQueryAsync + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestExecuteQueryAsync_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestExecuteQueryAsync_1"; + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = await _sqlQuery.Select().From( "Products.Product" ).In( "ProductId", new[] { id, id2 } ).ExecuteQueryAsync(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.ProductId == id ); + Assert.Contains( result, t => t.ProductId == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestExecuteQueryAsync_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestExecuteQueryAsync_2"; + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = await _sqlQuery.Select( "ProductId As Id,Code" ).From( "Products.Product" ).In( "ProductId", new[] { id, id2 } ).ExecuteQueryAsync(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id ); + Assert.Contains( result, t => t.Id == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestExecuteQueryAsync_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Sales.Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "Sales.OrderItem" ) + .Values( orderItemId, orderId, TestConfig.DecimalValue ) + .ExecuteAsync(); + + ////ȡ + var result = await _sqlQuery + .Select( "i.OrderItemId As Id,i.*" ) //OrderItemIdӳΪId,֮OrderItemص + .Select( "o.OrderId As Id,o.*" ) //OrderIdӳΪId,֮ǰOrderItemеϢ,֮Orderе + .From( "Sales.OrderItem As i" ) + .LeftJoin( "Sales.Order As o" ).On( "o.OrderId", "i.OrderId" ) + .Where( "i.OrderItemId", orderItemId ) + .ExecuteQueryAsync( ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion + + #region ToList + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestToList_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestToList_1"; + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = _sqlQuery.Select().From( "Products.Product" ).In( "ProductId", new[] { id, id2 } ).ToList(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.ProductId == id ); + Assert.Contains( result, t => t.ProductId == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 1Ͳ + /// + [Fact] + public async Task TestToList_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestToList_2"; + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = _sqlQuery.Select( "ProductId As Id,Code" ).From( "Products.Product" ).In( "ProductId", new[] { id, id2 } ).ToList(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id ); + Assert.Contains( result, t => t.Id == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestToList_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Sales.Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "Sales.OrderItem" ) + .Values( orderItemId, orderId, TestConfig.DecimalValue ) + .ExecuteAsync(); + + ////ȡ + var result = _sqlQuery + .Select( "i.OrderItemId As Id,i.*" ) //OrderItemIdӳΪId,֮OrderItemص + .Select( "o.OrderId As Id,o.*" ) //OrderIdӳΪId,֮ǰOrderItemеϢ,֮Orderе + .From( "Sales.OrderItem As i" ) + .LeftJoin( "Sales.Order As o" ).On( "o.OrderId", "i.OrderId" ) + .Where( "i.OrderItemId", orderItemId ) + .ToList( ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion + + #region ToListAsync + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestToListAsync_1() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestToListAsync_1"; + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = await _sqlQuery.Select().From( "Products.Product" ).In( "ProductId", new[] { id, id2 } ).ToListAsync(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.ProductId == id ); + Assert.Contains( result, t => t.ProductId == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 + /// + [Fact] + public async Task TestToListAsync_2() { + //2 + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var code = "TestToListAsync_2"; + await _sqlExecutor.Insert( "ProductId,Code", "Products.Product" ) + .Values( id, code ) + .Values( id2, code ) + .ExecuteAsync(); + + //ȡ + var result = await _sqlQuery.Select( "ProductId As Id,Code" ).From( "Products.Product" ).In( "ProductId", new[] { id, id2 } ).ToListAsync(); + + // + Assert.NotNull( result ); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Id == id ); + Assert.Contains( result, t => t.Id == id2 ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// Իȡʵ弯 - 3Ͳ - ӱ,ù + /// + [Fact] + public async Task TestToListAsync_3() { + //붩 + var orderId = Id.Create(); + var customerName = "customerName"; + await _sqlExecutor.Insert( "OrderId,CustomerName", "Sales.Order" ) + .Values( orderId, customerName ) + .ExecuteAsync(); + + //붩ϸ + var orderItemId = Id.CreateGuid(); + await _sqlExecutor.Insert( "OrderItemId,OrderId,Price", "Sales.OrderItem" ) + .Values( orderItemId, orderId, TestConfig.DecimalValue ) + .ExecuteAsync(); + + ////ȡ + var result = await _sqlQuery + .Select( "i.OrderItemId As Id,i.*" ) //OrderItemIdӳΪId,֮OrderItemص + .Select( "o.OrderId As Id,o.*" ) //OrderIdӳΪId,֮ǰOrderItemеϢ,֮Orderе + .From( "Sales.OrderItem As i" ) + .LeftJoin( "Sales.Order As o" ).On( "o.OrderId", "i.OrderId" ) + .Where( "i.OrderItemId", orderItemId ) + .ToListAsync( ( item, order ) => { + item.Order = order; + return item; + } ); + + // + Assert.Single( (IEnumerable)result ); + Assert.Equal( orderItemId, result[0].Id ); + Assert.Equal( TestConfig.DecimalValue, result[0].Price ); + Assert.Equal( orderId, result[0].OrderId ); + Assert.Equal( orderId, result[0].Order.Id ); + Assert.Equal( customerName, result[0].Order.CustomerName ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlQuery/SqlServerSqlQueryTest.Scalar.cs b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlQuery/SqlServerSqlQueryTest.Scalar.cs new file mode 100644 index 000000000..e2427640b --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlQuery/SqlServerSqlQueryTest.Scalar.cs @@ -0,0 +1,629 @@ +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlQuery; + +/// +/// Sql Server Sqlѯ - ֵ +/// +public partial class SqlServerSqlQueryTest { + + #region ExecuteScalar + + /// + /// Իȡֵ + /// + [Fact] + public void TestExecuteScalar_1() { + var result = _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ExecuteScalar(); + Assert.Equal( TestConfig.Value, result.SafeString() ); + } + + /// + /// Իȡֵ - + /// + [Fact] + public void TestExecuteScalar_2() { + var result = _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ExecuteScalar(); + Assert.Equal( TestConfig.Value, result ); + } + + #endregion + + #region ExecuteScalarAsync + + /// + /// Իȡֵ + /// + [Fact] + public async Task TestExecuteScalarAsync_1() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ExecuteScalarAsync(); + Assert.Equal( TestConfig.Value, result.SafeString() ); + } + + /// + /// Իȡֵ - + /// + [Fact] + public async Task TestExecuteScalarAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ExecuteScalarAsync(); + Assert.Equal( TestConfig.Value, result ); + } + + #endregion + + #region ToStringAsync + + /// + /// Իȡֵַ + /// + [Fact] + public async Task TestToStringAsync() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToStringAsync(); + Assert.Equal( TestConfig.Value, result ); + } + + #endregion + + #region ToInt + + /// + /// Իȡ32λֵ + /// + [Fact] + public void TestToInt() { + var result = _sqlQuery.Select( "IntPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToInt(); + Assert.Equal( TestConfig.IntValue, result ); + } + + #endregion + + #region ToIntAsync + + /// + /// Իȡ32λֵ + /// + [Fact] + public async Task TestToIntAsync() { + var result = await _sqlQuery.Select( "IntPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToIntAsync(); + Assert.Equal( TestConfig.IntValue, result ); + } + + #endregion + + #region ToIntOrNull + + /// + /// Իȡ32λֵ - ֵ + /// + [Fact] + public void TestToIntOrNull_1() { + var result = _sqlQuery.Select( "IntPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToIntOrNull(); + Assert.Equal( TestConfig.IntValue, result ); + } + + /// + /// Իȡ32λɿֵ - ֵ + /// + [Fact] + public void TestToIntOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToIntOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToIntOrNullAsync + + /// + /// Իȡ32λɿֵ - ֵ + /// + [Fact] + public async Task TestToIntOrNullAsync_1() { + var result = await _sqlQuery.Select( "IntPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToIntOrNullAsync(); + Assert.Equal( TestConfig.IntValue, result ); + } + + /// + /// Իȡ32λɿֵ - ֵ + /// + [Fact] + public async Task TestToIntOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToIntOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToLong + + /// + /// Իȡ64λֵ + /// + [Fact] + public void TestToLong() { + var result = _sqlQuery.Select( "LongPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToLong(); + Assert.Equal( TestConfig.LongValue, result ); + } + + #endregion + + #region ToLongAsync + + /// + /// Իȡ64λֵ + /// + [Fact] + public async Task TestToLongAsync() { + var result = await _sqlQuery.Select( "LongPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToLongAsync(); + Assert.Equal( TestConfig.LongValue, result ); + } + + #endregion + + #region ToLongOrNull + + /// + /// Իȡ64λɿֵ - ֵ + /// + [Fact] + public void TestToLongOrNull_1() { + var result = _sqlQuery.Select( "LongPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToLongOrNull(); + Assert.Equal( TestConfig.LongValue, result ); + } + + /// + /// Իȡ64λɿֵ - ֵ + /// + [Fact] + public void TestToLongOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToLongOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToLongOrNullAsync + + /// + /// Իȡ64λɿֵ - ֵ + /// + [Fact] + public async Task TestToLongOrNullAsync_1() { + var result = await _sqlQuery.Select( "LongPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToLongOrNullAsync(); + Assert.Equal( TestConfig.LongValue, result ); + } + + /// + /// Իȡ64λɿֵ - ֵ + /// + [Fact] + public async Task TestToLongOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToLongOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToGuid + + /// + /// ԻȡGuidֵ + /// + [Fact] + public void TestToGuid() { + var result = _sqlQuery.Select( "ProductId" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToGuid(); + Assert.Equal( TestConfig.Id, result ); + } + + #endregion + + #region ToGuidAsync + + /// + /// ԻȡGuidֵ + /// + [Fact] + public async Task TestToGuidAsync() { + var result = await _sqlQuery.Select( "ProductId" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToGuidAsync(); + Assert.Equal( TestConfig.Id, result ); + } + + #endregion + + #region ToGuidOrNull + + /// + /// ԻȡɿGuidֵ - ֵ + /// + [Fact] + public void TestToGuidOrNull_1() { + var result = _sqlQuery.Select( "ProductId" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToGuidOrNull(); + Assert.Equal( TestConfig.Id, result ); + } + + /// + /// ԻȡɿGuidֵ - ֵ + /// + [Fact] + public void TestToGuidOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToGuidOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToGuidOrNullAsync + + /// + /// ԻȡɿGuidֵ - ֵ + /// + [Fact] + public async Task TestToGuidOrNullAsync_1() { + var result = await _sqlQuery.Select( "ProductId" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToGuidOrNullAsync(); + Assert.Equal( TestConfig.Id, result ); + } + + /// + /// ԻȡɿGuidֵ - ֵ + /// + [Fact] + public async Task TestToGuidOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToGuidOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToBool + + /// + /// Իȡֵ + /// + [Fact] + public void TestToBool() { + var result = _sqlQuery.Select( "Enabled" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToBool(); + Assert.Equal( TestConfig.BoolValue, result ); + } + + #endregion + + #region ToBoolAsync + + /// + /// Իȡֵ + /// + [Fact] + public async Task TestToBoolAsync() { + var result = await _sqlQuery.Select( "Enabled" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToBoolAsync(); + Assert.Equal( TestConfig.BoolValue, result ); + } + + #endregion + + #region ToBoolOrNull + + /// + /// Իȡɿղֵ - ֵ + /// + [Fact] + public void TestToBoolOrNull_1() { + var result = _sqlQuery.Select( "Enabled" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToBoolOrNull(); + Assert.Equal( TestConfig.BoolValue, result ); + } + + /// + /// Իȡɿղֵ - ֵ + /// + [Fact] + public void TestToBoolOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToBoolOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToBoolOrNullAsync + + /// + /// Իȡɿղֵ - ֵ + /// + [Fact] + public async Task TestToBoolOrNullAsync_1() { + var result = await _sqlQuery.Select( "Enabled" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToBoolOrNullAsync(); + Assert.Equal( TestConfig.BoolValue, result ); + } + + /// + /// Իȡɿղֵ - ֵ + /// + [Fact] + public async Task TestToBoolOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToBoolOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToFloat + + /// + /// ԻȡFloatֵ + /// + [Fact] + public void TestToFloat() { + var result = _sqlQuery.Select( "FloatPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToFloat(); + Assert.Equal( TestConfig.FloatValue, result ); + } + + #endregion + + #region ToFloatAsync + + /// + /// ԻȡFloatֵ + /// + [Fact] + public async Task TestToFloatAsync() { + var result = await _sqlQuery.Select( "FloatPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToFloatAsync(); + Assert.Equal( TestConfig.FloatValue, result ); + } + + #endregion + + #region ToFloatOrNull + + /// + /// ԻȡɿFloatֵ - ֵ + /// + [Fact] + public void TestToFloatOrNull_1() { + var result = _sqlQuery.Select( "FloatPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToFloatOrNull(); + Assert.Equal( TestConfig.FloatValue, result ); + } + + /// + /// ԻȡɿFloatֵ - ֵ + /// + [Fact] + public void TestToFloatOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToFloatOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToFloatOrNullAsync + + /// + /// ԻȡɿFloatֵ - ֵ + /// + [Fact] + public async Task TestToFloatOrNullAsync_1() { + var result = await _sqlQuery.Select( "FloatPrice" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToFloatOrNullAsync(); + Assert.Equal( TestConfig.FloatValue, result ); + } + + /// + /// ԻȡɿFloatֵ - ֵ + /// + [Fact] + public async Task TestToFloatOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToFloatOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToDouble + + /// + /// ԻȡDoubleֵ + /// + [Fact] + public void TestToDouble() { + var result = _sqlQuery.Select( "Price" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDouble(); + Assert.Equal( TestConfig.DoubleValue, result ); + } + + #endregion + + #region ToDoubleAsync + + /// + /// ԻȡDoubleֵ + /// + [Fact] + public async Task TestToDoubleAsync() { + var result = await _sqlQuery.Select( "Price" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleAsync(); + Assert.Equal( TestConfig.DoubleValue, result ); + } + + #endregion + + #region ToDoubleOrNull + + /// + /// ԻȡɿDoubleֵ - ֵ + /// + [Fact] + public void TestToDoubleOrNull_1() { + var result = _sqlQuery.Select( "Price" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleOrNull(); + Assert.Equal( TestConfig.DoubleValue, result ); + } + + /// + /// ԻȡɿDoubleֵ - ֵ + /// + [Fact] + public void TestToDoubleOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToDoubleOrNullAsync + + /// + /// ԻȡɿDoubleֵ - ֵ + /// + [Fact] + public async Task TestToDoubleOrNullAsync_1() { + var result = await _sqlQuery.Select( "Price" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleOrNullAsync(); + Assert.Equal( TestConfig.DoubleValue, result ); + } + + /// + /// ԻȡɿDoubleֵ - ֵ + /// + [Fact] + public async Task TestToDoubleOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDoubleOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToDecimal + + /// + /// ԻȡDecimalֵ + /// + [Fact] + public void TestToDecimal() { + var result = _sqlQuery.Select( "Price" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDecimal(); + Assert.Equal( TestConfig.DecimalValue, result ); + } + + #endregion + + #region ToDecimalAsync + + /// + /// ԻȡDecimalֵ + /// + [Fact] + public async Task TestToDecimalAsync() { + var result = await _sqlQuery.Select( "Price" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalAsync(); + Assert.Equal( TestConfig.DecimalValue, result ); + } + + #endregion + + #region ToDecimalOrNull + + /// + /// ԻȡDecimalֵ - ֵ + /// + [Fact] + public void TestToDecimalOrNull_1() { + var result = _sqlQuery.Select( "Price" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalOrNull(); + Assert.Equal( TestConfig.DecimalValue, result ); + } + + /// + /// ԻȡDecimalֵ - ֵ + /// + [Fact] + public void TestToDecimalOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToDecimalOrNullAsync + + /// + /// ԻȡɿDecimalֵ - ֵ + /// + [Fact] + public async Task TestToDecimalOrNullAsync_1() { + var result = await _sqlQuery.Select( "Price" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalOrNullAsync(); + Assert.Equal( TestConfig.DecimalValue, result ); + } + + /// + /// ԻȡɿDecimalֵ - ֵ + /// + [Fact] + public async Task TestToDecimalOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToDecimalOrNullAsync(); + Assert.Null( result ); + } + + #endregion + + #region ToDateTime + + /// + /// Իȡֵ + /// + [Fact] + public void TestToDateTime() { + var result = _sqlQuery.Select( "Birthday" ).From( "Customers.Customer" ).Where( "CustomerId", 1 ).ToDateTime(); + Assert.Equal( TestConfig.DateTimeValue, result ); + } + + #endregion + + #region ToDateTimeAsync + + /// + /// Իȡֵ + /// + [Fact] + public async Task TestToDateTimeAsync() { + var result = await _sqlQuery.Select( "Birthday" ).From( "Customers.Customer" ).Where( "CustomerId", 1 ).ToDateTimeAsync(); + Assert.Equal( TestConfig.DateTimeValue, result ); + } + + #endregion + + #region ToDateTimeOrNull + + /// + /// Իȡɿֵ - ֵ + /// + [Fact] + public void TestToDateTimeOrNull_1() { + var result = _sqlQuery.Select( "Birthday" ).From( "Customers.Customer" ).Where( "CustomerId", 1 ).ToDateTimeOrNull(); + Assert.Equal( TestConfig.DateTimeValue, result ); + } + + /// + /// Իȡɿֵ - ֵ + /// + [Fact] + public void TestToDateTimeOrNull_2() { + var result = _sqlQuery.Select( "Code" ).From( "Customers.Customer" ).Where( "CustomerId", 1 ).ToDateTimeOrNull(); + Assert.Null( result ); + } + + #endregion + + #region ToDateTimeOrNullAsync + + /// + /// Իȡɿֵ - ֵ + /// + [Fact] + public async Task TestToDateTimeOrNullAsync_1() { + var result = await _sqlQuery.Select( "Birthday" ).From( "Customers.Customer" ).Where( "CustomerId", 1 ).ToDateTimeOrNullAsync(); + Assert.Equal( TestConfig.DateTimeValue, result ); + } + + /// + /// Իȡɿֵ - ֵ + /// + [Fact] + public async Task TestToDateTimeOrNullAsync_2() { + var result = await _sqlQuery.Select( "Code" ).From( "Customers.Customer" ).Where( "CustomerId", 1 ).ToDateTimeOrNullAsync(); + Assert.Null( result ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlQuery/SqlServerSqlQueryTest.Single.cs b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlQuery/SqlServerSqlQueryTest.Single.cs new file mode 100644 index 000000000..1965b5f85 --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlQuery/SqlServerSqlQueryTest.Single.cs @@ -0,0 +1,52 @@ +using System.Threading.Tasks; +using Util.Data.Sql; +using Util.Tests.Configs; +using Util.Tests.Models; +using Xunit; + +namespace Util.Data.Dapper.Tests.SqlQuery; + +/// +/// Sql Server Sqlѯ - ȡ +/// +public partial class SqlServerSqlQueryTest { + /// + /// Իȡʵ + /// + [Fact] + public void TestExecuteSingle() { + var result = _sqlQuery.Select( "ProductId as Id,Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ExecuteSingle(); + Assert.Equal( TestConfig.Id, result.Id ); + Assert.Equal( TestConfig.Value, result.Code ); + } + + /// + /// Իȡʵ + /// + [Fact] + public async Task TestExecuteSingleAsync() { + var result = await _sqlQuery.Select( "ProductId as Id,Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ExecuteSingleAsync(); + Assert.Equal( TestConfig.Id, result.Id ); + Assert.Equal( TestConfig.Value, result.Code ); + } + + /// + /// Իȡʵ + /// + [Fact] + public void TestToEntity() { + var result = _sqlQuery.Select( "ProductId as Id,Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToEntity(); + Assert.Equal( TestConfig.Id, result.Id ); + Assert.Equal( TestConfig.Value, result.Code ); + } + + /// + /// Իȡʵ + /// + [Fact] + public async Task TestToEntityAsync() { + var result = await _sqlQuery.Select( "ProductId as Id,Code" ).From( "Products.Product" ).Where( "ProductId", TestConfig.Id ).ToEntityAsync(); + Assert.Equal( TestConfig.Id, result.Id ); + Assert.Equal( TestConfig.Value, result.Code ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlQuery/SqlServerSqlQueryTest.cs b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlQuery/SqlServerSqlQueryTest.cs new file mode 100644 index 000000000..73653212b --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/SqlQuery/SqlServerSqlQueryTest.cs @@ -0,0 +1,25 @@ +using Util.Data.Sql; + +namespace Util.Data.Dapper.Tests.SqlQuery; + +/// +/// Sql Server Sqlѯ +/// +public partial class SqlServerSqlQueryTest { + /// + /// Sqlִ + /// + private readonly ISqlExecutor _sqlExecutor; + /// + /// Sqlѯ + /// + private readonly ISqlQuery _sqlQuery; + + /// + /// Գʼ + /// + public SqlServerSqlQueryTest( ISqlExecutor sqlExecutor, ISqlQuery sqlQuery ) { + _sqlExecutor = sqlExecutor; + _sqlQuery = sqlQuery; + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/Startup.cs b/test/Util.Data.Dapper.SqlServer.Tests.Integration/Startup.cs new file mode 100644 index 000000000..3a94f5a29 --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/Startup.cs @@ -0,0 +1,56 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Aop; +using Util.Data.Dapper.Metadata; +using Util.Data.Dapper.Sql; +using Util.Data.Dapper.Tests.Infrastructure; +using Util.Data.EntityFrameworkCore; +using Util.Data.Metadata; +using Util.Helpers; +using Util.Sessions; +using Util.Tests.Infrastructure; +using Util.Tests.UnitOfWorks; +using Xunit.DependencyInjection.Logging; + +namespace Util.Data.Dapper.Tests; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .ConfigureServices( ( context, services ) => { + services.AddTransient(); + } ) + .AsBuild() + .AddAop() + .AddSqlServerSqlQuery( Config.GetConnectionString( "connection" ) ) + .AddSqlServerSqlExecutor( Config.GetConnectionString( "connection" ) ) + .AddSqlServerUnitOfWork( Config.GetConnectionString( "connection" ) ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddSingleton(); + InitDatabase( services ); + } + + /// + /// ʼݿ + /// + private void InitDatabase( IServiceCollection services ) { + var unitOfWork = (SqlServerUnitOfWork)services.BuildServiceProvider().GetService(); + unitOfWork.EnsureDeleted(); + unitOfWork.EnsureCreated(); + DatabaseScript.InitProcedures( unitOfWork?.Database ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/Util.Data.Dapper.SqlServer.Tests.Integration.csproj b/test/Util.Data.Dapper.SqlServer.Tests.Integration/Util.Data.Dapper.SqlServer.Tests.Integration.csproj new file mode 100644 index 000000000..755c36071 --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/Util.Data.Dapper.SqlServer.Tests.Integration.csproj @@ -0,0 +1,43 @@ + + + + $(NetTargetFramework) + false + Util.Data.Dapper.Tests + Util.Data.Dapper.Tests.Startup + + + + + Always + + + Always + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/appsettings.Development.json b/test/Util.Data.Dapper.SqlServer.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..6222e63e5 --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Trace" + } + }, + "ConnectionStrings": { + "connection": "Server=.;Database=Util.Data.Dapper.Test;uid=sa;pwd=Pass@word;MultipleActiveResultSets=true;TrustServerCertificate=true" + } +} \ No newline at end of file diff --git a/test/Util.Data.Dapper.SqlServer.Tests.Integration/appsettings.json b/test/Util.Data.Dapper.SqlServer.Tests.Integration/appsettings.json new file mode 100644 index 000000000..89ae8e015 --- /dev/null +++ b/test/Util.Data.Dapper.SqlServer.Tests.Integration/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + }, + "ConnectionStrings": { + "connection": "Server=192.168.31.123\\sql2019;Database=Util.Data.Dapper.Test;uid=sa;pwd=sa;MultipleActiveResultSets=true;TrustServerCertificate=true" + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Startup.cs b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Startup.cs new file mode 100644 index 000000000..2612556c0 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Startup.cs @@ -0,0 +1,45 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Aop; +using Util.Helpers; +using Util.Sessions; +using Util.Tests.Infrastructure; +using Util.Tests.UnitOfWorks; +using Xunit.DependencyInjection.Logging; + +namespace Util.Data.EntityFrameworkCore; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .AsBuild() + .AddAop() + .AddMySqlUnitOfWork( Config.GetConnectionString( "connection" ) ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddSingleton(); + InitDatabase( services ); + } + + /// + /// ʼݿ + /// + private void InitDatabase( IServiceCollection services ) { + var unitOfWork = services.BuildServiceProvider().GetService(); + unitOfWork.EnsureDeleted(); + unitOfWork.EnsureCreated(); + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/CustomerRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/CustomerRepositoryTest.cs new file mode 100644 index 000000000..38fbda0ab --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/CustomerRepositoryTest.cs @@ -0,0 +1,604 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Util.Data.Queries; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests; + +/// +/// 客户仓储测试 +/// 说明: +/// 1. 测试自增长int类型标识 +/// 2. 测试日期范围查询 +/// 3. 测试分页查询 +/// 4. 测试Utc日期 +/// +public class CustomerRepositoryTest : TestBase { + + #region 测试初始化 + + /// + /// 仓储 + /// + private readonly ICustomerRepository _repository; + + /// + /// 测试初始化 + /// + public CustomerRepositoryTest( ICustomerRepository repository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = repository; + } + + #endregion + + #region 辅助操作 + + /// + /// 添加实体列表 + /// + /// 添加数量 + /// 客户名称 + private async Task> AddCustomersAsync( int count = 2, string name = null ) { + var customers = CustomerFakeService.GetCustomers( count ); + customers.ForEach( customer => { + if ( name != null ) + customer.Name = name; + customer.CreationTime = null; + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + return customers.OrderBy( t => t.Code ).ToList(); + } + + #endregion + + #region Between + + /// + /// 测试添加范围查询条件 - 日期带时间 + /// + [Fact] + public async Task TestBetween_DateTime_1() { + //常量 + var name = "TestBetween_DateTime_1"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 06:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 06:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday ).ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 包含左边界 + /// + [Fact] + public async Task TestBetween_DateTime_2() { + //常量 + var name = "TestBetween_DateTime_2"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Left ).ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 包含右边界 + /// + [Fact] + public async Task TestBetween_DateTime_3() { + //常量 + var name = "TestBetween_DateTime_3"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Right ).ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 包含两边 + /// + [Fact] + public async Task TestBetween_DateTime_4() { + //常量 + var name = "TestBetween_DateTime_4"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Both ).ToListAsync(); + + //验证 + Assert.Equal( 3, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 不包含边界 + /// + [Fact] + public async Task TestBetween_DateTime_5() { + //常量 + var name = "TestBetween_DateTime_5"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Neither ).ToListAsync(); + + //验证 + Assert.Single( result ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 最大值为空 + /// + [Fact] + public async Task TestBetween_DateTime_6() { + //常量 + var name = "TestBetween_DateTime_6"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, null, boundary: Boundary.Both ).ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 最小值为空 + /// + [Fact] + public async Task TestBetween_DateTime_7() { + //常量 + var name = "TestBetween_DateTime_7"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, null, maxBirthday, boundary: Boundary.Both ).ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 包含左边界 + /// + [Fact] + public async Task TestBetween_Date_1() { + //常量 + var name = "TestBetween_Date_1"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Left ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + Assert.Equal( 6, result[0].Birthday.SafeValue().Day ); + Assert.Equal( 7, result[1].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 包含右边界 + /// + [Fact] + public async Task TestBetween_Date_2() { + //常量 + var name = "TestBetween_Date_2"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Right ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + Assert.Equal( 7, result[0].Birthday.SafeValue().Day ); + Assert.Equal( 8, result[1].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 包含两边 + /// + [Fact] + public async Task TestBetween_Date_3() { + //常量 + var name = "TestBetween_Date_3"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Both ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 3, result.Count ); + Assert.Equal( 6, result[0].Birthday.SafeValue().Day ); + Assert.Equal( 7, result[1].Birthday.SafeValue().Day ); + Assert.Equal( 8, result[2].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 不包含边界 + /// + [Fact] + public async Task TestBetween_Date_4() { + //常量 + var name = "TestBetween_Date_4"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Neither ) + .ToListAsync(); + + //验证 + Assert.Single( result ); + Assert.Equal( 7, result[0].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 最大值为空 + /// + [Fact] + public async Task TestBetween_Date_5() { + //常量 + var name = "TestBetween_Date_5"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Nickname = name; + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, null, false ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + Assert.Equal( 6, result[0].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 最小值为空 + /// + [Fact] + public async Task TestBetween_Date_6() { + //常量 + var name = "TestBetween_Date_6"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, null, maxBirthday, false ) + .ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + } + + #endregion + + #region ToPageList + + /// + /// 测试获取分页列表 + /// + [Fact] + public async Task TestToPageList_1() { + //常量 + var name = "TestToPageList_1"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 1, Order = "Code" }; + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).ToPageList( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 1, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[0].Id, result.Data[0].Id ); + Assert.Equal( customers[1].Id, result.Data[1].Id ); + Assert.Equal( customers[2].Id, result.Data[2].Id ); + } + + /// + /// 测试获取分页列表 - 第二页 + /// + [Fact] + public async Task TestToPageList_2() { + //常量 + var name = "TestToPageList_2"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 2, Order = "Code" }; + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).ToPageList( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 2, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[3].Id, result.Data[0].Id ); + Assert.Equal( customers[4].Id, result.Data[1].Id ); + Assert.Equal( customers[5].Id, result.Data[2].Id ); + } + + /// + /// 测试获取分页列表 - Queryable设置排序 + /// + [Fact] + public async Task TestToPageList_3() { + //常量 + var name = "TestToPageList_3"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 2 }; + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).OrderBy( t => t.Code ).ToPageList( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 2, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[3].Id, result.Data[0].Id ); + Assert.Equal( customers[4].Id, result.Data[1].Id ); + Assert.Equal( customers[5].Id, result.Data[2].Id ); + } + + #endregion + + #region ToPageListAsync + + /// + /// 测试获取分页列表 + /// + [Fact] + public async Task TestToPageListAsync() { + //常量 + var name = "TestToPageListAsync"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 2, Order = "Code" }; + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).ToPageListAsync( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 2, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[3].Id, result.Data[0].Id ); + Assert.Equal( customers[4].Id, result.Data[1].Id ); + Assert.Equal( customers[5].Id, result.Data[2].Id ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/EntityEventsTest.cs b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/EntityEventsTest.cs new file mode 100644 index 000000000..5388978d5 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/EntityEventsTest.cs @@ -0,0 +1,211 @@ +using System.Threading.Tasks; +using Util.Tests.EventHandlers; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Data.EntityFrameworkCore.Tests; + +/// +/// 实体事件测试 +/// +public class EntityEventsTest : TestBase { + + #region 测试初始化 + + /// + /// 测试输出 + /// + private readonly ITestOutputHelper _testOutputHelper; + /// + /// 产品仓储 + /// + private readonly IProductRepository _productRepository; + /// + /// 操作日志仓储 + /// + private readonly IOperationLogRepository _operationLogRepository; + + /// + /// 测试初始化 + /// + public EntityEventsTest( ITestOutputHelper testOutputHelper, IProductRepository productRepository, + IOperationLogRepository operationLogRepository, ITestUnitOfWork unitOfWork ) :base(unitOfWork){ + _testOutputHelper = testOutputHelper; + _productRepository = productRepository; + _operationLogRepository = operationLogRepository; + } + + #endregion + + #region 实体创建事件 + + /// + /// 测试实体创建事件 + /// + [Fact] + public async Task TestEntityCreatedEvent() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityCreatedEvent"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityCreatedEvent" && t.LogName == nameof( ProductCreatedEventHandler ) ) ); + } + + /// + /// 测试实体变更事件 - 创建 + /// + [Fact] + public async Task TestEntityChangedEvent_Created() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityChangedEvent_Created"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityChangedEvent_Created" && t.LogName == nameof( ProductChangedEventHandler ) ) ); + } + + #endregion + + #region 实体修改事件 + + /// + /// 测试实体修改事件 + /// + [Fact] + public async Task TestEntityUpdatedEvent() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //修改实体 + var product = await _productRepository.FindByIdAsync( entity.Id ); + product.Name = "EntityUpdatedEvent"; + await _productRepository.UpdateAsync( product ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityUpdatedEvent" && t.LogName == nameof( ProductUpdatedEventHandler ) ) ); + } + + /// + /// 测试实体变更事件 - 修改 + /// + [Fact] + public async Task TestEntityChangedEvent_Updated() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //修改实体 + var product = _productRepository.FindById( entity.Id ); + product.Name = "EntityChangedEvent_Updated"; + await _productRepository.UpdateAsync( product ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityChangedEvent_Updated" && t.LogName == nameof( ProductChangedEventHandler ) ) ); + } + + #endregion + + #region 实体删除事件 + + /// + /// 测试实体删除事件 - 逻辑删除 + /// + [Fact] + public async Task TestEntityDeletedEvent_1() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityDeletedEvent"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _productRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityDeletedEvent" && t.LogName == nameof( ProductDeletedEventHandler ) ) ); + } + + /// + /// 测试实体删除事件 - 物理删除 + /// + [Fact] + public async Task TestEntityDeletedEvent_2() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + entity.Init(); + entity.LogName = "EntityDeletedEvent"; + await _operationLogRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _operationLogRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.LogName == "Test" && t.Caption == nameof( OperationLogDeletedEventHandler ) && t.Content == "EntityDeletedEvent" ) ); + } + + /// + /// 测试实体变更事件 - 逻辑删除 + /// + [Fact] + public async Task TestEntityChangedEvent_Deleted_1() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityChangedEvent_Deleted"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _productRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityChangedEvent_Deleted" && t.LogName == nameof( ProductChangedEventHandler ) ) ); + } + + /// + /// 测试实体变更事件 - 物理删除 + /// + [Fact] + public async Task TestEntityChangedEvent_Deleted_2() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + entity.Init(); + entity.LogName = "EntityChangedEvent_Deleted"; + await _operationLogRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _operationLogRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.LogName == "Test" && t.Caption == nameof( OperationLogChangedEventHandler ) && t.Content == "EntityChangedEvent_Deleted" ) ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/OperationLogRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/OperationLogRepositoryTest.cs new file mode 100644 index 000000000..cc1596dc1 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/OperationLogRepositoryTest.cs @@ -0,0 +1,63 @@ +using System.Threading.Tasks; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests; + +/// +/// 操作日志仓储测试 +/// 说明: +/// 测试自增长long类型标识 +/// +public class OperationLogRepositoryTest : TestBase { + /// + /// 仓储 + /// + private readonly IOperationLogRepository _repository; + + /// + /// 测试初始化 + /// + public OperationLogRepositoryTest( IOperationLogRepository operationLogRepository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = operationLogRepository; + } + + /// + /// 测试添加实体 + /// + [Fact] + public async Task TestAdd() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( entity.Id > 0 ); + Assert.True( _repository.Exists( t => t.Id == entity.Id ) ); + } + + /// + /// 测试通过标识移除实体 - 物理删除 + /// + [Fact] + public async Task TestRemove_Id() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/OrderRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/OrderRepositoryTest.cs new file mode 100644 index 000000000..d1831c6df --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/OrderRepositoryTest.cs @@ -0,0 +1,130 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests; + +/// +/// 订单仓储测试 +/// 说明: +/// 1. 测试string类型标识 +/// 2. 测试一对多导航属性 +/// +public class OrderRepositoryTest : TestBase { + + #region 测试初始化 + + /// + /// 仓储 + /// + private readonly IOrderRepository _repository; + + /// + /// 测试初始化 + /// + public OrderRepositoryTest( IOrderRepository repository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = repository; + } + + #endregion + + #region 辅助方法 + + /// + /// 添加订单 + /// + private async Task AddOrderAsync() { + var order = OrderFakeService.GetOrder(); + order.Init(); + order.OrderItems.Add( CreateOrderItem() ); + order.OrderItems.Add( CreateOrderItem() ); + await _repository.AddAsync( order ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + return order; + } + + /// + /// 创建订单项 + /// + private OrderItem CreateOrderItem() { + var item = OrderItemFakeService.GetOrderItem(); + item.Init(); + return item; + } + + #endregion + + #region Single + + /// + /// 测试查找单个实体 + /// + [Fact] + public async Task TestSingle_1() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id ); + + //验证 + Assert.Empty( result.OrderItems ); + } + + /// + /// 测试查找单个实体 - 使用Include包含子实体集合 + /// + [Fact] + public async Task TestSingle_2() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id, queryable => queryable.Include( t => t.OrderItems ) ); + + //验证 + Assert.Equal( 2, result.OrderItems.Count ); + } + + #endregion + + #region SingleAsync + + /// + /// 测试查找单个实体 + /// + [Fact] + public async Task TestSingleAsync_1() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id ); + + //验证 + Assert.Empty( result.OrderItems ); + } + + /// + /// 测试查找单个实体 - 使用Include包含子实体集合 + /// + [Fact] + public async Task TestSingleAsync_2() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id, queryable => queryable.Include( t => t.OrderItems ) ); + + //验证 + Assert.Equal( 2, result.OrderItems.Count ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/PostRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/PostRepositoryTest.cs new file mode 100644 index 000000000..535faa2b4 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/PostRepositoryTest.cs @@ -0,0 +1,54 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests; + +/// +/// 贴子仓储测试 +/// 说明: +/// 1. 测试多对多 +/// +public class PostRepositoryTest : TestBase { + /// + /// 仓储 + /// + private readonly IPostRepository _repository; + + /// + /// 测试初始化 + /// + public PostRepositoryTest(IPostRepository repository, ITestUnitOfWork unitOfWork ) : base(unitOfWork){ + _repository = repository; + } + + /// + /// 测试添加实体 + /// + [Fact] + public async Task TestAddAsync() { + //添加实体 + var post = PostFakeService.GetPost(); + post.Init(); + var tag1 = TagFakeService.GetTag(); + tag1.Init(); + post.Tags.Add( tag1 ); + + var tag2 = TagFakeService.GetTag(); + tag2.Init(); + post.Tags.Add( tag2 ); + + await _repository.AddAsync( post ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.SingleAsync( t => t.Id == post.Id, queryable => queryable.Include( t => t.Tags ) ); + Assert.NotNull( result ); + Assert.Equal( 2,result.Tags.Count ); + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/ProductRepositoryTest.Queryable.cs b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/ProductRepositoryTest.Queryable.cs new file mode 100644 index 000000000..91df4bfca --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/ProductRepositoryTest.Queryable.cs @@ -0,0 +1,390 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Util.Data.Queries; +using Util.Tests.Conditions; +using Util.Tests.Fakes; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests; + +/// +/// 产品仓储测试 - 查询测试 +/// +public partial class ProductRepositoryTest { + + #region Where + + /// + /// 测试设置查询条件 - 添加查询条件对象 + /// + [Fact] + public async Task TestWhere() { + //常量 + var name = "TestWhere"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //创建查询条件 + var condition = new ProductNameCondition( name ); + + //查询 + var result = _repository.Find().Where( condition ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + Assert.True( result.TrueForAll( t => products.Any( p => p.Id == t.Id ) ) ); + } + + #endregion + + #region WhereIf + + /// + /// 测试设置查询条件 - IQueryable添加WhereIf扩展 - true + /// + [Fact] + public async Task TestWhereIf_True() { + //常量 + var name = "TestWhereIf_True"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().WhereIf( t => t.Name == name, true ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加WhereIf扩展 - false + /// + [Fact] + public async Task TestWhereIf_False() { + //常量 + var name = "TestWhereIf_False"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().WhereIf( t => t.Name == name, false ).ToList(); + + //验证 + Assert.NotEqual( products.Count, result.Count ); + } + + #endregion + + #region WhereIfNotEmpty + + /// + /// 测试设置查询条件 - IQueryable添加WhereIfNotEmpty扩展 - 有效条件 + /// + [Fact] + public async Task TestWhereIfNotEmpty_Valid() { + //常量 + var name = "TestWhereIfNotEmpty_Valid"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().WhereIfNotEmpty( t => t.Name == name ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加WhereIfNotEmpty扩展 - 忽略条件 + /// + [Fact] + public async Task TestWhereIfNotEmpty_Ignore() { + //常量 + var name = "TestWhereIfNotEmpty_Ignore"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).WhereIfNotEmpty( t => t.Code == "" ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加WhereIfNotEmpty扩展 - 无效条件,抛出异常 + /// + [Fact] + public async Task TestWhereIfNotEmpty_Invalid() { + //添加实体列表 + await AddProductsAsync(); + + //查询 + Assert.Throws( () => { + _repository.Find().WhereIfNotEmpty( t => t.Name == "a" && t.Code == "b" ); + } ); + } + + #endregion + + #region OrIfNotEmpty + + /// + /// 测试设置查询条件 - IQueryable添加OrIfNotEmpty扩展 - 有效条件 + /// + [Fact] + public async Task TestOrIfNotEmpty_Valid() { + //常量 + var name1 = "TestOrIfNotEmpty_Valid_1"; + var name2 = "TestOrIfNotEmpty_Valid_2"; + var name3 = "TestOrIfNotEmpty_Valid_3"; + + //添加实体列表 + await AddProductsAsync(); + await AddProductsAsync( 2, name1 ); + await AddProductsAsync( 3, name2 ); + await AddProductsAsync( 4, name3 ); + + //查询 + var result = _repository.Find().OrIfNotEmpty( t => t.Name == name1,t => t.Name == name2, t => t.Name == name3 ).ToList(); + + //验证 + Assert.Equal( 9, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加OrIfNotEmpty扩展 - 忽略条件 + /// + [Fact] + public async Task TestOrIfNotEmpty_Ignore() { + //常量 + var name1 = "TestOrIfNotEmpty_Ignore_1"; + var name2 = "TestOrIfNotEmpty_Ignore_2"; + var name3 = "TestOrIfNotEmpty_Ignore_3"; + + //添加实体列表 + await AddProductsAsync(); + await AddProductsAsync( 2, name1 ); + await AddProductsAsync( 3, name2 ); + await AddProductsAsync( 4, name3 ); + + //查询 + var result = _repository.Find().OrIfNotEmpty( t => t.Name == name1, t => t.Name == name2, t => t.Name == "" ).ToList(); + + //验证 + Assert.Equal( 5, result.Count ); + } + + #endregion + + #region Between + + /// + /// 测试添加范围查询条件 + /// + [Fact] + public async Task TestBetween_1() { + //常量 + var name = "TestBetween_1"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price,11.5M,13.5M ).ToList(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 包含左边界 + /// + [Fact] + public async Task TestBetween_2() { + //常量 + var name = "TestBetween_2"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M,Boundary.Left ).ToList(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 包含右边界 + /// + [Fact] + public async Task TestBetween_3() { + //常量 + var name = "TestBetween_3"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M, Boundary.Right ).ToList(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 包含两边 + /// + [Fact] + public async Task TestBetween_4() { + //常量 + var name = "TestBetween_4"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M ).ToList(); + + //验证 + Assert.Equal( 3, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 不包含边界 + /// + [Fact] + public async Task TestBetween_5() { + //常量 + var name = "TestBetween_5"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M,Boundary.Neither ).ToList(); + + //验证 + Assert.Single( result ); + } + + /// + /// 测试添加范围查询条件 - 最大值为空 + /// + [Fact] + public async Task TestBetween_6() { + //常量 + var name = "TestBetween_6"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 9.5M, null ).ToList(); + + //验证 + Assert.Equal( 5, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 最小值为空 + /// + [Fact] + public async Task TestBetween_7() { + //常量 + var name = "TestBetween_7"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, null, 15.5M ).ToList(); + + //验证 + Assert.Equal( 5, result.Count ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/ProductRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/ProductRepositoryTest.cs new file mode 100644 index 000000000..249040cc5 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/ProductRepositoryTest.cs @@ -0,0 +1,1408 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Util.Exceptions; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; +using Xunit.Abstractions; +using Xunit.DependencyInjection; + +namespace Util.Data.EntityFrameworkCore.Tests; + +/// +/// 产品仓储测试 +/// 说明: +/// 1. 测试Guid类型标识 +/// 2. 测试仓储基础方法 +/// 3. 测试扩展属性 +/// +public partial class ProductRepositoryTest : TestBase { + + #region 测试初始化 + + /// + /// 日志消息 + /// + private readonly ITestOutputHelper _log; + /// + /// 仓储 + /// + private readonly IProductRepository _repository; + + /// + /// 测试初始化 + /// + public ProductRepositoryTest( ITestOutputHelperAccessor accessor, IProductRepository repository, ITestUnitOfWork unitOfWork ) :base(unitOfWork) { + _log = accessor.Output; + _repository = repository; + } + + #endregion + + #region 辅助方法 + + /// + /// 添加产品 + /// + private async Task AddProductAsync( string name = null ) { + var entity = ProductFakeService.GetProduct(); + entity.Init(); + if( name != null ) + entity.Name = name; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + return entity; + } + + /// + /// 添加实体列表 + /// + /// 添加数量 + /// 产品名称 + private async Task> AddProductsAsync( int count = 2, string name = null ) { + var products = ProductFakeService.GetProducts( count ); + products.ForEach( product => { + product.Init(); + if( name != null ) + product.Name = name; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + return products.OrderBy( t => t.Id ).ToList(); + } + + /// + /// 打印更改跟踪调试信息 + /// + private void WriteTrackerDebug() { + _log.WriteLine( UnitOfWork.GetChangeTrackerDebugView() ); + } + + #endregion + + #region FindById + + /// + /// 测试通过标识查找实体 - 跟踪实体 + /// + [Fact] + public async Task TestFindById_Tracking_1() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 跟踪实体 - 字符串标识 + /// + [Fact] + public async Task TestFindById_Tracking_2() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id.ToString() ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 不跟踪实体 + /// + [Fact] + public async Task TestFindById_NoTracking() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.NoTracking().FindById( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region FindByIdAsync + + /// + /// 测试通过标识查找实体 - 跟踪 - 异步 + /// + [Fact] + public async Task TestFindByIdAsync_Tracking_1() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 跟踪 - 异步 - 字符串标识 + /// + [Fact] + public async Task TestFindByIdAsync_Tracking_2() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id.ToString() ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 不跟踪 - 异步 + /// + [Fact] + public async Task TestFindByIdAsync_NoTracking() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.NoTracking().FindByIdAsync( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region FindByIds + + /// + /// 测试通过标识列表查找实体列表 - 传入逗号分隔的标识列表 + /// + [Fact] + public async Task TestFindByIds_1() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = _repository.FindByIds( products[0].Id, products[1].Id, products[2].Id ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入集合 + /// + [Fact] + public async Task TestFindByIds_2() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = _repository.FindByIds( products.Select( t => t.Id ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入标识列表字符串 + /// + [Fact] + public async Task TestFindByIds_3() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = _repository.FindByIds( products.Select( t => t.Id ).Join() ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + #endregion + + #region FindByIdsAsync + + /// + /// 测试通过标识列表查找实体列表 - 传入逗号分隔的标识列表 - 异步 + /// + [Fact] + public async Task TestFindByIdsAsync_1() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = ( await _repository.FindByIdsAsync( products[0].Id, products[1].Id, products[2].Id ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入集合 - 异步 + /// + [Fact] + public async Task TestFindByIdsAsync_2() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = ( await _repository.FindByIdsAsync( products.Select( t => t.Id ) ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入标识列表字符串 - 异步 + /// + [Fact] + public async Task TestFindByIdsAsync_3() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = ( await _repository.FindByIdsAsync( products.Select( t => t.Id ).Join() ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + #endregion + + #region Single + + /// + /// 测试查找单个实体 + /// + [Fact] + public async Task TestSingle_1() { + //常量 + var name = "TestSingle"; + + //添加实体 + var entity = await AddProductAsync( name ); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.Single( t => t.Name == name ); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region SingleAsync + + /// + /// 测试查找单个实体 - 异步 + /// + [Fact] + public async Task TestSingleAsync() { + //常量 + var name = "TestSingleAsync"; + + //添加实体 + var entity = await AddProductAsync( name ); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.SingleAsync( t => t.Name == name ); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region Exists + + /// + /// 测试判断是否存在 + /// + [Fact] + public async Task TestExists() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + Assert.True( _repository.Exists( t => t.Id == entity.Id ) ); + } + + #endregion + + #region ExistsAsync + + /// + /// 测试判断是否存在 - 异步 + /// + [Fact] + public async Task TestExistsAsync() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + Assert.True( await _repository.ExistsAsync( t => t.Id == entity.Id ) ); + } + + #endregion + + #region Count + + /// + /// 测试查找数量 + /// + [Fact] + public async Task TestCount() { + //常量 + var name = "TestCount"; + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ); + products.ForEach( product => { + product.Init(); + product.Name = name; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = _repository.Count( t => t.Name == name ); + Assert.Equal( count, result ); + } + + #endregion + + #region CountAsync + + /// + /// 测试查找数量 - 异步 + /// + [Fact] + public async Task TestCountAsync() { + //常量 + var name = "TestCountAsync"; + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ); + products.ForEach( product => { + product.Init(); + product.Name = name; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = await _repository.CountAsync( t => t.Name == name ); + Assert.Equal( count, result ); + } + + #endregion + + #region Update + + /// + /// 测试更新方法1 - 查找并修改实体,直接提交工作单元 + /// + [Fact] + public async Task TestUpdate_1() { + //常量 + var code = "TestUpdate_1_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //查找实体并修改 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + + //直接提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试更新并发 - 使用更新方法1 + /// 更新方法1的缺陷: 通过赋值的方式更新查找实体的版本号,无法触发乐观锁 + /// + [Fact] + public async Task TestUpdate_1_Concurrency() { + //常量 + var code = "TestUpdate_1_Concurrency_Code"; + var description = "TestUpdate_1_Concurrency_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,查找实体,并进行赋值 + var result = _repository.FindById( entity.Id ); + result.Code = product.Code; + result.Description = product.Description; + result.Version = product.Version; + //提交工作单元,由于版本号变化,预期应触发乐观锁,但实际未触发 + await UnitOfWork.CommitAsync(); + + //结果:用户1覆盖了用户2设置的Description属性 + Assert.Equal( code, result.Code ); + Assert.NotEqual( description, result.Description ); + } + + /// + /// 测试更新方法2 - 创建未跟踪实体,调用dbcontext.Update方法 + /// + [Fact] + public async Task TestUpdate_2() { + //常量 + var code = "TestUpdate_2_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + //调用dbcontext.Update方法更新实体 + _repository.UpdateProduct( product ); + //提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试更新方法2的陷阱 + /// 如果在创建未跟踪实体并调用dbcontext.Update方法之前,从dbcontext中已查找出该实体,将发生异常 + /// + [Fact] + public async Task TestUpdate_2_InvalidOperation() { + //常量 + var code = "TestUpdate_2_InvalidOperation_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //模拟更新之前无意中已查找出该实体 + await _repository.FindByIdAsync( entity.Id ); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + + //由于实体已被跟踪,调用dbcontext.Update方法更新实体将触发异常 + Assert.Throws( () => { + _repository.UpdateProduct( product ); + } ); + } + + /// + /// 测试更新并发 - 使用更新方法2 + /// + [Fact] + public async Task TestUpdate_2_Concurrency() { + //常量 + var code = "TestUpdate_2_Concurrency_Code"; + var description = "TestUpdate_2_Concurrency_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,创建一个新的实体,并赋值 + var result = product.MapTo(); + + //调用dbcontext.Update方法更新实体 + _repository.UpdateProduct( result ); + + //提交工作单元,由于版本号变化,触发乐观锁 + await Assert.ThrowsAsync( async () => { + await UnitOfWork.CommitAsync(); + } ); + } + + /// + /// 测试Util Update更新方法 - 查找并修改实体,提交工作单元 + /// + [Fact] + public async Task TestUpdate_3_1() { + //常量 + var code = "TestUpdate_3_1_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //查找实体并修改 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + + //调用Update方法 + await _repository.UpdateAsync( product ); + + //提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试Util Update更新方法 - 创建未跟踪实体,调用Update方法 + /// + [Fact] + public async Task TestUpdate_3_2() { + //常量 + var code = "TestUpdate_3_2_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + + //调用Update方法更新实体 + await _repository.UpdateAsync( product ); + + //提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试Util Update更新方法 - 允许在创建未跟踪实体并调用Update方法之前查找实体,不会发生异常 + /// + [Fact] + public async Task TestUpdate_3_3() { + //常量 + var code = "TestUpdate_3_3_InvalidOperation_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //模拟更新之前无意中已查找出该实体 + await _repository.FindByIdAsync( entity.Id ); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + + //调用Update方法更新实体 + await _repository.UpdateAsync( product ); + + //提交工作单元 + _log.WriteLine( UnitOfWork.GetChangeTrackerDebugView() ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试更新并发 - 使用Util Update更新方法 + /// 通过赋值的方式更新查找实体的版本号,触发乐观锁 + /// + [Fact] + public async Task TestUpdate_3_Concurrency_1() { + //常量 + var code = "TestUpdate_3_Concurrency_1_Code"; + var description = "TestUpdate_3_Concurrency_1_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,查找实体,并进行赋值 + var result = await _repository.FindByIdAsync( entity.Id ); + result.Code = product.Code; + result.Description = product.Description; + result.Version = product.Version; + + //调用Update方法更新实体时触发乐观锁 + await Assert.ThrowsAsync( async () => { + await _repository.UpdateAsync( result ); + } ); + } + + /// + /// 测试更新并发 - 使用Util Update更新方法 + /// 更新未跟踪实体触发乐观锁 + /// + [Fact] + public async Task TestUpdate_3_Concurrency_2() { + //常量 + var code = "TestUpdate_3_Concurrency_2_Code"; + var description = "TestUpdate_3_Concurrency_2_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,创建一个新的实体,并赋值 + var result = product.MapTo(); + + //调用Update方法更新实体 + await _repository.UpdateAsync( result ); + + //提交工作单元,由于版本号变化,触发乐观锁 + await Assert.ThrowsAsync( async () => { + await UnitOfWork.CommitAsync(); + } ); + } + + /// + /// 测试更新集合 + /// + [Fact] + public async Task TestUpdate_4() { + //常量 + var count = 3; + var code = "TestUpdate_4_Code"; + + //添加实体列表 + var entities = ProductFakeService.GetProducts( count ); + entities.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( entities ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查找实体,赋值,调用Update方法,提交工作单元 + var products = await _repository.FindByIdsAsync( entities.Select( t => t.Id ) ); + products.ForEach( product => product.Code = code ); + await _repository.UpdateAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdsAsync( entities.Select( t => t.Id ) ); + Assert.Equal( code, result[0].Code ); + Assert.Equal( code, result[1].Code ); + Assert.Equal( code, result[2].Code ); + } + + #endregion + + #region Remove + + /// + /// 测试通过标识移除实体 - 逻辑删除 + /// + [Fact] + public async Task TestRemoveAsync_Id_1() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试通过标识移除实体 - 逻辑删除 - 使用工作单元禁用逻辑删除过滤器 + /// + [Fact] + public async Task TestRemoveAsync_Id_2() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //使用工作单元禁用逻辑删除过滤器,可以查出 + using( UnitOfWork.DisableDeleteFilter() ) { + var product = await _repository.FindByIdAsync( entity.Id ); + Assert.True( product.IsDeleted ); + Assert.Equal( entity.Code, product.Code ); + } + UnitOfWork.ClearCache(); + + //逻辑删除过滤器已启用,无法查出 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试通过标识移除实体 - 逻辑删除 - 使用仓储禁用逻辑删除过滤器 + /// + [Fact] + public async Task TestRemoveAsync_Id_3() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //使用仓储禁用逻辑删除过滤器,可以查出 + using( _repository.DisableDeleteFilter() ) { + var product = await _repository.FindByIdAsync( entity.Id ); + Assert.True( product.IsDeleted ); + Assert.Equal( entity.Code, product.Code ); + } + UnitOfWork.ClearCache(); + + //逻辑删除过滤器已启用,无法查出 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试移除实体 - 逻辑删除 + /// + [Fact] + public async Task TestRemoveAsync_Entity() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试移除实体集合 - 逻辑删除 - 通过标识列表 + /// + [Fact] + public async Task TestRemoveAsync_Entities_1() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //移除实体集合 + var ids = products.Select( t => t.Id ).ToList(); + await _repository.RemoveAsync( ids ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + using( _repository.DisableDeleteFilter() ) { + var result = await _repository.FindByIdsAsync( ids ); + Assert.Equal( count, result.Count ); + result.ForEach( t => Assert.True( t.IsDeleted ) ); + } + } + + /// + /// 测试移除实体集合 - 逻辑删除 - 通过实体列表 + /// + [Fact] + public async Task TestRemoveAsync_Entities_2() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //移除实体集合 + await _repository.RemoveAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + using( _repository.DisableDeleteFilter() ) { + var result = await _repository.FindByIdsAsync( products.Select( t => t.Id ) ); + Assert.Equal( count, result.Count ); + result.ForEach( t => Assert.True( t.IsDeleted ) ); + } + } + + #endregion + + #region Audit + + /// + /// 测试设置创建审计属性 + /// + [Fact] + public async Task TestCreationAudit() { + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _repository.AddAsync( entity ); + Assert.NotEqual( TestSession.TestUserId, entity.CreatorId ); + await UnitOfWork.CommitAsync(); + Assert.Equal( TestSession.TestUserId, entity.CreatorId ); + Assert.Equal( TestSession.TestUserId, entity.LastModifierId ); + } + + /// + /// 测试设置修改审计属性 + /// + [Fact] + public async Task TestModificationAudit() { + //工作单元 + var unitOfWork = (MySqlUnitOfWork)UnitOfWork; + + //常量 + var code = "TestModificationAudit"; + + //添加实体 + var entity = await AddProductAsync(); + unitOfWork.ClearCache(); + + //修改 + var product = entity.MapTo(); + product.Code = code; + await _repository.UpdateAsync( product ); + unitOfWork.SetSession( new TestSession { UserId = TestSession.TestUserId2.ToString() } ); + + //验证修改前审计属性 + Assert.Equal( TestSession.TestUserId, product.CreatorId ); + Assert.Equal( TestSession.TestUserId, product.LastModifierId ); + + //提交 + await unitOfWork.CommitAsync(); + + //验证修改后审计属性 + Assert.Equal( TestSession.TestUserId, product.CreatorId ); + Assert.Equal( TestSession.TestUserId2, product.LastModifierId ); + } + + #endregion + + #region ExtraProperties + + /// + /// 测试扩展属性 - 添加简单扩展属性 + /// + [Fact] + public async Task TestExtraProperties_1() { + //常量 + var value = "TestExtraProperties_1"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = value; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( value, result.TestProperty1 ); + } + + /// + /// 测试扩展属性 - 修改简单扩展属性 + /// + [Fact] + public async Task TestExtraProperties_2() { + //常量 + var oldValue = "TestExtraProperties_2_1"; + var newValue = "TestExtraProperties_2_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = oldValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( oldValue, oldEntity.TestProperty1 ); + + //修改实体 + oldEntity.TestProperty1 = newValue; + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newValue, result.TestProperty1 ); + } + + /// + /// 测试扩展属性 - 添加对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_3() { + //常量 + var code = 2; + var name = "TestExtraProperties_3"; + var enumValue = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = name; + entity.TestProperty2.EnumValue = enumValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id ); + Assert.Equal( code, result.TestProperty2.Code ); + Assert.Equal( name, result.TestProperty2.Name ); + Assert.Equal( enumValue, result.TestProperty2.EnumValue ); + } + + /// + /// 测试扩展属性 - 修改对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_4() { + //常量 + var code = 2; + var oldName = "TestExtraProperties_4_1"; + var newName = "TestExtraProperties_4_2"; + var oldEnumValue = ProductEnum.Hello; + var newEnumValue = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = oldName; + entity.TestProperty2.EnumValue = oldEnumValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, oldEntity.TestProperty2.Code ); + Assert.Equal( oldName, oldEntity.TestProperty2.Name ); + Assert.Equal( oldEnumValue, oldEntity.TestProperty2.EnumValue ); + + //修改实体 + oldEntity.TestProperty2.Name = newName; + oldEntity.TestProperty2.EnumValue = newEnumValue; + WriteTrackerDebug(); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newName, result.TestProperty2.Name ); + Assert.Equal( newEnumValue, result.TestProperty2.EnumValue ); + } + + /// + /// 测试扩展属性 - 添加对象集合扩展属性 + /// + [Fact] + public async Task TestExtraProperties_5() { + //常量 + var code = 1; + var name1 = "TestExtraProperties_5_1"; + var name2 = "TestExtraProperties_5_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperties.Add( new ProductItem { Code = code, Name = name1 } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = name2 } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( 2, result.TestProperties.Count ); + Assert.Equal( code, result.TestProperties[0].Code ); + Assert.Equal( name1, result.TestProperties[0].Name ); + Assert.Equal( code, result.TestProperties[1].Code ); + Assert.Equal( name2, result.TestProperties[1].Name ); + } + + /// + /// 测试扩展属性 - 修改对象集合扩展属性 + /// + [Fact] + public async Task TestExtraProperties_6() { + //常量 + var code = 1; + var oldName = "TestExtraProperties_6_1"; + var newName = "TestExtraProperties_6_2"; + var name3 = "TestExtraProperties_6_3"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( 2, oldEntity.TestProperties.Count ); + + //修改实体 + oldEntity.TestProperties[0].Name = newName; + oldEntity.TestProperties[1].Name = newName; + oldEntity.TestProperties.Add( new ProductItem { Code = code, Name = name3 } ); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( 3, result.TestProperties.Count ); + Assert.Equal( code, result.TestProperties[0].Code ); + Assert.Equal( newName, result.TestProperties[0].Name ); + Assert.Equal( code, result.TestProperties[1].Code ); + Assert.Equal( newName, result.TestProperties[1].Name ); + Assert.Equal( code, result.TestProperties[2].Code ); + Assert.Equal( name3, result.TestProperties[2].Name ); + } + + /// + /// 测试扩展属性 - 添加多个扩展属性 + /// + [Fact] + public async Task TestExtraProperties_7() { + //常量 + var code = 2; + var name = "TestExtraProperties_7"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = name; + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = name; + entity.TestProperties.Add( new ProductItem { Code = code, Name = name } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = name } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( name, result.TestProperty1 ); + Assert.Equal( code, result.TestProperty2.Code ); + Assert.Equal( name, result.TestProperty2.Name ); + Assert.Equal( code, result.TestProperties[0].Code ); + Assert.Equal( name, result.TestProperties[0].Name ); + Assert.Equal( code, result.TestProperties[1].Code ); + Assert.Equal( name, result.TestProperties[1].Name ); + } + + /// + /// 测试扩展属性 - 修改多个扩展属性 + /// + [Fact] + public async Task TestExtraProperties_8() { + //常量 + var code = 2; + var oldName = "TestExtraProperties_8_1"; + var newName = "TestExtraProperties_8_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = oldName; + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = oldName; + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( oldName, oldEntity.TestProperty1 ); + Assert.Equal( code, oldEntity.TestProperty2.Code ); + Assert.Equal( oldName, oldEntity.TestProperty2.Name ); + Assert.Equal( 2, oldEntity.TestProperties.Count ); + + //修改实体 + oldEntity.TestProperty1 = newName; + oldEntity.TestProperty2.Name = newName; + oldEntity.TestProperties[0].Name = newName; + oldEntity.TestProperties[1].Name = newName; + oldEntity.TestProperties.Add( new ProductItem{ Code = code, Name = newName } ); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newName, result.TestProperty1 ); + Assert.Equal( newName, result.TestProperty2.Name ); + Assert.Equal( 3, result.TestProperties.Count ); + Assert.Equal( newName, result.TestProperties[0].Name ); + Assert.Equal( newName, result.TestProperties[1].Name ); + Assert.Equal( newName, result.TestProperties[2].Name ); + } + + /// + /// 测试扩展属性 - 添加枚举扩展属性 + /// + [Fact] + public async Task TestExtraProperties_9() { + //常量 + ProductEnum value = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty3 = value; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( value, result.TestProperty3 ); + } + + /// + /// 测试扩展属性 - 修改枚举扩展属性 + /// + [Fact] + public async Task TestExtraProperties_10() { + //常量 + ProductEnum oldValue = ProductEnum.Hello; + ProductEnum newValue = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty3 = oldValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( oldValue, oldEntity.TestProperty3 ); + + //修改实体 + oldEntity.TestProperty3 = newValue; + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newValue, result.TestProperty3 ); + } + + /// + /// 测试扩展属性 - 添加不可变对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_11() { + //常量 + var code = 2; + var name = "TestExtraProperties_11"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty4 = new ProductItem2( code, name ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.TestProperty4.Code ); + Assert.Equal( name, result.TestProperty4.Name ); + } + + /// + /// 测试扩展属性 - 修改对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_12() { + //常量 + var code = 2; + var oldName = "TestExtraProperties_12_1"; + var newName = "TestExtraProperties_12_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty4 = new ProductItem2( code, oldName ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, oldEntity.TestProperty4.Code ); + Assert.Equal( oldName, oldEntity.TestProperty4.Name ); + + //修改实体 + oldEntity.TestProperty4 = new ProductItem2( code, newName ); + WriteTrackerDebug(); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newName, result.TestProperty4.Name ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/TagRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/TagRepositoryTest.cs new file mode 100644 index 000000000..783e7e6ed --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Tests/TagRepositoryTest.cs @@ -0,0 +1,41 @@ +using System.Threading.Tasks; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests; + +/// +/// 标签仓储测试 +/// +public class TagRepositoryTest : TestBase { + /// + /// 仓储 + /// + private readonly ITagRepository _repository; + + /// + /// 测试初始化 + /// + public TagRepositoryTest( ITagRepository repository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = repository; + } + + /// + /// 测试添加实体 + /// + [Fact] + public async Task TestAddAsync() { + //添加实体 + var entity = TagFakeService.GetTag(); + entity.Init(); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + Assert.True( await _repository.ExistsAsync( t => t.Id == entity.Id ) ); + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Util.Data.EntityFrameworkCore.MySql.Tests.Integration.csproj b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Util.Data.EntityFrameworkCore.MySql.Tests.Integration.csproj new file mode 100644 index 000000000..8f51bbbb7 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/Util.Data.EntityFrameworkCore.MySql.Tests.Integration.csproj @@ -0,0 +1,39 @@ + + + + $(NetTargetFramework) + false + Util.Data.EntityFrameworkCore + Util.Data.EntityFrameworkCore.Startup + + + + + Always + + + Always + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/appsettings.Development.json b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..77895a687 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Trace" + } + }, + "ConnectionStrings": { + "connection": "server=127.0.0.1;user=root;password=admin;database=Util.Data.EntityFrameworkCore.Test;port=3306;charset=utf8" + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/appsettings.json b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/appsettings.json new file mode 100644 index 000000000..3c59b9f2d --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.MySql.Tests.Integration/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + }, + "ConnectionStrings": { + "connection": "server=mysql.common;user=root;password=admin;database=Util.Data.EntityFrameworkCore.Test;port=3306;charset=utf8" + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Startup.cs b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Startup.cs new file mode 100644 index 000000000..b9130c6eb --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Startup.cs @@ -0,0 +1,45 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Aop; +using Util.Helpers; +using Util.Sessions; +using Util.Tests.Infrastructure; +using Util.Tests.UnitOfWorks; +using Xunit.DependencyInjection.Logging; + +namespace Util.Data.EntityFrameworkCore { + /// + /// + /// + public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .AsBuild() + .AddAop() + .AddOracleUnitOfWork( Config.GetConnectionString( "connection" ),isGuidToString:false ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddSingleton(); + InitDatabase( services ); + } + + /// + /// ʼݿ + /// + private void InitDatabase( IServiceCollection services ) { + var unitOfWork = services.BuildServiceProvider().GetService(); + unitOfWork.EnsureDeleted(); + unitOfWork.EnsureCreated(); + } + } +} diff --git a/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/CustomerRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/CustomerRepositoryTest.cs new file mode 100644 index 000000000..4f1362643 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/CustomerRepositoryTest.cs @@ -0,0 +1,604 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Util.Data.Queries; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 客户仓储测试 + /// 说明: + /// 1. 测试自增长int类型标识 + /// 2. 测试日期范围查询 + /// 3. 测试分页查询 + /// 4. 测试Utc日期 + /// + public class CustomerRepositoryTest : TestBase { + + #region 测试初始化 + + /// + /// 仓储 + /// + private readonly ICustomerRepository _repository; + + /// + /// 测试初始化 + /// + public CustomerRepositoryTest( ICustomerRepository repository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = repository; + } + + #endregion + + #region 辅助操作 + + /// + /// 添加实体列表 + /// + /// 添加数量 + /// 客户名称 + private async Task> AddCustomersAsync( int count = 2, string name = null ) { + var customers = CustomerFakeService.GetCustomers( count ); + customers.ForEach( customer => { + if ( name != null ) + customer.Name = name; + customer.CreationTime = null; + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + return customers.OrderBy( t => t.Code ).ToList(); + } + + #endregion + + #region Between + + /// + /// 测试添加范围查询条件 - 日期带时间 + /// + [Fact] + public async Task TestBetween_DateTime_1() { + //常量 + var name = "TestBetween_DateTime_1"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 06:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 06:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday ).ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 包含左边界 + /// + [Fact] + public async Task TestBetween_DateTime_2() { + //常量 + var name = "TestBetween_DateTime_2"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Left ).ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 包含右边界 + /// + [Fact] + public async Task TestBetween_DateTime_3() { + //常量 + var name = "TestBetween_DateTime_3"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Right ).ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 包含两边 + /// + [Fact] + public async Task TestBetween_DateTime_4() { + //常量 + var name = "TestBetween_DateTime_4"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Both ).ToListAsync(); + + //验证 + Assert.Equal( 3, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 不包含边界 + /// + [Fact] + public async Task TestBetween_DateTime_5() { + //常量 + var name = "TestBetween_DateTime_5"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Neither ).ToListAsync(); + + //验证 + Assert.Single( result ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 最大值为空 + /// + [Fact] + public async Task TestBetween_DateTime_6() { + //常量 + var name = "TestBetween_DateTime_6"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, null, boundary: Boundary.Both ).ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 最小值为空 + /// + [Fact] + public async Task TestBetween_DateTime_7() { + //常量 + var name = "TestBetween_DateTime_7"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, null, maxBirthday, boundary: Boundary.Both ).ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 包含左边界 + /// + [Fact] + public async Task TestBetween_Date_1() { + //常量 + var name = "TestBetween_Date_1"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Left ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + Assert.Equal( 6, result[0].Birthday.SafeValue().Day ); + Assert.Equal( 7, result[1].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 包含右边界 + /// + [Fact] + public async Task TestBetween_Date_2() { + //常量 + var name = "TestBetween_Date_2"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Right ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + Assert.Equal( 7, result[0].Birthday.SafeValue().Day ); + Assert.Equal( 8, result[1].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 包含两边 + /// + [Fact] + public async Task TestBetween_Date_3() { + //常量 + var name = "TestBetween_Date_3"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Both ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 3, result.Count ); + Assert.Equal( 6, result[0].Birthday.SafeValue().Day ); + Assert.Equal( 7, result[1].Birthday.SafeValue().Day ); + Assert.Equal( 8, result[2].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 不包含边界 + /// + [Fact] + public async Task TestBetween_Date_4() { + //常量 + var name = "TestBetween_Date_4"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Neither ) + .ToListAsync(); + + //验证 + Assert.Single( result ); + Assert.Equal( 7, result[0].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 最大值为空 + /// + [Fact] + public async Task TestBetween_Date_5() { + //常量 + var name = "TestBetween_Date_5"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Nickname = name; + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, null, false ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + Assert.Equal( 6, result[0].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 最小值为空 + /// + [Fact] + public async Task TestBetween_Date_6() { + //常量 + var name = "TestBetween_Date_6"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, null, maxBirthday, false ) + .ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + } + + #endregion + + #region ToPageList + + /// + /// 测试获取分页列表 + /// + [Fact] + public async Task TestToPageList_1() { + //常量 + var name = "TestToPageList_1"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 1, Order = "Code" }; + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).ToPageList( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 1, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[0].Id, result.Data[0].Id ); + Assert.Equal( customers[1].Id, result.Data[1].Id ); + Assert.Equal( customers[2].Id, result.Data[2].Id ); + } + + /// + /// 测试获取分页列表 - 第二页 + /// + [Fact] + public async Task TestToPageList_2() { + //常量 + var name = "TestToPageList_2"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 2, Order = "Code" }; + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).ToPageList( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 2, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[3].Id, result.Data[0].Id ); + Assert.Equal( customers[4].Id, result.Data[1].Id ); + Assert.Equal( customers[5].Id, result.Data[2].Id ); + } + + /// + /// 测试获取分页列表 - Queryable设置排序 + /// + [Fact] + public async Task TestToPageList_3() { + //常量 + var name = "TestToPageList_3"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 2 }; + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).OrderBy( t => t.Code ).ToPageList( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 2, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[3].Id, result.Data[0].Id ); + Assert.Equal( customers[4].Id, result.Data[1].Id ); + Assert.Equal( customers[5].Id, result.Data[2].Id ); + } + + #endregion + + #region ToPageListAsync + + /// + /// 测试获取分页列表 + /// + [Fact] + public async Task TestToPageListAsync() { + //常量 + var name = "TestToPageListAsync"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 2, Order = "Code" }; + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).ToPageListAsync( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 2, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[3].Id, result.Data[0].Id ); + Assert.Equal( customers[4].Id, result.Data[1].Id ); + Assert.Equal( customers[5].Id, result.Data[2].Id ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/EntityEventsTest.cs b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/EntityEventsTest.cs new file mode 100644 index 000000000..7dcdd8795 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/EntityEventsTest.cs @@ -0,0 +1,211 @@ +using System.Threading.Tasks; +using Util.Tests.EventHandlers; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 实体事件测试 + /// + public class EntityEventsTest : TestBase { + + #region 测试初始化 + + /// + /// 测试输出 + /// + private readonly ITestOutputHelper _testOutputHelper; + /// + /// 产品仓储 + /// + private readonly IProductRepository _productRepository; + /// + /// 操作日志仓储 + /// + private readonly IOperationLogRepository _operationLogRepository; + + /// + /// 测试初始化 + /// + public EntityEventsTest( ITestOutputHelper testOutputHelper, IProductRepository productRepository, + IOperationLogRepository operationLogRepository, ITestUnitOfWork unitOfWork ) :base(unitOfWork){ + _testOutputHelper = testOutputHelper; + _productRepository = productRepository; + _operationLogRepository = operationLogRepository; + } + + #endregion + + #region 实体创建事件 + + /// + /// 测试实体创建事件 + /// + [Fact] + public async Task TestEntityCreatedEvent() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityCreatedEvent"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityCreatedEvent" && t.LogName == nameof( ProductCreatedEventHandler ) ) ); + } + + /// + /// 测试实体变更事件 - 创建 + /// + [Fact] + public async Task TestEntityChangedEvent_Created() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityChangedEvent_Created"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityChangedEvent_Created" && t.LogName == nameof( ProductChangedEventHandler ) ) ); + } + + #endregion + + #region 实体修改事件 + + /// + /// 测试实体修改事件 + /// + [Fact] + public async Task TestEntityUpdatedEvent() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //修改实体 + var product = await _productRepository.FindByIdAsync( entity.Id ); + product.Name = "EntityUpdatedEvent"; + await _productRepository.UpdateAsync( product ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityUpdatedEvent" && t.LogName == nameof( ProductUpdatedEventHandler ) ) ); + } + + /// + /// 测试实体变更事件 - 修改 + /// + [Fact] + public async Task TestEntityChangedEvent_Updated() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //修改实体 + var product = _productRepository.FindById( entity.Id ); + product.Name = "EntityChangedEvent_Updated"; + await _productRepository.UpdateAsync( product ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityChangedEvent_Updated" && t.LogName == nameof( ProductChangedEventHandler ) ) ); + } + + #endregion + + #region 实体删除事件 + + /// + /// 测试实体删除事件 - 逻辑删除 + /// + [Fact] + public async Task TestEntityDeletedEvent_1() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityDeletedEvent"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _productRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityDeletedEvent" && t.LogName == nameof( ProductDeletedEventHandler ) ) ); + } + + /// + /// 测试实体删除事件 - 物理删除 + /// + [Fact] + public async Task TestEntityDeletedEvent_2() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + entity.Init(); + entity.LogName = "EntityDeletedEvent"; + await _operationLogRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _operationLogRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.LogName == "Test" && t.Caption == nameof( OperationLogDeletedEventHandler ) && t.Content == "EntityDeletedEvent" ) ); + } + + /// + /// 测试实体变更事件 - 逻辑删除 + /// + [Fact] + public async Task TestEntityChangedEvent_Deleted_1() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityChangedEvent_Deleted"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _productRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityChangedEvent_Deleted" && t.LogName == nameof( ProductChangedEventHandler ) ) ); + } + + /// + /// 测试实体变更事件 - 物理删除 + /// + [Fact] + public async Task TestEntityChangedEvent_Deleted_2() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + entity.Init(); + entity.LogName = "EntityChangedEvent_Deleted"; + await _operationLogRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _operationLogRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.LogName == "Test" && t.Caption == nameof( OperationLogChangedEventHandler ) && t.Content == "EntityChangedEvent_Deleted" ) ); + } + + #endregion + } +} diff --git a/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/OperationLogRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/OperationLogRepositoryTest.cs new file mode 100644 index 000000000..3e63861b6 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/OperationLogRepositoryTest.cs @@ -0,0 +1,63 @@ +using System.Threading.Tasks; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 操作日志仓储测试 + /// 说明: + /// 测试自增长long类型标识 + /// + public class OperationLogRepositoryTest : TestBase { + /// + /// 仓储 + /// + private readonly IOperationLogRepository _repository; + + /// + /// 测试初始化 + /// + public OperationLogRepositoryTest( IOperationLogRepository operationLogRepository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = operationLogRepository; + } + + /// + /// 测试添加实体 + /// + [Fact] + public async Task TestAdd() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( entity.Id > 0 ); + Assert.True( _repository.Exists( t => t.Id == entity.Id ) ); + } + + /// + /// 测试通过标识移除实体 - 物理删除 + /// + [Fact] + public async Task TestRemove_Id() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + } +} diff --git a/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/OrderRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/OrderRepositoryTest.cs new file mode 100644 index 000000000..713ac562a --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/OrderRepositoryTest.cs @@ -0,0 +1,130 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 订单仓储测试 + /// 说明: + /// 1. 测试string类型标识 + /// 2. 测试一对多导航属性 + /// + public class OrderRepositoryTest : TestBase { + + #region 测试初始化 + + /// + /// 仓储 + /// + private readonly IOrderRepository _repository; + + /// + /// 测试初始化 + /// + public OrderRepositoryTest( IOrderRepository repository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = repository; + } + + #endregion + + #region 辅助方法 + + /// + /// 添加订单 + /// + private async Task AddOrderAsync() { + var order = OrderFakeService.GetOrder(); + order.Init(); + order.OrderItems.Add( CreateOrderItem() ); + order.OrderItems.Add( CreateOrderItem() ); + await _repository.AddAsync( order ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + return order; + } + + /// + /// 创建订单项 + /// + private OrderItem CreateOrderItem() { + var item = OrderItemFakeService.GetOrderItem(); + item.Init(); + return item; + } + + #endregion + + #region Single + + /// + /// 测试查找单个实体 + /// + [Fact] + public async Task TestSingle_1() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id ); + + //验证 + Assert.Empty( result.OrderItems ); + } + + /// + /// 测试查找单个实体 - 使用Include包含子实体集合 + /// + [Fact] + public async Task TestSingle_2() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id, queryable => queryable.Include( t => t.OrderItems ) ); + + //验证 + Assert.Equal( 2, result.OrderItems.Count ); + } + + #endregion + + #region SingleAsync + + /// + /// 测试查找单个实体 + /// + [Fact] + public async Task TestSingleAsync_1() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id ); + + //验证 + Assert.Empty( result.OrderItems ); + } + + /// + /// 测试查找单个实体 - 使用Include包含子实体集合 + /// + [Fact] + public async Task TestSingleAsync_2() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id, queryable => queryable.Include( t => t.OrderItems ) ); + + //验证 + Assert.Equal( 2, result.OrderItems.Count ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/PostRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/PostRepositoryTest.cs new file mode 100644 index 000000000..573430a30 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/PostRepositoryTest.cs @@ -0,0 +1,54 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 贴子仓储测试 + /// 说明: + /// 1. 测试多对多 + /// + public class PostRepositoryTest : TestBase { + /// + /// 仓储 + /// + private readonly IPostRepository _repository; + + /// + /// 测试初始化 + /// + public PostRepositoryTest(IPostRepository repository, ITestUnitOfWork unitOfWork ) : base(unitOfWork){ + _repository = repository; + } + + /// + /// 测试添加实体 + /// + [Fact] + public async Task TestAddAsync() { + //添加实体 + var post = PostFakeService.GetPost(); + post.Init(); + var tag1 = TagFakeService.GetTag(); + tag1.Init(); + post.Tags.Add( tag1 ); + + var tag2 = TagFakeService.GetTag(); + tag2.Init(); + post.Tags.Add( tag2 ); + + await _repository.AddAsync( post ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.SingleAsync( t => t.Id == post.Id, queryable => queryable.Include( t => t.Tags ) ); + Assert.NotNull( result ); + Assert.Equal( 2,result.Tags.Count ); + } + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/ProductRepositoryTest.Queryable.cs b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/ProductRepositoryTest.Queryable.cs new file mode 100644 index 000000000..5ad8a9475 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/ProductRepositoryTest.Queryable.cs @@ -0,0 +1,390 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Util.Data.Queries; +using Util.Tests.Conditions; +using Util.Tests.Fakes; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 产品仓储测试 - 查询测试 + /// + public partial class ProductRepositoryTest { + + #region Where + + /// + /// 测试设置查询条件 - 添加查询条件对象 + /// + [Fact] + public async Task TestWhere() { + //常量 + var name = "TestWhere"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //创建查询条件 + var condition = new ProductNameCondition( name ); + + //查询 + var result = _repository.Find().Where( condition ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + Assert.True( result.TrueForAll( t => products.Any( p => p.Id == t.Id ) ) ); + } + + #endregion + + #region WhereIf + + /// + /// 测试设置查询条件 - IQueryable添加WhereIf扩展 - true + /// + [Fact] + public async Task TestWhereIf_True() { + //常量 + var name = "TestWhereIf_True"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().WhereIf( t => t.Name == name, true ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加WhereIf扩展 - false + /// + [Fact] + public async Task TestWhereIf_False() { + //常量 + var name = "TestWhereIf_False"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().WhereIf( t => t.Name == name, false ).ToList(); + + //验证 + Assert.NotEqual( products.Count, result.Count ); + } + + #endregion + + #region WhereIfNotEmpty + + /// + /// 测试设置查询条件 - IQueryable添加WhereIfNotEmpty扩展 - 有效条件 + /// + [Fact] + public async Task TestWhereIfNotEmpty_Valid() { + //常量 + var name = "TestWhereIfNotEmpty_Valid"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().WhereIfNotEmpty( t => t.Name == name ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加WhereIfNotEmpty扩展 - 忽略条件 + /// + [Fact] + public async Task TestWhereIfNotEmpty_Ignore() { + //常量 + var name = "TestWhereIfNotEmpty_Ignore"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).WhereIfNotEmpty( t => t.Code == "" ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加WhereIfNotEmpty扩展 - 无效条件,抛出异常 + /// + [Fact] + public async Task TestWhereIfNotEmpty_Invalid() { + //添加实体列表 + await AddProductsAsync(); + + //查询 + Assert.Throws( () => { + _repository.Find().WhereIfNotEmpty( t => t.Name == "a" && t.Code == "b" ); + } ); + } + + #endregion + + #region OrIfNotEmpty + + /// + /// 测试设置查询条件 - IQueryable添加OrIfNotEmpty扩展 - 有效条件 + /// + [Fact] + public async Task TestOrIfNotEmpty_Valid() { + //常量 + var name1 = "TestOrIfNotEmpty_Valid_1"; + var name2 = "TestOrIfNotEmpty_Valid_2"; + var name3 = "TestOrIfNotEmpty_Valid_3"; + + //添加实体列表 + await AddProductsAsync(); + await AddProductsAsync( 2, name1 ); + await AddProductsAsync( 3, name2 ); + await AddProductsAsync( 4, name3 ); + + //查询 + var result = _repository.Find().OrIfNotEmpty( t => t.Name == name1,t => t.Name == name2, t => t.Name == name3 ).ToList(); + + //验证 + Assert.Equal( 9, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加OrIfNotEmpty扩展 - 忽略条件 + /// + [Fact] + public async Task TestOrIfNotEmpty_Ignore() { + //常量 + var name1 = "TestOrIfNotEmpty_Ignore_1"; + var name2 = "TestOrIfNotEmpty_Ignore_2"; + var name3 = "TestOrIfNotEmpty_Ignore_3"; + + //添加实体列表 + await AddProductsAsync(); + await AddProductsAsync( 2, name1 ); + await AddProductsAsync( 3, name2 ); + await AddProductsAsync( 4, name3 ); + + //查询 + var result = _repository.Find().OrIfNotEmpty( t => t.Name == name1, t => t.Name == name2, t => t.Name == "" ).ToList(); + + //验证 + Assert.Equal( 5, result.Count ); + } + + #endregion + + #region Between + + /// + /// 测试添加范围查询条件 + /// + [Fact] + public async Task TestBetween_1() { + //常量 + var name = "TestBetween_1"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price,11.5M,13.5M ).ToList(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 包含左边界 + /// + [Fact] + public async Task TestBetween_2() { + //常量 + var name = "TestBetween_2"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M,Boundary.Left ).ToList(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 包含右边界 + /// + [Fact] + public async Task TestBetween_3() { + //常量 + var name = "TestBetween_3"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M, Boundary.Right ).ToList(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 包含两边 + /// + [Fact] + public async Task TestBetween_4() { + //常量 + var name = "TestBetween_4"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M ).ToList(); + + //验证 + Assert.Equal( 3, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 不包含边界 + /// + [Fact] + public async Task TestBetween_5() { + //常量 + var name = "TestBetween_5"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M,Boundary.Neither ).ToList(); + + //验证 + Assert.Single( result ); + } + + /// + /// 测试添加范围查询条件 - 最大值为空 + /// + [Fact] + public async Task TestBetween_6() { + //常量 + var name = "TestBetween_6"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 9.5M, null ).ToList(); + + //验证 + Assert.Equal( 5, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 最小值为空 + /// + [Fact] + public async Task TestBetween_7() { + //常量 + var name = "TestBetween_7"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, null, 15.5M ).ToList(); + + //验证 + Assert.Equal( 5, result.Count ); + } + + #endregion + } +} diff --git a/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/ProductRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/ProductRepositoryTest.cs new file mode 100644 index 000000000..7ad69c590 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/ProductRepositoryTest.cs @@ -0,0 +1,1408 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Util.Exceptions; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; +using Xunit.Abstractions; +using Xunit.DependencyInjection; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 产品仓储测试 + /// 说明: + /// 1. 测试Guid类型标识 + /// 2. 测试仓储基础方法 + /// 3. 测试扩展属性 + /// + public partial class ProductRepositoryTest : TestBase { + + #region 测试初始化 + + /// + /// 日志消息 + /// + private readonly ITestOutputHelper _log; + /// + /// 仓储 + /// + private readonly IProductRepository _repository; + + /// + /// 测试初始化 + /// + public ProductRepositoryTest( ITestOutputHelperAccessor accessor, IProductRepository repository, ITestUnitOfWork unitOfWork ) :base(unitOfWork) { + _log = accessor.Output; + _repository = repository; + } + + #endregion + + #region 辅助方法 + + /// + /// 添加产品 + /// + private async Task AddProductAsync( string name = null ) { + var entity = ProductFakeService.GetProduct(); + entity.Init(); + if( name != null ) + entity.Name = name; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + return entity; + } + + /// + /// 添加实体列表 + /// + /// 添加数量 + /// 产品名称 + private async Task> AddProductsAsync( int count = 2, string name = null ) { + var products = ProductFakeService.GetProducts( count ); + products.ForEach( product => { + product.Init(); + if( name != null ) + product.Name = name; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + return products.OrderBy( t => t.Id ).ToList(); + } + + /// + /// 打印更改跟踪调试信息 + /// + private void WriteTrackerDebug() { + _log.WriteLine( UnitOfWork.GetChangeTrackerDebugView() ); + } + + #endregion + + #region FindById + + /// + /// 测试通过标识查找实体 - 跟踪实体 + /// + [Fact] + public async Task TestFindById_Tracking_1() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 跟踪实体 - 字符串标识 + /// + [Fact] + public async Task TestFindById_Tracking_2() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id.ToString() ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 不跟踪实体 + /// + [Fact] + public async Task TestFindById_NoTracking() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.NoTracking().FindById( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region FindByIdAsync + + /// + /// 测试通过标识查找实体 - 跟踪 - 异步 + /// + [Fact] + public async Task TestFindByIdAsync_Tracking_1() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 跟踪 - 异步 - 字符串标识 + /// + [Fact] + public async Task TestFindByIdAsync_Tracking_2() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id.ToString() ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 不跟踪 - 异步 + /// + [Fact] + public async Task TestFindByIdAsync_NoTracking() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.NoTracking().FindByIdAsync( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region FindByIds + + /// + /// 测试通过标识列表查找实体列表 - 传入逗号分隔的标识列表 + /// + [Fact] + public async Task TestFindByIds_1() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = _repository.FindByIds( products[0].Id, products[1].Id, products[2].Id ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入集合 + /// + [Fact] + public async Task TestFindByIds_2() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = _repository.FindByIds( products.Select( t => t.Id ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入标识列表字符串 + /// + [Fact] + public async Task TestFindByIds_3() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = _repository.FindByIds( products.Select( t => t.Id ).Join() ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + #endregion + + #region FindByIdsAsync + + /// + /// 测试通过标识列表查找实体列表 - 传入逗号分隔的标识列表 - 异步 + /// + [Fact] + public async Task TestFindByIdsAsync_1() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = ( await _repository.FindByIdsAsync( products[0].Id, products[1].Id, products[2].Id ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入集合 - 异步 + /// + [Fact] + public async Task TestFindByIdsAsync_2() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = ( await _repository.FindByIdsAsync( products.Select( t => t.Id ) ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入标识列表字符串 - 异步 + /// + [Fact] + public async Task TestFindByIdsAsync_3() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = ( await _repository.FindByIdsAsync( products.Select( t => t.Id ).Join() ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + #endregion + + #region Single + + /// + /// 测试查找单个实体 + /// + [Fact] + public async Task TestSingle_1() { + //常量 + var name = "TestSingle"; + + //添加实体 + var entity = await AddProductAsync( name ); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.Single( t => t.Name == name ); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region SingleAsync + + /// + /// 测试查找单个实体 - 异步 + /// + [Fact] + public async Task TestSingleAsync() { + //常量 + var name = "TestSingleAsync"; + + //添加实体 + var entity = await AddProductAsync( name ); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.SingleAsync( t => t.Name == name ); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region Exists + + /// + /// 测试判断是否存在 + /// + [Fact] + public async Task TestExists() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + Assert.True( _repository.Exists( t => t.Id == entity.Id ) ); + } + + #endregion + + #region ExistsAsync + + /// + /// 测试判断是否存在 - 异步 + /// + [Fact] + public async Task TestExistsAsync() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + Assert.True( await _repository.ExistsAsync( t => t.Id == entity.Id ) ); + } + + #endregion + + #region Count + + /// + /// 测试查找数量 + /// + [Fact] + public async Task TestCount() { + //常量 + var name = "TestCount"; + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ); + products.ForEach( product => { + product.Init(); + product.Name = name; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = _repository.Count( t => t.Name == name ); + Assert.Equal( count, result ); + } + + #endregion + + #region CountAsync + + /// + /// 测试查找数量 - 异步 + /// + [Fact] + public async Task TestCountAsync() { + //常量 + var name = "TestCountAsync"; + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ); + products.ForEach( product => { + product.Init(); + product.Name = name; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = await _repository.CountAsync( t => t.Name == name ); + Assert.Equal( count, result ); + } + + #endregion + + #region Update + + /// + /// 测试更新方法1 - 查找并修改实体,直接提交工作单元 + /// + [Fact] + public async Task TestUpdate_1() { + //常量 + var code = "TestUpdate_1_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //查找实体并修改 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + + //直接提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试更新并发 - 使用更新方法1 + /// 更新方法1的缺陷: 通过赋值的方式更新查找实体的版本号,无法触发乐观锁 + /// + [Fact] + public async Task TestUpdate_1_Concurrency() { + //常量 + var code = "TestUpdate_1_Concurrency_Code"; + var description = "TestUpdate_1_Concurrency_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,查找实体,并进行赋值 + var result = _repository.FindById( entity.Id ); + result.Code = product.Code; + result.Description = product.Description; + result.Version = product.Version; + //提交工作单元,由于版本号变化,预期应触发乐观锁,但实际未触发 + await UnitOfWork.CommitAsync(); + + //结果:用户1覆盖了用户2设置的Description属性 + Assert.Equal( code, result.Code ); + Assert.NotEqual( description, result.Description ); + } + + /// + /// 测试更新方法2 - 创建未跟踪实体,调用dbcontext.Update方法 + /// + [Fact] + public async Task TestUpdate_2() { + //常量 + var code = "TestUpdate_2_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + //调用dbcontext.Update方法更新实体 + _repository.UpdateProduct( product ); + //提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试更新方法2的陷阱 + /// 如果在创建未跟踪实体并调用dbcontext.Update方法之前,从dbcontext中已查找出该实体,将发生异常 + /// + [Fact] + public async Task TestUpdate_2_InvalidOperation() { + //常量 + var code = "TestUpdate_2_InvalidOperation_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //模拟更新之前无意中已查找出该实体 + await _repository.FindByIdAsync( entity.Id ); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + + //由于实体已被跟踪,调用dbcontext.Update方法更新实体将触发异常 + Assert.Throws( () => { + _repository.UpdateProduct( product ); + } ); + } + + /// + /// 测试更新并发 - 使用更新方法2 + /// + [Fact] + public async Task TestUpdate_2_Concurrency() { + //常量 + var code = "TestUpdate_2_Concurrency_Code"; + var description = "TestUpdate_2_Concurrency_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,创建一个新的实体,并赋值 + var result = product.MapTo(); + + //调用dbcontext.Update方法更新实体 + _repository.UpdateProduct( result ); + + //提交工作单元,由于版本号变化,触发乐观锁 + await Assert.ThrowsAsync( async () => { + await UnitOfWork.CommitAsync(); + } ); + } + + /// + /// 测试Util Update更新方法 - 查找并修改实体,提交工作单元 + /// + [Fact] + public async Task TestUpdate_3_1() { + //常量 + var code = "TestUpdate_3_1_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //查找实体并修改 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + + //调用Update方法 + await _repository.UpdateAsync( product ); + + //提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试Util Update更新方法 - 创建未跟踪实体,调用Update方法 + /// + [Fact] + public async Task TestUpdate_3_2() { + //常量 + var code = "TestUpdate_3_2_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + + //调用Update方法更新实体 + await _repository.UpdateAsync( product ); + + //提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试Util Update更新方法 - 允许在创建未跟踪实体并调用Update方法之前查找实体,不会发生异常 + /// + [Fact] + public async Task TestUpdate_3_3() { + //常量 + var code = "TestUpdate_3_3_InvalidOperation_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //模拟更新之前无意中已查找出该实体 + await _repository.FindByIdAsync( entity.Id ); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + + //调用Update方法更新实体 + await _repository.UpdateAsync( product ); + + //提交工作单元 + _log.WriteLine( UnitOfWork.GetChangeTrackerDebugView() ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试更新并发 - 使用Util Update更新方法 + /// 通过赋值的方式更新查找实体的版本号,触发乐观锁 + /// + [Fact] + public async Task TestUpdate_3_Concurrency_1() { + //常量 + var code = "TestUpdate_3_Concurrency_1_Code"; + var description = "TestUpdate_3_Concurrency_1_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,查找实体,并进行赋值 + var result = await _repository.FindByIdAsync( entity.Id ); + result.Code = product.Code; + result.Description = product.Description; + result.Version = product.Version; + + //调用Update方法更新实体时触发乐观锁 + await Assert.ThrowsAsync( async () => { + await _repository.UpdateAsync( result ); + } ); + } + + /// + /// 测试更新并发 - 使用Util Update更新方法 + /// 更新未跟踪实体触发乐观锁 + /// + [Fact] + public async Task TestUpdate_3_Concurrency_2() { + //常量 + var code = "TestUpdate_3_Concurrency_2_Code"; + var description = "TestUpdate_3_Concurrency_2_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,创建一个新的实体,并赋值 + var result = product.MapTo(); + + //调用Update方法更新实体 + await _repository.UpdateAsync( result ); + + //提交工作单元,由于版本号变化,触发乐观锁 + await Assert.ThrowsAsync( async () => { + await UnitOfWork.CommitAsync(); + } ); + } + + /// + /// 测试更新集合 + /// + [Fact] + public async Task TestUpdate_4() { + //常量 + var count = 3; + var code = "TestUpdate_4_Code"; + + //添加实体列表 + var entities = ProductFakeService.GetProducts( count ); + entities.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( entities ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查找实体,赋值,调用Update方法,提交工作单元 + var products = await _repository.FindByIdsAsync( entities.Select( t => t.Id ) ); + products.ForEach( product => product.Code = code ); + await _repository.UpdateAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdsAsync( entities.Select( t => t.Id ) ); + Assert.Equal( code, result[0].Code ); + Assert.Equal( code, result[1].Code ); + Assert.Equal( code, result[2].Code ); + } + + #endregion + + #region Remove + + /// + /// 测试通过标识移除实体 - 逻辑删除 + /// + [Fact] + public async Task TestRemoveAsync_Id_1() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试通过标识移除实体 - 逻辑删除 - 使用工作单元禁用逻辑删除过滤器 + /// + [Fact] + public async Task TestRemoveAsync_Id_2() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //使用工作单元禁用逻辑删除过滤器,可以查出 + using( UnitOfWork.DisableDeleteFilter() ) { + var product = await _repository.FindByIdAsync( entity.Id ); + Assert.True( product.IsDeleted ); + Assert.Equal( entity.Code, product.Code ); + } + UnitOfWork.ClearCache(); + + //逻辑删除过滤器已启用,无法查出 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试通过标识移除实体 - 逻辑删除 - 使用仓储禁用逻辑删除过滤器 + /// + [Fact] + public async Task TestRemoveAsync_Id_3() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //使用仓储禁用逻辑删除过滤器,可以查出 + using( _repository.DisableDeleteFilter() ) { + var product = await _repository.FindByIdAsync( entity.Id ); + Assert.True( product.IsDeleted ); + Assert.Equal( entity.Code, product.Code ); + } + UnitOfWork.ClearCache(); + + //逻辑删除过滤器已启用,无法查出 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试移除实体 - 逻辑删除 + /// + [Fact] + public async Task TestRemoveAsync_Entity() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试移除实体集合 - 逻辑删除 - 通过标识列表 + /// + [Fact] + public async Task TestRemoveAsync_Entities_1() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //移除实体集合 + var ids = products.Select( t => t.Id ).ToList(); + await _repository.RemoveAsync( ids ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + using( _repository.DisableDeleteFilter() ) { + var result = await _repository.FindByIdsAsync( ids ); + Assert.Equal( count, result.Count ); + result.ForEach( t => Assert.True( t.IsDeleted ) ); + } + } + + /// + /// 测试移除实体集合 - 逻辑删除 - 通过实体列表 + /// + [Fact] + public async Task TestRemoveAsync_Entities_2() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //移除实体集合 + await _repository.RemoveAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + using( _repository.DisableDeleteFilter() ) { + var result = await _repository.FindByIdsAsync( products.Select( t => t.Id ) ); + Assert.Equal( count, result.Count ); + result.ForEach( t => Assert.True( t.IsDeleted ) ); + } + } + + #endregion + + #region Audit + + /// + /// 测试设置创建审计属性 + /// + [Fact] + public async Task TestCreationAudit() { + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _repository.AddAsync( entity ); + Assert.NotEqual( TestSession.TestUserId, entity.CreatorId ); + await UnitOfWork.CommitAsync(); + Assert.Equal( TestSession.TestUserId, entity.CreatorId ); + Assert.Equal( TestSession.TestUserId, entity.LastModifierId ); + } + + /// + /// 测试设置修改审计属性 + /// + [Fact] + public async Task TestModificationAudit() { + //工作单元 + var unitOfWork = (OracleUnitOfWork)UnitOfWork; + + //常量 + var code = "TestModificationAudit"; + + //添加实体 + var entity = await AddProductAsync(); + unitOfWork.ClearCache(); + + //修改 + var product = entity.MapTo(); + product.Code = code; + await _repository.UpdateAsync( product ); + unitOfWork.SetSession( new TestSession { UserId = TestSession.TestUserId2.ToString() } ); + + //验证修改前审计属性 + Assert.Equal( TestSession.TestUserId, product.CreatorId ); + Assert.Equal( TestSession.TestUserId, product.LastModifierId ); + + //提交 + await unitOfWork.CommitAsync(); + + //验证修改后审计属性 + Assert.Equal( TestSession.TestUserId, product.CreatorId ); + Assert.Equal( TestSession.TestUserId2, product.LastModifierId ); + } + + #endregion + + #region ExtraProperties + + /// + /// 测试扩展属性 - 添加简单扩展属性 + /// + [Fact] + public async Task TestExtraProperties_1() { + //常量 + var value = "TestExtraProperties_1"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = value; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( value, result.TestProperty1 ); + } + + /// + /// 测试扩展属性 - 修改简单扩展属性 + /// + [Fact] + public async Task TestExtraProperties_2() { + //常量 + var oldValue = "TestExtraProperties_2_1"; + var newValue = "TestExtraProperties_2_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = oldValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( oldValue, oldEntity.TestProperty1 ); + + //修改实体 + oldEntity.TestProperty1 = newValue; + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newValue, result.TestProperty1 ); + } + + /// + /// 测试扩展属性 - 添加对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_3() { + //常量 + var code = 2; + var name = "TestExtraProperties_3"; + var enumValue = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = name; + entity.TestProperty2.EnumValue = enumValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id ); + Assert.Equal( code, result.TestProperty2.Code ); + Assert.Equal( name, result.TestProperty2.Name ); + Assert.Equal( enumValue, result.TestProperty2.EnumValue ); + } + + /// + /// 测试扩展属性 - 修改对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_4() { + //常量 + var code = 2; + var oldName = "TestExtraProperties_4_1"; + var newName = "TestExtraProperties_4_2"; + var oldEnumValue = ProductEnum.Hello; + var newEnumValue = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = oldName; + entity.TestProperty2.EnumValue = oldEnumValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, oldEntity.TestProperty2.Code ); + Assert.Equal( oldName, oldEntity.TestProperty2.Name ); + Assert.Equal( oldEnumValue, oldEntity.TestProperty2.EnumValue ); + + //修改实体 + oldEntity.TestProperty2.Name = newName; + oldEntity.TestProperty2.EnumValue = newEnumValue; + WriteTrackerDebug(); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newName, result.TestProperty2.Name ); + Assert.Equal( newEnumValue, result.TestProperty2.EnumValue ); + } + + /// + /// 测试扩展属性 - 添加对象集合扩展属性 + /// + [Fact] + public async Task TestExtraProperties_5() { + //常量 + var code = 1; + var name1 = "TestExtraProperties_5_1"; + var name2 = "TestExtraProperties_5_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperties.Add( new ProductItem { Code = code, Name = name1 } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = name2 } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( 2, result.TestProperties.Count ); + Assert.Equal( code, result.TestProperties[0].Code ); + Assert.Equal( name1, result.TestProperties[0].Name ); + Assert.Equal( code, result.TestProperties[1].Code ); + Assert.Equal( name2, result.TestProperties[1].Name ); + } + + /// + /// 测试扩展属性 - 修改对象集合扩展属性 + /// + [Fact] + public async Task TestExtraProperties_6() { + //常量 + var code = 1; + var oldName = "TestExtraProperties_6_1"; + var newName = "TestExtraProperties_6_2"; + var name3 = "TestExtraProperties_6_3"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( 2, oldEntity.TestProperties.Count ); + + //修改实体 + oldEntity.TestProperties[0].Name = newName; + oldEntity.TestProperties[1].Name = newName; + oldEntity.TestProperties.Add( new ProductItem { Code = code, Name = name3 } ); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( 3, result.TestProperties.Count ); + Assert.Equal( code, result.TestProperties[0].Code ); + Assert.Equal( newName, result.TestProperties[0].Name ); + Assert.Equal( code, result.TestProperties[1].Code ); + Assert.Equal( newName, result.TestProperties[1].Name ); + Assert.Equal( code, result.TestProperties[2].Code ); + Assert.Equal( name3, result.TestProperties[2].Name ); + } + + /// + /// 测试扩展属性 - 添加多个扩展属性 + /// + [Fact] + public async Task TestExtraProperties_7() { + //常量 + var code = 2; + var name = "TestExtraProperties_7"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = name; + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = name; + entity.TestProperties.Add( new ProductItem { Code = code, Name = name } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = name } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( name, result.TestProperty1 ); + Assert.Equal( code, result.TestProperty2.Code ); + Assert.Equal( name, result.TestProperty2.Name ); + Assert.Equal( code, result.TestProperties[0].Code ); + Assert.Equal( name, result.TestProperties[0].Name ); + Assert.Equal( code, result.TestProperties[1].Code ); + Assert.Equal( name, result.TestProperties[1].Name ); + } + + /// + /// 测试扩展属性 - 修改多个扩展属性 + /// + [Fact] + public async Task TestExtraProperties_8() { + //常量 + var code = 2; + var oldName = "TestExtraProperties_8_1"; + var newName = "TestExtraProperties_8_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = oldName; + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = oldName; + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( oldName, oldEntity.TestProperty1 ); + Assert.Equal( code, oldEntity.TestProperty2.Code ); + Assert.Equal( oldName, oldEntity.TestProperty2.Name ); + Assert.Equal( 2, oldEntity.TestProperties.Count ); + + //修改实体 + oldEntity.TestProperty1 = newName; + oldEntity.TestProperty2.Name = newName; + oldEntity.TestProperties[0].Name = newName; + oldEntity.TestProperties[1].Name = newName; + oldEntity.TestProperties.Add( new ProductItem{ Code = code, Name = newName } ); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newName, result.TestProperty1 ); + Assert.Equal( newName, result.TestProperty2.Name ); + Assert.Equal( 3, result.TestProperties.Count ); + Assert.Equal( newName, result.TestProperties[0].Name ); + Assert.Equal( newName, result.TestProperties[1].Name ); + Assert.Equal( newName, result.TestProperties[2].Name ); + } + + /// + /// 测试扩展属性 - 添加枚举扩展属性 + /// + [Fact] + public async Task TestExtraProperties_9() { + //常量 + ProductEnum value = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty3 = value; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( value, result.TestProperty3 ); + } + + /// + /// 测试扩展属性 - 修改枚举扩展属性 + /// + [Fact] + public async Task TestExtraProperties_10() { + //常量 + ProductEnum oldValue = ProductEnum.Hello; + ProductEnum newValue = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty3 = oldValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( oldValue, oldEntity.TestProperty3 ); + + //修改实体 + oldEntity.TestProperty3 = newValue; + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newValue, result.TestProperty3 ); + } + + /// + /// 测试扩展属性 - 添加不可变对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_11() { + //常量 + var code = 2; + var name = "TestExtraProperties_11"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty4 = new ProductItem2( code, name ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.TestProperty4.Code ); + Assert.Equal( name, result.TestProperty4.Name ); + } + + /// + /// 测试扩展属性 - 修改对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_12() { + //常量 + var code = 2; + var oldName = "TestExtraProperties_12_1"; + var newName = "TestExtraProperties_12_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty4 = new ProductItem2( code, oldName ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, oldEntity.TestProperty4.Code ); + Assert.Equal( oldName, oldEntity.TestProperty4.Name ); + + //修改实体 + oldEntity.TestProperty4 = new ProductItem2( code, newName ); + WriteTrackerDebug(); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newName, result.TestProperty4.Name ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/TagRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/TagRepositoryTest.cs new file mode 100644 index 000000000..cec65b128 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Tests/TagRepositoryTest.cs @@ -0,0 +1,41 @@ +using System.Threading.Tasks; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 标签仓储测试 + /// + public class TagRepositoryTest : TestBase { + /// + /// 仓储 + /// + private readonly ITagRepository _repository; + + /// + /// 测试初始化 + /// + public TagRepositoryTest( ITagRepository repository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = repository; + } + + /// + /// 测试添加实体 + /// + [Fact] + public async Task TestAddAsync() { + //添加实体 + var entity = TagFakeService.GetTag(); + entity.Init(); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + Assert.True( await _repository.ExistsAsync( t => t.Id == entity.Id ) ); + } + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration.csproj b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration.csproj new file mode 100644 index 000000000..2ebb191f2 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration.csproj @@ -0,0 +1,40 @@ + + + + $(NetTargetFramework) + false + Util.Data.EntityFrameworkCore + Util.Data.EntityFrameworkCore.Startup + + + + + Always + + + Always + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/appsettings.Development.json b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..1ba2c378d --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Trace" + } + }, + "ConnectionStrings": { + "connection": "User Id=c##test;Password=test;Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1521)));" + } +} diff --git a/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/appsettings.json b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/appsettings.json new file mode 100644 index 000000000..39722103e --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Oracle.Tests.Integration/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + }, + "ConnectionStrings": { + "connection": "" + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Startup.cs b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Startup.cs new file mode 100644 index 000000000..2dacbb2af --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Startup.cs @@ -0,0 +1,47 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Aop; +using Util.Dates; +using Util.Helpers; +using Util.Sessions; +using Util.Tests.Infrastructure; +using Util.Tests.UnitOfWorks; +using Xunit.DependencyInjection.Logging; + +namespace Util.Data.EntityFrameworkCore { + /// + /// + /// + public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .AsBuild() + .AddAop() + .AddUtc() + .AddPgSqlUnitOfWork( Config.GetConnectionString( "connection" ) ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddSingleton(); + InitDatabase( services ); + } + + /// + /// ʼݿ + /// + private void InitDatabase( IServiceCollection services ) { + var unitOfWork = services.BuildServiceProvider().GetService(); + unitOfWork.EnsureDeleted(); + unitOfWork.EnsureCreated(); + } + } +} diff --git a/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/CustomerRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/CustomerRepositoryTest.cs new file mode 100644 index 000000000..4f1362643 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/CustomerRepositoryTest.cs @@ -0,0 +1,604 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Util.Data.Queries; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 客户仓储测试 + /// 说明: + /// 1. 测试自增长int类型标识 + /// 2. 测试日期范围查询 + /// 3. 测试分页查询 + /// 4. 测试Utc日期 + /// + public class CustomerRepositoryTest : TestBase { + + #region 测试初始化 + + /// + /// 仓储 + /// + private readonly ICustomerRepository _repository; + + /// + /// 测试初始化 + /// + public CustomerRepositoryTest( ICustomerRepository repository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = repository; + } + + #endregion + + #region 辅助操作 + + /// + /// 添加实体列表 + /// + /// 添加数量 + /// 客户名称 + private async Task> AddCustomersAsync( int count = 2, string name = null ) { + var customers = CustomerFakeService.GetCustomers( count ); + customers.ForEach( customer => { + if ( name != null ) + customer.Name = name; + customer.CreationTime = null; + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + return customers.OrderBy( t => t.Code ).ToList(); + } + + #endregion + + #region Between + + /// + /// 测试添加范围查询条件 - 日期带时间 + /// + [Fact] + public async Task TestBetween_DateTime_1() { + //常量 + var name = "TestBetween_DateTime_1"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 06:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 06:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday ).ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 包含左边界 + /// + [Fact] + public async Task TestBetween_DateTime_2() { + //常量 + var name = "TestBetween_DateTime_2"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Left ).ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 包含右边界 + /// + [Fact] + public async Task TestBetween_DateTime_3() { + //常量 + var name = "TestBetween_DateTime_3"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Right ).ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 包含两边 + /// + [Fact] + public async Task TestBetween_DateTime_4() { + //常量 + var name = "TestBetween_DateTime_4"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Both ).ToListAsync(); + + //验证 + Assert.Equal( 3, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 不包含边界 + /// + [Fact] + public async Task TestBetween_DateTime_5() { + //常量 + var name = "TestBetween_DateTime_5"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Neither ).ToListAsync(); + + //验证 + Assert.Single( result ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 最大值为空 + /// + [Fact] + public async Task TestBetween_DateTime_6() { + //常量 + var name = "TestBetween_DateTime_6"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, null, boundary: Boundary.Both ).ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 最小值为空 + /// + [Fact] + public async Task TestBetween_DateTime_7() { + //常量 + var name = "TestBetween_DateTime_7"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, null, maxBirthday, boundary: Boundary.Both ).ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 包含左边界 + /// + [Fact] + public async Task TestBetween_Date_1() { + //常量 + var name = "TestBetween_Date_1"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Left ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + Assert.Equal( 6, result[0].Birthday.SafeValue().Day ); + Assert.Equal( 7, result[1].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 包含右边界 + /// + [Fact] + public async Task TestBetween_Date_2() { + //常量 + var name = "TestBetween_Date_2"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Right ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + Assert.Equal( 7, result[0].Birthday.SafeValue().Day ); + Assert.Equal( 8, result[1].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 包含两边 + /// + [Fact] + public async Task TestBetween_Date_3() { + //常量 + var name = "TestBetween_Date_3"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Both ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 3, result.Count ); + Assert.Equal( 6, result[0].Birthday.SafeValue().Day ); + Assert.Equal( 7, result[1].Birthday.SafeValue().Day ); + Assert.Equal( 8, result[2].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 不包含边界 + /// + [Fact] + public async Task TestBetween_Date_4() { + //常量 + var name = "TestBetween_Date_4"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Neither ) + .ToListAsync(); + + //验证 + Assert.Single( result ); + Assert.Equal( 7, result[0].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 最大值为空 + /// + [Fact] + public async Task TestBetween_Date_5() { + //常量 + var name = "TestBetween_Date_5"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Nickname = name; + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, null, false ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + Assert.Equal( 6, result[0].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 最小值为空 + /// + [Fact] + public async Task TestBetween_Date_6() { + //常量 + var name = "TestBetween_Date_6"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, null, maxBirthday, false ) + .ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + } + + #endregion + + #region ToPageList + + /// + /// 测试获取分页列表 + /// + [Fact] + public async Task TestToPageList_1() { + //常量 + var name = "TestToPageList_1"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 1, Order = "Code" }; + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).ToPageList( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 1, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[0].Id, result.Data[0].Id ); + Assert.Equal( customers[1].Id, result.Data[1].Id ); + Assert.Equal( customers[2].Id, result.Data[2].Id ); + } + + /// + /// 测试获取分页列表 - 第二页 + /// + [Fact] + public async Task TestToPageList_2() { + //常量 + var name = "TestToPageList_2"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 2, Order = "Code" }; + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).ToPageList( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 2, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[3].Id, result.Data[0].Id ); + Assert.Equal( customers[4].Id, result.Data[1].Id ); + Assert.Equal( customers[5].Id, result.Data[2].Id ); + } + + /// + /// 测试获取分页列表 - Queryable设置排序 + /// + [Fact] + public async Task TestToPageList_3() { + //常量 + var name = "TestToPageList_3"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 2 }; + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).OrderBy( t => t.Code ).ToPageList( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 2, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[3].Id, result.Data[0].Id ); + Assert.Equal( customers[4].Id, result.Data[1].Id ); + Assert.Equal( customers[5].Id, result.Data[2].Id ); + } + + #endregion + + #region ToPageListAsync + + /// + /// 测试获取分页列表 + /// + [Fact] + public async Task TestToPageListAsync() { + //常量 + var name = "TestToPageListAsync"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 2, Order = "Code" }; + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).ToPageListAsync( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 2, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[3].Id, result.Data[0].Id ); + Assert.Equal( customers[4].Id, result.Data[1].Id ); + Assert.Equal( customers[5].Id, result.Data[2].Id ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/EntityEventsTest.cs b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/EntityEventsTest.cs new file mode 100644 index 000000000..7dcdd8795 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/EntityEventsTest.cs @@ -0,0 +1,211 @@ +using System.Threading.Tasks; +using Util.Tests.EventHandlers; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 实体事件测试 + /// + public class EntityEventsTest : TestBase { + + #region 测试初始化 + + /// + /// 测试输出 + /// + private readonly ITestOutputHelper _testOutputHelper; + /// + /// 产品仓储 + /// + private readonly IProductRepository _productRepository; + /// + /// 操作日志仓储 + /// + private readonly IOperationLogRepository _operationLogRepository; + + /// + /// 测试初始化 + /// + public EntityEventsTest( ITestOutputHelper testOutputHelper, IProductRepository productRepository, + IOperationLogRepository operationLogRepository, ITestUnitOfWork unitOfWork ) :base(unitOfWork){ + _testOutputHelper = testOutputHelper; + _productRepository = productRepository; + _operationLogRepository = operationLogRepository; + } + + #endregion + + #region 实体创建事件 + + /// + /// 测试实体创建事件 + /// + [Fact] + public async Task TestEntityCreatedEvent() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityCreatedEvent"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityCreatedEvent" && t.LogName == nameof( ProductCreatedEventHandler ) ) ); + } + + /// + /// 测试实体变更事件 - 创建 + /// + [Fact] + public async Task TestEntityChangedEvent_Created() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityChangedEvent_Created"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityChangedEvent_Created" && t.LogName == nameof( ProductChangedEventHandler ) ) ); + } + + #endregion + + #region 实体修改事件 + + /// + /// 测试实体修改事件 + /// + [Fact] + public async Task TestEntityUpdatedEvent() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //修改实体 + var product = await _productRepository.FindByIdAsync( entity.Id ); + product.Name = "EntityUpdatedEvent"; + await _productRepository.UpdateAsync( product ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityUpdatedEvent" && t.LogName == nameof( ProductUpdatedEventHandler ) ) ); + } + + /// + /// 测试实体变更事件 - 修改 + /// + [Fact] + public async Task TestEntityChangedEvent_Updated() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //修改实体 + var product = _productRepository.FindById( entity.Id ); + product.Name = "EntityChangedEvent_Updated"; + await _productRepository.UpdateAsync( product ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityChangedEvent_Updated" && t.LogName == nameof( ProductChangedEventHandler ) ) ); + } + + #endregion + + #region 实体删除事件 + + /// + /// 测试实体删除事件 - 逻辑删除 + /// + [Fact] + public async Task TestEntityDeletedEvent_1() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityDeletedEvent"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _productRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityDeletedEvent" && t.LogName == nameof( ProductDeletedEventHandler ) ) ); + } + + /// + /// 测试实体删除事件 - 物理删除 + /// + [Fact] + public async Task TestEntityDeletedEvent_2() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + entity.Init(); + entity.LogName = "EntityDeletedEvent"; + await _operationLogRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _operationLogRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.LogName == "Test" && t.Caption == nameof( OperationLogDeletedEventHandler ) && t.Content == "EntityDeletedEvent" ) ); + } + + /// + /// 测试实体变更事件 - 逻辑删除 + /// + [Fact] + public async Task TestEntityChangedEvent_Deleted_1() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityChangedEvent_Deleted"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _productRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityChangedEvent_Deleted" && t.LogName == nameof( ProductChangedEventHandler ) ) ); + } + + /// + /// 测试实体变更事件 - 物理删除 + /// + [Fact] + public async Task TestEntityChangedEvent_Deleted_2() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + entity.Init(); + entity.LogName = "EntityChangedEvent_Deleted"; + await _operationLogRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _operationLogRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.LogName == "Test" && t.Caption == nameof( OperationLogChangedEventHandler ) && t.Content == "EntityChangedEvent_Deleted" ) ); + } + + #endregion + } +} diff --git a/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/OperationLogRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/OperationLogRepositoryTest.cs new file mode 100644 index 000000000..7a32b0f96 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/OperationLogRepositoryTest.cs @@ -0,0 +1,63 @@ +using System.Threading.Tasks; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 操作日志仓储测试 + /// 说明: + /// 测试自增长long类型标识 + /// + public class OperationLogRepositoryTest : TestBase { + /// + /// 仓储 + /// + private readonly IOperationLogRepository _repository; + + /// + /// 测试初始化 + /// + public OperationLogRepositoryTest( IOperationLogRepository operationLogRepository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = operationLogRepository; + } + + /// + /// 测试添加实体 + /// + [Fact] + public async Task TestAdd() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( entity.Id > 0 ); + Assert.True( await _repository.ExistsAsync( t => t.Id == entity.Id ) ); + } + + /// + /// 测试通过标识移除实体 - 物理删除 + /// + [Fact] + public async Task TestRemove_Id() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + } +} diff --git a/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/OrderRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/OrderRepositoryTest.cs new file mode 100644 index 000000000..713ac562a --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/OrderRepositoryTest.cs @@ -0,0 +1,130 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 订单仓储测试 + /// 说明: + /// 1. 测试string类型标识 + /// 2. 测试一对多导航属性 + /// + public class OrderRepositoryTest : TestBase { + + #region 测试初始化 + + /// + /// 仓储 + /// + private readonly IOrderRepository _repository; + + /// + /// 测试初始化 + /// + public OrderRepositoryTest( IOrderRepository repository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = repository; + } + + #endregion + + #region 辅助方法 + + /// + /// 添加订单 + /// + private async Task AddOrderAsync() { + var order = OrderFakeService.GetOrder(); + order.Init(); + order.OrderItems.Add( CreateOrderItem() ); + order.OrderItems.Add( CreateOrderItem() ); + await _repository.AddAsync( order ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + return order; + } + + /// + /// 创建订单项 + /// + private OrderItem CreateOrderItem() { + var item = OrderItemFakeService.GetOrderItem(); + item.Init(); + return item; + } + + #endregion + + #region Single + + /// + /// 测试查找单个实体 + /// + [Fact] + public async Task TestSingle_1() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id ); + + //验证 + Assert.Empty( result.OrderItems ); + } + + /// + /// 测试查找单个实体 - 使用Include包含子实体集合 + /// + [Fact] + public async Task TestSingle_2() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id, queryable => queryable.Include( t => t.OrderItems ) ); + + //验证 + Assert.Equal( 2, result.OrderItems.Count ); + } + + #endregion + + #region SingleAsync + + /// + /// 测试查找单个实体 + /// + [Fact] + public async Task TestSingleAsync_1() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id ); + + //验证 + Assert.Empty( result.OrderItems ); + } + + /// + /// 测试查找单个实体 - 使用Include包含子实体集合 + /// + [Fact] + public async Task TestSingleAsync_2() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id, queryable => queryable.Include( t => t.OrderItems ) ); + + //验证 + Assert.Equal( 2, result.OrderItems.Count ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/PostRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/PostRepositoryTest.cs new file mode 100644 index 000000000..573430a30 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/PostRepositoryTest.cs @@ -0,0 +1,54 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 贴子仓储测试 + /// 说明: + /// 1. 测试多对多 + /// + public class PostRepositoryTest : TestBase { + /// + /// 仓储 + /// + private readonly IPostRepository _repository; + + /// + /// 测试初始化 + /// + public PostRepositoryTest(IPostRepository repository, ITestUnitOfWork unitOfWork ) : base(unitOfWork){ + _repository = repository; + } + + /// + /// 测试添加实体 + /// + [Fact] + public async Task TestAddAsync() { + //添加实体 + var post = PostFakeService.GetPost(); + post.Init(); + var tag1 = TagFakeService.GetTag(); + tag1.Init(); + post.Tags.Add( tag1 ); + + var tag2 = TagFakeService.GetTag(); + tag2.Init(); + post.Tags.Add( tag2 ); + + await _repository.AddAsync( post ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.SingleAsync( t => t.Id == post.Id, queryable => queryable.Include( t => t.Tags ) ); + Assert.NotNull( result ); + Assert.Equal( 2,result.Tags.Count ); + } + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/ProductRepositoryTest.Queryable.cs b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/ProductRepositoryTest.Queryable.cs new file mode 100644 index 000000000..5ad8a9475 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/ProductRepositoryTest.Queryable.cs @@ -0,0 +1,390 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Util.Data.Queries; +using Util.Tests.Conditions; +using Util.Tests.Fakes; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 产品仓储测试 - 查询测试 + /// + public partial class ProductRepositoryTest { + + #region Where + + /// + /// 测试设置查询条件 - 添加查询条件对象 + /// + [Fact] + public async Task TestWhere() { + //常量 + var name = "TestWhere"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //创建查询条件 + var condition = new ProductNameCondition( name ); + + //查询 + var result = _repository.Find().Where( condition ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + Assert.True( result.TrueForAll( t => products.Any( p => p.Id == t.Id ) ) ); + } + + #endregion + + #region WhereIf + + /// + /// 测试设置查询条件 - IQueryable添加WhereIf扩展 - true + /// + [Fact] + public async Task TestWhereIf_True() { + //常量 + var name = "TestWhereIf_True"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().WhereIf( t => t.Name == name, true ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加WhereIf扩展 - false + /// + [Fact] + public async Task TestWhereIf_False() { + //常量 + var name = "TestWhereIf_False"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().WhereIf( t => t.Name == name, false ).ToList(); + + //验证 + Assert.NotEqual( products.Count, result.Count ); + } + + #endregion + + #region WhereIfNotEmpty + + /// + /// 测试设置查询条件 - IQueryable添加WhereIfNotEmpty扩展 - 有效条件 + /// + [Fact] + public async Task TestWhereIfNotEmpty_Valid() { + //常量 + var name = "TestWhereIfNotEmpty_Valid"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().WhereIfNotEmpty( t => t.Name == name ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加WhereIfNotEmpty扩展 - 忽略条件 + /// + [Fact] + public async Task TestWhereIfNotEmpty_Ignore() { + //常量 + var name = "TestWhereIfNotEmpty_Ignore"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).WhereIfNotEmpty( t => t.Code == "" ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加WhereIfNotEmpty扩展 - 无效条件,抛出异常 + /// + [Fact] + public async Task TestWhereIfNotEmpty_Invalid() { + //添加实体列表 + await AddProductsAsync(); + + //查询 + Assert.Throws( () => { + _repository.Find().WhereIfNotEmpty( t => t.Name == "a" && t.Code == "b" ); + } ); + } + + #endregion + + #region OrIfNotEmpty + + /// + /// 测试设置查询条件 - IQueryable添加OrIfNotEmpty扩展 - 有效条件 + /// + [Fact] + public async Task TestOrIfNotEmpty_Valid() { + //常量 + var name1 = "TestOrIfNotEmpty_Valid_1"; + var name2 = "TestOrIfNotEmpty_Valid_2"; + var name3 = "TestOrIfNotEmpty_Valid_3"; + + //添加实体列表 + await AddProductsAsync(); + await AddProductsAsync( 2, name1 ); + await AddProductsAsync( 3, name2 ); + await AddProductsAsync( 4, name3 ); + + //查询 + var result = _repository.Find().OrIfNotEmpty( t => t.Name == name1,t => t.Name == name2, t => t.Name == name3 ).ToList(); + + //验证 + Assert.Equal( 9, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加OrIfNotEmpty扩展 - 忽略条件 + /// + [Fact] + public async Task TestOrIfNotEmpty_Ignore() { + //常量 + var name1 = "TestOrIfNotEmpty_Ignore_1"; + var name2 = "TestOrIfNotEmpty_Ignore_2"; + var name3 = "TestOrIfNotEmpty_Ignore_3"; + + //添加实体列表 + await AddProductsAsync(); + await AddProductsAsync( 2, name1 ); + await AddProductsAsync( 3, name2 ); + await AddProductsAsync( 4, name3 ); + + //查询 + var result = _repository.Find().OrIfNotEmpty( t => t.Name == name1, t => t.Name == name2, t => t.Name == "" ).ToList(); + + //验证 + Assert.Equal( 5, result.Count ); + } + + #endregion + + #region Between + + /// + /// 测试添加范围查询条件 + /// + [Fact] + public async Task TestBetween_1() { + //常量 + var name = "TestBetween_1"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price,11.5M,13.5M ).ToList(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 包含左边界 + /// + [Fact] + public async Task TestBetween_2() { + //常量 + var name = "TestBetween_2"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M,Boundary.Left ).ToList(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 包含右边界 + /// + [Fact] + public async Task TestBetween_3() { + //常量 + var name = "TestBetween_3"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M, Boundary.Right ).ToList(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 包含两边 + /// + [Fact] + public async Task TestBetween_4() { + //常量 + var name = "TestBetween_4"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M ).ToList(); + + //验证 + Assert.Equal( 3, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 不包含边界 + /// + [Fact] + public async Task TestBetween_5() { + //常量 + var name = "TestBetween_5"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M,Boundary.Neither ).ToList(); + + //验证 + Assert.Single( result ); + } + + /// + /// 测试添加范围查询条件 - 最大值为空 + /// + [Fact] + public async Task TestBetween_6() { + //常量 + var name = "TestBetween_6"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 9.5M, null ).ToList(); + + //验证 + Assert.Equal( 5, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 最小值为空 + /// + [Fact] + public async Task TestBetween_7() { + //常量 + var name = "TestBetween_7"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, null, 15.5M ).ToList(); + + //验证 + Assert.Equal( 5, result.Count ); + } + + #endregion + } +} diff --git a/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/ProductRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/ProductRepositoryTest.cs new file mode 100644 index 000000000..53e235c2e --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/ProductRepositoryTest.cs @@ -0,0 +1,1408 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Util.Exceptions; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; +using Xunit.Abstractions; +using Xunit.DependencyInjection; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 产品仓储测试 + /// 说明: + /// 1. 测试Guid类型标识 + /// 2. 测试仓储基础方法 + /// 3. 测试扩展属性 + /// + public partial class ProductRepositoryTest : TestBase { + + #region 测试初始化 + + /// + /// 日志消息 + /// + private readonly ITestOutputHelper _log; + /// + /// 仓储 + /// + private readonly IProductRepository _repository; + + /// + /// 测试初始化 + /// + public ProductRepositoryTest( ITestOutputHelperAccessor accessor, IProductRepository repository, ITestUnitOfWork unitOfWork ) :base(unitOfWork) { + _log = accessor.Output; + _repository = repository; + } + + #endregion + + #region 辅助方法 + + /// + /// 添加产品 + /// + private async Task AddProductAsync( string name = null ) { + var entity = ProductFakeService.GetProduct(); + entity.Init(); + if( name != null ) + entity.Name = name; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + return entity; + } + + /// + /// 添加实体列表 + /// + /// 添加数量 + /// 产品名称 + private async Task> AddProductsAsync( int count = 2, string name = null ) { + var products = ProductFakeService.GetProducts( count ); + products.ForEach( product => { + product.Init(); + if( name != null ) + product.Name = name; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + return products.OrderBy( t => t.Id ).ToList(); + } + + /// + /// 打印更改跟踪调试信息 + /// + private void WriteTrackerDebug() { + _log.WriteLine( UnitOfWork.GetChangeTrackerDebugView() ); + } + + #endregion + + #region FindById + + /// + /// 测试通过标识查找实体 - 跟踪实体 + /// + [Fact] + public async Task TestFindById_Tracking_1() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 跟踪实体 - 字符串标识 + /// + [Fact] + public async Task TestFindById_Tracking_2() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id.ToString() ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 不跟踪实体 + /// + [Fact] + public async Task TestFindById_NoTracking() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.NoTracking().FindById( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region FindByIdAsync + + /// + /// 测试通过标识查找实体 - 跟踪 - 异步 + /// + [Fact] + public async Task TestFindByIdAsync_Tracking_1() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 跟踪 - 异步 - 字符串标识 + /// + [Fact] + public async Task TestFindByIdAsync_Tracking_2() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id.ToString() ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 不跟踪 - 异步 + /// + [Fact] + public async Task TestFindByIdAsync_NoTracking() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.NoTracking().FindByIdAsync( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region FindByIds + + /// + /// 测试通过标识列表查找实体列表 - 传入逗号分隔的标识列表 + /// + [Fact] + public async Task TestFindByIds_1() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = _repository.FindByIds( products[0].Id, products[1].Id, products[2].Id ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入集合 + /// + [Fact] + public async Task TestFindByIds_2() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = _repository.FindByIds( products.Select( t => t.Id ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入标识列表字符串 + /// + [Fact] + public async Task TestFindByIds_3() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = _repository.FindByIds( products.Select( t => t.Id ).Join() ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + #endregion + + #region FindByIdsAsync + + /// + /// 测试通过标识列表查找实体列表 - 传入逗号分隔的标识列表 - 异步 + /// + [Fact] + public async Task TestFindByIdsAsync_1() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = ( await _repository.FindByIdsAsync( products[0].Id, products[1].Id, products[2].Id ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入集合 - 异步 + /// + [Fact] + public async Task TestFindByIdsAsync_2() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = ( await _repository.FindByIdsAsync( products.Select( t => t.Id ) ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入标识列表字符串 - 异步 + /// + [Fact] + public async Task TestFindByIdsAsync_3() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = ( await _repository.FindByIdsAsync( products.Select( t => t.Id ).Join() ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + #endregion + + #region Single + + /// + /// 测试查找单个实体 + /// + [Fact] + public async Task TestSingle_1() { + //常量 + var name = "TestSingle"; + + //添加实体 + var entity = await AddProductAsync( name ); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.Single( t => t.Name == name ); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region SingleAsync + + /// + /// 测试查找单个实体 - 异步 + /// + [Fact] + public async Task TestSingleAsync() { + //常量 + var name = "TestSingleAsync"; + + //添加实体 + var entity = await AddProductAsync( name ); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.SingleAsync( t => t.Name == name ); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region Exists + + /// + /// 测试判断是否存在 + /// + [Fact] + public async Task TestExists() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + Assert.True( _repository.Exists( t => t.Id == entity.Id ) ); + } + + #endregion + + #region ExistsAsync + + /// + /// 测试判断是否存在 - 异步 + /// + [Fact] + public async Task TestExistsAsync() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + Assert.True( await _repository.ExistsAsync( t => t.Id == entity.Id ) ); + } + + #endregion + + #region Count + + /// + /// 测试查找数量 + /// + [Fact] + public async Task TestCount() { + //常量 + var name = "TestCount"; + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ); + products.ForEach( product => { + product.Init(); + product.Name = name; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = _repository.Count( t => t.Name == name ); + Assert.Equal( count, result ); + } + + #endregion + + #region CountAsync + + /// + /// 测试查找数量 - 异步 + /// + [Fact] + public async Task TestCountAsync() { + //常量 + var name = "TestCountAsync"; + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ); + products.ForEach( product => { + product.Init(); + product.Name = name; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = await _repository.CountAsync( t => t.Name == name ); + Assert.Equal( count, result ); + } + + #endregion + + #region Update + + /// + /// 测试更新方法1 - 查找并修改实体,直接提交工作单元 + /// + [Fact] + public async Task TestUpdate_1() { + //常量 + var code = "TestUpdate_1_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //查找实体并修改 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + + //直接提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试更新并发 - 使用更新方法1 + /// 更新方法1的缺陷: 通过赋值的方式更新查找实体的版本号,无法触发乐观锁 + /// + [Fact] + public async Task TestUpdate_1_Concurrency() { + //常量 + var code = "TestUpdate_1_Concurrency_Code"; + var description = "TestUpdate_1_Concurrency_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,查找实体,并进行赋值 + var result = _repository.FindById( entity.Id ); + result.Code = product.Code; + result.Description = product.Description; + result.Version = product.Version; + //提交工作单元,由于版本号变化,预期应触发乐观锁,但实际未触发 + await UnitOfWork.CommitAsync(); + + //结果:用户1覆盖了用户2设置的Description属性 + Assert.Equal( code, result.Code ); + Assert.NotEqual( description, result.Description ); + } + + /// + /// 测试更新方法2 - 创建未跟踪实体,调用dbcontext.Update方法 + /// + [Fact] + public async Task TestUpdate_2() { + //常量 + var code = "TestUpdate_2_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + //调用dbcontext.Update方法更新实体 + _repository.UpdateProduct( product ); + //提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试更新方法2的陷阱 + /// 如果在创建未跟踪实体并调用dbcontext.Update方法之前,从dbcontext中已查找出该实体,将发生异常 + /// + [Fact] + public async Task TestUpdate_2_InvalidOperation() { + //常量 + var code = "TestUpdate_2_InvalidOperation_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //模拟更新之前无意中已查找出该实体 + await _repository.FindByIdAsync( entity.Id ); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + + //由于实体已被跟踪,调用dbcontext.Update方法更新实体将触发异常 + Assert.Throws( () => { + _repository.UpdateProduct( product ); + } ); + } + + /// + /// 测试更新并发 - 使用更新方法2 + /// + [Fact] + public async Task TestUpdate_2_Concurrency() { + //常量 + var code = "TestUpdate_2_Concurrency_Code"; + var description = "TestUpdate_2_Concurrency_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,创建一个新的实体,并赋值 + var result = product.MapTo(); + + //调用dbcontext.Update方法更新实体 + _repository.UpdateProduct( result ); + + //提交工作单元,由于版本号变化,触发乐观锁 + await Assert.ThrowsAsync( async () => { + await UnitOfWork.CommitAsync(); + } ); + } + + /// + /// 测试Util Update更新方法 - 查找并修改实体,提交工作单元 + /// + [Fact] + public async Task TestUpdate_3_1() { + //常量 + var code = "TestUpdate_3_1_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //查找实体并修改 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + + //调用Update方法 + await _repository.UpdateAsync( product ); + + //提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试Util Update更新方法 - 创建未跟踪实体,调用Update方法 + /// + [Fact] + public async Task TestUpdate_3_2() { + //常量 + var code = "TestUpdate_3_2_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + + //调用Update方法更新实体 + await _repository.UpdateAsync( product ); + + //提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试Util Update更新方法 - 允许在创建未跟踪实体并调用Update方法之前查找实体,不会发生异常 + /// + [Fact] + public async Task TestUpdate_3_3() { + //常量 + var code = "TestUpdate_3_3_InvalidOperation_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //模拟更新之前无意中已查找出该实体 + await _repository.FindByIdAsync( entity.Id ); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + + //调用Update方法更新实体 + await _repository.UpdateAsync( product ); + + //提交工作单元 + _log.WriteLine( UnitOfWork.GetChangeTrackerDebugView() ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试更新并发 - 使用Util Update更新方法 + /// 通过赋值的方式更新查找实体的版本号,触发乐观锁 + /// + [Fact] + public async Task TestUpdate_3_Concurrency_1() { + //常量 + var code = "TestUpdate_3_Concurrency_1_Code"; + var description = "TestUpdate_3_Concurrency_1_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,查找实体,并进行赋值 + var result = await _repository.FindByIdAsync( entity.Id ); + result.Code = product.Code; + result.Description = product.Description; + result.Version = product.Version; + + //调用Update方法更新实体时触发乐观锁 + await Assert.ThrowsAsync( async () => { + await _repository.UpdateAsync( result ); + } ); + } + + /// + /// 测试更新并发 - 使用Util Update更新方法 + /// 更新未跟踪实体触发乐观锁 + /// + [Fact] + public async Task TestUpdate_3_Concurrency_2() { + //常量 + var code = "TestUpdate_3_Concurrency_2_Code"; + var description = "TestUpdate_3_Concurrency_2_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,创建一个新的实体,并赋值 + var result = product.MapTo(); + + //调用Update方法更新实体 + await _repository.UpdateAsync( result ); + + //提交工作单元,由于版本号变化,触发乐观锁 + await Assert.ThrowsAsync( async () => { + await UnitOfWork.CommitAsync(); + } ); + } + + /// + /// 测试更新集合 + /// + [Fact] + public async Task TestUpdate_4() { + //常量 + var count = 3; + var code = "TestUpdate_4_Code"; + + //添加实体列表 + var entities = ProductFakeService.GetProducts( count ); + entities.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( entities ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查找实体,赋值,调用Update方法,提交工作单元 + var products = await _repository.FindByIdsAsync( entities.Select( t => t.Id ) ); + products.ForEach( product => product.Code = code ); + await _repository.UpdateAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdsAsync( entities.Select( t => t.Id ) ); + Assert.Equal( code, result[0].Code ); + Assert.Equal( code, result[1].Code ); + Assert.Equal( code, result[2].Code ); + } + + #endregion + + #region Remove + + /// + /// 测试通过标识移除实体 - 逻辑删除 + /// + [Fact] + public async Task TestRemoveAsync_Id_1() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试通过标识移除实体 - 逻辑删除 - 使用工作单元禁用逻辑删除过滤器 + /// + [Fact] + public async Task TestRemoveAsync_Id_2() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //使用工作单元禁用逻辑删除过滤器,可以查出 + using( UnitOfWork.DisableDeleteFilter() ) { + var product = await _repository.FindByIdAsync( entity.Id ); + Assert.True( product.IsDeleted ); + Assert.Equal( entity.Code, product.Code ); + } + UnitOfWork.ClearCache(); + + //逻辑删除过滤器已启用,无法查出 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试通过标识移除实体 - 逻辑删除 - 使用仓储禁用逻辑删除过滤器 + /// + [Fact] + public async Task TestRemoveAsync_Id_3() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //使用仓储禁用逻辑删除过滤器,可以查出 + using( _repository.DisableDeleteFilter() ) { + var product = await _repository.FindByIdAsync( entity.Id ); + Assert.True( product.IsDeleted ); + Assert.Equal( entity.Code, product.Code ); + } + UnitOfWork.ClearCache(); + + //逻辑删除过滤器已启用,无法查出 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试移除实体 - 逻辑删除 + /// + [Fact] + public async Task TestRemoveAsync_Entity() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试移除实体集合 - 逻辑删除 - 通过标识列表 + /// + [Fact] + public async Task TestRemoveAsync_Entities_1() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //移除实体集合 + var ids = products.Select( t => t.Id ).ToList(); + await _repository.RemoveAsync( ids ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + using( _repository.DisableDeleteFilter() ) { + var result = await _repository.FindByIdsAsync( ids ); + Assert.Equal( count, result.Count ); + result.ForEach( t => Assert.True( t.IsDeleted ) ); + } + } + + /// + /// 测试移除实体集合 - 逻辑删除 - 通过实体列表 + /// + [Fact] + public async Task TestRemoveAsync_Entities_2() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //移除实体集合 + await _repository.RemoveAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + using( _repository.DisableDeleteFilter() ) { + var result = await _repository.FindByIdsAsync( products.Select( t => t.Id ) ); + Assert.Equal( count, result.Count ); + result.ForEach( t => Assert.True( t.IsDeleted ) ); + } + } + + #endregion + + #region Audit + + /// + /// 测试设置创建审计属性 + /// + [Fact] + public async Task TestCreationAudit() { + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _repository.AddAsync( entity ); + Assert.NotEqual( TestSession.TestUserId, entity.CreatorId ); + await UnitOfWork.CommitAsync(); + Assert.Equal( TestSession.TestUserId, entity.CreatorId ); + Assert.Equal( TestSession.TestUserId, entity.LastModifierId ); + } + + /// + /// 测试设置修改审计属性 + /// + [Fact] + public async Task TestModificationAudit() { + //工作单元 + var unitOfWork = (PgSqlUnitOfWork)UnitOfWork; + + //常量 + var code = "TestModificationAudit"; + + //添加实体 + var entity = await AddProductAsync(); + unitOfWork.ClearCache(); + + //修改 + var product = entity.MapTo(); + product.Code = code; + await _repository.UpdateAsync( product ); + unitOfWork.SetSession( new TestSession { UserId = TestSession.TestUserId2.ToString() } ); + + //验证修改前审计属性 + Assert.Equal( TestSession.TestUserId, product.CreatorId ); + Assert.Equal( TestSession.TestUserId, product.LastModifierId ); + + //提交 + await unitOfWork.CommitAsync(); + + //验证修改后审计属性 + Assert.Equal( TestSession.TestUserId, product.CreatorId ); + Assert.Equal( TestSession.TestUserId2, product.LastModifierId ); + } + + #endregion + + #region ExtraProperties + + /// + /// 测试扩展属性 - 添加简单扩展属性 + /// + [Fact] + public async Task TestExtraProperties_1() { + //常量 + var value = "TestExtraProperties_1"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = value; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( value, result.TestProperty1 ); + } + + /// + /// 测试扩展属性 - 修改简单扩展属性 + /// + [Fact] + public async Task TestExtraProperties_2() { + //常量 + var oldValue = "TestExtraProperties_2_1"; + var newValue = "TestExtraProperties_2_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = oldValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( oldValue, oldEntity.TestProperty1 ); + + //修改实体 + oldEntity.TestProperty1 = newValue; + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newValue, result.TestProperty1 ); + } + + /// + /// 测试扩展属性 - 添加对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_3() { + //常量 + var code = 2; + var name = "TestExtraProperties_3"; + var enumValue = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = name; + entity.TestProperty2.EnumValue = enumValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id ); + Assert.Equal( code, result.TestProperty2.Code ); + Assert.Equal( name, result.TestProperty2.Name ); + Assert.Equal( enumValue, result.TestProperty2.EnumValue ); + } + + /// + /// 测试扩展属性 - 修改对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_4() { + //常量 + var code = 2; + var oldName = "TestExtraProperties_4_1"; + var newName = "TestExtraProperties_4_2"; + var oldEnumValue = ProductEnum.Hello; + var newEnumValue = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = oldName; + entity.TestProperty2.EnumValue = oldEnumValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, oldEntity.TestProperty2.Code ); + Assert.Equal( oldName, oldEntity.TestProperty2.Name ); + Assert.Equal( oldEnumValue, oldEntity.TestProperty2.EnumValue ); + + //修改实体 + oldEntity.TestProperty2.Name = newName; + oldEntity.TestProperty2.EnumValue = newEnumValue; + WriteTrackerDebug(); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newName, result.TestProperty2.Name ); + Assert.Equal( newEnumValue, result.TestProperty2.EnumValue ); + } + + /// + /// 测试扩展属性 - 添加对象集合扩展属性 + /// + [Fact] + public async Task TestExtraProperties_5() { + //常量 + var code = 1; + var name1 = "TestExtraProperties_5_1"; + var name2 = "TestExtraProperties_5_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperties.Add( new ProductItem { Code = code, Name = name1 } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = name2 } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( 2, result.TestProperties.Count ); + Assert.Equal( code, result.TestProperties[0].Code ); + Assert.Equal( name1, result.TestProperties[0].Name ); + Assert.Equal( code, result.TestProperties[1].Code ); + Assert.Equal( name2, result.TestProperties[1].Name ); + } + + /// + /// 测试扩展属性 - 修改对象集合扩展属性 + /// + [Fact] + public async Task TestExtraProperties_6() { + //常量 + var code = 1; + var oldName = "TestExtraProperties_6_1"; + var newName = "TestExtraProperties_6_2"; + var name3 = "TestExtraProperties_6_3"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( 2, oldEntity.TestProperties.Count ); + + //修改实体 + oldEntity.TestProperties[0].Name = newName; + oldEntity.TestProperties[1].Name = newName; + oldEntity.TestProperties.Add( new ProductItem { Code = code, Name = name3 } ); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( 3, result.TestProperties.Count ); + Assert.Equal( code, result.TestProperties[0].Code ); + Assert.Equal( newName, result.TestProperties[0].Name ); + Assert.Equal( code, result.TestProperties[1].Code ); + Assert.Equal( newName, result.TestProperties[1].Name ); + Assert.Equal( code, result.TestProperties[2].Code ); + Assert.Equal( name3, result.TestProperties[2].Name ); + } + + /// + /// 测试扩展属性 - 添加多个扩展属性 + /// + [Fact] + public async Task TestExtraProperties_7() { + //常量 + var code = 2; + var name = "TestExtraProperties_7"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = name; + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = name; + entity.TestProperties.Add( new ProductItem { Code = code, Name = name } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = name } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( name, result.TestProperty1 ); + Assert.Equal( code, result.TestProperty2.Code ); + Assert.Equal( name, result.TestProperty2.Name ); + Assert.Equal( code, result.TestProperties[0].Code ); + Assert.Equal( name, result.TestProperties[0].Name ); + Assert.Equal( code, result.TestProperties[1].Code ); + Assert.Equal( name, result.TestProperties[1].Name ); + } + + /// + /// 测试扩展属性 - 修改多个扩展属性 + /// + [Fact] + public async Task TestExtraProperties_8() { + //常量 + var code = 2; + var oldName = "TestExtraProperties_8_1"; + var newName = "TestExtraProperties_8_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = oldName; + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = oldName; + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( oldName, oldEntity.TestProperty1 ); + Assert.Equal( code, oldEntity.TestProperty2.Code ); + Assert.Equal( oldName, oldEntity.TestProperty2.Name ); + Assert.Equal( 2, oldEntity.TestProperties.Count ); + + //修改实体 + oldEntity.TestProperty1 = newName; + oldEntity.TestProperty2.Name = newName; + oldEntity.TestProperties[0].Name = newName; + oldEntity.TestProperties[1].Name = newName; + oldEntity.TestProperties.Add( new ProductItem{ Code = code, Name = newName } ); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newName, result.TestProperty1 ); + Assert.Equal( newName, result.TestProperty2.Name ); + Assert.Equal( 3, result.TestProperties.Count ); + Assert.Equal( newName, result.TestProperties[0].Name ); + Assert.Equal( newName, result.TestProperties[1].Name ); + Assert.Equal( newName, result.TestProperties[2].Name ); + } + + /// + /// 测试扩展属性 - 添加枚举扩展属性 + /// + [Fact] + public async Task TestExtraProperties_9() { + //常量 + ProductEnum value = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty3 = value; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( value, result.TestProperty3 ); + } + + /// + /// 测试扩展属性 - 修改枚举扩展属性 + /// + [Fact] + public async Task TestExtraProperties_10() { + //常量 + ProductEnum oldValue = ProductEnum.Hello; + ProductEnum newValue = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty3 = oldValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( oldValue, oldEntity.TestProperty3 ); + + //修改实体 + oldEntity.TestProperty3 = newValue; + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newValue, result.TestProperty3 ); + } + + /// + /// 测试扩展属性 - 添加不可变对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_11() { + //常量 + var code = 2; + var name = "TestExtraProperties_11"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty4 = new ProductItem2( code, name ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.TestProperty4.Code ); + Assert.Equal( name, result.TestProperty4.Name ); + } + + /// + /// 测试扩展属性 - 修改对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_12() { + //常量 + var code = 2; + var oldName = "TestExtraProperties_12_1"; + var newName = "TestExtraProperties_12_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty4 = new ProductItem2( code, oldName ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, oldEntity.TestProperty4.Code ); + Assert.Equal( oldName, oldEntity.TestProperty4.Name ); + + //修改实体 + oldEntity.TestProperty4 = new ProductItem2( code, newName ); + WriteTrackerDebug(); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newName, result.TestProperty4.Name ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/TagRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/TagRepositoryTest.cs new file mode 100644 index 000000000..cec65b128 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Tests/TagRepositoryTest.cs @@ -0,0 +1,41 @@ +using System.Threading.Tasks; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 标签仓储测试 + /// + public class TagRepositoryTest : TestBase { + /// + /// 仓储 + /// + private readonly ITagRepository _repository; + + /// + /// 测试初始化 + /// + public TagRepositoryTest( ITagRepository repository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = repository; + } + + /// + /// 测试添加实体 + /// + [Fact] + public async Task TestAddAsync() { + //添加实体 + var entity = TagFakeService.GetTag(); + entity.Init(); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + Assert.True( await _repository.ExistsAsync( t => t.Id == entity.Id ) ); + } + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration.csproj b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration.csproj new file mode 100644 index 000000000..883f75ffe --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration.csproj @@ -0,0 +1,39 @@ + + + + $(NetTargetFramework) + false + Util.Data.EntityFrameworkCore + Util.Data.EntityFrameworkCore.Startup + + + + + Always + + + Always + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/appsettings.Development.json b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..3ccc37072 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Trace" + } + }, + "ConnectionStrings": { + "connection": "server=127.0.0.1:15432;User Id=admin;password=admin;database=Util.Data.EntityFrameworkCore.Test" + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/appsettings.json b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/appsettings.json new file mode 100644 index 000000000..5c60ed8d5 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.PostgreSql.Tests.Integration/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + }, + "ConnectionStrings": { + "connection": "server=postgresql.common;User Id=admin;password=admin;database=Util.Data.EntityFrameworkCore.Test" + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Filters/DeleteFilterTest.cs b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Filters/DeleteFilterTest.cs new file mode 100644 index 000000000..aa824e841 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Filters/DeleteFilterTest.cs @@ -0,0 +1,70 @@ +using Util.Tests.Models; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Data.EntityFrameworkCore.Filters; + +/// +/// 逻辑删除过滤器测试 +/// +public class DeleteFilterTest { + /// + /// 测试消息输出 + /// + private readonly ITestOutputHelper _testOutputHelper; + /// + /// 逻辑删除过滤器 + /// + private readonly DeleteFilter _filter; + + /// + /// 测试初始化 + /// + public DeleteFilterTest( ITestOutputHelper testOutputHelper ) { + _testOutputHelper = testOutputHelper; + _filter = new DeleteFilter(); + } + + /// + /// 测试是否启用 + /// + [Fact] + public void TestIsEnabled() { + //默认值为启用 + Assert.True( _filter.IsEnabled ); + + //禁用过滤器 + _filter.Disable(); + Assert.False( _filter.IsEnabled ); + + //启用过滤器 + _filter.Enable(); + Assert.True( _filter.IsEnabled ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisable() { + //默认值为启用 + Assert.True( _filter.IsEnabled ); + + //禁用过滤器 + using ( _filter.Disable() ) { + Assert.False( _filter.IsEnabled ); + } + + //恢复启用状态 + Assert.True( _filter.IsEnabled ); + } + + /// + /// 测试实体是否启用过滤器 + /// + [Fact] + public void TestIsEntityEnabled() { + Assert.False( _filter.IsEntityEnabled() ); + Assert.True( _filter.IsEntityEnabled() ); + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Filters/FilterManagerTest.cs b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Filters/FilterManagerTest.cs new file mode 100644 index 000000000..a36ccdb56 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Filters/FilterManagerTest.cs @@ -0,0 +1,145 @@ +using Util.Data.EntityFrameworkCore.Samples; +using Util.Data.Filters; +using Util.Domain; +using Util.Tests.Models; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Filters; + +/// +/// 数据过滤器管理器测试 +/// +public class FilterManagerTest { + /// + /// 数据过滤器管理器 + /// + private readonly IFilterManager _filterManager; + + /// + /// 测试初始化 + /// + public FilterManagerTest( IFilterManager filterManager ) { + _filterManager = filterManager; + } + + /// + /// 测试获取过滤器 - 泛型 + /// + [Fact] + public void TestGetFilter_1() { + //逻辑删除过滤器已默认加载 + var filter = _filterManager.GetFilter(); + Assert.NotNull( filter ); + Assert.True( filter.IsEnabled ); + } + + /// + /// 测试获取过滤器 - 非泛型 + /// + [Fact] + public void TestGetFilter_2() { + //逻辑删除过滤器已默认加载 + var filter = _filterManager.GetFilter(typeof( IDelete ) ); + Assert.NotNull( filter ); + } + + /// + /// 测试禁用过滤器 + /// + [Fact] + public void TestDisableFilter_1() { + _filterManager.DisableFilter(); + var filter = _filterManager.GetFilter(); + Assert.False( filter.IsEnabled ); + } + + /// + /// 测试禁用过滤器 - 使用using + /// + [Fact] + public void TestDisableFilter_2() { + var filter = _filterManager.GetFilter(); + using ( _filterManager.DisableFilter() ) { + Assert.False( filter.IsEnabled ); + } + Assert.True( filter.IsEnabled ); + } + + /// + /// 测试启用过滤器 + /// + [Fact] + public void TestEnableFilter() { + var filter = _filterManager.GetFilter(); + + //禁用 + _filterManager.DisableFilter(); + Assert.False( filter.IsEnabled ); + + //启用 + _filterManager.EnableFilter(); + Assert.True( filter.IsEnabled ); + } + + /// + /// 测试实体是否启用过滤器 + /// + [Fact] + public void TestIsEntityEnabled() { + Assert.False( _filterManager.IsEntityEnabled() ); + Assert.True( _filterManager.IsEntityEnabled() ); + } + + /// + /// 测试过滤器是否启用 + /// + [Fact] + public void TestIsEnabled() { + Assert.True( _filterManager.IsEnabled() ); + Assert.False( _filterManager.IsEnabled() ); + } + + /// + /// 测试获取过滤表达式 - 1个过滤器 + /// + [Fact] + public void TestGetExpression_1() { + FilterManager.AddFilterType(); + FilterManager.RemoveFilterType(); + var expression = _filterManager.GetExpression( null ); + Assert.Equal( "t => False", expression?.ToString() ); + } + + /// + /// 测试获取过滤表达式 - 2个过滤器 + /// + [Fact] + public void TestGetExpression_2() { + FilterManager.AddFilterType(); + FilterManager.AddFilterType(); + var expression = _filterManager.GetExpression( null ); + Assert.Equal( "t => (False AndAlso True)", expression?.ToString() ); + } + + /// + /// 测试获取过滤表达式 - 2个过滤器 - 只启用一个过滤器 + /// + [Fact] + public void TestGetExpression_3() { + FilterManager.AddFilterType(); + FilterManager.AddFilterType(); + var expression = _filterManager.GetExpression( null ); + Assert.Equal( "t => True", expression?.ToString() ); + } + + /// + /// 测试获取过滤表达式 - 未启用过滤器 + /// + [Fact] + public void TestGetExpression_4() { + FilterManager.AddFilterType(); + FilterManager.AddFilterType(); + var expression = _filterManager.GetExpression( null ); + Assert.Null( expression ); + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Samples/Migrations/20230302102051_Init.cs b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Samples/Migrations/20230302102051_Init.cs new file mode 100644 index 000000000..43b4231cc --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Samples/Migrations/20230302102051_Init.cs @@ -0,0 +1,407 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace Systems.Data.SqlServer.Migrations +{ + /// + public partial class Init : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.EnsureSchema( + name: "Permissions"); + + migrationBuilder.CreateTable( + name: "Application", + schema: "Permissions", + columns: table => new + { + ApplicationId = table.Column(type: "uniqueidentifier", nullable: false, comment: "应用程序标识"), + Code = table.Column(type: "nvarchar(60)", maxLength: 60, nullable: false, comment: "应用程序编码"), + Name = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false, comment: "应用程序名称"), + IsApi = table.Column(type: "bit", nullable: false, comment: "是否Api"), + Enabled = table.Column(type: "bit", nullable: false, comment: "启用"), + Remark = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true, comment: "备注"), + CreationTime = table.Column(type: "datetime2", nullable: true, comment: "创建时间"), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true, comment: "创建人标识"), + LastModificationTime = table.Column(type: "datetime2", nullable: true, comment: "最后修改时间"), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true, comment: "最后修改人标识"), + IsDeleted = table.Column(type: "bit", nullable: false, comment: "是否删除"), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true, comment: "扩展属性"), + Version = table.Column(type: "rowversion", rowVersion: true, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Application", x => x.ApplicationId) + .Annotation("SqlServer:Clustered", false); + }, + comment: "应用程序"); + + migrationBuilder.CreateTable( + name: "Claim", + schema: "Permissions", + columns: table => new + { + ClaimId = table.Column(type: "uniqueidentifier", nullable: false, comment: "声明标识"), + Name = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false, comment: "声明名称"), + Enabled = table.Column(type: "bit", nullable: false, comment: "启用"), + SortId = table.Column(type: "int", nullable: true, comment: "排序号"), + Remark = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true, comment: "备注"), + CreationTime = table.Column(type: "datetime2", nullable: true, comment: "创建时间"), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true, comment: "创建人标识"), + LastModificationTime = table.Column(type: "datetime2", nullable: true, comment: "最后修改时间"), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true, comment: "最后修改人标识"), + IsDeleted = table.Column(type: "bit", nullable: false, comment: "是否删除"), + Version = table.Column(type: "rowversion", rowVersion: true, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Claim", x => x.ClaimId) + .Annotation("SqlServer:Clustered", false); + }, + comment: "声明"); + + migrationBuilder.CreateTable( + name: "Permission", + schema: "Permissions", + columns: table => new + { + PermissionId = table.Column(type: "uniqueidentifier", nullable: false, comment: "权限标识"), + RoleId = table.Column(type: "uniqueidentifier", nullable: false, comment: "角色标识"), + ResourceId = table.Column(type: "uniqueidentifier", nullable: false, comment: "资源标识"), + IsDeny = table.Column(type: "bit", nullable: false, comment: "拒绝"), + IsTemporary = table.Column(type: "bit", nullable: false, comment: "临时"), + ExpirationTime = table.Column(type: "datetime2", nullable: true, comment: "到期时间"), + CreationTime = table.Column(type: "datetime2", nullable: true, comment: "创建时间"), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true, comment: "创建人标识"), + LastModificationTime = table.Column(type: "datetime2", nullable: true, comment: "最后修改时间"), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true, comment: "最后修改人标识"), + IsDeleted = table.Column(type: "bit", nullable: false, comment: "是否删除"), + Version = table.Column(type: "rowversion", rowVersion: true, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Permission", x => x.PermissionId) + .Annotation("SqlServer:Clustered", false); + }, + comment: "权限"); + + migrationBuilder.CreateTable( + name: "Role", + schema: "Permissions", + columns: table => new + { + RoleId = table.Column(type: "uniqueidentifier", nullable: false, comment: "角色标识"), + Code = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false, comment: "角色编码"), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false, comment: "角色名称"), + NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false, comment: "标准化角色名称"), + Type = table.Column(type: "nvarchar(80)", maxLength: 80, nullable: false, comment: "角色类型"), + IsAdmin = table.Column(type: "bit", nullable: false, comment: "管理员"), + Remark = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true, comment: "备注"), + PinYin = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true, comment: "拼音简码"), + CreationTime = table.Column(type: "datetime2", nullable: true, comment: "创建时间"), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true, comment: "创建人标识"), + LastModificationTime = table.Column(type: "datetime2", nullable: true, comment: "最后修改时间"), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true, comment: "最后修改人标识"), + IsDeleted = table.Column(type: "bit", nullable: false, comment: "是否删除"), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true, comment: "扩展属性"), + Version = table.Column(type: "rowversion", rowVersion: true, nullable: true), + ParentId = table.Column(type: "uniqueidentifier", nullable: true, comment: "父标识"), + Path = table.Column(type: "nvarchar(max)", nullable: false, comment: "路径"), + Level = table.Column(type: "int", nullable: false, comment: "层级"), + Enabled = table.Column(type: "bit", nullable: false, comment: "启用"), + SortId = table.Column(type: "int", nullable: true, comment: "排序号") + }, + constraints: table => + { + table.PrimaryKey("PK_Role", x => x.RoleId) + .Annotation("SqlServer:Clustered", false); + }, + comment: "角色"); + + migrationBuilder.CreateTable( + name: "User", + schema: "Permissions", + columns: table => new + { + UserId = table.Column(type: "uniqueidentifier", nullable: false, comment: "用户标识"), + UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true, comment: "用户名"), + NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true, comment: "标准化用户名"), + Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true, comment: "安全邮箱"), + NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true, comment: "标准化邮箱"), + EmailConfirmed = table.Column(type: "bit", nullable: false, comment: "邮箱是否已确认"), + PhoneNumber = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true, comment: "安全手机号"), + PhoneNumberConfirmed = table.Column(type: "bit", nullable: false, comment: "手机号是否已确认"), + PasswordHash = table.Column(type: "nvarchar(1024)", maxLength: 1024, nullable: true, comment: "密码散列"), + TwoFactorEnabled = table.Column(type: "bit", nullable: false, comment: "是否启用双因素认证"), + Enabled = table.Column(type: "bit", nullable: true, comment: "是否启用"), + DisabledTime = table.Column(type: "datetime2", nullable: true, comment: "冻结时间"), + LockoutEnabled = table.Column(type: "bit", nullable: false, comment: "是否启用锁定"), + LockoutEnd = table.Column(type: "datetime2", nullable: true, comment: "锁定截止"), + AccessFailedCount = table.Column(type: "int", nullable: true, comment: "登录失败次数"), + LoginCount = table.Column(type: "int", nullable: true, comment: "登录次数"), + RegisterIp = table.Column(type: "nvarchar(30)", maxLength: 30, nullable: true, comment: "注册Ip"), + LastLoginTime = table.Column(type: "datetime2", nullable: true, comment: "上次登陆时间"), + LastLoginIp = table.Column(type: "nvarchar(30)", maxLength: 30, nullable: true, comment: "上次登陆Ip"), + CurrentLoginTime = table.Column(type: "datetime2", nullable: true, comment: "本次登陆时间"), + CurrentLoginIp = table.Column(type: "nvarchar(30)", maxLength: 30, nullable: true, comment: "本次登陆Ip"), + SecurityStamp = table.Column(type: "nvarchar(1024)", maxLength: 1024, nullable: true, comment: "安全戳"), + Remark = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true, comment: "备注"), + CreationTime = table.Column(type: "datetime2", nullable: true, comment: "创建时间"), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true, comment: "创建人标识"), + LastModificationTime = table.Column(type: "datetime2", nullable: true, comment: "最后修改时间"), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true, comment: "最后修改人标识"), + IsDeleted = table.Column(type: "bit", nullable: false, comment: "是否删除"), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true, comment: "扩展属性"), + Version = table.Column(type: "rowversion", rowVersion: true, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_User", x => x.UserId) + .Annotation("SqlServer:Clustered", false); + }, + comment: "用户"); + + migrationBuilder.CreateTable( + name: "Resource", + schema: "Permissions", + columns: table => new + { + ResourceId = table.Column(type: "uniqueidentifier", nullable: false, comment: "资源标识"), + ApplicationId = table.Column(type: "uniqueidentifier", nullable: true, comment: "应用程序标识"), + Uri = table.Column(type: "nvarchar(300)", maxLength: 300, nullable: true, comment: "资源标识符"), + Name = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false, comment: "资源名称"), + Type = table.Column(type: "int", nullable: false, comment: "资源类型"), + Remark = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true, comment: "备注"), + PinYin = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true, comment: "拼音简码"), + CreationTime = table.Column(type: "datetime2", nullable: true, comment: "创建时间"), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true, comment: "创建人标识"), + LastModificationTime = table.Column(type: "datetime2", nullable: true, comment: "最后修改时间"), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true, comment: "最后修改人标识"), + IsDeleted = table.Column(type: "bit", nullable: false, comment: "是否删除"), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: true, comment: "扩展属性"), + Version = table.Column(type: "rowversion", rowVersion: true, nullable: true), + ParentId = table.Column(type: "uniqueidentifier", nullable: true, comment: "父标识"), + Path = table.Column(type: "nvarchar(max)", nullable: false, comment: "路径"), + Level = table.Column(type: "int", nullable: false, comment: "层级"), + Enabled = table.Column(type: "bit", nullable: false, comment: "启用"), + SortId = table.Column(type: "int", nullable: true, comment: "排序号") + }, + constraints: table => + { + table.PrimaryKey("PK_Resource", x => x.ResourceId) + .Annotation("SqlServer:Clustered", false); + table.ForeignKey( + name: "FK_Resource_Application_ApplicationId", + column: x => x.ApplicationId, + principalSchema: "Permissions", + principalTable: "Application", + principalColumn: "ApplicationId"); + table.ForeignKey( + name: "FK_Resource_Resource_ParentId", + column: x => x.ParentId, + principalSchema: "Permissions", + principalTable: "Resource", + principalColumn: "ResourceId"); + }, + comment: "资源"); + + migrationBuilder.CreateTable( + name: "UserRole", + schema: "Permissions", + columns: table => new + { + UserId = table.Column(type: "uniqueidentifier", nullable: false, comment: "用户标识"), + RoleId = table.Column(type: "uniqueidentifier", nullable: false, comment: "角色标识") + }, + constraints: table => + { + table.PrimaryKey("PK_UserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_UserRole_Role_RoleId", + column: x => x.RoleId, + principalSchema: "Permissions", + principalTable: "Role", + principalColumn: "RoleId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_UserRole_User_UserId", + column: x => x.UserId, + principalSchema: "Permissions", + principalTable: "User", + principalColumn: "UserId", + onDelete: ReferentialAction.Cascade); + }, + comment: "用户角色"); + + migrationBuilder.InsertData( + schema: "Permissions", + table: "Application", + columns: new[] { "ApplicationId", "Code", "CreationTime", "CreatorId", "Enabled", "ExtraProperties", "IsApi", "IsDeleted", "LastModificationTime", "LastModifierId", "Name", "Remark" }, + values: new object[,] + { + { new Guid("0f5fb254-47a1-4e2f-b79a-c7ad1d226ab9"), "system-api", new DateTime(2023, 3, 2, 10, 20, 51, 332, DateTimeKind.Utc).AddTicks(3430), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), true, "{}", true, false, new DateTime(2023, 3, 2, 10, 20, 51, 332, DateTimeKind.Utc).AddTicks(3431), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), "系统Api", null }, + { new Guid("e9138a35-a4ff-460e-ac55-b743d55b9691"), "system-admin", new DateTime(2023, 3, 2, 10, 20, 51, 332, DateTimeKind.Utc).AddTicks(3420), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), true, "{}", false, false, new DateTime(2023, 3, 2, 10, 20, 51, 332, DateTimeKind.Utc).AddTicks(3424), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), "系统后台", null } + }); + + migrationBuilder.InsertData( + schema: "Permissions", + table: "Claim", + columns: new[] { "ClaimId", "CreationTime", "CreatorId", "Enabled", "IsDeleted", "LastModificationTime", "LastModifierId", "Name", "Remark", "SortId" }, + values: new object[,] + { + { new Guid("19af94f7-80a2-5d42-d432-278a23b10492"), new DateTime(2023, 3, 2, 10, 20, 51, 333, DateTimeKind.Utc).AddTicks(6380), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), true, false, new DateTime(2023, 3, 2, 10, 20, 51, 333, DateTimeKind.Utc).AddTicks(6381), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), "application_id", "应用程序标识", 7 }, + { new Guid("1a331188-3318-a029-c8c8-71258c7041b2"), new DateTime(2023, 3, 2, 10, 20, 51, 333, DateTimeKind.Utc).AddTicks(6368), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), true, false, new DateTime(2023, 3, 2, 10, 20, 51, 333, DateTimeKind.Utc).AddTicks(6368), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), "name", "用户名", 3 }, + { new Guid("27d23b13-0cb5-20aa-c65a-81bd90c35212"), new DateTime(2023, 3, 2, 10, 20, 51, 333, DateTimeKind.Utc).AddTicks(6371), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), true, false, new DateTime(2023, 3, 2, 10, 20, 51, 333, DateTimeKind.Utc).AddTicks(6371), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), "nickname", "昵称", 4 }, + { new Guid("5b422322-b7f5-4081-e10a-fa96a85c5b86"), new DateTime(2023, 3, 2, 10, 20, 51, 333, DateTimeKind.Utc).AddTicks(6355), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), true, false, new DateTime(2023, 3, 2, 10, 20, 51, 333, DateTimeKind.Utc).AddTicks(6357), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), "sub", "用户标识", 1 }, + { new Guid("70a9173d-2216-7bf6-2cbe-f0b2d38c524d"), new DateTime(2023, 3, 2, 10, 20, 51, 333, DateTimeKind.Utc).AddTicks(6364), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), true, false, new DateTime(2023, 3, 2, 10, 20, 51, 333, DateTimeKind.Utc).AddTicks(6365), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), "profile", "用户个人信息", 2 }, + { new Guid("88a7eae0-3187-ac06-3766-8edf13d06776"), new DateTime(2023, 3, 2, 10, 20, 51, 333, DateTimeKind.Utc).AddTicks(6376), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), true, false, new DateTime(2023, 3, 2, 10, 20, 51, 333, DateTimeKind.Utc).AddTicks(6377), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), "phone_number", "手机号", 6 }, + { new Guid("c38280ce-92f9-77be-1e17-87cd58c3fff1"), new DateTime(2023, 3, 2, 10, 20, 51, 333, DateTimeKind.Utc).AddTicks(6374), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), true, false, new DateTime(2023, 3, 2, 10, 20, 51, 333, DateTimeKind.Utc).AddTicks(6374), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), "email", "电子邮件", 5 } + }); + + migrationBuilder.InsertData( + schema: "Permissions", + table: "Resource", + columns: new[] { "ResourceId", "ApplicationId", "CreationTime", "CreatorId", "Enabled", "ExtraProperties", "IsDeleted", "LastModificationTime", "LastModifierId", "Level", "Name", "ParentId", "Path", "PinYin", "Remark", "SortId", "Type", "Uri" }, + values: new object[,] + { + { new Guid("3493f51d-ac81-4f39-80ea-0acb02c9fee2"), null, new DateTime(2023, 3, 2, 10, 20, 51, 339, DateTimeKind.Utc).AddTicks(1408), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), true, "{\"Claims\":[\"sub\"]}", false, new DateTime(2023, 3, 2, 10, 20, 51, 339, DateTimeKind.Utc).AddTicks(1409), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), 1, "openid", null, "3493f51d-ac81-4f39-80ea-0acb02c9fee2,", null, "用户标识", 1, 5, "openid" }, + { new Guid("cda87744-449d-4060-8f99-88c4223d103f"), null, new DateTime(2023, 3, 2, 10, 20, 51, 339, DateTimeKind.Utc).AddTicks(1416), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), true, "{\"Claims\":[\"profile\",\"name\"]}", false, new DateTime(2023, 3, 2, 10, 20, 51, 339, DateTimeKind.Utc).AddTicks(1416), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), 1, "profile", null, "cda87744-449d-4060-8f99-88c4223d103f,", null, "用户信息", 1, 5, "profile" } + }); + + migrationBuilder.InsertData( + schema: "Permissions", + table: "Role", + columns: new[] { "RoleId", "Code", "CreationTime", "CreatorId", "Enabled", "ExtraProperties", "IsAdmin", "IsDeleted", "LastModificationTime", "LastModifierId", "Level", "Name", "NormalizedName", "ParentId", "Path", "PinYin", "Remark", "SortId", "Type" }, + values: new object[] { new Guid("d5c3cbde-f2be-47ac-bc85-a329f79588f8"), "admin", new DateTime(2023, 3, 2, 10, 20, 51, 346, DateTimeKind.Utc).AddTicks(4600), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), true, "{}", true, false, new DateTime(2023, 3, 2, 10, 20, 51, 346, DateTimeKind.Utc).AddTicks(4603), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), 1, "管理员", "管理员", null, "d5c3cbde-f2be-47ac-bc85-a329f79588f8,", "gly", "管理员", 1, "Role" }); + + migrationBuilder.InsertData( + schema: "Permissions", + table: "User", + columns: new[] { "UserId", "AccessFailedCount", "CreationTime", "CreatorId", "CurrentLoginIp", "CurrentLoginTime", "DisabledTime", "Email", "EmailConfirmed", "Enabled", "ExtraProperties", "IsDeleted", "LastLoginIp", "LastLoginTime", "LastModificationTime", "LastModifierId", "LockoutEnabled", "LockoutEnd", "LoginCount", "NormalizedEmail", "NormalizedUserName", "PasswordHash", "PhoneNumber", "PhoneNumberConfirmed", "RegisterIp", "Remark", "SecurityStamp", "TwoFactorEnabled", "UserName" }, + values: new object[] { new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), null, new DateTime(2023, 3, 2, 10, 20, 51, 348, DateTimeKind.Utc).AddTicks(3222), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), null, null, null, null, false, true, "{}", false, null, null, new DateTime(2023, 3, 2, 10, 20, 51, 348, DateTimeKind.Utc).AddTicks(3226), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), false, null, null, null, "ADMIN", "AQAAAAEAACcQAAAAEP+Co27jHqc5JQ0LPfqcMbUtsrCHkZhK/oRC/xPysrV9FTT+siiMEOELuOL+LeN7Jw==", null, false, null, "管理员", "E3LEMZVTQRBJD2GDJXDNNJ7BF3GEEUBF", false, "admin" }); + + migrationBuilder.InsertData( + schema: "Permissions", + table: "Resource", + columns: new[] { "ResourceId", "ApplicationId", "CreationTime", "CreatorId", "Enabled", "ExtraProperties", "IsDeleted", "LastModificationTime", "LastModifierId", "Level", "Name", "ParentId", "Path", "PinYin", "Remark", "SortId", "Type", "Uri" }, + values: new object[] { new Guid("ec9c35b4-3dfd-4cee-be70-9d83993b40e5"), new Guid("e9138a35-a4ff-460e-ac55-b743d55b9691"), new DateTime(2023, 3, 2, 10, 20, 51, 339, DateTimeKind.Utc).AddTicks(1322), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), true, "{\"Claims\":[]}", false, new DateTime(2023, 3, 2, 10, 20, 51, 339, DateTimeKind.Utc).AddTicks(1324), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), 1, "系统管理", null, "ec9c35b4-3dfd-4cee-be70-9d83993b40e5,", "xtgl", "系统管理", 1, 1, null }); + + migrationBuilder.InsertData( + schema: "Permissions", + table: "UserRole", + columns: new[] { "RoleId", "UserId" }, + values: new object[] { new Guid("d5c3cbde-f2be-47ac-bc85-a329f79588f8"), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896") }); + + migrationBuilder.InsertData( + schema: "Permissions", + table: "Resource", + columns: new[] { "ResourceId", "ApplicationId", "CreationTime", "CreatorId", "Enabled", "ExtraProperties", "IsDeleted", "LastModificationTime", "LastModifierId", "Level", "Name", "ParentId", "Path", "PinYin", "Remark", "SortId", "Type", "Uri" }, + values: new object[,] + { + { new Guid("1a01a8c3-1e6f-47d8-be75-b66da7f7a746"), new Guid("e9138a35-a4ff-460e-ac55-b743d55b9691"), new DateTime(2023, 3, 2, 10, 20, 51, 339, DateTimeKind.Utc).AddTicks(1385), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), true, "{\"Claims\":[]}", false, new DateTime(2023, 3, 2, 10, 20, 51, 339, DateTimeKind.Utc).AddTicks(1385), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), 2, "角色", new Guid("ec9c35b4-3dfd-4cee-be70-9d83993b40e5"), "ec9c35b4-3dfd-4cee-be70-9d83993b40e5,1a01a8c3-1e6f-47d8-be75-b66da7f7a746,", "js", "角色", 4, 1, "/permission/role" }, + { new Guid("9412c649-2353-4f37-a178-21a66a7ad3bf"), new Guid("e9138a35-a4ff-460e-ac55-b743d55b9691"), new DateTime(2023, 3, 2, 10, 20, 51, 339, DateTimeKind.Utc).AddTicks(1356), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), true, "{\"Claims\":[]}", false, new DateTime(2023, 3, 2, 10, 20, 51, 339, DateTimeKind.Utc).AddTicks(1357), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), 2, "应用程序", new Guid("ec9c35b4-3dfd-4cee-be70-9d83993b40e5"), "ec9c35b4-3dfd-4cee-be70-9d83993b40e5,9412c649-2353-4f37-a178-21a66a7ad3bf,", "yycx", "应用程序", 1, 1, "/permission/application" }, + { new Guid("a4c32fa8-f8eb-4ce9-a517-d96d431fcb04"), new Guid("e9138a35-a4ff-460e-ac55-b743d55b9691"), new DateTime(2023, 3, 2, 10, 20, 51, 339, DateTimeKind.Utc).AddTicks(1394), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), true, "{\"Claims\":[]}", false, new DateTime(2023, 3, 2, 10, 20, 51, 339, DateTimeKind.Utc).AddTicks(1394), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), 2, "用户", new Guid("ec9c35b4-3dfd-4cee-be70-9d83993b40e5"), "ec9c35b4-3dfd-4cee-be70-9d83993b40e5,a4c32fa8-f8eb-4ce9-a517-d96d431fcb04,", "yh", "用户", 5, 1, "/permission/user" }, + { new Guid("ccb74938-7e59-4532-a1ec-850ca75dd7b9"), new Guid("e9138a35-a4ff-460e-ac55-b743d55b9691"), new DateTime(2023, 3, 2, 10, 20, 51, 339, DateTimeKind.Utc).AddTicks(1364), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), true, "{\"Claims\":[]}", false, new DateTime(2023, 3, 2, 10, 20, 51, 339, DateTimeKind.Utc).AddTicks(1365), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), 2, "声明", new Guid("ec9c35b4-3dfd-4cee-be70-9d83993b40e5"), "ec9c35b4-3dfd-4cee-be70-9d83993b40e5,ccb74938-7e59-4532-a1ec-850ca75dd7b9,", "sm", "声明", 2, 1, "/permission/claim" }, + { new Guid("f85e2381-f85f-4978-aeaa-dd0a3106d1ab"), new Guid("e9138a35-a4ff-460e-ac55-b743d55b9691"), new DateTime(2023, 3, 2, 10, 20, 51, 339, DateTimeKind.Utc).AddTicks(1378), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), true, "{\"Claims\":[]}", false, new DateTime(2023, 3, 2, 10, 20, 51, 339, DateTimeKind.Utc).AddTicks(1378), new Guid("55ba53a6-e482-4d9b-8d91-3fba6610b896"), 2, "资源", new Guid("ec9c35b4-3dfd-4cee-be70-9d83993b40e5"), "ec9c35b4-3dfd-4cee-be70-9d83993b40e5,f85e2381-f85f-4978-aeaa-dd0a3106d1ab,", "zy", "资源", 3, 1, "/permission/resource" } + }); + + migrationBuilder.CreateIndex( + name: "IX_Application_CreationTime", + schema: "Permissions", + table: "Application", + column: "CreationTime") + .Annotation("SqlServer:Clustered", true); + + migrationBuilder.CreateIndex( + name: "IX_Claim_CreationTime", + schema: "Permissions", + table: "Claim", + column: "CreationTime") + .Annotation("SqlServer:Clustered", true); + + migrationBuilder.CreateIndex( + name: "IX_Permission_CreationTime", + schema: "Permissions", + table: "Permission", + column: "CreationTime") + .Annotation("SqlServer:Clustered", true); + + migrationBuilder.CreateIndex( + name: "IX_Resource_ApplicationId", + schema: "Permissions", + table: "Resource", + column: "ApplicationId"); + + migrationBuilder.CreateIndex( + name: "IX_Resource_CreationTime", + schema: "Permissions", + table: "Resource", + column: "CreationTime") + .Annotation("SqlServer:Clustered", true); + + migrationBuilder.CreateIndex( + name: "IX_Resource_ParentId", + schema: "Permissions", + table: "Resource", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX_Role_CreationTime", + schema: "Permissions", + table: "Role", + column: "CreationTime") + .Annotation("SqlServer:Clustered", true); + + migrationBuilder.CreateIndex( + name: "IX_User_CreationTime", + schema: "Permissions", + table: "User", + column: "CreationTime") + .Annotation("SqlServer:Clustered", true); + + migrationBuilder.CreateIndex( + name: "IX_UserRole_RoleId", + schema: "Permissions", + table: "UserRole", + column: "RoleId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Claim", + schema: "Permissions"); + + migrationBuilder.DropTable( + name: "Permission", + schema: "Permissions"); + + migrationBuilder.DropTable( + name: "Resource", + schema: "Permissions"); + + migrationBuilder.DropTable( + name: "UserRole", + schema: "Permissions"); + + migrationBuilder.DropTable( + name: "Application", + schema: "Permissions"); + + migrationBuilder.DropTable( + name: "Role", + schema: "Permissions"); + + migrationBuilder.DropTable( + name: "User", + schema: "Permissions"); + } + } +} diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Samples/Test.cs b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Samples/Test.cs new file mode 100644 index 000000000..221a7fb24 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Samples/Test.cs @@ -0,0 +1,24 @@ +namespace Util.Data.EntityFrameworkCore.Samples; + +public interface ITest { + /// + /// 是否删除 + /// + bool IsDeleted { get; set; } +} + +public interface ITest2 { + /// + /// 租户标识 + /// + string TenantId { get; set; } +} + +public class Test:ITest,ITest2 { + public bool IsDeleted { get; set; } + public string TenantId { get; set; } +} + +public class Test2 : ITest2 { + public string TenantId { get; set; } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Samples/TestFilter.cs b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Samples/TestFilter.cs new file mode 100644 index 000000000..c255dfa0e --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Samples/TestFilter.cs @@ -0,0 +1,33 @@ +using System; +using System.Linq.Expressions; +using Util.Data.EntityFrameworkCore.Filters; + +namespace Util.Data.EntityFrameworkCore.Samples; + +/// +/// 测试过滤器1 +/// +public class TestFilter1 : FilterBase { + /// + /// 获取过滤表达式 + /// + /// 实体类型 + public override Expression> GetExpression( object state ) where TEntity : class { + Expression> expression = t => false; + return expression; + } +} + +/// +/// 测试过滤器2 +/// +public class TestFilter2 : FilterBase { + /// + /// 获取过滤表达式 + /// + /// 实体类型 + public override Expression> GetExpression( object state ) where TEntity : class { + Expression> expression = t => true; + return expression; + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Startup.cs b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Startup.cs new file mode 100644 index 000000000..a25ea407f --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Startup.cs @@ -0,0 +1,49 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Aop; +using Util.Dates; +using Util.Helpers; +using Util.Sessions; +using Util.Tenants; +using Util.Tests.Infrastructure; +using Util.Tests.UnitOfWorks; +using Xunit.DependencyInjection.Logging; + +namespace Util.Data.EntityFrameworkCore; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Util.Helpers.Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .AsBuild() + .AddAop() + .AddTenant() + .AddUtc() + .AddSqlServerUnitOfWork( Config.GetConnectionString( "connection" ) ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddSingleton(); + InitDatabase( services ); + } + + /// + /// ʼݿ + /// + private void InitDatabase( IServiceCollection services ) { + var unitOfWork = services.BuildServiceProvider().GetService(); + unitOfWork.EnsureDeleted(); + unitOfWork.EnsureCreated(); + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/CustomerRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/CustomerRepositoryTest.cs new file mode 100644 index 000000000..68604d4cc --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/CustomerRepositoryTest.cs @@ -0,0 +1,605 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Util.Data.Queries; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests; + +/// +/// 客户仓储测试 +/// 说明: +/// 1. 测试自增长int类型标识 +/// 2. 测试日期范围查询 +/// 3. 测试分页查询 +/// 4. 测试Utc日期 +/// +public class CustomerRepositoryTest : TestBase { + + #region 测试初始化 + + /// + /// 仓储 + /// + private readonly ICustomerRepository _repository; + + /// + /// 测试初始化 + /// + public CustomerRepositoryTest( ICustomerRepository repository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = repository; + } + + #endregion + + #region 辅助操作 + + /// + /// 添加实体列表 + /// + /// 添加数量 + /// 客户名称 + private async Task> AddCustomersAsync( int count = 2, string name = null ) { + var customers = CustomerFakeService.GetCustomers( count ); + customers.ForEach( customer => { + if ( name != null ) + customer.Name = name; + customer.CreationTime = null; + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + return customers.OrderBy( t => t.Code ).ToList(); + } + + #endregion + + #region Between + + /// + /// 测试添加范围查询条件 - 日期带时间 + /// + [Fact] + public async Task TestBetween_DateTime_1() { + //常量 + var name = "TestBetween_DateTime_1"; + var birthday = "2005-5-5 18:05:05".ToDateTime(); + var minBirthday = new DateTime( 2005, 5, 6, 19, 05, 05 ); + var maxBirthday = new DateTime( 2005, 5, 8, 18, 05, 05 ); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday ).ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 包含左边界 + /// + [Fact] + public async Task TestBetween_DateTime_2() { + //常量 + var name = "TestBetween_DateTime_2"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Left ).ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 包含右边界 + /// + [Fact] + public async Task TestBetween_DateTime_3() { + //常量 + var name = "TestBetween_DateTime_3"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Right ).ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 包含两边 + /// + [Fact] + public async Task TestBetween_DateTime_4() { + //常量 + var name = "TestBetween_DateTime_4"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Both ).ToListAsync(); + + //验证 + Assert.Equal( 3, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 不包含边界 + /// + [Fact] + public async Task TestBetween_DateTime_5() { + //常量 + var name = "TestBetween_DateTime_5"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Neither ).ToListAsync(); + + //验证 + Assert.Single( result ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 最大值为空 + /// + [Fact] + public async Task TestBetween_DateTime_6() { + //常量 + var name = "TestBetween_DateTime_6"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, null, boundary: Boundary.Both ).ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 最小值为空 + /// + [Fact] + public async Task TestBetween_DateTime_7() { + //常量 + var name = "TestBetween_DateTime_7"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, null, maxBirthday, boundary: Boundary.Both ).ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 包含左边界 + /// + [Fact] + public async Task TestBetween_Date_1() { + //常量 + var name = "TestBetween_Date_1"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Left ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + Assert.Equal( 6, result[0].Birthday.SafeValue().Day ); + Assert.Equal( 7, result[1].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 包含右边界 + /// + [Fact] + public async Task TestBetween_Date_2() { + //常量 + var name = "TestBetween_Date_2"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Right ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + Assert.Equal( 7, result[0].Birthday.SafeValue().Day ); + Assert.Equal( 8, result[1].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 包含两边 + /// + [Fact] + public async Task TestBetween_Date_3() { + //常量 + var name = "TestBetween_Date_3"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Both ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 3, result.Count ); + Assert.Equal( 6, result[0].Birthday.SafeValue().Day ); + Assert.Equal( 7, result[1].Birthday.SafeValue().Day ); + Assert.Equal( 8, result[2].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 不包含边界 + /// + [Fact] + public async Task TestBetween_Date_4() { + //常量 + var name = "TestBetween_Date_4"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Neither ) + .ToListAsync(); + + //验证 + Assert.Single( result ); + Assert.Equal( 7, result[0].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 最大值为空 + /// + [Fact] + public async Task TestBetween_Date_5() { + //常量 + var name = "TestBetween_Date_5"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Nickname = name; + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, null, false ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + Assert.Equal( 6, result[0].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 最小值为空 + /// + [Fact] + public async Task TestBetween_Date_6() { + //常量 + var name = "TestBetween_Date_6"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, null, maxBirthday, false ) + .ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + } + + #endregion + + #region ToPageList + + /// + /// 测试获取分页列表 + /// + [Fact] + public async Task TestToPageList_1() { + //常量 + var name = "TestToPageList_1"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 1, Order = "Code" }; + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).ToPageList( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 1, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[0].Id, result.Data[0].Id ); + Assert.Equal( customers[1].Id, result.Data[1].Id ); + Assert.Equal( customers[2].Id, result.Data[2].Id ); + } + + /// + /// 测试获取分页列表 - 第二页 + /// + [Fact] + public async Task TestToPageList_2() { + //常量 + var name = "TestToPageList_2"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 2, Order = "Code" }; + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).ToPageList( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 2, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[3].Id, result.Data[0].Id ); + Assert.Equal( customers[4].Id, result.Data[1].Id ); + Assert.Equal( customers[5].Id, result.Data[2].Id ); + } + + /// + /// 测试获取分页列表 - Queryable设置排序 + /// + [Fact] + public async Task TestToPageList_3() { + //常量 + var name = "TestToPageList_3"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 2 }; + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).OrderBy( t => t.Code ).ToPageList( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 2, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[3].Id, result.Data[0].Id ); + Assert.Equal( customers[4].Id, result.Data[1].Id ); + Assert.Equal( customers[5].Id, result.Data[2].Id ); + } + + #endregion + + #region ToPageListAsync + + /// + /// 测试获取分页列表 + /// + [Fact] + public async Task TestToPageListAsync() { + //常量 + var name = "TestToPageListAsync"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 2, Order = "Code" }; + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).ToPageListAsync( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 2, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[3].Id, result.Data[0].Id ); + Assert.Equal( customers[4].Id, result.Data[1].Id ); + Assert.Equal( customers[5].Id, result.Data[2].Id ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/EntityEventsTest.cs b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/EntityEventsTest.cs new file mode 100644 index 000000000..f167a1fff --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/EntityEventsTest.cs @@ -0,0 +1,322 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Util.Events; +using Util.Exceptions; +using Util.Tests.EventHandlers; +using Util.Tests.Events; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Data.EntityFrameworkCore.Tests; + +/// +/// 实体事件测试 +/// +public class EntityEventsTest : TestBase { + + #region 测试初始化 + + /// + /// 测试输出 + /// + private readonly ITestOutputHelper _testOutputHelper; + /// + /// 产品仓储 + /// + private readonly IProductRepository _productRepository; + /// + /// 操作日志仓储 + /// + private readonly IOperationLogRepository _operationLogRepository; + /// + /// 事件总线 + /// + private readonly IEventBus _eventBus; + + /// + /// 测试初始化 + /// + public EntityEventsTest( ITestOutputHelper testOutputHelper, IProductRepository productRepository, + IOperationLogRepository operationLogRepository, ITestUnitOfWork unitOfWork, IEventBus eventBus ) : base( unitOfWork ) { + _testOutputHelper = testOutputHelper; + _productRepository = productRepository; + _operationLogRepository = operationLogRepository; + _eventBus = eventBus; + } + + #endregion + + #region 实体创建事件 + + /// + /// 测试实体创建事件 + /// + [Fact] + public async Task TestEntityCreatedEvent() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityCreatedEvent"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityCreatedEvent" && t.LogName == nameof( ProductCreatedEventHandler ) ) ); + } + + /// + /// 测试实体变更事件 - 创建 + /// + [Fact] + public async Task TestEntityChangedEvent_Created() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityChangedEvent_Created"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityChangedEvent_Created" && t.LogName == nameof( ProductChangedEventHandler ) ) ); + } + + #endregion + + #region 实体修改事件 + + /// + /// 测试实体修改事件 + /// + [Fact] + public async Task TestEntityUpdatedEvent() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //修改实体 + var product = await _productRepository.FindByIdAsync( entity.Id ); + product.Name = "EntityUpdatedEvent"; + await _productRepository.UpdateAsync( product ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityUpdatedEvent" && t.LogName == nameof( ProductUpdatedEventHandler ) ) ); + } + + /// + /// 测试实体变更事件 - 修改 + /// + [Fact] + public async Task TestEntityChangedEvent_Updated() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //修改实体 + var product = await _productRepository.FindByIdAsync( entity.Id ); + product.Name = "EntityChangedEvent_Updated"; + await _productRepository.UpdateAsync( product ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityChangedEvent_Updated" && t.LogName == nameof( ProductChangedEventHandler ) ) ); + } + + #endregion + + #region 实体删除事件 + + /// + /// 测试实体删除事件 - 逻辑删除 + /// + [Fact] + public async Task TestEntityDeletedEvent_1() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityDeletedEvent"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _productRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityDeletedEvent" && t.LogName == nameof( ProductDeletedEventHandler ) ) ); + } + + /// + /// 测试实体删除事件 - 物理删除 + /// + [Fact] + public async Task TestEntityDeletedEvent_2() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + entity.Init(); + entity.LogName = "EntityDeletedEvent"; + await _operationLogRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _operationLogRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.LogName == "Test" && t.Caption == nameof( OperationLogDeletedEventHandler ) && t.Content == "EntityDeletedEvent" ) ); + } + + /// + /// 测试实体变更事件 - 逻辑删除 + /// + [Fact] + public async Task TestEntityChangedEvent_Deleted_1() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityChangedEvent_Deleted"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _productRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityChangedEvent_Deleted" && t.LogName == nameof( ProductChangedEventHandler ) ) ); + } + + /// + /// 测试实体变更事件 - 物理删除 + /// + [Fact] + public async Task TestEntityChangedEvent_Deleted_2() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + entity.Init(); + entity.LogName = "EntityChangedEvent_Deleted"; + await _operationLogRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _operationLogRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.LogName == "Test" && t.Caption == nameof( OperationLogChangedEventHandler ) && t.Content == "EntityChangedEvent_Deleted" ) ); + } + + #endregion + + #region 领域事件 + + /// + /// 测试领域事件 - 添加一个本地事件 + /// + [Fact] + public async Task TestDomainEvent_1() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + var code = $"TestDomainEvent_{entity.Id}"; + try { + entity = await _productRepository.FindByIdAsync( entity.Id ); + entity.Code = code; + entity.TestDomainEvent1(); + + //保存前触发事件处理器,抛出异常 + await UnitOfWork.CommitAsync(); + } + catch ( Warning ex ) { + //事件处理器抛出异常消息为实体ID + Assert.Equal( entity.Id.ToString(), ex.Message ); + + //保存前触发事件,所以没有保存成功 + entity = await _productRepository.SingleAsync( t => t.Code == code ); + Assert.Null( entity ); + return; + } + Assert.Fail(); + } + + /// + /// 测试领域事件 - 添加一个集成事件 + /// + [Fact] + public async Task TestDomainEvent_2() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + var description = $"TestDomainEvent_{entity.Id}"; + try { + entity = await _productRepository.FindByIdAsync( entity.Id ); + entity.Description = description; + entity.TestDomainEvent2(); + + //保存后触发事件处理器,抛出异常 + await UnitOfWork.CommitAsync(); + } + catch ( Warning ex ) { + //事件处理器抛出异常消息为实体ID + Assert.Equal( entity.Id.ToString(), ex.Message ); + + //保存后触发事件,所以保存成功 + entity = await _productRepository.SingleAsync( t => t.Description == description ); + Assert.NotNull( entity ); + return; + } + Assert.Fail(); + } + + #endregion + + #region TestEventBus_ServiceLifetime_Transient + + /// + /// 测试内存事件总线服务生命周期 + /// + [Fact] + public async Task TestEventBus_ServiceLifetime_Transient() { + var entity = new Product( Guid.NewGuid() ) { + Code = "Code", + Name = "Name" + }; + await _productRepository.AddAsync( entity ); + await _eventBus.PublishAsync( new TestEvent() { Id = entity.Id } ); + } + + /// + /// 内存事件处理器能,获取实体 + /// + public class TestEventHandler : EventHandlerBase { + private readonly IProductRepository _repository; + public TestEventHandler( IProductRepository applicationRepository ) { + _repository = applicationRepository; + } + public override async Task HandleAsync( TestEvent @event, CancellationToken cancellationToken ) { + var entity = await _repository.FindByIdAsync( @event.Id, cancellationToken ); + Assert.NotNull( entity ); + Assert.Equal( "Name", entity.Name ); + } + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/MigrationFileServiceTest.cs b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/MigrationFileServiceTest.cs new file mode 100644 index 000000000..64fd0ee02 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/MigrationFileServiceTest.cs @@ -0,0 +1,66 @@ +using Util.Data.EntityFrameworkCore.Migrations; +using Util.Helpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Data.EntityFrameworkCore.Tests; + +/// +/// 迁移文件服务测试 +/// +public class MigrationFileServiceTest { + /// + /// 迁移文件服务 + /// + private readonly IMigrationFileService _service; + /// + /// 测试消息输出 + /// + private readonly ITestOutputHelper _testOutputHelper; + /// + /// 迁移目录路径 + /// + private readonly string _migrationsPath; + + /// + /// 测试初始化 + /// + public MigrationFileServiceTest( IMigrationFileService service, ITestOutputHelper testOutputHelper ) { + _service = service; + _testOutputHelper = testOutputHelper; + _migrationsPath = Common.JoinPath( Common.GetCurrentDirectory(), "Samples/Migrations" ); + } + + /// + /// 测试获取迁移文件路径 + /// + [Fact] + public void TestGetFilePath() { + var path = _service.MigrationsPath( _migrationsPath ).MigrationName( "Init" ).GetFilePath(); + var result = Common.JoinPath( Common.GetCurrentDirectory(), "Samples/Migrations/20230302102051_Init.cs" ); + Assert.Equal( result, path.Replace( "\\","/" ) ); + } + + /// + /// 测试获取迁移文件内容 + /// + [Fact] + public void TestGetContent() { + var content = _service.MigrationsPath( _migrationsPath ).MigrationName( "Init" ).GetContent(); + Assert.Contains( "public partial class Init : Migration", content ); + Assert.Contains( "table.ForeignKey", content ); + } + + /// + /// 测试移除所有外键 + /// + [Fact] + public void TestRemoveForeignKeys() { + var filePath = Common.JoinPath( Common.GetCurrentDirectory(), "Samples/Migrations/Init_Copy.cs" ); + _service.MigrationsPath( _migrationsPath ).MigrationName( "Init" ).RemoveForeignKeys().Save( filePath ); + var result = Util.Helpers.File.ReadToString( filePath ); + _testOutputHelper.WriteLine(result); + Assert.DoesNotContain( "table.ForeignKey", result ); + Util.Helpers.File.Delete( filePath ); + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/OperationLogRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/OperationLogRepositoryTest.cs new file mode 100644 index 000000000..f9b487388 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/OperationLogRepositoryTest.cs @@ -0,0 +1,63 @@ +using System.Threading.Tasks; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests; + +/// +/// 操作日志仓储测试 +/// 说明: +/// 测试自增长long类型标识 +/// +public class OperationLogRepositoryTest : TestBase { + /// + /// 仓储 + /// + private readonly IOperationLogRepository _repository; + + /// + /// 测试初始化 + /// + public OperationLogRepositoryTest( IOperationLogRepository operationLogRepository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = operationLogRepository; + } + + /// + /// 测试添加实体 + /// + [Fact] + public async Task TestAdd() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( entity.Id > 0 ); + Assert.True( await _repository.ExistsAsync( t => t.Id == entity.Id ) ); + } + + /// + /// 测试通过标识移除实体 - 物理删除 + /// + [Fact] + public async Task TestRemove_Id() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/OrderRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/OrderRepositoryTest.cs new file mode 100644 index 000000000..d1831c6df --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/OrderRepositoryTest.cs @@ -0,0 +1,130 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests; + +/// +/// 订单仓储测试 +/// 说明: +/// 1. 测试string类型标识 +/// 2. 测试一对多导航属性 +/// +public class OrderRepositoryTest : TestBase { + + #region 测试初始化 + + /// + /// 仓储 + /// + private readonly IOrderRepository _repository; + + /// + /// 测试初始化 + /// + public OrderRepositoryTest( IOrderRepository repository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = repository; + } + + #endregion + + #region 辅助方法 + + /// + /// 添加订单 + /// + private async Task AddOrderAsync() { + var order = OrderFakeService.GetOrder(); + order.Init(); + order.OrderItems.Add( CreateOrderItem() ); + order.OrderItems.Add( CreateOrderItem() ); + await _repository.AddAsync( order ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + return order; + } + + /// + /// 创建订单项 + /// + private OrderItem CreateOrderItem() { + var item = OrderItemFakeService.GetOrderItem(); + item.Init(); + return item; + } + + #endregion + + #region Single + + /// + /// 测试查找单个实体 + /// + [Fact] + public async Task TestSingle_1() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id ); + + //验证 + Assert.Empty( result.OrderItems ); + } + + /// + /// 测试查找单个实体 - 使用Include包含子实体集合 + /// + [Fact] + public async Task TestSingle_2() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id, queryable => queryable.Include( t => t.OrderItems ) ); + + //验证 + Assert.Equal( 2, result.OrderItems.Count ); + } + + #endregion + + #region SingleAsync + + /// + /// 测试查找单个实体 + /// + [Fact] + public async Task TestSingleAsync_1() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id ); + + //验证 + Assert.Empty( result.OrderItems ); + } + + /// + /// 测试查找单个实体 - 使用Include包含子实体集合 + /// + [Fact] + public async Task TestSingleAsync_2() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id, queryable => queryable.Include( t => t.OrderItems ) ); + + //验证 + Assert.Equal( 2, result.OrderItems.Count ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/PostRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/PostRepositoryTest.cs new file mode 100644 index 000000000..535faa2b4 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/PostRepositoryTest.cs @@ -0,0 +1,54 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests; + +/// +/// 贴子仓储测试 +/// 说明: +/// 1. 测试多对多 +/// +public class PostRepositoryTest : TestBase { + /// + /// 仓储 + /// + private readonly IPostRepository _repository; + + /// + /// 测试初始化 + /// + public PostRepositoryTest(IPostRepository repository, ITestUnitOfWork unitOfWork ) : base(unitOfWork){ + _repository = repository; + } + + /// + /// 测试添加实体 + /// + [Fact] + public async Task TestAddAsync() { + //添加实体 + var post = PostFakeService.GetPost(); + post.Init(); + var tag1 = TagFakeService.GetTag(); + tag1.Init(); + post.Tags.Add( tag1 ); + + var tag2 = TagFakeService.GetTag(); + tag2.Init(); + post.Tags.Add( tag2 ); + + await _repository.AddAsync( post ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.SingleAsync( t => t.Id == post.Id, queryable => queryable.Include( t => t.Tags ) ); + Assert.NotNull( result ); + Assert.Equal( 2,result.Tags.Count ); + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/ProductRepositoryTest.Queryable.cs b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/ProductRepositoryTest.Queryable.cs new file mode 100644 index 000000000..91df4bfca --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/ProductRepositoryTest.Queryable.cs @@ -0,0 +1,390 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Util.Data.Queries; +using Util.Tests.Conditions; +using Util.Tests.Fakes; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests; + +/// +/// 产品仓储测试 - 查询测试 +/// +public partial class ProductRepositoryTest { + + #region Where + + /// + /// 测试设置查询条件 - 添加查询条件对象 + /// + [Fact] + public async Task TestWhere() { + //常量 + var name = "TestWhere"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //创建查询条件 + var condition = new ProductNameCondition( name ); + + //查询 + var result = _repository.Find().Where( condition ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + Assert.True( result.TrueForAll( t => products.Any( p => p.Id == t.Id ) ) ); + } + + #endregion + + #region WhereIf + + /// + /// 测试设置查询条件 - IQueryable添加WhereIf扩展 - true + /// + [Fact] + public async Task TestWhereIf_True() { + //常量 + var name = "TestWhereIf_True"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().WhereIf( t => t.Name == name, true ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加WhereIf扩展 - false + /// + [Fact] + public async Task TestWhereIf_False() { + //常量 + var name = "TestWhereIf_False"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().WhereIf( t => t.Name == name, false ).ToList(); + + //验证 + Assert.NotEqual( products.Count, result.Count ); + } + + #endregion + + #region WhereIfNotEmpty + + /// + /// 测试设置查询条件 - IQueryable添加WhereIfNotEmpty扩展 - 有效条件 + /// + [Fact] + public async Task TestWhereIfNotEmpty_Valid() { + //常量 + var name = "TestWhereIfNotEmpty_Valid"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().WhereIfNotEmpty( t => t.Name == name ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加WhereIfNotEmpty扩展 - 忽略条件 + /// + [Fact] + public async Task TestWhereIfNotEmpty_Ignore() { + //常量 + var name = "TestWhereIfNotEmpty_Ignore"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).WhereIfNotEmpty( t => t.Code == "" ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加WhereIfNotEmpty扩展 - 无效条件,抛出异常 + /// + [Fact] + public async Task TestWhereIfNotEmpty_Invalid() { + //添加实体列表 + await AddProductsAsync(); + + //查询 + Assert.Throws( () => { + _repository.Find().WhereIfNotEmpty( t => t.Name == "a" && t.Code == "b" ); + } ); + } + + #endregion + + #region OrIfNotEmpty + + /// + /// 测试设置查询条件 - IQueryable添加OrIfNotEmpty扩展 - 有效条件 + /// + [Fact] + public async Task TestOrIfNotEmpty_Valid() { + //常量 + var name1 = "TestOrIfNotEmpty_Valid_1"; + var name2 = "TestOrIfNotEmpty_Valid_2"; + var name3 = "TestOrIfNotEmpty_Valid_3"; + + //添加实体列表 + await AddProductsAsync(); + await AddProductsAsync( 2, name1 ); + await AddProductsAsync( 3, name2 ); + await AddProductsAsync( 4, name3 ); + + //查询 + var result = _repository.Find().OrIfNotEmpty( t => t.Name == name1,t => t.Name == name2, t => t.Name == name3 ).ToList(); + + //验证 + Assert.Equal( 9, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加OrIfNotEmpty扩展 - 忽略条件 + /// + [Fact] + public async Task TestOrIfNotEmpty_Ignore() { + //常量 + var name1 = "TestOrIfNotEmpty_Ignore_1"; + var name2 = "TestOrIfNotEmpty_Ignore_2"; + var name3 = "TestOrIfNotEmpty_Ignore_3"; + + //添加实体列表 + await AddProductsAsync(); + await AddProductsAsync( 2, name1 ); + await AddProductsAsync( 3, name2 ); + await AddProductsAsync( 4, name3 ); + + //查询 + var result = _repository.Find().OrIfNotEmpty( t => t.Name == name1, t => t.Name == name2, t => t.Name == "" ).ToList(); + + //验证 + Assert.Equal( 5, result.Count ); + } + + #endregion + + #region Between + + /// + /// 测试添加范围查询条件 + /// + [Fact] + public async Task TestBetween_1() { + //常量 + var name = "TestBetween_1"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price,11.5M,13.5M ).ToList(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 包含左边界 + /// + [Fact] + public async Task TestBetween_2() { + //常量 + var name = "TestBetween_2"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M,Boundary.Left ).ToList(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 包含右边界 + /// + [Fact] + public async Task TestBetween_3() { + //常量 + var name = "TestBetween_3"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M, Boundary.Right ).ToList(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 包含两边 + /// + [Fact] + public async Task TestBetween_4() { + //常量 + var name = "TestBetween_4"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M ).ToList(); + + //验证 + Assert.Equal( 3, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 不包含边界 + /// + [Fact] + public async Task TestBetween_5() { + //常量 + var name = "TestBetween_5"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M,Boundary.Neither ).ToList(); + + //验证 + Assert.Single( result ); + } + + /// + /// 测试添加范围查询条件 - 最大值为空 + /// + [Fact] + public async Task TestBetween_6() { + //常量 + var name = "TestBetween_6"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 9.5M, null ).ToList(); + + //验证 + Assert.Equal( 5, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 最小值为空 + /// + [Fact] + public async Task TestBetween_7() { + //常量 + var name = "TestBetween_7"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, null, 15.5M ).ToList(); + + //验证 + Assert.Equal( 5, result.Count ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/ProductRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/ProductRepositoryTest.cs new file mode 100644 index 000000000..cdc2f4d72 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/ProductRepositoryTest.cs @@ -0,0 +1,1651 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Util.Exceptions; +using Util.Helpers; +using Util.Tenants.Managements; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; +using Xunit.Abstractions; +using Xunit.DependencyInjection; + +namespace Util.Data.EntityFrameworkCore.Tests; + +/// +/// 产品仓储测试 +/// 说明: +/// 1. 测试Guid类型标识 +/// 2. 测试仓储基础方法 +/// 3. 测试扩展属性 +/// 4. 测试逻辑删除过滤器 +/// 5. 测试租户过滤器 +/// +public partial class ProductRepositoryTest : TestBase { + + #region 测试初始化 + + /// + /// 日志消息 + /// + private readonly ITestOutputHelper _log; + /// + /// 仓储 + /// + private readonly IProductRepository _repository; + + /// + /// 测试初始化 + /// + public ProductRepositoryTest( ITestOutputHelperAccessor accessor, IProductRepository repository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _log = accessor.Output; + _repository = repository; + } + + #endregion + + #region 辅助方法 + + /// + /// 添加产品 + /// + private async Task AddProductAsync( string name = null ) { + var entity = ProductFakeService.GetProduct(); + entity.Init(); + if ( name != null ) + entity.Name = name; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + return entity; + } + + /// + /// 添加实体列表 + /// + /// 添加数量 + /// 产品名称 + private async Task> AddProductsAsync( int count = 2, string name = null ) { + var products = ProductFakeService.GetProducts( count ); + products.ForEach( product => { + product.Init(); + if ( name != null ) + product.Name = name; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + return products.OrderBy( t => t.Id ).ToList(); + } + + /// + /// 打印更改跟踪调试信息 + /// + private void WriteTrackerDebug() { + _log.WriteLine( UnitOfWork.GetChangeTrackerDebugView() ); + } + + #endregion + + #region FindById + + /// + /// 测试通过标识查找实体 - 跟踪实体 + /// + [Fact] + public async Task TestFindById_Tracking_1() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 跟踪实体 - 字符串标识 + /// + [Fact] + public async Task TestFindById_Tracking_2() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id.ToString() ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 不跟踪实体 + /// + [Fact] + public async Task TestFindById_NoTracking() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.NoTracking().FindByIdAsync( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region FindByIdAsync + + /// + /// 测试通过标识查找实体 - 跟踪 - 异步 + /// + [Fact] + public async Task TestFindByIdAsync_Tracking_1() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 跟踪 - 异步 - 字符串标识 + /// + [Fact] + public async Task TestFindByIdAsync_Tracking_2() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id.ToString() ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 不跟踪 - 异步 + /// + [Fact] + public async Task TestFindByIdAsync_NoTracking() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.NoTracking().FindByIdAsync( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region FindByIds + + /// + /// 测试通过标识列表查找实体列表 - 传入逗号分隔的标识列表 + /// + [Fact] + public async Task TestFindByIds_1() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = ( await _repository.FindByIdsAsync( products[0].Id, products[1].Id, products[2].Id ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入集合 + /// + [Fact] + public async Task TestFindByIds_2() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = ( await _repository.FindByIdsAsync( products.Select( t => t.Id ) ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入标识列表字符串 + /// + [Fact] + public async Task TestFindByIds_3() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = ( await _repository.FindByIdsAsync( products.Select( t => t.Id ).Join() ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + #endregion + + #region FindByIdsAsync + + /// + /// 测试通过标识列表查找实体列表 - 传入逗号分隔的标识列表 - 异步 + /// + [Fact] + public async Task TestFindByIdsAsync_1() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = ( await _repository.FindByIdsAsync( products[0].Id, products[1].Id, products[2].Id ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入集合 - 异步 + /// + [Fact] + public async Task TestFindByIdsAsync_2() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = ( await _repository.FindByIdsAsync( products.Select( t => t.Id ) ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入标识列表字符串 - 异步 + /// + [Fact] + public async Task TestFindByIdsAsync_3() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = ( await _repository.FindByIdsAsync( products.Select( t => t.Id ).Join() ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + #endregion + + #region Single + + /// + /// 测试查找单个实体 + /// + [Fact] + public async Task TestSingle_1() { + //常量 + var name = "TestSingle"; + + //添加实体 + var entity = await AddProductAsync( name ); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.SingleAsync( t => t.Name == name ); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region SingleAsync + + /// + /// 测试查找单个实体 - 异步 + /// + [Fact] + public async Task TestSingleAsync() { + //常量 + var name = "TestSingleAsync"; + + //添加实体 + var entity = await AddProductAsync( name ); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.SingleAsync( t => t.Name == name ); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region Exists + + /// + /// 测试判断是否存在 + /// + [Fact] + public async Task TestExists() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + Assert.True( await _repository.ExistsAsync( t => t.Id == entity.Id ) ); + } + + #endregion + + #region ExistsAsync + + /// + /// 测试判断是否存在 - 异步 + /// + [Fact] + public async Task TestExistsAsync() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + Assert.True( await _repository.ExistsAsync( t => t.Id == entity.Id ) ); + } + + #endregion + + #region Count + + /// + /// 测试查找数量 + /// + [Fact] + public async Task TestCount() { + //常量 + var name = "TestCount"; + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ); + products.ForEach( product => { + product.Init(); + product.Name = name; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = await _repository.CountAsync( t => t.Name == name ); + Assert.Equal( count, result ); + } + + #endregion + + #region CountAsync + + /// + /// 测试查找数量 - 异步 + /// + [Fact] + public async Task TestCountAsync() { + //常量 + var name = "TestCountAsync"; + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ); + products.ForEach( product => { + product.Init(); + product.Name = name; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = await _repository.CountAsync( t => t.Name == name ); + Assert.Equal( count, result ); + } + + #endregion + + #region Update + + /// + /// 测试更新方法1 - 查找并修改实体,直接提交工作单元 + /// + [Fact] + public async Task TestUpdate_1() { + //常量 + var code = "TestUpdate_1_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //查找实体并修改 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + + //直接提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试更新并发 - 使用更新方法1 + /// 更新方法1的缺陷: 通过赋值的方式更新查找实体的版本号,无法触发乐观锁 + /// + [Fact] + public async Task TestUpdate_1_Concurrency() { + //常量 + var code = "TestUpdate_1_Concurrency_Code"; + var description = "TestUpdate_1_Concurrency_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,查找实体,并进行赋值 + var result = await _repository.FindByIdAsync( entity.Id ); + result.Code = product.Code; + result.Description = product.Description; + result.Version = product.Version; + //提交工作单元,由于版本号变化,预期应触发乐观锁,但实际未触发 + await UnitOfWork.CommitAsync(); + + //结果:用户1覆盖了用户2设置的Description属性 + Assert.Equal( code, result.Code ); + Assert.NotEqual( description, result.Description ); + } + + /// + /// 测试更新方法2 - 创建未跟踪实体,调用dbcontext.Update方法 + /// + [Fact] + public async Task TestUpdate_2() { + //常量 + var code = "TestUpdate_2_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + //调用dbcontext.Update方法更新实体 + _repository.UpdateProduct( product ); + //提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试更新方法2的陷阱 + /// 如果在创建未跟踪实体并调用dbcontext.Update方法之前,从dbcontext中已查找出该实体,将发生异常 + /// + [Fact] + public async Task TestUpdate_2_InvalidOperation() { + //常量 + var code = "TestUpdate_2_InvalidOperation_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //模拟更新之前无意中已查找出该实体 + await _repository.FindByIdAsync( entity.Id ); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + + //由于实体已被跟踪,调用dbcontext.Update方法更新实体将触发异常 + Assert.Throws( () => { + _repository.UpdateProduct( product ); + } ); + } + + /// + /// 测试更新并发 - 使用更新方法2 + /// + [Fact] + public async Task TestUpdate_2_Concurrency() { + //常量 + var code = "TestUpdate_2_Concurrency_Code"; + var description = "TestUpdate_2_Concurrency_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,创建一个新的实体,并赋值 + var result = product.MapTo(); + + //调用dbcontext.Update方法更新实体 + _repository.UpdateProduct( result ); + + //提交工作单元,由于版本号变化,触发乐观锁 + await Assert.ThrowsAsync( async () => { + await UnitOfWork.CommitAsync(); + } ); + } + + /// + /// 测试Util Update更新方法 - 查找并修改实体,提交工作单元 + /// + [Fact] + public async Task TestUpdate_3_1() { + //常量 + var code = "TestUpdate_3_1_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //查找实体并修改 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + + //调用Update方法 + await _repository.UpdateAsync( product ); + + //提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试Util Update更新方法 - 创建未跟踪实体,调用Update方法 + /// + [Fact] + public async Task TestUpdate_3_2() { + //常量 + var code = "TestUpdate_3_2_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + + //调用Update方法更新实体 + await _repository.UpdateAsync( product ); + + //提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试Util Update更新方法 - 允许在创建未跟踪实体并调用Update方法之前查找实体,不会发生异常 + /// + [Fact] + public async Task TestUpdate_3_3() { + //常量 + var code = "TestUpdate_3_3_InvalidOperation_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //模拟更新之前无意中已查找出该实体 + await _repository.FindByIdAsync( entity.Id ); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + + //调用Update方法更新实体 + await _repository.UpdateAsync( product ); + + //提交工作单元 + _log.WriteLine( UnitOfWork.GetChangeTrackerDebugView() ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试更新并发 - 使用Util Update更新方法 + /// 通过赋值的方式更新查找实体的版本号,触发乐观锁 + /// + [Fact] + public async Task TestUpdate_3_Concurrency_1() { + //常量 + var code = "TestUpdate_3_Concurrency_1_Code"; + var description = "TestUpdate_3_Concurrency_1_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,查找实体,并进行赋值 + var result = await _repository.FindByIdAsync( entity.Id ); + result.Code = product.Code; + result.Description = product.Description; + result.Version = product.Version; + + //调用Update方法更新实体时触发乐观锁 + await Assert.ThrowsAsync( async () => { + await _repository.UpdateAsync( result ); + } ); + } + + /// + /// 测试更新并发 - 使用Util Update更新方法 + /// 更新未跟踪实体触发乐观锁 + /// + [Fact] + public async Task TestUpdate_3_Concurrency_2() { + //常量 + var code = "TestUpdate_3_Concurrency_2_Code"; + var description = "TestUpdate_3_Concurrency_2_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,创建一个新的实体,并赋值 + var result = product.MapTo(); + + //调用Update方法更新实体 + await _repository.UpdateAsync( result ); + + //提交工作单元,由于版本号变化,触发乐观锁 + await Assert.ThrowsAsync( async () => { + await UnitOfWork.CommitAsync(); + } ); + } + + /// + /// 测试更新集合 + /// + [Fact] + public async Task TestUpdate_4() { + //常量 + var count = 3; + var code = "TestUpdate_4_Code"; + + //添加实体列表 + var entities = ProductFakeService.GetProducts( count ); + entities.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( entities ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查找实体,赋值,调用Update方法,提交工作单元 + var products = await _repository.FindByIdsAsync( entities.Select( t => t.Id ) ); + products.ForEach( product => product.Code = code ); + await _repository.UpdateAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdsAsync( entities.Select( t => t.Id ) ); + Assert.Equal( code, result[0].Code ); + Assert.Equal( code, result[1].Code ); + Assert.Equal( code, result[2].Code ); + } + + /// + /// 测试更新实体 - 先添加,不提交,查找修改, 跳过并发检测 + /// + [Fact] + public async Task TestUpdate_5() { + //常量 + var code = "TestUpdate_5_Code"; + + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _repository.AddAsync( entity ); + + //查找实体,赋值,调用Update方法 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + await _repository.UpdateAsync( product ); + + //提交工作单元 + await UnitOfWork.CommitAsync(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.Code ); + } + + #endregion + + #region Remove + + /// + /// 测试通过标识移除实体 - 逻辑删除 + /// + [Fact] + public async Task TestRemoveAsync_Id_1() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试通过标识移除实体 - 逻辑删除 - 使用工作单元禁用逻辑删除过滤器 + /// + [Fact] + public async Task TestRemoveAsync_Id_2() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //使用工作单元禁用逻辑删除过滤器,可以查出 + using ( UnitOfWork.DisableDeleteFilter() ) { + var product = await _repository.FindByIdAsync( entity.Id ); + Assert.True( product.IsDeleted ); + Assert.Equal( entity.Code, product.Code ); + } + UnitOfWork.ClearCache(); + + //逻辑删除过滤器已启用,无法查出 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试通过标识移除实体 - 逻辑删除 - 使用仓储禁用逻辑删除过滤器 + /// + [Fact] + public async Task TestRemoveAsync_Id_3() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //使用仓储禁用逻辑删除过滤器,可以查出 + using ( _repository.DisableDeleteFilter() ) { + var product = await _repository.FindByIdAsync( entity.Id ); + Assert.True( product.IsDeleted ); + Assert.Equal( entity.Code, product.Code ); + } + UnitOfWork.ClearCache(); + + //逻辑删除过滤器已启用,无法查出 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试移除实体 - 逻辑删除 + /// + [Fact] + public async Task TestRemoveAsync_Entity() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试移除实体集合 - 逻辑删除 - 通过标识列表 + /// + [Fact] + public async Task TestRemoveAsync_Entities_1() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //移除实体集合 + var ids = products.Select( t => t.Id ).ToList(); + await _repository.RemoveAsync( ids ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + using ( _repository.DisableDeleteFilter() ) { + var result = await _repository.FindByIdsAsync( ids ); + Assert.Equal( count, result.Count ); + result.ForEach( t => Assert.True( t.IsDeleted ) ); + } + } + + /// + /// 测试移除实体集合 - 逻辑删除 - 通过实体列表 + /// + [Fact] + public async Task TestRemoveAsync_Entities_2() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //移除实体集合 + await _repository.RemoveAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + using ( _repository.DisableDeleteFilter() ) { + var result = await _repository.FindByIdsAsync( products.Select( t => t.Id ) ); + Assert.Equal( count, result.Count ); + result.ForEach( t => Assert.True( t.IsDeleted ) ); + } + } + + #endregion + + #region Audit + + /// + /// 测试设置创建审计属性 + /// + [Fact] + public async Task TestCreationAudit() { + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _repository.AddAsync( entity ); + Assert.NotEqual( TestSession.TestUserId, entity.CreatorId ); + await UnitOfWork.CommitAsync(); + Assert.Equal( TestSession.TestUserId, entity.CreatorId ); + Assert.Equal( TestSession.TestUserId, entity.LastModifierId ); + } + + /// + /// 测试设置创建审计属性 - 手工赋值 + /// + [Fact] + public async Task TestCreationAudit_2() { + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _repository.AddAsync( entity ); + entity.CreatorId = TestSession.TestUserId2; + await UnitOfWork.CommitAsync(); + Assert.Equal( TestSession.TestUserId2, entity.CreatorId ); + Assert.Equal( TestSession.TestUserId, entity.LastModifierId ); + } + + /// + /// 测试设置修改审计属性 + /// + [Fact] + public async Task TestModificationAudit() { + //工作单元 + var unitOfWork = (SqlServerUnitOfWork)UnitOfWork; + + //常量 + var code = "TestModificationAudit"; + + //添加实体 + var entity = await AddProductAsync(); + unitOfWork.ClearCache(); + + //修改 + var product = entity.MapTo(); + product.Code = code; + await _repository.UpdateAsync( product ); + unitOfWork.SetSession( new TestSession { UserId = TestSession.TestUserId2.ToString() } ); + + //验证修改前审计属性 + Assert.Equal( TestSession.TestUserId, product.CreatorId ); + Assert.Equal( TestSession.TestUserId, product.LastModifierId ); + + //提交 + await unitOfWork.CommitAsync(); + + //验证修改后审计属性 + Assert.Equal( TestSession.TestUserId, product.CreatorId ); + Assert.Equal( TestSession.TestUserId2, product.LastModifierId ); + } + + #endregion + + #region ExtraProperties + + /// + /// 测试扩展属性 - 添加简单扩展属性 + /// + [Fact] + public async Task TestExtraProperties_1() { + //常量 + var value = "TestExtraProperties_1"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = value; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( value, result.TestProperty1 ); + } + + /// + /// 测试扩展属性 - 修改简单扩展属性 + /// + [Fact] + public async Task TestExtraProperties_2() { + //常量 + var oldValue = "TestExtraProperties_2_1"; + var newValue = "TestExtraProperties_2_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = oldValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( oldValue, oldEntity.TestProperty1 ); + + //修改实体 + oldEntity.TestProperty1 = newValue; + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newValue, result.TestProperty1 ); + } + + /// + /// 测试扩展属性 - 添加对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_3() { + //常量 + var code = 2; + var name = "TestExtraProperties_3"; + var enumValue = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = name; + entity.TestProperty2.EnumValue = enumValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.TestProperty2.Code ); + Assert.Equal( name, result.TestProperty2.Name ); + Assert.Equal( enumValue, result.TestProperty2.EnumValue ); + } + + /// + /// 测试扩展属性 - 修改对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_4() { + //常量 + var code = 2; + var oldName = "TestExtraProperties_4_1"; + var newName = "TestExtraProperties_4_2"; + var oldEnumValue = ProductEnum.Hello; + var newEnumValue = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = oldName; + entity.TestProperty2.EnumValue = oldEnumValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, oldEntity.TestProperty2.Code ); + Assert.Equal( oldName, oldEntity.TestProperty2.Name ); + Assert.Equal( oldEnumValue, oldEntity.TestProperty2.EnumValue ); + + //修改实体 + oldEntity.TestProperty2.Name = newName; + oldEntity.TestProperty2.EnumValue = newEnumValue; + WriteTrackerDebug(); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newName, result.TestProperty2.Name ); + Assert.Equal( newEnumValue, result.TestProperty2.EnumValue ); + } + + /// + /// 测试扩展属性 - 添加对象集合扩展属性 + /// + [Fact] + public async Task TestExtraProperties_5() { + //常量 + var code = 1; + var name1 = "TestExtraProperties_5_1"; + var name2 = "TestExtraProperties_5_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperties.Add( new ProductItem { Code = code, Name = name1 } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = name2 } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( 2, result.TestProperties.Count ); + Assert.Equal( code, result.TestProperties[0].Code ); + Assert.Equal( name1, result.TestProperties[0].Name ); + Assert.Equal( code, result.TestProperties[1].Code ); + Assert.Equal( name2, result.TestProperties[1].Name ); + } + + /// + /// 测试扩展属性 - 修改对象集合扩展属性 + /// + [Fact] + public async Task TestExtraProperties_6() { + //常量 + var code = 1; + var oldName = "TestExtraProperties_6_1"; + var newName = "TestExtraProperties_6_2"; + var name3 = "TestExtraProperties_6_3"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( 2, oldEntity.TestProperties.Count ); + + //修改实体 + oldEntity.TestProperties[0].Name = newName; + oldEntity.TestProperties[1].Name = newName; + oldEntity.TestProperties.Add( new ProductItem { Code = code, Name = name3 } ); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( 3, result.TestProperties.Count ); + Assert.Equal( code, result.TestProperties[0].Code ); + Assert.Equal( newName, result.TestProperties[0].Name ); + Assert.Equal( code, result.TestProperties[1].Code ); + Assert.Equal( newName, result.TestProperties[1].Name ); + Assert.Equal( code, result.TestProperties[2].Code ); + Assert.Equal( name3, result.TestProperties[2].Name ); + } + + /// + /// 测试扩展属性 - 添加多个扩展属性 + /// + [Fact] + public async Task TestExtraProperties_7() { + //常量 + var code = 2; + var name = "TestExtraProperties_7"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = name; + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = name; + entity.TestProperties.Add( new ProductItem { Code = code, Name = name } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = name } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( name, result.TestProperty1 ); + Assert.Equal( code, result.TestProperty2.Code ); + Assert.Equal( name, result.TestProperty2.Name ); + Assert.Equal( code, result.TestProperties[0].Code ); + Assert.Equal( name, result.TestProperties[0].Name ); + Assert.Equal( code, result.TestProperties[1].Code ); + Assert.Equal( name, result.TestProperties[1].Name ); + } + + /// + /// 测试扩展属性 - 修改多个扩展属性 + /// + [Fact] + public async Task TestExtraProperties_8() { + //常量 + var code = 2; + var oldName = "TestExtraProperties_8_1"; + var newName = "TestExtraProperties_8_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = oldName; + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = oldName; + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( oldName, oldEntity.TestProperty1 ); + Assert.Equal( code, oldEntity.TestProperty2.Code ); + Assert.Equal( oldName, oldEntity.TestProperty2.Name ); + Assert.Equal( 2, oldEntity.TestProperties.Count ); + + //修改实体 + oldEntity.TestProperty1 = newName; + oldEntity.TestProperty2.Name = newName; + oldEntity.TestProperties[0].Name = newName; + oldEntity.TestProperties[1].Name = newName; + oldEntity.TestProperties.Add( new ProductItem { Code = code, Name = newName } ); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newName, result.TestProperty1 ); + Assert.Equal( newName, result.TestProperty2.Name ); + Assert.Equal( 3, result.TestProperties.Count ); + Assert.Equal( newName, result.TestProperties[0].Name ); + Assert.Equal( newName, result.TestProperties[1].Name ); + Assert.Equal( newName, result.TestProperties[2].Name ); + } + + /// + /// 测试扩展属性 - 添加枚举扩展属性 + /// + [Fact] + public async Task TestExtraProperties_9() { + //常量 + ProductEnum value = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty3 = value; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( value, result.TestProperty3 ); + } + + /// + /// 测试扩展属性 - 修改枚举扩展属性 + /// + [Fact] + public async Task TestExtraProperties_10() { + //常量 + ProductEnum oldValue = ProductEnum.Hello; + ProductEnum newValue = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty3 = oldValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( oldValue, oldEntity.TestProperty3 ); + + //修改实体 + oldEntity.TestProperty3 = newValue; + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newValue, result.TestProperty3 ); + } + + /// + /// 测试扩展属性 - 添加不可变对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_11() { + //常量 + var code = 2; + var name = "TestExtraProperties_11"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty4 = new ProductItem2( code, name ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.TestProperty4.Code ); + Assert.Equal( name, result.TestProperty4.Name ); + } + + /// + /// 测试扩展属性 - 修改对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_12() { + //常量 + var code = 2; + var oldName = "TestExtraProperties_12_1"; + var newName = "TestExtraProperties_12_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty4 = new ProductItem2( code, oldName ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, oldEntity.TestProperty4.Code ); + Assert.Equal( oldName, oldEntity.TestProperty4.Name ); + + //修改实体 + oldEntity.TestProperty4 = new ProductItem2( code, newName ); + WriteTrackerDebug(); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newName, result.TestProperty4.Name ); + } + + /// + /// 测试扩展属性 - 读取对象扩展属性,不应该导致更新 + /// + [Fact] + public async Task TestExtraProperties_13() { + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty4 = new ProductItem2( 3, "TestExtraProperties_13" ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //读取对象扩展属性 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + var property = oldEntity.TestProperty4; + _log.WriteLine( UnitOfWork.GetChangeTrackerDebugView() ); + + //验证 + Assert.False( UnitOfWork.IsChange() ); + } + + + /// + /// 测试扩展属性 - 读取对象扩展属性,不应该导致更新 + /// + [Fact] + public async Task TestExtraProperties_14() { + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty5 = new ApplicationExtend() { + ApplicationType = 2, + Client = new Client() { + RedirectUris = new List() + { + "http://util.com", + "http://baidu.com", + } + }, + IsClient = true + }; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //读取对象扩展属性 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + var property5 = oldEntity.TestProperty5.Client.RedirectUris; + _log.WriteLine( UnitOfWork.GetChangeTrackerDebugView() ); + + //验证 + Assert.False( UnitOfWork.IsChange() ); + } + + /// + /// 测试扩展属性 - 清除字符串属性两端空白 + /// + [Fact] + public async Task TestExtraProperties_15() { + //常量 + var value = " a "; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = value; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( "a", result.TestProperty1 ); + } + + /// + /// 测试扩展属性 - 添加日期扩展属性 + /// + [Fact] + public async Task TestExtraProperties_16() { + //常量 + var value = DateTime.Now; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty6 = value; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( value.ToDateTimeString(), result.TestProperty6.ToDateTimeString() ); + } + + /// + /// 测试扩展属性 - 添加日期集合扩展属性 + /// + [Fact] + public async Task TestExtraProperties_17() { + //常量 + var value = DateTime.Now; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TaskRecordList = [ + new TaskRecord { + ExecutionTime = value + } + ]; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( value.ToDateTimeString(), result.TaskRecordList?.FirstOrDefault()?.ExecutionTime.ToDateTimeString() ); + } + + #endregion + + #region 测试租户过滤器 + + /// + /// 测试租户过滤器 + /// + [Fact] + public async Task TestTenantFilter_1() { + //变量定义 + var name = $"TestTenantFilter_{Id.Create()}"; + var tenantId = "tenant_1"; + var tenantId2 = "tenant_2"; + + //设置当前租户标识 + TenantManager.CurrentTenantId = tenantId; + + //添加实体1 + var entity = await AddProductAsync( name ); + UnitOfWork.ClearCache(); + + //验证租户标识已设置 + Assert.Equal( tenantId, entity.TenantId ); + + //设置当前租户标识 + TenantManager.CurrentTenantId = tenantId2; + + //添加实体2 + entity = await AddProductAsync( name ); + UnitOfWork.ClearCache(); + + //验证租户标识已设置 + Assert.Equal( tenantId2, entity.TenantId ); + + //查询 + var result = await _repository.FindAllAsync( t => t.Name == name ); + Assert.Single( result ); + Assert.Equal( tenantId2, result[0].TenantId ); + } + + /// + /// 测试租户过滤器 - 禁用租户过滤器 + /// + [Fact] + public async Task TestTenantFilter_2() { + //变量定义 + var name = $"TestTenantFilter_{Id.Create()}"; + var tenantId = "tenant_1"; + var tenantId2 = "tenant_2"; + + //设置当前租户标识 + TenantManager.CurrentTenantId = tenantId; + + //添加实体1 + var entity = await AddProductAsync( name ); + UnitOfWork.ClearCache(); + + //验证租户标识已设置 + Assert.Equal( tenantId, entity.TenantId ); + + //设置当前租户标识 + TenantManager.CurrentTenantId = tenantId2; + + //添加实体2 + entity = await AddProductAsync( name ); + UnitOfWork.ClearCache(); + + //验证租户标识已设置 + Assert.Equal( tenantId2, entity.TenantId ); + + //查询 + using ( _repository.DisableTenantFilter() ) { + var result = await _repository.FindAllAsync( t => t.Name == name ); + Assert.Equal( 2, result.Count ); + } + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/TagRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/TagRepositoryTest.cs new file mode 100644 index 000000000..9dfce91a2 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Tests/TagRepositoryTest.cs @@ -0,0 +1,44 @@ +using System.Threading.Tasks; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests; + +/// +/// 标签仓储测试 +/// +public class TagRepositoryTest : TestBase { + /// + /// 仓储 + /// + private readonly ITagRepository _repository; + + /// + /// 测试初始化 + /// + public TagRepositoryTest( ITagRepository repository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = repository; + } + + /// + /// 测试添加实体 - 自动清除字符串两端空白 + /// + [Fact] + public async Task TestAddAsync() { + //添加实体 + var entity = TagFakeService.GetTag(); + entity.Init(); + entity.Name = " a "; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.True( result != null ); + Assert.Equal( "a",result.Name ); + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration.csproj b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration.csproj new file mode 100644 index 000000000..a65849932 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration.csproj @@ -0,0 +1,54 @@ + + + + $(NetTargetFramework) + false + Util.Data.EntityFrameworkCore + Util.Data.EntityFrameworkCore.Startup + + + + + + + + + Always + + + Always + + + + + + Always + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/appsettings.Development.json b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..8ff212a2c --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Information" + } + }, + "ConnectionStrings": { + "connection": "Server=.;Database=Util.Data.EntityFrameworkCore.Test;uid=sa;pwd=Pass@word;TrustServerCertificate=true" + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/appsettings.json b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/appsettings.json new file mode 100644 index 000000000..0cf1fa8b8 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.SqlServer.Tests.Integration/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + }, + "ConnectionStrings": { + "connection": "Server=192.168.31.123\\sql2019;Database=Util.Data.EntityFrameworkCore.Test;uid=sa;pwd=sa;MultipleActiveResultSets=true;TrustServerCertificate=true" + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Startup.cs b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Startup.cs new file mode 100644 index 000000000..6bbb94965 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Startup.cs @@ -0,0 +1,45 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Aop; +using Util.Helpers; +using Util.Sessions; +using Util.Tests.Infrastructure; +using Util.Tests.UnitOfWorks; +using Xunit.DependencyInjection.Logging; + +namespace Util.Data.EntityFrameworkCore { + /// + /// + /// + public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .AsBuild() + .AddAop() + .AddSqliteUnitOfWork( Config.GetConnectionString( "connection" ) ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddSingleton(); + InitDatabase( services ); + } + + /// + /// ʼݿ + /// + private void InitDatabase( IServiceCollection services ) { + var unitOfWork = services.BuildServiceProvider().GetService(); + unitOfWork.EnsureDeleted(); + unitOfWork.EnsureCreated(); + } + } +} diff --git a/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/CustomerRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/CustomerRepositoryTest.cs new file mode 100644 index 000000000..4f1362643 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/CustomerRepositoryTest.cs @@ -0,0 +1,604 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Util.Data.Queries; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 客户仓储测试 + /// 说明: + /// 1. 测试自增长int类型标识 + /// 2. 测试日期范围查询 + /// 3. 测试分页查询 + /// 4. 测试Utc日期 + /// + public class CustomerRepositoryTest : TestBase { + + #region 测试初始化 + + /// + /// 仓储 + /// + private readonly ICustomerRepository _repository; + + /// + /// 测试初始化 + /// + public CustomerRepositoryTest( ICustomerRepository repository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = repository; + } + + #endregion + + #region 辅助操作 + + /// + /// 添加实体列表 + /// + /// 添加数量 + /// 客户名称 + private async Task> AddCustomersAsync( int count = 2, string name = null ) { + var customers = CustomerFakeService.GetCustomers( count ); + customers.ForEach( customer => { + if ( name != null ) + customer.Name = name; + customer.CreationTime = null; + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + return customers.OrderBy( t => t.Code ).ToList(); + } + + #endregion + + #region Between + + /// + /// 测试添加范围查询条件 - 日期带时间 + /// + [Fact] + public async Task TestBetween_DateTime_1() { + //常量 + var name = "TestBetween_DateTime_1"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 06:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 06:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday ).ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 包含左边界 + /// + [Fact] + public async Task TestBetween_DateTime_2() { + //常量 + var name = "TestBetween_DateTime_2"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Left ).ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 包含右边界 + /// + [Fact] + public async Task TestBetween_DateTime_3() { + //常量 + var name = "TestBetween_DateTime_3"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Right ).ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 包含两边 + /// + [Fact] + public async Task TestBetween_DateTime_4() { + //常量 + var name = "TestBetween_DateTime_4"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Both ).ToListAsync(); + + //验证 + Assert.Equal( 3, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 不包含边界 + /// + [Fact] + public async Task TestBetween_DateTime_5() { + //常量 + var name = "TestBetween_DateTime_5"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, maxBirthday, boundary: Boundary.Neither ).ToListAsync(); + + //验证 + Assert.Single( result ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 最大值为空 + /// + [Fact] + public async Task TestBetween_DateTime_6() { + //常量 + var name = "TestBetween_DateTime_6"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, minBirthday, null, boundary: Boundary.Both ).ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期带时间 - 最小值为空 + /// + [Fact] + public async Task TestBetween_DateTime_7() { + //常量 + var name = "TestBetween_DateTime_7"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8 05:05:05".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).Between( t => t.Birthday, null, maxBirthday, boundary: Boundary.Both ).ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 包含左边界 + /// + [Fact] + public async Task TestBetween_Date_1() { + //常量 + var name = "TestBetween_Date_1"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Left ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + Assert.Equal( 6, result[0].Birthday.SafeValue().Day ); + Assert.Equal( 7, result[1].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 包含右边界 + /// + [Fact] + public async Task TestBetween_Date_2() { + //常量 + var name = "TestBetween_Date_2"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Right ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 2, result.Count ); + Assert.Equal( 7, result[0].Birthday.SafeValue().Day ); + Assert.Equal( 8, result[1].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 包含两边 + /// + [Fact] + public async Task TestBetween_Date_3() { + //常量 + var name = "TestBetween_Date_3"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Both ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 3, result.Count ); + Assert.Equal( 6, result[0].Birthday.SafeValue().Day ); + Assert.Equal( 7, result[1].Birthday.SafeValue().Day ); + Assert.Equal( 8, result[2].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 不包含边界 + /// + [Fact] + public async Task TestBetween_Date_4() { + //常量 + var name = "TestBetween_Date_4"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, maxBirthday, false, Boundary.Neither ) + .ToListAsync(); + + //验证 + Assert.Single( result ); + Assert.Equal( 7, result[0].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 最大值为空 + /// + [Fact] + public async Task TestBetween_Date_5() { + //常量 + var name = "TestBetween_Date_5"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var minBirthday = "2005-5-6".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Nickname = name; + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, minBirthday, null, false ) + .OrderBy( t => t.Birthday ) + .ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + Assert.Equal( 6, result[0].Birthday.SafeValue().Day ); + } + + /// + /// 测试添加范围查询条件 - 日期不带时间 - 最小值为空 + /// + [Fact] + public async Task TestBetween_Date_6() { + //常量 + var name = "TestBetween_Date_6"; + var birthday = "2005-5-5 05:05:05".ToDateTime(); + var maxBirthday = "2005-5-8".ToDateTime(); + + //添加实体列表 + await AddCustomersAsync(); + var customers = CustomerFakeService.GetCustomers( 5 ); + customers.ForEach( customer => { + customer.Name = name; + customer.Birthday = birthday; + birthday = birthday.AddDays( 1 ); + } ); + await _repository.AddAsync( customers ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = await _repository.Find() + .Where( t => t.Name == name ) + .Between( t => t.Birthday, null, maxBirthday, false ) + .ToListAsync(); + + //验证 + Assert.Equal( 4, result.Count ); + } + + #endregion + + #region ToPageList + + /// + /// 测试获取分页列表 + /// + [Fact] + public async Task TestToPageList_1() { + //常量 + var name = "TestToPageList_1"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 1, Order = "Code" }; + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).ToPageList( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 1, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[0].Id, result.Data[0].Id ); + Assert.Equal( customers[1].Id, result.Data[1].Id ); + Assert.Equal( customers[2].Id, result.Data[2].Id ); + } + + /// + /// 测试获取分页列表 - 第二页 + /// + [Fact] + public async Task TestToPageList_2() { + //常量 + var name = "TestToPageList_2"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 2, Order = "Code" }; + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).ToPageList( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 2, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[3].Id, result.Data[0].Id ); + Assert.Equal( customers[4].Id, result.Data[1].Id ); + Assert.Equal( customers[5].Id, result.Data[2].Id ); + } + + /// + /// 测试获取分页列表 - Queryable设置排序 + /// + [Fact] + public async Task TestToPageList_3() { + //常量 + var name = "TestToPageList_3"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 2 }; + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).OrderBy( t => t.Code ).ToPageList( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 2, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[3].Id, result.Data[0].Id ); + Assert.Equal( customers[4].Id, result.Data[1].Id ); + Assert.Equal( customers[5].Id, result.Data[2].Id ); + } + + #endregion + + #region ToPageListAsync + + /// + /// 测试获取分页列表 + /// + [Fact] + public async Task TestToPageListAsync() { + //常量 + var name = "TestToPageListAsync"; + + //添加实体列表 + var customers = await AddCustomersAsync( 10, name ); + + //创建查询参数 + var parameter = new QueryParameter { PageSize = 3, Page = 2, Order = "Code" }; + + //查询 + var result = await _repository.Find().Where( t => t.Name == name ).ToPageListAsync( parameter ); + + //验证 + Assert.Equal( 4, result.PageCount ); + Assert.Equal( 2, result.Page ); + Assert.Equal( 3, result.PageSize ); + Assert.Equal( 10, result.Total ); + Assert.Equal( 3, result.Data.Count ); + Assert.Equal( customers[3].Id, result.Data[0].Id ); + Assert.Equal( customers[4].Id, result.Data[1].Id ); + Assert.Equal( customers[5].Id, result.Data[2].Id ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/EntityEventsTest.cs b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/EntityEventsTest.cs new file mode 100644 index 000000000..7dcdd8795 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/EntityEventsTest.cs @@ -0,0 +1,211 @@ +using System.Threading.Tasks; +using Util.Tests.EventHandlers; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 实体事件测试 + /// + public class EntityEventsTest : TestBase { + + #region 测试初始化 + + /// + /// 测试输出 + /// + private readonly ITestOutputHelper _testOutputHelper; + /// + /// 产品仓储 + /// + private readonly IProductRepository _productRepository; + /// + /// 操作日志仓储 + /// + private readonly IOperationLogRepository _operationLogRepository; + + /// + /// 测试初始化 + /// + public EntityEventsTest( ITestOutputHelper testOutputHelper, IProductRepository productRepository, + IOperationLogRepository operationLogRepository, ITestUnitOfWork unitOfWork ) :base(unitOfWork){ + _testOutputHelper = testOutputHelper; + _productRepository = productRepository; + _operationLogRepository = operationLogRepository; + } + + #endregion + + #region 实体创建事件 + + /// + /// 测试实体创建事件 + /// + [Fact] + public async Task TestEntityCreatedEvent() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityCreatedEvent"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityCreatedEvent" && t.LogName == nameof( ProductCreatedEventHandler ) ) ); + } + + /// + /// 测试实体变更事件 - 创建 + /// + [Fact] + public async Task TestEntityChangedEvent_Created() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityChangedEvent_Created"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityChangedEvent_Created" && t.LogName == nameof( ProductChangedEventHandler ) ) ); + } + + #endregion + + #region 实体修改事件 + + /// + /// 测试实体修改事件 + /// + [Fact] + public async Task TestEntityUpdatedEvent() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //修改实体 + var product = await _productRepository.FindByIdAsync( entity.Id ); + product.Name = "EntityUpdatedEvent"; + await _productRepository.UpdateAsync( product ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityUpdatedEvent" && t.LogName == nameof( ProductUpdatedEventHandler ) ) ); + } + + /// + /// 测试实体变更事件 - 修改 + /// + [Fact] + public async Task TestEntityChangedEvent_Updated() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //修改实体 + var product = _productRepository.FindById( entity.Id ); + product.Name = "EntityChangedEvent_Updated"; + await _productRepository.UpdateAsync( product ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityChangedEvent_Updated" && t.LogName == nameof( ProductChangedEventHandler ) ) ); + } + + #endregion + + #region 实体删除事件 + + /// + /// 测试实体删除事件 - 逻辑删除 + /// + [Fact] + public async Task TestEntityDeletedEvent_1() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityDeletedEvent"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _productRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityDeletedEvent" && t.LogName == nameof( ProductDeletedEventHandler ) ) ); + } + + /// + /// 测试实体删除事件 - 物理删除 + /// + [Fact] + public async Task TestEntityDeletedEvent_2() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + entity.Init(); + entity.LogName = "EntityDeletedEvent"; + await _operationLogRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _operationLogRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.LogName == "Test" && t.Caption == nameof( OperationLogDeletedEventHandler ) && t.Content == "EntityDeletedEvent" ) ); + } + + /// + /// 测试实体变更事件 - 逻辑删除 + /// + [Fact] + public async Task TestEntityChangedEvent_Deleted_1() { + //添加实体 + var entity = ProductFakeService.GetProduct(); + entity.Init(); + entity.Name = "EntityChangedEvent_Deleted"; + await _productRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _productRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.Caption == "EntityChangedEvent_Deleted" && t.LogName == nameof( ProductChangedEventHandler ) ) ); + } + + /// + /// 测试实体变更事件 - 物理删除 + /// + [Fact] + public async Task TestEntityChangedEvent_Deleted_2() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + entity.Init(); + entity.LogName = "EntityChangedEvent_Deleted"; + await _operationLogRepository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //删除实体 + await _operationLogRepository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( await _operationLogRepository.ExistsAsync( t => t.LogName == "Test" && t.Caption == nameof( OperationLogChangedEventHandler ) && t.Content == "EntityChangedEvent_Deleted" ) ); + } + + #endregion + } +} diff --git a/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/OperationLogRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/OperationLogRepositoryTest.cs new file mode 100644 index 000000000..3e63861b6 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/OperationLogRepositoryTest.cs @@ -0,0 +1,63 @@ +using System.Threading.Tasks; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 操作日志仓储测试 + /// 说明: + /// 测试自增长long类型标识 + /// + public class OperationLogRepositoryTest : TestBase { + /// + /// 仓储 + /// + private readonly IOperationLogRepository _repository; + + /// + /// 测试初始化 + /// + public OperationLogRepositoryTest( IOperationLogRepository operationLogRepository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = operationLogRepository; + } + + /// + /// 测试添加实体 + /// + [Fact] + public async Task TestAdd() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + + //验证 + Assert.True( entity.Id > 0 ); + Assert.True( _repository.Exists( t => t.Id == entity.Id ) ); + } + + /// + /// 测试通过标识移除实体 - 物理删除 + /// + [Fact] + public async Task TestRemove_Id() { + //添加实体 + var entity = OperationLogFakeService.GetOperationLog(); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + } +} diff --git a/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/OrderRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/OrderRepositoryTest.cs new file mode 100644 index 000000000..713ac562a --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/OrderRepositoryTest.cs @@ -0,0 +1,130 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 订单仓储测试 + /// 说明: + /// 1. 测试string类型标识 + /// 2. 测试一对多导航属性 + /// + public class OrderRepositoryTest : TestBase { + + #region 测试初始化 + + /// + /// 仓储 + /// + private readonly IOrderRepository _repository; + + /// + /// 测试初始化 + /// + public OrderRepositoryTest( IOrderRepository repository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = repository; + } + + #endregion + + #region 辅助方法 + + /// + /// 添加订单 + /// + private async Task AddOrderAsync() { + var order = OrderFakeService.GetOrder(); + order.Init(); + order.OrderItems.Add( CreateOrderItem() ); + order.OrderItems.Add( CreateOrderItem() ); + await _repository.AddAsync( order ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + return order; + } + + /// + /// 创建订单项 + /// + private OrderItem CreateOrderItem() { + var item = OrderItemFakeService.GetOrderItem(); + item.Init(); + return item; + } + + #endregion + + #region Single + + /// + /// 测试查找单个实体 + /// + [Fact] + public async Task TestSingle_1() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id ); + + //验证 + Assert.Empty( result.OrderItems ); + } + + /// + /// 测试查找单个实体 - 使用Include包含子实体集合 + /// + [Fact] + public async Task TestSingle_2() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id, queryable => queryable.Include( t => t.OrderItems ) ); + + //验证 + Assert.Equal( 2, result.OrderItems.Count ); + } + + #endregion + + #region SingleAsync + + /// + /// 测试查找单个实体 + /// + [Fact] + public async Task TestSingleAsync_1() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id ); + + //验证 + Assert.Empty( result.OrderItems ); + } + + /// + /// 测试查找单个实体 - 使用Include包含子实体集合 + /// + [Fact] + public async Task TestSingleAsync_2() { + //添加实体 + var entity = await AddOrderAsync(); + + //查找单个实体 + var result = await _repository.SingleAsync( t => t.Id == entity.Id, queryable => queryable.Include( t => t.OrderItems ) ); + + //验证 + Assert.Equal( 2, result.OrderItems.Count ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/PostRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/PostRepositoryTest.cs new file mode 100644 index 000000000..573430a30 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/PostRepositoryTest.cs @@ -0,0 +1,54 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 贴子仓储测试 + /// 说明: + /// 1. 测试多对多 + /// + public class PostRepositoryTest : TestBase { + /// + /// 仓储 + /// + private readonly IPostRepository _repository; + + /// + /// 测试初始化 + /// + public PostRepositoryTest(IPostRepository repository, ITestUnitOfWork unitOfWork ) : base(unitOfWork){ + _repository = repository; + } + + /// + /// 测试添加实体 + /// + [Fact] + public async Task TestAddAsync() { + //添加实体 + var post = PostFakeService.GetPost(); + post.Init(); + var tag1 = TagFakeService.GetTag(); + tag1.Init(); + post.Tags.Add( tag1 ); + + var tag2 = TagFakeService.GetTag(); + tag2.Init(); + post.Tags.Add( tag2 ); + + await _repository.AddAsync( post ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.SingleAsync( t => t.Id == post.Id, queryable => queryable.Include( t => t.Tags ) ); + Assert.NotNull( result ); + Assert.Equal( 2,result.Tags.Count ); + } + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/ProductRepositoryTest.Queryable.cs b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/ProductRepositoryTest.Queryable.cs new file mode 100644 index 000000000..5ad8a9475 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/ProductRepositoryTest.Queryable.cs @@ -0,0 +1,390 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Util.Data.Queries; +using Util.Tests.Conditions; +using Util.Tests.Fakes; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 产品仓储测试 - 查询测试 + /// + public partial class ProductRepositoryTest { + + #region Where + + /// + /// 测试设置查询条件 - 添加查询条件对象 + /// + [Fact] + public async Task TestWhere() { + //常量 + var name = "TestWhere"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //创建查询条件 + var condition = new ProductNameCondition( name ); + + //查询 + var result = _repository.Find().Where( condition ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + Assert.True( result.TrueForAll( t => products.Any( p => p.Id == t.Id ) ) ); + } + + #endregion + + #region WhereIf + + /// + /// 测试设置查询条件 - IQueryable添加WhereIf扩展 - true + /// + [Fact] + public async Task TestWhereIf_True() { + //常量 + var name = "TestWhereIf_True"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().WhereIf( t => t.Name == name, true ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加WhereIf扩展 - false + /// + [Fact] + public async Task TestWhereIf_False() { + //常量 + var name = "TestWhereIf_False"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().WhereIf( t => t.Name == name, false ).ToList(); + + //验证 + Assert.NotEqual( products.Count, result.Count ); + } + + #endregion + + #region WhereIfNotEmpty + + /// + /// 测试设置查询条件 - IQueryable添加WhereIfNotEmpty扩展 - 有效条件 + /// + [Fact] + public async Task TestWhereIfNotEmpty_Valid() { + //常量 + var name = "TestWhereIfNotEmpty_Valid"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().WhereIfNotEmpty( t => t.Name == name ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加WhereIfNotEmpty扩展 - 忽略条件 + /// + [Fact] + public async Task TestWhereIfNotEmpty_Ignore() { + //常量 + var name = "TestWhereIfNotEmpty_Ignore"; + + //添加实体列表 + await AddProductsAsync(); + var products = await AddProductsAsync( name: name ); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).WhereIfNotEmpty( t => t.Code == "" ).ToList(); + + //验证 + Assert.Equal( products.Count, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加WhereIfNotEmpty扩展 - 无效条件,抛出异常 + /// + [Fact] + public async Task TestWhereIfNotEmpty_Invalid() { + //添加实体列表 + await AddProductsAsync(); + + //查询 + Assert.Throws( () => { + _repository.Find().WhereIfNotEmpty( t => t.Name == "a" && t.Code == "b" ); + } ); + } + + #endregion + + #region OrIfNotEmpty + + /// + /// 测试设置查询条件 - IQueryable添加OrIfNotEmpty扩展 - 有效条件 + /// + [Fact] + public async Task TestOrIfNotEmpty_Valid() { + //常量 + var name1 = "TestOrIfNotEmpty_Valid_1"; + var name2 = "TestOrIfNotEmpty_Valid_2"; + var name3 = "TestOrIfNotEmpty_Valid_3"; + + //添加实体列表 + await AddProductsAsync(); + await AddProductsAsync( 2, name1 ); + await AddProductsAsync( 3, name2 ); + await AddProductsAsync( 4, name3 ); + + //查询 + var result = _repository.Find().OrIfNotEmpty( t => t.Name == name1,t => t.Name == name2, t => t.Name == name3 ).ToList(); + + //验证 + Assert.Equal( 9, result.Count ); + } + + /// + /// 测试设置查询条件 - IQueryable添加OrIfNotEmpty扩展 - 忽略条件 + /// + [Fact] + public async Task TestOrIfNotEmpty_Ignore() { + //常量 + var name1 = "TestOrIfNotEmpty_Ignore_1"; + var name2 = "TestOrIfNotEmpty_Ignore_2"; + var name3 = "TestOrIfNotEmpty_Ignore_3"; + + //添加实体列表 + await AddProductsAsync(); + await AddProductsAsync( 2, name1 ); + await AddProductsAsync( 3, name2 ); + await AddProductsAsync( 4, name3 ); + + //查询 + var result = _repository.Find().OrIfNotEmpty( t => t.Name == name1, t => t.Name == name2, t => t.Name == "" ).ToList(); + + //验证 + Assert.Equal( 5, result.Count ); + } + + #endregion + + #region Between + + /// + /// 测试添加范围查询条件 + /// + [Fact] + public async Task TestBetween_1() { + //常量 + var name = "TestBetween_1"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price,11.5M,13.5M ).ToList(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 包含左边界 + /// + [Fact] + public async Task TestBetween_2() { + //常量 + var name = "TestBetween_2"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M,Boundary.Left ).ToList(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 包含右边界 + /// + [Fact] + public async Task TestBetween_3() { + //常量 + var name = "TestBetween_3"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M, Boundary.Right ).ToList(); + + //验证 + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 包含两边 + /// + [Fact] + public async Task TestBetween_4() { + //常量 + var name = "TestBetween_4"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M ).ToList(); + + //验证 + Assert.Equal( 3, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 不包含边界 + /// + [Fact] + public async Task TestBetween_5() { + //常量 + var name = "TestBetween_5"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 11.1M, 13.1M,Boundary.Neither ).ToList(); + + //验证 + Assert.Single( result ); + } + + /// + /// 测试添加范围查询条件 - 最大值为空 + /// + [Fact] + public async Task TestBetween_6() { + //常量 + var name = "TestBetween_6"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, 9.5M, null ).ToList(); + + //验证 + Assert.Equal( 5, result.Count ); + } + + /// + /// 测试添加范围查询条件 - 最小值为空 + /// + [Fact] + public async Task TestBetween_7() { + //常量 + var name = "TestBetween_7"; + var price = 10.1M; + + //添加实体列表 + await AddProductsAsync(); + var products = ProductFakeService.GetProducts( 5 ); + products.ForEach( product => { + product.Init(); + product.Name = name; + product.Price = price++; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查询 + var result = _repository.Find().Where( t => t.Name == name ).Between( t => t.Price, null, 15.5M ).ToList(); + + //验证 + Assert.Equal( 5, result.Count ); + } + + #endregion + } +} diff --git a/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/ProductRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/ProductRepositoryTest.cs new file mode 100644 index 000000000..89cf6120a --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/ProductRepositoryTest.cs @@ -0,0 +1,1408 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Util.Exceptions; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; +using Xunit.Abstractions; +using Xunit.DependencyInjection; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 产品仓储测试 + /// 说明: + /// 1. 测试Guid类型标识 + /// 2. 测试仓储基础方法 + /// 3. 测试扩展属性 + /// + public partial class ProductRepositoryTest : TestBase { + + #region 测试初始化 + + /// + /// 日志消息 + /// + private readonly ITestOutputHelper _log; + /// + /// 仓储 + /// + private readonly IProductRepository _repository; + + /// + /// 测试初始化 + /// + public ProductRepositoryTest( ITestOutputHelperAccessor accessor, IProductRepository repository, ITestUnitOfWork unitOfWork ) :base(unitOfWork) { + _log = accessor.Output; + _repository = repository; + } + + #endregion + + #region 辅助方法 + + /// + /// 添加产品 + /// + private async Task AddProductAsync( string name = null ) { + var entity = ProductFakeService.GetProduct(); + entity.Init(); + if( name != null ) + entity.Name = name; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + return entity; + } + + /// + /// 添加实体列表 + /// + /// 添加数量 + /// 产品名称 + private async Task> AddProductsAsync( int count = 2, string name = null ) { + var products = ProductFakeService.GetProducts( count ); + products.ForEach( product => { + product.Init(); + if( name != null ) + product.Name = name; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + return products.OrderBy( t => t.Id ).ToList(); + } + + /// + /// 打印更改跟踪调试信息 + /// + private void WriteTrackerDebug() { + _log.WriteLine( UnitOfWork.GetChangeTrackerDebugView() ); + } + + #endregion + + #region FindById + + /// + /// 测试通过标识查找实体 - 跟踪实体 + /// + [Fact] + public async Task TestFindById_Tracking_1() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 跟踪实体 - 字符串标识 + /// + [Fact] + public async Task TestFindById_Tracking_2() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id.ToString() ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 不跟踪实体 + /// + [Fact] + public async Task TestFindById_NoTracking() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.NoTracking().FindById( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region FindByIdAsync + + /// + /// 测试通过标识查找实体 - 跟踪 - 异步 + /// + [Fact] + public async Task TestFindByIdAsync_Tracking_1() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 跟踪 - 异步 - 字符串标识 + /// + [Fact] + public async Task TestFindByIdAsync_Tracking_2() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id.ToString() ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + /// + /// 测试通过标识查找实体 - 不跟踪 - 异步 + /// + [Fact] + public async Task TestFindByIdAsync_NoTracking() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.NoTracking().FindByIdAsync( entity.Id ); + WriteTrackerDebug(); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region FindByIds + + /// + /// 测试通过标识列表查找实体列表 - 传入逗号分隔的标识列表 + /// + [Fact] + public async Task TestFindByIds_1() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = _repository.FindByIds( products[0].Id, products[1].Id, products[2].Id ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入集合 + /// + [Fact] + public async Task TestFindByIds_2() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = _repository.FindByIds( products.Select( t => t.Id ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入标识列表字符串 + /// + [Fact] + public async Task TestFindByIds_3() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = _repository.FindByIds( products.Select( t => t.Id ).Join() ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + #endregion + + #region FindByIdsAsync + + /// + /// 测试通过标识列表查找实体列表 - 传入逗号分隔的标识列表 - 异步 + /// + [Fact] + public async Task TestFindByIdsAsync_1() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = ( await _repository.FindByIdsAsync( products[0].Id, products[1].Id, products[2].Id ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入集合 - 异步 + /// + [Fact] + public async Task TestFindByIdsAsync_2() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = ( await _repository.FindByIdsAsync( products.Select( t => t.Id ) ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + /// + /// 测试通过标识列表查找实体列表 - 传入标识列表字符串 - 异步 + /// + [Fact] + public async Task TestFindByIdsAsync_3() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).OrderBy( t => t.Code ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = ( await _repository.FindByIdsAsync( products.Select( t => t.Id ).Join() ) ).OrderBy( t => t.Code ).ToList(); + Assert.Equal( products[0].Description, result[0].Description ); + Assert.Equal( products[1].Description, result[1].Description ); + Assert.Equal( products[2].Description, result[2].Description ); + } + + #endregion + + #region Single + + /// + /// 测试查找单个实体 + /// + [Fact] + public async Task TestSingle_1() { + //常量 + var name = "TestSingle"; + + //添加实体 + var entity = await AddProductAsync( name ); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.Single( t => t.Name == name ); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region SingleAsync + + /// + /// 测试查找单个实体 - 异步 + /// + [Fact] + public async Task TestSingleAsync() { + //常量 + var name = "TestSingleAsync"; + + //添加实体 + var entity = await AddProductAsync( name ); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.SingleAsync( t => t.Name == name ); + Assert.Equal( entity.Code, result.Code ); + } + + #endregion + + #region Exists + + /// + /// 测试判断是否存在 + /// + [Fact] + public async Task TestExists() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + Assert.True( _repository.Exists( t => t.Id == entity.Id ) ); + } + + #endregion + + #region ExistsAsync + + /// + /// 测试判断是否存在 - 异步 + /// + [Fact] + public async Task TestExistsAsync() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //验证 + Assert.True( await _repository.ExistsAsync( t => t.Id == entity.Id ) ); + } + + #endregion + + #region Count + + /// + /// 测试查找数量 + /// + [Fact] + public async Task TestCount() { + //常量 + var name = "TestCount"; + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ); + products.ForEach( product => { + product.Init(); + product.Name = name; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = _repository.Count( t => t.Name == name ); + Assert.Equal( count, result ); + } + + #endregion + + #region CountAsync + + /// + /// 测试查找数量 - 异步 + /// + [Fact] + public async Task TestCountAsync() { + //常量 + var name = "TestCountAsync"; + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ); + products.ForEach( product => { + product.Init(); + product.Name = name; + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + + //验证 + var result = await _repository.CountAsync( t => t.Name == name ); + Assert.Equal( count, result ); + } + + #endregion + + #region Update + + /// + /// 测试更新方法1 - 查找并修改实体,直接提交工作单元 + /// + [Fact] + public async Task TestUpdate_1() { + //常量 + var code = "TestUpdate_1_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //查找实体并修改 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + + //直接提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试更新并发 - 使用更新方法1 + /// 更新方法1的缺陷: 通过赋值的方式更新查找实体的版本号,无法触发乐观锁 + /// + [Fact] + public async Task TestUpdate_1_Concurrency() { + //常量 + var code = "TestUpdate_1_Concurrency_Code"; + var description = "TestUpdate_1_Concurrency_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,查找实体,并进行赋值 + var result = _repository.FindById( entity.Id ); + result.Code = product.Code; + result.Description = product.Description; + result.Version = product.Version; + //提交工作单元,由于版本号变化,预期应触发乐观锁,但实际未触发 + await UnitOfWork.CommitAsync(); + + //结果:用户1覆盖了用户2设置的Description属性 + Assert.Equal( code, result.Code ); + Assert.NotEqual( description, result.Description ); + } + + /// + /// 测试更新方法2 - 创建未跟踪实体,调用dbcontext.Update方法 + /// + [Fact] + public async Task TestUpdate_2() { + //常量 + var code = "TestUpdate_2_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + //调用dbcontext.Update方法更新实体 + _repository.UpdateProduct( product ); + //提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试更新方法2的陷阱 + /// 如果在创建未跟踪实体并调用dbcontext.Update方法之前,从dbcontext中已查找出该实体,将发生异常 + /// + [Fact] + public async Task TestUpdate_2_InvalidOperation() { + //常量 + var code = "TestUpdate_2_InvalidOperation_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //模拟更新之前无意中已查找出该实体 + await _repository.FindByIdAsync( entity.Id ); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + + //由于实体已被跟踪,调用dbcontext.Update方法更新实体将触发异常 + Assert.Throws( () => { + _repository.UpdateProduct( product ); + } ); + } + + /// + /// 测试更新并发 - 使用更新方法2 + /// + [Fact] + public async Task TestUpdate_2_Concurrency() { + //常量 + var code = "TestUpdate_2_Concurrency_Code"; + var description = "TestUpdate_2_Concurrency_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,创建一个新的实体,并赋值 + var result = product.MapTo(); + + //调用dbcontext.Update方法更新实体 + _repository.UpdateProduct( result ); + + //提交工作单元,由于版本号变化,触发乐观锁 + await Assert.ThrowsAsync( async () => { + await UnitOfWork.CommitAsync(); + } ); + } + + /// + /// 测试Util Update更新方法 - 查找并修改实体,提交工作单元 + /// + [Fact] + public async Task TestUpdate_3_1() { + //常量 + var code = "TestUpdate_3_1_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //查找实体并修改 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + + //调用Update方法 + await _repository.UpdateAsync( product ); + + //提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试Util Update更新方法 - 创建未跟踪实体,调用Update方法 + /// + [Fact] + public async Task TestUpdate_3_2() { + //常量 + var code = "TestUpdate_3_2_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + + //调用Update方法更新实体 + await _repository.UpdateAsync( product ); + + //提交工作单元 + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试Util Update更新方法 - 允许在创建未跟踪实体并调用Update方法之前查找实体,不会发生异常 + /// + [Fact] + public async Task TestUpdate_3_3() { + //常量 + var code = "TestUpdate_3_3_InvalidOperation_Code"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //模拟更新之前无意中已查找出该实体 + await _repository.FindByIdAsync( entity.Id ); + + //创建未跟踪实体 + var product = entity.MapTo(); + product.Code = code; + + //调用Update方法更新实体 + await _repository.UpdateAsync( product ); + + //提交工作单元 + _log.WriteLine( UnitOfWork.GetChangeTrackerDebugView() ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id ); + Assert.Equal( code, result.Code ); + } + + /// + /// 测试更新并发 - 使用Util Update更新方法 + /// 通过赋值的方式更新查找实体的版本号,触发乐观锁 + /// + [Fact] + public async Task TestUpdate_3_Concurrency_1() { + //常量 + var code = "TestUpdate_3_Concurrency_1_Code"; + var description = "TestUpdate_3_Concurrency_1_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,查找实体,并进行赋值 + var result = await _repository.FindByIdAsync( entity.Id ); + result.Code = product.Code; + result.Description = product.Description; + result.Version = product.Version; + + //调用Update方法更新实体时触发乐观锁 + await Assert.ThrowsAsync( async () => { + await _repository.UpdateAsync( result ); + } ); + } + + /// + /// 测试更新并发 - 使用Util Update更新方法 + /// 更新未跟踪实体触发乐观锁 + /// + [Fact] + public async Task TestUpdate_3_Concurrency_2() { + //常量 + var code = "TestUpdate_3_Concurrency_2_Code"; + var description = "TestUpdate_3_Concurrency_2_Description"; + + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1打开表单并修改,但尚未提交,这将保存旧实体 + var product = await _repository.FindByIdAsync( entity.Id ); + product.Code = code; + UnitOfWork.ClearCache(); + + //此处模拟用户2更新该实体,这将导致版本号变化 + var product2 = await _repository.FindByIdAsync( entity.Id ); + product2.Description = description; + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //此处模拟用户1提交表单,创建一个新的实体,并赋值 + var result = product.MapTo(); + + //调用Update方法更新实体 + await _repository.UpdateAsync( result ); + + //提交工作单元,由于版本号变化,触发乐观锁 + await Assert.ThrowsAsync( async () => { + await UnitOfWork.CommitAsync(); + } ); + } + + /// + /// 测试更新集合 + /// + [Fact] + public async Task TestUpdate_4() { + //常量 + var count = 3; + var code = "TestUpdate_4_Code"; + + //添加实体列表 + var entities = ProductFakeService.GetProducts( count ); + entities.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( entities ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //查找实体,赋值,调用Update方法,提交工作单元 + var products = await _repository.FindByIdsAsync( entities.Select( t => t.Id ) ); + products.ForEach( product => product.Code = code ); + await _repository.UpdateAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdsAsync( entities.Select( t => t.Id ) ); + Assert.Equal( code, result[0].Code ); + Assert.Equal( code, result[1].Code ); + Assert.Equal( code, result[2].Code ); + } + + #endregion + + #region Remove + + /// + /// 测试通过标识移除实体 - 逻辑删除 + /// + [Fact] + public async Task TestRemoveAsync_Id_1() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试通过标识移除实体 - 逻辑删除 - 使用工作单元禁用逻辑删除过滤器 + /// + [Fact] + public async Task TestRemoveAsync_Id_2() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //使用工作单元禁用逻辑删除过滤器,可以查出 + using( UnitOfWork.DisableDeleteFilter() ) { + var product = await _repository.FindByIdAsync( entity.Id ); + Assert.True( product.IsDeleted ); + Assert.Equal( entity.Code, product.Code ); + } + UnitOfWork.ClearCache(); + + //逻辑删除过滤器已启用,无法查出 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试通过标识移除实体 - 逻辑删除 - 使用仓储禁用逻辑删除过滤器 + /// + [Fact] + public async Task TestRemoveAsync_Id_3() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity.Id ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //使用仓储禁用逻辑删除过滤器,可以查出 + using( _repository.DisableDeleteFilter() ) { + var product = await _repository.FindByIdAsync( entity.Id ); + Assert.True( product.IsDeleted ); + Assert.Equal( entity.Code, product.Code ); + } + UnitOfWork.ClearCache(); + + //逻辑删除过滤器已启用,无法查出 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试移除实体 - 逻辑删除 + /// + [Fact] + public async Task TestRemoveAsync_Entity() { + //添加实体 + var entity = await AddProductAsync(); + UnitOfWork.ClearCache(); + + //移除实体 + await _repository.RemoveAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Null( result ); + } + + /// + /// 测试移除实体集合 - 逻辑删除 - 通过标识列表 + /// + [Fact] + public async Task TestRemoveAsync_Entities_1() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //移除实体集合 + var ids = products.Select( t => t.Id ).ToList(); + await _repository.RemoveAsync( ids ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + using( _repository.DisableDeleteFilter() ) { + var result = await _repository.FindByIdsAsync( ids ); + Assert.Equal( count, result.Count ); + result.ForEach( t => Assert.True( t.IsDeleted ) ); + } + } + + /// + /// 测试移除实体集合 - 逻辑删除 - 通过实体列表 + /// + [Fact] + public async Task TestRemoveAsync_Entities_2() { + //常量 + var count = 3; + + //添加实体列表 + var products = ProductFakeService.GetProducts( count ).ToList(); + products.ForEach( product => { + product.Init(); + } ); + await _repository.AddAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //移除实体集合 + await _repository.RemoveAsync( products ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + using( _repository.DisableDeleteFilter() ) { + var result = await _repository.FindByIdsAsync( products.Select( t => t.Id ) ); + Assert.Equal( count, result.Count ); + result.ForEach( t => Assert.True( t.IsDeleted ) ); + } + } + + #endregion + + #region Audit + + /// + /// 测试设置创建审计属性 + /// + [Fact] + public async Task TestCreationAudit() { + var entity = ProductFakeService.GetProduct(); + entity.Init(); + await _repository.AddAsync( entity ); + Assert.NotEqual( TestSession.TestUserId, entity.CreatorId ); + await UnitOfWork.CommitAsync(); + Assert.Equal( TestSession.TestUserId, entity.CreatorId ); + Assert.Equal( TestSession.TestUserId, entity.LastModifierId ); + } + + /// + /// 测试设置修改审计属性 + /// + [Fact] + public async Task TestModificationAudit() { + //工作单元 + var unitOfWork = (SqliteUnitOfWork)UnitOfWork; + + //常量 + var code = "TestModificationAudit"; + + //添加实体 + var entity = await AddProductAsync(); + unitOfWork.ClearCache(); + + //修改 + var product = entity.MapTo(); + product.Code = code; + await _repository.UpdateAsync( product ); + unitOfWork.SetSession( new TestSession { UserId = TestSession.TestUserId2.ToString() } ); + + //验证修改前审计属性 + Assert.Equal( TestSession.TestUserId, product.CreatorId ); + Assert.Equal( TestSession.TestUserId, product.LastModifierId ); + + //提交 + await unitOfWork.CommitAsync(); + + //验证修改后审计属性 + Assert.Equal( TestSession.TestUserId, product.CreatorId ); + Assert.Equal( TestSession.TestUserId2, product.LastModifierId ); + } + + #endregion + + #region ExtraProperties + + /// + /// 测试扩展属性 - 添加简单扩展属性 + /// + [Fact] + public async Task TestExtraProperties_1() { + //常量 + var value = "TestExtraProperties_1"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = value; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( value, result.TestProperty1 ); + } + + /// + /// 测试扩展属性 - 修改简单扩展属性 + /// + [Fact] + public async Task TestExtraProperties_2() { + //常量 + var oldValue = "TestExtraProperties_2_1"; + var newValue = "TestExtraProperties_2_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = oldValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( oldValue, oldEntity.TestProperty1 ); + + //修改实体 + oldEntity.TestProperty1 = newValue; + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newValue, result.TestProperty1 ); + } + + /// + /// 测试扩展属性 - 添加对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_3() { + //常量 + var code = 2; + var name = "TestExtraProperties_3"; + var enumValue = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = name; + entity.TestProperty2.EnumValue = enumValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = _repository.FindById( entity.Id ); + Assert.Equal( code, result.TestProperty2.Code ); + Assert.Equal( name, result.TestProperty2.Name ); + Assert.Equal( enumValue, result.TestProperty2.EnumValue ); + } + + /// + /// 测试扩展属性 - 修改对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_4() { + //常量 + var code = 2; + var oldName = "TestExtraProperties_4_1"; + var newName = "TestExtraProperties_4_2"; + var oldEnumValue = ProductEnum.Hello; + var newEnumValue = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = oldName; + entity.TestProperty2.EnumValue = oldEnumValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, oldEntity.TestProperty2.Code ); + Assert.Equal( oldName, oldEntity.TestProperty2.Name ); + Assert.Equal( oldEnumValue, oldEntity.TestProperty2.EnumValue ); + + //修改实体 + oldEntity.TestProperty2.Name = newName; + oldEntity.TestProperty2.EnumValue = newEnumValue; + WriteTrackerDebug(); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newName, result.TestProperty2.Name ); + Assert.Equal( newEnumValue, result.TestProperty2.EnumValue ); + } + + /// + /// 测试扩展属性 - 添加对象集合扩展属性 + /// + [Fact] + public async Task TestExtraProperties_5() { + //常量 + var code = 1; + var name1 = "TestExtraProperties_5_1"; + var name2 = "TestExtraProperties_5_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperties.Add( new ProductItem { Code = code, Name = name1 } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = name2 } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( 2, result.TestProperties.Count ); + Assert.Equal( code, result.TestProperties[0].Code ); + Assert.Equal( name1, result.TestProperties[0].Name ); + Assert.Equal( code, result.TestProperties[1].Code ); + Assert.Equal( name2, result.TestProperties[1].Name ); + } + + /// + /// 测试扩展属性 - 修改对象集合扩展属性 + /// + [Fact] + public async Task TestExtraProperties_6() { + //常量 + var code = 1; + var oldName = "TestExtraProperties_6_1"; + var newName = "TestExtraProperties_6_2"; + var name3 = "TestExtraProperties_6_3"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( 2, oldEntity.TestProperties.Count ); + + //修改实体 + oldEntity.TestProperties[0].Name = newName; + oldEntity.TestProperties[1].Name = newName; + oldEntity.TestProperties.Add( new ProductItem { Code = code, Name = name3 } ); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( 3, result.TestProperties.Count ); + Assert.Equal( code, result.TestProperties[0].Code ); + Assert.Equal( newName, result.TestProperties[0].Name ); + Assert.Equal( code, result.TestProperties[1].Code ); + Assert.Equal( newName, result.TestProperties[1].Name ); + Assert.Equal( code, result.TestProperties[2].Code ); + Assert.Equal( name3, result.TestProperties[2].Name ); + } + + /// + /// 测试扩展属性 - 添加多个扩展属性 + /// + [Fact] + public async Task TestExtraProperties_7() { + //常量 + var code = 2; + var name = "TestExtraProperties_7"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = name; + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = name; + entity.TestProperties.Add( new ProductItem { Code = code, Name = name } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = name } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( name, result.TestProperty1 ); + Assert.Equal( code, result.TestProperty2.Code ); + Assert.Equal( name, result.TestProperty2.Name ); + Assert.Equal( code, result.TestProperties[0].Code ); + Assert.Equal( name, result.TestProperties[0].Name ); + Assert.Equal( code, result.TestProperties[1].Code ); + Assert.Equal( name, result.TestProperties[1].Name ); + } + + /// + /// 测试扩展属性 - 修改多个扩展属性 + /// + [Fact] + public async Task TestExtraProperties_8() { + //常量 + var code = 2; + var oldName = "TestExtraProperties_8_1"; + var newName = "TestExtraProperties_8_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty1 = oldName; + entity.TestProperty2.Code = code; + entity.TestProperty2.Name = oldName; + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + entity.TestProperties.Add( new ProductItem { Code = code, Name = oldName } ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( oldName, oldEntity.TestProperty1 ); + Assert.Equal( code, oldEntity.TestProperty2.Code ); + Assert.Equal( oldName, oldEntity.TestProperty2.Name ); + Assert.Equal( 2, oldEntity.TestProperties.Count ); + + //修改实体 + oldEntity.TestProperty1 = newName; + oldEntity.TestProperty2.Name = newName; + oldEntity.TestProperties[0].Name = newName; + oldEntity.TestProperties[1].Name = newName; + oldEntity.TestProperties.Add( new ProductItem{ Code = code, Name = newName } ); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newName, result.TestProperty1 ); + Assert.Equal( newName, result.TestProperty2.Name ); + Assert.Equal( 3, result.TestProperties.Count ); + Assert.Equal( newName, result.TestProperties[0].Name ); + Assert.Equal( newName, result.TestProperties[1].Name ); + Assert.Equal( newName, result.TestProperties[2].Name ); + } + + /// + /// 测试扩展属性 - 添加枚举扩展属性 + /// + [Fact] + public async Task TestExtraProperties_9() { + //常量 + ProductEnum value = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty3 = value; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( value, result.TestProperty3 ); + } + + /// + /// 测试扩展属性 - 修改枚举扩展属性 + /// + [Fact] + public async Task TestExtraProperties_10() { + //常量 + ProductEnum oldValue = ProductEnum.Hello; + ProductEnum newValue = ProductEnum.World; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty3 = oldValue; + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( oldValue, oldEntity.TestProperty3 ); + + //修改实体 + oldEntity.TestProperty3 = newValue; + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newValue, result.TestProperty3 ); + } + + /// + /// 测试扩展属性 - 添加不可变对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_11() { + //常量 + var code = 2; + var name = "TestExtraProperties_11"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty4 = new ProductItem2( code, name ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, result.TestProperty4.Code ); + Assert.Equal( name, result.TestProperty4.Name ); + } + + /// + /// 测试扩展属性 - 修改对象扩展属性 + /// + [Fact] + public async Task TestExtraProperties_12() { + //常量 + var code = 2; + var oldName = "TestExtraProperties_12_1"; + var newName = "TestExtraProperties_12_2"; + + //添加实体 + var entity = new Product(); + entity.Init(); + entity.TestProperty4 = new ProductItem2( code, oldName ); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var oldEntity = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( code, oldEntity.TestProperty4.Code ); + Assert.Equal( oldName, oldEntity.TestProperty4.Name ); + + //修改实体 + oldEntity.TestProperty4 = new ProductItem2( code, newName ); + WriteTrackerDebug(); + await _repository.UpdateAsync( oldEntity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + var result = await _repository.FindByIdAsync( entity.Id ); + Assert.Equal( newName, result.TestProperty4.Name ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/TagRepositoryTest.cs b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/TagRepositoryTest.cs new file mode 100644 index 000000000..cec65b128 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Tests/TagRepositoryTest.cs @@ -0,0 +1,41 @@ +using System.Threading.Tasks; +using Util.Tests.Fakes; +using Util.Tests.Infrastructure; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; +using Xunit; + +namespace Util.Data.EntityFrameworkCore.Tests { + /// + /// 标签仓储测试 + /// + public class TagRepositoryTest : TestBase { + /// + /// 仓储 + /// + private readonly ITagRepository _repository; + + /// + /// 测试初始化 + /// + public TagRepositoryTest( ITagRepository repository, ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + _repository = repository; + } + + /// + /// 测试添加实体 + /// + [Fact] + public async Task TestAddAsync() { + //添加实体 + var entity = TagFakeService.GetTag(); + entity.Init(); + await _repository.AddAsync( entity ); + await UnitOfWork.CommitAsync(); + UnitOfWork.ClearCache(); + + //验证 + Assert.True( await _repository.ExistsAsync( t => t.Id == entity.Id ) ); + } + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration.csproj b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration.csproj new file mode 100644 index 000000000..c2085a905 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration.csproj @@ -0,0 +1,39 @@ + + + + $(NetTargetFramework) + false + Util.Data.EntityFrameworkCore + Util.Data.EntityFrameworkCore.Startup + + + + + Always + + + Always + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/appsettings.Development.json b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..4c064eeb9 --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Trace" + } + }, + "ConnectionStrings": { + "connection": "Data Source=Util.Data.EntityFrameworkCore.Test.db" + } +} \ No newline at end of file diff --git a/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/appsettings.json b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/appsettings.json new file mode 100644 index 000000000..cb558111f --- /dev/null +++ b/test/Util.Data.EntityFrameworkCore.Sqlite.Tests.Integration/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + }, + "ConnectionStrings": { + "connection": "Filename=Util.Data.EntityFrameworkCore.Test.db" + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Clauses/EndClauseTest.cs b/test/Util.Data.Sql.Tests/Builders/Clauses/EndClauseTest.cs new file mode 100644 index 000000000..f4ea3dc71 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Clauses/EndClauseTest.cs @@ -0,0 +1,188 @@ +using System.Text; +using Util.Data.Queries; +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Clauses; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Clauses; + +/// +/// 结束子句测试 +/// +public class EndClauseTest { + + #region 测试初始化 + + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + /// + /// 结束子句 + /// + private readonly EndClause _clause; + + /// + /// 测试初始化 + /// + public EndClauseTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + _clause = CreateEndClause( TestDialect.Instance ); + } + + /// + /// 创建结束子句 + /// + private EndClause CreateEndClause( IDialect dialect ) { + return new EndClause( new TestSqlBuilder( dialect, parameterManager:_parameterManager ) ); + } + + /// + /// 获取Sql语句 + /// + private string GetSql( IEndClause clause = null ) { + clause ??= _clause; + var builder = new StringBuilder(); + clause.AppendTo( builder ); + return builder.ToString(); + } + + #endregion + + #region Take + + /// + /// 测试设置获取行数 + /// + [Fact] + public void TestTake() { + _clause.Take( 20 ); + Assert.Equal( "Limit @_p_0 OFFSET @_p_1", GetSql() ); + Assert.Equal( 20, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( 0, _parameterManager.GetValue( "@_p_1" ) ); + } + + #endregion + + #region Skip + + /// + /// 测试设置跳过行数 + /// + [Fact] + public void TestSkip() { + _clause.Skip( 80 ); + _clause.Take( 40 ); + Assert.Equal( "Limit @_p_1 OFFSET @_p_0", GetSql() ); + Assert.Equal( 80, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( 40, _parameterManager.GetValue( "@_p_1" ) ); + } + + #endregion + + #region Page + + /// + /// 测试设置分页 + /// + [Fact] + public void TestPage() { + var page = new Pager( 3, 40 ); + _clause.Page( page ); + Assert.Equal( "Limit @_p_1 OFFSET @_p_0", GetSql() ); + Assert.Equal( 80, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( 40, _parameterManager.GetValue( "@_p_1" ) ); + } + + #endregion + + #region AppendSql + + /// + /// 测试添加到结束位置 - 替换[]为特定转义字符 + /// + [Fact] + public void TestAppendSql_1() { + var clause = CreateEndClause( new TestDialect2() ); + clause.AppendSql( "[a].[b]", false ); + Assert.Equal( "$a&.$b&", GetSql( clause ) ); + } + + /// + /// 测试添加到结束位置 - 原样添加 + /// + [Fact] + public void TestAppendSql_2() { + var clause = CreateEndClause( new TestDialect2() ); + clause.AppendSql( "[a].[b]", true ); + Assert.Equal( "[a].[b]", GetSql( clause ) ); + } + + /// + /// 测试添加到结束位置 - 多次调用 + /// + [Fact] + public void TestAppendSql_3() { + var clause = CreateEndClause( new TestDialect2() ); + clause.AppendSql( "[a].", true ); + clause.AppendSql( "[b]", true ); + Assert.Equal( "[a].[b]", GetSql( clause ) ); + } + + #endregion + + #region ClearPage + + /// + /// 测试清理分页 + /// + [Fact] + public void TestClearPage() { + var page = new Pager( 3, 40 ); + _clause.Page( page ); + _clause.ClearPage(); + Assert.Empty( GetSql() ); + } + + #endregion + + #region Clear + + /// + /// 测试清理 + /// + [Fact] + public void TestClear() { + var page = new Pager( 3, 40 ); + _clause.Page( page ); + _clause.AppendSql( "[a].[b]", false ); + _clause.Clear(); + Assert.Empty( GetSql() ); + } + + #endregion + + #region Clone + + /// + /// 测试复制结束子句 + /// + [Fact] + public void TestClone() { + //设置分页 + var page = new Pager( 3, 40 ); + _clause.Page( page ); + + //复制并验证 + var clone = _clause.Clone( new TestSqlBuilder() ); + Assert.Equal( "Limit @_p_1 OFFSET @_p_0", GetSql( clone ) ); + + //修改原始子句,复制子句不应受影响 + _clause.Clear(); + Assert.Equal( "Limit @_p_1 OFFSET @_p_0", GetSql( clone ) ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Clauses/FromClauseTest.cs b/test/Util.Data.Sql.Tests/Builders/Clauses/FromClauseTest.cs new file mode 100644 index 000000000..1419e85c2 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Clauses/FromClauseTest.cs @@ -0,0 +1,187 @@ +using System.Text; +using Util.Data.Sql.Builders.Clauses; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Clauses; + +/// +/// From子句测试 +/// +public class FromClauseTest { + + #region 测试初始化 + + /// + /// From子句 + /// + private readonly FromClause _clause; + + /// + /// 测试初始化 + /// + public FromClauseTest() { + _clause = new FromClause( new TestSqlBuilder() ); + } + + /// + /// 获取Sql语句 + /// + private string GetSql( IFromClause clause = null ) { + clause ??= _clause; + var builder = new StringBuilder(); + clause.AppendTo( builder ); + return builder.ToString(); + } + + #endregion + + #region 默认输出 + + /// + /// 默认输出空 + /// + [Fact] + public void TestDefault() { + Assert.Empty( GetSql() ); + } + + #endregion + + #region From + + /// + /// 测试设置简单表 + /// + [Fact] + public void TestFrom_1() { + _clause.From( "a" ); + Assert.Equal( "From [a]", GetSql() ); + } + + /// + /// 测试设置带架构的表 + /// + [Fact] + public void TestFrom_2() { + _clause.From( "a.b" ); + Assert.Equal( "From [a].[b]", GetSql() ); + } + + /// + /// 测试设置带表别名的表 + /// + [Fact] + public void TestFrom_3() { + _clause.From( "a as b" ); + Assert.Equal( "From [a] [b]", GetSql() ); + } + + /// + /// 测试设置带架构和表别名的表 + /// + [Fact] + public void TestFrom_4() { + _clause.From( "a.b as c" ); + Assert.Equal( "From [a].[b] [c]", GetSql() ); + } + + /// + /// 测试设置子查询表 + /// + [Fact] + public void TestFrom_5() { + var result = new StringBuilder(); + result.AppendLine( "From (Select [a] " ); + result.Append( "From [b]) As [c]" ); + + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + _clause.From( builder, "c" ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试设置子查询表 - 内嵌表达式 + /// + [Fact] + public void TestFrom_6() { + var result = new StringBuilder(); + result.AppendLine( "From (Select [a] " ); + result.Append( "From [b]) As [c]" ); + + _clause.From( t => t.Select( "a" ).From( "b" ), "c" ); + Assert.Equal( result.ToString(), GetSql() ); + } + + #endregion + + #region AppendSql + + /// + /// 添加到from子句 - 替换[]为特定转义字符 + /// + [Fact] + public void TestAppendSql_1() { + var clause = new FromClause( new TestSqlBuilder( new TestDialect2() ) ); + clause.AppendSql( "[a].[b], c.[D]", false ); + Assert.Equal( "From $a&.$b&, c.$D&", GetSql( clause ) ); + } + + /// + /// 添加到from子句 - 原样添加 + /// + [Fact] + public void TestAppendSql_2() { + var clause = new FromClause( new TestSqlBuilder( new TestDialect2() ) ); + clause.AppendSql( "[a].[b], c.[D]", true ); + Assert.Equal( "From [a].[b], c.[D]", GetSql( clause ) ); + } + + /// + /// 添加到from子句 - 多次调用 + /// + [Fact] + public void TestAppendSql_3() { + var clause = new FromClause( new TestSqlBuilder( new TestDialect2() ) ); + clause.AppendSql( "[a].", true ); + clause.AppendSql( "[b]", true ); + Assert.Equal( "From [a].[b]", GetSql( clause ) ); + } + + #endregion + + #region Clear + + /// + /// 测试清理 + /// + [Fact] + public void TestClear() { + _clause.From( "a" ); + _clause.Clear(); + Assert.Empty( GetSql() ); + } + + #endregion + + #region Clone + + /// + /// 测试复制From子句 + /// + [Fact] + public void TestClone() { + //设置表 + _clause.From( "a" ); + + //复制并验证 + var clone = _clause.Clone( new TestSqlBuilder() ); + Assert.Equal( "From [a]", GetSql( clone ) ); + + //修改原始子句,复制子句不应受影响 + _clause.Clear(); + Assert.Equal( "From [a]", GetSql( clone ) ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Clauses/GroupByClauseTest.cs b/test/Util.Data.Sql.Tests/Builders/Clauses/GroupByClauseTest.cs new file mode 100644 index 000000000..e15a38c35 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Clauses/GroupByClauseTest.cs @@ -0,0 +1,267 @@ +using System.Text; +using Util.Data.Sql.Builders.Clauses; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Clauses; + +/// +/// Group By子句测试 +/// +public class GroupByClauseTest { + + #region 测试初始化 + + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + /// + /// 分组子句 + /// + private readonly GroupByClause _clause; + + /// + /// 测试初始化 + /// + public GroupByClauseTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + _clause = new GroupByClause( new TestSqlBuilder(parameterManager: _parameterManager ) ); + } + + /// + /// 获取Sql语句 + /// + private string GetSql( IGroupByClause clause = null ) { + clause ??= _clause; + var builder = new StringBuilder(); + clause.AppendTo( builder ); + return builder.ToString(); + } + + #endregion + + #region 默认输出 + + /// + /// 默认输出 + /// + [Fact] + public void TestDefault() { + Assert.Empty( GetSql() ); + } + + #endregion + + #region GroupBy + + /// + /// 测试添加分组 - 传入空值 + /// + [Fact] + public void TestGroupBy_1() { + _clause.GroupBy( "" ); + Assert.Empty( GetSql() ); + } + + /// + /// 测试添加分组 - 1个列 + /// + [Fact] + public void TestGroupBy_2() { + _clause.GroupBy( "a.B" ); + Assert.Equal( "Group By [a].[B]", GetSql() ); + } + + /// + /// 测试添加分组 - 2个列 + /// + [Fact] + public void TestGroupBy_3() { + _clause.GroupBy( "a.B,c.D" ); + Assert.Equal( "Group By [a].[B],[c].[D]", GetSql() ); + } + + /// + /// 测试添加分组 - 多次调用GroupBy方法 + /// + [Fact] + public void TestGroupBy_4() { + _clause.GroupBy( "a.B" ); + _clause.GroupBy( "c.D" ); + Assert.Equal( "Group By [a].[B],[c].[D]", GetSql() ); + } + + #endregion + + #region Having + + /// + /// 测试添加分组条件 - 未设置GroupBy输出空 + /// + [Fact] + public void TestHaving_1() { + _clause.Having( "a","b" ); + Assert.Empty( GetSql() ); + } + + /// + /// 测试添加分组条件 - 1个条件 + /// + [Fact] + public void TestHaving_2() { + var result = new StringBuilder(); + result.Append( "Group By [a].[B] " ); + result.Append( "Having sum(a)>@_p_0" ); + + _clause.GroupBy( "a.B" ); + _clause.Having( "sum(a)", "1",Operator.Greater ); + Assert.Equal( result.ToString(), GetSql() ); + Assert.Equal( "1", _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试添加分组条件 - 2个条件 + /// + [Fact] + public void TestHaving_3() { + var result = new StringBuilder(); + result.Append( "Group By [a].[B] " ); + result.Append( "Having sum(a)>@_p_0 And max(b)<@_p_1" ); + + _clause.GroupBy( "a.B" ); + _clause.Having( "sum(a)", "1", Operator.Greater ); + _clause.Having( "max(b)", 2, Operator.Less ); + Assert.Equal( result.ToString(), GetSql() ); + Assert.Equal( "1", _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( 2, _parameterManager.GetValue( "@_p_1" ) ); + } + + #endregion + + #region AppendGroupBy + + /// + /// 测试添加到Group By子句 - 替换[] + /// + [Fact] + public void TestAppendGroupBy_1() { + var result = new StringBuilder(); + result.Append( "Group By $a&.$id&" ); + + var clause = new GroupByClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ) ) ); + clause.AppendGroupBy( "[a].[id]", false ); + Assert.Equal( result.ToString(), GetSql( clause ) ); + } + + /// + /// 测试添加到Group By子句 - 原样添加 + /// + [Fact] + public void TestAppendGroupBy_2() { + var result = new StringBuilder(); + result.Append( "Group By [a].[id]" ); + + var clause = new GroupByClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ) ) ); + clause.AppendGroupBy( "[a].[id]", true ); + Assert.Equal( result.ToString(), GetSql( clause ) ); + } + + /// + /// 测试添加到Group By子句 - 多次调用 + /// + [Fact] + public void TestAppendGroupBy_3() { + var result = new StringBuilder(); + result.Append( "Group By [a].[id]" ); + + var clause = new GroupByClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ) ) ); + clause.AppendGroupBy( "[a].", true ); + clause.AppendGroupBy( "[id]", true ); + Assert.Equal( result.ToString(), GetSql( clause ) ); + } + + #endregion + + #region AppendHaving + + /// + /// 测试添加到Having子句 - 替换[] + /// + [Fact] + public void TestAppendHaving_1() { + var result = new StringBuilder(); + result.Append( "Group By $a1&.$id&,$b1&.$id& " ); + result.Append( "Having Count($a&.$id&)>1 And sum($b&.$id&)=2" ); + + var clause = new GroupByClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ) ) ); + clause.GroupBy( "a1.id" ); + clause.GroupBy( "b1.id" ); + clause.AppendHaving( "Count([a].[id])>1", false ); + clause.AppendHaving( "sum([b].[id])=2", false ); + Assert.Equal( result.ToString(), GetSql( clause ) ); + } + + /// + /// 测试添加到Having子句 - 原样添加 + /// + [Fact] + public void TestAppendHaving_2() { + var result = new StringBuilder(); + result.Append( "Group By $a1&.$id&,$b1&.$id& " ); + result.Append( "Having Count([a].[id])>1 And sum([b].[id])=2" ); + + var clause = new GroupByClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ) ) ); + clause.GroupBy( "a1.id" ); + clause.GroupBy( "b1.id" ); + clause.AppendHaving( "Count([a].[id])>1", true ); + clause.AppendHaving( "sum([b].[id])=2", true ); + Assert.Equal( result.ToString(), GetSql( clause ) ); + } + + #endregion + + #region Clear + + /// + /// 测试清理 + /// + [Fact] + public void TestClear() { + _clause.GroupBy( "a.B" ); + _clause.Having( "sum(a)", "1", Operator.Greater ); + _clause.Clear(); + Assert.Empty( GetSql() ); + } + + #endregion + + #region Clone + + /// + /// 测试复制Group By子句 + /// + [Fact] + public void TestClone() { + //结果 + var result = new StringBuilder(); + result.Append( "Group By [a].[B] " ); + result.Append( "Having sum(a)>@_p_0" ); + + //设置分组 + _clause.GroupBy( "a.B" ); + _clause.Having( "sum(a)", "1", Operator.Greater ); + + //复制并验证 + var clone = _clause.Clone( new TestSqlBuilder() ); + Assert.Equal( result.ToString(), GetSql( clone ) ); + + //修改原始子句,复制子句不应受影响 + _clause.GroupBy( "c.d" ); + _clause.Having( "sum(e)", "2", Operator.Greater ); + Assert.Equal( result.ToString(), GetSql( clone ) ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Clauses/InsertClauseTest.cs b/test/Util.Data.Sql.Tests/Builders/Clauses/InsertClauseTest.cs new file mode 100644 index 000000000..b69124460 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Clauses/InsertClauseTest.cs @@ -0,0 +1,270 @@ +using System.Text; +using Util.Data.Sql.Builders.Clauses; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Clauses; + +/// +/// 插入子句测试 +/// +public class InsertClauseTest { + + #region 测试初始化 + + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + /// + /// Select子句 + /// + private readonly InsertClause _clause; + + /// + /// 测试初始化 + /// + public InsertClauseTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + _clause = new InsertClause( new TestSqlBuilder( parameterManager: _parameterManager ) ); + } + + /// + /// 获取Sql语句 + /// + private string GetSql( IInsertClause clause = null ) { + clause ??= _clause; + var builder = new StringBuilder(); + clause.AppendTo( builder ); + return builder.ToString(); + } + + #endregion + + #region Insert + + /// + /// 测试插入一列 - 未设置值 + /// + [Fact] + public void TestInsert_1() { + var result = new StringBuilder(); + result.Append( "Insert Into [t].[c]([a].[b]) " ); + + _clause.Insert( "a.b", "t.c" ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试插入2列 + /// + [Fact] + public void TestInsert_2() { + var result = new StringBuilder(); + result.AppendLine( "Insert Into [z].[t]([a].[b],[c].[d]) " ); + result.Append( "Values(@_p_0,@_p_1)" ); + + _clause.Insert( "a.b,c.d", "z.t" ); + _clause.Values( 1, "a" ); + Assert.Equal( result.ToString(), GetSql() ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( "a", _parameterManager.GetValue( "@_p_1" ) ); + } + + /// + /// 测试插入2列 - 多次调用Insert + /// + [Fact] + public void TestInsert_3() { + var result = new StringBuilder(); + result.AppendLine( "Insert Into [z].[t]([a].[b],[c].[d]) " ); + result.Append( "Values(@_p_0,@_p_1)" ); + + _clause.Insert( "a.b", "z.t" ); + _clause.Insert( "c.d" ); + _clause.Values( 1, "a" ); + Assert.Equal( result.ToString(), GetSql() ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( "a", _parameterManager.GetValue( "@_p_1" ) ); + } + + #endregion + + #region Values + + /// + /// 测试插入一行 + /// + [Fact] + public void TestValues_1() { + var result = new StringBuilder(); + result.AppendLine( "Insert Into [t].[c]([a].[b]) " ); + result.Append( "Values(@_p_0)" ); + + _clause.Insert( "a.b", "t.c" ); + _clause.Values( 1 ); + Assert.Equal( result.ToString(), GetSql() ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试插入多行 + /// + [Fact] + public void TestValues_2() { + var result = new StringBuilder(); + result.AppendLine( "Insert Into [z].[t]([a].[b],[c].[d]) " ); + result.Append( "Values(@_p_0,@_p_1)," ); + result.Append( "(@_p_2,@_p_3)" ); + + _clause.Insert( "a.b", "z.t" ); + _clause.Insert( "c.d" ); + _clause.Values( 1, "a" ); + _clause.Values( 2, "b" ); + Assert.Equal( result.ToString(), GetSql() ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( "a", _parameterManager.GetValue( "@_p_1" ) ); + Assert.Equal( 2, _parameterManager.GetValue( "@_p_2" ) ); + Assert.Equal( "b", _parameterManager.GetValue( "@_p_3" ) ); + } + + #endregion + + #region AppendInsert + + /// + /// 测试添加到Insert子句 - 替换[]为特定转义字符 + /// + [Fact] + public void TestAppendInsert_1() { + var result = new StringBuilder(); + result.Append( "Insert Into $t&.$c&($a&.$b&) " ); + + var clause = new InsertClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ) ) ); + clause.AppendInsert( "[t].[c]([a].[b])", false ); + Assert.Equal( result.ToString(), GetSql( clause ) ); + } + + /// + /// 测试添加到Insert子句 - 原样添加 + /// + [Fact] + public void TestAppendInsert_2() { + var result = new StringBuilder(); + result.Append( "Insert Into [t].[c]([a].[b]) " ); + + var clause = new InsertClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ) ) ); + clause.AppendInsert( "[t].[c]([a].[b])", true ); + Assert.Equal( result.ToString(), GetSql( clause ) ); + } + + /// + /// 测试添加到Insert子句 - 多次调用 + /// + [Fact] + public void TestAppendInsert_3() { + var result = new StringBuilder(); + result.Append( "Insert Into [t].[c]([a].[b],[c].[d]) " ); + + var clause = new InsertClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ) ) ); + clause.AppendInsert( "[t].[c]([a].[b],", true ); + clause.AppendInsert( "[c].[d])", true ); + Assert.Equal( result.ToString(), GetSql( clause ) ); + } + + #endregion + + #region AppendValues + + /// + /// 测试添加到Values子句 - 替换[]为特定转义字符 + /// + [Fact] + public void TestAppendValues_1() { + var result = new StringBuilder(); + result.AppendLine( "Insert Into $t&($a&) " ); + result.Append( "Values($c&)" ); + + var clause = new InsertClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ) ) ); + clause.Insert( "a", "t" ); + clause.AppendValues( "([c])", false ); + Assert.Equal( result.ToString(), GetSql( clause ) ); + } + + /// + /// 测试添加到Values子句 - 原样添加 + /// + [Fact] + public void TestAppendValues_2() { + var result = new StringBuilder(); + result.AppendLine( "Insert Into $t&($a&) " ); + result.Append( "Values([c])" ); + + var clause = new InsertClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ) ) ); + clause.Insert( "a", "t" ); + clause.AppendValues( "([c])", true ); + Assert.Equal( result.ToString(), GetSql( clause ) ); + } + + /// + /// 测试添加到Values子句 - 多次调用 + /// + [Fact] + public void TestAppendValues_3() { + var result = new StringBuilder(); + result.AppendLine( "Insert Into $t&($a&) " ); + result.Append( "Values([c],[d])" ); + + var clause = new InsertClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ) ) ); + clause.Insert( "a", "t" ); + clause.AppendValues( "([c]", true ); + clause.AppendValues( ",[d])", true ); + Assert.Equal( result.ToString(), GetSql( clause ) ); + } + + #endregion + + #region Clear + + /// + /// 测试清理 + /// + [Fact] + public void TestClear() { + _clause.Insert( "a.b", "t.c" ); + _clause.Values( 1 ); + _clause.AppendInsert( "[t].[c]([a].[b])", false ); + _clause.Clear(); + Assert.Empty( GetSql() ); + } + + #endregion + + #region Clone + + /// + /// 测试复制Select子句 + /// + [Fact] + public void TestClone() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Insert Into [t].[c]([a].[b]) " ); + result.Append( "Values(@_p_0)" ); + + //插入数据 + _clause.Insert( "a.b", "t.c" ); + _clause.Values( 1 ); + + //复制并验证 + var clone = _clause.Clone( new TestSqlBuilder() ); + Assert.Equal( result.ToString(), GetSql( clone ) ); + + //修改原始子句,复制子句不应受影响 + _clause.Values( 2 ); + Assert.Equal( result.ToString(), GetSql( clone ) ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Clauses/JoinClauseTest.cs b/test/Util.Data.Sql.Tests/Builders/Clauses/JoinClauseTest.cs new file mode 100644 index 000000000..a5d7f421b --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Clauses/JoinClauseTest.cs @@ -0,0 +1,502 @@ +using System.Text; +using Util.Data.Sql.Builders.Clauses; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Clauses; + +/// +/// Join子句测试 +/// +public class JoinClauseTest { + + #region 测试初始化 + + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + /// + /// Join子句 + /// + private readonly JoinClause _clause; + + /// + /// 测试初始化 + /// + public JoinClauseTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + _clause = new JoinClause( new TestSqlBuilder( parameterManager: _parameterManager ) ); + } + + /// + /// 获取Sql语句 + /// + private string GetSql( IJoinClause clause = null ) { + clause ??= _clause; + var builder = new StringBuilder(); + clause.AppendTo( builder ); + return builder.ToString(); + } + + #endregion + + #region 默认输出 + + /// + /// 默认输出 + /// + [Fact] + public void TestDefault() { + Assert.Empty( GetSql() ); + } + + #endregion + + #region On + + /// + /// 测试表连接条件 - 未设置join返回空 + /// + [Fact] + public void TestOn_1() { + _clause.On( "a.id", "b" ); + Assert.Empty( GetSql() ); + } + + #endregion + + #region Join + + /// + /// 测试内连接 + /// + [Fact] + public void TestJoin_1() { + _clause.Join( "a.b as c" ); + Assert.Equal( "Join [a].[b] [c]", GetSql() ); + } + + /// + /// 测试内连接 - 连接列 + /// + [Fact] + public void TestJoin_2() { + var result = new StringBuilder(); + result.Append( "Join [t] " ); + result.Append( "On [t].[id]=[a].[id]" ); + + _clause.Join( "t" ); + _clause.On( "t.id", "a.id" ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试内连接 - 2个条件 + /// + [Fact] + public void TestJoin_3() { + var result = new StringBuilder(); + result.Append( "Join [t] " ); + result.Append( "On [t].[id]=[a].[id] And [t].[id]>@_p_0" ); + + _clause.Join( "t" ); + _clause.On( "t.id", "a.id" ); + _clause.On( "t.id", 1, Operator.Greater, true ); + Assert.Equal( result.ToString(), GetSql() ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试2个内连接 + /// + [Fact] + public void TestJoin_4() { + var result = new StringBuilder(); + result.Append( "Join [b] " ); + result.AppendLine( "On [b].[id]=[a].[id] " ); + result.Append( "Join [c] " ); + result.Append( "On [c].[id]=[b].[id] And [c].[id]>@_p_0" ); + + _clause.Join( "b" ); + _clause.On( "b.id", "a.id" ); + _clause.Join( "c" ); + _clause.On( "c.id", "b.id" ); + _clause.On( "c.id", 1, Operator.Greater, true ); + Assert.Equal( result.ToString(), GetSql() ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试内连接 - 子查询 + /// + [Fact] + public void TestJoin_5() { + var result = new StringBuilder(); + result.Append( "Join [t] " ); + result.AppendLine( "On [t].[id]=[a].[id] " ); + result.AppendLine( "Join (Select [a] " ); + result.Append( "From [b]) As [c]" ); + + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + _clause.Join( "t" ); + _clause.On( "t.id", "a.id" ); + _clause.Join( builder, "c" ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试内连接 - 子查询 - 内嵌表达式 + /// + [Fact] + public void TestJoin_6() { + var result = new StringBuilder(); + result.Append( "Join [t] " ); + result.AppendLine( "On [t].[id]=[a].[id] " ); + result.AppendLine( "Join (Select [a] " ); + result.Append( "From [b]) As [c]" ); + + _clause.Join( "t" ); + _clause.On( "t.id", "a.id" ); + _clause.Join( t => t.Select( "a" ).From( "b" ), "c" ); + Assert.Equal( result.ToString(), GetSql() ); + } + + #endregion + + #region LeftJoin + + /// + /// 测试左外连接 + /// + [Fact] + public void TestLeftJoin_1() { + _clause.LeftJoin( "a.b as c" ); + Assert.Equal( "Left Join [a].[b] [c]", GetSql() ); + } + + /// + /// 测试左外连接 - 子查询 + /// + [Fact] + public void TestLeftJoin_2() { + var result = new StringBuilder(); + result.Append( "Join [t] " ); + result.AppendLine( "On [t].[id]=[a].[id] " ); + result.AppendLine( "Left Join (Select [a] " ); + result.Append( "From [b]) As [c]" ); + + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + _clause.Join( "t" ); + _clause.On( "t.id", "a.id" ); + _clause.LeftJoin( builder, "c" ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试左外连接 - 子查询 - 内嵌表达式 + /// + [Fact] + public void TestLeftJoin_3() { + var result = new StringBuilder(); + result.Append( "Join [t] " ); + result.AppendLine( "On [t].[id]=[a].[id] " ); + result.AppendLine( "Left Join (Select [a] " ); + result.Append( "From [b]) As [c]" ); + + _clause.Join( "t" ); + _clause.On( "t.id", "a.id" ); + _clause.LeftJoin( t => t.Select( "a" ).From( "b" ), "c" ); + Assert.Equal( result.ToString(), GetSql() ); + } + + #endregion + + #region RightJoin + + /// + /// 测试右外连接 + /// + [Fact] + public void TestRightJoin_1() { + _clause.RightJoin( "a.b as c" ); + Assert.Equal( "Right Join [a].[b] [c]", GetSql() ); + } + + /// + /// 测试右外连接 - 子查询 + /// + [Fact] + public void TestRightJoin_2() { + var result = new StringBuilder(); + result.Append( "Join [t] " ); + result.AppendLine( "On [t].[id]=[a].[id] " ); + result.AppendLine( "Right Join (Select [a] " ); + result.Append( "From [b]) As [c]" ); + + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + _clause.Join( "t" ); + _clause.On( "t.id", "a.id" ); + _clause.RightJoin( builder, "c" ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试右外连接 - 子查询 - 内嵌表达式 + /// + [Fact] + public void TestRightJoin_3() { + var result = new StringBuilder(); + result.Append( "Join [t] " ); + result.AppendLine( "On [t].[id]=[a].[id] " ); + result.AppendLine( "Right Join (Select [a] " ); + result.Append( "From [b]) As [c]" ); + + _clause.Join( "t" ); + _clause.On( "t.id", "a.id" ); + _clause.RightJoin( t => t.Select( "a" ).From( "b" ), "c" ); + Assert.Equal( result.ToString(), GetSql() ); + } + + #endregion + + #region AppendJoin + + /// + /// 测试添加到内连接 - 替换[]为特定转义字符 + /// + [Fact] + public void TestAppendJoin_1() { + var clause = new JoinClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ),_parameterManager ) ); + clause.AppendJoin( "[a].[b]", false ); + Assert.Equal( "Join $a&.$b&", GetSql( clause ) ); + } + + /// + /// 测试添加到内连接 - 原样添加 + /// + [Fact] + public void TestAppendJoin_2() { + var clause = new JoinClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ), _parameterManager ) ); + clause.AppendJoin( "[a].[b] as c", true ); + Assert.Equal( "Join [a].[b] as c", GetSql( clause ) ); + } + + /// + /// 测试添加到内连接 - 先调用select,再调用appendJoin + /// + [Fact] + public void TestAppendJoin_3() { + var result = new StringBuilder(); + result.Append( "Join [t] " ); + result.AppendLine( "On [t].[id]=[a].[id] " ); + result.Append( "Join [a].[b] as c" ); + + _clause.Join( "t" ); + _clause.On( "t.id", "a.id" ); + _clause.AppendJoin( "[a].[b] as c", false ); + Assert.Equal( result.ToString(), GetSql() ); + } + + #endregion + + #region AppendLeftJoin + + /// + /// 测试添加到左外连接 - 替换[]为特定转义字符 + /// + [Fact] + public void TestAppendLeftJoin_1() { + var clause = new JoinClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ), _parameterManager ) ); + clause.AppendLeftJoin( "[a].[b]", false ); + Assert.Equal( "Left Join $a&.$b&", GetSql( clause ) ); + } + + /// + /// 测试添加到左外连接 - 原样添加 + /// + [Fact] + public void TestAppendLeftJoin_2() { + var clause = new JoinClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ), _parameterManager ) ); + clause.AppendLeftJoin( "[a].[b] as c", true ); + Assert.Equal( "Left Join [a].[b] as c", GetSql( clause ) ); + } + + /// + /// 测试添加到左外连接 - 先调用select,再调用AppendLeftJoin + /// + [Fact] + public void TestAppendLeftJoin_3() { + var result = new StringBuilder(); + result.Append( "Join [t] " ); + result.AppendLine( "On [t].[id]=[a].[id] " ); + result.Append( "Left Join [a].[b] as c" ); + + _clause.Join( "t" ); + _clause.On( "t.id", "a.id" ); + _clause.AppendLeftJoin( "[a].[b] as c", false ); + Assert.Equal( result.ToString(), GetSql() ); + } + + #endregion + + #region AppendRightJoin + + /// + /// 测试添加到右外连接 - 替换[]为特定转义字符 + /// + [Fact] + public void TestAppendRightJoin_1() { + var clause = new JoinClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ), _parameterManager ) ); + clause.AppendRightJoin( "[a].[b]", false ); + Assert.Equal( "Right Join $a&.$b&", GetSql( clause ) ); + } + + /// + /// 测试添加到右外连接 - 原样添加 + /// + [Fact] + public void TestAppendRightJoin_2() { + var clause = new JoinClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ), _parameterManager ) ); + clause.AppendRightJoin( "[a].[b] as c", true ); + Assert.Equal( "Right Join [a].[b] as c", GetSql( clause ) ); + } + + /// + /// 测试添加到右外连接 - 先调用select,再调用AppendRightJoin + /// + [Fact] + public void TestAppendRightJoin_3() { + var result = new StringBuilder(); + result.Append( "Join [t] " ); + result.AppendLine( "On [t].[id]=[a].[id] " ); + result.Append( "Right Join [a].[b] as c" ); + + _clause.Join( "t" ); + _clause.On( "t.id", "a.id" ); + _clause.AppendRightJoin( "[a].[b] as c", false ); + Assert.Equal( result.ToString(), GetSql() ); + } + + #endregion + + #region AppendOn + + /// + /// 添加到On子句 + /// + [Fact] + public void TestAppendOn_1() { + //结果 + var result = new StringBuilder(); + result.Append( "Join $t& " ); + result.Append( "On $a&.$id&=b.id" ); + + //操作 + var clause = new JoinClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ), _parameterManager ) ); + clause.Join( "t" ); + clause.AppendOn( "[a].[id]=b.id", false ); + + + //验证 + Assert.Equal( result.ToString(), GetSql( clause ) ); + } + + /// + /// 添加到On子句 - 先调用On,再调用AppendOn + /// + [Fact] + public void TestAppendOn_2() { + //结果 + var result = new StringBuilder(); + result.Append( "Join $t& " ); + result.Append( "On $a&.$id&=$b&.$id& " ); + result.Append( "And [c].[id]=b.id" ); + + //操作 + var clause = new JoinClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ), _parameterManager ) ); + clause.Join( "t" ); + clause.On( "a.id", "b.id" ); + clause.AppendOn( "[c].[id]=b.id", true ); + + + //验证 + Assert.Equal( result.ToString(), GetSql( clause ) ); + } + + /// + /// 添加到On子句 - 综合调用 + /// + [Fact] + public void TestAppendOn_3() { + //结果 + var result = new StringBuilder(); + result.Append( "Join [t] " ); + result.Append( "On [a].[id]=[b].[id] " ); + result.AppendLine( "And [c].[id]=b.id " ); + result.Append( "Join a " ); + result.Append( "On [a].[id2]=[b].[id] " ); + result.Append( "And [c].[id]=[a].[id] " ); + result.Append( "And c.id=b.id" ); + + //操作 + _clause.Join( "t" ); + _clause.On( "a.id", "b.id" ); + _clause.AppendOn( "[c].[id]=b.id", false ); + _clause.AppendJoin( "a", false ); + _clause.AppendOn( "[a].[id2]=[b].[id]", false ); + _clause.On( "c.id", "a.id" ); + _clause.AppendOn( "c.id=b.id", false ); + + + //验证 + Assert.Equal( result.ToString(), GetSql() ); + } + + #endregion + + #region Clear + + /// + /// 测试清理 + /// + [Fact] + public void TestClear() { + _clause.Join( "t" ); + _clause.On( "t.id", "a.id" ); + _clause.Clear(); + Assert.Empty( GetSql() ); + } + + #endregion + + #region Clone + + /// + /// 测试复制Join子句 + /// + [Fact] + public void TestClone() { + //结果 + var result = new StringBuilder(); + result.Append( "Join [t] " ); + result.Append( "On [t].[id]=[a].[id]" ); + + //设置join条件 + _clause.Join( "t" ); + _clause.On( "t.id", "a.id" ); + + + //复制并验证 + var clone = _clause.Clone( new TestSqlBuilder() ); + Assert.Equal( result.ToString(), GetSql( clone ) ); + + //修改原始子句,复制子句不应受影响 + _clause.Join( "t2" ); + _clause.On( "t2.id", "b.id" ); + Assert.Equal( result.ToString(), GetSql( clone ) ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Clauses/OrderByClauseTest.cs b/test/Util.Data.Sql.Tests/Builders/Clauses/OrderByClauseTest.cs new file mode 100644 index 000000000..998a64948 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Clauses/OrderByClauseTest.cs @@ -0,0 +1,183 @@ +using System.Text; +using Util.Data.Sql.Builders.Clauses; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Clauses; + +/// +/// Order By子句测试 +/// +public class OrderByClauseTest { + + #region 测试初始化 + + /// + /// 排序子句 + /// + private readonly OrderByClause _clause; + + /// + /// 测试初始化 + /// + public OrderByClauseTest() { + _clause = new OrderByClause( new TestSqlBuilder() ); + } + + /// + /// 获取Sql语句 + /// + private string GetSql( IOrderByClause clause = null ) { + clause ??= _clause; + var builder = new StringBuilder(); + clause.AppendTo( builder ); + return builder.ToString(); + } + + #endregion + + #region 默认输出 + + /// + /// 默认输出 + /// + [Fact] + public void TestDefault() { + Assert.Empty( GetSql() ); + } + + #endregion + + #region OrderBy + + /// + /// 测试排序 - 升序 + /// + [Fact] + public void TestOrderBy_1() { + _clause.OrderBy( "a.B" ); + Assert.Equal( "Order By [a].[B]", GetSql() ); + } + + /// + /// 测试排序 - asc关键字 + /// + [Fact] + public void TestOrderBy_2() { + _clause.OrderBy( "a.B asc" ); + Assert.Equal( "Order By [a].[B]", GetSql() ); + } + + /// + /// 测试排序 - 降序 + /// + [Fact] + public void TestOrderBy_3() { + _clause.OrderBy( "a.B desc" ); + Assert.Equal( "Order By [a].[B] Desc", GetSql() ); + } + + /// + /// 测试排序 - 2个排序列 + /// + [Fact] + public void TestOrderBy_4() { + _clause.OrderBy( "a.B,c.D desc" ); + Assert.Equal( "Order By [a].[B],[c].[D] Desc", GetSql() ); + } + + /// + /// 测试排序 - 2个排序列 - 添加多余空格 + /// + [Fact] + public void TestOrderBy_5() { + _clause.OrderBy( " a .B, c. D desc " ); + Assert.Equal( "Order By [a].[B],[c].[D] Desc", GetSql() ); + } + + /// + /// 测试排序 - 多次调用 + /// + [Fact] + public void TestOrderBy_6() { + _clause.OrderBy( "a.B,c.D desc" ); + _clause.OrderBy( "[e].F ASC,g.H DESC" ); + Assert.Equal( "Order By [a].[B],[c].[D] Desc,[e].[F],[g].[H] Desc", GetSql() ); + } + + #endregion + + #region AppendSql + + /// + /// 测试添加到Order By子句 - 替换[]为特定转义字符 + /// + [Fact] + public void TestAppendSql_1() { + var clause = new OrderByClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ) ) ); + clause.AppendSql( "[a].[b]", false ); + Assert.Equal( "Order By $a&.$b&", GetSql( clause ) ); + } + + /// + /// 测试添加到Order By子句 - 原样添加 + /// + [Fact] + public void TestAppendSql_2() { + var clause = new OrderByClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ) ) ); + clause.AppendSql( "[a].[b]", true ); + Assert.Equal( "Order By [a].[b]", GetSql( clause ) ); + } + + /// + /// 测试添加到Order By子句 - 多次调用 + /// + [Fact] + public void TestAppendSql_3() { + _clause.OrderBy( "a.b DESC" ); + _clause.OrderBy( "c,D.e desc" ); + _clause.AppendSql( "a", true ); + _clause.AppendSql( "b", true ); + Assert.Equal( "Order By [a].[b] Desc,[c],[D].[e] Desc,ab", GetSql() ); + } + + #endregion + + #region Clear + + /// + /// 测试清理 + /// + [Fact] + public void TestClear() { + _clause.OrderBy( "a.b DESC" ); + _clause.OrderBy( "c,D.e desc" ); + _clause.AppendSql( "a", true ); + _clause.AppendSql( "b", true ); + _clause.Clear(); + Assert.Empty( GetSql() ); + } + + #endregion + + #region Clone + + /// + /// 测试复制Order By子句 + /// + [Fact] + public void TestClone() { + //设置排序 + _clause.OrderBy( "a.B" ); + + //复制并验证 + var clone = _clause.Clone( new TestSqlBuilder() ); + Assert.Equal( "Order By [a].[B]", GetSql( clone ) ); + + //修改原始子句,复制子句不应受影响 + _clause.OrderBy( "c" ); + Assert.Equal( "Order By [a].[B]", GetSql( clone ) ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Clauses/SelectClauseTest.cs b/test/Util.Data.Sql.Tests/Builders/Clauses/SelectClauseTest.cs new file mode 100644 index 000000000..5516b16e9 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Clauses/SelectClauseTest.cs @@ -0,0 +1,297 @@ +using System.Text; +using Util.Data.Sql.Builders.Clauses; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Clauses; + +/// +/// Select子句测试 +/// +public class SelectClauseTest { + + #region 测试初始化 + + /// + /// Select子句 + /// + private readonly SelectClause _clause; + + /// + /// 测试初始化 + /// + public SelectClauseTest() { + _clause = new SelectClause( new TestSqlBuilder() ); + } + + /// + /// 获取Sql语句 + /// + private string GetSql( ISelectClause clause = null ) { + clause ??= _clause; + var builder = new StringBuilder(); + clause.AppendTo( builder ); + return builder.ToString(); + } + + #endregion + + #region 默认输出 + + /// + /// 默认输出 + /// + [Fact] + public void TestDefault() { + Assert.Empty( GetSql() ); + } + + #endregion + + #region Select + + /// + /// 测试设置列名为* + /// + [Fact] + public void TestSelect_1() { + _clause.Select(); + Assert.Equal( "Select *", GetSql() ); + } + + /// + /// 测试设置列名为* + /// + [Fact] + public void TestSelect_2() { + _clause.Select( "*" ); + Assert.Equal( "Select *", GetSql() ); + } + + /// + /// 测试设置1个简单列 + /// + [Fact] + public void TestSelect_3() { + _clause.Select( "a" ); + Assert.Equal( "Select [a]", GetSql() ); + } + + /// + /// 测试设置2个简单列 + /// + [Fact] + public void TestSelect_4() { + _clause.Select( "a,b" ); + Assert.Equal( "Select [a],[b]", GetSql() ); + } + + /// + /// 测试设置1个带表别名的列 + /// + [Fact] + public void TestSelect_5() { + _clause.Select( "a.b" ); + Assert.Equal( "Select [a].[b]", GetSql() ); + } + + /// + /// 测试设置1个带表别名和列别名的列 + /// + [Fact] + public void TestSelect_6() { + _clause.Select( "a.b as c" ); + Assert.Equal( "Select [a].[b] As [c]", GetSql() ); + } + + /// + /// 测试设置2个带表别名和列别名的列 + /// + [Fact] + public void TestSelect_7() { + _clause.Select( "a.b as c,[d].[e] [F]" ); + Assert.Equal( "Select [a].[b] As [c],[d].[e] As [F]", GetSql() ); + } + + /// + /// 测试设置多个select + /// + [Fact] + public void TestSelect_8() { + _clause.Select( "a.b as c" ); + _clause.Select( "[d].[e] [F]" ); + Assert.Equal( "Select [a].[b] As [c],[d].[e] As [F]", GetSql() ); + } + + /// + /// 测试设置子查询列 + /// + [Fact] + public void TestSelect_9() { + var result = new StringBuilder(); + result.Append( "Select [a].[b]," ); + result.AppendLine( "(Select [a] " ); + result.Append( "From [b]) As [c]" ); + + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + _clause.Select( "a.b" ); + _clause.Select( builder,"c" ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试设置子查询列 - 内嵌表达式 + /// + [Fact] + public void TestSelect_10() { + var result = new StringBuilder(); + result.Append( "Select [a].[b]," ); + result.AppendLine( "(Select [a] " ); + result.Append( "From [b]) As [c]" ); + + _clause.Select( "a.b" ); + _clause.Select( t => t.Select( "a" ).From( "b" ), "c" ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试设置列 - 验证空值 + /// + [Fact] + public void TestSelect_11() { + _clause.Select( "a" ); + _clause.Select( "" ); + Assert.Equal( "Select [a]", GetSql() ); + } + + #endregion + + #region AppendSql + + /// + /// 添加到select子句 - 替换[]为特定转义字符 + /// + [Fact] + public void TestAppendSql_1() { + var clause = new SelectClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ) ) ); + clause.AppendSql( "[a].[b], c.[D]", false ); + Assert.Equal( "Select $a&.$b&, c.$D&", GetSql( clause ) ); + } + + /// + /// 添加到select子句 - 原样添加 + /// + [Fact] + public void TestAppendSql_2() { + var clause = new SelectClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ) ) ); + clause.AppendSql( "[a].[b], c.[D]", true ); + Assert.Equal( "Select [a].[b], c.[D]", GetSql( clause ) ); + } + + /// + /// 添加到select子句 - 多次调用 + /// + [Fact] + public void TestAppendSql_3() { + var clause = new SelectClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ) ) ); + clause.AppendSql( "[a].[b],", true ); + clause.AppendSql( "c.[D]", true ); + Assert.Equal( "Select [a].[b],c.[D]", GetSql( clause ) ); + } + + /// + /// 测试添加到select子句 - 子查询 + /// + [Fact] + public void TestAppendSql_4() { + var result = new StringBuilder(); + result.Append( "Select [a].[b]," ); + result.AppendLine( "(Select [a] " ); + result.Append( "From [b]) As [c]" ); + + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + _clause.AppendSql( "[a].[b],",true ); + _clause.AppendSql( "(", true ); + _clause.AppendSql( builder ); + _clause.AppendSql( ") As [c]", true ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试添加到select子句 - 子查询 - 内嵌表达式 + /// + [Fact] + public void TestAppendSql_5() { + var result = new StringBuilder(); + result.Append( "Select [a].[b]," ); + result.AppendLine( "(Select [a] " ); + result.Append( "From [b]) As [c]" ); + + _clause.AppendSql( "[a].[b],", true ); + _clause.AppendSql( "(", true ); + _clause.AppendSql( t => t.Select( "a" ).From( "b" ) ); + _clause.AppendSql( ") As [c]", true ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试添加到select子句 - AppendSql末尾添加逗号,后跟Select + /// + [Fact] + public void TestAppendSql_6() { + var result = new StringBuilder(); + result.Append( "Select [a].[b],c,[d]" ); + + _clause.Select( "a.b" ); + _clause.AppendSql( ",c,", true ); + _clause.Select( "d" ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 添加到select子句 - 对[]转义 + /// + [Fact] + public void TestAppendSql_7() { + var clause = new SelectClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ) ) ); + clause.AppendSql( "[[a]].[b], c.[D]", false ); + Assert.Equal( "Select [a].$b&, c.$D&", GetSql( clause ) ); + } + + #endregion + + #region Clear + + /// + /// 测试清理 + /// + [Fact] + public void TestClear() { + _clause.Select( "a" ); + _clause.Clear(); + Assert.Empty( GetSql() ); + } + + #endregion + + #region Clone + + /// + /// 测试复制Select子句 + /// + [Fact] + public void TestClone() { + //设置列 + _clause.Select( "a" ); + + //复制并验证 + var clone = _clause.Clone( new TestSqlBuilder() ); + Assert.Equal( "Select [a]", GetSql( clone ) ); + + //修改原始子句,复制子句不应受影响 + _clause.Select( "b" ); + Assert.Equal( "Select [a]", GetSql( clone ) ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Clauses/StartClauseTest.cs b/test/Util.Data.Sql.Tests/Builders/Clauses/StartClauseTest.cs new file mode 100644 index 000000000..350460442 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Clauses/StartClauseTest.cs @@ -0,0 +1,187 @@ +using System.Text; +using Util.Data.Sql.Builders.Clauses; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Clauses; + +/// +/// 起始子句测试 +/// +public class StartClauseTest { + + #region 测试初始化 + + /// + /// 排序子句 + /// + private readonly StartClause _clause; + + /// + /// 测试初始化 + /// + public StartClauseTest() { + _clause = new StartClause( new TestSqlBuilder() ); + } + + /// + /// 获取Sql语句 + /// + private string GetSql( IStartClause clause = null ) { + clause ??= _clause; + var builder = new StringBuilder(); + clause.AppendTo( builder ); + return builder.ToString(); + } + + #endregion + + #region Cte(公用表表达式) + + /// + /// 测试公用表表达式CTE + /// + [Fact] + public void TestCte_1() { + var result = new StringBuilder(); + result.AppendLine( "With [Test] " ); + result.AppendLine( "As (Select [a] " ); + result.Append( "From [b])" ); + + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + _clause.Cte( "Test", builder ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试公用表表达式CTE - 两个表达式 + /// + [Fact] + public void TestCte_2() { + var result = new StringBuilder(); + result.AppendLine( "With [Test1] " ); + result.AppendLine( "As (Select [a] " ); + result.AppendLine( "From [b])," ); + result.AppendLine( "[Test2] " ); + result.AppendLine( "As (Select [a] " ); + result.Append( "From [b])" ); + + var builder1 = new TestSqlBuilder().Select( "a" ).From( "b" ); + var builder2 = new TestSqlBuilder().Select( "a" ).From( "b" ); + _clause.Cte( "Test1", builder1 ); + _clause.Cte( "Test2", builder2 ); + Assert.Equal( result.ToString(), GetSql() ); + } + + #endregion + + #region Append + + /// + /// 测试添加到起始位置 - 替换[]为特定转义字符 + /// + [Fact] + public void TestAppend_1() { + var clause = new StartClause( new TestSqlBuilder( new TestDialect2() ) ); + clause.Append( "[a].[b]", false ); + Assert.Equal( "$a&.$b&", GetSql( clause ) ); + } + + /// + /// 测试添加到起始位置 - 原样添加 + /// + [Fact] + public void TestAppend_2() { + var clause = new StartClause( new TestSqlBuilder( new TestDialect2() ) ); + clause.Append( "[a].[b]", true ); + Assert.Equal( "[a].[b]", GetSql( clause ) ); + } + + /// + /// 测试添加到起始位置 - 多次调用 + /// + [Fact] + public void TestAppend_3() { + var clause = new StartClause( new TestSqlBuilder( new TestDialect2() ) ); + clause.Append( "[a].", true ); + clause.Append( "[b]", true ); + Assert.Equal( "[a].[b]", GetSql( clause ) ); + } + + #endregion + + #region ClearCte + + /// + /// 测试清理公用表表达式CTE + /// + [Fact] + public void TestClearCte() { + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + _clause.Cte( "Test", builder ); + _clause.ClearCte(); + Assert.Empty( GetSql() ); + } + + #endregion + + #region Clear + + /// + /// 测试清理 + /// + [Fact] + public void TestClear() { + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + _clause.Cte( "Test", builder ); + _clause.Append( "[a].[b]", false ); + _clause.Clear(); + Assert.Empty( GetSql() ); + } + + #endregion + + #region Clone + + /// + /// 测试起始子句 - 复制子句结果 + /// + [Fact] + public void TestClone_Result() { + _clause.Append( "a", true ); + + //复制并验证 + var clone = _clause.Clone( new TestSqlBuilder() ); + Assert.Equal( "a", GetSql( clone ) ); + + //修改原始子句,复制子句不应受影响 + _clause.Append( "b", true ); + Assert.Equal( "a", GetSql( clone ) ); + } + + /// + /// 测试起始子句 - 复制CTE + /// + [Fact] + public void TestClone_Cte() { + //Cte结果 + var result = new StringBuilder(); + result.AppendLine( "With [Test] " ); + result.AppendLine( "As (Select [a] " ); + result.Append( "From [b])" ); + + //设置Cte + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + _clause.Cte( "Test", builder ); + + //复制并验证 + var clone = _clause.Clone( new TestSqlBuilder() ); + Assert.Equal( result.ToString(), GetSql( clone ) ); + + //修改原Sql生成器,复制子句不应受影响 + builder.Where( "c", 1 ); + Assert.Equal( result.ToString(), GetSql( clone ) ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Clauses/WhereClauseTest.cs b/test/Util.Data.Sql.Tests/Builders/Clauses/WhereClauseTest.cs new file mode 100644 index 000000000..83bf4056e --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Clauses/WhereClauseTest.cs @@ -0,0 +1,595 @@ +using System; +using System.Text; +using Util.Data.Queries; +using Util.Data.Sql.Builders.Clauses; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Clauses; + +/// +/// Where子句测试 +/// +public class WhereClauseTest { + + #region 测试初始化 + + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + /// + /// Where子句 + /// + private readonly WhereClause _clause; + + /// + /// 测试初始化 + /// + public WhereClauseTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + _clause = new WhereClause( new TestSqlBuilder( parameterManager: _parameterManager ) ); + } + + /// + /// 获取Sql语句 + /// + private string GetSql( IWhereClause clause = null ) { + clause ??= _clause; + var builder = new StringBuilder(); + clause.AppendTo( builder ); + return builder.ToString(); + } + + #endregion + + #region 默认输出 + + /// + /// 默认输出 + /// + [Fact] + public void TestDefault() { + Assert.Empty( GetSql() ); + } + + #endregion + + #region Where(设置条件) + + /// + /// 设置相等条件 - 1个条件 + /// + [Fact] + public void TestWhere_1() { + _clause.Where( "Name", "a", Operator.Equal ); + Assert.Equal( "Where [Name]=@_p_0", GetSql() ); + Assert.Equal( "a", _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 设置相等条件 - 带表别名 + /// + [Fact] + public void TestWhere_2() { + _clause.Where( "f.Name", "a", Operator.Equal ); + Assert.Equal( "Where [f].[Name]=@_p_0", GetSql() ); + Assert.Equal( "a", _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 设置相等条件 - 2个条件 + /// + [Fact] + public void TestWhere_3() { + _clause.Where( "f.Name", "a", Operator.Equal ); + _clause.Where( "s.Age", "b", Operator.Equal ); + Assert.Equal( "Where [f].[Name]=@_p_0 And [s].[Age]=@_p_1", GetSql() ); + Assert.Equal( "a", _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( "b", _parameterManager.GetValue( "@_p_1" ) ); + } + + /// + /// 测试设置子查询条件 + /// + [Fact] + public void TestWhere_4() { + var result = new StringBuilder(); + result.Append( "Where [a].[b]=" ); + result.AppendLine( "(Select [a] " ); + result.Append( "From [b])" ); + + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + _clause.Where( "a.b", builder,Operator.Equal ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试设置子查询条件 - 内嵌表达式 + /// + [Fact] + public void TestWhere_5() { + var result = new StringBuilder(); + result.Append( "Where [a].[b]=" ); + result.AppendLine( "(Select [a] " ); + result.Append( "From [b])" ); + + _clause.Where( "a.b", t => t.Select( "a" ).From( "b" ), Operator.Equal ); + Assert.Equal( result.ToString(), GetSql() ); + } + + #endregion + + #region IsNull + + /// + /// 设置Is Null条件 + /// + [Fact] + public void TestIsNull() { + _clause.IsNull( "f.Name" ); + Assert.Equal( "Where [f].[Name] Is Null", GetSql() ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + #endregion + + #region IsNotNull + + /// + /// 设置Is Not Null条件 + /// + [Fact] + public void TestIsNotNull_1() { + _clause.IsNotNull( "f.Name" ); + Assert.Equal( "Where [f].[Name] Is Not Null", GetSql() ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + #endregion + + #region IsEmpty + + /// + /// 设置空条件 + /// + [Fact] + public void TestIsEmpty_1() { + _clause.IsEmpty( "f.Name" ); + Assert.Equal( "Where ([f].[Name] Is Null Or [f].[Name]='')", GetSql() ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + #endregion + + #region IsNotEmpty + + /// + /// 设置空条件 + /// + [Fact] + public void TestIsNotEmpty_1() { + _clause.IsNotEmpty( "f.Name" ); + Assert.Equal( "Where [f].[Name] Is Not Null And [f].[Name]<>''", GetSql() ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + #endregion + + #region Between(范围查询) + + /// + /// 测试范围查询 - 整型 + /// + [Fact] + public void TestBetween_1() { + //结果 + var result = new StringBuilder(); + result.Append( "Where [a].[B]>=@_p_0 And [a].[B]<=@_p_1" ); + + //执行 + _clause.Between( "a.B", 1, 2, Boundary.Both ); + + //验证 + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( 2, _parameterManager.GetValue( "@_p_1" ) ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试范围查询 - 整型 - 不包含边界 + /// + [Fact] + public void TestBetween_2() { + //结果 + var result = new StringBuilder(); + result.Append( "Where [a].[B]>@_p_0 And [a].[B]<@_p_1" ); + + //执行 + _clause.Between( "a.B", 1, 2, Boundary.Neither ); + + //验证 + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( 2, _parameterManager.GetValue( "@_p_1" ) ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试范围查询 - 整型 - 最小值大于最大值,则交换大小值的位置 + /// + [Fact] + public void TestBetween_3() { + //结果 + var result = new StringBuilder(); + result.Append( "Where [a].[B]>@_p_0 And [a].[B]<@_p_1" ); + + //执行 + _clause.Between( "a.B", 2, 1, Boundary.Neither ); + + //验证 + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( 2, _parameterManager.GetValue( "@_p_1" ) ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试范围查询 - 整型 - 最小值为空,忽略最小值条件 + /// + [Fact] + public void TestBetween_4() { + //结果 + var result = new StringBuilder(); + result.Append( "Where [a].[B]<=@_p_0" ); + + //执行 + _clause.Between( "a.B", null, 2, Boundary.Both ); + + //验证 + Assert.Equal( 2, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试范围查询 - 整型 - 最大值为空,忽略最大值条件 + /// + [Fact] + public void TestBetween_5() { + //结果 + var result = new StringBuilder(); + result.Append( "Where [a].[B]>=@_p_0" ); + + //执行 + _clause.Between( "a.B", 1, null, Boundary.Both ); + + //验证 + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试范围查询 - 整型 - 最大值和最小值均为null,忽略所有条件 + /// + [Fact] + public void TestBetween_6() { + //执行 + _clause.Between( "a.B", null, null, Boundary.Both ); + + //验证 + Assert.Empty( _parameterManager.GetParams() ); + Assert.Empty( GetSql() ); + } + + /// + /// 测试范围查询 - double + /// + [Fact] + public void TestBetween_7() { + //结果 + var result = new StringBuilder(); + result.Append( "Where [a].[B]>=@_p_0 And [a].[B]<=@_p_1" ); + + //执行 + _clause.Between( "a.B", 1.2, 3.4, Boundary.Both ); + + //验证 + Assert.Equal( 1.2, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( 3.4, _parameterManager.GetValue( "@_p_1" ) ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试范围查询 - decimal + /// + [Fact] + public void TestBetween_8() { + //结果 + var result = new StringBuilder(); + result.Append( "Where [a].[B]>=@_p_0 And [a].[B]<=@_p_1" ); + + //执行 + _clause.Between( "a.B", 1.2M, 3.4M, Boundary.Both ); + + //验证 + Assert.Equal( 1.2M, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( 3.4M, _parameterManager.GetValue( "@_p_1" ) ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试范围查询 - 日期 - 包含时间 + /// + [Fact] + public void TestBetween_9() { + //结果 + var result = new StringBuilder(); + result.Append( "Where [a].[B]>=@_p_0 And [a].[B]<=@_p_1" ); + + //执行 + var min = DateTime.Parse( "2000-1-1 10:10:10" ); + var max = DateTime.Parse( "2000-1-2 10:10:10" ); + _clause.Between( "a.B", min, max, true, null ); + + //验证 + Assert.Equal( min, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( max, _parameterManager.GetValue( "@_p_1" ) ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试范围查询 - 日期 - 不包含时间 + /// + [Fact] + public void TestBetween_10() { + //结果 + var result = new StringBuilder(); + result.Append( "Where [a].[B]>=@_p_0 And [a].[B]<@_p_1" ); + + //执行 + var min = DateTime.Parse( "2000-1-1 10:10:10" ); + var max = DateTime.Parse( "2000-1-2 10:10:10" ); + _clause.Between( "a.B", min, max, false, null ); + + //验证 + Assert.Equal( DateTime.Parse( "2000-1-1" ), _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( DateTime.Parse( "2000-1-3" ), _parameterManager.GetValue( "@_p_1" ) ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试范围查询 - 日期 - 设置边界 + /// + [Fact] + public void TestBetween_11() { + //结果 + var result = new StringBuilder(); + result.Append( "Where [a].[B]>@_p_0 And [a].[B]<@_p_1" ); + + //执行 + var min = DateTime.Parse( "2000-1-1 10:10:10" ); + var max = DateTime.Parse( "2000-1-2 10:10:10" ); + _clause.Between( "a.B", min, max, true, Boundary.Neither ); + + //验证 + Assert.Equal( min, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( max, _parameterManager.GetValue( "@_p_1" ) ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试范围查询 - 日期 - 不包含时间 - 最大值为空 + /// + [Fact] + public void TestBetween_12() { + //结果 + var result = new StringBuilder(); + result.Append( "Where [a].[B]>=@_p_0" ); + + //执行 + var min = DateTime.Parse( "2000-1-1 10:10:10" ); + _clause.Between( "a.B", min, null, false, null ); + + //验证 + Assert.Equal( DateTime.Parse( "2000-1-1" ), _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试范围查询 - 日期 - 包含时间 - 最大值为空 + /// + [Fact] + public void TestBetween_13() { + //结果 + var result = new StringBuilder(); + result.Append( "Where [a].[B]>=@_p_0" ); + + //执行 + var min = DateTime.Parse( "2000-1-1 10:10:10" ); + _clause.Between( "a.B", min, null, true, null ); + + //验证 + Assert.Equal( min, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试范围查询 - 日期 - 不包含时间 - 最小值为空 + /// + [Fact] + public void TestBetween_14() { + //结果 + var result = new StringBuilder(); + result.Append( "Where [a].[B]<@_p_0" ); + + //执行 + var max = DateTime.Parse( "2000-1-2 10:10:10" ); + _clause.Between( "a.B", null, max, false, null ); + + //验证 + Assert.Equal( DateTime.Parse( "2000-1-3" ), _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 测试范围查询 - 日期 - 包含时间 - 最小值为空 + /// + [Fact] + public void TestBetween_15() { + //结果 + var result = new StringBuilder(); + result.Append( "Where [a].[B]<=@_p_0" ); + + //执行 + var max = DateTime.Parse( "2000-1-2 10:10:10" ); + _clause.Between( "a.B", null, max, true, null ); + + //验证 + Assert.Equal( max, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( result.ToString(), GetSql() ); + } + + #endregion + + #region Exists + + /// + /// 设置Exists条件 - 子查询Sql生成器 + /// + [Fact] + public void TestExists_1() { + //结果 + var result = new StringBuilder(); + result.Append( "Where Exists (" ); + result.AppendLine( "Select [Name] " ); + result.Append( "From [t]" ); + result.Append( ")" ); + + //子查询 + var subBuilder = new TestSqlBuilder(); + subBuilder.Select( "Name" ).From( "t" ); + + //执行 + _clause.Exists( subBuilder ); + + //验证 + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 设置Exists条件 - 子查询操作 + /// + [Fact] + public void TestExists_2() { + //结果 + var result = new StringBuilder(); + result.Append( "Where Exists (" ); + result.AppendLine( "Select [Name] " ); + result.Append( "From [t]" ); + result.Append( ")" ); + + //执行 + _clause.Exists( t => t.Select( "Name" ).From( "t" ) ); + + //验证 + Assert.Equal( result.ToString(), GetSql() ); + } + + #endregion + + #region NotExists + + /// + /// 设置Not Exists条件 - 子查询Sql生成器 + /// + [Fact] + public void TestNotExists_1() { + //结果 + var result = new StringBuilder(); + result.Append( "Where Not Exists (" ); + result.AppendLine( "Select [Name] " ); + result.Append( "From [t]" ); + result.Append( ")" ); + + //子查询 + var subBuilder = new TestSqlBuilder(); + subBuilder.Select( "Name" ).From( "t" ); + + //执行 + _clause.NotExists( subBuilder ); + + //验证 + Assert.Equal( result.ToString(), GetSql() ); + } + + /// + /// 设置Not Exists条件 - 子查询操作 + /// + [Fact] + public void TestNotExists_2() { + //结果 + var result = new StringBuilder(); + result.Append( "Where Not Exists (" ); + result.AppendLine( "Select [Name] " ); + result.Append( "From [t]" ); + result.Append( ")" ); + + //执行 + _clause.NotExists( t => t.Select( "Name" ).From( "t" ) ); + + //验证 + Assert.Equal( result.ToString(), GetSql() ); + } + + #endregion + + #region AppendSql(添加到Where子句) + + /// + /// 添加到Where子句 + /// + [Fact] + public void TestAppendSql() { + var clause = new WhereClause( new TestSqlBuilder( new TestDialect2(), new TestColumnCache( new TestDialect2() ) ) ); + clause.AppendSql( "[a]", false ); + clause.AppendSql( "[b]", true ); + Assert.Equal( "Where $a& And [b]", GetSql( clause ) ); + } + + #endregion + + #region Clear + + /// + /// 测试清理 + /// + [Fact] + public void TestClear() { + _clause.Where( "Name", "a", Operator.Equal ); + _clause.Clear(); + Assert.Empty( GetSql() ); + } + + #endregion + + #region Clone + + /// + /// 测试复制Where子句 + /// + [Fact] + public void TestClone() { + //设置条件 + _clause.Where( "Name", "a", Operator.Equal ); + + //复制并验证 + var sqlBuilder = new TestSqlBuilder(); + var clone = _clause.Clone( sqlBuilder ); + Assert.Equal( "Where [Name]=@_p_0", GetSql( clone ) ); + + //修改原始子句,复制子句不应受影响 + _clause.Where( "Code", "b", Operator.Equal ); + Assert.Equal( "Where [Name]=@_p_0", GetSql( clone ) ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Conditions/AndConditionTest.cs b/test/Util.Data.Sql.Tests/Builders/Conditions/AndConditionTest.cs new file mode 100644 index 000000000..0a7fc54a3 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Conditions/AndConditionTest.cs @@ -0,0 +1,103 @@ +using System.Text; +using Util.Data.Sql.Builders.Conditions; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Conditions; + +/// +/// And连接条件测试 +/// +public class AndConditionTest { + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + + /// + /// 测试初始化 + /// + public AndConditionTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + } + + /// + /// 测试与连接条件 + /// + [Fact] + public void Test_1() { + var result = new StringBuilder(); + var condition1 = new EqualCondition( _parameterManager, "Name", "@Name", false ); + condition1.AppendTo( result ); + var condition2 = new GreaterCondition( _parameterManager, "Age", "@Age", false ); + var andCondition = new AndCondition( condition2 ); + andCondition.AppendTo( result ); + Assert.Equal( "Name=@Name And Age>@Age", result.ToString() ); + } + + /// + /// 测试或连接条件 - 构造器传入2个条件 + /// + [Fact] + public void Test_2() { + var result = new StringBuilder(); + var condition1 = new EqualCondition( _parameterManager, "Name", "@Name", false ); + condition1.AppendTo( result ); + var condition2 = new EqualCondition( _parameterManager, "Code", "@Code", false ); + var condition3 = new GreaterCondition( _parameterManager, "Age", "@Age", false ); + var orCondition = new AndCondition( condition2, condition3 ); + orCondition.AppendTo( result ); + Assert.Equal( "Name=@Name And Code=@Code And Age>@Age", result.ToString() ); + } + + /// + /// 测试或连接条件 - 第2个条件为空条件 + /// + [Fact] + public void Test_3() { + var result = new StringBuilder(); + var condition1 = new EqualCondition( _parameterManager, "Name", "@Name", false ); + condition1.AppendTo( result ); + var condition2 = new EqualCondition( _parameterManager, "Code", "@Code", false ); + var orCondition = new AndCondition( condition2, NullCondition.Instance ); + orCondition.AppendTo( result ); + Assert.Equal( "Name=@Name And Code=@Code", result.ToString() ); + } + + /// + /// 测试条件1为空 + /// + [Fact] + public void Test_4() { + var result = new StringBuilder(); + var condition2 = new GreaterCondition( _parameterManager, "Age", "@Age", false ); + var andCondition = new AndCondition( condition2 ); + andCondition.AppendTo( result ); + Assert.Equal( "Age>@Age", result.ToString() ); + } + + /// + /// 测试条件2为空 + /// + [Fact] + public void Test_5() { + var result = new StringBuilder(); + var condition1 = new EqualCondition( _parameterManager, "Name", "@Name", false ); + condition1.AppendTo( result ); + var andCondition = new AndCondition( null ); + andCondition.AppendTo( result ); + Assert.Equal( "Name=@Name", result.ToString() ); + } + + /// + /// 测试条件都为空 + /// + [Fact] + public void Test_6() { + var result = new StringBuilder(); + var andCondition = new AndCondition( null ); + andCondition.AppendTo( result ); + Assert.Empty( result.ToString() ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Conditions/ContainsConditionTest.cs b/test/Util.Data.Sql.Tests/Builders/Conditions/ContainsConditionTest.cs new file mode 100644 index 000000000..0e34b745f --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Conditions/ContainsConditionTest.cs @@ -0,0 +1,64 @@ +using System; +using System.Text; +using Util.Data.Sql.Builders.Conditions; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Conditions; + +/// +/// Sql模糊匹配查询条件测试 +/// +public class ContainsConditionTest { + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + + /// + /// 测试初始化 + /// + public ContainsConditionTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + } + + /// + /// 获取结果 + /// + private string GetResult( ISqlCondition condition ) { + var result = new StringBuilder(); + condition.AppendTo( result ); + return result.ToString(); + } + + /// + /// 测试创建条件 - 验证列名为空 + /// + [Fact] + public void TestCreate_Validate() { + Assert.Throws( () => { + var condition = new ContainsCondition( _parameterManager, "", 1, true ); + } ); + } + + /// + /// 测试获取条件 - 参数化 + /// + [Fact] + public void TestGetCondition_1() { + var condition = new ContainsCondition( _parameterManager, "a", "b", true ); + Assert.Equal( "a Like @_p_0", GetResult( condition ) ); + Assert.Equal( "%b%", _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试获取条件 - 非参数化 + /// + [Fact] + public void TestGetCondition_2() { + var condition = new ContainsCondition( _parameterManager, "a", "b", false ); + Assert.Equal( "a Like '%b%'", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Conditions/EndsConditionTest.cs b/test/Util.Data.Sql.Tests/Builders/Conditions/EndsConditionTest.cs new file mode 100644 index 000000000..84ae67590 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Conditions/EndsConditionTest.cs @@ -0,0 +1,64 @@ +using System; +using System.Text; +using Util.Data.Sql.Builders.Conditions; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Conditions; + +/// +/// Sql尾匹配查询条件测试 +/// +public class EndsConditionTest { + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + + /// + /// 测试初始化 + /// + public EndsConditionTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + } + + /// + /// 获取结果 + /// + private string GetResult( ISqlCondition condition ) { + var result = new StringBuilder(); + condition.AppendTo( result ); + return result.ToString(); + } + + /// + /// 测试创建条件 - 验证列名为空 + /// + [Fact] + public void TestCreate_Validate() { + Assert.Throws( () => { + var condition = new EndsCondition( _parameterManager, "", 1, true ); + } ); + } + + /// + /// 测试获取条件 - 参数化 + /// + [Fact] + public void TestGetCondition_1() { + var condition = new EndsCondition( _parameterManager, "a", "b", true ); + Assert.Equal( "a Like @_p_0", GetResult( condition ) ); + Assert.Equal( "%b", _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试获取条件 - 非参数化 + /// + [Fact] + public void TestGetCondition_2() { + var condition = new EndsCondition( _parameterManager, "a", "b", false ); + Assert.Equal( "a Like '%b'", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Conditions/EqualConditionTest.cs b/test/Util.Data.Sql.Tests/Builders/Conditions/EqualConditionTest.cs new file mode 100644 index 000000000..845fc6c18 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Conditions/EqualConditionTest.cs @@ -0,0 +1,90 @@ +using System; +using System.Text; +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Conditions; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Conditions; + +/// +/// Sql相等查询条件测试 +/// +public class EqualConditionTest { + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + + /// + /// 测试初始化 + /// + public EqualConditionTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + } + + /// + /// 获取结果 + /// + private string GetResult( ISqlCondition condition ) { + var result = new StringBuilder(); + condition.AppendTo( result ); + return result.ToString(); + } + + /// + /// 测试创建条件 - 验证列名为空 + /// + [Fact] + public void TestCreate_Validate() { + Assert.Throws( () => { + var condition = new EqualCondition( _parameterManager, "", 1, true ); + } ); + } + + /// + /// 测试获取条件 - 参数化 + /// + [Fact] + public void TestGetCondition_1() { + var condition = new EqualCondition( _parameterManager, "a", 1, true ); + Assert.Equal( "a=@_p_0", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试获取条件 - 非参数化 + /// + [Fact] + public void TestGetCondition_2() { + var condition = new EqualCondition( _parameterManager, "a", "b", false ); + Assert.Equal( "a=b", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + /// + /// 测试获取条件 - 参数值为null,则输出 is null + /// + [Fact] + public void TestGetCondition_3() { + var condition = new EqualCondition( _parameterManager, "a", null, true ); + Assert.Equal( "a Is Null", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + /// + /// 测试获取条件 - 值为ISqlBuilder + /// + [Fact] + public void TestGetCondition_4() { + var result = new StringBuilder(); + result.Append( "a=" ); + result.AppendLine( "(Select [a] " ); + result.Append( "From [b])" ); + + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + var condition = new EqualCondition( _parameterManager, "a", builder, true ); + Assert.Equal( result.ToString(), GetResult( condition ) ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Conditions/ExistsConditionTest.cs b/test/Util.Data.Sql.Tests/Builders/Conditions/ExistsConditionTest.cs new file mode 100644 index 000000000..352244675 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Conditions/ExistsConditionTest.cs @@ -0,0 +1,43 @@ +using System.Text; +using Util.Data.Sql.Builders.Conditions; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Conditions; + +/// +/// Exists查询条件测试 +/// +public class ExistsConditionTest { + /// + /// 获取结果 + /// + private string GetResult( ISqlCondition condition ) { + var result = new StringBuilder(); + condition.AppendTo( result ); + return result.ToString(); + } + + /// + /// 测试Exists查询条件 + /// + [Fact] + public void Test() { + //结果 + var result = new StringBuilder(); + result.Append( "Exists (" ); + result.AppendLine( "Select [Name] " ); + result.Append( "From [t]" ); + result.Append( ")" ); + + //子查询 + var subBuilder = new TestSqlBuilder(); + subBuilder.Select( "Name" ).From( "t" ); + + //创建条件 + var condition = new ExistsCondition( subBuilder ); + + //验证 + Assert.Equal( result.ToString(), GetResult( condition ) ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Conditions/GreaterConditionTest.cs b/test/Util.Data.Sql.Tests/Builders/Conditions/GreaterConditionTest.cs new file mode 100644 index 000000000..9116a5fb5 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Conditions/GreaterConditionTest.cs @@ -0,0 +1,79 @@ +using System; +using System.Text; +using Util.Data.Sql.Builders.Conditions; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Conditions; + +/// +/// Sql大于查询条件测试 +/// +public class GreaterConditionTest { + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + + /// + /// 测试初始化 + /// + public GreaterConditionTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + } + + /// + /// 获取结果 + /// + private string GetResult( ISqlCondition condition ) { + var result = new StringBuilder(); + condition.AppendTo( result ); + return result.ToString(); + } + + /// + /// 测试创建条件 - 验证列名为空 + /// + [Fact] + public void TestCreate_Validate() { + Assert.Throws( () => { + var condition = new GreaterCondition( _parameterManager, "", 1, true ); + } ); + } + + /// + /// 测试获取条件 - 参数化 + /// + [Fact] + public void TestGetCondition_1() { + var condition = new GreaterCondition( _parameterManager, "a", 1, true ); + Assert.Equal( "a>@_p_0", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试获取条件 - 非参数化 + /// + [Fact] + public void TestGetCondition_2() { + var condition = new GreaterCondition( _parameterManager, "a", "b", false ); + Assert.Equal( "a>b", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + /// + /// 测试获取条件 - 值为ISqlBuilder + /// + [Fact] + public void TestGetCondition_3() { + var result = new StringBuilder(); + result.Append( "a>" ); + result.AppendLine( "(Select [a] " ); + result.Append( "From [b])" ); + + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + var condition = new GreaterCondition( _parameterManager, "a", builder, true ); + Assert.Equal( result.ToString(), GetResult( condition ) ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Conditions/GreaterEqualConditionTest.cs b/test/Util.Data.Sql.Tests/Builders/Conditions/GreaterEqualConditionTest.cs new file mode 100644 index 000000000..acdc1fae3 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Conditions/GreaterEqualConditionTest.cs @@ -0,0 +1,79 @@ +using System; +using System.Text; +using Util.Data.Sql.Builders.Conditions; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Conditions; + +/// +/// Sql大于等于查询条件测试 +/// +public class GreaterEqualConditionTest { + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + + /// + /// 测试初始化 + /// + public GreaterEqualConditionTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + } + + /// + /// 获取结果 + /// + private string GetResult( ISqlCondition condition ) { + var result = new StringBuilder(); + condition.AppendTo( result ); + return result.ToString(); + } + + /// + /// 测试创建条件 - 验证列名为空 + /// + [Fact] + public void TestCreate_Validate() { + Assert.Throws( () => { + var condition = new GreaterEqualCondition( _parameterManager, "", 1, true ); + } ); + } + + /// + /// 测试获取条件 - 参数化 + /// + [Fact] + public void TestGetCondition_1() { + var condition = new GreaterEqualCondition( _parameterManager, "a", 1, true ); + Assert.Equal( "a>=@_p_0", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试获取条件 - 非参数化 + /// + [Fact] + public void TestGetCondition_2() { + var condition = new GreaterEqualCondition( _parameterManager, "a", "b", false ); + Assert.Equal( "a>=b", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + /// + /// 测试获取条件 - 值为ISqlBuilder + /// + [Fact] + public void TestGetCondition_3() { + var result = new StringBuilder(); + result.Append( "a>=" ); + result.AppendLine( "(Select [a] " ); + result.Append( "From [b])" ); + + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + var condition = new GreaterEqualCondition( _parameterManager, "a", builder, true ); + Assert.Equal( result.ToString(), GetResult( condition ) ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Conditions/InConditionTest.cs b/test/Util.Data.Sql.Tests/Builders/Conditions/InConditionTest.cs new file mode 100644 index 000000000..600714a20 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Conditions/InConditionTest.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Util.Data.Sql.Builders.Conditions; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Conditions; + +/// +/// In查询条件测试 +/// +public class InConditionTest { + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + + /// + /// 测试初始化 + /// + public InConditionTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + } + + /// + /// 获取结果 + /// + private string GetResult( ISqlCondition condition ) { + var result = new StringBuilder(); + condition.AppendTo( result ); + return result.ToString(); + } + + /// + /// 测试创建条件 - 验证列名为空 + /// + [Fact] + public void TestCreate_Validate() { + var parameters = new List { "b" }; + Assert.Throws( () => { + var condition = new InCondition( _parameterManager, "", parameters, true ); + } ); + } + + /// + /// 测试获取条件 - 参数化 - 1个字符串型参数 + /// + [Fact] + public void TestGetCondition_1() { + var parameters = new List { "b" }; + var condition = new InCondition( _parameterManager, "a", parameters, true ); + Assert.Equal( "a In (@_p_0)", GetResult( condition ) ); + Assert.Equal( "b", _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试获取条件 - 参数化 - 1个数值型参数 + /// + [Fact] + public void TestGetCondition_2() { + var parameters = new List { 1 }; + var condition = new InCondition( _parameterManager, "a", parameters, true ); + Assert.Equal( "a In (@_p_0)", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试获取条件 - 参数化 - 2个参数 + /// + [Fact] + public void TestGetCondition_3() { + var parameters = new List { "b", "c" }; + var condition = new InCondition( _parameterManager, "a", parameters, true ); + Assert.Equal( "a In (@_p_0,@_p_1)", GetResult( condition ) ); + Assert.Equal( "b", _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( "c", _parameterManager.GetValue( "@_p_1" ) ); + } + + /// + /// 测试获取条件 - 参数化 - 0个参数 + /// + [Fact] + public void TestGetCondition_4() { + var parameters = new List(); + var condition = new InCondition( _parameterManager, "a", parameters, true ); + Assert.Empty( GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + /// + /// 测试获取条件 - 非参数化 - 1个字符串型参数 + /// + [Fact] + public void TestGetCondition_5() { + var parameters = new List { "b" }; + var condition = new InCondition( _parameterManager, "a", parameters, false ); + Assert.Equal( "a In ('b')", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + /// + /// 测试获取条件 - 非参数化 - 1个数值型参数 + /// + [Fact] + public void TestGetCondition_6() { + var parameters = new List { 1 }; + var condition = new InCondition( _parameterManager, "a", parameters, false ); + Assert.Equal( "a In (1)", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + /// + /// 测试获取条件 - 非参数化 - 2个参数 + /// + [Fact] + public void TestGetCondition_7() { + var parameters = new List { "b", "c" }; + var condition = new InCondition( _parameterManager, "a", parameters, false ); + Assert.Equal( "a In ('b','c')", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + /// + /// 测试获取条件 - 值为ISqlBuilder + /// + [Fact] + public void TestGetCondition_8() { + var result = new StringBuilder(); + result.Append( "a In " ); + result.AppendLine( "(Select [a] " ); + result.Append( "From [b])" ); + + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + var condition = new InCondition( _parameterManager, "a", builder, true ); + Assert.Equal( result.ToString(), GetResult( condition ) ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Conditions/IsNotNullConditionTest.cs b/test/Util.Data.Sql.Tests/Builders/Conditions/IsNotNullConditionTest.cs new file mode 100644 index 000000000..97a4e8bda --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Conditions/IsNotNullConditionTest.cs @@ -0,0 +1,37 @@ +using System.Text; +using Util.Data.Sql.Builders.Conditions; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Conditions; + +/// +/// Is Not Null查询条件测试 +/// +public class IsNotNullConditionTest { + /// + /// 获取结果 + /// + private string GetResult( ISqlCondition condition ) { + var result = new StringBuilder(); + condition.AppendTo( result ); + return result.ToString(); + } + + /// + /// 获取条件 + /// + [Fact] + public void Test_1() { + var condition = new IsNotNullCondition( "Email" ); + Assert.Equal( "Email Is Not Null", GetResult( condition ) ); + } + + /// + /// 获取条件 - 验证列为空 + /// + [Fact] + public void Test_2() { + var condition = new IsNotNullCondition( "" ); + Assert.Empty( GetResult( condition ) ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Conditions/IsNullConditionTest.cs b/test/Util.Data.Sql.Tests/Builders/Conditions/IsNullConditionTest.cs new file mode 100644 index 000000000..6062b4603 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Conditions/IsNullConditionTest.cs @@ -0,0 +1,37 @@ +using System.Text; +using Util.Data.Sql.Builders.Conditions; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Conditions; + +/// +/// Is Null查询条件测试 +/// +public class IsNullConditionTest { + /// + /// 获取结果 + /// + private string GetResult( ISqlCondition condition ) { + var result = new StringBuilder(); + condition.AppendTo( result ); + return result.ToString(); + } + + /// + /// 获取条件 + /// + [Fact] + public void Test_1() { + var condition = new IsNullCondition( "Email" ); + Assert.Equal( "Email Is Null", GetResult( condition ) ); + } + + /// + /// 获取条件 - 验证列为空 + /// + [Fact] + public void Test_2() { + var condition = new IsNullCondition( "" ); + Assert.Empty( GetResult( condition ) ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Conditions/LessConditionTest.cs b/test/Util.Data.Sql.Tests/Builders/Conditions/LessConditionTest.cs new file mode 100644 index 000000000..feb56701b --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Conditions/LessConditionTest.cs @@ -0,0 +1,79 @@ +using System; +using System.Text; +using Util.Data.Sql.Builders.Conditions; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Conditions; + +/// +/// Sql小于查询条件测试 +/// +public class LessConditionTest { + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + + /// + /// 测试初始化 + /// + public LessConditionTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + } + + /// + /// 获取结果 + /// + private string GetResult( ISqlCondition condition ) { + var result = new StringBuilder(); + condition.AppendTo( result ); + return result.ToString(); + } + + /// + /// 测试创建条件 - 验证列名为空 + /// + [Fact] + public void TestCreate_Validate() { + Assert.Throws( () => { + var condition = new LessCondition( _parameterManager, "", 1, true ); + } ); + } + + /// + /// 测试获取条件 - 参数化 + /// + [Fact] + public void TestGetCondition_1() { + var condition = new LessCondition( _parameterManager, "a", 1, true ); + Assert.Equal( "a<@_p_0", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试获取条件 - 非参数化 + /// + [Fact] + public void TestGetCondition_2() { + var condition = new LessCondition( _parameterManager, "a", "b", false ); + Assert.Equal( "a + /// 测试获取条件 - 值为ISqlBuilder + /// + [Fact] + public void TestGetCondition_3() { + var result = new StringBuilder(); + result.Append( "a<" ); + result.AppendLine( "(Select [a] " ); + result.Append( "From [b])" ); + + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + var condition = new LessCondition( _parameterManager, "a", builder, true ); + Assert.Equal( result.ToString(), GetResult( condition ) ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Conditions/LessEqualConditionTest.cs b/test/Util.Data.Sql.Tests/Builders/Conditions/LessEqualConditionTest.cs new file mode 100644 index 000000000..ff3f008a5 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Conditions/LessEqualConditionTest.cs @@ -0,0 +1,79 @@ +using System; +using System.Text; +using Util.Data.Sql.Builders.Conditions; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Conditions; + +/// +/// Sql小于等于查询条件测试 +/// +public class LessEqualConditionTest { + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + + /// + /// 测试初始化 + /// + public LessEqualConditionTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + } + + /// + /// 获取结果 + /// + private string GetResult( ISqlCondition condition ) { + var result = new StringBuilder(); + condition.AppendTo( result ); + return result.ToString(); + } + + /// + /// 测试创建条件 - 验证列名为空 + /// + [Fact] + public void TestCreate_Validate() { + Assert.Throws( () => { + var condition = new LessEqualCondition( _parameterManager, "", 1, true ); + } ); + } + + /// + /// 测试获取条件 - 参数化 + /// + [Fact] + public void TestGetCondition_1() { + var condition = new LessEqualCondition( _parameterManager, "a", 1, true ); + Assert.Equal( "a<=@_p_0", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试获取条件 - 非参数化 + /// + [Fact] + public void TestGetCondition_2() { + var condition = new LessEqualCondition( _parameterManager, "a", "b", false ); + Assert.Equal( "a<=b", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + /// + /// 测试获取条件 - 值为ISqlBuilder + /// + [Fact] + public void TestGetCondition_3() { + var result = new StringBuilder(); + result.Append( "a<=" ); + result.AppendLine( "(Select [a] " ); + result.Append( "From [b])" ); + + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + var condition = new LessEqualCondition( _parameterManager, "a", builder, true ); + Assert.Equal( result.ToString(), GetResult( condition ) ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Conditions/NotEqualConditionTest.cs b/test/Util.Data.Sql.Tests/Builders/Conditions/NotEqualConditionTest.cs new file mode 100644 index 000000000..010373700 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Conditions/NotEqualConditionTest.cs @@ -0,0 +1,89 @@ +using System; +using System.Text; +using Util.Data.Sql.Builders.Conditions; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Conditions; + +/// +/// Sql不相等查询条件测试 +/// +public class NotEqualConditionTest { + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + + /// + /// 测试初始化 + /// + public NotEqualConditionTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + } + + /// + /// 获取结果 + /// + private string GetResult( ISqlCondition condition ) { + var result = new StringBuilder(); + condition.AppendTo( result ); + return result.ToString(); + } + + /// + /// 测试创建条件 - 验证列名为空 + /// + [Fact] + public void TestCreate_Validate() { + Assert.Throws( () => { + var condition = new NotEqualCondition( _parameterManager, "", 1, true ); + } ); + } + + /// + /// 测试获取条件 - 参数化 + /// + [Fact] + public void TestGetCondition_1() { + var condition = new NotEqualCondition( _parameterManager, "a", 1, true ); + Assert.Equal( "a<>@_p_0", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试获取条件 - 非参数化 + /// + [Fact] + public void TestGetCondition_2() { + var condition = new NotEqualCondition( _parameterManager, "a", "b", false ); + Assert.Equal( "a<>b", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + /// + /// 测试获取条件 - 参数值为null,则输出 is null + /// + [Fact] + public void TestGetCondition_3() { + var condition = new NotEqualCondition( _parameterManager, "a", null, true ); + Assert.Equal( "a Is Not Null", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + /// + /// 测试获取条件 - 值为ISqlBuilder + /// + [Fact] + public void TestGetCondition_4() { + var result = new StringBuilder(); + result.Append( "a<>" ); + result.AppendLine( "(Select [a] " ); + result.Append( "From [b])" ); + + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + var condition = new NotEqualCondition( _parameterManager, "a", builder, true ); + Assert.Equal( result.ToString(), GetResult( condition ) ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Conditions/NotExistsConditionTest.cs b/test/Util.Data.Sql.Tests/Builders/Conditions/NotExistsConditionTest.cs new file mode 100644 index 000000000..6564dec93 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Conditions/NotExistsConditionTest.cs @@ -0,0 +1,43 @@ +using System.Text; +using Util.Data.Sql.Builders.Conditions; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Conditions; + +/// +/// Not Exists查询条件测试 +/// +public class NotExistsConditionTest { + /// + /// 获取结果 + /// + private string GetResult( ISqlCondition condition ) { + var result = new StringBuilder(); + condition.AppendTo( result ); + return result.ToString(); + } + + /// + /// 测试Not Exists查询条件 + /// + [Fact] + public void Test() { + //结果 + var result = new StringBuilder(); + result.Append( "Not Exists (" ); + result.AppendLine( "Select [Name] " ); + result.Append( "From [t]" ); + result.Append( ")" ); + + //子查询 + var subBuilder = new TestSqlBuilder(); + subBuilder.Select( "Name" ).From( "t" ); + + //创建条件 + var condition = new NotExistsCondition( subBuilder ); + + //验证 + Assert.Equal( result.ToString(), GetResult( condition ) ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Conditions/NotInConditionTest.cs b/test/Util.Data.Sql.Tests/Builders/Conditions/NotInConditionTest.cs new file mode 100644 index 000000000..4d78a4eb4 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Conditions/NotInConditionTest.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Util.Data.Sql.Builders.Conditions; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Conditions; + +/// +/// Not In查询条件测试 +/// +public class NotInConditionTest { + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + + /// + /// 测试初始化 + /// + public NotInConditionTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + } + + /// + /// 获取结果 + /// + private string GetResult( ISqlCondition condition ) { + var result = new StringBuilder(); + condition.AppendTo( result ); + return result.ToString(); + } + + /// + /// 测试创建条件 - 验证列名为空 + /// + [Fact] + public void TestCreate_Validate() { + var parameters = new List { "b" }; + Assert.Throws( () => { + var condition = new NotInCondition( _parameterManager, "", parameters, true ); + } ); + } + + /// + /// 测试获取条件 - 参数化 - 1个字符串型参数 + /// + [Fact] + public void TestGetCondition_1() { + var parameters = new List { "b" }; + var condition = new NotInCondition( _parameterManager, "a", parameters, true ); + Assert.Equal( "a Not In (@_p_0)", GetResult( condition ) ); + Assert.Equal( "b", _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试获取条件 - 参数化 - 1个数值型参数 + /// + [Fact] + public void TestGetCondition_2() { + var parameters = new List { 1 }; + var condition = new NotInCondition( _parameterManager, "a", parameters, true ); + Assert.Equal( "a Not In (@_p_0)", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试获取条件 - 参数化 - 2个参数 + /// + [Fact] + public void TestGetCondition_3() { + var parameters = new List { "b", "c" }; + var condition = new NotInCondition( _parameterManager, "a", parameters, true ); + Assert.Equal( "a Not In (@_p_0,@_p_1)", GetResult( condition ) ); + Assert.Equal( "b", _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( "c", _parameterManager.GetValue( "@_p_1" ) ); + } + + /// + /// 测试获取条件 - 参数化 - 0个参数 + /// + [Fact] + public void TestGetCondition_4() { + var parameters = new List(); + var condition = new NotInCondition( _parameterManager, "a", parameters, true ); + Assert.Empty( GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + /// + /// 测试获取条件 - 非参数化 - 1个字符串型参数 + /// + [Fact] + public void TestGetCondition_5() { + var parameters = new List { "b" }; + var condition = new NotInCondition( _parameterManager, "a", parameters, false ); + Assert.Equal( "a Not In ('b')", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + /// + /// 测试获取条件 - 非参数化 - 1个数值型参数 + /// + [Fact] + public void TestGetCondition_6() { + var parameters = new List { 1 }; + var condition = new NotInCondition( _parameterManager, "a", parameters, false ); + Assert.Equal( "a Not In (1)", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + /// + /// 测试获取条件 - 非参数化 - 2个参数 + /// + [Fact] + public void TestGetCondition_7() { + var parameters = new List { "b", "c" }; + var condition = new NotInCondition( _parameterManager, "a", parameters, false ); + Assert.Equal( "a Not In ('b','c')", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + /// + /// 测试获取条件 - 值为ISqlBuilder + /// + [Fact] + public void TestGetCondition_8() { + var result = new StringBuilder(); + result.Append( "a Not In " ); + result.AppendLine( "(Select [a] " ); + result.Append( "From [b])" ); + + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + var condition = new NotInCondition( _parameterManager, "a", builder, true ); + Assert.Equal( result.ToString(), GetResult( condition ) ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Conditions/OrConditionTest.cs b/test/Util.Data.Sql.Tests/Builders/Conditions/OrConditionTest.cs new file mode 100644 index 000000000..0e0e8c5e7 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Conditions/OrConditionTest.cs @@ -0,0 +1,103 @@ +using System.Text; +using Util.Data.Sql.Builders.Conditions; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Conditions; + +/// +/// Or连接条件测试 +/// +public class OrConditionTest { + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + + /// + /// 测试初始化 + /// + public OrConditionTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + } + + /// + /// 测试或连接条件 - 构造器传入1个条件 + /// + [Fact] + public void Test_1() { + var result = new StringBuilder(); + var condition1 = new EqualCondition( _parameterManager, "Name", "@Name", false ); + condition1.AppendTo( result ); + var condition2 = new GreaterCondition( _parameterManager, "Age", "@Age", false ); + var orCondition = new OrCondition( condition2 ); + orCondition.AppendTo( result ); + Assert.Equal( "(Name=@Name Or Age>@Age)", result.ToString() ); + } + + /// + /// 测试或连接条件 - 构造器传入2个条件 + /// + [Fact] + public void Test_2() { + var result = new StringBuilder(); + var condition1 = new EqualCondition( _parameterManager, "Name", "@Name", false ); + condition1.AppendTo( result ); + var condition2 = new EqualCondition( _parameterManager, "Code", "@Code", false ); + var condition3 = new GreaterCondition( _parameterManager, "Age", "@Age", false ); + var orCondition = new OrCondition( condition2, condition3 ); + orCondition.AppendTo( result ); + Assert.Equal( "Name=@Name And (Code=@Code Or Age>@Age)", result.ToString() ); + } + + /// + /// 测试或连接条件 - 第2个条件为空条件 + /// + [Fact] + public void Test_3() { + var result = new StringBuilder(); + var condition1 = new EqualCondition( _parameterManager, "Name", "@Name", false ); + condition1.AppendTo( result ); + var condition2 = new EqualCondition( _parameterManager, "Code", "@Code", false ); + var orCondition = new OrCondition( condition2, NullCondition.Instance ); + orCondition.AppendTo( result ); + Assert.Equal( "(Name=@Name Or Code=@Code)", result.ToString() ); + } + + /// + /// 测试条件1为空 + /// + [Fact] + public void Test_4() { + var result = new StringBuilder(); + var condition2 = new GreaterCondition( _parameterManager, "Age", "@Age", false ); + var orCondition = new OrCondition( condition2 ); + orCondition.AppendTo( result ); + Assert.Equal( "Age>@Age", result.ToString() ); + } + + /// + /// 测试条件2为空 + /// + [Fact] + public void Test_5() { + var result = new StringBuilder(); + var condition1 = new EqualCondition( _parameterManager, "Name", "@Name", false ); + condition1.AppendTo( result ); + var orCondition = new OrCondition( null ); + orCondition.AppendTo( result ); + Assert.Equal( "Name=@Name", result.ToString() ); + } + + /// + /// 测试条件都为空 + /// + [Fact] + public void Test_6() { + var result = new StringBuilder(); + var orCondition = new OrCondition( null ); + orCondition.AppendTo( result ); + Assert.Empty( result.ToString() ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Conditions/SegmentConditionTest.cs b/test/Util.Data.Sql.Tests/Builders/Conditions/SegmentConditionTest.cs new file mode 100644 index 000000000..ba21e54c8 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Conditions/SegmentConditionTest.cs @@ -0,0 +1,119 @@ +using System; +using System.Text; +using Util.Data.Queries; +using Util.Data.Sql.Builders.Conditions; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Conditions; + +/// +/// 范围过滤条件测试 +/// +public class SegmentConditionTest { + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + + /// + /// 测试初始化 + /// + public SegmentConditionTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + } + + /// + /// 获取结果 + /// + private string GetResult( ISqlCondition condition ) { + var result = new StringBuilder(); + condition.AppendTo( result ); + return result.ToString(); + } + + /// + /// 测试创建条件 - 验证列名为空 + /// + [Fact] + public void TestCreate_Validate() { + Assert.Throws( () => { + var condition = new SegmentCondition( _parameterManager, "", 1,2,Boundary.Left, true ); + } ); + } + + /// + /// 测试获取条件 - 参数化 - 包含两边边界 + /// + [Fact] + public void TestGetCondition_1() { + var condition = new SegmentCondition( _parameterManager, "a", 1, 2, Boundary.Both, true ); + Assert.Equal( "a>=@_p_0 And a<=@_p_1", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( 2, _parameterManager.GetValue( "@_p_1" ) ); + } + + /// + /// 测试获取条件 - 参数化 - 包含左边边界 + /// + [Fact] + public void TestGetCondition_2() { + var condition = new SegmentCondition( _parameterManager, "a", 1, 2, Boundary.Left, true ); + Assert.Equal( "a>=@_p_0 And a<@_p_1", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( 2, _parameterManager.GetValue( "@_p_1" ) ); + } + + /// + /// 测试获取条件 - 参数化 - 包含右边边界 + /// + [Fact] + public void TestGetCondition_3() { + var condition = new SegmentCondition( _parameterManager, "a", 1, 2, Boundary.Right, true ); + Assert.Equal( "a>@_p_0 And a<=@_p_1", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( 2, _parameterManager.GetValue( "@_p_1" ) ); + } + + /// + /// 测试获取条件 - 参数化 - 不包含边界 + /// + [Fact] + public void TestGetCondition_4() { + var condition = new SegmentCondition( _parameterManager, "a", 1, 2, Boundary.Neither, true ); + Assert.Equal( "a>@_p_0 And a<@_p_1", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( 2, _parameterManager.GetValue( "@_p_1" ) ); + } + + /// + /// 测试获取条件 - 参数化 - 没有最大值 + /// + [Fact] + public void TestGetCondition_5() { + var condition = new SegmentCondition( _parameterManager, "a", 1, null, Boundary.Both, true ); + Assert.Equal( "a>=@_p_0", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试获取条件 - 参数化 - 没有最小值 + /// + [Fact] + public void TestGetCondition_6() { + var condition = new SegmentCondition( _parameterManager, "a", null, 1, Boundary.Both, true ); + Assert.Equal( "a<=@_p_0", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试获取条件 - 非参数化 + /// + [Fact] + public void TestGetCondition_7() { + var condition = new SegmentCondition( _parameterManager, "a", 1, 2, Boundary.Both, false ); + Assert.Equal( "a>=1 And a<=2", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Conditions/StartsConditionTest.cs b/test/Util.Data.Sql.Tests/Builders/Conditions/StartsConditionTest.cs new file mode 100644 index 000000000..aab6cecce --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Conditions/StartsConditionTest.cs @@ -0,0 +1,64 @@ +using System; +using System.Text; +using Util.Data.Sql.Builders.Conditions; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Conditions; + +/// +/// Sql头匹配查询条件测试 +/// +public class StartsConditionTest { + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + + /// + /// 测试初始化 + /// + public StartsConditionTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + } + + /// + /// 获取结果 + /// + private string GetResult( ISqlCondition condition ) { + var result = new StringBuilder(); + condition.AppendTo( result ); + return result.ToString(); + } + + /// + /// 测试创建条件 - 验证列名为空 + /// + [Fact] + public void TestCreate_Validate() { + Assert.Throws( () => { + var condition = new StartsCondition( _parameterManager, "", 1, true ); + } ); + } + + /// + /// 测试获取条件 - 参数化 + /// + [Fact] + public void TestGetCondition_1() { + var condition = new StartsCondition( _parameterManager, "a", "b", true ); + Assert.Equal( "a Like @_p_0", GetResult( condition ) ); + Assert.Equal( "b%", _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试获取条件 - 非参数化 + /// + [Fact] + public void TestGetCondition_2() { + var condition = new StartsCondition( _parameterManager, "a", "b", false ); + Assert.Equal( "a Like 'b%'", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Core/ColumnItemTest.cs b/test/Util.Data.Sql.Tests/Builders/Core/ColumnItemTest.cs new file mode 100644 index 000000000..e08141100 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Core/ColumnItemTest.cs @@ -0,0 +1,164 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Core; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Core; + +/// +/// 列测试 +/// +public class ColumnItemTest { + /// + /// Sql方言 + /// + private readonly IDialect _dialect; + + /// + /// 测试初始化 + /// + public ColumnItemTest() { + _dialect = TestDialect.Instance; + } + + /// + /// 测试解析简单列名 + /// + [Fact] + public void Test_1() { + var item = new ColumnItem( _dialect,"a" ); + Assert.Null( item.TableAlias ); + Assert.Equal( "a", item.Name ); + } + + /// + /// 测试解析带表别名的列 + /// + [Fact] + public void Test_2() { + var item = new ColumnItem( _dialect, "a.b" ); + Assert.Equal( "a", item.TableAlias ); + Assert.Equal( "b", item.Name ); + } + + /// + /// 测试解析带表别名和列别名的列 - 使用as关键字 + /// + [Fact] + public void Test_3() { + var item = new ColumnItem( _dialect, "a.b as c" ); + Assert.Equal( "a", item.TableAlias ); + Assert.Equal( "b", item.Name ); + Assert.Equal( "c", item.ColumnAlias ); + } + + /// + /// 测试解析带表别名和列别名的列 - 使用As关键字 + /// + [Fact] + public void Test_4() { + var item = new ColumnItem( _dialect, "a.b As c" ); + Assert.Equal( "a", item.TableAlias ); + Assert.Equal( "b", item.Name ); + Assert.Equal( "c", item.ColumnAlias ); + } + + /// + /// 测试解析带表别名和列别名的列 - 使用空格 + /// + [Fact] + public void Test_5() { + var item = new ColumnItem( _dialect, "a.b c" ); + Assert.Equal( "a", item.TableAlias ); + Assert.Equal( "b", item.Name ); + Assert.Equal( "c", item.ColumnAlias ); + } + + /// + /// 测试.前有空格 + /// + [Fact] + public void Test_6() { + var item = new ColumnItem( _dialect, "a .b" ); + Assert.Equal( "a", item.TableAlias ); + Assert.Equal( "b", item.Name ); + } + + /// + /// 测试.后有空格 + /// + [Fact] + public void Test_7() { + var item = new ColumnItem( _dialect, "a. b" ); + Assert.Equal( "a", item.TableAlias ); + Assert.Equal( "b", item.Name ); + } + + /// + /// 测试.后有空格 + /// + [Fact] + public void Test_8() { + var item = new ColumnItem( _dialect, "a . b" ); + Assert.Equal( "a", item.TableAlias ); + Assert.Equal( "b", item.Name ); + } + + /// + /// 测试多个空格 + /// + [Fact] + public void Test_9() { + var item = new ColumnItem( _dialect, " a . b As c " ); + Assert.Equal( "a", item.TableAlias ); + Assert.Equal( "b", item.Name ); + Assert.Equal( "c", item.ColumnAlias ); + } + + /// + /// 测试添加了转义符 + /// + [Fact] + public void Test_10() { + var item = new ColumnItem( _dialect, "[a].[b] as [c]" ); + Assert.Equal( "[a]", item.TableAlias ); + Assert.Equal( "[b]", item.Name ); + Assert.Equal( "[c]", item.ColumnAlias ); + } + + /// + /// 测试获取Sql结果 - 简单列名 + /// + [Fact] + public void TestToSql_1() { + var item = new ColumnItem( _dialect, "a" ); + Assert.Equal( "[a]", item.ToResult() ); + } + + /// + /// 测试获取Sql结果 - 带表别名的列 + /// + [Fact] + public void TestToSql_2() { + var item = new ColumnItem( _dialect, "a.b" ); + Assert.Equal( "[a].[b]", item.ToResult() ); + } + + /// + /// 测试获取Sql结果 - 带表别名和列别名的列 + /// + [Fact] + public void TestToSql_3() { + var item = new ColumnItem( _dialect, "a.b as c" ); + Assert.Equal( "[a].[b] As [c]", item.ToResult() ); + } + + /// + /// 测试获取Sql结果 - 添加了转义符和多余的空格 + /// + [Fact] + public void TestToSql_4() { + var item = new ColumnItem( _dialect, " [a] . [b] as [c] " ); + Assert.Equal( "[a].[b] As [c]", item.ToResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Core/OrderByItemTest.cs b/test/Util.Data.Sql.Tests/Builders/Core/OrderByItemTest.cs new file mode 100644 index 000000000..7cb6534a7 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Core/OrderByItemTest.cs @@ -0,0 +1,105 @@ +using Util.Data.Sql.Builders.Core; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Core; + +/// +/// 排序项测试 +/// +public class OrderByItemTest { + /// + /// 测试初始化 + /// + public OrderByItemTest() { + } + + /// + /// 测试解析排序列 + /// + [Fact] + public void Test_1() { + var item = new OrderByItem( "a.B" ); + Assert.Equal( "a.B", item.Column ); + Assert.False( item.Desc ); + } + + /// + /// 测试解析排序列 - 前后有空格 + /// + [Fact] + public void Test_2() { + var item = new OrderByItem( " a.B " ); + Assert.Equal( "a.B", item.Column ); + Assert.False( item.Desc ); + } + + /// + /// 测试解析排序列 - 带ASC + /// + [Fact] + public void Test_3() { + var item = new OrderByItem( "a.B Asc" ); + Assert.Equal( "a.B", item.Column ); + Assert.False( item.Desc ); + } + + /// + /// 测试解析排序列 - 列名为ASC + /// + [Fact] + public void Test_4() { + var item = new OrderByItem( "Asc" ); + Assert.Equal( "Asc", item.Column ); + Assert.False( item.Desc ); + } + + /// + /// 测试解析排序列 - 列名为ASC,前后有空格 + /// + [Fact] + public void Test_5() { + var item = new OrderByItem( " Asc " ); + Assert.Equal( "Asc", item.Column ); + Assert.False( item.Desc ); + } + + /// + /// 测试解析排序列 - 降序 + /// + [Fact] + public void Test_6() { + var item = new OrderByItem( "a.B Desc" ); + Assert.Equal( "a.B", item.Column ); + Assert.True( item.Desc ); + } + + /// + /// 测试解析排序列 - 降序,前后添加空格 + /// + [Fact] + public void Test_7() { + var item = new OrderByItem( " a.B desc " ); + Assert.Equal( "a.B", item.Column ); + Assert.True( item.Desc ); + } + + /// + /// 测试解析排序列 - 列名为Desc + /// + [Fact] + public void Test_8() { + var item = new OrderByItem( "Desc" ); + Assert.Equal( "Desc", item.Column ); + Assert.False( item.Desc ); + } + + /// + /// 测试解析排序列 - 列名为Desc,前后有空格 + /// + [Fact] + public void Test_9() { + var item = new OrderByItem( " Desc " ); + Assert.Equal( "Desc", item.Column ); + Assert.False( item.Desc ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Core/SplitItemTest.cs b/test/Util.Data.Sql.Tests/Builders/Core/SplitItemTest.cs new file mode 100644 index 000000000..f303d5bde --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Core/SplitItemTest.cs @@ -0,0 +1,69 @@ +using Util.Data.Sql.Builders.Core; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Core; + +/// +/// 拆分项测试 +/// +public class SplitItemTest { + /// + /// 测试设置单个字符 + /// + [Fact] + public void Test_1() { + var item = new SplitItem( "a" ); + Assert.Equal( "a", item.Left ); + Assert.Null( item.Right ); + } + + /// + /// 测试设置空字符 + /// + [Fact] + public void Test_2() { + var item = new SplitItem( "" ); + Assert.Null( item.Left ); + Assert.Null( item.Right ); + } + + /// + /// 测试设置null + /// + [Fact] + public void Test_3() { + var item = new SplitItem( null ); + Assert.Null( item.Left ); + Assert.Null( item.Right ); + } + + /// + /// 测试默认使用空格拆分 + /// + [Fact] + public void Test_4() { + var item = new SplitItem( "a b" ); + Assert.Equal( "a", item.Left ); + Assert.Equal( "b", item.Right ); + } + + /// + /// 测试设置.进行拆分 + /// + [Fact] + public void Test_5() { + var item = new SplitItem( "a.b","." ); + Assert.Equal( "a", item.Left ); + Assert.Equal( "b", item.Right ); + } + + /// + /// 测试去掉两端空格 + /// + [Fact] + public void Test_6() { + var item = new SplitItem( " a.b ", "." ); + Assert.Equal( "a", item.Left ); + Assert.Equal( "b", item.Right ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Core/SqlBuilderItemTest.cs b/test/Util.Data.Sql.Tests/Builders/Core/SqlBuilderItemTest.cs new file mode 100644 index 000000000..fe59e1d38 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Core/SqlBuilderItemTest.cs @@ -0,0 +1,43 @@ +using System.Text; +using Util.Data.Sql.Builders.Core; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Core; + +/// +/// Sql生成器项测试 +/// +public class SqlBuilderItemTest { + /// + /// 测试带别名 + /// + [Fact] + public void Test_1() { + var result = new StringBuilder(); + result.AppendLine( "(Select [a] " ); + result.Append( "From [b]) As [c]" ); + + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + var item = new SqlBuilderItem( TestDialect.Instance, builder, "c" ); + var sql = new StringBuilder(); + item.AppendTo( sql ); + Assert.Equal( result.ToString(), sql.ToString() ); + } + + /// + /// 测试不带别名 + /// + [Fact] + public void Test_2() { + var result = new StringBuilder(); + result.AppendLine( "(Select [a] " ); + result.Append( "From [b])" ); + + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + var item = new SqlBuilderItem( TestDialect.Instance, builder ); + var sql = new StringBuilder(); + item.AppendTo( sql ); + Assert.Equal( result.ToString(), sql.ToString() ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Core/SqlConditionFactoryTest.cs b/test/Util.Data.Sql.Tests/Builders/Core/SqlConditionFactoryTest.cs new file mode 100644 index 000000000..a6d8f2af7 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Core/SqlConditionFactoryTest.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Core; + +/// +/// Sql条件工厂测试 +/// +public class SqlConditionFactoryTest { + /// + /// 参数管理器 + /// + private readonly IParameterManager _parameterManager; + /// + /// Sql条件工厂 + /// + private readonly SqlConditionFactory _conditionFactory; + + /// + /// 测试初始化 + /// + public SqlConditionFactoryTest() { + _parameterManager = new ParameterManager( TestDialect.Instance ); + _conditionFactory = new SqlConditionFactory( _parameterManager ); + } + + /// + /// 获取结果 + /// + private string GetResult( ISqlCondition condition ) { + var result = new StringBuilder(); + condition.AppendTo( result ); + return result.ToString(); + } + + /// + /// 测试创建条件 - 验证列名为空 + /// + [Fact] + public void TestCreate_Validate() { + Assert.Throws( () => { + _conditionFactory.Create( "", 1, Operator.Equal ); + } ); + } + + /// + /// 测试创建条件 - 相等 + /// + [Fact] + public void TestCreate_1() { + var condition = _conditionFactory.Create( "a", 1, Operator.Equal ); + Assert.Equal( "a=@_p_0", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试创建条件 - 不相等 + /// + [Fact] + public void TestCreate_2() { + var condition = _conditionFactory.Create( "a", 1, Operator.NotEqual ); + Assert.Equal( "a<>@_p_0", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试创建条件 - is null + /// + [Fact] + public void TestCreate_3() { + var condition = _conditionFactory.Create( "a", null, Operator.Equal ); + Assert.Equal( "a Is Null", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + /// + /// 测试创建条件 - is not null + /// + [Fact] + public void TestCreate_4() { + var condition = _conditionFactory.Create( "a", null, Operator.NotEqual ); + Assert.Equal( "a Is Not Null", GetResult( condition ) ); + Assert.Equal( 0, _parameterManager.GetParams().Count ); + } + + /// + /// 测试创建条件 - 大于 + /// + [Fact] + public void TestCreate_5() { + var condition = _conditionFactory.Create( "a", 1, Operator.Greater ); + Assert.Equal( "a>@_p_0", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试创建条件 - 大于等于 + /// + [Fact] + public void TestCreate_6() { + var condition = _conditionFactory.Create( "a", 1, Operator.GreaterEqual ); + Assert.Equal( "a>=@_p_0", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试创建条件 - 小于 + /// + [Fact] + public void TestCreate_7() { + var condition = _conditionFactory.Create( "a", 1, Operator.Less ); + Assert.Equal( "a<@_p_0", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试创建条件 - 小于等于 + /// + [Fact] + public void TestCreate_8() { + var condition = _conditionFactory.Create( "a", 1, Operator.LessEqual ); + Assert.Equal( "a<=@_p_0", GetResult( condition ) ); + Assert.Equal( 1, _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试创建条件 - 模糊匹配 + /// + [Fact] + public void TestCreate_9() { + var condition = _conditionFactory.Create( "a", "abc", Operator.Contains ); + Assert.Equal( "a Like @_p_0", GetResult( condition ) ); + Assert.Equal( "%abc%", _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试创建条件 - 头匹配 + /// + [Fact] + public void TestCreate_10() { + var condition = _conditionFactory.Create( "a", "abc", Operator.Starts ); + Assert.Equal( "a Like @_p_0", GetResult( condition ) ); + Assert.Equal( "abc%", _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试创建条件 - 尾匹配 + /// + [Fact] + public void TestCreate_11() { + var condition = _conditionFactory.Create( "a", "abc", Operator.Ends ); + Assert.Equal( "a Like @_p_0", GetResult( condition ) ); + Assert.Equal( "%abc", _parameterManager.GetValue( "@_p_0" ) ); + } + + /// + /// 测试创建条件 - in + /// + [Fact] + public void TestCreate_12() { + var list = new List { "1", "2" }; + var condition = _conditionFactory.Create( "a", list, Operator.In ); + Assert.Equal( "a In (@_p_0,@_p_1)", GetResult( condition ) ); + Assert.Equal( 2, _parameterManager.GetParams().Count ); + Assert.Equal( "1", _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( "2", _parameterManager.GetValue( "@_p_1" ) ); + } + + /// + /// 测试创建条件 - not in + /// + [Fact] + public void TestCreate_13() { + var list = new List { "1", "2" }; + var condition = _conditionFactory.Create( "a", list, Operator.NotIn ); + Assert.Equal( "a Not In (@_p_0,@_p_1)", GetResult( condition ) ); + Assert.Equal( 2, _parameterManager.GetParams().Count ); + Assert.Equal( "1", _parameterManager.GetValue( "@_p_0" ) ); + Assert.Equal( "2", _parameterManager.GetValue( "@_p_1" ) ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Core/TableItemTest.cs b/test/Util.Data.Sql.Tests/Builders/Core/TableItemTest.cs new file mode 100644 index 000000000..be4ea2bae --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Core/TableItemTest.cs @@ -0,0 +1,135 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Core; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Core; + +/// +/// 表测试 +/// +public class TableItemTest { + /// + /// Sql方言 + /// + private readonly IDialect _dialect; + + /// + /// 测试初始化 + /// + public TableItemTest() { + _dialect = TestDialect.Instance; + } + + /// + /// 测试解析简单表名 + /// + [Fact] + public void Test_1() { + var item = new TableItem( _dialect,"a" ); + Assert.Null( item.Schema ); + Assert.Equal( "a", item.Name ); + Assert.Null( item.TableAlias ); + } + + /// + /// 测试解析带架构的表 + /// + [Fact] + public void Test_2() { + var item = new TableItem( _dialect, "a.b" ); + Assert.Equal( "a", item.Schema ); + Assert.Equal( "b", item.Name ); + Assert.Null( item.TableAlias ); + } + + /// + /// 测试解析带架构和表别名的表 - 使用as关键字 + /// + [Fact] + public void Test_3() { + var item = new TableItem( _dialect, "a.b as c" ); + Assert.Equal( "a", item.Schema ); + Assert.Equal( "b", item.Name ); + Assert.Equal( "c", item.TableAlias ); + } + + /// + /// 测试解析带架构和表别名的表 - 使用空格 + /// + [Fact] + public void Test_4() { + var item = new TableItem( _dialect, "a.b c" ); + Assert.Equal( "a", item.Schema ); + Assert.Equal( "b", item.Name ); + Assert.Equal( "c", item.TableAlias ); + } + + /// + /// 测试.前后有空格 + /// + [Fact] + public void Test_5() { + var item = new TableItem( _dialect, "a . b" ); + Assert.Equal( "a", item.Schema ); + Assert.Equal( "b", item.Name ); + } + + /// + /// 测试多个空格 + /// + [Fact] + public void Test_6() { + var item = new TableItem( _dialect, " a . b As c " ); + Assert.Equal( "a", item.Schema ); + Assert.Equal( "b", item.Name ); + Assert.Equal( "c", item.TableAlias ); + } + + /// + /// 测试添加了转义符 + /// + [Fact] + public void Test_7() { + var item = new TableItem( _dialect, "[a].[b] as [c]" ); + Assert.Equal( "[a]", item.Schema ); + Assert.Equal( "[b]", item.Name ); + Assert.Equal( "[c]", item.TableAlias ); + } + + /// + /// 测试获取Sql结果 - 简单表名 + /// + [Fact] + public void TestToResult_1() { + var item = new TableItem( _dialect, "a" ); + Assert.Equal( "[a]", item.ToResult() ); + } + + /// + /// 测试获取Sql结果 - 带架构的表 + /// + [Fact] + public void TestToResult_2() { + var item = new TableItem( _dialect, "a.b" ); + Assert.Equal( "[a].[b]", item.ToResult() ); + } + + /// + /// 测试获取Sql结果 - 带架构和表别名的表 + /// + [Fact] + public void TestToResult_3() { + var item = new TableItem( _dialect, "a.b as c" ); + Assert.Equal( "[a].[b] [c]", item.ToResult() ); + } + + /// + /// 测试获取Sql结果 - 添加了转义符和多余的空格 + /// + [Fact] + public void TestToResult_4() { + var item = new TableItem( _dialect, " [a] . [b] as [c] " ); + Assert.Equal( "[a].[b] [c]", item.ToResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Params/ParameterManagerTest.cs b/test/Util.Data.Sql.Tests/Builders/Params/ParameterManagerTest.cs new file mode 100644 index 000000000..6bfea2cba --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Params/ParameterManagerTest.cs @@ -0,0 +1,187 @@ +using Util.Data.Sql.Builders.Params; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Params; + +/// +/// Sql参数管理器测试 +/// +public class ParameterManagerTest { + + #region 测试初始化 + + /// + /// Sql参数管理器 + /// + private readonly ParameterManager _manager; + + /// + /// 测试初始化 + /// + public ParameterManagerTest() { + _manager = new ParameterManager( TestDialect.Instance ); + } + + #endregion + + #region GenerateName + + /// + /// 测试创建参数名 + /// + [Fact] + public void TestGenerateName() { + Assert.Equal( "@_p_0", _manager.GenerateName() ); + Assert.Equal( "@_p_1", _manager.GenerateName() ); + } + + #endregion + + #region Contains + + /// + /// 测试是否包含参数 + /// + [Fact] + public void TestContains_1() { + _manager.Add( "a", 1 ); + Assert.True( _manager.Contains("a") ); + Assert.True( _manager.Contains( "@a" ) ); + Assert.False( _manager.Contains( "b" ) ); + } + + /// + /// 测试是否包含参数 + /// + [Fact] + public void TestContains_2() { + _manager.Add( "@a", 1 ); + Assert.True( _manager.Contains( "a" ) ); + Assert.True( _manager.Contains( "@a" ) ); + Assert.False( _manager.Contains( "b" ) ); + } + + #endregion + + #region GetParams + + /// + /// 测试获取参数列表 + /// + [Fact] + public void TestGetParams() { + var parameters = _manager.GetParams(); + Assert.Equal( 0, parameters.Count ); + } + + #endregion + + #region Add + + /// + /// 测试添加1个参数 + /// + [Fact] + public void TestAdd_1() { + _manager.Add( "a", 1 ); + var parameters = _manager.GetParams(); + Assert.Equal( 1, parameters.Count ); + Assert.Equal( 1, _manager.GetValue( "a" ) ); + } + + /// + /// 测试添加2个参数 + /// + [Fact] + public void TestAdd_2() { + _manager.Add( "a", 1 ); + _manager.Add( "b", 2 ); + var parameters = _manager.GetParams(); + Assert.Equal( 2, parameters.Count ); + Assert.Equal( 1, _manager.GetValue( "a" ) ); + Assert.Equal( 2, _manager.GetValue( "b" ) ); + } + + /// + /// 测试覆盖参数 + /// + [Fact] + public void TestAdd_3() { + _manager.Add( "a", 1 ); + _manager.Add( "a", 2 ); + var parameters = _manager.GetParams(); + Assert.Equal( 1, parameters.Count ); + Assert.Equal( 2, _manager.GetValue( "a" ) ); + } + + /// + /// 测试添加参数 - 参数名为空 + /// + [Fact] + public void TestAdd_4() { + _manager.Add( "", 1 ); + var parameters = _manager.GetParams(); + Assert.Equal( 0, parameters.Count ); + } + + #endregion + + #region AddDynamicParams + + /// + /// 测试添加1个动态参数 + /// + [Fact] + public void TestAddDynamicParams() { + _manager.AddDynamicParams( new { A = 2 } ); + var parameters = _manager.GetDynamicParams(); + Assert.Equal( 1, parameters.Count ); + Assert.Equal( 2, ( (dynamic)parameters[0] ).A ); + } + + #endregion + + #region Clear + + /// + /// 测试清空参数 + /// + [Fact] + public void TestClear() { + var paramName = _manager.GenerateName(); + _manager.Add( paramName, 1 ); + _manager.Clear(); + var parameters = _manager.GetParams(); + Assert.Equal( 0, parameters.Count ); + Assert.Equal( "@_p_0", _manager.GenerateName() ); + } + + #endregion + + #region Clone + + /// + /// 测试复制Sql参数管理器副本 - 参数 + /// + [Fact] + public void TestClone_Param() { + _manager.Add( "name", "a" ); + var clone = _manager.Clone(); + Assert.Equal( "a", clone.GetValue( "name" ) ); + } + + /// + /// 测试复制Sql参数管理器副本 - 动态参数 + /// + [Fact] + public void TestClone_DynamicParam() { + var parameter = new TestParameter { Code = "1" }; + _manager.AddDynamicParams( parameter ); + var clone = _manager.Clone(); + var result = (TestParameter)clone.GetDynamicParams()[0]; + Assert.Equal( "1", result.Code ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/Sets/SqlBuilderSetTest.cs b/test/Util.Data.Sql.Tests/Builders/Sets/SqlBuilderSetTest.cs new file mode 100644 index 000000000..fad71f7c0 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/Sets/SqlBuilderSetTest.cs @@ -0,0 +1,361 @@ +using System.Text; +using Util.Data.Sql.Builders.Sets; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders.Sets; + +/// +/// Sql生成器集合测试 +/// +public class SqlBuilderSetTest { + + #region 测试初始化 + + /// + /// Sql生成器集合 + /// + private readonly SqlBuilderSet _set; + + /// + /// 测试初始化 + /// + public SqlBuilderSetTest() { + var builder = CreateMasterSqlBuilder(); + _set = new SqlBuilderSet( builder ); + } + + /// + /// 创建主Sql生成器 + /// + private TestSqlBuilder CreateMasterSqlBuilder() { + return new TestSqlBuilder() + .Select( "a" ) + .From( "b" ) + .Join( "c" ).On( "b.id","c.id" ) + .AppendWhere( "[d]=1" ) + .GroupBy( "e" ).AppendHaving( "Count(f)>1" ) + .OrderBy( "g" ) + .Take( 10 ); + } + + /// + /// 创建测试Sql生成器 + /// + private ISqlBuilder CreateSqlBuilder2() { + return new TestSqlBuilder() + .Select( "a1" ) + .From( "b1" ) + .Join( "c1" ).On( "b1.id", "c1.id" ) + .AppendWhere( "[d1]=1" ) + .GroupBy( "e1" ).AppendHaving( "Count(f1)>1" ) + .OrderBy( "g1" ) + .Take( 20 ); + } + + /// + /// 创建测试Sql生成器 + /// + private ISqlBuilder CreateSqlBuilder3() { + return new TestSqlBuilder() + .Select( "a2" ) + .From( "b2" ) + .Join( "c2" ).On( "b2.id", "c2.id" ) + .AppendWhere( "[d2]=1" ) + .GroupBy( "e2" ).AppendHaving( "Count(f2)>1" ) + .OrderBy( "g2" ) + .Take( 30 ); + } + + #endregion + + #region ToResult + + /// + /// 测试获取Sql - 只有主Sql生成器 + /// + [Fact] + public void TestToResult() { + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Join [c] On [b].[id]=[c].[id] " ); + result.AppendLine( "Where [d]=1 " ); + result.AppendLine( "Group By [e] Having Count(f)>1 " ); + result.AppendLine( "Order By [g] " ); + result.Append( "Limit @_p_0 OFFSET @_p_1" ); + Assert.Equal( result.ToString(), _set.ToResult() ); + } + + #endregion + + #region Union + + /// + /// 测试联合操作 - 合并1个查询 + /// + [Fact] + public void TestUnion_1() { + var result = new StringBuilder(); + result.AppendLine( "(" ); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Join [c] On [b].[id]=[c].[id] " ); + result.AppendLine( "Where [d]=1 " ); + result.AppendLine( "Group By [e] Having Count(f)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Union " ); + result.AppendLine( "(" ); + result.AppendLine( "Select [a1] " ); + result.AppendLine( "From [b1] " ); + result.AppendLine( "Join [c1] On [b1].[id]=[c1].[id] " ); + result.AppendLine( "Where [d1]=1 " ); + result.AppendLine( "Group By [e1] Having Count(f1)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Order By [g] " ); + result.Append( "Limit @_p_0 OFFSET @_p_1" ); + + var builder2 = CreateSqlBuilder2(); + _set.Union( builder2 ); + + Assert.Equal( result.ToString(), _set.ToResult() ); + } + + /// + /// 测试联合操作 - 合并2个查询 + /// + [Fact] + public void TestUnion_2() { + var result = new StringBuilder(); + result.AppendLine( "(" ); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Join [c] On [b].[id]=[c].[id] " ); + result.AppendLine( "Where [d]=1 " ); + result.AppendLine( "Group By [e] Having Count(f)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Union " ); + result.AppendLine( "(" ); + result.AppendLine( "Select [a1] " ); + result.AppendLine( "From [b1] " ); + result.AppendLine( "Join [c1] On [b1].[id]=[c1].[id] " ); + result.AppendLine( "Where [d1]=1 " ); + result.AppendLine( "Group By [e1] Having Count(f1)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Union " ); + result.AppendLine( "(" ); + result.AppendLine( "Select [a2] " ); + result.AppendLine( "From [b2] " ); + result.AppendLine( "Join [c2] On [b2].[id]=[c2].[id] " ); + result.AppendLine( "Where [d2]=1 " ); + result.AppendLine( "Group By [e2] Having Count(f2)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Order By [g] " ); + result.Append( "Limit @_p_0 OFFSET @_p_1" ); + + var builder2 = CreateSqlBuilder2(); + var builder3 = CreateSqlBuilder3(); + _set.Union( builder2, builder3 ); + + Assert.Equal( result.ToString(), _set.ToResult() ); + } + + #endregion + + #region UnionAll + + /// + /// 测试联合操作 + /// + [Fact] + public void TestUnionAll() { + var result = new StringBuilder(); + result.AppendLine( "(" ); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Join [c] On [b].[id]=[c].[id] " ); + result.AppendLine( "Where [d]=1 " ); + result.AppendLine( "Group By [e] Having Count(f)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Union All " ); + result.AppendLine( "(" ); + result.AppendLine( "Select [a1] " ); + result.AppendLine( "From [b1] " ); + result.AppendLine( "Join [c1] On [b1].[id]=[c1].[id] " ); + result.AppendLine( "Where [d1]=1 " ); + result.AppendLine( "Group By [e1] Having Count(f1)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Union All " ); + result.AppendLine( "(" ); + result.AppendLine( "Select [a2] " ); + result.AppendLine( "From [b2] " ); + result.AppendLine( "Join [c2] On [b2].[id]=[c2].[id] " ); + result.AppendLine( "Where [d2]=1 " ); + result.AppendLine( "Group By [e2] Having Count(f2)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Order By [g] " ); + result.Append( "Limit @_p_0 OFFSET @_p_1" ); + + var builder2 = CreateSqlBuilder2(); + var builder3 = CreateSqlBuilder3(); + _set.UnionAll( builder2, builder3 ); + + Assert.Equal( result.ToString(), _set.ToResult() ); + } + + #endregion + + #region Intersect + + /// + /// 测试交集 + /// + [Fact] + public void TestIntersect() { + var result = new StringBuilder(); + result.AppendLine( "(" ); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Join [c] On [b].[id]=[c].[id] " ); + result.AppendLine( "Where [d]=1 " ); + result.AppendLine( "Group By [e] Having Count(f)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Intersect " ); + result.AppendLine( "(" ); + result.AppendLine( "Select [a1] " ); + result.AppendLine( "From [b1] " ); + result.AppendLine( "Join [c1] On [b1].[id]=[c1].[id] " ); + result.AppendLine( "Where [d1]=1 " ); + result.AppendLine( "Group By [e1] Having Count(f1)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Intersect " ); + result.AppendLine( "(" ); + result.AppendLine( "Select [a2] " ); + result.AppendLine( "From [b2] " ); + result.AppendLine( "Join [c2] On [b2].[id]=[c2].[id] " ); + result.AppendLine( "Where [d2]=1 " ); + result.AppendLine( "Group By [e2] Having Count(f2)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Order By [g] " ); + result.Append( "Limit @_p_0 OFFSET @_p_1" ); + + var builder2 = CreateSqlBuilder2(); + var builder3 = CreateSqlBuilder3(); + _set.Intersect( builder2, builder3 ); + + Assert.Equal( result.ToString(), _set.ToResult() ); + } + + #endregion + + #region Except + + /// + /// 测试差集 + /// + [Fact] + public void TestExcept() { + var result = new StringBuilder(); + result.AppendLine( "(" ); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Join [c] On [b].[id]=[c].[id] " ); + result.AppendLine( "Where [d]=1 " ); + result.AppendLine( "Group By [e] Having Count(f)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Except " ); + result.AppendLine( "(" ); + result.AppendLine( "Select [a1] " ); + result.AppendLine( "From [b1] " ); + result.AppendLine( "Join [c1] On [b1].[id]=[c1].[id] " ); + result.AppendLine( "Where [d1]=1 " ); + result.AppendLine( "Group By [e1] Having Count(f1)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Except " ); + result.AppendLine( "(" ); + result.AppendLine( "Select [a2] " ); + result.AppendLine( "From [b2] " ); + result.AppendLine( "Join [c2] On [b2].[id]=[c2].[id] " ); + result.AppendLine( "Where [d2]=1 " ); + result.AppendLine( "Group By [e2] Having Count(f2)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Order By [g] " ); + result.Append( "Limit @_p_0 OFFSET @_p_1" ); + + var builder2 = CreateSqlBuilder2(); + var builder3 = CreateSqlBuilder3(); + _set.Except( builder2, builder3 ); + + Assert.Equal( result.ToString(), _set.ToResult() ); + } + + #endregion + + #region Clear + + /// + /// 测试清理 + /// + [Fact] + public void TestClear() { + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Join [c] On [b].[id]=[c].[id] " ); + result.AppendLine( "Where [d]=1 " ); + result.AppendLine( "Group By [e] Having Count(f)>1 " ); + result.AppendLine( "Order By [g] " ); + result.Append( "Limit @_p_0 OFFSET @_p_1" ); + + var builder2 = CreateSqlBuilder2(); + _set.Union( builder2 ); + _set.Clear(); + + Assert.Equal( result.ToString(), _set.ToResult() ); + } + + #endregion + + #region Clone + + /// + /// 测试复制Sql生成器集合 + /// + [Fact] + public void TestClone() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "(" ); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Join [c] On [b].[id]=[c].[id] " ); + result.AppendLine( "Where [d]=1 " ); + result.AppendLine( "Group By [e] Having Count(f)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Union " ); + result.AppendLine( "(" ); + result.AppendLine( "Select [a1] " ); + result.AppendLine( "From [b1] " ); + result.AppendLine( "Join [c1] On [b1].[id]=[c1].[id] " ); + result.AppendLine( "Where [d1]=1 " ); + result.AppendLine( "Group By [e1] Having Count(f1)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Order By [g] " ); + result.Append( "Limit @_p_0 OFFSET @_p_1" ); + + //合并一个Sql集合 + var builder2 = CreateSqlBuilder2(); + _set.Union( builder2 ); + + //复制并验证 + var clone = _set.Clone( CreateMasterSqlBuilder() ); + Assert.Equal( result.ToString(), clone.ToResult() ); + + //修改原始集合,复制集合不应受影响 + var builder3 = CreateSqlBuilder3(); + _set.Union( builder3 ); + Assert.Equal( result.ToString(), clone.ToResult() ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.End.cs b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.End.cs new file mode 100644 index 000000000..9f83ff856 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.End.cs @@ -0,0 +1,172 @@ +using System.Text; +using Util.Data.Queries; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders; + +/// +/// Sql生成器测试 - 结束子句 +/// +public partial class SqlBuilderTest { + + #region Take + + /// + /// 测试设置获取行数 + /// + [Fact] + public void TestTake() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [c].[D] As [e] " ); + result.AppendLine( "From [a].[B] [c] " ); + result.Append( "Limit @_p_0 OFFSET @_p_1" ); + + //执行 + _builder + .Select( "c.D as e" ) + .From( "a.B as c" ) + .Take( 20 ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + Assert.Equal( 20, _builder.GetParam( "@_p_0" ) ); + Assert.Equal( 0, _builder.GetParam( "@_p_1" ) ); + } + + #endregion + + #region Skip + + /// + /// 测试设置跳过行数 + /// + [Fact] + public void TestSkip() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [c].[D] As [e] " ); + result.AppendLine( "From [a].[B] [c] " ); + result.Append( "Limit @_p_1 OFFSET @_p_0" ); + + //执行 + _builder + .Select( "c.D as e" ) + .From( "a.B as c" ) + .Skip( 80 ) + .Take( 40 ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + Assert.Equal( 80, _builder.GetParam("@_p_0") ); + Assert.Equal( 40, _builder.GetParam( "@_p_1" ) ); + } + + #endregion + + #region Page + + /// + /// 测试设置分页 + /// + [Fact] + public void TestPage() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [c].[D] As [e] " ); + result.AppendLine( "From [a].[B] [c] " ); + result.Append( "Limit @_p_1 OFFSET @_p_0" ); + + //执行 + var page = new Pager( 3, 40 ); + _builder + .Select( "c.D as e" ) + .From( "a.B as c" ) + .Page( page ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + Assert.Equal( 80, _builder.GetParam( "@_p_0" ) ); + Assert.Equal( 40, _builder.GetParam( "@_p_1" ) ); + } + + #endregion + + #region AppendEnd + + /// + /// 添加到结束位置 + /// + [Fact] + public void TestAppendEnd_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [c].[D] As [e] " ); + result.AppendLine( "From [a].[B] [c] " ); + result.Append( "Order By abc" ); + + //执行 + _builder + .Select( "c.D as e" ) + .From( "a.B as c" ) + .AppendEnd( "Order By abc" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region ClearPage + + /// + /// 测试清理分页 + /// + [Fact] + public void TestClearPage() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [c].[D] As [e] " ); + result.Append( "From [a].[B] [c]" ); + + //执行 + var page = new Pager( 3, 40 ); + _builder + .Select( "c.D as e" ) + .From( "a.B as c" ) + .Page( page ) + .ClearPage(); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region ClearEnd + + /// + /// 测试清理结束子句 + /// + [Fact] + public void TestClearEnd() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [c].[D] As [e] " ); + result.Append( "From [a].[B] [c]" ); + + //执行 + var page = new Pager( 3, 40 ); + _builder + .Select( "c.D as e" ) + .From( "a.B as c" ) + .Page( page ) + .AppendEnd( "Order By abc" ) + .ClearEnd(); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.From.cs b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.From.cs new file mode 100644 index 000000000..97277911b --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.From.cs @@ -0,0 +1,118 @@ +using System.Text; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders; + +/// +/// Sql生成器测试 - From子句 +/// +public partial class SqlBuilderTest { + + #region From + + /// + /// 设置表 + /// + [Fact] + public void TestFrom_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [c].[D] As [e] " ); + result.Append( "From [a].[B] [c]" ); + + //执行 + _builder.Select( "c.D as e" ) + .From( "a.B as c" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + /// + /// 设置子查询表 + /// + [Fact] + public void TestFrom_2() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[b] " ); + result.AppendLine( "From (Select [a] " ); + result.Append( "From [b]) As [c]" ); + + //执行 + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + _builder.Select( "a.b" ) + .From( builder, "c" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + /// + /// 设置子查询表 - 内嵌表达式 + /// + [Fact] + public void TestFrom_3() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[b] " ); + result.AppendLine( "From (Select [a] " ); + result.Append( "From [b]) As [c]" ); + + //执行 + _builder.Select( "a.b" ) + .From( t => t.Select( "a" ).From( "b" ), "c" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region AppendFrom + + /// + /// 添加到from子句 + /// + [Fact] + public void TestAppendFrom_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [c].[D] As [e] " ); + result.Append( "From [a].[B] As [c]" ); + + //执行 + _builder.Select( "c.D as e" ) + .AppendFrom( "[a].[B] As [c]" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region ClearFrom + + /// + /// 测试清空From子句 + /// + [Fact] + public void TestClearFrom() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [c].[D] As [e] " ); + result.Append( "From [d]" ); + + //执行 + _builder.Select( "c.D as e" ) + .From( "a.B as c" ) + .ClearFrom() + .From( "d" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.GroupBy.cs b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.GroupBy.cs new file mode 100644 index 000000000..a1df0d352 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.GroupBy.cs @@ -0,0 +1,87 @@ +using System.Text; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders; + +/// +/// Sql生成器测试 - GroupBy子句 +/// +public partial class SqlBuilderTest { + + #region GroupBy + + /// + /// 测试分组 + /// + [Fact] + public void TestGroupBy_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[b] As [c] " ); + result.AppendLine( "From [t] " ); + result.Append( "Group By [t].[x] " ); + result.Append( "Having Count([a].[x])=10" ); + + //执行 + _builder.Select( "a.b as c" ) + .From( "t" ) + .GroupBy( "t.x" ) + .Having( "Count([a].[x])", 10, Operator.Equal, false ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region AppendGroupBy + + /// + /// 测试添加到Group By子句 + /// + [Fact] + public void TestAppendGroupBy() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[b] As [c] " ); + result.AppendLine( "From [t] " ); + result.Append( "Group By [t].[x] " ); + result.Append( "Having Count([a].[x])=10" ); + + //执行 + _builder.Select( "a.b as c" ) + .From( "t" ) + .AppendGroupBy( "[t].[x]" ) + .AppendHaving( "Count([a].[x])=10" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region ClearGroupBy + + /// + /// 测试清空GroupBy子句 + /// + [Fact] + public void TestClearGroupBy() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[b] As [c] " ); + result.Append( "From [t]" ); + + //执行 + _builder.Select( "a.b as c" ) + .From( "t" ) + .GroupBy( "t.x" ) + .Having( "Count([a].[x])", 10, Operator.Equal, false ) + .ClearGroupBy(); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Insert.cs b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Insert.cs new file mode 100644 index 000000000..ca02f165f --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Insert.cs @@ -0,0 +1,123 @@ +using System.Text; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders; + +/// +/// Sql生成器测试 - Insert子句 +/// +public partial class SqlBuilderTest { + + #region Insert + + /// + /// 测试插入一行 + /// + [Fact] + public void TestInsert_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Insert Into [t]([a],[b]) " ); + result.Append( "Values(@_p_0,@_p_1)" ); + + //执行 + _builder.Insert( "a,b","t" ) + .Values( 1,"a" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + Assert.Equal( 1, _builder.GetParam( "@_p_0" ) ); + Assert.Equal( "a", _builder.GetParam( "@_p_1" ) ); + } + + /// + /// 测试插入2行 + /// + [Fact] + public void TestInsert_2() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Insert Into [t]([a],[b]) " ); + result.Append( "Values(@_p_0,@_p_1),(@_p_2,@_p_3)" ); + + //执行 + _builder.Insert( "a,b", "t" ) + .Values( 1, "a" ) + .Values( 2, "b" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + Assert.Equal( 1, _builder.GetParam( "@_p_0" ) ); + Assert.Equal( "a", _builder.GetParam( "@_p_1" ) ); + Assert.Equal( 2, _builder.GetParam( "@_p_2" ) ); + Assert.Equal( "b", _builder.GetParam( "@_p_3" ) ); + } + + #endregion + + #region AppendInsert + + /// + /// 添加到Insert子句 + /// + [Fact] + public void TestAppendInsert() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Insert Into [t]([a],[b]) " ); + result.Append( "Values(@_p_0,@_p_1)" ); + + //执行 + _builder.AppendInsert( "[t]([a],[b])" ) + .Values( 1, "a" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + Assert.Equal( 1, _builder.GetParam( "@_p_0" ) ); + Assert.Equal( "a", _builder.GetParam( "@_p_1" ) ); + } + + #endregion + + #region AppendValues + + /// + /// 添加到Values子句 + /// + [Fact] + public void TestAppendValues() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Insert Into [t]([a],[b]) " ); + result.Append( "Values(@_p_0,@_p_1)" ); + + //执行 + _builder.Insert( "a,b", "t" ) + .AppendValues( "(@_p_0,@_p_1)" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region ClearInsert + + /// + /// 测试清空Insert子句 + /// + [Fact] + public void TestClearInsert() { + //执行 + _builder.Insert( "a,b", "t" ) + .Values( 1, "a" ) + .AppendInsert( "[t]([a],[b])" ) + .AppendValues( "(@_p_0,@_p_1)" ) + .ClearInsert(); + + //验证 + Assert.Empty( _builder.GetSql() ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Join.cs b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Join.cs new file mode 100644 index 000000000..08819a8d1 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Join.cs @@ -0,0 +1,357 @@ +using System.Text; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders; + +/// +/// Sql生成器测试 - Join子句 +/// +public partial class SqlBuilderTest { + + #region Join + + /// + /// 内连接 + /// + [Fact] + public void TestJoin_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Join [c] [d]" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .Join( "c as d" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + /// + /// 内连接 - 子查询 + /// + [Fact] + public void TestJoin_2() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Join (Select [a] " ); + result.Append( "From [b]) As [c]" ); + + //执行 + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + _builder.Select( "a" ) + .From( "b" ) + .Join( builder, "c" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + /// + /// 内连接 - 子查询 - 内嵌表达式 + /// + [Fact] + public void TestJoin_3() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Join (Select [a] " ); + result.Append( "From [b]) As [c]" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .Join( t => t.Select( "a" ).From( "b" ), "c" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region LeftJoin + + /// + /// 左外连接 + /// + [Fact] + public void TestLeftJoin_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Left Join [c] [d]" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .LeftJoin( "c as d" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + /// + /// 左外连接 - 子查询 + /// + [Fact] + public void TestLeftJoin_2() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Left Join (Select [a] " ); + result.Append( "From [b]) As [c]" ); + + //执行 + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + _builder.Select( "a" ) + .From( "b" ) + .LeftJoin( builder, "c" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + /// + /// 左外连接 - 子查询 - 内嵌表达式 + /// + [Fact] + public void TestLeftJoin_3() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Left Join (Select [a] " ); + result.Append( "From [b]) As [c]" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .LeftJoin( t => t.Select( "a" ).From( "b" ), "c" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region RightJoin + + /// + /// 右外连接 + /// + [Fact] + public void TestRightJoin_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Right Join [c] [d]" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .RightJoin( "c as d" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + /// + /// 右外连接 - 子查询 + /// + [Fact] + public void TestRightJoin_2() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Right Join (Select [a] " ); + result.Append( "From [b]) As [c]" ); + + //执行 + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + _builder.Select( "a" ) + .From( "b" ) + .RightJoin( builder, "c" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + /// + /// 右外连接 - 子查询 - 内嵌表达式 + /// + [Fact] + public void TestRightJoin_3() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Right Join (Select [a] " ); + result.Append( "From [b]) As [c]" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .RightJoin( t => t.Select( "a" ).From( "b" ), "c" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region On + + /// + /// 连接条件 + /// + [Fact] + public void TestOn_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Join [c] [d] On [b].[Id]<>[d].[Id]" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .Join( "c as d" ).On( "b.Id", "d.Id", Operator.NotEqual ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region AppendJoin + + /// + /// 测试添加到内连接 + /// + [Fact] + public void TestAppendJoin() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Join c" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .AppendJoin( "c" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region AppendLeftJoin + + /// + /// 测试添加到左外连接子句 + /// + [Fact] + public void TestAppendLeftJoin() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Left Join c" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .AppendLeftJoin( "c" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region AppendRightJoin + + /// + /// 测试添加到右外连接子句 + /// + [Fact] + public void TestAppendRightJoin() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Right Join c" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .AppendRightJoin( "c" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region AppendOn + + /// + /// 测试添加到On子句 + /// + [Fact] + public void TestAppendOn() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Join c On d" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .AppendJoin( "c" ) + .AppendOn( "d" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region ClearJoin + + /// + /// 测试清空Join子句 + /// + [Fact] + public void TestClearJoin() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.Append( "From [b]" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .Join( "c as d" ).On( "b.Id", "d.Id", Operator.NotEqual ) + .ClearJoin(); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.OrderBy.cs b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.OrderBy.cs new file mode 100644 index 000000000..1e995aff1 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.OrderBy.cs @@ -0,0 +1,82 @@ +using System.Text; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders; + +/// +/// Sql生成器测试 - OrderBy子句 +/// +public partial class SqlBuilderTest { + + #region OrderBy + + /// + /// 测试排序 + /// + [Fact] + public void TestOrderBy_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [c].[D] As [e] " ); + result.AppendLine( "From [a].[B] [c] " ); + result.Append( "Order By [c].[b] Desc" ); + + //执行 + _builder.Select( "c.D as e" ) + .From( "a.B as c" ) + .OrderBy( "c.b desc" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region AppendOrderBy + + /// + /// 测试添加到Order By子句 + /// + [Fact] + public void TestAppendOrderBy_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [c].[D] As [e] " ); + result.AppendLine( "From [a].[B] [c] " ); + result.Append( "Order By [c].[b] Desc" ); + + //执行 + _builder.Select( "c.D as e" ) + .From( "a.B as c" ) + .AppendOrderBy( "[c].[b] Desc" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region ClearOrderBy + + /// + /// 测试清空Order By子句 + /// + [Fact] + public void TestClearOrderBy() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [c].[D] As [e] " ); + result.Append( "From [a].[B] [c]" ); + + //执行 + _builder.Select( "c.D as e" ) + .From( "a.B as c" ) + .OrderBy( "c.b desc" ) + .ClearOrderBy(); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Parameter.cs b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Parameter.cs new file mode 100644 index 000000000..96485aa4d --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Parameter.cs @@ -0,0 +1,62 @@ +using System.Text; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders; + +/// +/// Sql生成器测试 - 参数 +/// +public partial class SqlBuilderTest { + + #region AddParam + + /// + /// 测试添加Sql参数 + /// + [Fact] + public void TestAddParam() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Where [b].[Name]<>@Test" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .AppendWhere( "[b].[Name]<>@Test" ) + .AddParam("@Test", "abc" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + Assert.Single( _builder.GetParams() ); + Assert.Equal( "abc", _builder.GetParam( "@Test" ) ); + } + + #endregion + + #region ClearParams + + /// + /// 测试清空Sql参数 + /// + [Fact] + public void TestClearParams() { + //添加参数 + _builder.Select( "a" ) + .From( "b" ) + .Where( "b.Name", "abc", Operator.NotEqual ); + + //验证 + Assert.Single( _builder.GetParams() ); + Assert.Equal( "abc", _builder.GetParam( "@_p_0" ) ); + + //清空参数 + _builder.ClearParams(); + + //验证 + Assert.Empty( _builder.GetParams() ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Select.cs b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Select.cs new file mode 100644 index 000000000..9eac7e860 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Select.cs @@ -0,0 +1,180 @@ +using System.Text; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders; + +/// +/// Sql生成器测试 - Select子句 +/// +public partial class SqlBuilderTest { + + #region Select + + /// + /// 设置*列 + /// + [Fact] + public void TestSelect_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select * " ); + result.Append( "From [t]" ); + + //执行 + _builder.Select().From( "t" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + /// + /// 设置列 + /// + [Fact] + public void TestSelect_2() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[b] As [c] " ); + result.Append( "From [t]" ); + + //执行 + _builder.Select( "a.b as c" ) + .From( "t" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + /// + /// 设置子查询列 + /// + [Fact] + public void TestSelect_3() { + //结果 + var result = new StringBuilder(); + result.Append( "Select [a].[b]," ); + result.AppendLine( "(Select [a] " ); + result.AppendLine( "From [b]) As [c] " ); + result.Append( "From [t]" ); + + //执行 + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + _builder.Select( "a.b" ) + .Select( builder,"c" ) + .From( "t" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + /// + /// 设置子查询列 - 内嵌表达式 + /// + [Fact] + public void TestSelect_4() { + //结果 + var result = new StringBuilder(); + result.Append( "Select [a].[b]," ); + result.AppendLine( "(Select [a] " ); + result.AppendLine( "From [b]) As [c] " ); + result.Append( "From [t]" ); + + //执行 + _builder.Select( "a.b" ) + .Select( t => t.Select( "a" ).From( "b" ), "c" ) + .From( "t" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region AppendSelect + + /// + /// 添加到select子句 + /// + [Fact] + public void TestAppendSelect_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[b] as [c] " ); + result.Append( "From [t]" ); + + //执行 + _builder.AppendSelect( "[a].[b] " ) + .AppendSelect( "as [c]" ) + .From( "t" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + /// + /// 添加到select子句 - 子查询 + /// + [Fact] + public void TestAppendSelect_2() { + //结果 + var result = new StringBuilder(); + result.Append( "Select [a].[b]," ); + result.AppendLine( "(Select [a] " ); + result.AppendLine( "From [b]) As [c] " ); + result.Append( "From [t]" ); + + //执行 + var builder = new TestSqlBuilder().Select( "a" ).From( "b" ); + _builder.AppendSelect( "[a].[b]," ) + .AppendSelect( "(" ) + .AppendSelect( builder ) + .AppendSelect( ") As [c]" ) + .From( "t" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + /// + /// 添加到select子句 - 子查询 - 内嵌表达式 + /// + [Fact] + public void TestAppendSelect_3() { + //结果 + var result = new StringBuilder(); + result.Append( "Select [a].[b]," ); + result.AppendLine( "(Select [a] " ); + result.AppendLine( "From [b]) As [c] " ); + result.Append( "From [t]" ); + + //执行 + _builder.AppendSelect( "[a].[b]," ) + .AppendSelect( "(" ) + .AppendSelect( t => t.Select( "a" ).From( "b" ) ) + .AppendSelect( ") As [c]" ) + .From( "t" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region ClearSelect + + /// + /// 测试清空Select子句 + /// + [Fact] + public void TestClearSelect() { + //执行 + _builder.Select( "a.b as c" ) + .ClearSelect(); + + //验证 + Assert.Empty( _builder.GetSql() ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Set.cs b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Set.cs new file mode 100644 index 000000000..7af31214b --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Set.cs @@ -0,0 +1,275 @@ +using System.Text; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders; + +/// +/// Sql生成器测试 - 集合操作 +/// +public partial class SqlBuilderTest { + + #region 辅助方法 + + /// + /// 创建主Sql生成器 + /// + private ISqlBuilder CreateMasterSqlBuilder() { + return new TestSqlBuilder() + .Select( "a" ) + .From( "b" ) + .Join( "c" ).On( "b.id", "c.id" ) + .AppendWhere( "[d]=1" ) + .GroupBy( "e" ).AppendHaving( "Count(f)>1" ) + .OrderBy( "g" ) + .Take( 10 ); + } + + /// + /// 创建测试Sql生成器 + /// + private ISqlBuilder CreateSqlBuilder2() { + return new TestSqlBuilder() + .Select( "a1" ) + .From( "b1" ) + .Join( "c1" ).On( "b1.id", "c1.id" ) + .AppendWhere( "[d1]=1" ) + .GroupBy( "e1" ).AppendHaving( "Count(f1)>1" ) + .OrderBy( "g1" ) + .Take( 20 ); + } + + /// + /// 创建测试Sql生成器 + /// + private ISqlBuilder CreateSqlBuilder3() { + return new TestSqlBuilder() + .Select( "a2" ) + .From( "b2" ) + .Join( "c2" ).On( "b2.id", "c2.id" ) + .AppendWhere( "[d2]=1" ) + .GroupBy( "e2" ).AppendHaving( "Count(f2)>1" ) + .OrderBy( "g2" ) + .Take( 30 ); + } + + #endregion + + #region Union + + /// + /// 测试合并结果集 + /// + [Fact] + public void TestUnion() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "(" ); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Join [c] On [b].[id]=[c].[id] " ); + result.AppendLine( "Where [d]=1 " ); + result.AppendLine( "Group By [e] Having Count(f)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Union " ); + result.AppendLine( "(" ); + result.AppendLine( "Select [a1] " ); + result.AppendLine( "From [b1] " ); + result.AppendLine( "Join [c1] On [b1].[id]=[c1].[id] " ); + result.AppendLine( "Where [d1]=1 " ); + result.AppendLine( "Group By [e1] Having Count(f1)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Union " ); + result.AppendLine( "(" ); + result.AppendLine( "Select [a2] " ); + result.AppendLine( "From [b2] " ); + result.AppendLine( "Join [c2] On [b2].[id]=[c2].[id] " ); + result.AppendLine( "Where [d2]=1 " ); + result.AppendLine( "Group By [e2] Having Count(f2)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Order By [g] " ); + result.Append( "Limit @_p_0 OFFSET @_p_1" ); + + //执行 + var builder = CreateMasterSqlBuilder(); + var builder2 = CreateSqlBuilder2(); + var builder3 = CreateSqlBuilder3(); + builder.Union( builder2, builder3 ); + + //验证 + Assert.Equal( result.ToString(), builder.GetSql() ); + } + + #endregion + + #region UnionAll + + /// + /// 测试合并结果集 + /// + [Fact] + public void TestUnionAll() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "(" ); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Join [c] On [b].[id]=[c].[id] " ); + result.AppendLine( "Where [d]=1 " ); + result.AppendLine( "Group By [e] Having Count(f)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Union All " ); + result.AppendLine( "(" ); + result.AppendLine( "Select [a1] " ); + result.AppendLine( "From [b1] " ); + result.AppendLine( "Join [c1] On [b1].[id]=[c1].[id] " ); + result.AppendLine( "Where [d1]=1 " ); + result.AppendLine( "Group By [e1] Having Count(f1)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Union All " ); + result.AppendLine( "(" ); + result.AppendLine( "Select [a2] " ); + result.AppendLine( "From [b2] " ); + result.AppendLine( "Join [c2] On [b2].[id]=[c2].[id] " ); + result.AppendLine( "Where [d2]=1 " ); + result.AppendLine( "Group By [e2] Having Count(f2)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Order By [g] " ); + result.Append( "Limit @_p_0 OFFSET @_p_1" ); + + //执行 + var builder = CreateMasterSqlBuilder(); + var builder2 = CreateSqlBuilder2(); + var builder3 = CreateSqlBuilder3(); + builder.UnionAll( builder2, builder3 ); + + //验证 + Assert.Equal( result.ToString(), builder.GetSql() ); + } + + #endregion + + #region Intersect + + /// + /// 测试交集 + /// + [Fact] + public void TestIntersect() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "(" ); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Join [c] On [b].[id]=[c].[id] " ); + result.AppendLine( "Where [d]=1 " ); + result.AppendLine( "Group By [e] Having Count(f)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Intersect " ); + result.AppendLine( "(" ); + result.AppendLine( "Select [a1] " ); + result.AppendLine( "From [b1] " ); + result.AppendLine( "Join [c1] On [b1].[id]=[c1].[id] " ); + result.AppendLine( "Where [d1]=1 " ); + result.AppendLine( "Group By [e1] Having Count(f1)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Intersect " ); + result.AppendLine( "(" ); + result.AppendLine( "Select [a2] " ); + result.AppendLine( "From [b2] " ); + result.AppendLine( "Join [c2] On [b2].[id]=[c2].[id] " ); + result.AppendLine( "Where [d2]=1 " ); + result.AppendLine( "Group By [e2] Having Count(f2)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Order By [g] " ); + result.Append( "Limit @_p_0 OFFSET @_p_1" ); + + //执行 + var builder = CreateMasterSqlBuilder(); + var builder2 = CreateSqlBuilder2(); + var builder3 = CreateSqlBuilder3(); + builder.Intersect( builder2, builder3 ); + + //验证 + Assert.Equal( result.ToString(), builder.GetSql() ); + } + + #endregion + + #region Except + + /// + /// 测试差集 + /// + [Fact] + public void TestExcept() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "(" ); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Join [c] On [b].[id]=[c].[id] " ); + result.AppendLine( "Where [d]=1 " ); + result.AppendLine( "Group By [e] Having Count(f)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Except " ); + result.AppendLine( "(" ); + result.AppendLine( "Select [a1] " ); + result.AppendLine( "From [b1] " ); + result.AppendLine( "Join [c1] On [b1].[id]=[c1].[id] " ); + result.AppendLine( "Where [d1]=1 " ); + result.AppendLine( "Group By [e1] Having Count(f1)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Except " ); + result.AppendLine( "(" ); + result.AppendLine( "Select [a2] " ); + result.AppendLine( "From [b2] " ); + result.AppendLine( "Join [c2] On [b2].[id]=[c2].[id] " ); + result.AppendLine( "Where [d2]=1 " ); + result.AppendLine( "Group By [e2] Having Count(f2)>1 " ); + result.AppendLine( ")" ); + result.AppendLine( "Order By [g] " ); + result.Append( "Limit @_p_0 OFFSET @_p_1" ); + + //执行 + var builder = CreateMasterSqlBuilder(); + var builder2 = CreateSqlBuilder2(); + var builder3 = CreateSqlBuilder3(); + builder.Except( builder2, builder3 ); + + //验证 + Assert.Equal( result.ToString(), builder.GetSql() ); + } + + #endregion + + #region ClearSets + + /// + /// 测试清理集合 + /// + [Fact] + public void TestClearSets() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.AppendLine( "Join [c] On [b].[id]=[c].[id] " ); + result.AppendLine( "Where [d]=1 " ); + result.AppendLine( "Group By [e] Having Count(f)>1 " ); + result.AppendLine( "Order By [g] " ); + result.Append( "Limit @_p_0 OFFSET @_p_1" ); + + //执行 + var builder = CreateMasterSqlBuilder(); + var builder2 = CreateSqlBuilder2(); + var builder3 = CreateSqlBuilder3(); + builder.Union( builder2, builder3 ); + builder.ClearSets(); + + //验证 + Assert.Equal( result.ToString(), builder.GetSql() ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Start.cs b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Start.cs new file mode 100644 index 000000000..2e0755a42 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Start.cs @@ -0,0 +1,152 @@ +using System.Text; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders; + +/// +/// Sql生成器测试 - 起始子句 +/// +public partial class SqlBuilderTest { + + #region Cte + + /// + /// 测试公用表表达式CTE + /// + [Fact] + public void TestCte() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "With [Test] " ); + result.AppendLine( "As (Select [a],[b] " ); + result.AppendLine( "From [Test2])," ); + result.AppendLine( "[Test3] " ); + result.AppendLine( "As (Select [a],[b] " ); + result.AppendLine( "From [Test3]) " ); + result.AppendLine( "Select [a],[b] " ); + result.Append( "From [Test]" ); + + //执行 + var builder2 = _builder.New().Select( "a,b" ).From( "Test2" ); + var builder3 = _builder.New().Select( "a,b" ).From( "Test3" ); + _builder.Cte( "Test", builder2 ).Cte( "Test3", builder3 ).Select( "a,b" ).From( "Test" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region Append + + /// + /// 添加到起始位置 + /// + [Fact] + public void TestAppend() { + //结果 + var result = new StringBuilder(); + result.Append( "Select a From b" ); + + //执行 + _builder.Append( "Select a " ); + _builder.Append( "From b" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region AppendLine + + /// + /// 添加到起始位置并换行 + /// + [Fact] + public void TestAppendLine() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select a " ); + result.Append( "From b" ); + + //执行 + _builder.AppendLine( "Select a " ); + _builder.Append( "From b" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region AppendStart + + /// + /// 添加到起始位置 + /// + [Fact] + public void TestAppendStart_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "with(a) " ); + result.AppendLine( "Select [c].[D] As [e] " ); + result.Append( "From [a].[B] [c]" ); + + //执行 + _builder.AppendStart( "with(a)" ) + .Select( "c.D as e" ) + .From( "a.B as c" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region ClearCte + + /// + /// 测试清理公用表表达式CTE + /// + [Fact] + public void TestClearCte() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a],[b] " ); + result.Append( "From [Test]" ); + + //执行 + var builder2 = _builder.New().Select( "a,b" ).From( "Test2" ); + var builder3 = _builder.New().Select( "a,b" ).From( "Test3" ); + _builder.Cte( "Test", builder2 ).Cte( "Test3", builder3 ).Select( "a,b" ).From( "Test" ).ClearCte(); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region ClearStart + + /// + /// 测试清理起始子句 + /// + [Fact] + public void TestClearStart() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a],[b] " ); + result.Append( "From [Test]" ); + + //执行 + var builder2 = _builder.New().Select( "a,b" ).From( "Test2" ); + var builder3 = _builder.New().Select( "a,b" ).From( "Test3" ); + _builder.AppendStart( "with(a)" ).Cte( "Test", builder2 ).Cte( "Test3", builder3 ).Select( "a,b" ).From( "Test" ).ClearStart(); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Where.cs b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Where.cs new file mode 100644 index 000000000..7a912e763 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.Where.cs @@ -0,0 +1,663 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Util.Data.Queries; +using Util.Data.Sql.Tests.Samples; +using Xunit; + +namespace Util.Data.Sql.Tests.Builders; + +/// +/// Sql生成器测试 - Where子句 +/// +public partial class SqlBuilderTest { + + #region Where + + /// + /// 设置条件 + /// + [Fact] + public void TestWhere_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Where [b].[Name]<>@_p_0" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .Where( "b.Name", "abc", Operator.NotEqual ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + Assert.Single( _builder.GetParams() ); + Assert.Equal( "abc", _builder.GetParam( "@_p_0" ) ); + } + + /// + /// 添加Where子查询 + /// + [Fact] + public void TestWhere_2() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select * " ); + result.AppendLine( "From [abc].[Test] " ); + result.Append( "Where [b2]<>" ); + result.AppendLine( "(Select [a] " ); + result.AppendLine( "From [Test2] " ); + result.Append( "Where [Name]=@_p_0) " ); + result.Append( "And [Age]=@_p_1" ); + + //执行 + var builder2 = _builder.New().Select("a").From( "Test2" ).Where( "Name", "a" ); + _builder.Select().From( "abc.Test" ).Where( "b2", builder2, Operator.NotEqual ).Where( "Age", 1 ); + _output.WriteLine( _builder.GetSql() ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + Assert.Equal( 2, _builder.GetParams().Count ); + Assert.Equal( "a", _builder.GetParam( "@_p_0" ) ); + Assert.Equal( 1, _builder.GetParam( "@_p_1" ) ); + } + + /// + /// 添加Where子查询 - 内嵌表达式 + /// + [Fact] + public void TestWhere_3() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select * " ); + result.AppendLine( "From [abc].[Test] " ); + result.Append( "Where [b2]<>" ); + result.AppendLine( "(Select [a] " ); + result.AppendLine( "From [Test2] " ); + result.Append( "Where [Name]=@_p_0) " ); + result.Append( "And [Age]=@_p_1" ); + + //执行 + _builder.Select().From( "abc.Test" ).Where( "b2", t => t.Select( "a" ).From( "Test2" ).Where( "Name", "a" ), Operator.NotEqual ).Where( "Age", 1 ); + _output.WriteLine( _builder.GetSql() ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + Assert.Equal( 2, _builder.GetParams().Count ); + Assert.Equal( "a", _builder.GetParam( "@_p_0" ) ); + Assert.Equal( 1, _builder.GetParam( "@_p_1" ) ); + } + + #endregion + + #region In + + /// + /// 设置In条件 + /// + [Fact] + public void TestIn_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[Email] " ); + result.AppendLine( "From [Sample] [a] " ); + result.Append( "Where [a].[Email] In (@_p_0,@_p_1)" ); + + //执行 + var list = new List { "a", "b" }; + _builder.Select( "a.Email" ) + .From( "Sample as a" ) + .In( "a.Email", list ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + Assert.Equal( 2, _builder.GetParams().Count ); + Assert.Equal( "a", _builder.GetParam( "@_p_0" ) ); + Assert.Equal( "b", _builder.GetParam( "@_p_1" ) ); + } + + /// + /// 设置In条件 - 数组 + /// + [Fact] + public void TestIn_2() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[Email] " ); + result.AppendLine( "From [Sample] [a] " ); + result.Append( "Where [a].[Email] In (@_p_0,@_p_1)" ); + + //执行 + var list = new[] { "a", "b" }; + _builder.Select( "a.Email" ) + .From( "Sample as a" ) + .In( "a.Email", list ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + Assert.Equal( 2, _builder.GetParams().Count ); + Assert.Equal( "a", _builder.GetParam( "@_p_0" ) ); + Assert.Equal( "b", _builder.GetParam( "@_p_1" ) ); + } + + /// + /// 设置In条件 - 子查询 + /// + [Fact] + public void TestIn_3() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[Email] " ); + result.AppendLine( "From [Sample] [a] " ); + result.Append( "Where [a].[Name]=@_p_1 And " ); + result.Append( "[a].[Email] In (" ); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Where [b].[Url]=@_p_0" ); + result.Append( ")" ); + + //执行 + var builder = _builder.New().Select( "a" ).From( "b" ).Where( "b.Url", "u" ); + _builder.Select( "a.Email" ) + .From( "Sample as a" ) + .Where( "a.Name", "n" ) + .In( "a.Email", builder ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + Assert.Equal( 2, _builder.GetParams().Count ); + Assert.Equal( "u", _builder.GetParam( "@_p_0" ) ); + Assert.Equal( "n", _builder.GetParam( "@_p_1" ) ); + } + + /// + /// 设置In条件 - 子查询 - 内嵌表达式 + /// + [Fact] + public void TestIn_4() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[Email] " ); + result.AppendLine( "From [Sample] [a] " ); + result.Append( "Where [a].[Name]=@_p_0 And " ); + result.Append( "[a].[Email] In (" ); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Where [b].[Url]=@_p_1" ); + result.Append( ")" ); + + //执行 + _builder.Select( "a.Email" ) + .From( "Sample as a" ) + .Where( "a.Name", "n" ) + .In( "a.Email", builder => builder.Select( "a" ).From( "b" ).Where( "b.Url", "u" ) ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + Assert.Equal( 2, _builder.GetParams().Count ); + Assert.Equal( "n", _builder.GetParam( "@_p_0" ) ); + Assert.Equal( "u", _builder.GetParam( "@_p_1" ) ); + } + + #endregion + + #region NotIn + + /// + /// 设置NotIn条件 + /// + [Fact] + public void TestNotIn_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[Email] " ); + result.AppendLine( "From [Sample] [a] " ); + result.Append( "Where [a].[Email] Not In (@_p_0,@_p_1)" ); + + //执行 + var list = new List { "a", "b" }; + _builder.Select( "a.Email" ) + .From( "Sample as a" ) + .NotIn( "a.Email", list ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + Assert.Equal( 2, _builder.GetParams().Count ); + Assert.Equal( "a", _builder.GetParam( "@_p_0" ) ); + Assert.Equal( "b", _builder.GetParam( "@_p_1" ) ); + } + + /// + /// 设置NotIn条件 - 数组 + /// + [Fact] + public void TestNotIn_2() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[Email] " ); + result.AppendLine( "From [Sample] [a] " ); + result.Append( "Where [a].[Email] Not In (@_p_0,@_p_1)" ); + + //执行 + var list = new[] { "a", "b" }; + _builder.Select( "a.Email" ) + .From( "Sample as a" ) + .NotIn( "a.Email", list ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + Assert.Equal( 2, _builder.GetParams().Count ); + Assert.Equal( "a", _builder.GetParam( "@_p_0" ) ); + Assert.Equal( "b", _builder.GetParam( "@_p_1" ) ); + } + + /// + /// 设置NotIn条件 - 子查询 + /// + [Fact] + public void TestNotIn_3() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[Email] " ); + result.AppendLine( "From [Sample] [a] " ); + result.Append( "Where [a].[Name]=@_p_1 And " ); + result.Append( "[a].[Email] Not In (" ); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Where [b].[Url]=@_p_0" ); + result.Append( ")" ); + + //执行 + var builder = _builder.New().Select( "a" ).From( "b" ).Where( "b.Url", "u" ); + _builder.Select( "a.Email" ) + .From( "Sample as a" ) + .Where( "a.Name", "n" ) + .NotIn( "a.Email", builder ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + Assert.Equal( 2, _builder.GetParams().Count ); + Assert.Equal( "u", _builder.GetParam( "@_p_0" ) ); + Assert.Equal( "n", _builder.GetParam( "@_p_1" ) ); + } + + /// + /// 设置NotIn条件 - 子查询 - 内嵌表达式 + /// + [Fact] + public void TestNotIn_4() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[Email] " ); + result.AppendLine( "From [Sample] [a] " ); + result.Append( "Where [a].[Name]=@_p_0 And " ); + result.Append( "[a].[Email] Not In (" ); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Where [b].[Url]=@_p_1" ); + result.Append( ")" ); + + //执行 + _builder.Select( "a.Email" ) + .From( "Sample as a" ) + .Where( "a.Name", "n" ) + .NotIn( "a.Email", builder => builder.Select( "a" ).From( "b" ).Where( "b.Url", "u" ) ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + Assert.Equal( 2, _builder.GetParams().Count ); + Assert.Equal( "n", _builder.GetParam( "@_p_0" ) ); + Assert.Equal( "u", _builder.GetParam( "@_p_1" ) ); + } + + #endregion + + #region IsNull + + /// + /// 设置Is Null条件 + /// + [Fact] + public void TestIsNull() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[Email] " ); + result.AppendLine( "From [Sample] [a] " ); + result.Append( "Where [a].[Email] Is Null" ); + + //执行 + _builder.Select( "a.Email" ) + .From( "Sample a" ) + .IsNull( "a.Email" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region IsNotNull + + /// + /// 设置条件 - Is Not Null + /// + [Fact] + public void TestIsNotNull() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[Email] " ); + result.AppendLine( "From [Sample] [a] " ); + result.Append( "Where [a].[Email] Is Not Null" ); + + //执行 + _builder.Select( "a.Email" ) + .From( "Sample a" ) + .IsNotNull( "a.Email" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region IsEmpty + + /// + /// 测试设置空条件 + /// + [Fact] + public void TestIsEmpty() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[Email] " ); + result.AppendLine( "From [Sample] [a] " ); + result.Append( "Where ([a].[Email] Is Null Or [a].[Email]='')" ); + + //执行 + _builder.Select( "a.Email" ) + .From( "Sample a" ) + .IsEmpty( "a.Email" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region IsNotEmpty + + /// + /// 设置非空条件 + /// + [Fact] + public void TestIsNotEmpty() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[Email] " ); + result.AppendLine( "From [Sample] [a] " ); + result.Append( "Where [a].[Email] Is Not Null And [a].[Email]<>''" ); + + //执行 + _builder.Select( "a.Email" ) + .From( "Sample a" ) + .IsNotEmpty( "a.Email" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region Between + + /// + /// 测试范围查询 - int类型 + /// + [Fact] + public void TestBetween_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[Email] " ); + result.AppendLine( "From [Sample] [a] " ); + result.Append( "Where [a].[B]>=@_p_0 And [a].[B]<@_p_1" ); + + //执行 + _builder.Select( "a.Email" ) + .From( "Sample a" ) + .Between( "a.B", 1, 2,Boundary.Left ); + + //验证 + Assert.Equal( 1, _builder.GetParam( "@_p_0" ) ); + Assert.Equal( 2, _builder.GetParam( "@_p_1" ) ); + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + /// + /// 测试范围查询 - double类型 + /// + [Fact] + public void TestBetween_2() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[Email] " ); + result.AppendLine( "From [Sample] [a] " ); + result.Append( "Where [a].[B]>=@_p_0 And [a].[B]<@_p_1" ); + + //执行 + _builder.Select( "a.Email" ) + .From( "Sample a" ) + .Between( "a.B", 1.1, 2.2, Boundary.Left ); + + //验证 + Assert.Equal( 1.1, _builder.GetParam( "@_p_0" ) ); + Assert.Equal( 2.2, _builder.GetParam( "@_p_1" ) ); + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + /// + /// 测试范围查询 - decimal类型 + /// + [Fact] + public void TestBetween_3() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[Email] " ); + result.AppendLine( "From [Sample] [a] " ); + result.Append( "Where [a].[B]>=@_p_0 And [a].[B]<@_p_1" ); + + //执行 + _builder.Select( "a.Email" ) + .From( "Sample a" ) + .Between( "a.B", 1.1M, 2.2M, Boundary.Left ); + + //验证 + Assert.Equal( 1.1M, _builder.GetParam( "@_p_0" ) ); + Assert.Equal( 2.2M, _builder.GetParam( "@_p_1" ) ); + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + /// + /// 测试范围查询 - DateTime类型 + /// + [Fact] + public void TestBetween_4() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a].[Email] " ); + result.AppendLine( "From [Sample] [a] " ); + result.Append( "Where [a].[B]>=@_p_0 And [a].[B]<@_p_1" ); + + //日期 + var min = "2012-12-12 12:12:12".ToDateTime(); + var max = "2012-12-16 12:12:12".ToDateTime(); + + //执行 + _builder.Select( "a.Email" ) + .From( "Sample a" ) + .Between( "a.B", min, max,true, Boundary.Left ); + + //验证 + Assert.Equal( min, _builder.GetParam( "@_p_0" ) ); + Assert.Equal( max, _builder.GetParam( "@_p_1" ) ); + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region Exists + + /// + /// 设置Exists条件 - 子查询Sql生成器 + /// + [Fact] + public void TestExists_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Where Exists (" ); + result.AppendLine( "Select [Name] " ); + result.Append( "From [t]" ); + result.Append( ")" ); + + //子查询 + var subBuilder = new TestSqlBuilder(); + subBuilder.Select( "Name" ).From( "t" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .Exists( subBuilder ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + /// + /// 设置Exists条件 - 子查询操作 + /// + [Fact] + public void TestExists_2() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Where Exists (" ); + result.AppendLine( "Select [Name] " ); + result.Append( "From [t]" ); + result.Append( ")" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .Exists( t => t.Select( "Name" ).From( "t" ) ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region NotExists + + /// + /// 设置Not Exists条件 - 子查询Sql生成器 + /// + [Fact] + public void TestNotExists_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Where Not Exists (" ); + result.AppendLine( "Select [Name] " ); + result.Append( "From [t]" ); + result.Append( ")" ); + + //子查询 + var subBuilder = new TestSqlBuilder(); + subBuilder.Select( "Name" ).From( "t" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .NotExists( subBuilder ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + /// + /// 设置Not Exists条件 - 子查询操作 + /// + [Fact] + public void TestNotExists_2() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Where Not Exists (" ); + result.AppendLine( "Select [Name] " ); + result.Append( "From [t]" ); + result.Append( ")" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .NotExists( t => t.Select( "Name" ).From( "t" ) ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region AppendWhere + + /// + /// 测试添加到Where子句 + /// + [Fact] + public void TestAppendWhere_1() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.AppendLine( "From [b] " ); + result.Append( "Where [b].[Name]<>'abc'" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .AppendWhere( "[b].[Name]<>'abc'" ); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion + + #region ClearWhere + + /// + /// 测试清空Where子句 + /// + [Fact] + public void TestClearWhere() { + //结果 + var result = new StringBuilder(); + result.AppendLine( "Select [a] " ); + result.Append( "From [b]" ); + + //执行 + _builder.Select( "a" ) + .From( "b" ) + .Where( "b.Name", "abc", Operator.NotEqual ) + .ClearWhere(); + + //验证 + Assert.Equal( result.ToString(), _builder.GetSql() ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.cs b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.cs new file mode 100644 index 000000000..c8dc8041f --- /dev/null +++ b/test/Util.Data.Sql.Tests/Builders/SqlBuilderTest.cs @@ -0,0 +1,27 @@ +using Util.Data.Sql.Tests.Samples; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Data.Sql.Tests.Builders; + +/// +/// Sql生成器测试 +/// +public partial class SqlBuilderTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// 测试Sql生成器 + /// + private readonly TestSqlBuilder _builder; + + /// + /// 测试初始化 + /// + public SqlBuilderTest( ITestOutputHelper output ) { + _output = output; + _builder = new TestSqlBuilder(); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Samples/TestColumnCache.cs b/test/Util.Data.Sql.Tests/Samples/TestColumnCache.cs new file mode 100644 index 000000000..e77807761 --- /dev/null +++ b/test/Util.Data.Sql.Tests/Samples/TestColumnCache.cs @@ -0,0 +1,49 @@ +using System.Collections.Concurrent; +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Caches; + +namespace Util.Data.Sql.Tests.Samples; + +/// +/// 测试列缓存 +/// +public class TestColumnCache : ColumnCacheBase { + /// + /// 列名缓存 + /// + private readonly ConcurrentDictionary _cache; + + /// + /// 初始化测试列缓存 + /// + public TestColumnCache() : this( TestDialect.Instance ) { + } + + /// + /// 初始化测试列缓存 + /// + public TestColumnCache( IDialect dialect ) : base( dialect ) { + _cache = new ConcurrentDictionary(); + } + + /// + /// 测试列缓存实例 + /// + public static readonly IColumnCache Instance = new TestColumnCache(); + + /// + /// 从缓存中获取处理后的列集合 + /// + /// 列集合 + public override string GetSafeColumns( string columns ) { + return _cache.GetOrAdd( columns.GetHashCode(), key => NormalizeColumns( columns ) ); + } + + /// + /// 从缓存中获取处理后的列 + /// + /// 列 + public override string GetSafeColumn( string column ) { + return _cache.GetOrAdd( column.GetHashCode(), key => NormalizeColumn( column ) ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Samples/TestDialect.cs b/test/Util.Data.Sql.Tests/Samples/TestDialect.cs new file mode 100644 index 000000000..ea8da770a --- /dev/null +++ b/test/Util.Data.Sql.Tests/Samples/TestDialect.cs @@ -0,0 +1,41 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Core; + +namespace Util.Data.Sql.Tests.Samples; + +/// +/// 测试方言 +/// +public class TestDialect : DialectBase { + /// + /// 封闭构造方法 + /// + private TestDialect() { + } + + /// + /// 测试方言实例 + /// + public static readonly IDialect Instance = new TestDialect(); + + /// + /// 获取起始转义标识符 + /// + public override string GetOpeningIdentifier() { + return "["; + } + + /// + /// 获取结束转义标识符 + /// + public override string GetClosingIdentifier() { + return "]"; + } + + /// + /// 获取参数前缀 + /// + public override string GetPrefix() { + return "@"; + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Samples/TestDialect2.cs b/test/Util.Data.Sql.Tests/Samples/TestDialect2.cs new file mode 100644 index 000000000..45cacc80d --- /dev/null +++ b/test/Util.Data.Sql.Tests/Samples/TestDialect2.cs @@ -0,0 +1,29 @@ +using Util.Data.Sql.Builders.Core; + +namespace Util.Data.Sql.Tests.Samples; + +/// +/// 测试方言2 +/// +public class TestDialect2 : DialectBase { + /// + /// 获取起始转义标识符 + /// + public override string GetOpeningIdentifier() { + return "$"; + } + + /// + /// 获取结束转义标识符 + /// + public override string GetClosingIdentifier() { + return "&"; + } + + /// + /// 获取参数前缀 + /// + public override string GetPrefix() { + return "*"; + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Samples/TestParameter.cs b/test/Util.Data.Sql.Tests/Samples/TestParameter.cs new file mode 100644 index 000000000..ab4c9d33a --- /dev/null +++ b/test/Util.Data.Sql.Tests/Samples/TestParameter.cs @@ -0,0 +1,11 @@ +namespace Util.Data.Sql.Tests.Samples; + +/// +/// 测试参数 +/// +public class TestParameter { + /// + /// 编码 + /// + public string Code { get; set; } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Samples/TestSqlBuilder.cs b/test/Util.Data.Sql.Tests/Samples/TestSqlBuilder.cs new file mode 100644 index 000000000..e2ff4a10b --- /dev/null +++ b/test/Util.Data.Sql.Tests/Samples/TestSqlBuilder.cs @@ -0,0 +1,62 @@ +using Util.Data.Sql.Builders; +using Util.Data.Sql.Builders.Caches; +using Util.Data.Sql.Builders.Params; + +namespace Util.Data.Sql.Tests.Samples; + +/// +/// 测试Sql生成器 +/// +public class TestSqlBuilder : SqlBuilderBase { + /// + /// Sql方言 + /// + private readonly IDialect _dialect; + /// + /// 列缓存 + /// + private readonly IColumnCache _columnCache; + + /// + /// 初始化Sql生成器 + /// + public TestSqlBuilder( IDialect dialect = null, IColumnCache columnCache = null, IParameterManager parameterManager = null ) + : base( parameterManager ) { + _dialect = dialect; + _columnCache = columnCache; + } + + /// + /// 获取Sql方言 + /// + protected override IDialect CreateDialect() { + if ( _dialect != null ) + return _dialect; + return TestDialect.Instance; + } + + /// + /// 获取列缓存 + /// + protected override IColumnCache CreateColumnCache() { + if ( _columnCache != null ) + return _columnCache; + return TestColumnCache.Instance; + } + + /// + /// 创建Sql生成器 + /// + public override ISqlBuilder New() { + return new TestSqlBuilder( Dialect,ColumnCache, ParameterManager ); + } + + /// + /// 复制Sql生成器 + /// + public override ISqlBuilder Clone() { + var result = new TestSqlBuilder(); + result.Clone( this ); + return result; + } +} \ No newline at end of file diff --git a/test/Util.Data.Sql.Tests/Util.Data.Sql.Tests.csproj b/test/Util.Data.Sql.Tests/Util.Data.Sql.Tests.csproj new file mode 100644 index 000000000..6299ebe8e --- /dev/null +++ b/test/Util.Data.Sql.Tests/Util.Data.Sql.Tests.csproj @@ -0,0 +1,25 @@ + + + + $(NetTargetFramework) + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.Data.Tests/Pages/PageListTest.cs b/test/Util.Data.Tests/Pages/PageListTest.cs new file mode 100644 index 000000000..ced862caa --- /dev/null +++ b/test/Util.Data.Tests/Pages/PageListTest.cs @@ -0,0 +1,46 @@ +using Util.Data.Tests.Samples; +using Xunit; + +namespace Util.Data.Tests.Pages; + +/// +/// 分页集合测试 +/// +public class PagerListTest { + /// + /// 分页集合 + /// + private readonly PageList _list; + + /// + /// 测试初始化 + /// + public PagerListTest() { + _list = new PageList( 1, 2, 3, "Name" ); + _list.Add( Sample.Create1() ); + _list.Add( Sample.Create2() ); + } + + /// + /// 测试集合 + /// + [Fact] + public void TestList() { + Assert.Equal( 2, _list.Data.Count ); + Assert.Equal( "B", _list[1].StringValue ); + } + + /// + /// 测试转换类型 + /// + [Fact] + public void TestConvert() { + var result = _list.Convert( t => new Sample() ); + Assert.Equal( 2, result.Data.Count ); + Assert.Equal( 1, result.Page ); + Assert.Equal( 2, result.PageSize ); + Assert.Equal( 3, result.Total ); + Assert.Equal( 2, result.PageCount ); + Assert.Equal( "Name", result.Order ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Tests/Pages/PagerTest.cs b/test/Util.Data.Tests/Pages/PagerTest.cs new file mode 100644 index 000000000..39bc3e6cf --- /dev/null +++ b/test/Util.Data.Tests/Pages/PagerTest.cs @@ -0,0 +1,134 @@ +using Util.Data.Queries; +using Xunit; + +namespace Util.Data.Tests.Pages; + +/// +/// 分页参数测试 +/// +public class PagerTest { + /// + /// 测试初始化 + /// + public PagerTest() { + _pager = new Pager(); + } + + /// + /// 分页参数 + /// + private readonly Pager _pager; + + /// + /// 测试分页默认值 + /// + [Fact] + public void TestDefault() { + Assert.Equal( 1, _pager.Page ); + Assert.Equal( 20, _pager.PageSize ); + Assert.Equal( 0, _pager.Total ); + Assert.Equal( 0, _pager.GetPageCount() ); + Assert.Equal( 0, _pager.GetSkipCount() ); + Assert.Equal( "", _pager.Order ); + Assert.Equal( 1, _pager.GetStartNumber() ); + Assert.Equal( 20, _pager.GetEndNumber() ); + } + + /// + /// 测试总页数 + /// + [Fact] + public void TestPageCount() { + _pager.Total = 0; + Assert.Equal( 0, _pager.GetPageCount() ); + + _pager.Total = 100; + Assert.Equal( 5, _pager.GetPageCount() ); + + _pager.Total = 1; + Assert.Equal( 1, _pager.GetPageCount() ); + + _pager.PageSize = 10; + _pager.Total = 100; + Assert.Equal( 10, _pager.GetPageCount() ); + } + + /// + /// 测试页索引 - 小于1,则修正为1 + /// + [Fact] + public void TestPage() { + _pager.Page = 0; + Assert.Equal( 1, _pager.Page ); + + _pager.Page = -1; + Assert.Equal( 1, _pager.Page ); + } + + /// + /// 测试跳过的行数 + /// + [Fact] + public void TestSkipCount() { + _pager.Total = 100; + + _pager.Page = 0; + Assert.Equal( 0, _pager.GetSkipCount() ); + + _pager.Page = 1; + Assert.Equal( 0, _pager.GetSkipCount() ); + + _pager.Page = 2; + Assert.Equal( 20, _pager.GetSkipCount() ); + + _pager.Page = 3; + Assert.Equal( 40, _pager.GetSkipCount() ); + + _pager.Page = 4; + Assert.Equal( 60, _pager.GetSkipCount() ); + + _pager.Page = 5; + Assert.Equal( 80, _pager.GetSkipCount() ); + + _pager.Page = 6; + Assert.Equal( 100, _pager.GetSkipCount() ); + + _pager.Total = 99; + + _pager.Page = 0; + Assert.Equal( 0, _pager.GetSkipCount() ); + + _pager.Page = 1; + Assert.Equal( 0, _pager.GetSkipCount() ); + + _pager.Page = 2; + Assert.Equal( 20, _pager.GetSkipCount() ); + + _pager.Page = 3; + Assert.Equal( 40, _pager.GetSkipCount() ); + + _pager.Page = 4; + Assert.Equal( 60, _pager.GetSkipCount() ); + + _pager.Page = 5; + Assert.Equal( 80, _pager.GetSkipCount() ); + + _pager.Page = 6; + Assert.Equal( 100, _pager.GetSkipCount() ); + + _pager.Total = 0; + _pager.Page = 1; + Assert.Equal( 0, _pager.GetSkipCount() ); + } + + /// + /// 测试起始和结束行数 + /// + [Fact] + public void TestGetStartNumber() { + _pager.Page = 2; + _pager.PageSize = 10; + Assert.Equal( 11, _pager.GetStartNumber() ); + Assert.Equal( 20, _pager.GetEndNumber() ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Tests/Queries/Conditions/DateSegmentConditionTest.cs b/test/Util.Data.Tests/Queries/Conditions/DateSegmentConditionTest.cs new file mode 100644 index 000000000..669378f8b --- /dev/null +++ b/test/Util.Data.Tests/Queries/Conditions/DateSegmentConditionTest.cs @@ -0,0 +1,110 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Text; +using Util.Data.Queries; +using Util.Data.Queries.Conditions; +using Util.Data.Tests.Samples; +using Util.Helpers; +using Xunit; +using Xunit.Abstractions; +using Thread = System.Threading.Thread; + +namespace Util.Data.Tests.Queries.Conditions; + +/// +/// 测试日期范围过滤条件 - 不包含时间 +/// +public class DateSegmentConditionTest { + /// + /// 输出日志 + /// + private readonly ITestOutputHelper _output; + + /// + /// 测试初始化 + /// + public DateSegmentConditionTest( ITestOutputHelper output) { + Thread.CurrentThread.CurrentCulture = new CultureInfo( "zh-CN" ) { + DateTimeFormat = new DateTimeFormatInfo { + ShortDatePattern = "yyyy/M/d" + } + }; + _output = output; + _min = "2000-1-1 10:10:10".ToDateTime(); + _max = "2000-1-3 10:10:10".ToDateTime(); + } + + /// + /// 最小日期 + /// + private readonly DateTime? _min; + /// + /// 最大日期 + /// + private readonly DateTime? _max; + + /// + /// 测试获取查询条件 - 不包含边界 + /// + [Fact] + public void TestGetCondition_Neither() { + var condition = new DateSegmentCondition( t => t.DateValue, _min, _max, Boundary.Neither ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + _output.WriteLine( condition.GetCondition().ToString() ); + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( "2000-1-2 00:00:00".ToDateTime(), Lambda.GetValue( leftExpression ).SafeString().ToDateTime() ); + Assert.Equal( Operator.Less, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( "2000-1-3 00:00:00".ToDateTime(), Lambda.GetValue( rightExpression ).SafeString().ToDateTime() ); + } + + /// + /// 测试获取查询条件 - 包含左边 + /// + [Fact] + public void TestGetCondition_Left() { + var condition = new DateSegmentCondition( t => t.DateValue, _min, _max, Boundary.Left ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + _output.WriteLine( condition.GetCondition().ToString() ); + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( "2000-1-1 00:00:00".ToDateTime(), Lambda.GetValue( leftExpression ).SafeString().ToDateTime() ); + Assert.Equal( Operator.Less, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( "2000-1-3 00:00:00".ToDateTime(), Lambda.GetValue( rightExpression ).SafeString().ToDateTime() ); + } + + /// + /// 测试获取查询条件 - 包含右边 + /// + [Fact] + public void TestGetCondition_Right() { + var condition = new DateSegmentCondition( t => t.NullableDateValue, _min, _max, Boundary.Right ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + _output.WriteLine( condition.GetCondition().ToString() ); + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( "2000-1-2 00:00:00".ToDateTime(), Lambda.GetValue( leftExpression ).SafeString().ToDateTime() ); + Assert.Equal( Operator.Less, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( "2000-1-4 00:00:00".ToDateTime(), Lambda.GetValue( rightExpression ).SafeString().ToDateTime() ); + } + + /// + /// 测试获取查询条件 - 包含两边 + /// + [Fact] + public void TestGetCondition_Both() { + var condition = new DateSegmentCondition( t => t.NullableDateValue, _min, _max, Boundary.Both ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + _output.WriteLine( condition.GetCondition().ToString() ); + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( "2000-1-1 00:00:00".ToDateTime(), Lambda.GetValue( leftExpression ).SafeString().ToDateTime() ); + Assert.Equal( Operator.Less, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( "2000-1-4 00:00:00".ToDateTime(), Lambda.GetValue( rightExpression ).SafeString().ToDateTime() ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Tests/Queries/Conditions/DateTimeSegmentConditionTest.cs b/test/Util.Data.Tests/Queries/Conditions/DateTimeSegmentConditionTest.cs new file mode 100644 index 000000000..d1aedee2a --- /dev/null +++ b/test/Util.Data.Tests/Queries/Conditions/DateTimeSegmentConditionTest.cs @@ -0,0 +1,128 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Text; +using Util.Data.Queries; +using Util.Data.Queries.Conditions; +using Util.Data.Tests.Samples; +using Util.Helpers; +using Xunit; + +namespace Util.Data.Tests.Queries.Conditions; + +/// +/// 测试日期范围过滤条件 - 包含时间 +/// +public class DateTimeSegmentConditionTest { + /// + /// 测试初始化 + /// + public DateTimeSegmentConditionTest() { + System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo( "zh-CN" ) { + DateTimeFormat = new DateTimeFormatInfo { + ShortDatePattern = "yyyy/M/d" + } + }; + _min = DateTime.Parse( "2000-1-1 10:10:10" ); + _max = DateTime.Parse( "2000-1-2 10:10:10" ); + } + + /// + /// 最小日期 + /// + private readonly DateTime? _min; + /// + /// 最大日期 + /// + private readonly DateTime? _max; + + /// + /// 测试获取查询条件 + /// + [Fact] + public void TestGetCondition_DateTime() { + var condition = new DateTimeSegmentCondition( t => t.DateValue, _min, _max ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( "2000-1-1 10:10:10".ToDateTime(), Lambda.GetValue( leftExpression ).SafeString().ToDateTime() ); + Assert.Equal( Operator.LessEqual, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( "2000-1-2 10:10:10".ToDateTime(), Lambda.GetValue( rightExpression ).SafeString().ToDateTime() ); + } + + /// + /// 测试获取查询条件 - 可空 + /// + [Fact] + public void TestGetCondition_NullableDateTime() { + var condition = new DateTimeSegmentCondition( t => t.NullableDateValue, _min, _max ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( "2000-1-1 10:10:10".ToDateTime(), Lambda.GetValue( leftExpression ).SafeString().ToDateTime() ); + Assert.Equal( Operator.LessEqual, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( "2000-1-2 10:10:10".ToDateTime(), Lambda.GetValue( rightExpression ).SafeString().ToDateTime() ); + } + + /// + /// 测试获取查询条件 - 不包含边界 + /// + [Fact] + public void TestGetCondition_Boundary_1() { + var condition = new DateTimeSegmentCondition( t => t.DateValue, _min, _max, Boundary.Neither ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + Assert.Equal( Operator.Greater, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( "2000-1-1 10:10:10".ToDateTime(), Lambda.GetValue( leftExpression ).SafeString().ToDateTime() ); + Assert.Equal( Operator.Less, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( "2000-1-2 10:10:10".ToDateTime(), Lambda.GetValue( rightExpression ).SafeString().ToDateTime() ); + } + + /// + /// 测试获取查询条件 - 包含左边界 + /// + [Fact] + public void TestGetCondition_Boundary_2() { + var condition = new DateTimeSegmentCondition( t => t.DateValue, _min, _max, Boundary.Left ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( "2000-1-1 10:10:10".ToDateTime(), Lambda.GetValue( leftExpression ).SafeString().ToDateTime() ); + Assert.Equal( Operator.Less, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( "2000-1-2 10:10:10".ToDateTime(), Lambda.GetValue( rightExpression ).SafeString().ToDateTime() ); + } + + /// + /// 测试获取查询条件 - 包含右边界 + /// + [Fact] + public void TestGetCondition_Boundary_3() { + var condition = new DateTimeSegmentCondition( t => t.NullableDateValue, _min, _max, Boundary.Right ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + Assert.Equal( Operator.Greater, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( "2000-1-1 10:10:10".ToDateTime(), Lambda.GetValue( leftExpression ).SafeString().ToDateTime() ); + Assert.Equal( Operator.LessEqual, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( "2000-1-2 10:10:10".ToDateTime(), Lambda.GetValue( rightExpression ).SafeString().ToDateTime() ); + } + + /// + /// 测试获取查询条件 - 包含两边 + /// + [Fact] + public void TestGetCondition_Boundary_4() { + var condition = new DateTimeSegmentCondition( t => t.NullableDateValue, _min, _max, Boundary.Both ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( "2000-1-1 10:10:10".ToDateTime(), Lambda.GetValue( leftExpression ).SafeString().ToDateTime() ); + Assert.Equal( Operator.LessEqual, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( "2000-1-2 10:10:10".ToDateTime(), Lambda.GetValue( rightExpression ).SafeString().ToDateTime() ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Tests/Queries/Conditions/DecimalSegmentConditionTest.cs b/test/Util.Data.Tests/Queries/Conditions/DecimalSegmentConditionTest.cs new file mode 100644 index 000000000..ce97808cc --- /dev/null +++ b/test/Util.Data.Tests/Queries/Conditions/DecimalSegmentConditionTest.cs @@ -0,0 +1,43 @@ +using System.Linq; +using Util.Data.Queries.Conditions; +using Util.Data.Tests.Samples; +using Util.Helpers; +using Xunit; +using Convert = Util.Helpers.Convert; + +namespace Util.Data.Tests.Queries.Conditions; + +/// +/// 测试double范围过滤条件 +/// +public class DecimalSegmentConditionTest { + /// + /// 测试获取查询条件 + /// + [Fact] + public void TestGetCondition_1() { + var condition = new DecimalSegmentCondition( t => t.DecimalValue, 1.1M, 10.1M ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( 1.1, Convert.ToDouble( Lambda.GetValue( leftExpression )) ); + Assert.Equal( Operator.LessEqual, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( 10.1, Convert.ToDouble( Lambda.GetValue( rightExpression ) ) ); + } + + /// + /// 测试获取查询条件 - 可空 + /// + [Fact] + public void TestGetCondition_2() { + var condition = new DecimalSegmentCondition( t => t.NullableDecimalValue, 1.1M, 10.1M ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( 1.1, Convert.ToDouble( Lambda.GetValue( leftExpression ) ) ); + Assert.Equal( Operator.LessEqual, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( 10.1, Convert.ToDouble( Lambda.GetValue( rightExpression ) ) ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Tests/Queries/Conditions/DoubleSegmentConditionTest.cs b/test/Util.Data.Tests/Queries/Conditions/DoubleSegmentConditionTest.cs new file mode 100644 index 000000000..7943ae2e3 --- /dev/null +++ b/test/Util.Data.Tests/Queries/Conditions/DoubleSegmentConditionTest.cs @@ -0,0 +1,154 @@ +using System.Linq; +using Util.Data.Queries; +using Util.Data.Queries.Conditions; +using Util.Data.Tests.Samples; +using Util.Helpers; +using Xunit; + +namespace Util.Data.Tests.Queries.Conditions; + +/// +/// 测试double范围过滤条件 +/// +public class DoubleSegmentConditionTest { + /// + /// 测试获取查询条件 + /// + [Fact] + public void TestGetCondition_1() { + var condition = new DoubleSegmentCondition( t => t.DoubleValue, 1.1, 10.1 ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( 1.1, Lambda.GetValue( leftExpression ) ); + Assert.Equal( Operator.LessEqual, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( 10.1, Lambda.GetValue( rightExpression ) ); + } + + /// + /// 测试获取查询条件 - 可空 + /// + [Fact] + public void TestGetCondition_2() { + var condition = new DoubleSegmentCondition( t => t.NullableDoubleValue, 1.1, 10.1 ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( 1.1, Lambda.GetValue( leftExpression ) ); + Assert.Equal( Operator.LessEqual, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( 10.1, Lambda.GetValue( rightExpression ) ); + } + + /// + /// 测试获取查询条件 - 不包含边界 + /// + [Fact] + public void TestGetCondition_3() { + var condition = new DoubleSegmentCondition( t => t.DoubleValue, 1.1, 10.1, Boundary.Neither ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + Assert.Equal( Operator.Greater, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( 1.1, Lambda.GetValue( leftExpression ) ); + Assert.Equal( Operator.Less, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( 10.1, Lambda.GetValue( rightExpression ) ); + } + + /// + /// 测试获取查询条件 - 包含左边界 + /// + [Fact] + public void TestGetCondition_4() { + var condition = new DoubleSegmentCondition( t => t.DoubleValue, 1.1, 10.1, Boundary.Left ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( 1.1, Lambda.GetValue( leftExpression ) ); + Assert.Equal( Operator.Less, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( 10.1, Lambda.GetValue( rightExpression ) ); + } + + /// + /// 测试获取查询条件 - 包含右边界 + /// + [Fact] + public void TestGetCondition_5() { + var condition = new DoubleSegmentCondition( t => t.NullableDoubleValue, 1.1, 10.1, Boundary.Right ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + Assert.Equal( Operator.Greater, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( 1.1, Lambda.GetValue( leftExpression ) ); + Assert.Equal( Operator.LessEqual, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( 10.1, Lambda.GetValue( rightExpression ) ); + } + + /// + /// 测试获取查询条件 - 包含两边 + /// + [Fact] + public void TestGetCondition_6() { + var condition = new DoubleSegmentCondition( t => t.NullableDoubleValue, 1.1, 10.1, Boundary.Both ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( 1.1, Lambda.GetValue( leftExpression ) ); + Assert.Equal( Operator.LessEqual, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( 10.1, Lambda.GetValue( rightExpression ) ); + } + + /// + /// 测试获取查询条件 - 最小值大于最大值,则交换大小值的位置 + /// + [Fact] + public void TestGetCondition_7() { + var condition = new DoubleSegmentCondition( t => t.DoubleValue, 10.1, 1.1 ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( 1.1, Lambda.GetValue( leftExpression ) ); + Assert.Equal( Operator.LessEqual, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( 10.1, Lambda.GetValue( rightExpression ) ); + } + + /// + /// 测试获取查询条件 - 最小值为空,忽略最小值条件 + /// + [Fact] + public void TestGetCondition_8() { + var condition = new DoubleSegmentCondition( t => t.DoubleValue, null, 10.1 ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + Assert.Equal( Operator.LessEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( 10.1, Lambda.GetValue( leftExpression ) ); + Assert.Single( expression); + } + + /// + /// 测试获取查询条件 - 最大值为空,忽略最大值条件 + /// + [Fact] + public void TestGetCondition_9() { + var condition = new DoubleSegmentCondition( t => t.DoubleValue, 1.1, null ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( 1.1, Lambda.GetValue( leftExpression ) ); + Assert.Single( expression ); + } + + /// + /// 测试获取查询条件 - 最大值为空,忽略最大值条件 + /// + [Fact] + public void TestGetCondition_10() { + var condition = new DoubleSegmentCondition( t => t.DoubleValue, null, null ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).FirstOrDefault(); + Assert.Null( expression ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Tests/Queries/Conditions/IntSegmentConditionTest.cs b/test/Util.Data.Tests/Queries/Conditions/IntSegmentConditionTest.cs new file mode 100644 index 000000000..9d01dd72f --- /dev/null +++ b/test/Util.Data.Tests/Queries/Conditions/IntSegmentConditionTest.cs @@ -0,0 +1,42 @@ +using System.Linq; +using Util.Data.Queries.Conditions; +using Util.Data.Tests.Samples; +using Util.Helpers; +using Xunit; + +namespace Util.Data.Tests.Queries.Conditions; + +/// +/// 测试整数范围过滤条件 +/// +public class IntSegmentConditionTest { + /// + /// 测试获取查询条件 + /// + [Fact] + public void TestGetCondition_1() { + var condition = new IntSegmentCondition( t => t.Tel, 1, 10 ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( 1, Convert.ToDouble( Lambda.GetValue( leftExpression ) ) ); + Assert.Equal( Operator.LessEqual, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( 10, Convert.ToDouble( Lambda.GetValue( rightExpression ) ) ); + } + + /// + /// 测试获取查询条件 - 可空 + /// + [Fact] + public void TestGetCondition_2() { + var condition = new IntSegmentCondition( t => t.Age, 1, 10 ); + var expression = Lambda.GetGroupPredicates( condition.GetCondition() ).First(); + var leftExpression = expression[0]; + var rightExpression = expression[1]; + Assert.Equal( Operator.GreaterEqual, Lambda.GetOperator( leftExpression ) ); + Assert.Equal( 1, Convert.ToDouble( Lambda.GetValue( leftExpression ) ) ); + Assert.Equal( Operator.LessEqual, Lambda.GetOperator( rightExpression ) ); + Assert.Equal( 10, Convert.ToDouble( Lambda.GetValue( rightExpression ) ) ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Tests/Queries/Conditions/OrIfNotEmptyConditionTest.cs b/test/Util.Data.Tests/Queries/Conditions/OrIfNotEmptyConditionTest.cs new file mode 100644 index 000000000..eb4cac1a7 --- /dev/null +++ b/test/Util.Data.Tests/Queries/Conditions/OrIfNotEmptyConditionTest.cs @@ -0,0 +1,89 @@ +using System; +using System.Linq.Expressions; +using Util.Data.Queries.Conditions; +using Util.Data.Tests.Samples; +using Xunit; + +namespace Util.Data.Tests.Queries.Conditions; + +/// +/// OrIfNotEmpty条件测试 +/// +public class OrIfNotEmptyConditionTest { + /// + /// 测试全部传入null + /// + [Fact] + public void Test_1() { + var condition = new OrIfNotEmptyCondition( null, null ); + Assert.Null( condition.GetCondition() ); + } + + /// + /// 测试条件2传入null + /// + [Fact] + public void Test_2() { + Expression> expression = t => t.StringValue == "a"; + var condition = new OrIfNotEmptyCondition( expression, null ); + Assert.Equal( expression, condition.GetCondition() ); + } + + /// + /// 测试条件1传入null + /// + [Fact] + public void Test_3() { + Expression> expression = t => t.StringValue == "a"; + var condition = new OrIfNotEmptyCondition( null, expression ); + Assert.Equal( expression, condition.GetCondition() ); + } + + /// + /// 测试三个有效条件 + /// + [Fact] + public void Test_4() { + Expression> expression1 = t => t.Email == "a"; + Expression> expression2 = t => t.Url == "b"; + Expression> expression3 = t => t.StringValue == "c"; + Expression> result = t => t.Email == "a" || t.Url == "b" || t.StringValue == "c"; + var condition = new OrIfNotEmptyCondition( expression1, expression2, expression3 ); + Assert.Equal( result.ToString(), condition.GetCondition().ToString() ); + } + + /// + /// 测试一个条件被忽略 + /// + [Fact] + public void Test_5() { + Expression> expression1 = t => t.Email == "a"; + Expression> expression2 = t => t.Url == ""; + var condition = new OrIfNotEmptyCondition( expression1, expression2 ); + Assert.Equal( expression1, condition.GetCondition() ); + } + + /// + /// 测试一个条件被忽略 + /// + [Fact] + public void Test_6() { + Expression> expression1 = t => t.Email == "a"; + Expression> expression2 = t => t.Url == "b"; + Expression> expression3 = t => t.StringValue == ""; + Expression> result = t => t.Email == "a" || t.Url == "b"; + var condition = new OrIfNotEmptyCondition( expression1, expression2, expression3 ); + Assert.Equal( result.ToString(), condition.GetCondition().ToString() ); + } + + /// + /// 测试无效条件 + /// + [Fact] + public void Test_7() { + Expression> expression1 = t => t.StringValue == "a"; + Expression> expression2 = t => t.Email == "a" && t.Url == "b"; + var condition = new OrIfNotEmptyCondition( expression1, expression2 ); + Assert.Throws( () => condition.GetCondition() ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Tests/Queries/Conditions/WhereIfNotEmptyConditionTest.cs b/test/Util.Data.Tests/Queries/Conditions/WhereIfNotEmptyConditionTest.cs new file mode 100644 index 000000000..5918a0a5a --- /dev/null +++ b/test/Util.Data.Tests/Queries/Conditions/WhereIfNotEmptyConditionTest.cs @@ -0,0 +1,42 @@ +using System; +using System.Linq.Expressions; +using Util.Data.Queries.Conditions; +using Util.Data.Tests.Samples; +using Xunit; + +namespace Util.Data.Tests.Queries.Conditions; + +/// +/// WhereIfNotEmpty条件测试 +/// +public class WhereIfNotEmptyConditionTest { + /// + /// 测试有效条件 + /// + [Fact] + public void Test_Valid() { + Expression> expression = t => t.StringValue == "a"; + var condition = new WhereIfNotEmptyCondition( expression ); + Assert.Equal( expression, condition.GetCondition() ); + } + + /// + /// 测试忽略条件 + /// + [Fact] + public void Test_Ignore() { + Expression> expression = t => t.StringValue == ""; + var condition = new WhereIfNotEmptyCondition( expression ); + Assert.Null( condition.GetCondition() ); + } + + /// + /// 测试无效条件 + /// + [Fact] + public void Test_Invalid() { + Expression> expression = t => t.StringValue == "a" && t.IntValue == 1; + var condition = new WhereIfNotEmptyCondition( expression ); + Assert.Throws( () => condition.GetCondition() ); + } +} \ No newline at end of file diff --git a/test/Util.Data.Tests/Samples/Sample.cs b/test/Util.Data.Tests/Samples/Sample.cs new file mode 100644 index 000000000..ce3c1b141 --- /dev/null +++ b/test/Util.Data.Tests/Samples/Sample.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace Util.Data.Tests.Samples; + +/// +/// 测试样例 +/// +public class Sample { + /// + /// 初始化测试样例 + /// + public Sample() { + StringList = new List(); + StringArray = StringList.ToArray(); + } + + /// + /// 年龄 + /// + public int? Age { get; set; } + + /// + /// 电话 + /// + [Description( "电话" )] + public int Tel { get; set; } + /// + /// 测试值 + /// + [Display( Name = "测试值" )] + [Required( ErrorMessage = "测试值不能为空" )] + [StringLength( 20, ErrorMessage = "测试值长度不能超过20" )] + [EmailAddress] + [Url] + public string TestValue { get; set; } + /// + /// 电子邮件验证 + /// + [EmailAddress( ErrorMessage = "电子邮件不正确" )] + public string Email { get; set; } + /// + /// 网址验证 + /// + [Url( ErrorMessage = "网址不正确" )] + public string Url { get; set; } + /// + /// 最大长度 + /// + [MaxLength( 2, ErrorMessage = "最大长度是2" )] + public string MaxLengthValue { get; set; } + /// + /// 长度验证 + /// + [StringLength( 3, MinimumLength = 2, ErrorMessage = "最大长度是3,最小长度是2" )] + public string StringLengthValue { get; set; } + /// + /// DisplayName + /// + [DisplayName( "DisplayNameValue值" )] + public string DisplayNameValue { get; set; } + /// + /// Display + /// + [Display( Name = "DisplayValue值" )] + public string DisplayValue { get; set; } + /// + /// string值 + /// + [Display( Name = "字符串值" )] + [StringLength( 20, ErrorMessage = "长度不能超过20" )] + [Required( ErrorMessage = "不能为空" )] + public string StringValue { get; set; } + /// + /// decimal值 + /// + public decimal DecimalValue { get; set; } + /// + /// 可空decimal值 + /// + public decimal? NullableDecimalValue { get; set; } + /// + /// float值 + /// + public float FloatValue { get; set; } + /// + /// 可空float值 + /// + public float? NullableFloatValue { get; set; } + /// + /// double值 + /// + public double DoubleValue { get; set; } + /// + /// 可空double值 + /// + public double? NullableDoubleValue { get; set; } + /// + /// bool值 + /// + public bool BoolValue { get; set; } + /// + /// 可空bool值 + /// + public bool? NullableBoolValue { get; set; } + /// + /// DateTime值 + /// + public DateTime DateValue { get; set; } + /// + /// 可空DateTime值 + /// + public DateTime? NullableDateValue { get; set; } + /// + /// int值 + /// + [Description( "IntValue" )] + public int IntValue { get; set; } + /// + /// 可空int值 + /// + public int? NullableIntValue { get; set; } + /// + /// short值 + /// + public short ShortValue { get; set; } + /// + /// 可空short值 + /// + public short? NullableShortValue { get; set; } + /// + /// long值 + /// + public long LongValue { get; set; } + /// + /// 可空long值 + /// + public long? NullableLongValue { get; set; } + /// + /// 字符串列表 + /// + public List StringList { get; set; } + /// + /// 字符串数据 + /// + public string[] StringArray { get; set; } + /// + /// Guid值 + /// + public Guid GuidValue { get; set; } + + /// + /// 创建测试实例1 + /// + public static Sample Create1() { + return new Sample { StringValue = "A" }; + } + + /// + /// 创建测试实例2 + /// + public static Sample Create2() { + return new Sample { StringValue = "B" }; + } +} \ No newline at end of file diff --git a/test/Util.Data.Tests/Util.Data.Tests.csproj b/test/Util.Data.Tests/Util.Data.Tests.csproj new file mode 100644 index 000000000..70caa8d58 --- /dev/null +++ b/test/Util.Data.Tests/Util.Data.Tests.csproj @@ -0,0 +1,25 @@ + + + + $(NetTargetFramework) + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.Datas.Tests.Integration/02-Util.Datas.Tests.Integration.csproj b/test/Util.Datas.Tests.Integration/02-Util.Datas.Tests.Integration.csproj deleted file mode 100644 index 62224711e..000000000 --- a/test/Util.Datas.Tests.Integration/02-Util.Datas.Tests.Integration.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - netcoreapp2.0 - Util.Datas.Tests.Integration - Util.Datas.Tests - - 何镇汐;dotnetcore - - - - ..\..\output\test - - - - ..\..\output\test - - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - - diff --git a/test/Util.Datas.Tests.Integration/02-Util.Datas.Tests.Integration.csproj.user b/test/Util.Datas.Tests.Integration/02-Util.Datas.Tests.Integration.csproj.user deleted file mode 100644 index baf24173c..000000000 --- a/test/Util.Datas.Tests.Integration/02-Util.Datas.Tests.Integration.csproj.user +++ /dev/null @@ -1,6 +0,0 @@ - - - - false - - \ No newline at end of file diff --git a/test/Util.Datas.Tests.Integration/Logs/EfLogTest.cs b/test/Util.Datas.Tests.Integration/Logs/EfLogTest.cs deleted file mode 100644 index 2fb927ef2..000000000 --- a/test/Util.Datas.Tests.Integration/Logs/EfLogTest.cs +++ /dev/null @@ -1,248 +0,0 @@ -using System.Linq; -using System.Text.RegularExpressions; -using Util.Datas.Ef.Logs; -using Xunit; - -namespace Util.Datas.Tests.Logs { - /// - /// Ef日志测试 - /// - public class EfLogTest { - /// - /// 获取Sql参数字典 - /// - [Fact] - public void TestGetSqlParameters_1() { - var sqlParams = "@p0='c4a42939-3569-4915-bb53-14edad2323cf'"; - var list = EfLog.GetSqlParameters( sqlParams ); - Assert.Equal( 1, list.Count ); - Assert.Equal( "@p0", list.Keys.FirstOrDefault() ); - Assert.Equal( "'c4a42939-3569-4915-bb53-14edad2323cf'", list.Values.FirstOrDefault() ); - } - - /// - /// 获取Sql参数字典 - /// - [Fact] - public void TestGetSqlParameters_2() { - Assert.Empty( EfLog.GetSqlParameters( null ) ); - Assert.Empty( EfLog.GetSqlParameters( "" ) ); - Assert.Empty( EfLog.GetSqlParameters( " " ) ); - } - - /// - /// 获取Sql参数字典 - /// - [Fact] - public void TestGetSqlParameters_3() { - var sqlParams = "@p0='70eac351-1eb0-45d1-b431-431646df7ab1' (Nullable = false) (Size = 450), @p1='18.52'"; - var list = EfLog.GetSqlParameters( sqlParams ); - Assert.Equal( 2, list.Count ); - Assert.Equal( "@p0", list.Keys.FirstOrDefault() ); - Assert.Equal( "'70eac351-1eb0-45d1-b431-431646df7ab1'", list.Values.FirstOrDefault() ); - Assert.Equal( "@p1", list.Keys.LastOrDefault() ); - Assert.Equal( "'18.52'", list.Values.LastOrDefault() ); - } - - /// - /// 获取Sql参数字典 - /// - [Fact] - public void TestGetSqlParameters_4() { - var sqlParams = "@p0='' (Nullable = false) (Size = 450)"; - var list = EfLog.GetSqlParameters( sqlParams ); - Assert.Equal( 1, list.Count ); - Assert.Equal( "@p0", list.Keys.FirstOrDefault() ); - Assert.Equal( "''", list.Values.FirstOrDefault() ); - } - - /// - /// 获取Sql参数字典 - /// - [Fact] - public void TestGetSqlParameters_5() { - var sqlParams = "@p0='70eac351-1eb0-45d1-b431-431646df7ab1' (Nullable = false) (Size = 450), @p1='18.52', @p2='11/15/2017 00:00:41' (Nullable = true), @p3='' (DbType = Guid), @p4='' (Size = 100) (DbType = String), @p5='' (DbType = Int32), @p6='11/15/2017 00:00:41' (Nullable = true), @p7='' (DbType = Guid), @p8='' (Size = 20) (DbType = String), @p9='5a0b1329ab89e01250' (Nullable = false) (Size = 20), @p10='' (Size = 30) (DbType = String), @p11='' (Size = 20) (DbType = String), @p12='c4f1ba98-dab6-451f-8257-3c35ab06a304', @p13='code' (Nullable = false) (Size = 30), @p14='a' (Nullable = false) (Size = 200)"; - var list = EfLog.GetSqlParameters( sqlParams ); - Assert.Equal( 15, list.Count ); - Assert.Equal( "@p0", list.Keys.FirstOrDefault() ); - Assert.Equal( "'70eac351-1eb0-45d1-b431-431646df7ab1'", list.Values.FirstOrDefault() ); - Assert.Equal( "@p14", list.Keys.LastOrDefault() ); - Assert.Equal( "'a'", list.Values.LastOrDefault() ); - } - - /// - /// 获取Sql参数字典 - /// - [Fact] - public void TestGetSqlParameters_6() { - var sqlParams = "@p0='' (DbType = Guid)"; - var list = EfLog.GetSqlParameters( sqlParams ); - Assert.Equal( 1, list.Count ); - Assert.Equal( "@p0", list.Keys.FirstOrDefault() ); - Assert.Equal( "null", list.Values.FirstOrDefault() ); - } - - /// - /// 获取Sql参数字典 - /// - [Fact] - public void TestGetSqlParameters_7() { - var sqlParams = "@p0='{'a':'a','b':'b','c':'888'}' (Size = 500)"; - var list = EfLog.GetSqlParameters( sqlParams ); - Assert.Equal( 1, list.Count ); - Assert.Equal( "@p0", list.Keys.FirstOrDefault() ); - Assert.Equal( "'{'a':'a','b':'b','c':'888'}'", list.Values.FirstOrDefault() ); - } - - /// - /// 获取Sql参数字典 - /// - [Fact] - public void TestGetSqlParameters_8() { - var sqlParams = "@p0='{'a':'a','b':'b','c':'888'}' (Size = 500), @p1='18.52'"; - var list = EfLog.GetSqlParameters( sqlParams ); - Assert.Equal( 2, list.Count ); - Assert.Equal( "@p0", list.Keys.FirstOrDefault() ); - Assert.Equal( "'{'a':'a','b':'b','c':'888'}'", list.Values.FirstOrDefault() ); - Assert.Equal( "@p1", list.Keys.LastOrDefault() ); - Assert.Equal( "'18.52'", list.Values.LastOrDefault() ); - } - - /// - /// 获取Sql参数字典 - /// - [Fact] - public void TestGetSqlParameters_9() { - var sqlParams = "@__a_b_0='70eac351-1eb0-45d1-b431-431646df7ab1' (Nullable = false)"; - var list = EfLog.GetSqlParameters( sqlParams ); - Assert.Equal( 1, list.Count ); - Assert.Equal( "@__a_b_0", list.Keys.FirstOrDefault() ); - Assert.Equal( "'70eac351-1eb0-45d1-b431-431646df7ab1'", list.Values.FirstOrDefault() ); - } - - /// - /// 获取Sql参数字典 - /// - [Fact(Skip = "尚未实现")] - public void TestGetSqlParameters_10() { - var sqlParams = "@__a_b_0='a' (Nullable = false),@__a_c='b' (Nullable = false)"; - var list = EfLog.GetSqlParameters( sqlParams ); - Assert.Equal( 2, list.Count ); - Assert.Equal( "@__a_b_0", list.Keys.FirstOrDefault() ); - Assert.Equal( "'a'", list.Values.FirstOrDefault() ); - Assert.Equal( "@__a_c", list.Keys.LastOrDefault() ); - Assert.Equal( "'b'", list.Values.LastOrDefault() ); - } - - /// - /// 获取Sql - /// - [Fact] - public void TestGetSql_1() { - var sql = "VALUES (@p0)"; - var sqlParams = "@p0='c4a42939-3569-4915-bb53-14edad2323cf'"; - var result = EfLog.GetSql( sql, sqlParams ); - Assert.Equal( "VALUES ('c4a42939-3569-4915-bb53-14edad2323cf')", result ); - } - - /// - /// 获取Sql - /// - [Fact] - public void TestGetSql_2() { - var sql = "VALUES (@p0, @p1)"; - var sqlParams = "@p0='c4a42939-3569-4915-bb53-14edad2323cf', @p1='18.52'"; - var result = EfLog.GetSql( sql, sqlParams ); - Assert.Equal( "VALUES ('c4a42939-3569-4915-bb53-14edad2323cf', '18.52')", result ); - } - - /// - /// 获取Sql - /// - [Fact] - public void TestGetSql_3() { - var sql = "VALUES (1)"; - var result = EfLog.GetSql( sql, null ); - Assert.Equal( sql, result ); - result = EfLog.GetSql( sql, "" ); - Assert.Equal( sql, result ); - } - - /// - /// 获取Sql - /// - [Fact] - public void TestGetSql_4() { - var sql = "VALUES (@p0,@p1,@p12)"; - var sqlParams = "@p0='a', @p1='1.5',@p12='b'"; - var result = EfLog.GetSql( sql, sqlParams ); - Assert.Equal( "VALUES ('a','1.5','b')", result ); - } - - /// - /// 获取Sql - /// - [Fact] - public void TestGetSql_5() { - var sql = "VALUES (@p1111,@p111,@p1,@p11,@p1)"; - var sqlParams = "@p111='c',@p11='b',@p1='a' "; - var result = EfLog.GetSql( sql, sqlParams ); - Assert.Equal( "VALUES (@p1111,'c','a','b','a')", result ); - } - - /// - /// 获取Sql - /// - [Fact] - public void TestGetSql_6() { - var sql = "VALUES (@p1)"; - var sqlParams = "@p1='' (DbType = Guid)"; - var result = EfLog.GetSql( sql, sqlParams ); - Assert.Equal( "VALUES (null)", result ); - } - - /// - /// 获取Sql - /// - [Fact] - public void TestGetSql_7() { - var sql = "VALUES (@p0)"; - var sqlParams = "@p0='{\"a\":1}'"; - var result = EfLog.GetSql( sql, sqlParams ); - Assert.Equal( "VALUES ('{\"a\":1}')", result ); - } - - /// - /// 获取Sql - /// - [Fact] - public void TestGetSql_8() { - var sql = "VALUES (@p0)"; - var sqlParams = "@p0='{'a':1}'"; - var result = EfLog.GetSql( sql, sqlParams ); - Assert.Equal( "VALUES ('{'a':1}')", result ); - } - - /// - /// 获取Sql - /// - [Fact] - public void TestGetSql_9() { - var sql = "VALUES (@p0,@p1)"; - var sqlParams = "@p0='{'a':'a','b':'b','c':'888'}' (Size = 500), @p1='18.52'"; - var result = EfLog.GetSql( sql, sqlParams ); - Assert.Equal( "VALUES ('{'a':'a','b':'b','c':'888'}','18.52')", result ); - } - - /// - /// 获取Sql - /// - [Fact] - public void TestGetSql_10() { - var sql = "VALUES (@__orderQuery_memberId_0,@__orderQuery_memberId_1)"; - var sqlParams = "@__orderQuery_memberId_0='{'a':'a','b':'b','c':'888'}' (Size = 500), @__orderQuery_memberId_1='18.52'"; - var result = EfLog.GetSql( sql, sqlParams ); - Assert.Equal( "VALUES ('{'a':'a','b':'b','c':'888'}','18.52')", result ); - } - } -} diff --git a/test/Util.Datas.Tests.Integration/PgSql/Configs/AppConfig.cs b/test/Util.Datas.Tests.Integration/PgSql/Configs/AppConfig.cs deleted file mode 100644 index 3d7a8aea0..000000000 --- a/test/Util.Datas.Tests.Integration/PgSql/Configs/AppConfig.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Util.Dependency; - -namespace Util.Datas.Tests.PgSql.Configs { - /// - /// 测试应用程序配置 - /// - public static class AppConfig { - /// - /// PgSql测试容器 - /// - public static IContainer Container { get; set; } - /// - /// 连接字符串 - /// - public const string Connection = "server=192.168.244.138;User Id=postgres;password=sa;database=UtilTest"; - /// - /// 当前用户编号 - /// - public const string UserId = "71B18059-7572-4EC3-8B11-2BAA0D0F6169"; - } -} diff --git a/test/Util.Datas.Tests.Integration/PgSql/Configs/GlobalConfig.cs b/test/Util.Datas.Tests.Integration/PgSql/Configs/GlobalConfig.cs deleted file mode 100644 index 608965aa6..000000000 --- a/test/Util.Datas.Tests.Integration/PgSql/Configs/GlobalConfig.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Xunit; - -namespace Util.Datas.Tests.PgSql.Configs { - /// - /// 全局测试配置 - /// - [CollectionDefinition( "PgSqlGlobalConfig" )] - public class GlobalConfig : ICollectionFixture { - } -} diff --git a/test/Util.Datas.Tests.Integration/PgSql/Configs/GlobalFixture.cs b/test/Util.Datas.Tests.Integration/PgSql/Configs/GlobalFixture.cs deleted file mode 100644 index 79fb04124..000000000 --- a/test/Util.Datas.Tests.Integration/PgSql/Configs/GlobalFixture.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Util.Helpers; - -namespace Util.Datas.Tests.PgSql.Configs { - /// - /// 全局测试配置 - /// - public class GlobalFixture { - /// - /// 测试初始化 - /// - public GlobalFixture() { - AppConfig.Container = Ioc.CreateContainer( new IocConfig() ); - } - } -} diff --git a/test/Util.Datas.Tests.Integration/PgSql/Configs/IocConfig.cs b/test/Util.Datas.Tests.Integration/PgSql/Configs/IocConfig.cs deleted file mode 100644 index a1361cf85..000000000 --- a/test/Util.Datas.Tests.Integration/PgSql/Configs/IocConfig.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Autofac; -using Util.Datas.Tests.Samples.Datas.PgSql.Repositories; -using Util.Datas.Tests.Samples.Datas.PgSql.UnitOfWorks; -using Util.Datas.Tests.Samples.Domains.Repositories; -using Util.Datas.UnitOfWorks; -using Util.Dependency; -using Util.Domains.Sessions; - -namespace Util.Datas.Tests.PgSql.Configs { - /// - /// 依赖注入配置 - /// - public class IocConfig : ConfigBase { - /// - /// 加载配置 - /// - protected override void Load( ContainerBuilder builder ) { - LoadInfrastructure( builder ); - LoadRepositories( builder ); - } - - /// - /// 加载基础设施 - /// - private void LoadInfrastructure( ContainerBuilder builder ) { - builder.AddScoped(); - builder.AddScoped().PropertiesAutowired(); - builder.AddSingleton( new Session( AppConfig.UserId ) ); - } - - /// - /// 加载仓储 - /// - private void LoadRepositories( ContainerBuilder builder ) { - builder.AddScoped(); - } - } -} \ No newline at end of file diff --git a/test/Util.Datas.Tests.Integration/PgSql/Scripts/Pd/Samples.pdb b/test/Util.Datas.Tests.Integration/PgSql/Scripts/Pd/Samples.pdb deleted file mode 100644 index 516dc160a..000000000 --- a/test/Util.Datas.Tests.Integration/PgSql/Scripts/Pd/Samples.pdb +++ /dev/null @@ -1,6114 +0,0 @@ - - - - - - - - - -983AA762-5652-4ACC-9A88-2B8B90A0CAA9 -Samples2 -Samples2 -1514588679 -Administrator -1514588679 -Administrator -ORG {91AD3F79-C708-47A6-81A2-7B9E077A7DF4} -DAT 1514588679 -[FolderOptions] - -[FolderOptions\Physical Objects] -GenerationCheckModel=Yes -GenerationPath= -GenerationOptions= -GenerationTasks= -GenerationTargets= -GenerationSelections= -RevPkey=Yes -RevFkey=Yes -RevAkey=Yes -RevCheck=Yes -RevIndx=Yes -RevOpts=Yes -RevViewAsTabl=No -RevViewOpts=Yes -RevSystAsTabl=Yes -RevTablPerm=No -RevViewPerm=No -RevProcPerm=No -RevDbpkPerm=No -RevSqncPerm=No -RevAdtPerm=No -RevUserPriv=No -RevUserOpts=No -RevGrpePriv=No -RevRolePriv=No -RevDtbsOpts=Yes -RevDtbsPerm=No -RevViewIndx=Yes -RevJidxOpts=Yes -RevStats=No -RevTspcPerm=No -RevCaseSensitive=No -GenTrgrStdMsg=Yes -GenTrgrMsgTab= -GenTrgrMsgNo= -GenTrgrMsgTxt= -TrgrPreserve=No -TrgrIns=Yes -TrgrUpd=Yes -TrgrDel=Yes -TrgrC2Ins=Yes -TrgrC2Upd=Yes -TrgrC3=Yes -TrgrC4=Yes -TrgrC5=Yes -TrgrC6=Yes -TrgrC7=Yes -TrgrC8=Yes -TrgrC9=Yes -TrgrC10=Yes -TrgrC11=Yes -TrgrC1=Yes -TrgrC12Ins=Yes -TrgrC12Upd=Yes -TrgrC13=Yes -UpdateTableStatistics=Yes -UpdateColumnStatistics=Yes - -[FolderOptions\Physical Objects\Database Generation] -GenScriptName=crebas -GenScriptName0= -GenScriptName1= -GenScriptName2= -GenScriptName3= -GenScriptName4= -GenScriptName5= -GenScriptName6= -GenScriptName7= -GenScriptName8= -GenScriptName9= -GenPathName= -GenSingleFile=Yes -GenODBC=No -GenCheckModel=Yes -GenScriptPrev=Yes -GenArchiveModel=No -GenUseSync=No -GenSyncChoice=0 -GenSyncArch= -GenSyncRmg=0 - -[FolderOptions\Physical Objects\Database Generation\Format] -GenScriptTitle=Yes -GenScriptNamLabl=No -GenScriptQDtbs=No -GenScriptQOwnr=Yes -GenScriptCase=0 -GenScriptEncoding=ANSI -GenScriptNAcct=No -IdentifierDelimiter=" - -[FolderOptions\Physical Objects\Database Generation\Database] -Create=Yes -Open=Yes -Close=Yes -Drop=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Database\Create] -Physical Options=Yes -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\Tablespace] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Tablespace\Create] -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\Storage] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\User] -Create=Yes -Drop=Yes -Comment=Yes -Privilege=No - -[FolderOptions\Physical Objects\Database Generation\User\Create] -Physical Options=No - -[FolderOptions\Physical Objects\Database Generation\Group] -Create=Yes -Drop=Yes -Comment=Yes -Privilege=No - -[FolderOptions\Physical Objects\Database Generation\Role] -Create=Yes -Drop=Yes -Privilege=No - -[FolderOptions\Physical Objects\Database Generation\UserDefinedDataType] -Create=Yes -Comment=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\UserDefinedDataType\Create] -Default value=Yes -Check=Yes - -[FolderOptions\Physical Objects\Database Generation\AbstractDataType] -Create=Yes -Header=Yes -Footer=Yes -Drop=Yes -Comment=Yes -Install JAVA class=Yes -Remove JAVA class=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Rule] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Default] -Create=Yes -Comment=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\Sequence] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Table&&Column] - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Table] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Table\Create] -Check=Yes -Physical Options=Yes -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Table\Create\Check] -Constraint declaration=No - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Column] -User datatype=Yes -Default value=Yes -Check=Yes -Physical Options=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Column\Check] -Constraint declaration=No - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key] - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key\Primary key] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key\Primary key\Create] -Constraint declaration=No -Physical Options=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key\Alternate key] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key\Alternate key\Create] -Constraint declaration=No -Physical Options=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Foreign key] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Foreign key\Create] -Constraint declaration=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Index] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Index\Create] -Constraint declaration=Yes -Physical Options=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Index\Filter] -Primary key=No -Foreign key=Yes -Alternate key=No -Cluster=Yes -Other=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Trigger] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Trigger\Filter] -For insert=Yes -For update=Yes -For delete=Yes -For other=Yes - -[FolderOptions\Physical Objects\Database Generation\View] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\View\Create] -Force Column list=No -Physical Options=Yes -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\View\ViewColumn] -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\View\ViewIndex] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\View\ViewIndex\Create] -Physical Options=Yes - -[FolderOptions\Physical Objects\Database Generation\View\ViewIndex\Filter] -Cluster=Yes -Other=Yes - -[FolderOptions\Physical Objects\Database Generation\View\Trigger] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\View\Trigger\Filter] -For insert=Yes -For update=Yes -For delete=Yes -For other=Yes - -[FolderOptions\Physical Objects\Database Generation\DBMSTrigger] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Synonym] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\Synonym\Filter] -Table=Yes -View=Yes -Proc=Yes -Synonym=Yes -Database Package=Yes -Sequence=Yes - -[FolderOptions\Physical Objects\Database Generation\JoinIndex] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\JoinIndex\Create] -Physical Options=Yes -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\Procedure] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Procedure\Create] -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\DatabasePackage] -Create=Yes -Drop=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\WebService] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Dimension] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\Synchronization] -GenBackupTabl=1 -GenKeepBackTabl=1 -GenTmpTablDrop=No -GenKeepTablOpts=No - -[FolderOptions\Physical Objects\Database Generation\User <<Schema>>] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\User <<User>>] -Create=Yes -Drop=Yes -Comment=Yes -Privilege=No - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Aggregate>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Aggregate>>\ExtendedSubObject <<AggregateParameter>>] -Create=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Assembly>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<AsymmetricKey>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Certificate>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Contract>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<EndPoint>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<EventNotification>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<FullTextCatalog>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<MessageType>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<PartitionFunction>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<PartitionScheme>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Queue>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<RemoteServiceBinding>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<ResourcePool>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Route>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Service>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<SymmetricKey>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<WorkloadGroup>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<XMLSchemaCollection>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Test Data] -GenDataPathName= -GenDataSinglefile=Yes -GenDataScriptName=testdata -GenDataScriptName0= -GenDataScriptName1= -GenDataScriptName2= -GenDataScriptName3= -GenDataScriptName4= -GenDataScriptName5= -GenDataScriptName6= -GenDataScriptName7= -GenDataScriptName8= -GenDataScriptName9= -GenDataOdbc=0 -GenDataDelOld=No -GenDataTitle=No -GenDataDefNumRows=20 -GenDataCommit=0 -GenDataPacket=0 -GenDataOwner=No -GenDataProfNumb= -GenDataProfChar= -GenDataProfDate= -GenDataCSVSeparator=, -GenDataFileFormat=CSV -GenDataUseWizard=No - -[FolderOptions\Pdm] -IndxIQName=%COLUMN%_%INDEXTYPE% -IndxPK=Yes -IndxFK=Yes -IndxAK=Yes -IndxPKName=%TABLE%_PK -IndxFKName=%REFR%_FK -IndxAKName=%AKEY%_AK -IndxPreserve=No -IndxThreshold=0 -IndxStats=No -RefrPreserve=No -JidxPreserve=No -RbldMultiFact=Yes -RbldMultiDim=Yes -RbldMultiJidx=Yes -CubePreserve=No -TablStProcPreserve=No -ProcDepPreserve=Yes -TrgrDepPreserve=Yes -CubeScriptPath= -CubeScriptCase=0 -CubeScriptEncoding=ANSI -CubeScriptNacct=No -CubeScriptHeader=No -CubeScriptExt=csv -CubeScriptExt0=txt -CubeScriptExt1= -CubeScriptExt2= -CubeScriptSep=, -CubeScriptDeli=" -EstimationYears=0 -DfltDomnName=D_%.U:VALUE% -DfltColnName=D_%.U:VALUE% -DfltReuse=Yes -DfltDrop=Yes - -[FolderOptions\CheckModel] - -[FolderOptions\CheckModel\Package] - -[FolderOptions\CheckModel\Package\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\CheckPackageMissTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\DefaultCheckPackageMissTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\CircularReference] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\ConstraintName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\CnstMaxLen] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\CircularDependency] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\ShortcutUniqCode] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Table] - -[FolderOptions\CheckModel\Table\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\UniqIndex] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\MaxLen - NAME] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\EmptyColl - COLNCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\EmptyColl - INDXCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\EmptyColl - KEYCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\SerialColumnNumber] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\EmptyCollYesYes] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\TableIndexes] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\Mapping] -CheckSeverity=No -FixRequested=Yes -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\MappingSFMap] -CheckSeverity=No -FixRequested=Yes -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\EmptyColl - PERMCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Table\CheckTablePartitionKey] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckTableStartDate] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckTableRefNoLifecycle] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckTableSourceMapping] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckTablePartialColumnMapping] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckTableKeyColumnMapping] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckTableNotOnLifecycleTablespace] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\MSSQLSRV2012_Table_Table_Timestamp_Uniqueness] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column] - -[FolderOptions\CheckModel\Table.Column\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\DomainDivergence] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\ColumnMandatory] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckNumParam] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckPrecSupLng] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckUndefDttp] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\FkeyDttpDivergence] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\FkeyCheckDivergence] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\ColnSqncNoKey] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\ColnSqncDttp] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\SerialColumnFK] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\ColumnCompExpr] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckColumnOneToOneMapping] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckColumnDataTypeMapping] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckColumnNoMapping] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckDttpIncompatibleFormat] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\MSSQLSRV2012_Column_Identity_Seed_and_Increment_validity] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index] - -[FolderOptions\CheckModel\Table.Index\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\EmptyColl - CIDXCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\UndefIndexType] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\IndexColumnCount] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\IQIndxHNGUniq] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\CheckIncludeColl - Tabl] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\MSSQLSRV2012_BaseIndex_XML_index_specific_checks] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key] - -[FolderOptions\CheckModel\Table.Key\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\EmptyColl - COLNCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\CheckIncludeColl - Tabl] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\MultiKeySqnc] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger] - -[FolderOptions\CheckModel\Table.Trigger\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index] - -[FolderOptions\CheckModel\Join Index\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View] - -[FolderOptions\CheckModel\View\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\EmptyColl - PERMCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\View.View Index] - -[FolderOptions\CheckModel\View.View Index\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\EmptyColl - CIDXCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\IndexColumnCount] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\CheckIncludeColl - Tabl] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\MSSQLSRV2012_BaseIndex_XML_index_specific_checks] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference] - -[FolderOptions\CheckModel\Reference\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\Reflexive] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\EmptyColl - RFJNCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\IncompleteJoin] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\JoinOrder] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference] - -[FolderOptions\CheckModel\View Reference\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\EmptyColl - VRFJNCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain] - -[FolderOptions\CheckModel\Domain\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\CheckNumParam] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\CheckPrecSupLng] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\CheckUndefDttp] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\CheckDttpIncompatibleFormat] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\MSSQLSRV2012_PhysicalDomain_Domain_missing_default_object] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default] - -[FolderOptions\CheckModel\Default\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\DfltValeEmpty] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\DfltSameVale] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User] - -[FolderOptions\CheckModel\User\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\UniquePassword] -CheckSeverity=No -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Group] - -[FolderOptions\CheckModel\Group\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\EmptyColl - USERCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\UniquePassword] -CheckSeverity=No -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Role] - -[FolderOptions\CheckModel\Role\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\EmptyColl - USERCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure] - -[FolderOptions\CheckModel\Procedure\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\ProcBodyEmpty] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\EmptyColl - PERMCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\DBMS Trigger] - -[FolderOptions\CheckModel\DBMS Trigger\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\DbmsTriggerEvent] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\MSSQLSRV2012_DBMSTrigger_Logon_must_be_unique_trigger_event] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source] - -[FolderOptions\CheckModel\Data Source\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\EmptyColl - MODLSRC] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\DtscTargets] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\CheckDataSourceModels] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Horizontal Partitioning] - -[FolderOptions\CheckModel\Horizontal Partitioning\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Horizontal Partitioning\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Horizontal Partitioning\EmptyColl - PARTCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Horizontal Partitioning\TargetTables] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Vertical Partitioning] - -[FolderOptions\CheckModel\Vertical Partitioning\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Vertical Partitioning\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Vertical Partitioning\EmptyColl - PARTCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Vertical Partitioning\TargetTables] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table Collapsing] - -[FolderOptions\CheckModel\Table Collapsing\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table Collapsing\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table Collapsing\EmptyColl - TargetTable] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table Collapsing\TargetTables] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact] - -[FolderOptions\CheckModel\Fact\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\EmptyColl - MEASCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\Mapping] -CheckSeverity=No -FixRequested=Yes -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\MappingSFMap] -CheckSeverity=No -FixRequested=Yes -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\EmptyColl - ALLOLINKCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\CubeDupAssociation] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension] - -[FolderOptions\CheckModel\Dimension\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\EmptyColl - DATTRCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\EmptyColl - HIERCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\DimnDupHierarchy] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\DimnDefHierarchy] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\Mapping] -CheckSeverity=No -FixRequested=Yes -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\MappingSFMap] -CheckSeverity=No -FixRequested=Yes -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\SerialColumnNumber] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Association] - -[FolderOptions\CheckModel\Association\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Association\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Association\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Association\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Association\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Association\EmptyColl - Hierarchy] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute] - -[FolderOptions\CheckModel\Dimension.Attribute\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure] - -[FolderOptions\CheckModel\Fact.Measure\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy] - -[FolderOptions\CheckModel\Dimension.Hierarchy\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\EmptyColl - DATTRCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym] - -[FolderOptions\CheckModel\Synonym\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\MaxLen - NAME] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\EmptyColl - BASEOBJ] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type] - -[FolderOptions\CheckModel\Abstract Data Type\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\AdtInstantiable] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\AdtAbstractUsed] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure] - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\AdtProcUniqName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\UniqueDefinition] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\ReturnDataType] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package] - -[FolderOptions\CheckModel\Database Package\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\MaxLen - NAME] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\EmptyColl - PROCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\EmptyColl - CURCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Database Package\EmptyColl - VARCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Database Package\EmptyColl - TYPCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Database Package\EmptyColl - EXCCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Database Package.Database Package Procedure] - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\UniqueDefinition] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\EmptyColl - PARM] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\ReturnDataType] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence] - -[FolderOptions\CheckModel\Sequence\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor] - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\UniqueDefinition] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\ReturnDataType] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\EmptyColl - PARM] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Database Package.Database Package Variable] - -[FolderOptions\CheckModel\Database Package.Database Package Variable\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\CheckUndefDttp] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type] - -[FolderOptions\CheckModel\Database Package.Database Package Type\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\UniqueDefinition] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception] - -[FolderOptions\CheckModel\Database Package.Database Package Exception\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace] - -[FolderOptions\CheckModel\Tablespace\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\IsObjectUsed] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage] - -[FolderOptions\CheckModel\Storage\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\IsObjectUsed] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database] - -[FolderOptions\CheckModel\Database\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\IsObjectUsed] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service] - -[FolderOptions\CheckModel\Web Service\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation] - -[FolderOptions\CheckModel\Web Service.Web Operation\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle] - -[FolderOptions\CheckModel\Lifecycle\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\CheckLifecyclePhase] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\CheckLifecycleRetention] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\CheckPartitionRange] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase] - -[FolderOptions\CheckModel\Lifecycle.Phase\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseTbspace] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseIQTbspace] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseDuplicateTbspace] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseTbspaceCurrency] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseRetention] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseIdlePeriod] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseDataSource] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseExternalOnFirst] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Replication] - -[FolderOptions\CheckModel\Replication\PartialReplication] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule] - -[FolderOptions\CheckModel\Business Rule\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\EmptyColl - OBJCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object] - -[FolderOptions\CheckModel\Extended Object\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\MSSQLSRV2012_ExtendedObject_Queue_Valid_maximum_number_of_readers_] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link] - -[FolderOptions\CheckModel\Extended Link\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File] - -[FolderOptions\CheckModel\File\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File\CheckPathExists] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format] - -[FolderOptions\CheckModel\Data Format\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\CheckDataFormatNullExpression] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model] - -[FolderOptions\CheckModel\Physical Data Model\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\CheckPackageMissTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\DefaultCheckPackageMissTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\MSSQLSRV2012_Model_Storage_requires_a_database_] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes -[ModelOptions] - -[ModelOptions\Physical Objects] -CaseSensitive=No -DisplayName=Yes -EnableTrans=No -UseTerm=No -EnableRequirements=No -EnableFullShortcut=Yes -DefaultDttp= -IgnoreOwner=No -RebuildTrigger=Yes -RefrUnique=No -RefrAutoMigrate=Yes -RefrMigrateReuse=Yes -RefrMigrateDomain=Yes -RefrMigrateCheck=Yes -RefrMigrateRule=Yes -RefrMigrateExtd=No -RefrMigrDefaultLink=No -RefrDfltImpl=D -RefrPrgtColn=No -RefrMigrateToEnd=No -RebuildTriggerDep=No -ColnFKName=%.3:PARENT%_%COLUMN% -ColnFKNameUse=No -DomnCopyDttp=Yes -DomnCopyChck=No -DomnCopyRule=No -DomnCopyMand=No -DomnCopyExtd=No -DomnCopyProf=No -Notation=0 -DomnDefaultMandatory=No -ColnDefaultMandatory=No -TablDefaultOwner= -ViewDefaultOwner= -TrgrDefaultOwnerTabl= -TrgrDefaultOwnerView= -IdxDefaultOwnerTabl= -IdxDefaultOwnerView= -JdxDefaultOwner= -DBPackDefaultOwner= -SeqDefaultOwner= -ProcDefaultOwner= -DBMSTrgrDefaultOwner= -Currency=USD -RefrDeleteConstraint=1 -RefrUpdateConstraint=1 -RefrParentMandatory=No -RefrParentChangeAllow=Yes -RefrCheckOnCommit=No - -[ModelOptions\Physical Objects\NamingOptionsTemplates] - -[ModelOptions\Physical Objects\ClssNamingOptions] - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMPCKG] - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMPCKG\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMPCKG\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMDOMN] - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMDOMN\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMDOMN\Code] -Template= -MaxLen=128 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\TABL] - -[ModelOptions\Physical Objects\ClssNamingOptions\TABL\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\TABL\Code] -Template= -MaxLen=128 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\COLN] - -[ModelOptions\Physical Objects\ClssNamingOptions\COLN\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\COLN\Code] -Template= -MaxLen=128 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\INDX] - -[ModelOptions\Physical Objects\ClssNamingOptions\INDX\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\INDX\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\REFR] - -[ModelOptions\Physical Objects\ClssNamingOptions\REFR\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\REFR\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VREF] - -[ModelOptions\Physical Objects\ClssNamingOptions\VREF\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VREF\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEW] - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEW\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEW\Code] -Template= -MaxLen=128 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEWC] - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEWC\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEWC\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBSERV] - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBSERV\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBSERV\Code] -Template= -MaxLen=254 -Case=M -ValidChar='a'-'z','A'-'Z','0'-'9',"/-_.!~*'()" -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBOP] - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBOP\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBOP\Code] -Template= -MaxLen=254 -Case=M -ValidChar='a'-'z','A'-'Z','0'-'9',"/-_.!~*'()" -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WPARAM] - -[ModelOptions\Physical Objects\ClssNamingOptions\WPARAM\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WPARAM\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FACT] - -[ModelOptions\Physical Objects\ClssNamingOptions\FACT\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FACT\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DIMN] - -[ModelOptions\Physical Objects\ClssNamingOptions\DIMN\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DIMN\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\MEAS] - -[ModelOptions\Physical Objects\ClssNamingOptions\MEAS\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\MEAS\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DATTR] - -[ModelOptions\Physical Objects\ClssNamingOptions\DATTR\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DATTR\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FILO] - -[ModelOptions\Physical Objects\ClssNamingOptions\FILO\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FILO\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMEOBJ] - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMEOBJ\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMEOBJ\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMELNK] - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMELNK\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMELNK\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DefaultClass] - -[ModelOptions\Physical Objects\ClssNamingOptions\DefaultClass\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DefaultClass\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Connection] - -[ModelOptions\Pdm] - -[ModelOptions\Generate] - -[ModelOptions\Generate\Xsm] -GenRootElement=Yes -GenComplexType=No -GenAttribute=Yes -CheckModel=Yes -SaveLinks=Yes -ORMapping=No -NameToCode=No - -[ModelOptions\Generate\Pdm] -RRMapping=No -PreserveMode=Yes -CheckModel=Yes -SaveLinks=Yes -NameToCode=No -EnableTransformations=No - -[ModelOptions\Generate\Cdm] -CheckModel=Yes -SaveLinks=Yes -NameToCode=No -Notation=2 - -[ModelOptions\Generate\Oom] -CheckModel=Yes -SaveLinks=Yes -ORMapping=No -NameToCode=Yes -ClassPrefix= - -[ModelOptions\Generate\Ldm] -CheckModel=Yes -SaveLinks=Yes -NameToCode=No - -[ModelOptions\Default Opts] - -[ModelOptions\Default Opts\TABL] -PhysOpts= - -[ModelOptions\Default Opts\COLN] -PhysOpts= - -[ModelOptions\Default Opts\INDX] -PhysOpts= - -[ModelOptions\Default Opts\AKEY] -PhysOpts= - -[ModelOptions\Default Opts\PKEY] -PhysOpts= - -[ModelOptions\Default Opts\STOR] -PhysOpts= - -[ModelOptions\Default Opts\TSPC] -PhysOpts= - -[ModelOptions\Default Opts\SQNC] -PhysOpts= - -[ModelOptions\Default Opts\DTBS] -PhysOpts= - -[ModelOptions\Default Opts\USER] -PhysOpts= - -[ModelOptions\Default Opts\JIDX] -PhysOpts= - -[ModelOptions\Default Opts\FRMEOBJ<<ResourcePool>>] -PhysOpts= - -[ModelOptions\Default Opts\FRMEOBJ<<WorkloadGroup>>] -PhysOpts= - -[ModelOptions\Default Opts\FRMESOB<<ResourcePool>>] -PhysOpts= - -[ModelOptions\Default Opts\FRMESOB<<WorkloadGroup>>] -PhysOpts= - - -0220C4FC-A988-47A4-958E-3F8100D0D78C -Samples -Samples -1514588680 -Administrator -1514588680 -Administrator - -91AD3F79-C708-47A6-81A2-7B9E077A7DF4 -CDE44E21-9669-11D1-9914-006097355D9B - - - - -F7C7A338-9D4B-4B6A-9A6E-A178CA0B07BD -PostgreSQL 9.x -PGSQL9 -1514588676 -Administrator -1514588676 -Administrator - -83063C86-4E0A-4954-BF00-620C0D0F02D8 -4BA9F647-DAB1-11D1-9944-006097355D9B - - - - -99402516-CE39-453F-8C7F-B95ED4661C50 -Order -Order -1514588679 -Administrator -1514588679 -Administrator -ORG {92360091-5759-4724-BE43-894656D9A995} -DAT 1514588679 -[DisplayPreferences] - -[DisplayPreferences\PDM] - -[DisplayPreferences\General] -Adjust to text=Yes -Snap Grid=No -Constrain Labels=Yes -Display Grid=No -Show Page Delimiter=Yes -Show Links intersections=No -Activate automatic link routing=No -Grid size=0 -Graphic unit=2 -Window color=255, 255, 255 -Background image= -Background mode=8 -Watermark image= -Watermark mode=8 -Show watermark on screen=No -Gradient mode=0 -Gradient end color=255, 255, 255 -Show Swimlane=No -SwimlaneVert=Yes -TreeVert=No -CompDark=0 - -[DisplayPreferences\Object] -Show Icon=No -Mode=2 -Trunc Length=40 -Word Length=40 -Word Text=!"#$%&')*+,-./:;=>?@\]^_`|}~ -Shortcut IntIcon=Yes -Shortcut IntLoct=Yes -Shortcut IntFullPath=No -Shortcut IntLastPackage=Yes -Shortcut ExtIcon=Yes -Shortcut ExtLoct=No -Shortcut ExtFullPath=No -Shortcut ExtLastPackage=Yes -Shortcut ExtIncludeModl=Yes -EObjShowStrn=Yes -ExtendedObject.Comment=No -ExtendedObject.IconPicture=No -ExtendedObject.TextStyle=No -ExtendedObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Object Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -ELnkShowStrn=Yes -ELnkShowName=Yes -ExtendedLink_SymbolLayout=<Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Source" >[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] </Form>[CRLF]</Form> -FileObject.Stereotype=No -FileObject.DisplayName=Yes -FileObject.LocationOrName=No -FileObject.IconPicture=No -FileObject.TextStyle=No -FileObject.IconMode=Yes -FileObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Location" Attribute="LocationOrName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Package.Stereotype=Yes -Package.Comment=No -Package.IconPicture=No -Package.TextStyle=No -Package_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Display Model Version=Yes -Table.Stereotype=Yes -Table.DisplayName=Yes -Table.OwnerDisplayName=No -Table.Columns=Yes -Table.Columns._Filter="All Columns" PDMCOLNALL -Table.Columns._Columns=Stereotype DataType KeyIndicator -Table.Columns._Limit=-5 -Table.Keys=No -Table.Keys._Columns=Stereotype Indicator -Table.Indexes=No -Table.Indexes._Columns=Stereotype -Table.Triggers=No -Table.Triggers._Columns=Stereotype -Table.Comment=No -Table.IconPicture=No -Table.TextStyle=No -Table_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="Stereotype No\r\nDisplayName Yes\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nDomain No\r\nKeyIndicator No\r\nIndexIndicator No\r\nNullStatus No" Filters="&quot;All Columns&quot; PDMCOLNALL &quot;&quot;\r\n&quot;PK Columns&quot; PDMCOLNPK &quot;\&quot;PRIM \&quot;TRUE\&quot; TRUE\&quot;&quot;\r\n&quot;Key Columns&quot; PDMCOLNKEY &quot;\&quot;KEYS \&quot;TRUE\&quot; TRUE\&quot;&quot;" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Keys" Collection="Keys" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Triggers" Collection="Triggers" Columns="Stereotype No\r\nDisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -View.Stereotype=Yes -View.DisplayName=Yes -View.OwnerDisplayName=No -View.Columns=Yes -View.Columns._Columns=DisplayName -View.Columns._Limit=-5 -View.TemporaryVTables=Yes -View.Indexes=No -View.Comment=No -View.IconPicture=No -View.TextStyle=No -View_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="DisplayName No\r\nExpression No\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nIndexIndicator No" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Tables" Collection="TemporaryVTables" Columns="Name Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="DisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Procedure.Stereotype=No -Procedure.DisplayName=Yes -Procedure.OwnerDisplayName=No -Procedure.Comment=No -Procedure.IconPicture=No -Procedure.TextStyle=No -Procedure_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Reference.Cardinality=No -Reference.ImplementationType=No -Reference.ChildRole=Yes -Reference.Stereotype=Yes -Reference.DisplayName=No -Reference.ForeignKeyConstraintName=No -Reference.JoinExpression=No -Reference.Integrity=No -Reference.ParentRole=Yes -Reference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Cardinality" Attribute="Cardinality" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Implementation" Attribute="ImplementationType" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Cons&amp;traint Name" Attribute="ForeignKeyConstraintName" Prefix="" Suffix="" Caption="Cons&amp;traint Name" Mandatory="No" />[CRLF] <StandardAttribute Name="Join" Attribute="JoinExpression" Prefix="" Suffix="" Caption="Join" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Referential integrity" Attribute="Integrity" Prefix="" Suffix="" Caption="Referential integrity" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> -ViewReference.ChildRole=Yes -ViewReference.Stereotype=Yes -ViewReference.DisplayName=No -ViewReference.JoinExpression=No -ViewReference.ParentRole=Yes -ViewReference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Join Expression" Attribute="JoinExpression" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> - -[DisplayPreferences\Symbol] - -[DisplayPreferences\Symbol\FRMEOBJ] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=6000 -Height=2000 -Brush color=255 255 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=64 -Brush gradient color=192 192 192 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 255 128 128 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FRMELNK] -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FILO] -OBJSTRNFont=新宋体,8,N -OBJSTRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LCNMFont=新宋体,8,N -LCNMFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=3600 -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PDMPCKG] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 178 178 178 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\TABL] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -KeysFont=新宋体,8,N -KeysFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -TriggersFont=新宋体,8,N -TriggersFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=178 214 252 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VIEW] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -TemporaryVTablesFont=新宋体,8,N -TemporaryVTablesFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=208 208 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PROC] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4000 -Height=1000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 108 0 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\REFR] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VREF] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\USRDEPD] -OBJXSTRFont=新宋体,8,N -OBJXSTRFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=2 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\Free Symbol] -Free TextFont=新宋体,8,N -Free TextFont color=0, 0, 0 -Line style=0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 -(8268, 11693) -((315,354), (433,354)) -1 -7 - - -1514588679 -1514588679 -((-5249,-9277), (-4799,-2683)) -((-5024,-2683),(-5024,-9277)) -1 -1 -12615680 -12632256 -CENTER 0 新宋体,8,N -SOURCE 0 新宋体,8,N -DESTINATION 0 新宋体,8,N - - - - - - - - - - - -1514588679 -1514588679 -((-3717,2041), (-3267,8646)) -((-3492,2041),(-3492,8646)) -1 -1 -12615680 -12632256 -CENTER 0 新宋体,8,N -SOURCE 0 新宋体,8,N -DESTINATION 0 新宋体,8,N - - - - - - - - - - - -1514588679 -1514588679 --1 -((-11260,6510), (7534,11634)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - -1514588679 -1514588679 --1 -((-11088,-3939), (10170,3194)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - -1514588679 -1514588679 --1 -((-10585,-13983), (8209,-6187)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - - - -D132344A-A8E9-4A57-8F6D-7691E6408656 -Product -Product -1514588679 -Administrator -1514588679 -Administrator -ORG {DE41247D-905B-4995-863B-693380B257D6} -DAT 1514588679 -[DisplayPreferences] - -[DisplayPreferences\PDM] - -[DisplayPreferences\General] -Adjust to text=Yes -Snap Grid=No -Constrain Labels=Yes -Display Grid=No -Show Page Delimiter=Yes -Show Links intersections=No -Activate automatic link routing=No -Grid size=0 -Graphic unit=2 -Window color=255, 255, 255 -Background image= -Background mode=8 -Watermark image= -Watermark mode=8 -Show watermark on screen=No -Gradient mode=0 -Gradient end color=255, 255, 255 -Show Swimlane=No -SwimlaneVert=Yes -TreeVert=No -CompDark=0 - -[DisplayPreferences\Object] -Show Icon=No -Mode=2 -Trunc Length=40 -Word Length=40 -Word Text=!"#$%&')*+,-./:;=>?@\]^_`|}~ -Shortcut IntIcon=Yes -Shortcut IntLoct=Yes -Shortcut IntFullPath=No -Shortcut IntLastPackage=Yes -Shortcut ExtIcon=Yes -Shortcut ExtLoct=No -Shortcut ExtFullPath=No -Shortcut ExtLastPackage=Yes -Shortcut ExtIncludeModl=Yes -EObjShowStrn=Yes -ExtendedObject.Comment=No -ExtendedObject.IconPicture=No -ExtendedObject.TextStyle=No -ExtendedObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Object Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -ELnkShowStrn=Yes -ELnkShowName=Yes -ExtendedLink_SymbolLayout=<Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Source" >[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] </Form>[CRLF]</Form> -FileObject.Stereotype=No -FileObject.DisplayName=Yes -FileObject.LocationOrName=No -FileObject.IconPicture=No -FileObject.TextStyle=No -FileObject.IconMode=Yes -FileObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Location" Attribute="LocationOrName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Package.Stereotype=Yes -Package.Comment=No -Package.IconPicture=No -Package.TextStyle=No -Package_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Display Model Version=Yes -Table.Stereotype=Yes -Table.DisplayName=Yes -Table.OwnerDisplayName=No -Table.Columns=Yes -Table.Columns._Filter="All Columns" PDMCOLNALL -Table.Columns._Columns=Stereotype DataType KeyIndicator -Table.Columns._Limit=-5 -Table.Keys=No -Table.Keys._Columns=Stereotype Indicator -Table.Indexes=No -Table.Indexes._Columns=Stereotype -Table.Triggers=No -Table.Triggers._Columns=Stereotype -Table.Comment=No -Table.IconPicture=No -Table.TextStyle=No -Table_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="Stereotype No\r\nDisplayName Yes\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nDomain No\r\nKeyIndicator No\r\nIndexIndicator No\r\nNullStatus No" Filters="&quot;All Columns&quot; PDMCOLNALL &quot;&quot;\r\n&quot;PK Columns&quot; PDMCOLNPK &quot;\&quot;PRIM \&quot;TRUE\&quot; TRUE\&quot;&quot;\r\n&quot;Key Columns&quot; PDMCOLNKEY &quot;\&quot;KEYS \&quot;TRUE\&quot; TRUE\&quot;&quot;" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Keys" Collection="Keys" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Triggers" Collection="Triggers" Columns="Stereotype No\r\nDisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -View.Stereotype=Yes -View.DisplayName=Yes -View.OwnerDisplayName=No -View.Columns=Yes -View.Columns._Columns=DisplayName -View.Columns._Limit=-5 -View.TemporaryVTables=Yes -View.Indexes=No -View.Comment=No -View.IconPicture=No -View.TextStyle=No -View_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="DisplayName No\r\nExpression No\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nIndexIndicator No" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Tables" Collection="TemporaryVTables" Columns="Name Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="DisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Procedure.Stereotype=No -Procedure.DisplayName=Yes -Procedure.OwnerDisplayName=No -Procedure.Comment=No -Procedure.IconPicture=No -Procedure.TextStyle=No -Procedure_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Reference.Cardinality=No -Reference.ImplementationType=No -Reference.ChildRole=Yes -Reference.Stereotype=Yes -Reference.DisplayName=No -Reference.ForeignKeyConstraintName=No -Reference.JoinExpression=No -Reference.Integrity=No -Reference.ParentRole=Yes -Reference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Cardinality" Attribute="Cardinality" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Implementation" Attribute="ImplementationType" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Cons&amp;traint Name" Attribute="ForeignKeyConstraintName" Prefix="" Suffix="" Caption="Cons&amp;traint Name" Mandatory="No" />[CRLF] <StandardAttribute Name="Join" Attribute="JoinExpression" Prefix="" Suffix="" Caption="Join" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Referential integrity" Attribute="Integrity" Prefix="" Suffix="" Caption="Referential integrity" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> -ViewReference.ChildRole=Yes -ViewReference.Stereotype=Yes -ViewReference.DisplayName=No -ViewReference.JoinExpression=No -ViewReference.ParentRole=Yes -ViewReference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Join Expression" Attribute="JoinExpression" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> - -[DisplayPreferences\Symbol] - -[DisplayPreferences\Symbol\FRMEOBJ] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=6000 -Height=2000 -Brush color=255 255 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=64 -Brush gradient color=192 192 192 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 255 128 128 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FRMELNK] -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FILO] -OBJSTRNFont=新宋体,8,N -OBJSTRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LCNMFont=新宋体,8,N -LCNMFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=3600 -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PDMPCKG] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 178 178 178 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\TABL] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -KeysFont=新宋体,8,N -KeysFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -TriggersFont=新宋体,8,N -TriggersFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=178 214 252 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VIEW] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -TemporaryVTablesFont=新宋体,8,N -TemporaryVTablesFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=208 208 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PROC] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4000 -Height=1000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 108 0 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\REFR] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VREF] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\USRDEPD] -OBJXSTRFont=新宋体,8,N -OBJXSTRFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=2 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\Free Symbol] -Free TextFont=新宋体,8,N -Free TextFont color=0, 0, 0 -Line style=0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 -(8268, 11693) -((315,354), (433,354)) -1 -7 - - -1514588679 -1514588679 --1 -((-9995,-2970), (8799,3952)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - - - -7B4923DF-FCA1-4AF9-B77C-A48CCE34483F -Customer -Customer -1514588679 -Administrator -1514588679 -Administrator -ORG {68F8168C-38E6-4220-9B3C-0FA0C72EE518} -DAT 1514588679 -[DisplayPreferences] - -[DisplayPreferences\PDM] - -[DisplayPreferences\General] -Adjust to text=Yes -Snap Grid=No -Constrain Labels=Yes -Display Grid=No -Show Page Delimiter=Yes -Show Links intersections=No -Activate automatic link routing=No -Grid size=0 -Graphic unit=2 -Window color=255, 255, 255 -Background image= -Background mode=8 -Watermark image= -Watermark mode=8 -Show watermark on screen=No -Gradient mode=0 -Gradient end color=255, 255, 255 -Show Swimlane=No -SwimlaneVert=Yes -TreeVert=No -CompDark=0 - -[DisplayPreferences\Object] -Show Icon=No -Mode=2 -Trunc Length=40 -Word Length=40 -Word Text=!"#$%&')*+,-./:;=>?@\]^_`|}~ -Shortcut IntIcon=Yes -Shortcut IntLoct=Yes -Shortcut IntFullPath=No -Shortcut IntLastPackage=Yes -Shortcut ExtIcon=Yes -Shortcut ExtLoct=No -Shortcut ExtFullPath=No -Shortcut ExtLastPackage=Yes -Shortcut ExtIncludeModl=Yes -EObjShowStrn=Yes -ExtendedObject.Comment=No -ExtendedObject.IconPicture=No -ExtendedObject.TextStyle=No -ExtendedObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Object Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -ELnkShowStrn=Yes -ELnkShowName=Yes -ExtendedLink_SymbolLayout=<Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Source" >[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] </Form>[CRLF]</Form> -FileObject.Stereotype=No -FileObject.DisplayName=Yes -FileObject.LocationOrName=No -FileObject.IconPicture=No -FileObject.TextStyle=No -FileObject.IconMode=Yes -FileObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Location" Attribute="LocationOrName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Package.Stereotype=Yes -Package.Comment=No -Package.IconPicture=No -Package.TextStyle=No -Package_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Display Model Version=Yes -Table.Stereotype=Yes -Table.DisplayName=Yes -Table.OwnerDisplayName=No -Table.Columns=Yes -Table.Columns._Filter="All Columns" PDMCOLNALL -Table.Columns._Columns=Stereotype DataType KeyIndicator -Table.Columns._Limit=-5 -Table.Keys=No -Table.Keys._Columns=Stereotype Indicator -Table.Indexes=No -Table.Indexes._Columns=Stereotype -Table.Triggers=No -Table.Triggers._Columns=Stereotype -Table.Comment=No -Table.IconPicture=No -Table.TextStyle=No -Table_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="Stereotype No\r\nDisplayName Yes\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nDomain No\r\nKeyIndicator No\r\nIndexIndicator No\r\nNullStatus No" Filters="&quot;All Columns&quot; PDMCOLNALL &quot;&quot;\r\n&quot;PK Columns&quot; PDMCOLNPK &quot;\&quot;PRIM \&quot;TRUE\&quot; TRUE\&quot;&quot;\r\n&quot;Key Columns&quot; PDMCOLNKEY &quot;\&quot;KEYS \&quot;TRUE\&quot; TRUE\&quot;&quot;" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Keys" Collection="Keys" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Triggers" Collection="Triggers" Columns="Stereotype No\r\nDisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -View.Stereotype=Yes -View.DisplayName=Yes -View.OwnerDisplayName=No -View.Columns=Yes -View.Columns._Columns=DisplayName -View.Columns._Limit=-5 -View.TemporaryVTables=Yes -View.Indexes=No -View.Comment=No -View.IconPicture=No -View.TextStyle=No -View_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="DisplayName No\r\nExpression No\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nIndexIndicator No" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Tables" Collection="TemporaryVTables" Columns="Name Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="DisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Procedure.Stereotype=No -Procedure.DisplayName=Yes -Procedure.OwnerDisplayName=No -Procedure.Comment=No -Procedure.IconPicture=No -Procedure.TextStyle=No -Procedure_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Reference.Cardinality=No -Reference.ImplementationType=No -Reference.ChildRole=Yes -Reference.Stereotype=Yes -Reference.DisplayName=No -Reference.ForeignKeyConstraintName=No -Reference.JoinExpression=No -Reference.Integrity=No -Reference.ParentRole=Yes -Reference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Cardinality" Attribute="Cardinality" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Implementation" Attribute="ImplementationType" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Cons&amp;traint Name" Attribute="ForeignKeyConstraintName" Prefix="" Suffix="" Caption="Cons&amp;traint Name" Mandatory="No" />[CRLF] <StandardAttribute Name="Join" Attribute="JoinExpression" Prefix="" Suffix="" Caption="Join" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Referential integrity" Attribute="Integrity" Prefix="" Suffix="" Caption="Referential integrity" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> -ViewReference.ChildRole=Yes -ViewReference.Stereotype=Yes -ViewReference.DisplayName=No -ViewReference.JoinExpression=No -ViewReference.ParentRole=Yes -ViewReference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Join Expression" Attribute="JoinExpression" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> - -[DisplayPreferences\Symbol] - -[DisplayPreferences\Symbol\FRMEOBJ] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=6000 -Height=2000 -Brush color=255 255 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=64 -Brush gradient color=192 192 192 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 255 128 128 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FRMELNK] -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FILO] -OBJSTRNFont=新宋体,8,N -OBJSTRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LCNMFont=新宋体,8,N -LCNMFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=3600 -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PDMPCKG] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 178 178 178 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\TABL] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -KeysFont=新宋体,8,N -KeysFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -TriggersFont=新宋体,8,N -TriggersFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=178 214 252 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VIEW] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -TemporaryVTablesFont=新宋体,8,N -TemporaryVTablesFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=208 208 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PROC] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4000 -Height=1000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 108 0 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\REFR] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VREF] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\USRDEPD] -OBJXSTRFont=新宋体,8,N -OBJXSTRFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=2 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\Free Symbol] -Free TextFont=新宋体,8,N -Free TextFont color=0, 0, 0 -Line style=0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 -(8268, 11693) -((315,354), (433,354)) -1 -7 - - -1514588679 -1514588679 --1 -((-13056,-66), (10598,13178)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - - - - - - - - -A86CD6BD-CEF1-4DFB-A046-A8D4588B6F56 -Sales -Sales -1514588679 -Administrator -1514588679 -Administrator -ORG {D792A2DB-5F90-499F-8B39-0AAFF559CD36} -DAT 1514588679 -Schema - - -8ADE3B77-4A05-461C-8412-6140835B6F15 -Productions -Productions -1514588679 -Administrator -1514588679 -Administrator -ORG {8B9F6BB3-2312-48C1-8479-F48207BDE3BC} -DAT 1514588679 -Schema - - -36B570C7-BF41-458E-88E1-1A16D3D04300 -Customers -Customers -1514588679 -Administrator -1514588679 -Administrator -ORG {DFC6388B-EEDB-4184-94C6-68548BFC8ADC} -DAT 1514588679 -Schema - - - - -F6E1FCB9-73D5-4220-9A1C-5722CB56572C -订单( Orders ) -Orders -1514588679 -Administrator -1514588679 -Administrator -订单 -ORG {DCFB7BAF-61C8-4927-8C69-7EC2C03A17BB} -DAT 1514588679 -ATT CLUS - - - -EB20AA9E-2AC7-446B-91E7-E863761E19DA -订单编号( OrderId ) -OrderId -1514588679 -Administrator -1514588679 -Administrator -订单编号 -ORG {44A75471-51DC-4FF7-9579-4F96537D6184} -DAT 1514588679 -UUID -1 - - -EB4CD169-76A0-4CFB-8C32-731D7C7268C3 -订单编码( Code ) -Code -1514588679 -Administrator -1514588679 -Administrator -订单编码 -ORG {64DB43B2-EFDA-499C-A1DF-F5D6A1F6FB0D} -DAT 1514588679 -VARCHAR(30) -30 -1 - - -7A465D53-749D-400A-BE0C-06433AFC5A5B -订单名称( Name ) -Name -1514588679 -Administrator -1514588679 -Administrator -订单名称 -ORG {935D2B35-8FDD-4862-8E04-77E196989914} -DAT 1514588679 -VARCHAR(200) -200 -1 - - -9489686D-08B1-408E-ABF1-DDB849835333 -版本号( Version ) -Version -1514588679 -Administrator -1514588679 -Administrator -版本号 -ORG {F83621F4-DAA3-4F37-8FBC-EEFCECA527A8} -DAT 1514588679 -BYTEA - - - - -5502B1A8-D6B1-40A6-8AD5-854AC9AA4D57 -Key_1 -Key_1 -1514588679 -Administrator -1514588679 -Administrator -ORG {92740D57-C6F0-457B-8D5D-4845D070F063} -DAT 1514588679 - - - - - - - - - - - - - -E8CB90D9-10CD-472E-AA2E-F0F8CE4C3DE6 -商品( Products ) -Products -1514588679 -Administrator -1514588679 -Administrator -商品 -ORG {D181918A-EA17-4145-A88E-907A77FACA10} -DAT 1514588679 -ATT CLUS - - - -B5FDCE96-B1EE-4177-B10E-0EDA7CD384F9 -商品编号( ProductId ) -ProductId -1514588679 -Administrator -1514588679 -Administrator -商品编号 -ORG {B53960D7-4FB2-4954-BED8-14AB641761B2} -DAT 1514588679 -INT4 -4 -1 - - -914D879F-F8E1-43B6-9B7B-0CB4007A46A9 -商品编码( Code ) -Code -1514588679 -Administrator -1514588679 -Administrator -商品编码 -ORG {82CB28CE-8970-4273-8B82-05213E608D8F} -DAT 1514588679 -VARCHAR(30) -30 -1 - - -26C30F45-BD28-4877-BF15-03F60A64C856 -商品名称( Name ) -Name -1514588679 -Administrator -1514588679 -Administrator -商品名称 -ORG {8A8B240F-EB3A-417B-B551-A984300FD92F} -DAT 1514588679 -VARCHAR(200) -200 -1 - - -4FC5AABB-29A6-4D66-A22E-3013A5FF2123 -扩展属性( Extends ) -Extends -1514588679 -Administrator -1514588679 -Administrator -扩展属性 -ORG {E956A6FE-EF12-4FD1-A6F9-7A0B2C7E3149} -DAT 1514588679 -CHAR - - -474F8823-957D-4821-90A6-AAA75B071659 -价格( Price ) -Price -1514588679 -Administrator -1514588679 -Administrator -价格 -ORG {509CD7FA-70E7-4E7D-88E5-5D6468A73853} -DAT 1514588679 -DECIMAL(10,2) -10 -2 - - -E642AA1F-5400-4208-B3E9-552DE8F509A5 -是否删除( IsDeleted ) -IsDeleted -1514588679 -Administrator -1514588679 -Administrator -是否删除 -ORG {C8C5119A-D798-456A-B4FD-CB35C05B804C} -DAT 1514588679 -BOOL -1 - - -08623767-AE80-427B-82AD-5894EC0811E0 -版本号( Version ) -Version -1514588679 -Administrator -1514588679 -Administrator -版本号 -ORG {269BD895-4B63-4E11-B8C1-EA90928F070F} -DAT 1514588679 -BYTEA - - - - -C68B00F7-856C-42F2-8767-241ABC6D4D01 -Key_1 -Key_1 -1514588679 -Administrator -1514588679 -Administrator -ORG {B1442255-04B5-4E29-99AA-B7218DA5522E} -DAT 1514588679 - - - - - - - - - - - - - -8D03640D-E748-4541-AE89-14C9CE0A2B1E -订单明细( OrderItems ) -OrderItems -1514588679 -Administrator -1514588679 -Administrator -订单明细 -ORG {BA70D8F5-EDAF-42FF-A493-ADD0F270786D} -DAT 1514588679 -ATT CLUS - - - -600B47DF-584F-4A54-8DB4-EAA31DA53E09 -订单明细编号( OrderItemId ) -OrderItemId -1514588679 -Administrator -1514588679 -Administrator -订单明细编号 -ORG {0CC5B156-1828-4743-A78A-1BA46EA6EF63} -DAT 1514588679 -UUID -1 - - -7E2225EE-79CB-4C71-9192-951B544ADE37 -订单编号( OrderId ) -OrderId -1514588679 -Administrator -1514588679 -Administrator -订单编号 -ORG {DFD8CD92-6982-4B46-AA1B-1F5C5ACEA826} -DAT 1514588679 -UUID -1 - - -5AF42155-A317-442E-A356-008EDE403C73 -商品编号( ProductId ) -ProductId -1514588679 -Administrator -1514588679 -Administrator -商品编号 -ORG {258D4008-B597-465B-A18B-9230CF186B40} -DAT 1514588679 -INT4 -4 -1 - - -BF1201F7-2E8C-49AD-B10A-5E7FB54C1673 -商品名称( ProductName ) -ProductName -1514588679 -Administrator -1514588679 -Administrator -商品名称 -ORG {36726035-757C-4149-9B60-C814BC94A7BE} -DAT 1514588679 -VARCHAR(200) -200 -1 - - -F98B684D-838D-4648-B04A-D391B9115A87 -数量( Quantity ) -Quantity -1514588679 -Administrator -1514588679 -Administrator -数量 -ORG {40D46135-FFF4-46EF-B62C-D10D3368930C} -DAT 1514588679 -INT4 -4 -1 - - -2358BE1B-3A11-4204-9A14-11DC3EDA0A68 -单价( Price ) -Price -1514588679 -Administrator -1514588679 -Administrator -单价 -ORG {B1C7C098-90D1-4B4C-BC25-06777E071830} -DAT 1514588679 -DECIMAL(10,2) -10 -2 -1 - - - - -5E0C739D-581D-470F-8018-0E860A0528CE -Key_1 -Key_1 -1514588679 -Administrator -1514588679 -Administrator -ORG {0F965720-6103-46E8-8EE7-B48237AF21E8} -DAT 1514588679 - - - - - - - - - - - - - -B9905C53-6A71-454F-A252-6AF77730A58B -客户( Customers ) -Customers -1514588679 -Administrator -1514588679 -Administrator -客户 -ORG {C5252905-0F60-4917-A321-FD71B172ACA0} -DAT 1514588679 -ATT CLUS - - - -07AEA1E3-6584-4721-AA86-E554B72027EB -客户编号( CustomerId ) -CustomerId -1514588679 -Administrator -1514588679 -Administrator -客户编号 -ORG {A823E497-5CB0-432E-BF50-FD37EBC2B648} -DAT 1514588679 -VARCHAR(50) -50 -1 - - -CF2B3140-9BA1-4A25-8FAD-5AC90E0624D9 -客户名称( Name ) -Name -1514588679 -Administrator -1514588679 -Administrator -客户名称 -ORG {A58AEBD8-0122-43F0-813E-091BA1D20FED} -DAT 1514588679 -VARCHAR(20) -20 -1 - - -E13F7460-9EA1-44A4-838D-F0E2134975B1 -昵称( Nickname ) -Nickname -1514588679 -Administrator -1514588679 -Administrator -昵称 -ORG {26AF2F9E-4450-46ED-B9F3-00FDD4D69E87} -DAT 1514588679 -VARCHAR(30) -30 - - -51136895-A0CD-404F-87E1-530EE2E17D3A -余额( Balance ) -Balance -1514588679 -Administrator -1514588679 -Administrator -余额 -ORG {A3AC3300-5774-4BA9-BDC3-2B833A31E13F} -DAT 1514588679 -DECIMAL(18,2) -18 -2 -1 - - -3CDF9FB3-F743-4E39-857D-E5780FDA17A7 -性别( Gender ) -Gender -1514588679 -Administrator -1514588679 -Administrator -性别 -ORG {B3906707-46B9-426E-A2F9-8D132FBE47BA} -DAT 1514588679 -INT4 -4 - - -ADE5200B-0F08-4CCE-9E38-5C3A8C9AFBA4 -联系电话( Tel ) -Tel -1514588679 -Administrator -1514588679 -Administrator -联系电话 -ORG {422A0FBF-02EE-4C77-ACF3-0F6DFF20B846} -DAT 1514588679 -VARCHAR(20) -20 - - -9CDD2969-F400-4CB7-BA7E-C7ED73A2D729 -手机号( Mobile ) -Mobile -1514588679 -Administrator -1514588679 -Administrator -手机号 -ORG {8FA4B750-24CD-43B8-8397-F6D1EDECEBC9} -DAT 1514588679 -VARCHAR(20) -20 - - -2B6E2E54-0A53-4567-8EF0-4F93307D1A37 -电子邮件( Email ) -Email -1514588679 -Administrator -1514588679 -Administrator -电子邮件 -ORG {94E1690C-B465-4C47-B724-B4199213FBC0} -DAT 1514588679 -VARCHAR(100) -100 - - -8B0ED0A1-11D3-4C7F-9567-3D2F675C284C -创建时间( CreationTime ) -CreationTime -1514588679 -Administrator -1514588679 -Administrator -创建时间 -ORG {3DA62965-6815-4CF9-858E-6D5298D97075} -DAT 1514588679 -TIMESTAMP - - -E18D527C-5BDC-4101-87C3-2AC71EB82454 -创建人( CreatorId ) -CreatorId -1514588679 -Administrator -1514588679 -Administrator -创建人 -ORG {98A5605B-3958-404E-9658-384E3363DF0E} -DAT 1514588679 -UUID - - -EF3CF853-6173-4129-B471-E6E77A71A77B -最后修改时间( LastModificationTime ) -LastModificationTime -1514588679 -Administrator -1514588679 -Administrator -最后修改时间 -ORG {7A4191EC-ADFF-4E5D-A672-9CCB5ECE907D} -DAT 1514588679 -TIMESTAMP - - -7509D740-EA9D-4F60-B5F3-28F8B5AA79B5 -最后修改人( LastModifierId ) -LastModifierId -1514588679 -Administrator -1514588679 -Administrator -最后修改人 -ORG {4BAC5792-FF54-4C9B-AA55-32B63774DA44} -DAT 1514588679 -UUID - - -2484838C-F755-4593-AE5F-BD22BF28E21B -版本号( Version ) -Version -1514588679 -Administrator -1514588679 -Administrator -版本号 -ORG {1CBFD184-6982-4407-BEBA-A169BD9AEADF} -DAT 1514588679 -BYTEA - - - - -2423B547-4B05-46B0-A8FD-56F8C976C573 -Key_1 -Key_1 -1514588679 -Administrator -1514588679 -Administrator -ORG {9AB7B83B-729A-4865-8A84-1A8038CA22D5} -DAT 1514588679 - - - - - - - - - - - - - - - -B762A082-F574-4F2E-B192-7010761FC449 -Reference_1 -Reference_1 -1514588679 -Administrator -1514588679 -Administrator -ORG {E41C4795-1F29-4BDA-ACAF-614C9A3FB6A0} -DAT 1514588679 -0..* - - - - - - - - - - - -FC81CB6C-11ED-46B8-BC95-5BF6B50558F4 -1514588679 -Administrator -1514588679 -Administrator -ORG {AE653EE6-E7FE-4276-BCFE-3F49F078877C} -DAT 1514588679 - - - - - - - - - - -F4577E79-697C-4C8F-A8C3-4CDF4C680BF8 -Reference_2 -Reference_2 -1514588679 -Administrator -1514588679 -Administrator -ORG {76ABB6B3-CE4D-496B-B6EA-8563C5C6E8BA} -DAT 1514588679 -0..* - - - - - - - - - - - -F1267C6F-D3FF-436E-8982-930A6A20A6C3 -1514588679 -Administrator -1514588679 -Administrator -ORG {5900BB99-C0D5-4638-B555-26BC4FCB28A9} -DAT 1514588679 - - - - - - - - - - - - -D4C5C18B-12BB-44F2-81F7-FC9E92CEEA2C -PUBLIC -PUBLIC -1514588676 -Administrator -1514588676 -Administrator -ORG {AFA9EDB8-8EE4-4FD5-8275-60A5D6D96228} -DAT 1514588679 - - - - - - - - - -1BCA575B-7AC1-478E-91D3-AC40C793F1D4 -PostgreSQL 9.x -PGSQL9 -1514588676 -Administrator -1514588676 -Administrator -file:///%_DBMS%/pgsql9.xdb -83063C86-4E0A-4954-BF00-620C0D0F02D8 -4BA9F647-DAB1-11D1-9944-006097355D9B -1514588659 - - - - - -3D934EBA-A4CE-47AA-B707-D0A6033BAA36 -Samples -Samples -1514588680 -Administrator -1514588680 -Administrator -file:///D|/Github/Util/test/Util.Datas.Tests.Integration/SqlServer/Scripts/Pd/Samples.pdm -91AD3F79-C708-47A6-81A2-7B9E077A7DF4 -CDE44E21-9669-11D1-9914-006097355D9B -1514588725 - - - - - - - - - - \ No newline at end of file diff --git a/test/Util.Datas.Tests.Integration/PgSql/Scripts/Pd/Samples.pdm b/test/Util.Datas.Tests.Integration/PgSql/Scripts/Pd/Samples.pdm deleted file mode 100644 index e2188bd72..000000000 --- a/test/Util.Datas.Tests.Integration/PgSql/Scripts/Pd/Samples.pdm +++ /dev/null @@ -1,6117 +0,0 @@ - - - - - - - - - -983AA762-5652-4ACC-9A88-2B8B90A0CAA9 -Samples -Samples -1514588679 -Administrator -1514588728 -Administrator -ORG {91AD3F79-C708-47A6-81A2-7B9E077A7DF4} -DAT 1514588679 -ATT NAME -ATT DISPNAME -ATT CODE -[FolderOptions] - -[FolderOptions\Physical Objects] -GenerationCheckModel=Yes -GenerationPath= -GenerationOptions= -GenerationTasks= -GenerationTargets= -GenerationSelections= -RevPkey=Yes -RevFkey=Yes -RevAkey=Yes -RevCheck=Yes -RevIndx=Yes -RevOpts=Yes -RevViewAsTabl=No -RevViewOpts=Yes -RevSystAsTabl=Yes -RevTablPerm=No -RevViewPerm=No -RevProcPerm=No -RevDbpkPerm=No -RevSqncPerm=No -RevAdtPerm=No -RevUserPriv=No -RevUserOpts=No -RevGrpePriv=No -RevRolePriv=No -RevDtbsOpts=Yes -RevDtbsPerm=No -RevViewIndx=Yes -RevJidxOpts=Yes -RevStats=No -RevTspcPerm=No -RevCaseSensitive=No -GenTrgrStdMsg=Yes -GenTrgrMsgTab= -GenTrgrMsgNo= -GenTrgrMsgTxt= -TrgrPreserve=No -TrgrIns=Yes -TrgrUpd=Yes -TrgrDel=Yes -TrgrC2Ins=Yes -TrgrC2Upd=Yes -TrgrC3=Yes -TrgrC4=Yes -TrgrC5=Yes -TrgrC6=Yes -TrgrC7=Yes -TrgrC8=Yes -TrgrC9=Yes -TrgrC10=Yes -TrgrC11=Yes -TrgrC1=Yes -TrgrC12Ins=Yes -TrgrC12Upd=Yes -TrgrC13=Yes -UpdateTableStatistics=Yes -UpdateColumnStatistics=Yes - -[FolderOptions\Physical Objects\Database Generation] -GenScriptName=crebas -GenScriptName0= -GenScriptName1= -GenScriptName2= -GenScriptName3= -GenScriptName4= -GenScriptName5= -GenScriptName6= -GenScriptName7= -GenScriptName8= -GenScriptName9= -GenPathName= -GenSingleFile=Yes -GenODBC=No -GenCheckModel=Yes -GenScriptPrev=Yes -GenArchiveModel=No -GenUseSync=No -GenSyncChoice=0 -GenSyncArch= -GenSyncRmg=0 - -[FolderOptions\Physical Objects\Database Generation\Format] -GenScriptTitle=Yes -GenScriptNamLabl=No -GenScriptQDtbs=No -GenScriptQOwnr=Yes -GenScriptCase=0 -GenScriptEncoding=ANSI -GenScriptNAcct=No -IdentifierDelimiter=" - -[FolderOptions\Physical Objects\Database Generation\Database] -Create=Yes -Open=Yes -Close=Yes -Drop=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Database\Create] -Physical Options=Yes -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\Tablespace] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Tablespace\Create] -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\Storage] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\User] -Create=Yes -Drop=Yes -Comment=Yes -Privilege=No - -[FolderOptions\Physical Objects\Database Generation\User\Create] -Physical Options=No - -[FolderOptions\Physical Objects\Database Generation\Group] -Create=Yes -Drop=Yes -Comment=Yes -Privilege=No - -[FolderOptions\Physical Objects\Database Generation\Role] -Create=Yes -Drop=Yes -Privilege=No - -[FolderOptions\Physical Objects\Database Generation\UserDefinedDataType] -Create=Yes -Comment=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\UserDefinedDataType\Create] -Default value=Yes -Check=Yes - -[FolderOptions\Physical Objects\Database Generation\AbstractDataType] -Create=Yes -Header=Yes -Footer=Yes -Drop=Yes -Comment=Yes -Install JAVA class=Yes -Remove JAVA class=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Rule] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Default] -Create=Yes -Comment=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\Sequence] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Table&&Column] - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Table] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Table\Create] -Check=Yes -Physical Options=Yes -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Table\Create\Check] -Constraint declaration=No - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Column] -User datatype=Yes -Default value=Yes -Check=Yes -Physical Options=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Column\Check] -Constraint declaration=No - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key] - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key\Primary key] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key\Primary key\Create] -Constraint declaration=No -Physical Options=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key\Alternate key] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key\Alternate key\Create] -Constraint declaration=No -Physical Options=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Foreign key] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Foreign key\Create] -Constraint declaration=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Index] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Index\Create] -Constraint declaration=Yes -Physical Options=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Index\Filter] -Primary key=No -Foreign key=Yes -Alternate key=No -Cluster=Yes -Other=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Trigger] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Trigger\Filter] -For insert=Yes -For update=Yes -For delete=Yes -For other=Yes - -[FolderOptions\Physical Objects\Database Generation\View] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\View\Create] -Force Column list=No -Physical Options=Yes -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\View\ViewColumn] -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\View\ViewIndex] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\View\ViewIndex\Create] -Physical Options=Yes - -[FolderOptions\Physical Objects\Database Generation\View\ViewIndex\Filter] -Cluster=Yes -Other=Yes - -[FolderOptions\Physical Objects\Database Generation\View\Trigger] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\View\Trigger\Filter] -For insert=Yes -For update=Yes -For delete=Yes -For other=Yes - -[FolderOptions\Physical Objects\Database Generation\DBMSTrigger] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Synonym] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\Synonym\Filter] -Table=Yes -View=Yes -Proc=Yes -Synonym=Yes -Database Package=Yes -Sequence=Yes - -[FolderOptions\Physical Objects\Database Generation\JoinIndex] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\JoinIndex\Create] -Physical Options=Yes -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\Procedure] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Procedure\Create] -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\DatabasePackage] -Create=Yes -Drop=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\WebService] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Dimension] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\Synchronization] -GenBackupTabl=1 -GenKeepBackTabl=1 -GenTmpTablDrop=No -GenKeepTablOpts=No - -[FolderOptions\Physical Objects\Database Generation\User <<Schema>>] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\User <<User>>] -Create=Yes -Drop=Yes -Comment=Yes -Privilege=No - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Aggregate>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Aggregate>>\ExtendedSubObject <<AggregateParameter>>] -Create=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Assembly>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<AsymmetricKey>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Certificate>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Contract>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<EndPoint>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<EventNotification>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<FullTextCatalog>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<MessageType>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<PartitionFunction>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<PartitionScheme>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Queue>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<RemoteServiceBinding>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<ResourcePool>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Route>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Service>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<SymmetricKey>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<WorkloadGroup>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<XMLSchemaCollection>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Test Data] -GenDataPathName= -GenDataSinglefile=Yes -GenDataScriptName=testdata -GenDataScriptName0= -GenDataScriptName1= -GenDataScriptName2= -GenDataScriptName3= -GenDataScriptName4= -GenDataScriptName5= -GenDataScriptName6= -GenDataScriptName7= -GenDataScriptName8= -GenDataScriptName9= -GenDataOdbc=0 -GenDataDelOld=No -GenDataTitle=No -GenDataDefNumRows=20 -GenDataCommit=0 -GenDataPacket=0 -GenDataOwner=No -GenDataProfNumb= -GenDataProfChar= -GenDataProfDate= -GenDataCSVSeparator=, -GenDataFileFormat=CSV -GenDataUseWizard=No - -[FolderOptions\Pdm] -IndxIQName=%COLUMN%_%INDEXTYPE% -IndxPK=Yes -IndxFK=Yes -IndxAK=Yes -IndxPKName=%TABLE%_PK -IndxFKName=%REFR%_FK -IndxAKName=%AKEY%_AK -IndxPreserve=No -IndxThreshold=0 -IndxStats=No -RefrPreserve=No -JidxPreserve=No -RbldMultiFact=Yes -RbldMultiDim=Yes -RbldMultiJidx=Yes -CubePreserve=No -TablStProcPreserve=No -ProcDepPreserve=Yes -TrgrDepPreserve=Yes -CubeScriptPath= -CubeScriptCase=0 -CubeScriptEncoding=ANSI -CubeScriptNacct=No -CubeScriptHeader=No -CubeScriptExt=csv -CubeScriptExt0=txt -CubeScriptExt1= -CubeScriptExt2= -CubeScriptSep=, -CubeScriptDeli=" -EstimationYears=0 -DfltDomnName=D_%.U:VALUE% -DfltColnName=D_%.U:VALUE% -DfltReuse=Yes -DfltDrop=Yes - -[FolderOptions\CheckModel] - -[FolderOptions\CheckModel\Package] - -[FolderOptions\CheckModel\Package\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\CheckPackageMissTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\DefaultCheckPackageMissTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\CircularReference] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\ConstraintName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\CnstMaxLen] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\CircularDependency] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\ShortcutUniqCode] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Table] - -[FolderOptions\CheckModel\Table\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\UniqIndex] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\MaxLen - NAME] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\EmptyColl - COLNCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\EmptyColl - INDXCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\EmptyColl - KEYCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\SerialColumnNumber] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\EmptyCollYesYes] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\TableIndexes] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\Mapping] -CheckSeverity=No -FixRequested=Yes -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\MappingSFMap] -CheckSeverity=No -FixRequested=Yes -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\EmptyColl - PERMCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Table\CheckTablePartitionKey] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckTableStartDate] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckTableRefNoLifecycle] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckTableSourceMapping] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckTablePartialColumnMapping] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckTableKeyColumnMapping] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckTableNotOnLifecycleTablespace] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\MSSQLSRV2012_Table_Table_Timestamp_Uniqueness] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column] - -[FolderOptions\CheckModel\Table.Column\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\DomainDivergence] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\ColumnMandatory] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckNumParam] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckPrecSupLng] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckUndefDttp] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\FkeyDttpDivergence] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\FkeyCheckDivergence] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\ColnSqncNoKey] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\ColnSqncDttp] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\SerialColumnFK] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\ColumnCompExpr] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckColumnOneToOneMapping] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckColumnDataTypeMapping] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckColumnNoMapping] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckDttpIncompatibleFormat] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\MSSQLSRV2012_Column_Identity_Seed_and_Increment_validity] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index] - -[FolderOptions\CheckModel\Table.Index\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\EmptyColl - CIDXCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\UndefIndexType] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\IndexColumnCount] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\IQIndxHNGUniq] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\CheckIncludeColl - Tabl] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\MSSQLSRV2012_BaseIndex_XML_index_specific_checks] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key] - -[FolderOptions\CheckModel\Table.Key\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\EmptyColl - COLNCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\CheckIncludeColl - Tabl] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\MultiKeySqnc] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger] - -[FolderOptions\CheckModel\Table.Trigger\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index] - -[FolderOptions\CheckModel\Join Index\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View] - -[FolderOptions\CheckModel\View\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\EmptyColl - PERMCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\View.View Index] - -[FolderOptions\CheckModel\View.View Index\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\EmptyColl - CIDXCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\IndexColumnCount] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\CheckIncludeColl - Tabl] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\MSSQLSRV2012_BaseIndex_XML_index_specific_checks] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference] - -[FolderOptions\CheckModel\Reference\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\Reflexive] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\EmptyColl - RFJNCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\IncompleteJoin] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\JoinOrder] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference] - -[FolderOptions\CheckModel\View Reference\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\EmptyColl - VRFJNCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain] - -[FolderOptions\CheckModel\Domain\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\CheckNumParam] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\CheckPrecSupLng] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\CheckUndefDttp] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\CheckDttpIncompatibleFormat] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\MSSQLSRV2012_PhysicalDomain_Domain_missing_default_object] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default] - -[FolderOptions\CheckModel\Default\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\DfltValeEmpty] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\DfltSameVale] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User] - -[FolderOptions\CheckModel\User\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\UniquePassword] -CheckSeverity=No -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Group] - -[FolderOptions\CheckModel\Group\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\EmptyColl - USERCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\UniquePassword] -CheckSeverity=No -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Role] - -[FolderOptions\CheckModel\Role\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\EmptyColl - USERCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure] - -[FolderOptions\CheckModel\Procedure\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\ProcBodyEmpty] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\EmptyColl - PERMCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\DBMS Trigger] - -[FolderOptions\CheckModel\DBMS Trigger\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\DbmsTriggerEvent] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\MSSQLSRV2012_DBMSTrigger_Logon_must_be_unique_trigger_event] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source] - -[FolderOptions\CheckModel\Data Source\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\EmptyColl - MODLSRC] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\DtscTargets] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\CheckDataSourceModels] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Horizontal Partitioning] - -[FolderOptions\CheckModel\Horizontal Partitioning\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Horizontal Partitioning\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Horizontal Partitioning\EmptyColl - PARTCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Horizontal Partitioning\TargetTables] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Vertical Partitioning] - -[FolderOptions\CheckModel\Vertical Partitioning\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Vertical Partitioning\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Vertical Partitioning\EmptyColl - PARTCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Vertical Partitioning\TargetTables] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table Collapsing] - -[FolderOptions\CheckModel\Table Collapsing\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table Collapsing\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table Collapsing\EmptyColl - TargetTable] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table Collapsing\TargetTables] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact] - -[FolderOptions\CheckModel\Fact\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\EmptyColl - MEASCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\Mapping] -CheckSeverity=No -FixRequested=Yes -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\MappingSFMap] -CheckSeverity=No -FixRequested=Yes -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\EmptyColl - ALLOLINKCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\CubeDupAssociation] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension] - -[FolderOptions\CheckModel\Dimension\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\EmptyColl - DATTRCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\EmptyColl - HIERCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\DimnDupHierarchy] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\DimnDefHierarchy] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\Mapping] -CheckSeverity=No -FixRequested=Yes -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\MappingSFMap] -CheckSeverity=No -FixRequested=Yes -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\SerialColumnNumber] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Association] - -[FolderOptions\CheckModel\Association\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Association\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Association\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Association\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Association\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Association\EmptyColl - Hierarchy] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute] - -[FolderOptions\CheckModel\Dimension.Attribute\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure] - -[FolderOptions\CheckModel\Fact.Measure\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy] - -[FolderOptions\CheckModel\Dimension.Hierarchy\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\EmptyColl - DATTRCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym] - -[FolderOptions\CheckModel\Synonym\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\MaxLen - NAME] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\EmptyColl - BASEOBJ] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type] - -[FolderOptions\CheckModel\Abstract Data Type\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\AdtInstantiable] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\AdtAbstractUsed] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure] - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\AdtProcUniqName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\UniqueDefinition] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\ReturnDataType] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package] - -[FolderOptions\CheckModel\Database Package\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\MaxLen - NAME] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\EmptyColl - PROCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\EmptyColl - CURCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Database Package\EmptyColl - VARCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Database Package\EmptyColl - TYPCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Database Package\EmptyColl - EXCCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Database Package.Database Package Procedure] - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\UniqueDefinition] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\EmptyColl - PARM] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\ReturnDataType] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence] - -[FolderOptions\CheckModel\Sequence\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor] - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\UniqueDefinition] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\ReturnDataType] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\EmptyColl - PARM] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Database Package.Database Package Variable] - -[FolderOptions\CheckModel\Database Package.Database Package Variable\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\CheckUndefDttp] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type] - -[FolderOptions\CheckModel\Database Package.Database Package Type\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\UniqueDefinition] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception] - -[FolderOptions\CheckModel\Database Package.Database Package Exception\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace] - -[FolderOptions\CheckModel\Tablespace\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\IsObjectUsed] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage] - -[FolderOptions\CheckModel\Storage\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\IsObjectUsed] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database] - -[FolderOptions\CheckModel\Database\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\IsObjectUsed] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service] - -[FolderOptions\CheckModel\Web Service\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation] - -[FolderOptions\CheckModel\Web Service.Web Operation\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle] - -[FolderOptions\CheckModel\Lifecycle\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\CheckLifecyclePhase] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\CheckLifecycleRetention] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\CheckPartitionRange] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase] - -[FolderOptions\CheckModel\Lifecycle.Phase\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseTbspace] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseIQTbspace] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseDuplicateTbspace] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseTbspaceCurrency] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseRetention] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseIdlePeriod] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseDataSource] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseExternalOnFirst] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Replication] - -[FolderOptions\CheckModel\Replication\PartialReplication] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule] - -[FolderOptions\CheckModel\Business Rule\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\EmptyColl - OBJCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object] - -[FolderOptions\CheckModel\Extended Object\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\MSSQLSRV2012_ExtendedObject_Queue_Valid_maximum_number_of_readers_] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link] - -[FolderOptions\CheckModel\Extended Link\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File] - -[FolderOptions\CheckModel\File\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File\CheckPathExists] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format] - -[FolderOptions\CheckModel\Data Format\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\CheckDataFormatNullExpression] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model] - -[FolderOptions\CheckModel\Physical Data Model\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\CheckPackageMissTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\DefaultCheckPackageMissTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\MSSQLSRV2012_Model_Storage_requires_a_database_] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes -[ModelOptions] - -[ModelOptions\Physical Objects] -CaseSensitive=No -DisplayName=Yes -EnableTrans=No -UseTerm=No -EnableRequirements=No -EnableFullShortcut=Yes -DefaultDttp= -IgnoreOwner=No -RebuildTrigger=Yes -RefrUnique=No -RefrAutoMigrate=Yes -RefrMigrateReuse=Yes -RefrMigrateDomain=Yes -RefrMigrateCheck=Yes -RefrMigrateRule=Yes -RefrMigrateExtd=No -RefrMigrDefaultLink=No -RefrDfltImpl=D -RefrPrgtColn=No -RefrMigrateToEnd=No -RebuildTriggerDep=No -ColnFKName=%.3:PARENT%_%COLUMN% -ColnFKNameUse=No -DomnCopyDttp=Yes -DomnCopyChck=No -DomnCopyRule=No -DomnCopyMand=No -DomnCopyExtd=No -DomnCopyProf=No -Notation=0 -DomnDefaultMandatory=No -ColnDefaultMandatory=No -TablDefaultOwner= -ViewDefaultOwner= -TrgrDefaultOwnerTabl= -TrgrDefaultOwnerView= -IdxDefaultOwnerTabl= -IdxDefaultOwnerView= -JdxDefaultOwner= -DBPackDefaultOwner= -SeqDefaultOwner= -ProcDefaultOwner= -DBMSTrgrDefaultOwner= -Currency=USD -RefrDeleteConstraint=1 -RefrUpdateConstraint=1 -RefrParentMandatory=No -RefrParentChangeAllow=Yes -RefrCheckOnCommit=No - -[ModelOptions\Physical Objects\NamingOptionsTemplates] - -[ModelOptions\Physical Objects\ClssNamingOptions] - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMPCKG] - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMPCKG\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMPCKG\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMDOMN] - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMDOMN\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMDOMN\Code] -Template= -MaxLen=128 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\TABL] - -[ModelOptions\Physical Objects\ClssNamingOptions\TABL\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\TABL\Code] -Template= -MaxLen=128 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\COLN] - -[ModelOptions\Physical Objects\ClssNamingOptions\COLN\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\COLN\Code] -Template= -MaxLen=128 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\INDX] - -[ModelOptions\Physical Objects\ClssNamingOptions\INDX\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\INDX\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\REFR] - -[ModelOptions\Physical Objects\ClssNamingOptions\REFR\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\REFR\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VREF] - -[ModelOptions\Physical Objects\ClssNamingOptions\VREF\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VREF\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEW] - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEW\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEW\Code] -Template= -MaxLen=128 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEWC] - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEWC\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEWC\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBSERV] - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBSERV\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBSERV\Code] -Template= -MaxLen=254 -Case=M -ValidChar='a'-'z','A'-'Z','0'-'9',"/-_.!~*'()" -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBOP] - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBOP\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBOP\Code] -Template= -MaxLen=254 -Case=M -ValidChar='a'-'z','A'-'Z','0'-'9',"/-_.!~*'()" -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WPARAM] - -[ModelOptions\Physical Objects\ClssNamingOptions\WPARAM\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WPARAM\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FACT] - -[ModelOptions\Physical Objects\ClssNamingOptions\FACT\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FACT\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DIMN] - -[ModelOptions\Physical Objects\ClssNamingOptions\DIMN\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DIMN\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\MEAS] - -[ModelOptions\Physical Objects\ClssNamingOptions\MEAS\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\MEAS\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DATTR] - -[ModelOptions\Physical Objects\ClssNamingOptions\DATTR\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DATTR\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FILO] - -[ModelOptions\Physical Objects\ClssNamingOptions\FILO\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FILO\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMEOBJ] - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMEOBJ\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMEOBJ\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMELNK] - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMELNK\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMELNK\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DefaultClass] - -[ModelOptions\Physical Objects\ClssNamingOptions\DefaultClass\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DefaultClass\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Connection] - -[ModelOptions\Pdm] - -[ModelOptions\Generate] - -[ModelOptions\Generate\Xsm] -GenRootElement=Yes -GenComplexType=No -GenAttribute=Yes -CheckModel=Yes -SaveLinks=Yes -ORMapping=No -NameToCode=No - -[ModelOptions\Generate\Pdm] -RRMapping=No -PreserveMode=Yes -CheckModel=Yes -SaveLinks=Yes -NameToCode=No -EnableTransformations=No - -[ModelOptions\Generate\Cdm] -CheckModel=Yes -SaveLinks=Yes -NameToCode=No -Notation=2 - -[ModelOptions\Generate\Oom] -CheckModel=Yes -SaveLinks=Yes -ORMapping=No -NameToCode=Yes -ClassPrefix= - -[ModelOptions\Generate\Ldm] -CheckModel=Yes -SaveLinks=Yes -NameToCode=No - -[ModelOptions\Default Opts] - -[ModelOptions\Default Opts\TABL] -PhysOpts= - -[ModelOptions\Default Opts\COLN] -PhysOpts= - -[ModelOptions\Default Opts\INDX] -PhysOpts= - -[ModelOptions\Default Opts\AKEY] -PhysOpts= - -[ModelOptions\Default Opts\PKEY] -PhysOpts= - -[ModelOptions\Default Opts\STOR] -PhysOpts= - -[ModelOptions\Default Opts\TSPC] -PhysOpts= - -[ModelOptions\Default Opts\SQNC] -PhysOpts= - -[ModelOptions\Default Opts\DTBS] -PhysOpts= - -[ModelOptions\Default Opts\USER] -PhysOpts= - -[ModelOptions\Default Opts\JIDX] -PhysOpts= - -[ModelOptions\Default Opts\FRMEOBJ<<ResourcePool>>] -PhysOpts= - -[ModelOptions\Default Opts\FRMEOBJ<<WorkloadGroup>>] -PhysOpts= - -[ModelOptions\Default Opts\FRMESOB<<ResourcePool>>] -PhysOpts= - -[ModelOptions\Default Opts\FRMESOB<<WorkloadGroup>>] -PhysOpts= - - -0220C4FC-A988-47A4-958E-3F8100D0D78C -Samples -Samples -1514588680 -Administrator -1514588680 -Administrator - -91AD3F79-C708-47A6-81A2-7B9E077A7DF4 -CDE44E21-9669-11D1-9914-006097355D9B - - - - -F7C7A338-9D4B-4B6A-9A6E-A178CA0B07BD -PostgreSQL 9.x -PGSQL9 -1514588676 -Administrator -1514588676 -Administrator - -83063C86-4E0A-4954-BF00-620C0D0F02D8 -4BA9F647-DAB1-11D1-9944-006097355D9B - - - - -99402516-CE39-453F-8C7F-B95ED4661C50 -Order -Order -1514588679 -Administrator -1514588679 -Administrator -ORG {92360091-5759-4724-BE43-894656D9A995} -DAT 1514588679 -[DisplayPreferences] - -[DisplayPreferences\PDM] - -[DisplayPreferences\General] -Adjust to text=Yes -Snap Grid=No -Constrain Labels=Yes -Display Grid=No -Show Page Delimiter=Yes -Show Links intersections=No -Activate automatic link routing=No -Grid size=0 -Graphic unit=2 -Window color=255, 255, 255 -Background image= -Background mode=8 -Watermark image= -Watermark mode=8 -Show watermark on screen=No -Gradient mode=0 -Gradient end color=255, 255, 255 -Show Swimlane=No -SwimlaneVert=Yes -TreeVert=No -CompDark=0 - -[DisplayPreferences\Object] -Show Icon=No -Mode=2 -Trunc Length=40 -Word Length=40 -Word Text=!"#$%&')*+,-./:;=>?@\]^_`|}~ -Shortcut IntIcon=Yes -Shortcut IntLoct=Yes -Shortcut IntFullPath=No -Shortcut IntLastPackage=Yes -Shortcut ExtIcon=Yes -Shortcut ExtLoct=No -Shortcut ExtFullPath=No -Shortcut ExtLastPackage=Yes -Shortcut ExtIncludeModl=Yes -EObjShowStrn=Yes -ExtendedObject.Comment=No -ExtendedObject.IconPicture=No -ExtendedObject.TextStyle=No -ExtendedObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Object Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -ELnkShowStrn=Yes -ELnkShowName=Yes -ExtendedLink_SymbolLayout=<Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Source" >[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] </Form>[CRLF]</Form> -FileObject.Stereotype=No -FileObject.DisplayName=Yes -FileObject.LocationOrName=No -FileObject.IconPicture=No -FileObject.TextStyle=No -FileObject.IconMode=Yes -FileObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Location" Attribute="LocationOrName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Package.Stereotype=Yes -Package.Comment=No -Package.IconPicture=No -Package.TextStyle=No -Package_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Display Model Version=Yes -Table.Stereotype=Yes -Table.DisplayName=Yes -Table.OwnerDisplayName=No -Table.Columns=Yes -Table.Columns._Filter="All Columns" PDMCOLNALL -Table.Columns._Columns=Stereotype DataType KeyIndicator -Table.Columns._Limit=-5 -Table.Keys=No -Table.Keys._Columns=Stereotype Indicator -Table.Indexes=No -Table.Indexes._Columns=Stereotype -Table.Triggers=No -Table.Triggers._Columns=Stereotype -Table.Comment=No -Table.IconPicture=No -Table.TextStyle=No -Table_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="Stereotype No\r\nDisplayName Yes\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nDomain No\r\nKeyIndicator No\r\nIndexIndicator No\r\nNullStatus No" Filters="&quot;All Columns&quot; PDMCOLNALL &quot;&quot;\r\n&quot;PK Columns&quot; PDMCOLNPK &quot;\&quot;PRIM \&quot;TRUE\&quot; TRUE\&quot;&quot;\r\n&quot;Key Columns&quot; PDMCOLNKEY &quot;\&quot;KEYS \&quot;TRUE\&quot; TRUE\&quot;&quot;" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Keys" Collection="Keys" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Triggers" Collection="Triggers" Columns="Stereotype No\r\nDisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -View.Stereotype=Yes -View.DisplayName=Yes -View.OwnerDisplayName=No -View.Columns=Yes -View.Columns._Columns=DisplayName -View.Columns._Limit=-5 -View.TemporaryVTables=Yes -View.Indexes=No -View.Comment=No -View.IconPicture=No -View.TextStyle=No -View_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="DisplayName No\r\nExpression No\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nIndexIndicator No" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Tables" Collection="TemporaryVTables" Columns="Name Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="DisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Procedure.Stereotype=No -Procedure.DisplayName=Yes -Procedure.OwnerDisplayName=No -Procedure.Comment=No -Procedure.IconPicture=No -Procedure.TextStyle=No -Procedure_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Reference.Cardinality=No -Reference.ImplementationType=No -Reference.ChildRole=Yes -Reference.Stereotype=Yes -Reference.DisplayName=No -Reference.ForeignKeyConstraintName=No -Reference.JoinExpression=No -Reference.Integrity=No -Reference.ParentRole=Yes -Reference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Cardinality" Attribute="Cardinality" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Implementation" Attribute="ImplementationType" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Cons&amp;traint Name" Attribute="ForeignKeyConstraintName" Prefix="" Suffix="" Caption="Cons&amp;traint Name" Mandatory="No" />[CRLF] <StandardAttribute Name="Join" Attribute="JoinExpression" Prefix="" Suffix="" Caption="Join" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Referential integrity" Attribute="Integrity" Prefix="" Suffix="" Caption="Referential integrity" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> -ViewReference.ChildRole=Yes -ViewReference.Stereotype=Yes -ViewReference.DisplayName=No -ViewReference.JoinExpression=No -ViewReference.ParentRole=Yes -ViewReference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Join Expression" Attribute="JoinExpression" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> - -[DisplayPreferences\Symbol] - -[DisplayPreferences\Symbol\FRMEOBJ] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=6000 -Height=2000 -Brush color=255 255 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=64 -Brush gradient color=192 192 192 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 255 128 128 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FRMELNK] -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FILO] -OBJSTRNFont=新宋体,8,N -OBJSTRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LCNMFont=新宋体,8,N -LCNMFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=3600 -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PDMPCKG] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 178 178 178 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\TABL] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -KeysFont=新宋体,8,N -KeysFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -TriggersFont=新宋体,8,N -TriggersFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=178 214 252 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VIEW] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -TemporaryVTablesFont=新宋体,8,N -TemporaryVTablesFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=208 208 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PROC] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4000 -Height=1000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 108 0 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\REFR] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VREF] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\USRDEPD] -OBJXSTRFont=新宋体,8,N -OBJXSTRFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=2 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\Free Symbol] -Free TextFont=新宋体,8,N -Free TextFont color=0, 0, 0 -Line style=0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 -(8268, 11693) -((315,354), (433,354)) -1 -7 - - -1514588679 -1514588679 -((-5249,-9277), (-4799,-2683)) -((-5024,-2683),(-5024,-9277)) -1 -1 -12615680 -12632256 -CENTER 0 新宋体,8,N -SOURCE 0 新宋体,8,N -DESTINATION 0 新宋体,8,N - - - - - - - - - - - -1514588679 -1514588679 -((-3717,2041), (-3267,8646)) -((-3492,2041),(-3492,8646)) -1 -1 -12615680 -12632256 -CENTER 0 新宋体,8,N -SOURCE 0 新宋体,8,N -DESTINATION 0 新宋体,8,N - - - - - - - - - - - -1514588679 -1514588679 --1 -((-11260,6510), (7534,11634)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - -1514588679 -1514588679 --1 -((-11088,-3939), (10170,3194)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - -1514588679 -1514588679 --1 -((-10585,-13983), (8209,-6187)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - - - -D132344A-A8E9-4A57-8F6D-7691E6408656 -Product -Product -1514588679 -Administrator -1514588679 -Administrator -ORG {DE41247D-905B-4995-863B-693380B257D6} -DAT 1514588679 -[DisplayPreferences] - -[DisplayPreferences\PDM] - -[DisplayPreferences\General] -Adjust to text=Yes -Snap Grid=No -Constrain Labels=Yes -Display Grid=No -Show Page Delimiter=Yes -Show Links intersections=No -Activate automatic link routing=No -Grid size=0 -Graphic unit=2 -Window color=255, 255, 255 -Background image= -Background mode=8 -Watermark image= -Watermark mode=8 -Show watermark on screen=No -Gradient mode=0 -Gradient end color=255, 255, 255 -Show Swimlane=No -SwimlaneVert=Yes -TreeVert=No -CompDark=0 - -[DisplayPreferences\Object] -Show Icon=No -Mode=2 -Trunc Length=40 -Word Length=40 -Word Text=!"#$%&')*+,-./:;=>?@\]^_`|}~ -Shortcut IntIcon=Yes -Shortcut IntLoct=Yes -Shortcut IntFullPath=No -Shortcut IntLastPackage=Yes -Shortcut ExtIcon=Yes -Shortcut ExtLoct=No -Shortcut ExtFullPath=No -Shortcut ExtLastPackage=Yes -Shortcut ExtIncludeModl=Yes -EObjShowStrn=Yes -ExtendedObject.Comment=No -ExtendedObject.IconPicture=No -ExtendedObject.TextStyle=No -ExtendedObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Object Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -ELnkShowStrn=Yes -ELnkShowName=Yes -ExtendedLink_SymbolLayout=<Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Source" >[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] </Form>[CRLF]</Form> -FileObject.Stereotype=No -FileObject.DisplayName=Yes -FileObject.LocationOrName=No -FileObject.IconPicture=No -FileObject.TextStyle=No -FileObject.IconMode=Yes -FileObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Location" Attribute="LocationOrName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Package.Stereotype=Yes -Package.Comment=No -Package.IconPicture=No -Package.TextStyle=No -Package_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Display Model Version=Yes -Table.Stereotype=Yes -Table.DisplayName=Yes -Table.OwnerDisplayName=No -Table.Columns=Yes -Table.Columns._Filter="All Columns" PDMCOLNALL -Table.Columns._Columns=Stereotype DataType KeyIndicator -Table.Columns._Limit=-5 -Table.Keys=No -Table.Keys._Columns=Stereotype Indicator -Table.Indexes=No -Table.Indexes._Columns=Stereotype -Table.Triggers=No -Table.Triggers._Columns=Stereotype -Table.Comment=No -Table.IconPicture=No -Table.TextStyle=No -Table_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="Stereotype No\r\nDisplayName Yes\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nDomain No\r\nKeyIndicator No\r\nIndexIndicator No\r\nNullStatus No" Filters="&quot;All Columns&quot; PDMCOLNALL &quot;&quot;\r\n&quot;PK Columns&quot; PDMCOLNPK &quot;\&quot;PRIM \&quot;TRUE\&quot; TRUE\&quot;&quot;\r\n&quot;Key Columns&quot; PDMCOLNKEY &quot;\&quot;KEYS \&quot;TRUE\&quot; TRUE\&quot;&quot;" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Keys" Collection="Keys" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Triggers" Collection="Triggers" Columns="Stereotype No\r\nDisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -View.Stereotype=Yes -View.DisplayName=Yes -View.OwnerDisplayName=No -View.Columns=Yes -View.Columns._Columns=DisplayName -View.Columns._Limit=-5 -View.TemporaryVTables=Yes -View.Indexes=No -View.Comment=No -View.IconPicture=No -View.TextStyle=No -View_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="DisplayName No\r\nExpression No\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nIndexIndicator No" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Tables" Collection="TemporaryVTables" Columns="Name Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="DisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Procedure.Stereotype=No -Procedure.DisplayName=Yes -Procedure.OwnerDisplayName=No -Procedure.Comment=No -Procedure.IconPicture=No -Procedure.TextStyle=No -Procedure_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Reference.Cardinality=No -Reference.ImplementationType=No -Reference.ChildRole=Yes -Reference.Stereotype=Yes -Reference.DisplayName=No -Reference.ForeignKeyConstraintName=No -Reference.JoinExpression=No -Reference.Integrity=No -Reference.ParentRole=Yes -Reference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Cardinality" Attribute="Cardinality" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Implementation" Attribute="ImplementationType" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Cons&amp;traint Name" Attribute="ForeignKeyConstraintName" Prefix="" Suffix="" Caption="Cons&amp;traint Name" Mandatory="No" />[CRLF] <StandardAttribute Name="Join" Attribute="JoinExpression" Prefix="" Suffix="" Caption="Join" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Referential integrity" Attribute="Integrity" Prefix="" Suffix="" Caption="Referential integrity" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> -ViewReference.ChildRole=Yes -ViewReference.Stereotype=Yes -ViewReference.DisplayName=No -ViewReference.JoinExpression=No -ViewReference.ParentRole=Yes -ViewReference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Join Expression" Attribute="JoinExpression" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> - -[DisplayPreferences\Symbol] - -[DisplayPreferences\Symbol\FRMEOBJ] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=6000 -Height=2000 -Brush color=255 255 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=64 -Brush gradient color=192 192 192 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 255 128 128 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FRMELNK] -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FILO] -OBJSTRNFont=新宋体,8,N -OBJSTRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LCNMFont=新宋体,8,N -LCNMFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=3600 -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PDMPCKG] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 178 178 178 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\TABL] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -KeysFont=新宋体,8,N -KeysFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -TriggersFont=新宋体,8,N -TriggersFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=178 214 252 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VIEW] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -TemporaryVTablesFont=新宋体,8,N -TemporaryVTablesFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=208 208 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PROC] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4000 -Height=1000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 108 0 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\REFR] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VREF] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\USRDEPD] -OBJXSTRFont=新宋体,8,N -OBJXSTRFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=2 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\Free Symbol] -Free TextFont=新宋体,8,N -Free TextFont color=0, 0, 0 -Line style=0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 -(8268, 11693) -((315,354), (433,354)) -1 -7 - - -1514588679 -1514588679 --1 -((-9995,-2970), (8799,3952)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - - - -7B4923DF-FCA1-4AF9-B77C-A48CCE34483F -Customer -Customer -1514588679 -Administrator -1514588679 -Administrator -ORG {68F8168C-38E6-4220-9B3C-0FA0C72EE518} -DAT 1514588679 -[DisplayPreferences] - -[DisplayPreferences\PDM] - -[DisplayPreferences\General] -Adjust to text=Yes -Snap Grid=No -Constrain Labels=Yes -Display Grid=No -Show Page Delimiter=Yes -Show Links intersections=No -Activate automatic link routing=No -Grid size=0 -Graphic unit=2 -Window color=255, 255, 255 -Background image= -Background mode=8 -Watermark image= -Watermark mode=8 -Show watermark on screen=No -Gradient mode=0 -Gradient end color=255, 255, 255 -Show Swimlane=No -SwimlaneVert=Yes -TreeVert=No -CompDark=0 - -[DisplayPreferences\Object] -Show Icon=No -Mode=2 -Trunc Length=40 -Word Length=40 -Word Text=!"#$%&')*+,-./:;=>?@\]^_`|}~ -Shortcut IntIcon=Yes -Shortcut IntLoct=Yes -Shortcut IntFullPath=No -Shortcut IntLastPackage=Yes -Shortcut ExtIcon=Yes -Shortcut ExtLoct=No -Shortcut ExtFullPath=No -Shortcut ExtLastPackage=Yes -Shortcut ExtIncludeModl=Yes -EObjShowStrn=Yes -ExtendedObject.Comment=No -ExtendedObject.IconPicture=No -ExtendedObject.TextStyle=No -ExtendedObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Object Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -ELnkShowStrn=Yes -ELnkShowName=Yes -ExtendedLink_SymbolLayout=<Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Source" >[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] </Form>[CRLF]</Form> -FileObject.Stereotype=No -FileObject.DisplayName=Yes -FileObject.LocationOrName=No -FileObject.IconPicture=No -FileObject.TextStyle=No -FileObject.IconMode=Yes -FileObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Location" Attribute="LocationOrName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Package.Stereotype=Yes -Package.Comment=No -Package.IconPicture=No -Package.TextStyle=No -Package_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Display Model Version=Yes -Table.Stereotype=Yes -Table.DisplayName=Yes -Table.OwnerDisplayName=No -Table.Columns=Yes -Table.Columns._Filter="All Columns" PDMCOLNALL -Table.Columns._Columns=Stereotype DataType KeyIndicator -Table.Columns._Limit=-5 -Table.Keys=No -Table.Keys._Columns=Stereotype Indicator -Table.Indexes=No -Table.Indexes._Columns=Stereotype -Table.Triggers=No -Table.Triggers._Columns=Stereotype -Table.Comment=No -Table.IconPicture=No -Table.TextStyle=No -Table_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="Stereotype No\r\nDisplayName Yes\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nDomain No\r\nKeyIndicator No\r\nIndexIndicator No\r\nNullStatus No" Filters="&quot;All Columns&quot; PDMCOLNALL &quot;&quot;\r\n&quot;PK Columns&quot; PDMCOLNPK &quot;\&quot;PRIM \&quot;TRUE\&quot; TRUE\&quot;&quot;\r\n&quot;Key Columns&quot; PDMCOLNKEY &quot;\&quot;KEYS \&quot;TRUE\&quot; TRUE\&quot;&quot;" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Keys" Collection="Keys" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Triggers" Collection="Triggers" Columns="Stereotype No\r\nDisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -View.Stereotype=Yes -View.DisplayName=Yes -View.OwnerDisplayName=No -View.Columns=Yes -View.Columns._Columns=DisplayName -View.Columns._Limit=-5 -View.TemporaryVTables=Yes -View.Indexes=No -View.Comment=No -View.IconPicture=No -View.TextStyle=No -View_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="DisplayName No\r\nExpression No\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nIndexIndicator No" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Tables" Collection="TemporaryVTables" Columns="Name Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="DisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Procedure.Stereotype=No -Procedure.DisplayName=Yes -Procedure.OwnerDisplayName=No -Procedure.Comment=No -Procedure.IconPicture=No -Procedure.TextStyle=No -Procedure_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Reference.Cardinality=No -Reference.ImplementationType=No -Reference.ChildRole=Yes -Reference.Stereotype=Yes -Reference.DisplayName=No -Reference.ForeignKeyConstraintName=No -Reference.JoinExpression=No -Reference.Integrity=No -Reference.ParentRole=Yes -Reference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Cardinality" Attribute="Cardinality" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Implementation" Attribute="ImplementationType" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Cons&amp;traint Name" Attribute="ForeignKeyConstraintName" Prefix="" Suffix="" Caption="Cons&amp;traint Name" Mandatory="No" />[CRLF] <StandardAttribute Name="Join" Attribute="JoinExpression" Prefix="" Suffix="" Caption="Join" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Referential integrity" Attribute="Integrity" Prefix="" Suffix="" Caption="Referential integrity" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> -ViewReference.ChildRole=Yes -ViewReference.Stereotype=Yes -ViewReference.DisplayName=No -ViewReference.JoinExpression=No -ViewReference.ParentRole=Yes -ViewReference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Join Expression" Attribute="JoinExpression" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> - -[DisplayPreferences\Symbol] - -[DisplayPreferences\Symbol\FRMEOBJ] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=6000 -Height=2000 -Brush color=255 255 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=64 -Brush gradient color=192 192 192 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 255 128 128 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FRMELNK] -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FILO] -OBJSTRNFont=新宋体,8,N -OBJSTRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LCNMFont=新宋体,8,N -LCNMFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=3600 -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PDMPCKG] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 178 178 178 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\TABL] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -KeysFont=新宋体,8,N -KeysFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -TriggersFont=新宋体,8,N -TriggersFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=178 214 252 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VIEW] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -TemporaryVTablesFont=新宋体,8,N -TemporaryVTablesFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=208 208 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PROC] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4000 -Height=1000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 108 0 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\REFR] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VREF] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\USRDEPD] -OBJXSTRFont=新宋体,8,N -OBJXSTRFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=2 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\Free Symbol] -Free TextFont=新宋体,8,N -Free TextFont color=0, 0, 0 -Line style=0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 -(8268, 11693) -((315,354), (433,354)) -1 -7 - - -1514588679 -1514588679 --1 -((-13056,-66), (10598,13178)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - - - - - - - - -A86CD6BD-CEF1-4DFB-A046-A8D4588B6F56 -Sales -Sales -1514588679 -Administrator -1514588679 -Administrator -ORG {D792A2DB-5F90-499F-8B39-0AAFF559CD36} -DAT 1514588679 -Schema - - -8ADE3B77-4A05-461C-8412-6140835B6F15 -Productions -Productions -1514588679 -Administrator -1514588679 -Administrator -ORG {8B9F6BB3-2312-48C1-8479-F48207BDE3BC} -DAT 1514588679 -Schema - - -36B570C7-BF41-458E-88E1-1A16D3D04300 -Customers -Customers -1514588679 -Administrator -1514588679 -Administrator -ORG {DFC6388B-EEDB-4184-94C6-68548BFC8ADC} -DAT 1514588679 -Schema - - - - -F6E1FCB9-73D5-4220-9A1C-5722CB56572C -订单( Orders ) -Orders -1514588679 -Administrator -1514588679 -Administrator -订单 -ORG {DCFB7BAF-61C8-4927-8C69-7EC2C03A17BB} -DAT 1514588679 -ATT CLUS - - - -EB20AA9E-2AC7-446B-91E7-E863761E19DA -订单编号( OrderId ) -OrderId -1514588679 -Administrator -1514588679 -Administrator -订单编号 -ORG {44A75471-51DC-4FF7-9579-4F96537D6184} -DAT 1514588679 -UUID -1 - - -EB4CD169-76A0-4CFB-8C32-731D7C7268C3 -订单编码( Code ) -Code -1514588679 -Administrator -1514588679 -Administrator -订单编码 -ORG {64DB43B2-EFDA-499C-A1DF-F5D6A1F6FB0D} -DAT 1514588679 -VARCHAR(30) -30 -1 - - -7A465D53-749D-400A-BE0C-06433AFC5A5B -订单名称( Name ) -Name -1514588679 -Administrator -1514588679 -Administrator -订单名称 -ORG {935D2B35-8FDD-4862-8E04-77E196989914} -DAT 1514588679 -VARCHAR(200) -200 -1 - - -9489686D-08B1-408E-ABF1-DDB849835333 -版本号( Version ) -Version -1514588679 -Administrator -1514588679 -Administrator -版本号 -ORG {F83621F4-DAA3-4F37-8FBC-EEFCECA527A8} -DAT 1514588679 -BYTEA - - - - -5502B1A8-D6B1-40A6-8AD5-854AC9AA4D57 -Key_1 -Key_1 -1514588679 -Administrator -1514588679 -Administrator -ORG {92740D57-C6F0-457B-8D5D-4845D070F063} -DAT 1514588679 - - - - - - - - - - - - - -E8CB90D9-10CD-472E-AA2E-F0F8CE4C3DE6 -商品( Products ) -Products -1514588679 -Administrator -1514588679 -Administrator -商品 -ORG {D181918A-EA17-4145-A88E-907A77FACA10} -DAT 1514588679 -ATT CLUS - - - -B5FDCE96-B1EE-4177-B10E-0EDA7CD384F9 -商品编号( ProductId ) -ProductId -1514588679 -Administrator -1514588679 -Administrator -商品编号 -ORG {B53960D7-4FB2-4954-BED8-14AB641761B2} -DAT 1514588679 -INT4 -4 -1 - - -914D879F-F8E1-43B6-9B7B-0CB4007A46A9 -商品编码( Code ) -Code -1514588679 -Administrator -1514588679 -Administrator -商品编码 -ORG {82CB28CE-8970-4273-8B82-05213E608D8F} -DAT 1514588679 -VARCHAR(30) -30 -1 - - -26C30F45-BD28-4877-BF15-03F60A64C856 -商品名称( Name ) -Name -1514588679 -Administrator -1514588679 -Administrator -商品名称 -ORG {8A8B240F-EB3A-417B-B551-A984300FD92F} -DAT 1514588679 -VARCHAR(200) -200 -1 - - -4FC5AABB-29A6-4D66-A22E-3013A5FF2123 -扩展属性( Extends ) -Extends -1514588679 -Administrator -1514588679 -Administrator -扩展属性 -ORG {E956A6FE-EF12-4FD1-A6F9-7A0B2C7E3149} -DAT 1514588679 -CHAR - - -474F8823-957D-4821-90A6-AAA75B071659 -价格( Price ) -Price -1514588679 -Administrator -1514588679 -Administrator -价格 -ORG {509CD7FA-70E7-4E7D-88E5-5D6468A73853} -DAT 1514588679 -DECIMAL(10,2) -10 -2 - - -E642AA1F-5400-4208-B3E9-552DE8F509A5 -是否删除( IsDeleted ) -IsDeleted -1514588679 -Administrator -1514588679 -Administrator -是否删除 -ORG {C8C5119A-D798-456A-B4FD-CB35C05B804C} -DAT 1514588679 -BOOL -1 - - -08623767-AE80-427B-82AD-5894EC0811E0 -版本号( Version ) -Version -1514588679 -Administrator -1514588679 -Administrator -版本号 -ORG {269BD895-4B63-4E11-B8C1-EA90928F070F} -DAT 1514588679 -BYTEA - - - - -C68B00F7-856C-42F2-8767-241ABC6D4D01 -Key_1 -Key_1 -1514588679 -Administrator -1514588679 -Administrator -ORG {B1442255-04B5-4E29-99AA-B7218DA5522E} -DAT 1514588679 - - - - - - - - - - - - - -8D03640D-E748-4541-AE89-14C9CE0A2B1E -订单明细( OrderItems ) -OrderItems -1514588679 -Administrator -1514588679 -Administrator -订单明细 -ORG {BA70D8F5-EDAF-42FF-A493-ADD0F270786D} -DAT 1514588679 -ATT CLUS - - - -600B47DF-584F-4A54-8DB4-EAA31DA53E09 -订单明细编号( OrderItemId ) -OrderItemId -1514588679 -Administrator -1514588679 -Administrator -订单明细编号 -ORG {0CC5B156-1828-4743-A78A-1BA46EA6EF63} -DAT 1514588679 -UUID -1 - - -7E2225EE-79CB-4C71-9192-951B544ADE37 -订单编号( OrderId ) -OrderId -1514588679 -Administrator -1514588679 -Administrator -订单编号 -ORG {DFD8CD92-6982-4B46-AA1B-1F5C5ACEA826} -DAT 1514588679 -UUID -1 - - -5AF42155-A317-442E-A356-008EDE403C73 -商品编号( ProductId ) -ProductId -1514588679 -Administrator -1514588679 -Administrator -商品编号 -ORG {258D4008-B597-465B-A18B-9230CF186B40} -DAT 1514588679 -INT4 -4 -1 - - -BF1201F7-2E8C-49AD-B10A-5E7FB54C1673 -商品名称( ProductName ) -ProductName -1514588679 -Administrator -1514588679 -Administrator -商品名称 -ORG {36726035-757C-4149-9B60-C814BC94A7BE} -DAT 1514588679 -VARCHAR(200) -200 -1 - - -F98B684D-838D-4648-B04A-D391B9115A87 -数量( Quantity ) -Quantity -1514588679 -Administrator -1514588679 -Administrator -数量 -ORG {40D46135-FFF4-46EF-B62C-D10D3368930C} -DAT 1514588679 -INT4 -4 -1 - - -2358BE1B-3A11-4204-9A14-11DC3EDA0A68 -单价( Price ) -Price -1514588679 -Administrator -1514588679 -Administrator -单价 -ORG {B1C7C098-90D1-4B4C-BC25-06777E071830} -DAT 1514588679 -DECIMAL(10,2) -10 -2 -1 - - - - -5E0C739D-581D-470F-8018-0E860A0528CE -Key_1 -Key_1 -1514588679 -Administrator -1514588679 -Administrator -ORG {0F965720-6103-46E8-8EE7-B48237AF21E8} -DAT 1514588679 - - - - - - - - - - - - - -B9905C53-6A71-454F-A252-6AF77730A58B -客户( Customers ) -Customers -1514588679 -Administrator -1514588679 -Administrator -客户 -ORG {C5252905-0F60-4917-A321-FD71B172ACA0} -DAT 1514588679 -ATT CLUS - - - -07AEA1E3-6584-4721-AA86-E554B72027EB -客户编号( CustomerId ) -CustomerId -1514588679 -Administrator -1514588679 -Administrator -客户编号 -ORG {A823E497-5CB0-432E-BF50-FD37EBC2B648} -DAT 1514588679 -VARCHAR(50) -50 -1 - - -CF2B3140-9BA1-4A25-8FAD-5AC90E0624D9 -客户名称( Name ) -Name -1514588679 -Administrator -1514588679 -Administrator -客户名称 -ORG {A58AEBD8-0122-43F0-813E-091BA1D20FED} -DAT 1514588679 -VARCHAR(20) -20 -1 - - -E13F7460-9EA1-44A4-838D-F0E2134975B1 -昵称( Nickname ) -Nickname -1514588679 -Administrator -1514588679 -Administrator -昵称 -ORG {26AF2F9E-4450-46ED-B9F3-00FDD4D69E87} -DAT 1514588679 -VARCHAR(30) -30 - - -51136895-A0CD-404F-87E1-530EE2E17D3A -余额( Balance ) -Balance -1514588679 -Administrator -1514588679 -Administrator -余额 -ORG {A3AC3300-5774-4BA9-BDC3-2B833A31E13F} -DAT 1514588679 -DECIMAL(18,2) -18 -2 -1 - - -3CDF9FB3-F743-4E39-857D-E5780FDA17A7 -性别( Gender ) -Gender -1514588679 -Administrator -1514588679 -Administrator -性别 -ORG {B3906707-46B9-426E-A2F9-8D132FBE47BA} -DAT 1514588679 -INT4 -4 - - -ADE5200B-0F08-4CCE-9E38-5C3A8C9AFBA4 -联系电话( Tel ) -Tel -1514588679 -Administrator -1514588679 -Administrator -联系电话 -ORG {422A0FBF-02EE-4C77-ACF3-0F6DFF20B846} -DAT 1514588679 -VARCHAR(20) -20 - - -9CDD2969-F400-4CB7-BA7E-C7ED73A2D729 -手机号( Mobile ) -Mobile -1514588679 -Administrator -1514588679 -Administrator -手机号 -ORG {8FA4B750-24CD-43B8-8397-F6D1EDECEBC9} -DAT 1514588679 -VARCHAR(20) -20 - - -2B6E2E54-0A53-4567-8EF0-4F93307D1A37 -电子邮件( Email ) -Email -1514588679 -Administrator -1514588679 -Administrator -电子邮件 -ORG {94E1690C-B465-4C47-B724-B4199213FBC0} -DAT 1514588679 -VARCHAR(100) -100 - - -8B0ED0A1-11D3-4C7F-9567-3D2F675C284C -创建时间( CreationTime ) -CreationTime -1514588679 -Administrator -1514588679 -Administrator -创建时间 -ORG {3DA62965-6815-4CF9-858E-6D5298D97075} -DAT 1514588679 -TIMESTAMP - - -E18D527C-5BDC-4101-87C3-2AC71EB82454 -创建人( CreatorId ) -CreatorId -1514588679 -Administrator -1514588679 -Administrator -创建人 -ORG {98A5605B-3958-404E-9658-384E3363DF0E} -DAT 1514588679 -UUID - - -EF3CF853-6173-4129-B471-E6E77A71A77B -最后修改时间( LastModificationTime ) -LastModificationTime -1514588679 -Administrator -1514588679 -Administrator -最后修改时间 -ORG {7A4191EC-ADFF-4E5D-A672-9CCB5ECE907D} -DAT 1514588679 -TIMESTAMP - - -7509D740-EA9D-4F60-B5F3-28F8B5AA79B5 -最后修改人( LastModifierId ) -LastModifierId -1514588679 -Administrator -1514588679 -Administrator -最后修改人 -ORG {4BAC5792-FF54-4C9B-AA55-32B63774DA44} -DAT 1514588679 -UUID - - -2484838C-F755-4593-AE5F-BD22BF28E21B -版本号( Version ) -Version -1514588679 -Administrator -1514588679 -Administrator -版本号 -ORG {1CBFD184-6982-4407-BEBA-A169BD9AEADF} -DAT 1514588679 -BYTEA - - - - -2423B547-4B05-46B0-A8FD-56F8C976C573 -Key_1 -Key_1 -1514588679 -Administrator -1514588679 -Administrator -ORG {9AB7B83B-729A-4865-8A84-1A8038CA22D5} -DAT 1514588679 - - - - - - - - - - - - - - - -B762A082-F574-4F2E-B192-7010761FC449 -Reference_1 -Reference_1 -1514588679 -Administrator -1514588679 -Administrator -ORG {E41C4795-1F29-4BDA-ACAF-614C9A3FB6A0} -DAT 1514588679 -0..* - - - - - - - - - - - -FC81CB6C-11ED-46B8-BC95-5BF6B50558F4 -1514588679 -Administrator -1514588679 -Administrator -ORG {AE653EE6-E7FE-4276-BCFE-3F49F078877C} -DAT 1514588679 - - - - - - - - - - -F4577E79-697C-4C8F-A8C3-4CDF4C680BF8 -Reference_2 -Reference_2 -1514588679 -Administrator -1514588679 -Administrator -ORG {76ABB6B3-CE4D-496B-B6EA-8563C5C6E8BA} -DAT 1514588679 -0..* - - - - - - - - - - - -F1267C6F-D3FF-436E-8982-930A6A20A6C3 -1514588679 -Administrator -1514588679 -Administrator -ORG {5900BB99-C0D5-4638-B555-26BC4FCB28A9} -DAT 1514588679 - - - - - - - - - - - - -D4C5C18B-12BB-44F2-81F7-FC9E92CEEA2C -PUBLIC -PUBLIC -1514588676 -Administrator -1514588676 -Administrator -ORG {AFA9EDB8-8EE4-4FD5-8275-60A5D6D96228} -DAT 1514588679 - - - - - - - - - -1BCA575B-7AC1-478E-91D3-AC40C793F1D4 -PostgreSQL 9.x -PGSQL9 -1514588676 -Administrator -1514588676 -Administrator -file:///%_DBMS%/pgsql9.xdb -83063C86-4E0A-4954-BF00-620C0D0F02D8 -4BA9F647-DAB1-11D1-9944-006097355D9B -1514588659 - - - - - -3D934EBA-A4CE-47AA-B707-D0A6033BAA36 -Samples -Samples -1514588680 -Administrator -1514588725 -Administrator -file:///D|/Github/Util/test/Util.Datas.Tests.Integration/SqlServer/Scripts/Pd/Samples.pdm -91AD3F79-C708-47A6-81A2-7B9E077A7DF4 -CDE44E21-9669-11D1-9914-006097355D9B -1514588729 - - - - - - - - - - \ No newline at end of file diff --git a/test/Util.Datas.Tests.Integration/PgSql/Scripts/database.sql b/test/Util.Datas.Tests.Integration/PgSql/Scripts/database.sql deleted file mode 100644 index 7f76a0d2a..000000000 --- a/test/Util.Datas.Tests.Integration/PgSql/Scripts/database.sql +++ /dev/null @@ -1,80 +0,0 @@ -/*========================================================== 1. 创建数据库 ===========================================================*/ ---删除数据库 - ---创建数据库 ---create database "UtilTest"; - -/*========================================================== 2. 创建架构 ===========================================================*/ -/* 1. Sales */ -create schema "Sales"; - -/* 2. Productions */ -create schema "Productions"; - -/* 3. Customers */ -create schema "Customers"; - - -/*========================================================== 3. 创建表 ===========================================================*/ - -/*==============================================================*/ -/* Table: Customers */ -/*==============================================================*/ -create table "Customers"."Customers" ( - "CustomerId" VARCHAR(50) not null, - "Name" VARCHAR(20) not null, - "Nickname" VARCHAR(30) null, - "Balance" DECIMAL(18,2) not null, - "Gender" INT4 null, - "Tel" VARCHAR(20) null, - "Mobile" VARCHAR(20) null, - "Email" VARCHAR(100) null, - "CreationTime" TIMESTAMP null, - "CreatorId" UUID null, - "LastModificationTime" TIMESTAMP null, - "LastModifierId" UUID null, - "Version" BYTEA null, - constraint PK_CUSTOMERS primary key ("CustomerId") -); - -comment on table "Customers"."Customers" is -'客户'; - -comment on column "Customers"."Customers"."CustomerId" is -'客户编号'; - -comment on column "Customers"."Customers"."Name" is -'客户名称'; - -comment on column "Customers"."Customers"."Nickname" is -'昵称'; - -comment on column "Customers"."Customers"."Balance" is -'余额'; - -comment on column "Customers"."Customers"."Gender" is -'性别'; - -comment on column "Customers"."Customers"."Tel" is -'联系电话'; - -comment on column "Customers"."Customers"."Mobile" is -'手机号'; - -comment on column "Customers"."Customers"."Email" is -'电子邮件'; - -comment on column "Customers"."Customers"."CreationTime" is -'创建时间'; - -comment on column "Customers"."Customers"."CreatorId" is -'创建人'; - -comment on column "Customers"."Customers"."LastModificationTime" is -'最后修改时间'; - -comment on column "Customers"."Customers"."LastModifierId" is -'最后修改人'; - -comment on column "Customers"."Customers"."Version" is -'版本号'; diff --git a/test/Util.Datas.Tests.Integration/PgSql/Tests/CustomerRepositoryTest.cs b/test/Util.Datas.Tests.Integration/PgSql/Tests/CustomerRepositoryTest.cs deleted file mode 100644 index 897c77727..000000000 --- a/test/Util.Datas.Tests.Integration/PgSql/Tests/CustomerRepositoryTest.cs +++ /dev/null @@ -1,335 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Util.Datas.Ef; -using Util.Datas.Queries; -using Util.Datas.Tests.PgSql.Configs; -using Util.Datas.Tests.Samples.Datas.Criterias; -using Util.Datas.Tests.Samples.Datas.PgSql.UnitOfWorks; -using Util.Datas.Tests.Samples.Domains.Models; -using Util.Datas.Tests.Samples.Domains.Repositories; -using Util.Dependency; -using Util.Helpers; -using Xunit; - -namespace Util.Datas.Tests.PgSql.Tests { - /// - /// 客户仓储测试 - 主要测试审计和查询操作 - /// - [Collection( "PgSqlGlobalConfig" )] - public class CustomerRepositoryTest : IDisposable { - /// - /// 容器作用域 - /// - private readonly IScope _scope; - - /// - /// 工作单元 - /// - private readonly IPgSqlUnitOfWork _unitOfWork; - - /// - /// 客户仓储 - /// - private readonly ICustomerRepository _customerRepository; - - /// - /// 测试初始化 - /// - public CustomerRepositoryTest() { - _scope = AppConfig.Container.BeginScope(); - _unitOfWork = _scope.Create(); - _customerRepository = _scope.Create(); - } - - /// - /// 测试清理 - /// - public void Dispose() { - _scope.Dispose(); - } - - /// - /// 添加客户集合 - /// - private List AddCustomers() { - var customers = Customer.CreateCustomers(); - _customerRepository.Add( customers ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - return customers; - } - - /// - /// 测试添加 - 测试创建操作审计 - /// - [Fact] - public void TestAdd() { - string id = Id.ObjectId(); - var customer = new Customer( id ) {Name = "util"}; - _customerRepository.Add( customer ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - var result = _customerRepository.Single( t => t.Id == id ); - Assert.NotNull( result.CreationTime ); - Assert.NotNull( result.LastModificationTime ); - Assert.Equal( AppConfig.UserId.ToGuidOrNull(), result.CreatorId ); - Assert.Equal( AppConfig.UserId.ToGuidOrNull(), result.LastModifierId ); - } - - /// - /// 测试修改 - 测试修改操作审计 - /// - [Fact] - public void TestUpdate() { - string id = Id.ObjectId(); - var customer = new Customer( id ) {Name = "util"}; - _customerRepository.Add( customer ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - var result = _customerRepository.Single( t => t.Id == id ); - var creationTime = result.CreationTime; - var lastModificationTime = result.LastModificationTime; - Assert.Equal( "util", result.Name ); - result.Name = "utilcore"; - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - result = _customerRepository.Single( t => t.Id == id ); - Assert.Equal( "utilcore", result.Name ); - Assert.Equal( creationTime, result.CreationTime ); - Assert.NotEqual( lastModificationTime, result.LastModificationTime ); - Assert.Equal( AppConfig.UserId.ToGuidOrNull(), result.CreatorId ); - Assert.Equal( AppConfig.UserId.ToGuidOrNull(), result.LastModifierId ); - } - - /// - /// 测试查询 - 通过查询条件对象过滤 - /// - [Fact] - public void TestFind_Criteria() { - AddCustomers(); - var result = _customerRepository.Find( new CustomerCriteria( "B", "B1" ) ).FirstOrDefault(); - Assert.Equal( "3", result.Mobile ); - } - - /// - /// 测试查询数量 - /// - [Fact] - public void TestCount() { - var customers = AddCustomers(); - var ids = customers.Select( t => t.Id ).ToList(); - Assert.Equal( customers.Count, _customerRepository.Count( t => ids.Contains( t.Id ) ) ); - } - - /// - /// 测试异步查询数量 - /// - [Fact] - public async Task TestCountAsync() { - var customers = AddCustomers(); - var ids = customers.Select( t => t.Id ).ToList(); - Assert.Equal( customers.Count, await _customerRepository.CountAsync( t => ids.Contains( t.Id ) ) ); - } - - /// - /// 测试查询 - 通过查询对象过滤 - /// - [Fact] - public void TestQuery() { - AddCustomers(); - var query = new Query(); - query.Where( t => t.Name == "B" && t.Nickname == "B1" ); - var result = _customerRepository.Query( query ); - Assert.Equal( "3", result.First().Mobile ); - } - - /// - /// 测试异步查询 - 通过查询对象过滤 - /// - [Fact] - public async Task TestQueryAsync() { - AddCustomers(); - var query = new Query(); - query.Where( t => t.Name == "B" && t.Nickname == "B1" ); - var result = await _customerRepository.QueryAsync( query ); - Assert.Equal( "3", result.First().Mobile ); - } - - /// - /// 测试查询 - 通过查询对象过滤,获取未跟踪实体 - /// - [Fact] - public void TestQueryAsNoTracking() { - AddCustomers(); - var query = new Query(); - query.Where( t => t.Name == "B" && t.Nickname == "B1" ); - var entity = _customerRepository.QueryAsNoTracking( query ).First(); - Assert.Equal( "3", entity.Mobile ); - - var id = entity.Id; - entity.Name = "abc"; - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - Assert.False( _customerRepository.Exists( t => t.Id == id && t.Name == "abc" ) ); - } - - /// - /// 测试查询 - 通过查询对象过滤,获取未跟踪实体 - /// - [Fact] - public async Task TestQueryAsNoTrackingAsync() { - AddCustomers(); - var query = new Query(); - query.Where( t => t.Name == "B" && t.Nickname == "B1" ); - var result = await _customerRepository.QueryAsNoTrackingAsync( query ); - var entity = result.First(); - Assert.Equal( "3", entity.Mobile ); - - var id = entity.Id; - entity.Name = "abc"; - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - Assert.False( await _customerRepository.ExistsAsync( t => t.Id == id && t.Name == "abc" ) ); - } - - /// - /// 测试分页查询 - /// - [Fact] - public void TestPagerQuery() { - var ids = AddCustomers().Select( t => t.Id ).ToList(); - QueryParameter parameter = new QueryParameter {PageSize = 2, Page = 2}; - var query = new Query( parameter ); - query.Where( t => ids.Contains( t.Id ) ); - var result = _customerRepository.PagerQuery( query ); - Assert.Equal( 5, result.TotalCount ); - Assert.Equal( 3, result.PageCount ); - Assert.Equal( 2, result.Data.Count ); - } - - /// - /// 测试异步分页查询 - /// - [Fact] - public async Task TestPagerQueryAsync() { - var ids = AddCustomers().Select( t => t.Id ).ToList(); - QueryParameter parameter = new QueryParameter {PageSize = 2, Page = 2}; - var query = new Query( parameter ); - query.Where( t => ids.Contains( t.Id ) ); - var result = await _customerRepository.PagerQueryAsync( query ); - Assert.Equal( 5, result.TotalCount ); - Assert.Equal( 3, result.PageCount ); - Assert.Equal( 2, result.Data.Count ); - } - - /// - /// 测试查询 - 选择性添加条件 - /// - [Fact] - public void TestWhereIf() { - var ids = AddCustomers().Select( t => t.Id ).ToList(); - Assert.Single( _customerRepository.Find().WhereIf( t => t.Id == ids[0], true ).ToList() ); - Assert.Equal( ids.Count, - _customerRepository.Find().Where( t => ids.Contains( t.Id ) ).WhereIf( t => t.Name == "C", false ) - .ToList().Count ); - } - - /// - /// 测试查询 - 选择性添加条件 - /// - [Fact] - public void TestWhereIf_2() { - var ids = AddCustomers().Select( t => t.Id ).ToList(); - - var query = new Query(); - query.WhereIf( t => t.Id == ids[0], true ); - Assert.Single( _customerRepository.Find( query ).ToList() ); - - query = new Query(); - query.Where( t => ids.Contains( t.Id ) ); - query.WhereIf( t => ids.Contains( t.Id ), false ); - Assert.Equal( ids.Count, _customerRepository.Find( query ).ToList().Count ); - } - - /// - /// 测试查询 - 添加条件 - 自动判断空值 - /// - [Fact] - public void TestWhereIfNotEmpty() { - var ids = AddCustomers().Select( t => t.Id ).ToList(); - Assert.NotEmpty( _customerRepository.Find().Where( t => ids.Contains( t.Id ) ) - .WhereIfNotEmpty( t => t.Name == "" ).ToList() ); - Assert.Single( _customerRepository.Find().Where( t => ids.Contains( t.Id ) ) - .WhereIfNotEmpty( t => t.Name == "C" ).ToList() ); - } - - /// - /// 测试查询 - 添加条件 - 自动判断空值 - /// - [Fact] - public void TestWhereIfNotEmpty_2() { - var ids = AddCustomers().Select( t => t.Id ).ToList(); - - var query = new Query(); - query.Where( t => ids.Contains( t.Id ) ); - query.WhereIfNotEmpty( t => t.Name == "" ); - Assert.NotEmpty( _customerRepository.Find( query ).ToList() ); - - query = new Query(); - query.Where( t => ids.Contains( t.Id ) ); - query.WhereIfNotEmpty( t => t.Name == "C" ); - Assert.Single( _customerRepository.Find( query ).ToList() ); - } - - /// - /// 测试删除 - /// - [Fact] - public void TestRemove() { - string id = Id.ObjectId(); - var customer = new Customer( id ) { Name = "util" }; - _customerRepository.Add( customer ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - var result = _customerRepository.Single( t => t.Id == id ); - Assert.NotNull( result ); - - _customerRepository.Remove( id ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - result = _customerRepository.Single( t => t.Id == id ); - Assert.Null( result ); - } - - /// - /// 测试删除 - /// - [Fact] - public async Task TestRemoveAsync() { - string id = Id.ObjectId(); - var customer = new Customer( id ) { Name = "util" }; - await _customerRepository.AddAsync( customer ); - await _unitOfWork.CommitAsync(); - _unitOfWork.ClearCache(); - - var result = await _customerRepository.SingleAsync( t => t.Id == id ); - Assert.NotNull( result ); - - await _customerRepository.RemoveAsync( id ); - await _unitOfWork.CommitAsync(); - _unitOfWork.ClearCache(); - - result = await _customerRepository.SingleAsync( t => t.Id == id ); - Assert.Null( result ); - } - } -} diff --git a/test/Util.Datas.Tests.Integration/Samples/Datas/Criterias/CustomerCriteria.cs b/test/Util.Datas.Tests.Integration/Samples/Datas/Criterias/CustomerCriteria.cs deleted file mode 100644 index eef3f4f30..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Datas/Criterias/CustomerCriteria.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Linq.Expressions; -using Util.Datas.Tests.Samples.Domains.Models; -using Util.Domains.Repositories; - -namespace Util.Datas.Tests.Samples.Datas.Criterias { - /// - /// 客户查询条件 - /// - public class CustomerCriteria : ICriteria { - /// - /// 姓名 - /// - private readonly string _name; - /// - /// 昵称 - /// - private readonly string _nickname; - - /// - /// 初始化客户查询条件 - /// - /// 姓名 - /// 昵称 - public CustomerCriteria( string name, string nickname ) { - _name = name; - _nickname = nickname; - } - - /// - /// 获取过滤条件 - /// - public Expression> GetPredicate() { - return customer => customer.Name == _name && customer.Nickname == _nickname; - } - } -} diff --git a/test/Util.Datas.Tests.Integration/Samples/Datas/PgSql/Mappings/CustomerMap.cs b/test/Util.Datas.Tests.Integration/Samples/Datas/PgSql/Mappings/CustomerMap.cs deleted file mode 100644 index 8032099af..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Datas/PgSql/Mappings/CustomerMap.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Util.Datas.Tests.Samples.Domains.Models; - -namespace Util.Datas.Tests.Samples.Datas.PgSql.Mappings { - /// - /// 客户映射配置 - /// - public class CustomerMap : Util.Datas.Ef.PgSql.AggregateRootMap { - /// - /// 映射表 - /// - protected override void MapTable( EntityTypeBuilder builder ) { - builder.ToTable( "Customers", "Customers" ); - } - - /// - /// 映射属性 - /// - protected override void MapProperties( EntityTypeBuilder builder ) { - base.MapProperties( builder ); - //客户编号 - builder.Property( t => t.Id ) - .HasColumnName( "CustomerId" ); - } - } -} diff --git a/test/Util.Datas.Tests.Integration/Samples/Datas/PgSql/Repositories/CustomerRepository.cs b/test/Util.Datas.Tests.Integration/Samples/Datas/PgSql/Repositories/CustomerRepository.cs deleted file mode 100644 index 218436345..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Datas/PgSql/Repositories/CustomerRepository.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Util.Datas.Ef.Core; -using Util.Datas.Tests.Samples.Datas.PgSql.UnitOfWorks; -using Util.Datas.Tests.Samples.Domains.Models; -using Util.Datas.Tests.Samples.Domains.Repositories; - -namespace Util.Datas.Tests.Samples.Datas.PgSql.Repositories { - /// - /// 客户仓储 - /// - public class CustomerRepository : RepositoryBase, ICustomerRepository { - /// - /// 初始化客户仓储 - /// - /// 工作单元 - public CustomerRepository( IPgSqlUnitOfWork unitOfWork ) : base( unitOfWork ) { - } - } -} diff --git a/test/Util.Datas.Tests.Integration/Samples/Datas/PgSql/UnitOfWorks/IPgSqlUnitOfWork.cs b/test/Util.Datas.Tests.Integration/Samples/Datas/PgSql/UnitOfWorks/IPgSqlUnitOfWork.cs deleted file mode 100644 index 89d7e6c22..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Datas/PgSql/UnitOfWorks/IPgSqlUnitOfWork.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Util.Datas.UnitOfWorks; - -namespace Util.Datas.Tests.Samples.Datas.PgSql.UnitOfWorks { - /// - /// PgSql工作单元 - /// - public interface IPgSqlUnitOfWork : IUnitOfWork { - } -} \ No newline at end of file diff --git a/test/Util.Datas.Tests.Integration/Samples/Datas/PgSql/UnitOfWorks/PgSqlUnitOfWork.cs b/test/Util.Datas.Tests.Integration/Samples/Datas/PgSql/UnitOfWorks/PgSqlUnitOfWork.cs deleted file mode 100644 index 6bc8554f8..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Datas/PgSql/UnitOfWorks/PgSqlUnitOfWork.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Util.Datas.Tests.PgSql.Configs; -using Util.Datas.UnitOfWorks; - -namespace Util.Datas.Tests.Samples.Datas.PgSql.UnitOfWorks { - /// - /// PgSql工作单元 - /// - public class PgSqlUnitOfWork : Util.Datas.Ef.PgSql.UnitOfWork, IPgSqlUnitOfWork { - /// - /// 初始化PgSql工作单元 - /// - public PgSqlUnitOfWork( IUnitOfWorkManager unitOfWorkManager) : base( new DbContextOptionsBuilder().UseNpgsql( AppConfig.Connection ).Options, unitOfWorkManager ) { - } - } -} \ No newline at end of file diff --git a/test/Util.Datas.Tests.Integration/Samples/Datas/Pos/Extension.ProductPo.cs b/test/Util.Datas.Tests.Integration/Samples/Datas/Pos/Extension.ProductPo.cs deleted file mode 100644 index 1d8a5c6c9..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Datas/Pos/Extension.ProductPo.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Util.Datas.Tests.Samples.Domains.Models; -using Util.Maps; - -namespace Util.Datas.Tests.Samples.Datas.Pos { - /// - /// 商品持久化对象扩展 - /// - public static partial class Extension { - /// - /// 转换为商品实体 - /// - /// 商品持久化对象 - public static Product ToEntity( this ProductPo po ) { - if( po == null ) - return null; - var entity = po.MapTo( new Product( po.Id ) ); - entity.ProductType = Util.Helpers.Json.ToObject( po.Extends ); - return entity; - } - - /// - /// 转换为商品持久化对象 - /// - /// 商品实体 - public static ProductPo ToPo( this Product entity ) { - if( entity == null ) - return null; - var po = entity.MapTo(); - po.Extends = Util.Helpers.Json.ToJson( entity.ProductType ); - return po; - } - } -} diff --git a/test/Util.Datas.Tests.Integration/Samples/Datas/Pos/ProductPo.cs b/test/Util.Datas.Tests.Integration/Samples/Datas/Pos/ProductPo.cs deleted file mode 100644 index 5466a8244..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Datas/Pos/ProductPo.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Util.Datas.Persistence; -using Util.Domains; - -namespace Util.Datas.Tests.Samples.Datas.Pos { - /// - /// 商品持久化对象 - /// - public class ProductPo : PersistentObjectBase, IDelete { - /// - /// 商品编码 - /// - public string Code { get; set; } - /// - /// 商品名称 - /// - public string Name { get; set; } - /// - /// 扩展属性 - /// - public string Extends { get; set; } - /// - /// 价格 - /// - public decimal? Price { get; set; } - /// - /// 是否删除 - /// - public bool IsDeleted { get; set; } - } -} \ No newline at end of file diff --git a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Mappings/CustomerMap.cs b/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Mappings/CustomerMap.cs deleted file mode 100644 index f1183959c..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Mappings/CustomerMap.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Util.Datas.Tests.Samples.Domains.Models; - -namespace Util.Datas.Tests.Samples.Datas.SqlServer.Mappings { - /// - /// 客户映射配置 - /// - public class CustomerMap : Util.Datas.Ef.SqlServer.AggregateRootMap { - /// - /// 映射表 - /// - protected override void MapTable( EntityTypeBuilder builder ) { - builder.ToTable( "Customers", "Customers" ); - } - - /// - /// 映射属性 - /// - protected override void MapProperties( EntityTypeBuilder builder ) { - base.MapProperties( builder ); - //客户编号 - builder.Property( t => t.Id ) - .HasColumnName( "CustomerId" ); - } - } -} diff --git a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Mappings/OrderItemMap.cs b/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Mappings/OrderItemMap.cs deleted file mode 100644 index af78cb42d..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Mappings/OrderItemMap.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Util.Datas.Tests.Samples.Domains.Models; - -namespace Util.Datas.Tests.Samples.Datas.SqlServer.Mappings { - /// - /// 订单明细映射配置 - /// - public class OrderItemMap : Util.Datas.Ef.SqlServer.EntityMap { - /// - /// 映射表 - /// - protected override void MapTable( EntityTypeBuilder builder ) { - builder.ToTable( "OrderItems", "Sales" ); - } - - /// - /// 映射属性 - /// - protected override void MapProperties( EntityTypeBuilder builder ) { - base.MapProperties( builder ); - //订单明细编号 - builder.Property( t => t.Id ) - .HasColumnName( "OrderItemId" ); - } - } -} diff --git a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Mappings/OrderMap.cs b/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Mappings/OrderMap.cs deleted file mode 100644 index 94b939e1c..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Mappings/OrderMap.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Util.Datas.Tests.Samples.Domains.Models; - -namespace Util.Datas.Tests.Samples.Datas.SqlServer.Mappings { - /// - /// 订单映射配置 - /// - public class OrderMap : Util.Datas.Ef.SqlServer.AggregateRootMap { - /// - /// 映射表 - /// - protected override void MapTable( EntityTypeBuilder builder ) { - builder.ToTable( "Orders", "Sales" ); - } - - /// - /// 映射属性 - /// - protected override void MapProperties( EntityTypeBuilder builder ) { - base.MapProperties( builder ); - //订单编号 - builder.Property( t => t.Id ) - .HasColumnName( "OrderId" ); - } - } -} diff --git a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Mappings/ProductPoMap.cs b/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Mappings/ProductPoMap.cs deleted file mode 100644 index 9a9cf1354..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Mappings/ProductPoMap.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Util.Datas.Tests.Samples.Datas.Pos; - -namespace Util.Datas.Tests.Samples.Datas.SqlServer.Mappings { - /// - /// 商品持久化对象映射配置 - /// - public class ProductPoMap : Util.Datas.Ef.SqlServer.AggregateRootMap { - /// - /// 映射表 - /// - protected override void MapTable( EntityTypeBuilder builder ) { - builder.ToTable( "Products", "Productions" ); - } - - /// - /// 映射属性 - /// - protected override void MapProperties( EntityTypeBuilder builder ) { - base.MapProperties( builder ); - //商品编号 - builder.Property( t => t.Id ) - .HasColumnName( "ProductId" ); - } - } -} diff --git a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Repositories/CustomerRepository.cs b/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Repositories/CustomerRepository.cs deleted file mode 100644 index 4e55ffcd4..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Repositories/CustomerRepository.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Util.Datas.Ef.Core; -using Util.Datas.Tests.Samples.Datas.SqlServer.UnitOfWorks; -using Util.Datas.Tests.Samples.Domains.Models; -using Util.Datas.Tests.Samples.Domains.Repositories; - -namespace Util.Datas.Tests.Samples.Datas.SqlServer.Repositories { - /// - /// 客户仓储 - /// - public class CustomerRepository : RepositoryBase, ICustomerRepository { - /// - /// 初始化客户仓储 - /// - /// 工作单元 - public CustomerRepository( ISqlServerUnitOfWork unitOfWork ) : base( unitOfWork ) { - } - } -} diff --git a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Repositories/OrderRepository.cs b/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Repositories/OrderRepository.cs deleted file mode 100644 index b2e71c7ca..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Repositories/OrderRepository.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Util.Datas.Ef.Core; -using Util.Datas.Tests.Samples.Datas.SqlServer.UnitOfWorks; -using Util.Datas.Tests.Samples.Domains.Models; -using Util.Datas.Tests.Samples.Domains.Repositories; - -namespace Util.Datas.Tests.Samples.Datas.SqlServer.Repositories { - /// - /// 订单仓储 - /// - public class OrderRepository : RepositoryBase, IOrderRepository { - /// - /// 初始化订单仓储 - /// - /// 工作单元 - public OrderRepository( ISqlServerUnitOfWork unitOfWork ) : base( unitOfWork ) { - } - } -} diff --git a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Repositories/ProductRepository.cs b/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Repositories/ProductRepository.cs deleted file mode 100644 index 631517df1..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Repositories/ProductRepository.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Util.Datas.Ef.Core; -using Util.Datas.Tests.Samples.Datas.Pos; -using Util.Datas.Tests.Samples.Datas.SqlServer.Stores; -using Util.Datas.Tests.Samples.Domains.Models; -using Util.Datas.Tests.Samples.Domains.Repositories; - -namespace Util.Datas.Tests.Samples.Datas.SqlServer.Repositories { - /// - /// 商品仓储 - /// - public class ProductRepository : PersistentRepositoryBase, IProductRepository { - /// - /// 商品持久化存储 - /// - private readonly IProductPoStore _store; - - /// - /// 初始化商品仓储 - /// - /// 商品持久化存储 - public ProductRepository( IProductPoStore store ) : base( store ) { - _store = store; - } - - /// - /// 将持久化对象转成实体 - /// - /// 持久化对象 - protected override Product ToEntity( ProductPo po ) { - return po.ToEntity(); - } - - /// - /// 将实体转成持久化对象 - /// - /// 实体 - protected override ProductPo ToPo( Product entity ) { - return entity.ToPo(); - } - - /// - /// 通过编号获取商品 - /// - /// 商品编号 - public Product GetById( int id ) { - return ToEntity( _store.Single( t => t.Id == id ) ); - } - } -} diff --git a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Stores/IProductPoStore.cs b/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Stores/IProductPoStore.cs deleted file mode 100644 index 9734810a5..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Stores/IProductPoStore.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Util.Datas.Persistence; -using Util.Datas.Tests.Samples.Datas.Pos; - -namespace Util.Datas.Tests.Samples.Datas.SqlServer.Stores { - /// - /// 商品持久化存储 - /// - public interface IProductPoStore : IPersistentStore { - } -} diff --git a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Stores/ProductPoStore.cs b/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Stores/ProductPoStore.cs deleted file mode 100644 index 28d160ddc..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/Stores/ProductPoStore.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Util.Datas.Ef.Core; -using Util.Datas.Tests.Samples.Datas.Pos; -using Util.Datas.Tests.Samples.Datas.SqlServer.UnitOfWorks; - -namespace Util.Datas.Tests.Samples.Datas.SqlServer.Stores { - /// - /// 商品持久化存储 - /// - public class ProductPoStore : PersistentStoreBase, IProductPoStore { - /// - /// 初始化商品持久化存储 - /// - /// 工作单元 - public ProductPoStore( ISqlServerUnitOfWork unitOfWork ) : base( unitOfWork ) { - } - } -} diff --git a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/UnitOfWorks/ISqlServerUnitOfWork.cs b/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/UnitOfWorks/ISqlServerUnitOfWork.cs deleted file mode 100644 index 30e94dcfe..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/UnitOfWorks/ISqlServerUnitOfWork.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Util.Datas.UnitOfWorks; - -namespace Util.Datas.Tests.Samples.Datas.SqlServer.UnitOfWorks { - /// - /// SqlServer工作单元 - /// - public interface ISqlServerUnitOfWork : IUnitOfWork { - } -} \ No newline at end of file diff --git a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/UnitOfWorks/SqlServerUnitOfWork.cs b/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/UnitOfWorks/SqlServerUnitOfWork.cs deleted file mode 100644 index bb066843d..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Datas/SqlServer/UnitOfWorks/SqlServerUnitOfWork.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Util.Datas.Tests.SqlServer.Configs; -using Util.Datas.UnitOfWorks; - -namespace Util.Datas.Tests.Samples.Datas.SqlServer.UnitOfWorks { - /// - /// SqlServer工作单元 - /// - public class SqlServerUnitOfWork : Util.Datas.Ef.SqlServer.UnitOfWork, ISqlServerUnitOfWork { - /// - /// 初始化SqlServer工作单元 - /// - public SqlServerUnitOfWork(IUnitOfWorkManager unitOfWorkManager) : base( new DbContextOptionsBuilder().UseSqlServer( AppConfig.Connection ).Options, unitOfWorkManager ) { - } - } - - /// - /// SqlServer工作单元 - /// - public class SqlServerUnitOfWork2 : Util.Datas.Ef.SqlServer.UnitOfWork, ISqlServerUnitOfWork { - /// - /// 初始化 - /// - /// 配置项 - /// 工作单元服务 - public SqlServerUnitOfWork2( DbContextOptions options, IUnitOfWorkManager unitOfWorkManager ) : base( options, unitOfWorkManager ) { - } - } -} \ No newline at end of file diff --git a/test/Util.Datas.Tests.Integration/Samples/Domains/Models/Customer.cs b/test/Util.Datas.Tests.Integration/Samples/Domains/Models/Customer.cs deleted file mode 100644 index fed89ec0b..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Domains/Models/Customer.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using Util.Domains; -using Util.Domains.Auditing; - -namespace Util.Datas.Tests.Samples.Domains.Models { - /// - /// 客户 - 字符串类型标识 - /// - [DisplayName( "客户" )] - public class Customer : AggregateRoot, IAudited { - /// - /// 初始化客户 - /// - public Customer() : this( string.Empty ) { - } - - /// - /// 初始化客户 - /// - /// 客户标识 - public Customer( string id ) : base( id ) { - } - - /// - /// 客户名称 - /// - [DisplayName( "客户名称" )] - [Required( ErrorMessage = "客户名称不能为空" )] - [StringLength( 20, ErrorMessage = "客户名称输入过长,不能超过20位" )] - public string Name { get; set; } - /// - /// 昵称 - /// - [DisplayName( "昵称" )] - [StringLength( 30, ErrorMessage = "昵称输入过长,不能超过30位" )] - public string Nickname { get; set; } - /// - /// 余额 - /// - [DisplayName( "余额" )] - [Required( ErrorMessage = "余额不能为空" )] - public decimal Balance { get; set; } - /// - /// 性别 - /// - [DisplayName( "性别" )] - public int? Gender { get; set; } - /// - /// 联系电话 - /// - [DisplayName( "联系电话" )] - [StringLength( 20, ErrorMessage = "联系电话输入过长,不能超过20位" )] - public string Tel { get; set; } - /// - /// 手机号 - /// - [DisplayName( "手机号" )] - [StringLength( 20, ErrorMessage = "手机号输入过长,不能超过20位" )] - public string Mobile { get; set; } - /// - /// 电子邮件 - /// - [DisplayName( "电子邮件" )] - [StringLength( 100, ErrorMessage = "电子邮件输入过长,不能超过100位" )] - public string Email { get; set; } - /// - /// 创建时间 - /// - [DisplayName( "创建时间" )] - public DateTime? CreationTime { get; set; } - /// - /// 创建人 - /// - [DisplayName( "创建人" )] - public Guid? CreatorId { get; set; } - /// - /// 最后修改时间 - /// - [DisplayName( "最后修改时间" )] - public DateTime? LastModificationTime { get; set; } - /// - /// 最后修改人 - /// - [DisplayName( "最后修改人" )] - public Guid? LastModifierId { get; set; } - - /// - /// 添加描述 - /// - protected override void AddDescriptions() { - AddDescription( "客户编号", Id ); - AddDescription( "客户名称", Name ); - AddDescription( "昵称", Nickname ); - AddDescription( "余额", Balance ); - AddDescription( "性别", Gender ); - AddDescription( "联系电话", Tel ); - AddDescription( "手机号", Mobile ); - AddDescription( "电子邮件", Email ); - AddDescription( "创建时间", CreationTime ); - AddDescription( "创建人", CreatorId ); - AddDescription( "最后修改时间", LastModificationTime ); - AddDescription( "最后修改人", LastModifierId ); - } - - /// - /// 添加变更列表 - /// - protected override void AddChanges( Customer other ) { - AddChange( "Id", "客户编号", Id, other.Id ); - AddChange( "Name", "客户名称", Name, other.Name ); - AddChange( "Nickname", "昵称", Nickname, other.Nickname ); - AddChange( "Balance", "余额", Balance, other.Balance ); - AddChange( "Gender", "性别", Gender, other.Gender ); - AddChange( "Tel", "联系电话", Tel, other.Tel ); - AddChange( "Mobile", "手机号", Mobile, other.Mobile ); - AddChange( "Email", "电子邮件", Email, other.Email ); - AddChange( "CreationTime", "创建时间", CreationTime, other.CreationTime ); - AddChange( "CreatorId", "创建人", CreatorId, other.CreatorId ); - AddChange( "LastModificationTime", "最后修改时间", LastModificationTime, other.LastModificationTime ); - AddChange( "LastModifierId", "最后修改人", LastModifierId, other.LastModifierId ); - } - - /// - /// 添加客户集合 - /// - public static List CreateCustomers() { - return new List { - new Customer( Util.Helpers.Id.ObjectId() ) {Name = "A",Nickname = "A1",Mobile = "1"}, - new Customer( Util.Helpers.Id.ObjectId() ) {Name = "A",Nickname = "A2",Mobile = "2"}, - new Customer( Util.Helpers.Id.ObjectId() ) {Name = "B",Nickname = "B1",Mobile = "3"}, - new Customer( Util.Helpers.Id.ObjectId() ) {Name = "B",Nickname = "B2",Mobile = "4"}, - new Customer( Util.Helpers.Id.ObjectId() ) {Name = "C",Mobile = "5"} - }; - } - } -} \ No newline at end of file diff --git a/test/Util.Datas.Tests.Integration/Samples/Domains/Models/Order.cs b/test/Util.Datas.Tests.Integration/Samples/Domains/Models/Order.cs deleted file mode 100644 index ae9289197..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Domains/Models/Order.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using Util.Domains; - -namespace Util.Datas.Tests.Samples.Domains.Models { - /// - /// 订单 - /// - [Description( "订单" )] - public class Order : AggregateRoot { - /// - /// 订单明细列表 - /// - private readonly List _items; - - /// - /// 初始化订单 - /// - public Order() : this( Guid.Empty ) { - } - - /// - /// 初始化订单 - /// - /// 订单标识 - public Order( Guid id ) : base( id ) { - _items = new List(); - } - - /// - /// 订单编码 - /// - [Required( ErrorMessage = "订单编码不能为空" )] - [StringLength( 30, ErrorMessage = "订单编码输入过长,不能超过30位" )] - public string Code { get; set; } - /// - /// 订单名称 - /// - [Required( ErrorMessage = "订单名称不能为空" )] - [StringLength( 200, ErrorMessage = "订单名称输入过长,不能超过200位" )] - public string Name { get; set; } - - /// - /// 订单明细列表 - /// - public IReadOnlyCollection Items => _items; - - /// - /// 添加订单明细 - /// - /// 商品 - /// 数量 - public void AddItem( Product product, int quantity ) { - var item = new OrderItem( Guid.NewGuid(), this ); - item.Booking( product, quantity ); - _items.Add( item ); - } - - /// - /// 移除订单明细 - /// - /// 订单明细编号 - public void RemoveItem( Guid itemId ) { - _items.Remove( FindItem( itemId ) ); - } - - /// - /// 查找订单明细 - /// - /// 订单明细编号 - public OrderItem FindItem( Guid itemId ) { - return _items.Find( t => t.Id == itemId ); - } - } -} \ No newline at end of file diff --git a/test/Util.Datas.Tests.Integration/Samples/Domains/Models/OrderItem.cs b/test/Util.Datas.Tests.Integration/Samples/Domains/Models/OrderItem.cs deleted file mode 100644 index fc3b01243..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Domains/Models/OrderItem.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using Util.Domains; - -namespace Util.Datas.Tests.Samples.Domains.Models { - /// - /// 订单明细 - /// - [Description( "订单明细" )] - public class OrderItem : EntityBase { - /// - /// 初始化订单明细 - /// - public OrderItem() : this( Guid.Empty, null ) { - } - /// - /// 初始化订单明细 - /// - /// 订单 - public OrderItem( Order order ) : this( Guid.Empty, order ) { - } - - /// - /// 初始化订单明细 - /// - /// 订单明细标识 - /// 订单 - public OrderItem( Guid id,Order order ) : base( id ) { - if ( order == null ) - return; - Order = order; - OrderId = order.Id; - } - - /// - /// 商品名称 - /// - [DisplayName( "商品名称" )] - [Required( ErrorMessage = "商品名称不能为空" )] - [StringLength( 200, ErrorMessage = "商品名称输入过长,不能超过200位" )] - public string ProductName { get; private set; } - - /// - /// 数量 - /// - [DisplayName( "数量" )] - [Required( ErrorMessage = "数量不能为空" )] - public int Quantity { get; private set; } - - /// - /// 单价 - /// - [DisplayName( "单价" )] - [Required( ErrorMessage = "单价不能为空" )] - public decimal Price { get; private set; } - - /// - /// 商品编号 - /// - public int ProductId { get; private set; } - - /// - /// 订单编号 - /// - public Guid OrderId { get; private set; } - - /// - /// 订单 - /// - [ForeignKey( "OrderId" )] - public Order Order { get; private set; } - - /// - /// 订购 - /// - /// 商品 - /// 数量 - public void Booking( Product product, int quantity ) { - ProductId = product.Id; - ProductName = product.Name; - Price = product.Price.SafeValue(); - Quantity = quantity; - } - } -} \ No newline at end of file diff --git a/test/Util.Datas.Tests.Integration/Samples/Domains/Models/Product.cs b/test/Util.Datas.Tests.Integration/Samples/Domains/Models/Product.cs deleted file mode 100644 index 3d8194b8c..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Domains/Models/Product.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using Util.Domains; - -namespace Util.Datas.Tests.Samples.Domains.Models { - /// - /// 商品 - int标识 - /// - [Description( "商品" )] - public class Product : AggregateRoot, IDelete { - /// - /// 初始化商品 - /// - public Product() : this( 0 ) { - } - - /// - /// 初始化商品 - /// - /// 商品标识 - public Product( int id ) : base( id ) { - } - - /// - /// 商品编码 - /// - [Required( ErrorMessage = "商品编码不能为空" )] - [StringLength( 30, ErrorMessage = "商品编码输入过长,不能超过30位" )] - public string Code { get; set; } - /// - /// 商品名称 - /// - [Required( ErrorMessage = "商品名称不能为空" )] - [StringLength( 200, ErrorMessage = "商品名称输入过长,不能超过200位" )] - public string Name { get; set; } - /// - /// 商品类型 - /// - public ProductType ProductType { get; set; } - /// - /// 价格 - /// - [DisplayName( "价格" )] - public decimal? Price { get; set; } - /// - /// 是否删除 - /// - [Required( ErrorMessage = "是否删除不能为空" )] - public bool IsDeleted { get; set; } - - /// - /// 添加描述 - /// - protected override void AddDescriptions() { - AddDescription( "商品编号", Id ); - AddDescription( "商品编码", Code ); - AddDescription( "商品名称", Name ); - } - - /// - /// 添加变更列表 - /// - protected override void AddChanges( Product other ) { - AddChange( "Id", "商品编号", Id, other.Id ); - AddChange( "Code", "商品编码", Code, other.Code ); - AddChange( t=> t.Name, other.Name ); - } - } -} \ No newline at end of file diff --git a/test/Util.Datas.Tests.Integration/Samples/Domains/Models/ProductProperty.cs b/test/Util.Datas.Tests.Integration/Samples/Domains/Models/ProductProperty.cs deleted file mode 100644 index 6c1f80639..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Domains/Models/ProductProperty.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Util.Datas.Tests.Samples.Domains.Models { - /// - /// 商品属性 - /// - public class ProductProperty : Util.Domains.ValueObjectBase { - /// - /// 初始化商品属性 - /// - /// 属性名 - /// 属性值 - public ProductProperty( string name,string value ) { - Name = name; - Value = value; - } - - /// - /// 属性名 - /// - public string Name { get; } - /// - /// 属性值 - /// - public string Value { get; } - } -} diff --git a/test/Util.Datas.Tests.Integration/Samples/Domains/Models/ProductType.cs b/test/Util.Datas.Tests.Integration/Samples/Domains/Models/ProductType.cs deleted file mode 100644 index 44a0dbb27..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Domains/Models/ProductType.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Collections.Generic; - -namespace Util.Datas.Tests.Samples.Domains.Models { - /// - /// 商品类型 - /// - public class ProductType : Util.Domains.ValueObjectBase { - /// - /// 商品属性集合 - /// - private readonly List _properties; - - /// - /// 初始化商品类型 - /// - /// 商品类型名称 - /// 商品属性集合 - public ProductType( string name,List properties ) { - Name = name; - _properties = properties; - } - - /// - /// 商品类型名称 - /// - public string Name { get; } - - /// - /// 商品属性集合 - /// - public IReadOnlyCollection Properties => _properties?.AsReadOnly(); - } -} diff --git a/test/Util.Datas.Tests.Integration/Samples/Domains/Repositories/ICustomerRepository.cs b/test/Util.Datas.Tests.Integration/Samples/Domains/Repositories/ICustomerRepository.cs deleted file mode 100644 index ceff5031b..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Domains/Repositories/ICustomerRepository.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Util.Datas.Tests.Samples.Domains.Models; -using Util.Domains.Repositories; - -namespace Util.Datas.Tests.Samples.Domains.Repositories { - /// - /// 客户仓储 - /// - public interface ICustomerRepository : IRepository { - } -} diff --git a/test/Util.Datas.Tests.Integration/Samples/Domains/Repositories/IOrderRepository.cs b/test/Util.Datas.Tests.Integration/Samples/Domains/Repositories/IOrderRepository.cs deleted file mode 100644 index 0ce76ff27..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Domains/Repositories/IOrderRepository.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Util.Datas.Tests.Samples.Domains.Models; -using Util.Domains.Repositories; - -namespace Util.Datas.Tests.Samples.Domains.Repositories { - /// - /// 订单仓储 - /// - public interface IOrderRepository : IRepository { - } -} diff --git a/test/Util.Datas.Tests.Integration/Samples/Domains/Repositories/IProductRepository.cs b/test/Util.Datas.Tests.Integration/Samples/Domains/Repositories/IProductRepository.cs deleted file mode 100644 index cc6a30590..000000000 --- a/test/Util.Datas.Tests.Integration/Samples/Domains/Repositories/IProductRepository.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Util.Datas.Tests.Samples.Domains.Models; -using Util.Domains.Repositories; - -namespace Util.Datas.Tests.Samples.Domains.Repositories { - /// - /// 商品仓储 - /// - public interface IProductRepository : ICompactRepository { - /// - /// 通过编号获取商品 - 内部采用FirstOrDefault方法获取 - /// - /// 商品编号 - Product GetById( int id ); - } -} diff --git a/test/Util.Datas.Tests.Integration/SqlServer/Configs/AppConfig.cs b/test/Util.Datas.Tests.Integration/SqlServer/Configs/AppConfig.cs deleted file mode 100644 index 2f5a6ed6f..000000000 --- a/test/Util.Datas.Tests.Integration/SqlServer/Configs/AppConfig.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Util.Datas.Tests.SqlServer.Configs { - /// - /// 测试应用程序配置 - /// - public static class AppConfig { - /// - /// 连接字符串 - /// - public const string Connection = "Server=.\\sql2014;Database=UtilTest;uid=sa;pwd=sa;"; - /// - /// 当前用户编号 - /// - public const string UserId = "71B18059-7572-4EC3-8B11-2BAA0D0F6169"; - } -} diff --git a/test/Util.Datas.Tests.Integration/SqlServer/Configs/GlobalConfig.cs b/test/Util.Datas.Tests.Integration/SqlServer/Configs/GlobalConfig.cs deleted file mode 100644 index b1e9db553..000000000 --- a/test/Util.Datas.Tests.Integration/SqlServer/Configs/GlobalConfig.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Xunit; - -namespace Util.Datas.Tests.SqlServer.Configs { - /// - /// 全局测试配置 - /// - [CollectionDefinition( "GlobalConfig" )] - public class GlobalConfig : ICollectionFixture { - } -} diff --git a/test/Util.Datas.Tests.Integration/SqlServer/Configs/GlobalFixture.cs b/test/Util.Datas.Tests.Integration/SqlServer/Configs/GlobalFixture.cs deleted file mode 100644 index 06942f1bf..000000000 --- a/test/Util.Datas.Tests.Integration/SqlServer/Configs/GlobalFixture.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Util.Helpers; - -namespace Util.Datas.Tests.SqlServer.Configs { - /// - /// 全局测试配置 - /// - public class GlobalFixture { - /// - /// 测试初始化 - /// - public GlobalFixture() { - Ioc.Register( new IocConfig() ); - } - } -} diff --git a/test/Util.Datas.Tests.Integration/SqlServer/Configs/IocConfig.cs b/test/Util.Datas.Tests.Integration/SqlServer/Configs/IocConfig.cs deleted file mode 100644 index f71f55f98..000000000 --- a/test/Util.Datas.Tests.Integration/SqlServer/Configs/IocConfig.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Autofac; -using Util.Datas.Tests.Samples.Datas.SqlServer.Repositories; -using Util.Datas.Tests.Samples.Datas.SqlServer.Stores; -using Util.Datas.Tests.Samples.Datas.SqlServer.UnitOfWorks; -using Util.Datas.Tests.Samples.Domains.Repositories; -using Util.Datas.UnitOfWorks; -using Util.Dependency; -using Util.Domains.Sessions; - -namespace Util.Datas.Tests.SqlServer.Configs { - /// - /// 依赖注入配置 - /// - public class IocConfig : ConfigBase { - /// - /// 加载配置 - /// - protected override void Load( ContainerBuilder builder ) { - LoadInfrastructure( builder ); - LoadRepositories( builder ); - } - - /// - /// 加载基础设施 - /// - private void LoadInfrastructure( ContainerBuilder builder ) { - builder.AddScoped(); - builder.AddScoped().PropertiesAutowired(); - builder.AddScoped(); - builder.AddSingleton( new Session( AppConfig.UserId ) ); - } - - /// - /// 加载仓储 - /// - private void LoadRepositories( ContainerBuilder builder ) { - builder.AddScoped(); - builder.AddScoped(); - builder.AddScoped(); - } - } -} \ No newline at end of file diff --git a/test/Util.Datas.Tests.Integration/SqlServer/Scripts/Pd/Samples.pdb b/test/Util.Datas.Tests.Integration/SqlServer/Scripts/Pd/Samples.pdb deleted file mode 100644 index eb82d1fa7..000000000 --- a/test/Util.Datas.Tests.Integration/SqlServer/Scripts/Pd/Samples.pdb +++ /dev/null @@ -1,3357 +0,0 @@ - - - - - - - - - -91AD3F79-C708-47A6-81A2-7B9E077A7DF4 -Samples -Samples -1502368829 -Administrator -1503008669 -Administrator -[FolderOptions] - -[FolderOptions\Physical Objects] -GenerationCheckModel=Yes -GenerationPath= -GenerationOptions= -GenerationTasks= -GenerationTargets= -GenerationSelections= -RevPkey=Yes -RevFkey=Yes -RevAkey=Yes -RevCheck=Yes -RevIndx=Yes -RevOpts=Yes -RevViewAsTabl=No -RevViewOpts=Yes -RevSystAsTabl=Yes -RevTablPerm=No -RevViewPerm=No -RevProcPerm=No -RevDbpkPerm=No -RevSqncPerm=No -RevAdtPerm=No -RevUserPriv=No -RevUserOpts=No -RevGrpePriv=No -RevRolePriv=No -RevDtbsOpts=Yes -RevDtbsPerm=No -RevViewIndx=Yes -RevJidxOpts=Yes -RevStats=No -RevTspcPerm=No -RevCaseSensitive=No -GenTrgrStdMsg=Yes -GenTrgrMsgTab= -GenTrgrMsgNo= -GenTrgrMsgTxt= -TrgrPreserve=No -TrgrIns=Yes -TrgrUpd=Yes -TrgrDel=Yes -TrgrC2Ins=Yes -TrgrC2Upd=Yes -TrgrC3=Yes -TrgrC4=Yes -TrgrC5=Yes -TrgrC6=Yes -TrgrC7=Yes -TrgrC8=Yes -TrgrC9=Yes -TrgrC10=Yes -TrgrC11=Yes -TrgrC1=Yes -TrgrC12Ins=Yes -TrgrC12Upd=Yes -TrgrC13=Yes -UpdateTableStatistics=Yes -UpdateColumnStatistics=Yes - -[FolderOptions\Physical Objects\Database Generation] -GenScriptName=crebas -GenScriptName0= -GenScriptName1= -GenScriptName2= -GenScriptName3= -GenScriptName4= -GenScriptName5= -GenScriptName6= -GenScriptName7= -GenScriptName8= -GenScriptName9= -GenPathName= -GenSingleFile=Yes -GenODBC=No -GenCheckModel=Yes -GenScriptPrev=Yes -GenArchiveModel=No -GenUseSync=No -GenSyncChoice=0 -GenSyncArch= -GenSyncRmg=0 - -[FolderOptions\Physical Objects\Database Generation\Format] -GenScriptTitle=Yes -GenScriptNamLabl=No -GenScriptQDtbs=No -GenScriptQOwnr=Yes -GenScriptCase=0 -GenScriptEncoding=ANSI -GenScriptNAcct=No -IdentifierDelimiter=" - -[FolderOptions\Physical Objects\Database Generation\Database] -Create=Yes -Open=Yes -Close=Yes -Drop=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Database\Create] -Physical Options=Yes -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\Tablespace] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Tablespace\Create] -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\Storage] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\User] -Create=Yes -Drop=Yes -Comment=Yes -Privilege=No - -[FolderOptions\Physical Objects\Database Generation\User\Create] -Physical Options=No - -[FolderOptions\Physical Objects\Database Generation\Group] -Create=Yes -Drop=Yes -Comment=Yes -Privilege=No - -[FolderOptions\Physical Objects\Database Generation\Role] -Create=Yes -Drop=Yes -Privilege=No - -[FolderOptions\Physical Objects\Database Generation\UserDefinedDataType] -Create=Yes -Comment=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\UserDefinedDataType\Create] -Default value=Yes -Check=Yes - -[FolderOptions\Physical Objects\Database Generation\AbstractDataType] -Create=Yes -Header=Yes -Footer=Yes -Drop=Yes -Comment=Yes -Install JAVA class=Yes -Remove JAVA class=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Rule] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Default] -Create=Yes -Comment=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\Sequence] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Table&&Column] - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Table] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Table\Create] -Check=Yes -Physical Options=Yes -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Table\Create\Check] -Constraint declaration=No - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Column] -User datatype=Yes -Default value=Yes -Check=Yes -Physical Options=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Column\Check] -Constraint declaration=No - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key] - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key\Primary key] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key\Primary key\Create] -Constraint declaration=No -Physical Options=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key\Alternate key] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key\Alternate key\Create] -Constraint declaration=No -Physical Options=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Foreign key] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Foreign key\Create] -Constraint declaration=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Index] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Index\Create] -Constraint declaration=Yes -Physical Options=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Index\Filter] -Primary key=No -Foreign key=Yes -Alternate key=No -Cluster=Yes -Other=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Trigger] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Trigger\Filter] -For insert=Yes -For update=Yes -For delete=Yes -For other=Yes - -[FolderOptions\Physical Objects\Database Generation\View] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\View\Create] -Force Column list=No -Physical Options=Yes -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\View\ViewColumn] -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\View\ViewIndex] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\View\ViewIndex\Create] -Physical Options=Yes - -[FolderOptions\Physical Objects\Database Generation\View\ViewIndex\Filter] -Cluster=Yes -Other=Yes - -[FolderOptions\Physical Objects\Database Generation\View\Trigger] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\View\Trigger\Filter] -For insert=Yes -For update=Yes -For delete=Yes -For other=Yes - -[FolderOptions\Physical Objects\Database Generation\DBMSTrigger] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Synonym] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\Synonym\Filter] -Table=Yes -View=Yes -Proc=Yes -Synonym=Yes -Database Package=Yes -Sequence=Yes - -[FolderOptions\Physical Objects\Database Generation\JoinIndex] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\JoinIndex\Create] -Physical Options=Yes -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\Procedure] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Procedure\Create] -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\DatabasePackage] -Create=Yes -Drop=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\WebService] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Dimension] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\Synchronization] -GenBackupTabl=1 -GenKeepBackTabl=1 -GenTmpTablDrop=No -GenKeepTablOpts=No - -[FolderOptions\Physical Objects\Database Generation\User <<Schema>>] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\User <<User>>] -Create=Yes -Drop=Yes -Comment=Yes -Privilege=No - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Aggregate>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Aggregate>>\ExtendedSubObject <<AggregateParameter>>] -Create=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Assembly>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<AsymmetricKey>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Certificate>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Contract>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<EndPoint>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<EventNotification>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<FullTextCatalog>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<MessageType>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<PartitionFunction>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<PartitionScheme>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Queue>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<RemoteServiceBinding>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<ResourcePool>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Route>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Service>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<SymmetricKey>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<WorkloadGroup>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<XMLSchemaCollection>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Test Data] -GenDataPathName= -GenDataSinglefile=Yes -GenDataScriptName=testdata -GenDataScriptName0= -GenDataScriptName1= -GenDataScriptName2= -GenDataScriptName3= -GenDataScriptName4= -GenDataScriptName5= -GenDataScriptName6= -GenDataScriptName7= -GenDataScriptName8= -GenDataScriptName9= -GenDataOdbc=0 -GenDataDelOld=No -GenDataTitle=No -GenDataDefNumRows=20 -GenDataCommit=0 -GenDataPacket=0 -GenDataOwner=No -GenDataProfNumb= -GenDataProfChar= -GenDataProfDate= -GenDataCSVSeparator=, -GenDataFileFormat=CSV -GenDataUseWizard=No - -[FolderOptions\Pdm] -IndxIQName=%COLUMN%_%INDEXTYPE% -IndxPK=Yes -IndxFK=Yes -IndxAK=Yes -IndxPKName=%TABLE%_PK -IndxFKName=%REFR%_FK -IndxAKName=%AKEY%_AK -IndxPreserve=No -IndxThreshold=0 -IndxStats=No -RefrPreserve=No -JidxPreserve=No -RbldMultiFact=Yes -RbldMultiDim=Yes -RbldMultiJidx=Yes -CubePreserve=No -TablStProcPreserve=No -ProcDepPreserve=Yes -TrgrDepPreserve=Yes -CubeScriptPath= -CubeScriptCase=0 -CubeScriptEncoding=ANSI -CubeScriptNacct=No -CubeScriptHeader=No -CubeScriptExt=csv -CubeScriptExt0=txt -CubeScriptExt1= -CubeScriptExt2= -CubeScriptSep=, -CubeScriptDeli=" -EstimationYears=0 -DfltDomnName=D_%.U:VALUE% -DfltColnName=D_%.U:VALUE% -DfltReuse=Yes -DfltDrop=Yes -[ModelOptions] - -[ModelOptions\Physical Objects] -CaseSensitive=No -DisplayName=Yes -EnableTrans=No -UseTerm=No -EnableRequirements=No -EnableFullShortcut=Yes -DefaultDttp= -IgnoreOwner=No -RebuildTrigger=Yes -RefrUnique=No -RefrAutoMigrate=Yes -RefrMigrateReuse=Yes -RefrMigrateDomain=Yes -RefrMigrateCheck=Yes -RefrMigrateRule=Yes -RefrMigrateExtd=No -RefrMigrDefaultLink=No -RefrDfltImpl=D -RefrPrgtColn=No -RefrMigrateToEnd=No -RebuildTriggerDep=No -ColnFKName=%.3:PARENT%_%COLUMN% -ColnFKNameUse=No -DomnCopyDttp=Yes -DomnCopyChck=No -DomnCopyRule=No -DomnCopyMand=No -DomnCopyExtd=No -DomnCopyProf=No -Notation=0 -DomnDefaultMandatory=No -ColnDefaultMandatory=No -TablDefaultOwner= -ViewDefaultOwner= -TrgrDefaultOwnerTabl= -TrgrDefaultOwnerView= -IdxDefaultOwnerTabl= -IdxDefaultOwnerView= -JdxDefaultOwner= -DBPackDefaultOwner= -SeqDefaultOwner= -ProcDefaultOwner= -DBMSTrgrDefaultOwner= -Currency=USD -RefrDeleteConstraint=0 -RefrUpdateConstraint=0 -RefrParentMandatory=No -RefrParentChangeAllow=Yes -RefrCheckOnCommit=No - -[ModelOptions\Physical Objects\NamingOptionsTemplates] - -[ModelOptions\Physical Objects\ClssNamingOptions] - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMPCKG] - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMPCKG\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMPCKG\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMDOMN] - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMDOMN\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMDOMN\Code] -Template= -MaxLen=128 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\TABL] - -[ModelOptions\Physical Objects\ClssNamingOptions\TABL\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\TABL\Code] -Template= -MaxLen=128 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\COLN] - -[ModelOptions\Physical Objects\ClssNamingOptions\COLN\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\COLN\Code] -Template= -MaxLen=128 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\INDX] - -[ModelOptions\Physical Objects\ClssNamingOptions\INDX\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\INDX\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\REFR] - -[ModelOptions\Physical Objects\ClssNamingOptions\REFR\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\REFR\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VREF] - -[ModelOptions\Physical Objects\ClssNamingOptions\VREF\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VREF\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEW] - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEW\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEW\Code] -Template= -MaxLen=128 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEWC] - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEWC\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEWC\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBSERV] - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBSERV\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBSERV\Code] -Template= -MaxLen=254 -Case=M -ValidChar='a'-'z','A'-'Z','0'-'9',"/-_.!~*'()" -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBOP] - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBOP\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBOP\Code] -Template= -MaxLen=254 -Case=M -ValidChar='a'-'z','A'-'Z','0'-'9',"/-_.!~*'()" -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WPARAM] - -[ModelOptions\Physical Objects\ClssNamingOptions\WPARAM\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WPARAM\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FACT] - -[ModelOptions\Physical Objects\ClssNamingOptions\FACT\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FACT\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DIMN] - -[ModelOptions\Physical Objects\ClssNamingOptions\DIMN\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DIMN\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\MEAS] - -[ModelOptions\Physical Objects\ClssNamingOptions\MEAS\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\MEAS\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DATTR] - -[ModelOptions\Physical Objects\ClssNamingOptions\DATTR\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DATTR\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FILO] - -[ModelOptions\Physical Objects\ClssNamingOptions\FILO\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FILO\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMEOBJ] - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMEOBJ\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMEOBJ\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMELNK] - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMELNK\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMELNK\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DefaultClass] - -[ModelOptions\Physical Objects\ClssNamingOptions\DefaultClass\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DefaultClass\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Connection] - -[ModelOptions\Pdm] - -[ModelOptions\Generate] - -[ModelOptions\Generate\Xsm] -GenRootElement=Yes -GenComplexType=No -GenAttribute=Yes -CheckModel=Yes -SaveLinks=Yes -ORMapping=No -NameToCode=No - -[ModelOptions\Generate\Pdm] -RRMapping=No - -[ModelOptions\Generate\Cdm] -CheckModel=Yes -SaveLinks=Yes -NameToCode=No -Notation=2 - -[ModelOptions\Generate\Oom] -CheckModel=Yes -SaveLinks=Yes -ORMapping=No -NameToCode=Yes -ClassPrefix= - -[ModelOptions\Generate\Ldm] -CheckModel=Yes -SaveLinks=Yes -NameToCode=No - -[ModelOptions\Default Opts] - -[ModelOptions\Default Opts\TABL] -PhysOpts= - -[ModelOptions\Default Opts\COLN] -PhysOpts= - -[ModelOptions\Default Opts\INDX] -PhysOpts= - -[ModelOptions\Default Opts\AKEY] -PhysOpts= - -[ModelOptions\Default Opts\PKEY] -PhysOpts= - -[ModelOptions\Default Opts\STOR] -PhysOpts= - -[ModelOptions\Default Opts\TSPC] -PhysOpts= - -[ModelOptions\Default Opts\SQNC] -PhysOpts= - -[ModelOptions\Default Opts\DTBS] -PhysOpts= - -[ModelOptions\Default Opts\USER] -PhysOpts= - -[ModelOptions\Default Opts\JIDX] -PhysOpts= - -[ModelOptions\Default Opts\FRMEOBJ<<ResourcePool>>] -PhysOpts= - -[ModelOptions\Default Opts\FRMEOBJ<<WorkloadGroup>>] -PhysOpts= - -[ModelOptions\Default Opts\FRMESOB<<ResourcePool>>] -PhysOpts= - -[ModelOptions\Default Opts\FRMESOB<<WorkloadGroup>>] -PhysOpts= - - -14ADDF71-0085-4260-A4E4-A1BAAEB38F59 -Microsoft SQL Server 2012 -MSSQLSRV2012 -1502368828 -Administrator -1502368828 -Administrator - -11D14009-FEF7-43A3-BC76-BE5B34F11D08 -4BA9F647-DAB1-11D1-9944-006097355D9B - - - - -92360091-5759-4724-BE43-894656D9A995 -Order -Order -1502368829 -Administrator -1502807435 -Administrator -[DisplayPreferences] - -[DisplayPreferences\PDM] - -[DisplayPreferences\General] -Adjust to text=Yes -Snap Grid=No -Constrain Labels=Yes -Display Grid=No -Show Page Delimiter=Yes -Show Links intersections=No -Activate automatic link routing=No -Grid size=0 -Graphic unit=2 -Window color=255, 255, 255 -Background image= -Background mode=8 -Watermark image= -Watermark mode=8 -Show watermark on screen=No -Gradient mode=0 -Gradient end color=255, 255, 255 -Show Swimlane=No -SwimlaneVert=Yes -TreeVert=No -CompDark=0 - -[DisplayPreferences\Object] -Show Icon=No -Mode=2 -Trunc Length=40 -Word Length=40 -Word Text=!"#$%&')*+,-./:;=>?@\]^_`|}~ -Shortcut IntIcon=Yes -Shortcut IntLoct=Yes -Shortcut IntFullPath=No -Shortcut IntLastPackage=Yes -Shortcut ExtIcon=Yes -Shortcut ExtLoct=No -Shortcut ExtFullPath=No -Shortcut ExtLastPackage=Yes -Shortcut ExtIncludeModl=Yes -EObjShowStrn=Yes -ExtendedObject.Comment=No -ExtendedObject.IconPicture=No -ExtendedObject.TextStyle=No -ExtendedObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Object Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -ELnkShowStrn=Yes -ELnkShowName=Yes -ExtendedLink_SymbolLayout=<Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Source" >[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] </Form>[CRLF]</Form> -FileObject.Stereotype=No -FileObject.DisplayName=Yes -FileObject.LocationOrName=No -FileObject.IconPicture=No -FileObject.TextStyle=No -FileObject.IconMode=Yes -FileObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Location" Attribute="LocationOrName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Package.Stereotype=Yes -Package.Comment=No -Package.IconPicture=No -Package.TextStyle=No -Package_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Display Model Version=Yes -Table.Stereotype=Yes -Table.DisplayName=Yes -Table.OwnerDisplayName=No -Table.Columns=Yes -Table.Columns._Filter="All Columns" PDMCOLNALL -Table.Columns._Columns=Stereotype DataType KeyIndicator -Table.Columns._Limit=-5 -Table.Keys=No -Table.Keys._Columns=Stereotype Indicator -Table.Indexes=No -Table.Indexes._Columns=Stereotype -Table.Triggers=No -Table.Triggers._Columns=Stereotype -Table.Comment=No -Table.IconPicture=No -Table.TextStyle=No -Table_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="Stereotype No\r\nDisplayName Yes\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nDomain No\r\nKeyIndicator No\r\nIndexIndicator No\r\nNullStatus No" Filters="&quot;All Columns&quot; PDMCOLNALL &quot;&quot;\r\n&quot;PK Columns&quot; PDMCOLNPK &quot;\&quot;PRIM \&quot;TRUE\&quot; TRUE\&quot;&quot;\r\n&quot;Key Columns&quot; PDMCOLNKEY &quot;\&quot;KEYS \&quot;TRUE\&quot; TRUE\&quot;&quot;" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Keys" Collection="Keys" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Triggers" Collection="Triggers" Columns="Stereotype No\r\nDisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -View.Stereotype=Yes -View.DisplayName=Yes -View.OwnerDisplayName=No -View.Columns=Yes -View.Columns._Columns=DisplayName -View.Columns._Limit=-5 -View.TemporaryVTables=Yes -View.Indexes=No -View.Comment=No -View.IconPicture=No -View.TextStyle=No -View_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="DisplayName No\r\nExpression No\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nIndexIndicator No" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Tables" Collection="TemporaryVTables" Columns="Name Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="DisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Procedure.Stereotype=No -Procedure.DisplayName=Yes -Procedure.OwnerDisplayName=No -Procedure.Comment=No -Procedure.IconPicture=No -Procedure.TextStyle=No -Procedure_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Reference.Cardinality=No -Reference.ImplementationType=No -Reference.ChildRole=Yes -Reference.Stereotype=Yes -Reference.DisplayName=No -Reference.ForeignKeyConstraintName=No -Reference.JoinExpression=No -Reference.Integrity=No -Reference.ParentRole=Yes -Reference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Cardinality" Attribute="Cardinality" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Implementation" Attribute="ImplementationType" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Cons&amp;traint Name" Attribute="ForeignKeyConstraintName" Prefix="" Suffix="" Caption="Cons&amp;traint Name" Mandatory="No" />[CRLF] <StandardAttribute Name="Join" Attribute="JoinExpression" Prefix="" Suffix="" Caption="Join" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Referential integrity" Attribute="Integrity" Prefix="" Suffix="" Caption="Referential integrity" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> -ViewReference.ChildRole=Yes -ViewReference.Stereotype=Yes -ViewReference.DisplayName=No -ViewReference.JoinExpression=No -ViewReference.ParentRole=Yes -ViewReference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Join Expression" Attribute="JoinExpression" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> - -[DisplayPreferences\Symbol] - -[DisplayPreferences\Symbol\FRMEOBJ] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=6000 -Height=2000 -Brush color=255 255 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=64 -Brush gradient color=192 192 192 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 255 128 128 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FRMELNK] -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FILO] -OBJSTRNFont=新宋体,8,N -OBJSTRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LCNMFont=新宋体,8,N -LCNMFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=3600 -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PDMPCKG] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 178 178 178 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\TABL] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -KeysFont=新宋体,8,N -KeysFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -TriggersFont=新宋体,8,N -TriggersFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=178 214 252 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VIEW] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -TemporaryVTablesFont=新宋体,8,N -TemporaryVTablesFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=208 208 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PROC] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4000 -Height=1000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 108 0 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\REFR] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VREF] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\USRDEPD] -OBJXSTRFont=新宋体,8,N -OBJXSTRFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=2 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\Free Symbol] -Free TextFont=新宋体,8,N -Free TextFont color=0, 0, 0 -Line style=0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 -(8268, 11693) -((315,354), (433,354)) -1 -7 - - -1502807428 -1502807598 -((-5249,-9277), (-4799,-2683)) -((-5024,-2683),(-5024,-9277)) -1 -1 -12615680 -12632256 -CENTER 0 新宋体,8,N -SOURCE 0 新宋体,8,N -DESTINATION 0 新宋体,8,N - - - - - - - - - - - -1502807435 -1502807435 -((-3717,2041), (-3267,8646)) -((-3492,2041),(-3492,8646)) -1 -1 -12615680 -12632256 -CENTER 0 新宋体,8,N -SOURCE 0 新宋体,8,N -DESTINATION 0 新宋体,8,N - - - - - - - - - - - -1502368959 -1502369707 --1 -((-11260,6510), (7534,11634)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - -1502807154 -1502807593 --1 -((-11088,-3939), (10170,3194)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - -1502807407 -1502810053 --1 -((-10585,-13983), (8209,-6187)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - - - -DE41247D-905B-4995-863B-693380B257D6 -Product -Product -1502613808 -Administrator -1502613840 -Administrator -[DisplayPreferences] - -[DisplayPreferences\PDM] - -[DisplayPreferences\General] -Adjust to text=Yes -Snap Grid=No -Constrain Labels=Yes -Display Grid=No -Show Page Delimiter=Yes -Show Links intersections=No -Activate automatic link routing=No -Grid size=0 -Graphic unit=2 -Window color=255, 255, 255 -Background image= -Background mode=8 -Watermark image= -Watermark mode=8 -Show watermark on screen=No -Gradient mode=0 -Gradient end color=255, 255, 255 -Show Swimlane=No -SwimlaneVert=Yes -TreeVert=No -CompDark=0 - -[DisplayPreferences\Object] -Show Icon=No -Mode=2 -Trunc Length=40 -Word Length=40 -Word Text=!"#$%&')*+,-./:;=>?@\]^_`|}~ -Shortcut IntIcon=Yes -Shortcut IntLoct=Yes -Shortcut IntFullPath=No -Shortcut IntLastPackage=Yes -Shortcut ExtIcon=Yes -Shortcut ExtLoct=No -Shortcut ExtFullPath=No -Shortcut ExtLastPackage=Yes -Shortcut ExtIncludeModl=Yes -EObjShowStrn=Yes -ExtendedObject.Comment=No -ExtendedObject.IconPicture=No -ExtendedObject.TextStyle=No -ExtendedObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Object Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -ELnkShowStrn=Yes -ELnkShowName=Yes -ExtendedLink_SymbolLayout=<Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Source" >[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] </Form>[CRLF]</Form> -FileObject.Stereotype=No -FileObject.DisplayName=Yes -FileObject.LocationOrName=No -FileObject.IconPicture=No -FileObject.TextStyle=No -FileObject.IconMode=Yes -FileObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Location" Attribute="LocationOrName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Package.Stereotype=Yes -Package.Comment=No -Package.IconPicture=No -Package.TextStyle=No -Package_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Display Model Version=Yes -Table.Stereotype=Yes -Table.DisplayName=Yes -Table.OwnerDisplayName=No -Table.Columns=Yes -Table.Columns._Filter="All Columns" PDMCOLNALL -Table.Columns._Columns=Stereotype DataType KeyIndicator -Table.Columns._Limit=-5 -Table.Keys=No -Table.Keys._Columns=Stereotype Indicator -Table.Indexes=No -Table.Indexes._Columns=Stereotype -Table.Triggers=No -Table.Triggers._Columns=Stereotype -Table.Comment=No -Table.IconPicture=No -Table.TextStyle=No -Table_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="Stereotype No\r\nDisplayName Yes\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nDomain No\r\nKeyIndicator No\r\nIndexIndicator No\r\nNullStatus No" Filters="&quot;All Columns&quot; PDMCOLNALL &quot;&quot;\r\n&quot;PK Columns&quot; PDMCOLNPK &quot;\&quot;PRIM \&quot;TRUE\&quot; TRUE\&quot;&quot;\r\n&quot;Key Columns&quot; PDMCOLNKEY &quot;\&quot;KEYS \&quot;TRUE\&quot; TRUE\&quot;&quot;" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Keys" Collection="Keys" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Triggers" Collection="Triggers" Columns="Stereotype No\r\nDisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -View.Stereotype=Yes -View.DisplayName=Yes -View.OwnerDisplayName=No -View.Columns=Yes -View.Columns._Columns=DisplayName -View.Columns._Limit=-5 -View.TemporaryVTables=Yes -View.Indexes=No -View.Comment=No -View.IconPicture=No -View.TextStyle=No -View_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="DisplayName No\r\nExpression No\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nIndexIndicator No" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Tables" Collection="TemporaryVTables" Columns="Name Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="DisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Procedure.Stereotype=No -Procedure.DisplayName=Yes -Procedure.OwnerDisplayName=No -Procedure.Comment=No -Procedure.IconPicture=No -Procedure.TextStyle=No -Procedure_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Reference.Cardinality=No -Reference.ImplementationType=No -Reference.ChildRole=Yes -Reference.Stereotype=Yes -Reference.DisplayName=No -Reference.ForeignKeyConstraintName=No -Reference.JoinExpression=No -Reference.Integrity=No -Reference.ParentRole=Yes -Reference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Cardinality" Attribute="Cardinality" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Implementation" Attribute="ImplementationType" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Cons&amp;traint Name" Attribute="ForeignKeyConstraintName" Prefix="" Suffix="" Caption="Cons&amp;traint Name" Mandatory="No" />[CRLF] <StandardAttribute Name="Join" Attribute="JoinExpression" Prefix="" Suffix="" Caption="Join" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Referential integrity" Attribute="Integrity" Prefix="" Suffix="" Caption="Referential integrity" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> -ViewReference.ChildRole=Yes -ViewReference.Stereotype=Yes -ViewReference.DisplayName=No -ViewReference.JoinExpression=No -ViewReference.ParentRole=Yes -ViewReference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Join Expression" Attribute="JoinExpression" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> - -[DisplayPreferences\Symbol] - -[DisplayPreferences\Symbol\FRMEOBJ] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=6000 -Height=2000 -Brush color=255 255 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=64 -Brush gradient color=192 192 192 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 255 128 128 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FRMELNK] -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FILO] -OBJSTRNFont=新宋体,8,N -OBJSTRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LCNMFont=新宋体,8,N -LCNMFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=3600 -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PDMPCKG] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 178 178 178 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\TABL] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -KeysFont=新宋体,8,N -KeysFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -TriggersFont=新宋体,8,N -TriggersFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=178 214 252 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VIEW] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -TemporaryVTablesFont=新宋体,8,N -TemporaryVTablesFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=208 208 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PROC] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4000 -Height=1000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 108 0 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\REFR] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VREF] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\USRDEPD] -OBJXSTRFont=新宋体,8,N -OBJXSTRFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=2 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\Free Symbol] -Free TextFont=新宋体,8,N -Free TextFont color=0, 0, 0 -Line style=0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 -(8268, 11693) -((315,354), (433,354)) -1 -7 - - -1502613840 -1502614067 --1 -((-9995,-2970), (8799,3952)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - - - -68F8168C-38E6-4220-9B3C-0FA0C72EE518 -Customer -Customer -1503007461 -Administrator -1503007472 -Administrator -[DisplayPreferences] - -[DisplayPreferences\PDM] - -[DisplayPreferences\General] -Adjust to text=Yes -Snap Grid=No -Constrain Labels=Yes -Display Grid=No -Show Page Delimiter=Yes -Show Links intersections=No -Activate automatic link routing=No -Grid size=0 -Graphic unit=2 -Window color=255, 255, 255 -Background image= -Background mode=8 -Watermark image= -Watermark mode=8 -Show watermark on screen=No -Gradient mode=0 -Gradient end color=255, 255, 255 -Show Swimlane=No -SwimlaneVert=Yes -TreeVert=No -CompDark=0 - -[DisplayPreferences\Object] -Show Icon=No -Mode=2 -Trunc Length=40 -Word Length=40 -Word Text=!"#$%&')*+,-./:;=>?@\]^_`|}~ -Shortcut IntIcon=Yes -Shortcut IntLoct=Yes -Shortcut IntFullPath=No -Shortcut IntLastPackage=Yes -Shortcut ExtIcon=Yes -Shortcut ExtLoct=No -Shortcut ExtFullPath=No -Shortcut ExtLastPackage=Yes -Shortcut ExtIncludeModl=Yes -EObjShowStrn=Yes -ExtendedObject.Comment=No -ExtendedObject.IconPicture=No -ExtendedObject.TextStyle=No -ExtendedObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Object Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -ELnkShowStrn=Yes -ELnkShowName=Yes -ExtendedLink_SymbolLayout=<Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Source" >[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] </Form>[CRLF]</Form> -FileObject.Stereotype=No -FileObject.DisplayName=Yes -FileObject.LocationOrName=No -FileObject.IconPicture=No -FileObject.TextStyle=No -FileObject.IconMode=Yes -FileObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Location" Attribute="LocationOrName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Package.Stereotype=Yes -Package.Comment=No -Package.IconPicture=No -Package.TextStyle=No -Package_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Display Model Version=Yes -Table.Stereotype=Yes -Table.DisplayName=Yes -Table.OwnerDisplayName=No -Table.Columns=Yes -Table.Columns._Filter="All Columns" PDMCOLNALL -Table.Columns._Columns=Stereotype DataType KeyIndicator -Table.Columns._Limit=-5 -Table.Keys=No -Table.Keys._Columns=Stereotype Indicator -Table.Indexes=No -Table.Indexes._Columns=Stereotype -Table.Triggers=No -Table.Triggers._Columns=Stereotype -Table.Comment=No -Table.IconPicture=No -Table.TextStyle=No -Table_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="Stereotype No\r\nDisplayName Yes\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nDomain No\r\nKeyIndicator No\r\nIndexIndicator No\r\nNullStatus No" Filters="&quot;All Columns&quot; PDMCOLNALL &quot;&quot;\r\n&quot;PK Columns&quot; PDMCOLNPK &quot;\&quot;PRIM \&quot;TRUE\&quot; TRUE\&quot;&quot;\r\n&quot;Key Columns&quot; PDMCOLNKEY &quot;\&quot;KEYS \&quot;TRUE\&quot; TRUE\&quot;&quot;" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Keys" Collection="Keys" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Triggers" Collection="Triggers" Columns="Stereotype No\r\nDisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -View.Stereotype=Yes -View.DisplayName=Yes -View.OwnerDisplayName=No -View.Columns=Yes -View.Columns._Columns=DisplayName -View.Columns._Limit=-5 -View.TemporaryVTables=Yes -View.Indexes=No -View.Comment=No -View.IconPicture=No -View.TextStyle=No -View_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="DisplayName No\r\nExpression No\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nIndexIndicator No" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Tables" Collection="TemporaryVTables" Columns="Name Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="DisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Procedure.Stereotype=No -Procedure.DisplayName=Yes -Procedure.OwnerDisplayName=No -Procedure.Comment=No -Procedure.IconPicture=No -Procedure.TextStyle=No -Procedure_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Reference.Cardinality=No -Reference.ImplementationType=No -Reference.ChildRole=Yes -Reference.Stereotype=Yes -Reference.DisplayName=No -Reference.ForeignKeyConstraintName=No -Reference.JoinExpression=No -Reference.Integrity=No -Reference.ParentRole=Yes -Reference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Cardinality" Attribute="Cardinality" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Implementation" Attribute="ImplementationType" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Cons&amp;traint Name" Attribute="ForeignKeyConstraintName" Prefix="" Suffix="" Caption="Cons&amp;traint Name" Mandatory="No" />[CRLF] <StandardAttribute Name="Join" Attribute="JoinExpression" Prefix="" Suffix="" Caption="Join" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Referential integrity" Attribute="Integrity" Prefix="" Suffix="" Caption="Referential integrity" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> -ViewReference.ChildRole=Yes -ViewReference.Stereotype=Yes -ViewReference.DisplayName=No -ViewReference.JoinExpression=No -ViewReference.ParentRole=Yes -ViewReference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Join Expression" Attribute="JoinExpression" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> - -[DisplayPreferences\Symbol] - -[DisplayPreferences\Symbol\FRMEOBJ] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=6000 -Height=2000 -Brush color=255 255 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=64 -Brush gradient color=192 192 192 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 255 128 128 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FRMELNK] -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FILO] -OBJSTRNFont=新宋体,8,N -OBJSTRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LCNMFont=新宋体,8,N -LCNMFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=3600 -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PDMPCKG] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 178 178 178 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\TABL] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -KeysFont=新宋体,8,N -KeysFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -TriggersFont=新宋体,8,N -TriggersFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=178 214 252 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VIEW] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -TemporaryVTablesFont=新宋体,8,N -TemporaryVTablesFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=208 208 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PROC] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4000 -Height=1000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 108 0 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\REFR] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VREF] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\USRDEPD] -OBJXSTRFont=新宋体,8,N -OBJXSTRFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=2 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\Free Symbol] -Free TextFont=新宋体,8,N -Free TextFont color=0, 0, 0 -Line style=0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 -(8268, 11693) -((315,354), (433,354)) -1 -7 - - -1503007472 -1503102523 --1 -((-13056,-66), (10598,13178)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - - - - - - - - -D792A2DB-5F90-499F-8B39-0AAFF559CD36 -Sales -Sales -1502368978 -Administrator -1502807211 -Administrator -Schema - - -8B9F6BB3-2312-48C1-8479-F48207BDE3BC -Productions -Productions -1502613870 -Administrator -1502613934 -Administrator -Schema - - -DFC6388B-EEDB-4184-94C6-68548BFC8ADC -Customers -Customers -1503008601 -Administrator -1503008677 -Administrator -Schema - - - - -DCFB7BAF-61C8-4927-8C69-7EC2C03A17BB -订单( Orders ) -Orders -1502368959 -Administrator -1502612809 -Administrator -订单 - - - -44A75471-51DC-4FF7-9579-4F96537D6184 -订单编号( OrderId ) -OrderId -1502369000 -Administrator -1502369037 -Administrator -订单编号 -uniqueidentifier -1 - - -64DB43B2-EFDA-499C-A1DF-F5D6A1F6FB0D -订单编码( Code ) -Code -1502369007 -Administrator -1502369564 -Administrator -订单编码 -nvarchar(30) -30 -1 - - -935D2B35-8FDD-4862-8E04-77E196989914 -订单名称( Name ) -Name -1502612591 -Administrator -1502612826 -Administrator -订单名称 -nvarchar(200) -200 -1 - - -F83621F4-DAA3-4F37-8FBC-EEFCECA527A8 -版本号( Version ) -Version -1408388616 -Administrator -1502369696 -Administrator -版本号 -timestamp - - - - -92740D57-C6F0-457B-8D5D-4845D070F063 -Key_1 -Key_1 -1502369007 -Administrator -1502369037 -Administrator - - - - - - - - - - - - - - - - -D181918A-EA17-4145-A88E-907A77FACA10 -商品( Products ) -Products -1502368959 -Administrator -1502810035 -Administrator -商品 - - - -B53960D7-4FB2-4954-BED8-14AB641761B2 -商品编号( ProductId ) -ProductId -1502369000 -Administrator -1502614868 -Administrator -商品编号 -int -1 - - -82CB28CE-8970-4273-8B82-05213E608D8F -商品编码( Code ) -Code -1502369007 -Administrator -1502614005 -Administrator -商品编码 -nvarchar(30) -30 -1 - - -8A8B240F-EB3A-417B-B551-A984300FD92F -商品名称( Name ) -Name -1502612591 -Administrator -1502614005 -Administrator -商品名称 -nvarchar(200) -200 -1 - - -E956A6FE-EF12-4FD1-A6F9-7A0B2C7E3149 -扩展属性( Extends ) -Extends -1502614005 -Administrator -1502614064 -Administrator -扩展属性 -nvarchar(max) - - -509CD7FA-70E7-4E7D-88E5-5D6468A73853 -价格( Price ) -Price -1502807514 -Administrator -1502810103 -Administrator -价格 -decimal(10,2) -10 -2 - - -C8C5119A-D798-456A-B4FD-CB35C05B804C -是否删除( IsDeleted ) -IsDeleted -1441104989 -Administrator -1502776824 -Administrator -是否删除 -bit -1 - - -269BD895-4B63-4E11-B8C1-EA90928F070F -版本号( Version ) -Version -1408388616 -Administrator -1502613840 -Administrator -版本号 -timestamp - - - - -B1442255-04B5-4E29-99AA-B7218DA5522E -Key_1 -Key_1 -1502369007 -Administrator -1502613840 -Administrator - - - - - - - - - - - - - - - - -BA70D8F5-EDAF-42FF-A493-ADD0F270786D -订单明细( OrderItems ) -OrderItems -1502807154 -Administrator -1502807526 -Administrator -订单明细 - - - -0CC5B156-1828-4743-A78A-1BA46EA6EF63 -订单明细编号( OrderItemId ) -OrderItemId -1502807211 -Administrator -1502807345 -Administrator -订单明细编号 -uniqueidentifier -1 - - -DFD8CD92-6982-4B46-AA1B-1F5C5ACEA826 -订单编号( OrderId ) -OrderId -1502807435 -Administrator -1502857657 -Administrator -订单编号 -uniqueidentifier -1 - - -258D4008-B597-465B-A18B-9230CF186B40 -商品编号( ProductId ) -ProductId -1502807428 -Administrator -1502807585 -Administrator -商品编号 -int -1 - - -36726035-757C-4149-9B60-C814BC94A7BE -商品名称( ProductName ) -ProductName -1502612591 -Administrator -1502808511 -Administrator -商品名称 -nvarchar(200) -200 -1 - - -40D46135-FFF4-46EF-B62C-D10D3368930C -数量( Quantity ) -Quantity -1502807446 -Administrator -1502807487 -Administrator -数量 -int -1 - - -B1C7C098-90D1-4B4C-BC25-06777E071830 -单价( Price ) -Price -1502807514 -Administrator -1502807567 -Administrator -单价 -decimal(10,2) -10 -2 -1 - - - - -0F965720-6103-46E8-8EE7-B48237AF21E8 -Key_1 -Key_1 -1502807341 -Administrator -1502857657 -Administrator - - - - - - - - - - - - - - - - -C5252905-0F60-4917-A321-FD71B172ACA0 -客户( Customers ) -Customers -1503007472 -Administrator -1503102513 -Administrator -客户 - - - -A823E497-5CB0-432E-BF50-FD37EBC2B648 -客户编号( CustomerId ) -CustomerId -1503008705 -Administrator -1503008831 -Administrator -客户编号 -nvarchar(50) -50 -1 - - -A58AEBD8-0122-43F0-813E-091BA1D20FED -客户名称( Name ) -Name -1502612591 -Administrator -1503009006 -Administrator -客户名称 -nvarchar(20) -20 -1 - - -26AF2F9E-4450-46ED-B9F3-00FDD4D69E87 -昵称( Nickname ) -Nickname -1347015477 -Administrator -1503009023 -Administrator -昵称 -nvarchar(30) -30 - - -A3AC3300-5774-4BA9-BDC3-2B833A31E13F -余额( Balance ) -Balance -1503065241 -Administrator -1503066488 -Administrator -余额 -decimal(18,2) -18 -2 -1 - - -B3906707-46B9-426E-A2F9-8D132FBE47BA -性别( Gender ) -Gender -1347015477 -Administrator -1503009032 -Administrator -性别 -int - - -422A0FBF-02EE-4C77-ACF3-0F6DFF20B846 -联系电话( Tel ) -Tel -1347015628 -Administrator -1503009090 -Administrator -联系电话 -nvarchar(20) -20 - - -8FA4B750-24CD-43B8-8397-F6D1EDECEBC9 -手机号( Mobile ) -Mobile -1347000983 -Administrator -1503009090 -Administrator -手机号 -nvarchar(20) -20 - - -94E1690C-B465-4C47-B724-B4199213FBC0 -电子邮件( Email ) -Email -1347000983 -Administrator -1503009103 -Administrator -电子邮件 -nvarchar(100) -100 - - -3DA62965-6815-4CF9-858E-6D5298D97075 -创建时间( CreationTime ) -CreationTime -1387961893 -Administrator -1503102513 -Administrator -创建时间 -datetime - - -98A5605B-3958-404E-9658-384E3363DF0E -创建人( CreatorId ) -CreatorId -1441284440 -Administrator -1503102513 -Administrator -创建人 -uniqueidentifier - - -7A4191EC-ADFF-4E5D-A672-9CCB5ECE907D -最后修改时间( LastModificationTime ) -LastModificationTime -1387961893 -Administrator -1503102513 -Administrator -最后修改时间 -datetime - - -4BAC5792-FF54-4C9B-AA55-32B63774DA44 -最后修改人( LastModifierId ) -LastModifierId -1441284440 -Administrator -1503102513 -Administrator -最后修改人 -uniqueidentifier - - -1CBFD184-6982-4407-BEBA-A169BD9AEADF -版本号( Version ) -Version -1408388616 -Administrator -1503008851 -Administrator -版本号 -timestamp - - - - -9AB7B83B-729A-4865-8A84-1A8038CA22D5 -Key_1 -Key_1 -1503008825 -Administrator -1503008831 -Administrator - - - - - - - - - - - - - - - - - - -E41C4795-1F29-4BDA-ACAF-614C9A3FB6A0 -Reference_1 -Reference_1 -1502807428 -Administrator -1502807428 -Administrator -0..* - - - - - - - - - - - -AE653EE6-E7FE-4276-BCFE-3F49F078877C -1502807428 -Administrator -1502807428 -Administrator - - - - - - - - - - -76ABB6B3-CE4D-496B-B6EA-8563C5C6E8BA -Reference_2 -Reference_2 -1502807435 -Administrator -1502807435 -Administrator -0..* - - - - - - - - - - - -5900BB99-C0D5-4638-B555-26BC4FCB28A9 -1502807435 -Administrator -1502807435 -Administrator - - - - - - - - - - - - -AFA9EDB8-8EE4-4FD5-8275-60A5D6D96228 -PUBLIC -PUBLIC -1502368828 -Administrator -1503008669 -Administrator - - - - - - - - - -B2E885F2-096F-4518-8A93-545A15C84563 -Microsoft SQL Server 2012 -MSSQLSRV2012 -1502368828 -Administrator -1502368828 -Administrator -file:///%_DBMS%/sqlsv2012.xdb -11D14009-FEF7-43A3-BC76-BE5B34F11D08 -4BA9F647-DAB1-11D1-9944-006097355D9B -1432291048 - - - - - - - - - - \ No newline at end of file diff --git a/test/Util.Datas.Tests.Integration/SqlServer/Scripts/Pd/Samples.pdm b/test/Util.Datas.Tests.Integration/SqlServer/Scripts/Pd/Samples.pdm deleted file mode 100644 index ebdd57531..000000000 --- a/test/Util.Datas.Tests.Integration/SqlServer/Scripts/Pd/Samples.pdm +++ /dev/null @@ -1,5988 +0,0 @@ - - - - - - - - - -91AD3F79-C708-47A6-81A2-7B9E077A7DF4 -Samples -Samples -1502368829 -Administrator -1514588592 -Administrator -[FolderOptions] - -[FolderOptions\Physical Objects] -GenerationCheckModel=Yes -GenerationPath= -GenerationOptions= -GenerationTasks= -GenerationTargets= -GenerationSelections= -RevPkey=Yes -RevFkey=Yes -RevAkey=Yes -RevCheck=Yes -RevIndx=Yes -RevOpts=Yes -RevViewAsTabl=No -RevViewOpts=Yes -RevSystAsTabl=Yes -RevTablPerm=No -RevViewPerm=No -RevProcPerm=No -RevDbpkPerm=No -RevSqncPerm=No -RevAdtPerm=No -RevUserPriv=No -RevUserOpts=No -RevGrpePriv=No -RevRolePriv=No -RevDtbsOpts=Yes -RevDtbsPerm=No -RevViewIndx=Yes -RevJidxOpts=Yes -RevStats=No -RevTspcPerm=No -RevCaseSensitive=No -GenTrgrStdMsg=Yes -GenTrgrMsgTab= -GenTrgrMsgNo= -GenTrgrMsgTxt= -TrgrPreserve=No -TrgrIns=Yes -TrgrUpd=Yes -TrgrDel=Yes -TrgrC2Ins=Yes -TrgrC2Upd=Yes -TrgrC3=Yes -TrgrC4=Yes -TrgrC5=Yes -TrgrC6=Yes -TrgrC7=Yes -TrgrC8=Yes -TrgrC9=Yes -TrgrC10=Yes -TrgrC11=Yes -TrgrC1=Yes -TrgrC12Ins=Yes -TrgrC12Upd=Yes -TrgrC13=Yes -UpdateTableStatistics=Yes -UpdateColumnStatistics=Yes - -[FolderOptions\Physical Objects\Database Generation] -GenScriptName=crebas -GenScriptName0= -GenScriptName1= -GenScriptName2= -GenScriptName3= -GenScriptName4= -GenScriptName5= -GenScriptName6= -GenScriptName7= -GenScriptName8= -GenScriptName9= -GenPathName= -GenSingleFile=Yes -GenODBC=No -GenCheckModel=Yes -GenScriptPrev=Yes -GenArchiveModel=No -GenUseSync=No -GenSyncChoice=0 -GenSyncArch= -GenSyncRmg=0 - -[FolderOptions\Physical Objects\Database Generation\Format] -GenScriptTitle=Yes -GenScriptNamLabl=No -GenScriptQDtbs=No -GenScriptQOwnr=Yes -GenScriptCase=0 -GenScriptEncoding=ANSI -GenScriptNAcct=No -IdentifierDelimiter=" - -[FolderOptions\Physical Objects\Database Generation\Database] -Create=Yes -Open=Yes -Close=Yes -Drop=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Database\Create] -Physical Options=Yes -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\Tablespace] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Tablespace\Create] -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\Storage] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\User] -Create=Yes -Drop=Yes -Comment=Yes -Privilege=No - -[FolderOptions\Physical Objects\Database Generation\User\Create] -Physical Options=No - -[FolderOptions\Physical Objects\Database Generation\Group] -Create=Yes -Drop=Yes -Comment=Yes -Privilege=No - -[FolderOptions\Physical Objects\Database Generation\Role] -Create=Yes -Drop=Yes -Privilege=No - -[FolderOptions\Physical Objects\Database Generation\UserDefinedDataType] -Create=Yes -Comment=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\UserDefinedDataType\Create] -Default value=Yes -Check=Yes - -[FolderOptions\Physical Objects\Database Generation\AbstractDataType] -Create=Yes -Header=Yes -Footer=Yes -Drop=Yes -Comment=Yes -Install JAVA class=Yes -Remove JAVA class=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Rule] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Default] -Create=Yes -Comment=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\Sequence] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Table&&Column] - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Table] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Table\Create] -Check=Yes -Physical Options=Yes -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Table\Create\Check] -Constraint declaration=No - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Column] -User datatype=Yes -Default value=Yes -Check=Yes -Physical Options=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Column\Check] -Constraint declaration=No - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key] - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key\Primary key] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key\Primary key\Create] -Constraint declaration=No -Physical Options=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key\Alternate key] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Key\Alternate key\Create] -Constraint declaration=No -Physical Options=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Foreign key] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Foreign key\Create] -Constraint declaration=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Index] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Index\Create] -Constraint declaration=Yes -Physical Options=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Index\Filter] -Primary key=No -Foreign key=Yes -Alternate key=No -Cluster=Yes -Other=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Trigger] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Table&&Column\Trigger\Filter] -For insert=Yes -For update=Yes -For delete=Yes -For other=Yes - -[FolderOptions\Physical Objects\Database Generation\View] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\View\Create] -Force Column list=No -Physical Options=Yes -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\View\ViewColumn] -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\View\ViewIndex] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\View\ViewIndex\Create] -Physical Options=Yes - -[FolderOptions\Physical Objects\Database Generation\View\ViewIndex\Filter] -Cluster=Yes -Other=Yes - -[FolderOptions\Physical Objects\Database Generation\View\Trigger] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\View\Trigger\Filter] -For insert=Yes -For update=Yes -For delete=Yes -For other=Yes - -[FolderOptions\Physical Objects\Database Generation\DBMSTrigger] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Synonym] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\Synonym\Filter] -Table=Yes -View=Yes -Proc=Yes -Synonym=Yes -Database Package=Yes -Sequence=Yes - -[FolderOptions\Physical Objects\Database Generation\JoinIndex] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\JoinIndex\Create] -Physical Options=Yes -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\Procedure] -Create=Yes -Drop=Yes -Comment=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\Procedure\Create] -Header=Yes -Footer=Yes - -[FolderOptions\Physical Objects\Database Generation\DatabasePackage] -Create=Yes -Drop=Yes -Permission=No - -[FolderOptions\Physical Objects\Database Generation\WebService] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\Dimension] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\Synchronization] -GenBackupTabl=1 -GenKeepBackTabl=1 -GenTmpTablDrop=No -GenKeepTablOpts=No - -[FolderOptions\Physical Objects\Database Generation\User <<Schema>>] -Create=Yes -Drop=Yes -Comment=Yes - -[FolderOptions\Physical Objects\Database Generation\User <<User>>] -Create=Yes -Drop=Yes -Comment=Yes -Privilege=No - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Aggregate>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Aggregate>>\ExtendedSubObject <<AggregateParameter>>] -Create=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Assembly>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<AsymmetricKey>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Certificate>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Contract>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<EndPoint>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<EventNotification>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<FullTextCatalog>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<MessageType>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<PartitionFunction>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<PartitionScheme>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Queue>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<RemoteServiceBinding>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<ResourcePool>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Route>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<Service>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<SymmetricKey>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<WorkloadGroup>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Database Generation\ExtendedObject <<XMLSchemaCollection>>] -Create=Yes -Drop=Yes - -[FolderOptions\Physical Objects\Test Data] -GenDataPathName= -GenDataSinglefile=Yes -GenDataScriptName=testdata -GenDataScriptName0= -GenDataScriptName1= -GenDataScriptName2= -GenDataScriptName3= -GenDataScriptName4= -GenDataScriptName5= -GenDataScriptName6= -GenDataScriptName7= -GenDataScriptName8= -GenDataScriptName9= -GenDataOdbc=0 -GenDataDelOld=No -GenDataTitle=No -GenDataDefNumRows=20 -GenDataCommit=0 -GenDataPacket=0 -GenDataOwner=No -GenDataProfNumb= -GenDataProfChar= -GenDataProfDate= -GenDataCSVSeparator=, -GenDataFileFormat=CSV -GenDataUseWizard=No - -[FolderOptions\Pdm] -IndxIQName=%COLUMN%_%INDEXTYPE% -IndxPK=Yes -IndxFK=Yes -IndxAK=Yes -IndxPKName=%TABLE%_PK -IndxFKName=%REFR%_FK -IndxAKName=%AKEY%_AK -IndxPreserve=No -IndxThreshold=0 -IndxStats=No -RefrPreserve=No -JidxPreserve=No -RbldMultiFact=Yes -RbldMultiDim=Yes -RbldMultiJidx=Yes -CubePreserve=No -TablStProcPreserve=No -ProcDepPreserve=Yes -TrgrDepPreserve=Yes -CubeScriptPath= -CubeScriptCase=0 -CubeScriptEncoding=ANSI -CubeScriptNacct=No -CubeScriptHeader=No -CubeScriptExt=csv -CubeScriptExt0=txt -CubeScriptExt1= -CubeScriptExt2= -CubeScriptSep=, -CubeScriptDeli=" -EstimationYears=0 -DfltDomnName=D_%.U:VALUE% -DfltColnName=D_%.U:VALUE% -DfltReuse=Yes -DfltDrop=Yes - -[FolderOptions\CheckModel] - -[FolderOptions\CheckModel\Package] - -[FolderOptions\CheckModel\Package\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\CheckPackageMissTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\DefaultCheckPackageMissTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\CircularReference] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\ConstraintName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\CnstMaxLen] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\CircularDependency] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Package\ShortcutUniqCode] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Table] - -[FolderOptions\CheckModel\Table\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\UniqIndex] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\MaxLen - NAME] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\EmptyColl - COLNCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\EmptyColl - INDXCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\EmptyColl - KEYCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\SerialColumnNumber] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\EmptyCollYesYes] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\TableIndexes] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\Mapping] -CheckSeverity=No -FixRequested=Yes -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\MappingSFMap] -CheckSeverity=No -FixRequested=Yes -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\EmptyColl - PERMCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Table\CheckTablePartitionKey] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckTableStartDate] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckTableRefNoLifecycle] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckTableSourceMapping] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckTablePartialColumnMapping] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckTableKeyColumnMapping] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\CheckTableNotOnLifecycleTablespace] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table\MSSQLSRV2012_Table_Table_Timestamp_Uniqueness] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column] - -[FolderOptions\CheckModel\Table.Column\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\DomainDivergence] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\ColumnMandatory] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckNumParam] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckPrecSupLng] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckUndefDttp] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\FkeyDttpDivergence] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\FkeyCheckDivergence] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\ColnSqncNoKey] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\ColnSqncDttp] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\SerialColumnFK] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\ColumnCompExpr] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckColumnOneToOneMapping] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckColumnDataTypeMapping] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckColumnNoMapping] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\CheckDttpIncompatibleFormat] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Column\MSSQLSRV2012_Column_Identity_Seed_and_Increment_validity] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index] - -[FolderOptions\CheckModel\Table.Index\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\EmptyColl - CIDXCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\UndefIndexType] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\IndexColumnCount] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\IQIndxHNGUniq] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\CheckIncludeColl - Tabl] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Index\MSSQLSRV2012_BaseIndex_XML_index_specific_checks] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key] - -[FolderOptions\CheckModel\Table.Key\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\EmptyColl - COLNCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\CheckIncludeColl - Tabl] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Key\MultiKeySqnc] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger] - -[FolderOptions\CheckModel\Table.Trigger\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table.Trigger\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index] - -[FolderOptions\CheckModel\Join Index\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Join Index\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View] - -[FolderOptions\CheckModel\View\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View\EmptyColl - PERMCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\View.View Index] - -[FolderOptions\CheckModel\View.View Index\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\EmptyColl - CIDXCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\IndexColumnCount] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\CheckIncludeColl - Tabl] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View.View Index\MSSQLSRV2012_BaseIndex_XML_index_specific_checks] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference] - -[FolderOptions\CheckModel\Reference\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\Reflexive] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\EmptyColl - RFJNCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\IncompleteJoin] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Reference\JoinOrder] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference] - -[FolderOptions\CheckModel\View Reference\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\View Reference\EmptyColl - VRFJNCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain] - -[FolderOptions\CheckModel\Domain\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\CheckNumParam] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\CheckPrecSupLng] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\CheckUndefDttp] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\CheckDttpIncompatibleFormat] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Domain\MSSQLSRV2012_PhysicalDomain_Domain_missing_default_object] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default] - -[FolderOptions\CheckModel\Default\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\DfltValeEmpty] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Default\DfltSameVale] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User] - -[FolderOptions\CheckModel\User\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\User\UniquePassword] -CheckSeverity=No -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Group] - -[FolderOptions\CheckModel\Group\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\EmptyColl - USERCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Group\UniquePassword] -CheckSeverity=No -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Role] - -[FolderOptions\CheckModel\Role\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Role\EmptyColl - USERCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure] - -[FolderOptions\CheckModel\Procedure\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\ProcBodyEmpty] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Procedure\EmptyColl - PERMCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\DBMS Trigger] - -[FolderOptions\CheckModel\DBMS Trigger\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\DbmsTriggerEvent] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\DBMS Trigger\MSSQLSRV2012_DBMSTrigger_Logon_must_be_unique_trigger_event] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source] - -[FolderOptions\CheckModel\Data Source\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\EmptyColl - MODLSRC] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\DtscTargets] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Source\CheckDataSourceModels] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Horizontal Partitioning] - -[FolderOptions\CheckModel\Horizontal Partitioning\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Horizontal Partitioning\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Horizontal Partitioning\EmptyColl - PARTCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Horizontal Partitioning\TargetTables] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Vertical Partitioning] - -[FolderOptions\CheckModel\Vertical Partitioning\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Vertical Partitioning\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Vertical Partitioning\EmptyColl - PARTCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Vertical Partitioning\TargetTables] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table Collapsing] - -[FolderOptions\CheckModel\Table Collapsing\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table Collapsing\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table Collapsing\EmptyColl - TargetTable] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Table Collapsing\TargetTables] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact] - -[FolderOptions\CheckModel\Fact\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\EmptyColl - MEASCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\Mapping] -CheckSeverity=No -FixRequested=Yes -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\MappingSFMap] -CheckSeverity=No -FixRequested=Yes -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\EmptyColl - ALLOLINKCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact\CubeDupAssociation] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension] - -[FolderOptions\CheckModel\Dimension\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\EmptyColl - DATTRCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\EmptyColl - HIERCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\DimnDupHierarchy] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\DimnDefHierarchy] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\Mapping] -CheckSeverity=No -FixRequested=Yes -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\MappingSFMap] -CheckSeverity=No -FixRequested=Yes -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension\SerialColumnNumber] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Association] - -[FolderOptions\CheckModel\Association\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Association\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Association\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Association\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Association\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Association\EmptyColl - Hierarchy] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute] - -[FolderOptions\CheckModel\Dimension.Attribute\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Attribute\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure] - -[FolderOptions\CheckModel\Fact.Measure\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Fact.Measure\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy] - -[FolderOptions\CheckModel\Dimension.Hierarchy\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Dimension.Hierarchy\EmptyColl - DATTRCOL] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym] - -[FolderOptions\CheckModel\Synonym\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\MaxLen - NAME] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Synonym\EmptyColl - BASEOBJ] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type] - -[FolderOptions\CheckModel\Abstract Data Type\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\AdtInstantiable] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type\AdtAbstractUsed] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure] - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\AdtProcUniqName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\UniqueDefinition] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Abstract Data Type.Abstract Data Type Procedure\ReturnDataType] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package] - -[FolderOptions\CheckModel\Database Package\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\MaxLen - NAME] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\EmptyColl - PROCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package\EmptyColl - CURCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Database Package\EmptyColl - VARCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Database Package\EmptyColl - TYPCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Database Package\EmptyColl - EXCCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Database Package.Database Package Procedure] - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\UniqueDefinition] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\EmptyColl - PARM] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Database Package.Database Package Procedure\ReturnDataType] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence] - -[FolderOptions\CheckModel\Sequence\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Sequence\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor] - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\UniqueDefinition] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\ReturnDataType] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Cursor\EmptyColl - PARM] -CheckSeverity=Yes -FixRequested=No -CheckRequested=No - -[FolderOptions\CheckModel\Database Package.Database Package Variable] - -[FolderOptions\CheckModel\Database Package.Database Package Variable\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Variable\CheckUndefDttp] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type] - -[FolderOptions\CheckModel\Database Package.Database Package Type\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Type\UniqueDefinition] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception] - -[FolderOptions\CheckModel\Database Package.Database Package Exception\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database Package.Database Package Exception\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace] - -[FolderOptions\CheckModel\Tablespace\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Tablespace\IsObjectUsed] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage] - -[FolderOptions\CheckModel\Storage\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Storage\IsObjectUsed] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database] - -[FolderOptions\CheckModel\Database\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Database\IsObjectUsed] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service] - -[FolderOptions\CheckModel\Web Service\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation] - -[FolderOptions\CheckModel\Web Service.Web Operation\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Web Service.Web Operation\MaxLen - CODE] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle] - -[FolderOptions\CheckModel\Lifecycle\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\CheckLifecyclePhase] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\CheckLifecycleRetention] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle\CheckPartitionRange] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase] - -[FolderOptions\CheckModel\Lifecycle.Phase\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseTbspace] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseIQTbspace] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseDuplicateTbspace] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseTbspaceCurrency] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseRetention] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseIdlePeriod] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseDataSource] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Lifecycle.Phase\CheckPhaseExternalOnFirst] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Replication] - -[FolderOptions\CheckModel\Replication\PartialReplication] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule] - -[FolderOptions\CheckModel\Business Rule\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Business Rule\EmptyColl - OBJCOL] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object] - -[FolderOptions\CheckModel\Extended Object\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Object\MSSQLSRV2012_ExtendedObject_Queue_Valid_maximum_number_of_readers_] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link] - -[FolderOptions\CheckModel\Extended Link\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Extended Link\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File] - -[FolderOptions\CheckModel\File\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\File\CheckPathExists] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format] - -[FolderOptions\CheckModel\Data Format\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Data Format\CheckDataFormatNullExpression] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model] - -[FolderOptions\CheckModel\Physical Data Model\CheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\CheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\DefaultCheckUseOnlyTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\DefaultCheckUseTermBySynonym] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\NotApprovedTerms] -CheckSeverity=Yes -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\CheckPackageMissTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\DefaultCheckPackageMissTerms] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\UniqueName] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\UniqueCode] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes - -[FolderOptions\CheckModel\Physical Data Model\MSSQLSRV2012_Model_Storage_requires_a_database_] -CheckSeverity=No -FixRequested=No -CheckRequested=Yes -[ModelOptions] - -[ModelOptions\Physical Objects] -CaseSensitive=No -DisplayName=Yes -EnableTrans=No -UseTerm=No -EnableRequirements=No -EnableFullShortcut=Yes -DefaultDttp= -IgnoreOwner=No -RebuildTrigger=Yes -RefrUnique=No -RefrAutoMigrate=Yes -RefrMigrateReuse=Yes -RefrMigrateDomain=Yes -RefrMigrateCheck=Yes -RefrMigrateRule=Yes -RefrMigrateExtd=No -RefrMigrDefaultLink=No -RefrDfltImpl=D -RefrPrgtColn=No -RefrMigrateToEnd=No -RebuildTriggerDep=No -ColnFKName=%.3:PARENT%_%COLUMN% -ColnFKNameUse=No -DomnCopyDttp=Yes -DomnCopyChck=No -DomnCopyRule=No -DomnCopyMand=No -DomnCopyExtd=No -DomnCopyProf=No -Notation=0 -DomnDefaultMandatory=No -ColnDefaultMandatory=No -TablDefaultOwner= -ViewDefaultOwner= -TrgrDefaultOwnerTabl= -TrgrDefaultOwnerView= -IdxDefaultOwnerTabl= -IdxDefaultOwnerView= -JdxDefaultOwner= -DBPackDefaultOwner= -SeqDefaultOwner= -ProcDefaultOwner= -DBMSTrgrDefaultOwner= -Currency=USD -RefrDeleteConstraint=0 -RefrUpdateConstraint=0 -RefrParentMandatory=No -RefrParentChangeAllow=Yes -RefrCheckOnCommit=No - -[ModelOptions\Physical Objects\NamingOptionsTemplates] - -[ModelOptions\Physical Objects\ClssNamingOptions] - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMPCKG] - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMPCKG\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMPCKG\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMDOMN] - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMDOMN\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\PDMDOMN\Code] -Template= -MaxLen=128 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\TABL] - -[ModelOptions\Physical Objects\ClssNamingOptions\TABL\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\TABL\Code] -Template= -MaxLen=128 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\COLN] - -[ModelOptions\Physical Objects\ClssNamingOptions\COLN\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\COLN\Code] -Template= -MaxLen=128 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\INDX] - -[ModelOptions\Physical Objects\ClssNamingOptions\INDX\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\INDX\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\REFR] - -[ModelOptions\Physical Objects\ClssNamingOptions\REFR\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\REFR\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VREF] - -[ModelOptions\Physical Objects\ClssNamingOptions\VREF\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VREF\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEW] - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEW\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEW\Code] -Template= -MaxLen=128 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEWC] - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEWC\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\VIEWC\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBSERV] - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBSERV\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBSERV\Code] -Template= -MaxLen=254 -Case=M -ValidChar='a'-'z','A'-'Z','0'-'9',"/-_.!~*'()" -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBOP] - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBOP\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WEBOP\Code] -Template= -MaxLen=254 -Case=M -ValidChar='a'-'z','A'-'Z','0'-'9',"/-_.!~*'()" -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WPARAM] - -[ModelOptions\Physical Objects\ClssNamingOptions\WPARAM\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\WPARAM\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FACT] - -[ModelOptions\Physical Objects\ClssNamingOptions\FACT\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FACT\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DIMN] - -[ModelOptions\Physical Objects\ClssNamingOptions\DIMN\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DIMN\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\MEAS] - -[ModelOptions\Physical Objects\ClssNamingOptions\MEAS\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\MEAS\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DATTR] - -[ModelOptions\Physical Objects\ClssNamingOptions\DATTR\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DATTR\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FILO] - -[ModelOptions\Physical Objects\ClssNamingOptions\FILO\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FILO\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMEOBJ] - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMEOBJ\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMEOBJ\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMELNK] - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMELNK\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\FRMELNK\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DefaultClass] - -[ModelOptions\Physical Objects\ClssNamingOptions\DefaultClass\Name] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Physical Objects\ClssNamingOptions\DefaultClass\Code] -Template= -MaxLen=254 -Case=M -ValidChar= -InvldChar= -AllValid=Yes -NoAccent=No -DefaultChar= -Script= -ConvTable= -ConvTablePath=%_HOME%\Resource Files\Conversion Tables - -[ModelOptions\Connection] - -[ModelOptions\Pdm] - -[ModelOptions\Generate] - -[ModelOptions\Generate\Xsm] -GenRootElement=Yes -GenComplexType=No -GenAttribute=Yes -CheckModel=Yes -SaveLinks=Yes -ORMapping=No -NameToCode=No - -[ModelOptions\Generate\Pdm] -RRMapping=No -PreserveMode=Yes -CheckModel=Yes -SaveLinks=Yes -NameToCode=No -EnableTransformations=No - -[ModelOptions\Generate\Cdm] -CheckModel=Yes -SaveLinks=Yes -NameToCode=No -Notation=2 - -[ModelOptions\Generate\Oom] -CheckModel=Yes -SaveLinks=Yes -ORMapping=No -NameToCode=Yes -ClassPrefix= - -[ModelOptions\Generate\Ldm] -CheckModel=Yes -SaveLinks=Yes -NameToCode=No - -[ModelOptions\Default Opts] - -[ModelOptions\Default Opts\TABL] -PhysOpts= - -[ModelOptions\Default Opts\COLN] -PhysOpts= - -[ModelOptions\Default Opts\INDX] -PhysOpts= - -[ModelOptions\Default Opts\AKEY] -PhysOpts= - -[ModelOptions\Default Opts\PKEY] -PhysOpts= - -[ModelOptions\Default Opts\STOR] -PhysOpts= - -[ModelOptions\Default Opts\TSPC] -PhysOpts= - -[ModelOptions\Default Opts\SQNC] -PhysOpts= - -[ModelOptions\Default Opts\DTBS] -PhysOpts= - -[ModelOptions\Default Opts\USER] -PhysOpts= - -[ModelOptions\Default Opts\JIDX] -PhysOpts= - -[ModelOptions\Default Opts\FRMEOBJ<<ResourcePool>>] -PhysOpts= - -[ModelOptions\Default Opts\FRMEOBJ<<WorkloadGroup>>] -PhysOpts= - -[ModelOptions\Default Opts\FRMESOB<<ResourcePool>>] -PhysOpts= - -[ModelOptions\Default Opts\FRMESOB<<WorkloadGroup>>] -PhysOpts= - - -14ADDF71-0085-4260-A4E4-A1BAAEB38F59 -Microsoft SQL Server 2012 -MSSQLSRV2012 -1502368828 -Administrator -1502368828 -Administrator - -11D14009-FEF7-43A3-BC76-BE5B34F11D08 -4BA9F647-DAB1-11D1-9944-006097355D9B - - - - -92360091-5759-4724-BE43-894656D9A995 -Order -Order -1502368829 -Administrator -1502807435 -Administrator -[DisplayPreferences] - -[DisplayPreferences\PDM] - -[DisplayPreferences\General] -Adjust to text=Yes -Snap Grid=No -Constrain Labels=Yes -Display Grid=No -Show Page Delimiter=Yes -Show Links intersections=No -Activate automatic link routing=No -Grid size=0 -Graphic unit=2 -Window color=255, 255, 255 -Background image= -Background mode=8 -Watermark image= -Watermark mode=8 -Show watermark on screen=No -Gradient mode=0 -Gradient end color=255, 255, 255 -Show Swimlane=No -SwimlaneVert=Yes -TreeVert=No -CompDark=0 - -[DisplayPreferences\Object] -Show Icon=No -Mode=2 -Trunc Length=40 -Word Length=40 -Word Text=!"#$%&')*+,-./:;=>?@\]^_`|}~ -Shortcut IntIcon=Yes -Shortcut IntLoct=Yes -Shortcut IntFullPath=No -Shortcut IntLastPackage=Yes -Shortcut ExtIcon=Yes -Shortcut ExtLoct=No -Shortcut ExtFullPath=No -Shortcut ExtLastPackage=Yes -Shortcut ExtIncludeModl=Yes -EObjShowStrn=Yes -ExtendedObject.Comment=No -ExtendedObject.IconPicture=No -ExtendedObject.TextStyle=No -ExtendedObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Object Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -ELnkShowStrn=Yes -ELnkShowName=Yes -ExtendedLink_SymbolLayout=<Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Source" >[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] </Form>[CRLF]</Form> -FileObject.Stereotype=No -FileObject.DisplayName=Yes -FileObject.LocationOrName=No -FileObject.IconPicture=No -FileObject.TextStyle=No -FileObject.IconMode=Yes -FileObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Location" Attribute="LocationOrName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Package.Stereotype=Yes -Package.Comment=No -Package.IconPicture=No -Package.TextStyle=No -Package_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Display Model Version=Yes -Table.Stereotype=Yes -Table.DisplayName=Yes -Table.OwnerDisplayName=No -Table.Columns=Yes -Table.Columns._Filter="All Columns" PDMCOLNALL -Table.Columns._Columns=Stereotype DataType KeyIndicator -Table.Columns._Limit=-5 -Table.Keys=No -Table.Keys._Columns=Stereotype Indicator -Table.Indexes=No -Table.Indexes._Columns=Stereotype -Table.Triggers=No -Table.Triggers._Columns=Stereotype -Table.Comment=No -Table.IconPicture=No -Table.TextStyle=No -Table_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="Stereotype No\r\nDisplayName Yes\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nDomain No\r\nKeyIndicator No\r\nIndexIndicator No\r\nNullStatus No" Filters="&quot;All Columns&quot; PDMCOLNALL &quot;&quot;\r\n&quot;PK Columns&quot; PDMCOLNPK &quot;\&quot;PRIM \&quot;TRUE\&quot; TRUE\&quot;&quot;\r\n&quot;Key Columns&quot; PDMCOLNKEY &quot;\&quot;KEYS \&quot;TRUE\&quot; TRUE\&quot;&quot;" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Keys" Collection="Keys" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Triggers" Collection="Triggers" Columns="Stereotype No\r\nDisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -View.Stereotype=Yes -View.DisplayName=Yes -View.OwnerDisplayName=No -View.Columns=Yes -View.Columns._Columns=DisplayName -View.Columns._Limit=-5 -View.TemporaryVTables=Yes -View.Indexes=No -View.Comment=No -View.IconPicture=No -View.TextStyle=No -View_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="DisplayName No\r\nExpression No\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nIndexIndicator No" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Tables" Collection="TemporaryVTables" Columns="Name Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="DisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Procedure.Stereotype=No -Procedure.DisplayName=Yes -Procedure.OwnerDisplayName=No -Procedure.Comment=No -Procedure.IconPicture=No -Procedure.TextStyle=No -Procedure_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Reference.Cardinality=No -Reference.ImplementationType=No -Reference.ChildRole=Yes -Reference.Stereotype=Yes -Reference.DisplayName=No -Reference.ForeignKeyConstraintName=No -Reference.JoinExpression=No -Reference.Integrity=No -Reference.ParentRole=Yes -Reference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Cardinality" Attribute="Cardinality" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Implementation" Attribute="ImplementationType" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Cons&amp;traint Name" Attribute="ForeignKeyConstraintName" Prefix="" Suffix="" Caption="Cons&amp;traint Name" Mandatory="No" />[CRLF] <StandardAttribute Name="Join" Attribute="JoinExpression" Prefix="" Suffix="" Caption="Join" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Referential integrity" Attribute="Integrity" Prefix="" Suffix="" Caption="Referential integrity" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> -ViewReference.ChildRole=Yes -ViewReference.Stereotype=Yes -ViewReference.DisplayName=No -ViewReference.JoinExpression=No -ViewReference.ParentRole=Yes -ViewReference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Join Expression" Attribute="JoinExpression" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> - -[DisplayPreferences\Symbol] - -[DisplayPreferences\Symbol\FRMEOBJ] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=6000 -Height=2000 -Brush color=255 255 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=64 -Brush gradient color=192 192 192 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 255 128 128 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FRMELNK] -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FILO] -OBJSTRNFont=新宋体,8,N -OBJSTRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LCNMFont=新宋体,8,N -LCNMFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=3600 -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PDMPCKG] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 178 178 178 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\TABL] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -KeysFont=新宋体,8,N -KeysFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -TriggersFont=新宋体,8,N -TriggersFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=178 214 252 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VIEW] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -TemporaryVTablesFont=新宋体,8,N -TemporaryVTablesFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=208 208 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PROC] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4000 -Height=1000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 108 0 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\REFR] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VREF] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\USRDEPD] -OBJXSTRFont=新宋体,8,N -OBJXSTRFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=2 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\Free Symbol] -Free TextFont=新宋体,8,N -Free TextFont color=0, 0, 0 -Line style=0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 -(8268, 11693) -((315,354), (433,354)) -1 -7 - - -1502807428 -1502807598 -((-5249,-9277), (-4799,-2683)) -((-5024,-2683),(-5024,-9277)) -1 -1 -12615680 -12632256 -CENTER 0 新宋体,8,N -SOURCE 0 新宋体,8,N -DESTINATION 0 新宋体,8,N - - - - - - - - - - - -1502807435 -1502807435 -((-3717,2041), (-3267,8646)) -((-3492,2041),(-3492,8646)) -1 -1 -12615680 -12632256 -CENTER 0 新宋体,8,N -SOURCE 0 新宋体,8,N -DESTINATION 0 新宋体,8,N - - - - - - - - - - - -1502368959 -1502369707 --1 -((-11260,6510), (7534,11634)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - -1502807154 -1502807593 --1 -((-11088,-3939), (10170,3194)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - -1502807407 -1502810053 --1 -((-10585,-13983), (8209,-6187)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - - - -DE41247D-905B-4995-863B-693380B257D6 -Product -Product -1502613808 -Administrator -1502613840 -Administrator -[DisplayPreferences] - -[DisplayPreferences\PDM] - -[DisplayPreferences\General] -Adjust to text=Yes -Snap Grid=No -Constrain Labels=Yes -Display Grid=No -Show Page Delimiter=Yes -Show Links intersections=No -Activate automatic link routing=No -Grid size=0 -Graphic unit=2 -Window color=255, 255, 255 -Background image= -Background mode=8 -Watermark image= -Watermark mode=8 -Show watermark on screen=No -Gradient mode=0 -Gradient end color=255, 255, 255 -Show Swimlane=No -SwimlaneVert=Yes -TreeVert=No -CompDark=0 - -[DisplayPreferences\Object] -Show Icon=No -Mode=2 -Trunc Length=40 -Word Length=40 -Word Text=!"#$%&')*+,-./:;=>?@\]^_`|}~ -Shortcut IntIcon=Yes -Shortcut IntLoct=Yes -Shortcut IntFullPath=No -Shortcut IntLastPackage=Yes -Shortcut ExtIcon=Yes -Shortcut ExtLoct=No -Shortcut ExtFullPath=No -Shortcut ExtLastPackage=Yes -Shortcut ExtIncludeModl=Yes -EObjShowStrn=Yes -ExtendedObject.Comment=No -ExtendedObject.IconPicture=No -ExtendedObject.TextStyle=No -ExtendedObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Object Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -ELnkShowStrn=Yes -ELnkShowName=Yes -ExtendedLink_SymbolLayout=<Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Source" >[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] </Form>[CRLF]</Form> -FileObject.Stereotype=No -FileObject.DisplayName=Yes -FileObject.LocationOrName=No -FileObject.IconPicture=No -FileObject.TextStyle=No -FileObject.IconMode=Yes -FileObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Location" Attribute="LocationOrName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Package.Stereotype=Yes -Package.Comment=No -Package.IconPicture=No -Package.TextStyle=No -Package_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Display Model Version=Yes -Table.Stereotype=Yes -Table.DisplayName=Yes -Table.OwnerDisplayName=No -Table.Columns=Yes -Table.Columns._Filter="All Columns" PDMCOLNALL -Table.Columns._Columns=Stereotype DataType KeyIndicator -Table.Columns._Limit=-5 -Table.Keys=No -Table.Keys._Columns=Stereotype Indicator -Table.Indexes=No -Table.Indexes._Columns=Stereotype -Table.Triggers=No -Table.Triggers._Columns=Stereotype -Table.Comment=No -Table.IconPicture=No -Table.TextStyle=No -Table_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="Stereotype No\r\nDisplayName Yes\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nDomain No\r\nKeyIndicator No\r\nIndexIndicator No\r\nNullStatus No" Filters="&quot;All Columns&quot; PDMCOLNALL &quot;&quot;\r\n&quot;PK Columns&quot; PDMCOLNPK &quot;\&quot;PRIM \&quot;TRUE\&quot; TRUE\&quot;&quot;\r\n&quot;Key Columns&quot; PDMCOLNKEY &quot;\&quot;KEYS \&quot;TRUE\&quot; TRUE\&quot;&quot;" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Keys" Collection="Keys" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Triggers" Collection="Triggers" Columns="Stereotype No\r\nDisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -View.Stereotype=Yes -View.DisplayName=Yes -View.OwnerDisplayName=No -View.Columns=Yes -View.Columns._Columns=DisplayName -View.Columns._Limit=-5 -View.TemporaryVTables=Yes -View.Indexes=No -View.Comment=No -View.IconPicture=No -View.TextStyle=No -View_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="DisplayName No\r\nExpression No\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nIndexIndicator No" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Tables" Collection="TemporaryVTables" Columns="Name Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="DisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Procedure.Stereotype=No -Procedure.DisplayName=Yes -Procedure.OwnerDisplayName=No -Procedure.Comment=No -Procedure.IconPicture=No -Procedure.TextStyle=No -Procedure_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Reference.Cardinality=No -Reference.ImplementationType=No -Reference.ChildRole=Yes -Reference.Stereotype=Yes -Reference.DisplayName=No -Reference.ForeignKeyConstraintName=No -Reference.JoinExpression=No -Reference.Integrity=No -Reference.ParentRole=Yes -Reference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Cardinality" Attribute="Cardinality" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Implementation" Attribute="ImplementationType" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Cons&amp;traint Name" Attribute="ForeignKeyConstraintName" Prefix="" Suffix="" Caption="Cons&amp;traint Name" Mandatory="No" />[CRLF] <StandardAttribute Name="Join" Attribute="JoinExpression" Prefix="" Suffix="" Caption="Join" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Referential integrity" Attribute="Integrity" Prefix="" Suffix="" Caption="Referential integrity" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> -ViewReference.ChildRole=Yes -ViewReference.Stereotype=Yes -ViewReference.DisplayName=No -ViewReference.JoinExpression=No -ViewReference.ParentRole=Yes -ViewReference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Join Expression" Attribute="JoinExpression" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> - -[DisplayPreferences\Symbol] - -[DisplayPreferences\Symbol\FRMEOBJ] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=6000 -Height=2000 -Brush color=255 255 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=64 -Brush gradient color=192 192 192 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 255 128 128 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FRMELNK] -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FILO] -OBJSTRNFont=新宋体,8,N -OBJSTRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LCNMFont=新宋体,8,N -LCNMFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=3600 -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PDMPCKG] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 178 178 178 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\TABL] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -KeysFont=新宋体,8,N -KeysFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -TriggersFont=新宋体,8,N -TriggersFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=178 214 252 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VIEW] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -TemporaryVTablesFont=新宋体,8,N -TemporaryVTablesFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=208 208 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PROC] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4000 -Height=1000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 108 0 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\REFR] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VREF] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\USRDEPD] -OBJXSTRFont=新宋体,8,N -OBJXSTRFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=2 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\Free Symbol] -Free TextFont=新宋体,8,N -Free TextFont color=0, 0, 0 -Line style=0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 -(8268, 11693) -((315,354), (433,354)) -1 -7 - - -1502613840 -1502614067 --1 -((-9995,-2970), (8799,3952)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - - - -68F8168C-38E6-4220-9B3C-0FA0C72EE518 -Customer -Customer -1503007461 -Administrator -1503007472 -Administrator -[DisplayPreferences] - -[DisplayPreferences\PDM] - -[DisplayPreferences\General] -Adjust to text=Yes -Snap Grid=No -Constrain Labels=Yes -Display Grid=No -Show Page Delimiter=Yes -Show Links intersections=No -Activate automatic link routing=No -Grid size=0 -Graphic unit=2 -Window color=255, 255, 255 -Background image= -Background mode=8 -Watermark image= -Watermark mode=8 -Show watermark on screen=No -Gradient mode=0 -Gradient end color=255, 255, 255 -Show Swimlane=No -SwimlaneVert=Yes -TreeVert=No -CompDark=0 - -[DisplayPreferences\Object] -Show Icon=No -Mode=2 -Trunc Length=40 -Word Length=40 -Word Text=!"#$%&')*+,-./:;=>?@\]^_`|}~ -Shortcut IntIcon=Yes -Shortcut IntLoct=Yes -Shortcut IntFullPath=No -Shortcut IntLastPackage=Yes -Shortcut ExtIcon=Yes -Shortcut ExtLoct=No -Shortcut ExtFullPath=No -Shortcut ExtLastPackage=Yes -Shortcut ExtIncludeModl=Yes -EObjShowStrn=Yes -ExtendedObject.Comment=No -ExtendedObject.IconPicture=No -ExtendedObject.TextStyle=No -ExtendedObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Object Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -ELnkShowStrn=Yes -ELnkShowName=Yes -ExtendedLink_SymbolLayout=<Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Source" >[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] </Form>[CRLF]</Form> -FileObject.Stereotype=No -FileObject.DisplayName=Yes -FileObject.LocationOrName=No -FileObject.IconPicture=No -FileObject.TextStyle=No -FileObject.IconMode=Yes -FileObject_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Location" Attribute="LocationOrName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Package.Stereotype=Yes -Package.Comment=No -Package.IconPicture=No -Package.TextStyle=No -Package_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Display Model Version=Yes -Table.Stereotype=Yes -Table.DisplayName=Yes -Table.OwnerDisplayName=No -Table.Columns=Yes -Table.Columns._Filter="All Columns" PDMCOLNALL -Table.Columns._Columns=Stereotype DataType KeyIndicator -Table.Columns._Limit=-5 -Table.Keys=No -Table.Keys._Columns=Stereotype Indicator -Table.Indexes=No -Table.Indexes._Columns=Stereotype -Table.Triggers=No -Table.Triggers._Columns=Stereotype -Table.Comment=No -Table.IconPicture=No -Table.TextStyle=No -Table_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="Stereotype No\r\nDisplayName Yes\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nDomain No\r\nKeyIndicator No\r\nIndexIndicator No\r\nNullStatus No" Filters="&quot;All Columns&quot; PDMCOLNALL &quot;&quot;\r\n&quot;PK Columns&quot; PDMCOLNPK &quot;\&quot;PRIM \&quot;TRUE\&quot; TRUE\&quot;&quot;\r\n&quot;Key Columns&quot; PDMCOLNKEY &quot;\&quot;KEYS \&quot;TRUE\&quot; TRUE\&quot;&quot;" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Keys" Collection="Keys" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="Stereotype No\r\nDisplayName Yes\r\nIndicator No" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Triggers" Collection="Triggers" Columns="Stereotype No\r\nDisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -View.Stereotype=Yes -View.DisplayName=Yes -View.OwnerDisplayName=No -View.Columns=Yes -View.Columns._Columns=DisplayName -View.Columns._Limit=-5 -View.TemporaryVTables=Yes -View.Indexes=No -View.Comment=No -View.IconPicture=No -View.TextStyle=No -View_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardCollection Name="Columns" Collection="Columns" Columns="DisplayName No\r\nExpression No\r\nDataType No\r\nSymbolDataType No &quot;Domain or Data type&quot;\r\nIndexIndicator No" HasLimit="Yes" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Tables" Collection="TemporaryVTables" Columns="Name Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardCollection Name="Indexes" Collection="Indexes" Columns="DisplayName Yes" HasLimit="No" HideEmpty="No" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Procedure.Stereotype=No -Procedure.DisplayName=Yes -Procedure.OwnerDisplayName=No -Procedure.Comment=No -Procedure.IconPicture=No -Procedure.TextStyle=No -Procedure_SymbolLayout=<Form>[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="Yes" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Owner and Name" Attribute="OwnerDisplayName" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <Separator Name="Separator" />[CRLF] <StandardAttribute Name="Comment" Attribute="Comment" Prefix="" Suffix="" Alignment="LEFT" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Icon" Attribute="IconPicture" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF] <StandardAttribute Name="Force top align" Attribute="TextStyle" Prefix="" Suffix="" Alignment="CNTR" Caption="" Mandatory="Yes" />[CRLF]</Form> -Reference.Cardinality=No -Reference.ImplementationType=No -Reference.ChildRole=Yes -Reference.Stereotype=Yes -Reference.DisplayName=No -Reference.ForeignKeyConstraintName=No -Reference.JoinExpression=No -Reference.Integrity=No -Reference.ParentRole=Yes -Reference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Cardinality" Attribute="Cardinality" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Implementation" Attribute="ImplementationType" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Cons&amp;traint Name" Attribute="ForeignKeyConstraintName" Prefix="" Suffix="" Caption="Cons&amp;traint Name" Mandatory="No" />[CRLF] <StandardAttribute Name="Join" Attribute="JoinExpression" Prefix="" Suffix="" Caption="Join" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] <StandardAttribute Name="Referential integrity" Attribute="Integrity" Prefix="" Suffix="" Caption="Referential integrity" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> -ViewReference.ChildRole=Yes -ViewReference.Stereotype=Yes -ViewReference.DisplayName=No -ViewReference.JoinExpression=No -ViewReference.ParentRole=Yes -ViewReference_SymbolLayout=<Form>[CRLF] <Form Name="Source" >[CRLF] <StandardAttribute Name="Child Role" Attribute="ChildRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF] <Form Name="Center" >[CRLF] <StandardAttribute Name="Stereotype" Attribute="Stereotype" Prefix="&lt;&lt;" Suffix="&gt;&gt;" Caption="" Mandatory="No" />[CRLF] <ExclusiveChoice Name="Exclusive Choice" Mandatory="No" Display="HorizontalRadios" >[CRLF] <StandardAttribute Name="Name" Attribute="DisplayName" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] <StandardAttribute Name="Join Expression" Attribute="JoinExpression" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </ExclusiveChoice>[CRLF] </Form>[CRLF] <Form Name="Destination" >[CRLF] <StandardAttribute Name="Parent Role" Attribute="ParentRole" Prefix="" Suffix="" Caption="" Mandatory="No" />[CRLF] </Form>[CRLF]</Form> - -[DisplayPreferences\Symbol] - -[DisplayPreferences\Symbol\FRMEOBJ] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=6000 -Height=2000 -Brush color=255 255 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=64 -Brush gradient color=192 192 192 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 255 128 128 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FRMELNK] -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\FILO] -OBJSTRNFont=新宋体,8,N -OBJSTRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LCNMFont=新宋体,8,N -LCNMFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=3600 -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PDMPCKG] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 178 178 178 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\TABL] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -KeysFont=新宋体,8,N -KeysFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -TriggersFont=新宋体,8,N -TriggersFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=178 214 252 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VIEW] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -ColumnsFont=新宋体,8,N -ColumnsFont color=0, 0, 0 -TablePkColumnsFont=新宋体,8,U -TablePkColumnsFont color=0, 0, 0 -TableFkColumnsFont=新宋体,8,N -TableFkColumnsFont color=0, 0, 0 -TemporaryVTablesFont=新宋体,8,N -TemporaryVTablesFont color=0, 0, 0 -IndexesFont=新宋体,8,N -IndexesFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4800 -Height=4000 -Brush color=208 208 255 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\PROC] -STRNFont=新宋体,8,N -STRNFont color=0, 0, 0 -DISPNAMEFont=新宋体,8,N -DISPNAMEFont color=0, 0, 0 -OWNRDISPNAMEFont=新宋体,8,N -OWNRDISPNAMEFont color=0, 0, 0 -LABLFont=新宋体,8,N -LABLFont color=0, 0, 0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Width=4000 -Height=1000 -Brush color=255 255 192 -Fill Color=Yes -Brush style=6 -Brush bitmap mode=12 -Brush gradient mode=65 -Brush gradient color=255 255 255 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 108 0 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\REFR] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\VREF] -SOURCEFont=新宋体,8,N -SOURCEFont color=0, 0, 0 -CENTERFont=新宋体,8,N -CENTERFont color=0, 0, 0 -DESTINATIONFont=新宋体,8,N -DESTINATIONFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 128 128 192 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\USRDEPD] -OBJXSTRFont=新宋体,8,N -OBJXSTRFont color=0, 0, 0 -Line style=1 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=2 0 128 128 255 -Shadow color=192 192 192 -Shadow=0 - -[DisplayPreferences\Symbol\Free Symbol] -Free TextFont=新宋体,8,N -Free TextFont color=0, 0, 0 -Line style=0 -AutoAdjustToText=Yes -Keep aspect=No -Keep center=No -Keep size=No -Brush color=255 255 255 -Fill Color=Yes -Brush style=1 -Brush bitmap mode=12 -Brush gradient mode=0 -Brush gradient color=118 118 118 -Brush background image= -Custom shape= -Custom text mode=0 -Pen=1 0 0 0 255 -Shadow color=192 192 192 -Shadow=0 -(8268, 11693) -((315,354), (433,354)) -1 -7 - - -1503007472 -1503102523 --1 -((-13056,-66), (10598,13178)) -0 -12615680 -16570034 -12632256 -STRN 0 新宋体,8,N -DISPNAME 0 新宋体,8,N -OWNRDISPNAME 0 新宋体,8,N -Columns 0 新宋体,8,N -TablePkColumns 0 新宋体,8,U -TableFkColumns 0 新宋体,8,N -Keys 0 新宋体,8,N -Indexes 0 新宋体,8,N -Triggers 0 新宋体,8,N -LABL 0 新宋体,8,N -6 -65 -16777215 -1 - - - - - - - - - - - - -D792A2DB-5F90-499F-8B39-0AAFF559CD36 -Sales -Sales -1502368978 -Administrator -1502807211 -Administrator -Schema - - -8B9F6BB3-2312-48C1-8479-F48207BDE3BC -Productions -Productions -1502613870 -Administrator -1502613934 -Administrator -Schema - - -DFC6388B-EEDB-4184-94C6-68548BFC8ADC -Customers -Customers -1503008601 -Administrator -1503008677 -Administrator -Schema - - - - -DCFB7BAF-61C8-4927-8C69-7EC2C03A17BB -订单( Orders ) -Orders -1502368959 -Administrator -1502612809 -Administrator -订单 - - - -44A75471-51DC-4FF7-9579-4F96537D6184 -订单编号( OrderId ) -OrderId -1502369000 -Administrator -1502369037 -Administrator -订单编号 -uniqueidentifier -1 - - -64DB43B2-EFDA-499C-A1DF-F5D6A1F6FB0D -订单编码( Code ) -Code -1502369007 -Administrator -1502369564 -Administrator -订单编码 -nvarchar(30) -30 -1 - - -935D2B35-8FDD-4862-8E04-77E196989914 -订单名称( Name ) -Name -1502612591 -Administrator -1502612826 -Administrator -订单名称 -nvarchar(200) -200 -1 - - -F83621F4-DAA3-4F37-8FBC-EEFCECA527A8 -版本号( Version ) -Version -1408388616 -Administrator -1502369696 -Administrator -版本号 -timestamp - - - - -92740D57-C6F0-457B-8D5D-4845D070F063 -Key_1 -Key_1 -1502369007 -Administrator -1502369037 -Administrator - - - - - - - - - - - - - - - - -D181918A-EA17-4145-A88E-907A77FACA10 -商品( Products ) -Products -1502368959 -Administrator -1502810035 -Administrator -商品 - - - -B53960D7-4FB2-4954-BED8-14AB641761B2 -商品编号( ProductId ) -ProductId -1502369000 -Administrator -1502614868 -Administrator -商品编号 -int -1 - - -82CB28CE-8970-4273-8B82-05213E608D8F -商品编码( Code ) -Code -1502369007 -Administrator -1502614005 -Administrator -商品编码 -nvarchar(30) -30 -1 - - -8A8B240F-EB3A-417B-B551-A984300FD92F -商品名称( Name ) -Name -1502612591 -Administrator -1502614005 -Administrator -商品名称 -nvarchar(200) -200 -1 - - -E956A6FE-EF12-4FD1-A6F9-7A0B2C7E3149 -扩展属性( Extends ) -Extends -1502614005 -Administrator -1502614064 -Administrator -扩展属性 -nvarchar(max) - - -509CD7FA-70E7-4E7D-88E5-5D6468A73853 -价格( Price ) -Price -1502807514 -Administrator -1502810103 -Administrator -价格 -decimal(10,2) -10 -2 - - -C8C5119A-D798-456A-B4FD-CB35C05B804C -是否删除( IsDeleted ) -IsDeleted -1441104989 -Administrator -1502776824 -Administrator -是否删除 -bit -1 - - -269BD895-4B63-4E11-B8C1-EA90928F070F -版本号( Version ) -Version -1408388616 -Administrator -1502613840 -Administrator -版本号 -timestamp - - - - -B1442255-04B5-4E29-99AA-B7218DA5522E -Key_1 -Key_1 -1502369007 -Administrator -1502613840 -Administrator - - - - - - - - - - - - - - - - -BA70D8F5-EDAF-42FF-A493-ADD0F270786D -订单明细( OrderItems ) -OrderItems -1502807154 -Administrator -1502807526 -Administrator -订单明细 - - - -0CC5B156-1828-4743-A78A-1BA46EA6EF63 -订单明细编号( OrderItemId ) -OrderItemId -1502807211 -Administrator -1502807345 -Administrator -订单明细编号 -uniqueidentifier -1 - - -DFD8CD92-6982-4B46-AA1B-1F5C5ACEA826 -订单编号( OrderId ) -OrderId -1502807435 -Administrator -1502857657 -Administrator -订单编号 -uniqueidentifier -1 - - -258D4008-B597-465B-A18B-9230CF186B40 -商品编号( ProductId ) -ProductId -1502807428 -Administrator -1502807585 -Administrator -商品编号 -int -1 - - -36726035-757C-4149-9B60-C814BC94A7BE -商品名称( ProductName ) -ProductName -1502612591 -Administrator -1502808511 -Administrator -商品名称 -nvarchar(200) -200 -1 - - -40D46135-FFF4-46EF-B62C-D10D3368930C -数量( Quantity ) -Quantity -1502807446 -Administrator -1502807487 -Administrator -数量 -int -1 - - -B1C7C098-90D1-4B4C-BC25-06777E071830 -单价( Price ) -Price -1502807514 -Administrator -1502807567 -Administrator -单价 -decimal(10,2) -10 -2 -1 - - - - -0F965720-6103-46E8-8EE7-B48237AF21E8 -Key_1 -Key_1 -1502807341 -Administrator -1502857657 -Administrator - - - - - - - - - - - - - - - - -C5252905-0F60-4917-A321-FD71B172ACA0 -客户( Customers ) -Customers -1503007472 -Administrator -1503102513 -Administrator -客户 - - - -A823E497-5CB0-432E-BF50-FD37EBC2B648 -客户编号( CustomerId ) -CustomerId -1503008705 -Administrator -1503008831 -Administrator -客户编号 -nvarchar(50) -50 -1 - - -A58AEBD8-0122-43F0-813E-091BA1D20FED -客户名称( Name ) -Name -1502612591 -Administrator -1503009006 -Administrator -客户名称 -nvarchar(20) -20 -1 - - -26AF2F9E-4450-46ED-B9F3-00FDD4D69E87 -昵称( Nickname ) -Nickname -1347015477 -Administrator -1503009023 -Administrator -昵称 -nvarchar(30) -30 - - -A3AC3300-5774-4BA9-BDC3-2B833A31E13F -余额( Balance ) -Balance -1503065241 -Administrator -1503066488 -Administrator -余额 -decimal(18,2) -18 -2 -1 - - -B3906707-46B9-426E-A2F9-8D132FBE47BA -性别( Gender ) -Gender -1347015477 -Administrator -1503009032 -Administrator -性别 -int - - -422A0FBF-02EE-4C77-ACF3-0F6DFF20B846 -联系电话( Tel ) -Tel -1347015628 -Administrator -1503009090 -Administrator -联系电话 -nvarchar(20) -20 - - -8FA4B750-24CD-43B8-8397-F6D1EDECEBC9 -手机号( Mobile ) -Mobile -1347000983 -Administrator -1503009090 -Administrator -手机号 -nvarchar(20) -20 - - -94E1690C-B465-4C47-B724-B4199213FBC0 -电子邮件( Email ) -Email -1347000983 -Administrator -1503009103 -Administrator -电子邮件 -nvarchar(100) -100 - - -3DA62965-6815-4CF9-858E-6D5298D97075 -创建时间( CreationTime ) -CreationTime -1387961893 -Administrator -1503102513 -Administrator -创建时间 -datetime - - -98A5605B-3958-404E-9658-384E3363DF0E -创建人( CreatorId ) -CreatorId -1441284440 -Administrator -1503102513 -Administrator -创建人 -uniqueidentifier - - -7A4191EC-ADFF-4E5D-A672-9CCB5ECE907D -最后修改时间( LastModificationTime ) -LastModificationTime -1387961893 -Administrator -1503102513 -Administrator -最后修改时间 -datetime - - -4BAC5792-FF54-4C9B-AA55-32B63774DA44 -最后修改人( LastModifierId ) -LastModifierId -1441284440 -Administrator -1503102513 -Administrator -最后修改人 -uniqueidentifier - - -1CBFD184-6982-4407-BEBA-A169BD9AEADF -版本号( Version ) -Version -1408388616 -Administrator -1503008851 -Administrator -版本号 -timestamp - - - - -9AB7B83B-729A-4865-8A84-1A8038CA22D5 -Key_1 -Key_1 -1503008825 -Administrator -1503008831 -Administrator - - - - - - - - - - - - - - - - - - -E41C4795-1F29-4BDA-ACAF-614C9A3FB6A0 -Reference_1 -Reference_1 -1502807428 -Administrator -1502807428 -Administrator -0..* - - - - - - - - - - - -AE653EE6-E7FE-4276-BCFE-3F49F078877C -1502807428 -Administrator -1502807428 -Administrator - - - - - - - - - - -76ABB6B3-CE4D-496B-B6EA-8563C5C6E8BA -Reference_2 -Reference_2 -1502807435 -Administrator -1502807435 -Administrator -0..* - - - - - - - - - - - -5900BB99-C0D5-4638-B555-26BC4FCB28A9 -1502807435 -Administrator -1502807435 -Administrator - - - - - - - - - - - - -AFA9EDB8-8EE4-4FD5-8275-60A5D6D96228 -PUBLIC -PUBLIC -1502368828 -Administrator -1503008669 -Administrator - - - - - - - - - -B2E885F2-096F-4518-8A93-545A15C84563 -Microsoft SQL Server 2012 -MSSQLSRV2012 -1502368828 -Administrator -1514588405 -Administrator -file:///%_DBMS%/sqlsv2012.xdb -11D14009-FEF7-43A3-BC76-BE5B34F11D08 -4BA9F647-DAB1-11D1-9944-006097355D9B -1514556467 - - - - - - - - - - \ No newline at end of file diff --git a/test/Util.Datas.Tests.Integration/SqlServer/Scripts/database.sql b/test/Util.Datas.Tests.Integration/SqlServer/Scripts/database.sql deleted file mode 100644 index c175f3445..000000000 --- a/test/Util.Datas.Tests.Integration/SqlServer/Scripts/database.sql +++ /dev/null @@ -1,707 +0,0 @@ -/*========================================================== 1. 创建数据库 ===========================================================*/ -USE [master] -GO - ---删除数据库 -EXEC msdb.dbo.sp_delete_database_backuphistory @database_name = N'UtilTest' -GO -IF EXISTS (SELECT name FROM sys.databases WHERE name = N'UtilTest') -Begin -DROP DATABASE [UtilTest] -End -GO - ---创建数据库 -CREATE DATABASE [UtilTest] -GO - -/*========================================================== 2. 创建架构 ===========================================================*/ -USE [UtilTest] -GO - -/* 1. Sales */ -IF EXISTS (SELECT * FROM sys.schemas WHERE name = N'Sales') -DROP SCHEMA [Sales] -GO -CREATE SCHEMA [Sales] AUTHORIZATION [dbo] -GO - -/* 2. Productions */ -IF EXISTS (SELECT * FROM sys.schemas WHERE name = N'Productions') -DROP SCHEMA [Productions] -GO -CREATE SCHEMA [Productions] AUTHORIZATION [dbo] -GO - -/* 3. Customers */ -IF EXISTS (SELECT * FROM sys.schemas WHERE name = N'Customers') -DROP SCHEMA [Customers] -GO -CREATE SCHEMA [Customers] AUTHORIZATION [dbo] -GO - - -/*========================================================== 3. 创建表 ===========================================================*/ - -if exists (select 1 - from sysobjects - where id = object_id('Customers.Customers') - and type = 'U') - drop table Customers.Customers -go - -/*==============================================================*/ -/* Table: Customers */ -/*==============================================================*/ -create table Customers.Customers ( - CustomerId nvarchar(50) not null, - Name nvarchar(20) not null, - Nickname nvarchar(30) null, - Balance decimal(18,2) not null, - Gender int null, - Tel nvarchar(20) null, - Mobile nvarchar(20) null, - Email nvarchar(100) null, - CreationTime datetime null, - CreatorId uniqueidentifier null, - LastModificationTime datetime null, - LastModifierId uniqueidentifier null, - Version timestamp null, - constraint PK_CUSTOMERS primary key (CustomerId) -) -go - -if exists (select 1 from sys.extended_properties - where major_id = object_id('Customers.Customers') and minor_id = 0) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Customers', 'table', 'Customers' - -end - - -execute sp_addextendedproperty 'MS_Description', - '客户', - 'schema', 'Customers', 'table', 'Customers' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Customers.Customers') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'CustomerId') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Customers', 'table', 'Customers', 'column', 'CustomerId' - -end - - -execute sp_addextendedproperty 'MS_Description', - '客户编号', - 'schema', 'Customers', 'table', 'Customers', 'column', 'CustomerId' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Customers.Customers') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'Name') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Customers', 'table', 'Customers', 'column', 'Name' - -end - - -execute sp_addextendedproperty 'MS_Description', - '客户名称', - 'schema', 'Customers', 'table', 'Customers', 'column', 'Name' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Customers.Customers') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'Nickname') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Customers', 'table', 'Customers', 'column', 'Nickname' - -end - - -execute sp_addextendedproperty 'MS_Description', - '昵称', - 'schema', 'Customers', 'table', 'Customers', 'column', 'Nickname' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Customers.Customers') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'Balance') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Customers', 'table', 'Customers', 'column', 'Balance' - -end - - -execute sp_addextendedproperty 'MS_Description', - '余额', - 'schema', 'Customers', 'table', 'Customers', 'column', 'Balance' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Customers.Customers') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'Gender') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Customers', 'table', 'Customers', 'column', 'Gender' - -end - - -execute sp_addextendedproperty 'MS_Description', - '性别', - 'schema', 'Customers', 'table', 'Customers', 'column', 'Gender' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Customers.Customers') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'Tel') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Customers', 'table', 'Customers', 'column', 'Tel' - -end - - -execute sp_addextendedproperty 'MS_Description', - '联系电话', - 'schema', 'Customers', 'table', 'Customers', 'column', 'Tel' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Customers.Customers') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'Mobile') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Customers', 'table', 'Customers', 'column', 'Mobile' - -end - - -execute sp_addextendedproperty 'MS_Description', - '手机号', - 'schema', 'Customers', 'table', 'Customers', 'column', 'Mobile' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Customers.Customers') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'Email') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Customers', 'table', 'Customers', 'column', 'Email' - -end - - -execute sp_addextendedproperty 'MS_Description', - '电子邮件', - 'schema', 'Customers', 'table', 'Customers', 'column', 'Email' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Customers.Customers') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'CreationTime') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Customers', 'table', 'Customers', 'column', 'CreationTime' - -end - - -execute sp_addextendedproperty 'MS_Description', - '创建时间', - 'schema', 'Customers', 'table', 'Customers', 'column', 'CreationTime' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Customers.Customers') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'CreatorId') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Customers', 'table', 'Customers', 'column', 'CreatorId' - -end - - -execute sp_addextendedproperty 'MS_Description', - '创建人', - 'schema', 'Customers', 'table', 'Customers', 'column', 'CreatorId' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Customers.Customers') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'LastModificationTime') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Customers', 'table', 'Customers', 'column', 'LastModificationTime' - -end - - -execute sp_addextendedproperty 'MS_Description', - '最后修改时间', - 'schema', 'Customers', 'table', 'Customers', 'column', 'LastModificationTime' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Customers.Customers') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'LastModifierId') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Customers', 'table', 'Customers', 'column', 'LastModifierId' - -end - - -execute sp_addextendedproperty 'MS_Description', - '最后修改人', - 'schema', 'Customers', 'table', 'Customers', 'column', 'LastModifierId' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Customers.Customers') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'Version') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Customers', 'table', 'Customers', 'column', 'Version' - -end - - -execute sp_addextendedproperty 'MS_Description', - '版本号', - 'schema', 'Customers', 'table', 'Customers', 'column', 'Version' -go - - - - - - -if exists (select 1 - from sysobjects - where id = object_id('Productions.Products') - and type = 'U') - drop table Productions.Products -go - -/*==============================================================*/ -/* Table: Products */ -/*==============================================================*/ -create table Productions.Products ( - ProductId int not null, - Code nvarchar(30) not null, - Name nvarchar(200) not null, - Extends nvarchar(max) null, - Price decimal(10,2) null, - IsDeleted bit not null, - Version timestamp null, - constraint PK_PRODUCTS primary key (ProductId) -) -go - -if exists (select 1 from sys.extended_properties - where major_id = object_id('Productions.Products') and minor_id = 0) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Productions', 'table', 'Products' - -end - - -execute sp_addextendedproperty 'MS_Description', - '商品', - 'schema', 'Productions', 'table', 'Products' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Productions.Products') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'ProductId') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Productions', 'table', 'Products', 'column', 'ProductId' - -end - - -execute sp_addextendedproperty 'MS_Description', - '商品编号', - 'schema', 'Productions', 'table', 'Products', 'column', 'ProductId' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Productions.Products') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'Code') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Productions', 'table', 'Products', 'column', 'Code' - -end - - -execute sp_addextendedproperty 'MS_Description', - '商品编码', - 'schema', 'Productions', 'table', 'Products', 'column', 'Code' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Productions.Products') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'Name') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Productions', 'table', 'Products', 'column', 'Name' - -end - - -execute sp_addextendedproperty 'MS_Description', - '商品名称', - 'schema', 'Productions', 'table', 'Products', 'column', 'Name' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Productions.Products') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'Extends') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Productions', 'table', 'Products', 'column', 'Extends' - -end - - -execute sp_addextendedproperty 'MS_Description', - '扩展属性', - 'schema', 'Productions', 'table', 'Products', 'column', 'Extends' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Productions.Products') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'Price') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Productions', 'table', 'Products', 'column', 'Price' - -end - - -execute sp_addextendedproperty 'MS_Description', - '价格', - 'schema', 'Productions', 'table', 'Products', 'column', 'Price' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Productions.Products') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'IsDeleted') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Productions', 'table', 'Products', 'column', 'IsDeleted' - -end - - -execute sp_addextendedproperty 'MS_Description', - '是否删除', - 'schema', 'Productions', 'table', 'Products', 'column', 'IsDeleted' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Productions.Products') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'Version') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Productions', 'table', 'Products', 'column', 'Version' - -end - - -execute sp_addextendedproperty 'MS_Description', - '版本号', - 'schema', 'Productions', 'table', 'Products', 'column', 'Version' -go - - - - - - -if exists (select 1 - from sysobjects - where id = object_id('Sales.Orders') - and type = 'U') - drop table Sales.Orders -go - -/*==============================================================*/ -/* Table: Orders */ -/*==============================================================*/ -create table Sales.Orders ( - OrderId uniqueidentifier not null, - Code nvarchar(30) not null, - Name nvarchar(200) not null, - Version timestamp null, - constraint PK_ORDERS primary key (OrderId) -) -go - -if exists (select 1 from sys.extended_properties - where major_id = object_id('Sales.Orders') and minor_id = 0) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Sales', 'table', 'Orders' - -end - - -execute sp_addextendedproperty 'MS_Description', - '订单', - 'schema', 'Sales', 'table', 'Orders' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Sales.Orders') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'OrderId') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Sales', 'table', 'Orders', 'column', 'OrderId' - -end - - -execute sp_addextendedproperty 'MS_Description', - '订单编号', - 'schema', 'Sales', 'table', 'Orders', 'column', 'OrderId' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Sales.Orders') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'Code') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Sales', 'table', 'Orders', 'column', 'Code' - -end - - -execute sp_addextendedproperty 'MS_Description', - '订单编码', - 'schema', 'Sales', 'table', 'Orders', 'column', 'Code' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Sales.Orders') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'Name') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Sales', 'table', 'Orders', 'column', 'Name' - -end - - -execute sp_addextendedproperty 'MS_Description', - '订单名称', - 'schema', 'Sales', 'table', 'Orders', 'column', 'Name' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Sales.Orders') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'Version') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Sales', 'table', 'Orders', 'column', 'Version' - -end - - -execute sp_addextendedproperty 'MS_Description', - '版本号', - 'schema', 'Sales', 'table', 'Orders', 'column', 'Version' -go - - - - -if exists (select 1 - from sys.sysreferences r join sys.sysobjects o on (o.id = r.constid and o.type = 'F') - where r.fkeyid = object_id('Sales.OrderItems') and o.name = 'FK_ORDERITE_REFERENCE_PRODUCTS') -alter table Sales.OrderItems - drop constraint FK_ORDERITE_REFERENCE_PRODUCTS -go - -if exists (select 1 - from sys.sysreferences r join sys.sysobjects o on (o.id = r.constid and o.type = 'F') - where r.fkeyid = object_id('Sales.OrderItems') and o.name = 'FK_ORDERITE_REFERENCE_ORDERS') -alter table Sales.OrderItems - drop constraint FK_ORDERITE_REFERENCE_ORDERS -go - -if exists (select 1 - from sysobjects - where id = object_id('Sales.OrderItems') - and type = 'U') - drop table Sales.OrderItems -go - -/*==============================================================*/ -/* Table: OrderItems */ -/*==============================================================*/ -create table Sales.OrderItems ( - OrderItemId uniqueidentifier not null, - OrderId uniqueidentifier not null, - ProductId int not null, - ProductName nvarchar(200) not null, - Quantity int not null, - Price decimal(10,2) not null, - constraint PK_ORDERITEMS primary key (OrderItemId) -) -go - -if exists (select 1 from sys.extended_properties - where major_id = object_id('Sales.OrderItems') and minor_id = 0) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Sales', 'table', 'OrderItems' - -end - - -execute sp_addextendedproperty 'MS_Description', - '订单明细', - 'schema', 'Sales', 'table', 'OrderItems' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Sales.OrderItems') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'OrderItemId') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Sales', 'table', 'OrderItems', 'column', 'OrderItemId' - -end - - -execute sp_addextendedproperty 'MS_Description', - '订单明细编号', - 'schema', 'Sales', 'table', 'OrderItems', 'column', 'OrderItemId' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Sales.OrderItems') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'OrderId') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Sales', 'table', 'OrderItems', 'column', 'OrderId' - -end - - -execute sp_addextendedproperty 'MS_Description', - '订单编号', - 'schema', 'Sales', 'table', 'OrderItems', 'column', 'OrderId' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Sales.OrderItems') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'ProductId') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Sales', 'table', 'OrderItems', 'column', 'ProductId' - -end - - -execute sp_addextendedproperty 'MS_Description', - '商品编号', - 'schema', 'Sales', 'table', 'OrderItems', 'column', 'ProductId' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Sales.OrderItems') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'ProductName') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Sales', 'table', 'OrderItems', 'column', 'ProductName' - -end - - -execute sp_addextendedproperty 'MS_Description', - '商品名称', - 'schema', 'Sales', 'table', 'OrderItems', 'column', 'ProductName' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Sales.OrderItems') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'Quantity') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Sales', 'table', 'OrderItems', 'column', 'Quantity' - -end - - -execute sp_addextendedproperty 'MS_Description', - '数量', - 'schema', 'Sales', 'table', 'OrderItems', 'column', 'Quantity' -go - -if exists(select 1 from sys.extended_properties p where - p.major_id = object_id('Sales.OrderItems') - and p.minor_id = (select c.column_id from sys.columns c where c.object_id = p.major_id and c.name = 'Price') -) -begin - execute sp_dropextendedproperty 'MS_Description', - 'schema', 'Sales', 'table', 'OrderItems', 'column', 'Price' - -end - - -execute sp_addextendedproperty 'MS_Description', - '单价', - 'schema', 'Sales', 'table', 'OrderItems', 'column', 'Price' -go - -alter table Sales.OrderItems - add constraint FK_ORDERITE_REFERENCE_PRODUCTS foreign key (ProductId) - references Productions.Products (ProductId) -go - -alter table Sales.OrderItems - add constraint FK_ORDERITE_REFERENCE_ORDERS foreign key (OrderId) - references Sales.Orders (OrderId) -go diff --git a/test/Util.Datas.Tests.Integration/SqlServer/Tests/CustomerRepositoryTest.cs b/test/Util.Datas.Tests.Integration/SqlServer/Tests/CustomerRepositoryTest.cs deleted file mode 100644 index d39e7ea9a..000000000 --- a/test/Util.Datas.Tests.Integration/SqlServer/Tests/CustomerRepositoryTest.cs +++ /dev/null @@ -1,291 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Util.Datas.Ef; -using Util.Datas.Queries; -using Util.Datas.Tests.Samples.Datas.Criterias; -using Util.Datas.Tests.Samples.Datas.SqlServer.UnitOfWorks; -using Util.Datas.Tests.Samples.Domains.Models; -using Util.Datas.Tests.Samples.Domains.Repositories; -using Util.Datas.Tests.SqlServer.Configs; -using Util.Dependency; -using Util.Helpers; -using Xunit; - -namespace Util.Datas.Tests.SqlServer.Tests { - /// - /// 客户仓储测试 - 主要测试审计和查询操作 - /// - [Collection( "GlobalConfig" )] - public class CustomerRepositoryTest : IDisposable { - /// - /// 容器作用域 - /// - private readonly IScope _scope; - - /// - /// 工作单元 - /// - private readonly ISqlServerUnitOfWork _unitOfWork; - - /// - /// 客户仓储 - /// - private readonly ICustomerRepository _customerRepository; - - /// - /// 测试初始化 - /// - public CustomerRepositoryTest() { - _scope = Ioc.BeginScope(); - _unitOfWork = _scope.Create(); - _customerRepository = _scope.Create(); - } - - /// - /// 测试清理 - /// - public void Dispose() { - _scope.Dispose(); - } - - /// - /// 添加客户集合 - /// - private List AddCustomers() { - var customers = Customer.CreateCustomers(); - _customerRepository.Add( customers ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - return customers; - } - - /// - /// 测试添加 - 测试创建操作审计 - /// - [Fact] - public void TestAdd() { - string id = Id.ObjectId(); - var customer = new Customer( id ) {Name = "util"}; - _customerRepository.Add( customer ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - var result = _customerRepository.Single( t => t.Id == id ); - Assert.NotNull( result.CreationTime ); - Assert.NotNull( result.LastModificationTime ); - Assert.Equal( AppConfig.UserId.ToGuidOrNull(), result.CreatorId ); - Assert.Equal( AppConfig.UserId.ToGuidOrNull(), result.LastModifierId ); - } - - /// - /// 测试修改 - 测试修改操作审计 - /// - [Fact] - public void TestUpdate() { - string id = Id.ObjectId(); - var customer = new Customer( id ) {Name = "util"}; - _customerRepository.Add( customer ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - var result = _customerRepository.Single( t => t.Id == id ); - var creationTime = result.CreationTime; - var lastModificationTime = result.LastModificationTime; - Assert.Equal( "util", result.Name ); - result.Name = "utilcore"; - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - result = _customerRepository.Single( t => t.Id == id ); - Assert.Equal( "utilcore", result.Name ); - Assert.Equal( creationTime, result.CreationTime ); - Assert.NotEqual( lastModificationTime, result.LastModificationTime ); - Assert.Equal( AppConfig.UserId.ToGuidOrNull(), result.CreatorId ); - Assert.Equal( AppConfig.UserId.ToGuidOrNull(), result.LastModifierId ); - } - - /// - /// 测试查询 - 通过查询条件对象过滤 - /// - [Fact] - public void TestFind_Criteria() { - AddCustomers(); - var result = _customerRepository.Find( new CustomerCriteria( "B", "B1" ) ).FirstOrDefault(); - Assert.Equal( "3", result.Mobile ); - } - - /// - /// 测试查询数量 - /// - [Fact] - public void TestCount() { - var customers = AddCustomers(); - var ids = customers.Select( t => t.Id ).ToList(); - Assert.Equal( customers.Count, _customerRepository.Count( t => ids.Contains( t.Id ) ) ); - } - - /// - /// 测试异步查询数量 - /// - [Fact] - public async Task TestCountAsync() { - var customers = AddCustomers(); - var ids = customers.Select( t => t.Id ).ToList(); - Assert.Equal( customers.Count, await _customerRepository.CountAsync( t => ids.Contains( t.Id ) ) ); - } - - /// - /// 测试查询 - 通过查询对象过滤 - /// - [Fact] - public void TestQuery() { - AddCustomers(); - var query = new Query(); - query.Where( t => t.Name == "B" && t.Nickname == "B1" ); - var result = _customerRepository.Query( query ); - Assert.Equal( "3", result.First().Mobile ); - } - - /// - /// 测试异步查询 - 通过查询对象过滤 - /// - [Fact] - public async Task TestQueryAsync() { - AddCustomers(); - var query = new Query(); - query.Where( t => t.Name == "B" && t.Nickname == "B1" ); - var result = await _customerRepository.QueryAsync( query ); - Assert.Equal( "3", result.First().Mobile ); - } - - /// - /// 测试查询 - 通过查询对象过滤,获取未跟踪实体 - /// - [Fact] - public void TestQueryAsNoTracking() { - AddCustomers(); - var query = new Query(); - query.Where( t => t.Name == "B" && t.Nickname == "B1" ); - var entity = _customerRepository.QueryAsNoTracking( query ).First(); - Assert.Equal( "3", entity.Mobile ); - - var id = entity.Id; - entity.Name = "abc"; - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - Assert.False( _customerRepository.Exists( t => t.Id == id && t.Name == "abc" ) ); - } - - /// - /// 测试查询 - 通过查询对象过滤,获取未跟踪实体 - /// - [Fact] - public async Task TestQueryAsNoTrackingAsync() { - AddCustomers(); - var query = new Query(); - query.Where( t => t.Name == "B" && t.Nickname == "B1" ); - var result = await _customerRepository.QueryAsNoTrackingAsync( query ); - var entity = result.First(); - Assert.Equal( "3", entity.Mobile ); - - var id = entity.Id; - entity.Name = "abc"; - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - Assert.False( await _customerRepository.ExistsAsync( t => t.Id == id && t.Name == "abc" ) ); - } - - /// - /// 测试分页查询 - /// - [Fact] - public void TestPagerQuery() { - var ids = AddCustomers().Select( t => t.Id ).ToList(); - QueryParameter parameter = new QueryParameter {PageSize = 2, Page = 2}; - var query = new Query( parameter ); - query.Where( t => ids.Contains( t.Id ) ); - var result = _customerRepository.PagerQuery( query ); - Assert.Equal( 5, result.TotalCount ); - Assert.Equal( 3, result.PageCount ); - Assert.Equal( 2, result.Data.Count ); - } - - /// - /// 测试异步分页查询 - /// - [Fact] - public async Task TestPagerQueryAsync() { - var ids = AddCustomers().Select( t => t.Id ).ToList(); - QueryParameter parameter = new QueryParameter {PageSize = 2, Page = 2}; - var query = new Query( parameter ); - query.Where( t => ids.Contains( t.Id ) ); - var result = await _customerRepository.PagerQueryAsync( query ); - Assert.Equal( 5, result.TotalCount ); - Assert.Equal( 3, result.PageCount ); - Assert.Equal( 2, result.Data.Count ); - } - - /// - /// 测试查询 - 选择性添加条件 - /// - [Fact] - public void TestWhereIf() { - var ids = AddCustomers().Select( t => t.Id ).ToList(); - Assert.Single( _customerRepository.Find().WhereIf( t => t.Id == ids[0], true ).ToList() ); - Assert.Equal( ids.Count, - _customerRepository.Find().Where( t => ids.Contains( t.Id ) ).WhereIf( t => t.Name == "C", false ) - .ToList().Count ); - } - - /// - /// 测试查询 - 选择性添加条件 - /// - [Fact] - public void TestWhereIf_2() { - var ids = AddCustomers().Select( t => t.Id ).ToList(); - - var query = new Query(); - query.WhereIf( t => t.Id == ids[0], true ); - Assert.Single( _customerRepository.Find( query ).ToList() ); - - query = new Query(); - query.Where( t => ids.Contains( t.Id ) ); - query.WhereIf( t => ids.Contains( t.Id ), false ); - Assert.Equal( ids.Count, _customerRepository.Find( query ).ToList().Count ); - } - - /// - /// 测试查询 - 添加条件 - 自动判断空值 - /// - [Fact] - public void TestWhereIfNotEmpty() { - var ids = AddCustomers().Select( t => t.Id ).ToList(); - Assert.NotEmpty( _customerRepository.Find().Where( t => ids.Contains( t.Id ) ) - .WhereIfNotEmpty( t => t.Name == "" ).ToList() ); - Assert.Single( _customerRepository.Find().Where( t => ids.Contains( t.Id ) ) - .WhereIfNotEmpty( t => t.Name == "C" ).ToList() ); - } - - /// - /// 测试查询 - 添加条件 - 自动判断空值 - /// - [Fact] - public void TestWhereIfNotEmpty_2() { - var ids = AddCustomers().Select( t => t.Id ).ToList(); - - var query = new Query(); - query.Where( t => ids.Contains( t.Id ) ); - query.WhereIfNotEmpty( t => t.Name == "" ); - Assert.NotEmpty( _customerRepository.Find( query ).ToList() ); - - query = new Query(); - query.Where( t => ids.Contains( t.Id ) ); - query.WhereIfNotEmpty( t => t.Name == "C" ); - Assert.Single( _customerRepository.Find( query ).ToList() ); - } - } -} diff --git a/test/Util.Datas.Tests.Integration/SqlServer/Tests/OrderRepositoryTest.cs b/test/Util.Datas.Tests.Integration/SqlServer/Tests/OrderRepositoryTest.cs deleted file mode 100644 index 0ac386042..000000000 --- a/test/Util.Datas.Tests.Integration/SqlServer/Tests/OrderRepositoryTest.cs +++ /dev/null @@ -1,467 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Util.Datas.Ef; -using Util.Datas.Tests.Samples.Datas.SqlServer.UnitOfWorks; -using Util.Datas.Tests.Samples.Domains.Models; -using Util.Datas.Tests.Samples.Domains.Repositories; -using Util.Datas.Tests.SqlServer.Configs; -using Util.Datas.Tests.XUnitHelpers; -using Util.Dependency; -using Util.Exceptions; -using Util.Helpers; -using Xunit; - -namespace Util.Datas.Tests.SqlServer.Tests { - /// - /// 订单仓储测试 - /// - [Collection( "GlobalConfig" )] - public class OrderRepositoryTest : IDisposable { - /// - /// 容器作用域 - /// - private readonly IScope _scope; - /// - /// 工作单元 - /// - private readonly ISqlServerUnitOfWork _unitOfWork; - /// - /// 订单仓储 - /// - private readonly IOrderRepository _orderRepository; - /// - /// 商品仓储 - /// - private readonly IProductRepository _productRepository; - /// - /// 随机数操作 - /// - private readonly Util.Helpers.Random _random; - - /// - /// 测试初始化 - /// - public OrderRepositoryTest() { - _scope = Ioc.BeginScope(); - _unitOfWork = _scope.Create(); - _orderRepository = _scope.Create(); - _productRepository = _scope.Create(); - _random = new Util.Helpers.Random(); - } - - /// - /// 测试清理 - /// - public void Dispose() { - _scope.Dispose(); - } - - /// - /// 测试添加 - /// - [Fact] - public void TestAdd() { - Guid id = Guid.NewGuid(); - var order = new Order( id ) { Name = "Name", Code = "Code" }; - _orderRepository.Add( order ); - _unitOfWork.Commit(); - - var result = _orderRepository.Single( t => t.Id == id ); - Assert.Equal( "Name", result.Name ); - } - - /// - /// 测试添加 - 添加子实体 - /// - [Fact] - public void TestAdd_Items() { - //创建两个商品 - var product1 = new Product( _random.Next( 999999999 ) ) { Name = "dotnet", Code = "1", Price = 10 }; - _productRepository.Add( product1 ); - var product2 = new Product( _random.Next( 999999999 ) ) { Name = "dotnetcore", Code = "2", Price = 20 }; - _productRepository.Add( product2 ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - //创建订单,添加两个订单明细 - Guid orderId = Guid.NewGuid(); - var order = new Order( orderId ) { Name = "Order", Code = "123" }; - order.AddItem( product1, 2 ); - order.AddItem( product2, 3 ); - _orderRepository.Add( order ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - //验证 - var result = _orderRepository.Find().Include( t => t.Items ).FirstOrDefault( t => t.Id == orderId ); - Assert.Equal( "123", result.Code ); - Assert.Equal( 2, result.Items.Count ); - - //从外部无法通过导航属性添加订单明细,必须调用AddItem方法 - var item = new OrderItem( Guid.NewGuid(), result ); - item.Booking( product1, 2 ); - var items = result.Items.ToList(); - items.Add( item ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - result = _orderRepository.Find().Include( t => t.Items ).FirstOrDefault( t => t.Id == orderId ); - Assert.Equal( 2, result.Items.Count ); - } - - /// - /// 测试添加 - 添加集合 - /// - [Fact] - public void TestAdd_List() { - Guid id = Guid.NewGuid(); - Guid id2 = Guid.NewGuid(); - var order = new Order( id ) { Name = "Name", Code = "Code" }; - var order2 = new Order( id2 ) { Name = "Name", Code = "Code" }; - _orderRepository.Add( new[] { order, order2 } ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - var result = _orderRepository.FindByIds( id, id2 ); - Assert.Equal( 2, result.Count ); - } - - /// - /// 测试异步添加 - /// - [Fact] - public async Task TestAddAsync() { - Guid id = Guid.NewGuid(); - var order = new Order( id ) { Name = "Name", Code = "Code" }; - await _orderRepository.AddAsync( order ); - await _unitOfWork.CommitAsync(); - - var result = await _orderRepository.SingleAsync( t => t.Id == id ); - Assert.Equal( id, result.Id ); - } - - /// - /// 测试异步添加 - 添加集合 - /// - [Fact] - public async Task TestAddAsync_List() { - Guid id = Guid.NewGuid(); - Guid id2 = Guid.NewGuid(); - var order = new Order( id ) { Name = "Name", Code = "Code" }; - var order2 = new Order( id2 ) { Name = "Name", Code = "Code" }; - await _orderRepository.AddAsync( new[] { order, order2 } ); - await _unitOfWork.CommitAsync(); - _unitOfWork.ClearCache(); - - var result = await _orderRepository.FindByIdsAsync( id, id2 ); - Assert.Equal( 2, result.Count ); - } - - /// - /// 测试更新 - 修改方式1:先从仓储中查找出来,直接修改对象属性,提交工作单元 - /// - [Fact] - public void TestUpdate_1() { - Guid id = Guid.NewGuid(); - var order = new Order( id ) { Name = "Name", Code = "Code" }; - _orderRepository.Add( order ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - order = _orderRepository.Find( id ); - order.Code = "B"; - _unitOfWork.Commit(); - - var result = _orderRepository.Single( t => t.Id == id ); - Assert.Equal( "B", result.Code ); - } - - /// - /// 测试更新 - 修改方式2:创建出修改对象,调用仓储的Update,提交工作单元 - /// 应用场景:通过Http Put方法传入DTO,该DTO包含待修改实体的全部属性,将该DTO转成待更新实体 - /// 注意:创建的更新实体必须包含全部数据,否则导致数据丢失 - /// - [Fact] - public void TestUpdate_2() { - Guid id = Guid.NewGuid(); - var order = new Order( id ) { Name = "Name", Code = "Code" }; - _orderRepository.Add( order ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - order = new Order( id ) { Name = "Name", Code = "B", Version = order.Version }; - _orderRepository.Update( order ); - _unitOfWork.Commit(); - - var result = _orderRepository.Single( t => t.Id == id ); - Assert.Equal( "B", result.Code ); - } - - /// - /// 测试更新 - 更新子实体 - /// - [Fact] - public void TestUpdate_Items() { - //创建两个商品 - var product1 = new Product( _random.Next( 999999999 ) ) { Name = "dotnet", Code = "1", Price = 10 }; - _productRepository.Add( product1 ); - var product2 = new Product( _random.Next( 999999999 ) ) { Name = "dotnetcore", Code = "2", Price = 20 }; - _productRepository.Add( product2 ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - //创建订单,添加两个订单明细 - Guid orderId = Guid.NewGuid(); - var order = new Order( orderId ) { Name = "Order", Code = "123" }; - order.AddItem( product1, 2 ); - order.AddItem( product2, 3 ); - _orderRepository.Add( order ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - //验证 - order = _orderRepository.Find().Include( t => t.Items ).FirstOrDefault( t => t.Id == orderId ); - Assert.Equal( 2, order.Items.Count ); - - //获取订单明细标识 - var itemId = order.Items.ToList()[0].Id; - var itemId2 = order.Items.ToList()[1].Id; - - //移除一个订单明细 - order.RemoveItem( itemId ); - //修改一个订单明细 - var item = order.FindItem( itemId2 ); - item.Booking( product1, 4 ); - //提交 - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - //验证 - order = _orderRepository.Find().Include( t => t.Items ).FirstOrDefault( t => t.Id == orderId ); - Assert.Equal( 1, order.Items.Count ); - Assert.Equal( 4, order.Items.ToList()[0].Quantity ); - } - - /// - /// 测试异步更新 - /// - [Fact] - public async Task TestUpdateAsync() { - Guid id = Guid.NewGuid(); - var order = new Order( id ) { Name = "Name", Code = "Code" }; - await _orderRepository.AddAsync( order ); - await _unitOfWork.CommitAsync(); - _unitOfWork.ClearCache(); - - var oldEntity = await _orderRepository.FindAsync( id ); - order = new Order( id ) { Name = "Name", Code = "B", Version = oldEntity.Version }; - await _orderRepository.UpdateAsync( order ); - await _unitOfWork.CommitAsync(); - - var result = await _orderRepository.SingleAsync( t => t.Id == id ); - Assert.Equal( "B", result.Code ); - } - - /// - /// 测试更新并发 - Version属性设置为无效 - /// - [Fact] - public void TestUpdate_Concurrency() { - Guid id = Guid.NewGuid(); - var order = new Order( id ) { Name = "Name", Code = "Code" }; - _orderRepository.Add( order ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - AssertHelper.Throws( () => { - var order2 = new Order( id ) { Name = "Name", Code = "B", Version = Guid.NewGuid().ToByteArray() }; - _orderRepository.Update( order2 ); - _unitOfWork.Commit(); - } ); - } - - /// - /// 测试删除 - 通过实体标识删除 - /// - [Fact] - public void TestRemove_Key() { - Guid id = Guid.NewGuid(); - var order = new Order( id ) { Name = "Name", Code = "Code" }; - _orderRepository.Add( order ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - _orderRepository.Remove( id ); - _unitOfWork.Commit(); - - Assert.False( _orderRepository.Exists( t => t.Id == id ) ); - } - - /// - /// 测试异步删除 - 通过实体标识删除 - /// - [Fact] - public async Task TestRemoveAsync_Key() { - Guid id = Guid.NewGuid(); - var order = new Order( id ) { Name = "Name", Code = "Code" }; - await _orderRepository.AddAsync( order ); - await _unitOfWork.CommitAsync(); - _unitOfWork.ClearCache(); - - await _orderRepository.RemoveAsync( id ); - await _unitOfWork.CommitAsync(); - - Assert.False( await _orderRepository.ExistsAsync( t => t.Id == id ) ); - } - - /// - /// 测试删除 - 通过实体删除 - Find查出来,Remove - /// - [Fact] - public void TestRemove_Entity() { - Guid id = Guid.NewGuid(); - var order = new Order( id ) { Name = "Name", Code = "Code" }; - _orderRepository.Add( order ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - order = _orderRepository.Find( id ); - Assert.NotNull( order ); - _orderRepository.Remove( order ); - _unitOfWork.Commit(); - - Assert.False( _orderRepository.Exists( id ) ); - } - - /// - /// 测试删除 - 通过实体删除 - 创建待删除实体,Remove - /// - [Fact] - public void TestRemove_Entity_2() { - Guid id = Guid.NewGuid(); - var order = new Order( id ) { Name = "Name", Code = "Code" }; - _orderRepository.Add( order ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - _orderRepository.Find( id ); - order = new Order( id ) { Name = "Name", Code = "Code", Version = order.Version }; - _orderRepository.Remove( order ); - _unitOfWork.Commit(); - - Assert.False( _orderRepository.Exists( id ) ); - } - - /// - /// 测试异步删除 - 通过实体删除 - /// - [Fact] - public async Task TestRemoveAsync_Entity() { - Guid id = Guid.NewGuid(); - var order = new Order( id ) { Name = "Name", Code = "Code" }; - await _orderRepository.AddAsync( order ); - await _unitOfWork.CommitAsync(); - _unitOfWork.ClearCache(); - - order = await _orderRepository.FindAsync( id ); - Assert.NotNull( order ); - await _orderRepository.RemoveAsync( order ); - await _unitOfWork.CommitAsync(); - - Assert.False( await _orderRepository.ExistsAsync( id ) ); - } - - /// - /// 测试删除 - 通过实体标识集合删除 - /// - [Fact] - public void TestRemove_Key_List() { - Guid id = Guid.NewGuid(); - Guid id2 = Guid.NewGuid(); - var order = new Order( id ) { Name = "Name", Code = "Code" }; - var order2 = new Order( id2 ) { Name = "Name", Code = "Code" }; - _orderRepository.Add( new[] { order, order2 } ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - var result = _orderRepository.FindByIds( id, id2 ); - Assert.Equal( 2, result.Count ); - - _orderRepository.Remove( new[] { id, id2 } ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - Assert.False( _orderRepository.Exists( id, id2 ) ); - } - - /// - /// 测试删除 - 通过实体集合删除 - /// - [Fact] - public void TestRemove_Entity_List() { - Guid id = Guid.NewGuid(); - Guid id2 = Guid.NewGuid(); - var order = new Order( id ) { Name = "Name", Code = "Code" }; - var order2 = new Order( id2 ) { Name = "Name", Code = "Code" }; - _orderRepository.Add( new[] { order, order2 } ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - var result = _orderRepository.FindByIds( $"{id},{id2}" ); - Assert.Equal( 2, result.Count ); - - _orderRepository.Remove( result ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - Assert.False( _orderRepository.Exists( id, id2 ) ); - } - - /// - /// 测试异步删除 - 通过实体标识集合删除 - /// - [Fact] - public async Task TestRemoveAsync_Key_List() { - Guid id = Guid.NewGuid(); - Guid id2 = Guid.NewGuid(); - var order = new Order( id ) { Name = "Name", Code = "Code" }; - var order2 = new Order( id2 ) { Name = "Name", Code = "Code" }; - await _orderRepository.AddAsync( new[] { order, order2 } ); - await _unitOfWork.CommitAsync(); - _unitOfWork.ClearCache(); - - var result = await _orderRepository.FindByIdsAsync( id, id2 ); - Assert.Equal( 2, result.Count ); - - await _orderRepository.RemoveAsync( new[] { id, id2 } ); - await _unitOfWork.CommitAsync(); - _unitOfWork.ClearCache(); - - Assert.False( await _orderRepository.ExistsAsync( id, id2 ) ); - } - - /// - /// 测试异步删除 - 通过实体集合删除 - /// - [Fact] - public async Task TestRemoveAsync_Entity_List() { - Guid id = Guid.NewGuid(); - Guid id2 = Guid.NewGuid(); - var order = new Order( id ) { Name = "Name", Code = "Code" }; - var order2 = new Order( id2 ) { Name = "Name", Code = "Code" }; - await _orderRepository.AddAsync( new[] { order, order2 } ); - await _unitOfWork.CommitAsync(); - _unitOfWork.ClearCache(); - - var result = await _orderRepository.FindByIdsAsync( $"{id},{id2}" ); - Assert.Equal( 2, result.Count ); - - await _orderRepository.RemoveAsync( result ); - await _unitOfWork.CommitAsync(); - _unitOfWork.ClearCache(); - - Assert.False( await _orderRepository.ExistsAsync( id, id2 ) ); - } - } -} diff --git a/test/Util.Datas.Tests.Integration/SqlServer/Tests/ProductRepositoryTest.cs b/test/Util.Datas.Tests.Integration/SqlServer/Tests/ProductRepositoryTest.cs deleted file mode 100644 index 6d220abac..000000000 --- a/test/Util.Datas.Tests.Integration/SqlServer/Tests/ProductRepositoryTest.cs +++ /dev/null @@ -1,389 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Util.Datas.Ef; -using Util.Datas.Tests.Samples.Datas.SqlServer.UnitOfWorks; -using Util.Datas.Tests.Samples.Domains.Models; -using Util.Datas.Tests.Samples.Domains.Repositories; -using Util.Dependency; -using Util.Helpers; -using Xunit; - -namespace Util.Datas.Tests.SqlServer.Tests { - /// - /// 商品仓储测试 - /// - [Collection( "GlobalConfig" )] - public class ProductRepositoryTest : IDisposable { - /// - /// 容器作用域 - /// - private readonly IScope _scope; - /// - /// 工作单元 - /// - private readonly ISqlServerUnitOfWork _unitOfWork; - /// - /// 商品仓储 - /// - private readonly IProductRepository _productRepository; - /// - /// 随机数操作 - /// - private readonly Util.Helpers.Random _random; - - /// - /// 测试初始化 - /// - public ProductRepositoryTest() { - _scope = Ioc.BeginScope(); - _unitOfWork = _scope.Create(); - _productRepository = _scope.Create(); - _random = new Util.Helpers.Random(); - } - - /// - /// 测试清理 - /// - public void Dispose() { - _scope.Dispose(); - } - - /// - /// 测试添加 - /// - [Fact] - public void TestAdd() { - int id = _random.Next( 999999999 ); - var product = new Product( id ) { Name = "Name", Code = "Code" }; - product.ProductType = new ProductType( "Type", new List() { new ProductProperty( "A", "1" ), new ProductProperty( "B", "2" ) } ); - _productRepository.Add( product ); - _unitOfWork.Commit(); - - Product result = _productRepository.GetById( id ); - Assert.Equal( id, result.Id ); - Assert.Equal( "Type", result.ProductType.Name ); - Assert.Equal( "2", result.ProductType.Properties.ToList()[1].Value ); - } - - /// - /// 测试添加 - 添加集合 - /// - [Fact] - public void TestAdd_List() { - int id = _random.Next( 999999999 ); - int id2 = _random.Next( 999999999 ); - var product = new Product( id ) { Name = "Name", Code = "Code" }; - var product2 = new Product( id2 ) { Name = "Name", Code = "Code" }; - _productRepository.Add( new[]{ product, product2 } ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - var result = _productRepository.FindByIds( id,id2 ); - Assert.Equal( 2, result.Count ); - } - - /// - /// 测试异步添加 - /// - [Fact] - public async Task TestAddAsync() { - int id = _random.Next( 999999999 ); - var product = new Product( id ) { Name = "Name", Code = "Code" }; - product.ProductType = new ProductType( "Type", new List() { new ProductProperty( "A", "1" ), new ProductProperty( "B", "2" ) } ); - await _productRepository.AddAsync( product ); - await _unitOfWork.CommitAsync(); - - Product result = _productRepository.GetById( id ); - Assert.Equal( id, result.Id ); - Assert.Equal( "Type", result.ProductType.Name ); - Assert.Equal( "2", result.ProductType.Properties.ToList()[1].Value ); - } - - /// - /// 测试异步添加 - 添加集合 - /// - [Fact] - public async Task TestAddAsync_List() { - int id = _random.Next( 999999999 ); - int id2 = _random.Next( 999999999 ); - var product = new Product( id ) { Name = "Name", Code = "Code" }; - var product2 = new Product( id2 ) { Name = "Name", Code = "Code" }; - await _productRepository.AddAsync( new[] { product, product2 } ); - await _unitOfWork.CommitAsync(); - _unitOfWork.ClearCache(); - - var result = await _productRepository.FindByIdsAsync( id, id2 ); - Assert.Equal( 2, result.Count ); - } - - /// - /// 测试更新 - 先从仓储中查找出来,修改对象属性,直接提交工作单元,该更新方法无效 - /// 原因: 如果使用了Po,从仓储中Find出来的实体只是普通对象,没有被EF跟踪,所以修改属性提交工作单元没有保存更新,必须调用Update方法 - /// - [Fact] - public void TestUpdate_Invalid() { - int id = _random.Next( 999999999 ); - var product = new Product( id ) { Name = "Name", Code = "Code" }; - _productRepository.Add( product ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - var result = _productRepository.Find( id ); - result.Code = "B"; - _unitOfWork.Commit(); - - result = _productRepository.GetById( id ); - Assert.Equal( "Code", result.Code ); - } - - /// - /// 测试更新 - /// - [Fact] - public void TestUpdate() { - int id = _random.Next( 999999999 ); - var product = new Product( id ) { Name = "Name", Code = "Code" }; - _productRepository.Add( product ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - //Find出来修改,必须调用Update方法才能生效 - product = _productRepository.Find( id ); - product.Code = "B"; - _productRepository.Update( product ); - _unitOfWork.Commit(); - var result = _productRepository.GetById( id ); - Assert.Equal( "B", result.Code ); - _unitOfWork.ClearCache(); - - //手工定义一个待更新对象,调用Update - product = new Product( id ) { Name = "Name", Code = "C", Version = result.Version }; - _productRepository.Update( product ); - _unitOfWork.Commit(); - result = _productRepository.GetById( id ); - Assert.Equal( "C", result.Code ); - _unitOfWork.ClearCache(); - - //手工定义一个待更新对象,从仓储中Find出原对象,没有影响 - var old = _productRepository.Find( id ); - product = new Product( id ) { Name = "Name", Code = "D", Version = old.Version }; - _productRepository.Update( product ); - _unitOfWork.Commit(); - result = _productRepository.GetById( id ); - Assert.Equal( "D", result.Code ); - } - - /// - /// 测试异步更新 - /// - [Fact] - public async Task TestUpdateAsync() { - int id = _random.Next( 999999999 ); - var product = new Product( id ) { Name = "Name", Code = "Code" }; - await _productRepository.AddAsync( product ); - await _unitOfWork.CommitAsync(); - _unitOfWork.ClearCache(); - - //Find出来修改,必须调用Update方法才能生效 - product = await _productRepository.FindAsync( id ); - product.Code = "B"; - await _productRepository.UpdateAsync( product ); - await _unitOfWork.CommitAsync(); - var result = _productRepository.GetById( id ); - Assert.Equal( "B", result.Code ); - _unitOfWork.ClearCache(); - - //手工定义一个待更新对象,调用Update - var old = await _productRepository.FindAsync( id ); - product = new Product( id ) { Name = "Name", Code = "C", Version = old.Version }; - await _productRepository.UpdateAsync( product ); - await _unitOfWork.CommitAsync(); - result = _productRepository.GetById( id ); - Assert.Equal( "C", result.Code ); - _unitOfWork.ClearCache(); - } - - /// - /// 测试删除 - 传入实体标识 - /// - [Fact] - public void TestRemove_Key() { - int id = _random.Next( 999999999 ); - var product = new Product( id ) { Name = "Name", Code = "Code" }; - _productRepository.Add( product ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - _productRepository.Remove( id ); - _unitOfWork.Commit(); - - var result = _productRepository.GetById( id ); - Assert.NotNull( result ); - Assert.True( result.IsDeleted ); - } - - /// - /// 测试异步删除 - 传入实体标识 - /// - [Fact] - public async Task TestRemoveAsync_Key() { - int id = _random.Next( 999999999 ); - var product = new Product( id ) { Name = "Name", Code = "Code" }; - await _productRepository.AddAsync( product ); - await _unitOfWork.CommitAsync(); - _unitOfWork.ClearCache(); - - await _productRepository.RemoveAsync( id ); - await _unitOfWork.CommitAsync(); - - Assert.True( _productRepository.Exists( id ) ); - Assert.True( _productRepository.GetById( id ).IsDeleted ); - } - - /// - /// 测试删除 - 传入实体 - /// - [Fact] - public void TestRemove_Entity() { - int id = _random.Next( 999999999 ); - var product = new Product( id ) { Name = "Name", Code = "Code" }; - _productRepository.Add( product ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - product = _productRepository.Find( id ); - _productRepository.Remove( product ); - _unitOfWork.Commit(); - - var result = _productRepository.GetById( id ); - Assert.NotNull( result ); - Assert.True( result.IsDeleted ); - } - - /// - /// 测试异步删除 - 传入实体 - /// - [Fact] - public async Task TestRemoveAsync_Entity() { - int id = _random.Next( 999999999 ); - var product = new Product( id ) { Name = "Name", Code = "Code" }; - await _productRepository.AddAsync( product ); - await _unitOfWork.CommitAsync(); - _unitOfWork.ClearCache(); - - product = await _productRepository.FindAsync( id ); - await _productRepository.RemoveAsync( product ); - await _unitOfWork.CommitAsync(); - - Assert.True( await _productRepository.ExistsAsync( id ) ); - Assert.True( _productRepository.GetById( id ).IsDeleted ); - } - - /// - /// 测试删除 - 通过实体标识集合删除 - /// - [Fact] - public void TestRemove_Key_List() { - int id = _random.Next( 999999999 ); - int id2 = _random.Next( 999999999 ); - var product = new Product( id ) { Name = "Name", Code = "Code" }; - var product2 = new Product( id2 ) { Name = "Name", Code = "Code" }; - _productRepository.Add( new[] { product, product2 } ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - var result = _productRepository.FindByIds( id, id2 ); - Assert.Equal( 2, result.Count ); - - _productRepository.Remove( new[]{ id, id2 } ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - result = _productRepository.FindByIds( id, id2 ); - Assert.Equal( 2, result.Count ); - Assert.True( result[0].IsDeleted ); - Assert.True( result[1].IsDeleted ); - } - - /// - /// 测试删除 - 通过实体集合删除 - /// - [Fact] - public void TestRemove_Entity_List() { - int id = _random.Next( 999999999 ); - int id2 = _random.Next( 999999999 ); - var product = new Product( id ) { Name = "Name", Code = "Code" }; - var product2 = new Product( id2 ) { Name = "Name", Code = "Code" }; - _productRepository.Add( new[] { product, product2 } ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - var result = _productRepository.FindByIds( id, id2 ); - Assert.Equal( 2, result.Count ); - - _productRepository.Remove( result ); - _unitOfWork.Commit(); - _unitOfWork.ClearCache(); - - result = _productRepository.FindByIds( id, id2 ); - Assert.Equal( 2, result.Count ); - Assert.True( result[0].IsDeleted ); - Assert.True( result[1].IsDeleted ); - } - - /// - /// 测试异步删除 - 通过实体标识集合删除 - /// - [Fact] - public async Task TestRemoveAsync_Key_List() { - int id = _random.Next( 999999999 ); - int id2 = _random.Next( 999999999 ); - var product = new Product( id ) { Name = "Name", Code = "Code" }; - var product2 = new Product( id2 ) { Name = "Name", Code = "Code" }; - await _productRepository.AddAsync( new[] { product, product2 } ); - await _unitOfWork.CommitAsync(); - _unitOfWork.ClearCache(); - - var result = await _productRepository.FindByIdsAsync( id, id2 ); - Assert.Equal( 2, result.Count ); - - await _productRepository.RemoveAsync( new[] { id, id2 } ); - await _unitOfWork.CommitAsync(); - _unitOfWork.ClearCache(); - - result = await _productRepository.FindByIdsAsync( id, id2 ); - Assert.Equal( 2, result.Count ); - Assert.True( result[0].IsDeleted ); - Assert.True( result[1].IsDeleted ); - } - - /// - /// 测试异步删除 - 通过实体集合删除 - /// - [Fact] - public async Task TestRemoveAsync_Entity_List() { - int id = _random.Next( 999999999 ); - int id2 = _random.Next( 999999999 ); - var product = new Product( id ) { Name = "Name", Code = "Code" }; - var product2 = new Product( id2 ) { Name = "Name", Code = "Code" }; - await _productRepository.AddAsync( new[] { product, product2 } ); - await _unitOfWork.CommitAsync(); - _unitOfWork.ClearCache(); - - var result = await _productRepository.FindByIdsAsync( id, id2 ); - Assert.Equal( 2, result.Count ); - - await _productRepository.RemoveAsync( result ); - await _unitOfWork.CommitAsync(); - _unitOfWork.ClearCache(); - - result = await _productRepository.FindByIdsAsync( id, id2 ); - Assert.Equal( 2, result.Count ); - Assert.True( result[0].IsDeleted ); - Assert.True( result[1].IsDeleted ); - } - } -} - diff --git a/test/Util.Datas.Tests.Integration/XUnitHelpers/AssertHelper.cs b/test/Util.Datas.Tests.Integration/XUnitHelpers/AssertHelper.cs deleted file mode 100644 index 8403a0580..000000000 --- a/test/Util.Datas.Tests.Integration/XUnitHelpers/AssertHelper.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using AspectCore.DynamicProxy; -using Xunit; - -namespace Util.Datas.Tests.XUnitHelpers { - /// - /// 断言操作 - /// - public class AssertHelper { - /// - /// 抛出异常,并从异常消息中搜索特定关键字 - /// - /// 异常类型 - /// 操作 - /// 关键字 - public static TException Throws( Action action, string keyword = "" ) where TException : Exception { - var exception = GetException( action ); - if( !string.IsNullOrWhiteSpace( keyword ) ) - Assert.Contains( keyword, exception.Message ); - return exception; - } - - /// - /// 获取异常 - /// - private static TException GetException( Action action ) where TException : Exception { - try { - var exception = Assert.Throws( action ); - return (TException)exception.InnerException; - } - catch { - return Assert.Throws( action ); - } - } - } -} diff --git a/test/Util.Domain.Tests/Auditing/CreationAuditedSetterTest.cs b/test/Util.Domain.Tests/Auditing/CreationAuditedSetterTest.cs new file mode 100644 index 000000000..1e3ecbd55 --- /dev/null +++ b/test/Util.Domain.Tests/Auditing/CreationAuditedSetterTest.cs @@ -0,0 +1,130 @@ +using System; +using Util.Domain.Auditing; +using Util.Domain.Tests.Samples.Auditing; +using Util.Helpers; +using Xunit; + +namespace Util.Domain.Tests.Auditing; + +/// +/// 创建操作审计设置器测试 +/// +public class CreationAuditedSetterTest : IDisposable { + /// + /// 日期 + /// + private readonly DateTime _date = "2020-1-1 10:10:10".ToDateTime(); + + /// + /// 测试初始化 + /// + public CreationAuditedSetterTest() { + Time.SetTime( _date ); + } + + /// + /// 测试清理 + /// + public void Dispose() { + Time.Reset(); + } + + /// + /// 测试设置Guid创建人标识 + /// + [Fact] + public void TestSet_Guid() { + var value = Guid.NewGuid().ToString(); + var entity = new GuidAuditedEntity(); + CreationAuditedSetter.Set( entity, value ); + Assert.Equal( _date, entity.CreationTime ); + Assert.Equal( value, entity.CreatorId.SafeString() ); + } + + /// + /// 测试设置Guid创建人标识 - 已设置则跳过 + /// + [Fact] + public void TestSet_Guid_2() { + var value = Guid.NewGuid().ToString(); + var value2 = Guid.NewGuid(); + var entity = new GuidAuditedEntity { + CreatorId = value2 + }; + CreationAuditedSetter.Set( entity, value ); + Assert.Equal( _date, entity.CreationTime ); + Assert.Equal( value2, entity.CreatorId ); + } + + /// + /// 测试设置可空Guid创建人标识 + /// + [Fact] + public void TestSet_Guid_Nullable() { + var value = Guid.NewGuid().ToString(); + var entity = new NullableGuidAuditedEntity(); + CreationAuditedSetter.Set( entity, value ); + Assert.Equal( _date, entity.CreationTime ); + Assert.Equal( value, entity.CreatorId.SafeString() ); + } + + /// + /// 测试设置int创建人标识 + /// + [Fact] + public void TestSet_Int() { + var value = "1"; + var entity = new IntAuditedEntity(); + CreationAuditedSetter.Set( entity, value ); + Assert.Equal( _date, entity.CreationTime ); + Assert.Equal( value, entity.CreatorId.SafeString() ); + } + + /// + /// 测试设置可空int创建人标识 + /// + [Fact] + public void TestSet_Int_Nullable() { + var value = "1"; + var entity = new NullableIntAuditedEntity(); + CreationAuditedSetter.Set( entity, value ); + Assert.Equal( _date, entity.CreationTime ); + Assert.Equal( value, entity.CreatorId.SafeString() ); + } + + /// + /// 测试设置long创建人标识 + /// + [Fact] + public void TestSet_Long() { + var value = "1"; + var entity = new LongAuditedEntity(); + CreationAuditedSetter.Set( entity, value ); + Assert.Equal( _date, entity.CreationTime ); + Assert.Equal( value, entity.CreatorId.SafeString() ); + } + + /// + /// 测试设置可空long创建人标识 + /// + [Fact] + public void TestSet_Long_Nullable() { + var value = "1"; + var entity = new NullableLongAuditedEntity(); + CreationAuditedSetter.Set( entity, value ); + Assert.Equal( _date, entity.CreationTime ); + Assert.Equal( value, entity.CreatorId.SafeString() ); + } + + /// + /// 测试设置string创建人标识 + /// + [Fact] + public void TestSet_String() { + var value = "abc"; + var entity = new StringAuditedEntity(); + CreationAuditedSetter.Set( entity, value ); + Assert.Equal( _date, entity.CreationTime ); + Assert.Equal( value, entity.CreatorId.SafeString() ); + } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Auditing/ModificationAuditedSetterTest.cs b/test/Util.Domain.Tests/Auditing/ModificationAuditedSetterTest.cs new file mode 100644 index 000000000..485eda1cb --- /dev/null +++ b/test/Util.Domain.Tests/Auditing/ModificationAuditedSetterTest.cs @@ -0,0 +1,115 @@ +using System; +using Util.Domain.Auditing; +using Util.Domain.Tests.Samples.Auditing; +using Util.Helpers; +using Xunit; + +namespace Util.Domain.Tests.Auditing; + +/// +/// 修改操作审计设置器测试 +/// +public class ModificationAuditedSetterTest : IDisposable { + /// + /// 日期 + /// + private readonly DateTime _date = "2020-1-1 10:10:10".ToDateTime(); + + /// + /// 测试初始化 + /// + public ModificationAuditedSetterTest() { + Time.SetTime( _date ); + } + + /// + /// 测试清理 + /// + public void Dispose() { + Time.Reset(); + } + + /// + /// 测试设置Guid修改人标识 + /// + [Fact] + public void TestSet_Guid() { + var value = Guid.NewGuid().ToString(); + var entity = new GuidAuditedEntity(); + ModificationAuditedSetter.Set( entity, value ); + Assert.Equal( _date, entity.LastModificationTime ); + Assert.Equal( value, entity.LastModifierId.SafeString() ); + } + + /// + /// 测试设置可空Guid修改人标识 + /// + [Fact] + public void TestSet_Guid_Nullable() { + var value = Guid.NewGuid().ToString(); + var entity = new NullableGuidAuditedEntity(); + ModificationAuditedSetter.Set( entity, value ); + Assert.Equal( _date, entity.LastModificationTime ); + Assert.Equal( value, entity.LastModifierId.SafeString() ); + } + + /// + /// 测试设置int修改人标识 + /// + [Fact] + public void TestSet_Int() { + var value = "1"; + var entity = new IntAuditedEntity(); + ModificationAuditedSetter.Set( entity, value ); + Assert.Equal( _date, entity.LastModificationTime ); + Assert.Equal( value, entity.LastModifierId.SafeString() ); + } + + /// + /// 测试设置可空int修改人标识 + /// + [Fact] + public void TestSet_Int_Nullable() { + var value = "1"; + var entity = new NullableIntAuditedEntity(); + ModificationAuditedSetter.Set( entity, value ); + Assert.Equal( _date, entity.LastModificationTime ); + Assert.Equal( value, entity.LastModifierId.SafeString() ); + } + + /// + /// 测试设置long修改人标识 + /// + [Fact] + public void TestSet_Long() { + var value = "1"; + var entity = new LongAuditedEntity(); + ModificationAuditedSetter.Set( entity, value ); + Assert.Equal( _date, entity.LastModificationTime ); + Assert.Equal( value, entity.LastModifierId.SafeString() ); + } + + /// + /// 测试设置可空long修改人标识 + /// + [Fact] + public void TestSet_Long_Nullable() { + var value = "1"; + var entity = new NullableLongAuditedEntity(); + ModificationAuditedSetter.Set( entity, value ); + Assert.Equal( _date, entity.LastModificationTime ); + Assert.Equal( value, entity.LastModifierId.SafeString() ); + } + + /// + /// 测试设置string修改人标识 + /// + [Fact] + public void TestSet_String() { + var value = "abc"; + var entity = new StringAuditedEntity(); + ModificationAuditedSetter.Set( entity, value ); + Assert.Equal( _date, entity.LastModificationTime ); + Assert.Equal( value, entity.LastModifierId.SafeString() ); + } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Compare/ChangeValueTest.cs b/test/Util.Domain.Tests/Compare/ChangeValueTest.cs new file mode 100644 index 000000000..781d56218 --- /dev/null +++ b/test/Util.Domain.Tests/Compare/ChangeValueTest.cs @@ -0,0 +1,18 @@ +using Util.Domain.Compare; +using Xunit; + +namespace Util.Domain.Tests.Compare; + +/// +/// 变更值测试 +/// +public class ChangeValueTest { + /// + /// 测试变更值输出结果 + /// + [Fact] + public void TestToString() { + var result = new ChangeValue( "a", "b", "1", "2" ).ToString(); + Assert.Equal( "a(b),原始值:1,新值:2", result ); + } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Compare/KeyListComparatorTest.cs b/test/Util.Domain.Tests/Compare/KeyListComparatorTest.cs new file mode 100644 index 000000000..fe1ebfdff --- /dev/null +++ b/test/Util.Domain.Tests/Compare/KeyListComparatorTest.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using Util.Domain.Compare; +using Util.Domain.Tests.XUnitHelpers; +using Xunit; + +namespace Util.Domain.Tests.Compare; + +/// +/// 键列表比较器测试 +/// +public class KeyListComparatorTest { + /// + /// 键列表比较器 + /// + private readonly KeyListComparator _comparator; + + /// + /// 测试初始化 + /// + public KeyListComparatorTest() { + _comparator = new KeyListComparator(); + } + + /// + /// 测试列表比较 - 验证集合为空 + /// + [Fact] + public void TestCompare_Validate() { + var list = new List(); + AssertHelper.Throws( () => { + _comparator.Compare( null, list ); + } ); + AssertHelper.Throws( () => { + _comparator.Compare( list, null ); + } ); + AssertHelper.Throws( () => { + list.Compare( null ); + } ); + } + + /// + /// 测试列表比较 - 获取创建列表 + /// + [Fact] + public void TestCompare_CreateList() { + var id = Guid.NewGuid(); + var newList = new List { + id + }; + var result = _comparator.Compare( newList, new List() ); + Assert.Single( result.CreateList ); + Assert.Equal( id, result.CreateList[0] ); + } + + /// + /// 测试列表比较 - 获取删除列表 + /// + [Fact] + public void TestCompare_DeleteList() { + var id = Guid.NewGuid(); + var originalList = new List { + id + }; + var result = _comparator.Compare( new List(), originalList ); + Assert.Empty( result.CreateList ); + Assert.Single( result.DeleteList ); + Assert.Equal( id, result.DeleteList[0] ); + } + + /// + /// 测试列表比较 - 获取更新列表 + /// + [Fact] + public void TestCompare_UpdateList() { + var id = Guid.NewGuid(); + var newList = new List { + id + }; + var originalList = new List { + id + }; + var result = _comparator.Compare( newList, originalList ); + Assert.Empty( result.CreateList ); + Assert.Empty( result.DeleteList ); + Assert.Single( result.UpdateList ); + Assert.Equal( id, result.UpdateList[0] ); + } + + /// + /// 测试列表比较 + /// + [Fact] + public void TestCompare() { + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var id3 = Guid.NewGuid(); + var id4 = Guid.NewGuid(); + var newList = new List { + id,id2,id3 + }; + var originalList = new List { + id2,id3,id4 + }; + var result = newList.Compare( originalList ); + Assert.Single( result.CreateList ); + Assert.Equal( id, result.CreateList[0] ); + + Assert.Single( result.DeleteList ); + Assert.Equal( id4, result.DeleteList[0] ); + + Assert.Equal( 2, result.UpdateList.Count ); + Assert.Equal( id2, result.UpdateList[0] ); + Assert.Equal( id3, result.UpdateList[1] ); + } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Compare/ListComparatorTest.cs b/test/Util.Domain.Tests/Compare/ListComparatorTest.cs new file mode 100644 index 000000000..9331f9325 --- /dev/null +++ b/test/Util.Domain.Tests/Compare/ListComparatorTest.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using Util.Domain.Compare; +using Util.Domain.Tests.Samples; +using Util.Domain.Tests.XUnitHelpers; +using Xunit; + +namespace Util.Domain.Tests.Compare; + +/// +/// 列表比较器测试 +/// +public class ListComparatorTest { + /// + /// 列表比较器 + /// + private readonly ListComparator _comparator; + + /// + /// 测试初始化 + /// + public ListComparatorTest() { + _comparator = new ListComparator(); + } + + /// + /// 测试列表比较 - 验证集合为空 + /// + [Fact] + public void TestCompare_Validate() { + var list = new List(); + AssertHelper.Throws( () => { + _comparator.Compare( null, list ); + } ); + AssertHelper.Throws( () => { + _comparator.Compare( list, null ); + } ); + AssertHelper.Throws( () => { + list.Compare( null ); + } ); + } + + /// + /// 测试列表比较 - 获取创建列表 + /// + [Fact] + public void TestCompare_CreateList() { + var id = Guid.NewGuid(); + var newList = new List { + new(id){Name = "a"} + }; + var result = _comparator.Compare( newList, new List() ); + Assert.Single( result.CreateList ); + Assert.Equal( "a", result.CreateList[0].Name ); + } + + /// + /// 测试列表比较 - 获取删除列表 + /// + [Fact] + public void TestCompare_DeleteList() { + var id = Guid.NewGuid(); + var oldList = new List { + new(id){Name = "a"} + }; + var result = _comparator.Compare( new List(), oldList ); + Assert.Empty( result.CreateList ); + Assert.Single( result.DeleteList ); + Assert.Equal( "a", result.DeleteList[0].Name ); + } + + /// + /// 测试列表比较 - 获取更新列表 + /// + [Fact] + public void TestCompare_UpdateList() { + var id = Guid.NewGuid(); + var newList = new List { + new(id){Name = "a"} + }; + var oldList = new List { + new(id){Name = "b"} + }; + var result = _comparator.Compare( newList, oldList ); + Assert.Empty( result.CreateList ); + Assert.Empty( result.DeleteList ); + Assert.Single( result.UpdateList ); + Assert.Equal( "a", result.UpdateList[0].Name ); + } + + /// + /// 测试列表比较 + /// + [Fact] + public void TestCompare() { + var id = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var id3 = Guid.NewGuid(); + var id4 = Guid.NewGuid(); + var newList = new List { + new(id), + new(id2), + new(id3) + }; + var oldList = new List { + new(id2), + new(id3), + new(id4) + }; + var result = newList.Compare( oldList ); + Assert.Single( result.CreateList ); + Assert.Equal( id, result.CreateList[0].Id ); + + Assert.Single( result.DeleteList ); + Assert.Equal( id4, result.DeleteList[0].Id ); + + Assert.Equal( 2, result.UpdateList.Count ); + Assert.Equal( id2, result.UpdateList[0].Id ); + Assert.Equal( id3, result.UpdateList[1].Id ); + } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Entities/GuidEntityTest.cs b/test/Util.Domain.Tests/Entities/GuidEntityTest.cs new file mode 100644 index 000000000..9fd849d72 --- /dev/null +++ b/test/Util.Domain.Tests/Entities/GuidEntityTest.cs @@ -0,0 +1,128 @@ +using System; +using Util.Domain.Tests.Samples; +using Util.Domain.Tests.XUnitHelpers; +using Util.Exceptions; +using Util.Validation; +using Xunit; + +namespace Util.Domain.Tests.Entities; + +/// +/// Guid标识实体测试 +/// +public class GuidEntityTest { + /// + /// 聚合根测试样例 + /// + private AggregateRootSample _sample; + /// + /// 聚合根测试样例2 + /// + private AggregateRootSample _sample2; + + /// + /// 测试初始化 + /// + public GuidEntityTest() { + _sample = new AggregateRootSample(); + _sample2 = new AggregateRootSample(); + } + + /// + /// 测试实体相等性 - 判空 + /// + [Fact] + public void TestEquals_Null() { + Assert.False( _sample.Equals( _sample2 ) ); + Assert.False( _sample.Equals( null ) ); + + Assert.False( _sample == _sample2 ); + Assert.False( _sample == null ); + Assert.False( null == _sample2 ); + + Assert.True( _sample != _sample2 ); + Assert.True( _sample != null ); + Assert.True( null != _sample2 ); + + _sample2 = null; + Assert.True( _sample2 == null ); + Assert.False( _sample.Equals( _sample2 ) ); + + _sample = null; + Assert.True( _sample == _sample2 ); + Assert.True( _sample2 == _sample ); + } + + /// + /// 测试实体相等性 - 类型无效 + /// + [Fact] + public void TestEquals_InvalidType() { + Guid id = Guid.NewGuid(); + _sample = new AggregateRootSample( id ); + AggregateRootSample2 sample2 = new AggregateRootSample2( id ); + Assert.False( _sample.Equals( sample2 ) ); + Assert.True( _sample != sample2 ); + Assert.True( sample2 != _sample ); + } + + /// + /// 测试实体相等性 - 当两个实体的标识相同,则实体相同 + /// + [Fact] + public void TestEquals_IdEquals() { + Guid id = Guid.NewGuid(); + _sample = new AggregateRootSample( id ); + _sample2 = new AggregateRootSample( id ); + Assert.True( _sample.Equals( _sample2 ) ); + Assert.True( _sample == _sample2 ); + Assert.False( _sample != _sample2 ); + } + + /// + /// 测试验证 - Id为空,无法通过 + /// + [Fact] + public void TestValidate_IdIsEmpty() { + AssertHelper.Throws( () => { + _sample = AggregateRootSample.CreateSample( Guid.Empty ); + _sample.Validate(); + }, "标识" ); + } + + /// + /// 验证必填项,通过字符串设置错误消息 + /// + [Fact] + public void TestValidate_Required() { + AssertHelper.Throws( () => { + _sample.Name = null; + _sample.Validate(); + }, "姓名不能为空" ); + } + + /// + /// 测试添加验证规则 + /// + [Fact] + public void TestAddValidationRule() { + _sample = AggregateRootSample.CreateSample(); + AssertHelper.Throws( () => { + _sample.Name = "abcd"; + _sample.AddValidationRule( new ValidationRuleSample( _sample ) ); + _sample.Validate(); + }, "名称长度不能大于3" ); + } + + /// + /// 测试设置验证处理器 - 不进行任何操作,所以不会抛出异常 + /// + [Fact] + public void TestSetValidationHandler() { + _sample = AggregateRootSample.CreateSample(); + _sample.Name = "abcd"; + _sample.AddValidationRule( new ValidationRuleSample( _sample ) ); + _sample.SetValidationHandler( new NothingHandler() ); + _sample.Validate(); + } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Entities/IntEntityTest.cs b/test/Util.Domain.Tests/Entities/IntEntityTest.cs new file mode 100644 index 000000000..295b48fc9 --- /dev/null +++ b/test/Util.Domain.Tests/Entities/IntEntityTest.cs @@ -0,0 +1,50 @@ +using Util.Domain.Tests.Samples; +using Xunit; + +namespace Util.Domain.Tests.Entities; + +/// +/// int标识实体测试 +/// +public class IntEntityTest { + /// + /// 测试初始化 + /// + public IntEntityTest() { + _sample = new IntAggregateRootSample(); + _sample2 = new IntAggregateRootSample(); + } + + /// + /// 聚合根测试样例 + /// + private IntAggregateRootSample _sample; + /// + /// 聚合根测试样例2 + /// + private IntAggregateRootSample _sample2; + + /// + /// 测试实体相等性 - 当两个实体的标识相同,则实体相同 + /// + [Fact] + public void TestEquals_IdEquals() { + _sample = new IntAggregateRootSample( 1 ); + _sample2 = new IntAggregateRootSample( 1 ); + Assert.True( _sample.Equals( _sample2 ) ); + Assert.True( _sample == _sample2 ); + Assert.False( _sample != _sample2 ); + } + + /// + /// 测试实体相等性 - Id为空 + /// + [Fact] + public void TestEquals_Id_Empty() { + _sample = new IntAggregateRootSample( 0 ); + _sample2 = new IntAggregateRootSample( 1 ); + Assert.False( _sample.Equals( _sample2 ) ); + Assert.False( _sample == _sample2 ); + Assert.False( _sample2 == _sample ); + } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Entities/StringEntityTest.cs b/test/Util.Domain.Tests/Entities/StringEntityTest.cs new file mode 100644 index 000000000..0baccbff0 --- /dev/null +++ b/test/Util.Domain.Tests/Entities/StringEntityTest.cs @@ -0,0 +1,73 @@ +using Util.Domain.Tests.Samples; +using Util.Domain.Tests.XUnitHelpers; +using Util.Exceptions; +using Xunit; + +namespace Util.Domain.Tests.Entities; + +/// +/// string标识实体测试 +/// +public class StringEntityTest { + /// + /// 测试初始化 + /// + public StringEntityTest() { + _sample = new StringAggregateRootSample(); + _sample2 = new StringAggregateRootSample(); + } + + /// + /// 聚合根测试样例 + /// + private StringAggregateRootSample _sample; + /// + /// 聚合根测试样例2 + /// + private StringAggregateRootSample _sample2; + + /// + /// 测试实体相等性 - 当两个实体的标识相同,则实体相同 + /// + [Fact] + public void TestEquals_IdEquals() { + _sample = new StringAggregateRootSample( "a" ); + _sample2 = new StringAggregateRootSample( "a" ); + Assert.True( _sample.Equals( _sample2 ) ); + Assert.True( _sample == _sample2 ); + Assert.False( _sample != _sample2 ); + } + + /// + /// 测试实体相等性 - Id为空 + /// + [Fact] + public void TestEquals_Id_Null() { + _sample = new StringAggregateRootSample( null ); + _sample2 = new StringAggregateRootSample( "a" ); + Assert.False( _sample.Equals( _sample2 ) ); + Assert.False( _sample == _sample2 ); + Assert.False( _sample2 == _sample ); + + _sample = new StringAggregateRootSample( "a" ); + _sample2 = new StringAggregateRootSample( null ); + Assert.False( _sample.Equals( _sample2 ) ); + Assert.False( _sample == _sample2 ); + Assert.False( _sample2 == _sample ); + } + + /// + /// 测试验证 - Id为空,无法通过 + /// + [Fact] + public void TestValidate_IdIsEmpty() { + AssertHelper.Throws( () => { + _sample = new StringAggregateRootSample( null ); + _sample.Validate(); + }, "标识" ); + AssertHelper.Throws( () => { + _sample = new StringAggregateRootSample( "" ); + _sample.Validate(); + }, "标识" ); + } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Entities/ValueObjectBaseTest.cs b/test/Util.Domain.Tests/Entities/ValueObjectBaseTest.cs new file mode 100644 index 000000000..d3dc44bc1 --- /dev/null +++ b/test/Util.Domain.Tests/Entities/ValueObjectBaseTest.cs @@ -0,0 +1,114 @@ +using Util.Domain.Tests.Samples; +using Xunit; + +namespace Util.Domain.Tests.Entities; + +/// +/// 测试值对象 +/// +public class ValueObjectBaseTest { + /// + /// 测试初始化 + /// + public ValueObjectBaseTest() { + _sample = new ValueObjectSample( "a", "b" ); + _sample2 = new ValueObjectSample( "a", "b" ); + _sample3 = new ValueObjectSample( "1", "" ); + _aggregateRootSample = new AggregateRootSample(); + _sample4 = new ValueObjectSample( "a", "b", _aggregateRootSample ); + _sample5 = new ValueObjectSample( "a", "b", _aggregateRootSample ); + _sample6 = new ValueObjectSample( "a", "b", _aggregateRootSample, new ValueObjectSample( "a", "b" ) ); + _sample7 = new ValueObjectSample( "a", "b", _aggregateRootSample, new ValueObjectSample( "a", "b" ) ); + } + + /// + /// 值对象测试样例1 + /// + private ValueObjectSample _sample; + /// + /// 值对象测试样例2 + /// + private ValueObjectSample _sample2; + /// + /// 值对象测试样例3 + /// + private ValueObjectSample _sample3; + /// + /// 值对象测试样例4 + /// + private ValueObjectSample _sample4; + /// + /// 值对象测试样例5 + /// + private ValueObjectSample _sample5; + /// + /// 值对象测试样例6 + /// + private ValueObjectSample _sample6; + /// + /// 值对象测试样例7 + /// + private ValueObjectSample _sample7; + /// + /// 聚合根测试样例 + /// + private AggregateRootSample _aggregateRootSample; + + /// + /// 测试对象相等性 + /// + [Fact] + public void TestEquals() { + Assert.False( _sample.Equals( new AggregateRootSample() ) ); + Assert.True( _sample.Equals( _sample2 ), "1" ); + Assert.True( _sample == _sample2, "2" ); + Assert.False( _sample != _sample2, "3" ); + Assert.False( _sample == _sample3, "4" ); + Assert.True( _sample4 == _sample5, "5" ); + Assert.True( _sample4.Equals( _sample5 ), "6" ); + Assert.False( _sample6 == _sample7, "7" ); + Assert.False( _sample6.Equals( _sample7 ), "8" ); + } + + /// + /// 测试对象相等性 - 判空 + /// + [Fact] + public void TestEquals_Null() { + Assert.False( _sample.Equals( null ) ); + Assert.False( _sample == null ); + Assert.False( null == _sample ); + Assert.True( _sample != null ); + + _sample2 = null; + Assert.False( _sample.Equals( _sample2 ) ); + + _sample = null; + Assert.False( _sample == _sample2 ); + Assert.False( _sample2 == _sample ); + } + + /// + /// 测试哈希 + /// + [Fact] + public void TestGetHashCode() { + Assert.True( _sample.GetHashCode() == _sample2.GetHashCode() ); + } + + /// + /// 测试克隆 + /// + [Fact] + public void TestClone() { + _sample3 = _sample.Clone(); + Assert.NotSame( _sample, _sample3 ); + Assert.True( _sample == _sample3 ); + Assert.Equal( "a", _sample3.City ); + + _sample = _sample6.Clone(); + Assert.True( _sample == _sample6 ); + Assert.Equal( "a", _sample.Child.City ); + Assert.Same( _sample.Child, _sample6.Child ); + } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Samples/AggregateRootSample.cs b/test/Util.Domain.Tests/Samples/AggregateRootSample.cs new file mode 100644 index 000000000..2df5d80f6 --- /dev/null +++ b/test/Util.Domain.Tests/Samples/AggregateRootSample.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Domain.Entities; + +namespace Util.Domain.Tests.Samples; + +/// +/// Guid聚合根测试样例 +/// +public class AggregateRootSample : AggregateRoot { + /// + /// 初始化聚合根测试样例 + /// + public AggregateRootSample() + : this( Guid.Empty ) { + } + + /// + /// 初始化聚合根测试样例 + /// + /// 标识 + public AggregateRootSample( Guid id ) + : base( id ) { + IntSamples = new List(); + } + + /// + /// 姓名 + /// + [Required( ErrorMessage = "姓名不能为空" )] + public string Name { get; set; } + + /// + /// 编码 + /// + public string Code { get; set; } + + /// + /// 英文名 + /// + [Required( ErrorMessageResourceType = typeof( TestResource ), ErrorMessageResourceName = "EnglishNameIsEmpty" )] + public string EnglishName { get; set; } + + /// + /// 手机号 + /// + [Display(Name = "手机号" )] + public string MobilePhone { get; set; } + + /// + /// 年龄 + /// + public int? Age { get; set; } + + /// + /// 电话 + /// + [Description( "电话" )] + public int Tel { get; set; } + + /// + /// decimal值 + /// + public decimal DecimalValue { get; set; } + /// + /// 可空decimal值 + /// + public decimal? NullableDecimalValue { get; set; } + /// + /// float值 + /// + public float FloatValue { get; set; } + /// + /// 可空float值 + /// + public float? NullableFloatValue { get; set; } + /// + /// double值 + /// + public double DoubleValue { get; set; } + /// + /// 可空double值 + /// + public double? NullableDoubleValue { get; set; } + /// + /// bool值 + /// + public bool BoolValue { get; set; } + /// + /// 可空bool值 + /// + public bool? NullableBoolValue { get; set; } + /// + /// Enum值 + /// + public EnumSample EnumValue { get; set; } + /// + /// 可空Enum值 + /// + public EnumSample? NullableEnumValue { get; set; } + /// + /// DateTime值 + /// + public DateTime DateValue { get; set; } + /// + /// 可空DateTime值 + /// + public DateTime? NullableDateValue { get; set; } + /// + /// int值 + /// + public int IntValue { get; set; } + /// + /// 可空int值 + /// + public int? NullableIntValue { get; set; } + /// + /// short值 + /// + public short ShortValue { get; set; } + /// + /// 可空short值 + /// + public short? NullableShortValue { get; set; } + /// + /// long值 + /// + public long LongValue { get; set; } + /// + /// 可空long值 + /// + public long? NullableLongValue { get; set; } + + /// + /// 导航属性 + /// + public StringAggregateRootSample StringSample { get; set; } + + /// + /// 导航属性集合 + /// + public ICollection IntSamples { get; set; } + + /// + /// 添加变更列表 + /// + protected override void AddChanges( AggregateRootSample other ) { + AddChange( "Name", "姓名", Name, other.Name ); + AddChange( t => t.MobilePhone, other.MobilePhone ); + AddChange( t => t.Tel, other.Tel ); + AddChange( StringSample, other.StringSample ); + AddChange( IntSamples, other.IntSamples ); + } + + /// + /// 创建测试样例 + /// + public static AggregateRootSample CreateSample() { + return CreateSample( Guid.NewGuid() ); + } + + /// + /// 创建测试样例 + /// + public static AggregateRootSample CreateSample( Guid id ) { + return new AggregateRootSample( id ) { + Name = "TestName", + EnglishName = "TestEnglishName", + MobilePhone = "13012345678" + }; + } + + /// + /// 创建测试样例2 + /// + public static AggregateRootSample CreateSample2() { + return CreateSample2( Guid.NewGuid() ); + } + + /// + /// 创建测试样例2 + /// + public static AggregateRootSample CreateSample2( Guid id ) { + return new AggregateRootSample( id ) { + Name = "TestName2", + EnglishName = "TestEnglishName2", + MobilePhone = "12012345678" + }; + } +} + +/// +/// 聚合根测试样例2 +/// +public class AggregateRootSample2 : AggregateRoot { + /// + /// 初始化聚合根测试样例 + /// + /// 标识 + public AggregateRootSample2( Guid id ) + : base( id ) { + } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Samples/Auditing/GuidAuditedEntity.cs b/test/Util.Domain.Tests/Samples/Auditing/GuidAuditedEntity.cs new file mode 100644 index 000000000..b4be25ffc --- /dev/null +++ b/test/Util.Domain.Tests/Samples/Auditing/GuidAuditedEntity.cs @@ -0,0 +1,45 @@ +using System; +using System.ComponentModel; +using Util.Domain.Auditing; +using Util.Domain.Entities; + +namespace Util.Domain.Tests.Samples.Auditing; + +/// +/// Guid审计测试实体 +/// +public class GuidAuditedEntity : AggregateRoot,IAudited { + /// + /// 初始化 + /// + public GuidAuditedEntity() : this( Guid.Empty ) { + } + + /// + /// 初始化 + /// + /// 实体标识 + public GuidAuditedEntity( Guid id ) : base( id ) { + } + + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid LastModifierId { get; set; } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Samples/Auditing/IntAuditedEntity.cs b/test/Util.Domain.Tests/Samples/Auditing/IntAuditedEntity.cs new file mode 100644 index 000000000..190fadbfb --- /dev/null +++ b/test/Util.Domain.Tests/Samples/Auditing/IntAuditedEntity.cs @@ -0,0 +1,45 @@ +using System; +using System.ComponentModel; +using Util.Domain.Auditing; +using Util.Domain.Entities; + +namespace Util.Domain.Tests.Samples.Auditing; + +/// +/// int审计测试实体 +/// +public class IntAuditedEntity : AggregateRoot,IAudited { + /// + /// 初始化 + /// + public IntAuditedEntity() : this( Guid.Empty ) { + } + + /// + /// 初始化 + /// + /// 实体标识 + public IntAuditedEntity( Guid id ) : base( id ) { + } + + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public int CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public int LastModifierId { get; set; } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Samples/Auditing/LongAuditedEntity.cs b/test/Util.Domain.Tests/Samples/Auditing/LongAuditedEntity.cs new file mode 100644 index 000000000..68c56d390 --- /dev/null +++ b/test/Util.Domain.Tests/Samples/Auditing/LongAuditedEntity.cs @@ -0,0 +1,45 @@ +using System; +using System.ComponentModel; +using Util.Domain.Auditing; +using Util.Domain.Entities; + +namespace Util.Domain.Tests.Samples.Auditing; + +/// +/// long审计测试实体 +/// +public class LongAuditedEntity : AggregateRoot,IAudited { + /// + /// 初始化 + /// + public LongAuditedEntity() : this( Guid.Empty ) { + } + + /// + /// 初始化 + /// + /// 实体标识 + public LongAuditedEntity( Guid id ) : base( id ) { + } + + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public long CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public long LastModifierId { get; set; } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Samples/Auditing/NullableGuidAuditedEntity.cs b/test/Util.Domain.Tests/Samples/Auditing/NullableGuidAuditedEntity.cs new file mode 100644 index 000000000..1f8db03c7 --- /dev/null +++ b/test/Util.Domain.Tests/Samples/Auditing/NullableGuidAuditedEntity.cs @@ -0,0 +1,45 @@ +using System; +using System.ComponentModel; +using Util.Domain.Auditing; +using Util.Domain.Entities; + +namespace Util.Domain.Tests.Samples.Auditing; + +/// +/// 可空Guid审计测试实体 +/// +public class NullableGuidAuditedEntity : AggregateRoot,IAudited { + /// + /// 初始化 + /// + public NullableGuidAuditedEntity() : this( Guid.Empty ) { + } + + /// + /// 初始化 + /// + /// 实体标识 + public NullableGuidAuditedEntity( Guid id ) : base( id ) { + } + + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Samples/Auditing/NullableIntAuditedEntity.cs b/test/Util.Domain.Tests/Samples/Auditing/NullableIntAuditedEntity.cs new file mode 100644 index 000000000..28e77e39a --- /dev/null +++ b/test/Util.Domain.Tests/Samples/Auditing/NullableIntAuditedEntity.cs @@ -0,0 +1,45 @@ +using System; +using System.ComponentModel; +using Util.Domain.Auditing; +using Util.Domain.Entities; + +namespace Util.Domain.Tests.Samples.Auditing; + +/// +/// 可空int审计测试实体 +/// +public class NullableIntAuditedEntity : AggregateRoot,IAudited { + /// + /// 初始化 + /// + public NullableIntAuditedEntity() : this( Guid.Empty ) { + } + + /// + /// 初始化 + /// + /// 实体标识 + public NullableIntAuditedEntity( Guid id ) : base( id ) { + } + + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public int? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public int? LastModifierId { get; set; } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Samples/Auditing/NullableLongAuditedEntity.cs b/test/Util.Domain.Tests/Samples/Auditing/NullableLongAuditedEntity.cs new file mode 100644 index 000000000..020664856 --- /dev/null +++ b/test/Util.Domain.Tests/Samples/Auditing/NullableLongAuditedEntity.cs @@ -0,0 +1,45 @@ +using System; +using System.ComponentModel; +using Util.Domain.Auditing; +using Util.Domain.Entities; + +namespace Util.Domain.Tests.Samples.Auditing; + +/// +/// long审计测试实体 +/// +public class NullableLongAuditedEntity : AggregateRoot,IAudited { + /// + /// 初始化 + /// + public NullableLongAuditedEntity() : this( Guid.Empty ) { + } + + /// + /// 初始化 + /// + /// 实体标识 + public NullableLongAuditedEntity( Guid id ) : base( id ) { + } + + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public long? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public long? LastModifierId { get; set; } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Samples/Auditing/StringAuditedEntity.cs b/test/Util.Domain.Tests/Samples/Auditing/StringAuditedEntity.cs new file mode 100644 index 000000000..0c4489ec2 --- /dev/null +++ b/test/Util.Domain.Tests/Samples/Auditing/StringAuditedEntity.cs @@ -0,0 +1,45 @@ +using System; +using System.ComponentModel; +using Util.Domain.Auditing; +using Util.Domain.Entities; + +namespace Util.Domain.Tests.Samples.Auditing; + +/// +/// string审计测试实体 +/// +public class StringAuditedEntity : AggregateRoot,IAudited { + /// + /// 初始化 + /// + public StringAuditedEntity() : this( Guid.Empty ) { + } + + /// + /// 初始化 + /// + /// 实体标识 + public StringAuditedEntity( Guid id ) : base( id ) { + } + + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public string CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public string LastModifierId { get; set; } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Samples/Customer.cs b/test/Util.Domain.Tests/Samples/Customer.cs new file mode 100644 index 000000000..d55d41efd --- /dev/null +++ b/test/Util.Domain.Tests/Samples/Customer.cs @@ -0,0 +1,115 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Domain.Auditing; +using Util.Domain.Entities; + +namespace Util.Domain.Tests.Samples; + +/// +/// 客户 +/// +[Description( "客户" )] +public class Customer : AggregateRoot,IDelete,IAudited{ + /// + /// 初始化客户 + /// + public Customer() : this( 0 ) { + } + + /// + /// 初始化客户 + /// + /// 客户标识 + public Customer( int id ) : base( id ) { + } + + /// + /// 编码 + /// + [Description( "编码" )] + [MaxLength( 100 )] + public string Code { get; set; } + /// + /// 姓名 + /// + [Description( "姓名" )] + [MaxLength( 200 )] + public string Name { get; set; } + /// + /// 昵称 + /// + [Description( "昵称" )] + [MaxLength( 50 )] + public string Nickname { get; set; } + /// + /// 性别 + /// + [Description( "性别" )] + public int? Gender { get; set; } + /// + /// 出生日期 + /// + [Description( "出生日期" )] + public DateTime? Birthday { get; set; } + /// + /// 民族 + /// + [Description( "民族" )] + public int? Nation { get; set; } + /// + /// 手机号 + /// + [Description( "手机号" )] + [MaxLength( 50 )] + public string Phone { get; set; } + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + [MaxLength( 500 )] + public string Email { get; set; } + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + + /// + /// 添加变更列表 + /// + protected override void AddChanges( Customer other ) { + AddChange( t => t.Code, other.Code ); + AddChange( t => t.Name, other.Name ); + AddChange( t => t.Nickname, other.Nickname ); + AddChange( t => t.Gender, other.Gender ); + AddChange( t => t.Birthday, other.Birthday ); + AddChange( t => t.Nation, other.Nation ); + AddChange( t => t.Phone, other.Phone ); + AddChange( t => t.Email, other.Email ); + AddChange( t => t.CreationTime, other.CreationTime ); + AddChange( t => t.CreatorId, other.CreatorId ); + AddChange( t => t.LastModificationTime, other.LastModificationTime ); + AddChange( t => t.LastModifierId, other.LastModifierId ); + } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Samples/EnumSample.cs b/test/Util.Domain.Tests/Samples/EnumSample.cs new file mode 100644 index 000000000..67e0caffe --- /dev/null +++ b/test/Util.Domain.Tests/Samples/EnumSample.cs @@ -0,0 +1,18 @@ +using System.ComponentModel; + +namespace Util.Domain.Tests.Samples; + +/// +/// 枚举测试样例 +/// +public enum EnumSample { + A = 1, + [Description( "B2" )] + B = 2, + [Description( "C3" )] + C = 3, + [Description( "D4" )] + D = 4, + [Description( "E5" )] + E = 5 +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Samples/IntAggregateRootSample.cs b/test/Util.Domain.Tests/Samples/IntAggregateRootSample.cs new file mode 100644 index 000000000..ea0bbf515 --- /dev/null +++ b/test/Util.Domain.Tests/Samples/IntAggregateRootSample.cs @@ -0,0 +1,40 @@ +using Util.Domain.Entities; + +namespace Util.Domain.Tests.Samples; + +/// +/// int聚合根测试样例 +/// +public class IntAggregateRootSample : AggregateRoot { + /// + /// 初始化int聚合根测试样例 + /// + public IntAggregateRootSample() + : this( 0 ) { + } + + /// + /// 初始化int聚合根测试样例 + /// + /// 标识 + public IntAggregateRootSample( int id ) + : base( id ) { + } + + /// + /// 姓名 + /// + public string Name { get; set; } + + /// + /// 编码 + /// + public string Code { get; set; } + + /// + /// 添加变更列表 + /// + protected override void AddChanges( IntAggregateRootSample other ) { + AddChange( "Name", "IntSampleName", Name, other.Name ); + } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Samples/Role.cs b/test/Util.Domain.Tests/Samples/Role.cs new file mode 100644 index 000000000..8b42e4567 --- /dev/null +++ b/test/Util.Domain.Tests/Samples/Role.cs @@ -0,0 +1,32 @@ +using System; +using Util.Domain.Trees; + +namespace Util.Domain.Tests.Samples; + +/// +/// 角色 +/// +public class Role : TreeEntityBase { + /// + /// 初始化角色 + /// + public Role() + : this( Guid.Empty, "", 0 ) { + } + + /// + /// 初始化角色 + /// + /// 角色标识 + /// 路径 + /// 级数 + public Role( Guid id, string path, int level ) + : base( id, path, level ) { + } +} + +/// +/// 角色仓储 +/// +public interface IRoleRepository : ITreeRepository { +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Samples/StringAggregateRootSample.cs b/test/Util.Domain.Tests/Samples/StringAggregateRootSample.cs new file mode 100644 index 000000000..ae008f430 --- /dev/null +++ b/test/Util.Domain.Tests/Samples/StringAggregateRootSample.cs @@ -0,0 +1,36 @@ +using System; +using Util.Domain.Entities; + +namespace Util.Domain.Tests.Samples; + +/// +/// string聚合根测试样例 +/// +public class StringAggregateRootSample : AggregateRoot { + /// + /// 初始化string聚合根测试样例 + /// + public StringAggregateRootSample() + : this( Guid.NewGuid().ToString() ) { + } + + /// + /// 初始化string聚合根测试样例 + /// + /// 标识 + public StringAggregateRootSample( string id ) + : base( id ) { + } + + /// + /// 姓名 + /// + public string Name { get; set; } + + /// + /// 添加变更列表 + /// + protected override void AddChanges( StringAggregateRootSample other ) { + AddChange( "Name", "StringSampleName", Name, other.Name ); + } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Samples/TreeEntitySample.cs b/test/Util.Domain.Tests/Samples/TreeEntitySample.cs new file mode 100644 index 000000000..c9a4f615b --- /dev/null +++ b/test/Util.Domain.Tests/Samples/TreeEntitySample.cs @@ -0,0 +1,29 @@ +using System; +using Util.Domain.Trees; + +namespace Util.Domain.Tests.Samples; + +/// +/// 树形实体测试样例 +/// +public class TreeEntitySample : TreeEntityBase { + /// + /// 初始化树型实体测试样例 + /// + public TreeEntitySample() : this( Guid.Empty ) { + } + + /// + /// 初始化树型实体测试样例 + /// + public TreeEntitySample( Guid id ) + : base( id, "", 0 ) { + } + + /// + /// 初始化树型实体测试样例 + /// + public TreeEntitySample( Guid id, string path ) + : base( id, path, 0 ) { + } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Samples/ValidationRuleSample.cs b/test/Util.Domain.Tests/Samples/ValidationRuleSample.cs new file mode 100644 index 000000000..29f1429c6 --- /dev/null +++ b/test/Util.Domain.Tests/Samples/ValidationRuleSample.cs @@ -0,0 +1,31 @@ +using System.ComponentModel.DataAnnotations; +using Util.Validation; + +namespace Util.Domain.Tests.Samples; + +/// +/// 验证规则样例 - 名称长度大于3将验证失败 +/// +public class ValidationRuleSample : IValidationRule { + /// + /// 初始化名称长度验证规则 + /// + /// 聚合根测试样例 + public ValidationRuleSample( AggregateRootSample sample ) { + _sample = sample; + } + + /// + /// 聚合根测试样例 + /// + private readonly AggregateRootSample _sample; + + /// + /// 验证 + /// + public ValidationResult Validate() { + if( _sample.Name.Length > 3 ) + return new ValidationResult( "名称长度不能大于3" ); + return ValidationResult.Success; + } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Samples/ValueObjectSample.cs b/test/Util.Domain.Tests/Samples/ValueObjectSample.cs new file mode 100644 index 000000000..1b9226084 --- /dev/null +++ b/test/Util.Domain.Tests/Samples/ValueObjectSample.cs @@ -0,0 +1,63 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Util.Domain.Entities; + +namespace Util.Domain.Tests.Samples; + +/// +/// 值对象测试样例 +/// +public class ValueObjectSample : ValueObjectBase { + /// + /// 初始化值对象测试样例 + /// + public ValueObjectSample() { + } + + /// + /// 初始化值对象测试样例 + /// + public ValueObjectSample( string city, string street ) + : this( city, street, null ) { + } + + /// + /// 初始化值对象测试样例 + /// + public ValueObjectSample( string city, string street, AggregateRootSample sample ) + : this( city, street, sample, null ) { + } + + /// + /// 初始化值对象测试样例 + /// + public ValueObjectSample( string city, string street, AggregateRootSample sample, ValueObjectSample child ) { + City = city; + Street = street; + AggregateRoot = sample; + Child = child; + } + + /// + /// 城市 + /// + [StringLength( 100, ErrorMessage = "城市输入过长,不能超过100位" )] + public string City { get; private set; } + /// + /// 街道 + /// + [StringLength( 200, ErrorMessage = "街道输入过长,不能超过200位" )] + public string Street { get; private set; } + + /// + /// 聚合根测试样例 + /// + [NotMapped] + public AggregateRootSample AggregateRoot { get; private set; } + + /// + /// 子值对象测试样例 + /// + [NotMapped] + public ValueObjectSample Child { get; private set; } +} \ No newline at end of file diff --git a/test/Util.Tests/TestResource.Designer.cs b/test/Util.Domain.Tests/TestResource.Designer.cs similarity index 86% rename from test/Util.Tests/TestResource.Designer.cs rename to test/Util.Domain.Tests/TestResource.Designer.cs index 75a02106b..bf6d2011f 100644 --- a/test/Util.Tests/TestResource.Designer.cs +++ b/test/Util.Domain.Tests/TestResource.Designer.cs @@ -1,72 +1,72 @@ -//------------------------------------------------------------------------------ -// -// 此代码由工具生成。 -// 运行时版本:4.0.30319.42000 -// -// 对此文件的更改可能会导致不正确的行为,并且如果 -// 重新生成代码,这些更改将会丢失。 -// -//------------------------------------------------------------------------------ - -namespace Util.Tests { - using System; - - - /// - /// 一个强类型的资源类,用于查找本地化的字符串等。 - /// - // 此类是由 StronglyTypedResourceBuilder - // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 - // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen - // (以 /str 作为命令选项),或重新生成 VS 项目。 - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class TestResource { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal TestResource() { - } - - /// - /// 返回此类使用的缓存的 ResourceManager 实例。 - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Util.Tests.TestResource", typeof(TestResource).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// 使用此强类型资源类,为所有资源查找 - /// 重写当前线程的 CurrentUICulture 属性。 - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// 查找类似 英文名不能为空 的本地化字符串。 - /// - public static string EnglishNameIsEmpty { - get { - return ResourceManager.GetString("EnglishNameIsEmpty", resourceCulture); - } - } - } -} +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace Util.Domain.Tests { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class TestResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal TestResource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Util.Domain.Tests.TestResource", typeof(TestResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 英文名不能为空 的本地化字符串。 + /// + public static string EnglishNameIsEmpty { + get { + return ResourceManager.GetString("EnglishNameIsEmpty", resourceCulture); + } + } + } +} diff --git a/test/Util.Tests/TestResource.resx b/test/Util.Domain.Tests/TestResource.resx similarity index 97% rename from test/Util.Tests/TestResource.resx rename to test/Util.Domain.Tests/TestResource.resx index 575f72d15..c9f0f013a 100644 --- a/test/Util.Tests/TestResource.resx +++ b/test/Util.Domain.Tests/TestResource.resx @@ -1,123 +1,123 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 英文名不能为空 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 英文名不能为空 + \ No newline at end of file diff --git a/test/Util.Domain.Tests/Trees/TreeEntityBaseTest.cs b/test/Util.Domain.Tests/Trees/TreeEntityBaseTest.cs new file mode 100644 index 000000000..a21828221 --- /dev/null +++ b/test/Util.Domain.Tests/Trees/TreeEntityBaseTest.cs @@ -0,0 +1,111 @@ +using System; +using Util.Domain.Tests.Samples; +using Xunit; + +namespace Util.Domain.Tests.Trees; + +/// +/// 树形实体测试 +/// +public class TreeEntityBaseTest { + /// + /// 测试初始化路径 - 1级 + /// + [Fact] + public void TestInitPath_1Level() { + var id = Guid.NewGuid(); + var entity = new TreeEntitySample( id ); + entity.InitPath(); + Assert.Equal( 1, entity.Level ); + Assert.Equal( $"{id},", entity.Path ); + } + + /// + /// 测试初始化路径 - 2级 + /// + [Fact] + public void TestInitPath_2Level() { + var parentId = Guid.NewGuid(); + var id = Guid.NewGuid(); + var parent = new TreeEntitySample( parentId ); + parent.InitPath(); + var child = new TreeEntitySample( id ); + child.InitPath( parent ); + Assert.Equal( 2, child.Level ); + Assert.Equal( $"{parentId},{id},", child.Path ); + } + + /// + /// 测试初始化路径 - 3级 + /// + [Fact] + public void TestInitPath_3Level() { + Guid id = Guid.NewGuid(); + Guid id2 = Guid.NewGuid(); + Guid id3 = Guid.NewGuid(); + var one = new TreeEntitySample( id ); + one.InitPath(); + var two = new TreeEntitySample( id2 ); + two.InitPath( one ); + var three = new TreeEntitySample( id3 ); + three.InitPath( two ); + Assert.Equal( 3, three.Level ); + Assert.Equal( $"{id},{id2},{id3},", three.Path ); + } + + /// + /// 测试从路径中获取所有上级节点标识 - 空路径 + /// + [Theory] + [InlineData(null)] + [InlineData( "" )] + public void TestGetParentIdsFromPath_Empty( string path ) { + var entity = new TreeEntitySample( Guid.NewGuid(), path ); + Assert.Empty( entity.GetParentIdsFromPath()); + } + + /// + /// 测试从路径中获取所有上级节点标识 - 1级 + /// + [Fact] + public void TestGetParentIdsFromPath_1Level() { + var entity = new TreeEntitySample( Guid.NewGuid() ); + entity.InitPath(); + Assert.Empty( entity.GetParentIdsFromPath()); + } + + /// + /// 测试从路径中获取所有上级节点标识 - 2级 + /// + [Fact] + public void TestGetParentIdsFromPath_2Level() { + var parentId = Guid.NewGuid(); + var id = Guid.NewGuid(); + var parent = new TreeEntitySample( parentId ); + parent.InitPath(); + var entity = new TreeEntitySample( id ); + entity.InitPath( parent ); + Assert.Single( entity.GetParentIdsFromPath()); + Assert.Equal( parentId, entity.GetParentIdsFromPath()[0] ); + } + + /// + /// 测试从路径中获取所有上级节点标识 - 忽略大小写 + /// + [Fact] + public void TestGetParentIdsFromPath_IgnoreCase() { + Guid id = new Guid( "38F7F754-802F-4F54-925D-CC066483F9DA" ); + var entity = new TreeEntitySample( id, "38F7F754-802F-4F54-925D-CC066483F9da" ); + Assert.Empty( entity.GetParentIdsFromPath()); + } + + /// + /// 测试从路径中获取所有上级节点标识 - 包含当前节点 + /// + [Fact] + public void TestGetParentIdsFromPath_ContainsSelf() { + var entity = new TreeEntitySample( Guid.NewGuid() ); + entity.InitPath(); + Assert.Single( entity.GetParentIdsFromPath( false )); + } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Trees/UpdatePathManagerTest.cs b/test/Util.Domain.Tests/Trees/UpdatePathManagerTest.cs new file mode 100644 index 000000000..89d893706 --- /dev/null +++ b/test/Util.Domain.Tests/Trees/UpdatePathManagerTest.cs @@ -0,0 +1,231 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Moq; +using Util.Domain.Properties; +using Util.Domain.Tests.Samples; +using Util.Domain.Tests.XUnitHelpers; +using Util.Domain.Trees; +using Util.Exceptions; +using Xunit; + +namespace Util.Domain.Tests.Trees; + +/// +/// 树型更新路径服务测试 +/// +public class UpdatePathManagerTest { + /// + /// 树型服务 + /// + private readonly UpdatePathManager _manager; + /// + /// 角色仓储 + /// + private readonly Mock _mockRepository; + /// + /// 编号 + /// + private readonly Guid _id = "10000001-AAF2-4D03-9310-FEF2F47B9FE2".ToGuid(); + /// + /// 编号2 + /// + private readonly Guid _id2 = "10000002-AAF2-4D03-9310-FEF2F47B9FE2".ToGuid(); + /// + /// 编号3 + /// + private readonly Guid _id3 = "10000003-AAF2-4D03-9310-FEF2F47B9FE2".ToGuid(); + /// + /// 编号4 + /// + private readonly Guid _id4 = "10000004-AAF2-4D03-9310-FEF2F47B9FE2".ToGuid(); + /// + /// 编号5 + /// + private readonly Guid _id5 = "10000005-AAF2-4D03-9310-FEF2F47B9FE2".ToGuid(); + + + /// + /// 测试初始化 + /// + public UpdatePathManagerTest() { + _mockRepository = new Mock(); + _manager = new UpdatePathManager( _mockRepository.Object ); + } + + /// + /// 修改父节点 - 数据库中未找到 - Path无变化 + /// + [Fact] + public async Task TestUpdatePathAsync_1() { + //设置 + string path = $"{_id},"; + _mockRepository.Setup( t => t.FindByIdAsync( _id, It.IsAny() ) ).Returns( Task.FromResult( null ) ); + + //执行 + Role role = new Role( _id, path, 0 ) { ParentId = _id2 }; + await _manager.UpdatePathAsync( role ); + + //验证 + _mockRepository.Verify( t => t.GetAllChildrenAsync( role ), Times.Never ); + Assert.Equal( path, role.Path ); + } + + /// + /// 修改父节点 - 父节点未修改 - Path无变化 + /// + [Fact] + public async Task TestUpdatePathAsync_2() { + //设置 + string path = $"{_id2},{_id},"; + _mockRepository.Setup( t => t.FindByIdAsync( _id, It.IsAny() ) ).ReturnsAsync( new Role( _id, path, 2 ) { ParentId = _id2 } ); + + //执行 + Role role = new Role( _id, path, 2 ) { ParentId = _id2 }; + await _manager.UpdatePathAsync( role ); + + //验证 + _mockRepository.Verify(t => t.GetAllChildrenAsync( role ),Times.Never ); + Assert.Equal( path, role.Path ); + } + + /// + /// 修改父节点 - 父节点已修改 - 无下级子节点 + /// + [Fact] + public async Task TestUpdatePathAsync_3() { + //设置 + string path = $"{_id2},{_id},"; + var old = new Role( _id, path, 2 ) { ParentId = _id2 }; + _mockRepository.Setup( t => t.FindByIdAsync( _id, It.IsAny() ) ).ReturnsAsync( old ); + _mockRepository.Setup( t => t.FindByIdAsync( _id3, It.IsAny() ) ).ReturnsAsync( new Role( _id3, $"{_id3},", 1 ) ); + + var list = new List { }; + _mockRepository.Setup( t => t.GetAllChildrenAsync( old ) ).ReturnsAsync( list ); + + //执行 + Role role = new Role( _id, path, 2 ) { ParentId = _id3 }; + await _manager.UpdatePathAsync( role ); + + //验证 + Assert.Equal( $"{_id3},{_id},", role.Path ); + Assert.Equal( 2, role.Level ); + } + + /// + /// 修改父节点 - 父节点已修改 - 下级1个子节点 + /// + [Fact] + public async Task TestUpdatePathAsync_4() { + //设置 + string path = $"{_id2},{_id},"; + var old = new Role( _id, path, 2 ) { ParentId = _id2 }; + var child = new Role( _id4, $"{path},{_id4}", 3 ) { ParentId = _id }; + _mockRepository.Setup( t => t.FindByIdAsync( _id, It.IsAny() ) ).ReturnsAsync( old ); + _mockRepository.Setup( t => t.FindByIdAsync( _id3, It.IsAny() ) ).ReturnsAsync( new Role( _id3, $"{_id3},", 1 ) ); + + var list = new List { child }; + _mockRepository.Setup( t => t.GetAllChildrenAsync( old ) ).ReturnsAsync( list ); + + //执行 + Role role = new Role( _id, path, 2 ) { ParentId = _id3 }; + await _manager.UpdatePathAsync( role ); + + //验证 + Assert.Equal( $"{_id3},{_id},", role.Path ); + Assert.Equal( $"{_id3},{_id},{_id4},", list[0].Path ); + } + + /// + /// 修改父节点 - 父节点已修改 - 直接下级2个子节点 + /// + [Fact] + public async Task TestUpdatePathAsync_5() { + //设置 + string path = $"{_id2},{_id},"; + var old = new Role( _id, path, 2 ) { ParentId = _id2 }; + var child1 = new Role( _id4, $"{path},{_id4}", 3 ) { ParentId = _id }; + var child2 = new Role( _id5, $"{path},{_id5}", 3 ) { ParentId = _id }; + _mockRepository.Setup( t => t.FindByIdAsync( _id, It.IsAny() ) ).ReturnsAsync( old ); + _mockRepository.Setup( t => t.FindByIdAsync( _id3, It.IsAny() ) ).ReturnsAsync( new Role( _id3, $"{_id3},", 1 ) ); + + var list = new List { child1, child2 }; + _mockRepository.Setup( t => t.GetAllChildrenAsync( old ) ).ReturnsAsync( list ); + + //执行 + Role role = new Role( _id, path, 2 ) { ParentId = _id3 }; + await _manager.UpdatePathAsync( role ); + + //验证 + Assert.Equal( $"{_id3},{_id},", role.Path ); + Assert.Equal( $"{_id3},{_id},{_id4},", list[0].Path ); + Assert.Equal( $"{_id3},{_id},{_id5},", list[1].Path ); + } + + /// + /// 修改父节点 - 父节点已修改 - 下级2层子节点 + /// + [Fact] + public async Task TestUpdatePathAsync_6() { + //设置 + string path = $"{_id2},{_id},"; + var old = new Role( _id, path, 2 ) { ParentId = _id2 }; + var child1 = new Role( _id4, $"{path},{_id4}", 3 ) { ParentId = _id }; + var child2 = new Role( _id5, $"{path},{_id4},{_id5}", 4 ) { ParentId = _id4 }; + _mockRepository.Setup( t => t.FindByIdAsync( _id, It.IsAny() ) ).ReturnsAsync( old ); + _mockRepository.Setup( t => t.FindByIdAsync( _id3, It.IsAny() ) ).ReturnsAsync( new Role( _id3, $"{_id3},", 1 ) ); + + var list = new List { child1, child2 }; + _mockRepository.Setup( t => t.GetAllChildrenAsync( old ) ).ReturnsAsync( list ); + + //执行 + Role role = new Role( _id, path, 2 ) { ParentId = _id3 }; + await _manager.UpdatePathAsync( role ); + + //验证 + Assert.Equal( $"{_id3},{_id},", role.Path ); + Assert.Equal( $"{_id3},{_id},{_id4},", list[0].Path ); + Assert.Equal( $"{_id3},{_id},{_id4},{_id5},", list[1].Path ); + } + + /// + /// 修改父节点 - 将父节点移动到子节点下 + /// + [Fact] + public async Task TestUpdatePathAsync_7() { + //设置 + string path = $"{_id},"; + var old = new Role( _id, path, 1 ); + var child = new Role( _id2, $"{_id},{_id2},", 2 ) { ParentId = _id }; + _mockRepository.Setup( t => t.FindByIdAsync( _id, It.IsAny() ) ).ReturnsAsync( old ); + var list = new List { child }; + _mockRepository.Setup( t => t.GetAllChildrenAsync( old ) ).ReturnsAsync( list ); + + //执行验证 + await AssertHelper.ThrowsAsync( async () => { + Role result = new Role( _id, path, 1 ) { ParentId = _id2 }; + await _manager.UpdatePathAsync( result ); + }, DomainResource.NotSupportMoveToChildren ); + } + + /// + /// 修改父节点 - 将父节点设置为自己 + /// + [Fact] + public async Task TestUpdatePathAsync_8() { + //设置 + string path = $"{_id},"; + var old = new Role( _id, path, 1 ); + var child = new Role( _id2, $"{_id},{_id2},", 2 ) { ParentId = _id }; + _mockRepository.Setup( t => t.FindByIdAsync( _id, It.IsAny() ) ).ReturnsAsync( old ); + var list = new List { child }; + _mockRepository.Setup( t => t.GetAllChildrenAsync( old ) ).ReturnsAsync( list ); + + //执行验证 + await AssertHelper.ThrowsAsync( async () => { + Role result = new Role( _id, path, 1 ) { ParentId = _id }; + await _manager.UpdatePathAsync( result ); + }, DomainResource.NotSupportMoveToChildren ); + } +} \ No newline at end of file diff --git a/test/Util.Domain.Tests/Util.Domain.Tests.csproj b/test/Util.Domain.Tests/Util.Domain.Tests.csproj new file mode 100644 index 000000000..14ee7018e --- /dev/null +++ b/test/Util.Domain.Tests/Util.Domain.Tests.csproj @@ -0,0 +1,41 @@ + + + + $(NetTargetFramework) + false + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + True + True + TestResource.resx + + + + + + PublicResXFileCodeGenerator + TestResource.Designer.cs + + + + diff --git a/test/Util.Domain.Tests/XUnitHelpers/AssertHelper.cs b/test/Util.Domain.Tests/XUnitHelpers/AssertHelper.cs new file mode 100644 index 000000000..8db8fb9b2 --- /dev/null +++ b/test/Util.Domain.Tests/XUnitHelpers/AssertHelper.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading.Tasks; +using Xunit; + +namespace Util.Domain.Tests.XUnitHelpers; + +/// +/// 断言辅助操作 +/// +public class AssertHelper { + /// + /// 抛出异常,并从异常消息中搜索特定关键字 + /// + /// 异常类型 + /// 操作 + /// 关键字 + public static TException Throws( Action action,string keyword = "" ) where TException : Exception { + var exception = Assert.Throws( action ); + if( !string.IsNullOrWhiteSpace( keyword ) ) + Assert.Contains( keyword, exception.Message ); + return exception; + } + + /// + /// 抛出异常,并从异常消息中搜索特定关键字 + /// + /// 异常类型 + /// 操作 + /// 关键字 + public static async Task ThrowsAsync( Func action, string keyword = "" ) where TException : Exception { + var exception = await Assert.ThrowsAsync( action ); + if( !string.IsNullOrWhiteSpace( keyword ) ) + Assert.Contains( keyword, exception.Message ); + return exception; + } +} \ No newline at end of file diff --git a/test/Util.Events.MediatR.Tests.Integration/Local/EventBusExtensionsTest.cs b/test/Util.Events.MediatR.Tests.Integration/Local/EventBusExtensionsTest.cs new file mode 100644 index 000000000..81c6d7280 --- /dev/null +++ b/test/Util.Events.MediatR.Tests.Integration/Local/EventBusExtensionsTest.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Util.Events.Tests.Samples; +using Xunit; + +namespace Util.Events.Tests.Local; + +/// +/// 本地事件总线扩展测试 +/// +public class EventBusExtensionsTest { + /// + /// 本地事件总线 + /// + private readonly ILocalEventBus _eventBus; + + /// + /// 测试初始化 + /// + public EventBusExtensionsTest( ILocalEventBus eventBus ) { + _eventBus = eventBus; + } + + /// + /// 测试发布事件集合 + /// + [Fact] + public async Task TestPublishAsync() { + var event1 = new EventSample { Value = "a" }; + var event2 = new EventSample2(); + var event3 = new EventSample3(); + var events = new List { event1, event2, event3 }; + await _eventBus.PublishAsync( events ); + Assert.Equal( "1:a", event1.Result ); + Assert.Equal( "2", event2.Result ); + Assert.Equal( "3", event3.Result ); + } +} \ No newline at end of file diff --git a/test/Util.Events.MediatR.Tests.Integration/Local/EventBusTest.cs b/test/Util.Events.MediatR.Tests.Integration/Local/EventBusTest.cs new file mode 100644 index 000000000..5e035d7c0 --- /dev/null +++ b/test/Util.Events.MediatR.Tests.Integration/Local/EventBusTest.cs @@ -0,0 +1,31 @@ +using System.Threading.Tasks; +using Util.Events.Tests.Samples; + +namespace Util.Events.Tests.Local; + +/// +/// 事件总线测试 +/// +public class EventBusTest { + /// + /// 事件总线 + /// + private readonly IEventBus _eventBus; + + /// + /// 测试初始化 + /// + public EventBusTest( IEventBus eventBus ) { + _eventBus = eventBus; + } + + /// + /// 测试发布事件 + /// + [Fact] + public async Task TestPublishAsync() { + var @event = new EventSample { Value = "a" }; + await _eventBus.PublishAsync( @event ); + Assert.Equal( "1:a", @event.Result ); + } +} \ No newline at end of file diff --git a/test/Util.Events.MediatR.Tests.Integration/Local/LocalEventBusTest.cs b/test/Util.Events.MediatR.Tests.Integration/Local/LocalEventBusTest.cs new file mode 100644 index 000000000..fb9112137 --- /dev/null +++ b/test/Util.Events.MediatR.Tests.Integration/Local/LocalEventBusTest.cs @@ -0,0 +1,43 @@ +using System.Threading; +using System.Threading.Tasks; +using Util.Events.Tests.Samples; + +namespace Util.Events.Tests.Local; + +/// +/// 本地事件总线测试 +/// +public class LocalEventBusTest { + /// + /// 本地事件总线 + /// + private readonly ILocalEventBus _eventBus; + + /// + /// 测试初始化 + /// + public LocalEventBusTest( ILocalEventBus eventBus ) { + _eventBus = eventBus; + } + + /// + /// 测试发布事件 - 传入事件参数是具体事件类型 + /// + [Fact] + public async Task TestPublishAsync() { + var token = new CancellationTokenSource().Token; + var @event = new EventSample { Value = "a" }; + await _eventBus.PublishAsync( @event, token ); + Assert.Equal( "1:a", @event.Result ); + } + + /// + /// 测试发布事件 - 传入事件参数是IEvent接口 + /// + [Fact] + public async Task TestPublishAsync_2() { + IEvent @event = new EventSample { Value = "a" }; + await _eventBus.PublishAsync( @event ); + Assert.Equal( "1:a", ((EventSample)@event).Result ); + } +} \ No newline at end of file diff --git a/test/Util.Events.MediatR.Tests.Integration/Samples/EventHandlerSample.cs b/test/Util.Events.MediatR.Tests.Integration/Samples/EventHandlerSample.cs new file mode 100644 index 000000000..f2bf8838c --- /dev/null +++ b/test/Util.Events.MediatR.Tests.Integration/Samples/EventHandlerSample.cs @@ -0,0 +1,50 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Util.Events.Tests.Samples; + +/// +/// 本地事件处理器样例 +/// +public class EventHandlerSample : EventHandlerBase { + /// + /// 处理事件 + /// + /// 事件 + /// 取消令牌 + public override Task HandleAsync( EventSample @event, CancellationToken cancellationToken = default ) { + @event.Result = $"1:{@event.Value}"; + return Task.CompletedTask; + } +} + +/// +/// 本地事件处理器样例2 +/// +public class EventHandlerSample2 : EventHandlerBase { + /// + /// 处理事件 + /// + /// 事件 + /// 取消令牌 + public override Task HandleAsync( EventSample2 @event, CancellationToken cancellationToken = default ) { + @event.Result += "2"; + return Task.CompletedTask; + } +} + + +/// +/// 本地事件处理器样例4 +/// +public class EventHandlerSample4 : EventHandlerBase { + /// + /// 处理事件 + /// + /// 事件 + /// 取消令牌 + public override Task HandleAsync( EventSample3 @event, CancellationToken cancellationToken = default ) { + @event.Result += "3"; + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/test/Util.Events.MediatR.Tests.Integration/Samples/EventSample.cs b/test/Util.Events.MediatR.Tests.Integration/Samples/EventSample.cs new file mode 100644 index 000000000..8d37b454b --- /dev/null +++ b/test/Util.Events.MediatR.Tests.Integration/Samples/EventSample.cs @@ -0,0 +1,35 @@ +namespace Util.Events.Tests.Samples; + +/// +/// 本地事件样例 +/// +public class EventSample : EventBase { + /// + /// 值 + /// + public string Value { get; set; } + /// + /// 结果 + /// + public string Result { get; set; } +} + +/// +/// 本地事件样例2 +/// +public class EventSample2 : EventBase { + /// + /// 结果 + /// + public string Result { get; set; } +} + +/// +/// 本地事件样例3 +/// +public class EventSample3 : EventBase { + /// + /// 结果 + /// + public string Result { get; set; } +} \ No newline at end of file diff --git a/test/Util.Events.MediatR.Tests.Integration/Startup.cs b/test/Util.Events.MediatR.Tests.Integration/Startup.cs new file mode 100644 index 000000000..65f5469ea --- /dev/null +++ b/test/Util.Events.MediatR.Tests.Integration/Startup.cs @@ -0,0 +1,27 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit.DependencyInjection.Logging; + +namespace Util.Events.Tests; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + hostBuilder + .AsBuild() + .AddMediatR() + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + } +} \ No newline at end of file diff --git a/test/Util.Events.MediatR.Tests.Integration/Usings.cs b/test/Util.Events.MediatR.Tests.Integration/Usings.cs new file mode 100644 index 000000000..8c927eb74 --- /dev/null +++ b/test/Util.Events.MediatR.Tests.Integration/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/test/Util.Events.MediatR.Tests.Integration/Util.Events.MediatR.Tests.Integration.csproj b/test/Util.Events.MediatR.Tests.Integration/Util.Events.MediatR.Tests.Integration.csproj new file mode 100644 index 000000000..5253f97d0 --- /dev/null +++ b/test/Util.Events.MediatR.Tests.Integration/Util.Events.MediatR.Tests.Integration.csproj @@ -0,0 +1,30 @@ + + + + $(NetTargetFramework) + false + Util.Events.Tests + Util.Events.Tests + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.Events.Tests.Integration/Local/EventBusExtensionsTest.cs b/test/Util.Events.Tests.Integration/Local/EventBusExtensionsTest.cs new file mode 100644 index 000000000..6c511febb --- /dev/null +++ b/test/Util.Events.Tests.Integration/Local/EventBusExtensionsTest.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Util.Events.Tests.Samples; +using Xunit; + +namespace Util.Events.Tests.Local; + +/// +/// 本地事件总线扩展测试 +/// +public class EventBusExtensionsTest { + /// + /// 本地事件总线 + /// + private readonly ILocalEventBus _eventBus; + + /// + /// 测试初始化 + /// + public EventBusExtensionsTest( ILocalEventBus eventBus ) { + _eventBus = eventBus; + } + + /// + /// 测试发布事件集合 + /// + [Fact] + public async Task TestPublishAsync() { + var event1 = new EventSample { Value = "a" }; + var event2 = new EventSample2(); + var event3 = new EventSample3(); + var events = new List { event1, event2, event3 }; + await _eventBus.PublishAsync( events ); + Assert.Equal( "1:a", event1.Result ); + Assert.Equal( "23", event2.Result ); + Assert.Equal( "54", event3.Result ); + } +} \ No newline at end of file diff --git a/test/Util.Events.Tests.Integration/Local/EventBusTest.cs b/test/Util.Events.Tests.Integration/Local/EventBusTest.cs new file mode 100644 index 000000000..b19f9f7d0 --- /dev/null +++ b/test/Util.Events.Tests.Integration/Local/EventBusTest.cs @@ -0,0 +1,52 @@ +using System.Threading.Tasks; +using Util.Events.Tests.Samples; +using Xunit; + +namespace Util.Events.Tests.Local; + +/// +/// 事件总线测试 +/// +public class EventBusTest { + /// + /// 事件总线 + /// + private readonly IEventBus _eventBus; + + /// + /// 测试初始化 + /// + public EventBusTest( IEventBus eventBus ) { + _eventBus = eventBus; + } + + /// + /// 测试发布事件 + /// + [Fact] + public async Task TestPublishAsync() { + var @event = new EventSample { Value = "a" }; + await _eventBus.PublishAsync( @event ); + Assert.Equal( "1:a", @event.Result ); + } + + /// + /// 测试发布事件 - 测试排序 - 未设置序号 + /// + [Fact] + public async Task TestPublishAsync_Order_1() { + var @event = new EventSample2(); + await _eventBus.PublishAsync( @event ); + Assert.Equal( "23", @event.Result ); + } + + /// + /// 测试发布事件 - 测试排序 - 设置序号 + /// + [Fact] + public async Task TestPublishAsync_Order_2() { + var @event = new EventSample3(); + await _eventBus.PublishAsync( @event ); + Assert.Equal( "54", @event.Result ); + } +} \ No newline at end of file diff --git a/test/Util.Events.Tests.Integration/Local/LocalEventBusTest.cs b/test/Util.Events.Tests.Integration/Local/LocalEventBusTest.cs new file mode 100644 index 000000000..6d28c485c --- /dev/null +++ b/test/Util.Events.Tests.Integration/Local/LocalEventBusTest.cs @@ -0,0 +1,74 @@ +using System.Threading; +using System.Threading.Tasks; +using Util.Events.Tests.Samples; +using Xunit; + +namespace Util.Events.Tests.Local; + +/// +/// 本地事件总线测试 +/// +public class LocalEventBusTest { + /// + /// 本地事件总线 + /// + private readonly ILocalEventBus _eventBus; + + /// + /// 测试初始化 + /// + public LocalEventBusTest( ILocalEventBus eventBus ) { + _eventBus = eventBus; + } + + /// + /// 测试发布事件 - 传入事件参数是具体事件类型 + /// + [Fact] + public async Task TestPublishAsync() { + var token = new CancellationTokenSource().Token; + var @event = new EventSample { Value = "a" }; + await _eventBus.PublishAsync( @event, token ); + Assert.Equal( "1:a", @event.Result ); + } + + /// + /// 测试发布事件 - 传入事件参数是IEvent接口 + /// + [Fact] + public async Task TestPublishAsync_2() { + IEvent @event = new EventSample { Value = "a" }; + await _eventBus.PublishAsync( @event ); + Assert.Equal( "1:a", ((EventSample)@event).Result ); + } + + /// + /// 测试发布事件 - 测试排序 - 未设置序号 + /// + [Fact] + public async Task TestPublishAsync_Order() { + var @event = new EventSample2(); + await _eventBus.PublishAsync( @event ); + Assert.Equal( "23", @event.Result ); + } + + /// + /// 测试发布事件 - 测试排序 - 设置序号 + /// + [Fact] + public async Task TestPublishAsync_Order_2() { + var @event = new EventSample3(); + await _eventBus.PublishAsync( @event ); + Assert.Equal( "54", @event.Result ); + } + + /// + /// 测试发布事件 - 测试排序 - 设置序号 - 传入事件参数是IEvent接口 + /// + [Fact] + public async Task TestPublishAsync_Order_3() { + IEvent @event = new EventSample3(); + await _eventBus.PublishAsync( @event ); + Assert.Equal( "54", ( (EventSample3)@event ).Result ); + } +} \ No newline at end of file diff --git a/test/Util.Events.Tests.Integration/Samples/EventHandlerSample.cs b/test/Util.Events.Tests.Integration/Samples/EventHandlerSample.cs new file mode 100644 index 000000000..148569520 --- /dev/null +++ b/test/Util.Events.Tests.Integration/Samples/EventHandlerSample.cs @@ -0,0 +1,114 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Util.Events.Tests.Samples; + +/// +/// 本地事件处理器样例 +/// +public class EventHandlerSample : EventHandlerBase { + /// + /// 处理事件 + /// + /// 事件 + /// 取消令牌 + public override Task HandleAsync( EventSample @event, CancellationToken cancellationToken = default ) { + @event.Result = $"1:{@event.Value}"; + return Task.CompletedTask; + } +} + +/// +/// 本地事件处理器样例2 +/// +public class EventHandlerSample2 : EventHandlerBase { + /// + /// 处理事件 + /// + /// 事件 + /// 取消令牌 + public override Task HandleAsync( EventSample2 @event, CancellationToken cancellationToken = default ) { + @event.Result += "2"; + return Task.CompletedTask; + } +} + +/// +/// 本地事件处理器样例3 +/// +public class EventHandlerSample3 : EventHandlerBase { + /// + /// 处理事件 + /// + /// 事件 + /// 取消令牌 + public override Task HandleAsync( EventSample2 @event, CancellationToken cancellationToken = default ) { + @event.Result += "3"; + return Task.CompletedTask; + } +} + +/// +/// 本地事件处理器样例4 +/// +public class EventHandlerSample4 : EventHandlerBase { + /// + /// 处理事件 + /// + /// 事件 + /// 取消令牌 + public override Task HandleAsync( EventSample3 @event, CancellationToken cancellationToken = default ) { + @event.Result += "4"; + return Task.CompletedTask; + } + + /// + /// 序号 + /// + public override int Order => 2; +} + +/// +/// 本地事件处理器样例5 +/// +public class EventHandlerSample5: EventHandlerBase { + /// + /// 处理事件 + /// + /// 事件 + /// 取消令牌 + public override Task HandleAsync( EventSample3 @event, CancellationToken cancellationToken = default ) { + @event.Result += "5"; + return Task.CompletedTask; + } + + /// + /// 序号 + /// + public override int Order => 1; +} + +/// +/// 本地事件处理器样例6 - 禁用该事件处理器 +/// +public class EventHandlerSample6 : EventHandlerBase { + /// + /// 处理事件 + /// + /// 事件 + /// 取消令牌 + public override Task HandleAsync( EventSample3 @event, CancellationToken cancellationToken = default ) { + @event.Result += "6"; + return Task.CompletedTask; + } + + /// + /// 序号 + /// + public override int Order => 3; + + /// + /// 是否启用 + /// + public override bool Enabled => false; +} \ No newline at end of file diff --git a/test/Util.Events.Tests.Integration/Samples/EventSample.cs b/test/Util.Events.Tests.Integration/Samples/EventSample.cs new file mode 100644 index 000000000..d4450b260 --- /dev/null +++ b/test/Util.Events.Tests.Integration/Samples/EventSample.cs @@ -0,0 +1,35 @@ +namespace Util.Events.Tests.Samples; + +/// +/// 本地事件样例 +/// +public class EventSample : IEvent{ + /// + /// 值 + /// + public string Value { get; set; } + /// + /// 结果 + /// + public string Result { get; set; } +} + +/// +/// 本地事件样例2 +/// +public class EventSample2 : IEvent { + /// + /// 结果 + /// + public string Result { get; set; } +} + +/// +/// 本地事件样例3 +/// +public class EventSample3 : IEvent { + /// + /// 结果 + /// + public string Result { get; set; } +} \ No newline at end of file diff --git a/test/Util.Events.Tests.Integration/Startup.cs b/test/Util.Events.Tests.Integration/Startup.cs new file mode 100644 index 000000000..fd9a95373 --- /dev/null +++ b/test/Util.Events.Tests.Integration/Startup.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit.DependencyInjection.Logging; + +namespace Util.Events.Tests; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + hostBuilder + .AsBuild() + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + } +} \ No newline at end of file diff --git a/test/Util.Events.Tests.Integration/Util.Events.Tests.Integration.csproj b/test/Util.Events.Tests.Integration/Util.Events.Tests.Integration.csproj new file mode 100644 index 000000000..5970e2e38 --- /dev/null +++ b/test/Util.Events.Tests.Integration/Util.Events.Tests.Integration.csproj @@ -0,0 +1,30 @@ + + + + $(NetTargetFramework) + false + Util.Events.Tests + Util.Events.Tests + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.FileStorage.Aliyun.Tests.Integration/Resources/a.png b/test/Util.FileStorage.Aliyun.Tests.Integration/Resources/a.png new file mode 100644 index 000000000..ace11c472 Binary files /dev/null and b/test/Util.FileStorage.Aliyun.Tests.Integration/Resources/a.png differ diff --git a/test/Util.FileStorage.Aliyun.Tests.Integration/Samples/TestSession.cs b/test/Util.FileStorage.Aliyun.Tests.Integration/Samples/TestSession.cs new file mode 100644 index 000000000..0c71ff39d --- /dev/null +++ b/test/Util.FileStorage.Aliyun.Tests.Integration/Samples/TestSession.cs @@ -0,0 +1,15 @@ +using System; +using Util.Sessions; + +namespace Util.FileStorage.Aliyun.Samples; + +/// +/// 测试用户会话 +/// +public class TestSession : ISession { + public IServiceProvider ServiceProvider => null; + public static Guid TestUserId = new ( "2af5e99e-391c-451b-9112-f7a3eb9b0a55" ); + public bool IsAuthenticated => true; + public string UserId => TestUserId.ToString(); + public string TenantId => string.Empty; +} \ No newline at end of file diff --git a/test/Util.FileStorage.Aliyun.Tests.Integration/Startup.cs b/test/Util.FileStorage.Aliyun.Tests.Integration/Startup.cs new file mode 100644 index 000000000..743a6491d --- /dev/null +++ b/test/Util.FileStorage.Aliyun.Tests.Integration/Startup.cs @@ -0,0 +1,45 @@ +using Util.FileStorage.Aliyun.Samples; + +namespace Util.FileStorage.Aliyun; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Util.Helpers.Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .ConfigureWebHostDefaults( webHostBuilder => { + webHostBuilder.UseTestServer() + .ConfigureServices( services => { + services.AddControllers(); + services.AddHttpClient(); + } ) + .Configure( t => { + t.UseRouting(); + t.UseEndpoints( endpoints => { + endpoints.MapControllers(); + } ); + } ); + } ) + .AsBuild() + .AddAliyunOss( options => { + options.Endpoint = Util.Helpers.Config.GetValue( "AliyunOss:Endpoint" ); + options.AccessKeyId = Util.Helpers.Config.GetValue( "AliyunOss:AccessKeyId" ); + options.AccessKeySecret = Util.Helpers.Config.GetValue( "AliyunOss:AccessKeySecret" ); + options.DefaultBucketName = "Util.AliyunOss.Test"; + } ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddSingleton(); + } +} \ No newline at end of file diff --git a/test/Util.FileStorage.Aliyun.Tests.Integration/Tests/AliyunFileStoreTest.cs b/test/Util.FileStorage.Aliyun.Tests.Integration/Tests/AliyunFileStoreTest.cs new file mode 100644 index 000000000..969ac3860 --- /dev/null +++ b/test/Util.FileStorage.Aliyun.Tests.Integration/Tests/AliyunFileStoreTest.cs @@ -0,0 +1,381 @@ +using Util.FileStorage.Aliyun.Samples; +using Util.Helpers; +using Util.Http; +using File = Util.Helpers.File; + +namespace Util.FileStorage.Aliyun.Tests; + +/// +/// 阿里云对象存储服务测试 +/// +public class AliyunFileStoreTest : IDisposable { + + #region 测试初始化 + + /// + /// 文件存储服务 + /// + private readonly AliyunFileStore _fileStore; + /// + /// 输出操作 + /// + private readonly ITestOutputHelper _testOutputHelper; + /// + /// Http客户端 + /// + private IHttpClient _client; + + /// + /// 测试初始化 + /// + public AliyunFileStoreTest( IFileStore fileStore, ITestOutputHelper testOutputHelper, IHttpClient client ) { + _fileStore = fileStore as AliyunFileStore; + _testOutputHelper = testOutputHelper; + _client = client; + Time.SetTime( new DateTime( 2012, 12, 12, 12, 12, 12, 123 ) ); + } + + #endregion + + #region 测试清理 + + /// + /// 测试清理 + /// + public void Dispose() { + Time.Reset(); + } + + #endregion + + #region CreateBucketAsync + + /// + /// 测试创建存储桶 - 修正名称有大写字母和下划线及两侧横线 + /// + [Fact] + public async Task TestCreateBucketAsync_1() { + var name = $"-Test{Id.Create()}_1_"; + + //创建桶 + await _fileStore.CreateBucketAsync( name ); + + //验证 + var result = await _fileStore.BucketExistsAsync( name ); + Assert.True( result ); + + //删除桶 + await _fileStore.DeleteBucketAsync( name ); + + //验证桶已不存在 + result = await _fileStore.BucketExistsAsync( name ); + Assert.False( result ); + } + + #endregion + + #region FileExistsAsync + + /// + /// 测试文件是否存在 - 默认桶 + /// + [Fact] + public async Task TestFileExistsAsync_1() { + //定义文件名 + var name = $"{Id.Create()}.png"; + + //文件未创建不存在 + var result = await _fileStore.FileExistsAsync( name ); + Assert.False( result ); + + //读取文件 + var path = Common.GetPhysicalPath( "~/Resources/a.png" ); + await using var stream = File.ReadToStream( path ); + + //创建文件 + await _fileStore.SaveFileAsync( stream, name ); + + //验证 + result = await _fileStore.FileExistsAsync( name ); + Assert.True( result ); + } + + /// + /// 测试文件是否存在 - 指定存储桶 + /// + [Fact] + public async Task TestFileExistsAsync_2() { + //定义变量 + var name = $"{Id.Create()}.png"; + var bucketName = "TestFileExistsAsync_2"; + + //文件未创建不存在 + var result = await _fileStore.FileExistsAsync( new FileExistsArgs( name ) { BucketName = bucketName } ); + Assert.False( result ); + + //读取文件 + var path = Common.GetPhysicalPath( "~/Resources/a.png" ); + await using var stream = File.ReadToStream( path ); + + //创建文件 + await _fileStore.SaveFileAsync( new SaveFileArgs( name, stream ) { BucketName = bucketName } ); + + //验证 + result = await _fileStore.FileExistsAsync( new FileExistsArgs( name ) { BucketName = bucketName } ); + Assert.True( result ); + } + + #endregion + + #region GetFileStreamAsync + + /// + /// 测试获取文件流 + /// + [Fact] + public async Task TestGetFileStreamAsync() { + //保存文件 + var path = Common.GetPhysicalPath( "~/Resources/a.png" ); + var fileInfo = new FileInfo( path ); + var result = await _fileStore.SaveFileAsync( fileInfo ); + + //验证 + var exists = await _fileStore.FileExistsAsync( result.FileName ); + Assert.True( exists ); + + //获取文件 + await using var stream = await _fileStore.GetFileStreamAsync( result.FileName ); + var bytes = await File.ToBytesAsync( stream ); + Assert.Equal( result.Size.Size, bytes.Length ); + } + + #endregion + + #region GetFileBytesAsync + + /// + /// 测试获取文件字节流 + /// + [Fact] + public async Task TestGetFileBytesAsync() { + //保存文件 + var path = Common.GetPhysicalPath( "~/Resources/a.png" ); + var fileInfo = new FileInfo( path ); + var result = await _fileStore.SaveFileAsync( fileInfo ); + + //获取文件 + var bytes = await _fileStore.GetFileBytesAsync( result.FileName ); + Assert.Equal( result.Size.Size, bytes.Length ); + } + + #endregion + + #region SaveFileAsync + + /// + /// 测试保存文件 - 使用默认桶 + /// + [Fact] + public async Task TestSaveFileAsync_1() { + var path = Common.GetPhysicalPath( "~/Resources/a.png" ); + await using var stream = File.ReadToStream( path ); + var result = await _fileStore.SaveFileAsync( stream, "b.png" ); + Assert.Equal( "b.png", result.FilePath ); + Assert.Equal( "b.png", result.FileName ); + Assert.Equal( "png", result.Extension ); + Assert.Equal( "5.23 KB", result.Size.ToString() ); + } + + /// + /// 测试保存文件 - 指定存储桶 + /// + [Fact] + public async Task TestSaveFileAsync_2() { + var fileName = "a.png"; + var bucketName = "TestSaveFileAsync_2"; + var path = Common.GetPhysicalPath( "~/Resources/a.png" ); + await using var stream = File.ReadToStream( path ); + await _fileStore.SaveFileAsync( new SaveFileArgs( fileName, stream ) { BucketName = bucketName } ); + var result = await _fileStore.FileExistsAsync( new FileExistsArgs( fileName ) { BucketName = bucketName } ); + Assert.True( result ); + } + + /// + /// 测试保存文件 - 指定文件名处理策略 + /// + [Fact] + public async Task TestSaveFileAsync_3() { + var fileName = "b.png"; + var path = Common.GetPhysicalPath( "~/Resources/a.png" ); + await using var stream = File.ReadToStream( path ); + var result = await _fileStore.SaveFileAsync( stream, fileName, UserTimeFileNameProcessor.Policy ); + Assert.Equal( $"{TestSession.TestUserId}/2012-12-12-12-12-12-123/b.png", result.FilePath ); + Assert.Equal( "b.png", result.FileName ); + Assert.Equal( "png", result.Extension ); + } + + /// + /// 测试保存文件 - 使用FileInfo + /// + [Fact] + public async Task TestSaveFileAsync_4() { + var path = Common.GetPhysicalPath( "~/Resources/a.png" ); + var fileInfo = new FileInfo( path ); + var result = await _fileStore.SaveFileAsync( fileInfo ); + Assert.Equal( "a.png", result.FilePath ); + Assert.Equal( "a.png", result.FileName ); + Assert.Equal( "png", result.Extension ); + } + + /// + /// 测试保存文件 - 使用字节流 + /// + [Fact] + public async Task TestSaveFileAsync_5() { + var path = Common.GetPhysicalPath( "~/Resources/a.png" ); + var stream = Util.Helpers.File.ReadToBytes( path ); + var result = await _fileStore.SaveFileAsync( stream, "b.png" ); + Assert.Equal( "b.png", result.FileName ); + Assert.Equal( "png", result.Extension ); + } + + #endregion + + #region SaveFileByUrlAsync + + /// + /// 测试下载远程文件并保存到文件存储 + /// + [Fact] + public async Task TestSaveFileByUrlAsync_1() { + var result = await _fileStore.SaveFileByUrlAsync( "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png", "baidu.png" ); + var exists = await _fileStore.FileExistsAsync( result.FilePath ); + Assert.True( exists ); + } + + #endregion + + #region CopyFileAsync + + /// + /// 测试复制文件 + /// + [Fact] + public async Task TestCopyFileAsync() { + var sourceName = "e.png"; + var destinationName = "a/b/c/f.png"; + + //保存文件 + var path = Common.GetPhysicalPath( "~/Resources/a.png" ); + await using var stream = File.ReadToStream( path ); + await _fileStore.SaveFileAsync( stream, sourceName ); + + //复制文件 + await _fileStore.CopyFileAsync( sourceName, destinationName ); + + //验证 + var result = await _fileStore.FileExistsAsync( sourceName ); + Assert.True( result ); + result = await _fileStore.FileExistsAsync( destinationName ); + Assert.True( result ); + } + + #endregion + + #region MoveFileAsync + + /// + /// 测试移动文件 + /// + [Fact] + public async Task TestMoveFileAsync() { + var sourceName = "r.png"; + var destinationName = "t/y/z/g.png"; + + //保存文件 + var path = Common.GetPhysicalPath( "~/Resources/a.png" ); + await using var stream = File.ReadToStream( path ); + await _fileStore.SaveFileAsync( stream, sourceName ); + + //移动文件 + await _fileStore.MoveFileAsync( sourceName, destinationName ); + + //验证 + var result = await _fileStore.FileExistsAsync( sourceName ); + Assert.False( result ); + result = await _fileStore.FileExistsAsync( destinationName ); + Assert.True( result ); + } + + #endregion + + #region DeleteFileAsync + + /// + /// 测试删除文件 + /// + [Fact] + public async Task TestDeleteFileAsync() { + var name = "b.png"; + + //保存文件 + var path = Common.GetPhysicalPath( "~/Resources/a.png" ); + await using var stream = File.ReadToStream( path ); + await _fileStore.SaveFileAsync( stream, name ); + + //验证已存在 + var result = await _fileStore.FileExistsAsync( name ); + Assert.True( result ); + + //删除文件 + await _fileStore.DeleteFileAsync( name ); + + //验证已不存在 + result = await _fileStore.FileExistsAsync( name ); + Assert.False( result ); + } + + #endregion + + #region GenerateDownloadUrlAsync + + /// + /// 测试生成下载Url + /// + [Fact] + public async Task TestGenerateDownloadUrlAsync() { + var args = new GenerateDownloadUrlArgs("a.jpg") { + BucketName = "test" + }; + var url = await _fileStore.GenerateDownloadUrlAsync( args ); + _testOutputHelper.WriteLine( url ); + Assert.StartsWith( "https://test.oss-cn-beijing.aliyuncs.com/a.jpg", url ); + } + + #endregion + + #region GenerateUploadUrlAsync + + /// + /// 测试生成上传Url + /// + [Fact] + public async Task TestGenerateUploadUrlAsync() { + //生成上传Url + var fileName = Id.Create(); + var path = Common.GetPhysicalPath( "~/Resources/a.png" ); + var upload = await _fileStore.GenerateUploadUrlAsync( new GenerateUploadUrlArgs( fileName ) { BucketName = "util-test" } ); + _testOutputHelper.WriteLine( Util.Helpers.Json.ToJson( upload ) ); + + //发送请求 + await _client.Post( upload.Url ).FileContent( path ).Content( upload.Data ) + .OnFail( ( m, o ) => { _testOutputHelper.WriteLine( Util.Helpers.Json.ToJson( o ) ); } ) + .GetResultAsync(); + + //验证 + var exists = await _fileStore.FileExistsAsync( new FileExistsArgs( fileName ) {BucketName = "util-test" } ); + Assert.True( exists ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.FileStorage.Aliyun.Tests.Integration/Usings.cs b/test/Util.FileStorage.Aliyun.Tests.Integration/Usings.cs new file mode 100644 index 000000000..6fbf080b2 --- /dev/null +++ b/test/Util.FileStorage.Aliyun.Tests.Integration/Usings.cs @@ -0,0 +1,12 @@ +global using System; +global using System.IO; +global using System.Threading.Tasks; +global using Microsoft.AspNetCore.Builder; +global using Microsoft.AspNetCore.Hosting; +global using Microsoft.AspNetCore.TestHost; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; +global using Xunit; +global using Xunit.DependencyInjection.Logging; +global using Xunit.Abstractions; +global using Util.Sessions; \ No newline at end of file diff --git a/test/Util.FileStorage.Aliyun.Tests.Integration/Util.FileStorage.Aliyun.Tests.Integration.csproj b/test/Util.FileStorage.Aliyun.Tests.Integration/Util.FileStorage.Aliyun.Tests.Integration.csproj new file mode 100644 index 000000000..d8c5622ff --- /dev/null +++ b/test/Util.FileStorage.Aliyun.Tests.Integration/Util.FileStorage.Aliyun.Tests.Integration.csproj @@ -0,0 +1,52 @@ + + + + $(NetTargetFramework) + false + Util.FileStorage.Aliyun + Util.FileStorage.Aliyun.Startup + + + + + + + + + + Always + + + Always + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + Always + + + + diff --git a/test/Util.FileStorage.Aliyun.Tests.Integration/appsettings.Development.json b/test/Util.FileStorage.Aliyun.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..a37de2076 --- /dev/null +++ b/test/Util.FileStorage.Aliyun.Tests.Integration/appsettings.Development.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Information" + } + }, + "AliyunOss": { + "Endpoint": "oss-cn-beijing.aliyuncs.com", + "AccessKeyId": "", + "AccessKeySecret": "" + } +} \ No newline at end of file diff --git a/test/Util.FileStorage.Aliyun.Tests.Integration/appsettings.json b/test/Util.FileStorage.Aliyun.Tests.Integration/appsettings.json new file mode 100644 index 000000000..686f86bab --- /dev/null +++ b/test/Util.FileStorage.Aliyun.Tests.Integration/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + } +} \ No newline at end of file diff --git a/test/Util.FileStorage.Minio.Tests.Integration/Resources/a.jpg b/test/Util.FileStorage.Minio.Tests.Integration/Resources/a.jpg new file mode 100644 index 000000000..0cdfefd95 Binary files /dev/null and b/test/Util.FileStorage.Minio.Tests.Integration/Resources/a.jpg differ diff --git a/test/Util.FileStorage.Minio.Tests.Integration/Resources/b.jpg b/test/Util.FileStorage.Minio.Tests.Integration/Resources/b.jpg new file mode 100644 index 000000000..da6f56d6e Binary files /dev/null and b/test/Util.FileStorage.Minio.Tests.Integration/Resources/b.jpg differ diff --git a/test/Util.FileStorage.Minio.Tests.Integration/Samples/TestSession.cs b/test/Util.FileStorage.Minio.Tests.Integration/Samples/TestSession.cs new file mode 100644 index 000000000..59cd71929 --- /dev/null +++ b/test/Util.FileStorage.Minio.Tests.Integration/Samples/TestSession.cs @@ -0,0 +1,15 @@ +using System; +using Util.Sessions; + +namespace Util.FileStorage.Minio.Samples; + +/// +/// 测试用户会话 +/// +public class TestSession : ISession { + public IServiceProvider ServiceProvider => null; + public static Guid TestUserId = new ( "2af5e99e-391c-451b-9112-f7a3eb9b0a55" ); + public bool IsAuthenticated => true; + public string UserId => TestUserId.ToString(); + public string TenantId => string.Empty; +} \ No newline at end of file diff --git a/test/Util.FileStorage.Minio.Tests.Integration/Startup.cs b/test/Util.FileStorage.Minio.Tests.Integration/Startup.cs new file mode 100644 index 000000000..681af7fa4 --- /dev/null +++ b/test/Util.FileStorage.Minio.Tests.Integration/Startup.cs @@ -0,0 +1,53 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.FileStorage.Minio.Samples; +using Util.Sessions; +using Xunit.DependencyInjection.Logging; + +namespace Util.FileStorage.Minio; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Util.Helpers.Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .ConfigureWebHostDefaults( webHostBuilder => { + webHostBuilder.UseTestServer() + .ConfigureServices( services => { + services.AddControllers(); + services.AddHttpClient(); + } ) + .Configure( t => { + t.UseRouting(); + t.UseEndpoints( endpoints => { + endpoints.MapControllers(); + } ); + } ); + } ) + .AsBuild() + .AddMinio( options => { + options.Endpoint = Util.Helpers.Config.GetValue( "Minio:Endpoint" ); + options.AccessKey = Util.Helpers.Config.GetValue( "Minio:AccessKey" ); + options.SecretKey = Util.Helpers.Config.GetValue( "Minio:SecretKey" ); + options.DefaultBucketName = "Util.FileStorage.Minio.Test"; + options.UseSSL = false; + } ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddSingleton(); + } +} \ No newline at end of file diff --git a/test/Util.FileStorage.Minio.Tests.Integration/Tests/MinioFileStoreTest.cs b/test/Util.FileStorage.Minio.Tests.Integration/Tests/MinioFileStoreTest.cs new file mode 100644 index 000000000..cf898ad57 --- /dev/null +++ b/test/Util.FileStorage.Minio.Tests.Integration/Tests/MinioFileStoreTest.cs @@ -0,0 +1,411 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Util.FileStorage.Minio.Samples; +using Util.Helpers; +using Xunit; +using Xunit.Abstractions; +using File = Util.Helpers.File; + +namespace Util.FileStorage.Minio.Tests; + +/// +/// Minio文件存储服务测试 +/// +public class MinioFileStoreTest : IDisposable { + + #region 测试初始化 + + /// + /// 文件存储服务 + /// + private readonly IFileStore _fileStore; + /// + /// 输出操作 + /// + private readonly ITestOutputHelper _testOutputHelper; + + /// + /// 测试初始化 + /// + public MinioFileStoreTest( IFileStore fileStore, ITestOutputHelper testOutputHelper ) { + _fileStore = fileStore; + _testOutputHelper = testOutputHelper; + Time.SetTime( new DateTime( 2012, 12, 12, 12, 12, 12, 123 ) ); + Id.SetId("id"); + } + + #endregion + + #region 测试清理 + + /// + /// 测试清理 + /// + public void Dispose() { + Time.Reset(); + } + + #endregion + + #region BucketExistsAsync + + /// + /// 测试存储桶是否存在 + /// + [Fact] + public async Task TestBucketExistsAsync() { + var name = $"{Guid.NewGuid()}"; + + //未创建的桶不存在 + var result = await _fileStore.BucketExistsAsync( name ); + Assert.False( result ); + + //创建桶 + await _fileStore.CreateBucketAsync( name ); + + //验证 + result = await _fileStore.BucketExistsAsync( name ); + Assert.True( result ); + } + + #endregion + + #region CreateBucketAsync + + /// + /// 测试创建存储桶 - 修正名称有大写字母和下划线 + /// + [Fact] + public async Task TestCreateBucketAsync_1() { + var name = "TestCreateBucketAsync_1"; + + //创建桶 + await _fileStore.CreateBucketAsync( name ); + + //验证 + var result = await _fileStore.BucketExistsAsync( "testcreatebucketasync-1" ); + Assert.True( result ); + } + + #endregion + + #region DeleteBucketAsync + + /// + /// 测试删除存储桶 + /// + [Fact] + public async Task TestDeleteBucketAsync_1() { + var name = "TestDeleteBucketAsync_1"; + + //创建桶 + await _fileStore.CreateBucketAsync( name ); + + //验证桶已存在 + var result = await _fileStore.BucketExistsAsync( name ); + Assert.True( result ); + + //删除桶 + await _fileStore.DeleteBucketAsync( name ); + + //验证桶已不存在 + result = await _fileStore.BucketExistsAsync( name ); + Assert.False( result ); + } + + #endregion + + #region FileExistsAsync + + /// + /// 测试文件是否存在 - 默认桶 + /// + [Fact] + public async Task TestFileExistsAsync_1() { + //定义文件名 + var name = $"{Guid.NewGuid()}.jpg"; + + //文件未创建不存在 + var result = await _fileStore.FileExistsAsync( name ); + Assert.False( result ); + + //读取文件 + var path = Common.GetPhysicalPath( "~/Resources/b.jpg" ); + await using var stream = File.ReadToStream( path ); + + //创建文件 + await _fileStore.SaveFileAsync( stream, name ); + + //验证 + result = await _fileStore.FileExistsAsync( name ); + Assert.True( result ); + } + + /// + /// 测试文件是否存在 - 指定存储桶 + /// + [Fact] + public async Task TestFileExistsAsync_2() { + //定义变量 + var name = $"{Guid.NewGuid()}.jpg"; + var bucketName = "TestFileExistsAsync_2"; + + //文件未创建不存在 + var result = await _fileStore.FileExistsAsync( new FileExistsArgs( name ) { BucketName = bucketName } ); + Assert.False( result ); + + //读取文件 + var path = Common.GetPhysicalPath( "~/Resources/b.jpg" ); + await using var stream = File.ReadToStream( path ); + + //创建文件 + await _fileStore.SaveFileAsync( new SaveFileArgs( name, stream ) { BucketName = bucketName } ); + + //验证 + result = await _fileStore.FileExistsAsync( new FileExistsArgs( name ) { BucketName = bucketName } ); + Assert.True( result ); + } + + #endregion + + #region GetFileStreamAsync + + /// + /// 测试获取文件流 + /// + [Fact] + public async Task TestGetFileStreamAsync() { + //保存文件 + var path = Common.GetPhysicalPath( "~/Resources/b.jpg" ); + var fileInfo = new FileInfo( path ); + var result = await _fileStore.SaveFileAsync( fileInfo ); + + //验证 + var exists = await _fileStore.FileExistsAsync( result.FileName ); + Assert.True( exists ); + + //获取文件 + await using var stream = await _fileStore.GetFileStreamAsync( result.FileName ); + var bytes = await File.ToBytesAsync( stream ); + Assert.Equal( result.Size.Size, bytes.Length ); + } + + #endregion + + #region GetFileBytesAsync + + /// + /// 测试获取文件字节流 + /// + [Fact] + public async Task TestGetFileBytesAsync() { + //保存文件 + var path = Common.GetPhysicalPath( "~/Resources/b.jpg" ); + var fileInfo = new FileInfo( path ); + var result = await _fileStore.SaveFileAsync( fileInfo ); + + //获取文件 + var bytes = await _fileStore.GetFileBytesAsync( result.FileName ); + Assert.Equal( result.Size.Size, bytes.Length ); + } + + #endregion + + #region SaveFileAsync + + /// + /// 测试保存文件 - 使用默认桶 + /// + [Fact] + public async Task TestSaveFileAsync_1() { + var path = Common.GetPhysicalPath( "~/Resources/b.jpg" ); + await using var stream = File.ReadToStream( path ); + var result = await _fileStore.SaveFileAsync( stream, "b.jpg" ); + Assert.Equal( "b.jpg", result.FilePath ); + Assert.Equal( "b.jpg", result.FileName ); + Assert.Equal( "jpg", result.Extension ); + Assert.Equal( "1.09 MB", result.Size.ToString() ); + } + + /// + /// 测试保存文件 - 指定存储桶 + /// + [Fact] + public async Task TestSaveFileAsync_2() { + var fileName = "b.jpg"; + var bucketName = "TestSaveFileAsync_2"; + var path = Common.GetPhysicalPath( "~/Resources/b.jpg" ); + await using var stream = File.ReadToStream( path ); + await _fileStore.SaveFileAsync( new SaveFileArgs( fileName, stream ) { BucketName = bucketName } ); + var result = await _fileStore.FileExistsAsync( new FileExistsArgs( fileName ) { BucketName = bucketName } ); + Assert.True( result ); + } + + /// + /// 测试保存文件 - 指定文件名处理策略 + /// + [Fact] + public async Task TestSaveFileAsync_3() { + var fileName = "b.jpg"; + var path = Common.GetPhysicalPath( "~/Resources/b.jpg" ); + await using var stream = File.ReadToStream( path ); + var result = await _fileStore.SaveFileAsync( stream, fileName,UserTimeFileNameProcessor.Policy ); + Assert.Equal( $"{TestSession.TestUserId}/2012-12-12/id.jpg", result.FilePath ); + Assert.Equal( "id.jpg", result.FileName ); + Assert.Equal( "jpg", result.Extension ); + } + + /// + /// 测试保存文件 - 使用FileInfo + /// + [Fact] + public async Task TestSaveFileAsync_4() { + var path = Common.GetPhysicalPath( "~/Resources/b.jpg" ); + var fileInfo = new FileInfo( path ); + var result = await _fileStore.SaveFileAsync( fileInfo ); + Assert.Equal( "b.jpg", result.FilePath ); + Assert.Equal( "b.jpg", result.FileName ); + Assert.Equal( "jpg", result.Extension ); + Assert.Equal( "1.09 MB", result.Size.ToString() ); + } + + /// + /// 测试保存文件 - 使用字节流 + /// + [Fact] + public async Task TestSaveFileAsync_5() { + var path = Common.GetPhysicalPath( "~/Resources/b.jpg" ); + var stream = Util.Helpers.File.ReadToBytes( path ); + var result = await _fileStore.SaveFileAsync( stream, "b.jpg" ); + Assert.Equal( "b.jpg", result.FileName ); + Assert.Equal( "jpg", result.Extension ); + Assert.Equal( "1.09 MB", result.Size.ToString() ); + } + + #endregion + + #region SaveFileByUrlAsync + + /// + /// 测试下载远程文件并保存到文件存储 + /// + [Fact] + public async Task TestSaveFileByUrlAsync_1() { + var result = await _fileStore.SaveFileByUrlAsync( "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png", "baidu.png" ); + var exists = await _fileStore.FileExistsAsync( result.FilePath ); + Assert.True( exists ); + } + + #endregion + + #region CopyFileAsync + + /// + /// 测试复制文件 + /// + [Fact] + public async Task TestCopyFileAsync() { + var sourceName = "e.jpg"; + var destinationName = "a/b/c/f.jpg"; + + //保存文件 + var path = Common.GetPhysicalPath( "~/Resources/a.jpg" ); + await using var stream = File.ReadToStream( path ); + await _fileStore.SaveFileAsync( stream, sourceName ); + + //复制文件 + await _fileStore.CopyFileAsync( sourceName, destinationName ); + + //验证 + var result = await _fileStore.FileExistsAsync( sourceName ); + Assert.True( result ); + result = await _fileStore.FileExistsAsync( destinationName ); + Assert.True( result ); + } + + #endregion + + #region MoveFileAsync + + /// + /// 测试移动文件 + /// + [Fact] + public async Task TestMoveFileAsync() { + var sourceName = "r.jpg"; + var destinationName = "t/y/z/g.jpg"; + + //保存文件 + var path = Common.GetPhysicalPath( "~/Resources/a.jpg" ); + await using var stream = File.ReadToStream( path ); + await _fileStore.SaveFileAsync( stream, sourceName ); + + //移动文件 + await _fileStore.MoveFileAsync( sourceName, destinationName ); + + //验证 + var result = await _fileStore.FileExistsAsync( sourceName ); + Assert.False( result ); + result = await _fileStore.FileExistsAsync( destinationName ); + Assert.True( result ); + } + + #endregion + + #region DeleteFileAsync + + /// + /// 测试删除文件 + /// + [Fact] + public async Task TestDeleteFileAsync() { + var name = "b.jpg"; + + //保存文件 + var path = Common.GetPhysicalPath( "~/Resources/b.jpg" ); + await using var stream = File.ReadToStream( path ); + await _fileStore.SaveFileAsync( stream, name ); + + //验证已存在 + var result = await _fileStore.FileExistsAsync( name ); + Assert.True( result ); + + //删除文件 + await _fileStore.DeleteFileAsync( name ); + + //验证已不存在 + result = await _fileStore.FileExistsAsync( name ); + Assert.False( result ); + } + + #endregion + + #region GenerateDownloadUrlAsync + + /// + /// 测试生成下载Url + /// + [Fact] + public async Task TestGenerateDownloadUrlAsync() { + var url = await _fileStore.GenerateDownloadUrlAsync( "a.jpg" ); + _testOutputHelper.WriteLine( url ); + Assert.Equal( "http://127.0.0.1:9000/util-filestorage-minio-test/a.jpg", url ); + } + + #endregion + + #region GenerateUploadUrlAsync + + /// + /// 测试生成上传Url + /// + [Fact] + public async Task TestGenerateUploadUrlAsync() { + var result = await _fileStore.GenerateUploadUrlAsync( "a.jpg" ); + _testOutputHelper.WriteLine( result.Url ); + Assert.StartsWith( "http", result.Url ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.FileStorage.Minio.Tests.Integration/Tests/UserTimeFileNameProcessorTest.cs b/test/Util.FileStorage.Minio.Tests.Integration/Tests/UserTimeFileNameProcessorTest.cs new file mode 100644 index 000000000..e32f016e3 --- /dev/null +++ b/test/Util.FileStorage.Minio.Tests.Integration/Tests/UserTimeFileNameProcessorTest.cs @@ -0,0 +1,37 @@ +using System; +using Util.FileStorage.Minio.Samples; +using Util.Helpers; +using Xunit; + +namespace Util.FileStorage.Minio.Tests; + +/// +/// 基于用户标识和时间的文件名处理器测试 +/// +public class UserTimeFileNameProcessorTest : IDisposable { + /// + /// 测试初始化 + /// + public UserTimeFileNameProcessorTest() { + Time.SetTime( new DateTime( 2012, 12, 12 ) ); + Id.SetId( "id" ); + } + + /// + /// 测试清理 + /// + public void Dispose() { + Time.Reset(); + Id.Reset(); + } + + /// + /// 测试处理文件名 + /// + [Fact] + public void TestProcess_1() { + var processor = new UserTimeFileNameProcessor( new TestSession() ); + var result = processor.Process( "a.jpg" ); + Assert.Equal( $"{TestSession.TestUserId}/2012-12-12/id.jpg", result.Name ); + } +} \ No newline at end of file diff --git a/test/Util.FileStorage.Minio.Tests.Integration/Util.FileStorage.Minio.Tests.Integration.csproj b/test/Util.FileStorage.Minio.Tests.Integration/Util.FileStorage.Minio.Tests.Integration.csproj new file mode 100644 index 000000000..7e09e19a2 --- /dev/null +++ b/test/Util.FileStorage.Minio.Tests.Integration/Util.FileStorage.Minio.Tests.Integration.csproj @@ -0,0 +1,55 @@ + + + + $(NetTargetFramework) + false + Util.FileStorage.Minio + Util.FileStorage.Minio.Startup + + + + + + + + + + Always + + + Always + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + Always + + + Always + + + + diff --git a/test/Util.FileStorage.Minio.Tests.Integration/appsettings.Development.json b/test/Util.FileStorage.Minio.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..23dff96f0 --- /dev/null +++ b/test/Util.FileStorage.Minio.Tests.Integration/appsettings.Development.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Information" + } + }, + "Minio": { + "Endpoint": "127.0.0.1:9000", + "AccessKey": "admin", + "SecretKey": "admin123" + } +} \ No newline at end of file diff --git a/test/Util.FileStorage.Minio.Tests.Integration/appsettings.json b/test/Util.FileStorage.Minio.Tests.Integration/appsettings.json new file mode 100644 index 000000000..686f86bab --- /dev/null +++ b/test/Util.FileStorage.Minio.Tests.Integration/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + } +} \ No newline at end of file diff --git a/test/Util.FileStorage.Tests.Integration/Resources/1.jpg b/test/Util.FileStorage.Tests.Integration/Resources/1.jpg new file mode 100644 index 000000000..3440bb277 Binary files /dev/null and b/test/Util.FileStorage.Tests.Integration/Resources/1.jpg differ diff --git a/test/Util.FileStorage.Tests.Integration/Resources/2.png b/test/Util.FileStorage.Tests.Integration/Resources/2.png new file mode 100644 index 000000000..ace11c472 Binary files /dev/null and b/test/Util.FileStorage.Tests.Integration/Resources/2.png differ diff --git a/test/Util.FileStorage.Tests.Integration/Resources/3.gif b/test/Util.FileStorage.Tests.Integration/Resources/3.gif new file mode 100644 index 000000000..642e4b9d7 Binary files /dev/null and b/test/Util.FileStorage.Tests.Integration/Resources/3.gif differ diff --git a/test/Util.FileStorage.Tests.Integration/Resources/4.pdf b/test/Util.FileStorage.Tests.Integration/Resources/4.pdf new file mode 100644 index 000000000..657c75b13 Binary files /dev/null and b/test/Util.FileStorage.Tests.Integration/Resources/4.pdf differ diff --git a/test/Util.FileStorage.Tests.Integration/Resources/5.docx b/test/Util.FileStorage.Tests.Integration/Resources/5.docx new file mode 100644 index 000000000..5ae14d7f8 Binary files /dev/null and b/test/Util.FileStorage.Tests.Integration/Resources/5.docx differ diff --git a/test/Util.FileStorage.Tests.Integration/Resources/6.xlsx b/test/Util.FileStorage.Tests.Integration/Resources/6.xlsx new file mode 100644 index 000000000..4f2ad85e2 Binary files /dev/null and b/test/Util.FileStorage.Tests.Integration/Resources/6.xlsx differ diff --git a/test/Util.FileStorage.Tests.Integration/Resources/a.jpg b/test/Util.FileStorage.Tests.Integration/Resources/a.jpg new file mode 100644 index 000000000..3440bb277 Binary files /dev/null and b/test/Util.FileStorage.Tests.Integration/Resources/a.jpg differ diff --git a/test/Util.FileStorage.Tests.Integration/Resources/a.pdf b/test/Util.FileStorage.Tests.Integration/Resources/a.pdf new file mode 100644 index 000000000..3440bb277 Binary files /dev/null and b/test/Util.FileStorage.Tests.Integration/Resources/a.pdf differ diff --git a/test/Util.FileStorage.Tests.Integration/Resources/b.png b/test/Util.FileStorage.Tests.Integration/Resources/b.png new file mode 100644 index 000000000..ace11c472 Binary files /dev/null and b/test/Util.FileStorage.Tests.Integration/Resources/b.png differ diff --git a/test/Util.FileStorage.Tests.Integration/Resources/c.gif b/test/Util.FileStorage.Tests.Integration/Resources/c.gif new file mode 100644 index 000000000..642e4b9d7 Binary files /dev/null and b/test/Util.FileStorage.Tests.Integration/Resources/c.gif differ diff --git a/test/Util.FileStorage.Tests.Integration/Samples/TestFileNameProcessor.cs b/test/Util.FileStorage.Tests.Integration/Samples/TestFileNameProcessor.cs new file mode 100644 index 000000000..8008662fa --- /dev/null +++ b/test/Util.FileStorage.Tests.Integration/Samples/TestFileNameProcessor.cs @@ -0,0 +1,13 @@ +namespace Util.FileStorage.Samples; + +/// +/// 测试文件名处理器 +/// +public class TestFileNameProcessor : IFileNameProcessor { + /// + public ProcessedName Process( string fileName ) { + if( fileName == "2.png" ) + return new ProcessedName( "a.png", "2.png" ); + return new ProcessedName( fileName, fileName ); + } +} \ No newline at end of file diff --git a/test/Util.FileStorage.Tests.Integration/Samples/TestFileNameProcessorFactory.cs b/test/Util.FileStorage.Tests.Integration/Samples/TestFileNameProcessorFactory.cs new file mode 100644 index 000000000..64dcd1999 --- /dev/null +++ b/test/Util.FileStorage.Tests.Integration/Samples/TestFileNameProcessorFactory.cs @@ -0,0 +1,19 @@ +using System; +using Util.Dependency; + +namespace Util.FileStorage.Samples; + +/// +/// 文件名处理器测试工厂 +/// +[Ioc(9)] +public class TestFileNameProcessorFactory : IFileNameProcessorFactory { + /// + public IFileNameProcessor CreateProcessor( string policy ) { + if ( policy.IsEmpty() ) + return new FileNameProcessor(); + if ( policy.ToLowerInvariant() == "test" ) + return new TestFileNameProcessor(); + throw new NotImplementedException( $"文件名处理策略 {policy} 未实现." ); + } +} \ No newline at end of file diff --git a/test/Util.FileStorage.Tests.Integration/Startup.cs b/test/Util.FileStorage.Tests.Integration/Startup.cs new file mode 100644 index 000000000..95c2bb67f --- /dev/null +++ b/test/Util.FileStorage.Tests.Integration/Startup.cs @@ -0,0 +1,28 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit.DependencyInjection.Logging; + +namespace Util.FileStorage; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Util.Helpers.Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .AsBuild() + .AddLocalFileStore() + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + } +} \ No newline at end of file diff --git a/test/Util.FileStorage.Tests.Integration/Tests/FileExtensionInspectorTest.cs b/test/Util.FileStorage.Tests.Integration/Tests/FileExtensionInspectorTest.cs new file mode 100644 index 000000000..5d6930b00 --- /dev/null +++ b/test/Util.FileStorage.Tests.Integration/Tests/FileExtensionInspectorTest.cs @@ -0,0 +1,60 @@ +namespace Util.FileStorage.Tests; + +/// +/// 文件扩展名检查器测试 +/// +public class FileExtensionInspectorTest { + /// + /// 文件扩展名检查器 + /// + private readonly IFileExtensionInspector _inspector; + + /// + /// 测试初始化 + /// + public FileExtensionInspectorTest( IFileExtensionInspector inspector ) { + _inspector = inspector; + } + + /// + /// 测试是否图片文件 + /// + [Fact] + public void TestIsImage() { + var filePath = Util.Helpers.Common.GetPhysicalPath( "Resources/a.jpg" ); + using var stream = Util.Helpers.File.ReadToStream( filePath ); + Assert.True( _inspector.IsImage( stream ) ); + + filePath = Util.Helpers.Common.GetPhysicalPath( "Resources/b.png" ); + using var stream2 = Util.Helpers.File.ReadToStream( filePath ); + Assert.True( _inspector.IsImage( stream2 ) ); + + filePath = Util.Helpers.Common.GetPhysicalPath( "Resources/c.gif" ); + using var stream3 = Util.Helpers.File.ReadToStream( filePath ); + Assert.True( _inspector.IsImage( stream3 ) ); + } + + /// + /// 测试是否pdf文件 + /// + [Fact] + public void TestIsPdf() { + var filePath = Util.Helpers.Common.GetPhysicalPath( "Resources/4.pdf" ); + using var stream = Util.Helpers.File.ReadToStream( filePath ); + Assert.True( _inspector.IsPdf( stream ) ); + } + + /// + /// 测试是否Office文件 + /// + [Fact] + public void TestIsOffice() { + var filePath = Util.Helpers.Common.GetPhysicalPath( "Resources/5.docx" ); + using var stream = Util.Helpers.File.ReadToStream( filePath ); + Assert.True( _inspector.IsOffice( stream ) ); + + filePath = Util.Helpers.Common.GetPhysicalPath( "Resources/6.xlsx" ); + using var stream2 = Util.Helpers.File.ReadToStream( filePath ); + Assert.True( _inspector.IsOffice( stream2 ) ); + } +} \ No newline at end of file diff --git a/test/Util.FileStorage.Tests.Integration/Tests/FileResultTest.cs b/test/Util.FileStorage.Tests.Integration/Tests/FileResultTest.cs new file mode 100644 index 000000000..b78047d71 --- /dev/null +++ b/test/Util.FileStorage.Tests.Integration/Tests/FileResultTest.cs @@ -0,0 +1,41 @@ +namespace Util.FileStorage.Tests; + +/// +/// 文件处理结果测试 +/// +public class FileResultTest { + /// + /// 测试文件处理结果 + /// + [Fact] + public void Test_1() { + var fileInfo = new FileResult( "a.jpg", 1024 ); + Assert.Equal( "a.jpg", fileInfo.FilePath ); + Assert.Equal( "a.jpg", fileInfo.FileName ); + Assert.Equal( "jpg", fileInfo.Extension ); + Assert.Equal( 1024, fileInfo.Size.GetSize() ); + Assert.Equal( "1 KB", fileInfo.Size.ToString() ); + } + + /// + /// 测试文件处理结果 - 不带扩展名 + /// + [Fact] + public void Test_2() { + var fileInfo = new FileResult( "a", 1024 ); + Assert.Equal( "a", fileInfo.FilePath ); + Assert.Equal( "a", fileInfo.FileName ); + Assert.True( fileInfo.Extension.IsEmpty() ); + } + + /// + /// 测试文件处理结果 - 文件名带路径 + /// + [Fact] + public void Test_3() { + var fileInfo = new FileResult( "a/b.jpg", 1024 ); + Assert.Equal( "a/b.jpg", fileInfo.FilePath ); + Assert.Equal( "b.jpg", fileInfo.FileName ); + Assert.Equal( "jpg", fileInfo.Extension ); + } +} \ No newline at end of file diff --git a/test/Util.FileStorage.Tests.Integration/Tests/FileSizeTest.cs b/test/Util.FileStorage.Tests.Integration/Tests/FileSizeTest.cs new file mode 100644 index 000000000..aeb6009c9 --- /dev/null +++ b/test/Util.FileStorage.Tests.Integration/Tests/FileSizeTest.cs @@ -0,0 +1,59 @@ +namespace Util.FileStorage.Tests; + +/// +/// 测试文件大小 +/// +public class FileSizeTest { + /// + /// 文件字节数 + /// + [Theory] + [InlineData( FileSizeUnit.Byte, 1 )] + [InlineData( FileSizeUnit.K, 1024 )] + [InlineData( FileSizeUnit.M, 1024 * 1024 )] + [InlineData( FileSizeUnit.G, 1024 * 1024 * 1024 )] + public void TestSize( FileSizeUnit unit, long result ) { + Assert.Equal( result, new FileSize( 1, unit ).Size ); + } + + /// + /// 测试文件大小描述 + /// + [Fact] + public void TestToString() { + Assert.Equal( "1 B", new FileSize( 1 ).ToString() ); + Assert.Equal( "1 KB", new FileSize( 1 * 1024 ).ToString() ); + Assert.Equal( "1 MB", new FileSize( 1 * 1024 * 1024 ).ToString() ); + Assert.Equal( "1 GB", new FileSize( 1 * 1024 * 1024 * 1024 ).ToString() ); + } + + /// + /// 获取文件大小,单位:K + /// + [Fact] + public void TestGetSizeByK() { + Assert.Equal( 0, new FileSize( 0 ).GetSizeByK() ); + Assert.Equal( 1, new FileSize( 1024 ).GetSizeByK() ); + Assert.Equal( 0.5, new FileSize( 512 ).GetSizeByK() ); + } + + /// + /// 获取文件大小,单位:M + /// + [Fact] + public void TestGetSizeByM() { + Assert.Equal( 0, new FileSize( 0 ).GetSizeByM() ); + Assert.Equal( 1, new FileSize( 1024 * 1024 ).GetSizeByM() ); + Assert.Equal( 0.5, new FileSize( 512 * 1024 ).GetSizeByM() ); + } + + /// + /// 获取文件大小,单位:G + /// + [Fact] + public void TestGetSizeByG() { + Assert.Equal( 0, new FileSize( 0 ).GetSizeByG() ); + Assert.Equal( 1, new FileSize( 1024 * 1024 * 1024 ).GetSizeByG() ); + Assert.Equal( 0.5, new FileSize( 512 * 1024 * 1024 ).GetSizeByG() ); + } +} \ No newline at end of file diff --git a/test/Util.FileStorage.Tests.Integration/Tests/FileValidationTest.cs b/test/Util.FileStorage.Tests.Integration/Tests/FileValidationTest.cs new file mode 100644 index 000000000..c3d774b9c --- /dev/null +++ b/test/Util.FileStorage.Tests.Integration/Tests/FileValidationTest.cs @@ -0,0 +1,28 @@ +namespace Util.FileStorage.Tests; + +/// +/// 文件验证操作测试 +/// +public class FileValidationTest { + /// + /// 测试扩展名是否有效 + /// + [Fact] + public void TestIsValidExtension() { + Assert.True( FileValidation.IsValidExtension( "a.jpg", "" ) ); + Assert.False( FileValidation.IsValidExtension( "", ".jpg" ) ); + Assert.True( FileValidation.IsValidExtension( "a.jpg", ".jpg" ) ); + Assert.True( FileValidation.IsValidExtension( "a.jpg", "jpg" ) ); + Assert.False( FileValidation.IsValidExtension( "a.png", ".jpg" ) ); + Assert.True( FileValidation.IsValidExtension( "a.jpg", ".jpg,.png" ) ); + Assert.True( FileValidation.IsValidExtension( "a.png", ".jpg,.png" ) ); + Assert.True( FileValidation.IsValidExtension( "a.jpg", "jpg,png" ) ); + Assert.True( FileValidation.IsValidExtension( "a.png", "jpg,png" ) ); + Assert.True( FileValidation.IsValidExtension( ".jpg", ".jpg,png" ) ); + Assert.True( FileValidation.IsValidExtension( ".png", "jpg,.png" ) ); + Assert.True( FileValidation.IsValidExtension( "a.png", "," ) ); + Assert.False( FileValidation.IsValidExtension( "a.png", ",.jpg" ) ); + Assert.False( FileValidation.IsValidExtension( "a", ",.jpg" ) ); + Assert.True( FileValidation.IsValidExtension( "a", ",." ) ); + } +} \ No newline at end of file diff --git a/test/Util.FileStorage.Tests.Integration/Tests/LocalFileStoreTest.cs b/test/Util.FileStorage.Tests.Integration/Tests/LocalFileStoreTest.cs new file mode 100644 index 000000000..a8a17350a --- /dev/null +++ b/test/Util.FileStorage.Tests.Integration/Tests/LocalFileStoreTest.cs @@ -0,0 +1,152 @@ +using Util.Helpers; + +namespace Util.FileStorage.Tests; + +/// +/// 本地文件存储服务测试 +/// +public class LocalFileStoreTest { + /// + /// 本地文件存储服务 + /// + private readonly ILocalFileStore _fileStore; + + /// + /// 测试初始化 + /// + public LocalFileStoreTest( ILocalFileStore fileStore ) { + _fileStore = fileStore; + } + + /// + /// 测试保存文件 + /// + [Fact] + public async Task TestSaveFileAsync_1() { + //保存文件 + var path = Common.GetPhysicalPath( "~/Resources/1.jpg" ); + await using var stream = File.ReadToStream( path ); + var result = await _fileStore.SaveFileAsync( stream, "1.jpg" ); + Assert.Equal( "upload\\1.jpg", result.FilePath ); + Assert.Equal( "1.jpg", result.FileName ); + Assert.Equal( "jpg", result.Extension ); + + //验证上传文件是否存在 + var exists = await _fileStore.FileExistsAsync( "1.jpg" ); + Assert.True( exists ); + } + + /// + /// 测试保存文件 - 自定义文件名策略 + /// + [Fact] + public async Task TestSaveFileAsync_2() { + var path = Common.GetPhysicalPath( "~/Resources/2.png" ); + await using var stream = File.ReadToStream( path ); + var result = await _fileStore.SaveFileAsync( stream, "2.png","test" ); + Assert.Equal( "upload\\a.png", result.FilePath ); + Assert.Equal( "a.png", result.FileName ); + Assert.Equal( "2.png", result.OriginalFileName ); + Assert.Equal( "png", result.Extension ); + } + + /// + /// 测试获取文件流 + /// + [Fact] + public async Task TestGetFileStreamAsync() { + //保存文件 + var path = Common.GetPhysicalPath( "~/Resources/1.jpg" ); + var fileInfo = new System.IO.FileInfo( path ); + var result = await _fileStore.SaveFileAsync( fileInfo ); + + //验证 + var exists = await _fileStore.FileExistsAsync( result.FileName ); + Assert.True( exists ); + + //获取文件 + await using var stream = await _fileStore.GetFileStreamAsync( result.FileName ); + var bytes = await File.ToBytesAsync( stream ); + Assert.Equal( result.Size.Size, bytes.Length ); + } + + /// + /// 测试下载远程文件并保存到文件存储 + /// + [Fact] + public async Task TestSaveFileByUrlAsync_1() { + var result = await _fileStore.SaveFileByUrlAsync( "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png", "baidu.png" ); + var exists = await _fileStore.FileExistsAsync( result.FileName ); + Assert.True( exists ); + } + + /// + /// 测试复制文件 + /// + [Fact] + public async Task TestCopyFileAsync() { + var sourceName = "e.jpg"; + var destinationName = "a/b/c/f.jpg"; + + //保存文件 + var path = Common.GetPhysicalPath( "~/Resources/1.jpg" ); + await using var stream = File.ReadToStream( path ); + await _fileStore.SaveFileAsync( stream, sourceName ); + + //复制文件 + await _fileStore.CopyFileAsync( sourceName, destinationName ); + + //验证 + var result = await _fileStore.FileExistsAsync( sourceName ); + Assert.True( result ); + result = await _fileStore.FileExistsAsync( destinationName ); + Assert.True( result ); + } + + /// + /// 测试移动文件 + /// + [Fact] + public async Task TestMoveFileAsync() { + var sourceName = "e.jpg"; + var destinationName = "a/b/c/g.jpg"; + + //保存文件 + var path = Common.GetPhysicalPath( "~/Resources/1.jpg" ); + await using var stream = File.ReadToStream( path ); + await _fileStore.SaveFileAsync( stream, sourceName ); + + //复制文件 + await _fileStore.MoveFileAsync( sourceName, destinationName ); + + //验证 + var result = await _fileStore.FileExistsAsync( sourceName ); + Assert.False( result ); + result = await _fileStore.FileExistsAsync( destinationName ); + Assert.True( result ); + } + + /// + /// 测试删除文件 + /// + [Fact] + public async Task TestDeleteFileAsync() { + var name = "b.jpg"; + + //保存文件 + var path = Common.GetPhysicalPath( "~/Resources/1.jpg" ); + await using var stream = File.ReadToStream( path ); + await _fileStore.SaveFileAsync( stream, name ); + + //验证已存在 + var result = await _fileStore.FileExistsAsync( name ); + Assert.True( result ); + + //删除文件 + await _fileStore.DeleteFileAsync( name ); + + //验证已不存在 + result = await _fileStore.FileExistsAsync( name ); + Assert.False( result ); + } +} \ No newline at end of file diff --git a/test/Util.FileStorage.Tests.Integration/Usings.cs b/test/Util.FileStorage.Tests.Integration/Usings.cs new file mode 100644 index 000000000..662ef7ee8 --- /dev/null +++ b/test/Util.FileStorage.Tests.Integration/Usings.cs @@ -0,0 +1,3 @@ +global using Xunit; +global using System.Threading.Tasks; +global using Util.FileStorage.Local; \ No newline at end of file diff --git a/test/Util.FileStorage.Tests.Integration/Util.FileStorage.Tests.Integration.csproj b/test/Util.FileStorage.Tests.Integration/Util.FileStorage.Tests.Integration.csproj new file mode 100644 index 000000000..13490d280 --- /dev/null +++ b/test/Util.FileStorage.Tests.Integration/Util.FileStorage.Tests.Integration.csproj @@ -0,0 +1,79 @@ + + + + $(NetTargetFramework) + false + Util.FileStorage + Util.FileStorage.Startup + + + + + + + + + + Always + + + Always + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + + diff --git a/test/Util.FileStorage.Tests.Integration/appsettings.Development.json b/test/Util.FileStorage.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..23dff96f0 --- /dev/null +++ b/test/Util.FileStorage.Tests.Integration/appsettings.Development.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Information" + } + }, + "Minio": { + "Endpoint": "127.0.0.1:9000", + "AccessKey": "admin", + "SecretKey": "admin123" + } +} \ No newline at end of file diff --git a/test/Util.FileStorage.Tests.Integration/appsettings.json b/test/Util.FileStorage.Tests.Integration/appsettings.json new file mode 100644 index 000000000..686f86bab --- /dev/null +++ b/test/Util.FileStorage.Tests.Integration/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + } +} \ No newline at end of file diff --git a/test/Util.Generators.Razor.Tests.Integration/RazorTemplateFinderTest.cs b/test/Util.Generators.Razor.Tests.Integration/RazorTemplateFinderTest.cs new file mode 100644 index 000000000..b1f38e385 --- /dev/null +++ b/test/Util.Generators.Razor.Tests.Integration/RazorTemplateFinderTest.cs @@ -0,0 +1,32 @@ +using System.Linq; +using Util.Generators.Contexts; +using Util.Generators.Templates; +using Xunit; + +namespace Util.Generators.Razor.Tests.Integration { + /// + /// Razor模板查找器测试 + /// + public class RazorTemplateFinderTest { + /// + /// Razor模板查找器 + /// + private readonly ITemplateFinder _finder; + + /// + /// 测试初始化 + /// + public RazorTemplateFinderTest( ITemplateFinder finder ) { + _finder = finder; + } + + /// + /// 测试查找模板列表 + /// + [Fact] + public void TestFind() { + var result = _finder.Find( "Templates",new ProjectContext( new GeneratorContext() ) ).ToList(); + Assert.Equal( 3, result.Count ); + } + } +} diff --git a/test/Util.Generators.Razor.Tests.Integration/RazorTemplateTest.cs b/test/Util.Generators.Razor.Tests.Integration/RazorTemplateTest.cs new file mode 100644 index 000000000..ef35c1878 --- /dev/null +++ b/test/Util.Generators.Razor.Tests.Integration/RazorTemplateTest.cs @@ -0,0 +1,43 @@ +using System.IO; +using System.Threading.Tasks; +using Util.Generators.Contexts; +using Util.Helpers; +using Util.Templates; +using Xunit; + +namespace Util.Generators.Razor.Tests.Integration { + /// + /// Razor模板测试 + /// + public class RazorTemplateTest { + /// + /// 模板引擎 + /// + private readonly ITemplateEngine _templateEngine; + + /// + /// 测试初始化 + /// + public RazorTemplateTest( ITemplateEngine templateEngine ) { + _templateEngine = templateEngine; + } + + /// + /// 测试渲染模板 + /// + [Fact] + public async Task TestRender() { + var path = Common.GetPhysicalPath( "Templates/Test1/Template.cshtml" ); + var template = new RazorTemplate( _templateEngine, new FileInfo( path ) ); + var generatorContext = new GeneratorContext { + TemplateRootPath = Common.GetPhysicalPath( "~/Templates" ), + OutputRootPath = Common.GetPhysicalPath( "~/Output" ) + }; + var projectContext = new ProjectContext( generatorContext ) { Name = "test" }; + var entityContext = new EntityContext( projectContext, "Hello" ); + var result = await template.RenderAsync( entityContext ); + Assert.Equal( "Hello,World", result ); + Assert.Equal( "Test1", entityContext.Output.RelativeRootPath ); + } + } +} diff --git a/test/Util.Generators.Razor.Tests.Integration/Startup.cs b/test/Util.Generators.Razor.Tests.Integration/Startup.cs new file mode 100644 index 000000000..6aa45fbce --- /dev/null +++ b/test/Util.Generators.Razor.Tests.Integration/Startup.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Helpers; +using Util.Logging.Serilog; +using Xunit.DependencyInjection.Logging; + +namespace Util.Generators.Razor.Tests.Integration { + /// + /// + /// + public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .AsBuild() + .AddSerilog() + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + } + } +} diff --git a/test/Util.Generators.Razor.Tests.Integration/Templates/Test1/Parts/Test.Part.cshtml b/test/Util.Generators.Razor.Tests.Integration/Templates/Test1/Parts/Test.Part.cshtml new file mode 100644 index 000000000..9e99cdea0 --- /dev/null +++ b/test/Util.Generators.Razor.Tests.Integration/Templates/Test1/Parts/Test.Part.cshtml @@ -0,0 +1,3 @@ +@model Util.Generators.Contexts.EntityContext + +@Model.Name,Test.Part \ No newline at end of file diff --git a/test/Util.Generators.Razor.Tests.Integration/Templates/Test1/Template.cshtml b/test/Util.Generators.Razor.Tests.Integration/Templates/Test1/Template.cshtml new file mode 100644 index 000000000..0d857ef7e --- /dev/null +++ b/test/Util.Generators.Razor.Tests.Integration/Templates/Test1/Template.cshtml @@ -0,0 +1,5 @@ +@model Util.Generators.Contexts.EntityContext +@{ + Model.Output.Extension = ".cs"; +} +@Model.Name,World \ No newline at end of file diff --git a/test/Util.Generators.Razor.Tests.Integration/Templates/Test1/Test2/Test.cshtml b/test/Util.Generators.Razor.Tests.Integration/Templates/Test1/Test2/Test.cshtml new file mode 100644 index 000000000..79f0e63e6 --- /dev/null +++ b/test/Util.Generators.Razor.Tests.Integration/Templates/Test1/Test2/Test.cshtml @@ -0,0 +1,9 @@ +@using Util.Generators +@model Util.Generators.Contexts.EntityContext + +@{ + Model.Output.Extension = ".ts"; + Model.Output.NamingConvention = NamingConvention.CamelCase; +} + +@Model.Name,Test2 \ No newline at end of file diff --git a/test/Util.Generators.Razor.Tests.Integration/Templates/Test1/Test2/TestPart.cshtml b/test/Util.Generators.Razor.Tests.Integration/Templates/Test1/Test2/TestPart.cshtml new file mode 100644 index 000000000..31627b371 --- /dev/null +++ b/test/Util.Generators.Razor.Tests.Integration/Templates/Test1/Test2/TestPart.cshtml @@ -0,0 +1,3 @@ +@model Util.Generators.Contexts.EntityContext + +@Model.Name,TestPart \ No newline at end of file diff --git a/test/Util.Generators.Razor.Tests.Integration/Util.Generators.Razor.Tests.Integration.csproj b/test/Util.Generators.Razor.Tests.Integration/Util.Generators.Razor.Tests.Integration.csproj new file mode 100644 index 000000000..9e488fe04 --- /dev/null +++ b/test/Util.Generators.Razor.Tests.Integration/Util.Generators.Razor.Tests.Integration.csproj @@ -0,0 +1,51 @@ + + + + $(NetTargetFramework) + false + + + + + true + PreserveNewest + Always + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + Always + + + Always + + + Always + + + Always + + + + diff --git a/test/Util.Generators.Razor.Tests.Integration/appsettings.json b/test/Util.Generators.Razor.Tests.Integration/appsettings.json new file mode 100644 index 000000000..243202278 --- /dev/null +++ b/test/Util.Generators.Razor.Tests.Integration/appsettings.json @@ -0,0 +1,18 @@ +{ + "Generator": { + "TemplatePath": "Templates", + "OutputPath": "d:\\Output", + "Projects": { + "Test": { + "Name": "Project1", + "DbType": "SqlServer", + "ConnectionString": "Server=.;Database=UtilTest;uid=sa;pwd=Pass@word;MultipleActiveResultSets=true" + }, + "Test2": { + "Name": "Project2", + "DbType": "SqlServer", + "ConnectionString": "Server=.;Database=UtilTest;uid=sa;pwd=Pass@word;MultipleActiveResultSets=true" + } + } + } +} diff --git a/test/Util.Generators.Tests/Configuration/GeneratorOptionsBuilderTest.cs b/test/Util.Generators.Tests/Configuration/GeneratorOptionsBuilderTest.cs new file mode 100644 index 000000000..cd0d760d2 --- /dev/null +++ b/test/Util.Generators.Tests/Configuration/GeneratorOptionsBuilderTest.cs @@ -0,0 +1,87 @@ +using Util.Data; +using Util.Generators.Configuration; +using Xunit; + +namespace Util.Generators.Tests.Configuration { + /// + /// 生成器配置项构建器测试 + /// + public class GeneratorOptionsBuilderTest { + /// + /// 生成器配置项 + /// + private readonly GeneratorOptions _options; + + /// + /// 测试初始化 + /// + public GeneratorOptionsBuilderTest() { + var builder = new GeneratorOptionsBuilder(); + _options = builder.BuildAsync().Result; + } + + /// + /// 测试基础配置 + /// + [Fact] + public void TestBaseInfo() { + Assert.Equal( "TemplatePath", _options.TemplatePath ); + Assert.Equal( "OutputPath", _options.OutputPath ); + } + + /// + /// 测试项目配置1 + /// + [Fact] + public void TestProjects_1() { + var project = _options.Projects["Test"]; + Assert.Equal( "Test", project.Name ); + Assert.Equal( DatabaseType.PgSql, project.DbType ); + Assert.Null( project.TargetDbType ); + Assert.Equal( "ConnectionString_Test", project.ConnectionString ); + Assert.Equal( "UnitOfWorkName_Test", project.UnitOfWorkName ); + Assert.Equal( ProjectType.WebApi, project.ProjectType ); + Assert.Equal( "123", project.ApiPort ); + Assert.Equal( "666", project.UiPort ); + Assert.Equal( "ClientAppName_Test", project.Client.AppName ); + Assert.Equal( "1", project.Client.Port ); + Assert.True( project.Enabled ); + Assert.True( project.Utc ); + Assert.True( project.I18n ); + Assert.True( project.EnableSchema ); + Assert.Equal( "1", project.Extend ); + } + + /// + /// 测试项目配置2 + /// + [Fact] + public void TestProjects_2() { + var project = _options.Projects["Test2"]; + Assert.Equal( "Test2", project.Name ); + Assert.Equal( DatabaseType.MySql, project.DbType ); + Assert.Equal( DatabaseType.SqlServer, project.TargetDbType ); + Assert.Equal( "ConnectionString_Test2", project.ConnectionString ); + Assert.Equal( "UnitOfWorkName_Test2", project.UnitOfWorkName ); + Assert.Equal( ProjectType.Ui, project.ProjectType ); + Assert.Equal( "456", project.ApiPort ); + Assert.Equal( "888", project.UiPort ); + Assert.Equal( "ClientAppName_Test2", project.Client.AppName ); + Assert.Equal( "2", project.Client.Port ); + Assert.False( project.Enabled ); + Assert.False( project.Utc ); + Assert.False( project.I18n ); + Assert.False( project.EnableSchema ); + Assert.Equal( "2", project.Extend ); + } + + /// + /// 测试消息配置 + /// + [Fact] + public void TestMessages() { + Assert.Equal( "RequiredMessage", _options.Messages.RequiredMessage ); + Assert.Equal( "MaxLengthMessage", _options.Messages.MaxLengthMessage ); + } + } +} diff --git a/test/Util.Generators.Tests/Contexts/EntityContextTest.cs b/test/Util.Generators.Tests/Contexts/EntityContextTest.cs new file mode 100644 index 000000000..1bf1615df --- /dev/null +++ b/test/Util.Generators.Tests/Contexts/EntityContextTest.cs @@ -0,0 +1,42 @@ +using Util.Generators.Contexts; +using Xunit; + +namespace Util.Generators.Tests.Contexts { + /// + /// 实体上下文测试 + /// + public class EntityContextTest { + /// + /// 测试复制 + /// + [Fact] + public void TestClone() { + //创建实体上下文 + var generatorContext = new GeneratorContext { + TemplateRootPath = "Templates", + OutputRootPath = "Output" + }; + var projectContext = new ProjectContext( generatorContext ) { Name = "Project" }; + var context = new EntityContext( projectContext, "a" ) { + Schema = "TestSchema", + Description = "TestDescription", + Output = { + Extension = ".txt" + } + }; + + //复制副本 + var clone = context.Clone( projectContext ); + + //验证实体上下文 + Assert.NotSame( context, clone ); + Assert.Equal( context.Name, clone.Name ); + Assert.Equal( context.Schema, clone.Schema ); + Assert.Equal( context.Description, clone.Description ); + + //验证输出 + Assert.Equal( @"Output/Project", clone.Output.RootPath.Replace( "\\","/" ) ); + Assert.Equal( ".txt", clone.Output.Extension ); + } + } +} diff --git a/test/Util.Generators.Tests/Contexts/GeneratorContextBuilderTest.cs b/test/Util.Generators.Tests/Contexts/GeneratorContextBuilderTest.cs new file mode 100644 index 000000000..f43cc678b --- /dev/null +++ b/test/Util.Generators.Tests/Contexts/GeneratorContextBuilderTest.cs @@ -0,0 +1,430 @@ +using System; +using System.Data; +using System.Threading.Tasks; +using Util.Data; +using Util.Data.Dapper.Metadata; +using Util.Generators.Configuration; +using Util.Generators.Contexts; +using Util.Generators.Logs; +using Util.Generators.Tests.Mocks; +using Xunit; + +namespace Util.Generators.Tests.Contexts { + /// + /// 生成器上下文构建器测试 + /// + public class GeneratorContextBuilderTest { + /// + /// 生成器上下文 + /// + private GeneratorContext _context; + + /// + /// 测试初始化 + /// + public GeneratorContextBuilderTest() { + _context = new GeneratorContextBuilder( NullGeneratorLogger.Instance, new MockGeneratorOptionsBuilder(), new MockMetadataServiceFactory(), new TypeConverterFactory() ).BuildAsync().Result; + } + + /// + /// 测试生成器上下文 - 相对路径 + /// + [Fact] + public void TestGeneratorContext_Path_1() { + Assert.Equal( $"{AppContext.BaseDirectory}Templates", _context.TemplateRootPath ); + Assert.Equal( $"{AppContext.BaseDirectory}Output", _context.OutputRootPath ); + } + + /// + /// 测试生成器上下文 - 绝对路径 + /// + [Fact] + public async Task TestGeneratorContext_Path_2() { + _context = await new GeneratorContextBuilder( NullGeneratorLogger.Instance, new MockGeneratorOptionsBuilder2(), new MockMetadataServiceFactory(), new TypeConverterFactory() ).BuildAsync(); + Assert.Equal( @"c:\Templates", _context.TemplateRootPath ); + Assert.Equal( @"d:\Output", _context.OutputRootPath ); + } + + /// + /// 测试项目名称 + /// + [Fact] + public void TestProjectContext_Name() { + var projectContext = _context.Projects[0]; + Assert.Equal( "Test", projectContext.Name ); + var projectContext2 = _context.Projects[1]; + Assert.Equal( "Test2", projectContext2.Name ); + } + + /// + /// 测试项目工作单元名称 + /// + [Fact] + public void TestProjectContext_UnitOfWorkName() { + var projectContext = _context.Projects[0]; + Assert.Equal( "UnitOfWork", projectContext.UnitOfWorkName ); + var projectContext2 = _context.Projects[1]; + Assert.Equal( "UnitOfWork2", projectContext2.UnitOfWorkName ); + } + + /// + /// 测试项目客户端 + /// + [Fact] + public void TestProjectContext_Client() { + var projectContext = _context.Projects[0]; + Assert.Equal( "ClientApp", projectContext.Client.AppName ); + Assert.Equal( "1", projectContext.Client.Port ); + var projectContext2 = _context.Projects[1]; + Assert.Equal( "ClientApp2", projectContext2.Client.AppName ); + Assert.Equal( "2", projectContext2.Client.Port ); + } + + /// + /// 测试项目数据库类型 + /// + [Fact] + public void TestProjectContext_DbType() { + var projectContext = _context.Projects[0]; + Assert.Equal( DatabaseType.SqlServer, projectContext.DbType ); + var projectContext2 = _context.Projects[1]; + Assert.Equal( DatabaseType.PgSql, projectContext2.DbType ); + } + + /// + /// 测试项目目标数据库类型 + /// + [Fact] + public void TestProjectContext_TargetDbType() { + var projectContext = _context.Projects[0]; + Assert.Equal( DatabaseType.PgSql, projectContext.TargetDbType ); + var projectContext2 = _context.Projects[1]; + Assert.Equal( DatabaseType.PgSql, projectContext2.TargetDbType ); + } + + /// + /// 测试项目连接字符串 + /// + [Fact] + public void TestProjectContext_ConnectionString() { + var projectContext = _context.Projects[0]; + Assert.Equal( "TestConnection", projectContext.ConnectionString ); + var projectContext2 = _context.Projects[1]; + Assert.Equal( "TestConnection2", projectContext2.ConnectionString ); + } + + /// + /// 测试项目是否启用 + /// + [Fact] + public void TestProjectContext_Enabled() { + var projectContext = _context.Projects[0]; + Assert.True( projectContext.Enabled ); + var projectContext2 = _context.Projects[1]; + Assert.True( projectContext2.Enabled ); + var projectContext3 = _context.Projects[2]; + Assert.False( projectContext3.Enabled ); + } + + /// + /// 测试项目是否启用Utc + /// + [Fact] + public void TestProjectContext_Utc() { + var projectContext = _context.Projects[0]; + Assert.True( projectContext.Utc ); + var projectContext2 = _context.Projects[1]; + Assert.True( projectContext2.Utc ); + var projectContext3 = _context.Projects[2]; + Assert.False( projectContext3.Utc ); + } + + /// + /// 测试项目是否启用多语言 + /// + [Fact] + public void TestProjectContext_I18n() { + var projectContext = _context.Projects[0]; + Assert.True( projectContext.I18n ); + var projectContext2 = _context.Projects[1]; + Assert.True( projectContext2.I18n ); + var projectContext3 = _context.Projects[2]; + Assert.False( projectContext3.I18n ); + } + + /// + /// 测试项目类型 + /// + [Fact] + public void TestProjectContext_ProjectType() { + var projectContext = _context.Projects[0]; + Assert.Equal( ProjectType.WebApi, projectContext.ProjectType ); + var projectContext2 = _context.Projects[1]; + Assert.Equal( ProjectType.Ui, projectContext2.ProjectType ); + } + + /// + /// 测试Web Api项目端口 + /// + [Fact] + public void TestProjectContext_ApiPort() { + var projectContext = _context.Projects[0]; + Assert.Equal( "123", projectContext.ApiPort ); + var projectContext2 = _context.Projects[1]; + Assert.Equal( "456", projectContext2.ApiPort ); + } + + /// + /// 测试UI项目端口 + /// + [Fact] + public void TestProjectContext_UiPort() { + var projectContext = _context.Projects[0]; + Assert.Equal( "666", projectContext.UiPort ); + var projectContext2 = _context.Projects[1]; + Assert.Equal( "888", projectContext2.UiPort ); + } + + /// + /// 测试是否启用架构 + /// + [Fact] + public void TestProjectContext_EnableSchema() { + var projectContext = _context.Projects[0]; + Assert.True( projectContext.EnableSchema ); + var projectContext2 = _context.Projects[1]; + Assert.True( projectContext2.EnableSchema ); + } + + /// + /// 测试项目扩展 + /// + [Fact] + public void TestProjectContext_Extend() { + var projectContext = _context.Projects[0]; + Assert.Equal( "Extend1", projectContext.Extend ); + var projectContext2 = _context.Projects[1]; + Assert.Equal( "Extend2", projectContext2.Extend ); + } + + /// + /// 测试项目架构列表 + /// + [Fact] + public void TestProjectContext_Schemas() { + var projectContext = _context.Projects[0]; + Assert.Equal( 2, projectContext.Schemas.Count ); + Assert.Equal( "TestSchema", projectContext.Schemas[0] ); + Assert.Equal( "TestSchema2", projectContext.Schemas[1] ); + var projectContext2 = _context.Projects[1]; + Assert.Equal( 2, projectContext2.Schemas.Count ); + } + + /// + /// 测试实体架构 + /// + [Fact] + public void TestEntityContext_Schema() { + var projectContext = _context.Projects[0]; + Assert.Equal( "TestSchema", projectContext.Entities[0].Schema ); + Assert.Equal( "TestSchema2", projectContext.Entities[1].Schema ); + + var projectContext2 = _context.Projects[1]; + Assert.Equal( "TestSchema3", projectContext2.Entities[0].Schema ); + Assert.Equal( "TestSchema4", projectContext2.Entities[1].Schema ); + } + + /// + /// 测试实体描述 + /// + [Fact] + public void TestEntityContext_Description() { + var projectContext = _context.Projects[0]; + Assert.Equal( "TestComment", projectContext.Entities[0].Description ); + Assert.Equal( "TestComment2", projectContext.Entities[1].Description ); + + var projectContext2 = _context.Projects[1]; + Assert.Equal( "TestComment3", projectContext2.Entities[0].Description ); + Assert.Equal( "TestComment4", projectContext2.Entities[1].Description ); + } + + /// + /// 测试实体上下文根路径 + /// + [Fact] + public void TestEntityContext_RootPath() { + var projectContext = _context.Projects[0]; + Assert.Equal( @$"{GetBaseDirectory()}Output/Test", projectContext.Entities[0].Output.RootPath.Replace( "\\", "/" ) ); + Assert.Equal( @$"{GetBaseDirectory()}Output/Test", projectContext.Entities[1].Output.RootPath.Replace( "\\", "/" ) ); + + var projectContext2 = _context.Projects[1]; + Assert.Equal( @$"{GetBaseDirectory()}Output/Test2", projectContext2.Entities[0].Output.RootPath.Replace( "\\", "/" ) ); + Assert.Equal( @$"{GetBaseDirectory()}Output/Test2", projectContext2.Entities[1].Output.RootPath.Replace( "\\", "/" ) ); + } + + /// + /// 获取基路径 + /// + private string GetBaseDirectory() { + return AppContext.BaseDirectory.Replace( "\\", "/" ); + + } + + /// + /// 测试实体上下文输出文件名 + /// + [Fact] + public void TestEntityContext_FileNameNoExtension() { + var projectContext = _context.Projects[0]; + Assert.Equal( "TestName", projectContext.Entities[0].Output.FileNameNoExtension ); + Assert.Equal( "TestName2", projectContext.Entities[1].Output.FileNameNoExtension ); + + var projectContext2 = _context.Projects[1]; + Assert.Equal( "TestName3", projectContext2.Entities[0].Output.FileNameNoExtension ); + Assert.Equal( "TestName4", projectContext2.Entities[1].Output.FileNameNoExtension ); + } + + /// + /// 测试实体上下文路径 - 命名约定 + /// + [Fact] + public void TestEntityContext_Path_NamingConvention() { + var projectContext = _context.Projects[0]; + var entityContext = projectContext.Entities[0]; + entityContext.Output.FileNameNoExtension = "testName"; + entityContext.Output.Extension = ".ts"; + entityContext.Output.NamingConvention = NamingConvention.PascalCase; + Assert.Equal( @$"{GetBaseDirectory()}Output/Test/TestName.ts", entityContext.Output.Path.Replace( "\\","/" ) ); + + var entityContext2 = projectContext.Entities[1]; + entityContext2.Output.FileNameNoExtension = "TestName"; + entityContext2.Output.Extension = ".ts"; + entityContext2.Output.NamingConvention = NamingConvention.CamelCase; + Assert.Equal( @$"{GetBaseDirectory()}Output/Test/testName.ts", entityContext2.Output.Path.Replace( "\\", "/" ) ); + } + + /// + /// 测试实体上下文属性列表 + /// + [Fact] + public void TestEntityContext_Properties() { + var projectContext = _context.Projects[0]; + Assert.Equal( 2, projectContext.Entities[0].Properties.Count ); + Assert.Equal( 2, projectContext.Entities[1].Properties.Count ); + + var property = projectContext.Entities[0].Properties[0]; + Assert.Equal( "TestColumn", property.Name ); + Assert.Equal( "TestComment", property.Description ); + } + + /// + /// 测试实体上下文标识列 + /// + [Fact] + public void TestEntityContext_Key() { + var projectContext = _context.Projects[0]; + + var entity = projectContext.Entities[0]; + var property = entity.Properties[0]; + Assert.Equal( "TestColumn", property.Name ); + Assert.False( property.IsKey ); + + var property2 = entity.Properties[1]; + Assert.Equal( "TestColumn2", property2.Name ); + Assert.True( property2.IsKey ); + + Assert.Equal( "TestColumn2", entity.Key.Name ); + } + + /// + /// 测试实体上下文自增列 + /// + [Fact] + public void TestEntityContext_IsAutoIncrement() { + var projectContext = _context.Projects[0]; + + var entity = projectContext.Entities[0]; + var property = entity.Properties[0]; + Assert.Equal( "TestColumn", property.Name ); + Assert.False( property.IsAutoIncrement ); + + var property2 = entity.Properties[1]; + Assert.Equal( "TestColumn2", property2.Name ); + Assert.True( property2.IsAutoIncrement ); + } + + /// + /// 测试属性类型 + /// + [Fact] + public void TestProperty_Type() { + var projectContext = _context.Projects[0]; + var entity = projectContext.Entities[0]; + + var property = entity.Properties[0]; + Assert.Equal( "TestColumn", property.Name ); + Assert.Equal( DbType.String, property.Type ); + Assert.Equal( SystemType.String, property.SystemType ); + Assert.Equal( "nvarchar", property.NativeType ); + + var property2 = entity.Properties[1]; + Assert.Equal( "TestColumn2", property2.Name ); + Assert.Equal( DbType.AnsiString, property2.Type ); + Assert.Equal( SystemType.String, property2.SystemType ); + Assert.Equal( "varchar", property2.NativeType ); + } + + /// + /// 测试属性必填项 + /// + [Fact] + public void TestProperty_IsRequired() { + var projectContext = _context.Projects[0]; + var entity = projectContext.Entities[1]; + + var property = entity.Properties[0]; + Assert.Equal( "TestColumn3", property.Name ); + Assert.False( property.IsRequired ); + + var property2 = entity.Properties[1]; + Assert.Equal( "TestColumn4", property2.Name ); + Assert.True( property2.IsRequired ); + } + + /// + /// 测试属性最大长度 + /// + [Fact] + public void TestProperty_MaxLength() { + var projectContext = _context.Projects[0]; + var entity = projectContext.Entities[0]; + + var property = entity.Properties[0]; + Assert.Equal( "TestColumn", property.Name ); + Assert.Equal( 200, property.MaxLength ); + } + + /// + /// 测试属性精度 + /// + [Fact] + public void TestProperty_Precision() { + var projectContext = _context.Projects[0]; + var entity = projectContext.Entities[1]; + + var property = entity.Properties[0]; + Assert.Equal( "TestColumn3", property.Name ); + Assert.Equal( 5, property.Precision ); + Assert.Equal( 2, property.Scale ); + } + + /// + /// 测试生成器上下文 - 消息 + /// + [Fact] + public void TestGeneratorContext_Message() { + Assert.Equal( "Required_Message", _context.Message.RequiredMessage ); + Assert.Equal( "MaxLength_Message", _context.Message.MaxLengthMessage ); + } + } +} diff --git a/test/Util.Generators.Tests/Contexts/GeneratorContextTest.cs b/test/Util.Generators.Tests/Contexts/GeneratorContextTest.cs new file mode 100644 index 000000000..2ba62a60f --- /dev/null +++ b/test/Util.Generators.Tests/Contexts/GeneratorContextTest.cs @@ -0,0 +1,89 @@ +using Util.Generators.Configuration; +using Util.Generators.Contexts; +using Xunit; + +namespace Util.Generators.Tests.Contexts { + /// + /// 生成器上下文测试 + /// + public class GeneratorContextTest { + /// + /// 测试复制 + /// + [Fact] + public void TestClone() { + //创建生成器上下文 + var context = new GeneratorContext { + TemplateRootPath = "a", + OutputRootPath = "b", + Message = new MessageOptions { + RequiredMessage = "RequiredMessage", + MaxLengthMessage = "MaxLengthMessage" + } + }; + + //添加项目上下文1 + var project1 = new ProjectContext( context ) { Name = "c" }; + context.Projects.Add( project1 ); + + //添加实体上下文1 + var entityContext1 = new EntityContext( project1, "a" ); + project1.Entities.Add( entityContext1 ); + + //添加属性1 + var property = new Property( entityContext1 ) { Name = "p1" }; + entityContext1.Properties.Add( property ); + + //添加属性2 + var property2 = new Property( entityContext1 ) { Name = "p2" }; + entityContext1.Properties.Add( property2 ); + + //添加实体上下文2 + var entityContext2 = new EntityContext( project1, "b" ); + project1.Entities.Add( entityContext2 ); + + //添加项目上下文2 + var project2 = new ProjectContext( context ); + project2.Name = "d"; + context.Projects.Add( project2 ); + + //添加实体上下文3 + var entityContext3 = new EntityContext( project2, "c" ); + project2.Entities.Add( entityContext3 ); + + //添加实体上下文4 + var entityContext4 = new EntityContext( project2, "d" ); + project2.Entities.Add( entityContext4 ); + + //复制副本 + var clone = context.Clone(); + + //验证生成器上下文 + Assert.NotSame( context, clone ); + Assert.Equal( context.TemplateRootPath, clone.TemplateRootPath ); + Assert.Equal( context.OutputRootPath, clone.OutputRootPath ); + Assert.Equal( context.Message.RequiredMessage, clone.Message.RequiredMessage ); + Assert.Equal( context.Message.MaxLengthMessage, clone.Message.MaxLengthMessage ); + + //验证项目上下文 + Assert.Equal( 2, clone.Projects.Count ); + Assert.Equal( "c", clone.Projects[0].Name ); + Assert.Equal( "d", clone.Projects[1].Name ); + Assert.Same( clone, clone.Projects[0].GeneratorContext ); + Assert.Same( clone, clone.Projects[1].GeneratorContext ); + + //验证实体上下文 + Assert.Equal( 2, clone.Projects[0].Entities.Count ); + Assert.Equal( 2, clone.Projects[1].Entities.Count ); + Assert.Equal( "a", clone.Projects[0].Entities[0].Name ); + Assert.Equal( "b", clone.Projects[0].Entities[1].Name ); + Assert.Equal( "c", clone.Projects[1].Entities[0].Name ); + Assert.Equal( "d", clone.Projects[1].Entities[1].Name ); + Assert.Equal( clone, clone.Projects[0].Entities[0].ProjectContext.GeneratorContext ); + + //验证属性 + var entity = clone.Projects[0].Entities[0]; + Assert.Equal( 2, entity.Properties.Count ); + } + } +} diff --git a/test/Util.Generators.Tests/Contexts/OutputTest.cs b/test/Util.Generators.Tests/Contexts/OutputTest.cs new file mode 100644 index 000000000..d3e5aa88a --- /dev/null +++ b/test/Util.Generators.Tests/Contexts/OutputTest.cs @@ -0,0 +1,44 @@ +using Util.Generators.Contexts; +using Xunit; + +namespace Util.Generators.Tests.Contexts { + /// + /// 输出测试 + /// + public class OutputTest { + /// + /// 测试输出路径 + /// + [Fact] + public void TestPath() { + var output = new Output { + RootPath = @"c:\", + RelativeRootPath = @"a\b", + FileNameNoExtension = "e", + Extension = ".cs" + }; + Assert.Equal( @"c:/a/b/e.cs", output.Path ); + } + + /// + /// 测试复制 + /// + [Fact] + public void TestCopyTo() { + var output = new Output { + RootPath = "a", + RelativeRootPath = "b", + FileNameNoExtension = "c", + Extension = "d", + NamingConvention = NamingConvention.CamelCase + }; + var copy = new Output(); + output.CopyTo( copy ); + Assert.Equal( "a",copy.RootPath ); + Assert.Equal( "b", copy.RelativeRootPath ); + Assert.Equal( "c", copy.FileNameNoExtension ); + Assert.Equal( "d", copy.Extension ); + Assert.Equal( NamingConvention.CamelCase, copy.NamingConvention ); + } + } +} diff --git a/test/Util.Generators.Tests/Contexts/ProjectContextTest.cs b/test/Util.Generators.Tests/Contexts/ProjectContextTest.cs new file mode 100644 index 000000000..50c3eead4 --- /dev/null +++ b/test/Util.Generators.Tests/Contexts/ProjectContextTest.cs @@ -0,0 +1,83 @@ +using Util.Data; +using Util.Generators.Configuration; +using Util.Generators.Contexts; +using Util.Generators.Tests.Samples; +using Xunit; + +namespace Util.Generators.Tests.Contexts { + /// + /// 项目上下文测试 + /// + public class ProjectContextTest { + /// + /// 测试复制 + /// + [Fact] + public void TestClone() { + //创建项目上下文 + var generatorContext = new GeneratorContext { + TemplateRootPath = "Templates", + OutputRootPath = "Output" + }; + var projectContext = new ProjectContext( generatorContext ) { + Name = "Project", + UnitOfWorkName = "UnitOfWork", + DbType = DatabaseType.MySql, + TargetDbType = DatabaseType.PgSql, + ConnectionString = "ConnectionString", + Client = { + AppName = "ClientApp", + Port = "Port" + }, + Enabled = true, + Utc = true, + I18n = true, + EnableSchema = true, + ProjectType = ProjectType.Ui, + ApiPort = "80", + UiPort = "90", + Extend = "{\"Id\":\"1\",\"Name\":\"Name\"}" + }; + + //添加架构列表 + projectContext.Schemas.Add( "a" ); + projectContext.Schemas.Add( "b" ); + + //添加实体上下文1 + var entityContext1 = new EntityContext( projectContext, "a" ); + projectContext.Entities.Add( entityContext1 ); + + //添加实体上下文2 + var entityContext2 = new EntityContext( projectContext, "b" ); + projectContext.Entities.Add( entityContext2 ); + + //复制副本 + var clone = projectContext.Clone( generatorContext ); + + //验证项目上下文 + Assert.NotSame( projectContext, clone ); + Assert.Equal( projectContext.Name, clone.Name ); + Assert.Equal( projectContext.UnitOfWorkName, clone.UnitOfWorkName ); + Assert.Equal( projectContext.DbType, clone.DbType ); + Assert.Equal( projectContext.TargetDbType, clone.TargetDbType ); + Assert.Equal( projectContext.ConnectionString, clone.ConnectionString ); + Assert.Equal( projectContext.Client.AppName, clone.Client.AppName ); + Assert.Equal( projectContext.Client.Port, clone.Client.Port ); + Assert.Equal( projectContext.Enabled, clone.Enabled ); + Assert.Equal( projectContext.Utc, clone.Utc ); + Assert.Equal( projectContext.I18n, clone.I18n ); + Assert.Equal( projectContext.ProjectType, clone.ProjectType ); + Assert.Equal( projectContext.ApiPort, clone.ApiPort ); + Assert.Equal( projectContext.UiPort, clone.UiPort ); + Assert.Equal( projectContext.EnableSchema, clone.EnableSchema ); + Assert.Equal( projectContext.GetExtend().Id, clone.GetExtend().Id ); + Assert.Equal( projectContext.GetExtend().Name, clone.GetExtend().Name ); + + //验证架构列表 + Assert.Equal( 2, clone.Schemas.Count ); + + //验证实体上下文 + Assert.Equal( 2, clone.Entities.Count ); + } + } +} diff --git a/test/Util.Generators.Tests/Contexts/PropertyTest.cs b/test/Util.Generators.Tests/Contexts/PropertyTest.cs new file mode 100644 index 000000000..bca381004 --- /dev/null +++ b/test/Util.Generators.Tests/Contexts/PropertyTest.cs @@ -0,0 +1,59 @@ +using System.Data; +using Util.Generators.Contexts; +using Xunit; + +namespace Util.Generators.Tests.Contexts { + /// + /// 属性测试 + /// + public class PropertyTest { + /// + /// 测试复制 + /// + [Fact] + public void TestClone() { + //创建属性 + var generatorContext = new GeneratorContext { + TemplateRootPath = "Templates", + OutputRootPath = "Output" + }; + var projectContext = new ProjectContext( generatorContext ) { Name = "Project" }; + var context = new EntityContext( projectContext, "a" ) { + Output = { + Extension = ".txt" + } + }; + var property = new Property( context ) { + Name = "b", + Description = "c", + IsKey = true, + IsRequired = true, + SystemType = SystemType.DateTime, + Type = DbType.Boolean, + NativeType = "nvarchar", + MaxLength = 1, + IsAutoIncrement = true, + Precision = 5, + Scale = 2 + }; + + //复制副本 + var clone = property.Clone( context ); + + //验证属性 + Assert.NotSame( property, clone ); + Assert.Equal( property.Name, clone.Name ); + Assert.Equal( property.Description, clone.Description ); + Assert.Equal( property.IsKey, clone.IsKey ); + Assert.Equal( property.IsRequired, clone.IsRequired ); + Assert.Equal( property.Type, clone.Type ); + Assert.Equal( property.SystemType, clone.SystemType ); + Assert.Equal( property.NativeType, clone.NativeType ); + Assert.Equal( property.MaxLength, clone.MaxLength ); + Assert.Equal( property.IsAutoIncrement, clone.IsAutoIncrement ); + Assert.Equal( property.Precision, clone.Precision ); + Assert.Equal( property.Scale, clone.Scale ); + } + } +} + diff --git a/test/Util.Generators.Tests/GeneratorTest.cs b/test/Util.Generators.Tests/GeneratorTest.cs new file mode 100644 index 000000000..1081821f1 --- /dev/null +++ b/test/Util.Generators.Tests/GeneratorTest.cs @@ -0,0 +1,280 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Moq; +using Util.Generators.Contexts; +using Util.Generators.Logs; +using Util.Generators.Resources; +using Util.Generators.Templates; +using Xunit; + +namespace Util.Generators.Tests { + /// + /// 生成器测试 + /// + public class GeneratorTest { + /// + /// 生成器 + /// + private Generators.Generator _generator; + /// + /// 模拟生成器上下文构建器 + /// + private readonly Mock _mockGeneratorContextBuilder; + /// + /// 模拟模板查找器 + /// + private readonly Mock _mockTemplateFinder; + /// + /// 模拟静态资源管理器 + /// + private readonly Mock _mockResourceManager; + + /// + /// 测试初始化 + /// + public GeneratorTest() { + _mockGeneratorContextBuilder = new Mock(); + _mockTemplateFinder = new Mock(); + _mockResourceManager = new Mock(); + } + + /// + /// 测试生成 - 1个项目,1个实体,1个模板 + /// + [Fact] + public async Task TestGenerate_1() { + var path = "a"; + + //创建生成器上下文 + var generatorContext = new GeneratorContext { TemplateRootPath = path, OutputRootPath = "b" }; + var projectContext = new ProjectContext( generatorContext ) { Name = "project", Enabled = true, }; + projectContext.Entities.Add( new EntityContext( projectContext, "entity1" ) ); + generatorContext.Projects.Add( projectContext ); + + //设置模拟操作 + _mockGeneratorContextBuilder.Setup( t => t.BuildAsync() ).Returns( Task.FromResult(generatorContext) ); + var mockTemplate = new Mock(); + _mockTemplateFinder.Setup( t => t.Find( path, It.IsAny() ) ).Returns( new List { mockTemplate.Object } ); + + //执行生成 + _generator = new Generators.Generator( NullGeneratorLogger.Instance, _mockGeneratorContextBuilder.Object, _mockTemplateFinder.Object, _mockResourceManager.Object ); + await _generator.GenerateAsync(); + + //验证 + mockTemplate.Verify( t => t.RenderAsync( It.IsAny() ), Times.Once ); + } + + /// + /// 测试生成 - 1个项目,1个实体,2个模板 + /// + [Fact] + public async Task TestGenerate_2() { + var path = "a"; + + //创建生成器上下文 + var generatorContext = new GeneratorContext { TemplateRootPath = path, OutputRootPath = "b" }; + var projectContext = new ProjectContext( generatorContext ) { Name = "project", Enabled = true, }; + projectContext.Entities.Add( new EntityContext( projectContext, "entity1" ) ); + generatorContext.Projects.Add( projectContext ); + + //设置模拟操作 + _mockGeneratorContextBuilder.Setup( t => t.BuildAsync() ).Returns( Task.FromResult( generatorContext ) ); + var mockTemplate = new Mock(); + var mockTemplate2 = new Mock(); + _mockTemplateFinder.Setup( t => t.Find( path, It.IsAny() ) ).Returns( new List { mockTemplate.Object, mockTemplate2.Object } ); + + //执行生成 + _generator = new Generator( NullGeneratorLogger.Instance, _mockGeneratorContextBuilder.Object, _mockTemplateFinder.Object, _mockResourceManager.Object ); + await _generator.GenerateAsync(); + + //验证 + mockTemplate.Verify( t => t.RenderAsync( It.IsAny() ), Times.Once ); + mockTemplate2.Verify( t => t.RenderAsync( It.IsAny() ), Times.Once ); + } + + /// + /// 测试生成 - 1个项目,2个实体,1个模板 - 执行2次 + /// + [Fact] + public async Task TestGenerate_3() { + var path = "a"; + + //创建生成器上下文 + var generatorContext = new GeneratorContext { TemplateRootPath = path, OutputRootPath = "b" }; + var projectContext = new ProjectContext( generatorContext ) { Name = "project", Enabled = true, }; + projectContext.Entities.Add( new EntityContext( projectContext, "entity1" ) ); + projectContext.Entities.Add( new EntityContext( projectContext, "entity2" ) ); + generatorContext.Projects.Add( projectContext ); + + //设置模拟操作 + _mockGeneratorContextBuilder.Setup( t => t.BuildAsync() ).Returns( Task.FromResult( generatorContext ) ); + var mockTemplate = new Mock(); + _mockTemplateFinder.Setup( t => t.Find( path, It.IsAny() ) ).Returns( new List { mockTemplate.Object } ); + + //执行生成 + _generator = new Generator( NullGeneratorLogger.Instance, _mockGeneratorContextBuilder.Object, _mockTemplateFinder.Object, _mockResourceManager.Object ); + await _generator.GenerateAsync(); + + //验证 + mockTemplate.Verify( t => t.RenderAsync( It.IsAny() ), Times.Exactly( 2 ) ); + } + + /// + /// 测试生成 - 1个项目,2个实体,2个模板 - 每个模板执行2次 + /// + [Fact] + public async Task TestGenerate_4() { + var path = "a"; + + //创建生成器上下文 + var generatorContext = new GeneratorContext { TemplateRootPath = path, OutputRootPath = "b" }; + var projectContext = new ProjectContext( generatorContext ) { Name = "project", Enabled = true, }; + projectContext.Entities.Add( new EntityContext( projectContext, "entity1" ) ); + projectContext.Entities.Add( new EntityContext( projectContext, "entity2" ) ); + generatorContext.Projects.Add( projectContext ); + + //设置模拟操作 + _mockGeneratorContextBuilder.Setup( t => t.BuildAsync() ).Returns( Task.FromResult( generatorContext ) ); + var mockTemplate = new Mock(); + var mockTemplate2 = new Mock(); + _mockTemplateFinder.Setup( t => t.Find( path, It.IsAny() ) ).Returns( new List { mockTemplate.Object, mockTemplate2.Object } ); + + //执行生成 + _generator = new Generator( NullGeneratorLogger.Instance, _mockGeneratorContextBuilder.Object, _mockTemplateFinder.Object, _mockResourceManager.Object ); + await _generator.GenerateAsync(); + + //验证 + mockTemplate.Verify( t => t.RenderAsync( It.IsAny() ), Times.Exactly( 2 ) ); + mockTemplate2.Verify( t => t.RenderAsync( It.IsAny() ), Times.Exactly( 2 ) ); + } + + /// + /// 测试生成 - 2个项目,4个实体,2个模板 - 每个模板执行4次 + /// + [Fact] + public async Task TestGenerate_5() { + var path = "a"; + + //创建生成器上下文 + var generatorContext = new GeneratorContext { TemplateRootPath = path, OutputRootPath = "b" }; + + //创建项目上下文 + var projectContext = new ProjectContext( generatorContext ) { Name = "project", Enabled = true, }; + projectContext.Entities.Add( new EntityContext( projectContext, "entity1" ) ); + projectContext.Entities.Add( new EntityContext( projectContext, "entity2" ) ); + generatorContext.Projects.Add( projectContext ); + + //创建项目上下文2 + var projectContext2 = new ProjectContext( generatorContext ) { Name = "project2", Enabled = true, }; + projectContext2.Entities.Add( new EntityContext( projectContext2, "entity3" ) ); + projectContext2.Entities.Add( new EntityContext( projectContext2, "entity4" ) ); + generatorContext.Projects.Add( projectContext2 ); + + //设置模拟操作 + _mockGeneratorContextBuilder.Setup( t => t.BuildAsync() ).Returns( Task.FromResult( generatorContext ) ); + var mockTemplate = new Mock(); + var mockTemplate2 = new Mock(); + _mockTemplateFinder.Setup( t => t.Find( path, It.IsAny() ) ).Returns( new List { mockTemplate.Object, mockTemplate2.Object } ); + + //执行生成 + _generator = new Generator( NullGeneratorLogger.Instance, _mockGeneratorContextBuilder.Object, _mockTemplateFinder.Object, _mockResourceManager.Object ); + await _generator.GenerateAsync(); + + //验证 + mockTemplate.Verify( t => t.RenderAsync( It.IsAny() ), Times.Exactly( 4 ) ); + mockTemplate2.Verify( t => t.RenderAsync( It.IsAny() ), Times.Exactly( 4 ) ); + } + + /// + /// 测试生成 - 1个项目,项目已禁用,不生成 + /// + [Fact] + public async Task TestGenerate_6() { + var path = "a"; + + //创建生成器上下文 + var generatorContext = new GeneratorContext { TemplateRootPath = path, OutputRootPath = "b" }; + var projectContext = new ProjectContext( generatorContext ) { Name = "project", Enabled = false, }; + projectContext.Entities.Add( new EntityContext( projectContext, "entity1" ) ); + generatorContext.Projects.Add( projectContext ); + + //设置模拟操作 + _mockGeneratorContextBuilder.Setup( t => t.BuildAsync() ).Returns( Task.FromResult( generatorContext ) ); + var mockTemplate = new Mock(); + _mockTemplateFinder.Setup( t => t.Find( path, It.IsAny() ) ).Returns( new List { mockTemplate.Object } ); + + //执行生成 + _generator = new Generator( NullGeneratorLogger.Instance, _mockGeneratorContextBuilder.Object, _mockTemplateFinder.Object, _mockResourceManager.Object ); + await _generator.GenerateAsync(); + + //验证 + mockTemplate.Verify( t => t.RenderAsync( It.IsAny() ), Times.Never ); + } + + /// + /// 测试生成 - 2个项目,第一个项目已禁用 + /// + [Fact] + public async Task TestGenerate_7() { + var path = "a"; + + //创建生成器上下文 + var generatorContext = new GeneratorContext { TemplateRootPath = path, OutputRootPath = "b" }; + + //创建项目上下文 + var projectContext = new ProjectContext( generatorContext ) { Name = "project", Enabled = false, }; + projectContext.Entities.Add( new EntityContext( projectContext, "entity1" ) ); + projectContext.Entities.Add( new EntityContext( projectContext, "entity2" ) ); + generatorContext.Projects.Add( projectContext ); + + //创建项目上下文2 + var projectContext2 = new ProjectContext( generatorContext ) { Name = "project2", Enabled = true, }; + projectContext2.Entities.Add( new EntityContext( projectContext2, "entity3" ) ); + projectContext2.Entities.Add( new EntityContext( projectContext2, "entity4" ) ); + generatorContext.Projects.Add( projectContext2 ); + + //设置模拟操作 + _mockGeneratorContextBuilder.Setup( t => t.BuildAsync() ).Returns( Task.FromResult( generatorContext ) ); + var mockTemplate = new Mock(); + var mockTemplate2 = new Mock(); + _mockTemplateFinder.Setup( t => t.Find( path, It.IsAny() ) ).Returns( new List { mockTemplate.Object, mockTemplate2.Object } ); + + //执行生成 + _generator = new Generator( NullGeneratorLogger.Instance, _mockGeneratorContextBuilder.Object, _mockTemplateFinder.Object, _mockResourceManager.Object ); + await _generator.GenerateAsync(); + + //验证 + mockTemplate.Verify( t => t.RenderAsync( It.IsAny() ), Times.Exactly( 2 ) ); + mockTemplate2.Verify( t => t.RenderAsync( It.IsAny() ), Times.Exactly( 2 ) ); + } + + /// + /// 测试生成 - 导入静态资源 + /// + [Fact] + public async Task TestGenerate_8() { + var templateRootPath = "a"; + var outputRootPath = "b"; + var project = "project"; + + //创建生成器上下文 + var generatorContext = new GeneratorContext { TemplateRootPath = templateRootPath, OutputRootPath = outputRootPath }; + + //创建项目上下文 + var projectContext = new ProjectContext( generatorContext ) { Name = project, Enabled = true }; + generatorContext.Projects.Add( projectContext ); + + //设置模拟操作 + _mockGeneratorContextBuilder.Setup( t => t.BuildAsync() ).Returns( Task.FromResult( generatorContext ) ); + var mockTemplate = new Mock(); + _mockTemplateFinder.Setup( t => t.Find( templateRootPath, It.IsAny() ) ).Returns( new List { mockTemplate.Object } ); + + //执行生成 + _generator = new Generator( NullGeneratorLogger.Instance, _mockGeneratorContextBuilder.Object, _mockTemplateFinder.Object, _mockResourceManager.Object ); + await _generator.GenerateAsync(); + + //验证 + _mockResourceManager.Verify( t => t.ImportsAsync( templateRootPath, outputRootPath, projectContext ) ); + } + } +} diff --git a/test/Util.Generators.Tests/Mocks/MockGeneratorOptionsBuilder.cs b/test/Util.Generators.Tests/Mocks/MockGeneratorOptionsBuilder.cs new file mode 100644 index 000000000..58f6c6583 --- /dev/null +++ b/test/Util.Generators.Tests/Mocks/MockGeneratorOptionsBuilder.cs @@ -0,0 +1,119 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Util.Data; +using Util.Generators.Configuration; + +namespace Util.Generators.Tests.Mocks { + /// + /// 模拟生成器配置项构建器 + /// + public class MockGeneratorOptionsBuilder : IGeneratorOptionsBuilder { + /// + /// 构建生成器配置项 + /// + public Task BuildAsync() { + var options = new GeneratorOptions { + TemplatePath = "Templates", + OutputPath = "Output", + Messages = new MessageOptions { + RequiredMessage = "Required_Message", + MaxLengthMessage = "MaxLength_Message" + }, + Projects = new Dictionary { + {"Test",new ProjectOptions { + Name = "Test", + UnitOfWorkName = "UnitOfWork", + DbType = DatabaseType.SqlServer, + TargetDbType = DatabaseType.PgSql, + ConnectionString = "TestConnection", + Enabled = true, + Utc = true, + I18n = true, + Client = { + AppName = "ClientApp", + Port = "1" + }, + ProjectType = ProjectType.WebApi, + ApiPort = "123", + UiPort = "666", + EnableSchema = true, + Extend = "Extend1" + }}, + {"Test2",new ProjectOptions { + Name = "Test2", + UnitOfWorkName = "UnitOfWork2", + DbType = DatabaseType.PgSql, + ConnectionString = "TestConnection2", + Enabled = true, + Utc = true, + I18n = true, + EnableSchema = true, + Client = { + AppName = "ClientApp2", + Port = "2" + }, + ProjectType = ProjectType.Ui, + ApiPort = "456", + UiPort = "888", + Extend = "Extend2" + }}, + {"Test3",new ProjectOptions { + Name = "Test3", + UnitOfWorkName = "UnitOfWork3", + DbType = DatabaseType.SqlServer, + TargetDbType = DatabaseType.PgSql, + ConnectionString = "TestConnection3", + Enabled = false, + Utc = false, + I18n = false, + EnableSchema = false, + Client = { + AppName = "ClientApp3", + Port = "3" + }, + ProjectType = ProjectType.WebApi, + ApiPort = "789" + }}, + } + }; + return Task.FromResult( options ); + } + } + + /// + /// 模拟生成器配置项构建器2 + /// + public class MockGeneratorOptionsBuilder2 : IGeneratorOptionsBuilder { + /// + /// 构建生成器配置项 + /// + public Task BuildAsync() { + var options = new GeneratorOptions { + TemplatePath = @"c:\Templates", + OutputPath = @"d:\Output", + Projects = new Dictionary { + {"Test",new ProjectOptions { + Name = "Test", + UnitOfWorkName = "UnitOfWork", + DbType = DatabaseType.SqlServer, + ConnectionString = "TestConnection", + Client = { + AppName = "ClientApp", + } + }}, + {"Test2",new ProjectOptions { + Name = "Test2", + UnitOfWorkName = "UnitOfWork2", + DbType = DatabaseType.PgSql, + TargetDbType = DatabaseType.SqlServer, + ConnectionString = "TestConnection2", + Client = { + AppName = "ClientApp2", + } + }} + } + }; + return Task.FromResult( options ); + } + } +} diff --git a/test/Util.Generators.Tests/Mocks/MockMetadataService.cs b/test/Util.Generators.Tests/Mocks/MockMetadataService.cs new file mode 100644 index 000000000..e51e0d2f9 --- /dev/null +++ b/test/Util.Generators.Tests/Mocks/MockMetadataService.cs @@ -0,0 +1,90 @@ +using System.Threading.Tasks; +using Util.Data.Metadata; + +namespace Util.Generators.Tests.Mocks { + /// + /// 模拟数据库元数据服务 + /// + public class MockMetadataService : IMetadataService { + /// + /// 获取数据库信息 + /// + public Task GetDatabaseInfoAsync() { + var result = new DatabaseInfo(); + TableInfo table = new TableInfo { + Schema = "TestSchema", + Name = "TestName", + Comment = "TestComment" + }; + result.Tables.Add( table ); + + var column = new ColumnInfo { + Name = "TestColumn", + Comment = "TestComment", + DataType = "nvarchar", + Length = 200 + }; + table.Columns.Add( column ); + + var column2 = new ColumnInfo { + Name = "TestColumn2", + Comment = "TestComment2", + IsPrimaryKey = true, + DataType = "varchar", + IsAutoIncrement = true + }; + table.Columns.Add( column2 ); + + TableInfo table2 = new TableInfo { + Schema = "TestSchema2", + Name = "TestName2", + Comment = "TestComment2" + }; + result.Tables.Add( table2 ); + + var column3 = new ColumnInfo { + Name = "TestColumn3", + Comment = "TestComment3", + DataType = "decimal", + Precision = 5, + Scale = 2 + }; + table2.Columns.Add( column3 ); + + var column4 = new ColumnInfo { + Name = "TestColumn4", + Comment = "TestComment4", + IsNullable = false + }; + table2.Columns.Add( column4 ); + + return Task.FromResult( result ); + } + } + + /// + /// 模拟数据库元数据服务 + /// + public class MockMetadataService2 : IMetadataService { + /// + /// 获取数据库信息 + /// + public Task GetDatabaseInfoAsync() { + var result = new DatabaseInfo(); + TableInfo table = new TableInfo { + Schema = "TestSchema3", + Name = "TestName3", + Comment = "TestComment3" + }; + result.Tables.Add( table ); + + TableInfo table2 = new TableInfo { + Schema = "TestSchema4", + Name = "TestName4", + Comment = "TestComment4" + }; + result.Tables.Add( table2 ); + return Task.FromResult( result ); + } + } +} diff --git a/test/Util.Generators.Tests/Mocks/MockMetadataServiceFactory.cs b/test/Util.Generators.Tests/Mocks/MockMetadataServiceFactory.cs new file mode 100644 index 000000000..e1b72c4cd --- /dev/null +++ b/test/Util.Generators.Tests/Mocks/MockMetadataServiceFactory.cs @@ -0,0 +1,20 @@ +using Util.Data; +using Util.Data.Metadata; + +namespace Util.Generators.Tests.Mocks { + /// + /// 模拟数据库元数据服务工厂 + /// + public class MockMetadataServiceFactory : IMetadataServiceFactory { + /// + /// 创建数据库元数据服务 + /// + /// 数据库类型 + /// 数据库连接字符串 + public IMetadataService Create( DatabaseType dbType, string connection ) { + if( connection == "TestConnection" ) + return new MockMetadataService(); + return new MockMetadataService2(); + } + } +} diff --git a/test/Util.Generators.Tests/Samples/TestExtend.cs b/test/Util.Generators.Tests/Samples/TestExtend.cs new file mode 100644 index 000000000..fe39bda1c --- /dev/null +++ b/test/Util.Generators.Tests/Samples/TestExtend.cs @@ -0,0 +1,16 @@ +namespace Util.Generators.Tests.Samples { + /// + /// 测试扩展 + /// + public class TestExtend { + /// + /// 标识 + /// + public string Id { get; set; } + /// + /// 名称 + /// + public string Name { get; set; } + + } +} diff --git a/test/Util.Generators.Tests/Templates/TemplateFilterManagerTest.cs b/test/Util.Generators.Tests/Templates/TemplateFilterManagerTest.cs new file mode 100644 index 000000000..4743dfbdd --- /dev/null +++ b/test/Util.Generators.Tests/Templates/TemplateFilterManagerTest.cs @@ -0,0 +1,85 @@ +using System; +using Moq; +using Util.Generators.Contexts; +using Util.Generators.Templates; +using Xunit; + +namespace Util.Generators.Tests.Templates { + /// + /// 模板过滤器管理器测试 + /// + public class TemplateFilterManagerTest : IDisposable{ + /// + /// 模板过滤器管理器 + /// + private readonly TemplateFilterManager _filterManager; + + /// + /// 测试初始化 + /// + public TemplateFilterManagerTest() { + _filterManager = new TemplateFilterManager(); + } + + /// + /// 测试清理 + /// + public void Dispose() { + TemplateFilterManager.Clear(); + } + + /// + /// 测试是否过滤模板 - 1个过滤器返回true,结果为true + /// + [Fact] + public void TestIsFilter_1() { + //创建一个模板过滤器 + var mockFilter = new Mock(); + mockFilter.Setup( t => t.IsFilter( It.IsAny(), It.IsAny() ) ).Returns( true ); + + //添加到管理器 + TemplateFilterManager.AddFilter( mockFilter.Object ); + + //验证 + Assert.True( _filterManager.IsFilter( null,null ) ); + } + + /// + /// 测试是否过滤模板 - 2个过滤器,其中一个返回true,结果为true + /// + [Fact] + public void TestIsFilter_2() { + //创建2个模板过滤器 + var mockFilter = new Mock(); + mockFilter.Setup( t => t.IsFilter( It.IsAny(), It.IsAny() ) ).Returns( false ); + var mockFilter2 = new Mock(); + mockFilter2.Setup( t => t.IsFilter( It.IsAny(), It.IsAny() ) ).Returns( true ); + + //添加到管理器 + TemplateFilterManager.AddFilter( mockFilter.Object ); + TemplateFilterManager.AddFilter( mockFilter2.Object ); + + //验证 + Assert.True( _filterManager.IsFilter( null, null ) ); + } + + /// + /// 测试是否过滤模板 - 2个过滤器,都返回false,结果为false + /// + [Fact] + public void TestIsFilter_3() { + //创建2个模板过滤器 + var mockFilter = new Mock(); + mockFilter.Setup( t => t.IsFilter( It.IsAny(), It.IsAny() ) ).Returns( false ); + var mockFilter2 = new Mock(); + mockFilter2.Setup( t => t.IsFilter( It.IsAny(), It.IsAny() ) ).Returns( false ); + + //添加到管理器 + TemplateFilterManager.AddFilter( mockFilter.Object ); + TemplateFilterManager.AddFilter( mockFilter2.Object ); + + //验证 + Assert.False( _filterManager.IsFilter( null, null ) ); + } + } +} diff --git a/test/Util.Generators.Tests/Util.Generators.Tests.csproj b/test/Util.Generators.Tests/Util.Generators.Tests.csproj new file mode 100644 index 000000000..3b1373012 --- /dev/null +++ b/test/Util.Generators.Tests/Util.Generators.Tests.csproj @@ -0,0 +1,38 @@ + + + + $(NetTargetFramework) + false + + + + + + + + + true + PreserveNewest + Always + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.Generators.Tests/appsettings.json b/test/Util.Generators.Tests/appsettings.json new file mode 100644 index 000000000..7f75ed5b8 --- /dev/null +++ b/test/Util.Generators.Tests/appsettings.json @@ -0,0 +1,49 @@ +{ + "Generator": { + "TemplatePath": "TemplatePath", + "OutputPath": "OutputPath", + "Projects": { + "Test": { + "Name": "Test", + "DbType": "PgSql", + "ConnectionString": "ConnectionString_Test", + "UnitOfWorkName": "UnitOfWorkName_Test", + "Enabled": "true", + "Utc": "true", + "I18n": "true", + "ProjectType": "WebApi", + "ApiPort": "123", + "UiPort": "666", + "EnableSchema": "true", + "Client": { + "AppName": "ClientAppName_Test", + "Port": "1" + }, + "Extend": "1" + }, + "Test2": { + "Name": "Test2", + "DbType": "MySql", + "TargetDbType": "SqlServer", + "ConnectionString": "ConnectionString_Test2", + "UnitOfWorkName": "UnitOfWorkName_Test2", + "Enabled": "false", + "Utc": "false", + "I18n": "false", + "ProjectType": "Ui", + "ApiPort": "456", + "UiPort": "888", + "EnableSchema": "false", + "Client": { + "AppName": "ClientAppName_Test2", + "Port": "2" + }, + "Extend": "2" + } + }, + "Messages": { + "RequiredMessage": "RequiredMessage", + "MaxLengthMessage": "MaxLengthMessage" + } + } +} diff --git a/test/Util.IdGenerator.Tests/Helpers/IdTest.cs b/test/Util.IdGenerator.Tests/Helpers/IdTest.cs new file mode 100644 index 000000000..df8186f27 --- /dev/null +++ b/test/Util.IdGenerator.Tests/Helpers/IdTest.cs @@ -0,0 +1,148 @@ +namespace Util.IdGenerator.Tests.Helpers; + +/// +/// Id测试 +/// +public class IdTest { + /// + /// 输出日志 + /// + private readonly ITestOutputHelper _output; + + /// + /// 测试初始化 + /// + public IdTest( ITestOutputHelper output ) { + _output = output; + } + + /// + /// 测试设置自定义标识 + /// + [Fact] + public void TestSetId_1() { + var guid = Guid.NewGuid().ToString(); + Id.SetId( guid ); + Assert.Equal( guid, Id.Create() ); + Assert.Equal( new Guid( guid ), Id.CreateGuid() ); + Assert.Equal( guid, Id.CreateNanoid() ); + Id.Reset(); + Assert.NotEqual( guid, Id.Create() ); + Assert.NotEqual( new Guid( guid ), Id.CreateGuid() ); + Assert.NotEqual( guid, Id.CreateNanoid() ); + } + + /// + /// 测试设置自定义标识 - Nanoid异步方法 + /// + [Fact] + public async Task TestSetId_2() { + var guid = Guid.NewGuid().ToString(); + Id.SetId( guid ); + Assert.Equal( guid, await Id.CreateNanoidAsync() ); + Id.Reset(); + Assert.NotEqual( guid, await Id.CreateNanoidAsync() ); + } + + /// + /// 测试设置自定义标识 - YitId + /// + [Fact] + public void TestSetId_3() { + var id = 1; + Id.SetId( id.ToString() ); + Assert.Equal( id, Id.CreateYitId() ); + Id.Reset(); + Assert.NotEqual( id, Id.CreateYitId() ); + } + + /// + /// 测试 Nanoid - 默认返回21个字符 + /// + [Fact] + public void TestCreateNanoid_1() { + var id = Id.CreateNanoid(); + _output.WriteLine( id ); + Assert.Equal( 21, id.Length ); + } + + /// + /// 测试 Nanoid - 设置字符数 + /// + [Fact] + public void TestCreateNanoid_2() { + var id = Id.CreateNanoid( 10 ); + _output.WriteLine( id ); + Assert.Equal( 10, id.Length ); + } + + /// + /// 测试 Nanoid - 设置可选字符 + /// + [Fact] + public void TestCreateNanoid_3() { + var id = Id.CreateNanoid( "ab" ); + _output.WriteLine( id ); + Assert.Equal( 21, id.Count( t => t == 'a' ) + id.Count( t => t == 'b' ) ); + } + + /// + /// 测试 Nanoid - 设置可选字符和字符数 + /// + [Fact] + public void TestCreateNanoid_4() { + var id = Id.CreateNanoid( "ab", 10 ); + _output.WriteLine( id ); + Assert.Equal( 10, id.Count( t => t == 'a' ) + id.Count( t => t == 'b' ) ); + } + + /// + /// 测试 Nanoid - 默认返回21个字符 + /// + [Fact] + public async Task TestCreateNanoidAsync_1() { + var id = await Id.CreateNanoidAsync(); + _output.WriteLine( id ); + Assert.Equal( 21, id.Length ); + } + + /// + /// 测试 Nanoid - 设置字符数 + /// + [Fact] + public async Task TestCreateNanoidAsync_2() { + var id = await Id.CreateNanoidAsync( 10 ); + _output.WriteLine( id ); + Assert.Equal( 10, id.Length ); + } + + /// + /// 测试 Nanoid - 设置可选字符 + /// + [Fact] + public async Task TestCreateNanoidAsync_3() { + var id = await Id.CreateNanoidAsync( "ab" ); + _output.WriteLine( id ); + Assert.Equal( 21, id.Count( t => t == 'a' ) + id.Count( t => t == 'b' ) ); + } + + /// + /// 测试 Nanoid - 设置可选字符和字符数 + /// + [Fact] + public async Task TestCreateNanoidAsync_4() { + var id = await Id.CreateNanoidAsync( "ab", 10 ); + _output.WriteLine( id ); + Assert.Equal( 10, id.Count( t => t == 'a' ) + id.Count( t => t == 'b' ) ); + } + + /// + /// 测试使用 YitIdHelper 雪花漂移算法创建标识 + /// + [Fact] + public void TestCreateYitId() { + var id = Id.CreateYitId(); + _output.WriteLine( id.ToString() ); + Assert.NotEqual( 0, id ); + } +} \ No newline at end of file diff --git a/test/Util.IdGenerator.Tests/Usings.cs b/test/Util.IdGenerator.Tests/Usings.cs new file mode 100644 index 000000000..8db928bd6 --- /dev/null +++ b/test/Util.IdGenerator.Tests/Usings.cs @@ -0,0 +1,3 @@ +global using Xunit; +global using Xunit.Abstractions; +global using Util.Helpers; \ No newline at end of file diff --git a/test/Util.IdGenerator.Tests/Util.IdGenerator.Tests.csproj b/test/Util.IdGenerator.Tests/Util.IdGenerator.Tests.csproj new file mode 100644 index 000000000..89c3307de --- /dev/null +++ b/test/Util.IdGenerator.Tests/Util.IdGenerator.Tests.csproj @@ -0,0 +1,27 @@ + + + + $(NetTargetFramework) + enable + false + true + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.Images.Avatar.Tests.Integration/Fonts/STZHONGS.TTF b/test/Util.Images.Avatar.Tests.Integration/Fonts/STZHONGS.TTF new file mode 100644 index 000000000..412dfcda1 Binary files /dev/null and b/test/Util.Images.Avatar.Tests.Integration/Fonts/STZHONGS.TTF differ diff --git a/test/Util.Images.Avatar.Tests.Integration/Startup.cs b/test/Util.Images.Avatar.Tests.Integration/Startup.cs new file mode 100644 index 000000000..912419882 --- /dev/null +++ b/test/Util.Images.Avatar.Tests.Integration/Startup.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit.DependencyInjection.Logging; + +namespace Util.Images; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + hostBuilder.ConfigureDefaults( null ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + } +} \ No newline at end of file diff --git a/test/Util.Images.Avatar.Tests.Integration/Tests/AvatarManagerTest.cs b/test/Util.Images.Avatar.Tests.Integration/Tests/AvatarManagerTest.cs new file mode 100644 index 000000000..3dcf3d2c2 --- /dev/null +++ b/test/Util.Images.Avatar.Tests.Integration/Tests/AvatarManagerTest.cs @@ -0,0 +1,41 @@ +using System.Threading.Tasks; +using Util.Helpers; +using Xunit; + +namespace Util.Images.Tests; + +/// +/// 测试头像操作 +/// +public class AvatarManagerTest { + /// + /// 图片服务 + /// + private readonly IAvatarManager _avatarManager; + /// + /// 输出路径 + /// + private const string Path = "./test/{0}.gif"; + + /// + /// 测试初始化 + /// + public AvatarManagerTest( IAvatarManager avatarManager ) { + _avatarManager = avatarManager; + } + + /// + /// 测试 + /// + [Fact] + public async Task Test_1() { + ImageManager.LoadFont( "a", Common.GetPhysicalPath( "~/Fonts/STZHONGS.TTF" ) ); + await _avatarManager + .Size( 128 ) + .Uppercase( false ) + .Font( "a" ) + .FontSize( 64 ) + .Text( "Util+V" ) + .SaveAsync( string.Format( Path, "Test_1" ) ); + } +} \ No newline at end of file diff --git a/test/Util.Images.Avatar.Tests.Integration/Tests/AvatarManagerUnitTest.cs b/test/Util.Images.Avatar.Tests.Integration/Tests/AvatarManagerUnitTest.cs new file mode 100644 index 000000000..851567338 --- /dev/null +++ b/test/Util.Images.Avatar.Tests.Integration/Tests/AvatarManagerUnitTest.cs @@ -0,0 +1,277 @@ +using System.Threading.Tasks; +using Moq; +using SixLabors.Fonts; +using Xunit; + +namespace Util.Images.Tests; + +/// +/// 测试头像服务 - 单元测试 +/// +public class AvatarManagerUnitTest { + + #region 测试初始化 + + /// + /// 图片服务 + /// + private readonly Mock _mockImageManager; + /// + /// 图片操作包装器 + /// + private readonly Mock _mockImageWrapper; + /// + /// 头像服务 + /// + private readonly AvatarManager _avatarManager; + + /// + /// 测试初始化 + /// + public AvatarManagerUnitTest() { + _mockImageManager = new Mock(); + _mockImageWrapper = new Mock(); + InitMockImageManager(); + InitMockImageWrapper(); + _avatarManager = new AvatarManager( _mockImageManager.Object ); + } + + /// + /// 初始化图片模拟服务 + /// + private void InitMockImageManager() { + _mockImageManager.Setup( t => t.CreateImage( It.IsAny(), It.IsAny(), It.IsAny() ) ) + .Returns( _mockImageWrapper.Object ); + } + + /// + /// 初始化图片模拟包装器 + /// + private void InitMockImageWrapper() { + _mockImageWrapper.Setup( t => t.Font( It.IsAny(), It.IsAny(), It.IsAny() ) ) + .Returns( _mockImageWrapper.Object ); + _mockImageWrapper.Setup( t => t.TextCenter( It.IsAny(), It.IsAny() ) ) + .Returns( _mockImageWrapper.Object ); + } + + #endregion + + #region Size + + /// + /// 测试设置头像大小 - 默认值 + /// + [Fact] + public async Task TestSize_Default() { + await _avatarManager.Text( "a" ).SaveAsync( "path" ); + _mockImageManager.Verify( t => t.CreateImage( 64, 64, It.IsAny() ) ); + } + + /// + /// 测试设置头像大小 - 最低有效值 + /// + [Fact] + public async Task TestSize_1() { + await _avatarManager.Size( 16 ).Text( "a" ).SaveAsync( "path" ); + _mockImageManager.Verify( t => t.CreateImage( 16, 16, It.IsAny() ) ); + } + + /// + /// 测试设置头像大小 - 最高有效值 + /// + [Fact] + public async Task TestSize_2() { + await _avatarManager.Size( 512 ).Text( "a" ).SaveAsync( "path" ); + _mockImageManager.Verify( t => t.CreateImage( 512, 512, It.IsAny() ) ); + } + + /// + /// 测试设置头像大小 - 低于最低有效值 + /// + [Fact] + public async Task TestSize_3() { + await _avatarManager.Size( 1 ).Text( "a" ).SaveAsync( "path" ); + _mockImageManager.Verify( t => t.CreateImage( 16, 16, It.IsAny() ) ); + } + + /// + /// 测试设置头像大小 - 高于最高有效值 + /// + [Fact] + public async Task TestSize_4() { + await _avatarManager.Size( 600 ).Text( "a" ).SaveAsync( "path" ); + _mockImageManager.Verify( t => t.CreateImage( 512, 512, It.IsAny() ) ); + } + + #endregion + + #region Text + + /// + /// 测试设置文本 - 设置一个空值 + /// + [Fact] + public async Task TestText_Null() { + await _avatarManager.Text( null ).SaveAsync( "path" ); + _mockImageWrapper.Verify( t => t.TextCenter( It.IsAny(), It.IsAny() ),Times.Never ); + } + + /// + /// 测试设置文本 - 设置一个字符 + /// + [Fact] + public async Task TestText_1() { + await _avatarManager.Text( "a" ).SaveAsync( "path" ); + _mockImageWrapper.Verify( t => t.TextCenter( "A",It.IsAny() ) ); + } + + /// + /// 测试设置文本 - 设置2个字符,默认长度为1 + /// + [Fact] + public async Task TestText_2() { + await _avatarManager.Text( "ab" ).SaveAsync( "path" ); + _mockImageWrapper.Verify( t => t.TextCenter( "A", It.IsAny() ) ); + } + + /// + /// 测试设置文本 - 设置2个字符,长度限制为2 + /// + [Fact] + public async Task TestText_3() { + await _avatarManager.Text( "ab",2 ).SaveAsync( "path" ); + _mockImageWrapper.Verify( t => t.TextCenter( "AB", It.IsAny() ) ); + } + + /// + /// 测试设置文本 - 设置3个字符,长度限制为2 + /// + [Fact] + public async Task TestText_4() { + await _avatarManager.Text( "abc", 2 ).SaveAsync( "path" ); + _mockImageWrapper.Verify( t => t.TextCenter( "AB", It.IsAny() ) ); + } + + /// + /// 测试设置文本 - 设置2个字符,长度限制为3 + /// + [Fact] + public async Task TestText_5() { + await _avatarManager.Text( "ab", 3 ).SaveAsync( "path" ); + _mockImageWrapper.Verify( t => t.TextCenter( "AB", It.IsAny() ) ); + } + + /// + /// 测试设置文本 - 带加号 + /// + [Fact] + public async Task TestText_6() { + await _avatarManager.Text( "ab+cd" ).SaveAsync( "path" ); + _mockImageWrapper.Verify( t => t.TextCenter( "AC", It.IsAny() ) ); + } + + /// + /// 测试设置文本 - 加号有空格 + /// + [Fact] + public async Task TestText_7() { + await _avatarManager.Text( "ab + cd" ).SaveAsync( "path" ); + _mockImageWrapper.Verify( t => t.TextCenter( "AC", It.IsAny() ) ); + } + + /// + /// 测试设置文本 - 2个加号,操作数有空值 + /// + [Fact] + public async Task TestText_8() { + await _avatarManager.Text( " + ab + cd " ).SaveAsync( "path" ); + _mockImageWrapper.Verify( t => t.TextCenter( "AC", It.IsAny() ) ); + } + + /// + /// 测试设置文本 - 不转大写 + /// + [Fact] + public async Task TestText_9() { + await _avatarManager.Uppercase( false ).Text( "Ab+cd" ).SaveAsync( "path" ); + _mockImageWrapper.Verify( t => t.TextCenter( "Ac", It.IsAny() ) ); + } + + #endregion + + #region FontSize + + /// + /// 测试设置字体大小 - 默认值 + /// + [Fact] + public async Task TestFontSize_Default() { + await _avatarManager.Size( 128 ).Text( "a" ).SaveAsync( "path" ); + _mockImageWrapper.Verify( t => t.Font( 64, It.IsAny(), It.IsAny() ) ); + } + + /// + /// 测试设置字体大小 - 最低有效像素值 + /// + [Fact] + public async Task TestFontSize_1() { + await _avatarManager.Size( 16 ).FontSize( 16 ).Text( "a" ).SaveAsync( "path" ); + _mockImageWrapper.Verify( t => t.Font( 16,It.IsAny(), It.IsAny() ) ); + } + + /// + /// 测试设置字体大小 - 最高有效像素值 + /// + [Fact] + public async Task TestFontSize_2() { + await _avatarManager.Size( 512 ).FontSize( 512 ).Text( "a" ).SaveAsync( "path" ); + _mockImageWrapper.Verify( t => t.Font( 512, It.IsAny(), It.IsAny() ) ); + } + + /// + /// 测试设置字体大小 - 低于最低有效像素值 + /// + [Fact] + public async Task TestFontSize_3() { + await _avatarManager.Size( 32 ).FontSize( 10 ).Text( "a" ).SaveAsync( "path" ); + _mockImageWrapper.Verify( t => t.Font( 16, It.IsAny(), It.IsAny() ) ); + } + + /// + /// 测试设置字体大小 - 字体像素高于头像像素 + /// + [Fact] + public async Task TestFontSize_4() { + await _avatarManager.Size( 32 ).FontSize( 64 ).Text( "a" ).SaveAsync( "path" ); + _mockImageWrapper.Verify( t => t.Font( 16, It.IsAny(), It.IsAny() ) ); + } + + /// + /// 测试设置字体大小 - 高于最高有效像素值 + /// + [Fact] + public async Task TestFontSize_5() { + await _avatarManager.Size( 128 ).FontSize( 600 ).Text( "a" ).SaveAsync( "path" ); + _mockImageWrapper.Verify( t => t.Font( 64, It.IsAny(), It.IsAny() ) ); + } + + /// + /// 测试设置字体大小 - 最小百分比 + /// + [Fact] + public async Task TestFontSize_6() { + await _avatarManager.Size( 128 ).FontSize( 0.1 ).Text( "a" ).SaveAsync( "path" ); + _mockImageWrapper.Verify( t => t.Font( 12.8f, It.IsAny(), It.IsAny() ) ); + } + + /// + /// 测试设置字体大小 - 最大百分比 + /// + [Fact] + public async Task TestFontSize_7() { + await _avatarManager.Size( 128 ).FontSize( 1 ).Text( "a" ).SaveAsync( "path" ); + _mockImageWrapper.Verify( t => t.Font( 128, It.IsAny(), It.IsAny() ) ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Images.Avatar.Tests.Integration/Util.Images.Avatar.Tests.Integration.csproj b/test/Util.Images.Avatar.Tests.Integration/Util.Images.Avatar.Tests.Integration.csproj new file mode 100644 index 000000000..4a366ae7d --- /dev/null +++ b/test/Util.Images.Avatar.Tests.Integration/Util.Images.Avatar.Tests.Integration.csproj @@ -0,0 +1,36 @@ + + + + $(NetTargetFramework) + false + Util.Images + Util.Images.Startup + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + Always + + + + diff --git a/test/Util.Images.ImageSharp.Tests.Integration/Fonts/STZHONGS.TTF b/test/Util.Images.ImageSharp.Tests.Integration/Fonts/STZHONGS.TTF new file mode 100644 index 000000000..412dfcda1 Binary files /dev/null and b/test/Util.Images.ImageSharp.Tests.Integration/Fonts/STZHONGS.TTF differ diff --git a/test/Util.Images.ImageSharp.Tests.Integration/Startup.cs b/test/Util.Images.ImageSharp.Tests.Integration/Startup.cs new file mode 100644 index 000000000..2eaea3102 --- /dev/null +++ b/test/Util.Images.ImageSharp.Tests.Integration/Startup.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit.DependencyInjection.Logging; + +namespace Util.Images; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + hostBuilder.ConfigureDefaults( null ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + } +} \ No newline at end of file diff --git a/test/Util.Images.ImageSharp.Tests.Integration/Tests/DrawTextTest.cs b/test/Util.Images.ImageSharp.Tests.Integration/Tests/DrawTextTest.cs new file mode 100644 index 000000000..5d2d61f63 --- /dev/null +++ b/test/Util.Images.ImageSharp.Tests.Integration/Tests/DrawTextTest.cs @@ -0,0 +1,92 @@ +using System.Threading.Tasks; +using Util.Helpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Images.Tests; + +/// +/// 测试图片操作 - 写文本 +/// +public class DrawTextTest { + /// + /// 测试输出 + /// + private readonly ITestOutputHelper _testOutputHelper; + /// + /// 图片服务 + /// + private readonly IImageManager _imageManager; + /// + /// 输出路径 + /// + private const string Path = "./test/{0}.gif"; + + /// + /// 测试初始化 + /// + public DrawTextTest( ITestOutputHelper testOutputHelper, IImageManager imageManager ) { + _testOutputHelper = testOutputHelper; + _imageManager = imageManager; + } + + /// + /// 测试 - 内置字体,指定坐标 + /// + [Fact] + public async Task Test_1() { + await _imageManager.CreateImage( 128, 128, "5d005d" ) + .DefaultFontName( "Microsoft YaHei" ) + .Font( 48 ) + .Text( "U", "fff", 50, 38 ) + .SaveAsync( string.Format( Path, "Test_1" ) ); + } + + /// + /// 测试 - 加载字体,居中显示 + /// + [Fact] + public async Task Test_2() { + ImageManager.LoadFont( "a", Common.GetPhysicalPath( "~/Fonts/STZHONGS.TTF" ) ); + await _imageManager.CreateImage( 128, 128, "5d005d" ) + .Font( 48,"a" ) + .TextCenter( "U", "fff" ) + .SaveAsync( string.Format( Path, "Test_2" ) ); + } + + /// + /// 测试获取流 + /// + [Fact] + public async Task TestToStreamAsync() { + var result = await _imageManager.CreateImage( 128, 128, "5d005d" ) + .ImageType( ImageType.Gif ) + .Font( 48 ) + .TextCenter( "U", "fff" ) + .ToStreamAsync(); + await Util.Helpers.File.WriteAsync( string.Format( Path, "TestToStreamAsync" ),result ); + } + + /// + /// 测试获取base64 + /// + [Fact] + public void TestToBase64() { + var result = _imageManager.CreateImage( 128, 128, "5d005d" ) + .ImageType( ImageType.Gif ) + .Font( 48 ) + .TextCenter( "U", "fff" ) + .ToBase64(); + _testOutputHelper.WriteLine( result ); + } + + /// + /// 测试受支持的字体列表 + /// + [Fact] + public void TestGetSupportedFonts() { + ImageManager.LoadFont( "a", Common.GetPhysicalPath( "~/Fonts/STZHONGS.TTF" ) ); + var result = ImageManager.GetSupportedFonts(); + _testOutputHelper.WriteLine( result.Join() ); + } +} \ No newline at end of file diff --git a/test/Util.Images.ImageSharp.Tests.Integration/Util.Images.ImageSharp.Tests.Integration.csproj b/test/Util.Images.ImageSharp.Tests.Integration/Util.Images.ImageSharp.Tests.Integration.csproj new file mode 100644 index 000000000..755e747f4 --- /dev/null +++ b/test/Util.Images.ImageSharp.Tests.Integration/Util.Images.ImageSharp.Tests.Integration.csproj @@ -0,0 +1,35 @@ + + + + $(NetTargetFramework) + false + Util.Images + Util.Images.Startup + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + Always + + + + diff --git a/test/Util.Localization.Resources/Properties/AssemblyInfo.cs b/test/Util.Localization.Resources/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..7128ad7d9 --- /dev/null +++ b/test/Util.Localization.Resources/Properties/AssemblyInfo.cs @@ -0,0 +1,2 @@ +using Microsoft.Extensions.Localization; +[assembly: ResourceLocation( "Resources2" )] \ No newline at end of file diff --git a/test/Util.Localization.Resources/ResourceTypes/Resource101.cs b/test/Util.Localization.Resources/ResourceTypes/Resource101.cs new file mode 100644 index 000000000..2fa01ca4a --- /dev/null +++ b/test/Util.Localization.Resources/ResourceTypes/Resource101.cs @@ -0,0 +1,7 @@ +namespace Util.Localization.Resources.ResourceTypes; + +/// +/// 资源类型101 +/// +public class Resource101 { +} \ No newline at end of file diff --git a/test/Util.Localization.Resources/ResourceTypes/Resource102.cs b/test/Util.Localization.Resources/ResourceTypes/Resource102.cs new file mode 100644 index 000000000..a54670ab1 --- /dev/null +++ b/test/Util.Localization.Resources/ResourceTypes/Resource102.cs @@ -0,0 +1,7 @@ +namespace Util.Localization.Resources.ResourceTypes; + +/// +/// 资源类型102 +/// +public class Resource102 { +} \ No newline at end of file diff --git a/test/Util.Localization.Resources/Resources2/ResourceTypes.Resource101.en-US.json b/test/Util.Localization.Resources/Resources2/ResourceTypes.Resource101.en-US.json new file mode 100644 index 000000000..3872235a3 --- /dev/null +++ b/test/Util.Localization.Resources/Resources2/ResourceTypes.Resource101.en-US.json @@ -0,0 +1,3 @@ +{ + "Hello": "hi" +} diff --git a/test/Util.Localization.Resources/Resources2/ResourceTypes.Resource101.zh-CN.json b/test/Util.Localization.Resources/Resources2/ResourceTypes.Resource101.zh-CN.json new file mode 100644 index 000000000..ac7a21b4e --- /dev/null +++ b/test/Util.Localization.Resources/Resources2/ResourceTypes.Resource101.zh-CN.json @@ -0,0 +1,3 @@ +{ + "Hello": "哈楼" +} diff --git a/test/Util.Localization.Resources/Resources2/ResourceTypes/Resource102.zh-CN.json b/test/Util.Localization.Resources/Resources2/ResourceTypes/Resource102.zh-CN.json new file mode 100644 index 000000000..353d43fbf --- /dev/null +++ b/test/Util.Localization.Resources/Resources2/ResourceTypes/Resource102.zh-CN.json @@ -0,0 +1,3 @@ +{ + "Hello": "哈罗" +} diff --git a/test/Util.Localization.Resources/Util.Localization.Resources.csproj b/test/Util.Localization.Resources/Util.Localization.Resources.csproj new file mode 100644 index 000000000..b8789deb2 --- /dev/null +++ b/test/Util.Localization.Resources/Util.Localization.Resources.csproj @@ -0,0 +1,36 @@ + + + + $(NetTargetFramework) + enable + + + + + + + + + + + PreserveNewest + true + PreserveNewest + + + PreserveNewest + true + PreserveNewest + + + PreserveNewest + true + PreserveNewest + + + + + + + + diff --git a/test/Util.Localization.Tests.Integration/Properties/AssemblyInfo.cs b/test/Util.Localization.Tests.Integration/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..852ad197f --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Properties/AssemblyInfo.cs @@ -0,0 +1,2 @@ +using Microsoft.Extensions.Localization; +[assembly: RootNamespace( "Util.Localization" )] \ No newline at end of file diff --git a/test/Util.Localization.Tests.Integration/Properties/launchSettings.json b/test/Util.Localization.Tests.Integration/Properties/launchSettings.json new file mode 100644 index 000000000..0be0c4653 --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Util.Localization.Tests.Integration": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:54279;http://localhost:54280" + } + } +} \ No newline at end of file diff --git a/test/Util.Localization.Tests.Integration/ResourceTypes/Resource1.cs b/test/Util.Localization.Tests.Integration/ResourceTypes/Resource1.cs new file mode 100644 index 000000000..ea47ac451 --- /dev/null +++ b/test/Util.Localization.Tests.Integration/ResourceTypes/Resource1.cs @@ -0,0 +1,7 @@ +namespace Util.Localization.ResourceTypes; + +/// +/// 资源类型1 +/// +public class Resource1 { +} \ No newline at end of file diff --git a/test/Util.Localization.Tests.Integration/ResourceTypes/Resource2.cs b/test/Util.Localization.Tests.Integration/ResourceTypes/Resource2.cs new file mode 100644 index 000000000..4e3d52376 --- /dev/null +++ b/test/Util.Localization.Tests.Integration/ResourceTypes/Resource2.cs @@ -0,0 +1,7 @@ +namespace Util.Localization.ResourceTypes; + +/// +/// 资源类型2 +/// +public class Resource2 { +} \ No newline at end of file diff --git a/test/Util.Localization.Tests.Integration/ResourceTypes/Resource3.cs b/test/Util.Localization.Tests.Integration/ResourceTypes/Resource3.cs new file mode 100644 index 000000000..e98db3650 --- /dev/null +++ b/test/Util.Localization.Tests.Integration/ResourceTypes/Resource3.cs @@ -0,0 +1,7 @@ +namespace Util.Localization.ResourceTypes; + +/// +/// 资源类型3 +/// +public class Resource3 { +} \ No newline at end of file diff --git a/test/Util.Localization.Tests.Integration/ResourceTypes/Resource4.cs b/test/Util.Localization.Tests.Integration/ResourceTypes/Resource4.cs new file mode 100644 index 000000000..841584620 --- /dev/null +++ b/test/Util.Localization.Tests.Integration/ResourceTypes/Resource4.cs @@ -0,0 +1,14 @@ +namespace Util.Localization.ResourceTypes; + +/// +/// 资源类型4 +/// +public class Resource4 { + + /// + /// 资源类型41 - 测试内部类 + /// + public class Resource41 { + } + +} \ No newline at end of file diff --git a/test/Util.Localization.Tests.Integration/ResourceTypes/Resource5.cs b/test/Util.Localization.Tests.Integration/ResourceTypes/Resource5.cs new file mode 100644 index 000000000..07fcdb7d8 --- /dev/null +++ b/test/Util.Localization.Tests.Integration/ResourceTypes/Resource5.cs @@ -0,0 +1,7 @@ +namespace Util.Localization.ResourceTypes; + +/// +/// 资源类型5 +/// +public class Resource5 { +} \ No newline at end of file diff --git a/test/Util.Localization.Tests.Integration/ResourceTypes/Resource6.cs b/test/Util.Localization.Tests.Integration/ResourceTypes/Resource6.cs new file mode 100644 index 000000000..6510badfa --- /dev/null +++ b/test/Util.Localization.Tests.Integration/ResourceTypes/Resource6.cs @@ -0,0 +1,7 @@ +namespace Util.Localization.ResourceTypes; + +/// +/// 资源类型6 +/// +public class Resource6 { +} \ No newline at end of file diff --git a/test/Util.Localization.Tests.Integration/Resources/ResourceTypes.Resource1.en-US.json b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes.Resource1.en-US.json new file mode 100644 index 000000000..3872235a3 --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes.Resource1.en-US.json @@ -0,0 +1,3 @@ +{ + "Hello": "hi" +} diff --git a/test/Util.Localization.Tests.Integration/Resources/ResourceTypes.Resource1.zh-CN.json b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes.Resource1.zh-CN.json new file mode 100644 index 000000000..ac7a21b4e --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes.Resource1.zh-CN.json @@ -0,0 +1,3 @@ +{ + "Hello": "哈楼" +} diff --git a/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource2.en-US.json b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource2.en-US.json new file mode 100644 index 000000000..3872235a3 --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource2.en-US.json @@ -0,0 +1,3 @@ +{ + "Hello": "hi" +} diff --git a/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource2.zh-CN.json b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource2.zh-CN.json new file mode 100644 index 000000000..e8768122b --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource2.zh-CN.json @@ -0,0 +1,5 @@ +{ + "Hello": "哈楼", + "World": "世界", + "Hello,{0},{1},World": "哈楼,{0},{1},世界" +} diff --git a/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource2.zh-Hans.json b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource2.zh-Hans.json new file mode 100644 index 000000000..ac074d1eb --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource2.zh-Hans.json @@ -0,0 +1,4 @@ +{ + "Hello": "哈哈", + "World": "世界" +} diff --git a/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource2.zh.json b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource2.zh.json new file mode 100644 index 000000000..a93499693 --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource2.zh.json @@ -0,0 +1,4 @@ +{ + "Hello": "嘿嘿", + "World": "世界" +} diff --git a/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource3.zh-Hans.json b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource3.zh-Hans.json new file mode 100644 index 000000000..ac074d1eb --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource3.zh-Hans.json @@ -0,0 +1,4 @@ +{ + "Hello": "哈哈", + "World": "世界" +} diff --git a/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource4.Resource41.zh-CN.json b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource4.Resource41.zh-CN.json new file mode 100644 index 000000000..bc4c2798c --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource4.Resource41.zh-CN.json @@ -0,0 +1,4 @@ +{ + "Hello": "嘻嘻", + "World": "世界" +} diff --git a/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource4.zh.json b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource4.zh.json new file mode 100644 index 000000000..a93499693 --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource4.zh.json @@ -0,0 +1,4 @@ +{ + "Hello": "嘿嘿", + "World": "世界" +} diff --git a/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource5.zh-CN.json b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource5.zh-CN.json new file mode 100644 index 000000000..4c9815c0d --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource5.zh-CN.json @@ -0,0 +1,4 @@ +{ + "Hello": "哈楼", + "World": "世界" +} diff --git a/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource5.zh-Hans.json b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource5.zh-Hans.json new file mode 100644 index 000000000..0bf6f9055 --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource5.zh-Hans.json @@ -0,0 +1,4 @@ +{ + "Hello2": "哈楼2", + "World2": "世界2" +} \ No newline at end of file diff --git a/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource5.zh.json b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource5.zh.json new file mode 100644 index 000000000..a833d36d9 --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Resources/ResourceTypes/Resource5.zh.json @@ -0,0 +1,4 @@ +{ + "Hello3": "哈楼3", + "World3": "世界3" +} \ No newline at end of file diff --git a/test/Util.Localization.Tests.Integration/Resources/zh.json b/test/Util.Localization.Tests.Integration/Resources/zh.json new file mode 100644 index 000000000..da9e62f97 --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Resources/zh.json @@ -0,0 +1,4 @@ +{ + "Hello": "呜呜", + "World": "世界" +} diff --git a/test/Util.Localization.Tests.Integration/Startup.cs b/test/Util.Localization.Tests.Integration/Startup.cs new file mode 100644 index 000000000..2784a351a --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Startup.cs @@ -0,0 +1,46 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Http; +using Xunit.DependencyInjection.Logging; + +namespace Util.Localization; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + hostBuilder.ConfigureDefaults( null ) + .ConfigureWebHostDefaults( webHostBuilder => { + webHostBuilder.UseTestServer() + .Configure( t => { + t.UseRouting(); + t.UseEndpoints( endpoints => { + endpoints.MapControllers(); + } ); + } ); + } ) + .AsBuild() + .AddJsonLocalization() + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddControllers(); + services.AddTransient( t => { + var client = new HttpClientService(); + client.SetHttpClient( t.GetService().GetTestClient() ); + return client; + } ); + } +} \ No newline at end of file diff --git a/test/Util.Localization.Tests.Integration/Tests/JsonStringLocalizerFactoryTest.cs b/test/Util.Localization.Tests.Integration/Tests/JsonStringLocalizerFactoryTest.cs new file mode 100644 index 000000000..000c577e6 --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Tests/JsonStringLocalizerFactoryTest.cs @@ -0,0 +1,110 @@ +using System.Globalization; +using System.Reflection; +using Microsoft.Extensions.Localization; +using Util.Localization.Resources.ResourceTypes; +using Util.Localization.ResourceTypes; +using Xunit; + +namespace Util.Localization.Tests; + +/// +/// Json字符串定位器工厂测试 +/// +public class JsonStringLocalizerFactoryTest { + /// + /// Json字符串定位器工厂 + /// + private readonly IStringLocalizerFactory _factory; + + /// + /// 测试初始化 + /// + /// Json字符串定位器工厂 + public JsonStringLocalizerFactoryTest( IStringLocalizerFactory factory ) { + _factory = factory; + } + + /// + /// 测试获取本地化字符串值 - 读取 Resources\ResourceTypes.Resource1.en-US.json + /// + [Fact] + public void Test_1() { + CultureInfo.CurrentUICulture = new CultureInfo( "en-US" ); + var assemblyName = new AssemblyName( GetType().Assembly.FullName ); + var localizer = _factory.Create( "ResourceTypes.Resource1", assemblyName.Name ); + var value = localizer["Hello"]; + Assert.Equal( "hi", value ); + } + + /// + /// 测试获取本地化字符串值 - 读取 Resources\ResourceTypes.Resource1.zh-CN.json + /// + [Fact] + public void Test_2() { + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + var assemblyName = new AssemblyName( GetType().Assembly.FullName ); + var localizer = _factory.Create( "ResourceTypes.Resource1", assemblyName.Name ); + var value = localizer["Hello"]; + Assert.Equal( "哈楼", value ); + } + + /// + /// 测试获取本地化字符串值 - 读取 Resources2\ResourceTypes.Resource101.en-US.json + /// + [Fact] + public void Test_3() { + CultureInfo.CurrentUICulture = new CultureInfo( "en-US" ); + var assemblyName = new AssemblyName( typeof( Resource101 ).Assembly.FullName ); + var localizer = _factory.Create( "ResourceTypes.Resource101", assemblyName.Name ); + var value = localizer["Hello"]; + Assert.Equal( "hi", value ); + } + + /// + /// 测试获取本地化字符串值 - 读取 Resources2\ResourceTypes.Resource101.zh-CN.json + /// + [Fact] + public void Test_4() { + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + var assemblyName = new AssemblyName( typeof( Resource101 ).Assembly.FullName ); + var localizer = _factory.Create( "ResourceTypes.Resource101", assemblyName.Name ); + var value = localizer["Hello"]; + Assert.Equal( "哈楼", value ); + } + + /// + /// 测试获取本地化字符串值 - 读取 Resources\ResourceTypes\Resource2.en-US.json + /// + [Fact] + public void Test_5() { + CultureInfo.CurrentUICulture = new CultureInfo( "en-US" ); + var assemblyName = new AssemblyName( typeof( Resource2 ).Assembly.FullName ); + var localizer = _factory.Create( "ResourceTypes.Resource2", assemblyName.Name ); + var value = localizer["Hello"]; + Assert.Equal( "hi", value ); + } + + /// + /// 测试获取本地化字符串值 - 读取 Resources\ResourceTypes\Resource2.zh-CN.json + /// + [Fact] + public void Test_6() { + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + var assemblyName = new AssemblyName( typeof( Resource2 ).Assembly.FullName ); + var localizer = _factory.Create( "ResourceTypes.Resource2", assemblyName.Name ); + var value = localizer["Hello"]; + Assert.Equal( "哈楼", value ); + } + + /// + /// 测试获取本地化字符串值 - 读取 Resources\zh.json + /// + [Fact] + public void Test_7() { + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + var assemblyName = new AssemblyName( GetType().Assembly.FullName ); + var localizer = _factory.Create( null, assemblyName.Name ); + var value = localizer["Hello"]; + Assert.Equal( "呜呜", value ); + } +} \ No newline at end of file diff --git a/test/Util.Localization.Tests.Integration/Tests/PathResolverTest.cs b/test/Util.Localization.Tests.Integration/Tests/PathResolverTest.cs new file mode 100644 index 000000000..13d263956 --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Tests/PathResolverTest.cs @@ -0,0 +1,118 @@ +using System.Globalization; +using System.IO; +using Util.Helpers; +using Util.Localization.Json; +using Util.Localization.Resources.ResourceTypes; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Localization.Tests; + +/// +/// 路径解析器测试 +/// +public class PathResolverTest { + /// + /// 路径解析器 + /// + private readonly PathResolver _resolver; + /// + /// 输出消息 + /// + private readonly ITestOutputHelper _output; + + /// + /// 初始化路径解析器 + /// + public PathResolverTest( ITestOutputHelper output ) { + _output = output; + _resolver = new PathResolver(); + } + + /// + /// 测试获取根命名空间 - 未设置RootNamespace特性,返回程序集名称 + /// + [Fact] + public void TestGetRootNamespace_AssemblyName() { + var type = typeof(Resource101); + Assert.Equal( "Util.Localization.Resources", _resolver.GetRootNamespace( type.Assembly )); + } + + /// + /// 测试获取根命名空间 - 设置RootNamespace特性,返回特性值 + /// + [Fact] + public void TestGetRootNamespace_RootNamespace() { + var type = typeof( PathResolverTest ); + Assert.Equal( "Util.Localization", _resolver.GetRootNamespace( type.Assembly ) ); + } + + /// + /// 测试获取资源根目录路径 - 未设置ResourceLocation特性,返回传入的资源根路径 + /// + [Fact] + public void TestGetResourcesRootPath_ResourcesPath() { + var type = typeof( PathResolverTest ); + Assert.Equal( "Resources", _resolver.GetResourcesRootPath( type.Assembly, "Resources" ) ); + } + + /// + /// 测试获取资源根目录路径 - 设置ResourceLocation特性,返回特性值 + /// + [Fact] + public void TestGetResourcesRootPath_ResourceLocation() { + var type = typeof( Resource101 ); + Assert.Equal( "Resources2", _resolver.GetResourcesRootPath( type.Assembly, "Resources" ) ); + } + + /// + /// 测试获取资源基名称 - 未设置RootNamespace特性 + /// + [Fact] + public void TestGetResourcesBaseName_1() { + var type = typeof( Resource101 ); + Assert.Equal( "ResourceTypes.Resource101", _resolver.GetResourcesBaseName( type.Assembly, type.FullName ) ); + } + + /// + /// 测试获取资源基名称 - 设置RootNamespace特性 + /// + [Fact] + public void TestGetResourcesBaseName_2() { + var type = typeof( PathResolverTest ); + Assert.Equal( "Tests.PathResolverTest", _resolver.GetResourcesBaseName( type.Assembly, type.FullName ) ); + } + + /// + /// 测试获取Json资源文件绝对路径 + /// + [Fact] + public void TestGetJsonResourcePath() { + var result = Path.Combine( Common.ApplicationBaseDirectory, "Resources", "Tests.PathResolverTest.zh-CN.json" ); + var path = _resolver.GetJsonResourcePath( "Resources", "Tests.PathResolverTest", new CultureInfo( "zh-CN" ) ); + _output.WriteLine( path ); + Assert.Equal( result,path ); + } + + /// + /// 测试获取Json资源文件绝对路径 - 内部类带+ + /// + [Fact] + public void TestGetJsonResourcePath_InnerClass() { + var result = Path.Combine( Common.ApplicationBaseDirectory, "Resources", "ResourceTypes.Resource4.Resource41.zh-CN.json" ); + var path = _resolver.GetJsonResourcePath( "Resources", "ResourceTypes.Resource4+Resource41", new CultureInfo( "zh-CN" ) ); + _output.WriteLine( path ); + Assert.Equal( result, path ); + } + + /// + /// 测试获取Json资源文件绝对路径 - 基名称为空 + /// + [Fact] + public void TestGetJsonResourcePath_NullBaseName() { + var result = Path.Combine( Common.ApplicationBaseDirectory, "Resources", "zh-CN.json" ); + var path = _resolver.GetJsonResourcePath( "Resources", null, new CultureInfo( "zh-CN" ) ); + _output.WriteLine( path ); + Assert.Equal( result, path ); + } +} \ No newline at end of file diff --git a/test/Util.Localization.Tests.Integration/Tests/StringLocalizerTest.cs b/test/Util.Localization.Tests.Integration/Tests/StringLocalizerTest.cs new file mode 100644 index 000000000..c6150231d --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Tests/StringLocalizerTest.cs @@ -0,0 +1,254 @@ +using System.Globalization; +using System.Linq; +using Microsoft.Extensions.Localization; +using Util.Localization.Resources.ResourceTypes; +using Util.Localization.ResourceTypes; +using Xunit; + +namespace Util.Localization.Tests; + +/// +/// Json字符串定位器测试 +/// +public class StringLocalizerTest { + /// + /// Json资源文件字符串定位器1 + /// + private readonly IStringLocalizer _resource1Localizer; + /// + /// Json资源文件字符串定位器101 + /// + private readonly IStringLocalizer _resource101Localizer; + /// + /// Json资源文件字符串定位器2 + /// + private readonly IStringLocalizer _resource2Localizer; + /// + /// Json资源文件字符串定位器102 + /// + private readonly IStringLocalizer _resource102Localizer; + /// + /// Json资源文件字符串定位器3 + /// + private readonly IStringLocalizer _resource3Localizer; + /// + /// Json资源文件字符串定位器4 + /// + private readonly IStringLocalizer _resource4Localizer; + /// + /// Json资源文件字符串定位器41 + /// + private readonly IStringLocalizer _resource41Localizer; + /// + /// Json资源文件字符串定位器5 + /// + private readonly IStringLocalizer _resource5Localizer; + /// + /// Json资源文件字符串定位器 - 非泛型 + /// + private readonly IStringLocalizer _localizer; + /// + /// Json资源文件字符串定位器6 + /// + private readonly IStringLocalizer _resource6Localizer; + + /// + /// 测试初始化 + /// + public StringLocalizerTest( IStringLocalizer resource1Localizer, IStringLocalizer resource101Localizer, + IStringLocalizer resource2Localizer, IStringLocalizer resource102Localizer, + IStringLocalizer resource3Localizer, IStringLocalizer resource4Localizer, + IStringLocalizer resource41Localizer, IStringLocalizer resource5Localizer, + IStringLocalizer localizer, IStringLocalizer resource6Localizer ) { + _resource1Localizer = resource1Localizer; + _resource101Localizer = resource101Localizer; + _resource2Localizer = resource2Localizer; + _resource102Localizer = resource102Localizer; + _resource3Localizer = resource3Localizer; + _resource4Localizer = resource4Localizer; + _resource41Localizer = resource41Localizer; + _resource5Localizer = resource5Localizer; + _localizer = localizer; + _resource6Localizer = resource6Localizer; + } + + /// + /// 测试获取本地化字符串值 - 读取 Resources\ResourceTypes.Resource1.en-US.json + /// + [Fact] + public void Test_1() { + CultureInfo.CurrentUICulture = new CultureInfo( "en-US" ); + var value = _resource1Localizer["Hello"]; + Assert.Equal( "hi", value ); + } + + /// + /// 测试获取本地化字符串值 - 读取 Resources\ResourceTypes.Resource1.zh-CN.json + /// + [Fact] + public void Test_2() { + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + var value = _resource1Localizer["Hello"]; + Assert.Equal( "哈楼", value ); + } + + /// + /// 测试获取本地化字符串值 - 读取 Resources2\ResourceTypes.Resource101.en-US.json + /// + [Fact] + public void Test_3() { + CultureInfo.CurrentUICulture = new CultureInfo( "en-US" ); + var value = _resource101Localizer["Hello"]; + Assert.Equal( "hi", value ); + } + + /// + /// 测试获取本地化字符串值 - 读取 Resources2\ResourceTypes.Resource101.zh-CN.json + /// + [Fact] + public void Test_4() { + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + var value = _resource101Localizer["Hello"]; + Assert.Equal( "哈楼", value ); + } + + /// + /// 测试获取本地化字符串值 - 读取 Resources\ResourceTypes\Resource2.en-US.json + /// + [Fact] + public void Test_5() { + CultureInfo.CurrentUICulture = new CultureInfo( "en-US" ); + var value = _resource2Localizer["Hello"]; + Assert.Equal( "hi", value ); + } + + /// + /// 测试获取本地化字符串值 - 读取 Resources\ResourceTypes\Resource2.zh-CN.json + /// + [Fact] + public void Test_6() { + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + var value = _resource2Localizer["Hello"]; + Assert.Equal( "哈楼", value ); + } + + /// + /// 测试获取本地化字符串值 - 读取 Resources2\ResourceTypes\Resource102.zh-CN.json + /// + [Fact] + public void Test_7() { + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + var value = _resource102Localizer["Hello"]; + Assert.Equal( "哈罗", value ); + } + + /// + /// 测试获取本地化字符串值 - 读取 Resources\ResourceTypes\Resource3.zh-Hans.json + /// + [Fact] + public void Test_8() { + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + var value = _resource3Localizer["Hello"]; + Assert.Equal( "哈哈", value ); + } + + /// + /// 测试获取本地化字符串值 - 读取 Resources\ResourceTypes\Resource4.zh.json + /// + [Fact] + public void Test_9() { + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + var value = _resource4Localizer["Hello"]; + Assert.Equal( "嘿嘿", value ); + } + + /// + /// 测试获取本地化字符串值 - 读取 Resources\ResourceTypes\Resource4.Resource41.zh-CN.json + /// + [Fact] + public void Test_10() { + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + var value = _resource41Localizer["Hello"]; + Assert.Equal( "嘻嘻", value ); + } + + /// + /// 测试获取本地化字符串值 - 带参数 - 读取 Resources\ResourceTypes\Resource2.zh-CN.json + /// + [Fact] + public void Test_11() { + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + var value = _resource2Localizer["Hello,{0},{1},World", "你", "我"]; + Assert.Equal( "哈楼,你,我,世界", value ); + } + + /// + /// 测试获取本地化字符串值 - 读取 Resources\zh.json + /// + [Fact] + public void Test_12() { + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + var value = _localizer["Hello"]; + Assert.Equal( "呜呜", value ); + } + + /// + /// 测试获取本地化字符串值 - 资源6未设置, 读取 Resources\zh.json + /// + [Fact] + public void Test_13() { + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + var value = _resource6Localizer["Hello"]; + Assert.Equal( "呜呜", value ); + } + + /// + /// 测试获取本地化字符串值 - 未找到返回名称 + /// + [Fact] + public void Test_14() { + CultureInfo.CurrentUICulture = new CultureInfo( "en-US" ); + var value = _resource1Localizer["HelloWorld2"]; + Assert.Equal( "HelloWorld2", value ); + } + + /// + /// 测试获取本地化字符串值 - 性能测试 + /// + [Fact] + public void Test_15() { + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + for ( int i = 0; i < 10000; i++ ) { + var value = _localizer["Hello"]; + Assert.Equal( "呜呜", value ); + } + } + + /// + /// 测试获取全部本地化字符串 - 不包含父区域 + /// + [Fact] + public void TestGetAllStrings_1() { + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + var value = _resource5Localizer.GetAllStrings( false ).ToList(); + Assert.Equal( 2, value.Count ); + var resource = value.Find( t => t.Name == "Hello" ); + Assert.Equal( "哈楼", resource?.Value ); + Assert.Equal( "Resources.ResourceTypes.Resource5", resource?.SearchedLocation ); + Assert.False( resource?.ResourceNotFound ); + } + + /// + /// 测试获取全部本地化字符串 - 包含父区域 + /// + [Fact] + public void TestGetAllStrings_2() { + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + var value = _resource5Localizer.GetAllStrings( true ).ToList(); + Assert.Equal( 6, value.Count ); + var resource = value.Find( t => t.Name == "Hello3" ); + Assert.Equal( "哈楼3", resource?.Value ); + Assert.Equal( "Resources.ResourceTypes.Resource5", resource?.SearchedLocation ); + Assert.False( resource?.ResourceNotFound ); + } +} \ No newline at end of file diff --git a/test/Util.Localization.Tests.Integration/Util.Localization.Tests.Integration.csproj b/test/Util.Localization.Tests.Integration/Util.Localization.Tests.Integration.csproj new file mode 100644 index 000000000..140af9dd2 --- /dev/null +++ b/test/Util.Localization.Tests.Integration/Util.Localization.Tests.Integration.csproj @@ -0,0 +1,33 @@ + + + + $(NetTargetFramework) + false + Util.Localization + Util.Localization.Startup + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + \ No newline at end of file diff --git a/test/Util.Localization.Tests/Samples/TestType.cs b/test/Util.Localization.Tests/Samples/TestType.cs new file mode 100644 index 000000000..616364492 --- /dev/null +++ b/test/Util.Localization.Tests/Samples/TestType.cs @@ -0,0 +1,14 @@ +namespace Util.Localization.Store.Samples; + +/// +/// 测试类型 +/// +public class TestType { +} + +/// +/// 测试类型2 +/// +[LocalizedType("test2")] +public class TestType2 { +} \ No newline at end of file diff --git a/test/Util.Localization.Tests/Tests/LocalizedManagerTest.cs b/test/Util.Localization.Tests/Tests/LocalizedManagerTest.cs new file mode 100644 index 000000000..903bbec54 --- /dev/null +++ b/test/Util.Localization.Tests/Tests/LocalizedManagerTest.cs @@ -0,0 +1,127 @@ +namespace Util.Localization.Store.Tests; + +public class LocalizedManagerTest { + /// + /// 模拟缓存 + /// + private readonly Mock _mockMemoryCache; + /// + /// 模拟本地化资源存储器 + /// + private readonly Mock _mockLocalizedStore; + /// + /// 本地化资源管理器 + /// + private readonly LocalizedManager _manager; + + /// + /// 测试初始化 + /// + public LocalizedManagerTest() { + _mockMemoryCache = new Mock(); + var mockCacheEntry = new Mock(); + _mockMemoryCache.Setup( t => t.CreateEntry( It.IsAny() ) ).Returns( mockCacheEntry.Object ); + _mockLocalizedStore = new Mock(); + _mockLocalizedStore.Setup( t => t.GetTypes() ).Returns( new List() ); + var mockOptions = new Mock>(); + _manager = new LocalizedManager( _mockLocalizedStore.Object, _mockMemoryCache.Object, mockOptions.Object ); + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + } + + /// + /// 测试加载全部资源 + /// + [Fact] + public void TestLoadAllResources_1() { + //设置返回资源 + var resources = new Dictionary { { "name", "名称" } }; + _mockLocalizedStore.Setup( t => t.GetResources( "zh-CN", null ) ).Returns( resources ); + + //执行 + _manager.LoadAllResources(); + + //验证 + _mockMemoryCache.Verify( t => t.CreateEntry( "zh-CN--name" ) ); + } + + /// + /// 测试加载全部资源 - 本地化配置设置区域文化 - 多个区域文化 + /// + [Fact] + public void TestLoadAllResources_2() { + //设置区域文化 + _mockLocalizedStore.Setup( t => t.GetCultures() ).Returns( new []{ "en-US" } ); + + //设置返回资源 + var resources = new Dictionary { { "name", "名称" } }; + _mockLocalizedStore.Setup( t => t.GetResources( "zh-CN", null ) ).Returns( resources ); + var resources2 = new Dictionary { { "name", "Name2" } }; + _mockLocalizedStore.Setup( t => t.GetResources( "en-US", null ) ).Returns( resources2 ); + + //执行 + _manager.LoadAllResources(); + + //验证 + _mockMemoryCache.Verify( t => t.CreateEntry( "zh-CN--name" ) ); + _mockMemoryCache.Verify( t => t.CreateEntry( "en-US--name" ) ); + } + + /// + /// 测试加载全部资源 - 1个资源类型 + /// + [Fact] + public void TestLoadAllResources_3() { + //设置区域文化 + _mockLocalizedStore.Setup( t => t.GetCultures() ).Returns( new[] { "en-US" } ); + + //设置资源类型 + _mockLocalizedStore.Setup( t => t.GetTypes() ).Returns( new List { "type1" } ); + + //设置返回资源 + var resources = new Dictionary { { "name", "名称" } }; + _mockLocalizedStore.Setup( t => t.GetResources( "zh-CN", "type1" ) ).Returns( resources ); + var resources2 = new Dictionary { { "name", "Name2" } }; + _mockLocalizedStore.Setup( t => t.GetResources( "en-US", "type1" ) ).Returns( resources2 ); + + //执行 + _manager.LoadAllResources(); + + //验证 + _mockMemoryCache.Verify( t => t.CreateEntry( "zh-CN-type1-name" ) ); + _mockMemoryCache.Verify( t => t.CreateEntry( "en-US-type1-name" ) ); + } + + /// + /// 测试加载全部资源 - 2个资源类型 + /// + [Fact] + public void TestLoadAllResources_4() { + //设置区域文化 + _mockLocalizedStore.Setup( t => t.GetCultures() ).Returns( new[] { "en-US" } ); + + //设置资源类型 + _mockLocalizedStore.Setup( t => t.GetTypes() ).Returns( new List { "type1", "type2" } ); + + //设置返回资源 + var resources = new Dictionary { { "name", "名称" } }; + _mockLocalizedStore.Setup( t => t.GetResources( "zh-CN", "type1" ) ).Returns( resources ); + + var resources2 = new Dictionary { { "name", "名称2" } }; + _mockLocalizedStore.Setup( t => t.GetResources( "zh-CN", "type2" ) ).Returns( resources2 ); + + var resources3 = new Dictionary { { "name", "Name3" } }; + _mockLocalizedStore.Setup( t => t.GetResources( "en-US", "type1" ) ).Returns( resources3 ); + + var resources4 = new Dictionary { { "name", "Name4" } }; + _mockLocalizedStore.Setup( t => t.GetResources( "en-US", "type2" ) ).Returns( resources4 ); + + //执行 + _manager.LoadAllResources(); + + //验证 + _mockMemoryCache.Verify( t => t.CreateEntry( "zh-CN-type1-name" ) ); + _mockMemoryCache.Verify( t => t.CreateEntry( "zh-CN-type2-name" ) ); + _mockMemoryCache.Verify( t => t.CreateEntry( "en-US-type1-name" ) ); + _mockMemoryCache.Verify( t => t.CreateEntry( "en-US-type2-name" ) ); + } +} \ No newline at end of file diff --git a/test/Util.Localization.Tests/Tests/StoreStringLocalizerFactoryTest.cs b/test/Util.Localization.Tests/Tests/StoreStringLocalizerFactoryTest.cs new file mode 100644 index 000000000..e19f89005 --- /dev/null +++ b/test/Util.Localization.Tests/Tests/StoreStringLocalizerFactoryTest.cs @@ -0,0 +1,61 @@ +using Util.Localization.Store.Samples; + +namespace Util.Localization.Store.Tests; + +/// +/// 数据存储本地化资源查找器工厂测试 +/// +public class StoreStringLocalizerFactoryTest { + /// + /// 模拟本地化资源存储器 + /// + private readonly Mock _mockLocalizedStore; + /// + /// 本地化资源查找器工厂 + /// + private readonly IStringLocalizerFactory _localizerFactory; + + /// + /// 测试初始化 + /// + public StoreStringLocalizerFactoryTest() { + var mockOptions = new Mock>(); + var mockLoggerFactory = new Mock(); + mockLoggerFactory.Setup( t => t.CreateLogger( "Util.Localization.Store.StoreStringLocalizer" ) ).Returns( NullLogger.Instance ); + _mockLocalizedStore = new Mock(); + var mockMemoryCache = new Mock(); + var mockCacheEntry = new Mock(); + mockMemoryCache.Setup( t => t.CreateEntry( It.IsAny() ) ).Returns( mockCacheEntry.Object ); + _localizerFactory = new StoreStringLocalizerFactory( mockOptions.Object,mockLoggerFactory.Object, _mockLocalizedStore.Object, mockMemoryCache.Object ); + } + + /// + /// 测试创建本地化资源查找器 - IStringLocalizer泛型版本返回类型 + /// + [Fact] + public void TestCreate_1() { + var localizer = _localizerFactory.Create( typeof( TestType ) ); + var result = localizer["a"]; + _mockLocalizedStore.Verify( t => t.GetValue( It.IsAny(), "TestType", It.IsAny() ) ); + } + + /// + /// 测试创建本地化资源查找器 - IStringLocalizer泛型版本返回类型 - LocalizedType特性 + /// + [Fact] + public void TestCreate_2() { + var localizer = _localizerFactory.Create( typeof( TestType2 ) ); + var result = localizer["a"]; + _mockLocalizedStore.Verify( t => t.GetValue( It.IsAny(), "test2", It.IsAny() ) ); + } + + /// + /// 测试创建本地化资源查找器 - 类型字符串 + /// + [Fact] + public void TestCreate_3() { + var localizer = _localizerFactory.Create( "type","" ); + var result = localizer["a"]; + _mockLocalizedStore.Verify( t => t.GetValue( It.IsAny(), "type", It.IsAny() ) ); + } +} \ No newline at end of file diff --git a/test/Util.Localization.Tests/Tests/StoreStringLocalizerTest.cs b/test/Util.Localization.Tests/Tests/StoreStringLocalizerTest.cs new file mode 100644 index 000000000..e1bb59ad9 --- /dev/null +++ b/test/Util.Localization.Tests/Tests/StoreStringLocalizerTest.cs @@ -0,0 +1,166 @@ +namespace Util.Localization.Store.Tests; + +/// +/// 数据存储本地化资源查找器测试 +/// +public class StoreStringLocalizerTest { + /// + /// 模拟缓存 + /// + private readonly Mock _mockMemoryCache; + /// + /// 模拟本地化资源存储器 + /// + private readonly Mock _mockLocalizedStore; + /// + /// 模拟本地化配置 + /// + private Mock> _mockOptions; + /// + /// 本地化资源查找器 + /// + private IStringLocalizer _localizer; + + /// + /// 测试初始化 + /// + public StoreStringLocalizerTest() { + _mockOptions = new Mock>(); + _mockMemoryCache = new Mock(); + var mockCacheEntry = new Mock(); + _mockMemoryCache.Setup( t => t.CreateEntry( It.IsAny() ) ).Returns( mockCacheEntry.Object ); + _mockLocalizedStore = new Mock(); + _localizer = new StoreStringLocalizer( NullLogger.Instance, _mockMemoryCache.Object, _mockLocalizedStore.Object, null, _mockOptions.Object ); + CultureInfo.CurrentUICulture = new CultureInfo( "zh-CN" ); + } + + /// + /// 测试获取本地化值 - 未找到本地化值,返回key + /// + [Fact] + public void Test_1() { + var result = _localizer["value"]; + Assert.Equal( "value", result ); + } + + /// + /// 测试获取本地化值 - 未找到本地化值,返回key - 带参数 + /// + [Fact] + public void Test_2() { + var result = _localizer["value_{0}", "a"]; + Assert.Equal( "value_a", result ); + } + + /// + /// 测试获取本地化值 - 从存储器获取值 + /// + [Fact] + public void Test_3() { + //设置存储器获取资源 + _mockLocalizedStore.Setup( t => t.GetValue( "zh-CN", null, "name" ) ).Returns( "value" ); + + //执行 + var result = _localizer["name"]; + + //验证 + Assert.Equal( "value", result ); + } + + /// + /// 测试获取本地化值 - 从存储器获取值 - 带参数 + /// + [Fact] + public void Test_4() { + //设置存储器获取资源 + _mockLocalizedStore.Setup( t => t.GetValue( "zh-CN", null, "name_{0}" ) ).Returns( "value_{0}" ); + + //执行 + var result = _localizer["name_{0}", "a"]; + + //验证 + Assert.Equal( "value_a", result ); + } + + /// + /// 测试获取本地化值 - 从存储器获取值 - 设置资源类型 + /// + [Fact] + public void Test_5() { + //设置资源类型 + _localizer = new StoreStringLocalizer( NullLogger.Instance, _mockMemoryCache.Object, _mockLocalizedStore.Object, "type1", _mockOptions.Object ); + + //设置存储器获取资源 + _mockLocalizedStore.Setup( t => t.GetValue( "zh-CN", "type1", "name_{0}" ) ).Returns( "value_{0}" ); + + //执行 + var result = _localizer["name_{0}", "a"]; + + //验证 + Assert.Equal( "value_a", result ); + } + + /// + /// 测试获取全部本地化资源 - 包含父区域资源 + /// + [Fact] + public void TestGetAllStrings_1() { + //设置返回资源 + var resources = new Dictionary { { "name", "名称" } }; + _mockLocalizedStore.Setup( t => t.GetResources( "zh-CN", null ) ).Returns( resources ); + var resources2 = new Dictionary { { "name2", "名称2" } }; + _mockLocalizedStore.Setup( t => t.GetResources( "zh", null ) ).Returns( resources2 ); + + //执行 + var result = _localizer.GetAllStrings().ToList(); + + //验证 + Assert.Equal( 2, result.Count ); + Assert.Equal( "name", result[0].Name ); + Assert.Equal( "名称", result[0].Value ); + Assert.False( result[0].ResourceNotFound ); + } + + /// + /// 测试获取全部本地化资源 - 不包含父区域资源 + /// + [Fact] + public void TestGetAllStrings_2() { + //设置返回资源 + var resources = new Dictionary { { "name", "名称" } }; + _mockLocalizedStore.Setup( t => t.GetResources( "zh-CN", null ) ).Returns( resources ); + var resources2 = new Dictionary { { "name2", "名称2" } }; + _mockLocalizedStore.Setup( t => t.GetResources( "zh", null ) ).Returns( resources2 ); + + //执行 + var result = _localizer.GetAllStrings( false ).ToList(); + + //验证 + Assert.Single( result ); + Assert.Equal( "name", result[0].Name ); + Assert.Equal( "名称", result[0].Value ); + Assert.False( result[0].ResourceNotFound ); + } + + /// + /// 测试获取全部本地化资源 - 资源类型 + /// + [Fact] + public void TestGetAllStrings_3() { + //设置资源类型 + _mockLocalizedStore.Setup( t => t.GetTypes() ).Returns( new List { "type1", "type2" } ); + + //设置返回资源 + var resources = new Dictionary { { "name", "名称" } }; + _mockLocalizedStore.Setup( t => t.GetResources( "zh-CN", "type1" ) ).Returns( resources ); + + var resources2 = new Dictionary { { "name2", "名称2" } }; + _mockLocalizedStore.Setup( t => t.GetResources( "zh-CN", "type2" ) ).Returns( resources2 ); + + //执行 + var result = _localizer.GetAllStrings().ToList(); + + //验证 + Assert.Equal( 2, result.Count ); + } +} \ No newline at end of file diff --git a/test/Util.Localization.Tests/Usings.cs b/test/Util.Localization.Tests/Usings.cs new file mode 100644 index 000000000..e75e0f12c --- /dev/null +++ b/test/Util.Localization.Tests/Usings.cs @@ -0,0 +1,10 @@ +global using System.Globalization; +global using System.Linq; +global using System.Collections.Generic; +global using Microsoft.Extensions.Localization; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Logging.Abstractions; +global using Microsoft.Extensions.Caching.Memory; +global using Microsoft.Extensions.Options; +global using Xunit; +global using Moq; \ No newline at end of file diff --git a/test/Util.Localization.Tests/Util.Localization.Tests.csproj b/test/Util.Localization.Tests/Util.Localization.Tests.csproj new file mode 100644 index 000000000..70b30331c --- /dev/null +++ b/test/Util.Localization.Tests/Util.Localization.Tests.csproj @@ -0,0 +1,27 @@ + + + + $(NetTargetFramework) + false + Util.Localization.Store + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/LogContextAccessor.cs b/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/LogContextAccessor.cs new file mode 100644 index 000000000..d333d8fee --- /dev/null +++ b/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/LogContextAccessor.cs @@ -0,0 +1,25 @@ +namespace Util.Logging.Tests; + +/// +/// 日志上下文访问器 +/// +public class LogContextAccessor : ILogContextAccessor { + /// + /// 日志上下文键名 + /// + public const string LogContextKey = "Util.Logging.LogContext"; + + /// + /// 日志上下文 + /// + public LogContext Context { + get { + var slot = System.Threading.Thread.GetNamedDataSlot( LogContextKey ); + return Util.Helpers.Convert.To( System.Threading.Thread.GetData( slot ) ); + } + set { + var slot = System.Threading.Thread.GetNamedDataSlot( LogContextKey ); + System.Threading.Thread.SetData( slot, value ); + } + } +} \ No newline at end of file diff --git a/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/LogTest.cs b/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/LogTest.cs new file mode 100644 index 000000000..c57d21e7f --- /dev/null +++ b/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/LogTest.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Util.Helpers; +using Util.Logging.Tests.Samples; +using Xunit; + +namespace Util.Logging.Tests; + +/// +/// ExceptionLess־ +/// +public class LogTest { + /// + /// ־ + /// + private readonly ILog _log; + + /// + /// Գʼ + /// + public LogTest( IServiceProvider serviceProvider, ILog log, ILogContextAccessor accessor ) { + Ioc.SetServiceProviderAction( () => serviceProvider ); + _log = log; + accessor.Context = new LogContext { Stopwatch = Stopwatch.StartNew(), TraceId = Id.Create() }; + } + + /// + /// д־ - üϢ + /// + [Fact] + public void TestLogTrace_Message_1() { + _log.Message( $"TestTrace_Message_1_{Id.Create()}" ).LogTrace(); + } + + /// + /// д־ - ־Ϣ1 + /// + [Fact] + public void TestLogTrace_Message_2() { + _log.Message( "TestTrace_Message_2_{ProductCode}",Id.Create() ).LogTrace(); + } + + /// + /// д־ - ־Ϣ2 + /// + [Fact] + public void TestLogTrace_Message_3() { + _log.Message( "TestTrace_Message_3_{id}_{code}", $"id_{Id.Create()}", $"code_{Id.Create()}" ).LogTrace(); + } + + /// + /// д־ - εMessage־Ϣ + /// + [Fact] + public void TestLogTrace_Message_4() { + _log.Message( "TestTrace_Message_4_{id}_{code}_", $"id_{Id.Create()}", $"code_{Id.Create()}" ) + .Message( "{name}_{note}", $"name_{Id.Create()}", $"note_{Id.Create()}" ) + .LogTrace(); + } + + /// + /// д־ - @Ϣ - + /// + [Fact] + public void TestLogTrace_Message_5() { + _log.Message( "TestTrace_Message_5,@Product={@Product}", new Product{Code = "a",Name = "b",Price = 123} ) + .LogTrace(); + } + + /// + /// д־ - $Ϣ - + /// + [Fact] + public void TestLogTrace_Message_6() { + _log.Message( "TestTrace_Message_6,$Product={$Product}", new Product { Code = "a", Name = "b", Price = 123 } ) + .LogTrace(); + } + + /// + /// д־ - @Ϣ - ֵ + /// + [Fact] + public void TestLogTrace_Message_7() { + _log.Message( "TestTrace_Message_7,@Dictionary={@Dictionary}", new Dictionary { {"Code","a"},{"Name","b"},{"Price",123} } ) + .LogTrace(); + } + + /// + /// д־ - $Ϣ - ֵ + /// + [Fact] + public void TestLogTrace_Message_8() { + _log.Message( "TestTrace_Message_8,$Dictionary={$Dictionary}", new Dictionary { { "Code", "a" }, { "Name", "b" }, { "Price", 123 } } ) + .LogTrace(); + } + + /// + /// д־ - ͬʱø + /// + [Fact] + public void TestLogTrace_Message_9() { + _log.EventId( 119 ) + .Message( "TestTrace_Message_9_{id}", $"id_{Id.Create()}" ) + .State( new Product { Code = "a", Name = "b", Price = 123 } ) + .Property( "Age", "18" ) + .Property( "Description", "hello" ) + .LogTrace(); + } + + /// + /// д־ - Զչ + /// + [Fact] + public void TestLogTrace_Property_1() { + _log.EventId( 120 ) + .Property( "Age", "18" ) + .Property( "Description", "hello" ) + .LogTrace(); + } + + /// + /// д־ - ԶչԺ״̬ + /// + [Fact] + public void TestLogTrace_Property_2() { + _log.EventId( 121 ) + .Property( "Age", "18" ) + .Property( "Description", "hello" ) + .State( new Product { Code = "a", Name = "b", Price = 123 } ) + .LogTrace(); + } +} \ No newline at end of file diff --git a/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/Samples/Product.cs b/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/Samples/Product.cs new file mode 100644 index 000000000..678f27626 --- /dev/null +++ b/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/Samples/Product.cs @@ -0,0 +1,19 @@ +namespace Util.Logging.Tests.Samples; + +/// +/// 产品 +/// +public class Product { + /// + /// 产品编码 + /// + public string Code { get; set; } + /// + /// 产品名称 + /// + public string Name { get; set; } + /// + /// 价格 + /// + public decimal Price { get; set; } +} \ No newline at end of file diff --git a/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/Startup.cs b/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/Startup.cs new file mode 100644 index 000000000..cf66d6247 --- /dev/null +++ b/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/Startup.cs @@ -0,0 +1,33 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Helpers; +using Util.Logging.Serilog; +using Xunit.DependencyInjection.Logging; + +namespace Util.Logging.Tests; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + hostBuilder.ConfigureDefaults( null ) + .ConfigureHostConfiguration( builder => { + Environment.SetDevelopment(); + } ) + .AsBuild() + .AddExceptionless() + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddSingleton(); + } +} \ No newline at end of file diff --git a/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/Util.Logging.Serilog.Exceptionless.Tests.Integration.csproj b/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/Util.Logging.Serilog.Exceptionless.Tests.Integration.csproj new file mode 100644 index 000000000..0bf7410eb --- /dev/null +++ b/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/Util.Logging.Serilog.Exceptionless.Tests.Integration.csproj @@ -0,0 +1,40 @@ + + + + $(NetTargetFramework) + false + Util.Logging.Tests + Util.Logging.Tests.Startup + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + Always + + + Always + + + + diff --git a/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/appsettings.Production.json b/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/appsettings.Production.json new file mode 100644 index 000000000..19573b5ea --- /dev/null +++ b/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/appsettings.Production.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Warning" + } + }, + "Exceptionless": { + "ApiKey": "0yYDCrbsgdGZX7PImygwk06KMTf5dNI5opsrKA6x", + "ServerUrl": "http://exceptionless.common/", + "QueueMaxAttempts": 1 + } +} \ No newline at end of file diff --git a/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/appsettings.json b/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/appsettings.json new file mode 100644 index 000000000..dbebfc878 --- /dev/null +++ b/test/Util.Logging.Serilog.Exceptionless.Tests.Integration/appsettings.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Warning" + } + }, + "Exceptionless": { + "ApiKey": "iopUwmIbUdFKQs1WnUzxaPKg8lvJ02rxXjxHuhTg", + "ServerUrl": "http://localhost:5480", + "QueueMaxAttempts": 1 + } +} \ No newline at end of file diff --git a/test/Util.Logging.Serilog.Tests.Integration/LogContextAccessor.cs b/test/Util.Logging.Serilog.Tests.Integration/LogContextAccessor.cs new file mode 100644 index 000000000..d333d8fee --- /dev/null +++ b/test/Util.Logging.Serilog.Tests.Integration/LogContextAccessor.cs @@ -0,0 +1,25 @@ +namespace Util.Logging.Tests; + +/// +/// 日志上下文访问器 +/// +public class LogContextAccessor : ILogContextAccessor { + /// + /// 日志上下文键名 + /// + public const string LogContextKey = "Util.Logging.LogContext"; + + /// + /// 日志上下文 + /// + public LogContext Context { + get { + var slot = System.Threading.Thread.GetNamedDataSlot( LogContextKey ); + return Util.Helpers.Convert.To( System.Threading.Thread.GetData( slot ) ); + } + set { + var slot = System.Threading.Thread.GetNamedDataSlot( LogContextKey ); + System.Threading.Thread.SetData( slot, value ); + } + } +} \ No newline at end of file diff --git a/test/Util.Logging.Serilog.Tests.Integration/LogTest.cs b/test/Util.Logging.Serilog.Tests.Integration/LogTest.cs new file mode 100644 index 000000000..957eaa0c0 --- /dev/null +++ b/test/Util.Logging.Serilog.Tests.Integration/LogTest.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Util.Helpers; +using Util.Logging.Tests.Samples; +using Xunit; + +namespace Util.Logging.Tests; + +/// +/// ־ +/// +public class LogTest{ + /// + /// ־ + /// + private readonly ILog _log; + + /// + /// Գʼ + /// + public LogTest( IServiceProvider serviceProvider, ILog log, ILogContextAccessor accessor ) { + Ioc.SetServiceProviderAction( () => serviceProvider ); + _log = log; + accessor.Context = new LogContext { Stopwatch = Stopwatch.StartNew(), TraceId = Id.Create() }; + } + + /// + /// д־ - üϢ + /// + [Fact] + public void TestLogTrace_Message_1() { + _log.Message( $"TestTrace_Message_1_{Id.Create()}" ).LogTrace(); + } + + /// + /// д־ - ־Ϣ1 + /// + [Fact] + public void TestLogTrace_Message_2() { + _log.Message( "TestTrace_Message_2_{ProductCode}",Id.Create() ).LogTrace(); + } + + /// + /// д־ - ־Ϣ2 + /// + [Fact] + public void TestLogTrace_Message_3() { + _log.Message( "TestTrace_Message_3_{id}_{code}", $"id_{Id.Create()}", $"code_{Id.Create()}" ).LogTrace(); + + } + + /// + /// д־ - εMessage־Ϣ + /// + [Fact] + public void TestLogTrace_Message_4() { + _log.Message( "TestTrace_Message_4_{id}_{code}_", $"id_{Id.Create()}", $"code_{Id.Create()}" ) + .Message( "{name}_{note}", $"name_{Id.Create()}", $"note_{Id.Create()}" ) + .LogTrace(); + } + + /// + /// д־ - @Ϣ - + /// + [Fact] + public void TestLogTrace_Message_5() { + _log.Message( "TestTrace_Message_5,@Product={@Product}", new Product{Code = "a",Name = "b",Price = 123} ) + .LogTrace(); + } + + /// + /// д־ - $Ϣ - + /// + [Fact] + public void TestLogTrace_Message_6() { + _log.Message( "TestTrace_Message_6,$Product={$Product}", new Product { Code = "a", Name = "b", Price = 123 } ) + .LogTrace(); + } + + /// + /// д־ - @Ϣ - ֵ + /// + [Fact] + public void TestLogTrace_Message_7() { + _log.Message( "TestTrace_Message_7,@Dictionary={@Dictionary}", new Dictionary { {"Code","a"},{"Name","b"},{"Price",123} } ) + .LogTrace(); + } + + /// + /// д־ - $Ϣ - ֵ + /// + [Fact] + public void TestLogTrace_Message_8() { + _log.Message( "TestTrace_Message_8,$Dictionary={$Dictionary}", new Dictionary { { "Code", "a" }, { "Name", "b" }, { "Price", 123 } } ) + .LogTrace(); + } + + /// + /// д־ - ͬʱø + /// + [Fact] + public void TestLogTrace_Message_9() { + _log.EventId( 119 ) + .Message( "TestTrace_Message_9_{id}", $"id_{Id.Create()}" ) + .State( new Product { Code = "a", Name = "b", Price = 123 } ) + .Property( "Age", "18" ) + .Property( "Description", "hello" ) + .LogTrace(); + } + + /// + /// д־ - Զչ + /// + [Fact] + public void TestLogTrace_Property_1() { + _log.EventId( 120 ) + .Property( "Age", "18" ) + .Property( "Description", "hello" ) + .LogTrace(); + } + + /// + /// д־ - ԶչԺ״̬ + /// + [Fact] + public void TestLogTrace_Property_2() { + _log.EventId( 121 ) + .Property( "Age", "18" ) + .Property( "Description", "hello" ) + .State( new Product { Code = "a", Name = "b", Price = 123 } ) + .LogTrace(); + } +} \ No newline at end of file diff --git a/test/Util.Logging.Serilog.Tests.Integration/Samples/Product.cs b/test/Util.Logging.Serilog.Tests.Integration/Samples/Product.cs new file mode 100644 index 000000000..678f27626 --- /dev/null +++ b/test/Util.Logging.Serilog.Tests.Integration/Samples/Product.cs @@ -0,0 +1,19 @@ +namespace Util.Logging.Tests.Samples; + +/// +/// 产品 +/// +public class Product { + /// + /// 产品编码 + /// + public string Code { get; set; } + /// + /// 产品名称 + /// + public string Name { get; set; } + /// + /// 价格 + /// + public decimal Price { get; set; } +} \ No newline at end of file diff --git a/test/Util.Logging.Serilog.Tests.Integration/Startup.cs b/test/Util.Logging.Serilog.Tests.Integration/Startup.cs new file mode 100644 index 000000000..c9c27523f --- /dev/null +++ b/test/Util.Logging.Serilog.Tests.Integration/Startup.cs @@ -0,0 +1,40 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Logging.Serilog; +using Xunit.DependencyInjection.Logging; +using serilog = Serilog; + +namespace Util.Logging.Tests; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; + hostBuilder.ConfigureDefaults( null ) + .AsBuild() + .AddSerilog() + .AddUtil(); + } + + /// + /// ˳ʱͷ־ʵ,ڽSeq޷д + /// + private void CurrentDomain_ProcessExit( object sender, EventArgs e ) { + var log = (serilog.Core.Logger)serilog.Log.Logger; + log.Dispose(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddSingleton(); + } +} \ No newline at end of file diff --git a/test/Util.Logging.Serilog.Tests.Integration/Util.Logging.Serilog.Tests.Integration.csproj b/test/Util.Logging.Serilog.Tests.Integration/Util.Logging.Serilog.Tests.Integration.csproj new file mode 100644 index 000000000..214753e27 --- /dev/null +++ b/test/Util.Logging.Serilog.Tests.Integration/Util.Logging.Serilog.Tests.Integration.csproj @@ -0,0 +1,37 @@ + + + + $(NetTargetFramework) + false + Util.Logging.Tests + Util.Logging.Tests.Startup + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + Always + + + + diff --git a/test/Util.Logging.Serilog.Tests.Integration/appsettings.json b/test/Util.Logging.Serilog.Tests.Integration/appsettings.json new file mode 100644 index 000000000..a5fda7fd9 --- /dev/null +++ b/test/Util.Logging.Serilog.Tests.Integration/appsettings.json @@ -0,0 +1,29 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Error" + } + }, + "Serilog": { + "WriteTo": [ + { + "Name": "File", + "Args": { + "path": "c:\\log.serilog.test\\log.txt", + "rollingInterval": "Day", + "rollOnFileSizeLimit": true, + "fileSizeLimitBytes": 102400, + "retainedFileCountLimit": 10, + "outputTemplate": "{LogLevel}: {TraceId} >> [{SourceContext}] [{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] [{Duration}]{NewLine}{Message}{NewLine}{Exception}" + } + }, + { + "Name": "Seq", + "Args": { + "serverUrl": "http://localhost:5380" + } + } + ] + } +} \ No newline at end of file diff --git a/test/Util.Logging.Tests/LogTest.Critical .cs b/test/Util.Logging.Tests/LogTest.Critical .cs new file mode 100644 index 000000000..e9608166b --- /dev/null +++ b/test/Util.Logging.Tests/LogTest.Critical .cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Logging; +using Moq; +using Util.Logging.Tests.Samples; +using Xunit; + +namespace Util.Logging.Tests; + +/// +/// ־ - +/// +public partial class LogTest { + /// + /// д־ - ͬʱԶչ,״̬,־Ϣ + /// + [Fact] + public void TestLogCritical_Message() { + var product = new Product { Code = "a", Name = "b", Price = 123 }; + _log.Message( "a{b}{c}", 1, 2 ) + .Property( "d", "3" ) + .Property( "e", "4" ) + .State( product ) + .LogCritical(); + _mockLogger.Verify( t => t.LogCritical( 0, null, "[d:{d},e:{e},Code:{Code},Name:{Name},Price:{Price}]a{b}{c}", "3", "4", "a", "b", 123, 1, 2 ) ); + } + + /// + /// д־ - ԶչԺ״̬ + /// + [Fact] + public void TestLogCritical_Property() { + var product = new Product { Code = "a", Name = "b", Price = 123 }; + _log.Property( "Age", "18" ).State( product ).LogCritical(); + _mockLogger.Verify( t => t.Log( LogLevel.Critical, 0, + It.Is>( dic => dic.Count == 4 && dic["Age"].ToString() == "18" && dic["Name"].ToString() == "b" ), + null, + It.IsAny, Exception, string>>() ) ); + } +} \ No newline at end of file diff --git a/test/Util.Logging.Tests/LogTest.Debug .cs b/test/Util.Logging.Tests/LogTest.Debug .cs new file mode 100644 index 000000000..101b0437a --- /dev/null +++ b/test/Util.Logging.Tests/LogTest.Debug .cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Logging; +using Moq; +using Util.Logging.Tests.Samples; +using Xunit; + +namespace Util.Logging.Tests; + +/// +/// ־ - ԵԼ +/// +public partial class LogTest { + /// + /// д־ - ͬʱԶչ,״̬,־Ϣ + /// + [Fact] + public void TestLogDebug_Message() { + var product = new Product { Code = "a", Name = "b", Price = 123 }; + _log.Message( "a{b}{c}", 1, 2 ) + .Property( "d", "3" ) + .Property( "e", "4" ) + .State( product ) + .LogDebug(); + _mockLogger.Verify( t => t.LogDebug( 0, null, "[d:{d},e:{e},Code:{Code},Name:{Name},Price:{Price}]a{b}{c}", "3", "4", "a", "b", 123, 1, 2 ) ); + } + + /// + /// д־ - ԶչԺ״̬ + /// + [Fact] + public void TestLogDebug_Property() { + var product = new Product { Code = "a", Name = "b", Price = 123 }; + _log.Property( "Age", "18" ).State( product ).LogDebug(); + _mockLogger.Verify( t => t.Log( LogLevel.Debug, 0, + It.Is>( dic => dic.Count == 4 && dic["Age"].ToString() == "18" && dic["Name"].ToString() == "b" ), + null, + It.IsAny, Exception, string>>() ) ); + } +} \ No newline at end of file diff --git a/test/Util.Logging.Tests/LogTest.Error .cs b/test/Util.Logging.Tests/LogTest.Error .cs new file mode 100644 index 000000000..fd4abcf6e --- /dev/null +++ b/test/Util.Logging.Tests/LogTest.Error .cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Logging; +using Moq; +using Util.Logging.Tests.Samples; +using Xunit; + +namespace Util.Logging.Tests; + +/// +/// ־ - Դ󼶱 +/// +public partial class LogTest { + /// + /// д־ - ͬʱԶչ,״̬,־Ϣ + /// + [Fact] + public void TestLogError_Message() { + var product = new Product { Code = "a", Name = "b", Price = 123 }; + _log.Message( "a{b}{c}", 1, 2 ) + .Property( "d", "3" ) + .Property( "e", "4" ) + .State( product ) + .LogError(); + _mockLogger.Verify( t => t.LogError( 0, null, "[d:{d},e:{e},Code:{Code},Name:{Name},Price:{Price}]a{b}{c}", "3", "4", "a", "b", 123, 1, 2 ) ); + } + + /// + /// д־ - ԶչԺ״̬ + /// + [Fact] + public void TestLogError_Property() { + var product = new Product { Code = "a", Name = "b", Price = 123 }; + _log.Property( "Age", "18" ).State( product ).LogError(); + _mockLogger.Verify( t => t.Log( LogLevel.Error, 0, + It.Is>( dic => dic.Count == 4 && dic["Age"].ToString() == "18" && dic["Name"].ToString() == "b" ), + null, + It.IsAny, Exception, string>>() ) ); + } +} \ No newline at end of file diff --git a/test/Util.Logging.Tests/LogTest.Information .cs b/test/Util.Logging.Tests/LogTest.Information .cs new file mode 100644 index 000000000..601ecb2be --- /dev/null +++ b/test/Util.Logging.Tests/LogTest.Information .cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Logging; +using Moq; +using Util.Logging.Tests.Samples; +using Xunit; + +namespace Util.Logging.Tests; + +/// +/// ־ - Ϣ +/// +public partial class LogTest { + /// + /// дϢ־ - ͬʱԶչ,״̬,־Ϣ + /// + [Fact] + public void TestLogInformation_Message() { + var product = new Product { Code = "a", Name = "b", Price = 123 }; + _log.Message( "a{b}{c}", 1, 2 ) + .Property( "d", "3" ) + .Property( "e", "4" ) + .State( product ) + .LogInformation(); + _mockLogger.Verify( t => t.LogInformation( 0, null, "[d:{d},e:{e},Code:{Code},Name:{Name},Price:{Price}]a{b}{c}", "3", "4", "a", "b", 123, 1, 2 ) ); + } + + /// + /// дϢ־ - ԶչԺ״̬ + /// + [Fact] + public void TestLogInformation_Property() { + var product = new Product { Code = "a", Name = "b", Price = 123 }; + _log.Property( "Age", "18" ).State( product ).LogInformation(); + _mockLogger.Verify( t => t.Log( LogLevel.Information, 0, + It.Is>( dic => dic.Count == 4 && dic["Age"].ToString() == "18" && dic["Name"].ToString() == "b" ), + null, + It.IsAny, Exception, string>>() ) ); + } +} \ No newline at end of file diff --git a/test/Util.Logging.Tests/LogTest.Trace .cs b/test/Util.Logging.Tests/LogTest.Trace .cs new file mode 100644 index 000000000..3e0886848 --- /dev/null +++ b/test/Util.Logging.Tests/LogTest.Trace .cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Logging; +using Moq; +using Util.Logging.Tests.Samples; +using Xunit; + +namespace Util.Logging.Tests; + +/// +/// ־ - Ըټ +/// +public partial class LogTest { + /// + /// д־ - ֤ÿϢ + /// + [Fact] + public void TestLogTrace_Message_Validate_Empty() { + _log.Message( "" ).LogTrace(); + _mockLogger.Verify( t => t.LogTrace( It.IsAny(), It.IsAny(), It.IsAny() ), Times.Never ); + } + + /// + /// д־ - üϢ + /// + [Fact] + public void TestLogTrace_Message_1() { + _log.Message( "a" ).LogTrace(); + _mockLogger.Verify( t => t.LogTrace( 0,null, "a" ) ); + } + + /// + /// д־ - ־Ϣ1 + /// + [Fact] + public void TestLogTrace_Message_2() { + _log.Message( "a{b}",1 ).LogTrace(); + _mockLogger.Verify( t => t.LogTrace( 0, null, "a{b}",1 ) ); + } + + /// + /// д־ - ־Ϣ2 + /// + [Fact] + public void TestLogTrace_Message_3() { + _log.Message( "a{b}{c}", 1,2 ).LogTrace(); + _mockLogger.Verify( t => t.LogTrace( 0, null, "a{b}{c}", 1,2 ) ); + } + + /// + /// д־ - εMessage־Ϣ + /// + [Fact] + public void TestLogTrace_Message_4() { + _log.Message( "a{b}{c}", 1, 2 ) + .Message( "e{f}{g}", 3, 4 ) + .LogTrace(); + _mockLogger.Verify( t => t.LogTrace( 0, null, "a{b}{c}e{f}{g}", 1, 2,3,4 ) ); + } + + /// + /// д־ - ͬʱԶչԺ־Ϣ + /// + [Fact] + public void TestLogTrace_Message_5() { + _log.Message( "a{b}{c}", 1, 2 ) + .Property( "d","3" ) + .Property( "e", "4" ) + .LogTrace(); + _mockLogger.Verify( t => t.LogTrace( 0, null, "[d:{d},e:{e}]a{b}{c}", "3","4", 1, 2 ) ); + } + + /// + /// д־ - ͬʱ״̬־Ϣ + /// + [Fact] + public void TestLogTrace_Message_6() { + var product = new Product {Code = "a", Name = "b", Price = 123 }; + _log.Message( "a{b}{c}", 1, 2 ) + .State( product ) + .LogTrace(); + _mockLogger.Verify( t => t.LogTrace( 0, null, "[Code:{Code},Name:{Name},Price:{Price}]a{b}{c}", "a","b",123,1,2 ) ); + } + + /// + /// д־ - ͬʱԶչ,״̬,־Ϣ + /// + [Fact] + public void TestLogTrace_Message_7() { + var product = new Product { Code = "a", Name = "b", Price = 123 }; + _log.Message( "a{b}{c}", 1, 2 ) + .Property( "d", "3" ) + .Property( "e", "4" ) + .State( product ) + .LogTrace(); + _mockLogger.Verify( t => t.LogTrace( 0, null, "[d:{d},e:{e},Code:{Code},Name:{Name},Price:{Price}]a{b}{c}", "3", "4", "a", "b", 123, 1, 2 ) ); + } + + /// + /// д־ - ͬʱEventId,쳣,־Ϣ + /// + [Fact] + public void TestLogTrace_Message_8() { + _log.EventId( 110 ) + .Exception( new Exception( "a" ) ) + .Message( "a{b}{c}", 1, 2 ) + .LogTrace(); + _mockLogger.Verify( t => t.LogTrace( 110, It.Is( e => e.Message == "a" ), "a{b}{c}", 1, 2 ) ); + } + + /// + /// д־ - 1Զչ + /// + [Fact] + public void TestLogTrace_Property_1() { + _log.Property( "a","1" ).LogTrace(); + _mockLogger.Verify( t => t.Log( LogLevel.Trace, 0, + It.Is>( dic => dic.Count == 1 && dic["a"].ToString()=="1" ), + null, + It.IsAny, Exception, string>>() ) ); + } + + /// + /// д־ - 2Զչ + /// + [Fact] + public void TestLogTrace_Property_2() { + _log.Property( "a", "1" ).Property( "b","2" ).LogTrace(); + _mockLogger.Verify( t => t.Log( LogLevel.Trace, 0, + It.Is>( dic => dic.Count == 2 && dic["a"].ToString() == "1" && dic["b"].ToString() =="2" ), + null, + It.IsAny, Exception, string>>() ) ); + } + + /// + /// д־ - ԶչԺ״̬ + /// + [Fact] + public void TestLogTrace_Property_3() { + var product = new Product { Code = "a", Name = "b", Price = 123 }; + _log.Property( "Age", "18" ).State( product ).LogTrace(); + _mockLogger.Verify( t => t.Log( LogLevel.Trace, 0, + It.Is>( dic => dic.Count == 4 && dic["Age"].ToString() == "18" && dic["Name"].ToString() == "b" ), + null, + It.IsAny, Exception, string>>() ) ); + } + + /// + /// д־ - һͨ״̬ - Կֵ + /// + [Fact] + public void TestLogTrace_Sate_1() { + var product = new Product { Code = "a" }; + _log.State( product ).LogTrace(); + _mockLogger.Verify( t => t.Log( LogLevel.Trace, 0, + It.Is>( dic => dic.Count == 2 && dic["Code"].ToString() == "a" && dic["Price"].ToString() == "0" ), + null, + It.IsAny, Exception, string>>() ) ); + } + + /// + /// д־ - һֵ״̬ - Կֵ + /// + [Fact] + public void TestLogTrace_Sate_2() { + var content = new Dictionary { + { "Code", "a" }, + { "Name", "" }, + { "Price", 0 } + }; + _log.State( content ).LogTrace(); + _mockLogger.Verify( t => t.Log( LogLevel.Trace, 0, + It.Is>( dic => dic.Count == 2 && dic["Code"].ToString() == "a" && dic["Price"].ToString() == "0" ), + null, + It.IsAny, Exception, string>>() ) ); + } +} \ No newline at end of file diff --git a/test/Util.Logging.Tests/LogTest.Warning .cs b/test/Util.Logging.Tests/LogTest.Warning .cs new file mode 100644 index 000000000..ae0c9f163 --- /dev/null +++ b/test/Util.Logging.Tests/LogTest.Warning .cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Logging; +using Moq; +using Util.Logging.Tests.Samples; +using Xunit; + +namespace Util.Logging.Tests; + +/// +/// ־ - Ծ漶 +/// +public partial class LogTest { + /// + /// д־ - ͬʱԶչ,״̬,־Ϣ + /// + [Fact] + public void TestLogWarning_Message() { + var product = new Product { Code = "a", Name = "b", Price = 123 }; + _log.Message( "a{b}{c}", 1, 2 ) + .Property( "d", "3" ) + .Property( "e", "4" ) + .State( product ) + .LogWarning(); + _mockLogger.Verify( t => t.LogWarning( 0, null, "[d:{d},e:{e},Code:{Code},Name:{Name},Price:{Price}]a{b}{c}", "3", "4", "a", "b", 123, 1, 2 ) ); + } + + /// + /// д־ - ԶչԺ״̬ + /// + [Fact] + public void TestLogWarning_Property() { + var product = new Product { Code = "a", Name = "b", Price = 123 }; + _log.Property( "Age", "18" ).State( product ).LogWarning(); + _mockLogger.Verify( t => t.Log( LogLevel.Warning, 0, + It.Is>( dic => dic.Count == 4 && dic["Age"].ToString() == "18" && dic["Name"].ToString() == "b" ), + null, + It.IsAny, Exception, string>>() ) ); + } +} \ No newline at end of file diff --git a/test/Util.Logging.Tests/LogTest.cs b/test/Util.Logging.Tests/LogTest.cs new file mode 100644 index 000000000..bc2769dc4 --- /dev/null +++ b/test/Util.Logging.Tests/LogTest.cs @@ -0,0 +1,25 @@ +using Moq; + +namespace Util.Logging.Tests; + +/// +/// ־ +/// +public partial class LogTest { + /// + /// ģ־ + /// + private readonly Mock _mockLogger; + /// + /// ־ + /// + private readonly ILog _log; + + /// + /// Գʼ + /// + public LogTest() { + _mockLogger = new Mock(); + _log = new Log( _mockLogger.Object ); + } +} \ No newline at end of file diff --git a/test/Util.Logging.Tests/Samples/Product.cs b/test/Util.Logging.Tests/Samples/Product.cs new file mode 100644 index 000000000..33f7f90d8 --- /dev/null +++ b/test/Util.Logging.Tests/Samples/Product.cs @@ -0,0 +1,19 @@ +namespace Util.Logging.Tests.Samples; + +/// +/// 产品 +/// +public class Product { + /// + /// 产品编码 + /// + public string Code { get; set; } + /// + /// 产品名称 + /// + public string Name { get; set; } + /// + /// 价格 + /// + public int Price { get; set; } +} \ No newline at end of file diff --git a/test/Util.Logging.Tests/Util.Logging.Tests.csproj b/test/Util.Logging.Tests/Util.Logging.Tests.csproj new file mode 100644 index 000000000..e73915881 --- /dev/null +++ b/test/Util.Logging.Tests/Util.Logging.Tests.csproj @@ -0,0 +1,26 @@ + + + + $(NetTargetFramework) + false + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.Microservices.Dapr.PubsubSample/Controllers/IntegrationEventController.cs b/test/Util.Microservices.Dapr.PubsubSample/Controllers/IntegrationEventController.cs new file mode 100644 index 000000000..356ed3f89 --- /dev/null +++ b/test/Util.Microservices.Dapr.PubsubSample/Controllers/IntegrationEventController.cs @@ -0,0 +1,61 @@ +using Util.Exceptions; +using Util.Microservices.Dapr.Events; +using Util.Microservices.Dapr.PubsubSample.Events; + +namespace Util.Microservices.Dapr.PubsubSample.Controllers; + +/// +/// 集成事件控制器 +/// +public class IntegrationEventController : IntegrationEventControllerBase { + /// + /// 状态管理操作 + /// + private readonly IStateManager _stateManager; + /// + /// 日志操作 + /// + private readonly ILogger _log; + + /// + /// 初始化集成事件控制器 + /// + public IntegrationEventController( IStateManager stateManager, ILogger log ) { + _stateManager = stateManager; + _log = log; + } + + /// + /// 订阅1 + /// + [HttpPost( "Pubsub_Test1" )] + [Topic( nameof( TestEvent ) )] + public async Task Test1Async( TestEvent @event ) { + _log.LogInformation( "========================================================Pubsub_Test1:{@TestEvent}", @event ); + await _stateManager.StoreName( "event-state-store" ).AddAsync( $"pubsub_{@event.Code}", @event ); + return Success(); + } + + /// + /// 订阅B + /// + [HttpPost( "Pubsub_B" )] + [Topic( "b" )] + public async Task TestBAsync( TestEvent @event ) { + _log.LogInformation( "========================================================Pubsub_B:{@TestEvent}", @event ); + await Task.CompletedTask; + return Success(); + } + + /// + /// 订阅C + /// + [HttpPost( "Pubsub_C" )] + [Topic( "c" )] + public async Task TestCAsync( TestEvent @event ) { + _log.LogInformation( "========================================================Pubsub_C:{@TestEvent}", @event ); + await Task.CompletedTask; + throw new Warning( "Pubsub_Warning_C" ); + return Success(); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.PubsubSample/Dtos/CustomerDto.cs b/test/Util.Microservices.Dapr.PubsubSample/Dtos/CustomerDto.cs new file mode 100644 index 000000000..09eca5a44 --- /dev/null +++ b/test/Util.Microservices.Dapr.PubsubSample/Dtos/CustomerDto.cs @@ -0,0 +1,76 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Applications.Dtos; + +namespace Util.Microservices.Dapr.WebApiSample.Dtos { + /// + /// 客户参数 + /// + public class CustomerDto : DtoBase { + /// + /// 编码 + /// + [Description( "编码" )] + [MaxLength( 100 )] + public string Code { get; set; } + /// + /// 姓名 + /// + [Description( "姓名" )] + [MaxLength( 200 )] + public string Name { get; set; } + /// + /// 昵称 + /// + [Description( "昵称" )] + [MaxLength( 50 )] + public string Nickname { get; set; } + /// + /// 出生日期 + /// + [Description( "出生日期" )] + public DateTime? Birthday { get; set; } + /// + /// 手机号 + /// + [Description( "手机号" )] + [MaxLength( 50 )] + public string Phone { get; set; } + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + [MaxLength( 500 )] + public string Email { get; set; } + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + /// + /// 版本号 + /// + [Description( "版本号" )] + public byte[] Version { get; set; } + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.PubsubSample/Dtos/CustomerQuery.cs b/test/Util.Microservices.Dapr.PubsubSample/Dtos/CustomerQuery.cs new file mode 100644 index 000000000..e7124fcd3 --- /dev/null +++ b/test/Util.Microservices.Dapr.PubsubSample/Dtos/CustomerQuery.cs @@ -0,0 +1,113 @@ +using System.ComponentModel; +using Util.Data.Queries; + +namespace Util.Microservices.Dapr.WebApiSample.Dtos; + +/// +/// 客户查询参数 +/// +public class CustomerQuery : QueryParameter { + /// + /// 客户标识 + /// + [Description( "客户标识" )] + public int? CustomerId { get; set; } + /// + /// 编码 + /// + private string _code = string.Empty; + /// + /// 编码 + /// + [Description( "编码" )] + public string Code { + get => _code == null ? string.Empty : _code.Trim(); + set => _code = value; + } + /// + /// 姓名 + /// + private string _name = string.Empty; + /// + /// 姓名 + /// + [Description( "姓名" )] + public string Name { + get => _name == null ? string.Empty : _name.Trim(); + set => _name = value; + } + /// + /// 昵称 + /// + private string _nickname = string.Empty; + /// + /// 昵称 + /// + [Description( "昵称" )] + public string Nickname { + get => _nickname == null ? string.Empty : _nickname.Trim(); + set => _nickname = value; + } + /// + /// 起始出生日期 + /// + public DateTime? BeginBirthday { get; set; } + /// + /// 结束出生日期 + /// + public DateTime? EndBirthday { get; set; } + /// + /// 手机号 + /// + private string _phone = string.Empty; + /// + /// 手机号 + /// + [Description( "手机号" )] + public string Phone { + get => _phone == null ? string.Empty : _phone.Trim(); + set => _phone = value; + } + /// + /// 电子邮件 + /// + private string _email = string.Empty; + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + public string Email { + get => _email == null ? string.Empty : _email.Trim(); + set => _email = value; + } + /// + /// 年龄 + /// + public int? Age { get; set; } + /// + /// 起始创建时间 + /// + public DateTime? BeginCreationTime { get; set; } + /// + /// 结束创建时间 + /// + public DateTime? EndCreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 起始最后修改时间 + /// + public DateTime? BeginLastModificationTime { get; set; } + /// + /// 结束最后修改时间 + /// + public DateTime? EndLastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.PubsubSample/Events/TestEvent.cs b/test/Util.Microservices.Dapr.PubsubSample/Events/TestEvent.cs new file mode 100644 index 000000000..ed00d4544 --- /dev/null +++ b/test/Util.Microservices.Dapr.PubsubSample/Events/TestEvent.cs @@ -0,0 +1,10 @@ +using Util.Events; + +namespace Util.Microservices.Dapr.PubsubSample.Events; + +/// +/// 测试事件 +/// +/// 编码 +/// 名称 +public record TestEvent( string Code, string Name ) : IntegrationEvent; \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.PubsubSample/Program.cs b/test/Util.Microservices.Dapr.PubsubSample/Program.cs new file mode 100644 index 000000000..99ca707a8 --- /dev/null +++ b/test/Util.Microservices.Dapr.PubsubSample/Program.cs @@ -0,0 +1,20 @@ +using Util.Helpers; +using Util.Logging.Serilog; +using Util.Microservices.Dapr; + +var builder = WebApplication.CreateBuilder( args ); +builder.Services.AddControllers(); +builder.AsBuild() + .AddSerilog( "Util.Microservices.Dapr.PubsubSample" ) + .AddDapr( t => t.AppId = "app_pubsub", daprBuilder => { + daprBuilder.UseHttpEndpoint( $"http://127.0.0.1:{Config.GetValue( "DaprPorts:HttpPort" )}" ) + .UseGrpcEndpoint( $"http://127.0.0.1:{Config.GetValue( "DaprPorts:GrpcPort" )}" ); + } ) + .AddUtil(); + +var app = builder.Build(); +app.UseCloudEventHeaders(); +app.UseCloudEvents(); +app.MapSubscribeHandler(); +app.MapControllers(); +app.Run(); \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.PubsubSample/Properties/launchSettings.json b/test/Util.Microservices.Dapr.PubsubSample/Properties/launchSettings.json new file mode 100644 index 000000000..3ed7949de --- /dev/null +++ b/test/Util.Microservices.Dapr.PubsubSample/Properties/launchSettings.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "", + "applicationUrl": "http://localhost:51475", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/test/Util.Microservices.Dapr.PubsubSample/Usings.cs b/test/Util.Microservices.Dapr.PubsubSample/Usings.cs new file mode 100644 index 000000000..85103500f --- /dev/null +++ b/test/Util.Microservices.Dapr.PubsubSample/Usings.cs @@ -0,0 +1,10 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Microsoft.AspNetCore.Mvc; +global using Util; \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.PubsubSample/Util.Microservices.Dapr.PubsubSample.csproj b/test/Util.Microservices.Dapr.PubsubSample/Util.Microservices.Dapr.PubsubSample.csproj new file mode 100644 index 000000000..51936dfa0 --- /dev/null +++ b/test/Util.Microservices.Dapr.PubsubSample/Util.Microservices.Dapr.PubsubSample.csproj @@ -0,0 +1,14 @@ + + + + $(NetTargetFramework) + enable + + + + + + + + + diff --git a/test/Util.Microservices.Dapr.PubsubSample/appsettings.Development.json b/test/Util.Microservices.Dapr.PubsubSample/appsettings.Development.json new file mode 100644 index 000000000..17246e7c9 --- /dev/null +++ b/test/Util.Microservices.Dapr.PubsubSample/appsettings.Development.json @@ -0,0 +1,22 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft.AspNetCore": "Trace" + } + }, + "Serilog": { + "WriteTo": [ + { + "Name": "Seq", + "Args": { + "serverUrl": "http://localhost:5380" + } + } + ] + }, + "DaprPorts": { + "HttpPort": "51476", + "GrpcPort": "51477" + } +} diff --git a/test/Util.Microservices.Dapr.PubsubSample/appsettings.json b/test/Util.Microservices.Dapr.PubsubSample/appsettings.json new file mode 100644 index 000000000..a531fb681 --- /dev/null +++ b/test/Util.Microservices.Dapr.PubsubSample/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft.AspNetCore": "Trace" + } + }, + "AllowedHosts": "*" +} diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Events/PubsubTest.EventLog.cs b/test/Util.Microservices.Dapr.Tests.Integration/Events/PubsubTest.EventLog.cs new file mode 100644 index 000000000..beef3acca --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Events/PubsubTest.EventLog.cs @@ -0,0 +1,97 @@ +using System.Linq; +using Util.Microservices.Dapr.Tests.Samples; +using Util.Microservices.Events; + +namespace Util.Microservices.Dapr.Tests.Events; + +/// +/// 事件发布订阅测试 - 测试日志记录 +/// +public partial class PubsubTest { + /// + /// 测试集成事件日志记录 - 事件状态:已发布 + /// + [Fact] + public async Task TestIntegrationEventLog_Published() { + //清空集成事件计数 + await _eventManager.ClearCountAsync(); + + //发布事件,没有订阅者 + var testEvent = new TestEvent( "1", "test" ); + await _eventBus.Topic( "a" ).PublishAsync( testEvent ); + + //获取日志记录 + var log = await _eventManager.GetAsync( testEvent.EventId ); + Assert.True( log.State == EventState.Published ); + + //获取计数 + var count = await _eventManager.GetCountAsync(); + Assert.Equal( 1,count ); + + //再次发布事件 + testEvent = new TestEvent( "1", "test" ); + await _eventBus.Topic( "a" ).PublishAsync( testEvent ); + + //获取计数 + count = await _eventManager.GetCountAsync(); + Assert.Equal( 2, count ); + } + + /// + /// 测试集成事件日志记录 - 事件状态:成功 + /// + [Fact] + public async Task TestIntegrationEventLog_Success() { + var testEvent = new TestEvent( Id.Create(), Id.Create() ); + await _eventBus.Topic( "b" ).PublishAsync( testEvent ); + + //获取日志记录 + var log = await GetSuccessEventLog( testEvent.EventId ); + Assert.True( log.State == EventState.Success ); + } + + /// + /// 获取集成事件日志记录 - 成功状态 + /// + private async Task GetSuccessEventLog( string eventId ) { + for ( int i = 0; i < 100; i++ ) { + var result = await _eventManager.GetAsync( eventId ); + if ( result is { State: EventState.Success } ) { + _logger.LogInformation( "获取集成事件日志记录成功: i={i},eventId={eventId},data={@data}", i, eventId, result ); + return result; + } + await Task.Delay( 100 ); + } + return null; + } + + /// + /// 测试集成事件日志记录 - 事件状态:失败 + /// + [Fact] + public async Task TestIntegrationEventLog_Fail() { + var testEvent = new TestEvent( Id.Create(), Id.Create() ); + await _eventBus.Topic( "c" ).PublishAsync( testEvent ); + + //获取日志记录 + var log = await GetFailEventLog( testEvent.EventId ); + Assert.True( log.State == EventState.Fail ); + Assert.Contains( log.SubscriptionLogs, t => t.RetryCount == 3 ); + Assert.Contains( log.SubscriptionLogs, t => t.RetryCount.SafeValue() == 0 ); + } + + /// + /// 获取集成事件日志记录 - 失败状态 + /// + private async Task GetFailEventLog( string eventId ) { + for ( int i = 0; i < 100; i++ ) { + var result = await _eventManager.GetAsync( eventId ); + if ( result is { State: EventState.Fail } && result.SubscriptionLogs.Any( t => t.RetryCount == 3 ) ) { + _logger.LogInformation( "获取集成事件日志记录成功: i={i},eventId={eventId},data={@data}", i, eventId, result ); + return result; + } + await Task.Delay( 100 ); + } + return null; + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Events/PubsubTest.cs b/test/Util.Microservices.Dapr.Tests.Integration/Events/PubsubTest.cs new file mode 100644 index 000000000..04e58b700 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Events/PubsubTest.cs @@ -0,0 +1,99 @@ +using Util.Events; +using Util.Microservices.Dapr.Tests.Samples; +using Util.Microservices.Events; + +namespace Util.Microservices.Dapr.Tests.Events; + +/// +/// 事件发布订阅测试 +/// +[Collection( "Global" )] +public partial class PubsubTest { + /// + /// 集成事件总线 + /// + private readonly IIntegrationEventBus _eventBus; + /// + /// 状态管理 + /// + private readonly IStateManager _stateManager; + /// + /// 集成事件日志记录存储器 + /// + private readonly IIntegrationEventLogStore _eventLogStore; + /// + /// 集成事件管理器 + /// + private readonly IIntegrationEventManager _eventManager; + /// + /// 日志 + /// + private readonly ILogger _logger; + + /// + /// 测试初始化 + /// + public PubsubTest( IIntegrationEventBus eventBus, IStateManager stateManager, IIntegrationEventLogStore eventLogStore, + IIntegrationEventManager eventManager,ILogger logger ) { + _eventBus = eventBus; + _eventManager = eventManager; + _stateManager = stateManager; + _eventLogStore = eventLogStore; + _logger = logger; + } + + /// + /// 测试发布订阅 + /// + [Fact] + public async Task TestPublishAsync() { + var code = Id.Create(); + var name = Id.Create(); + var testEvent = new TestEvent( code, name ); + await _eventBus.PublishAsync( testEvent ); + + //验证webapi订阅写入的数据 + var result = await GetResult( $"webapi_{code}" ); + Assert.NotNull( result ); + + //验证pubsub订阅写入的数据 + result = await GetResult( $"pubsub_{code}" ); + Assert.NotNull( result ); + } + + /// + /// 获取测试结果 - 由订阅服务写入的数据 + /// + private async Task GetResult( string key ) { + for ( int i = 0; i < 100; i++ ) { + var result = await _stateManager.StoreName( "event-state-store" ).GetAsync( key ); + if ( result != null ) { + _logger.LogInformation( "成功获取事件发布订阅测试数据: i={i},key={key},data={@data}", i, key, result ); + return result; + } + await Task.Delay( 100 ); + } + return null; + } + + /// + /// 测试重新发布集成事件 + /// + [Fact] + public async Task TestRepublishAsync() { + var testEvent = new TestEvent( Id.Create(), Id.Create() ); + await _eventBus.Topic( "c" ).PublishAsync( testEvent ); + + //执行失败 + var log = await GetFailEventLog( testEvent.EventId ); + Assert.True( log.State == EventState.Fail ); + + //重发事件 + await _eventBus.RepublishAsync( testEvent.EventId ); + + //验证 + log = await GetFailEventLog( testEvent.EventId ); + Assert.Contains( log.SubscriptionLogs, t => t.RetryLogs?.Count == 7 ); + Assert.Contains( log.SubscriptionLogs, t => t.RetryLogs?.Count == 2 ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Fixtures/GlobalCollection.cs b/test/Util.Microservices.Dapr.Tests.Integration/Fixtures/GlobalCollection.cs new file mode 100644 index 000000000..dc44ad212 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Fixtures/GlobalCollection.cs @@ -0,0 +1,8 @@ +namespace Util.Microservices.Dapr.Tests.Fixtures; + +/// +/// 全局测试集合 +/// +[CollectionDefinition( "Global" )] +public class GlobalCollection : ICollectionFixture { +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Fixtures/GlobalFixture.cs b/test/Util.Microservices.Dapr.Tests.Integration/Fixtures/GlobalFixture.cs new file mode 100644 index 000000000..e31b1ddc4 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Fixtures/GlobalFixture.cs @@ -0,0 +1,102 @@ +using Serilog; +using Util.Logging.Serilog; + +namespace Util.Microservices.Dapr.Tests.Fixtures; + +/// +/// 全局测试设置 +/// +public class GlobalFixture : IAsyncDisposable { + /// + /// app_webapi + /// + public const string WebApiAppId = "app_webapi"; + /// + /// app_pubsub + /// + public const string PubsubAppId = "app_pubsub"; + /// + /// Dapr Http端口 + /// + public int DaprHttpPort { get; } + /// + /// Dapr Grpc端口 + /// + public int DaprGrpcPort { get; } + + /// + /// 测试初始化 + /// + public GlobalFixture() { + StopDapr(); + ConfigLog(); + var loggerFactory = Ioc.Create(); + var log = loggerFactory.CreateLogger( "Util.Microservices.Dapr.Tests.Integration" ); + var command = ExecuteDaprRunCommand( log, WebApiAppId, "Util.Microservices.Dapr.WebApiSample", + Config.GetValue( "DaprPorts:WebApi:AppPort" ), + Config.GetValue( "DaprPorts:WebApi:HttpPort" ), + Config.GetValue( "DaprPorts:WebApi:GrpcPort" ) + ); + ExecuteDaprRunCommand( log, PubsubAppId, "Util.Microservices.Dapr.PubsubSample", + Config.GetValue( "DaprPorts:Pubsub:AppPort" ), + Config.GetValue( "DaprPorts:Pubsub:HttpPort" ), + Config.GetValue( "DaprPorts:Pubsub:GrpcPort" ) + ); + DaprHttpPort = command.GetDaprHttpPort(); + DaprGrpcPort = command.GetDaprGrpcPort(); + } + + /// + /// 配置日志 + /// + private void ConfigLog() { + var services = Ioc.GetServices(); + services.AddLogging( loggingBuilder => { + Log.Logger = new LoggerConfiguration() + .WriteTo.Seq( Config.GetValue( "SeqUrl" ) ) + .MinimumLevel.Verbose() + .Enrich.FromLogContext() + .Enrich.WithLogContext() + .Enrich.WithProperty( "ApplicationName", "Util.Microservices.Dapr.Tests.Integration" ) + .CreateLogger(); + loggingBuilder.AddSerilog( Log.Logger ); + } ); + } + + /// + /// 通过命令行启动Dapr + /// + private DaprRunCommand ExecuteDaprRunCommand( Microsoft.Extensions.Logging.ILogger log,string appId,string project, + int appPort,int daprHttpPort,int daprGrpcPort ) { + var command = DaprRunCommand.Create( appId ); + if ( project.IsEmpty() == false ) + command.Project( $"../../../../../test/{project}/{project}.csproj" ); + command.AppPort( appPort ) + .DaprHttpPort( daprHttpPort ) + .DaprGrpcPort( daprGrpcPort ) + .ComponentsPath( "Resources/components/" ) + .ConfigPath( "Resources/configuration/config.yaml" ) + .Log( log ) + .Execute(); + return command; + } + + /// + /// 测试清理 + /// + public async ValueTask DisposeAsync() { + await Task.Delay( 3000 ); + DaprStopCommand.Create( WebApiAppId ).Execute(); + DaprStopCommand.Create( PubsubAppId ).Execute(); + StopDapr(); + CommandLine.ExecutePowerShell( "Stop-Process -Name \"dotnet*\"" ); + } + + /// + /// 停止Dapr进程 + /// + private void StopDapr() { + CommandLine.ExecutePowerShell( "Stop-Process -Name \"Util.Microservices.Dapr*\"" ); + CommandLine.ExecutePowerShell( "Stop-Process -Name \"dapr*\"" ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Resources/components/event-state-store.yaml b/test/Util.Microservices.Dapr.Tests.Integration/Resources/components/event-state-store.yaml new file mode 100644 index 000000000..878e89f8d --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Resources/components/event-state-store.yaml @@ -0,0 +1,15 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: event-state-store +spec: + type: state.mongodb + metadata: + - name: keyPrefix + value: name + - name: host + value: localhost + - name: databaseName + value: util_dapr_tests + - name: collectionName + value: events \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Resources/components/pubsub.yaml b/test/Util.Microservices.Dapr.Tests.Integration/Resources/components/pubsub.yaml new file mode 100644 index 000000000..333ca844e --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Resources/components/pubsub.yaml @@ -0,0 +1,21 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: pubsub + namespace: default +spec: + type: pubsub.rabbitmq + version: v1 + metadata: + - name: host + value: "amqp://localhost:5672" + - name: durable + value: "false" + - name: deletedWhenUnused + value: "false" + - name: autoAck + value: "false" + - name: reconnectWait + value: "0" + - name: concurrencyMode + value: parallel \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Resources/components/resiliency-pubsub.yaml b/test/Util.Microservices.Dapr.Tests.Integration/Resources/components/resiliency-pubsub.yaml new file mode 100644 index 000000000..35345c7c1 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Resources/components/resiliency-pubsub.yaml @@ -0,0 +1,16 @@ +apiVersion: dapr.io/v1alpha1 +kind: Resiliency +metadata: + name: resiliency-pubsub +spec: + policies: + retries: + pubsubRetry: + policy: constant + duration: 1s + maxRetries: 3 + targets: + components: + pubsub: + inbound: + retry: pubsubRetry \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Resources/components/statestore.yaml b/test/Util.Microservices.Dapr.Tests.Integration/Resources/components/statestore.yaml new file mode 100644 index 000000000..1876233b3 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Resources/components/statestore.yaml @@ -0,0 +1,13 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: statestore +spec: + type: state.mongodb + metadata: + - name: host + value: localhost + - name: databaseName + value: util_dapr_tests + - name: collectionName + value: default \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Resources/configuration/config.yaml b/test/Util.Microservices.Dapr.Tests.Integration/Resources/configuration/config.yaml new file mode 100644 index 000000000..69b47eebc --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Resources/configuration/config.yaml @@ -0,0 +1,9 @@ +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: daprConfig +spec: + tracing: + samplingRate: "1" + zipkin: + endpointAddress: http://localhost:9411/api/v2/spans \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Samples/CustomerDto.cs b/test/Util.Microservices.Dapr.Tests.Integration/Samples/CustomerDto.cs new file mode 100644 index 000000000..95fe30c90 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Samples/CustomerDto.cs @@ -0,0 +1,96 @@ +using Util.Data; +using Util.Domain.Biz.Enums; +using Util.Events; + +namespace Util.Microservices.Dapr.Tests.Samples; + +/// +/// 客户参数 +/// +public class CustomerDto { + /// + /// 编码 + /// + [Description( "编码" )] + [MaxLength( 100 )] + public string Code { get; set; } + /// + /// 姓名 + /// + [Description( "姓名" )] + [MaxLength( 200 )] + public string Name { get; set; } + /// + /// 昵称 + /// + [Description( "昵称" )] + [MaxLength( 50 )] + public string Nickname { get; set; } + /// + /// 性别 + /// + [Description( "性别" )] + public Gender? Gender { get; set; } + /// + /// 出生日期 + /// + [Description( "出生日期" )] + public DateTime? Birthday { get; set; } + /// + /// 民族 + /// + [Description( "民族" )] + public Nation? Nation { get; set; } + /// + /// 手机号 + /// + [Description( "手机号" )] + [MaxLength( 50 )] + public string Phone { get; set; } + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + [MaxLength( 500 )] + public string Email { get; set; } + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + /// + /// 版本号 + /// + [Description( "版本号" )] + public byte[] Version { get; set; } +} + +/// +/// 客户参数2 +/// +public class CustomerDto2 : CustomerDto, IDataKey,IDataType, IETag { + public string Id { get; set; } + public string ETag { get; set; } + public string DataType { get; set; } + public ProductDto Product { get; set; } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Samples/CustomerQuery.cs b/test/Util.Microservices.Dapr.Tests.Integration/Samples/CustomerQuery.cs new file mode 100644 index 000000000..d6b6f255c --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Samples/CustomerQuery.cs @@ -0,0 +1,112 @@ +using Util.Data.Queries; + +namespace Util.Microservices.Dapr.Tests.Samples; + +/// +/// 客户查询参数 +/// +public class CustomerQuery : QueryParameter { + /// + /// 客户标识 + /// + [Description( "客户标识" )] + public int? CustomerId { get; set; } + /// + /// 编码 + /// + private string _code = string.Empty; + /// + /// 编码 + /// + [Description( "编码" )] + public string Code { + get => _code == null ? string.Empty : _code.Trim(); + set => _code = value; + } + /// + /// 姓名 + /// + private string _name = string.Empty; + /// + /// 姓名 + /// + [Description( "姓名" )] + public string Name { + get => _name == null ? string.Empty : _name.Trim(); + set => _name = value; + } + /// + /// 昵称 + /// + private string _nickname = string.Empty; + /// + /// 昵称 + /// + [Description( "昵称" )] + public string Nickname { + get => _nickname == null ? string.Empty : _nickname.Trim(); + set => _nickname = value; + } + /// + /// 起始出生日期 + /// + public DateTime? BeginBirthday { get; set; } + /// + /// 结束出生日期 + /// + public DateTime? EndBirthday { get; set; } + /// + /// 手机号 + /// + private string _phone = string.Empty; + /// + /// 手机号 + /// + [Description( "手机号" )] + public string Phone { + get => _phone == null ? string.Empty : _phone.Trim(); + set => _phone = value; + } + /// + /// 电子邮件 + /// + private string _email = string.Empty; + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + public string Email { + get => _email == null ? string.Empty : _email.Trim(); + set => _email = value; + } + /// + /// 年龄 + /// + public int? Age { get; set; } + /// + /// 起始创建时间 + /// + public DateTime? BeginCreationTime { get; set; } + /// + /// 结束创建时间 + /// + public DateTime? EndCreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 起始最后修改时间 + /// + public DateTime? BeginLastModificationTime { get; set; } + /// + /// 结束最后修改时间 + /// + public DateTime? EndLastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Samples/MockHttpContextAccessor.cs b/test/Util.Microservices.Dapr.Tests.Integration/Samples/MockHttpContextAccessor.cs new file mode 100644 index 000000000..42da23226 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Samples/MockHttpContextAccessor.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; + +namespace Util.Microservices.Dapr.Tests.Samples; + +/// +/// 模拟Http上下文访问器 +/// +public class MockHttpContextAccessor : IHttpContextAccessor { + /// + /// 初始化模拟Http上下文访问器 + /// + public MockHttpContextAccessor() { + HttpContext = new DefaultHttpContext(); + HttpContext.Request.Headers["test_1"] = "test_1"; + HttpContext.Request.Headers.ContentLanguage = new StringValues( "en" ); + } + + /// + /// Http上下文 + /// + public HttpContext HttpContext { get; set; } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Samples/ProductDto.cs b/test/Util.Microservices.Dapr.Tests.Integration/Samples/ProductDto.cs new file mode 100644 index 000000000..e173b5bd3 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Samples/ProductDto.cs @@ -0,0 +1,70 @@ +using Util.Applications.Dtos; + +namespace Util.Microservices.Dapr.Tests.Samples; + +/// +/// 产品参数 +/// +public class ProductDto : DtoBase { + /// + /// 产品编码 + /// + [Description( "产品编码" )] + [MaxLength( 50 )] + public string Code { get; set; } + /// + /// 产品名称 + /// + [Description( "产品名称" )] + [MaxLength( 500 )] + public string Name { get; set; } + /// + /// 价格 + /// + [Description( "价格" )] + public decimal Price { get; set; } + /// + /// 描述 + /// + [Description( "描述" )] + public string Description { get; set; } + /// + /// 启用 + /// + [Description( "启用" )] + public bool Enabled { get; set; } + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + /// + /// 版本号 + /// + [Description( "版本号" )] + public byte[] Version { get; set; } + /// + /// 简单扩展属性 + /// + public string TestProperty1 { get; set; } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Samples/ProductQuery.cs b/test/Util.Microservices.Dapr.Tests.Integration/Samples/ProductQuery.cs new file mode 100644 index 000000000..06a250493 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Samples/ProductQuery.cs @@ -0,0 +1,86 @@ +using Util.Data.Queries; + +namespace Util.Microservices.Dapr.Tests.Samples; + +/// +/// 产品查询参数 +/// +public class ProductQuery : QueryParameter { + /// + /// 产品标识 + /// + [Description( "产品标识" )] + public Guid? ProductId { get; set; } + /// + /// 产品编码 + /// + private string _code = string.Empty; + /// + /// 产品编码 + /// + [Description( "产品编码" )] + public string Code { + get => _code == null ? string.Empty : _code.Trim(); + set => _code = value; + } + /// + /// 产品名称 + /// + private string _name = string.Empty; + /// + /// 产品名称 + /// + [Description( "产品名称" )] + public string Name { + get => _name == null ? string.Empty : _name.Trim(); + set => _name = value; + } + /// + /// 价格 + /// + [Description( "价格" )] + public decimal? Price { get; set; } + /// + /// 描述 + /// + private string _description = string.Empty; + /// + /// 描述 + /// + [Description( "描述" )] + public string Description { + get => _description == null ? string.Empty : _description.Trim(); + set => _description = value; + } + /// + /// 启用 + /// + [Description( "启用" )] + public bool? Enabled { get; set; } + /// + /// 起始创建时间 + /// + public DateTime? BeginCreationTime { get; set; } + /// + /// 结束创建时间 + /// + public DateTime? EndCreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 起始最后修改时间 + /// + public DateTime? BeginLastModificationTime { get; set; } + /// + /// 结束最后修改时间 + /// + public DateTime? EndLastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Samples/TestEvent.cs b/test/Util.Microservices.Dapr.Tests.Integration/Samples/TestEvent.cs new file mode 100644 index 000000000..4cdb2fc93 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Samples/TestEvent.cs @@ -0,0 +1,10 @@ +using Util.Events; + +namespace Util.Microservices.Dapr.Tests.Samples; + +/// +/// 测试事件 +/// +/// 编码 +/// 名称 +public record TestEvent( string Code, string Name ) : IntegrationEvent; \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/HttpClientTest.cs b/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/HttpClientTest.cs new file mode 100644 index 000000000..92a1f2d3a --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/HttpClientTest.cs @@ -0,0 +1,62 @@ +using Util.Microservices.Dapr.Tests.Fixtures; +using Util.Microservices.Dapr.Tests.Samples; + +namespace Util.Microservices.Dapr.Tests.ServiceInvocations; + +/// +/// 使用Http Client调用服务测试 +/// 说明: 请求WebApi项目 Util.Microservices.Dapr.WebApiSample +/// +[Collection( "Global" )] +public class HttpClientTest { + /// + /// Http客户端操作 + /// + private readonly IHttpClient _client; + /// + /// 日志 + /// + private readonly ILogger _logger; + + /// + /// 测试初始化 + /// + public HttpClientTest( GlobalFixture fixture, IMicroserviceClientFactory factory, ILogger logger ) { + _client = factory.DaprHttpPort( fixture.DaprHttpPort ) + .AppId( GlobalFixture.WebApiAppId ) + .Create() + .HttpClient; + _logger = logger; + } + + /// + /// 请求Test1Controller控制器Get_1方法,返回字符串 + /// + [Fact] + public async Task Test_1() { + var result = await _client.Get( "test1" ).GetResultAsync(); + _logger.LogDebug( result ); + Assert.Equal( "ok", result ); + } + + /// + /// 请求Test1Controller控制器Get_2方法,传入id参数 + /// + [Fact] + public async Task Test_2() { + var result = await _client.Get( "test1/1" ).GetResultAsync(); + _logger.LogDebug( result ); + Assert.Equal( "id:1", result ); + } + + /// + /// 测试调用方法 - 请求Test3Controller控制器Query方法 + /// + [Fact] + public async Task Test_3() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _client.Get>( "/api/test3/query", query ).GetResultAsync(); + Assert.NotNull( result ); + Assert.Equal( "ok", result.Data.Name ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/ServiceInvocationTest.NotUnPack.cs b/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/ServiceInvocationTest.NotUnPack.cs new file mode 100644 index 000000000..d78aa6a05 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/ServiceInvocationTest.NotUnPack.cs @@ -0,0 +1,94 @@ +using Util.Exceptions; +using Util.Microservices.Dapr.Tests.Samples; + +namespace Util.Microservices.Dapr.Tests.ServiceInvocations; + +/// +/// 服务调用测试 - Web Api接口测试 - 不进行解包 +/// 说明: 请求项目 Util.Microservices.Dapr.WebApiSample +/// +public partial class ServiceInvocationTest { + + #region 测试无参数无返回值的场景 + + /// + /// 测试调用方法 - 请求Test2Controller控制器Get方法 - 无参数,无返回值 - 成功 + /// + [Fact] + public async Task TestInvokeAsync_NotUnpack_1() { + await _serviceInvocation.UnpackResult( false ).InvokeAsync( "/Test2" ); + } + + /// + /// 测试调用方法 - 请求Test2Controller控制器Get_Fail方法 - 无参数,无返回值 - 失败,抛出异常 + /// + [Fact] + public async Task TestInvokeAsync_NotUnpack_2() { + await Assert.ThrowsAsync( async () => { + await _serviceInvocation.UnpackResult( false ).InvokeAsync( "/Test2/fail" ); + } ); + } + + #endregion + + #region 测试有参数无返回值的场景 + + /// + /// 测试调用方法 - 请求Test2Controller控制器Post方法 - 有参数,无返回值 - Code参数为2,成功 + /// + [Fact] + public async Task TestInvokeAsync_NotUnpack_3() { + var dto = new CustomerDto { Code = "2" }; + await _serviceInvocation.UnpackResult( false ).InvokeAsync( "/Test2", dto ); + } + + /// + /// 测试调用方法 - 请求Test2Controller控制器Post方法 - 有参数,无返回值 - Code参数为1,返回失败消息 + /// + [Fact] + public async Task TestInvokeAsync_NotUnpack_4() { + await Assert.ThrowsAsync( async () => { + var dto = new CustomerDto { Code = "1" }; + await _serviceInvocation.UnpackResult( false ).InvokeAsync( "/Test2", dto ); + } ); + } + + #endregion + + #region 测试无参数有返回值的场景 + + /// + /// 测试调用方法 - 请求Test2Controller控制器Get_Customer方法 - 无参数,有返回值 + /// + [Fact] + public async Task TestInvokeAsync_NotUnpack_5() { + var result = await _serviceInvocation.UnpackResult( false ).InvokeAsync( "/Test2/customer" ); + Assert.Equal( "ok", result.Name ); + } + + #endregion + + #region 测试有参数有返回值的场景 + + /// + /// 测试调用方法 - 请求Test2Controller控制器Query方法 - 有参数,有返回值 + /// + [Fact] + public async Task TestInvokeAsync_NotUnpack_6() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation.UnpackResult( false ).InvokeAsync( "/Test2/query", query ); + Assert.Equal( "ok", result.Name ); + } + + /// + /// 测试调用方法 - 请求Test2Controller控制器Query方法 - 有参数,有返回值 - 请求参数使用泛型 + /// + [Fact] + public async Task TestInvokeAsync_NotUnpack_7() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation.UnpackResult( false ).InvokeAsync( "/Test2/query", query ); + Assert.Equal( "ok", result.Name ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/ServiceInvocationTest.UnPack.cs b/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/ServiceInvocationTest.UnPack.cs new file mode 100644 index 000000000..f0def15d7 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/ServiceInvocationTest.UnPack.cs @@ -0,0 +1,288 @@ +using System.Collections.Generic; +using Util.Data; +using Util.Exceptions; +using Util.Microservices.Dapr.Tests.Samples; + +namespace Util.Microservices.Dapr.Tests.ServiceInvocations; + +/// +/// 服务调用测试 - Web Api接口测试 - 使用ServiceResult解包 +/// 说明: 请求项目 Util.Microservices.Dapr.WebApiSample +/// +public partial class ServiceInvocationTest { + + #region 测试无参数无返回值的场景 + + /// + /// 测试调用方法 - 请求Test3Controller控制器Get方法 - 无参数,无返回值 - 状态码为成功 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_1() { + await _serviceInvocation.InvokeAsync( "/api/Test3" ); + } + + /// + /// 测试调用方法 - 请求Test3Controller控制器Get_Fail方法 - 无参数,无返回值 - 状态码为失败,抛出异常 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_2() { + await Assert.ThrowsAsync( async () => { + await _serviceInvocation.InvokeAsync( "/api/Test3/fail" ); + } ); + } + + #endregion + + #region 测试有参数无返回值的场景 + + /// + /// 测试调用方法 - 请求Test3Controller控制器Post方法 - 有参数,无返回值 - Code参数为2,返回成功 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_3() { + var dto = new CustomerDto { Code = "2" }; + await _serviceInvocation.InvokeAsync( "/api/Test3", dto ); + } + + /// + /// 测试调用方法 - 请求Test3Controller控制器Post方法 - 有参数,无返回值 - Code参数为1,返回失败消息 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_4() { + await Assert.ThrowsAsync( async () => { + var dto = new CustomerDto { Code = "1" }; + await _serviceInvocation.InvokeAsync( "/api/Test3", dto ); + } ); + } + + #endregion + + #region 测试无参数有返回值的场景 + + /// + /// 测试调用方法 - 请求Test3Controller控制器Get_Customer方法 - 无参数,有返回值 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_5() { + var result = await _serviceInvocation.InvokeAsync( "/api/Test3/customer" ); + Assert.Equal( "ok", result.Name ); + } + + #endregion + + #region 测试有参数有返回值的场景 + + /// + /// 测试调用方法 - 请求Test3Controller控制器Query方法 - 有参数,有返回值 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_6() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation.InvokeAsync( "/api/Test3/query", query ); + Assert.Equal( "ok", result.Name ); + } + + /// + /// 测试调用方法 - 请求Test3Controller控制器Query方法 - 有参数,有返回值 - 请求参数使用泛型 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_7() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation.InvokeAsync( "/api/Test3/query", query ); + Assert.Equal( "ok", result.Name ); + } + + #endregion + + #region 请求地址处理 + + /// + /// 测试调用方法 - 请求Test3Controller控制器Query方法 - 请求地址处理,Test3/query处理为 /api/Test3/query + /// + [Fact] + public async Task TestInvokeAsync_Unpack_8() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation.InvokeAsync( "Test3/query", query ); + Assert.Equal( "ok", result.Name ); + } + + /// + /// 测试调用方法 - 请求Test3Controller控制器Query2方法 - 请求地址以/开头,不进行处理 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_9() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation.InvokeAsync( "/query", query ); + Assert.Equal( "ok", result.Name ); + } + + #endregion + + #region 请求头处理 + + /// + /// 测试设置请求头 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_Header_1() { + var query = new CustomerQuery(); + var result = await _serviceInvocation.Header( "CustomerName", "abc" ).InvokeAsync( "test3/query_header", query ); + Assert.Equal( "abc", result.Name ); + } + + /// + /// 测试设置请求头集合 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_Header_2() { + var query = new CustomerQuery(); + var result = await _serviceInvocation.Header( new Dictionary { + {"CustomerCode","a"}, + {"CustomerName","b"} + } ).InvokeAsync( "test3/query_header", query ); + Assert.Equal( "a", result.Code ); + Assert.Equal( "b", result.Name ); + } + + /// + /// 测试移除请求头 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_Header_3() { + var query = new CustomerQuery(); + var result = await _serviceInvocation.Header( new Dictionary { + {"CustomerCode","a"}, + {"CustomerName","b"} + } ) + .RemoveHeader( "CustomerCode" ) + .InvokeAsync( "test3/query_header", query ); + Assert.Null( result.Code ); + Assert.Equal( "b", result.Name ); + } + + /// + /// 测试移除请求头 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_Header_4() { + Util.Helpers.Web.HttpContextAccessor = new MockHttpContextAccessor(); + var query = new CustomerQuery(); + var result = await _serviceInvocation + .ImportHeader( "test_1" ) + .InvokeAsync( "test3/query_header_import", query ); + Assert.Equal( "test_1", result.Code ); + } + + /// + /// 测试导入内容头 - Content-Language + /// + [Fact] + public async Task TestInvokeAsync_Unpack_ImportHeader_1() { + Util.Helpers.Web.HttpContextAccessor = new MockHttpContextAccessor(); + var query = new CustomerQuery(); + var result = await _serviceInvocation.ImportHeader( "Content-Language" ) + .InvokeAsync( "test3/query_header_import", query ); + Assert.Equal( "en", result.Name ); + } + + #endregion + + #region 测试事件处理 + + /// + /// 测试状态转换事件处理 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_OnState() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation + .OnState( ( code ) => ServiceState.Fail ) + .OnFail( async ( request, response, ex ) => { + query.Phone = "123"; + await Task.CompletedTask; + } ) + .InvokeAsync( "Test3/query", query ); + Assert.Equal( "123", query.Phone ); + } + + /// + /// 测试调用前事件处理 - 返回false,不执行 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_OnBefore() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation.OnBefore( request => false ).InvokeAsync( "Test3/query", query ); + Assert.Null( result ); + } + + /// + /// 测试转换结果事件处理 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_OnResult() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation + .OnResult( async ( response,options,token ) => { + var json = await response.Content.ReadAsStringAsync( token ); + var result = Json.ToObject>( json, options ); + result.Data.Phone = "123"; + return result; + } ) + .InvokeAsync( "Test3/query", query ); + Assert.NotNull( result ); + Assert.Equal( "ok", result.Name ); + Assert.Equal( "123", result.Phone ); + } + + /// + /// 测试调用成功事件处理 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_OnSuccess() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation + .OnSuccess( async ( request, response, result ) => { + result.Phone = "123"; + await Task.CompletedTask; + } ) + .InvokeAsync( "Test3/query", query ); + Assert.NotNull( result ); + Assert.Equal( "ok", result.Name ); + Assert.Equal( "123", result.Phone ); + } + + /// + /// 测试失败事件处理 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_OnFail() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation + .OnFail( async ( request, response, ex ) => { + query.Phone = "123"; + await Task.CompletedTask; + } ) + .InvokeAsync( "Test3/fail", query ); + Assert.Equal( "123", query.Phone ); + } + + /// + /// 测试完成事件处理 + /// + [Fact] + public async Task TestInvokeAsync_Unpack_OnComplete() { + var query = new CustomerQuery { Name = "ok" }; + var result = await _serviceInvocation + .OnComplete( async ( request, response ) => { + query.Phone = "123"; + await Task.CompletedTask; + } ) + .InvokeAsync>( "Test3/page_query", query ); + Assert.NotNull( result ); + Assert.Equal( 50, result.Total ); + Assert.Equal( "ok", result.Data[0].Name ); + Assert.Equal( "123", query.Phone ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/ServiceInvocationTest.cs b/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/ServiceInvocationTest.cs new file mode 100644 index 000000000..8ceb5ea99 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/ServiceInvocations/ServiceInvocationTest.cs @@ -0,0 +1,28 @@ +using Util.Microservices.Dapr.Tests.Fixtures; + +namespace Util.Microservices.Dapr.Tests.ServiceInvocations; + +/// +/// 服务调用测试 - Web Api接口测试 +/// 说明: 请求项目 Util.Microservices.Dapr.WebApiSample +/// +[Collection( "Global" )] +public partial class ServiceInvocationTest { + /// + /// 服务调用操作 + /// + private readonly IServiceInvocation _serviceInvocation; + /// + /// 日志 + /// + private readonly ILogger _logger; + + /// + /// 测试初始化 + /// + public ServiceInvocationTest( IServiceInvocation serviceInvocation, ILogger logger ) { + _serviceInvocation = serviceInvocation; + _serviceInvocation.Service( GlobalFixture.WebApiAppId ); + _logger = logger; + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Startup.cs b/test/Util.Microservices.Dapr.Tests.Integration/Startup.cs new file mode 100644 index 000000000..fa76e7d4c --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Startup.cs @@ -0,0 +1,31 @@ +using Util.Logging.Serilog; + +namespace Util.Microservices.Dapr.Tests; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Util.Helpers.Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .AsBuild() + .AddSerilog( "Util.Microservices.Dapr.Tests.Integration" ) + .AddDapr( t => t.AppId = "app_webapi", builder => { + builder.UseHttpEndpoint( $"http://127.0.0.1:{Config.GetValue( "DaprPorts:WebApi:HttpPort" )}" ) + .UseGrpcEndpoint( $"http://127.0.0.1:{Config.GetValue( "DaprPorts:WebApi:GrpcPort" )}" ); + } ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + Ioc.SetServiceProviderAction( services.BuildServiceProvider ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/StateManagements/StateManagerOfTTest.cs b/test/Util.Microservices.Dapr.Tests.Integration/StateManagements/StateManagerOfTTest.cs new file mode 100644 index 000000000..203997933 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/StateManagements/StateManagerOfTTest.cs @@ -0,0 +1,73 @@ +using Util.Microservices.Dapr.Tests.Samples; + +namespace Util.Microservices.Dapr.Tests.StateManagements; + +/// +/// 状态管理测试 - 泛型接口测试 +/// +[Collection( "Global" )] +public class StateManagerOfTTest { + /// + /// 状态管理操作 + /// + private readonly IStateManager _stateManager; + /// + /// 日志 + /// + private readonly ILogger _logger; + + /// + /// 测试初始化 + /// + public StateManagerOfTTest( IStateManager stateManager, ILogger logger ) { + _stateManager = stateManager; + _logger = logger; + } + + /// + /// 测试查询 - 1个相等条件 - 属性表达式 + /// + [Fact] + public async Task TestQueryAsync_1() { + //变量定义 + var code = $"TestQueryAsync_{Id.Create()}"; + var code2 = $"TestQueryAsync_{Id.Create()}"; + + //添加数据1 + var dto = new CustomerDto2 { Code = code }; + await _stateManager.SaveAsync( dto ); + + //添加数据2 + var dto2 = new CustomerDto2 { Code = code2 }; + await _stateManager.SaveAsync( dto2 ); + + //获取数据并验证 + var result = await _stateManager.Equal( t => t.Code, code ).QueryAsync(); + Assert.Single( result ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// 测试查询 - 1个In条件 - 属性表达式 + /// + [Fact] + public async Task TestQueryAsync_2() { + //变量定义 + var code = $"TestQueryAsync_{Id.Create()}"; + var code2 = $"TestQueryAsync_{Id.Create()}"; + + //添加数据1 + var dto = new CustomerDto2 { Code = code }; + await _stateManager.SaveAsync( dto ); + + //添加数据2 + var dto2 = new CustomerDto2 { Code = code2 }; + await _stateManager.SaveAsync( dto2 ); + + //获取数据并验证 + var result = await _stateManager.In( t => t.Code, code, code2 ).QueryAsync(); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Code == code ); + Assert.Contains( result, t => t.Code == code2 ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/StateManagements/StateManagerTest.Query.cs b/test/Util.Microservices.Dapr.Tests.Integration/StateManagements/StateManagerTest.Query.cs new file mode 100644 index 000000000..8db791cb1 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/StateManagements/StateManagerTest.Query.cs @@ -0,0 +1,290 @@ +using Util.Data.Queries; +using Util.Microservices.Dapr.Tests.Samples; + +namespace Util.Microservices.Dapr.Tests.StateManagements; + +/// +/// 状态管理测试 - 查询测试 +/// +public partial class StateManagerTest { + + #region GetAsync + + /// + /// 测试根据键批量获取 + /// + [Fact] + public async Task TestGetAsync_1() { + //添加数据1 + var dto_1 = new CustomerDto2 { Code = "1" }; + var key_1 = await _stateManager.SaveAsync( dto_1 ); + + //添加数据2 + var dto_2 = new CustomerDto2 { Code = "2" }; + var key_2 = await _stateManager.SaveAsync( dto_2 ); + + //获取数据并验证 + var result = await _stateManager.GetAsync( new[] { key_1, key_2 } ); + Assert.Contains( result, t => t.Code == "1" ); + Assert.Contains( result, t => t.Code == "2" ); + } + + #endregion + + #region GetByIdAsync + + /// + /// 测试通过标识获取数据 + /// + [Fact] + public async Task TestGetByIdAsync() { + //变量定义 + var dto = new CustomerDto2 { Code = "123" }; + + //添加数据 + await _stateManager.SaveAsync( dto ); + + //获取数据并验证 + var result = await _stateManager.GetByIdAsync( dto.Id ); + Assert.Equal( "123", result.Code ); + } + + #endregion + + #region GetAllAsync + + /// + /// 测试获取指定类型的全部数据 + /// + [Fact] + public async Task TestGetAllAsync() { + //变量定义 + var code = $"TestGetAllAsync_{Id.Create()}"; + var code2 = $"TestGetAllAsync_{Id.Create()}"; + + //添加数据1 + var dto = new CustomerDto2 { Code = code }; + await _stateManager.SaveAsync( dto ); + + //添加数据2 + var dto2 = new CustomerDto2 { Code = code2 }; + await _stateManager.SaveAsync( dto2 ); + + //获取数据并验证 + var result = await _stateManager.GetAllAsync(); + Assert.Contains( result, t => t.Code == code ); + Assert.Contains( result, t => t.Code == code2 ); + } + + #endregion + + #region SingleAsync + + /// + /// 测试获取指定类型的单条数据 + /// + [Fact] + public async Task TestSingleAsync() { + //变量定义 + var code = $"TestSingleAsync_{Id.Create()}"; + + //添加数据 + var dto = new CustomerDto2 { Code = code }; + await _stateManager.SaveAsync( dto ); + + //获取数据并验证 + var result = await _stateManager.Equal( t => t.Code, code ).SingleAsync(); + Assert.Equal( code, result.Code ); + } + + #endregion + + #region QueryAsync + + /// + /// 测试查询 - 1个相等条件 + /// + [Fact] + public async Task TestQueryAsync_1() { + //变量定义 + var code = $"TestQueryAsync_1_{Id.Create()}"; + var code2 = $"TestQueryAsync_1_{Id.Create()}"; + + //添加数据1 + var dto = new CustomerDto2 { Code = code }; + await _stateManager.SaveAsync( dto ); + + //添加数据2 + var dto2 = new CustomerDto2 { Code = code2 }; + await _stateManager.SaveAsync( dto2 ); + + //获取数据并验证 + var result = await _stateManager.Equal( "code", code ).QueryAsync(); + Assert.Single( result ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// 测试查询 - 1个相等条件 - 属性表达式 + /// + [Fact] + public async Task TestQueryAsync_2() { + //变量定义 + var code = $"TestQueryAsync_2_{Id.Create()}"; + var code2 = $"TestQueryAsync_2_{Id.Create()}"; + + //添加数据1 + var dto = new CustomerDto2 { Code = code }; + await _stateManager.SaveAsync( dto ); + + //添加数据2 + var dto2 = new CustomerDto2 { Code = code2 }; + await _stateManager.SaveAsync( dto2 ); + + //获取数据并验证 + var result = await _stateManager.Equal( t => t.Code, code ).QueryAsync(); + Assert.Single( result ); + Assert.Contains( result, t => t.Code == code ); + } + + /// + /// 测试查询 - 1个In条件 + /// + [Fact] + public async Task TestQueryAsync_3() { + //变量定义 + var code = $"TestQueryAsync_3_{Id.Create()}"; + var code2 = $"TestQueryAsync_3_{Id.Create()}"; + + //添加数据1 + var dto = new CustomerDto2 { Code = code }; + await _stateManager.SaveAsync( dto ); + + //添加数据2 + var dto2 = new CustomerDto2 { Code = code2 }; + await _stateManager.SaveAsync( dto2 ); + + //获取数据并验证 + var result = await _stateManager.In( "code", code, code2 ).QueryAsync(); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Code == code ); + Assert.Contains( result, t => t.Code == code2 ); + } + + /// + /// 测试查询 - 1个In条件 - 属性表达式 + /// + [Fact] + public async Task TestQueryAsync_4() { + //变量定义 + var code = $"TestQueryAsync_4_{Id.Create()}"; + var code2 = $"TestQueryAsync_4_{Id.Create()}"; + + //添加数据1 + var dto = new CustomerDto2 { Code = code }; + await _stateManager.SaveAsync( dto ); + + //添加数据2 + var dto2 = new CustomerDto2 { Code = code2 }; + await _stateManager.SaveAsync( dto2 ); + + //获取数据并验证 + var result = await _stateManager.In( t => t.Code, code, code2 ).QueryAsync(); + Assert.Equal( 2, result.Count ); + Assert.Contains( result, t => t.Code == code ); + Assert.Contains( result, t => t.Code == code2 ); + } + + /// + /// 测试查询 - 升序排序 + /// + [Fact] + public async Task TestQueryAsync_OrderBy() { + //变量定义 + var code = $"TestQueryAsync_OrderBy_{Id.Create()}"; + + //添加数据1 + var dto = new CustomerDto2 { Code = code, Name = "b" }; + await _stateManager.SaveAsync( dto ); + + //添加数据2 + var dto2 = new CustomerDto2 { Code = code, Name = "a" }; + await _stateManager.SaveAsync( dto2 ); + + //获取数据并验证 + var result = await _stateManager.OrderBy( t => t.Name ).Equal( "code", code ).QueryAsync(); + Assert.Equal( "a", result[0].Name ); + Assert.Equal( "b", result[1].Name ); + } + + /// + /// 测试查询 - 降序排序 + /// + [Fact] + public async Task TestQueryAsync_OrderByDescending() { + //变量定义 + var code = $"TestQueryAsync_OrderBy_{Id.Create()}"; + + //添加数据1 + var dto = new CustomerDto2 { Code = code, Name = "a" }; + await _stateManager.SaveAsync( dto ); + + //添加数据2 + var dto2 = new CustomerDto2 { Code = code, Name = "b" }; + await _stateManager.SaveAsync( dto2 ); + + //获取数据并验证 + var result = await _stateManager.OrderByDescending( t => t.Name ).Equal( "code", code ).QueryAsync(); + Assert.Equal( "b", result[0].Name ); + Assert.Equal( "a", result[1].Name ); + } + + #endregion + + #region PageQueryAsync + + /// + /// 测试分页查询 + /// + [Fact] + public async Task TestPageQueryAsync() { + //变量定义 + var code = $"TestPageQueryAsync_{Id.Create()}"; + + //添加数据 + await _stateManager.SaveAsync( new[] { + new CustomerDto2 { Code = code, Name = "a" }, + new CustomerDto2 { Code = code, Name = "b" }, + new CustomerDto2 { Code = code, Name = "c" }, + new CustomerDto2 { Code = code, Name = "d" }, + new CustomerDto2 { Code = code, Name = "e" } + } ); + + //分页查询第1页 + var query = new Pager( 1, 2, "name desc" ); + var result = await _stateManager.Equal( "code", code ).PageQueryAsync( query ); + Assert.Equal( 2, result.Data.Count ); + Assert.Equal( "e", result.Data[0].Name ); + Assert.Equal( "d", result.Data[1].Name ); + + //分页查询第2页 + query = new Pager( 2, 2, "name desc" ); + result = await _stateManager.Equal( "code", code ).PageQueryAsync( query ); + Assert.Equal( 2, result.Data.Count ); + Assert.Equal( "c", result.Data[0].Name ); + Assert.Equal( "b", result.Data[1].Name ); + + //分页查询第3页 + query = new Pager( 3, 2, "name desc" ); + result = await _stateManager.Equal( "code", code ).PageQueryAsync( query ); + Assert.Single( result.Data ); + Assert.Equal( "a", result.Data[0].Name ); + + //分页查询第4页 + query = new Pager( 4, 2, "name desc" ); + result = await _stateManager.Equal( "code", code ).PageQueryAsync( query ); + Assert.Empty( result.Data ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/StateManagements/StateManagerTest.cs b/test/Util.Microservices.Dapr.Tests.Integration/StateManagements/StateManagerTest.cs new file mode 100644 index 000000000..005f2c124 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/StateManagements/StateManagerTest.cs @@ -0,0 +1,333 @@ +using Util.Microservices.Dapr.Tests.Samples; + +namespace Util.Microservices.Dapr.Tests.StateManagements; + +/// +/// 状态管理测试 +/// +[Collection( "Global" )] +public partial class StateManagerTest { + /// + /// 状态管理操作 + /// + private readonly IStateManager _stateManager; + /// + /// 日志 + /// + private readonly ILogger _logger; + + /// + /// 测试初始化 + /// + public StateManagerTest( IStateManager stateManager, ILogger logger ) { + _stateManager = stateManager; + _logger = logger; + } + + /// + /// 测试 + /// + [Fact] + public async Task Test_1() { + //变量定义 + var key = $"key_{Id.Create()}"; + var dto = new CustomerDto { Code = "123" }; + + //添加数据 + await _stateManager.AddAsync( key, dto ); + + //获取数据并验证 + var result = await _stateManager.GetAsync( key ); + Assert.Equal( "123", result.Code ); + } + + /// + /// 测试并发标记 + /// + [Fact] + public async Task Test_2() { + //变量定义 + var key = $"key_{Id.Create()}"; + var dto = new CustomerDto { Code = "123" }; + + //添加数据 + await _stateManager.AddAsync( key, dto ); + + //获取数据 + var eTagData = await _stateManager.GetStateAndETagAsync( key ); + Assert.Equal( "123", eTagData.value.Code ); + + //修改数据 + dto.Code = "456"; + var isUpdate = await _stateManager.UpdateAsync( key, dto, eTagData.etag ); + + //获取数据并验证 + var result = await _stateManager.GetAsync( key ); + Assert.True( isUpdate ); + Assert.Equal( "456", result.Code ); + } + + /// + /// 测试并发标记 - 无效标记 + /// + [Fact] + public async Task Test_3() { + //变量定义 + var key = $"key_{Id.Create()}"; + var dto = new CustomerDto { Code = "123" }; + + //添加数据 + await _stateManager.AddAsync( key, dto ); + + //修改数据 + dto.Code = "456"; + var isUpdate = await _stateManager.UpdateAsync( key, dto, "" ); + Assert.False( isUpdate ); + } + + /// + /// 测试删除数据 + /// + [Fact] + public async Task Test_4() { + //变量定义 + var key = $"key_{Id.Create()}"; + var dto = new CustomerDto { Code = "123" }; + + //添加数据 + await _stateManager.AddAsync( key, dto ); + + //删除 + await _stateManager.RemoveAsync( key ); + + //获取数据并验证 + var result = await _stateManager.GetAsync( key ); + Assert.Null( result ); + } + + /// + /// 测试保存实体 + /// + [Fact] + public async Task Test_5() { + //变量定义 + var dto = new CustomerDto2 { Code = "123" }; + + //添加数据 + var key = await _stateManager.SaveAsync( dto ); + + //获取数据并验证 + var result = await _stateManager.GetAsync( key ); + Assert.Equal( "123", result.Code ); + Assert.False( result.ETag.IsEmpty() ); + _logger.LogInformation( $"Test_5.1.ETag:{result.ETag}" ); + + //修改数据 + result.Code = "456"; + await _stateManager.SaveAsync( result ); + + //获取数据并验证 + result = await _stateManager.GetAsync( key ); + Assert.Equal( "456", result.Code ); + _logger.LogInformation( $"Test_5.2.ETag:{result.ETag}" ); + } + + /// + /// 测试保存实体 - 传入key + /// + [Fact] + public async Task Test_6() { + //变量定义 + var key = $"key_{Id.Create()}"; + var dto = new CustomerDto2 { Code = "123" }; + + //添加数据 + await _stateManager.SaveAsync( dto, key: key ); + + //获取数据并验证 + var result = await _stateManager.GetAsync( key ); + Assert.Equal( "123", result.Code ); + Assert.False( result.ETag.IsEmpty() ); + + //修改数据 + result.Code = "456"; + await _stateManager.SaveAsync( result, key: key ); + + //获取数据并验证 + result = await _stateManager.GetAsync( key ); + Assert.Equal( "456", result.Code ); + } + + /// + /// 测试事务操作 + /// + [Fact] + public async Task Test_7() { + //添加数据1 + var key_1 = $"key_{Id.Create()}"; + var dto_1 = new CustomerDto { Code = "1" }; + await _stateManager.AddAsync( key_1, dto_1 ); + + //添加数据2 + var dto_2 = new CustomerDto2 { Code = "2" }; + var key_2 = await _stateManager.SaveAsync( dto_2 ); + + //开始事务 + _stateManager.BeginTransaction(); + + //添加数据3 + var key_3 = $"key_{Id.Create()}"; + var dto_3 = new CustomerDto { Code = "3" }; + await _stateManager.AddAsync( key_3, dto_3 ); + + //修改数据1 + var result1 = await _stateManager.GetStateAndETagAsync( key_1 ); + result1.value.Code = "4"; + await _stateManager.UpdateAsync( key_1, result1.value, result1.etag ); + + //删除数据2 + await _stateManager.RemoveAsync( key_2 ); + + //提交事务 + await _stateManager.CommitAsync(); + + //获取数据并验证 + var result = await _stateManager.GetAsync( key_1 ); + Assert.Equal( "4", result.Code ); + result = await _stateManager.GetAsync( key_2 ); + Assert.Null( result ); + result = await _stateManager.GetAsync( key_3 ); + Assert.Equal( "3", result.Code ); + } + + /// + /// 测试事务操作 - 未提交 - 添加 + /// + [Fact] + public async Task Test_8() { + //开始事务 + _stateManager.BeginTransaction(); + + //添加数据1 + var key_1 = $"key_{Id.Create()}"; + var dto_1 = new CustomerDto { Code = "1" }; + await _stateManager.AddAsync( key_1, dto_1 ); + + //获取数据并验证 + var result = await _stateManager.GetAsync( key_1 ); + Assert.Null( result ); + } + + /// + /// 测试事务操作 - 未提交 - 修改 + /// + [Fact] + public async Task Test_9() { + //添加数据 + var key_1 = $"key_{Id.Create()}"; + var dto_1 = new CustomerDto { Code = "1" }; + await _stateManager.AddAsync( key_1, dto_1 ); + + //开始事务 + _stateManager.BeginTransaction(); + + //修改数据 + var result = await _stateManager.GetStateAndETagAsync( key_1 ); + result.value.Code = "2"; + await _stateManager.UpdateAsync( key_1, result.value, result.etag ); + + //获取数据并验证 + dto_1 = await _stateManager.GetAsync( key_1 ); + Assert.Equal( "1", dto_1.Code ); + } + + /// + /// 测试事务操作 - 未提交 - 删除 + /// + [Fact] + public async Task Test_10() { + //添加数据 + var key_1 = $"key_{Id.Create()}"; + var dto_1 = new CustomerDto { Code = "1" }; + await _stateManager.AddAsync( key_1, dto_1 ); + + //开始事务 + _stateManager.BeginTransaction(); + + //修改数据 + await _stateManager.RemoveAsync( key_1 ); + + //获取数据并验证 + dto_1 = await _stateManager.GetAsync( key_1 ); + Assert.Equal( "1", dto_1.Code ); + } + + /// + /// 测试事务操作 - 未提交 - 保存 - 添加 + /// + [Fact] + public async Task Test_11() { + //开始事务 + _stateManager.BeginTransaction(); + + //添加数据 + var dto = new CustomerDto2 { Code = "1" }; + var key = await _stateManager.SaveAsync( dto ); + + //获取数据并验证 + var result = await _stateManager.GetAsync( key ); + Assert.Null( result ); + } + + /// + /// 测试通过标识删除数据 + /// + [Fact] + public async Task Test_12() { + //变量定义 + var key = $"key_{Id.Create()}"; + var dto = new CustomerDto2 { Code = "123" }; + + //添加数据 + await _stateManager.AddAsync( key, dto ); + + //删除 + await _stateManager.RemoveByIdAsync( dto.Id ); + + //获取数据并验证 + var result = await _stateManager.GetAsync( key ); + Assert.Null( result ); + } + + /// + /// 测试添加int类型数据 + /// + [Fact] + public async Task Test_13() { + //变量定义 + var key = $"key_{Id.Create()}"; + + //添加数据 + await _stateManager.AddAsync( key, 1 ); + + //获取数据并验证 + var result = await _stateManager.GetAsync( key ); + Assert.Equal( 1, result ); + } + + /// + /// 测试添加string类型数据 + /// + [Fact] + public async Task Test_14() { + //变量定义 + var key = $"key_{Id.Create()}"; + + //添加数据 + await _stateManager.AddAsync( key, "a" ); + + //获取数据并验证 + var result = await _stateManager.GetAsync( key ); + Assert.Equal( "a", result ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Usings.cs b/test/Util.Microservices.Dapr.Tests.Integration/Usings.cs new file mode 100644 index 000000000..bcc2b546c --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Usings.cs @@ -0,0 +1,14 @@ +global using System; +global using Xunit; +global using Xunit.Abstractions; +global using Xunit.DependencyInjection.Logging; +global using System.Text; +global using System.Threading.Tasks; +global using System.ComponentModel; +global using System.ComponentModel.DataAnnotations; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; +global using Util.Helpers; +global using Util.Microservices.Dapr.CommandLines; +global using Util.Http; \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/Util.Microservices.Dapr.Tests.Integration.csproj b/test/Util.Microservices.Dapr.Tests.Integration/Util.Microservices.Dapr.Tests.Integration.csproj new file mode 100644 index 000000000..9af974a55 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/Util.Microservices.Dapr.Tests.Integration.csproj @@ -0,0 +1,70 @@ + + + + $(NetTargetFramework) + false + Util.Microservices.Dapr.Tests + Util.Microservices.Dapr.Tests.Startup + + + + + + + + + Always + true + PreserveNewest + + + Always + true + PreserveNewest + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + Always + + + Always + + + Always + + + Always + + + Always + + + + diff --git a/test/Util.Microservices.Dapr.Tests.Integration/appsettings.Development.json b/test/Util.Microservices.Dapr.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..2d5594282 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/appsettings.Development.json @@ -0,0 +1,31 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Trace" + } + }, + "Serilog": { + "WriteTo": [ + { + "Name": "Seq", + "Args": { + "serverUrl": "http://localhost:5380" + } + } + ] + }, + "SeqUrl": "http://localhost:5380", + "DaprPorts": { + "WebApi": { + "AppPort": "51472", + "HttpPort": "51473", + "GrpcPort": "51474" + }, + "Pubsub": { + "AppPort": "51475", + "HttpPort": "51476", + "GrpcPort": "51477" + } + } +} diff --git a/test/Util.Microservices.Dapr.Tests.Integration/appsettings.json b/test/Util.Microservices.Dapr.Tests.Integration/appsettings.json new file mode 100644 index 000000000..03565085b --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Trace" + } + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests.Integration/clear.ps1 b/test/Util.Microservices.Dapr.Tests.Integration/clear.ps1 new file mode 100644 index 000000000..fc85683c8 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests.Integration/clear.ps1 @@ -0,0 +1,6 @@ +Write-Host "Stop-Process -Name Util.Microservices.Dapr*" +Stop-Process -Name "Util.Microservices.Dapr*" +Write-Host "Stop-Process -Name dapr*" +Stop-Process -Name "dapr*" +Write-Host "Stop-Process -Name dotnet*" +Stop-Process -Name "dotnet*" \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests/CommandLines/DaprRunCommandTest.cs b/test/Util.Microservices.Dapr.Tests/CommandLines/DaprRunCommandTest.cs new file mode 100644 index 000000000..45c100f27 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests/CommandLines/DaprRunCommandTest.cs @@ -0,0 +1,83 @@ +namespace Util.Microservices.Dapr.Tests.CommandLines; + +/// +/// dapr run启动命令测试 +/// +public class DaprRunCommandTest { + /// + /// 输出操作 + /// + private readonly ITestOutputHelper _output; + + /// + /// 测试初始化 + /// + public DaprRunCommandTest( ITestOutputHelper output ) { + _output = output; + } + + /// + /// 测试默认设置 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "dapr run --app-id app" ); + var command = DaprRunCommand.Create( "app" ).GetDebugText(); + _output.WriteLine( command ); + Assert.Contains( result.ToString(), command ); + } + + /// + /// 测试设置命令参数 + /// + [Fact] + public void TestArguments() { + var result = new StringBuilder(); + result.Append( "dapr run --app-id app " ); + result.Append( "--app-port 80 " ); + result.Append( "--app-protocol grpc " ); + result.Append( "--dapr-http-port 81 " ); + result.Append( "--dapr-grpc-port 82 " ); + result.Append( "--metrics-port 83 " ); + result.Append( "--resources-path components-path " ); + result.Append( "--config config-path " ); + result.Append( "--log-level debug " ); + result.Append( "-- dotnet run --project ../app.csproj " ); + result.Append( "--urls http://localhost:80" ); + var command = DaprRunCommand.Create( "app" ) + .UseFreePorts() + .AppPort( 80 ).AppProtocol( "grpc" ) + .DaprHttpPort( 81 ).DaprGrpcPort( 82 ).MetricsPort( 83 ) + .ComponentsPath( "components-path" ) + .ConfigPath( "config-path" ) + .Project( "../app.csproj" ) + .GetDebugText(); + Assert.Equal( result.ToString(), command ); + } + + /// + /// 测试设置命令参数 + /// + [Fact] + public void TestArguments_2() { + var result = new StringBuilder(); + result.Append( "dapr run --app-id app " ); + result.Append( "--app-port 80 " ); + result.Append( "--app-protocol grpc " ); + result.Append( "--dapr-http-port 81 " ); + result.Append( "--dapr-grpc-port 82 " ); + result.Append( "--metrics-port 83 " ); + result.Append( "--resources-path components-path " ); + result.Append( "--config config-path " ); + result.Append( "--log-level debug" ); + var command = DaprRunCommand.Create( "app" ) + .UseFreePorts() + .AppPort( 80 ).AppProtocol( "grpc" ) + .DaprHttpPort( 81 ).DaprGrpcPort( 82 ).MetricsPort( 83 ) + .ComponentsPath( "components-path" ) + .ConfigPath( "config-path" ) + .GetDebugText(); + Assert.Equal( result.ToString(), command ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests/Events/IntegrationEventManagerTest.cs b/test/Util.Microservices.Dapr.Tests/Events/IntegrationEventManagerTest.cs new file mode 100644 index 000000000..2ae8e8abc --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests/Events/IntegrationEventManagerTest.cs @@ -0,0 +1,936 @@ +using System.Linq; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Util.Exceptions; +using Util.Microservices.Dapr.Tests.Samples; + +namespace Util.Microservices.Dapr.Tests.Events; + +/// +/// 集成事件管理器测试 +/// +public class IntegrationEventManagerTest { + + #region 测试初始化 + + /// + /// 集成事件管理器 + /// + private readonly IntegrationEventManager _manager; + /// + /// 模拟集成事件日志记录存储器 + /// + private readonly Mock _mockStore; + /// + /// 模拟用户会话 + /// + private readonly Mock _mockSession; + /// + /// 模拟获取应用标识 + /// + private readonly Mock _mockGetAppId; + + /// + /// 测试初始化 + /// + public IntegrationEventManagerTest() { + _mockStore = new Mock(); + _mockSession = new Mock(); + _mockGetAppId = new Mock(); + var options = new Mock>(); + options.Setup( t => t.Value ).Returns( new DaprOptions { Pubsub = { MaxRetry = 10 } } ); + var log = new Mock>(); + _manager = new IntegrationEventManager( _mockStore.Object, _mockSession.Object, _mockGetAppId.Object, options.Object, log.Object ); + } + + #endregion + + #region CanSubscriptionAsync + + /// + /// 测试是否允许处理订阅 - 首次订阅 + /// + [Fact] + public async Task TestCanSubscriptionAsync_1() { + //参数定义 + var eventId = "123"; + var appId = "appid_sub"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Published + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog ); + + //创建订阅日志 + var result = await _manager.CanSubscriptionAsync( eventId ); + + //验证 + Assert.True( result ); + } + + /// + /// 测试是否允许处理订阅 - 多次调用同一个订阅端口 - 前一个调用正在执行,不允许处理 + /// + [Fact] + public async Task TestCanSubscriptionAsync_2() { + //参数定义 + var eventId = "123"; + var appId = "appid_sub"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Published, + SubscriptionLogs = { new SubscriptionLog { AppId = appId, State = SubscriptionState.Processing } } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog ); + + //创建订阅日志 + var result = await _manager.CanSubscriptionAsync( eventId ); + + //验证 + Assert.False( result ); + } + + /// + /// 测试是否允许处理订阅 - 多次调用同一个订阅端口 - 前一个调用已成功完成,不允许处理 + /// + [Fact] + public async Task TestCanSubscriptionAsync_3() { + //参数定义 + var eventId = "123"; + var appId = "appid_sub"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Published, + SubscriptionLogs = { new SubscriptionLog { AppId = appId, State = SubscriptionState.Success } } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog ); + + //创建订阅日志 + var result = await _manager.CanSubscriptionAsync( eventId ); + + //验证 + Assert.False( result ); + } + + /// + /// 测试是否允许处理订阅 - 多次调用同一个订阅端口 - 前一个调用已失败,但重试次数还没有达到最大值,允许处理 + /// + [Fact] + public async Task TestCanSubscriptionAsync_4() { + //参数定义 + var eventId = "123"; + var appId = "appid_sub"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Published, + SubscriptionLogs = { new SubscriptionLog { AppId = appId, State = SubscriptionState.Fail, RetryCount = 9 } } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog ); + + //创建订阅日志 + var result = await _manager.CanSubscriptionAsync( eventId ); + + //验证 + Assert.True( result ); + } + + /// + /// 测试是否允许处理订阅 - 多次调用同一个订阅端口 - 前一个调用已失败,重试次数达到最大值,不允许处理 + /// + [Fact] + public async Task TestCanSubscriptionAsync_5() { + //参数定义 + var eventId = "123"; + var appId = "appid_sub"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Published, + SubscriptionLogs = { new SubscriptionLog { AppId = appId, State = SubscriptionState.Fail, RetryCount = 10 } } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog ); + + //创建订阅日志 + var result = await _manager.CanSubscriptionAsync( eventId ); + + //验证 + Assert.False( result ); + } + + #endregion + + #region CreatePublishLogAsync + + /// + /// 测试创建集成事件发布日志记录 + /// + [Fact] + public async Task TestCreatePublishLogAsync() { + //设置模拟时间 + var time = new DateTime( 2000, 1, 1, 1, 1, 1 ); + Time.SetTime( time ); + + //参数定义 + var testEvent = new TestEvent( "code", "name" ); + var cloudEvent = new CloudEvent( testEvent.EventId, testEvent ); + cloudEvent.Headers.Add( "c", "2" ); + var metadata = new Dictionary { { "a", "1" } }; + var argument = new PubsubArgument( "pubsub2", "topic", cloudEvent, metadata ); + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( "appid" ); + _mockSession.Setup( t => t.UserId ).Returns( "userid" ); + + //创建发布日志 + var result = await _manager.CreatePublishLogAsync( argument ); + + //验证 + _mockStore.Verify( store => store.SaveAsync( It.Is( log => log == result ), It.IsAny() ) ); + Assert.Equal( testEvent.EventId, result.Id ); + Assert.Equal( "appid", result.AppId ); + Assert.Equal( "userid", result.UserId ); + Assert.Equal( "pubsub2", result.PubsubName ); + Assert.Equal( "topic", result.Topic ); + Assert.Equal( EventState.Published, result.State ); + Assert.Equal( time, result.PublishTime ); + Assert.Equal( time, result.LastModificationTime ); + Assert.Empty( result.SubscriptionLogs ); + Assert.Equal( "a", result.GetValue().Metadata.First().Key ); + Assert.Equal( "1", result.GetValue().Metadata.First().Value ); + Assert.Equal( "c", result.GetValue().GetEventData>().Headers.First().Key ); + Assert.Equal( "2", result.GetValue().GetEventData>().Headers.First().Value ); + Assert.Equal( "code", result.GetValue().GetEventData>().GetData().Code ); + Assert.Equal( "name", result.GetValue().GetEventData>().GetData().Name ); + } + + #endregion + + #region CreateSubscriptionLogAsync + + /// + /// 测试创建集成事件订阅日志记录 - 首次触发订阅 + /// + [Fact] + public async Task TestCreateSubscriptionLogAsync_1() { + //设置模拟时间 + var time = new DateTime( 2000, 1, 1, 1, 1, 1 ); + Time.SetTime( time ); + + //参数定义 + var eventId = "123"; + var appId = "appid_sub"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Published + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog ); + + //创建订阅日志 + var result = await _manager.CreateSubscriptionLogAsync( eventId, "routeUrl" ); + + //验证 + _mockStore.Verify( t => t.SaveAsync( It.Is( log => log == result ), It.IsAny() ), Times.Once ); + Assert.Equal( EventState.Processing, result.State ); + Assert.Equal( time, result.LastModificationTime ); + Assert.Single( result.SubscriptionLogs ); + var subLog = result.SubscriptionLogs[0]; + Assert.Equal( appId, subLog.AppId ); + Assert.Equal( "routeUrl", subLog.RouteUrl ); + Assert.Null( subLog.RetryCount ); + Assert.Equal( SubscriptionState.Processing, subLog.State ); + Assert.Equal( time, subLog.SubscriptionTime ); + Assert.Equal( time, subLog.LastModificationTime ); + Assert.Empty( subLog.RetryLogs ); + } + + /// + /// 测试创建集成事件订阅日志记录 - 多次触发订阅 - 之前已成功完成 + /// + [Fact] + public async Task TestCreateSubscriptionLogAsync_2() { + //设置模拟时间 + var time = new DateTime( 2000, 1, 1, 1, 1, 1 ); + var time2 = new DateTime( 2111, 1, 1, 1, 1, 1 ); + Time.SetTime( time ); + + //参数定义 + var eventId = "123"; + var appId = "appid_sub"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Processing, + SubscriptionLogs = { + new SubscriptionLog{ AppId = appId,State = SubscriptionState.Success,RouteUrl = "routeUrl",SubscriptionTime = time2,LastModificationTime = time2} + } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog ); + + //创建订阅日志 + var result = await _manager.CreateSubscriptionLogAsync( eventId, "routeUrl" ); + + //验证 + _mockStore.Verify( t => t.SaveAsync( It.Is( log => log == result ), It.IsAny() ), Times.Never ); + Assert.Equal( EventState.Processing, result.State ); + Assert.Single( result.SubscriptionLogs ); + var subLog = result.SubscriptionLogs[0]; + Assert.Null( subLog.RetryCount ); + Assert.Equal( SubscriptionState.Success, subLog.State ); + Assert.Equal( time2, subLog.SubscriptionTime ); + Assert.Equal( time2, subLog.LastModificationTime ); + Assert.Empty( subLog.RetryLogs ); + } + + /// + /// 测试创建集成事件订阅日志记录 - 多次触发同一个订阅 - 之前的订阅正在处理 + /// + [Fact] + public async Task TestCreateSubscriptionLogAsync_3() { + //设置模拟时间 + var time = new DateTime( 2000, 1, 1, 1, 1, 1 ); + var time2 = new DateTime( 2111, 1, 1, 1, 1, 1 ); + Time.SetTime( time ); + + //参数定义 + var eventId = "123"; + var appId = "appid_sub"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Processing, + SubscriptionLogs = { + new SubscriptionLog{ AppId = appId,State = SubscriptionState.Processing,SubscriptionTime = time2,LastModificationTime = time2 } + } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog ); + + //创建订阅日志 + var result = await _manager.CreateSubscriptionLogAsync( eventId, "routeUrl" ); + + //验证 + _mockStore.Verify( t => t.SaveAsync( It.Is( log => log == result ), It.IsAny() ), Times.Never ); + Assert.Equal( EventState.Processing, result.State ); + Assert.Single( result.SubscriptionLogs ); + var subLog = result.SubscriptionLogs[0]; + Assert.Equal( appId, subLog.AppId ); + Assert.Null( subLog.RetryCount ); + Assert.Equal( SubscriptionState.Processing, subLog.State ); + Assert.Equal( time2, subLog.SubscriptionTime ); + Assert.Equal( time2, subLog.LastModificationTime ); + Assert.Empty( subLog.RetryLogs ); + } + + /// + /// 测试创建集成事件订阅日志记录 - 多次触发同一个订阅 - 之前的订阅已失败,第一次重试 + /// + [Fact] + public async Task TestCreateSubscriptionLogAsync_4() { + //设置模拟时间 + var time = new DateTime( 2000, 1, 1, 1, 1, 1 ); + var time2 = new DateTime( 2111, 1, 1, 1, 1, 1 ); + Time.SetTime( time ); + + //参数定义 + var eventId = "123"; + var appId = "appid_sub"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Fail, + SubscriptionLogs = { + new SubscriptionLog{ + AppId = appId, + State = SubscriptionState.Fail, + SubscriptionTime = time2, + LastModificationTime = time2, + RetryCount = 0, + RetryLogs = { new SubscriptionRetryLog{Number = 1,Message = "a" }} + } + } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog ); + + //创建订阅日志 + var result = await _manager.CreateSubscriptionLogAsync( eventId, "routeUrl" ); + + //验证 + _mockStore.Verify( t => t.SaveAsync( It.Is( log => log == result ), It.IsAny() ), Times.Once ); + Assert.Equal( EventState.Processing, result.State ); + Assert.Single( result.SubscriptionLogs ); + var subLog = result.SubscriptionLogs[0]; + Assert.Equal( 1, subLog.RetryCount ); + Assert.Equal( SubscriptionState.Processing, subLog.State ); + Assert.Equal( time2, subLog.SubscriptionTime ); + Assert.Equal( time, subLog.LastModificationTime ); + Assert.Single( subLog.RetryLogs ); + Assert.Equal( 1, subLog.RetryLogs.First().Number ); + Assert.Equal( "a", subLog.RetryLogs.First().Message ); + Assert.Equal( time, subLog.RetryLogs.First().RetryTime ); + } + + /// + /// 测试创建集成事件订阅日志记录 - 多次触发同一个订阅 - 之前的订阅已失败,重试已达到最大值 + /// + [Fact] + public async Task TestCreateSubscriptionLogAsync_5() { + //设置模拟时间 + var time = new DateTime( 2000, 1, 1, 1, 1, 1 ); + var time2 = new DateTime( 2111, 1, 1, 1, 1, 1 ); + Time.SetTime( time ); + + //参数定义 + var eventId = "123"; + var appId = "appid_sub"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Fail, + SubscriptionLogs = { + new SubscriptionLog{ + AppId = appId, + State = SubscriptionState.Fail, + SubscriptionTime = time2, + LastModificationTime = time2, + RetryCount = 10 + } + } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog ); + + //创建订阅日志 + var result = await _manager.CreateSubscriptionLogAsync( eventId, "routeUrl" ); + + //验证 + _mockStore.Verify( t => t.SaveAsync( It.Is( log => log == result ), It.IsAny() ), Times.Never ); + Assert.Equal( EventState.Fail, result.State ); + Assert.Single( result.SubscriptionLogs ); + var subLog = result.SubscriptionLogs[0]; + Assert.Equal( 10, subLog.RetryCount ); + Assert.Equal( SubscriptionState.Fail, subLog.State ); + Assert.Equal( time2, subLog.SubscriptionTime ); + Assert.Equal( time2, subLog.LastModificationTime ); + } + + /// + /// 测试创建集成事件订阅日志记录 - 并发处理 + /// + [Fact] + public async Task TestCreateSubscriptionLogAsync_6() { + //设置模拟时间 + var time = new DateTime( 2000, 1, 1, 1, 1, 1 ); + Time.SetTime( time ); + + //参数定义 + var eventId = "123"; + var appId = "appid_1"; + var appId2 = "appid_2"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Published, + ETag = "1" + }; + var eventLog2 = new IntegrationEventLog { + Id = eventId, + State = EventState.Published, + ETag = "2", + SubscriptionLogs = { new SubscriptionLog { AppId = appId2 } } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog2 ); + _mockStore.Setup( t => t.SaveAsync( It.Is( log => log.ETag == "1" ), It.IsAny() ) ).Throws(); + + //创建订阅日志 + var result = await _manager.CreateSubscriptionLogAsync( eventLog, "routeUrl" ); + + //验证 + _mockStore.Verify( t => t.SaveAsync( It.Is( log => log == result ), It.IsAny() ), Times.Once ); + Assert.Equal( EventState.Processing, result.State ); + Assert.Equal( time, result.LastModificationTime ); + Assert.Equal( 2, result.SubscriptionLogs.Count ); + Assert.Equal( appId2, result.SubscriptionLogs[0].AppId ); + var subLog = result.SubscriptionLogs[1]; + Assert.Equal( appId, subLog.AppId ); + Assert.Equal( "routeUrl", subLog.RouteUrl ); + Assert.Null( subLog.RetryCount ); + Assert.Equal( SubscriptionState.Processing, subLog.State ); + Assert.Equal( time, subLog.SubscriptionTime ); + Assert.Equal( time, subLog.LastModificationTime ); + Assert.Empty( subLog.RetryLogs ); + } + + #endregion + + #region SubscriptionSuccessAsync + + /// + /// 测试集成事件订阅成功 - 一个订阅 - 成功 + /// + [Fact] + public async Task TestSubscriptionSuccessAsync_1() { + //设置模拟时间 + var time = new DateTime( 2000, 1, 1, 1, 1, 1 ); + Time.SetTime( time ); + + //参数定义 + var eventId = "123"; + var appId = "appid_sub"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Processing, + SubscriptionLogs = { new SubscriptionLog { AppId = appId, State = SubscriptionState.Processing } } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog ); + + //订阅成功 + var result = await _manager.SubscriptionSuccessAsync( eventId ); + + //验证 + _mockStore.Verify( t => t.SaveAsync( It.Is( log => log == result ), It.IsAny() ), Times.Once ); + Assert.Equal( EventState.Success, result.State ); + Assert.Equal( time, result.LastModificationTime ); + Assert.Single( result.SubscriptionLogs ); + var subLog = result.SubscriptionLogs[0]; + Assert.Null( subLog.RetryCount ); + Assert.Equal( SubscriptionState.Success, subLog.State ); + Assert.Equal( time, subLog.LastModificationTime ); + Assert.Empty( subLog.RetryLogs ); + } + + /// + /// 测试集成事件订阅成功 - 2个订阅 - 都成功 + /// + [Fact] + public async Task TestSubscriptionSuccessAsync_2() { + //设置模拟时间 + var time = new DateTime( 2000, 1, 1, 1, 1, 1 ); + Time.SetTime( time ); + + //参数定义 + var eventId = "123"; + var appId = "appid_sub"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Processing, + SubscriptionLogs = { + new SubscriptionLog { AppId = appId, State = SubscriptionState.Processing }, + new SubscriptionLog { AppId = "a", State = SubscriptionState.Success } + } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog ); + + //订阅成功 + var result = await _manager.SubscriptionSuccessAsync( eventId ); + + //验证 + _mockStore.Verify( t => t.SaveAsync( It.Is( log => log == result ), It.IsAny() ), Times.Once ); + Assert.Equal( EventState.Success, result.State ); + Assert.Equal( time, result.LastModificationTime ); + Assert.Equal( 2, result.SubscriptionLogs.Count ); + var subLog = result.SubscriptionLogs[0]; + Assert.Null( subLog.RetryCount ); + Assert.Equal( SubscriptionState.Success, subLog.State ); + Assert.Equal( time, subLog.LastModificationTime ); + Assert.Empty( subLog.RetryLogs ); + } + + /// + /// 测试集成事件订阅成功 - 2个订阅 - 前一个处理中 + /// + [Fact] + public async Task TestSubscriptionSuccessAsync_3() { + //设置模拟时间 + var time = new DateTime( 2000, 1, 1, 1, 1, 1 ); + Time.SetTime( time ); + + //参数定义 + var eventId = "123"; + var appId = "appid_sub"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Processing, + SubscriptionLogs = { + new SubscriptionLog { AppId = appId, State = SubscriptionState.Processing }, + new SubscriptionLog { AppId = "a", State = SubscriptionState.Processing } + } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog ); + + //订阅成功 + var result = await _manager.SubscriptionSuccessAsync( eventId ); + + //验证 + _mockStore.Verify( t => t.SaveAsync( It.Is( log => log == result ), It.IsAny() ), Times.Once ); + Assert.Equal( EventState.Processing, result.State ); + Assert.Equal( time, result.LastModificationTime ); + Assert.Equal( 2, result.SubscriptionLogs.Count ); + var subLog = result.SubscriptionLogs[0]; + Assert.Null( subLog.RetryCount ); + Assert.Equal( SubscriptionState.Success, subLog.State ); + Assert.Equal( time, subLog.LastModificationTime ); + Assert.Empty( subLog.RetryLogs ); + } + + /// + /// 测试集成事件订阅成功 - 2个订阅 - 前一个失败 + /// + [Fact] + public async Task TestSubscriptionSuccessAsync_4() { + //设置模拟时间 + var time = new DateTime( 2000, 1, 1, 1, 1, 1 ); + Time.SetTime( time ); + + //参数定义 + var eventId = "123"; + var appId = "appid_sub"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Processing, + SubscriptionLogs = { + new SubscriptionLog { AppId = appId, State = SubscriptionState.Processing }, + new SubscriptionLog { AppId = "a", State = SubscriptionState.Fail } + } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog ); + + //订阅成功 + var result = await _manager.SubscriptionSuccessAsync( eventId ); + + //验证 + _mockStore.Verify( t => t.SaveAsync( It.Is( log => log == result ), It.IsAny() ), Times.Once ); + Assert.Equal( EventState.Fail, result.State ); + Assert.Equal( time, result.LastModificationTime ); + Assert.Equal( 2, result.SubscriptionLogs.Count ); + var subLog = result.SubscriptionLogs[0]; + Assert.Null( subLog.RetryCount ); + Assert.Equal( SubscriptionState.Success, subLog.State ); + Assert.Equal( time, subLog.LastModificationTime ); + Assert.Empty( subLog.RetryLogs ); + } + + /// + /// 测试集成事件订阅成功 - 并发处理 + /// + [Fact] + public async Task TestSubscriptionSuccessAsync_5() { + //设置模拟时间 + var time = new DateTime( 2000, 1, 1, 1, 1, 1 ); + Time.SetTime( time ); + + //参数定义 + var eventId = "123"; + var appId = "appid_1"; + var appId2 = "appid_2"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Processing, + ETag = "1", + SubscriptionLogs = { + new SubscriptionLog { AppId = appId, State = SubscriptionState.Processing }, + new SubscriptionLog { AppId = appId2, State = SubscriptionState.Processing } + } + }; + var eventLog2 = new IntegrationEventLog { + Id = eventId, + State = EventState.Processing, + ETag = "2", + SubscriptionLogs = { + new SubscriptionLog { AppId = appId, State = SubscriptionState.Processing }, + new SubscriptionLog { AppId = appId2, State = SubscriptionState.Success } + } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog2 ); + _mockStore.Setup( t => t.SaveAsync( It.Is( log => log.ETag == "1" ), It.IsAny() ) ).Throws(); + + //订阅成功 + var result = await _manager.SubscriptionSuccessAsync( eventLog ); + + //验证 + _mockStore.Verify( t => t.SaveAsync( It.Is( log => log == result ), It.IsAny() ), Times.Once ); + Assert.Equal( EventState.Success, result.State ); + Assert.Equal( time, result.LastModificationTime ); + Assert.Equal( 2, result.SubscriptionLogs.Count ); + var subLog = result.SubscriptionLogs[0]; + Assert.Null( subLog.RetryCount ); + Assert.Equal( SubscriptionState.Success, subLog.State ); + Assert.Equal( time, subLog.LastModificationTime ); + } + + #endregion + + #region SubscriptionFailAsync + + /// + /// 测试集成事件订阅失败 - 一个订阅 - 失败 + /// + [Fact] + public async Task TestSubscriptionFailAsync_1() { + //设置模拟时间 + var time = new DateTime( 2000, 1, 1, 1, 1, 1 ); + Time.SetTime( time ); + + //参数定义 + var eventId = "123"; + var appId = "appid_sub"; + var message = "a"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Processing, + SubscriptionLogs = { new SubscriptionLog { AppId = appId, State = SubscriptionState.Processing } } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog ); + + //订阅失败 + var result = await _manager.SubscriptionFailAsync( eventId, message ); + + //验证 + _mockStore.Verify( t => t.SaveAsync( It.Is( log => log == result ), It.IsAny() ), Times.Once ); + Assert.Equal( EventState.Fail, result.State ); + Assert.Equal( time, result.LastModificationTime ); + Assert.Single( result.SubscriptionLogs ); + var subLog = result.SubscriptionLogs[0]; + Assert.Null( subLog.RetryCount ); + Assert.Equal( SubscriptionState.Fail, subLog.State ); + Assert.Equal( time, subLog.LastModificationTime ); + Assert.Single( subLog.RetryLogs ); + var retryLog = subLog.RetryLogs[0]; + Assert.Equal( 1, retryLog.Number ); + Assert.Equal( message, retryLog.Message ); + Assert.Null( retryLog.RetryTime ); + } + + /// + /// 测试集成事件订阅失败 - 2个订阅 - 一个处理中,一个失败 + /// + [Fact] + public async Task TestSubscriptionFailAsync_2() { + //设置模拟时间 + var time = new DateTime( 2000, 1, 1, 1, 1, 1 ); + Time.SetTime( time ); + + //参数定义 + var eventId = "123"; + var appId = "appid_sub"; + var message = "a"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Processing, + SubscriptionLogs = { + new SubscriptionLog { AppId = appId, State = SubscriptionState.Processing }, + new SubscriptionLog { AppId = "app", State = SubscriptionState.Processing } + } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog ); + + //订阅失败 + var result = await _manager.SubscriptionFailAsync( eventId, message ); + + //验证 + _mockStore.Verify( t => t.SaveAsync( It.Is( log => log == result ), It.IsAny() ), Times.Once ); + Assert.Equal( EventState.Processing, result.State ); + Assert.Equal( time, result.LastModificationTime ); + Assert.Equal( 2, result.SubscriptionLogs.Count ); + var subLog = result.SubscriptionLogs[0]; + Assert.Null( subLog.RetryCount ); + Assert.Equal( SubscriptionState.Fail, subLog.State ); + Assert.Equal( time, subLog.LastModificationTime ); + Assert.Single( subLog.RetryLogs ); + var retryLog = subLog.RetryLogs[0]; + Assert.Equal( 1, retryLog.Number ); + Assert.Equal( message, retryLog.Message ); + Assert.Null( retryLog.RetryTime ); + } + + /// + /// 测试集成事件订阅失败 - 2个订阅 - 一个成功,一个失败 + /// + [Fact] + public async Task TestSubscriptionFailAsync_3() { + //设置模拟时间 + var time = new DateTime( 2000, 1, 1, 1, 1, 1 ); + Time.SetTime( time ); + + //参数定义 + var eventId = "123"; + var appId = "appid_sub"; + var message = "a"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Processing, + SubscriptionLogs = { + new SubscriptionLog { AppId = appId, State = SubscriptionState.Processing }, + new SubscriptionLog { AppId = "app", State = SubscriptionState.Success } + } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog ); + + //订阅失败 + var result = await _manager.SubscriptionFailAsync( eventId, message ); + + //验证 + _mockStore.Verify( t => t.SaveAsync( It.Is( log => log == result ), It.IsAny() ), Times.Once ); + Assert.Equal( EventState.Fail, result.State ); + Assert.Equal( time, result.LastModificationTime ); + Assert.Equal( 2, result.SubscriptionLogs.Count ); + var subLog = result.SubscriptionLogs[0]; + Assert.Null( subLog.RetryCount ); + Assert.Equal( SubscriptionState.Fail, subLog.State ); + Assert.Equal( time, subLog.LastModificationTime ); + Assert.Single( subLog.RetryLogs ); + var retryLog = subLog.RetryLogs[0]; + Assert.Equal( 1, retryLog.Number ); + Assert.Equal( message, retryLog.Message ); + Assert.Null( retryLog.RetryTime ); + } + + /// + /// 测试集成事件订阅失败 - 1个订阅 - 2个重试记录 + /// + [Fact] + public async Task TestSubscriptionFailAsync_4() { + //设置模拟时间 + var time = new DateTime( 2000, 1, 1, 1, 1, 1 ); + Time.SetTime( time ); + + //参数定义 + var eventId = "123"; + var appId = "appid_sub"; + var message = "a"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Processing, + SubscriptionLogs = { + new SubscriptionLog { AppId = appId, State = SubscriptionState.Processing,RetryLogs = { new SubscriptionRetryLog{ Number = 1,Message = "m" } } } + } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog ); + + //订阅失败 + var result = await _manager.SubscriptionFailAsync( eventId, message ); + + //验证 + _mockStore.Verify( t => t.SaveAsync( It.Is( log => log == result ), It.IsAny() ), Times.Once ); + Assert.Equal( EventState.Fail, result.State ); + Assert.Equal( time, result.LastModificationTime ); + Assert.Single( result.SubscriptionLogs ); + var subLog = result.SubscriptionLogs[0]; + Assert.Null( subLog.RetryCount ); + Assert.Equal( SubscriptionState.Fail, subLog.State ); + Assert.Equal( time, subLog.LastModificationTime ); + Assert.Equal( 2, subLog.RetryLogs.Count ); + var retryLog = subLog.RetryLogs[1]; + Assert.Equal( 2, retryLog.Number ); + Assert.Equal( message, retryLog.Message ); + Assert.Null( retryLog.RetryTime ); + } + + /// + /// 测试集成事件订阅失败 - 并发处理 + /// + [Fact] + public async Task TestSubscriptionFailAsync_5() { + //设置模拟时间 + var time = new DateTime( 2000, 1, 1, 1, 1, 1 ); + Time.SetTime( time ); + + //参数定义 + var eventId = "123"; + var appId = "appid_1"; + var appId2 = "appid_2"; + var message = "a"; + var eventLog = new IntegrationEventLog { + Id = eventId, + State = EventState.Processing, + ETag = "1", + SubscriptionLogs = { + new SubscriptionLog { AppId = appId, State = SubscriptionState.Processing }, + new SubscriptionLog { AppId = appId2, State = SubscriptionState.Processing } + } + }; + var eventLog2 = new IntegrationEventLog { + Id = eventId, + State = EventState.Processing, + ETag = "2", + SubscriptionLogs = { + new SubscriptionLog { AppId = appId, State = SubscriptionState.Processing }, + new SubscriptionLog { AppId = appId2, State = SubscriptionState.Success } + } + }; + + //设置模拟操作 + _mockGetAppId.Setup( t => t.GetAppId() ).Returns( appId ); + _mockStore.Setup( t => t.GetAsync( eventId, It.IsAny() ) ).ReturnsAsync( eventLog2 ); + _mockStore.Setup( t => t.SaveAsync( It.Is( log => log.ETag == "1" ), It.IsAny() ) ).Throws(); + + //订阅处理 + var result = await _manager.SubscriptionFailAsync( eventLog, message ); + + //验证 + _mockStore.Verify( t => t.SaveAsync( It.Is( log => log == result ), It.IsAny() ), Times.Once ); + Assert.Equal( EventState.Fail, result.State ); + Assert.Equal( time, result.LastModificationTime ); + Assert.Equal( 2, result.SubscriptionLogs.Count ); + var subLog = result.SubscriptionLogs[0]; + Assert.Equal( SubscriptionState.Fail, subLog.State ); + Assert.Equal( time, subLog.LastModificationTime ); + Assert.Single( subLog.RetryLogs ); + var retryLog = subLog.RetryLogs[0]; + Assert.Equal( 1, retryLog.Number ); + Assert.Equal( message, retryLog.Message ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests/Samples/CustomerDto.cs b/test/Util.Microservices.Dapr.Tests/Samples/CustomerDto.cs new file mode 100644 index 000000000..1781f532e --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests/Samples/CustomerDto.cs @@ -0,0 +1,84 @@ +using System; + +namespace Util.Microservices.Dapr.Tests.Samples; + +/// +/// 客户参数 +/// +public class CustomerDto { + /// + /// 编码 + /// + [Description( "编码" )] + [MaxLength( 100 )] + public string Code { get; set; } + /// + /// 姓名 + /// + [Description( "姓名" )] + [MaxLength( 200 )] + public string Name { get; set; } + /// + /// 昵称 + /// + [Description( "昵称" )] + [MaxLength( 50 )] + public string Nickname { get; set; } + /// + /// 出生日期 + /// + [Description( "出生日期" )] + public DateTime? Birthday { get; set; } + /// + /// 手机号 + /// + [Description( "手机号" )] + [MaxLength( 50 )] + public string Phone { get; set; } + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + [MaxLength( 500 )] + public string Email { get; set; } + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + /// + /// 版本号 + /// + [Description( "版本号" )] + public byte[] Version { get; set; } +} + +/// +/// 客户参数 +/// +public class CustomerDto2 : CustomerDto, IDataKey,IDataType, IETag { + public string Id { get; set; } + public string ETag { get; set; } + public string DataType { get; set; } + public ProductDto Product { get; set; } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests/Samples/CustomerQuery.cs b/test/Util.Microservices.Dapr.Tests/Samples/CustomerQuery.cs new file mode 100644 index 000000000..016850456 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests/Samples/CustomerQuery.cs @@ -0,0 +1,110 @@ +namespace Util.Microservices.Dapr.Tests.Samples; + +/// +/// 客户查询参数 +/// +public class CustomerQuery { + /// + /// 客户标识 + /// + [Description( "客户标识" )] + public int? CustomerId { get; set; } + /// + /// 编码 + /// + private string _code = string.Empty; + /// + /// 编码 + /// + [Description( "编码" )] + public string Code { + get => _code == null ? string.Empty : _code.Trim(); + set => _code = value; + } + /// + /// 姓名 + /// + private string _name = string.Empty; + /// + /// 姓名 + /// + [Description( "姓名" )] + public string Name { + get => _name == null ? string.Empty : _name.Trim(); + set => _name = value; + } + /// + /// 昵称 + /// + private string _nickname = string.Empty; + /// + /// 昵称 + /// + [Description( "昵称" )] + public string Nickname { + get => _nickname == null ? string.Empty : _nickname.Trim(); + set => _nickname = value; + } + /// + /// 起始出生日期 + /// + public DateTime? BeginBirthday { get; set; } + /// + /// 结束出生日期 + /// + public DateTime? EndBirthday { get; set; } + /// + /// 手机号 + /// + private string _phone = string.Empty; + /// + /// 手机号 + /// + [Description( "手机号" )] + public string Phone { + get => _phone == null ? string.Empty : _phone.Trim(); + set => _phone = value; + } + /// + /// 电子邮件 + /// + private string _email = string.Empty; + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + public string Email { + get => _email == null ? string.Empty : _email.Trim(); + set => _email = value; + } + /// + /// 年龄 + /// + public int? Age { get; set; } + /// + /// 起始创建时间 + /// + public DateTime? BeginCreationTime { get; set; } + /// + /// 结束创建时间 + /// + public DateTime? EndCreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 起始最后修改时间 + /// + public DateTime? BeginLastModificationTime { get; set; } + /// + /// 结束最后修改时间 + /// + public DateTime? EndLastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests/Samples/ProductDto.cs b/test/Util.Microservices.Dapr.Tests/Samples/ProductDto.cs new file mode 100644 index 000000000..11282d8e4 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests/Samples/ProductDto.cs @@ -0,0 +1,66 @@ +using System; + +namespace Util.Microservices.Dapr.Tests.Samples; + +/// +/// 产品参数 +/// +public class ProductDto { + /// + /// 产品编码 + /// + [Description( "产品编码" )] + [MaxLength( 50 )] + public string Code { get; set; } + /// + /// 产品名称 + /// + [Description( "产品名称" )] + [MaxLength( 500 )] + public string Name { get; set; } + /// + /// 价格 + /// + [Description( "价格" )] + public decimal Price { get; set; } + /// + /// 描述 + /// + [Description( "描述" )] + public string Description { get; set; } + /// + /// 启用 + /// + [Description( "启用" )] + public bool Enabled { get; set; } + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + /// + /// 版本号 + /// + [Description( "版本号" )] + public byte[] Version { get; set; } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests/Samples/TestEvent.cs b/test/Util.Microservices.Dapr.Tests/Samples/TestEvent.cs new file mode 100644 index 000000000..4cdb2fc93 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests/Samples/TestEvent.cs @@ -0,0 +1,10 @@ +using Util.Events; + +namespace Util.Microservices.Dapr.Tests.Samples; + +/// +/// 测试事件 +/// +/// 编码 +/// 名称 +public record TestEvent( string Code, string Name ) : IntegrationEvent; \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/Conditions/AndConditionTest.cs b/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/Conditions/AndConditionTest.cs new file mode 100644 index 000000000..7a9e92694 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/Conditions/AndConditionTest.cs @@ -0,0 +1,104 @@ +namespace Util.Microservices.Dapr.Tests.StateManagements.Queries.Conditions; + +/// +/// And查询条件测试 +/// +public class AndConditionTest { + /// + /// 测试And条件 - 1个条件 + /// + [Fact] + public void Test_1() { + var equalCondition = new EqualCondition( "a", "1" ); + var andCondition = new AndCondition( equalCondition ); + var json = Util.Helpers.Json.ToJson( andCondition ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"AND\":[" ); + result.Append( "{\"EQ\":{\"a\":\"1\"}}" ); + result.Append( "]" ); + result.Append( "}" ); + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试And条件 - 2个条件 + /// + [Fact] + public void Test_2() { + var equalCondition1 = new EqualCondition( "a", "1" ); + var equalCondition2 = new EqualCondition( "b", "2" ); + var andCondition = new AndCondition( equalCondition1, equalCondition2 ); + var json = Util.Helpers.Json.ToJson( andCondition ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"AND\":[" ); + result.Append( "{\"EQ\":{\"a\":\"1\"}}," ); + result.Append( "{\"EQ\":{\"b\":\"2\"}}" ); + result.Append( "]" ); + result.Append( "}" ); + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试And条件 - 3个条件 + /// + [Fact] + public void Test_3() { + //创建and条件 + var equalCondition1 = new EqualCondition( "a", "1" ); + var equalCondition2 = new EqualCondition( "b", "2" ); + var andCondition = new AndCondition( equalCondition1, equalCondition2 ); + + //添加第3个条件 + var equalCondition3 = new EqualCondition( "c", "3" ); + andCondition.AddCondition( equalCondition3 ); + + //获取json + var json = Util.Helpers.Json.ToJson( andCondition ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"AND\":[" ); + result.Append( "{\"EQ\":{\"a\":\"1\"}}," ); + result.Append( "{\"EQ\":{\"b\":\"2\"}}," ); + result.Append( "{\"EQ\":{\"c\":\"3\"}}" ); + result.Append( "]" ); + result.Append( "}" ); + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试And条件 - and条件合并 + /// + [Fact] + public void Test_4() { + //创建and条件 + var equalCondition1 = new EqualCondition( "a", "1" ); + var equalCondition2 = new EqualCondition( "b", "2" ); + var andCondition = new AndCondition( equalCondition1, equalCondition2 ); + + //添加第3个条件 + var equalCondition3 = new EqualCondition( "c", "3" ); + var andCondition2 = new AndCondition( andCondition, equalCondition3 ); + + //获取json + var json = Util.Helpers.Json.ToJson( andCondition2 ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"AND\":[" ); + result.Append( "{\"EQ\":{\"a\":\"1\"}}," ); + result.Append( "{\"EQ\":{\"b\":\"2\"}}," ); + result.Append( "{\"EQ\":{\"c\":\"3\"}}" ); + result.Append( "]" ); + result.Append( "}" ); + Assert.Equal( result.ToString(), json ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/Conditions/EqualConditionTest.cs b/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/Conditions/EqualConditionTest.cs new file mode 100644 index 000000000..b489d84bd --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/Conditions/EqualConditionTest.cs @@ -0,0 +1,16 @@ +namespace Util.Microservices.Dapr.Tests.StateManagements.Queries.Conditions; + +/// +/// 相等查询条件测试 +/// +public class EqualConditionTest { + /// + /// 测试相等条件 + /// + [Fact] + public void Test() { + var condition = new EqualCondition( "a","1" ); + var json = Util.Helpers.Json.ToJson( condition ); + Assert.Equal( "{\"EQ\":{\"a\":\"1\"}}", json ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/Conditions/InConditionTest.cs b/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/Conditions/InConditionTest.cs new file mode 100644 index 000000000..4e5f2e469 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/Conditions/InConditionTest.cs @@ -0,0 +1,16 @@ +namespace Util.Microservices.Dapr.Tests.StateManagements.Queries.Conditions; + +/// +/// In查询条件测试 +/// +public class InConditionTest { + /// + /// 测试In条件 + /// + [Fact] + public void Test() { + var condition = new InCondition( "state", new[] { "a", "b" } ); + var json = Util.Helpers.Json.ToJson( condition ); + Assert.Equal( "{\"IN\":{\"state\":[\"a\",\"b\"]}}", json ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/Conditions/OrConditionTest.cs b/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/Conditions/OrConditionTest.cs new file mode 100644 index 000000000..766b16b0b --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/Conditions/OrConditionTest.cs @@ -0,0 +1,104 @@ +namespace Util.Microservices.Dapr.Tests.StateManagements.Queries.Conditions; + +/// +/// Or查询条件测试 +/// +public class ConditionTest { + /// + /// 测试Or条件 - 1个条件 + /// + [Fact] + public void Test_1() { + var equalCondition = new EqualCondition( "a", "1" ); + var orCondition = new OrCondition( equalCondition ); + var json = Util.Helpers.Json.ToJson( orCondition ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"OR\":[" ); + result.Append( "{\"EQ\":{\"a\":\"1\"}}" ); + result.Append( "]" ); + result.Append( "}" ); + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试Or条件 - 2个条件 + /// + [Fact] + public void Test_2() { + var equalCondition1 = new EqualCondition( "a", "1" ); + var equalCondition2 = new EqualCondition( "b", "2" ); + var orCondition = new OrCondition( equalCondition1, equalCondition2 ); + var json = Util.Helpers.Json.ToJson( orCondition ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"OR\":[" ); + result.Append( "{\"EQ\":{\"a\":\"1\"}}," ); + result.Append( "{\"EQ\":{\"b\":\"2\"}}" ); + result.Append( "]" ); + result.Append( "}" ); + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试Or条件 - 3个条件 + /// + [Fact] + public void Test_3() { + //创建or条件 + var equalCondition1 = new EqualCondition( "a", "1" ); + var equalCondition2 = new EqualCondition( "b", "2" ); + var orCondition = new OrCondition( equalCondition1, equalCondition2 ); + + //添加第3个条件 + var equalCondition3 = new EqualCondition( "c", "3" ); + orCondition.AddCondition( equalCondition3 ); + + //获取json + var json = Util.Helpers.Json.ToJson( orCondition ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"OR\":[" ); + result.Append( "{\"EQ\":{\"a\":\"1\"}}," ); + result.Append( "{\"EQ\":{\"b\":\"2\"}}," ); + result.Append( "{\"EQ\":{\"c\":\"3\"}}" ); + result.Append( "]" ); + result.Append( "}" ); + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试Or条件 - or条件合并 + /// + [Fact] + public void Test_4() { + //创建or条件 + var equalCondition1 = new EqualCondition( "a", "1" ); + var equalCondition2 = new EqualCondition( "b", "2" ); + var orCondition = new OrCondition( equalCondition1, equalCondition2 ); + + //添加第3个条件 + var equalCondition3 = new EqualCondition( "c", "3" ); + var orCondition2 = new OrCondition( orCondition, equalCondition3 ); + + //获取json + var json = Util.Helpers.Json.ToJson( orCondition2 ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"OR\":[" ); + result.Append( "{\"EQ\":{\"a\":\"1\"}}," ); + result.Append( "{\"EQ\":{\"b\":\"2\"}}," ); + result.Append( "{\"EQ\":{\"c\":\"3\"}}" ); + result.Append( "]" ); + result.Append( "}" ); + Assert.Equal( result.ToString(), json ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/StateConditionExtensionsTest.cs b/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/StateConditionExtensionsTest.cs new file mode 100644 index 000000000..78e8726e4 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/StateConditionExtensionsTest.cs @@ -0,0 +1,269 @@ +namespace Util.Microservices.Dapr.Tests.StateManagements.Queries; + +/// +/// 状态对象查询条件扩展测试 +/// +public class StateConditionExtensionsTest { + + #region And + + /// + /// 测试And连接条件 - 两个条件都为空 + /// + [Fact] + public void TestAnd_1() { + IStateCondition condition1 = null; + IStateCondition condition2 = null; + var result = condition1.And( condition2 ); + Assert.Null( result ); + } + + /// + /// 测试And连接条件 - 第1个条件为空 + /// + [Fact] + public void TestAnd_2() { + IStateCondition condition1 = null; + IStateCondition condition2 = new EqualCondition( "a", "1" ); + var result = condition1.And( condition2 ); + var json = Json.ToJson( result ); + Assert.Equal( "{\"EQ\":{\"a\":\"1\"}}", json ); + } + + /// + /// 测试And连接条件 - 第2个条件为空 + /// + [Fact] + public void TestAnd_3() { + IStateCondition condition1 = new EqualCondition( "a", "1" ); + IStateCondition condition2 = null; + var result = condition1.And( condition2 ); + var json = Json.ToJson( result ); + Assert.Equal( "{\"EQ\":{\"a\":\"1\"}}", json ); + } + + /// + /// 测试And连接条件 - 2个相等条件连接 + /// + [Fact] + public void TestAnd_4() { + IStateCondition condition1 = new EqualCondition( "a", "1" ); + IStateCondition condition2 = new EqualCondition( "b", "2" ); + var and = condition1.And( condition2 ); + var json = Json.ToJson( and ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"AND\":[" ); + result.Append( "{\"EQ\":{\"a\":\"1\"}}," ); + result.Append( "{\"EQ\":{\"b\":\"2\"}}" ); + result.Append( "]" ); + result.Append( "}" ); + + //验证 + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试And连接条件 - 第1个条件为And + /// + [Fact] + public void TestAnd_5() { + IStateCondition condition1 = new AndCondition( new EqualCondition( "a", "1" ) ); + IStateCondition condition2 = new EqualCondition( "b", "2" ); + var and = condition1.And( condition2 ); + var json = Json.ToJson( and ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"AND\":[" ); + result.Append( "{\"EQ\":{\"a\":\"1\"}}," ); + result.Append( "{\"EQ\":{\"b\":\"2\"}}" ); + result.Append( "]" ); + result.Append( "}" ); + + //验证 + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试And连接条件 - 第2个条件为And + /// + [Fact] + public void TestAnd_6() { + IStateCondition condition1 = new EqualCondition( "a", "1" ); + IStateCondition condition2 = new AndCondition( new EqualCondition( "b", "2" ) ); + var and = condition1.And( condition2 ); + var json = Json.ToJson( and ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"AND\":[" ); + result.Append( "{\"EQ\":{\"b\":\"2\"}}," ); + result.Append( "{\"EQ\":{\"a\":\"1\"}}" ); + result.Append( "]" ); + result.Append( "}" ); + + //验证 + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试And连接条件 - 2个条件都为And + /// + [Fact] + public void TestAnd_7() { + IStateCondition condition1 = new AndCondition( new EqualCondition( "a", "1" ) ); + IStateCondition condition2 = new AndCondition( new EqualCondition( "b", "2" ) ); + var and = condition1.And( condition2 ); + var json = Json.ToJson( and ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"AND\":[" ); + result.Append( "{\"EQ\":{\"a\":\"1\"}}," ); + result.Append( "{\"EQ\":{\"b\":\"2\"}}" ); + result.Append( "]" ); + result.Append( "}" ); + + //验证 + Assert.Equal( result.ToString(), json ); + } + + #endregion + + #region Or + + /// + /// 测试Or连接条件 - 两个条件都为空 + /// + [Fact] + public void TestOr_1() { + IStateCondition condition1 = null; + IStateCondition condition2 = null; + var result = condition1.Or( condition2 ); + Assert.Null( result ); + } + + /// + /// 测试Or连接条件 - 第1个条件为空 + /// + [Fact] + public void TestOr_2() { + IStateCondition condition1 = null; + IStateCondition condition2 = new EqualCondition( "a", "1" ); + var result = condition1.Or( condition2 ); + var json = Json.ToJson( result ); + Assert.Equal( "{\"EQ\":{\"a\":\"1\"}}", json ); + } + + /// + /// 测试Or连接条件 - 第2个条件为空 + /// + [Fact] + public void TestOr_3() { + IStateCondition condition1 = new EqualCondition( "a", "1" ); + IStateCondition condition2 = null; + var result = condition1.Or( condition2 ); + var json = Json.ToJson( result ); + Assert.Equal( "{\"EQ\":{\"a\":\"1\"}}", json ); + } + + /// + /// 测试Or连接条件 - 2个相等条件连接 + /// + [Fact] + public void TestOr_4() { + IStateCondition condition1 = new EqualCondition( "a", "1" ); + IStateCondition condition2 = new EqualCondition( "b", "2" ); + var or = condition1.Or( condition2 ); + var json = Json.ToJson( or ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"OR\":[" ); + result.Append( "{\"EQ\":{\"a\":\"1\"}}," ); + result.Append( "{\"EQ\":{\"b\":\"2\"}}" ); + result.Append( "]" ); + result.Append( "}" ); + + //验证 + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试Or连接条件 - 第1个条件为Or + /// + [Fact] + public void TestOr_5() { + IStateCondition condition1 = new OrCondition( new EqualCondition( "a", "1" ) ); + IStateCondition condition2 = new EqualCondition( "b", "2" ); + var or = condition1.Or( condition2 ); + var json = Json.ToJson( or ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"OR\":[" ); + result.Append( "{\"EQ\":{\"a\":\"1\"}}," ); + result.Append( "{\"EQ\":{\"b\":\"2\"}}" ); + result.Append( "]" ); + result.Append( "}" ); + + //验证 + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试Or连接条件 - 第2个条件为Or + /// + [Fact] + public void TestOr_6() { + IStateCondition condition1 = new EqualCondition( "a", "1" ); + IStateCondition condition2 = new OrCondition( new EqualCondition( "b", "2" ) ); + var or = condition1.Or( condition2 ); + var json = Json.ToJson( or ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"OR\":[" ); + result.Append( "{\"EQ\":{\"b\":\"2\"}}," ); + result.Append( "{\"EQ\":{\"a\":\"1\"}}" ); + result.Append( "]" ); + result.Append( "}" ); + + //验证 + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试Or连接条件 - 2个条件都为Or + /// + [Fact] + public void TestOr_7() { + IStateCondition condition1 = new OrCondition( new EqualCondition( "a", "1" ) ); + IStateCondition condition2 = new OrCondition( new EqualCondition( "b", "2" ) ); + var or = condition1.Or( condition2 ); + var json = Json.ToJson( or ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"OR\":[" ); + result.Append( "{\"EQ\":{\"a\":\"1\"}}," ); + result.Append( "{\"EQ\":{\"b\":\"2\"}}" ); + result.Append( "]" ); + result.Append( "}" ); + + //验证 + Assert.Equal( result.ToString(), json ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/StateFilterTest.cs b/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/StateFilterTest.cs new file mode 100644 index 000000000..cd0db84a0 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/StateFilterTest.cs @@ -0,0 +1,160 @@ +using Util.Microservices.Dapr.Tests.Samples; + +namespace Util.Microservices.Dapr.Tests.StateManagements.Queries; + +/// +/// 状态过滤器测试 +/// +public class StateFilterTest { + + #region 测试初始化 + + /// + /// 状态过滤器 + /// + private readonly StateFilter _filter; + + /// + /// 测试初始化 + /// + public StateFilterTest() { + _filter = new StateFilter(); + } + + #endregion + + #region 默认输出 + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var json = Json.ToJson( _filter.GetCondition() ); + Assert.Equal( "", json ); + } + + #endregion + + #region Equal + + /// + /// 测试添加相等条件 - 1个相等条件 + /// + [Fact] + public void TestEqual_1() { + _filter.Equal( "a","1" ); + var json = Json.ToJson( _filter.GetCondition() ); + Assert.Equal( "{\"EQ\":{\"a\":\"1\"}}", json ); + } + + /// + /// 测试添加相等条件 - 2个相等条件 + /// + [Fact] + public void TestEqual_2() { + //添加2个相等条件 + _filter.Equal( "a", "1" ); + _filter.Equal( "b", "2" ); + var json = Json.ToJson( _filter.GetCondition() ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"AND\":[" ); + result.Append( "{\"EQ\":{\"a\":\"1\"}}," ); + result.Append( "{\"EQ\":{\"b\":\"2\"}}" ); + result.Append( "]" ); + result.Append( "}" ); + + //验证 + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试添加相等条件 - 属性表达式 + /// + [Fact] + public void TestEqual_3() { + _filter.Equal( t => t.Product.CreationTime, "1" ); + var json = Json.ToJson( _filter.GetCondition() ); + Assert.Equal( "{\"EQ\":{\"product.creationTime\":\"1\"}}", json ); + } + + #endregion + + #region In + + /// + /// 测试添加In条件 - 1个In条件 + /// + [Fact] + public void TestIn_1() { + _filter.In( "a", new[] { "1", "2" } ); + var json = Json.ToJson( _filter.GetCondition() ); + Assert.Equal( "{\"IN\":{\"a\":[\"1\",\"2\"]}}", json ); + } + + /// + /// 测试添加In条件 - 2个In条件 + /// + [Fact] + public void TestIn_2() { + _filter.In( "a", new[] { "1", "2" } ); + _filter.In( "b", new[] { "3", "4" } ); + var json = Json.ToJson( _filter.GetCondition() ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"AND\":[" ); + result.Append( "{\"IN\":{\"a\":[\"1\",\"2\"]}}," ); + result.Append( "{\"IN\":{\"b\":[\"3\",\"4\"]}}" ); + result.Append( "]" ); + result.Append( "}" ); + + //验证 + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试添加In条件 - 属性表达式 + /// + [Fact] + public void TestIn_3() { + _filter.In( t => t.Product.CreationTime, new[] { "1", "2" } ); + var json = Json.ToJson( _filter.GetCondition() ); + Assert.Equal( "{\"IN\":{\"product.creationTime\":[\"1\",\"2\"]}}", json ); + } + + #endregion + + #region Or + + /// + /// 测试添加Or条件 + /// + [Fact] + public void TestOr() { + //添加相等条件 + _filter.Equal( "a", "1" ); + + //添加Or条件 + _filter.Or( new EqualCondition( "b", "2" ) ); + var json = Json.ToJson( _filter.GetCondition() ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"OR\":[" ); + result.Append( "{\"EQ\":{\"a\":\"1\"}}," ); + result.Append( "{\"EQ\":{\"b\":\"2\"}}" ); + result.Append( "]" ); + result.Append( "}" ); + + //验证 + Assert.Equal( result.ToString(), json ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/StateQueryTest.cs b/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/StateQueryTest.cs new file mode 100644 index 000000000..0c8d2ce5e --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/StateQueryTest.cs @@ -0,0 +1,93 @@ +namespace Util.Microservices.Dapr.Tests.StateManagements.Queries; + +/// +/// 状态查询对象测试 +/// +public class StateQueryTest { + /// + /// 状态查询对象 + /// + private readonly StateQuery _query; + + /// + /// 测试初始化 + /// + public StateQueryTest() { + _query = new StateQuery(); + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var json = Util.Helpers.Json.ToJson( _query ); + Assert.Equal( "{}", json ); + } + + /// + /// 测试过滤条件 + /// + [Fact] + public void TestFilter() { + var filter = new StateFilter(); + filter.Equal( "a","1" ); + _query.Filter = filter.GetCondition(); + var json = Json.ToJson( _query ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"filter\":{" ); + result.Append( "\"EQ\":{\"a\":\"1\"}" ); + result.Append( "}}" ); + + //验证 + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试排序条件 + /// + [Fact] + public void TestSort() { + var sort = new StateSort { "a", "b desc" }; + _query.Sort = sort; + var json = Json.ToJson( _query ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"sort\":" ); + result.Append( "[" ); + result.Append( "{\"key\":\"a\"}," ); + result.Append( "{\"key\":\"b\"," ); + result.Append( "\"order\":\"DESC\"}" ); + result.Append( "]" ); + result.Append( "}" ); + + //验证 + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试分页条件 + /// + [Fact] + public void TestPage() { + var page = new StatePage { Limit = 1,Token = "2"}; + _query.Page = page; + var json = Json.ToJson( _query ); + + //结果 + var result = new StringBuilder(); + result.Append( "{" ); + result.Append( "\"page\":{" ); + result.Append( "\"limit\":1," ); + result.Append( "\"token\":\"2\"" ); + result.Append( "}}" ); + + //验证 + Assert.Equal( result.ToString(), json ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/StateSortTest.cs b/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/StateSortTest.cs new file mode 100644 index 000000000..e0a634fcf --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests/StateManagements/Queries/StateSortTest.cs @@ -0,0 +1,148 @@ +using Util.Microservices.Dapr.Tests.Samples; + +namespace Util.Microservices.Dapr.Tests.StateManagements.Queries; + +/// +/// 状态排序对象测试 +/// +public class StateSortTest { + /// + /// 状态查询对象 + /// + private readonly StateSort _sort; + + /// + /// 测试初始化 + /// + public StateSortTest() { + _sort = new StateSort(); + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var json = Util.Helpers.Json.ToJson( _sort ); + Assert.Equal( "[]", json ); + } + + /// + /// 测试添加排序条件 - 1个升序字段 + /// + [Fact] + public void TestOrderBy_1() { + _sort.OrderBy( "a" ); + var json = Util.Helpers.Json.ToJson( _sort ); + + //结果 + var result = new StringBuilder(); + result.Append( "[" ); + result.Append( "{\"key\":\"a\"}" ); + result.Append( "]" ); + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试添加排序条件 - 1个升序字段 - 属性表达式 + /// + [Fact] + public void TestOrderBy_2() { + _sort.OrderBy( t => t.Product.CreationTime ); + var json = Util.Helpers.Json.ToJson( _sort ); + + //结果 + var result = new StringBuilder(); + result.Append( "[" ); + result.Append( "{\"key\":\"product.creationTime\"}" ); + result.Append( "]" ); + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试添加排序条件 - 1个降序字段 + /// + [Fact] + public void TestOrderByDescending_1() { + _sort.OrderByDescending( "a" ); + var json = Util.Helpers.Json.ToJson( _sort ); + + //结果 + var result = new StringBuilder(); + result.Append( "[" ); + result.Append( "{\"key\":\"a\"," ); + result.Append( "\"order\":\"DESC\"}" ); + result.Append( "]" ); + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试添加排序条件 - 1个降序字段 - 属性表达式 + /// + [Fact] + public void TestOrderByDescending_2() { + _sort.OrderByDescending( t => t.Product.CreationTime ); + var json = Util.Helpers.Json.ToJson( _sort ); + + //结果 + var result = new StringBuilder(); + result.Append( "[" ); + result.Append( "{\"key\":\"product.creationTime\"," ); + result.Append( "\"order\":\"DESC\"}" ); + result.Append( "]" ); + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试添加排序条件 - 1个字段 - 升序 + /// + [Fact] + public void TestAdd_1() { + _sort.Add( "a" ); + var json = Util.Helpers.Json.ToJson( _sort ); + + //结果 + var result = new StringBuilder(); + result.Append( "[" ); + result.Append( "{\"key\":\"a\"}" ); + result.Append( "]" ); + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试添加排序条件 - 2个字段 + /// + [Fact] + public void TestAdd_2() { + _sort.Add( "a" ); + _sort.Add( "b desc" ); + var json = Util.Helpers.Json.ToJson( _sort ); + + //结果 + var result = new StringBuilder(); + result.Append( "[" ); + result.Append( "{\"key\":\"a\"}," ); + result.Append( "{\"key\":\"b\"," ); + result.Append( "\"order\":\"DESC\"}" ); + result.Append( "]" ); + Assert.Equal( result.ToString(), json ); + } + + /// + /// 测试添加排序条件 - 2个字段 - 一个字符串 + /// + [Fact] + public void TestAdd_3() { + _sort.Add( " a , b desc " ); + var json = Util.Helpers.Json.ToJson( _sort ); + + //结果 + var result = new StringBuilder(); + result.Append( "[" ); + result.Append( "{\"key\":\"a\"}," ); + result.Append( "{\"key\":\"b\"," ); + result.Append( "\"order\":\"DESC\"}" ); + result.Append( "]" ); + Assert.Equal( result.ToString(), json ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests/Usings.cs b/test/Util.Microservices.Dapr.Tests/Usings.cs new file mode 100644 index 000000000..4d5856238 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests/Usings.cs @@ -0,0 +1,18 @@ +global using System; +global using System.Text; +global using System.ComponentModel; +global using System.ComponentModel.DataAnnotations; +global using System.Collections.Generic; +global using System.Threading; +global using System.Threading.Tasks; +global using Xunit; +global using Xunit.Abstractions; +global using Moq; +global using Util.Microservices.Dapr.CommandLines; +global using Util.Microservices.Dapr.StateManagements.Queries.Conditions; +global using Util.Microservices.Dapr.StateManagements.Queries; +global using Util.Helpers; +global using Util.Data; +global using Util.Sessions; +global using Util.Microservices.Dapr.Events; +global using Util.Microservices.Events; \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.Tests/Util.Microservices.Dapr.Tests.csproj b/test/Util.Microservices.Dapr.Tests/Util.Microservices.Dapr.Tests.csproj new file mode 100644 index 000000000..da2c0ed82 --- /dev/null +++ b/test/Util.Microservices.Dapr.Tests/Util.Microservices.Dapr.Tests.csproj @@ -0,0 +1,27 @@ + + + + $(NetTargetFramework) + false + true + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.Microservices.Dapr.WebApiSample/Controllers/IntegrationEventController.cs b/test/Util.Microservices.Dapr.WebApiSample/Controllers/IntegrationEventController.cs new file mode 100644 index 000000000..d8c38e875 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Controllers/IntegrationEventController.cs @@ -0,0 +1,60 @@ +using Util.Microservices.Dapr.Events; +using Util.Microservices.Dapr.WebApiSample.Events; + +namespace Util.Microservices.Dapr.WebApiSample.Controllers; + +/// +/// 集成事件控制器 +/// +public class IntegrationEventController : IntegrationEventControllerBase { + /// + /// 状态管理操作 + /// + private readonly IStateManager _stateManager; + /// + /// 日志操作 + /// + private readonly ILogger _log; + + /// + /// 初始化集成事件控制器 + /// + public IntegrationEventController( IStateManager stateManager, ILogger log ) { + _stateManager = stateManager; + _log = log; + } + + /// + /// 订阅1 + /// + [HttpPost( "WebApi_Test1" )] + [Topic( nameof( TestEvent ) )] + public async Task Test1Async( TestEvent @event ) { + _log.LogInformation( "========================================================WebApi_Test1:{@TestEvent}", @event ); + await _stateManager.StoreName( "event-state-store" ).AddAsync( $"webapi_{@event.Code}", @event ); + return Success(); + } + + /// + /// 订阅B + /// + [HttpPost( "WebApi_B" )] + [Topic( "b" )] + public async Task TestBAsync( TestEvent @event ) { + _log.LogInformation( "========================================================WebApi_B:{@TestEvent}", @event ); + await Task.CompletedTask; + return Success(); + } + + /// + /// 订阅C + /// + [HttpPost( "WebApi_C" )] + [Topic( "c" )] + public async Task TestCAsync( TestEvent @event ) { + _log.LogInformation( "========================================================WebApi_C:{@TestEvent}", @event ); + await Task.CompletedTask; + throw new Exception( "WebApi_Exception_C" ); + return Success(); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.WebApiSample/Controllers/Test1Controller.cs b/test/Util.Microservices.Dapr.WebApiSample/Controllers/Test1Controller.cs new file mode 100644 index 000000000..fdef22450 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Controllers/Test1Controller.cs @@ -0,0 +1,24 @@ +namespace Util.Microservices.Dapr.WebApiSample.Controllers; + +/// +/// 1 - ڲʹHttpClientȡַ +/// +[ApiController] +[Route("Test1")] +public class Test1Controller : ControllerBase { + /// + /// HttpGet,ַ + /// + [HttpGet] + public string Get_1() { + return "ok"; + } + + /// + /// HttpGet, + /// + [HttpGet( "{id}" )] + public string Get_2( string id ) { + return $"id:{id}"; + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.WebApiSample/Controllers/Test2Controller.cs b/test/Util.Microservices.Dapr.WebApiSample/Controllers/Test2Controller.cs new file mode 100644 index 000000000..768c7f8c6 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Controllers/Test2Controller.cs @@ -0,0 +1,57 @@ +using Util.Exceptions; +using Util.Microservices.Dapr.WebApiSample.Dtos; + +namespace Util.Microservices.Dapr.WebApiSample.Controllers { + /// + /// 2 - ڲʹ÷ûȡδװԭʼ + /// + [ApiController] + [Route("Test2")] + public class Test2Controller : ControllerBase { + /// + /// Get - ޲ - ޷ֵ - ɹ + /// + [HttpGet] + public void Get() { + } + + /// + /// Get - ޲ - ޷ֵ - ʧ + /// + [HttpGet( "fail" )] + public IActionResult Get_Fail() { + return new BadRequestResult(); + } + + /// + /// Post - в - ޷ֵ + /// + [HttpPost] + public void Post( CustomerDto dto ) { + if ( dto.Code != "2" ) + throw new Warning( "idΪ2" ); + } + + /// + /// Get - ޲ - зֵ + /// + [HttpGet( "customer" )] + public CustomerDto Get_Customer() { + var dto = new CustomerDto { + Name = "ok" + }; + return dto; + } + + /// + /// Get - в - зֵ + /// + [HttpGet( "query" )] + public CustomerDto Query( [FromQuery] CustomerQuery query ) { + var dto = new CustomerDto { + Name = query.Name + }; + return dto; + } + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.WebApiSample/Controllers/Test3Controller.cs b/test/Util.Microservices.Dapr.WebApiSample/Controllers/Test3Controller.cs new file mode 100644 index 000000000..4618b8c56 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Controllers/Test3Controller.cs @@ -0,0 +1,125 @@ +using Microsoft.AspNetCore.Authorization; +using Util.Applications.Controllers; +using Util.Data; +using Util.Exceptions; +using Util.Microservices.Dapr.WebApiSample.Dtos; + +namespace Util.Microservices.Dapr.WebApiSample.Controllers; + +/// +/// 测试3控制器 - 用于测试包装结果对象 +/// +public class Test3Controller : WebApiControllerBase { + /// + /// 测试Get方法访问 - 无参数 - 无返回值 - 成功 + /// + [HttpGet] + public IActionResult Get() { + return Success(); + } + + /// + /// 测试Get方法访问 - 无参数 - 无返回值 - 失败 + /// + [HttpGet("fail")] + public IActionResult Get_Fail() { + return Fail( "fail" ); + } + + /// + /// 测试Post方法访问 - 有参数 - 无返回值 + /// + [HttpPost] + public IActionResult Post( CustomerDto dto ) { + if ( dto.Code != "2" ) + throw new Warning( "id必须为2" ); + return Success(); + } + + /// + /// 测试Get方法访问 - 无参数 - 有返回值 + /// + [HttpGet( "customer" )] + public IActionResult Get_Customer() { + var dto = new CustomerDto { + Name = "ok" + }; + return Success( dto ); + } + + /// + /// 测试Get方法访问 - 有参数 - 有返回值 + /// + [HttpGet( "query" )] + public IActionResult Query( [FromQuery] CustomerQuery query ) { + var dto = new CustomerDto { + Name = query.Name + }; + return Success( dto ); + } + + /// + /// 测试自定义地址 + /// + [HttpGet( "/query" )] + public IActionResult Query2( [FromQuery] CustomerQuery query ) { + var dto = new CustomerDto { + Name = query.Name + }; + return Success( dto ); + } + + /// + /// 测试需要身份认证 + /// + [HttpGet( "query_authorize" )] + [Authorize] + public IActionResult Query_Authorize( [FromQuery] CustomerQuery query ) { + var dto = new CustomerDto { + Name = query.Name + }; + return Success( dto ); + } + + /// + /// 测试分页对象 + /// + [HttpGet( "page_query" )] + public IActionResult PageQuery( [FromQuery] CustomerQuery query ) { + PageList result = new PageList(); + result.Total = 50; + result.Data.Add( new CustomerDto { + Name = query.Name + } ); + return Success( result ); + } + + /// + /// 测试添加请求头 + /// + [HttpGet( "query_header" )] + public IActionResult Query2_Header_1( [FromQuery] CustomerQuery query ) { + var customerCode = Request.Headers["CustomerCode"].FirstOrDefault(); + var customerName = Request.Headers["CustomerName"].FirstOrDefault(); + var test_1 = Request.Headers["test_1"].FirstOrDefault(); + var dto = new CustomerDto { + Code = customerCode, + Name = customerName + }; + return Success( dto ); + } + + /// + /// 测试导入请求头 + /// + [HttpGet( "query_header_import" )] + public IActionResult Query2_Header_2( [FromQuery] CustomerQuery query ) { + var test_1 = Request.Headers["test_1"].FirstOrDefault(); + var contentLanguage = Request.Headers.ContentLanguage.FirstOrDefault(); + var dto = new CustomerDto { + Code = test_1, + Name = contentLanguage + }; + return Success( dto ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.WebApiSample/Dtos/CustomerDto.cs b/test/Util.Microservices.Dapr.WebApiSample/Dtos/CustomerDto.cs new file mode 100644 index 000000000..09eca5a44 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Dtos/CustomerDto.cs @@ -0,0 +1,76 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Applications.Dtos; + +namespace Util.Microservices.Dapr.WebApiSample.Dtos { + /// + /// 客户参数 + /// + public class CustomerDto : DtoBase { + /// + /// 编码 + /// + [Description( "编码" )] + [MaxLength( 100 )] + public string Code { get; set; } + /// + /// 姓名 + /// + [Description( "姓名" )] + [MaxLength( 200 )] + public string Name { get; set; } + /// + /// 昵称 + /// + [Description( "昵称" )] + [MaxLength( 50 )] + public string Nickname { get; set; } + /// + /// 出生日期 + /// + [Description( "出生日期" )] + public DateTime? Birthday { get; set; } + /// + /// 手机号 + /// + [Description( "手机号" )] + [MaxLength( 50 )] + public string Phone { get; set; } + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + [MaxLength( 500 )] + public string Email { get; set; } + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + /// + /// 版本号 + /// + [Description( "版本号" )] + public byte[] Version { get; set; } + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.WebApiSample/Dtos/CustomerQuery.cs b/test/Util.Microservices.Dapr.WebApiSample/Dtos/CustomerQuery.cs new file mode 100644 index 000000000..e7124fcd3 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Dtos/CustomerQuery.cs @@ -0,0 +1,113 @@ +using System.ComponentModel; +using Util.Data.Queries; + +namespace Util.Microservices.Dapr.WebApiSample.Dtos; + +/// +/// 客户查询参数 +/// +public class CustomerQuery : QueryParameter { + /// + /// 客户标识 + /// + [Description( "客户标识" )] + public int? CustomerId { get; set; } + /// + /// 编码 + /// + private string _code = string.Empty; + /// + /// 编码 + /// + [Description( "编码" )] + public string Code { + get => _code == null ? string.Empty : _code.Trim(); + set => _code = value; + } + /// + /// 姓名 + /// + private string _name = string.Empty; + /// + /// 姓名 + /// + [Description( "姓名" )] + public string Name { + get => _name == null ? string.Empty : _name.Trim(); + set => _name = value; + } + /// + /// 昵称 + /// + private string _nickname = string.Empty; + /// + /// 昵称 + /// + [Description( "昵称" )] + public string Nickname { + get => _nickname == null ? string.Empty : _nickname.Trim(); + set => _nickname = value; + } + /// + /// 起始出生日期 + /// + public DateTime? BeginBirthday { get; set; } + /// + /// 结束出生日期 + /// + public DateTime? EndBirthday { get; set; } + /// + /// 手机号 + /// + private string _phone = string.Empty; + /// + /// 手机号 + /// + [Description( "手机号" )] + public string Phone { + get => _phone == null ? string.Empty : _phone.Trim(); + set => _phone = value; + } + /// + /// 电子邮件 + /// + private string _email = string.Empty; + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + public string Email { + get => _email == null ? string.Empty : _email.Trim(); + set => _email = value; + } + /// + /// 年龄 + /// + public int? Age { get; set; } + /// + /// 起始创建时间 + /// + public DateTime? BeginCreationTime { get; set; } + /// + /// 结束创建时间 + /// + public DateTime? EndCreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 起始最后修改时间 + /// + public DateTime? BeginLastModificationTime { get; set; } + /// + /// 结束最后修改时间 + /// + public DateTime? EndLastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } +} \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.WebApiSample/Events/TestEvent.cs b/test/Util.Microservices.Dapr.WebApiSample/Events/TestEvent.cs new file mode 100644 index 000000000..1cd016b39 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Events/TestEvent.cs @@ -0,0 +1,10 @@ +using Util.Events; + +namespace Util.Microservices.Dapr.WebApiSample.Events; + +/// +/// 测试事件 +/// +/// 编码 +/// 名称 +public record TestEvent( string Code, string Name ) : IntegrationEvent; \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.WebApiSample/Program.cs b/test/Util.Microservices.Dapr.WebApiSample/Program.cs new file mode 100644 index 000000000..e6cc7b669 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Program.cs @@ -0,0 +1,20 @@ +using Util.Helpers; +using Util.Logging.Serilog; +using Util.Microservices.Dapr; + +var builder = WebApplication.CreateBuilder( args ); +builder.Services.AddControllers(); +builder.AsBuild() + .AddSerilog( "Util.Microservices.Dapr.WebApiSample" ) + .AddDapr( t => t.AppId = "app_webapi", daprBuilder => { + daprBuilder.UseHttpEndpoint( $"http://127.0.0.1:{Config.GetValue( "DaprPorts:HttpPort" )}" ) + .UseGrpcEndpoint( $"http://127.0.0.1:{Config.GetValue( "DaprPorts:GrpcPort" )}" ); + } ) + .AddUtil(); + +var app = builder.Build(); +app.UseCloudEventHeaders(); +app.UseCloudEvents(); +app.MapSubscribeHandler(); +app.MapControllers(); +app.Run(); \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.WebApiSample/Properties/launchSettings.json b/test/Util.Microservices.Dapr.WebApiSample/Properties/launchSettings.json new file mode 100644 index 000000000..79a617c92 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Properties/launchSettings.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:3166", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/test/Util.Microservices.Dapr.WebApiSample/Usings.cs b/test/Util.Microservices.Dapr.WebApiSample/Usings.cs new file mode 100644 index 000000000..85103500f --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Usings.cs @@ -0,0 +1,10 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Microsoft.AspNetCore.Mvc; +global using Util; \ No newline at end of file diff --git a/test/Util.Microservices.Dapr.WebApiSample/Util.Microservices.Dapr.WebApiSample.csproj b/test/Util.Microservices.Dapr.WebApiSample/Util.Microservices.Dapr.WebApiSample.csproj new file mode 100644 index 000000000..51936dfa0 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/Util.Microservices.Dapr.WebApiSample.csproj @@ -0,0 +1,14 @@ + + + + $(NetTargetFramework) + enable + + + + + + + + + diff --git a/test/Util.Microservices.Dapr.WebApiSample/appsettings.Development.json b/test/Util.Microservices.Dapr.WebApiSample/appsettings.Development.json new file mode 100644 index 000000000..218a54b32 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/appsettings.Development.json @@ -0,0 +1,22 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft.AspNetCore": "Trace" + } + }, + "Serilog": { + "WriteTo": [ + { + "Name": "Seq", + "Args": { + "serverUrl": "http://localhost:5380" + } + } + ] + }, + "DaprPorts": { + "HttpPort": "51473", + "GrpcPort": "51474" + } +} diff --git a/test/Util.Microservices.Dapr.WebApiSample/appsettings.json b/test/Util.Microservices.Dapr.WebApiSample/appsettings.json new file mode 100644 index 000000000..a531fb681 --- /dev/null +++ b/test/Util.Microservices.Dapr.WebApiSample/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Trace", + "Microsoft.AspNetCore": "Trace" + } + }, + "AllowedHosts": "*" +} diff --git a/test/Util.Microservices.Polly.Tests.Integration/PolicyTest.Retry.Async.cs b/test/Util.Microservices.Polly.Tests.Integration/PolicyTest.Retry.Async.cs new file mode 100644 index 000000000..8bc3f1ff2 --- /dev/null +++ b/test/Util.Microservices.Polly.Tests.Integration/PolicyTest.Retry.Async.cs @@ -0,0 +1,310 @@ +using Util.Microservices.Polly.Tests.Samples; + +namespace Util.Microservices.Polly.Tests; + +/// +/// ԴԲ - Բ - 첽 +/// +public partial class PolicyTest { + + #region HandleException + + /// + /// - 쳣 - 1 + /// + [Fact] + public async Task TestRetry_1_Async() { + var i = 0; + await Assert.ThrowsAsync( async () => { + await _policy.Retry().HandleException().ExecuteAsync( async () => { + i++; + await Task.CompletedTask; + throw new Exception(); + } ); + } ); + Assert.Equal( 2, i ); + } + + /// + /// - 쳣 - 3 + /// + [Fact] + public async Task TestRetry_2_Async() { + var i = 0; + await Assert.ThrowsAsync( async () => { + await _policy.Retry( 3 ).HandleException().ExecuteAsync( async () => { + i++; + await Task.CompletedTask; + throw new Exception(); + } ); + } ); + Assert.Equal( 4, i ); + } + + /// + /// - 쳣 - + /// + [Fact] + public async Task TestRetry_3_Async() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + await _policy.Retry().HandleException().Forever().ExecuteAsync( async () => { + i++; + await Task.CompletedTask; + if ( i < 100 ) + throw new Exception(); + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( 100, i ); + Assert.True( stopwatch.Elapsed.Milliseconds < 50 ); + } + + /// + /// - 쳣 - 3 - ȴ + /// + [Fact] + public async Task TestRetry_4_Async() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + await Assert.ThrowsAsync( async () => { + await _policy.Retry( 3 ).HandleException() + .Wait( retry => TimeSpan.FromMilliseconds( retry * 10 ) ) + .ExecuteAsync( async () => { + i++; + await Task.CompletedTask; + throw new Exception(); + } ); + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( 4, i ); + Assert.True( stopwatch.Elapsed.Milliseconds > 60 ); + } + + /// + /// - 쳣 - ص + /// + [Fact] + public async Task TestRetry_5_Async() { + var i = 0; + await Assert.ThrowsAsync( async () => { + await _policy.Retry( 3 ).HandleException() + .OnRetry( ( e, n ) => { i = n; } ) + .ExecuteAsync( async () => { + await Task.CompletedTask; + throw new Exception(); + } ); + } ); + Assert.Equal( 3, i ); + } + + /// + /// - 쳣 - ص - + /// + [Fact] + public async Task TestRetry_6_Async() { + var i = 0; + await _policy.Retry().HandleException().Forever() + .OnRetry( ( e, n ) => { i = n; } ) + .ExecuteAsync( async () => { + await Task.CompletedTask; + if ( i < 3 ) + throw new Exception(); + } ); + Assert.Equal( 3, i ); + } + + /// + /// - 쳣 - ص - ȴ + /// + [Fact] + public async Task TestRetry_7_Async() { + var i = 0; + await Assert.ThrowsAsync( async () => { + await _policy.Retry( 3 ).HandleException() + .Wait( retry => TimeSpan.FromMilliseconds( 1 ) ) + .OnRetry( ( e, n ) => { i = n; } ) + .ExecuteAsync( async () => { + await Task.CompletedTask; + throw new Exception(); + } ); + } ); + Assert.Equal( 3, i ); + } + + /// + /// - 쳣 - ص - ȴ - + /// + [Fact] + public async Task TestRetry_8_Async() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + await _policy.Retry().HandleException().Forever() + .Wait( retry => TimeSpan.FromMilliseconds( retry * 10 ) ) + .OnRetry( ( e, n ) => { i = n; } ) + .ExecuteAsync( async () => { + await Task.CompletedTask; + if ( i < 3 ) + throw new Exception(); + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( 3, i ); + Assert.True( stopwatch.Elapsed.Milliseconds > 60 ); + } + + #endregion + + #region HandleResult + + /// + /// - - 1 + /// + [Fact] + public async Task TestRetry_HandleResult_1_Async() { + var i = 0; + var result = await _policy.Retry().HandleResult( t => t.Result != "ok" ) + .ExecuteAsync( async () => { + i++; + await Task.CompletedTask; + return new SampleResult { Result = i.ToString() }; + } ); + Assert.Equal( "2", result.Result ); + } + + /// + /// - - 3 + /// + [Fact] + public async Task TestRetry_HandleResult_2_Async() { + var i = 0; + var result = await _policy.Retry( 3 ).HandleResult( t => t.Result != "ok" ) + .ExecuteAsync( async () => { + i++; + await Task.CompletedTask; + return new SampleResult { Result = i.ToString() }; + } ); + Assert.Equal( "4", result.Result ); + } + + /// + /// - - + /// + [Fact] + public async Task TestRetry_HandleResult_3_Async() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var result = await _policy.Retry() + .HandleResult( t => t.Result != "ok" ) + .Forever() + .ExecuteAsync( async () => { + i++; + await Task.CompletedTask; + if ( i < 100 ) + return new SampleResult { Result = i.ToString() }; + return new SampleResult { Result = "ok" }; + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( "ok", result.Result ); + Assert.True( stopwatch.Elapsed.Milliseconds < 50 ); + } + + /// + /// - - 3 - ȴ + /// + [Fact] + public async Task TestRetry_HandleResult_4_Async() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var result = await _policy.Retry( 3 ) + .HandleResult( t => t.Result != "ok" ) + .Wait( retry => TimeSpan.FromMilliseconds( retry * 10 ) ) + .ExecuteAsync( async () => { + i++; + await Task.CompletedTask; + return new SampleResult { Result = i.ToString() }; + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( "4", result.Result ); + Assert.True( stopwatch.Elapsed.Milliseconds > 60 ); + } + + /// + /// - - ص + /// + [Fact] + public async Task TestRetry_HandleResult_5_Async() { + var sample = new SampleResult(); + var result = await _policy.Retry( 3 ) + .HandleResult( t => t.Result != "ok" ) + .OnRetry( ( result, n ) => { + result.Result.Result = n.ToString(); + } ) + .ExecuteAsync( async () => await Task.FromResult(sample) ); + Assert.Equal( "3", result.Result ); + } + + /// + /// - - ص - + /// + [Fact] + public async Task TestRetry_HandleResult_6_Async() { + var sample = new SampleResult(); + var result = await _policy.Retry() + .HandleResult( t => t.Result != "10" ) + .Forever() + .OnRetry( ( result, n ) => { + result.Result.Result = n.ToString(); + } ) + .ExecuteAsync( async () => await Task.FromResult( sample ) ); + Assert.Equal( "10", result.Result ); + } + + /// + /// - - ص - ȴ + /// + [Fact] + public async Task TestRetry_HandleResult_7_Async() { + var sample = new SampleResult(); + var result = await _policy.Retry( 3 ) + .HandleResult( t => t.Result != "ok" ) + .Wait( retry => TimeSpan.FromMilliseconds( 1 ) ) + .OnRetry( ( result, n ) => { + result.Result.Result = n.ToString(); + } ) + .ExecuteAsync( async () => await Task.FromResult( sample ) ); + Assert.Equal( "3", result.Result ); + } + + /// + /// - - ص - ȴ - + /// + [Fact] + public async Task TestRetry_HandleResult_8_Async() { + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var sample = new SampleResult(); + var result = await _policy.Retry() + .HandleResult( t => t.Result != "3" ) + .Wait( retry => TimeSpan.FromMilliseconds( retry * 10 ) ) + .Forever() + .OnRetry( ( result, n ) => { + result.Result.Result = n.ToString(); + } ) + .ExecuteAsync( async () => await Task.FromResult( sample ) ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( "3", result.Result ); + Assert.True( stopwatch.Elapsed.Milliseconds > 60 ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Microservices.Polly.Tests.Integration/PolicyTest.Retry.cs b/test/Util.Microservices.Polly.Tests.Integration/PolicyTest.Retry.cs new file mode 100644 index 000000000..9c741b148 --- /dev/null +++ b/test/Util.Microservices.Polly.Tests.Integration/PolicyTest.Retry.cs @@ -0,0 +1,307 @@ +using Util.Microservices.Polly.Tests.Samples; + +namespace Util.Microservices.Polly.Tests; + +/// +/// ԴԲ - Բ +/// +public partial class PolicyTest { + + #region HandleException + + /// + /// - 쳣 - 1 + /// + [Fact] + public void TestRetry_1() { + var i = 0; + Assert.Throws( () => { + _policy.Retry().HandleException().Execute( () => { + i++; + throw new Exception(); + } ); + } ); + Assert.Equal( 2, i ); + } + + /// + /// - 쳣 - 3 + /// + [Fact] + public void TestRetry_2() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + Assert.Throws( () => { + _policy.Retry( 3 ).HandleException().Execute( () => { + i++; + throw new Exception(); + } ); + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( 4, i ); + Assert.True( stopwatch.Elapsed.Milliseconds < 50 ); + } + + /// + /// - 쳣 - + /// + [Fact] + public void TestRetry_3() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + _policy.Retry().HandleException().Forever().Execute( () => { + i++; + if ( i < 100 ) + throw new Exception(); + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( 100, i ); + Assert.True( stopwatch.Elapsed.Milliseconds < 50 ); + } + + /// + /// - 쳣 - 3 - 1εȴ10룬2εȴ20룬3εȴ30 + /// + [Fact] + public void TestRetry_4() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + Assert.Throws( () => { + _policy.Retry( 3 ).HandleException() + .Wait( retry => TimeSpan.FromMilliseconds( retry * 10 ) ) + .Execute( () => { + i++; + throw new Exception(); + } ); + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( 4, i ); + Assert.True( stopwatch.Elapsed.Milliseconds > 60 ); + } + + /// + /// - 쳣 - ص + /// + [Fact] + public void TestRetry_5() { + var i = 0; + Assert.Throws( () => { + _policy.Retry( 3 ).HandleException() + .OnRetry( ( e, n ) => { + i = n; + } ) + .Execute( () => throw new Exception() ); + } ); + Assert.Equal( 3, i ); + } + + /// + /// - 쳣 - ص - + /// + [Fact] + public void TestRetry_6() { + var i = 0; + _policy.Retry().HandleException().Forever() + .OnRetry( ( e, n ) => { + i = n; + } ) + .Execute( () => { + if ( i < 3 ) + throw new Exception(); + } ); + Assert.Equal( 3, i ); + } + + /// + /// - 쳣 - ص - ȴ + /// + [Fact] + public void TestRetry_7() { + var i = 0; + Assert.Throws( () => { + _policy.Retry( 3 ).HandleException() + .Wait( retry => TimeSpan.FromMilliseconds( 1 ) ) + .OnRetry( ( e, n ) => { + i = n; + } ) + .Execute( () => throw new Exception() ); + } ); + Assert.Equal( 3, i ); + } + + /// + /// - 쳣 - ص - ȴ - + /// + [Fact] + public void TestRetry_8() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + _policy.Retry().HandleException().Forever() + .Wait( retry => TimeSpan.FromMilliseconds( retry * 10 ) ) + .OnRetry( ( e, n ) => { + i = n; + } ) + .Execute( () => { + if ( i < 3 ) + throw new Exception(); + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( 3, i ); + Assert.True( stopwatch.Elapsed.Milliseconds > 60 ); + } + + #endregion + + #region HandleResult + + /// + /// - - 1 + /// + [Fact] + public void TestRetry_HandleResult_1() { + var i = 0; + var result = _policy.Retry().HandleResult( t => t.Result != "ok" ) + .Execute( () => { + i++; + return new SampleResult { Result = i.ToString() }; + } ); + Assert.Equal( "2", result.Result ); + } + + /// + /// - - 3 + /// + [Fact] + public void TestRetry_HandleResult_2() { + var i = 0; + var result = _policy.Retry( 3 ).HandleResult( t => t.Result != "ok" ) + .Execute( () => { + i++; + return new SampleResult { Result = i.ToString() }; + } ); + Assert.Equal( "4", result.Result ); + } + + /// + /// - - + /// + [Fact] + public void TestRetry_HandleResult_3() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var result = _policy.Retry() + .HandleResult( t => t.Result != "ok" ) + .Forever() + .Execute( () => { + i++; + if ( i < 100 ) + return new SampleResult { Result = i.ToString() }; + return new SampleResult { Result = "ok" }; + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( "ok", result.Result ); + Assert.True( stopwatch.Elapsed.Milliseconds < 50 ); + } + + /// + /// - - 3 - ȴ + /// + [Fact] + public void TestRetry_HandleResult_4() { + var i = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var result = _policy.Retry( 3 ) + .HandleResult( t => t.Result != "ok" ) + .Wait( retry => TimeSpan.FromMilliseconds( retry * 10 ) ) + .Execute( () => { + i++; + return new SampleResult { Result = i.ToString() }; + } ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( "4", result.Result ); + Assert.True( stopwatch.Elapsed.Milliseconds > 60 ); + } + + /// + /// - - ص + /// + [Fact] + public void TestRetry_HandleResult_5() { + var sample = new SampleResult(); + var result = _policy.Retry( 3 ) + .HandleResult( t => t.Result != "ok" ) + .OnRetry( ( result, n ) => { + result.Result.Result = n.ToString(); + } ) + .Execute( () => sample ); + Assert.Equal( "3", result.Result ); + } + + /// + /// - - ص - + /// + [Fact] + public void TestRetry_HandleResult_6() { + var sample = new SampleResult(); + var result = _policy.Retry() + .HandleResult( t => t.Result != "10" ) + .Forever() + .OnRetry( ( result, n ) => { + result.Result.Result = n.ToString(); + } ) + .Execute( () => sample ); + Assert.Equal( "10", result.Result ); + } + + /// + /// - - ص - ȴ + /// + [Fact] + public void TestRetry_HandleResult_7() { + var sample = new SampleResult(); + var result = _policy.Retry( 3 ) + .HandleResult( t => t.Result != "ok" ) + .Wait( retry => TimeSpan.FromMilliseconds( 1 ) ) + .OnRetry( ( result, n ) => { + result.Result.Result = n.ToString(); + } ) + .Execute( () => sample ); + Assert.Equal( "3", result.Result ); + } + + /// + /// - - ص - ȴ - + /// + [Fact] + public void TestRetry_HandleResult_8() { + var sample = new SampleResult(); + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var result = _policy.Retry() + .HandleResult( t => t.Result != "3" ) + .Wait( retry => TimeSpan.FromMilliseconds( retry * 10 ) ) + .Forever() + .OnRetry( ( result, n ) => { + result.Result.Result = n.ToString(); + } ) + .Execute( () => sample ); + stopwatch.Stop(); + _output.WriteLine( stopwatch.Elapsed.Milliseconds.ToString() ); + Assert.Equal( "3", result.Result ); + Assert.True( stopwatch.Elapsed.Milliseconds > 60 ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Microservices.Polly.Tests.Integration/PolicyTest.cs b/test/Util.Microservices.Polly.Tests.Integration/PolicyTest.cs new file mode 100644 index 000000000..7785844c4 --- /dev/null +++ b/test/Util.Microservices.Polly.Tests.Integration/PolicyTest.cs @@ -0,0 +1,23 @@ +namespace Util.Microservices.Polly.Tests; + +/// +/// ԴԲ +/// +public partial class PolicyTest { + /// + /// Դ + /// + private readonly IPolicy _policy; + /// + /// + /// + private readonly ITestOutputHelper _output; + + /// + /// Գʼ + /// + public PolicyTest( IPolicy policy, ITestOutputHelper output ) { + _policy = policy; + _output = output; + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Polly.Tests.Integration/Samples/SampleException.cs b/test/Util.Microservices.Polly.Tests.Integration/Samples/SampleException.cs new file mode 100644 index 000000000..f09abf6a2 --- /dev/null +++ b/test/Util.Microservices.Polly.Tests.Integration/Samples/SampleException.cs @@ -0,0 +1,7 @@ +namespace Util.Microservices.Polly.Tests.Samples; + +/// +/// 样例异常 +/// +public class SampleException : Exception { +} \ No newline at end of file diff --git a/test/Util.Microservices.Polly.Tests.Integration/Samples/SampleResult.cs b/test/Util.Microservices.Polly.Tests.Integration/Samples/SampleResult.cs new file mode 100644 index 000000000..ade9d1ec4 --- /dev/null +++ b/test/Util.Microservices.Polly.Tests.Integration/Samples/SampleResult.cs @@ -0,0 +1,11 @@ +namespace Util.Microservices.Polly.Tests.Samples; + +/// +/// 样例结果 +/// +public class SampleResult { + /// + /// 结果 + /// + public string Result { get; set; } +} \ No newline at end of file diff --git a/test/Util.Microservices.Polly.Tests.Integration/Startup.cs b/test/Util.Microservices.Polly.Tests.Integration/Startup.cs new file mode 100644 index 000000000..2d35ec1f5 --- /dev/null +++ b/test/Util.Microservices.Polly.Tests.Integration/Startup.cs @@ -0,0 +1,22 @@ +namespace Util.Microservices.Polly.Tests; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + hostBuilder.ConfigureDefaults( null ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + Ioc.SetServiceProviderAction( services.BuildServiceProvider ); + } +} \ No newline at end of file diff --git a/test/Util.Microservices.Polly.Tests.Integration/Usings.cs b/test/Util.Microservices.Polly.Tests.Integration/Usings.cs new file mode 100644 index 000000000..7fe41f2cb --- /dev/null +++ b/test/Util.Microservices.Polly.Tests.Integration/Usings.cs @@ -0,0 +1,14 @@ +global using System; +global using System.Net.Http; +global using Xunit; +global using Xunit.Abstractions; +global using Xunit.DependencyInjection.Logging; +global using System.Text; +global using System.Threading.Tasks; +global using System.Diagnostics; +global using System.ComponentModel; +global using System.ComponentModel.DataAnnotations; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; +global using Util.Helpers; diff --git a/test/Util.Microservices.Polly.Tests.Integration/Util.Microservices.Polly.Tests.Integration.csproj b/test/Util.Microservices.Polly.Tests.Integration/Util.Microservices.Polly.Tests.Integration.csproj new file mode 100644 index 000000000..a3ec92337 --- /dev/null +++ b/test/Util.Microservices.Polly.Tests.Integration/Util.Microservices.Polly.Tests.Integration.csproj @@ -0,0 +1,29 @@ + + + + $(NetTargetFramework) + false + Util.Microservices.Polly.Tests + Util.Microservices.Polly.Tests.Startup + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.ObjectMapping.AutoMapper.Tests/Configs/TestAutoMapperConfig.cs b/test/Util.ObjectMapping.AutoMapper.Tests/Configs/TestAutoMapperConfig.cs new file mode 100644 index 000000000..5ae293e95 --- /dev/null +++ b/test/Util.ObjectMapping.AutoMapper.Tests/Configs/TestAutoMapperConfig.cs @@ -0,0 +1,18 @@ +using AutoMapper; +using Util.ObjectMapping.AutoMapper.Tests.Samples; + +namespace Util.ObjectMapping.AutoMapper.Tests.Configs; + +/// +/// AutoMapper测试配置 - 该配置通过启动器扫描注册 +/// +public class TestAutoMapperConfig : IAutoMapperConfig { + /// + /// 配置映射 + /// + /// 配置映射表达式 + public void Config( IMapperConfigurationExpression expression ) { + expression.CreateMap() + .ForMember( o => o.StringValue, o => o.MapFrom( ( s, d ) => s.StringValue + "-1" ) ); + } +} \ No newline at end of file diff --git a/test/Util.ObjectMapping.AutoMapper.Tests/ObjectMapperExtensionsTest.cs b/test/Util.ObjectMapping.AutoMapper.Tests/ObjectMapperExtensionsTest.cs new file mode 100644 index 000000000..beef02196 --- /dev/null +++ b/test/Util.ObjectMapping.AutoMapper.Tests/ObjectMapperExtensionsTest.cs @@ -0,0 +1,226 @@ +using System; +using System.Collections.Generic; +using AutoMapper; +using Util.Helpers; +using Util.ObjectMapping.AutoMapper.Tests.Samples; +using Xunit; + +namespace Util.ObjectMapping.AutoMapper.Tests; + +/// +/// ӳ +/// +public class ObjectMapperExtensionsTest { + /// + /// ӳ - Sample2 -> Sample δ,Զӳ + /// + [Fact] + public void TestMapTo_1() { + var sample = new Sample { StringValue = "a" }; + var sample2 = sample.MapTo(); + Assert.Equal( "a", sample2.StringValue ); + } + + /// + /// ӳ - ӳͬIJͬ + /// + [Fact] + public void TestMapTo_2() { + Sample sample = new Sample { Test3 = new Sample3Copy { StringValue = "a" } }; + Sample2 sample2 = sample.MapTo(); + Assert.Equal( "a", sample2.Test3.StringValue ); + } + + /// + /// ӳ - ӳͬIJͬ󼯺 + /// + [Fact] + public void TestMapTo_3() { + Sample sample = new Sample { TestList = new List { new() { StringValue = "a" }, new() { StringValue = "b" } } }; + Sample2 sample2 = sample.MapTo(); + Assert.Equal( 2, sample2.TestList.Count ); + Assert.Equal( "a", sample2.TestList[0].StringValue ); + Assert.Equal( "b", sample2.TestList[1].StringValue ); + } + + /// + /// ӳ伯 + /// + [Fact] + public void TestMapTo_4() { + List sampleList = new List { new() { StringValue = "a" }, new() { StringValue = "b" } }; + List sample2List = new List(); + sampleList.MapTo( sample2List ); + Assert.Equal( 2, sample2List.Count ); + Assert.Equal( "a", sample2List[0].StringValue ); + } + + /// + /// ӳ伯 + /// + [Fact] + public void TestMapTo_5() { + List sampleList = new List { new() { StringValue = "a" }, new() { StringValue = "b" } }; + List sample2List = sampleList.MapTo>(); + Assert.Equal( 2, sample2List.Count ); + Assert.Equal( "a", sample2List[0].StringValue ); + } + + /// + /// + /// + [Fact] + public void TestMapTo_6() { + Thread.ParallelFor( () => { + var sample = new Sample { StringValue = "a" }; + var sample2 = sample.MapTo(); + Assert.Equal( "a", sample2.StringValue ); + }, 20 ); + } + + /// + /// ӳ - Sample -> Sample4 ӳϵ + /// + [Fact] + public void TestMapTo_7() { + var sample = new Sample { StringValue = "a" }; + var sample4 = sample.MapTo(); + Assert.Equal( "a-1", sample4.StringValue ); + } + + /// + /// ӳ- ӳϵ - + /// + [Fact] + public void TestMapTo_8() { + var sample = new Sample { StringValue = "a" }; + var sample4 = new Sample4(); + sample.MapTo( sample4 ); + Assert.Equal( "a-1", sample4.StringValue ); + } + + /// + /// ӳ - ִԤӳ + /// + [Fact] + public void TestMapTo_9() { + var sample = new Sample { StringValue = "a" }; + var sample4 = sample.MapTo(); + Assert.Equal( "a-1", sample4.StringValue ); + + sample.StringValue = "b"; + sample4 = sample.MapTo(); + Assert.Equal( "b-1", sample4.StringValue ); + + sample.StringValue = "c"; + sample4 = sample.MapTo(); + Assert.Equal( "c-1", sample4.StringValue ); + } + + /// + /// ӳ - ԤӳԶӳִ + /// + [Fact] + public void TestMapTo_10() { + var sample = new Sample { StringValue = "a" }; + var sample4 = sample.MapTo(); + Assert.Equal( "a-1", sample4.StringValue ); + + sample.StringValue = "b"; + var sample2 = sample.MapTo(); + Assert.Equal( "b", sample2.StringValue ); + + sample.StringValue = "c"; + sample4 = sample.MapTo(); + Assert.Equal( "c-1", sample4.StringValue ); + } + + /// + /// Ծмѭij + /// + [Fact] + public void TestMapTo_11() { + var sample = new Sample6() { + Name = "Sample6", + Sample51 = new Sample5 { + Name = "Sample51", + Sample61 = new() { + Name = "Sample61", + Sample51 = new() { + Name = "Sample512" + } + }, + }, + Sample71 = new Sample7 { + Name = "Sample71", + Sample75 = new() { Name = "Sample75" } + } + }; + var result = sample.MapTo(); + Assert.Equal( "Sample6", result.Name ); + Assert.Equal( "Sample51", result.Sample51.Name ); + Assert.Equal( "Sample61", result.Sample51.Sample61.Name ); + Assert.Equal( "Sample512", result.Sample51.Sample61.Sample51.Name ); + Assert.Equal( "Sample71", result.Sample71.Name ); + Assert.Equal( "Sample75", result.Sample71.Sample75.Name ); + } + + /// + /// ӳ伯 + /// + [Fact] + public void TestMapToList() { + List sampleList = new List { new() { StringValue = "a" }, new() { StringValue = "b" } }; + List sample2List = sampleList.MapToList(); + Assert.Equal( 2, sample2List.Count ); + Assert.Equal( "a", sample2List[0].StringValue ); + } + + /// + /// ӳ伯 - Կռ + /// + [Fact] + public void TestMapToList_2() { + List sampleList = new List(); + List sample2List = sampleList.MapToList(); + Assert.Empty( sample2List ); + } + + /// + /// ӳ伯 - + /// + [Fact] + public void TestMapToList_3() { + Sample[] sampleList = { new() { StringValue = "a" }, new() { StringValue = "b" } }; + List sample2List = sampleList.MapToList(); + Assert.Equal( 2, sample2List.Count ); + Assert.Equal( "a", sample2List[0].StringValue ); + } + + /// + /// ʧӳ׳쳣 + /// + [Fact] + public void TestMapTo_Throw() { + //ɹӳ + var parentId = Guid.NewGuid().SafeString(); + var dto = new TreeEntityDto() { + Name = "Test1", + ParentId = parentId + }; + var entity = dto.MapTo(); + Assert.Equal( parentId, entity.ParentId.SafeString() ); + + //ӳʧ,׳쳣 + var parentId2 = "/"; + var dto2 = new TreeEntityDto() { + Name = "Test2", + ParentId = parentId2 + }; + Assert.Throws( () => dto2.MapTo() ); + + //ɹӳ,ʧӳ䲻Ӱ֮ǰӳϵ + var entity3 = dto.MapTo(); + Assert.Equal( parentId, entity3.ParentId.SafeString() ); + } +} \ No newline at end of file diff --git a/test/Util.ObjectMapping.AutoMapper.Tests/ObjectMapperTest.cs b/test/Util.ObjectMapping.AutoMapper.Tests/ObjectMapperTest.cs new file mode 100644 index 000000000..0658aadc7 --- /dev/null +++ b/test/Util.ObjectMapping.AutoMapper.Tests/ObjectMapperTest.cs @@ -0,0 +1,71 @@ +using Util.ObjectMapping.AutoMapper.Tests.Samples; +using Xunit; + +namespace Util.ObjectMapping.AutoMapper.Tests; + +/// +/// ӳ +/// +public class ObjectMapperTest { + /// + /// ӳ + /// + private readonly IObjectMapper _mapper; + + /// + /// Գʼ + /// + public ObjectMapperTest( IObjectMapper mapper ) { + _mapper = mapper; + } + + /// + /// ӳ - Sample -> Sample2 ӳϵ + /// + [Fact] + public void TestMap_1() { + var sample = new Sample { StringValue = "a" }; + var sample2 =_mapper.Map( sample ); + Assert.Equal( "a", sample2.StringValue ); + } + + /// + /// ӳ- Sample -> Sample2 ӳϵ - + /// + [Fact] + public void TestMap_2() { + var sample = new Sample { StringValue = "a" }; + var sample2 = new Sample2(); + _mapper.Map( sample, sample2 ); + Assert.Equal( "a", sample2.StringValue ); + } + + /// + /// ӳ - Sample2 -> Sample δ,Զӳ + /// + [Fact] + public void TestMap_3() { + var sample2 = new Sample2 { StringValue = "a" }; + var sample = _mapper.Map( sample2 ); + Assert.Equal( "a", sample.StringValue ); + } + + /// + /// ӳ - ֶ̬úӳ֮ǰó + /// 1. ִSample -> Sample2ӳ,ӳϵ + /// 2. ִSample2 -> Sampleӳ,δ,Զӳ + /// 3. ظִSample -> Sample2ӳ + /// + [Fact] + public void TestMap_4() { + var sample = new Sample { StringValue = "a" }; + var sample2 = _mapper.Map( sample ); + Assert.Equal( "a", sample2.StringValue ); + + var sample3 = _mapper.Map( sample2 ); + Assert.Equal( "a", sample3.StringValue ); + + var sample4 = _mapper.Map( sample ); + Assert.Equal( "a", sample4.StringValue ); + } +} \ No newline at end of file diff --git a/test/Util.ObjectMapping.AutoMapper.Tests/Samples/EnumSample.cs b/test/Util.ObjectMapping.AutoMapper.Tests/Samples/EnumSample.cs new file mode 100644 index 000000000..8c4dd611d --- /dev/null +++ b/test/Util.ObjectMapping.AutoMapper.Tests/Samples/EnumSample.cs @@ -0,0 +1,18 @@ +using System.ComponentModel; + +namespace Util.ObjectMapping.AutoMapper.Tests.Samples; + +/// +/// 枚举测试样例 +/// +public enum EnumSample { + A = 1, + [Description( "B2" )] + B = 2, + [Description( "C3" )] + C = 3, + [Description( "D4" )] + D = 4, + [Description( "E5" )] + E = 5 +} \ No newline at end of file diff --git a/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample.cs b/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample.cs new file mode 100644 index 000000000..11b79519c --- /dev/null +++ b/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace Util.ObjectMapping.AutoMapper.Tests.Samples; + +public interface ISample { +} + +/// +/// 测试样例 +/// +[Description( "测试样例" )] +public class Sample : ISample { + /// + /// 初始化测试样例 + /// + public Sample() { + StringList = new List(); + StringArray = StringList.ToArray(); + } + + /// + /// 测试值 + /// + [Display( Name = "测试值" )] + [Required( ErrorMessage = "测试值不能为空" )] + [StringLength( 20, ErrorMessage = "测试值长度不能超过20" )] + [EmailAddress] + [Url] + public string TestValue { get; set; } + /// + /// 电子邮件验证 + /// + [EmailAddress( ErrorMessage = "电子邮件不正确" )] + public string Email { get; set; } + /// + /// 网址验证 + /// + [Url( ErrorMessage = "网址不正确" )] + public string Url { get; set; } + /// + /// 最大长度 + /// + [MaxLength( 2, ErrorMessage = "最大长度是2" )] + public string MaxLengthValue { get; set; } + /// + /// 长度验证 + /// + [StringLength( 3, MinimumLength = 2, ErrorMessage = "最大长度是3,最小长度是2" )] + public string StringLengthValue { get; set; } + /// + /// DisplayName + /// + [DisplayName( "DisplayNameValue值" )] + public string DisplayNameValue { get; set; } + /// + /// Display + /// + [Display( Name = "DisplayValue值" )] + public string DisplayValue { get; set; } + /// + /// string值 + /// + [Display( Name = "字符串值" )] + [StringLength( 20, ErrorMessage = "长度不能超过20" )] + [Required( ErrorMessage = "不能为空" )] + public string StringValue { get; set; } + /// + /// decimal值 + /// + public decimal DecimalValue { get; set; } + /// + /// 可空decimal值 + /// + public decimal? NullableDecimalValue { get; set; } + /// + /// float值 + /// + public float FloatValue { get; set; } + /// + /// 可空float值 + /// + public float? NullableFloatValue { get; set; } + /// + /// double值 + /// + public double DoubleValue { get; set; } + /// + /// 可空double值 + /// + public double? NullableDoubleValue { get; set; } + /// + /// bool值 + /// + public bool BoolValue { get; set; } + /// + /// 可空bool值 + /// + public bool? NullableBoolValue { get; set; } + /// + /// Enum值 + /// + public EnumSample EnumValue { get; set; } + /// + /// 可空Enum值 + /// + public EnumSample? NullableEnumValue { get; set; } + /// + /// DateTime值 + /// + public DateTime DateValue { get; set; } + /// + /// 可空DateTime值 + /// + public DateTime? NullableDateValue { get; set; } + /// + /// int值 + /// + [Description( "IntValue" )] + public int IntValue { get; set; } + /// + /// 可空int值 + /// + public int? NullableIntValue { get; set; } + /// + /// short值 + /// + public short ShortValue { get; set; } + /// + /// 可空short值 + /// + public short? NullableShortValue { get; set; } + /// + /// long值 + /// + public long LongValue { get; set; } + /// + /// 可空long值 + /// + public long? NullableLongValue { get; set; } + /// + /// 字符串列表 + /// + public List StringList { get; set; } + /// + /// 字符串数据 + /// + public string[] StringArray { get; set; } + /// + /// Guid值 + /// + public Guid GuidValue { get; set; } + + /// + /// 测试2 + /// + public Sample2 Test2 { get; set; } + + /// + /// 测试3 + /// + public Sample3Copy Test3 { get; set; } + + /// + /// 导航属性 + /// + public List TestList { get; set; } + + /// + /// 静态属性 + /// + public static string StaticString => "TestStaticString"; + + /// + /// 静态对象 + /// + public static Sample2 StaticSample => new Sample2 { StringValue = "TestStaticSample" }; + + /// + /// 创建测试实例1 + /// + public static Sample Create1() { + return new Sample { StringValue = "A" }; + } + + /// + /// 创建测试实例2 + /// + public static Sample Create2() { + return new Sample { StringValue = "B" }; + } +} \ No newline at end of file diff --git a/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample2.cs b/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample2.cs new file mode 100644 index 000000000..574b24494 --- /dev/null +++ b/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample2.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace Util.ObjectMapping.AutoMapper.Tests.Samples; + +/// +/// 测试样例2 +/// +[DisplayName( "测试样例2" )] +public class Sample2 { + /// + /// 描述 + /// + [Description("描述")] + public string Description { get; set; } + /// + /// 显示名 + /// + [DisplayName( "显示名" )] + public string DisplayName { get; set; } + /// + /// 显示描述 + /// + [Display( Description= "显示描述" )] + public string Display { get; set; } + /// + /// string值 + /// + public string StringValue { get; set; } + /// + /// int值 + /// + public int IntValue { get; set; } + /// + /// bool值 + /// + public bool BoolValue { get; set; } + /// + /// 可空bool值 + /// + public bool? NullableBoolValue { get; set; } + /// + /// 导航属性 + /// + public Sample3 Test3 { get; set; } + /// + /// 导航属性 + /// + public List TestList { get; set; } +} \ No newline at end of file diff --git a/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample3.cs b/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample3.cs new file mode 100644 index 000000000..eba261436 --- /dev/null +++ b/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample3.cs @@ -0,0 +1,24 @@ +using System.ComponentModel; + +namespace Util.ObjectMapping.AutoMapper.Tests.Samples; + +/// +/// 测试样例3 +/// +[DisplayName( "测试样例3" )] +public class Sample3 { + /// + /// string值 + /// + public string StringValue { get; set; } +} + +/// +/// 测试样例3 +/// +public class Sample3Copy { + /// + /// string值 + /// + public string StringValue { get; set; } +} \ No newline at end of file diff --git a/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample4.cs b/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample4.cs new file mode 100644 index 000000000..7a92a64a5 --- /dev/null +++ b/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample4.cs @@ -0,0 +1,11 @@ +namespace Util.ObjectMapping.AutoMapper.Tests.Samples; + +/// +/// 测试样例4 +/// +public class Sample4 { + /// + /// string值 + /// + public string StringValue { get; set; } +} \ No newline at end of file diff --git a/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample5.cs b/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample5.cs new file mode 100644 index 000000000..c330d78ba --- /dev/null +++ b/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample5.cs @@ -0,0 +1,17 @@ +namespace Util.ObjectMapping.AutoMapper.Tests.Samples; + +/// +/// 样例5 +/// +public class Sample5 { + public string Name { get; set; } + public Sample6 Sample61 { get; set; } +} + +/// +/// 样例转换5 +/// +public class Sample5to { + public string Name { get; set; } + public Sample6to Sample61 { get; set; } +} \ No newline at end of file diff --git a/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample6.cs b/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample6.cs new file mode 100644 index 000000000..dbda05bc0 --- /dev/null +++ b/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample6.cs @@ -0,0 +1,19 @@ +namespace Util.ObjectMapping.AutoMapper.Tests.Samples; + +/// +/// 6 +/// +public class Sample6 { + public string Name { get; set; } + public Sample7 Sample71 { get; set; } + public Sample5 Sample51 { get; set; } +} + +/// +/// ת6 +/// +public class Sample6to { + public string Name { get; set; } + public Sample7to Sample71 { get; set; } + public Sample5to Sample51 { get; set; } +} \ No newline at end of file diff --git a/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample7.cs b/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample7.cs new file mode 100644 index 000000000..db582222a --- /dev/null +++ b/test/Util.ObjectMapping.AutoMapper.Tests/Samples/Sample7.cs @@ -0,0 +1,17 @@ +namespace Util.ObjectMapping.AutoMapper.Tests.Samples; + +/// +/// 样例7 +/// +public class Sample7 { + public string Name { get; set; } + public Sample5 Sample75 { get; set; } +} + +/// +/// 样例转换7 +/// +public class Sample7to { + public string Name { get; set; } + public Sample5 Sample75 { get; set; } +} \ No newline at end of file diff --git a/test/Util.ObjectMapping.AutoMapper.Tests/Samples/TreeEntitySample.cs b/test/Util.ObjectMapping.AutoMapper.Tests/Samples/TreeEntitySample.cs new file mode 100644 index 000000000..b208f2af6 --- /dev/null +++ b/test/Util.ObjectMapping.AutoMapper.Tests/Samples/TreeEntitySample.cs @@ -0,0 +1,49 @@ +using System; +using Util.Applications.Trees; +using Util.Domain.Trees; + +namespace Util.ObjectMapping.AutoMapper.Tests.Samples; + +/// +/// 树形实体测试样例 +/// +public class TreeEntitySample : TreeEntityBase { + /// + /// 初始化树型实体测试样例 + /// + public TreeEntitySample() : this( Guid.Empty ) { + } + + /// + /// 初始化树型实体测试样例 + /// + public TreeEntitySample( Guid id ) + : base( id, "", 0 ) { + } + + /// + /// 初始化树型实体测试样例 + /// + public TreeEntitySample( Guid id, string path ) + : base( id, path, 0 ) { + } + + /// + /// 名称 + /// + public string Name { get; set; } +} + +/// +/// 树形Dto测试样例 +/// +public class TreeEntityDto : TreeDtoBase { + /// + /// 名称 + /// + public string Name { get; set; } + + public override string GetText() { + return Name; + } +} \ No newline at end of file diff --git a/test/Util.ObjectMapping.AutoMapper.Tests/Startup.cs b/test/Util.ObjectMapping.AutoMapper.Tests/Startup.cs new file mode 100644 index 000000000..c693604dc --- /dev/null +++ b/test/Util.ObjectMapping.AutoMapper.Tests/Startup.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit.DependencyInjection.Logging; + +namespace Util.ObjectMapping.AutoMapper.Tests; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + hostBuilder.AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + } +} \ No newline at end of file diff --git a/test/Util.ObjectMapping.AutoMapper.Tests/Util.ObjectMapping.AutoMapper.Tests.csproj b/test/Util.ObjectMapping.AutoMapper.Tests/Util.ObjectMapping.AutoMapper.Tests.csproj new file mode 100644 index 000000000..5459710c2 --- /dev/null +++ b/test/Util.ObjectMapping.AutoMapper.Tests/Util.ObjectMapping.AutoMapper.Tests.csproj @@ -0,0 +1,30 @@ + + + + $(NetTargetFramework) + false + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + diff --git a/test/Util.QrCode.ZXing.Tests.Integration/Icons/icon.jpg b/test/Util.QrCode.ZXing.Tests.Integration/Icons/icon.jpg new file mode 100644 index 000000000..b591214cc Binary files /dev/null and b/test/Util.QrCode.ZXing.Tests.Integration/Icons/icon.jpg differ diff --git a/test/Util.QrCode.ZXing.Tests.Integration/Startup.cs b/test/Util.QrCode.ZXing.Tests.Integration/Startup.cs new file mode 100644 index 000000000..36c7d8891 --- /dev/null +++ b/test/Util.QrCode.ZXing.Tests.Integration/Startup.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit.DependencyInjection.Logging; + +namespace Util.QrCode; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + hostBuilder.ConfigureDefaults( null ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + } +} \ No newline at end of file diff --git a/test/Util.QrCode.ZXing.Tests.Integration/Tests/ZXingQrCodeServiceTest.cs b/test/Util.QrCode.ZXing.Tests.Integration/Tests/ZXingQrCodeServiceTest.cs new file mode 100644 index 000000000..c4893c876 --- /dev/null +++ b/test/Util.QrCode.ZXing.Tests.Integration/Tests/ZXingQrCodeServiceTest.cs @@ -0,0 +1,151 @@ +using System.Drawing; +using System.Threading.Tasks; +using Util.Images; +using Xunit; +using Xunit.Abstractions; + +namespace Util.QrCode.Tests; + +/// +/// ZXing二维码服务 +/// +public class ZXingQrCodeServiceTest { + /// + /// 二维码服务 + /// + private readonly IQrCodeService _service; + /// + /// 测试输出 + /// + private readonly ITestOutputHelper _output; + /// + /// 内容 + /// + private readonly string _content; + + /// + /// 测试初始化 + /// + public ZXingQrCodeServiceTest( IQrCodeService service, ITestOutputHelper output ) { + _service = service; + _output = output; + _content = "https://gitee.com/util-core/util"; + } + + /// + /// 测试获取二维码流 + /// + [Fact] + public void TestToStream() { + var result = _service.Content( _content ).ToStream(); + Util.Helpers.File.Write( "./test/ToStream.png", result ); + } + + /// + /// 测试获取二维码字节流 + /// + [Fact] + public void TestToBytes() { + var result = _service.Content( _content ).ToBytes(); + Util.Helpers.File.Write( "./test/ToBytes.png", result ); + } + + /// + /// 测试获取二维码Base64字符串 + /// + [Fact] + public void TestToBase64_1() { + var result = _service.Content( _content ).ToBase64(); + string base64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKAAAACgCAYAAACLz2ctAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADyklEQVR4nO3dQU4jOxRAUZB6/1vm/0lLPYiaCn7xdaXPGQMp4MqK43L519f/PiDy6wNCAiQlQFICJCVAUgIk9dcAPz8/P07x6NOileu7+vOqT6mu/m7Tf5dp3/39jICkBEhKgKQESOrpAHe8Ka/eRE//bisTiR2Trmk/uT4jICkBkhIgKQGSGgnwpDfHV9+8X131WPm6q9f3yI6J2An/NyMgKQGSEiApAZJ6uwB3TBpW3rxPT1bu7u0C5F4ESEqApARI6tYBrrxRn16RmF5FOWl/yivdOkDuT4CkBEhKgKRGAjxp83Z1u9P0CswOJ0xqjICkBEhKgKQESOrpAE96E33Vjv0fJ33dI6f+34yApARISoCkBEjqrwF+HX77z+mP2b36und8EtYUIyApAZISICkBkho5J2THp/Erb+ivXsvKZGDH5Gfl51W3izknhKMJkJQASQmQ1MtWQnZMYHY89errDTeD/7bjNrDvGAFJCZCUAEkJkNTL9oTsWFXYsUdix8rASXtMph8/bCWEowmQlABJCZDUywLcMeGYvpYdhxWedBvY9MTuJ6sjRkBSAiQlQFICJPV0gNO3J01/Gn9VtRl85fednqxc9coDFo2ApARISoCkBEgqXwl5pPqEfvogwZP2mOzY1P4TRkBSAiQlQFICJJWfE3LSrVcrX7di5ZqrAxGnJiZGQFICJCVAUgIk9bIAp59cNX0tj0yvcOw4J6RabZma6BgBSQmQlABJCZDUyJ6QHSsXK3bsuZh+3WnVOSHfMQKSEiApAZISIKmnDyuc/sT/qunH8e7Yw3H6GSMn7G0xApISICkBkhIgqa2HFZ5+MOHVa1l5jR2rDztWb6ZWeYyApARISoCkBEhqJMAdk4YddjyV61+7ves7RkBSAiQlQFICJPX07VhX7VilqL73dDvOO3E7Fm9BgKQESEqApF52O9aKldu2pl93xzkh1d6M6YMTf8IISEqApARISoCkXrYSMm1lQ/z06z6yY4Kw8r2n3qJlBCQlQFICJCVAUiOP6J1WnZGxY0Vix2b16Sd6rawGfccISEqApARISoCkXrYx/arpSc2OW6Cmr+WRlcnAih0TrD8ZAUkJkJQASQmQ1K0DrPZw7FCtmOzeJ3LrALk/AZISICkBknq7AKc3tZ+0yjP9utMTHSsh3I4ASQmQlABJjQR40mrBCqsjno7FP0aApARISoCkng7wpCdmrTjp5PLqKV+79388YgQkJUBSAiQlQFJHnhNy1fT1nfTzdnxvdX1/MgKSEiApAZISICkBkhIgKQGS+g/OvjqVV+EClwAAAABJRU5ErkJggg=="; + _output.WriteLine( result ); + Assert.Equal( base64, result ); + } + + /// + /// 测试获取二维码Base64字符串 - 设置图片类型 + /// + [Fact] + public void TestToBase64_2() { + var result = _service.Content( _content ).ImageType( ImageType.Jpg ).ToBase64(); + string base64 = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/wAARCACgAKADASIAAhEBAxEB/8QBogAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoLEAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+foBAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKCxEAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9sAhAAIBgYHBgUIBwcHCQkICgwUDQwLCwwZEhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQyAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/2gAMAwEAAhEDEQA/APe554bW3luLiWOGCJC8kkjBVRQMkkngADnNYf8Awnfg/wD6GvQ//BjD/wDFUeO/+SeeJf8AsFXX/opq+YPhl8Mv+Fjf2p/xN/7P+weV/wAu3m79+/8A21xjZ79aAPp//hO/B/8A0Neh/wDgxh/+Ko/4Tvwf/wBDXof/AIMYf/iq8g/4Zl/6m7/ym/8A22j/AIZl/wCpu/8AKb/9toA9f/4Tvwf/ANDXof8A4MYf/iq1IdW0250s6pBqFpLp4RnN2kytEFXO47wcYGDk54wa+XPiP8H/APhX/h631b+3ft/nXa23lfZPKxlHbdne39zGMd69P8G/8mvXP/YK1L/0KagD0D/hO/B//Q16H/4MYf8A4qrFj4s8N6neR2dh4g0q7upM7IYL2OR2wCThQcnABP4V8MV6B8Ev+SvaF/28f+k8lAH1XqXiXQdGuFt9U1vTbGdkDrHdXSRMVyRkBiDjIIz7GtSvL/iP8H/+FgeIbfVv7d+weTaLbeV9k83OHdt2d6/38Yx2r1CgAorP13U/7E8Panq3k+d9htJbnyt23fsQttzg4zjGcGvEP+Gmv+pR/wDKl/8AaqAPd76/s9Ms5Ly/u4LS1jxvmnkEaLkgDLHgZJA/Gsf/AITvwf8A9DXof/gxh/8Aiqz9Z0z/AIWZ8L47bzv7N/ti0trndt87yclJduMru6Yzx6+1fMHxH8C/8K/8Q2+k/wBo/b/OtFufN8jysZd1243N/cznPegD7D1LVtN0a3W41TULSxgZwiyXUyxKWwTgFiBnAJx7Gsv/AITvwf8A9DXof/gxh/8Aiq8//aO/5J5p/wD2FY//AEVLXAeCfgX/AMJj4Qsde/4SP7H9q8z9x9h8zbtkZPveYM5256d6APf/APhO/B//AENeh/8Agxh/+Ko/4Tvwf/0Neh/+DGH/AOKryD/hmX/qbv8Aym//AG2j/hmX/qbv/Kb/APbaAPX/APhO/B//AENeh/8Agxh/+KroK+MPiP4F/wCFf+IbfSf7R+3+daLc+b5HlYy7rtxub+5nOe9fZ9AHP+O/+SeeJf8AsFXX/opq8g/Zl/5mn/t0/wDa1ev+O/8AknniX/sFXX/opq8g/Zl/5mn/ALdP/a1AHnfjTxp4qtfHXiG3t/EuswwRancpHHHfyqqKJWAAAbAAHGKw/wDhO/GH/Q165/4MZv8A4qjx3/yUPxL/ANhW6/8ARrVz9AH0/wDtHf8AJPNP/wCwrH/6Klo8G/8AJr1z/wBgrUv/AEKaj9o7/knmn/8AYVj/APRUtanwo02HWfgRYaXcNIsF7b3dvI0ZAYK80qkjIIzg+hoA8c+D+s+BtI/tn/hNIrGTzfI+yfa7A3OMeZvxhG29U9M8elU/EnhDxRot7qXjfRrSTT9Be4e5sb20uEhK28z4iKorB1BV1G3AIBwQOa9b/wCGcfB//QS1z/v/AA//ABqu81PwVpuq+A08Hzz3a6elvBbiRHUS7YipU5KkZ+QZ49elAHH/AAC1bUtZ8C31xqmoXd9OupyIsl1M0rBfKiOAWJOMknHua5fwVdeL/h5rM2rfEzU9Sg0Wa3a2ha6vjeKbgsrKAiM5B2pJ82MdRnnn1jwV4K03wHo02l6XPdzQS3DXDNdOrMGKquBtVRjCDt618weNfizr3jzRodL1S002GCK4W4VrWN1YsFZcHc7DGHPb0oAsfEv4gXuteM9XbQvEepPoNykaJCk8scTL5Sq48s44Lbsgjnn1q58H9Z8DaR/bP/CaRWMnm+R9k+12BucY8zfjCNt6p6Z49K1NA+E2g6r8GpfGE93qS6glld3AjSRBFuiMgUYKE4+QZ59elZfwf+HGj/ED+2f7Wub6H7D5Hl/ZHRc7/Mzncrf3B6d6APU/EPj7Q/FPhObwv8PNWk/t6ZI00+3tI5bQqsbKzBXZUVAI0bjI4GB1xXlGpfCj4q6zcLcapp13fTqgRZLrU4ZWC5JwC0hOMknHua5+PUpvhz8Tb240dY530m9ubeAXYLBlBeLLbSuTtOeMc/lX0v8ACbxrqXjzwrdapqkFpDPFevbqtqjKpUIjZO5mOcue/pQBzf7R3/JPNP8A+wrH/wCipak8DTzWv7M0lxbyyQzxaZqDxyRsVZGDzEEEcgg85qP9o7/knmn/APYVj/8ARUtHg3/k165/7BWpf+hTUAeAf8J34w/6GvXP/BjN/wDFV3Hwg8WeJNT+KWjWd/4g1W7tZPP3wz3skiNiCQjKk4OCAfwryevQPgl/yV7Qv+3j/wBJ5KAOg/aO/wCSh6f/ANgqP/0bLX0/XzB+0d/yUPT/APsFR/8Ao2Wvp+gDn/Hf/JPPEv8A2Crr/wBFNXzh8H/iPo/w/wD7Z/ta2vpvt3keX9kRGxs8zOdzL/fHr3r6j1aGxudGvoNUMY0+S3kS6MkmxREVIfLZG0bc85GK8r/4Q34Gf8/Wh/8Ag9b/AOPUAH/DR3g//oG65/34h/8AjtH/AA0d4P8A+gbrn/fiH/47W5B8GfhtdW8Vxb6JHNBKgeOSO/nZXUjIIIkwQRzms/Uvhv8AB7RrhbfVIdNsZ2QOsd1q8kTFckZAaUHGQRn2NAHnHxZ+LOg+PPCtrpel2mpQzxXqXDNdRoqlQjrgbXY5y47etd34N/5Neuf+wVqX/oU1H/CG/Az/AJ+tD/8AB63/AMer0DRdF8NP4LGjaMsE/h6aKWFVguWlR0csJAJNxJ5LDg8e2KAPIP2Zf+Zp/wC3T/2tW34x8a6b8Qzqvwz0mC7h1qe4a3We7RVtw0EnmOSyszYIibHy9SM47U/HWhax8N/sH/CrNKvrf7f5n9o/ZLd73ds2+VnzA+z78nTGeeuOOs8N+EPC+i2Wm+N9ZtI9P157dLm+vbu4eELcTJiUsjMEUlnYbcAAnAA4oA+aPGvgrUvAesw6Xqk9pNPLbrcK1q7MoUsy4O5VOcoe3pWx41+E2veA9Gh1TVLvTZoJbhbdVtZHZgxVmydyKMYQ9/Sus+NdheeMfGVnqPhe0n1yxj09IHudMjNzGsgkkYoWjyAwDKcdcMPWuj+PviXQdZ8C2Nvpet6bfTrqcbtHa3SSsF8qUZIUk4yQM+4oA4TwP8ONYj0zS/iIbmx/siwl/tCWEO/ntHbyEuFXbt3Hy2wCwHIyRXYeJ/8AjIH7L/win+hf2Jv+0/2r+73+djbs8vfnHlNnOOo69uj+E+r+Gbr4SaX4c1PV9N8+7S4tZbF7xUlcSTSDZt3BssGGMc8jFd54Y8E+HfB32r+wdP8Asf2rZ5376STdtzt++xxjc3T1oA+RNM8Falqvjx/B8E9ouoJcT25kd2EW6IMWOQpOPkOOPTpXsfhvxJZ/AjTpPC/iiOe8vrqU6gkmmKJIxGwEYBMhQ7sxNxjGCOfToPiL4W0bwZoOseOtAs/sfiWKUTJe+a8mHmlVJDsclOVkcfd4zxjArwTUpPG/xGuF1i4sdS1l4UFqLi1sCVUAlth8tAM/Pn15HtQB7n+0d/yTzT/+wrH/AOipa5j4d/Gvw34R8Cabod/ZarJdWvm73gijKHdK7jBMgPRh2r2/xJ4W0bxdp0dhrln9rtY5RMqea8eHAIByhB6Mfzrg5vhv8HrbVBpc8OmxagXVBaPq8iylmxtGwy5ycjAxzkUAU/8Aho7wf/0Ddc/78Q//AB2j/ho7wf8A9A3XP+/EP/x2tDU/hh8JNE8r+1rSxsPOz5f2vVZYt+MZxulGcZH5is//AIQ34Gf8/Wh/+D1v/j1AHjnxZ8a6b488VWuqaXBdwwRWSW7LdIqsWDu2RtZhjDjv619h14//AMIb8DP+frQ//B63/wAer2CgDP13TP7b8PanpPneT9utJbbzdu7ZvQruxkZxnOMivEP+GZf+pu/8pv8A9tr3+vD/ANofXdY0T/hHP7J1W+sPO+0+Z9kuHi348rGdpGcZP5mgD0DWdT/4Vn8L47nyf7S/se0trbbu8nzsFIt2cNt65xz6e9fMHxH8df8ACwPENvq39nfYPJtFtvK8/wA3OHdt2dq/38Yx2rQ8E+OZ38X2K+NNevr3w8fM+129/LLdQv8Au22boju3YfYRwcEA9qj+LOp+FNV8VWs/g+O0TT1skSQWtobdfN3uTlSq5O0rzj09KAPRP+GZf+pu/wDKb/8Abav6F46/4Vv4h0z4Wf2d/aPkXcVt/aXn+Tu+0OJN3lbWxt83GN3O3tni5418Yn4h6NDpPwz1i7n1qG4W5mW1eSzYW4VlYl32Ajc8fy5z0OOOMRta8NeHfA15pni1oF+I1vaTk3E9s092k5DNbsLlVb5gpi2sH+XAGRjgA7/4m/E3/hXP9l/8Sj+0Pt/m/wDLz5WzZs/2Gznf7dK4D/hZv/C4/wDigv7I/sj+1f8Al++0/aPK8r99/q9ibs+Xt+8MZzzjFHwL/wCK1/t7/hK/+J99k+z/AGb+1f8ASvJ3+Zu2eZnbnaucddo9Kn+Ivif4e6ToOsWXhcWOm+KrWUQxSWGntbzROsqrKFlVBj5Q4OG5BI5zQBB/wk//AAz9/wAUp9j/ALe+1/8AEy+1eb9l2b/3ezZh848rOc/xYxxyf8My/wDU3f8AlN/+215npvhHx58RrdtYt47vWUhc2puLq+QspADbB5jg4+fPpyfepPDepfEnxdqMlhoev65d3UcRmZP7VePCAgE5dwOrD86APV9C/Z4/sTxDpmrf8JT532G7iufK/s/bv2OG258w4zjGcGvcK+MNb8QfEDw5rE+k6t4j1y3voNvmRf2pI+3coYcq5B4IPWvQPg/8VLXSP7Z/4TTxNfSeb5H2T7W09zjHmb8YDbeqemePSgD0/wCNv/JIdd/7d/8A0ojrn/2cf+Seah/2FZP/AEVFXORReI4/GM/irxVcXdx8N57ia5Au7n7Rbvby7vs5NtuZsbniIUplTgkDHHJ/EfxxZR+IbcfDvV59O0j7Ipmi0wSWUbT733MUAXLbdg3Y6ADPFAH1fXyh8T9T/sT9oC61byfO+w3dlc+Vu279kUTbc4OM4xnBr0vxr4xPxD0aHSfhnrF3PrUNwtzMtq8lmwtwrKxLvsBG54/lznocccaGm+AJ7z4TXMXiDQ7S88Yy2V0hubsRTXDSnzBDmck8hfLAO7gADjFAHKf8nGf9S9/YX/b35/n/APfvbt8n3zu7Y58v0bwL/a/xQk8F/wBo+Vsu7m2+2eRuz5Ic7tm4ddnTdxnvRqejeOfhn5X2mW+0T+0M7fsl+B53l4znynPTf3/vHHeu8+E3gLxhD4+0XxbqVhI2nzpJdPeyXUbs4lhfa5G8uSxcdRnnmgDU/wCGZf8Aqbv/ACm//ba9/rxv4s6B8SNV8VWs/g+bUk09bJEkFrqQt183e5OVLrk7SvOPT0r2SgArj/HXw40f4gfYP7Wub6H7D5nl/ZHRc79uc7lb+4PTvXYVx/jr4j6P8P8A7B/a1tfTfbvM8v7IiNjZtzncy/3x696AOP8A+GcfB/8A0Etc/wC/8P8A8ao/4Zx8H/8AQS1z/v8Aw/8AxquY+Inxr8N+LvAmpaHYWWqx3V15Wx54owg2yo5yRIT0U9q8HoA6TwV411LwHrM2qaXBaTTy27W7LdIzKFLK2RtZTnKDv61T8U+JLzxd4ju9cv44I7q62b0gUhBtRUGAST0Ud69g8N+G7z4EajJ4o8USQXljdRHT0j0xjJIJGIkBIkCDbiJuc5yRx6cfc+JLPxd8ftL1ywjnjtbrVbDYk6gONpiQ5AJHVT3oA7j9mX/maf8At0/9rVw9t4bs/F3x+1TQ7+SeO1utVv8Ae8DAONplcYJBHVR2r6H8dfEfR/h/9g/ta2vpvt3meX9kRGxs25zuZf749e9Y/hb41+G/F3iO00OwstVjurrfseeKMINqM5yRIT0U9qAOA8SeJLz4EajH4X8LxwXljdRDUHk1NTJIJGJjIBjKDbiJeMZyTz6Y/wCzj/yUPUP+wVJ/6Nir6fr5g/4Zx8Yf9BLQ/wDv/N/8aoA9X8U/BTw34u8R3euX97qsd1dbN6QSxhBtRUGAYyeijvXjHxg+HGj/AA//ALG/sm5vpvt3n+Z9rdGxs8vGNqr/AHz69q9ntvDd54R+AOqaHfyQSXVrpV/veBiUO4SuMEgHow7VxH7Mv/M0/wDbp/7WoA871P4s69qvgNPB89ppq6elvBbiRI3Eu2IqVOS5GfkGePXpXUfCb4TaD488K3Wqapd6lDPFevbqtrIiqVCI2TuRjnLnv6V6Pq3x98K6NrN9pdxp+stPZXElvI0cMRUsjFSRmQHGR6Cuw8FeNdN8eaNNqmlwXcMEVw1uy3SKrFgqtkbWYYw47+tAHgn7OP8AyUPUP+wVJ/6Nirp/iJ8a/EnhHx3qWh2FlpUlra+VseeKQud0SOckSAdWPaqHhvw3efAjUZPFHiiSC8sbqI6ekemMZJBIxEgJEgQbcRNznOSOPTp/+GjvB/8A0Ddc/wC/EP8A8doA8Q8dfEfWPiB9g/ta2sYfsPmeX9kR1zv25zuZv7g9O9dJpPx98VaNo1jpdvp+jNBZW8dvG0kMpYqihQTiQDOB6CvS/wDho7wf/wBA3XP+/EP/AMdrxzQPGum6V8ZZfGE8F22nve3dwI0RTLtlEgUYLAZ+cZ59etAH0P8ACbxrqXjzwrdapqkFpDPFevbqtqjKpUIjZO5mOcue/pXeVzfgrxrpvjzRptU0uC7hgiuGt2W6RVYsFVsjazDGHHf1rpKACvD/ANofQtY1v/hHP7J0q+v/ACftPmfZLd5dmfKxnaDjOD+Rr2DXdT/sTw9qereT532G0lufK3bd+xC23ODjOMZwa4/4ZfE3/hY39qf8Sj+z/sHlf8vPm79+/wD2FxjZ79aAPnT4a+HLTWfifp2g69YyNAzzpcW0heJgyROcHBDAhlHHHStT41+FtG8I+MrOw0Oz+yWsmnpMyea8mXMkgJy5J6KPyr2fRvg//ZHxQk8af275u+7ubn7H9k2484ONu/eem/rt5x2o+I/wf/4WB4ht9W/t37B5Nott5X2Tzc4d23Z3r/fxjHagDY8Sal8NvF2nR2Gua/od3axyiZU/tVI8OAQDlHB6Mfzrn7Dw38FdM1G2v7O+0OO6tZUmhf8Atwna6kFTgy4OCB1rmP8AhmX/AKm7/wApv/22vL9Z8C/2R8UI/Bf9o+bvu7a2+2eRtx5wQ7tm49N/TdzjtQB6B+0Pruj63/wjn9k6rY3/AJP2nzPslwkuzPlYztJxnB/I11fw/wBP+Fei6boGurqOjWuvJZRvLJJq+GWV4sSZRpMA/Mwxjj2rE/4Zl/6m7/ym/wD22vL9G8C/2v8AFCTwX/aPlbLu5tvtnkbs+SHO7ZuHXZ03cZ70Aev/ABH8a+NpPENufh3dT6jpH2RRNLplml7Gs+99ylwjYbbsO3PQg45rU+LPxOi0rwraz+D/ABPpr6g16iSC1mhuG8rY5OVO7A3BecenrXOf8JP/AMM/f8Up9j/t77X/AMTL7V5v2XZv/d7NmHzjys5z/FjHHJ/wzL/1N3/lN/8AttAHofgPVW8WfB+C/wDFNzHOl5b3SX00m2FTEJJEbJXaFAQdRjpmo/DEnwu8Hfav7B1rQ7P7Vs87/ibrJu252/fkOMbm6etaGjeBf7I+F8ngv+0fN32lzbfbPI2484ud2zcem/pu5x2rzD/hmX/qbv8Aym//AG2gDT+LPgLwfD4B1rxbpthG2oTvHdJex3Ujq5lmTc4G8oQwc9Bjnirn7OP/ACTzUP8AsKyf+ioq0Pifpn9ifs/3Wk+d532G0srbzdu3fsliXdjJxnGcZNZ/7OP/ACTzUP8AsKyf+ioqAOQ+HGt6j8W/ENxoPji4/tXTLe0a9ig2LBtmV0QNuiCsflkcYJxz04Fd3N8N/g9baoNLnh02LUC6oLR9XkWUs2No2GXOTkYGOcivNP2cf+Sh6h/2CpP/AEbFWf8AE/U/7E/aAutW8nzvsN3ZXPlbtu/ZFE23ODjOMZwaAPX9T+GHwk0Tyv7WtLGw87Pl/a9Vli34xnG6UZxkfmK4j4ieGvhTp/gTUrrw1caU+rp5X2cQas0znMqBsIZDn5S3bjrVj/k4z/qXv7C/7e/P8/8A797dvk++d3bHPl+jeBf7X+KEngv+0fK2XdzbfbPI3Z8kOd2zcOuzpu4z3oA9v/Zx/wCSeah/2FZP/RUVewVx/wAOPAv/AAr/AMPXGk/2j9v867a583yPKxlEXbjc39zOc967CgCOeCG6t5be4ijmglQpJHIoZXUjBBB4II4xVPTNC0fRPN/snSrGw87HmfZLdIt+M4ztAzjJ/M1H4l1KbRvCur6pbrG09lZTXEayAlSyIWAOCDjI9RXzp/w0d4w/6Buh/wDfib/47QB6f448cWXiDTNU8IeENXnPi4y+TDBAJLdw8UgaUCUhVGER/wCLnGBnPOX4K8Yn4eaNNpPxM1i7g1qa4a5hW6eS8Y25VVUh03gDckny5z1OOefCNM8a6lpXjx/GEEFo2oPcT3BjdGMW6UMGGAwOPnOOfTrR418a6l481mHVNUgtIZ4rdbdVtUZVKhmbJ3Mxzlz39KAPd/GvjE/EPRodJ+GesXc+tQ3C3My2ryWbC3CsrEu+wEbnj+XOehxxxctfCsum/B6+1LxJpkDeLrXT7ud9RnCTXaSJ5hicTjLblUJtIbK4GMYrn/Enhuz+BGnR+KPC8k95fXUo0949TYSRiNgZCQIwh3ZiXnOME8emBYfGvxJ4x1G28L6jZaVFY6zKmn3ElvFIsixzERsUJkIDAMcEgjPY0Aeb/wDCd+MP+hr1z/wYzf8AxVe7+JbCz074DQeKLG0gtfELafZTtqsEYS7MkjRCRzMPn3MGbcc5O456mvOPjB8ONH+H/wDY39k3N9N9u8/zPtbo2Nnl4xtVf759e1e76b4bs/F3wZ0TQ7+SeO1utKst7wMA42rG4wSCOqjtQB82ab4R8efEa3bWLeO71lIXNqbi6vkLKQA2weY4OPnz6cn3qTw3qXxJ8XajJYaHr+uXd1HEZmT+1XjwgIBOXcDqw/OvQPEniS8+BGox+F/C8cF5Y3UQ1B5NTUySCRiYyAYyg24iXjGck8+mP+zj/wAlD1D/ALBUn/o2KgD2PQNM8VwfBqXTtRkuz4oNldorvdh5fNYyeV+93EZwUwd3HHTFcX4F13WPhv8Ab/8Ahaeq31v9v8v+zvtdw97u2bvNx5ZfZ9+PrjPHXHHuFeAftNf8yt/29/8AtGgDyvxl4y1XXNe1uKLX9SudFuL2V4IHuZPKaLzC0f7tjgADaQCOMD0r0z4KfETwr4R8G3lhrmqfZLqTUHmVPs8smUMcYByikdVP5V4PRQB0ngrTPFeq6zNB4Pku01BbdnkNrdi3bytyg5YsuRuK8Z9PSukvvhD8T9TvJLy/0me7upMb5p9RhkdsAAZYyZOAAPwrm/BXjXUvAeszappcFpNPLbtbst0jMoUsrZG1lOcoO/rXef8ADR3jD/oG6H/34m/+O0AZ+mfDD4t6J5v9k2l9YedjzPsmqxRb8ZxnbKM4yfzNZeq+A/iJ4TS48U39td2Twvvl1BNQjMoaRtpO5HLksXwT7nPevc/g/wDEfWPiB/bP9rW1jD9h8jy/siOud/mZzuZv7g9O9dx4p8N2fi7w5d6HfyTx2t1s3vAwDja6uMEgjqo7UAfMHhu2+LXi7TpL/Q9W1y7tY5TCz/2yY8OACRh5AejD86+t65vwV4K03wHo02l6XPdzQS3DXDNdOrMGKquBtVRjCDt610lAFPVtSh0bRr7VLhZGgsreS4kWMAsVRSxAyQM4HqK8r/4aO8H/APQN1z/vxD/8dr0Dx3/yTzxL/wBgq6/9FNXgHwL8E+HfGP8Ab39vaf8AbPsv2fyf30ke3d5m77jDOdq9fSgDv/8Aho7wf/0Ddc/78Q//AB2j/ho7wf8A9A3XP+/EP/x2pJ/A3wStbiW3uJdGhnicpJHJrbqyMDgggy5BB4xUf/CG/Az/AJ+tD/8AB63/AMeoAP8Aho7wf/0Ddc/78Q//AB2u0j1KH4jfDK9uNHWSBNWsrm3gF2ApViHiy20tgbhnjPH5V5J8a/h34V8I+DbO/wBD0v7JdSagkLP9olkyhjkJGHYjqo/Kuv8Ah9fXGmfs3i/s5PLurXT7+aF9oO11kmKnB4OCB1oA84/4Zx8Yf9BLQ/8Av/N/8ar6L8NabNo3hXSNLuGjaeysobeRoySpZECkjIBxkegryP4P/FS61f8Atn/hNPE1jH5XkfZPtbQW2c+ZvxgLu6J6449a9km1bTbbSxqk+oWkWnlFcXbzKsRVsbTvJxg5GDnnIoAuV8wf8M4+MP8AoJaH/wB/5v8A41XX/Efxr42k8Q25+Hd1PqOkfZFE0umWaXsaz733KXCNhtuw7c9CDjmtT4s/E6LSvCtrP4P8T6a+oNeokgtZobhvK2OTlTuwNwXnHp60AYdn4ks/Cvhdvg/fRzyeIZopNPW5gUG0El0WaMliQ+0CZdx2ZGDgHjPUfB/4cax8P/7Z/ta5sZvt3keX9kd2xs8zOdyr/fHr3qn4B8PaB4p8MaZ8Q/FEEc2vb3up9RknaFVMEjKjlVZYwFWNe2Plyc81H8TfG3iKT+y/+Fa6h/aePN+3/wBlQx33l/c8vfhX2Z/eY6ZweuKAPKLbxJZ+Efj9qmuX8c8lra6rf70gUFzuMqDAJA6sO9U/iz4103x54qtdU0uC7hgiskt2W6RVYsHdsjazDGHHf1r0/wAXfDLT7z4VyeIIvD93N4xu7e2ubgp5xla4kdDMfJBwD8z5UKAOeBivANS0nUtGuFt9U0+7sZ2QOsd1C0TFckZAYA4yCM+xoA+z/GvjXTfAejQ6pqkF3NBLcLbqtqiswYqzZO5lGMIe/pWXqXiSz8XfBnW9csI547W60q92JOoDjasiHIBI6qe9HiTUvht4u06Ow1zX9Du7WOUTKn9qpHhwCAco4PRj+dZesax4F0b4W61oOg67oywLpl2lvbR6kkrFnRzgZcsSWY8c9aAPkyvQPgl/yV7Qv+3j/wBJ5K0Pg/o3gbV/7Z/4TSWxj8ryPsn2u/NtnPmb8Ydd3RPXHHrWe2geLPD3jm81fwboeqi1gu5zpt1b2L3EbQMWVGRmVg6lG4bnIOc96APoPxr8WdB8B6zDpeqWmpTTy263CtaxoyhSzLg7nU5yh7eld5Xg/huz0LxVp0l98YHgg8QxymG3XU5zp8htQAVIjBjyu9pfmxycjPGB7xQBz/jv/knniX/sFXX/AKKavIP2Zf8Amaf+3T/2tXr/AI7/AOSeeJf+wVdf+imryD9mX/maf+3T/wBrUAeP+O/+Sh+Jf+wrdf8Ao1q5+vWPFnwg8d6n4y1y/s9C8y1utQuJoX+1wDcjSMVOC+RkEdax/wDhSXxD/wChe/8AJ23/APjlAHr/AO0d/wAk80//ALCsf/oqWtD4YaZ/bf7P9rpPneT9utL2283bu2b5ZV3YyM4znGRWf+0d/wAk80//ALCsf/oqWtj4Q31vpnwQ0y/vJPLtbWK6mmfaTtRZpSxwOTgA9KAPCPib8Mv+Fc/2X/xN/wC0Pt/m/wDLt5WzZs/22znf7dK7DQvHX/CyPD2mfCz+zv7O8+0itv7S8/ztv2dBJu8raud3lYxu43d8c9/qfxP+Emt+V/a13Y3/AJOfL+16VLLszjON0RxnA/IVyHjbxp8NU8IXzeC3sbLxCPL+yXFhpr2syfvF37ZRGu3KbweRkEjvQAf8JP8A8M/f8Up9j/t77X/xMvtXm/Zdm/8Ad7NmHzjys5z/ABYxxyf8My/9Td/5Tf8A7bXmem+EfHnxGt21i3ju9ZSFzam4ur5CykANsHmODj58+nJ96k8N6l8SfF2oyWGh6/rl3dRxGZk/tV48ICATl3A6sPzoA+h5PDH/AAh3wN1fQftn2z7LpV9+/wDK8vduWR/u5OMbsde1cB+zL/zNP/bp/wC1qz9C8GfF9vEOmDXH1WfSDdxC+iuNYSWOSDePMV08071K5BXByOMGvf8ATNC0fRPN/snSrGw87HmfZLdIt+M4ztAzjJ/M0AZ/jbxP/wAId4Qvte+x/bPsvl/uPN8vdukVPvYOMbs9O1eQf8Ix/wANA/8AFV/bP7B+yf8AEt+y+V9q37P3m/flMZ83GMfw5zzx3/xt/wCSQ67/ANu//pRHXnHwU+InhXwj4NvLDXNU+yXUmoPMqfZ5ZMoY4wDlFI6qfyoA8HrQ0LTP7b8Q6ZpPneT9uu4rbzdu7ZvcLuxkZxnOMivVPBXg4fDzWZtW+Jmj2kGizW7W0LXSR3im4LKygIm8g7Uk+bGOozzz1eu+M/hAvh7UzoaaVBq4tJTYy2+jvFJHPsPlsj+UNjBsENkYPORQB5h8Tfhl/wAK5/sv/ib/ANofb/N/5dvK2bNn+22c7/bpXYaF+0P/AGJ4e0zSf+EW877DaRW3m/2ht37EC7seWcZxnGTWf8MvG3h2T+1P+Flah/aePK+wf2rDJfeX9/zNmVfZn93npnA64rg7rSm8WfEHULDwtbRzpeXtw9jDHthUxAs64DbQoCDocdMUAeuf8Ix/w0D/AMVX9s/sH7J/xLfsvlfat+z95v35TGfNxjH8Oc88e/18qab8N/jDo1u1vpcOpWMDOXaO11eOJS2AMkLKBnAAz7CvqugDn/Hf/JPPEv8A2Crr/wBFNXyh4F+I+sfD/wC3/wBk21jN9u8vzPtaO2Nm7GNrL/fPr2r7PooA+YP+GjvGH/QN0P8A78Tf/HaP+GjvGH/QN0P/AL8Tf/Ha+n6KAPjzxr8Wde8eaNDpeqWmmwwRXC3CtaxurFgrLg7nYYw57elex+Df+TXrn/sFal/6FNXsFFAHwBXUfDvw3Z+LvHem6HfyTx2t15u94GAcbYncYJBHVR2r7XooA+ePEniS8+BGox+F/C8cF5Y3UQ1B5NTUySCRiYyAYyg24iXjGck8+mP+zj/yUPUP+wVJ/wCjYq+n6KAMvxLqU2jeFdX1S3WNp7KymuI1kBKlkQsAcEHGR6ivnT/ho7xh/wBA3Q/+/E3/AMdr6fooA8r+JGpTaz+zvLqlwsaz3tlY3EixghQzyQsQMknGT6mvlSvv+igDx/8AaO/5J5p//YVj/wDRUtcJoHwm0HVfg1L4wnu9SXUEsru4EaSIIt0RkCjBQnHyDPPr0r6booA+UPg/8ONH+IH9s/2tc30P2HyPL+yOi53+ZnO5W/uD0713+t/DjR/hJo8/jjQbm+udT0zb5MV+6PC3mMIm3BFVj8sjEYYcgdele4UUAcH8JvGupePPCt1qmqQWkM8V69uq2qMqlQiNk7mY5y57+ld5RRQB/9k="; + _output.WriteLine( result ); + Assert.Equal( base64, result ); + } + + /// + /// 测试保存二维码图片 + /// + [Fact] + public void TestSave() { + _service.Content( _content ).Save( "./test/Save.png" ); + } + + /// + /// 测试获取二维码流 + /// + [Fact] + public async Task TestToStreamAsync() { + var result = await _service.Content( _content ).ToStreamAsync(); + await Util.Helpers.File.WriteAsync( "./test/ToStreamAsync.png", result ); + } + + /// + /// 测试获取二维码字节流 + /// + [Fact] + public async Task TestToBytesAsync() { + var result = await _service.Content( _content ).ToBytesAsync(); + await Util.Helpers.File.WriteAsync( "./test/ToBytesAsync.png", result ); + } + + /// + /// 测试保存二维码图片 + /// + [Fact] + public async Task TestSaveAsync_1() { + await _service.Content( _content ).SaveAsync( "./test/SaveAsync_1.png" ); + } + + /// + /// 测试保存二维码图片 - 设置二维码尺寸 + /// + [Fact] + public async Task TestSaveAsync_2() { + await _service.Content( _content ).Size( QrSize.Small ).SaveAsync( "./test/SaveAsync_Small.png" ); + await _service.Content( _content ).Size( QrSize.Middle ).SaveAsync( "./test/SaveAsync_Middle.png" ); + await _service.Content( _content ).Size( QrSize.Large ).SaveAsync( "./test/SaveAsync_Large.png" ); + } + + /// + /// 测试保存二维码图片 - 设置前景色 + /// + [Fact] + public async Task TestSaveAsync_3() { + await _service.Content( _content ).Color( Color.Red ).SaveAsync( "./test/SaveAsync_3.png" ); + } + + /// + /// 测试保存二维码图片 - 设置背景色 + /// + [Fact] + public async Task TestSaveAsync_4() { + await _service.Content( _content ).BgColor( Color.Aquamarine ).SaveAsync( "./test/SaveAsync_4.png" ); + } + + /// + /// 测试保存二维码图片 - 设置边距 + /// + [Fact] + public async Task TestSaveAsync_5() { + await _service.Content( _content ).Margin( 5 ).SaveAsync( "./test/SaveAsync_5.png" ); + } + + /// + /// 测试保存二维码图片 - 设置图标 + /// + [Fact] + public async Task TestSaveAsync_6() { + var iconPath = Util.Helpers.Common.GetPhysicalPath( "./Icons/icon.jpg" ); + await _service.Content( _content ).Icon( iconPath ).SaveAsync( "./test/SaveAsync_6.png" ); + } +} \ No newline at end of file diff --git a/test/Util.QrCode.ZXing.Tests.Integration/Util.QrCode.ZXing.Tests.Integration.csproj b/test/Util.QrCode.ZXing.Tests.Integration/Util.QrCode.ZXing.Tests.Integration.csproj new file mode 100644 index 000000000..0bec2c769 --- /dev/null +++ b/test/Util.QrCode.ZXing.Tests.Integration/Util.QrCode.ZXing.Tests.Integration.csproj @@ -0,0 +1,35 @@ + + + + $(NetTargetFramework) + false + Util.QrCode + Util.QrCode.Startup + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + Always + + + + \ No newline at end of file diff --git a/test/Util.Scheduling.Hangfire.Tests.Integration/Jobs/TestJob1.cs b/test/Util.Scheduling.Hangfire.Tests.Integration/Jobs/TestJob1.cs new file mode 100644 index 000000000..ab8cc7285 --- /dev/null +++ b/test/Util.Scheduling.Hangfire.Tests.Integration/Jobs/TestJob1.cs @@ -0,0 +1,25 @@ +using Util.Tests.Fakes; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; + +namespace Util.Scheduling.Hangfire.Tests.Jobs; + +/// +/// 后台任务1 +/// +public class TestJob1 : JobBase { + /// + protected override void ConfigTrigger( IJobTrigger trigger ) { + trigger.Delay( TimeSpan.FromSeconds( 1 ) ); + } + + /// + protected override async Task Execute( HangfireExecutionContext context ) { + var unitOfWork = context.GetService(); + var repository = context.GetService(); + var customer = CustomerFakeService.GetCustomer(); + customer.Name = "TestJob1"; + await repository.AddAsync( customer ); + await unitOfWork.CommitAsync(); + } +} \ No newline at end of file diff --git a/test/Util.Scheduling.Hangfire.Tests.Integration/Jobs/TestJob2.cs b/test/Util.Scheduling.Hangfire.Tests.Integration/Jobs/TestJob2.cs new file mode 100644 index 000000000..e1ceba846 --- /dev/null +++ b/test/Util.Scheduling.Hangfire.Tests.Integration/Jobs/TestJob2.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading.Tasks; +using Util.Tests.Fakes; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; + +namespace Util.Scheduling.Hangfire.Tests.Jobs; + +/// +/// 后台任务2 +/// +public class TestJob2 : JobBase { + /// + protected override void ConfigTrigger( IJobTrigger trigger ) { + trigger.Delay( TimeSpan.FromSeconds( 1 ) ); + } + + /// + protected override async Task Execute( HangfireExecutionContext context ) { + var unitOfWork = context.GetService(); + var repository = context.GetService(); + var customer = CustomerFakeService.GetCustomer(); + customer.Name = "TestJob2"; + await repository.AddAsync( customer ); + await unitOfWork.CommitAsync(); + } +} \ No newline at end of file diff --git a/test/Util.Scheduling.Hangfire.Tests.Integration/Properties/launchSettings.json b/test/Util.Scheduling.Hangfire.Tests.Integration/Properties/launchSettings.json new file mode 100644 index 000000000..5ae6cf116 --- /dev/null +++ b/test/Util.Scheduling.Hangfire.Tests.Integration/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Util.Scheduling.Hangfire.Tests.Integration": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:63169;http://localhost:63170" + } + } +} \ No newline at end of file diff --git a/test/Util.Scheduling.Hangfire.Tests.Integration/SchedulerTest.cs b/test/Util.Scheduling.Hangfire.Tests.Integration/SchedulerTest.cs new file mode 100644 index 000000000..1318cc9c3 --- /dev/null +++ b/test/Util.Scheduling.Hangfire.Tests.Integration/SchedulerTest.cs @@ -0,0 +1,45 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Util.Tests.Repositories; +using Xunit; + +namespace Util.Scheduling.Hangfire.Tests; + +/// +/// 调度器测试 +/// +public class SchedulerTest { + /// + /// 仓储 + /// + private readonly ICustomerRepository _repository; + + /// + /// 测试初始化 + /// + public SchedulerTest( ICustomerRepository repository ) { + _repository = repository; + } + + /// + /// 测试后台任务1 + /// + [Fact] + public async Task TestJob1() { + await Task.Delay( TimeSpan.FromSeconds( 2 ) ); + var result = await _repository.Find().Where( t => t.Name == "TestJob1" ).CountAsync(); + Assert.Equal( 1, result ); + } + + /// + /// 测试后台任务2 + /// + [Fact] + public async Task TestJob2() { + await Task.Delay( TimeSpan.FromSeconds( 2 ) ); + var result = await _repository.Find().Where( t => t.Name == "TestJob2" ).CountAsync(); + Assert.Equal( 1, result ); + } +} \ No newline at end of file diff --git a/test/Util.Scheduling.Hangfire.Tests.Integration/Startup.cs b/test/Util.Scheduling.Hangfire.Tests.Integration/Startup.cs new file mode 100644 index 000000000..2e6bbf5b1 --- /dev/null +++ b/test/Util.Scheduling.Hangfire.Tests.Integration/Startup.cs @@ -0,0 +1,60 @@ +using System; +using Hangfire; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Data.EntityFrameworkCore; +using Util.Helpers; +using Util.Tests.UnitOfWorks; +using Xunit.DependencyInjection.Logging; + +namespace Util.Scheduling.Hangfire.Tests; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Util.Helpers.Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .ConfigureWebHostDefaults( webHostBuilder => { + webHostBuilder + .Configure( t => { + t.UseRouting(); + t.UseEndpoints( endpoints => { + endpoints.MapControllers(); + endpoints.MapHangfireDashboard(); + } ); + } ); + } ) + .AsBuild() + .AddHangfire( + config => config.UseSqlServerStorage( Config.GetConnectionString( "HangfireConnection" ) ), + config => config.SchedulePollingInterval = TimeSpan.FromSeconds( 1 ) + ) + .AddSqlServerUnitOfWork( Config.GetConnectionString( "connection" ) ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddMvc(); + InitDatabase( services ); + } + + /// + /// ʼݿ + /// + private void InitDatabase( IServiceCollection services ) { + var unitOfWork = services.BuildServiceProvider().GetService(); + unitOfWork.EnsureDeleted(); + unitOfWork.EnsureCreated(); + } +} \ No newline at end of file diff --git a/test/Util.Scheduling.Hangfire.Tests.Integration/Usings.cs b/test/Util.Scheduling.Hangfire.Tests.Integration/Usings.cs new file mode 100644 index 000000000..d4eb04364 --- /dev/null +++ b/test/Util.Scheduling.Hangfire.Tests.Integration/Usings.cs @@ -0,0 +1,29 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using System.Diagnostics; +global using System.Text; +global using System.IO; +global using System.Reflection; +global using System.ComponentModel; +global using System.Globalization; +global using System.Text.Json; +global using System.Linq.Expressions; +global using System.Text.RegularExpressions; +global using System.ComponentModel.DataAnnotations; +global using System.Runtime.InteropServices; +global using System.Text.Encodings.Web; +global using System.Text.Json.Serialization; +global using System.Text.Unicode; +global using System.Buffers; +global using System.Diagnostics.CodeAnalysis; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Logging.Abstractions; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Hangfire; +global using Xunit; diff --git a/test/Util.Scheduling.Hangfire.Tests.Integration/Util.Scheduling.Hangfire.Tests.Integration.csproj b/test/Util.Scheduling.Hangfire.Tests.Integration/Util.Scheduling.Hangfire.Tests.Integration.csproj new file mode 100644 index 000000000..3e482b5b6 --- /dev/null +++ b/test/Util.Scheduling.Hangfire.Tests.Integration/Util.Scheduling.Hangfire.Tests.Integration.csproj @@ -0,0 +1,45 @@ + + + + $(NetTargetFramework) + false + Util.Scheduling.Hangfire.Tests + Util.Scheduling.Hangfire.Tests.Startup + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + Always + + + Always + + + + diff --git a/test/Util.Scheduling.Hangfire.Tests.Integration/appsettings.Development.json b/test/Util.Scheduling.Hangfire.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..1ac80eae9 --- /dev/null +++ b/test/Util.Scheduling.Hangfire.Tests.Integration/appsettings.Development.json @@ -0,0 +1,12 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Information" + } + }, + "ConnectionStrings": { + "connection": "Server=.;Database=Util.Scheduling.Hangfire.Tests;uid=sa;pwd=Pass@word;MultipleActiveResultSets=true;TrustServerCertificate=true", + "HangfireConnection": "Server=.;Database=Util.Scheduling.Hangfire.Tests;uid=sa;pwd=Pass@word;MultipleActiveResultSets=true;TrustServerCertificate=true" + } +} \ No newline at end of file diff --git a/test/Util.Scheduling.Hangfire.Tests.Integration/appsettings.json b/test/Util.Scheduling.Hangfire.Tests.Integration/appsettings.json new file mode 100644 index 000000000..9bf259ae9 --- /dev/null +++ b/test/Util.Scheduling.Hangfire.Tests.Integration/appsettings.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + }, + + "ConnectionStrings": { + "connection": "Server=192.168.31.123\\sql2019;Database=Util.Scheduling.Hangfire.Tests;uid=sa;pwd=sa;MultipleActiveResultSets=true;TrustServerCertificate=true", + "HangfireConnection": "Server=192.168.31.123\\sql2019;Database=Util.Scheduling.Hangfire.Tests;uid=sa;pwd=sa;MultipleActiveResultSets=true;TrustServerCertificate=true" + } +} \ No newline at end of file diff --git a/test/Util.Scheduling.Quartz.Tests.Integration/Jobs/TestJob1.cs b/test/Util.Scheduling.Quartz.Tests.Integration/Jobs/TestJob1.cs new file mode 100644 index 000000000..3108d8b45 --- /dev/null +++ b/test/Util.Scheduling.Quartz.Tests.Integration/Jobs/TestJob1.cs @@ -0,0 +1,31 @@ +using System.Threading.Tasks; +using Util.Tests.Fakes; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; + +namespace Util.Scheduling.Quartz.Tests.Jobs { + /// + /// 后台任务1 + /// + public class TestJob1 : JobBase { + /// + protected override void ConfigDetail( IJobInfo job ) { + job.Name( "test1" ); + } + + /// + protected override void ConfigTrigger( IJobTrigger trigger ) { + trigger.Name( "test1" ).RepeatCount( 1 ).IntervalInSeconds( 1 ); + } + + /// + protected override async Task Execute( QuartzExecutionContext context ) { + var unitOfWork = context.GetService(); + var repository = context.GetService(); + var customer = CustomerFakeService.GetCustomer(); + customer.Name = "TestJob1"; + await repository.AddAsync( customer ); + await unitOfWork.CommitAsync(); + } + } +} diff --git a/test/Util.Scheduling.Quartz.Tests.Integration/Jobs/TestJob2.cs b/test/Util.Scheduling.Quartz.Tests.Integration/Jobs/TestJob2.cs new file mode 100644 index 000000000..ed857c5c0 --- /dev/null +++ b/test/Util.Scheduling.Quartz.Tests.Integration/Jobs/TestJob2.cs @@ -0,0 +1,31 @@ +using System.Threading.Tasks; +using Util.Tests.Fakes; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; + +namespace Util.Scheduling.Quartz.Tests.Jobs { + /// + /// 后台任务1 + /// + public class TestJob2 : JobBase { + /// + protected override void ConfigDetail( IJobInfo job ) { + job.Name( "test2" ); + } + + /// + protected override void ConfigTrigger( IJobTrigger trigger ) { + trigger.Name( "test2" ).RepeatCount( 2 ).IntervalInSeconds( 1 ); + } + + /// + protected override async Task Execute( QuartzExecutionContext context ) { + var unitOfWork = context.GetService(); + var repository = context.GetService(); + var customer = CustomerFakeService.GetCustomer(); + customer.Name = "TestJob2"; + await repository.AddAsync( customer ); + await unitOfWork.CommitAsync(); + } + } +} diff --git a/test/Util.Scheduling.Quartz.Tests.Integration/SchedulerTest.cs b/test/Util.Scheduling.Quartz.Tests.Integration/SchedulerTest.cs new file mode 100644 index 000000000..063f5cb82 --- /dev/null +++ b/test/Util.Scheduling.Quartz.Tests.Integration/SchedulerTest.cs @@ -0,0 +1,45 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Util.Tests.Repositories; +using Xunit; + +namespace Util.Scheduling.Quartz.Tests { + /// + /// 调度器测试 + /// + public class SchedulerTest { + /// + /// 仓储 + /// + private readonly ICustomerRepository _repository; + + /// + /// 测试初始化 + /// + public SchedulerTest( ICustomerRepository repository ) { + _repository = repository; + } + + /// + /// 测试后台任务1 + /// + [Fact] + public async Task TestJob1() { + await Task.Delay( TimeSpan.FromSeconds( 3 ) ); + var result = await _repository.Find().Where( t => t.Name == "TestJob1" ).CountAsync(); + Assert.Equal( 2, result ); + } + + /// + /// 测试后台任务2 + /// + [Fact] + public async Task TestJob2() { + await Task.Delay( TimeSpan.FromSeconds( 4 ) ); + var result = await _repository.Find().Where( t => t.Name == "TestJob2" ).CountAsync(); + Assert.Equal( 3, result ); + } + } +} diff --git a/test/Util.Scheduling.Quartz.Tests.Integration/Startup.cs b/test/Util.Scheduling.Quartz.Tests.Integration/Startup.cs new file mode 100644 index 000000000..09dfdceab --- /dev/null +++ b/test/Util.Scheduling.Quartz.Tests.Integration/Startup.cs @@ -0,0 +1,42 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Data.EntityFrameworkCore; +using Util.Helpers; +using Util.Tests.UnitOfWorks; +using Xunit.DependencyInjection.Logging; + +namespace Util.Scheduling.Quartz.Tests { + /// + /// + /// + public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .AsBuild() + .AddQuartz() + .AddSqlServerUnitOfWork( Config.GetConnectionString( "connection" ) ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + InitDatabase( services ); + } + + /// + /// ʼݿ + /// + private void InitDatabase( IServiceCollection services ) { + var unitOfWork = services.BuildServiceProvider().GetService(); + unitOfWork.EnsureDeleted(); + unitOfWork.EnsureCreated(); + } + } +} diff --git a/test/Util.Scheduling.Quartz.Tests.Integration/Util.Scheduling.Quartz.Tests.Integration.csproj b/test/Util.Scheduling.Quartz.Tests.Integration/Util.Scheduling.Quartz.Tests.Integration.csproj new file mode 100644 index 000000000..2b5a43f87 --- /dev/null +++ b/test/Util.Scheduling.Quartz.Tests.Integration/Util.Scheduling.Quartz.Tests.Integration.csproj @@ -0,0 +1,49 @@ + + + + $(NetTargetFramework) + false + Util.Scheduling.Quartz.Tests + Util.Scheduling.Quartz.Tests.Startup + + + + + + + + + + Always + + + Always + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + diff --git a/test/Util.Scheduling.Quartz.Tests.Integration/appsettings.Development.json b/test/Util.Scheduling.Quartz.Tests.Integration/appsettings.Development.json new file mode 100644 index 000000000..92d69ac9e --- /dev/null +++ b/test/Util.Scheduling.Quartz.Tests.Integration/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Information" + } + }, + "ConnectionStrings": { + "connection": "Server=.;Database=Util.Scheduling.Quartz.Tests;uid=sa;pwd=Pass@word;MultipleActiveResultSets=true;TrustServerCertificate=true" + } +} \ No newline at end of file diff --git a/test/Util.Scheduling.Quartz.Tests.Integration/appsettings.json b/test/Util.Scheduling.Quartz.Tests.Integration/appsettings.json new file mode 100644 index 000000000..a0eb61a68 --- /dev/null +++ b/test/Util.Scheduling.Quartz.Tests.Integration/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Error", + "Microsoft": "Error" + } + }, + "ConnectionStrings": { + "connection": "Server=192.168.31.123\\sql2019;Database=Util.Scheduling.Quartz.Tests;uid=sa;pwd=sa;MultipleActiveResultSets=true;TrustServerCertificate=true" + } +} \ No newline at end of file diff --git a/test/Util.Security.Tests/Authorization/AclPolicyHelperTest.cs b/test/Util.Security.Tests/Authorization/AclPolicyHelperTest.cs new file mode 100644 index 000000000..1d05bb271 --- /dev/null +++ b/test/Util.Security.Tests/Authorization/AclPolicyHelperTest.cs @@ -0,0 +1,33 @@ +using Util.Security.Authorization; +using Xunit; + +namespace Util.Security.Tests.Authorization; + +/// +/// 授权辅助操作测试 +/// +public class AclPolicyHelperTest { + /// + /// 授权策略 + /// + private const string Policy = "acl_{\"Ignore\":true,\"Uri\":\"test\"}"; + + /// + /// 测试获取授权策略 + /// + [Fact] + public void TestGetPolicy() { + var result = AclPolicyHelper.GetPolicy( "test", true ); + Assert.Equal( Policy, result ); + } + + /// + /// 测试获取授权要求 + /// + [Fact] + public void TestGetRequirement() { + var result = AclPolicyHelper.GetRequirement( Policy ); + Assert.Equal( "test", result.Uri ); + Assert.True( result.Ignore ); + } +} \ No newline at end of file diff --git a/test/Util.Security.Tests/Helpers/EncryptTest.cs b/test/Util.Security.Tests/Helpers/EncryptTest.cs new file mode 100644 index 000000000..aea0ea53f --- /dev/null +++ b/test/Util.Security.Tests/Helpers/EncryptTest.cs @@ -0,0 +1,204 @@ +using System.Text; +using Util.Helpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Security.Tests.Helpers; + +/// +/// 加密测试 +/// +public class EncryptTest { + /// + /// 私钥 + /// + private const string PrivateKey = "MIIEowIBAAKCAQEAokcqBnZ6Q1JSdTr7Hqg9rd+JKB60JtZk4LOz3PSPmPppMO7M6/iaYnZgjhWXqGtSxP5yfFiiAFEx2JcTWkmS8kMBMQl3Fzl0AkSybi6Sr/JifMoUYAaGANbzNEpK9Qr9ARdiVeyxSPNbZX4h1HD7TbMx2n106In4C0mMoZukwVcPnEmxJut5FqIdFARiNb/HdlBcMUwGFtGPXM5YAFXBmc+5GCGTjpBOgnhd0OTYB0Cwg7o5aW1KYb1ivDI8AYEdKibl3VAOqMfX+idd77fJlEXeeMVrxFaZBucnn4eS9c0ES/gIqmLvPU3oJbXLopeTttVi75xnKU+FIXnbX2xPWQIDAQABAoIBAQCYf/AKasSzB2XeLTNYuBpej7dBMLlz1f6u+7GHb3yS8qBwD7ob92B+L2jFnZ0L9O+vmL7WBCUZzzPcqvzQ2Ftzt2wjPRXhiWhvyUc1LCdFma7cPruvEfJUT6v18+tFJLJmmCcdQHXKbIfzdPktv48qkb/D6Co5bY7gbJnwNWg7F6rJfsPWN240o9nISLcEHem5khOw7JsYi34QQyGtixFa/4uQBGg4D1Semok4vr4h9GoedK+NJBnWp8/gorowSoZ1t8HWLSpt0y9trb1kvgr9ZmyZYkDSbhJJRlWqODqHvgf50P90b/GqPYroLY5/uED2HmX/16wEZl1Y0tBMqvIBAoGBAMxu7SvJUduwtfoKgJlz3sDodgNVWuQdz6ZZ94faGDOmErl30d/WDau5iN/4n2aAnibRksmPPnUwxOVuX4sR0A2UeZPuoYUzKElYD9Trmw/lKgScZrFsL42lb36kjy3gYTpLk3xPlfm6jBsj6tY38ZAUOBes3EfqEURRMIzG7guJAoGBAMs2Gb4w+i4y9AqDKU4Zbk+Y08p9VdLKvdA4yZp+E2Tb8RzCA3wRzA5iYtTFkQQesPxw3cXN5baR+ugLhZkyk9plJr7+soZav3mdENrs0hbtqzsLT83+gaPQ9oJJtMEx+7XRRNYFcSve57AnFZndeiI8JbLAil5pSDBKRnFMLiFRAoGAEIBbsI8dhmgrKDW0z64kqtmYvQgkwAkP+9ODQXn/PhwHouTjEfhLPjNJsxp0c2eqXGPKP27KkdcP7Z0NJUKY0p/LeH9olkHc3J65GGy0JYJP8/NT7rpW1E4oQ8awr/lOn8/95aje6DuAl6g8inIVk5WuOWsAlOV141fSXWqpSFECgYB7z7ibkse7gjwCc6uk9AWr5ZeU1gyGZBSGWzMqOAqk9wTO2r64xzcmxHcm4EDc275F8JaOwEZnmEXhP7PkhVZkSCD1WcV36q6i8DmmyYevhJtBXEEBZ0Ghh93JwBKra0LLondoVuR/ME7FmuqkVrblSPRHFty3bToYggitWxb4IQKBgFc61S0ecneqV6NxpUZPlaEp81h+JW82Z1IgfCB/mrGty9SHAghhNCl8NGICIwwRGiTMEb1J3DYjAFVt+NxXxh1XyZZp5Gmh+ofRlD4YPieb1klPywTNPCOv2Rf/DyyUTekSwfwR64GvQoFrVgTpOaukFNqy58EWkoIbAJzDTzpG"; + /// + /// 公钥 + /// + private const string PublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAokcqBnZ6Q1JSdTr7Hqg9rd+JKB60JtZk4LOz3PSPmPppMO7M6/iaYnZgjhWXqGtSxP5yfFiiAFEx2JcTWkmS8kMBMQl3Fzl0AkSybi6Sr/JifMoUYAaGANbzNEpK9Qr9ARdiVeyxSPNbZX4h1HD7TbMx2n106In4C0mMoZukwVcPnEmxJut5FqIdFARiNb/HdlBcMUwGFtGPXM5YAFXBmc+5GCGTjpBOgnhd0OTYB0Cwg7o5aW1KYb1ivDI8AYEdKibl3VAOqMfX+idd77fJlEXeeMVrxFaZBucnn4eS9c0ES/gIqmLvPU3oJbXLopeTttVi75xnKU+FIXnbX2xPWQIDAQAB"; + /// + /// 控制台输出 + /// + private readonly ITestOutputHelper _output; + + /// + /// 测试初始化 + /// + public EncryptTest( ITestOutputHelper output ) { + _output = output; + } + + /// + /// 测试Md5加密,返回16位结果 + /// + [Theory] + [InlineData( null, "" )] + [InlineData( "", "" )] + [InlineData( " ", "" )] + [InlineData( "a", "C0F1B6A831C399E2" )] + [InlineData( "中国", "CB143ACD6C929826" )] + public void TestMd5By16( string input, string result ) { + Assert.Equal( result, Encrypt.Md5By16( input ) ); + } + + /// + /// 测试Md5加密,返回32位结果 + /// + [Theory] + [InlineData( null, "" )] + [InlineData( "", "" )] + [InlineData( " ", "" )] + [InlineData( "a", "0CC175B9C0F1B6A831C399E269772661" )] + [InlineData( "中国", "C13DCEABCB143ACD6C9298265D618A9F" )] + public void TestMd5By32( string input, string result ) { + Assert.Equal( result, Encrypt.Md5By32( input ) ); + } + + /// + /// 测试DES加密验证 + /// + [Theory] + [InlineData( null, "", "" )] + [InlineData( "", "", "" )] + [InlineData( "1", "", "" )] + [InlineData( "1", "2", "" )] + public void TestDes_Validate( string input, string key, string result ) { + Assert.Equal( result, Encrypt.DesEncrypt( input, key, Encoding.UTF8 ) ); + Assert.Equal( result, Encrypt.DesDecrypt( input, key, Encoding.UTF8 ) ); + } + + /// + /// 测试DES加密 + /// + [Fact] + public void TestDes_1() { + const double value = 100.123; + var encode = Encrypt.DesEncrypt( value ); + _output.WriteLine( encode ); + Assert.Equal( value.SafeString(), Encrypt.DesDecrypt( encode ) ); + } + + /// + /// 测试DES加密 + /// + [Fact] + public void TestDes_2() { + const string value2 = @"~!@#$%^&*()_+|,./;[]'{}""}{?>:<> \\ qwe测 *试rtyuiopadE15R3JrMnByS3c9sdfghjklzxcvbnm1234567890-=\"; + var encode = Encrypt.DesEncrypt( value2 ); + _output.WriteLine( encode ); + Assert.Equal( value2, Encrypt.DesDecrypt( encode ) ); + } + + /// + /// 测试AES加密验证 + /// + [Theory] + [InlineData( null, "", "" )] + [InlineData( "", "", "" )] + [InlineData( "1", "", "" )] + public void TestAes_Validate( string input, string key, string result ) { + Assert.Equal( result, Encrypt.AesEncrypt( input, key, Encoding.UTF8 ) ); + Assert.Equal( result, Encrypt.AesDecrypt( input, key, Encoding.UTF8 ) ); + } + + /// + /// 测试AES加密 + /// + [Fact] + public void TestAes_1() { + var encode = Encrypt.AesEncrypt( "a" ); + _output.WriteLine( encode ); + Assert.Equal( "a", Encrypt.AesDecrypt( encode ) ); + } + + /// + /// 测试AES加密 + /// + [Fact] + public void TestAes_2() { + var encode = Encrypt.AesEncrypt( "中国" ); + _output.WriteLine( encode ); + Assert.Equal( "中国", Encrypt.AesDecrypt( encode ) ); + } + + /// + /// 测试HmacSha256加密 + /// + [Theory] + [InlineData( null, "" )] + [InlineData( "", "" )] + [InlineData( " ", "" )] + [InlineData( "a", "780c3db4ce3de5b9e55816fba98f590631d96c075271b26976238d5f4444219b" )] + [InlineData( "中国", "dde7619d5465b73d94c18e6d979ab3dd9e478cb91b00d312ece776b282b7e0a9" )] + public void TestHmacSha256( string input, string result ) { + var key = "key"; + _output.WriteLine( $"input:{input},result:{Encrypt.HmacSha256( input, key )}" ); + Assert.Equal( result, Encrypt.HmacSha256( input, key ) ); + } + + /// + /// 测试HmacSha256加密 - 允许空值 + /// + [Theory] + [InlineData(null, "5d5d139563c95b5967b9bd9a8c9b233a9dedb45072794cd232dc1b74832607d0")] + [InlineData("", "5d5d139563c95b5967b9bd9a8c9b233a9dedb45072794cd232dc1b74832607d0")] + [InlineData(" ", "5d5d139563c95b5967b9bd9a8c9b233a9dedb45072794cd232dc1b74832607d0")] + [InlineData(" ", "5d5d139563c95b5967b9bd9a8c9b233a9dedb45072794cd232dc1b74832607d0")] + [InlineData("a", "780c3db4ce3de5b9e55816fba98f590631d96c075271b26976238d5f4444219b")] + [InlineData("中国", "dde7619d5465b73d94c18e6d979ab3dd9e478cb91b00d312ece776b282b7e0a9")] + public void TestHmacSha256_AllowEmptyValue( string input, string result ) { + var key = "key"; + _output.WriteLine( $"input:{input},result:{Encrypt.HmacSha256( input, key, allowEmptyValue: true )}" ); + Assert.Equal( result, Encrypt.HmacSha256( input, key, allowEmptyValue: true ) ); + } + + /// + /// 测试Rsa签名和验签 + /// + [Fact] + public void TestRsaSign() { + const string value = "哈楼,World"; + const string result = "bp8XUr2dFUEIo8q9yySRabuFjpSCChRzgL8+dDps16XscyyUtlPyCBRyRJOWrtDuucoC38knwqUK/XKbQvzdf0HAJrdWsP7k6se3ryeEEJm29zIhqDulefGOrDh9WbhBQSOMNC86mo7jMW+jqwPw8Uhc0aGMHJnEw6lETQ50Mmhqj5Jzq+rdQ5aQEd3WKrY3hU6h60yHyESsjObCEYcXFF0sIKHHT7hambbOxeywgeCd8FZDE0a7rMLE2KsJT+/lHiFizuP1fQS5E6LKK0P/hxecQVONgDENAKEMkS/fbCgp2nnsNph/jHKIlBDmbCI7EsAEiVVCe7LCB01ylsfX+A=="; + var sign = Encrypt.RsaSign( value, PrivateKey ); + _output.WriteLine( sign ); + Assert.Equal(result, sign); + Assert.True( Encrypt.RsaVerify( value, PublicKey, sign ) ); + } + + /// + /// 测试Rsa加密解密 + /// + [Fact] + public void TestRsaEncrypt() { + const string value = "哈楼,World"; + var encryptValue = Util.Helpers.Encrypt.RsaEncrypt( value, PublicKey ); + var result = Util.Helpers.Encrypt.RsaDecrypt( encryptValue, PrivateKey ); + Assert.Equal( value, result ); + } + + /// + /// 测试生成RSA公钥和私钥对 - 加密解密 + /// + [Fact] + public void TestCreateRsaKey_1() { + var keys = Util.Helpers.Encrypt.CreateRsaKey(); + const string value = "哈楼,World"; + var encryptValue = Util.Helpers.Encrypt.RsaEncrypt( value, keys.Item1 ); + var result = Util.Helpers.Encrypt.RsaDecrypt( encryptValue, keys.Item2 ); + Assert.Equal( value, result ); + } + + + /// + /// 测试生成RSA公钥和私钥对 - 签名验签 + /// + [Fact] + public void TestCreateRsaKey_2() { + var keys = Util.Helpers.Encrypt.CreateRsaKey(); + const string value = "哈楼,World"; + var sign = Util.Helpers.Encrypt.RsaSign( value, keys.Item2 ); + var result = Util.Helpers.Encrypt.RsaVerify( value, keys.Item1, sign ); + Assert.True( result ); + } +} \ No newline at end of file diff --git a/test/Util.Security.Tests/Util.Security.Tests.csproj b/test/Util.Security.Tests/Util.Security.Tests.csproj new file mode 100644 index 000000000..a334c04b7 --- /dev/null +++ b/test/Util.Security.Tests/Util.Security.Tests.csproj @@ -0,0 +1,26 @@ + + + + $(NetTargetFramework) + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/test/Util.Templates.Handlebars.Tests.Integration/HandlebarsTemplateEngineTest.cs b/test/Util.Templates.Handlebars.Tests.Integration/HandlebarsTemplateEngineTest.cs new file mode 100644 index 000000000..e52101ed0 --- /dev/null +++ b/test/Util.Templates.Handlebars.Tests.Integration/HandlebarsTemplateEngineTest.cs @@ -0,0 +1,80 @@ +using System.Threading.Tasks; +using Util.Templates.HandlebarsDotNet; +using Xunit; + +namespace Util.Templates.Handlebars.Tests.Integration; + +/// +/// Razorģ - Ⱦģַ +/// +public class HandlebarsTemplateEngineTest { + /// + /// Razorģ + /// + private readonly IHandlebarsTemplateEngine _templateEngine; + + /// + /// Գʼ + /// + public HandlebarsTemplateEngineTest( IHandlebarsTemplateEngine templateEngine ) { + _templateEngine = templateEngine; + } + + /// + /// Ⱦģ - Ⱦûݵģ + /// + [Fact] + public void TestRender_1() { + var result = _templateEngine.Render( "a" ); + Assert.Equal( "a", result ); + } + + /// + /// Ⱦģ - Ⱦģ,ʹ󴫵 + /// + [Fact] + public void TestRender_2() { + var result = _templateEngine.Render( "hello {{Name}}", new { Name = "util" } ); + Assert.Equal( "hello util", result ); + } + + /// + /// Ⱦģ - ı + /// + [Fact] + public void TestRender_3() { + var source = "
{{Text}} {}
"; + var value = new { Text = "<>" }; + var actual2 = _templateEngine.Render( source, value ); + Assert.Equal( "
<> {}
", actual2 ); + } + + /// + /// Ⱦģ - Html + /// + [Fact] + public void TestHtmlEncoder() { + var source = "
{{Text2}} {}
"; + var value = new { Text2 = "<>" }; + var actual2 = _templateEngine.HtmlEncoder().Render( source, value ); + Assert.Equal( "
<> {}
", actual2 ); + } + + /// + /// 첽Ⱦģ + /// + [Fact] + public async Task TestRenderAsync() { + var result = await _templateEngine.RenderAsync( "a" ); + Assert.Equal( "a", result ); + } + + /// + /// Ⱦģ - Ⱦģ,ʹ󴫵 + /// + [Fact] + public async Task TestRenderAsync_2() { + var result = await _templateEngine.RenderAsync( "hello {{Name}}", new { Name = "util" } ); + Assert.Equal( "hello util", result ); + } +} \ No newline at end of file diff --git a/test/Util.Templates.Handlebars.Tests.Integration/Startup.cs b/test/Util.Templates.Handlebars.Tests.Integration/Startup.cs new file mode 100644 index 000000000..439f40b60 --- /dev/null +++ b/test/Util.Templates.Handlebars.Tests.Integration/Startup.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit.DependencyInjection.Logging; + +namespace Util.Templates.Handlebars.Tests.Integration; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + hostBuilder.AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + } +} \ No newline at end of file diff --git a/test/Util.Templates.Handlebars.Tests.Integration/Util.Templates.Handlebars.Tests.Integration.csproj b/test/Util.Templates.Handlebars.Tests.Integration/Util.Templates.Handlebars.Tests.Integration.csproj new file mode 100644 index 000000000..3e638fa01 --- /dev/null +++ b/test/Util.Templates.Handlebars.Tests.Integration/Util.Templates.Handlebars.Tests.Integration.csproj @@ -0,0 +1,28 @@ + + + + $(NetTargetFramework) + false + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.Templates.Razor.Tests.Integration/Filters/ModelFilterTest.cs b/test/Util.Templates.Razor.Tests.Integration/Filters/ModelFilterTest.cs new file mode 100644 index 000000000..8c58e5587 --- /dev/null +++ b/test/Util.Templates.Razor.Tests.Integration/Filters/ModelFilterTest.cs @@ -0,0 +1,41 @@ +using System.Text; +using Util.Templates.Razor.Filters; +using Xunit; + +namespace Util.Templates.Razor.Tests.Integration.Filters; + +/// +/// 模型过滤器测试 +/// +public class ModelFilterTest { + /// + /// 模型过滤器 + /// + private readonly ModelFilter _filter; + + /// + /// 测试初始化 + /// + public ModelFilterTest() { + _filter = new ModelFilter(); + } + + /// + /// 测试将@model替换为@inherits RazorEngineCore.RazorEngineTemplateBase + /// + [Fact] + public void TestFilter() { + var template = new StringBuilder(); + template.Append( "@using Util.Templates.Razor.Tests.Samples.Models " ); + template.Append( "@model TestModel " ); + template.Append( "hello,@Model.Name" ); + var filterTemplate = _filter.Filter( template.ToString() ); + + var result = new StringBuilder(); + result.Append( "@using Util.Templates.Razor.Tests.Samples.Models " ); + result.Append( "@inherits RazorEngineCore.RazorEngineTemplateBase " ); + result.Append( "hello,@Model.Name" ); + + Assert.Equal( result.ToString(), filterTemplate ); + } +} \ No newline at end of file diff --git a/test/Util.Templates.Razor.Tests.Integration/Properties/launchSettings.json b/test/Util.Templates.Razor.Tests.Integration/Properties/launchSettings.json new file mode 100644 index 000000000..1e8744eca --- /dev/null +++ b/test/Util.Templates.Razor.Tests.Integration/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:14906/", + "sslPort": 44319 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Util.Templates.Razor.Tests": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/test/Util.Templates.Razor.Tests.Integration/RazorTemplateEngineTest.Path.cs b/test/Util.Templates.Razor.Tests.Integration/RazorTemplateEngineTest.Path.cs new file mode 100644 index 000000000..105904adc --- /dev/null +++ b/test/Util.Templates.Razor.Tests.Integration/RazorTemplateEngineTest.Path.cs @@ -0,0 +1,83 @@ +using System.Threading.Tasks; +using Util.Helpers; +using Util.Templates.Razor.Tests.Integration.Samples.Models; +using Xunit; + +namespace Util.Templates.Razor.Tests.Integration; + +/// +/// Razorģ - Ⱦģļ +/// +public partial class RazorTemplateEngineTest { + /// + /// ͨ·Ⱦģ + /// + [Fact] + public void TestRenderByPath() { + var model = new TestModel { Name = "util" }; + var path = Common.GetPhysicalPath( "Samples/Templates/TestTemplate.cshtml" ); + var result = _templateEngine.RenderByPath( path, model ); + Assert.Equal( "hello,util", result ); + } + + /// + /// ͨ·Ⱦģ + /// + [Fact] + public void TestRenderByPath_2() { + var model = new TestModel { Name = "a", Model2 = new TestModel2 { Name = "b", Model3 = new TestModel3 { Name = "c" } } }; + var path = Common.GetPhysicalPath( "Samples/Templates/TestTemplate2.cshtml" ); + var result = _templateEngine.RenderByPath( path, model ); + Assert.Equal( "a,b,c", result ); + } + + /// + /// ͨ·Ⱦģ + /// + [Fact] + public async Task TestRenderByPathAsync() { + var model = new TestModel { Name = "util" }; + var path = Common.GetPhysicalPath( "Samples/Templates/TestTemplate.cshtml" ); + var result = await _templateEngine.RenderByPathAsync( path, model ); + Assert.Equal( "hello,util", result ); + } + + /// + /// ͨ·Ⱦģ + /// + [Fact] + public async Task TestRenderByPathAsync_2() { + var model = new TestModel { Name = "a", Model2 = new TestModel2 { Name = "b", Model3 = new TestModel3 { Name = "c" } } }; + var path = Common.GetPhysicalPath( "Samples/Templates/TestTemplate2.cshtml" ); + var result = await _templateEngine.RenderByPathAsync( path, model ); + Assert.Equal( "a,b,c", result ); + } + + /// + /// ͨ·Ⱦģ岢浽ļ + /// + [Fact] + public void TestSaveByPath() { + var filePath = Common.GetPhysicalPath( "result/SaveByPath.txt" ); + Util.Helpers.File.Delete( filePath ); + var model = new TestModel { Name = "util" }; + var templatePath = Common.GetPhysicalPath( "Samples/Templates/TestTemplate.cshtml" ); + _templateEngine.SaveByPath( templatePath, model, filePath ); + var result = Util.Helpers.File.ReadToString( filePath ); + Assert.Equal( "hello,util", result ); + } + + /// + /// ͨ·Ⱦģ岢浽ļ + /// + [Fact] + public async Task TestSaveByPathAsync() { + var filePath = Common.GetPhysicalPath( "result/SaveByPathAsync.txt" ); + Util.Helpers.File.Delete( filePath ); + var model = new TestModel { Name = "util" }; + var templatePath = Common.GetPhysicalPath( "Samples/Templates/TestTemplate.cshtml" ); + await _templateEngine.SaveByPathAsync( templatePath, model, filePath ); + var result = await Util.Helpers.File.ReadToStringAsync( filePath ); + Assert.Equal( "hello,util", result ); + } +} \ No newline at end of file diff --git a/test/Util.Templates.Razor.Tests.Integration/RazorTemplateEngineTest.cs b/test/Util.Templates.Razor.Tests.Integration/RazorTemplateEngineTest.cs new file mode 100644 index 000000000..26c9da334 --- /dev/null +++ b/test/Util.Templates.Razor.Tests.Integration/RazorTemplateEngineTest.cs @@ -0,0 +1,137 @@ +using System.Text; +using System.Threading.Tasks; +using Util.Helpers; +using Util.Templates.Razor.Tests.Integration.Samples.Models; +using Xunit; + +namespace Util.Templates.Razor.Tests.Integration; + +/// +/// Razorģ - Ⱦģַ +/// +public partial class RazorTemplateEngineTest { + /// + /// Razorģ + /// + private readonly ITemplateEngine _templateEngine; + + /// + /// Գʼ + /// + public RazorTemplateEngineTest( ITemplateEngine templateEngine ) { + _templateEngine = templateEngine; + _templateEngine.ClearTemplateCache(); + } + + /// + /// Ⱦģ - Ⱦûݵģ + /// + [Fact] + public void TestRender_1() { + var result = _templateEngine.Render( "a" ); + Assert.Equal( "a", result ); + } + + /// + /// Ⱦģ - Ⱦģ,ʹ󴫵 + /// + [Fact] + public void TestRender_2() { + var result = _templateEngine.Render( "hello @Model.Name", new { Name = "util" } ); + Assert.Equal( "hello util", result ); + } + + /// + /// Ⱦģ - Ⱦģ,TestModel + /// + [Fact] + public void TestRender_3() { + var model = new TestModel { Name = "util" }; + var result = _templateEngine.Render( "hello @Model.Name", model ); + Assert.Equal( "hello util", result ); + } + + /// + /// Ⱦģ - ӳ + /// + [Fact] + public void TestRender_4() { + RazorTemplateEngine.DisableAutoLoadAssemblies(); + var template = new StringBuilder(); + template.Append( "@using Util.Templates.Razor.Tests.Integration.Samples.Models\n" ); + template.Append( "@inherits RazorEngineCore.RazorEngineTemplateBase\n" ); + template.Append( "hello,@Model.Name" ); + var model = new TestModel { Name = "util" }; + var result = _templateEngine.Render( template.ToString(), model, builder => builder.AddAssemblyReference( GetType() ) ); + Assert.Equal( "hello,util", result ); + } + + /// + /// Ⱦģ + /// + [Fact] + public async Task TestRenderAsync_1() { + var result = await _templateEngine.RenderAsync( "a" ); + Assert.Equal( "a", result ); + } + + /// + /// Ⱦģ - Ⱦģ,ʹ󴫵 + /// + [Fact] + public async Task TestRenderAsync_2() { + var result = await _templateEngine.RenderAsync( "hello @Model.Name", new { Name = "util" } ); + Assert.Equal( "hello util", result ); + } + + /// + /// Ⱦģ - Ⱦģ,TestModel + /// + [Fact] + public async Task TestRenderAsync_3() { + var model = new TestModel { Name = "util" }; + var result = await _templateEngine.RenderAsync( "hello @Model.Name", model ); + Assert.Equal( "hello util", result ); + } + + /// + /// Ⱦģ - ӳ + /// + [Fact] + public async Task TestRenderAsync_4() { + RazorTemplateEngine.DisableAutoLoadAssemblies(); + var template = new StringBuilder(); + template.Append( "@using Util.Templates.Razor.Tests.Integration.Samples.Models\n" ); + template.Append( "@inherits RazorEngineCore.RazorEngineTemplateBase\n" ); + template.Append( "hello,@Model.Name" ); + var model = new TestModel { Name = "util" }; + var result = await _templateEngine.RenderAsync( template.ToString(), model, builder => builder.AddAssemblyReference( GetType() ) ); + Assert.Equal( "hello,util", result ); + } + + /// + /// Ⱦģ岢浽ļ + /// + [Fact] + public void TestSave() { + var filePath = Common.GetPhysicalPath( "result/save.txt" ); + Util.Helpers.File.Delete( filePath ); + var model = new TestModel { Name = "util" }; + _templateEngine.Save( "hello @Model.Name", model, filePath ); + var result = Util.Helpers.File.ReadToString( filePath ); + Assert.Equal( "hello util", result ); + } + + /// + /// Ⱦģ岢浽ļ + /// + [Fact] + public async Task TestSaveAsync() { + var filePath = Common.GetPhysicalPath( "result/saveasync.txt" ); + Util.Helpers.File.Delete( filePath ); + var model = new TestModel { Name = "util" }; + await _templateEngine.SaveAsync( "hello @Model.Name", model, filePath ); + var result = await Util.Helpers.File.ReadToStringAsync( filePath ); + Assert.Equal( "hello util", result ); + } +} \ No newline at end of file diff --git a/test/Util.Templates.Razor.Tests.Integration/Samples/Models/TestModel.cs b/test/Util.Templates.Razor.Tests.Integration/Samples/Models/TestModel.cs new file mode 100644 index 000000000..c7bbbccd2 --- /dev/null +++ b/test/Util.Templates.Razor.Tests.Integration/Samples/Models/TestModel.cs @@ -0,0 +1,51 @@ +namespace Util.Templates.Razor.Tests.Integration.Samples.Models; + +/// +/// 测试模型 +/// +public class TestModel { + /// + /// 名称 + /// + public string Name { get; set; } + /// + /// 模型2 + /// + public TestModel2 Model2 { get; set; } +} + +/// +/// 测试模型2 +/// +public class TestModel2 { + /// + /// 名称 + /// + public string Name { get; set; } + /// + /// 模型3 + /// + public TestModel3 Model3 { get; set; } +} + +/// +/// 测试模型3 +/// +public class TestModel3 { + /// + /// 名称 + /// + public string Name { get; set; } +} + +/// +/// 测试模型扩展 +/// +public static class TestModelExtensions { + /// + /// 获取名称 + /// + public static string GetName( this TestModel model ) { + return model.Name; + } +} \ No newline at end of file diff --git a/test/Util.Templates.Razor.Tests.Integration/Samples/Templates/Child/Child2/TestChildTemplate2.cshtml b/test/Util.Templates.Razor.Tests.Integration/Samples/Templates/Child/Child2/TestChildTemplate2.cshtml new file mode 100644 index 000000000..ffd1fc899 --- /dev/null +++ b/test/Util.Templates.Razor.Tests.Integration/Samples/Templates/Child/Child2/TestChildTemplate2.cshtml @@ -0,0 +1,3 @@ +@using Util.Templates.Razor.Tests.Integration.Samples.Models +@model TestModel3 +@Model.Name \ No newline at end of file diff --git a/test/Util.Templates.Razor.Tests.Integration/Samples/Templates/Child/TestChildTemplate.cshtml b/test/Util.Templates.Razor.Tests.Integration/Samples/Templates/Child/TestChildTemplate.cshtml new file mode 100644 index 000000000..4b2722b51 --- /dev/null +++ b/test/Util.Templates.Razor.Tests.Integration/Samples/Templates/Child/TestChildTemplate.cshtml @@ -0,0 +1,3 @@ +@using Util.Templates.Razor.Tests.Integration.Samples.Models +@model TestModel2 +@Model.Name,@Html.PartialAsync( "~/Samples/Templates/Child/Child2/TestChildTemplate2.cshtml",Model.Model3 ) \ No newline at end of file diff --git a/test/Util.Templates.Razor.Tests.Integration/Samples/Templates/TestTemplate.cshtml b/test/Util.Templates.Razor.Tests.Integration/Samples/Templates/TestTemplate.cshtml new file mode 100644 index 000000000..11133556e --- /dev/null +++ b/test/Util.Templates.Razor.Tests.Integration/Samples/Templates/TestTemplate.cshtml @@ -0,0 +1,3 @@ +@using Util.Templates.Razor.Tests.Integration.Samples.Models +@model TestModel +hello,@Model.Name \ No newline at end of file diff --git a/test/Util.Templates.Razor.Tests.Integration/Samples/Templates/TestTemplate2.cshtml b/test/Util.Templates.Razor.Tests.Integration/Samples/Templates/TestTemplate2.cshtml new file mode 100644 index 000000000..fc738ec0a --- /dev/null +++ b/test/Util.Templates.Razor.Tests.Integration/Samples/Templates/TestTemplate2.cshtml @@ -0,0 +1,3 @@ +@using Util.Templates.Razor.Tests.Integration.Samples.Models +@model TestModel +@Model.Name,@await Html.PartialAsync( "~/Samples/Templates/Child/TestChildTemplate.cshtml",Model.Model2 ) \ No newline at end of file diff --git a/test/Util.Templates.Razor.Tests.Integration/Startup.cs b/test/Util.Templates.Razor.Tests.Integration/Startup.cs new file mode 100644 index 000000000..151a10db5 --- /dev/null +++ b/test/Util.Templates.Razor.Tests.Integration/Startup.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit.DependencyInjection.Logging; + +namespace Util.Templates.Razor.Tests.Integration; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + hostBuilder.AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + } +} \ No newline at end of file diff --git a/test/Util.Templates.Razor.Tests.Integration/Util.Templates.Razor.Tests.Integration.csproj b/test/Util.Templates.Razor.Tests.Integration/Util.Templates.Razor.Tests.Integration.csproj new file mode 100644 index 000000000..d108d0d61 --- /dev/null +++ b/test/Util.Templates.Razor.Tests.Integration/Util.Templates.Razor.Tests.Integration.csproj @@ -0,0 +1,62 @@ + + + + $(NetTargetFramework) + false + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + Always + + + Always + + + Always + + + + + + Always + + + Always + + + Always + + + Always + + + Always + + + + diff --git a/test/Util.Tenants.Tests.Integration/Controllers/TestController.cs b/test/Util.Tenants.Tests.Integration/Controllers/TestController.cs new file mode 100644 index 000000000..027dc4a4a --- /dev/null +++ b/test/Util.Tenants.Tests.Integration/Controllers/TestController.cs @@ -0,0 +1,29 @@ +namespace Util.Tenants.Tests.Controllers; + +/// +/// 测试控制器 +/// +[Route( "api/test" )] +public class TestController : ControllerBase { + /// + /// 租户管理器 + /// + private readonly ITenantManager _tenantManager; + + /// + /// 初始化测试控制器 + /// + /// 租户管理器 + public TestController( ITenantManager tenantManager ) { + _tenantManager = tenantManager; + } + + /// + /// 获取当前租户标识 + /// + [HttpGet] + public IActionResult GetTenantIdAsync() { + var result = _tenantManager.GetTenantId(); + return new ContentResult { Content = result }; + } +} \ No newline at end of file diff --git a/test/Util.Tenants.Tests.Integration/Properties/launchSettings.json b/test/Util.Tenants.Tests.Integration/Properties/launchSettings.json new file mode 100644 index 000000000..03a57330c --- /dev/null +++ b/test/Util.Tenants.Tests.Integration/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Util.Tenants.Tests.Integration": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:39350;http://localhost:39351" + } + } +} \ No newline at end of file diff --git a/test/Util.Tenants.Tests.Integration/Resolvers/CookieTenantResolverTest.cs b/test/Util.Tenants.Tests.Integration/Resolvers/CookieTenantResolverTest.cs new file mode 100644 index 000000000..cd68482ba --- /dev/null +++ b/test/Util.Tenants.Tests.Integration/Resolvers/CookieTenantResolverTest.cs @@ -0,0 +1,34 @@ +namespace Util.Tenants.Tests.Resolvers; + +/// +/// Cookie租户解析器测试 +/// +public class CookieTenantResolverTest { + /// + /// Http客户端 + /// + private readonly IHttpClient _client; + /// + /// 租户配置 + /// + private readonly TenantOptions _options; + + /// + /// 测试初始化 + /// + public CookieTenantResolverTest( IHttpClient client, IOptions options ) { + _client = client; + _options = options.Value; + } + + /// + /// 测试解析租户标识 + /// + [Fact] + public async Task TestResolveAsync() { + var result = await _client.Get( "/api/test" ) + .Cookie( _options.TenantKey,"a" ) + .GetResultAsync(); + Assert.Equal( "a", result ); + } +} \ No newline at end of file diff --git a/test/Util.Tenants.Tests.Integration/Resolvers/DomainTenantResolverTest.cs b/test/Util.Tenants.Tests.Integration/Resolvers/DomainTenantResolverTest.cs new file mode 100644 index 000000000..3e695a557 --- /dev/null +++ b/test/Util.Tenants.Tests.Integration/Resolvers/DomainTenantResolverTest.cs @@ -0,0 +1,51 @@ +namespace Util.Tenants.Tests.Resolvers; + +/// +/// 域名租户解析器测试 +/// +public class DomainTenantResolverTest { + /// + /// Http客户端 + /// + private readonly IHttpClient _client; + /// + /// 域名租户解析器 + /// + private readonly IDomainTenantResolver _resolver; + + /// + /// 测试初始化 + /// + public DomainTenantResolverTest( IHttpClient client, IDomainTenantResolver resolver ) { + _client = client; + _resolver = resolver; + } + + /// + /// 测试解析租户标识 + /// + [Fact] + public async Task TestResolveAsync() { + var result = await _client.Get( "http://a.test.com/api/test" ) + .GetResultAsync(); + Assert.Equal( "a", result ); + } + + /// + /// 测试解析租户标识 - 从域名格式解析 + /// + [Fact] + public async Task TestResolveTenantIdAsync_1() { + var result = await _resolver.ResolveTenantIdAsync( "http://a1.a.test.com" ); + Assert.Equal( "a1", result ); + } + + /// + /// 测试解析租户标识 - 从域名映射解析 + /// + [Fact] + public async Task TestResolveTenantIdAsync_2() { + var result = await _resolver.ResolveTenantIdAsync( "http://b.test.com" ); + Assert.Equal( "b1", result ); + } +} \ No newline at end of file diff --git a/test/Util.Tenants.Tests.Integration/Resolvers/HeaderTenantResolverTest.cs b/test/Util.Tenants.Tests.Integration/Resolvers/HeaderTenantResolverTest.cs new file mode 100644 index 000000000..1f5e52453 --- /dev/null +++ b/test/Util.Tenants.Tests.Integration/Resolvers/HeaderTenantResolverTest.cs @@ -0,0 +1,34 @@ +namespace Util.Tenants.Tests.Resolvers; + +/// +/// 请求头租户解析器测试 +/// +public class HeaderTenantResolverTest { + /// + /// Http客户端 + /// + private readonly IHttpClient _client; + /// + /// 租户配置 + /// + private readonly TenantOptions _options; + + /// + /// 测试初始化 + /// + public HeaderTenantResolverTest( IHttpClient client, IOptions options ) { + _client = client; + _options = options.Value; + } + + /// + /// 测试解析租户标识 + /// + [Fact] + public async Task TestResolveAsync() { + var result = await _client.Get( "/api/test" ) + .Header( _options.TenantKey, "a" ) + .GetResultAsync(); + Assert.Equal( "a", result ); + } +} \ No newline at end of file diff --git a/test/Util.Tenants.Tests.Integration/Resolvers/QueryStringTenantResolverTest.cs b/test/Util.Tenants.Tests.Integration/Resolvers/QueryStringTenantResolverTest.cs new file mode 100644 index 000000000..7579230aa --- /dev/null +++ b/test/Util.Tenants.Tests.Integration/Resolvers/QueryStringTenantResolverTest.cs @@ -0,0 +1,33 @@ +namespace Util.Tenants.Tests.Resolvers; + +/// +/// 查询字符串租户解析器测试 +/// +public class QueryStringTenantResolverTest { + /// + /// Http客户端 + /// + private readonly IHttpClient _client; + /// + /// 租户配置 + /// + private readonly TenantOptions _options; + + /// + /// 测试初始化 + /// + public QueryStringTenantResolverTest( IHttpClient client, IOptions options ) { + _client = client; + _options = options.Value; + } + + /// + /// 测试解析租户标识 + /// + [Fact] + public async Task TestResolveAsync() { + var result = await _client.Get( $"/api/test?{_options.TenantKey}=a" ) + .GetResultAsync(); + Assert.Equal( "a", result ); + } +} \ No newline at end of file diff --git a/test/Util.Tenants.Tests.Integration/Startup.cs b/test/Util.Tenants.Tests.Integration/Startup.cs new file mode 100644 index 000000000..e0eda4c52 --- /dev/null +++ b/test/Util.Tenants.Tests.Integration/Startup.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using Util.Helpers; +using Util.Tenants.Resolvers; + +namespace Util.Tenants.Tests; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + Environment.SetDevelopment(); + hostBuilder.ConfigureDefaults( null ) + .ConfigureWebHostDefaults( webHostBuilder => { + webHostBuilder.UseTestServer() + .Configure( t => { + t.UseTenant(); + t.UseRouting(); + t.UseEndpoints( endpoints => { + endpoints.MapControllers(); + } ); + } ); + } ) + .AsBuild() + .AddAop() + .AddTenant( t => { + var map = new Dictionary { { "b.test.com", "b1" } }; + t.Resolvers.Add( new DomainTenantResolver( map ) { Priority = 90 } ); + t.Resolvers.Add( "a", new DomainTenantResolver( "{0}.a.test.com" ) { Priority = 80 } ); + map = new Dictionary { { "c.test.com", "c1" } }; + t.Resolvers.Add( "b", new DomainTenantResolver( map ) { Priority = 70 } ); + t.Resolvers.Add( "c", new DomainTenantResolver( "{0}.b.test.com" ) { Priority = 100 } ); + } ) + .AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + services.AddControllers(); + services.AddTransient( t => { + var client = new HttpClientService(); + client.SetHttpClient( t.GetService().GetTestClient() ); + return client; + } ); + } +} \ No newline at end of file diff --git a/test/Util.Tenants.Tests.Integration/Usings.cs b/test/Util.Tenants.Tests.Integration/Usings.cs new file mode 100644 index 000000000..a785370d5 --- /dev/null +++ b/test/Util.Tenants.Tests.Integration/Usings.cs @@ -0,0 +1,12 @@ +global using System.Threading.Tasks; +global using Microsoft.AspNetCore.Builder; +global using Microsoft.AspNetCore.Hosting; +global using Microsoft.AspNetCore.TestHost; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.Options; +global using Microsoft.AspNetCore.Mvc; +global using Xunit; +global using Xunit.DependencyInjection.Logging; +global using Util.Http; +global using Util.Aop; \ No newline at end of file diff --git a/test/Util.Tenants.Tests.Integration/Util.Tenants.Tests.Integration.csproj b/test/Util.Tenants.Tests.Integration/Util.Tenants.Tests.Integration.csproj new file mode 100644 index 000000000..ce3f73ed5 --- /dev/null +++ b/test/Util.Tenants.Tests.Integration/Util.Tenants.Tests.Integration.csproj @@ -0,0 +1,32 @@ + + + + $(NetTargetFramework) + false + Util.Tenants.Tests + Util.Tenants.Tests.Startup + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + \ No newline at end of file diff --git a/test/Util.Tenants.Tests/Managements/TenantManagerTest.cs b/test/Util.Tenants.Tests/Managements/TenantManagerTest.cs new file mode 100644 index 000000000..0dc457eaa --- /dev/null +++ b/test/Util.Tenants.Tests/Managements/TenantManagerTest.cs @@ -0,0 +1,281 @@ +using Util.Tenants.Managements; +using Util.Tenants.Tests.Samples; +using ISession = Util.Sessions.ISession; + +namespace Util.Tenants.Tests.Managements; + +/// +/// 租户管理器测试 +/// +public class TenantManagerTest { + + #region 测试初始化 + + /// + /// 模拟租户存储器 + /// + private readonly Mock _mockStore; + /// + /// 模拟查看租户管理器 + /// + private readonly Mock _mockView; + /// + /// 模拟切换租户管理器 + /// + private readonly Mock _mockSwitch; + /// + /// 模拟配置 + /// + private readonly Mock> _mockOptions; + /// + /// 租户管理器 + /// + private TenantManager _tenantManager; + + /// + /// 测试初始化 + /// + public TenantManagerTest() { + _mockStore = new Mock(); + _mockView = new Mock(); + _mockSwitch = new Mock(); + _mockOptions = new Mock>(); + _mockOptions.Setup( t => t.Value ).Returns( new TenantOptions { IsEnabled = true } ); + _tenantManager = new TenantManager( _mockStore.Object, _mockView.Object, _mockSwitch.Object, GetTestSession(), _mockOptions.Object ); + } + + /// + /// 获取测试用户会话 + /// + private ISession GetTestSession() { + return new TestSession( false, null, null ); + } + + #endregion + + #region IsHost + + /// + /// 测试是否平台供应商 + /// + [Fact] + public void TestIsHost_1() { + //设置 + var tenantId = "t"; + _mockOptions.Setup( t => t.Value ).Returns( new TenantOptions { DefaultTenantId = tenantId } ); + TenantManager.CurrentTenantId = tenantId; + + //执行 + _tenantManager = new TenantManager( _mockStore.Object, _mockView.Object, _mockSwitch.Object, new TestSession( true, "u", tenantId ), _mockOptions.Object ); + + //验证 + Assert.True( _tenantManager.IsHost() ); + } + + /// + /// 测试是否平台供应商 - 未登录 + /// + [Fact] + public void TestIsHost_2() { + //设置 + var tenantId = "t"; + _mockOptions.Setup( t => t.Value ).Returns( new TenantOptions { DefaultTenantId = tenantId } ); + TenantManager.CurrentTenantId = tenantId; + + //执行 + _tenantManager = new TenantManager( _mockStore.Object, _mockView.Object, _mockSwitch.Object, new TestSession( false, "u", tenantId ), _mockOptions.Object ); + + //验证 + Assert.False( _tenantManager.IsHost() ); + } + + /// + /// 测试是否平台供应商 - 用户名为空 + /// + [Fact] + public void TestIsHost_3() { + //设置 + var tenantId = "t"; + _mockOptions.Setup( t => t.Value ).Returns( new TenantOptions { DefaultTenantId = tenantId } ); + TenantManager.CurrentTenantId = tenantId; + + //执行 + _tenantManager = new TenantManager( _mockStore.Object, _mockView.Object, _mockSwitch.Object, new TestSession( true, "", tenantId ), _mockOptions.Object ); + + //验证 + Assert.False( _tenantManager.IsHost() ); + } + + /// + /// 测试是否平台供应商 - 用户会话中的租户标识与当前租户不同 + /// + [Fact] + public void TestIsHost_4() { + //设置 + var tenantId = "t"; + _mockOptions.Setup( t => t.Value ).Returns( new TenantOptions { DefaultTenantId = tenantId } ); + TenantManager.CurrentTenantId = "t2"; + + //执行 + _tenantManager = new TenantManager( _mockStore.Object, _mockView.Object, _mockSwitch.Object, new TestSession( true, "u", tenantId ), _mockOptions.Object ); + + //验证 + Assert.False( _tenantManager.IsHost() ); + } + + /// + /// 测试是否平台供应商 - 当前租户标识与默认租户不同 + /// + [Fact] + public void TestIsHost_5() { + //设置 + var tenantId = "t"; + _mockOptions.Setup( t => t.Value ).Returns( new TenantOptions { DefaultTenantId = "" } ); + TenantManager.CurrentTenantId = tenantId; + + //执行 + _tenantManager = new TenantManager( _mockStore.Object, _mockView.Object, _mockSwitch.Object, new TestSession( true, "u", tenantId ), _mockOptions.Object ); + + //验证 + Assert.False( _tenantManager.IsHost() ); + } + + #endregion + + #region GetTenantId + + /// + /// 测试获取当前租户标识 - 普通租户,返回租户标识 + /// + [Fact] + public void TestGetTenantId_1() { + //设置当前租户标识 + TenantManager.CurrentTenantId = "a"; + + //执行 + var result = _tenantManager.GetTenantId(); + + //验证 + Assert.Equal( "a", result ); + } + + /// + /// 测试获取当前租户标识 - 平台供应商,未切换租户,返回平台供应商租户标识 + /// + [Fact] + public void TestGetTenantId_2() { + //变量定义 + var tenantId = "t"; + + //设置当前租户标识 + TenantManager.CurrentTenantId = tenantId; + + //设置 + _mockOptions.Setup( t => t.Value ).Returns( new TenantOptions { IsEnabled = true, DefaultTenantId = tenantId } ); + + //重新初始化 + _tenantManager = new TenantManager( _mockStore.Object, _mockView.Object, _mockSwitch.Object, new TestSession( true, "u", tenantId ), _mockOptions.Object ); + + //执行 + var result = _tenantManager.GetTenantId(); + + //验证 + Assert.Equal( tenantId, result ); + } + + /// + /// 测试获取当前租户标识 - 平台供应商,切换租户,返回切换的租户标识 + /// + [Fact] + public void TestGetTenantId_3() { + //变量定义 + var tenantId = "t"; + + //设置当前租户标识 + TenantManager.CurrentTenantId = tenantId; + + //设置 + _mockOptions.Setup( t => t.Value ).Returns( new TenantOptions { IsEnabled = true, DefaultTenantId = tenantId } ); + _mockSwitch.Setup( t => t.GetSwitchTenantId() ).Returns( "t2" ); + + //重新初始化 + _tenantManager = new TenantManager( _mockStore.Object, _mockView.Object, _mockSwitch.Object, new TestSession( true, "u", tenantId ), _mockOptions.Object ); + + //执行 + var result = _tenantManager.GetTenantId(); + + //验证 + Assert.Equal( "t2", result ); + } + + #endregion + + #region IsDisableTenantFilter + + /// + /// 测试是否禁用租户过滤器 - 普通租户,不禁用 + /// + [Fact] + public void TestIsDisableTenantFilter_1() { + //设置 + _mockView.Setup( t => t.IsDisableTenantFilter() ).Returns( true ); + + //执行 + var result = _tenantManager.IsDisableTenantFilter(); + + //验证 + Assert.False( result ); + } + + /// + /// 测试是否禁用租户过滤器 - 平台供应商,查看租户管理器设置为true + /// + [Fact] + public void TestIsDisableTenantFilter_2() { + //变量定义 + var tenantId = "t"; + + //设置当前租户标识 + TenantManager.CurrentTenantId = tenantId; + + //设置 + _mockOptions.Setup( t => t.Value ).Returns( new TenantOptions { IsEnabled = true, DefaultTenantId = tenantId } ); + _mockView.Setup( t => t.IsDisableTenantFilter() ).Returns( true ); + + //重新初始化 + _tenantManager = new TenantManager( _mockStore.Object, _mockView.Object, _mockSwitch.Object, new TestSession( true, "u", tenantId ), _mockOptions.Object ); + + //执行 + var result = _tenantManager.IsDisableTenantFilter(); + + //验证 + Assert.True( result ); + } + + /// + /// 测试是否禁用租户过滤器 - 平台供应商,查看租户管理器设置为false + /// + [Fact] + public void TestIsDisableTenantFilter_3() { + //变量定义 + var tenantId = "t"; + + //设置当前租户标识 + TenantManager.CurrentTenantId = tenantId; + + //设置 + _mockOptions.Setup( t => t.Value ).Returns( new TenantOptions { IsEnabled = true, DefaultTenantId = tenantId } ); + _mockView.Setup( t => t.IsDisableTenantFilter() ).Returns( false ); + + //重新初始化 + _tenantManager = new TenantManager( _mockStore.Object, _mockView.Object, _mockSwitch.Object, new TestSession( true, "u", tenantId ), _mockOptions.Object ); + + //执行 + var result = _tenantManager.IsDisableTenantFilter(); + + //验证 + Assert.False( result ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Tenants.Tests/Resolvers/CookieTenantResolverTest.cs b/test/Util.Tenants.Tests/Resolvers/CookieTenantResolverTest.cs new file mode 100644 index 000000000..6d9315f2f --- /dev/null +++ b/test/Util.Tenants.Tests/Resolvers/CookieTenantResolverTest.cs @@ -0,0 +1,70 @@ +using Util.Helpers; +using Util.Tenants.Resolvers; + +namespace Util.Tenants.Tests.Resolvers; + +/// +/// Cookie租户解析器测试 +/// +public class CookieTenantResolverTest { + /// + /// Cookie租户解析器 + /// + private readonly CookieTenantResolver _resolver; + /// + /// 模拟Http上下文 + /// + private readonly Mock _mockHttpContext; + + /// + /// 测试初始化 + /// + public CookieTenantResolverTest() { + _resolver = new CookieTenantResolver(); + _mockHttpContext = new Mock(); + } + + /// + /// 测试解析租户标识 - 默认租户键名 + /// + [Fact] + public async Task TestResolveAsync_1() { + //设置Cookie + var mockCookies = new Mock(); + mockCookies.Setup( t => t[TenantOptions.DefaultTenantKey] ).Returns( "a" ); + _mockHttpContext.Setup( t => t.Request.Cookies ).Returns( mockCookies.Object ); + + //设置租户配置 + var container = Ioc.CreateContainer(); + container.GetServices().Configure( t => t.IsEnabled = true ); + _mockHttpContext.Setup( t => t.RequestServices ).Returns( container.GetServiceProvider() ); + + //执行 + var result = await _resolver.ResolveAsync( _mockHttpContext.Object ); + + //验证 + Assert.Equal( "a", result ); + } + + /// + /// 测试解析租户标识 - 指定租户键名 + /// + [Fact] + public async Task TestResolveAsync_2() { + //设置Cookie + var mockCookies = new Mock(); + mockCookies.Setup( t => t["key"] ).Returns( "a" ); + _mockHttpContext.Setup( t => t.Request.Cookies ).Returns( mockCookies.Object ); + + //设置租户键名 + var container = Ioc.CreateContainer(); + container.GetServices().Configure( t => t.TenantKey = "key" ); + _mockHttpContext.Setup( t => t.RequestServices ).Returns( container.GetServiceProvider() ); + + //执行 + var result = await _resolver.ResolveAsync( _mockHttpContext.Object ); + + //验证 + Assert.Equal( "a", result ); + } +} \ No newline at end of file diff --git a/test/Util.Tenants.Tests/Resolvers/DefaultTenantResolverTest.cs b/test/Util.Tenants.Tests/Resolvers/DefaultTenantResolverTest.cs new file mode 100644 index 000000000..d24ef49a4 --- /dev/null +++ b/test/Util.Tenants.Tests/Resolvers/DefaultTenantResolverTest.cs @@ -0,0 +1,130 @@ +using Util.Helpers; +using Util.Tenants.Resolvers; +using Util.Tenants.Tests.Samples; +using ISession = Util.Sessions.ISession; + +namespace Util.Tenants.Tests.Resolvers; + +/// +/// 默认租户解析器测试 +/// +public class DefaultTenantResolverTest { + /// + /// 默认租户解析器 + /// + private readonly DefaultTenantResolver _resolver; + + /// + /// 测试初始化 + /// + public DefaultTenantResolverTest() { + _resolver = new DefaultTenantResolver( GetMockOptions().Object ); + } + + /// + /// 获取模拟配置 + /// + private Mock> GetMockOptions( bool isEnabled = true ) { + var mockOptions = new Mock>(); + var options = new TenantOptions { + IsEnabled = isEnabled + }; + options.Resolvers.Clear(); + options.Resolvers.Add( new TestTenantResolver() ); + options.Resolvers.Add( new Test2TenantResolver() ); + options.Resolvers.Add( new Test3TenantResolver() ); + mockOptions.Setup( t => t.Value ).Returns( options ); + return mockOptions; + } + + /// + /// 测试解析租户标识 - Test3TenantResolver优先级最高,但返回空字符串,Test2TenantResolver的优先级次之,TestTenantResolver不执行 + /// + [Fact] + public async Task TestResolveAsync_1() { + //创建模拟用户会话 + var mockSession = new Mock(); + mockSession.Setup( t => t.IsAuthenticated ).Returns( false ); + mockSession.Setup( t => t.TenantId ).Returns( "a" ); + + //创建模拟Http上下文 + var container = Ioc.CreateContainer(); + var services = container.GetServices(); + services.AddTransient( _ => mockSession.Object ); + var mockHttpContext = new Mock(); + mockHttpContext.Setup( t => t.RequestServices ).Returns( container.GetServiceProvider() ); + + //执行并验证 + var result = await _resolver.ResolveAsync( mockHttpContext.Object ); + Assert.Equal( "Test2", result ); + } + + /// + /// 测试解析租户标识 - 未启用租户,返回空 + /// + [Fact] + public async Task TestResolveAsync_2() { + //禁用租户 + var resolver = new DefaultTenantResolver( GetMockOptions( false ).Object ); + + //创建模拟用户会话 + var mockSession = new Mock(); + mockSession.Setup( t => t.IsAuthenticated ).Returns( false ); + mockSession.Setup( t => t.TenantId ).Returns( "a" ); + + //创建模拟Http上下文 + var container = Ioc.CreateContainer(); + var services = container.GetServices(); + services.AddTransient( _ => mockSession.Object ); + var mockHttpContext = new Mock(); + mockHttpContext.Setup( t => t.RequestServices ).Returns( container.GetServiceProvider() ); + + //执行并验证 + var result = await resolver.ResolveAsync( mockHttpContext.Object ); + Assert.Null( result ); + } + + /// + /// 测试解析租户标识 - 用户已登录,返回用户租户标识 + /// + [Fact] + public async Task TestResolveAsync_3() { + //创建模拟用户会话 + var mockSession = new Mock(); + mockSession.Setup( t => t.IsAuthenticated ).Returns( true ); + mockSession.Setup( t => t.TenantId ).Returns( "a" ); + + //创建模拟Http上下文 + var container = Ioc.CreateContainer(); + var services = container.GetServices(); + services.AddTransient( _ => mockSession.Object ); + var mockHttpContext = new Mock(); + mockHttpContext.Setup( t => t.RequestServices ).Returns( container.GetServiceProvider() ); + + //执行并验证 + var result = await _resolver.ResolveAsync( mockHttpContext.Object ); + Assert.Equal( "a", result ); + } + + /// + /// 测试解析租户标识 - 用户已登录,返回用户租户标识 - 租户标识为空 + /// + [Fact] + public async Task TestResolveAsync_4() { + //创建模拟用户会话 + var mockSession = new Mock(); + mockSession.Setup( t => t.IsAuthenticated ).Returns( true ); + mockSession.Setup( t => t.TenantId ).Returns( string.Empty ); + + //创建模拟Http上下文 + var container = Ioc.CreateContainer(); + var services = container.GetServices(); + services.AddTransient( _ => mockSession.Object ); + var mockHttpContext = new Mock(); + mockHttpContext.Setup( t => t.RequestServices ).Returns( container.GetServiceProvider() ); + + //执行并验证 + var result = await _resolver.ResolveAsync( mockHttpContext.Object ); + Assert.Equal( string.Empty, result ); + } +} \ No newline at end of file diff --git a/test/Util.Tenants.Tests/Resolvers/DomainTenantResolverTest.cs b/test/Util.Tenants.Tests/Resolvers/DomainTenantResolverTest.cs new file mode 100644 index 000000000..c7185d4f3 --- /dev/null +++ b/test/Util.Tenants.Tests/Resolvers/DomainTenantResolverTest.cs @@ -0,0 +1,222 @@ +using Util.Helpers; +using Util.Tenants.Resolvers; + +namespace Util.Tenants.Tests.Resolvers; + +/// +/// 域名租户解析器测试 +/// +public class DomainTenantResolverTest { + /// + /// 域名租户解析器 + /// + private DomainTenantResolver _resolver; + /// + /// 模拟Http上下文 + /// + private readonly Mock _mockHttpContext; + + /// + /// 测试初始化 + /// + public DomainTenantResolverTest() { + _resolver = new DomainTenantResolver(); + _mockHttpContext = new Mock(); + } + + /// + /// 测试解析租户标识 - 默认解析 - 2级域名 + /// + [Fact] + public async Task TestResolveAsync_1() { + //设置主机名 + _mockHttpContext.Setup( t => t.Request.Host ).Returns( new HostString( "http://a.test.com" ) ); + _mockHttpContext.Setup( t => t.RequestServices ).Returns( Ioc.GetServiceProvider() ); + + //执行 + var result = await _resolver.ResolveAsync( _mockHttpContext.Object ); + + //验证 + Assert.Equal( "a", result ); + } + + /// + /// 测试解析租户标识 - 默认解析 - 顶级域名 + /// + [Fact] + public async Task TestResolveAsync_2() { + //设置主机名 + _mockHttpContext.Setup( t => t.Request.Host ).Returns( new HostString( "http://test.com" ) ); + _mockHttpContext.Setup( t => t.RequestServices ).Returns( Ioc.GetServiceProvider() ); + + //执行 + var result = await _resolver.ResolveAsync( _mockHttpContext.Object ); + + //验证 + Assert.Null( result ); + } + + /// + /// 测试解析租户标识 - 默认解析 - 3级域名 + /// + [Fact] + public async Task TestResolveAsync_3() { + //设置主机名 + _mockHttpContext.Setup( t => t.Request.Host ).Returns( new HostString( "https://a.b.test.com" ) ); + _mockHttpContext.Setup( t => t.RequestServices ).Returns( Ioc.GetServiceProvider() ); + + //执行 + var result = await _resolver.ResolveAsync( _mockHttpContext.Object ); + + //验证 + Assert.Equal( "a", result ); + } + + /// + /// 测试解析租户标识 - 指定域名格式 + /// + [Fact] + public async Task TestResolveAsync_4() { + //传入域名格式 + _resolver = new DomainTenantResolver( "b.{0}.test.com" ); + + //设置主机名 + _mockHttpContext.Setup( t => t.Request.Host ).Returns( new HostString( "https://b.a.test.com" ) ); + _mockHttpContext.Setup( t => t.RequestServices ).Returns( Ioc.GetServiceProvider() ); + + //执行 + var result = await _resolver.ResolveAsync( _mockHttpContext.Object ); + + //验证 + Assert.Equal( "a", result ); + } + + /// + /// 测试解析租户标识 - 指定域名格式 - http前缀 + /// + [Fact] + public async Task TestResolveAsync_5() { + //传入域名格式 + _resolver = new DomainTenantResolver( "http://b.{0}.test.com" ); + + //设置主机名 + _mockHttpContext.Setup( t => t.Request.Host ).Returns( new HostString( "https://b.a.test.com" ) ); + _mockHttpContext.Setup( t => t.RequestServices ).Returns( Ioc.GetServiceProvider() ); + + //执行 + var result = await _resolver.ResolveAsync( _mockHttpContext.Object ); + + //验证 + Assert.Equal( "a", result ); + } + + /// + /// 测试解析租户标识 - 指定多个域名格式 - 逗号分隔 + /// + [Fact] + public async Task TestResolveAsync_6() { + //传入域名格式 + _resolver = new DomainTenantResolver( "b.{0}.test.com,{0}.test.com" ); + + //设置主机名 + _mockHttpContext.Setup( t => t.Request.Host ).Returns( new HostString( "https://b.a.test.com" ) ); + _mockHttpContext.Setup( t => t.RequestServices ).Returns( Ioc.GetServiceProvider() ); + + //执行 + var result = await _resolver.ResolveAsync( _mockHttpContext.Object ); + + //验证 + Assert.Equal( "a", result ); + } + + /// + /// 测试解析租户标识 - 指定多个域名格式 - 分号分隔 + /// + [Fact] + public async Task TestResolveAsync_7() { + //传入域名格式 + _resolver = new DomainTenantResolver( "http://b.{0}.test.com;;https://{0}.test.com" ); + + //设置主机名 + _mockHttpContext.Setup( t => t.Request.Host ).Returns( new HostString( "https://b.a.test.com" ) ); + _mockHttpContext.Setup( t => t.RequestServices ).Returns( Ioc.GetServiceProvider() ); + + //执行 + var result = await _resolver.ResolveAsync( _mockHttpContext.Object ); + + //验证 + Assert.Equal( "a", result ); + } + + /// + /// 测试解析租户标识 - 域名映射配置 + /// + [Fact] + public async Task TestResolveAsync_8() { + //传入域名格式 + var map = new Dictionary { + {"a.test.com","b"} + }; + _resolver = new DomainTenantResolver( map ); + + //设置主机名 + _mockHttpContext.Setup( t => t.Request.Host ).Returns( new HostString( "https://a.test.com" ) ); + _mockHttpContext.Setup( t => t.RequestServices ).Returns( Ioc.GetServiceProvider() ); + + //执行 + var result = await _resolver.ResolveAsync( _mockHttpContext.Object ); + + //验证 + Assert.Equal( "b", result ); + } + + /// + /// 测试解析租户标识 - 租户域名存储器 + /// + [Fact] + public async Task TestResolveAsync_9() { + //设置主机名 + _mockHttpContext.Setup( t => t.Request.Host ).Returns( new HostString( "https://a.test.com" ) ); + + //设置租户域名存储器 + var mockResolver = new Mock(); + mockResolver.Setup( t => t.GetAsync() ).ReturnsAsync( new Dictionary{{ "a.test.com", "b" } } ); + var container = Ioc.CreateContainer(); + container.GetServices().AddTransient( _ => mockResolver.Object ); + _mockHttpContext.Setup( t => t.RequestServices ).Returns( container.GetServiceProvider() ); + + //执行 + var result = await _resolver.ResolveAsync( _mockHttpContext.Object ); + + //验证 + Assert.Equal( "b", result ); + } + + /// + /// 测试解析租户标识 - 租户域名存储器 - 合并设置 + /// + [Fact] + public async Task TestResolveAsync_10() { + //传入域名格式 + var map = new Dictionary { + {"a.test.com","b"} + }; + _resolver = new DomainTenantResolver( map ); + + //设置主机名 + _mockHttpContext.Setup( t => t.Request.Host ).Returns( new HostString( "https://a.test.com" ) ); + + //设置租户域名存储器 + var mockResolver = new Mock(); + mockResolver.Setup( t => t.GetAsync() ).ReturnsAsync( new Dictionary { { "c.test.com", "c" } } ); + var container = Ioc.CreateContainer(); + container.GetServices().AddTransient( _ => mockResolver.Object ); + _mockHttpContext.Setup( t => t.RequestServices ).Returns( container.GetServiceProvider() ); + + //执行 + var result = await _resolver.ResolveAsync( _mockHttpContext.Object ); + + //验证 + Assert.Equal( "b", result ); + } +} \ No newline at end of file diff --git a/test/Util.Tenants.Tests/Resolvers/HeaderTenantResolverTest.cs b/test/Util.Tenants.Tests/Resolvers/HeaderTenantResolverTest.cs new file mode 100644 index 000000000..543a05816 --- /dev/null +++ b/test/Util.Tenants.Tests/Resolvers/HeaderTenantResolverTest.cs @@ -0,0 +1,73 @@ +using Microsoft.Extensions.Primitives; +using Util.Helpers; +using Util.Tenants.Resolvers; + +namespace Util.Tenants.Tests.Resolvers; + +/// +/// 请求头租户解析器测试 +/// +public class HeaderTenantResolverTest { + /// + /// 请求头租户解析器 + /// + private readonly HeaderTenantResolver _resolver; + /// + /// 模拟Http上下文 + /// + private readonly Mock _mockHttpContext; + + /// + /// 测试初始化 + /// + public HeaderTenantResolverTest() { + _resolver = new HeaderTenantResolver(); + _mockHttpContext = new Mock(); + } + + /// + /// 测试解析租户标识 - 默认租户键名 + /// + [Fact] + public async Task TestResolveAsync_1() { + //设置请求头 + var headers = new Dictionary { + { TenantOptions.DefaultTenantKey,"a" } + }; + _mockHttpContext.Setup( t => t.Request.Headers ).Returns( new HeaderDictionary( headers ) ); + + //设置租户配置 + var container = Ioc.CreateContainer(); + container.GetServices().Configure( t => t.IsEnabled = true ); + _mockHttpContext.Setup( t => t.RequestServices ).Returns( container.GetServiceProvider() ); + + //执行 + var result = await _resolver.ResolveAsync( _mockHttpContext.Object ); + + //验证 + Assert.Equal( "a", result ); + } + + /// + /// 测试解析租户标识 - 指定租户键名 + /// + [Fact] + public async Task TestResolveAsync_2() { + //设置请求头 + var headers = new Dictionary { + { "key","a" } + }; + _mockHttpContext.Setup( t => t.Request.Headers ).Returns( new HeaderDictionary( headers ) ); + + //设置租户键名 + var container = Ioc.CreateContainer(); + container.GetServices().Configure( t => t.TenantKey = "key" ); + _mockHttpContext.Setup( t => t.RequestServices ).Returns( container.GetServiceProvider() ); + + //执行 + var result = await _resolver.ResolveAsync( _mockHttpContext.Object ); + + //验证 + Assert.Equal( "a", result ); + } +} \ No newline at end of file diff --git a/test/Util.Tenants.Tests/Resolvers/QueryStringTenantResolverTest.cs b/test/Util.Tenants.Tests/Resolvers/QueryStringTenantResolverTest.cs new file mode 100644 index 000000000..fac22f8f0 --- /dev/null +++ b/test/Util.Tenants.Tests/Resolvers/QueryStringTenantResolverTest.cs @@ -0,0 +1,73 @@ +using Microsoft.Extensions.Primitives; +using Util.Helpers; +using Util.Tenants.Resolvers; + +namespace Util.Tenants.Tests.Resolvers; + +/// +/// 查询字符串租户解析器测试 +/// +public class QueryStringTenantResolverTest { + /// + /// 查询字符串租户解析器 + /// + private readonly QueryStringTenantResolver _resolver; + /// + /// 模拟Http上下文 + /// + private readonly Mock _mockHttpContext; + + /// + /// 测试初始化 + /// + public QueryStringTenantResolverTest() { + _resolver = new QueryStringTenantResolver(); + _mockHttpContext = new Mock(); + } + + /// + /// 测试解析租户标识 - 默认租户键名 + /// + [Fact] + public async Task TestResolveAsync_1() { + //设置查询字符串 + var query = new Dictionary { + { TenantOptions.DefaultTenantKey,"a" } + }; + _mockHttpContext.Setup( t => t.Request.Query ).Returns( new QueryCollection( query ) ); + + //设置租户配置 + var container = Ioc.CreateContainer(); + container.GetServices().Configure( t => t.IsEnabled = true ); + _mockHttpContext.Setup( t => t.RequestServices ).Returns( container.GetServiceProvider() ); + + //执行 + var result = await _resolver.ResolveAsync( _mockHttpContext.Object ); + + //验证 + Assert.Equal( "a", result ); + } + + /// + /// 测试解析租户标识 - 指定租户键名 + /// + [Fact] + public async Task TestResolveAsync_2() { + //设置查询字符串 + var query = new Dictionary { + { "key","a" } + }; + _mockHttpContext.Setup( t => t.Request.Query ).Returns( new QueryCollection( query ) ); + + //设置租户键名 + var container = Ioc.CreateContainer(); + container.GetServices().Configure( t => t.TenantKey = "key" ); + _mockHttpContext.Setup( t => t.RequestServices ).Returns( container.GetServiceProvider() ); + + //执行 + var result = await _resolver.ResolveAsync( _mockHttpContext.Object ); + + //验证 + Assert.Equal( "a", result ); + } +} \ No newline at end of file diff --git a/test/Util.Tenants.Tests/Resolvers/TenantResolverCollectionTest.cs b/test/Util.Tenants.Tests/Resolvers/TenantResolverCollectionTest.cs new file mode 100644 index 000000000..d5a885a6e --- /dev/null +++ b/test/Util.Tenants.Tests/Resolvers/TenantResolverCollectionTest.cs @@ -0,0 +1,134 @@ +using Util.Tenants.Tests.Samples; + +namespace Util.Tenants.Tests.Resolvers; + +/// +/// 租户解析器集合测试 +/// +public class TenantResolverCollectionTest { + /// + /// 租户解析器集合 + /// + private readonly TenantResolverCollection _resolvers; + + /// + /// 测试初始化 + /// + public TenantResolverCollectionTest() { + _resolvers = new TenantResolverCollection(); + } + + /// + /// 测试添加租户解析器 + /// + [Fact] + public void TestAddTenantResolver_1() { + _resolvers.Add( new TestTenantResolver() ); + var result = _resolvers.GetResolvers(); + Assert.Single( result ); + } + + /// + /// 测试添加租户解析器 - 重复检测 + /// + [Fact] + public void TestAddTenantResolver_2() { + _resolvers.Add( new TestTenantResolver() ); + _resolvers.Add( new TestTenantResolver() ); + var result = _resolvers.GetResolvers(); + Assert.Single( result ); + } + + /// + /// 测试添加租户解析器 - 指定键 + /// + [Fact] + public void TestAddTenantResolver_3() { + _resolvers.Add( "a", new TestTenantResolver() ); + _resolvers.Add( "b", new TestTenantResolver() ); + var result = _resolvers.GetResolvers(); + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试添加租户解析器集合 + /// + [Fact] + public void TestAddTenantResolvers() { + _resolvers.Add( new List { new TestTenantResolver(), new Test2TenantResolver() } ); + var result = _resolvers.GetResolvers(); + Assert.Equal( 2, result.Count ); + } + + /// + /// 测试移除租户解析器 - 按类型移除 + /// + [Fact] + public void TestRemoveTenantResolver_1() { + _resolvers.Add( new TestTenantResolver() ); + _resolvers.Remove(); + var result = _resolvers.GetResolvers(); + Assert.Empty( result ); + } + + /// + /// 测试移除租户解析器 - 按键名移除 + /// + [Fact] + public void TestRemoveTenantResolver_2() { + _resolvers.Add( "a", new TestTenantResolver() ); + _resolvers.Add( "b", new Test2TenantResolver() ); + _resolvers.Remove( "a" ); + var result = _resolvers.GetResolvers(); + Assert.Single( result ); + Assert.Equal( typeof( Test2TenantResolver ), result[0].GetType() ); + } + + /// + /// 测试获取租户解析器 - 按类型获取 + /// + [Fact] + public void TestGetResolver_1() { + _resolvers.Add( new TestTenantResolver() ); + _resolvers.Add( new Test2TenantResolver() ); + var result = _resolvers.GetResolver(); + Assert.Equal( typeof( Test2TenantResolver ), result.GetType() ); + } + + /// + /// 测试获取租户解析器 - 按键获取 + /// + [Fact] + public void TestGetResolver_2() { + _resolvers.Add( "a", new TestTenantResolver() ); + _resolvers.Add( "b", new Test2TenantResolver() ); + var result = _resolvers.GetResolver( "b" ); + Assert.Equal( typeof( Test2TenantResolver ), result.GetType() ); + } + + /// + /// 测试获取租户解析器列表 - 获取全部 + /// + [Fact] + public void TestGetResolvers_1() { + _resolvers.Add( "a", new TestTenantResolver() ); + _resolvers.Add( new Test2TenantResolver() ); + _resolvers.Add( "b", new Test2TenantResolver() ); + var result = _resolvers.GetResolvers(); + Assert.Equal( 3, result.Count ); + } + + /// + /// 测试获取租户解析器列表 - 按类型获取 + /// + [Fact] + public void TestGetResolvers_2() { + _resolvers.Add( "a", new TestTenantResolver() ); + _resolvers.Add( new Test2TenantResolver() ); + _resolvers.Add( "b", new Test2TenantResolver() ); + var result = _resolvers.GetResolvers(); + Assert.Equal( 2, result.Count ); + Assert.Equal( typeof( Test2TenantResolver ), result[0].GetType() ); + Assert.Equal( typeof( Test2TenantResolver ), result[1].GetType() ); + } +} \ No newline at end of file diff --git a/test/Util.Tenants.Tests/Samples/TestSession.cs b/test/Util.Tenants.Tests/Samples/TestSession.cs new file mode 100644 index 000000000..2c3a6d3d7 --- /dev/null +++ b/test/Util.Tenants.Tests/Samples/TestSession.cs @@ -0,0 +1,22 @@ +using ISession = Util.Sessions.ISession; + +namespace Util.Tenants.Tests.Samples; + +/// +/// 测试用户会话 +/// +public class TestSession : ISession { + /// + /// 初始化测试用户会话 + /// + public TestSession( bool isAuthenticated, string userId, string tenantId ) { + IsAuthenticated = isAuthenticated; + UserId = userId; + TenantId = tenantId; + } + + public IServiceProvider ServiceProvider { get; } + public bool IsAuthenticated { get; } + public string UserId { get; } + public string TenantId { get; } +} \ No newline at end of file diff --git a/test/Util.Tenants.Tests/Samples/TestTenantResolver.cs b/test/Util.Tenants.Tests/Samples/TestTenantResolver.cs new file mode 100644 index 000000000..b8ae82c56 --- /dev/null +++ b/test/Util.Tenants.Tests/Samples/TestTenantResolver.cs @@ -0,0 +1,63 @@ +using Microsoft.AspNetCore.Http; + +namespace Util.Tenants.Tests.Samples; + +/// +/// 测试租户解析器 +/// +public class TestTenantResolver : ITenantResolver { + /// + /// 初始化测试租户解析器 + /// + public TestTenantResolver() { + Priority = 1; + } + + /// + public Task ResolveAsync( HttpContext context ) { + return Task.FromResult( "Test" ); + } + + /// + public int Priority { get; set; } +} + +/// +/// 测试租户解析器2 +/// +public class Test2TenantResolver : ITenantResolver { + /// + /// 初始化测试租户解析器 + /// + public Test2TenantResolver() { + Priority = 2; + } + + /// + public Task ResolveAsync( HttpContext context ) { + return Task.FromResult( "Test2" ); + } + + /// + public int Priority { get; set; } +} + +/// +/// 测试租户解析器3 +/// +public class Test3TenantResolver : ITenantResolver { + /// + /// 初始化测试租户解析器 + /// + public Test3TenantResolver() { + Priority = 3; + } + + /// + public Task ResolveAsync( HttpContext context ) { + return Task.FromResult( "" ); + } + + /// + public int Priority { get; set; } +} \ No newline at end of file diff --git a/test/Util.Tenants.Tests/Usings.cs b/test/Util.Tenants.Tests/Usings.cs new file mode 100644 index 000000000..837246791 --- /dev/null +++ b/test/Util.Tenants.Tests/Usings.cs @@ -0,0 +1,12 @@ +global using System; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Linq; +global using System.Threading; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Options; +global using Microsoft.AspNetCore.Http; +global using Moq; +global using Util.Security.Authorization; +global using Util.Sessions; +global using Xunit; \ No newline at end of file diff --git a/test/Util.Tenants.Tests/Util.Tenants.Tests.csproj b/test/Util.Tenants.Tests/Util.Tenants.Tests.csproj new file mode 100644 index 000000000..a94711025 --- /dev/null +++ b/test/Util.Tenants.Tests/Util.Tenants.Tests.csproj @@ -0,0 +1,26 @@ + + + + $(NetTargetFramework) + false + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.TestShare.MySql/EntityTypeConfigurations/CustomerConfiguration.cs b/test/Util.TestShare.MySql/EntityTypeConfigurations/CustomerConfiguration.cs new file mode 100644 index 000000000..5a2dbd713 --- /dev/null +++ b/test/Util.TestShare.MySql/EntityTypeConfigurations/CustomerConfiguration.cs @@ -0,0 +1,94 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Configs; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 客户类型配置 + /// + public class CustomerConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + InitData( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Customer",t => t.HasComment( "客户" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "CustomerId" ) + .HasComment( "客户标识" ) + .ValueGeneratedOnAdd(); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Code ) + .HasColumnName( "Code" ) + .HasComment( "编码" ); + builder.Property( t => t.Name ) + .HasColumnName( "Name" ) + .HasComment( "姓名" ); + builder.Property( t => t.Nickname ) + .HasColumnName( "Nickname" ) + .HasComment( "昵称" ); + builder.Property( t => t.Gender ) + .HasColumnName( "Gender" ) + .HasComment( "性别" ); + builder.Property( t => t.Birthday ) + .HasColumnName( "Birthday" ) + .HasComment( "出生日期" ); + builder.Property( t => t.Nation ) + .HasColumnName( "Nation" ) + .HasComment( "民族" ); + builder.Property( t => t.Phone ) + .HasColumnName( "Phone" ) + .HasComment( "手机号" ); + builder.Property( t => t.Email ) + .HasColumnName( "Email" ) + .HasComment( "电子邮件" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + builder.Property( t => t.IsDeleted ) + .HasDefaultValue( false ); + } + + /// + /// 初始化数据 + /// + private void InitData( EntityTypeBuilder builder ) { + var customer = new Customer( 1 ) { + Code = TestConfig.Value, + Birthday = TestConfig.DateTimeValue + }; + builder.HasData( customer ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.MySql/EntityTypeConfigurations/OperationLogConfiguration.cs b/test/Util.TestShare.MySql/EntityTypeConfigurations/OperationLogConfiguration.cs new file mode 100644 index 000000000..b216e18b4 --- /dev/null +++ b/test/Util.TestShare.MySql/EntityTypeConfigurations/OperationLogConfiguration.cs @@ -0,0 +1,55 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 操作日志类型配置 + /// + public class OperationLogConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "OperationLog", t => t.HasComment( "操作日志" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "LogId" ) + .HasComment( "操作标识" ) + .ValueGeneratedOnAdd(); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.LogName ) + .HasColumnName( "LogName" ) + .HasComment( "日志名称" ); + builder.Property( t => t.OperationTime ) + .HasColumnName( "OperationTime" ) + .HasComment( "操作时间" ); + builder.Property( t => t.Caption ) + .HasColumnName( "Caption" ) + .HasComment( "标题" ); + builder.Property( t => t.Content ) + .HasColumnName( "Content" ) + .HasComment( "内容" ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.MySql/EntityTypeConfigurations/OrderConfiguration.cs b/test/Util.TestShare.MySql/EntityTypeConfigurations/OrderConfiguration.cs new file mode 100644 index 000000000..2e974caff --- /dev/null +++ b/test/Util.TestShare.MySql/EntityTypeConfigurations/OrderConfiguration.cs @@ -0,0 +1,65 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Configs; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 订单类型配置 + /// + public class OrderConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Order",t => t.HasComment( "订单" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasMaxLength( 50 ) + .HasColumnName( "OrderId" ) + .HasComment( "订单标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.CustomerId ) + .HasColumnName( "CustomerId" ) + .HasComment( "客户标识" ); + builder.Property( t => t.CustomerName ) + .HasColumnName( "CustomerName" ) + .HasComment( "客户名称" ); + builder.Property( t => t.Amount ) + .HasColumnName( "Amount" ) + .HasComment( "金额" ) + .HasPrecision(15,2) + .HasDefaultValue(0); + builder.Property( t => t.PlaceOrderTime ) + .HasColumnName( "PlaceOrderTime" ) + .HasComment( "下单时间" ) + .HasDefaultValue( TestConfig.DateTimeValue ); + builder.Property( t => t.State ) + .HasColumnName( "State" ) + .HasComment( "订单状态" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.IsDeleted ) + .HasDefaultValue( false ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.MySql/EntityTypeConfigurations/OrderItemConfiguration.cs b/test/Util.TestShare.MySql/EntityTypeConfigurations/OrderItemConfiguration.cs new file mode 100644 index 000000000..ef3ff8173 --- /dev/null +++ b/test/Util.TestShare.MySql/EntityTypeConfigurations/OrderItemConfiguration.cs @@ -0,0 +1,70 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 订单明细类型配置 + /// + public class OrderItemConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + ConfigAssociations( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "OrderItem", t => t.HasComment( "订单明细" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "OrderItemId" ) + .HasComment( "订单明细标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.OrderId ) + .HasColumnName( "OrderId" ) + .HasMaxLength( 50 ) + .HasComment( "订单标识" ); + builder.Property( t => t.ProductId ) + .HasColumnName( "ProductId" ) + .HasComment( "产品标识" ); + builder.Property( t => t.ProductName ) + .HasColumnName( "ProductName" ) + .HasComment( "产品名称" ); + builder.Property( t => t.Price ) + .HasColumnName( "Price" ) + .HasComment( "单价" ) + .HasPrecision(12,2); + builder.Property( t => t.Quantity ) + .HasColumnName( "Quantity" ) + .HasComment( "数量" ) + .HasDefaultValue( 1 ); + } + + /// + /// 映射导航属性 + /// + private void ConfigAssociations( EntityTypeBuilder builder ) { + builder.HasOne( t => t.Order ) + .WithMany( t => t.OrderItems ) + .HasForeignKey( t => t.OrderId ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.MySql/EntityTypeConfigurations/PostConfiguration.cs b/test/Util.TestShare.MySql/EntityTypeConfigurations/PostConfiguration.cs new file mode 100644 index 000000000..2c2084eaf --- /dev/null +++ b/test/Util.TestShare.MySql/EntityTypeConfigurations/PostConfiguration.cs @@ -0,0 +1,63 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 贴子类型配置 + /// + public class PostConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Post", t => t.HasComment( "贴子" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "PostId" ) + .HasComment( "贴子标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Title ) + .HasColumnName( "Title" ) + .HasComment( "标题" ); + builder.Property( t => t.Content ) + .HasColumnName( "Content" ) + .HasComment( "内容" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + builder.HasMany( t => t.Tags ) + .WithMany( t => t.Posts ) + .UsingEntity( t => t.ToTable( "PostTag2" ) ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.MySql/EntityTypeConfigurations/ProductConfiguration.cs b/test/Util.TestShare.MySql/EntityTypeConfigurations/ProductConfiguration.cs new file mode 100644 index 000000000..377c5fa3b --- /dev/null +++ b/test/Util.TestShare.MySql/EntityTypeConfigurations/ProductConfiguration.cs @@ -0,0 +1,102 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Configs; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 产品类型配置 + /// + public class ProductConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + InitData( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Product", t => t.HasComment( "产品" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "ProductId" ) + .HasComment( "产品标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Code ) + .HasColumnName( "Code" ) + .HasComment( "产品编码" ); + builder.Property( t => t.Name ) + .HasColumnName( "Name" ) + .HasComment( "产品名称" ); + builder.Property( t => t.Price ) + .HasColumnName( "Price" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ) + .HasPrecision( 12, 2 ); + builder.Property( t => t.IntPrice ) + .HasColumnName( "IntPrice" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.LongPrice ) + .HasColumnName( "LongPrice" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.FloatPrice ) + .HasColumnName( "FloatPrice" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.Description ) + .HasColumnName( "Description" ) + .HasComment( "描述" ); + builder.Property( t => t.Enabled ) + .HasColumnName( "Enabled" ) + .HasDefaultValue( TestConfig.BoolValue ) + .HasComment( "启用" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + builder.Property( t => t.IsDeleted ) + .HasDefaultValue( false ); + } + + /// + /// 初始化数据 + /// + private void InitData( EntityTypeBuilder builder ) { + var product = new Product( TestConfig.Id ) { + Code = TestConfig.Value, + Price = TestConfig.DecimalValue, + IntPrice = TestConfig.IntValue, + LongPrice = TestConfig.LongValue, + FloatPrice = TestConfig.FloatValue + }; + builder.HasData( product ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.MySql/EntityTypeConfigurations/TagConfiguration.cs b/test/Util.TestShare.MySql/EntityTypeConfigurations/TagConfiguration.cs new file mode 100644 index 000000000..8a22438ca --- /dev/null +++ b/test/Util.TestShare.MySql/EntityTypeConfigurations/TagConfiguration.cs @@ -0,0 +1,57 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 标签类型配置 + /// + public class TagConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Tag", t => t.HasComment( "标签" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "TagId" ) + .HasComment( "标签标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Name ) + .HasColumnName( "Name" ) + .HasComment( "标签名称" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.MySql/Repositories/CustomerRepository.cs b/test/Util.TestShare.MySql/Repositories/CustomerRepository.cs new file mode 100644 index 000000000..f73cdd0eb --- /dev/null +++ b/test/Util.TestShare.MySql/Repositories/CustomerRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 客户仓储 + /// + public class CustomerRepository : RepositoryBase,ICustomerRepository { + /// + /// 初始化客户仓储 + /// + /// 工作单元 + public CustomerRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.MySql/Repositories/OperationLogRepository.cs b/test/Util.TestShare.MySql/Repositories/OperationLogRepository.cs new file mode 100644 index 000000000..112510a83 --- /dev/null +++ b/test/Util.TestShare.MySql/Repositories/OperationLogRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 操作日志仓储 + /// + public class OperationLogRepository : RepositoryBase,IOperationLogRepository { + /// + /// 初始化操作日志仓储 + /// + /// 工作单元 + public OperationLogRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.MySql/Repositories/OrderRepository.cs b/test/Util.TestShare.MySql/Repositories/OrderRepository.cs new file mode 100644 index 000000000..324767646 --- /dev/null +++ b/test/Util.TestShare.MySql/Repositories/OrderRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 订单仓储 + /// + public class OrderRepository : RepositoryBase,IOrderRepository { + /// + /// 初始化订单仓储 + /// + /// 工作单元 + public OrderRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.MySql/Repositories/PostRepository.cs b/test/Util.TestShare.MySql/Repositories/PostRepository.cs new file mode 100644 index 000000000..ca71fe2e6 --- /dev/null +++ b/test/Util.TestShare.MySql/Repositories/PostRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 贴子仓储 + /// + public class PostRepository : RepositoryBase,IPostRepository { + /// + /// 初始化贴子仓储 + /// + /// 工作单元 + public PostRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.MySql/Repositories/ProductRepository.cs b/test/Util.TestShare.MySql/Repositories/ProductRepository.cs new file mode 100644 index 000000000..bd3a30f89 --- /dev/null +++ b/test/Util.TestShare.MySql/Repositories/ProductRepository.cs @@ -0,0 +1,25 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 产品仓储 + /// + public class ProductRepository : RepositoryBase,IProductRepository { + /// + /// 初始化产品仓储 + /// + /// 工作单元 + public ProductRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + + /// + /// 更新产品 + /// + /// 产品 + public void UpdateProduct( Product product ) { + UnitOfWork.Update( product ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.MySql/Repositories/TagRepository.cs b/test/Util.TestShare.MySql/Repositories/TagRepository.cs new file mode 100644 index 000000000..cfad8f0a2 --- /dev/null +++ b/test/Util.TestShare.MySql/Repositories/TagRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 标签仓储 + /// + public class TagRepository : RepositoryBase,ITagRepository { + /// + /// 初始化标签仓储 + /// + /// 工作单元 + public TagRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.MySql/UnitOfWorks/MySqlUnitOfWork.cs b/test/Util.TestShare.MySql/UnitOfWorks/MySqlUnitOfWork.cs new file mode 100644 index 000000000..9a43c42e9 --- /dev/null +++ b/test/Util.TestShare.MySql/UnitOfWorks/MySqlUnitOfWork.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Util.Data.EntityFrameworkCore; +using Util.Sessions; + +namespace Util.Tests.UnitOfWorks { + /// + /// MySql工作单元 + /// + public class MySqlUnitOfWork : MySqlUnitOfWorkBase, ITestUnitOfWork { + /// + /// 初始化工作单元 + /// + /// 服务提供器 + /// 配置项 + public MySqlUnitOfWork( IServiceProvider serviceProvider ,DbContextOptions options) : base( serviceProvider, options ) { + } + + /// + /// 设置用户会话 + /// + public void SetSession( ISession session ) { + Session = session; + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.MySql/Util.TestShare.MySql.csproj b/test/Util.TestShare.MySql/Util.TestShare.MySql.csproj new file mode 100644 index 000000000..0da89f303 --- /dev/null +++ b/test/Util.TestShare.MySql/Util.TestShare.MySql.csproj @@ -0,0 +1,13 @@ + + + + $(NetTargetFramework) + false + Util.Tests + + + + + + + \ No newline at end of file diff --git a/test/Util.TestShare.Oracle/EntityTypeConfigurations/CustomerConfiguration.cs b/test/Util.TestShare.Oracle/EntityTypeConfigurations/CustomerConfiguration.cs new file mode 100644 index 000000000..5a2dbd713 --- /dev/null +++ b/test/Util.TestShare.Oracle/EntityTypeConfigurations/CustomerConfiguration.cs @@ -0,0 +1,94 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Configs; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 客户类型配置 + /// + public class CustomerConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + InitData( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Customer",t => t.HasComment( "客户" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "CustomerId" ) + .HasComment( "客户标识" ) + .ValueGeneratedOnAdd(); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Code ) + .HasColumnName( "Code" ) + .HasComment( "编码" ); + builder.Property( t => t.Name ) + .HasColumnName( "Name" ) + .HasComment( "姓名" ); + builder.Property( t => t.Nickname ) + .HasColumnName( "Nickname" ) + .HasComment( "昵称" ); + builder.Property( t => t.Gender ) + .HasColumnName( "Gender" ) + .HasComment( "性别" ); + builder.Property( t => t.Birthday ) + .HasColumnName( "Birthday" ) + .HasComment( "出生日期" ); + builder.Property( t => t.Nation ) + .HasColumnName( "Nation" ) + .HasComment( "民族" ); + builder.Property( t => t.Phone ) + .HasColumnName( "Phone" ) + .HasComment( "手机号" ); + builder.Property( t => t.Email ) + .HasColumnName( "Email" ) + .HasComment( "电子邮件" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + builder.Property( t => t.IsDeleted ) + .HasDefaultValue( false ); + } + + /// + /// 初始化数据 + /// + private void InitData( EntityTypeBuilder builder ) { + var customer = new Customer( 1 ) { + Code = TestConfig.Value, + Birthday = TestConfig.DateTimeValue + }; + builder.HasData( customer ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Oracle/EntityTypeConfigurations/OperationLogConfiguration.cs b/test/Util.TestShare.Oracle/EntityTypeConfigurations/OperationLogConfiguration.cs new file mode 100644 index 000000000..878c2fd0e --- /dev/null +++ b/test/Util.TestShare.Oracle/EntityTypeConfigurations/OperationLogConfiguration.cs @@ -0,0 +1,55 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 操作日志类型配置 + /// + public class OperationLogConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "OperationLog",t => t.HasComment( "操作日志" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "LogId" ) + .HasComment( "操作标识" ) + .ValueGeneratedOnAdd(); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.LogName ) + .HasColumnName( "LogName" ) + .HasComment( "日志名称" ); + builder.Property( t => t.OperationTime ) + .HasColumnName( "OperationTime" ) + .HasComment( "操作时间" ); + builder.Property( t => t.Caption ) + .HasColumnName( "Caption" ) + .HasComment( "标题" ); + builder.Property( t => t.Content ) + .HasColumnName( "Content" ) + .HasComment( "内容" ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Oracle/EntityTypeConfigurations/OrderConfiguration.cs b/test/Util.TestShare.Oracle/EntityTypeConfigurations/OrderConfiguration.cs new file mode 100644 index 000000000..2e974caff --- /dev/null +++ b/test/Util.TestShare.Oracle/EntityTypeConfigurations/OrderConfiguration.cs @@ -0,0 +1,65 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Configs; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 订单类型配置 + /// + public class OrderConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Order",t => t.HasComment( "订单" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasMaxLength( 50 ) + .HasColumnName( "OrderId" ) + .HasComment( "订单标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.CustomerId ) + .HasColumnName( "CustomerId" ) + .HasComment( "客户标识" ); + builder.Property( t => t.CustomerName ) + .HasColumnName( "CustomerName" ) + .HasComment( "客户名称" ); + builder.Property( t => t.Amount ) + .HasColumnName( "Amount" ) + .HasComment( "金额" ) + .HasPrecision(15,2) + .HasDefaultValue(0); + builder.Property( t => t.PlaceOrderTime ) + .HasColumnName( "PlaceOrderTime" ) + .HasComment( "下单时间" ) + .HasDefaultValue( TestConfig.DateTimeValue ); + builder.Property( t => t.State ) + .HasColumnName( "State" ) + .HasComment( "订单状态" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.IsDeleted ) + .HasDefaultValue( false ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Oracle/EntityTypeConfigurations/OrderItemConfiguration.cs b/test/Util.TestShare.Oracle/EntityTypeConfigurations/OrderItemConfiguration.cs new file mode 100644 index 000000000..aa54d6e8b --- /dev/null +++ b/test/Util.TestShare.Oracle/EntityTypeConfigurations/OrderItemConfiguration.cs @@ -0,0 +1,70 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 订单明细类型配置 + /// + public class OrderItemConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + ConfigAssociations( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "OrderItem",t => t.HasComment( "订单明细" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "OrderItemId" ) + .HasComment( "订单明细标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.OrderId ) + .HasColumnName( "OrderId" ) + .HasMaxLength( 50 ) + .HasComment( "订单标识" ); + builder.Property( t => t.ProductId ) + .HasColumnName( "ProductId" ) + .HasComment( "产品标识" ); + builder.Property( t => t.ProductName ) + .HasColumnName( "ProductName" ) + .HasComment( "产品名称" ); + builder.Property( t => t.Price ) + .HasColumnName( "Price" ) + .HasComment( "单价" ) + .HasPrecision(12,2); + builder.Property( t => t.Quantity ) + .HasColumnName( "Quantity" ) + .HasComment( "数量" ) + .HasDefaultValue( 1 ); + } + + /// + /// 映射导航属性 + /// + private void ConfigAssociations( EntityTypeBuilder builder ) { + builder.HasOne( t => t.Order ) + .WithMany( t => t.OrderItems ) + .HasForeignKey( t => t.OrderId ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Oracle/EntityTypeConfigurations/PostConfiguration.cs b/test/Util.TestShare.Oracle/EntityTypeConfigurations/PostConfiguration.cs new file mode 100644 index 000000000..1475e06fa --- /dev/null +++ b/test/Util.TestShare.Oracle/EntityTypeConfigurations/PostConfiguration.cs @@ -0,0 +1,63 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 贴子类型配置 + /// + public class PostConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Post",t => t.HasComment( "贴子" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "PostId" ) + .HasComment( "贴子标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Title ) + .HasColumnName( "Title" ) + .HasComment( "标题" ); + builder.Property( t => t.Content ) + .HasColumnName( "Content" ) + .HasComment( "内容" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + builder.HasMany( t => t.Tags ) + .WithMany( t => t.Posts ) + .UsingEntity( t => t.ToTable( "PostTag2" ) ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Oracle/EntityTypeConfigurations/ProductConfiguration.cs b/test/Util.TestShare.Oracle/EntityTypeConfigurations/ProductConfiguration.cs new file mode 100644 index 000000000..9481d67d8 --- /dev/null +++ b/test/Util.TestShare.Oracle/EntityTypeConfigurations/ProductConfiguration.cs @@ -0,0 +1,102 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Configs; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 产品类型配置 + /// + public class ProductConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + InitData( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Product",t => t.HasComment( "产品" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "ProductId" ) + .HasComment( "产品标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Code ) + .HasColumnName( "Code" ) + .HasComment( "产品编码" ); + builder.Property( t => t.Name ) + .HasColumnName( "Name" ) + .HasComment( "产品名称" ); + builder.Property( t => t.Price ) + .HasColumnName( "Price" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ) + .HasPrecision( 12, 2 ); + builder.Property( t => t.IntPrice ) + .HasColumnName( "IntPrice" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.LongPrice ) + .HasColumnName( "LongPrice" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.FloatPrice ) + .HasColumnName( "FloatPrice" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.Description ) + .HasColumnName( "Description" ) + .HasComment( "描述" ); + builder.Property( t => t.Enabled ) + .HasColumnName( "Enabled" ) + .HasDefaultValue( TestConfig.BoolValue ) + .HasComment( "启用" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + builder.Property( t => t.IsDeleted ) + .HasDefaultValue( false ); + } + + /// + /// 初始化数据 + /// + private void InitData( EntityTypeBuilder builder ) { + var product = new Product( TestConfig.Id ) { + Code = TestConfig.Value, + Price = TestConfig.DecimalValue, + IntPrice = TestConfig.IntValue, + LongPrice = TestConfig.LongValue, + FloatPrice = TestConfig.FloatValue + }; + builder.HasData( product ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Oracle/EntityTypeConfigurations/TagConfiguration.cs b/test/Util.TestShare.Oracle/EntityTypeConfigurations/TagConfiguration.cs new file mode 100644 index 000000000..933ab60ba --- /dev/null +++ b/test/Util.TestShare.Oracle/EntityTypeConfigurations/TagConfiguration.cs @@ -0,0 +1,57 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 标签类型配置 + /// + public class TagConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Tag",t => t.HasComment( "标签" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "TagId" ) + .HasComment( "标签标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Name ) + .HasColumnName( "Name" ) + .HasComment( "标签名称" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Oracle/Repositories/CustomerRepository.cs b/test/Util.TestShare.Oracle/Repositories/CustomerRepository.cs new file mode 100644 index 000000000..f73cdd0eb --- /dev/null +++ b/test/Util.TestShare.Oracle/Repositories/CustomerRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 客户仓储 + /// + public class CustomerRepository : RepositoryBase,ICustomerRepository { + /// + /// 初始化客户仓储 + /// + /// 工作单元 + public CustomerRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Oracle/Repositories/OperationLogRepository.cs b/test/Util.TestShare.Oracle/Repositories/OperationLogRepository.cs new file mode 100644 index 000000000..112510a83 --- /dev/null +++ b/test/Util.TestShare.Oracle/Repositories/OperationLogRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 操作日志仓储 + /// + public class OperationLogRepository : RepositoryBase,IOperationLogRepository { + /// + /// 初始化操作日志仓储 + /// + /// 工作单元 + public OperationLogRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Oracle/Repositories/OrderRepository.cs b/test/Util.TestShare.Oracle/Repositories/OrderRepository.cs new file mode 100644 index 000000000..324767646 --- /dev/null +++ b/test/Util.TestShare.Oracle/Repositories/OrderRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 订单仓储 + /// + public class OrderRepository : RepositoryBase,IOrderRepository { + /// + /// 初始化订单仓储 + /// + /// 工作单元 + public OrderRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Oracle/Repositories/PostRepository.cs b/test/Util.TestShare.Oracle/Repositories/PostRepository.cs new file mode 100644 index 000000000..ca71fe2e6 --- /dev/null +++ b/test/Util.TestShare.Oracle/Repositories/PostRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 贴子仓储 + /// + public class PostRepository : RepositoryBase,IPostRepository { + /// + /// 初始化贴子仓储 + /// + /// 工作单元 + public PostRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Oracle/Repositories/ProductRepository.cs b/test/Util.TestShare.Oracle/Repositories/ProductRepository.cs new file mode 100644 index 000000000..bd3a30f89 --- /dev/null +++ b/test/Util.TestShare.Oracle/Repositories/ProductRepository.cs @@ -0,0 +1,25 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 产品仓储 + /// + public class ProductRepository : RepositoryBase,IProductRepository { + /// + /// 初始化产品仓储 + /// + /// 工作单元 + public ProductRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + + /// + /// 更新产品 + /// + /// 产品 + public void UpdateProduct( Product product ) { + UnitOfWork.Update( product ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Oracle/Repositories/TagRepository.cs b/test/Util.TestShare.Oracle/Repositories/TagRepository.cs new file mode 100644 index 000000000..cfad8f0a2 --- /dev/null +++ b/test/Util.TestShare.Oracle/Repositories/TagRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 标签仓储 + /// + public class TagRepository : RepositoryBase,ITagRepository { + /// + /// 初始化标签仓储 + /// + /// 工作单元 + public TagRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Oracle/UnitOfWorks/OracleUnitOfWork.cs b/test/Util.TestShare.Oracle/UnitOfWorks/OracleUnitOfWork.cs new file mode 100644 index 000000000..ce5871faf --- /dev/null +++ b/test/Util.TestShare.Oracle/UnitOfWorks/OracleUnitOfWork.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Util.Data.EntityFrameworkCore; +using Util.Sessions; + +namespace Util.Tests.UnitOfWorks { + /// + /// Oracle工作单元 + /// + public class OracleUnitOfWork : OracleUnitOfWorkBase, ITestUnitOfWork { + /// + /// 初始化工作单元 + /// + /// 服务提供器 + /// 配置项 + public OracleUnitOfWork( IServiceProvider serviceProvider, DbContextOptions options ) : base( serviceProvider, options ) { + } + + /// + /// 设置用户会话 + /// + public void SetSession( ISession session ) { + Session = session; + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Oracle/Util.TestShare.Oracle.csproj b/test/Util.TestShare.Oracle/Util.TestShare.Oracle.csproj new file mode 100644 index 000000000..7c4e965bb --- /dev/null +++ b/test/Util.TestShare.Oracle/Util.TestShare.Oracle.csproj @@ -0,0 +1,13 @@ + + + + $(NetTargetFramework) + false + Util.Tests + + + + + + + \ No newline at end of file diff --git a/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/CustomerConfiguration.cs b/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/CustomerConfiguration.cs new file mode 100644 index 000000000..fcc91504c --- /dev/null +++ b/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/CustomerConfiguration.cs @@ -0,0 +1,94 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Configs; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 客户类型配置 + /// + public class CustomerConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + InitData( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Customer", "Customers", t => t.HasComment( "客户" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "CustomerId" ) + .HasComment( "客户标识" ) + .ValueGeneratedOnAdd(); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Code ) + .HasColumnName( "Code" ) + .HasComment( "编码" ); + builder.Property( t => t.Name ) + .HasColumnName( "Name" ) + .HasComment( "姓名" ); + builder.Property( t => t.Nickname ) + .HasColumnName( "Nickname" ) + .HasComment( "昵称" ); + builder.Property( t => t.Gender ) + .HasColumnName( "Gender" ) + .HasComment( "性别" ); + builder.Property( t => t.Birthday ) + .HasColumnName( "Birthday" ) + .HasComment( "出生日期" ); + builder.Property( t => t.Nation ) + .HasColumnName( "Nation" ) + .HasComment( "民族" ); + builder.Property( t => t.Phone ) + .HasColumnName( "Phone" ) + .HasComment( "手机号" ); + builder.Property( t => t.Email ) + .HasColumnName( "Email" ) + .HasComment( "电子邮件" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + builder.Property( t => t.IsDeleted ) + .HasDefaultValue( false ); + } + + /// + /// 初始化数据 + /// + private void InitData( EntityTypeBuilder builder ) { + var customer = new Customer( 1 ) { + Code = TestConfig.Value, + Birthday = TestConfig.DateTimeValue + }; + builder.HasData( customer ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/OperationLogConfiguration.cs b/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/OperationLogConfiguration.cs new file mode 100644 index 000000000..7c18950e6 --- /dev/null +++ b/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/OperationLogConfiguration.cs @@ -0,0 +1,55 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 操作日志类型配置 + /// + public class OperationLogConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "OperationLog", "Systems",t => t.HasComment( "操作日志" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "LogId" ) + .HasComment( "操作标识" ) + .ValueGeneratedOnAdd(); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.LogName ) + .HasColumnName( "LogName" ) + .HasComment( "日志名称" ); + builder.Property( t => t.OperationTime ) + .HasColumnName( "OperationTime" ) + .HasComment( "操作时间" ); + builder.Property( t => t.Caption ) + .HasColumnName( "Caption" ) + .HasComment( "标题" ); + builder.Property( t => t.Content ) + .HasColumnName( "Content" ) + .HasComment( "内容" ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/OrderConfiguration.cs b/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/OrderConfiguration.cs new file mode 100644 index 000000000..473943760 --- /dev/null +++ b/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/OrderConfiguration.cs @@ -0,0 +1,64 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Configs; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 订单类型配置 + /// + public class OrderConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Order", "Sales",t => t.HasComment( "订单" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasMaxLength( 50 ) + .HasColumnName( "OrderId" ) + .HasComment( "订单标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.CustomerId ) + .HasColumnName( "CustomerId" ) + .HasComment( "客户标识" ); + builder.Property( t => t.CustomerName ) + .HasColumnName( "CustomerName" ) + .HasComment( "客户名称" ); + builder.Property( t => t.Amount ) + .HasColumnName( "Amount" ) + .HasComment( "金额" ) + .HasPrecision(15,2) + .HasDefaultValue( 0 ); + builder.Property( t => t.PlaceOrderTime ) + .HasColumnName( "PlaceOrderTime" ) + .HasComment( "下单时间" ); + builder.Property( t => t.State ) + .HasColumnName( "State" ) + .HasComment( "订单状态" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.IsDeleted ) + .HasDefaultValue( false ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/OrderItemConfiguration.cs b/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/OrderItemConfiguration.cs new file mode 100644 index 000000000..f8728d1f2 --- /dev/null +++ b/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/OrderItemConfiguration.cs @@ -0,0 +1,70 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 订单明细类型配置 + /// + public class OrderItemConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + ConfigAssociations( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "OrderItem", "Sales" ).HasComment( "订单明细" ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "OrderItemId" ) + .HasComment( "订单明细标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.OrderId ) + .HasColumnName( "OrderId" ) + .HasMaxLength( 50 ) + .HasComment( "订单标识" ); + builder.Property( t => t.ProductId ) + .HasColumnName( "ProductId" ) + .HasComment( "产品标识" ); + builder.Property( t => t.ProductName ) + .HasColumnName( "ProductName" ) + .HasComment( "产品名称" ); + builder.Property( t => t.Price ) + .HasColumnName( "Price" ) + .HasComment( "单价" ) + .HasPrecision(12,2); + builder.Property( t => t.Quantity ) + .HasColumnName( "Quantity" ) + .HasComment( "数量" ) + .HasDefaultValue( 1 ); + } + + /// + /// 映射导航属性 + /// + private void ConfigAssociations( EntityTypeBuilder builder ) { + builder.HasOne( t => t.Order ) + .WithMany( t => t.OrderItems ) + .HasForeignKey( t => t.OrderId ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/PostConfiguration.cs b/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/PostConfiguration.cs new file mode 100644 index 000000000..79f200480 --- /dev/null +++ b/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/PostConfiguration.cs @@ -0,0 +1,63 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 贴子类型配置 + /// + public class PostConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Post", "Blogs" ).HasComment( "贴子" ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "PostId" ) + .HasComment( "贴子标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Title ) + .HasColumnName( "Title" ) + .HasComment( "标题" ); + builder.Property( t => t.Content ) + .HasColumnName( "Content" ) + .HasComment( "内容" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + builder.HasMany( t => t.Tags ) + .WithMany( t => t.Posts ) + .UsingEntity( t => t.ToTable( "PostTag2" ) ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/ProductConfiguration.cs b/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/ProductConfiguration.cs new file mode 100644 index 000000000..a40ad246f --- /dev/null +++ b/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/ProductConfiguration.cs @@ -0,0 +1,102 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Configs; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 产品类型配置 + /// + public class ProductConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + InitData( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Product", "Products" ).HasComment( "产品" ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "ProductId" ) + .HasComment( "产品标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Code ) + .HasColumnName( "Code" ) + .HasComment( "产品编码" ); + builder.Property( t => t.Name ) + .HasColumnName( "Name" ) + .HasComment( "产品名称" ); + builder.Property( t => t.Price ) + .HasColumnName( "Price" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ) + .HasPrecision( 12, 2 ); + builder.Property( t => t.IntPrice ) + .HasColumnName( "IntPrice" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.LongPrice ) + .HasColumnName( "LongPrice" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.FloatPrice ) + .HasColumnName( "FloatPrice" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.Description ) + .HasColumnName( "Description" ) + .HasComment( "描述" ); + builder.Property( t => t.Enabled ) + .HasColumnName( "Enabled" ) + .HasDefaultValue( TestConfig.BoolValue ) + .HasComment( "启用" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + builder.Property( t => t.IsDeleted ) + .HasDefaultValue( false ); + } + + /// + /// 初始化数据 + /// + private void InitData( EntityTypeBuilder builder ) { + var product = new Product( TestConfig.Id ) { + Code = TestConfig.Value, + Price = TestConfig.DecimalValue, + IntPrice = TestConfig.IntValue, + LongPrice = TestConfig.LongValue, + FloatPrice = TestConfig.FloatValue + }; + builder.HasData( product ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/TagConfiguration.cs b/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/TagConfiguration.cs new file mode 100644 index 000000000..4a219b8b2 --- /dev/null +++ b/test/Util.TestShare.PostgreSql/EntityTypeConfigurations/TagConfiguration.cs @@ -0,0 +1,57 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 标签类型配置 + /// + public class TagConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Tag", "Blogs" ).HasComment( "标签" ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "TagId" ) + .HasComment( "标签标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Name ) + .HasColumnName( "Name" ) + .HasComment( "标签名称" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.PostgreSql/Repositories/CustomerRepository.cs b/test/Util.TestShare.PostgreSql/Repositories/CustomerRepository.cs new file mode 100644 index 000000000..f73cdd0eb --- /dev/null +++ b/test/Util.TestShare.PostgreSql/Repositories/CustomerRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 客户仓储 + /// + public class CustomerRepository : RepositoryBase,ICustomerRepository { + /// + /// 初始化客户仓储 + /// + /// 工作单元 + public CustomerRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.PostgreSql/Repositories/OperationLogRepository.cs b/test/Util.TestShare.PostgreSql/Repositories/OperationLogRepository.cs new file mode 100644 index 000000000..112510a83 --- /dev/null +++ b/test/Util.TestShare.PostgreSql/Repositories/OperationLogRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 操作日志仓储 + /// + public class OperationLogRepository : RepositoryBase,IOperationLogRepository { + /// + /// 初始化操作日志仓储 + /// + /// 工作单元 + public OperationLogRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.PostgreSql/Repositories/OrderRepository.cs b/test/Util.TestShare.PostgreSql/Repositories/OrderRepository.cs new file mode 100644 index 000000000..324767646 --- /dev/null +++ b/test/Util.TestShare.PostgreSql/Repositories/OrderRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 订单仓储 + /// + public class OrderRepository : RepositoryBase,IOrderRepository { + /// + /// 初始化订单仓储 + /// + /// 工作单元 + public OrderRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.PostgreSql/Repositories/PostRepository.cs b/test/Util.TestShare.PostgreSql/Repositories/PostRepository.cs new file mode 100644 index 000000000..ca71fe2e6 --- /dev/null +++ b/test/Util.TestShare.PostgreSql/Repositories/PostRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 贴子仓储 + /// + public class PostRepository : RepositoryBase,IPostRepository { + /// + /// 初始化贴子仓储 + /// + /// 工作单元 + public PostRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.PostgreSql/Repositories/ProductRepository.cs b/test/Util.TestShare.PostgreSql/Repositories/ProductRepository.cs new file mode 100644 index 000000000..bd3a30f89 --- /dev/null +++ b/test/Util.TestShare.PostgreSql/Repositories/ProductRepository.cs @@ -0,0 +1,25 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 产品仓储 + /// + public class ProductRepository : RepositoryBase,IProductRepository { + /// + /// 初始化产品仓储 + /// + /// 工作单元 + public ProductRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + + /// + /// 更新产品 + /// + /// 产品 + public void UpdateProduct( Product product ) { + UnitOfWork.Update( product ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.PostgreSql/Repositories/TagRepository.cs b/test/Util.TestShare.PostgreSql/Repositories/TagRepository.cs new file mode 100644 index 000000000..cfad8f0a2 --- /dev/null +++ b/test/Util.TestShare.PostgreSql/Repositories/TagRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 标签仓储 + /// + public class TagRepository : RepositoryBase,ITagRepository { + /// + /// 初始化标签仓储 + /// + /// 工作单元 + public TagRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.PostgreSql/UnitOfWorks/PgSqlUnitOfWork.cs b/test/Util.TestShare.PostgreSql/UnitOfWorks/PgSqlUnitOfWork.cs new file mode 100644 index 000000000..e6a1a61e0 --- /dev/null +++ b/test/Util.TestShare.PostgreSql/UnitOfWorks/PgSqlUnitOfWork.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Util.Data.EntityFrameworkCore; +using Util.Sessions; + +namespace Util.Tests.UnitOfWorks { + /// + /// PgSql工作单元 + /// + public class PgSqlUnitOfWork : PgSqlUnitOfWorkBase, ITestUnitOfWork { + /// + /// 初始化工作单元 + /// + /// 服务提供器 + /// 配置项 + public PgSqlUnitOfWork( IServiceProvider serviceProvider ,DbContextOptions options) : base( serviceProvider, options ) { + } + + /// + /// 设置用户会话 + /// + public void SetSession( ISession session ) { + Session = session; + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.PostgreSql/Util.TestShare.PostgreSql.csproj b/test/Util.TestShare.PostgreSql/Util.TestShare.PostgreSql.csproj new file mode 100644 index 000000000..3c9eccad1 --- /dev/null +++ b/test/Util.TestShare.PostgreSql/Util.TestShare.PostgreSql.csproj @@ -0,0 +1,13 @@ + + + + $(NetTargetFramework) + false + Util.Tests + + + + + + + diff --git a/test/Util.TestShare.SqlServer/EntityTypeConfigurations/CustomerConfiguration.cs b/test/Util.TestShare.SqlServer/EntityTypeConfigurations/CustomerConfiguration.cs new file mode 100644 index 000000000..fa02c9a92 --- /dev/null +++ b/test/Util.TestShare.SqlServer/EntityTypeConfigurations/CustomerConfiguration.cs @@ -0,0 +1,94 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Configs; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 客户类型配置 + /// + public class CustomerConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + InitData( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Customer", "Customers" ).HasComment( "客户" ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "CustomerId" ) + .HasComment( "客户标识" ) + .ValueGeneratedOnAdd(); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Code ) + .HasColumnName( "Code" ) + .HasComment( "编码" ); + builder.Property( t => t.Name ) + .HasColumnName( "Name" ) + .HasComment( "姓名" ); + builder.Property( t => t.Nickname ) + .HasColumnName( "Nickname" ) + .HasComment( "昵称" ); + builder.Property( t => t.Gender ) + .HasColumnName( "Gender" ) + .HasComment( "性别" ); + builder.Property( t => t.Birthday ) + .HasColumnName( "Birthday" ) + .HasComment( "出生日期" ); + builder.Property( t => t.Nation ) + .HasColumnName( "Nation" ) + .HasComment( "民族" ); + builder.Property( t => t.Phone ) + .HasColumnName( "Phone" ) + .HasComment( "手机号" ); + builder.Property( t => t.Email ) + .HasColumnName( "Email" ) + .HasComment( "电子邮件" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + builder.Property( t => t.IsDeleted ) + .HasDefaultValue( false ); + } + + /// + /// 初始化数据 + /// + private void InitData( EntityTypeBuilder builder ) { + var customer = new Customer(1) { + Code = TestConfig.Value, + Birthday = TestConfig.DateTimeValue + }; + builder.HasData( customer ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.SqlServer/EntityTypeConfigurations/OperationLogConfiguration.cs b/test/Util.TestShare.SqlServer/EntityTypeConfigurations/OperationLogConfiguration.cs new file mode 100644 index 000000000..7c18950e6 --- /dev/null +++ b/test/Util.TestShare.SqlServer/EntityTypeConfigurations/OperationLogConfiguration.cs @@ -0,0 +1,55 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 操作日志类型配置 + /// + public class OperationLogConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "OperationLog", "Systems",t => t.HasComment( "操作日志" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "LogId" ) + .HasComment( "操作标识" ) + .ValueGeneratedOnAdd(); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.LogName ) + .HasColumnName( "LogName" ) + .HasComment( "日志名称" ); + builder.Property( t => t.OperationTime ) + .HasColumnName( "OperationTime" ) + .HasComment( "操作时间" ); + builder.Property( t => t.Caption ) + .HasColumnName( "Caption" ) + .HasComment( "标题" ); + builder.Property( t => t.Content ) + .HasColumnName( "Content" ) + .HasComment( "内容" ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.SqlServer/EntityTypeConfigurations/OrderConfiguration.cs b/test/Util.TestShare.SqlServer/EntityTypeConfigurations/OrderConfiguration.cs new file mode 100644 index 000000000..4f14d2892 --- /dev/null +++ b/test/Util.TestShare.SqlServer/EntityTypeConfigurations/OrderConfiguration.cs @@ -0,0 +1,65 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Configs; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 订单类型配置 + /// + public class OrderConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Order", "Sales" ).HasComment( "订单" ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasMaxLength( 50 ) + .HasColumnName( "OrderId" ) + .HasComment( "订单标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.CustomerId ) + .HasColumnName( "CustomerId" ) + .HasComment( "客户标识" ); + builder.Property( t => t.CustomerName ) + .HasColumnName( "CustomerName" ) + .HasComment( "客户名称" ); + builder.Property( t => t.Amount ) + .HasColumnName( "Amount" ) + .HasComment( "金额" ) + .HasPrecision(15,2) + .HasDefaultValue( 0 ); + builder.Property( t => t.PlaceOrderTime ) + .HasColumnName( "PlaceOrderTime" ) + .HasComment( "下单时间" ) + .HasDefaultValue(TestConfig.DateTimeValue); + builder.Property( t => t.State ) + .HasColumnName( "State" ) + .HasComment( "订单状态" ) + .HasDefaultValue(0); + builder.Property( t => t.IsDeleted ) + .HasDefaultValue( false ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.SqlServer/EntityTypeConfigurations/OrderItemConfiguration.cs b/test/Util.TestShare.SqlServer/EntityTypeConfigurations/OrderItemConfiguration.cs new file mode 100644 index 000000000..a26c5801b --- /dev/null +++ b/test/Util.TestShare.SqlServer/EntityTypeConfigurations/OrderItemConfiguration.cs @@ -0,0 +1,70 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 订单明细类型配置 + /// + public class OrderItemConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + ConfigAssociations( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "OrderItem", "Sales" ).HasComment( "订单明细" ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "OrderItemId" ) + .HasComment( "订单明细标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.OrderId ) + .HasColumnName( "OrderId" ) + .HasMaxLength( 50 ) + .HasComment( "订单标识" ); + builder.Property( t => t.ProductId ) + .HasColumnName( "ProductId" ) + .HasComment( "产品标识" ); + builder.Property( t => t.ProductName ) + .HasColumnName( "ProductName" ) + .HasComment( "产品名称" ); + builder.Property( t => t.Price ) + .HasColumnName( "Price" ) + .HasComment( "单价" ) + .HasPrecision(12,2); + builder.Property( t => t.Quantity ) + .HasColumnName( "Quantity" ) + .HasComment( "数量" ) + .HasDefaultValue(1); + } + + /// + /// 映射导航属性 + /// + private void ConfigAssociations( EntityTypeBuilder builder ) { + builder.HasOne( t => t.Order ) + .WithMany( t => t.OrderItems ) + .HasForeignKey( t => t.OrderId ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.SqlServer/EntityTypeConfigurations/PostConfiguration.cs b/test/Util.TestShare.SqlServer/EntityTypeConfigurations/PostConfiguration.cs new file mode 100644 index 000000000..79f200480 --- /dev/null +++ b/test/Util.TestShare.SqlServer/EntityTypeConfigurations/PostConfiguration.cs @@ -0,0 +1,63 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 贴子类型配置 + /// + public class PostConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Post", "Blogs" ).HasComment( "贴子" ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "PostId" ) + .HasComment( "贴子标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Title ) + .HasColumnName( "Title" ) + .HasComment( "标题" ); + builder.Property( t => t.Content ) + .HasColumnName( "Content" ) + .HasComment( "内容" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + builder.HasMany( t => t.Tags ) + .WithMany( t => t.Posts ) + .UsingEntity( t => t.ToTable( "PostTag2" ) ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.SqlServer/EntityTypeConfigurations/ProductConfiguration.cs b/test/Util.TestShare.SqlServer/EntityTypeConfigurations/ProductConfiguration.cs new file mode 100644 index 000000000..d847a4b06 --- /dev/null +++ b/test/Util.TestShare.SqlServer/EntityTypeConfigurations/ProductConfiguration.cs @@ -0,0 +1,102 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Configs; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 产品类型配置 + /// + public class ProductConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + InitData( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Product", "Products",t => t.HasComment( "产品" ) ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "ProductId" ) + .HasComment( "产品标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Code ) + .HasColumnName( "Code" ) + .HasComment( "产品编码" ); + builder.Property( t => t.Name ) + .HasColumnName( "Name" ) + .HasComment( "产品名称" ); + builder.Property( t => t.Price ) + .HasColumnName( "Price" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ) + .HasPrecision( 12, 2 ); + builder.Property( t => t.IntPrice ) + .HasColumnName( "IntPrice" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.LongPrice ) + .HasColumnName( "LongPrice" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.FloatPrice ) + .HasColumnName( "FloatPrice" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.Description ) + .HasColumnName( "Description" ) + .HasComment( "描述" ); + builder.Property( t => t.Enabled ) + .HasColumnName( "Enabled" ) + .HasDefaultValue( TestConfig.BoolValue ) + .HasComment( "启用" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + builder.Property( t => t.IsDeleted ) + .HasDefaultValue( false ); + } + + /// + /// 初始化数据 + /// + private void InitData( EntityTypeBuilder builder ) { + var product = new Product( TestConfig.Id ) { + Code = TestConfig.Value, + Price = TestConfig.DecimalValue, + IntPrice = TestConfig.IntValue, + LongPrice = TestConfig.LongValue, + FloatPrice = TestConfig.FloatValue + }; + builder.HasData( product ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.SqlServer/EntityTypeConfigurations/ResourceConfiguration.cs b/test/Util.TestShare.SqlServer/EntityTypeConfigurations/ResourceConfiguration.cs new file mode 100644 index 000000000..7b949f722 --- /dev/null +++ b/test/Util.TestShare.SqlServer/EntityTypeConfigurations/ResourceConfiguration.cs @@ -0,0 +1,78 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 资源类型配置 + /// + public class ResourceConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Resource", "Permissions" ).HasComment( "资源" ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "ResourceId" ) + .HasComment( "资源标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Uri ) + .HasColumnName( "Uri" ) + .HasComment( "资源标识符" ); + builder.Property( t => t.Name ) + .HasColumnName( "Name" ) + .HasComment( "资源名称" ); + builder.Property( t => t.ParentId ) + .HasColumnName( "ParentId" ) + .HasComment( "父标识" ); + builder.Property( t => t.Path ) + .HasColumnName( "Path" ) + .HasComment( "路径" ); + builder.Property( t => t.Level ) + .HasColumnName( "Level" ) + .HasComment( "层级" ); + builder.Property( t => t.Remark ) + .HasColumnName( "Remark" ) + .HasComment( "备注" ); + builder.Property( t => t.Enabled ) + .HasColumnName( "Enabled" ) + .HasComment( "启用" ); + builder.Property( t => t.SortId ) + .HasColumnName( "SortId" ) + .HasComment( "排序号" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人标识" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人标识" ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.SqlServer/EntityTypeConfigurations/TagConfiguration.cs b/test/Util.TestShare.SqlServer/EntityTypeConfigurations/TagConfiguration.cs new file mode 100644 index 000000000..4a219b8b2 --- /dev/null +++ b/test/Util.TestShare.SqlServer/EntityTypeConfigurations/TagConfiguration.cs @@ -0,0 +1,57 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 标签类型配置 + /// + public class TagConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Tag", "Blogs" ).HasComment( "标签" ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "TagId" ) + .HasComment( "标签标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Name ) + .HasColumnName( "Name" ) + .HasComment( "标签名称" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.SqlServer/Repositories/CustomerRepository.cs b/test/Util.TestShare.SqlServer/Repositories/CustomerRepository.cs new file mode 100644 index 000000000..f73cdd0eb --- /dev/null +++ b/test/Util.TestShare.SqlServer/Repositories/CustomerRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 客户仓储 + /// + public class CustomerRepository : RepositoryBase,ICustomerRepository { + /// + /// 初始化客户仓储 + /// + /// 工作单元 + public CustomerRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.SqlServer/Repositories/OperationLogRepository.cs b/test/Util.TestShare.SqlServer/Repositories/OperationLogRepository.cs new file mode 100644 index 000000000..112510a83 --- /dev/null +++ b/test/Util.TestShare.SqlServer/Repositories/OperationLogRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 操作日志仓储 + /// + public class OperationLogRepository : RepositoryBase,IOperationLogRepository { + /// + /// 初始化操作日志仓储 + /// + /// 工作单元 + public OperationLogRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.SqlServer/Repositories/OrderRepository.cs b/test/Util.TestShare.SqlServer/Repositories/OrderRepository.cs new file mode 100644 index 000000000..324767646 --- /dev/null +++ b/test/Util.TestShare.SqlServer/Repositories/OrderRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 订单仓储 + /// + public class OrderRepository : RepositoryBase,IOrderRepository { + /// + /// 初始化订单仓储 + /// + /// 工作单元 + public OrderRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.SqlServer/Repositories/PostRepository.cs b/test/Util.TestShare.SqlServer/Repositories/PostRepository.cs new file mode 100644 index 000000000..ca71fe2e6 --- /dev/null +++ b/test/Util.TestShare.SqlServer/Repositories/PostRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 贴子仓储 + /// + public class PostRepository : RepositoryBase,IPostRepository { + /// + /// 初始化贴子仓储 + /// + /// 工作单元 + public PostRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.SqlServer/Repositories/ProductRepository.cs b/test/Util.TestShare.SqlServer/Repositories/ProductRepository.cs new file mode 100644 index 000000000..bd3a30f89 --- /dev/null +++ b/test/Util.TestShare.SqlServer/Repositories/ProductRepository.cs @@ -0,0 +1,25 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 产品仓储 + /// + public class ProductRepository : RepositoryBase,IProductRepository { + /// + /// 初始化产品仓储 + /// + /// 工作单元 + public ProductRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + + /// + /// 更新产品 + /// + /// 产品 + public void UpdateProduct( Product product ) { + UnitOfWork.Update( product ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.SqlServer/Repositories/ResourceRepository.cs b/test/Util.TestShare.SqlServer/Repositories/ResourceRepository.cs new file mode 100644 index 000000000..d24f005d5 --- /dev/null +++ b/test/Util.TestShare.SqlServer/Repositories/ResourceRepository.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using System; +using Microsoft.EntityFrameworkCore; +using Util.Data.EntityFrameworkCore.Trees; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 资源仓储 + /// + public class ResourceRepository : TreeRepositoryBase,IResourceRepository { + /// + /// 初始化资源仓储 + /// + /// 工作单元 + public ResourceRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + + /// + /// 生成排序号 + /// + /// 父标识 + public async Task GenerateSortIdAsync( Guid? parentId ) { + var maxSortId = await Find( t => t.ParentId == parentId ).MaxAsync( t => t.SortId ); + return maxSortId.SafeValue() + 1; + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.SqlServer/Repositories/TagRepository.cs b/test/Util.TestShare.SqlServer/Repositories/TagRepository.cs new file mode 100644 index 000000000..cfad8f0a2 --- /dev/null +++ b/test/Util.TestShare.SqlServer/Repositories/TagRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 标签仓储 + /// + public class TagRepository : RepositoryBase,ITagRepository { + /// + /// 初始化标签仓储 + /// + /// 工作单元 + public TagRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.SqlServer/UnitOfWorks/SqlServerUnitOfWork.cs b/test/Util.TestShare.SqlServer/UnitOfWorks/SqlServerUnitOfWork.cs new file mode 100644 index 000000000..b3b648f98 --- /dev/null +++ b/test/Util.TestShare.SqlServer/UnitOfWorks/SqlServerUnitOfWork.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Util.Data.EntityFrameworkCore; +using Util.Sessions; + +namespace Util.Tests.UnitOfWorks { + /// + /// SqlServer工作单元 + /// + public class SqlServerUnitOfWork : SqlServerUnitOfWorkBase, ITestUnitOfWork, ITestUnitOfWork2 { + /// + /// 初始化工作单元 + /// + /// 服务提供器 + /// 配置项 + public SqlServerUnitOfWork( IServiceProvider serviceProvider ,DbContextOptions options) : base( serviceProvider, options ) { + } + + /// + /// 设置用户会话 + /// + public void SetSession( ISession session ) { + Session = session; + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.SqlServer/Util.TestShare.SqlServer.csproj b/test/Util.TestShare.SqlServer/Util.TestShare.SqlServer.csproj new file mode 100644 index 000000000..55e6959de --- /dev/null +++ b/test/Util.TestShare.SqlServer/Util.TestShare.SqlServer.csproj @@ -0,0 +1,14 @@ + + + + $(NetTargetFramework) + false + Util.Tests + + + + + + + + diff --git a/test/Util.TestShare.Sqlite/EntityTypeConfigurations/CustomerConfiguration.cs b/test/Util.TestShare.Sqlite/EntityTypeConfigurations/CustomerConfiguration.cs new file mode 100644 index 000000000..e3c595fd2 --- /dev/null +++ b/test/Util.TestShare.Sqlite/EntityTypeConfigurations/CustomerConfiguration.cs @@ -0,0 +1,94 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Configs; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 客户类型配置 + /// + public class CustomerConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + InitData( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Customer" ).HasComment( "客户" ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "CustomerId" ) + .HasComment( "客户标识" ) + .ValueGeneratedOnAdd(); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Code ) + .HasColumnName( "Code" ) + .HasComment( "编码" ); + builder.Property( t => t.Name ) + .HasColumnName( "Name" ) + .HasComment( "姓名" ); + builder.Property( t => t.Nickname ) + .HasColumnName( "Nickname" ) + .HasComment( "昵称" ); + builder.Property( t => t.Gender ) + .HasColumnName( "Gender" ) + .HasComment( "性别" ); + builder.Property( t => t.Birthday ) + .HasColumnName( "Birthday" ) + .HasComment( "出生日期" ); + builder.Property( t => t.Nation ) + .HasColumnName( "Nation" ) + .HasComment( "民族" ); + builder.Property( t => t.Phone ) + .HasColumnName( "Phone" ) + .HasComment( "手机号" ); + builder.Property( t => t.Email ) + .HasColumnName( "Email" ) + .HasComment( "电子邮件" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + builder.Property( t => t.IsDeleted ) + .HasDefaultValue( false ); + } + + /// + /// 初始化数据 + /// + private void InitData( EntityTypeBuilder builder ) { + var customer = new Customer( 1 ) { + Code = TestConfig.Value, + Birthday = TestConfig.DateTimeValue + }; + builder.HasData( customer ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Sqlite/EntityTypeConfigurations/OperationLogConfiguration.cs b/test/Util.TestShare.Sqlite/EntityTypeConfigurations/OperationLogConfiguration.cs new file mode 100644 index 000000000..a85415b1c --- /dev/null +++ b/test/Util.TestShare.Sqlite/EntityTypeConfigurations/OperationLogConfiguration.cs @@ -0,0 +1,55 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 操作日志类型配置 + /// + public class OperationLogConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "OperationLog" ).HasComment( "操作日志" ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "LogId" ) + .HasComment( "操作标识" ) + .ValueGeneratedOnAdd(); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.LogName ) + .HasColumnName( "LogName" ) + .HasComment( "日志名称" ); + builder.Property( t => t.OperationTime ) + .HasColumnName( "OperationTime" ) + .HasComment( "操作时间" ); + builder.Property( t => t.Caption ) + .HasColumnName( "Caption" ) + .HasComment( "标题" ); + builder.Property( t => t.Content ) + .HasColumnName( "Content" ) + .HasComment( "内容" ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Sqlite/EntityTypeConfigurations/OrderConfiguration.cs b/test/Util.TestShare.Sqlite/EntityTypeConfigurations/OrderConfiguration.cs new file mode 100644 index 000000000..de27bea6e --- /dev/null +++ b/test/Util.TestShare.Sqlite/EntityTypeConfigurations/OrderConfiguration.cs @@ -0,0 +1,65 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Configs; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 订单类型配置 + /// + public class OrderConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Order" ).HasComment( "订单" ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasMaxLength( 50 ) + .HasColumnName( "OrderId" ) + .HasComment( "订单标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.CustomerId ) + .HasColumnName( "CustomerId" ) + .HasComment( "客户标识" ); + builder.Property( t => t.CustomerName ) + .HasColumnName( "CustomerName" ) + .HasComment( "客户名称" ); + builder.Property( t => t.Amount ) + .HasColumnName( "Amount" ) + .HasComment( "金额" ) + .HasPrecision(15,2) + .HasDefaultValue(0); + builder.Property( t => t.PlaceOrderTime ) + .HasColumnName( "PlaceOrderTime" ) + .HasComment( "下单时间" ) + .HasDefaultValue( TestConfig.DateTimeValue ); + builder.Property( t => t.State ) + .HasColumnName( "State" ) + .HasComment( "订单状态" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.IsDeleted ) + .HasDefaultValue( false ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Sqlite/EntityTypeConfigurations/OrderItemConfiguration.cs b/test/Util.TestShare.Sqlite/EntityTypeConfigurations/OrderItemConfiguration.cs new file mode 100644 index 000000000..5f3103ec6 --- /dev/null +++ b/test/Util.TestShare.Sqlite/EntityTypeConfigurations/OrderItemConfiguration.cs @@ -0,0 +1,70 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 订单明细类型配置 + /// + public class OrderItemConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + ConfigAssociations( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "OrderItem" ).HasComment( "订单明细" ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "OrderItemId" ) + .HasComment( "订单明细标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.OrderId ) + .HasColumnName( "OrderId" ) + .HasMaxLength( 50 ) + .HasComment( "订单标识" ); + builder.Property( t => t.ProductId ) + .HasColumnName( "ProductId" ) + .HasComment( "产品标识" ); + builder.Property( t => t.ProductName ) + .HasColumnName( "ProductName" ) + .HasComment( "产品名称" ); + builder.Property( t => t.Price ) + .HasColumnName( "Price" ) + .HasComment( "单价" ) + .HasPrecision(12,2); + builder.Property( t => t.Quantity ) + .HasColumnName( "Quantity" ) + .HasComment( "数量" ) + .HasDefaultValue( 1 ); + } + + /// + /// 映射导航属性 + /// + private void ConfigAssociations( EntityTypeBuilder builder ) { + builder.HasOne( t => t.Order ) + .WithMany( t => t.OrderItems ) + .HasForeignKey( t => t.OrderId ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Sqlite/EntityTypeConfigurations/PostConfiguration.cs b/test/Util.TestShare.Sqlite/EntityTypeConfigurations/PostConfiguration.cs new file mode 100644 index 000000000..bb6c6da89 --- /dev/null +++ b/test/Util.TestShare.Sqlite/EntityTypeConfigurations/PostConfiguration.cs @@ -0,0 +1,63 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 贴子类型配置 + /// + public class PostConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Post" ).HasComment( "贴子" ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "PostId" ) + .HasComment( "贴子标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Title ) + .HasColumnName( "Title" ) + .HasComment( "标题" ); + builder.Property( t => t.Content ) + .HasColumnName( "Content" ) + .HasComment( "内容" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + builder.HasMany( t => t.Tags ) + .WithMany( t => t.Posts ) + .UsingEntity( t => t.ToTable( "PostTag2" ) ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Sqlite/EntityTypeConfigurations/ProductConfiguration.cs b/test/Util.TestShare.Sqlite/EntityTypeConfigurations/ProductConfiguration.cs new file mode 100644 index 000000000..bb057341b --- /dev/null +++ b/test/Util.TestShare.Sqlite/EntityTypeConfigurations/ProductConfiguration.cs @@ -0,0 +1,102 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Configs; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 产品类型配置 + /// + public class ProductConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + InitData( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Product" ).HasComment( "产品" ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "ProductId" ) + .HasComment( "产品标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Code ) + .HasColumnName( "Code" ) + .HasComment( "产品编码" ); + builder.Property( t => t.Name ) + .HasColumnName( "Name" ) + .HasComment( "产品名称" ); + builder.Property( t => t.Price ) + .HasColumnName( "Price" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ) + .HasPrecision( 12, 2 ); + builder.Property( t => t.IntPrice ) + .HasColumnName( "IntPrice" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.LongPrice ) + .HasColumnName( "LongPrice" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.FloatPrice ) + .HasColumnName( "FloatPrice" ) + .HasComment( "价格" ) + .HasDefaultValue( 0 ); + builder.Property( t => t.Description ) + .HasColumnName( "Description" ) + .HasComment( "描述" ); + builder.Property( t => t.Enabled ) + .HasColumnName( "Enabled" ) + .HasDefaultValue( TestConfig.BoolValue ) + .HasComment( "启用" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + builder.Property( t => t.IsDeleted ) + .HasDefaultValue( false ); + } + + /// + /// 初始化数据 + /// + private void InitData( EntityTypeBuilder builder ) { + var product = new Product( TestConfig.Id ) { + Code = TestConfig.Value, + Price = TestConfig.DecimalValue, + IntPrice = TestConfig.IntValue, + LongPrice = TestConfig.LongValue, + FloatPrice = TestConfig.FloatValue + }; + builder.HasData( product ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Sqlite/EntityTypeConfigurations/TagConfiguration.cs b/test/Util.TestShare.Sqlite/EntityTypeConfigurations/TagConfiguration.cs new file mode 100644 index 000000000..f6b3a0f09 --- /dev/null +++ b/test/Util.TestShare.Sqlite/EntityTypeConfigurations/TagConfiguration.cs @@ -0,0 +1,57 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Util.Tests.Models; + +namespace Util.Tests.EntityTypeConfigurations { + /// + /// 标签类型配置 + /// + public class TagConfiguration : IEntityTypeConfiguration { + /// + /// 配置 + /// + /// 实体类型生成器 + public void Configure( EntityTypeBuilder builder ) { + ConfigTable( builder ); + ConfigId( builder ); + ConfigProperties( builder ); + } + + /// + /// 配置表 + /// + private void ConfigTable( EntityTypeBuilder builder ) { + builder.ToTable( "Tag" ).HasComment( "标签" ); + } + + /// + /// 配置标识 + /// + private void ConfigId( EntityTypeBuilder builder ) { + builder.Property( t => t.Id ) + .HasColumnName( "TagId" ) + .HasComment( "标签标识" ); + } + + /// + /// 配置属性 + /// + private void ConfigProperties( EntityTypeBuilder builder ) { + builder.Property( t => t.Name ) + .HasColumnName( "Name" ) + .HasComment( "标签名称" ); + builder.Property( t => t.CreationTime ) + .HasColumnName( "CreationTime" ) + .HasComment( "创建时间" ); + builder.Property( t => t.CreatorId ) + .HasColumnName( "CreatorId" ) + .HasComment( "创建人" ); + builder.Property( t => t.LastModificationTime ) + .HasColumnName( "LastModificationTime" ) + .HasComment( "最后修改时间" ); + builder.Property( t => t.LastModifierId ) + .HasColumnName( "LastModifierId" ) + .HasComment( "最后修改人" ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Sqlite/Repositories/CustomerRepository.cs b/test/Util.TestShare.Sqlite/Repositories/CustomerRepository.cs new file mode 100644 index 000000000..f73cdd0eb --- /dev/null +++ b/test/Util.TestShare.Sqlite/Repositories/CustomerRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 客户仓储 + /// + public class CustomerRepository : RepositoryBase,ICustomerRepository { + /// + /// 初始化客户仓储 + /// + /// 工作单元 + public CustomerRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Sqlite/Repositories/OperationLogRepository.cs b/test/Util.TestShare.Sqlite/Repositories/OperationLogRepository.cs new file mode 100644 index 000000000..112510a83 --- /dev/null +++ b/test/Util.TestShare.Sqlite/Repositories/OperationLogRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 操作日志仓储 + /// + public class OperationLogRepository : RepositoryBase,IOperationLogRepository { + /// + /// 初始化操作日志仓储 + /// + /// 工作单元 + public OperationLogRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Sqlite/Repositories/OrderRepository.cs b/test/Util.TestShare.Sqlite/Repositories/OrderRepository.cs new file mode 100644 index 000000000..324767646 --- /dev/null +++ b/test/Util.TestShare.Sqlite/Repositories/OrderRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 订单仓储 + /// + public class OrderRepository : RepositoryBase,IOrderRepository { + /// + /// 初始化订单仓储 + /// + /// 工作单元 + public OrderRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Sqlite/Repositories/PostRepository.cs b/test/Util.TestShare.Sqlite/Repositories/PostRepository.cs new file mode 100644 index 000000000..ca71fe2e6 --- /dev/null +++ b/test/Util.TestShare.Sqlite/Repositories/PostRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 贴子仓储 + /// + public class PostRepository : RepositoryBase,IPostRepository { + /// + /// 初始化贴子仓储 + /// + /// 工作单元 + public PostRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Sqlite/Repositories/ProductRepository.cs b/test/Util.TestShare.Sqlite/Repositories/ProductRepository.cs new file mode 100644 index 000000000..bd3a30f89 --- /dev/null +++ b/test/Util.TestShare.Sqlite/Repositories/ProductRepository.cs @@ -0,0 +1,25 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 产品仓储 + /// + public class ProductRepository : RepositoryBase,IProductRepository { + /// + /// 初始化产品仓储 + /// + /// 工作单元 + public ProductRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + + /// + /// 更新产品 + /// + /// 产品 + public void UpdateProduct( Product product ) { + UnitOfWork.Update( product ); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Sqlite/Repositories/TagRepository.cs b/test/Util.TestShare.Sqlite/Repositories/TagRepository.cs new file mode 100644 index 000000000..cfad8f0a2 --- /dev/null +++ b/test/Util.TestShare.Sqlite/Repositories/TagRepository.cs @@ -0,0 +1,17 @@ +using Util.Data.EntityFrameworkCore; +using Util.Tests.Models; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Repositories { + /// + /// 标签仓储 + /// + public class TagRepository : RepositoryBase,ITagRepository { + /// + /// 初始化标签仓储 + /// + /// 工作单元 + public TagRepository( ITestUnitOfWork unitOfWork ) : base( unitOfWork ) { + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Sqlite/UnitOfWorks/SqliteUnitOfWork.cs b/test/Util.TestShare.Sqlite/UnitOfWorks/SqliteUnitOfWork.cs new file mode 100644 index 000000000..2d78d9eb1 --- /dev/null +++ b/test/Util.TestShare.Sqlite/UnitOfWorks/SqliteUnitOfWork.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Util.Data.EntityFrameworkCore; +using Util.Sessions; + +namespace Util.Tests.UnitOfWorks { + /// + /// Sqlite工作单元 + /// + public class SqliteUnitOfWork : SqliteUnitOfWorkBase, ITestUnitOfWork { + /// + /// 初始化工作单元 + /// + /// 服务提供器 + /// 配置项 + public SqliteUnitOfWork( IServiceProvider serviceProvider ,DbContextOptions options) : base( serviceProvider, options ) { + } + + /// + /// 设置用户会话 + /// + public void SetSession( ISession session ) { + Session = session; + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare.Sqlite/Util.TestShare.Sqlite.csproj b/test/Util.TestShare.Sqlite/Util.TestShare.Sqlite.csproj new file mode 100644 index 000000000..9fa055139 --- /dev/null +++ b/test/Util.TestShare.Sqlite/Util.TestShare.Sqlite.csproj @@ -0,0 +1,13 @@ + + + + $(NetTargetFramework) + false + Util.Tests + + + + + + + \ No newline at end of file diff --git a/test/Util.TestShare/Conditions/ProductNameCondition.cs b/test/Util.TestShare/Conditions/ProductNameCondition.cs new file mode 100644 index 000000000..1bc74be8a --- /dev/null +++ b/test/Util.TestShare/Conditions/ProductNameCondition.cs @@ -0,0 +1,31 @@ +using System; +using System.Linq.Expressions; +using Util.Data; +using Util.Tests.Models; + +namespace Util.Tests.Conditions; + +/// +/// 产品名称查询条件 +/// +public class ProductNameCondition : ICondition { + /// + /// 产品名称 + /// + private readonly string _name; + + /// + /// 初始化产品名称查询条件 + /// + /// 产品名称 + public ProductNameCondition( string name ) { + _name = name; + } + + /// + /// 获取查询条件 + /// + public Expression> GetCondition() { + return t => t.Name == _name; + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Configs/TestConfig.cs b/test/Util.TestShare/Configs/TestConfig.cs new file mode 100644 index 000000000..0beade22b --- /dev/null +++ b/test/Util.TestShare/Configs/TestConfig.cs @@ -0,0 +1,49 @@ +using System; + +namespace Util.Tests.Configs; + +/// +/// 测试配置 +/// +public class TestConfig { + /// + /// 测试标识 + /// + public static Guid Id { get; } = "08255312-DF8E-4F0C-AD4D-638B3A63D889".ToGuid(); + /// + /// 测试值 + /// + public static string Value { get; } = "abc"; + /// + /// 测试整型值 + /// + public static int IntValue { get; } = 123; + /// + /// 测试长整型值 + /// + public static long LongValue { get; } = 123456789; + /// + /// 测试Guid值 + /// + public static Guid GuidValue { get; } = "04255312-DF8E-4F0C-AD4D-638B3A63D868".ToGuid(); + /// + /// 测试布尔值 + /// + public static bool BoolValue { get; } = true; + /// + /// 测试float值 + /// + public static float FloatValue { get; } = 123.45F; + /// + /// 测试double值 + /// + public static double DoubleValue { get; } = 12345.67; + /// + /// 测试decimal值 + /// + public static decimal DecimalValue { get; } = 12345.67M; + /// + /// 测试日期值 + /// + public static DateTime? DateTimeValue { get; } = "2012-12-12 12:12:12".ToDateTime(); +} \ No newline at end of file diff --git a/test/Util.TestShare/Controllers/CustomerController.cs b/test/Util.TestShare/Controllers/CustomerController.cs new file mode 100644 index 000000000..da5dc221d --- /dev/null +++ b/test/Util.TestShare/Controllers/CustomerController.cs @@ -0,0 +1,62 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Util.Applications.Controllers; +using Util.Tests.Dtos; +using Util.Tests.Queries; +using Util.Tests.Services; + +namespace Util.Tests.Controllers; + +/// +/// 客户查询控制器 +/// +public class CustomerController : QueryControllerBase { + /// + /// 初始化客户查询控制器 + /// + /// 客户服务 + public CustomerController( ICustomerService service ) : base( service ) { + CustomerService = service; + } + + /// + /// 客户服务 + /// + public ICustomerService CustomerService { get; } + + /// + /// 获取单个实体 + /// + /// 标识 + [HttpGet( "{id}" )] + public new async Task GetAsync( string id ) { + return await base.GetAsync( id ); + } + + /// + /// 查询 + /// + /// 查询参数 + [HttpGet( "Query" )] + public new async Task QueryAsync( [FromQuery] CustomerQuery query ) { + return await base.QueryAsync( query ); + } + + /// + /// 分页查询 + /// + /// 查询参数 + [HttpGet] + public new async Task PageQueryAsync( [FromQuery] CustomerQuery query ) { + return await base.PageQueryAsync( query ); + } + + /// + /// 获取项列表 + /// + /// 查询参数 + [HttpGet( "Items" )] + public new async Task GetItemsAsync( [FromQuery] CustomerQuery query ) { + return await base.GetItemsAsync( query ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Controllers/LockController.cs b/test/Util.TestShare/Controllers/LockController.cs new file mode 100644 index 000000000..de277d6fd --- /dev/null +++ b/test/Util.TestShare/Controllers/LockController.cs @@ -0,0 +1,41 @@ +using System.Threading; +using Microsoft.AspNetCore.Mvc; +using Util.Applications.Controllers; +using Util.Applications.Filters; +using Util.Applications.Locks; + +namespace Util.Tests.Controllers; + +/// +/// 业务锁控制器 +/// +public class LockController : WebApiControllerBase { + /// + /// 未设置防重复过滤器 + /// + [HttpGet("nolock")] + public IActionResult GetAsync() { + Thread.Sleep( 3000 ); + return Success( "ok" ); + } + + /// + /// 设置防重复过滤器 - 全局锁 + /// + [HttpGet( "GlobalLock" )] + [Lock(Type = LockType.Global)] + public IActionResult Get2Async() { + Thread.Sleep( 3000 ); + return Success( "ok" ); + } + + /// + /// 设置防重复过滤器 - 用户锁 + /// + [HttpGet( "UserLock" )] + [Lock( Type = LockType.User )] + public IActionResult Get3Async() { + Thread.Sleep( 3000 ); + return Success( "ok" ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Controllers/ProductController.cs b/test/Util.TestShare/Controllers/ProductController.cs new file mode 100644 index 000000000..c671f1025 --- /dev/null +++ b/test/Util.TestShare/Controllers/ProductController.cs @@ -0,0 +1,90 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Util.Applications.Controllers; +using Util.Tests.Dtos; +using Util.Tests.Queries; +using Util.Tests.Services; + +namespace Util.Tests.Controllers; + +/// +/// 产品控制器 +/// +public class ProductController : CrudControllerBase { + /// + /// 初始化产品控制器 + /// + /// 产品服务 + public ProductController( IProductService service ) : base( service ) { + ProductService = service; + } + + /// + /// 产品服务 + /// + public IProductService ProductService { get; } + + /// + /// 获取单个实体 + /// + /// 标识 + [HttpGet( "{id}" )] + public new async Task GetAsync( string id ) { + return await base.GetAsync( id ); + } + + /// + /// 查询 + /// + /// 查询参数 + [HttpGet( "Query" )] + public new async Task QueryAsync( [FromQuery] ProductQuery query ) { + return await base.QueryAsync( query ); + } + + /// + /// 分页查询 + /// + /// 查询参数 + [HttpGet] + public new async Task PageQueryAsync( [FromQuery] ProductQuery query ) { + return await base.PageQueryAsync( query ); + } + + /// + /// 创建 + /// + /// 创建参数 + [HttpPost] + public new async Task CreateAsync( ProductDto request ) { + return await base.CreateAsync( request ); + } + + /// + /// 修改 + /// + /// 标识 + /// 修改参数 + [HttpPut( "{id?}" )] + public new async Task UpdateAsync( string id, ProductDto request ) { + return await base.UpdateAsync( id, request ); + } + + /// + /// 删除,注意:该方法用于删除单个实体,批量删除请使用POST提交,否则可能失败 + /// + /// 标识 + [HttpDelete( "{id}" )] + public new async Task DeleteAsync( string id ) { + return await base.DeleteAsync( id ); + } + + /// + /// 批量删除 + /// + /// 标识列表,多个Id用逗号分隔,范例:1,2,3 + [HttpPost( "delete" )] + public async Task BatchDeleteAsync( [FromBody] string ids ) { + return await base.DeleteAsync( ids ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Dtos/CustomerDto.cs b/test/Util.TestShare/Dtos/CustomerDto.cs new file mode 100644 index 000000000..d7c234917 --- /dev/null +++ b/test/Util.TestShare/Dtos/CustomerDto.cs @@ -0,0 +1,88 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Applications.Dtos; +using Util.Domain.Biz.Enums; + +namespace Util.Tests.Dtos; + +/// +/// 客户参数 +/// +public class CustomerDto : DtoBase { + /// + /// 编码 + /// + [Description( "编码" )] + [MaxLength( 100 )] + public string Code { get; set; } + /// + /// 姓名 + /// + [Description( "姓名" )] + [MaxLength( 200 )] + public string Name { get; set; } + /// + /// 昵称 + /// + [Description( "昵称" )] + [MaxLength( 50 )] + public string Nickname { get; set; } + /// + /// 性别 + /// + [Description( "性别" )] + public Gender? Gender { get; set; } + /// + /// 出生日期 + /// + [Description( "出生日期" )] + public DateTime? Birthday { get; set; } + /// + /// 民族 + /// + [Description( "民族" )] + public Nation? Nation { get; set; } + /// + /// 手机号 + /// + [Description( "手机号" )] + [MaxLength( 50 )] + public string Phone { get; set; } + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + [MaxLength( 500 )] + public string Email { get; set; } + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + /// + /// 版本号 + /// + [Description( "版本号" )] + public byte[] Version { get; set; } +} \ No newline at end of file diff --git a/test/Util.TestShare/Dtos/ProductDto.cs b/test/Util.TestShare/Dtos/ProductDto.cs new file mode 100644 index 000000000..975589047 --- /dev/null +++ b/test/Util.TestShare/Dtos/ProductDto.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Applications.Dtos; +using Util.Tests.Models; + +namespace Util.Tests.Dtos; + +/// +/// 产品参数 +/// +public class ProductDto : DtoBase { + /// + /// 产品参数 + /// + public ProductDto() { + TestProperty2 = new ProductItem(); + TestProperties = new List(); + } + + /// + /// 产品编码 + /// + [Description( "产品编码" )] + [MaxLength( 50 )] + public string Code { get; set; } + /// + /// 产品名称 + /// + [Description( "产品名称" )] + [MaxLength( 500 )] + public string Name { get; set; } + /// + /// 价格 + /// + [Description( "价格" )] + public decimal Price { get; set; } + /// + /// 描述 + /// + [Description( "描述" )] + public string Description { get; set; } + /// + /// 启用 + /// + [Description( "启用" )] + public bool Enabled { get; set; } + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + /// + /// 版本号 + /// + [Description( "版本号" )] + public byte[] Version { get; set; } + /// + /// 简单扩展属性 + /// + public string TestProperty1 { get; set; } + /// + /// 对象扩展属性 + /// + public ProductItem TestProperty2 { get; set; } + /// + /// 对象集合扩展属性 + /// + public List TestProperties { get; set; } +} \ No newline at end of file diff --git a/test/Util.TestShare/Dtos/ResourceDto.cs b/test/Util.TestShare/Dtos/ResourceDto.cs new file mode 100644 index 000000000..5b9ffb325 --- /dev/null +++ b/test/Util.TestShare/Dtos/ResourceDto.cs @@ -0,0 +1,59 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Util.Applications.Trees; + +namespace Util.Tests.Dtos; + +/// +/// 资源参数 +/// +public class ResourceDto : TreeDtoBase { + /// + /// 资源标识符 + /// + [Display( Name = "资源标识符" )] + [MaxLength( 300 )] + public string Uri { get; set; } + /// + /// 资源名称 + /// + [Display( Name = "资源名称" )] + [Required] + [MaxLength( 200 )] + public string Name { get; set; } + /// + /// 备注 + /// + [Display( Name = "备注" )] + [MaxLength( 500 )] + public string Remark { get; set; } + /// + /// 创建时间 + /// + [Display( Name = "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人标识 + /// + [Display( Name = "创建人标识" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Display( Name = "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人标识 + /// + [Display( Name = "最后修改人标识" )] + public Guid? LastModifierId { get; set; } + /// + /// 版本号 + /// + [Display( Name = "版本号" )] + public byte[] Version { get; set; } + + public override string GetText() { + return Name; + } +} \ No newline at end of file diff --git a/test/Util.TestShare/EventHandlers/OperationLogChangedEventHandler.cs b/test/Util.TestShare/EventHandlers/OperationLogChangedEventHandler.cs new file mode 100644 index 000000000..749c00d91 --- /dev/null +++ b/test/Util.TestShare/EventHandlers/OperationLogChangedEventHandler.cs @@ -0,0 +1,46 @@ +using System.Threading; +using System.Threading.Tasks; +using Util.Domain.Events; +using Util.Events; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.EventHandlers; + +/// +/// 操作日志变更事件处理器 +/// +public class OperationLogChangedEventHandler : EventHandlerBase> { + /// + /// 工作单元 + /// + private readonly ITestUnitOfWork _unitOfWork; + /// + /// 操作日志仓储 + /// + private readonly IOperationLogRepository _operationLogRepository; + + /// + /// 初始化操作日志变更事件处理器 + /// + /// 工作单元 + /// 操作日志仓储 + public OperationLogChangedEventHandler( ITestUnitOfWork unitOfWork, IOperationLogRepository operationLogRepository ) { + _unitOfWork = unitOfWork; + _operationLogRepository = operationLogRepository; + } + + /// + /// 处理事件 + /// + /// 事件 + /// 取消令牌 + public override async Task HandleAsync( EntityChangedEvent @event, CancellationToken cancellationToken ) { + if ( @event.ChangeType == EntityChangeType.Deleted && @event.Entity.LogName == "EntityChangedEvent_Deleted" ) { + var log = new OperationLog { LogName = "Test", Caption = nameof( OperationLogChangedEventHandler ), Content = @event.Entity.LogName }; + await _operationLogRepository.AddAsync( log, cancellationToken ); + await _unitOfWork.CommitAsync(); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare/EventHandlers/OperationLogDeletedEventHandler.cs b/test/Util.TestShare/EventHandlers/OperationLogDeletedEventHandler.cs new file mode 100644 index 000000000..0f218f5e3 --- /dev/null +++ b/test/Util.TestShare/EventHandlers/OperationLogDeletedEventHandler.cs @@ -0,0 +1,44 @@ +using System.Threading; +using System.Threading.Tasks; +using Util.Domain.Events; +using Util.Events; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.EventHandlers; + +/// +/// 操作日志删除事件处理器 +/// +public class OperationLogDeletedEventHandler : EventHandlerBase> { + /// + /// 工作单元 + /// + private readonly ITestUnitOfWork _unitOfWork; + /// + /// 操作日志仓储 + /// + private readonly IOperationLogRepository _operationLogRepository; + + /// + /// 初始化产品删除事件处理器 + /// + /// 工作单元 + /// 操作日志仓储 + public OperationLogDeletedEventHandler( ITestUnitOfWork unitOfWork, IOperationLogRepository operationLogRepository ) { + _unitOfWork = unitOfWork; + _operationLogRepository = operationLogRepository; + } + + /// + /// 处理事件 + /// + public override async Task HandleAsync( EntityDeletedEvent @event, CancellationToken cancellationToken ) { + if( @event.Entity.LogName != "EntityDeletedEvent" ) + return; + var log = new OperationLog { LogName = "Test",Caption = nameof( OperationLogDeletedEventHandler ), Content = @event.Entity.LogName }; + await _operationLogRepository.AddAsync( log, cancellationToken ); + await _unitOfWork.CommitAsync(); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/EventHandlers/ProductChangedEventHandler.cs b/test/Util.TestShare/EventHandlers/ProductChangedEventHandler.cs new file mode 100644 index 000000000..c8edaf8ef --- /dev/null +++ b/test/Util.TestShare/EventHandlers/ProductChangedEventHandler.cs @@ -0,0 +1,56 @@ +using System.Threading; +using System.Threading.Tasks; +using Util.Domain.Events; +using Util.Events; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.EventHandlers; + +/// +/// 产品变更事件处理器 +/// +public class ProductChangedEventHandler : EventHandlerBase> { + /// + /// 工作单元 + /// + private readonly ITestUnitOfWork _unitOfWork; + /// + /// 操作日志仓储 + /// + private readonly IOperationLogRepository _operationLogRepository; + + /// + /// 初始化产品变更事件处理器 + /// + /// 工作单元 + /// 操作日志仓储 + public ProductChangedEventHandler( ITestUnitOfWork unitOfWork, IOperationLogRepository operationLogRepository ) { + _unitOfWork = unitOfWork; + _operationLogRepository = operationLogRepository; + } + + /// + /// 处理事件 + /// + public override async Task HandleAsync( EntityChangedEvent @event, CancellationToken cancellationToken ) { + if ( @event.ChangeType == EntityChangeType.Created && @event.Entity.Name == "EntityChangedEvent_Created" ) { + var log = new OperationLog { Caption = @event.Entity.Name, LogName = nameof( ProductChangedEventHandler ) }; + await _operationLogRepository.AddAsync( log, cancellationToken ); + await _unitOfWork.CommitAsync(); + return; + } + if( @event.ChangeType == EntityChangeType.Updated && @event.Entity.Name == "EntityChangedEvent_Updated" ) { + var log = new OperationLog { Caption = @event.Entity.Name, LogName = nameof( ProductChangedEventHandler ) }; + await _operationLogRepository.AddAsync( log, cancellationToken ); + await _unitOfWork.CommitAsync(); + return; + } + if( @event.ChangeType == EntityChangeType.Deleted && @event.Entity.Name == "EntityChangedEvent_Deleted" ) { + var log = new OperationLog { Caption = @event.Entity.Name, LogName = nameof( ProductChangedEventHandler ) }; + await _operationLogRepository.AddAsync( log, cancellationToken ); + await _unitOfWork.CommitAsync(); + } + } +} \ No newline at end of file diff --git a/test/Util.TestShare/EventHandlers/ProductCreatedEventHandler.cs b/test/Util.TestShare/EventHandlers/ProductCreatedEventHandler.cs new file mode 100644 index 000000000..d49921993 --- /dev/null +++ b/test/Util.TestShare/EventHandlers/ProductCreatedEventHandler.cs @@ -0,0 +1,44 @@ +using System.Threading; +using System.Threading.Tasks; +using Util.Domain.Events; +using Util.Events; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.EventHandlers; + +/// +/// 产品创建事件处理器 +/// +public class ProductCreatedEventHandler : EventHandlerBase> { + /// + /// 工作单元 + /// + private readonly ITestUnitOfWork _unitOfWork; + /// + /// 操作日志仓储 + /// + private readonly IOperationLogRepository _operationLogRepository; + + /// + /// 初始化产品创建事件处理器 + /// + /// 工作单元 + /// 操作日志仓储 + public ProductCreatedEventHandler( ITestUnitOfWork unitOfWork, IOperationLogRepository operationLogRepository ) { + _unitOfWork = unitOfWork; + _operationLogRepository = operationLogRepository; + } + + /// + /// 处理事件 + /// + public override async Task HandleAsync( EntityCreatedEvent @event, CancellationToken cancellationToken ) { + if( @event.Entity.Name != "EntityCreatedEvent" ) + return; + var log = new OperationLog { Caption = @event.Entity.Name,LogName = nameof( ProductCreatedEventHandler ) }; + await _operationLogRepository.AddAsync( log, cancellationToken ); + await _unitOfWork.CommitAsync(); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/EventHandlers/ProductDeletedEventHandler.cs b/test/Util.TestShare/EventHandlers/ProductDeletedEventHandler.cs new file mode 100644 index 000000000..c9d9aa334 --- /dev/null +++ b/test/Util.TestShare/EventHandlers/ProductDeletedEventHandler.cs @@ -0,0 +1,44 @@ +using System.Threading; +using System.Threading.Tasks; +using Util.Domain.Events; +using Util.Events; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.EventHandlers; + +/// +/// 产品删除事件处理器 +/// +public class ProductDeletedEventHandler : EventHandlerBase> { + /// + /// 工作单元 + /// + private readonly ITestUnitOfWork _unitOfWork; + /// + /// 操作日志仓储 + /// + private readonly IOperationLogRepository _operationLogRepository; + + /// + /// 初始化产品删除事件处理器 + /// + /// 工作单元 + /// 操作日志仓储 + public ProductDeletedEventHandler( ITestUnitOfWork unitOfWork, IOperationLogRepository operationLogRepository ) { + _unitOfWork = unitOfWork; + _operationLogRepository = operationLogRepository; + } + + /// + /// 处理事件 + /// + public override async Task HandleAsync( EntityDeletedEvent @event, CancellationToken cancellationToken ) { + if( @event.Entity.Name != "EntityDeletedEvent" ) + return; + var log = new OperationLog { Caption = @event.Entity.Name,LogName = nameof( ProductDeletedEventHandler ) }; + await _operationLogRepository.AddAsync( log, cancellationToken ); + await _unitOfWork.CommitAsync(); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/EventHandlers/ProductUpdatedEventHandler.cs b/test/Util.TestShare/EventHandlers/ProductUpdatedEventHandler.cs new file mode 100644 index 000000000..7fc6fe238 --- /dev/null +++ b/test/Util.TestShare/EventHandlers/ProductUpdatedEventHandler.cs @@ -0,0 +1,44 @@ +using System.Threading; +using System.Threading.Tasks; +using Util.Domain.Events; +using Util.Events; +using Util.Tests.Models; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.EventHandlers; + +/// +/// 产品修改事件处理器 +/// +public class ProductUpdatedEventHandler : EventHandlerBase> { + /// + /// 工作单元 + /// + private readonly ITestUnitOfWork _unitOfWork; + /// + /// 操作日志仓储 + /// + private readonly IOperationLogRepository _operationLogRepository; + + /// + /// 初始化产品修改事件处理器 + /// + /// 工作单元 + /// 操作日志仓储 + public ProductUpdatedEventHandler( ITestUnitOfWork unitOfWork, IOperationLogRepository operationLogRepository ) { + _unitOfWork = unitOfWork; + _operationLogRepository = operationLogRepository; + } + + /// + /// 处理事件 + /// + public override async Task HandleAsync( EntityUpdatedEvent @event, CancellationToken cancellationToken ) { + if ( @event.Entity.Name != "EntityUpdatedEvent" ) + return; + var log = new OperationLog { Caption = @event.Entity.Name,LogName = nameof( ProductUpdatedEventHandler ) }; + await _operationLogRepository.AddAsync( log, cancellationToken ); + await _unitOfWork.CommitAsync(); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/EventHandlers/TestEventHandler.cs b/test/Util.TestShare/EventHandlers/TestEventHandler.cs new file mode 100644 index 000000000..ede474145 --- /dev/null +++ b/test/Util.TestShare/EventHandlers/TestEventHandler.cs @@ -0,0 +1,33 @@ +using System.Threading; +using System.Threading.Tasks; +using Util.Events; +using Util.Exceptions; +using Util.Tests.Events; + +namespace Util.Tests.EventHandlers; + +/// +/// 测试事件2处理器 +/// +public class TestEvent2Handler : EventHandlerBase { + /// + /// 处理事件 + /// + public override async Task HandleAsync( TestEvent2 @event, CancellationToken cancellationToken ) { + await Task.CompletedTask; + throw new Warning( @event.Id.ToString() ); + } +} + +/// +/// 测试事件3处理器 +/// +public class TestEvent3Handler : EventHandlerBase { + /// + /// 处理事件 + /// + public override async Task HandleAsync( TestEvent3 @event, CancellationToken cancellationToken ) { + await Task.CompletedTask; + throw new Warning( @event.Id.ToString() ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Events/TestEvent.cs b/test/Util.TestShare/Events/TestEvent.cs new file mode 100644 index 000000000..8f8c39401 --- /dev/null +++ b/test/Util.TestShare/Events/TestEvent.cs @@ -0,0 +1,26 @@ +using System; +using Util.Events; + +namespace Util.Tests.Events; + +/// +/// 测试事件 +/// +public class TestEvent : IEvent { + public Guid Id { get; set; } + public string Name { get; set; } +} + +/// +/// 测试事件2 +/// +public class TestEvent2 : IEvent { + public Guid Id { get; set; } + public string Name { get; set; } +} + +/// +/// 测试事件3 - 集成事件 +/// +public record TestEvent3( Guid Id, string Name ) : IntegrationEvent { +} \ No newline at end of file diff --git a/test/Util.TestShare/Events/TestEventBus.cs b/test/Util.TestShare/Events/TestEventBus.cs new file mode 100644 index 000000000..39ddf8d84 --- /dev/null +++ b/test/Util.TestShare/Events/TestEventBus.cs @@ -0,0 +1,29 @@ +using System.Threading; +using System.Threading.Tasks; +using Util.Data; +using Util.Dependency; +using Util.Events; + +namespace Util.Tests.Events; + +/// +/// 测试事件总线 +/// +public interface ITestEventBus : IEventBus, ITransientDependency { +} + +/// +/// 测试事件总线,用于测试工作单元操作管理器 +/// +public class TestEventBus : ITestEventBus { + private readonly IUnitOfWorkActionManager _manager; + private readonly ILocalEventBus _eventBus; + public TestEventBus( IUnitOfWorkActionManager unitOfWorkActionManager, ILocalEventBus eventBus ) { + _manager = unitOfWorkActionManager; + _eventBus = eventBus; + } + public Task PublishAsync( TEvent @event, CancellationToken cancellationToken = default ) where TEvent : IEvent { + _manager.Register( async () => await _eventBus.PublishAsync( @event, cancellationToken ) ); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Fakes/CustomerFakeService.cs b/test/Util.TestShare/Fakes/CustomerFakeService.cs new file mode 100644 index 000000000..42254144d --- /dev/null +++ b/test/Util.TestShare/Fakes/CustomerFakeService.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using System.Linq; +using AutoBogus; +using Util.Tests.Dtos; +using Util.Tests.Models; + +namespace Util.Tests.Fakes; + +/// +/// 客户模拟数据服务 +/// +public static class CustomerFakeService { + /// + /// 获取客户 + /// + public static Customer GetCustomer() { + return GetCustomers( 1 ).First(); + } + + /// + /// 获取客户列表 + /// + /// 行数 + public static List GetCustomers( int count ) { + return new AutoFaker() + .RuleFor( t => t.Code, t => t.Random.String2( 1, 100 ) ) + .RuleFor( t => t.Name, t => t.Random.String2( 1, 200 ) ) + .RuleFor( t => t.Nickname, t => t.Random.String2( 1, 50 ) ) + .RuleFor( t => t.Phone, t => t.Random.String2( 1, 50 ) ) + .RuleFor( t => t.Email, t => t.Random.String2( 1, 500 ) ) + .RuleFor( t => t.IsDeleted, false ) + .Generate( count ); + } + + /// + /// 获取客户 + /// + public static CustomerDto GetCustomerDto() { + return GetCustomerDtos( 1 ).First(); + } + + /// + /// 获取客户列表 + /// + /// 行数 + public static List GetCustomerDtos( int count ) { + return new AutoFaker() + .Configure( builder => builder + .WithSkip( t => t.Id ) + ) + .RuleFor( t => t.Code, t => t.Random.String2( 1, 100 ) ) + .RuleFor( t => t.Name, t => t.Random.String2( 1, 200 ) ) + .RuleFor( t => t.Nickname, t => t.Random.String2( 1, 50 ) ) + .RuleFor( t => t.Phone, t => t.Random.String2( 1, 50 ) ) + .RuleFor( t => t.Email, t => t.Random.String2( 1, 500 ) ) + .RuleFor( t => t.IsDeleted, false ) + .Generate( count ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Fakes/OperationLogFakeService.cs b/test/Util.TestShare/Fakes/OperationLogFakeService.cs new file mode 100644 index 000000000..2c511b63c --- /dev/null +++ b/test/Util.TestShare/Fakes/OperationLogFakeService.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Linq; +using AutoBogus; +using Util.Tests.Models; + +namespace Util.Tests.Fakes; + +/// +/// 操作日志模拟数据服务 +/// +public static class OperationLogFakeService { + /// + /// 获取操作日志 + /// + public static OperationLog GetOperationLog() { + return GetOperationLogs( 1 ).First(); + } + + /// + /// 获取操作日志列表 + /// + /// 行数 + public static List GetOperationLogs( int count ) { + return new AutoFaker() + .RuleFor( t => t.LogName, t => t.Random.String2( 1, 50 ) ) + .Generate( count ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Fakes/OrderFakeService.cs b/test/Util.TestShare/Fakes/OrderFakeService.cs new file mode 100644 index 000000000..b68f3ae77 --- /dev/null +++ b/test/Util.TestShare/Fakes/OrderFakeService.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Linq; +using AutoBogus; +using Util.Tests.Models; + +namespace Util.Tests.Fakes; + +/// +/// 订单模拟数据服务 +/// +public static class OrderFakeService { + /// + /// 获取订单 + /// + public static Order GetOrder() { + return GetOrders( 1 ).First(); + } + + /// + /// 获取订单列表 + /// + /// 行数 + public static List GetOrders( int count ) { + return new AutoFaker() + .Ignore( t => t.OrderItems ) + .RuleFor( t => t.IsDeleted, false ) + .Generate( count ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Fakes/OrderItemFakeService.cs b/test/Util.TestShare/Fakes/OrderItemFakeService.cs new file mode 100644 index 000000000..87cb3f928 --- /dev/null +++ b/test/Util.TestShare/Fakes/OrderItemFakeService.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Linq; +using AutoBogus; +using Util.Tests.Models; + +namespace Util.Tests.Fakes; + +/// +/// 订单明细模拟数据服务 +/// +public static class OrderItemFakeService { + /// + /// 获取订单明细 + /// + public static OrderItem GetOrderItem() { + return GetOrderItems( 1 ).First(); + } + + /// + /// 获取订单明细列表 + /// + /// 行数 + public static List GetOrderItems( int count ) { + return new AutoFaker() + .Ignore( t => t.Order ) + .Generate( count ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Fakes/PostFakeService.cs b/test/Util.TestShare/Fakes/PostFakeService.cs new file mode 100644 index 000000000..b313eaa98 --- /dev/null +++ b/test/Util.TestShare/Fakes/PostFakeService.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Linq; +using AutoBogus; +using Util.Tests.Models; + +namespace Util.Tests.Fakes; + +/// +/// 贴子模拟数据服务 +/// +public static class PostFakeService { + /// + /// 获取贴子 + /// + public static Post GetPost() { + return GetPosts( 1 ).First(); + } + + /// + /// 获取贴子列表 + /// + /// 行数 + public static List GetPosts( int count ) { + return new AutoFaker() + .Ignore( t => t.Tags ) + .RuleFor( t => t.IsDeleted, false ) + .Generate( count ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Fakes/ProductFakeService.cs b/test/Util.TestShare/Fakes/ProductFakeService.cs new file mode 100644 index 000000000..a640a7250 --- /dev/null +++ b/test/Util.TestShare/Fakes/ProductFakeService.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using System.Linq; +using AutoBogus; +using Util.Tests.Dtos; +using Util.Tests.Models; + +namespace Util.Tests.Fakes; + +/// +/// 产品模拟数据服务 +/// +public static class ProductFakeService { + /// + /// 获取产品 + /// + public static Product GetProduct() { + return GetProducts( 1 ).First(); + } + + /// + /// 获取产品列表 + /// + /// 行数 + public static List GetProducts( int count ) { + return new AutoFaker() + .RuleFor( t => t.Code, t => t.Random.String2( 1, 20 ) ) + .RuleFor( t => t.Name, t => t.Random.String2( 1, 500 ) ) + .Ignore( t => t.CreationTime ) + .Ignore( t => t.CreatorId ) + .Ignore( t => t.LastModificationTime ) + .Ignore( t => t.LastModifierId ) + .Ignore( t => t.TenantId ) + .Ignore( t => t.TestProperty1 ) + .Ignore( t => t.Version ) + .RuleFor( t => t.IsDeleted, false ) + .Generate( count ); + } + + /// + /// 获取产品 + /// + public static ProductDto GetProductDto() { + return GetProductDtos( 1 ).First(); + } + + /// + /// 获取产品列表 + /// + /// 行数 + public static List GetProductDtos( int count ) { + return new AutoFaker() + .Configure( builder => builder + .WithSkip( t => t.Id ) + ) + .RuleFor( t => t.Code, t => t.Random.String2( 1, 20 ) ) + .RuleFor( t => t.Name, t => t.Random.String2( 1, 500 ) ) + .Ignore( t => t.CreationTime ) + .Ignore( t => t.CreatorId ) + .Ignore( t => t.LastModificationTime ) + .Ignore( t => t.LastModifierId ) + .Ignore( t => t.TestProperty1 ) + .RuleFor( t => t.IsDeleted, false ) + .Generate( count ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Fakes/ResourceDtoFakeService.cs b/test/Util.TestShare/Fakes/ResourceDtoFakeService.cs new file mode 100644 index 000000000..9b51860f2 --- /dev/null +++ b/test/Util.TestShare/Fakes/ResourceDtoFakeService.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.Linq; +using AutoBogus; +using Util.Tests.Dtos; + +namespace Util.Tests.Fakes; + +/// +/// 资源模拟数据服务 +/// +public static class ResourceDtoFakeService { + /// + /// 获取资源 + /// + public static ResourceDto GetResourceDto() { + return GetResourceDtos(1).First(); + } + + /// + /// 获取资源列表 + /// + /// 行数 + public static List GetResourceDtos( int count ) { + return new AutoFaker() + .Ignore( t => t.Id ) + .Ignore( t => t.ParentId ) + .Ignore( t => t.Path ) + .Ignore( t => t.Level ) + .Ignore( t => t.SortId ) + .RuleFor( t => t.Uri,t => t.Random.String2( 1,300 ) ) + .RuleFor( t => t.Name,t => t.Random.String2( 1,200 ) ) + .RuleFor( t => t.Remark,t => t.Random.String2( 1,500 ) ) + .Generate( count ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Fakes/TagFakeService.cs b/test/Util.TestShare/Fakes/TagFakeService.cs new file mode 100644 index 000000000..1576c75a1 --- /dev/null +++ b/test/Util.TestShare/Fakes/TagFakeService.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Linq; +using AutoBogus; +using Util.Tests.Models; + +namespace Util.Tests.Fakes; + +/// +/// 标签模拟数据服务 +/// +public static class TagFakeService { + /// + /// 获取标签 + /// + public static Tag GetTag() { + return GetTags( 1 ).First(); + } + + /// + /// 获取标签列表 + /// + /// 行数 + public static List GetTags( int count ) { + return new AutoFaker() + .Ignore( t => t.Posts ) + .RuleFor( t => t.IsDeleted, false ) + .Generate( count ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Infrastructure/ControllerTestBase.cs b/test/Util.TestShare/Infrastructure/ControllerTestBase.cs new file mode 100644 index 000000000..ed926e2b8 --- /dev/null +++ b/test/Util.TestShare/Infrastructure/ControllerTestBase.cs @@ -0,0 +1,103 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Util.Http; + +namespace Util.Tests.Infrastructure; + +/// +/// 控制器测试基类 +/// +public abstract class ControllerTestBase { + /// + /// 测试初始化 + /// + protected ControllerTestBase( IHttpClient client, IHttpContextAccessor accessor = null ) { + Client = client; + if ( accessor != null ) + Context = accessor.HttpContext; + } + + /// + /// Http客户端 + /// + protected IHttpClient Client { get; } + + /// + /// Http上下文 + /// + protected HttpContext Context { get; } + + /// + /// Http Get操作 + /// + /// 服务地址 + /// 参数 + protected async Task> GetAsync( string url, object data = null ) { + return await Client.Get>( url, data ).GetResultAsync(); + } + + /// + /// Http Get操作 + /// + /// 返回结果类型 + /// 服务地址 + /// 参数 + protected async Task> GetAsync( string url, object data = null ) { + return await Client.Get>( url,data ).GetResultAsync(); + } + + /// + /// Http Post操作 + /// + /// 服务地址 + /// 参数 + protected async Task> PostAsync( string url, object data ) { + return await PostAsync( url, data ); + } + + /// + /// Http Post操作 + /// + /// 返回结果类型 + /// 服务地址 + /// 参数 + protected async Task> PostAsync( string url, object data ) { + return await Client.Post>( url, data ).GetResultAsync(); + } + + /// + /// Http Put操作 + /// + /// 服务地址 + /// 参数 + protected async Task> PutAsync( string url, object data ) { + return await PutAsync( url, data ); + } + + /// + /// Http Put操作 + /// + /// 返回结果类型 + /// 服务地址 + /// 参数 + protected async Task> PutAsync( string url, object data ) { + return await Client.Put>( url, data ).GetResultAsync(); + } + + /// + /// Http Delete操作 + /// + /// 服务地址 + protected async Task> DeleteAsync( string url ) { + return await DeleteAsync( url ); + } + + /// + /// Http Delete操作 + /// + /// 返回结果类型 + /// 服务地址 + protected async Task> DeleteAsync( string url ) { + return await Client.Delete>( url ).GetResultAsync(); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Infrastructure/TestBase.cs b/test/Util.TestShare/Infrastructure/TestBase.cs new file mode 100644 index 000000000..0b4f225a2 --- /dev/null +++ b/test/Util.TestShare/Infrastructure/TestBase.cs @@ -0,0 +1,22 @@ +using Util.Data; + +namespace Util.Tests.Infrastructure; + +/// +/// 测试基类 +/// +public abstract class TestBase { + /// + /// 测试初始化 + /// + protected TestBase( IUnitOfWork unitOfWork ) { + UnitOfWork = unitOfWork; + } + + /// + /// 工作单元 + /// + protected IUnitOfWork UnitOfWork { + get; + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Infrastructure/TestSession.cs b/test/Util.TestShare/Infrastructure/TestSession.cs new file mode 100644 index 000000000..ae84fccb9 --- /dev/null +++ b/test/Util.TestShare/Infrastructure/TestSession.cs @@ -0,0 +1,32 @@ +using System; +using Util.Sessions; + +namespace Util.Tests.Infrastructure; + +/// +/// 测试用户会话 +/// +public class TestSession : ISession { + /// + /// 服务提供器 + /// + public IServiceProvider ServiceProvider => null; + + public TestSession() { + UserId = TestUserId.ToString(); + } + + public bool IsAuthenticated => true; + public string UserId { get; set; } + public string TenantId { get; set; } + + /// + /// 测试用户标识 + /// + public static Guid TestUserId = "c75521e5-e247-42b2-9115-6c3cce5c3403".ToGuid(); + + /// + /// 测试用户标识2 + /// + public static Guid TestUserId2 = "0e81ddbc-f43c-2622-e753-4abdfdc8db06".ToGuid(); +} \ No newline at end of file diff --git a/test/Util.TestShare/Infrastructure/WebApiResult.cs b/test/Util.TestShare/Infrastructure/WebApiResult.cs new file mode 100644 index 000000000..6958b4099 --- /dev/null +++ b/test/Util.TestShare/Infrastructure/WebApiResult.cs @@ -0,0 +1,19 @@ +namespace Util.Tests.Infrastructure; + +/// +/// Web Api操作结果 +/// +public class WebApiResult { + /// + /// 状态码 + /// + public string Code { get; set; } + /// + /// 消息 + /// + public string Message { get; set; } + /// + /// 数据 + /// + public T Data { get; set; } +} \ No newline at end of file diff --git a/test/Util.TestShare/Middlewares/TestUserMiddleware.cs b/test/Util.TestShare/Middlewares/TestUserMiddleware.cs new file mode 100644 index 000000000..801c883cf --- /dev/null +++ b/test/Util.TestShare/Middlewares/TestUserMiddleware.cs @@ -0,0 +1,36 @@ +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Util.Tests.Middlewares; + +/// +/// 测试用户中间件 +/// +public class TestUserMiddleware { + /// + /// 下个中间件 + /// + private readonly RequestDelegate _next; + + /// + /// 初始化测试用户中间件 + /// + /// 下个中间件 + public TestUserMiddleware( RequestDelegate next ) { + _next = next; + } + + /// + /// 执行中间件 + /// + /// Http上下文 + public async Task Invoke( HttpContext context ) { + var userId = context.Request.Headers["user-id"].SafeString(); + if ( userId.IsEmpty() == false ) { + var identity = new ClaimsIdentity( new[] { new Claim( "sub", userId ) } ); + context.User = new ClaimsPrincipal( identity ); + } + await _next( context ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Models/ApplicationExtend.cs b/test/Util.TestShare/Models/ApplicationExtend.cs new file mode 100644 index 000000000..c0f7c29d9 --- /dev/null +++ b/test/Util.TestShare/Models/ApplicationExtend.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; + +namespace Util.Tests.Models; + +/// +/// 应用程序扩展信息 +/// +public class ApplicationExtend +{ + /// + /// 初始化 + /// + public ApplicationExtend() + { + Client = new Client(); + } + + /// + /// 应用程序类型 + /// + public int ApplicationType { get; set; } + /// + /// 是否客户端 + /// + public bool IsClient { get; set; } + /// + /// 客户端 + /// + public Client Client { get; set; } +} + +/// +/// 客户端 +/// +public class Client +{ + /// + /// 初始化客户端 + /// + public Client() + { + AllowedCorsOrigins = new List(); + RedirectUris = new List(); + PostLogoutRedirectUris = new List(); + AllowedScopes = new List(); + } + + /// + /// 允许的授权类型 + /// + public int AllowedGrantType { get; set; } + /// + /// 允许通过浏览器访问令牌 + /// + public bool? AllowAccessTokensViaBrowser { get; set; } + /// + /// 允许的跨域来源 + /// + public List AllowedCorsOrigins { get; set; } + /// + /// 需要同意 + /// + public bool? RequireConsent { get; set; } + /// + /// 需要客户端密钥 + /// + public bool? RequireClientSecret { get; set; } + /// + /// 客户端密钥列表 + /// + public List ClientSecrets { get; set; } + /// + /// 认证重定向地址列表 + /// + public List RedirectUris { get; set; } + /// + /// 注销重定向地址列表 + /// + public List PostLogoutRedirectUris { get; set; } + /// + /// 允许的作用域 + /// + public List AllowedScopes { get; set; } + /// + /// 访问令牌生命周期 + /// + public int AccessTokenLifetime { get; set; } +} \ No newline at end of file diff --git a/test/Util.TestShare/Models/Customer.cs b/test/Util.TestShare/Models/Customer.cs new file mode 100644 index 000000000..268161a8f --- /dev/null +++ b/test/Util.TestShare/Models/Customer.cs @@ -0,0 +1,117 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Domain; +using Util.Domain.Auditing; +using Util.Domain.Biz.Enums; +using Util.Domain.Entities; + +namespace Util.Tests.Models; + +/// +/// 客户 +/// +[Description( "客户" )] +public class Customer : AggregateRoot,IDelete,IAudited { + /// + /// 初始化客户 + /// + public Customer() : this( 0 ) { + } + + /// + /// 初始化客户 + /// + /// 客户标识 + public Customer( int id ) : base( id ) { + } + + /// + /// 编码 + /// + [Description( "编码" )] + [MaxLength( 100 )] + public string Code { get; set; } + /// + /// 姓名 + /// + [Description( "姓名" )] + [MaxLength( 200 )] + public string Name { get; set; } + /// + /// 昵称 + /// + [Description( "昵称" )] + [MaxLength( 50 )] + public string Nickname { get; set; } + /// + /// 性别 + /// + [Description( "性别" )] + public Gender? Gender { get; set; } + /// + /// 出生日期 + /// + [Description( "出生日期" )] + public DateTime? Birthday { get; set; } + /// + /// 民族 + /// + [Description( "民族" )] + public Nation? Nation { get; set; } + /// + /// 手机号 + /// + [Description( "手机号" )] + [MaxLength( 50 )] + public string Phone { get; set; } + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + [MaxLength( 500 )] + public string Email { get; set; } + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + + /// + /// 添加变更列表 + /// + protected override void AddChanges( Customer other ) { + AddChange( t => t.Code, other.Code ); + AddChange( t => t.Name, other.Name ); + AddChange( t => t.Nickname, other.Nickname ); + AddChange( t => t.Gender, other.Gender ); + AddChange( t => t.Birthday, other.Birthday ); + AddChange( t => t.Nation, other.Nation ); + AddChange( t => t.Phone, other.Phone ); + AddChange( t => t.Email, other.Email ); + AddChange( t => t.CreationTime, other.CreationTime ); + AddChange( t => t.CreatorId, other.CreatorId ); + AddChange( t => t.LastModificationTime, other.LastModificationTime ); + AddChange( t => t.LastModifierId, other.LastModifierId ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Models/OperationLog.cs b/test/Util.TestShare/Models/OperationLog.cs new file mode 100644 index 000000000..d6dc99fee --- /dev/null +++ b/test/Util.TestShare/Models/OperationLog.cs @@ -0,0 +1,66 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Domain.Entities; + +namespace Util.Tests.Models; + +/// +/// 操作日志 +/// +[Description( "操作日志" )] +public class OperationLog : AggregateRoot { + /// + /// 初始化操作日志 + /// + public OperationLog() : this( 0 ) { + } + + /// + /// 初始化操作日志 + /// + /// 操作日志标识 + public OperationLog( long id ) : base( id ) { + } + + /// + /// 日志名称 + /// + [Description( "日志名称" )] + [Required( ErrorMessage = "日志名称不能为空" )] + [MaxLength( 50 )] + public string LogName { get; set; } + /// + /// 操作时间 + /// + [Description( "操作时间" )] + public DateTime? OperationTime { get; set; } + /// + /// 标题 + /// + [Description( "标题" )] + [MaxLength( 200 )] + public string Caption { get; set; } + /// + /// 内容 + /// + [Description( "内容" )] + public string Content { get; set; } + + /// + /// 添加变更列表 + /// + protected override void AddChanges( OperationLog other ) { + AddChange( t => t.LogName, other.LogName ); + AddChange( t => t.OperationTime, other.OperationTime ); + AddChange( t => t.Caption, other.Caption ); + AddChange( t => t.Content, other.Content ); + } + + /// + /// 初始化 + /// + public override void Init() { + OperationTime = DateTime.Now; + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Models/Order.cs b/test/Util.TestShare/Models/Order.cs new file mode 100644 index 000000000..b1569bc99 --- /dev/null +++ b/test/Util.TestShare/Models/Order.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Domain; +using Util.Domain.Entities; + +namespace Util.Tests.Models; + +/// +/// 订单 +/// +[Description( "订单" )] +public class Order : AggregateRoot, IDelete { + /// + /// 初始化订单 + /// + public Order() : this( string.Empty ) { + } + + /// + /// 初始化订单 + /// + /// 订单标识 + public Order( string id ) : base( id ) { + OrderItems = new List(); + } + + /// + /// 客户标识 + /// + [Description( "客户标识" )] + public int? CustomerId { get; set; } + /// + /// 客户名称 + /// + [Description( "客户名称" )] + [MaxLength( 200 )] + public string CustomerName { get; set; } + /// + /// 金额 + /// + [Description( "金额" )] + [Required( ErrorMessage = "金额不能为空" )] + public decimal Amount { get; set; } + /// + /// 下单时间 + /// + [Description( "下单时间" )] + public DateTime? PlaceOrderTime { get; set; } + /// + /// 订单状态 + /// + [Description( "订单状态" )] + [Required( ErrorMessage = "订单状态不能为空" )] + public int State { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + + /// + /// 订单项列表 + /// + public ICollection OrderItems { get; set; } + + /// + /// 添加变更列表 + /// + protected override void AddChanges( Order other ) { + AddChange( t => t.CustomerId, other.CustomerId ); + AddChange( t => t.CustomerName, other.CustomerName ); + AddChange( t => t.Amount, other.Amount ); + AddChange( t => t.PlaceOrderTime, other.PlaceOrderTime ); + AddChange( t => t.State, other.State ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Models/OrderItem.cs b/test/Util.TestShare/Models/OrderItem.cs new file mode 100644 index 000000000..ab27b25d5 --- /dev/null +++ b/test/Util.TestShare/Models/OrderItem.cs @@ -0,0 +1,70 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Domain.Entities; + +namespace Util.Tests.Models; + +/// +/// 订单明细 +/// +[Description( "订单明细" )] +public class OrderItem : EntityBase { + /// + /// 初始化订单明细 + /// + public OrderItem() : this( Guid.Empty ) { + } + + /// + /// 初始化订单明细 + /// + /// 订单明细标识 + public OrderItem( Guid id ) : base( id ) { + } + + /// + /// 订单标识 + /// + [Description( "订单标识" )] + [Required( ErrorMessage = "订单标识不能为空" )] + [MaxLength( 256 )] + public string OrderId { get; set; } + /// + /// 产品标识 + /// + [Description( "产品标识" )] + public Guid? ProductId { get; set; } + /// + /// 产品名称 + /// + [Description( "产品名称" )] + [MaxLength( 500 )] + public string ProductName { get; set; } + /// + /// 单价 + /// + [Description( "单价" )] + public decimal Price { get; set; } + /// + /// 数量 + /// + [Description( "数量" )] + public int Quantity { get; set; } + + /// + /// 订单 + /// + public Order Order { get; set; } + + /// + /// 添加变更列表 + /// + protected override void AddChanges( OrderItem other ) { + AddChange( t => t.OrderId, other.OrderId ); + AddChange( t => t.ProductId, other.ProductId ); + AddChange( t => t.ProductName, other.ProductName ); + AddChange( t => t.Price, other.Price ); + AddChange( t => t.Quantity, other.Quantity ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Models/Post.cs b/test/Util.TestShare/Models/Post.cs new file mode 100644 index 000000000..c3ae0f66d --- /dev/null +++ b/test/Util.TestShare/Models/Post.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Domain; +using Util.Domain.Auditing; +using Util.Domain.Entities; + +namespace Util.Tests.Models; + +/// +/// 贴子 +/// +[Description( "贴子" )] +public class Post : AggregateRoot, IDelete, IAudited { + /// + /// 初始化贴子 + /// + public Post() : this( Guid.Empty ) { + } + + /// + /// 初始化贴子 + /// + /// 贴子标识 + public Post( Guid id ) : base( id ) { + Tags = new List(); + } + + /// + /// 标题 + /// + [Description( "标题" )] + [MaxLength( 200 )] + public string Title { get; set; } + /// + /// 内容 + /// + [Description( "内容" )] + public string Content { get; set; } + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + + /// + /// 标签列表 + /// + public ICollection Tags { get; set; } + + /// + /// 添加变更列表 + /// + protected override void AddChanges( Post other ) { + AddChange( t => t.Title, other.Title ); + AddChange( t => t.Content, other.Content ); + AddChange( t => t.CreationTime, other.CreationTime ); + AddChange( t => t.CreatorId, other.CreatorId ); + AddChange( t => t.LastModificationTime, other.LastModificationTime ); + AddChange( t => t.LastModifierId, other.LastModifierId ); + AddChange( t => t.ExtraProperties, other.ExtraProperties ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Models/Product.cs b/test/Util.TestShare/Models/Product.cs new file mode 100644 index 000000000..c78aac4d9 --- /dev/null +++ b/test/Util.TestShare/Models/Product.cs @@ -0,0 +1,221 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Util.Domain; +using Util.Domain.Auditing; +using Util.Domain.Entities; +using Util.Domain.Extending; +using Util.Tenants; +using Util.Tests.Events; + +namespace Util.Tests.Models; + +/// +/// 产品 +/// +[Description( "产品" )] +public class Product : AggregateRoot, IDelete, ITenant, IAudited, IExtraProperties { + /// + /// 初始化产品 + /// + public Product() : this( Guid.Empty ) { + } + + /// + /// 初始化产品 + /// + /// 产品标识 + public Product( Guid id ) : base( id ) { + TestProperty2 = new ProductItem(); + TestProperty4 = new ProductItem2(); + TestProperties = new List(); + } + + /// + /// 产品编码 + /// + [Description( "产品编码" )] + [MaxLength( 50 )] + public string Code { get; set; } + /// + /// 产品名称 + /// + [Description( "产品名称" )] + [MaxLength( 500 )] + public string Name { get; set; } + /// + /// 价格 + /// + [Description( "价格" )] + public decimal Price { get; set; } + /// + /// 价格 + /// + [Description( "价格" )] + public int IntPrice { get; set; } + /// + /// 价格 + /// + [Description( "价格" )] + public long LongPrice { get; set; } + /// + /// 价格 + /// + [Description( "价格" )] + public float FloatPrice { get; set; } + /// + /// 描述 + /// + [Description( "描述" )] + public string Description { get; set; } + /// + /// 启用 + /// + [Description( "启用" )] + public bool Enabled { get; set; } + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + /// + /// 租户标识 + /// + [Description( "租户标识" )] + public string TenantId { get; set; } + + /// + /// 简单扩展属性 + /// + [NotMapped] + public string TestProperty1 { + get => ExtraProperties.GetProperty( nameof( TestProperty1 ) ); + set => ExtraProperties.SetProperty( nameof( TestProperty1 ), value ); + } + + private readonly ExtraProperty _property2 = new( nameof( TestProperty2 ) ); + /// + /// 对象扩展属性 + /// + [NotMapped] + public ProductItem TestProperty2 { + get => _property2.GetProperty( ExtraProperties ); + set => _property2.SetProperty( ExtraProperties, value ); + } + + /// + /// 枚举扩展属性 + /// + [NotMapped] + public ProductEnum? TestProperty3 { + get => ExtraProperties.GetProperty( nameof( TestProperty3 ) ); + set => ExtraProperties.SetProperty( nameof( TestProperty3 ), value ); + } + + private readonly ExtraProperty _property4 = new( nameof( TestProperty4 ) ); + /// + /// 不可变对象扩展属性 + /// + [NotMapped] + public ProductItem2 TestProperty4 { + get => _property4.GetProperty( ExtraProperties ); + set => _property4.SetProperty( ExtraProperties, value ); + } + + private readonly ExtraProperty> _properties = new( nameof( TestProperties ) ); + /// + /// 对象集合扩展属性 + /// + [NotMapped] + public List TestProperties { + get => _properties.GetProperty( ExtraProperties ); + set => _properties.SetProperty( ExtraProperties, value ); + } + + private readonly ExtraProperty _property5 = new( nameof( TestProperty5 ) ); + /// + /// 不可变对象扩展属性 + /// + [NotMapped] + public ApplicationExtend TestProperty5 { + get => _property5.GetProperty( ExtraProperties ); + set => _property5.SetProperty( ExtraProperties, value ); + } + + /// + /// 日期扩展属性 + /// + [NotMapped] + public DateTime TestProperty6 { + get => ExtraProperties.GetProperty( nameof( TestProperty6 ) ); + set => ExtraProperties.SetProperty( nameof( TestProperty6 ), value ); + } + + private readonly ExtraProperty> _property7 = new( nameof( TaskRecordList ) ); + /// + /// 执行记录 + /// + [NotMapped] + public List TaskRecordList { + get => _property7.GetProperty( ExtraProperties ); + set => _property7.SetProperty( ExtraProperties, value ); + } + + /// + /// 添加变更列表 + /// + protected override void AddChanges( Product other ) { + AddChange( t => t.Code, other.Code ); + AddChange( t => t.Name, other.Name ); + AddChange( t => t.Price, other.Price ); + AddChange( t => t.Description, other.Description ); + AddChange( t => t.Enabled, other.Enabled ); + AddChange( t => t.CreationTime, other.CreationTime ); + AddChange( t => t.CreatorId, other.CreatorId ); + AddChange( t => t.LastModificationTime, other.LastModificationTime ); + AddChange( t => t.LastModifierId, other.LastModifierId ); + } + + /// + /// 测试领域事件方法1 - 添加1个本地事件 TestEvent2 + /// + public void TestDomainEvent1() { + AddDomainEvent( new TestEvent2 { Id = Id, Name = "a" } ); + } + + /// + /// 测试领域事件方法2 - 添加1个集成事件 TestEvent3 + /// + public void TestDomainEvent2() { + AddDomainEvent( new TestEvent3( Id, "a" ) ); + } + + /// + /// 获取扩展属性Json + /// + public string GetExtraPropertiesJson() { + return Util.Helpers.Json.ToJson( ExtraProperties ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Models/ProductEnum.cs b/test/Util.TestShare/Models/ProductEnum.cs new file mode 100644 index 000000000..c68ea5e24 --- /dev/null +++ b/test/Util.TestShare/Models/ProductEnum.cs @@ -0,0 +1,9 @@ +namespace Util.Tests.Models; + +/// +/// 用于测试枚举扩展属性 +/// +public enum ProductEnum { + Hello, + World +} \ No newline at end of file diff --git a/test/Util.TestShare/Models/ProductItem.cs b/test/Util.TestShare/Models/ProductItem.cs new file mode 100644 index 000000000..c2400207a --- /dev/null +++ b/test/Util.TestShare/Models/ProductItem.cs @@ -0,0 +1,19 @@ +namespace Util.Tests.Models; + +/// +/// 用于测试扩展属性 +/// +public class ProductItem { + /// + /// 编码 + /// + public int Code { get; set; } + /// + /// 名称 + /// + public string Name { get; set; } + /// + /// 枚举 + /// + public ProductEnum EnumValue { get; set; } +} \ No newline at end of file diff --git a/test/Util.TestShare/Models/ProductItem2.cs b/test/Util.TestShare/Models/ProductItem2.cs new file mode 100644 index 000000000..163eea4e6 --- /dev/null +++ b/test/Util.TestShare/Models/ProductItem2.cs @@ -0,0 +1,25 @@ + +namespace Util.Tests.Models; + +/// +/// 用于测试不可变扩展属性 +/// +public class ProductItem2 { + + public ProductItem2() { + } + + public ProductItem2( int code, string name ) { + Code = code; + Name = name; + } + + /// + /// 编码 + /// + public int Code { get; init; } + /// + /// 名称 + /// + public string Name { get; init; } +} \ No newline at end of file diff --git a/test/Util.TestShare/Models/Resource.cs b/test/Util.TestShare/Models/Resource.cs new file mode 100644 index 000000000..09b94b7f0 --- /dev/null +++ b/test/Util.TestShare/Models/Resource.cs @@ -0,0 +1,93 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Domain; +using Util.Domain.Auditing; +using Util.Domain.Extending; +using Util.Domain.Trees; + +namespace Util.Tests.Models; + +/// +/// 资源 +/// +[Description( "资源" )] +public class Resource : TreeEntityBase,IDelete,IAudited,IExtraProperties { + /// + /// 初始化资源 + /// + public Resource() : this( Guid.Empty, "", 0 ) { + } + + /// + /// 初始化资源 + /// + /// 资源标识 + /// 路径 + /// 层级 + public Resource( Guid id, string path, int level ) : base( id, path, level ) { + } + + /// + /// 资源标识符 + /// + [DisplayName( "资源标识符" )] + [MaxLength( 300 )] + public string Uri { get; set; } + /// + /// 资源名称 + /// + [DisplayName( "资源名称" )] + [Required] + [MaxLength( 200 )] + public string Name { get; set; } + /// + /// 备注 + /// + [DisplayName( "备注" )] + [MaxLength( 500 )] + public string Remark { get; set; } + /// + /// 创建时间 + /// + [DisplayName( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人标识 + /// + [DisplayName( "创建人标识" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [DisplayName( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人标识 + /// + [DisplayName( "最后修改人标识" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [DisplayName( "是否删除" )] + public bool IsDeleted { get; set; } + + /// + /// 添加变更列表 + /// + protected override void AddChanges( Resource other ) { + AddChange( t => t.Uri, other.Uri ); + AddChange( t => t.Name, other.Name ); + AddChange( t => t.ParentId, other.ParentId ); + AddChange( t => t.Path, other.Path ); + AddChange( t => t.Level, other.Level ); + AddChange( t => t.Remark, other.Remark ); + AddChange( t => t.Enabled, other.Enabled ); + AddChange( t => t.SortId, other.SortId ); + AddChange( t => t.CreationTime, other.CreationTime ); + AddChange( t => t.CreatorId, other.CreatorId ); + AddChange( t => t.LastModificationTime, other.LastModificationTime ); + AddChange( t => t.LastModifierId, other.LastModifierId ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Models/Tag.cs b/test/Util.TestShare/Models/Tag.cs new file mode 100644 index 000000000..80cc20229 --- /dev/null +++ b/test/Util.TestShare/Models/Tag.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Domain; +using Util.Domain.Auditing; +using Util.Domain.Entities; + +namespace Util.Tests.Models; + +/// +/// 标签 +/// +[Description( "标签" )] +public class Tag : AggregateRoot,IDelete,IAudited{ + /// + /// 初始化标签 + /// + public Tag() : this( Guid.Empty ) { + } + + /// + /// 初始化标签 + /// + /// 标签标识 + public Tag( Guid id ) : base( id ) { + Posts = new List(); + } + + /// + /// 标签名称 + /// + [Description( "标签名称" )] + [MaxLength( 200 )] + public string Name { get; set; } + /// + /// 创建时间 + /// + [Description( "创建时间" )] + public DateTime? CreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 最后修改时间 + /// + [Description( "最后修改时间" )] + public DateTime? LastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } + /// + /// 是否删除 + /// + [Description( "是否删除" )] + public bool IsDeleted { get; set; } + + /// + /// 贴子列表 + /// + public ICollection Posts { get; set; } + + /// + /// 添加变更列表 + /// + protected override void AddChanges( Tag other ) { + AddChange( t => t.Name, other.Name ); + AddChange( t => t.CreationTime, other.CreationTime ); + AddChange( t => t.CreatorId, other.CreatorId ); + AddChange( t => t.LastModificationTime, other.LastModificationTime ); + AddChange( t => t.LastModifierId, other.LastModifierId ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Models/TaskRecord.cs b/test/Util.TestShare/Models/TaskRecord.cs new file mode 100644 index 000000000..8affa5ece --- /dev/null +++ b/test/Util.TestShare/Models/TaskRecord.cs @@ -0,0 +1,13 @@ +using System; + +namespace Util.Tests.Models; + +/// +/// 任务记录 +/// +public class TaskRecord { + /// + /// 执行时间 + /// + public DateTime ExecutionTime { get; set; } +} \ No newline at end of file diff --git a/test/Util.TestShare/Queries/CustomerQuery.cs b/test/Util.TestShare/Queries/CustomerQuery.cs new file mode 100644 index 000000000..cb343080a --- /dev/null +++ b/test/Util.TestShare/Queries/CustomerQuery.cs @@ -0,0 +1,125 @@ +using System; +using System.ComponentModel; +using Util.Data.Queries; +using Util.Domain.Biz.Enums; + +namespace Util.Tests.Queries; + +/// +/// 客户查询参数 +/// +public class CustomerQuery : QueryParameter { + /// + /// 客户标识 + /// + [Description( "客户标识" )] + public int? CustomerId { get; set; } + /// + /// 编码 + /// + private string _code = string.Empty; + /// + /// 编码 + /// + [Description( "编码" )] + public string Code { + get => _code == null ? string.Empty : _code.Trim(); + set => _code = value; + } + /// + /// 姓名 + /// + private string _name = string.Empty; + /// + /// 姓名 + /// + [Description( "姓名" )] + public string Name { + get => _name == null ? string.Empty : _name.Trim(); + set => _name = value; + } + /// + /// 昵称 + /// + private string _nickname = string.Empty; + /// + /// 昵称 + /// + [Description( "昵称" )] + public string Nickname { + get => _nickname == null ? string.Empty : _nickname.Trim(); + set => _nickname = value; + } + /// + /// 性别 + /// + [Description( "性别" )] + public Gender? Gender { get; set; } + /// + /// 起始出生日期 + /// + public DateTime? BeginBirthday { get; set; } + /// + /// 结束出生日期 + /// + public DateTime? EndBirthday { get; set; } + /// + /// 民族 + /// + [Description( "民族" )] + public Nation? Nation { get; set; } + /// + /// 手机号 + /// + private string _phone = string.Empty; + /// + /// 手机号 + /// + [Description( "手机号" )] + public string Phone { + get => _phone == null ? string.Empty : _phone.Trim(); + set => _phone = value; + } + /// + /// 电子邮件 + /// + private string _email = string.Empty; + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + public string Email { + get => _email == null ? string.Empty : _email.Trim(); + set => _email = value; + } + /// + /// 年龄 + /// + public int? Age { get; set; } + /// + /// 起始创建时间 + /// + public DateTime? BeginCreationTime { get; set; } + /// + /// 结束创建时间 + /// + public DateTime? EndCreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 起始最后修改时间 + /// + public DateTime? BeginLastModificationTime { get; set; } + /// + /// 结束最后修改时间 + /// + public DateTime? EndLastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } +} \ No newline at end of file diff --git a/test/Util.TestShare/Queries/ProductQuery.cs b/test/Util.TestShare/Queries/ProductQuery.cs new file mode 100644 index 000000000..e5214e223 --- /dev/null +++ b/test/Util.TestShare/Queries/ProductQuery.cs @@ -0,0 +1,88 @@ +using System; +using System.ComponentModel; +using Util.Data.Queries; + +namespace Util.Tests.Queries; + +/// +/// 产品查询参数 +/// +public class ProductQuery : QueryParameter { + /// + /// 产品标识 + /// + [Description( "产品标识" )] + public Guid? ProductId { get; set; } + /// + /// 产品编码 + /// + private string _code = string.Empty; + /// + /// 产品编码 + /// + [Description( "产品编码" )] + public string Code { + get => _code == null ? string.Empty : _code.Trim(); + set => _code = value; + } + /// + /// 产品名称 + /// + private string _name = string.Empty; + /// + /// 产品名称 + /// + [Description( "产品名称" )] + public string Name { + get => _name == null ? string.Empty : _name.Trim(); + set => _name = value; + } + /// + /// 价格 + /// + [Description( "价格" )] + public decimal? Price { get; set; } + /// + /// 描述 + /// + private string _description = string.Empty; + /// + /// 描述 + /// + [Description( "描述" )] + public string Description { + get => _description == null ? string.Empty : _description.Trim(); + set => _description = value; + } + /// + /// 启用 + /// + [Description( "启用" )] + public bool? Enabled { get; set; } + /// + /// 起始创建时间 + /// + public DateTime? BeginCreationTime { get; set; } + /// + /// 结束创建时间 + /// + public DateTime? EndCreationTime { get; set; } + /// + /// 创建人 + /// + [Description( "创建人" )] + public Guid? CreatorId { get; set; } + /// + /// 起始最后修改时间 + /// + public DateTime? BeginLastModificationTime { get; set; } + /// + /// 结束最后修改时间 + /// + public DateTime? EndLastModificationTime { get; set; } + /// + /// 最后修改人 + /// + [Description( "最后修改人" )] + public Guid? LastModifierId { get; set; } +} \ No newline at end of file diff --git a/test/Util.TestShare/Queries/ResourceQuery.cs b/test/Util.TestShare/Queries/ResourceQuery.cs new file mode 100644 index 000000000..b6d745469 --- /dev/null +++ b/test/Util.TestShare/Queries/ResourceQuery.cs @@ -0,0 +1,31 @@ +using System; +using System.ComponentModel; +using Util.Data.Trees; + +namespace Util.Tests.Queries; + +/// +/// 资源查询参数 +/// +public class ResourceQuery : TreeQueryParameter { + /// + /// 资源标识 + /// + [Description( "资源标识" )] + public Guid? ResourceId { get; set; } + /// + /// 资源标识符 + /// + [Description( "资源标识符" )] + public string Uri { get; set; } + /// + /// 资源名称 + /// + [Description( "资源名称" )] + public string Name { get; set; } + /// + /// 备注 + /// + [Description( "备注" )] + public string Remark { get; set; } +} \ No newline at end of file diff --git a/test/Util.TestShare/Repositories/ICustomerRepository.cs b/test/Util.TestShare/Repositories/ICustomerRepository.cs new file mode 100644 index 000000000..0fc38ca06 --- /dev/null +++ b/test/Util.TestShare/Repositories/ICustomerRepository.cs @@ -0,0 +1,10 @@ +using Util.Domain.Repositories; +using Util.Tests.Models; + +namespace Util.Tests.Repositories; + +/// +/// 客户仓储 +/// +public interface ICustomerRepository : IRepository { +} \ No newline at end of file diff --git a/test/Util.TestShare/Repositories/IOperationLogRepository.cs b/test/Util.TestShare/Repositories/IOperationLogRepository.cs new file mode 100644 index 000000000..7351b1179 --- /dev/null +++ b/test/Util.TestShare/Repositories/IOperationLogRepository.cs @@ -0,0 +1,10 @@ +using Util.Domain.Repositories; +using Util.Tests.Models; + +namespace Util.Tests.Repositories; + +/// +/// 操作日志仓储 +/// +public interface IOperationLogRepository : IRepository { +} \ No newline at end of file diff --git a/test/Util.TestShare/Repositories/IOrderRepository.cs b/test/Util.TestShare/Repositories/IOrderRepository.cs new file mode 100644 index 000000000..457f4370c --- /dev/null +++ b/test/Util.TestShare/Repositories/IOrderRepository.cs @@ -0,0 +1,10 @@ +using Util.Domain.Repositories; +using Util.Tests.Models; + +namespace Util.Tests.Repositories; + +/// +/// 订单仓储 +/// +public interface IOrderRepository : IRepository { +} \ No newline at end of file diff --git a/test/Util.TestShare/Repositories/IPostRepository.cs b/test/Util.TestShare/Repositories/IPostRepository.cs new file mode 100644 index 000000000..edd3a7e0a --- /dev/null +++ b/test/Util.TestShare/Repositories/IPostRepository.cs @@ -0,0 +1,10 @@ +using Util.Domain.Repositories; +using Util.Tests.Models; + +namespace Util.Tests.Repositories; + +/// +/// 贴子仓储 +/// +public interface IPostRepository : IRepository { +} \ No newline at end of file diff --git a/test/Util.TestShare/Repositories/IProductRepository.cs b/test/Util.TestShare/Repositories/IProductRepository.cs new file mode 100644 index 000000000..2ff867721 --- /dev/null +++ b/test/Util.TestShare/Repositories/IProductRepository.cs @@ -0,0 +1,15 @@ +using Util.Domain.Repositories; +using Util.Tests.Models; + +namespace Util.Tests.Repositories; + +/// +/// 产品仓储 +/// +public interface IProductRepository : IRepository { + /// + /// 更新产品 + /// + /// 产品 + public void UpdateProduct( Product product ); +} \ No newline at end of file diff --git a/test/Util.TestShare/Repositories/IResourceRepository.cs b/test/Util.TestShare/Repositories/IResourceRepository.cs new file mode 100644 index 000000000..fb108b0eb --- /dev/null +++ b/test/Util.TestShare/Repositories/IResourceRepository.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using System; +using Util.Domain.Trees; +using Util.Tests.Models; + +namespace Util.Tests.Repositories; + +/// +/// 资源仓储 +/// +public interface IResourceRepository : ITreeRepository { + /// + /// 生成排序号 + /// + /// 父标识 + Task GenerateSortIdAsync( Guid? parentId ); +} \ No newline at end of file diff --git a/test/Util.TestShare/Repositories/ITagRepository.cs b/test/Util.TestShare/Repositories/ITagRepository.cs new file mode 100644 index 000000000..63db0b411 --- /dev/null +++ b/test/Util.TestShare/Repositories/ITagRepository.cs @@ -0,0 +1,10 @@ +using Util.Domain.Repositories; +using Util.Tests.Models; + +namespace Util.Tests.Repositories; + +/// +/// 标签仓储 +/// +public interface ITagRepository : IRepository { +} \ No newline at end of file diff --git a/test/Util.TestShare/Services/CustomerQueryService.cs b/test/Util.TestShare/Services/CustomerQueryService.cs new file mode 100644 index 000000000..20d92a71e --- /dev/null +++ b/test/Util.TestShare/Services/CustomerQueryService.cs @@ -0,0 +1,32 @@ +using System; +using System.Linq; +using Util.Applications; +using Util.Data; +using Util.Tests.Dtos; +using Util.Tests.Models; +using Util.Tests.Queries; +using Util.Tests.Repositories; + +namespace Util.Tests.Services; + +/// +/// 客户查询服务 +/// +public class CustomerQueryService : QueryServiceBase,ICustomerQueryService { + /// + /// 初始化客户查询服务 + /// + /// 服务提供器 + /// 客户仓储 + public CustomerQueryService( IServiceProvider serviceProvider,ICustomerRepository repository ) : base( serviceProvider,repository ) { + } + + /// + /// 过滤 + /// + /// 查询对象 + /// 查询参数 + protected override IQueryable Filter( IQueryable queryable, CustomerQuery query ) { + return queryable.WhereIfNotEmpty( t => t.Name == query.Name ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Services/CustomerService.cs b/test/Util.TestShare/Services/CustomerService.cs new file mode 100644 index 000000000..4bdf576d6 --- /dev/null +++ b/test/Util.TestShare/Services/CustomerService.cs @@ -0,0 +1,34 @@ +using System; +using System.Linq; +using Util.Applications; +using Util.Data; +using Util.Tests.Dtos; +using Util.Tests.Models; +using Util.Tests.Queries; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Services; + +/// +/// 客户服务 +/// +public class CustomerService : CrudServiceBase, ICustomerService { + /// + /// 初始化客户服务 + /// + /// 服务提供器 + /// 工作单元 + /// 仓储 + public CustomerService( IServiceProvider serviceProvider, ITestUnitOfWork unitOfWork, ICustomerRepository repository ) : base( serviceProvider,unitOfWork, repository ) { + } + + /// + /// 过滤 + /// + /// 查询对象 + /// 查询参数 + protected override IQueryable Filter( IQueryable queryable, CustomerQuery query ) { + return queryable.WhereIfNotEmpty( t => t.Name == query.Name ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Services/ICustomerQueryService.cs b/test/Util.TestShare/Services/ICustomerQueryService.cs new file mode 100644 index 000000000..0503c03f6 --- /dev/null +++ b/test/Util.TestShare/Services/ICustomerQueryService.cs @@ -0,0 +1,11 @@ +using Util.Applications; +using Util.Tests.Dtos; +using Util.Tests.Queries; + +namespace Util.Tests.Services; + +/// +/// 客户查询服务 +/// +public interface ICustomerQueryService : IQueryService { +} \ No newline at end of file diff --git a/test/Util.TestShare/Services/ICustomerService.cs b/test/Util.TestShare/Services/ICustomerService.cs new file mode 100644 index 000000000..b73559e52 --- /dev/null +++ b/test/Util.TestShare/Services/ICustomerService.cs @@ -0,0 +1,11 @@ +using Util.Applications; +using Util.Tests.Dtos; +using Util.Tests.Queries; + +namespace Util.Tests.Services; + +/// +/// 客户服务 +/// +public interface ICustomerService : ICrudService { +} \ No newline at end of file diff --git a/test/Util.TestShare/Services/IProductService.cs b/test/Util.TestShare/Services/IProductService.cs new file mode 100644 index 000000000..2d1bba1c7 --- /dev/null +++ b/test/Util.TestShare/Services/IProductService.cs @@ -0,0 +1,11 @@ +using Util.Applications; +using Util.Tests.Dtos; +using Util.Tests.Queries; + +namespace Util.Tests.Services; + +/// +/// 产品服务 +/// +public interface IProductService : ICrudService { +} \ No newline at end of file diff --git a/test/Util.TestShare/Services/IResourceService.cs b/test/Util.TestShare/Services/IResourceService.cs new file mode 100644 index 000000000..c4658ed75 --- /dev/null +++ b/test/Util.TestShare/Services/IResourceService.cs @@ -0,0 +1,11 @@ +using Util.Applications.Trees; +using Util.Tests.Dtos; +using Util.Tests.Queries; + +namespace Util.Tests.Services; + +/// +/// 资源服务 +/// +public interface IResourceService : ITreeService { +} \ No newline at end of file diff --git a/test/Util.TestShare/Services/ProductService.cs b/test/Util.TestShare/Services/ProductService.cs new file mode 100644 index 000000000..b5efd830a --- /dev/null +++ b/test/Util.TestShare/Services/ProductService.cs @@ -0,0 +1,23 @@ +using System; +using Util.Applications; +using Util.Tests.Dtos; +using Util.Tests.Models; +using Util.Tests.Queries; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Services; + +/// +/// 产品服务 +/// +public class ProductService : CrudServiceBase, IProductService { + /// + /// 初始化产品服务 + /// + /// 服务提供器 + /// 工作单元 + /// 仓储 + public ProductService( IServiceProvider serviceProvider, ITestUnitOfWork unitOfWork, IProductRepository repository ) : base( serviceProvider,unitOfWork, repository ) { + } +} \ No newline at end of file diff --git a/test/Util.TestShare/Services/ResourceService.cs b/test/Util.TestShare/Services/ResourceService.cs new file mode 100644 index 000000000..9bf652f71 --- /dev/null +++ b/test/Util.TestShare/Services/ResourceService.cs @@ -0,0 +1,37 @@ +using System; +using System.Threading.Tasks; +using Util.Applications.Trees; +using Util.Tests.Dtos; +using Util.Tests.Models; +using Util.Tests.Queries; +using Util.Tests.Repositories; +using Util.Tests.UnitOfWorks; + +namespace Util.Tests.Services; + +/// +/// 资源服务 +/// +public class ResourceService : TreeServiceBase, IResourceService { + /// + /// 仓储 + /// + private IResourceRepository _repository; + + /// + /// 初始化资源服务 + /// + /// 服务提供器 + /// 工作单元 + /// 仓储 + public ResourceService( IServiceProvider serviceProvider,ITestUnitOfWork unitOfWork, IResourceRepository repository ) : base( serviceProvider,unitOfWork,repository ) { + _repository = repository; + } + + /// + /// 创建前操作 + /// + protected override async Task CreateBeforeAsync( Resource entity ) { + entity.SortId = await _repository.GenerateSortIdAsync( entity.ParentId ); + } +} \ No newline at end of file diff --git a/test/Util.TestShare/UnitOfWorks/ITestUnitOfWork.cs b/test/Util.TestShare/UnitOfWorks/ITestUnitOfWork.cs new file mode 100644 index 000000000..daf4032b9 --- /dev/null +++ b/test/Util.TestShare/UnitOfWorks/ITestUnitOfWork.cs @@ -0,0 +1,15 @@ +using Util.Data; + +namespace Util.Tests.UnitOfWorks; + +/// +/// 工作单元 +/// +public interface ITestUnitOfWork : IUnitOfWork { +} + +/// +/// 工作单元2 +/// +public interface ITestUnitOfWork2 : IUnitOfWork { +} \ No newline at end of file diff --git a/test/Util.TestShare/Util.TestShare.csproj b/test/Util.TestShare/Util.TestShare.csproj new file mode 100644 index 000000000..57c7af72d --- /dev/null +++ b/test/Util.TestShare/Util.TestShare.csproj @@ -0,0 +1,15 @@ + + + $(NetTargetFramework) + false + Util.Tests + + + + + + + + + + diff --git a/test/Util.Tests/01-Util.Tests.csproj b/test/Util.Tests/01-Util.Tests.csproj deleted file mode 100644 index 6279a4f8f..000000000 --- a/test/Util.Tests/01-Util.Tests.csproj +++ /dev/null @@ -1,59 +0,0 @@ - - - - netcoreapp2.0 - Util.Tests - Util.Tests - - 何镇汐;dotnetcore - 何镇汐;dotnetcore - - - - ..\..\output\test - - - - - ..\..\output\test - - - - - - - - - - - - - - - - - - - - - - - - True - True - TestResource.resx - - - - - - PublicResXFileCodeGenerator - TestResource.Designer.cs - - - - - - - - diff --git a/test/Util.Tests/Applications/CrudServiceTest.BatchSave.cs b/test/Util.Tests/Applications/CrudServiceTest.BatchSave.cs deleted file mode 100644 index 7f5aca20c..000000000 --- a/test/Util.Tests/Applications/CrudServiceTest.BatchSave.cs +++ /dev/null @@ -1,301 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using NSubstitute; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Applications { - /// - /// 增删改查服务测试 - 批量删除 - /// - public partial class CrudServiceTest { - /// - /// 测试添加空集合操作 - /// - [Fact] - public void TestSave_Add_Empty() { - _service.Save( DtoSample.EmptyList(), DtoSample.EmptyList(), DtoSample.EmptyList() ); - _repository.DidNotReceive().Add( Arg.Any() ); - } - - /// - /// 过滤新增列表 - 要删除的不需要新增 - /// - [Fact] - public void TestSave_FilterAddListByDeleteList() { - var input = new List { new DtoSample { Id = "1" } }; - _service.Save( input, DtoSample.EmptyList(), input ); - _repository.DidNotReceive().Add( Arg.Any() ); - } - - /// - /// 过滤更新列表 - 要删除的不需要更新 - /// - [Fact] - public void TestSave_FilterUpdateListByDeleteList() { - var input = new List { new DtoSample { Id = "1" } }; - _service.Save( DtoSample.EmptyList(), input, input ); - _repository.DidNotReceive().Update( Arg.Any() ); - } - - /// - /// 验证重复添加 - /// - [Fact] - public void TestSave_Add_Repeat() { - var input = new List { - new DtoSample{Id = _id.ToString()}, - new DtoSample{Id = _id.ToString()} - }; - _service.Save( input, DtoSample.EmptyList(), DtoSample.EmptyList() ); - _repository.Received( 1 ).Add( Arg.Any() ); - } - - /// - /// 验证重复更新 - /// - [Fact] - public void TestSave_Update_Repeat() { - _repository.Find( _id ).Returns( t => new EntitySample( _id ) ); - var input = new List { - new DtoSample{Id = _id.ToString()}, - new DtoSample{Id = _id.ToString()} - }; - _service.Save( DtoSample.EmptyList(), input, DtoSample.EmptyList() ); - _repository.Received( 1 ).Update( Arg.Any() ); - } - - /// - /// 验证重复删除 - /// - [Fact] - public void TestSave_Delete_Repeat() { - var input = new List { - new DtoSample{Id = _id.ToString()}, - new DtoSample{Id = _id.ToString()} - }; - _service.Save( DtoSample.EmptyList(), DtoSample.EmptyList(), input ); - _repository.Received( 1 ).Remove( Arg.Any() ); - } - - /// - /// 添加1个项 - /// - [Fact] - public void TestSave_Add_1() { - var input = new List { - new DtoSample{Id = _id.ToString()} - }; - _service.Save( input, DtoSample.EmptyList(), DtoSample.EmptyList() ); - _repository.Received( 1 ).Add( Arg.Any() ); - _repository.Received().Add( Arg.Is( t => t.Id == _id ) ); - } - - /// - /// 添加2个项 - /// - [Fact] - public void TestSave_Add_2() { - var input = new List { - new DtoSample{Id = _id.ToString()}, - new DtoSample{Id = _id2.ToString()} - }; - _service.Save( input, DtoSample.EmptyList(), DtoSample.EmptyList() ); - _repository.Received( 2 ).Add( Arg.Any() ); - _repository.Received().Add( Arg.Is( t => t.Id == _id ) ); - _repository.Received().Add( Arg.Is( t => t.Id == _id2 ) ); - } - - /// - /// 修改1个项 - /// - [Fact] - public void TestSave_Update_1() { - _repository.Find( _id ).Returns( t => new EntitySample( _id ) ); - var input = new List { - new DtoSample{Id = _id.ToString(),Name = "b"} - }; - _service.Save( DtoSample.EmptyList(), input, DtoSample.EmptyList() ); - _repository.Received( 1 ).Update( Arg.Any() ); - _repository.Received().Update( Arg.Is( t => t.Id == _id && t.Name == "b" ) ); - } - - /// - /// 修改2个项 - /// - [Fact] - public void TestSave_Update_2() { - _repository.Find( _id ).Returns( t => new EntitySample( _id ) ); - _repository.Find( _id2 ).Returns( t => new EntitySample( _id2 ) ); - var input = new List { - new DtoSample{Id = _id.ToString(),Name = "b"}, - new DtoSample{Id = _id2.ToString(),Name = "b"} - }; - _service.Save( DtoSample.EmptyList(), input, DtoSample.EmptyList() ); - _repository.Received( 2 ).Update( Arg.Any() ); - _repository.Received().Update( Arg.Is( t => t.Id == _id && t.Name == "b" ) ); - _repository.Received().Update( Arg.Is( t => t.Id == _id2 && t.Name == "b" ) ); - } - - /// - /// 删除1个项 - /// - [Fact] - public void TestSave_Delete_1() { - var input = new List { - new DtoSample{Id = _id.ToString()} - }; - _service.Save( DtoSample.EmptyList(), DtoSample.EmptyList(), input ); - _repository.Received( 1 ).Remove( Arg.Any() ); - _repository.Received().Remove( Arg.Is( id => id == _id ) ); - } - - /// - /// 删除2个项 - /// - [Fact] - public void TestSave_Delete_2() { - var input = new List { - new DtoSample{Id = _id.ToString()}, - new DtoSample{Id = _id2.ToString()} - }; - _service.Save( DtoSample.EmptyList(), DtoSample.EmptyList(), input ); - _repository.Received( 2 ).Remove( Arg.Any() ); - _repository.Received().Remove( Arg.Is( id => id == _id ) ); - _repository.Received().Remove( Arg.Is( id => id == _id2 ) ); - } - - /// - /// 测试保存 - /// - [Fact] - public void TestSave() { - var id = Guid.NewGuid(); - var id2 = Guid.NewGuid(); - var id3 = Guid.NewGuid(); - var id4 = Guid.NewGuid(); - var id5 = Guid.NewGuid(); - var addList = new List { - new DtoSample{Id = id.ToString()}, - new DtoSample{Id = id2.ToString()} - }; - var updateList = new List { - new DtoSample{Id = id3.ToString()}, - new DtoSample{Id = id4.ToString()}, - new DtoSample{Id = id5.ToString()} - }; - var deleteList = new List { - new DtoSample{Id = id3.ToString()} - }; - var result = _service.Save( addList, updateList, deleteList ); - _repository.Received( 2 ).Add( Arg.Any() ); - _repository.Received( 2 ).Update( Arg.Any() ); - _repository.Received( 1 ).Remove( Arg.Is( t => t == id3 ) ); - _unitOfWork.Received( 1 ).Commit(); - Assert.Equal( 4, result.Count ); - } - - /// - /// 添加1个项 - /// - [Fact] - public async Task TestSaveAsync_Add_1() { - var input = new List { - new DtoSample{Id = _id.ToString()} - }; - await _service.SaveAsync( input, DtoSample.EmptyList(), DtoSample.EmptyList() ); - await _repository.Received( 1 ).AddAsync( Arg.Any() ); - await _repository.Received().AddAsync( Arg.Is( t => t.Id == _id ) ); - } - - /// - /// 添加2个项 - /// - [Fact] - public async Task TestSaveAsync_Add_2() { - var input = new List { - new DtoSample{Id = _id.ToString()}, - new DtoSample{Id = _id2.ToString()} - }; - await _service.SaveAsync( input, DtoSample.EmptyList(), DtoSample.EmptyList() ); - await _repository.Received( 2 ).AddAsync( Arg.Any() ); - await _repository.Received().AddAsync( Arg.Is( t => t.Id == _id ) ); - await _repository.Received().AddAsync( Arg.Is( t => t.Id == _id2 ) ); - } - - /// - /// 修改1个项 - /// - [Fact] - public async Task TestSaveAsync_Update_1() { - _repository.Find( _id ).Returns( t => new EntitySample( _id ) ); - var input = new List { - new DtoSample{Id = _id.ToString(),Name = "b"} - }; - await _service.SaveAsync( DtoSample.EmptyList(), input, DtoSample.EmptyList() ); - await _repository.Received( 1 ).UpdateAsync( Arg.Any() ); - await _repository.Received().UpdateAsync( Arg.Is( t => t.Id == _id && t.Name == "b" ) ); - } - - /// - /// 修改2个项 - /// - [Fact] - public async Task TestSaveAsync_Update_2() { - _repository.Find( _id ).Returns( t => new EntitySample( _id ) ); - _repository.Find( _id2 ).Returns( t => new EntitySample( _id2 ) ); - var input = new List { - new DtoSample{Id = _id.ToString(),Name = "b"}, - new DtoSample{Id = _id2.ToString(),Name = "b"} - }; - await _service.SaveAsync( DtoSample.EmptyList(), input, DtoSample.EmptyList() ); - await _repository.Received( 2 ).UpdateAsync( Arg.Any() ); - await _repository.Received().UpdateAsync( Arg.Is( t => t.Id == _id && t.Name == "b" ) ); - await _repository.Received().UpdateAsync( Arg.Is( t => t.Id == _id2 && t.Name == "b" ) ); - } - - /// - /// 删除1个项 - /// - [Fact] - public async Task TestSaveAsync_Delete_1() { - var input = new List { - new DtoSample{Id = _id.ToString()} - }; - await _service.SaveAsync( DtoSample.EmptyList(), DtoSample.EmptyList(), input ); - await _repository.Received( 1 ).RemoveAsync( Arg.Any() ); - await _repository.Received().RemoveAsync( Arg.Is( id => id == _id ) ); - } - - /// - /// 测试保存 - /// - [Fact] - public async Task TestSaveAsync() { - var id = Guid.NewGuid(); - var id2 = Guid.NewGuid(); - var id3 = Guid.NewGuid(); - var id4 = Guid.NewGuid(); - var id5 = Guid.NewGuid(); - var addList = new List { - new DtoSample{Id = id.ToString()}, - new DtoSample{Id = id2.ToString()} - }; - var updateList = new List { - new DtoSample{Id = id3.ToString()}, - new DtoSample{Id = id4.ToString()}, - new DtoSample{Id = id5.ToString()} - }; - var deleteList = new List { - new DtoSample{Id = id3.ToString()} - }; - var result = await _service.SaveAsync( addList, updateList, deleteList ); - await _repository.Received( 2 ).AddAsync( Arg.Any() ); - await _repository.Received( 2 ).UpdateAsync( Arg.Any() ); - await _repository.Received( 1 ).RemoveAsync( Arg.Is( t => t == id3 ) ); - await _unitOfWork.Received( 1 ).CommitAsync(); - Assert.Equal( 4, result.Count ); - } - } -} diff --git a/test/Util.Tests/Applications/CrudServiceTest.Save.cs b/test/Util.Tests/Applications/CrudServiceTest.Save.cs deleted file mode 100644 index a0504874d..000000000 --- a/test/Util.Tests/Applications/CrudServiceTest.Save.cs +++ /dev/null @@ -1,179 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Autofac; -using NSubstitute; -using Util.Datas.UnitOfWorks; -using Util.Dependency; -using Util.Exceptions; -using Util.Helpers; -using Util.Tests.Samples; -using Util.Tests.XUnitHelpers; -using Xunit; - -namespace Util.Tests.Applications { - /// - /// 增删改查服务测试 - /// - public partial class CrudServiceTest { - /// - /// Id - /// - private Guid _id; - /// - /// Id2 - /// - private Guid _id2; - /// - /// 实体 - /// - private EntitySample _entity; - /// - /// 实体2 - /// - private EntitySample _entity2; - /// - /// 数据传输对象 - /// - private DtoSample _dto; - /// - /// 数据传输对象2 - /// - private DtoSample _dto2; - /// - /// 增删改查服务 - /// - private CrudServiceSample _service; - /// - /// 工作单元 - /// - private IUnitOfWork _unitOfWork; - /// - /// 仓储 - /// - private IRepositorySample _repository; - - /// - /// 测试初始化 - /// - public CrudServiceTest() { - _id = Guid.NewGuid(); - _id2 = Guid.NewGuid(); - _entity = new EntitySample( _id ) { Name = "A" }; - _entity2 = new EntitySample( _id2 ) { Name = "B" }; - _dto = new DtoSample{Id = _id.ToString(),Name = "A"}; - _dto2 = new DtoSample { Id = _id2.ToString(), Name = "B" }; - _unitOfWork = Substitute.For(); - _repository = Substitute.For(); - _service = new CrudServiceSample( _unitOfWork,_repository ); - } - - /// - /// 获取实体集合 - /// - private List GetEntities() { - return new List { _entity, _entity2 }; - } - - /// - /// 通过AOP进行DTO验证 - /// - [Fact] - public void TestSave_ValidateDto() { - var container = Ioc.CreateContainer( new IocConfig() ); - var service = container.Create(); - - //有效 - service.Save( _dto ); - - //无效 - AssertHelper.Throws( () => { - service.Save( new DtoSample() ); - }, "名称不能为空" ); - } - - /// - /// 测试添加 - /// - [Fact] - public void TestSave_Add() { - _service.Save( new DtoSample{Name = "a"} ); - _repository.Received().Add( Arg.Is( t => t.Name == "a" ) ); - _repository.DidNotReceive().Update( Arg.Any() ); - } - - /// - /// 测试修改 - /// - [Fact] - public void TestSave_Update() { - _service.Save( new DtoSample { Id =_id.ToString(), Name = "b" } ); - _repository.DidNotReceive().Add( Arg.Any() ); - _repository.Received().Update( Arg.Is( t => t.Name == "b" ) ); - } - - /// - /// 测试添加 - /// - [Fact] - public async Task TestSaveAsync_Add() { - await _service.SaveAsync( new DtoSample { Name = "a" } ); - await _repository.Received().AddAsync( Arg.Is( t => t.Name == "a" ) ); - } - - /// - /// 测试修改 - /// - [Fact] - public async Task TestSaveAsync_Update() { - await _service.SaveAsync( new DtoSample { Id = _id.ToString(), Name = "b" } ); - await _repository.DidNotReceive().AddAsync( Arg.Any() ); - await _repository.Received().UpdateAsync( Arg.Is( t => t.Name == "b" ) ); - } - - /// - /// 测试删除 - /// - [Fact] - public void TestDelete() { - var ids = new[] {_id, _id2}; - _repository.FindByIds( ids.Join() ).Returns( GetEntities() ); - _service.Delete( ids.Join() ); - _repository.Received().Remove( Arg.Is>( t => t.All( d => ids.Contains( d.Id ) ) ) ); - } - - /// - /// 测试删除 - id无效 - /// - [Fact] - public void TestDelete_IdInvalid() { - var ids = new[] { Guid.NewGuid(), Guid.NewGuid() }; - _repository.FindByIds( ids.Join() ).Returns( new List() ); - _service.Delete( ids.Join() ); - _repository.DidNotReceive().Remove( Arg.Any>() ); - } - - /// - /// 测试删除 - /// - [Fact] - public async Task TestDeleteAsync() { - var ids = new[] { _id, _id2 }; - _repository.FindByIdsAsync( ids.Join() ).Returns( GetEntities() ); - await _service.DeleteAsync( ids.Join() ); - await _repository.Received().RemoveAsync( Arg.Is>( t => t.All( d => ids.Contains( d.Id ) ) ) ); - } - } - - /// - /// Ioc配置 - /// - public class IocConfig : ConfigBase { - protected override void Load( ContainerBuilder builder ) { - builder.AddScoped(); - builder.AddScoped(); - builder.AddScoped(); - } - } -} diff --git a/test/Util.Tests/Applications/QueryServiceTest.cs b/test/Util.Tests/Applications/QueryServiceTest.cs deleted file mode 100644 index 66426bd16..000000000 --- a/test/Util.Tests/Applications/QueryServiceTest.cs +++ /dev/null @@ -1,185 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using NSubstitute; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Applications { - /// - /// 查询服务测试 - /// - public class QueryServiceTest { - /// - /// Id - /// - private Guid _id; - /// - /// Id2 - /// - private Guid _id2; - /// - /// 实体 - /// - private EntitySample _entity; - /// - /// 实体2 - /// - private EntitySample _entity2; - /// - /// 查询服务 - /// - private QueryServiceSample _service; - /// - /// 仓储 - /// - private IRepositorySample _repository; - - /// - /// 测试初始化 - /// - public QueryServiceTest() { - _id = Guid.NewGuid(); - _id2 = Guid.NewGuid(); - _entity = new EntitySample( _id ) {Name = "A"}; - _entity2 = new EntitySample( _id2 ) { Name = "B" }; - _repository = Substitute.For(); - _service = new QueryServiceSample( _repository ); - } - - /// - /// 获取实体集合 - /// - private List GetEntities() { - return new List{ _entity , _entity2 }; - } - - /// - /// 测试获取全部 - /// - [Fact] - public void TestGetAll() { - _repository.FindAll().Returns( GetEntities() ); - var dtos = _service.GetAll(); - Assert.Equal( 2,dtos.Count ); - Assert.Equal( "A", dtos[0].Name ); - Assert.Equal( "B", dtos[1].Name ); - } - - /// - /// 测试获取全部 - /// - [Fact] - public async Task TestGetAllAsync() { - _repository.FindAllAsync().Returns( GetEntities() ); - var dtos = await _service.GetAllAsync(); - Assert.Equal( 2, dtos.Count ); - Assert.Equal( "A", dtos[0].Name ); - Assert.Equal( "B", dtos[1].Name ); - } - - /// - /// 测试通过编号获取 - /// - [Fact] - public void TestGetById() { - _repository.Find( _id ).Returns( _entity ); - var dto = _service.GetById( _id.ToString() ); - Assert.Equal( "A", dto.Name ); - } - - /// - /// 测试通过编号获取 - /// - [Fact] - public async Task TestGetByIdAsync() { - _repository.FindAsync( _id ).Returns( _entity ); - var dto = await _service.GetByIdAsync( _id.ToString() ); - Assert.Equal( "A", dto.Name ); - } - - /// - /// 测试通过编号列表获取 - /// - [Fact] - public void TestGetByIds() { - var ids = $"{_id},{_id2}"; - _repository.FindByIds( ids ).Returns( GetEntities() ); - var dtos = _service.GetByIds( ids ); - Assert.Equal( 2, dtos.Count ); - Assert.Equal( "A", dtos[0].Name ); - Assert.Equal( "B", dtos[1].Name ); - } - - /// - /// 测试通过编号列表获取 - /// - [Fact] - public async Task TestGetByIdsAsync() { - var ids = $"{_id},{_id2}"; - _repository.FindByIdsAsync( ids ).Returns( GetEntities() ); - var dtos = await _service.GetByIdsAsync( ids ); - Assert.Equal( 2, dtos.Count ); - Assert.Equal( "A", dtos[0].Name ); - Assert.Equal( "B", dtos[1].Name ); - } - - /// - /// 测试查询 - /// - [Fact] - public void TestQuery_1() { - var queryable = GetEntities().AsQueryable(); - _repository.Find().Returns( queryable ); - QueryParameterSample parameter = new QueryParameterSample(); - var dtos = _service.Query( parameter ); - Assert.Equal( 2, dtos.Count ); - Assert.Equal( "A", dtos[0].Name ); - Assert.Equal( "B", dtos[1].Name ); - } - - /// - /// 测试查询 - /// - [Fact] - public void TestQuery_2() { - var queryable = GetEntities().AsQueryable(); - _repository.Find().Returns( queryable ); - QueryParameterSample parameter = new QueryParameterSample(); - parameter.Name = "A"; - var dtos = _service.Query( parameter ); - Assert.Single( dtos); - Assert.Equal( "A", dtos[0].Name ); - } - - /// - /// 测试分页查询 - /// - [Fact] - public void TestPagerQuery_1() { - var queryable = GetEntities().AsQueryable(); - _repository.Find().Returns( queryable ); - QueryParameterSample parameter = new QueryParameterSample(); - parameter.Order = "Name"; - var result = _service.PagerQuery( parameter ); - Assert.Equal( 2, result.TotalCount ); - Assert.Equal( "A", result[0].Name ); - Assert.Equal( "B", result[1].Name ); - } - - /// - /// 测试分页查询 - /// - [Fact] - public void TestPagerQuery_2() { - var queryable = GetEntities().AsQueryable(); - _repository.Find().Returns( queryable ); - QueryParameterSample parameter = new QueryParameterSample(); - parameter.Name = "B"; - var result = _service.PagerQuery( parameter ); - Assert.Equal( 1, result.TotalCount ); - Assert.Equal( "B", result[0].Name ); - } - } -} diff --git a/test/Util.Tests/Datas/Persistence/PersistentObjectTest.cs b/test/Util.Tests/Datas/Persistence/PersistentObjectTest.cs deleted file mode 100644 index d23ddd918..000000000 --- a/test/Util.Tests/Datas/Persistence/PersistentObjectTest.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Datas.Persistence { - /// - /// 持久化对象测试 - /// - public class PersistentObjectTest { - /// - /// 持久化对象测试样例 - /// - private PersistentObjectSample _sample; - /// - /// 持久化对象测试样例2 - /// - private PersistentObjectSample _sample2; - - /// - /// 测试初始化 - /// - public PersistentObjectTest() { - _sample = new PersistentObjectSample(); - _sample2 = new PersistentObjectSample(); - } - - /// - /// 测试实体相等性 - 判空 - /// - [Fact] - public void TestEquals_Null() { - Assert.False( _sample.Equals( _sample2 ) ); - Assert.False( _sample.Equals( null ) ); - - Assert.False( _sample == _sample2 ); - Assert.False( _sample == null ); - Assert.False( null == _sample2 ); - - Assert.True( _sample != _sample2 ); - Assert.True( _sample != null ); - Assert.True( null != _sample2 ); - - _sample2 = null; - Assert.False( _sample.Equals( _sample2 ) ); - - _sample = null; - Assert.True( _sample == _sample2 ); - Assert.True( _sample2 == _sample ); - } - - /// - /// 测试实体相等性 - 类型无效 - /// - [Fact] - public void TestEquals_InvalidType() { - Guid id = Guid.NewGuid(); - _sample = new PersistentObjectSample( id ); - PersistentObjectSample2 sample2 = new PersistentObjectSample2( id ); - Assert.False( _sample.Equals( sample2 ) ); - Assert.True( _sample != sample2 ); - Assert.True( sample2 != _sample ); - } - - /// - /// 测试实体相等性 - 当两个实体的标识相同,则实体相同 - /// - [Fact] - public void TestEquals_IdEquals() { - Guid id = Guid.NewGuid(); - _sample = new PersistentObjectSample( id ); - _sample2 = new PersistentObjectSample( id ); - Assert.True( _sample.Equals( _sample2 ) ); - Assert.True( _sample == _sample2 ); - Assert.False( _sample != _sample2 ); - } - } -} \ No newline at end of file diff --git a/test/Util.Tests/Datas/Queries/Criterias/AndCriteriaTest.cs b/test/Util.Tests/Datas/Queries/Criterias/AndCriteriaTest.cs deleted file mode 100644 index 78db29319..000000000 --- a/test/Util.Tests/Datas/Queries/Criterias/AndCriteriaTest.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Util.Datas.Queries.Criterias; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Datas.Queries.Criterias { - /// - /// 测试与查询条件 - /// - public class AndCriteriaTest { - /// - /// 测试获取查询条件 - /// - [Fact] - public void TestGetPredicate() { - var criteria = new AndCriteria( t => t.Name == "a",t => t.Name != "b" ); - Assert.Equal( "t => ((t.Name == \"a\") AndAlso (t.Name != \"b\"))", criteria.GetPredicate().ToString() ); - } - } -} diff --git a/test/Util.Tests/Datas/Queries/Criterias/DateSegmentCriteriaTest.cs b/test/Util.Tests/Datas/Queries/Criterias/DateSegmentCriteriaTest.cs deleted file mode 100644 index 3d5d61078..000000000 --- a/test/Util.Tests/Datas/Queries/Criterias/DateSegmentCriteriaTest.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Text; -using Util.Datas.Queries; -using Util.Datas.Queries.Criterias; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Datas.Queries.Criterias { - /// - /// 测试日期范围过滤条件 - /// - public class DateSegmentCriteriaTest { - /// - /// 测试初始化 - /// - public DateSegmentCriteriaTest() { - _min = DateTime.Parse( "2000-1-1 10:10:10" ); - _max = DateTime.Parse( "2000-1-2 10:10:10" ); - } - - /// - /// 最小日期 - /// - private readonly DateTime? _min; - /// - /// 最大日期 - /// - private readonly DateTime? _max; - - /// - /// 测试获取查询条件 - /// - [Fact] - public void TestGetPredicate() { - var result = new StringBuilder(); - result.AppendFormat( "t => ((t.DateValue >= {0})",DateTime.Parse( "2000/1/1 0:00:00" ) ); - result.AppendFormat( " AndAlso (t.DateValue < {0}))", DateTime.Parse( "2000/1/3 0:00:00" ) ); - var criteria = new DateSegmentCriteria( t => t.DateValue, _min, _max ); - Assert.Equal( result.ToString(), criteria.GetPredicate().ToString() ); - - result = new StringBuilder(); - result.AppendFormat( "t => ((t.NullableDateValue >= {0})", DateTime.Parse( "2000/1/1 0:00:00" ) ); - result.AppendFormat( " AndAlso (t.NullableDateValue < {0}))", DateTime.Parse( "2000/1/3 0:00:00" ) ); - var criteria2 = new DateSegmentCriteria( t => t.NullableDateValue, _min, _max ); - Assert.Equal( result.ToString(), criteria2.GetPredicate().ToString() ); - } - - /// - /// 测试获取查询条件 - 设置边界 - /// - [Fact] - public void TestGetPredicate_Boundary() { - var result = new StringBuilder(); - result.AppendFormat( "t => ((t.DateValue > {0})", DateTime.Parse( "2000/1/1 0:00:00" ) ); - result.AppendFormat( " AndAlso (t.DateValue < {0}))", DateTime.Parse( "2000/1/3 0:00:00" ) ); - var criteria = new DateSegmentCriteria( t => t.DateValue, _min, _max, Boundary.Neither ); - Assert.Equal( result.ToString(), criteria.GetPredicate().ToString() ); - - result = new StringBuilder(); - result.AppendFormat( "t => ((t.DateValue >= {0})", DateTime.Parse( "2000/1/1 0:00:00" ) ); - result.AppendFormat( " AndAlso (t.DateValue < {0}))", DateTime.Parse( "2000/1/3 0:00:00" ) ); - criteria = new DateSegmentCriteria( t => t.DateValue, _min, _max, Boundary.Left ); - Assert.Equal( result.ToString(), criteria.GetPredicate().ToString() ); - - result = new StringBuilder(); - result.AppendFormat( "t => ((t.NullableDateValue > {0})", DateTime.Parse( "2000/1/1 0:00:00" ) ); - result.AppendFormat( " AndAlso (t.NullableDateValue <= {0}))", DateTime.Parse( "2000/1/3 0:00:00" ) ); - var criteria2 = new DateSegmentCriteria( t => t.NullableDateValue, _min, _max,Boundary.Right ); - Assert.Equal( result.ToString(), criteria2.GetPredicate().ToString() ); - - result = new StringBuilder(); - result.AppendFormat( "t => ((t.NullableDateValue >= {0})", DateTime.Parse( "2000/1/1 0:00:00" ) ); - result.AppendFormat( " AndAlso (t.NullableDateValue <= {0}))", DateTime.Parse( "2000/1/3 0:00:00" ) ); - criteria2 = new DateSegmentCriteria( t => t.NullableDateValue, _min, _max, Boundary.Both ); - Assert.Equal( result.ToString(), criteria2.GetPredicate().ToString() ); - } - } -} diff --git a/test/Util.Tests/Datas/Queries/Criterias/DateTimeSegmentCriteriaTest.cs b/test/Util.Tests/Datas/Queries/Criterias/DateTimeSegmentCriteriaTest.cs deleted file mode 100644 index 485e3fadd..000000000 --- a/test/Util.Tests/Datas/Queries/Criterias/DateTimeSegmentCriteriaTest.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Text; -using Util.Datas.Queries; -using Util.Datas.Queries.Criterias; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Datas.Queries.Criterias { - /// - /// 测试日期范围过滤条件 - 包含时间 - /// - public class DateTimeSegmentCriteriaTest { - /// - /// 测试初始化 - /// - public DateTimeSegmentCriteriaTest() { - _min = DateTime.Parse( "2000-1-1 10:10:10" ); - _max = DateTime.Parse( "2000-1-2 10:10:10" ); - } - - /// - /// 最小日期 - /// - private readonly DateTime? _min; - /// - /// 最大日期 - /// - private readonly DateTime? _max; - - /// - /// 测试获取查询条件 - /// - [Fact] - public void TestGetPredicate() { - var result = new StringBuilder(); - result.AppendFormat( "t => ((t.DateValue >= {0})", DateTime.Parse( "2000/1/1 10:10:10" ) ); - result.AppendFormat( " AndAlso (t.DateValue <= {0}))", DateTime.Parse( "2000/1/2 10:10:10" ) ); - var criteria = new DateTimeSegmentCriteria( t => t.DateValue, _min, _max ); - Assert.Equal( result.ToString(), criteria.GetPredicate().ToString() ); - - result = new StringBuilder(); - result.AppendFormat( "t => ((t.NullableDateValue >= {0})", DateTime.Parse( "2000/1/1 10:10:10" ) ); - result.AppendFormat( " AndAlso (t.NullableDateValue <= {0}))", DateTime.Parse( "2000/1/2 10:10:10" ) ); - var criteria2 = new DateTimeSegmentCriteria( t => t.NullableDateValue, _min, _max ); - Assert.Equal( result.ToString(), criteria2.GetPredicate().ToString() ); - } - - /// - /// 测试获取查询条件 - 设置边界 - /// - [Fact] - public void TestGetPredicate_Boundary() { - var result = new StringBuilder(); - result.AppendFormat( "t => ((t.DateValue > {0})", DateTime.Parse( "2000/1/1 10:10:10" ) ); - result.AppendFormat( " AndAlso (t.DateValue < {0}))", DateTime.Parse( "2000/1/2 10:10:10" ) ); - var criteria = new DateTimeSegmentCriteria( t => t.DateValue, _min, _max, Boundary.Neither ); - Assert.Equal( result.ToString(), criteria.GetPredicate().ToString() ); - - result = new StringBuilder(); - result.AppendFormat( "t => ((t.DateValue >= {0})", DateTime.Parse( "2000/1/1 10:10:10" ) ); - result.AppendFormat( " AndAlso (t.DateValue < {0}))", DateTime.Parse( "2000/1/2 10:10:10" ) ); - criteria = new DateTimeSegmentCriteria( t => t.DateValue, _min, _max, Boundary.Left ); - Assert.Equal( result.ToString(), criteria.GetPredicate().ToString() ); - - result = new StringBuilder(); - result.AppendFormat( "t => ((t.NullableDateValue > {0})", DateTime.Parse( "2000/1/1 10:10:10" ) ); - result.AppendFormat( " AndAlso (t.NullableDateValue <= {0}))", DateTime.Parse( "2000/1/2 10:10:10" ) ); - var criteria2 = new DateTimeSegmentCriteria( t => t.NullableDateValue, _min, _max,Boundary.Right ); - Assert.Equal( result.ToString(), criteria2.GetPredicate().ToString() ); - - result = new StringBuilder(); - result.AppendFormat( "t => ((t.NullableDateValue >= {0})", DateTime.Parse( "2000/1/1 10:10:10" ) ); - result.AppendFormat( " AndAlso (t.NullableDateValue <= {0}))", DateTime.Parse( "2000/1/2 10:10:10" ) ); - criteria2 = new DateTimeSegmentCriteria( t => t.NullableDateValue, _min, _max, Boundary.Both ); - Assert.Equal( result.ToString(), criteria2.GetPredicate().ToString() ); - } - } -} diff --git a/test/Util.Tests/Datas/Queries/Criterias/DecimalSegmentCriteriaTest.cs b/test/Util.Tests/Datas/Queries/Criterias/DecimalSegmentCriteriaTest.cs deleted file mode 100644 index cef73e4d6..000000000 --- a/test/Util.Tests/Datas/Queries/Criterias/DecimalSegmentCriteriaTest.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Util.Datas.Queries; -using Util.Datas.Queries.Criterias; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Datas.Queries.Criterias { - /// - /// 测试double范围过滤条件 - /// - public class DecimalSegmentCriteriaTest { - /// - /// 测试获取查询条件 - /// - [Fact] - public void TestGetPredicate() { - DecimalSegmentCriteria criteria = new DecimalSegmentCriteria( t => t.DecimalValue, 1.1M, 10.1M ); - Assert.Equal( "t => ((t.DecimalValue >= 1.1) AndAlso (t.DecimalValue <= 10.1))", criteria.GetPredicate().ToString() ); - - DecimalSegmentCriteria criteria2 = new DecimalSegmentCriteria( t => t.NullableDecimalValue, 1.1M, 10.1M ); - Assert.Equal( "t => ((t.NullableDecimalValue >= 1.1) AndAlso (t.NullableDecimalValue <= 10.1))", criteria2.GetPredicate().ToString() ); - } - - /// - /// 测试获取查询条件 - 设置边界 - /// - [Fact] - public void TestGetPredicate_Boundary() { - DecimalSegmentCriteria criteria = new DecimalSegmentCriteria( t => t.DecimalValue, 1.1M, 10.1M, Boundary.Neither ); - Assert.Equal( "t => ((t.DecimalValue > 1.1) AndAlso (t.DecimalValue < 10.1))", criteria.GetPredicate().ToString() ); - - criteria = new DecimalSegmentCriteria( t => t.DecimalValue, 1.1M, 10.1M, Boundary.Left ); - Assert.Equal( "t => ((t.DecimalValue >= 1.1) AndAlso (t.DecimalValue < 10.1))", criteria.GetPredicate().ToString() ); - - DecimalSegmentCriteria criteria2 = new DecimalSegmentCriteria( t => t.NullableDecimalValue, 1.1M, 10.1M,Boundary.Right ); - Assert.Equal( "t => ((t.NullableDecimalValue > 1.1) AndAlso (t.NullableDecimalValue <= 10.1))", criteria2.GetPredicate().ToString() ); - - criteria2 = new DecimalSegmentCriteria( t => t.NullableDecimalValue, 1.1M, 10.1M, Boundary.Both ); - Assert.Equal( "t => ((t.NullableDecimalValue >= 1.1) AndAlso (t.NullableDecimalValue <= 10.1))", criteria2.GetPredicate().ToString() ); - } - } -} diff --git a/test/Util.Tests/Datas/Queries/Criterias/DefaultCriteriaTest.cs b/test/Util.Tests/Datas/Queries/Criterias/DefaultCriteriaTest.cs deleted file mode 100644 index ed8cd4cd6..000000000 --- a/test/Util.Tests/Datas/Queries/Criterias/DefaultCriteriaTest.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Util.Datas.Queries.Criterias; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Datas.Queries.Criterias { - /// - /// 测试默认查询条件 - /// - public class DefaultCriteriaTest { - /// - /// 测试获取查询条件 - /// - [Fact] - public void TestGetPredicate() { - var criteria = new DefaultCriteria( t => t.Name == "a" ); - Assert.Equal( "t => (t.Name == \"a\")", criteria.GetPredicate().ToString() ); - } - } -} diff --git a/test/Util.Tests/Datas/Queries/Criterias/DoubleSegmentCriteriaTest.cs b/test/Util.Tests/Datas/Queries/Criterias/DoubleSegmentCriteriaTest.cs deleted file mode 100644 index a6de897ae..000000000 --- a/test/Util.Tests/Datas/Queries/Criterias/DoubleSegmentCriteriaTest.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System; -using Util.Datas.Queries; -using Util.Datas.Queries.Criterias; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Datas.Queries.Criterias { - /// - /// 测试double范围过滤条件 - /// - public class DoubleSegmentCriteriaTest { - /// - /// 测试获取查询条件 - /// - [Fact] - public void TestGetPredicate() { - DoubleSegmentCriteria criteria = new DoubleSegmentCriteria( t => t.DoubleValue, 1.1, 10.1 ); - Assert.Equal( "t => ((t.DoubleValue >= 1.1) AndAlso (t.DoubleValue <= 10.1))", criteria.GetPredicate().ToString() ); - - DoubleSegmentCriteria criteria2 = new DoubleSegmentCriteria( t => t.NullableDoubleValue, 1.1, 10.1 ); - Assert.Equal( "t => ((t.NullableDoubleValue >= 1.1) AndAlso (t.NullableDoubleValue <= 10.1))", criteria2.GetPredicate().ToString() ); - } - - /// - /// 测试获取查询条件 - 设置边界 - /// - [Fact] - public void TestGetPredicate_Boundary() { - DoubleSegmentCriteria criteria = new DoubleSegmentCriteria( t => t.DoubleValue, 1.1, 10.1, Boundary.Neither ); - Assert.Equal( "t => ((t.DoubleValue > 1.1) AndAlso (t.DoubleValue < 10.1))", criteria.GetPredicate().ToString() ); - - criteria = new DoubleSegmentCriteria( t => t.DoubleValue, 1.1, 10.1, Boundary.Left ); - Assert.Equal( "t => ((t.DoubleValue >= 1.1) AndAlso (t.DoubleValue < 10.1))", criteria.GetPredicate().ToString() ); - - DoubleSegmentCriteria criteria2 = new DoubleSegmentCriteria( t => t.NullableDoubleValue, 1.1, 10.1,Boundary.Right ); - Assert.Equal( "t => ((t.NullableDoubleValue > 1.1) AndAlso (t.NullableDoubleValue <= 10.1))", criteria2.GetPredicate().ToString() ); - - criteria2 = new DoubleSegmentCriteria( t => t.NullableDoubleValue, 1.1, 10.1, Boundary.Both ); - Assert.Equal( "t => ((t.NullableDoubleValue >= 1.1) AndAlso (t.NullableDoubleValue <= 10.1))", criteria2.GetPredicate().ToString() ); - } - - /// - /// 测试获取查询条件 - 属性为int类型,则发生类型不匹配异常 - /// - [Fact] - public void TestGetPredicate_ValidateType() { - Assert.Throws( () => { - var criteria = new DoubleSegmentCriteria( t => t.Age, 1.1, 10.1 ); - var result = criteria.GetPredicate(); - } ); - } - - /// - /// 测试获取查询条件 - 最小值大于最大值,则交换大小值的位置 - /// - [Fact] - public void TestGetPredicate_MinGreaterMax() { - DoubleSegmentCriteria criteria = new DoubleSegmentCriteria( t => t.DoubleValue, 10.1, 1.1 ); - Assert.Equal( "t => ((t.DoubleValue >= 1.1) AndAlso (t.DoubleValue <= 10.1))", criteria.GetPredicate().ToString() ); - - DoubleSegmentCriteria criteria2 = new DoubleSegmentCriteria( t => t.NullableDoubleValue, 10.1, 1.1 ); - Assert.Equal( "t => ((t.NullableDoubleValue >= 1.1) AndAlso (t.NullableDoubleValue <= 10.1))", criteria2.GetPredicate().ToString() ); - } - - /// - /// 测试获取查询条件 - 最小值为空,忽略最小值条件 - /// - [Fact] - public void TestGetPredicate_MinIsNull() { - DoubleSegmentCriteria criteria = new DoubleSegmentCriteria( t => t.DoubleValue, null, 10.1 ); - Assert.Equal( "t => (t.DoubleValue <= 10.1)", criteria.GetPredicate().ToString() ); - - DoubleSegmentCriteria criteria2 = new DoubleSegmentCriteria( t => t.NullableDoubleValue, null, 10.1 ); - Assert.Equal( "t => (t.NullableDoubleValue <= 10.1)", criteria2.GetPredicate().ToString() ); - } - - /// - /// 测试获取查询条件 - 最大值为空,忽略最大值条件 - /// - [Fact] - public void TestGetPredicate_MaxIsNull() { - DoubleSegmentCriteria criteria = new DoubleSegmentCriteria( t => t.DoubleValue, 1.1, null ); - Assert.Equal( "t => (t.DoubleValue >= 1.1)", criteria.GetPredicate().ToString() ); - - DoubleSegmentCriteria criteria2 = new DoubleSegmentCriteria( t => t.NullableDoubleValue, 1.1, null ); - Assert.Equal( "t => (t.NullableDoubleValue >= 1.1)", criteria2.GetPredicate().ToString() ); - } - - /// - /// 测试获取查询条件 - 最大值和最小值均为null,忽略所有条件 - /// - [Fact] - public void TestGetPredicate_BothNull() { - DoubleSegmentCriteria criteria = new DoubleSegmentCriteria( t => t.DoubleValue, null, null ); - Assert.Null( criteria.GetPredicate() ); - - DoubleSegmentCriteria criteria2 = new DoubleSegmentCriteria( t => t.NullableDoubleValue, null, null ); - Assert.Null( criteria2.GetPredicate() ); - } - } -} diff --git a/test/Util.Tests/Datas/Queries/Criterias/IntSegmentCriteriaTest.cs b/test/Util.Tests/Datas/Queries/Criterias/IntSegmentCriteriaTest.cs deleted file mode 100644 index 3a6e3afb6..000000000 --- a/test/Util.Tests/Datas/Queries/Criterias/IntSegmentCriteriaTest.cs +++ /dev/null @@ -1,89 +0,0 @@ -using Util.Datas.Queries; -using Util.Datas.Queries.Criterias; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Datas.Queries.Criterias { - /// - /// 测试整数范围过滤条件 - /// - public class IntSegmentCriteriaTest { - /// - /// 测试获取查询条件 - /// - [Fact] - public void TestGetPredicate() { - IntSegmentCriteria criteria = new IntSegmentCriteria( t => t.Tel, 1, 10 ); - Assert.Equal( "t => ((t.Tel >= 1) AndAlso (t.Tel <= 10))", criteria.GetPredicate().ToString() ); - - IntSegmentCriteria criteria2 = new IntSegmentCriteria( t => t.Age, 1, 10 ); - Assert.Equal( "t => ((t.Age >= 1) AndAlso (t.Age <= 10))", criteria2.GetPredicate().ToString() ); - } - - /// - /// 测试获取查询条件 - 设置边界 - /// - [Fact] - public void TestGetPredicate_Boundary() { - IntSegmentCriteria criteria = new IntSegmentCriteria( t => t.Tel, 1, 10, Boundary.Neither ); - Assert.Equal( "t => ((t.Tel > 1) AndAlso (t.Tel < 10))", criteria.GetPredicate().ToString() ); - - criteria = new IntSegmentCriteria( t => t.Tel, 1, 10,Boundary.Left ); - Assert.Equal( "t => ((t.Tel >= 1) AndAlso (t.Tel < 10))", criteria.GetPredicate().ToString() ); - - IntSegmentCriteria criteria2 = new IntSegmentCriteria( t => t.Age, 1, 10, Boundary.Right ); - Assert.Equal( "t => ((t.Age > 1) AndAlso (t.Age <= 10))", criteria2.GetPredicate().ToString() ); - - criteria2 = new IntSegmentCriteria( t => t.Age, 1, 10, Boundary.Both ); - Assert.Equal( "t => ((t.Age >= 1) AndAlso (t.Age <= 10))", criteria2.GetPredicate().ToString() ); - } - - /// - /// 测试获取查询条件 - 最小值大于最大值,则交换大小值的位置 - /// - [Fact] - public void TestGetPredicate_MinGreaterMax() { - IntSegmentCriteria criteria = new IntSegmentCriteria( t => t.Tel, 10, 1 ); - Assert.Equal( "t => ((t.Tel >= 1) AndAlso (t.Tel <= 10))", criteria.GetPredicate().ToString() ); - - IntSegmentCriteria criteria2 = new IntSegmentCriteria( t => t.Age, 10, 1 ); - Assert.Equal( "t => ((t.Age >= 1) AndAlso (t.Age <= 10))", criteria2.GetPredicate().ToString() ); - } - - /// - /// 测试获取查询条件 - 最小值为空,忽略最小值条件 - /// - [Fact] - public void TestGetPredicate_MinIsNull() { - IntSegmentCriteria criteria = new IntSegmentCriteria( t => t.Tel, null, 10 ); - Assert.Equal( "t => (t.Tel <= 10)", criteria.GetPredicate().ToString() ); - - IntSegmentCriteria criteria2 = new IntSegmentCriteria( t => t.Age, null, 10 ); - Assert.Equal( "t => (t.Age <= 10)", criteria2.GetPredicate().ToString() ); - } - - /// - /// 测试获取查询条件 - 最大值为空,忽略最大值条件 - /// - [Fact] - public void TestGetPredicate_MaxIsNull() { - IntSegmentCriteria criteria = new IntSegmentCriteria( t => t.Tel, 1, null ); - Assert.Equal( "t => (t.Tel >= 1)", criteria.GetPredicate().ToString() ); - - IntSegmentCriteria criteria2 = new IntSegmentCriteria( t => t.Age, 1, null ); - Assert.Equal( "t => (t.Age >= 1)", criteria2.GetPredicate().ToString() ); - } - - /// - /// 测试获取查询条件 - 最大值和最小值均为null,忽略所有条件 - /// - [Fact] - public void Test_BothNull() { - IntSegmentCriteria criteria = new IntSegmentCriteria( t => t.Tel, null, null ); - Assert.Null( criteria.GetPredicate() ); - - IntSegmentCriteria criteria2 = new IntSegmentCriteria( t => t.Age, null, null ); - Assert.Null( criteria2.GetPredicate() ); - } - } -} diff --git a/test/Util.Tests/Datas/Queries/Criterias/OrCriteriaTest.cs b/test/Util.Tests/Datas/Queries/Criterias/OrCriteriaTest.cs deleted file mode 100644 index f89e3b6f0..000000000 --- a/test/Util.Tests/Datas/Queries/Criterias/OrCriteriaTest.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Util.Datas.Queries.Criterias; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Datas.Queries.Criterias { - /// - /// 测试或查询条件 - /// - public class OrCriteriaTest { - /// - /// 测试获取查询条件 - /// - [Fact] - public void TestGetPredicate() { - var criteria = new OrCriteria( t => t.Name == "a",t => t.Name != "b" ); - Assert.Equal( "t => ((t.Name == \"a\") OrElse (t.Name != \"b\"))", criteria.GetPredicate().ToString() ); - } - } -} diff --git a/test/Util.Tests/Datas/Queries/OrderByBuilderTest.cs b/test/Util.Tests/Datas/Queries/OrderByBuilderTest.cs deleted file mode 100644 index 46134139c..000000000 --- a/test/Util.Tests/Datas/Queries/OrderByBuilderTest.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Util.Datas.Queries; -using Xunit; - -namespace Util.Tests.Datas.Queries { - /// - /// 测试排序生成器 - /// - public class OrderByBuilderTest { - /// - /// 测试初始化 - /// - public OrderByBuilderTest() { - _builder = new OrderByBuilder(); - } - - /// - /// 排序生成器 - /// - private readonly OrderByBuilder _builder; - - /// - /// 测试生成排序 - /// - [Fact] - public void TestGenerate() { - Assert.Equal( "", _builder.Generate() ); - _builder.Add( "" ); - _builder.Add( "A" ); - _builder.Add( "B", true ); - _builder.Add( "C.D", true ); - Assert.Equal( "A,B desc,C.D desc", _builder.Generate() ); - } - } -} diff --git a/test/Util.Tests/Datas/Queries/QueryTest.cs b/test/Util.Tests/Datas/Queries/QueryTest.cs deleted file mode 100644 index 2751dddad..000000000 --- a/test/Util.Tests/Datas/Queries/QueryTest.cs +++ /dev/null @@ -1,279 +0,0 @@ -using System; -using System.Linq.Expressions; -using System.Text; -using Util.Datas.Queries; -using Util.Datas.Queries.Criterias; -using Util.Properties; -using Util.Tests.Samples; -using Util.Tests.XUnitHelpers; -using Xunit; - -namespace Util.Tests.Datas.Queries { - /// - /// 查询对象测试 - /// - public class QueryTest { - /// - /// 测试初始化 - /// - public QueryTest() { - _query = new Query(); - } - - /// - /// 查询对象 - /// - private IQuery _query; - - /// - /// 测试获取分页 - /// - [Fact] - public void TestGetPager() { - QueryParameterSample sample = new QueryParameterSample() { - Order = "A", - Page = 2, - PageSize = 30, - TotalCount = 40 - }; - _query = new Query( sample ); - _query.OrderBy( "B", true ); - var pager = _query.GetPager(); - Assert.Equal( 2, pager.Page ); - Assert.Equal( 30, pager.PageSize ); - Assert.Equal( 40, pager.TotalCount ); - Assert.Equal( "A,B desc", pager.Order ); - } - - /// - /// 测试添加查询条件- 当值为空时,不会被忽略 - /// - [Fact] - public void TestWhere() { - _query.Where( t => t.Name == "A" ); - Assert.Equal( "t => (t.Name == \"A\")", _query.GetPredicate().SafeString() ); - - _query.Where( t => t.Tel == 1 ); - Assert.Equal( "t => ((t.Name == \"A\") AndAlso (t.Tel == 1))", _query.GetPredicate().SafeString() ); - - _query = new Query(); - _query.Where( t => t.Name == "A" && t.Tel == 1 ); - Assert.Equal( "t => ((t.Name == \"A\") AndAlso (t.Tel == 1))", _query.GetPredicate().SafeString() ); - - _query = new Query(); - _query.Where( t => t.Name == "" ); - Assert.NotNull( _query.GetPredicate() ); - } - - /// - /// 测试添加查询条件 - 添加规约对象,当值为空时,不会被忽略 - /// - [Fact] - public void TestWhere_Criteria() { - _query.Where( new CriteriaSample() ); - Assert.Equal( "t => ((t.Name == \"A\") AndAlso (t.Tel == 1))", _query.GetPredicate().ToString() ); - - _query = new Query(); - _query.Where( new DefaultCriteria( t => t.Name == null ) ); - Assert.NotNull( _query.GetPredicate() ); - } - - /// - /// 测试添加查询条件 - 当第二个参数为false表示不添加条件 - /// - [Fact] - public void TestWhereIf_False() { - _query.WhereIf( t => t.Name == "A", false ); - Assert.Null( _query.GetPredicate() ); - - _query.WhereIf( t => t.Name == "A", true ); - Assert.Equal( "t => (t.Name == \"A\")", _query.GetPredicate().SafeString() ); - } - - /// - /// 测试添加查询条件 - /// - [Fact] - public void TestWhereIfNotEmpty() { - _query.WhereIfNotEmpty( t => t.Name == "" ); - Assert.Null( _query.GetPredicate() ); - - _query.WhereIfNotEmpty( t => t.Name == null ); - Assert.Null( _query.GetPredicate() ); - - _query.WhereIfNotEmpty( t => t.Name == "A" ); - Assert.Equal( "t => (t.Name == \"A\")", _query.GetPredicate().ToString() ); - - _query.WhereIfNotEmpty( d => d.Tel == 1 ); - Assert.Equal( "t => ((t.Name == \"A\") AndAlso (t.Tel == 1))", _query.GetPredicate().ToString() ); - } - - /// - /// 测试添加查询条件 - 同时添加2个查询条件,抛出异常 - /// - [Fact] - public void TestWhereIfNotEmpty_2Condition_Throw() { - AssertHelper.Throws( () => { - _query.WhereIfNotEmpty( t => t.Name == "A" && t.Tel == 1 ); - }, string.Format( LibraryResource.OnlyOnePredicate, "t => ((t.Name == \"A\") AndAlso (t.Tel == 1))" ) ); - } - - /// - /// 添加范围查询条件 - 整型 - /// - [Fact] - public void TestBetween_Int() { - _query.Between( t => t.Tel, 1, 10, Boundary.Left ); - Assert.Equal( "t => ((t.Tel >= 1) AndAlso (t.Tel < 10))", _query.GetPredicate().ToString() ); - } - - /// - /// 添加范围查询条件 - double - /// - [Fact] - public void TestBetween_Double() { - _query.Between( t => t.DoubleValue, 1.1, 10.1, Boundary.Right ); - Assert.Equal( "t => ((t.DoubleValue > 1.1) AndAlso (t.DoubleValue <= 10.1))", _query.GetPredicate().ToString() ); - } - - /// - /// 添加范围查询条件 - decimal - /// - [Fact] - public void TestBetween_Decimal() { - _query.Between( t => t.DecimalValue, 1.1M, 10.1M, Boundary.Neither ); - Assert.Equal( "t => ((t.DecimalValue > 1.1) AndAlso (t.DecimalValue < 10.1))", _query.GetPredicate().ToString() ); - } - - /// - /// 添加范围查询条件 - 日期 - 不包含时间 - /// - [Fact] - public void TestBetween_Date() { - var min = DateTime.Parse( "2000-1-1 10:10:10" ); - var max = DateTime.Parse( "2000-1-2 10:10:10" ); - var result = new StringBuilder(); - result.AppendFormat( "t => ((t.DateValue >= {0})", DateTime.Parse( "2000/1/1 0:00:00" ) ); - result.AppendFormat( " AndAlso (t.DateValue < {0}))", DateTime.Parse( "2000/1/3 0:00:00" ) ); - - _query.Between( t => t.DateValue, min, max, false ); - Assert.Equal( result.ToString(), _query.GetPredicate().ToString() ); - } - - /// - /// 添加范围查询条件 - 日期 - 包含时间 - /// - [Fact] - public void TestBetween_DateTime() { - var min = DateTime.Parse( "2000-1-1 10:10:10" ); - var max = DateTime.Parse( "2000-1-2 10:10:10" ); - var result = new StringBuilder(); - result.AppendFormat( "t => ((t.DateValue >= {0})", DateTime.Parse( "2000/1/1 10:10:10" ) ); - result.AppendFormat( " AndAlso (t.DateValue <= {0}))", DateTime.Parse( "2000/1/2 10:10:10" ) ); - - _query.Between( t => t.DateValue, min, max ); - Assert.Equal( result.ToString(), _query.GetPredicate().ToString() ); - } - - /// - /// 测试排序 - /// - [Fact] - public void TestOrderBy() { - QueryParameterSample sample = new QueryParameterSample { Order = "Name" }; - _query = new Query( sample ); - Assert.Equal( "Name", _query.GetOrder() ); - - _query.OrderBy( "Age", true ); - Assert.Equal( "Name,Age desc", _query.GetOrder() ); - - _query.OrderBy( t => t.Tel, true ); - Assert.Equal( "Name,Age desc,Tel desc", _query.GetOrder() ); - } - - /// - /// 测试与连接 - /// - [Fact] - public void TestAnd() { - Expression> expression = null; - _query.And( expression ); - _query.And( t => t.Name == "A" ); - Assert.Equal( "t => (t.Name == \"A\")", _query.GetPredicate().ToString() ); - - _query.And( t => t.Tel == 1 ); - Assert.Equal( "t => ((t.Name == \"A\") AndAlso (t.Tel == 1))", _query.GetPredicate().ToString() ); - } - - /// - /// 测试与连接 - 连接查询对象 - /// - [Fact] - public void TestAnd_Query() { - var query = new Query(); - query.Where( t => t.Name == "A" ); - query.OrderBy( t => t.Name ); - _query.And( query ); - Assert.Equal( "t => (t.Name == \"A\")", _query.GetPredicate().ToString() ); - Assert.Equal( "Name", _query.GetOrder() ); - - query = new Query(); - query.Where( t => t.Tel == 1 ); - query.OrderBy( t => t.Tel, true ); - _query.And( query ); - Assert.Equal( "t => ((t.Name == \"A\") AndAlso (t.Tel == 1))", _query.GetPredicate().ToString() ); - Assert.Equal( "Name,Tel desc", _query.GetOrder() ); - } - - /// - /// 测试或连接 - /// - [Fact] - public void TestOr() { - Expression> expression = null; - _query.Or( expression ); - - _query.Where( t => t.Name == "A" ); - var query = new Query(); - query.Where( t => t.Tel == 1 ); - _query.Or( query ); - Assert.Equal( "t => ((t.Name == \"A\") OrElse (t.Tel == 1))", _query.GetPredicate().ToString() ); - } - - /// - /// 测试或连接 - /// - [Fact] - public void TestOr_2() { - _query.Or( t => t.Name == "A", t => t.Name == "", t => t.Tel == 1 ); - Assert.Equal( "t => ((t.Name == \"A\") OrElse (t.Tel == 1))", _query.GetPredicate().ToString() ); - } - - /// - /// 测试或连接 - 连接查询对象 - /// - [Fact] - public void TestOr_Query() { - _query.OrderBy( t => t.Name ); - _query.Where( t => t.Name == "A" ); - _query.Where( t => t.EnglishName == "A" ); - - var query2 = new Query(); - query2.OrderBy( t => t.Age, true ); - query2.Where( t => t.Name == "B" ); - query2.Where( t => t.Age == 1 ); - _query.Or( query2 ); - - var query3 = new Query(); - query3.OrderBy( t => t.Tel ); - query3.Where( t => t.Name == "C" ); - query3.Where( t => t.Age > 10 ); - _query.And( query3 ); - - Expression> expected = t => ( ( t.Name == "A" && t.EnglishName == "A" ) - || ( t.Name == "B" && t.Age == 1 ) ) && ( t.Name == "C" && t.Age > 10 ); - Assert.Equal( expected.ToString(), _query.GetPredicate().ToString() ); - Assert.Equal( "Name,Age desc,Tel", _query.GetOrder() ); - } - } -} diff --git a/test/Util.Tests/Datas/UnitOfWorks/UnitOfWorkManagerTest.cs b/test/Util.Tests/Datas/UnitOfWorks/UnitOfWorkManagerTest.cs deleted file mode 100644 index 3c222842a..000000000 --- a/test/Util.Tests/Datas/UnitOfWorks/UnitOfWorkManagerTest.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using NSubstitute; -using Util.Datas.UnitOfWorks; -using Util.Tests.XUnitHelpers; -using Xunit; - -namespace Util.Tests.Datas.UnitOfWorks { - /// - /// 工作单元服务测试 - /// - public class UnitOfWorkManagerTest { - /// - /// 工作单元 - /// - private readonly IUnitOfWork _unitOfWork; - /// - /// 工作单元2 - /// - private readonly IUnitOfWork _unitOfWork2; - /// - /// 工作单元服务 - /// - private readonly UnitOfWorkManager _manager; - - /// - /// 测试初始化 - /// - public UnitOfWorkManagerTest() { - _unitOfWork = Substitute.For(); - _unitOfWork2 = Substitute.For(); - _manager = new UnitOfWorkManager(); - } - - /// - /// 注册一个空工作单元 - /// - [Fact] - public void TestRegister_Null() { - AssertHelper.Throws( () => _manager.Register( null ) ); - } - - /// - /// 注册一个工作单元并提交 - /// - [Fact] - public void TestCommit_1() { - _manager.Register( _unitOfWork ); - _manager.Commit(); - _unitOfWork.Received(1).Commit(); - } - - /// - /// 注册2个工作单元并提交 - /// - [Fact] - public void TestCommit_2() { - _manager.Register( _unitOfWork ); - _manager.Register( _unitOfWork2 ); - _manager.Commit(); - _unitOfWork.Received( 1 ).Commit(); - _unitOfWork.Received( 1 ).Commit(); - } - - /// - /// 注册一个工作单元并提交 - /// - [Fact] - public async void TestCommitAsync_1() { - _manager.Register( _unitOfWork ); - await _manager.CommitAsync(); - await _unitOfWork.Received( 1 ).CommitAsync(); - } - - /// - /// 注册2个工作单元并提交 - /// - [Fact] - public async void TestCommitAsync_2() { - _manager.Register( _unitOfWork ); - _manager.Register( _unitOfWork2 ); - await _manager.CommitAsync(); - await _unitOfWork.Received( 1 ).CommitAsync(); - await _unitOfWork.Received( 1 ).CommitAsync(); - } - } -} diff --git a/test/Util.Tests/Domains/EntityChangeTest.cs b/test/Util.Tests/Domains/EntityChangeTest.cs deleted file mode 100644 index 83332d8d6..000000000 --- a/test/Util.Tests/Domains/EntityChangeTest.cs +++ /dev/null @@ -1,109 +0,0 @@ -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Domains { - /// - /// 测试获取变更属性集 - 实体 - /// - public class EntityChangeTest { - /// - /// 测试初始化 - /// - public EntityChangeTest() { - _sample = new AggregateRootSample(); - _sample.StringSample = new StringAggregateRootSample(); - _sample2 = new AggregateRootSample(); - _sample2.StringSample = new StringAggregateRootSample(); - } - - /// - /// 聚合根测试样例 - /// - private readonly AggregateRootSample _sample; - /// - /// 聚合根测试样例2 - /// - private readonly AggregateRootSample _sample2; - - /// - /// 测试获取变更属性集 - /// - [Fact] - public void TestGetChanges() { - var changes = _sample.GetChanges( _sample2 ); - Assert.Empty( changes ); - - _sample2.Name = "a"; - changes = _sample.GetChanges( _sample2 ); - Assert.Single( changes ); - Assert.Equal( "", changes[0].OldValue ); - Assert.Equal( "a", changes[0].NewValue ); - - _sample.Name = "a"; - changes = _sample.GetChanges( _sample2 ); - Assert.Empty( changes ); - } - - /// - /// 测试获取变更属性集 - /// - [Fact] - public void TestGetChanges_Lambda_Display() { - _sample2.MobilePhone = "a"; - var changes = _sample.GetChanges( _sample2 ); - Assert.Single( changes ); - Assert.Equal( "MobilePhone", changes[0].PropertyName ); - Assert.Equal( "手机号", changes[0].Description ); - Assert.Equal( "", changes[0].OldValue ); - Assert.Equal( "a", changes[0].NewValue ); - } - - /// - /// 测试获取变更属性集 - /// - [Fact] - public void TestGetChanges_Lambda_Description() { - _sample2.Tel = 1; - var changes = _sample.GetChanges( _sample2 ); - Assert.Single( changes ); - Assert.Equal( "Tel", changes[0].PropertyName ); - Assert.Equal( "电话", changes[0].Description ); - Assert.Equal( "0", changes[0].OldValue ); - Assert.Equal( "1", changes[0].NewValue ); - } - - /// - /// 测试获取变更属性集 - 导航属性 - /// - [Fact] - public void TestGetChanges_Navigate() { - _sample2.Name = "a"; - _sample2.StringSample.Name = "b"; - var changes = _sample.GetChanges( _sample2 ); - Assert.Equal( 2, changes.Count ); - Assert.Equal( "姓名", changes[0].Description ); - Assert.Equal( "", changes[0].OldValue ); - Assert.Equal( "a", changes[0].NewValue ); - Assert.Equal( "StringSampleName", changes[1].Description ); - Assert.Equal( "", changes[1].OldValue ); - Assert.Equal( "b", changes[1].NewValue ); - } - - /// - /// 测试获取变更属性集 - 导航属性集合 - /// - [Fact] - public void TestGetChanges_NavigateCollection() { - _sample.IntSamples.Add( new IntAggregateRootSample { Name = "a" } ); - _sample.IntSamples.Add( new IntAggregateRootSample() { Name = "b" } ); - _sample2.IntSamples.Add( new IntAggregateRootSample() { Name = "a2" } ); - _sample2.IntSamples.Add( new IntAggregateRootSample() { Name = "b2" } ); - var changes = _sample.GetChanges( _sample2 ); - Assert.Equal( 2, changes.Count ); - Assert.Equal( "a", changes[0].OldValue ); - Assert.Equal( "a2", changes[0].NewValue ); - Assert.Equal( "b", changes[1].OldValue ); - Assert.Equal( "b2", changes[1].NewValue ); - } - } -} diff --git a/test/Util.Tests/Domains/GuidEntityTest.cs b/test/Util.Tests/Domains/GuidEntityTest.cs deleted file mode 100644 index 1ce822ef6..000000000 --- a/test/Util.Tests/Domains/GuidEntityTest.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using Util.Exceptions; -using Util.Tests.Samples; -using Util.Tests.XUnitHelpers; -using Xunit; - -namespace Util.Tests.Domains { - /// - /// Guid标识实体测试 - /// - public class GuidEntityTest { - /// - /// 聚合根测试样例 - /// - private AggregateRootSample _sample; - /// - /// 聚合根测试样例2 - /// - private AggregateRootSample _sample2; - - /// - /// 测试初始化 - /// - public GuidEntityTest() { - _sample = new AggregateRootSample(); - _sample2 = new AggregateRootSample(); - } - - /// - /// 测试实体相等性 - 判空 - /// - [Fact] - public void TestEquals_Null() { - Assert.False( _sample.Equals( _sample2 ) ); - Assert.False( _sample.Equals( null ) ); - - Assert.False( _sample == _sample2 ); - Assert.False( _sample == null ); - Assert.False( null == _sample2 ); - - Assert.True( _sample != _sample2 ); - Assert.True( _sample != null ); - Assert.True( null != _sample2 ); - - _sample2 = null; - Assert.False( _sample.Equals( _sample2 ) ); - - _sample = null; - Assert.True( _sample == _sample2 ); - Assert.True( _sample2 == _sample ); - } - - /// - /// 测试实体相等性 - 类型无效 - /// - [Fact] - public void TestEquals_InvalidType() { - Guid id = Guid.NewGuid(); - _sample = new AggregateRootSample( id ); - AggregateRootSample2 sample2 = new AggregateRootSample2( id ); - Assert.False( _sample.Equals( sample2 ) ); - Assert.True( _sample != sample2 ); - Assert.True( sample2 != _sample ); - } - - /// - /// 测试实体相等性 - 当两个实体的标识相同,则实体相同 - /// - [Fact] - public void TestEquals_IdEquals() { - Guid id = Guid.NewGuid(); - _sample = new AggregateRootSample( id ); - _sample2 = new AggregateRootSample( id ); - Assert.True( _sample.Equals( _sample2 ) ); - Assert.True( _sample == _sample2 ); - Assert.False( _sample != _sample2 ); - } - - /// - /// 测试状态输出 - /// - [Fact] - public void TestToString() { - _sample = new AggregateRootSample { Name = "a" }; - Assert.Equal( string.Format( "Id:{0},姓名:a", _sample.Id ), _sample.ToString() ); - } - - /// - /// 测试验证 - Id为空,无法通过 - /// - [Fact] - public void TestValidate_IdIsEmpty() { - AssertHelper.Throws( () => { - _sample = AggregateRootSample.CreateSample( Guid.Empty ); - _sample.Validate(); - }, "Id" ); - } - - /// - /// 验证必填项,通过字符串设置错误消息 - /// - [Fact] - public void TestValidate_Required() { - AssertHelper.Throws( () => { - _sample.Name = null; - _sample.Validate(); - }, "姓名不能为空" ); - } - - /// - /// 测试添加验证规则 - /// - [Fact] - public void TestAddValidationRule() { - _sample = AggregateRootSample.CreateSample(); - AssertHelper.Throws( () => { - _sample.Name = "abcd"; - _sample.AddValidationRule( new ValidationRuleSample( _sample ) ); - _sample.Validate(); - }, "名称长度不能大于3" ); - } - - /// - /// 测试设置验证处理器 - 不进行任何操作,所以不会抛出异常 - /// - [Fact] - public void TestSetValidationHandler() { - _sample = AggregateRootSample.CreateSample(); - _sample.Name = "abcd"; - _sample.AddValidationRule( new ValidationRuleSample( _sample ) ); - _sample.SetValidationHandler( new ValidationHandlerSample() ); - _sample.Validate(); - } - } -} diff --git a/test/Util.Tests/Domains/IntEntityTest.cs b/test/Util.Tests/Domains/IntEntityTest.cs deleted file mode 100644 index 310fcf687..000000000 --- a/test/Util.Tests/Domains/IntEntityTest.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using Util.Exceptions; -using Util.Tests.Samples; -using Util.Tests.XUnitHelpers; -using Xunit; - -namespace Util.Tests.Domains { - /// - /// int标识实体测试 - /// - public class IntEntityTest { - /// - /// 测试初始化 - /// - public IntEntityTest() { - _sample = new IntAggregateRootSample(); - _sample2 = new IntAggregateRootSample(); - } - - /// - /// 聚合根测试样例 - /// - private IntAggregateRootSample _sample; - /// - /// 聚合根测试样例2 - /// - private IntAggregateRootSample _sample2; - - /// - /// 测试实体相等性 - 当两个实体的标识相同,则实体相同 - /// - [Fact] - public void TestEquals_IdEquals() { - _sample = new IntAggregateRootSample( 1 ); - _sample2 = new IntAggregateRootSample( 1 ); - Assert.True( _sample.Equals( _sample2 ) ); - Assert.True( _sample == _sample2 ); - Assert.False( _sample != _sample2 ); - } - - /// - /// 测试实体相等性 - Id为空 - /// - [Fact] - public void TestEquals_Id_Empty() { - _sample = new IntAggregateRootSample( 0 ); - _sample2 = new IntAggregateRootSample( 1 ); - Assert.False( _sample.Equals( _sample2 ) ); - Assert.False( _sample == _sample2 ); - Assert.False( _sample2 == _sample ); - } - - /// - /// 测试状态输出 - /// - [Fact] - public void TestToString() { - _sample = new IntAggregateRootSample { Name = "a" }; - Assert.Equal( string.Format( "Id:{0},姓名:a", _sample.Id ), _sample.ToString() ); - } - - /// - /// 测试验证 - Id为空,无法通过 - /// - [Fact] - public void TestValidate_IdIsEmpty() { - AssertHelper.Throws( () => { - _sample = new IntAggregateRootSample( 0 ); - _sample.Validate(); - }, "Id" ); - } - } -} \ No newline at end of file diff --git a/test/Util.Tests/Domains/Repositories/PagerListTest.cs b/test/Util.Tests/Domains/Repositories/PagerListTest.cs deleted file mode 100644 index b35c3df72..000000000 --- a/test/Util.Tests/Domains/Repositories/PagerListTest.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Util.Domains.Repositories; -using Util.Helpers; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Domains.Repositories { - /// - /// 分页集合测试 - /// - public class PagerListTest { - /// - /// 分页集合 - /// - private readonly PagerList _list; - - /// - /// 测试初始化 - /// - public PagerListTest() { - _list = new PagerList( 1, 2, 3, "Name" ); - _list.Add( AggregateRootSample.CreateSample() ); - _list.Add( AggregateRootSample.CreateSample2() ); - } - - /// - /// 测试集合 - /// - [Fact] - public void TestList() { - Assert.Equal( 2, _list.Data.Count ); - Assert.Equal( "TestName2", _list[1].Name ); - } - - /// - /// 测试转换类型 - /// - [Fact] - public void TestConvert() { - var result = _list.Convert( t => new AggregateRootSample() ); - Assert.Equal( 2, result.Data.Count ); - Assert.Equal( 1, result.Page ); - Assert.Equal( 2, result.PageSize ); - Assert.Equal( 3, result.TotalCount ); - Assert.Equal( 2, result.PageCount ); - Assert.Equal( "Name", result.Order ); - } - - /// - /// 测试json序列化 - /// - [Fact] - public void TestToJson() { - PagerList list = new PagerList(); - list.Add( new Sample() ); - Assert.Contains( "PageCount", Json.ToJson( list ) ); - } - } -} diff --git a/test/Util.Tests/Domains/Repositories/PagerTest.cs b/test/Util.Tests/Domains/Repositories/PagerTest.cs deleted file mode 100644 index bcf2aef82..000000000 --- a/test/Util.Tests/Domains/Repositories/PagerTest.cs +++ /dev/null @@ -1,134 +0,0 @@ -using Util.Domains.Repositories; -using Xunit; - -namespace Util.Tests.Domains.Repositories { - /// - /// 分页参数测试 - /// - public class PagerTest { - /// - /// 测试初始化 - /// - public PagerTest() { - _pager = new Pager(); - } - - /// - /// 分页参数 - /// - private readonly Pager _pager; - - /// - /// 测试分页默认值 - /// - [Fact] - public void TestDefault() { - Assert.Equal( 1, _pager.Page ); - Assert.Equal( 20, _pager.PageSize ); - Assert.Equal( 0, _pager.TotalCount ); - Assert.Equal( 0, _pager.GetPageCount() ); - Assert.Equal( 0, _pager.GetSkipCount() ); - Assert.Equal( "", _pager.Order ); - Assert.Equal( 1, _pager.GetStartNumber() ); - Assert.Equal( 20, _pager.GetEndNumber() ); - } - - /// - /// 测试总页数 - /// - [Fact] - public void TestPageCount() { - _pager.TotalCount = 0; - Assert.Equal( 0, _pager.GetPageCount() ); - - _pager.TotalCount = 100; - Assert.Equal( 5, _pager.GetPageCount() ); - - _pager.TotalCount = 1; - Assert.Equal( 1, _pager.GetPageCount() ); - - _pager.PageSize = 10; - _pager.TotalCount = 100; - Assert.Equal( 10, _pager.GetPageCount() ); - } - - /// - /// 测试页索引 - 小于1,则修正为1 - /// - [Fact] - public void TestPage() { - _pager.Page = 0; - Assert.Equal( 1, _pager.Page ); - - _pager.Page = -1; - Assert.Equal( 1, _pager.Page ); - } - - /// - /// 测试跳过的行数 - /// - [Fact] - public void TestSkipCount() { - _pager.TotalCount = 100; - - _pager.Page = 0; - Assert.Equal( 0, _pager.GetSkipCount() ); - - _pager.Page = 1; - Assert.Equal( 0, _pager.GetSkipCount() ); - - _pager.Page = 2; - Assert.Equal( 20, _pager.GetSkipCount() ); - - _pager.Page = 3; - Assert.Equal( 40, _pager.GetSkipCount() ); - - _pager.Page = 4; - Assert.Equal( 60, _pager.GetSkipCount() ); - - _pager.Page = 5; - Assert.Equal( 80, _pager.GetSkipCount() ); - - _pager.Page = 6; - Assert.Equal( 80, _pager.GetSkipCount() ); - - _pager.TotalCount = 99; - - _pager.Page = 0; - Assert.Equal( 0, _pager.GetSkipCount() ); - - _pager.Page = 1; - Assert.Equal( 0, _pager.GetSkipCount() ); - - _pager.Page = 2; - Assert.Equal( 20, _pager.GetSkipCount() ); - - _pager.Page = 3; - Assert.Equal( 40, _pager.GetSkipCount() ); - - _pager.Page = 4; - Assert.Equal( 60, _pager.GetSkipCount() ); - - _pager.Page = 5; - Assert.Equal( 80, _pager.GetSkipCount() ); - - _pager.Page = 6; - Assert.Equal( 80, _pager.GetSkipCount() ); - - _pager.TotalCount = 0; - _pager.Page = 1; - Assert.Equal( 0, _pager.GetSkipCount() ); - } - - /// - /// 测试起始和结束行数 - /// - [Fact] - public void TestGetStartNumber() { - _pager.Page = 2; - _pager.PageSize = 10; - Assert.Equal( 11, _pager.GetStartNumber() ); - Assert.Equal( 20, _pager.GetEndNumber() ); - } - } -} diff --git a/test/Util.Tests/Domains/StringEntityTest.cs b/test/Util.Tests/Domains/StringEntityTest.cs deleted file mode 100644 index 859384af8..000000000 --- a/test/Util.Tests/Domains/StringEntityTest.cs +++ /dev/null @@ -1,82 +0,0 @@ -using Util.Exceptions; -using Util.Tests.Samples; -using Util.Tests.XUnitHelpers; -using Xunit; - -namespace Util.Tests.Domains { - /// - /// string标识实体测试 - /// - public class StringEntityTest { - /// - /// 测试初始化 - /// - public StringEntityTest() { - _sample = new StringAggregateRootSample(); - _sample2 = new StringAggregateRootSample(); - } - - /// - /// 聚合根测试样例 - /// - private StringAggregateRootSample _sample; - /// - /// 聚合根测试样例2 - /// - private StringAggregateRootSample _sample2; - - /// - /// 测试实体相等性 - 当两个实体的标识相同,则实体相同 - /// - [Fact] - public void TestEquals_IdEquals() { - _sample = new StringAggregateRootSample( "a" ); - _sample2 = new StringAggregateRootSample( "a" ); - Assert.True( _sample.Equals( _sample2 ) ); - Assert.True( _sample == _sample2 ); - Assert.False( _sample != _sample2 ); - } - - /// - /// 测试实体相等性 - Id为空 - /// - [Fact] - public void TestEquals_Id_Null() { - _sample = new StringAggregateRootSample( null ); - _sample2 = new StringAggregateRootSample( "a" ); - Assert.False( _sample.Equals( _sample2 ) ); - Assert.False( _sample == _sample2 ); - Assert.False( _sample2 == _sample ); - - _sample = new StringAggregateRootSample( "a" ); - _sample2 = new StringAggregateRootSample( null ); - Assert.False( _sample.Equals( _sample2 ) ); - Assert.False( _sample == _sample2 ); - Assert.False( _sample2 == _sample ); - } - - /// - /// 测试状态输出 - /// - [Fact] - public void TestToString() { - _sample = new StringAggregateRootSample { Name = "a" }; - Assert.Equal( string.Format( "Id:{0},姓名:a", _sample.Id ), _sample.ToString() ); - } - - /// - /// 测试验证 - Id为空,无法通过 - /// - [Fact] - public void TestValidate_IdIsEmpty() { - AssertHelper.Throws( () => { - _sample = new StringAggregateRootSample( null ); - _sample.Validate(); - }, "Id" ); - AssertHelper.Throws( () => { - _sample = new StringAggregateRootSample( "" ); - _sample.Validate(); - }, "Id" ); - } - } -} \ No newline at end of file diff --git a/test/Util.Tests/Domains/Trees/TreeEntityBaseTest.cs b/test/Util.Tests/Domains/Trees/TreeEntityBaseTest.cs deleted file mode 100644 index 4f08e5135..000000000 --- a/test/Util.Tests/Domains/Trees/TreeEntityBaseTest.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Domains.Trees { - /// - /// 测试树型实体 - /// - public class TreeEntityBaseTest { - /// - /// 初始化一级节点,Guid标识 - /// - [Fact] - public void TestInitPath_1Level() { - var id = Guid.NewGuid(); - var treeObject = new TreeEntitySample( id ); - treeObject.InitPath(); - Assert.Equal( 1, treeObject.Level ); - Assert.Equal( $"{id},", treeObject.Path ); - } - - /// - /// 设置父节点 - /// - [Fact] - public void TestParent() { - var parentId = Guid.NewGuid(); - var id = Guid.NewGuid(); - var parent = new TreeEntitySample( parentId ); - parent.InitPath(); - var child = new TreeEntitySample( id ) { Parent = parent }; - Assert.Same( parent, child.Parent ); - Assert.Equal( 2, child.Level ); - Assert.Equal( $"{parentId},{id},", child.Path ); - } - - /// - /// 设置父节点 - 3级 - /// - [Fact] - public void TestParent_3Level() { - Guid oneId = Guid.NewGuid(); - Guid twoId = Guid.NewGuid(); - Guid threeId = Guid.NewGuid(); - var one = new TreeEntitySample( oneId ); - one.InitPath(); - var two = new TreeEntitySample( twoId ) { Parent = one }; - var three = new TreeEntitySample( threeId ) { Parent = two }; - Assert.Equal( 3, three.Level ); - Assert.Equal( $"{oneId},{twoId},{threeId},", three.Path ); - } - - /// - /// 从路径中获取所有上级节点编号 - /// - [Theory] - [InlineData(null)] - [InlineData( "" )] - public void TestGetParentIdsFromPath_Empty( string path ) { - var entity = new TreeEntitySample( Guid.NewGuid(), path ); - Assert.Equal( 0, entity.GetParentIdsFromPath().Count ); - } - - /// - /// 从路径中获取所有上级节点编号 - /// - [Fact] - public void TestGetParentIdsFromPath_1Level() { - var entity = new TreeEntitySample( Guid.NewGuid() ); - entity.InitPath(); - Assert.Equal( 0, entity.GetParentIdsFromPath().Count ); - } - - /// - /// 从路径中获取所有上级节点编号 - /// - [Fact] - public void TestGetParentIdsFromPath_2Level() { - var parentId = Guid.NewGuid(); - var id = Guid.NewGuid(); - var parent = new TreeEntitySample( parentId ); - parent.InitPath(); - var entity = new TreeEntitySample( id ) { Parent = parent }; - Assert.Equal( 1, entity.GetParentIdsFromPath().Count ); - Assert.Equal( parentId, entity.GetParentIdsFromPath()[0] ); - } - - /// - /// 忽略大小写 - /// - [Fact] - public void TestGetParentIdsFromPath_IgnoreCase() { - Guid id = new Guid( "38F7F754-802F-4F54-925D-CC066483F9DA" ); - var entity = new TreeEntitySample( id, "38F7F754-802F-4F54-925D-CC066483F9da" ); - Assert.Equal( 0, entity.GetParentIdsFromPath().Count ); - } - - /// - /// 包含当前节点 - /// - [Fact] - public void TestGetParentIdsFromPath_ContainsSelf() { - Guid oneLevelId = Guid.NewGuid(); - var entity = new TreeEntitySample( oneLevelId ); - entity.InitPath(); - Assert.Equal( 1, entity.GetParentIdsFromPath( false ).Count ); - } - - /// - /// 测试初始化路径 - /// - [Fact] - public void TestInitPath() { - var parentId = Guid.NewGuid(); - var id = Guid.NewGuid(); - var parent = new TreeEntitySample( parentId ); - parent.InitPath(); - var child = new TreeEntitySample( id ); - child.InitPath( parent ); - Assert.Null( child.Parent ); - Assert.Equal( 2, child.Level ); - Assert.Equal( $"{parentId},{id},", child.Path ); - } - } -} diff --git a/test/Util.Tests/Domains/ValidTest.cs b/test/Util.Tests/Domains/ValidTest.cs deleted file mode 100644 index 7ed3aefac..000000000 --- a/test/Util.Tests/Domains/ValidTest.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using Autofac; -using Util.Dependency; -using Util.Exceptions; -using Util.Helpers; -using Util.Tests.Samples; -using Util.Tests.XUnitHelpers; -using Xunit; -using IContainer = Util.Dependency.IContainer; - -namespace Util.Tests.Domains { - /// - /// 验证测试 - /// - public class ValidTest : IDisposable{ - /// - /// 容器 - /// - private readonly IContainer _container; - /// - /// 测试初始化 - /// - public ValidTest() { - _container = Ioc.CreateContainer( new IocConfig() ); - } - - /// - /// 测试清理 - /// - public void Dispose() { - _container.Dispose(); - } - - /// - /// 测试添加时验证无效 - /// - [Fact] - public void TestAdd_Invalid() { - EntitySample entity = new EntitySample(); - var repository = _container.Create(); - AssertHelper.Throws ( () => repository.Add( entity ), "名称不能为空" ); - } - - /// - /// 测试添加时验证有效 - /// - [Fact] - public void TestAdd_Valid() { - EntitySample entity = new EntitySample{Name = "a"}; - var repository = _container.Create(); - repository.Add( entity ); - } - } - - /// - /// Ioc配置 - /// - public class IocConfig : ConfigBase { - protected override void Load( ContainerBuilder builder ) { - builder.AddScoped(); - } - } -} diff --git a/test/Util.Tests/Domains/ValueObjectBaseTest.cs b/test/Util.Tests/Domains/ValueObjectBaseTest.cs deleted file mode 100644 index b1bcf0194..000000000 --- a/test/Util.Tests/Domains/ValueObjectBaseTest.cs +++ /dev/null @@ -1,115 +0,0 @@ - -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Domains { - /// - /// 测试值对象 - /// - public class ValueObjectBaseTest { - /// - /// 测试初始化 - /// - public ValueObjectBaseTest() { - _sample = new ValueObjectSample( "a", "b" ); - _sample2 = new ValueObjectSample( "a", "b" ); - _sample3 = new ValueObjectSample( "1", "" ); - _aggregateRootSample = new AggregateRootSample(); - _sample4 = new ValueObjectSample( "a", "b", _aggregateRootSample ); - _sample5 = new ValueObjectSample( "a", "b", _aggregateRootSample ); - _sample6 = new ValueObjectSample( "a", "b", _aggregateRootSample, new ValueObjectSample( "a", "b" ) ); - _sample7 = new ValueObjectSample( "a", "b", _aggregateRootSample, new ValueObjectSample( "a", "b" ) ); - } - - /// - /// 值对象测试样例1 - /// - private ValueObjectSample _sample; - /// - /// 值对象测试样例2 - /// - private ValueObjectSample _sample2; - /// - /// 值对象测试样例3 - /// - private ValueObjectSample _sample3; - /// - /// 值对象测试样例4 - /// - private ValueObjectSample _sample4; - /// - /// 值对象测试样例5 - /// - private ValueObjectSample _sample5; - /// - /// 值对象测试样例6 - /// - private ValueObjectSample _sample6; - /// - /// 值对象测试样例7 - /// - private ValueObjectSample _sample7; - /// - /// 聚合根测试样例 - /// - private AggregateRootSample _aggregateRootSample; - - /// - /// 测试对象相等性 - /// - [Fact] - public void TestEquals() { - Assert.False( _sample.Equals( new AggregateRootSample() ) ); - Assert.True( _sample.Equals( _sample2 ), "1" ); - Assert.True( _sample == _sample2, "2" ); - Assert.False( _sample != _sample2, "3" ); - Assert.False( _sample == _sample3, "4" ); - Assert.True( _sample4 == _sample5, "5" ); - Assert.True( _sample4.Equals( _sample5 ), "6" ); - Assert.False( _sample6 == _sample7, "7" ); - Assert.False( _sample6.Equals( _sample7 ), "8" ); - } - - /// - /// 测试对象相等性 - 判空 - /// - [Fact] - public void TestEquals_Null() { - Assert.False( _sample.Equals( null ) ); - Assert.False( _sample == null ); - Assert.False( null == _sample ); - Assert.True( _sample != null ); - - _sample2 = null; - Assert.False( _sample.Equals( _sample2 ) ); - - _sample = null; - Assert.True( _sample == _sample2 ); - Assert.True( _sample2 == _sample ); - } - - /// - /// 测试哈希 - /// - [Fact] - public void TestGetHashCode() { - Assert.True( _sample.GetHashCode() == _sample2.GetHashCode() ); - } - - /// - /// 测试克隆 - /// - [Fact] - public void TestClone() { - _sample3 = _sample.Clone(); - Assert.NotSame( _sample, _sample3 ); - Assert.True( _sample == _sample3 ); - Assert.Equal( "a", _sample3.City ); - - _sample = _sample6.Clone(); - Assert.True( _sample == _sample6 ); - Assert.Equal( "a", _sample.Child.City ); - Assert.Same( _sample.Child, _sample6.Child ); - } - } -} diff --git a/test/Util.Tests/Events/EventBusTest.cs b/test/Util.Tests/Events/EventBusTest.cs deleted file mode 100644 index eec43d1ff..000000000 --- a/test/Util.Tests/Events/EventBusTest.cs +++ /dev/null @@ -1,54 +0,0 @@ -using NSubstitute; -using Util.Events.Default; -using Util.Events.Handlers; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Events { - /// - /// 事件总线测试 - /// - public class EventBusTest { - /// - /// 事件处理器 - /// - private readonly IEventHandler _handler; - /// - /// 事件处理器2 - /// - private readonly IEventHandler _handler2; - - /// - /// 测试初始化 - /// - public EventBusTest() { - _handler = Substitute.For>(); - _handler2 = Substitute.For>(); - } - - /// - /// 测试发布事件 - /// - [Fact] - public void TestPublish() { - var manager = new EventHandlerManagerSample( _handler ); - var eventBus = new EventBus( manager ); - var @event = new EventSample { Name = "a" }; - eventBus.Publish( @event ); - _handler.Received( 1 ).Handle( @event ); - } - - /// - /// 测试发布事件 - /// - [Fact] - public void TestPublish_2() { - var manager = new EventHandlerManagerSample( _handler , _handler2 ); - var eventBus = new EventBus( manager ); - var @event = new EventSample { Name = "a" }; - eventBus.Publish( @event ); - _handler.Received( 1 ).Handle( @event ); - _handler2.Received( 1 ).Handle( @event ); - } - } -} diff --git a/test/Util.Tests/Exceptions/WarningTest.cs b/test/Util.Tests/Exceptions/WarningTest.cs deleted file mode 100644 index 19631831c..000000000 --- a/test/Util.Tests/Exceptions/WarningTest.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; -using Util.Exceptions; -using Xunit; - -namespace Util.Tests.Exceptions { - /// - /// 应用程序异常测试 - /// - public class WarningTest { - /// - /// 测试设置消息 - /// - [Fact] - public void TestMessage() { - Warning warning = new Warning( "A" ); - Assert.Equal( "A", warning.Message ); - } - - /// - /// 测试消息为空 - /// - [Fact] - public void TestMessage_Null() { - Warning warning = new Warning( null, "A" ); - Assert.Equal( string.Empty, warning.Message ); - } - - /// - /// 测试设置错误码 - /// - [Fact] - public void TestCode() { - Warning warning = new Warning( "", "B" ); - Assert.Equal( "B", warning.Code ); - } - - /// - /// 测试设置异常 - /// - [Fact] - public void TestException() { - Warning warning = new Warning( new Exception( "A" ) ); - Assert.Equal( "A", warning.Message ); - } - - /// - /// 测试设置错误消息和异常 - /// - [Fact] - public void TestMessageAndException() { - Warning warning = new Warning( "A", "", new Exception( "C" ) ); - Assert.Equal( $"A{Environment.NewLine}C", warning.Message ); - } - - /// - /// 测试设置2层异常 - /// - [Fact] - public void TestException_2Layer() { - Warning warning = new Warning( "A", "",new Exception( "C", new Exception( "D" ) ) ); - Assert.Equal( $"A{Environment.NewLine}C{Environment.NewLine}D", warning.Message ); - } - - /// - /// 测试设置Warning - /// - [Fact] - public void TestWarning() { - Warning warning = new Warning( new Warning( "A" ) ); - Assert.Equal( "A", warning.Message ); - } - - /// - /// 测试设置2层Warning - /// - [Fact] - public void TestWarning_2Layer() { - Warning warning = new Warning( "A", "", new Warning( "B", "", new Warning( "C" ) ) ); - Assert.Equal( $"A{Environment.NewLine}B{Environment.NewLine}C", warning.Message ); - } - - /// - /// 测试设置3层Warning - /// - [Fact] - public void TestWarning_3Layer() { - Warning warning = new Warning( "A", "", new Warning( "B", "", new Exception( "C", new Warning( "D" ) ) ) ); - Assert.Equal( $"A{Environment.NewLine}B{Environment.NewLine}C{Environment.NewLine}D", warning.Message ); - } - - /// - /// 添加异常数据 - /// - [Fact] - public void TestAddData_1Layer() { - Warning warning = new Warning( "A" ); - warning.Data.Add( "key1", "value1" ); - warning.Data.Add( "key2", "value2" ); - Assert.Equal( $"A{Environment.NewLine}key1:value1{Environment.NewLine}key2:value2{Environment.NewLine}", warning.Message ); - } - - /// - /// 添加2级异常数据 - /// - [Fact] - public void TestAdd_2Layer() { - Exception exception = new Exception( "A" ); - exception.Data.Add( "a", "a1" ); - exception.Data.Add( "b", "b1" ); - Warning warning = new Warning( exception ); - warning.Data.Add( "key1", "value1" ); - warning.Data.Add( "key2", "value2" ); - Assert.Equal( $"A{Environment.NewLine}a:a1{Environment.NewLine}b:b1{Environment.NewLine}key1:value1{Environment.NewLine}key2:value2{Environment.NewLine}", warning.Message ); - } - } -} diff --git a/test/Util.Tests/ExtensionTest.Common.cs b/test/Util.Tests/ExtensionTest.Common.cs deleted file mode 100644 index 5fd48b9b7..000000000 --- a/test/Util.Tests/ExtensionTest.Common.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Collections.Generic; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests { - /// - /// 扩展测试 - 公共扩展 - /// - public partial class ExtensionTest { - /// - /// 测试安全获取值 - /// - [Fact] - public void TestSafeValue() { - int? value = null; - Assert.Equal( 0, value.SafeValue() ); - value = 1; - Assert.Equal( 1, value.SafeValue() ); - } - - /// - /// 测试获取枚举值 - /// - [Fact] - public void TestValue() { - Assert.Equal( 2, EnumSample.B.Value() ); - Assert.Equal( "2", EnumSample.B.Value() ); - } - - /// - /// 测试获取枚举描述 - /// - [Fact] - public void TestDescription() { - Assert.Equal( "B2", EnumSample.B.Description() ); - } - - /// - /// 转换为用分隔符连接的字符串 - /// - [Fact] - public void TestJoin() { - Assert.Equal( "1,2,3", new List { 1, 2, 3 }.Join() ); - Assert.Equal( "'1','2','3'", new List { 1, 2, 3 }.Join( "'" ) ); - } - } -} diff --git a/test/Util.Tests/ExtensionTest.Convert.cs b/test/Util.Tests/ExtensionTest.Convert.cs deleted file mode 100644 index 7474bde3f..000000000 --- a/test/Util.Tests/ExtensionTest.Convert.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.Collections.Generic; -using Xunit; - -namespace Util.Tests { - /// - /// 扩展测试 - 类型转换 - /// - public partial class ExtensionTest { - /// - /// 安全转换为字符串 - /// - /// 输入值 - /// 结果 - [Theory] - [InlineData( null, "" )] - [InlineData( " ", "" )] - [InlineData( 1,"1" )] - [InlineData( " 1 ", "1" )] - public void TestSafeString( object input, string result ) { - Assert.Equal( result, input.SafeString() ); - } - - /// - /// 转换为整数 - /// - [Theory] - [InlineData( "", 0 )] - [InlineData( "1", 1 )] - public void TestToInt( string input, int result ) { - Assert.Equal( result, input.ToInt() ); - } - - /// - /// 转换为可空整数 - /// - [Theory] - [InlineData( "", null )] - [InlineData( "1", 1 )] - public void TestToIntOrNull( string input, int? result ) { - Assert.Equal( result, input.ToIntOrNull() ); - } - - /// - /// 转换为双精度浮点数 - /// - [Theory] - [InlineData( "", 0 )] - [InlineData( "1.2", 1.2 )] - public void TestToDouble( string input, double result ) { - Assert.Equal( result, input.ToDouble() ); - } - - /// - /// 转换为可空双精度浮点数 - /// - [Theory] - [InlineData( "", null )] - [InlineData( "1.2", 1.2 )] - public void TestToDoubleOrNull( string input, double? result ) { - Assert.Equal( result, input.ToDoubleOrNull() ); - } - - /// - /// 转换为高精度浮点数 - /// - [Fact] - public void TestToDecimal() { - Assert.Equal( 0, "".ToDecimal() ); - Assert.Equal( 1.2M, "1.2".ToDecimal() ); - } - - /// - /// 转换为可空高精度浮点数 - /// - [Fact] - public void TestToDecimalOrNull() { - Assert.Null( "".ToDecimalOrNull() ); - Assert.Equal( 1.2M, "1.2".ToDecimalOrNull() ); - } - - /// - /// 转换为日期 - /// - [Fact] - public void TestToDate() { - Assert.Equal( DateTime.MinValue, "".ToDate() ); - Assert.Equal( new DateTime( 2000, 1, 1 ), "2000-1-1".ToDate() ); - } - - /// - /// 转换为可空日期 - /// - [Fact] - public void TestToDateOrNull() { - Assert.Null( "".ToDateOrNull() ); - Assert.Equal( new DateTime( 2000, 1, 1 ), "2000-1-1".ToDateOrNull() ); - } - - /// - /// 转换为Guid - /// - [Fact] - public void TestToGuid() { - Assert.Equal( Guid.Empty, "".ToGuid() ); - Assert.Equal( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ), "B9EB56E9-B720-40B4-9425-00483D311DDC".ToGuid() ); - } - - /// - /// 转换为可空Guid - /// - [Fact] - public void TestToGuidOrNull() { - Assert.Null( "".ToGuidOrNull() ); - Assert.Equal( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ), "B9EB56E9-B720-40B4-9425-00483D311DDC".ToGuidOrNull() ); - } - - /// - /// 转换为Guid集合,值为字符串 - /// - [Fact] - public void TestToGuidList_String() { - const string guid = "83B0233C-A24F-49FD-8083-1337209EBC9A,,EAB523C6-2FE7-47BE-89D5-C6D440C3033A,"; - Assert.Equal( 2, guid.ToGuidList().Count ); - Assert.Equal( new Guid( "83B0233C-A24F-49FD-8083-1337209EBC9A" ), guid.ToGuidList()[0] ); - Assert.Equal( new Guid( "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" ), guid.ToGuidList()[1] ); - } - - /// - /// 转换为Guid集合,值为字符串集合 - /// - [Fact] - public void TestToGuidList_StringList() { - var list = new List { "83B0233C-A24F-49FD-8083-1337209EBC9A", "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" }; - Assert.Equal( 2, list.ToGuidList().Count ); - Assert.Equal( new Guid( "83B0233C-A24F-49FD-8083-1337209EBC9A" ), list.ToGuidList()[0] ); - Assert.Equal( new Guid( "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" ), list.ToGuidList()[1] ); - } - } -} \ No newline at end of file diff --git a/test/Util.Tests/ExtensionTest.DateTime.cs b/test/Util.Tests/ExtensionTest.DateTime.cs deleted file mode 100644 index c8e8aab1f..000000000 --- a/test/Util.Tests/ExtensionTest.DateTime.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System; -using Xunit; - -namespace Util.Tests { - /// - /// 系统扩展测试 - 日期格式扩展 - /// - public partial class ExtensionTest { - /// - /// 测试获取格式化日期时间字符串 - /// - [Fact] - public void TestToDateTimeString() { - string date = "2012-01-02 11:11:11"; - Assert.Equal( date, Util.Helpers.Convert.ToDate( date ).ToDateTimeString() ); - Assert.Equal( "2012-01-02 11:11", Util.Helpers.Convert.ToDate( date ).ToDateTimeString( true ) ); - Assert.Equal( "", Util.Helpers.Convert.ToDateOrNull( "" ).ToDateTimeString() ); - Assert.Equal( date, Util.Helpers.Convert.ToDateOrNull( date ).ToDateTimeString() ); - } - - /// - /// 获取格式化日期字符串 - /// - [Fact] - public void TestToDateString() { - string date = "2012-01-02"; - Assert.Equal( date, Util.Helpers.Convert.ToDate( date ).ToDateString() ); - Assert.Equal( "", Util.Helpers.Convert.ToDateOrNull( "" ).ToDateString() ); - Assert.Equal( date, Util.Helpers.Convert.ToDateOrNull( date ).ToDateString() ); - } - - /// - /// 获取格式化时间字符串 - /// - [Fact] - public void TestToTimeString() { - string date = "2012-01-02 11:11:11"; - Assert.Equal( "11:11:11", Util.Helpers.Convert.ToDate( date ).ToTimeString() ); - Assert.Equal( "", Util.Helpers.Convert.ToDateOrNull( "" ).ToTimeString() ); - Assert.Equal( "11:11:11", Util.Helpers.Convert.ToDateOrNull( date ).ToTimeString() ); - } - - /// - /// 获取格式化毫秒字符串 - /// - [Fact] - public void TestToMillisecondString() { - string date = "2012-01-02 11:11:11.123"; - Assert.Equal( date, Util.Helpers.Convert.ToDate( date ).ToMillisecondString() ); - Assert.Equal( "", Util.Helpers.Convert.ToDateOrNull( "" ).ToMillisecondString() ); - Assert.Equal( date, Util.Helpers.Convert.ToDateOrNull( date ).ToMillisecondString() ); - } - - /// - /// 获取格式化中文日期字符串 - /// - [Fact] - public void TestToChineseDateString() { - string date = "2012-01-02"; - Assert.Equal( "2012年1月2日", Util.Helpers.Convert.ToDate( date ).ToChineseDateString() ); - Assert.Equal( "2012年12月12日", Util.Helpers.Convert.ToDate( "2012-12-12" ).ToChineseDateString() ); - Assert.Equal( "", Util.Helpers.Convert.ToDateOrNull( "" ).ToChineseDateString() ); - Assert.Equal( "2012年1月2日", Util.Helpers.Convert.ToDateOrNull( date ).ToChineseDateString() ); - } - - /// - /// 获取格式化中文日期时间字符串 - /// - [Fact] - public void TestToChineseDateTimeString() { - string date = "2012-01-02 11:11:11"; - Assert.Equal( "2012年1月2日 11时11分11秒", Util.Helpers.Convert.ToDate( date ).ToChineseDateTimeString() ); - Assert.Equal( "2012年12月12日 11时11分11秒", Util.Helpers.Convert.ToDate( "2012-12-12 11:11:11" ).ToChineseDateTimeString() ); - Assert.Equal( "2012年1月2日 11时11分", Util.Helpers.Convert.ToDate( date ).ToChineseDateTimeString( true ) ); - Assert.Equal( "", Util.Helpers.Convert.ToDateOrNull( "" ).ToChineseDateTimeString() ); - Assert.Equal( "2012年1月2日 11时11分11秒", Util.Helpers.Convert.ToDateOrNull( date ).ToChineseDateTimeString() ); - Assert.Equal( "2012年1月2日 11时11分", Util.Helpers.Convert.ToDateOrNull( date ).ToChineseDateTimeString( true ) ); - } - - /// - /// 测试获取时间间隔描述 - /// - [Fact] - public void TestDescription_Span() { - TimeSpan span = new DateTime( 2000, 1, 1, 1, 0, 1 ) - new DateTime( 2000, 1, 1, 1, 0, 0 ); - Assert.Equal( "1秒", span.Description() ); - span = new DateTime( 2000, 1, 1, 1, 1, 0 ) - new DateTime( 2000, 1, 1, 1, 0, 0 ); - Assert.Equal( "1分", span.Description() ); - span = new DateTime( 2000, 1, 1, 1, 0, 0 ) - new DateTime( 2000, 1, 1, 0, 0, 0 ); - Assert.Equal( "1小时", span.Description() ); - span = new DateTime( 2000, 1, 2, 0, 0, 0 ) - new DateTime( 2000, 1, 1, 0, 0, 0 ); - Assert.Equal( "1天", span.Description() ); - span = new DateTime( 2000, 1, 2, 0, 2, 0 ) - new DateTime( 2000, 1, 1, 0, 0, 0 ); - Assert.Equal( "1天2分", span.Description() ); - span = "2000-1-1 06:10:10.123".ToDate() - "2000-1-1 06:10:10.122".ToDate(); - Assert.Equal( "1毫秒", span.Description() ); - span = "2000-1-1 06:10:10.1000001".ToDate() - "2000-1-1 06:10:10.1000000".ToDate(); - Assert.Equal( "0.0001毫秒", span.Description() ); - } - } -} diff --git a/test/Util.Tests/ExtensionTest.Format.cs b/test/Util.Tests/ExtensionTest.Format.cs deleted file mode 100644 index ec5e7875a..000000000 --- a/test/Util.Tests/ExtensionTest.Format.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Xunit; - -namespace Util.Tests { - /// - /// 扩展测试 - 格式化 - /// - public partial class ExtensionTest { - /// - /// 测试获取布尔值描述 - /// - [Fact] - public void TestDescription_Bool() { - bool? value = null; - Assert.Equal( "", value.Description() ); - Assert.Equal( "是", true.Description() ); - Assert.Equal( "否", false.Description() ); - } - } -} diff --git a/test/Util.Tests/ExtensionTest.Lambda.cs b/test/Util.Tests/ExtensionTest.Lambda.cs deleted file mode 100644 index 8f7a4b121..000000000 --- a/test/Util.Tests/ExtensionTest.Lambda.cs +++ /dev/null @@ -1,217 +0,0 @@ -using System; -using System.Linq.Expressions; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests { - /// - /// 扩展测试 - Lambda表达式扩展 - /// - public partial class ExtensionTest { - /// - /// 测试初始化 - /// - public ExtensionTest() { - _parameterExpression = Expression.Parameter( typeof( Sample ), "t" ); - _expression1 = _parameterExpression.Property( "StringValue" ).Call( "Contains", Expression.Constant( "A" ) ); - _expression2 = _parameterExpression.Property( "NullableDateValue" ) - .Property( "Value" ) - .Property( "Year" ) - .Greater( Expression.Constant( 2000 ) ); - } - - /// - /// 参数表达式 - /// - private readonly ParameterExpression _parameterExpression; - - /// - /// 表达式1 - /// - private Expression _expression1; - - /// - /// 表达式2 - /// - private Expression _expression2; - - /// - /// 测试And方法 - /// - [Fact] - public void TestAnd() { - var andExpression = _expression1.And( _expression2 ).ToLambda>( _parameterExpression ); - Expression> expected = t => t.StringValue.Contains( "A" ) && t.NullableDateValue.Value.Year > 2000; - Assert.Equal( expected.ToString(), andExpression.ToString() ); - - Expression> left = t => t.StringValue == "A"; - Expression> right = t => t.StringValue == "B"; - expected = t => t.StringValue == "A" && t.StringValue == "B"; - Assert.Equal( expected.ToString(), left.And( right ).ToString() ); - } - - /// - /// 测试Or方法 - /// - [Fact] - public void TestOr() { - var andExpression = _expression1.Or( _expression2 ).ToLambda>( _parameterExpression ); - Expression> expected = t => t.StringValue.Contains( "A" ) || t.NullableDateValue.Value.Year > 2000; - Assert.Equal( expected.ToString(), andExpression.ToString() ); - } - - /// - /// 测试Or方法 - 左表达式为空 - /// - [Fact] - public void TestOr_LeftIsNull() { - _expression1 = null; - var andExpression = _expression1.Or( _expression2 ).ToLambda>( _parameterExpression ); - Expression> expected = t => t.NullableDateValue.Value.Year > 2000; - Assert.Equal( expected.ToString(), andExpression.ToString() ); - } - - /// - /// 测试Or方法 - 右表达式为空 - /// - [Fact] - public void TestOr_RightIsNull() { - _expression2 = null; - var andExpression = _expression1.Or( _expression2 ).ToLambda>( _parameterExpression ); - Expression> expected = t => t.StringValue.Contains( "A" ); - Assert.Equal( expected.ToString(), andExpression.ToString() ); - } - - /// - /// 测试Or方法 - /// - [Fact] - public void TestOr_2() { - Expression> left = t => t.StringValue == "A"; - Expression> right = t => t.StringValue == "B"; - Expression> expected = t => t.StringValue == "A" || t.StringValue == "B"; - Assert.Equal( expected.ToString(), left.Or( right ).ToString() ); - } - - /// - /// 测试Or方法 - 左表达式为空 - /// - [Fact] - public void TestOr_2_LeftIsNull() { - Expression> left = null; - Expression> right = t => t.StringValue == "B"; - Expression> expected = t => t.StringValue == "B"; - Assert.Equal( expected.ToString(), left.Or( right ).ToString() ); - } - - /// - /// 测试Or方法 - 右表达式为空 - /// - [Fact] - public void TestOr_2_RightIsNull() { - Expression> left = t => t.StringValue == "A"; - Expression> right = null; - Expression> expected = t => t.StringValue == "A"; - Assert.Equal( expected.ToString(), left.Or( right ).ToString() ); - } - - /// - /// 获取lambda表达式成员值 - /// - [Fact] - public void TestValue_LambdaExpression() { - Expression> expression = test => test.StringValue == "A"; - Assert.Equal( "A", expression.Value() ); - } - - /// - /// 测试相等 - /// - [Fact] - public void TestEqual() { - _expression1 = _parameterExpression.Property( "IntValue" ).Equal( 1 ); - Assert.Equal( "t => (t.IntValue == 1)", - _expression1.ToLambda>( _parameterExpression ).ToString() ); - } - - /// - /// 测试不相等 - /// - [Fact] - public void TestNotEqual() { - _expression1 = _parameterExpression.Property( "NullableIntValue" ).NotEqual( 1 ); - Assert.Equal( "t => (t.NullableIntValue != 1)", - _expression1.ToLambda>( _parameterExpression ).ToString() ); - } - - /// - /// 测试大于 - /// - [Fact] - public void TestGreater() { - _expression1 = _parameterExpression.Property( "NullableIntValue" ).Greater( 1 ); - Assert.Equal( "t => (t.NullableIntValue > 1)", - _expression1.ToLambda>( _parameterExpression ).ToString() ); - } - - /// - /// 测试大于等于 - /// - [Fact] - public void TestGreaterEqual_Nullable() { - _expression1 = _parameterExpression.Property( "NullableIntValue" ).GreaterEqual( 1 ); - Assert.Equal( "t => (t.NullableIntValue >= 1)", - _expression1.ToLambda>( _parameterExpression ).ToString() ); - } - - /// - /// 测试小于 - /// - [Fact] - public void TestLess() { - _expression1 = _parameterExpression.Property( "NullableIntValue" ).Less( 1 ); - Assert.Equal( "t => (t.NullableIntValue < 1)", - _expression1.ToLambda>( _parameterExpression ).ToString() ); - } - - /// - /// 测试小于等于 - /// - [Fact] - public void TestLessEqual() { - _expression1 = _parameterExpression.Property( "NullableIntValue" ).LessEqual( 1 ); - Assert.Equal( "t => (t.NullableIntValue <= 1)", - _expression1.ToLambda>( _parameterExpression ).ToString() ); - } - - /// - /// 测试头匹配 - /// - [Fact] - public void TestStartsWith() { - _expression1 = _parameterExpression.Property( "StringValue" ).StartsWith( "a" ); - Assert.Equal( "t => t.StringValue.StartsWith(\"a\")", - _expression1.ToLambda>( _parameterExpression ).ToString() ); - } - - /// - /// 测试尾匹配 - /// - [Fact] - public void TestEndsWith() { - _expression1 = _parameterExpression.Property( "StringValue" ).EndsWith( "a" ); - Assert.Equal( "t => t.StringValue.EndsWith(\"a\")", - _expression1.ToLambda>( _parameterExpression ).ToString() ); - } - - /// - /// 测试模糊匹配 - /// - [Fact] - public void TestContains() { - _expression1 = _parameterExpression.Property( "StringValue" ).Contains( "a" ); - Assert.Equal( "t => t.StringValue.Contains(\"a\")", - _expression1.ToLambda>( _parameterExpression ).ToString() ); - } - } -} diff --git a/test/Util.Tests/ExtensionTest.Validate.cs b/test/Util.Tests/ExtensionTest.Validate.cs deleted file mode 100644 index bcdd3e6fd..000000000 --- a/test/Util.Tests/ExtensionTest.Validate.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using Util.Helpers; -using Util.Tests.Samples; -using Util.Tests.XUnitHelpers; -using Xunit; - -namespace Util.Tests { - /// - /// 扩展测试 - 验证 - /// - public partial class ExtensionTest { - /// - /// 检查空值,不为空则正常执行 - /// - [Fact] - public void TestCheckNull() { - object test = new object(); - test.CheckNull( "test" ); - } - - /// - /// 检查空值,值为null则抛出异常 - /// - [Fact] - public void TestCheckNull_Null_Throw() { - AssertHelper.Throws( () => { - object test = new object(); - test = null; - test.CheckNull( "test" ); - }, "test" ); - } - - /// - /// 测试是否空值 - 字符串 - /// - [Theory] - [InlineData( null,true )] - [InlineData( "", true )] - [InlineData( " ", true )] - [InlineData( "a", false )] - public void TestIsEmpty_String( string value,bool result ) { - Assert.Equal( value.IsEmpty(),result ); - } - - /// - /// 测试是否空值 - Guid - /// - [Fact] - public void TestIsEmpty_Guid() { - Assert.True( Guid.Empty.IsEmpty() ); - Assert.False( Guid.NewGuid().IsEmpty() ); - } - - /// - /// 测试是否空值 - 可空Guid - /// - [Fact] - public void TestIsEmpty_Guid_Nullable() { - Guid? value = null; - Assert.True( value.IsEmpty() ); - value = Guid.Empty; - Assert.True( value.IsEmpty() ); - value = Guid.NewGuid(); - Assert.False( value.IsEmpty() ); - } - } -} diff --git a/test/Util.Tests/Files/FileSizeTest.cs b/test/Util.Tests/Files/FileSizeTest.cs deleted file mode 100644 index ae830c2ab..000000000 --- a/test/Util.Tests/Files/FileSizeTest.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Util.Files; -using Xunit; - -namespace Util.Tests.Files { - /// - /// 测试文件大小 - /// - public class FileSizeTest { - /// - /// 文件字节数 - /// - [Theory] - [InlineData( FileSizeUnit.Byte, 1 )] - [InlineData( FileSizeUnit.K, 1024 )] - [InlineData( FileSizeUnit.M, 1024 * 1024 )] - [InlineData( FileSizeUnit.G, 1024 * 1024 * 1024 )] - public void TestSize( FileSizeUnit unit, long result ) { - Assert.Equal( result, new FileSize( 1, unit ).Size ); - } - - /// - /// 测试文件大小描述 - /// - [Fact] - public void TestToString() { - Assert.Equal( "1 B", new FileSize( 1 ).ToString() ); - Assert.Equal( "1 KB", new FileSize( 1 * 1024 ).ToString() ); - Assert.Equal( "1 MB", new FileSize( 1 * 1024 * 1024 ).ToString() ); - Assert.Equal( "1 GB", new FileSize( 1 * 1024 * 1024 * 1024 ).ToString() ); - } - - /// - /// 获取文件大小,单位:K - /// - [Fact] - public void TestGetSizeByK() { - Assert.Equal( 0, new FileSize( 0 ).GetSizeByK() ); - Assert.Equal( 1, new FileSize( 1024 ).GetSizeByK() ); - Assert.Equal( 0.5, new FileSize( 512 ).GetSizeByK() ); - } - - /// - /// 获取文件大小,单位:M - /// - [Fact] - public void TestGetSizeByM() { - Assert.Equal( 0, new FileSize( 0 ).GetSizeByM() ); - Assert.Equal( 1, new FileSize( 1024 * 1024 ).GetSizeByM() ); - Assert.Equal( 0.5, new FileSize( 512 * 1024 ).GetSizeByM() ); - } - - /// - /// 获取文件大小,单位:G - /// - [Fact] - public void TestGetSizeByG() { - Assert.Equal( 0, new FileSize( 0 ).GetSizeByG() ); - Assert.Equal( 1, new FileSize( 1024 * 1024 * 1024 ).GetSizeByG() ); - Assert.Equal( 0.5, new FileSize( 512 * 1024 * 1024 ).GetSizeByG() ); - } - } -} \ No newline at end of file diff --git a/test/Util.Tests/Files/Paths/DefaultPathGeneratorTest.cs b/test/Util.Tests/Files/Paths/DefaultPathGeneratorTest.cs deleted file mode 100644 index 0c0134779..000000000 --- a/test/Util.Tests/Files/Paths/DefaultPathGeneratorTest.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using Util.Files.Paths; -using Util.Helpers; -using Util.Randoms; -using Util.Tests.XUnitHelpers; -using Xunit; - -namespace Util.Tests.Files.Paths { - /// - /// 路径生成器 - /// - public class DefaultPathGeneratorTest : IDisposable { - /// - /// 路径生成器 - /// - private readonly IPathGenerator _generator; - - /// - /// 初始化测试 - /// - public DefaultPathGeneratorTest() { - _generator = new DefaultPathGenerator( new DefaultBasePath( @"c:\" ) ,new StubRandomGenerator() ); - Time.SetTime( "2000-1-1 10:11:12" ); - } - - /// - /// 测试清理 - /// - public void Dispose() { - Time.Reset(); - } - - /// - /// 测试生成路径 - /// - [Fact] - public void TestGenerate() { - Assert.Equal( @"c:\a-101112.txt", _generator.Generate( "a.txt" ) ); - } - - /// - /// 测试生成路径,验证文件名为空 - /// - [Fact] - public void TestGenerate_FileNameIsEmpty() { - AssertHelper.Throws( () => { - _generator.Generate( "" ); - }, "fileName" ); - } - - /// - /// 测试生成路径,文件名仅包含扩展名,自动创建随机文件名 - /// - [Theory] - [InlineData( "txt" )] - [InlineData( ".txt" )] - public void TestGenerate_Extension( string fileName ) { - Assert.Equal( @"c:\random-101112.txt", _generator.Generate( fileName ) ); - } - - /// - /// 测试生成路径,文件名仅包含扩展名,自动创建随机文件名 - /// - [Theory] - [InlineData( " 中国 *.jpg", @"c:\zg-101112.jpg" )] - [InlineData( "中国*.jpg", @"c:\zg-101112.jpg" )] - [InlineData( "a*!#.jpg", @"c:\a-101112.jpg" )] - public void TestGenerate_( string fileName,string result ) { - Assert.Equal( result, _generator.Generate( fileName ) ); - } - } -} diff --git a/test/Util.Tests/Helpers/ConvertTest.cs b/test/Util.Tests/Helpers/ConvertTest.cs deleted file mode 100644 index a82354a49..000000000 --- a/test/Util.Tests/Helpers/ConvertTest.cs +++ /dev/null @@ -1,396 +0,0 @@ -using System; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Helpers { - /// - /// 类型转换测试 - /// - public class ConvertTest { - /// - /// 转换为整型 - /// - /// 输入值 - /// 结果 - [Theory] - [InlineData( null, 0 )] - [InlineData( "", 0 )] - [InlineData( "1A", 0 )] - [InlineData( "0", 0 )] - [InlineData( "1", 1 )] - [InlineData( "1778019.78", 1778020 )] - [InlineData( "1778019.7801684", 1778020 )] - public void TestToInt( object input, int result ) { - Assert.Equal( result, Util.Helpers.Convert.ToInt( input ) ); - } - - /// - /// 转换为可空整型 - /// - /// 输入值 - /// 结果 - [Theory] - [InlineData( null, null )] - [InlineData( "", null )] - [InlineData( "1A", null )] - [InlineData( "0", 0 )] - [InlineData( "1", 1 )] - [InlineData( "1778019.78", 1778020 )] - [InlineData( "1778019.7801684", 1778020 )] - public void TestToIntOrNull( object input, int? result ) { - Assert.Equal( result, Util.Helpers.Convert.ToIntOrNull( input ) ); - } - - /// - /// 转换为64位整型 - /// - /// 输入值 - /// 结果 - [Theory] - [InlineData( null, 0 )] - [InlineData( "", 0 )] - [InlineData( "1A", 0 )] - [InlineData( "0", 0 )] - [InlineData( "1", 1 )] - [InlineData( "1778019.7801684", 1778020 )] - [InlineData( "177801978016841234", 177801978016841234 )] - public void TestToLong( object input, long result ) { - Assert.Equal( result, Util.Helpers.Convert.ToLong( input ) ); - } - - /// - /// 转换为64位可空整型 - /// - /// 输入值 - /// 结果 - [Theory] - [InlineData( null, null )] - [InlineData( "", null )] - [InlineData( "1A", null )] - [InlineData( "0", 0L )] - [InlineData( "1", 1L )] - [InlineData( "1778019.7801684", 1778020L )] - [InlineData( "177801978016841234", 177801978016841234L )] - public void TestToLongOrNull( object input, long? result ) { - Assert.Equal( result, Util.Helpers.Convert.ToLongOrNull( input ) ); - } - - /// - /// 转换为32位浮点型 - /// - /// 输入值 - /// 结果 - /// 小数位数 - [Theory] - [InlineData( null, 0, null )] - [InlineData( "", 0, null )] - [InlineData( "1A", 0, null )] - [InlineData( "0", 0, null )] - [InlineData( "1", 1, null )] - [InlineData( "1.2", 1.2, null )] - [InlineData( "12.346", 12.35, 2 )] - public void TestToFloat( object input, float result, int? digits ) { - Assert.Equal( result, Util.Helpers.Convert.ToFloat( input, digits ) ); - } - - /// - /// 转换为32位可空浮点型 - /// - /// 输入值 - /// 结果 - /// 小数位数 - [Theory] - [InlineData( null, null, null )] - [InlineData( "", null, null )] - [InlineData( "1A", null, null )] - [InlineData( "0", 0f, null )] - [InlineData( "1", 1f, null )] - [InlineData( "1.2", 1.2f, null )] - [InlineData( "12.346", 12.35f, 2 )] - public void TestToFloatOrNull( object input, float? result, int? digits ) { - Assert.Equal( result, Util.Helpers.Convert.ToFloatOrNull( input, digits ) ); - } - - /// - /// 转换为64位浮点型 - /// - /// 输入值 - /// 结果 - /// 小数位数 - [Theory] - [InlineData( null, 0, null )] - [InlineData( "", 0, null )] - [InlineData( "1A", 0, null )] - [InlineData( "0", 0, null )] - [InlineData( "1", 1, null )] - [InlineData( "1.2", 1.2, null )] - [InlineData( "12.235", 12.24, 2 )] - [InlineData( "12.345", 12.34, 2 )] - [InlineData( "12.3451", 12.35, 2 )] - [InlineData( "12.346", 12.35, 2 )] - public void TestToDouble( object input, double result, int? digits ) { - Assert.Equal( result, Util.Helpers.Convert.ToDouble( input, digits ) ); - } - - /// - /// 转换为64位可空浮点型 - /// - /// 输入值 - /// 结果 - /// 小数位数 - [Theory] - [InlineData( null, null, null )] - [InlineData( "", null, null )] - [InlineData( "1A", null, null )] - [InlineData( "0", 0d, null )] - [InlineData( "1", 1d, null )] - [InlineData( "1.2", 1.2, null )] - [InlineData( "12.355", 12.36, 2 )] - public void TestToDoubleOrNull( object input, double? result, int? digits ) { - Assert.Equal( result, Util.Helpers.Convert.ToDoubleOrNull( input, digits ) ); - } - - /// - /// 转换为128位浮点型 - /// - /// 输入值 - /// 结果 - /// 小数位数 - [Theory] - [InlineData( null, 0, null )] - [InlineData( "", 0, null )] - [InlineData( "1A", 0, null )] - [InlineData( "0", 0, null )] - [InlineData( "1", 1, null )] - [InlineData( "1.2", 1.2, null )] - [InlineData( "12.235", 12.24, 2 )] - [InlineData( "12.345", 12.34, 2 )] - [InlineData( "12.3451", 12.35, 2 )] - [InlineData( "12.346", 12.35, 2 )] - public void TestToDecimal( object input, decimal result, int? digits ) { - Assert.Equal( result, Util.Helpers.Convert.ToDecimal( input, digits ) ); - } - - /// - /// 转换为128位可空浮点型,验证 - /// - /// 输入值 - /// 结果 - /// 小数位数 - [Theory] - [InlineData( null, null, null )] - [InlineData( "", null, null )] - [InlineData( "1A", null, null )] - [InlineData( "1A", null, 2 )] - public void TestToDecimalOrNull_Validate( object input, decimal? result, int? digits ) { - Assert.Equal( result, Util.Helpers.Convert.ToDecimalOrNull( input, digits ) ); - } - - /// - /// 转换为128位可空浮点型,输入值为"0" - /// - [Fact] - public void TestToDecimalOrNull() { - Assert.Equal( 0M, Util.Helpers.Convert.ToDecimalOrNull( "0" ) ); - Assert.Equal( 1.2M, Util.Helpers.Convert.ToDecimalOrNull( "1.2" ) ); - Assert.Equal( 23.46M, Util.Helpers.Convert.ToDecimalOrNull( "23.456", 2 ) ); - } - - /// - /// 转换为布尔型 - /// - /// 输入值 - /// 结果 - [Theory] - [InlineData( null, false )] - [InlineData( "", false )] - [InlineData( "1A", false )] - [InlineData( "0", false )] - [InlineData( "否", false )] - [InlineData( "不", false )] - [InlineData( "no", false )] - [InlineData( "No", false )] - [InlineData( "false", false )] - [InlineData( "fail", false )] - [InlineData( "1", true )] - [InlineData( "是", true )] - [InlineData( "yes", true )] - [InlineData( "true", true )] - [InlineData( "ok", true )] - public void TestToBool( object input, bool result ) { - Assert.Equal( result, Util.Helpers.Convert.ToBool( input ) ); - } - - /// - /// 转换为可空布尔型 - /// - /// 输入值 - /// 结果 - [Theory] - [InlineData( null, null )] - [InlineData( "", null )] - [InlineData( "1A", null )] - [InlineData( "0", false )] - [InlineData( "否", false )] - [InlineData( "不", false )] - [InlineData( "no", false )] - [InlineData( "No", false )] - [InlineData( "false", false )] - [InlineData( "fail", false )] - [InlineData( "1", true )] - [InlineData( "是", true )] - [InlineData( "yes", true )] - [InlineData( "true", true )] - [InlineData( "ok", true )] - public void TestToBoolOrNull( object input, bool? result ) { - Assert.Equal( result, Util.Helpers.Convert.ToBoolOrNull( input ) ); - } - - /// - /// 转换为日期,验证 - /// - [Fact] - public void TestToDate_Validate() { - Assert.Equal( DateTime.MinValue, Util.Helpers.Convert.ToDate( null ) ); - Assert.Equal( DateTime.MinValue, Util.Helpers.Convert.ToDate( "" ) ); - Assert.Equal( DateTime.MinValue, Util.Helpers.Convert.ToDate( "1A" ) ); - } - - /// - /// 转换为日期 - /// - [Fact] - public void TestToDate() { - Assert.Equal( new DateTime( 2000, 1, 1 ), Util.Helpers.Convert.ToDate( "2000-1-1" ) ); - } - - /// - /// 转换为可空日期,验证 - /// - /// 输入值 - /// 结果 - [Theory] - [InlineData( null, null )] - [InlineData( "", null )] - [InlineData( "1A", null )] - public void TestToDateOrNull_Validate( object input, DateTime? result ) { - Assert.Equal( result, Util.Helpers.Convert.ToDateOrNull( input ) ); - } - - /// - /// 转换为可空日期 - /// - [Fact] - public void TestToDateOrNull() { - Assert.Equal( new DateTime( 2000, 1, 1 ), Util.Helpers.Convert.ToDateOrNull( "2000-1-1" ) ); - } - - /// - /// 转换为Guid,验证 - /// - [Fact] - public void TestToGuid_Validate() { - Assert.Equal( Guid.Empty, Util.Helpers.Convert.ToGuid( null ) ); - Assert.Equal( Guid.Empty, Util.Helpers.Convert.ToGuid( "" ) ); - Assert.Equal( Guid.Empty, Util.Helpers.Convert.ToGuid( "1A" ) ); - } - - /// - /// 转换为Guid - /// - [Fact] - public void TestToGuid() { - Assert.Equal( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ), Util.Helpers.Convert.ToGuid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ) ); - } - - /// - /// 转换为可空Guid,验证 - /// - /// 输入值 - /// 结果 - [Theory] - [InlineData( null, null )] - [InlineData( "", null )] - [InlineData( "1A", null )] - public void TestToGuidOrNull_Validate( object input, Guid? result ) { - Assert.Equal( result, Util.Helpers.Convert.ToGuidOrNull( input ) ); - } - - /// - /// 转换为可空Guid - /// - [Fact] - public void TestToGuidOrNull() { - Assert.Equal( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ), Util.Helpers.Convert.ToGuidOrNull( "B9EB56E9-B720-40B4-9425-00483D311DDC" ) ); - } - - /// - /// 转换为Guid集合 - /// - [Fact] - public void TestToGuidList() { - Assert.Equal( 0, Util.Helpers.Convert.ToGuidList( null ).Count ); - Assert.Equal( 0, Util.Helpers.Convert.ToGuidList( "" ).Count ); - - const string guid = "83B0233C-A24F-49FD-8083-1337209EBC9A"; - Assert.Equal( 1, Util.Helpers.Convert.ToGuidList( guid ).Count ); - Assert.Equal( new Guid( guid ), Util.Helpers.Convert.ToGuidList( guid )[0] ); - - const string guid2 = "83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A"; - Assert.Equal( 2, Util.Helpers.Convert.ToGuidList( guid2 ).Count ); - Assert.Equal( new Guid( "83B0233C-A24F-49FD-8083-1337209EBC9A" ), Util.Helpers.Convert.ToGuidList( guid2 )[0] ); - Assert.Equal( new Guid( "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" ), Util.Helpers.Convert.ToGuidList( guid2 )[1] ); - } - - /// - /// 转换为Guid集合 - /// - [Fact] - public void TestToGuidList_2() { - const string guid = "83B0233C-A24F-49FD-8083-1337209EBC9A,,EAB523C6-2FE7-47BE-89D5-C6D440C3033A,"; - Assert.Equal( 2, Util.Helpers.Convert.ToGuidList( guid ).Count ); - Assert.Equal( new Guid( "83B0233C-A24F-49FD-8083-1337209EBC9A" ), Util.Helpers.Convert.ToGuidList( guid )[0] ); - Assert.Equal( new Guid( "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" ), Util.Helpers.Convert.ToGuidList( guid )[1] ); - } - - /// - /// 泛型集合转换 - /// - [Fact] - public void TestToList() { - Assert.Empty( Util.Helpers.Convert.ToList( null )); - Assert.Single( Util.Helpers.Convert.ToList( "1" )); - Assert.Equal( 2, Util.Helpers.Convert.ToList( "1,2" ).Count ); - Assert.Equal( 2, Util.Helpers.Convert.ToList( "1,2" )[1] ); - } - - /// - /// 通用泛型转换 - /// - [Fact] - public void TestTo() { - Assert.Null( Util.Helpers.Convert.To( "" ) ); - Assert.Equal( "1A", Util.Helpers.Convert.To( "1A" ) ); - Assert.Equal( 0, Util.Helpers.Convert.To( null ) ); - Assert.Equal( 0, Util.Helpers.Convert.To( "" ) ); - Assert.Equal( 0, Util.Helpers.Convert.To( "2A" ) ); - Assert.Equal( 1, Util.Helpers.Convert.To( "1" ) ); - Assert.Null( Util.Helpers.Convert.To( null ) ); - Assert.Null( Util.Helpers.Convert.To( "" ) ); - Assert.Null( Util.Helpers.Convert.To( "3A" ) ); - Assert.Equal( Guid.Empty, Util.Helpers.Convert.To( "" ) ); - Assert.Equal( Guid.Empty, Util.Helpers.Convert.To( "4A" ) ); - Assert.Equal( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ), Util.Helpers.Convert.To( "B9EB56E9-B720-40B4-9425-00483D311DDC" ) ); - Assert.Equal( new Guid( "B9EB56E9-B720-40B4-9425-00483D311DDC" ), Util.Helpers.Convert.To( "B9EB56E9-B720-40B4-9425-00483D311DDC" ) ); - Assert.Equal( 12.5, Util.Helpers.Convert.To( "12.5" ) ); - Assert.Equal( 12.5, Util.Helpers.Convert.To( "12.5" ) ); - Assert.Equal( 12.5M, Util.Helpers.Convert.To( "12.5" ) ); - Assert.True( Util.Helpers.Convert.To( "true" ) ); - Assert.Equal( new DateTime( 2000, 1, 1 ), Util.Helpers.Convert.To( "2000-1-1" ) ); - Assert.Equal( new DateTime( 2000, 1, 1 ), Util.Helpers.Convert.To( "2000-1-1" ) ); - var guid = Guid.NewGuid(); - Assert.Equal( guid.ToString(), Util.Helpers.Convert.To( guid ) ); - Assert.Equal( EnumSample.C, Util.Helpers.Convert.To( "c" ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Tests/Helpers/EncryptTest.cs b/test/Util.Tests/Helpers/EncryptTest.cs deleted file mode 100644 index d666b6c7d..000000000 --- a/test/Util.Tests/Helpers/EncryptTest.cs +++ /dev/null @@ -1,157 +0,0 @@ -using System.Text; -using Util.Helpers; -using Xunit; -using Xunit.Abstractions; - -namespace Util.Tests.Helpers { - /// - /// 加密测试 - /// - public class EncryptTest { - /// - /// 控制台输出 - /// - private readonly ITestOutputHelper _output; - - /// - /// 测试初始化 - /// - public EncryptTest( ITestOutputHelper output ) { - _output = output; - } - - /// - /// 测试Md5加密,返回16位结果 - /// - [Theory] - [InlineData(null,"")] - [InlineData( "", "" )] - [InlineData( " ", "" )] - [InlineData( "a", "C0F1B6A831C399E2" )] - [InlineData( "中国", "CB143ACD6C929826" )] - public void TestMd5By16( string input, string result ) { - Assert.Equal( result, Encrypt.Md5By16( input ) ); - } - - /// - /// 测试Md5加密,返回32位结果 - /// - [Theory] - [InlineData( null, "" )] - [InlineData( "", "" )] - [InlineData( " ", "" )] - [InlineData( "a", "0CC175B9C0F1B6A831C399E269772661" )] - [InlineData( "中国", "C13DCEABCB143ACD6C9298265D618A9F" )] - public void TestMd5By32( string input, string result ) { - Assert.Equal( result, Encrypt.Md5By32( input ) ); - } - - /// - /// 测试DES加密验证 - /// - [Theory] - [InlineData( null,"", "" )] - [InlineData( "","", "" )] - [InlineData( "1", "", "" )] - [InlineData( "1", "2", "" )] - public void TestDes_Validate( string input,string key, string result ) { - Assert.Equal( result, Encrypt.DesEncrypt( input, key,Encoding.UTF8 ) ); - Assert.Equal( result, Encrypt.DesDecrypt( input, key, Encoding.UTF8 ) ); - } - - /// - /// 测试DES加密 - /// - [Fact] - public void TestDes_1() { - const double value = 100.123; - var encode = Encrypt.DesEncrypt( value ); - _output.WriteLine( encode ); - Assert.Equal( value.SafeString(), Encrypt.DesDecrypt( encode ) ); - } - - /// - /// 测试DES加密 - /// - [Fact] - public void TestDes_2() { - const string value2 = @"~!@#$%^&*()_+|,./;[]'{}""}{?>:<> \\ qwe测 *试rtyuiopadE15R3JrMnByS3c9sdfghjklzxcvbnm1234567890-=\"; - var encode = Encrypt.DesEncrypt( value2 ); - _output.WriteLine( encode ); - Assert.Equal( value2, Encrypt.DesDecrypt( encode ) ); - } - - /// - /// 测试AES加密验证 - /// - [Theory] - [InlineData( null, "", "" )] - [InlineData( "", "", "" )] - [InlineData( "1", "", "" )] - public void TestAes_Validate( string input, string key, string result ) { - Assert.Equal( result, Encrypt.AesEncrypt( input, key, Encoding.UTF8 ) ); - Assert.Equal( result, Encrypt.AesDecrypt( input, key, Encoding.UTF8 ) ); - } - - /// - /// 测试AES加密 - /// - [Fact] - public void TestAes_1() { - var encode = Encrypt.AesEncrypt( "a" ); - _output.WriteLine( encode ); - Assert.Equal( "a", Encrypt.AesDecrypt( encode ) ); - } - - /// - /// 测试AES加密 - /// - [Fact] - public void TestAes_2() { - var encode = Encrypt.AesEncrypt( "中国" ); - _output.WriteLine( encode ); - Assert.Equal( "中国", Encrypt.AesDecrypt( encode ) ); - } - - /// - /// 测试Rsa签名验证 - /// - [Theory] - [InlineData( null, "", "" )] - [InlineData( "", "", "" )] - [InlineData( "1", "", "" )] - public void TestRsaSign_Validate( string input, string key, string result ) { - Assert.Equal( result, Encrypt.AesEncrypt( input, key, Encoding.UTF8 ) ); - Assert.Equal( result, Encrypt.AesDecrypt( input, key, Encoding.UTF8 ) ); - } - - /// - /// Rsa私钥 - /// - public const string RsaKey = "MIIEogIBAAKCAQEAuLbs8Jugb3qhzDu4rvMqQ8n1RS8TQCpJ3+Cg9qR/RgMcpBx8+0tUiYkfOOnzxGlBuIwGF7Hqyho2E1ICNoIeNY4GkUhxBk7/wz4M6/tbfKSmWp1PAi9gVOxT0Io1kNBAV0it+uiDA176qk2tIKPxQ7UBPRB6qVELHuM7Y9AVoOQbHe56+rEoTiRo13NTx01yg0xiZDzS5gAe/vu+rDAKBczm7ZQ0A4U//modw1/rV+GKiqJ8CIDHe7a8oW2rthDNTZ2C/CHug4QMEmhaNazvhzjyAE1rfvYLF0o92qEfkip3IQRJnFM4rrr9QvWjkSPO7sPu5rMyE4oeUZHJ8luhIwIDAQABAoIBAGE8ytaO1pJY+DvPZJWUpLcy5c8ZzQSGPoWAdrvgNK/ii31JEfIn4cTVTn5jilPnJRXFgJ+QpYzm53icP1X6gXSn44UvoXA0vidFzv+bProK4xfon+MCla+fCTBK0Y/+USChvhTLucxYf5SPd4grRaLi8lf3CNuBMl18OZN9wyUCicgcqOp2mwi46daqqqvNLJwzmiKVCMb82JKEVShkmRDp8+ST7imwtXypUzLnwRt00xobiO8Gi1B1jYE4xM0hmkVgEHHLMGUOlfECH/VcWFLRkwosM3P6PYwb/mfBiDrIzN2mbueKZxMZureUqll9uLWRwPEvTosqc+tl+a4jCkECgYEA784SWiUGoSBDuKu9wm2D5Almz0gZavMv15cYsTfbu6UzsLcIRLEqLG1Rv9bFnwR39Gd1Cl3JAKDlxhs8qxJhy5zi0TJUowF9/QAg7TgBkGZVU4p+6SKF4mPKKsC4tnpENM+FnylncpCjFs45+MNruUIq1OVkcfxGBaBxDB/SM6kCgYEAxTBozzAyfH+9HHNisCQ2x7iNWEuaydY39V8vAlGPRZSbbo8OoV3wlidZm2wAhRybUCg7wefdbMAJlH8Uq/HnwuwanEkMO4IH/t5WI/MKk4Wc55iwC1abqQZrAxNCfjG+fShr2AZHO1jTj4oyd3BuQFTQBdRtiB+g8ibrGYG1resCgYA/sJ2TL45JMQaLf6GQiAGliRGzL9UAYMJuIgU+3DUR61iFMLeTdvJahlZV+zbVexxY3zlonWwLLLCaIxXD4cfziiF7qkBsYrMRhP05w8w2i9dRrtDyHmcsr5A8Np9YZ7TByfQVR6vf86Y9IlynQ0/TDk3N6Xb6BySZzfj4XWM4sQKBgBKeW4cUme+/b++7xVm0UafR+SaZHOhp3abBcgLaCJkdSv/Jaiw6XnkPBhryu6nV5aRP6DSK3BFkoILw7Na/ZI63FFwlWY5U3MRn4eJLFHiRaRtFA3pOlywCeyAzNVgNAlt28ZfYH+munWs0NUepyf8xAuNKB32O3vd+TTx/TtQ5AoGAGgD1Rc2hTopjXI8P5K1wuEMrvUp2uo0apguHu2uzIUpFyQYgjp80hBsqDj6e22R0LDzQTPrj8i+KvLZ4Xu8iCCQrUMqOvE6oZbQ7ukJ+wZebLOnrI6/AzD/zza3LZMXt/lKFgbaiYFLOSPEwxg+VBxxe10aQ3ddp5NuBDjQXJW0="; - - /// - /// 测试Rsa签名算法 - /// - [Fact] - public void TestRsaSign() { - const string value = "sign_type=RSA"; - const string result = "b5jaOTwneLBLTXmemp2BvThFqpmgBsM60GX28MUERYx0vRGiWLw31sif7mlt6Sz063p9zo3quQ8hCwDy6Qssz7i50pqHaJuobpayQZJNHiYpzH07ZxkvJhqsG8IrXj4Q2rmyxpDayU3kg9lhT14VadnLflaypwTpvuy3nGpIoY62d5ciObwxJBDYeilIJag5iY/xTWqv1z4TAr5E6u4zo2aY4rGGKruIX4vsI58EmxzI82clz11uK964Eco/RXCZrha3Vthx5sa6yIPvr2xG95Va4UZglgX7c/wXyMyFp1t/MtKb/0IdQocuRtBmyJw5n0CdmTfw7WxcdNEecgwqjQ=="; - var encode = Encrypt.RsaSign( value, RsaKey ); - _output.WriteLine( encode ); - Assert.Equal( result, encode ); - } - - /// - /// 测试Rsa2签名算法 - /// - [Fact] - public void TestRsa2Sign() { - const string value = "sign_type=RSA2"; - const string result = "cFIjAWDAuNzRYzGOr65ux4e5GEOUvKUT0mLTpAJ89vem70IsdKCrs0IY2TANw3I6pBqdeG0Lz6kNeWHkurN+tj1+C/7ZpRgHIilV+sUU5Dv0Nw/cDVjvs4fyKJ4CEr8zcs1MB1ek0COuQ/kfHxbAr9sWE9a0nqxnZ/FnsDy5ogFP1LQStkms+e7Ph9CC/dyl6JRlpgZx7/NwnN9kF3zEnVwdPxxLq5as1EV7FmlpLcuI/tkCpL8G+vPJcB3xktM9EBBRMR+peDbusZ1fOAuxE7zbW3XVsgz7JzKUcHE5KNS3zzcov404zKT/8i/ezyCxRCWRHDy3O3zHg5bUUOluIQ=="; - var encode = Encrypt.Rsa2Sign( value, RsaKey ); - _output.WriteLine( encode ); - Assert.Equal( result, encode ); - } - } -} diff --git a/test/Util.Tests/Helpers/EnumTest.cs b/test/Util.Tests/Helpers/EnumTest.cs deleted file mode 100644 index 90bf3e33a..000000000 --- a/test/Util.Tests/Helpers/EnumTest.cs +++ /dev/null @@ -1,195 +0,0 @@ -using System; -using Util.Tests.Samples; -using Util.Tests.XUnitHelpers; -using Xunit; - -namespace Util.Tests.Helpers { - /// - /// 测试枚举操作 - /// - public class EnumTest { - /// - /// 测试获取枚举实例 - /// - [Theory] - [InlineData( "C", EnumSample.C )] - [InlineData( "3", EnumSample.C )] - public void TestParse( string memeber, EnumSample sample ) { - Assert.Equal( sample, Util.Helpers.Enum.Parse( memeber ) ); - } - - /// - /// 测试获取枚举实例 - 参数为空,抛出异常 - /// - [Theory] - [InlineData(null)] - [InlineData( "" )] - public void TestParse_MemberIsEmpty( string member ) { - AssertHelper.Throws( () => { Util.Helpers.Enum.Parse( member ); }, "member" ); - } - - /// - /// 测试获取枚举实例 - 可空枚举 - /// - [Theory] - [InlineData( null, null )] - [InlineData( "", null )] - [InlineData( " ", null )] - [InlineData( "C", EnumSample.C )] - [InlineData( "3", EnumSample.C )] - public void TestParse_Nullable( string memeber, EnumSample? sample ) { - Assert.Equal( sample, Util.Helpers.Enum.Parse( memeber ) ); - } - - /// - /// 测试获取枚举成员名 - /// - [Theory] - [InlineData( null,"" )] - [InlineData( "","" )] - [InlineData( " ", " " )] - [InlineData( "C", "C" )] - [InlineData( 3, "C" )] - [InlineData( EnumSample.C, "C" )] - public void TestGetName( object member, string name ) { - Assert.Equal( name, Util.Helpers.Enum.GetName( member ) ); - } - - /// - /// 测试获取枚举成员名 - 验证传入的枚举参数并非枚举类型 - /// - [Fact] - public void TestGetName_Validate() { - Assert.Equal( string.Empty, Util.Helpers.Enum.GetName( typeof( Sample ), 3 ) ); - } - - /// - /// 测试获取枚举成员名 - 可空枚举 - /// - [Theory] - [InlineData( null, "" )] - [InlineData( "", "" )] - [InlineData( " ", " " )] - [InlineData( "C", "C" )] - [InlineData( 3, "C" )] - [InlineData( EnumSample.C, "C" )] - public void TestGetName_Nullable( object member, string name ) { - Assert.Equal( name, Util.Helpers.Enum.GetName( member ) ); - } - - /// - /// 测试获取枚举成员值 - 验证 - /// - [Fact] - public void TestGetValue_Validate() { - AssertHelper.Throws( () => Util.Helpers.Enum.GetValue( null ), "member" ); - AssertHelper.Throws( () => Util.Helpers.Enum.GetValue( string.Empty ), "member" ); - AssertHelper.Throws( () => Util.Helpers.Enum.GetValue( string.Empty ), "member" ); - } - - /// - /// 测试获取枚举成员值 - /// - [Theory] - [InlineData("C",3)] - [InlineData( 3, 3 )] - [InlineData( EnumSample.C, 3 )] - public void TestGetValue( object member, int value ) { - Assert.Equal( value, Util.Helpers.Enum.GetValue( member ) ); - } - - /// - /// 测试获取枚举成员值 - 可空枚举 - /// - [Theory] - [InlineData( "C", 3 )] - [InlineData( 3, 3 )] - [InlineData( EnumSample.C, 3 )] - public void TestGetValue_Nullable( object member, int value ) { - Assert.Equal( value, Util.Helpers.Enum.GetValue( member ) ); - } - - /// - /// 测试获取枚举描述 - /// - [Theory] - [InlineData(null,"")] - [InlineData("","")] - [InlineData( "A", "A" )] - [InlineData( "B", "B2" )] - [InlineData( 2, "B2" )] - [InlineData( EnumSample.B, "B2" )] - public void TestGetDescription(object member,string description) { - Assert.Equal( description, Util.Helpers.Enum.GetDescription( member ) ); - } - - /// - /// 测试获取枚举描述 - 可空枚举 - /// - [Theory] - [InlineData( null, "" )] - [InlineData( "", "" )] - [InlineData( "A", "A" )] - [InlineData( "B", "B2" )] - [InlineData( 2, "B2" )] - [InlineData( EnumSample.B, "B2" )] - public void TestGetDescription_Nullable( object member, string description ) { - Assert.Equal( description, Util.Helpers.Enum.GetDescription( member ) ); - } - - /// - /// 测试获取项集合 - /// - [Fact] - public void TestGetItems() { - var items = Util.Helpers.Enum.GetItems(); - Assert.Equal( 5, items.Count ); - Assert.Equal( "A", items[0].Text ); - Assert.Equal( 1, items[0].Value ); - Assert.Equal( "D4", items[3].Text ); - Assert.Equal( 4, items[3].Value ); - Assert.Equal( "E5", items[4].Text ); - Assert.Equal( 5, items[4].Value ); - } - - /// - /// 测试获取项集合 - /// - [Fact] - public void TestGetItems_Type() { - var items = Util.Helpers.Enum.GetItems(typeof( EnumSample ) ); - Assert.Equal( 5, items.Count ); - Assert.Equal( "A", items[0].Text ); - Assert.Equal( 1, items[0].Value ); - Assert.Equal( "D4", items[3].Text ); - Assert.Equal( 4, items[3].Value ); - Assert.Equal( "E5", items[4].Text ); - Assert.Equal( 5, items[4].Value ); - } - - /// - /// 测试获取项集合 - 可空枚举 - /// - [Fact] - public void TestGetItems_Nullable() { - var items = Util.Helpers.Enum.GetItems(); - Assert.Equal( 5, items.Count ); - Assert.Equal( "A", items[0].Text ); - Assert.Equal( 1, items[0].Value ); - Assert.Equal( "D4", items[3].Text ); - Assert.Equal( 4, items[3].Value ); - Assert.Equal( "E5", items[4].Text ); - Assert.Equal( 5, items[4].Value ); - } - - /// - /// 测试获取项集合 - 验证枚举类型 - /// - [Fact] - public void TestGetItems_Validate() { - AssertHelper.Throws( () => { - Util.Helpers.Enum.GetItems(); - }, "类型 Util.Tests.Samples.Sample 不是枚举" ); - } - } -} diff --git a/test/Util.Tests/Helpers/IocTest.cs b/test/Util.Tests/Helpers/IocTest.cs deleted file mode 100644 index 761dec91f..000000000 --- a/test/Util.Tests/Helpers/IocTest.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System.Collections.Generic; -using Autofac; -using Util.Dependency; -using Util.Helpers; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Helpers { - /// - /// Ioc配置 - /// - public class TestIocConfig : ConfigBase { - /// - /// 加载配置 - /// - protected override void Load( ContainerBuilder builder ) { - builder.AddScoped(); - } - } - - /// - /// Ioc测试 - /// - public class IocTest { - /// - /// 初始化Ioc测试 - /// - public IocTest() { - Ioc.Register( new TestIocConfig() ); - } - - /// - /// 测试创建实例 - /// - [Fact] - public void TestCreate() { - var sample = Ioc.Create(); - Assert.NotNull( sample ); - } - - /// - /// 测试创建实例 - /// - [Fact] - public void TestCreate_2() { - var sample = Ioc.Create(typeof(ISample)); - Assert.NotNull( sample ); - } - - /// - /// 测试作用域 - /// - [Fact] - public void TestScope() { - using ( var scope = Ioc.BeginScope() ) { - var sample = scope.Create(); - Assert.NotNull( sample ); - } - } - - /// - /// 测试集合 - /// - [Fact] - public void TestCollection() { - var samples = Ioc.Create>(); - Assert.NotNull( samples ); - Assert.Single( samples ); - } - - /// - /// 创建集合 - /// - [Fact] - public void TestCreateList() { - var samples = Ioc.CreateList(); - Assert.NotNull( samples ); - Assert.Single( samples ); - } - - /// - /// 创建集合 - /// - [Fact] - public void TestCreateList_2() { - var samples = Ioc.CreateList( typeof( ISample ) ); - Assert.NotNull( samples ); - Assert.Single( samples ); - } - } -} diff --git a/test/Util.Tests/Helpers/JsonTest.cs b/test/Util.Tests/Helpers/JsonTest.cs deleted file mode 100644 index 72a299a4a..000000000 --- a/test/Util.Tests/Helpers/JsonTest.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Linq; -using System.Text; -using Newtonsoft.Json; -using Util.Helpers; -using Util.Tests.Samples; -using Util.Tests.XUnitHelpers; -using Xunit; - -namespace Util.Tests.Helpers { - /// - /// Json测试 - /// - public class JsonTest { - /// - /// 测试循环引用序列化 - /// - [Fact] - public void TestLoop() { - A a = new A { Name = "a" }; - B b = new B { Name = "b" }; - C c = new C { Name = "c" }; - a.B = b; - b.C = c; - c.A = a; - AssertHelper.Throws( () => Json.ToJson( c ) ); - } - - /// - /// 转成Json,验证空 - /// - [Fact] - public void TestToJson_Null() { - Assert.Equal( "{}", Json.ToJson( null ) ); - } - - /// - /// 测试转成Json - /// - [Fact] - public void TestToJson() { - var result = new StringBuilder(); - result.Append( "{" ); - result.Append( "\"Name\":\"a\"," ); - result.Append( "\"nickname\":\"b\"," ); - result.Append( "\"Value\":null," ); - result.Append( "\"Date\":\"2012/1/1 0:00:00\"," ); - result.Append( "\"Age\":1," ); - result.Append( "\"isShow\":true" ); - result.Append( "}" ); - var actualData = JsonTestSample.Create(); - actualData.Date = DateTime.Parse(actualData.Date).ToString("yyyy/M/d 0:00:00"); - Assert.Equal( result.ToString(), Json.ToJson(actualData) ); - } - - /// - /// 测试转成Json,将双引号转成单引号 - /// - [Fact] - public void TestToJson_ToSingleQuotes() { - var result = new StringBuilder(); - result.Append( "{" ); - result.Append( "'Name':'a'," ); - result.Append( "'nickname':'b'," ); - result.Append( "'Value':null," ); - result.Append( "'Date':'2012/1/1 0:00:00'," ); - result.Append( "'Age':1," ); - result.Append( "'isShow':true" ); - result.Append( "}" ); - - var actualData = JsonTestSample.Create(); - actualData.Date = DateTime.Parse(actualData.Date).ToString("yyyy/M/d 0:00:00"); - Assert.Equal( result.ToString(), Json.ToJson(actualData, true ) ); - } - - /// - /// 测试转换实体为Json - /// - [Fact] - public void TestToJson_Entity() { - AggregateRootSample entity = new AggregateRootSample(); - entity.Code = "a"; - entity.IntSamples.Add( new IntAggregateRootSample{Name = "b"} ); - var json = Util.Helpers.Json.ToJson( entity ); - entity = Util.Helpers.Json.ToObject( json ); - Assert.Equal( "a", entity.Code ); - Assert.Equal( "b", entity.IntSamples.First().Name ); - } - - /// - /// 测试转成对象 - /// - [Fact] - public void TestToObject() { - var customer = Json.ToObject( "{\"Name\":\"a\"}" ); - Assert.Equal( "a", customer.Name ); - } - } -} diff --git a/test/Util.Tests/Helpers/LambdaTest.cs b/test/Util.Tests/Helpers/LambdaTest.cs deleted file mode 100644 index b3efddc6b..000000000 --- a/test/Util.Tests/Helpers/LambdaTest.cs +++ /dev/null @@ -1,515 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using Util.Datas.Queries; -using Util.Helpers; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Helpers { - /// - /// 测试Lambda表达式操作 - /// - public class LambdaTest { - - #region GetMember(获取成员) - - /// - /// 测试获取成员 - /// - [Fact] - public void TestGetMember() { - Assert.Null( Lambda.GetMember( null ) ); - - Expression> expression = t => t.StringValue; - Assert.Equal( "StringValue", Lambda.GetMember( expression ).Name ); - - Expression> expression2 = t => t.Test2.StringValue; - Assert.Equal( "StringValue", ( (PropertyInfo)Lambda.GetMember( expression2 ) ).Name ); - } - - #endregion - - #region GetName(获取成员名称) - - /// - /// 测试获取成员名称 - /// - [Fact] - public void TestGetName() { - Assert.Equal( "", Lambda.GetName( null ) ); - - Expression> expression = t => t.StringValue; - Assert.Equal( "StringValue", Lambda.GetName( expression ) ); - - Expression> expression2 = t => t.Test2.StringValue; - Assert.Equal( "Test2.StringValue", Lambda.GetName( expression2 ) ); - - Expression> expression3 = t => t.Test2.Test3.StringValue; - Assert.Equal( "Test2.Test3.StringValue", Lambda.GetName( expression3 ) ); - - Expression> expression4 = t => t.NullableIntValue; - Assert.Equal( "NullableIntValue", Lambda.GetName( expression4 ) ); - - Expression> expression5 = t => t.IntValue; - Assert.Equal( "IntValue", Lambda.GetName( expression5 ) ); - } - - #endregion - - #region GetNames(获取成员名称列表) - - /// - /// 测试获取成员名称列表 - /// - [Fact] - public void TestGetNames() { - Expression> expression = ( t => new object[] { t.Test2.StringValue, t.IntValue } ); - Assert.Equal( 2, Lambda.GetNames( expression ).Count ); - Assert.Equal( "Test2.StringValue", Lambda.GetNames( expression )[0] ); - Assert.Equal( "IntValue", Lambda.GetNames( expression )[1] ); - } - - #endregion - - #region GetValue(获取成员值) - - /// - /// 测试获取成员值 - 返回类型为Object - /// - [Fact] - public void TestGetValue_Object() { - Expression> expression = t => t.StringValue == "A"; - Assert.Equal( "A", Lambda.GetValue( expression ) ); - } - - /// - /// 测试获取成员值 - 返回类型为bool - /// - [Fact] - public void TestGetValue() { - Assert.Null( Lambda.GetValue( null ) ); - - Expression> expression = t => t.StringValue == "A"; - Assert.Equal( "A", Lambda.GetValue( expression ) ); - - Expression> expression2 = t => t.Test2.IntValue == 1; - Assert.Equal( 1, Lambda.GetValue( expression2 ) ); - - Expression> expression3 = t => t.Test2.Test3.StringValue == "B"; - Assert.Equal( "B", Lambda.GetValue( expression3 ) ); - } - - /// - /// 测试获取成员值 - 运算符 - /// - [Fact] - public void TestGetValue_Operation() { - Expression> expression = t => t.Test2.IntValue != 1; - Assert.Equal( 1, Lambda.GetValue( expression ) ); - - expression = t => t.Test2.IntValue > 1; - Assert.Equal( 1, Lambda.GetValue( expression ) ); - - expression = t => t.Test2.IntValue < 1; - Assert.Equal( 1, Lambda.GetValue( expression ) ); - - expression = t => t.Test2.IntValue >= 1; - Assert.Equal( 1, Lambda.GetValue( expression ) ); - - expression = t => t.Test2.IntValue <= 1; - Assert.Equal( 1, Lambda.GetValue( expression ) ); - } - - /// - /// 测试获取成员值 - 可空类型 - /// - [Fact] - public void TestGetValue_Nullable() { - Expression> expression = t => t.NullableIntValue == 1; - Assert.Equal( 1, Lambda.GetValue( expression ) ); - - expression = t => t.NullableDecimalValue == 1.5M; - Assert.Equal( 1.5M, Lambda.GetValue( expression ) ); - } - - /// - /// 测试获取成员值 - 方法 - /// - [Fact] - public void TestGetValue_Method() { - Expression> expression = t => t.StringValue.Contains( "A" ); - Assert.Equal( "A", Lambda.GetValue( expression ) ); - - expression = t => t.Test2.StringValue.Contains( "B" ); - Assert.Equal( "B", Lambda.GetValue( expression ) ); - - expression = t => t.Test2.Test3.StringValue.StartsWith( "C" ); - Assert.Equal( "C", Lambda.GetValue( expression ) ); - } - - /// - /// 测试获取成员值 - 实例 - /// - [Fact] - public void TestGetValue_Instance() { - var test = new Sample() { StringValue = "a", Test2 = new Sample2() { StringValue = "b", Test3 = new Sample3() { StringValue = "c" } } }; - - Expression> expression = () => test.StringValue; - Assert.Equal( "a", Lambda.GetValue( expression ) ); - - Expression> expression2 = () => test.Test2.StringValue; - Assert.Equal( "b", Lambda.GetValue( expression2 ) ); - - Expression> expression3 = () => test.Test2.Test3.StringValue; - Assert.Equal( "c", Lambda.GetValue( expression3 ) ); - } - - /// - /// 测试获取成员值 - 复杂类型 - /// - [Fact] - public void TestGetValue_Complex() { - var test = new Sample() { StringValue = "a", Test2 = new Sample2() { StringValue = "b" } }; - - Expression> expression = t => t.StringValue == test.StringValue; - Assert.Equal( "a", Lambda.GetValue( expression ) ); - Expression> expression2 = t => t.StringValue == test.Test2.StringValue; - Assert.Equal( "b", Lambda.GetValue( expression2 ) ); - - Expression> expression3 = t => t.StringValue.Contains( test.StringValue ); - Assert.Equal( "a", Lambda.GetValue( expression3 ) ); - Expression> expression4 = t => t.StringValue.Contains( test.Test2.StringValue ); - Assert.Equal( "b", Lambda.GetValue( expression4 ) ); - } - - /// - /// 测试获取成员值 - 枚举 - /// - [Fact] - public void TestGetValue_Enum() { - var test1 = new Sample { NullableEnumValue = EnumSample.C }; - - Expression> expression = test => test.EnumValue == EnumSample.D; - Assert.Equal( EnumSample.D.Value(), Util.Helpers.Enum.GetValue( Lambda.GetValue( expression ) ) ); - - expression = test => test.EnumValue == test1.NullableEnumValue; - Assert.Equal( EnumSample.C, Lambda.GetValue( expression ) ); - - expression = test => test.NullableEnumValue == EnumSample.E; - Assert.Equal( EnumSample.E, Lambda.GetValue( expression ) ); - - expression = test => test.NullableEnumValue == test1.NullableEnumValue; - Assert.Equal( EnumSample.C, Lambda.GetValue( expression ) ); - - test1.NullableEnumValue = null; - expression = test => test.NullableEnumValue == test1.NullableEnumValue; - Assert.Null( Lambda.GetValue( expression ) ); - } - - /// - /// 测试获取成员值 - 集合 - /// - [Fact] - public void TestGetValue_List() { - var list = new List { "a", "b" }; - Expression> expression = t => list.Contains( t.StringValue ); - Assert.Equal( list, Lambda.GetValue( expression ) ); - } - - /// - /// 测试获取成员值 - 静态成员 - /// - [Fact] - public void TestGetValue_Static() { - Expression> expression = t => t.StringValue == Sample.StaticString; - Assert.Equal( "TestStaticString", Lambda.GetValue( expression ) ); - - expression = t => t.StringValue == Sample.StaticSample.StringValue; - Assert.Equal( "TestStaticSample", Lambda.GetValue( expression ) ); - - expression = t => t.Test2.StringValue == Sample.StaticString; - Assert.Equal( "TestStaticString", Lambda.GetValue( expression ) ); - - expression = t => t.Test2.StringValue == Sample.StaticSample.StringValue; - Assert.Equal( "TestStaticSample", Lambda.GetValue( expression ) ); - - expression = t => t.Test2.StringValue.Contains( Sample.StaticSample.StringValue ); - Assert.Equal( "TestStaticSample", Lambda.GetValue( expression ) ); - } - - #endregion - - #region GetParameter(获取参数) - - /// - /// 测试获取参数 - /// - [Fact] - public void TestGetParameter() { - Assert.Null( Lambda.GetParameter( null ) ); - - Expression> expression = test => test.StringValue == "A"; - Assert.Equal( "test", Lambda.GetParameter( expression ).ToString() ); - - expression = test => test.Test2.IntValue == 1; - Assert.Equal( "test", Lambda.GetParameter( expression ).ToString() ); - - expression = test => test.Test2.Test3.StringValue == "B"; - Assert.Equal( "test", Lambda.GetParameter( expression ).ToString() ); - - expression = test => test.Test2.IntValue; - Assert.Equal( "test", Lambda.GetParameter( expression ).ToString() ); - - expression = test => test.Test2.Test3.StringValue.Contains( "B" ); - Assert.Equal( "test", Lambda.GetParameter( expression ).ToString() ); - } - - #endregion - - #region GetCriteriaCount(获取查询条件个数) - - /// - /// 测试获取查询条件个数 - /// - [Fact] - public void TestGetConditionCount() { - Assert.Equal( 0, Lambda.GetConditionCount( null ) ); - - Expression> expression = test => test.StringValue == "A"; - Assert.Equal( 1, Lambda.GetConditionCount( expression ) ); - - expression = test => test.StringValue == "A" && test.StringValue == "B"; - Assert.Equal( 2, Lambda.GetConditionCount( expression ) ); - - expression = test => test.StringValue == "A" || test.StringValue == "B"; - Assert.Equal( 2, Lambda.GetConditionCount( expression ) ); - - expression = test => test.StringValue == "A" && test.StringValue == "B" || test.StringValue == "C"; - Assert.Equal( 3, Lambda.GetConditionCount( expression ) ); - - expression = test => test.Test2.StringValue == "A" && test.StringValue == "B" || test.StringValue == "C"; - Assert.Equal( 3, Lambda.GetConditionCount( expression ) ); - - expression = t => t.StringValue.Contains( "A" ); - Assert.Equal( 1, Lambda.GetConditionCount( expression ) ); - - expression = t => t.StringValue.Contains( "A" ) && t.StringValue == "A"; - Assert.Equal( 2, Lambda.GetConditionCount( expression ) ); - - expression = t => t.StringValue.Contains( "A" ) || t.Test2.StringValue == "A"; - Assert.Equal( 2, Lambda.GetConditionCount( expression ) ); - } - - #endregion - - #region GetAttribute(获取特性) - - /// - /// 测试获取特性 - /// - [Fact] - public void TestGetAttribute() { - DisplayAttribute attribute = Lambda.GetAttribute( t => t.StringValue ); - Assert.Equal( "字符串值", attribute.Name ); - } - - #endregion - - #region GetAttributes(获取特性列表) - - /// - /// 测试获取特性列表 - /// - [Fact] - public void TestGetAttributes() { - IEnumerable attributes = Lambda.GetAttributes( t => t.StringValue ); - Assert.Equal( 2, attributes.Count() ); - } - - #endregion - - #region Constant(获取常量表达式) - - /// - /// 测试获取常量表达式 - /// - [Fact] - public void TestConstant() { - Expression> property = t => t.NullableIntValue; - ConstantExpression constantExpression = Lambda.Constant( property, 1 ); - Assert.Equal( typeof( int ), constantExpression.Type ); - } - - #endregion - - #region Equal(创建等于表达式) - - /// - /// 测试创建等于表达式 - /// - [Fact] - public void TestEqual() { - Expression> expected = t => t.IntValue == 1; - Assert.Equal( expected.ToString(), Lambda.Equal( "IntValue", 1 ).ToString() ); - - Expression> expected2 = t => t.Test2.IntValue == 1; - Assert.Equal( expected2.ToString(), Lambda.Equal( "Test2.IntValue", 1 ).ToString() ); - } - - #endregion - - #region NotEqual(创建不等于表达式) - - /// - /// 测试创建不等于表达式 - /// - [Fact] - public void TestNotEqual() { - Expression> expected = t => t.IntValue != 1; - Assert.Equal( expected.ToString(), Lambda.NotEqual( "IntValue", 1 ).ToString() ); - - Expression> expected2 = t => t.Test2.IntValue != 1; - Assert.Equal( expected2.ToString(), Lambda.NotEqual( "Test2.IntValue", 1 ).ToString() ); - } - - #endregion - - #region Greater(创建大于表达式) - - /// - /// 测试创建大于表达式 - /// - [Fact] - public void TestGreater() { - Expression> expected = t => t.IntValue > 1; - Assert.Equal( expected.ToString(), Lambda.Greater( "IntValue", 1 ).ToString() ); - - Expression> expected2 = t => t.Test2.IntValue > 1; - Assert.Equal( expected2.ToString(), Lambda.Greater( "Test2.IntValue", 1 ).ToString() ); - } - - #endregion - - #region GreaterEqual(创建大于等于表达式) - - /// - /// 测试创建大于等于表达式 - /// - [Fact] - public void TestGreaterEqual() { - Expression> expected = t => t.IntValue >= 1; - Assert.Equal( expected.ToString(), Lambda.GreaterEqual( "IntValue", 1 ).ToString() ); - - Expression> expected2 = t => t.Test2.IntValue >= 1; - Assert.Equal( expected2.ToString(), Lambda.GreaterEqual( "Test2.IntValue", 1 ).ToString() ); - } - - #endregion - - #region Less(创建小于表达式) - - /// - /// 测试创建小于表达式 - /// - [Fact] - public void TestLess() { - Expression> expected = t => t.IntValue < 1; - Assert.Equal( expected.ToString(), Lambda.Less( "IntValue", 1 ).ToString() ); - - Expression> expected2 = t => t.Test2.IntValue < 1; - Assert.Equal( expected2.ToString(), Lambda.Less( "Test2.IntValue", 1 ).ToString() ); - } - - #endregion - - #region LessEqual(创建小于等于表达式) - - /// - /// 测试创建小于等于表达式 - /// - [Fact] - public void TestLessEqual() { - Expression> expected = t => t.IntValue <= 1; - Assert.Equal( expected.ToString(), Lambda.LessEqual( "IntValue", 1 ).ToString() ); - - Expression> expected2 = t => t.Test2.IntValue <= 1; - Assert.Equal( expected2.ToString(), Lambda.LessEqual( "Test2.IntValue", 1 ).ToString() ); - } - - #endregion - - #region Starts(调用StartsWith方法) - - /// - /// 测试调用StartsWith方法 - /// - [Fact] - public void TestStarts() { - Expression> expected = t => t.StringValue.StartsWith( "A" ); - Assert.Equal( expected.ToString(), Lambda.Starts( "StringValue", "A" ).ToString() ); - - Expression> expected2 = t => t.Test2.StringValue.StartsWith( "A" ); - Assert.Equal( expected2.ToString(), Lambda.Starts( "Test2.StringValue", "A" ).ToString() ); - - Expression> expected3 = t => t.Test2.Test3.StringValue.StartsWith( "A" ); - Assert.Equal( expected3.ToString(), Lambda.Starts( "Test2.Test3.StringValue", "A" ).ToString() ); - } - - #endregion - - #region Ends(调用EndsWith方法) - - /// - /// 测试调用EndsWith方法 - /// - [Fact] - public void TestEnds() { - Expression> expected = t => t.StringValue.EndsWith( "A" ); - Assert.Equal( expected.ToString(), Lambda.Ends( "StringValue", "A" ).ToString() ); - - Expression> expected2 = t => t.Test2.StringValue.EndsWith( "A" ); - Assert.Equal( expected2.ToString(), Lambda.Ends( "Test2.StringValue", "A" ).ToString() ); - - Expression> expected3 = t => t.Test2.Test3.StringValue.EndsWith( "A" ); - Assert.Equal( expected3.ToString(), Lambda.Ends( "Test2.Test3.StringValue", "A" ).ToString() ); - } - - #endregion - - #region Contains(调用Contains方法) - - /// - /// 测试调用Contains方法 - /// - [Fact] - public void TestContains() { - Expression> expected = t => t.StringValue.Contains( "A" ); - Assert.Equal( expected.ToString(), Lambda.Contains( "StringValue", "A" ).ToString() ); - - Expression> expected2 = t => t.Test2.StringValue.Contains( "A" ); - Assert.Equal( expected2.ToString(), Lambda.Contains( "Test2.StringValue", "A" ).ToString() ); - - Expression> expected3 = t => t.Test2.Test3.StringValue.Contains( "A" ); - Assert.Equal( expected3.ToString(), Lambda.Contains( "Test2.Test3.StringValue", "A" ).ToString() ); - } - - #endregion - - #region ParsePredicate(解析为谓词表达式) - - /// - /// 测试解析为谓词表达式 - /// - [Fact] - public void TestParsePredicate() { - Assert.Equal( "t => (t.StringValue == \"A\")", Lambda.ParsePredicate( "StringValue", "A", Operator.Equal ).SafeString() ); - Assert.Equal( "t => t.StringValue.Contains(\"A\")", Lambda.ParsePredicate( "StringValue", "A", Operator.Contains ).SafeString() ); - } - - #endregion - } -} diff --git a/test/Util.Tests/Helpers/RandomTest.cs b/test/Util.Tests/Helpers/RandomTest.cs deleted file mode 100644 index 3b1ae1fe1..000000000 --- a/test/Util.Tests/Helpers/RandomTest.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Linq; -using Xunit; - -namespace Util.Tests.Helpers { - /// - /// 随机数操作测试 - /// - public class RandomTest { - /// - /// 获取随机排序的集合 - /// - [Fact] - public void TestSort() { - int[] input = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - var list = Util.Helpers.Random.Sort( input ); - Assert.Equal( 10, list.Distinct().Count() ); - Assert.NotEqual( "1,2,3,4,5,6,7,8,9,10", list.Join() ); - } - } -} diff --git a/test/Util.Tests/Helpers/ReflectionTest.cs b/test/Util.Tests/Helpers/ReflectionTest.cs deleted file mode 100644 index 330e16964..000000000 --- a/test/Util.Tests/Helpers/ReflectionTest.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System.Reflection; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Helpers { - /// - /// 测试反射操作 - /// - public class ReflectionTest { - /// - /// 初始化测试 - /// - public ReflectionTest() { - _sample = new Sample(); - } - - /// - /// 测试样例 - /// - private Sample _sample; - - /// - /// 测试获取类成员描述 - /// - [Fact] - public void TestGetDescription() { - Assert.Equal( "", Util.Helpers.Reflection.GetDescription( "X" ) ); - Assert.Equal( "A", Util.Helpers.Reflection.GetDescription( "A" ) ); - Assert.Equal( "B2", Util.Helpers.Reflection.GetDescription( "B" ) ); - Assert.Equal( "IntValue", Util.Helpers.Reflection.GetDescription( "IntValue" ) ); - } - - /// - /// 测试获取类描述 - /// - [Fact] - public void TestGetDescription_Class() { - Assert.Equal( "测试样例", Util.Helpers.Reflection.GetDescription() ); - Assert.Equal( "Sample2", Util.Helpers.Reflection.GetDescription() ); - } - - /// - /// 测试显示名 - /// - [Fact] - public void TestGetDisplayName() { - Assert.Equal( "", Util.Helpers.Reflection.GetDisplayName() ); - Assert.Equal( "测试样例2", Util.Helpers.Reflection.GetDisplayName() ); - } - - /// - /// 测试获取类描述或显示名 - /// - [Fact] - public void TestGetDescriptionOrDisplayName() { - Assert.Equal( "测试样例", Util.Helpers.Reflection.GetDisplayNameOrDescription() ); - Assert.Equal( "测试样例2", Util.Helpers.Reflection.GetDisplayNameOrDescription() ); - Assert.Equal( "测试样例", Util.Helpers.Reflection.GetDisplayNameOrDescription() ); - } - - /// - /// 测试是否布尔类型 - /// - [Fact] - public void TestIsBool() { - Assert.True( Util.Helpers.Reflection.IsBool( _sample.BoolValue.GetType().GetTypeInfo() ), "BoolValue GetType" ); - Assert.True( Util.Helpers.Reflection.IsBool( _sample.GetType().GetMember( "BoolValue" )[0] ), "BoolValue" ); - Assert.True( Util.Helpers.Reflection.IsBool( _sample.GetType().GetMember( "NullableBoolValue" )[0] ), "NullableBoolValue" ); - Assert.False( Util.Helpers.Reflection.IsBool( _sample.GetType().GetMember( "EnumValue" )[0] ), "EnumValue" ); - } - - /// - /// 测试是否枚举类型 - /// - [Fact] - public void TestIsEnum() { - Assert.True( Util.Helpers.Reflection.IsEnum( _sample.EnumValue.GetType().GetTypeInfo() ), "EnumValue GetType" ); - Assert.True( Util.Helpers.Reflection.IsEnum( _sample.GetType().GetMember( "EnumValue" )[0] ), "EnumValue" ); - Assert.True( Util.Helpers.Reflection.IsEnum( _sample.GetType().GetMember( "NullableEnumValue" )[0] ), "NullableEnumValue" ); - Assert.False( Util.Helpers.Reflection.IsEnum( _sample.GetType().GetMember( "BoolValue" )[0] ), "BoolValue" ); - Assert.False( Util.Helpers.Reflection.IsEnum( _sample.GetType().GetMember( "NullableBoolValue" )[0] ), "NullableBoolValue" ); - } - - /// - /// 测试是否日期类型 - /// - [Fact] - public void TestIsDate() { - Assert.True( Util.Helpers.Reflection.IsDate( _sample.DateValue.GetType().GetTypeInfo() ), "DateValue GetType" ); - Assert.True( Util.Helpers.Reflection.IsDate( _sample.GetType().GetMember( "DateValue" )[0] ), "DateValue" ); - Assert.True( Util.Helpers.Reflection.IsDate( _sample.GetType().GetMember( "NullableDateValue" )[0] ), "NullableDateValue" ); - Assert.False( Util.Helpers.Reflection.IsDate( _sample.GetType().GetMember( "EnumValue" )[0] ), "EnumValue" ); - } - - /// - /// 测试是否整型 - /// - [Fact] - public void TestIsInt() { - Assert.True( Util.Helpers.Reflection.IsInt( _sample.IntValue.GetType().GetTypeInfo() ), "IntValue GetType" ); - Assert.True( Util.Helpers.Reflection.IsInt( _sample.GetType().GetMember( "IntValue" )[0] ), "IntValue" ); - Assert.True( Util.Helpers.Reflection.IsInt( _sample.GetType().GetMember( "NullableIntValue" )[0] ), "NullableIntValue" ); - - Assert.True( Util.Helpers.Reflection.IsInt( _sample.ShortValue.GetType().GetTypeInfo() ), "ShortValue GetType" ); - Assert.True( Util.Helpers.Reflection.IsInt( _sample.GetType().GetMember( "ShortValue" )[0] ), "ShortValue" ); - Assert.True( Util.Helpers.Reflection.IsInt( _sample.GetType().GetMember( "NullableShortValue" )[0] ), "NullableShortValue" ); - - Assert.True( Util.Helpers.Reflection.IsInt( _sample.LongValue.GetType().GetTypeInfo() ), "LongValue GetType" ); - Assert.True( Util.Helpers.Reflection.IsInt( _sample.GetType().GetMember( "LongValue" )[0] ), "LongValue" ); - Assert.True( Util.Helpers.Reflection.IsInt( _sample.GetType().GetMember( "NullableLongValue" )[0] ), "NullableLongValue" ); - } - - /// - /// 测试是否数值类型 - /// - [Fact] - public void TestIsNumber() { - Assert.True( Util.Helpers.Reflection.IsNumber( _sample.DoubleValue.GetType().GetTypeInfo() ), "DoubleValue GetType" ); - Assert.True( Util.Helpers.Reflection.IsNumber( _sample.GetType().GetMember( "DoubleValue" )[0] ), "DoubleValue" ); - Assert.True( Util.Helpers.Reflection.IsNumber( _sample.GetType().GetMember( "NullableDoubleValue" )[0] ), "NullableDoubleValue" ); - - Assert.True( Util.Helpers.Reflection.IsNumber( _sample.DecimalValue.GetType().GetTypeInfo() ), "DecimalValue GetType" ); - Assert.True( Util.Helpers.Reflection.IsNumber( _sample.GetType().GetMember( "DecimalValue" )[0] ), "DecimalValue" ); - Assert.True( Util.Helpers.Reflection.IsNumber( _sample.GetType().GetMember( "NullableDecimalValue" )[0] ), "NullableDecimalValue" ); - - Assert.True( Util.Helpers.Reflection.IsNumber( _sample.FloatValue.GetType().GetTypeInfo() ), "FloatValue GetType" ); - Assert.True( Util.Helpers.Reflection.IsNumber( _sample.GetType().GetMember( "FloatValue" )[0] ), "FloatValue" ); - Assert.True( Util.Helpers.Reflection.IsNumber( _sample.GetType().GetMember( "NullableFloatValue" )[0] ), "NullableFloatValue" ); - - Assert.True( Util.Helpers.Reflection.IsNumber( _sample.IntValue.GetType().GetTypeInfo() ), "IntValue GetType" ); - Assert.True( Util.Helpers.Reflection.IsNumber( _sample.GetType().GetMember( "IntValue" )[0] ), "IntValue" ); - Assert.True( Util.Helpers.Reflection.IsNumber( _sample.GetType().GetMember( "NullableIntValue" )[0] ), "NullableIntValue" ); - } - - /// - /// 测试是否泛型集合 - /// - [Fact] - public void TestIsGenericCollection() { - Assert.True( Util.Helpers.Reflection.IsGenericCollection( _sample.StringList.GetType() ) ); - } - } -} diff --git a/test/Util.Tests/Helpers/RegexTest.cs b/test/Util.Tests/Helpers/RegexTest.cs deleted file mode 100644 index 034e34cdd..000000000 --- a/test/Util.Tests/Helpers/RegexTest.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Xunit; - -namespace Util.Tests.Helpers { - /// - /// 正则操作测试 - /// - public class RegexTest { - /// - /// 测试获取值 - /// - /// 输入字符串 - /// 模式字符串 - /// 结果模式字符串 - /// 结果 - [Theory] - [InlineData( "", "", "", "" )] - [InlineData( "123", "a", "", "" )] - [InlineData( "123", @"\d", "", "1" )] - [InlineData( "123abc456", @"\d+([a-z]+\d+)", "$1", "abc456" )] - [InlineData( "123abc456", @"\d+([a-z]\d+)", "$1", "" )] - public void TestGetValue( string input, string pattern,string resultPattern, string result ) { - Assert.Equal( result, Util.Helpers.Regex.GetValue( input, pattern, resultPattern ) ); - } - - /// - /// 测试获取值集合 - /// - [Fact] - public void TestGetValues() { - Assert.Empty( Util.Helpers.Regex.GetValues( "", "",null ) ); - Assert.Empty( Util.Helpers.Regex.GetValues( "123abc456", @"\d{5}",new []{"$1"} ) ); - Assert.Equal( "123", Util.Helpers.Regex.GetValues( "123abc456", @"(\d*)", new[] { "$1" } )["$1"] ); - Assert.Equal( "abc", Util.Helpers.Regex.GetValues( "123abc456", @"\d*([a-z]*)\d*", new[] { "$1" } )["$1"] ); - Assert.Equal( "123", Util.Helpers.Regex.GetValues( "123abc456", @"(\d*)([a-z]*)(\d*)", new[] { "$1", "$2", "$3" } )["$1"] ); - Assert.Equal( "abc", Util.Helpers.Regex.GetValues( "123abc456", @"(\d*)([a-z]*)(\d*)", new[] { "$1", "$2", "$3" } )["$2"] ); - Assert.Equal( "456", Util.Helpers.Regex.GetValues( "123abc456", @"(\d*)([a-z]*)(\d*)", new[] { "$1", "$2", "$3" } )["$3"] ); - } - } -} diff --git a/test/Util.Tests/Helpers/StringTest.Builder.cs b/test/Util.Tests/Helpers/StringTest.Builder.cs deleted file mode 100644 index 1f3f99bb2..000000000 --- a/test/Util.Tests/Helpers/StringTest.Builder.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using Xunit; -using String = Util.Helpers.String; - -namespace Util.Tests.Helpers { - /// - /// 测试字符串生成器 - /// - public partial class StringTest { - /// - /// 测试初始化 - /// - public StringTest() { - Builder = new String(); - } - - /// - /// 字符串 - /// - private String Builder { get; } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - Assert.Equal( string.Empty, Builder.ToString() ); - } - - /// - /// 测试添加 - /// - [Theory] - [InlineData( "a", "a" )] - [InlineData( 1, "1" )] - public void TestAppend( object value, string result ) { - Builder.Append( value ); - Assert.Equal( result, Builder.ToString() ); - } - - /// - /// 测试添加 - 2个值 - /// - [Theory] - [InlineData( "a", 1, "a1" )] - [InlineData( "{", "}", "{}" )] - public void TestAppend_2Value(object value,object value2,string result) { - Builder.Append( value ); - Builder.Append( value2 ); - Assert.Equal( result, Builder.ToString() ); - } - - /// - /// 测试添加 - 带一个参数 - /// - [Theory] - [InlineData( "a{0}b", 1, "a1b" )] - [InlineData( "a{0}b", null, "ab" )] - public void TestAppend_1Arg( string value,object arg,string result ) { - Builder.Append( value, arg ); - Assert.Equal( result, Builder.ToString() ); - } - - /// - /// 测试添加 - 带2个参数 - /// - [Theory] - [InlineData( "a{0}b{1}", 1, 5.5, "a1b5.5" )] - [InlineData( "a{0}b{1}", null, null, "ab" )] - public void TestAdd_2Arg( string value, object arg, object arg2, string result ) { - Builder.Append( value, arg, arg2 ); - Assert.Equal( result, Builder.ToString() ); - } - - /// - /// 测试换行 - /// - [Fact] - public void TestAddLine() { - Builder.AppendLine(); - Assert.Equal(Environment.NewLine , Builder.ToString() ); - } - - /// - /// 测试换行,添加内容 - /// - [Fact] - public void TestAddLine_Value() { - Builder.AppendLine( 1 ); - Builder.Append( "b" ); - Assert.Equal( string.Format( "1{0}b", Environment.NewLine ) , Builder.ToString() ); - } - - /// - /// 测试换行,带参数 - /// - [Fact] - public void TestAddLine_Params() { - Builder.AppendLine( "a{0}", 1 ); - Builder.Append( "b" ); - Assert.Equal( string.Format( "a1{0}b", Environment.NewLine ), Builder.ToString() ); - } - - /// - /// 测试清空字符串 - /// - [Fact] - public void TestClear() { - Builder.Append( "a" ); - Builder.Clear(); - Assert.Equal( "", Builder.ToString() ); - } - - /// - /// 测试移除末尾的字符串 - /// - [Theory] - [InlineData( "a,",",","a")] - [InlineData( "ab", "a", "ab" )] - [InlineData( "abc", "bc", "a" )] - public void TestRemoveEnd( string append,string remove,string result ) { - Builder.Append( append ); - Builder.RemoveEnd( remove ); - Assert.Equal( result, Builder.ToString() ); - } - } -} diff --git a/test/Util.Tests/Helpers/StringTest.Tools.cs b/test/Util.Tests/Helpers/StringTest.Tools.cs deleted file mode 100644 index bd67b692c..000000000 --- a/test/Util.Tests/Helpers/StringTest.Tools.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; -using Xunit; - -namespace Util.Tests.Helpers { - /// - /// 测试字符串操作 - 工具库 - /// - public partial class StringTest { - /// - /// 测试将集合连接为带分隔符的字符串 - /// - [Fact] - public void TestJoin() { - Assert.Equal( "1,2,3", Util.Helpers.String.Join( new List { 1, 2, 3 } ) ); - Assert.Equal( "'1','2','3'", Util.Helpers.String.Join( new List { 1, 2, 3 }, "'" ) ); - Assert.Equal( "123", Util.Helpers.String.Join( new List { 1, 2, 3 }, "", "" ) ); - Assert.Equal( "\"1\",\"2\",\"3\"", Util.Helpers.String.Join( new List { 1, 2, 3 }, "\"" ) ); - Assert.Equal( "1 2 3", Util.Helpers.String.Join( new List { 1, 2, 3 }, "", " " ) ); - Assert.Equal( "1;2;3", Util.Helpers.String.Join( new List { 1, 2, 3 }, "", ";" ) ); - Assert.Equal( "1,2,3", Util.Helpers.String.Join( new List { "1", "2", "3" } ) ); - Assert.Equal( "'1','2','3'", Util.Helpers.String.Join( new List { "1", "2", "3" }, "'" ) ); - - var list = new List { - new Guid( "83B0233C-A24F-49FD-8083-1337209EBC9A" ), - new Guid( "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" ) - }; - Assert.Equal( "83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A".ToLower(), Util.Helpers.String.Join( list ) ); - Assert.Equal( "'83B0233C-A24F-49FD-8083-1337209EBC9A','EAB523C6-2FE7-47BE-89D5-C6D440C3033A'".ToLower(), Util.Helpers.String.Join( list, "'" ) ); - } - - /// - /// 测试获取拼音简码 - /// - [Theory] - [InlineData( null, "" )] - [InlineData( "", "" )] - [InlineData( "中国", "zg" )] - [InlineData( "a1宝藏b2", "a1bcb2" )] - [InlineData( "饕餮", "tt" )] - [InlineData( "爩", "y" )] - public void TestPinYin( string input, string result ) { - Assert.Equal( result, Util.Helpers.String.PinYin( input ) ); - } - - /// - /// 首字母小写 - /// - [Theory] - [InlineData( null, "" )] - [InlineData( "", "" )] - [InlineData( " ", "" )] - [InlineData( "a", "a" )] - [InlineData( "A", "a" )] - [InlineData( "Ab", "ab" )] - [InlineData( "AB", "aB" )] - [InlineData( "Abc", "abc" )] - public void TestFirstLowerCase( string value, string result ) { - Assert.Equal( result, Util.Helpers.String.FirstLowerCase( value ) ); - } - } -} diff --git a/test/Util.Tests/Helpers/TimeTest.cs b/test/Util.Tests/Helpers/TimeTest.cs deleted file mode 100644 index e8210be5e..000000000 --- a/test/Util.Tests/Helpers/TimeTest.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using Util.Helpers; -using Xunit; - -namespace Util.Tests.Helpers { - /// - /// 时间操作测试 - /// - public class TimeTest : IDisposable { - /// - /// 测试清理 - /// - public void Dispose() { - Time.Reset(); - } - - /// - /// 日期字符串,"2012-01-02" - /// - public const string DateString1 = "2012-01-02"; - - /// - /// 日期,2012-01-02 - /// - public static readonly DateTime Date1 = DateTime.Parse( DateString1 ); - - /// - /// 日期字符串,"2012-11-12" - /// - public const string DateString2 = "2012-11-12"; - - /// - /// 日期时间字符串,"2012-01-02 01:02:03" - /// - public const string DatetimeString1 = "2012-01-02 01:02:03"; - - /// - /// 日期时间,2012-01-02 01:02:03 - /// - public static readonly DateTime Datetime1 = DateTime.Parse( DatetimeString1 ); - - /// - /// 日期时间字符串,"2012-11-12 13:04:05" - /// - public const string DatetimeString2 = "2012-11-12 13:04:05"; - - /// - /// 日期时间,2012-11-12 13:04:05 - /// - public static readonly DateTime Datetime2 = DateTime.Parse( DatetimeString2 ); - - /// - /// 测试设置时间 - /// - [Fact] - public void TestSetTime() { - Time.SetTime( Datetime1 ); - Assert.Equal( Datetime1, Time.GetDateTime() ); - Time.Reset(); - Assert.NotEqual( Datetime1, Time.GetDateTime() ); - } - - /// - /// 测试获取Unix时间戳 - /// - [Fact] - public void TestGetUnixTimestamp() { - Assert.Equal( 15132, Time.GetUnixTimestamp( new DateTime( 1970, 01, 01, 12, 12, 12 ) ) ); - Assert.Equal( 976594332, Time.GetUnixTimestamp( new DateTime( 2000, 12, 12, 12, 12, 12 ) ) ); - Assert.Equal( 1392668699, Time.GetUnixTimestamp( new DateTime( 2014, 02, 18, 04, 24, 59 ) ) ); - } - - /// - /// 测试从Unix时间戳获取时间 - /// - [Fact] - public void TestGetTimeFromUnixTimestamp() { - Assert.Equal( new DateTime( 1970, 01, 01, 12, 12, 12 ), Time.GetTimeFromUnixTimestamp( 15132 ) ); - Assert.Equal( new DateTime( 2000, 12, 12, 12, 12, 12 ), Time.GetTimeFromUnixTimestamp( 976594332 ) ); - Assert.Equal( new DateTime( 2014, 02, 18, 04, 24, 59 ), Time.GetTimeFromUnixTimestamp( 1392668699 ) ); - } - } -} diff --git a/test/Util.Tests/Helpers/ValidationTest.cs b/test/Util.Tests/Helpers/ValidationTest.cs deleted file mode 100644 index 1510f4f2c..000000000 --- a/test/Util.Tests/Helpers/ValidationTest.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Util.Helpers; -using Xunit; - -namespace Util.Tests.Helpers { - /// - /// 验证操作测试 - /// - public class ValidationTest { - /// - /// 是否数字 - /// - [Fact] - public void TestIsNumber() { - Assert.False( Validation.IsNumber( "" ) ); - Assert.True( Validation.IsNumber( "1" ) ); - Assert.False( Validation.IsNumber( "1a" ) ); - Assert.True( Validation.IsNumber( "100.04" ) ); - Assert.False( Validation.IsNumber( "100a.01" ) ); - } - } -} diff --git a/test/Util.Tests/Maps/MapTest.cs b/test/Util.Tests/Maps/MapTest.cs deleted file mode 100644 index 6b1fa02bb..000000000 --- a/test/Util.Tests/Maps/MapTest.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Util.Helpers; -using Util.Maps; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Maps { - /// - /// 对象映射测试 - /// - public class MapTest { - /// - /// 测试映射 - /// - [Fact] - public void TestMapTo() { - Sample sample = new Sample(); - Sample2 sample2 = new Sample2 { StringValue = "a" }; - sample2.MapTo( sample ); - Assert.Equal( "a", sample.StringValue ); - } - - /// - /// 测试映射 - /// - [Fact] - public void TestMapTo_2() { - Sample sample = new Sample { StringValue = "a" }; - Sample2 sample2 = sample.MapTo(); - Assert.Equal( "a", sample2.StringValue ); - } - - /// - /// 测试映射集合 - /// - [Fact] - public void TestMapTo_List() { - List sampleList = new List { new Sample { StringValue = "a" }, new Sample { StringValue = "b" } }; - List sample2List = new List(); - sampleList.MapTo( sample2List ); - Assert.Equal( 2, sample2List.Count ); - Assert.Equal( "a", sample2List[0].StringValue ); - } - - /// - /// 测试映射集合 - /// - [Fact] - public void TestMapTo_List_2() { - List sampleList = new List { new Sample { StringValue = "a" }, new Sample { StringValue = "b" } }; - List sample2List = sampleList.MapTo>(); - Assert.Equal( 2, sample2List.Count ); - Assert.Equal( "a", sample2List[0].StringValue ); - } - - /// - /// 测试映射集合 - /// - [Fact] - public void TestMapToList() { - List sampleList = new List { new Sample { StringValue = "a" }, new Sample { StringValue = "b" } }; - List sample2List = sampleList.MapToList(); - Assert.Equal( 2, sample2List.Count ); - Assert.Equal( "a", sample2List[0].StringValue ); - } - - /// - /// 映射集合 - 测试空集合 - /// - [Fact] - public void TestMapToList_Empty() { - List sampleList = new List(); - List sample2List = sampleList.MapToList(); - Assert.Empty( sample2List ); - } - - /// - /// 映射集合 - 测试数组 - /// - [Fact] - public void TestMapToList_Array() { - Sample[] sampleList = new Sample[] { new Sample { StringValue = "a" }, new Sample { StringValue = "b" } }; - List sample2List = sampleList.MapToList(); - Assert.Equal( 2, sample2List.Count ); - Assert.Equal( "a", sample2List[0].StringValue ); - } - - /// - /// 并发测试 - /// - [Fact] - public void TestMapTo_MultipleThread() { - Thread.WaitAll( () => { - var sample = new Sample { StringValue = "a" }; - var sample2 = sample.MapTo(); - Assert.Equal( "a", sample2.StringValue ); - },() => { - var sample = new DtoSample { Name = "a" }; - var sample2 = sample.MapTo(); - Assert.Equal( "a", sample2.Name ); - }, () => { - var sample = new Sample2 { StringValue = "a" }; - var sample2 = sample.MapTo(); - Assert.Equal( "a", sample2.StringValue ); - }, () => { - var sample = new EntitySample{ Name = "a" }; - var sample2 = sample.MapTo(); - Assert.Equal( "a", sample2.Name ); - } ); - } - } -} diff --git a/test/Util.Tests/Parameters/ParameterBuilderTest.cs b/test/Util.Tests/Parameters/ParameterBuilderTest.cs deleted file mode 100644 index 23b68203a..000000000 --- a/test/Util.Tests/Parameters/ParameterBuilderTest.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System; -using Util.Parameters; -using Util.Tests.Samples; -using Xunit; - -namespace Util.Tests.Parameters { - /// - /// 参数生成器测试 - /// - public class ParameterBuilderTest { - /// - /// 参数生成器 - /// - private readonly ParameterBuilder _builder; - - /// - /// 测试初始化 - /// - public ParameterBuilderTest() { - _builder = new ParameterBuilder(); - } - - /// - /// 验证key为空 - /// - [Theory] - [InlineData( null )] - [InlineData( "" )] - [InlineData( " " )] - public void TestAdd_KeyIsEmpty( string key ) { - Assert.Empty( _builder.Add( key, "b" ).Result( new ParameterFormatterSample() ) ); - } - - /// - /// 验证value为空 - /// - [Theory] - [InlineData( null )] - [InlineData( "" )] - [InlineData( " " )] - public void TestAdd_ValueIsEmpty( string value ) { - Assert.Empty( _builder.Add( "a", value ).Result( new ParameterFormatterSample() ) ); - } - - /// - /// 去除键两端空格 - /// - [Fact] - public void TestAdd_TrimKey() { - Assert.Equal( "a:1", _builder.Add( " a ", "1" ).Result( new ParameterFormatterSample() ) ); - } - - /// - /// 去除值两端空格 - /// - [Fact] - public void TestAdd_TrimValue() { - Assert.Equal( "a:1", _builder.Add( "a", " 1 " ).Result( new ParameterFormatterSample() ) ); - } - - /// - /// 添加字符串参数 - /// - [Fact] - public void TestAdd_String() { - Assert.Equal( "a:1", _builder.Add( "a", "1" ).Result( new ParameterFormatterSample() ) ); - } - - /// - /// 添加2个字符串参数 - /// - [Fact] - public void TestAdd_String_2() { - Assert.Equal( "a:1|b:2", _builder.Add( "a", " 1" ).Add( "b ", "2 " ).Result( new ParameterFormatterSample() ) ); - } - - /// - /// 添加日期参数 - /// - [Fact] - public void TestAdd_DateTime() { - DateTime value = new DateTime( 2000, 10, 10, 10, 10, 10 ); - Assert.Equal( "a:2000-10-10 10:10:10", _builder.Add( "a", value ).Result( new ParameterFormatterSample() ) ); - } - - /// - /// 添加日期参数 - 可空 - /// - [Fact] - public void TestAdd_DateTime_Nullable() { - DateTime? value = new DateTime( 2000, 10, 10, 10, 10, 10 ); - Assert.Equal( "a:2000-10-10 10:10:10", _builder.Add( "a", value ).Result( new ParameterFormatterSample() ) ); - } - - /// - /// 添加日期参数 - 可空 - 传入null - /// - [Fact] - public void TestAdd_DateTime_Nullable_Null() { - DateTime? value = null; - Assert.Empty( _builder.Add( "a", value ).Result( new ParameterFormatterSample() ) ); - } - - /// - /// 添加整型参数 - /// - [Fact] - public void TestAdd_Int() { - int value = 1; - Assert.Equal( "a:1", _builder.Add( "a", value ).Result( new ParameterFormatterSample() ) ); - } - - /// - /// 添加整型参数 - 可空 - /// - [Fact] - public void TestAdd_Int_Nullable() { - int? value = 1; - Assert.Equal( "a:1", _builder.Add( "a", value ).Result( new ParameterFormatterSample() ) ); - } - - /// - /// 添加整型参数 - 可空 - 传入null - /// - [Fact] - public void TestAdd_Int_Nullable_Null() { - int? value = null; - Assert.Empty( _builder.Add( "a", value ).Result( new ParameterFormatterSample() ) ); - } - - /// - /// 添加布尔参数 - /// - [Fact] - public void TestAdd_Bool() { - bool value = true; - Assert.Equal( "a:true", _builder.Add( "a", value ).Result( new ParameterFormatterSample() ) ); - } - - /// - /// 添加布尔参数 - 可空 - /// - [Fact] - public void TestAdd_Bool_Nullable() { - bool? value = false; - Assert.Equal( "a:false", _builder.Add( "a", value ).Result( new ParameterFormatterSample() ) ); - } - - /// - /// 添加布尔参数 - 可空 - 传入null - /// - [Fact] - public void TestAdd_Bool_Nullable_Null() { - bool? value = null; - Assert.Empty( _builder.Add( "a", value ).Result( new ParameterFormatterSample() ) ); - } - - /// - /// 已添加参数则覆盖 - /// - [Fact] - public void TestAdd_Exist() { - Assert.Equal( "a:2", _builder.Add( "a", "1" ).Add( "a", "2" ).Result( new ParameterFormatterSample() ) ); - } - - /// - /// 转换为json - /// - [Fact] - public void TestToJson() { - Assert.Equal( "{\"a\":\"true\",\"b\":\"2000-10-10 10:10:10\"}", _builder.Add( "a", true ).Add( "b",new DateTime(2000,10,10,10,10,10) ).ToJson() ); - } - } -} diff --git a/test/Util.Tests/Parameters/UrlParameterBuilderTest.cs b/test/Util.Tests/Parameters/UrlParameterBuilderTest.cs deleted file mode 100644 index d52a89d64..000000000 --- a/test/Util.Tests/Parameters/UrlParameterBuilderTest.cs +++ /dev/null @@ -1,130 +0,0 @@ -using Util.Parameters; -using Xunit; - -namespace Util.Tests.Parameters { - /// - /// Url参数生成器测试 - /// - public class UrlParameterBuilderTest { - /// - /// Url参数生成器 - /// - private UrlParameterBuilder _builder; - - /// - /// 测试初始化 - /// - public UrlParameterBuilderTest() { - _builder = new UrlParameterBuilder(); - } - - /// - /// 测试默认值 - /// - [Fact] - public void TestDefault() { - Assert.Empty( _builder.Result() ); - } - - /// - /// 加载Url - /// - [Fact] - public void TestLoadUrl_1() { - _builder = new UrlParameterBuilder("a"); - Assert.Equal( 0, _builder.GetDictionary().Count ); - } - - /// - /// 加载Url - /// - [Fact] - public void TestLoadUrl_2() { - _builder = new UrlParameterBuilder( "a=b" ); - Assert.Equal( 1, _builder.GetDictionary().Count ); - } - - /// - /// 加载Url - /// - [Fact] - public void TestLoadUrl_3() { - _builder = new UrlParameterBuilder( "a=1&b=2" ); - Assert.Equal( 2, _builder.GetDictionary().Count ); - } - - /// - /// 加载Url - /// - [Fact] - public void TestLoadUrl_4() { - _builder = new UrlParameterBuilder( "a=1&b" ); - Assert.Equal( 1, _builder.GetDictionary().Count ); - } - - /// - /// 加载Url - /// - [Fact] - public void TestLoadUrl_5() { - _builder = new UrlParameterBuilder( "a=1&b=" ); - Assert.Equal( 1, _builder.GetDictionary().Count ); - } - - /// - /// 加载Url - /// - [Fact] - public void TestLoadUrl_6() { - _builder = new UrlParameterBuilder( "http://test.com?b=2&c=3&a=1" ); - Assert.Equal( 3, _builder.GetDictionary().Count ); - Assert.Equal( "a=1&b=2&c=3", _builder.Result( true ) ); - } - - /// - /// 加载Url - /// - [Fact] - public void TestLoadUrl_7() { - _builder = new UrlParameterBuilder( "http://test.com ? b = 2 & c & a = 1 " ); - Assert.Equal( "a=1&b=2", _builder.Result( true ) ); - } - - /// - /// 添加参数 - /// - [Fact] - public void TestAdd_1() { - Assert.Equal( "a=b", _builder.Add( "a", "b" ).Result() ); - Assert.Equal( "a=b&c=d", _builder.Add( "c", "d" ).Result() ); - } - - /// - /// 添加参数 - 测试空格 - /// - [Fact] - public void TestAdd_2() { - Assert.Equal( "b=2&a=1", _builder.Add( " b ", " 2 " ).Add( " a ", " 1 " ).Result() ); - } - - /// - /// 添加参数 - 测试排序 - /// - [Fact] - public void TestResult_Sort() { - Assert.Equal( "a=1&b=2", _builder.Add( "b", "2" ).Add( "a", "1" ).Result( true ) ); - } - - /// - /// 连接URL - /// - [Theory] - [InlineData( "http://test.com", "http://test.com?a=1&b=2" )] - [InlineData( "http://test.com?", "http://test.com?a=1&b=2" )] - [InlineData( "http://test.com?c=3", "http://test.com?c=3&a=1&b=2" )] - [InlineData( "http://test.com?c=3&", "http://test.com?c=3&a=1&b=2" )] - public void TestJoinUrl( string url, string result ) { - Assert.Equal( result, _builder.Add( "a", "1" ).Add( "b", "2" ).JoinUrl( url ) ); - } - } -} diff --git a/test/Util.Tests/Reflections/FinderTest.cs b/test/Util.Tests/Reflections/FinderTest.cs deleted file mode 100644 index ae2a7ad27..000000000 --- a/test/Util.Tests/Reflections/FinderTest.cs +++ /dev/null @@ -1,156 +0,0 @@ -using Util.Reflections; -using Xunit; - -namespace Util.Tests.Reflections { - /// - /// 类型查找器测试 - /// - public class FinderTest { - /// - /// 类型查找器 - /// - private readonly IFind _finder; - - /// - /// 初始化类型查找器测试 - /// - public FinderTest() { - _finder = new Finder(); - } - - /// - /// 查找类型集合 - /// - [Fact] - public void TestFind() { - var types = _finder.Find(); - Assert.Single( types ); - Assert.Equal( typeof( A ), types[0] ); - } - - /// - /// 查找类型集合 - /// - [Fact] - public void TestFind_2() { - var types = _finder.Find(); - Assert.Equal( 2, types.Count ); - Assert.Equal( typeof( A ), types[0] ); - Assert.Equal( typeof( B ), types[1] ); - } - - /// - /// 查找的结果类型包含泛型 - /// - [Fact] - public void TestFind_3() { - var types = _finder.Find(); - Assert.Equal( 2, types.Count ); - Assert.Equal( typeof( B ), types[0] ); - Assert.Equal( typeof( D<> ), types[1] ); - } - - /// - /// 查找泛型类型 - /// - [Fact] - public void TestFind_4() { - var types = _finder.Find>(); - Assert.Single( types ); - Assert.Equal( typeof( E ), types[0] ); - } - - /// - /// 查找泛型类型 - /// - [Fact] - public void TestFind_5() { - var types = _finder.Find( typeof( IG<> ) ); - Assert.Equal( 2, types.Count ); - Assert.Equal( typeof( E ), types[0] ); - Assert.Equal( typeof( F2<> ), types[1] ); - } - - /// - /// 测试并发 - /// - [Fact] - public void TestFind_6() { - Util.Helpers.Thread.WaitAll( () => { - var types = _finder.Find(); - Assert.Single( types ); - Assert.Equal( typeof( A ), types[0] ); - }, () => { - var types = _finder.Find(); - Assert.Equal( 2, types.Count ); - Assert.Equal( typeof( A ), types[0] ); - Assert.Equal( typeof( B ), types[1] ); - }, () => { - var types = _finder.Find(); - Assert.Equal( 2, types.Count ); - Assert.Equal( typeof( B ), types[0] ); - Assert.Equal( typeof( D<> ), types[1] ); - } ); - } - } - - /// - /// 测试查找接口 - /// - public interface IA { - } - - /// - /// 测试类型 - /// - public class A : IA, IB { - } - - /// - /// 测试查找接口 - /// - public interface IB { - } - - /// - /// 测试类型 - /// - public class B : IB, IC { - } - - /// - /// 测试类型 - /// - public abstract class C : IB, IC { - } - - /// - /// 测试查找接口 - /// - public interface IC { - } - - /// - /// 测试类型 - /// - public class D : IC { - } - - /// - /// 测试查找接口 - /// - public interface IG { - } - - /// - /// 测试类型 - /// - public class E : IG { - } - - /// - /// 测试类型 - /// - public class F2 : IG { - } -} diff --git a/test/Util.Tests/Samples/AggregateRootSample.cs b/test/Util.Tests/Samples/AggregateRootSample.cs deleted file mode 100644 index 21f445c3b..000000000 --- a/test/Util.Tests/Samples/AggregateRootSample.cs +++ /dev/null @@ -1,213 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using Util.Domains; - -namespace Util.Tests.Samples { - /// - /// 聚合根测试样例 - /// - public class AggregateRootSample : AggregateRoot { - /// - /// 初始化聚合根测试样例 - /// - public AggregateRootSample() - : this( Guid.Empty ) { - } - - /// - /// 初始化聚合根测试样例 - /// - /// 标识 - public AggregateRootSample( Guid id ) - : base( id ) { - IntSamples = new List(); - } - - /// - /// 姓名 - /// - [Required( ErrorMessage = "姓名不能为空" )] - public string Name { get; set; } - - /// - /// 编码 - /// - public string Code { get; set; } - - /// - /// 英文名 - /// - [Required( ErrorMessageResourceType = typeof( TestResource ), ErrorMessageResourceName = "EnglishNameIsEmpty" )] - public string EnglishName { get; set; } - - /// - /// 手机号 - /// - [Display(Name = "手机号" )] - public string MobilePhone { get; set; } - - /// - /// 年龄 - /// - public int? Age { get; set; } - - /// - /// 电话 - /// - [Description( "电话" )] - public int Tel { get; set; } - - /// - /// decimal值 - /// - public decimal DecimalValue { get; set; } - /// - /// 可空decimal值 - /// - public decimal? NullableDecimalValue { get; set; } - /// - /// float值 - /// - public float FloatValue { get; set; } - /// - /// 可空float值 - /// - public float? NullableFloatValue { get; set; } - /// - /// double值 - /// - public double DoubleValue { get; set; } - /// - /// 可空double值 - /// - public double? NullableDoubleValue { get; set; } - /// - /// bool值 - /// - public bool BoolValue { get; set; } - /// - /// 可空bool值 - /// - public bool? NullableBoolValue { get; set; } - /// - /// Enum值 - /// - public EnumSample EnumValue { get; set; } - /// - /// 可空Enum值 - /// - public EnumSample? NullableEnumValue { get; set; } - /// - /// DateTime值 - /// - public DateTime DateValue { get; set; } - /// - /// 可空DateTime值 - /// - public DateTime? NullableDateValue { get; set; } - /// - /// int值 - /// - public int IntValue { get; set; } - /// - /// 可空int值 - /// - public int? NullableIntValue { get; set; } - /// - /// short值 - /// - public short ShortValue { get; set; } - /// - /// 可空short值 - /// - public short? NullableShortValue { get; set; } - /// - /// long值 - /// - public long LongValue { get; set; } - /// - /// 可空long值 - /// - public long? NullableLongValue { get; set; } - - /// - /// 导航属性 - /// - public StringAggregateRootSample StringSample { get; set; } - - /// - /// 导航属性集合 - /// - public ICollection IntSamples { get; set; } - - /// - /// 添加描述 - /// - protected override void AddDescriptions() { - AddDescription( "Id:" + Id + "," ); - AddDescription( "姓名", Name ); - } - - /// - /// 添加变更列表 - /// - protected override void AddChanges( AggregateRootSample other ) { - AddChange( "Name", "姓名", Name, other.Name ); - AddChange( t => t.MobilePhone, other.MobilePhone ); - AddChange( t => t.Tel, other.Tel ); - AddChange( StringSample, other.StringSample ); - AddChange( IntSamples, other.IntSamples ); - } - - /// - /// 创建测试样例 - /// - public static AggregateRootSample CreateSample() { - return CreateSample( Guid.NewGuid() ); - } - - /// - /// 创建测试样例 - /// - public static AggregateRootSample CreateSample( Guid id ) { - return new AggregateRootSample( id ) { - Name = "TestName", - EnglishName = "TestEnglishName", - MobilePhone = "13012345678" - }; - } - - /// - /// 创建测试样例2 - /// - public static AggregateRootSample CreateSample2() { - return CreateSample2( Guid.NewGuid() ); - } - - /// - /// 创建测试样例2 - /// - public static AggregateRootSample CreateSample2( Guid id ) { - return new AggregateRootSample( id ) { - Name = "TestName2", - EnglishName = "TestEnglishName2", - MobilePhone = "12012345678" - }; - } - } - - /// - /// 聚合根测试样例2 - /// - public class AggregateRootSample2 : AggregateRoot { - /// - /// 初始化聚合根测试样例 - /// - /// 标识 - public AggregateRootSample2( Guid id ) - : base( id ) { - } - } -} diff --git a/test/Util.Tests/Samples/CriteriaSample.cs b/test/Util.Tests/Samples/CriteriaSample.cs deleted file mode 100644 index 359d829c2..000000000 --- a/test/Util.Tests/Samples/CriteriaSample.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Linq.Expressions; -using Util.Domains.Repositories; - -namespace Util.Tests.Samples { - /// - /// 查询条件对象样例 - /// - public class CriteriaSample : ICriteria { - /// - /// 获取查询条件,返回结果:"t => ((t.Name == \"A\") AndAlso (t.Tel == 1))" - /// - public Expression> GetPredicate() { - return t => t.Name == "A" && t.Tel == 1; - } - } -} diff --git a/test/Util.Tests/Samples/CrudServiceSample.cs b/test/Util.Tests/Samples/CrudServiceSample.cs deleted file mode 100644 index 5b62c95b5..000000000 --- a/test/Util.Tests/Samples/CrudServiceSample.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Util.Applications; -using Util.Applications.Dtos; -using Util.Datas.Queries; -using Util.Datas.UnitOfWorks; -using Util.Domains.Repositories; -using Util.Maps; - -namespace Util.Tests.Samples { - /// - /// 增删改查服务样例 - /// - public interface ICrudServiceSample : ICrudService { - } - - /// - /// 工作单元样例 - /// - public class UnitOfWorkSample : IUnitOfWork { - public void Dispose() { - } - - public int Commit() { - return 1; - } - - public Task CommitAsync() { - return Task.FromResult( 1 ); - } - } - - /// - /// 增删改查服务样例 - /// - public class CrudServiceSample : CrudServiceBase ,ICrudServiceSample { - public CrudServiceSample( IUnitOfWork unitOfWork, IRepositorySample repository ) : base( unitOfWork, repository ) { - } - - /// - /// 转换为实体 - /// - /// 数据传输对象 - protected override EntitySample ToEntity( DtoSample dto ) { - return dto.MapTo( new EntitySample( dto.Id.ToGuid() ) ); - } - - /// - /// 创建查询对象 - /// - /// 查询参数 - protected override IQueryBase CreateQuery( QueryParameterSample parameter ) { - return new Query( parameter ); - } - } -} diff --git a/test/Util.Tests/Samples/EnumSample.cs b/test/Util.Tests/Samples/EnumSample.cs deleted file mode 100644 index d9009bcea..000000000 --- a/test/Util.Tests/Samples/EnumSample.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.ComponentModel; - -namespace Util.Tests.Samples { - /// - /// 枚举测试样例 - /// - public enum EnumSample { - A = 1, - [Description( "B2" )] - B = 2, - [Description( "C3" )] - C = 3, - [Description( "D4" )] - D = 4, - [Description( "E5" )] - E = 5 - } -} diff --git a/test/Util.Tests/Samples/EventSample.cs b/test/Util.Tests/Samples/EventSample.cs deleted file mode 100644 index b1a0c68e1..000000000 --- a/test/Util.Tests/Samples/EventSample.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Util.Events; -using Util.Events.Handlers; - -namespace Util.Tests.Samples { - /// - /// 事件样例 - /// - public class EventSample : Event{ - /// - /// 名称 - /// - public string Name { get; set; } - } - - /// - /// 事件处理器服务样例 - /// - public class EventHandlerManagerSample : IEventHandlerManager { - /// - /// 事件处理器列表 - /// - private readonly IEventHandler[] _handlers; - - /// - /// 初始化事件处理器服务样例 - /// - /// 事件处理器列表 - public EventHandlerManagerSample( params IEventHandler[] handlers ) { - _handlers = handlers; - } - - /// - /// 获取事件处理器列表 - /// - /// 事件类型 - public List> GetHandlers() where TEvent : IEvent { - return _handlers.Select( t => t as IEventHandler ).ToList(); - } - } -} diff --git a/test/Util.Tests/Samples/IntAggregateRootSample.cs b/test/Util.Tests/Samples/IntAggregateRootSample.cs deleted file mode 100644 index bd3210060..000000000 --- a/test/Util.Tests/Samples/IntAggregateRootSample.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Util.Domains; - -namespace Util.Tests.Samples { - /// - /// int聚合根测试样例 - /// - public class IntAggregateRootSample : AggregateRoot { - /// - /// 初始化int聚合根测试样例 - /// - public IntAggregateRootSample() - : this( 0 ) { - } - - /// - /// 初始化int聚合根测试样例 - /// - /// 标识 - public IntAggregateRootSample( int id ) - : base( id ) { - } - - /// - /// 姓名 - /// - public string Name { get; set; } - - /// - /// 编码 - /// - public string Code { get; set; } - - /// - /// 添加描述 - /// - protected override void AddDescriptions() { - AddDescription( "Id:" + Id + "," ); - AddDescription( "姓名", Name ); - } - - /// - /// 添加变更列表 - /// - protected override void AddChanges( IntAggregateRootSample other ) { - AddChange( "Name", "IntSampleName", Name, other.Name ); - } - } -} diff --git a/test/Util.Tests/Samples/JsonTestSample.cs b/test/Util.Tests/Samples/JsonTestSample.cs deleted file mode 100644 index 5525ceea6..000000000 --- a/test/Util.Tests/Samples/JsonTestSample.cs +++ /dev/null @@ -1,65 +0,0 @@ -namespace Util.Tests.Samples { - public class A { - public string Name { get; set; } - public B B { get; set; } - } - - public class B { - public string Name { get; set; } - public C C { get; set; } - } - - public class C { - public string Name { get; set; } - public A A { get; set; } - } - - /// - /// Json测试样例 - /// - public class JsonTestSample { - /// - /// 名称,测试公共属性,且首字母大写 - /// - public string Name { get; set; } - /// - /// 私有属性,应被忽略 - /// - private string A { get; set; } - /// - /// 昵称,用来测试小写 - /// - public string nickname { get; set; } - /// - /// 测试null - /// - public object Value { get; set; } - /// - /// 日期 - /// - public string Date { get; set; } - /// - /// 测试整型,不添加引号 - /// - public int Age { get; set; } - /// - /// 测试布尔型 - /// - public bool isShow { get; set; } - - /// - /// 创建客户 - /// - public static JsonTestSample Create() { - return new JsonTestSample() { - Name = "a", - A = "1", - nickname = "b", - Value = null, - Date = Util.Helpers.Convert.ToDate( "2012-1-1" ).ToString(), - Age = 1, - isShow = true - }; - } - } -} diff --git a/test/Util.Tests/Samples/ParameterFormatterSample.cs b/test/Util.Tests/Samples/ParameterFormatterSample.cs deleted file mode 100644 index fb32a6acb..000000000 --- a/test/Util.Tests/Samples/ParameterFormatterSample.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Util.Parameters.Formats; - -namespace Util.Tests.Samples { - /// - /// 参数格式化器样例 - /// - public class ParameterFormatterSample : ParameterFormatBase { - /// - /// 格式化分割符 - /// - protected override string FormatSeparator => ":"; - - /// - /// 连接符 - /// - protected override string JoinSeparator => "|"; - } -} diff --git a/test/Util.Tests/Samples/PersistentObjectSample.cs b/test/Util.Tests/Samples/PersistentObjectSample.cs deleted file mode 100644 index b44b343b6..000000000 --- a/test/Util.Tests/Samples/PersistentObjectSample.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using Util.Datas.Persistence; -namespace Util.Tests.Samples { - /// - /// 持久化对象测试样例 - /// - public class PersistentObjectSample : PersistentObjectBase { - /// - /// 初始化持久化对象测试样例 - /// - public PersistentObjectSample() : this( Guid.Empty ) { - } - - /// - /// 初始化持久化对象测试样例 - /// - /// 标识 - public PersistentObjectSample( Guid id ) { - Id = id; - } - - /// - /// 姓名 - /// - public string Name { get; set; } - - /// - /// 编码 - /// - public string Code { get; set; } - } - - /// - /// 持久化对象测试样例2 - /// - public class PersistentObjectSample2 : PersistentObjectBase { - /// - /// 初始化持久化对象测试样例2 - /// - public PersistentObjectSample2() : this( Guid.Empty ) { - } - - /// - /// 初始化持久化对象测试样例2 - /// - /// 标识 - public PersistentObjectSample2( Guid id ) { - Id = id; - } - - /// - /// 姓名 - /// - public string Name { get; set; } - - /// - /// 编码 - /// - public string Code { get; set; } - } -} \ No newline at end of file diff --git a/test/Util.Tests/Samples/QueryParameterSample.cs b/test/Util.Tests/Samples/QueryParameterSample.cs deleted file mode 100644 index 825f90052..000000000 --- a/test/Util.Tests/Samples/QueryParameterSample.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Util.Datas.Queries; - -namespace Util.Tests.Samples { - /// - /// 查询参数测试样例 - /// - public class QueryParameterSample : QueryParameter{ - /// - /// 名称 - /// - public string Name { get; set; } - } -} diff --git a/test/Util.Tests/Samples/QueryServiceSample.cs b/test/Util.Tests/Samples/QueryServiceSample.cs deleted file mode 100644 index e85d5c21b..000000000 --- a/test/Util.Tests/Samples/QueryServiceSample.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using Util.Applications; -using Util.Applications.Dtos; -using Util.Datas.Queries; -using Util.Domains.Repositories; - -namespace Util.Tests.Samples { - /// - /// 数据传输对象样例 - /// - public class DtoSample : DtoBase { - /// - /// 名称 - /// - [Required(ErrorMessage = "名称不能为空" )] - public string Name { get; set; } - - /// - /// 创建空集合 - /// - public static List EmptyList() { - return new List(); - } - } - - /// - /// 查询服务样例 - /// - public class QueryServiceSample : QueryServiceBase { - public QueryServiceSample( IRepositorySample repository ) : base( repository ) { - } - - /// - /// 创建查询对象 - /// - /// 查询参数 - protected override IQueryBase CreateQuery( QueryParameterSample parameter ) { - return new Query( parameter ).WhereIfNotEmpty( t => t.Name == parameter.Name ); - } - - /// - /// 查询时是否跟踪对象 - /// - protected override bool IsTracking => true; - } -} diff --git a/test/Util.Tests/Samples/RepositorySample.cs b/test/Util.Tests/Samples/RepositorySample.cs deleted file mode 100644 index cb43b2ba0..000000000 --- a/test/Util.Tests/Samples/RepositorySample.cs +++ /dev/null @@ -1,219 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using Util.Domains; -using Util.Domains.Repositories; - -namespace Util.Tests.Samples { - /// - /// 实体样例 - /// - [DisplayName( "实体样例" )] - public class EntitySample : AggregateRoot { - public EntitySample( ) : this( Guid.NewGuid() ) { - } - - public EntitySample( Guid id ) : base( id ) { - } - - /// - /// 名称 - /// - [Required(ErrorMessage = "名称不能为空")] - public string Name { get; set; } - } - - /// - /// 仓储样例 - /// - public interface IRepositorySample : IRepository { - } - - /// - /// 仓储样例 - /// - public class RepositorySample : IRepositorySample { - - public IQueryable Find() { - throw new NotImplementedException(); - } - - public IQueryable FindAsNoTracking() { - throw new NotImplementedException(); - } - - public IQueryable Find( ICriteria criteria ) { - throw new NotImplementedException(); - } - - public IQueryable Find( Expression> predicate ) { - throw new NotImplementedException(); - } - - public EntitySample Find( object id ) { - throw new NotImplementedException(); - } - - public Task FindAsync( object id ) { - throw new NotImplementedException(); - } - - public List FindByIds( params Guid[] ids ) { - throw new NotImplementedException(); - } - - public List FindByIds( IEnumerable ids ) { - throw new NotImplementedException(); - } - - public List FindByIds( string ids ) { - throw new NotImplementedException(); - } - - public Task> FindByIdsAsync( params Guid[] ids ) { - throw new NotImplementedException(); - } - - public Task> FindByIdsAsync( IEnumerable ids ) { - throw new NotImplementedException(); - } - - public Task> FindByIdsAsync( string ids ) { - throw new NotImplementedException(); - } - - public EntitySample Single( Expression> predicate ) { - throw new NotImplementedException(); - } - - public Task SingleAsync( Expression> predicate ) { - throw new NotImplementedException(); - } - - public List FindAll( Expression> predicate = null ) { - throw new NotImplementedException(); - } - - public Task> FindAllAsync( Expression> predicate = null ) { - throw new NotImplementedException(); - } - - public bool Exists( Expression> predicate ) { - throw new NotImplementedException(); - } - - public bool Exists( params Guid[] ids ) { - throw new NotImplementedException(); - } - - public Task ExistsAsync( Expression> predicate ) { - throw new NotImplementedException(); - } - - public Task ExistsAsync( params Guid[] ids ) { - throw new NotImplementedException(); - } - - public int Count( Expression> predicate = null ) { - throw new NotImplementedException(); - } - - public Task CountAsync( Expression> predicate = null ) { - throw new NotImplementedException(); - } - - public List Query( IQueryBase query ) { - throw new NotImplementedException(); - } - - public Task> QueryAsync( IQueryBase query ) { - throw new NotImplementedException(); - } - - public List QueryAsNoTracking( IQueryBase query ) { - throw new NotImplementedException(); - } - - public Task> QueryAsNoTrackingAsync( IQueryBase query ) { - throw new NotImplementedException(); - } - - public PagerList PagerQuery( IQueryBase query ) { - throw new NotImplementedException(); - } - - public Task> PagerQueryAsync( IQueryBase query ) { - throw new NotImplementedException(); - } - - public PagerList PagerQueryAsNoTracking( IQueryBase query ) { - throw new NotImplementedException(); - } - - public Task> PagerQueryAsNoTrackingAsync( IQueryBase query ) { - throw new NotImplementedException(); - } - - public IQueryable OrderBy( IQueryable queryable, string orderBy ) { - throw new NotImplementedException(); - } - - public void Add( EntitySample entity ) { - } - - public void Add( IEnumerable entities ) { - throw new NotImplementedException(); - } - - public Task AddAsync( EntitySample entity ) { - throw new NotImplementedException(); - } - - public Task AddAsync( IEnumerable entities ) { - throw new NotImplementedException(); - } - - public void Update( EntitySample entity ) { - } - - public Task UpdateAsync( EntitySample entity ) { - throw new NotImplementedException(); - } - - public void Remove( Guid id ) { - throw new NotImplementedException(); - } - - public Task RemoveAsync( Guid id ) { - throw new NotImplementedException(); - } - - public void Remove( EntitySample entity ) { - throw new NotImplementedException(); - } - - public Task RemoveAsync( EntitySample entity ) { - throw new NotImplementedException(); - } - - public void Remove( IEnumerable ids ) { - throw new NotImplementedException(); - } - - public Task RemoveAsync( IEnumerable ids ) { - throw new NotImplementedException(); - } - - public void Remove( IEnumerable entities ) { - throw new NotImplementedException(); - } - - public Task RemoveAsync( IEnumerable entities ) { - throw new NotImplementedException(); - } - } -} diff --git a/test/Util.Tests/Samples/Sample.cs b/test/Util.Tests/Samples/Sample.cs deleted file mode 100644 index 9196dbb78..000000000 --- a/test/Util.Tests/Samples/Sample.cs +++ /dev/null @@ -1,179 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; - -namespace Util.Tests.Samples { - public interface ISample { - } - - /// - /// 测试样例 - /// - [Description( "测试样例" )] - public class Sample : ISample { - /// - /// 初始化测试样例 - /// - public Sample() { - StringList = new List(); - } - - /// - /// 测试值 - /// - [Display( Name = "测试值" )] - [Required( ErrorMessage = "测试值不能为空" )] - [StringLength( 20, ErrorMessage = "测试值长度不能超过20" )] - [EmailAddress] - [Url] - public string TestValue { get; set; } - /// - /// 电子邮件验证 - /// - [EmailAddress( ErrorMessage = "电子邮件不正确" )] - public string Email { get; set; } - /// - /// 网址验证 - /// - [Url( ErrorMessage = "网址不正确" )] - public string Url { get; set; } - /// - /// 最大长度 - /// - [MaxLength( 2, ErrorMessage = "最大长度是2" )] - public string MaxLengthValue { get; set; } - /// - /// 长度验证 - /// - [StringLength( 3, MinimumLength = 2, ErrorMessage = "最大长度是3,最小长度是2" )] - public string StringLengthValue { get; set; } - /// - /// DisplayName - /// - [DisplayName( "DisplayNameValue值" )] - public string DisplayNameValue { get; set; } - /// - /// Display - /// - [Display( Name = "DisplayValue值" )] - public string DisplayValue { get; set; } - /// - /// string值 - /// - [Display( Name = "字符串值" )] - [StringLength( 20, ErrorMessage = "长度不能超过20" )] - [Required( ErrorMessage = "不能为空" )] - public string StringValue { get; set; } - /// - /// decimal值 - /// - public decimal DecimalValue { get; set; } - /// - /// 可空decimal值 - /// - public decimal? NullableDecimalValue { get; set; } - /// - /// float值 - /// - public float FloatValue { get; set; } - /// - /// 可空float值 - /// - public float? NullableFloatValue { get; set; } - /// - /// double值 - /// - public double DoubleValue { get; set; } - /// - /// 可空double值 - /// - public double? NullableDoubleValue { get; set; } - /// - /// bool值 - /// - public bool BoolValue { get; set; } - /// - /// 可空bool值 - /// - public bool? NullableBoolValue { get; set; } - /// - /// Enum值 - /// - public EnumSample EnumValue { get; set; } - /// - /// 可空Enum值 - /// - public EnumSample? NullableEnumValue { get; set; } - /// - /// DateTime值 - /// - public DateTime DateValue { get; set; } - /// - /// 可空DateTime值 - /// - public DateTime? NullableDateValue { get; set; } - /// - /// int值 - /// - [Description( "IntValue" )] - public int IntValue { get; set; } - /// - /// 可空int值 - /// - public int? NullableIntValue { get; set; } - /// - /// short值 - /// - public short ShortValue { get; set; } - /// - /// 可空short值 - /// - public short? NullableShortValue { get; set; } - /// - /// long值 - /// - public long LongValue { get; set; } - /// - /// 可空long值 - /// - public long? NullableLongValue { get; set; } - /// - /// 字符串列表 - /// - public List StringList { get; set; } - - /// - /// 测试2 - /// - public Sample2 Test2 { get; set; } - - /// - /// 静态属性 - /// - public static string StaticString { - get { return "TestStaticString"; } - } - - /// - /// 静态对象 - /// - public static Sample2 StaticSample { - get { return new Sample2() {StringValue = "TestStaticSample" };} - } - - /// - /// 创建测试实例1 - /// - public static Sample Create1() { - return new Sample { StringValue = "A" }; - } - - /// - /// 创建测试实例2 - /// - public static Sample Create2() { - return new Sample { StringValue = "B" }; - } - } -} diff --git a/test/Util.Tests/Samples/Sample2.cs b/test/Util.Tests/Samples/Sample2.cs deleted file mode 100644 index 2b4fb7758..000000000 --- a/test/Util.Tests/Samples/Sample2.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; - -namespace Util.Tests.Samples { - /// - /// 测试样例2 - /// - [DisplayName( "测试样例2" )] - public class Sample2 { - /// - /// 描述 - /// - [Description("描述")] - public string Description { get; set; } - /// - /// 显示名 - /// - [DisplayName( "显示名" )] - public string DisplayName { get; set; } - /// - /// 显示描述 - /// - [Display( Description= "显示描述" )] - public string Display { get; set; } - /// - /// string值 - /// - public string StringValue { get; set; } - /// - /// int值 - /// - public int IntValue { get; set; } - /// - /// 导航属性 - /// - public Sample3 Test3 { get; set; } - } -} diff --git a/test/Util.Tests/Samples/Sample3.cs b/test/Util.Tests/Samples/Sample3.cs deleted file mode 100644 index e97bd71d4..000000000 --- a/test/Util.Tests/Samples/Sample3.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.ComponentModel; - -namespace Util.Tests.Samples { - /// - /// 测试样例3 - /// - [DisplayName( "测试样例3" )] - public class Sample3 { - /// - /// string值 - /// - public string StringValue { get; set; } - } -} diff --git a/test/Util.Tests/Samples/StringAggregateRootSample.cs b/test/Util.Tests/Samples/StringAggregateRootSample.cs deleted file mode 100644 index 3e1e3a3cf..000000000 --- a/test/Util.Tests/Samples/StringAggregateRootSample.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using Util.Domains; - -namespace Util.Tests.Samples { - /// - /// string聚合根测试样例 - /// - public class StringAggregateRootSample : AggregateRoot { - /// - /// 初始化string聚合根测试样例 - /// - public StringAggregateRootSample() - : this( Guid.NewGuid().ToString() ) { - } - - /// - /// 初始化string聚合根测试样例 - /// - /// 标识 - public StringAggregateRootSample( string id ) - : base( id ) { - } - - /// - /// 姓名 - /// - public string Name { get; set; } - - /// - /// 添加描述 - /// - protected override void AddDescriptions() { - AddDescription( "Id:" + Id + "," ); - AddDescription( "姓名", Name ); - } - - /// - /// 添加变更列表 - /// - protected override void AddChanges( StringAggregateRootSample other ) { - AddChange( "Name", "StringSampleName", Name, other.Name ); - } - } -} diff --git a/test/Util.Tests/Samples/TreeEntitySample.cs b/test/Util.Tests/Samples/TreeEntitySample.cs deleted file mode 100644 index b4791be81..000000000 --- a/test/Util.Tests/Samples/TreeEntitySample.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using Util.Domains.Trees; - -namespace Util.Tests.Samples { - /// - /// 树型实体测试样例 - /// - public class TreeEntitySample : TreeEntityBase { - /// - /// 初始化树型实体测试样例 - /// - public TreeEntitySample() : this( Guid.Empty ) { - } - - /// - /// 初始化树型实体测试样例 - /// - public TreeEntitySample( Guid id ) - : base( id, "", 0 ) { - } - - /// - /// 初始化树型实体测试样例 - /// - public TreeEntitySample( Guid id, string path ) - : base( id, path, 0 ) { - } - } -} diff --git a/test/Util.Tests/Samples/ValidationHandlerSample.cs b/test/Util.Tests/Samples/ValidationHandlerSample.cs deleted file mode 100644 index c4553ab4e..000000000 --- a/test/Util.Tests/Samples/ValidationHandlerSample.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Util.Validations; - -namespace Util.Tests.Samples { - /// - /// 验证处理器样例 - 什么也不做 - /// - public class ValidationHandlerSample : IValidationHandler { - /// - /// 处理错误 - /// - /// 验证结果集合 - public void Handle( ValidationResultCollection results ) { - } - } -} diff --git a/test/Util.Tests/Samples/ValidationRuleSample.cs b/test/Util.Tests/Samples/ValidationRuleSample.cs deleted file mode 100644 index 813cc8ae2..000000000 --- a/test/Util.Tests/Samples/ValidationRuleSample.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Util.Tests.Samples; -using Util.Validations; - -namespace Util.Tests.Samples { - /// - /// 验证规则样例 - 名称长度大于3将验证失败 - /// - public class ValidationRuleSample : IValidationRule { - /// - /// 初始化名称长度验证规则 - /// - /// 聚合根测试样例 - public ValidationRuleSample( AggregateRootSample sample ) { - _sample = sample; - } - - /// - /// 聚合根测试样例 - /// - private readonly AggregateRootSample _sample; - - /// - /// 验证 - /// - public ValidationResult Validate() { - if( _sample.Name.Length > 3 ) - return new ValidationResult( "名称长度不能大于3" ); - return ValidationResult.Success; - } - } -} diff --git a/test/Util.Tests/Samples/ValueObjectSample.cs b/test/Util.Tests/Samples/ValueObjectSample.cs deleted file mode 100644 index 749beb842..000000000 --- a/test/Util.Tests/Samples/ValueObjectSample.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using Util.Domains; - -namespace Util.Tests.Samples { - /// - /// 值对象测试样例 - /// - public class ValueObjectSample : ValueObjectBase { - /// - /// 初始化值对象测试样例 - /// - public ValueObjectSample() { - } - - /// - /// 初始化值对象测试样例 - /// - public ValueObjectSample( string city, string street ) - : this( city, street, null ) { - } - - /// - /// 初始化值对象测试样例 - /// - public ValueObjectSample( string city, string street, AggregateRootSample sample ) - : this( city, street, sample, null ) { - } - - /// - /// 初始化值对象测试样例 - /// - public ValueObjectSample( string city, string street, AggregateRootSample sample, ValueObjectSample child ) { - City = city; - Street = street; - AggregateRoot = sample; - Child = child; - } - - /// - /// 城市 - /// - [StringLength( 100, ErrorMessage = "城市输入过长,不能超过100位" )] - public string City { get; private set; } - /// - /// 街道 - /// - [StringLength( 200, ErrorMessage = "街道输入过长,不能超过200位" )] - public string Street { get; private set; } - - /// - /// 聚合根测试样例 - /// - [NotMapped] - public AggregateRootSample AggregateRoot { get; private set; } - - /// - /// 子值对象测试样例 - /// - [NotMapped] - public ValueObjectSample Child { get; private set; } - } -} diff --git a/test/Util.Tests/Signatures/SignManagerTest.cs b/test/Util.Tests/Signatures/SignManagerTest.cs deleted file mode 100644 index b6339fa1e..000000000 --- a/test/Util.Tests/Signatures/SignManagerTest.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Util.Signatures; -using Util.Tests.Helpers; -using Xunit; -using Xunit.Abstractions; - -namespace Util.Tests.Signatures { - /// - /// 签名服务测试 - /// - public class SignManagerTest { - /// - /// 输出 - /// - private readonly ITestOutputHelper _output; - /// - /// 签名服务 - /// - private readonly SignManager _manager; - - /// - /// 测试初始化 - /// - public SignManagerTest( ITestOutputHelper output ) { - _output = output; - _manager = new SignManager( new SignKey( EncryptTest.RsaKey ) ); - } - - /// - /// 测试签名 - /// - [Fact] - public void TestSign_1() { - const string result = "cFIjAWDAuNzRYzGOr65ux4e5GEOUvKUT0mLTpAJ89vem70IsdKCrs0IY2TANw3I6pBqdeG0Lz6kNeWHkurN+tj1+C/7ZpRgHIilV+sUU5Dv0Nw/cDVjvs4fyKJ4CEr8zcs1MB1ek0COuQ/kfHxbAr9sWE9a0nqxnZ/FnsDy5ogFP1LQStkms+e7Ph9CC/dyl6JRlpgZx7/NwnN9kF3zEnVwdPxxLq5as1EV7FmlpLcuI/tkCpL8G+vPJcB3xktM9EBBRMR+peDbusZ1fOAuxE7zbW3XVsgz7JzKUcHE5KNS3zzcov404zKT/8i/ezyCxRCWRHDy3O3zHg5bUUOluIQ=="; - _manager.Add( "sign_type", "RSA2" ); - _output.WriteLine( _manager.Sign() ); - Assert.Equal( result, _manager.Sign() ); - } - - /// - /// 测试签名 - /// - [Fact] - public void TestSign_2() { - const string result = "nGnJ0ANw7e17enJRGzMqsrLFQhObn6sKi+Po17H4ff+3VgqVNXsQC6L9TTzEzATz/0dfZ1w3Wbq+EeXJYC6RvAg86OBTztSOCr7TJDeGyriClftyDvSPXW07He3R0N46+kM8Yel16FP1wXon5cZS2JFvPpfmaQnyCWngW2de5HkXJmwZ4OJ44zqw4aD9rB8UNtq/R2Tu3078w+ux6FdLNVsC2TIq9kXHT2mG6yyUsJZxVX4DFwnc7gPXAOhiwEsLMqUnyBpW5QtiPi9DjdKwIr35f/+ZydheqnE9im4cl8WVwKHRyTElvgWIO31mvpNzkBwo0yaie65ENrPw7eVhrQ=="; - _manager.Add( "sign_type", "RSA2" ); - _manager.Add( "d", "" ); - _manager.Add( "c", "12" ); - _manager.Add( "a", "2" ); - _output.WriteLine( _manager.Sign() ); - Assert.Equal( result, _manager.Sign() ); - } - } -} diff --git a/test/Util.Tests/Validations/DataAnnotationValidationTest.cs b/test/Util.Tests/Validations/DataAnnotationValidationTest.cs deleted file mode 100644 index c91db385e..000000000 --- a/test/Util.Tests/Validations/DataAnnotationValidationTest.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Linq; -using Util.Tests.Samples; -using Util.Validations; -using Xunit; - -namespace Util.Tests.Validations { - /// - /// 测试验证操作 - /// - public class DataAnnotationValidationTest { - /// - /// 聚合根测试样例 - /// - private readonly AggregateRootSample _sample; - - /// - /// 测试初始化 - /// - public DataAnnotationValidationTest() { - _sample = AggregateRootSample.CreateSample(); - } - - /// - /// 测试验证 - /// - [Fact] - public void TestValidate() { - _sample.Name = null; - _sample.EnglishName = " "; - var result = DataAnnotationValidation.Validate( _sample ); - Assert.Equal( 2, result.Count ); - } - - /// - /// 测试验证 - 名称为必填项 - /// - [Fact] - public void TestValidate_Name_Required() { - _sample.Name = null; - var result = DataAnnotationValidation.Validate( _sample ); - Assert.Equal( "姓名不能为空", result.First().ErrorMessage ); - } - - /// - /// 测试验证 - 从资源文件中加载错误消息 - /// - [Fact] - public void TestValidate_Resource() { - _sample.EnglishName = null; - var result = DataAnnotationValidation.Validate( _sample ); - Assert.Equal( "英文名不能为空", result.First().ErrorMessage ); - } - } -} diff --git a/test/Util.Tests/Validations/ValidationResultCollectionTest.cs b/test/Util.Tests/Validations/ValidationResultCollectionTest.cs deleted file mode 100644 index 6c38b758d..000000000 --- a/test/Util.Tests/Validations/ValidationResultCollectionTest.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.Linq; -using Util.Validations; -using Xunit; - -namespace Util.Tests.Validations { - /// - /// 测试验证结果集合 - /// - public class ValidationResultCollectionTest { - /// - /// 验证结果集合 - /// - private readonly ValidationResultCollection _results; - - /// - /// 测试初始化 - /// - public ValidationResultCollectionTest() { - _results = new ValidationResultCollection(); - } - - /// - /// 测试在默认情况下,结果集合个数为0 - /// - [Fact] - public void TestDefault() { - Assert.Equal( 0, _results.Count ); - Assert.True( _results.IsValid ); - } - - /// - /// 测试添加null值,直接忽略 - /// - [Fact] - public void TestAdd_Null() { - _results.Add( null ); - Assert.Equal( 0, _results.Count ); - } - - /// - /// 测试添加验证成功结果,忽略 - /// - [Fact] - public void TestAdd_Success() { - _results.Add( ValidationResult.Success ); - Assert.Equal( 0, _results.Count ); - } - - /// - /// 测试添加1个验证结果 - /// - [Fact] - public void TestAdd_1Result() { - _results.Add( new ValidationResult( "a" ) ); - Assert.Equal( 1, _results.Count ); - Assert.Equal( "a", _results.First().ErrorMessage ); - Assert.False( _results.IsValid ); - } - - /// - /// 测试添加null验证结果集合,直接忽略 - /// - [Fact] - public void TestAddList_Null() { - _results.AddList( null ); - Assert.Equal( 0, _results.Count ); - } - - /// - /// 测试添加2个验证结果 - /// - [Fact] - public void TestAddList_2Result() { - _results.AddList( new[] { new ValidationResult( "a" ), new ValidationResult( "b" ) } ); - Assert.Equal( 2, _results.Count ); - Assert.Equal( "b", _results.Last().ErrorMessage ); - } - } -} diff --git a/test/Util.Tests/XUnitHelpers/AssertHelper.cs b/test/Util.Tests/XUnitHelpers/AssertHelper.cs deleted file mode 100644 index de6a7a74a..000000000 --- a/test/Util.Tests/XUnitHelpers/AssertHelper.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using AspectCore.DynamicProxy; -using Xunit; - -namespace Util.Tests.XUnitHelpers { - /// - /// 断言操作 - /// - public class AssertHelper { - /// - /// 抛出异常,并从异常消息中搜索特定关键字 - /// - /// 异常类型 - /// 操作 - /// 关键字 - public static TException Throws( Action action,string keyword = "" ) where TException : Exception { - var exception = GetException( action ); - if( !string.IsNullOrWhiteSpace(keyword) ) - Assert.Contains( keyword, exception.Message ); - return exception; - } - - /// - /// 获取异常 - /// - private static TException GetException( Action action ) where TException : Exception { - try { - var exception = Assert.Throws( action ); - return (TException)exception.InnerException; - } - catch { - return Assert.Throws( action ); - } - } - } -} diff --git a/test/Util.Ui.NgAlain.Tests/Ellipsis/EllipsisTagHelperTest.Expression.cs b/test/Util.Ui.NgAlain.Tests/Ellipsis/EllipsisTagHelperTest.Expression.cs new file mode 100644 index 000000000..88b590d67 --- /dev/null +++ b/test/Util.Ui.NgAlain.Tests/Ellipsis/EllipsisTagHelperTest.Expression.cs @@ -0,0 +1,22 @@ +using System.Text; +using Xunit; + +namespace Util.Ui.NgAlain.Tests.Ellipsis; + +/// +/// 文本省略组件测试 +/// +public partial class EllipsisTagHelperTest { + /// + /// 测试解析表达式属性for + /// + [Fact] + public void TestFor_1() { + _wrapper.SetExpression( t => t.Code ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{model.code}}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgAlain.Tests/Ellipsis/EllipsisTagHelperTest.cs b/test/Util.Ui.NgAlain.Tests/Ellipsis/EllipsisTagHelperTest.cs new file mode 100644 index 000000000..d5161eb15 --- /dev/null +++ b/test/Util.Ui.NgAlain.Tests/Ellipsis/EllipsisTagHelperTest.cs @@ -0,0 +1,128 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Ellipsis; +using Util.Ui.NgAlain.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgAlain.Tests.Ellipsis; + +/// +/// 文本省略组件测试 +/// +public partial class EllipsisTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public EllipsisTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new EllipsisTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试工具提示 + /// + [Fact] + public void TestTooltip() { + _wrapper.SetContextAttribute( UiConst.Tooltip, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本最大长度 + /// + [Fact] + public void TestLength() { + _wrapper.SetContextAttribute( UiConst.Length, "2" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本最大行数 + /// + [Fact] + public void TestLines() { + _wrapper.SetContextAttribute( UiConst.Lines, "2" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否将全角字符的长度视为2来计算字符串长度 + /// + [Fact] + public void TestFullWidthRecognition() { + _wrapper.SetContextAttribute( UiConst.FullWidthRecognition, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试溢出尾巴 + /// + [Fact] + public void TestTail() { + _wrapper.SetContextAttribute( UiConst.Tail, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试溢出尾巴 + /// + [Fact] + public void TestBindTail() { + _wrapper.SetContextAttribute( AngularConst.BindTail, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestAppendContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgAlain.Tests/FooterToolbars/FooterToolbarTagHelperTest.cs b/test/Util.Ui.NgAlain.Tests/FooterToolbars/FooterToolbarTagHelperTest.cs new file mode 100644 index 000000000..3515cfd9c --- /dev/null +++ b/test/Util.Ui.NgAlain.Tests/FooterToolbars/FooterToolbarTagHelperTest.cs @@ -0,0 +1,94 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.FooterToolbars; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgAlain.Tests.FooterToolbars; + +/// +/// 底部工具栏测试 +/// +public class FooterToolbarTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public FooterToolbarTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new FooterToolbarTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试错误收集 + /// + [Fact] + public void TestErrorCollect() { + _wrapper.SetContextAttribute( UiConst.ErrorCollect, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试额外信息 + /// + [Fact] + public void TestExtra() { + _wrapper.SetContextAttribute( UiConst.Extra, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试额外信息 + /// + [Fact] + public void TestBindExtra() { + _wrapper.SetContextAttribute( AngularConst.BindExtra, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestAppendContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgAlain.Tests/I18n/I18nTagHelperTest.cs b/test/Util.Ui.NgAlain.Tests/I18n/I18nTagHelperTest.cs new file mode 100644 index 000000000..da76ac451 --- /dev/null +++ b/test/Util.Ui.NgAlain.Tests/I18n/I18nTagHelperTest.cs @@ -0,0 +1,84 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.I18n; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgAlain.Tests.I18n; + +/// +/// i18n多语言显示组件测试 +/// +public class I18nTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public I18nTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new I18nTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试键 + /// + [Fact] + public void TestKey() { + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试参数 - 参数有大括号 + /// + [Fact] + public void TestParam_1() { + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.Param, " {id:1}" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试参数 - 参数没有大括号 + /// + [Fact] + public void TestParam_2() { + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.Param, " id:1,name:'a' " ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgAlain.Tests/PageHeaders/PageHeaderTagHelperTest.cs b/test/Util.Ui.NgAlain.Tests/PageHeaders/PageHeaderTagHelperTest.cs new file mode 100644 index 000000000..a69c4afc0 --- /dev/null +++ b/test/Util.Ui.NgAlain.Tests/PageHeaders/PageHeaderTagHelperTest.cs @@ -0,0 +1,317 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.PageHeaders; +using Util.Ui.NgZorro; +using Util.Ui.NgZorro.Configs; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgAlain.Tests.PageHeaders; + +/// +/// 页头测试 +/// +public class PageHeaderTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public PageHeaderTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new PageHeaderTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 - 支持多语言 + /// + [Fact] + public void TestTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions{EnableI18n = true} ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动设置标题 + /// + [Fact] + public void TestAutoTitle() { + _wrapper.SetContextAttribute( UiConst.AutoTitle, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试同步标题 + /// + [Fact] + public void TestSyncTitle() { + _wrapper.SetContextAttribute( UiConst.SyncTitle, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试首页文本 + /// + [Fact] + public void TestHome() { + _wrapper.SetContextAttribute( UiConst.Home, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试首页文本 + /// + [Fact] + public void TestBindHome() { + _wrapper.SetContextAttribute( AngularConst.BindHome, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试首页链接 + /// + [Fact] + public void TestHomeLink() { + _wrapper.SetContextAttribute( UiConst.HomeLink, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试首页链接 + /// + [Fact] + public void TestBindHomeLink() { + _wrapper.SetContextAttribute( AngularConst.BindHomeLink, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试首页国际化 + /// + [Fact] + public void TestHomeI18n() { + _wrapper.SetContextAttribute( UiConst.HomeI18n, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试首页国际化 + /// + [Fact] + public void TestBindHomeI18n() { + _wrapper.SetContextAttribute( AngularConst.BindHomeI18n, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动生成导航 + /// + [Fact] + public void TestAutoBreadcrumb() { + _wrapper.SetContextAttribute( UiConst.AutoBreadcrumb, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试递归查找导航 + /// + [Fact] + public void TestRecursiveBreadcrumb() { + _wrapper.SetContextAttribute( UiConst.RecursiveBreadcrumb, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载状态 + /// + [Fact] + public void TestLoading() { + _wrapper.SetContextAttribute( UiConst.Loading, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否定宽 + /// + [Fact] + public void TestWide() { + _wrapper.SetContextAttribute( UiConst.Wide, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否固定模式 + /// + [Fact] + public void TestFixed() { + _wrapper.SetContextAttribute( UiConst.Fixed, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定偏移值 + /// + [Fact] + public void TestFixedOffsetTop() { + _wrapper.SetContextAttribute( UiConst.FixedOffsetTop, "6" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义导航区域 + /// + [Fact] + public void TestBreadcrumb() { + _wrapper.SetContextAttribute( UiConst.Breadcrumb, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义Logo区域 + /// + [Fact] + public void TestLogo() { + _wrapper.SetContextAttribute( UiConst.Logo, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义操作区域 + /// + [Fact] + public void TestAction() { + _wrapper.SetContextAttribute( UiConst.Action, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义内容区域 + /// + [Fact] + public void TestContent() { + _wrapper.SetContextAttribute( UiConst.Content, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义额外信息区域 + /// + [Fact] + public void TestExtra() { + _wrapper.SetContextAttribute( UiConst.Extra, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义标签区域 + /// + [Fact] + public void TestTab() { + _wrapper.SetContextAttribute( UiConst.Tab, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestAppendContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgAlain.Tests/Samples/Customer.cs b/test/Util.Ui.NgAlain.Tests/Samples/Customer.cs new file mode 100644 index 000000000..df84e163e --- /dev/null +++ b/test/Util.Ui.NgAlain.Tests/Samples/Customer.cs @@ -0,0 +1,84 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Domain.Biz.Enums; + +namespace Util.Ui.NgAlain.Tests.Samples; + +/// +/// 客户 +/// +public class Customer { + /// + /// 编码 + /// + [Display( Name = "code" )] + [MaxLength( 100 )] + [MinLength( 10, ErrorMessage = "编码最小为10位" )] + [Required(ErrorMessage = "编码不能是空值" )] + public string Code { get; set; } + /// + /// 姓名 + /// + [Description( "姓名" )] + [MaxLength( 200 )] + public string Name { get; set; } + /// + /// 昵称 + /// + [Description( "昵称" )] + [MaxLength( 50 )] + public string Nickname { get; set; } + /// + /// 密码 + /// + [Description( "密码" )] + [DataType(DataType.Password)] + public string Password { get; set; } + /// + /// 性别 + /// + [Description( "性别" )] + public Gender? Gender { get; set; } + /// + /// 出生日期 + /// + [Description( "出生日期" )] + [Required] + public DateTime? Birthday { get; set; } + /// + /// 民族 + /// + [Description( "民族" )] + public Nation? Nation { get; set; } + /// + /// 手机号 + /// + [Description( "手机号" )] + [Phone( ErrorMessage = "手机号错误" )] + public int Phone { get; set; } + /// + /// 身份证 + /// + [Description( "身份证" )] + [IdCard( ErrorMessage = "身份证错误" )] + public int IdCard { get; set; } + /// + /// 年龄 + /// + [Description( "年龄" )] + [Required] + [Range( 5.5,8.8 )] + public double Age { get; set; } + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + [EmailAddress(ErrorMessage = "email错误")] + public string Email { get; set; } + /// + /// 启用 + /// + [Description( "启用" )] + public bool Enabled { get; set; } +} \ No newline at end of file diff --git a/test/Util.Ui.NgAlain.Tests/Sg/SgContainerTagHelperTest.cs b/test/Util.Ui.NgAlain.Tests/Sg/SgContainerTagHelperTest.cs new file mode 100644 index 000000000..3ac5622dd --- /dev/null +++ b/test/Util.Ui.NgAlain.Tests/Sg/SgContainerTagHelperTest.cs @@ -0,0 +1,82 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Sg; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgAlain.Tests.Sg; + +/// +/// 简易栅格容器组件测试 +/// +public class SgContainerTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public SgContainerTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new SgContainerTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距 + /// + [Fact] + public void TestGutter() { + _wrapper.SetContextAttribute( UiConst.Gutter, "2" ); + var result = new StringBuilder(); + result.Append( "
" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列数 + /// + [Fact] + public void TestColumn() { + _wrapper.SetContextAttribute( UiConst.Column, "2" ); + var result = new StringBuilder(); + result.Append( "
" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "
a
" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgAlain.Tests/Sg/SgTagHelperTest.cs b/test/Util.Ui.NgAlain.Tests/Sg/SgTagHelperTest.cs new file mode 100644 index 000000000..cf7487058 --- /dev/null +++ b/test/Util.Ui.NgAlain.Tests/Sg/SgTagHelperTest.cs @@ -0,0 +1,61 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Sg; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgAlain.Tests.Sg; + +/// +/// 简易栅格列组件测试 +/// +public class SgTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public SgTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new SgTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列跨度 + /// + [Fact] + public void TestSpan() { + _wrapper.SetContextAttribute( UiConst.Span, "2" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgAlain.Tests/Startup.cs b/test/Util.Ui.NgAlain.Tests/Startup.cs new file mode 100644 index 000000000..f57e45a4d --- /dev/null +++ b/test/Util.Ui.NgAlain.Tests/Startup.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit.DependencyInjection.Logging; + +namespace Util.Ui.NgAlain.Tests; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + hostBuilder.ConfigureDefaults( null ).AddUtil(); + Util.Helpers.Environment.IsTest = true; + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgAlain.Tests/Sv/SvContainerTagHelperTest.cs b/test/Util.Ui.NgAlain.Tests/Sv/SvContainerTagHelperTest.cs new file mode 100644 index 000000000..9eefa8df1 --- /dev/null +++ b/test/Util.Ui.NgAlain.Tests/Sv/SvContainerTagHelperTest.cs @@ -0,0 +1,184 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Sv; +using Util.Ui.NgAlain.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgAlain.Tests.Sv; + +/// +/// 查看容器组件测试 +/// +public class SvContainerTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public SvContainerTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new SvContainerTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试列数 + /// + [Fact] + public void TestColumn() { + _wrapper.SetContextAttribute( UiConst.Column, "2" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, SvSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试布局 + /// + [Fact] + public void TestLayout() { + _wrapper.SetContextAttribute( UiConst.Layout, SvLayout.Vertical ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试布局 + /// + [Fact] + public void TestBindLayout() { + _wrapper.SetContextAttribute( AngularConst.BindLayout, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距 + /// + [Fact] + public void TestGutter() { + _wrapper.SetContextAttribute( UiConst.Gutter, "2" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签文本宽度 + /// + [Fact] + public void TestLabelWidth() { + _wrapper.SetContextAttribute( UiConst.LabelWidth, 2 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示默认文本 + /// + [Fact] + public void TestDefault() { + _wrapper.SetContextAttribute( UiConst.Default, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否不显示标签后面的冒号 + /// + [Fact] + public void TestNoColon() { + _wrapper.SetContextAttribute( UiConst.NoColon, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示边框 + /// + [Fact] + public void TestBordered() { + _wrapper.SetContextAttribute( UiConst.Bordered, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgAlain.Tests/Sv/SvTagHelperTest.Expression.cs b/test/Util.Ui.NgAlain.Tests/Sv/SvTagHelperTest.Expression.cs new file mode 100644 index 000000000..4ebaa8b72 --- /dev/null +++ b/test/Util.Ui.NgAlain.Tests/Sv/SvTagHelperTest.Expression.cs @@ -0,0 +1,105 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro; +using Xunit; + +namespace Util.Ui.NgAlain.Tests.Sv; + +/// +/// 查看列组件测试 - 表达式测试 +/// +public partial class SvTagHelperTest { + /// + /// 测试属性表达式 - 文本类型 + /// + [Fact] + public void TestFor_1() { + _wrapper.SetExpression( t => t.Code ); + var result = new StringBuilder(); + result.Append( "{{model.code}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 文本类型 - 多语言支持 + /// + [Fact] + public void TestFor_2() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetExpression( t => t.Code ); + _wrapper.SetContextAttribute( UiConst.ValueI18n, true ); + var result = new StringBuilder(); + result.Append( "{{model.code|i18n}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试测试属性表达式 - 复制到剪贴板 + /// + [Fact] + public void TestFor_3() { + _wrapper.SetExpression( t => t.Code ); + _wrapper.SetContextAttribute( UiConst.Clipboard, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{model.code}}" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 布尔类型 + /// + [Fact] + public void TestFor_4() { + _wrapper.SetExpression( t => t.Enabled ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 日期类型 + /// + [Fact] + public void TestFor_5() { + _wrapper.SetExpression( t => t.Birthday ); + var result = new StringBuilder(); + result.Append( "{{model.birthday|date:\"yyyy-MM-dd HH:mm:ss\"}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 日期类型 - 设置日期格式 + /// + [Fact] + public void TestFor_6() { + _wrapper.SetContextAttribute( UiConst.DateFormat, "yyyy-MM" ); + _wrapper.SetExpression( t => t.Birthday ); + var result = new StringBuilder(); + result.Append( "{{model.birthday|date:\"yyyy-MM\"}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 数值类型 + /// + [Fact] + public void TestFor_7() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetExpression( t => t.IdCard ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{model.idCard}}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgAlain.Tests/Sv/SvTagHelperTest.cs b/test/Util.Ui.NgAlain.Tests/Sv/SvTagHelperTest.cs new file mode 100644 index 000000000..d1ee8b920 --- /dev/null +++ b/test/Util.Ui.NgAlain.Tests/Sv/SvTagHelperTest.cs @@ -0,0 +1,306 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Sv; +using Util.Ui.NgAlain.Enums; +using Util.Ui.NgAlain.Tests.Samples; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgAlain.Tests.Sv; + +/// +/// 查看列组件测试 +/// +public partial class SvTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public SvTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new SvTagHelper().ToWrapper(); + Id.SetId("id"); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试列跨度 + /// + [Fact] + public void TestColumn() { + _wrapper.SetContextAttribute( UiConst.Column, "2" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签 + /// + [Fact] + public void TestLabel() { + _wrapper.SetContextAttribute( UiConst.Label, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签 - 多语言 + /// + [Fact] + public void TestLabel_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Label, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签 + /// + [Fact] + public void TestBindLabel() { + _wrapper.SetContextAttribute( AngularConst.BindLabel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单位 + /// + [Fact] + public void TestUnit() { + _wrapper.SetContextAttribute( UiConst.Unit, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单位 + /// + [Fact] + public void TestBindUnit() { + _wrapper.SetContextAttribute( AngularConst.BindUnit, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示默认文本 + /// + [Fact] + public void TestDefault() { + _wrapper.SetContextAttribute( UiConst.Default, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试类型 + /// + [Fact] + public void TestType() { + _wrapper.SetContextAttribute( UiConst.Type, SvType.Primary ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试类型 + /// + [Fact] + public void TestBindType() { + _wrapper.SetContextAttribute( AngularConst.BindType, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签可选信息 + /// + [Fact] + public void TestOptional() { + _wrapper.SetContextAttribute( UiConst.Optional, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签可选信息 + /// + [Fact] + public void TestBindOptional() { + _wrapper.SetContextAttribute( AngularConst.BindOptional, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签可选帮助 + /// + [Fact] + public void TestOptionalHelp() { + _wrapper.SetContextAttribute( UiConst.OptionalHelp, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签可选帮助 + /// + [Fact] + public void TestBindOptionalHelp() { + _wrapper.SetContextAttribute( AngularConst.BindOptionalHelp, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签可选帮助背景颜色 + /// + [Fact] + public void TestOptionalHelpColor() { + _wrapper.SetContextAttribute( UiConst.OptionalHelpColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签可选帮助背景颜色 + /// + [Fact] + public void TestBindOptionalHelpColor() { + _wrapper.SetContextAttribute( AngularConst.BindOptionalHelpColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试不显示冒号 + /// + [Fact] + public void TestNoColon() { + _wrapper.SetContextAttribute( UiConst.NoColon, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试隐藏标签 + /// + [Fact] + public void TestHideLabel() { + _wrapper.SetContextAttribute( UiConst.HideLabel, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestValue() { + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + var result = new StringBuilder(); + result.Append( "{{a}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 - 多语言 + /// + [Fact] + public void TestValueI18n() { + _wrapper.SetContextAttribute( UiConst.ValueI18n, true ); + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + var result = new StringBuilder(); + result.Append( "{{a|i18n}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复制到剪贴板 + /// + [Fact] + public void TestClipboard() { + _wrapper.SetContextAttribute( UiConst.Value, "model.code" ); + _wrapper.SetContextAttribute( UiConst.Clipboard, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{model.code}}" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复制到剪贴板 - 多语言 + /// + [Fact] + public void TestClipboard_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Value, "model.code" ); + _wrapper.SetContextAttribute( UiConst.Clipboard, true ); + _wrapper.SetContextAttribute( UiConst.ValueI18n, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{model.code|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgAlain.Tests/Sv/SvTitleTagHelperTest.cs b/test/Util.Ui.NgAlain.Tests/Sv/SvTitleTagHelperTest.cs new file mode 100644 index 000000000..de95a6efe --- /dev/null +++ b/test/Util.Ui.NgAlain.Tests/Sv/SvTitleTagHelperTest.cs @@ -0,0 +1,75 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Sv; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgAlain.Tests.Sv; + +/// +/// 查看标题组件测试 +/// +public class SvTitleTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public SvTitleTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new SvTitleTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 - 多语言 + /// + [Fact] + public void TestTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "{{'a'|i18n}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgAlain.Tests/Sv/SvValueTagHelperTest.cs b/test/Util.Ui.NgAlain.Tests/Sv/SvValueTagHelperTest.cs new file mode 100644 index 000000000..76e4acc7c --- /dev/null +++ b/test/Util.Ui.NgAlain.Tests/Sv/SvValueTagHelperTest.cs @@ -0,0 +1,140 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Sv; +using Util.Ui.NgAlain.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgAlain.Tests.Sv; + +/// +/// 查看值组件测试 +/// +public class SvValueTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public SvValueTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new SvValueTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试前缀 + /// + [Fact] + public void TestPrefix() { + _wrapper.SetContextAttribute( UiConst.Prefix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前缀 + /// + [Fact] + public void TestBindPrefix() { + _wrapper.SetContextAttribute( AngularConst.BindPrefix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单位 + /// + [Fact] + public void TestUnit() { + _wrapper.SetContextAttribute( UiConst.Unit, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单位 + /// + [Fact] + public void TestBindUnit() { + _wrapper.SetContextAttribute( AngularConst.BindUnit, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文字提示 + /// + [Fact] + public void TestTooltip() { + _wrapper.SetContextAttribute( UiConst.Tooltip, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文字提示 + /// + [Fact] + public void TestBindTooltip() { + _wrapper.SetContextAttribute( AngularConst.BindTooltip, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, SvValueSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgAlain.Tests/Tinymce/TinymceTagHelperTest.Expression.cs b/test/Util.Ui.NgAlain.Tests/Tinymce/TinymceTagHelperTest.Expression.cs new file mode 100644 index 000000000..61a3af9a7 --- /dev/null +++ b/test/Util.Ui.NgAlain.Tests/Tinymce/TinymceTagHelperTest.Expression.cs @@ -0,0 +1,31 @@ +using System.Text; +using Xunit; + +namespace Util.Ui.NgAlain.Tests.Tinymce; + +/// +/// Tinymce富文本编辑器测试 - 表达式解析测试 +/// +public partial class TinymceTagHelperTest { + /// + /// 测试解析表达式属性for + /// + [Fact] + public void TestFor_1() { + _wrapper.SetExpression( t => t.Code ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "code" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_code.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgAlain.Tests/Tinymce/TinymceTagHelperTest.cs b/test/Util.Ui.NgAlain.Tests/Tinymce/TinymceTagHelperTest.cs new file mode 100644 index 000000000..29ca844ed --- /dev/null +++ b/test/Util.Ui.NgAlain.Tests/Tinymce/TinymceTagHelperTest.cs @@ -0,0 +1,235 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgAlain.Components.Tinymce; +using Util.Ui.NgAlain.Enums; +using Util.Ui.NgAlain.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgAlain.Tests.Tinymce; + +/// +/// Tinymce富文本编辑器测试 +/// +public partial class TinymceTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TinymceTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TinymceTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试品牌 + /// + [Fact] + public void TestBranding() { + _wrapper.SetContextAttribute( UiConst.Branding, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否允许粘贴图片 + /// + [Fact] + public void TestPasteDataImages() { + _wrapper.SetContextAttribute( UiConst.PasteDataImages, "false" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试菜单栏 + /// + [Fact] + public void TestMenubar() { + _wrapper.SetContextAttribute( UiConst.Menubar, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试工具栏模式 + /// + [Fact] + public void TestToolbarMode() { + _wrapper.SetContextAttribute( UiConst.ToolbarMode, TinymceToolbarMode.Scrolling ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试插件列表 + /// + [Fact] + public void TestPlugins() { + _wrapper.SetContextAttribute( UiConst.Plugins, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试工具栏 + /// + [Fact] + public void TestToolbar() { + _wrapper.SetContextAttribute( UiConst.Toolbar, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "false" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试必填项验证 + /// + [Fact] + public void TestRequired() { + _wrapper.SetContextAttribute( UiConst.Required, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试占位文本 + /// + [Fact] + public void TestPlaceholder() { + _wrapper.SetContextAttribute( UiConst.Placeholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试占位文本 + /// + [Fact] + public void TestBindPlaceholder() { + _wrapper.SetContextAttribute( AngularConst.BindPlaceholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试配置 + /// + [Fact] + public void TestConfig() { + _wrapper.SetContextAttribute( UiConst.Config, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestNgModel() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestBindNgModel() { + _wrapper.SetContextAttribute( AngularConst.BindNgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签文本 + /// + [Fact] + public void TestLabelText() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestAppendContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容变更事件 + /// + [Fact] + public void TestOnChange() { + _wrapper.SetContextAttribute( UiConst.OnChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgAlain.Tests/Util.Ui.NgAlain.Tests.csproj b/test/Util.Ui.NgAlain.Tests/Util.Ui.NgAlain.Tests.csproj new file mode 100644 index 000000000..70a8ea2e2 --- /dev/null +++ b/test/Util.Ui.NgAlain.Tests/Util.Ui.NgAlain.Tests.csproj @@ -0,0 +1,29 @@ + + + + $(NetTargetFramework) + false + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/test/Util.Ui.NgZorro.Tests/Affixes/AffixTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Affixes/AffixTagHelperTest.cs new file mode 100644 index 000000000..cb0469812 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Affixes/AffixTagHelperTest.cs @@ -0,0 +1,104 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Affixes; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Affixes { + /// + /// 固钉测试 + /// + public class AffixTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public AffixTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new AffixTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试顶部偏移量 + /// + [Fact] + public void TestOffsetTop() { + _wrapper.SetContextAttribute( UiConst.OffsetTop, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试底部偏移量 + /// + [Fact] + public void TestOffsetBottom() { + _wrapper.SetContextAttribute( UiConst.OffsetBottom, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试目标 + /// + [Fact] + public void TestTarget() { + _wrapper.SetContextAttribute( UiConst.Target, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试变更事件 + /// + [Fact] + public void TestOnChange() { + _wrapper.SetContextAttribute( UiConst.OnChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Alerts/AlertTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Alerts/AlertTagHelperTest.cs new file mode 100644 index 000000000..0826d9f39 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Alerts/AlertTagHelperTest.cs @@ -0,0 +1,258 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Alerts; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Alerts { + /// + /// 警告提示测试 + /// + public class AlertTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public AlertTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new AlertTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否顶部公告 + /// + [Fact] + public void TestBanner() { + _wrapper.SetContextAttribute( UiConst.Banner, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否可关闭 + /// + [Fact] + public void TestCloseable() { + _wrapper.SetContextAttribute( UiConst.Closeable, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试关闭按钮文字 + /// + [Fact] + public void TestCloseText() { + _wrapper.SetContextAttribute( UiConst.CloseText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试关闭按钮文字 + /// + [Fact] + public void TestBindCloseText() { + _wrapper.SetContextAttribute( AngularConst.BindCloseText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试描述 + /// + [Fact] + public void TestDescription() { + _wrapper.SetContextAttribute( UiConst.Description, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试描述 + /// + [Fact] + public void TestBindDescription() { + _wrapper.SetContextAttribute( AngularConst.BindDescription, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示内容 + /// + [Fact] + public void TestMessage() { + _wrapper.SetContextAttribute( UiConst.Message, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示内容 + /// + [Fact] + public void TestBindMessage() { + _wrapper.SetContextAttribute( AngularConst.BindMessage, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示图标 + /// + [Fact] + public void TestShowIcon() { + _wrapper.SetContextAttribute( UiConst.ShowIcon, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标类型 + /// + [Fact] + public void TestIconType() { + _wrapper.SetContextAttribute( UiConst.IconType, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标类型 + /// + [Fact] + public void TestBindIconType() { + _wrapper.SetContextAttribute( AngularConst.BindIconType, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标 + /// + [Fact] + public void TestIcon() { + _wrapper.SetContextAttribute( UiConst.Icon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试警告类型 + /// + [Fact] + public void TestType() { + _wrapper.SetContextAttribute( UiConst.Type, AlertType.Warning ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试警告类型 + /// + [Fact] + public void TestBindType() { + _wrapper.SetContextAttribute( AngularConst.BindType, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义操作项 + /// + [Fact] + public void TestAction() { + _wrapper.SetContextAttribute( UiConst.Action, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试关闭事件 + /// + [Fact] + public void TestOnClose() { + _wrapper.SetContextAttribute( UiConst.OnClose, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 - 自动创建模板 + /// + [Fact] + public void TestContent_1() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 - 设置了bind-message,则不自动创建模板 + /// + [Fact] + public void TestContent_2() { + _wrapper.SetContextAttribute( AngularConst.BindMessage, "a" ); + _wrapper.AppendContent( "b" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Anchors/AnchorTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Anchors/AnchorTagHelperTest.cs new file mode 100644 index 000000000..5b1dccee1 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Anchors/AnchorTagHelperTest.cs @@ -0,0 +1,216 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Anchors; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Anchors { + /// + /// 锚点测试 + /// + public class AnchorTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public AnchorTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new AnchorTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否固定模式 + /// + [Fact] + public void TestAffix() { + _wrapper.SetContextAttribute( UiConst.Affix, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试区域边界 + /// + [Fact] + public void TestBounds() { + _wrapper.SetContextAttribute( UiConst.Bounds, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试顶部偏移量 + /// + [Fact] + public void TestOffsetTop() { + _wrapper.SetContextAttribute( UiConst.OffsetTop, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定模式是否显示小圆点 + /// + [Fact] + public void TestShowInkInFixed() { + _wrapper.SetContextAttribute( UiConst.ShowInkInFixed, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试容器 + /// + [Fact] + public void TestContainer() { + _wrapper.SetContextAttribute( UiConst.Container, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试容器 + /// + [Fact] + public void TestBindContainer() { + _wrapper.SetContextAttribute( AngularConst.BindContainer, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义高亮的锚点 + /// + [Fact] + public void TestCurrentAnchor() { + _wrapper.SetContextAttribute( UiConst.CurrentAnchor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义高亮的锚点 + /// + [Fact] + public void TestBindCurrentAnchor() { + _wrapper.SetContextAttribute( AngularConst.BindCurrentAnchor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试锚点滚动偏移量 + /// + [Fact] + public void TestTargetOffset() { + _wrapper.SetContextAttribute( UiConst.TargetOffset, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试方向 + /// + [Fact] + public void TestDirection() { + _wrapper.SetContextAttribute( UiConst.Direction, AnchorDirection.Horizontal ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试方向 + /// + [Fact] + public void TestBindDirection() { + _wrapper.SetContextAttribute( AngularConst.BindDirection, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击事件 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试锚点链接变更事件 + /// + [Fact] + public void TestOnChange() { + _wrapper.SetContextAttribute( UiConst.OnChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试滚动事件 + /// + [Fact] + public void TestOnScroll() { + _wrapper.SetContextAttribute( UiConst.OnScroll, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Anchors/LinkTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Anchors/LinkTagHelperTest.cs new file mode 100644 index 000000000..2b447028f --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Anchors/LinkTagHelperTest.cs @@ -0,0 +1,128 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.Enums; +using Util.Ui.NgZorro.Components.Anchors; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Anchors { + /// + /// 链接测试 + /// + public class LinkTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public LinkTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new LinkTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试锚点链接 + /// + [Fact] + public void TestHref() { + _wrapper.SetContextAttribute( UiConst.Href, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试锚点链接 + /// + [Fact] + public void TestBindHref() { + _wrapper.SetContextAttribute( AngularConst.BindHref, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试链接打开目标 + /// + [Fact] + public void TestTarget() { + _wrapper.SetContextAttribute( UiConst.Target, ATarget.Parent ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试链接打开目标 + /// + [Fact] + public void TestBindTarget() { + _wrapper.SetContextAttribute( AngularConst.BindTarget, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Autocompletes/AutoOptionGroupTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Autocompletes/AutoOptionGroupTagHelperTest.cs new file mode 100644 index 000000000..d19dc4112 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Autocompletes/AutoOptionGroupTagHelperTest.cs @@ -0,0 +1,83 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Autocompletes; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Autocompletes { + /// + /// 自动完成选项组测试 + /// + public class AutoOptionGroupTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public AutoOptionGroupTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new AutoOptionGroupTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试组名 + /// + [Fact] + public void TestLabel() { + _wrapper.SetContextAttribute( UiConst.Label, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试组名 + /// + [Fact] + public void TestBindLabel() { + _wrapper.SetContextAttribute( AngularConst.BindLabel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Autocompletes/AutoOptionTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Autocompletes/AutoOptionTagHelperTest.cs new file mode 100644 index 000000000..7ac48aa2e --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Autocompletes/AutoOptionTagHelperTest.cs @@ -0,0 +1,140 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Autocompletes; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Autocompletes { + /// + /// 自动完成项测试 + /// + public class AutoOptionTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public AutoOptionTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new AutoOptionTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestValue() { + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestBindValue() { + _wrapper.SetContextAttribute( AngularConst.BindValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签 - 内容为空时,将标签写入内容 + /// + [Fact] + public void TestLabel() { + _wrapper.SetContextAttribute( UiConst.Label, "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签 - 内容不为空时,保留内容 + /// + [Fact] + public void TestLabel_2() { + _wrapper.SetContextAttribute( UiConst.Label, "a" ); + _wrapper.AppendContent( "b" ); + var result = new StringBuilder(); + result.Append( "b" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签 - 内容为空时,将标签写入内容 + /// + [Fact] + public void TestBindLabel() { + _wrapper.SetContextAttribute( AngularConst.BindLabel, "a" ); + var result = new StringBuilder(); + result.Append( "{{a}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签 - 内容不为空时,保留内容 + /// + [Fact] + public void TestBindLabel_2() { + _wrapper.SetContextAttribute( AngularConst.BindLabel, "a" ); + _wrapper.AppendContent( "b" ); + var result = new StringBuilder(); + result.Append( "b" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Autocompletes/AutocompleteTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/Autocompletes/AutocompleteTagHelperTest.Expression.cs new file mode 100644 index 000000000..5961d142e --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Autocompletes/AutocompleteTagHelperTest.Expression.cs @@ -0,0 +1,70 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Autocompletes { + /// + /// 自动完成测试 - 表达式解析测试 + /// + public partial class AutocompleteTagHelperTest { + /// + /// 测试解析表达式属性for - 布尔 + /// + [Fact] + public void TestFor_Bool() { + _wrapper.SetContextAttribute( UiConst.Id, "enabled" ); + _wrapper.SetExpression( t => t.Enabled ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@if(x_enabled.isGroup){" ); + result.Append( "@for(group of x_enabled.optionGroups;track group.text ){" ); + result.Append( "" ); + result.Append( "@for(item of group.value;track item.value){" ); + result.Append( "" ); + result.Append( "{{item.text}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + result.Append( "}} @else {" ); + result.Append( "@for(item of x_enabled.options;track item.value){" ); + result.Append( "" ); + result.Append( "{{item.text}}" ); + result.Append( "" ); + result.Append( "}}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试解析表达式属性for - 布尔 - 多语言 + /// + [Fact] + public void TestFor_Bool_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Id, "enabled" ); + _wrapper.SetExpression( t => t.Enabled ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@if(x_enabled.isGroup){" ); + result.Append( "@for(group of x_enabled.optionGroups;track group.text ){" ); + result.Append( "" ); + result.Append( "@for(item of group.value;track item.value){" ); + result.Append( "" ); + result.Append( "{{item.text|i18n}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + result.Append( "}} @else {" ); + result.Append( "@for(item of x_enabled.options;track item.value){" ); + result.Append( "" ); + result.Append( "{{item.text|i18n}}" ); + result.Append( "" ); + result.Append( "}}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Autocompletes/AutocompleteTagHelperTest.Extend.cs b/test/Util.Ui.NgZorro.Tests/Autocompletes/AutocompleteTagHelperTest.Extend.cs new file mode 100644 index 000000000..671507bd2 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Autocompletes/AutocompleteTagHelperTest.Extend.cs @@ -0,0 +1,217 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Autocompletes { + /// + /// 自动完成测试 - 指令扩展 + /// + public partial class AutocompleteTagHelperTest { + + #region 辅助操作 + + /// + /// 添加选项 + /// + private void AppendOptions( StringBuilder result ) { + result.Append( "@if(x_id.isGroup){" ); + result.Append( "@for(group of x_id.optionGroups;track group.text ){" ); + result.Append( "" ); + result.Append( "@for(item of group.value;track item.value){" ); + result.Append( "" ); + result.Append( "{{item.text}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + result.Append( "}} @else {" ); + result.Append( "@for(item of x_id.options;track item.value){" ); + result.Append( "" ); + result.Append( "{{item.text}}" ); + result.Append( "" ); + result.Append( "}}" ); + } + + #endregion + + #region EnableExtend + + /// + /// 测试启用扩展指令 + /// + [Fact] + public void TestEnableExtend_1() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + var result = new StringBuilder(); + result.Append( "" ); + AppendOptions( result ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用扩展指令 - 不启用 + /// + [Fact] + public void TestEnableExtend_2() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, false ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region AutoLoad + + /// + /// 测试初始化时是否自动加载数据 + /// + [Fact] + public void TestAutoLoad() { + _wrapper.SetContextAttribute( UiConst.AutoLoad, "false" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region QueryParam + + /// + /// 测试查询参数 + /// + [Fact] + public void TestQueryParam() { + _wrapper.SetContextAttribute( UiConst.QueryParam, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Sort + + /// + /// 测试排序条件 + /// + [Fact] + public void TestSort() { + _wrapper.SetContextAttribute( UiConst.Sort, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试排序条件 + /// + [Fact] + public void TestBindSort() { + _wrapper.SetContextAttribute( AngularConst.BindSort, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Url + + /// + /// 测试Api地址 + /// + [Fact] + public void TestUrl_1() { + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + AppendOptions( result ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Api地址 + /// + [Fact] + public void TestBindUrl() { + _wrapper.SetContextAttribute( AngularConst.BindUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + AppendOptions( result ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Data + + /// + /// 测试数据源 + /// + [Fact] + public void TestData() { + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + AppendOptions( result ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数据源 - 多语言 + /// + [Fact] + public void TestData_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@if(x_id.isGroup){" ); + result.Append( "@for(group of x_id.optionGroups;track group.text ){" ); + result.Append( "" ); + result.Append( "@for(item of group.value;track item.value){" ); + result.Append( "" ); + result.Append( "{{item.text|i18n}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + result.Append( "}} @else {" ); + result.Append( "@for(item of x_id.options;track item.value){" ); + result.Append( "" ); + result.Append( "{{item.text|i18n}}" ); + result.Append( "" ); + result.Append( "}}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnLoad + + /// + /// 测试数据加载完成事件 + /// + [Fact] + public void TestOnLoad() { + _wrapper.SetContextAttribute( UiConst.OnLoad, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Autocompletes/AutocompleteTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Autocompletes/AutocompleteTagHelperTest.cs new file mode 100644 index 000000000..1e9ead7f9 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Autocompletes/AutocompleteTagHelperTest.cs @@ -0,0 +1,174 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Autocompletes; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Autocompletes { + /// + /// 自动完成测试 + /// + public partial class AutocompleteTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public AutocompleteTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new AutocompleteTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试引用变量 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试回填选中项 + /// + [Fact] + public void TestBackfill() { + _wrapper.SetContextAttribute( UiConst.Backfill, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数据源 + /// + [Fact] + public void TestDataSource() { + _wrapper.SetContextAttribute( UiConst.Datasource, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试高亮第一项 + /// + [Fact] + public void TestDefaultActiveFirstOption() { + _wrapper.SetContextAttribute( UiConst.DefaultActiveFirstOption, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽度 + /// + [Fact] + public void TestWidth() { + _wrapper.SetContextAttribute( UiConst.Width, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉根元素类名 + /// + [Fact] + public void TestOverlayClassName() { + _wrapper.SetContextAttribute( UiConst.OverlayClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉根元素类名 + /// + [Fact] + public void TestBindOverlayClassName() { + _wrapper.SetContextAttribute( AngularConst.BindOverlayClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉根元素样式 + /// + [Fact] + public void TestOverlayStyle() { + _wrapper.SetContextAttribute( UiConst.OverlayStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉根元素样式 + /// + [Fact] + public void TestBindOverlayStyle() { + _wrapper.SetContextAttribute( AngularConst.BindOverlayStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试比较算法函数 + /// + [Fact] + public void TestCompareWith() { + _wrapper.SetContextAttribute( UiConst.CompareWith, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Avatars/AvatarGroupTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Avatars/AvatarGroupTagHelperTest.cs new file mode 100644 index 000000000..e24815449 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Avatars/AvatarGroupTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Avatars; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Avatars { + /// + /// 头像组测试 + /// + public class AvatarGroupTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public AvatarGroupTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new AvatarGroupTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Avatars/AvatarTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Avatars/AvatarTagHelperTest.cs new file mode 100644 index 000000000..54a46dbb8 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Avatars/AvatarTagHelperTest.cs @@ -0,0 +1,364 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Avatars; +using Util.Ui.NgZorro.Components.Comments; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Avatars { + /// + /// 头像测试 + /// + public class AvatarTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public AvatarTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new AvatarTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试访问控制 + /// + [Fact] + public void TestAcl_1() { + _wrapper.SetContextAttribute( UiConst.Acl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试访问控制 - '开头 + /// + [Fact] + public void TestAcl_2() { + _wrapper.SetContextAttribute( UiConst.Acl, "'a'" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试访问控制 - [开头 + /// + [Fact] + public void TestAcl_3() { + _wrapper.SetContextAttribute( UiConst.Acl, "['a']" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试访问控制 - or条件 + /// + [Fact] + public void TestAcl_4() { + _wrapper.SetContextAttribute( UiConst.Acl, " a || b " ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试访问控制 - or条件 - 带引号 + /// + [Fact] + public void TestAcl_5() { + _wrapper.SetContextAttribute( UiConst.Acl, "'a' || 'b'" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试访问控制 - or条件 - 带[] + /// + [Fact] + public void TestAcl_6() { + _wrapper.SetContextAttribute( UiConst.Acl, "['a','b']" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试访问控制 - and条件 + /// + [Fact] + public void TestAcl_7() { + _wrapper.SetContextAttribute( UiConst.Acl, " a && b " ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试访问控制 - and条件 - 带引号 + /// + [Fact] + public void TestAcl_8() { + _wrapper.SetContextAttribute( UiConst.Acl, " 'a' && 'b' " ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试访问控制 + /// + [Fact] + public void TestAclElse() { + _wrapper.SetContextAttribute( UiConst.Acl, "a" ); + _wrapper.SetContextAttribute( UiConst.AclElseTemplateId, "b" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试访问控制 + /// + [Fact] + public void TestBindAcl() { + _wrapper.SetContextAttribute( AngularConst.BindAcl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标 + /// + [Fact] + public void TestIcon() { + _wrapper.SetContextAttribute( UiConst.Icon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标 + /// + [Fact] + public void TestBindIcon() { + _wrapper.SetContextAttribute( AngularConst.BindIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试形状 + /// + [Fact] + public void TestShape() { + _wrapper.SetContextAttribute( UiConst.Shape, AvatarShape.Square ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试形状 + /// + [Fact] + public void TestBindShape() { + _wrapper.SetContextAttribute( AngularConst.BindShape, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试大小 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, AvatarSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试大小 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本距离两侧间距 + /// + [Fact] + public void TestGap() { + _wrapper.SetContextAttribute( UiConst.Gap, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图片头像地址 + /// + [Fact] + public void TestSrc() { + _wrapper.SetContextAttribute( UiConst.Src, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图片头像地址 + /// + [Fact] + public void TestBindSrc() { + _wrapper.SetContextAttribute( AngularConst.BindSrc, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图片头像响应式资源地址 + /// + [Fact] + public void TestSrcSet() { + _wrapper.SetContextAttribute( UiConst.SrcSet, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图片头像响应式资源地址 + /// + [Fact] + public void TestBindSrcSet() { + _wrapper.SetContextAttribute( AngularConst.BindSrcSet, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图片无法显示时的替代文本 + /// + [Fact] + public void TestAlt() { + _wrapper.SetContextAttribute( UiConst.Alt, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图片无法显示时的替代文本 + /// + [Fact] + public void TestBindAlt() { + _wrapper.SetContextAttribute( AngularConst.BindAlt, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本 + /// + [Fact] + public void TestText() { + _wrapper.SetContextAttribute( UiConst.Text, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本 + /// + [Fact] + public void TestBindText() { + _wrapper.SetContextAttribute( AngularConst.BindText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图片加载失败事件 + /// + [Fact] + public void TestOnError() { + _wrapper.SetContextAttribute( UiConst.OnError, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试评论头像-头像标签放入评论标签中,会自动添加nz-comment-avatar属性 + /// + [Fact] + public void TestCommentAvatar() { + var comment = new CommentTagHelper().ToWrapper(); + comment.AppendContent( _wrapper ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), comment.GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/BackTops/BackTopTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/BackTops/BackTopTagHelperTest.cs new file mode 100644 index 000000000..eb4c6ad6d --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/BackTops/BackTopTagHelperTest.cs @@ -0,0 +1,121 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.BackTops; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.BackTops; + +/// +/// 回到顶部测试 +/// +public class BackTopTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public BackTopTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new BackTopTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义内容 + /// + [Fact] + public void TestTemplate() { + _wrapper.SetContextAttribute( UiConst.Template, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试可见高度 + /// + [Fact] + public void TestVisibilityHeight() { + _wrapper.SetContextAttribute( UiConst.VisibilityHeight, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试监听目标 + /// + [Fact] + public void TestTarget() { + _wrapper.SetContextAttribute( UiConst.Target, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试持续时间 + /// + [Fact] + public void TestDuration() { + _wrapper.SetContextAttribute( UiConst.Duration, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击事件 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Badges/BadgeTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Badges/BadgeTagHelperTest.cs new file mode 100644 index 000000000..ab84cc6c5 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Badges/BadgeTagHelperTest.cs @@ -0,0 +1,273 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Badges; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Badges { + /// + /// 徽标测试 + /// + public class BadgeTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public BadgeTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new BadgeTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否独立使用 + /// + [Fact] + public void TestStandalone() { + _wrapper.SetContextAttribute( UiConst.Standalone, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色 + /// + [Fact] + public void TestColorType() { + _wrapper.SetContextAttribute( UiConst.ColorType, AntDesignColor.Red ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色 + /// + [Fact] + public void TestColor() { + _wrapper.SetContextAttribute( UiConst.Color, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色 + /// + [Fact] + public void TestBindColor() { + _wrapper.SetContextAttribute( AngularConst.BindColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示的数字 + /// + [Fact] + public void TestCount() { + _wrapper.SetContextAttribute( UiConst.Count, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否不显示数字,仅显示小红点 + /// + [Fact] + public void TestDot() { + _wrapper.SetContextAttribute( UiConst.Dot, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示小红点 + /// + [Fact] + public void TestShowDot() { + _wrapper.SetContextAttribute( UiConst.ShowDot, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试封顶数字 + /// + [Fact] + public void TestOverflowCount() { + _wrapper.SetContextAttribute( UiConst.OverflowCount, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示0值 + /// + [Fact] + public void TestShowZero() { + _wrapper.SetContextAttribute( UiConst.ShowZero, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestStatus() { + _wrapper.SetContextAttribute( UiConst.Status, BadgeStatus.Processing ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestBindStatus() { + _wrapper.SetContextAttribute( AngularConst.BindStatus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态点文本 + /// + [Fact] + public void TestText() { + _wrapper.SetContextAttribute( UiConst.Text, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态点文本 - 多语言 + /// + [Fact] + public void TestText_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Text, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态点文本 + /// + [Fact] + public void TestBindText() { + _wrapper.SetContextAttribute( AngularConst.BindText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态点提示 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态点提示 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态点位置偏移 + /// + [Fact] + public void TestOffset() { + _wrapper.SetContextAttribute( UiConst.Offset, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试徽标尺寸 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, BadgeSize.Small ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试徽标尺寸 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Badges/RibbonTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Badges/RibbonTagHelperTest.cs new file mode 100644 index 000000000..081f3ac74 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Badges/RibbonTagHelperTest.cs @@ -0,0 +1,153 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Badges; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Badges { + /// + /// 缎带徽标测试 + /// + public class RibbonTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public RibbonTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new RibbonTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色 + /// + [Fact] + public void TestColorType() { + _wrapper.SetContextAttribute( UiConst.ColorType, AntDesignColor.Red ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色 + /// + [Fact] + public void TestColor() { + _wrapper.SetContextAttribute( UiConst.Color, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色 + /// + [Fact] + public void TestBindColor() { + _wrapper.SetContextAttribute( AngularConst.BindColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试位置 + /// + [Fact] + public void TestPlacement() { + _wrapper.SetContextAttribute( UiConst.Placement, RibbonPlacement.End ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试位置 + /// + [Fact] + public void TestBindPlacement() { + _wrapper.SetContextAttribute( AngularConst.BindPlacement, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + + /// + /// 测试文本内容 + /// + [Fact] + public void TestText() { + _wrapper.SetContextAttribute( UiConst.Text, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本内容 - 多语言 + /// + [Fact] + public void TestText_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Text, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本内容 + /// + [Fact] + public void TestBindText() { + _wrapper.SetContextAttribute( AngularConst.BindText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Breadcrumbs/BreadcrumbItemTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Breadcrumbs/BreadcrumbItemTagHelperTest.cs new file mode 100644 index 000000000..723c8068c --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Breadcrumbs/BreadcrumbItemTagHelperTest.cs @@ -0,0 +1,73 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Breadcrumbs; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Breadcrumbs { + /// + /// 面包屑项测试 + /// + public class BreadcrumbItemTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public BreadcrumbItemTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new BreadcrumbItemTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试弹出层 + /// + [Fact] + public void TestOverlay() { + _wrapper.SetContextAttribute( UiConst.Overlay, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Breadcrumbs/BreadcrumbSeparatorTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Breadcrumbs/BreadcrumbSeparatorTagHelperTest.cs new file mode 100644 index 000000000..4b3c932c9 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Breadcrumbs/BreadcrumbSeparatorTagHelperTest.cs @@ -0,0 +1,62 @@ +using System.Text; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.NgZorro.Components.Breadcrumbs; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Breadcrumbs { + /// + /// 面包屑分隔符测试 + /// + public class BreadcrumbSeparatorTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public BreadcrumbSeparatorTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new BreadcrumbSeparatorTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Breadcrumbs/BreadcrumbTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Breadcrumbs/BreadcrumbTagHelperTest.cs new file mode 100644 index 000000000..1815ad83c --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Breadcrumbs/BreadcrumbTagHelperTest.cs @@ -0,0 +1,145 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Breadcrumbs; +using Util.Ui.NgZorro.Components.PageHeaders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Breadcrumbs { + /// + /// 面包屑测试 + /// + public class BreadcrumbTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public BreadcrumbTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new BreadcrumbTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试分隔符 + /// + [Fact] + public void TestSeparator() { + _wrapper.SetContextAttribute( UiConst.Separator, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试分隔符 + /// + [Fact] + public void TestBindSeparator() { + _wrapper.SetContextAttribute( AngularConst.BindSeparator, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动生成 + /// + [Fact] + public void TestAutoGenerate() { + _wrapper.SetContextAttribute( UiConst.AutoGenerate, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试路由属性名 + /// + [Fact] + public void TestRouteLabel() { + _wrapper.SetContextAttribute( UiConst.RouteLabel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试路由属性名 + /// + [Fact] + public void TestBindRouteLabel() { + _wrapper.SetContextAttribute( AngularConst.BindRouteLabel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试路由属性名转换函数 + /// + [Fact] + public void TestRouteLabelFn() { + _wrapper.SetContextAttribute( UiConst.RouteLabelFn, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试页头面包屑 + /// + [Fact] + public void TestPageHeader() { + var pageHeader = new PageHeaderTagHelper().ToWrapper(); + pageHeader.AppendContent( _wrapper ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), pageHeader.GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Buttons/ATagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Buttons/ATagHelperTest.cs new file mode 100644 index 000000000..fd882f588 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Buttons/ATagHelperTest.cs @@ -0,0 +1,569 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.Enums; +using Util.Ui.NgZorro.Components.Buttons; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Buttons { + /// + /// 链接测试 + /// + public class ATagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ATagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ATagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试链接地址 + /// + [Fact] + public void TestHref() { + _wrapper.SetContextAttribute( UiConst.Href, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试链接地址 + /// + [Fact] + public void TestBindHref() { + _wrapper.SetContextAttribute( AngularConst.BindHref, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试链接打开目标 + /// + [Fact] + public void TestTarget() { + _wrapper.SetContextAttribute( UiConst.Target, ATarget.Parent ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试链接打开目标 + /// + [Fact] + public void TestBindTarget() { + _wrapper.SetContextAttribute( AngularConst.BindTarget, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试链接关系 + /// + [Fact] + public void TestRel() { + _wrapper.SetContextAttribute( UiConst.Rel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试链接关系 + /// + [Fact] + public void TestBindRel() { + _wrapper.SetContextAttribute( AngularConst.BindRel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试危险状态 + /// + [Fact] + public void TestDanger() { + _wrapper.SetContextAttribute( UiConst.Danger, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试路由链接 + /// + [Fact] + public void TestRouterLink() { + _wrapper.SetContextAttribute( AngularConst.RouterLink, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试路由链接 + /// + [Fact] + public void TestBindRouterLink() { + _wrapper.SetContextAttribute( AngularConst.BindRouterLink, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试活动路由链接 + /// + [Fact] + public void TestRouterLinkActive() { + _wrapper.SetContextAttribute( AngularConst.RouterLinkActive, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试活动路由链接 + /// + [Fact] + public void TestBindRouterLinkActive() { + _wrapper.SetContextAttribute( AngularConst.BindRouterLinkActive, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试路由查询参数 + /// + [Fact] + public void TestQueryParams() { + _wrapper.SetContextAttribute( UiConst.QueryParams, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试路由链接查询参数处理方式 + /// + [Fact] + public void TestQueryParamsHandling() { + _wrapper.SetContextAttribute( UiConst.QueryParamsHandling, QueryParamsHandling.Merge ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试路由链接查询参数处理方式 + /// + [Fact] + public void TestBindQueryParamsHandling() { + _wrapper.SetContextAttribute( AngularConst.BindQueryParamsHandling, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单 + /// + [Fact] + public void TestDropdownMenu() { + _wrapper.SetContextAttribute( UiConst.DropdownMenu, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单弹出位置 + /// + [Fact] + public void TestDropdownMenuPlacement() { + _wrapper.SetContextAttribute( UiConst.DropdownMenuPlacement, DropdownMenuPlacement.BottomLeft ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单弹出位置 + /// + [Fact] + public void TestBindDropdownMenuPlacement() { + _wrapper.SetContextAttribute( AngularConst.BindDropdownMenuPlacement, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单触发方式 + /// + [Fact] + public void TestDropdownMenuTrigger() { + _wrapper.SetContextAttribute( UiConst.DropdownMenuTrigger, DropdownMenuTrigger.Click ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单触发方式 + /// + [Fact] + public void TestBindDropdownMenuTrigger() { + _wrapper.SetContextAttribute( AngularConst.BindDropdownMenuTrigger, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试点击隐藏下拉菜单 + /// + [Fact] + public void TestDropdownMenuClickHide() { + _wrapper.SetContextAttribute( UiConst.DropdownMenuClickHide, "false" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单可见性 + /// + [Fact] + public void TestDropdownMenuVisible() { + _wrapper.SetContextAttribute( UiConst.DropdownMenuVisible, "false" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单可见性 + /// + [Fact] + public void TestBindonDropdownMenuVisible() { + _wrapper.SetContextAttribute( AngularConst.BindonDropdownMenuVisible, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单根元素类名 + /// + [Fact] + public void TestDropdownMenuOverlayClassName() { + _wrapper.SetContextAttribute( UiConst.DropdownMenuOverlayClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单根元素类名 + /// + [Fact] + public void TestBindDropdownMenuOverlayClassName() { + _wrapper.SetContextAttribute( AngularConst.BindDropdownMenuOverlayClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单根元素样式 + /// + [Fact] + public void TestDropdownMenuOverlayStyle() { + _wrapper.SetContextAttribute( UiConst.DropdownMenuOverlayStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试查询表单链接 - 查询条件数量超过初始显示数量则显示 + /// + [Fact] + public void TestIsSearch_1() { + _wrapper.SetContextAttribute( UiConst.IsSearch, true ); + var formShareConfig = new FormShareConfig { SearchFormShowNumber = 1 }; + formShareConfig.AddColumnId( "a" ); + formShareConfig.AddColumnId( "b" ); + formShareConfig.AddColumnId( "action" ); + _wrapper.SetItem( formShareConfig ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{expand?'收起':'展开'}}" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试查询表单链接 - 查询条件数量超过初始显示数量则显示 - 多语言 + /// + [Fact] + public void TestIsSearch_2() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.IsSearch, true ); + var formShareConfig = new FormShareConfig { SearchFormShowNumber = 1 }; + formShareConfig.AddColumnId( "a" ); + formShareConfig.AddColumnId( "b" ); + formShareConfig.AddColumnId( "action" ); + _wrapper.SetItem( formShareConfig ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{expand?('util.collapse'|i18n):('util.expand'|i18n)}}" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试查询表单链接 - 查询条件数量少于等于初始显示数量则不显示 + /// + [Fact] + public void TestIsSearch_3() { + _wrapper.SetContextAttribute( UiConst.IsSearch, true ); + var formShareConfig = new FormShareConfig { SearchFormShowNumber = 2 }; + formShareConfig.AddColumnId( "a" ); + formShareConfig.AddColumnId( "b" ); + formShareConfig.AddColumnId( "action" ); + _wrapper.SetItem( formShareConfig ); + Assert.Empty( GetResult() ); + } + + /// + /// 测试显示表格设置 + /// + [Fact] + public void TestShowTableSettings() { + _wrapper.SetContextAttribute( UiConst.ShowTableSettings, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示表格设置 - 多语言 + /// + [Fact] + public void TestShowTableSettings_i18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.ShowTableSettings, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试全屏 + /// + [Fact] + public void TestFullscreen() { + _wrapper.SetContextAttribute( UiConst.Fullscreen, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试全屏 - 多语言 + /// + [Fact] + public void TestFullscreen_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Fullscreen, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试全屏外层容器样式类名 + /// + [Fact] + public void TestFullscreenWrapClass() { + _wrapper.SetContextAttribute( UiConst.Fullscreen, "a" ); + _wrapper.SetContextAttribute( UiConst.FullscreenWrapClass, "b" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试全屏包装 + /// + [Fact] + public void TestFullscreenPack() { + _wrapper.SetContextAttribute( UiConst.Fullscreen, "a" ); + _wrapper.SetContextAttribute( UiConst.FullscreenPack, false ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试测试全屏标题 + /// + [Fact] + public void TestFullscreenTitle() { + _wrapper.SetContextAttribute( UiConst.Fullscreen, "a" ); + _wrapper.SetContextAttribute( UiConst.FullscreenTitle, "b" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击事件 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单显示状态变化事件 + /// + [Fact] + public void TestOnVisibleChange() { + _wrapper.SetContextAttribute( UiConst.OnVisibleChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选项卡路由联动 + /// + [Fact] + public void TestTabLink_1() { + _wrapper.SetContextAttribute( UiConst.TabLink, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选项卡路由联动 - 空值 + /// + [Fact] + public void TestTabLink_2() { + _wrapper.SetContextAttribute( UiConst.TabLink, "" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选项卡路由联动 - true + /// + [Fact] + public void TestTabLink_3() { + _wrapper.SetContextAttribute( UiConst.TabLink, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Buttons/ButtonGroupTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonGroupTagHelperTest.cs new file mode 100644 index 000000000..357e5c8b8 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonGroupTagHelperTest.cs @@ -0,0 +1,84 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Buttons; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Buttons { + /// + /// 按钮组测试 + /// + public class ButtonGroupTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ButtonGroupTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ButtonGroupTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试按钮尺寸 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, ButtonSize.Small ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试按钮尺寸 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.DropDown.cs b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.DropDown.cs new file mode 100644 index 000000000..b53c10be3 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.DropDown.cs @@ -0,0 +1,144 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Enums; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Buttons { + /// + /// 按钮测试 - 下拉菜单相关 + /// + public partial class ButtonTagHelperTest { + /// + /// 测试下拉菜单 + /// + [Fact] + public void TestDropdownMenu() { + _wrapper.SetContextAttribute( UiConst.DropdownMenu, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单弹出位置 + /// + [Fact] + public void TestDropdownMenuPlacement() { + _wrapper.SetContextAttribute( UiConst.DropdownMenuPlacement, DropdownMenuPlacement.BottomLeft ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单弹出位置 + /// + [Fact] + public void TestBindDropdownMenuPlacement() { + _wrapper.SetContextAttribute( AngularConst.BindDropdownMenuPlacement, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单触发方式 + /// + [Fact] + public void TestDropdownMenuTrigger() { + _wrapper.SetContextAttribute( UiConst.DropdownMenuTrigger, DropdownMenuTrigger.Click ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单触发方式 + /// + [Fact] + public void TestBindDropdownMenuTrigger() { + _wrapper.SetContextAttribute( AngularConst.BindDropdownMenuTrigger, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试点击隐藏下拉菜单 + /// + [Fact] + public void TestDropdownMenuClickHide() { + _wrapper.SetContextAttribute( UiConst.DropdownMenuClickHide, "false" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单可见性 + /// + [Fact] + public void TestDropdownMenuVisible() { + _wrapper.SetContextAttribute( UiConst.DropdownMenuVisible, "false" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单可见性 + /// + [Fact] + public void TestBindonDropdownMenuVisible() { + _wrapper.SetContextAttribute( AngularConst.BindonDropdownMenuVisible, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单根元素类名 + /// + [Fact] + public void TestDropdownMenuOverlayClassName() { + _wrapper.SetContextAttribute( UiConst.DropdownMenuOverlayClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单根元素类名 + /// + [Fact] + public void TestBindDropdownMenuOverlayClassName() { + _wrapper.SetContextAttribute( AngularConst.BindDropdownMenuOverlayClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单根元素样式 + /// + [Fact] + public void TestDropdownMenuOverlayStyle() { + _wrapper.SetContextAttribute( UiConst.DropdownMenuOverlayStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单显示状态变化事件 + /// + [Fact] + public void TestOnVisibleChange() { + _wrapper.SetContextAttribute( UiConst.OnVisibleChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Extend.cs b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Extend.cs new file mode 100644 index 000000000..34012982f --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Extend.cs @@ -0,0 +1,246 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Buttons; + +/// +/// 按钮测试 - 指令扩展 +/// +public partial class ButtonTagHelperTest { + /// + /// 测试是否验证表单 + /// + [Fact] + public void TestValidateForm_1() { + _wrapper.SetItem( new FormShareConfig { FormId = "formId" } ); + _wrapper.SetContextAttribute( UiConst.ValidateForm, true ); + _wrapper.SetContextAttribute( UiConst.OnClick, "test()" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否验证表单 - 指定按钮标识 + /// + [Fact] + public void TestValidateForm_2() { + _wrapper.SetItem( new FormShareConfig { FormId = "formId" } ); + _wrapper.SetContextAttribute( UiConst.ValidateForm, true ); + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + _wrapper.SetContextAttribute( UiConst.OnClick, "test()" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复制到剪贴板 + /// + [Fact] + public void TestCopyToClipboard() { + _wrapper.SetContextAttribute( UiConst.CopyToClipboard, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复制到剪贴板 - 多语言 + /// + [Fact] + public void TestCopyToClipboard_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.CopyToClipboard, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试全屏 + /// + [Fact] + public void TestFullscreen() { + _wrapper.SetContextAttribute( UiConst.Fullscreen, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试全屏 - 多语言 + /// + [Fact] + public void TestFullscreen_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Fullscreen, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试全屏外层容器样式类名 + /// + [Fact] + public void TestFullscreenWrapClass() { + _wrapper.SetContextAttribute( UiConst.Fullscreen, "a" ); + _wrapper.SetContextAttribute( UiConst.FullscreenWrapClass, "b" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试全屏包装 + /// + [Fact] + public void TestFullscreenPack() { + _wrapper.SetContextAttribute( UiConst.Fullscreen, "a" ); + _wrapper.SetContextAttribute( UiConst.FullscreenWrapClass, "b" ); + _wrapper.SetContextAttribute( UiConst.FullscreenPack, false ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试全屏包装 - 不设置外层容器样式类名 + /// + [Fact] + public void TestFullscreenPack_2() { + _wrapper.SetContextAttribute( UiConst.Fullscreen, "a" ); + _wrapper.SetContextAttribute( UiConst.FullscreenPack, false ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试全屏标题 + /// + [Fact] + public void TestFullscreenTitle() { + _wrapper.SetContextAttribute( UiConst.Fullscreen, "a" ); + _wrapper.SetContextAttribute( UiConst.FullscreenWrapClass, "b" ); + _wrapper.SetContextAttribute( UiConst.FullscreenPack, false ); + _wrapper.SetContextAttribute( UiConst.FullscreenTitle, "c" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试全屏标题 - 全屏包装未设置 + /// + [Fact] + public void TestFullscreenTitle_2() { + _wrapper.SetContextAttribute( UiConst.Fullscreen, "a" ); + _wrapper.SetContextAttribute( UiConst.FullscreenWrapClass, "b" ); + _wrapper.SetContextAttribute( UiConst.FullscreenTitle, "c" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试全屏标题 - 全屏包装和外层容器样式类名未设置 + /// + [Fact] + public void TestFullscreenTitle_3() { + _wrapper.SetContextAttribute( UiConst.Fullscreen, "a" ); + _wrapper.SetContextAttribute( UiConst.FullscreenTitle, "c" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试全屏 + /// + [Fact] + public void TestFullscreen_LinkType() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ); + _wrapper.SetContextAttribute( UiConst.Fullscreen, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试全屏包装 + /// + [Fact] + public void TestFullscreenPack_LinkType() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ); + _wrapper.SetContextAttribute( UiConst.Fullscreen, "a" ); + _wrapper.SetContextAttribute( UiConst.FullscreenPack, false ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试测试全屏标题 + /// + [Fact] + public void TestFullscreenTitle_LinkType() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ); + _wrapper.SetContextAttribute( UiConst.Fullscreen, "a" ); + _wrapper.SetContextAttribute( UiConst.FullscreenTitle, "b" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Form.cs b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Form.cs new file mode 100644 index 000000000..f299eb6bf --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Form.cs @@ -0,0 +1,45 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Buttons { + /// + /// 按钮测试 - 表单相关 + /// + public partial class ButtonTagHelperTest { + /// + /// 测试提交按钮类型 + /// + [Fact] + public void TestIsSubmit() { + _wrapper.SetContextAttribute( UiConst.IsSubmit, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提交按钮类型 - 当按钮放进表单中 + /// + [Fact] + public void TestIsSubmit_2() { + _wrapper.SetItem( new FormShareConfig() ); + _wrapper.SetContextAttribute( UiConst.IsSubmit, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试按钮类型 - 当按钮放进表单中自动设置按钮类型 + /// + [Fact] + public void TestButtonType_1() { + _wrapper.SetItem( new FormShareConfig() ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.I18n.cs b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.I18n.cs new file mode 100644 index 000000000..3f30cd6c6 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.I18n.cs @@ -0,0 +1,701 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Buttons { + /// + /// 按钮测试 - 多语言相关 + /// + public partial class ButtonTagHelperTest { + /// + /// 测试文本 + /// + [Fact] + public void TestText() { + _wrapper.SetContextAttribute( UiConst.Text, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本 - 支持多语言 + /// + [Fact] + public void TestText_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Text, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Ok文本 + /// + [Fact] + public void TestTextOk() { + _wrapper.SetContextAttribute( UiConst.TextOk, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Ok文本 - 支持多语言 + /// + [Fact] + public void TestTextOk_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextOk, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Cancel文本 + /// + [Fact] + public void TestTextCancel() { + _wrapper.SetContextAttribute( UiConst.TextCancel, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Cancel文本 - 支持多语言 + /// + [Fact] + public void TestTextCancel_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextCancel, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Create文本 + /// + [Fact] + public void TestTextCreate() { + _wrapper.SetContextAttribute( UiConst.TextCreate, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Create文本 - 支持多语言 + /// + [Fact] + public void TestTextCreate_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextCreate, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Update文本 + /// + [Fact] + public void TestTextUpdate() { + _wrapper.SetContextAttribute( UiConst.TextUpdate, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Update文本 - 支持多语言 + /// + [Fact] + public void TestTextUpdate_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextUpdate, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Delete文本 + /// + [Fact] + public void TestTextDelete() { + _wrapper.SetContextAttribute( UiConst.TextDelete, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Delete文本 - 支持多语言 + /// + [Fact] + public void TestTextDelete_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextDelete, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Detail文本 + /// + [Fact] + public void TestTextDetail() { + _wrapper.SetContextAttribute( UiConst.TextDetail, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Detail文本 - 支持多语言 + /// + [Fact] + public void TestTextDetail_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextDetail, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Query文本 + /// + [Fact] + public void TestTextQuery() { + _wrapper.SetContextAttribute( UiConst.TextQuery, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Query文本 - 支持多语言 + /// + [Fact] + public void TestTextQuery_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextQuery, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Refresh文本 + /// + [Fact] + public void TestTextRefresh() { + _wrapper.SetContextAttribute( UiConst.TextRefresh, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Refresh文本 - 支持多语言 + /// + [Fact] + public void TestTextRefresh_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextRefresh, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Save文本 + /// + [Fact] + public void TestTextSave() { + _wrapper.SetContextAttribute( UiConst.TextSave, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Save文本 - 支持多语言 + /// + [Fact] + public void TestTextSave_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextSave, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Enable文本 + /// + [Fact] + public void TestTextEnable() { + _wrapper.SetContextAttribute( UiConst.TextEnable, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Enable文本 - 支持多语言 + /// + [Fact] + public void TestTextEnable_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextEnable, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Disable文本 + /// + [Fact] + public void TestTextDisable() { + _wrapper.SetContextAttribute( UiConst.TextDisable, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Disable文本 - 支持多语言 + /// + [Fact] + public void TestTextDisable_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextDisable, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Select All文本 + /// + [Fact] + public void TestTextSelectAll() { + _wrapper.SetContextAttribute( UiConst.TextSelectAll, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Select All文本 - 支持多语言 + /// + [Fact] + public void TestTextSelectAll_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextSelectAll, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Deselect All文本 + /// + [Fact] + public void TestTextDeselectAll() { + _wrapper.SetContextAttribute( UiConst.TextDeselectAll, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Deselect All文本 - 支持多语言 + /// + [Fact] + public void TestTextDeselectAll_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextDeselectAll, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Upload文本 + /// + [Fact] + public void TestTextUpload() { + _wrapper.SetContextAttribute( UiConst.TextUpload, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Upload文本 - 支持多语言 + /// + [Fact] + public void TestTextUpload_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextUpload, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Download文本 + /// + [Fact] + public void TestTextDownload() { + _wrapper.SetContextAttribute( UiConst.TextDownload, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Download文本 - 支持多语言 + /// + [Fact] + public void TestTextDownload_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextDownload, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Publish文本 + /// + [Fact] + public void TestTextPublish() { + _wrapper.SetContextAttribute( UiConst.TextPublish, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Publish文本 - 支持多语言 + /// + [Fact] + public void TestTextPublish_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextPublish, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Run文本 + /// + [Fact] + public void TestTextRun() { + _wrapper.SetContextAttribute( UiConst.TextRun, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Run文本 - 支持多语言 + /// + [Fact] + public void TestTextRun_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextRun, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Start文本 + /// + [Fact] + public void TestTextStart() { + _wrapper.SetContextAttribute( UiConst.TextStart, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Start文本 - 支持多语言 + /// + [Fact] + public void TestTextStart_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextStart, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Stop文本 + /// + [Fact] + public void TestTextStop() { + _wrapper.SetContextAttribute( UiConst.TextStop, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Stop文本 - 支持多语言 + /// + [Fact] + public void TestTextStop_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextStop, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Add文本 + /// + [Fact] + public void TestTextAdd() { + _wrapper.SetContextAttribute( UiConst.TextAdd, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Add文本 - 支持多语言 + /// + [Fact] + public void TestTextAdd_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextAdd, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Remove文本 + /// + [Fact] + public void TestTextRemove() { + _wrapper.SetContextAttribute( UiConst.TextRemove, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Remove文本 - 支持多语言 + /// + [Fact] + public void TestTextRemove_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextRemove, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Open文本 + /// + [Fact] + public void TestTextOpen() { + _wrapper.SetContextAttribute( UiConst.TextOpen, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Open文本 - 支持多语言 + /// + [Fact] + public void TestTextOpen_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextOpen, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Close文本 + /// + [Fact] + public void TestTextClose() { + _wrapper.SetContextAttribute( UiConst.TextClose, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Close文本 - 支持多语言 + /// + [Fact] + public void TestTextClose_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextClose, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Send文本 + /// + [Fact] + public void TestTextSend() { + _wrapper.SetContextAttribute( UiConst.TextSend, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Send文本 - 支持多语言 + /// + [Fact] + public void TestTextSend_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextSend, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Clear文本 + /// + [Fact] + public void TestTextClear() { + _wrapper.SetContextAttribute( UiConst.TextClear, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Clear文本 - 支持多语言 + /// + [Fact] + public void TestTextClear_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextClear, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Import文本 + /// + [Fact] + public void TestTextImport() { + _wrapper.SetContextAttribute( UiConst.TextImport, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Import文本 - 支持多语言 + /// + [Fact] + public void TestTextImport_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextImport, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Export文本 + /// + [Fact] + public void TestTextExport() { + _wrapper.SetContextAttribute( UiConst.TextExport, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Export文本 - 支持多语言 + /// + [Fact] + public void TestTextExport_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextExport, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Reset文本 + /// + [Fact] + public void TestTextReset() { + _wrapper.SetContextAttribute( UiConst.TextReset, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Reset文本 - 支持多语言 + /// + [Fact] + public void TestTextReset_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextReset, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Unedit文本 + /// + [Fact] + public void TestTextUnedit() { + _wrapper.SetContextAttribute( UiConst.TextUnedit, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Unedit文本 - 支持多语言 + /// + [Fact] + public void TestTextUnedit_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextUnedit, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Link.cs b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Link.cs new file mode 100644 index 000000000..e57eee426 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Link.cs @@ -0,0 +1,228 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.Enums; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Enums; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Buttons { + /// + /// 按钮测试 - 链接相关 + /// + public partial class ButtonTagHelperTest { + /// + /// 测试链接按钮 + /// + [Fact] + public void TestLinkType() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试链接地址 + /// + [Fact] + public void TestHref() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ) + .SetContextAttribute( UiConst.Href, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试链接地址 + /// + [Fact] + public void TestBindHref() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ) + .SetContextAttribute( AngularConst.BindHref, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试链接打开目标 + /// + [Fact] + public void TestTarget() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ) + .SetContextAttribute( UiConst.Target, ATarget.Parent ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试链接打开目标 + /// + [Fact] + public void TestBindTarget() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ) + .SetContextAttribute( AngularConst.BindTarget, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试链接关系 + /// + [Fact] + public void TestRel() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ) + .SetContextAttribute( UiConst.Rel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试链接关系 + /// + [Fact] + public void TestBindRel() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ) + .SetContextAttribute( AngularConst.BindRel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试路由链接 + /// + [Fact] + public void TestRouterLink() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ) + .SetContextAttribute( AngularConst.RouterLink, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试路由链接 + /// + [Fact] + public void TestBindRouterLink() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ) + .SetContextAttribute( AngularConst.BindRouterLink, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试活动路由链接 + /// + [Fact] + public void TestRouterLinkActive() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ) + .SetContextAttribute( AngularConst.RouterLinkActive, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试活动路由链接 + /// + [Fact] + public void TestBindRouterLinkActive() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ) + .SetContextAttribute( AngularConst.BindRouterLinkActive, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试路由查询参数 + /// + [Fact] + public void TestQueryParams() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ) + .SetContextAttribute( UiConst.QueryParams, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试路由链接查询参数处理方式 + /// + [Fact] + public void TestQueryParamsHandling() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ) + .SetContextAttribute( UiConst.QueryParamsHandling, QueryParamsHandling.Merge ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试路由链接查询参数处理方式 + /// + [Fact] + public void TestBindQueryParamsHandling() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ) + .SetContextAttribute( AngularConst.BindQueryParamsHandling, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试查询表单链接 + /// + [Fact] + public void TestIsSearch() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ) + .SetContextAttribute( UiConst.IsSearch, true ); + var formShareConfig = new FormShareConfig { SearchFormShowNumber = 1 }; + formShareConfig.AddColumnId( "a" ); + formShareConfig.AddColumnId( "b" ); + formShareConfig.AddColumnId( "action" ); + _wrapper.SetItem( formShareConfig ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{expand?'收起':'展开'}}" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示表格设置 + /// + [Fact] + public void TestShowTableSettings() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ) + .SetContextAttribute( UiConst.ShowTableSettings, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选项卡路由联动 + /// + [Fact] + public void TestTabLink() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Link ) + .SetContextAttribute( UiConst.TabLink, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Popconfirm.cs b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Popconfirm.cs new file mode 100644 index 000000000..266c1e050 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Popconfirm.cs @@ -0,0 +1,399 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Buttons { + /// + /// 按钮测试 - 气泡确认框相关 + /// + public partial class ButtonTagHelperTest { + /// + /// 测试气泡确认框标题 + /// + [Fact] + public void TestPopconfirmTitle() { + _wrapper.SetContextAttribute( UiConst.PopconfirmTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡确认框标题 - 多语言 + /// + [Fact] + public void TestPopconfirmTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.PopconfirmTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡确认框标题 + /// + [Fact] + public void TestBindPopconfirmTitle() { + _wrapper.SetContextAttribute( AngularConst.BindPopconfirmTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡确认框触发行为 + /// + [Fact] + public void TestPopconfirmTrigger() { + _wrapper.SetContextAttribute( UiConst.PopconfirmTrigger, PopconfirmTrigger.Click ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡确认框触发行为 + /// + [Fact] + public void TestBindPopconfirmTrigger() { + _wrapper.SetContextAttribute( AngularConst.BindPopconfirmTrigger, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框位置 + /// + [Fact] + public void TestPopconfirmPlacement() { + _wrapper.SetContextAttribute( UiConst.PopconfirmPlacement, PopconfirmPlacement.BottomLeft ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框位置 + /// + [Fact] + public void TestBindPopconfirmPlacement() { + _wrapper.SetContextAttribute( AngularConst.BindPopconfirmPlacement, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框定位元素 + /// + [Fact] + public void TestPopconfirmOrigin() { + _wrapper.SetContextAttribute( UiConst.PopconfirmOrigin, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框是否可见 + /// + [Fact] + public void TestPopconfirmVisible() { + _wrapper.SetContextAttribute( UiConst.PopconfirmVisible, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框是否可见 + /// + [Fact] + public void TestBindonPopconfirmVisible() { + _wrapper.SetContextAttribute( AngularConst.BindonPopconfirmVisible, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框是否显示箭头 + /// + [Fact] + public void TestPopconfirmShowArrow() { + _wrapper.SetContextAttribute( UiConst.PopconfirmShowArrow, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试箭头是否指向锚点的中心 + /// + [Fact] + public void TestPopconfirmArrowPointAtCenter() { + _wrapper.SetContextAttribute( UiConst.PopconfirmArrowPointAtCenter, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框移入延时 + /// + [Fact] + public void TestPopconfirmMouseEnterDelay() { + _wrapper.SetContextAttribute( UiConst.PopconfirmMouseEnterDelay, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框移出延时 + /// + [Fact] + public void TestPopconfirmMouseLeaveDelay() { + _wrapper.SetContextAttribute( UiConst.PopconfirmMouseLeaveDelay, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框样式类名 + /// + [Fact] + public void TestPopconfirmOverlayClassName() { + _wrapper.SetContextAttribute( UiConst.PopconfirmOverlayClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框样式类名 + /// + [Fact] + public void TestBindPopconfirmOverlayClassName() { + _wrapper.SetContextAttribute( AngularConst.BindPopconfirmOverlayClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框样式 + /// + [Fact] + public void TestPopconfirmOverlayStyle() { + _wrapper.SetContextAttribute( UiConst.PopconfirmOverlayStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框浮层是否应带背景 + /// + [Fact] + public void TestPopconfirmBackdrop() { + _wrapper.SetContextAttribute( UiConst.PopconfirmBackdrop, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框取消按钮文字 + /// + [Fact] + public void TestPopconfirmCancelText() { + _wrapper.SetContextAttribute( UiConst.PopconfirmCancelText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框取消按钮文字 + /// + [Fact] + public void TestBindPopconfirmCancelText() { + _wrapper.SetContextAttribute( AngularConst.BindPopconfirmCancelText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框确认按钮文字 + /// + [Fact] + public void TestPopconfirmOkText() { + _wrapper.SetContextAttribute( UiConst.PopconfirmOkText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框确认按钮文字 + /// + [Fact] + public void TestBindPopconfirmOkText() { + _wrapper.SetContextAttribute( AngularConst.BindPopconfirmOkText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框确认按钮类型 + /// + [Fact] + public void TestPopconfirmOkType() { + _wrapper.SetContextAttribute( UiConst.PopconfirmOkType, ButtonType.Primary ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框确认按钮类型 + /// + [Fact] + public void TestBindPopconfirmOkType() { + _wrapper.SetContextAttribute( AngularConst.BindPopconfirmOkType, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框确认按钮是否为危险按钮 + /// + [Fact] + public void TestPopconfirmOkDanger() { + _wrapper.SetContextAttribute( UiConst.PopconfirmOkDanger, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框是否禁用确认按钮 + /// + [Fact] + public void TestPopconfirmOkDisabled() { + _wrapper.SetContextAttribute( UiConst.PopconfirmOkDisabled, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡确认框按钮自动聚焦 + /// + [Fact] + public void TestPopconfirmAutoFocus() { + _wrapper.SetContextAttribute( UiConst.PopconfirmAutoFocus, PopconfirmAutoFocus.Cancel ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡确认框按钮自动聚焦 + /// + [Fact] + public void TestBindPopconfirmAutoFocus() { + _wrapper.SetContextAttribute( AngularConst.BindPopconfirmAutoFocus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框条件触发 + /// + [Fact] + public void TestPopconfirmCondition() { + _wrapper.SetContextAttribute( UiConst.PopconfirmCondition, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框自定义图标 + /// + [Fact] + public void TestPopconfirmIcon() { + _wrapper.SetContextAttribute( UiConst.PopconfirmIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框自定义图标 + /// + [Fact] + public void TestBindPopconfirmIcon() { + _wrapper.SetContextAttribute( AngularConst.BindPopconfirmIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试确认前操作钩子 + /// + [Fact] + public void TestPopconfirmBeforeConfirm() { + _wrapper.SetContextAttribute( UiConst.PopconfirmBeforeConfirm, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框显示状态变化事件 + /// + [Fact] + public void TestOnPopconfirmVisibleChange() { + _wrapper.SetContextAttribute( UiConst.OnPopconfirmVisibleChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框取消事件 + /// + [Fact] + public void TestOnPopconfirmCancel() { + _wrapper.SetContextAttribute( UiConst.OnPopconfirmCancel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡框确认事件 + /// + [Fact] + public void TestOnPopconfirmConfirm() { + _wrapper.SetContextAttribute( UiConst.OnPopconfirmConfirm, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Popover.cs b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Popover.cs new file mode 100644 index 000000000..1f4c5e797 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Popover.cs @@ -0,0 +1,234 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Buttons { + /// + /// 按钮测试 - 气泡卡片相关 + /// + public partial class ButtonTagHelperTest { + /// + /// 测试气泡卡片标题 + /// + [Fact] + public void TestPopoverTitle() { + _wrapper.SetContextAttribute( UiConst.PopoverTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡卡片标题 - 多语言 + /// + [Fact] + public void TestPopoverTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.PopoverTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡卡片标题 + /// + [Fact] + public void TestBindPopoverTitle() { + _wrapper.SetContextAttribute( AngularConst.BindPopoverTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡卡片内容 + /// + [Fact] + public void TestPopoverContent() { + _wrapper.SetContextAttribute( UiConst.PopoverContent, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡卡片内容 + /// + [Fact] + public void TestBindPopoverContent() { + _wrapper.SetContextAttribute( AngularConst.BindPopoverContent, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡卡片触发行为 + /// + [Fact] + public void TestPopoverTrigger() { + _wrapper.SetContextAttribute( UiConst.PopoverTrigger, PopoverTrigger.Focus ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡卡片触发行为 + /// + [Fact] + public void TestBindPopoverTrigger() { + _wrapper.SetContextAttribute( AngularConst.BindPopoverTrigger, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡卡片位置 + /// + [Fact] + public void TestPopoverPlacement() { + _wrapper.SetContextAttribute( UiConst.PopoverPlacement, PopoverPlacement.BottomLeft ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡卡片位置 + /// + [Fact] + public void TestBindPopoverPlacement() { + _wrapper.SetContextAttribute( AngularConst.BindPopoverPlacement, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试箭头是否指向锚点的中心 + /// + [Fact] + public void TestPopoverArrowPointAtCenter() { + _wrapper.SetContextAttribute( UiConst.PopoverArrowPointAtCenter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡卡片定位元素 + /// + [Fact] + public void TestPopoverOrigin() { + _wrapper.SetContextAttribute( UiConst.PopoverOrigin, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示气泡卡片 + /// + [Fact] + public void TestPopoverVisible() { + _wrapper.SetContextAttribute( UiConst.PopoverVisible, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示气泡卡片 + /// + [Fact] + public void TestBindonPopoverVisible() { + _wrapper.SetContextAttribute( AngularConst.BindonPopoverVisible, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡卡片移入延时 + /// + [Fact] + public void TestPopoverMouseEnterDelay() { + _wrapper.SetContextAttribute( UiConst.PopoverMouseEnterDelay, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡卡片移出延时 + /// + [Fact] + public void TestPopoverMouseLeaveDelay() { + _wrapper.SetContextAttribute( UiConst.PopoverMouseLeaveDelay, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡卡片样式类名 + /// + [Fact] + public void TestPopoverOverlayClassName() { + _wrapper.SetContextAttribute( UiConst.PopoverOverlayClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡卡片样式类名 + /// + [Fact] + public void TestBindPopoverOverlayClassName() { + _wrapper.SetContextAttribute( AngularConst.BindPopoverOverlayClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡卡片样式 + /// + [Fact] + public void TestPopoverOverlayStyle() { + _wrapper.SetContextAttribute( UiConst.PopoverOverlayStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡卡片浮层是否带背景 + /// + [Fact] + public void TestPopoverBackdrop() { + _wrapper.SetContextAttribute( UiConst.PopoverBackdrop, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡卡片显示状态变化事件 + /// + [Fact] + public void TestOnPopoverVisibleChange() { + _wrapper.SetContextAttribute( UiConst.OnPopoverVisibleChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Space.cs b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Space.cs new file mode 100644 index 000000000..6f45df500 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Space.cs @@ -0,0 +1,32 @@ +using System.Text; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Buttons { + /// + /// 按钮测试 - 间距相关 + /// + public partial class ButtonTagHelperTest { + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 - 设置为false不设置*nzSpaceItem + /// + [Fact] + public void TestSpaceItem_False() { + _wrapper.SetContextAttribute( UiConst.SpaceItem, false ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Tooltip.cs b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Tooltip.cs new file mode 100644 index 000000000..03cad7a01 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.Tooltip.cs @@ -0,0 +1,314 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Buttons { + /// + /// 按钮测试 - 文字提示相关 + /// + public partial class ButtonTagHelperTest { + /// + /// 测试提示文字 + /// + [Fact] + public void TestTooltipTitle() { + _wrapper.SetContextAttribute( UiConst.TooltipTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示文字 - 多语言 + /// + [Fact] + public void TestTooltipTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TooltipTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示文字 + /// + [Fact] + public void TestBindTooltipTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTooltipTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示文字显示update + /// + [Fact] + public void TestTooltipTitleUpdate() { + _wrapper.SetContextAttribute( UiConst.TooltipTitleUpdate, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示文字显示update - 多语言 + /// + [Fact] + public void TestTooltipTitleUpdate_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TooltipTitleUpdate, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示文字显示delete + /// + [Fact] + public void TestTooltipTitleDelete() { + _wrapper.SetContextAttribute( UiConst.TooltipTitleDelete, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示文字显示update - 多语言 + /// + [Fact] + public void TestTooltipTitleDelete_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TooltipTitleDelete, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示文字显示detail + /// + [Fact] + public void TestTooltipTitleDetail() { + _wrapper.SetContextAttribute( UiConst.TooltipTitleDetail, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示文字显示detail - 多语言 + /// + [Fact] + public void TestTooltipTitleDetail_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TooltipTitleDetail, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示框位置 + /// + [Fact] + public void TestTooltipPlacement() { + _wrapper.SetContextAttribute( UiConst.TooltipPlacement, TooltipPlacement.BottomLeft ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示框位置 + /// + [Fact] + public void TestBindTooltipPlacement() { + _wrapper.SetContextAttribute( AngularConst.BindTooltipPlacement, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试箭头是否指向锚点的中心 + /// + [Fact] + public void TestTooltipArrowPointAtCenter() { + _wrapper.SetContextAttribute( UiConst.TooltipArrowPointAtCenter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示文字模板上下文 + /// + [Fact] + public void TestTooltipTitleContext() { + _wrapper.SetContextAttribute( UiConst.TooltipTitleContext, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文字提示触发行为 + /// + [Fact] + public void TestTooltipTrigger() { + _wrapper.SetContextAttribute( UiConst.TooltipTrigger, TooltipTrigger.Click ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文字提示触发行为 + /// + [Fact] + public void TestBindTooltipTrigger() { + _wrapper.SetContextAttribute( AngularConst.BindTooltipTrigger, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文字提示背景颜色 + /// + [Fact] + public void TestTooltipColorType() { + _wrapper.SetContextAttribute( UiConst.TooltipColorType, AntDesignColor.Blue ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文字提示背景颜色 + /// + [Fact] + public void TestTooltipColor() { + _wrapper.SetContextAttribute( UiConst.TooltipColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文字提示背景颜色 + /// + [Fact] + public void TestBindTooltipColor() { + _wrapper.SetContextAttribute( AngularConst.BindTooltipColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文字提示定位元素 + /// + [Fact] + public void TestTooltipOrigin() { + _wrapper.SetContextAttribute( UiConst.TooltipOrigin, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示框是否可见 + /// + [Fact] + public void TestTooltipVisible() { + _wrapper.SetContextAttribute( UiConst.TooltipVisible, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示框是否可见 + /// + [Fact] + public void TestBindonTooltipVisible() { + _wrapper.SetContextAttribute( AngularConst.BindonTooltipVisible, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示框移入延时 + /// + [Fact] + public void TestTooltipMouseEnterDelay() { + _wrapper.SetContextAttribute( UiConst.TooltipMouseEnterDelay, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示框移出延时 + /// + [Fact] + public void TestTooltipMouseLeaveDelay() { + _wrapper.SetContextAttribute( UiConst.TooltipMouseLeaveDelay, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示框样式类名 + /// + [Fact] + public void TestTooltipOverlayClassName() { + _wrapper.SetContextAttribute( UiConst.TooltipOverlayClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示框样式类名 + /// + [Fact] + public void TestBindTooltipOverlayClassName() { + _wrapper.SetContextAttribute( AngularConst.BindTooltipOverlayClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示框样式 + /// + [Fact] + public void TestTooltipOverlayStyle() { + _wrapper.SetContextAttribute( UiConst.TooltipOverlayStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示框显示状态变化事件 + /// + [Fact] + public void TestOnTooltipVisibleChange() { + _wrapper.SetContextAttribute( UiConst.OnTooltipVisibleChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.cs new file mode 100644 index 000000000..18a52f862 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Buttons/ButtonTagHelperTest.cs @@ -0,0 +1,237 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Buttons; +using Util.Ui.NgZorro.Components.Icons; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Buttons; + +/// +/// 按钮测试 +/// +public partial class ButtonTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ButtonTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ButtonTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标识 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试按钮类型 + /// + [Fact] + public void TestType() { + _wrapper.SetContextAttribute( UiConst.Type, ButtonType.Primary ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试按钮类型 + /// + [Fact] + public void TestBindType() { + _wrapper.SetContextAttribute( AngularConst.BindType, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试按钮尺寸 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, ButtonSize.Small ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试按钮尺寸 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试按钮形状 + /// + [Fact] + public void TestShape() { + _wrapper.SetContextAttribute( UiConst.Shape, ButtonShape.Circle ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试按钮形状 + /// + [Fact] + public void TestBindShape() { + _wrapper.SetContextAttribute( AngularConst.BindShape, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试危险按钮 + /// + [Fact] + public void TestDanger() { + _wrapper.SetContextAttribute( UiConst.Danger, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载状态 + /// + [Fact] + public void TestLoading() { + _wrapper.SetContextAttribute( UiConst.Loading, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试幽灵按钮 + /// + [Fact] + public void TestGhost() { + _wrapper.SetContextAttribute( UiConst.Ghost, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Block按钮 + /// + [Fact] + public void TestBlock() { + _wrapper.SetContextAttribute( UiConst.Block, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置图标 + /// + [Fact] + public void TestIcon() { + _wrapper.SetContextAttribute( UiConst.Icon, AntDesignIcon.Check ); + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置图标 + /// + [Fact] + public void TestIcon_2() { + var icon = new IconTagHelper().ToWrapper(); + icon.SetContextAttribute( UiConst.Type, AntDesignIcon.Check ); + _wrapper.AppendContent( icon ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击事件 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Calendars/CalendarTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Calendars/CalendarTagHelperTest.cs new file mode 100644 index 000000000..9bcbf4616 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Calendars/CalendarTagHelperTest.cs @@ -0,0 +1,227 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Calendars; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Calendars { + /// + /// 日历测试 + /// + public class CalendarTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public CalendarTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new CalendarTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestNgModel() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestBindNgModel() { + _wrapper.SetContextAttribute( AngularConst.BindNgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示模式 + /// + [Fact] + public void TestMode() { + _wrapper.SetContextAttribute( UiConst.Mode, CalendarMode.Year ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示模式 + /// + [Fact] + public void TestBindMode() { + _wrapper.SetContextAttribute( AngularConst.BindMode, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示模式 + /// + [Fact] + public void TestBindonMode() { + _wrapper.SetContextAttribute( AngularConst.BindonMode, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否全屏显示 + /// + [Fact] + public void TestFullscreen() { + _wrapper.SetContextAttribute( UiConst.Fullscreen, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义渲染日期单元格模板,模版内容会被追加到单元格 + /// + [Fact] + public void TestDateCell() { + _wrapper.SetContextAttribute( UiConst.DateCell, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义渲染日期单元格模板,模版内容覆盖单元格 + /// + [Fact] + public void TestDateFullCell() { + _wrapper.SetContextAttribute( UiConst.DateFullCell, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义渲染月单元格模板,模版内容会被追加到单元格 + /// + [Fact] + public void TestMonthCell() { + _wrapper.SetContextAttribute( UiConst.MonthCell, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义渲染月单元格模板,模版内容覆盖单元格 + /// + [Fact] + public void TestMonthFullCell() { + _wrapper.SetContextAttribute( UiConst.MonthFullCell, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试不可选择日期函数 + /// + [Fact] + public void TestDisabledDate() { + _wrapper.SetContextAttribute( UiConst.DisabledDate, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义头部内容 + /// + [Fact] + public void TestCustomHeader() { + _wrapper.SetContextAttribute( UiConst.CustomHeader, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义头部内容 + /// + [Fact] + public void TestBindCustomHeader() { + _wrapper.SetContextAttribute( AngularConst.BindCustomHeader, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试面板变化事件 + /// + [Fact] + public void TestOnPanelChange() { + _wrapper.SetContextAttribute( UiConst.OnPanelChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选择日期变化事件 + /// + [Fact] + public void TestOnSelectChange() { + _wrapper.SetContextAttribute( UiConst.OnSelectChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Cards/CardGridTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Cards/CardGridTagHelperTest.cs new file mode 100644 index 000000000..459b8da21 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Cards/CardGridTagHelperTest.cs @@ -0,0 +1,82 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Cards; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Cards { + /// + /// 网格内嵌卡片测试 + /// + public class CardGridTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public CardGridTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new CardGridTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试鼠标滑过时是否可浮起 + /// + [Fact] + public void TestHoverable() { + _wrapper.SetContextAttribute( UiConst.Hoverable, "true" ); + var result = new StringBuilder(); + result.Append( "
" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "
a
" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试鼠标滑过时是否可浮起 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "
" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Cards/CardMetaTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Cards/CardMetaTagHelperTest.cs new file mode 100644 index 000000000..f82988104 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Cards/CardMetaTagHelperTest.cs @@ -0,0 +1,129 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Cards; +using Util.Ui.NgZorro.Configs; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Cards { + /// + /// 卡片元信息测试 + /// + public class CardMetaTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public CardMetaTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new CardMetaTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 - 支持多语言 + /// + [Fact] + public void TestTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试头像 + /// + [Fact] + public void TestAvatar() { + _wrapper.SetContextAttribute( UiConst.Avatar, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试描述 + /// + [Fact] + public void TestDescription() { + _wrapper.SetContextAttribute( UiConst.Description, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试描述 + /// + [Fact] + public void TestBindDescription() { + _wrapper.SetContextAttribute( AngularConst.BindDescription, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Cards/CardTabTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Cards/CardTabTagHelperTest.cs new file mode 100644 index 000000000..9e4504706 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Cards/CardTabTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Cards; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Cards { + /// + /// 卡片标签页测试 + /// + public class CardTabTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public CardTabTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new CardTabTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Cards/CardTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Cards/CardTagHelperTest.cs new file mode 100644 index 000000000..587195a16 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Cards/CardTagHelperTest.cs @@ -0,0 +1,251 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Cards; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Cards { + /// + /// 卡片测试 + /// + public class CardTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public CardTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new CardTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 - 支持多语言 + /// + [Fact] + public void TestTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试操作组 + /// + [Fact] + public void TestActions() { + _wrapper.SetContextAttribute( UiConst.Actions, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容区域自定义样式 + /// + [Fact] + public void TestBodyStyle() { + _wrapper.SetContextAttribute( UiConst.BodyStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示边框 + /// + [Fact] + public void TestBordered() { + _wrapper.SetContextAttribute( UiConst.Bordered, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试封面 + /// + [Fact] + public void TestCover() { + _wrapper.SetContextAttribute( UiConst.Cover, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试右上角操作区域 + /// + [Fact] + public void TestExtra() { + _wrapper.SetContextAttribute( UiConst.Extra, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试鼠标滑过时是否可浮起 + /// + [Fact] + public void TestHoverable() { + _wrapper.SetContextAttribute( UiConst.Hoverable, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否加载状态 + /// + [Fact] + public void TestLoading() { + _wrapper.SetContextAttribute( UiConst.Loading, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试卡片类型 + /// + [Fact] + public void TestType() { + _wrapper.SetContextAttribute( UiConst.Type, CardType.Inner ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试卡片类型 - default不设置nzType属性 + /// + [Fact] + public void TestType_Default() { + _wrapper.SetContextAttribute( UiConst.Type, CardType.Default ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试卡片类型 + /// + [Fact] + public void TestBindType() { + _wrapper.SetContextAttribute( AngularConst.BindType, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试卡片大小 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, CardSize.Small ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试卡片大小 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击事件 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Carousels/CarouselContentTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Carousels/CarouselContentTagHelperTest.cs new file mode 100644 index 000000000..575ced29d --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Carousels/CarouselContentTagHelperTest.cs @@ -0,0 +1,48 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Carousels; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Carousels { + /// + /// 走马灯内容区域测试 + /// + public class CarouselContentTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public CarouselContentTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new CarouselContentTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Carousels/CarouselTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Carousels/CarouselTagHelperTest.cs new file mode 100644 index 000000000..5ff6353d3 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Carousels/CarouselTagHelperTest.cs @@ -0,0 +1,194 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Carousels; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Carousels { + /// + /// 走马灯测试 + /// + public class CarouselTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public CarouselTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new CarouselTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否自动切换 + /// + [Fact] + public void TestAutoPlay() { + _wrapper.SetContextAttribute( UiConst.AutoPlay, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试切换时间 + /// + [Fact] + public void TestAutoPlaySpeed() { + _wrapper.SetContextAttribute( UiConst.AutoPlaySpeed, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试指示点渲染模板 + /// + [Fact] + public void TestDotRender() { + _wrapper.SetContextAttribute( UiConst.DotRender, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试指示点位置 + /// + [Fact] + public void TestDotPosition() { + _wrapper.SetContextAttribute( UiConst.DotPosition, CarouselDotPosition.Left ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试指示点位置 + /// + [Fact] + public void TestBindDotPosition() { + _wrapper.SetContextAttribute( AngularConst.BindDotPosition, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示指示点 + /// + [Fact] + public void TestDots() { + _wrapper.SetContextAttribute( UiConst.Dots, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试动画效果 + /// + [Fact] + public void TestEffect() { + _wrapper.SetContextAttribute( UiConst.Effect, CarouselEffect.Fade ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试动画效果 + /// + [Fact] + public void TestBindEffect() { + _wrapper.SetContextAttribute( AngularConst.BindEffect, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否支持手势划动切换 + /// + [Fact] + public void TestEnableSwipe() { + _wrapper.SetContextAttribute( UiConst.EnableSwipe, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试循环 + /// + [Fact] + public void TestLoop() { + _wrapper.SetContextAttribute( UiConst.Loop, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试切换面板事件 + /// + [Fact] + public void TestOnAfterChange() { + _wrapper.SetContextAttribute( UiConst.OnAfterChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试切换面板前事件 + /// + [Fact] + public void TestOnBeforeChange() { + _wrapper.SetContextAttribute( UiConst.OnBeforeChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Cascaders/CascaderTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Cascaders/CascaderTagHelperTest.cs new file mode 100644 index 000000000..a8066dc6f --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Cascaders/CascaderTagHelperTest.cs @@ -0,0 +1,601 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Cascaders; +using Util.Ui.NgZorro.Components.Forms; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Cascaders { + /// + /// 级联选择测试 + /// + public class CascaderTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public CascaderTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new CascaderTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试引用变量 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试名称 + /// + [Fact] + public void TestName() { + _wrapper.SetContextAttribute( UiConst.Name, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试允许清除 + /// + [Fact] + public void TestAllowClear() { + _wrapper.SetContextAttribute( UiConst.AllowClear, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动聚焦 + /// + [Fact] + public void TestAutoFocus() { + _wrapper.SetContextAttribute( UiConst.AutoFocus, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试变更函数 + /// + [Fact] + public void TestChangeOn() { + _wrapper.SetContextAttribute( UiConst.ChangeOn, "Ab" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选择变更 + /// + [Fact] + public void TestChangeOnSelect() { + _wrapper.SetContextAttribute( UiConst.ChangeOnSelect, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列类名 + /// + [Fact] + public void TestColumnClassName() { + _wrapper.SetContextAttribute( UiConst.ColumnClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列类名 + /// + [Fact] + public void TestBindColumnClassName() { + _wrapper.SetContextAttribute( AngularConst.BindColumnClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试展开图标 + /// + [Fact] + public void TestExpandIcon() { + _wrapper.SetContextAttribute( UiConst.ExpandIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试展开图标 + /// + [Fact] + public void TestBindExpandIcon() { + _wrapper.SetContextAttribute( AngularConst.BindExpandIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试展开方式 + /// + [Fact] + public void TestExpandTrigger() { + _wrapper.SetContextAttribute( UiConst.ExpandTrigger, CascaderExpandTrigger.Hover ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试展开方式 + /// + [Fact] + public void TestBindExpandTrigger() { + _wrapper.SetContextAttribute( AngularConst.BindExpandTrigger, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试触发操作 + /// + [Fact] + public void TestTriggerAction() { + _wrapper.SetContextAttribute( UiConst.TriggerAction, CascaderTriggerAction.Hover ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试触发操作 + /// + [Fact] + public void TestBindTriggerAction() { + _wrapper.SetContextAttribute( AngularConst.BindTriggerAction, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签属性名 + /// + [Fact] + public void TestLabelProperty() { + _wrapper.SetContextAttribute( UiConst.LabelProperty, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签属性名 + /// + [Fact] + public void TestBindLabelProperty() { + _wrapper.SetContextAttribute( AngularConst.BindLabelProperty, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签渲染模板 + /// + [Fact] + public void TestLabelRender() { + _wrapper.SetContextAttribute( UiConst.LabelRender, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试动态加载函数 + /// + [Fact] + public void TestLoadData() { + _wrapper.SetContextAttribute( UiConst.LoadData, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试浮层类名 + /// + [Fact] + public void TestMenuClassName() { + _wrapper.SetContextAttribute( UiConst.MenuClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试浮层类名 + /// + [Fact] + public void TestBindMenuClassName() { + _wrapper.SetContextAttribute( AngularConst.BindMenuClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试浮层样式 + /// + [Fact] + public void TestMenuStyle() { + _wrapper.SetContextAttribute( UiConst.MenuStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试空列表默认内容 + /// + [Fact] + public void TestNotFoundContent() { + _wrapper.SetContextAttribute( UiConst.NotFoundContent, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试空列表默认内容 + /// + [Fact] + public void TestBindNotFoundContent() { + _wrapper.SetContextAttribute( AngularConst.BindNotFoundContent, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选项渲染模板 + /// + [Fact] + public void TestOptionRender() { + _wrapper.SetContextAttribute( UiConst.OptionRender, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试可选项数据源 + /// + [Fact] + public void TestOptions() { + _wrapper.SetContextAttribute( UiConst.Options, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试占位提示 + /// + [Fact] + public void TestPlaceholder() { + _wrapper.SetContextAttribute( UiConst.Placeholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试占位提示 + /// + [Fact] + public void TestBindPlaceholder() { + _wrapper.SetContextAttribute( AngularConst.BindPlaceholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示箭头 + /// + [Fact] + public void TestShowArrow() { + _wrapper.SetContextAttribute( UiConst.ShowArrow, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示输入框 + /// + [Fact] + public void TestShowInput() { + _wrapper.SetContextAttribute( UiConst.ShowInput, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示搜索 + /// + [Fact] + public void TestShowSearch() { + _wrapper.SetContextAttribute( UiConst.ShowSearch, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入框大小 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, InputSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入框大小 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选择框后缀图标 + /// + [Fact] + public void TestSuffixIcon() { + _wrapper.SetContextAttribute( UiConst.SuffixIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选择框后缀图标 + /// + [Fact] + public void TestBindSuffixIcon() { + _wrapper.SetContextAttribute( AngularConst.BindSuffixIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值属性名 + /// + [Fact] + public void TestValueProperty() { + _wrapper.SetContextAttribute( UiConst.ValueProperty, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值属性名 + /// + [Fact] + public void TestBindValueProperty() { + _wrapper.SetContextAttribute( AngularConst.BindValueProperty, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestStatus() { + _wrapper.SetContextAttribute( UiConst.Status, FormControlStatus.Error ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestBindStatus() { + _wrapper.SetContextAttribute( AngularConst.BindStatus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试浮层是否应带有背景板 + /// + [Fact] + public void TestBackdrop() { + _wrapper.SetContextAttribute( UiConst.Backdrop, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型变更事件 + /// + [Fact] + public void TestOnModelChange() { + _wrapper.SetContextAttribute( UiConst.OnModelChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试清除值事件 + /// + [Fact] + public void TestOnClear() { + _wrapper.SetContextAttribute( UiConst.OnClear, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示状态改变事件 + /// + [Fact] + public void TestOnVisibleChange() { + _wrapper.SetContextAttribute( UiConst.OnVisibleChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选中状态改变事件 + /// + [Fact] + public void TestOnSelectionChange() { + _wrapper.SetContextAttribute( UiConst.OnSelectionChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动设置nzFor - 该组件尚未支持 + /// + [Fact] + public void TestAutoNzFor() { + var form = new FormTagHelper().ToWrapper(); + + var formItem = new FormItemTagHelper().ToWrapper(); + form.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + _wrapper.SetContextAttribute( UiConst.AutoNzFor, true ); + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( _wrapper ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
" ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxGroupTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxGroupTagHelperTest.cs new file mode 100644 index 000000000..fe5b9e85f --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxGroupTagHelperTest.cs @@ -0,0 +1,170 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Checkboxes; +using Util.Ui.NgZorro.Components.Forms; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Checkboxes { + /// + /// 复选框组合测试 + /// + public class CheckboxGroupTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public CheckboxGroupTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new CheckboxGroupTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试引用变量 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试名称 + /// + [Fact] + public void TestName() { + _wrapper.SetContextAttribute( UiConst.Name, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestNgModel() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestBindNgModel() { + _wrapper.SetContextAttribute( AngularConst.BindNgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型变更事件 + /// + [Fact] + public void TestOnModelChange() { + _wrapper.SetContextAttribute( UiConst.OnModelChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表单容器属性 + /// + [Fact] + public void TestFormContainer() { + var input = new CheckboxTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelText, "b" ); + _wrapper.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动设置nzFor - 不支持 + /// + [Fact] + public void TestAutoNzFor() { + var form = new FormTagHelper().ToWrapper(); + + var formItem = new FormItemTagHelper().ToWrapper(); + form.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + _wrapper.SetContextAttribute( UiConst.AutoNzFor, true ); + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( _wrapper ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
" ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxTagHelperTest.Expression.cs new file mode 100644 index 000000000..2faa7dee6 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxTagHelperTest.Expression.cs @@ -0,0 +1,25 @@ +using System.Text; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Checkboxes { + /// + /// 复选框测试 - 表达式解析测试 + /// + public partial class CheckboxTagHelperTest { + /// + /// 测试解析表达式属性for + /// + [Fact] + public void TestFor_1() { + _wrapper.SetExpression( t => t.Code ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "code" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxTagHelperTest.Form.cs b/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxTagHelperTest.Form.cs new file mode 100644 index 000000000..c81816757 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxTagHelperTest.Form.cs @@ -0,0 +1,58 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Forms; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Checkboxes { + /// + /// 复选框测试 - 表单结构相关 + /// + public partial class CheckboxTagHelperTest { + /// + /// 测试标签文本 + /// + [Fact] + public void TestLabelText() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动设置nzFor + /// + [Fact] + public void TestAutoNzFor() { + var form = new FormTagHelper().ToWrapper(); + + var formItem = new FormItemTagHelper().ToWrapper(); + form.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + _wrapper.SetContextAttribute( UiConst.AutoNzFor, true ); + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( _wrapper ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
" ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxTagHelperTest.cs new file mode 100644 index 000000000..2c4d71766 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxTagHelperTest.cs @@ -0,0 +1,299 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Checkboxes; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Checkboxes { + /// + /// 复选框测试 + /// + public partial class CheckboxTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public CheckboxTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new CheckboxTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加标识 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内部 id + /// + [Fact] + public void TestNzId() { + _wrapper.SetContextAttribute( UiConst.NzId, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内部 id + /// + [Fact] + public void TestBindNzId() { + _wrapper.SetContextAttribute( AngularConst.BindNzId, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置名称 + /// + [Fact] + public void TestName() { + _wrapper.SetContextAttribute( UiConst.Name, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置名称 + /// + [Fact] + public void TestBindName() { + _wrapper.SetContextAttribute( AngularConst.BindName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动获取焦点 + /// + [Fact] + public void TestAutoFocus() { + _wrapper.SetContextAttribute( UiConst.AutoFocus, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestNgModel() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置中间状态 + /// + [Fact] + public void TestIndeterminate() { + _wrapper.SetContextAttribute( UiConst.Indeterminate, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestValue() { + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestBindValue() { + _wrapper.SetContextAttribute( AngularConst.BindValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置标签 + /// + [Fact] + public void TestLabel() { + _wrapper.SetContextAttribute( UiConst.Label, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置标签 - 多语言 + /// + [Fact] + public void TestLabel_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Label, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置标签绑定 + /// + [Fact] + public void TestBindLabel() { + _wrapper.SetContextAttribute( AngularConst.BindLabel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 - 如果设置内容,则label属性将无效 + /// + [Fact] + public void TestContent_2() { + _wrapper.SetContextAttribute( UiConst.Label, "a" ); + _wrapper.AppendContent( "b" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件栅格跨度 + /// + [Fact] + public void TestControlSpan() { + _wrapper.SetContextAttribute( UiConst.ControlSpan, 2 ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem_1() { + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem_2() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选中 + /// + [Fact] + public void TestChecked() { + _wrapper.SetContextAttribute( UiConst.Checked, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选中状态变化事件 + /// + [Fact] + public void TestOnCheckedChange() { + _wrapper.SetContextAttribute( UiConst.OnCheckedChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxWrapperTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxWrapperTagHelperTest.cs new file mode 100644 index 000000000..3a18df049 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Checkboxes/CheckboxWrapperTagHelperTest.cs @@ -0,0 +1,103 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Checkboxes; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Checkboxes { + /// + /// 复选框包装器测试 + /// + public class CheckboxWrapperTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public CheckboxWrapperTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new CheckboxWrapperTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加标识 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试变更事件 + /// + [Fact] + public void TestOnChange() { + _wrapper.SetContextAttribute( UiConst.OnChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表单容器属性 + /// + [Fact] + public void TestFormContainer() { + var input = new CheckboxTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelText, "b" ); + _wrapper.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Collapses/CollapsePanelTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Collapses/CollapsePanelTagHelperTest.cs new file mode 100644 index 000000000..e01862c06 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Collapses/CollapsePanelTagHelperTest.cs @@ -0,0 +1,183 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Collapses; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Collapses { + /// + /// 折叠面板测试 + /// + public class CollapsePanelTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public CollapsePanelTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new CollapsePanelTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试面板头内容 + /// + [Fact] + public void TestHeader() { + _wrapper.SetContextAttribute( UiConst.Header, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试面板头内容 + /// + [Fact] + public void TestBindHeader() { + _wrapper.SetContextAttribute( AngularConst.BindHeader, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标 + /// + [Fact] + public void TestExpandedIcon() { + _wrapper.SetContextAttribute( UiConst.ExpandedIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标 + /// + [Fact] + public void TestBindExpandedIcon() { + _wrapper.SetContextAttribute( AngularConst.BindExpandedIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试右上角额外内容 + /// + [Fact] + public void TestExtra() { + _wrapper.SetContextAttribute( UiConst.Extra, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试右上角额外内容 + /// + [Fact] + public void TestBindExtra() { + _wrapper.SetContextAttribute( AngularConst.BindExtra, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示箭头 + /// + [Fact] + public void TestShowArrow() { + _wrapper.SetContextAttribute( UiConst.ShowArrow, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否展开面板 + /// + [Fact] + public void TestActive() { + _wrapper.SetContextAttribute( UiConst.Active, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否展开面板 + /// + [Fact] + public void TestBindonActive() { + _wrapper.SetContextAttribute( AngularConst.BindonActive, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试面板展开变化事件 + /// + [Fact] + public void TestOnActiveChange() { + _wrapper.SetContextAttribute( UiConst.OnActiveChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Collapses/CollapseTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Collapses/CollapseTagHelperTest.cs new file mode 100644 index 000000000..3c1d4582d --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Collapses/CollapseTagHelperTest.cs @@ -0,0 +1,117 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Collapses; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Collapses { + /// + /// 折叠测试 + /// + public class CollapseTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public CollapseTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new CollapseTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否手风琴 + /// + [Fact] + public void TestAccordion() { + _wrapper.SetContextAttribute( UiConst.Accordion, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否有边框 + /// + [Fact] + public void TestBordered() { + _wrapper.SetContextAttribute( UiConst.Bordered, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否幽灵面板 + /// + [Fact] + public void TestGhost() { + _wrapper.SetContextAttribute( UiConst.Ghost, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标位置 + /// + [Fact] + public void TestExpandIconPosition() { + _wrapper.SetContextAttribute( UiConst.ExpandIconPosition, CollapseIconPosition.Left ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标位置 + /// + [Fact] + public void TestBindExpandIconPosition() { + _wrapper.SetContextAttribute( AngularConst.BindExpandIconPosition, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/ColorPickers/ColorBlockTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/ColorPickers/ColorBlockTagHelperTest.cs new file mode 100644 index 000000000..e909725f6 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/ColorPickers/ColorBlockTagHelperTest.cs @@ -0,0 +1,117 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.ColorPickers; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.ColorPickers { + /// + /// 颜色块测试 + /// + public class ColorBlockTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ColorBlockTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ColorBlockTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色值 + /// + [Fact] + public void TestColor() { + _wrapper.SetContextAttribute( UiConst.Color, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色值 + /// + [Fact] + public void TestBindColor() { + _wrapper.SetContextAttribute( AngularConst.BindColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件尺寸 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, InputSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件尺寸 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试点击事件 + /// + [Fact] + public void TestOnChange() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/ColorPickers/ColorPickerTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/ColorPickers/ColorPickerTagHelperTest.Expression.cs new file mode 100644 index 000000000..200cce5a0 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/ColorPickers/ColorPickerTagHelperTest.Expression.cs @@ -0,0 +1,29 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.ColorPickers { + /// + /// 颜色选择测试 - 表达式解析测试 + /// + public partial class ColorPickerTagHelperTest { + /// + /// 测试解析表达式属性for + /// + [Fact] + public void TestFor_1() { + _wrapper.SetItem( new FormShareConfig { FormCreated = true } ); + _wrapper.SetExpression( t => t.Name ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "姓名" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/ColorPickers/ColorPickerTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/ColorPickers/ColorPickerTagHelperTest.cs new file mode 100644 index 000000000..e2aa5ab1b --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/ColorPickers/ColorPickerTagHelperTest.cs @@ -0,0 +1,352 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.ColorPickers; +using Util.Ui.NgZorro.Components.Forms; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.ColorPickers { + /// + /// 颜色选择测试 + /// + public partial class ColorPickerTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ColorPickerTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ColorPickerTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 - 支持多语言 + /// + [Fact] + public void TestTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色默认值 + /// + [Fact] + public void TestDefaultValue() { + _wrapper.SetContextAttribute( UiConst.DefaultValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色默认值 + /// + [Fact] + public void TestBindDefaultValue() { + _wrapper.SetContextAttribute( AngularConst.BindDefaultValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色值 + /// + [Fact] + public void TestValue() { + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色值 + /// + [Fact] + public void TestBindValue() { + _wrapper.SetContextAttribute( AngularConst.BindValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示颜色文本 + /// + [Fact] + public void TestShowText() { + _wrapper.SetContextAttribute( UiConst.ShowText, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件尺寸 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, InputSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件尺寸 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用透明度 + /// + [Fact] + public void TestDisabledAlpha() { + _wrapper.SetContextAttribute( UiConst.DisabledAlpha, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试触发模式 + /// + [Fact] + public void TestTrigger() { + _wrapper.SetContextAttribute( UiConst.Trigger, ColorPickerTrigger.Hover ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试触发模式 + /// + [Fact] + public void TestBindTrigger() { + _wrapper.SetContextAttribute( AngularConst.BindTrigger, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试允许清除 + /// + [Fact] + public void TestAllowClear() { + _wrapper.SetContextAttribute( UiConst.AllowClear, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色格式 + /// + [Fact] + public void TestFormat() { + _wrapper.SetContextAttribute( UiConst.Format, ColorPickerFormat.Hsb ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色格式 + /// + [Fact] + public void TestBindFormat() { + _wrapper.SetContextAttribute( AngularConst.BindFormat, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示弹出窗口 + /// + [Fact] + public void TestOpen() { + _wrapper.SetContextAttribute( UiConst.Open, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义触发元素 + /// + [Fact] + public void TestFlipFlop() { + _wrapper.SetContextAttribute( UiConst.FlipFlop, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色变化事件 + /// + [Fact] + public void TestOnChange() { + _wrapper.SetContextAttribute( UiConst.OnChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试清除事件 + /// + [Fact] + public void TestOnClear() { + _wrapper.SetContextAttribute( UiConst.OnClear, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色格式变化事件 + /// + [Fact] + public void TestOnFormatChange() { + _wrapper.SetContextAttribute( UiConst.OnFormatChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试打开颜色面板事件 + /// + [Fact] + public void TestOnOpenChange() { + _wrapper.SetContextAttribute( UiConst.OnOpenChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动设置nzFor - 不支持 + /// + [Fact] + public void TestAutoNzFor() { + var form = new FormTagHelper().ToWrapper(); + + var formItem = new FormItemTagHelper().ToWrapper(); + form.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + _wrapper.SetContextAttribute( UiConst.AutoNzFor, true ); + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( _wrapper ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
" ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Comments/CommentActionTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Comments/CommentActionTagHelperTest.cs new file mode 100644 index 000000000..203eac3cd --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Comments/CommentActionTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Comments; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Comments { + /// + /// 评论操作测试 + /// + public class CommentActionTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public CommentActionTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new CommentActionTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Comments/CommentContentTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Comments/CommentContentTagHelperTest.cs new file mode 100644 index 000000000..578f968af --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Comments/CommentContentTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Comments; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Comments { + /// + /// 评论内容测试 + /// + public class CommentContentTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public CommentContentTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new CommentContentTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Comments/CommentTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Comments/CommentTagHelperTest.cs new file mode 100644 index 000000000..9a3d543ed --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Comments/CommentTagHelperTest.cs @@ -0,0 +1,105 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Comments; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Comments { + /// + /// 评论测试 + /// + public class CommentTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public CommentTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new CommentTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试作者 + /// + [Fact] + public void TestAuthor() { + _wrapper.SetContextAttribute( UiConst.Author, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试作者 + /// + [Fact] + public void TestBindAuthor() { + _wrapper.SetContextAttribute( AngularConst.BindAuthor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试时间描述 + /// + [Fact] + public void TestDatetime() { + _wrapper.SetContextAttribute( UiConst.Datetime, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试时间描述 + /// + [Fact] + public void TestBindDatetime() { + _wrapper.SetContextAttribute( AngularConst.BindDatetime, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Containers/ContainerTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Containers/ContainerTagHelperTest.cs new file mode 100644 index 000000000..a72611103 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Containers/ContainerTagHelperTest.cs @@ -0,0 +1,226 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Containers; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Containers { + /// + /// ng-container容器测试 + /// + public class ContainerTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ContainerTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ContainerTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标识 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试原始标识 + /// + [Fact] + public void TestRawId() { + _wrapper.SetContextAttribute( AngularConst.RawId, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试样式 + /// + [Fact] + public void TestStyle() { + _wrapper.SetContextAttribute( UiConst.Style, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试样式类 + /// + [Fact] + public void TestClass() { + _wrapper.SetContextAttribute( UiConst.Class, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试隐藏 + /// + [Fact] + public void TestHidden() { + _wrapper.SetContextAttribute( UiConst.Hidden, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加其它未配置的属性 + /// + [Fact] + public void TestOutputAttributes() { + _wrapper.SetOutputAttribute( "a", "1" ).SetOutputAttribute( "b", "2" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试*ngFor + /// + [Fact] + public void TestNgFor() { + _wrapper.SetContextAttribute( AngularConst.NgFor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试*ngIf + /// + [Fact] + public void TestNgIf() { + _wrapper.SetContextAttribute( AngularConst.NgIf, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试ngSwitch + /// + [Fact] + public void TestNgSwitch() { + _wrapper.SetContextAttribute( AngularConst.NgSwitch, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试ngSwitchCase + /// + [Fact] + public void TestNgSwitchCase() { + _wrapper.SetContextAttribute( AngularConst.NgSwitchCase, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试*ngSwitchDefault + /// + [Fact] + public void TestNgSwitchDefault() { + _wrapper.SetContextAttribute( AngularConst.NgSwitchDefault, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试ngClass + /// + [Fact] + public void TestNgClass() { + _wrapper.SetContextAttribute( AngularConst.NgClass, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试ngStyle + /// + [Fact] + public void TestNgStyle() { + _wrapper.SetContextAttribute( AngularConst.NgStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提及建议 + /// + [Fact] + public void TestMentionSuggestion() { + _wrapper.SetContextAttribute( UiConst.MentionSuggestion, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试*ngTemplateOutlet + /// + [Fact] + public void TestNgTemplateOutlet() { + _wrapper.SetContextAttribute( AngularConst.NgTemplateOutlet, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Containers/RouterOutletTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Containers/RouterOutletTagHelperTest.cs new file mode 100644 index 000000000..5570bd70b --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Containers/RouterOutletTagHelperTest.cs @@ -0,0 +1,48 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Containers; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Containers { + /// + /// router-outlet测试 + /// + public class RouterOutletTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public RouterOutletTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new RouterOutletTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Data/TreeResultTest.cs b/test/Util.Ui.NgZorro.Tests/Data/TreeResultTest.cs new file mode 100644 index 000000000..6eaf93eb0 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Data/TreeResultTest.cs @@ -0,0 +1,350 @@ +using System.Collections.Generic; +using System.Linq; +using Util.Ui.NgZorro.Tests.Samples; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Data { + /// + /// 树形结果测试 + /// + public class TreeResultTest { + /// + /// 标识 + /// + public const string Id = "1"; + /// + /// 标识2 + /// + public const string Id2 = "2"; + /// + /// 标识3 + /// + public const string Id3 = "3"; + /// + /// 标识4 + /// + public const string Id4 = "4"; + /// + /// 标识5 + /// + public const string Id5 = "5"; + /// + /// 标题 + /// + public const string Title = "a"; + /// + /// 标题2 + /// + public const string Title2 = "b"; + /// + /// 标题3 + /// + public const string Title3 = "c"; + /// + /// 标题4 + /// + public const string Title4 = "d"; + /// + /// 标题5 + /// + public const string Title5 = "e"; + + /// + /// 获取节点1 + /// + private TreeDto GetNode1() { + return new TreeDto { + Id = Id, + Text = Title, + Level = 1, + Enabled = false, + Expanded = true, + Checked = true, + DisableCheckbox = true, + Selectable = false, + Selected = true + }; + } + + /// + /// 获取节点2 + /// + private TreeDto GetNode2() { + return new TreeDto { + Id = Id2, + ParentId = Id, + Text = Title2, + Level = 2, + Enabled = false, + Expanded = true, + Checked = true, + DisableCheckbox = true, + Selectable = false, + Selected = true + }; + } + + /// + /// 获取节点3 + /// + private TreeDto GetNode3() { + return new TreeDto { + Id = Id3, + Text = Title3, + Icon = "a", + Level = 1, + Enabled = false, + Expanded = true, + Checked = true, + DisableCheckbox = true, + Selectable = false, + Selected = true + }; + } + + /// + /// 获取节点4 + /// + private TreeDto GetNode4() { + return new TreeDto { + Id = Id4, + ParentId = Id, + Text = Title4, + Level = 2, + Enabled = false, + Expanded = true, + Checked = true, + DisableCheckbox = true, + Selectable = false, + Selected = true + }; + } + + /// + /// 获取节点5 + /// + private TreeDto GetNode5() { + return new TreeDto { + Id = Id5, + ParentId = Id4, + Text = Title5 + }; + } + + /// + /// 添加一个节点 + /// + [Fact] + public void TestGetResult_1() { + var list = new List { GetNode1() }; + var result = list.ToTreeResult(); + Assert.Single( result.Nodes ); + var node = result.Nodes.First(); + Assert.Equal( Id, node.Key ); + Assert.Equal( Title, node.Title ); + Assert.True( node.Disabled, "node.IsDisabled" ); + Assert.True( node.Expanded,"node.IsExpanded" ); + Assert.True( node.Checked,"node.IsChecked" ); + Assert.True( node.DisableCheckbox,"node.IsDisableCheckbox" ); + Assert.False( node.Selectable,"node.IsSelectable" ); + Assert.True( node.Selected,"node.IsSelected" ); + Assert.True( node.IsLeaf,"node.IsLeaf" ); + } + + /// + /// 添加一个子节点 + /// + [Fact] + public void TestGetResult_2() { + var list = new List { GetNode1(), GetNode2() }; + var result = list.ToTreeResult(); + + //根节点 + Assert.Single( result.Nodes ); + var root = result.Nodes.First(); + Assert.Equal( Id, root.Key ); + Assert.Equal( Title, root.Title ); + Assert.True( root.Disabled ); + Assert.True( root.Expanded ); + Assert.True( root.Checked ); + Assert.True( root.DisableCheckbox ); + Assert.False( root.Selectable ); + Assert.True( root.Selected ); + Assert.False( root.IsLeaf ); + + //子节点 + Assert.Single( root.Children ); + var child = root.Children[0]; + Assert.Equal( Id2, child.Key ); + Assert.Equal( Title2, child.Title ); + Assert.True( child.Disabled ); + Assert.True( child.Expanded ); + Assert.True( child.Checked ); + Assert.True( child.DisableCheckbox ); + Assert.False( child.Selectable ); + Assert.True( child.Selected ); + Assert.True( child.IsLeaf ); + } + + /// + /// 添加2个根节点,其中一个有子节点 + /// + [Fact] + public void TestGetResult_3() { + var list = new List { GetNode1(), GetNode2(), GetNode3() }; + var result = list.ToTreeResult(); + + //根节点1 + Assert.Equal( 2, result.Nodes.Count ); + var root = result.Nodes.First(); + Assert.Equal( Id, root.Key ); + Assert.Equal( Title, root.Title ); + Assert.True( root.Disabled ); + Assert.True( root.Expanded ); + Assert.True( root.Checked ); + Assert.True( root.DisableCheckbox ); + Assert.False( root.Selectable ); + Assert.True( root.Selected ); + Assert.False( root.IsLeaf ); + + //根节点2 + var root2 = result.Nodes[1]; + Assert.Equal( Id3, root2.Key ); + Assert.Equal( Title3, root2.Title ); + Assert.Equal( "a", root2.Icon ); + Assert.True( root2.Disabled ); + Assert.True( root2.Expanded ); + Assert.True( root2.Checked ); + Assert.True( root2.DisableCheckbox ); + Assert.False( root2.Selectable ); + Assert.True( root2.Selected ); + Assert.True( root2.IsLeaf ); + + //子节点 + Assert.Single( root.Children ); + var child = root.Children[0]; + Assert.Equal( Id2, child.Key ); + Assert.Equal( Title2, child.Title ); + Assert.True( child.Disabled ); + Assert.True( child.Expanded ); + Assert.True( child.Checked ); + Assert.True( child.DisableCheckbox ); + Assert.False( child.Selectable ); + Assert.True( child.Selected ); + Assert.True( child.IsLeaf ); + } + + /// + /// 添加2个子节点 + /// + [Fact] + public void TestGetResult_4() { + var list = new List { GetNode1(), GetNode2(), GetNode3(), GetNode4() }; + var result = list.ToTreeResult(); + + //根节点 + Assert.Equal( 2, result.Nodes.Count ); + var root = result.Nodes.First(); + Assert.Equal( Id, root.Key ); + Assert.False( root.IsLeaf ); + + //根节点2 + var root2 = result.Nodes[1]; + Assert.Equal( Id3, root2.Key ); + Assert.True( root2.IsLeaf ); + + //子节点 + Assert.Equal( 2, root.Children.Count ); + var child = root.Children[0]; + Assert.Equal( Id2, child.Key ); + Assert.True( child.IsLeaf ); + + //子节点2 + Assert.Equal( 2, root.Children.Count ); + var child2 = root.Children[1]; + Assert.Equal( Id4, child2.Key ); + Assert.True( child2.IsLeaf ); + } + + /// + /// 添加3级节点 + /// + [Fact] + public void TestGetResult_5() { + var list = new List { GetNode1(), GetNode2(), GetNode3(), GetNode4(), GetNode5() }; + var result = list.ToTreeResult(); + + //根节点 + Assert.Equal( 2, result.Nodes.Count ); + var root = result.Nodes.First(); + Assert.Equal( Id, root.Key ); + Assert.False( root.IsLeaf ); + + //根节点2 + var root2 = result.Nodes[1]; + Assert.Equal( Id3, root2.Key ); + Assert.True( root2.IsLeaf ); + + //子节点 + Assert.Equal( 2, root.Children.Count ); + var child = root.Children[0]; + Assert.Equal( Id2, child.Key ); + Assert.True( child.IsLeaf ); + + //子节点2 + Assert.Equal( 2, root.Children.Count ); + var child2 = root.Children[1]; + Assert.Equal( Id4, child2.Key ); + Assert.False( child2.IsLeaf ); + + //孙节点 + Assert.Single( child2.Children ); + var child3 = child2.Children[0]; + Assert.Equal( Id5, child3.Key ); + Assert.True( child3.IsLeaf ); + } + + /// + /// 测试删除原始节点的子节点列表 + /// + [Fact] + public void TestGetResult_6() { + var list = new List { GetNode1(), GetNode2(), GetNode3(), GetNode4(), GetNode5() }; + var result = list.ToTreeResult(); + var root = result.Nodes.First(); + Assert.Null( root.GetOriginalNode().Children ); + Assert.True( root.Children?.Count > 0 ); + } + + /// + /// 测试复选框选中节点的标识列表 + /// + [Fact] + public void TestCheckedKeys() { + var node2 = GetNode2(); + node2.Checked = false; + var list = new List { GetNode1(), node2, GetNode3(), GetNode4(), GetNode5() }; + var result = list.ToTreeResult(); + + Assert.Equal( Id, result.CheckedKeys[0] ); + Assert.Equal( Id3, result.CheckedKeys[1] ); + Assert.Equal( Id4, result.CheckedKeys[2] ); + } + + /// + /// 测试选中节点的标识列表 + /// + [Fact] + public void TestSelectedKeys() { + var node2 = GetNode2(); + node2.Selected = false; + var list = new List { GetNode1(), node2, GetNode3(), GetNode4(), GetNode5() }; + var result = list.ToTreeResult(); + + Assert.Equal( Id, result.SelectedKeys[0] ); + Assert.Equal( Id3, result.SelectedKeys[1] ); + Assert.Equal( Id4, result.SelectedKeys[2] ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/DatePickers/DatePickerTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/DatePickers/DatePickerTagHelperTest.Expression.cs new file mode 100644 index 000000000..d78fadf76 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/DatePickers/DatePickerTagHelperTest.Expression.cs @@ -0,0 +1,30 @@ +using System.Text; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.DatePickers { + /// + /// 日期选择测试 - 表达式解析测试 + /// + public partial class DatePickerTagHelperTest { + /// + /// 测试解析表达式属性for + /// + [Fact] + public void TestFor_1() { + _wrapper.SetExpression( t => t.Birthday ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "出生日期" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_birthday.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/DatePickers/DatePickerTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/DatePickers/DatePickerTagHelperTest.cs new file mode 100644 index 000000000..eceb6f8fa --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/DatePickers/DatePickerTagHelperTest.cs @@ -0,0 +1,605 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.DatePickers; +using Util.Ui.NgZorro.Components.Forms; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.DatePickers { + /// + /// 日期选择测试 + /// + public partial class DatePickerTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public DatePickerTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new DatePickerTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入框标识 + /// + [Fact] + public void TestNzId() { + _wrapper.SetContextAttribute( UiConst.NzId, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入框标识 + /// + [Fact] + public void TestBindNzId() { + _wrapper.SetContextAttribute( AngularConst.BindNzId, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试允许清除 + /// + [Fact] + public void TestAllowClear() { + _wrapper.SetContextAttribute( UiConst.AllowClear, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动聚焦 + /// + [Fact] + public void TestAutoFocus() { + _wrapper.SetContextAttribute( UiConst.AutoFocus, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试浮层是否应带有背景板 + /// + [Fact] + public void TestBackdrop() { + _wrapper.SetContextAttribute( UiConst.Backdrop, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试默认面板日期 + /// + [Fact] + public void TestDefaultPickerValue() { + _wrapper.SetContextAttribute( UiConst.DefaultPickerValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试默认面板日期 + /// + [Fact] + public void TestBindDefaultPickerValue() { + _wrapper.SetContextAttribute( AngularConst.BindDefaultPickerValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试不可选择日期函数 + /// + [Fact] + public void TestDisabledDate() { + _wrapper.SetContextAttribute( UiConst.DisabledDate, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试不可选择时间函数 + /// + [Fact] + public void TestDisabledTime() { + _wrapper.SetContextAttribute( UiConst.DisabledTime, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试弹出日历样式类名 + /// + [Fact] + public void TestDropdownClassName() { + _wrapper.SetContextAttribute( UiConst.DropdownClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试弹出日历样式类名 + /// + [Fact] + public void TestBindDropdownClassName() { + _wrapper.SetContextAttribute( AngularConst.BindDropdownClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试弹出日历样式 + /// + [Fact] + public void TestPopupStyle() { + _wrapper.SetContextAttribute( UiConst.PopupStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试日期格式 + /// + [Fact] + public void TestFormat() { + _wrapper.SetContextAttribute( UiConst.Format, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试日期格式 + /// + [Fact] + public void TestBindFormat() { + _wrapper.SetContextAttribute( AngularConst.BindFormat, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试只读 + /// + [Fact] + public void TestInputReadOnly() { + _wrapper.SetContextAttribute( UiConst.InputReadonly, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试国际化 + /// + [Fact] + public void TestLocale() { + _wrapper.SetContextAttribute( UiConst.Locale, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模式 + /// + [Fact] + public void TestMode() { + _wrapper.SetContextAttribute( UiConst.Mode, DatePickerMode.Month ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模式 - 周,修复格式字符串 + /// + [Fact] + public void TestMode_Week() { + _wrapper.SetContextAttribute( UiConst.Mode, DatePickerMode.Week ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模式 + /// + [Fact] + public void TestBindMode() { + _wrapper.SetContextAttribute( AngularConst.BindMode, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试占位提示 + /// + [Fact] + public void TestPlaceholder() { + _wrapper.SetContextAttribute( UiConst.Placeholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试占位提示 + /// + [Fact] + public void TestBindPlaceholder() { + _wrapper.SetContextAttribute( AngularConst.BindPlaceholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试额外页脚 + /// + [Fact] + public void TestRenderExtraFooter() { + _wrapper.SetContextAttribute( UiConst.RenderExtraFooter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试额外页脚 + /// + [Fact] + public void TestBindRenderExtraFooter() { + _wrapper.SetContextAttribute( AngularConst.BindRenderExtraFooter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入框大小 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, InputSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入框大小 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀图标 + /// + [Fact] + public void TestSuffixIcon() { + _wrapper.SetContextAttribute( UiConst.SuffixIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀图标 + /// + [Fact] + public void TestBindSuffixIcon() { + _wrapper.SetContextAttribute( AngularConst.BindSuffixIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试移除边框 + /// + [Fact] + public void TestBorderless() { + _wrapper.SetContextAttribute( UiConst.Borderless, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内联模式 + /// + [Fact] + public void TestInline() { + _wrapper.SetContextAttribute( UiConst.Inline, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示周数 + /// + [Fact] + public void TestShowWeekNumber() { + _wrapper.SetContextAttribute( UiConst.ShowWeekNumber, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义日期单元格内容 + /// + [Fact] + public void TestDateRender() { + _wrapper.SetContextAttribute( UiConst.DateRender, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示时间选择 + /// + [Fact] + public void TestShowTime() { + _wrapper.SetContextAttribute( UiConst.ShowTime, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示“今天”按钮 + /// + [Fact] + public void TestShowToday() { + _wrapper.SetContextAttribute( UiConst.ShowToday, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示“此刻”按钮 + /// + [Fact] + public void TestShowNow() { + _wrapper.SetContextAttribute( UiConst.ShowNow, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestStatus() { + _wrapper.SetContextAttribute( UiConst.Status, FormControlStatus.Error ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestBindStatus() { + _wrapper.SetContextAttribute( AngularConst.BindStatus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试日期选择框弹出位置 + /// + [Fact] + public void TestPlacement() { + _wrapper.SetContextAttribute( UiConst.Placement, DatePickerPlacement.TopLeft ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试日期选择框弹出位置 + /// + [Fact] + public void TestBindPlacement() { + _wrapper.SetContextAttribute( AngularConst.BindPlacement, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试必填项验证 + /// + [Fact] + public void TestRequired() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试必填项消息 + /// + [Fact] + public void TestRequiredMessage() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( UiConst.RequiredMessage, "a" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试弹出关闭日历事件 + /// + [Fact] + public void TestOnOpenChange() { + _wrapper.SetContextAttribute( UiConst.OnOpenChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试确定事件 + /// + [Fact] + public void TestOnOk() { + _wrapper.SetContextAttribute( UiConst.OnOk, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动设置nzFor + /// + [Fact] + public void TestAutoNzFor() { + var form = new FormTagHelper().ToWrapper(); + + var formItem = new FormItemTagHelper().ToWrapper(); + form.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + _wrapper.SetContextAttribute( UiConst.AutoNzFor, true ); + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( _wrapper ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
" ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/DatePickers/RangePickerTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/DatePickers/RangePickerTagHelperTest.Expression.cs new file mode 100644 index 000000000..583c5c98b --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/DatePickers/RangePickerTagHelperTest.Expression.cs @@ -0,0 +1,54 @@ +using System.Text; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.DatePickers { + /// + /// 日期范围选择测试 - 表达式解析测试 + /// + public partial class RangePickerTagHelperTest { + /// + /// 测试解析表达式属性for + /// + [Fact] + public void TestFor_1() { + _wrapper.SetExpression( t => t.Birthday ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "出生日期" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_birthday.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试解析表达式属性for-begin,for-end + /// + [Fact] + public void TestForBegin_ForEnd_1() { + _wrapper.SetExpression( UiConst.ForBegin, t => t.BeginBirthday ); + _wrapper.SetExpression( UiConst.ForEnd, t => t.EndBirthday ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "出生日期" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_begin_birthday.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/DatePickers/RangePickerTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/DatePickers/RangePickerTagHelperTest.cs new file mode 100644 index 000000000..6423246bf --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/DatePickers/RangePickerTagHelperTest.cs @@ -0,0 +1,631 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.DatePickers; +using Util.Ui.NgZorro.Components.Forms; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.DatePickers { + /// + /// 日期范围选择测试 + /// + public partial class RangePickerTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public RangePickerTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new RangePickerTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入框标识 + /// + [Fact] + public void TestNzId() { + _wrapper.SetContextAttribute( UiConst.NzId, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入框标识 + /// + [Fact] + public void TestBindNzId() { + _wrapper.SetContextAttribute( AngularConst.BindNzId, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试允许清除 + /// + [Fact] + public void TestAllowClear() { + _wrapper.SetContextAttribute( UiConst.AllowClear, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动聚焦 + /// + [Fact] + public void TestAutoFocus() { + _wrapper.SetContextAttribute( UiConst.AutoFocus, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试浮层是否应带有背景板 + /// + [Fact] + public void TestBackdrop() { + _wrapper.SetContextAttribute( UiConst.Backdrop, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试默认面板日期 + /// + [Fact] + public void TestDefaultPickerValue() { + _wrapper.SetContextAttribute( UiConst.DefaultPickerValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试默认面板日期 + /// + [Fact] + public void TestBindDefaultPickerValue() { + _wrapper.SetContextAttribute( AngularConst.BindDefaultPickerValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试不可选择日期函数 + /// + [Fact] + public void TestDisabledDate() { + _wrapper.SetContextAttribute( UiConst.DisabledDate, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试弹出日历样式类名 + /// + [Fact] + public void TestDropdownClassName() { + _wrapper.SetContextAttribute( UiConst.DropdownClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试弹出日历样式类名 + /// + [Fact] + public void TestBindDropdownClassName() { + _wrapper.SetContextAttribute( AngularConst.BindDropdownClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试弹出日历样式 + /// + [Fact] + public void TestPopupStyle() { + _wrapper.SetContextAttribute( UiConst.PopupStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试日期格式 + /// + [Fact] + public void TestFormat() { + _wrapper.SetContextAttribute( UiConst.Format, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试日期格式 + /// + [Fact] + public void TestBindFormat() { + _wrapper.SetContextAttribute( AngularConst.BindFormat, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试只读 + /// + [Fact] + public void TestInputReadOnly() { + _wrapper.SetContextAttribute( UiConst.InputReadonly, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试国际化 + /// + [Fact] + public void TestLocale() { + _wrapper.SetContextAttribute( UiConst.Locale, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模式 + /// + [Fact] + public void TestMode() { + _wrapper.SetContextAttribute( UiConst.Mode, DatePickerMode.Month ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模式 + /// + [Fact] + public void TestBindMode() { + _wrapper.SetContextAttribute( AngularConst.BindMode, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试占位提示 + /// + [Fact] + public void TestPlaceholder() { + _wrapper.SetContextAttribute( UiConst.Placeholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试占位提示 + /// + [Fact] + public void TestBindPlaceholder() { + _wrapper.SetContextAttribute( AngularConst.BindPlaceholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试额外页脚 + /// + [Fact] + public void TestRenderExtraFooter() { + _wrapper.SetContextAttribute( UiConst.RenderExtraFooter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试额外页脚 + /// + [Fact] + public void TestBindRenderExtraFooter() { + _wrapper.SetContextAttribute( AngularConst.BindRenderExtraFooter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义日期单元格内容 + /// + [Fact] + public void TestDateRender() { + _wrapper.SetContextAttribute( UiConst.DateRender, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入框大小 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, InputSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入框大小 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀图标 + /// + [Fact] + public void TestSuffixIcon() { + _wrapper.SetContextAttribute( UiConst.SuffixIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀图标 + /// + [Fact] + public void TestBindSuffixIcon() { + _wrapper.SetContextAttribute( AngularConst.BindSuffixIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试移除边框 + /// + [Fact] + public void TestBorderless() { + _wrapper.SetContextAttribute( UiConst.Borderless, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内联模式 + /// + [Fact] + public void TestInline() { + _wrapper.SetContextAttribute( UiConst.Inline, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示周数 + /// + [Fact] + public void TestShowWeekNumber() { + _wrapper.SetContextAttribute( UiConst.ShowWeekNumber, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试预设时间范围 + /// + [Fact] + public void TestRanges() { + _wrapper.SetContextAttribute( UiConst.Ranges, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试分隔符 + /// + [Fact] + public void TestSeparator() { + _wrapper.SetContextAttribute( UiConst.Separator, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试分隔符 + /// + [Fact] + public void TestBindSeparator() { + _wrapper.SetContextAttribute( AngularConst.BindSeparator, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示时间选择 + /// + [Fact] + public void TestShowTime() { + _wrapper.SetContextAttribute( UiConst.ShowTime, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestStatus() { + _wrapper.SetContextAttribute( UiConst.Status, FormControlStatus.Error ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestBindStatus() { + _wrapper.SetContextAttribute( AngularConst.BindStatus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试日期选择框弹出位置 + /// + [Fact] + public void TestPlacement() { + _wrapper.SetContextAttribute( UiConst.Placement, DatePickerPlacement.TopLeft ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试日期选择框弹出位置 + /// + [Fact] + public void TestBindPlacement() { + _wrapper.SetContextAttribute( AngularConst.BindPlacement, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试必填项验证 + /// + [Fact] + public void TestRequired() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试必填项消息 + /// + [Fact] + public void TestRequiredMessage() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( UiConst.RequiredMessage, "a" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试弹出关闭日历事件 + /// + [Fact] + public void TestOnOpenChange() { + _wrapper.SetContextAttribute( UiConst.OnOpenChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试待选日期改变事件 + /// + [Fact] + public void TestOnCalendarChange() { + _wrapper.SetContextAttribute( UiConst.OnCalendarChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试确定事件 + /// + [Fact] + public void TestOnOk() { + _wrapper.SetContextAttribute( UiConst.OnOk, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试起始日期 + /// + [Fact] + public void TestBeginDate_1() { + _wrapper.SetContextAttribute( UiConst.BeginDate, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试结束日期 + /// + [Fact] + public void TestEndDate() { + _wrapper.SetContextAttribute( UiConst.EndDate, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动设置nzFor + /// + [Fact] + public void TestAutoNzFor() { + var form = new FormTagHelper().ToWrapper(); + + var formItem = new FormItemTagHelper().ToWrapper(); + form.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + _wrapper.SetContextAttribute( UiConst.AutoNzFor, true ); + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( _wrapper ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
" ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Descriptions/DescriptionItemTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/Descriptions/DescriptionItemTagHelperTest.Expression.cs new file mode 100644 index 000000000..5f27ec7fc --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Descriptions/DescriptionItemTagHelperTest.Expression.cs @@ -0,0 +1,104 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Descriptions { + /// + /// 描述列表项测试 - 表达式解析 + /// + public partial class DescriptionItemTagHelperTest { + /// + /// 测试属性表达式 - 文本类型 + /// + [Fact] + public void TestFor_1() { + _wrapper.SetExpression( t => t.Code ); + var result = new StringBuilder(); + result.Append( "{{model.code}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 文本类型 - 多语言支持 + /// + [Fact] + public void TestFor_i18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetExpression( t => t.Code ); + _wrapper.SetContextAttribute( UiConst.ValueI18n, true ); + var result = new StringBuilder(); + result.Append( "{{model.code|i18n}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试测试属性表达式 - 复制到剪贴板 + /// + [Fact] + public void TestFor_Clipboard() { + _wrapper.SetExpression( t => t.Code ); + _wrapper.SetContextAttribute( UiConst.Clipboard, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{model.code}}" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 布尔类型 + /// + [Fact] + public void TestFor_2() { + _wrapper.SetExpression( t => t.Enabled ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 日期类型 + /// + [Fact] + public void TestFor_3() { + _wrapper.SetExpression( t => t.Birthday ); + var result = new StringBuilder(); + result.Append( "{{model.birthday|date:\"yyyy-MM-dd HH:mm:ss\"}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 日期类型 - 设置日期格式 + /// + [Fact] + public void TestFor_4() { + _wrapper.SetContextAttribute( UiConst.DateFormat, "yyyy-MM" ); + _wrapper.SetExpression( t => t.Birthday ); + var result = new StringBuilder(); + result.Append( "{{model.birthday|date:\"yyyy-MM\"}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 数值类型 + /// + [Fact] + public void TestFor_5() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetExpression( t => t.IdCard ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{model.idCard}}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Descriptions/DescriptionItemTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Descriptions/DescriptionItemTagHelperTest.cs new file mode 100644 index 000000000..8b585d949 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Descriptions/DescriptionItemTagHelperTest.cs @@ -0,0 +1,183 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Descriptions; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Descriptions { + /// + /// 描述列表项测试 + /// + public partial class DescriptionItemTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public DescriptionItemTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new DescriptionItemTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列跨度 + /// + [Fact] + public void TestSpan() { + _wrapper.SetContextAttribute( UiConst.Span, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列跨度 + /// + [Fact] + public void TestBindSpan() { + _wrapper.SetContextAttribute( AngularConst.BindSpan, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 - 多语言 + /// + [Fact] + public void TestTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestValue() { + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + var result = new StringBuilder(); + result.Append( "{{a}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 - 多语言 + /// + [Fact] + public void TestValueI18n() { + _wrapper.SetContextAttribute( UiConst.ValueI18n, true ); + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + var result = new StringBuilder(); + result.Append( "{{a|i18n}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复制到剪贴板 + /// + [Fact] + public void TestClipboard() { + _wrapper.SetContextAttribute( UiConst.Value, "model.code" ); + _wrapper.SetContextAttribute( UiConst.Clipboard, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{model.code}}" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复制到剪贴板 - 多语言 + /// + [Fact] + public void TestClipboard_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Value, "model.code" ); + _wrapper.SetContextAttribute( UiConst.Clipboard, true ); + _wrapper.SetContextAttribute( UiConst.ValueI18n, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{model.code|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Descriptions/DescriptionTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Descriptions/DescriptionTagHelperTest.cs new file mode 100644 index 000000000..25a8e09e8 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Descriptions/DescriptionTagHelperTest.cs @@ -0,0 +1,208 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Descriptions; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Descriptions { + /// + /// 描述列表测试 + /// + public class DescriptionTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public DescriptionTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new DescriptionTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 - 多语言 + /// + [Fact] + public void TestTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试操作区域 + /// + [Fact] + public void TestExtra() { + _wrapper.SetContextAttribute( UiConst.Extra, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试操作区域 + /// + [Fact] + public void TestBindExtra() { + _wrapper.SetContextAttribute( AngularConst.BindExtra, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示边框 + /// + [Fact] + public void TestBordered() { + _wrapper.SetContextAttribute( UiConst.Bordered, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试一行中描述列表项的数量 + /// + [Fact] + public void TestColumn() { + _wrapper.SetContextAttribute( UiConst.Column, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试一行中描述列表项的数量 + /// + [Fact] + public void TestXxlColumn() { + _wrapper.SetContextAttribute( UiConst.XxlColumn, 3 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列表大小 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, DescriptionSize.Small ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列表大小 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列表布局 + /// + [Fact] + public void TestLayout() { + _wrapper.SetContextAttribute( UiConst.Layout, DescriptionLayout.Vertical ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列表布局 + /// + [Fact] + public void TestBindLayout() { + _wrapper.SetContextAttribute( AngularConst.BindLayout, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示冒号 + /// + [Fact] + public void TestColon() { + _wrapper.SetContextAttribute( UiConst.Colon, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Display/DisplayTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Display/DisplayTagHelperTest.cs new file mode 100644 index 000000000..241e236d9 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Display/DisplayTagHelperTest.cs @@ -0,0 +1,319 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Display; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Display { + /// + /// 数据项展示测试 + /// + public class DisplayTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public DisplayTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new DisplayTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestValue() { + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{a}}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 - 布尔类型 + /// + [Fact] + public void TestValue_Bool() { + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + _wrapper.SetContextAttribute( UiConst.Type,DataType.Bool ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{a?'是':'否'}}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 - 布尔类型 - 多语言 + /// + [Fact] + public void TestValue_Bool_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + _wrapper.SetContextAttribute( UiConst.Type, DataType.Bool ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{(a?'util.yes':'util.no')|i18n}}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复制到剪贴板 + /// + [Fact] + public void TestClipboard() { + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + _wrapper.SetContextAttribute( UiConst.Clipboard, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{a}}" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复制到剪贴板 - 多语言 + /// + [Fact] + public void TestClipboard_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + _wrapper.SetContextAttribute( UiConst.Clipboard, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{a|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示标签 + /// + [Fact] + public void TestShowLabel() { + _wrapper.SetContextAttribute( UiConst.ShowLabel, true ); + _wrapper.SetExpression( t => t.Nickname ); + var result = new StringBuilder(); + result.Append( "a.nickname:" ); + result.Append( "{{model.nickname}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示标签 - 多语言 + /// + [Fact] + public void TestShowLabel_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.ShowLabel, true ); + _wrapper.SetExpression( t => t.Nickname ); + var result = new StringBuilder(); + result.Append( "{{'a.nickname'|i18n}}:" ); + result.Append( "{{model.nickname|i18n}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示标签 - 放入form中,自动创建form-item + /// + [Fact] + public void TestShowLabel_Form() { + _wrapper.SetItem( new FormShareConfig { LabelSpan = "4", ControlSpan = "20", Gutter = "16" } ); + _wrapper.SetContextAttribute( UiConst.ShowLabel, true ); + _wrapper.SetExpression( t => t.Nickname ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a.nickname" ); + result.Append( "" ); + result.Append( "{{model.nickname}}" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示标签 - 放入form中,自动创建form-item - 设置value覆盖 + /// + [Fact] + public void TestShowLabel_Form_2() { + _wrapper.SetItem( new FormShareConfig { LabelSpan = "4", ControlSpan = "20", Gutter = "16" } ); + _wrapper.SetContextAttribute( UiConst.ShowLabel, true ); + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + _wrapper.SetExpression( t => t.Nickname ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a.nickname" ); + result.Append( "" ); + result.Append( "{{a}}" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示标签 - 放入form中,自动创建form-item - 多语言 + /// + [Fact] + public void TestShowLabel_Form_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetItem( new FormShareConfig { LabelSpan = "4", ControlSpan = "20", Gutter = "16" } ); + _wrapper.SetContextAttribute( UiConst.ShowLabel, true ); + _wrapper.SetExpression( t => t.Nickname ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{'a.nickname'|i18n}}" ); + result.Append( "" ); + result.Append( "{{model.nickname|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 + /// + [Fact] + public void TestFor_1() { + _wrapper.SetExpression( t => t.Code ); + var result = new StringBuilder(); + result.Append( "{{model.code}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 + /// + [Fact] + public void TestFor_1_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetExpression( t => t.Code ); + var result = new StringBuilder(); + result.Append( "{{model.code|i18n}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 设置value覆盖 + /// + [Fact] + public void TestFor_2() { + _wrapper.SetExpression( t => t.Code ); + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + var result = new StringBuilder(); + result.Append( "{{a}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 布尔类型 + /// + [Fact] + public void TestFor_3() { + _wrapper.SetExpression( t => t.Enabled ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{model.enabled?'是':'否'}}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 布尔类型 - 多语言 + /// + [Fact] + public void TestFor_3_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetExpression( t => t.Enabled ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{(model.enabled?'util.yes':'util.no')|i18n}}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 日期类型 + /// + [Fact] + public void TestFor_4() { + _wrapper.SetExpression( t => t.Birthday ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{model.birthday|date:\"yyyy-MM-dd HH:mm:ss\"}}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 日期类型 - 设置日期格式 + /// + [Fact] + public void TestFor_5() { + _wrapper.SetContextAttribute( UiConst.DateFormat, "yyyy-MM" ); + _wrapper.SetExpression( t => t.Birthday ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{model.birthday|date:\"yyyy-MM\"}}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Dividers/DividerTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Dividers/DividerTagHelperTest.cs new file mode 100644 index 000000000..812e4db67 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Dividers/DividerTagHelperTest.cs @@ -0,0 +1,174 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Dividers; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Dividers { + /// + /// 分隔线测试 + /// + public class DividerTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + /// + /// 标识 + /// + private readonly string _id; + + /// + /// 测试初始化 + /// + public DividerTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new DividerTagHelper().ToWrapper(); + _id = "id"; + Id.SetId( _id ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚线 + /// + [Fact] + public void TestDashed() { + _wrapper.SetContextAttribute( UiConst.Dashed, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试类型 + /// + [Fact] + public void TestType() { + _wrapper.SetContextAttribute( UiConst.Type, DividerType.Vertical ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试类型 + /// + [Fact] + public void TestBindType() { + _wrapper.SetContextAttribute( AngularConst.BindType, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置文字 + /// + [Fact] + public void TestText() { + _wrapper.SetContextAttribute( UiConst.Text, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置文字 - 多语言 + /// + [Fact] + public void TestText_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Text, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置文字 + /// + [Fact] + public void TestBindText() { + _wrapper.SetContextAttribute( AngularConst.BindText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置文字方向 + /// + [Fact] + public void TestOrientation() { + _wrapper.SetContextAttribute( UiConst.Orientation, DividerOrientation.Right ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置文字方向 + /// + [Fact] + public void TestBindOrientation() { + _wrapper.SetContextAttribute( AngularConst.BindOrientation, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试正文样式 + /// + [Fact] + public void TestPlain() { + _wrapper.SetContextAttribute( UiConst.Plain, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.AppendFormat( "",_id ); + result.AppendFormat( "", _id ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Drawers/DrawerContentTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Drawers/DrawerContentTagHelperTest.cs new file mode 100644 index 000000000..43f8b5427 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Drawers/DrawerContentTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Drawers; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Drawers; + +/// +/// 抽屉内容测试 +/// +public class DrawerContentTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public DrawerContentTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new DrawerContentTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestAppendContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Drawers/DrawerFooterTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Drawers/DrawerFooterTagHelperTest.cs new file mode 100644 index 000000000..6c952b006 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Drawers/DrawerFooterTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Drawers; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Drawers { + /// + /// 抽屉页脚测试 + /// + public class DrawerFooterTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public DrawerFooterTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new DrawerFooterTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "
a
" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Drawers/DrawerTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Drawers/DrawerTagHelperTest.cs new file mode 100644 index 000000000..ec764bdef --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Drawers/DrawerTagHelperTest.cs @@ -0,0 +1,383 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Drawers; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Drawers { + /// + /// 抽屉测试 + /// + public class DrawerTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public DrawerTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new DrawerTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 - 支持多语言 + /// + [Fact] + public void TestTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否可关闭 + /// + [Fact] + public void TestClosable() { + _wrapper.SetContextAttribute( UiConst.Closable, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试关闭图标 + /// + [Fact] + public void TestCloseIcon() { + _wrapper.SetContextAttribute( UiConst.CloseIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试关闭图标 + /// + [Fact] + public void TestBindCloseIcon() { + _wrapper.SetContextAttribute( AngularConst.BindCloseIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试右上角操作区域 + /// + [Fact] + public void TestExtra() { + _wrapper.SetContextAttribute( UiConst.Extra, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试点击遮罩是否允许关闭 + /// + [Fact] + public void TestMaskClosable() { + _wrapper.SetContextAttribute( UiConst.MaskClosable, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示遮罩 + /// + [Fact] + public void TestMask() { + _wrapper.SetContextAttribute( UiConst.Mask, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试导航时是否关闭 + /// + [Fact] + public void TestCloseOnNavigation() { + _wrapper.SetContextAttribute( UiConst.CloseOnNavigation, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试遮罩样式 + /// + [Fact] + public void TestMaskStyle() { + _wrapper.SetContextAttribute( UiConst.MaskStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否支持键盘ESC键关闭 + /// + [Fact] + public void TestKeyboard() { + _wrapper.SetContextAttribute( UiConst.Keyboard, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试主体样式 + /// + [Fact] + public void TestBodyStyle() { + _wrapper.SetContextAttribute( UiConst.BodyStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试页脚 + /// + [Fact] + public void TestFooter() { + _wrapper.SetContextAttribute( UiConst.Footer, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试页脚 + /// + [Fact] + public void TestBindFooter() { + _wrapper.SetContextAttribute( AngularConst.BindFooter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否可见 + /// + [Fact] + public void TestVisible() { + _wrapper.SetContextAttribute( UiConst.Visible, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否可见 + /// + [Fact] + public void TestBindonVisible() { + _wrapper.SetContextAttribute( AngularConst.BindonVisible, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试抽屉方向 + /// + [Fact] + public void TestPlacement() { + _wrapper.SetContextAttribute( UiConst.Placement, DrawerPlacement.Bottom ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试抽屉方向 + /// + [Fact] + public void TestBindPlacement() { + _wrapper.SetContextAttribute( AngularConst.BindPlacement, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试抽屉尺寸 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, DrawerSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试抽屉尺寸 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽度 + /// + [Fact] + public void TestWidth() { + _wrapper.SetContextAttribute( UiConst.Width, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试高度 + /// + [Fact] + public void TestHeight() { + _wrapper.SetContextAttribute( UiConst.Height, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试X坐标偏移量 + /// + [Fact] + public void TestOffsetX() { + _wrapper.SetContextAttribute( UiConst.OffsetX, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Y坐标偏移量 + /// + [Fact] + public void TestOffsetY() { + _wrapper.SetContextAttribute( UiConst.OffsetY, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试对话框外层容器样式类名 + /// + [Fact] + public void TestWrapClassName() { + _wrapper.SetContextAttribute( UiConst.WrapClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试对话框外层容器样式类名 + /// + [Fact] + public void TestBindWrapClassName() { + _wrapper.SetContextAttribute( AngularConst.BindWrapClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试z-index + /// + [Fact] + public void TestZIndex() { + _wrapper.SetContextAttribute( UiConst.ZIndex, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试关闭事件 + /// + [Fact] + public void TestOnClose() { + _wrapper.SetContextAttribute( UiConst.OnClose, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Dropdowns/DropdownMenuTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Dropdowns/DropdownMenuTagHelperTest.cs new file mode 100644 index 000000000..88cfcaf0e --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Dropdowns/DropdownMenuTagHelperTest.cs @@ -0,0 +1,118 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Dropdowns; +using Util.Ui.NgZorro.Components.Menus; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Dropdowns { + /// + /// 下拉菜单测试 + /// + public class DropdownMenuTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public DropdownMenuTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new DropdownMenuTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
    " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标识 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "
      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试允许选中 + /// + [Fact] + public void TestSelectable() { + _wrapper.SetContextAttribute( UiConst.Selectable, "false" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "
        " ); + result.Append( "
        " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "
          " ); + result.Append( "a" ); + result.Append( "
        " ); + result.Append( "
        " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击事件 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试手工创建菜单标签 + /// + [Fact] + public void TestMenu() { + var menu = new MenuTagHelper().ToWrapper(); + _wrapper.AppendContent( menu ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "
            " ); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Empties/EmptyTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Empties/EmptyTagHelperTest.cs new file mode 100644 index 000000000..400863cfd --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Empties/EmptyTagHelperTest.cs @@ -0,0 +1,127 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Empties; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Empties { + /// + /// 空状态测试 + /// + public class EmptyTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public EmptyTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new EmptyTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图片 + /// + [Fact] + public void TestNotFoundImage() { + _wrapper.SetContextAttribute( UiConst.NotFoundImage, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图片 + /// + [Fact] + public void TestBindNotFoundImage() { + _wrapper.SetContextAttribute( AngularConst.BindNotFoundImage, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestNotFoundContent() { + _wrapper.SetContextAttribute( UiConst.NotFoundContent, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestBindNotFoundContent() { + _wrapper.SetContextAttribute( AngularConst.BindNotFoundContent, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试底部内容 + /// + [Fact] + public void TestNotFoundFooter() { + _wrapper.SetContextAttribute( UiConst.NotFoundFooter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试底部内容 + /// + [Fact] + public void TestBindNotFoundFooter() { + _wrapper.SetContextAttribute( AngularConst.BindNotFoundFooter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Flex/FlexTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Flex/FlexTagHelperTest.cs new file mode 100644 index 000000000..1d72c28a7 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Flex/FlexTagHelperTest.cs @@ -0,0 +1,183 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Flex; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Flex; + +/// +/// 弹性布局栅格测试 +/// +public class FlexTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public FlexTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new FlexTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否垂直布局 + /// + [Fact] + public void TestVertical() { + _wrapper.SetContextAttribute( UiConst.Vertical, "true" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试对齐方式 + /// + [Fact] + public void TestJustify() { + _wrapper.SetContextAttribute( UiConst.Justify, FlexJustify.FlexEnd ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试对齐方式 + /// + [Fact] + public void TestBindJustify() { + _wrapper.SetContextAttribute( AngularConst.BindJustify, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试垂直对齐方式 + /// + [Fact] + public void TestAlign() { + _wrapper.SetContextAttribute( UiConst.Align, FlexAlign.FlexEnd ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试垂直对齐方式 + /// + [Fact] + public void TestBindAlign() { + _wrapper.SetContextAttribute( AngularConst.BindAlign, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距 + /// + [Fact] + public void TestGap() { + _wrapper.SetContextAttribute( UiConst.Gap, FlexGap.Middle ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距 + /// + [Fact] + public void TestBindGap() { + _wrapper.SetContextAttribute( AngularConst.BindGap, "10" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动换行 + /// + [Fact] + public void TestWrap() { + _wrapper.SetContextAttribute( UiConst.Wrap, FlexWrap.Nowrap ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动换行 + /// + [Fact] + public void TestBindWrap() { + _wrapper.SetContextAttribute( AngularConst.BindWrap, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试flex + /// + [Fact] + public void TestFlex() { + _wrapper.SetContextAttribute( UiConst.Flex, "1" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试flex + /// + [Fact] + public void TestBindFlex() { + _wrapper.SetContextAttribute( AngularConst.BindFlex, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "
          a
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Forms/FormContainerTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Forms/FormContainerTagHelperTest.cs new file mode 100644 index 000000000..9925c8937 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Forms/FormContainerTagHelperTest.cs @@ -0,0 +1,3606 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Forms; +using Util.Ui.NgZorro.Components.Inputs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Forms { + /// + /// 表单容器测试 + /// + public class FormContainerTagHelperTest { + + #region 测试初始化 + + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public FormContainerTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new FormContainerTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + #endregion + + #region 默认测试 + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Align(垂直对齐方式) + + /// + /// 测试设置垂直对齐方式 + /// + [Fact] + public void TestAlign() { + _wrapper.SetContextAttribute( UiConst.Align, Align.Middle ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置垂直对齐方式 + /// + [Fact] + public void TestBindAlign() { + _wrapper.SetContextAttribute( AngularConst.BindAlign, "a" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Gutter(栅格间隔) + + /// + /// 测试设置栅格间隔 + /// + [Fact] + public void TestGutter() { + _wrapper.SetContextAttribute( UiConst.Gutter, "2" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Justify(水平排列方式) + + /// + /// 测试设置水平排列方式 + /// + [Fact] + public void TestJustify() { + _wrapper.SetContextAttribute( UiConst.Justify, Justify.Center ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置水平排列方式 + /// + [Fact] + public void TestBindJustify() { + _wrapper.SetContextAttribute( AngularConst.BindJustify, "a" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region HasFeedback(显示校验状态反馈图标) + + /// + /// 测试显示校验状态反馈图标 + /// + [Fact] + public void TestHasFeedback_1() { + _wrapper.SetContextAttribute( UiConst.HasFeedback, true ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( AngularConst.NgModel, "model" ); + input.SetContextAttribute( UiConst.Required, "a" ); + _wrapper.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示校验状态反馈图标 - 覆盖 form 设置 + /// + [Fact] + public void TestHasFeedback_2() { + var form = new FormTagHelper().ToWrapper(); + form.SetContextAttribute( UiConst.HasFeedback, true ); + + _wrapper.SetContextAttribute( UiConst.HasFeedback, false ); + form.AppendContent(_wrapper); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( AngularConst.NgModel, "model" ); + input.SetContextAttribute( UiConst.Required, "a" ); + _wrapper.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + + #endregion + + #region 表单标签常规栅格测试 + + /// + /// 测试设置标签跨度 + /// + [Fact] + public void TestLabelSpan() { + _wrapper.SetContextAttribute( UiConst.LabelSpan, 3 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置标签跨度 - 全部form label都会被设置 + /// + [Fact] + public void TestLabelSpan_2() { + _wrapper.SetContextAttribute( UiConst.LabelSpan, 3 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var formItem2 = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem2 ); + + var formLabel2 = new FormLabelTagHelper().ToWrapper(); + formLabel2.AppendContent( "b" ); + formItem2.AppendContent( formLabel2 ); + + var formControl2 = new FormControlTagHelper().ToWrapper(); + formControl2.AppendContent( new InputTagHelper() ); + formItem2.AppendContent( formControl2 ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置标签跨度,表单先设置,使用表单容器进行覆盖 + /// + [Fact] + public void TestLabelSpan_3() { + var form = new FormTagHelper().ToWrapper(); + form.SetContextAttribute( UiConst.LabelSpan, 2 ); + + _wrapper.SetContextAttribute( UiConst.LabelSpan, 3 ); + form.AppendContent( _wrapper ); + + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + + /// + /// 测试设置标签跨度,添加两个输入框,一个自动创建nz-form-item,一个手工创建 + /// + [Fact] + public void TestLabelSpan_4() { + _wrapper.SetContextAttribute( UiConst.LabelSpan, 3 ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelText, "a" ); + _wrapper.AppendContent( input ); + + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置标签偏移量 + /// + [Fact] + public void TestLabelOffset() { + _wrapper.SetContextAttribute( UiConst.LabelOffset, 3 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置标签栅格顺序 + /// + [Fact] + public void TestLabelOrder() { + _wrapper.SetContextAttribute( UiConst.LabelOrder, 3 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签栅格左移 + /// + [Fact] + public void TestLabelPull() { + _wrapper.SetContextAttribute( UiConst.LabelPull, 3 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签栅格右移 + /// + [Fact] + public void TestLabelPush() { + _wrapper.SetContextAttribute( UiConst.LabelPush, 3 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签Flex布局 + /// + [Fact] + public void TestLabelFlex() { + _wrapper.SetContextAttribute( UiConst.LabelFlex, 3 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region 表单标签超窄尺寸栅格测试 + + /// + /// 测试标签超窄尺寸栅格 + /// + [Fact] + public void TestLabelXs_1() { + _wrapper.SetContextAttribute( UiConst.LabelXs, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签超窄尺寸栅格,表单控件覆盖 + /// + [Fact] + public void TestLabelXs_2() { + _wrapper.SetContextAttribute( UiConst.LabelXs, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formItem.AppendContent( formControl ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelXs, "c" ); + formControl.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签超窄尺寸栅格,表单标签覆盖 + /// + [Fact] + public void TestLabelXs_3() { + _wrapper.SetContextAttribute( UiConst.LabelXs, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.SetContextAttribute( UiConst.Xs, "c" ); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签超窄尺寸响应式栅格跨度 + /// + [Fact] + public void TestLabelXsSpan() { + _wrapper.SetContextAttribute( UiConst.LabelXsSpan, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签超窄尺寸响应式栅格偏移量 + /// + [Fact] + public void TestLabelXsOffset() { + _wrapper.SetContextAttribute( UiConst.LabelXsOffset, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签超窄尺寸响应式栅格顺序 + /// + [Fact] + public void TestLabelXsOrder() { + _wrapper.SetContextAttribute( UiConst.LabelXsOrder, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签超窄尺寸响应式栅格左移 + /// + [Fact] + public void TestLabelXsPull() { + _wrapper.SetContextAttribute( UiConst.LabelXsPull, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签超窄尺寸响应式栅格右移 + /// + [Fact] + public void TestLabelXsPush() { + _wrapper.SetContextAttribute( UiConst.LabelXsPush, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region 表单标签窄尺寸栅格测试 + + /// + /// 测试标签窄尺寸栅格 + /// + [Fact] + public void TestLabelSm_1() { + _wrapper.SetContextAttribute( UiConst.LabelSm, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签窄尺寸栅格,表单控件覆盖 + /// + [Fact] + public void TestLabelSm_2() { + _wrapper.SetContextAttribute( UiConst.LabelSm, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formItem.AppendContent( formControl ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelSm, "c" ); + formControl.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签窄尺寸栅格,表单标签覆盖 + /// + [Fact] + public void TestLabelSm_3() { + _wrapper.SetContextAttribute( UiConst.LabelSm, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.SetContextAttribute( UiConst.Sm, "c" ); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签窄尺寸响应式栅格跨度 + /// + [Fact] + public void TestLabelSmSpan() { + _wrapper.SetContextAttribute( UiConst.LabelSmSpan, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签窄尺寸响应式栅格偏移量 + /// + [Fact] + public void TestLabelSmOffset() { + _wrapper.SetContextAttribute( UiConst.LabelSmOffset, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签窄尺寸响应式栅格顺序 + /// + [Fact] + public void TestLabelSmOrder() { + _wrapper.SetContextAttribute( UiConst.LabelSmOrder, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签窄尺寸响应式栅格左移 + /// + [Fact] + public void TestLabelSmPull() { + _wrapper.SetContextAttribute( UiConst.LabelSmPull, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签窄尺寸响应式栅格右移 + /// + [Fact] + public void TestLabelSmPush() { + _wrapper.SetContextAttribute( UiConst.LabelSmPush, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region 表单标签中尺寸栅格测试 + + /// + /// 测试标签中尺寸栅格 + /// + [Fact] + public void TestLabelMd_1() { + _wrapper.SetContextAttribute( UiConst.LabelMd, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签中尺寸栅格,表单控件覆盖 + /// + [Fact] + public void TestLabelMd_2() { + _wrapper.SetContextAttribute( UiConst.LabelMd, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formItem.AppendContent( formControl ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelMd, "c" ); + formControl.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签中尺寸栅格,表单标签覆盖 + /// + [Fact] + public void TestLabelMd_3() { + _wrapper.SetContextAttribute( UiConst.LabelMd, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.SetContextAttribute( UiConst.Md, "c" ); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签中尺寸响应式栅格跨度 + /// + [Fact] + public void TestLabelMdSpan() { + _wrapper.SetContextAttribute( UiConst.LabelMdSpan, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签中尺寸响应式栅格偏移量 + /// + [Fact] + public void TestLabelMdOffset() { + _wrapper.SetContextAttribute( UiConst.LabelMdOffset, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签中尺寸响应式栅格顺序 + /// + [Fact] + public void TestLabelMdOrder() { + _wrapper.SetContextAttribute( UiConst.LabelMdOrder, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签中尺寸响应式栅格左移 + /// + [Fact] + public void TestLabelMdPull() { + _wrapper.SetContextAttribute( UiConst.LabelMdPull, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签中尺寸响应式栅格右移 + /// + [Fact] + public void TestLabelMdPush() { + _wrapper.SetContextAttribute( UiConst.LabelMdPush, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region 表单标签宽尺寸栅格测试 + + /// + /// 测试标签宽尺寸栅格 + /// + [Fact] + public void TestLabelLg_1() { + _wrapper.SetContextAttribute( UiConst.LabelLg, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签宽尺寸栅格,表单控件覆盖 + /// + [Fact] + public void TestLabelLg_2() { + _wrapper.SetContextAttribute( UiConst.LabelLg, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formItem.AppendContent( formControl ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelLg, "c" ); + formControl.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签宽尺寸栅格,表单标签覆盖 + /// + [Fact] + public void TestLabelLg_3() { + _wrapper.SetContextAttribute( UiConst.LabelLg, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.SetContextAttribute( UiConst.Lg, "c" ); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签宽尺寸响应式栅格跨度 + /// + [Fact] + public void TestLabelLgSpan() { + _wrapper.SetContextAttribute( UiConst.LabelLgSpan, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签宽尺寸响应式栅格偏移量 + /// + [Fact] + public void TestLabelLgOffset() { + _wrapper.SetContextAttribute( UiConst.LabelLgOffset, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签宽尺寸响应式栅格顺序 + /// + [Fact] + public void TestLabelLgOrder() { + _wrapper.SetContextAttribute( UiConst.LabelLgOrder, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签宽尺寸响应式栅格左移 + /// + [Fact] + public void TestLabelLgPull() { + _wrapper.SetContextAttribute( UiConst.LabelLgPull, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签宽尺寸响应式栅格右移 + /// + [Fact] + public void TestLabelLgPush() { + _wrapper.SetContextAttribute( UiConst.LabelLgPush, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region 表单标签超宽尺寸栅格测试 + + /// + /// 测试标签超宽尺寸栅格 + /// + [Fact] + public void TestLabelXl_1() { + _wrapper.SetContextAttribute( UiConst.LabelXl, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签超宽尺寸栅格,表单控件覆盖 + /// + [Fact] + public void TestLabelXl_2() { + _wrapper.SetContextAttribute( UiConst.LabelXl, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formItem.AppendContent( formControl ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelXl, "c" ); + formControl.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签超宽尺寸栅格,表单标签覆盖 + /// + [Fact] + public void TestLabelXl_3() { + _wrapper.SetContextAttribute( UiConst.LabelXl, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.SetContextAttribute( UiConst.Xl, "c" ); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签超宽尺寸响应式栅格跨度 + /// + [Fact] + public void TestLabelXlSpan() { + _wrapper.SetContextAttribute( UiConst.LabelXlSpan, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签超宽尺寸响应式栅格偏移量 + /// + [Fact] + public void TestLabelXlOffset() { + _wrapper.SetContextAttribute( UiConst.LabelXlOffset, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签超宽尺寸响应式栅格顺序 + /// + [Fact] + public void TestLabelXlOrder() { + _wrapper.SetContextAttribute( UiConst.LabelXlOrder, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签超宽尺寸响应式栅格左移 + /// + [Fact] + public void TestLabelXlPull() { + _wrapper.SetContextAttribute( UiConst.LabelXlPull, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签超宽尺寸响应式栅格右移 + /// + [Fact] + public void TestLabelXlPush() { + _wrapper.SetContextAttribute( UiConst.LabelXlPush, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region 表单标签极宽尺寸栅格测试 + + /// + /// 测试标签极宽尺寸栅格 + /// + [Fact] + public void TestLabelXxl_1() { + _wrapper.SetContextAttribute( UiConst.LabelXxl, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签极宽尺寸栅格,表单控件覆盖 + /// + [Fact] + public void TestLabelXxl_2() { + _wrapper.SetContextAttribute( UiConst.LabelXxl, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formItem.AppendContent( formControl ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelXxl, "c" ); + formControl.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签极宽尺寸栅格,表单标签覆盖 + /// + [Fact] + public void TestLabelXxl_3() { + _wrapper.SetContextAttribute( UiConst.LabelXxl, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.SetContextAttribute( UiConst.Xxl, "c" ); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签极宽尺寸响应式栅格跨度 + /// + [Fact] + public void TestLabelXxlSpan() { + _wrapper.SetContextAttribute( UiConst.LabelXxlSpan, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签极宽尺寸响应式栅格偏移量 + /// + [Fact] + public void TestLabelXxlOffset() { + _wrapper.SetContextAttribute( UiConst.LabelXxlOffset, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签极宽尺寸响应式栅格顺序 + /// + [Fact] + public void TestLabelXxlOrder() { + _wrapper.SetContextAttribute( UiConst.LabelXxlOrder, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签极宽尺寸响应式栅格左移 + /// + [Fact] + public void TestLabelXxlPull() { + _wrapper.SetContextAttribute( UiConst.LabelXxlPull, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签极宽尺寸响应式栅格右移 + /// + [Fact] + public void TestLabelXxlPush() { + _wrapper.SetContextAttribute( UiConst.LabelXxlPush, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region 表单控件常规栅格测试 + + /// + /// 测试设置控件跨度 + /// + [Fact] + public void TestControlSpan() { + _wrapper.SetContextAttribute( UiConst.ControlSpan, 3 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置控件跨度 - 全部form control都会被设置 + /// + [Fact] + public void TestControlSpan_2() { + _wrapper.SetContextAttribute( UiConst.ControlSpan, 3 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var formItem2 = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem2 ); + + var formLabel2 = new FormLabelTagHelper().ToWrapper(); + formLabel2.AppendContent( "b" ); + formItem2.AppendContent( formLabel2 ); + + var formControl2 = new FormControlTagHelper().ToWrapper(); + formControl2.AppendContent( new InputTagHelper() ); + formItem2.AppendContent( formControl2 ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置控件跨度,表单先设置,使用表单容器进行覆盖 + /// + [Fact] + public void TestControlSpan_3() { + var form = new FormTagHelper().ToWrapper(); + form.SetContextAttribute( UiConst.ControlSpan, 2 ); + + _wrapper.SetContextAttribute( UiConst.ControlSpan, 3 ); + form.AppendContent( _wrapper ); + + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + + /// + /// 测试设置控件偏移量 + /// + [Fact] + public void TestControlOffset() { + _wrapper.SetContextAttribute( UiConst.ControlOffset, 3 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region 表单控件超窄尺寸栅格测试 + + /// + /// 测试控件超窄尺寸栅格 + /// + [Fact] + public void TestControlXs_1() { + _wrapper.SetContextAttribute( UiConst.ControlXs, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件超窄尺寸栅格,表单控件覆盖 + /// + [Fact] + public void TestControlXs_2() { + _wrapper.SetContextAttribute( UiConst.ControlXs, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formItem.AppendContent( formControl ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.ControlXs, "c" ); + formControl.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件超窄尺寸栅格,表单控件覆盖 + /// + [Fact] + public void TestControlXs_3() { + _wrapper.SetContextAttribute( UiConst.ControlXs, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.SetContextAttribute( UiConst.Xs, "c" ); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件超窄尺寸响应式栅格跨度 + /// + [Fact] + public void TestControlXsSpan() { + _wrapper.SetContextAttribute( UiConst.ControlXsSpan, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件超窄尺寸响应式栅格偏移量 + /// + [Fact] + public void TestControlXsOffset() { + _wrapper.SetContextAttribute( UiConst.ControlXsOffset, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件超窄尺寸响应式栅格顺序 + /// + [Fact] + public void TestControlXsOrder() { + _wrapper.SetContextAttribute( UiConst.ControlXsOrder, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件超窄尺寸响应式栅格左移 + /// + [Fact] + public void TestControlXsPull() { + _wrapper.SetContextAttribute( UiConst.ControlXsPull, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件超窄尺寸响应式栅格右移 + /// + [Fact] + public void TestControlXsPush() { + _wrapper.SetContextAttribute( UiConst.ControlXsPush, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region 表单控件窄尺寸栅格测试 + + /// + /// 测试控件窄尺寸栅格 + /// + [Fact] + public void TestControlSm_1() { + _wrapper.SetContextAttribute( UiConst.ControlSm, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件窄尺寸栅格,表单控件覆盖 + /// + [Fact] + public void TestControlSm_2() { + _wrapper.SetContextAttribute( UiConst.ControlSm, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formItem.AppendContent( formControl ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.ControlSm, "c" ); + formControl.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件窄尺寸栅格,表单控件覆盖 + /// + [Fact] + public void TestControlSm_3() { + _wrapper.SetContextAttribute( UiConst.ControlSm, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.SetContextAttribute( UiConst.Sm, "c" ); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件窄尺寸响应式栅格跨度 + /// + [Fact] + public void TestControlSmSpan() { + _wrapper.SetContextAttribute( UiConst.ControlSmSpan, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件窄尺寸响应式栅格偏移量 + /// + [Fact] + public void TestControlSmOffset() { + _wrapper.SetContextAttribute( UiConst.ControlSmOffset, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件窄尺寸响应式栅格顺序 + /// + [Fact] + public void TestControlSmOrder() { + _wrapper.SetContextAttribute( UiConst.ControlSmOrder, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件窄尺寸响应式栅格左移 + /// + [Fact] + public void TestControlSmPull() { + _wrapper.SetContextAttribute( UiConst.ControlSmPull, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件窄尺寸响应式栅格右移 + /// + [Fact] + public void TestControlSmPush() { + _wrapper.SetContextAttribute( UiConst.ControlSmPush, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region 表单控件中尺寸栅格测试 + + /// + /// 测试控件中尺寸栅格 + /// + [Fact] + public void TestControlMd_1() { + _wrapper.SetContextAttribute( UiConst.ControlMd, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件中尺寸栅格,表单控件覆盖 + /// + [Fact] + public void TestControlMd_2() { + _wrapper.SetContextAttribute( UiConst.ControlMd, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formItem.AppendContent( formControl ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.ControlMd, "c" ); + formControl.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件中尺寸栅格,表单控件覆盖 + /// + [Fact] + public void TestControlMd_3() { + _wrapper.SetContextAttribute( UiConst.ControlMd, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.SetContextAttribute( UiConst.Md, "c" ); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件中尺寸响应式栅格跨度 + /// + [Fact] + public void TestControlMdSpan() { + _wrapper.SetContextAttribute( UiConst.ControlMdSpan, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件中尺寸响应式栅格偏移量 + /// + [Fact] + public void TestControlMdOffset() { + _wrapper.SetContextAttribute( UiConst.ControlMdOffset, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件中尺寸响应式栅格顺序 + /// + [Fact] + public void TestControlMdOrder() { + _wrapper.SetContextAttribute( UiConst.ControlMdOrder, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件中尺寸响应式栅格左移 + /// + [Fact] + public void TestControlMdPull() { + _wrapper.SetContextAttribute( UiConst.ControlMdPull, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件中尺寸响应式栅格右移 + /// + [Fact] + public void TestControlMdPush() { + _wrapper.SetContextAttribute( UiConst.ControlMdPush, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region 表单控件宽尺寸栅格测试 + + /// + /// 测试控件宽尺寸栅格 + /// + [Fact] + public void TestControlLg_1() { + _wrapper.SetContextAttribute( UiConst.ControlLg, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件宽尺寸栅格,表单控件覆盖 + /// + [Fact] + public void TestControlLg_2() { + _wrapper.SetContextAttribute( UiConst.ControlLg, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formItem.AppendContent( formControl ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.ControlLg, "c" ); + formControl.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件宽尺寸栅格,表单控件覆盖 + /// + [Fact] + public void TestControlLg_3() { + _wrapper.SetContextAttribute( UiConst.ControlLg, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.SetContextAttribute( UiConst.Lg, "c" ); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件宽尺寸响应式栅格跨度 + /// + [Fact] + public void TestControlLgSpan() { + _wrapper.SetContextAttribute( UiConst.ControlLgSpan, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件宽尺寸响应式栅格偏移量 + /// + [Fact] + public void TestControlLgOffset() { + _wrapper.SetContextAttribute( UiConst.ControlLgOffset, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件宽尺寸响应式栅格顺序 + /// + [Fact] + public void TestControlLgOrder() { + _wrapper.SetContextAttribute( UiConst.ControlLgOrder, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件宽尺寸响应式栅格左移 + /// + [Fact] + public void TestControlLgPull() { + _wrapper.SetContextAttribute( UiConst.ControlLgPull, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件宽尺寸响应式栅格右移 + /// + [Fact] + public void TestControlLgPush() { + _wrapper.SetContextAttribute( UiConst.ControlLgPush, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region 表单控件超宽尺寸栅格测试 + + /// + /// 测试控件超宽尺寸栅格 + /// + [Fact] + public void TestControlXl_1() { + _wrapper.SetContextAttribute( UiConst.ControlXl, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件超宽尺寸栅格,表单控件覆盖 + /// + [Fact] + public void TestControlXl_2() { + _wrapper.SetContextAttribute( UiConst.ControlXl, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formItem.AppendContent( formControl ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.ControlXl, "c" ); + formControl.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件超宽尺寸栅格,表单控件覆盖 + /// + [Fact] + public void TestControlXl_3() { + _wrapper.SetContextAttribute( UiConst.ControlXl, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.SetContextAttribute( UiConst.Xl, "c" ); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件超宽尺寸响应式栅格跨度 + /// + [Fact] + public void TestControlXlSpan() { + _wrapper.SetContextAttribute( UiConst.ControlXlSpan, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件超宽尺寸响应式栅格偏移量 + /// + [Fact] + public void TestControlXlOffset() { + _wrapper.SetContextAttribute( UiConst.ControlXlOffset, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件超宽尺寸响应式栅格顺序 + /// + [Fact] + public void TestControlXlOrder() { + _wrapper.SetContextAttribute( UiConst.ControlXlOrder, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件超宽尺寸响应式栅格左移 + /// + [Fact] + public void TestControlXlPull() { + _wrapper.SetContextAttribute( UiConst.ControlXlPull, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件超宽尺寸响应式栅格右移 + /// + [Fact] + public void TestControlXlPush() { + _wrapper.SetContextAttribute( UiConst.ControlXlPush, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region 表单控件极宽尺寸栅格测试 + + /// + /// 测试控件极宽尺寸栅格 + /// + [Fact] + public void TestControlXxl_1() { + _wrapper.SetContextAttribute( UiConst.ControlXxl, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件极宽尺寸栅格,表单控件覆盖 + /// + [Fact] + public void TestControlXxl_2() { + _wrapper.SetContextAttribute( UiConst.ControlXxl, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formItem.AppendContent( formControl ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.ControlXxl, "c" ); + formControl.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件极宽尺寸栅格,表单控件覆盖 + /// + [Fact] + public void TestControlXxl_3() { + _wrapper.SetContextAttribute( UiConst.ControlXxl, "b" ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.SetContextAttribute( UiConst.Xxl, "c" ); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件极宽尺寸响应式栅格跨度 + /// + [Fact] + public void TestControlXxlSpan() { + _wrapper.SetContextAttribute( UiConst.ControlXxlSpan, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件极宽尺寸响应式栅格偏移量 + /// + [Fact] + public void TestControlXxlOffset() { + _wrapper.SetContextAttribute( UiConst.ControlXxlOffset, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件极宽尺寸响应式栅格顺序 + /// + [Fact] + public void TestControlXxlOrder() { + _wrapper.SetContextAttribute( UiConst.ControlXxlOrder, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件极宽尺寸响应式栅格左移 + /// + [Fact] + public void TestControlXxlPull() { + _wrapper.SetContextAttribute( UiConst.ControlXxlPull, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试控件极宽尺寸响应式栅格右移 + /// + [Fact] + public void TestControlXxlPush() { + _wrapper.SetContextAttribute( UiConst.ControlXxlPush, 1 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Forms/FormControlTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Forms/FormControlTagHelperTest.cs new file mode 100644 index 000000000..3e385f029 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Forms/FormControlTagHelperTest.cs @@ -0,0 +1,438 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Forms; +using Util.Ui.NgZorro.Components.Inputs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Forms { + /// + /// 表单域测试 + /// + public class FormControlTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public FormControlTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new FormControlTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试校验状态 + /// + [Fact] + public void TestValidateStatus() { + _wrapper.SetContextAttribute( UiConst.ValidateStatus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试展示校验状态图标 + /// + [Fact] + public void TestHasFeedback() { + _wrapper.SetContextAttribute( UiConst.HasFeedback, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试额外提示 + /// + [Fact] + public void TestExtra() { + _wrapper.SetContextAttribute( UiConst.Extra, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试额外提示 - 多语言 + /// + [Fact] + public void TestExtra_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Extra, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试额外提示 + /// + [Fact] + public void TestBindExtra() { + _wrapper.SetContextAttribute( AngularConst.BindExtra, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试校验成功状态提示 + /// + [Fact] + public void TestSuccessTip() { + _wrapper.SetContextAttribute( UiConst.SuccessTip, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试校验成功状态提示 + /// + [Fact] + public void TestBindSuccessTip() { + _wrapper.SetContextAttribute( AngularConst.BindSuccessTip, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试校验警告状态提示 + /// + [Fact] + public void TestWarningTip() { + _wrapper.SetContextAttribute( UiConst.WarningTip, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试校验警告状态提示 + /// + [Fact] + public void TestBindWarningTip() { + _wrapper.SetContextAttribute( AngularConst.BindWarningTip, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试校验错误状态提示 + /// + [Fact] + public void TestErrorTip() { + _wrapper.SetContextAttribute( UiConst.ErrorTip, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试校验错误状态提示 + /// + [Fact] + public void TestBindErrorTip() { + _wrapper.SetContextAttribute( AngularConst.BindErrorTip, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试校验中状态提示 + /// + [Fact] + public void TestValidatingTip() { + _wrapper.SetContextAttribute( UiConst.ValidatingTip, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试校验中状态提示 + /// + [Fact] + public void TestBindValidatingTip() { + _wrapper.SetContextAttribute( AngularConst.BindValidatingTip, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动提示 + /// + [Fact] + public void TestAutoTips() { + _wrapper.SetContextAttribute( UiConst.AutoTips, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用自动提示 + /// + [Fact] + public void TestDisableAutoTips() { + _wrapper.SetContextAttribute( UiConst.DisableAutoTips, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试跨度 + /// + [Fact] + public void TestSpan() { + _wrapper.SetContextAttribute( UiConst.Span, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置表单和表单容器控件跨度,使用控件属性进行覆盖 + /// + [Fact] + public void TestSpan_2() { + var form = new FormTagHelper().ToWrapper(); + form.SetContextAttribute( UiConst.ControlSpan, 2 ); + + var formContainer = new FormContainerTagHelper().ToWrapper(); + formContainer.SetContextAttribute( UiConst.ControlSpan, 3 ); + form.AppendContent( formContainer ); + + var formItem = new FormItemTagHelper().ToWrapper(); + formContainer.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.SetContextAttribute( UiConst.Span, 5 ); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + + /// + /// 测试偏移量 + /// + [Fact] + public void TestOffset() { + _wrapper.SetContextAttribute( UiConst.Offset, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试左移 + /// + [Fact] + public void TestPull() { + _wrapper.SetContextAttribute( UiConst.Pull, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试右移 + /// + [Fact] + public void TestPush() { + _wrapper.SetContextAttribute( UiConst.Push, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试栅格顺序 + /// + [Fact] + public void TestOrder() { + _wrapper.SetContextAttribute( UiConst.Order, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试flex布局 + /// + [Fact] + public void TestFlex() { + _wrapper.SetContextAttribute( UiConst.Flex, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试flex布局 + /// + [Fact] + public void TestBindFlex() { + _wrapper.SetContextAttribute( AngularConst.BindFlex, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试超窄尺寸栅格跨度 + /// + [Fact] + public void TestXsSpan() { + _wrapper.SetContextAttribute( UiConst.XsSpan, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试窄尺寸栅格跨度 + /// + [Fact] + public void TestSmSpan() { + _wrapper.SetContextAttribute( UiConst.SmSpan, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试中尺寸栅格跨度 + /// + [Fact] + public void TestMdSpan() { + _wrapper.SetContextAttribute( UiConst.MdSpan, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽尺寸栅格跨度 + /// + [Fact] + public void TestLgSpan() { + _wrapper.SetContextAttribute( UiConst.LgSpan, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试超宽尺寸栅格跨度 + /// + [Fact] + public void TestXlSpan() { + _wrapper.SetContextAttribute( UiConst.XlSpan, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试极宽尺寸栅格跨度 + /// + [Fact] + public void TestXxlSpan() { + _wrapper.SetContextAttribute( UiConst.XxlSpan, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Forms/FormItemTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Forms/FormItemTagHelperTest.cs new file mode 100644 index 000000000..86677e72c --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Forms/FormItemTagHelperTest.cs @@ -0,0 +1,112 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Forms; +using Util.Ui.NgZorro.Components.Inputs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Forms { + /// + /// 表单项测试 + /// + public class FormItemTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public FormItemTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new FormItemTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试垂直对齐方式 + /// + [Fact] + public void TestAlign() { + _wrapper.SetContextAttribute( UiConst.Align, Align.Middle ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试垂直对齐方式 + /// + [Fact] + public void TestBindAlign() { + _wrapper.SetContextAttribute( AngularConst.BindAlign, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间隔 + /// + [Fact] + public void TestGutter() { + _wrapper.SetContextAttribute( UiConst.Gutter, "2" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水平排列方式 + /// + [Fact] + public void TestJustify() { + _wrapper.SetContextAttribute( UiConst.Justify, Justify.Center ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水平排列方式 + /// + [Fact] + public void TestBindJustify() { + _wrapper.SetContextAttribute( AngularConst.BindJustify, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Forms/FormLabelTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Forms/FormLabelTagHelperTest.cs new file mode 100644 index 000000000..44457abe4 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Forms/FormLabelTagHelperTest.cs @@ -0,0 +1,426 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Forms; +using Util.Ui.NgZorro.Components.Inputs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Forms { + /// + /// 表单标签测试 + /// + public class FormLabelTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public FormLabelTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new FormLabelTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试必填样式 + /// + [Fact] + public void TestRequired() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试不显示冒号 + /// + [Fact] + public void TestNoColon() { + _wrapper.SetContextAttribute( UiConst.NoColon, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签文本是否换行 + /// + [Fact] + public void TestLabelWrap() { + _wrapper.SetContextAttribute( UiConst.LabelWrap, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签文本是否换行 + /// + [Fact] + public void TestLabelAlign() { + _wrapper.SetContextAttribute( UiConst.LabelAlign, LabelAlign.Right ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签文本是否换行 + /// + [Fact] + public void TestBindLabelAlign() { + _wrapper.SetContextAttribute( AngularConst.BindLabelAlign, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试NzFor + /// + [Fact] + public void TestNzFor() { + _wrapper.SetContextAttribute( UiConst.NzFor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签for + /// + [Fact] + public void TestBindLabelFor() { + _wrapper.SetContextAttribute( AngularConst.BindNzFor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示信息 + /// + [Fact] + public void TestTooltipTitle() { + _wrapper.SetContextAttribute( UiConst.TooltipTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示信息 + /// + [Fact] + public void TestBindTooltipTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTooltipTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示图标 + /// + [Fact] + public void TestTooltipIcon() { + _wrapper.SetContextAttribute( UiConst.TooltipIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示图标 + /// + [Fact] + public void TestBindTooltipIcon() { + _wrapper.SetContextAttribute( AngularConst.BindTooltipIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试跨度 + /// + [Fact] + public void TestSpan() { + _wrapper.SetContextAttribute( UiConst.Span, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置表单和表单容器标签跨度,使用标签属性进行覆盖 + /// + [Fact] + public void TestSpan_2() { + var form = new FormTagHelper().ToWrapper(); + form.SetContextAttribute( UiConst.LabelSpan, 2 ); + + var formContainer = new FormContainerTagHelper().ToWrapper(); + formContainer.SetContextAttribute( UiConst.LabelSpan, 3 ); + form.AppendContent( formContainer ); + + var formItem = new FormItemTagHelper().ToWrapper(); + formContainer.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.SetContextAttribute( UiConst.Span, 5 ); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + + /// + /// 测试偏移量 + /// + [Fact] + public void TestOffset() { + _wrapper.SetContextAttribute( UiConst.Offset, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试左移 + /// + [Fact] + public void TestPull() { + _wrapper.SetContextAttribute( UiConst.Pull, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试右移 + /// + [Fact] + public void TestPush() { + _wrapper.SetContextAttribute( UiConst.Push, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试栅格顺序 + /// + [Fact] + public void TestOrder() { + _wrapper.SetContextAttribute( UiConst.Order, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试flex布局 + /// + [Fact] + public void TestFlex() { + _wrapper.SetContextAttribute( UiConst.Flex, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试flex布局 + /// + [Fact] + public void TestBindFlex() { + _wrapper.SetContextAttribute( AngularConst.BindFlex, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试超窄尺寸栅格跨度 + /// + [Fact] + public void TestXsSpan() { + _wrapper.SetContextAttribute( UiConst.XsSpan, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试窄尺寸栅格跨度 + /// + [Fact] + public void TestSmSpan() { + _wrapper.SetContextAttribute( UiConst.SmSpan, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试中尺寸栅格跨度 + /// + [Fact] + public void TestMdSpan() { + _wrapper.SetContextAttribute( UiConst.MdSpan, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽尺寸栅格跨度 + /// + [Fact] + public void TestLgSpan() { + _wrapper.SetContextAttribute( UiConst.LgSpan, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试超宽尺寸栅格跨度 + /// + [Fact] + public void TestXlSpan() { + _wrapper.SetContextAttribute( UiConst.XlSpan, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试极宽尺寸栅格跨度 + /// + [Fact] + public void TestXxlSpan() { + _wrapper.SetContextAttribute( UiConst.XxlSpan, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本 + /// + [Fact] + public void TestText() { + _wrapper.SetContextAttribute( UiConst.Text, "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本 - 多语言 + /// + [Fact] + public void TestText_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Text, "a" ); + var result = new StringBuilder(); + result.Append( "{{'a'|i18n}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 + /// + [Fact] + public void TestFor() { + _wrapper.SetExpression( t => t.Code ); + var result = new StringBuilder(); + result.Append( "code" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 多语言 + /// + [Fact] + public void TestFor_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetExpression( t => t.Code ); + var result = new StringBuilder(); + result.Append( "{{'code'|i18n}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Forms/FormSplitTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Forms/FormSplitTagHelperTest.cs new file mode 100644 index 000000000..72b118bcf --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Forms/FormSplitTagHelperTest.cs @@ -0,0 +1,48 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Forms; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Forms { + /// + /// 表单分隔符测试 + /// + public class FormSplitTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public FormSplitTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new FormSplitTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Forms/FormTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Forms/FormTagHelperTest.cs new file mode 100644 index 000000000..3ac447589 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Forms/FormTagHelperTest.cs @@ -0,0 +1,655 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Forms; +using Util.Ui.NgZorro.Components.Inputs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Forms { + /// + /// 表单测试 + /// + public partial class FormTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public FormTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new FormTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加标识 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试布局 + /// + [Fact] + public void TestLayout() { + _wrapper.SetContextAttribute( UiConst.Layout, FormLayout.Inline ); + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试布局 + /// + [Fact] + public void TestBindLayout() { + _wrapper.SetContextAttribute( AngularConst.BindLayout, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动提示 + /// + [Fact] + public void TestAutoTips() { + _wrapper.SetContextAttribute( UiConst.AutoTips, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用自动提示 + /// + [Fact] + public void TestDisableAutoTips() { + _wrapper.SetContextAttribute( UiConst.DisableAutoTips, "true" ); + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试不显示标签冒号 + /// + [Fact] + public void TestNoColon() { + _wrapper.SetContextAttribute( UiConst.NoColon, "true" ); + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签文本是否换行 + /// + [Fact] + public void TestLabelWrap() { + _wrapper.SetContextAttribute( UiConst.LabelWrap, "true" ); + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签文本是否换行 + /// + [Fact] + public void TestLabelAlign() { + _wrapper.SetContextAttribute( UiConst.LabelAlign, LabelAlign.Right ); + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签文本是否换行 + /// + [Fact] + public void TestBindLabelAlign() { + _wrapper.SetContextAttribute( AngularConst.BindLabelAlign, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签提示图标 + /// + [Fact] + public void TestTooltipIcon() { + _wrapper.SetContextAttribute( UiConst.TooltipIcon, AntDesignIcon.AlignCenter ); + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签提示图标 + /// + [Fact] + public void TestBindTooltipIcon() { + _wrapper.SetContextAttribute( AngularConst.BindTooltipIcon, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动完成打开 + /// + [Fact] + public void TestAutoComplete_On() { + _wrapper.SetContextAttribute( UiConst.Autocomplete, true ); + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动完成关闭 + /// + [Fact] + public void TestAutoComplete_Off() { + _wrapper.SetContextAttribute( UiConst.Autocomplete, false ); + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置 autocomplete="off" + /// + [Fact] + public void TestEnableAutocompleteOff() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableAutocompleteOff = true } ); + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表单组 + /// + [Fact] + public void TestFormGroup() { + _wrapper.SetContextAttribute( UiConst.FormGroup, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表单组 - 设置id + /// + [Fact] + public void TestFormGroup_2() { + _wrapper.SetContextAttribute( UiConst.Id, "id" ); + _wrapper.SetContextAttribute( UiConst.FormGroup, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否不验证表单 + /// + [Fact] + public void TestNovalidate() { + _wrapper.SetContextAttribute( UiConst.Novalidate, true ); + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "
          a
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提交事件 + /// + [Fact] + public void TestOnSubmit() { + _wrapper.SetContextAttribute( UiConst.OnSubmit, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动设置标签for + /// + [Fact] + public void TestAutoNzFor_1() { + _wrapper.SetContextAttribute( UiConst.AutoNzFor, true ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动设置标签for - 全局设置 + /// + [Fact] + public void TestAutoNzFor_2() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableAutoNzFor = true } ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动设置标签for - 覆盖全局设置 + /// + [Fact] + public void TestAutoNzFor_3() { + _wrapper.SetContextAttribute( UiConst.AutoNzFor, false ); + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableAutoNzFor = true } ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置标签跨度 + /// + [Fact] + public void TestLabelSpan() { + _wrapper.SetContextAttribute( UiConst.LabelSpan, 3 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置标签偏移量 + /// + [Fact] + public void TestLabelOffset() { + _wrapper.SetContextAttribute( UiConst.LabelOffset, 3 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置控件跨度 + /// + [Fact] + public void TestControlSpan() { + _wrapper.SetContextAttribute( UiConst.ControlSpan, 3 ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置垂直对齐方式 - 手工创建formitem + /// + [Fact] + public void TestAlign() { + _wrapper.SetContextAttribute( UiConst.Align, Align.Middle ); + var formItem = new FormItemTagHelper().ToWrapper(); + _wrapper.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( new InputTagHelper() ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置垂直对齐方式 - 自动生成formitem + /// + [Fact] + public void TestAlign_2() { + _wrapper.SetContextAttribute( UiConst.Align, Align.Middle ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelText, "a" ); + _wrapper.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置是否显示标签 + /// + [Fact] + public void TestShowLabel() { + _wrapper.SetContextAttribute( UiConst.ShowLabel, false ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelText, "a" ); + _wrapper.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置标签宽度 + /// + [Fact] + public void TestLabelWidth() { + _wrapper.SetContextAttribute( UiConst.LabelWidth, "100" ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelText, "a" ); + _wrapper.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置标签宽度 - 控件覆盖表单设置 + /// + [Fact] + public void TestLabelWidth_2() { + _wrapper.SetContextAttribute( UiConst.LabelWidth, "100" ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelText, "a" ); + input.SetContextAttribute( UiConst.LabelWidth, "120" ); + _wrapper.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示校验状态反馈图标 - 未验证不设置 + /// + [Fact] + public void TestHasFeedback_1() { + _wrapper.SetContextAttribute( UiConst.HasFeedback,true ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelText, "a" ); + _wrapper.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示校验状态反馈图标 + /// + [Fact] + public void TestHasFeedback_2() { + _wrapper.SetContextAttribute( UiConst.HasFeedback, true ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( AngularConst.NgModel, "model" ); + input.SetContextAttribute( UiConst.Required, "a" ); + _wrapper.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示校验状态反馈图标 - 表单组件覆盖 + /// + [Fact] + public void TestHasFeedback_3() { + _wrapper.SetContextAttribute( UiConst.HasFeedback, true ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( AngularConst.NgModel, "model" ); + input.SetContextAttribute( UiConst.Required, "a" ); + input.SetContextAttribute( UiConst.HasFeedback, false ); + _wrapper.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Forms/FormTextTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Forms/FormTextTagHelperTest.cs new file mode 100644 index 000000000..07f2b7140 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Forms/FormTextTagHelperTest.cs @@ -0,0 +1,48 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Forms; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Forms { + /// + /// 表单文本测试 + /// + public class FormTextTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public FormTextTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new FormTextTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Forms/SearchFormTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Forms/SearchFormTagHelperTest.cs new file mode 100644 index 000000000..3e6835b8e --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Forms/SearchFormTagHelperTest.cs @@ -0,0 +1,264 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Forms; +using Util.Ui.NgZorro.Components.Grids; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Forms; + +/// +/// 表单测试 - 查询表单 +/// +public class SearchFormTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public SearchFormTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new SearchFormTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试查询表单 - 2个栅格列 - 1个查询条件,1个查询操作 + /// + [Fact] + public void Test_1() { + //添加1个查询条件 + var column = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column ); + + //添加查询操作 + var columnAction = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( columnAction ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试查询表单 - 3个栅格列 - 2个查询条件,1个查询操作 + /// + [Fact] + public void Test_2() { + //添加2个栅格列 + var column = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column ); + + var column2 = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column2 ); + + //添加查询操作 + var columnAction = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( columnAction ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试查询表单 - 4个栅格列 - 3个查询条件,1个查询操作 + /// + [Fact] + public void Test_3() { + //添加3个栅格列 + var column = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column ); + + var column2 = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column2 ); + + var column3 = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column3 ); + + //添加查询操作 + var columnAction = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( columnAction ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试查询表单 - 5个栅格列 - 4个查询条件,1个查询操作 + /// + [Fact] + public void Test_4() { + //添加4个栅格列 + var column = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column ); + + var column2 = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column2 ); + + var column3 = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column3 ); + + var column4 = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column4 ); + + //添加查询操作 + var columnAction = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( columnAction ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试查询表单 - 初始显示2个条件,每行显示6个条件 + /// + [Fact] + public void Test_5() { + //设置为查询表单 + _wrapper.SetContextAttribute( UiConst.ShowNumber, 2 ); + _wrapper.SetContextAttribute( UiConst.ColumnsNumber, 6 ); + + //添加8个条件 + var column = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column ); + + var column2 = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column2 ); + + var column3 = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column3 ); + + var column4 = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column4 ); + + var column5 = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column5 ); + + var column6 = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column6 ); + + var column7 = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column7 ); + + var column8 = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column8 ); + + //添加查询操作 + var columnAction = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( columnAction ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试查询表单 - 手工覆盖操作按钮栅格属性 + /// + [Fact] + public void Test_6() { + //添加2个栅格列 + var column = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column ); + + var column2 = new ColumnTagHelper().ToWrapper(); + _wrapper.AppendContent( column2 ); + + //添加查询操作 + var columnAction = new ColumnTagHelper().ToWrapper(); + columnAction.SetContextAttribute( UiConst.Md, "a" ); + columnAction.SetContextAttribute( UiConst.Lg, "a" ); + columnAction.SetContextAttribute( UiConst.Xl, "a" ); + columnAction.SetContextAttribute( UiConst.Xxl, "a" ); + _wrapper.AppendContent( columnAction ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest .Sm.cs b/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest .Sm.cs new file mode 100644 index 000000000..c93b47917 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest .Sm.cs @@ -0,0 +1,98 @@ +using System.Text; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Grids { + /// + /// 栅格列测试 - Sm窄尺寸响应式栅格 + /// + public partial class ColumnTagHelperTest { + /// + /// 测试窄尺寸栅格跨度 + /// + [Fact] + public void TestSmSpan() { + _wrapper.SetContextAttribute( UiConst.SmSpan, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试窄尺寸栅格偏移量 + /// + [Fact] + public void TestSmOffset() { + _wrapper.SetContextAttribute( UiConst.SmOffset, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试窄尺寸栅格顺序 + /// + [Fact] + public void TestSmOrder() { + _wrapper.SetContextAttribute( UiConst.SmOrder, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试窄尺寸栅格左移 + /// + [Fact] + public void TestSmPull() { + _wrapper.SetContextAttribute( UiConst.SmPull, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试窄尺寸栅格右移 + /// + [Fact] + public void TestSmPush() { + _wrapper.SetContextAttribute( UiConst.SmPush, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试窄尺寸栅格 + /// + [Fact] + public void TestSm_1() { + _wrapper.SetContextAttribute( UiConst.Sm, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试同时设置窄尺寸栅格跨度和右移 + /// + [Fact] + public void TestSm_2() { + _wrapper.SetContextAttribute( UiConst.SmSpan, 1 ).SetContextAttribute( UiConst.SmPush, 2 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试同时设置窄尺寸栅格Sm和SmSpan属性,Sm生效 + /// + [Fact] + public void TestSm_3() { + _wrapper.SetContextAttribute( UiConst.Sm, "a" ).SetContextAttribute( UiConst.SmSpan, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest.Lg.cs b/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest.Lg.cs new file mode 100644 index 000000000..6ae886365 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest.Lg.cs @@ -0,0 +1,99 @@ +using System.Text; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Grids { + /// + /// 栅格列测试 - Lg宽尺寸响应式栅格 + /// + public partial class ColumnTagHelperTest { + /// + /// 测试宽尺寸栅格跨度 + /// + [Fact] + public void TestLgSpan() { + _wrapper.SetContextAttribute( UiConst.LgSpan, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽尺寸栅格偏移量 + /// + [Fact] + public void TestLgOffset() { + _wrapper.SetContextAttribute( UiConst.LgOffset, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽尺寸栅格顺序 + /// + [Fact] + public void TestLgOrder() { + _wrapper.SetContextAttribute( UiConst.LgOrder, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽尺寸栅格左移 + /// + [Fact] + public void TestLgPull() { + _wrapper.SetContextAttribute( UiConst.LgPull, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽尺寸栅格右移 + /// + [Fact] + public void TestLgPush() { + _wrapper.SetContextAttribute( UiConst.LgPush, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽尺寸栅格 + /// + [Fact] + public void TestLg_1() { + _wrapper.SetContextAttribute( UiConst.Lg, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试同时设置宽尺寸栅格跨度和右移 + /// + [Fact] + public void TestLg_2() { + _wrapper.SetContextAttribute( UiConst.LgSpan, 1 ).SetContextAttribute( UiConst.LgPush, 2 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试同时设置宽尺寸栅格Lg和LgSpan属性,Lg生效 + /// + [Fact] + public void TestLg_3() { + _wrapper.SetContextAttribute( UiConst.Lg, "a" ).SetContextAttribute( UiConst.LgSpan, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest.Md.cs b/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest.Md.cs new file mode 100644 index 000000000..8595f38b1 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest.Md.cs @@ -0,0 +1,99 @@ +using System.Text; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Grids { + /// + /// 栅格列测试 - Md中尺寸响应式栅格 + /// + public partial class ColumnTagHelperTest { + /// + /// 测试中尺寸栅格跨度 + /// + [Fact] + public void TestMdSpan() { + _wrapper.SetContextAttribute( UiConst.MdSpan, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试中尺寸栅格偏移量 + /// + [Fact] + public void TestMdOffset() { + _wrapper.SetContextAttribute( UiConst.MdOffset, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试中尺寸栅格顺序 + /// + [Fact] + public void TestMdOrder() { + _wrapper.SetContextAttribute( UiConst.MdOrder, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试中尺寸栅格左移 + /// + [Fact] + public void TestMdPull() { + _wrapper.SetContextAttribute( UiConst.MdPull, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试中尺寸栅格右移 + /// + [Fact] + public void TestMdPush() { + _wrapper.SetContextAttribute( UiConst.MdPush, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试中尺寸栅格 + /// + [Fact] + public void TestMd_1() { + _wrapper.SetContextAttribute( UiConst.Md, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试同时设置中尺寸栅格跨度和右移 + /// + [Fact] + public void TestMd_2() { + _wrapper.SetContextAttribute( UiConst.MdSpan, 1 ).SetContextAttribute( UiConst.MdPush, 2 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试同时设置中尺寸栅格Md和MdSpan属性,Md生效 + /// + [Fact] + public void TestMd_3() { + _wrapper.SetContextAttribute( UiConst.Md, "a" ).SetContextAttribute( UiConst.MdSpan, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest.XXl.cs b/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest.XXl.cs new file mode 100644 index 000000000..414668002 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest.XXl.cs @@ -0,0 +1,98 @@ +using System.Text; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Grids { + /// + /// 栅格列测试 - XXl极宽尺寸响应式栅格 + /// + public partial class ColumnTagHelperTest { + /// + /// 测试极宽尺寸栅格跨度 + /// + [Fact] + public void TestXxlSpan() { + _wrapper.SetContextAttribute( UiConst.XxlSpan, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试极宽尺寸栅格偏移量 + /// + [Fact] + public void TestXxlOffset() { + _wrapper.SetContextAttribute( UiConst.XxlOffset, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试极宽尺寸栅格顺序 + /// + [Fact] + public void TestXxlOrder() { + _wrapper.SetContextAttribute( UiConst.XxlOrder, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试极宽尺寸栅格左移 + /// + [Fact] + public void TestXxlPull() { + _wrapper.SetContextAttribute( UiConst.XxlPull, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试极宽尺寸栅格右移 + /// + [Fact] + public void TestXxlPush() { + _wrapper.SetContextAttribute( UiConst.XxlPush, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试极宽尺寸栅格 + /// + [Fact] + public void TestXxl_1() { + _wrapper.SetContextAttribute( UiConst.Xxl, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试同时设置极宽尺寸栅格跨度和右移 + /// + [Fact] + public void TestXxl_2() { + _wrapper.SetContextAttribute( UiConst.XxlSpan, 1 ).SetContextAttribute( UiConst.XxlPush, 2 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试同时设置极宽尺寸栅格Xxl和XxlSpan属性,Xxl生效 + /// + [Fact] + public void TestXxl_3() { + _wrapper.SetContextAttribute( UiConst.Xxl, "a" ).SetContextAttribute( UiConst.XxlSpan, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest.Xl.cs b/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest.Xl.cs new file mode 100644 index 000000000..c0684fb73 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest.Xl.cs @@ -0,0 +1,99 @@ +using System.Text; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Grids { + /// + /// 栅格列测试 - Xl超宽尺寸响应式栅格 + /// + public partial class ColumnTagHelperTest { + /// + /// 测试超宽尺寸栅格跨度 + /// + [Fact] + public void TestXlSpan() { + _wrapper.SetContextAttribute( UiConst.XlSpan, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试超宽尺寸栅格偏移量 + /// + [Fact] + public void TestXlOffset() { + _wrapper.SetContextAttribute( UiConst.XlOffset, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试超宽尺寸栅格顺序 + /// + [Fact] + public void TestXlOrder() { + _wrapper.SetContextAttribute( UiConst.XlOrder, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试超宽尺寸栅格左移 + /// + [Fact] + public void TestXlPull() { + _wrapper.SetContextAttribute( UiConst.XlPull, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试超宽尺寸栅格右移 + /// + [Fact] + public void TestXlPush() { + _wrapper.SetContextAttribute( UiConst.XlPush, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试超宽尺寸栅格 + /// + [Fact] + public void TestXl_1() { + _wrapper.SetContextAttribute( UiConst.Xl, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试同时设置超宽尺寸栅格跨度和右移 + /// + [Fact] + public void TestXl_2() { + _wrapper.SetContextAttribute( UiConst.XlSpan, 1 ).SetContextAttribute( UiConst.XlPush, 2 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试同时设置超宽尺寸栅格Xl和XlSpan属性,Xl生效 + /// + [Fact] + public void TestXl_3() { + _wrapper.SetContextAttribute( UiConst.Xl, "a" ).SetContextAttribute( UiConst.XlSpan, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest.Xs.cs b/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest.Xs.cs new file mode 100644 index 000000000..8d7e2c270 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest.Xs.cs @@ -0,0 +1,99 @@ +using System.Text; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Grids { + /// + /// 栅格列测试 - Xs超窄尺寸响应式栅格 + /// + public partial class ColumnTagHelperTest { + /// + /// 测试超窄尺寸栅格跨度 + /// + [Fact] + public void TestXsSpan() { + _wrapper.SetContextAttribute( UiConst.XsSpan, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试超窄尺寸栅格偏移量 + /// + [Fact] + public void TestXsOffset() { + _wrapper.SetContextAttribute( UiConst.XsOffset, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试超窄尺寸栅格顺序 + /// + [Fact] + public void TestXsOrder() { + _wrapper.SetContextAttribute( UiConst.XsOrder, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试超窄尺寸栅格左移 + /// + [Fact] + public void TestXsPull() { + _wrapper.SetContextAttribute( UiConst.XsPull, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试超窄尺寸栅格右移 + /// + [Fact] + public void TestXsPush() { + _wrapper.SetContextAttribute( UiConst.XsPush, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试超窄尺寸栅格 + /// + [Fact] + public void TestXs_1() { + _wrapper.SetContextAttribute( UiConst.Xs, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试同时设置超窄尺寸栅格跨度和右移 + /// + [Fact] + public void TestXs_2() { + _wrapper.SetContextAttribute( UiConst.XsSpan, 1 ).SetContextAttribute( UiConst.XsPush, 2 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试同时设置超窄尺寸栅格Xs和XsSpan属性,Xs生效 + /// + [Fact] + public void TestXs_3() { + _wrapper.SetContextAttribute( UiConst.Xs, "a" ).SetContextAttribute( UiConst.XsSpan, 1 ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest.cs new file mode 100644 index 000000000..275e4a283 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Grids/ColumnTagHelperTest.cs @@ -0,0 +1,127 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Grids; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Grids { + /// + /// 栅格列测试 + /// + public partial class ColumnTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ColumnTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ColumnTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试跨度 + /// + [Fact] + public void TestSpan() { + _wrapper.SetContextAttribute( UiConst.Span, "1" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试偏移量 + /// + [Fact] + public void TestOffset() { + _wrapper.SetContextAttribute( UiConst.Offset, "1" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试左移 + /// + [Fact] + public void TestPull() { + _wrapper.SetContextAttribute( UiConst.Pull, "1" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试右移 + /// + [Fact] + public void TestPush() { + _wrapper.SetContextAttribute( UiConst.Push, "1" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试栅格顺序 + /// + [Fact] + public void TestOrder() { + _wrapper.SetContextAttribute( UiConst.Order, "1" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试flex布局 + /// + [Fact] + public void TestFlex() { + _wrapper.SetContextAttribute( UiConst.Flex, "1" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试flex布局 + /// + [Fact] + public void TestBindFlex() { + _wrapper.SetContextAttribute( AngularConst.BindFlex, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Grids/RowTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Grids/RowTagHelperTest.cs new file mode 100644 index 000000000..6720579a4 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Grids/RowTagHelperTest.cs @@ -0,0 +1,106 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Grids; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Grids { + /// + /// 栅格行测试 + /// + public class RowTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public RowTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new RowTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试垂直对齐方式 + /// + [Fact] + public void TestAlign() { + _wrapper.SetContextAttribute( UiConst.Align, Align.Middle ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试垂直对齐方式 + /// + [Fact] + public void TestBindAlign() { + _wrapper.SetContextAttribute( AngularConst.BindAlign, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间隔 + /// + [Fact] + public void TestGutter() { + _wrapper.SetContextAttribute( UiConst.Gutter, "2" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水平排列方式 + /// + [Fact] + public void TestJustify() { + _wrapper.SetContextAttribute( UiConst.Justify, Justify.Center ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水平排列方式 + /// + [Fact] + public void TestBindJustify() { + _wrapper.SetContextAttribute( AngularConst.BindJustify, "a" ); + var result = new StringBuilder(); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/HashCodes/HashCodeTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/HashCodes/HashCodeTagHelperTest.cs new file mode 100644 index 000000000..bd10a64d5 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/HashCodes/HashCodeTagHelperTest.cs @@ -0,0 +1,183 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.HashCodes; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.HashCodes; + +/// +/// 哈希码测试 +/// +public class HashCodeTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public HashCodeTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new HashCodeTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestValue() { + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestBindValue() { + _wrapper.SetContextAttribute( AngularConst.BindValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标志 + /// + [Fact] + public void TestLogo() { + _wrapper.SetContextAttribute( UiConst.Logo, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标志 + /// + [Fact] + public void TestBindLogo() { + _wrapper.SetContextAttribute( AngularConst.BindLogo, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试展示模式 + /// + [Fact] + public void TestMode() { + _wrapper.SetContextAttribute( UiConst.Mode, HashCodeMode.Rect ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试展示模式 + /// + [Fact] + public void TestBindMode() { + _wrapper.SetContextAttribute( AngularConst.BindMode, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试样式 + /// + [Fact] + public void TestType() { + _wrapper.SetContextAttribute( UiConst.Type, HashCodeType.Primary ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试样式 + /// + [Fact] + public void TestBindType() { + _wrapper.SetContextAttribute( AngularConst.BindType, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复制事件 + /// + [Fact] + public void TestOnCopy() { + _wrapper.SetContextAttribute( UiConst.OnCopy, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Icons/IconTagHelperTest.Popover.cs b/test/Util.Ui.NgZorro.Tests/Icons/IconTagHelperTest.Popover.cs new file mode 100644 index 000000000..8fa20856d --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Icons/IconTagHelperTest.Popover.cs @@ -0,0 +1,34 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Enums; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Icons { + /// + /// 图标测试 - 气泡卡片相关测试 + /// + public partial class IconTagHelperTest { + /// + /// 测试气泡卡片标题 + /// + [Fact] + public void TestPopoverTitle() { + _wrapper.SetContextAttribute( UiConst.PopoverTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试气泡卡片标题 + /// + [Fact] + public void TestBindPopoverTitle() { + _wrapper.SetContextAttribute( AngularConst.BindPopoverTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Icons/IconTagHelperTest.Tooltip.cs b/test/Util.Ui.NgZorro.Tests/Icons/IconTagHelperTest.Tooltip.cs new file mode 100644 index 000000000..872ad7b1f --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Icons/IconTagHelperTest.Tooltip.cs @@ -0,0 +1,21 @@ +using System.Text; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Icons { + /// + /// 图标测试 - 工具提示相关测试 + /// + public partial class IconTagHelperTest { + /// + /// 测试提示文字 + /// + [Fact] + public void TestTooltipTitle() { + _wrapper.SetContextAttribute( UiConst.TooltipTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Icons/IconTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Icons/IconTagHelperTest.cs new file mode 100644 index 000000000..c36beac30 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Icons/IconTagHelperTest.cs @@ -0,0 +1,216 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Icons; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Icons { + /// + /// 图标测试 + /// + public partial class IconTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public IconTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new IconTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标识 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标类型 + /// + [Fact] + public void TestType() { + _wrapper.SetContextAttribute( UiConst.Type, AntDesignIcon.InfoCircle ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标类型 + /// + [Fact] + public void TestBindType() { + _wrapper.SetContextAttribute( AngularConst.BindType, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标主题 + /// + [Fact] + public void TestTheme() { + _wrapper.SetContextAttribute( UiConst.Theme, IconTheme.Outline ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标主题 + /// + [Fact] + public void TestBindThme() { + _wrapper.SetContextAttribute( AngularConst.BindTheme, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标持续旋转 + /// + [Fact] + public void TestSpin() { + _wrapper.SetContextAttribute( UiConst.Spin, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标旋转角度 + /// + [Fact] + public void TestRotate() { + _wrapper.SetContextAttribute( UiConst.Rotate, "180" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试双色图标主题色 + /// + [Fact] + public void TestTwotoneColorType() { + _wrapper.SetContextAttribute( UiConst.TwotoneColorType, AntDesignColor.Red ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试双色图标主题色 + /// + [Fact] + public void TestTwotoneColor() { + _wrapper.SetContextAttribute( UiConst.TwotoneColor, "#eb2f96" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试双色图标主题色 + /// + [Fact] + public void TestTwotoneColor_2() { + _wrapper.SetContextAttribute( UiConst.Theme, IconTheme.Twotone ).SetContextAttribute( UiConst.TwotoneColor, "#eb2f96" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试双色图标主题色 + /// + [Fact] + public void TestBindTwotoneColor() { + _wrapper.SetContextAttribute( AngularConst.BindTwotoneColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Iconfont图标 + /// + [Fact] + public void TestIconfont() { + _wrapper.SetContextAttribute( UiConst.IconFont, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Iconfont图标 + /// + [Fact] + public void TestBindIconfont() { + _wrapper.SetContextAttribute( AngularConst.BindIconFont, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击事件 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Images/ImageGroupTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Images/ImageGroupTagHelperTest.cs new file mode 100644 index 000000000..042bde48a --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Images/ImageGroupTagHelperTest.cs @@ -0,0 +1,60 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Images; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Images { + /// + /// 图片分组测试 + /// + public class ImageGroupTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ImageGroupTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ImageGroupTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试缩放的每步倍数 + /// + [Fact] + public void TestScaleStep() { + _wrapper.SetContextAttribute( UiConst.ScaleStep, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Images/ImageTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Images/ImageTagHelperTest.cs new file mode 100644 index 000000000..28c93784e --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Images/ImageTagHelperTest.cs @@ -0,0 +1,282 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Images; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Images { + /// + /// 图片测试 + /// + public class ImageTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ImageTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ImageTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图片地址 + /// + [Fact] + public void TestSrc() { + _wrapper.SetContextAttribute( UiConst.Src, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图片地址 + /// + [Fact] + public void TestBindSrc() { + _wrapper.SetContextAttribute( AngularConst.BindSrc, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载失败容错地址 + /// + [Fact] + public void TestFallback() { + _wrapper.SetContextAttribute( UiConst.Fallback, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载失败容错地址 + /// + [Fact] + public void TestBindFallback() { + _wrapper.SetContextAttribute( AngularConst.BindFallback, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载占位地址 + /// + [Fact] + public void TestPlaceholder() { + _wrapper.SetContextAttribute( UiConst.Placeholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载占位地址 + /// + [Fact] + public void TestBindPlaceholder() { + _wrapper.SetContextAttribute( AngularConst.BindPlaceholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否禁止预览 + /// + [Fact] + public void TestDisablePreview() { + _wrapper.SetContextAttribute( UiConst.DisablePreview, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试导航时是否关闭预览 + /// + [Fact] + public void TestCloseOnNavigation() { + _wrapper.SetContextAttribute( UiConst.CloseOnNavigation, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文字方向 + /// + [Fact] + public void TestDirection() { + _wrapper.SetContextAttribute( UiConst.Direction, Direction.Rtl ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文字方向 + /// + [Fact] + public void TestBindDirection() { + _wrapper.SetContextAttribute( AngularConst.BindDirection, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽度 - 像素 + /// + [Fact] + public void TestWidth_1() { + _wrapper.SetContextAttribute( UiConst.Width, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽度 - 像素 + /// + [Fact] + public void TestWidth_2() { + _wrapper.SetContextAttribute( UiConst.Width, "10px" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽度 - 百分比 + /// + [Fact] + public void TestWidth_3() { + _wrapper.SetContextAttribute( UiConst.Width, "10%" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽度 + /// + [Fact] + public void TestBindWidth() { + _wrapper.SetContextAttribute( AngularConst.BindWidth, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试高度 + /// + [Fact] + public void TestHeight() { + _wrapper.SetContextAttribute( UiConst.Height, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试高度 + /// + [Fact] + public void TestBindHeight() { + _wrapper.SetContextAttribute( AngularConst.BindHeight, 'a' ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本描述 + /// + [Fact] + public void TestAlt() { + _wrapper.SetContextAttribute( UiConst.Alt, "a" ); + var result = new StringBuilder(); + result.Append( "\"a\"" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本描述 + /// + [Fact] + public void TestBindAlt() { + _wrapper.SetContextAttribute( AngularConst.BindAlt, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试缩放的每步倍数 + /// + [Fact] + public void TestScaleStep() { + _wrapper.SetContextAttribute( UiConst.ScaleStep, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图片加载完成事件 + /// + [Fact] + public void TestOnLoad() { + _wrapper.SetContextAttribute( UiConst.OnLoad, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberGroupTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberGroupTagHelperTest.cs new file mode 100644 index 000000000..affeb7f5d --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberGroupTagHelperTest.cs @@ -0,0 +1,333 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.InputNumbers; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.InputNumbers { + /// + /// 数字输入框组合测试 + /// + public class InputNumberGroupTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public InputNumberGroupTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new InputNumberGroupTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前置标签 + /// + [Fact] + public void TestAddOnBefore() { + _wrapper.SetContextAttribute( UiConst.AddOnBefore, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前置标签 + /// + [Fact] + public void TestBindAddOnBefore() { + _wrapper.SetContextAttribute( AngularConst.BindAddOnBefore, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后置标签 + /// + [Fact] + public void TestAddOnAfter() { + _wrapper.SetContextAttribute( UiConst.AddOnAfter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后置标签 + /// + [Fact] + public void TestBindAddOnAfter() { + _wrapper.SetContextAttribute( AngularConst.BindAddOnAfter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前置图标 + /// + [Fact] + public void TestAddOnBeforeIcon() { + _wrapper.SetContextAttribute( UiConst.AddOnBeforeIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前置图标 + /// + [Fact] + public void TestBindAddOnBeforeIcon() { + _wrapper.SetContextAttribute( AngularConst.BindAddOnBeforeIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后置图标 + /// + [Fact] + public void TestAddOnAfterIcon() { + _wrapper.SetContextAttribute( UiConst.AddOnAfterIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后置图标 + /// + [Fact] + public void TestBindAddOnAfterIcon() { + _wrapper.SetContextAttribute( AngularConst.BindAddOnAfterIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前缀 + /// + [Fact] + public void TestPrefix() { + _wrapper.SetContextAttribute( UiConst.Prefix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前缀 + /// + [Fact] + public void TestBindPrefix() { + _wrapper.SetContextAttribute( AngularConst.BindPrefix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀 + /// + [Fact] + public void TestSuffix() { + _wrapper.SetContextAttribute( UiConst.Suffix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀 + /// + [Fact] + public void TestBindSuffix() { + _wrapper.SetContextAttribute( AngularConst.BindSuffix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前缀图标 + /// + [Fact] + public void TestPrefixIcon() { + _wrapper.SetContextAttribute( UiConst.PrefixIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前缀图标 + /// + [Fact] + public void TestBindPrefixIcon() { + _wrapper.SetContextAttribute( AngularConst.BindPrefixIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀图标 + /// + [Fact] + public void TestSuffixIcon() { + _wrapper.SetContextAttribute( UiConst.SuffixIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀图标 + /// + [Fact] + public void TestBindSuffixIcon() { + _wrapper.SetContextAttribute( AngularConst.BindSuffixIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, InputSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试紧凑模式 + /// + [Fact] + public void TestCompact() { + _wrapper.SetContextAttribute( UiConst.Compact, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表单容器属性 + /// + [Fact] + public void TestFormContainer() { + _wrapper.SetContextAttribute( UiConst.AddOnBefore, "a" ); + + var input = new InputNumberTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelText, "b" ); + _wrapper.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加带验证模板的输入框 + /// + [Fact] + public void TestInput_Validation() { + //创建输入框 + var input = new InputNumberTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelText, "code" ); + input.SetContextAttribute( UiConst.Required, "true" ); + input.SetContextAttribute( AngularConst.NgModel, "model.code" ); + _wrapper.AppendContent( input ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "code" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //执行 + var html = _wrapper.GetResult(); + _output.WriteLine( html ); + + //验证 + Assert.Equal( result.ToString(), html ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberTagHelperTest.Expression.cs new file mode 100644 index 000000000..824c8b16b --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberTagHelperTest.Expression.cs @@ -0,0 +1,31 @@ +using System.Text; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.InputNumbers { + /// + /// 数字输入框测试 - 表达式解析测试 + /// + public partial class InputNumberTagHelperTest { + /// + /// 测试解析表达式属性for + /// + [Fact] + public void TestFor_1() { + _wrapper.SetExpression( t => t.Age ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "年龄" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_age.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberTagHelperTest.Overide.cs b/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberTagHelperTest.Overide.cs new file mode 100644 index 000000000..378142a89 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberTagHelperTest.Overide.cs @@ -0,0 +1,166 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Forms; +using Util.Ui.NgZorro.Components.InputNumbers; +using Util.Ui.NgZorro.Enums; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.InputNumbers { + /// + /// 数字输入框测试 - 测试属性覆盖 + /// + public partial class InputNumberTagHelperTest { + /// + /// 测试垂直对齐方式 - form设置垂直对齐方式,formitem覆盖 + /// + [Fact] + public void TestAlign_Override_1() { + var form = new FormTagHelper().ToWrapper(); + form.SetContextAttribute( UiConst.Align, Align.Bottom ); + + var formItem = new FormItemTagHelper().ToWrapper(); + formItem.SetContextAttribute( UiConst.Align, Align.Middle ); + form.AppendContent( formItem ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( _wrapper ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + + /// + /// 测试垂直对齐方式 - form设置垂直对齐方式,数字输入框覆盖 + /// + [Fact] + public void TestAlign_Override_2() { + var form = new FormTagHelper().ToWrapper(); + form.SetContextAttribute( UiConst.Align, Align.Bottom ); + + var formItem = new FormItemTagHelper().ToWrapper(); + form.AppendContent( formItem ); + + var formControl = new FormControlTagHelper().ToWrapper(); + _wrapper.SetContextAttribute( UiConst.Align, Align.Middle ); + formControl.AppendContent( _wrapper ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + + /// + /// 测试垂直对齐方式 - form设置垂直对齐方式,formitem覆盖,数字输入框覆盖,formitem优先级最高 + /// + [Fact] + public void TestAlign_Override_3() { + var form = new FormTagHelper().ToWrapper(); + form.SetContextAttribute( UiConst.Align, Align.Bottom ); + + var formItem = new FormItemTagHelper().ToWrapper(); + formItem.SetContextAttribute( UiConst.Align, Align.Middle ); + form.AppendContent( formItem ); + + var formControl = new FormControlTagHelper().ToWrapper(); + _wrapper.SetContextAttribute( UiConst.Align, Align.Top ); + formControl.AppendContent( _wrapper ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + + /// + /// 测试垂直对齐方式 - 两个组件 + /// + [Fact] + public void TestAlign_Override_4() { + var form = new FormTagHelper().ToWrapper(); + form.SetContextAttribute( UiConst.Align, Align.Bottom ); + + _wrapper.SetContextAttribute( UiConst.Align, Align.Middle ); + form.AppendContent( _wrapper ); + + var input = new InputNumberTagHelper().ToWrapper(); + form.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + result.Append( "
          " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + + /// + /// 测试垂直对齐方式 - 两个组件,其中一个的formitem手工创建 + /// + [Fact] + public void TestAlign_Override_5() { + var form = new FormTagHelper().ToWrapper(); + form.SetContextAttribute( UiConst.Align, Align.Bottom ); + + var formItem = new FormItemTagHelper().ToWrapper(); + formItem.SetContextAttribute( UiConst.Align, Align.Middle ); + form.AppendContent( formItem ); + + var formControl = new FormControlTagHelper().ToWrapper(); + _wrapper.SetContextAttribute( UiConst.Align, Align.Top ); + formControl.AppendContent( _wrapper ); + formItem.AppendContent( formControl ); + + var input = new InputNumberTagHelper().ToWrapper(); + form.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + result.Append( "
          " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberTagHelperTest.Validation.cs b/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberTagHelperTest.Validation.cs new file mode 100644 index 000000000..3012112da --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberTagHelperTest.Validation.cs @@ -0,0 +1,32 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.InputNumbers { + /// + /// 数字输入框测试 - 验证测试 + /// + public partial class InputNumberTagHelperTest { + /// + /// 测试必填项验证 + /// + [Fact] + public void TestRequired() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberTagHelperTest.cs new file mode 100644 index 000000000..599062578 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/InputNumbers/InputNumberTagHelperTest.cs @@ -0,0 +1,314 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.InputNumbers; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.InputNumbers { + /// + /// 数字输入框测试 + /// + public partial class InputNumberTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public InputNumberTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new InputNumberTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入框标识 + /// + [Fact] + public void TestInputId() { + _wrapper.SetContextAttribute( UiConst.InputId, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestNgModel() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestBindNgModel() { + _wrapper.SetContextAttribute( AngularConst.BindNgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动聚焦 + /// + [Fact] + public void TestAutoFocus() { + _wrapper.SetContextAttribute( UiConst.AutoFocus, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试最大值 + /// + [Fact] + public void TestMax() { + _wrapper.SetContextAttribute( UiConst.Max, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试最小值 + /// + [Fact] + public void TestMin() { + _wrapper.SetContextAttribute( UiConst.Min, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试格式化函数 + /// + [Fact] + public void TestFormatter() { + _wrapper.SetContextAttribute( UiConst.Formatter,"a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试转换函数 + /// + [Fact] + public void TestParser() { + _wrapper.SetContextAttribute( UiConst.Parser, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试精度 + /// + [Fact] + public void TestPrecision() { + _wrapper.SetContextAttribute( UiConst.Precision, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试精度模式 + /// + [Fact] + public void TestPrecisionMode() { + _wrapper.SetContextAttribute( UiConst.PrecisionMode, InputNumberPrecisionMode.ToFixed ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试精度模式 + /// + [Fact] + public void TestBindPrecisionMode() { + _wrapper.SetContextAttribute( AngularConst.BindPrecisionMode, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入框大小 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, InputSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入框大小 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试步数 + /// + [Fact] + public void TestStep() { + _wrapper.SetContextAttribute( UiConst.Step, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入模式 + /// + [Fact] + public void TestInputMode() { + _wrapper.SetContextAttribute( UiConst.InputMode, InputMode.Search ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入模式 + /// + [Fact] + public void TestBindInputMode() { + _wrapper.SetContextAttribute( AngularConst.BindInputMode, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试占位提示 + /// + [Fact] + public void TestPlaceholder() { + _wrapper.SetContextAttribute( UiConst.Placeholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试占位提示 + /// + [Fact] + public void TestBindPlaceholder() { + _wrapper.SetContextAttribute( AngularConst.BindPlaceholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试获得焦点事件 + /// + [Fact] + public void TestOnFocus() { + _wrapper.SetContextAttribute( UiConst.OnFocus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试失去焦点事件 + /// + [Fact] + public void TestOnBlur() { + _wrapper.SetContextAttribute( UiConst.OnBlur, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputGroupTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputGroupTagHelperTest.cs new file mode 100644 index 000000000..e51cc41f6 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputGroupTagHelperTest.cs @@ -0,0 +1,405 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Inputs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Inputs { + /// + /// 输入框组合测试 + /// + public class InputGroupTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public InputGroupTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new InputGroupTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前置标签 + /// + [Fact] + public void TestAddOnBefore() { + _wrapper.SetContextAttribute( UiConst.AddOnBefore, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前置标签 + /// + [Fact] + public void TestBindAddOnBefore() { + _wrapper.SetContextAttribute( AngularConst.BindAddOnBefore, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后置标签 + /// + [Fact] + public void TestAddOnAfter() { + _wrapper.SetContextAttribute( UiConst.AddOnAfter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后置标签 + /// + [Fact] + public void TestBindAddOnAfter() { + _wrapper.SetContextAttribute( AngularConst.BindAddOnAfter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前置图标 + /// + [Fact] + public void TestAddOnBeforeIcon() { + _wrapper.SetContextAttribute( UiConst.AddOnBeforeIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前置图标 + /// + [Fact] + public void TestBindAddOnBeforeIcon() { + _wrapper.SetContextAttribute( AngularConst.BindAddOnBeforeIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后置图标 + /// + [Fact] + public void TestAddOnAfterIcon() { + _wrapper.SetContextAttribute( UiConst.AddOnAfterIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后置图标 + /// + [Fact] + public void TestBindAddOnAfterIcon() { + _wrapper.SetContextAttribute( AngularConst.BindAddOnAfterIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前缀 + /// + [Fact] + public void TestPrefix() { + _wrapper.SetContextAttribute( UiConst.Prefix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前缀 + /// + [Fact] + public void TestBindPrefix() { + _wrapper.SetContextAttribute( AngularConst.BindPrefix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀 + /// + [Fact] + public void TestSuffix() { + _wrapper.SetContextAttribute( UiConst.Suffix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀 + /// + [Fact] + public void TestBindSuffix() { + _wrapper.SetContextAttribute( AngularConst.BindSuffix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前缀图标 + /// + [Fact] + public void TestPrefixIcon() { + _wrapper.SetContextAttribute( UiConst.PrefixIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前缀图标 + /// + [Fact] + public void TestBindPrefixIcon() { + _wrapper.SetContextAttribute( AngularConst.BindPrefixIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀图标 + /// + [Fact] + public void TestSuffixIcon() { + _wrapper.SetContextAttribute( UiConst.SuffixIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀图标 + /// + [Fact] + public void TestBindSuffixIcon() { + _wrapper.SetContextAttribute( AngularConst.BindSuffixIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试搜索框 + /// + [Fact] + public void TestSearch() { + _wrapper.SetContextAttribute( UiConst.Search, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, InputSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestStatus() { + _wrapper.SetContextAttribute( UiConst.Status, FormControlStatus.Error ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestBindStatus() { + _wrapper.SetContextAttribute( AngularConst.BindStatus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试紧凑模式 + /// + [Fact] + public void TestCompact() { + _wrapper.SetContextAttribute( UiConst.Compact, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表单容器属性 + /// + [Fact] + public void TestFormContainer() { + _wrapper.SetContextAttribute( UiConst.AddOnBefore, "a" ); + + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelText, "b" ); + _wrapper.AppendContent( input ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加带验证模板的输入框 + /// + [Fact] + public void TestInput_Validation() { + //创建输入框 + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelText, "code" ); + input.SetContextAttribute( UiConst.Required, "true" ); + input.SetContextAttribute( AngularConst.NgModel, "model.code" ); + _wrapper.AppendContent( input ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "code" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //执行 + var html = _wrapper.GetResult(); + _output.WriteLine( html ); + + //验证 + Assert.Equal( result.ToString(), html ); + } + + /// + /// 测试添加带验证模板的输入框 - 多语言 + /// + [Fact] + public void TestInput_Validation_I18n() { + //开启多语言 + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + + //创建输入框 + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.LabelText, "code" ); + input.SetContextAttribute( UiConst.Required, "true" ); + input.SetContextAttribute( AngularConst.NgModel, "model.code" ); + _wrapper.AppendContent( input ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{'code'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //执行 + var html = _wrapper.GetResult(); + _output.WriteLine( html ); + + //验证 + Assert.Equal( result.ToString(), html ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Autocomplete.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Autocomplete.cs new file mode 100644 index 000000000..e7c26bf9a --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Autocomplete.cs @@ -0,0 +1,34 @@ +using System.Text; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Inputs { + /// + /// 输入框测试 - 自动完成相关 + /// + public partial class InputTagHelperTest { + /// + /// 测试自动完成 + /// + [Fact] + public void TestAutocomplete() { + _wrapper.SetContextAttribute( UiConst.Autocomplete, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动完成搜索关键字 + /// + [Fact] + public void TestAutocompleteSearchKeyword() { + _wrapper.SetContextAttribute( UiConst.Autocomplete, "a" ); + _wrapper.SetContextAttribute( UiConst.AutocompleteSearchKeyword, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Base.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Base.cs new file mode 100644 index 000000000..6fab84999 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Base.cs @@ -0,0 +1,155 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Inputs { + /// + /// 输入框测试 - 基础属性测试 + /// + public partial class InputTagHelperTest { + /// + /// 测试原始标识 + /// + [Fact] + public void TestRawId() { + _wrapper.SetContextAttribute( AngularConst.RawId, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试引用变量 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试名称 + /// + [Fact] + public void TestName() { + _wrapper.SetContextAttribute( UiConst.Name, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试名称 + /// + [Fact] + public void TestBindName() { + _wrapper.SetContextAttribute( AngularConst.BindName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试样式 + /// + [Fact] + public void TestStyle() { + _wrapper.SetContextAttribute( UiConst.Style, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试样式类 + /// + [Fact] + public void TestClass() { + _wrapper.SetContextAttribute( UiConst.Class, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试隐藏 + /// + [Fact] + public void TestHidden() { + _wrapper.SetContextAttribute( UiConst.Hidden, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加其它未配置的属性 + /// + [Fact] + public void TestOutputAttributes() { + _wrapper.SetOutputAttribute( "a", "1" ).SetOutputAttribute( "b", "2" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestNgModel() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestBindNgModel() { + _wrapper.SetContextAttribute( AngularConst.BindNgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表单控件实例 + /// + [Fact] + public void TestFormControl() { + _wrapper.SetContextAttribute( UiConst.FormControl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表单控件名 + /// + [Fact] + public void TestFormControlName() { + _wrapper.SetContextAttribute( UiConst.FormControlName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表单控件名 + /// + [Fact] + public void TestBindFormControlName() { + _wrapper.SetContextAttribute( AngularConst.BindFormControlName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Events.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Events.cs new file mode 100644 index 000000000..3268c3672 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Events.cs @@ -0,0 +1,66 @@ +using System.Text; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Inputs { + /// + /// 输入框测试 - 事件测试 + /// + public partial class InputTagHelperTest { + /// + /// 测试模型变更事件 + /// + [Fact] + public void TestOnModelChange() { + _wrapper.SetContextAttribute( UiConst.OnModelChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入事件 + /// + [Fact] + public void TestOnInput() { + _wrapper.SetContextAttribute( UiConst.OnInput, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试失去焦点事件 + /// + [Fact] + public void TestOnBlur() { + _wrapper.SetContextAttribute( UiConst.OnBlur, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试回车事件 + /// + [Fact] + public void TestOnKeyupEnter() { + _wrapper.SetContextAttribute( UiConst.OnKeyupEnter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试回车事件 + /// + [Fact] + public void TestOnKeydownEnter() { + _wrapper.SetContextAttribute( UiConst.OnKeydownEnter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Expression.cs new file mode 100644 index 000000000..a5143a4ae --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Expression.cs @@ -0,0 +1,179 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Forms; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Inputs { + /// + /// 输入框测试 - 表达式解析测试 + /// + public partial class InputTagHelperTest { + /// + /// 测试解析表达式属性for + /// + [Fact] + public void TestFor_1() { + _wrapper.SetExpression( t => t.Code ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "code" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_code.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试解析表达式属性for - 密码类型 + /// + [Fact] + public void TestFor_2() { + _wrapper.SetExpression( t => t.Password ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "密码" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试解析表达式属性for - 电子邮件验证 + /// + [Fact] + public void TestFor_3() { + _wrapper.SetExpression( t => t.Email ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "电子邮件" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_email.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试解析表达式属性for - 手机号验证 + /// + [Fact] + public void TestFor_4() { + _wrapper.SetExpression( t => t.Phone ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "手机号" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_phone.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试解析表达式属性for - 身份证验证 + /// + [Fact] + public void TestFor_5() { + _wrapper.SetExpression( t => t.IdCard ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "身份证" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id_card.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试解析表达式属性for - 正则验证 + /// + [Fact] + public void TestFor_6() { + _wrapper.SetExpression( t => t.Regex ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "正则表达式" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_regex.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试解析表达式属性for - 手工设置容器组件 + /// + [Fact] + public void TestFor_7() { + var form = new FormTagHelper().ToWrapper(); + form.SetContextAttribute( UiConst.ControlSpan, 2 ); + + var formContainer = new FormContainerTagHelper().ToWrapper(); + formContainer.SetContextAttribute( UiConst.ControlSpan, 3 ); + form.AppendContent( formContainer ); + + var formItem = new FormItemTagHelper().ToWrapper(); + formContainer.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formItem.AppendContent( formControl ); + + _wrapper.SetExpression( t => t.Code ); + formControl.AppendContent( _wrapper ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_code.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + var formResult = form.GetResult(); + _output.WriteLine( formResult ); + Assert.Equal( result.ToString(), formResult ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Form.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Form.cs new file mode 100644 index 000000000..a4a4f80a1 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Form.cs @@ -0,0 +1,607 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Forms; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Inputs { + /// + /// 输入框测试 - 表单结构相关 + /// + public partial class InputTagHelperTest { + /// + /// 测试垂直对齐方式 + /// + [Fact] + public void TestAlign() { + _wrapper.SetContextAttribute( UiConst.Align, Align.Middle ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试垂直对齐方式 - 设置name + /// + [Fact] + public void TestAlign_Name() { + _wrapper.SetContextAttribute( UiConst.Align, Align.Middle ); + _wrapper.SetOutputAttribute( UiConst.Name, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试额外提示 + /// + [Fact] + public void TestExtra() { + _wrapper.SetContextAttribute( UiConst.Extra, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试额外提示 - 多语言 + /// + [Fact] + public void TestExtra_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Extra, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试额外提示 + /// + [Fact] + public void TestBindExtra() { + _wrapper.SetContextAttribute( AngularConst.BindExtra, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试校验成功状态提示 + /// + [Fact] + public void TestSuccessTip() { + _wrapper.SetContextAttribute( UiConst.SuccessTip, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试校验警告状态提示 + /// + [Fact] + public void TestWarningTip() { + _wrapper.SetContextAttribute( UiConst.WarningTip, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试校验错误状态提示 + /// + [Fact] + public void TestErrorTip() { + _wrapper.SetContextAttribute( UiConst.ErrorTip, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试校验中状态提示 + /// + [Fact] + public void TestValidatingTip() { + _wrapper.SetContextAttribute( UiConst.ValidatingTip, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签文本 + /// + [Fact] + public void TestLabelText() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签文本 - 启用I18n + /// + [Fact] + public void TestLabelText_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{'a'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签文本 + /// + [Fact] + public void TestBindLabelText() { + _wrapper.SetContextAttribute( AngularConst.BindLabelText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{a}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示标签 + /// + [Fact] + public void TestShowLabel() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + _wrapper.SetContextAttribute( UiConst.ShowLabel, false ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单独设置标签跨度,不会创建表单结构 + /// + [Fact] + public void TestLabelSpan() { + _wrapper.SetContextAttribute( UiConst.LabelSpan, 3 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试同时设置标签文本和标签跨度 + /// + [Fact] + public void TestLabelSpan_2() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ).SetContextAttribute( UiConst.LabelSpan, 3 ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置标签跨度,手工创建nz-form-label + /// + [Fact] + public void TestLabelSpan_3() { + var form = new FormTagHelper().ToWrapper(); + var formItem = new FormItemTagHelper().ToWrapper(); + form.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + _wrapper.SetContextAttribute( UiConst.LabelSpan, 3 ); + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( _wrapper ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + + /// + /// 测试设置表单和表单容器标签跨度,进行覆盖 + /// + [Fact] + public void TestLabelSpan_4() { + var form = new FormTagHelper().ToWrapper(); + form.SetContextAttribute( UiConst.LabelSpan, 2 ); + + var formContainer = new FormContainerTagHelper().ToWrapper(); + formContainer.SetContextAttribute( UiConst.LabelSpan, 3 ); + form.AppendContent( formContainer ); + + var formItem = new FormItemTagHelper().ToWrapper(); + formContainer.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formItem.AppendContent( formControl ); + + _wrapper.SetContextAttribute( UiConst.LabelSpan,5 ); + formControl.AppendContent( _wrapper ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + + /// + /// 测试设置表单标签跨度,手工创建form-control,自动创建form-label,继承容器属性 + /// + [Fact] + public void TestLabelSpan_5() { + var form = new FormTagHelper().ToWrapper(); + form.SetContextAttribute( UiConst.LabelSpan, 2 ); + + var formItem = new FormItemTagHelper().ToWrapper(); + form.AppendContent( formItem ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formItem.AppendContent( formControl ); + + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + formControl.AppendContent( _wrapper ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), GetResult( form ) ); + } + + /// + /// 测试控件跨度 - 默认会自动创建nz-form-control,nz-form-item + /// + [Fact] + public void TestControlSpan() { + _wrapper.SetContextAttribute( UiConst.ControlSpan, 3 ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置控件跨度 - 手工创建nz-form-item,自动创建nz-form-control + /// + [Fact] + public void TestControlSpan_2() { + var formItem = new FormItemTagHelper().ToWrapper(); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + _wrapper.SetContextAttribute( UiConst.ControlSpan, 3 ); + formItem.AppendContent( _wrapper ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), formItem.GetResult() ); + } + + /// + /// 测试设置控件跨度 - 手工创建nz-form-item,nz-form-control + /// + [Fact] + public void TestControlSpan_3() { + var form = new FormTagHelper().ToWrapper(); + + var formItem = new FormItemTagHelper().ToWrapper(); + form.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formItem.AppendContent( formControl ); + + _wrapper.SetContextAttribute( UiConst.ControlSpan, 3 ); + formControl.AppendContent( _wrapper ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + + /// + /// 测试设置表单和表单容器标签跨度,进行覆盖 + /// + [Fact] + public void TestControlSpan_4() { + var form = new FormTagHelper().ToWrapper(); + form.SetContextAttribute( UiConst.ControlSpan, 2 ); + + var formContainer = new FormContainerTagHelper().ToWrapper(); + formContainer.SetContextAttribute( UiConst.ControlSpan, 3 ); + form.AppendContent( formContainer ); + + var formItem = new FormItemTagHelper().ToWrapper(); + formContainer.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + var formControl = new FormControlTagHelper().ToWrapper(); + formItem.AppendContent( formControl ); + + _wrapper.SetContextAttribute( UiConst.ControlSpan, 5 ); + formControl.AppendContent( _wrapper ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + + /// + /// 测试自动设置表单标签的 nzFor 属性,不会自动创建表单标签 + /// + [Fact] + public void TestAutoNzFor() { + _wrapper.SetContextAttribute( UiConst.AutoNzFor, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动创建控件Id,并设置标签for + /// + [Fact] + public void TestAutoNzFor_2() { + var form = new FormTagHelper().ToWrapper(); + + var formItem = new FormItemTagHelper().ToWrapper(); + form.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + _wrapper.SetContextAttribute( UiConst.AutoNzFor, true ); + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( _wrapper ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + + /// + /// 测试手工设置控件Id,并设置标签for + /// + [Fact] + public void TestAutoNzFor_3() { + var form = new FormTagHelper().ToWrapper(); + + var formItem = new FormItemTagHelper().ToWrapper(); + form.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + _wrapper.SetContextAttribute( AngularConst.RawId, "a" ); + _wrapper.SetContextAttribute( UiConst.AutoNzFor, true ); + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( _wrapper ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + + /// + /// 测试表单设置标签for,表单控件取消设置 + /// + [Fact] + public void TestAutoNzFor_4() { + var form = new FormTagHelper().ToWrapper(); + form.SetContextAttribute( UiConst.AutoNzFor, true ); + + var formItem = new FormItemTagHelper().ToWrapper(); + form.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + _wrapper.SetContextAttribute( UiConst.AutoNzFor, false ); + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( _wrapper ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
          " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
          " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + + /// + /// 测试同时设置标签文本和标签for + /// + [Fact] + public void TestAutoNzFor_5() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ).SetContextAttribute( UiConst.AutoNzFor, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置输出属性时,仅添加到输入框 + /// + [Fact] + public void TestOutputAttribute() { + _wrapper.SetContextAttribute( UiConst.Align, Align.Middle ); + _wrapper.SetOutputAttribute( "(key)", "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置标签宽度 + /// + [Fact] + public void TestLabelWidth() { + _wrapper.SetContextAttribute( UiConst.LabelWidth, "100" ); + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.InputGroup.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.InputGroup.cs new file mode 100644 index 000000000..605d8c7af --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.InputGroup.cs @@ -0,0 +1,459 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Inputs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Inputs { + /// + /// 输入框测试 - 输入框组合相关 + /// + public partial class InputTagHelperTest { + /// + /// 测试前置标签 + /// + [Fact] + public void TestAddOnBefore() { + _wrapper.SetContextAttribute( UiConst.AddOnBefore, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前置标签 - 设置大小 + /// + [Fact] + public void TestAddOnBefore_Size() { + _wrapper.SetContextAttribute( UiConst.AddOnBefore, "a" ); + _wrapper.SetContextAttribute( UiConst.Size, InputSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前置标签 - 设置大小 + /// + [Fact] + public void TestAddOnBefore_BindSize() { + _wrapper.SetContextAttribute( UiConst.AddOnBefore, "a" ); + _wrapper.SetContextAttribute( AngularConst.BindSize, "b" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前置标签 - 设置状态 + /// + [Fact] + public void TestAddOnBefore_Status() { + _wrapper.SetContextAttribute( UiConst.AddOnBefore, "a" ); + _wrapper.SetContextAttribute( UiConst.Status, FormControlStatus.Error ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前置标签 - 设置状态 + /// + [Fact] + public void TestAddOnBefore_BindStatus() { + _wrapper.SetContextAttribute( UiConst.AddOnBefore, "a" ); + _wrapper.SetContextAttribute( AngularConst.BindStatus, "b" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前置标签 + /// + [Fact] + public void TestBindAddOnBefore() { + _wrapper.SetContextAttribute( AngularConst.BindAddOnBefore, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后置标签 + /// + [Fact] + public void TestAddOnAfter() { + _wrapper.SetContextAttribute( UiConst.AddOnAfter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后置标签 + /// + [Fact] + public void TestBindAddOnAfter() { + _wrapper.SetContextAttribute( AngularConst.BindAddOnAfter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前置图标 + /// + [Fact] + public void TestAddOnBeforeIcon() { + _wrapper.SetContextAttribute( UiConst.AddOnBeforeIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前置图标 + /// + [Fact] + public void TestBindAddOnBeforeIcon() { + _wrapper.SetContextAttribute( AngularConst.BindAddOnBeforeIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后置图标 + /// + [Fact] + public void TestAddOnAfterIcon() { + _wrapper.SetContextAttribute( UiConst.AddOnAfterIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后置图标 + /// + [Fact] + public void TestBindAddOnAfterIcon() { + _wrapper.SetContextAttribute( AngularConst.BindAddOnAfterIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后置图标 - 同时设置前置图标 + /// + [Fact] + public void TestAddOnAfterIcon_2() { + _wrapper.SetContextAttribute( UiConst.AddOnBeforeIcon, AntDesignIcon.AccountBook ).SetContextAttribute( UiConst.AddOnAfterIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前缀 + /// + [Fact] + public void TestPrefix() { + _wrapper.SetContextAttribute( UiConst.Prefix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前缀 + /// + [Fact] + public void TestBindPrefix() { + _wrapper.SetContextAttribute( AngularConst.BindPrefix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀 + /// + [Fact] + public void TestSuffix() { + _wrapper.SetContextAttribute( UiConst.Suffix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀 + /// + [Fact] + public void TestBindSuffix() { + _wrapper.SetContextAttribute( AngularConst.BindSuffix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前缀图标 + /// + [Fact] + public void TestPrefixIcon() { + _wrapper.SetContextAttribute( UiConst.PrefixIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前缀图标 + /// + [Fact] + public void TestBindPrefixIcon() { + _wrapper.SetContextAttribute( AngularConst.BindPrefixIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀图标 + /// + [Fact] + public void TestSuffixIcon() { + _wrapper.SetContextAttribute( UiConst.SuffixIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀图标 + /// + [Fact] + public void TestBindSuffixIcon() { + _wrapper.SetContextAttribute( AngularConst.BindSuffixIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀图标 - 属性设置在input上,外层添加input-group标签 + /// + [Fact] + public void TestBindSuffixIcon_2() { + var inputGroup = new InputGroupTagHelper().ToWrapper(); + _wrapper.SetContextAttribute( AngularConst.BindSuffixIcon, "a" ); + inputGroup.AppendContent( _wrapper ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), inputGroup.GetResult() ); + } + + /// + /// 测试允许清除 + /// + [Fact] + public void TestAllowClear() { + _wrapper.SetContextAttribute( UiConst.AllowClear, true ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "code" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试允许清除 - 全局设置 + /// + [Fact] + public void TestAllowClear_2() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableAllowClear = true } ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "code" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试允许清除 - 手工创建输入框组 + /// + [Fact] + public void TestAllowClear_3() { + //设置输入框允许清除 + _wrapper.SetContextAttribute( AngularConst.NgModel, "code" ); + _wrapper.SetContextAttribute( UiConst.AllowClear, true ); + + //创建输入框组 + var inputGroup = new InputGroupTagHelper().ToWrapper(); + inputGroup.SetContextAttribute( UiConst.Size, InputSize.Small ); + inputGroup.AppendContent( _wrapper ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //执行 + var html = inputGroup.GetResult(); + _output.WriteLine( html ); + + //验证 + Assert.Equal( result.ToString(), html ); + } + + /// + /// 测试允许清除 - 设置表单控件实例 ,禁用允许清除 + /// + [Fact] + public void TestAllowClear_4() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableAllowClear = true } ); + _wrapper.SetContextAttribute( UiConst.FormControl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试允许清除 - 未设置ngModel,禁用允许清除 + /// + [Fact] + public void TestAllowClear_5() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableAllowClear = true } ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试允许清除 - 指定后缀图标 + /// + [Fact] + public void TestAllowClear_6() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableAllowClear = true } ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "code" ); + _wrapper.SetContextAttribute( UiConst.Suffix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试允许清除 - 指定后缀图标 + /// + [Fact] + public void TestAllowClear_7() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableAllowClear = true } ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "code" ); + _wrapper.SetContextAttribute( AngularConst.BindSuffix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试密码类型 + /// + [Fact] + public void TestType_Password() { + _wrapper.SetContextAttribute( UiConst.Type, InputType.Password ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试密码类型允许清除 + /// + [Fact] + public void TestPassword_AllowClear() { + _wrapper.SetContextAttribute( UiConst.Type, InputType.Password ); + _wrapper.SetContextAttribute( UiConst.AllowClear, true ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "code" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Mention.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Mention.cs new file mode 100644 index 000000000..b07b7271d --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Mention.cs @@ -0,0 +1,118 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Mentions; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Inputs { + /// + /// 输入框测试 - 提及相关 + /// + public partial class InputTagHelperTest { + /// + /// 测试提及触发元素 + /// + [Fact] + public void TestMentionTrigger() { + var mention = new MentionTagHelper().ToWrapper(); + mention.AppendContent( _wrapper ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), mention.GetResult() ); + } + + /// + /// 测试放入提及组件中,并设置前置标签 + /// + [Fact] + public void TestMention_AddOnBefore() { + _wrapper.SetContextAttribute( UiConst.AddOnBefore, "a" ); + + var mention = new MentionTagHelper().ToWrapper(); + mention.AppendContent( _wrapper ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), mention.GetResult() ); + } + + /// + /// 测试放入提及组件中,并设置标签文本 + /// + [Fact] + public void TestMention_LabelText() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + + var mention = new MentionTagHelper().ToWrapper(); + mention.AppendContent( _wrapper ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), mention.GetResult() ); + } + + /// + /// 测试放入提及组件中,并设置标签文本和前置标签 + /// + [Fact] + public void TestMention_LabelText_AddOnBefore() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + _wrapper.SetContextAttribute( UiConst.AddOnBefore, "a" ); + + var mention = new MentionTagHelper().ToWrapper(); + mention.AppendContent( _wrapper ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), mention.GetResult() ); + } + + /// + /// 测试放入提及组件中,设置标签文本和前置标签,并自动设置标签for + /// + [Fact] + public void TestMention_LabelText_AddOnBefore_AutoNzFor() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + _wrapper.SetContextAttribute( UiConst.AddOnBefore, "a" ); + _wrapper.SetContextAttribute( UiConst.AutoNzFor, true ); + + var mention = new MentionTagHelper().ToWrapper(); + mention.AppendContent( _wrapper ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), mention.GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.NgIf.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.NgIf.cs new file mode 100644 index 000000000..45126a807 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.NgIf.cs @@ -0,0 +1,86 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Inputs { + /// + /// 输入框测试 - ngIf* + /// + public partial class InputTagHelperTest { + /// + /// 测试*ngIf - 仅输入框组件 + /// + [Fact] + public void TestNgIf_1() { + _wrapper.SetContextAttribute( AngularConst.NgIf, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试ngIf* - 生成form-item容器 + /// + [Fact] + public void TestNgIf_2() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ) + .SetContextAttribute( AngularConst.NgIf,"a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试ngIf* - 生成nz-input-group容器 + /// + [Fact] + public void TestNgIf_3() { + _wrapper.SetContextAttribute( UiConst.AllowClear, true ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "code" ) + .SetContextAttribute( AngularConst.NgIf, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试ngIf* - 生成form-item容器 和 nz-input-group容器 + /// + [Fact] + public void TestNgIf_4() { + _wrapper.SetContextAttribute( UiConst.AllowClear, true ) + .SetContextAttribute( AngularConst.NgModel, "code" ) + .SetContextAttribute( UiConst.LabelText, "a" ) + .SetContextAttribute( AngularConst.NgIf, "b" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Space.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Space.cs new file mode 100644 index 000000000..539fd73e2 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Space.cs @@ -0,0 +1,64 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Inputs { + /// + /// 输入框测试 - 间距相关 + /// + public partial class InputTagHelperTest { + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem_1() { + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 - 创建 nz-input-group + /// + [Fact] + public void TestSpaceItem_2() { + _wrapper.SetContextAttribute( UiConst.AllowClear, true ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "code" ); + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 - 创建 nz-form-item + /// + [Fact] + public void TestSpaceItem_3() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.TableEdit.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.TableEdit.cs new file mode 100644 index 000000000..1f74d4b86 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.TableEdit.cs @@ -0,0 +1,110 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Inputs { + /// + /// 输入框测试 - 表格编辑 + /// + public partial class InputTagHelperTest { + /// + /// 测试启用编辑模式 + /// + [Fact] + public void TestIsEdit_1() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.IsEdit, true ); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + table.AppendContent( column ); + + //添加输入框组件 + column.AppendContent( _wrapper ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试启用编辑模式 - 输入组件设置表达式 + /// + [Fact] + public void TestIsEdit_2() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.IsEdit, true ); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + table.AppendContent( column ); + + //添加输入框组件 + _wrapper.SetExpression( t => t.Code ); + column.AppendContent( _wrapper ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_control_code.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Validation.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Validation.cs new file mode 100644 index 000000000..ff1f5e94d --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.Validation.cs @@ -0,0 +1,343 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Forms; +using Util.Ui.NgZorro.Enums; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Inputs { + /// + /// 输入框测试 - 验证测试 + /// + public partial class InputTagHelperTest { + /// + /// 测试必填项验证 + /// + [Fact] + public void TestRequired() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试必填项验证 - 允许清除 + /// + [Fact] + public void TestRequired_AllowClear() { + _wrapper.SetContextAttribute( UiConst.AllowClear, true ); + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试必填项验证 - 设置ErrorTip + /// + [Fact] + public void TestRequired_ErrorTip() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( UiConst.ErrorTip, "a" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试必填项验证 - 设置BindErrorTip + /// + [Fact] + public void TestRequired_BindErrorTip() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( AngularConst.BindErrorTip, "a" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试必填项验证 - 设置BindErrorTip - 手工创建 formControl + /// + [Fact] + public void TestRequired_BindErrorTip_2() { + var formItem = new FormItemTagHelper().ToWrapper(); + + var formControl = new FormControlTagHelper().ToWrapper(); + formItem.AppendContent( formControl ); + + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( AngularConst.BindErrorTip, "a" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + formControl.AppendContent( _wrapper ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( formItem ) ); + } + + /// + /// 测试必填项验证 - 设置BindErrorTip - 手工创建 formControl - formControl设置的 BindErrorTip 优先级更高 + /// + [Fact] + public void TestRequired_BindErrorTip_3() { + var formItem = new FormItemTagHelper().ToWrapper(); + + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.SetContextAttribute( AngularConst.BindErrorTip, "b" ); + formItem.AppendContent( formControl ); + + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( AngularConst.BindErrorTip, "a" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + formControl.AppendContent( _wrapper ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( formItem ) ); + } + + /// + /// 测试必填项消息 + /// + [Fact] + public void TestRequiredMessage() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( UiConst.RequiredMessage, "a" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试必填项消息 + /// + [Fact] + public void TestBindRequiredMessage() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( AngularConst.BindRequiredMessage, "a" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试最小长度验证 + /// + [Fact] + public void TestMinLength() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + _wrapper.SetContextAttribute( UiConst.MinLength, "2" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试最小长度验证消息 + /// + [Fact] + public void TestMinLengthMessage() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + _wrapper.SetContextAttribute( UiConst.MinLength, "2" ); + _wrapper.SetContextAttribute( UiConst.MinLengthMessage, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试最小长度验证消息 + /// + [Fact] + public void TestBindMinLengthMessage() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + _wrapper.SetContextAttribute( UiConst.MinLength, "2" ); + _wrapper.SetContextAttribute( AngularConst.BindMinLengthMessage, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试最大长度验证 + /// + [Fact] + public void TestMaxLength() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + _wrapper.SetContextAttribute( UiConst.MaxLength, "2" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试电子邮件验证消息 + /// + [Fact] + public void TestEmailMessage() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + _wrapper.SetContextAttribute( UiConst.Type, InputType.Email ); + _wrapper.SetContextAttribute( UiConst.EmailMessage, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试电子邮件验证消息 + /// + [Fact] + public void TestBindEmailMessage() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + _wrapper.SetContextAttribute( UiConst.Type, InputType.Email ); + _wrapper.SetContextAttribute( AngularConst.BindEmailMessage, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试正则表达式验证 + /// + [Fact] + public void TestPattern_PatternMessage() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + _wrapper.SetContextAttribute( UiConst.Pattern, "a" ); + _wrapper.SetContextAttribute( UiConst.PatternMessage, "b" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试正则表达式验证 + /// + [Fact] + public void TestBindPattern_BindPatternMessage() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + _wrapper.SetContextAttribute( AngularConst.BindPattern, "a" ); + _wrapper.SetContextAttribute( AngularConst.BindPatternMessage, "b" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.cs new file mode 100644 index 000000000..52b6ec505 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Inputs/InputTagHelperTest.cs @@ -0,0 +1,181 @@ +using System; +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Inputs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Inputs { + /// + /// 输入框测试 + /// + public partial class InputTagHelperTest : IDisposable { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public InputTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new InputTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 测试清理 + /// + public void Dispose() { + Id.Reset(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + return GetResult( _wrapper ); + } + + /// + /// 获取结果 + /// + private string GetResult( TagHelperWrapper wrapper ) { + var result = wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置占位符提示 + /// + [Fact] + public void TestPlaceholder() { + _wrapper.SetContextAttribute( UiConst.Placeholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置占位符提示 + /// + [Fact] + public void TestBindPlaceholder() { + _wrapper.SetContextAttribute( AngularConst.BindPlaceholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试只读 + /// + [Fact] + public void TestReadonly() { + _wrapper.SetContextAttribute( UiConst.Readonly, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, InputSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestStatus() { + _wrapper.SetContextAttribute( UiConst.Status, FormControlStatus.Error ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestBindStatus() { + _wrapper.SetContextAttribute( AngularConst.BindStatus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试类型 - 电子邮件 + /// + [Fact] + public void TestType_Email() { + _wrapper.SetContextAttribute( UiConst.Type, InputType.Email ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试类型 + /// + [Fact] + public void TestBindType() { + _wrapper.SetContextAttribute( AngularConst.BindType, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/Label/LabelTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Label/LabelTagHelperTest.cs new file mode 100644 index 000000000..f1efa606d --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Label/LabelTagHelperTest.cs @@ -0,0 +1,116 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Label; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Label { + /// + /// 标签测试 + /// + public class LabelTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public LabelTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new LabelTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本 + /// + [Fact] + public void TestText() { + _wrapper.SetContextAttribute( UiConst.Text, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本 - 多语言 + /// + [Fact] + public void TestText_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Text, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{'a'|i18n}}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 + /// + [Fact] + public void TestFor() { + _wrapper.SetExpression( t => t.Nickname ); + var result = new StringBuilder(); + result.Append( "a.nickname" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 多语言 + /// + [Fact] + public void TestFor_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetExpression( t => t.Nickname ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{'a.nickname'|i18n}}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Layouts/ContentTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Layouts/ContentTagHelperTest.cs new file mode 100644 index 000000000..b30f13da4 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Layouts/ContentTagHelperTest.cs @@ -0,0 +1,61 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Layouts; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Layouts { + /// + /// 内容区布局测试 + /// + public class ContentTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ContentTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ContentTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Layouts/FooterTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Layouts/FooterTagHelperTest.cs new file mode 100644 index 000000000..429d893db --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Layouts/FooterTagHelperTest.cs @@ -0,0 +1,61 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Layouts; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Layouts { + /// + /// 底部布局测试 + /// + public class FooterTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public FooterTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new FooterTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Layouts/HeaderTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Layouts/HeaderTagHelperTest.cs new file mode 100644 index 000000000..111f3f2b2 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Layouts/HeaderTagHelperTest.cs @@ -0,0 +1,61 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Layouts; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Layouts { + /// + /// 顶部布局测试 + /// + public class HeaderTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public HeaderTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new HeaderTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Layouts/LayoutTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Layouts/LayoutTagHelperTest.cs new file mode 100644 index 000000000..17305bbd4 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Layouts/LayoutTagHelperTest.cs @@ -0,0 +1,61 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Layouts; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Layouts { + /// + /// 布局测试 + /// + public class LayoutTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public LayoutTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new LayoutTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Layouts/SiderTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Layouts/SiderTagHelperTest.cs new file mode 100644 index 000000000..c8e2424af --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Layouts/SiderTagHelperTest.cs @@ -0,0 +1,208 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Layouts; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Layouts { + /// + /// 侧边栏布局测试 + /// + public class SiderTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public SiderTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new SiderTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽度 + /// + [Fact] + public void TestWidth() { + _wrapper.SetContextAttribute( UiConst.Width, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试主题 + /// + [Fact] + public void TestTheme() { + _wrapper.SetContextAttribute( UiConst.Theme, SiderTheme.Dark ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试主题 + /// + [Fact] + public void TestBindTheme() { + _wrapper.SetContextAttribute( AngularConst.BindTheme, "Ab" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试响应式布局断点 + /// + [Fact] + public void TestBreakpoint() { + _wrapper.SetContextAttribute( UiConst.Breakpoint, GridSize.Md ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试响应式布局断点 + /// + [Fact] + public void TestBindBreakpoint() { + _wrapper.SetContextAttribute( AngularConst.BindBreakpoint, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试收缩宽度 + /// + [Fact] + public void TestCollapsedWidth() { + _wrapper.SetContextAttribute( UiConst.CollapsedWidth, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试可收缩 + /// + [Fact] + public void TestCollapsible() { + _wrapper.SetContextAttribute( UiConst.Collapsible, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试收缩状态 + /// + [Fact] + public void TestCollapsed() { + _wrapper.SetContextAttribute( UiConst.Collapsed, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试收缩状态 + /// + [Fact] + public void TestBindonCollapsed() { + _wrapper.SetContextAttribute( AngularConst.BindonCollapsed, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试翻转折叠提示箭头方向 + /// + [Fact] + public void TestReverseArrow() { + _wrapper.SetContextAttribute( UiConst.ReverseArrow, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义触发器 + /// + [Fact] + public void TestTrigger() { + _wrapper.SetContextAttribute( UiConst.Trigger, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试0触发器 + /// + [Fact] + public void TestZeroTrigger() { + _wrapper.SetContextAttribute( UiConst.ZeroTrigger, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击事件 + /// + [Fact] + public void TestOnCollapsedChange() { + _wrapper.SetContextAttribute( UiConst.OnCollapsedChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Lists/ListEmptyTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Lists/ListEmptyTagHelperTest.cs new file mode 100644 index 000000000..2ec222c2a --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Lists/ListEmptyTagHelperTest.cs @@ -0,0 +1,83 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Lists { + /// + /// 列表空内容测试 + /// + public class ListEmptyTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ListEmptyTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ListEmptyTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试空内容显示文本 + /// + [Fact] + public void TestNoResult() { + _wrapper.SetContextAttribute( UiConst.NoResult, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试空内容显示文本 + /// + [Fact] + public void TestBindNoResult() { + _wrapper.SetContextAttribute( AngularConst.BindNoResult, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Lists/ListFooterTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Lists/ListFooterTagHelperTest.cs new file mode 100644 index 000000000..6bb80f747 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Lists/ListFooterTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Lists; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Lists { + /// + /// 列表底部测试 + /// + public class ListFooterTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ListFooterTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ListFooterTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Lists/ListHeaderTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Lists/ListHeaderTagHelperTest.cs new file mode 100644 index 000000000..e7d21f3bd --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Lists/ListHeaderTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Lists; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Lists { + /// + /// 列表头部测试 + /// + public class ListHeaderTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ListHeaderTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ListHeaderTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Lists/ListItemActionTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Lists/ListItemActionTagHelperTest.cs new file mode 100644 index 000000000..0a9d1db7a --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Lists/ListItemActionTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Lists; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Lists { + /// + /// 列表项操作项测试 + /// + public class ListItemActionTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ListItemActionTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ListItemActionTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Lists/ListItemActionsTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Lists/ListItemActionsTagHelperTest.cs new file mode 100644 index 000000000..d4c649900 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Lists/ListItemActionsTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Lists; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Lists { + /// + /// 列表项操作测试 + /// + public class ListItemActionsTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ListItemActionsTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ListItemActionsTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
            " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "
              a
            " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Lists/ListItemExtraTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Lists/ListItemExtraTagHelperTest.cs new file mode 100644 index 000000000..47b44f88b --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Lists/ListItemExtraTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Lists; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Lists { + /// + /// 列表项扩展测试 + /// + public class ListItemExtraTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ListItemExtraTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ListItemExtraTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Lists/ListItemMetaAvatarTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Lists/ListItemMetaAvatarTagHelperTest.cs new file mode 100644 index 000000000..5068cb30b --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Lists/ListItemMetaAvatarTagHelperTest.cs @@ -0,0 +1,83 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Lists { + /// + /// 列表项元信息头像测试 + /// + public class ListItemMetaAvatarTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ListItemMetaAvatarTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ListItemMetaAvatarTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试头像地址 + /// + [Fact] + public void TestSrc() { + _wrapper.SetContextAttribute( UiConst.Src, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试头像地址 + /// + [Fact] + public void TestBindSrc() { + _wrapper.SetContextAttribute( AngularConst.BindSrc, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Lists/ListItemMetaDescriptionTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Lists/ListItemMetaDescriptionTagHelperTest.cs new file mode 100644 index 000000000..3f5bab28b --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Lists/ListItemMetaDescriptionTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Lists; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Lists { + /// + /// 列表项元信息描述测试 + /// + public class ListItemMetaDescriptionTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ListItemMetaDescriptionTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ListItemMetaDescriptionTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Lists/ListItemMetaTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Lists/ListItemMetaTagHelperTest.cs new file mode 100644 index 000000000..bb3b237e3 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Lists/ListItemMetaTagHelperTest.cs @@ -0,0 +1,127 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Lists { + /// + /// 列表项元信息测试 + /// + public class ListItemMetaTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ListItemMetaTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ListItemMetaTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试头像图标 + /// + [Fact] + public void TestAvatar() { + _wrapper.SetContextAttribute( UiConst.Avatar, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试头像图标 + /// + [Fact] + public void TestBindAvatar() { + _wrapper.SetContextAttribute( AngularConst.BindAvatar, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试描述 + /// + [Fact] + public void TestDescription() { + _wrapper.SetContextAttribute( UiConst.Description, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试描述 + /// + [Fact] + public void TestBindDescription() { + _wrapper.SetContextAttribute( AngularConst.BindDescription, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Lists/ListItemMetaTitleTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Lists/ListItemMetaTitleTagHelperTest.cs new file mode 100644 index 000000000..ba67f7fa1 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Lists/ListItemMetaTitleTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Lists; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Lists { + /// + /// 列表项元信息标题测试 + /// + public class ListItemMetaTitleTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ListItemMetaTitleTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ListItemMetaTitleTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Lists/ListItemTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Lists/ListItemTagHelperTest.cs new file mode 100644 index 000000000..47111a3f8 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Lists/ListItemTagHelperTest.cs @@ -0,0 +1,127 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Lists { + /// + /// 列表项测试 + /// + public class ListItemTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ListItemTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ListItemTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列表项操作 + /// + [Fact] + public void TestActions() { + _wrapper.SetContextAttribute( UiConst.Actions, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列表项内容 + /// + [Fact] + public void TestContent() { + _wrapper.SetContextAttribute( UiConst.Content, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列表项内容 + /// + [Fact] + public void TestBindContent() { + _wrapper.SetContextAttribute( AngularConst.BindContent, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列表项扩展 + /// + [Fact] + public void TestExtra() { + _wrapper.SetContextAttribute( UiConst.Extra, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否非flex布局 + /// + [Fact] + public void TestNoFlex() { + _wrapper.SetContextAttribute( UiConst.NoFlex, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚拟滚动循环 + /// + [Fact] + public void TestVirtualFor() { + _wrapper.SetContextAttribute( UiConst.VirtualFor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestAppendContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Lists/ListLoadMoreTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Lists/ListLoadMoreTagHelperTest.cs new file mode 100644 index 000000000..08825c141 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Lists/ListLoadMoreTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Lists; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Lists { + /// + /// 列表加载更多区域测试 + /// + public class ListLoadMoreTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ListLoadMoreTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ListLoadMoreTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Lists/ListPaginationTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Lists/ListPaginationTagHelperTest.cs new file mode 100644 index 000000000..7547efe50 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Lists/ListPaginationTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Lists; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Lists { + /// + /// 列表分页测试 + /// + public class ListPaginationTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ListPaginationTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ListPaginationTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Lists/ListTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Lists/ListTagHelperTest.cs new file mode 100644 index 000000000..90d85f619 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Lists/ListTagHelperTest.cs @@ -0,0 +1,227 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Lists; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Lists { + /// + /// 列表测试 + /// + public class ListTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ListTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ListTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示边框 + /// + [Fact] + public void TestBordered() { + _wrapper.SetContextAttribute( UiConst.Bordered, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列表底部 + /// + [Fact] + public void TestFooter() { + _wrapper.SetContextAttribute( UiConst.Footer, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列表底部 + /// + [Fact] + public void TestBindFooter() { + _wrapper.SetContextAttribute( AngularConst.BindFooter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列表头部 + /// + [Fact] + public void TestHeader() { + _wrapper.SetContextAttribute( UiConst.Header, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列表头部 + /// + [Fact] + public void TestBindHeader() { + _wrapper.SetContextAttribute( AngularConst.BindHeader, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列表项布局方式 + /// + [Fact] + public void TestItemLayout() { + _wrapper.SetContextAttribute( UiConst.ItemLayout, ListItemLayout.Horizontal ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列表项布局方式 + /// + [Fact] + public void TestBindItemLayout() { + _wrapper.SetContextAttribute( AngularConst.BindItemLayout, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否加载状态 + /// + [Fact] + public void TestLoading() { + _wrapper.SetContextAttribute( UiConst.Loading, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列表大小 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, ListSize.Small ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列表大小 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示分割线 + /// + [Fact] + public void TestSplit() { + _wrapper.SetContextAttribute( UiConst.Split, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试栅格 + /// + [Fact] + public void TestGrid() { + _wrapper.SetContextAttribute( UiConst.Grid, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试栅格 + /// + [Fact] + public void TestGrid_2() { + _wrapper.SetContextAttribute( UiConst.Grid, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数据源 + /// + [Fact] + public void TestDataSource() { + _wrapper.SetContextAttribute( UiConst.Datasource, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义项模板 + /// + [Fact] + public void TestRenderItem() { + _wrapper.SetContextAttribute( UiConst.RenderItem, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Mentions/MentionTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Mentions/MentionTagHelperTest.cs new file mode 100644 index 000000000..3e6e8e17a --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Mentions/MentionTagHelperTest.cs @@ -0,0 +1,216 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Mentions; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Mentions { + /// + /// 提及测试 + /// + public class MentionTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public MentionTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new MentionTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试建议内容 + /// + [Fact] + public void TestSuggestions() { + _wrapper.SetContextAttribute( UiConst.Suggestions, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载状态 + /// + [Fact] + public void TestLoading() { + _wrapper.SetContextAttribute( UiConst.Loading, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试取值函数 + /// + [Fact] + public void TestValueWith() { + _wrapper.SetContextAttribute( UiConst.ValueWith, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试触发前缀 + /// + [Fact] + public void TestPrefix() { + _wrapper.SetContextAttribute( UiConst.Prefix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试建议框位置 + /// + [Fact] + public void TestPlacement() { + _wrapper.SetContextAttribute( UiConst.Placement, MentionPlacement.Top ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试建议框位置 + /// + [Fact] + public void TestBindPlacement() { + _wrapper.SetContextAttribute( AngularConst.BindPlacement, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试空建议默认内容 + /// + [Fact] + public void TestNotFoundContent() { + _wrapper.SetContextAttribute( UiConst.NotFoundContent, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试空建议默认内容 + /// + [Fact] + public void TestBindNotFoundContent() { + _wrapper.SetContextAttribute( AngularConst.BindNotFoundContent, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试指定提及的触发元素 + /// + [Fact] + public void TestMentionTrigger() { + _wrapper.SetContextAttribute( UiConst.MentionTrigger, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义建议渲染模板 + /// + [Fact] + public void TestMentionSuggestion() { + _wrapper.SetContextAttribute( UiConst.MentionSuggestion, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试校验状态 + /// + [Fact] + public void TestStatus() { + _wrapper.SetContextAttribute( UiConst.Status, FormControlStatus.Error ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试校验状态 + /// + [Fact] + public void TestBindStatus() { + _wrapper.SetContextAttribute( AngularConst.BindStatus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选择事件 + /// + [Fact] + public void TestOnSelect() { + _wrapper.SetContextAttribute( UiConst.OnSelect, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试搜索改变事件 + /// + [Fact] + public void TestOnSearchChange() { + _wrapper.SetContextAttribute( UiConst.OnSearchChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Menus/MenuDividerTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Menus/MenuDividerTagHelperTest.cs new file mode 100644 index 000000000..3788baaf2 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Menus/MenuDividerTagHelperTest.cs @@ -0,0 +1,48 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Menus; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Menus { + /// + /// 菜单分隔线测试 + /// + public class MenuDividerTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public MenuDividerTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new MenuDividerTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
          • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Menus/MenuGroupTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Menus/MenuGroupTagHelperTest.cs new file mode 100644 index 000000000..ad367eab7 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Menus/MenuGroupTagHelperTest.cs @@ -0,0 +1,85 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Menus; +using Util.Ui.NgZorro.Configs; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Menus { + /// + /// 菜单组测试 + /// + public class MenuGroupTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public MenuGroupTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new MenuGroupTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
            • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "
              • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 - 多语言 + /// + [Fact] + public void TestTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "
                • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Menus/MenuItemTagHelperTest.I18n.cs b/test/Util.Ui.NgZorro.Tests/Menus/MenuItemTagHelperTest.I18n.cs new file mode 100644 index 000000000..d831f7287 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Menus/MenuItemTagHelperTest.I18n.cs @@ -0,0 +1,173 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Menus { + /// + /// 菜单项测试 - 多语言测试 + /// + public partial class MenuItemTagHelperTest { + /// + /// 测试文本 + /// + [Fact] + public void TestText() { + _wrapper.SetContextAttribute( UiConst.Text, "a" ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + result.Append( "a" ); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本 - 支持多语言 + /// + [Fact] + public void TestText_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Text, "a" ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + result.Append( "{{'a'|i18n}}" ); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Update文本 + /// + [Fact] + public void TestTextUpdate() { + _wrapper.SetContextAttribute( UiConst.TextUpdate, true ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + result.Append( "Update" ); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Update文本 - 支持多语言 + /// + [Fact] + public void TestTextUpdate_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextUpdate, true ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + result.Append( "{{'util.update'|i18n}}" ); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Delete文本 + /// + [Fact] + public void TestTextDelete() { + _wrapper.SetContextAttribute( UiConst.TextDelete, true ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + result.Append( "Delete" ); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Delete文本 - 支持多语言 + /// + [Fact] + public void TestTextDelete_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextDelete, true ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + result.Append( "{{'util.delete'|i18n}}" ); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Detail文本 + /// + [Fact] + public void TestTextDetail() { + _wrapper.SetContextAttribute( UiConst.TextDetail, true ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + result.Append( "Detail" ); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Detail文本 - 支持多语言 + /// + [Fact] + public void TestTextDetail_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextDetail, true ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + result.Append( "{{'util.detail'|i18n}}" ); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Enable文本 + /// + [Fact] + public void TestTextEnable() { + _wrapper.SetContextAttribute( UiConst.TextEnable, true ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + result.Append( "Enable" ); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Enable文本 - 支持多语言 + /// + [Fact] + public void TestTextEnable_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextEnable, true ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + result.Append( "{{'util.enable'|i18n}}" ); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Disable文本 + /// + [Fact] + public void TestTextDisable() { + _wrapper.SetContextAttribute( UiConst.TextDisable, true ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + result.Append( "Disable" ); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Disable文本 - 支持多语言 + /// + [Fact] + public void TestTextDisable_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextDisable, true ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + result.Append( "{{'util.disable'|i18n}}" ); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Menus/MenuItemTagHelperTest.Icon.cs b/test/Util.Ui.NgZorro.Tests/Menus/MenuItemTagHelperTest.Icon.cs new file mode 100644 index 000000000..4733ba15e --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Menus/MenuItemTagHelperTest.Icon.cs @@ -0,0 +1,39 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Enums; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Menus { + /// + /// 菜单项测试 - 图标扩展 + /// + public partial class MenuItemTagHelperTest { + /// + /// 测试图标 + /// + [Fact] + public void TestIcon_1() { + _wrapper.SetContextAttribute( UiConst.Icon, AntDesignIcon.InfoCircle ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + result.Append( "" ); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标 - 添加文本 + /// + [Fact] + public void TestIcon_2() { + _wrapper.SetContextAttribute( UiConst.Text, "a" ); + _wrapper.SetContextAttribute( UiConst.Icon, AntDesignIcon.InfoCircle ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Menus/MenuItemTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Menus/MenuItemTagHelperTest.cs new file mode 100644 index 000000000..7e51a7a1f --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Menus/MenuItemTagHelperTest.cs @@ -0,0 +1,138 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Menus; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Menus { + /// + /// 菜单项测试 + /// + public partial class MenuItemTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public MenuItemTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new MenuItemTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选中 + /// + [Fact] + public void TestSelected() { + _wrapper.SetContextAttribute( UiConst.Selected, "true" ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试危险状态 + /// + [Fact] + public void TestDanger() { + _wrapper.SetContextAttribute( UiConst.Danger, "true" ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试匹配路由 + /// + [Fact] + public void TestMatchRouter() { + _wrapper.SetContextAttribute( UiConst.MatchRouter, "true" ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试精确匹配路由 + /// + [Fact] + public void TestMatchRouterExact() { + _wrapper.SetContextAttribute( UiConst.MatchRouterExact, "true" ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试左内边距 + /// + [Fact] + public void TestPaddingLeft() { + _wrapper.SetContextAttribute( UiConst.PaddingLeft, "a" ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "
                  • a
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击事件 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "
                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Menus/MenuTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Menus/MenuTagHelperTest.cs new file mode 100644 index 000000000..689eb7f85 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Menus/MenuTagHelperTest.cs @@ -0,0 +1,150 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Menus; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Menus { + /// + /// 菜单测试 + /// + public class MenuTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public MenuTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new MenuTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模式 + /// + [Fact] + public void TestMode() { + _wrapper.SetContextAttribute( UiConst.Mode, MenuMode.Inline ); + var result = new StringBuilder(); + result.Append( "
                        " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模式 + /// + [Fact] + public void TestBindMode() { + _wrapper.SetContextAttribute( AngularConst.BindMode, "a" ); + var result = new StringBuilder(); + result.Append( "
                          " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试允许选中 + /// + [Fact] + public void TestSelectable() { + _wrapper.SetContextAttribute( UiConst.Selectable, "false" ); + var result = new StringBuilder(); + result.Append( "
                            " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试主题 + /// + [Fact] + public void TestTheme() { + _wrapper.SetContextAttribute( UiConst.Theme, MenuTheme.Light ); + var result = new StringBuilder(); + result.Append( "
                              " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试主题 + /// + [Fact] + public void TestBindTheme() { + _wrapper.SetContextAttribute( AngularConst.BindTheme, "a" ); + var result = new StringBuilder(); + result.Append( "
                                " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试折叠状态 + /// + [Fact] + public void TestInlineCollapsed() { + _wrapper.SetContextAttribute( UiConst.InlineCollapsed, "false" ); + var result = new StringBuilder(); + result.Append( "
                                  " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试缩进宽度 + /// + [Fact] + public void TestInlineIndent() { + _wrapper.SetContextAttribute( UiConst.InlineIndent, 2 ); + var result = new StringBuilder(); + result.Append( "
                                    " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试缩进宽度 + /// + [Fact] + public void TestBindInlineIndent() { + _wrapper.SetContextAttribute( AngularConst.BindInlineIndent, "a" ); + var result = new StringBuilder(); + result.Append( "
                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击事件 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "
                                        " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Menus/SubMenuTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Menus/SubMenuTagHelperTest.cs new file mode 100644 index 000000000..81d3b6ae3 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Menus/SubMenuTagHelperTest.cs @@ -0,0 +1,218 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Menus; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Menus { + /// + /// 子菜单测试 + /// + public class SubMenuTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public SubMenuTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new SubMenuTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
                                        • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "
                                          • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 - 多语言 + /// + [Fact] + public void TestTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "
                                            • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "
                                              • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "
                                                • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标 + /// + [Fact] + public void TestIcon() { + _wrapper.SetContextAttribute( UiConst.Icon, AntDesignIcon.Alert ); + var result = new StringBuilder(); + result.Append( "
                                                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标 + /// + [Fact] + public void TestBindIcon() { + _wrapper.SetContextAttribute( AngularConst.BindIcon, "a" ); + var result = new StringBuilder(); + result.Append( "
                                                    • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试展开 + /// + [Fact] + public void TestOpen() { + _wrapper.SetContextAttribute( UiConst.Open, "true" ); + var result = new StringBuilder(); + result.Append( "
                                                      • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试展开 + /// + [Fact] + public void TestBindonOpen() { + _wrapper.SetContextAttribute( AngularConst.BindonOpen, "a" ); + var result = new StringBuilder(); + result.Append( "
                                                        • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义子菜单容器类名 + /// + [Fact] + public void TestMenuClassName() { + _wrapper.SetContextAttribute( UiConst.MenuClassName, "a" ); + var result = new StringBuilder(); + result.Append( "
                                                          • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义子菜单容器类名 + /// + [Fact] + public void TestBindMenuClassName() { + _wrapper.SetContextAttribute( AngularConst.BindMenuClassName, "a" ); + var result = new StringBuilder(); + result.Append( "
                                                            • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试菜单弹出位置 + /// + [Fact] + public void TestPlacement() { + _wrapper.SetContextAttribute( UiConst.Placement, DropdownMenuPlacement.BottomLeft ); + var result = new StringBuilder(); + result.Append( "
                                                              • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试菜单弹出位置 + /// + [Fact] + public void TestBindPlacement() { + _wrapper.SetContextAttribute( AngularConst.BindPlacement, "a" ); + var result = new StringBuilder(); + result.Append( "
                                                                • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试左内边距 + /// + [Fact] + public void TestPaddingLeft() { + _wrapper.SetContextAttribute( UiConst.PaddingLeft, "a" ); + var result = new StringBuilder(); + result.Append( "
                                                                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "
                                                                    • a
                                                                  • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试展开变更事件 + /// + [Fact] + public void TestOnOpenChange() { + _wrapper.SetContextAttribute( UiConst.OnOpenChange, "a" ); + var result = new StringBuilder(); + result.Append( "
                                                                    • " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Modals/ModalContentTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Modals/ModalContentTagHelperTest.cs new file mode 100644 index 000000000..0a9bd26c3 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Modals/ModalContentTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Modals; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Modals; + +/// +/// 对话框内容测试 +/// +public class ModalContentTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ModalContentTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ModalContentTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestAppendContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "
                                                                      a
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Modals/ModalFooterTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Modals/ModalFooterTagHelperTest.cs new file mode 100644 index 000000000..6e98b76d3 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Modals/ModalFooterTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Modals; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Modals; + +/// +/// 对话框页脚测试 +/// +public class ModalFooterTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ModalFooterTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ModalFooterTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestAppendContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "
                                                                      a
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Modals/ModalTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Modals/ModalTagHelperTest.cs new file mode 100644 index 000000000..245683459 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Modals/ModalTagHelperTest.cs @@ -0,0 +1,548 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Modals; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Modals; + +/// +/// 对话框测试 +/// +public class ModalTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ModalTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ModalTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 - 支持多语言 + /// + [Fact] + public void TestTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示遮罩 + /// + [Fact] + public void TestMask() { + _wrapper.SetContextAttribute( UiConst.Mask, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试点击遮罩是否允许关闭 + /// + [Fact] + public void TestMaskClosable() { + _wrapper.SetContextAttribute( UiConst.MaskClosable, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试导航时是否关闭 + /// + [Fact] + public void TestCloseOnNavigation() { + _wrapper.SetContextAttribute( UiConst.CloseOnNavigation, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否可见 + /// + [Fact] + public void TestVisible() { + _wrapper.SetContextAttribute( UiConst.Visible, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否可关闭 + /// + [Fact] + public void TestClosable() { + _wrapper.SetContextAttribute( UiConst.Closable, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模态框是否可拖动 + /// + [Fact] + public void TestDraggable() { + _wrapper.SetContextAttribute( UiConst.Draggable, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试确定按钮是否加载状态 + /// + [Fact] + public void TestOkLoading() { + _wrapper.SetContextAttribute( UiConst.OkLoading, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否禁用确定按钮 + /// + [Fact] + public void TestOkDisabled() { + _wrapper.SetContextAttribute( UiConst.OkDisabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试取消按钮是否加载状态 + /// + [Fact] + public void TestCancelLoading() { + _wrapper.SetContextAttribute( UiConst.CancelLoading, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否禁用取消按钮 + /// + [Fact] + public void TestCancelDisabled() { + _wrapper.SetContextAttribute( UiConst.CancelDisabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否支持键盘ESC键关闭 + /// + [Fact] + public void TestKeyboard() { + _wrapper.SetContextAttribute( UiConst.Keyboard, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否垂直居中显示 + /// + [Fact] + public void TestCentered() { + _wrapper.SetContextAttribute( UiConst.Centered, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.SetContextAttribute( UiConst.Content, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试组件参数 + /// + [Fact] + public void TestComponentParams() { + _wrapper.SetContextAttribute( UiConst.ComponentParams, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试底部内容 + /// + [Fact] + public void TestFooter() { + _wrapper.SetContextAttribute( UiConst.Footer, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试z-index + /// + [Fact] + public void TestZIndex() { + _wrapper.SetContextAttribute( UiConst.ZIndex, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽度 + /// + [Fact] + public void TestWidth() { + _wrapper.SetContextAttribute( UiConst.Width, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试对话框外层容器样式类名 + /// + [Fact] + public void TestWrapClassName() { + _wrapper.SetContextAttribute( UiConst.WrapClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试对话框外层容器样式类名 + /// + [Fact] + public void TestBindWrapClassName() { + _wrapper.SetContextAttribute( AngularConst.BindWrapClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试对话框样式类名 + /// + [Fact] + public void TestClassName() { + _wrapper.SetContextAttribute( UiConst.ClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试对话框样式类名 + /// + [Fact] + public void TestBindClassName() { + _wrapper.SetContextAttribute( AngularConst.BindClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试浮层样式 + /// + [Fact] + public void TestNzStyle() { + _wrapper.SetContextAttribute( UiConst.NzStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试关闭图标 + /// + [Fact] + public void TestCloseIcon() { + _wrapper.SetContextAttribute( UiConst.CloseIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试关闭图标 + /// + [Fact] + public void TestBindCloseIcon() { + _wrapper.SetContextAttribute( AngularConst.BindCloseIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试遮罩样式 + /// + [Fact] + public void TestMaskStyle() { + _wrapper.SetContextAttribute( UiConst.MaskStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试主体样式 + /// + [Fact] + public void TestBodyStyle() { + _wrapper.SetContextAttribute( UiConst.BodyStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试确认按钮文字 + /// + [Fact] + public void TestOkText() { + _wrapper.SetContextAttribute( UiConst.OkText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试确认按钮文字 + /// + [Fact] + public void TestBindOkText() { + _wrapper.SetContextAttribute( AngularConst.BindOkText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试取消按钮文字 + /// + [Fact] + public void TestCancelText() { + _wrapper.SetContextAttribute( UiConst.CancelText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试取消按钮文字 + /// + [Fact] + public void TestBindCancelText() { + _wrapper.SetContextAttribute( AngularConst.BindCancelText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试确认按钮类型 + /// + [Fact] + public void TestOkType() { + _wrapper.SetContextAttribute( UiConst.OkType, ButtonType.Primary ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试确认按钮类型 + /// + [Fact] + public void TestBindOkType() { + _wrapper.SetContextAttribute( AngularConst.BindOkType, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试确认按钮是否为危险按钮 + /// + [Fact] + public void TestOkDanger() { + _wrapper.SetContextAttribute( UiConst.OkDanger, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标类型 + /// + [Fact] + public void TestIconType() { + _wrapper.SetContextAttribute( UiConst.IconType, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标类型 + /// + [Fact] + public void TestBindIconType() { + _wrapper.SetContextAttribute( AngularConst.BindIconType, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动聚焦 + /// + [Fact] + public void TestAutofocus() { + _wrapper.SetContextAttribute( UiConst.Autofocus, ModalAutofocus.Cancel ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动聚焦 + /// + [Fact] + public void TestBindAutofocus() { + _wrapper.SetContextAttribute( AngularConst.BindAutofocus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试子内容 + /// + [Fact] + public void TestAppendContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试确定事件 + /// + [Fact] + public void TestOnOk() { + _wrapper.SetContextAttribute( UiConst.OnOk, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试取消事件 + /// + [Fact] + public void TestOnCancel() { + _wrapper.SetContextAttribute( UiConst.OnCancel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试打开后事件 + /// + [Fact] + public void TestOnAfterOpen() { + _wrapper.SetContextAttribute( UiConst.OnAfterOpen, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试关闭后事件 + /// + [Fact] + public void TestOnAfterClose() { + _wrapper.SetContextAttribute( UiConst.OnAfterClose, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Modals/ModalTitleTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Modals/ModalTitleTagHelperTest.cs new file mode 100644 index 000000000..f5b7a7214 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Modals/ModalTitleTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Modals; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Modals; + +/// +/// 对话框标题测试 +/// +public class ModalTitleTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ModalTitleTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ModalTitleTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestAppendContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "
                                                                      a
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderContentTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderContentTagHelperTest.cs new file mode 100644 index 000000000..e0bf77887 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderContentTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.PageHeaders; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.PageHeaders { + /// + /// 页头内容测试 + /// + public class PageHeaderContentTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public PageHeaderContentTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new PageHeaderContentTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderExtraTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderExtraTagHelperTest.cs new file mode 100644 index 000000000..7c21fe0c1 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderExtraTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.PageHeaders; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.PageHeaders { + /// + /// 页头标题行尾操作区测试 + /// + public class PageHeaderExtraTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public PageHeaderExtraTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new PageHeaderExtraTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderFooterTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderFooterTagHelperTest.cs new file mode 100644 index 000000000..586cca561 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderFooterTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.PageHeaders; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.PageHeaders { + /// + /// 页头底部测试 + /// + public class PageHeaderFooterTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public PageHeaderFooterTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new PageHeaderFooterTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderSubTitleTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderSubTitleTagHelperTest.cs new file mode 100644 index 000000000..91c4a4ac5 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderSubTitleTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.PageHeaders; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.PageHeaders { + /// + /// 页头子标题测试 + /// + public class PageHeaderSubTitleTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public PageHeaderSubTitleTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new PageHeaderSubTitleTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderTagHelperTest.cs new file mode 100644 index 000000000..dfb55e12c --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderTagHelperTest.cs @@ -0,0 +1,208 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.PageHeaders; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.PageHeaders { + /// + /// 页头测试 + /// + public class PageHeaderTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public PageHeaderTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new PageHeaderTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试透明背景 + /// + [Fact] + public void TestGhost() { + _wrapper.SetContextAttribute( UiConst.Ghost, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 - 多语言 + /// + [Fact] + public void TestTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试子标题 + /// + [Fact] + public void TestSubtitle() { + _wrapper.SetContextAttribute( UiConst.Subtitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试子标题 - 多语言 + /// + [Fact] + public void TestSubtitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Subtitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试子标题 + /// + [Fact] + public void TestBindSubtitle() { + _wrapper.SetContextAttribute( AngularConst.BindSubtitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示返回按钮 + /// + [Fact] + public void TestShowBack_True() { + _wrapper.SetContextAttribute( UiConst.ShowBack, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示返回按钮 + /// + [Fact] + public void TestShowBack_False() { + _wrapper.SetContextAttribute( UiConst.ShowBack, false ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置返回按钮图标 + /// + [Fact] + public void TestBackIcon() { + _wrapper.SetContextAttribute( UiConst.BackIcon, AntDesignIcon.Smile ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置返回按钮图标 + /// + [Fact] + public void TestBindBackIcon() { + _wrapper.SetContextAttribute( AngularConst.BindBackIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置返回按钮图标,同时设置ShowBack + /// + [Fact] + public void TestBindBackIcon_ShowBack() { + _wrapper.SetContextAttribute( AngularConst.BindBackIcon, "a" ).SetContextAttribute( UiConst.ShowBack, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试返回事件 + /// + [Fact] + public void TestOnBack() { + _wrapper.SetContextAttribute( UiConst.OnBack, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderTagsTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderTagsTagHelperTest.cs new file mode 100644 index 000000000..c02efd27d --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderTagsTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.PageHeaders; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.PageHeaders { + /// + /// 页头标签列表测试 + /// + public class PageHeaderTagsTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public PageHeaderTagsTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new PageHeaderTagsTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderTitleTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderTitleTagHelperTest.cs new file mode 100644 index 000000000..cdbbdb8ca --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/PageHeaders/PageHeaderTitleTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.PageHeaders; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.PageHeaders { + /// + /// 页头标题测试 + /// + public class PageHeaderTitleTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public PageHeaderTitleTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new PageHeaderTitleTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Paginations/PaginationTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Paginations/PaginationTagHelperTest.cs new file mode 100644 index 000000000..f02029634 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Paginations/PaginationTagHelperTest.cs @@ -0,0 +1,260 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Paginations; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Paginations { + /// + /// 分页测试 + /// + public class PaginationTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public PaginationTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new PaginationTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试总数 + /// + [Fact] + public void TestTotal() { + _wrapper.SetContextAttribute( UiConst.Total, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试当前页 + /// + [Fact] + public void TestPageIndex() { + _wrapper.SetContextAttribute( UiConst.PageIndex, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试当前页 + /// + [Fact] + public void TestBindonPageIndex() { + _wrapper.SetContextAttribute( AngularConst.BindonPageIndex, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试每页显示行数 + /// + [Fact] + public void TestPageSize() { + _wrapper.SetContextAttribute( UiConst.PageSize, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试每页显示行数 + /// + [Fact] + public void TestBindonPageSize() { + _wrapper.SetContextAttribute( AngularConst.BindonPageSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示改变分页大小按钮 + /// + [Fact] + public void TestShowSizeChanger() { + _wrapper.SetContextAttribute( UiConst.ShowSizeChanger, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示快速跳转 + /// + [Fact] + public void TestShowQuickJumper() { + _wrapper.SetContextAttribute( UiConst.ShowQuickJumper, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, PaginationSize.Small ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示总行数和当前数据范围的模板 + /// + [Fact] + public void TestShowTotal() { + _wrapper.SetContextAttribute( UiConst.ShowTotal, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试简单分页 + /// + [Fact] + public void TestSimple() { + _wrapper.SetContextAttribute( UiConst.Simple, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试响应式 + /// + [Fact] + public void TestResponsive() { + _wrapper.SetContextAttribute( UiConst.Responsive, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试每页行数选择列表 + /// + [Fact] + public void TestPageSizeOptions() { + _wrapper.SetContextAttribute( UiConst.PageSizeOptions, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义页码结构 + /// + [Fact] + public void TestItemRender() { + _wrapper.SetContextAttribute( UiConst.ItemRender, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试只有一页时是否隐藏分页器 + /// + [Fact] + public void TestHideOnSinglePage() { + _wrapper.SetContextAttribute( UiConst.HideOnSinglePage, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试页码改变事件 + /// + [Fact] + public void TestOnPageIndexChange() { + _wrapper.SetContextAttribute( UiConst.OnPageIndexChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试每页行数改变事件 + /// + [Fact] + public void TestOnPageSizeChange() { + _wrapper.SetContextAttribute( UiConst.OnPageSizeChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Progresses/ProgressTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Progresses/ProgressTagHelperTest.cs new file mode 100644 index 000000000..403d8b960 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Progresses/ProgressTagHelperTest.cs @@ -0,0 +1,304 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Progresses; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Progresses { + /// + /// 进度条测试 + /// + public class ProgressTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ProgressTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ProgressTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试类型 + /// + [Fact] + public void TestType() { + _wrapper.SetContextAttribute( UiConst.Type, ProgressType.Dashboard ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试类型 + /// + [Fact] + public void TestBindType() { + _wrapper.SetContextAttribute( AngularConst.BindType, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试格式化函数 + /// + [Fact] + public void TestFormat() { + _wrapper.SetContextAttribute( UiConst.Format, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试百分比 + /// + [Fact] + public void TestPercent() { + _wrapper.SetContextAttribute( UiConst.Percent, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示进度信息 + /// + [Fact] + public void TestShowInfo() { + _wrapper.SetContextAttribute( UiConst.ShowInfo, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestStatus() { + _wrapper.SetContextAttribute( UiConst.Status, ProgressStatus.Exception ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestBindStatus() { + _wrapper.SetContextAttribute( AngularConst.BindStatus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试端点形状 + /// + [Fact] + public void TestStrokeLinecap() { + _wrapper.SetContextAttribute( UiConst.StrokeLinecap, ProgressStrokeLinecap.Round ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试端点形状 + /// + [Fact] + public void TestBindStrokeLinecap() { + _wrapper.SetContextAttribute( AngularConst.BindStrokeLinecap, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色 + /// + [Fact] + public void TestStrokeColorType() { + _wrapper.SetContextAttribute( UiConst.StrokeColorType, AntDesignColor.Red ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色 + /// + [Fact] + public void TestStrokeColor() { + _wrapper.SetContextAttribute( UiConst.StrokeColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色 + /// + [Fact] + public void TestBindStrokeColor() { + _wrapper.SetContextAttribute( AngularConst.BindStrokeColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试已完成分段百分比 + /// + [Fact] + public void TestSuccessPercent() { + _wrapper.SetContextAttribute( UiConst.SuccessPercent, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试线条宽度 + /// + [Fact] + public void TestStrokeWidth() { + _wrapper.SetContextAttribute( UiConst.StrokeWidth, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试总步数 + /// + [Fact] + public void TestSteps() { + _wrapper.SetContextAttribute( UiConst.Steps, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试画布宽度 + /// + [Fact] + public void TestWidth() { + _wrapper.SetContextAttribute( UiConst.Width, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试缺口角度 + /// + [Fact] + public void TestGapDegree() { + _wrapper.SetContextAttribute( UiConst.GapDegree, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试缺口位置 + /// + [Fact] + public void TestGapPosition() { + _wrapper.SetContextAttribute( UiConst.GapPosition, ProgressGapPosition.Bottom ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试缺口位置 + /// + [Fact] + public void TestBindGapPosition() { + _wrapper.SetContextAttribute( AngularConst.BindGapPosition, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, ProgressSize.Small ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示文字 + /// + [Fact] + public void TestTooltipTitle() { + _wrapper.SetContextAttribute( UiConst.TooltipTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/QrCodes/QrCodeTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/QrCodes/QrCodeTagHelperTest.cs new file mode 100644 index 000000000..806f35a44 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/QrCodes/QrCodeTagHelperTest.cs @@ -0,0 +1,249 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.QrCodes; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.QrCodes; + +/// +/// 二维码测试 +/// +public class QrCodeTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public QrCodeTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new QrCodeTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestValue() { + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestBindValue() { + _wrapper.SetContextAttribute( AngularConst.BindValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色 + /// + [Fact] + public void TestColor() { + _wrapper.SetContextAttribute( UiConst.Color, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色 + /// + [Fact] + public void TestBindColor() { + _wrapper.SetContextAttribute( AngularConst.BindColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试背景色 + /// + [Fact] + public void TestBgColor() { + _wrapper.SetContextAttribute( UiConst.BgColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试背景色 + /// + [Fact] + public void TestBindBgColor() { + _wrapper.SetContextAttribute( AngularConst.BindBgColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试填充 + /// + [Fact] + public void TestPadding() { + _wrapper.SetContextAttribute( UiConst.Padding, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标地址 + /// + [Fact] + public void TestIcon() { + _wrapper.SetContextAttribute( UiConst.Icon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标地址 + /// + [Fact] + public void TestBindIcon() { + _wrapper.SetContextAttribute( AngularConst.BindIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标尺寸 + /// + [Fact] + public void TestIconSize() { + _wrapper.SetContextAttribute( UiConst.IconSize, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试边框 + /// + [Fact] + public void TestBordered() { + _wrapper.SetContextAttribute( UiConst.Bordered, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestStatus() { + _wrapper.SetContextAttribute( UiConst.Status, QrCodeStatus.Expired ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestBindStatus() { + _wrapper.SetContextAttribute( AngularConst.BindStatus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试容错级别 + /// + [Fact] + public void TestLevel() { + _wrapper.SetContextAttribute( UiConst.Level, QrCodeCorrectionLevel.H ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试容错级别 + /// + [Fact] + public void TestBindLevel() { + _wrapper.SetContextAttribute( AngularConst.BindLevel, 'a' ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试刷新事件 + /// + [Fact] + public void TestOnRefresh() { + _wrapper.SetContextAttribute( UiConst.OnRefresh, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Radios/RadioButtonTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Radios/RadioButtonTagHelperTest.cs new file mode 100644 index 000000000..7da15a136 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Radios/RadioButtonTagHelperTest.cs @@ -0,0 +1,62 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.NgZorro.Components.Radios; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Radios { + /// + /// 按钮样式单选框测试 + /// + public class RadioButtonTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public RadioButtonTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new RadioButtonTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Radios/RadioGroupTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/Radios/RadioGroupTagHelperTest.Expression.cs new file mode 100644 index 000000000..82c9f6245 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Radios/RadioGroupTagHelperTest.Expression.cs @@ -0,0 +1,121 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Radios { + /// + /// 单选框组合测试 - 表达式解析测试 + /// + public partial class RadioGroupTagHelperTest { + /// + /// 测试属性表达式 - 布尔值 + /// + [Fact] + public void TestFor_Bool() { + _wrapper.SetItem( new FormShareConfig { FormCreated = true } ); + _wrapper.SetExpression( t => t.Enabled ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "启用" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "@for(item of x_enabled.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 布尔值 - 多语言 + /// + [Fact] + public void TestFor_Bool_I18n() { + _wrapper.SetItem( new FormShareConfig { FormCreated = true } ); + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetExpression( t => t.Enabled ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'启用'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "@for(item of x_enabled.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 枚举值 + /// + [Fact] + public void TestFor_Enum() { + _wrapper.SetItem( new FormShareConfig { FormCreated = true } ); + _wrapper.SetExpression( t => t.Gender ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "性别" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "@for(item of x_gender.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 枚举值 - 多语言 + /// + [Fact] + public void TestFor_Enum_I18n() { + _wrapper.SetItem( new FormShareConfig { FormCreated = true } ); + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetExpression( t => t.Gender ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'性别'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "@for(item of x_gender.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Radios/RadioGroupTagHelperTest.Extend.cs b/test/Util.Ui.NgZorro.Tests/Radios/RadioGroupTagHelperTest.Extend.cs new file mode 100644 index 000000000..61a138b35 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Radios/RadioGroupTagHelperTest.Extend.cs @@ -0,0 +1,222 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Radios; +using Util.Ui.NgZorro.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Radios { + /// + /// 单选框组合测试 - 指令扩展测试 + /// + public partial class RadioGroupTagHelperTest { + /// + /// 测试启用扩展指令 + /// + [Fact] + public void TestEnableExtend_1() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用扩展指令 - 不启用 + /// + [Fact] + public void TestEnableExtend_2() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, false ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试初始化时是否自动加载数据 + /// + [Fact] + public void TestAutoLoad() { + _wrapper.SetContextAttribute( UiConst.AutoLoad, false ); + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试查询参数 + /// + [Fact] + public void TestQueryParam() { + _wrapper.SetContextAttribute( UiConst.QueryParam, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试排序条件 + /// + [Fact] + public void TestSort() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( UiConst.Sort, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试排序条件 + /// + [Fact] + public void TestBindSort() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( AngularConst.BindSort, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置数据源 + /// + [Fact] + public void TestData_1() { + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置数据源 - 多语言 + /// + [Fact] + public void TestData_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置数据源 - 创建单选框,则不自动生成 + /// + [Fact] + public void TestData_2() { + //设置数据源 + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + + //创建单选框 + var radio = new RadioTagHelper().ToWrapper(); + radio.SetContextAttribute( UiConst.Id, "radio1" ); + _wrapper.AppendContent( radio ); + + //验证 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置Api地址 + /// + [Fact] + public void TestUrl() { + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置Api地址 + /// + [Fact] + public void TestBindUrl() { + _wrapper.SetContextAttribute( AngularConst.BindUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数据加载完成事件 + /// + [Fact] + public void TestOnLoad() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( UiConst.OnLoad, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Radios/RadioGroupTagHelperTest.FormItem.cs b/test/Util.Ui.NgZorro.Tests/Radios/RadioGroupTagHelperTest.FormItem.cs new file mode 100644 index 000000000..112f8be31 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Radios/RadioGroupTagHelperTest.FormItem.cs @@ -0,0 +1,32 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Checkboxes; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Radios { + /// + /// 单选框组合测试 - 表单项扩展测试 + /// + public partial class RadioGroupTagHelperTest { + /// + /// 测试表单容器属性 + /// + [Fact] + public void TestFormContainer() { + var checkbox = new CheckboxTagHelper().ToWrapper(); + checkbox.SetContextAttribute( UiConst.LabelText, "b" ); + _wrapper.AppendContent( checkbox ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Radios/RadioGroupTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Radios/RadioGroupTagHelperTest.cs new file mode 100644 index 000000000..60ef1cf3e --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Radios/RadioGroupTagHelperTest.cs @@ -0,0 +1,189 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Radios; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Radios { + /// + /// 单选框组合测试 + /// + public partial class RadioGroupTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public RadioGroupTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new RadioGroupTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试名称 + /// + [Fact] + public void TestName() { + _wrapper.SetContextAttribute( UiConst.Name, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试名称 + /// + [Fact] + public void TestBindName() { + _wrapper.SetContextAttribute( AngularConst.BindName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, ButtonSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试风格 + /// + [Fact] + public void TestButtonStyle() { + _wrapper.SetContextAttribute( UiConst.ButtonStyle, RadioStyle.Outline ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试风格 + /// + [Fact] + public void TestBindButtonStyle() { + _wrapper.SetContextAttribute( AngularConst.BindButtonStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem_1() { + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem_2() { + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + _wrapper.AppendContent( new RadioTagHelper().ToWrapper() ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestNgModel() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestBindNgModel() { + _wrapper.SetContextAttribute( AngularConst.BindNgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型变更事件 + /// + [Fact] + public void TestOnModelChange() { + _wrapper.SetContextAttribute( UiConst.OnModelChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Radios/RadioTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/Radios/RadioTagHelperTest.Expression.cs new file mode 100644 index 000000000..94021e4c0 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Radios/RadioTagHelperTest.Expression.cs @@ -0,0 +1,150 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Radios { + /// + /// 单选框测试 - 表达式解析测试 + /// + public partial class RadioTagHelperTest { + /// + /// 测试属性表达式 - 布尔值 + /// + [Fact] + public void TestFor_Bool() { + _wrapper.SetItem( new FormShareConfig { FormCreated = true } ); + _wrapper.SetExpression( t => t.Enabled ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "启用" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "@for(item of x_enabled.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 布尔值 - 多语言 + /// + [Fact] + public void TestFor_Bool_2() { + _wrapper.SetItem( new FormShareConfig { FormCreated = true } ); + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetExpression( t => t.Enabled ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'启用'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "@for(item of x_enabled.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 布尔值 - 必填项 + /// + [Fact] + public void TestFor_Bool_3() { + _wrapper.SetItem( new FormShareConfig { FormCreated = true } ); + _wrapper.SetExpression( t => t.IsRequired ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "必填项" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "@for(item of x_is_required.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + result.Append( "{{v_is_required.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 枚举值 + /// + [Fact] + public void TestFor_Enum() { + _wrapper.SetItem( new FormShareConfig { FormCreated = true } ); + _wrapper.SetExpression( t => t.Gender ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "性别" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "@for(item of x_gender.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 枚举值 - 多语言 + /// + [Fact] + public void TestFor_Enum_I18n() { + _wrapper.SetItem( new FormShareConfig { FormCreated = true } ); + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetExpression( t => t.Gender ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'性别'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "@for(item of x_gender.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Radios/RadioTagHelperTest.Extend.cs b/test/Util.Ui.NgZorro.Tests/Radios/RadioTagHelperTest.Extend.cs new file mode 100644 index 000000000..1ce04df75 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Radios/RadioTagHelperTest.Extend.cs @@ -0,0 +1,310 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Radios; +using Util.Ui.NgZorro.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Radios { + /// + /// 单选框测试 - 指令扩展测试 + /// + public partial class RadioTagHelperTest { + /// + /// 测试设置标签 + /// + [Fact] + public void TestLabel() { + _wrapper.SetContextAttribute( UiConst.Label, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置标签 - 多语言 + /// + [Fact] + public void TestLabel_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Label, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置标签绑定 + /// + [Fact] + public void TestBindLabel() { + _wrapper.SetContextAttribute( AngularConst.BindLabel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用扩展指令 + /// + [Fact] + public void TestEnableExtend_1() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用扩展指令 - 不启用 + /// + [Fact] + public void TestEnableExtend_2() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, false ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试初始化时是否自动加载数据 + /// + [Fact] + public void TestAutoLoad() { + _wrapper.SetContextAttribute( UiConst.AutoLoad, false ); + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试查询参数 + /// + [Fact] + public void TestQueryParam() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( UiConst.QueryParam, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试排序条件 + /// + [Fact] + public void TestSort() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( UiConst.Sort, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试排序条件 + /// + [Fact] + public void TestBindSort() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( AngularConst.BindSort, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置数据源 + /// + [Fact] + public void TestData_1() { + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置数据源 - 多语言 + /// + [Fact] + public void TestData_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置数据源 - 设置了单选框组合,则不自动生成 + /// + [Fact] + public void TestData_2() { + var group = new RadioGroupTagHelper().ToWrapper(); + group.SetContextAttribute( UiConst.Id, "group1" ); + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + group.AppendContent( _wrapper ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_group1.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + var actual = group.GetResult(); + _output.WriteLine( actual ); + Assert.Equal( result.ToString(), actual ); + } + + /// + /// 测试设置数据源 - Name + /// + [Fact] + public void TestData_3() { + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + _wrapper.SetContextAttribute( UiConst.Name, "b" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置数据源 - BindName + /// + [Fact] + public void TestData_4() { + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + _wrapper.SetContextAttribute( AngularConst.BindName, "b" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置数据源 - OnModelChange + /// + [Fact] + public void TestData_5() { + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + _wrapper.SetContextAttribute( UiConst.OnModelChange, "b" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置Api地址 + /// + [Fact] + public void TestUrl() { + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置Api地址 + /// + [Fact] + public void TestBindUrl() { + _wrapper.SetContextAttribute( AngularConst.BindUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数据加载完成事件 + /// + [Fact] + public void TestOnLoad() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( UiConst.OnLoad, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.value ){" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Radios/RadioTagHelperTest.FormItem.cs b/test/Util.Ui.NgZorro.Tests/Radios/RadioTagHelperTest.FormItem.cs new file mode 100644 index 000000000..30059ff3d --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Radios/RadioTagHelperTest.FormItem.cs @@ -0,0 +1,25 @@ +using System.Text; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Radios { + /// + /// 单选框测试 - 表单项扩展测试 + /// + public partial class RadioTagHelperTest { + /// + /// 测试控件栅格跨度 + /// + [Fact] + public void TestControlSpan() { + _wrapper.SetContextAttribute( UiConst.ControlSpan, 2 ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Radios/RadioTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Radios/RadioTagHelperTest.cs new file mode 100644 index 000000000..f685fdd76 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Radios/RadioTagHelperTest.cs @@ -0,0 +1,175 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Radios; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Radios { + /// + /// 单选框测试 + /// + public partial class RadioTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public RadioTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new RadioTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加标识 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置名称 + /// + [Fact] + public void TestName() { + _wrapper.SetContextAttribute( UiConst.Name, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置名称 + /// + [Fact] + public void TestBindName() { + _wrapper.SetContextAttribute( AngularConst.BindName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动获取焦点 + /// + [Fact] + public void TestAutoFocus() { + _wrapper.SetContextAttribute( UiConst.AutoFocus, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestNgModel() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestValue() { + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestBindValue() { + _wrapper.SetContextAttribute( AngularConst.BindValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 - 如果设置内容,则label属性将无效 + /// + [Fact] + public void TestContent_2() { + _wrapper.SetContextAttribute( UiConst.Label, "a" ); + _wrapper.AppendContent( "b" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Rates/RateTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Rates/RateTagHelperTest.cs new file mode 100644 index 000000000..669522c35 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Rates/RateTagHelperTest.cs @@ -0,0 +1,192 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Rates; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Rates { + /// + /// 评分测试 + /// + public class RateTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public RateTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new RateTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试允许清除 + /// + [Fact] + public void TestAllowClear() { + _wrapper.SetContextAttribute( UiConst.AllowClear, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试允许半选 + /// + [Fact] + public void TestAllowHalf() { + _wrapper.SetContextAttribute( UiConst.AllowHalf, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动聚焦 + /// + [Fact] + public void TestAutoFocus() { + _wrapper.SetContextAttribute( UiConst.AutoFocus, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义字符 + /// + [Fact] + public void TestCharacter() { + _wrapper.SetContextAttribute( UiConst.Character, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数量 + /// + [Fact] + public void TestCount() { + _wrapper.SetContextAttribute( UiConst.Count, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示信息 + /// + [Fact] + public void TestTooltips() { + _wrapper.SetContextAttribute( UiConst.Tooltips, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型变更事件 + /// + [Fact] + public void TestOnModelChange() { + _wrapper.SetContextAttribute( UiConst.OnModelChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试获得焦点事件 + /// + [Fact] + public void TestOnFocus() { + _wrapper.SetContextAttribute( UiConst.OnFocus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试失去焦点事件 + /// + [Fact] + public void TestOnBlur() { + _wrapper.SetContextAttribute( UiConst.OnBlur, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试滑动变更事件 + /// + [Fact] + public void TestOnHoverChange() { + _wrapper.SetContextAttribute( UiConst.OnHoverChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试键盘按下事件 + /// + [Fact] + public void TestOnKeyDown() { + _wrapper.SetContextAttribute( UiConst.OnKeydown, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Results/ResultContentTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Results/ResultContentTagHelperTest.cs new file mode 100644 index 000000000..4a66d9c5a --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Results/ResultContentTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Results; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Results { + /// + /// 结果内容测试 + /// + public class ResultContentTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ResultContentTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ResultContentTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "
                                                                      a
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Results/ResultExtraTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Results/ResultExtraTagHelperTest.cs new file mode 100644 index 000000000..ab653e920 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Results/ResultExtraTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Results; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Results { + /// + /// 结果操作区测试 + /// + public class ResultExtraTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ResultExtraTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ResultExtraTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "
                                                                      a
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Results/ResultIconTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Results/ResultIconTagHelperTest.cs new file mode 100644 index 000000000..c8f118005 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Results/ResultIconTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Results; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Results { + /// + /// 结果图标测试 + /// + public class ResultIconTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ResultIconTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ResultIconTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "
                                                                      a
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Results/ResultSubtitleTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Results/ResultSubtitleTagHelperTest.cs new file mode 100644 index 000000000..915ff4086 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Results/ResultSubtitleTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Results; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Results { + /// + /// 结果副标题测试 + /// + public class ResultSubtitleTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ResultSubtitleTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ResultSubtitleTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "
                                                                      a
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Results/ResultTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Results/ResultTagHelperTest.cs new file mode 100644 index 000000000..54348938f --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Results/ResultTagHelperTest.cs @@ -0,0 +1,161 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Results; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Results { + /// + /// 结果测试 + /// + public class ResultTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ResultTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ResultTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试副标题 + /// + [Fact] + public void TestSubTitle() { + _wrapper.SetContextAttribute( UiConst.Subtitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试副标题 + /// + [Fact] + public void TestBindSubTitle() { + _wrapper.SetContextAttribute( AngularConst.BindSubtitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestStatus() { + _wrapper.SetContextAttribute( UiConst.Status, ResultStatus.StatusCode404 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestBindStatus() { + _wrapper.SetContextAttribute( AngularConst.BindStatus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标 + /// + [Fact] + public void TestIcon() { + _wrapper.SetContextAttribute( UiConst.Icon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标 + /// + [Fact] + public void TestBindIcon() { + _wrapper.SetContextAttribute( AngularConst.BindIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试操作区 + /// + [Fact] + public void TestExtra() { + _wrapper.SetContextAttribute( UiConst.Extra, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Results/ResultTitleTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Results/ResultTitleTagHelperTest.cs new file mode 100644 index 000000000..df34f5e0f --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Results/ResultTitleTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Results; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Results { + /// + /// 结果标题测试 + /// + public class ResultTitleTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ResultTitleTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ResultTitleTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "
                                                                      a
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Samples/Customer.cs b/test/Util.Ui.NgZorro.Tests/Samples/Customer.cs new file mode 100644 index 000000000..5c32e93a5 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Samples/Customer.cs @@ -0,0 +1,107 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Domain.Biz.Enums; + +namespace Util.Ui.NgZorro.Tests.Samples { + /// + /// 客户 + /// + public class Customer { + /// + /// 编码 + /// + [Display( Name = "code" )] + [MaxLength( 100 )] + [MinLength( 10, ErrorMessage = "编码最小为10位" )] + [Required(ErrorMessage = "编码不能是空值" )] + public string Code { get; set; } + /// + /// 姓名 + /// + [Description( "姓名" )] + [MaxLength( 200 )] + public string Name { get; set; } + /// + /// 昵称 + /// + [Description( "a.nickname" )] + [MaxLength( 50 )] + public string Nickname { get; set; } + /// + /// 密码 + /// + [Description( "密码" )] + [DataType(DataType.Password)] + public string Password { get; set; } + /// + /// 性别 + /// + [Description( "性别" )] + public Gender? Gender { get; set; } + /// + /// 出生日期 + /// + [Description( "出生日期" )] + [Required] + public DateTime? Birthday { get; set; } + /// + /// 民族 + /// + [Description( "民族" )] + public Nation? Nation { get; set; } + /// + /// 手机号 + /// + [Description( "手机号" )] + [Phone( ErrorMessage = "手机号错误" )] + public int Phone { get; set; } + /// + /// 身份证 + /// + [Description( "身份证" )] + [IdCard( ErrorMessage = "身份证错误" )] + public int IdCard { get; set; } + /// + /// 年龄 + /// + [Description( "年龄" )] + [Required] + [Range( 5.5,8.8 )] + public double Age { get; set; } + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + [EmailAddress(ErrorMessage = "email错误")] + public string Email { get; set; } + /// + /// 启用 + /// + [Description( "启用" )] + public bool Enabled { get; set; } + /// + /// 布尔必填项 + /// + [Description( "必填项" )] + [Required(ErrorMessage = "必须填写")] + public bool IsRequired { get; set; } + /// + /// 正则表达式 + /// + [Description( "正则表达式" )] + [RegularExpression( "a", ErrorMessage = "正则表达式错误" )] + public string Regex { get; set; } + /// + /// 起始出生日期 + /// + [Description( "出生日期" )] + [Required] + public DateTime? BeginBirthday { get; set; } + /// + /// 结束出生日期 + /// + [Description( "结束出生日期" )] + public DateTime? EndBirthday { get; set; } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Samples/TreeDto.cs b/test/Util.Ui.NgZorro.Tests/Samples/TreeDto.cs new file mode 100644 index 000000000..0782240f0 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Samples/TreeDto.cs @@ -0,0 +1,14 @@ +using Util.Applications.Trees; + +namespace Util.Ui.NgZorro.Tests.Samples { + /// + /// 树形参数 + /// + public class TreeDto : TreeDtoBase { + public string Text { get; set; } + + public override string GetText() { + return Text; + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Segments/SegmentedTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/Segments/SegmentedTagHelperTest.Expression.cs new file mode 100644 index 000000000..fc1def5e5 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Segments/SegmentedTagHelperTest.Expression.cs @@ -0,0 +1,31 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Segments { + /// + /// 分段控制器测试 - 表达式解析测试 + /// + public partial class SegmentedTagHelperTest { + /// + /// 测试解析表达式属性for - 布尔 + /// + [Fact] + public void TestFor_Bool() { + _wrapper.SetItem( new FormShareConfig { FormCreated = true } ); + _wrapper.SetExpression( t => t.Enabled ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "启用" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Segments/SegmentedTagHelperTest.Extend.cs b/test/Util.Ui.NgZorro.Tests/Segments/SegmentedTagHelperTest.Extend.cs new file mode 100644 index 000000000..ac03aa78e --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Segments/SegmentedTagHelperTest.Extend.cs @@ -0,0 +1,195 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Segments { + /// + /// 分段控制器测试 - 指令扩展 + /// + public partial class SegmentedTagHelperTest { + + #region EnableExtend + + /// + /// 测试启用扩展指令 + /// + [Fact] + public void TestEnableExtend_1() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用扩展指令 - 不启用 + /// + [Fact] + public void TestEnableExtend_2() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, false ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region AutoLoad + + /// + /// 测试初始化时是否自动加载数据 + /// + [Fact] + public void TestAutoLoad() { + _wrapper.SetContextAttribute( UiConst.AutoLoad, "false" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Value + + /// + /// 测试值绑定 + /// + [Fact] + public void TestValue() { + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region QueryParam + + /// + /// 测试查询参数 + /// + [Fact] + public void TestQueryParam() { + _wrapper.SetContextAttribute( UiConst.QueryParam, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Url + + /// + /// 测试Api地址 + /// + [Fact] + public void TestUrl_1() { + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Api地址 + /// + [Fact] + public void TestBindUrl() { + _wrapper.SetContextAttribute( AngularConst.BindUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Data + + /// + /// 测试数据源 + /// + [Fact] + public void TestData() { + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnIndexChange + + /// + /// 测试索引变更事件 + /// + [Fact] + public void TestOnIndexChange_1() { + _wrapper.SetContextAttribute( UiConst.OnIndexChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试索引变更事件 - 设置数据源 + /// + [Fact] + public void TestOnIndexChange_2() { + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + _wrapper.SetContextAttribute( UiConst.OnIndexChange, "b" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnValueChange + + /// + /// 测试值变更事件 + /// + [Fact] + public void TestOnValueChange() { + _wrapper.SetContextAttribute( UiConst.OnValueChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnLoad + + /// + /// 测试数据加载完成事件 + /// + [Fact] + public void TestOnLoad() { + _wrapper.SetContextAttribute( UiConst.OnLoad, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Segments/SegmentedTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Segments/SegmentedTagHelperTest.cs new file mode 100644 index 000000000..d6d6d3ec8 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Segments/SegmentedTagHelperTest.cs @@ -0,0 +1,162 @@ +using System; +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Segments; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Segments { + /// + /// 分段控制器测试 + /// + public partial class SegmentedTagHelperTest : IDisposable { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public SegmentedTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new SegmentedTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 测试清理 + /// + public void Dispose() { + NgZorroOptionsService.ClearOptions(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestNgModel() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试大小 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, InputSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试大小 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选项列表 + /// + [Fact] + public void TestOptions() { + _wrapper.SetContextAttribute( UiConst.Options, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试将宽度调整为父元素宽度 + /// + [Fact] + public void TestBlock() { + _wrapper.SetContextAttribute( UiConst.Block, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义渲染选项 + /// + [Fact] + public void TestLabelTemplate() { + _wrapper.SetContextAttribute( UiConst.LabelTemplate, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型变更事件 + /// + [Fact] + public void TestOnModelChange() { + _wrapper.SetContextAttribute( UiConst.OnModelChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Selects/OptionGroupTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Selects/OptionGroupTagHelperTest.cs new file mode 100644 index 000000000..dbfb93e40 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Selects/OptionGroupTagHelperTest.cs @@ -0,0 +1,83 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Selects; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Selects { + /// + /// 选项组测试 + /// + public class OptionGroupTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public OptionGroupTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new OptionGroupTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试组名 + /// + [Fact] + public void TestLabel() { + _wrapper.SetContextAttribute( UiConst.Label, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试组名 + /// + [Fact] + public void TestBindLabel() { + _wrapper.SetContextAttribute( AngularConst.BindLabel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Selects/OptionTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Selects/OptionTagHelperTest.cs new file mode 100644 index 000000000..ff5e7c0dc --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Selects/OptionTagHelperTest.cs @@ -0,0 +1,138 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Selects; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Selects { + /// + /// 选项测试 + /// + public class OptionTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public OptionTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new OptionTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签 + /// + [Fact] + public void TestLabel() { + _wrapper.SetContextAttribute( UiConst.Label, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签 + /// + [Fact] + public void TestBindLabel() { + _wrapper.SetContextAttribute( AngularConst.BindLabel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestValue() { + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestBindValue() { + _wrapper.SetContextAttribute( AngularConst.BindValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试隐藏 + /// + [Fact] + public void TestHide() { + _wrapper.SetContextAttribute( UiConst.Hide, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否自定义内容 + /// + [Fact] + public void TestCustomContent() { + _wrapper.SetContextAttribute( UiConst.CustomContent, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.Expression.cs new file mode 100644 index 000000000..b4c5879df --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.Expression.cs @@ -0,0 +1,132 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Forms.Configs; +using Util.Ui.NgZorro.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Selects { + /// + /// 选择器测试 - 表达式解析测试 + /// + public partial class SelectTagHelperTest { + /// + /// 测试解析表达式属性for - 布尔 + /// + [Fact] + public void TestFor_Bool() { + _wrapper.SetItem( new FormShareConfig { FormCreated = true } ); + _wrapper.SetExpression( t => t.Enabled ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "启用" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试解析表达式属性for - 布尔 - 多语言 + /// + [Fact] + public void TestFor_Bool_I18n() { + _wrapper.SetItem( new FormShareConfig { FormCreated = true } ); + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetExpression( t => t.Enabled ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{'启用'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试解析表达式属性for - 覆盖name + /// + [Fact] + public void TestFor_Name() { + _wrapper.SetItem( new FormShareConfig { FormCreated = true } ); + _wrapper.SetExpression( t => t.Enabled ); + _wrapper.SetContextAttribute( UiConst.Name,"abc" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "启用" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试解析表达式属性for - 设置[name] + /// + [Fact] + public void TestFor_BindName() { + _wrapper.SetItem( new FormShareConfig { FormCreated = true } ); + _wrapper.SetExpression( t => t.Enabled ); + _wrapper.SetContextAttribute( AngularConst.BindName, "abc" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "启用" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.Extend.cs b/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.Extend.cs new file mode 100644 index 000000000..0b8ecbdec --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.Extend.cs @@ -0,0 +1,413 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Forms; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Selects { + /// + /// 选择器测试 - 指令扩展 + /// + public partial class SelectTagHelperTest { + + #region 辅助操作 + + /// + /// 添加选项 + /// + private void AppendOptions( StringBuilder result ) { + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + } + + #endregion + + #region EnableExtend + + /// + /// 测试启用扩展指令 + /// + [Fact] + public void TestEnableExtend_1() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + var result = new StringBuilder(); + result.Append( "" ); + AppendOptions( result ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用扩展指令 - 不启用 + /// + [Fact] + public void TestEnableExtend_2() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, false ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region AutoLoad + + /// + /// 测试初始化时是否自动加载数据 + /// + [Fact] + public void TestAutoLoad() { + _wrapper.SetContextAttribute( UiConst.AutoLoad, false ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region QueryParam + + /// + /// 测试查询参数 + /// + [Fact] + public void TestQueryParam() { + _wrapper.SetContextAttribute( UiConst.QueryParam, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Sort + + /// + /// 测试排序条件 + /// + [Fact] + public void TestSort() { + _wrapper.SetContextAttribute( UiConst.Sort, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试排序条件 + /// + [Fact] + public void TestBindSort() { + _wrapper.SetContextAttribute( AngularConst.BindSort, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Url + + /// + /// 测试Api地址 + /// + [Fact] + public void TestUrl_1() { + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + AppendOptions( result ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Api地址 + /// + [Fact] + public void TestBindUrl() { + _wrapper.SetContextAttribute( AngularConst.BindUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + AppendOptions( result ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Data + + /// + /// 测试数据源 + /// + [Fact] + public void TestData() { + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + AppendOptions( result ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数据源 - 多语言 + /// + [Fact] + public void TestData_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region DefaultOptionText + + /// + /// 测试默认项文本 + /// + [Fact] + public void TestDefaultOptionText_1() { + _wrapper.SetContextAttribute( UiConst.DefaultOptionText, " " ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试默认项文本 - 启用默认项文本 + /// + [Fact] + public void TestDefaultOptionText_2() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableDefaultOptionText = true } ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + AppendOptions( result ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试默认项文本 - 多选模式不启用默认项文本 + /// + [Fact] + public void TestDefaultOptionText_3() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableDefaultOptionText = true } ); + _wrapper.SetContextAttribute( UiConst.Mode, SelectMode.Multiple ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试默认项文本 - 标签模式不启用默认项文本 + /// + [Fact] + public void TestDefaultOptionText_4() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableDefaultOptionText = true } ); + _wrapper.SetContextAttribute( UiConst.Mode, SelectMode.Tags ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试默认项文本 - 启用默认项文本,覆盖 + /// + [Fact] + public void TestDefaultOptionText_5() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableDefaultOptionText = true } ); + _wrapper.SetContextAttribute( UiConst.DefaultOptionText, " " ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + AppendOptions( result ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region ShowDefaultOption + + /// + /// 测试是否显示默认项 - 显示 + /// + [Fact] + public void TestShowDefaultOption_1() { + _wrapper.SetContextAttribute( UiConst.ShowDefaultOption, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + AppendOptions( result ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示默认项 - 不显示,覆盖全局配置 + /// + [Fact] + public void TestShowDefaultOption_2() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableDefaultOptionText = true } ); + _wrapper.SetContextAttribute( UiConst.ShowDefaultOption, false ); + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + AppendOptions( result ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region SearchKeyword + + /// + /// 测试搜索关键字 + /// + [Fact] + public void TestSearchKeyword() { + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + _wrapper.SetContextAttribute( UiConst.SearchKeyword, true ); + var result = new StringBuilder(); + result.Append( "" ); + AppendOptions( result ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region SearchDelay + + /// + /// 测试搜索延迟 + /// + [Fact] + public void TestSearchDelay() { + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + _wrapper.SetContextAttribute( UiConst.SearchKeyword, true ); + _wrapper.SetContextAttribute( UiConst.SearchDelay, 100 ); + var result = new StringBuilder(); + result.Append( "" ); + AppendOptions( result ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region ScrollLoad + + /// + /// 测试滚动加载 + /// + [Fact] + public void TestScrollLoad() { + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + _wrapper.SetContextAttribute( UiConst.ScrollLoad, true ); + var result = new StringBuilder(); + result.Append( "" ); + AppendOptions( result ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnLoad + + /// + /// 测试数据加载完成事件 + /// + [Fact] + public void TestOnLoad() { + _wrapper.SetContextAttribute( UiConst.OnLoad, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region AutoNzFor + + /// + /// 测试自动创建控件Id,并设置标签for + /// + [Fact] + public void TestAutoNzFor() { + var form = new FormTagHelper().ToWrapper(); + + var formItem = new FormItemTagHelper().ToWrapper(); + form.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + _wrapper.SetContextAttribute( UiConst.AutoNzFor, true ); + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( _wrapper ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.TableEdit.cs b/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.TableEdit.cs new file mode 100644 index 000000000..bec54129b --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.TableEdit.cs @@ -0,0 +1,56 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Selects { + /// + /// 选择器测试 - 表格编辑 + /// + public partial class SelectTagHelperTest { + /// + /// 测试启用编辑模式 + /// + [Fact] + public void TestIsEdit_1() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.IsEdit, true ); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + table.AppendContent( column ); + + //添加选择器组件 + column.AppendContent( _wrapper ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.Validation.cs b/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.Validation.cs new file mode 100644 index 000000000..e38f95d81 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.Validation.cs @@ -0,0 +1,71 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Selects { + /// + /// 选择器测试 - 验证测试 + /// + public partial class SelectTagHelperTest { + /// + /// 测试必填项验证 + /// + [Fact] + public void TestRequired() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试必填项消息 + /// + [Fact] + public void TestRequiredMessage() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( UiConst.RequiredMessage, "a" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试必填项消息 + /// + [Fact] + public void TestBindRequiredMessage() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( AngularConst.BindRequiredMessage, "a" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.cs new file mode 100644 index 000000000..5fa799e69 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Selects/SelectTagHelperTest.cs @@ -0,0 +1,643 @@ +using System; +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Selects; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Selects { + /// + /// 选择器测试 + /// + public partial class SelectTagHelperTest : IDisposable{ + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public SelectTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new SelectTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 测试清理 + /// + public void Dispose() { + NgZorroOptionsService.ClearOptions(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加标识 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加名称 + /// + [Fact] + public void TestName() { + _wrapper.SetContextAttribute( UiConst.Name, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加名称 + /// + [Fact] + public void TestBindName() { + _wrapper.SetContextAttribute( AngularConst.BindName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加名称 - 同时添加name和[name] + /// + [Fact] + public void TestBindName_2() { + _wrapper.SetContextAttribute( UiConst.Name, "a" ); + _wrapper.SetContextAttribute( AngularConst.BindName, "b" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestNgModel() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试比较算法函数 + /// + [Fact] + public void TestCompareWith() { + _wrapper.SetContextAttribute( UiConst.CompareWith, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试清除搜索值 + /// + [Fact] + public void TestAutoClearSearchValue() { + _wrapper.SetContextAttribute( UiConst.AutoClearSearchValue, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试允许清除 + /// + [Fact] + public void TestAllowClear() { + _wrapper.SetContextAttribute( UiConst.AllowClear, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试移除边框 + /// + [Fact] + public void TestBorderless() { + _wrapper.SetContextAttribute( UiConst.Borderless, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否打开下拉菜单 + /// + [Fact] + public void TestOpen() { + _wrapper.SetContextAttribute( UiConst.Open, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否打开下拉菜单 + /// + [Fact] + public void TestBindonOpen() { + _wrapper.SetContextAttribute( AngularConst.BindonOpen, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动聚焦 + /// + [Fact] + public void TestAutoFocus() { + _wrapper.SetContextAttribute( UiConst.AutoFocus, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单样式类名 + /// + [Fact] + public void TestDropdownClassName() { + _wrapper.SetContextAttribute( UiConst.DropdownClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单样式类名 + /// + [Fact] + public void TestBindDropdownClassName() { + _wrapper.SetContextAttribute( AngularConst.BindDropdownClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单样式 + /// + [Fact] + public void TestDropdownStyle() { + _wrapper.SetContextAttribute( UiConst.DropdownStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单和选择器同宽 + /// + [Fact] + public void TestDropdownMatchSelectWidth() { + _wrapper.SetContextAttribute( UiConst.DropdownMatchSelectWidth, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义模板 + /// + [Fact] + public void TestCustomTemplate() { + _wrapper.SetContextAttribute( UiConst.CustomTemplate, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试服务端搜索 + /// + [Fact] + public void TestServerSearch() { + _wrapper.SetContextAttribute( UiConst.ServerSearch, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试过滤项函数 + /// + [Fact] + public void TestFilterOption() { + _wrapper.SetContextAttribute( UiConst.FilterOption, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试最大多选数量 + /// + [Fact] + public void TestMaxMultipleCount() { + _wrapper.SetContextAttribute( UiConst.MaxMultipleCount, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模式 + /// + [Fact] + public void TestMode() { + _wrapper.SetContextAttribute( UiConst.Mode, SelectMode.Multiple ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模式 + /// + [Fact] + public void TestBindMode() { + _wrapper.SetContextAttribute( AngularConst.BindMode, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试空列表默认内容 + /// + [Fact] + public void TestNotFoundContent() { + _wrapper.SetContextAttribute( UiConst.NotFoundContent, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试空列表默认内容 + /// + [Fact] + public void TestBindNotFoundContent() { + _wrapper.SetContextAttribute( AngularConst.BindNotFoundContent, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试占位提示 + /// + [Fact] + public void TestPlaceholder() { + _wrapper.SetContextAttribute( UiConst.Placeholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试占位提示 + /// + [Fact] + public void TestBindPlaceholder() { + _wrapper.SetContextAttribute( AngularConst.BindPlaceholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示箭头 + /// + [Fact] + public void TestShowArrow() { + _wrapper.SetContextAttribute( UiConst.ShowArrow, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示搜索 + /// + [Fact] + public void TestShowSearch() { + _wrapper.SetContextAttribute( UiConst.ShowSearch, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选择框大小 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, InputSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选择框大小 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀图标 + /// + [Fact] + public void TestSuffixIcon() { + _wrapper.SetContextAttribute( UiConst.SuffixIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀图标 + /// + [Fact] + public void TestBindSuffixIcon() { + _wrapper.SetContextAttribute( AngularConst.BindSuffixIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试清除图标 + /// + [Fact] + public void TestRemoveIcon() { + _wrapper.SetContextAttribute( UiConst.RemoveIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试清空图标 + /// + [Fact] + public void TestClearIcon() { + _wrapper.SetContextAttribute( UiConst.ClearIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选中项图标 + /// + [Fact] + public void TestMenuItemSelectedIcon() { + _wrapper.SetContextAttribute( UiConst.MenuItemSelectedIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动分词分隔符 + /// + [Fact] + public void TestTokenSeparators() { + _wrapper.SetContextAttribute( UiConst.TokenSeparators, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载状态 + /// + [Fact] + public void TestLoading() { + _wrapper.SetContextAttribute( UiConst.Loading, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试最大标签数量 + /// + [Fact] + public void TestMaxTagCount() { + _wrapper.SetContextAttribute( UiConst.MaxTagCount, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试最大标签占位符 + /// + [Fact] + public void TestMaxTagPlaceholder() { + _wrapper.SetContextAttribute( UiConst.MaxTagPlaceholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选项列表 + /// + [Fact] + public void TestOptions() { + _wrapper.SetContextAttribute( UiConst.Options, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选项高度 + /// + [Fact] + public void TestOptionHeightPx() { + _wrapper.SetContextAttribute( UiConst.OptionHeightPx, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试最大显示选项数量 + /// + [Fact] + public void TestOptionOverflowSize() { + _wrapper.SetContextAttribute( UiConst.OptionOverflowSize, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置下拉扩展模板 + /// + [Fact] + public void TestDropdownRender() { + _wrapper.SetContextAttribute( UiConst.DropdownRender, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型变更事件 + /// + [Fact] + public void TestOnModelChange() { + _wrapper.SetContextAttribute( UiConst.OnModelChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单打开状态变更事件 + /// + [Fact] + public void TestOnOpenChange() { + _wrapper.SetContextAttribute( UiConst.OnOpenChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试滚动到底部事件 + /// + [Fact] + public void TestOnScrollToBottom() { + _wrapper.SetContextAttribute( UiConst.OnScrollToBottom, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试搜索事件 + /// + [Fact] + public void TestOnSearch() { + _wrapper.SetContextAttribute( UiConst.OnSearch, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试获得焦点事件 + /// + [Fact] + public void TestOnFocus() { + _wrapper.SetContextAttribute( UiConst.OnFocus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试失去焦点事件 + /// + [Fact] + public void TestOnBlur() { + _wrapper.SetContextAttribute( UiConst.OnBlur, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Skeletons/SkeletonElementTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Skeletons/SkeletonElementTagHelperTest.cs new file mode 100644 index 000000000..789bc57b9 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Skeletons/SkeletonElementTagHelperTest.cs @@ -0,0 +1,172 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Skeletons; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Skeletons { + /// + /// 骨架屏元素测试 + /// + public class SkeletonElementTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public SkeletonElementTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new SkeletonElementTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试类型 + /// + [Fact] + public void TestType() { + _wrapper.SetContextAttribute( UiConst.Type, SkeletonElementType.Button ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试类型 + /// + [Fact] + public void TestBindType() { + _wrapper.SetContextAttribute( AngularConst.BindType, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示动画效果 + /// + [Fact] + public void TestActive() { + _wrapper.SetContextAttribute( UiConst.Active, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试大小 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, SkeletonElementSize.Small ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试大小 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试形状 + /// + [Fact] + public void TestShape() { + _wrapper.SetContextAttribute( UiConst.Shape, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试按钮形状 + /// + [Fact] + public void TestButtonShape() { + _wrapper.SetContextAttribute( UiConst.ButtonShape, SkeletonButtonShape.Round ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试头像形状 + /// + [Fact] + public void TestAvatarShape() { + _wrapper.SetContextAttribute( UiConst.AvatarShape, SkeletonAvatarShape.Circle ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试形状 + /// + [Fact] + public void TestBindShape() { + _wrapper.SetContextAttribute( AngularConst.BindShape, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Skeletons/SkeletonTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Skeletons/SkeletonTagHelperTest.cs new file mode 100644 index 000000000..00e874223 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Skeletons/SkeletonTagHelperTest.cs @@ -0,0 +1,126 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Skeletons; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Skeletons { + /// + /// 骨架屏测试 + /// + public class SkeletonTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public SkeletonTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new SkeletonTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示动画效果 + /// + [Fact] + public void TestActive() { + _wrapper.SetContextAttribute( UiConst.Active, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示头像占位图 + /// + [Fact] + public void TestAvatar() { + _wrapper.SetContextAttribute( UiConst.Avatar, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否加载状态 + /// + [Fact] + public void TestLoading() { + _wrapper.SetContextAttribute( UiConst.Loading, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示段落占位图 + /// + [Fact] + public void TestParagraph() { + _wrapper.SetContextAttribute( UiConst.Paragraph, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示标题占位图 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试圆形 + /// + [Fact] + public void TestRound() { + _wrapper.SetContextAttribute( UiConst.Round, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Sliders/SliderTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Sliders/SliderTagHelperTest.cs new file mode 100644 index 000000000..69d31026e --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Sliders/SliderTagHelperTest.cs @@ -0,0 +1,249 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Sliders; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Sliders { + /// + /// 滑动输入条测试 + /// + public class SliderTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public SliderTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new SliderTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否只能拖拽到刻度 + /// + [Fact] + public void TestDots() { + _wrapper.SetContextAttribute( UiConst.Dots, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否包含 + /// + [Fact] + public void TestIncluded() { + _wrapper.SetContextAttribute( UiConst.Included, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试刻度标记 + /// + [Fact] + public void TestMarks() { + _wrapper.SetContextAttribute( UiConst.Marks, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试最大值 + /// + [Fact] + public void TestMax() { + _wrapper.SetContextAttribute( UiConst.Max, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试最小值 + /// + [Fact] + public void TestMin() { + _wrapper.SetContextAttribute( UiConst.Min, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试双滑块模式 + /// + [Fact] + public void TestRange() { + _wrapper.SetContextAttribute( UiConst.Range, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试步长 + /// + [Fact] + public void TestStep() { + _wrapper.SetContextAttribute( UiConst.Step, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示格式化函数 + /// + [Fact] + public void TestTipFormatter() { + _wrapper.SetContextAttribute( UiConst.TipFormatter,"a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试垂直方向 + /// + [Fact] + public void TestVertical() { + _wrapper.SetContextAttribute( UiConst.Vertical, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试反向 + /// + [Fact] + public void TestReverse() { + _wrapper.SetContextAttribute( UiConst.Reverse, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示可见性 + /// + [Fact] + public void TestTooltipVisible() { + _wrapper.SetContextAttribute( UiConst.TooltipVisible, SliderTooltipVisible.Always ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示可见性 + /// + [Fact] + public void TestBindTooltipVisible() { + _wrapper.SetContextAttribute( AngularConst.BindTooltipVisible, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示位置 + /// + [Fact] + public void TestTooltipPlacement() { + _wrapper.SetContextAttribute( UiConst.TooltipPlacement, TooltipPlacement.BottomRight ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示位置 + /// + [Fact] + public void TestBindTooltipPlacement() { + _wrapper.SetContextAttribute( AngularConst.BindTooltipPlacement, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型变更事件 + /// + [Fact] + public void TestOnModelChange() { + _wrapper.SetContextAttribute( UiConst.OnModelChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试变更后事件 + /// + [Fact] + public void TestOnFocus() { + _wrapper.SetContextAttribute( UiConst.OnAfterChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Spaces/SpaceTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Spaces/SpaceTagHelperTest.cs new file mode 100644 index 000000000..f51e94c09 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Spaces/SpaceTagHelperTest.cs @@ -0,0 +1,152 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Spaces; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Spaces { + /// + /// 间距测试 + /// + public class SpaceTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public SpaceTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new SpaceTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距大小 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, SpaceSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距大小 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距方向 + /// + [Fact] + public void TestDirection() { + _wrapper.SetContextAttribute( UiConst.Direction, SpaceDirection.Vertical ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距方向 + /// + [Fact] + public void TestBindDirection() { + _wrapper.SetContextAttribute( AngularConst.BindDirection, "Ab" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试对齐方式 + /// + [Fact] + public void TestAlign() { + _wrapper.SetContextAttribute( UiConst.Align, SpaceAlign.End ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试对齐方式 + /// + [Fact] + public void TestBindAlign() { + _wrapper.SetContextAttribute( AngularConst.BindAlign, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置分隔符 + /// + [Fact] + public void TestSplit() { + _wrapper.SetContextAttribute( UiConst.Split, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动换行 + /// + [Fact] + public void TestWrap() { + _wrapper.SetContextAttribute( UiConst.Wrap, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Spins/SpinTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Spins/SpinTagHelperTest.cs new file mode 100644 index 000000000..7ac50f6a6 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Spins/SpinTagHelperTest.cs @@ -0,0 +1,150 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Spins; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Spins { + /// + /// 加载中测试 + /// + public class SpinTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public SpinTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new SpinTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试延迟显示时间 + /// + [Fact] + public void TestDelay() { + _wrapper.SetContextAttribute( UiConst.Delay, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载指示符 + /// + [Fact] + public void TestIndicator() { + _wrapper.SetContextAttribute( UiConst.Indicator, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试大小 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, SpinSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试大小 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否旋转 + /// + [Fact] + public void TestSpinning() { + _wrapper.SetContextAttribute( UiConst.Spinning, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否简单模式 + /// + [Fact] + public void TestSimple() { + _wrapper.SetContextAttribute( UiConst.Simple, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示文字 + /// + [Fact] + public void TestTip() { + _wrapper.SetContextAttribute( UiConst.Tip, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示文字 + /// + [Fact] + public void TestBindTip() { + _wrapper.SetContextAttribute( AngularConst.BindTip, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Startup.cs b/test/Util.Ui.NgZorro.Tests/Startup.cs new file mode 100644 index 000000000..3bb9ce9ac --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Startup.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit.DependencyInjection.Logging; + +namespace Util.Ui.NgZorro.Tests { + /// + /// + /// + public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + hostBuilder.ConfigureDefaults( null ).AddUtil(); + Util.Helpers.Environment.IsTest = true; + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Statistics/CountdownTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Statistics/CountdownTagHelperTest.cs new file mode 100644 index 000000000..e0bf5d7b6 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Statistics/CountdownTagHelperTest.cs @@ -0,0 +1,206 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Statistics; +using Util.Ui.NgZorro.Configs; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Statistics { + /// + /// 倒计时测试 + /// + public class CountdownTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public CountdownTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new CountdownTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 - 多语言 + /// + [Fact] + public void TestTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestValue() { + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试值 + /// + [Fact] + public void TestBindValue() { + _wrapper.SetContextAttribute( AngularConst.BindValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试格式化 + /// + [Fact] + public void TestFormat() { + _wrapper.SetContextAttribute( UiConst.Format, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试格式化 + /// + [Fact] + public void TestBindFormat() { + _wrapper.SetContextAttribute( AngularConst.BindFormat, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前缀 + /// + [Fact] + public void TestPrefix() { + _wrapper.SetContextAttribute( UiConst.Prefix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前缀 + /// + [Fact] + public void TestBindPrefix() { + _wrapper.SetContextAttribute( AngularConst.BindPrefix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀 + /// + [Fact] + public void TestSuffix() { + _wrapper.SetContextAttribute( UiConst.Suffix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀 + /// + [Fact] + public void TestBindSuffix() { + _wrapper.SetContextAttribute( AngularConst.BindSuffix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模板 + /// + [Fact] + public void TestValueTemplate() { + _wrapper.SetContextAttribute( UiConst.ValueTemplate, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试倒计时完成事件 + /// + [Fact] + public void TestOnCountdownFinish() { + _wrapper.SetContextAttribute( UiConst.OnCountdownFinish, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Statistics/StatisticTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Statistics/StatisticTagHelperTest.cs new file mode 100644 index 000000000..2e7092c53 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Statistics/StatisticTagHelperTest.cs @@ -0,0 +1,185 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Statistics; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Statistics { + /// + /// 统计测试 + /// + public class StatisticTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public StatisticTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new StatisticTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 - 多语言 + /// + [Fact] + public void TestTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数值 + /// + [Fact] + public void TestValue() { + _wrapper.SetContextAttribute( UiConst.Value, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数值 + /// + [Fact] + public void TestBindValue() { + _wrapper.SetContextAttribute( AngularConst.BindValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前缀 + /// + [Fact] + public void TestPrefix() { + _wrapper.SetContextAttribute( UiConst.Prefix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试前缀 + /// + [Fact] + public void TestBindPrefix() { + _wrapper.SetContextAttribute( AngularConst.BindPrefix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀 + /// + [Fact] + public void TestSuffix() { + _wrapper.SetContextAttribute( UiConst.Suffix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀 + /// + [Fact] + public void TestBindSuffix() { + _wrapper.SetContextAttribute( AngularConst.BindSuffix, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试样式 + /// + [Fact] + public void TestValueStyle() { + _wrapper.SetContextAttribute( UiConst.ValueStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模板 + /// + [Fact] + public void TestValueTemplate() { + _wrapper.SetContextAttribute( UiConst.ValueTemplate, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Steps/StepTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Steps/StepTagHelperTest.cs new file mode 100644 index 000000000..adca2695f --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Steps/StepTagHelperTest.cs @@ -0,0 +1,219 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Steps; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Steps { + /// + /// 步骤测试 + /// + public class StepTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public StepTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new StepTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 - 多语言 + /// + [Fact] + public void TestTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试子标题 + /// + [Fact] + public void TestSubtitle() { + _wrapper.SetContextAttribute( UiConst.Subtitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试子标题 - 多语言 + /// + [Fact] + public void TestSubtitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Subtitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试子标题 + /// + [Fact] + public void TestBindSubtitle() { + _wrapper.SetContextAttribute( AngularConst.BindSubtitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试描述 + /// + [Fact] + public void TestDescription() { + _wrapper.SetContextAttribute( UiConst.Description, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试描述 + /// + [Fact] + public void TestBindDescription() { + _wrapper.SetContextAttribute( AngularConst.BindDescription, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标 + /// + [Fact] + public void TestIcon() { + _wrapper.SetContextAttribute( UiConst.Icon, AntDesignIcon.Alert ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标 + /// + [Fact] + public void TestBindIcon() { + _wrapper.SetContextAttribute( AngularConst.BindIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestStatus() { + _wrapper.SetContextAttribute( UiConst.Status, StepStatus.Finish ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestBindStatus() { + _wrapper.SetContextAttribute( AngularConst.BindStatus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试进度百分比 + /// + [Fact] + public void TestPercentage() { + _wrapper.SetContextAttribute( UiConst.Percentage, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Steps/StepsTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Steps/StepsTagHelperTest.cs new file mode 100644 index 000000000..40be0a172 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Steps/StepsTagHelperTest.cs @@ -0,0 +1,216 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Steps; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Steps { + /// + /// 步骤条测试 + /// + public class StepsTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public StepsTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new StepsTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试类型 + /// + [Fact] + public void TestType() { + _wrapper.SetContextAttribute( UiConst.Type, StepsType.Navigation ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试类型 + /// + [Fact] + public void TestBindType() { + _wrapper.SetContextAttribute( AngularConst.BindType, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试当前步骤 + /// + [Fact] + public void TestCurrent() { + _wrapper.SetContextAttribute( UiConst.Current, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, StepsSize.Small ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试起始序号 + /// + [Fact] + public void TestStartIndex() { + _wrapper.SetContextAttribute( UiConst.StartIndex, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试方向 + /// + [Fact] + public void TestDirection() { + _wrapper.SetContextAttribute( UiConst.Direction, StepsDirection.Vertical ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试方向 + /// + [Fact] + public void TestBindDirection() { + _wrapper.SetContextAttribute( AngularConst.BindDirection, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestStatus() { + _wrapper.SetContextAttribute( UiConst.Status, StepStatus.Finish ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestBindStatus() { + _wrapper.SetContextAttribute( AngularConst.BindStatus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试点状 + /// + [Fact] + public void TestProgressDot() { + _wrapper.SetContextAttribute( UiConst.ProgressDot, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签位置 + /// + [Fact] + public void TestLabelPlacement() { + _wrapper.SetContextAttribute( UiConst.LabelPlacement, StepsLabelPlacement.Vertical ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签位置 + /// + [Fact] + public void TestBindLabelPlacement() { + _wrapper.SetContextAttribute( AngularConst.BindLabelPlacement, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试点击步骤事件 + /// + [Fact] + public void TestOnIndexChange() { + _wrapper.SetContextAttribute( UiConst.OnIndexChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Switches/SwitchTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/Switches/SwitchTagHelperTest.Expression.cs new file mode 100644 index 000000000..5d8830529 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Switches/SwitchTagHelperTest.Expression.cs @@ -0,0 +1,25 @@ +using System.Text; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Switches { + /// + /// 开关测试 - 表达式解析测试 + /// + public partial class SwitchTagHelperTest { + /// + /// 测试解析表达式属性for + /// + [Fact] + public void TestFor_1() { + _wrapper.SetExpression( t => t.Code ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "code" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Switches/SwitchTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Switches/SwitchTagHelperTest.cs new file mode 100644 index 000000000..f9f8540fc --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Switches/SwitchTagHelperTest.cs @@ -0,0 +1,246 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Switches; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Switches { + /// + /// 开关测试 + /// + public partial class SwitchTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public SwitchTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new SwitchTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加标识 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置名称 + /// + [Fact] + public void TestName() { + _wrapper.SetContextAttribute( UiConst.Name, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置名称 + /// + [Fact] + public void TestBindName() { + _wrapper.SetContextAttribute( AngularConst.BindName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestNgModel() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选中显示标签 + /// + [Fact] + public void TestCheckedChildren() { + _wrapper.SetContextAttribute( UiConst.CheckedChildren, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选中显示标签 + /// + [Fact] + public void TestBindCheckedChildren() { + _wrapper.SetContextAttribute( AngularConst.BindCheckedChildren, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试未选中显示标签 + /// + [Fact] + public void TestUncheckedChildren() { + _wrapper.SetContextAttribute( UiConst.UncheckedChildren, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试未选中显示标签 + /// + [Fact] + public void TestBindUncheckedChildren() { + _wrapper.SetContextAttribute( AngularConst.BindUncheckedChildren, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, SwitchSize.Small ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试尺寸 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载状态 + /// + [Fact] + public void TestLoading() { + _wrapper.SetContextAttribute( UiConst.Loading, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试用户控制状态 + /// + [Fact] + public void TestControl() { + _wrapper.SetContextAttribute( UiConst.Control, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型变更事件 + /// + [Fact] + public void TestOnModelChange() { + _wrapper.SetContextAttribute( UiConst.OnModelChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击事件 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} diff --git a/test/Util.Ui.NgZorro.Tests/Tables/FilterTriggerTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Tables/FilterTriggerTagHelperTest.cs new file mode 100644 index 000000000..a5d557d5a --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/FilterTriggerTagHelperTest.cs @@ -0,0 +1,127 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表头单元格过滤器触发按钮测试 +/// +public class FilterTriggerTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public FilterTriggerTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new FilterTriggerTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单 + /// + [Fact] + public void TestExpand() { + _wrapper.SetContextAttribute( UiConst.DropdownMenu, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示下拉菜单 + /// + [Fact] + public void TestVisible() { + _wrapper.SetContextAttribute( UiConst.Visible, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示下拉菜单 + /// + [Fact] + public void TestBindonVisible() { + _wrapper.SetContextAttribute( AngularConst.BindonVisible, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否激活选中图标效果 + /// + [Fact] + public void TestActive() { + _wrapper.SetContextAttribute( UiConst.Active, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否附带背景 + /// + [Fact] + public void TestHasBackdrop() { + _wrapper.SetContextAttribute( UiConst.HasBackdrop, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单显示状态变化事件 + /// + [Fact] + public void TestOnVisibleChange() { + _wrapper.SetContextAttribute( UiConst.OnVisibleChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableBodyTagHelperTest.AutoCreate.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableBodyTagHelperTest.AutoCreate.cs new file mode 100644 index 000000000..c2dacc759 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableBodyTagHelperTest.AutoCreate.cs @@ -0,0 +1,92 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格主体测试 - 自动创建嵌套结构 +/// +public partial class TableBodyTagHelperTest { + /// + /// 测试自动创建嵌套结构 - 添加一个列,自动创建表格主体行 + /// + [Fact] + public void TestAutoCreate_1() { + //设置表格共享配置 + _wrapper.SetItem( new TableShareConfig() ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "a" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动创建嵌套结构 - 添加表格主体行和一个列,不自动创建表格主体行 + /// + [Fact] + public void TestAutoCreate_2() { + //设置表格共享配置 + _wrapper.SetItem( new TableShareConfig() ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + _wrapper.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "a" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动创建嵌套结构 - 添加一个列,取消自动创建 + /// + [Fact] + public void TestAutoCreate_3() { + //设置表格共享配置 + _wrapper.SetItem( new TableShareConfig() ); + + //取消自动创建 + _wrapper.SetContextAttribute( UiConst.EnableAutoCreate, "false" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "a" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableBodyTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableBodyTagHelperTest.cs new file mode 100644 index 000000000..491d1eaf7 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableBodyTagHelperTest.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格主体测试 +/// +public partial class TableBodyTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TableBodyTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TableBodyTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableColumnControlTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableColumnControlTagHelperTest.cs new file mode 100644 index 000000000..0270abfc4 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableColumnControlTagHelperTest.cs @@ -0,0 +1,155 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格编辑列控件区域测试 +/// +public class TableColumnControlTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TableColumnControlTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TableColumnControlTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void Test_1() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试在编辑模式中输出 + /// + [Fact] + public void Test_2() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + table.SetContextAttribute( UiConst.Id, "tb" ); + + //添加列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.IsEdit, true ); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "name" ); + table.AppendContent( column ); + + //添加控件区域 + _wrapper.AppendContent( "a" ); + column.AppendContent( _wrapper ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试在编辑模式中输出 - 设置控件区域标识 + /// + [Fact] + public void Test_3() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + table.SetContextAttribute( UiConst.Id, "tb" ); + + //添加列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.IsEdit, true ); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "name" ); + table.AppendContent( column ); + + //添加显示区域 + var display = new TableColumnDisplayTagHelper().ToWrapper(); + display.AppendContent( "a" ); + column.AppendContent( display ); + + //添加控件区域并设置标识 + _wrapper.SetContextAttribute( UiConst.Id, "id" ); + _wrapper.AppendContent( "b" ); + column.AppendContent( _wrapper ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableColumnDisplayTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableColumnDisplayTagHelperTest.cs new file mode 100644 index 000000000..556274b1b --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableColumnDisplayTagHelperTest.cs @@ -0,0 +1,111 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格编辑列显示区域测试 +/// +public class TableColumnDisplayTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TableColumnDisplayTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TableColumnDisplayTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void Test_1() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试在编辑模式中输出 + /// + [Fact] + public void Test_2() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + table.SetContextAttribute( UiConst.Id, "tb" ); + + //添加列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.IsEdit, true ); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "name" ); + table.AppendContent( column ); + + //添加显示区域 + _wrapper.AppendContent( "a" ); + column.AppendContent( _wrapper ); + + //添加控件区域 + var control = new TableColumnControlTagHelper().ToWrapper(); + control.AppendContent( "b" ); + column.AppendContent( control ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableColumnTagHelperTest.Edit.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableColumnTagHelperTest.Edit.cs new file mode 100644 index 000000000..ff81d07e5 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableColumnTagHelperTest.Edit.cs @@ -0,0 +1,291 @@ +using System; +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Inputs; +using Util.Ui.NgZorro.Components.Tables; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格单元格测试 - 编辑指令扩展 +/// +public partial class TableColumnTagHelperTest { + /// + /// 测试启用编辑模式 - 自动创建显示区域和控件区域 + /// + [Fact] + public void TestIsEdit_1() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置编辑模式 + _wrapper.SetContextAttribute( UiConst.IsEdit, true ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + _wrapper.SetContextAttribute( UiConst.Column, "b" ); + + //添加输入框组件 + var input = new InputTagHelper().ToWrapper(); + _wrapper.AppendContent( input ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试启用编辑模式 - 自动创建显示区域和控件区域 - 设置表格标识 + /// + [Fact] + public void TestIsEdit_2() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + table.SetContextAttribute( UiConst.Id, "tb" ); + + //添加列 + table.AppendContent( _wrapper ); + + //设置编辑模式 + _wrapper.SetContextAttribute( UiConst.IsEdit, true ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + _wrapper.SetContextAttribute( UiConst.Column, "b" ); + + //添加输入框组件 + var input = new InputTagHelper().ToWrapper(); + _wrapper.AppendContent( input ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试启用编辑模式 - 自动创建显示区域和控件区域 - 设置表格标识,行标识,控件标识 + /// + [Fact] + public void TestIsEdit_3() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + table.SetContextAttribute( UiConst.Id, "tb" ); + + //添加行 + var row = new TableRowTagHelper().ToWrapper(); + row.SetContextAttribute( UiConst.Id, "rowId" ); + table.AppendContent( row ); + + //添加列 + row.AppendContent( _wrapper ); + + //设置编辑模式 + _wrapper.SetContextAttribute( UiConst.IsEdit, true ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + _wrapper.SetContextAttribute( UiConst.Column, "b" ); + + //添加输入框组件 + var input = new InputTagHelper().ToWrapper(); + input.SetContextAttribute( UiConst.Id, "input" ); + _wrapper.AppendContent( input ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + var tableResult = table.GetResult(); + _output.WriteLine( tableResult ); + + //验证 + Assert.Equal( result.ToString(), tableResult ); + } + + /// + /// 测试启用编辑模式 - 未设置content,抛出异常 + /// + [Fact] + public void TestIsEdit_4() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置编辑模式 + _wrapper.SetContextAttribute( UiConst.IsEdit, true ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + _wrapper.SetContextAttribute( UiConst.Column, "b" ); + + //验证 + Assert.Throws( () => table.GetResult() ); + } + + /// + /// 测试启用编辑模式 - 手工创建显示区域和控件区域 + /// + [Fact] + public void TestIsEdit_5() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + _wrapper.SetContextAttribute( UiConst.IsEdit, true ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + _wrapper.SetContextAttribute( UiConst.Column, "b" ); + table.AppendContent( _wrapper ); + + //添加显示区域 + var display = new TableColumnDisplayTagHelper().ToWrapper(); + display.AppendContent( "c" ); + _wrapper.AppendContent( display ); + + //添加控件区域 + var control = new TableColumnControlTagHelper().ToWrapper(); + _wrapper.AppendContent( control ); + + //添加输入框组件 + var input = new InputTagHelper().ToWrapper(); + control.AppendContent( input ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "c" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试启用编辑模式 - 手工创建显示区域和控件区域 - 设置控件区域标识 + /// + [Fact] + public void TestIsEdit_6() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + _wrapper.SetContextAttribute( UiConst.IsEdit, true ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + _wrapper.SetContextAttribute( UiConst.Column, "b" ); + table.AppendContent( _wrapper ); + + //添加显示区域 + var display = new TableColumnDisplayTagHelper().ToWrapper(); + display.AppendContent( "c" ); + _wrapper.AppendContent( display ); + + //添加控件区域 + var control = new TableColumnControlTagHelper().ToWrapper(); + control.SetContextAttribute( UiConst.Id, "control_1" ); + _wrapper.AppendContent( control ); + + //添加输入框组件 + var input = new InputTagHelper().ToWrapper(); + control.AppendContent( input ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "c" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableColumnTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableColumnTagHelperTest.Expression.cs new file mode 100644 index 000000000..900e36dde --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableColumnTagHelperTest.Expression.cs @@ -0,0 +1,361 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.NgZorro.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格单元格测试 - 表达式解析 +/// +public partial class TableColumnTagHelperTest { + /// + /// 测试属性表达式 + /// + [Fact] + public void TestFor() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置表达式 + _wrapper.SetExpression( UiConst.For, t => t.Code ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "code" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.code}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试属性表达式 - 布尔类型 + /// + [Fact] + public void TestFor_2() { + //配置获取表格布尔列内容操作 + var options = new NgZorroOptions(); + NgZorroOptionsService.SetOptions( options ); + options.GetTableColumnBoolContentAction = column => { + var result = new StringBuilder(); + result.Append( "{{" ); + if ( options.EnableI18n ) + result.Append( $"(row.{column}?'{I18nKeys.Yes}':'{I18nKeys.No}')|i18n" ); + else + result.Append( $"row.{column}?'是':'否'" ); + result.Append( "}}" ); + return result.ToString(); + }; + + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置表达式 + _wrapper.SetExpression( UiConst.For, t => t.Enabled ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "启用" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.enabled?'是':'否'}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试属性表达式 - 布尔类型 - 多语言 + /// + [Fact] + public void TestFor_3() { + //配置获取表格布尔列内容操作 + var options = new NgZorroOptions { EnableI18n = true }; + NgZorroOptionsService.SetOptions( options ); + options.GetTableColumnBoolContentAction = column => { + var result = new StringBuilder(); + result.Append( "{{" ); + if ( options.EnableI18n ) + result.Append( $"(row.{column}?'{I18nKeys.Yes}':'{I18nKeys.No}')|i18n" ); + else + result.Append( $"row.{column}?'是':'否'" ); + result.Append( "}}" ); + return result.ToString(); + }; + + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置表达式 + _wrapper.SetExpression( UiConst.For, t => t.Enabled ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'启用'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{(row.enabled?'util.yes':'util.no')|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试属性表达式 - 布尔类型 + /// + [Fact] + public void TestFor_4() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置表达式 + _wrapper.SetExpression( UiConst.For, t => t.Enabled ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "启用" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.AppendLine( "" ); + result.AppendLine( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试属性表达式 - 枚举类型 + /// + [Fact] + public void TestFor_5() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置表达式 + _wrapper.SetExpression( UiConst.For, t => t.Gender ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "性别" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "util.female" ); + result.Append( "util.male" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试属性表达式 - 枚举类型 - 多语言 + /// + [Fact] + public void TestFor_6() { + //启用多语言 + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置表达式 + _wrapper.SetExpression( UiConst.For, t => t.Gender ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'性别'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.female'|i18n}}" ); + result.Append( "{{'util.male'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试属性表达式 - 覆盖标题 + /// + [Fact] + public void TestFor_7() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置表达式 + _wrapper.SetExpression( UiConst.For, t => t.Code ); + + //设置标题 + _wrapper.SetContextAttribute( UiConst.Title, "c" ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "c" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.code}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试属性表达式 - 启用排序 + /// + [Fact] + public void TestFor_8() { + //启用设置 + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableTableSort = true } ); + + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置表达式 + _wrapper.SetExpression( UiConst.For, t => t.Code ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "code" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.code}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试属性表达式 - 启用排序 - 手工覆盖 + /// + [Fact] + public void TestFor_9() { + //启用设置 + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableTableSort = true } ); + + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置表达式 + _wrapper.SetExpression( UiConst.For, t => t.Code ); + + //覆盖排序 + _wrapper.SetContextAttribute( UiConst.Sort, false ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "code" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.code}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableColumnTagHelperTest.Extend.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableColumnTagHelperTest.Extend.cs new file mode 100644 index 000000000..b0e174299 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableColumnTagHelperTest.Extend.cs @@ -0,0 +1,667 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格单元格测试 - 指令扩展 +/// +public partial class TableColumnTagHelperTest { + + #region 辅助操作 + + /// + /// 添加启用扩展的表格起始标签 + /// + /// 结果 + private void AppendExtendTable( StringBuilder result ) { + result.Append( "" ); + } + + /// + /// 添加表格总行数模板标签 + /// + /// 结果 + private void AppendTotalTemplate( StringBuilder result ) { + result.Append( "" ); + result.Append( "{{ 'util.tableTotalTemplate'|i18n:{start:range[0],end:range[1],total:total} }}" ); + result.Append( "" ); + } + + #endregion + + #region Column + + /// + /// 测试列名 + /// + [Fact] + public void TestColumn() { + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + var result = new StringBuilder(); + result.Append( "{{row.a}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列名 + /// + [Fact] + public void TestColumn_2() { + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + _wrapper.AppendContent( "b" ); + var result = new StringBuilder(); + result.Append( "b" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Title + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置标题和内容 + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + _wrapper.AppendContent( "b" ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试标题 - 多语言 + /// + [Fact] + public void TestTitle_I18n() { + //启用多语言 + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置标题和内容 + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + _wrapper.AppendContent( "b" ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'a'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试标题 - 固定到左侧 + /// + [Fact] + public void TestTitle_Left() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置标题和内容 + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + _wrapper.AppendContent( "b" ); + + //固定到左侧 + _wrapper.SetContextAttribute( UiConst.Left, "10px" ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + #endregion + + #region TitleOperation + + /// + /// 测试Operation标题 + /// + [Fact] + public void TestTitleOperation() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置标题和内容 + _wrapper.SetContextAttribute( UiConst.TitleOperation, true ); + _wrapper.AppendContent( "b" ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "Operation" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试Operation标题 - 多语言 + /// + [Fact] + public void TestTitleOperation_I18n() { + //启用多语言 + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置标题和内容 + _wrapper.SetContextAttribute( UiConst.TitleOperation, true ); + _wrapper.AppendContent( "b" ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.operation'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试Operation标题 - 固定到右侧 + /// + [Fact] + public void TestTitleOperation_Right() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置标题和内容 + _wrapper.SetContextAttribute( UiConst.TitleOperation, true ); + _wrapper.AppendContent( "b" ); + + //固定到右侧 + _wrapper.SetContextAttribute( UiConst.Right, "10px" ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "Operation" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + #endregion + + #region Checkbox + + /// + /// 测试设置复选框 + /// + [Fact] + public void TestCheckbox() { + _wrapper.SetItem( new TableShareConfig( "id" ) { IsShowCheckbox = true } ); + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.a}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Radio + + /// + /// 测试设置单选框 + /// + [Fact] + public void TestRadio_1() { + _wrapper.SetItem( new TableShareConfig( "id" ) { IsShowRadio = true } ); + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.a}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置单选框 - 如果已设置复选框,则单选框不生效 + /// + [Fact] + public void TestRadio_2() { + _wrapper.SetItem( new TableShareConfig( "id" ) { IsShowCheckbox = true, IsShowRadio = true } ); + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.a}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region LineNumber + + /// + /// 测试序号 + /// + [Fact] + public void TestLineNumber() { + _wrapper.SetItem( new TableShareConfig( "id" ) { IsShowLineNumber = true } ); + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + var result = new StringBuilder(); + result.Append( "{{row.lineNumber}}" ); + result.Append( "{{row.a}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试序号 - 带标题 + /// + [Fact] + public void TestLineNumber_Title() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + table.SetContextAttribute( UiConst.ShowLineNumber, true ); + + //添加列 + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + _wrapper.SetContextAttribute( UiConst.Title, "b" ); + table.AppendContent( _wrapper ); + + //结果 + var result = new StringBuilder(); + AppendExtendTable( result ); + result.Append( "" ); + result.Append( "" ); + result.Append( "序号" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.lineNumber}}" ); + result.Append( "{{row.a}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试序号 - 带标题,多语言支持 + /// + [Fact] + public void TestLineNumber_Title_I18n() { + //启用多语言 + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + + //创建表格 + var table = new TableTagHelper().ToWrapper(); + table.SetContextAttribute( UiConst.ShowLineNumber, true ); + + //添加列 + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + _wrapper.SetContextAttribute( UiConst.Title, "b" ); + table.AppendContent( _wrapper ); + + //结果 + var result = new StringBuilder(); + AppendExtendTable( result ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.lineNumber'|i18n}}" ); + result.Append( "{{'b'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.lineNumber}}" ); + result.Append( "{{row.a}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + #endregion + + #region Bool + + /// + /// 测试布尔类型列 + /// + [Fact] + public void TestType_Bool() { + _wrapper.SetContextAttribute( UiConst.Type, TableColumnType.Bool ); + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.AppendLine( "" ); + result.AppendLine( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Date + + /// + /// 测试日期类型列 + /// + [Fact] + public void TestType_Date() { + _wrapper.SetContextAttribute( UiConst.Type, TableColumnType.Date ); + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{row.a|date:'yyyy-MM-dd HH:mm'}}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试日期类型列 - 自定义日期格式 + /// + [Fact] + public void TestType_Date_2() { + _wrapper.SetContextAttribute( UiConst.Type, TableColumnType.Date ); + _wrapper.SetContextAttribute( UiConst.DateFormat, "yyyy" ); + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{row.a|date:'yyyy'}}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试日期类型列 - 仅显示日期 + /// + [Fact] + public void TestType_Date_3() { + _wrapper.SetContextAttribute( UiConst.Type, TableColumnType.Date ); + _wrapper.SetContextAttribute( UiConst.ShowDateOnly, true ); + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{row.a|date:'yyyy-MM-dd'}}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Width + + /// + /// 测试宽度 + /// + [Fact] + public void TestWidth() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置标题,内容,宽度 + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + _wrapper.SetContextAttribute( UiConst.Width, "100" ); + _wrapper.AppendContent( "b" ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + #endregion + + #region Sort + + /// + /// 测试排序 + /// + [Fact] + public void TestSort() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + table.SetContextAttribute( UiConst.EnableExtend, true ); + + //添加列 + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + _wrapper.SetContextAttribute( UiConst.Title, "b" ); + _wrapper.SetContextAttribute( UiConst.Sort, true ); + table.AppendContent( _wrapper ); + + //结果 + var result = new StringBuilder(); + AppendExtendTable( result ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.a}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + #endregion + + #region Acl + + /// + /// 测试访问控制列表 + /// + [Fact] + public void TestAcl() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置标题和内容 + _wrapper.SetContextAttribute( UiConst.Acl, "a" ); + _wrapper.SetContextAttribute( UiConst.AclElseTemplateId, "b" ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + _wrapper.AppendContent( "b" ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + #endregion + + #region Tooltip + + /// + /// 测试提示文字 + /// + [Fact] + public void TestTooltipTitle() { + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + _wrapper.AppendContent( "b" ); + _wrapper.SetContextAttribute( UiConst.TooltipTitle, "a" ); + var result = new StringBuilder(); + result.Append( "b" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Clipboard + + /// + /// 测试复制到剪贴板 + /// + [Fact] + public void TestClipboard() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + _wrapper.SetContextAttribute( UiConst.Clipboard, true ); + _wrapper.SetContextAttribute( UiConst.Column, "code" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "{{row.code}}" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableColumnTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableColumnTagHelperTest.cs new file mode 100644 index 000000000..1c833d3cc --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableColumnTagHelperTest.cs @@ -0,0 +1,342 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格单元格测试 +/// +public partial class TableColumnTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TableColumnTagHelperTest( ITestOutputHelper output ) { + Id.SetId( "id" ); + _output = output; + _wrapper = new TableColumnTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示复选框 + /// + [Fact] + public void TestShowCheckbox() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否禁用复选框 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复选框是否中间状态 + /// + [Fact] + public void TestIndeterminate() { + _wrapper.SetContextAttribute( UiConst.Indeterminate, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否选中复选框 + /// + [Fact] + public void TestChecked() { + _wrapper.SetContextAttribute( UiConst.Checked, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否选中复选框 + /// + [Fact] + public void TestBindonChecked() { + _wrapper.SetContextAttribute( AngularConst.BindonChecked, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示展开按钮 + /// + [Fact] + public void TestShowExpand() { + _wrapper.SetContextAttribute( UiConst.ShowExpand, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否已展开 + /// + [Fact] + public void TestExpand() { + _wrapper.SetContextAttribute( UiConst.Expand, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否展开 + /// + [Fact] + public void TestBindonExpand() { + _wrapper.SetContextAttribute( AngularConst.BindonExpand, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试左侧距离 + /// + [Fact] + public void TestLeft() { + _wrapper.SetContextAttribute( UiConst.Left, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试左侧距离 + /// + [Fact] + public void TestBindLeft() { + _wrapper.SetContextAttribute( AngularConst.BindLeft, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试右侧距离 + /// + [Fact] + public void TestRight() { + _wrapper.SetContextAttribute( UiConst.Right, "10px" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试右侧距离 + /// + [Fact] + public void TestBindRight() { + _wrapper.SetContextAttribute( AngularConst.BindRight, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试对齐方式 + /// + [Fact] + public void TestAlign() { + _wrapper.SetContextAttribute( UiConst.Align, TableHeadColumnAlign.Left ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试对齐方式 + /// + [Fact] + public void TestBindAlign() { + _wrapper.SetContextAttribute( AngularConst.BindAlign, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否折行显示 + /// + [Fact] + public void TestBreakWord() { + _wrapper.SetContextAttribute( UiConst.BreakWord, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动省略 + /// + [Fact] + public void TestEllipsis() { + _wrapper.SetContextAttribute( UiConst.Ellipsis, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动省略 - 自动设置表格布局 + /// + [Fact] + public void TestEllipsis_2() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //添加列 + table.AppendContent( _wrapper ); + + //设置标题,内容,省略号 + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + _wrapper.SetContextAttribute( UiConst.Ellipsis, "true" ); + _wrapper.AppendContent( "b" ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + + /// + /// 测试缩进宽度 + /// + [Fact] + public void TestIndentSize() { + _wrapper.SetContextAttribute( UiConst.IndentSize, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单元格控件 + /// + [Fact] + public void TestCellControl() { + _wrapper.SetContextAttribute( UiConst.CellControl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单元格控件 + /// + [Fact] + public void TestBindCellControl() { + _wrapper.SetContextAttribute( AngularConst.BindCellControl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复选框选中状态变化事件 + /// + [Fact] + public void TestOnCheckedChange() { + _wrapper.SetContextAttribute( UiConst.OnCheckedChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试展开状态变化事件 + /// + [Fact] + public void TestOnExpandChange() { + _wrapper.SetContextAttribute( UiConst.OnExpandChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击事件 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableHeadColumnTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableHeadColumnTagHelperTest.Expression.cs new file mode 100644 index 000000000..cd554a62b --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableHeadColumnTagHelperTest.Expression.cs @@ -0,0 +1,91 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表头单元格测试 - 表达式支持 +/// +public partial class TableHeadColumnTagHelperTest { + /// + /// 测试表达式 + /// + [Fact] + public void TestFor_1() { + _wrapper.SetExpression( UiConst.For, t => t.Code ); + var result = new StringBuilder(); + result.Append( "code" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表达式 - 多语言 + /// + [Fact] + public void TestFor_2() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetExpression( UiConst.For, t => t.Code ); + var result = new StringBuilder(); + result.Append( "{{'code'|i18n}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表达式 - 覆盖标题 + /// + [Fact] + public void TestFor_3() { + _wrapper.SetExpression( UiConst.For, t => t.Code ); + _wrapper.SetContextAttribute( UiConst.Title, "c" ); + var result = new StringBuilder(); + result.Append( "c" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试属性表达式 - 启用排序 + /// + [Fact] + public void TestFor_4() { + //启用设置 + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableTableSort = true } ); + _wrapper.SetItem( new TableShareConfig() ); + + //设置表达式 + _wrapper.SetExpression( UiConst.For, t => t.Code ); + + //结果 + var result = new StringBuilder(); + result.Append( "code" ); + + //验证 + Assert.Equal( result.ToString(), _wrapper.GetResult() ); + } + + /// + /// 测试属性表达式 - 启用排序 - 手工覆盖 + /// + [Fact] + public void TestFor_5() { + //启用设置 + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableTableSort = true } ); + _wrapper.SetItem( new TableShareConfig() ); + + //设置表达式 + _wrapper.SetExpression( UiConst.For, t => t.Code ); + + //覆盖排序 + _wrapper.SetContextAttribute( UiConst.Sort, "" ); + + //结果 + var result = new StringBuilder(); + result.Append( "code" ); + + //验证 + Assert.Equal( result.ToString(), _wrapper.GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableHeadColumnTagHelperTest.Extend.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableHeadColumnTagHelperTest.Extend.cs new file mode 100644 index 000000000..068ea7df3 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableHeadColumnTagHelperTest.Extend.cs @@ -0,0 +1,206 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表头单元格测试 - 指令扩展测试 +/// +public partial class TableHeadColumnTagHelperTest { + + #region Title + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 - 多语言 + /// + [Fact] + public void TestTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "{{'a'|i18n}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region TitleOperation + + /// + /// 测试Operation标题 + /// + [Fact] + public void TestTitleOperation() { + _wrapper.SetContextAttribute( UiConst.TitleOperation, true ); + var result = new StringBuilder(); + result.Append( "Operation" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Operation标题 - 多语言 + /// + [Fact] + public void TestTitleOperation_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TitleOperation, true ); + var result = new StringBuilder(); + result.Append( "{{'util.operation'|i18n}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region TitleAlign + + /// + /// 测试标题对齐方式 + /// + [Fact] + public void TestTitleAlign() { + _wrapper.SetContextAttribute( UiConst.TitleAlign, TableHeadColumnAlign.Center ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题对齐方式 + /// + [Fact] + public void TestBindTitleAlign() { + _wrapper.SetContextAttribute( AngularConst.BindTitleAlign, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Checkbox + + /// + /// 测试表头复选框 + /// + [Fact] + public void TestCheckbox() { + _wrapper.SetItem( new TableShareConfig( "id" ) { IsShowCheckbox = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Radio + + /// + /// 测试表头单选框 + /// + [Fact] + public void TestRadio_1() { + _wrapper.SetItem( new TableShareConfig( "id" ) { IsShowRadio = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表头单选框 - 已设置复选框 + /// + [Fact] + public void TestRadio_2() { + _wrapper.SetItem( new TableShareConfig( "id" ) { IsShowCheckbox = true, IsShowRadio = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region LineNumber + + /// + /// 测试序号 + /// + [Fact] + public void TestLineNumber_1() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetItem( new TableShareConfig( "id" ) { IsShowLineNumber = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "{{'util.lineNumber'|i18n}}" ); + result.Append( "{{'a'|i18n}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试序号 - 同时设置复选框 + /// + [Fact] + public void TestLineNumber_2() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetItem( new TableShareConfig( "id" ) { + IsShowCheckbox = true, + IsShowLineNumber = true + } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.lineNumber'|i18n}}" ); + result.Append( "{{'a'|i18n}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Sort + + /// + /// 测试排序 + /// + [Fact] + public void TestSort() { + _wrapper.SetItem( new TableShareConfig( "id" ) ); + _wrapper.SetContextAttribute( UiConst.Sort, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableHeadColumnTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableHeadColumnTagHelperTest.cs new file mode 100644 index 000000000..66fd91d91 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableHeadColumnTagHelperTest.cs @@ -0,0 +1,472 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表头单元格测试 +/// +public partial class TableHeadColumnTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TableHeadColumnTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TableHeadColumnTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示复选框 + /// + [Fact] + public void TestShowCheckbox() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否禁用复选框 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复选框是否中间状态 + /// + [Fact] + public void TestIndeterminate() { + _wrapper.SetContextAttribute( UiConst.Indeterminate, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否选中复选框 + /// + [Fact] + public void TestChecked() { + _wrapper.SetContextAttribute( UiConst.Checked, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否选中复选框 + /// + [Fact] + public void TestBindonChecked() { + _wrapper.SetContextAttribute( AngularConst.BindonChecked, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否选中复选框 + /// + [Fact] + public void TestShowRowSelection() { + _wrapper.SetContextAttribute( UiConst.ShowRowSelection, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉选择的内容 + /// + [Fact] + public void TestSelections() { + _wrapper.SetContextAttribute( UiConst.Selections, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示排序 + /// + [Fact] + public void TestShowSort() { + _wrapper.SetContextAttribute( UiConst.ShowSort, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试排序函数 + /// + [Fact] + public void TestSortFn() { + _wrapper.SetContextAttribute( UiConst.SortFn, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试支持的排序方式 + /// + [Fact] + public void TestSortDirections() { + _wrapper.SetContextAttribute( UiConst.SortDirections, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试排序方向 + /// + [Fact] + public void TestSortOrder() { + _wrapper.SetContextAttribute( UiConst.SortOrder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试排序方向 + /// + [Fact] + public void TestBindSortOrder() { + _wrapper.SetContextAttribute( AngularConst.BindSortOrder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试排序方向 + /// + [Fact] + public void TestBindonSortOrder() { + _wrapper.SetContextAttribute( AngularConst.BindonSortOrder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示过滤 + /// + [Fact] + public void TestShowFilter() { + _wrapper.SetContextAttribute( UiConst.ShowFilter, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试过滤函数 + /// + [Fact] + public void TestFilterFn() { + _wrapper.SetContextAttribute( UiConst.FilterFn, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试过滤器 + /// + [Fact] + public void TestFilters() { + _wrapper.SetContextAttribute( UiConst.Filters, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否多选过滤器 + /// + [Fact] + public void TestFilterMultiple() { + _wrapper.SetContextAttribute( UiConst.FilterMultiple, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽度 - 像素值 + /// + [Fact] + public void TestWidth_1() { + _wrapper.SetContextAttribute( UiConst.Width,"1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽度 - 百分比 + /// + [Fact] + public void TestWidth_2() { + _wrapper.SetContextAttribute( UiConst.Width, "10%" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽度 + /// + [Fact] + public void TestBindWidth() { + _wrapper.SetContextAttribute( AngularConst.BindWidth, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试左侧距离 + /// + [Fact] + public void TestLeft() { + _wrapper.SetContextAttribute( UiConst.Left, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试左侧距离 + /// + [Fact] + public void TestBindLeft() { + _wrapper.SetContextAttribute( AngularConst.BindLeft, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试右侧距离 + /// + [Fact] + public void TestRight() { + _wrapper.SetContextAttribute( UiConst.Right, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试右侧距离 + /// + [Fact] + public void TestBindRight() { + _wrapper.SetContextAttribute( AngularConst.BindRight, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试对齐方式 + /// + [Fact] + public void TestAlign() { + _wrapper.SetContextAttribute( UiConst.Align, TableHeadColumnAlign.Left ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试对齐方式 + /// + [Fact] + public void TestBindAlign() { + _wrapper.SetContextAttribute( AngularConst.BindAlign, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否折行显示 + /// + [Fact] + public void TestBreakWord() { + _wrapper.SetContextAttribute( UiConst.BreakWord, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动省略 + /// + [Fact] + public void TestEllipsis() { + _wrapper.SetContextAttribute( UiConst.Ellipsis, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列跨度 + /// + [Fact] + public void TestColspan() { + _wrapper.SetContextAttribute( UiConst.Colspan, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试行跨度 + /// + [Fact] + public void TestRowspan() { + _wrapper.SetContextAttribute( UiConst.Rowspan, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列标识 + /// + [Fact] + public void TestColumnKey() { + _wrapper.SetContextAttribute( UiConst.ColumnKey, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列标识 + /// + [Fact] + public void TestBindColumnKey() { + _wrapper.SetContextAttribute( AngularConst.BindColumnKey, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单元格控件 + /// + [Fact] + public void TestCellControl() { + _wrapper.SetContextAttribute( UiConst.CellControl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单元格控件 + /// + [Fact] + public void TestBindCellControl() { + _wrapper.SetContextAttribute( AngularConst.BindCellControl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复选框选中状态变化事件 + /// + [Fact] + public void TestOnCheckedChange() { + _wrapper.SetContextAttribute( UiConst.OnCheckedChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试排序方向变化事件 + /// + [Fact] + public void TestOnSortOrderChange() { + _wrapper.SetContextAttribute( UiConst.OnSortOrderChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试过滤条件变化事件 + /// + [Fact] + public void TestOnFilterChange() { + _wrapper.SetContextAttribute( UiConst.OnFilterChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableHeadTagHelperTest.AutoCreate.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableHeadTagHelperTest.AutoCreate.cs new file mode 100644 index 000000000..b9f7b0744 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableHeadTagHelperTest.AutoCreate.cs @@ -0,0 +1,189 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Components.Tables.Helpers; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表头测试 - 自动创建嵌套结构 +/// +public partial class TableHeadTagHelperTest { + /// + /// 测试自动创建嵌套结构 - 添加一个表头单元格,自动创建表头行 + /// + [Fact] + public void TestAutoCreate_1() { + //设置表格共享配置 + _wrapper.SetItem( new TableShareConfig() ); + + //创建表头单元格 + var column = new TableHeadColumnTagHelper().ToWrapper(); + column.AppendContent( "a" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动创建嵌套结构 - 添加一个列,自动创建表头行和表头单元格 + /// + [Fact] + public void TestAutoCreate_2() { + //设置表格共享配置 + _wrapper.SetItem( new TableShareConfig{Columns = { new ColumnInfo{Title = "a"} }} ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动创建嵌套结构 - 添加表头行和一个表头单元格,不自动创建 + /// + [Fact] + public void TestAutoCreate_3() { + //设置表格共享配置 + _wrapper.SetItem( new TableShareConfig() ); + + //创建表头行 + var row = new TableRowTagHelper().ToWrapper(); + _wrapper.AppendContent( row ); + + //创建表头单元格 + var column = new TableHeadColumnTagHelper().ToWrapper(); + column.AppendContent( "a" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动创建嵌套结构 - 添加表头行和一个列,自动创建表头单元格 + /// + [Fact] + public void TestAutoCreate_4() { + //设置表格共享配置 + _wrapper.SetItem( new TableShareConfig { Columns = { new ColumnInfo { Title = "a" } } } ); + + //创建表头行 + var row = new TableRowTagHelper().ToWrapper(); + _wrapper.AppendContent( row ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动创建嵌套结构 - 添加一个表头单元格,取消自动创建表头行 + /// + [Fact] + public void TestAutoCreate_5() { + //设置表格共享配置 + _wrapper.SetItem( new TableShareConfig() ); + + //取消自动创建 + _wrapper.SetContextAttribute( UiConst.EnableAutoCreate, "false" ); + + //创建表头单元格 + var column = new TableHeadColumnTagHelper().ToWrapper(); + column.AppendContent( "a" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动创建嵌套结构 - 添加表头行和一个列,取消自动创建表头单元格 + /// + [Fact] + public void TestAutoCreate_6() { + //设置表格共享配置 + _wrapper.SetItem( new TableShareConfig { Columns = { new ColumnInfo { Title = "a" } } } ); + + //取消自动创建 + _wrapper.SetContextAttribute( UiConst.EnableAutoCreate, "false" ); + + //创建表头行 + var row = new TableRowTagHelper().ToWrapper(); + _wrapper.AppendContent( row ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动创建嵌套结构 - 添加表头行和一个列,取消自动创建表头单元格 + /// + [Fact] + public void TestAutoCreate_7() { + //设置表格共享配置 + _wrapper.SetItem( new TableShareConfig { Columns = { new ColumnInfo { Title = "a" } } } ); + + //创建表头行 + var row = new TableRowTagHelper().ToWrapper(); + _wrapper.AppendContent( row ); + + //取消表头行自动创建 + row.SetContextAttribute( UiConst.EnableAutoCreate, "false" ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableHeadTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableHeadTagHelperTest.cs new file mode 100644 index 000000000..4e920e00e --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableHeadTagHelperTest.cs @@ -0,0 +1,62 @@ +using System.Text; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表头测试 +/// +public partial class TableHeadTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TableHeadTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TableHeadTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + //设置表格共享配置 + _wrapper.SetItem( new TableShareConfig{IsAutoCreateHeadRow = false} ); + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableRowTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableRowTagHelperTest.cs new file mode 100644 index 000000000..7207e94d6 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableRowTagHelperTest.cs @@ -0,0 +1,167 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格行测试 +/// +public class TableRowTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TableRowTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TableRowTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试当前列是否展开 + /// + [Fact] + public void TestExpand() { + _wrapper.SetContextAttribute( UiConst.Expand, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击选中行 + /// + [Fact] + public void TestSelectOnClick() { + _wrapper.SetContextAttribute( UiConst.SelectOnClick, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击选中行 - 同时设置onclick + /// + [Fact] + public void TestSelectOnClick_2() { + _wrapper.SetContextAttribute( UiConst.SelectOnClick, true ); + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击仅选中一行 + /// + [Fact] + public void TestSelectOnlyOnClick() { + _wrapper.SetContextAttribute( UiConst.SelectOnlyOnClick, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击仅选中一行 - 同时设置onclick + /// + [Fact] + public void TestSelectOnlyOnClick_2() { + _wrapper.SetContextAttribute( UiConst.SelectOnlyOnClick, true ); + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试点击表格行时是否选中复选框或单选框 - 复选框 + /// + [Fact] + public void TestCheckOnClick_1() { + _wrapper.SetItem(new TableShareConfig { IsShowCheckbox = true }); + _wrapper.SetContextAttribute( UiConst.CheckOnClick, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试点击表格行时是否选中复选框或单选框 - 单选框 + /// + [Fact] + public void TestCheckOnClick_2() { + _wrapper.SetItem( new TableShareConfig { IsShowRadio = true } ); + _wrapper.SetContextAttribute( UiConst.CheckOnClick, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试勾选样式 + /// + [Fact] + public void TestEnableTableRowCheckedClass() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableTableRowCheckedClass = true } ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试行单击事件 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.AutoCreate.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.AutoCreate.cs new file mode 100644 index 000000000..2ce7da0fd --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.AutoCreate.cs @@ -0,0 +1,372 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格测试 - 自动创建嵌套结构 +/// +public partial class TableTagHelperTest { + /// + /// 测试自动创建嵌套结构 - 仅添加一个列,将自动创建其它嵌套结构 + /// + [Fact] + public void TestAutoCreate_1() { + _wrapper.SetContextAttribute( UiConst.Class, "a" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动创建嵌套结构 - 设置完整结构,不生成 + /// + [Fact] + public void TestAutoCreate_2() { + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + head.SetContextAttribute( "hidden", "true" ); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动创建嵌套结构 - 仅生成表头单元格 + /// + [Fact] + public void TestAutoCreate_3() { + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + head.SetContextAttribute( "hidden", "true" ); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动创建嵌套结构 - 生成表头行和表头单元格 + /// + [Fact] + public void TestAutoCreate_4() { + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + head.SetContextAttribute( "hidden", "true" ); + _wrapper.AppendContent( head ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动创建嵌套结构 - 生成表格主体行 + /// + [Fact] + public void TestAutoCreate_5() { + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + head.SetContextAttribute( "hidden", "true" ); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + body.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动创建嵌套结构 - 生成表格主体行和表头单元格 + /// + [Fact] + public void TestAutoCreate_6() { + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + head.SetContextAttribute( "hidden", "true" ); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + body.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动创建嵌套结构 - 生成表格主体行,表头行,表头单元格 + /// + [Fact] + public void TestAutoCreate_7() { + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + head.SetContextAttribute( "hidden", "true" ); + _wrapper.AppendContent( head ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + body.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动创建嵌套结构 - 生成表头行,表头单元格,表格主体 + /// + [Fact] + public void TestAutoCreate_8() { + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + row.SetContextAttribute( UiConst.Id, "rowId" ); + _wrapper.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动创建嵌套结构 - 添加一个列,取消自动创建 + /// + [Fact] + public void TestAutoCreate_9() { + //取消自动创建 + _wrapper.SetContextAttribute( UiConst.EnableAutoCreate, "false" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.Checkbox.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.Checkbox.cs new file mode 100644 index 000000000..3565e1234 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.Checkbox.cs @@ -0,0 +1,1329 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格测试 - 复选框 +/// +public partial class TableTagHelperTest { + /// + /// 测试设置复选框 - 添加一个列 + /// + [Fact] + public void TestShowCheckbox_1() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 创建完整结构 + /// + [Fact] + public void TestShowCheckbox_2() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 仅生成表头单元格 + /// + [Fact] + public void TestShowCheckbox_3() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 生成表头行和表头单元格 + /// + [Fact] + public void TestShowCheckbox_4() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 生成表格主体行 + /// + [Fact] + public void TestShowCheckbox_5() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + body.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 生成表格主体行和表头单元格 + /// + [Fact] + public void TestShowCheckbox_6() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + body.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 生成表格主体行,表头行,表头单元格 + /// + [Fact] + public void TestShowCheckbox_7() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + body.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 生成表头行,表头单元格,表格主体 + /// + [Fact] + public void TestShowCheckbox_8() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + _wrapper.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 启用自定义列 + /// + [Fact] + public void TestShowCheckbox_9() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 启用自定义列 - 创建完整结构 + /// + [Fact] + public void TestShowCheckbox_10() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 启用自定义列 - 表格设置启用固定列 + /// + [Fact] + public void TestShowCheckbox_11() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 启用自定义列 - 表格设置启用固定列 - 创建完整结构 + /// + [Fact] + public void TestShowCheckbox_12() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 合并列 + /// + [Fact] + public void TestShowCheckbox_13() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "t" ); + th.SetContextAttribute( UiConst.Colspan, 2 ); + headRow.AppendContent( th ); + + //创建表头行2 + var headRow2 = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow2 ); + + //创建表头单元格 + var th21 = new TableHeadColumnTagHelper().ToWrapper(); + th21.AppendContent( "a" ); + headRow2.AppendContent( th21 ); + + //创建表头单元格2 + var th22 = new TableHeadColumnTagHelper().ToWrapper(); + th22.AppendContent( "c" ); + headRow2.AppendContent( th22 ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //创建列2 + var column2 = new TableColumnTagHelper().ToWrapper(); + column2.AppendContent( "d" ); + row.AppendContent( column2 ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "t" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "c" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "d" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定复选框 + /// + [Fact] + public void TestCheckboxLeft_1() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.CheckboxLeft, "10px" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定复选框 - 手工创建完整结构 + /// + [Fact] + public void TestCheckboxLeft_2() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.CheckboxLeft, "10px" ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定复选框 - 启用自定义列 + /// + [Fact] + public void TestCheckboxLeft_3() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.CheckboxLeft, "10px" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定复选框 - 启用自定义列 - 表格设置启用固定列 + /// + [Fact] + public void TestCheckboxLeft_4() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.CheckboxLeft, "10px" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定复选框 - 启用自定义列 - 创建完整结构 + /// + [Fact] + public void TestCheckboxLeft_5() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.CheckboxLeft, "10px" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定复选框 - 启用自定义列 - 表格设置启用固定列 - 创建完整结构 + /// + [Fact] + public void TestCheckboxLeft_6() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.CheckboxLeft, "10px" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 第一列设置为左侧固定,自动设置复选框Left + /// + [Fact] + public void TestShowCheckbox_Left_1() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Left, "true" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 创建完整结构 - 第一列表头设置为左侧固定,自动设置复选框Left + /// + [Fact] + public void TestShowCheckbox_Left_2() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Left, "true" ); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.CustomColumn.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.CustomColumn.cs new file mode 100644 index 000000000..c2f5e6441 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.CustomColumn.cs @@ -0,0 +1,579 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格测试 - 自定义列扩展 +/// +public partial class TableTagHelperTest { + /// + /// 测试启用自定义列 + /// + [Fact] + public void TestEnableCustomColumn_1() { + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 添加1列,自动创建表头 + /// + [Fact] + public void TestEnableCustomColumn_2() { + //启用自定义列 + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 添加1列,自动创建表头 - 设置CellControl + /// + [Fact] + public void TestEnableCustomColumn_3() { + //启用自定义列 + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + column.SetContextAttribute( UiConst.CellControl, "c" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 添加1列,自动创建表头 - 设置宽度 + /// + [Fact] + public void TestEnableCustomColumn_4() { + //启用自定义列 + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + column.SetContextAttribute( UiConst.Width, "100px" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 添加1列,设置完整结构 + /// + [Fact] + public void TestEnableCustomColumn_5() { + //启用自定义列 + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Column, "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 添加1列,设置完整结构 - th设置CellControl + /// + [Fact] + public void TestEnableCustomColumn_6() { + //启用自定义列 + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + th.SetContextAttribute( UiConst.CellControl, "c" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Column, "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 添加1列,设置完整结构 - td设置宽度 + /// + [Fact] + public void TestEnableCustomColumn_7() { + //启用自定义列 + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Column, "b" ); + column.SetContextAttribute( UiConst.Width, "100" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 添加1列,设置完整结构 - th设置宽度 + /// + [Fact] + public void TestEnableCustomColumn_8() { + //启用自定义列 + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Width, "100" ); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Column, "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 添加2列,自动创建表头 + /// + [Fact] + public void TestEnableCustomColumn_9() { + //启用自定义列 + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列1 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + _wrapper.AppendContent( column ); + + //创建列2 + var column2 = new TableColumnTagHelper().ToWrapper(); + column2.SetContextAttribute( UiConst.Title, "c" ); + column2.SetContextAttribute( UiConst.Column, "d" ); + _wrapper.AppendContent( column2 ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "c" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "{{row.d}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 添加1列,自动创建表头 - 设置acl + /// + [Fact] + public void TestEnableCustomColumn_10() { + //启用自定义列 + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + column.SetContextAttribute( UiConst.Acl, "c" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 未设置key,使用id作为key + /// + [Fact] + public void TestEnableCustomColumn_11() { + //启用自定义列 + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 固定到左侧 - 设置完整结构 + /// + [Fact] + public void TestEnableCustomColumn_12() { + //启用自定义列 + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + th.SetContextAttribute( UiConst.Left, "10px" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Column, "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 固定到右侧 - 设置完整结构 + /// + [Fact] + public void TestEnableCustomColumn_13() { + //启用自定义列 + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + th.SetContextAttribute( UiConst.Right, "10px" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Column, "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.EnableFixedColumn.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.EnableFixedColumn.cs new file mode 100644 index 000000000..580985214 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.EnableFixedColumn.cs @@ -0,0 +1,297 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格测试 - 表格设置启用固定列扩展 +/// +public partial class TableTagHelperTest { + /// + /// 测试启用固定列 + /// + [Fact] + public void TestEnableFixedColumn_1() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用固定列 - 设置完整结构 + /// + [Fact] + public void TestEnableFixedColumn_2() { + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Column, "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用固定列 - 固定到左侧 + /// + [Fact] + public void TestEnableFixedColumn_3() { + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + column.SetContextAttribute( UiConst.Left, "true" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用固定列 - 固定到左侧 - 设置完整结构 + /// + [Fact] + public void TestEnableFixedColumn_4() { + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + th.SetContextAttribute( UiConst.Left, true ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Column, "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用固定列 - 固定到右侧 + /// + [Fact] + public void TestEnableFixedColumn_5() { + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + column.SetContextAttribute( UiConst.Right, true ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用固定列 - 固定到右侧 - 设置完整结构 + /// + [Fact] + public void TestEnableFixedColumn_6() { + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + th.SetContextAttribute( UiConst.Right, true ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Column, "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.Extend.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.Extend.cs new file mode 100644 index 000000000..6d90de693 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.Extend.cs @@ -0,0 +1,812 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Inputs; +using Util.Ui.NgZorro.Components.Tables; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格测试 - 指令扩展 +/// +public partial class TableTagHelperTest { + + #region QueryParam + + /// + /// 测试查询参数 + /// + [Fact] + public void TestQueryParam() { + _wrapper.SetContextAttribute( UiConst.QueryParam, "b" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Sort + + /// + /// 测试排序条件 + /// + [Fact] + public void TestSort() { + _wrapper.SetContextAttribute( UiConst.Sort, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region BindSort + + /// + /// 测试排序条件 + /// + [Fact] + public void TestBindSort() { + _wrapper.SetContextAttribute( AngularConst.BindSort, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region CheckedKeys + + /// + /// 测试复选框选中的标识列表 + /// + [Fact] + public void TestCheckedKeys() { + _wrapper.SetContextAttribute( UiConst.CheckedKeys, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Url + + /// + /// 测试Api地址 + /// + [Fact] + public void TestUrl_1() { + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 添加表格总行数模板标签 + /// + /// 结果 + private void AppendTotalTemplate( StringBuilder result ) { + result.Append( "" ); + result.Append( "{{ 'util.tableTotalTemplate'|i18n:{start:range[0],end:range[1],total:total} }}" ); + result.Append( "" ); + } + + /// + /// 测试Api地址 - 添加一个列,自动创建嵌套结构 + /// + [Fact] + public void TestUrl_2() { + //设置baseurl属性 + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Api地址 - 自定义表格主体行的*ngFor + /// + [Fact] + public void TestUrl_3() { + //设置baseurl属性 + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + head.SetContextAttribute( "hidden", "true" ); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + row.SetContextAttribute( AngularConst.NgFor, "a" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region BindUrl + + /// + /// 测试Api地址 + /// + [Fact] + public void TestBindUrl() { + _wrapper.SetContextAttribute( AngularConst.BindUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region DeleteUrl + + /// + /// 测试删除地址 + /// + [Fact] + public void TestDeleteUrl() { + _wrapper.SetContextAttribute( UiConst.DeleteUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region BindDeleteUrl + + /// + /// 测试删除地址 + /// + [Fact] + public void TestBindDeleteUrl() { + _wrapper.SetContextAttribute( AngularConst.BindDeleteUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region AutoLoad + + /// + /// 测试自动加载 + /// + [Fact] + public void TestAutoLoad() { + _wrapper.SetContextAttribute( UiConst.AutoLoad, false ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region EnableExtend + + /// + /// 测试启用扩展 + /// + [Fact] + public void TestEnableExtend() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用扩展 - 当启用扩展时,设置数据源会先输入到扩展指令再导出 + /// + [Fact] + public void TestEnableExtend_Data() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用扩展 - 覆盖是否显示改变分页大小按钮 + /// + [Fact] + public void TestEnableExtend_ShowSizeChanger() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( UiConst.ShowSizeChanger, "false" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用扩展 - 覆盖是否显示分页快速跳转 + /// + [Fact] + public void TestEnableExtend_ShowQuickJumper() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( UiConst.ShowQuickJumper, "false" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用扩展 - 覆盖总行数 + /// + [Fact] + public void TestEnableExtend_Total() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( UiConst.Total, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region SelectOnClickRow + + /// + /// 测试单击选中行 + /// + [Fact] + public void TestSelectOnClickRow_1() { + //设置单击选中行 + _wrapper.SetContextAttribute( UiConst.SelectOnClickRow, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击选中行 - 手工创建行 + /// + [Fact] + public void TestSelectOnClickRow_2() { + //设置单击选中行 + _wrapper.SetContextAttribute( UiConst.SelectOnClickRow, true ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + //设置行单击事件 + row.SetContextAttribute( UiConst.OnClick, "d" ); + _wrapper.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region SelectOnlyOnClickRow + + /// + /// 测试单击仅选中一行 + /// + [Fact] + public void TestSelectOnlyOnClickRow_1() { + //设置单击选中行 + _wrapper.SetContextAttribute( UiConst.SelectOnlyOnClickRow, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击仅选中一行 - 手工创建行 + /// + [Fact] + public void TestSelectOnlyOnClickRow_2() { + //设置单击选中行 + _wrapper.SetContextAttribute( UiConst.SelectOnlyOnClickRow, true ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + //设置行单击事件 + row.SetContextAttribute( UiConst.OnClick, "d" ); + _wrapper.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region CheckOnClickRow + + /// + /// 测试点击表格行时是否选中复选框或单选框 - 复选框 + /// + [Fact] + public void TestCheckOnClickRow_1() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.CheckOnClickRow, true ); + + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试点击表格行时是否选中复选框或单选框 - 单选框 + /// + [Fact] + public void TestCheckOnClickRow_2() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.CheckOnClickRow, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnLoad + + /// + /// 测试数据加载完成事件 + /// + [Fact] + public void TestOnLoad() { + _wrapper.SetContextAttribute( UiConst.OnLoad, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnClickRow + + /// + /// 测试行单击事件 + /// + [Fact] + public void TestOnClickRow_1() { + //设置行单击事件 + _wrapper.SetContextAttribute( UiConst.OnClickRow, "c" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试行单击事件 - 同时设置OnClickRow和行上的OnClick + /// + [Fact] + public void TestOnClickRow_2() { + //设置行单击事件 + _wrapper.SetContextAttribute( UiConst.OnClickRow, "c" ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + //设置行单击事件 + row.SetContextAttribute( UiConst.OnClick, "d" ); + _wrapper.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试行单击事件 - 设置完整结构 + /// + [Fact] + public void TestOnClickRow_3() { + //设置行单击事件 + _wrapper.SetContextAttribute( UiConst.OnClickRow, "c" ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region IsBatchEdit + + /// + /// 测试启用批量编辑模式 + /// + [Fact] + public void TestIsBatchEdit() { + //创建表格 + _wrapper.SetContextAttribute( UiConst.IsBatchEdit, true ); + + //添加列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.IsEdit, true ); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + _wrapper.AppendContent( column ); + + //添加输入框组件 + var input = new InputTagHelper().ToWrapper(); + column.AppendContent( input ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.LineNumber.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.LineNumber.cs new file mode 100644 index 000000000..06c80eaef --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.LineNumber.cs @@ -0,0 +1,919 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.NgZorro.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格测试 - 序号 +/// +public partial class TableTagHelperTest { + /// + /// 测试设置序号 - 添加一个列 + /// + [Fact] + public void TestShowLineNumber_1() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.ShowLineNumber, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.lineNumber'|i18n}}" ); + result.Append( "{{'a'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.lineNumber}}" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置序号 - 添加一个列,同时显示复选框 + /// + [Fact] + public void TestShowLineNumber_2() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.ShowLineNumber, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.lineNumber'|i18n}}" ); + result.Append( "{{'a'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.lineNumber}}" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 手工创建完整结构 + /// + [Fact] + public void TestShowLineNumber_3() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.ShowLineNumber, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.lineNumber'|i18n}}" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.lineNumber}}" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 手工创建完整结构,同时显示复选框 + /// + [Fact] + public void TestShowLineNumber_4() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.ShowLineNumber, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.lineNumber'|i18n}}" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.lineNumber}}" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置序号 - 启用自定义列 + /// + [Fact] + public void TestShowLineNumber_5() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.ShowLineNumber, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.lineNumber'|i18n}}" ); + result.Append( "{{'a'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.lineNumber}}" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置序号 - 启用自定义列 - 创建完整结构 + /// + [Fact] + public void TestShowLineNumber_6() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.ShowLineNumber, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.lineNumber'|i18n}}" ); + result.Append( "{{'a'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.lineNumber}}" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置序号 - 启用自定义列 - 表格设置启用固定列 + /// + [Fact] + public void TestShowLineNumber_7() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.ShowLineNumber, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.lineNumber'|i18n}}" ); + result.Append( "{{'a'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.lineNumber}}" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置序号 - 启用自定义列 - 表格设置启用固定列 - 创建完整结构 + /// + [Fact] + public void TestShowLineNumber_8() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.ShowLineNumber, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.lineNumber'|i18n}}" ); + result.Append( "{{'a'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.lineNumber}}" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置序号 - 合并列 + /// + [Fact] + public void TestShowLineNumber_9() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.ShowLineNumber, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "t" ); + th.SetContextAttribute( UiConst.Colspan, 2 ); + headRow.AppendContent( th ); + + //创建表头行2 + var headRow2 = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow2 ); + + //创建表头单元格 + var th21 = new TableHeadColumnTagHelper().ToWrapper(); + th21.AppendContent( "a" ); + headRow2.AppendContent( th21 ); + + //创建表头单元格2 + var th22 = new TableHeadColumnTagHelper().ToWrapper(); + th22.AppendContent( "c" ); + headRow2.AppendContent( th22 ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //创建列2 + var column2 = new TableColumnTagHelper().ToWrapper(); + column2.AppendContent( "d" ); + row.AppendContent( column2 ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.lineNumber'|i18n}}" ); + result.Append( "t" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "c" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.lineNumber}}" ); + result.Append( "b" ); + result.Append( "d" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定序号 + /// + [Fact] + public void TestLineNumberLeft_1() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.ShowLineNumber, true ); + _wrapper.SetContextAttribute( UiConst.LineNumberLeft, "true" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.lineNumber'|i18n}}" ); + result.Append( "{{'a'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.lineNumber}}" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定序号 - 手工创建完整结构,同时显示固定的复选框 + /// + [Fact] + public void TestLineNumberLeft_2() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.ShowLineNumber, true ); + _wrapper.SetContextAttribute( UiConst.CheckboxLeft, "10px" ); + _wrapper.SetContextAttribute( UiConst.LineNumberLeft, "10px" ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + row.SetContextAttribute( AngularConst.NgFor, "let row of x_id.dataSource" ); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.lineNumber'|i18n}}" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.lineNumber}}" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定序号 - 启用自定义列 + /// + [Fact] + public void TestLineNumberLeft_3() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.ShowLineNumber, true ); + _wrapper.SetContextAttribute( UiConst.LineNumberLeft, "10px" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.lineNumber'|i18n}}" ); + result.Append( "{{'a'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.lineNumber}}" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定序号 - 启用自定义列 - 表格设置启用固定列 + /// + [Fact] + public void TestLineNumberLeft_4() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.ShowLineNumber, true ); + _wrapper.SetContextAttribute( UiConst.LineNumberLeft, "10px" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.lineNumber'|i18n}}" ); + result.Append( "{{'a'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.lineNumber}}" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定序号 - 启用自定义列 - 创建完整结构 + /// + [Fact] + public void TestLineNumberLeft_5() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.ShowLineNumber, true ); + _wrapper.SetContextAttribute( UiConst.LineNumberLeft, "10px" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.lineNumber'|i18n}}" ); + result.Append( "{{'a'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.lineNumber}}" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定序号 - 启用自定义列 - 表格设置启用固定列 - 创建完整结构 + /// + [Fact] + public void TestLineNumberLeft_6() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.ShowLineNumber, true ); + _wrapper.SetContextAttribute( UiConst.LineNumberLeft, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{'util.lineNumber'|i18n}}" ); + result.Append( "{{'a'|i18n}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.lineNumber}}" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.Radio.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.Radio.cs new file mode 100644 index 000000000..62952511f --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.Radio.cs @@ -0,0 +1,849 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格测试 - 单选框 +/// +public partial class TableTagHelperTest { + /// + /// 测试设置单选框 - 添加一个列 + /// + [Fact] + public void TestShowRadio_1() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置单选框 - 添加一个列 - 同时设置复选框,则单选框不生效 + /// + [Fact] + public void TestShowRadio_2() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 创建完整结构 + /// + [Fact] + public void TestShowRadio_3() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置单选框 - 启用自定义列 + /// + [Fact] + public void TestShowRadio_4() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置单选框 - 启用自定义列 - 创建完整结构 + /// + [Fact] + public void TestShowRadio_5() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置单选框 - 启用自定义列 - 表格设置启用固定列 + /// + [Fact] + public void TestShowRadio_6() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置单选框 - 启用自定义列 - 表格设置启用固定列 - 创建完整结构 + /// + [Fact] + public void TestShowRadio_7() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置单选框 - 合并列 + /// + [Fact] + public void TestShowRadio_8() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "t" ); + th.SetContextAttribute( UiConst.Colspan, 2 ); + headRow.AppendContent( th ); + + //创建表头行2 + var headRow2 = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow2 ); + + //创建表头单元格 + var th21 = new TableHeadColumnTagHelper().ToWrapper(); + th21.AppendContent( "a" ); + headRow2.AppendContent( th21 ); + + //创建表头单元格2 + var th22 = new TableHeadColumnTagHelper().ToWrapper(); + th22.AppendContent( "c" ); + headRow2.AppendContent( th22 ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //创建列2 + var column2 = new TableColumnTagHelper().ToWrapper(); + column2.AppendContent( "d" ); + row.AppendContent( column2 ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "t" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "c" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "d" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定单选框 + /// + [Fact] + public void TestRadioLeft_1() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.RadioLeft, "10px" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定单选框 - 创建完整结构 + /// + [Fact] + public void TestRadioLeft_2() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.RadioLeft, "10px" ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定单选框 - 启用自定义列 + /// + [Fact] + public void TestRadioLeft_3() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.RadioLeft, "10px" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定单选框 - 启用自定义列 - 表格设置启用固定列 + /// + [Fact] + public void TestRadioLeft_4() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.RadioLeft, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定单选框 - 启用自定义列 - 创建完整结构 + /// + [Fact] + public void TestRadioLeft_5() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.RadioLeft, "10px" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定单选框 - 启用自定义列 - 表格设置启用固定列 - 创建完整结构 + /// + [Fact] + public void TestRadioLeft_6() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.RadioLeft, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.Resizable.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.Resizable.cs new file mode 100644 index 000000000..902143358 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.Resizable.cs @@ -0,0 +1,642 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.NgZorro.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格测试 - 拖动调整列宽 +/// +public partial class TableTagHelperTest { + /// + /// 测试启用拖动调整列宽 - 在单元格启用 + /// + [Fact] + public void TestEnableResizable_1() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + column.SetContextAttribute( UiConst.EnableResizable, true ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用拖动调整列宽 - 在单元格启用 - 设置初始宽度 + /// + [Fact] + public void TestEnableResizable_2() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + column.SetContextAttribute( UiConst.Width, "100px" ); + column.SetContextAttribute( UiConst.EnableResizable, true ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 在表头单元格启用 + /// + [Fact] + public void TestEnableResizable_3() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + th.SetContextAttribute( UiConst.EnableResizable, true ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Column, "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 在表头单元格启用 - 设置初始宽度 + /// + [Fact] + public void TestEnableResizable_4() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + th.SetContextAttribute( UiConst.Width, "100px" ); + th.SetContextAttribute( UiConst.EnableResizable, true ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Column, "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用拖动调整列宽 - 在表格启用 + /// + [Fact] + public void TestEnableResizable_5() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + _wrapper.SetContextAttribute( UiConst.EnableResizable, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用拖动调整列宽 - 在表格启用 - 多个列时最后一列不设置调整宽度手柄 + /// + [Fact] + public void TestEnableResizable_6() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + _wrapper.SetContextAttribute( UiConst.EnableResizable, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + _wrapper.AppendContent( column ); + + //创建列2 + var column2 = new TableColumnTagHelper().ToWrapper(); + column2.SetContextAttribute( UiConst.Title, "c" ); + column2.SetContextAttribute( UiConst.Column, "d" ); + _wrapper.AppendContent( column2 ); + + //创建列3 + var column3 = new TableColumnTagHelper().ToWrapper(); + column3.SetContextAttribute( UiConst.Title, "e" ); + column3.SetContextAttribute( UiConst.Column, "f" ); + _wrapper.AppendContent( column3 ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "c" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "e" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "{{row.d}}" ); + result.Append( "{{row.f}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 在表格启用 - 手工创建表头单元格 + /// + [Fact] + public void TestEnableResizable_7() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + _wrapper.SetContextAttribute( UiConst.EnableResizable, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Column, "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 在表格启用 - 手工创建表头单元格 - 多个列时最后一列不设置调整宽度手柄 + /// + [Fact] + public void TestEnableResizable_8() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + _wrapper.SetContextAttribute( UiConst.EnableResizable, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表头单元格2 + var th2 = new TableHeadColumnTagHelper().ToWrapper(); + th2.SetContextAttribute( UiConst.Title, "c" ); + headRow.AppendContent( th2 ); + + //创建表头单元格3 + var th3 = new TableHeadColumnTagHelper().ToWrapper(); + th3.SetContextAttribute( UiConst.Title, "e" ); + headRow.AppendContent( th3 ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Column, "b" ); + row.AppendContent( column ); + + //创建列2 + var column2 = new TableColumnTagHelper().ToWrapper(); + column2.SetContextAttribute( UiConst.Column, "d" ); + row.AppendContent( column2 ); + + //创建列3 + var column3 = new TableColumnTagHelper().ToWrapper(); + column3.SetContextAttribute( UiConst.Column, "f" ); + row.AppendContent( column3 ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "c" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "e" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "{{row.d}}" ); + result.Append( "{{row.f}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用拖动调整列宽 - 在表格启用 - 同时启用自定义列 + /// + [Fact] + public void TestEnableResizable_9() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + _wrapper.SetContextAttribute( UiConst.EnableResizable, true ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + _wrapper.AppendContent( column ); + + //创建列2 + var column2 = new TableColumnTagHelper().ToWrapper(); + column2.SetContextAttribute( UiConst.Title, "c" ); + column2.SetContextAttribute( UiConst.Column, "d" ); + _wrapper.AppendContent( column2 ); + + //创建列3 + var column3 = new TableColumnTagHelper().ToWrapper(); + column3.SetContextAttribute( UiConst.Title, "e" ); + column3.SetContextAttribute( UiConst.Column, "f" ); + _wrapper.AppendContent( column3 ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "c" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "e" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "{{row.d}}" ); + result.Append( "{{row.f}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 在表格启用 - 手工创建表头单元格 - 同时启用自定义列 + /// + [Fact] + public void TestEnableResizable_10() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + _wrapper.SetContextAttribute( UiConst.EnableResizable, true ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表头单元格2 + var th2 = new TableHeadColumnTagHelper().ToWrapper(); + th2.SetContextAttribute( UiConst.Title, "c" ); + headRow.AppendContent( th2 ); + + //创建表头单元格3 + var th3 = new TableHeadColumnTagHelper().ToWrapper(); + th3.SetContextAttribute( UiConst.Title, "e" ); + headRow.AppendContent( th3 ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Column, "b" ); + row.AppendContent( column ); + + //创建列2 + var column2 = new TableColumnTagHelper().ToWrapper(); + column2.SetContextAttribute( UiConst.Column, "d" ); + row.AppendContent( column2 ); + + //创建列3 + var column3 = new TableColumnTagHelper().ToWrapper(); + column3.SetContextAttribute( UiConst.Column, "f" ); + row.AppendContent( column3 ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "c" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "e" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "{{row.d}}" ); + result.Append( "{{row.f}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用拖动调整列宽 - 修改表格设置组件名称 + /// + [Fact] + public void TestEnableResizable_11() { + //配置表格设置组件标签 + NgZorroOptionsService.SetOptions( new NgZorroOptions { TableSettingsTag = "table-set" } ); + + //设置存储键 + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + column.SetContextAttribute( UiConst.EnableResizable, true ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.TableSettings.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.TableSettings.cs new file mode 100644 index 000000000..c5e385f9c --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.TableSettings.cs @@ -0,0 +1,438 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.NgZorro.Enums; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格测试 - 表格设置 +/// +public partial class TableTagHelperTest { + /// + /// 测试启用表格设置 + /// + [Fact] + public void TestEnableTableSettings_1() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + _wrapper.SetContextAttribute( UiConst.EnableTableSettings, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用表格设置 - 设置初始表格尺寸 + /// + [Fact] + public void TestEnableTableSettings_2() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + _wrapper.SetContextAttribute( UiConst.EnableTableSettings, true ); + _wrapper.SetContextAttribute( UiConst.Size, TableSize.Small ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用表格设置 - 设置滚动高度 + /// + [Fact] + public void TestEnableTableSettings_3() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + _wrapper.SetContextAttribute( UiConst.EnableTableSettings, true ); + _wrapper.SetContextAttribute( UiConst.ScrollHeight, "1" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用表格设置 - 设置滚动宽度 + /// + [Fact] + public void TestEnableTableSettings_4() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + _wrapper.SetContextAttribute( UiConst.EnableTableSettings, true ); + _wrapper.SetContextAttribute( UiConst.ScrollWidth, "1" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用表格设置 - 设置滚动宽高 + /// + [Fact] + public void TestEnableTableSettings_5() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + _wrapper.SetContextAttribute( UiConst.EnableTableSettings, true ); + _wrapper.SetContextAttribute( UiConst.Scroll, "{x:'1px',y:'1px'}" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用表格设置 - 设置初始表格边框 + /// + [Fact] + public void TestEnableTableSettings_6() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + _wrapper.SetContextAttribute( UiConst.EnableTableSettings, true ); + _wrapper.SetContextAttribute( UiConst.Bordered, "true" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用表格设置 - 设置完整结构 + /// + [Fact] + public void TestEnableTableSettings_7() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + _wrapper.SetContextAttribute( UiConst.EnableTableSettings, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + th.SetContextAttribute( UiConst.TitleAlign, TableHeadColumnAlign.Center ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Column, "b" ); + column.SetContextAttribute( UiConst.Align, TableHeadColumnAlign.Right ); + column.SetContextAttribute( UiConst.Ellipsis, true ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用表格设置 - 设置完整结构 - 2列 + /// + [Fact] + public void TestEnableTableSettings_8() { + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + _wrapper.SetContextAttribute( UiConst.EnableTableSettings, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + th.SetContextAttribute( UiConst.TitleAlign, TableHeadColumnAlign.Center ); + headRow.AppendContent( th ); + + //创建表头单元格2 + var th2 = new TableHeadColumnTagHelper().ToWrapper(); + th2.SetContextAttribute( UiConst.TitleOperation, true ); + th2.SetContextAttribute( UiConst.Right, "true" ); + headRow.AppendContent( th2 ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Column, "b" ); + column.SetContextAttribute( UiConst.Ellipsis, true ); + row.AppendContent( column ); + + //创建列2 + var column2 = new TableColumnTagHelper().ToWrapper(); + column2.SetContextAttribute( UiConst.Column, "d" ); + row.AppendContent( column2 ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "Operation" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "{{row.d}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用表格设置 - 编辑列显示区域嵌套列 + /// + [Fact] + public void TestEnableTableSettings_9() { + _wrapper.SetContextAttribute( UiConst.Id, "tb" ); + _wrapper.SetContextAttribute( UiConst.Key, "k" ); + _wrapper.SetContextAttribute( UiConst.EnableTableSettings, true ); + + //添加列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.IsEdit, true ); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "name" ); + _wrapper.AppendContent( column ); + + //添加显示区域 + var display = new TableColumnDisplayTagHelper().ToWrapper(); + var subColumn = new TableColumnTagHelper().ToWrapper(); + subColumn.SetContextAttribute( UiConst.Title, "b" ); + subColumn.SetContextAttribute( UiConst.Column, "c" ); + display.AppendContent( subColumn ); + column.AppendContent( display ); + + //添加控件区域 + var control = new TableColumnControlTagHelper().ToWrapper(); + control.AppendContent( "b" ); + column.AppendContent( control ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.EndsWith( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.cs new file mode 100644 index 000000000..c1e5033df --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tables/TableTagHelperTest.cs @@ -0,0 +1,603 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Tables; + +/// +/// 表格测试 +/// +public partial class TableTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TableTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TableTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标识 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数据 + /// + [Fact] + public void TestData() { + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否前端分页 + /// + [Fact] + public void TestFrontPagination() { + _wrapper.SetContextAttribute( UiConst.FrontPagination, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数据总行数 + /// + [Fact] + public void TestTotal() { + _wrapper.SetContextAttribute( UiConst.Total, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试当前页 + /// + [Fact] + public void TestPageIndex() { + _wrapper.SetContextAttribute( UiConst.PageIndex, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试当前页 + /// + [Fact] + public void TestBindonPageIndex() { + _wrapper.SetContextAttribute( AngularConst.BindonPageIndex, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试每页显示行数 + /// + [Fact] + public void TestPageSize() { + _wrapper.SetContextAttribute( UiConst.PageSize, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试每页显示行数 + /// + [Fact] + public void TestBindonPageSize() { + _wrapper.SetContextAttribute( AngularConst.BindonPageSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示分页 + /// + [Fact] + public void TestShowPagination() { + _wrapper.SetContextAttribute( UiConst.ShowPagination, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试分页位置 + /// + [Fact] + public void TestPaginationPosition() { + _wrapper.SetContextAttribute( UiConst.PaginationPosition, TablePaginationPosition.Bottom ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试分页位置 + /// + [Fact] + public void TestBindPaginationPosition() { + _wrapper.SetContextAttribute( AngularConst.BindPaginationPosition, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试分页类型 + /// + [Fact] + public void TestPaginationType() { + _wrapper.SetContextAttribute( UiConst.PaginationType, TablePaginationSize.Small ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试分页类型 + /// + [Fact] + public void TestBindPaginationType() { + _wrapper.SetContextAttribute( AngularConst.BindPaginationType, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示边框 + /// + [Fact] + public void TestBordered() { + _wrapper.SetContextAttribute( UiConst.Bordered, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示外边框 + /// + [Fact] + public void TestOuterBordered() { + _wrapper.SetContextAttribute( UiConst.OuterBordered, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列宽度配置 + /// + [Fact] + public void TestWidthConfig() { + _wrapper.SetContextAttribute( UiConst.WidthConfig, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表格大小 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, TableSize.Small ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表格大小 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否加载状态 + /// + [Fact] + public void TestLoading() { + _wrapper.SetContextAttribute( UiConst.Loading, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载指示符 + /// + [Fact] + public void TestLoadingIndicator() { + _wrapper.SetContextAttribute( UiConst.LoadingIndicator, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试延迟显示加载时间 + /// + [Fact] + public void TestLoadingDelay() { + _wrapper.SetContextAttribute( UiConst.LoadingDelay, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试滚动 + /// + [Fact] + public void TestScroll() { + _wrapper.SetContextAttribute( UiConst.Scroll, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试滚动高度 + /// + [Fact] + public void TestScrollHeight() { + _wrapper.SetContextAttribute( UiConst.ScrollHeight, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试滚动宽度 + /// + [Fact] + public void TestScrollWidth() { + _wrapper.SetContextAttribute( UiConst.ScrollWidth, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试底部 + /// + [Fact] + public void TestFooter() { + _wrapper.SetContextAttribute( UiConst.Footer, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试底部 + /// + [Fact] + public void TestBindFooter() { + _wrapper.SetContextAttribute( AngularConst.BindFooter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试无数据显示内容 + /// + [Fact] + public void TestNoResult() { + _wrapper.SetContextAttribute( UiConst.NoResult, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试无数据显示内容 + /// + [Fact] + public void TestBindNoResult() { + _wrapper.SetContextAttribute( AngularConst.BindNoResult, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试每页行数选择列表 + /// + [Fact] + public void TestPageSizeOptions() { + _wrapper.SetContextAttribute( UiConst.PageSizeOptions, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示快速跳转 + /// + [Fact] + public void TestShowQuickJumper() { + _wrapper.SetContextAttribute( UiConst.ShowQuickJumper, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示改变分页大小按钮 + /// + [Fact] + public void TestShowSizeChanger() { + _wrapper.SetContextAttribute( UiConst.ShowSizeChanger, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示总行数和当前数据范围的模板 + /// + [Fact] + public void TestShowTotal() { + _wrapper.SetContextAttribute( UiConst.ShowTotal, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义页码结构 + /// + [Fact] + public void TestItemRender() { + _wrapper.SetContextAttribute( UiConst.ItemRender, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试只有一页时是否隐藏分页器 + /// + [Fact] + public void TestHideOnSinglePage() { + _wrapper.SetContextAttribute( UiConst.HideOnSinglePage, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示简单分页 + /// + [Fact] + public void TestSimple() { + _wrapper.SetContextAttribute( UiConst.Simple, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示简单分页 + /// + [Fact] + public void TestTemplateMode() { + _wrapper.SetContextAttribute( UiConst.TemplateMode, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚拟滚动列高 + /// + [Fact] + public void TestVirtualItemSize() { + _wrapper.SetContextAttribute( UiConst.VirtualItemSize, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚拟滚动缓冲区最大高度 + /// + [Fact] + public void TestVirtualMaxBufferPx() { + _wrapper.SetContextAttribute( UiConst.VirtualMaxBufferPx, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚拟滚动缓冲区最小高度 + /// + [Fact] + public void TestVirtualMinBufferPx() { + _wrapper.SetContextAttribute( UiConst.VirtualMinBufferPx, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚拟滚动跟踪函数 + /// + [Fact] + public void TestVirtualForTrackBy() { + _wrapper.SetContextAttribute( UiConst.VirtualForTrackBy, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表格布局 + /// + [Fact] + public void TestLayout() { + _wrapper.SetContextAttribute( UiConst.Layout, TableLayout.Fixed ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表格布局 + /// + [Fact] + public void TestBindLayout() { + _wrapper.SetContextAttribute( AngularConst.BindLayout, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义列 + /// + [Fact] + public void TestCustomColumn() { + _wrapper.SetContextAttribute( UiConst.CustomColumn, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试页码变化事件 + /// + [Fact] + public void TestOnPageIndexChange() { + _wrapper.SetContextAttribute( UiConst.OnPageIndexChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试每页行数变化事件 + /// + [Fact] + public void TestOnPageSizeChange() { + _wrapper.SetContextAttribute( UiConst.OnPageSizeChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试当前页数据变化事件 + /// + [Fact] + public void TestOnCurrentPageDataChange() { + _wrapper.SetContextAttribute( UiConst.OnCurrentPageDataChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试查询参数变化事件 + /// + [Fact] + public void TestOnQueryParams() { + _wrapper.SetContextAttribute( UiConst.OnQueryParams, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tabs/TabSetTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Tabs/TabSetTagHelperTest.cs new file mode 100644 index 000000000..0c9df9c40 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tabs/TabSetTagHelperTest.cs @@ -0,0 +1,348 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tabs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Tabs { + /// + /// 标签页测试 + /// + public class TabSetTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TabSetTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TabSetTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选中标签索引 + /// + [Fact] + public void TestSelectedIndex() { + _wrapper.SetContextAttribute( UiConst.SelectedIndex, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选中标签索引 + /// + [Fact] + public void TestBindonSelectedIndex() { + _wrapper.SetContextAttribute( AngularConst.BindonSelectedIndex, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否使用动画切换选项卡 + /// + [Fact] + public void TestAnimated() { + _wrapper.SetContextAttribute( UiConst.Animated, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签大小 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, TabSize.Small ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签大小 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签栏附加内容 + /// + [Fact] + public void TestTabBarExtraContent() { + _wrapper.SetContextAttribute( UiConst.TabBarExtraContent, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签栏样式 + /// + [Fact] + public void TestTabBarStyle() { + _wrapper.SetContextAttribute( UiConst.TabBarStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签位置 + /// + [Fact] + public void TestTabPosition() { + _wrapper.SetContextAttribute( UiConst.TabPosition, TabPosition.Left ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签位置 + /// + [Fact] + public void TestBindTabPosition() { + _wrapper.SetContextAttribute( AngularConst.BindTabPosition, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签类型 + /// + [Fact] + public void TestType() { + _wrapper.SetContextAttribute( UiConst.Type, TabType.EditableCard ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签类型 + /// + [Fact] + public void TestBindType() { + _wrapper.SetContextAttribute( AngularConst.BindType, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签间隙 + /// + [Fact] + public void TestTabBarGutter() { + _wrapper.SetContextAttribute( UiConst.TabBarGutter, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否隐藏全部标签 + /// + [Fact] + public void TestHideAll() { + _wrapper.SetContextAttribute( UiConst.HideAll, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否支持路由联动 + /// + [Fact] + public void TestLinkRouter() { + _wrapper.SetContextAttribute( UiConst.LinkRouter, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否以严格模式匹配路由联动 + /// + [Fact] + public void TestLinkExact() { + _wrapper.SetContextAttribute( UiConst.LinkExact, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签守卫函数 + /// + [Fact] + public void TestCanDeactivate() { + _wrapper.SetContextAttribute( UiConst.CanDeactivate, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标签是否居中显示 + /// + [Fact] + public void TestCentered() { + _wrapper.SetContextAttribute( UiConst.Centered, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否隐藏添加按钮 + /// + [Fact] + public void TestHideAdd() { + _wrapper.SetContextAttribute( UiConst.HideAdd, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加按钮图标 + /// + [Fact] + public void TestAddIcon() { + _wrapper.SetContextAttribute( UiConst.AddIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加按钮图标 + /// + [Fact] + public void TestBindAddIcon() { + _wrapper.SetContextAttribute( AngularConst.BindAddIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试关闭按钮图标 + /// + [Fact] + public void TestCloseIcon() { + _wrapper.SetContextAttribute( UiConst.CloseIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试关闭按钮图标 + /// + [Fact] + public void TestBindCloseIcon() { + _wrapper.SetContextAttribute( AngularConst.BindCloseIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选中标签索引变化事件 + /// + [Fact] + public void TestOnSelectedIndexChange() { + _wrapper.SetContextAttribute( UiConst.OnSelectedIndexChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选中标签变化事件 + /// + [Fact] + public void TestOnSelectChange() { + _wrapper.SetContextAttribute( UiConst.OnSelectChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加标签事件 + /// + [Fact] + public void TestOnAdd() { + _wrapper.SetContextAttribute( UiConst.OnAdd, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试关闭标签事件 + /// + [Fact] + public void TestOnClose() { + _wrapper.SetContextAttribute( UiConst.OnClose, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tabs/TabTagHelperTest.Extend.cs b/test/Util.Ui.NgZorro.Tests/Tabs/TabTagHelperTest.Extend.cs new file mode 100644 index 000000000..7a0d22ffc --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tabs/TabTagHelperTest.Extend.cs @@ -0,0 +1,24 @@ +using System.Text; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tabs { + /// + /// 标签测试 - 扩展测试 + /// + public partial class TabTagHelperTest { + /// + /// 测试延迟加载 + /// + [Fact] + public void TestLazy() { + _wrapper.SetContextAttribute( UiConst.Lazy, true ); + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tabs/TabTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Tabs/TabTagHelperTest.cs new file mode 100644 index 000000000..338511a78 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tabs/TabTagHelperTest.cs @@ -0,0 +1,196 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tabs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Tabs { + /// + /// 标签测试 + /// + public partial class TabTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TabTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TabTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestTitle() { + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 - 多语言 + /// + [Fact] + public void TestTitle_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题 + /// + [Fact] + public void TestBindTitle() { + _wrapper.SetContextAttribute( AngularConst.BindTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否强制渲染 + /// + [Fact] + public void TestForceRender() { + _wrapper.SetContextAttribute( UiConst.ForceRender, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示关闭按钮 + /// + [Fact] + public void TestClosable() { + _wrapper.SetContextAttribute( UiConst.Closable, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试关闭按钮图标 + /// + [Fact] + public void TestCloseIcon() { + _wrapper.SetContextAttribute( UiConst.CloseIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试关闭按钮图标 + /// + [Fact] + public void TestBindCloseIcon() { + _wrapper.SetContextAttribute( AngularConst.BindCloseIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击事件 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试上下文菜单事件 + /// + [Fact] + public void TestOnContextmenu() { + _wrapper.SetContextAttribute( UiConst.OnContextmenu, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选中事件 + /// + [Fact] + public void TestOnSelect() { + _wrapper.SetContextAttribute( UiConst.OnSelect, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试取消选中事件 + /// + [Fact] + public void TestOnDeselect() { + _wrapper.SetContextAttribute( UiConst.OnDeselect, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tags/TagTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/Tags/TagTagHelperTest.Expression.cs new file mode 100644 index 000000000..5925cbd90 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tags/TagTagHelperTest.Expression.cs @@ -0,0 +1,27 @@ +using System.Text; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tags; + +/// +/// 标签测试 - 表达式支持 +/// +public partial class TagTagHelperTest { + /// + /// 测试数据源 + /// + [Fact] + public void TestFor() { + _wrapper.SetExpression( t => t.Gender ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.text){" ); + result.Append( "" ); + result.Append( "{{item.text}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tags/TagTagHelperTest.Extend.cs b/test/Util.Ui.NgZorro.Tests/Tags/TagTagHelperTest.Extend.cs new file mode 100644 index 000000000..286646c52 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tags/TagTagHelperTest.Extend.cs @@ -0,0 +1,346 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tags; + +/// +/// 标签测试 - 扩展 +/// +public partial class TagTagHelperTest { + + #region EnableExtend + + /// + /// 测试启用扩展指令 - 不启用 + /// + [Fact] + public void TestEnableExtend() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, false ); + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region AutoLoad + + /// + /// 测试初始化时是否自动加载数据 + /// + [Fact] + public void TestAutoLoad() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( UiConst.AutoLoad, false ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.text){" ); + result.Append( "" ); + result.Append( "{{item.text}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region QueryParam + + /// + /// 测试查询参数 + /// + [Fact] + public void TestQueryParam() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( UiConst.QueryParam, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.text){" ); + result.Append( "" ); + result.Append( "{{item.text}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region AllSelected + + /// + /// 测试全部选中 + /// + [Fact] + public void TestAllSelected() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( UiConst.AllSelected, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.text){" ); + result.Append( "" ); + result.Append( "{{item.text}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region SelectedText + + /// + /// 测试选中标签文本 + /// + [Fact] + public void TestSelectedText() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( UiConst.SelectedText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.text){" ); + result.Append( "" ); + result.Append( "{{item.text}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region SelectedValue + + /// + /// 测试选中标签文本 + /// + [Fact] + public void TestSelectedValue() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( UiConst.SelectedValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.text){" ); + result.Append( "" ); + result.Append( "{{item.text}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Url + + /// + /// 测试Api地址 + /// + [Fact] + public void TestUrl_1() { + _wrapper.SetContextAttribute( UiConst.Id, "i" ); + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_i.options;track item.text){" ); + result.Append( "" ); + result.Append( "{{item.text}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Api地址 + /// + [Fact] + public void TestBindUrl() { + _wrapper.SetContextAttribute( AngularConst.BindUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.text){" ); + result.Append( "" ); + result.Append( "{{item.text}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Data + + /// + /// 测试数据源 + /// + [Fact] + public void TestData() { + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.text){" ); + result.Append( "" ); + result.Append( "{{item.text}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数据源 + /// + [Fact] + public void TestData_2() { + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + _wrapper.AppendContent( "b" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.text){" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数据源 - 多语言 + /// + [Fact] + public void TestData_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.text){" ); + result.Append( "" ); + result.Append( "{{item.text|i18n}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnLoad + + /// + /// 测试数据加载完成事件 + /// + [Fact] + public void TestOnLoad() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( UiConst.OnLoad, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.text){" ); + result.Append( "" ); + result.Append( "{{item.text}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnSelectedTextChange + + /// + /// 测试选中文本变更事件 + /// + [Fact] + public void TestOnSelectedTextChange() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( UiConst.OnSelectedTextChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.text){" ); + result.Append( "" ); + result.Append( "{{item.text}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnSelectedValueChange + + /// + /// 测试选中值变更事件 + /// + [Fact] + public void TestOnSelectedValueChange() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( UiConst.OnSelectedValueChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.text){" ); + result.Append( "" ); + result.Append( "{{item.text}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnSelectedValueChange + + /// + /// 测试选中值变更事件 + /// + [Fact] + public void TestOnAllSelectedChange() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + _wrapper.SetContextAttribute( UiConst.OnAllSelectedChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.text){" ); + result.Append( "" ); + result.Append( "{{item.text}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Checked + + /// + /// 测试选中状态 + /// + [Fact] + public void TestCheckable_Checked() { + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + _wrapper.SetContextAttribute( UiConst.Mode, "checkable" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "@for(item of x_id.options;track item.text){" ); + result.Append( "" ); + result.Append( "{{item.text}}" ); + result.Append( "" ); + result.Append( "}" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tags/TagTagHelperTest.I18n.cs b/test/Util.Ui.NgZorro.Tests/Tags/TagTagHelperTest.I18n.cs new file mode 100644 index 000000000..626613e2c --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tags/TagTagHelperTest.I18n.cs @@ -0,0 +1,80 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Tags { + /// + /// 标签测试 - 多语言支持 + /// + public partial class TagTagHelperTest { + /// + /// 测试文本 + /// + [Fact] + public void TestText() { + _wrapper.SetContextAttribute( UiConst.Text, "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本 - 支持多语言 + /// + [Fact] + public void TestText_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.Text, "a" ); + var result = new StringBuilder(); + result.Append( "{{'a'|i18n}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Enabled文本 + /// + [Fact] + public void TestTextEnabled() { + _wrapper.SetContextAttribute( UiConst.TextEnabled, true ); + var result = new StringBuilder(); + result.Append( "Enabled" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Enabled文本 - 支持多语言 + /// + [Fact] + public void TestTextEnabled_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextEnabled, true ); + var result = new StringBuilder(); + result.Append( "{{'util.enabled'|i18n}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Not Enabled文本 + /// + [Fact] + public void TestTextNotEnabled() { + _wrapper.SetContextAttribute( UiConst.TextNotEnabled, true ); + var result = new StringBuilder(); + result.Append( "Not Enabled" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Not Enabled文本 - 支持多语言 + /// + [Fact] + public void TestTextNotEnabled_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetContextAttribute( UiConst.TextNotEnabled, true ); + var result = new StringBuilder(); + result.Append( "{{'util.notEnabled'|i18n}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Tags/TagTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Tags/TagTagHelperTest.cs new file mode 100644 index 000000000..f4addb43c --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Tags/TagTagHelperTest.cs @@ -0,0 +1,193 @@ +using System; +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tags; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Tags { + /// + /// 标签测试 + /// + public partial class TagTagHelperTest : IDisposable{ + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TagTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TagTagHelper().ToWrapper(); + Util.Helpers.Id.SetId( "id" ); + } + + /// + /// 测试清理 + /// + public void Dispose() { + Util.Helpers.Id.Reset(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模式 + /// + [Fact] + public void TestMode() { + _wrapper.SetContextAttribute( UiConst.Mode, TagMode.Checkable ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模式 + /// + [Fact] + public void TestBindMode() { + _wrapper.SetContextAttribute( AngularConst.BindMode, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否选中 + /// + [Fact] + public void TestChecked() { + _wrapper.SetContextAttribute( UiConst.Checked, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否选中 + /// + [Fact] + public void TestBindonChecked() { + _wrapper.SetContextAttribute( AngularConst.BindonChecked, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色枚举类型 + /// + [Fact] + public void TestColorType() { + _wrapper.SetContextAttribute( UiConst.ColorType, TagColor.GeekBlue ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色 + /// + [Fact] + public void TestColor() { + _wrapper.SetContextAttribute( UiConst.Color, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色 + /// + [Fact] + public void TestBindColor() { + _wrapper.SetContextAttribute( AngularConst.BindColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试边框 + /// + [Fact] + public void TestBordered() { + _wrapper.SetContextAttribute( UiConst.Bordered, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试点击事件 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试关闭事件 + /// + [Fact] + public void TestOnClose() { + _wrapper.SetContextAttribute( UiConst.OnClose, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选中状态变化事件 + /// + [Fact] + public void TestOnCheckedChange() { + _wrapper.SetContextAttribute( UiConst.OnCheckedChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Templates/TemplateTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Templates/TemplateTagHelperTest.cs new file mode 100644 index 000000000..0a045e453 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Templates/TemplateTagHelperTest.cs @@ -0,0 +1,83 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Templates; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Templates { + /// + /// ng-template模板测试 + /// + public class TemplateTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TemplateTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TemplateTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试延迟加载选项卡 + /// + [Fact] + public void TestTab() { + _wrapper.SetContextAttribute( UiConst.Tab, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模板出口 + /// + [Fact] + public void TestTemplateOutlet() { + _wrapper.SetContextAttribute( AngularConst.NgTemplateOutlet, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模板出口上下文 + /// + [Fact] + public void TestNgTemplateOutletContext() { + _wrapper.SetContextAttribute( AngularConst.NgTemplateOutletContext, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Textareas/TextareaCountTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaCountTagHelperTest.cs new file mode 100644 index 000000000..bf2adc953 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaCountTagHelperTest.cs @@ -0,0 +1,72 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Inputs; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Textareas { + /// + /// 文本域计数测试 + /// + public class TextareaCountTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TextareaCountTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TextareaCountTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数字提示最大值 + /// + [Fact] + public void TestMaxCharacterCount() { + _wrapper.SetContextAttribute( UiConst.MaxCharacterCount, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数字提示最大值计算函数 + /// + [Fact] + public void TestComputeCharacterCount() { + _wrapper.SetContextAttribute( UiConst.ComputeCharacterCount, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Autocomplete.cs b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Autocomplete.cs new file mode 100644 index 000000000..4f3bee646 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Autocomplete.cs @@ -0,0 +1,34 @@ +using System.Text; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Textareas { + /// + /// 文本域测试 - 自动完成相关 + /// + public partial class TextareaTagHelperTest { + /// + /// 测试自动完成 + /// + [Fact] + public void TestAutocomplete() { + _wrapper.SetContextAttribute( UiConst.Autocomplete, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动完成搜索关键字 + /// + [Fact] + public void TestAutocompleteSearchKeyword() { + _wrapper.SetContextAttribute( UiConst.Autocomplete, "a" ); + _wrapper.SetContextAttribute( UiConst.AutocompleteSearchKeyword, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Base.cs b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Base.cs new file mode 100644 index 000000000..dedc4ef20 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Base.cs @@ -0,0 +1,145 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Textareas { + /// + /// 文本域测试 - 基础属性测试 + /// + public partial class TextareaTagHelperTest { + /// + /// 测试原始标识 + /// + [Fact] + public void TestRawId() { + _wrapper.SetContextAttribute( AngularConst.RawId, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试引用变量 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试名称 + /// + [Fact] + public void TestName() { + _wrapper.SetContextAttribute( UiConst.Name, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试样式 + /// + [Fact] + public void TestStyle() { + _wrapper.SetContextAttribute( UiConst.Style, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试样式类 + /// + [Fact] + public void TestClass() { + _wrapper.SetContextAttribute( UiConst.Class, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加其它未配置的属性 + /// + [Fact] + public void TestOutputAttributes() { + _wrapper.SetOutputAttribute( "a", "1" ).SetOutputAttribute( "b", "2" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试*ngIf + /// + [Fact] + public void TestNgIf() { + _wrapper.SetContextAttribute( AngularConst.NgIf, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestNgModel() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestBindNgModel() { + _wrapper.SetContextAttribute( AngularConst.BindNgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表单控件实例 + /// + [Fact] + public void TestFormControl() { + _wrapper.SetContextAttribute( UiConst.FormControl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表单控件名 + /// + [Fact] + public void TestFormControlName() { + _wrapper.SetContextAttribute( UiConst.FormControlName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表单控件名 + /// + [Fact] + public void TestBindFormControlName() { + _wrapper.SetContextAttribute( AngularConst.BindFormControlName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Events.cs b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Events.cs new file mode 100644 index 000000000..3c941f0f3 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Events.cs @@ -0,0 +1,65 @@ +using System.Text; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Textareas { + /// + /// 文本域测试 - 事件测试 + /// + public partial class TextareaTagHelperTest { + /// + /// 测试模型变更事件 + /// + [Fact] + public void TestOnModelChange() { + _wrapper.SetContextAttribute( UiConst.OnModelChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入事件 + /// + [Fact] + public void TestOnInput() { + _wrapper.SetContextAttribute( UiConst.OnInput, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试失去焦点事件 + /// + [Fact] + public void TestOnBlur() { + _wrapper.SetContextAttribute( UiConst.OnBlur, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试回车事件 + /// + [Fact] + public void TestOnKeyupEnter() { + _wrapper.SetContextAttribute( UiConst.OnKeyupEnter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试回车事件 + /// + [Fact] + public void TestOnKeydownEnter() { + _wrapper.SetContextAttribute( UiConst.OnKeydownEnter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Expression.cs new file mode 100644 index 000000000..a1ddff9c5 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.Expression.cs @@ -0,0 +1,31 @@ +using System.Text; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Textareas { + /// + /// 文本域测试 - 表达式解析测试 + /// + public partial class TextareaTagHelperTest { + /// + /// 测试解析表达式属性for + /// + [Fact] + public void TestFor_1() { + _wrapper.SetExpression( t => t.Code ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "code" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_code.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.cs new file mode 100644 index 000000000..8866802c6 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Textareas/TextareaTagHelperTest.cs @@ -0,0 +1,270 @@ +using System; +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Inputs; +using Util.Ui.NgZorro.Components.Mentions; +using Util.Ui.NgZorro.Configs; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Textareas { + /// + /// 文本域测试 + /// + public partial class TextareaTagHelperTest : IDisposable { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TextareaTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TextareaTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 测试清理 + /// + public void Dispose() { + Id.Reset(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试只读 + /// + [Fact] + public void TestReadonly() { + _wrapper.SetContextAttribute( UiConst.Readonly, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置占位符提示 + /// + [Fact] + public void TestPlaceholder() { + _wrapper.SetContextAttribute( UiConst.Placeholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置占位符提示 + /// + [Fact] + public void TestBindPlaceholder() { + _wrapper.SetContextAttribute( AngularConst.BindPlaceholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试行数 + /// + [Fact] + public void TestRows() { + _wrapper.SetContextAttribute( UiConst.Rows, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列数 + /// + [Fact] + public void TestColumns() { + _wrapper.SetContextAttribute( UiConst.Columns, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自适应内容高度 + /// + [Fact] + public void TestAutosize() { + _wrapper.SetContextAttribute( UiConst.Autosize, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试最小行数 + /// + [Fact] + public void TestMinRows() { + _wrapper.SetContextAttribute( UiConst.MinRows, 3 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试最大行数 + /// + [Fact] + public void TestMaxRows() { + _wrapper.SetContextAttribute( UiConst.MaxRows, 3 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试同时设置最大行数和最小行数 + /// + [Fact] + public void TestMaxRows_MinRows() { + _wrapper.SetContextAttribute( UiConst.MinRows, 1 ).SetContextAttribute( UiConst.MaxRows, 2 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试允许清除 + /// + [Fact] + public void TestAllowClear() { + _wrapper.SetContextAttribute( UiConst.AllowClear, true ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "code" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试允许清除 - 全局设置 + /// + [Fact] + public void TestAllowClear_2() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableAllowClear = true } ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "code" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提及触发元素 + /// + [Fact] + public void TestMentionTrigger() { + var mention = new MentionTagHelper().ToWrapper(); + mention.AppendContent( _wrapper ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), mention.GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem_1() { + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 - 创建 nz-input-group + /// + [Fact] + public void TestSpaceItem_2() { + _wrapper.SetContextAttribute( UiConst.AllowClear, true ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "code" ); + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 - 创建 nz-form-item + /// + [Fact] + public void TestSpaceItem_3() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/TimePickers/TimePickerTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/TimePickers/TimePickerTagHelperTest.Expression.cs new file mode 100644 index 000000000..9a872ac76 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/TimePickers/TimePickerTagHelperTest.Expression.cs @@ -0,0 +1,30 @@ +using System.Text; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.TimePickers { + /// + /// 时间选择测试 - 表达式解析测试 + /// + public partial class TimePickerTagHelperTest { + /// + /// 测试解析表达式属性for + /// + [Fact] + public void TestFor_1() { + _wrapper.SetExpression( t => t.Birthday ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "出生日期" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_birthday.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/TimePickers/TimePickerTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/TimePickers/TimePickerTagHelperTest.cs new file mode 100644 index 000000000..01fb19f78 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/TimePickers/TimePickerTagHelperTest.cs @@ -0,0 +1,391 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.TimePickers; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.TimePickers { + /// + /// 时间选择测试 + /// + public partial class TimePickerTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TimePickerTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TimePickerTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试底部自定义内容 + /// + [Fact] + public void TestAddOn() { + _wrapper.SetContextAttribute( UiConst.AddOn, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试允许清除 + /// + [Fact] + public void TestAllowEmpty() { + _wrapper.SetContextAttribute( UiConst.AllowEmpty, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试清除按钮显示文本 + /// + [Fact] + public void TestClearText() { + _wrapper.SetContextAttribute( UiConst.ClearText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试清除按钮显示文本 + /// + [Fact] + public void TestBindClearText() { + _wrapper.SetContextAttribute( AngularConst.BindClearText, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动聚焦 + /// + [Fact] + public void TestAutoFocus() { + _wrapper.SetContextAttribute( UiConst.AutoFocus, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试默认值 + /// + [Fact] + public void TestDefaultOpenValue() { + _wrapper.SetContextAttribute( UiConst.DefaultOpenValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试默认值 + /// + [Fact] + public void TestBindDefaultOpenValue() { + _wrapper.SetContextAttribute( AngularConst.BindDefaultOpenValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试不可选择小时函数 + /// + [Fact] + public void TestDisabledHours() { + _wrapper.SetContextAttribute( UiConst.DisabledHours, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试不可选择分钟函数 + /// + [Fact] + public void TestDisabledMinutes() { + _wrapper.SetContextAttribute( UiConst.DisabledMinutes, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试不可选择秒函数 + /// + [Fact] + public void TestDisabledSeconds() { + _wrapper.SetContextAttribute( UiConst.DisabledSeconds, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试时间格式 + /// + [Fact] + public void TestFormat() { + _wrapper.SetContextAttribute( UiConst.Format, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试时间格式 + /// + [Fact] + public void TestBindFormat() { + _wrapper.SetContextAttribute( AngularConst.BindFormat, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试隐藏禁止选择的选项 + /// + [Fact] + public void TestHideDisabledOptions() { + _wrapper.SetContextAttribute( UiConst.HideDisabledOptions, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试小时选项间隔 + /// + [Fact] + public void TestHourStep() { + _wrapper.SetContextAttribute( UiConst.HourStep, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试分钟选项间隔 + /// + [Fact] + public void TestMinuteStep() { + _wrapper.SetContextAttribute( UiConst.MinuteStep, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试秒选项间隔 + /// + [Fact] + public void TestSecondStep() { + _wrapper.SetContextAttribute( UiConst.SecondStep, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试面板是否打开 + /// + [Fact] + public void TestOpen() { + _wrapper.SetContextAttribute( UiConst.Open, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试面板是否打开 + /// + [Fact] + public void TestBindonOpen() { + _wrapper.SetContextAttribute( AngularConst.BindonOpen, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试占位提示 + /// + [Fact] + public void TestPlaceholder() { + _wrapper.SetContextAttribute( UiConst.Placeholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试占位提示 + /// + [Fact] + public void TestBindPlaceholder() { + _wrapper.SetContextAttribute( AngularConst.BindPlaceholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试弹出层样式类名 + /// + [Fact] + public void TestPopupClassName() { + _wrapper.SetContextAttribute( UiConst.PopupClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试弹出层样式类名 + /// + [Fact] + public void TestBindPopupClassName() { + _wrapper.SetContextAttribute( AngularConst.BindPopupClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试12小时制 + /// + [Fact] + public void TestUse12Hours() { + _wrapper.SetContextAttribute( UiConst.Use12Hours, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀图标 + /// + [Fact] + public void TestSuffixIcon() { + _wrapper.SetContextAttribute( UiConst.SuffixIcon, AntDesignIcon.AccountBook ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试后缀图标 + /// + [Fact] + public void TestBindSuffixIcon() { + _wrapper.SetContextAttribute( AngularConst.BindSuffixIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型变更事件 + /// + [Fact] + public void TestOnModelChange() { + _wrapper.SetContextAttribute( UiConst.OnModelChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试打开关闭面板事件 + /// + [Fact] + public void TestOnOpenChange() { + _wrapper.SetContextAttribute( UiConst.OnOpenChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Timelines/TimelineItemTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Timelines/TimelineItemTagHelperTest.cs new file mode 100644 index 000000000..9bd142d18 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Timelines/TimelineItemTagHelperTest.cs @@ -0,0 +1,150 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Timelines; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Timelines { + /// + /// 时间轴节点测试 + /// + public class TimelineItemTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TimelineItemTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TimelineItemTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色 + /// + [Fact] + public void TestColorType() { + _wrapper.SetContextAttribute( UiConst.ColorType, AntDesignColor.Red ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色 + /// + [Fact] + public void TestColor() { + _wrapper.SetContextAttribute( UiConst.Color, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试颜色 + /// + [Fact] + public void TestBindColor() { + _wrapper.SetContextAttribute( AngularConst.BindColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义时间轴点 + /// + [Fact] + public void TestDot() { + _wrapper.SetContextAttribute( UiConst.Dot, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义节点位置 + /// + [Fact] + public void TestPosition() { + _wrapper.SetContextAttribute( UiConst.Position, TimelineItemPosition.Left ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义节点位置 + /// + [Fact] + public void TestBindPosition() { + _wrapper.SetContextAttribute( AngularConst.BindPosition, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置标签 + /// + [Fact] + public void TestLabel() { + _wrapper.SetContextAttribute( UiConst.Label, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置标签 + /// + [Fact] + public void TestBindLabel() { + _wrapper.SetContextAttribute( AngularConst.BindLabel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Timelines/TimelineTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Timelines/TimelineTagHelperTest.cs new file mode 100644 index 000000000..184b16522 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Timelines/TimelineTagHelperTest.cs @@ -0,0 +1,139 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Timelines; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Timelines { + /// + /// 时间轴测试 + /// + public class TimelineTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TimelineTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TimelineTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试幽灵节点 + /// + [Fact] + public void TestPending() { + _wrapper.SetContextAttribute( UiConst.Pending, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试幽灵节点 + /// + [Fact] + public void TestBindPending() { + _wrapper.SetContextAttribute( AngularConst.BindPending, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试幽灵节点时间图点 + /// + [Fact] + public void TestPendingDot() { + _wrapper.SetContextAttribute( UiConst.PendingDot, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试幽灵节点时间图点 + /// + [Fact] + public void TestBindPendingDot() { + _wrapper.SetContextAttribute( AngularConst.BindPendingDot, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否倒序排列 + /// + [Fact] + public void TestReverse() { + _wrapper.SetContextAttribute( UiConst.Reverse, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模式 + /// + [Fact] + public void TestMode() { + _wrapper.SetContextAttribute( UiConst.Mode, TimelineMode.Custom ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模式 + /// + [Fact] + public void TestBindMode() { + _wrapper.SetContextAttribute( AngularConst.BindMode, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Transfers/TransferTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Transfers/TransferTagHelperTest.cs new file mode 100644 index 000000000..cbcef925e --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Transfers/TransferTagHelperTest.cs @@ -0,0 +1,325 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Transfers; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Transfers { + /// + /// 穿梭框测试 + /// + public class TransferTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TransferTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TransferTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数据源 + /// + [Fact] + public void TestDatasource() { + _wrapper.SetContextAttribute( UiConst.Datasource, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试标题集合 + /// + [Fact] + public void TestTitles() { + _wrapper.SetContextAttribute( UiConst.Titles, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试操作按钮标题集合 + /// + [Fact] + public void TestOperations() { + _wrapper.SetContextAttribute( UiConst.Operations, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试样式 + /// + [Fact] + public void TestListStyle() { + _wrapper.SetContextAttribute( UiConst.ListStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单数项目单位 + /// + [Fact] + public void TestItemUnit() { + _wrapper.SetContextAttribute( UiConst.ItemUnit, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单数项目单位 + /// + [Fact] + public void TestBindItemUnit() { + _wrapper.SetContextAttribute( AngularConst.BindItemUnit, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复数项目单位 + /// + [Fact] + public void TestItemsUnit() { + _wrapper.SetContextAttribute( UiConst.ItemsUnit, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复数项目单位 + /// + [Fact] + public void TestBindItemsUnit() { + _wrapper.SetContextAttribute( AngularConst.BindItemsUnit, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试渲染列表 + /// + [Fact] + public void TestRenderList() { + _wrapper.SetContextAttribute( UiConst.RenderList, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试行渲染模板 + /// + [Fact] + public void TestRender() { + _wrapper.SetContextAttribute( UiConst.Render, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试底部渲染模板 + /// + [Fact] + public void TestFooter() { + _wrapper.SetContextAttribute( UiConst.Footer, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示搜索 + /// + [Fact] + public void TestShowSearch() { + _wrapper.SetContextAttribute( UiConst.ShowSearch, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试过滤项函数 + /// + [Fact] + public void TestFilterOption() { + _wrapper.SetContextAttribute( UiConst.FilterOption, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试搜索框占位提示 + /// + [Fact] + public void TestSearchPlaceholder() { + _wrapper.SetContextAttribute( UiConst.SearchPlaceholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试搜索框占位提示 + /// + [Fact] + public void TestBindSearchPlaceholder() { + _wrapper.SetContextAttribute( AngularConst.BindSearchPlaceholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试空列表默认内容 + /// + [Fact] + public void TestNotFoundContent() { + _wrapper.SetContextAttribute( UiConst.NotFoundContent, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试空列表默认内容 + /// + [Fact] + public void TestBindNotFoundContent() { + _wrapper.SetContextAttribute( AngularConst.BindNotFoundContent, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试可移动项函数 + /// + [Fact] + public void TestCanMove() { + _wrapper.SetContextAttribute( UiConst.CanMove, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选中项键列表 + /// + [Fact] + public void TestSelectedKeys() { + _wrapper.SetContextAttribute( UiConst.SelectedKeys, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试目标项键列表 + /// + [Fact] + public void TestTargetKeys() { + _wrapper.SetContextAttribute( UiConst.TargetKeys, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试转移改变事件 + /// + [Fact] + public void TestOnChange() { + _wrapper.SetContextAttribute( UiConst.OnChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试搜索改变事件 + /// + [Fact] + public void TestOnSearchChange() { + _wrapper.SetContextAttribute( UiConst.OnSearchChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选中状态改变事件 + /// + [Fact] + public void TestOnSelectChange() { + _wrapper.SetContextAttribute( UiConst.OnSelectChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/TreeSelects/TreeSelectTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/TreeSelects/TreeSelectTagHelperTest.Expression.cs new file mode 100644 index 000000000..38b5ead77 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/TreeSelects/TreeSelectTagHelperTest.Expression.cs @@ -0,0 +1,56 @@ +using System.Text; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.TreeSelects { + /// + /// 树选择测试 - 表达式解析测试 + /// + public partial class TreeSelectTagHelperTest { + /// + /// 测试解析表达式属性for + /// + [Fact] + public void TestFor_1() { + _wrapper.SetExpression( t => t.Code ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "code" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_code.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试解析表达式属性for - url加载 + /// + [Fact] + public void TestFor_2() { + _wrapper.SetExpression( t => t.Code ); + _wrapper.SetContextAttribute( UiConst.Url,"a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "code" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_code.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/TreeSelects/TreeSelectTagHelperTest.Extend.cs b/test/Util.Ui.NgZorro.Tests/TreeSelects/TreeSelectTagHelperTest.Extend.cs new file mode 100644 index 000000000..224a0af3a --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/TreeSelects/TreeSelectTagHelperTest.Extend.cs @@ -0,0 +1,434 @@ +using System.Text; +using Util.Applications.Trees; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Forms; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.TreeSelects { + /// + /// 树选择测试 - 指令扩展 + /// + public partial class TreeSelectTagHelperTest { + + #region EnableExtend + + /// + /// 测试启用扩展指令 + /// + [Fact] + public void TestEnableExtend_1() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用扩展指令 - 不启用 + /// + [Fact] + public void TestEnableExtend_2() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, false ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region AutoLoad + + /// + /// 测试初始化时是否自动加载数据 + /// + [Fact] + public void TestAutoLoad() { + _wrapper.SetContextAttribute( UiConst.AutoLoad, false ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region QueryParam + + /// + /// 测试查询参数 + /// + [Fact] + public void TestQueryParam() { + _wrapper.SetContextAttribute( UiConst.QueryParam, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Sort + + /// + /// 测试排序条件 + /// + [Fact] + public void TestSort() { + _wrapper.SetContextAttribute( UiConst.Sort, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试排序条件 + /// + [Fact] + public void TestBindSort() { + _wrapper.SetContextAttribute( AngularConst.BindSort, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Url + + /// + /// 测试Api地址 + /// + [Fact] + public void TestUrl_1() { + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Api地址 - 覆盖默认属性 + /// + [Fact] + public void TestUrl_2() { + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + _wrapper.SetContextAttribute( UiConst.Nodes, "b" ); + _wrapper.SetContextAttribute( UiConst.OnExpandChange, "c" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Api地址 + /// + [Fact] + public void TestBindUrl() { + _wrapper.SetContextAttribute( AngularConst.BindUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region LoadUrl + + /// + /// 测试加载地址 + /// + [Fact] + public void TestLoadUrl() { + _wrapper.SetContextAttribute( UiConst.LoadUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载地址 + /// + [Fact] + public void TestBindLoadUrl() { + _wrapper.SetContextAttribute( AngularConst.BindLoadUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region QueryUrl + + /// + /// 测试查询地址 + /// + [Fact] + public void TestQueryUrl() { + _wrapper.SetContextAttribute( UiConst.QueryUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试查询地址 + /// + [Fact] + public void TestBindQueryUrl() { + _wrapper.SetContextAttribute( AngularConst.BindQueryUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region LoadChildrenUrl + + /// + /// 测试加载下级节点地址 + /// + [Fact] + public void TestLoadChildrenUrl() { + _wrapper.SetContextAttribute( UiConst.LoadChildrenUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载下级节点地址 + /// + [Fact] + public void TestBindLoadChildrenUrl() { + _wrapper.SetContextAttribute( AngularConst.BindLoadChildrenUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region ExpandForRootAsync + + /// + /// 测试根节点异步加载模式是否展开子节点 + /// + [Fact] + public void TestExpandForRootAsync() { + _wrapper.SetContextAttribute( UiConst.ExpandForRootAsync, false ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region LoadMode + + /// + /// 测试加载模式 - 同步 + /// + [Fact] + public void TestLoadMode_1() { + _wrapper.SetContextAttribute( UiConst.LoadMode, LoadMode.Sync ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载模式 - 异步 + /// + [Fact] + public void TestLoadMode_2() { + _wrapper.SetContextAttribute( UiConst.LoadMode, LoadMode.Async ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载模式 - 根节点异步 + /// + [Fact] + public void TestLoadMode_3() { + _wrapper.SetContextAttribute( UiConst.LoadMode, LoadMode.RootAsync ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Required + + /// + /// 测试必填项验证 + /// + [Fact] + public void TestRequired() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试必填项消息 + /// + [Fact] + public void TestRequiredMessage() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( UiConst.RequiredMessage, "a" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "model" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnLoadChildrenBefore + + /// + /// 测试子节点加载前事件 + /// + [Fact] + public void TestOnLoadChildrenBefore() { + _wrapper.SetContextAttribute( UiConst.OnLoadChildrenBefore, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnLoadChildren + + /// + /// 测试子节点加载完成事件 + /// + [Fact] + public void TestOnLoadChildren() { + _wrapper.SetContextAttribute( UiConst.OnLoadChildren, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnExpand + + /// + /// 测试节点展开事件 + /// + [Fact] + public void TestOnExpand() { + _wrapper.SetContextAttribute( UiConst.OnExpand, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnCollapse + + /// + /// 测试节点折叠事件 + /// + [Fact] + public void TestOnCollapse() { + _wrapper.SetContextAttribute( UiConst.OnCollapse, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region AutoNzFor + + /// + /// 测试自动设置nzFor + /// + [Fact] + public void TestAutoNzFor() { + var form = new FormTagHelper().ToWrapper(); + + var formItem = new FormItemTagHelper().ToWrapper(); + form.AppendContent( formItem ); + + var formLabel = new FormLabelTagHelper().ToWrapper(); + formLabel.AppendContent( "a" ); + formItem.AppendContent( formLabel ); + + _wrapper.SetContextAttribute( UiConst.AutoNzFor, true ); + var formControl = new FormControlTagHelper().ToWrapper(); + formControl.AppendContent( _wrapper ); + formItem.AppendContent( formControl ); + + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), form.GetResult() ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/TreeSelects/TreeSelectTagHelperTest.TableEdit.cs b/test/Util.Ui.NgZorro.Tests/TreeSelects/TreeSelectTagHelperTest.TableEdit.cs new file mode 100644 index 000000000..2471ecbed --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/TreeSelects/TreeSelectTagHelperTest.TableEdit.cs @@ -0,0 +1,59 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.TreeSelects { + /// + /// 树选择测试 - 表格编辑 + /// + public partial class TreeSelectTagHelperTest { + /// + /// 测试启用编辑模式 + /// + [Fact] + public void TestIsEdit_1() { + //创建表格 + var table = new TableTagHelper().ToWrapper(); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.IsEdit, true ); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Column, "b" ); + table.AppendContent( column ); + + //设置宽度 + _wrapper.SetContextAttribute( UiConst.Width, "120" ); + + //添加组件 + column.AppendContent( _wrapper ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{row.b}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), table.GetResult() ); + } + } +} + diff --git a/test/Util.Ui.NgZorro.Tests/TreeSelects/TreeSelectTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/TreeSelects/TreeSelectTagHelperTest.cs new file mode 100644 index 000000000..6384e2e14 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/TreeSelects/TreeSelectTagHelperTest.cs @@ -0,0 +1,601 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.TreeSelects; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.TreeSelects { + /// + /// 树选择测试 + /// + public partial class TreeSelectTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TreeSelectTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TreeSelectTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加标识 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入框标识 + /// + [Fact] + public void TestNzId() { + _wrapper.SetContextAttribute( UiConst.NzId, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试输入框标识 + /// + [Fact] + public void TestBindNzId() { + _wrapper.SetContextAttribute( AngularConst.BindNzId, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestNgModel() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试允许清除 + /// + [Fact] + public void TestAllowClear() { + _wrapper.SetContextAttribute( UiConst.AllowClear, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试占位提示 + /// + [Fact] + public void TestPlaceholder() { + _wrapper.SetContextAttribute( UiConst.Placeholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试占位提示 + /// + [Fact] + public void TestBindPlaceholder() { + _wrapper.SetContextAttribute( AngularConst.BindPlaceholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示图标 + /// + [Fact] + public void TestShowIcon() { + _wrapper.SetContextAttribute( UiConst.ShowIcon, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示搜索 + /// + [Fact] + public void TestShowSearch() { + _wrapper.SetContextAttribute( UiConst.ShowSearch, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试空列表默认内容 + /// + [Fact] + public void TestNotFoundContent() { + _wrapper.SetContextAttribute( UiConst.NotFoundContent, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试空列表默认内容 + /// + [Fact] + public void TestBindNotFoundContent() { + _wrapper.SetContextAttribute( AngularConst.BindNotFoundContent, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单和选择器同宽 + /// + [Fact] + public void TestDropdownMatchSelectWidth() { + _wrapper.SetContextAttribute( UiConst.DropdownMatchSelectWidth, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单样式 + /// + [Fact] + public void TestDropdownStyle() { + _wrapper.SetContextAttribute( UiConst.DropdownStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单样式类名 + /// + [Fact] + public void TestDropdownClassName() { + _wrapper.SetContextAttribute( UiConst.DropdownClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下拉菜单样式类名 + /// + [Fact] + public void TestBindDropdownClassName() { + _wrapper.SetContextAttribute( AngularConst.BindDropdownClassName, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否支持多选 + /// + [Fact] + public void TestMultiple() { + _wrapper.SetContextAttribute( UiConst.Multiple, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试隐藏未匹配节点 + /// + [Fact] + public void TestHideUnmatched() { + _wrapper.SetContextAttribute( UiConst.HideUnmatched, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选择框大小 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, InputSize.Large ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选择框大小 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选择框弹出位置 + /// + [Fact] + public void TestPlacement() { + _wrapper.SetContextAttribute( UiConst.Placement, TreeSelectPlacement.BottomRight ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选择框弹出位置 + /// + [Fact] + public void TestBindPlacement() { + _wrapper.SetContextAttribute( AngularConst.BindPlacement, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestStatus() { + _wrapper.SetContextAttribute( UiConst.Status, FormControlStatus.Error ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试状态 + /// + [Fact] + public void TestBindStatus() { + _wrapper.SetContextAttribute( AngularConst.BindStatus, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试浮层是否应带有背景板 + /// + [Fact] + public void TestBackdrop() { + _wrapper.SetContextAttribute( UiConst.Backdrop, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试节点前是否添加复选框 + /// + [Fact] + public void TestCheckable() { + _wrapper.SetContextAttribute( UiConst.Checkable, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试严格勾选 + /// + [Fact] + public void TestCheckStrictly() { + _wrapper.SetContextAttribute( UiConst.CheckStrictly, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示展开图标 + /// + [Fact] + public void TestShowExpand() { + _wrapper.SetContextAttribute( UiConst.ShowExpand, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示连接线 + /// + [Fact] + public void TestShowLine() { + _wrapper.SetContextAttribute( UiConst.ShowLine, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否异步加载 + /// + [Fact] + public void TestAsyncData() { + _wrapper.SetContextAttribute( UiConst.AsyncData, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试节点数据 + /// + [Fact] + public void TestNodes() { + _wrapper.SetContextAttribute( UiConst.Nodes, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试默认展开所有节点 + /// + [Fact] + public void TestExpandAll() { + _wrapper.SetContextAttribute( UiConst.ExpandAll, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试默认展开节点的键集合 + /// + [Fact] + public void TestExpandedKeys() { + _wrapper.SetContextAttribute( UiConst.ExpandedKeys, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试显示函数 + /// + [Fact] + public void TestDisplayWith() { + _wrapper.SetContextAttribute( UiConst.DisplayWith, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试最大标签数量 + /// + [Fact] + public void TestMaxTagCount() { + _wrapper.SetContextAttribute( UiConst.MaxTagCount, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试最大标签占位符 + /// + [Fact] + public void TestMaxTagPlaceholder() { + _wrapper.SetContextAttribute( UiConst.MaxTagPlaceholder, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义模板 + /// + [Fact] + public void TestTreeTemplate() { + _wrapper.SetContextAttribute( UiConst.TreeTemplate, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚拟高度 + /// + [Fact] + public void TestVirtualHeight() { + _wrapper.SetContextAttribute( UiConst.VirtualHeight, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚拟高度 + /// + [Fact] + public void TestBindVirtualHeight() { + _wrapper.SetContextAttribute( AngularConst.BindVirtualHeight, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚拟滚动列高 + /// + [Fact] + public void TestVirtualItemSize() { + _wrapper.SetContextAttribute( UiConst.VirtualItemSize, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚拟滚动缓冲区最大高度 + /// + [Fact] + public void TestVirtualMaxBufferPx() { + _wrapper.SetContextAttribute( UiConst.VirtualMaxBufferPx, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚拟滚动缓冲区最小高度 + /// + [Fact] + public void TestVirtualMinBufferPx() { + _wrapper.SetContextAttribute( UiConst.VirtualMinBufferPx, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽度 - 像素 + /// + [Fact] + public void TestWidth_1() { + _wrapper.SetContextAttribute( UiConst.Width, "120" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽度 - 百分比 + /// + [Fact] + public void TestWidth_2() { + _wrapper.SetContextAttribute( UiConst.Width, "100%" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试宽度 - 添加到已存在的style + /// + [Fact] + public void TestWidth_3() { + _wrapper.SetContextAttribute( UiConst.Style, "height:100px" ); + _wrapper.SetContextAttribute( UiConst.Width, "100%" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型变更事件 + /// + [Fact] + public void TestOnModelChange() { + _wrapper.SetContextAttribute( UiConst.OnModelChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试展开收缩节点事件 + /// + [Fact] + public void TestOnOpenChange() { + _wrapper.SetContextAttribute( UiConst.OnExpandChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/TreeTables/TreeTableColumnTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/TreeTables/TreeTableColumnTagHelperTest.cs new file mode 100644 index 000000000..730064d6d --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/TreeTables/TreeTableColumnTagHelperTest.cs @@ -0,0 +1,209 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.TreeTables { + /// + /// 树形表格单元格测试 + /// + public class TreeTableColumnTagHelperTest { + + #region 测试初始化 + + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TreeTableColumnTagHelperTest( ITestOutputHelper output ) { + Id.SetId( "id" ); + _output = output; + _wrapper = new TableColumnTagHelper().ToWrapper(); + } + + #endregion + + #region 辅助操作 + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 添加启用扩展的表格起始标签 + /// + /// 结果 + private void AppendExtendTable( StringBuilder result ) { + result.Append( "" ); + } + + /// + /// 添加表格总行数模板标签 + /// + /// 结果 + private void AppendTotalTemplate( StringBuilder result ) { + result.Append( "" ); + result.Append( "{{ 'util.tableTotalTemplate'|i18n:{start:range[0],end:range[1],total:total} }}" ); + result.Append( "" ); + } + + #endregion + + #region Checkbox + + /// + /// 测试设置复选框 + /// + [Fact] + public void TestCheckbox() { + _wrapper.SetItem( new TableShareConfig( "id" ) { IsEnableExtend = true,IsShowCheckbox = true,IsTreeTable = true } ); + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Radio + + /// + /// 测试设置单选框 + /// + [Fact] + public void TestRadio_1() { + _wrapper.SetItem( new TableShareConfig( "id" ) { IsEnableExtend = true, IsShowRadio = true, IsTreeTable = true } ); + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置单选框 - 如果已设置复选框,则单选框不生效 + /// + [Fact] + public void TestRadio_2() { + _wrapper.SetItem( new TableShareConfig( "id" ) { IsEnableExtend = true, IsShowCheckbox = true, IsShowRadio = true, IsTreeTable = true } ); + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region CheckboxLeft + + /// + /// 测试固定复选框 + /// + [Fact] + public void TestCheckboxLeft() { + _wrapper.SetItem( new TableShareConfig( "id" ) { IsEnableExtend = true, IsShowCheckbox = true, IsCheckboxLeft = "a", IsTreeTable = true } ); + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region RadioLeft + + /// + /// 测试固定单选框 + /// + [Fact] + public void TestRadioLeft() { + _wrapper.SetItem( new TableShareConfig( "id" ) { IsEnableExtend = true, IsShowRadio = true,IsRadioLeft = "a",IsTreeTable = true } ); + _wrapper.SetContextAttribute( UiConst.Column, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/TreeTables/TreeTableHeadColumnTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/TreeTables/TreeTableHeadColumnTagHelperTest.cs new file mode 100644 index 000000000..9262c37a0 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/TreeTables/TreeTableHeadColumnTagHelperTest.cs @@ -0,0 +1,182 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.NgZorro.Components.Tables.Configs; +using Util.Ui.NgZorro.Configs; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.TreeTables { + /// + /// 树形表头单元格测试 + /// + public class TreeTableHeadColumnTagHelperTest { + + #region 测试初始化 + + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TreeTableHeadColumnTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TableHeadColumnTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + #endregion + + #region Checkbox + + /// + /// 测试表头复选框 + /// + [Fact] + public void TestCheckbox() { + _wrapper.SetItem( new TableShareConfig( "id" ) { IsTreeTable = true, IsShowCheckbox = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表头复选框 - 多语言 + /// + [Fact] + public void TestCheckbox_I18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetItem( new TableShareConfig( "id" ) { IsTreeTable = true, IsShowCheckbox = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Radio + + /// + /// 测试表头单选框 + /// + [Fact] + public void TestRadio_1() { + _wrapper.SetItem( new TableShareConfig( "id" ) { IsTreeTable = true, IsShowRadio = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表头单选框 - 支持多语言 + /// + [Fact] + public void TestRadio_i18n() { + NgZorroOptionsService.SetOptions( new NgZorroOptions { EnableI18n = true } ); + _wrapper.SetItem( new TableShareConfig( "id" ) { IsTreeTable = true, IsShowRadio = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "{{'a'|i18n}}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试表头单选框 - 已设置复选框 + /// + [Fact] + public void TestRadio_2() { + _wrapper.SetItem( new TableShareConfig( "id" ) { IsTreeTable = true, IsShowCheckbox = true, IsShowRadio = true } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region CheckboxLeft + + /// + /// 测试表头复选框 + /// + [Fact] + public void TestCheckboxLeft() { + _wrapper.SetItem( new TableShareConfig( "id" ) { IsTreeTable = true, IsShowCheckbox = true, IsCheckboxLeft = "a" } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region RadioLeft + + /// + /// 测试表头单选框 + /// + [Fact] + public void TestRadioLeft() { + _wrapper.SetItem( new TableShareConfig( "id" ) { IsTreeTable = true, IsShowRadio = true, IsRadioLeft = "a" } ); + _wrapper.SetContextAttribute( UiConst.Title, "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/TreeTables/TreeTableTagHelperTest.TableSettings.cs b/test/Util.Ui.NgZorro.Tests/TreeTables/TreeTableTagHelperTest.TableSettings.cs new file mode 100644 index 000000000..dd39f4be2 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/TreeTables/TreeTableTagHelperTest.TableSettings.cs @@ -0,0 +1,1815 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.TreeTables; +/// +/// 树形表格测试 - 表格设置测试 +/// +public partial class TreeTableTagHelperTest { + + #region EnableCustomColumn + + /// + /// 测试启用自定义列 - 1列 + /// + [Fact] + public void TestEnableCustomColumn_1() { + //设置url属性 + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 2列 + /// + [Fact] + public void TestEnableCustomColumn_2() { + //设置url属性 + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //创建列2 + var column2 = new TableColumnTagHelper().ToWrapper(); + column2.SetContextAttribute( UiConst.Title, "c" ); + column2.AppendContent( "d" ); + _wrapper.AppendContent( column2 ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "c" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "d" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 1列 - 创建完整结构 + /// + [Fact] + public void TestEnableCustomColumn_3() { + //设置url属性 + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 2列 - 创建完整结构 + /// + [Fact] + public void TestEnableCustomColumn_4() { + //设置url属性 + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表头单元格2 + var th2 = new TableHeadColumnTagHelper().ToWrapper(); + th2.SetContextAttribute( UiConst.Title, "c" ); + headRow.AppendContent( th2 ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //创建列2 + var column2 = new TableColumnTagHelper().ToWrapper(); + column2.AppendContent( "d" ); + row.AppendContent( column2 ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "c" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "d" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 设置复选框 + /// + [Fact] + public void TestEnableCustomColumn_5() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 设置复选框 - 创建完整结构 + /// + [Fact] + public void TestEnableCustomColumn_6() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 设置单选框 + /// + [Fact] + public void TestEnableCustomColumn_7() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 设置单选框 - 创建完整结构 + /// + [Fact] + public void TestEnableCustomColumn_8() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 固定列 + /// + [Fact] + public void TestEnableCustomColumn_9() { + //设置url属性 + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Left, "10px" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 固定列 - 创建完整结构 + /// + [Fact] + public void TestEnableCustomColumn_10() { + //设置url属性 + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + th.SetContextAttribute( UiConst.Left, "10px" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 设置复选框 - 固定列 + /// + [Fact] + public void TestEnableCustomColumn_11() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Left, "10px" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 设置复选框 - 固定列 - 创建完整结构 + /// + [Fact] + public void TestEnableCustomColumn_12() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + th.SetContextAttribute( UiConst.Left, "10px" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 设置单选框 - 固定列 + /// + [Fact] + public void TestEnableCustomColumn_13() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Left, "10px" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用自定义列 - 设置单选框 - 固定列 - 创建完整结构 + /// + [Fact] + public void TestEnableCustomColumn_14() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + th.SetContextAttribute( UiConst.Left, "10px" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region EnableResizable + + /// + /// 测试启用拖动调整列宽 + /// + [Fact] + public void TestEnableResizable_1() { + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableResizable, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用拖动调整列宽 - 创建完整结构 + /// + [Fact] + public void TestEnableResizable_2() { + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableResizable, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用拖动调整列宽 - 设置复选框 + /// + [Fact] + public void TestEnableResizable_3() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableResizable, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用拖动调整列宽 - 设置复选框 - 创建完整结构 + /// + [Fact] + public void TestEnableResizable_4() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableResizable, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用拖动调整列宽 - 设置单选框 + /// + [Fact] + public void TestEnableResizable_5() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableResizable, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用拖动调整列宽 - 设置单选框 - 创建完整结构 + /// + [Fact] + public void TestEnableResizable_6() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableResizable, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region EnableFixedColumn + + /// + /// 测试固定列 + /// + [Fact] + public void TestEnableFixedColumn_1() { + //设置url属性 + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Left, true ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定列 - 创建完整结构 + /// + [Fact] + public void TestEnableFixedColumn_2() { + //设置url属性 + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + th.SetContextAttribute( UiConst.Left, true ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定列 - 设置复选框 + /// + [Fact] + public void TestEnableFixedColumn_3() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Left, true ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定列 - 设置复选框 - 创建完整结构 + /// + [Fact] + public void TestEnableFixedColumn_4() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + th.SetContextAttribute( UiConst.Left, true ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定列 - 设置单选框 + /// + [Fact] + public void TestEnableFixedColumn_5() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.SetContextAttribute( UiConst.Left, true ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试固定列 - 设置单选框 - 创建完整结构 + /// + [Fact] + public void TestEnableFixedColumn_6() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.Key, "a" ); + _wrapper.SetContextAttribute( UiConst.EnableCustomColumn, true ); + _wrapper.SetContextAttribute( UiConst.EnableFixedColumn, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.SetContextAttribute( UiConst.Title, "a" ); + th.SetContextAttribute( UiConst.Left, true ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion +} diff --git a/test/Util.Ui.NgZorro.Tests/TreeTables/TreeTableTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/TreeTables/TreeTableTagHelperTest.cs new file mode 100644 index 000000000..6ea04130f --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/TreeTables/TreeTableTagHelperTest.cs @@ -0,0 +1,1329 @@ +using System.Text; +using Util.Applications.Trees; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Tables; +using Util.Ui.NgZorro.Components.TreeTables; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.TreeTables { + /// + /// 树形表格测试 + /// + public partial class TreeTableTagHelperTest { + + #region 测试初始化 + + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TreeTableTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TreeTableTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 添加表格总行数模板标签 + /// + /// 结果 + private void AppendTotalTemplate( StringBuilder result ) { + result.Append( "" ); + result.Append( "{{ 'util.tableTotalTemplate'|i18n:{start:range[0],end:range[1],total:total} }}" ); + result.Append( "" ); + } + + #endregion + + #region ExpandAll + + /// + /// 测试展开 + /// + [Fact] + public void TestExpandAll() { + _wrapper.SetContextAttribute( UiConst.ExpandAll, true ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region ExpandForRootAsync + + /// + /// 测试根节点异步加载模式是否展开子节点 + /// + [Fact] + public void TestExpandForRootAsync() { + _wrapper.SetContextAttribute( UiConst.ExpandForRootAsync, false ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region LoadMode + + /// + /// 测试加载模式 + /// + [Fact] + public void TestLoadMode() { + _wrapper.SetContextAttribute( UiConst.LoadMode, LoadMode.RootAsync ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载模式 - 同步加载时关闭分页 + /// + [Fact] + public void TestLoadMode_Sync() { + _wrapper.SetContextAttribute( UiConst.LoadMode, LoadMode.Sync ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region CheckLeafOnly + + /// + /// 测试只能选择叶节点 + /// + [Fact] + public void TestCheckLeafOnly() { + _wrapper.SetContextAttribute( UiConst.CheckLeafOnly, "true" ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region AutoCreate + + /// + /// 测试自动创建嵌套结构 - 仅添加一个列,将自动创建其它嵌套结构 + /// + [Fact] + public void TestAutoCreate_1() { + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自动创建嵌套结构 - 设置完整结构,不生成 + /// + [Fact] + public void TestAutoCreate_2() { + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + head.SetContextAttribute( "hidden", "true" ); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Url + + /// + /// 测试Api地址 - 添加一个列,自动创建嵌套结构 + /// + [Fact] + public void TestUrl_1() { + //设置url属性 + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Api地址 - 自定义表格主体行的*ngFor + /// + [Fact] + public void TestUrl_2() { + //设置url属性 + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + head.SetContextAttribute( "hidden", "true" ); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + row.SetContextAttribute( AngularConst.NgFor, "a" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region LoadUrl + + /// + /// 测试加载地址 + /// + [Fact] + public void TestLoadUrl() { + //设置LoadUrl属性 + _wrapper.SetContextAttribute( UiConst.LoadUrl, "a" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载地址 + /// + [Fact] + public void TestBindLoadUrl() { + //设置LoadUrl属性 + _wrapper.SetContextAttribute( AngularConst.BindLoadUrl, "a" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region QueryUrl + + /// + /// 测试查询地址 + /// + [Fact] + public void TestQueryUrl() { + //设置QueryUrl属性 + _wrapper.SetContextAttribute( UiConst.QueryUrl, "a" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试查询地址 + /// + [Fact] + public void TestBindQueryUrl() { + //设置QueryUrl属性 + _wrapper.SetContextAttribute( AngularConst.BindQueryUrl, "a" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region LoadChildrenUrl + + /// + /// 测试加载子节点地址 + /// + [Fact] + public void TestLoadChildrenUrl() { + //设置LoadChildrenUrl属性 + _wrapper.SetContextAttribute( UiConst.LoadChildrenUrl, "a" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载子节点地址 + /// + [Fact] + public void TestBindLoadChildrenUrl() { + //设置LoadChildrenUrl属性 + _wrapper.SetContextAttribute( AngularConst.BindLoadChildrenUrl, "a" ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region ShowCheckbox + + /// + /// 测试设置复选框 - 添加一个列 + /// + [Fact] + public void TestShowCheckbox_1() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 手工创建完整结构 + /// + [Fact] + public void TestShowCheckbox_2() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 添加2个列 + /// + [Fact] + public void TestShowCheckbox_3() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + + //创建列1 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //创建列2 + var column2 = new TableColumnTagHelper().ToWrapper(); + column2.SetContextAttribute( UiConst.Title, "c" ); + column2.AppendContent( "d" ); + _wrapper.AppendContent( column2 ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "c" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "d" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置复选框 - 手工创建完整结构,添加2个列,设置仅选择叶节点属性,单元格内容不应受影响 + /// + [Fact] + public void TestShowCheckbox_4() { + _wrapper.SetContextAttribute( UiConst.ShowCheckbox, true ); + _wrapper.SetContextAttribute( UiConst.CheckLeafOnly, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格1 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表头单元格2 + var th2 = new TableHeadColumnTagHelper().ToWrapper(); + th2.AppendContent( "a2" ); + headRow.AppendContent( th2 ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列1 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //创建列2 + var column2 = new TableColumnTagHelper().ToWrapper(); + column2.AppendContent( "b2" ); + row.AppendContent( column2 ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a2" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b2" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region ShowRadio + + /// + /// 测试设置单选框 - 添加一个列 + /// + [Fact] + public void TestShowRadio_1() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置单选框 - 手工创建完整结构 + /// + [Fact] + public void TestShowRadio_2() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置单选框 - 添加2个列 + /// + [Fact] + public void TestShowRadio_3() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + + //创建列1 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //创建列2 + var column2 = new TableColumnTagHelper().ToWrapper(); + column2.SetContextAttribute( UiConst.Title, "c" ); + column2.AppendContent( "d" ); + _wrapper.AppendContent( column2 ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "c" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "d" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置单选框 - 手工创建完整结构,添加2个列 + /// + [Fact] + public void TestShowRadio_4() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + + //创建表头 + var head = new TableHeadTagHelper().ToWrapper(); + _wrapper.AppendContent( head ); + + //创建表头行 + var headRow = new TableRowTagHelper().ToWrapper(); + head.AppendContent( headRow ); + + //创建表头单元格1 + var th = new TableHeadColumnTagHelper().ToWrapper(); + th.AppendContent( "a" ); + headRow.AppendContent( th ); + + //创建表头单元格2 + var th2 = new TableHeadColumnTagHelper().ToWrapper(); + th2.AppendContent( "a2" ); + headRow.AppendContent( th2 ); + + //创建表格主体 + var body = new TableBodyTagHelper().ToWrapper(); + _wrapper.AppendContent( body ); + + //创建表格主体行 + var row = new TableRowTagHelper().ToWrapper(); + body.AppendContent( row ); + + //创建列1 + var column = new TableColumnTagHelper().ToWrapper(); + column.AppendContent( "b" ); + row.AppendContent( column ); + + //创建列2 + var column2 = new TableColumnTagHelper().ToWrapper(); + column2.AppendContent( "b2" ); + row.AppendContent( column2 ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "a2" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b2" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置单选框 - 仅允许选中叶节点 + /// + [Fact] + public void TestShowRadio_5() { + _wrapper.SetContextAttribute( UiConst.ShowRadio, true ); + _wrapper.SetContextAttribute( UiConst.CheckLeafOnly, true ); + + //创建列 + var column = new TableColumnTagHelper().ToWrapper(); + column.SetContextAttribute( UiConst.Title, "a" ); + column.AppendContent( "b" ); + _wrapper.AppendContent( column ); + + //结果 + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "b" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + AppendTotalTemplate( result ); + + //验证 + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnLoadChildrenBefore + + /// + /// 测试子节点加载前事件 + /// + [Fact] + public void TestOnLoadChildrenBefore() { + _wrapper.SetContextAttribute( UiConst.OnLoadChildrenBefore, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnLoadChildren + + /// + /// 测试子节点加载完成事件 + /// + [Fact] + public void TestOnLoadChildren() { + _wrapper.SetContextAttribute( UiConst.OnLoadChildren, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnExpand + + /// + /// 测试节点展开事件 + /// + [Fact] + public void TestOnExpand() { + _wrapper.SetContextAttribute( UiConst.OnExpand, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnCollapse + + /// + /// 测试节点折叠事件 + /// + [Fact] + public void TestOnCollapse() { + _wrapper.SetContextAttribute( UiConst.OnCollapse, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/TreeViews/TreeNodeCheckboxTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/TreeViews/TreeNodeCheckboxTagHelperTest.cs new file mode 100644 index 000000000..38a58da22 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/TreeViews/TreeNodeCheckboxTagHelperTest.cs @@ -0,0 +1,105 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.TreeViews; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.TreeViews { + /// + /// 树节点复选框测试 + /// + public class TreeNodeCheckboxTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TreeNodeCheckboxTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TreeNodeCheckboxTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否勾选 + /// + [Fact] + public void TestChecked() { + _wrapper.SetContextAttribute( UiConst.Checked, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否半选 + /// + [Fact] + public void TestIndeterminate() { + _wrapper.SetContextAttribute( UiConst.Indeterminate, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击事件 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/TreeViews/TreeNodeOptionTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/TreeViews/TreeNodeOptionTagHelperTest.cs new file mode 100644 index 000000000..e0f07dc2b --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/TreeViews/TreeNodeOptionTagHelperTest.cs @@ -0,0 +1,94 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.TreeViews; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.TreeViews { + /// + /// 树节点可选项测试 + /// + public class TreeNodeOptionTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TreeNodeOptionTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TreeNodeOptionTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否选中 + /// + [Fact] + public void TestSelected() { + _wrapper.SetContextAttribute( UiConst.Selected, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击事件 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/TreeViews/TreeNodeTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/TreeViews/TreeNodeTagHelperTest.cs new file mode 100644 index 000000000..80fee98b9 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/TreeViews/TreeNodeTagHelperTest.cs @@ -0,0 +1,93 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.TreeViews; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.TreeViews { + /// + /// 树节点测试 + /// + public class TreeNodeTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TreeNodeTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TreeNodeTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试树节点定义指令 + /// + [Fact] + public void TestTreeNodeDef() { + _wrapper.SetContextAttribute( UiConst.TreeNodeDef, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试树节点内边距 + /// + [Fact] + public void TestTreeNodePadding() { + _wrapper.SetContextAttribute( UiConst.TreeNodePadding, "" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试树节点缩进线 + /// + [Fact] + public void TestTreeNodeIndentLine() { + _wrapper.SetContextAttribute( UiConst.TreeNodeIndentLine, "" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/TreeViews/TreeNodeToggleTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/TreeViews/TreeNodeToggleTagHelperTest.cs new file mode 100644 index 000000000..5623f85b0 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/TreeViews/TreeNodeToggleTagHelperTest.cs @@ -0,0 +1,131 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Icons; +using Util.Ui.NgZorro.Components.TreeViews; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.TreeViews { + /// + /// 树节点切换测试 + /// + public class TreeNodeToggleTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TreeNodeToggleTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TreeNodeToggleTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试空操作切换 + /// + [Fact] + public void TestTreeNodeNoopToggle() { + _wrapper.SetContextAttribute( UiConst.TreeNodeNoopToggle, "" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否递归展开/收起 + /// + [Fact] + public void TestRecursive() { + _wrapper.SetContextAttribute( UiConst.Recursive, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试旋转图标 - 默认设置 + /// + [Fact] + public void TestRotateIcon_1() { + var icon = new IconTagHelper().ToWrapper(); + icon.SetContextAttribute( UiConst.Type, AntDesignIcon.Alert ); + _wrapper.AppendContent( icon ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试旋转图标 - 设置取消 + /// + [Fact] + public void TestRotateIcon_2() { + var icon = new IconTagHelper().ToWrapper(); + icon.SetContextAttribute( UiConst.Type, AntDesignIcon.Alert ); + _wrapper.AppendContent( icon ); + _wrapper.SetContextAttribute( UiConst.RotateIcon, false ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试激活图标 + /// + [Fact] + public void TestActiveIcon() { + var icon = new IconTagHelper().ToWrapper(); + icon.SetContextAttribute( UiConst.Type, AntDesignIcon.Alert ); + _wrapper.AppendContent( icon ); + _wrapper.SetContextAttribute( UiConst.ActiveIcon, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/TreeViews/TreeViewTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/TreeViews/TreeViewTagHelperTest.cs new file mode 100644 index 000000000..f47a62180 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/TreeViews/TreeViewTagHelperTest.cs @@ -0,0 +1,115 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.TreeViews; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.TreeViews { + /// + /// 树视图测试 + /// + public class TreeViewTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TreeViewTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TreeViewTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试树控制器 + /// + [Fact] + public void TestTreeControl() { + _wrapper.SetContextAttribute( UiConst.TreeControl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数据源 + /// + [Fact] + public void TestDataSource() { + _wrapper.SetContextAttribute( UiConst.Datasource, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否文件夹样式 + /// + [Fact] + public void TestDirectoryTree() { + _wrapper.SetContextAttribute( UiConst.DirectoryTree, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试节点是否占整行 + /// + [Fact] + public void TestBlockNode() { + _wrapper.SetContextAttribute( UiConst.BlockNode, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试跟踪函数 + /// + [Fact] + public void TestTrackBy() { + _wrapper.SetContextAttribute( UiConst.TrackBy, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/TreeViews/TreeVirtualScrollViewTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/TreeViews/TreeVirtualScrollViewTagHelperTest.cs new file mode 100644 index 000000000..673cb3631 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/TreeViews/TreeVirtualScrollViewTagHelperTest.cs @@ -0,0 +1,138 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.TreeViews; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.TreeViews { + /// + /// 虚拟滚动树视图测试 + /// + public class TreeVirtualScrollViewTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TreeVirtualScrollViewTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TreeVirtualScrollViewTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试树控制器 + /// + [Fact] + public void TestTreeControl() { + _wrapper.SetContextAttribute( UiConst.TreeControl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数据源 + /// + [Fact] + public void TestDataSource() { + _wrapper.SetContextAttribute( UiConst.Datasource, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否文件夹样式 + /// + [Fact] + public void TestDirectoryTree() { + _wrapper.SetContextAttribute( UiConst.DirectoryTree, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试节点是否占整行 + /// + [Fact] + public void TestBlockNode() { + _wrapper.SetContextAttribute( UiConst.BlockNode, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚拟滚动列高 + /// + [Fact] + public void TestItemSize() { + _wrapper.SetContextAttribute( UiConst.ItemSize, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚拟滚动缓冲区最大高度 + /// + [Fact] + public void TestMaxBufferPx() { + _wrapper.SetContextAttribute( UiConst.MaxBufferPx, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚拟滚动缓冲区最小高度 + /// + [Fact] + public void TestMinBufferPx() { + _wrapper.SetContextAttribute( UiConst.MinBufferPx, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Trees/TreeTagHelperTest.Extend.cs b/test/Util.Ui.NgZorro.Tests/Trees/TreeTagHelperTest.Extend.cs new file mode 100644 index 000000000..09e3c2cc2 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Trees/TreeTagHelperTest.Extend.cs @@ -0,0 +1,382 @@ +using System.Text; +using Util.Applications.Trees; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Trees { + /// + /// 树形控件测试 - 指令扩展 + /// + public partial class TreeTagHelperTest { + + #region EnableExtend + + /// + /// 测试启用扩展指令 + /// + [Fact] + public void TestEnableExtend_1() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试启用扩展指令 - 不启用 + /// + [Fact] + public void TestEnableExtend_2() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, false ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region AutoLoad + + /// + /// 测试初始化时是否自动加载数据 + /// + [Fact] + public void TestAutoLoad() { + _wrapper.SetContextAttribute( UiConst.AutoLoad, false ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region QueryParam + + /// + /// 测试查询参数 + /// + [Fact] + public void TestQueryParam() { + _wrapper.SetContextAttribute( UiConst.QueryParam, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Sort + + /// + /// 测试排序条件 + /// + [Fact] + public void TestSort() { + _wrapper.SetContextAttribute( UiConst.Sort, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试排序条件 + /// + [Fact] + public void TestBindSort() { + _wrapper.SetContextAttribute( AngularConst.BindSort, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region Url + + /// + /// 测试Api地址 + /// + [Fact] + public void TestUrl_1() { + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Api地址 - 覆盖默认属性 + /// + [Fact] + public void TestUrl_2() { + _wrapper.SetContextAttribute( UiConst.Url, "a" ); + _wrapper.SetContextAttribute( UiConst.Data, "b" ); + _wrapper.SetContextAttribute( UiConst.CheckedKeys, "c" ); + _wrapper.SetContextAttribute( UiConst.SelectedKeys, "e" ); + _wrapper.SetContextAttribute( UiConst.OnExpandChange, "f" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试Api地址 + /// + [Fact] + public void TestBindUrl() { + _wrapper.SetContextAttribute( AngularConst.BindUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region LoadUrl + + /// + /// 测试加载地址 + /// + [Fact] + public void TestLoadUrl() { + _wrapper.SetContextAttribute( UiConst.LoadUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载地址 + /// + [Fact] + public void TestBindLoadUrl() { + _wrapper.SetContextAttribute( AngularConst.BindLoadUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region QueryUrl + + /// + /// 测试查询地址 + /// + [Fact] + public void TestQueryUrl() { + _wrapper.SetContextAttribute( UiConst.QueryUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试查询地址 + /// + [Fact] + public void TestBindQueryUrl() { + _wrapper.SetContextAttribute( AngularConst.BindQueryUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region LoadChildrenUrl + + /// + /// 测试加载下级节点地址 + /// + [Fact] + public void TestLoadChildrenUrl() { + _wrapper.SetContextAttribute( UiConst.LoadChildrenUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载下级节点地址 + /// + [Fact] + public void TestBindLoadChildrenUrl() { + _wrapper.SetContextAttribute( AngularConst.BindLoadChildrenUrl, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region ExpandForRootAsync + + /// + /// 测试根节点异步加载模式是否展开子节点 + /// + [Fact] + public void TestExpandForRootAsync() { + _wrapper.SetContextAttribute( UiConst.ExpandForRootAsync, false ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region LoadMode + + /// + /// 测试加载模式 - 同步 + /// + [Fact] + public void TestLoadMode_1() { + _wrapper.SetContextAttribute( UiConst.LoadMode, LoadMode.Sync ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载模式 - 异步 + /// + [Fact] + public void TestLoadMode_2() { + _wrapper.SetContextAttribute( UiConst.LoadMode, LoadMode.Async ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试加载模式 - 根节点异步 + /// + [Fact] + public void TestLoadMode_3() { + _wrapper.SetContextAttribute( UiConst.LoadMode, LoadMode.RootAsync ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnLoadChildrenBefore + + /// + /// 测试子节点加载前事件 + /// + [Fact] + public void TestOnLoadChildrenBefore() { + _wrapper.SetContextAttribute( UiConst.OnLoadChildrenBefore, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnLoadChildren + + /// + /// 测试子节点加载完成事件 + /// + [Fact] + public void TestOnLoadChildren() { + _wrapper.SetContextAttribute( UiConst.OnLoadChildren, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnExpand + + /// + /// 测试节点展开事件 + /// + [Fact] + public void TestOnExpand() { + _wrapper.SetContextAttribute( UiConst.OnExpand, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnCollapse + + /// + /// 测试节点折叠事件 + /// + [Fact] + public void TestOnCollapse() { + _wrapper.SetContextAttribute( UiConst.OnCollapse, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + + #region OnLoad + + /// + /// 测试数据加载完成事件 + /// + [Fact] + public void TestOnLoad() { + _wrapper.SetContextAttribute( UiConst.OnLoad, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Trees/TreeTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Trees/TreeTagHelperTest.cs new file mode 100644 index 000000000..14c5a20ab --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Trees/TreeTagHelperTest.cs @@ -0,0 +1,492 @@ +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Trees; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Trees { + /// + /// 树形控件测试 + /// + public partial class TreeTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TreeTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TreeTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数据 + /// + [Fact] + public void TestData() { + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否节点占一行 + /// + [Fact] + public void TestBlockNode() { + _wrapper.SetContextAttribute( UiConst.BlockNode, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试节点前是否显示复选框 + /// + [Fact] + public void TestCheckable() { + _wrapper.SetContextAttribute( UiConst.Checkable, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试节点前是否显示展开图标 + /// + [Fact] + public void TestShowExpand() { + _wrapper.SetContextAttribute( UiConst.ShowExpand, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示连接线 + /// + [Fact] + public void TestShowLine() { + _wrapper.SetContextAttribute( UiConst.ShowLine, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义展开图标 + /// + [Fact] + public void TestExpandedIcon() { + _wrapper.SetContextAttribute( UiConst.ExpandedIcon, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示节点文本前图标 + /// + [Fact] + public void TestShowIcon() { + _wrapper.SetContextAttribute( UiConst.ShowIcon, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否异步加载 + /// + [Fact] + public void TestAsyncData() { + _wrapper.SetContextAttribute( UiConst.AsyncData, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试节点是否可拖拽 + /// + [Fact] + public void TestDraggable() { + _wrapper.SetContextAttribute( UiConst.Draggable, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否支持点选多个节点 + /// + [Fact] + public void TestMultiple() { + _wrapper.SetContextAttribute( UiConst.Multiple, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否隐藏未匹配节点 + /// + [Fact] + public void TestHideUnMatched() { + _wrapper.SetContextAttribute( UiConst.HideUnmatched, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试严格勾选 + /// + [Fact] + public void TestCheckStrictly() { + _wrapper.SetContextAttribute( UiConst.CheckStrictly, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义模板 + /// + [Fact] + public void TestTreeTemplate() { + _wrapper.SetContextAttribute( UiConst.TreeTemplate, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试默认展开所有节点 + /// + [Fact] + public void TestExpandAll() { + _wrapper.SetContextAttribute( UiConst.ExpandAll, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试展开节点的键集合 + /// + [Fact] + public void TestExpandedKeys() { + _wrapper.SetContextAttribute( UiConst.ExpandedKeys, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试勾选节点复选框的键集合 + /// + [Fact] + public void TestCheckedKeys() { + _wrapper.SetContextAttribute( UiConst.CheckedKeys, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试选中节点的键集合 + /// + [Fact] + public void TestSelectedKeys() { + _wrapper.SetContextAttribute( UiConst.SelectedKeys, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试搜索值 + /// + [Fact] + public void TestSearchValue() { + _wrapper.SetContextAttribute( UiConst.SearchValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试搜索值 + /// + [Fact] + public void TestBindSearchValue() { + _wrapper.SetContextAttribute( AngularConst.BindSearchValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试搜索值 + /// + [Fact] + public void TestBindonSearchValue() { + _wrapper.SetContextAttribute( AngularConst.BindonSearchValue, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义搜索方法 + /// + [Fact] + public void TestSearchFunc() { + _wrapper.SetContextAttribute( UiConst.SearchFunc, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试拖拽前函数 + /// + [Fact] + public void TestBeforeDrop() { + _wrapper.SetContextAttribute( UiConst.BeforeDrop, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚拟高度 + /// + [Fact] + public void TestVirtualHeight() { + _wrapper.SetContextAttribute( UiConst.VirtualHeight, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚拟高度 + /// + [Fact] + public void TestBindVirtualHeight() { + _wrapper.SetContextAttribute( AngularConst.BindVirtualHeight, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚拟滚动列高 + /// + [Fact] + public void TestVirtualItemSize() { + _wrapper.SetContextAttribute( UiConst.VirtualItemSize, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚拟滚动缓冲区最大高度 + /// + [Fact] + public void TestVirtualMaxBufferPx() { + _wrapper.SetContextAttribute( UiConst.VirtualMaxBufferPx, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试虚拟滚动缓冲区最小高度 + /// + [Fact] + public void TestVirtualMinBufferPx() { + _wrapper.SetContextAttribute( UiConst.VirtualMinBufferPx, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试单击事件 + /// + [Fact] + public void TestOnClick() { + _wrapper.SetContextAttribute( UiConst.OnClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试双击事件 + /// + [Fact] + public void TestOnDblClick() { + _wrapper.SetContextAttribute( UiConst.OnDblClick, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试上下文菜单事件 + /// + [Fact] + public void TestOnContextMenu() { + _wrapper.SetContextAttribute( UiConst.OnContextmenu, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试树节点复选框选中状态变化事件 + /// + [Fact] + public void TestOnCheckBoxChange() { + _wrapper.SetContextAttribute( UiConst.OnCheckboxChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试展开收缩节点事件 + /// + [Fact] + public void TestOnExpandChange() { + _wrapper.SetContextAttribute( UiConst.OnExpandChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试搜索值变化事件 + /// + [Fact] + public void TestOnSearchValueChange() { + _wrapper.SetContextAttribute( UiConst.OnSearchValueChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试开始拖拽事件 + /// + [Fact] + public void TestOnDragStart() { + _wrapper.SetContextAttribute( UiConst.OnDragStart, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试dragenter事件 + /// + [Fact] + public void TestOnDragEnter() { + _wrapper.SetContextAttribute( UiConst.OnDragEnter, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试dragover事件 + /// + [Fact] + public void TestOnDragOver() { + _wrapper.SetContextAttribute( UiConst.OnDragOver, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试dragleave事件 + /// + [Fact] + public void TestOnDragLeave() { + _wrapper.SetContextAttribute( UiConst.OnDragLeave, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试drop事件 + /// + [Fact] + public void TestOnDrop() { + _wrapper.SetContextAttribute( UiConst.OnDrop, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试dragend事件 + /// + [Fact] + public void TestOnDragEnd() { + _wrapper.SetContextAttribute( UiConst.OnDragEnd, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Typographies/ArticleTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Typographies/ArticleTagHelperTest.cs new file mode 100644 index 000000000..5d713c722 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Typographies/ArticleTagHelperTest.cs @@ -0,0 +1,60 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Typographies; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Typographies { + /// + /// 文章组件测试 + /// + public class ArticleTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public ArticleTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new ArticleTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Typographies/DivTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Typographies/DivTagHelperTest.cs new file mode 100644 index 000000000..5257385cc --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Typographies/DivTagHelperTest.cs @@ -0,0 +1,82 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Typographies; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Typographies { + /// + /// div组件测试 + /// + public class DivTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public DivTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new DivTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试排版指令 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试排版指令 + /// + [Fact] + public void TestTypography() { + _wrapper.SetContextAttribute( UiConst.Typography, true ); + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Typographies/HTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Typographies/HTagHelperTest.cs new file mode 100644 index 000000000..6a60a6192 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Typographies/HTagHelperTest.cs @@ -0,0 +1,103 @@ +using Org.BouncyCastle.Crypto; +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Typographies; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Typographies { + /// + /// h1-h6组件测试 + /// + public class HTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + + /// + /// 测试初始化 + /// + public HTagHelperTest( ITestOutputHelper output ) { + _output = output; + } + + /// + /// 测试h1 + /// + [Fact] + public void TestH1() { + var component = new H1TagHelper().ToWrapper(); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), component.GetResult() ); + } + + /// + /// 测试h2 + /// + [Fact] + public void TestH2() { + var component = new H2TagHelper().ToWrapper(); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), component.GetResult() ); + } + + /// + /// 测试h3 + /// + [Fact] + public void TestH3() { + var component = new H3TagHelper().ToWrapper(); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), component.GetResult() ); + } + + /// + /// 测试h4 + /// + [Fact] + public void TestH4() { + var component = new H4TagHelper().ToWrapper(); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), component.GetResult() ); + } + + /// + /// 测试h5 + /// + [Fact] + public void TestH5() { + var component = new H5TagHelper().ToWrapper(); + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), component.GetResult() ); + } + + /// + /// 测试h6 + /// + [Fact] + public void TestH6() { + var component = new H6TagHelper().ToWrapper(); + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), component.GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + var component = new H6TagHelper().ToWrapper(); + component.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); + result.Append( "
                                                                      " ); + Assert.Equal( result.ToString(), component.GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Typographies/PTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Typographies/PTagHelperTest.cs new file mode 100644 index 000000000..3c0a27367 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Typographies/PTagHelperTest.cs @@ -0,0 +1,348 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Typographies; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Typographies { + /// + /// 段落组件测试 + /// + public class PTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public PTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new PTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容属性 + /// + [Fact] + public void TestNzContent() { + _wrapper.SetContextAttribute( UiConst.Content, "a" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容属性 + /// + [Fact] + public void TestBindContent() { + _wrapper.SetContextAttribute( AngularConst.BindContent, "a" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容属性 + /// + [Fact] + public void TestBindonContent() { + _wrapper.SetContextAttribute( AngularConst.BindonContent, "a" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试可编辑 + /// + [Fact] + public void TestEditable() { + _wrapper.SetContextAttribute( UiConst.Editable, "true" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试编辑图标 + /// + [Fact] + public void TestEditIcon() { + _wrapper.SetContextAttribute( UiConst.EditIcon, AntDesignIcon.Highlight ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试编辑图标 + /// + [Fact] + public void TestBindEditIcon() { + _wrapper.SetContextAttribute( AngularConst.BindEditIcon, "a" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试编辑按钮工具提示 + /// + [Fact] + public void TestEditTooltip() { + _wrapper.SetContextAttribute( UiConst.EditTooltip, "a" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试编辑按钮工具提示 + /// + [Fact] + public void TestBindEditTooltip() { + _wrapper.SetContextAttribute( AngularConst.BindEditTooltip, "a" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试可复制 + /// + [Fact] + public void TestCopyable() { + _wrapper.SetContextAttribute( UiConst.Copyable, "true" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复制文本 + /// + [Fact] + public void TestCopyText() { + _wrapper.SetContextAttribute( UiConst.CopyText, "a" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复制文本 + /// + [Fact] + public void TestBindCopyText() { + _wrapper.SetContextAttribute( AngularConst.BindCopyText, "a" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复制图标 + /// + [Fact] + public void TestCopyIcons() { + _wrapper.SetContextAttribute( UiConst.CopyIcons, "a" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复制图标 + /// + [Fact] + public void TestCopyIcon() { + _wrapper.SetContextAttribute( UiConst.CopyIcon, AntDesignIcon.Smile ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试复制提示 + /// + [Fact] + public void TestCopyTooltips() { + _wrapper.SetContextAttribute( UiConst.CopyTooltips, "a" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本类型 + /// + [Fact] + public void TestType() { + _wrapper.SetContextAttribute( UiConst.Type, TextType.Success ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文本类型 + /// + [Fact] + public void TestBindType() { + _wrapper.SetContextAttribute( AngularConst.BindType, "a" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用文本 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试省略文本 + /// + [Fact] + public void TestEllipsis() { + _wrapper.SetContextAttribute( UiConst.Ellipsis, "true" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试省略可展开 + /// + [Fact] + public void TestExpandable() { + _wrapper.SetContextAttribute( UiConst.Expandable, "true" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试省略行数 + /// + [Fact] + public void TestEllipsisRows() { + _wrapper.SetContextAttribute( UiConst.EllipsisRows, "2" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试省略后缀 + /// + [Fact] + public void TestSuffix() { + _wrapper.SetContextAttribute( UiConst.Suffix, "a" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试省略后缀 + /// + [Fact] + public void TestBindSuffix() { + _wrapper.SetContextAttribute( AngularConst.BindSuffix, "a" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容更改事件 + /// + [Fact] + public void TestOnContentChange() { + _wrapper.SetContextAttribute( UiConst.OnContentChange, "a" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试省略展开更改事件 + /// + [Fact] + public void TestOnExpandChange() { + _wrapper.SetContextAttribute( UiConst.OnExpandChange, "a" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试省略更改事件 + /// + [Fact] + public void TestOnEllipsis() { + _wrapper.SetContextAttribute( UiConst.OnEllipsis, "a" ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容元素 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "

                                                                      a

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Typographies/SpanTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Typographies/SpanTagHelperTest.cs new file mode 100644 index 000000000..b5238da0c --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Typographies/SpanTagHelperTest.cs @@ -0,0 +1,220 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Typographies; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Typographies { + /// + /// span组件测试 + /// + public class SpanTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public SpanTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new SpanTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试*ngIf + /// + [Fact] + public void TestNgIf() { + _wrapper.SetContextAttribute( AngularConst.NgIf, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试code子标签 + /// + [Fact] + public void TestChildTag_Code() { + _wrapper.SetContextAttribute( UiConst.Typography, true ); + _wrapper.SetContextAttribute( UiConst.ChildTag, SpanChildTag.Code ); + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试del子标签 + /// + [Fact] + public void TestChildTag_Delete() { + _wrapper.SetContextAttribute( UiConst.Typography, true ); + _wrapper.SetContextAttribute( UiConst.ChildTag, SpanChildTag.Delete ); + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试kbd子标签 + /// + [Fact] + public void TestChildTag_Keyboard() { + _wrapper.SetContextAttribute( UiConst.Typography, true ); + _wrapper.SetContextAttribute( UiConst.ChildTag, SpanChildTag.Keyboard ); + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试mark子标签 + /// + [Fact] + public void TestChildTag_Mark() { + _wrapper.SetContextAttribute( UiConst.Typography, true ); + _wrapper.SetContextAttribute( UiConst.ChildTag, SpanChildTag.Mark ); + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试strong子标签 + /// + [Fact] + public void TestChildTag_Strong() { + _wrapper.SetContextAttribute( UiConst.Typography, true ); + _wrapper.SetContextAttribute( UiConst.ChildTag, SpanChildTag.Strong ); + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试u子标签 + /// + [Fact] + public void TestChildTag_Underline() { + _wrapper.SetContextAttribute( UiConst.Typography, true ); + _wrapper.SetContextAttribute( UiConst.ChildTag, SpanChildTag.Underline ); + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示文字 + /// + [Fact] + public void TestTooltipTitle() { + _wrapper.SetContextAttribute( UiConst.Typography, true ); + _wrapper.SetContextAttribute( UiConst.TooltipTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示文字 + /// + [Fact] + public void TestBindTooltipTitle() { + _wrapper.SetContextAttribute( UiConst.Typography, true ); + _wrapper.SetContextAttribute( AngularConst.BindTooltipTitle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置html + /// + [Fact] + public void TestHtml() { + _wrapper.SetContextAttribute( UiConst.Html, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置html + /// + [Fact] + public void TestBindHtml() { + _wrapper.SetContextAttribute( AngularConst.BindHtml, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem() { + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试提示框显示状态变化事件 + /// + [Fact] + public void TestOnTooltipVisibleChange() { + _wrapper.SetContextAttribute( UiConst.Typography, true ); + _wrapper.SetContextAttribute( UiConst.OnTooltipVisibleChange, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试上下文菜单事件 + /// + [Fact] + public void TestOnContextmenu() { + _wrapper.SetContextAttribute( UiConst.OnContextmenu, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Upload/UploadTagHelperTest.Expression.cs b/test/Util.Ui.NgZorro.Tests/Upload/UploadTagHelperTest.Expression.cs new file mode 100644 index 000000000..ee8231c8a --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Upload/UploadTagHelperTest.Expression.cs @@ -0,0 +1,32 @@ +using System.Text; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Upload { + /// + /// 上传测试 - 表达式测试 + /// + public partial class UploadTagHelperTest { + /// + /// 测试解析表达式属性for + /// + [Fact] + public void TestFor_1() { + _wrapper.SetExpression( t => t.Code ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "code" ); + result.Append( "" ); + result.Append( "" ); + result.Append( GetButton() ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_code.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Upload/UploadTagHelperTest.Extend.cs b/test/Util.Ui.NgZorro.Tests/Upload/UploadTagHelperTest.Extend.cs new file mode 100644 index 000000000..a02641886 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Upload/UploadTagHelperTest.Extend.cs @@ -0,0 +1,113 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Upload { + /// + /// 上传测试 - 扩展测试 + /// + public partial class UploadTagHelperTest { + /// + /// 测试禁用扩展 + /// + [Fact] + public void TestEnableExtend_False() { + _wrapper.SetContextAttribute( UiConst.EnableExtend, false ); + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( GetButton() ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestNgModel() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "code" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( GetButton() ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型变更事件 + /// + [Fact] + public void TestOnModelChange() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "a" ); + _wrapper.SetContextAttribute( UiConst.OnModelChange, "b" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( GetButton() ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestNotBindFileList() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "code" ); + _wrapper.SetContextAttribute( AngularConst.NotBindFileList, true ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( GetButton() ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否清除文件列表 + /// + [Fact] + public void TestClearFiles() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "code" ); + _wrapper.SetContextAttribute( UiConst.ClearFiles, "true" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( GetButton() ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试模型数据转换为上传文件列表的延迟时间 + /// + [Fact] + public void TestModelToFilesDebounceTime() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "code" ); + _wrapper.SetContextAttribute( UiConst.ModelToFilesDebounceTime, 10 ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( GetButton() ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试上传完成事件 + /// + [Fact] + public void TestOnUploadComplete() { + _wrapper.SetContextAttribute( AngularConst.NgModel, "code" ); + _wrapper.SetContextAttribute( UiConst.OnUploadComplete, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( GetButton() ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Upload/UploadTagHelperTest.Validation.cs b/test/Util.Ui.NgZorro.Tests/Upload/UploadTagHelperTest.Validation.cs new file mode 100644 index 000000000..24879ff5e --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Upload/UploadTagHelperTest.Validation.cs @@ -0,0 +1,33 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.NgZorro.Tests.Upload { + /// + /// 上传测试 - 验证测试 + /// + public partial class UploadTagHelperTest { + /// + /// 测试必填项验证 + /// + [Fact] + public void TestRequired() { + _wrapper.SetContextAttribute( UiConst.Required, "true" ); + _wrapper.SetContextAttribute( AngularConst.NgModel, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( GetButton() ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "{{v_id.getErrorMessage()}}" ); + result.Append( "" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Upload/UploadTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/Upload/UploadTagHelperTest.cs new file mode 100644 index 000000000..3bfb70ac1 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Upload/UploadTagHelperTest.cs @@ -0,0 +1,533 @@ +using System; +using System.Text; +using Util.Helpers; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.Upload; +using Util.Ui.NgZorro.Enums; +using Util.Ui.NgZorro.Tests.Samples; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.Upload { + /// + /// 上传测试 + /// + public partial class UploadTagHelperTest : IDisposable { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public UploadTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new UploadTagHelper().ToWrapper(); + Id.SetId( "id" ); + } + + /// + /// 测试清理 + /// + public void Dispose() { + Id.Reset(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 获取按钮 + /// + private string GetButton() { + var result = new StringBuilder(); + result.Append( "" ); + return result.ToString(); + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加标识 + /// + [Fact] + public void TestId() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试接受上传的文件类型 + /// + [Fact] + public void TestAccept() { + _wrapper.SetContextAttribute( UiConst.Accept, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试接受上传的文件类型 + /// + [Fact] + public void TestBindAccept() { + _wrapper.SetContextAttribute( AngularConst.BindAccept, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试上传地址 + /// + [Fact] + public void TestAction() { + _wrapper.SetContextAttribute( UiConst.Action, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试上传地址 + /// + [Fact] + public void TestBindAction() { + _wrapper.SetContextAttribute( AngularConst.BindAction, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否支持上传文件夹 + /// + [Fact] + public void TestDirectory() { + _wrapper.SetContextAttribute( UiConst.Directory, "true" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试上传前钩子函数 + /// + [Fact] + public void TestBeforeUpload() { + _wrapper.SetContextAttribute( UiConst.BeforeUpload, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义上传实现 + /// + [Fact] + public void TestCustomRequest() { + _wrapper.SetContextAttribute( UiConst.CustomRequest, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试上传参数 + /// + [Fact] + public void TestData() { + _wrapper.SetContextAttribute( UiConst.Data, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + _wrapper.SetContextAttribute( UiConst.Disabled, "true" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文件列表 + /// + [Fact] + public void TestFileList() { + _wrapper.SetContextAttribute( UiConst.FileList, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文件列表 + /// + [Fact] + public void TestBindonFileList() { + _wrapper.SetContextAttribute( AngularConst.BindonFileList, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试限制单次最多上传数量 + /// + [Fact] + public void TestLimit() { + _wrapper.SetContextAttribute( UiConst.Limit, 1 ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试限制单次最多上传数量 + /// + [Fact] + public void TestBindLimit() { + _wrapper.SetContextAttribute( AngularConst.BindLimit, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试限制文件大小 + /// + [Fact] + public void TestSize() { + _wrapper.SetContextAttribute( UiConst.Size, 1 ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试限制文件大小 + /// + [Fact] + public void TestBindSize() { + _wrapper.SetContextAttribute( AngularConst.BindSize, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试限制文件类型 + /// + [Fact] + public void TestFileType() { + _wrapper.SetContextAttribute( UiConst.FileType, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试限制文件类型 + /// + [Fact] + public void TestBindFileType() { + _wrapper.SetContextAttribute( AngularConst.BindFileType, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试过滤器 + /// + [Fact] + public void TestFilter() { + _wrapper.SetContextAttribute( UiConst.Filter, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试请求头部 + /// + [Fact] + public void TestHeaders() { + _wrapper.SetContextAttribute( UiConst.Headers, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列表类型 + /// + [Fact] + public void TestListType() { + _wrapper.SetContextAttribute( UiConst.ListType, UploadListType.PictureCard ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列表类型 + /// + [Fact] + public void TestBindListType() { + _wrapper.SetContextAttribute( AngularConst.BindListType, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否支持多选文件 + /// + [Fact] + public void TestMultiple() { + _wrapper.SetContextAttribute( UiConst.Multiple, "true" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试参数名 + /// + [Fact] + public void TestName() { + _wrapper.SetContextAttribute( UiConst.Name, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试参数名 + /// + [Fact] + public void TestBindName() { + _wrapper.SetContextAttribute( AngularConst.BindName, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示上传列表 + /// + [Fact] + public void TestShowUploadList() { + _wrapper.SetContextAttribute( UiConst.ShowUploadList, "true" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试是否显示上传按钮 + /// + [Fact] + public void TestShowButton() { + _wrapper.SetContextAttribute( UiConst.ShowButton, "true" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试上传时是否携带cookie + /// + [Fact] + public void TestWithCredentials() { + _wrapper.SetContextAttribute( UiConst.WithCredentials, "true" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试点击是否打开文件对话框 + /// + [Fact] + public void TestOpenFileDialogOnClick() { + _wrapper.SetContextAttribute( UiConst.OpenFileDialogOnClick, "true" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试点击预览函数 + /// + [Fact] + public void TestPreview() { + _wrapper.SetContextAttribute( UiConst.Preview, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试自定义文件预览函数 + /// + [Fact] + public void TestPreviewFile() { + _wrapper.SetContextAttribute( UiConst.PreviewFile, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试预览文件是否有效图片函数 + /// + [Fact] + public void TestPreviewIsImage() { + _wrapper.SetContextAttribute( UiConst.PreviewIsImage, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试移除文件函数 + /// + [Fact] + public void TestRemove() { + _wrapper.SetContextAttribute( UiConst.Remove, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试下载文件函数 + /// + [Fact] + public void TestDownload() { + _wrapper.SetContextAttribute( UiConst.Download, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试转换文件函数 + /// + [Fact] + public void TestTransformFile() { + _wrapper.SetContextAttribute( UiConst.TransformFile, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试图标渲染模板 + /// + [Fact] + public void TestIconRender() { + _wrapper.SetContextAttribute( UiConst.IconRender, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文件列表渲染模板 + /// + [Fact] + public void TestFileListRender() { + _wrapper.SetContextAttribute( UiConst.FileListRender, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试上传文件改变事件 + /// + [Fact] + public void TestOnChange() { + _wrapper.SetContextAttribute( UiConst.OnChange, "a" ); + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 + /// + [Fact] + public void TestSpaceItem_1() { + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + + var result = new StringBuilder(); + result.Append( $"{GetButton()}" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距项 - 创建form-item + /// + [Fact] + public void TestSpaceItem_2() { + _wrapper.SetContextAttribute( UiConst.LabelText, "a" ); + _wrapper.SetContextAttribute( UiConst.SpaceItem, true ); + + var result = new StringBuilder(); + result.Append( "" ); + result.Append( "a" ); + result.Append( "" ); + result.Append( $"{GetButton()}" ); + result.Append( "" ); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/Util.Ui.NgZorro.Tests.csproj b/test/Util.Ui.NgZorro.Tests/Util.Ui.NgZorro.Tests.csproj new file mode 100644 index 000000000..a1921070f --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/Util.Ui.NgZorro.Tests.csproj @@ -0,0 +1,29 @@ + + + + $(NetTargetFramework) + false + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/test/Util.Ui.NgZorro.Tests/VirtualScrolls/VirtualScrollTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/VirtualScrolls/VirtualScrollTagHelperTest.cs new file mode 100644 index 000000000..4ce7b5f52 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/VirtualScrolls/VirtualScrollTagHelperTest.cs @@ -0,0 +1,82 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.VirtualScrolls; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.VirtualScrolls { + /// + /// 虚拟滚动测试 + /// + public class VirtualScrollTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public VirtualScrollTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new VirtualScrollTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试数据引用 + /// + [Fact] + public void TestLetData() { + _wrapper.SetContextAttribute( UiConst.LetData, "" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试索引引用 + /// + [Fact] + public void TestLetIndex() { + _wrapper.SetContextAttribute( UiConst.LetIndex, "" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/VirtualScrolls/VirtualScrollViewportTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/VirtualScrolls/VirtualScrollViewportTagHelperTest.cs new file mode 100644 index 000000000..a748da499 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/VirtualScrolls/VirtualScrollViewportTagHelperTest.cs @@ -0,0 +1,72 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.VirtualScrolls; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.VirtualScrolls { + /// + /// 虚拟滚动窗口测试 + /// + public class VirtualScrollViewportTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public VirtualScrollViewportTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new VirtualScrollViewportTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试列高 + /// + [Fact] + public void TestItemSize() { + _wrapper.SetContextAttribute( UiConst.ItemSize, "1" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.NgZorro.Tests/WaterMarks/WaterMarkTagHelperTest.cs b/test/Util.Ui.NgZorro.Tests/WaterMarks/WaterMarkTagHelperTest.cs new file mode 100644 index 000000000..ec11d5af5 --- /dev/null +++ b/test/Util.Ui.NgZorro.Tests/WaterMarks/WaterMarkTagHelperTest.cs @@ -0,0 +1,293 @@ +using System.Text; +using Util.Ui.Angular.Configs; +using Util.Ui.Configs; +using Util.Ui.NgZorro.Components.WaterMarks; +using Util.Ui.NgZorro.Enums; +using Util.Ui.TagHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.NgZorro.Tests.WaterMarks; + +/// +/// 水印测试 +/// +public class WaterMarkTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public WaterMarkTagHelperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new WaterMarkTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水印文字内容 + /// + [Fact] + public void TestContent() { + _wrapper.SetContextAttribute( UiConst.Content, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水印文字内容 + /// + [Fact] + public void TestBindContent() { + _wrapper.SetContextAttribute( AngularConst.BindContent, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水印宽度 + /// + [Fact] + public void TestWidth() { + _wrapper.SetContextAttribute( UiConst.Width, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水印宽度 + /// + [Fact] + public void TestBindWidth() { + _wrapper.SetContextAttribute( AngularConst.BindWidth, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水印高度 + /// + [Fact] + public void TestHeight() { + _wrapper.SetContextAttribute( UiConst.Height, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水印高度 + /// + [Fact] + public void TestBindHeight() { + _wrapper.SetContextAttribute( AngularConst.BindHeight, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试旋转角度 + /// + [Fact] + public void TestRotate() { + _wrapper.SetContextAttribute( UiConst.Rotate, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试旋转角度 + /// + [Fact] + public void TestBindRotate() { + _wrapper.SetContextAttribute( AngularConst.BindRotate, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试z-index + /// + [Fact] + public void TestZIndex() { + _wrapper.SetContextAttribute( UiConst.ZIndex, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试z-index + /// + [Fact] + public void TestBindZIndex() { + _wrapper.SetContextAttribute( AngularConst.BindZIndex, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水印图片地址 + /// + [Fact] + public void TestImage() { + _wrapper.SetContextAttribute( UiConst.Image, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试水印图片地址 + /// + [Fact] + public void TestBindImage() { + _wrapper.SetContextAttribute( AngularConst.BindImage, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试文字样式 + /// + [Fact] + public void TestFont() { + _wrapper.SetContextAttribute( UiConst.Font, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试字体颜色 + /// + [Fact] + public void TestFontColorType() { + _wrapper.SetContextAttribute( UiConst.FontColorType, AntDesignColor.Red ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试字体颜色 + /// + [Fact] + public void TestFontColor() { + _wrapper.SetContextAttribute( UiConst.FontColor, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试字体大小 + /// + [Fact] + public void TestFontSize() { + _wrapper.SetContextAttribute( UiConst.FontSize, 1 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试字体粗细 + /// + [Fact] + public void TestFontWeight() { + _wrapper.SetContextAttribute( UiConst.FontWeight, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试字体类型 + /// + [Fact] + public void TestFontFamily() { + _wrapper.SetContextAttribute( UiConst.FontFamily, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试字体样式 + /// + [Fact] + public void TestFontStyle() { + _wrapper.SetContextAttribute( UiConst.FontStyle, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试间距 + /// + [Fact] + public void TestGap() { + _wrapper.SetContextAttribute( UiConst.Gap, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试偏移量 + /// + [Fact] + public void TestOffset() { + _wrapper.SetContextAttribute( UiConst.Offset, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试内容 + /// + [Fact] + public void TestAppendContent() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Builders/EmptyContainerTagBuilderTest.cs b/test/Util.Ui.Tests/Builders/EmptyContainerTagBuilderTest.cs new file mode 100644 index 000000000..6f89b6599 --- /dev/null +++ b/test/Util.Ui.Tests/Builders/EmptyContainerTagBuilderTest.cs @@ -0,0 +1,49 @@ +using Util.Ui.Builders; +using Util.Ui.Tests.Samples; +using Xunit; + +namespace Util.Ui.Tests.Builders { + /// + /// 空容器标签生成器测试 + /// + public class EmptyContainerTagBuilderTest { + /// + /// 测试初始化 + /// + public EmptyContainerTagBuilderTest() { + _builder = new EmptyContainerTagBuilder(); + } + + /// + /// 空容器标签生成器 + /// + private readonly EmptyContainerTagBuilder _builder; + + /// + /// 默认输出 + /// + [Fact] + public void TestDefault() { + Assert.Empty( _builder.ToString() ); + } + + /// + /// 测试添加内容 + /// + [Fact] + public void TestAppendContent() { + _builder.AppendContent( new DivBuilder() ); + Assert.Equal( "
                                                                      ", _builder.ToString() ); + } + + /// + /// 测试添加内容 + /// + [Fact] + public void TestAppendContent_2() { + _builder.AppendContent( new DivBuilder() ); + _builder.AppendContent( new SpanBuilder() ); + Assert.Equal( "
                                                                      ", _builder.ToString() ); + } + } +} diff --git a/test/Util.Ui.Tests/Builders/TagBuilderTest.cs b/test/Util.Ui.Tests/Builders/TagBuilderTest.cs index 06f6f404c..0717d67ab 100644 --- a/test/Util.Ui.Tests/Builders/TagBuilderTest.cs +++ b/test/Util.Ui.Tests/Builders/TagBuilderTest.cs @@ -1,53 +1,107 @@ -using Util.Ui.Builders; -using Xunit; - -namespace Util.Ui.Tests.Builders { - /// - /// 标签生成器测试 - /// - public class TagBuilderTest { - /// - /// 测试初始化 - /// - public TagBuilderTest() { - _builder = new TagBuilder( "div" ); - } - - /// - /// 标签生成器 - /// - private readonly TagBuilder _builder; - - /// - /// 默认输出 - /// - [Fact] - public void TestDefault() { - Assert.Equal( "
                                                                      ", _builder.ToString() ); - } - - /// - /// 添加属性 - /// - [Fact] - public void TestAttribute() { - _builder.Attribute( "name", "a" ); - Assert.Equal( "
                                                                      ", _builder.ToString() ); - _builder.Attribute( "name", "b" ); - Assert.Equal( "
                                                                      ", _builder.ToString() ); - _builder.Attribute( "name", "c",true ); - Assert.Equal( "
                                                                      ", _builder.ToString() ); - } - - /// - /// 添加class属性 - /// - [Fact] - public void TestClass() { - _builder.Class( "a" ); - Assert.Equal( "
                                                                      ", _builder.ToString() ); - _builder.Class( "b" ); - Assert.Equal( "
                                                                      ", _builder.ToString() ); - } - } -} +using Util.Ui.Builders; +using Xunit; + +namespace Util.Ui.Tests.Builders { + /// + /// 标签生成器测试 + /// + public class TagBuilderTest { + /// + /// 测试初始化 + /// + public TagBuilderTest() { + _builder = new TagBuilder( "div" ); + } + + /// + /// 标签生成器 + /// + private readonly TagBuilder _builder; + + /// + /// 默认输出 + /// + [Fact] + public void TestDefault() { + Assert.Equal( "
                                                                      ", _builder.ToString() ); + } + + /// + /// 添加属性 + /// + [Fact] + public void TestAttribute_1() { + _builder.Attribute( "name", "a" ); + Assert.Equal( "
                                                                      ", _builder.ToString() ); + } + + /// + /// 添加同名属性,第二个被忽略 + /// + [Fact] + public void TestAttribute_2() { + _builder.Attribute( "name", "a" ); + _builder.Attribute( "name", "b" ); + Assert.Equal( "
                                                                      ", _builder.ToString() ); + } + + /// + /// 添加同名属性,替换已存在的属性 + /// + [Fact] + public void TestAttribute_3() { + _builder.Attribute( "name", "a" ); + _builder.Attribute( "name", "b", true ); + Assert.Equal( "
                                                                      ", _builder.ToString() ); + } + + /// + /// 添加同名属性,属性值累加 + /// + [Fact] + public void TestAttribute_4() { + _builder.Attribute( "name", "a" ); + _builder.Attribute( "name", "b", append: true ); + Assert.Equal( "
                                                                      ", _builder.ToString() ); + } + + /// + /// 添加同名属性,属性值累加 - 只有一个属性 + /// + [Fact] + public void TestAttribute_5() { + _builder.Attribute( "name", "a", append: true ); + Assert.Equal( "
                                                                      ", _builder.ToString() ); + } + + /// + /// 添加同名属性,同时设置替换和属性值累加时,替换生效 + /// + [Fact] + public void TestAttribute_6() { + _builder.Attribute( "name", "a" ); + _builder.Attribute( "name", "b", true, true ); + Assert.Equal( "
                                                                      ", _builder.ToString() ); + } + + /// + /// 添加属性,值为空 + /// + [Fact] + public void TestAttribute_7() { + _builder.Attribute( "name" ); + Assert.Equal( "
                                                                      ", _builder.ToString() ); + } + + /// + /// 添加class属性 + /// + [Fact] + public void TestClass() { + _builder.Class( "a" ); + Assert.Equal( "
                                                                      ", _builder.ToString() ); + _builder.Class( "b" ); + Assert.Equal( "
                                                                      ", _builder.ToString() ); + } + } +} diff --git a/test/Util.Ui.Tests/Configs/ConfigTest.cs b/test/Util.Ui.Tests/Configs/ConfigTest.cs index e9b34c9b0..4e5a91a85 100644 --- a/test/Util.Ui.Tests/Configs/ConfigTest.cs +++ b/test/Util.Ui.Tests/Configs/ConfigTest.cs @@ -1,39 +1,114 @@ -using Util.Ui.Configs; -using Xunit; - -namespace Util.Ui.Tests.Configs { - /// - /// 配置测试 - /// - public class ConfigTest { - /// - /// 配置 - /// - private readonly Config _config; - - /// - /// 初始化配置测试 - /// - public ConfigTest() { - _config = new Config(); - } - - /// - /// 添加类 - /// - [Fact] - public void TestAddClass() { - Assert.Empty( _config.GetClassList()); - _config.AddClass( "" ); - _config.AddClass( " " ); - Assert.Empty( _config.GetClassList()); - _config.AddClass( "a" ); - Assert.Single( _config.GetClassList()); - Assert.Equal( "a", _config.GetClassList()[0] ); - _config.AddClass( "a" ); - Assert.Single( _config.GetClassList()); - _config.AddClass( "b" ); - Assert.Equal( 2, _config.GetClassList().Count ); - } - } -} +using System.Collections.Generic; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Configs; +using Xunit; + +namespace Util.Ui.Tests.Configs { + /// + /// UI配置测试 + /// + public class ConfigTest { + /// + /// 创建配置 + /// + private Config CreateConfig() { + TagHelperAttributeList attributes = new TagHelperAttributeList { { "a", 1 }, { "b", true } }; + TagHelperContext context = new TagHelperContext( attributes, new Dictionary(), Util.Helpers.Id.Create() ); + return new Config( context, null ); + } + + /// + /// 测试获取值 + /// + [Fact] + public void TestGetValue_1() { + var config = CreateConfig(); + Assert.Equal( "1", config.GetValue( "a" ) ); + } + + /// + /// 测试获取值 - 泛型 + /// + [Fact] + public void TestGetValue_2() { + var config = CreateConfig(); + Assert.Equal( 1, config.GetValue( "a" ) ); + } + + /// + /// 测试获取布尔值 + /// + [Fact] + public void TestGetBoolValue() { + var config = CreateConfig(); + Assert.Equal( "true", config.GetBoolValue( "b" ) ); + } + + /// + /// 测试设置属性 + /// + [Fact] + public void TestSetAttribute() { + var config = CreateConfig(); + config.SetAttribute( "SetAttribute", 3 ); + Assert.Equal( 3, config.GetValue( "SetAttribute" ) ); + } + + /// + /// 测试设置属性 - 替换已存在的属性 + /// + [Fact] + public void TestSetAttribute_2() { + var config = CreateConfig(); + config.SetAttribute( "a", 2 ); + Assert.Equal( 2, config.GetValue( "a" ) ); + } + + /// + /// 测试设置属性 - 属性已存在则忽略 + /// + [Fact] + public void TestSetAttribute_3() { + var config = CreateConfig(); + config.SetAttribute( "a", 2, false ); + Assert.Equal( 1, config.GetValue( "a" ) ); + } + + /// + /// 测试移除属性 + /// + [Fact] + public void TestRemoveAttribute() { + var config = CreateConfig(); + Assert.Equal( 1, config.GetValue( "a" ) ); + config.RemoveAttribute( "a" ); + Assert.Equal( 0, config.GetValue( "a" ) ); + } + + /// + /// 测试复制配置 - 复制一个属性 + /// + [Fact] + public void TestCopy_1() { + TagHelperAttributeList attributes = new TagHelperAttributeList { { "a", 1 } }; + TagHelperContext context = new TagHelperContext( attributes, new Dictionary(), Util.Helpers.Id.Create() ); + var config = new Config( context, null ); + var copy = config.Copy(); + Assert.Equal( 1, copy.GetValue( "a" ) ); + } + + /// + /// 测试复制配置 - 复制并修改一个属性,不会影响原配置 + /// + [Fact] + public void TestCopy_2() { + TagHelperAttributeList attributes = new TagHelperAttributeList { { "a", 1 } }; + TagHelperContext context = new TagHelperContext( attributes, new Dictionary(), Util.Helpers.Id.Create() ); + var config = new Config( context, null ); + var copy = config.Copy(); + copy.SetAttribute( "a",2 ); + Assert.Equal( 1, config.GetValue( "a" ) ); + Assert.Equal( 2, copy.GetValue( "a" ) ); + } + } +} diff --git a/test/Util.Ui.Tests/Expressions/ExpressionResolverTest.cs b/test/Util.Ui.Tests/Expressions/ExpressionResolverTest.cs new file mode 100644 index 000000000..a470f6c09 --- /dev/null +++ b/test/Util.Ui.Tests/Expressions/ExpressionResolverTest.cs @@ -0,0 +1,410 @@ +using System; +using Util.Domain.Biz.Enums; +using Util.Ui.Expressions; +using Util.Ui.Tests.Samples; +using Xunit; + +namespace Util.Ui.Tests.Expressions { + /// + /// 表达式解析器测试 + /// + public class ExpressionResolverTest { + /// + /// 表达式解析器 + /// + private readonly ExpressionResolver _resolver; + + /// + /// 测试初始化 + /// + public ExpressionResolverTest() { + _resolver = new ExpressionResolver(); + } + + /// + /// 测试解析模型类型 + /// + [Fact] + public void TestResolve_ModelType() { + var expression = ModelExpressionHelper.Create( "Code",t => t.Code ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "System.String", result.ModelType?.FullName ); + } + + /// + /// 测试解析原始属性名 + /// + [Fact] + public void TestResolve_OriginalPropertyName() { + var expression = ModelExpressionHelper.Create( "a" ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "a", result.OriginalPropertyName ); + } + + /// + /// 测试解析属性名 + /// + [Fact] + public void TestResolve_PropertyName_1() { + var expression = ModelExpressionHelper.Create( "a" ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "a", result.PropertyName ); + } + + /// + /// 测试解析属性名 - 修正为首字母小写 + /// + [Fact] + public void TestResolve_PropertyName_2() { + var expression = ModelExpressionHelper.Create( "CustomerName" ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "customerName", result.PropertyName ); + } + + /// + /// 测试解析属性名 - 2级对象 + /// + [Fact] + public void TestResolve_PropertyName_3() { + var expression = ModelExpressionHelper.Create( "Customer.UserName" ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "customer.userName", result.PropertyName ); + } + + /// + /// 测试解析属性名 - 3级对象 + /// + [Fact] + public void TestResolve_PropertyName_4() { + var expression = ModelExpressionHelper.Create( "Employee.Department.UserName" ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "employee.department.userName", result.PropertyName ); + } + + /// + /// 测试解析属性名 - 修正缩写大小写 + /// + [Fact] + public void TestResolve_PropertyName_5() { + var expression = ModelExpressionHelper.Create( "UIName" ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "uiName", result.PropertyName ); + } + + /// + /// 测试解析最后一级属性名 + /// + [Fact] + public void TestResolve_LastPropertyName_1() { + var expression = ModelExpressionHelper.Create( "Code",t => t.Code ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "code", result.LastPropertyName ); + } + + /// + /// 测试解析最后一级属性名 - 2级对象 + /// + [Fact] + public void TestResolve_LastPropertyName_2() { + var expression = ModelExpressionHelper.Create( "Employee.Name", t => t.Employee.Name ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "name", result.LastPropertyName ); + } + + /// + /// 测试解析安全属性名 + /// + [Fact] + public void TestResolve_GetSafePropertyName_1() { + var expression = ModelExpressionHelper.Create( "CustomerName" ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "model.customerName", result.GetSafePropertyName() ); + } + + /// + /// 测试解析安全属性名 - 2级对象 + /// + [Fact] + public void TestResolve_GetSafePropertyName_2() { + var expression = ModelExpressionHelper.Create( "Customer.UserName" ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "a.customer&&a.customer.userName", result.GetSafePropertyName("a") ); + } + + /// + /// 测试解析安全属性名 - 3级对象 + /// + [Fact] + public void TestResolve_GetSafePropertyName_3() { + var expression = ModelExpressionHelper.Create( "Employee.Department.UserName" ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "model.employee&&model.employee.department&&model.employee.department.userName", result.GetSafePropertyName() ); + } + + /// + /// 测试解析安全属性名 - 4级对象 + /// + [Fact] + public void TestResolve_GetSafePropertyName_4() { + var expression = ModelExpressionHelper.Create( "Employee.Department.Customer.UserName" ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "model.employee&&model.employee.department&&model.employee.department.customer&&model.employee.department.customer.userName", result.GetSafePropertyName() ); + } + + /// + /// 测试解析安全属性名 - 转换名称大小写 + /// + [Fact] + public void TestResolve_GetSafePropertyName_5() { + var expression = ModelExpressionHelper.Create( "UIName" ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "model.uiName", result.GetSafePropertyName() ); + } + + /// + /// 测试安全属性名 + /// + [Fact] + public void TestResolve_SafePropertyName_1() { + var expression = ModelExpressionHelper.Create( "CustomerName" ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "model.customerName", result.SafePropertyName ); + } + + /// + /// 测试安全属性名- 2级对象 + /// + [Fact] + public void TestResolve_SafePropertyName_2() { + var expression = ModelExpressionHelper.Create( "Customer.UserName" ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "model.customer&&model.customer.userName", result.SafePropertyName ); + } + + /// + /// 测试安全属性名- 设置模型名称 + /// + [Fact] + public void TestResolve_SafePropertyName_3() { + var expression = ModelExpressionHelper.Create( "Customer.UserName" ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "employee.customer&&employee.customer.userName", result.SafePropertyName ); + } + + /// + /// 测试解析模型名称 - 未设置ModelAttribute + /// + [Fact] + public void TestResolve_ModelName_1() { + var expression = ModelExpressionHelper.Create( "a" ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "model", result.ModelName ); + } + + /// + /// 测试解析模型名称 - 设置ModelAttribute + /// + [Fact] + public void TestResolve_ModelName_2() { + var expression = ModelExpressionHelper.Create( "a" ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "employee", result.ModelName ); + } + + /// + /// 测试解析显示名称 + /// + [Fact] + public void TestResolve_DisplayName_1() { + var expression = ModelExpressionHelper.Create( "a",t => t.Code ); + var result = _resolver.Resolve( expression ); + Assert.Equal( "编码", result.DisplayName ); + } + + /// + /// 测试解析是否密码类型 + /// + [Fact] + public void TestResolve_IsPassword() { + var expression = ModelExpressionHelper.Create( "a", t => t.Password ); + var result = _resolver.Resolve( expression ); + Assert.True( result.IsPassword ); + } + + /// + /// 测试解析是否布尔类型 + /// + [Fact] + public void TestResolve_IsBool() { + var expression = ModelExpressionHelper.Create( "a", t => t.Enabled ); + var result = _resolver.Resolve( expression ); + Assert.True( result.IsBool ); + } + + /// + /// 测试解析是否枚举类型 + /// + [Fact] + public void TestResolve_IsEnum() { + var expression = ModelExpressionHelper.Create( "a", t => t.Gender ); + var result = _resolver.Resolve( expression ); + Assert.True( result.IsEnum ); + } + + /// + /// 测试解析是否日期类型 + /// + [Fact] + public void TestResolve_IsDate() { + var expression = ModelExpressionHelper.Create( "a", t => t.Birthday ); + var result = _resolver.Resolve( expression ); + Assert.True( result.IsDate ); + } + + /// + /// 测试解析是否整型 + /// + [Fact] + public void TestResolve_IsInt() { + var expression = ModelExpressionHelper.Create( "a", t => t.Tel ); + var result = _resolver.Resolve( expression ); + Assert.True( result.IsInt ); + } + + /// + /// 测试解析是否数值类型 + /// + [Fact] + public void TestResolve_IsNumber() { + var expression = ModelExpressionHelper.Create( "a", t => t.Age ); + var result = _resolver.Resolve( expression ); + Assert.True( result.IsNumber ); + } + + /// + /// 测试解析是否必填项 + /// + [Fact] + public void TestResolve_IsRequired_1() { + var expression = ModelExpressionHelper.Create( "a", t => t.Code ); + var result = _resolver.Resolve( expression ); + Assert.True( result.IsRequired ); + Assert.Equal( "编码不能是空值", result.RequiredMessage ); + } + + /// + /// 测试解析是否必填项 + /// + [Fact] + public void TestResolve_IsRequired_2() { + var expression = ModelExpressionHelper.Create( "a", t => t.Name ); + var result = _resolver.Resolve( expression ); + Assert.False( result.IsRequired ); + Assert.Null( result.RequiredMessage ); + } + + /// + /// 测试解析StringLengthAttribute特性 + /// + [Fact] + public void TestResolve_StringLength() { + var expression = ModelExpressionHelper.Create( "a", t => t.Code ); + var result = _resolver.Resolve( expression ); + Assert.Equal( 50,result.MaxLength ); + Assert.Equal( 5, result.MinLength ); + Assert.Equal( "编码不能超过50位,也不能小于5位", result.MaxLengthMessage ); + Assert.Equal( "编码不能超过50位,也不能小于5位", result.MinLengthMessage ); + } + + /// + /// 测试解析MaxLengthAttribute特性 + /// + [Fact] + public void TestResolve_MaxLength() { + var expression = ModelExpressionHelper.Create( "a", t => t.Name ); + var result = _resolver.Resolve( expression ); + Assert.Equal( 200, result.MaxLength ); + Assert.Equal( "姓名不能超过200位", result.MaxLengthMessage ); + } + + /// + /// 测试解析MinLengthAttribute特性 + /// + [Fact] + public void TestResolve_MinLength() { + var expression = ModelExpressionHelper.Create( "a", t => t.Name ); + var result = _resolver.Resolve( expression ); + Assert.Equal( 2, result.MinLength ); + Assert.Equal( "姓名不能少于2位", result.MinLengthMessage ); + } + + /// + /// 测试解析RangeAttribute特性 + /// + [Fact] + public void TestResolve_Range() { + var expression = ModelExpressionHelper.Create( "a", t => t.Age ); + var result = _resolver.Resolve( expression ); + Assert.Equal( 1.1, result.Min ); + Assert.Equal( "年龄从1.1到99.99", result.MinMessage ); + Assert.Equal( 99.99, result.Max ); + Assert.Equal( "年龄从1.1到99.99", result.MaxMessage ); + } + + /// + /// 测试解析EmailAddressAttribute特性 + /// + [Fact] + public void TestResolve_Email() { + var expression = ModelExpressionHelper.Create( "a", t => t.Email ); + var result = _resolver.Resolve( expression ); + Assert.True( result.IsEmail ); + Assert.Equal( "电子邮件无效", result.EmailMessage ); + } + + /// + /// 测试解析PhoneAttribute特性 + /// + [Fact] + public void TestResolve_Phone() { + var expression = ModelExpressionHelper.Create( "a", t => t.Phone ); + var result = _resolver.Resolve( expression ); + Assert.True( result.IsPhone ); + Assert.Equal( "手机号无效", result.PhoneMessage ); + } + + /// + /// 测试解析IdCardAttribute特性 + /// + [Fact] + public void TestResolve_IdCard() { + var expression = ModelExpressionHelper.Create( "a", t => t.IdCard ); + var result = _resolver.Resolve( expression ); + Assert.True( result.IsIdCard ); + Assert.Equal( "身份证无效", result.IdCardMessage ); + } + + /// + /// 测试解析UrlAttribute特性 + /// + [Fact] + public void TestResolve_Url() { + var expression = ModelExpressionHelper.Create( "a", t => t.Url ); + var result = _resolver.Resolve( expression ); + Assert.True( result.IsUrl ); + Assert.Equal( "网址无效", result.UrlMessage ); + } + + /// + /// 测试解析RegularExpressionAttribute特性 + /// + [Fact] + public void TestResolve_RegularExpression() { + var expression = ModelExpressionHelper.Create( "a", t => t.Regular ); + var result = _resolver.Resolve( expression ); + Assert.True( result.IsRegularExpression ); + Assert.Equal( "a", result.Pattern ); + Assert.Equal( "正则表达式无效", result.RegularExpressionMessage ); + } + } +} diff --git a/test/Util.Ui.Tests/Extensions/TagHelperExtensionsTest.cs b/test/Util.Ui.Tests/Extensions/TagHelperExtensionsTest.cs new file mode 100644 index 000000000..d0cf2b578 --- /dev/null +++ b/test/Util.Ui.Tests/Extensions/TagHelperExtensionsTest.cs @@ -0,0 +1,107 @@ +using System.Collections.Generic; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Helpers; +using Util.Ui.Extensions; +using Util.Ui.Tests.Samples; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.Tests.Extensions { + /// + /// TagHelper扩展测试 + /// + public class TagHelperExtensionsTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + + /// + /// 测试初始化 + /// + public TagHelperExtensionsTest( ITestOutputHelper output ) { + _output = output; + } + + /// + /// 测试从items中获取值 + /// + [Fact] + public void TestGetValueFromItems_1() { + var items = new Dictionary { { "key", 1 } }; + var context = new TagHelperContext( new TagHelperAttributeList(), items, Id.Create() ); + Assert.Equal( 1, context.GetValueFromItems( "key" ) ); + } + + /// + /// 测试从items中获取值,泛型键 + /// + [Fact] + public void TestGetValueFromItems_2() { + var items = new Dictionary { { typeof( TestShareConfig ), new TestShareConfig( "1" ) } }; + var context = new TagHelperContext( new TagHelperAttributeList(), items, Id.Create() ); + Assert.Equal( "1", context.GetValueFromItems().Id ); + } + + /// + /// 测试从items中获取值,值为TagHelperAttribute类型 + /// + [Fact] + public void TestGetValueFromItems_3() { + var items = new Dictionary { { "key", new TagHelperAttribute( "", 1 ) } }; + var context = new TagHelperContext( new TagHelperAttributeList(), items, Id.Create() ); + Assert.Equal( 1, context.GetValueFromItems( "key" ) ); + } + + /// + /// 测试在items中设置值 + /// + [Fact] + public void TestSetValueToItems_1() { + var context = new TagHelperContext( new TagHelperAttributeList(), new Dictionary(), Id.Create() ); + context.SetValueToItems( "key", 1 ); + Assert.Equal( 1, context.GetValueFromItems( "key" ) ); + } + + /// + /// 测试在items中设置值,泛型 + /// + [Fact] + public void TestSetValueToItems_2() { + var context = new TagHelperContext( new TagHelperAttributeList(), new Dictionary(), Id.Create() ); + context.SetValueToItems( new TestShareConfig( "1" ) ); + Assert.Equal( "1", context.GetValueFromItems().Id ); + } + + /// + /// 测试在items中设置值,覆盖已存在的项 + /// + [Fact] + public void TestSetValueToItems_3() { + var items = new Dictionary { { "key", 1 } }; + var context = new TagHelperContext( new TagHelperAttributeList(), items, Id.Create() ); + context.SetValueToItems( "key", 2 ); + Assert.Equal( 2, context.GetValueFromItems( "key" ) ); + } + + /// + /// 测试从属性集合获取值 + /// + [Fact] + public void TestGetValueFromAttributes_1() { + TagHelperAttributeList attributes = new TagHelperAttributeList { { "a", 1 }, { "b", true } }; + TagHelperContext context = new TagHelperContext( attributes, new Dictionary(), Id.Create() ); + Assert.Equal( 1, context.GetValueFromAttributes( "a" ) ); + } + + /// + /// 测试从属性集合获取值 + /// + [Fact] + public void TestGetValueFromAttributes_2() { + TagHelperAttributeList attributes = new TagHelperAttributeList { new TagHelperAttribute( "a",1 ), { "b", true } }; + TagHelperContext context = new TagHelperContext( attributes, new Dictionary(), Id.Create() ); + Assert.Equal( 1, context.GetValueFromAttributes( "a" ) ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Buttons/ATagHelperTest.cs b/test/Util.Ui.Tests/Material/Buttons/ATagHelperTest.cs deleted file mode 100644 index a56267af8..000000000 --- a/test/Util.Ui.Tests/Material/Buttons/ATagHelperTest.cs +++ /dev/null @@ -1,137 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Buttons.TagHelpers; -using Util.Ui.Material.Enums; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Buttons { - /// - /// 链接测试 - /// - public class ATagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 按钮 - /// - private readonly ATagHelper _component; - - /// - /// 测试初始化 - /// - public ATagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new ATagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试文本 - /// - [Fact] - public void TestText() { - var attributes = new TagHelperAttributeList { { UiConst.Text, "a" } }; - var result = new String(); - result.Append( "a" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试样式 - /// - [Fact] - public void TestStyle() { - var attributes = new TagHelperAttributeList { { UiConst.Styles, ButtonStyle.Fab } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试颜色 - /// - [Fact] - public void TestColor() { - var attributes = new TagHelperAttributeList { { UiConst.Color, Color.Primary } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisabled() { - var attributes = new TagHelperAttributeList { { UiConst.Disabled, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试提示 - /// - [Fact] - public void TestTooltip() { - var attributes = new TagHelperAttributeList { { UiConst.Tooltip, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试路由链接地址 - /// - [Fact] - public void TestLink() { - var attributes = new TagHelperAttributeList { { UiConst.Link, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试单击事件 - /// - [Fact] - public void TestOnClick() { - var attributes = new TagHelperAttributeList { { UiConst.OnClick, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} diff --git a/test/Util.Ui.Tests/Material/Buttons/ATest.cs b/test/Util.Ui.Tests/Material/Buttons/ATest.cs deleted file mode 100644 index fd2e89c27..000000000 --- a/test/Util.Ui.Tests/Material/Buttons/ATest.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System.IO; -using System.Text.Encodings.Web; -using Util.Helpers; -using Util.Ui.Extensions; -using Util.Ui.Material.Buttons; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Extensions; -using Xunit; -using Xunit.Abstractions; - -namespace Util.Ui.Tests.Material.Buttons { - /// - /// 测试链接 - /// - public class ATest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 按钮 - /// - private readonly Anchor _component; - - /// - /// 测试初始化 - /// - public ATest( ITestOutputHelper output ) { - _output = output; - _component = new Anchor(); - } - - /// - /// 获取结果 - /// - private string GetResult( Anchor component ) { - component.WriteTo( new StringWriter(), HtmlEncoder.Default ); - var result = component.ToString(); - _output.WriteLine( result ); - return result; - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component ) ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Id( "a" ) ) ); - } - - /// - /// 测试文本 - /// - [Fact] - public void TestText() { - var result = new String(); - result.Append( "a" ); - Assert.Equal( result.ToString(), GetResult( _component.Text( "a" ) ) ); - } - - /// - /// 测试样式 - /// - [Fact] - public void TestStyle() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Style( ButtonStyle.Fab ) ) ); - } - - /// - /// 测试颜色 - /// - [Fact] - public void TestColor() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Color( Color.Primary ) ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisable() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Disable() ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisable_String() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Disable("a") ) ); - } - - /// - /// 测试提示 - /// - [Fact] - public void TestTooltip() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Tooltip( "a" ) ) ); - } - - /// - /// 测试路由链接地址 - /// - [Fact] - public void TestLink() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Link( "a" ) ) ); - } - - /// - /// 测试单击事件 - /// - [Fact] - public void TestOnClick() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.OnClick( "a" ) ) ); - } - } -} diff --git a/test/Util.Ui.Tests/Material/Buttons/ButtonTagHelperTest.cs b/test/Util.Ui.Tests/Material/Buttons/ButtonTagHelperTest.cs deleted file mode 100644 index fd6751a9f..000000000 --- a/test/Util.Ui.Tests/Material/Buttons/ButtonTagHelperTest.cs +++ /dev/null @@ -1,149 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Buttons.TagHelpers; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Buttons { - /// - /// 按钮测试 - /// - public class ButtonTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 按钮 - /// - private readonly ButtonTagHelper _component; - - /// - /// 测试初始化 - /// - public ButtonTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new ButtonTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试文本 - /// - [Fact] - public void TestText() { - var attributes = new TagHelperAttributeList { { UiConst.Text, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试类型 - /// - [Fact] - public void TestType() { - var attributes = new TagHelperAttributeList { { UiConst.Type, ButtonType.Reset } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试样式 - /// - [Fact] - public void TestStyle() { - var attributes = new TagHelperAttributeList { { UiConst.Styles, ButtonStyle.Fab } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试颜色 - /// - [Fact] - public void TestColor() { - var attributes = new TagHelperAttributeList { { UiConst.Color, Color.Primary } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisabled() { - var attributes = new TagHelperAttributeList { { UiConst.Disabled, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试提示 - /// - [Fact] - public void TestTooltip() { - var attributes = new TagHelperAttributeList { { UiConst.Tooltip, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试单击事件 - /// - [Fact] - public void TestOnClick() { - var attributes = new TagHelperAttributeList { { UiConst.OnClick, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置菜单标识 - /// - [Fact] - public void TestMenuId() { - var attributes = new TagHelperAttributeList { { MaterialConst.MenuId, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} diff --git a/test/Util.Ui.Tests/Material/Buttons/ButtonTest.cs b/test/Util.Ui.Tests/Material/Buttons/ButtonTest.cs deleted file mode 100644 index 932ed0a88..000000000 --- a/test/Util.Ui.Tests/Material/Buttons/ButtonTest.cs +++ /dev/null @@ -1,223 +0,0 @@ -using System.IO; -using System.Text.Encodings.Web; -using Util.Helpers; -using Util.Ui.Extensions; -using Util.Ui.Material.Buttons; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Extensions; -using Xunit; -using Xunit.Abstractions; - -namespace Util.Ui.Tests.Material.Buttons { - /// - /// 按钮测试 - /// - public class ButtonTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 按钮 - /// - private readonly Button _component; - - /// - /// 测试初始化 - /// - public ButtonTest( ITestOutputHelper output ) { - _output = output; - _component = new Button( new StringWriter() ); - } - - /// - /// 获取结果 - /// - private string GetResult( Button component ) { - component.WriteTo( new StringWriter(), HtmlEncoder.Default ); - var result = component.ToString(); - _output.WriteLine( result ); - return result; - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component ) ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Id( "a" ) ) ); - } - - /// - /// 测试文本 - /// - [Fact] - public void TestText() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Text( "a" ) ) ); - } - - /// - /// 测试设置重置类型 - /// - [Fact] - public void TestReset() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Reset() ) ); - } - - /// - /// 测试设置提交类型 - /// - [Fact] - public void TestSubmit() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Submit() ) ); - } - - /// - /// 测试样式 - /// - [Fact] - public void TestStyle() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Style( ButtonStyle.Fab ) ) ); - } - - /// - /// 测试颜色 - /// - [Fact] - public void TestColor() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Color( Color.Primary ) ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisable() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Disable() ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisable_String() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Disable( "a" ) ) ); - } - - /// - /// 测试提示 - /// - [Fact] - public void TestTooltip() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Tooltip( "a" ) ) ); - } - - /// - /// 测试单击事件 - /// - [Fact] - public void TestOnClick() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.OnClick( "a" ) ) ); - } - - /// - /// 测试设置菜单标识 - /// - [Fact] - public void TestMenuId() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Menu( "a" ) ) ); - } - - /// - /// 测试设置菜单标识 - 设置标识 - /// - [Fact] - public void TestMenuId_Id() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Menu( "a" ).Id( "b" ) ) ); - } - - /// - /// 测试设置菜单标识 - 设置文本 - /// - [Fact] - public void TestMenuId_Text() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Menu( "a" ).Text( "b" ) ) ); - } - - /// - /// 测试设置菜单标识 - 设置样式 - /// - [Fact] - public void TestMenuId_Style() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Menu( "a" ).Style( ButtonStyle.Fab ) ) ); - } - - /// - /// 测试设置菜单标识 - 设置颜色 - /// - [Fact] - public void TestMenuId_Color() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Menu( "a" ).Color( Color.Warn ) ) ); - } - - /// - /// 测试设置菜单标识 - 设置禁用 - /// - [Fact] - public void TestMenuId_Disable() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Menu( "a" ).Disable("b") ) ); - } - - /// - /// 测试设置菜单标识 - 设置提示 - /// - [Fact] - public void TestMenuId_Tooltip() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Menu( "a" ).Tooltip( "b" ) ) ); - } - } -} diff --git a/test/Util.Ui.Tests/Material/Cards/CardActionTagHelperTest.cs b/test/Util.Ui.Tests/Material/Cards/CardActionTagHelperTest.cs deleted file mode 100644 index 5aa075629..000000000 --- a/test/Util.Ui.Tests/Material/Cards/CardActionTagHelperTest.cs +++ /dev/null @@ -1,71 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.TagHelpers; -using Util.Ui.Material.Enums; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Cards { - /// - /// 卡片操作测试 - /// - public class CardActionTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 卡片操作 - /// - private readonly CardActionTagHelper _component; - - /// - /// 测试初始化 - /// - public CardActionTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new CardActionTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试对齐方式 - /// - [Fact] - public void TestAlign() { - var attributes = new TagHelperAttributeList { { UiConst.Align, XPosition.Right } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Cards/CardAvatarTagHelperTest.cs b/test/Util.Ui.Tests/Material/Cards/CardAvatarTagHelperTest.cs deleted file mode 100644 index 8b90f1fff..000000000 --- a/test/Util.Ui.Tests/Material/Cards/CardAvatarTagHelperTest.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Cards { - /// - /// 卡片头像测试 - /// - public class CardAvatarTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 卡片头像 - /// - private readonly CardAvatarTagHelper _component; - - /// - /// 测试初始化 - /// - public CardAvatarTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new CardAvatarTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试图片路径 - /// - [Fact] - public void TestSrc() { - var attributes = new TagHelperAttributeList { { UiConst.Src, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Cards/CardContentTagHelperTest.cs b/test/Util.Ui.Tests/Material/Cards/CardContentTagHelperTest.cs deleted file mode 100644 index 9ec8f6de4..000000000 --- a/test/Util.Ui.Tests/Material/Cards/CardContentTagHelperTest.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Cards { - /// - /// 卡片内容测试 - /// - public class CardContentTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 卡片内容 - /// - private readonly CardContentTagHelper _component; - - /// - /// 测试初始化 - /// - public CardContentTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new CardContentTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Cards/CardFooterTagHelperTest.cs b/test/Util.Ui.Tests/Material/Cards/CardFooterTagHelperTest.cs deleted file mode 100644 index e524a71c2..000000000 --- a/test/Util.Ui.Tests/Material/Cards/CardFooterTagHelperTest.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Cards { - /// - /// 卡片底部测试 - /// - public class CardFooterTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 卡片底部 - /// - private readonly CardFooterTagHelper _component; - - /// - /// 测试初始化 - /// - public CardFooterTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new CardFooterTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Cards/CardHeaderTagHelperTest.cs b/test/Util.Ui.Tests/Material/Cards/CardHeaderTagHelperTest.cs deleted file mode 100644 index 62591eb23..000000000 --- a/test/Util.Ui.Tests/Material/Cards/CardHeaderTagHelperTest.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Cards { - /// - /// 卡片头部测试 - /// - public class CardHeaderTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 卡片头部 - /// - private readonly CardHeaderTagHelper _component; - - /// - /// 测试初始化 - /// - public CardHeaderTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new CardHeaderTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Cards/CardImageTagHelperTest.cs b/test/Util.Ui.Tests/Material/Cards/CardImageTagHelperTest.cs deleted file mode 100644 index 3f6a2194e..000000000 --- a/test/Util.Ui.Tests/Material/Cards/CardImageTagHelperTest.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Cards { - /// - /// 卡片图片测试 - /// - public class CardImageTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 卡片图片 - /// - private readonly CardImageTagHelper _component; - - /// - /// 测试初始化 - /// - public CardImageTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new CardImageTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试图片路径 - /// - [Fact] - public void TestSrc() { - var attributes = new TagHelperAttributeList { { UiConst.Src, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Cards/CardSubTitleTagHelperTest.cs b/test/Util.Ui.Tests/Material/Cards/CardSubTitleTagHelperTest.cs deleted file mode 100644 index fbe21eaa4..000000000 --- a/test/Util.Ui.Tests/Material/Cards/CardSubTitleTagHelperTest.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Cards { - /// - /// 卡片副标题测试 - /// - public class CardSubTitleTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 卡片副标题 - /// - private readonly CardSubTitleTagHelper _component; - - /// - /// 测试初始化 - /// - public CardSubTitleTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new CardSubTitleTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Cards/CardTagHelperTest.cs b/test/Util.Ui.Tests/Material/Cards/CardTagHelperTest.cs deleted file mode 100644 index cdd443cee..000000000 --- a/test/Util.Ui.Tests/Material/Cards/CardTagHelperTest.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Cards { - /// - /// 卡片测试 - /// - public class CardTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 卡片 - /// - private readonly CardTagHelper _component; - - /// - /// 测试初始化 - /// - public CardTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new CardTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Cards/CardTitleGroupTagHelperTest.cs b/test/Util.Ui.Tests/Material/Cards/CardTitleGroupTagHelperTest.cs deleted file mode 100644 index cdbb07c74..000000000 --- a/test/Util.Ui.Tests/Material/Cards/CardTitleGroupTagHelperTest.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Cards { - /// - /// 卡片标题组测试 - /// - public class CardTitleGroupTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 卡片标题组 - /// - private readonly CardTitleGroupTagHelper _component; - - /// - /// 测试初始化 - /// - public CardTitleGroupTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new CardTitleGroupTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Cards/CardTitleImageTagHelperTest.cs b/test/Util.Ui.Tests/Material/Cards/CardTitleImageTagHelperTest.cs deleted file mode 100644 index acaa56bb4..000000000 --- a/test/Util.Ui.Tests/Material/Cards/CardTitleImageTagHelperTest.cs +++ /dev/null @@ -1,82 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.TagHelpers; -using Util.Ui.Material.Enums; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Cards { - /// - /// 卡片标题图片测试 - /// - public class CardTitleImageTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 卡片标题图片 - /// - private readonly CardTitleImageTagHelper _component; - - /// - /// 测试初始化 - /// - public CardTitleImageTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new CardTitleImageTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试图片路径 - /// - [Fact] - public void TestSrc() { - var attributes = new TagHelperAttributeList { { UiConst.Src, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试图片大小 - /// - [Fact] - public void TestSize() { - var attributes = new TagHelperAttributeList { { UiConst.Size, CardTitleImageSize.Large } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Cards/CardTitleTagHelperTest.cs b/test/Util.Ui.Tests/Material/Cards/CardTitleTagHelperTest.cs deleted file mode 100644 index 38870cb97..000000000 --- a/test/Util.Ui.Tests/Material/Cards/CardTitleTagHelperTest.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Cards.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Cards { - /// - /// 卡片标题测试 - /// - public class CardTitleTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 卡片标题 - /// - private readonly CardTitleTagHelper _component; - - /// - /// 测试初始化 - /// - public CardTitleTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new CardTitleTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Dividers/DividerTagHelperTest.cs b/test/Util.Ui.Tests/Material/Dividers/DividerTagHelperTest.cs deleted file mode 100644 index c54823832..000000000 --- a/test/Util.Ui.Tests/Material/Dividers/DividerTagHelperTest.cs +++ /dev/null @@ -1,82 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Dividers.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Dividers { - /// - /// 分隔线测试 - /// - public class DividerTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 分隔线 - /// - private readonly DividerTagHelper _component; - - /// - /// 测试初始化 - /// - public DividerTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new DividerTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置间距 - /// - [Fact] - public void TestInset() { - var attributes = new TagHelperAttributeList { { MaterialConst.Inset, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置垂直方向 - /// - [Fact] - public void TestVertical() { - var attributes = new TagHelperAttributeList { { UiConst.Vertical, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Forms/CheckBoxTest.cs b/test/Util.Ui.Tests/Material/Forms/CheckBoxTest.cs deleted file mode 100644 index 3c351b293..000000000 --- a/test/Util.Ui.Tests/Material/Forms/CheckBoxTest.cs +++ /dev/null @@ -1,155 +0,0 @@ -using System.IO; -using System.Text.Encodings.Web; -using Util.Ui.Configs; -using Util.Ui.Extensions; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Extensions; -using Util.Ui.Material.Forms; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Forms { - /// - /// 复选框测试 - /// - public class CheckBoxTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 复选框 - /// - private readonly CheckBox _component; - - /// - /// 测试初始化 - /// - public CheckBoxTest( ITestOutputHelper output ) { - _output = output; - _component = new CheckBox(); - Config.IsValidate = false; - } - - /// - /// 获取结果 - /// - private string GetResult( CheckBox component ) { - component.WriteTo( new StringWriter(), HtmlEncoder.Default ); - var result = component.ToString(); - _output.WriteLine( result ); - return result; - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component ) ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Id( "a" ) ) ); - } - - /// - /// 测试设置名称 - /// - [Fact] - public void TestName() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Name( "a" ) ) ); - } - - /// - /// 测试设置标签 - /// - [Fact] - public void TestText() { - var result = new String(); - result.Append( "a" ); - Assert.Equal( result.ToString(), GetResult( _component.Label( "a" ) ) ); - } - - /// - /// 测试标签位置 - /// - [Fact] - public void TestPosition() { - var result = new String(); - result.Append( "a" ); - Assert.Equal( result.ToString(), GetResult( _component.Label( "a", true ) ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisable() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Disable() ) ); - } - - /// - /// 测试颜色 - /// - [Fact] - public void TestColor() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Color( Color.Primary ) ) ); - } - - /// - /// 测试模型绑定 - /// - [Fact] - public void TestModel() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Model( "a" ) ) ); - } - - /// - /// 测试设置不确定样式 - /// - [Fact] - public void TestIndeterminate() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Indeterminate( "a" ) ) ); - } - - /// - /// 测试必填项 - /// - [Fact] - public void TestRequired() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Required() ) ); - } - - /// - /// 测试变更事件 - /// - [Fact] - public void TestOnChange() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.OnChange( "a" ) ) ); - } - } -} diff --git a/test/Util.Ui.Tests/Material/Forms/FormTest.cs b/test/Util.Ui.Tests/Material/Forms/FormTest.cs deleted file mode 100644 index 4d813c57c..000000000 --- a/test/Util.Ui.Tests/Material/Forms/FormTest.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.IO; -using Util.Ui.Extensions; -using Util.Ui.Material.Forms; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Forms { - /// - /// 表单测试 - /// - public class FormTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 表单 - /// - private readonly Form _form; - - /// - /// 测试初始化 - /// - public FormTest( ITestOutputHelper output ) { - _output = output; - _form = new Form( new StringWriter() ); - } - - /// - /// 获取结果 - /// - private string GetResult( Form form ) { - form.Begin(); - var result = form.ToString(); - _output.WriteLine( result ); - return result; - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "
                                                                      " ); - Assert.Equal( result.ToString(), GetResult( _form ) ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var result = new String(); - result.Append( "
                                                                      " ); - result.Append( "
                                                                      " ); - Assert.Equal( result.ToString(), GetResult( _form.Id( "a" ) ) ); - } - - /// - /// 测试提交事件 - /// - [Fact] - public void TestOnSubmit() { - var result = new String(); - result.Append( "
                                                                      " ); - result.Append( "
                                                                      " ); - Assert.Equal( result.ToString(), GetResult( _form.OnSubmit( "a" ) ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Forms/RadioTest.cs b/test/Util.Ui.Tests/Material/Forms/RadioTest.cs deleted file mode 100644 index 0006dcf78..000000000 --- a/test/Util.Ui.Tests/Material/Forms/RadioTest.cs +++ /dev/null @@ -1,225 +0,0 @@ -using System.IO; -using System.Text.Encodings.Web; -using Util.Biz.Enums; -using Util.Ui.Configs; -using Util.Ui.Extensions; -using Util.Ui.Material.Extensions; -using Util.Ui.Material.Forms; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Forms { - /// - /// 单选框测试 - /// - public class RadioTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 单选框 - /// - private readonly Radio _component; - - /// - /// 测试初始化 - /// - public RadioTest( ITestOutputHelper output ) { - _output = output; - _component = new Radio(); - Config.IsValidate = false; - } - - /// - /// 获取结果 - /// - private string GetResult( Radio component ) { - component.WriteTo( new StringWriter(), HtmlEncoder.Default ); - var result = component.ToString(); - _output.WriteLine( result ); - return result; - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component ) ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Id( "a" ) ) ); - } - - /// - /// 测试设置名称 - /// - [Fact] - public void TestName() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Name( "a" ) ) ); - } - - /// - /// 测试是否垂直布局 - /// - [Fact] - public void TestVertical() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Vertical() ) ); - } - - /// - /// 测试是否显示标签 - /// - [Fact] - public void TestShowLabel() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Label( true ) ) ); - } - - /// - /// 测试设置标签 - /// - [Fact] - public void TestLabel() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Label( "a" ) ) ); - } - - /// - /// 测试标签位置 - /// - [Fact] - public void TestPosition() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Label( "a", true ) ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisable() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Disable() ) ); - } - - /// - /// 测试设置Url - /// - [Fact] - public void TestUrl() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Url( "a" ) ) ); - } - - /// - /// 测试设置数据源 - /// - [Fact] - public void TestDataSource() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.DataSource( "a" ) ) ); - } - - /// - /// 测试模型绑定 - /// - [Fact] - public void TestModel() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Model( "a" ) ) ); - } - - /// - /// 测试必填项 - /// - [Fact] - public void TestRequired() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Required() ) ); - } - - /// - /// 测试变更事件 - /// - [Fact] - public void TestOnChange() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.OnChange( "a" ) ) ); - } - - /// - /// 测试添加项 - /// - [Fact] - public void TestAdd_1() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Add( "a", "1" ) ) ); - } - - /// - /// 测试添加项 - /// - [Fact] - public void TestAdd_2() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Add( new Item( "a", "1" ) ) ) ); - } - - /// - /// 测试绑定bool - /// - [Fact] - public void TestBool() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Bool() ) ); - } - - /// - /// 测试绑定枚举 - /// - [Fact] - public void TestEnum() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( (Radio)_component.Enum() ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Forms/SelectTest.cs b/test/Util.Ui.Tests/Material/Forms/SelectTest.cs deleted file mode 100644 index 894badd57..000000000 --- a/test/Util.Ui.Tests/Material/Forms/SelectTest.cs +++ /dev/null @@ -1,357 +0,0 @@ -using System.IO; -using System.Text.Encodings.Web; -using Util.Biz.Enums; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Extensions; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Extensions; -using Util.Ui.Material.Forms; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Forms { - /// - /// 下拉列表测试 - /// - public class SelectTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 下拉列表 - /// - private readonly Select _component; - - /// - /// 测试初始化 - /// - public SelectTest( ITestOutputHelper output ) { - _output = output; - _component = new Select(); - Config.IsValidate = false; - } - - /// - /// 获取结果 - /// - private string GetResult( Select component ) { - component.WriteTo( new StringWriter(), HtmlEncoder.Default ); - var result = component.ToString(); - _output.WriteLine( result ); - return result; - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component ) ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Id( "a" ) ) ); - } - - /// - /// 测试设置名称 - /// - [Fact] - public void TestName() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Name( "a" ) ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisable() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Disable() ) ); - } - - /// - /// 测试设置Url - /// - [Fact] - public void TestUrl() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Url("a") ) ); - } - - /// - /// 测试设置数据源 - /// - [Fact] - public void TestDataSource() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.DataSource( "a" ) ) ); - } - - /// - /// 测试设置占位提示 - /// - [Fact] - public void TestPlaceholder() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Placeholder( "a" ) ) ); - } - - /// - /// 测试设置占位提示 - /// - [Fact] - public void TestPlaceholder_Float() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Placeholder( "a", FloatType.Never ) ) ); - } - - /// - /// 测试设置起始提示 - /// - [Fact] - public void TestHint_Start() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Hint( "a" ) ) ); - } - - /// - /// 测试设置结束提示 - /// - [Fact] - public void TestHint_End() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Hint( "a",true ) ) ); - } - - /// - /// 测试设置前缀 - /// - [Fact] - public void TestPrefix() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Prefix( "a" ) ) ); - } - - /// - /// 测试设置后缀文本 - /// - [Fact] - public void TestSuffix_Text() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Suffix( "a" ) ) ); - } - - /// - /// 测试设置后缀FontAwesome图标 - /// - [Fact] - public void TestSuffix_FontAwesomeIcon() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Suffix( FontAwesomeIcon.Apple ) ) ); - } - - /// - /// 测试设置后缀Material图标 - /// - [Fact] - public void TestSuffix_MaterialIcon() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Suffix( MaterialIcon.Android ) ) ); - } - - /// - /// 测试设置后缀FontAwesome图标单击事件 - /// - [Fact] - public void TestSuffix_FontAwesomeIcon_Click() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Suffix( FontAwesomeIcon.Apple, "a" ) ) ); - } - - /// - /// 测试设置后缀Material图标单击事件 - /// - [Fact] - public void TestSuffix_MaterialIcon_Click() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Suffix( MaterialIcon.Android, "a" ) ) ); - } - - /// - /// 测试启用重置项 - /// - [Fact] - public void TestEnableResetOption_1() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.EnableResetOption( "a" ) ) ); - } - - /// - /// 测试启用重置项 - /// - [Fact] - public void TestEnableResetOption_2() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.EnableResetOption( false ) ) ); - } - - /// - /// 测试启用多选 - /// - [Fact] - public void TestMultiple() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Multiple() ) ); - } - - /// - /// 测试模型绑定 - /// - [Fact] - public void TestModel() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Model( "a" ) ) ); - } - - /// - /// 测试必填项 - /// - [Fact] - public void TestRequired() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Required("a") ) ); - } - - /// - /// 测试显示模板 - /// - [Fact] - public void TestTemplate() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Template( "a" ) ) ); - } - - /// - /// 测试变更事件 - /// - [Fact] - public void TestOnChange() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.OnChange( "a" ) ) ); - } - - /// - /// 测试获得焦点事件 - /// - [Fact] - public void TestOnFocus() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.OnFocus( "a" ) ) ); - } - - /// - /// 测试失去焦点事件 - /// - [Fact] - public void TestOnBlur() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.OnBlur( "a" ) ) ); - } - - /// - /// 测试键盘按下事件 - /// - [Fact] - public void TestOnKeydown() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.OnKeydown( "a" ) ) ); - } - - /// - /// 测试添加项 - /// - [Fact] - public void TestAdd_1() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Add( "a","1" ) ) ); - } - - /// - /// 测试添加项 - /// - [Fact] - public void TestAdd_2() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Add( new Item( "a", "1" ) ) ) ); - } - - /// - /// 测试绑定bool - /// - [Fact] - public void TestBool() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Bool() ) ); - } - - /// - /// 测试绑定枚举 - /// - [Fact] - public void TestEnum() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( (Select)_component.Enum() ) ); - } - } -} diff --git a/test/Util.Ui.Tests/Material/Forms/SlideToggleTest.cs b/test/Util.Ui.Tests/Material/Forms/SlideToggleTest.cs deleted file mode 100644 index 8110a9c98..000000000 --- a/test/Util.Ui.Tests/Material/Forms/SlideToggleTest.cs +++ /dev/null @@ -1,145 +0,0 @@ -using System.IO; -using System.Text.Encodings.Web; -using Util.Ui.Configs; -using Util.Ui.Extensions; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Extensions; -using Util.Ui.Material.Forms; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Forms { - /// - /// 滑动开关测试 - /// - public class SlideToggleTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 滑动开关 - /// - private readonly SlideToggle _component; - - /// - /// 测试初始化 - /// - public SlideToggleTest( ITestOutputHelper output ) { - _output = output; - _component = new SlideToggle(); - Config.IsValidate = false; - } - - /// - /// 获取结果 - /// - private string GetResult( SlideToggle component ) { - component.WriteTo( new StringWriter(), HtmlEncoder.Default ); - var result = component.ToString(); - _output.WriteLine( result ); - return result; - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component ) ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Id( "a" ) ) ); - } - - /// - /// 测试设置名称 - /// - [Fact] - public void TestName() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Name( "a" ) ) ); - } - - /// - /// 测试设置标签 - /// - [Fact] - public void TestText() { - var result = new String(); - result.Append( "a" ); - Assert.Equal( result.ToString(), GetResult( _component.Label( "a" ) ) ); - } - - /// - /// 测试标签位置 - /// - [Fact] - public void TestPosition() { - var result = new String(); - result.Append( "a" ); - Assert.Equal( result.ToString(), GetResult( _component.Label( "a", true ) ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisable() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Disable() ) ); - } - - /// - /// 测试颜色 - /// - [Fact] - public void TestColor() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Color( Color.Primary ) ) ); - } - - /// - /// 测试模型绑定 - /// - [Fact] - public void TestModel() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Model( "a" ) ) ); - } - - /// - /// 测试必填项 - /// - [Fact] - public void TestRequired() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Required() ) ); - } - - /// - /// 测试变更事件 - /// - [Fact] - public void TestOnChange() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.OnChange( "a" ) ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Forms/TagHelpers/CheckBoxTagHelperTest.cs b/test/Util.Ui.Tests/Material/Forms/TagHelpers/CheckBoxTagHelperTest.cs deleted file mode 100644 index d71f64db8..000000000 --- a/test/Util.Ui.Tests/Material/Forms/TagHelpers/CheckBoxTagHelperTest.cs +++ /dev/null @@ -1,162 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Forms.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Forms.TagHelpers { - /// - /// 复选框测试 - /// - public class CheckBoxTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 复选框 - /// - private readonly CheckBoxTagHelper _component; - - /// - /// 测试初始化 - /// - public CheckBoxTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new CheckBoxTagHelper(); - Config.IsValidate = false; - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null,TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置名称 - /// - [Fact] - public void TestName() { - var attributes = new TagHelperAttributeList { { UiConst.Name, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置标签 - /// - [Fact] - public void TestLabel() { - var attributes = new TagHelperAttributeList { { UiConst.Label, "a" } }; - var result = new String(); - result.Append( "a" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试标签位置 - /// - [Fact] - public void TestPosition() { - var attributes = new TagHelperAttributeList { { UiConst.Position, XPosition.Left } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisabled() { - var attributes = new TagHelperAttributeList { { UiConst.Disabled, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试颜色 - /// - [Fact] - public void TestColor() { - var attributes = new TagHelperAttributeList { { UiConst.Color, Color.Primary } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试模型绑定 - /// - [Fact] - public void TestModel() { - var attributes = new TagHelperAttributeList { { UiConst.Model, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置不确定样式 - /// - [Fact] - public void TestIndeterminate() { - var attributes = new TagHelperAttributeList { { UiConst.Indeterminate, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试必填项 - /// - [Fact] - public void TestRequired() { - var attributes = new TagHelperAttributeList { { UiConst.Required, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试变更事件 - /// - [Fact] - public void TestOnChange() { - var attributes = new TagHelperAttributeList { { UiConst.OnChange, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} diff --git a/test/Util.Ui.Tests/Material/Forms/TagHelpers/DatePickerTagHelperTest.cs b/test/Util.Ui.Tests/Material/Forms/TagHelpers/DatePickerTagHelperTest.cs deleted file mode 100644 index d6fc60dbb..000000000 --- a/test/Util.Ui.Tests/Material/Forms/TagHelpers/DatePickerTagHelperTest.cs +++ /dev/null @@ -1,339 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Forms.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Forms.TagHelpers { - /// - /// 日期选择框测试 - /// - public class DatePickerTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 文本框 - /// - private readonly DatePickerTagHelper _component; - - /// - /// 测试初始化 - /// - public DatePickerTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new DatePickerTagHelper(); - Config.IsValidate = false; - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置名称 - /// - [Fact] - public void TestName() { - var attributes = new TagHelperAttributeList { { UiConst.Name, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisabled() { - var attributes = new TagHelperAttributeList { { UiConst.Disabled, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试只读 - /// - [Fact] - public void TestReadOnly() { - var attributes = new TagHelperAttributeList { { UiConst.ReadOnly, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置占位提示 - /// - [Fact] - public void TestPlaceholder() { - var attributes = new TagHelperAttributeList { { UiConst.Placeholder, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置占位提示浮动位置 - /// - [Fact] - public void TestFloatPlaceholder() { - var attributes = new TagHelperAttributeList { { MaterialConst.FloatPlaceholder, FloatType.Never } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置起始提示 - /// - [Fact] - public void TestStartHint() { - var attributes = new TagHelperAttributeList { { MaterialConst.StartHint, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置结束提示 - /// - [Fact] - public void TestEndHint() { - var attributes = new TagHelperAttributeList { { MaterialConst.EndHint, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置前缀 - /// - [Fact] - public void TestPrefix() { - var attributes = new TagHelperAttributeList { { UiConst.Prefix, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置后缀文本 - /// - [Fact] - public void TestSuffixText() { - var attributes = new TagHelperAttributeList { { MaterialConst.SuffixText, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置后缀FontAwesome图标 - /// - [Fact] - public void TestSuffixFontAwesomeIcon() { - var attributes = new TagHelperAttributeList { { MaterialConst.SuffixFontAwesomeIcon, FontAwesomeIcon.Apple } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置后缀Material图标 - /// - [Fact] - public void TestSuffixMaterialIcon() { - var attributes = new TagHelperAttributeList { { MaterialConst.SuffixMaterialIcon, MaterialIcon.Android } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置后缀图标单击事件 - /// - [Fact] - public void TestOnSuffixIconClick() { - var attributes = new TagHelperAttributeList { { MaterialConst.OnSuffixIconClick, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试是否显示清除按钮 - /// - [Fact] - public void TestShowClearButton() { - var attributes = new TagHelperAttributeList { { MaterialConst.ShowClearButton, false } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - - /// - /// 测试模型绑定 - /// - [Fact] - public void TestModel() { - var attributes = new TagHelperAttributeList { { UiConst.Model, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试必填项 - /// - [Fact] - public void TestRequired() { - var attributes = new TagHelperAttributeList { { UiConst.Required, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试必填项错误消息 - /// - [Fact] - public void TestRequiredMessage() { - var attributes = new TagHelperAttributeList { { UiConst.RequiredMessage, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试变更事件 - /// - [Fact] - public void TestOnChange() { - var attributes = new TagHelperAttributeList { { UiConst.OnChange, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试获得焦点事件 - /// - [Fact] - public void TestOnFocus() { - var attributes = new TagHelperAttributeList { { UiConst.OnFocus, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试失去焦点事件 - /// - [Fact] - public void TestOnBlur() { - var attributes = new TagHelperAttributeList { { UiConst.OnBlur, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试键盘按下事件 - /// - [Fact] - public void TestOnKeydown() { - var attributes = new TagHelperAttributeList { { UiConst.OnKeydown, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试宽度 - /// - [Fact] - public void TestWidth() { - var attributes = new TagHelperAttributeList { { UiConst.Width, 1 } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试起始视图 - /// - [Fact] - public void TestStartView() { - var attributes = new TagHelperAttributeList { { MaterialConst.StartView, DateView.Year } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试触摸模式 - /// - [Fact] - public void TestTouchUi() { - var attributes = new TagHelperAttributeList { { MaterialConst.TouchUi, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试最小日期 - /// - [Fact] - public void TestMinDate() { - var attributes = new TagHelperAttributeList { { MaterialConst.MinDate, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试最大日期 - /// - [Fact] - public void TestMaxDate() { - var attributes = new TagHelperAttributeList { { MaterialConst.MaxDate, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Forms/TagHelpers/FormTagHelperTest.cs b/test/Util.Ui.Tests/Material/Forms/TagHelpers/FormTagHelperTest.cs deleted file mode 100644 index 570490432..000000000 --- a/test/Util.Ui.Tests/Material/Forms/TagHelpers/FormTagHelperTest.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Forms.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Forms.TagHelpers { - /// - /// 表单测试 - /// - public class FormTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 表单 - /// - private readonly FormTagHelper _component; - - /// - /// 测试初始化 - /// - public FormTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new FormTagHelper(); - Config.IsValidate = false; - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "
                                                                      " ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "
                                                                      " ); - result.Append( "
                                                                      " ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试提交事件 - /// - [Fact] - public void TestOnSubmit() { - var attributes = new TagHelperAttributeList { { UiConst.OnSubmit, "a" } }; - var result = new String(); - result.Append( "
                                                                      " ); - result.Append( "
                                                                      " ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Forms/TagHelpers/RadioTagHelperTest.cs b/test/Util.Ui.Tests/Material/Forms/TagHelpers/RadioTagHelperTest.cs deleted file mode 100644 index 069e1a70f..000000000 --- a/test/Util.Ui.Tests/Material/Forms/TagHelpers/RadioTagHelperTest.cs +++ /dev/null @@ -1,171 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Forms.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Forms.TagHelpers { - /// - /// 单选框测试 - /// - public class RadioTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 单选框 - /// - private readonly RadioTagHelper _component; - - /// - /// 测试初始化 - /// - public RadioTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new RadioTagHelper(); - Config.IsValidate = false; - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置名称 - /// - [Fact] - public void TestName() { - var attributes = new TagHelperAttributeList { { UiConst.Name, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试是否垂直布局 - /// - [Fact] - public void TestVertical() { - var attributes = new TagHelperAttributeList { { UiConst.Vertical, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置标签 - /// - [Fact] - public void TestLabel() { - var attributes = new TagHelperAttributeList { { UiConst.Label, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试文本位置 - /// - [Fact] - public void TestPosition() { - var attributes = new TagHelperAttributeList { { UiConst.Position, XPosition.Left } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisabled() { - var attributes = new TagHelperAttributeList { { UiConst.Disabled, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置Url - /// - [Fact] - public void TestUrl() { - var attributes = new TagHelperAttributeList { { UiConst.Url, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置数据源 - /// - [Fact] - public void TestDataSource() { - var attributes = new TagHelperAttributeList { { UiConst.DataSource, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试模型绑定 - /// - [Fact] - public void TestModel() { - var attributes = new TagHelperAttributeList { { UiConst.Model, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试必填项 - /// - [Fact] - public void TestRequired() { - var attributes = new TagHelperAttributeList { { UiConst.Required, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试变更事件 - /// - [Fact] - public void TestOnChange() { - var attributes = new TagHelperAttributeList { { UiConst.OnChange, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Forms/TagHelpers/SelectTagHelperTest.cs b/test/Util.Ui.Tests/Material/Forms/TagHelpers/SelectTagHelperTest.cs deleted file mode 100644 index 8330d4ae1..000000000 --- a/test/Util.Ui.Tests/Material/Forms/TagHelpers/SelectTagHelperTest.cs +++ /dev/null @@ -1,327 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Forms.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Forms.TagHelpers { - /// - /// 下拉列表测试 - /// - public class SelectTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 下拉列表 - /// - private readonly SelectTagHelper _component; - - /// - /// 测试初始化 - /// - public SelectTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new SelectTagHelper(); - Config.IsValidate = false; - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null,TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置名称 - /// - [Fact] - public void TestName() { - var attributes = new TagHelperAttributeList { { UiConst.Name, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisabled() { - var attributes = new TagHelperAttributeList { { UiConst.Disabled, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置Url - /// - [Fact] - public void TestUrl() { - var attributes = new TagHelperAttributeList { { UiConst.Url, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置数据源 - /// - [Fact] - public void TestDataSource() { - var attributes = new TagHelperAttributeList { { UiConst.DataSource, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置占位提示 - /// - [Fact] - public void TestPlaceholder() { - var attributes = new TagHelperAttributeList { { UiConst.Placeholder, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置占位提示浮动位置 - /// - [Fact] - public void TestFloatPlaceholder() { - var attributes = new TagHelperAttributeList { { MaterialConst.FloatPlaceholder, FloatType.Never } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置起始提示 - /// - [Fact] - public void TestStartHint() { - var attributes = new TagHelperAttributeList { { MaterialConst.StartHint, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置结束提示 - /// - [Fact] - public void TestEndHint() { - var attributes = new TagHelperAttributeList { { MaterialConst.EndHint, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置前缀 - /// - [Fact] - public void TestPrefix() { - var attributes = new TagHelperAttributeList { { UiConst.Prefix, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置后缀文本 - /// - [Fact] - public void TestSuffixText() { - var attributes = new TagHelperAttributeList { { MaterialConst.SuffixText, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置后缀FontAwesome图标 - /// - [Fact] - public void TestSuffixFontAwesomeIcon() { - var attributes = new TagHelperAttributeList { { MaterialConst.SuffixFontAwesomeIcon, FontAwesomeIcon.Apple } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置后缀Material图标 - /// - [Fact] - public void TestSuffixMaterialIcon() { - var attributes = new TagHelperAttributeList { { MaterialConst.SuffixMaterialIcon, MaterialIcon.Android } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置后缀图标单击事件 - /// - [Fact] - public void TestOnSuffixIconClick() { - var attributes = new TagHelperAttributeList { { MaterialConst.OnSuffixIconClick, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试启用重置项 - /// - [Fact] - public void TestEnableResetOption() { - var attributes = new TagHelperAttributeList { { MaterialConst.EnableResetOption, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置重置项文本 - /// - [Fact] - public void TestResetOptionText() { - var attributes = new TagHelperAttributeList { { MaterialConst.ResetOptionText, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试启用多选 - /// - [Fact] - public void TestMultiple() { - var attributes = new TagHelperAttributeList { { UiConst.Multiple, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试模型绑定 - /// - [Fact] - public void TestModel() { - var attributes = new TagHelperAttributeList { { UiConst.Model, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试必填项 - /// - [Fact] - public void TestRequired() { - var attributes = new TagHelperAttributeList { { UiConst.Required, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试必填项错误消息 - /// - [Fact] - public void TestRequiredMessage() { - var attributes = new TagHelperAttributeList { { UiConst.RequiredMessage, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试显示模板 - /// - [Fact] - public void TestTemplate() { - var attributes = new TagHelperAttributeList { { UiConst.Template, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试变更事件 - /// - [Fact] - public void TestOnChange() { - var attributes = new TagHelperAttributeList { { UiConst.OnChange, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试获得焦点事件 - /// - [Fact] - public void TestOnFocus() { - var attributes = new TagHelperAttributeList { { UiConst.OnFocus, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试失去焦点事件 - /// - [Fact] - public void TestOnBlur() { - var attributes = new TagHelperAttributeList { { UiConst.OnBlur, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试键盘按下事件 - /// - [Fact] - public void TestOnKeydown() { - var attributes = new TagHelperAttributeList { { UiConst.OnKeydown, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} diff --git a/test/Util.Ui.Tests/Material/Forms/TagHelpers/SlideToggleTagHelperTest.cs b/test/Util.Ui.Tests/Material/Forms/TagHelpers/SlideToggleTagHelperTest.cs deleted file mode 100644 index 67469d504..000000000 --- a/test/Util.Ui.Tests/Material/Forms/TagHelpers/SlideToggleTagHelperTest.cs +++ /dev/null @@ -1,149 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Forms.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Forms.TagHelpers { - /// - /// 滑动开关测试 - /// - public class SlideToggleTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 滑动开关 - /// - private readonly SlideToggleTagHelper _component; - - /// - /// 测试初始化 - /// - public SlideToggleTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new SlideToggleTagHelper(); - Config.IsValidate = false; - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置名称 - /// - [Fact] - public void TestName() { - var attributes = new TagHelperAttributeList { { UiConst.Name, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置标签 - /// - [Fact] - public void TestLabel() { - var attributes = new TagHelperAttributeList { { UiConst.Label, "a" } }; - var result = new String(); - result.Append( "a" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试标签位置 - /// - [Fact] - public void TestPosition() { - var attributes = new TagHelperAttributeList { { UiConst.Position, XPosition.Left } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisabled() { - var attributes = new TagHelperAttributeList { { UiConst.Disabled, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试颜色 - /// - [Fact] - public void TestColor() { - var attributes = new TagHelperAttributeList { { UiConst.Color, Color.Primary } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试模型绑定 - /// - [Fact] - public void TestModel() { - var attributes = new TagHelperAttributeList { { UiConst.Model, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试必填项 - /// - [Fact] - public void TestRequired() { - var attributes = new TagHelperAttributeList { { UiConst.Required, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试变更事件 - /// - [Fact] - public void TestOnChange() { - var attributes = new TagHelperAttributeList { { UiConst.OnChange, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Forms/TagHelpers/TextAreaTagHelperTest.cs b/test/Util.Ui.Tests/Material/Forms/TagHelpers/TextAreaTagHelperTest.cs deleted file mode 100644 index d2e3634b3..000000000 --- a/test/Util.Ui.Tests/Material/Forms/TagHelpers/TextAreaTagHelperTest.cs +++ /dev/null @@ -1,339 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Forms.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Forms.TagHelpers { - /// - /// 多行文本框测试 - /// - public class TextAreaTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 多行文本框 - /// - private readonly TextAreaTagHelper _component; - - /// - /// 测试初始化 - /// - public TextAreaTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new TextAreaTagHelper(); - Config.IsValidate = false; - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置名称 - /// - [Fact] - public void TestName() { - var attributes = new TagHelperAttributeList { { UiConst.Name, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisabled() { - var attributes = new TagHelperAttributeList { { UiConst.Disabled, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试只读 - /// - [Fact] - public void TestReadOnly() { - var attributes = new TagHelperAttributeList { { UiConst.ReadOnly, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置占位提示 - /// - [Fact] - public void TestPlaceholder() { - var attributes = new TagHelperAttributeList { { UiConst.Placeholder, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置占位提示浮动位置 - /// - [Fact] - public void TestFloatPlaceholder() { - var attributes = new TagHelperAttributeList { { MaterialConst.FloatPlaceholder, FloatType.Never } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置起始提示 - /// - [Fact] - public void TestStartHint() { - var attributes = new TagHelperAttributeList { { MaterialConst.StartHint, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置结束提示 - /// - [Fact] - public void TestEndHint() { - var attributes = new TagHelperAttributeList { { MaterialConst.EndHint, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置前缀 - /// - [Fact] - public void TestPrefix() { - var attributes = new TagHelperAttributeList { { UiConst.Prefix, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置后缀文本 - /// - [Fact] - public void TestSuffixText() { - var attributes = new TagHelperAttributeList { { MaterialConst.SuffixText, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置后缀FontAwesome图标 - /// - [Fact] - public void TestSuffixFontAwesomeIcon() { - var attributes = new TagHelperAttributeList { { MaterialConst.SuffixFontAwesomeIcon, FontAwesomeIcon.Apple } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置后缀Material图标 - /// - [Fact] - public void TestSuffixMaterialIcon() { - var attributes = new TagHelperAttributeList { { MaterialConst.SuffixMaterialIcon, MaterialIcon.Android } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置后缀图标单击事件 - /// - [Fact] - public void TestOnSuffixIconClick() { - var attributes = new TagHelperAttributeList { { MaterialConst.OnSuffixIconClick, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试是否显示清除按钮 - /// - [Fact] - public void TestShowClearButton() { - var attributes = new TagHelperAttributeList { { MaterialConst.ShowClearButton, false } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - - /// - /// 测试模型绑定 - /// - [Fact] - public void TestModel() { - var attributes = new TagHelperAttributeList { { UiConst.Model, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试必填项 - /// - [Fact] - public void TestRequired() { - var attributes = new TagHelperAttributeList { { UiConst.Required, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试必填项错误消息 - /// - [Fact] - public void TestRequiredMessage() { - var attributes = new TagHelperAttributeList { { UiConst.RequiredMessage, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试最小长度 - /// - [Fact] - public void TestMinLength() { - var attributes = new TagHelperAttributeList { { UiConst.MinLength, 3 } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试最小长度,添加指定错误消息 - /// - [Fact] - public void TestMinLength_Message() { - var attributes = new TagHelperAttributeList { { UiConst.MinLengthMessage, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试最大长度 - /// - [Fact] - public void TestMaxLength() { - var attributes = new TagHelperAttributeList { { UiConst.MaxLength, 3 } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试变更事件 - /// - [Fact] - public void TestOnChange() { - var attributes = new TagHelperAttributeList { { UiConst.OnChange, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试获得焦点事件 - /// - [Fact] - public void TestOnFocus() { - var attributes = new TagHelperAttributeList { { UiConst.OnFocus, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试失去焦点事件 - /// - [Fact] - public void TestOnBlur() { - var attributes = new TagHelperAttributeList { { UiConst.OnBlur, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试键盘按下事件 - /// - [Fact] - public void TestOnKeydown() { - var attributes = new TagHelperAttributeList { { UiConst.OnKeydown, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试最小行数 - /// - [Fact] - public void TestMinRows() { - var attributes = new TagHelperAttributeList { { MaterialConst.MinRows, 3 } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试最大行数 - /// - [Fact] - public void TestMaxRows() { - var attributes = new TagHelperAttributeList { { MaterialConst.MaxRows, 3 } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Forms/TagHelpers/TextBoxTagHelperTest.cs b/test/Util.Ui.Tests/Material/Forms/TagHelpers/TextBoxTagHelperTest.cs deleted file mode 100644 index 6cc771042..000000000 --- a/test/Util.Ui.Tests/Material/Forms/TagHelpers/TextBoxTagHelperTest.cs +++ /dev/null @@ -1,361 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Forms.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Forms.TagHelpers { - /// - /// 文本框测试 - /// - public class TextBoxTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 文本框 - /// - private readonly TextBoxTagHelper _component; - - /// - /// 测试初始化 - /// - public TextBoxTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new TextBoxTagHelper(); - Config.IsValidate = false; - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置名称 - /// - [Fact] - public void TestName() { - var attributes = new TagHelperAttributeList { { UiConst.Name, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisabled() { - var attributes = new TagHelperAttributeList { { UiConst.Disabled, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试只读 - /// - [Fact] - public void TestReadOnly() { - var attributes = new TagHelperAttributeList { { UiConst.ReadOnly, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置占位提示 - /// - [Fact] - public void TestPlaceholder() { - var attributes = new TagHelperAttributeList { { UiConst.Placeholder, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置占位提示浮动位置 - /// - [Fact] - public void TestFloatPlaceholder() { - var attributes = new TagHelperAttributeList { { MaterialConst.FloatPlaceholder, FloatType.Never } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置起始提示 - /// - [Fact] - public void TestStartHint() { - var attributes = new TagHelperAttributeList { { MaterialConst.StartHint, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置结束提示 - /// - [Fact] - public void TestEndHint() { - var attributes = new TagHelperAttributeList { { MaterialConst.EndHint, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置前缀 - /// - [Fact] - public void TestPrefix() { - var attributes = new TagHelperAttributeList { { UiConst.Prefix, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置后缀文本 - /// - [Fact] - public void TestSuffixText() { - var attributes = new TagHelperAttributeList { { MaterialConst.SuffixText, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置后缀FontAwesome图标 - /// - [Fact] - public void TestSuffixFontAwesomeIcon() { - var attributes = new TagHelperAttributeList { { MaterialConst.SuffixFontAwesomeIcon, FontAwesomeIcon.Apple } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置后缀Material图标 - /// - [Fact] - public void TestSuffixMaterialIcon() { - var attributes = new TagHelperAttributeList { { MaterialConst.SuffixMaterialIcon, MaterialIcon.Android } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置后缀图标单击事件 - /// - [Fact] - public void TestOnSuffixIconClick() { - var attributes = new TagHelperAttributeList { { MaterialConst.OnSuffixIconClick, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试是否显示清除按钮 - /// - [Fact] - public void TestShowClearButton() { - var attributes = new TagHelperAttributeList { { MaterialConst.ShowClearButton, false } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - - /// - /// 测试模型绑定 - /// - [Fact] - public void TestModel() { - var attributes = new TagHelperAttributeList { { UiConst.Model, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置为密码框 - /// - [Fact] - public void TestType_Password() { - var attributes = new TagHelperAttributeList { { UiConst.Type, TextBoxType.Password } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置为数字框 - /// - [Fact] - public void TestType_Number() { - var attributes = new TagHelperAttributeList { { UiConst.Type, TextBoxType.Number } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置为电子邮件框 - /// - [Fact] - public void TestType_Email() { - var attributes = new TagHelperAttributeList { { UiConst.Type, TextBoxType.Email } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置为电子邮件框 - /// - [Fact] - public void TestEmailMessage() { - var attributes = new TagHelperAttributeList { { UiConst.EmailMessage, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试必填项 - /// - [Fact] - public void TestRequired() { - var attributes = new TagHelperAttributeList { { UiConst.Required, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试必填项错误消息 - /// - [Fact] - public void TestRequiredMessage() { - var attributes = new TagHelperAttributeList { { UiConst.RequiredMessage, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试最小长度 - /// - [Fact] - public void TestMinLength() { - var attributes = new TagHelperAttributeList { { UiConst.MinLength, 3 } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试最小长度,添加指定错误消息 - /// - [Fact] - public void TestMinLength_Message() { - var attributes = new TagHelperAttributeList { { UiConst.MinLengthMessage, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试最大长度 - /// - [Fact] - public void TestMaxLength() { - var attributes = new TagHelperAttributeList { { UiConst.MaxLength, 3 } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试变更事件 - /// - [Fact] - public void TestOnChange() { - var attributes = new TagHelperAttributeList { { UiConst.OnChange, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试获得焦点事件 - /// - [Fact] - public void TestOnFocus() { - var attributes = new TagHelperAttributeList { { UiConst.OnFocus, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试失去焦点事件 - /// - [Fact] - public void TestOnBlur() { - var attributes = new TagHelperAttributeList { { UiConst.OnBlur, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试键盘按下事件 - /// - [Fact] - public void TestOnKeydown() { - var attributes = new TagHelperAttributeList { { UiConst.OnKeydown, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} diff --git a/test/Util.Ui.Tests/Material/Forms/TextBoxTest.cs b/test/Util.Ui.Tests/Material/Forms/TextBoxTest.cs deleted file mode 100644 index 37065e0e6..000000000 --- a/test/Util.Ui.Tests/Material/Forms/TextBoxTest.cs +++ /dev/null @@ -1,441 +0,0 @@ -using System; -using System.IO; -using System.Text.Encodings.Web; -using Util.Helpers; -using Util.Ui.Enums; -using Util.Ui.Extensions; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Extensions; -using Util.Ui.Material.Forms; -using Util.Webs; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Forms { - /// - /// 文本框测试 - /// - public class TextBoxTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 单引号 - /// - public const string Quote = HtmlEscape.SingleQuote27; - /// - /// 文本框 - /// - private readonly TextBox _component; - - /// - /// 测试初始化 - /// - public TextBoxTest( ITestOutputHelper output ) { - _output = output; - _component = new TextBox(); - } - - /// - /// 获取结果 - /// - private string GetResult( TextBox textBox ) { - textBox.WriteTo( new StringWriter(), HtmlEncoder.Default ); - var result = textBox.ToString(); - _output.WriteLine( result ); - return result; - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component ) ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Id( "a" ) ) ); - } - - /// - /// 测试添加名称 - /// - [Fact] - public void TestName() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Name( "a" ) ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisabled() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Disable() ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisabled_String() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Disable("a.B") ) ); - } - - /// - /// 测试只读 - /// - [Fact] - public void TestReadOnly() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.ReadOnly(false) ) ); - } - - /// - /// 测试只读 - /// - [Fact] - public void TestReadOnly_String() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.ReadOnly( "a" ) ) ); - } - - /// - /// 测试占位符 - /// - [Fact] - public void TestPlaceholder() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Placeholder( "a" ) ) ); - } - - /// - /// 测试设置占位提示浮动位置 - /// - [Fact] - public void TestPlaceholder_Float() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Placeholder( "a", FloatType.Never ) ) ); - } - - /// - /// 测试设置起始提示 - /// - [Fact] - public void TestHint_Start() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Hint( "a" ) ) ); - } - - /// - /// 测试设置结束提示 - /// - [Fact] - public void TestHint_End() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Hint( "a", true ) ) ); - } - - /// - /// 测试设置前缀 - /// - [Fact] - public void TestPrefix() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Prefix( "a" ) ) ); - } - - /// - /// 测试设置后缀文本 - /// - [Fact] - public void TestSuffix_Text() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Suffix( "a" ) ) ); - } - - /// - /// 测试设置后缀FontAwesome图标 - /// - [Fact] - public void TestSuffix_FontAwesomeIcon() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Suffix( FontAwesomeIcon.Apple ) ) ); - } - - /// - /// 测试设置后缀Material图标 - /// - [Fact] - public void TestSuffix_MaterialIcon() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Suffix( MaterialIcon.Android ) ) ); - } - - /// - /// 测试设置后缀FontAwesome图标单击事件 - /// - [Fact] - public void TestSuffix_FontAwesomeIcon_Click() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Suffix( FontAwesomeIcon.Apple, "a" ) ) ); - } - - /// - /// 测试设置后缀Material图标单击事件 - /// - [Fact] - public void TestSuffix_MaterialIcon_Click() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Suffix( MaterialIcon.Android, "a" ) ) ); - } - - /// - /// 测试是否显示清除按钮 - /// - [Fact] - public void TestShowClearButton() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.ShowClearButton( false ) ) ); - } - - /// - /// 测试模型绑定 - /// - [Fact] - public void TestModel() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Model( "a" ) ) ); - } - - /// - /// 测试必填项 - /// - [Fact] - public void TestRequired() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Required() ) ); - } - - /// - /// 测试必填项,添加指定错误消息 - /// - [Fact] - public void TestRequired_Message() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Required( "a" ) ) ); - } - - /// - /// 测试变更事件 - /// - [Fact] - public void TestOnChange() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.OnChange( "a" ) ) ); - } - - /// - /// 测试获得焦点事件 - /// - [Fact] - public void TestOnFocus() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.OnFocus( "a" ) ) ); - } - - /// - /// 测试失去焦点事件 - /// - [Fact] - public void TestOnBlur() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.OnBlur( "a" ) ) ); - } - - /// - /// 测试键盘按键事件 - /// - [Fact] - public void TestOnKeyUp() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.OnKeyup( "a" ) ) ); - } - - /// - /// 测试键盘按下事件 - /// - [Fact] - public void TestOnKeydown() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.OnKeydown( "a" ) ) ); - } - - /// - /// 测试设置为密码框 - /// - [Fact] - public void TestPassword() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Password() ) ); - } - - /// - /// 测试只能输入数字 - /// - [Fact] - public void TestNumber() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Number() ) ); - } - - /// - /// 测试电子邮件验证 - /// - [Fact] - public void TestEmail() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Email() ) ); - } - - /// - /// 测试设置电子邮件验证消息 - /// - [Fact] - public void TestEmail_Message() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Email("a") ) ); - } - - /// - /// 测试最小长度 - /// - [Fact] - public void TestMinLength() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.MinLength(3) ) ); - } - - /// - /// 测试最小长度,添加指定错误消息 - /// - [Fact] - public void TestMinLength_Message() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.MinLength( 3,"a" ) ) ); - } - - /// - /// 测试最大长度 - /// - [Fact] - public void TestMaxLength() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.MaxLength( 3 ) ) ); - } - - /// - /// 测试多行文本框 - /// - [Fact] - public void TestToTextArea_1() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.ToTextArea( 3) ) ); - } - - /// - /// 测试多行文本框 - /// - [Fact] - public void TestToTextArea_2() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.ToTextArea( 3,5 ) ) ); - } - - /// - /// 测试转换为日期选择框 - /// - [Fact] - public void TestToDatePicker_1() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.ToDatePicker() ) ); - } - - /// - /// 测试转换为日期选择框 - /// - [Fact] - public void TestToDatePicker_2() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.ToDatePicker("a") ) ); - } - - /// - /// 测试转换为日期选择框 - /// - [Fact] - public void TestToDatePicker_3() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.ToDatePicker( "a" ,"b") ) ); - } - - /// - /// 测试转换为日期选择框 - /// - [Fact] - public void TestToDatePicker_4() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.ToDatePicker( 1, DateView.Year, true ) ) ); - } - } -} diff --git a/test/Util.Ui.Tests/Material/Grids/GridColumnTagHelperTest.cs b/test/Util.Ui.Tests/Material/Grids/GridColumnTagHelperTest.cs deleted file mode 100644 index 8f3d12f41..000000000 --- a/test/Util.Ui.Tests/Material/Grids/GridColumnTagHelperTest.cs +++ /dev/null @@ -1,81 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Grids.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Grids { - /// - /// 栅格列测试 - /// - public class GridColumnTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 栅格列 - /// - private readonly GridColumnTagHelper _component; - - /// - /// 测试初始化 - /// - public GridColumnTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new GridColumnTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试合并列 - /// - [Fact] - public void TestColspan() { - var attributes = new TagHelperAttributeList { { UiConst.Colspan, 2 } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试合并行 - /// - [Fact] - public void TestRowspan() { - var attributes = new TagHelperAttributeList { { UiConst.Rowspan, 2 } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Grids/GridTagHelperTest.cs b/test/Util.Ui.Tests/Material/Grids/GridTagHelperTest.cs deleted file mode 100644 index cb1e4c9cd..000000000 --- a/test/Util.Ui.Tests/Material/Grids/GridTagHelperTest.cs +++ /dev/null @@ -1,115 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Grids.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Grids { - /// - /// 栅格测试 - /// - public class GridTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 栅格 - /// - private readonly GridTagHelper _component; - - /// - /// 测试初始化 - /// - public GridTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new GridTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试列数 - /// - [Fact] - public void TestColumns() { - var attributes = new TagHelperAttributeList { { UiConst.Columns, 2 } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试行高 - /// - [Fact] - public void TestRowHeight() { - var attributes = new TagHelperAttributeList { { UiConst.RowHeight, "2" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试行高 - /// - [Fact] - public void TestRowHeight_2() { - var attributes = new TagHelperAttributeList { { UiConst.RowHeight, "2em" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试单元格间距 - /// - [Fact] - public void TestGutterSize() { - var attributes = new TagHelperAttributeList { { MaterialConst.GutterSize, "2" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试单元格间距 - /// - [Fact] - public void TestGutterSize_2() { - var attributes = new TagHelperAttributeList { { MaterialConst.GutterSize, "2em" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Icons/IconTagHelperTest.cs b/test/Util.Ui.Tests/Material/Icons/IconTagHelperTest.cs deleted file mode 100644 index 3b58a38ce..000000000 --- a/test/Util.Ui.Tests/Material/Icons/IconTagHelperTest.cs +++ /dev/null @@ -1,255 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Helpers; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Material.Icons.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; - -namespace Util.Ui.Tests.Material.Icons { - /// - /// 图标测试 - /// - public class IconTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 图标 - /// - private readonly IconTagHelper _component; - - /// - /// 测试初始化 - /// - public IconTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new IconTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - Assert.Empty( GetResult() ); - } - - /// - /// 测试设置FontAwesome图标 - /// - [Fact] - public void TestFontAwesomeIcon() { - var attributes = new TagHelperAttributeList { { UiConst.FontAwesomeIcon, FontAwesomeIcon.Android } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试添加属性 - /// - [Fact] - public void TestAttribute() { - var contextAttributes = new TagHelperAttributeList { { UiConst.FontAwesomeIcon, FontAwesomeIcon.Android } }; - var outputAttributes = new TagHelperAttributeList { { "a", "1" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( contextAttributes, outputAttributes ) ); - } - - /// - /// 测试设置FontAwesome图标的class - /// - [Fact] - public void TestClass_FontAwesome() { - var contextAttributes = new TagHelperAttributeList { { UiConst.FontAwesomeIcon, FontAwesomeIcon.Android } }; - var outputAttributes = new TagHelperAttributeList { { "class", "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( contextAttributes, outputAttributes ) ); - } - - /// - /// 测试设置Material图标的class - /// - [Fact] - public void TestClass_Material() { - var contextAttributes = new TagHelperAttributeList { { UiConst.MaterialIcon, MaterialIcon.Android } }; - var outputAttributes = new TagHelperAttributeList { { "class", "a" } }; - var result = new String(); - result.Append( "android" ); - Assert.Equal( result.ToString(), GetResult( contextAttributes, outputAttributes ) ); - } - - /// - /// 测试设置style - /// - [Fact] - public void TestStyle() { - var contextAttributes = new TagHelperAttributeList { { UiConst.MaterialIcon, MaterialIcon.Android } }; - var outputAttributes = new TagHelperAttributeList { { "style", "a" } }; - var result = new String(); - result.Append( "android" ); - Assert.Equal( result.ToString(), GetResult( contextAttributes, outputAttributes ) ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { "id", "a" }, { UiConst.FontAwesomeIcon, FontAwesomeIcon.Android } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置Material图标 - /// - [Fact] - public void TestMaterialIcon() { - var attributes = new TagHelperAttributeList { { UiConst.MaterialIcon, MaterialIcon.Android } }; - var result = new String(); - result.Append( "android" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置FontAwesome图标大小 - /// - [Fact] - public void TestIconSize_FontAwesome() { - var attributes = new TagHelperAttributeList { { UiConst.Size, IconSize.Large2X }, { UiConst.FontAwesomeIcon, FontAwesomeIcon.Android } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置Material图标大小 - /// - [Fact] - public void TestIconSize_Material() { - var attributes = new TagHelperAttributeList { { UiConst.Size, IconSize.Large2X }, { UiConst.MaterialIcon, MaterialIcon.Android } }; - var result = new String(); - result.Append( "android" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置FontAwesome图标旋转 - /// - [Fact] - public void TestSpin_FontAwesome_True() { - var attributes = new TagHelperAttributeList { { UiConst.Spin, true }, { UiConst.FontAwesomeIcon, FontAwesomeIcon.Android } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置FontAwesome图标旋转 - /// - [Fact] - public void TestSpin_FontAwesome_False() { - var attributes = new TagHelperAttributeList { { UiConst.Spin, false }, { UiConst.FontAwesomeIcon, FontAwesomeIcon.Android } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置Material图标旋转 - /// - [Fact] - public void TestSpin_Material() { - var attributes = new TagHelperAttributeList { { UiConst.Spin, true }, { UiConst.MaterialIcon, MaterialIcon.Android } }; - var result = new String(); - result.Append( "android" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置FontAwesome图标旋转 - /// - [Fact] - public void TestRotate_FontAwesome() { - var attributes = new TagHelperAttributeList { { UiConst.Rotate, RotateType.Rotate180 }, { UiConst.FontAwesomeIcon, FontAwesomeIcon.Android } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置Material图标旋转 - /// - [Fact] - public void TestRotate_Material() { - var attributes = new TagHelperAttributeList { { UiConst.Rotate, RotateType.Rotate180 }, { UiConst.MaterialIcon, MaterialIcon.Android } }; - var result = new String(); - result.Append( "android" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置FontAwesome子图标 - /// - [Fact] - public void TestChild() { - var attributes = new TagHelperAttributeList { { UiConst.Child, FontAwesomeIcon.Twitter }, { UiConst.FontAwesomeIcon, FontAwesomeIcon.SquareO } }; - var result = new String(); - result.Append( "" ); - result.Append( "" ); - result.Append( "" ); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置FontAwesome子图标class - /// - [Fact] - public void TestChildClass() { - var attributes = new TagHelperAttributeList { - { UiConst.Child, FontAwesomeIcon.Twitter }, - { UiConst.ChildClass, "a" }, - { UiConst.FontAwesomeIcon, FontAwesomeIcon.SquareO } - }; - var result = new String(); - result.Append( "" ); - result.Append( "" ); - result.Append( "" ); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置FontAwesome子图标,设置大小 - /// - [Fact] - public void TestChild_Size() { - var attributes = new TagHelperAttributeList { - { UiConst.Child, FontAwesomeIcon.Twitter }, - { UiConst.Size, IconSize.Large2X }, - { UiConst.FontAwesomeIcon, FontAwesomeIcon.SquareO } - }; - var result = new String(); - result.Append( "" ); - result.Append( "" ); - result.Append( "" ); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} diff --git a/test/Util.Ui.Tests/Material/Icons/IconTest.cs b/test/Util.Ui.Tests/Material/Icons/IconTest.cs deleted file mode 100644 index 9610a6a30..000000000 --- a/test/Util.Ui.Tests/Material/Icons/IconTest.cs +++ /dev/null @@ -1,198 +0,0 @@ -using System.IO; -using System.Text.Encodings.Web; -using Util.Helpers; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Extensions; -using Util.Ui.Material.Icons; -using Xunit; -using Xunit.Abstractions; - -namespace Util.Ui.Tests.Material.Icons { - /// - /// 图标测试 - /// - public class IconTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 图标 - /// - private readonly Icon _icon; - - /// - /// 测试初始化 - /// - public IconTest( ITestOutputHelper output ) { - _output = output; - _icon = new Icon(); - Config.IsValidate = false; - } - - /// - /// 获取结果 - /// - private string GetResult( Icon icon ) { - icon.WriteTo( new StringWriter(), HtmlEncoder.Default ); - var result = icon.ToString(); - _output.WriteLine( result ); - return result; - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - Assert.Empty( GetResult( _icon ) ); - } - - /// - /// 测试设置Material图标 - /// - [Fact] - public void TestMaterial() { - var result = new String(); - result.Append( "android" ); - Assert.Equal( result.ToString(), GetResult( _icon.Icon( MaterialIcon.Android ) ) ); - } - - /// - /// 测试设置class - /// - [Fact] - public void TestClass() { - var result = new String(); - result.Append( "android" ); - Assert.Equal( result.ToString(), GetResult( _icon.Icon( MaterialIcon.Android ).Class("a") ) ); - } - - /// - /// 测试设置style - /// - [Fact] - public void TestStyle() { - var result = new String(); - result.Append( "android" ); - Assert.Equal( result.ToString(), GetResult( _icon.Icon( MaterialIcon.Android ).Style( "a" ) ) ); - } - - /// - /// 测试添加属性 - /// - [Fact] - public void TestAttribute() { - var result = new String(); - result.Append( "android" ); - Assert.Equal( result.ToString(), GetResult( _icon.Icon( MaterialIcon.Android ).Attribute( "a","1" ) ) ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var result = new String(); - result.Append( "android" ); - Assert.Equal( result.ToString(), GetResult( _icon.Icon( MaterialIcon.Android ).Id( "a" ) ) ); - } - - /// - /// 测试设置FontAwesome图标 - /// - [Fact] - public void TestFontAwesome() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _icon.Icon( FontAwesomeIcon.Android ) ) ); - } - - /// - /// 测试设置图标大小 - /// - [Fact] - public void TestIconSize() { - var result = new String(); - result.Append( "android" ); - Assert.Equal( result.ToString(), GetResult( _icon.Icon( MaterialIcon.Android ).Size(IconSize.Large2X) ) ); - } - - /// - /// 测试设置FontAwesome图标旋转 - /// - [Fact] - public void TestSpin_FontAwesome_True() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _icon.Icon( FontAwesomeIcon.Android ).Spin() ) ); - } - - /// - /// 测试设置FontAwesome图标旋转 - /// - [Fact] - public void TestSpin_FontAwesome_False() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _icon.Icon( FontAwesomeIcon.Android ).Spin().Spin( false ) ) ); - } - - /// - /// 测试设置Material图标旋转 - /// - [Fact] - public void TestSpin_Material() { - var result = new String(); - result.Append( "android" ); - Assert.Equal( result.ToString(), GetResult( _icon.Icon( MaterialIcon.Android ).Spin() ) ); - } - - /// - /// 测试设置FontAwesome图标旋转 - /// - [Fact] - public void TestRotate_FontAwesome() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _icon.Icon( FontAwesomeIcon.Android ).Rotate( RotateType.Rotate180 ) ) ); - } - - /// - /// 测试设置Material图标旋转 - /// - [Fact] - public void TestRotate_Material() { - var result = new String(); - result.Append( "android" ); - Assert.Equal( result.ToString(), GetResult( _icon.Icon( MaterialIcon.Android ).Rotate( RotateType.Rotate180 ) ) ); - } - - /// - /// 测试设置FontAwesome子图标 - /// - [Fact] - public void TestChild() { - var result = new String(); - result.Append( "" ); - result.Append( "" ); - result.Append( "" ); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _icon.Icon( FontAwesomeIcon.SquareO ).Child( FontAwesomeIcon.Twitter ) ) ); - } - - /// - /// 测试设置FontAwesome子图标 - /// - [Fact] - public void TestChild_Class() { - var result = new String(); - result.Append( "" ); - result.Append( "" ); - result.Append( "" ); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _icon.Icon( FontAwesomeIcon.SquareO ).Child( FontAwesomeIcon.Twitter,"a" ) ) ); - } - } -} diff --git a/test/Util.Ui.Tests/Material/Menus/MenuDataTest.cs b/test/Util.Ui.Tests/Material/Menus/MenuDataTest.cs deleted file mode 100644 index f029c8890..000000000 --- a/test/Util.Ui.Tests/Material/Menus/MenuDataTest.cs +++ /dev/null @@ -1,87 +0,0 @@ -using Util.Ui.Material.Menus.Datas; -using Xunit; - -namespace Util.Ui.Tests.Material.Menus { - /// - /// 菜单数据测试 - /// - public class MenuDataTest { - /// - /// 菜单节点集合 - /// - private readonly MenuNodeCollection _nodes; - - /// - /// 测试初始化 - /// - public MenuDataTest() { - _nodes = new MenuNodeCollection(); - } - - /// - /// 转换为菜单数据列表 - /// - [Fact] - public void TestToMenuDatas_1() { - _nodes.Nodes.Add( new MenuNode { Label = "a" } ); - var datas = _nodes.ToMenuDatas(); - Assert.Single( datas ); - Assert.Single( datas[0].Items ); - Assert.Equal( _nodes.RootId, datas[0].Id ); - Assert.Equal( "a", datas[0].Items[0].Label ); - } - - /// - /// 转换为菜单数据列表 - /// - [Fact] - public void TestToMenuDatas_2() { - _nodes.Nodes.Add( new MenuNode { Label = "a" } ); - _nodes.Nodes.Add( new MenuNode { Label = "b" } ); - var datas = _nodes.ToMenuDatas(); - Assert.Single( datas ); - Assert.Equal( 2, datas[0].Items.Count ); - Assert.Equal( "a", datas[0].Items[0].Label ); - Assert.Equal( "b", datas[0].Items[1].Label ); - } - - /// - /// 转换为菜单数据列表 - /// - [Fact] - public void TestToMenuDatas_3() { - _nodes.Nodes.Add( new MenuNode { Id = "1", Label = "a" } ); - _nodes.Nodes.Add( new MenuNode { Id = "2", Label = "b" } ); - _nodes.Nodes.Add( new MenuNode { ParentId = "2", Label = "c" } ); - var datas = _nodes.ToMenuDatas(); - Assert.Equal( 2, datas.Count ); - Assert.Equal( 2, datas[0].Items.Count ); - Assert.Single( datas[1].Items ); - Assert.Equal( "a", datas[0].Items[0].Label ); - Assert.Equal( "b", datas[0].Items[1].Label ); - Assert.Equal( "c", datas[1].Items[0].Label ); - Assert.True( datas[0].Items[0].MenuId.IsEmpty() ); - Assert.Equal( "m_2", datas[0].Items[1].MenuId ); - } - - /// - /// 转换为菜单数据列表 - /// - [Fact] - public void TestToMenuDatas_4() { - _nodes.Nodes.Add( new MenuNode { Id = "1", Label = "a" } ); - _nodes.Nodes.Add( new MenuNode { Id = "2", Label = "b" } ); - _nodes.Nodes.Add( new MenuNode { Id = "3", ParentId = "2", Label = "c" } ); - _nodes.Nodes.Add( new MenuNode { ParentId = "3", Label = "d" } ); - var datas = _nodes.ToMenuDatas(); - Assert.Equal( 3, datas.Count ); - Assert.Equal( 2, datas[0].Items.Count ); - Assert.Single( datas[1].Items ); - Assert.Single( datas[2].Items ); - Assert.True( datas[0].Items[0].MenuId.IsEmpty() ); - Assert.Equal( "m_2", datas[0].Items[1].MenuId ); - Assert.Equal( "m_3", datas[1].Items[0].MenuId ); - Assert.True( datas[2].Items[0].MenuId.IsEmpty() ); - } - } -} diff --git a/test/Util.Ui.Tests/Material/Menus/MenuItemTagHelperTest.cs b/test/Util.Ui.Tests/Material/Menus/MenuItemTagHelperTest.cs deleted file mode 100644 index d6462f147..000000000 --- a/test/Util.Ui.Tests/Material/Menus/MenuItemTagHelperTest.cs +++ /dev/null @@ -1,127 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Menus.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Menus { - /// - /// 菜单项测试 - /// - public class MenuItemTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 菜单项 - /// - private readonly MenuItemTagHelper _component; - - /// - /// 测试初始化 - /// - public MenuItemTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new MenuItemTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试标签 - /// - [Fact] - public void TestLabel() { - var attributes = new TagHelperAttributeList { { UiConst.Label, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试Material图标 - /// - [Fact] - public void TestMaterialIcon() { - var attributes = new TagHelperAttributeList { { UiConst.MaterialIcon, MaterialIcon.Add } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试FontAwesome图标 - /// - [Fact] - public void TestFontAwesomeIcon() { - var attributes = new TagHelperAttributeList { { UiConst.FontAwesomeIcon, FontAwesomeIcon.Bus } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisabled() { - var attributes = new TagHelperAttributeList { { UiConst.Disabled, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试路由链接地址 - /// - [Fact] - public void TestLink() { - var attributes = new TagHelperAttributeList { { UiConst.Link, "a" }, { UiConst.Label, "b" }, { UiConst.MaterialIcon, MaterialIcon.Add } }; - var result = new String(); - result.Append( "addb" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试单击事件 - /// - [Fact] - public void TestOnClick() { - var attributes = new TagHelperAttributeList { { UiConst.OnClick, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置子菜单 - /// - [Fact] - public void TestMenuId() { - var attributes = new TagHelperAttributeList { { MaterialConst.MenuId, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Menus/MenuItemTest.cs b/test/Util.Ui.Tests/Material/Menus/MenuItemTest.cs deleted file mode 100644 index 385e4ee05..000000000 --- a/test/Util.Ui.Tests/Material/Menus/MenuItemTest.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System.IO; -using System.Text.Encodings.Web; -using Util.Ui.Enums; -using Util.Ui.Extensions; -using Util.Ui.Material.Extensions; -using Util.Ui.Material.Menus; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Menus { - /// - /// 菜单项测试 - /// - public class MenuItemTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 菜单项 - /// - private readonly MenuItem _component; - - /// - /// 测试初始化 - /// - public MenuItemTest( ITestOutputHelper output ) { - _output = output; - _component = new MenuItem(); - } - - /// - /// 获取结果 - /// - private string GetResult( MenuItem component ) { - component.WriteTo( new StringWriter(), HtmlEncoder.Default ); - var result = component.ToString(); - _output.WriteLine( result ); - return result; - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component ) ); - } - - /// - /// 测试标签 - /// - [Fact] - public void TestLabel() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Label("a") ) ); - } - - /// - /// 测试Material图标 - /// - [Fact] - public void TestMaterialIcon() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Icon( MaterialIcon.Add ) ) ); - } - - /// - /// 测试FontAwesome图标 - /// - [Fact] - public void TestFontAwesomeIcon() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Icon( FontAwesomeIcon.Bus ) ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisabled() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Disable( "a" ) ) ); - } - - /// - /// 测试路由链接地址 - /// - [Fact] - public void TestLink() { - var result = new String(); - result.Append( "addb" ); - Assert.Equal( result.ToString(), GetResult( _component.Link("a").Label( "b" ).Icon( MaterialIcon.Add ) ) ); - } - - /// - /// 测试单击事件 - /// - [Fact] - public void TestOnClick() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.OnClick( "a" ) ) ); - } - - /// - /// 测试设置子菜单 - /// - [Fact] - public void TestMenu() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Menu( "a" ) ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Menus/MenuTagHelperTest.cs b/test/Util.Ui.Tests/Material/Menus/MenuTagHelperTest.cs deleted file mode 100644 index 24f1776fe..000000000 --- a/test/Util.Ui.Tests/Material/Menus/MenuTagHelperTest.cs +++ /dev/null @@ -1,93 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Menus.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Menus { - /// - /// 菜单测试 - /// - public class MenuTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 菜单 - /// - private readonly MenuTagHelper _component; - - /// - /// 测试初始化 - /// - public MenuTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new MenuTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试X位置 - /// - [Fact] - public void TestXPosition() { - var attributes = new TagHelperAttributeList { { UiConst.XPosition, XPosition.Right } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试Y位置 - /// - [Fact] - public void TestYPosition() { - var attributes = new TagHelperAttributeList { { UiConst.YPosition, YPosition.Below } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置重叠 - /// - [Fact] - public void TestOverlap() { - var attributes = new TagHelperAttributeList { { UiConst.Overlap, false } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Menus/MenuTest.cs b/test/Util.Ui.Tests/Material/Menus/MenuTest.cs deleted file mode 100644 index 6bffda967..000000000 --- a/test/Util.Ui.Tests/Material/Menus/MenuTest.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System.IO; -using Util.Ui.Extensions; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Extensions; -using Util.Ui.Material.Menus; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Menus { - /// - /// 菜单测试 - /// - public class MenuTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 菜单 - /// - private readonly Menu _component; - - /// - /// 测试初始化 - /// - public MenuTest( ITestOutputHelper output ) { - _output = output; - _component = new Menu( new StringWriter() ); - } - - /// - /// 获取结果 - /// - private string GetResult( Menu component ) { - component.Begin(); - var result = component.ToString(); - _output.WriteLine( result ); - return result; - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component ) ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Id( "a" ) ) ); - } - - /// - /// 测试X位置 - /// - [Fact] - public void TestPosition_X() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Position(XPosition.Right) ) ); - } - - /// - /// 测试Y位置 - /// - [Fact] - public void TestPosition_Y() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Position( null,YPosition.Below ) ) ); - } - - /// - /// 测试设置重叠 - /// - [Fact] - public void TestOverlap() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Overlap() ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Panels/AccordionTagHelperTest.cs b/test/Util.Ui.Tests/Material/Panels/AccordionTagHelperTest.cs deleted file mode 100644 index 2c2cd214c..000000000 --- a/test/Util.Ui.Tests/Material/Panels/AccordionTagHelperTest.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Panels.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Panels { - /// - /// 手风琴测试 - /// - public class AccordionTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 手风琴 - /// - private readonly AccordionTagHelper _component; - - /// - /// 测试初始化 - /// - public AccordionTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new AccordionTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置多展开状态 - /// - [Fact] - public void TestMultiple() { - var attributes = new TagHelperAttributeList { { UiConst.Multiple, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试显示模式 - /// - [Fact] - public void TestDisplayMode() { - var attributes = new TagHelperAttributeList { { MaterialConst.DisplayMode, AccordionDisplayMode.Flat } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Panels/PanelActionTagHelperTest.cs b/test/Util.Ui.Tests/Material/Panels/PanelActionTagHelperTest.cs deleted file mode 100644 index d5f2b3cfc..000000000 --- a/test/Util.Ui.Tests/Material/Panels/PanelActionTagHelperTest.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Panels.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Panels { - /// - /// 面板操作测试 - /// - public class PanelActionTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 面板操作 - /// - private readonly PanelActionTagHelper _component; - - /// - /// 测试初始化 - /// - public PanelActionTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new PanelActionTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Panels/PanelContentTagHelperTest.cs b/test/Util.Ui.Tests/Material/Panels/PanelContentTagHelperTest.cs deleted file mode 100644 index 2b9f7cbb7..000000000 --- a/test/Util.Ui.Tests/Material/Panels/PanelContentTagHelperTest.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Panels.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Panels { - /// - /// 面板内容测试 - /// - public class PanelContentTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 面板内容 - /// - private readonly PanelContentTagHelper _component; - - /// - /// 测试初始化 - /// - public PanelContentTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new PanelContentTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Panels/PanelDescriptionTagHelperTest.cs b/test/Util.Ui.Tests/Material/Panels/PanelDescriptionTagHelperTest.cs deleted file mode 100644 index 0720c9de8..000000000 --- a/test/Util.Ui.Tests/Material/Panels/PanelDescriptionTagHelperTest.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Panels.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Panels { - /// - /// 面板描述测试 - /// - public class PanelDescriptionTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 面板描述 - /// - private readonly PanelDescriptionTagHelper _component; - - /// - /// 测试初始化 - /// - public PanelDescriptionTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new PanelDescriptionTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Panels/PanelHeaderTagHelperTest.cs b/test/Util.Ui.Tests/Material/Panels/PanelHeaderTagHelperTest.cs deleted file mode 100644 index 3c4d58d48..000000000 --- a/test/Util.Ui.Tests/Material/Panels/PanelHeaderTagHelperTest.cs +++ /dev/null @@ -1,82 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Panels.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Panels { - /// - /// 面板头部测试 - /// - public class PanelHeaderTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 面板头部 - /// - private readonly PanelHeaderTagHelper _component; - - /// - /// 测试初始化 - /// - public PanelHeaderTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new PanelHeaderTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试面板折叠时标题的高度 - /// - [Fact] - public void TestCollapsedHeight() { - var attributes = new TagHelperAttributeList { { MaterialConst.CollapsedHeight, 1 } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试面板展开时标题的高度 - /// - [Fact] - public void TestExpandedHeight() { - var attributes = new TagHelperAttributeList { { MaterialConst.ExpandedHeight, 1 } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Panels/PanelTagHelperTest.cs b/test/Util.Ui.Tests/Material/Panels/PanelTagHelperTest.cs deleted file mode 100644 index f1bd7738f..000000000 --- a/test/Util.Ui.Tests/Material/Panels/PanelTagHelperTest.cs +++ /dev/null @@ -1,115 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Panels.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Panels { - /// - /// 面板测试 - /// - public class PanelTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 面板 - /// - private readonly PanelTagHelper _component; - - /// - /// 测试初始化 - /// - public PanelTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new PanelTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试打开事件 - /// - [Fact] - public void TestOnOpen() { - var attributes = new TagHelperAttributeList { { UiConst.OnOpen, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试关闭事件 - /// - [Fact] - public void TestOnClose() { - var attributes = new TagHelperAttributeList { { UiConst.OnClose, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试隐藏折叠开关 - /// - [Fact] - public void TestHideToggle() { - var attributes = new TagHelperAttributeList { { MaterialConst.HideToggle, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试展开 - /// - [Fact] - public void TestExpanded() { - var attributes = new TagHelperAttributeList { { UiConst.Expanded, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisabled() { - var attributes = new TagHelperAttributeList { { UiConst.Disabled, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Panels/PanelTitleTagHelperTest.cs b/test/Util.Ui.Tests/Material/Panels/PanelTitleTagHelperTest.cs deleted file mode 100644 index 23f9ae486..000000000 --- a/test/Util.Ui.Tests/Material/Panels/PanelTitleTagHelperTest.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Panels.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Panels { - /// - /// 面板标题测试 - /// - public class PanelTitleTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 面板标题 - /// - private readonly PanelTitleTagHelper _component; - - /// - /// 测试初始化 - /// - public PanelTitleTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new PanelTitleTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/SideNavs/DrawerContainerTagHelperTest.cs b/test/Util.Ui.Tests/Material/SideNavs/DrawerContainerTagHelperTest.cs deleted file mode 100644 index 6a303643a..000000000 --- a/test/Util.Ui.Tests/Material/SideNavs/DrawerContainerTagHelperTest.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.SideNavs.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.SideNavs { - /// - /// 侧边栏导航容器测试 - /// - public class DrawerContainerTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 侧边栏导航容器 - /// - private readonly DrawerContainerTagHelper _component; - - /// - /// 测试初始化 - /// - public DrawerContainerTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new DrawerContainerTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试自动调整大小 - /// - [Fact] - public void TestAutoSize() { - var attributes = new TagHelperAttributeList { { UiConst.AutoSize, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/SideNavs/DrawerContentTagHelperTest.cs b/test/Util.Ui.Tests/Material/SideNavs/DrawerContentTagHelperTest.cs deleted file mode 100644 index 470109066..000000000 --- a/test/Util.Ui.Tests/Material/SideNavs/DrawerContentTagHelperTest.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.SideNavs.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.SideNavs { - /// - /// 侧边栏导航内容测试 - /// - public class DrawerContentTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 侧边栏导航内容 - /// - private readonly DrawerContentTagHelper _component; - - /// - /// 测试初始化 - /// - public DrawerContentTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new DrawerContentTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/SideNavs/DrawerTagHelperTest.cs b/test/Util.Ui.Tests/Material/SideNavs/DrawerTagHelperTest.cs deleted file mode 100644 index 3b4a83588..000000000 --- a/test/Util.Ui.Tests/Material/SideNavs/DrawerTagHelperTest.cs +++ /dev/null @@ -1,116 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.SideNavs.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.SideNavs { - /// - /// 侧边栏导航测试 - /// - public class DrawerTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 侧边栏导航 - /// - private readonly DrawerTagHelper _component; - - /// - /// 测试初始化 - /// - public DrawerTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new DrawerTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置位置 - /// - [Fact] - public void TestPosition() { - var attributes = new TagHelperAttributeList { { UiConst.Position, XPosition.Right } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置打开 - /// - [Fact] - public void TestOpened() { - var attributes = new TagHelperAttributeList { { UiConst.Opened, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试打开模式 - /// - [Fact] - public void TestMode() { - var attributes = new TagHelperAttributeList { { UiConst.Mode, SideNavMode.Side } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试禁用关闭 - /// - [Fact] - public void TestDisableClose() { - var attributes = new TagHelperAttributeList { { MaterialConst.DisableClose, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置宽度 - /// - [Fact] - public void TestWidth() { - var attributes = new TagHelperAttributeList { { UiConst.Width, 50 } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/SideNavs/SideNavContainerTagHelperTest.cs b/test/Util.Ui.Tests/Material/SideNavs/SideNavContainerTagHelperTest.cs deleted file mode 100644 index 8e090723b..000000000 --- a/test/Util.Ui.Tests/Material/SideNavs/SideNavContainerTagHelperTest.cs +++ /dev/null @@ -1,81 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.SideNavs.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.SideNavs { - /// - /// 侧边栏导航容器测试 - /// - public class SideNavContainerTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 侧边栏导航容器 - /// - private readonly SideNavContainerTagHelper _component; - - /// - /// 测试初始化 - /// - public SideNavContainerTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new SideNavContainerTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置全屏 - /// - [Fact] - public void TestFullscreen() { - var attributes = new TagHelperAttributeList { { UiConst.Fullscreen, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试自动调整大小 - /// - [Fact] - public void TestAutoSize() { - var attributes = new TagHelperAttributeList { { UiConst.AutoSize, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/SideNavs/SideNavContentTagHelperTest.cs b/test/Util.Ui.Tests/Material/SideNavs/SideNavContentTagHelperTest.cs deleted file mode 100644 index c72a804ea..000000000 --- a/test/Util.Ui.Tests/Material/SideNavs/SideNavContentTagHelperTest.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.SideNavs.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.SideNavs { - /// - /// 侧边栏导航内容测试 - /// - public class SideNavContentTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 侧边栏导航内容 - /// - private readonly SideNavContentTagHelper _component; - - /// - /// 测试初始化 - /// - public SideNavContentTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new SideNavContentTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/SideNavs/SideNavTagHelperTest.cs b/test/Util.Ui.Tests/Material/SideNavs/SideNavTagHelperTest.cs deleted file mode 100644 index 10bdffa68..000000000 --- a/test/Util.Ui.Tests/Material/SideNavs/SideNavTagHelperTest.cs +++ /dev/null @@ -1,116 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.SideNavs.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.SideNavs { - /// - /// 侧边栏导航测试 - /// - public class SideNavTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 侧边栏导航 - /// - private readonly SideNavTagHelper _component; - - /// - /// 测试初始化 - /// - public SideNavTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new SideNavTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置位置 - /// - [Fact] - public void TestPosition() { - var attributes = new TagHelperAttributeList { { UiConst.Position, XPosition.Right } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置打开 - /// - [Fact] - public void TestOpened() { - var attributes = new TagHelperAttributeList { { UiConst.Opened, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试打开模式 - /// - [Fact] - public void TestMode() { - var attributes = new TagHelperAttributeList { { UiConst.Mode, SideNavMode.Side } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试禁用关闭 - /// - [Fact] - public void TestDisableClose() { - var attributes = new TagHelperAttributeList { { MaterialConst.DisableClose, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置宽度 - /// - [Fact] - public void TestWidth() { - var attributes = new TagHelperAttributeList { { UiConst.Width, 50 } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Tabs/TabGroupTagHelperTest.cs b/test/Util.Ui.Tests/Material/Tabs/TabGroupTagHelperTest.cs deleted file mode 100644 index 383ba17aa..000000000 --- a/test/Util.Ui.Tests/Material/Tabs/TabGroupTagHelperTest.cs +++ /dev/null @@ -1,138 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Commons.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Tabs.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Tabs { - /// - /// 选项卡组测试 - /// - public class TabGroupTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 选项卡组 - /// - private readonly TabGroupTagHelper _component; - - /// - /// 测试初始化 - /// - public TabGroupTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new TabGroupTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置背景色 - /// - [Fact] - public void TestBackgroundColor() { - var attributes = new TagHelperAttributeList { { UiConst.BackgroundColor, Color.Primary } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置主题色 - /// - [Fact] - public void TestColor() { - var attributes = new TagHelperAttributeList { { UiConst.Color, Color.Primary } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置固定高度 - /// - [Fact] - public void TestHeight() { - var attributes = new TagHelperAttributeList { { UiConst.Height, 200 } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置动态高度 - /// - [Fact] - public void TestDynamicHeight() { - var attributes = new TagHelperAttributeList { { MaterialConst.DynamicHeight, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试拉伸选项卡 - /// - [Fact] - public void TestStretch() { - var attributes = new TagHelperAttributeList { { UiConst.Stretch, true } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试选中索引 - /// - [Fact] - public void TestSelectedIndex() { - var attributes = new TagHelperAttributeList { { UiConst.SelectedIndex, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试标题位置 - /// - [Fact] - public void TestHeaderPosition() { - var attributes = new TagHelperAttributeList { { MaterialConst.HeaderPosition, YPosition.Below } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Tabs/TabGroupTest.cs b/test/Util.Ui.Tests/Material/Tabs/TabGroupTest.cs deleted file mode 100644 index c637c26ee..000000000 --- a/test/Util.Ui.Tests/Material/Tabs/TabGroupTest.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System.IO; -using Util.Ui.Extensions; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Extensions; -using Util.Ui.Material.Tabs; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Tabs { - /// - /// 选项卡组测试 - /// - public class TabGroupTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 选项卡组 - /// - private readonly TabGroup _component; - - /// - /// 测试初始化 - /// - public TabGroupTest( ITestOutputHelper output ) { - _output = output; - _component = new TabGroup( new StringWriter() ); - } - - /// - /// 获取结果 - /// - private string GetResult( TabGroup component ) { - component.Begin(); - var result = component.ToString(); - _output.WriteLine( result ); - return result; - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component ) ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Id( "a" ) ) ); - } - - /// - /// 测试设置背景色 - /// - [Fact] - public void TestBackgroundColor() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.BackgroundColor( Color.Primary ) ) ); - } - - /// - /// 测试设置主题色 - /// - [Fact] - public void TestColor() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Color( Color.Primary ) ) ); - } - - /// - /// 测试设置固定高度 - /// - [Fact] - public void TestHeight() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Height(200) ) ); - } - - /// - /// 测试设置动态高度 - /// - [Fact] - public void TestDynamicHeight() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.DynamicHeight() ) ); - } - - /// - /// 测试拉伸选项卡 - /// - [Fact] - public void TestStretch() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Stretch() ) ); - } - - /// - /// 测试选中索引 - /// - [Fact] - public void TestSelect() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Select( "a" ) ) ); - } - - /// - /// 测试标题位置 - /// - [Fact] - public void TestHeaderPosition() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.HeaderPosition( YPosition.Below ) ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Tabs/TabLinkTagHelperTest.cs b/test/Util.Ui.Tests/Material/Tabs/TabLinkTagHelperTest.cs deleted file mode 100644 index f23ae3568..000000000 --- a/test/Util.Ui.Tests/Material/Tabs/TabLinkTagHelperTest.cs +++ /dev/null @@ -1,128 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Helpers; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Material.Tabs.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Tabs { - /// - /// 链接选项卡测试 - /// - public class TabLinkTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 链接选项卡 - /// - private readonly TabLinkTagHelper _component; - - /// - /// 测试初始化 - /// - public TabLinkTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new TabLinkTagHelper(); - Id.SetId( "id" ); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "b" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试路由链接 - /// - [Fact] - public void TestLink() { - var attributes = new TagHelperAttributeList { { UiConst.Link, "b" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试添加标签 - /// - [Fact] - public void TestLabel() { - var attributes = new TagHelperAttributeList { { UiConst.Label, "label" } }; - var result = new String(); - result.Append( "label" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试Material图标 - /// - [Fact] - public void TestMaterialIcon() { - var attributes = new TagHelperAttributeList { { UiConst.MaterialIcon, MaterialIcon.Add } }; - var result = new String(); - result.Append( "add" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试FontAwesome图标 - /// - [Fact] - public void TestFontAwesomeIcon() { - var attributes = new TagHelperAttributeList { { UiConst.FontAwesomeIcon, FontAwesomeIcon.Bus } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试标签和Material图标 - /// - [Fact] - public void TestLabel_MaterialIcon() { - var attributes = new TagHelperAttributeList { { UiConst.Label, "a" }, { UiConst.MaterialIcon, MaterialIcon.Add } }; - var result = new String(); - result.Append( "adda" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisabled() { - var attributes = new TagHelperAttributeList { { UiConst.Disabled, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Tabs/TabLinkTest.cs b/test/Util.Ui.Tests/Material/Tabs/TabLinkTest.cs deleted file mode 100644 index f082d857b..000000000 --- a/test/Util.Ui.Tests/Material/Tabs/TabLinkTest.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System.IO; -using System.Text.Encodings.Web; -using Util.Helpers; -using Util.Ui.Enums; -using Util.Ui.Extensions; -using Util.Ui.Material.Extensions; -using Util.Ui.Material.Tabs; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Tabs { - /// - /// 链接选项卡测试 - /// - public class TabLinkTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 链接选项卡 - /// - private readonly TabLink _component; - - /// - /// 测试初始化 - /// - public TabLinkTest( ITestOutputHelper output ) { - _output = output; - _component = new TabLink(); - Id.SetId( "id" ); - } - - /// - /// 获取结果 - /// - private string GetResult( TabLink component ) { - component.WriteTo( new StringWriter(), HtmlEncoder.Default ); - var result = component.ToString(); - _output.WriteLine( result ); - return result; - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component ) ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Id( "b" ) ) ); - } - - /// - /// 测试路由链接 - /// - [Fact] - public void TestLink() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Link( "b" ) ) ); - } - - /// - /// 测试添加标签 - /// - [Fact] - public void TestLabel() { - var result = new String(); - result.Append( "label" ); - Assert.Equal( result.ToString(), GetResult( _component.Label( "label" ) ) ); - } - - /// - /// 测试Material图标 - /// - [Fact] - public void TestMaterialIcon() { - var result = new String(); - result.Append( "add" ); - Assert.Equal( result.ToString(), GetResult( _component.Icon( MaterialIcon.Add ) ) ); - } - - /// - /// 测试FontAwesome图标 - /// - [Fact] - public void TestFontAwesomeIcon() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Icon( FontAwesomeIcon.Bus ) ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisabled() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Disable( "a" ) ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Tabs/TabNavigationTagHelperTest.cs b/test/Util.Ui.Tests/Material/Tabs/TabNavigationTagHelperTest.cs deleted file mode 100644 index dc113c14f..000000000 --- a/test/Util.Ui.Tests/Material/Tabs/TabNavigationTagHelperTest.cs +++ /dev/null @@ -1,82 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Tabs.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Tabs { - /// - /// 导航选项卡测试 - /// - public class TabNavigationTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 导航选项卡 - /// - private readonly TabNavigationTagHelper _component; - - /// - /// 测试初始化 - /// - public TabNavigationTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new TabNavigationTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置背景色 - /// - [Fact] - public void TestBackgroundColor() { - var attributes = new TagHelperAttributeList { { UiConst.BackgroundColor, Color.Primary } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置主题色 - /// - [Fact] - public void TestColor() { - var attributes = new TagHelperAttributeList { { UiConst.Color, Color.Primary } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Tabs/TabNavigationTest.cs b/test/Util.Ui.Tests/Material/Tabs/TabNavigationTest.cs deleted file mode 100644 index 6b36a3a15..000000000 --- a/test/Util.Ui.Tests/Material/Tabs/TabNavigationTest.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.IO; -using Util.Ui.Extensions; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Extensions; -using Util.Ui.Material.Tabs; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Tabs { - /// - /// 导航选项卡测试 - /// - public class TabNavigationTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 导航选项卡 - /// - private readonly TabNavigation _component; - - /// - /// 测试初始化 - /// - public TabNavigationTest( ITestOutputHelper output ) { - _output = output; - _component = new TabNavigation( new StringWriter() ); - } - - /// - /// 获取结果 - /// - private string GetResult( TabNavigation component ) { - component.Begin(); - var result = component.ToString(); - _output.WriteLine( result ); - return result; - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component ) ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Id( "a" ) ) ); - } - - /// - /// 测试设置背景色 - /// - [Fact] - public void TestBackgroundColor() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.BackgroundColor( Color.Primary ) ) ); - } - - /// - /// 测试设置主题色 - /// - [Fact] - public void TestColor() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Color( Color.Primary ) ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Tabs/TabTagHelperTest.cs b/test/Util.Ui.Tests/Material/Tabs/TabTagHelperTest.cs deleted file mode 100644 index 0a7c2f524..000000000 --- a/test/Util.Ui.Tests/Material/Tabs/TabTagHelperTest.cs +++ /dev/null @@ -1,140 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Enums; -using Util.Ui.Material.Tabs.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Tabs { - /// - /// 选项卡测试 - /// - public class TabTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 选项卡 - /// - private readonly TabTagHelper _component; - - /// - /// 测试初始化 - /// - public TabTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new TabTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试添加标签 - /// - [Fact] - public void TestLabel() { - var attributes = new TagHelperAttributeList { { UiConst.Label, "a" } }; - var result = new String(); - result.Append( "a" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试Material图标 - /// - [Fact] - public void TestMaterialIcon() { - var attributes = new TagHelperAttributeList { { UiConst.MaterialIcon, MaterialIcon.Add } }; - var result = new String(); - result.Append( "add" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试FontAwesome图标 - /// - [Fact] - public void TestFontAwesomeIcon() { - var attributes = new TagHelperAttributeList { { UiConst.FontAwesomeIcon, FontAwesomeIcon.Bus } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试标签和Material图标 - /// - [Fact] - public void TestLabel_MaterialIcon() { - var attributes = new TagHelperAttributeList { { UiConst.Label, "a" },{ UiConst.MaterialIcon, MaterialIcon.Add } }; - var result = new String(); - result.Append( "adda" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisabled() { - var attributes = new TagHelperAttributeList { { UiConst.Disabled, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试延迟加载 - /// - [Fact] - public void TestLazyLoad() { - var attributes = new TagHelperAttributeList { { UiConst.LazyLoad,true } }; - var content = new DefaultTagHelperContent(); - content.AppendHtml( "a" ); - var result = new String(); - result.Append( "a" ); - Assert.Equal( result.ToString(), GetResult( attributes,content: content ) ); - } - - /// - /// 测试非延迟加载 - /// - [Fact] - public void TestNoLazyLoad() { - var content = new DefaultTagHelperContent(); - content.AppendHtml( "a" ); - var result = new String(); - result.Append( "a" ); - Assert.Equal( result.ToString(), GetResult( content: content ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Tabs/TabTest.cs b/test/Util.Ui.Tests/Material/Tabs/TabTest.cs deleted file mode 100644 index 1d362df61..000000000 --- a/test/Util.Ui.Tests/Material/Tabs/TabTest.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System.IO; -using Util.Ui.Enums; -using Util.Ui.Extensions; -using Util.Ui.Material.Extensions; -using Util.Ui.Material.Tabs; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Tabs { - /// - /// 选项卡测试 - /// - public class TabTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 选项卡 - /// - private readonly Tab _component; - - /// - /// 测试初始化 - /// - public TabTest( ITestOutputHelper output ) { - _output = output; - _component = new Tab( new StringWriter() ); - } - - /// - /// 获取结果 - /// - private string GetResult( Tab component ) { - component.Begin(); - var result = component.ToString(); - _output.WriteLine( result ); - return result; - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component ) ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Id( "a" ) ) ); - } - - /// - /// 测试添加标签 - /// - [Fact] - public void TestLabel() { - var result = new String(); - result.Append( "a" ); - Assert.Equal( result.ToString(), GetResult( _component.Label( "a" ) ) ); - } - - /// - /// 测试Material图标 - /// - [Fact] - public void TestMaterialIcon() { - var result = new String(); - result.Append( "add" ); - Assert.Equal( result.ToString(), GetResult( _component.Icon( MaterialIcon.Add ) ) ); - } - - /// - /// 测试FontAwesome图标 - /// - [Fact] - public void TestFontAwesomeIcon() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Icon( FontAwesomeIcon.Bus ) ) ); - } - - /// - /// 测试禁用 - /// - [Fact] - public void TestDisabled() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( _component.Disable( "a" ) ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Toolbars/ToolbarRowTagHelperTest.cs b/test/Util.Ui.Tests/Material/Toolbars/ToolbarRowTagHelperTest.cs deleted file mode 100644 index 18aec6dc5..000000000 --- a/test/Util.Ui.Tests/Material/Toolbars/ToolbarRowTagHelperTest.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Toolbars.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Toolbars { - /// - /// 工具栏项测试 - /// - public class ToolbarRowTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 工具栏项 - /// - private readonly ToolbarRowTagHelper _component; - - /// - /// 测试初始化 - /// - public ToolbarRowTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new ToolbarRowTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Toolbars/ToolbarTagHelperTest.cs b/test/Util.Ui.Tests/Material/Toolbars/ToolbarTagHelperTest.cs deleted file mode 100644 index cce109d16..000000000 --- a/test/Util.Ui.Tests/Material/Toolbars/ToolbarTagHelperTest.cs +++ /dev/null @@ -1,71 +0,0 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Ui.Configs; -using Util.Ui.Material.Enums; -using Util.Ui.Material.Toolbars.TagHelpers; -using Util.Ui.Tests.XUnitHelpers; -using Xunit; -using Xunit.Abstractions; -using String = Util.Helpers.String; - -namespace Util.Ui.Tests.Material.Toolbars { - /// - /// 工具栏测试 - /// - public class ToolbarTagHelperTest { - /// - /// 输出工具 - /// - private readonly ITestOutputHelper _output; - /// - /// 工具栏 - /// - private readonly ToolbarTagHelper _component; - - /// - /// 测试初始化 - /// - public ToolbarTagHelperTest( ITestOutputHelper output ) { - _output = output; - _component = new ToolbarTagHelper(); - } - - /// - /// 获取结果 - /// - private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { - return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); - } - - /// - /// 测试默认输出 - /// - [Fact] - public void TestDefault() { - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult() ); - } - - /// - /// 测试添加标识 - /// - [Fact] - public void TestId() { - var attributes = new TagHelperAttributeList { { UiConst.Id, "a" } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - - /// - /// 测试设置颜色 - /// - [Fact] - public void TestColor() { - var attributes = new TagHelperAttributeList { { UiConst.Color, Color.Primary } }; - var result = new String(); - result.Append( "" ); - Assert.Equal( result.ToString(), GetResult( attributes ) ); - } - } -} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Razor/PartViewPathResolverTest.cs b/test/Util.Ui.Tests/Razor/PartViewPathResolverTest.cs new file mode 100644 index 000000000..4d877009d --- /dev/null +++ b/test/Util.Ui.Tests/Razor/PartViewPathResolverTest.cs @@ -0,0 +1,61 @@ +using System.Text; +using Moq; +using Util.Ui.Razor.Internal; +using Xunit; + +namespace Util.Ui.Tests.Razor; + +/// +/// 分部视图路径解析器测试 +/// +public class PartViewPathResolverTest { + /// + /// 分部视图路径解析器 + /// + private readonly PartViewPathResolver _resolver; + + /// + /// 测试初始化 + /// + public PartViewPathResolverTest() { + var mockFinder = new Mock(); + mockFinder.Setup( t => t.Find( It.IsAny(), It.IsAny() ) ).Returns( ( _, partName ) => partName ); + _resolver = new PartViewPathResolver( mockFinder.Object ); + } + + /// + /// 测试解析分部视图路径 - 解析空值 + /// + [Fact] + public void TestResolve_1() { + Assert.Empty( _resolver.Resolve( null, "a" ) ); + Assert.Empty( _resolver.Resolve( "a", null ) ); + Assert.Empty( _resolver.Resolve( "a", "" ) ); + Assert.Empty( _resolver.Resolve( "a", " " ) ); + Assert.Empty( _resolver.Resolve( "a", "a" ) ); + } + + /// + /// 测试解析分部视图路径 - 包含1个分部视图 + /// + [Fact] + public void TestResolve_2() { + var result = _resolver.Resolve( "Views/Home", "@page\r\n\r\ntest\r\n\r\n\r\n\r\n abc" ); + Assert.Single( result ); + Assert.Equal( "Part", result[0] ); + } + + /// + /// 测试解析分部视图路径 - 包含2个分部视图 + /// + [Fact] + public void TestResolve_3() { + var content = new StringBuilder(); + content.AppendLine( " @page\r\n\r\ntest\r\n\r\n\r\n\r\n abc " ); + content.AppendLine( "r\ntest\r\n\r\n\r\n\r\n < partial name = \"Part2\" / > bbb " ); + var result = _resolver.Resolve( "Views/Home", content.ToString() ); + Assert.Equal( 2, result.Count ); + Assert.Equal( "Part", result[0] ); + Assert.Equal( "Part2", result[1] ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Razor/RazorViewContainerTest.cs b/test/Util.Ui.Tests/Razor/RazorViewContainerTest.cs new file mode 100644 index 000000000..50037fb8a --- /dev/null +++ b/test/Util.Ui.Tests/Razor/RazorViewContainerTest.cs @@ -0,0 +1,246 @@ +using System.Collections.Generic; +using Moq; +using Util.Ui.Razor.Internal; +using Xunit; + +namespace Util.Ui.Tests.Razor; + +/// +/// Razor视图容器测试 +/// +public class RazorViewContainerTest { + /// + /// Razor视图容器 + /// + private readonly RazorViewContainer _container; + /// + /// 分部视图路径模拟解析器 + /// + private readonly Mock _mockPartViewPathResolver; + /// + /// 视图内容模拟解析器 + /// + private readonly Mock _mockContentResolver; + + /// + /// 测试初始化 + /// + public RazorViewContainerTest() { + _mockPartViewPathResolver = new Mock(); + _mockContentResolver = new Mock(); + _container = new RazorViewContainer( _mockPartViewPathResolver.Object, _mockContentResolver.Object ); + } + + /// + /// 测试添加视图 - 不包含分部视图 + /// + [Fact] + public void TestAddView_1() { + //变量定义 + var path = "/path1"; + var content = "@page a"; + + //方法设置 + _mockPartViewPathResolver.Setup( t => t.Resolve( path, content ) ).Returns( [] ); + _mockContentResolver.Setup( t => t.Resolve( path ) ).Returns( content ); + + //执行 + var result = _container.AddView( path ); + Assert.True( result is MainView ); + Assert.Equal( path, result.Path ); + Assert.Empty( result.PartViews ); + } + + /// + /// 测试添加视图 - 1个分部视图 + /// + [Fact] + public void TestAddView_2() { + //变量定义 + var path = "/path1"; + var content = "@page a"; + var partPath = "/path2"; + + //方法设置 + _mockPartViewPathResolver.Setup( t => t.Resolve( path, content ) ).Returns( [partPath] ); + _mockContentResolver.Setup( t => t.Resolve( path ) ).Returns( content ); + _mockContentResolver.Setup( t => t.Resolve( partPath ) ).Returns( "a" ); + + //执行 + var result = _container.AddView( path ); + Assert.True( result is MainView ); + Assert.Equal( path, result.Path ); + Assert.Single( result.PartViews ); + Assert.Equal( partPath, result.PartViews[0].Path ); + Assert.Single( result.PartViews[0].MainViews ); + } + + /// + /// 测试添加视图 - 2个分部视图 + /// + [Fact] + public void TestCreateView_3() { + //变量定义 + var path = "/path1"; + var content = "@page a"; + var path2 = "/path2"; + var path3 = "/path3"; + + //方法设置 + _mockPartViewPathResolver.Setup( t => t.Resolve( path, content ) ).Returns( [path2, path3] ); + _mockContentResolver.Setup( t => t.Resolve( path ) ).Returns( content ); + _mockContentResolver.Setup( t => t.Resolve( path2 ) ).Returns( "a" ); + _mockContentResolver.Setup( t => t.Resolve( path3 ) ).Returns( "a" ); + + //执行 + var result = _container.AddView( path ); + Assert.Equal( 2, result.PartViews.Count ); + Assert.Equal( path2, result.PartViews[0].Path ); + Assert.Single( result.PartViews[0].MainViews ); + Assert.Equal( path3, result.PartViews[1].Path ); + Assert.Single( result.PartViews[1].MainViews ); + } + + /// + /// 测试添加视图 - 2个分部视图 - 分部视图嵌套 + /// + [Fact] + public void TestCreateView_4() { + //变量定义 + var path = "/path1"; + var content = "@page a"; + var path2 = "/path2"; + var path3 = "/path3"; + var path4 = "/path4"; + var content2 = "b"; + + //方法设置 + _mockPartViewPathResolver.Setup( t => t.Resolve( path, content ) ).Returns( [path2, path3] ); + _mockPartViewPathResolver.Setup( t => t.Resolve( path3, content2 ) ).Returns( [path4] ); + _mockContentResolver.Setup( t => t.Resolve( path ) ).Returns( content ); + _mockContentResolver.Setup( t => t.Resolve( path2 ) ).Returns( "a" ); + _mockContentResolver.Setup( t => t.Resolve( path3 ) ).Returns( content2 ); + _mockContentResolver.Setup( t => t.Resolve( path4 ) ).Returns( "a" ); + + //执行 + var result = _container.AddView( path ); + Assert.Equal( 2, result.PartViews.Count ); + Assert.Equal( path2, result.PartViews[0].Path ); + Assert.Single( result.PartViews[0].MainViews ); + Assert.Equal( path3, result.PartViews[1].Path ); + Assert.Single( result.PartViews[1].MainViews ); + Assert.Single( result.PartViews[1].PartViews ); + Assert.Equal( path4, result.PartViews[1].PartViews[0].Path ); + Assert.Equal( path3, result.PartViews[1].PartViews[0].MainViews[0].Path ); + } + + /// + /// 测试获取关联视图路径列表 - 获取1个主视图 + /// + [Fact] + public void TestGetViewPaths_1() { + //变量定义 + var path = "/path1"; + var content = "@page a"; + var list = new List { path }; + + //方法设置 + _mockPartViewPathResolver.Setup( t => t.Resolve( path, content ) ).Returns( [] ); + _mockContentResolver.Setup( t => t.Resolve( path ) ).Returns( content ); + + //初始化容器 + _container.Init( list ); + + //执行 + var result = _container.GetViewPaths( path ); + Assert.Single( result ); + Assert.Equal( path, result[0] ); + } + + /// + /// 测试获取关联视图路径列表 - 获取分部视图路径 - 1个主视图引用分部视图 + /// + [Fact] + public void TestGetViewPaths_2() { + //变量定义 + var path = "/path1"; + var content = "@page a"; + var partPath = "/path2"; + var list = new List { path }; + + //方法设置 + _mockPartViewPathResolver.Setup( t => t.Resolve( path, content ) ).Returns( [partPath] ); + _mockContentResolver.Setup( t => t.Resolve( path ) ).Returns( content ); + _mockContentResolver.Setup( t => t.Resolve( partPath ) ).Returns( "a" ); + + //初始化容器 + _container.Init( list ); + + //执行 + var result = _container.GetViewPaths( partPath ); + Assert.Single( result ); + Assert.Equal( path, result[0] ); + } + + /// + /// 测试获取关联视图路径列表 - 获取分部视图路径 - 2个主视图引用分部视图 + /// + [Fact] + public void TestGetViewPaths_3() { + //变量定义 + var path = "/path1"; + var path2 = "/path2"; + var content = "@page a"; + var partPath = "/partPath"; + var list = new List { path, path2 }; + + //方法设置 + _mockPartViewPathResolver.Setup( t => t.Resolve( path, content ) ).Returns( [partPath] ); + _mockPartViewPathResolver.Setup( t => t.Resolve( path2, content ) ).Returns( [partPath] ); + _mockContentResolver.Setup( t => t.Resolve( path ) ).Returns( content ); + _mockContentResolver.Setup( t => t.Resolve( path2 ) ).Returns( content ); + _mockContentResolver.Setup( t => t.Resolve( partPath ) ).Returns( "a" ); + + //初始化容器 + _container.Init( list ); + + //执行 + var result = _container.GetViewPaths( partPath ); + Assert.Equal( 2, result.Count ); + Assert.Equal( path, result[0] ); + Assert.Equal( path2, result[1] ); + } + + /// + /// 测试获取关联视图路径列表 - 获取分部视图路径 - 分部视图嵌套 + /// + [Fact] + public void TestGetViewPaths_4() { + //变量定义 + var path = "/path1"; + var path2 = "/path2"; + var content = "@page a"; + var content2 = "a"; + var partPath = "/partPath"; + var partPath2 = "/partPath2"; + var list = new List { path, path2 }; + + //方法设置 + _mockPartViewPathResolver.Setup( t => t.Resolve( path, content ) ).Returns( [partPath] ); + _mockPartViewPathResolver.Setup( t => t.Resolve( path2, content ) ).Returns( [partPath2] ); + _mockPartViewPathResolver.Setup( t => t.Resolve( partPath2, content2 ) ).Returns( [partPath] ); + _mockContentResolver.Setup( t => t.Resolve( path ) ).Returns( content ); + _mockContentResolver.Setup( t => t.Resolve( path2 ) ).Returns( content ); + _mockContentResolver.Setup( t => t.Resolve( partPath ) ).Returns( "a" ); + _mockContentResolver.Setup( t => t.Resolve( partPath2 ) ).Returns( content2 ); + + //初始化容器 + _container.Init( list ); + + //执行 + var result = _container.GetViewPaths( partPath ); + Assert.Equal( 2, result.Count ); + Assert.Equal( path, result[0] ); + Assert.Equal( path2, result[1] ); + } +} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Samples/Containers/Builders/ContainerBuilder.cs b/test/Util.Ui.Tests/Samples/Containers/Builders/ContainerBuilder.cs new file mode 100644 index 000000000..179591d9c --- /dev/null +++ b/test/Util.Ui.Tests/Samples/Containers/Builders/ContainerBuilder.cs @@ -0,0 +1,22 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.Tests.Samples.Containers.Builders { + /// + /// ng-container标签生成器 + /// + public class ContainerBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化ng-container标签生成器 + /// + /// 配置 + public ContainerBuilder( Config config ) : base( config,"ng-container" ) { + _config = config; + } + } +} diff --git a/test/Util.Ui.Tests/Samples/Containers/ContainerTagHelper.cs b/test/Util.Ui.Tests/Samples/Containers/ContainerTagHelper.cs new file mode 100644 index 000000000..9e6f37211 --- /dev/null +++ b/test/Util.Ui.Tests/Samples/Containers/ContainerTagHelper.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.Configs; +using Util.Ui.Renders; +using Util.Ui.Tests.Samples.Containers.Renders; + +namespace Util.Ui.Tests.Samples.Containers { + /// + /// 容器,生成的标签为<ng-container></ng-container> + /// + [HtmlTargetElement( "util-container" )] + public class ContainerTagHelper : AngularTagHelperBase { + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new ContainerRender( config ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Samples/Containers/Renders/ContainerRender.cs b/test/Util.Ui.Tests/Samples/Containers/Renders/ContainerRender.cs new file mode 100644 index 000000000..5a517a60c --- /dev/null +++ b/test/Util.Ui.Tests/Samples/Containers/Renders/ContainerRender.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Html; +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.Renders; +using Util.Ui.Tests.Samples.Containers.Builders; + +namespace Util.Ui.Tests.Samples.Containers.Renders { + /// + /// ng-container容器渲染器 + /// + public class ContainerRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化ng-container容器渲染器 + /// + /// 配置 + public ContainerRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new ContainerBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new ContainerRender( _config.Copy() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Samples/Customer.cs b/test/Util.Ui.Tests/Samples/Customer.cs new file mode 100644 index 000000000..8d16a9db6 --- /dev/null +++ b/test/Util.Ui.Tests/Samples/Customer.cs @@ -0,0 +1,103 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Util.Domain.Biz.Enums; + +namespace Util.Ui.Tests.Samples { + /// + /// 客户 + /// + public class Customer { + /// + /// 编码 + /// + [Description( "编码" )] + [Required( ErrorMessage = "编码不能是空值" )] + [StringLength( 50, MinimumLength = 5, ErrorMessage = "编码不能超过50位,也不能小于5位" )] + public string Code { get; set; } + /// + /// 姓名 + /// + [Description( "姓名" )] + [MaxLength( 200, ErrorMessage = "姓名不能超过200位" )] + [MinLength( 2, ErrorMessage = "姓名不能少于2位" )] + public string Name { get; set; } + /// + /// 昵称 + /// + [Description( "昵称" )] + [MaxLength( 50 )] + public string Nickname { get; set; } + /// + /// 性别 + /// + [Description( "性别" )] + public Gender? Gender { get; set; } + /// + /// 密码 + /// + [Description( "密码" )] + [DataType( DataType.Password )] + public string Password { get; set; } + /// + /// 出生日期 + /// + [Description( "出生日期" )] + public DateTime? Birthday { get; set; } + /// + /// 民族 + /// + [Description( "民族" )] + public Nation? Nation { get; set; } + /// + /// 手机号 + /// + [Description( "手机号" )] + [Phone( ErrorMessage = "手机号无效" )] + public string Phone { get; set; } + /// + /// 电话 + /// + [Description( "电话" )] + public int Tel { get; set; } + /// + /// 年龄 + /// + [Description( "年龄" )] + [Range( 1.1, 99.99, ErrorMessage = "年龄从1.1到99.99" )] + public double Age { get; set; } + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + [EmailAddress( ErrorMessage = "电子邮件无效" )] + public string Email { get; set; } + /// + /// 身份证 + /// + [Description( "身份证" )] + [IdCard( ErrorMessage = "身份证无效" )] + public string IdCard { get; set; } + /// + /// 网址 + /// + [Description( "网址" )] + [Url( ErrorMessage = "网址无效" )] + public string Url { get; set; } + /// + /// 正则表达式 + /// + [Description( "正则表达式" )] + [RegularExpression( "a", ErrorMessage = "正则表达式无效" )] + public string Regular { get; set; } + /// + /// 启用 + /// + [Description( "启用" )] + public bool Enabled { get; set; } + /// + /// 员工 + /// + public Employee Employee { get; set; } + } +} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Samples/Employee.cs b/test/Util.Ui.Tests/Samples/Employee.cs new file mode 100644 index 000000000..8661f7b61 --- /dev/null +++ b/test/Util.Ui.Tests/Samples/Employee.cs @@ -0,0 +1,58 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace Util.Ui.Tests.Samples { + /// + /// 员工 + /// + [Model( "employee" )] + public class Employee { + /// + /// 编码 + /// + [Description( "编码" )] + [MaxLength( 100 )] + [Required(ErrorMessage = "编码不能是空值" )] + public string Code { get; set; } + /// + /// 姓名 + /// + [Description( "姓名" )] + [MaxLength( 200 )] + public string Name { get; set; } + /// + /// 昵称 + /// + [Description( "昵称" )] + [MaxLength( 50 )] + public string Nickname { get; set; } + /// + /// 性别 + /// + [Description( "性别" )] + public int? Gender { get; set; } + /// + /// 出生日期 + /// + [Description( "出生日期" )] + public DateTime? Birthday { get; set; } + /// + /// 民族 + /// + [Description( "民族" )] + public int? Nation { get; set; } + /// + /// 手机号 + /// + [Description( "手机号" )] + [MaxLength( 50 )] + public string Phone { get; set; } + /// + /// 电子邮件 + /// + [Description( "电子邮件" )] + [MaxLength( 500 )] + public string Email { get; set; } + } +} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Samples/HBuilder.cs b/test/Util.Ui.Tests/Samples/HBuilder.cs new file mode 100644 index 000000000..b7614f639 --- /dev/null +++ b/test/Util.Ui.Tests/Samples/HBuilder.cs @@ -0,0 +1,69 @@ +using Util.Ui.Builders; + +namespace Util.Ui.Tests.Samples; + +/// +/// h1标签生成器 +/// +public class H1Builder : TagBuilder { + /// + /// 初始化h1标签生成器 + /// + public H1Builder() : base( "h1" ) { + } +} + +/// +/// h2标签生成器 +/// +public class H2Builder : TagBuilder { + /// + /// 初始化h2标签生成器 + /// + public H2Builder() : base( "h2" ) { + } +} + +/// +/// h3标签生成器 +/// +public class H3Builder : TagBuilder { + /// + /// 初始化h3标签生成器 + /// + public H3Builder() : base( "h3" ) { + } +} + +/// +/// h4标签生成器 +/// +public class H4Builder : TagBuilder { + /// + /// 初始化h4标签生成器 + /// + public H4Builder() : base( "h4" ) { + } +} + +/// +/// h5标签生成器 +/// +public class H5Builder : TagBuilder { + /// + /// 初始化h5标签生成器 + /// + public H5Builder() : base( "h5" ) { + } +} + +/// +/// h6标签生成器 +/// +public class H6Builder : TagBuilder { + /// + /// 初始化h6标签生成器 + /// + public H6Builder() : base( "h6" ) { + } +} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Samples/SpanBuilder.cs b/test/Util.Ui.Tests/Samples/SpanBuilder.cs new file mode 100644 index 000000000..d90e8a773 --- /dev/null +++ b/test/Util.Ui.Tests/Samples/SpanBuilder.cs @@ -0,0 +1,14 @@ +using Util.Ui.Builders; + +namespace Util.Ui.Tests.Samples; + +/// +/// span生成器 +/// +public class SpanBuilder : TagBuilder { + /// + /// 初始化span生成器 + /// + public SpanBuilder() : base( "span" ) { + } +} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Samples/StrongBuilder.cs b/test/Util.Ui.Tests/Samples/StrongBuilder.cs new file mode 100644 index 000000000..54ff9bb2a --- /dev/null +++ b/test/Util.Ui.Tests/Samples/StrongBuilder.cs @@ -0,0 +1,14 @@ +using Util.Ui.Builders; + +namespace Util.Ui.Tests.Samples; + +/// +/// div标签生成器 +/// +public class DivBuilder : TagBuilder { + /// + /// 初始化div标签生成器 + /// + public DivBuilder() : base( "div" ) { + } +} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Samples/Templates/Builders/TemplateBuilder.cs b/test/Util.Ui.Tests/Samples/Templates/Builders/TemplateBuilder.cs new file mode 100644 index 000000000..3d8008da3 --- /dev/null +++ b/test/Util.Ui.Tests/Samples/Templates/Builders/TemplateBuilder.cs @@ -0,0 +1,46 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Configs; + +namespace Util.Ui.Tests.Samples.Templates.Builders { + /// + /// ng-template标签生成器 + /// + public class TemplateBuilder : AngularTagBuilder { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化ng-template标签生成器 + /// + /// 配置 + public TemplateBuilder( Config config ) : base( config, "ng-template" ) { + _config = config; + } + + /// + /// 配置选项卡是否延迟加载 + /// + public TemplateBuilder Tab() { + Tab( _config.GetValue( UiConst.Tab ) == true ); + return this; + } + + /// + /// 配置选项卡是否延迟加载 + /// + public TemplateBuilder Tab( bool value ) { + AttributeIf( "nz-tab", value ); + return this; + } + + /// + /// 配置 + /// + public override void Config() { + base.Config(); + Tab(); + } + } +} diff --git a/test/Util.Ui.Tests/Samples/Templates/Renders/TemplateRender.cs b/test/Util.Ui.Tests/Samples/Templates/Renders/TemplateRender.cs new file mode 100644 index 000000000..31cce9daf --- /dev/null +++ b/test/Util.Ui.Tests/Samples/Templates/Renders/TemplateRender.cs @@ -0,0 +1,40 @@ +using System; +using Microsoft.AspNetCore.Html; +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.Renders; +using Util.Ui.Tests.Samples.Templates.Builders; + +namespace Util.Ui.Tests.Samples.Templates.Renders { + /// + /// ng-template模板渲染器 + /// + public class TemplateRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化模板渲染器 + /// + /// 配置 + public TemplateRender( Config config ) { + _config = config ?? throw new ArgumentNullException( nameof( config ) ); + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TemplateBuilder( _config ); + builder.Config(); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TemplateRender( _config.Copy() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Samples/Templates/TemplateTagHelper.cs b/test/Util.Ui.Tests/Samples/Templates/TemplateTagHelper.cs new file mode 100644 index 000000000..9a64d7002 --- /dev/null +++ b/test/Util.Ui.Tests/Samples/Templates/TemplateTagHelper.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.Configs; +using Util.Ui.Renders; +using Util.Ui.Tests.Samples.Templates.Renders; + +namespace Util.Ui.Tests.Samples.Templates { + /// + /// 模板,生成的标签为<ng-template></ng-template> + /// + [HtmlTargetElement( "util-template" )] + public class TemplateTagHelper : AngularTagHelperBase { + /// + /// nz-tab,选项卡是否延迟加载 + /// + public bool Tab { get; set; } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + var config = new Config( context, output, content ); + return new TemplateRender( config ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Samples/TestBuilder.cs b/test/Util.Ui.Tests/Samples/TestBuilder.cs new file mode 100644 index 000000000..0cb729ff0 --- /dev/null +++ b/test/Util.Ui.Tests/Samples/TestBuilder.cs @@ -0,0 +1,28 @@ +using Util.Ui.Angular.Builders; +using Util.Ui.Angular.Extensions; +using Util.Ui.Configs; + +namespace Util.Ui.Tests.Samples { + /// + /// 测试标签生成器 + /// + public class TestBuilder : AngularTagBuilder { + /// + /// 初始化测试标签生成器 + /// + public TestBuilder( Config config ) : base( config,"test" ) { + } + + /// + /// 配置自定义标识 + /// + protected override void ConfigId( Config config ) { + var shareConfig = config.GetValueFromItems(); + if ( string.IsNullOrWhiteSpace( shareConfig?.Id ) ) { + base.ConfigId( config ); + return; + } + this.Id( $"test_{shareConfig.Id}" ); + } + } +} diff --git a/test/Util.Ui.Tests/Samples/TestRender.cs b/test/Util.Ui.Tests/Samples/TestRender.cs new file mode 100644 index 000000000..104d6f255 --- /dev/null +++ b/test/Util.Ui.Tests/Samples/TestRender.cs @@ -0,0 +1,40 @@ +using Microsoft.AspNetCore.Html; +using Util.Ui.Builders; +using Util.Ui.Configs; +using Util.Ui.Renders; + +namespace Util.Ui.Tests.Samples { + /// + /// 测试渲染器 + /// + public class TestRender : RenderBase { + /// + /// 配置 + /// + private readonly Config _config; + + /// + /// 初始化测试渲染器 + /// + /// 配置 + public TestRender( Config config ) { + _config = config; + } + + /// + /// 获取标签生成器 + /// + protected override TagBuilder GetTagBuilder() { + var builder = new TestBuilder(_config); + builder.Config(); + builder.AttributeIfNotEmpty( UiConst.Name, _config.GetValue( UiConst.Name ) ); + builder.AttributeIfNotEmpty( UiConst.Title, _config.GetValue( UiConst.Title ) ); + return builder; + } + + /// + public override IHtmlContent Clone() { + return new TestRender( _config.Copy() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Samples/TestShareConfig.cs b/test/Util.Ui.Tests/Samples/TestShareConfig.cs new file mode 100644 index 000000000..ab57d0e5f --- /dev/null +++ b/test/Util.Ui.Tests/Samples/TestShareConfig.cs @@ -0,0 +1,19 @@ +namespace Util.Ui.Tests.Samples { + /// + /// 测试共享配置 + /// + public class TestShareConfig { + /// + /// 初始化测试共享配置 + /// + /// 标识 + public TestShareConfig( string id ) { + Id = id; + } + + /// + /// 标识 + /// + public string Id { get; set; } + } +} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Samples/TestTagHelper.cs b/test/Util.Ui.Tests/Samples/TestTagHelper.cs new file mode 100644 index 000000000..ffd184167 --- /dev/null +++ b/test/Util.Ui.Tests/Samples/TestTagHelper.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Angular.TagHelpers; +using Util.Ui.Configs; +using Util.Ui.Expressions; +using Util.Ui.Renders; + +namespace Util.Ui.Tests.Samples { + /// + /// 测试标签 + /// + [HtmlTargetElement( "test" )] + public class TestTagHelper : AngularTagHelperBase { + /// + /// 配置 + /// + private Config _config; + /// + /// 属性表达式 + /// + public ModelExpression For { get; set; } + + /// + protected override void ProcessBefore( TagHelperContext context, TagHelperOutput output ) { + _config = new Config( context, output ); + var expressionLoader = new ExpressionLoader(); + expressionLoader.Load( _config ); + } + + /// + protected override IRender GetRender( TagHelperContext context, TagHelperOutput output, TagHelperContent content ) { + _config.Content = content; + return new TestRender( _config ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.Tests/TagHelpers/TagHelperWrapperTest.cs b/test/Util.Ui.Tests/TagHelpers/TagHelperWrapperTest.cs new file mode 100644 index 000000000..9d90c7ac0 --- /dev/null +++ b/test/Util.Ui.Tests/TagHelpers/TagHelperWrapperTest.cs @@ -0,0 +1,230 @@ +using System.Text; +using Util.Ui.Configs; +using Util.Ui.TagHelpers; +using Util.Ui.Tests.Samples; +using Util.Ui.Tests.Samples.Containers; +using Util.Ui.Tests.Samples.Templates; +using Xunit; +using Xunit.Abstractions; + +namespace Util.Ui.Tests.TagHelpers { + /// + /// TagHelper包装器测试 + /// + public class TagHelperWrapperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// TagHelper包装器 + /// + private readonly TagHelperWrapper _wrapper; + + /// + /// 测试初始化 + /// + public TagHelperWrapperTest( ITestOutputHelper output ) { + _output = output; + _wrapper = new TestTagHelper().ToWrapper(); + } + + /// + /// 获取结果 + /// + private string GetResult() { + var result = _wrapper.GetResult(); + _output.WriteLine( result ); + return result; + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置上下文属性 + /// + [Fact] + public void TestSetContextAttribute() { + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置输出属性 + /// + [Fact] + public void TestSetOutputAttribute() { + _wrapper.SetOutputAttribute( "a", "1" ).SetOutputAttribute( "b", "2" ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置共享项 + /// + [Fact] + public void TestSetItem() { + var shareConfig = new TestShareConfig( "abc" ); + _wrapper.SetItem( shareConfig ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加字符串内容 + /// + [Fact] + public void TestAppendContent_1() { + _wrapper.AppendContent( "a" ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加两次字符串内容 + /// + [Fact] + public void TestAppendContent_2() { + _wrapper.AppendContent( "a" ).AppendContent( "b" ); + var result = new StringBuilder(); + result.Append( "ab" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加TagBuilder内容 + /// + [Fact] + public void TestAppendContent_3() { + _wrapper.AppendContent( new H1Builder() ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加两次TagBuilder内容 + /// + [Fact] + public void TestAppendContent_4() { + _wrapper.AppendContent( new H1Builder() ).AppendContent( new H2Builder() ); + var result = new StringBuilder(); + result.Append( "

                                                                      " ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试添加TagHelper内容 + /// + [Fact] + public void TestAppendContent_5() { + _wrapper.AppendContent( new ContainerTagHelper() ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), _wrapper.GetResult() ); + } + + /// + /// 测试添加两次TagHelper内容 + /// + [Fact] + public void TestAppendContent_6() { + _wrapper.AppendContent( new ContainerTagHelper() ).AppendContent( new TemplateTagHelper() ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), _wrapper.GetResult() ); + } + + /// + /// 测试添加混合内容 + /// + [Fact] + public void TestAppendContent_7() { + _wrapper.AppendContent( "a" ).AppendContent( new H1Builder() ).AppendContent( new ContainerTagHelper() ); + var result = new StringBuilder(); + result.Append( "a

                                                                      " ); + Assert.Equal( result.ToString(), _wrapper.GetResult() ); + } + + /// + /// 测试添加嵌套内容 + /// + [Fact] + public void TestAppendContent_8() { + var container = new ContainerTagHelper().ToWrapper(); + container.AppendContent( "a" ).AppendContent( new TemplateTagHelper() ); + _wrapper.AppendContent( container ); + var result = new StringBuilder(); + result.Append( "a" ); + Assert.Equal( result.ToString(), _wrapper.GetResult() ); + } + + /// + /// 测试嵌套组件同时设置相同属性 + /// + [Fact] + public void TestAppendContent_9() { + var container = new ContainerTagHelper().ToWrapper(); + container.SetContextAttribute( UiConst.Id, "b" ); + + _wrapper.SetContextAttribute( UiConst.Id, "a" ); + _wrapper.AppendContent( container ); + + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), _wrapper.GetResult() ); + } + + /// + /// 测试父组件设置共享配置,子组件使用共享配置 + /// + [Fact] + public void TestAppendContent_10() { + var container = new ContainerTagHelper().ToWrapper(); + container.SetItem( new TestShareConfig( "a" ) ); + container.AppendContent( _wrapper ); + + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), container.GetResult() ); + } + + /// + /// 测试嵌套组件同时设置相同输出属性 + /// + [Fact] + public void TestAppendContent_11() { + var container = new ContainerTagHelper().ToWrapper(); + container.SetOutputAttribute( "a", 1 ); + _wrapper.AppendContent( container ); + _wrapper.SetOutputAttribute( "b", 2 ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), _wrapper.GetResult() ); + } + + /// + /// 设置表达式 + /// + [Fact] + public void TestSetExpression() { + _wrapper.SetExpression( UiConst.For,t => t.Code ); + var result = new StringBuilder(); + result.Append( "" ); + Assert.Equal( result.ToString(), _wrapper.GetResult() ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Util.Ui.Tests.csproj b/test/Util.Ui.Tests/Util.Ui.Tests.csproj index 3e0123eb2..466637611 100644 --- a/test/Util.Ui.Tests/Util.Ui.Tests.csproj +++ b/test/Util.Ui.Tests/Util.Ui.Tests.csproj @@ -1,30 +1,28 @@ - - - - netcoreapp2.0 - - false - - - - ..\..\output\test - ..\..\output\test\netcoreapp2.0\Util.Ui.Tests.xml - - - - ..\..\output\test - ..\..\output\test\netcoreapp2.0\Util.Ui.Tests.xml - - - - - - - - - - - - - - + + + + $(NetTargetFramework) + false + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + diff --git a/test/Util.Ui.Tests/XUnitHelpers/Helper.cs b/test/Util.Ui.Tests/XUnitHelpers/Helper.cs deleted file mode 100644 index cc255eb67..000000000 --- a/test/Util.Ui.Tests/XUnitHelpers/Helper.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.TagHelpers; -using Util.Helpers; -using Xunit.Abstractions; - -namespace Util.Ui.Tests.XUnitHelpers { - /// - /// 辅助操作 - /// - public static class Helper { - /// - /// 获取结果 - /// - /// TagHelper组件类型 - /// 输出工具 - /// TagHelper组件 - /// 上下文属性集合 - /// 输出属性集合 - /// 内容 - public static string GetResult( ITestOutputHelper outputHelper, TTagHelper component, TagHelperAttributeList contextAttributes = null, - TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) where TTagHelper : TagHelper{ - var context = new TagHelperContext( "", contextAttributes ?? new TagHelperAttributeList(), new Dictionary(), Id.Guid() ); - var output = new TagHelperOutput( "", outputAttributes ?? new TagHelperAttributeList(),( useCachedResult, encoder ) => Task.FromResult( content ) ); - component.Process( context, output ); - var writer = new StringWriter(); - output.WriteTo( writer, HtmlEncoder.Default ); - var result = writer.ToString(); - outputHelper.WriteLine( result ); - return result; - } - } -} diff --git a/test/Util.Validation.Tests/DataAnnotationValidationTest.cs b/test/Util.Validation.Tests/DataAnnotationValidationTest.cs new file mode 100644 index 000000000..ace2d2073 --- /dev/null +++ b/test/Util.Validation.Tests/DataAnnotationValidationTest.cs @@ -0,0 +1,46 @@ +using System.Linq; +using Util.Validation.Tests.Samples; +using Xunit; + +namespace Util.Validation.Tests; + +/// +/// 数据注解验证操作测试 +/// +public class DataAnnotationValidationTest { + /// + /// 验证样例 + /// + private readonly Sample _sample; + + /// + /// 测试初始化 + /// + public DataAnnotationValidationTest() { + _sample = new Sample(); + } + + /// + /// 测试验证必填 + /// + [Fact] + public void TestValidate_Required() { + var result = DataAnnotationValidation.Validate( _sample ); + Assert.False( result.IsValid ); + Assert.Single( result ); + Assert.Equal( "Required", result.First().ErrorMessage ); + } + + /// + /// 测试验证字符串长度 + /// + [Fact] + public void TestValidate_StringLength() { + _sample.StringLengthValue = "abcd"; + var result = DataAnnotationValidation.Validate( _sample ); + Assert.False( result.IsValid ); + Assert.Equal( 2,result.Count ); + Assert.Equal( "Required", result[0].ErrorMessage ); + Assert.Equal( "StringLength", result[1].ErrorMessage ); + } +} \ No newline at end of file diff --git a/test/Util.Validation.Tests/Samples/ITestService.cs b/test/Util.Validation.Tests/Samples/ITestService.cs new file mode 100644 index 000000000..99effa393 --- /dev/null +++ b/test/Util.Validation.Tests/Samples/ITestService.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using Util.Dependency; + +namespace Util.Validation.Tests.Samples; + +/// +/// 测试服务 +/// +public interface ITestService : ISingletonDependency{ + /// + /// 验证单个值 + /// + /// 参数 + void Call( [Valid] Sample value ); + /// + /// 验证集合 + /// + /// 参数 + void CallCollection( [Valid] List value ); +} \ No newline at end of file diff --git a/test/Util.Validation.Tests/Samples/Sample.cs b/test/Util.Validation.Tests/Samples/Sample.cs new file mode 100644 index 000000000..2f6dc1a90 --- /dev/null +++ b/test/Util.Validation.Tests/Samples/Sample.cs @@ -0,0 +1,30 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Util.Validation.Tests.Samples; + +/// +/// 验证样例 +/// +public class Sample : IValidation{ + /// + /// 验证必填项 + /// + [Required( ErrorMessage = "Required" )] + public string RequiredValue { get; set; } + /// + /// 验证字符串长度 + /// + [StringLength(3, ErrorMessage = "StringLength" )] + public string StringLengthValue { get; set; } + + /// + /// 验证 + /// + public ValidationResultCollection Validate() { + var result = DataAnnotationValidation.Validate( this ); + if( result.IsValid ) + return ValidationResultCollection.Success; + throw new ArgumentException(); + } +} \ No newline at end of file diff --git a/test/Util.Validation.Tests/Samples/Sample2.cs b/test/Util.Validation.Tests/Samples/Sample2.cs new file mode 100644 index 000000000..c4b6b0d5c --- /dev/null +++ b/test/Util.Validation.Tests/Samples/Sample2.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; + +namespace Util.Validation.Tests.Samples; + +/// +/// 验证样例2 +/// +public class Sample2 { + /// + /// 验证身份证 + /// + [IdCard] + public string IdCard { get; set; } +} \ No newline at end of file diff --git a/test/Util.Validation.Tests/Samples/Sample3.cs b/test/Util.Validation.Tests/Samples/Sample3.cs new file mode 100644 index 000000000..212a50c5c --- /dev/null +++ b/test/Util.Validation.Tests/Samples/Sample3.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; + +namespace Util.Validation.Tests.Samples; + +/// +/// 验证样例3 +/// +public class Sample3 { + /// + /// 验证身份证 + /// + [IdCard( ErrorMessage = "IdCard" )] + public string IdCard { get; set; } +} \ No newline at end of file diff --git a/test/Util.Validation.Tests/Samples/TestService.cs b/test/Util.Validation.Tests/Samples/TestService.cs new file mode 100644 index 000000000..700abd1cd --- /dev/null +++ b/test/Util.Validation.Tests/Samples/TestService.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace Util.Validation.Tests.Samples; + +/// +/// 测试服务 +/// +public class TestService : ITestService{ + /// + /// 验证单个值 + /// + /// 参数 + public void Call( Sample value ) { + } + + /// + /// 验证集合 + /// + /// 参数 + public void CallCollection( List value ) { + } +} \ No newline at end of file diff --git a/test/Util.Validation.Tests/Startup.cs b/test/Util.Validation.Tests/Startup.cs new file mode 100644 index 000000000..772afb61b --- /dev/null +++ b/test/Util.Validation.Tests/Startup.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Util.Aop; +using Xunit.DependencyInjection.Logging; + +namespace Util.Validation.Tests; + +/// +/// +/// +public class Startup { + /// + /// + /// + public void ConfigureHost( IHostBuilder hostBuilder ) { + hostBuilder.AsBuild().AddAop().AddUtil(); + } + + /// + /// ÷ + /// + public void ConfigureServices( IServiceCollection services ) { + services.AddLogging( logBuilder => logBuilder.AddXunitOutput() ); + } +} \ No newline at end of file diff --git a/test/Util.Validation.Tests/Util.Validation.Tests.csproj b/test/Util.Validation.Tests/Util.Validation.Tests.csproj new file mode 100644 index 000000000..906bca5c4 --- /dev/null +++ b/test/Util.Validation.Tests/Util.Validation.Tests.csproj @@ -0,0 +1,28 @@ + + + + $(NetTargetFramework) + false + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/Util.Validation.Tests/ValidAttributeTest.cs b/test/Util.Validation.Tests/ValidAttributeTest.cs new file mode 100644 index 000000000..74d880bdd --- /dev/null +++ b/test/Util.Validation.Tests/ValidAttributeTest.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using Util.Validation.Tests.Samples; +using Xunit; + +namespace Util.Validation.Tests; + +/// +/// 验证拦截器测试 +/// +public class ValidAttributeTest { + /// + /// 测试服务 + /// + private readonly ITestService _service; + + /// + /// 测试初始化 + /// + public ValidAttributeTest( ITestService service ) { + _service = service; + } + + /// + /// 验证单个对象 + /// + [Fact] + public void TestValidate() { + Sample sample = new Sample(); + Assert.Throws( () => { + _service.Call( sample ); + } ); + } + + /// + /// 验证单个对象 - 验证通过不会抛出异常 + /// + [Fact] + public void TestValidate_2() { + Sample sample = new Sample {RequiredValue = "a"}; + _service.Call( sample ); + } + + /// + /// 验证集合对象 + /// + [Fact] + public void TestValidateCollection() { + List samples = new List {new Sample(), new Sample() }; + Assert.Throws( () => { + _service.CallCollection( samples ); + } ); + } + + /// + /// 验证集合对象 - 验证通过不会抛出异常 + /// + [Fact] + public void TestValidateCollection_2() { + List samples = new List { new Sample{RequiredValue = "a"}, new Sample{RequiredValue = "b"} }; + _service.CallCollection( samples ); + } +} \ No newline at end of file diff --git a/test/Util.Validation.Tests/Validators/IdCardAttributeTest.cs b/test/Util.Validation.Tests/Validators/IdCardAttributeTest.cs new file mode 100644 index 000000000..3b8281859 --- /dev/null +++ b/test/Util.Validation.Tests/Validators/IdCardAttributeTest.cs @@ -0,0 +1,60 @@ +using System.Linq; +using Util.Properties; +using Util.Validation.Tests.Samples; +using Xunit; + +namespace Util.Validation.Tests.Validators; + +/// +/// 身份证验证测试 +/// +public class IdCardAttributeTest { + /// + /// 验证样例2 + /// + private readonly Sample2 _sample2; + /// + /// 验证样例3 + /// + private readonly Sample3 _sample3; + + /// + /// 测试初始化 + /// + public IdCardAttributeTest() { + _sample2 = new Sample2(); + _sample3 = new Sample3(); + } + + /// + /// 测试验证身份证 - 有效 + /// + [Fact] + public void TestValidate_Valid() { + _sample2.IdCard = "522602196011070010"; + var result = DataAnnotationValidation.Validate( _sample2 ); + Assert.True( result.IsValid ); + } + + /// + /// 测试验证身份证 - 无效 - 默认错误消息 + /// + [Fact] + public void TestValidate_InValid_1() { + _sample2.IdCard = "5226021960110700103"; + var result = DataAnnotationValidation.Validate( _sample2 ); + Assert.False( result.IsValid ); + Assert.Equal( R.InvalidIdCard, result.First().ErrorMessage ); + } + + /// + /// 测试验证身份证 - 无效 + /// + [Fact] + public void TestValidate_InValid_2() { + _sample3.IdCard = "5226021960110700103"; + var result = DataAnnotationValidation.Validate( _sample3 ); + Assert.False( result.IsValid ); + Assert.Equal( "IdCard", result.First().ErrorMessage ); + } +} \ No newline at end of file